paseo-plugin-zcode-provider 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NOTICE.md +2 -2
- package/README.md +9 -9
- package/package.json +4 -4
- package/server/account.ts +10 -0
- package/server/build-info.ts +1 -1
- package/server/discovery/manifest.ts +2 -2
- package/server/mapping.ts +31 -0
- package/server/provider.ts +5 -18
- package/server/runtime/pins.ts +3 -3
- package/server/session.ts +7 -10
- package/server/unsupported-capabilities.ts +4 -1
- package/server/vendor/zcode/NOTICE.md +4 -1
- package/server/vendor/zcode/packages/rpc/src/channelClient.ts +1 -0
- package/server/vendor/zcode/packages/rpc/src/channelServer.ts +2 -0
- package/server/vendor/zcode/packages/rpc/src/channels.shared.ts +1 -0
- package/server/vendor/zcode/packages/rpc/src/protocol.ts +3 -2
- package/server/vendor/zcode/packages/shared/src/app-runtime-preferences.ts +18 -0
- package/server/vendor/zcode/packages/shared/src/attachment-source.ts +5 -0
- package/server/vendor/zcode/packages/shared/src/bots.ts +385 -2
- package/server/vendor/zcode/packages/shared/src/browser-use/commands.ts +3 -1
- package/server/vendor/zcode/packages/shared/src/channel-mention.ts +131 -0
- package/server/vendor/zcode/packages/shared/src/conversationSelection.ts +37 -0
- package/server/vendor/zcode/packages/shared/src/dynamic-workflow-feature.ts +137 -0
- package/server/vendor/zcode/packages/shared/src/env.ts +15 -11
- package/server/vendor/zcode/packages/shared/src/highspeed.ts +232 -0
- package/server/vendor/zcode/packages/shared/src/mcp-apps/appTools.ts +148 -0
- package/server/vendor/zcode/packages/shared/src/mcp-apps/contract.ts +262 -0
- package/server/vendor/zcode/packages/shared/src/mcp-apps/instance.ts +12 -0
- package/server/vendor/zcode/packages/shared/src/mcp-apps/resourceMime.ts +73 -0
- package/server/vendor/zcode/packages/shared/src/mcp-apps/sampling.ts +117 -0
- package/server/vendor/zcode/packages/shared/src/mcp-apps/schemas.ts +90 -0
- package/server/vendor/zcode/packages/shared/src/model-execution.ts +58 -1
- package/server/vendor/zcode/packages/shared/src/oauth.ts +3 -0
- package/server/vendor/zcode/packages/shared/src/off-peak-execution.ts +73 -0
- package/server/vendor/zcode/packages/shared/src/protocol.ts +78 -4
- package/server/vendor/zcode/packages/shared/src/remoteTarget.ts +65 -0
- package/server/vendor/zcode/packages/shared/src/request-security-edition/verification-policy.ts +46 -0
- package/server/vendor/zcode/packages/shared/src/server-remote.ts +93 -0
- package/server/vendor/zcode/packages/shared/src/source-server/compatibility.ts +115 -0
- package/server/vendor/zcode/packages/shared/src/source-server/contracts.ts +426 -0
- package/server/vendor/zcode/packages/shared/src/source-server/errorCodes.ts +74 -0
- package/server/vendor/zcode/packages/shared/src/sqlite-migrations/contract.ts +83 -0
- package/server/vendor/zcode/packages/shared/src/subagent-profile.ts +62 -0
- package/server/vendor/zcode/packages/shared/src/subagent-runtime-config.ts +51 -0
- package/server/vendor/zcode/packages/shared/src/subagents-types.ts +164 -0
- package/server/vendor/zcode/packages/shared/src/usage-stats.ts +14 -0
- package/server/vendor/zcode/packages/shared/src/validationAppSettings.ts +136 -3
- package/server/vendor/zcode/packages/shared/src/version.ts +13 -0
- package/server/vendor/zcode/packages/shared/src/zcode-agent-policy.ts +24 -1
- package/server/vendor/zcode/packages/shared/src/zcode-protocol/index.ts +697 -36
- package/server/vendor/zcode/packages/shared/src/zcode-protocol/trace.ts +11 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-legacy-types.ts +14 -2
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/apply.ts +28 -9
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/attachment-ref.ts +3 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/command.ts +73 -53
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/create-session-config.ts +15 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/create-workflow-display.ts +47 -1
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/delta.ts +58 -2
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/input-intent.ts +31 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/rows.ts +28 -13
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/sessions-index-workflow-activity.ts +37 -3
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/sessions-index.ts +36 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/snapshot.ts +21 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/submission.ts +1 -1
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/toolDisplay.ts +32 -4
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/transport.ts +31 -6
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/wire-assembler.ts +4 -1
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/wire-fault.ts +4 -1
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-artifacts.ts +62 -4
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-command-rejections.ts +30 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-row-meta.ts +40 -3
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-run-settings-command.ts +2 -2
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-runs-delta.ts +6 -5
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-runs.ts +103 -26
- package/server/vendor/zcode/packages/shared/src/zcode-task-mode-schema.ts +13 -1
- package/server/vendor/zcode/packages/shared/src/zcode-task-types-core.ts +31 -2
- package/server/vendor/zcode/packages/shared/src/zcodeEndpoint.ts +188 -73
- package/server/vendor/zcode/provenance.json +189 -80
|
@@ -1,10 +1,18 @@
|
|
|
1
|
+
import { conversationQuotesSchema } from "../conversationSelection.js";
|
|
2
|
+
import { botGroupInputSourceSchema } from "../bots.js";
|
|
1
3
|
// ConversationRow:行类型自包含。
|
|
2
4
|
// 三条结构性规则:row 自包含(渲染任一行不需看别的行);
|
|
3
5
|
// 结构变化换整行(row.upserted),文本增长用 append(row.delta);turn 是 row 上的标签不是容器。
|
|
4
6
|
import { z } from "zod";
|
|
7
|
+
import { highspeedMessageMetaSchema } from "../highspeed.js";
|
|
5
8
|
import { executionOutputPreviewSchema } from "../execution-output-preview.js";
|
|
6
9
|
import { timestampSchema } from "./core.js";
|
|
7
|
-
import {
|
|
10
|
+
import { conversationInputSourceSchema } from "./input-intent.js";
|
|
11
|
+
import {
|
|
12
|
+
backgroundResultOriginMetaSchema,
|
|
13
|
+
workflowLaunchMetaSchema,
|
|
14
|
+
} from "./workflow-row-meta.js";
|
|
15
|
+
import { attachmentRefSchema } from "./attachment-ref.js";
|
|
8
16
|
|
|
9
17
|
// RowBase。rowId:session 内单调、永不复用、事件日志的确定性纯函数。
|
|
10
18
|
const rowBaseFields = {
|
|
@@ -35,10 +43,12 @@ export type RowActions = NonNullable<z.infer<typeof rowActionsSchema>>;
|
|
|
35
43
|
// workflow 轮的行级元数据拆到了 workflow-row-meta.ts(max-lines);名字在此原样再导出。
|
|
36
44
|
export {
|
|
37
45
|
backgroundResultOriginMetaSchema,
|
|
46
|
+
workflowBlockedMetaSchema,
|
|
38
47
|
workflowLaunchMetaSchema,
|
|
39
48
|
workflowNotificationMetaSchema,
|
|
40
49
|
workflowSettingsAmendMetaSchema,
|
|
41
50
|
type BackgroundResultOriginMeta,
|
|
51
|
+
type WorkflowBlockedMeta,
|
|
42
52
|
type WorkflowLaunchMeta,
|
|
43
53
|
type WorkflowNotificationMeta,
|
|
44
54
|
type WorkflowSettingsAmendMeta,
|
|
@@ -79,6 +89,12 @@ export const turnHeaderRowSchema = z.object({
|
|
|
79
89
|
endedAt: timestampSchema.optional(),
|
|
80
90
|
// 权威工时:排除权限等待/用户输入等待/verifier 等待。
|
|
81
91
|
activeMs: z.number().optional(),
|
|
92
|
+
// 当前 product turn 内主链路 ModelComplete 的 output usage 累计;optional 兼容旧快照。
|
|
93
|
+
outputTokens: z.number().int().nonnegative().optional(),
|
|
94
|
+
// CLI 从运行时事件聚合的真实耗时;optional 兼容旧快照及普通 turn。
|
|
95
|
+
modelDurationMs: z.number().nonnegative().optional(),
|
|
96
|
+
toolDurationMs: z.number().nonnegative().optional(),
|
|
97
|
+
otherDurationMs: z.number().nonnegative().optional(),
|
|
82
98
|
// guide 不切 product turn,但每条 accepted guide 都开启独立视觉工作段。
|
|
83
99
|
// 普通 turn 缺省以保持旧 snapshot 兼容;一旦出现 guide,CLI 负责完整投影首段与后续段。
|
|
84
100
|
workSegments: z.array(turnWorkSegmentSchema).optional(),
|
|
@@ -100,6 +116,9 @@ export type TurnHeaderRow = z.infer<typeof turnHeaderRowSchema>;
|
|
|
100
116
|
export const userInputRowSchema = z.object({
|
|
101
117
|
...rowBaseFields,
|
|
102
118
|
kind: z.literal("userInput"),
|
|
119
|
+
inputOrigin: z.enum(["desktop", "mobile"]).optional(),
|
|
120
|
+
botGroupSource: botGroupInputSourceSchema.optional(),
|
|
121
|
+
conversationQuotes: conversationQuotesSchema.optional(),
|
|
103
122
|
text: z.string(),
|
|
104
123
|
// text 从此下标起是引擎附加文本(dwf ask 尾注 /
|
|
105
124
|
// nudge),GUI 把它折进默认收起的披露;0 = 整条都是;缺席 = 无(老转录、非工作流会话)。
|
|
@@ -135,17 +154,12 @@ export const userInputRowSchema = z.object({
|
|
|
135
154
|
rootSourceCommandId: z.string().optional(),
|
|
136
155
|
// 提交端身份由 CLI admission 写入;旧 transcript 可缺省。
|
|
137
156
|
clientId: z.string().optional(),
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
bytes: z.number(),
|
|
145
|
-
previewRef: z.string().optional(),
|
|
146
|
-
}),
|
|
147
|
-
)
|
|
148
|
-
.optional(),
|
|
157
|
+
// accelerated 历史展示的持久事实;缺省兼容普通消息和旧 snapshot。
|
|
158
|
+
highspeed: highspeedMessageMetaSchema.optional(),
|
|
159
|
+
// 重复定义曾在出队后的 row 校验中剥掉来源,导致历史附件丢失预览入口;与入站引用共用 schema。
|
|
160
|
+
attachments: z.array(attachmentRefSchema).optional(),
|
|
161
|
+
// 插件 UI 代发的消息带来源;卡片显示"来自插件 X"。旧 transcript 可缺省。
|
|
162
|
+
source: conversationInputSourceSchema.optional(),
|
|
149
163
|
});
|
|
150
164
|
export type UserInputRow = z.infer<typeof userInputRowSchema>;
|
|
151
165
|
|
|
@@ -203,9 +217,10 @@ export const toolCallRowSchema = z.object({
|
|
|
203
217
|
// display 解析失败只丢这张卡的载荷,不拒整条 row(理由见 toolDisplay.ts 的 toolOutputSchema
|
|
204
218
|
// 注释:装饰载荷不得决定 row/帧/订阅的生死)。
|
|
205
219
|
display: toolCallDisplaySchema.optional().catch(undefined),
|
|
220
|
+
// (2026-09-12):原 widgetState / widgetStateVisibility 已删除,插件 UI 界面状态不再进 row。
|
|
206
221
|
// status=error 时必带。
|
|
207
222
|
error: z.object({ code: z.string(), message: z.string() }).optional(),
|
|
208
|
-
//
|
|
223
|
+
// 运行中出现(ToolCallProgress 事件),终态清除。
|
|
209
224
|
progress: toolProgressSchema.optional(),
|
|
210
225
|
// continuous/replayable 共用的有界 Bash 内容,终态或后台移交时清除。
|
|
211
226
|
outputPreview: executionOutputPreviewSchema.optional(),
|
|
@@ -41,6 +41,12 @@ export const sessionWorkflowPhaseSummarySchema = z.object({
|
|
|
41
41
|
* 退化路(已进入的 phase)是按进入序拼出来的,没有并行可言,所以那时整个键缺席。
|
|
42
42
|
*/
|
|
43
43
|
alongside: z.array(z.number().int().nonnegative()).max(WORKFLOW_RUNS_LIMITS.maxPhases).optional(),
|
|
44
|
+
/**
|
|
45
|
+
* 这一站是一处**还开着的留白**(docs/dynamic-workflow/presentation.md「Holes on the timeline」):
|
|
46
|
+
* `open` = 还没走到,`waiting` = run 停在这里等主代理补全。侧栏据此把灯画成虚线(等待时警示色)。
|
|
47
|
+
* 补全之后它就是一个普通的站,键缺席。只有声明表那条路有这个事实(`run.phaseHoles`)。
|
|
48
|
+
*/
|
|
49
|
+
hole: z.enum(["open", "waiting"]).optional(),
|
|
44
50
|
});
|
|
45
51
|
export type SessionWorkflowPhaseSummary = z.infer<typeof sessionWorkflowPhaseSummarySchema>;
|
|
46
52
|
|
|
@@ -62,6 +68,12 @@ export const sessionWorkflowRunSummarySchema = z.object({
|
|
|
62
68
|
currentPhase: z.string().min(1).max(WORKFLOW_RUNS_LIMITS.maxPhaseNameLength).optional(),
|
|
63
69
|
/** status === "running" 的子代理数(tooltip 的「{n} agents working」)。 */
|
|
64
70
|
agentsWorking: z.number().int().nonnegative(),
|
|
71
|
+
/**
|
|
72
|
+
* 正在等主代理补全的留白名(`run.holes` 里第一条 waiting 的 `name`):侧栏运行行写
|
|
73
|
+
* 「{name} · 等待补全」(docs/dynamic-workflow/presentation.md「Holes on the timeline」的「The sidebar」)。
|
|
74
|
+
* 没有停驻的留白时缺席。
|
|
75
|
+
*/
|
|
76
|
+
waitingHole: z.string().min(1).max(WORKFLOW_RUNS_LIMITS.maxPhaseNameLength).optional(),
|
|
65
77
|
});
|
|
66
78
|
export type SessionWorkflowRunSummary = z.infer<typeof sessionWorkflowRunSummarySchema>;
|
|
67
79
|
|
|
@@ -98,16 +110,26 @@ function phaseNameMatches(stationName: string, stamp: string): boolean {
|
|
|
98
110
|
);
|
|
99
111
|
}
|
|
100
112
|
|
|
101
|
-
|
|
113
|
+
/**
|
|
114
|
+
* 站点表的唯一推导。侧栏运行行(经 `summarizeRun`)与任务岛的 run 行都读它,所以两处的灯
|
|
115
|
+
* 永远对同一件事说同一句话;任务岛不另写第二套。
|
|
116
|
+
*/
|
|
117
|
+
export function deriveSessionWorkflowPhases(run: WorkflowRunState): SessionWorkflowPhaseSummary[] {
|
|
102
118
|
const entered = new Set((run.phases ?? []).map((phase) => phase.name));
|
|
103
119
|
const current = run.currentPhase;
|
|
104
120
|
let names: string[];
|
|
105
121
|
// 「同时在跑」的下标说的是**声明表**里的位置,所以只有走声明表这条路时它才有意义;退化路
|
|
106
122
|
// (已进入的 phase + 当前 phase)是另一个下标空间,那时整张表不带。
|
|
107
123
|
let alongside: readonly (readonly number[])[] | undefined;
|
|
124
|
+
// 还开着的留白(声明表的下标);等待中的按名字从 `run.holes` 认——留白名按规则 9012 唯一。
|
|
125
|
+
let holeIndexes: ReadonlySet<number> = new Set();
|
|
126
|
+
const waitingNames = new Set(
|
|
127
|
+
(run.holes ?? []).filter((hole) => hole.state === "waiting").map((hole) => hole.name),
|
|
128
|
+
);
|
|
108
129
|
if (run.phaseNames !== undefined && run.phaseNames.length > 0) {
|
|
109
130
|
names = run.phaseNames;
|
|
110
131
|
alongside = run.phaseAlongside;
|
|
132
|
+
holeIndexes = new Set(run.phaseHoles ?? []);
|
|
111
133
|
} else {
|
|
112
134
|
names = (run.phases ?? []).map((phase) => phase.name);
|
|
113
135
|
if (current !== undefined && !entered.has(current)) names = [...names, current];
|
|
@@ -139,7 +161,14 @@ function derivePhases(run: WorkflowRunState): SessionWorkflowPhaseSummary[] {
|
|
|
139
161
|
const beside = (alongside?.[index] ?? []).filter(
|
|
140
162
|
(other) => Number.isInteger(other) && other >= 0 && other < emitted && other !== index,
|
|
141
163
|
);
|
|
142
|
-
|
|
164
|
+
// 留白站:等待中的按 `run.holes` 认(名字唯一),其余下标表点名的是还没走到的留白。
|
|
165
|
+
const hole = waitingNames.has(name) ? "waiting" : holeIndexes.has(index) ? "open" : undefined;
|
|
166
|
+
return {
|
|
167
|
+
name,
|
|
168
|
+
status,
|
|
169
|
+
...(beside.length === 0 ? {} : { alongside: beside }),
|
|
170
|
+
...(hole === undefined ? {} : { hole }),
|
|
171
|
+
};
|
|
143
172
|
});
|
|
144
173
|
}
|
|
145
174
|
|
|
@@ -148,6 +177,10 @@ function summarizeRun(
|
|
|
148
177
|
work: BackgroundWorkSummary | undefined,
|
|
149
178
|
): SessionWorkflowRunSummary {
|
|
150
179
|
const name = work?.title.trim();
|
|
180
|
+
// 在跑的 run 才有「等补全」可言:终态 run 的 waiting 记录已被 reducer 剥掉,这里再守一道。
|
|
181
|
+
const waitingHole = isSessionWorkflowRunLive(run.status)
|
|
182
|
+
? (run.holes ?? []).find((hole) => hole.state === "waiting")?.name
|
|
183
|
+
: undefined;
|
|
151
184
|
return {
|
|
152
185
|
runId: run.runId,
|
|
153
186
|
...(run.toolCallId === undefined ? {} : { toolCallId: run.toolCallId }),
|
|
@@ -155,9 +188,10 @@ function summarizeRun(
|
|
|
155
188
|
status: run.status,
|
|
156
189
|
...(run.stopReason === undefined ? {} : { stopReason: run.stopReason }),
|
|
157
190
|
...(work === undefined ? {} : { startedAt: work.startedAt }),
|
|
158
|
-
phases:
|
|
191
|
+
phases: deriveSessionWorkflowPhases(run),
|
|
159
192
|
...(run.currentPhase === undefined ? {} : { currentPhase: run.currentPhase }),
|
|
160
193
|
agentsWorking: run.actors.filter((actor) => actor.status === "running").length,
|
|
194
|
+
...(waitingHole === undefined ? {} : { waitingHole }),
|
|
161
195
|
};
|
|
162
196
|
}
|
|
163
197
|
|
|
@@ -35,6 +35,7 @@ export const sessionSummarySchema = z.object({
|
|
|
35
35
|
// custom = 用户显式重命名;default/generated 都不是产品语义上的手动标题。
|
|
36
36
|
// optional 是为了兼容旧 sessions-index frame / 旧持久化摘要。
|
|
37
37
|
titleSource: sessionMetaStateSchema.shape.titleSource.optional(),
|
|
38
|
+
lastOutcome: z.enum(["completed", "error", "interrupted"]).optional(),
|
|
38
39
|
phase: sessionPhaseSchema,
|
|
39
40
|
sessionEnded: z.boolean(),
|
|
40
41
|
// 列表小圆点用(此处保留布尔,避免为侧栏订阅整个 backgroundWorks)。
|
|
@@ -72,6 +73,41 @@ export const sessionsIndexDeltaSchema = z.discriminatedUnion("op", [
|
|
|
72
73
|
]);
|
|
73
74
|
export type SessionsIndexDelta = z.infer<typeof sessionsIndexDeltaSchema>;
|
|
74
75
|
|
|
76
|
+
/** snapshot 缺席不是删除;按明确 ID 查询持久事实,不会创建或恢复运行实例。 */
|
|
77
|
+
export const sessionsIndexReconcileParamsSchema = z
|
|
78
|
+
.object({
|
|
79
|
+
workspaceId: z.string().min(1),
|
|
80
|
+
sessionIds: z.array(z.string().min(1)).max(256),
|
|
81
|
+
})
|
|
82
|
+
.strict();
|
|
83
|
+
export type SessionsIndexReconcileParams = z.infer<typeof sessionsIndexReconcileParamsSchema>;
|
|
84
|
+
export const sessionsIndexReconcileResultSchema = z
|
|
85
|
+
.object({
|
|
86
|
+
sessions: z
|
|
87
|
+
.array(
|
|
88
|
+
z.discriminatedUnion("exists", [
|
|
89
|
+
z
|
|
90
|
+
.object({ sessionId: z.string(), exists: z.literal(false), deleted: z.literal(false) })
|
|
91
|
+
.strict(),
|
|
92
|
+
z
|
|
93
|
+
.object({
|
|
94
|
+
sessionId: z.string(),
|
|
95
|
+
exists: z.literal(true),
|
|
96
|
+
deleted: z.boolean(),
|
|
97
|
+
title: z.string(),
|
|
98
|
+
titleSource: sessionMetaStateSchema.shape.titleSource,
|
|
99
|
+
createdAt: timestampSchema,
|
|
100
|
+
updatedAt: timestampSchema,
|
|
101
|
+
lastOutcome: z.enum(["completed", "error", "interrupted"]).optional(),
|
|
102
|
+
})
|
|
103
|
+
.strict(),
|
|
104
|
+
]),
|
|
105
|
+
)
|
|
106
|
+
.max(256),
|
|
107
|
+
})
|
|
108
|
+
.strict();
|
|
109
|
+
export type SessionsIndexReconcileResult = z.infer<typeof sessionsIndexReconcileResultSchema>;
|
|
110
|
+
|
|
75
111
|
/** sessions-index topic key 构造(与 parseSessionsIndexTopic 对偶)。 */
|
|
76
112
|
export function sessionsIndexTopic(workspaceId: string): string {
|
|
77
113
|
return `sessions-index/${workspaceId}`;
|
|
@@ -60,6 +60,7 @@ export const activeWorkSummarySchema = z.object({
|
|
|
60
60
|
"turnSteer",
|
|
61
61
|
]),
|
|
62
62
|
foregroundExecutionId: z.string().min(1).optional(),
|
|
63
|
+
sourceCommandId: z.string().min(1).optional(),
|
|
63
64
|
startedAt: timestampSchema,
|
|
64
65
|
});
|
|
65
66
|
export type ActiveWorkSummary = z.infer<typeof activeWorkSummarySchema>;
|
|
@@ -99,6 +100,10 @@ export const errorAttributionSchema = z
|
|
|
99
100
|
statusCode: z.number().int().min(100).max(599).optional(),
|
|
100
101
|
providerErrorCode: z.string().min(1).max(160).optional(),
|
|
101
102
|
retryable: z.boolean().optional(),
|
|
103
|
+
/** string=使用该 Highspeed 卡,null=明确未使用,undefined=旧 Agent 未发布。 */
|
|
104
|
+
highspeedCardId: z.string().nullable().optional(),
|
|
105
|
+
/** true=本轮 Highspeed 失败已命中退回声明但无可用退回目标;Renderer 据此作废匹配卡。 */
|
|
106
|
+
highspeedFallbackUnavailable: z.boolean().optional(),
|
|
102
107
|
})
|
|
103
108
|
.strict();
|
|
104
109
|
export type ErrorAttribution = z.infer<typeof errorAttributionSchema>;
|
|
@@ -467,7 +472,21 @@ export const workspaceHookAdmissionStateSchema = z.object({
|
|
|
467
472
|
});
|
|
468
473
|
export type WorkspaceHookAdmissionSnapshotState = z.infer<typeof workspaceHookAdmissionStateSchema>;
|
|
469
474
|
|
|
475
|
+
// 冷历史可读不代表 runtime 已可执行;缺席兼容旧发送端。
|
|
476
|
+
export const runtimeInitializationSchema = z.discriminatedUnion("status", [
|
|
477
|
+
z.object({ status: z.literal("initializing"), attemptId: z.string().min(1) }),
|
|
478
|
+
z.object({ status: z.literal("ready"), attemptId: z.string().min(1) }),
|
|
479
|
+
z.object({
|
|
480
|
+
status: z.literal("failed"),
|
|
481
|
+
attemptId: z.string().min(1),
|
|
482
|
+
reasonCode: z.string(),
|
|
483
|
+
message: z.string(),
|
|
484
|
+
}),
|
|
485
|
+
]);
|
|
486
|
+
export type RuntimeInitialization = z.infer<typeof runtimeInitializationSchema>;
|
|
487
|
+
|
|
470
488
|
export const conversationSnapshotSchema = z.object({
|
|
489
|
+
runtimeInitialization: runtimeInitializationSchema.optional(),
|
|
471
490
|
protocolVersion: z.literal(1),
|
|
472
491
|
sessionId: z.string(),
|
|
473
492
|
logEpoch: z.string(),
|
|
@@ -503,6 +522,8 @@ export const conversationSnapshotSchema = z.object({
|
|
|
503
522
|
// 旧快照/旧发送端不携带此字段 → 解析得 null,不破坏兼容性(遵守冻结规则)。
|
|
504
523
|
// pendingCount === 0 时投影层置 null(提示条消失)。
|
|
505
524
|
workspaceHookAdmission: workspaceHookAdmissionStateSchema.nullable().default(null),
|
|
525
|
+
// (2026-09-12):原 additive 字段 pluginUiSurfaces 已删除,插件面板 widgetState 只留在
|
|
526
|
+
// 宿主 renderer 内存。
|
|
506
527
|
// B 区
|
|
507
528
|
rows: rowsWindowSchema,
|
|
508
529
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
|
|
3
3
|
/** Composer 可以显式提交的 Agent mode;auto 是 Runtime 内部状态,不进入用户 Submission。 */
|
|
4
|
-
export const submissionModeSchema = z.enum(["build", "edit", "plan", "yolo"]);
|
|
4
|
+
export const submissionModeSchema = z.enum(["build", "edit", "plan", "yolo", "guarded"]);
|
|
5
5
|
export type SubmissionMode = z.infer<typeof submissionModeSchema>;
|
|
@@ -6,6 +6,7 @@ import { bashOutputDisplaySchema } from "../bash-output-display.js";
|
|
|
6
6
|
import { timestampSchema } from "./core.js";
|
|
7
7
|
import { OFFICIAL_MCP_TOOL_ERROR_CODES } from "../official-mcp-tool-error.js";
|
|
8
8
|
import { cuaRequestAccessStatusSchema } from "./cuaPermission.js";
|
|
9
|
+
import { mcpToolDisplayUiSchema } from "../mcp-apps/schemas.js";
|
|
9
10
|
import { toolCallCreateWorkflowDisplaySchema } from "./create-workflow-display.js";
|
|
10
11
|
import {
|
|
11
12
|
toolCallEvalWorkflowSnippetDisplaySchema,
|
|
@@ -20,7 +21,7 @@ import {
|
|
|
20
21
|
// errorCode/suggestedAction/media(screenshot) 等结构化内容带到 renderer)。consume-main 之前
|
|
21
22
|
// 缺这个 union + toolOutputSchema.display 字段——协议层 zod 校验会把 agent 下发的 display 整个
|
|
22
23
|
// strip 掉,导致 UI 永远拿不到 display?.kind==="cua",CUA 工具调用退化成 fallback 渲染。
|
|
23
|
-
const toolResultDisplaySchema = z.discriminatedUnion("kind", [
|
|
24
|
+
export const toolResultDisplaySchema = z.discriminatedUnion("kind", [
|
|
24
25
|
bashOutputDisplaySchema,
|
|
25
26
|
z.object({
|
|
26
27
|
kind: z.literal("file_diff"),
|
|
@@ -133,6 +134,8 @@ const toolResultDisplaySchema = z.discriminatedUnion("kind", [
|
|
|
133
134
|
.object({ code: z.enum(OFFICIAL_MCP_TOOL_ERROR_CODES) })
|
|
134
135
|
.strict()
|
|
135
136
|
.optional(),
|
|
137
|
+
// 插件 UI 元数据;与 CLI contracts 的 mcpToolResultDisplayPayloadSchema 同源复用,见 mcp-apps/schemas.ts。
|
|
138
|
+
ui: mcpToolDisplayUiSchema.optional(),
|
|
136
139
|
}),
|
|
137
140
|
// buildToolOutput 把 CLI 侧 ToolResultDisplayPayload 原样塞进 toolOutput.display,
|
|
138
141
|
// 而这条 union 是 strict 的——create_workflow 不在成员里,CreateWorkflow 的 display 会被整段
|
|
@@ -150,8 +153,27 @@ const toolResultDisplaySchema = z.discriminatedUnion("kind", [
|
|
|
150
153
|
]);
|
|
151
154
|
export type ToolResultDisplay = z.infer<typeof toolResultDisplaySchema>;
|
|
152
155
|
|
|
153
|
-
// toolCall。终态 output 全档统一 head+tail
|
|
154
|
-
//
|
|
156
|
+
// §4.4.5 toolCall。终态 output 全档统一 head+tail 截断(R-10),超出走 truncated.ref 按需拉。
|
|
157
|
+
//
|
|
158
|
+
// ⚠ display 在信封层**不设门**(`.optional().catch(undefined)`):解析不过就退化成「这张卡没有
|
|
159
|
+
// 载荷」,而不是让整条 row、整帧、整条订阅失败。四个嵌入点共用这条规则——本文件的
|
|
160
|
+
// toolOutputSchema、rows.ts 的 toolCallRow、snapshot.ts 的确认预览、workflow-row-meta.ts 的
|
|
161
|
+
// 启动行图;spec 见 docs/v4-refactor/10-protocol-spec.md §4.4.5 与
|
|
162
|
+
// docs/v4-refactor/04-sync-and-recovery.md 封闭规则 11。
|
|
163
|
+
//
|
|
164
|
+
// 根因(2026-09-21,sess_4142de31):display 是装饰载荷,却长在 liveness 关键的信封里。CLI 给
|
|
165
|
+
// `list_workflow_runs` 的 run 行加了两个 lineage 键,本侧镜像没跟上 → 整帧被
|
|
166
|
+
// `proto.frameAssemblyInvalidPayload` 拒 → 恢复阶梯在同一份内容上重试 → 会话停在
|
|
167
|
+
// `fault.subscription.recoveryFailed`;而每次快照都重放同一份存量载荷,会话永不自愈。同款事故
|
|
168
|
+
// 2026-09-17 已经发生过一次(`get_workflow_run` 的 `providerStop`),当时只修了那一个字段。
|
|
169
|
+
//
|
|
170
|
+
// 严格性没有丢,只是回到该在的那一层:渲染侧 readToolResultDisplay
|
|
171
|
+
// (packages/ui/src/ToolCallBlocks/toolResultDisplay.ts)本来就按 kind 逐个 strict 解析,失败即
|
|
172
|
+
// 回 undefined、卡片退化成纯文本——这正是各 spec 一直承诺的行为。信封层那道门是重复的第二道
|
|
173
|
+
// 门,唯一的独有效果是杀死会话。
|
|
174
|
+
//
|
|
175
|
+
// 成员 schema 仍然 `.strict()`:它们定义「本端认得的形状」,catch 只把「不认得」从致命降级为
|
|
176
|
+
// 无卡。代价是 skew 变静默,补偿手段是构造侧与镜像侧的 parity 测试,而不是让线上订阅去发现。
|
|
155
177
|
export const toolOutputSchema = z.object({
|
|
156
178
|
text: z.string(),
|
|
157
179
|
display: toolResultDisplaySchema.optional().catch(undefined),
|
|
@@ -164,9 +186,14 @@ export const toolOutputSchema = z.object({
|
|
|
164
186
|
});
|
|
165
187
|
export type ToolOutput = z.infer<typeof toolOutputSchema>;
|
|
166
188
|
|
|
189
|
+
// 工具运行中的进度():MCP `notifications/progress` 经 ToolCallProgress 事件投影到行上。
|
|
190
|
+
// fraction 由 payload progress/total 推得(total 缺失时不填);bytes / previewLine 保留给 shell 输出型进度。
|
|
167
191
|
export const toolProgressSchema = z.object({
|
|
168
|
-
bytes: z.number(),
|
|
192
|
+
bytes: z.number().optional(),
|
|
169
193
|
previewLine: z.string().optional(),
|
|
194
|
+
fraction: z.number().min(0).max(1).optional(),
|
|
195
|
+
total: z.number().optional(),
|
|
196
|
+
message: z.string().max(200).optional(),
|
|
170
197
|
updatedAt: timestampSchema,
|
|
171
198
|
});
|
|
172
199
|
export type ToolProgress = z.infer<typeof toolProgressSchema>;
|
|
@@ -245,6 +272,7 @@ const toolCallMcpDisplaySchema = z
|
|
|
245
272
|
.object({ code: z.enum(OFFICIAL_MCP_TOOL_ERROR_CODES) })
|
|
246
273
|
.strict()
|
|
247
274
|
.optional(),
|
|
275
|
+
ui: mcpToolDisplayUiSchema.optional(),
|
|
248
276
|
})
|
|
249
277
|
.strict();
|
|
250
278
|
|
|
@@ -35,6 +35,7 @@ export const hostCapabilitiesSchema = z.object({
|
|
|
35
35
|
* `state.updated`,并且先经 `clampWorkflowRunsForLegacy` 裁到旧界。
|
|
36
36
|
*/
|
|
37
37
|
workflowRunDeltas: z.boolean().optional(),
|
|
38
|
+
historyFirst: z.boolean().optional(),
|
|
38
39
|
});
|
|
39
40
|
export type HostCapabilities = z.infer<typeof hostCapabilitiesSchema>;
|
|
40
41
|
|
|
@@ -82,6 +83,7 @@ export const clientHelloSchema = z
|
|
|
82
83
|
* 是 `.strict()` 的,老 Host 见到不认识的键会整条 clientHello 解析失败、连接握不上手。
|
|
83
84
|
*/
|
|
84
85
|
workflowRunDeltas: z.boolean().optional(),
|
|
86
|
+
historyFirst: z.boolean().optional(),
|
|
85
87
|
})
|
|
86
88
|
.strict()
|
|
87
89
|
.optional(),
|
|
@@ -112,6 +114,8 @@ export const subscribeParamsSchema = z
|
|
|
112
114
|
topic: z.string(),
|
|
113
115
|
// 水位不变量:仅当客户端真持有该时刻一致状态才允许带。
|
|
114
116
|
base: z.object({ logEpoch: z.string(), seq: z.number() }).optional(),
|
|
117
|
+
// 手动重试仅消费当前 failed attempt;普通重连不重跑 hook。
|
|
118
|
+
retryInitialization: z.string().min(1).optional(),
|
|
115
119
|
// QoS hint,只影响调度,不影响语义。
|
|
116
120
|
visibility: z.enum(["foreground", "background"]).optional(),
|
|
117
121
|
})
|
|
@@ -142,6 +146,7 @@ export const conversationOpenTimingSchema = z
|
|
|
142
146
|
cliRequestMs: openTimingMsSchema,
|
|
143
147
|
cliBootstrapMs: openTimingMsSchema,
|
|
144
148
|
cliSessionRestoreMs: openTimingMsSchema,
|
|
149
|
+
historyReadyMs: openTimingMsSchema,
|
|
145
150
|
initialFrameEncodeMs: openTimingMsSchema,
|
|
146
151
|
cliProcessState: z.enum(["spawned", "reused"]).optional(),
|
|
147
152
|
sessionRuntimeState: z.enum(["cold", "warm"]).optional(),
|
|
@@ -335,6 +340,7 @@ export const V4_METHODS = {
|
|
|
335
340
|
controllerResync: "v4/controller/resync",
|
|
336
341
|
controllerUnsubscribe: "v4/controller/unsubscribe",
|
|
337
342
|
conversationSubscribe: "v4/conversation/subscribe",
|
|
343
|
+
sessionsIndexReconcile: "v4/sessionsIndex/reconcile",
|
|
338
344
|
conversationResync: "v4/conversation/resync",
|
|
339
345
|
conversationUnsubscribe: "v4/conversation/unsubscribe",
|
|
340
346
|
// 行分页 query(rows/range):只读、无状态、超时重发安全。
|
|
@@ -343,6 +349,7 @@ export const V4_METHODS = {
|
|
|
343
349
|
conversationPlans: "v4/conversation/plans",
|
|
344
350
|
conversationFileChanges: "v4/conversation/fileChanges",
|
|
345
351
|
backgroundBashOutput: "v4/conversation/backgroundBashOutput",
|
|
352
|
+
offPeakControl: "v4/conversation/offPeakControl",
|
|
346
353
|
conversationFileRewindPreview: "v4/conversation/fileRewindPreview",
|
|
347
354
|
// workflow run 的事件日志分页(详情页审计面):只读、无状态、超时重发安全。
|
|
348
355
|
// 新方法天然偏斜安全——旧桌面根本不会调用它。
|
|
@@ -405,6 +412,7 @@ export type V4ConnectionFlowParams = z.infer<typeof v4ConnectionFlowParamsSchema
|
|
|
405
412
|
export const v4ConnectionFlowResultSchema = z.object({}).strict();
|
|
406
413
|
|
|
407
414
|
export const V4_NOTIFICATIONS = {
|
|
415
|
+
offPeakLifecycle: "v4/offPeak/lifecycle",
|
|
408
416
|
// 下行帧(snapshot / deltas),params = ConversationTopicFrame。
|
|
409
417
|
conversationFrame: "v4/conversation/frame",
|
|
410
418
|
// 仅 live ingest 的无正文事实;不进入 topic snapshot/recovery。
|
|
@@ -412,6 +420,7 @@ export const V4_NOTIFICATIONS = {
|
|
|
412
420
|
localTtftFacts: "v4/telemetry/local-ttft",
|
|
413
421
|
// 仅当前进程 live ToolCallResult 产生;历史与 replayable 链路不得补造。
|
|
414
422
|
cuaPermissionObservation: "v4/cua/permission-observation",
|
|
423
|
+
pluginUiToolResult: "v4/plugin-ui/tool-result",
|
|
415
424
|
} as const;
|
|
416
425
|
|
|
417
426
|
/** 3.3.6 SSH 历史任务归属证明与 sessions-index 冷种子共享同一有界窗口。 */
|
|
@@ -420,6 +429,7 @@ export const MAX_LEGACY_TASK_IDS_PER_SUBSCRIBE = 200;
|
|
|
420
429
|
// subscribe 请求 = 通用 SubscribeParams + connectionId(重订阅替换按
|
|
421
430
|
// (connectionId, topic) 判定;stdio 单管道场景由 host 为每个下游客户端分配)。
|
|
422
431
|
export const v4ConversationSubscribeParamsSchema = subscribeParamsSchema.extend({
|
|
432
|
+
additionalDirectories: z.array(z.string().trim().min(1)).max(256).optional(),
|
|
423
433
|
connectionId: z.string(),
|
|
424
434
|
clientMode: z.enum(["desktop-continuous", "web-remote-replayable"]),
|
|
425
435
|
// 当前可信 attachment 的 workspace;cold resume 优先使用它恢复身份,live 不消费。
|
|
@@ -434,6 +444,7 @@ export const v4ConversationSubscribeParamsSchema = subscribeParamsSchema.extend(
|
|
|
434
444
|
* 不是某一次订阅可以自选的口味。缺席 = 按旧消费者处理(整键 patch + 旧界裁剪)。
|
|
435
445
|
*/
|
|
436
446
|
workflowRunDeltas: z.boolean().optional(),
|
|
447
|
+
historyFirst: z.boolean().optional(),
|
|
437
448
|
});
|
|
438
449
|
export type V4ConversationSubscribeParams = z.infer<typeof v4ConversationSubscribeParamsSchema>;
|
|
439
450
|
|
|
@@ -596,13 +607,24 @@ export type V4ConversationFileChangesResult = z.infer<typeof v4ConversationFileC
|
|
|
596
607
|
// (`appendEvent` 单调分配),在 workflowRuns[].lastEventSequence 抬升时重取。
|
|
597
608
|
// 刻意**不是** v4 command:command 的 ACK 结果是 commandResultSchema 那个封闭的「变更结果」
|
|
598
609
|
// 判别联合,把一页只读事件塞进去等于把读放进写的词汇表,还要白背 baseRevision/幂等那套机制。
|
|
610
|
+
|
|
611
|
+
/**
|
|
612
|
+
* 事件日志一页的两道界,网关按它们取数(apps/zcode-cli/packages/dynamic-workflow/docs/execution-engine.md
|
|
613
|
+
* 「Reading the journal」)。字节按存储里的序列化字节计、取协议单帧上限的四分之一:条数单独
|
|
614
|
+
* 管不住字节,500 条带着大 report item 的事件足以顶到单帧上限。
|
|
615
|
+
*/
|
|
616
|
+
export const WORKFLOW_RUN_EVENTS_PAGE_LIMITS = {
|
|
617
|
+
maxEvents: 500,
|
|
618
|
+
maxBytes: PROTOCOL_V4_LIMITS.logicalFrameAssemblyMaxBytes / 4,
|
|
619
|
+
} as const;
|
|
620
|
+
|
|
599
621
|
export const v4ConversationWorkflowRunEventsParamsSchema = z
|
|
600
622
|
.object({
|
|
601
623
|
sessionId: z.string().min(1),
|
|
602
624
|
runId: z.string().min(1),
|
|
603
625
|
/** 只取 sequence 严格大于该值的事件;缺省从头取。 */
|
|
604
626
|
afterSequence: z.number().int().nonnegative().optional(),
|
|
605
|
-
limit: z.number().int().positive().max(
|
|
627
|
+
limit: z.number().int().positive().max(WORKFLOW_RUN_EVENTS_PAGE_LIMITS.maxEvents).optional(),
|
|
606
628
|
})
|
|
607
629
|
.strict();
|
|
608
630
|
export type V4ConversationWorkflowRunEventsParams = z.infer<
|
|
@@ -623,7 +645,7 @@ export const v4ConversationWorkflowRunEventsResultSchema = z
|
|
|
623
645
|
})
|
|
624
646
|
.strict(),
|
|
625
647
|
),
|
|
626
|
-
/**
|
|
648
|
+
/** 本页之后仍有事件(页可能因条数或字节上界提前收尾)。 */
|
|
627
649
|
hasMore: z.boolean(),
|
|
628
650
|
})
|
|
629
651
|
.strict();
|
|
@@ -1016,8 +1038,9 @@ export const v4AttachmentReadResultSchema = z
|
|
|
1016
1038
|
(value) =>
|
|
1017
1039
|
value.startsWith("image/") ||
|
|
1018
1040
|
value.startsWith("video/") ||
|
|
1019
|
-
value.split(";", 1)[0]?.trim().toLowerCase() === "application/pdf"
|
|
1020
|
-
|
|
1041
|
+
value.split(";", 1)[0]?.trim().toLowerCase() === "application/pdf" ||
|
|
1042
|
+
value.split(";", 1)[0]?.trim().toLowerCase() === "text/plain",
|
|
1043
|
+
"attachment preview only supports image/video/pdf/plain text types",
|
|
1021
1044
|
),
|
|
1022
1045
|
totalBytes: z.number().int().nonnegative().max(PROTOCOL_V4_LIMITS.attachmentPreviewMaxBytes),
|
|
1023
1046
|
nextOffset: z.number().int().positive().nullable(),
|
|
@@ -1025,7 +1048,9 @@ export const v4AttachmentReadResultSchema = z
|
|
|
1025
1048
|
.strict()
|
|
1026
1049
|
.superRefine((value, context) => {
|
|
1027
1050
|
if (
|
|
1028
|
-
|
|
1051
|
+
// 根因:运行时已支持材料文本,响应 schema 仍只接受媒体,导致跨进程预览失败。
|
|
1052
|
+
(value.mediaType.startsWith("image/") ||
|
|
1053
|
+
value.mediaType.split(";", 1)[0]?.trim().toLowerCase() === "text/plain") &&
|
|
1029
1054
|
value.totalBytes > PROTOCOL_V4_LIMITS.attachmentMaxBytes
|
|
1030
1055
|
) {
|
|
1031
1056
|
context.addIssue({
|
|
@@ -1033,7 +1058,7 @@ export const v4AttachmentReadResultSchema = z
|
|
|
1033
1058
|
maximum: PROTOCOL_V4_LIMITS.attachmentMaxBytes,
|
|
1034
1059
|
origin: "number",
|
|
1035
1060
|
inclusive: true,
|
|
1036
|
-
message: "image preview exceeds total byte limit",
|
|
1061
|
+
message: "image/text preview exceeds total byte limit",
|
|
1037
1062
|
path: ["totalBytes"],
|
|
1038
1063
|
});
|
|
1039
1064
|
}
|
|
@@ -192,7 +192,10 @@ export class TopicWireFrameAssembler<F> {
|
|
|
192
192
|
accept(wire: TopicWireFrameCandidate, now = Date.now()): TopicWireAssemblyEvent<F>[] {
|
|
193
193
|
const events = this.expire(now);
|
|
194
194
|
const key = routeKey(wire.topic, wire.subscriptionId);
|
|
195
|
-
if (
|
|
195
|
+
if (
|
|
196
|
+
!Number.isSafeInteger(wire.logicalFrameOrdinal) ||
|
|
197
|
+
wire.logicalFrameOrdinal < 1
|
|
198
|
+
) {
|
|
196
199
|
events.push(this.fault(wire, "proto.frameAssemblyMetadataMismatch"));
|
|
197
200
|
return events;
|
|
198
201
|
}
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
// V4 physical assembly fault 的分类:**内容确定性失败** vs 瞬态失败。
|
|
2
|
+
// spec:docs/v4-refactor/04-sync-and-recovery.md 封闭规则 11、
|
|
3
|
+
// docs/v4-refactor/05-transport-and-backpressure.md「客户端规则」。
|
|
2
4
|
//
|
|
3
5
|
// 恢复阶梯默认把 fault 当瞬态处理:先 same-sub resync(服务端可能回 resume),再断档则升级
|
|
4
6
|
// forceSnapshot,仍不成则 fail closed。这套升级对丢片、超时、校验和不符是对的——重投一次很可能
|
|
5
7
|
// 就好了。对 **schema 拒收** 却是错的:字节已经过了 length/checksum/UTF-8/JSON 四道关,被拒说明
|
|
6
8
|
// 本端读不懂对端发来的**内容**,而 resume 档只会把同一批 delta 再投一遍,必然同样被拒。
|
|
7
9
|
//
|
|
8
|
-
//
|
|
10
|
+
// 2026-09-21(sess_4142de31)的事故就是这条:一个工具卡载荷多带两个键,恢复阶梯在同一份内容上
|
|
11
|
+
// 烧掉三帧后 fail closed,用户手动「重新连接」两次、每次再烧三帧,会话再也打不开。
|
|
9
12
|
//
|
|
10
13
|
// 本模块只回答「这个 reason code 是不是确定性内容失败」,不决定怎么处置——处置在两个 store 里
|
|
11
14
|
// (conversationProjectionStore / sessionsIndexStore)。
|
|
@@ -49,8 +49,49 @@ export const WORKFLOW_ARTIFACT_LIMITS = {
|
|
|
49
49
|
maxItemsPerPage: 500,
|
|
50
50
|
/** 一页的缺省条数(调用方不传 limit 时网关用它)。 */
|
|
51
51
|
defaultItemsPerPage: 200,
|
|
52
|
+
/**
|
|
53
|
+
* `workflowRunArtifactData` 一页的字节上界(按存储里的序列化字节计),由网关传给存储层。
|
|
54
|
+
* 取协议单帧上限(16 MiB)的四分之一:条数上界单独管不住字节——500 条 × 32 KiB 就顶到了
|
|
55
|
+
* 单帧上限,于是一页可能根本发不出去。一页至少带一条,所以单条的上限(`REPORT_CAPS`)
|
|
56
|
+
* 必须远小于单帧上限。
|
|
57
|
+
*/
|
|
58
|
+
maxPageBytes: PROTOCOL_V4_LIMITS.logicalFrameAssemblyMaxBytes / 4,
|
|
59
|
+
/** `workflowRunArtifactData` 一次最多点名几个字段路径(一张看板的 spec 用不到这么多)。 */
|
|
60
|
+
maxFieldsPerRequest: 32,
|
|
61
|
+
/** 一个字段路径的最大长度。 */
|
|
62
|
+
maxFieldPathLength: 256,
|
|
63
|
+
/**
|
|
64
|
+
* 一个字段值的序列化字节上界。超过的值回成一段截短的字符串(前 1,000 个字符加「…」):
|
|
65
|
+
* 图表只用数值,表格与看板的格子本来就只显示短文本。
|
|
66
|
+
*/
|
|
67
|
+
maxFieldValueBytes: 4096,
|
|
52
68
|
} as const;
|
|
53
69
|
|
|
70
|
+
/**
|
|
71
|
+
* 按点路径从一条 report item 里取值:"timing.after"、"rounds.0.ms"(docs/execution-engine.md
|
|
72
|
+
* 「Reading the journal」)。
|
|
73
|
+
*
|
|
74
|
+
* 这是**协议上的契约**,不只是渲染器的工具函数:看板取数时 CLI 在 SQLite 里按同一套规则把
|
|
75
|
+
* 字段取出来(adapters 的 dwf-journal-fields.ts),渲染器拿到的值必须与它自己从整条 item 里
|
|
76
|
+
* 取出来的逐字相同。规则:按 `.` 切段;当前值是数组时,段按 `Number(段)` 当下标(非负整数且
|
|
77
|
+
* 在界内才算);是对象时按键取;走不通(null、标量、越界、缺键)得 `undefined`。
|
|
78
|
+
*/
|
|
79
|
+
export function readWorkflowArtifactField(item: unknown, path: string): unknown {
|
|
80
|
+
let cursor: unknown = item;
|
|
81
|
+
for (const segment of path.split(".")) {
|
|
82
|
+
if (cursor === null || cursor === undefined) return undefined;
|
|
83
|
+
if (Array.isArray(cursor)) {
|
|
84
|
+
const index = Number(segment);
|
|
85
|
+
if (!Number.isInteger(index) || index < 0 || index >= cursor.length) return undefined;
|
|
86
|
+
cursor = cursor[index];
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (typeof cursor !== "object") return undefined;
|
|
90
|
+
cursor = (cursor as Record<string, unknown>)[segment];
|
|
91
|
+
}
|
|
92
|
+
return cursor;
|
|
93
|
+
}
|
|
94
|
+
|
|
54
95
|
/**
|
|
55
96
|
* 一个产物版本的**元数据**(journal `dwf_node.result_json` 上 `ArtifactVersionRecord` 的
|
|
56
97
|
* zod 镜像)。字节不在这里——`uri` 指向 tool-artifact store,取字节走
|
|
@@ -120,7 +161,7 @@ export type WorkflowRunArtifact = z.infer<typeof workflowRunArtifactSchema>;
|
|
|
120
161
|
* `workflowRunArtifactRead` 取字节)按需拉——权威始终在 journal。
|
|
121
162
|
*
|
|
122
163
|
* `itemCount` 在这里的职责是**刷新信号**:看板 hook 见它变化就带 `afterSequence` 增量取数。
|
|
123
|
-
* 把标签 report
|
|
164
|
+
* 把标签 report 的原值放进这个高频状态键,一个 run 的条目有多少字节,每一帧就要搬多少字节。
|
|
124
165
|
*/
|
|
125
166
|
export const workflowRunArtifactSummarySchema = z
|
|
126
167
|
.object({
|
|
@@ -174,6 +215,18 @@ export const v4ConversationWorkflowRunArtifactDataParamsSchema = z
|
|
|
174
215
|
afterSequence: z.number().int().nonnegative().optional(),
|
|
175
216
|
/** 缺省 200、钳 [1, 500]——两者都在网关侧执行(存储层不得自造页大小,也不得再钳)。 */
|
|
176
217
|
limit: z.number().int().positive().max(WORKFLOW_ARTIFACT_LIMITS.maxItemsPerPage).optional(),
|
|
218
|
+
/**
|
|
219
|
+
* 只取这些字段路径({@link readWorkflowArtifactField} 的规则),而不是整条 item:看板只读
|
|
220
|
+
* spec 里点名的字段,一条 1 MiB 的 item 不该为了一个数被整条搬进渲染器。给了它,结果每条
|
|
221
|
+
* 带 `fields` 而不带 `item`。缺省 = 整条 item(老渲染器的形状)。
|
|
222
|
+
*
|
|
223
|
+
* 偏斜:老 CLI 的 strict schema 不认这个键、整条请求被拒;渲染器据此退回不带它重试。
|
|
224
|
+
*/
|
|
225
|
+
fields: z
|
|
226
|
+
.array(z.string().min(1).max(WORKFLOW_ARTIFACT_LIMITS.maxFieldPathLength))
|
|
227
|
+
.min(1)
|
|
228
|
+
.max(WORKFLOW_ARTIFACT_LIMITS.maxFieldsPerRequest)
|
|
229
|
+
.optional(),
|
|
177
230
|
})
|
|
178
231
|
.strict();
|
|
179
232
|
export type V4ConversationWorkflowRunArtifactDataParams = z.infer<
|
|
@@ -193,14 +246,19 @@ export const v4ConversationWorkflowRunArtifactDataResultSchema = z
|
|
|
193
246
|
/**
|
|
194
247
|
* 条目原值,**不做预览序列化**:看板的纯函数要按字段路径取数
|
|
195
248
|
* (`ChartSpec.x.field` 形如 "timing.after"),拿到一段 pretty JSON 文本就没法取了。
|
|
196
|
-
*
|
|
197
|
-
*
|
|
249
|
+
* 单条由写入侧的 `REPORT_CAPS.maxItemSerializedBytes` 有界,一页由
|
|
250
|
+
* `WORKFLOW_ARTIFACT_LIMITS.maxPageBytes` 有界。请求带 `fields` 时缺席。
|
|
198
251
|
*/
|
|
199
252
|
item: z.unknown(),
|
|
253
|
+
/**
|
|
254
|
+
* 请求带 `fields` 时:路径 → 值。走不通的路径不在表里(= `undefined`);超过
|
|
255
|
+
* `maxFieldValueBytes` 的值是一段截短的字符串。
|
|
256
|
+
*/
|
|
257
|
+
fields: z.record(z.string(), z.unknown()).optional(),
|
|
200
258
|
})
|
|
201
259
|
.strict(),
|
|
202
260
|
),
|
|
203
|
-
/**
|
|
261
|
+
/** 本页之后仍有条目(页可能因条数或字节上界提前收尾;由存储层判定)。 */
|
|
204
262
|
hasMore: z.boolean(),
|
|
205
263
|
})
|
|
206
264
|
.strict();
|