@sema-agent/server 7.125.1 → 7.127.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 (275) hide show
  1. package/MIGRATION.md +229 -2
  2. package/README.md +2 -2
  3. package/README.zh-CN.md +2 -2
  4. package/USAGE.md +346 -145
  5. package/deploy/sema-up/README.md +1 -1
  6. package/deploy/sema-up/chart/templates/networkpolicy-sandbox.yaml +57 -0
  7. package/deploy/sema-up/chart/templates/sandbox-rbac.yaml +32 -1
  8. package/deploy/sema-up/chart/templates/server-v4pro.yaml +3 -2
  9. package/deploy/sema-up/chart/templates/server.yaml +2 -1
  10. package/deploy/sema-up/chart/values.yaml +22 -0
  11. package/deploy/sema-up/cluster-up.sh +10 -10
  12. package/deploy/sema-up/kube-up.sh +20 -13
  13. package/deploy/sema-up/lib/dataroot.sh +1 -1
  14. package/deploy/sema-up/lib/envf.sh +5 -5
  15. package/deploy/sema-up/lib/flagshape.sh +5 -5
  16. package/deploy/sema-up/lib/lanes.py +1 -1
  17. package/deploy/sema-up/lib/node-addrs.sh +42 -0
  18. package/deploy/sema-up/lib/parse-machines.py +2 -2
  19. package/deploy/sema-up/lib/shell-access.sh +1 -1
  20. package/deploy/sema-up/preflight.sh +4 -4
  21. package/deploy/sema-up/sema-up.sh +33 -33
  22. package/deploy/sema-up/smoke.sh +3 -3
  23. package/deploy/sema-up/stack/compose.yaml +1 -1
  24. package/deploy/sema-up/stack/sema-stack-cluster.yaml +26 -3
  25. package/dist/approval-card.d.ts +20 -4
  26. package/dist/approval-content-kind.d.ts +38 -22
  27. package/dist/approval-content-kind.js +20 -4
  28. package/dist/audit.js +3 -0
  29. package/dist/bench/s1/arms.d.ts +9 -37
  30. package/dist/bench/s1/arms.js +4 -61
  31. package/dist/bench/s1/live-deps.d.ts +4 -10
  32. package/dist/bench/s1/live-deps.js +1 -222
  33. package/dist/bench/s1/oracle.d.ts +1 -1
  34. package/dist/bench/s1/reviewer.d.ts +1 -1
  35. package/dist/bench/s1/row.d.ts +5 -7
  36. package/dist/bench/s1/row.js +0 -2
  37. package/dist/bench/s1/run-firm.d.ts +2 -2
  38. package/dist/bench/s1/run-firm.js +1 -4
  39. package/dist/bench/s1/runner-ctx.d.ts +17 -29
  40. package/dist/bench/s1/runner-ctx.js +3 -7
  41. package/dist/bench/s1/tasks.d.ts +9 -46
  42. package/dist/bench/s1/tasks.js +4 -63
  43. package/dist/boot/execution-env.d.ts +38 -26
  44. package/dist/boot/execution-env.js +87 -41
  45. package/dist/boot/leader.d.ts +2 -1
  46. package/dist/boot/memory-boundary.d.ts +1 -1
  47. package/dist/boot/memory-consolidation.d.ts +37 -3
  48. package/dist/boot/memory-consolidation.js +18 -2
  49. package/dist/boot/parked-revive-gate.d.ts +0 -3
  50. package/dist/boot/parked-revive-gate.js +2 -2
  51. package/dist/boot/resolve-spec.d.ts +0 -2
  52. package/dist/boot/resolve-spec.js +12 -17
  53. package/dist/boot/retention-lane.d.ts +12 -1
  54. package/dist/boot/retention-lane.js +2 -0
  55. package/dist/boot/runner-deps.d.ts +7 -6
  56. package/dist/boot/runner-deps.js +5 -16
  57. package/dist/boot/session-faces.d.ts +7 -2
  58. package/dist/boot/session-faces.js +37 -1
  59. package/dist/boot/stage-03-store-backend.d.ts +1 -0
  60. package/dist/boot/stage-03-store-backend.js +2 -2
  61. package/dist/boot/stage-04-stores.d.ts +1 -0
  62. package/dist/boot/stage-05-execution-env.d.ts +2 -2
  63. package/dist/boot/stage-05-execution-env.js +7 -4
  64. package/dist/boot/stage-06-runners.d.ts +2 -3
  65. package/dist/boot/stage-06-runners.js +13 -9
  66. package/dist/boot/stage-07-capability-layer.d.ts +2 -3
  67. package/dist/boot/stage-07-capability-layer.js +3 -4
  68. package/dist/boot/stage-08-reapers.d.ts +2 -3
  69. package/dist/boot/stage-08-reapers.js +2 -2
  70. package/dist/boot/stage-09-leader.d.ts +2 -3
  71. package/dist/boot/stage-09-leader.js +2 -1
  72. package/dist/boot/stage-10-http-server.d.ts +2 -3
  73. package/dist/boot/stage-10-http-server.js +2 -2
  74. package/dist/boot/stores.d.ts +1 -0
  75. package/dist/boot/stores.js +4 -3
  76. package/dist/boot/web-admin.d.ts +5 -0
  77. package/dist/boot/web-admin.js +28 -14
  78. package/dist/brain.d.ts +2 -2
  79. package/dist/brain.js +1 -1
  80. package/dist/capabilities/builtin-tools.d.ts +2 -1
  81. package/dist/capabilities/composite-members.d.ts +1 -1
  82. package/dist/capabilities/composite-members.js +1 -1
  83. package/dist/capabilities/hands-lane.d.ts +9 -6
  84. package/dist/capabilities/hands-lane.js +2 -2
  85. package/dist/capabilities/memory-notice.d.ts +27 -22
  86. package/dist/capabilities/memory-notice.js +15 -17
  87. package/dist/capabilities/scenarios.d.ts +9 -14
  88. package/dist/capabilities/scenarios.js +5 -50
  89. package/dist/capability-facts.d.ts +22 -11
  90. package/dist/capability-facts.js +13 -6
  91. package/dist/config-catalog.js +14 -5
  92. package/dist/config-provider.d.ts +5 -1
  93. package/dist/config-provider.js +23 -4
  94. package/dist/config-types.d.ts +19 -2
  95. package/dist/config.js +41 -6
  96. package/dist/deployment-governance.d.ts +19 -21
  97. package/dist/deployment-governance.js +7 -8
  98. package/dist/env-name-allowlist-knobs.d.ts +5 -5
  99. package/dist/env-name-allowlist-knobs.js +1 -0
  100. package/dist/execution-lane-caps.d.ts +12 -0
  101. package/dist/execution-lane-caps.js +18 -4
  102. package/dist/fleet/subagent-tail-bus.js +15 -5
  103. package/dist/hooks/hook-runner.d.ts +84 -30
  104. package/dist/hooks/hook-runner.js +74 -41
  105. package/dist/http/admission.js +17 -10
  106. package/dist/http/durable-run-leg.d.ts +5 -4
  107. package/dist/http/durable-run-leg.js +2 -4
  108. package/dist/http/idempotency.d.ts +28 -6
  109. package/dist/http/idempotency.js +10 -2
  110. package/dist/http/principal-gate.d.ts +14 -0
  111. package/dist/http/principal-gate.js +7 -0
  112. package/dist/http/request-guards.d.ts +2 -1
  113. package/dist/http/request-guards.js +14 -14
  114. package/dist/http/resume-legs.d.ts +2 -2
  115. package/dist/http/resume-legs.js +20 -10
  116. package/dist/http/route-ctx.d.ts +15 -7
  117. package/dist/http/routes/a2a-serve.js +11 -7
  118. package/dist/http/routes/approvals-assistant.js +18 -3
  119. package/dist/http/routes/background.d.ts +1 -1
  120. package/dist/http/routes/background.js +1 -1
  121. package/dist/http/routes/capabilities.js +10 -2
  122. package/dist/http/routes/mcp-reconnect.js +1 -1
  123. package/dist/http/routes/runs.d.ts +4 -3
  124. package/dist/http/routes/runs.js +35 -14
  125. package/dist/http/routes/session-sync.js +14 -11
  126. package/dist/http/routes/sessions.js +9 -9
  127. package/dist/http/routes/side-query.js +6 -19
  128. package/dist/http/routes/tasks.js +29 -19
  129. package/dist/http/routes/workflows.js +8 -0
  130. package/dist/http/server.d.ts +4 -0
  131. package/dist/http/server.js +3 -5
  132. package/dist/http/submit-replay.d.ts +15 -8
  133. package/dist/http/submit-replay.js +6 -0
  134. package/dist/http/wire-types.d.ts +7 -9
  135. package/dist/index.d.ts +1 -1
  136. package/dist/index.js +1 -1
  137. package/dist/internal-run-env.d.ts +30 -0
  138. package/dist/internal-run-env.js +11 -0
  139. package/dist/leader/wire.js +3 -2
  140. package/dist/lsp/manager.js +1 -1
  141. package/dist/memory-scope.d.ts +19 -7
  142. package/dist/memory-scope.js +25 -8
  143. package/dist/observability/fail-open.d.ts +31 -3
  144. package/dist/observability/fail-open.js +31 -3
  145. package/dist/observability/metrics.d.ts +0 -1
  146. package/dist/observability/metrics.js +1 -4
  147. package/dist/observability/run-terminal.js +72 -4
  148. package/dist/permission-rule-vocab.d.ts +3 -2
  149. package/dist/permission-rule-vocab.js +1 -1
  150. package/dist/plan-cache-probe.d.ts +3 -2
  151. package/dist/plugins/approval-ask-store-sql.d.ts +1 -1
  152. package/dist/plugins/caching-session-store.d.ts +42 -22
  153. package/dist/plugins/caching-session-store.js +19 -9
  154. package/dist/plugins/checkpoint-store-sql.d.ts +42 -28
  155. package/dist/plugins/checkpoint-store-sql.js +57 -11
  156. package/dist/plugins/file-run-store.d.ts +25 -21
  157. package/dist/plugins/file-run-store.js +70 -34
  158. package/dist/plugins/k8s-exec-protocol.js +3 -0
  159. package/dist/plugins/local-checkpoint-store.d.ts +8 -5
  160. package/dist/plugins/local-checkpoint-store.js +5 -15
  161. package/dist/plugins/local-session-store.d.ts +66 -7
  162. package/dist/plugins/local-session-store.js +168 -101
  163. package/dist/plugins/mailbox-recipient-lifecycle.d.ts +1 -1
  164. package/dist/plugins/memory-run-store.d.ts +19 -3
  165. package/dist/plugins/memory-run-store.js +38 -3
  166. package/dist/plugins/one-time-migrations.d.ts +165 -39
  167. package/dist/plugins/one-time-migrations.js +51 -144
  168. package/dist/plugins/pg-pool.js +11 -1
  169. package/dist/plugins/pg-session-storage.d.ts +2 -0
  170. package/dist/plugins/pg-session-storage.js +9 -5
  171. package/dist/plugins/remote-env-adb.d.ts +3 -0
  172. package/dist/plugins/remote-env-adb.js +1 -0
  173. package/dist/plugins/remote-env-device.d.ts +3 -0
  174. package/dist/plugins/remote-env-device.js +1 -0
  175. package/dist/plugins/remote-env-e2b.d.ts +4 -0
  176. package/dist/plugins/remote-env-e2b.js +2 -1
  177. package/dist/plugins/remote-env-host.d.ts +23 -1
  178. package/dist/plugins/remote-env-host.js +20 -2
  179. package/dist/plugins/remote-env-k8s.d.ts +264 -41
  180. package/dist/plugins/remote-env-k8s.js +551 -82
  181. package/dist/plugins/remote-env-local-docker.d.ts +4 -0
  182. package/dist/plugins/remote-env-local-docker.js +2 -1
  183. package/dist/plugins/remote-env-ssh.d.ts +3 -0
  184. package/dist/plugins/remote-env-ssh.js +1 -0
  185. package/dist/plugins/retention-store-sql.d.ts +4 -1
  186. package/dist/plugins/retention-store-sql.js +11 -9
  187. package/dist/plugins/run-store-sql.d.ts +44 -6
  188. package/dist/plugins/run-store-sql.js +92 -27
  189. package/dist/plugins/session-capture-record-store-sql.d.ts +2 -3
  190. package/dist/plugins/session-store.d.ts +4 -3
  191. package/dist/plugins/session-store.js +1 -1
  192. package/dist/plugins/sql-driver.d.ts +2 -1
  193. package/dist/plugins/store-backend.d.ts +12 -5
  194. package/dist/plugins/store-backend.js +13 -4
  195. package/dist/plugins/store-contracts.d.ts +20 -3
  196. package/dist/plugins/store-open-wait.js +3 -0
  197. package/dist/plugins/tidb-pool.js +15 -3
  198. package/dist/plugins/tidb-session-storage.js +3 -2
  199. package/dist/plugins/tidb-session-store.d.ts +2 -0
  200. package/dist/plugins/tidb-session-store.js +6 -2
  201. package/dist/request-key-closure.d.ts +32 -7
  202. package/dist/request-key-closure.js +25 -9
  203. package/dist/router/route-orchestration.d.ts +3 -11
  204. package/dist/router/route-orchestration.js +0 -4
  205. package/dist/rule-entry-list.js +0 -4
  206. package/dist/rules-consent.js +3 -0
  207. package/dist/run-acceptance.d.ts +51 -0
  208. package/dist/run-acceptance.js +27 -0
  209. package/dist/run-local.d.ts +5 -4
  210. package/dist/run-local.js +23 -17
  211. package/dist/runs.d.ts +7 -11
  212. package/dist/runs.js +6 -16
  213. package/dist/sandbox-egress-policy.d.ts +61 -0
  214. package/dist/sandbox-egress-policy.js +235 -0
  215. package/dist/security.d.ts +7 -0
  216. package/dist/security.js +5 -0
  217. package/dist/server-secret-env.d.ts +10 -4
  218. package/dist/server-secret-env.js +4 -1
  219. package/dist/session-error-wire.d.ts +2 -2
  220. package/dist/session-incarnation.d.ts +46 -2
  221. package/dist/session-incarnation.js +14 -3
  222. package/dist/session-sync-kernel.d.ts +10 -3
  223. package/dist/session-titler.d.ts +5 -3
  224. package/dist/spec-fields.d.ts +24 -8
  225. package/dist/spec-fields.js +21 -2
  226. package/dist/task-workflow.d.ts +6 -4
  227. package/dist/task-workflow.js +3 -2
  228. package/dist/tool-approval.d.ts +78 -6
  229. package/dist/tool-approval.js +7 -3
  230. package/dist/trace/core-keyset-guard.d.ts +6 -5
  231. package/dist/trace/engine-notice-wire.d.ts +26 -1
  232. package/dist/trace/engine-notice-wire.js +7 -2
  233. package/dist/trace/ledger-events.d.ts +9 -3
  234. package/dist/trace/ledger-sink.d.ts +7 -0
  235. package/dist/trace/ledger-sink.js +14 -4
  236. package/dist/trace/project.d.ts +57 -11
  237. package/dist/trace/project.js +54 -8
  238. package/dist/trace/self-minted-wire.d.ts +94 -2
  239. package/dist/trace/self-minted-wire.js +28 -0
  240. package/dist/trace/tool-output-media.d.ts +26 -0
  241. package/dist/trace/tool-output-media.js +57 -0
  242. package/dist/trace/wire-projection-faces.d.ts +7 -1
  243. package/dist/trace/wire-projection-faces.js +2 -1
  244. package/dist/web-admin-client/apply-effective.d.ts +13 -7
  245. package/dist/web-admin-client/apply-effective.js +11 -26
  246. package/dist/web-admin-client/apply-ledger.d.ts +2 -2
  247. package/dist/web-admin-client/candidate-gate.d.ts +39 -4
  248. package/dist/web-admin-client/candidate-gate.js +9 -2
  249. package/dist/web-admin-client/facade.d.ts +2 -2
  250. package/dist/web-admin-client/http-client.d.ts +3 -1
  251. package/dist/web-admin-client/http-client.js +4 -3
  252. package/dist/web-admin-client/read-warnings.d.ts +33 -5
  253. package/dist/web-admin-client/read-warnings.js +41 -6
  254. package/dist/web-admin-client/types.d.ts +11 -5
  255. package/dist/workspace-cache.d.ts +45 -0
  256. package/dist/workspace-cache.js +30 -0
  257. package/dist/workspace-space-commit.d.ts +116 -0
  258. package/dist/workspace-space-commit.js +117 -0
  259. package/dist/workspace-space.d.ts +75 -9
  260. package/dist/workspace-space.js +52 -1
  261. package/package.json +4 -4
  262. package/dist/boot/session-incarnation-seat.d.ts +0 -41
  263. package/dist/boot/session-incarnation-seat.js +0 -69
  264. package/dist/capabilities/code-review-council.d.ts +0 -49
  265. package/dist/capabilities/code-review-council.js +0 -111
  266. package/dist/capabilities/collab-wire.d.ts +0 -36
  267. package/dist/capabilities/collab-wire.js +0 -2
  268. package/dist/capabilities/collab-workflows.d.ts +0 -47
  269. package/dist/capabilities/collab-workflows.js +0 -90
  270. package/dist/capabilities/prompts/code-review.d.ts +0 -25
  271. package/dist/capabilities/prompts/code-review.js +0 -39
  272. package/dist/capabilities/prompts/team.d.ts +0 -10
  273. package/dist/capabilities/prompts/team.js +0 -6
  274. package/dist/capabilities/team.d.ts +0 -113
  275. package/dist/capabilities/team.js +0 -70
@@ -66,12 +66,12 @@ export declare function skillToolListRefusal(skill: Record<string, unknown>): st
66
66
  /** [ref] 单条 agent 定义的形状裁决:null=合法;否则一句可 400 的英文错误(不带索引,调用方自己加)。
67
67
  * 白名单外键 fail-loud —— 与 resilience/limits 的「容忍未知键」相反:agents 是能力定义,静默丢字段 =
68
68
  * 定义被悄悄削(一个丢掉的 isolation 就是放宽),比 400 更危险,故整条拒。`allowTools` / `denyTools` 本门只判形与条数(`toolList`),
69
- * **名字归引擎一只判**(core 7.45.0 #1377 / #1385,开跑前,三来源同判):没有工具能是的拼法(带空白 / 括号形坏 / 不可见字符)、非字符串条目 ⇒ 整 run 拒
70
- * `config.agent.unknown_tool`;无人挂的名 ⇒ 不拒、经 `onError`(phase `"config"`)告一次(本仓汇点 `runner_config_advisory`);大小写孪生 / 退役名(`bash` / `BASH`)
71
- * ⇒ 开跑前拒 `config.agent.unknown_tool` —— 主车道的委派工具带 extraTools 工厂,本仓在同一处声明它的名册(core 7.46.0 `extraToolNames`;工厂与名册读同一批工具对象,
72
- * `capabilities/builtin-tools.ts` `createSubagentExtraTools`),引擎因此列得出子代工具池。两张表管子代手里的**全部**工具:委派池与 hands 内建(`Read` / `Edit` / `Write` /
73
- * `Bash` …,core #1430)—— `denyTools:["Write"]` ⇒ 子代没有 `Write`;`allowTools` 只点了调用方工具 ⇒ 子代一只 hands 工具都没有。hands 行之外的引擎工具
74
- * (`Monitor` 等)仍不受这两张表管(引擎已知限制)。`denyTools` 的 `Tool(content)` ⇒ 整只工具对该子代藏起并告。本仓不在受理口另做名字对照(那会是同一问的第二个判官)。
69
+ * **名字归引擎一只判**(core 7.49.0 #1623 / #1433 第二步起与根 run 的 `allowTools` / `excludeTools` 是同一只选择器,开跑前,三来源同判):没有任何工具答应的名字
70
+ * (无人挂的名、大小写孪生 / 退役名 `bash` / `BASH`、坏拼法、非字符串条目)⇒ 整 run 拒 `config.agent.unknown_tool` —— 主车道的委派工具带 extraTools 工厂,本仓在同一处
71
+ * 声明它的名册(core 7.46.0 `extraToolNames`;工厂与名册读同一批工具对象,`capabilities/builtin-tools.ts` `createSubagentExtraTools`),引擎因此列得出子代工具池。
72
+ * 两张表管子代可挂载的**全部**工具:委派池、hands 内建、引擎自己的工具(`Monitor` / `Artifact` / `Skill` / 记忆工具 …)与父的 MCP 挂载 ——
73
+ * `denyTools:["Monitor"]` ⇒ 子代没有 `Monitor`;`allowTools` 只点了调用方工具 ⇒ 子代只剩那几只与它这条腿的协议工具。`denyTools` 的 `Tool(content)` ⇒ 整只工具。
74
+ * 本仓不在受理口另做名字对照(那会是同一问的第二个判官)。
75
75
  * ⚠️ 键判断必须 Object.hasOwn:`in` 走原型链,JSON 里的 "toString"/"constructor"/"__proto__" 键会被
76
76
  * 误判为白名单字段,随后 SHAPES[k].ok 不是函数 ⇒ TypeError(fresh 500 / resume 砖;cross-review 复现)。 */
77
77
  export declare function taskAgentIssue(item: unknown): string | null;
@@ -130,6 +130,22 @@ export declare function retainBackgroundProcessesFromBody(raw: unknown, requireP
130
130
  * DEFENSIVE(resume 重放不过 HTTP 400 门):非数组 ⇒ undefined;项非非空字符串 ⇒ 逐项 DROP;空数组 ⇒
131
131
  * undefined(不挂键=core 缺省,byte-compat)。HTTP 门另有 fail-loud 400(提交面邻居姿势)。 */
132
132
  export declare function toolNameListFromBody(raw: unknown): string[] | undefined;
133
+ /** 引擎从哪一版起认 `TaskSpec.allowTools`(core 7.49.0 #1623,[ref])。 */
134
+ export declare const ALLOW_TOOLS_MIN_ENGINE = "7.49.0";
135
+ /**
136
+ * S-1448(core 7.49.0 #1623 / [ref])—— body.allowTools → `TaskSpec.allowTools`:引擎的**可用性选择器**,原样透传。
137
+ *
138
+ * 一条规则:这一 run 能用哪些工具由引擎一只运算答(`universe ∩ ceiling ∩ allow − deny`,prepare 对真实挂载的一切算一次)。
139
+ * 本仓**零读法** —— 不算补集、不逐项过滤(`[]` = 什么都不给,是合法值,不折成缺席;非串条目与未知名由引擎在 prepare 拒,
140
+ * `config.tool_face_control`)、不与 allow 层 `allowedTools`(免问)混词。受理门只判 wire 形(与 `excludeTools` / `deferTools` 同一只门)。
141
+ * 续跑重放同经此处:存量体逐字再投,坏形交引擎拒(收窄键丢了 = 放宽,所以不 DROP)。
142
+ *
143
+ * 装着的引擎不认该座(< {@link ALLOW_TOOLS_MIN_ENGINE})⇒ **响亮拒** 400 `request.field_invalid`(点名键、所需版本与装着的版本),
144
+ * 不静默丢。`engineVersion` 缺省读装着的 core(`CORE_ENGINE_VERSION`);测试传假低版。
145
+ */
146
+ export declare function allowToolsSpecFragment(raw: unknown, engineVersion?: string): {
147
+ allowTools?: string[];
148
+ };
133
149
  /** [ref]/[ref] — body.promptProfile → `TaskSpec.promptProfile`(core 1.328 R2 主件,CC LT(model_id)
134
150
  * 同构双形轴)。语义:纯呈现轴不碰策略(simple=CC 212 短形面缺省;classic=长形面,可按任务/模型试分)
135
151
  * ⇒ 无租户门(deferTools 同姿势);全委托树继承由 core 管。缺省不挂键(引擎缺省 simple,byte-compat)。
@@ -177,10 +193,10 @@ export declare function normalizeApproachNotice(raw: unknown): false | {
177
193
  * [ref]④ — body.limits 与 env 墙钟的合成规则(resolveSpec 的唯一 limits 装配点;run-local 无 body 车道不经此):
178
194
  * - `timeoutSec`:body 给了就用 body(caller 明确配速;已在 normalizeLimits 里被 TASK_TIMEOUT_MAX_SEC 封顶)。
179
195
  * body 缺席保持既有姿势 = tenancy 墙钟 + env `TASK_TIMEOUT_SEC` 只抬不降(taskWallClockSec 内 Math.max,
180
- * 误配低值不能砍 council 预算)。注意两条纪律不叠加:「只抬不降」是 env 对内建缺省墙的规则,body 是
196
+ * 误配低值不能砍 tenancy 基值)。注意两条纪律不叠加:「只抬不降」是 env 对内建缺省墙的规则,body 是
181
197
  * caller 的显式意图,直接生效(可低于 env 墙 —— 交互短任务要更紧的配速正是本件诉求)。
182
198
  * - `maxOutputTokens` / `maxTurns`:直透传(各自可选 env 上限已 clamp)。
183
199
  * 全空 ⇒ undefined(spec 不挂 limits 键,byte-compat)。
184
200
  */
185
- export declare function resolveTaskLimits(bodyLimits: unknown, caps: TaskLimitCaps, envTimeoutSec: number, requirePrincipal: boolean | undefined, big: boolean, warn?: (event: string, fields: Record<string, unknown>) => void): TaskSpec["limits"];
201
+ export declare function resolveTaskLimits(bodyLimits: unknown, caps: TaskLimitCaps, envTimeoutSec: number, requirePrincipal: boolean | undefined, warn?: (event: string, fields: Record<string, unknown>) => void): TaskSpec["limits"];
186
202
  //# sourceMappingURL=spec-fields.d.ts.map
@@ -1,6 +1,8 @@
1
1
  import { isThinkingLevel } from "@sema-agent/core";
2
2
  import { taskWallClockSec } from "./task-workflow.js";
3
3
  import { isAbsoluteHostPath } from "@sema-agent/settings-schema";
4
+ import { CORE_ENGINE_VERSION } from "./prompts-domain-validate.js";
5
+ import { createCodedHttpError } from "./security.js";
4
6
  import { allowLayerEntryCountIssue, ruleEntryListShapeOk } from "./rule-entry-list.js";
5
7
  export function normalizeSuggestNextPrompts(v) {
6
8
  if (v === true)
@@ -270,6 +272,23 @@ export function toolNameListFromBody(raw) {
270
272
  const names = raw.filter((v) => typeof v === "string" && v.length > 0);
271
273
  return names.length > 0 ? names : undefined;
272
274
  }
275
+ export const ALLOW_TOOLS_MIN_ENGINE = "7.49.0";
276
+ function engineAtLeast(version, floor) {
277
+ const parse = (v) => v.split(/[-+]/, 1)[0].split(".").map((n) => Number.parseInt(n, 10) || 0);
278
+ const [a, b] = [parse(version), parse(floor)];
279
+ for (let i = 0; i < 3; i++)
280
+ if ((a[i] ?? 0) !== (b[i] ?? 0))
281
+ return (a[i] ?? 0) > (b[i] ?? 0);
282
+ return true;
283
+ }
284
+ export function allowToolsSpecFragment(raw, engineVersion = CORE_ENGINE_VERSION) {
285
+ if (raw === undefined)
286
+ return {};
287
+ if (!engineAtLeast(engineVersion, ALLOW_TOOLS_MIN_ENGINE)) {
288
+ throw createCodedHttpError(400, "request.field_invalid", `allowTools needs engine ${ALLOW_TOOLS_MIN_ENGINE} or later; this server runs engine ${engineVersion}, which would drop the list and leave every tool available — send the request without allowTools, or upgrade the server`);
289
+ }
290
+ return { allowTools: raw };
291
+ }
273
292
  export function promptProfileFromBody(raw) {
274
293
  return raw === "simple" || raw === "classic" ? raw : undefined;
275
294
  }
@@ -318,10 +337,10 @@ export function normalizeApproachNotice(raw) {
318
337
  const frac = (n) => typeof n === "number" && Number.isFinite(n) && n > 0 && n <= 1;
319
338
  return frac(a) && frac(b) && a <= b ? { at: [a, b] } : undefined;
320
339
  }
321
- export function resolveTaskLimits(bodyLimits, caps, envTimeoutSec, requirePrincipal, big, warn) {
340
+ export function resolveTaskLimits(bodyLimits, caps, envTimeoutSec, requirePrincipal, warn) {
322
341
  const dropped = retiredLimitKeysPresent(bodyLimits);
323
342
  const requested = normalizeLimits(bodyLimits, caps);
324
- const wallSec = taskWallClockSec(envTimeoutSec, requirePrincipal, big);
343
+ const wallSec = taskWallClockSec(envTimeoutSec, requirePrincipal);
325
344
  const maxWalltimeMs = requested?.maxWalltimeMs ?? (wallSec !== undefined ? wallSec * 1000 : undefined);
326
345
  if (dropped.length > 0)
327
346
  warn?.("task_limits_legacy_key_dropped", { droppedKeys: dropped, effectiveMaxWalltimeMs: maxWalltimeMs ?? null });
@@ -113,10 +113,12 @@ export declare function normalizeRetainSubagentSessions(raw: unknown): boolean |
113
113
  * - SINGLE-USER turnkey(`requirePrincipal !== true`)= the user IS the superadmin → **NO wall clock**
114
114
  * (undefined; core arms none — its separate wedge safety-net still catches a stuck tool call, so "no wall"
115
115
  * never means "hangs forever"). CC has no whole-task decapitation either.
116
- * - MULTI-TENANT: base 2400s (big tasks — council/debate/discuss — 3600s). (PAIR-REVIEW F-4 附带修:此行
117
- * 曾写 600s/1500s 与代码漂移——真值以下方 base 为准。)
116
+ * - MULTI-TENANT: base 2400s (one base: the 3600s big-task lane existed only for council/debate/discuss, which
117
+ * left with the engine's team runtime in 7.49.0).
118
118
  * - Explicit `TASK_TIMEOUT_SEC` wins in ANY tenancy (the operator's deliberate wall) and keeps its documented
119
- * raise-only floor (`Math.max` with the base, so a misconfigured low value cannot shrink a council budget).
119
+ * raise-only floor (`Math.max` with the base, so a misconfigured low value cannot shrink the tenancy base).
120
120
  */
121
- export declare function taskWallClockSec(envTimeoutSec: number, requirePrincipal: boolean | undefined, big: boolean): number | undefined;
121
+ /** 多租户(requirePrincipal)任务墙钟基值(秒)—— 唯一来源:`taskWallClockSec` 与 resolve-spec 的 `server.limits.wallClockBaseSec` 声明都读它(3600 s 大任务档已随团队编排面退役删除)。 */
122
+ export declare const TENANCY_WALL_CLOCK_BASE_SEC = 2400;
123
+ export declare function taskWallClockSec(envTimeoutSec: number, requirePrincipal: boolean | undefined): number | undefined;
122
124
  //# sourceMappingURL=task-workflow.d.ts.map
@@ -39,8 +39,9 @@ export function normalizeRetainSubagentSessions(raw) {
39
39
  return undefined;
40
40
  return { ...(ttlMs !== undefined ? { ttlMs } : {}), ...(max !== undefined ? { max } : {}) };
41
41
  }
42
- export function taskWallClockSec(envTimeoutSec, requirePrincipal, big) {
43
- const base = big ? 3600 : 2400;
42
+ export const TENANCY_WALL_CLOCK_BASE_SEC = 2400;
43
+ export function taskWallClockSec(envTimeoutSec, requirePrincipal) {
44
+ const base = TENANCY_WALL_CLOCK_BASE_SEC;
44
45
  if (envTimeoutSec > 0)
45
46
  return Math.max(envTimeoutSec, base);
46
47
  return requirePrincipal === true ? base : undefined;
@@ -1,6 +1,64 @@
1
+ /**
2
+ * [ref]/[ref]② tool-approval bridge — the SERVICE side of core's `RunnerDeps.onAsk` seam (core `tool-policy.ts`
3
+ * `resolveAsk`, 1.290's sync-ask leg). The SIBLING of {@link import("./question.js").QuestionCoordinator}: onQuestion
4
+ * routes the agent's AskUserQuestion tool to a live human; THIS routes a policy `ask` DECISION (e.g. the
5
+ * `createFsWriteGatePolicy` fs-write gate, an ask-list rule, or a non-durable safety ask) to the live human as a
6
+ * CC-parity three-choice card (Yes / Yes-allow-all-session / No).
7
+ *
8
+ * Wire shape (mirrors question end to end): core adjudicates a tool call `ask` → `resolveAsk(req, onAsk)` calls this
9
+ * coordinator's `ask` → we mint a pending entry + emit a `tool_approval` SSE frame on the run's live stream → the
10
+ * shell renders the three-choice card → `POST /v1/tool-approvals/:id/respond` settles the parked promise →
11
+ * `resolveAsk` maps our boolean to allow/deny. RESPOND BODY selection rationale: the question precedent's body is
12
+ * core's own QuestionAnswer (a structured multi-answer object) because core FENCES its semantics; an approval has no
13
+ * core-side answer object — `OnAsk` resolves to a BARE boolean — so the wire body is a minimal closed enum
14
+ * `{decision: "allow" | "allow_session" | "deny"}` mapping 1:1 onto the CC card's three choices (no free text = no
15
+ * fence needed; "allow_session" carries the Yes-allow-all intent that a boolean cannot).
16
+ *
17
+ * Session state ([ref]② "桥内会话态"): `allow_session` remembers a per-CAPABILITY grant keyed by
18
+ * `[owner, sessionId, category]` — a LATER ask in that (owner, session) whose tool maps to the SAME category
19
+ * auto-allows WITHOUT a card (CC 207: "Yes, allow all edits during this session") only through the FOUR-WAY
20
+ * conjunction at the short-circuit (see `ask`): {@link standingAnswerHome} over the ask's three facts
21
+ * (NOT governance-forced [ref] 件7/[ref], NOT `requiresRealApproval` [ref], NOT `mandated` S-660 — the SAME
22
+ * predicate decides whether an `allow_session` answer may MINT a grant, see `finishRespond`) ∧ a truthy sessionId ∧
23
+ * the grant itself ∧ `sessionGrantUncontestedByRow` (the durable row must not contradict the local peek). The category collapses the
24
+ * fs-WRITE tool family (Write/Edit/NotebookEdit, canonical space) into ONE bucket `"fs-write"`; any OTHER tool is
25
+ * its OWN bucket `"tool:<canonical>"`. 🔴 A `allow_session` on a NON-fs-write ask (an irreversible-Bash safety ask,
26
+ * an ask-list rule on another tool) therefore unlocks ONLY that same tool for the session — it must NEVER unlock the
27
+ * fs-write family (the "approve a cheap ask once → all future Write/Edit auto-allowed" scope widening, 三路复审 A 组 修2).
28
+ * The key is scoped to the VERIFIED owner too, so a session id shared across principals cannot inherit another's
29
+ * grant. This is the cheapest mid-task implementation; a mode SWITCH (default → acceptEdits) still goes through the
30
+ * shell's intent on the next task ([ref] table).
31
+ *
32
+ * 🔒 FAIL-CLOSED everywhere (the inverse of the question lane — an unanswered QUESTION never blocks the run: since
33
+ * [ref] the coordinator reports `unavailable` and CORE decides the landing, which depends on WHEN the human became
34
+ * unreachable — at policy time the durable gate says `ask` and the leg parks; once the tool is already running core is
35
+ * past `suspendAsk`, so it continues on `declined_unavailable`; an unanswered APPROVAL must NOT let the write
36
+ * proceed): TTL expiry and client disconnect ([ref] `parkOpenAsksForTask` → `forceParkNow`) land on the ONE
37
+ * unattended-outcome former ({@link ToolApprovalCoordinator.unattendedOutcome}, [ref] R-13) — park route
38
+ * ("unavailable" ⇒ core durable-parks) by default, a DEPLOYMENT-POLICY refusal (`{allow:false, settledBy:"policy",
39
+ * reason}` ⇒ core settles `policy_refused`; S-689 — never a bare `false`, which core reads as a PERSON's refusal)
40
+ * ONLY where the deployment declared `UNATTENDED_APPROVAL_POLICY=deny`; a run ABORT (task cancel → `runCancel`)
41
+ * still resolves `false` — the row goes VOID, there is nothing left to redeem. Since [ref] G1 (core 1.295
42
+ * OnAsk 三值化) the "no reachable human" class returns
43
+ * "unavailable" instead of false — no ALS context AND no bound-closure broker hit(1.258 起 bg/嵌套子代经
44
+ * spec.onAsk 的 bound 闭包 + streams broker 直达宿主活流,「无 ctx」只剩 durable-submit/headless resume
45
+ * 残余腿)and emit failure
46
+ * (card never delivered, and only when this catch is the first settler — a human verdict that raced in during the
47
+ * await keeps its boolean) ⇒ "unavailable" → core durable-parks (or fail-closed denies without a checkpoint). core's
48
+ * `resolveAsk` maps false to a deny WITH the ask message, so the model can re-route (deny + continue turn, [ref]⑤).
49
+ * Owner gate on respond = the VERIFIED principal (gatedPrincipal at the HTTP layer), 404 without an existence oracle
50
+ * — same posture as question/elicit. The frame's model-authored surfaces (message + args) are secret-REDACTED
51
+ * before they are streamed; the SIZE bound is per-surface: args ride a byte cap (`MAX_APPROVAL_ARGS_BYTES`,
52
+ * over-cap ⇒ `argsOmitted`), while the legacy frame's `message` is redacted but NOT length-capped — the char
53
+ * cap (`MAX_MESSAGE`, approval-card.ts `clip`) lives on the card/`approval_request` projection only (same
54
+ * redact-at-write contract as question; the args are shown to a human,
55
+ * never re-fed to a model). No throttle (unlike question): an approval breach-default would be a DENY (task failure),
56
+ * and asks are turn-blocking by construction — the TTL bounds the human-side exposure instead.
57
+ */
58
+ import { type ParkedGateFace } from "./approval-content-kind.js";
1
59
  import { type ApprovalDecidedBy, type HostSettledBy, type RetiredDecisionKeyFacts } from "./host-decision.js";
2
60
  import type { IncomingMessage } from "node:http";
3
- import { type AskClass, type AskRequest, type AskOutcome, type PermissionResult, type CheckpointGate, type PendingAction, type PersistedRuleMandate, type ReadRootGrantCandidate, type RuleOffer, type AutoModeFallbackCause, type BoundSkillGrants } from "@sema-agent/core";
61
+ import { type AskClass, type AskRequest, type AskOutcome, type PermissionResult, type CheckpointGate, type PendingAction, type PersistedRuleMandate, type ReadRootGrantCandidate, type RuleOffer, type AutoModeFallbackCause, type BoundSkillGrants, type ToolApprovalCard } from "@sema-agent/core";
4
62
  import { type CardRulePersisted, type RuleConsentLane } from "./rules-consent.js";
5
63
  import { type AllowRuleShadowProjection, type DenialLimitFallback, type RuleOffersAbsence, type ApprovalRequestFrame, type ApprovalRevokeFrame, type RuleEvidenceProjection } from "./approval-card.js";
6
64
  import { type ApprovalAskStore, type AskRow } from "./plugins/approval-ask-store-sql.js";
@@ -18,6 +76,11 @@ import { type ApprovalExemptionStore } from "./plugins/approval-exemption-store.
18
76
  export declare const APPROVAL_GATE_KINDS: readonly ["human", "irreversible_ask"];
19
77
  export type ApprovalGateKind = (typeof APPROVAL_GATE_KINDS)[number];
20
78
  export declare function isApprovalGateKind(k: string | undefined): k is ApprovalGateKind;
79
+ /**
80
+ * 停驻行的到期归 SLA 拒扫腿(deny)而不是作废(expire)?—— **一只谓词**:审批门型 ∧ core `facetOf(gate)` 读作 `A`(问答卡 ⇒ `B`,到期作废;
81
+ * 无门型 / 非审批门 ⇒ 不归拒扫)。两只店的拒扫选行与作废腿的补集都读这一只(SQL 腿的 `gate_kind IN …` 只是它的粗筛前置,判官仍是 core)。
82
+ */
83
+ export declare function denySweepOwns(gate: ParkedGateFace | undefined): boolean;
21
84
  /** 两方言同形的 SQL IN 片段(值为闭集常量字面,无注入面)。 */
22
85
  export declare const APPROVAL_GATE_KINDS_SQL_IN: string;
23
86
  /**
@@ -345,7 +408,7 @@ export interface ToolApprovalFrame {
345
408
  /**
346
409
  * **S-721(core 7.32.0 #1092;ADDITIVE,`"tool_approval"` only)** —— 强制位**站在哪一个词上**:core `PERSISTED_RULE_MANDATES`
347
410
  * 六词之一(`operator_always` / `tool_marks` / `probe_mandate` / `probe_unanswered` / `write_protection` /
348
- * `write_protection_unresolved`,词义属主在 core)。只在 `mandated: true` 的**整因**是那一个词时在场;另一扇门(治理 / hook /
411
+ * `target_unresolved`,词义属主在 core)。只在 `mandated: true` 的**整因**是那一个词时在场;另一扇门(治理 / hook /
349
412
  * 祖先)或不止一个理由 ⇒ 缺席 —— **缺席从不命名一个词**,也不等于「不强制」(读 `mandated`)。值经 `approval-card.ts` 的
350
413
  * `readClosedWordSeat(req, "mandate")` 窄读(闭集筛 = core `isPersistedRuleMandate`,本仓不抄词表;集外词读作缺席 + 计一次)。echo-only:
351
414
  * 本仓不按词分支。耐久面同一个词:运维队列行(`parkRowFacts`,park 行孪生座)与 inbox 行(core `CheckpointSummary.mandate`)。
@@ -407,6 +470,15 @@ export interface ToolApprovalFrame {
407
470
  /** core 7.43.0 #1353:分类器没给判词的成因词(`error` / `timeout` / `parse_error`;只在类 `classifier_no_ruling` 的卡上)。闭集经 core 的
408
471
  * `readClosedWordSeat(req, "classifierNoRuling")` 窄读(筛 = core `isAutoModeFallbackCause`),集外 ⇒ 缺席 + 计一次。display:本仓不按它裁决。 */
409
472
  classifierNoRuling?: AutoModeFallbackCause;
473
+ /**
474
+ * **core 7.49.0 #1414(ADDITIVE,`"tool_approval"` only)—— 这一问渲在哪张卡上**:引擎在闸站铸的那一个词(闭集 core `TOOL_APPROVAL_CARDS`:
475
+ * `file-edit` / `file-write` / `notebook-edit` / `shell` / `fetch` / `plan` / `question` / `generic`),`AskRequest.approvalCard` 逐字过境,经
476
+ * `approval-content-kind.ts` 的 `readApprovalCard` 闭集窄读(集外 ⇒ 缺席 + 计一次 closed-word-out-of-set)。`question` = 问用户的问题(引擎保留的
477
+ * 问答工具),不是副作用工具。**本仓不从工具名回推卡**(工具名只作展示):此前按名字铸的 `livePending` 行 / 队列行 `contentKind` 随本键删。
478
+ * 停驻孪生 = 门的 `gate.approvalCard`(运维队列行顶层同名键,两只店读同一只 `parkedApprovalCard`)。display:本仓不按它裁决。
479
+ * 🔴 缺席即缺席(宿主自建的 ask / 老引擎),不编 `generic`。⚠️ `card_json` 卡面不带本键(`ApprovalCardSchema` `.strict()`,与 `mandated` 同)。
480
+ */
481
+ approvalCard?: ToolApprovalCard;
410
482
  /**
411
483
  * **[ref]**(core 7.19.0;**ADDITIVE**,`"tool_approval"` only)—— 这只**根外读** ask 的**清障目录**:
412
484
  * 把 `dir` 加进本会话的读目录(壳侧既有的 `/add-dir` 口),同一条调用就不会再问。
@@ -675,10 +747,10 @@ export interface LivePendingRow {
675
747
  * 子代 ask)上恒等成立。缺席 = 顶层 ask。`fromSubagent` 保留不动(byte-compat:先于本键的消费方仍按
676
748
  * 它判别)。 */
677
749
  originTaskId?: string;
678
- /** [ref]([ref]②):内容问句分型 —— `"content_ask"` 当 `toolName` 为 AskUserQuestion(词属主
679
- * `approval-content-kind.ts`,与 durable 两读面 / core summarize 面同词,OMIT 契约同族)。
680
- * 缺席 = 普通工具 ask。展示/分诊分型用,永不参与决议路由。 */
681
- contentKind?: "content_ask";
750
+ /** core 7.49.0 #1414:这一问的卡词 —— 与 {@link frame} 上的 `approvalCard` **同一个值**(行读帧上那一格,不另读一遍),与 durable
751
+ * 队列行顶层的同名键同层同形(那一面读门的 `gate.approvalCard`)。缺席 = 引擎没给(宿主自建 ask / 老引擎)。展示/分诊用,永不参与决议路由。
752
+ * (此前此处是按工具名铸的 `contentKind`,随 #1414 删:卡只有引擎铸的这一个词。) */
753
+ approvalCard?: ToolApprovalCard;
682
754
  /**
683
755
  * 🔴 S-454(P1;cli L-397 / 板 [ref]):这只 ask **本来会 emit 的那只活卡帧**。
684
756
  *
@@ -1,4 +1,4 @@
1
- import { contentKindOf } from "./approval-content-kind.js";
1
+ import { parkedFacetOf, readApprovalCard } from "./approval-content-kind.js";
2
2
  import { heldAskOf, liveSettledBy, readDecidedBy, retiredDecisionKeyFacts, DECIDED_BY_OUTSIDE_CLOSED_SET, POLICY_CANNOT_ALLOW_PERSON_ASK } from "./host-decision.js";
3
3
  import { AsyncLocalStorage } from "node:async_hooks";
4
4
  import { uuidv7, classifyAsk, foldArbitrationRow, policyCannotAnswer, screenAskClassMembers, MAX_RULE_TEXT_CHARS, READ_ROOT_CANDIDATE_DIR_MAX, parseRuleText, pathConfinableWriteToolNames } from "@sema-agent/core";
@@ -23,6 +23,9 @@ export const APPROVAL_GATE_KINDS = ["human", "irreversible_ask"];
23
23
  export function isApprovalGateKind(k) {
24
24
  return k !== undefined && APPROVAL_GATE_KINDS.includes(k);
25
25
  }
26
+ export function denySweepOwns(gate) {
27
+ return isApprovalGateKind(typeof gate?.kind === "string" ? gate.kind : undefined) && parkedFacetOf(gate) === "A";
28
+ }
26
29
  export const APPROVAL_GATE_KINDS_SQL_IN = `(${APPROVAL_GATE_KINDS.map((k) => `'${k}'`).join(",")})`;
27
30
  const MAX_APPROVAL_ARGS_BYTES = 16384;
28
31
  const DEFAULT_APPROVAL_TTL_MS = 5 * 60_000;
@@ -1151,6 +1154,7 @@ export class ToolApprovalCoordinator {
1151
1154
  let ruleLaneMaterial = this.buildRuleLaneMaterial(req, origin.owner, governanceForced, origin.sessionId);
1152
1155
  const mandateWord = readClosedWordSeat(req, "mandate");
1153
1156
  const noRulingCause = readClosedWordSeat(req, "classifierNoRuling");
1157
+ const cardWord = readApprovalCard(req.approvalCard);
1154
1158
  const originWord = typeof req.origin === "string" && req.origin.length > 0 ? req.origin : undefined;
1155
1159
  const askClassPair = readAskClassPair(req);
1156
1160
  const frame = {
@@ -1181,6 +1185,7 @@ export class ToolApprovalCoordinator {
1181
1185
  ...(standingAnswerHome(askStandingAnswerFacts(req, governanceForced, deploymentListAsks)) === "none" ? { rememberRefused: true } : {}),
1182
1186
  ...(typeof req.boundGrantDigest === "string" ? { boundGrantDigest: req.boundGrantDigest, ...(req.boundGrants !== undefined ? { boundGrants: req.boundGrants } : {}) } : {}),
1183
1187
  ...(noRulingCause !== undefined ? { classifierNoRuling: noRulingCause } : {}),
1188
+ ...(cardWord !== undefined ? { approvalCard: cardWord } : {}),
1184
1189
  ...(mandateWord !== undefined ? { mandate: mandateWord } : {}),
1185
1190
  ...(originWord !== undefined ? { origin: originWord } : {}),
1186
1191
  ...(askClassPair ?? {}),
@@ -2434,7 +2439,6 @@ export class ToolApprovalCoordinator {
2434
2439
  for (const [id, e] of this.pending) {
2435
2440
  if (scope !== undefined && e.owner !== scope)
2436
2441
  continue;
2437
- const contentKind = contentKindOf(e.toolName);
2438
2442
  rows.push({
2439
2443
  approvalId: id,
2440
2444
  toolName: e.toolName,
@@ -2448,7 +2452,7 @@ export class ToolApprovalCoordinator {
2448
2452
  ...(e.mandate !== undefined ? { mandate: e.mandate } : {}),
2449
2453
  ...(e.origin !== undefined ? { origin: e.origin } : {}),
2450
2454
  ...(e.originTaskId !== undefined ? { fromSubagent: true, originTaskId: e.originTaskId } : {}),
2451
- ...(contentKind !== undefined ? { contentKind } : {}),
2455
+ ...(e.frame.approvalCard !== undefined ? { approvalCard: e.frame.approvalCard } : {}),
2452
2456
  frame: e.frame.approvalId === id ? e.frame : { ...e.frame, approvalId: id },
2453
2457
  });
2454
2458
  }
@@ -43,14 +43,14 @@ type _GuardBgNotifReverse = AssertAllKeysHandled<Exclude<BgNotifProjected | BgNo
43
43
  type RosterProjected = "name" | "agentId" | "sessionId" | "toolUseId" | "owner" | "scope" | "sessionScoped" | "rootSessionId" | "model" | "modelFallback" | "createdAt";
44
44
  type _GuardRoster = AssertAllKeysHandled<Exclude<keyof RosterEntry, RosterProjected>>;
45
45
  type _GuardRosterReverse = AssertAllKeysHandled<Exclude<RosterProjected, keyof RosterEntry>>;
46
- type AskProjected = "toolName" | "toolCallId" | "args" | "message" | "sourceTaskId" | "fromSubagent" | "sourceAgentName" | "delegation" | "ruleOffers" | "ruleOffersAbsence" | "allowRuleShadowed" | "probeCause" | "probeReason" | "ruleEvidence" | "requiresRealApproval" | "denialLimitFallback" | "origin" | "readRootCandidate" | "mandated" | "mandate" | "askClass" | "classes" | "boundGrantDigest" | "boundGrants" | "classifierNoRuling";
46
+ type AskProjected = "toolName" | "toolCallId" | "args" | "message" | "sourceTaskId" | "fromSubagent" | "sourceAgentName" | "delegation" | "ruleOffers" | "ruleOffersAbsence" | "allowRuleShadowed" | "probeCause" | "probeReason" | "ruleEvidence" | "requiresRealApproval" | "denialLimitFallback" | "origin" | "readRootCandidate" | "mandated" | "mandate" | "askClass" | "classes" | "boundGrantDigest" | "boundGrants" | "classifierNoRuling" | "approvalCard";
47
47
  type AskExcluded = "preview" | "principal" | "riskAxes" | "boundInputHash" | "isDelegatedChild" | "hasBidiControls" | "previewWithheld" | "execCwd" | "authoredBy";
48
48
  type _GuardAsk = AssertAllKeysHandled<Exclude<keyof AskRequest, AskProjected | AskExcluded>>;
49
49
  type _GuardAskReverse = AssertAllKeysHandled<Exclude<AskProjected | AskExcluded, keyof AskRequest>>;
50
50
  type AskFrameServerMinted = "type" | "approvalId" | "argsOmitted" | "outcome" | "governanceForced" | "expiresAtMs" | "expiresInMs" | "serverNowMs" | "parked" | "cause" | "inputHasBidi" | "rememberRefused";
51
51
  type _GuardAskFrame = AssertAllKeysHandled<Exclude<keyof ToolApprovalFrame, AskProjected | AskFrameServerMinted>>;
52
52
  type _GuardAskFrameReverse = AssertAllKeysHandled<Exclude<AskProjected | AskFrameServerMinted, keyof ToolApprovalFrame>>;
53
- type TaskEventHandled = "text_delta" | "reasoning_delta" | "tool_start" | "tool_end" | "turn_end" | "compacted" | "diagnostics" | "message_committed" | "status" | "task_notification" | "task_progress" | "steering_injected" | "workspace_changed" | "done" | "context_usage" | "compaction_outcome" | "human_input" | "wiring_manifest" | "text_end" | "tool_roster_delta" | "tool_progress" | "tool_disclosure";
53
+ type TaskEventHandled = "text_delta" | "reasoning_delta" | "tool_start" | "tool_end" | "turn_end" | "compacted" | "diagnostics" | "message_committed" | "status" | "task_notification" | "task_progress" | "steering_injected" | "workspace_changed" | "done" | "context_usage" | "compaction_outcome" | "human_input" | "wiring_manifest" | "text_end" | "tool_roster_delta" | "tool_progress" | "tool_disclosure" | "task_started";
54
54
  type _GuardTaskEvent = AssertAllKeysHandled<Exclude<TaskEvent["type"], TaskEventHandled>>;
55
55
  type _GuardTaskEventReverse = AssertAllKeysHandled<Exclude<TaskEventHandled, TaskEvent["type"]>>;
56
56
  type MailboxProjected = "seq" | "from" | "content" | "sentAt" | "hopChain" | "peerMeta";
@@ -137,7 +137,7 @@ type _GuardPermissionTraceReverse = AssertAllKeysHandled<Exclude<PermissionTrace
137
137
  type AssertNonEmpty<T> = [T] extends [never] ? "EMPTY" : never;
138
138
  type _GuardPermissionTraceNonEmpty = AssertAllKeysHandled<AssertNonEmpty<PermissionTraceKind>>;
139
139
  type SummaryProjected = "sessionId" | "scope" | "createdAt" | "gateKind" | "severity" | "spentMicroUsd" | "deadline" | "hasBidiControls" | "requiresRealApproval" | "denialLimitFallback" | "origin" | "mandated" | "mandate" | "boundGrantDigest" | "boundGrants" | "classifierNoRuling";
140
- type SummaryExcluded = "token" | "toolInput" | "toolName" | "toolCallId" | "preview" | "principal" | "sourceTaskId" | "contentKind" | "restoreMode" | "checkpointId" | "previewWithheld" | "authoredBy";
140
+ type SummaryExcluded = "token" | "toolInput" | "toolName" | "toolCallId" | "preview" | "principal" | "sourceTaskId" | "approvalCard" | "restoreMode" | "checkpointId" | "previewWithheld" | "authoredBy";
141
141
  type _GuardInboxSummary = AssertAllKeysHandled<Exclude<keyof CheckpointSummary, SummaryProjected | SummaryExcluded>>;
142
142
  type _GuardInboxSummaryReverse = AssertAllKeysHandled<Exclude<SummaryProjected | SummaryExcluded, keyof CheckpointSummary>>;
143
143
  type ParkArmKeys = keyof Extract<PendingAction, {
@@ -148,8 +148,9 @@ type ParkArmQueueExcluded = "kind" | "args" | "batchToolCallIds" | "completedCal
148
148
  type _GuardParkArmQueue = AssertAllKeysHandled<Exclude<ParkArmKeys, ParkArmQueueProjected | ParkArmQueueExcluded>>;
149
149
  type _GuardParkArmQueueReverse = AssertAllKeysHandled<Exclude<ParkArmQueueProjected | ParkArmQueueExcluded, ParkArmKeys>>;
150
150
  type _NoDoubleBookParkArmQueue = AssertAllKeysHandled<Extract<ParkArmQueueProjected, ParkArmQueueExcluded>>;
151
- type TaskSpecWired = "objective" | "principal" | "images" | "sessionId" | "requireExistingSession" | "oneShot" | "clientContext" | "resumeAt" | "resumeAtMode" | "model" | "compactionModel" | "thinking" | "systemPrompt" | "appendSystemPrompt" | "promptProvider" | "memory" | "suggestNextPrompts" | "restoreFiles" | "acceptPartialRestore" | "rewindFilesTo" | "tools" | "excludeTools" | "deferTools" | "toolMaterializeStrategy" | "promptProfile" | "toolPolicy" | "onAsk" | "onQuestion" | "hooks" | "durableApproval" | "resourceSuspend" | "resilience" | "finalVerification" | "checkpointStore" | "handsReadOnly" | "memoryPersistenceCapable" | "additionalDirectories" | "additionalReadDirectories" | "enablePlanMode" | "interactiveTools" | "enableFork" | "shellGate" | "approvalReach" | "writeFace" | "mcp" | "a2a" | "skills" | "backgroundScope" | "envFacts" | "limits" | "configOverrides" | "forwardSubagentEvents" | "retainSubagentSessions" | "retainBackgroundProcesses" | "outputSchema" | "outputRetries" | "compaction" | "attachments" | "selfOrchestration" | "getApiKeyAndHeaders" | "interactionPosture" | "agents" | "permissionMode" | "taskId" | "signal" | "preemptSignal" | "modelRole" | "enableBlockedReport" | "maxSuspends" | "skillListingBudgetFraction" | "skillListingMaxDescChars" | "excludeAllTools" | "fileHistory" | "userInvokedSkills" | "outputStyle" | "allowedTools";
152
- type TaskSpecUnwired = "actor" | "roles" | "lspManager" | "tracer" | "readFace" | "readDenyPatterns" | "interactiveQuestionFallback" | "alwaysLoadTools" | "restoreGatedTools" | "deferSelfResolve" | "basePolicyForResumeEdit" | "lspDiagnostics" | "streamingToolExecution" | "toolResultThresholdChars" | "peerPresence";
151
+ type TaskSpecWired = "objective" | "principal" | "images" | "sessionId" | "requireExistingSession" | "oneShot" | "clientContext" | "resumeAt" | "resumeAtMode" | "model" | "compactionModel" | "thinking" | "systemPrompt" | "appendSystemPrompt" | "promptProvider" | "memory" | "suggestNextPrompts" | "restoreFiles" | "acceptPartialRestore" | "rewindFilesTo" | "tools" | "excludeTools" | "deferTools" | "toolMaterializeStrategy" | "promptProfile" | "toolPolicy" | "onAsk" | "onQuestion" | "hooks" | "durableApproval" | "resourceSuspend" | "resilience" | "finalVerification" | "checkpointStore" | "handsReadOnly" | "memoryPersistenceCapable" | "additionalDirectories" | "additionalReadDirectories" | "enablePlanMode" | "interactiveTools" | "enableFork" | "shellGate" | "approvalReach" | "writeFace" | "mcp" | "a2a" | "skills" | "backgroundScope" | "envFacts" | "limits" | "configOverrides" | "forwardSubagentEvents" | "retainSubagentSessions" | "retainBackgroundProcesses" | "outputSchema" | "outputRetries" | "compaction" | "attachments" | "selfOrchestration" | "getApiKeyAndHeaders" | "interactionPosture" | "agents" | "permissionMode" | "taskId" | "signal" | "preemptSignal" | "maxSuspends" | "skillListingBudgetFraction" | "skillListingMaxDescChars" | "excludeAllTools" | "fileHistory" | "userInvokedSkills" | "outputStyle" | "allowedTools" | "allowTools";
152
+ /** 导出:请求体键闭集门(`request-key-closure.ts`)据它锁「本仓记为未接线的引擎座,提交口不许受理」—— 两处分类只能同向。 */
153
+ export type TaskSpecUnwired = "actor" | "roles" | "lspManager" | "tracer" | "readFace" | "readDenyPatterns" | "interactiveQuestionFallback" | "alwaysLoadTools" | "restoreGatedTools" | "deferSelfResolve" | "basePolicyForResumeEdit" | "lspDiagnostics" | "streamingToolExecution" | "toolResultThresholdChars" | "peerPresence" | "writeProtectedAdditions" | "complianceAdditionalDenies" | "modelRole" | "enableBlockedReport" | "userAttachments";
153
154
  type _GuardTaskSpec = AssertAllKeysHandled<Exclude<keyof TaskSpec, TaskSpecWired | TaskSpecUnwired>>;
154
155
  type _GuardTaskSpecReverse = AssertAllKeysHandled<Exclude<TaskSpecWired | TaskSpecUnwired, keyof TaskSpec>>;
155
156
  type TaskResultForwarded = "taskId" | "runId" | "sessionId" | "terminal" | "model" | "effectiveReasoning" | "result" | "salvagedOutput" | "remoteEnvFailures" | "apiFailure" | "retryAfterMs" | "degraded" | "structuredOutput" | "rewindNotes" | "haltedOnUserRejection" | "haltedByUser" | "strandedHumanAnswers" | "pendingApproval" | "approvalWaitedMs" | "effectiveReadFace" | "effectiveReadDenyPatterns" | "effectiveWriteFace" | "effectiveMemoryScopes" | "stats" | "editedFiles" | "responseModel" | "compaction";
@@ -16,7 +16,7 @@
16
16
  * 同文件 G1 e2e 的全码扇出夹具、以及契约文档附录 D.3 那张表(含**小节标题里的码数** —— 那道门本批
17
17
  * 刚立,立完就在下一次加码时自己咬住了)。
18
18
  */
19
- export declare const ENGINE_NOTICE_WIRE_CODES: readonly ["memory.session_polluted", "memory.harvest_quarantined", "memory.delegation_static_mark_waived", "memory.hold_opened", "memory.hold_released", "memory.hold_disposed", "task.turn_interrupted", "steering.parked_input_blocked", "task.user_steer_undrained", "task.user_followup_undrained", "memory.capture_opted_out", "memory.capture_optout_unpersisted", "task.halt_unconsumed", "task.late_approval", "config.durable_gate_unavailable", "peer.inbound_disposition", "peer.held_settled", "peer.idle_subscription", "classifier.denial_limit", "mcp.injection_dropped", "delegation.ask_unresolvable", "task.interrupt_unconsumed", "approval.read_root_granted", "approval.read_root_grant_rejected", "instructions.source_changed", "session.records_skipped", "server.model_unreachable", "classifier.unavailable", "config.permission_mode_unavailable", "permission.ask_released_by_mode", "permission.read_deny_unapplied", "permission.rule_store_unavailable", "permission.rule_row_dropped", "permission.allowed_tool_not_granted", "task.tool_turn_incomplete"];
19
+ export declare const ENGINE_NOTICE_WIRE_CODES: readonly ["memory.session_polluted", "memory.harvest_quarantined", "memory.delegation_static_mark_waived", "memory.hold_opened", "memory.hold_released", "memory.hold_disposed", "task.turn_interrupted", "steering.parked_input_blocked", "task.user_steer_undrained", "task.user_followup_undrained", "memory.capture_opted_out", "memory.capture_optout_unpersisted", "task.halt_unconsumed", "task.late_approval", "config.durable_gate_unavailable", "peer.inbound_disposition", "peer.held_settled", "peer.idle_subscription", "classifier.denial_limit", "mcp.injection_dropped", "delegation.ask_unresolvable", "task.interrupt_unconsumed", "approval.read_root_granted", "approval.read_root_grant_rejected", "instructions.source_changed", "session.records_skipped", "server.model_unreachable", "classifier.unavailable", "config.permission_mode_unavailable", "permission.ask_released_by_mode", "permission.read_deny_unapplied", "permission.rule_store_unavailable", "permission.rule_row_dropped", "permission.allowed_tool_not_granted", "task.tool_turn_incomplete", "route.finish_reason_unknown", "route.stream_end_ignored"];
20
20
  export type EngineNoticeWireCode = (typeof ENGINE_NOTICE_WIRE_CODES)[number];
21
21
  /** 白名单谓词(单点):路由与门都读这一个,不许第二处手抄码串。 */
22
22
  export declare function isEngineNoticeWireCode(code: string): code is EngineNoticeWireCode;
@@ -62,6 +62,31 @@ export interface EngineNoticeWireRow {
62
62
  * · 其余(function / symbol / bigint / undefined)⇒ 丢键(JSON 不可承载或语义不明)。
63
63
  */
64
64
  export declare function sanitizeNoticeDetail(detail: Record<string, unknown> | undefined): Record<string, unknown>;
65
+ /**
66
+ * `engine_notice` 载荷构造器(三条终点腿同源:live SSE / durable 账本 / 未来的任何第四条腿)。
67
+ *
68
+ * 与 `trace/project.ts` 的 `*EventData` 族同纪律(白名单挑键、禁 `{...notice}`),但**不住在那个文件里**:
69
+ * 那一族的入参全是 core `TaskEvent` 的臂(有 `wire-whitelist-exhaustiveness` 的臂集编译门看着),
70
+ * 而 `EngineNotice` 不是 `TaskEvent` —— 混进去会让那道门的语义变成"两个不同来源的联合",判别力反而降。
71
+ */
72
+ /**
73
+ * 通告 `detail` 里**规则文本**的坐标:码 → `{ 数组键, 元素上的文本键 }`。新码的 detail 带规则文本时在这里登记(一张表,不在各处写分支)。
74
+ * 规则文本是人或模型写下的条目原串,最可能夹着凭据;这张表管的只有一件事 —— 它离开进程的每一条路都先过 {@link redactListEntry}。
75
+ * 导出是给钉用的:`test/notice-entry-presentation.test.ts` 的「日志行 = 会话流行」一组逐码迭代这张表,新登记的码自动入组。
76
+ */
77
+ export declare const RULE_TEXT_DETAIL_FIELDS: Readonly<Record<string, {
78
+ readonly array: string;
79
+ readonly field: string;
80
+ }>>;
81
+ /**
82
+ * 通告 `message`(引擎铸的人读句)的脱敏 —— **唯一一只**:会话流行(经 {@link engineNoticeEventData},之后截到 {@link NOTICE_TEXT_MAX})与运维日志行
83
+ * (`boot/runner-deps.ts` 的转发席,不截)同调它 ⇒ 同一条通告的 `message` 在两条出路上逐字节同(会话流行另有 {@link NOTICE_TEXT_MAX} 字上限,
84
+ * 超长的那一截只在日志行上)。对**全部**通告码一视同仁,不按码分支:带规则文本的码(本表 {@link RULE_TEXT_DETAIL_FIELDS} 的登记码)的 `message` 里
85
+ * 条目以引擎的转义形出现,和别的自由文本一样只在这一只上被认凭据;认出凭据形才改字节,没有凭据形的句子逐字节原样。
86
+ * ⚠️ 已知边界(既有,两条出路同,见 CHANGELOG 7.120.0「条目」条):引擎把条目里的零宽字符写成 `\uXXXX` 转义,夹零宽字符的凭据在转义后的句子里认不出;
87
+ * 句子归引擎,本仓不改写 —— 机读位 `detail` 里的规则文本是在原串上认的({@link redactNoticeRuleText}),不受此限。
88
+ */
89
+ export declare function redactNoticeMessage(message: unknown): string;
65
90
  /**
66
91
  * 通告 `detail` 里的**规则文本**换成条目的脱敏原值({@link redactListEntry}:凭据换记号,不加引号、不转义 —— 机读位不是显示面,显示形只属于人读句)。
67
92
  * 会话流行(经 {@link engineNoticeEventData},之后照常过 {@link sanitizeNoticeDetail})与运维日志行(`boot/runner-deps.ts` 的转发席,其余字段逐字)
@@ -37,6 +37,8 @@ export const ENGINE_NOTICE_WIRE_CODES = [
37
37
  "permission.rule_row_dropped",
38
38
  "permission.allowed_tool_not_granted",
39
39
  "task.tool_turn_incomplete",
40
+ "route.finish_reason_unknown",
41
+ "route.stream_end_ignored",
40
42
  ];
41
43
  const WIRE_CODES = new Set(ENGINE_NOTICE_WIRE_CODES);
42
44
  export function isEngineNoticeWireCode(code) {
@@ -93,10 +95,13 @@ function sanitizeValue(v, depth) {
93
95
  }
94
96
  return undefined;
95
97
  }
96
- const RULE_TEXT_DETAIL_FIELDS = {
98
+ export const RULE_TEXT_DETAIL_FIELDS = {
97
99
  "permission.allowed_tool_not_granted": { array: "entries", field: "entry" },
98
100
  "permission.rule_row_dropped": { array: "rows", field: "rule" },
99
101
  };
102
+ export function redactNoticeMessage(message) {
103
+ return redactSecrets(String(message));
104
+ }
100
105
  export function redactNoticeRuleText(code, detail) {
101
106
  const at = Object.hasOwn(RULE_TEXT_DETAIL_FIELDS, code) ? RULE_TEXT_DETAIL_FIELDS[code] : undefined;
102
107
  if (at === undefined || detail === undefined)
@@ -113,7 +118,7 @@ export function redactNoticeRuleText(code, detail) {
113
118
  export function engineNoticeEventData(notice, sessionId, nowMs) {
114
119
  return {
115
120
  code: notice.code,
116
- message: redactSecrets(String(notice.message)).slice(0, NOTICE_TEXT_MAX),
121
+ message: redactNoticeMessage(notice.message).slice(0, NOTICE_TEXT_MAX),
117
122
  detail: sanitizeNoticeDetail(redactNoticeRuleText(notice.code, notice.detail)),
118
123
  sessionId,
119
124
  ts: nowMs,
@@ -39,9 +39,15 @@ export type LedgerEventType = "reasoning" | "text" | "tool_start" | "tool_end" |
39
39
  /** [ref](core 7.18.0):本条腿的**延迟工具普查**快照(policy / thresholdPercent? / deferred[] / activated[])。
40
40
  * 与 `tool_roster_delta` 同判、同理由——**快照语义**(整只替换,回放零合并),而开腿那一份住在
41
41
  * `wiring_manifest` 之后的首帧、**之后每一次激活**只活在本帧上。整帧缺席 = 这条腿什么都没延迟。
42
- * 🔴 `tool_progress`([ref])**刻意不在本表**:core 的 `tool_progress.ephemeral` 契约逐字「LIVE ONLY …
43
- * core never persists this frame」,与 `task_progress` / `status` 同一处置(两条 durable 腿零 case)。 */
44
- | "tool_disclosure" | "human_input" | "task_progress" | "task_notification" | "turn_end"
42
+ * 🔴 `tool_progress`([ref])**刻意不在本表**,理由是它自己的、不是「core 标了 LIVE ONLY」:那一条只是**引擎**的义务
43
+ * (不进会话树 / 检查点 / 续跑重放),本仓的账本是异步车道的活流与断线重连面,律是「EPHEMERAL 帧带着回放面没有第二来源的
44
+ * 事实 ⇒ 落账」(`status` / `task_progress` / `task_started` 都落)。`tool_progress` 的事实由同一只调用的 `tool_end` 推出
45
+ * (真源在那里;帧的缺席也从不是证据 —— 一个周期内跑完的调用零帧),所以它不落。 */
46
+ | "tool_disclosure" | "human_input"
47
+ /** core 7.50.0:委派子代**起跑**帧(每周期一只,先于子代的首个 `task_progress`)。按上面那一条律落账:
48
+ * `agentType` / `rootSessionId` / `parentSessionId` / `startedAt` 只骑这一帧,`task_progress` 上没有 ⇒ 不落则异步车道
49
+ * (`POST /v1/runs` 的活流就是 `GET /v1/runs/:id/events` 的账本 tail)永远读不到。两条 durable 腿主流 + forward 道各一处写口。 */
50
+ | "task_started" | "task_progress" | "task_notification" | "turn_end"
45
51
  /** park 行名**两个**(`appendParked(kind)` 逐字取自 `result.status`)—— 运行期闭集见
46
52
  * {@link PARK_LEDGER_EVENT_TYPES}(本联合是它的类型面,两者由下方的双向断言锁步)。 */
47
53
  | "suspended" | "needs_review" | "done" | "failed" | "suggestions" | "model_usage" | "prompt_assembled" | "config_assembled" | "file_link"
@@ -97,6 +97,13 @@ export interface LedgerSink {
97
97
  * ⚠️ 用它的前提是**这条腿的全部写口都走它**(混用一个绕开链的写口 = 病灶原样保留)。
98
98
  */
99
99
  export declare function createSerialLedgerAppend(appendEvent: (seq: number, type: LedgerEventType, data: unknown) => Promise<void>, startSeq: number): (type: LedgerEventType, data: unknown) => Promise<void>;
100
+ /**
101
+ * durable 腿 forward 道(fire-and-forget,同步回调里不能 await)的写失败出口 —— 一只,两处用:本汇的 `onForwardEvent`(bg 腿 +
102
+ * sync detach 车道 + 续跑腿的 verify 臂)与续跑腿自己的 forward 回调(`http/resume-legs.ts`,那里另有既有的逐次 warn 行)。
103
+ * F 类留痕 `server.ledger.forward-append-failed`:这一帧永远不在回放面(子代的起跑帧 / tick / 工具卡),run 照跑、不改任何裁决。
104
+ * 返回 `.catch` 用的处理器(产物是函数 ⇒ `create*`)。
105
+ */
106
+ export declare function createForwardAppendFailedNote(type: string): (err: unknown) => void;
100
107
  export declare function createLedgerSink(opts: {
101
108
  /** The seq-stamped durable append — typically `(seq, type, data) => runStore.appendEvent(taskId, seq, type, data)`. */
102
109
  appendEvent: (seq: number, type: LedgerEventType, data: unknown) => Promise<void>;
@@ -1,6 +1,7 @@
1
1
  import {} from "@sema-agent/core";
2
2
  import { outwardPlane } from "../terminal.js";
3
- import { modelTextEventData, toolStartEventData, toolEndEventData, taskProgressEventData, taskNotificationEventData, compactedEventData, diagnosticsEventData, brainStatusEventData, steeringInjectedEventData, compactionOutcomeEventData, workspaceChangedEventData, wiringManifestEventData, toolRosterDeltaEventData, toolDisclosureEventData, humanInputEventData, turnEndEventData, contextUsageEventData } from "./project.js";
3
+ import { recordFailOpen } from "../observability/fail-open.js";
4
+ import { modelTextEventData, toolStartEventData, toolEndEventData, taskProgressEventData, taskStartedEventData, taskNotificationEventData, compactedEventData, diagnosticsEventData, brainStatusEventData, steeringInjectedEventData, compactionOutcomeEventData, workspaceChangedEventData, wiringManifestEventData, toolRosterDeltaEventData, toolDisclosureEventData, humanInputEventData, turnEndEventData, contextUsageEventData } from "./project.js";
4
5
  import { NotifiedKeys, taskNotificationStreamKeyOf } from "../orchestration/workflow-completion-inbox.js";
5
6
  import { isParkLedgerEventType } from "./ledger-events.js";
6
7
  import { isModelSegmentBoundary, reasoningDeltaBytes, reasoningSegmentEventId } from "./model-segment.js";
@@ -13,6 +14,9 @@ export function createSerialLedgerAppend(appendEvent, startSeq) {
13
14
  return step;
14
15
  };
15
16
  }
17
+ export function createForwardAppendFailedNote(type) {
18
+ return (err) => recordFailOpen("server.ledger.forward-append-failed", `type=${type} err=${err instanceof Error ? err.message : String(err)}`);
19
+ }
16
20
  export function createLedgerSink(opts) {
17
21
  const keys = opts.notifiedKeys ?? new NotifiedKeys();
18
22
  let seq = 0;
@@ -114,6 +118,9 @@ export function createLedgerSink(opts) {
114
118
  case "human_input":
115
119
  await append("human_input", humanInputEventData(ev));
116
120
  break;
121
+ case "task_started":
122
+ await append("task_started", taskStartedEventData(ev));
123
+ break;
117
124
  case "task_progress":
118
125
  await append("task_progress", taskProgressEventData(ev));
119
126
  break;
@@ -140,13 +147,16 @@ export function createLedgerSink(opts) {
140
147
  const onForwardEvent = (e) => {
141
148
  const t = e.type;
142
149
  if (t === "task_progress") {
143
- void append("task_progress", taskProgressEventData(e)).catch(() => undefined);
150
+ void append("task_progress", taskProgressEventData(e)).catch(createForwardAppendFailedNote(t));
151
+ }
152
+ else if (t === "task_started") {
153
+ void append("task_started", taskStartedEventData(e)).catch(createForwardAppendFailedNote(t));
144
154
  }
145
155
  else if (t === "tool_start") {
146
- void append("tool_start", toolStartEventData(e)).catch(() => undefined);
156
+ void append("tool_start", toolStartEventData(e)).catch(createForwardAppendFailedNote(t));
147
157
  }
148
158
  else if (t === "tool_end") {
149
- void append("tool_end", toolEndEventData(e)).catch(() => undefined);
159
+ void append("tool_end", toolEndEventData(e)).catch(createForwardAppendFailedNote(t));
150
160
  }
151
161
  };
152
162
  const appendStatus = (ev) => {