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.
Files changed (78) hide show
  1. package/NOTICE.md +2 -2
  2. package/README.md +9 -9
  3. package/package.json +4 -4
  4. package/server/account.ts +10 -0
  5. package/server/build-info.ts +1 -1
  6. package/server/discovery/manifest.ts +2 -2
  7. package/server/mapping.ts +31 -0
  8. package/server/provider.ts +5 -18
  9. package/server/runtime/pins.ts +3 -3
  10. package/server/session.ts +7 -10
  11. package/server/unsupported-capabilities.ts +4 -1
  12. package/server/vendor/zcode/NOTICE.md +4 -1
  13. package/server/vendor/zcode/packages/rpc/src/channelClient.ts +1 -0
  14. package/server/vendor/zcode/packages/rpc/src/channelServer.ts +2 -0
  15. package/server/vendor/zcode/packages/rpc/src/channels.shared.ts +1 -0
  16. package/server/vendor/zcode/packages/rpc/src/protocol.ts +3 -2
  17. package/server/vendor/zcode/packages/shared/src/app-runtime-preferences.ts +18 -0
  18. package/server/vendor/zcode/packages/shared/src/attachment-source.ts +5 -0
  19. package/server/vendor/zcode/packages/shared/src/bots.ts +385 -2
  20. package/server/vendor/zcode/packages/shared/src/browser-use/commands.ts +3 -1
  21. package/server/vendor/zcode/packages/shared/src/channel-mention.ts +131 -0
  22. package/server/vendor/zcode/packages/shared/src/conversationSelection.ts +37 -0
  23. package/server/vendor/zcode/packages/shared/src/dynamic-workflow-feature.ts +137 -0
  24. package/server/vendor/zcode/packages/shared/src/env.ts +15 -11
  25. package/server/vendor/zcode/packages/shared/src/highspeed.ts +232 -0
  26. package/server/vendor/zcode/packages/shared/src/mcp-apps/appTools.ts +148 -0
  27. package/server/vendor/zcode/packages/shared/src/mcp-apps/contract.ts +262 -0
  28. package/server/vendor/zcode/packages/shared/src/mcp-apps/instance.ts +12 -0
  29. package/server/vendor/zcode/packages/shared/src/mcp-apps/resourceMime.ts +73 -0
  30. package/server/vendor/zcode/packages/shared/src/mcp-apps/sampling.ts +117 -0
  31. package/server/vendor/zcode/packages/shared/src/mcp-apps/schemas.ts +90 -0
  32. package/server/vendor/zcode/packages/shared/src/model-execution.ts +58 -1
  33. package/server/vendor/zcode/packages/shared/src/oauth.ts +3 -0
  34. package/server/vendor/zcode/packages/shared/src/off-peak-execution.ts +73 -0
  35. package/server/vendor/zcode/packages/shared/src/protocol.ts +78 -4
  36. package/server/vendor/zcode/packages/shared/src/remoteTarget.ts +65 -0
  37. package/server/vendor/zcode/packages/shared/src/request-security-edition/verification-policy.ts +46 -0
  38. package/server/vendor/zcode/packages/shared/src/server-remote.ts +93 -0
  39. package/server/vendor/zcode/packages/shared/src/source-server/compatibility.ts +115 -0
  40. package/server/vendor/zcode/packages/shared/src/source-server/contracts.ts +426 -0
  41. package/server/vendor/zcode/packages/shared/src/source-server/errorCodes.ts +74 -0
  42. package/server/vendor/zcode/packages/shared/src/sqlite-migrations/contract.ts +83 -0
  43. package/server/vendor/zcode/packages/shared/src/subagent-profile.ts +62 -0
  44. package/server/vendor/zcode/packages/shared/src/subagent-runtime-config.ts +51 -0
  45. package/server/vendor/zcode/packages/shared/src/subagents-types.ts +164 -0
  46. package/server/vendor/zcode/packages/shared/src/usage-stats.ts +14 -0
  47. package/server/vendor/zcode/packages/shared/src/validationAppSettings.ts +136 -3
  48. package/server/vendor/zcode/packages/shared/src/version.ts +13 -0
  49. package/server/vendor/zcode/packages/shared/src/zcode-agent-policy.ts +24 -1
  50. package/server/vendor/zcode/packages/shared/src/zcode-protocol/index.ts +697 -36
  51. package/server/vendor/zcode/packages/shared/src/zcode-protocol/trace.ts +11 -0
  52. package/server/vendor/zcode/packages/shared/src/zcode-protocol-legacy-types.ts +14 -2
  53. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/apply.ts +28 -9
  54. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/attachment-ref.ts +3 -0
  55. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/command.ts +73 -53
  56. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/create-session-config.ts +15 -0
  57. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/create-workflow-display.ts +47 -1
  58. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/delta.ts +58 -2
  59. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/input-intent.ts +31 -0
  60. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/rows.ts +28 -13
  61. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/sessions-index-workflow-activity.ts +37 -3
  62. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/sessions-index.ts +36 -0
  63. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/snapshot.ts +21 -0
  64. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/submission.ts +1 -1
  65. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/toolDisplay.ts +32 -4
  66. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/transport.ts +31 -6
  67. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/wire-assembler.ts +4 -1
  68. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/wire-fault.ts +4 -1
  69. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-artifacts.ts +62 -4
  70. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-command-rejections.ts +30 -0
  71. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-row-meta.ts +40 -3
  72. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-run-settings-command.ts +2 -2
  73. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-runs-delta.ts +6 -5
  74. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-runs.ts +103 -26
  75. package/server/vendor/zcode/packages/shared/src/zcode-task-mode-schema.ts +13 -1
  76. package/server/vendor/zcode/packages/shared/src/zcode-task-types-core.ts +31 -2
  77. package/server/vendor/zcode/packages/shared/src/zcodeEndpoint.ts +188 -73
  78. package/server/vendor/zcode/provenance.json +189 -80
@@ -0,0 +1,30 @@
1
+ // 动态工作流命令的拒绝词表与 fault 前缀:从 command.ts 拆出(该文件在 max-lines 上限),
2
+ // 由 command.ts 再导出,既有导入路径不变。
3
+ import { z } from "zod";
4
+
5
+ // startSavedWorkflow 拒绝词表(docs/dynamic-workflow/launch.md「On the agent」):
6
+ // bootstrap handler 铸造 fault code,ui launcher 反查 i18n 文案,两侧共享此枚举避免漂移。
7
+ // invalid_name / not_found:解析阶段;invalid_args:实参校验;compile_failed:analyzeScript 诊断;
8
+ // session_busy:会话有活动 turn;start_failed:port.submit 之前的其它启动失败。
9
+ export const savedWorkflowStartRejectionReasonSchema = z.enum([
10
+ "invalid_name",
11
+ "not_found",
12
+ "invalid_args",
13
+ "compile_failed",
14
+ "session_busy",
15
+ "start_failed",
16
+ ]);
17
+ export type SavedWorkflowStartRejectionReason = z.infer<
18
+ typeof savedWorkflowStartRejectionReasonSchema
19
+ >;
20
+
21
+ // 完整 fault code = 前缀 + reason(如 fault.command.savedWorkflowStartRejected.not_found)。
22
+ // 与 workflowRunResumeRejected 命名空间同族;导出常量供 bootstrap 拼接、ui 前缀匹配。
23
+ export const SAVED_WORKFLOW_START_REJECTED_FAULT_PREFIX =
24
+ "fault.command.savedWorkflowStartRejected." as const;
25
+
26
+ // resumeWorkflowRun 的拒绝:前缀 + 端口 reason(not_found / not_resumable / superseded /
27
+ // already_running / script_missing / script_mismatch / compile_failed);`ack.message` 携带
28
+ // compile_failed 的有界诊断(docs/dynamic-workflow/presentation.md「The run pane」)。
29
+ export const WORKFLOW_RUN_RESUME_REJECTED_FAULT_PREFIX =
30
+ "fault.command.workflowRunResumeRejected." as const;
@@ -64,6 +64,23 @@ export const workflowNotificationMetaSchema = z.discriminatedUnion("kind", [
64
64
  context: z.string().max(4000).optional(),
65
65
  askedAt: z.number().optional(),
66
66
  }),
67
+ // 留白(docs/dynamic-workflow/transcript-and-notifications.md「The payload」的 `hole` 种类):run 走到一个
68
+ // 开着的 `hole<T>()`,那条分支停驻、主代理欠一段代码。`type` 是作者写的类型原文(`Plan`);`before` /
69
+ // `after` 是留白两侧的阶段名;`draftPath` / `line` 指向草稿文件里留白所在的那一行。prompt 与升级问题
70
+ // 同一条 4000 上界,发射侧先截。与契约同名(apps/zcode-cli contracts 侧手工镜像)。
71
+ z.object({
72
+ kind: z.literal("hole"),
73
+ siteId: z.string().min(1).max(64),
74
+ ordinal: z.number().int().nonnegative(),
75
+ name: z.string().min(1).max(128),
76
+ type: z.string().min(1).max(128),
77
+ prompt: z.string().max(4000).optional(),
78
+ draftPath: z.string().max(1024).optional(),
79
+ line: z.number().int().positive().optional(),
80
+ before: z.string().max(128).optional(),
81
+ after: z.string().max(128).optional(),
82
+ reachedAt: z.number().optional(),
83
+ }),
67
84
  // run 级停滞:每个 stall 段一条,不是终态。
68
85
  z.object({
69
86
  kind: z.literal("stall"),
@@ -74,6 +91,21 @@ export const workflowNotificationMetaSchema = z.discriminatedUnion("kind", [
74
91
  ]);
75
92
  export type WorkflowNotificationMeta = z.infer<typeof workflowNotificationMetaSchema>;
76
93
 
94
+ // 主代理闲着、run 还欠着决定时的提醒(docs/dynamic-workflow/transcript-and-notifications.md
95
+ // 「Reminding an idle main agent」):每个 run 一条,只带发出那一刻的计数与最久的等待——每个决定的
96
+ // 原文在它自己那条 escalation / hole 行上,这一行不重复,也不随后续状态翻转。
97
+ //
98
+ // ⚠ 它是 originMeta 上**独立的键**,不是上面那个 union 的成员(同一篇文档「The payload」)。它曾经是
99
+ // `kind: "blocked"`:闭集多一个成员,不认识它的客户端(手机远控页单独部署,可能比 CLI 旧)解析整帧
100
+ // 失败,强制快照里是同一行、再次失败,于是整个会话打不开。不认识的**键**
101
+ // 则只是被剥掉,行退回裸标题。所以新的通知种类一律加新的可选键,这个 union 不再加成员。
102
+ export const workflowBlockedMetaSchema = z.object({
103
+ questions: z.number().int().nonnegative(),
104
+ holes: z.number().int().nonnegative(),
105
+ sinceMs: z.number().int().nonnegative(),
106
+ });
107
+ export type WorkflowBlockedMeta = z.infer<typeof workflowBlockedMetaSchema>;
108
+
77
109
  export const backgroundResultOriginMetaSchema = z.object({
78
110
  // 三个取值与 contracts 的 BackgroundResultOriginMeta 保持同步。
79
111
  // "workflow" 是 dynamic-workflow run(workId ≡ runId),复用整条后台通知管线。
@@ -83,6 +115,9 @@ export const backgroundResultOriginMetaSchema = z.object({
83
115
  // 只在 backgroundSource === "workflow" 的单条通知轮上在场;zod 剥离未知键,
84
116
  // 这里不加即整条链路静默丢——本字段是 manifest 渲染的唯一数据源。
85
117
  workflowNotification: workflowNotificationMetaSchema.optional(),
118
+ // 提醒载荷,与 workflowNotification 至多在场一个。`.catch(undefined)`:将来它的形状变了,认识这个
119
+ // 键的客户端把它当作缺席(裸标题行),而不是丢掉整帧。
120
+ workflowBlocked: workflowBlockedMetaSchema.optional().catch(undefined),
86
121
  });
87
122
  export type BackgroundResultOriginMeta = z.infer<typeof backgroundResultOriginMetaSchema>;
88
123
 
@@ -102,12 +137,14 @@ const workflowSubagentModelTextSchema = z
102
137
  // 设置轮:用户在 run 卡 / 详情页的「配置」里
103
138
  // 改了设置,agent 以同一份脚本修订出新 run,并用一条与直接启动同形的 controlOnly 轮记下这件事。
104
139
  // 这一块说**改了什么**:只有改动过的设置在场;每一项的 from / to 缺一端即那一端是默认
105
- // (模型 = 会话模型,上限 = 本机上限)。`ceiling` 是本机上限,供「13 → 4」这种读法。
140
+ // (模型 = 会话模型,上限 = 默认并发)。`ceiling` 是默认并发 D(键名早于「默认并发」,为兼容旧端保留),
141
+ // 供「默认 13 → 4」这种读法。
106
142
  export const workflowSettingsAmendMetaSchema = z.object({
107
- // 修订自哪个 run。**缺席 = 就地生效**:只改并发上限、run
143
+ // 修订自哪个 run。**缺席 = 就地生效**(docs/dynamic-workflow/concurrency.md):只改并发上限、run
108
144
  // 又在飞时,那次「配置」不停这次 run、也不另起一次,于是没有前驱可指——`runId` 指的就是被调整的
109
145
  // 那一个。渲染端据此只出那一行、不再出卡(同一条 run 画两张卡会读成两次运行)。
110
- // 此字段为可选;生产者和消费者需使用一致的 schema 才能解析就地调整的设置记录。
146
+ // 记录在案的偏斜(与下方 scope / path 同一档):旧桌面上它是必填,就地生效的设置轮在那里 parse
147
+ // 失败、整行被丢——丢的是一行记录,run 卡与运行本身不受影响。
111
148
  predecessorRunId: z.string().min(1).max(128).optional(),
112
149
  subagentModel: z
113
150
  .object({
@@ -8,7 +8,7 @@ import { z } from "zod";
8
8
  import { WORKFLOW_RUNS_LIMITS } from "./workflow-runs.js";
9
9
 
10
10
  /**
11
- * 命令载荷。两项设置守工具的同一条三态规则:省略 = 沿用,`null` = 回到默认(会话模型 / 本机上限),
11
+ * 命令载荷。两项设置守工具的同一条三态规则:省略 = 沿用,`null` = 回到默认(会话模型 / 默认并发),
12
12
  * 值 = 设定。GUI 只发用户改过的那几项。刻意不携 baseRevision:与 cancelBackgroundWork /
13
13
  * resumeWorkflowRun 同类(workflowRuns 面免 revision)。
14
14
  */
@@ -22,7 +22,7 @@ export const amendWorkflowRunSettingsPayloadSchema = z.object({
22
22
  .max(WORKFLOW_RUNS_LIMITS.maxSubagentModelLength)
23
23
  .nullable()
24
24
  .optional(),
25
- /** 同时运行的子代理上限;`null` = 解除本 run 自己的界(回到本机上限)。agent 侧钳到 `[1, 天花板]`。 */
25
+ /** 同时运行的子代理上限;`null` = 解除本 run 自己的界(回到默认并发)。没有上限:高于默认也行。 */
26
26
  maxConcurrency: z.number().int().min(1).nullable().optional(),
27
27
  });
28
28
  export type AmendWorkflowRunSettingsPayload = z.infer<typeof amendWorkflowRunSettingsPayloadSchema>;
@@ -14,7 +14,7 @@
14
14
  // diff 的第一道闸是引用比较,深比较只发生在真正动过的那条 run 上。
15
15
  // 3. 正确性绝不依赖引用:引用相等只是快路径,不等时一律退回结构比较。
16
16
  //
17
- // 契约:对任意一对 reducer 产出的 (prior, next),
17
+ // 契约(被 property 测试钉住):对任意一对 reducer 产出的 (prior, next),
18
18
  // `JSON.stringify(applyAll(带 prior 的快照, diff(prior, next)).workflowRuns) === JSON.stringify(next)`
19
19
  // ——**逐字节**,不只是深相等。键序因此是本模块的一等公民,见 {@link canonicalWorkflowRun}。
20
20
 
@@ -42,7 +42,7 @@ import {
42
42
  * 重建 run 时没有那段历史。两边各自按这张表重排一次,序就对齐了——这也是为什么 reducer 的
43
43
  * 出口同样要走一遍本函数(那是一处,不是两处)。
44
44
  *
45
- * 键序从 schema 派生,避免独立维护字段表导致生产者和消费者使用不同的 run 结构。
45
+ * 从 schema 派生而不是手抄:手抄一份 run 字段表曾经酿成过事故(见 delta.ts 的 header 注释)。
46
46
  */
47
47
  const WORKFLOW_RUN_KEYS = Object.keys(workflowRunSchema.shape) as (keyof WorkflowRunState)[];
48
48
  const WORKFLOW_RUN_KEY_SET: ReadonlySet<string> = new Set<string>(WORKFLOW_RUN_KEYS);
@@ -412,7 +412,8 @@ function upsertWorkflowRunEntries<T extends { siteId: string; ordinal: number }>
412
412
  * 每次淘汰都让出一个位子,一个够宽的 flush 窗口里被删掉的不同键因此可以多于 1024 条。
413
413
  * 淘汰之前这不可能:表只增不减,一世之内被碰过的不同键最多就是表界那么多。
414
414
  *
415
- * 超界载荷会让整个 patch 解析失败、整帧被丢;publisher 的两道限制(500 op / 1 MiB)在这个
415
+ * 超界的载荷不会被剥掉一个键,它让整个 patch 解析失败、整帧被丢、那条订阅从此静默
416
+ * (workflow-runs.ts 记的那次事故的形状),而 publisher 的两道兜底(500 op / 1 MiB)在这个
416
417
  * 量级上都不会响。四张表都查是因为这道闸很便宜,而且不该依赖上面那条论证一直成立。
417
418
  *
418
419
  * 合并是**优化**,所以拒绝合并永远是安全的:两条 op 各自都在界内,逐条投递的终态一个字节都不差。
@@ -430,8 +431,8 @@ export function workflowRunUpdateWithinWireBounds(
430
431
  }
431
432
 
432
433
  /**
433
- * 条目表的容量上限,默认使用 {@link WORKFLOW_RUNS_LIMITS}。
434
- * 增量应用和合并必须使用一致的上限。
434
+ * 条目表的线上界。只为测试留的注入口(默认就是 {@link WORKFLOW_RUNS_LIMITS}):真界是 1024,
435
+ * 而「合并被拒」这条路径在小界下才走得到,property 测试需要它真的被走到。
435
436
  */
436
437
  export interface WorkflowRunWireBounds {
437
438
  readonly maxActors: number;
@@ -15,10 +15,15 @@ export const WORKFLOW_RUNS_LIMITS = {
15
15
  /** 最近若干个 run;超出按最旧淘汰。 */
16
16
  maxRuns: 8,
17
17
  /**
18
- * actors 与 nodes 使用相同的容量上限,避免节点可展示而所属子代理提前被截断。
19
- * 键级增量使每个事件只传输改动部分;容量上限用于限制单条 run 的投影大小,
20
- * 并限制异常脚本持续创建条目带来的资源消耗。跨 run 的总量由 {@link maxTotalEntries} 控制。
21
- * 这是展示状态的容量限制,不限制引擎实际运行的子代理数量。
18
+ * actors 与 nodes **同界**。每个节点都属于某个 actor,所以 nodes 的界已经隐含了 actor
19
+ * 数的量级;把 actors 压得更低(曾是 32)只会让一个平常的 50 路 fan-out 在检视器里
20
+ * 静默少掉 18 个子代理,而节点表、引擎、图三层都装得下。
21
+ *
22
+ * 2026-09-20 从 256 抬到 1024:原来那条界的理由是「状态键按事件整体重发,无界即 O(N²)
23
+ * 字节」,而键级增量(workflow-runs-delta.ts)落地后每条事件只发改动量,那条理由没了。
24
+ * 剩下的两条理由都还在,而且都不需要 256 这么紧:挡住「疯掉的脚本在循环里 `agent()`」,
25
+ * 以及让单条 run 的字节数有个上界。**跨 run** 的总量另有 {@link maxTotalEntries} 管。
26
+ * 它仍然不是产品意义上的子代理上限。
22
27
  */
23
28
  maxActors: 1_024,
24
29
  maxNodes: 1_024,
@@ -32,7 +37,7 @@ export const WORKFLOW_RUNS_LIMITS = {
32
37
  */
33
38
  maxTotalEntries: 6_144,
34
39
  /**
35
- * 详情页 Results 区的**展示**预算,刻意远小于引擎的 run 级 report 上限(256 条):
40
+ * 详情页 Results 区的**展示**预算,刻意远小于引擎的 run 级 report 上限(`REPORT_CAPS.maxItemsPerRun`):
36
41
  * 协议线上的界是展示预算,引擎的界才是契约,两者不必相等。超出这个界的条目仍在
37
42
  * journal 里(`dwf_node.kind = "report"`),只是不进这条高频状态键。
38
43
  */
@@ -56,7 +61,12 @@ export const WORKFLOW_RUNS_LIMITS = {
56
61
  */
57
62
  maxSubagentModelLength: 256,
58
63
  /**
59
- * `run.concurrencyCeiling` 的上界。天花板按 `min(16, cores − 2)` 推导,这条界只挡坏载荷
64
+ * actor `sessionId` 的线上上界。会话 id 是铸出来或读出来的标识,不是展示文本:超界的值
65
+ * 整条丢弃而不是截断——一个被砍短的会话 id 指向的是另一条(或不存在的)会话。
66
+ */
67
+ maxSessionIdLength: 256,
68
+ /**
69
+ * `run.concurrencyCeiling`(默认并发 D)的上界。D 按 `max(4, min(16, cores − 2))` 推导,这条界只挡坏载荷
60
70
  * (reducer 读到界外的值当作读不出,沿用已知值)。
61
71
  */
62
72
  maxConcurrencyCeiling: 1_024,
@@ -80,6 +90,16 @@ export const WORKFLOW_RUNS_LIMITS = {
80
90
  maxPhases: 32,
81
91
  /** 阶段名的线上上界,与 display 的 `CREATE_WORKFLOW_GRAPH_MAX_NAME_CHARS` 同值(UI 按名字关联两边)。 */
82
92
  maxPhaseNameLength: 128,
93
+ /**
94
+ * 一条 run 记得的留白条数(docs/dynamic-workflow/presentation.md「Holes on the timeline」)。与 display
95
+ * 载荷的 `holes` 同界:时间线上画不出的留白,投影里也不必记。一个留白就是一个阶段,所以它天然
96
+ * 不会多过 `maxPhases`。
97
+ */
98
+ maxHoles: 32,
99
+ /** 留白类型原文(`Plan`)的线上上界,与 display 的同名字段同值。 */
100
+ maxHoleTypeLength: 128,
101
+ /** 留白的提示语在 run 状态里的上限(侧板等待体要画它);超长时截到 499 字加 `…`。 */
102
+ maxHolePromptLength: 500,
83
103
  /**
84
104
  * 一次 ask 的**任务摘要**上界(`node-queued` 的 `instructionsHead`)。与引擎侧的
85
105
  * `INSTRUCTIONS_HEAD_MAX_CHARS` 同值:那一头已经按这条界切好,这里是线上的第二道闸。
@@ -101,7 +121,8 @@ export const WORKFLOW_RUNS_LIMITS = {
101
121
  * **旧消费者**(没有 `workflowRunDeltas` 能力的那一代)编译进去的 actors / nodes 界。
102
122
  *
103
123
  * ⚠ 这两个数**永远不能改**:它们不是我们的界,是别人二进制里的校验界。超界的载荷不会被
104
- * 剥掉一个键——它会让整个 `state.updated` patch 解析失败、整帧被丢,那条订阅从此静默。所以给这类订阅者
124
+ * 剥掉一个键——它会让整个 `state.updated` patch 解析失败、整帧被丢,那条订阅从此静默
125
+ * (2026-09-04 的那次 `fault.subscription.recoveryFailed` 就是这个形状)。所以给这类订阅者
105
126
  * 发帧前必须先过 `clampWorkflowRunsForLegacy`。
106
127
  *
107
128
  * 「前 256 条」不是随手取的:旧归约触界时是**拒新**,它产出的恰好就是最早的那 256 条。
@@ -112,7 +133,8 @@ export const WORKFLOW_RUNS_LEGACY_LIMITS = {
112
133
  } as const;
113
134
 
114
135
  /**
115
- * 一个被控制流进入过的阶段(`phase("…")` 标记)。`name` 是作者原词(时间线按它关联 display 的 `phases[].name`);`rounds` 是进入
136
+ * 一个被控制流进入过的阶段(`phase("…")` 标记;docs/dynamic-workflow/presentation.md
137
+ * 2026-09-05)。`name` 是作者原词(时间线按它关联 display 的 `phases[].name`);`rounds` 是进入
116
138
  * 次数——单调(reducer 取 max),所以 resume 重放的前缀不会把它加倍。
117
139
  */
118
140
  export const workflowRunPhaseSchema = z.object({
@@ -122,7 +144,7 @@ export const workflowRunPhaseSchema = z.object({
122
144
  export type WorkflowRunPhase = z.infer<typeof workflowRunPhaseSchema>;
123
145
 
124
146
  /**
125
- * 界在某个**出生阶段**上花掉了多少。
147
+ * 界在某个**出生阶段**上花掉了多少(docs/dynamic-workflow/presentation.md「Reduction」)。
126
148
  *
127
149
  * 两个计数器({@link workflowRunUsageSchema} 的 `nodesUnlisted` / `nodesUnlistedSettled`)说得出
128
150
  * 一条 run 总共少列了多少,说不出少在**哪一站**——而读面是按站画的:一个站点的花名册、计数环和
@@ -144,7 +166,7 @@ export const workflowRunUnlistedPhaseSchema = z.object({
144
166
  export type WorkflowRunUnlistedPhase = z.infer<typeof workflowRunUnlistedPhaseSchema>;
145
167
 
146
168
  /**
147
- * run 级用量:观察面,不是控制面。`spentTokens` 直接取
169
+ * run 级用量:观察面,不是控制面(docs/dynamic-workflow/authoring.md)。`spentTokens` 直接取
148
170
  * 引擎 `usage-updated` 事件携带的已花总量(与 `dwf_run.spent_tokens` 同一同步步骤写入,
149
171
  * 二者永远相等);`nodesUsed` 是本 run 已派发(dispatched)的节点数,由节点事件计数——
150
172
  * 没有任何上限可以拿来反算,也不需要。
@@ -153,7 +175,8 @@ export const workflowRunUsageSchema = z.object({
153
175
  spentTokens: z.number().int().nonnegative(),
154
176
  nodesUsed: z.number().int().nonnegative(),
155
177
  /**
156
- * 撞上 {@link WORKFLOW_RUNS_LIMITS.maxNodes} 被**拒之表外**的实例数,以及其中已结算的条数。`truncated` 只说得出「有东西没进来」,说不出
178
+ * 撞上 {@link WORKFLOW_RUNS_LIMITS.maxNodes} 被**拒之表外**的实例数,以及其中已结算的条数
179
+ * (docs/dynamic-workflow/presentation.md)。`truncated` 只说得出「有东西没进来」,说不出
157
180
  * 有多少——于是一个 3000 路 fan-out 的 run 在读面上会显示成「1024 步」,那是一句假话。
158
181
  *
159
182
  * 两条都是**加出来**的计数(被拒实例根本不在表里,没有可去重的身份),所以归约只在事件
@@ -175,14 +198,17 @@ export type WorkflowRunUsage = z.infer<typeof workflowRunUsageSchema>;
175
198
  * - `completed`:其余(全部节点已结算,或 run 已终态)。
176
199
  * 不存在可观察的 actor 级 failed:某次 ask 失败仍是「它的活干完了」,结果在节点上。
177
200
  *
178
- * `sessionId` 是 actor 会话 id(phase 5 的 transcript 下钻直接读它)。它同样不在 Boundary C 上,
179
- * 而是 run service 按 `(runId, actorRef)` 确定性铸造的同一个函数算出来的。
201
+ * `sessionId` 是**持有这个子代理转录的那条会话**(transcript 下钻直接读它,
202
+ * docs/dynamic-workflow/presentation.md「Subagent transcripts」)。通常是 run service 按
203
+ * `(runId, actorRef)` 确定性铸造的本 run 会话;修订 run 里一个到目前为止全部答案都来自导入
204
+ * 命中的子代理没有自己的会话(命中不建会话),这时它是前驱里被读取的那条会话,由缓存结算的
205
+ * `sourceSessionId` 带来,第一次 live 派发之后换回本 run 的会话(种子已抄入前缀)。
180
206
  */
181
207
  export const workflowRunActorSchema = z.object({
182
208
  siteId: z.string().min(1).max(64),
183
209
  ordinal: z.number().int().nonnegative(),
184
210
  name: z.string().min(1).max(WORKFLOW_RUNS_LIMITS.maxActorNameLength).optional(),
185
- sessionId: z.string().min(1).max(256).optional(),
211
+ sessionId: z.string().min(1).max(WORKFLOW_RUNS_LIMITS.maxSessionIdLength).optional(),
186
212
  status: z.enum(["waiting", "running", "completed"]),
187
213
  /**
188
214
  * 这个实例**出生**在哪个阶段:它的 ordinal 被铸造的那一刻,控制流所在的 `phase("…")` 标记名。UI 按**名字**与 `phases[].name`
@@ -192,6 +218,12 @@ export const workflowRunActorSchema = z.object({
192
218
  * 或者发事件的是不带这个键的旧 CLI。
193
219
  */
194
220
  phaseName: z.string().min(1).max(WORKFLOW_RUNS_LIMITS.maxPhaseNameLength).optional(),
221
+ /**
222
+ * 这个子代理的模型:persona 点名了模型时,宿主按本 run 的绑定表解析出的规范串
223
+ * `providerId/modelId[$level]`(docs/dynamic-workflow/presentation.md「Reduction」)。缺席即它跑在
224
+ * run 的子代理模型上(或发事件的是不带这个键的旧 CLI)。与 `subagentModel` 同界。
225
+ */
226
+ model: z.string().min(1).max(WORKFLOW_RUNS_LIMITS.maxSubagentModelLength).optional(),
195
227
  });
196
228
  export type WorkflowRunActor = z.infer<typeof workflowRunActorSchema>;
197
229
 
@@ -275,16 +307,17 @@ export type WorkflowRunNode = z.infer<typeof workflowRunNodeSchema>;
275
307
  * 实际并发是**两者取小**:
276
308
  *
277
309
  * - `cap`:本 run 所在 provider key 的**共享**闸门现状(治理器按 key 分桶、按 run 扇出,
278
- * 随 `concurrency-changed` 移动);`ceiling`:CPU 推导的天花板。事件本身不带 ceiling,
279
- * 归约按该 run 见过的最大 `previous` / `next` 推导(桶从天花板起步,所以第一条事件的
280
- * `previous` 就是它;只降不升的序列里它也恒是最大值)。
310
+ * 随 `concurrency-changed` 移动)。
311
+ * - `ceiling`:默认并发 D(线上键名早于「默认并发」,旧端还在读它,故保留)。它是参照线不是上限:
312
+ * 共享 cap 可以自动长到 2D,用户也可以把本 run 调到 D 之上。已知 `run.concurrencyCeiling` 时就取
313
+ * 它;只有老 CLI(不发 D)才退回「见过的最大 `previous` / `next`」。
281
314
  * - `limit`:本 run **自己的**界(`CreateWorkflow` / `AmendWorkflow` 的 `max_concurrency`
282
- * 落到 `caps.maxConcurrency`),随 `run-started` 到达、整条 run 不动。**只在低于天花板时
283
- * 在场**:跑在天花板上的 run 与从前一模一样,一个键都不多。
315
+ * 落到 `caps.maxConcurrency`),随 `run-started` 到达、就地 retune 时随 `run-caps-changed` 移动。
316
+ * **只在 ≠ D 时在场**(高于低于都算):跑在默认上的 run 与从前一模一样,一个键都不多。
284
317
  * - `cooldownMs`:带 Retry-After 的限流冻结新派发的时长,**相对量**(同 `retryInMs` 的理由);
285
318
  * `idle_reset` 与 run 终态清掉它。
286
319
  *
287
- * UI 只在 `min(cap, limit) < ceiling` 时显示读数(见 workflowRunConcurrencyView)。
320
+ * UI 的读数规则见 workflowRunConcurrencyView(docs/dynamic-workflow/concurrency.md「What the user sees」)。
288
321
  */
289
322
  export const workflowRunConcurrencySchema = z.object({
290
323
  key: z.string().min(1).max(WORKFLOW_RUNS_LIMITS.maxConcurrencyKeyLength).optional(),
@@ -358,6 +391,31 @@ export const workflowRunPendingQuestionSchema = z.object({
358
391
  });
359
392
  export type WorkflowRunPendingQuestion = z.infer<typeof workflowRunPendingQuestionSchema>;
360
393
 
394
+ /**
395
+ * 一个留白(docs/dynamic-workflow/authoring.md「Holes」;投影侧见 docs/dynamic-workflow/presentation.md
396
+ * 「Holes on the timeline」):脚本里一处 `hole<T>("名字")`,run 走到它就停驻那条分支、等主代理用
397
+ * `FillWorkflowHole` 补上代码。身份是**站点 id**(名字键 `hole#<8 位十六进制>`):同一站点在循环里可以停驻多个 ordinal,
398
+ * 表里只留最新的那一个——补全是按站点一次性的,读面要说的也只是「这个留白等着 / 补上了」。
399
+ *
400
+ * `state`:`waiting` = 引擎里有停驻的 deferred(真相在 CLI 进程内,与 pendingQuestions 同族,所以新的
401
+ * 一世与终态都把它剥掉);`filled` = 补全已接进 run(事实在 journal 的 `hole-filled` 事件里,跨世保留)。
402
+ * `type` 是作者写的类型原文;载荷不带它时缺席,读面退回 display 载荷的 `holes[].type` 按站点 id 取。
403
+ * `since` 是到达时刻(epoch 毫秒,由事件携带——本模块无时钟),`filledAt` / `filledBy` 随 `hole-filled` 到达。
404
+ */
405
+ export const workflowRunHoleSchema = z.object({
406
+ siteId: z.string().min(1).max(64),
407
+ ordinal: z.number().int().nonnegative(),
408
+ name: z.string().min(1).max(WORKFLOW_RUNS_LIMITS.maxPhaseNameLength),
409
+ type: z.string().min(1).max(WORKFLOW_RUNS_LIMITS.maxHoleTypeLength).optional(),
410
+ /** 主代理收到的提示语(`hole-reached` 载荷的 `prompt`),已按 {@link WORKFLOW_RUNS_LIMITS.maxHolePromptLength} 截断。 */
411
+ prompt: z.string().min(1).max(WORKFLOW_RUNS_LIMITS.maxHolePromptLength).optional(),
412
+ state: z.enum(["waiting", "filled"]),
413
+ since: z.number().int().nonnegative().optional(),
414
+ filledAt: z.number().int().nonnegative().optional(),
415
+ filledBy: z.string().min(1).max(128).optional(),
416
+ });
417
+ export type WorkflowRunHole = z.infer<typeof workflowRunHoleSchema>;
418
+
361
419
  export const workflowRunSchema = z.object({
362
420
  runId: z.string().min(1).max(128),
363
421
  /** 发起该 run 的 CreateWorkflow 工具调用(工具卡 → 详情页的关联键)。 */
@@ -411,16 +469,18 @@ export const workflowRunSchema = z.object({
411
469
  .max(WORKFLOW_RUNS_LIMITS.maxPendingQuestions)
412
470
  .optional(),
413
471
  /**
414
- * 并发现状(见 {@link workflowRunConcurrencySchema})。只在**两条界里有一条低于天花板**时
415
- * 在场:收到过 `concurrency-changed`(共享桶被限流压低),或 `run-started` 带来一个低于天花板
416
- * 的 `limit`(用户给这次 run 定了上限)。两者都没有的 run 一直跑在天花板上,没有可说的。
472
+ * 并发现状(见 {@link workflowRunConcurrencySchema})。收到过 `concurrency-changed`(共享桶动过),
473
+ * 或 `run-started` / `run-caps-changed` 带来一个 ≠ 默认并发的 `limit`(用户给这次 run 定了界)时
474
+ * 在场。两者都没有的 run 一直跑在默认上,没有可说的。
417
475
  * optional 的理由与 `reports` / `pendingQuestions` 同。
418
476
  */
419
477
  concurrency: workflowRunConcurrencySchema.optional(),
420
478
  /**
421
- * 本机的并发天花板(`run-started` 载荷的 `concurrencyCeiling`,CLI 铸载荷时拼进去的进程事实)。
422
- * 与 `concurrency.ceiling` 不同:那是读数芯片自己的水位,只随芯片在场;这一个**只要读得到就在**,
423
- * 不论本 run 是否低于它——「配置」弹层的步进器停在这里。optional 的理由与 `concurrency` 同:老 CLI 不发,少一个键是退化。
479
+ * 默认并发 D(`run-started` 载荷的 `concurrencyCeiling`,CLI 铸载荷时拼进去的进程事实;键名早于
480
+ * 「默认并发」这个概念,为兼容旧端保留)。它是起点不是上限。与 `concurrency` 不同,它**只要读得到
481
+ * 就在**,不论本 run 有没有自己的界——「配置」弹层据它写「默认 N」、判「等于默认 = 不设自己的界」
482
+ * (docs/dynamic-workflow/concurrency.md「Protocol state」)。optional 的理由与 `concurrency` 同:
483
+ * 老 CLI 不发,少一个键是退化。
424
484
  */
425
485
  concurrencyCeiling: z
426
486
  .number()
@@ -483,6 +543,23 @@ export const workflowRunSchema = z.object({
483
543
  .array(z.array(z.number().int().nonnegative()).max(WORKFLOW_RUNS_LIMITS.maxPhases))
484
544
  .max(WORKFLOW_RUNS_LIMITS.maxPhases)
485
545
  .optional(),
546
+ /**
547
+ * `phaseNames` 里哪几站是**还开着的留白**(下标表,`run-launched.holes` / `hole-filled.holes`;
548
+ * docs/dynamic-workflow/presentation.md「Holes on the timeline」的「The model」)。侧栏只从 `phaseNames`
549
+ * 画迷你轨道,没有 display 载荷可查,所以这张表是它把留白画成虚线灯的唯一依据。补全之后那一站
550
+ * 不再是留白,表随 `hole-filled` 整张换新。依附 `phaseNames`:后者不在场时它一定不在场;一个都没有
551
+ * 时同样缺席。
552
+ */
553
+ phaseHoles: z
554
+ .array(z.number().int().nonnegative())
555
+ .max(WORKFLOW_RUNS_LIMITS.maxPhases)
556
+ .optional(),
557
+ /**
558
+ * 这条 run 的留白(见 {@link workflowRunHoleSchema}),按首次到达顺序。**零条时整个键缺席**,与
559
+ * `pendingQuestions` 同规。optional 的理由与 `reports` 逐字相同:往已有状态键追加字段,旧 CLI 不发它
560
+ * 时少一个键是退化,不是整帧被丢。
561
+ */
562
+ holes: z.array(workflowRunHoleSchema).max(WORKFLOW_RUNS_LIMITS.maxHoles).optional(),
486
563
  /**
487
564
  * 界在各个出生阶段上花掉了多少(见 {@link workflowRunUnlistedPhaseSchema})。**一格都没有时
488
565
  * 整个键缺席**。表长比 `maxPhases` 多一格:那一格是「无阶段」,它与具名阶段共用同一张表。
@@ -3,4 +3,16 @@ import { z } from "zod";
3
3
  // 该 schema 同时被 validation 聚合入口和 legacy protocol 使用,必须放在无反向依赖的叶子模块。
4
4
  // 根因:protocol 从 validation 导入它,而 validation 又导入 protocol 的资源采样 schema,
5
5
  // ESM/Jiti 在 clean Docker 中会先读到尚未初始化的 binding,导致 `.optional()` 启动即崩溃。
6
- export const zcodeTaskModeSchema = z.enum(["yolo", "plan", "edit", "auto", "autoEdit", "build"]);
6
+ export const zcodeTaskModeSchema = z.enum([
7
+ "default",
8
+ "yolo",
9
+ "guarded",
10
+ "plan",
11
+ "edit",
12
+ "acceptEdits",
13
+ "auto",
14
+ "dontAsk",
15
+ "bypassPermissions",
16
+ "autoEdit",
17
+ "build",
18
+ ]);
@@ -146,7 +146,18 @@ export interface ZCodeTaskGoalChangedPatch {
146
146
  }
147
147
  // ---- ZCode task 模式 ----
148
148
 
149
- export type ZCodeTaskMode = "yolo" | "plan" | "edit" | "auto" | "autoEdit" | "build";
149
+ export type ZCodeTaskMode =
150
+ | "default"
151
+ | "yolo"
152
+ | "guarded"
153
+ | "plan"
154
+ | "edit"
155
+ | "acceptEdits"
156
+ | "auto"
157
+ | "dontAsk"
158
+ | "bypassPermissions"
159
+ | "autoEdit"
160
+ | "build";
150
161
 
151
162
  export type ZCodeOffPeakRunType = "init" | "resume";
152
163
 
@@ -198,7 +209,8 @@ export interface ZCodePromptFileAttachment {
198
209
  mimeType: string;
199
210
  sizeBytes: number;
200
211
  /** 附件来源;clipboard-text 表示由长文本粘贴落盘生成,agent 只应按临时文件引用处理。 */
201
- sourceKind?: "clipboard-text";
212
+ sourceKind?: "clipboard-text" | "topic-history";
213
+ messageCount?: number;
202
214
  /** 旧版/无路径环境的兼容回退;新桌面 GUI 不再为普通文件发送 base64。 */
203
215
  dataBase64?: string;
204
216
  /** 无本地路径时的小文本回退;有 localPath 时由 agent 自行读取。 */
@@ -284,6 +296,10 @@ export interface ZCodeTaskMeta {
284
296
  * 导致任务列表、快照和日志互相串读。这里补充 workspaceIdentity 参与隔离。
285
297
  */
286
298
  workspaceIdentity?: string;
299
+ /** Controller/renderer 任务列表投影使用;不参与 workspace identity 或持久化 key。 */
300
+ sourceServerId?: string;
301
+ /** Source directory 的用户可读名称;缺失时 UI 回退 sourceServerId。 */
302
+ serverDisplayName?: string;
287
303
  /** app-owned workspace 分类;缺省为 project,不参与 workspaceKey。 */
288
304
  workspacePurpose?: import("./workspacePurpose.js").WorkspacePurpose;
289
305
  createdAt: number;
@@ -338,11 +354,19 @@ export interface ZCodeTaskMeta {
338
354
  * 但拿不到错误正文,用户会以为发送没有触发。这里把失败原因随 task meta 一起持久化。
339
355
  */
340
356
  lastError?: ZCodeTaskLastError;
357
+ /** task 级历史修复状态,用于避免读 snapshot 时重复执行一次性迁移逻辑。 */
358
+ repairState?: ZCodeTaskRepairState;
341
359
  /** 任务级文件改动摘要,仅用于列表/标题展示,真实回滚仍以 fileChanges 为准 */
342
360
  changeSummary?: ZCodeTaskChangeSummary;
343
361
  /** zcode-cli /goal 会话目标;null 表示已显式清空。 */
344
362
  target?: ZCodeTaskGoal | null;
345
363
  }
364
+ export interface ZCodeTaskRepairState {
365
+ /** Claude 原生日志补齐历史 snapshot assistant 正文的修复版本。 */
366
+ claudeNativeSnapshotAssistantContentVersion?: number;
367
+ /** Codex 原生日志补齐历史 snapshot 子 agent 工具块的修复版本。 */
368
+ codexNativeSnapshotSubagentToolsVersion?: number;
369
+ }
346
370
  export interface ZCodeTaskChangeSummary {
347
371
  /** 整个任务里涉及过的唯一文件数 */
348
372
  fileCount: number;
@@ -562,8 +586,10 @@ export interface ZCodeToolCall {
562
586
  title: string;
563
587
  /** agent ToolCall 原始 payload,调试协议字段时以此为准 */
564
588
  raw: unknown;
589
+ pluginMetadata?: { pluginId: string; component: "mcp" };
565
590
  /** Skill resolved metadata;只用于 telemetry attribution。 */
566
591
  skillMetadata?: {
592
+ name?: string;
567
593
  qualifiedName?: string;
568
594
  pluginId?: string;
569
595
  source?: "agents" | "zcode" | "bundled" | "plugin" | "remote";
@@ -591,8 +617,10 @@ export interface ZCodeToolCallUpdate {
591
617
  error?: string;
592
618
  /** agent ToolCallUpdate 原始 payload,调试协议字段时以此为准 */
593
619
  raw: unknown;
620
+ pluginMetadata?: { pluginId: string; component: "mcp" };
594
621
  /** Skill resolved metadata;只用于 telemetry attribution。 */
595
622
  skillMetadata?: {
623
+ name?: string;
596
624
  qualifiedName?: string;
597
625
  pluginId?: string;
598
626
  source?: "agents" | "zcode" | "bundled" | "plugin" | "remote";
@@ -700,6 +728,7 @@ export interface ZCodeTaskComplete {
700
728
  usage?: ZCodeUsage;
701
729
  }
702
730
  export interface ZCodeTaskRunStarted {
731
+ inputOrigin?: "desktop" | "mobile";
703
732
  type: "task_run_started";
704
733
  taskId: string;
705
734
  traceId: TraceId;