@sema-agent/server 7.141.0 → 7.143.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 (341) hide show
  1. package/MIGRATION.md +657 -0
  2. package/USAGE.md +102 -30
  3. package/dist/adoption/plan.js +22 -0
  4. package/dist/approval-ask-audit-store.d.ts +61 -43
  5. package/dist/approval-ask-audit-store.js +69 -44
  6. package/dist/approval-ask-machine.d.ts +15 -7
  7. package/dist/approval-ask-machine.js +10 -4
  8. package/dist/approval-card.d.ts +126 -27
  9. package/dist/approval-card.js +103 -23
  10. package/dist/approval-crash-convergence.d.ts +17 -7
  11. package/dist/approval-crash-convergence.js +94 -22
  12. package/dist/approval-deny-reasons.d.ts +34 -30
  13. package/dist/approval-deny-reasons.js +21 -6
  14. package/dist/approval-policy-settlement.d.ts +1 -1
  15. package/dist/approval-queue-kind.d.ts +27 -0
  16. package/dist/approval-queue-kind.js +6 -0
  17. package/dist/approval-reconciler.d.ts +41 -9
  18. package/dist/approval-reconciler.js +45 -42
  19. package/dist/audit.js +4 -2
  20. package/dist/auth-bridge.d.ts +9 -6
  21. package/dist/auth-bridge.js +9 -3
  22. package/dist/boot/adoption.js +2 -1
  23. package/dist/boot/budget-tracing.d.ts +17 -1
  24. package/dist/boot/budget-tracing.js +10 -6
  25. package/dist/boot/device-lane.d.ts +1 -1
  26. package/dist/boot/execution-env.d.ts +24 -4
  27. package/dist/boot/execution-env.js +26 -23
  28. package/dist/boot/execution-lane.js +2 -1
  29. package/dist/boot/exit-boundary.js +5 -4
  30. package/dist/boot/governance-seams.d.ts +26 -41
  31. package/dist/boot/governance-seams.js +61 -28
  32. package/dist/boot/late-binds.d.ts +7 -4
  33. package/dist/boot/late-binds.js +3 -1
  34. package/dist/boot/leader.js +2 -4
  35. package/dist/boot/memory-consolidation.d.ts +0 -8
  36. package/dist/boot/memory-consolidation.js +5 -4
  37. package/dist/boot/parked-revive-gate.js +2 -1
  38. package/dist/boot/permission-rules-audit.d.ts +0 -28
  39. package/dist/boot/permission-rules-audit.js +2 -1
  40. package/dist/boot/read-deny-anchor-audit.js +2 -1
  41. package/dist/boot/reapers.js +9 -7
  42. package/dist/boot/resolve-spec.d.ts +1 -1
  43. package/dist/boot/resolve-spec.js +19 -10
  44. package/dist/boot/retention-lane.js +5 -3
  45. package/dist/boot/runner-deps.d.ts +10 -7
  46. package/dist/boot/runner-deps.js +20 -29
  47. package/dist/boot/runtime-caps.js +3 -2
  48. package/dist/boot/session-faces.js +4 -3
  49. package/dist/boot/shutdown.d.ts +9 -0
  50. package/dist/boot/shutdown.js +19 -4
  51. package/dist/boot/stage-02-web-admin-pull.js +3 -0
  52. package/dist/boot/stage-03-store-backend.d.ts +4 -1
  53. package/dist/boot/stage-03-store-backend.js +1 -2
  54. package/dist/boot/stage-04-stores.d.ts +5 -2
  55. package/dist/boot/stage-04-stores.js +6 -7
  56. package/dist/boot/stage-05-execution-env.d.ts +6 -3
  57. package/dist/boot/stage-05-execution-env.js +8 -6
  58. package/dist/boot/stage-06-runners.d.ts +6 -3
  59. package/dist/boot/stage-06-runners.js +3 -4
  60. package/dist/boot/stage-07-capability-layer.d.ts +6 -3
  61. package/dist/boot/stage-07-capability-layer.js +7 -2
  62. package/dist/boot/stage-08-reapers.d.ts +6 -3
  63. package/dist/boot/stage-08-reapers.js +3 -2
  64. package/dist/boot/stage-09-leader.d.ts +6 -3
  65. package/dist/boot/stage-10-http-server.d.ts +6 -3
  66. package/dist/boot/stage-10-http-server.js +14 -5
  67. package/dist/boot/stores.d.ts +5 -2
  68. package/dist/boot/stores.js +10 -9
  69. package/dist/boot/web-admin.d.ts +5 -1
  70. package/dist/boot/web-admin.js +48 -36
  71. package/dist/boot/webfetch-summarize-lane.js +2 -1
  72. package/dist/boot/workflow-orchestration.js +3 -2
  73. package/dist/capabilities/builtin-tools.d.ts +2 -1
  74. package/dist/capabilities/builtin-tools.js +2 -4
  75. package/dist/capabilities/composite-members.d.ts +2 -1
  76. package/dist/capabilities/composite-members.js +2 -1
  77. package/dist/capabilities/hands-lane.d.ts +9 -39
  78. package/dist/capabilities/hands-lane.js +8 -13
  79. package/dist/capabilities/mcp-probe.d.ts +3 -2
  80. package/dist/capabilities/scenarios.d.ts +11 -1
  81. package/dist/capabilities/scenarios.js +1 -0
  82. package/dist/capabilities/skills.d.ts +1 -17
  83. package/dist/capabilities/skills.js +0 -8
  84. package/dist/capability-facts.d.ts +31 -7
  85. package/dist/capability-facts.js +27 -4
  86. package/dist/card-void.d.ts +6 -9
  87. package/dist/config-catalog.js +2 -1
  88. package/dist/config-dir-trust.js +58 -29
  89. package/dist/config-invariants.d.ts +7 -1
  90. package/dist/config-invariants.js +7 -0
  91. package/dist/config-provider.d.ts +10 -0
  92. package/dist/config-provider.js +15 -4
  93. package/dist/config-root-migration.d.ts +13 -0
  94. package/dist/config-root-migration.js +99 -0
  95. package/dist/config-types.d.ts +19 -8
  96. package/dist/config.d.ts +10 -3
  97. package/dist/config.js +18 -4
  98. package/dist/delegated-session-owner.d.ts +5 -3
  99. package/dist/delegated-session-owner.js +1 -1
  100. package/dist/device-ws-protocol.d.ts +1 -0
  101. package/dist/device-ws-protocol.js +2 -1
  102. package/dist/env-model-table-gate.d.ts +53 -0
  103. package/dist/env-model-table-gate.js +53 -0
  104. package/dist/execution-lane-caps.d.ts +2 -1
  105. package/dist/fleet/fleet-bus.d.ts +34 -55
  106. package/dist/fleet/fleet-bus.js +35 -100
  107. package/dist/fleet/fleet-reconciler.d.ts +4 -4
  108. package/dist/fleet/fleet-reconciler.js +1 -1
  109. package/dist/fleet/subagent-tail-bus.d.ts +14 -11
  110. package/dist/fleet/subagent-tail-bus.js +15 -28
  111. package/dist/hooks/hook-runner.d.ts +6 -0
  112. package/dist/hooks/hook-runner.js +58 -8
  113. package/dist/host-decision.d.ts +54 -20
  114. package/dist/host-decision.js +42 -14
  115. package/dist/http/admission.d.ts +1 -1
  116. package/dist/http/admission.js +7 -3
  117. package/dist/http/approval-sweeps.js +11 -8
  118. package/dist/http/caller-trust.d.ts +1 -1
  119. package/dist/http/dispatch.js +4 -2
  120. package/dist/http/durable-run-leg.js +2 -2
  121. package/dist/http/engine-refusal.d.ts +16 -0
  122. package/dist/http/engine-refusal.js +17 -0
  123. package/dist/http/execution-context.d.ts +2 -2
  124. package/dist/http/idempotency.d.ts +1 -1
  125. package/dist/http/memory-compliance-failure-reply.d.ts +1 -3
  126. package/dist/http/memory-compliance-failure-reply.js +4 -6
  127. package/dist/http/owner-access.d.ts +11 -0
  128. package/dist/http/owner-access.js +13 -0
  129. package/dist/http/principal-gate.d.ts +3 -1
  130. package/dist/http/principal-gate.js +17 -8
  131. package/dist/http/resume-legs.d.ts +21 -7
  132. package/dist/http/resume-legs.js +76 -58
  133. package/dist/http/resume-restatement.d.ts +39 -0
  134. package/dist/http/resume-restatement.js +25 -0
  135. package/dist/http/route-ctx.d.ts +35 -10
  136. package/dist/http/route-ctx.js +1 -1
  137. package/dist/http/route-table.js +2 -0
  138. package/dist/http/routes/approvals-assistant.d.ts +9 -1
  139. package/dist/http/routes/approvals-assistant.js +142 -31
  140. package/dist/http/routes/attachments.js +18 -0
  141. package/dist/http/routes/capabilities.js +16 -4
  142. package/dist/http/routes/memory-policy.d.ts +2 -2
  143. package/dist/http/routes/memory-policy.js +27 -7
  144. package/dist/http/routes/notify-wake.js +23 -25
  145. package/dist/http/routes/rules.js +48 -13
  146. package/dist/http/routes/runs.js +12 -11
  147. package/dist/http/routes/session-memory-sync.d.ts +91 -0
  148. package/dist/http/routes/session-memory-sync.js +174 -0
  149. package/dist/http/routes/session-sync.js +13 -20
  150. package/dist/http/routes/sessions-list.d.ts +2 -1
  151. package/dist/http/routes/sessions.d.ts +2 -2
  152. package/dist/http/routes/sessions.js +22 -23
  153. package/dist/http/routes/tasks.js +34 -54
  154. package/dist/http/run-read-face.d.ts +7 -0
  155. package/dist/http/run-read-face.js +9 -3
  156. package/dist/http/send.d.ts +5 -0
  157. package/dist/http/send.js +9 -2
  158. package/dist/http/server.d.ts +27 -15
  159. package/dist/http/server.js +4 -6
  160. package/dist/http/stored-body-replay.js +15 -4
  161. package/dist/http/submit-replay.d.ts +4 -3
  162. package/dist/http/submit-replay.js +2 -2
  163. package/dist/http/sync-submit-body.d.ts +21 -4
  164. package/dist/http/sync-submit-body.js +13 -0
  165. package/dist/http/wire-types.d.ts +17 -7
  166. package/dist/identity-source.d.ts +21 -2
  167. package/dist/identity-source.js +6 -0
  168. package/dist/leader/leader.js +5 -2
  169. package/dist/leader/wire.d.ts +7 -1
  170. package/dist/leader/wire.js +6 -5
  171. package/dist/leg-facts.d.ts +37 -0
  172. package/dist/leg-facts.js +15 -0
  173. package/dist/memory-bundle-engine.d.ts +1 -4
  174. package/dist/memory-bundle-engine.js +2 -2
  175. package/dist/memory-incident-sink.d.ts +19 -6
  176. package/dist/memory-incident-sink.js +8 -4
  177. package/dist/memory-operator-faces.d.ts +30 -66
  178. package/dist/memory-operator-faces.js +17 -88
  179. package/dist/memory-scope-stores.d.ts +38 -40
  180. package/dist/memory-scope-stores.js +51 -36
  181. package/dist/memory-scope.d.ts +0 -9
  182. package/dist/memory-scope.js +0 -9
  183. package/dist/memory-sync-client.d.ts +21 -21
  184. package/dist/memory-sync-client.js +62 -43
  185. package/dist/memory-sync-identity.d.ts +24 -0
  186. package/dist/memory-sync-identity.js +57 -0
  187. package/dist/memory-sync.d.ts +27 -5
  188. package/dist/memory-sync.js +29 -7
  189. package/dist/model-plane-face.d.ts +13 -7
  190. package/dist/model-plane-face.js +2 -1
  191. package/dist/observability/fail-open.d.ts +47 -14
  192. package/dist/observability/fail-open.js +37 -11
  193. package/dist/observability/run-terminal.d.ts +92 -34
  194. package/dist/observability/run-terminal.js +171 -162
  195. package/dist/orchestration/workflow-completion-inbox.d.ts +5 -19
  196. package/dist/orchestration/workflow-completion-inbox.js +0 -12
  197. package/dist/orchestration/workflow-notify-journal.d.ts +3 -2
  198. package/dist/orchestration/workflow-park-decision.d.ts +8 -3
  199. package/dist/orchestration/workflow-park-decision.js +5 -3
  200. package/dist/org-delivery-gate.d.ts +11 -0
  201. package/dist/org-delivery-gate.js +19 -1
  202. package/dist/parked-decide.d.ts +13 -9
  203. package/dist/parked-decide.js +7 -7
  204. package/dist/parked-revive-surface.js +1 -1
  205. package/dist/peer-inbound-face.d.ts +127 -0
  206. package/dist/peer-inbound-face.js +158 -0
  207. package/dist/permission-rule-list.d.ts +13 -9
  208. package/dist/permission-rule-list.js +7 -3
  209. package/dist/plugins/approval-ask-store-file.d.ts +14 -4
  210. package/dist/plugins/approval-ask-store-file.js +64 -11
  211. package/dist/plugins/approval-ask-store-memory.d.ts +2 -1
  212. package/dist/plugins/approval-ask-store-memory.js +17 -14
  213. package/dist/plugins/approval-ask-store-sql.d.ts +70 -23
  214. package/dist/plugins/approval-ask-store-sql.js +79 -30
  215. package/dist/plugins/checkpoint-store-sql.d.ts +4 -4
  216. package/dist/plugins/checkpoint-store-sql.js +9 -7
  217. package/dist/plugins/file-history-store-sql.d.ts +24 -18
  218. package/dist/plugins/file-history-store-sql.js +24 -19
  219. package/dist/plugins/file-run-store.d.ts +35 -1
  220. package/dist/plugins/file-run-store.js +108 -9
  221. package/dist/plugins/imported-layer-root-retirement.d.ts +82 -0
  222. package/dist/plugins/imported-layer-root-retirement.js +96 -0
  223. package/dist/plugins/local-checkpoint-store.d.ts +1 -1
  224. package/dist/plugins/local-checkpoint-store.js +2 -2
  225. package/dist/plugins/local-session-store.d.ts +42 -9
  226. package/dist/plugins/local-session-store.js +83 -33
  227. package/dist/plugins/local-task-attachment-store.d.ts +12 -2
  228. package/dist/plugins/local-task-attachment-store.js +67 -43
  229. package/dist/plugins/mailbox-recipient-lifecycle.d.ts +9 -31
  230. package/dist/plugins/mailbox-recipient-lifecycle.js +4 -0
  231. package/dist/plugins/mailbox-store-sql.d.ts +12 -8
  232. package/dist/plugins/mailbox-store-sql.js +76 -7
  233. package/dist/plugins/memory-key-guards.d.ts +3 -3
  234. package/dist/plugins/memory-run-store.d.ts +6 -1
  235. package/dist/plugins/memory-run-store.js +11 -1
  236. package/dist/plugins/memory-sync-store-pg.d.ts +15 -0
  237. package/dist/plugins/memory-sync-store-pg.js +21 -0
  238. package/dist/plugins/memory-sync-store-tidb.d.ts +2 -0
  239. package/dist/plugins/memory-sync-store-tidb.js +13 -1
  240. package/dist/plugins/one-time-migrations.d.ts +58 -2
  241. package/dist/plugins/one-time-migrations.js +161 -4
  242. package/dist/plugins/org-delivery-quota-store.d.ts +1 -1
  243. package/dist/plugins/org-delivery-quota-store.js +2 -12
  244. package/dist/plugins/park-commit-watch.d.ts +1 -1
  245. package/dist/plugins/permission-rule-store-file.d.ts +10 -1
  246. package/dist/plugins/permission-rule-store-file.js +15 -2
  247. package/dist/plugins/permission-rule-store-sql.d.ts +31 -1
  248. package/dist/plugins/permission-rule-store-sql.js +35 -2
  249. package/dist/plugins/remote-env-device.d.ts +8 -11
  250. package/dist/plugins/remote-env-device.js +6 -3
  251. package/dist/plugins/remote-env-e2b.d.ts +23 -4
  252. package/dist/plugins/remote-env-e2b.js +111 -28
  253. package/dist/plugins/remote-env-k8s.d.ts +61 -22
  254. package/dist/plugins/remote-env-k8s.js +133 -33
  255. package/dist/plugins/remote-env-local-docker.d.ts +15 -3
  256. package/dist/plugins/remote-env-local-docker.js +40 -12
  257. package/dist/plugins/retention-store-sql.d.ts +2 -2
  258. package/dist/plugins/run-store-sql.d.ts +9 -1
  259. package/dist/plugins/run-store-sql.js +9 -1
  260. package/dist/plugins/sandbox-read-floor.d.ts +5 -7
  261. package/dist/plugins/sandbox-read-floor.js +2 -7
  262. package/dist/plugins/sql-errors.d.ts +10 -1
  263. package/dist/plugins/sql-errors.js +13 -0
  264. package/dist/plugins/store-backend.d.ts +33 -35
  265. package/dist/plugins/store-backend.js +10 -1
  266. package/dist/plugins/store-contracts.d.ts +50 -1
  267. package/dist/plugins/store-contracts.js +6 -0
  268. package/dist/plugins/task-attachment-store.d.ts +112 -14
  269. package/dist/plugins/task-attachment-store.js +207 -29
  270. package/dist/plugins/tool-result-store-sql.d.ts +9 -8
  271. package/dist/plugins/tool-result-store-sql.js +23 -2
  272. package/dist/plugins/wiring-section-codec.d.ts +32 -0
  273. package/dist/plugins/wiring-section-codec.js +55 -0
  274. package/dist/plugins/workspace-fill.d.ts +60 -0
  275. package/dist/plugins/workspace-fill.js +106 -0
  276. package/dist/project-bundle.d.ts +38 -0
  277. package/dist/project-bundle.js +609 -0
  278. package/dist/rule-entry-list.d.ts +3 -2
  279. package/dist/rule-entry-list.js +5 -1
  280. package/dist/rules-consent.d.ts +28 -5
  281. package/dist/rules-consent.js +40 -28
  282. package/dist/run-local.js +11 -13
  283. package/dist/runs.d.ts +12 -8
  284. package/dist/runs.js +6 -8
  285. package/dist/runtime-governance.d.ts +1 -1
  286. package/dist/sandbox-egress-policy.d.ts +4 -1
  287. package/dist/sandbox-egress-policy.js +3 -1
  288. package/dist/sandbox-git-plane.d.ts +31 -12
  289. package/dist/sandbox-git-plane.js +24 -4
  290. package/dist/security.d.ts +9 -0
  291. package/dist/security.js +8 -0
  292. package/dist/server-secret-env.d.ts +4 -0
  293. package/dist/session-error-wire.d.ts +36 -0
  294. package/dist/session-error-wire.js +13 -0
  295. package/dist/session-incarnation.d.ts +1 -1
  296. package/dist/session-notify-door.d.ts +106 -0
  297. package/dist/session-notify-door.js +93 -0
  298. package/dist/session-sync.d.ts +9 -14
  299. package/dist/session-sync.js +5 -5
  300. package/dist/session-workspace.d.ts +3 -13
  301. package/dist/session-workspace.js +0 -9
  302. package/dist/standing-answer-grant.d.ts +5 -0
  303. package/dist/standing-answer-grant.js +10 -0
  304. package/dist/stores-sql/session-policy-store-sql.d.ts +4 -2
  305. package/dist/stores-sql/session-policy-store-sql.js +3 -13
  306. package/dist/task-mcp.d.ts +1 -1
  307. package/dist/task-settings.d.ts +21 -10
  308. package/dist/task-settings.js +46 -13
  309. package/dist/terminal.d.ts +39 -19
  310. package/dist/terminal.js +65 -22
  311. package/dist/tool-approval.d.ts +56 -37
  312. package/dist/tool-approval.js +78 -71
  313. package/dist/trace/core-keyset-guard.d.ts +134 -14
  314. package/dist/trace/engine-notice-wire.d.ts +9 -3
  315. package/dist/trace/engine-notice-wire.js +7 -3
  316. package/dist/trace/exit-seat.d.ts +9 -2
  317. package/dist/trace/exit-seat.js +1 -0
  318. package/dist/trace/ledger-sink.d.ts +10 -6
  319. package/dist/trace/ledger-sink.js +2 -2
  320. package/dist/trace/model-segment.d.ts +55 -25
  321. package/dist/trace/model-segment.js +101 -33
  322. package/dist/trace/project.d.ts +25 -5
  323. package/dist/trace/project.js +22 -0
  324. package/dist/trace/redact.d.ts +61 -9
  325. package/dist/trace/redact.js +1490 -468
  326. package/dist/trace/self-minted-wire.d.ts +745 -24
  327. package/dist/trace/self-minted-wire.js +239 -9
  328. package/dist/trace/wire-projection-faces.d.ts +2 -2
  329. package/dist/trace/wire-projection-faces.js +2 -2
  330. package/dist/verifier-pause-card.d.ts +33 -5
  331. package/dist/verifier-pause-card.js +15 -10
  332. package/dist/web-admin-client/apply-effective.d.ts +12 -16
  333. package/dist/web-admin-client/apply-effective.js +109 -9
  334. package/dist/web-admin-client/effective-keys.js +2 -0
  335. package/dist/web-admin-client/types.d.ts +4 -0
  336. package/dist/workspace-space.d.ts +19 -3
  337. package/dist/workspace-space.js +8 -4
  338. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +3 -1
  339. package/package.json +3 -2
  340. package/wire-changes/server@7.142.0.json +639 -0
  341. package/wire-changes/server@7.143.0.json +1167 -0
@@ -0,0 +1,174 @@
1
+ import { memorySyncReportOf } from "@sema-agent/core";
2
+ import { sendJson, sendError } from "../send.js";
3
+ import { gatedPrincipal, sendPrincipalRefusal } from "../principal-gate.js";
4
+ import {} from "../route-ctx.js";
5
+ import { assertNever, SESSION_NOT_FOUND_MESSAGE } from "../route-ctx.js";
6
+ import * as facts from "../../capability-facts.js";
7
+ import { sessionOwnerCellOf } from "../../security.js";
8
+ import { leafOfAdmittedHead } from "../../session-incarnation.js";
9
+ import { declaredMemorySyncIdentityFlaw, memorySyncIdentityRule } from "../../memory-sync-identity.js";
10
+ import { MEMORY_OBSERVATION_ABSENT_REASONS } from "../../plugins/store-contracts.js";
11
+ export const SESSION_ACTIVE_SYNC_MESSAGE = "a run in this session is in flight, or the session moved on after the memory it reads was observed (a run was admitted or its transcript advanced) — wait for it to finish on its own, then sync again (do not cancel it to sync sooner)";
12
+ export const SESSION_MEMORY_UNOBSERVED_REASONS = ["no_settled_run", ...MEMORY_OBSERVATION_ABSENT_REASONS, "memory_not_mounted", "no_leaf"];
13
+ const LATEST_RUN_UNOBSERVED_MESSAGE = "the latest settled run in this session has no memory observation on record, and an earlier run is never read in its place — run another task in this session (if that run was refused, fix what refused it first), then sync";
14
+ export const SESSION_MEMORY_UNOBSERVED_MESSAGES = {
15
+ no_settled_run: "this session has no settled run with a memory observation yet, so there is nothing to say about which memory it reads — run a task in it (or let the run in flight finish), then sync",
16
+ run_result_missing: LATEST_RUN_UNOBSERVED_MESSAGE,
17
+ run_result_unobserved: LATEST_RUN_UNOBSERVED_MESSAGE,
18
+ memory_not_mounted: "memory was configured for the latest settled run in this session but did not mount, so which memory this session reads is unknown, and an earlier run is never read in its place — run another task in this session (if its memory fails to mount again, fix that first), then sync",
19
+ no_leaf: "this session has no transcript yet, so there is no revision to sync from — run a task in it (or let the run in flight finish), then sync",
20
+ };
21
+ export function sessionReadSetOf(observed) {
22
+ if (observed === undefined)
23
+ return { kind: "unobserved", reason: "no_settled_run" };
24
+ const { taskId, observation } = observed;
25
+ switch (observation.state) {
26
+ case "mounted":
27
+ return { kind: "read", taskId, scopes: observation.scopes.map((s) => s.scope) };
28
+ case "none":
29
+ return { kind: "read", taskId, scopes: [] };
30
+ case "memoryless":
31
+ return { kind: "unobserved", reason: "memory_not_mounted" };
32
+ case "absent":
33
+ return { kind: "unobserved", reason: observation.reason };
34
+ default:
35
+ return assertNever(observation);
36
+ }
37
+ }
38
+ class ReportMintRefused extends Error {
39
+ }
40
+ function reportOf(round) {
41
+ try {
42
+ return memorySyncReportOf(round);
43
+ }
44
+ catch (err) {
45
+ if (err instanceof TypeError)
46
+ throw new ReportMintRefused(err.message);
47
+ throw err;
48
+ }
49
+ }
50
+ const inflightByLeg = new WeakMap();
51
+ function mergedRound(leg, key, run) {
52
+ let table = inflightByLeg.get(leg);
53
+ if (table === undefined) {
54
+ table = new Map();
55
+ inflightByLeg.set(leg, table);
56
+ }
57
+ const inflight = table.get(key);
58
+ if (inflight !== undefined)
59
+ return inflight;
60
+ const owner = table;
61
+ const round = run().finally(() => {
62
+ if (owner.get(key) === round)
63
+ owner.delete(key);
64
+ });
65
+ table.set(key, round);
66
+ return round;
67
+ }
68
+ export async function handleSessionMemorySync(req, res, match, ctx) {
69
+ const { deps } = ctx;
70
+ switch (match.decl.id) {
71
+ case "session-memory-sync": {
72
+ const fact = facts.sessionMemorySync(deps);
73
+ if (!fact.available) {
74
+ switch (fact.why) {
75
+ case "memory_sync_leg_absent":
76
+ sendError(res, 501, "feature.memory_sync_disabled", "session memory sync is not available on this deployment — it runs only where a file memory engine syncs to a center (MEMORY_SYNC_URL)");
77
+ return;
78
+ case "run_store_absent":
79
+ sendError(res, 501, "capability.run_store_required", "session memory sync requires a durable run store (DB_BACKEND=mysql|pg|local) — the session's read set is the memory its latest settled run observed");
80
+ return;
81
+ case "session_head_face_absent":
82
+ sendError(res, 501, "capability.session_ownership_required", "session memory sync requires the session-ownership face (sessionStorage.headOf)");
83
+ return;
84
+ default:
85
+ return assertNever(fact.why);
86
+ }
87
+ }
88
+ const { leg, runStore, sessionStorage } = fact;
89
+ if (deps.config.requirePrincipal && gatedPrincipal(req, deps.config) === undefined) {
90
+ sendPrincipalRefusal(res, req, deps.config);
91
+ return;
92
+ }
93
+ if (ctx.helpers.rateLimited(req, res))
94
+ return;
95
+ const sessionId = ctx.helpers.safeDecode(match.params.sessionId);
96
+ if (sessionId === null) {
97
+ sendError(res, 400, "request.path_malformed", "session id segment is not valid percent-encoding");
98
+ return;
99
+ }
100
+ const readHead = () => sessionStorage.headOf(sessionId);
101
+ const head = await readHead();
102
+ if (head === undefined) {
103
+ sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
104
+ return;
105
+ }
106
+ if (!ctx.helpers.sessionOwnerOk(req, res, await sessionOwnerCellOf(sessionStorage, sessionId, head.owner)))
107
+ return;
108
+ const sourceRevision = leafOfAdmittedHead(head);
109
+ const body = await ctx.helpers.readJson(req);
110
+ if (body === null || typeof body !== "object" || Array.isArray(body) || Object.keys(body).some((k) => k !== "target") || typeof body.target !== "string") {
111
+ sendError(res, 400, "request.body_shape", "body must be { target: string } — the sync identity the target engine publishes (GET /v1/capabilities → memorySyncServe.identity), verbatim; no other key");
112
+ return;
113
+ }
114
+ const target = body.target;
115
+ const flaw = declaredMemorySyncIdentityFlaw(target);
116
+ if (flaw !== undefined) {
117
+ sendError(res, 400, "request.field_invalid", `target ${memorySyncIdentityRule(flaw)} — send the target engine's memorySyncServe.identity exactly as GET /v1/capabilities publishes it`, { field: "target", reason: flaw });
118
+ return;
119
+ }
120
+ const readSet = sessionReadSetOf(await runStore.latestMemoryObservationForSession(sessionId));
121
+ if (readSet.kind === "unobserved" || sourceRevision === null) {
122
+ const reason = readSet.kind === "unobserved" ? readSet.reason : "no_leaf";
123
+ const activeTaskId = await runStore.getActiveTaskId(sessionId);
124
+ sendError(res, 409, "conflict.session_memory_unobserved", SESSION_MEMORY_UNOBSERVED_MESSAGES[reason], { reason, ...(activeTaskId !== undefined ? { activeTaskId } : {}) });
125
+ return;
126
+ }
127
+ const { taskId: observedTaskId, scopes } = readSet;
128
+ const quiet = async () => {
129
+ if ((await runStore.getActiveTaskId(sessionId)) !== undefined)
130
+ return false;
131
+ if ((await runStore.latestRunForSession(sessionId))?.taskId !== observedTaskId)
132
+ return false;
133
+ const now = await readHead();
134
+ return now !== undefined && leafOfAdmittedHead(now) === sourceRevision;
135
+ };
136
+ const fenced = () => reportOf({ target, sourceRevision, scopes, refused: { code: "session_active", message: SESSION_ACTIVE_SYNC_MESSAGE } });
137
+ let report;
138
+ try {
139
+ if (target !== leg.peer) {
140
+ report = reportOf({ target, sourceRevision, scopes, refused: { code: "config.memory_sync_target_mismatch", reaches: leg.peer } });
141
+ }
142
+ else if (!(await quiet())) {
143
+ report = fenced();
144
+ }
145
+ else if (scopes.length === 0) {
146
+ report = reportOf({ target, sourceRevision, scopes, layers: [] });
147
+ }
148
+ else {
149
+ report = await mergedRound(leg, JSON.stringify([sessionId, sourceRevision, observedTaskId, target]), async () => {
150
+ const round = await leg.syncOnce("session-trigger", { reading: scopes });
151
+ if (!(await quiet()))
152
+ return fenced();
153
+ return reportOf({ target, sourceRevision, scopes, layers: round.calls.filter((c) => scopes.includes(c.scope)) });
154
+ });
155
+ }
156
+ }
157
+ catch (err) {
158
+ if (!(err instanceof ReportMintRefused))
159
+ throw err;
160
+ deps.logger?.warn?.("session_memory_sync_report_refused", { sessionId, err: err.message });
161
+ sendError(res, 500, "internal.error", "memory sync failed");
162
+ return;
163
+ }
164
+ sendJson(res, 200, report);
165
+ return;
166
+ }
167
+ default:
168
+ return assertNever(match.decl.id);
169
+ }
170
+ }
171
+ export const SESSION_MEMORY_SYNC_ROUTES = [
172
+ { id: "session-memory-sync", pattern: /^\/v1\/sessions\/(?<sessionId>[^/]+)\/memory\/sync$/, label: "/v1/sessions/:id/memory/sync", methods: ["POST"] },
173
+ ];
174
+ //# sourceMappingURL=session-memory-sync.js.map
@@ -1,5 +1,5 @@
1
1
  import { once } from "node:events";
2
- import { randomBytes, createHash } from "node:crypto";
2
+ import { randomBytes } from "node:crypto";
3
3
  import { sessionLogDigest, sessionLogDigestsComparable, StreamingImportValidator, validateFileHistoryExport } from "@sema-agent/core";
4
4
  import { StringDecoder } from "node:string_decoder";
5
5
  import { FileHistoryUnreadableError, blobGetterOf, exportSessionManifest, exportedFileHistory, fileHistoryImportFace, replayFileHistory, replayPolicyRecords, wipeSessionAttendants } from "../../session-sync.js";
@@ -222,6 +222,7 @@ export async function handleSessionSync(req, res, match, ctx) {
222
222
  return;
223
223
  }
224
224
  const fs = blobs.historyStore;
225
+ const exchange = blobs.blobExchange;
225
226
  let envelope;
226
227
  try {
227
228
  envelope = await exportedFileHistory(fs, sessionId);
@@ -238,7 +239,7 @@ export async function handleSessionSync(req, res, match, ctx) {
238
239
  }
239
240
  let bytes;
240
241
  try {
241
- bytes = await fs.getBlob(hash);
242
+ bytes = await exchange.get(hash);
242
243
  }
243
244
  catch (e) {
244
245
  if (sendPullUnreadable(req, res, deps.logger, e))
@@ -268,16 +269,15 @@ export async function handleSessionSync(req, res, match, ctx) {
268
269
  sendError(res, 400, "request.id_invalid", "blob hash must be 64 lowercase-hex chars");
269
270
  return;
270
271
  }
271
- const fs = blobs.historyStore;
272
272
  const body = await readRawBody(req, SYNC_BLOB_MAX_BYTES);
273
- if (createHash("sha256").update(body).digest("hex") !== hash) {
274
- sendError(res, 400, "request.hash_mismatch", "blob hash mismatch");
275
- return;
276
- }
277
- const put = await fs.putBlob(hash, new Uint8Array(body));
273
+ const put = await blobs.blobExchange.put(hash, new Uint8Array(body));
278
274
  if (!put.ok) {
279
- if (put.error.code === "blob_too_large_for_sql") {
280
- sendError(res, 413, "blob_too_large_for_sql", put.error.message, { wallSource: put.error.wallSource });
275
+ if (put.error.blobCap !== undefined) {
276
+ sendError(res, 413, "blob_too_large_for_sql", put.error.message, { wallSource: put.error.blobCap.wallSource });
277
+ return;
278
+ }
279
+ if (put.error.code === "adopt_failed") {
280
+ sendError(res, 400, "request.hash_mismatch", "blob hash mismatch");
281
281
  return;
282
282
  }
283
283
  sendError(res, 502, put.error.code, "blob store write failed");
@@ -751,15 +751,8 @@ async function firstMissingBlob(blobFaces, referenced) {
751
751
  return undefined;
752
752
  if (!blobFaces.available)
753
753
  return referenced[0];
754
- const store = blobFaces.historyStore;
755
- if (typeof store.hasBlobs === "function") {
756
- const present = await store.hasBlobs([...referenced]);
757
- return referenced.find((h) => !present.has(h));
758
- }
759
- for (const hash of referenced)
760
- if (!(await store.getBlob(hash)))
761
- return hash;
762
- return undefined;
754
+ const present = await blobFaces.blobExchange.has(referenced);
755
+ return referenced.find((h) => !present.has(h));
763
756
  }
764
757
  function sendSessionSyncUnavailable(res, why) {
765
758
  switch (why) {
@@ -779,7 +772,7 @@ function sendSessionSyncUnavailable(res, why) {
779
772
  case "policy_purge_absent":
780
773
  sendError(res, 501, "capability.policy_purge_required", "session sync is not available on this deployment: the session-policy store has no deleteBySession purge verb, so an overwrite-dst import could not wipe the destination's stale policy rows. Every in-tree policy store carries it since core 1.423 — upgrade the policy store");
781
774
  return;
782
- case "sync_blob_faces_absent":
775
+ case "blob_exchange_absent":
783
776
  sendError(res, 501, "capability.snapshot_store_required", "file-history blob transfer is not available on this deployment: its file-history store has no blob faces (SQL backends only; capabilities.sessionSyncFileHistory is false here). Sync the conversation without fileHistory");
784
777
  return;
785
778
  default:
@@ -16,7 +16,8 @@ export declare function handleSessionsList(req: IncomingMessage, res: ServerResp
16
16
  * 会话列表行的 **wire 投影** —— `GET /v1/sessions` 的每一行与 `PATCH /v1/sessions/:id` 的 200 回执是**同一只**(店行 → wire 行只有这一处)。
17
17
  * · 预览:店行读的是 run 行预览列的事实位(`""` = 有记录、剥完没有用户正文;NULL = 没记录);本面只说「有没有可显示的预览」⇒ 两者都是 `null`。
18
18
  * · `tempo`:见上方 [ref] 注(由 `lastStatus` 经唯一一张表派生;omit-when-absent)。
19
- * · 其余键(含 `title` / `pinned`)零投影透传 —— 店行是唯一铸点。
19
+ * · 其余键(含 `title` / `pinned` / `rowTag` / `unreadable`)零投影透传 —— 店行是唯一铸点(`unreadable` 的码出自 `session-error-wire.ts` 的那一张表,
20
+ * 与全局兜底对同一只会话答的码同读;SQL 孪生的叶子是行上一列 ⇒ 恒缺席)。
20
21
  */
21
22
  export declare function projectSessionListRow(r: SessionListItem): SessionListItem & {
22
23
  tempo?: ReturnType<typeof sessionTempoOfRunStatus>;
@@ -12,7 +12,7 @@
12
12
  * `sse.sseConnections` / `sse.sseOwnerLookups`——`let` 无法跨模块共享自增。判据/时机/配对释放逐字未动。
13
13
  */
14
14
  import type { IncomingMessage, ServerResponse } from "node:http";
15
- import { type SessionMemoryStatus } from "@sema-agent/core";
15
+ import type { SessionMemoryView } from "../../memory-operator-faces.js";
16
16
  import type { RouteCtx, RouteMatch, RouteIdsOf } from "../route-ctx.js";
17
17
  /** 单域独占状态。每个 server 实例一份——模块级会把连接坑/缓存跨实例串味。 */
18
18
  export interface SessionsLocal {
@@ -42,7 +42,7 @@ export interface SessionsLocal {
42
42
  * mcp/a2a 按 (scenario, principal) 分键是因为它们的面按人裁,这张面不按人裁,分键只会白扩表)。 */
43
43
  memoryStatusCache: Map<string, {
44
44
  at: number;
45
- status: Promise<SessionMemoryStatus>;
45
+ status: Promise<SessionMemoryView>;
46
46
  }>;
47
47
  }
48
48
  export declare function createSessionsLocal(): SessionsLocal;
@@ -8,13 +8,12 @@ import { recordFailOpen } from "../../observability/fail-open.js";
8
8
  import { MCP_DIAL_DEADLINE_MS, raceDeadline } from "../mcp-dial-deadline.js";
9
9
  import { sentMemoryComplianceFailure } from "../memory-compliance-failure-reply.js";
10
10
  import { readLayerSeat, sentMemoryLayerRefusal } from "../memory-layer-reply.js";
11
- import { foldPlaneStatuses } from "../../memory-operator-faces.js";
12
11
  import { MAX_SETTINGS_OUTPUT_STYLE_CHARS, MAX_SETTINGS_ENV_VARS, MAX_SETTINGS_ENV_KEY_CHARS, MAX_SETTINGS_ENV_VALUE_CHARS } from "../../task-settings.js";
13
12
  import { readWritableSessionPermissionRules } from "../../session-policy-wire.js";
14
13
  import { sendJson, sendError, sendSessionError, sendSessionPolicyError, sseHeaders, httpErrorBody } from "../send.js";
15
14
  import { fleetViewOf, gatedPrincipal, explicitOperatorOk, sessionCallerOf, sessionOwnerReadable, sendPrincipalRefusal, sendUnauthorized } from "../principal-gate.js";
16
15
  import { headReadOf, leafOfAdmittedHead, sessionFaceOfHead } from "../../session-incarnation.js";
17
- import { runRowReadableOnSessionFace } from "../owner-access.js";
16
+ import { foldOwnerUnreadable, runRowReadableOnSessionFace } from "../owner-access.js";
18
17
  import { assertNever, SESSION_NOT_FOUND_MESSAGE } from "../route-ctx.js";
19
18
  import * as facts from "../../capability-facts.js";
20
19
  import { rosterAgentId } from "./agents-roster.js";
@@ -84,7 +83,7 @@ export async function handleSessions(req, res, match, ctx) {
84
83
  const readOwnedSessionView = async (sessionAudit, sessionId, readOpts) => {
85
84
  const headOf = headReadOf(deps.sessionStorage);
86
85
  if (headOf !== undefined) {
87
- const owner = (await headOf(sessionId))?.owner;
86
+ const owner = (await foldOwnerUnreadable(deps.logger, match.decl.id, sessionId, () => headOf(sessionId)))?.owner;
88
87
  if (owner === undefined) {
89
88
  sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
90
89
  return undefined;
@@ -274,7 +273,7 @@ export async function handleSessions(req, res, match, ctx) {
274
273
  sendError(res, 400, "request.path_malformed", "session id segment is not valid percent-encoding");
275
274
  return;
276
275
  }
277
- const head = await headOf(headSession);
276
+ const head = await foldOwnerUnreadable(deps.logger, match.decl.id, headSession, () => headOf(headSession));
278
277
  if (head === undefined) {
279
278
  sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
280
279
  return;
@@ -304,7 +303,7 @@ export async function handleSessions(req, res, match, ctx) {
304
303
  sendError(res, 400, "request.path_malformed", "session id segment is not valid percent-encoding");
305
304
  return;
306
305
  }
307
- const reading = await placementFact.seat.read(placementSession);
306
+ const reading = await foldOwnerUnreadable(deps.logger, match.decl.id, placementSession, () => placementFact.seat.read(placementSession));
308
307
  if (reading === undefined) {
309
308
  sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
310
309
  return;
@@ -322,7 +321,7 @@ export async function handleSessions(req, res, match, ctx) {
322
321
  sendError(res, 501, "capability.session_store_required", "session events require a durable session store (DB_BACKEND=mysql|pg|local)");
323
322
  return;
324
323
  }
325
- const readHead = (id) => evFact.sessionStorage.headOf(id);
324
+ const readHead = (id) => foldOwnerUnreadable(deps.logger, match.decl.id, id, () => evFact.sessionStorage.headOf(id));
326
325
  const readOwner = async (id) => (await readHead(id))?.owner;
327
326
  const watch = evFact.watch;
328
327
  const evSession = safeDecode(match.params.sessionId);
@@ -541,6 +540,10 @@ export async function handleSessions(req, res, match, ctx) {
541
540
  sendError(res, 400, "request.body_shape", "invalid session memory erase body — expected { requestId: string, allowUnevidenced?: boolean } (no `select`: this face erases exactly this session's own contributions)");
542
541
  return;
543
542
  }
543
+ if ("scope" in raw) {
544
+ sendError(res, 400, "request.body_shape", "invalid session memory erase body — `scope` is not accepted: this face erases this session's own contributions in every memory store of the deployment, so there is no layer to name; resend without `scope`");
545
+ return;
546
+ }
544
547
  if ("select" in raw) {
545
548
  sendError(res, 400, "request.body_shape", "invalid session memory erase body — `select` is not accepted on the self-service face: the selector is fixed to { sessionId: <this session> } by the path; the operator face POST /v1/memory/erase takes explicit selectors");
546
549
  return;
@@ -553,17 +556,12 @@ export async function handleSessions(req, res, match, ctx) {
553
556
  sendError(res, 400, "request.field_invalid", "allowUnevidenced must be a boolean when present");
554
557
  return;
555
558
  }
556
- const at = readLayerSeat(res, Object.prototype.hasOwnProperty.call(raw, "scope"), raw.scope);
557
- if (at === null)
558
- return;
559
559
  const withheld = (coreMessage) => deps.logger?.warn?.("session_memory_erase_refused", { sessionId, err: coreMessage });
560
560
  try {
561
561
  const input = { requestId: raw.requestId, select: { sessionId }, ...(raw.allowUnevidenced !== undefined ? { allowUnevidenced: raw.allowUnevidenced } : {}) };
562
- sendJson(res, 200, await (at !== undefined ? faces.erase(input, at) : faces.erase(input)));
562
+ sendJson(res, 200, await faces.erase(input));
563
563
  }
564
564
  catch (err) {
565
- if (sentMemoryLayerRefusal(res, err, { kind: "session_owner", withheld }))
566
- return;
567
565
  if (sentMemoryComplianceFailure(res, err, { kind: "session_owner", withheld }, deps.logger))
568
566
  return;
569
567
  deps.logger?.warn?.("session_memory_erase_failed", { sessionId, err: String(err) });
@@ -601,21 +599,22 @@ export async function handleSessions(req, res, match, ctx) {
601
599
  return;
602
600
  try {
603
601
  const { memoryStatusCache } = ctx.local.sessions;
604
- const statusOf = (store) => {
605
- const cacheKey = JSON.stringify([sessionId, store.controlPlane]);
606
- const cached = memoryStatusCache.get(cacheKey);
607
- if (cached && Date.now() - cached.at <= MEMORY_STATUS_TTL_MS)
608
- return cached.status;
602
+ const reading = face.readingAt(at);
603
+ const cacheKey = JSON.stringify([sessionId, reading.key]);
604
+ const cached = memoryStatusCache.get(cacheKey);
605
+ let pending;
606
+ if (cached && Date.now() - cached.at <= MEMORY_STATUS_TTL_MS)
607
+ pending = cached.status;
608
+ else {
609
609
  if (memoryStatusCache.size > 1024)
610
610
  memoryStatusCache.clear();
611
- const status = store.status(sessionId);
612
- const fresh = { at: Date.now(), status };
611
+ pending = reading.status(sessionId);
612
+ const fresh = { at: Date.now(), status: pending };
613
613
  memoryStatusCache.set(cacheKey, fresh);
614
- status.catch(() => { if (memoryStatusCache.get(cacheKey) === fresh)
614
+ pending.catch(() => { if (memoryStatusCache.get(cacheKey) === fresh)
615
615
  memoryStatusCache.delete(cacheKey); });
616
- return status;
617
- };
618
- const s = foldPlaneStatuses(await Promise.all(face.storesAt(at).map(statusOf)));
616
+ }
617
+ const s = await pending;
619
618
  sendJson(res, 200, {
620
619
  sessionId,
621
620
  autoConsolidationArmed: deps.memoryAutoConsolidationArmed,
@@ -5,18 +5,18 @@ import { graftParkToolCallId, markChildrenStoppedByUserOnAbort, syncRefusalHttpS
5
5
  import { atHostBoundary, sealResultForExit, outwardPlane } from "../../terminal.js";
6
6
  import { voidVerifierPauseCard } from "../../verifier-pause-card.js";
7
7
  import { withPrincipal } from "../../observability/principal-context.js";
8
- import { fleetRunPublisher, fleetRunLabels, fleetRunResiduals, isFleetAgentTerminalNotification } from "../../fleet/fleet-bus.js";
8
+ import { fleetRunPublisher, fleetRunLabels, fleetRunResiduals } from "../../fleet/fleet-bus.js";
9
9
  import { defaultSubagentTailBus } from "../../fleet/subagent-tail-bus.js";
10
- import { createReasoningSegments, reasoningStreamOfTask } from "../../trace/model-segment.js";
10
+ import { createLiveModelText, modelStreamOfTask } from "../../trace/model-segment.js";
11
11
  import { emitPendingWorkflowCompletions, taskNotificationInboxEntry, taskNotificationStreamKeyOf, NotifiedKeys } from "../../orchestration/workflow-completion-inbox.js";
12
- import { createLedgerSink, createLedgerTwinEmit } from "../../trace/ledger-sink.js";
12
+ import { createForwardAppendFailedNote, createLedgerSink, createLedgerTwinEmit } from "../../trace/ledger-sink.js";
13
13
  import { registerEngineNoticeLeg } from "../../trace/engine-notice-wire.js";
14
14
  import { clearTurnActivity, readTurnActivityMs, recordTurnActivity } from "../../turn-activity.js";
15
15
  import { buildCancelledLedgerRow, createRunAbort, mintCancelledResult } from "../../run-cancel-context.js";
16
16
  import { recordResourceWindow } from "../../resource-window.js";
17
17
  import { redactSecrets } from "../../trace/redact.js";
18
18
  import { writeLiveFrame, writeLiveHeartbeat } from "../live-frame.js";
19
- import { contextUsageEventData, turnEndEventData, toolStartEventData, toolEndEventData, taskProgressEventData, taskStartedEventData, taskNotificationEventData, compactedEventData, diagnosticsEventData, brainStatusEventData, steeringInjectedEventData, compactionOutcomeEventData, compactionStartedEventData, workspaceChangedEventData, wiringManifestFaceEventData, toolRosterDeltaEventData, toolProgressEventData, toolDisclosureEventData, humanInputEventData, appendModelUsageDelta, attachModelUsage, textEndEventData, liveIdentityFields } from "../../trace/project.js";
19
+ import { contextUsageEventData, turnEndEventData, toolStartEventData, toolEndEventData, taskProgressEventData, taskStartedEventData, taskNotificationEventData, compactedEventData, diagnosticsEventData, brainStatusEventData, steeringInjectedEventData, compactionOutcomeEventData, compactionStartedEventData, workspaceChangedEventData, wiringManifestFaceEventData, toolRosterDeltaEventData, toolProgressEventData, toolDisclosureEventData, humanInputEventData, appendModelUsageDelta, attachModelUsage, liveIdentityFields } from "../../trace/project.js";
20
20
  import { cascadeConfig, runMeta } from "../run-meta.js";
21
21
  import { idemCallerOf, inflightKeyOf, submissionIdemOf } from "../idempotency.js";
22
22
  import { replayOfExistingRun } from "../submit-replay.js";
@@ -36,12 +36,11 @@ import * as facts from "../../capability-facts.js";
36
36
  import { persistSubmitCtx } from "../submit-ctx.js";
37
37
  import { serviceVersion } from "../../service-version.js";
38
38
  import { withModelReachScope } from "../../model-reach-scope.js";
39
- import { runSkillsOf } from "../../capabilities/skills.js";
40
39
  import { createInstructionSelectionLeg } from "../../instruction-selection.js";
41
40
  import { fieldConflictFacts } from "../../field-conflict.js";
42
41
  import { refusalBody } from "../refusal-body.js";
43
42
  import { heldWorkflowParkDecisions } from "../../parked-decide.js";
44
- import { createSessionWorkspaceRecorder, legSessionWitnessOf, sessionWorkspaceStoreOf } from "../../session-workspace.js";
43
+ import { createSessionWorkspaceRecorder, sessionWorkspaceStoreOf } from "../../session-workspace.js";
45
44
  const DISCONNECT_PARK_GRACE_MS = 10_000;
46
45
  async function mintSubmitExitResult(result, checkpoints) {
47
46
  return redactTerminalResult(sealResultForExit(await graftParkToolCallId(result, checkpoints)));
@@ -166,7 +165,7 @@ export async function handleTasks(req, res, match, ctx) {
166
165
  const wiringOperatorFace = explicitOperatorOk(gatedPrincipal(req, deps.config), deps.config);
167
166
  let wiringReceipt;
168
167
  const instructionLeg = createInstructionSelectionLeg();
169
- const legWiringSections = () => ({ instructions: instructionLeg.segment(), skills: runSkillsOf(prepared.spec) });
168
+ const legWiringSections = () => ({ instructions: instructionLeg.segment(), skills: prepared.legFacts?.skills });
170
169
  const withRunReceipt = (out) => wiringReceipt !== undefined ? { ...out, wiringManifest: wiringReceipt } : out;
171
170
  if (liveStream) {
172
171
  if (prepared.verify || prepared.cascade) {
@@ -342,7 +341,7 @@ export async function handleTasks(req, res, match, ctx) {
342
341
  if (durableTaskId && deps.runStore) {
343
342
  const rs = deps.runStore;
344
343
  const tid = durableTaskId;
345
- ledgerSink = createLedgerSink({ appendEvent: (seq, type, data) => rs.appendEvent(tid, seq, type, data), persistThinking: deps.config.traceThinking, onActivity: (kind, statusData) => recordTurnActivity(tid, kind, statusData), legSections: legWiringSections });
344
+ ledgerSink = createLedgerSink({ appendEvent: (seq, type, data) => rs.appendEvent(tid, seq, type, data), persistThinking: deps.config.traceThinking, onActivity: (kind, statusData) => recordTurnActivity(tid, kind, statusData), legSections: legWiringSections, usage: { modelUsage: deps.modelUsage, taskId: tid } });
346
345
  usageKey = tid;
347
346
  deps.modelUsage?.register(tid);
348
347
  const owner = principal ?? null;
@@ -400,7 +399,8 @@ export async function handleTasks(req, res, match, ctx) {
400
399
  ...(prepared.engineNotices !== undefined ? { pending: prepared.engineNotices } : {}),
401
400
  live: (row) => {
402
401
  if (res.writableEnded || res.destroyed) {
403
- recordFailOpen(failOpenTagForDroppedFrame("engine_notice"), "frame=engine_notice");
402
+ if (noticeLedger === undefined)
403
+ recordFailOpen(failOpenTagForDroppedFrame("engine_notice"), "frame=engine_notice");
404
404
  return;
405
405
  }
406
406
  writeLiveFrame(res, { type: "engine_notice", ...row });
@@ -417,8 +417,8 @@ export async function handleTasks(req, res, match, ctx) {
417
417
  const streamBody = async () => {
418
418
  await withPrincipal(principal, async () => {
419
419
  const tailContentMode = prepared.spec.forwardSubagentEvents === true ? "on" : "progress_only";
420
- const childReasoning = createReasoningSegments();
421
- const topReasoning = createReasoningSegments();
420
+ const childText = createLiveModelText();
421
+ const topText = createLiveModelText();
422
422
  const heldDecisions = await heldWorkflowParkDecisions(deps, prepared.spec.sessionId);
423
423
  const fwdInternals = {
424
424
  ...heldDecisions,
@@ -432,8 +432,8 @@ export async function handleTasks(req, res, match, ctx) {
432
432
  }
433
433
  if (res.writableEnded)
434
434
  return;
435
- for (const end of childReasoning.onEvent(e))
436
- writeLiveFrame(res, end);
435
+ for (const f of childText.onEvent(e))
436
+ writeLiveFrame(res, f);
437
437
  const t = e.type;
438
438
  if (t === "task_progress") {
439
439
  writeLiveFrame(res, { type: "task_progress", ...taskProgressEventData(e) });
@@ -449,34 +449,20 @@ export async function handleTasks(req, res, match, ctx) {
449
449
  const te = e;
450
450
  writeLiveFrame(res, { type: "tool_end", ...toolEndEventData(te) });
451
451
  }
452
- else if (t === "text_delta") {
453
- const td = e;
454
- writeLiveFrame(res, { type: "text_delta", delta: td.delta, ...(td.eventId ? { eventId: td.eventId } : {}), ...(td.parentToolCallId ? { parentToolCallId: td.parentToolCallId } : {}) });
455
- }
456
- else if (t === "text_end") {
457
- const tend = textEndEventData(e);
458
- if (tend !== null)
459
- writeLiveFrame(res, { type: "text_end", ...tend });
460
- }
461
- else if (t === "reasoning_delta") {
462
- const rd = e;
463
- writeLiveFrame(res, { type: "reasoning_delta", delta: redactSecrets(rd.delta), ...(rd.eventId ? { eventId: rd.eventId } : {}), ...(rd.parentToolCallId ? { parentToolCallId: rd.parentToolCallId } : {}) });
464
- }
465
452
  },
466
453
  onTaskNotification: (n, opts) => {
467
454
  if (n.task_type === "workflow")
468
455
  return;
469
456
  if (!res.writableEnded)
470
- for (const end of childReasoning.cut(reasoningStreamOfTask(n.task_id, n.sessionId)))
471
- writeLiveFrame(res, end);
457
+ for (const f of childText.cut(modelStreamOfTask(n.task_id)))
458
+ writeLiveFrame(res, f);
472
459
  defaultSubagentTailBus.forgetHandle(n.task_id);
473
460
  if (defaultSubagentTailBus.hasSubscribers(n.task_id)) {
474
461
  defaultSubagentTailBus.publish(n.task_id, { type: "task_settled", taskId: n.task_id, status: n.status, ...(typeof n.seq === "number" ? { seq: n.seq } : {}), ...(n.summary ? { summary: redactSecrets(n.summary) } : {}) });
475
462
  }
476
- const hadRow = isFleetAgentTerminalNotification(n) ? (fleetPub?.onChildTerminal(n.sessionId ?? n.task_id, n.status, n.task_id, n.toolUseId) ?? false) : false;
477
463
  const deliverable = syncLegLive && !res.writableEnded && !res.destroyed;
478
464
  const parked = !deliverable && deps.workflowCompletionInbox !== undefined;
479
- deps.logger?.info?.("task_notification_observed", { route: "sync-stream", taskId: n.task_id, taskType: n.task_type, status: n.status, hadFleetRow: hadRow, legLive: syncLegLive, parkedDurable: parked });
465
+ deps.logger?.info?.("task_notification_observed", { route: "sync-stream", taskId: n.task_id, taskType: n.task_type, status: n.status, legLive: syncLegLive, parkedDurable: parked });
480
466
  if (parked) {
481
467
  void deps.workflowCompletionInbox.enqueue(taskNotificationInboxEntry(prepared.spec.sessionId, askOwner, n, Date.now(), durableTaskId, opts?.priority)).catch((err) => deps.logger?.warn?.("park_enqueue_failed", { route: "sync-stream", taskId: n.task_id, err: err instanceof Error ? err.message : String(err) }));
482
468
  }
@@ -502,12 +488,12 @@ export async function handleTasks(req, res, match, ctx) {
502
488
  if (durableTaskId)
503
489
  steerableRuns.set(durableTaskId, liveStream);
504
490
  prepared.acceptance.attachStream({ reachable: () => !closed && !res.writableEnded && !res.destroyed, write: (frame) => writeLiveFrame(res, { ...frame }) });
505
- const liveWorkspace = createSessionWorkspaceRecorder(sessionWorkspaceStoreOf(deps.sessionStorage), legSessionWitnessOf(prepared.spec));
491
+ const liveWorkspace = createSessionWorkspaceRecorder(sessionWorkspaceStoreOf(deps.sessionStorage), prepared.legFacts?.sessionWitness);
506
492
  for await (const ev of liveStream) {
507
493
  if (closed)
508
494
  break;
509
- for (const end of ev.type === "done" ? [...childReasoning.cut(), ...topReasoning.cut()] : topReasoning.onEvent(ev))
510
- writeLiveFrame(res, end);
495
+ for (const f of ev.type === "done" ? [...childText.cut(), ...topText.cut()] : topText.onEvent(ev))
496
+ writeLiveFrame(res, f);
511
497
  const promptEntryId = promptCommit(ev);
512
498
  if (promptEntryId !== undefined)
513
499
  userMsgEntryId = promptEntryId;
@@ -568,8 +554,7 @@ export async function handleTasks(req, res, match, ctx) {
568
554
  writeLiveFrame(res, { type: "task_notification", ...taskNotificationEventData(ev) });
569
555
  }
570
556
  }
571
- else if (ev.type === "reasoning_delta" && typeof ev.delta === "string") {
572
- writeLiveFrame(res, { type: "reasoning_delta", delta: redactSecrets(ev.delta), ...(ev.eventId ? { eventId: ev.eventId } : {}), ...(ev.parentToolCallId ? { parentToolCallId: ev.parentToolCallId } : {}) });
557
+ else if (ev.type === "text_delta" || ev.type === "text_end" || ev.type === "reasoning_delta") {
573
558
  }
574
559
  else if (ev.type === "task_progress") {
575
560
  writeLiveFrame(res, { type: "task_progress", ...taskProgressEventData(ev) });
@@ -618,25 +603,19 @@ export async function handleTasks(req, res, match, ctx) {
618
603
  else if (ev.type === "human_input") {
619
604
  writeLiveFrame(res, { type: "human_input", ...humanInputEventData(ev) });
620
605
  }
621
- else if (ev.type === "text_delta" || ev.type === "text_end" || ev.type === "turn_end" || ev.type === "message_committed" || ev.type === "context_usage") {
606
+ else if (ev.type === "turn_end" || ev.type === "message_committed" || ev.type === "context_usage") {
622
607
  const e = ev;
623
608
  const ident = liveIdentityFields(e);
624
- const textEnd = ev.type === "text_end" ? textEndEventData(e) : undefined;
625
- const arm = ev.type === "text_delta"
626
- ? { type: "text_delta", delta: e.delta, ...ident }
627
- : ev.type === "text_end"
628
- ?
629
- textEnd === null || textEnd === undefined ? null : { type: "text_end", ...textEnd }
630
- : ev.type === "turn_end"
631
- ? { type: "turn_end", ...turnEndEventData(ev), ...ident }
632
- : ev.type === "message_committed"
633
- ? { type: "message_committed", entryId: e.entryId, role: e.role, ...(e.toolCallId !== undefined ? { toolCallId: e.toolCallId } : {}), ...ident }
634
- : { type: "context_usage", ...contextUsageEventData(ev), ...ident };
635
- if (arm !== null)
636
- writeLiveFrame(res, arm);
609
+ const arm = ev.type === "turn_end"
610
+ ? { type: "turn_end", ...turnEndEventData(ev), ...ident }
611
+ : ev.type === "message_committed"
612
+ ? { type: "message_committed", entryId: e.entryId, role: e.role, ...(e.toolCallId !== undefined ? { toolCallId: e.toolCallId } : {}), ...ident }
613
+ : { type: "context_usage", ...contextUsageEventData(ev), ...ident };
614
+ writeLiveFrame(res, arm);
637
615
  }
638
616
  else {
639
- writeLiveFrame(res, { ...ev });
617
+ const unknownArm = ev;
618
+ writeLiveFrame(res, { ...unknownArm });
640
619
  }
641
620
  }
642
621
  });
@@ -742,10 +721,11 @@ export async function handleTasks(req, res, match, ctx) {
742
721
  ? deps.sendUserFile.runWithContext({
743
722
  taskId: askTaskId,
744
723
  emit: async (f) => {
745
- void (await emitAsk(f));
724
+ if (ledgerSink === undefined || faceAlive())
725
+ void (await emitAsk(f));
746
726
  if (ledgerSink) {
747
727
  const { type, ...rest } = f;
748
- await ledgerSink.append(type, rest).catch(() => undefined);
728
+ await ledgerSink.append(type, rest).catch(createForwardAppendFailedNote(type));
749
729
  }
750
730
  },
751
731
  }, withApproval)
@@ -957,7 +937,7 @@ export async function handleTasks(req, res, match, ctx) {
957
937
  const cascadeLeg = () => runCascade(prepared.runner, specWithSignal, cascadeConfig(deps.config.cascadeLadder, prepared.spec.limits?.maxCostUsd), heldDecisions);
958
938
  const plainLeg = async () => {
959
939
  const stream = prepared.runner.runTaskStream(specWithSignal, undefined, heldDecisions);
960
- const syncWorkspace = createSessionWorkspaceRecorder(sessionWorkspaceStoreOf(deps.sessionStorage), legSessionWitnessOf(prepared.spec));
940
+ const syncWorkspace = createSessionWorkspaceRecorder(sessionWorkspaceStoreOf(deps.sessionStorage), prepared.legFacts?.sessionWitness);
961
941
  for await (const ev of stream) {
962
942
  observeSyncLegEvent(ev);
963
943
  prepared.acceptance.observe(ev);
@@ -975,7 +955,7 @@ export async function handleTasks(req, res, match, ctx) {
975
955
  inflightRuns.delete(durableTaskId);
976
956
  }
977
957
  const syncAbortCause = syncCancelCtrl.cause();
978
- await voidVerifierPauseCard(result, deps.checkpointStore);
958
+ await voidVerifierPauseCard(result, prepared.runner);
979
959
  if (syncAbortCause !== undefined) {
980
960
  result = mintCancelledResult({
981
961
  taskId: durableTaskId ?? "",
@@ -5,6 +5,13 @@ import type { ServiceConfig } from "../config-types.js";
5
5
  export interface RunFaceDeps {
6
6
  config: Pick<ServiceConfig, "runStaleSec" | "infraCostRates">;
7
7
  }
8
+ /** 一条行是不是「看起来失联」:盘上 `running`、而最后一次心跳早于 `runStaleSec` —— 读面把它折成 `failed`(`run.stalled`)。读它的人只此一只判据(读面与同步重放回执同读)。 */
9
+ export declare function runRowStale(deps: RunFaceDeps, run: RunRecord): boolean;
10
+ /**
11
+ * 一条行上的结果**当作出口结果读**(封口 + 脱敏的读入口)—— 读面的 `result` 与同步提交的重放体读的是同一只:
12
+ * 终局行按 taskId+updatedAt 记忆的读入口,非终局行(停驻 / 在跑)走店入口那一套封口的读入口;行上没有结果 ⇒ `undefined`。
13
+ */
14
+ export declare function runRowExitResult(run: RunRecord): import("../plugins/store-contracts.js").PersistedTaskResult | undefined;
8
15
  export declare function readRunFace(deps: RunFaceDeps, runStore: RunStore, run: RunRecord): Promise<{
9
16
  result: import("../plugins/store-contracts.js").PersistedTaskResult | undefined;
10
17
  supervisorCost: import("../observability/cost-taxonomy.js").SupervisorCostBreakdown | undefined;