@sema-agent/server 7.135.0 → 7.137.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 (342) hide show
  1. package/MIGRATION.md +237 -2
  2. package/USAGE.md +146 -41
  3. package/deploy/sema-up/README.md +15 -1
  4. package/deploy/sema-up/chart/templates/NOTES.txt +1 -1
  5. package/deploy/sema-up/chart/templates/sandbox-rbac.yaml +7 -1
  6. package/deploy/sema-up/chart/values.yaml +1 -1
  7. package/deploy/sema-up/stack/sema-stack-cluster.yaml +7 -1
  8. package/dist/approval-card.d.ts +60 -4
  9. package/dist/approval-card.js +18 -1
  10. package/dist/approval-content-kind.d.ts +7 -0
  11. package/dist/approval-content-kind.js +8 -1
  12. package/dist/approval-gate-currency.d.ts +48 -6
  13. package/dist/approval-gate-currency.js +16 -9
  14. package/dist/approval-hmac.d.ts +13 -1
  15. package/dist/approval-hmac.js +1 -1
  16. package/dist/approval.js +3 -3
  17. package/dist/auth-bridge.d.ts +34 -5
  18. package/dist/auth-bridge.js +22 -5
  19. package/dist/boot/budget-tracing.d.ts +1 -2
  20. package/dist/boot/budget-tracing.js +2 -2
  21. package/dist/boot/execution-env.d.ts +24 -1
  22. package/dist/boot/execution-env.js +69 -6
  23. package/dist/boot/org-memory.js +1 -1
  24. package/dist/boot/reapers.d.ts +3 -0
  25. package/dist/boot/reapers.js +15 -1
  26. package/dist/boot/resolve-spec.d.ts +4 -0
  27. package/dist/boot/resolve-spec.js +24 -22
  28. package/dist/boot/runner-deps.js +4 -2
  29. package/dist/boot/stage-03-store-backend.d.ts +1 -1
  30. package/dist/boot/stage-04-stores.d.ts +2 -2
  31. package/dist/boot/stage-05-execution-env.d.ts +4 -2
  32. package/dist/boot/stage-05-execution-env.js +11 -2
  33. package/dist/boot/stage-06-runners.d.ts +4 -2
  34. package/dist/boot/stage-06-runners.js +5 -1
  35. package/dist/boot/stage-07-capability-layer.d.ts +5 -2
  36. package/dist/boot/stage-07-capability-layer.js +1 -1
  37. package/dist/boot/stage-08-reapers.d.ts +5 -2
  38. package/dist/boot/stage-08-reapers.js +2 -1
  39. package/dist/boot/stage-09-leader.d.ts +5 -2
  40. package/dist/boot/stage-10-http-server.d.ts +5 -2
  41. package/dist/boot/stage-10-http-server.js +3 -2
  42. package/dist/boot/stores.d.ts +1 -1
  43. package/dist/boot/stores.js +2 -3
  44. package/dist/boot/web-admin.js +10 -17
  45. package/dist/brain.js +12 -3
  46. package/dist/broker/broker.d.ts +89 -0
  47. package/dist/broker/broker.js +287 -0
  48. package/dist/broker/constants.d.ts +39 -0
  49. package/dist/broker/constants.js +15 -0
  50. package/dist/broker/http1.d.ts +111 -0
  51. package/dist/broker/http1.js +256 -0
  52. package/dist/broker/lease-book.d.ts +87 -0
  53. package/dist/broker/lease-book.js +142 -0
  54. package/dist/broker/lease-file.d.ts +60 -0
  55. package/dist/broker/lease-file.js +127 -0
  56. package/dist/broker/main.d.ts +2 -0
  57. package/dist/broker/main.js +20 -0
  58. package/dist/budget.d.ts +3 -3
  59. package/dist/budget.js +4 -0
  60. package/dist/capabilities/composite-members.d.ts +2 -1
  61. package/dist/capabilities/composite-members.js +2 -1
  62. package/dist/capabilities/engine-capabilities.js +1 -0
  63. package/dist/capabilities/mcp-probe.d.ts +5 -4
  64. package/dist/capabilities/scenarios.d.ts +23 -8
  65. package/dist/capabilities/scenarios.js +13 -8
  66. package/dist/capabilities/skills.d.ts +28 -5
  67. package/dist/capabilities/skills.js +8 -0
  68. package/dist/capability-facts.d.ts +3 -7
  69. package/dist/capability-facts.js +3 -5
  70. package/dist/center-credential-renewer.js +5 -1
  71. package/dist/center-credential.d.ts +18 -4
  72. package/dist/center-credential.js +18 -3
  73. package/dist/checkpoint-ctx-seal-m28.d.ts +3 -10
  74. package/dist/checkpoint-ctx-seal-m28.js +1 -1
  75. package/dist/checkpoint-identity.d.ts +3 -3
  76. package/dist/config-catalog.js +21 -16
  77. package/dist/config-d-migration.js +1 -1
  78. package/dist/config-invariants.d.ts +1 -1
  79. package/dist/config-invariants.js +8 -0
  80. package/dist/config-lkg.d.ts +0 -38
  81. package/dist/config-lkg.js +1 -83
  82. package/dist/config-types.d.ts +21 -4
  83. package/dist/config.d.ts +1 -1
  84. package/dist/config.js +60 -16
  85. package/dist/deployment-governance.d.ts +2 -2
  86. package/dist/deployment-governance.js +7 -9
  87. package/dist/deployment-shape.d.ts +1 -1
  88. package/dist/device-ws-protocol.d.ts +1 -0
  89. package/dist/device-ws-protocol.js +2 -1
  90. package/dist/elicitation.d.ts +8 -7
  91. package/dist/elicitation.js +9 -8
  92. package/dist/file-history-disclosure.d.ts +8 -4
  93. package/dist/file-history-disclosure.js +24 -6
  94. package/dist/fleet-client.d.ts +32 -1
  95. package/dist/fleet-client.js +24 -4
  96. package/dist/fleet-lease.d.ts +7 -3
  97. package/dist/fleet-lease.js +31 -23
  98. package/dist/hooks/classic-stations.d.ts +11 -54
  99. package/dist/hooks/hook-llm.js +14 -8
  100. package/dist/hooks/hook-runner.d.ts +4 -4
  101. package/dist/hooks/hook-runner.js +2 -2
  102. package/dist/http/admission.js +10 -7
  103. package/dist/http/approval-sweeps.js +4 -3
  104. package/dist/http/caller-trust.d.ts +2 -1
  105. package/dist/http/caller-trust.js +1 -3
  106. package/dist/http/checkpoint-ctx.d.ts +76 -16
  107. package/dist/http/checkpoint-ctx.js +45 -1
  108. package/dist/http/dispatch.js +24 -19
  109. package/dist/http/durable-run-leg.d.ts +5 -1
  110. package/dist/http/durable-run-leg.js +1 -1
  111. package/dist/http/health-self-report.d.ts +2 -2
  112. package/dist/http/mcp-remote-text.d.ts +5 -0
  113. package/dist/http/mcp-remote-text.js +7 -0
  114. package/dist/http/principal-gate.d.ts +15 -10
  115. package/dist/http/principal-gate.js +48 -9
  116. package/dist/http/resume-ctx-read.d.ts +2 -16
  117. package/dist/http/resume-ctx-read.js +2 -3
  118. package/dist/http/resume-legs.d.ts +2 -20
  119. package/dist/http/resume-legs.js +52 -38
  120. package/dist/http/route-ctx.d.ts +8 -2
  121. package/dist/http/routes/approvals-assistant.d.ts +0 -2
  122. package/dist/http/routes/approvals-assistant.js +65 -16
  123. package/dist/http/routes/capabilities.js +11 -1
  124. package/dist/http/routes/mcp-reconnect.d.ts +1 -1
  125. package/dist/http/routes/mcp-reconnect.js +5 -7
  126. package/dist/http/routes/runs.js +4 -3
  127. package/dist/http/routes/session-sync.js +70 -12
  128. package/dist/http/routes/sessions.js +20 -15
  129. package/dist/http/routes/tasks.js +8 -6
  130. package/dist/http/server.d.ts +4 -0
  131. package/dist/http/stored-body-replay.d.ts +3 -3
  132. package/dist/http/stored-body-replay.js +6 -1
  133. package/dist/http/submit-ctx.js +1 -1
  134. package/dist/http/wire-types.d.ts +57 -30
  135. package/dist/identity-source.d.ts +65 -0
  136. package/dist/identity-source.js +45 -0
  137. package/dist/index.d.ts +1 -0
  138. package/dist/index.js +1 -0
  139. package/dist/memory-store-refusal.d.ts +3 -3
  140. package/dist/model-entry-refusal.d.ts +2 -0
  141. package/dist/model-entry-refusal.js +11 -0
  142. package/dist/model-route-gap.d.ts +49 -4
  143. package/dist/model-route-gap.js +34 -1
  144. package/dist/model-select.d.ts +6 -17
  145. package/dist/model-select.js +0 -3
  146. package/dist/namespaced-rule-name.d.ts +1 -1
  147. package/dist/object-store-buckets.d.ts +13 -0
  148. package/dist/object-store-buckets.js +3 -0
  149. package/dist/observability/fail-open.d.ts +19 -7
  150. package/dist/observability/fail-open.js +19 -7
  151. package/dist/observability/metrics.js +2 -2
  152. package/dist/observability/principal-context.d.ts +4 -1
  153. package/dist/observability/principal-context.js +6 -1
  154. package/dist/observability/run-terminal.js +1 -0
  155. package/dist/operator-ask.d.ts +2 -2
  156. package/dist/org-delivery-gate.js +1 -1
  157. package/dist/org-memory-admission.d.ts +2 -2
  158. package/dist/org-memory-admission.js +39 -2
  159. package/dist/parked-decide.d.ts +4 -3
  160. package/dist/parked-decide.js +29 -9
  161. package/dist/peer-directory.d.ts +2 -1
  162. package/dist/peer-directory.js +7 -5
  163. package/dist/permission-rule-list.d.ts +2 -2
  164. package/dist/plugins/approval-ask-store-sql.d.ts +8 -0
  165. package/dist/plugins/approval-ask-store-sql.js +3 -0
  166. package/dist/plugins/blob-backend.js +2 -1
  167. package/dist/plugins/checkpoint-index-columns.d.ts +50 -1
  168. package/dist/plugins/checkpoint-index-columns.js +28 -0
  169. package/dist/plugins/checkpoint-store-sql.d.ts +46 -12
  170. package/dist/plugins/checkpoint-store-sql.js +64 -35
  171. package/dist/plugins/file-history-store-sql.d.ts +6 -0
  172. package/dist/plugins/file-history-store-sql.js +37 -37
  173. package/dist/plugins/file-run-store.js +8 -2
  174. package/dist/plugins/k8s-exec-protocol.d.ts +69 -13
  175. package/dist/plugins/k8s-exec-protocol.js +116 -28
  176. package/dist/plugins/k8s-pod-delete-failure.d.ts +25 -0
  177. package/dist/plugins/k8s-pod-delete-failure.js +17 -0
  178. package/dist/plugins/k8s-pod-reaper.d.ts +112 -0
  179. package/dist/plugins/k8s-pod-reaper.js +122 -0
  180. package/dist/plugins/local-checkpoint-store.d.ts +3 -6
  181. package/dist/plugins/local-checkpoint-store.js +7 -6
  182. package/dist/plugins/memory-run-store.js +2 -0
  183. package/dist/plugins/one-time-migrations.d.ts +49 -37
  184. package/dist/plugins/one-time-migrations.js +87 -128
  185. package/dist/plugins/permission-rule-store-file.js +2 -2
  186. package/dist/plugins/permission-rule-store-sql.d.ts +17 -17
  187. package/dist/plugins/permission-rule-store-sql.js +16 -17
  188. package/dist/plugins/pg-pool.js +4 -0
  189. package/dist/plugins/pg-session-storage.js +2 -2
  190. package/dist/plugins/remote-env-adb.d.ts +3 -0
  191. package/dist/plugins/remote-env-adb.js +1 -0
  192. package/dist/plugins/remote-env-device.d.ts +3 -0
  193. package/dist/plugins/remote-env-device.js +4 -0
  194. package/dist/plugins/remote-env-e2b.d.ts +3 -0
  195. package/dist/plugins/remote-env-e2b.js +1 -0
  196. package/dist/plugins/remote-env-file-error.d.ts +8 -1
  197. package/dist/plugins/remote-env-file-error.js +7 -1
  198. package/dist/plugins/remote-env-host.d.ts +7 -1
  199. package/dist/plugins/remote-env-host.js +7 -1
  200. package/dist/plugins/remote-env-k8s.d.ts +154 -14
  201. package/dist/plugins/remote-env-k8s.js +300 -21
  202. package/dist/plugins/remote-env-local-docker.d.ts +3 -0
  203. package/dist/plugins/remote-env-local-docker.js +1 -0
  204. package/dist/plugins/remote-env-ssh.d.ts +3 -0
  205. package/dist/plugins/remote-env-ssh.js +1 -0
  206. package/dist/plugins/run-store-sql.js +10 -4
  207. package/dist/plugins/s3-answer.d.ts +15 -0
  208. package/dist/plugins/s3-answer.js +5 -0
  209. package/dist/plugins/session-ownership.d.ts +17 -3
  210. package/dist/plugins/session-ownership.js +19 -5
  211. package/dist/plugins/snapshot-bucket-presence.d.ts +39 -4
  212. package/dist/plugins/snapshot-bucket-presence.js +35 -7
  213. package/dist/plugins/sql-errors.d.ts +60 -0
  214. package/dist/plugins/sql-errors.js +64 -1
  215. package/dist/plugins/store-contracts.d.ts +10 -1
  216. package/dist/plugins/store-fault.d.ts +4 -0
  217. package/dist/plugins/store-fault.js +10 -0
  218. package/dist/plugins/tidb-pool.js +8 -1
  219. package/dist/plugins/tidb-session-store.js +2 -2
  220. package/dist/plugins/web-search.d.ts +1 -1
  221. package/dist/plugins/web-search.js +2 -2
  222. package/dist/request-declaration-refusal.d.ts +94 -0
  223. package/dist/request-declaration-refusal.js +78 -0
  224. package/dist/request-image-urls.d.ts +6 -0
  225. package/dist/request-image-urls.js +27 -0
  226. package/dist/request-key-closure.d.ts +5 -0
  227. package/dist/request-key-closure.js +5 -2
  228. package/dist/request-value-closure.d.ts +31 -2
  229. package/dist/request-value-closure.js +7 -1
  230. package/dist/rule-entry-list.d.ts +3 -2
  231. package/dist/rule-entry-list.js +2 -2
  232. package/dist/rules-consent.js +7 -4
  233. package/dist/run-local.d.ts +2 -2
  234. package/dist/runs.js +2 -1
  235. package/dist/runtime-caps-resolver.js +1 -1
  236. package/dist/runtime-governance.d.ts +5 -5
  237. package/dist/sandbox-git-lease.d.ts +49 -0
  238. package/dist/sandbox-git-lease.js +60 -0
  239. package/dist/sandbox-git-plane.d.ts +84 -0
  240. package/dist/sandbox-git-plane.js +105 -0
  241. package/dist/sandbox-pkg-source.d.ts +28 -1
  242. package/dist/sandbox-pkg-source.js +1 -2
  243. package/dist/sealed-key.d.ts +19 -0
  244. package/dist/sealed-key.js +47 -4
  245. package/dist/security.d.ts +16 -4
  246. package/dist/security.js +8 -3
  247. package/dist/server-secret-env.d.ts +6 -0
  248. package/dist/server-secret-env.js +14 -0
  249. package/dist/session-error-wire.d.ts +14 -6
  250. package/dist/session-error-wire.js +2 -1
  251. package/dist/session-incarnation.d.ts +16 -5
  252. package/dist/session-incarnation.js +7 -4
  253. package/dist/session-policy-wire.d.ts +1 -1
  254. package/dist/session-policy-wire.js +2 -2
  255. package/dist/session-row.d.ts +3 -17
  256. package/dist/session-row.js +4 -0
  257. package/dist/session-sync.d.ts +15 -3
  258. package/dist/session-sync.js +5 -2
  259. package/dist/spec-fields.d.ts +43 -13
  260. package/dist/spec-fields.js +33 -12
  261. package/dist/stored-request-prompt-profile-m30.d.ts +49 -0
  262. package/dist/stored-request-prompt-profile-m30.js +25 -0
  263. package/dist/task-a2a.d.ts +52 -81
  264. package/dist/task-a2a.js +219 -89
  265. package/dist/task-mcp.d.ts +7 -15
  266. package/dist/task-mcp.js +16 -51
  267. package/dist/task-settings.d.ts +2 -2
  268. package/dist/tool-approval.d.ts +35 -12
  269. package/dist/tool-approval.js +59 -39
  270. package/dist/tool-rule-names.d.ts +5 -0
  271. package/dist/trace/core-keyset-guard.d.ts +5 -5
  272. package/dist/trace/engine-notice-wire.d.ts +1 -1
  273. package/dist/trace/engine-notice-wire.js +2 -0
  274. package/dist/trace/exit-seat.js +37 -13
  275. package/dist/trace/ledger-sink.d.ts +4 -5
  276. package/dist/trace/ledger-sink.js +3 -7
  277. package/dist/trace/project.d.ts +37 -3
  278. package/dist/trace/project.js +20 -6
  279. package/dist/trace/redact.d.ts +40 -12
  280. package/dist/trace/redact.js +332 -128
  281. package/dist/trace/self-minted-wire.d.ts +641 -0
  282. package/dist/trace/self-minted-wire.js +201 -0
  283. package/dist/trace/wire-projection-faces.d.ts +5 -3
  284. package/dist/trace/wire-projection-faces.js +7 -3
  285. package/dist/url-credential-refusal.d.ts +52 -0
  286. package/dist/url-credential-refusal.js +14 -0
  287. package/dist/url-refusal.d.ts +13 -4
  288. package/dist/url-refusal.js +8 -2
  289. package/dist/web-admin-client/apply-effective.js +0 -1
  290. package/dist/web-admin-client/center-request.d.ts +20 -2
  291. package/dist/web-admin-client/center-request.js +40 -5
  292. package/dist/web-admin-client/http-client.js +6 -6
  293. package/dist/web-admin-client/types.d.ts +5 -5
  294. package/dist/workspace-space.d.ts +27 -0
  295. package/dist/workspace-space.js +26 -0
  296. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +11 -5
  297. package/package.json +4 -3
  298. package/scripts/drain-check-local.mjs +2 -1
  299. package/scripts/drain-check.mysql.sql +3 -1
  300. package/scripts/drain-check.pg.sql +3 -1
  301. package/skills/schedule.md +1 -1
  302. package/wire-changes/server@7.104.0.json +134 -0
  303. package/wire-changes/server@7.105.0.json +53 -0
  304. package/wire-changes/server@7.106.0.json +475 -0
  305. package/wire-changes/server@7.107.0.json +64 -0
  306. package/wire-changes/server@7.108.0.json +153 -0
  307. package/wire-changes/server@7.109.0.json +46 -0
  308. package/wire-changes/server@7.110.0.json +90 -0
  309. package/wire-changes/server@7.111.0.json +86 -0
  310. package/wire-changes/server@7.112.0.json +60 -0
  311. package/wire-changes/server@7.113.0.json +26 -0
  312. package/wire-changes/server@7.114.0.json +26 -0
  313. package/wire-changes/server@7.115.0.json +97 -0
  314. package/wire-changes/server@7.116.0.json +218 -0
  315. package/wire-changes/server@7.116.1.json +26 -0
  316. package/wire-changes/server@7.117.0.json +65 -0
  317. package/wire-changes/server@7.118.0.json +129 -0
  318. package/wire-changes/server@7.119.0.json +65 -0
  319. package/wire-changes/server@7.120.0.json +89 -0
  320. package/wire-changes/server@7.121.0.json +219 -0
  321. package/wire-changes/server@7.122.0.json +54 -0
  322. package/wire-changes/server@7.123.0.json +26 -0
  323. package/wire-changes/server@7.124.0.json +336 -0
  324. package/wire-changes/server@7.124.1.json +26 -0
  325. package/wire-changes/server@7.125.0.json +111 -0
  326. package/wire-changes/server@7.126.0.json +285 -0
  327. package/wire-changes/server@7.127.0.json +77 -0
  328. package/wire-changes/server@7.127.1.json +26 -0
  329. package/wire-changes/server@7.127.2.json +53 -0
  330. package/wire-changes/server@7.128.0.json +26 -0
  331. package/wire-changes/server@7.129.0.json +106 -0
  332. package/wire-changes/server@7.129.1.json +26 -0
  333. package/wire-changes/server@7.130.0.json +26 -0
  334. package/wire-changes/server@7.131.0.json +118 -0
  335. package/wire-changes/server@7.132.0.json +226 -0
  336. package/wire-changes/server@7.133.0.json +1155 -0
  337. package/wire-changes/server@7.134.0.json +107 -0
  338. package/wire-changes/server@7.135.0.json +174 -0
  339. package/wire-changes/server@7.136.0.json +448 -0
  340. package/wire-changes/server@7.137.0.json +825 -0
  341. package/dist/url-credential.d.ts +0 -52
  342. package/dist/url-credential.js +0 -49
@@ -16,6 +16,7 @@ import { CheckpointError, type QuestionAnswer, type CheckpointToken } from "@sem
16
16
  import type { DecideBinding } from "./wire-types.js";
17
17
  import type { CheckpointStoreFull } from "../plugins/store-backend.js";
18
18
  import type { HostDecision } from "../host-decision.js";
19
+ import { type GateSighting } from "../approval-gate-currency.js";
19
20
  import { type PlanApprovePermissionModeAfter } from "../task-settings.js";
20
21
  import type { RouteCtxBase, DriveResumeArgs, HostLegOutcome, PreparedPlanReview, ResumeLegOutcome, WorkflowDecisionDelivery } from "./route-ctx.js";
21
22
  import type { RunRegistry, RouteHelpers, RouteLegs } from "./route-ctx.js";
@@ -28,25 +29,6 @@ import { type ResumeRefusalAnswer } from "./checkpoint-ctx.js";
28
29
  * ⇒ `"terminal"`:与 `by_reason` 认不得的词同一条 fail-closed 方向(不宽容默认成可重试),也与修前手列串的缺省逐字相同。
29
30
  */
30
31
  export declare function resumeRefusalDisposition(e: CheckpointError): "cancelled" | "retriable" | "terminal";
31
- /**
32
- * 🔴 **引擎拒一次续跑(`CheckpointError`)的 HTTP 答复 —— 唯一组句器**([ref] / [ref],core 7.52.0;五条续跑腿〔`/decide` · `/resume` · `/wake` · `plan_review` ·
33
- * workflow 决定在宿主会话上起的新一跑〕同经 `driveResumeLeg` 的 catch 臂到这里,SLA 拒扫腿与 PARKED 赎回席经 `resumeCheckpoint` 同得)。
34
- *
35
- * 规则(一条):**一次拒 = 一个答复形,与「谁发现的」无关**。引擎在认领前 / CAS 前拒的,与本服务在重放存量体之前自己拒的(`resume_blocked_by_policy`,
36
- * `checkpoint-ctx.ts` `resumeBlockedByPolicyAnswer`;续跑入口上卡仍 pending ⇒ 同样带 `retriable:true`)读同一组机读键;体经拒体组句器 `refusalBody` 铸,
37
- * `retriable` 经续跑家族的唯一写点 `resumeRefusalWithCard` 追加。
38
- * 后台停驻子代的 parked 赎回不经本函数(引擎交的是赎回回执,不是 `CheckpointError`):`parked-decide.ts` 答 `422 parked_resume.startup_failed`,体带成因词 `cause`,
39
- * 引擎 7.54.0 起回执的 `details.detail` 交出同三位判别位,经**同一只**读法 `resumeRefusalFacts` 落成同名三键(下面第二条)。键律:
40
- * · `errorCode` —— 引擎码的原码镜像(`checkpoint.invalid_outcome` 按决断成因改铸,{@link decisionRefusalCauseOf});
41
- * · 引擎 `detail` 的三个机读判别位原样过境,键名与 `resume_blocked_by_policy` 同义:`detail.code` ⇒ `blockedBy`(挡住它的准入码 = 同一份坏配置在新跑上报的 `errorCode`;
42
- * 今天只有 `checkpoint.config_refused` 带它)· `detail.field` ⇒ `field`(哪一侧 / 哪一格)· `detail.reason` ⇒ `reason`(同码多臂时哪一臂,闭集 `CheckpointErrorReason`)。
43
- * 缺席不造键(引擎没给就是没给)。`detail` 的其余成员(`requires` / `differs`)不在这组判别位里,不上 wire;`retryAfterMs` 折成族口径的 `retryAfterSec`(下面);
44
- * · `retriable` —— 只在处置读口答 `retriable` 时在场(卡仍 pending、行重新 park;**不是**「原样重试会成功」—— 契约附录 A.1 注那一个意思,壳按它决定卡还挂不挂着)。
45
- * 出路(改那一格再决 / 只能在停它的那一版 worker 上跑完或取消 / 等一会)由 `errorCode` + `field` + `blockedBy` + `reason` 与引擎的句子说;
46
- * · `error` —— 引擎的句子原样(它点名条目、哪一臂与出路);本层不另铸第二句;
47
- * · `taskId`(抢到行才在)/ `retryAfterSec`(两码闭集)/ 两版本键(只在 `checkpoint.unsupported_version`:调用方按那一码交 `buildCheckpointVersionKeys` 的产物,本函数原样铺)—— 键律照旧。
48
- * 纯函数(钉直接调)。`retriable` 由调用方传入 —— 行的落点与 wire 的 `retriable` 读同一次求值(catch 臂一处),本函数不另判。
49
- */
50
32
  export declare function checkpointRefusalAnswer(e: CheckpointError, at: {
51
33
  readonly gate: string;
52
34
  readonly retriable: boolean;
@@ -89,7 +71,7 @@ export declare function createResumeLegs(ctx: {
89
71
  }, decider?: {
90
72
  principal?: string;
91
73
  explicitOperator: boolean;
92
- }) => Promise<{
74
+ }, sighting?: GateSighting) => Promise<{
93
75
  readonly ok: false;
94
76
  readonly answer: {
95
77
  status: number;
@@ -9,7 +9,7 @@ const headUnavailable = async (sessionId) => {
9
9
  throw new Error(`parked decide cannot verify which occupant session ${sessionId} belongs to: this deployment's session store has no head read (headOf; every built-in store has one) — refusing rather than resuming unverified`);
10
10
  };
11
11
  import { HttpError, decodeCheckpointScope, CHECKPOINT_PUBLIC_SCOPE } from "../security.js";
12
- import { parkToolCallId, TurnAnchorCapture, HEARTBEAT_MS, markChildrenStoppedByUserOnAbort } from "../runs.js";
12
+ import { graftParkToolCallId, parkToolCallId, TurnAnchorCapture, HEARTBEAT_MS, markChildrenStoppedByUserOnAbort } from "../runs.js";
13
13
  import { failureCodeOf, nestedPauseOf, sealResultForExit, outwardPlane } from "../terminal.js";
14
14
  import { buildCancelledLedgerRow, createRunAbort, mintCancelledResult } from "../run-cancel-context.js";
15
15
  import { isTerminalRunStatus } from "../plugins/store-contracts.js";
@@ -18,16 +18,17 @@ import { recordResumeResourceWindow } from "../resource-window.js";
18
18
  import { emitPendingWorkflowCompletions, taskNotificationInboxEntry, taskNotificationStreamKeyOf, NotifiedKeys } from "../orchestration/workflow-completion-inbox.js";
19
19
  import { fleetRunPublisher, fleetRunLabels, fleetRunResiduals, isFleetAgentTerminalNotification } from "../fleet/fleet-bus.js";
20
20
  import { defaultSubagentTailBus } from "../fleet/subagent-tail-bus.js";
21
- import { currentApprovalGateToken } from "../approval-gate-currency.js";
22
- import { createApprovalCardEmitter, declaredApprovalFrameFamilies, parkRowStandingAnswerFacts, resolveApprovalLeg, resolveStreamApprovalGate, standingAnswerDurablyRecordable } from "../tool-approval.js";
21
+ import { currentApprovalGateToken, currentGateFacesOf, sightsCurrentGate } from "../approval-gate-currency.js";
22
+ import { approverFacingArgs, createApprovalCardEmitter, declaredApprovalFrameFamilies, parkRowStandingAnswerFacts, resolveApprovalLeg, resolveStreamApprovalGate, standingAnswerDurablyRecordable } from "../tool-approval.js";
23
23
  import { buildCheckpointAnchorHash } from "../approval-ask-audit-store.js";
24
24
  import { APPROVAL_ENDING_CAUSES } from "../approval-deny-reasons.js";
25
25
  import { serviceVersion } from "../service-version.js";
26
26
  import { TERMINAL_PARK_GATE_KINDS } from "../plugins/checkpoint-store-sql.js";
27
27
  import { withPrincipal } from "../observability/principal-context.js";
28
28
  import { withModelReachScope } from "../model-reach-scope.js";
29
+ import { runSkillsOf } from "../capabilities/skills.js";
29
30
  import { createInstructionSelectionLeg } from "../instruction-selection.js";
30
- import { modelTextEventData, turnEndEventData, contextUsageEventData, toolStartEventData, toolEndEventData, taskProgressEventData, taskStartedEventData, taskNotificationEventData, compactedEventData, diagnosticsEventData, brainStatusEventData, steeringInjectedEventData, compactionOutcomeEventData, compactionStartedEventData, workspaceChangedEventData, wiringManifestEventData, toolRosterDeltaEventData, toolDisclosureEventData, humanInputEventData, appendModelUsageDelta, appendPromptManifest, attachModelUsage } from "../trace/project.js";
31
+ import { modelTextEventData, parkEventData, turnEndEventData, contextUsageEventData, toolStartEventData, toolEndEventData, taskProgressEventData, taskStartedEventData, taskNotificationEventData, compactedEventData, diagnosticsEventData, brainStatusEventData, steeringInjectedEventData, compactionOutcomeEventData, compactionStartedEventData, workspaceChangedEventData, wiringManifestEventData, toolRosterDeltaEventData, toolDisclosureEventData, humanInputEventData, appendModelUsageDelta, appendPromptManifest, attachModelUsage } from "../trace/project.js";
31
32
  import { redactSecrets } from "../trace/redact.js";
32
33
  import { buildRunObjectivePreview } from "../user-authored-text.js";
33
34
  import { createForwardAppendFailedNote, createLedgerSink, createSerialLedgerAppend } from "../trace/ledger-sink.js";
@@ -43,11 +44,11 @@ import { createSessionsLocal } from "./routes/sessions.js";
43
44
  import { createSessionSyncLocal } from "./routes/session-sync.js";
44
45
  import { createImagesLocal } from "./routes/images.js";
45
46
  import { createCapabilitiesLocal } from "./routes/capabilities.js";
46
- import { gatedPrincipal, explicitOperatorOk } from "./principal-gate.js";
47
+ import { gatedPrincipal, explicitOperatorOk, runIdentityOf } from "./principal-gate.js";
47
48
  import { buildActiveRunConflict, resumeEntryForGate } from "./active-run-conflict.js";
48
49
  import { commitPlacement, devicePlacementErrorBody } from "./device-placement.js";
49
50
  import { commitSessionInheritEnv } from "./session-inherit-env.js";
50
- import { ASK_USER_QUESTION_TOOL_NAME, isApprovalGateKind } from "../approval-content-kind.js";
51
+ import { ASK_USER_QUESTION_TOOL_NAME, isApprovalGateKind, isEngineQuestionRow } from "../approval-content-kind.js";
51
52
  import { deploymentListAsks } from "../approval.js";
52
53
  import { readResumeCtx, resumeCtxCorruptAnswer, resumeCtxUnsealableAnswer } from "./resume-ctx-read.js";
53
54
  import { buildCheckpointVersionKeys, resumeBlockedByPolicyAnswer, resumeRefusalWithCard, sealCheckpointCtx, storedBodyReplayAnswer, storedBodyReplayLogFields, withPermissionModeRewritten } from "./checkpoint-ctx.js";
@@ -79,6 +80,7 @@ const WORKER_SWAP_REDEEMABLE = {
79
80
  read_face_forged: false,
80
81
  write_face_damaged: false,
81
82
  skill_grants_damaged: false,
83
+ skill_scopes_damaged: false,
82
84
  constraint_chain_missing: false,
83
85
  hands_coordinates_damaged: false,
84
86
  installation_record_absent: true,
@@ -111,6 +113,7 @@ const CHECKPOINT_ERROR_DISPOSITION = {
111
113
  "resume.principal_mismatch": "retriable",
112
114
  "resume.placement_mismatch": "retriable",
113
115
  "resume.preflight_rejected": "retriable",
116
+ "resume.holder_superseded": "retriable",
114
117
  "wake.gate_pending": "retriable",
115
118
  "wake.nothing_to_deliver": "retriable",
116
119
  "checkpoint.config_refused": "retriable",
@@ -183,6 +186,26 @@ function decisionRefusalCauseOf(e, gate) {
183
186
  function notResumableAnswer() {
184
187
  return { status: 409, body: refusalBody({ errorCode: "conflict.not_resumable", message: "run is not in a resumable (suspended) state (already resumed, decided, or expired)" }) };
185
188
  }
189
+ function approvalStaleAnswer(facts) {
190
+ return {
191
+ status: 409,
192
+ body: refusalBody({
193
+ errorCode: "approval_stale",
194
+ message: "the approval you are deciding is no longer current (already resolved or superseded since you viewed it)",
195
+ facts: {
196
+ ...(facts.terminal !== undefined ? { terminal: facts.terminal } : {}),
197
+ ...(facts.currentPending !== undefined ? { currentPending: facts.currentPending } : {}),
198
+ },
199
+ }),
200
+ };
201
+ }
202
+ function acceptableFormFacts(detail) {
203
+ const rewritten = detail !== null && typeof detail === "object" ? detail.editRewritten : undefined;
204
+ if (rewritten === null || typeof rewritten !== "object" || !Object.hasOwn(rewritten, "acceptableForm"))
205
+ return {};
206
+ const shown = approverFacingArgs(rewritten.acceptableForm);
207
+ return shown === undefined ? {} : { acceptableForm: shown };
208
+ }
186
209
  export function checkpointRefusalAnswer(e, at) {
187
210
  const refusalCause = e.code === "checkpoint.invalid_outcome" ? decisionRefusalCauseOf(e, at.gate) : undefined;
188
211
  const detail = e.detail;
@@ -198,6 +221,7 @@ export function checkpointRefusalAnswer(e, at) {
198
221
  ? { retryAfterSec: Math.max(1, Math.ceil(detail.retryAfterMs / 1000)) }
199
222
  : {}),
200
223
  ...at.versionKeys,
224
+ ...(e.code === "resume.constraint_rejected" ? acceptableFormFacts(detail) : {}),
201
225
  },
202
226
  }),
203
227
  }, at.retriable);
@@ -209,7 +233,7 @@ export function createResumeLegs(ctx) {
209
233
  async function resumeCheckpoint(sessionId, decision, reason, hostDecision, req, answer, binding, onResumeCommitted, acceptEarly, decider) {
210
234
  const cs = deps.checkpointStore;
211
235
  const token = await currentApprovalGateToken(cs, sessionId);
212
- if (binding?.checkpointToken && binding.checkpointToken !== token) {
236
+ if (binding?.checkpointToken && !sightsCurrentGate({ face: "checkpointToken", value: binding.checkpointToken }, token ? { checkpointToken: token } : null)) {
213
237
  let seenUnreadable = false;
214
238
  const seen = await cs.get(binding.checkpointToken).catch((e) => {
215
239
  seenUnreadable = true;
@@ -245,17 +269,7 @@ export function createResumeLegs(ctx) {
245
269
  };
246
270
  }
247
271
  }
248
- return {
249
- status: 409,
250
- body: refusalBody({
251
- errorCode: "approval_stale",
252
- message: "the approval you are deciding is no longer current (already resolved or superseded since you viewed it)",
253
- facts: {
254
- ...(attributable ? { terminal: "resolved" } : {}),
255
- ...(currentPending ? { currentPending } : {}),
256
- },
257
- }),
258
- };
272
+ return approvalStaleAnswer({ ...(attributable ? { terminal: "resolved" } : {}), ...(currentPending ? { currentPending } : {}) });
259
273
  }
260
274
  if (!token) {
261
275
  deps.logger?.info?.("approval_decide_no_pending", { sessionId });
@@ -330,16 +344,7 @@ export function createResumeLegs(ctx) {
330
344
  ...(cp.pendingAction.boundInputHash ? { boundInputHash: cp.pendingAction.boundInputHash } : {}),
331
345
  };
332
346
  }
333
- return {
334
- status: 409,
335
- body: refusalBody({
336
- errorCode: "approval_stale",
337
- message: "the approval you are deciding is no longer current (already resolved or superseded since you viewed it)",
338
- facts: {
339
- ...(s57CurrentPending ? { currentPending: s57CurrentPending } : {}),
340
- },
341
- }),
342
- };
347
+ return approvalStaleAnswer(s57CurrentPending ? { currentPending: s57CurrentPending } : {});
343
348
  }
344
349
  const paForGrant = cp.pendingAction.kind === "tool_approval" ? cp.pendingAction : undefined;
345
350
  const rememberableTool = paForGrant !== undefined &&
@@ -498,7 +503,7 @@ export function createResumeLegs(ctx) {
498
503
  boundCallId: binding?.boundCallId ?? persistedCallId,
499
504
  boundInputHash: binding?.boundInputHash ?? persistedHash,
500
505
  decision: decision === "approve" ? "allow" : "deny",
501
- ...(decision === "approve" && binding?.updatedInput !== undefined ? { updatedInput: binding.updatedInput } : {}),
506
+ ...(decision === "approve" && !isEngineQuestionRow(pa) && binding?.updatedInput !== undefined ? { updatedInput: binding.updatedInput } : {}),
502
507
  ...(reason ? { reason } : {}),
503
508
  ...(binding?.feedback !== undefined ? { feedback: binding.feedback } : {}),
504
509
  ...(answer !== undefined ? { answer } : {}),
@@ -599,6 +604,7 @@ export function createResumeLegs(ctx) {
599
604
  const freshId = uuidv7();
600
605
  const created = await deps.runStore.createRun(freshId, sessionId, principal ?? null, deps.instanceId ?? "default", {
601
606
  source: args.mintFreshRun.source,
607
+ identity: args.mintFreshRun.identity,
602
608
  objectivePreview: buildRunObjectivePreview(resumeObjective ?? ""),
603
609
  });
604
610
  if (created.existing) {
@@ -725,6 +731,7 @@ export function createResumeLegs(ctx) {
725
731
  let unregisterEngineNotice;
726
732
  try {
727
733
  const instructionLeg = createInstructionSelectionLeg();
734
+ const legWiringSections = () => ({ instructions: instructionLeg.segment(), skills: runSkillsOf(taskConfig) });
728
735
  result = await instructionLeg.run(sessionId, () => withModelReachScope({ sessionId, taskId }, () => withPrincipal(principal, async () => {
729
736
  const resumeNotifiedKeys = new NotifiedKeys();
730
737
  if (deps.runStore && taskId) {
@@ -748,7 +755,7 @@ export function createResumeLegs(ctx) {
748
755
  persistThinking: deps.config.traceThinking,
749
756
  notifiedKeys: resumeNotifiedKeys,
750
757
  ...(taskId ? { onActivity: () => recordTurnActivity(taskId) } : {}),
751
- instructionsSegment: instructionLeg.segment,
758
+ legSections: legWiringSections,
752
759
  });
753
760
  const vAppend = (type, data) => verifySink.append(type, data);
754
761
  cancelLedgerAppend = vAppend;
@@ -820,7 +827,7 @@ export function createResumeLegs(ctx) {
820
827
  if (reSuspended || vrReopened) {
821
828
  await appendModelUsageDelta(ap, deps.modelUsage, taskId);
822
829
  const vrParkCallId = vrNested !== undefined ? await parkToolCallId({ terminal: vrNested }, deps.checkpointStore) : undefined;
823
- await ap(reParkStatus ?? reparkStatus, { gate: vrNested?.gate ?? null, ...(vrParkCallId !== undefined ? { toolCallId: vrParkCallId } : {}), ...(vrReopened ? { reopened: failureCodeOf(vr.terminal) ?? null } : {}) });
830
+ await ap(reParkStatus ?? reparkStatus, parkEventData({ terminal: vrNested, ...(vrParkCallId !== undefined ? { toolCallId: vrParkCallId } : {}) }, vrReopened ? (failureCodeOf(vr.terminal) ?? null) : undefined));
824
831
  }
825
832
  else {
826
833
  safe = await attachModelUsage(safe, { append: ap, getEvents: (id, a) => rs2.getEvents(id, a), modelUsage: deps.modelUsage, taskId });
@@ -1017,7 +1024,7 @@ export function createResumeLegs(ctx) {
1017
1024
  break;
1018
1025
  case "wiring_manifest":
1019
1026
  await flush();
1020
- await append("wiring_manifest", wiringManifestEventData(ev, instructionLeg.segment()));
1027
+ await append("wiring_manifest", wiringManifestEventData(ev, legWiringSections()));
1021
1028
  break;
1022
1029
  case "tool_roster_delta": {
1023
1030
  await flush();
@@ -1074,20 +1081,19 @@ export function createResumeLegs(ctx) {
1074
1081
  const r = causeAtDone !== undefined ? mintCancelledResult({ taskId, sessionId, startedAtMs: resumeLegStartedAt, message: "cancelled by user", engineResult: rEngine, cause: causeAtDone }) : rEngine;
1075
1082
  const rPark = r.terminal.kind === "paused" ? outwardPlane(r.terminal).status : undefined;
1076
1083
  if (rPark === "suspended") {
1077
- const parkCallId = await parkToolCallId(r, deps.checkpointStore);
1078
- await append("suspended", { gate: r.terminal.kind === "paused" ? r.terminal.gate : null, ...(parkCallId !== undefined ? { toolCallId: parkCallId } : {}) });
1084
+ await append("suspended", parkEventData(await graftParkToolCallId(r, deps.checkpointStore)));
1079
1085
  await appendModelUsageDelta(append, deps.modelUsage, taskId);
1080
1086
  return r;
1081
1087
  }
1082
1088
  if (rPark === "needs_review") {
1083
- await append("needs_review", { gate: r.terminal.kind === "paused" ? r.terminal.gate : null });
1089
+ await append("needs_review", parkEventData(r));
1084
1090
  await appendModelUsageDelta(append, deps.modelUsage, taskId);
1085
1091
  return r;
1086
1092
  }
1087
1093
  if (await reopenedAfterLeg(r, causeAtDone !== undefined)) {
1088
1094
  reopenConfirmedPark = true;
1089
1095
  try {
1090
- await append(reparkStatus, { gate: null, reopened: failureCodeOf(r.terminal) ?? null });
1096
+ await append(reparkStatus, parkEventData(r, failureCodeOf(r.terminal) ?? null));
1091
1097
  await appendModelUsageDelta(append, deps.modelUsage, taskId);
1092
1098
  }
1093
1099
  catch {
@@ -1339,6 +1345,7 @@ export function createResumeLegs(ctx) {
1339
1345
  fleetScope: resumeFleetScope(req, auth),
1340
1346
  elicitOwner: principal ?? null,
1341
1347
  source: "workflow_parked_decide",
1348
+ identity: runIdentityOf(req, deps.config, principal ?? null),
1342
1349
  workflowDecision,
1343
1350
  });
1344
1351
  if (out.existing) {
@@ -1424,7 +1431,7 @@ export function createResumeLegs(ctx) {
1424
1431
  outcome: { gate: "wake", ...(message !== undefined ? { message: { text: message, trusted } } : {}) },
1425
1432
  verifyRounds: undefined,
1426
1433
  resumeCtx: { sessionId, ctx },
1427
- mintFreshRun: { source: "wake" },
1434
+ mintFreshRun: { source: "wake", identity: runIdentityOf(req, deps.config, auth.principal ?? null) },
1428
1435
  ...(workflowDecision !== undefined ? { workflowDecision } : {}),
1429
1436
  ...(engineNotices.length > 0 ? { engineNotices } : {}),
1430
1437
  ...(acceptEarly === true ? { acceptEarly: true } : {}),
@@ -1432,7 +1439,7 @@ export function createResumeLegs(ctx) {
1432
1439
  ...(ownerLeaseAdmittedFor !== undefined && auth.principal === ownerLeaseAdmittedFor ? { ownerLeaseAdmitted: true } : {}),
1433
1440
  }));
1434
1441
  }
1435
- async function preparePlanReview(sessionId, log, decider) {
1442
+ async function preparePlanReview(sessionId, log, decider, sighting) {
1436
1443
  const cs = deps.checkpointStore;
1437
1444
  const refuse = (answer) => ({ ok: false, answer });
1438
1445
  const log404 = (r) => deps.logger?.info?.("decide_404", {
@@ -1490,6 +1497,13 @@ export function createResumeLegs(ctx) {
1490
1497
  }),
1491
1498
  });
1492
1499
  }
1500
+ if (sighting !== undefined) {
1501
+ const current = currentGateFacesOf(token, cp);
1502
+ if (!sightsCurrentGate(sighting, current)) {
1503
+ const currentId = decider !== undefined && cp.status === "pending" ? current.checkpointId : undefined;
1504
+ return refuse(approvalStaleAnswer(currentId !== undefined ? { currentPending: { checkpointId: currentId } } : {}));
1505
+ }
1506
+ }
1493
1507
  const ctxRead = await readResumeCtx(cs.getCtx(sessionId), ctxOpening(sessionId));
1494
1508
  if (ctxRead.kind === "corrupt")
1495
1509
  return refuse(resumeCtxCorruptAnswer(ctxRead.where));
@@ -27,8 +27,10 @@ import type { CapabilitiesLocal } from "./routes/capabilities.js";
27
27
  import type { SessionsLocal } from "./routes/sessions.js";
28
28
  import type { SessionSyncLocal } from "./routes/session-sync.js";
29
29
  import type { PlanApprovePermissionModeAfter } from "../task-settings.js";
30
+ import type { GateSighting } from "../approval-gate-currency.js";
30
31
  import type { VerifyRoundsSpec } from "./verify-rounds.js";
31
32
  import type { RunAbort } from "../run-cancel-context.js";
33
+ import type { RunIdentity } from "../identity-source.js";
32
34
  /** 跨域可变状态 = lens1 §C2 实测的那 8 条「运行期登记簿」,一个对象。
33
35
  * [ref]:`cancelledViaVerb`(按 taskId 键控的「这次是取消动词」旁表)删除 —— 中止成因改挂在每条腿自己的
34
36
  * {@link RunAbort} 句柄上(`inflightRuns` 的值),同寿同删,不再有一张与腿不同寿的旁表。 */
@@ -281,11 +283,13 @@ export interface DriveResumeArgs {
281
283
  /** [ref]/[ref]:调用方声明「本腿的 park 是终局后纯 park,session claim 已随终局 finalize 删除」——
282
284
  * getActiveTaskId 缺席时**铸新 canonical taskId + createRun 全生命周期行**(wake 续跑=同 session 新一轮
283
285
  * turn),而不是无行裸跑(不可取消/无计费归因/无 durable append/fleet 不可见,且 core `spec.taskId ??
284
- * sessionId` fallback 会把 lastRunId 铸成 sessionId)。`source` 进 run 行元数据(runMeta 同列)。
286
+ * sessionId` fallback 会把 lastRunId 铸成 sessionId)。`source` 进 run 行元数据(runMeta 同列,只作归因);`identity` = 新行的身份元组(调用方经
287
+ * `runIdentityOf(req, config, 卡属主)` 读凭据门铸下的那一份:按下按钮的就是卡属主才有,否则空)。
285
288
  * decide/answer/plan_review 腿**不设**此位:它们的 claim 在 park 期间特意保留,缺席=行被 reap 的竞态,
286
289
  * 维持既有兜底(checkpoint token 同拍过期 ⇒ resumeStream 拒),不铸新行。 */
287
290
  mintFreshRun?: {
288
291
  source: string;
292
+ identity: RunIdentity | null;
289
293
  };
290
294
  /** `/decide` 第三条车道交给本腿的那一件决定投递(语义属主 = {@link WorkflowDecisionDelivery} 顶注)。缺席 ⇒ 只摊决定行(其余四条 resume 腿恒缺席)。 */
291
295
  workflowDecision?: WorkflowDecisionDelivery;
@@ -457,7 +461,9 @@ export interface RouteLegs {
457
461
  decider?: {
458
462
  principal?: string;
459
463
  explicitOperator: boolean;
460
- }): Promise<{
464
+ },
465
+ /** [ref]:决断方看到的那道门(决断体的 `checkpointId`);不是当前门 ⇒ `409 approval_stale`(`approval_stale` 律,语义属主 = `resume-legs.ts` 的同名形参顶注)。 */
466
+ sighting?: GateSighting): Promise<{
461
467
  readonly ok: false;
462
468
  readonly answer: {
463
469
  status: number;
@@ -102,8 +102,6 @@ export type QuestionAnswerCheck = {
102
102
  * [ref] 起**同时闭键**:词表外的键(拼错 / 未来键 / `__proto__`)拒不丢。
103
103
  */
104
104
  export declare function checkQuestionAnswer(v: unknown): QuestionAnswerCheck;
105
- /** 路由体 = 从 `server.ts` 的 `handle()` 里**整段剪切**过来的原文(唯一改动:统一去缩进两格)。裸 `return;`
106
- * = 「本域已应答」;走到函数尾才是「没匹配上」。跨域的东西一律在首行解构出来。 */
107
105
  export declare function handleApprovalsAssistant(req: IncomingMessage, res: ServerResponse, match: RouteMatch<ApprovalsAssistantRouteId>, ctx: RouteCtx): Promise<void>;
108
106
  /**
109
107
  * 🔴 [ref]-2 / [ref]②:两条 durable 读面(`GET /v1/approvals` + `/v1/approvals/stream`)的**唯一**
@@ -1,6 +1,8 @@
1
1
  import { screenedGate } from "../../trace/project.js";
2
+ import { skillCardFacts } from "../../approval-card.js";
2
3
  import { hostDecisionFacts, readDecidedBy, retiredDecisionKeyFacts, DECIDED_BY_NOT_IN_PROOF, DECIDED_BY_OUTSIDE_CLOSED_SET } from "../../host-decision.js";
3
4
  import { sameExecutionRecord } from "@sema-agent/core";
5
+ import { findParkedAgentForCheckpoint } from "../../parked-decide.js";
4
6
  import { DECIDED_REPLAY_SCAN_CAP, approvalPayloadFingerprint } from "../../plugins/checkpoint-store-sql.js";
5
7
  import { carriesDeploymentListQuestion, standingAnswerDurablyRecordable } from "../../tool-approval.js";
6
8
  import { APPROVAL_GATE_KINDS } from "../../approval-content-kind.js";
@@ -24,10 +26,9 @@ import { callGrantBindingOf, questionGrantBindingOf } from "../../standing-answe
24
26
  import { sendResumeOutcome } from "../route-ctx.js";
25
27
  import { assertNever } from "../route-ctx.js";
26
28
  import * as facts from "../../capability-facts.js";
27
- import { KEEP_SEALED, readResumeCtx } from "../resume-ctx-read.js";
28
- import { StoreCorruptError } from "../../plugins/sql-json-column.js";
29
+ import { storeFaultClassOf } from "../../plugins/store-fault.js";
29
30
  import { recordFailOpen } from "../../observability/fail-open.js";
30
- import { durableRowIdentity } from "../../checkpoint-identity.js";
31
+ import { MAX_CHECKPOINT_ID_CHARS, durableRowIdentity } from "../../checkpoint-identity.js";
31
32
  import { MAX_KEY_ECHO, keyClosureClause, scanKeyClosure } from "../../request-key-closure.js";
32
33
  import { fieldConflictFacts } from "../../field-conflict.js";
33
34
  const DECIDE_BODY_DECLARED_KEYS = [
@@ -36,14 +37,15 @@ const DECIDE_BODY_DECLARED_KEYS = [
36
37
  const DECIDE_PROOF_COVERED_KEYS = ["answer", "boundCallId", "boundInputHash", "decision", "reason"];
37
38
  const DECIDE_NOT_IN_PROOF_KEYS = ["checkpointToken", "decidedBy", "feedback", "remember", "updatedInput"];
38
39
  const PLAN_REVIEW_BODY_DECLARED_KEYS = [
39
- "boundCallId", "decidedBy", "decision", "editedPlan", "permissionModeAfter", "reason",
40
+ "boundCallId", "checkpointId", "decidedBy", "decision", "editedPlan", "permissionModeAfter", "reason",
40
41
  ];
41
- const PLAN_REVIEW_PROOF_COVERED_KEYS = ["boundCallId", "decision", "editedPlan", "permissionModeAfter", "reason"];
42
+ const PLAN_REVIEW_PROOF_COVERED_KEYS = ["boundCallId", "checkpointId", "decision", "editedPlan", "permissionModeAfter", "reason"];
42
43
  const PLAN_REVIEW_NOT_IN_PROOF_KEYS = ["decidedBy"];
43
44
  const PROOF_SLOT_OF = {
44
45
  answer: "answer",
45
46
  boundCallId: "boundCallId",
46
47
  boundInputHash: "boundInputHash",
48
+ checkpointId: "checkpointId",
47
49
  decision: "decision",
48
50
  editedPlan: "boundInputHash",
49
51
  permissionModeAfter: "permissionModeAfter",
@@ -260,6 +262,26 @@ async function decideIdempotentReplay(cs, args) {
260
262
  },
261
263
  };
262
264
  }
265
+ async function gateObjectivePreview(reader, runStore, agentStore, gate) {
266
+ const previewOf = async (taskId) => readOwnedRun(reader.config, runStore, reader.principal, taskId, "owner_or_operator");
267
+ const own = (await runStore.getActiveTaskId(gate.sessionId)) ?? (await runStore.latestRunForSession(gate.sessionId))?.taskId;
268
+ if (own !== undefined)
269
+ return (await previewOf(own))?.objectivePreview ?? null;
270
+ if (agentStore === undefined)
271
+ return null;
272
+ let row = (await findParkedAgentForCheckpoint(agentStore, gate))?.row;
273
+ for (let hop = 0; row !== undefined && hop < MAX_DELEGATION_HOPS; hop++) {
274
+ const spawner = row.parentTaskId;
275
+ if (spawner === undefined)
276
+ return null;
277
+ const run = await previewOf(spawner);
278
+ if (run !== undefined)
279
+ return run.objectivePreview ?? null;
280
+ row = (await agentStore.get(spawner, row.scope)) ?? undefined;
281
+ }
282
+ return null;
283
+ }
284
+ const MAX_DELEGATION_HOPS = 8;
263
285
  export async function handleApprovalsAssistant(req, res, match, ctx) {
264
286
  const { deps } = ctx;
265
287
  const { preemptableRuns } = ctx.registry;
@@ -357,13 +379,18 @@ export async function handleApprovalsAssistant(req, res, match, ctx) {
357
379
  sendApprovalsReadFailure(req, res, deps.logger, err);
358
380
  return;
359
381
  }
382
+ const ledger = facts.durableRuns(deps);
383
+ const agentStore = deps.backgroundAgentStore;
360
384
  const inbox = (await Promise.all(summaries.map(async (s) => {
361
- const ctxRead = await readResumeCtx(cs.getCtx(s.sessionId), KEEP_SEALED);
362
- if (ctxRead.kind === "corrupt")
363
- recordFailOpen("server.assistant-inbox.objective-ctx-corrupt", ctxRead.where);
364
- const storedBody = ctxRead.kind === "ok" ? ctxRead.ctx.body : undefined;
365
- const storedObjective = typeof storedBody === "object" && storedBody !== null ? storedBody.objective : undefined;
366
- const objective = typeof storedObjective === "string" ? storedObjective : null;
385
+ let objective = null;
386
+ if (ledger.available) {
387
+ try {
388
+ objective = await gateObjectivePreview({ config: deps.config, principal }, ledger.runStore, agentStore, s);
389
+ }
390
+ catch (err) {
391
+ recordFailOpen("server.assistant-inbox.objective-preview-unreadable", `run row read for the inbox row of session ${JSON.stringify(s.sessionId)} failed: ${err instanceof Error ? err.message : String(err)}`);
392
+ }
393
+ }
367
394
  const input = s.toolInput != null ? redactedPreview(s.toolInput) : null;
368
395
  return {
369
396
  sessionId: s.sessionId,
@@ -380,9 +407,7 @@ export async function handleApprovalsAssistant(req, res, match, ctx) {
380
407
  ...(s.mandate !== undefined ? { mandate: s.mandate } : {}),
381
408
  ...(s.denialLimitFallback !== undefined ? { denialLimitFallback: s.denialLimitFallback } : {}),
382
409
  ...(s.origin !== undefined ? { origin: s.origin } : {}),
383
- ...(s.boundGrantDigest !== undefined ? { boundGrantDigest: s.boundGrantDigest } : {}),
384
- ...(s.boundGrants !== undefined ? { boundGrants: s.boundGrants } : {}),
385
- ...(s.classifierNoRuling !== undefined ? { classifierNoRuling: s.classifierNoRuling } : {}),
410
+ ...skillCardFacts(s),
386
411
  ...(s.boundGrantAbsence !== undefined ? { boundGrantAbsence: s.boundGrantAbsence } : {}),
387
412
  objective,
388
413
  input,
@@ -601,6 +626,17 @@ export async function handleApprovalsAssistant(req, res, match, ctx) {
601
626
  }
602
627
  }
603
628
  const planNonce = typeof body.boundCallId === "string" ? body.boundCallId : undefined;
629
+ if (body.checkpointId !== undefined) {
630
+ if (typeof body.checkpointId !== "string" || body.checkpointId.length === 0) {
631
+ sendError(res, 400, "request.field_invalid", "checkpointId must be a non-empty string (the identity of the plan gate you decided on: read terminal.checkpointId on the run result, or the checkpointId on its approvals / inbox entry)");
632
+ return;
633
+ }
634
+ if (body.checkpointId.length > MAX_CHECKPOINT_ID_CHARS) {
635
+ sendError(res, 400, "request.field_invalid", `checkpointId too large (max ${MAX_CHECKPOINT_ID_CHARS} chars)`);
636
+ return;
637
+ }
638
+ }
639
+ const sighting = typeof body.checkpointId === "string" ? { face: "checkpointId", value: body.checkpointId } : undefined;
604
640
  const run = await runStore.getRun(taskId);
605
641
  if (!run) {
606
642
  if (deps.config.directDoorActive) {
@@ -623,6 +659,7 @@ export async function handleApprovalsAssistant(req, res, match, ctx) {
623
659
  const hdr = (n) => { const h = req.headers[n]; return Array.isArray(h) ? h[0] : h; };
624
660
  const proof = verifyDirectDoorProof({ jwt: hdr(PRINCIPAL_TOKEN_HEADER), mac: hdr(APPROVAL_MAC_HEADER), kid: hdr(APPROVAL_MAC_KID_HEADER) }, buildApprovalEnvelope(run.sessionId, PLAN_REVIEW_PROOF_COVERED_KEYS, {
625
661
  boundCallId: planNonce ?? null,
662
+ checkpointId: sighting?.value,
626
663
  decision,
627
664
  editedPlan: decision === "edit" ? editedPlanBinding(body.editedPlan) : null,
628
665
  permissionModeAfter: isPlanApprovePermissionModeAfter(body.permissionModeAfter) ? body.permissionModeAfter : undefined,
@@ -641,7 +678,7 @@ export async function handleApprovalsAssistant(req, res, match, ctx) {
641
678
  sendError(res, 404, "not_found.run", "task not found");
642
679
  return;
643
680
  }
644
- const plan = await preparePlanReview(run.sessionId, { taskId, principalPresent: principal !== undefined }, { principal: deciderPrincipal, explicitOperator });
681
+ const plan = await preparePlanReview(run.sessionId, { taskId, principalPresent: principal !== undefined }, { principal: deciderPrincipal, explicitOperator }, sighting);
645
682
  if (!plan.ok) {
646
683
  sendResumeOutcome(res, plan.answer, deps.logger);
647
684
  return;
@@ -871,7 +908,19 @@ export async function handleApprovalsAssistant(req, res, match, ctx) {
871
908
  }
872
909
  const APPROVALS_STREAM_POLL_MS = 3000;
873
910
  function approvalsReadFailureOf(err) {
874
- return err instanceof StoreCorruptError ? "approvals.read_corrupt" : "approvals.read_unavailable";
911
+ const fault = storeFaultClassOf(err);
912
+ switch (fault) {
913
+ case "corrupt":
914
+ return "approvals.read_corrupt";
915
+ case "connection":
916
+ case "deployment":
917
+ case "unclassified":
918
+ return "approvals.read_unavailable";
919
+ default: {
920
+ const unhandled = fault;
921
+ throw new Error(`store fault class without an approvals read answer: ${String(unhandled)}`);
922
+ }
923
+ }
875
924
  }
876
925
  function sendApprovalsReadFailure(req, res, logger, err) {
877
926
  logRequestError(logger, req, err);
@@ -23,6 +23,7 @@ import { projectWriteProtectionCapability } from "../../write-protection.js";
23
23
  import { projectStoreCapability } from "../../store-posture.js";
24
24
  import { projectWebSearchCapability } from "../../plugins/web-search.js";
25
25
  import { projectExecutionLaneCapability } from "../../capabilities/execution-lane.js";
26
+ import { judgeSandboxGitPlane, sandboxGitPlanePremisesOf } from "../../sandbox-git-plane.js";
26
27
  import { MCP_ASSERTION_HEADER_SENT } from "../../mcp-assertion-header.js";
27
28
  import { gateCompositeMembers } from "../../capabilities/composite-members.js";
28
29
  import { projectApprovalsStreamLiveCapability } from "../../capabilities/approvals-push.js";
@@ -88,7 +89,14 @@ export async function handleCapabilities(req, res, match, ctx) {
88
89
  approvalReadRootGrant: facts.readRootGrant(deps).available,
89
90
  sessions: facts.sessionAuditView(deps).available,
90
91
  deviceExecutor: facts.deviceLane(deps).available
91
- ? { enabled: true, protocolVersion: DEVICE_PROTOCOL_VERSION, maxInflightPerDevice: deps.config.remoteExec?.provider === "device" ? (deps.config.remoteExec.maxInflightPerDevice ?? 4) : 4, wsPath: DEVICE_WS_PATH, management: facts.deviceManagement(deps).available }
92
+ ? {
93
+ enabled: true,
94
+ protocolVersion: DEVICE_PROTOCOL_VERSION,
95
+ maxInflightPerDevice: deps.config.remoteExec?.provider === "device" ? (deps.config.remoteExec.maxInflightPerDevice ?? 4) : 4,
96
+ wsPath: DEVICE_WS_PATH,
97
+ management: facts.deviceManagement(deps).available,
98
+ ...(deps.config.remoteExec?.provider === "device" && deps.config.remoteExec.relayUrl !== undefined ? { relayUrl: deps.config.remoteExec.relayUrl } : {}),
99
+ }
92
100
  : false,
93
101
  sessionEvents: facts.sessionEventStream(deps).available,
94
102
  fleet: facts.fleetStream(deps).available
@@ -180,6 +188,7 @@ export async function handleCapabilities(req, res, match, ctx) {
180
188
  approvalFrameFamilies: [...facts.approvalFrameFamilies(streamApprovalFact)],
181
189
  approvalDecisionNote: true,
182
190
  approvalDecidedBy: true,
191
+ planReviewCheckpointId: true,
183
192
  permissionModes: [...PERMISSION_MODES],
184
193
  sessionReadRedaction: true,
185
194
  sideQuery: true,
@@ -224,6 +233,7 @@ export async function handleCapabilities(req, res, match, ctx) {
224
233
  configCatalog: facts.configCatalog(deps).available,
225
234
  sealedKey: projectSealedKeyCapability(deps.sealedKeyPublic),
226
235
  workspaceSync: ((space, cache) => space.available || cache.available ? { ...(space.available ? space.policy : {}), ...(cache.available ? { cache: cache.cache } : {}) } : undefined)(facts.workspaceSpace(deps.config), facts.workspaceCache(deps.config)),
236
+ credentialPlane: { sandboxGit: deps.sandboxGitPlane ?? judgeSandboxGitPlane(sandboxGitPlanePremisesOf(deps.config, {})) },
227
237
  }));
228
238
  return;
229
239
  }
@@ -26,7 +26,7 @@
26
26
  *
27
27
  * ── 下一步动作按 `status`,不解析 `reason`(core [ref] 键表逐字)────────────────────────────────
28
28
  * `status.errorCode` / `status.httpStatus` 是机器面(`http_status` 401 是一扇重新授权的门,`spawn_failed` 是
29
- * 缺二进制);`reason` 是**人读**的一句话,经本仓 `redactSecrets` + 定长截断后投出(它可能嵌远端文本),
29
+ * 缺二进制);`reason` 是**人读**的一句话(它可能嵌远端文本:引擎铸点抹过请求回显与同主体词表,本仓那一遍只读引擎没抹的段、定长截断后投出 —— `http/mcp-remote-text.ts`),
30
30
  * 消费端**不许**解析它。名册**按 `tools` 的在场性**投成 `toolNames`(present-iff),**不按 `outcome`**。
31
31
  *
32
32
  * ── 查找与拒绝形(与 steer / capture-optout **同一只**,不新造)───────────────────────────────────
@@ -1,6 +1,5 @@
1
1
  import { z } from "zod";
2
2
  import { isParkedRunStatus, isTerminalRunStatus, runProvenElsewhere } from "../../plugins/store-contracts.js";
3
- import { redactSecrets } from "../../trace/redact.js";
4
3
  import { DeadlineExceeded, MCP_DIAL_DEADLINE_MS, raceDeadline } from "../mcp-dial-deadline.js";
5
4
  import { sendJson, sendError } from "../send.js";
6
5
  import { gatedPrincipal, explicitOperatorOk, sendPrincipalRefusal } from "../principal-gate.js";
@@ -8,9 +7,8 @@ import {} from "../route-ctx.js";
8
7
  import { assertNever, SESSION_NOT_FOUND_MESSAGE } from "../route-ctx.js";
9
8
  import * as facts from "../../capability-facts.js";
10
9
  import { sessionOwnerCellOf } from "../../security.js";
10
+ import { redactMcpRemoteText } from "../mcp-remote-text.js";
11
11
  const BodySchema = z.object({ server: z.string().min(1).max(190) }).strict();
12
- const NEUTRALIZED_TEXT_MAX = 200;
13
- const humanText = (s) => redactSecrets(s).slice(0, NEUTRALIZED_TEXT_MAX);
14
12
  function outcomeOf(coreOutcome) {
15
13
  switch (coreOutcome) {
16
14
  case "reconnected": return "accepted";
@@ -27,7 +25,7 @@ function projectStatus(s) {
27
25
  ...(s.httpStatus !== undefined ? { httpStatus: s.httpStatus } : {}),
28
26
  ...(s.delivered !== undefined ? { delivered: s.delivered } : {}),
29
27
  ...(s.serverInfo !== undefined ? { serverInfo: s.serverInfo } : {}),
30
- ...(s.error !== undefined ? { error: humanText(String(s.error)) } : {}),
28
+ ...(s.error !== undefined ? { error: redactMcpRemoteText(String(s.error)) } : {}),
31
29
  ...(s.transportClosed !== undefined ? { transportClosed: s.transportClosed } : {}),
32
30
  };
33
31
  }
@@ -36,7 +34,7 @@ function projectListingIncomplete(l) {
36
34
  reason: l.reason,
37
35
  pages: l.pages,
38
36
  ...(l.budgetMs !== undefined ? { budgetMs: l.budgetMs } : {}),
39
- ...(l.error !== undefined ? { error: humanText(l.error) } : {}),
37
+ ...(l.error !== undefined ? { error: redactMcpRemoteText(l.error) } : {}),
40
38
  };
41
39
  }
42
40
  function projectResult(r) {
@@ -48,7 +46,7 @@ function projectResult(r) {
48
46
  added: r.added,
49
47
  removed: r.removed,
50
48
  ...(r.tools !== undefined ? { toolNames: r.tools.map((t) => t.name) } : {}),
51
- ...(r.reason !== undefined ? { reason: humanText(r.reason) } : {}),
49
+ ...(r.reason !== undefined ? { reason: redactMcpRemoteText(r.reason) } : {}),
52
50
  ...(r.status !== undefined ? { status: projectStatus(r.status) } : {}),
53
51
  ...(r.listingIncomplete !== undefined ? { listingIncomplete: projectListingIncomplete(r.listingIncomplete) } : {}),
54
52
  };
@@ -114,7 +112,7 @@ export async function handleMcpReconnect(req, res, match, ctx) {
114
112
  const msg = e instanceof Error ? e.message : String(e);
115
113
  switch (code) {
116
114
  case "mcp.reconnect_unavailable":
117
- sendJson(res, 200, { taskId: activeTaskId, sessionId, server, outcome: "unsupported", reason: humanText(msg) });
115
+ sendJson(res, 200, { taskId: activeTaskId, sessionId, server, outcome: "unsupported", reason: redactMcpRemoteText(msg) });
118
116
  return;
119
117
  case "steering.invalid_content":
120
118
  sendError(res, 422, "steering.invalid_content", msg);