From bd632236c2d0a21d7f6dd9a2f67a5d2df22e57f7 Mon Sep 17 00:00:00 2001 From: weichen Date: Sun, 4 Oct 2026 18:18:57 +0000 Subject: [PATCH 1/2] Extend messages and context observation for variants and estimates Why: Labeled secrets, excluded turns, and candidate replies need fields on the shared message contract, and context snapshots need a section breakdown plus a stable prefix hash. Evidence: go test ./... in agent-system-core. New fields use omitempty, so existing golden JSON stays the same. Impact: Clients that ignore unknown fields keep working. Assemblers can now mark a message excluded or as one candidate in a group. Refs: docs/records/2026-10-04-sillytavern-review.md --- events.go | 15 ++++++++++++--- model.go | 13 ++++++++++--- ts/types.ts | 11 +++++++++++ 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/events.go b/events.go index 1179ee9..b9e6286 100644 --- a/events.go +++ b/events.go @@ -164,11 +164,20 @@ type RunCompletedPayload struct { Context *ContextObservation `json:"context,omitempty"` } +// ContextSection 是组装结果里一段的估算。EstTokens 用 UTF-8 字节数 / 3.35, +// 再按上一请求的真实用量校准。 +type ContextSection struct { + Name string `json:"name"` + EstTokens int `json:"estTokens"` +} + // ContextObservation 记录一次模型输入的实际构成与预算裁剪(ctxasm 产出)。 type ContextObservation struct { - Items []string `json:"items"` // 如 system(画像) / history(12) / attachments(1) / knowledge(3) - Truncations []string `json:"truncations,omitempty"` // 裁剪记录(历史超窗/工具结果超限/附件截断) - EstChars int `json:"estChars"` // 估算输入字符量 + Items []string `json:"items"` // 如 system(画像) / history(12) / attachments(1) / knowledge(3) + Truncations []string `json:"truncations,omitempty"` // 裁剪记录(历史超窗/工具结果超限/附件截断) + EstChars int `json:"estChars"` // 估算输入量(token;字段名保持兼容) + Sections []ContextSection `json:"sections,omitempty"` + PrefixHash string `json:"prefixHash,omitempty"` // 系统前缀哈希,前缀不变时保持稳定 } type RunFailedPayload struct { diff --git a/model.go b/model.go index c71f6b1..49f4554 100644 --- a/model.go +++ b/model.go @@ -36,9 +36,11 @@ type Conversation struct { AllowCrossRead bool `json:"allowCrossRead,omitempty"` // 跨会话出站:本会话读取同项目其他会话记录(需项目开关 crossSession.read) CrossPrivate bool `json:"crossPrivate,omitempty"` // 跨会话入站:私有会话,不被他读、不出现在他方列表 // PermissionMode 是会话权限:空或 default 沿用画像;plan 只读加计划工具;auto_edit 放行工作区写文件。 - PermissionMode string `json:"permissionMode,omitempty"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` + PermissionMode string `json:"permissionMode,omitempty"` + // SessionNote 是本会话要求,组装时附在上下文末尾,不进入系统前缀。 + SessionNote string `json:"sessionNote,omitempty"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` } // ---- 消息与部件 ---- @@ -74,6 +76,11 @@ type Message struct { Role Role `json:"role"` Parts []Part `json:"parts"` CreatedAt time.Time `json:"createdAt"` + // Excluded 为真时界面仍显示,但组装上下文时整条(及同一 run 的成组消息)不送入模型。 + Excluded bool `json:"excluded,omitempty"` + // VariantGroup 把同一轮的候选回复绑在一起。只把未排除的那条送入模型。 + VariantGroup string `json:"variantGroup,omitempty"` + VariantIndex int `json:"variantIndex,omitempty"` } // Part 是判别联合:Type 决定哪些字段有意义;未用字段为零值。 diff --git a/ts/types.ts b/ts/types.ts index becbeb6..91a1386 100644 --- a/ts/types.ts +++ b/ts/types.ts @@ -16,6 +16,7 @@ export interface Conversation { pinned?: boolean; archived?: boolean; allowCrossRead?: boolean; crossPrivate?: boolean; permissionMode?: 'default' | 'plan' | 'auto_edit' | ''; + sessionNote?: string; createdAt: string; updatedAt: string; } @@ -43,6 +44,9 @@ export interface Part { export interface Message { id: ID; conversationId: ID; runId?: ID; role: Role; parts: Part[]; createdAt: string; + excluded?: boolean; + variantGroup?: string; + variantIndex?: number; } export type RunKind = 'conversation' | 'subagent' | 'tool'; @@ -109,10 +113,17 @@ export interface RunCompletedPayload { context?: ContextObservation } +export interface ContextSection { + name: string + estTokens: number +} + export interface ContextObservation { items: string[] truncations?: string[] estChars: number + sections?: ContextSection[] + prefixHash?: string } export interface RunCheckPayload { passed: boolean; reason?: string } export interface RunFailedPayload { code: string; message: string } From 0b5c7747a3165bf0d0f2219204b7b70ec3ed7645 Mon Sep 17 00:00:00 2001 From: weichen Date: Sun, 4 Oct 2026 18:36:08 +0000 Subject: [PATCH 2/2] Keep the context section type inside the contract file Why: A separately exported ContextSection is unused inside the UI package and pushes the knip ratchet over its baseline. Evidence: UI knip reported 56 unused exports against baseline 55. The section shape stays on ContextObservation.sections. Impact: Generated frontend and UI contracts drop the extra export. JSON field names are unchanged. Refs: .task/feature-sillytavern-review.md Co-authored-by: Cursor --- ts/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ts/types.ts b/ts/types.ts index 91a1386..60936d3 100644 --- a/ts/types.ts +++ b/ts/types.ts @@ -113,7 +113,7 @@ export interface RunCompletedPayload { context?: ContextObservation } -export interface ContextSection { +interface ContextSection { name: string estTokens: number }