@sema-agent/server 7.139.0 → 7.141.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 (328) hide show
  1. package/MIGRATION.md +471 -0
  2. package/README.md +1 -1
  3. package/README.zh-CN.md +1 -1
  4. package/USAGE.md +94 -26
  5. package/dist/adoption/runner.js +1 -1
  6. package/dist/adoption/sql.js +1 -1
  7. package/dist/approval-batch-census.d.ts +27 -0
  8. package/dist/approval-batch-census.js +27 -0
  9. package/dist/approval-card.d.ts +37 -22
  10. package/dist/approval-card.js +6 -3
  11. package/dist/approval.d.ts +15 -0
  12. package/dist/approval.js +10 -0
  13. package/dist/audit.d.ts +4 -2
  14. package/dist/audit.js +2 -2
  15. package/dist/bench/s1/live-deps.js +3 -2
  16. package/dist/bench/s1/row.d.ts +5 -2
  17. package/dist/bench/s1/row.js +2 -22
  18. package/dist/boot/adoption.js +1 -1
  19. package/dist/boot/device-lane.d.ts +11 -0
  20. package/dist/boot/device-lane.js +16 -0
  21. package/dist/boot/engine-lease.d.ts +24 -3
  22. package/dist/boot/engine-lease.js +9 -2
  23. package/dist/boot/execution-env.js +18 -12
  24. package/dist/boot/execution-lane.d.ts +8 -0
  25. package/dist/boot/execution-lane.js +6 -1
  26. package/dist/boot/memory-boundary.d.ts +16 -53
  27. package/dist/boot/memory-boundary.js +10 -20
  28. package/dist/boot/memory-consolidation.d.ts +16 -12
  29. package/dist/boot/memory-consolidation.js +24 -13
  30. package/dist/boot/reapers.d.ts +2 -2
  31. package/dist/boot/resolve-spec.d.ts +3 -5
  32. package/dist/boot/resolve-spec.js +9 -8
  33. package/dist/boot/runner-deps.d.ts +3 -3
  34. package/dist/boot/runner-deps.js +1 -0
  35. package/dist/boot/session-faces.d.ts +1 -1
  36. package/dist/boot/session-faces.js +4 -3
  37. package/dist/boot/shutdown.js +5 -0
  38. package/dist/boot/stage-03-store-backend.d.ts +2 -6
  39. package/dist/boot/stage-03-store-backend.js +2 -3
  40. package/dist/boot/stage-04-stores.d.ts +2 -6
  41. package/dist/boot/stage-04-stores.js +5 -5
  42. package/dist/boot/stage-05-execution-env.d.ts +2 -6
  43. package/dist/boot/stage-05-execution-env.js +3 -1
  44. package/dist/boot/stage-06-runners.d.ts +2 -8
  45. package/dist/boot/stage-06-runners.js +3 -3
  46. package/dist/boot/stage-07-capability-layer.d.ts +2 -7
  47. package/dist/boot/stage-07-capability-layer.js +8 -10
  48. package/dist/boot/stage-08-reapers.d.ts +2 -7
  49. package/dist/boot/stage-09-leader.d.ts +2 -7
  50. package/dist/boot/stage-10-http-server.d.ts +2 -7
  51. package/dist/boot/stage-10-http-server.js +6 -4
  52. package/dist/boot/stores.d.ts +3 -6
  53. package/dist/boot/stores.js +37 -44
  54. package/dist/boot/task-list-lane.d.ts +1 -1
  55. package/dist/boot/task-list-lane.js +1 -1
  56. package/dist/boot/web-admin.js +36 -9
  57. package/dist/capabilities/execution-lane.d.ts +2 -2
  58. package/dist/capabilities/scenarios.d.ts +1 -1
  59. package/dist/capability-facts.d.ts +9 -0
  60. package/dist/capability-facts.js +9 -0
  61. package/dist/card-void.d.ts +38 -0
  62. package/dist/card-void.js +23 -0
  63. package/dist/config-catalog.js +12 -8
  64. package/dist/config-provider.d.ts +18 -17
  65. package/dist/config-provider.js +28 -36
  66. package/dist/config-types.d.ts +36 -9
  67. package/dist/config.js +56 -46
  68. package/dist/delegated-session-owner.d.ts +23 -20
  69. package/dist/delegated-session-owner.js +2 -2
  70. package/dist/deployment-shape.d.ts +9 -8
  71. package/dist/deployment-shape.js +4 -4
  72. package/dist/device-store.d.ts +7 -0
  73. package/dist/device-store.js +3 -0
  74. package/dist/execution-config-root.d.ts +22 -0
  75. package/dist/execution-config-root.js +35 -0
  76. package/dist/execution-lane-caps.d.ts +145 -107
  77. package/dist/execution-lane-caps.js +144 -83
  78. package/dist/execution-placement.d.ts +3 -0
  79. package/dist/execution-placement.js +7 -0
  80. package/dist/fleet/drain-entry.d.ts +3 -0
  81. package/dist/hosted-posture.d.ts +8 -0
  82. package/dist/hosted-posture.js +4 -0
  83. package/dist/http/admission.js +3 -3
  84. package/dist/http/caller-trust.d.ts +3 -2
  85. package/dist/http/caller-trust.js +2 -1
  86. package/dist/http/dispatch.js +4 -0
  87. package/dist/http/live-frame.d.ts +24 -0
  88. package/dist/http/live-frame.js +16 -0
  89. package/dist/http/memory-compliance-failure-reply.d.ts +3 -1
  90. package/dist/http/memory-compliance-failure-reply.js +5 -0
  91. package/dist/http/memory-layer-reply.d.ts +30 -0
  92. package/dist/http/memory-layer-reply.js +24 -0
  93. package/dist/http/resume-legs.js +49 -35
  94. package/dist/http/retired-service-deps.d.ts +1 -1
  95. package/dist/http/retired-service-deps.js +1 -1
  96. package/dist/http/route-table.js +2 -0
  97. package/dist/http/routes/adoption.js +1 -1
  98. package/dist/http/routes/approvals-assistant.js +1 -0
  99. package/dist/http/routes/capabilities.js +5 -4
  100. package/dist/http/routes/diagnostics.d.ts +0 -19
  101. package/dist/http/routes/diagnostics.js +16 -2
  102. package/dist/http/routes/memory-bundle.d.ts +2 -2
  103. package/dist/http/routes/memory-bundle.js +4 -1
  104. package/dist/http/routes/memory-compliance.js +9 -2
  105. package/dist/http/routes/memory-consolidation.js +12 -1
  106. package/dist/http/routes/memory-origin.js +14 -2
  107. package/dist/http/routes/memory-policy.js +7 -2
  108. package/dist/http/routes/run-permission-mode.d.ts +75 -0
  109. package/dist/http/routes/run-permission-mode.js +170 -0
  110. package/dist/http/routes/session-sync.d.ts +3 -6
  111. package/dist/http/routes/session-sync.js +7 -2
  112. package/dist/http/routes/sessions.js +33 -15
  113. package/dist/http/routes/tasks.js +67 -68
  114. package/dist/http/run-read-face.js +3 -3
  115. package/dist/http/send.d.ts +1 -1
  116. package/dist/http/send.js +2 -2
  117. package/dist/http/server.d.ts +14 -27
  118. package/dist/http/server.js +2 -2
  119. package/dist/http/sse-log.d.ts +3 -8
  120. package/dist/http/stored-body-replay.d.ts +3 -3
  121. package/dist/http/stored-body-replay.js +3 -1
  122. package/dist/identity-source.d.ts +1 -1
  123. package/dist/identity-source.js +1 -1
  124. package/dist/instruction-source-notice.d.ts +7 -0
  125. package/dist/instruction-source-notice.js +6 -6
  126. package/dist/lane-home-probe.d.ts +68 -42
  127. package/dist/lane-home-probe.js +53 -30
  128. package/dist/leader/wire.js +5 -4
  129. package/dist/mcp-assertion-header.d.ts +4 -0
  130. package/dist/mcp-assertion-header.js +5 -0
  131. package/dist/memory-bundle-engine.d.ts +16 -9
  132. package/dist/memory-bundle-engine.js +29 -6
  133. package/dist/memory-operator-faces.d.ts +85 -30
  134. package/dist/memory-operator-faces.js +136 -30
  135. package/dist/memory-posture.d.ts +1 -1
  136. package/dist/memory-project-layer.d.ts +3 -21
  137. package/dist/memory-project-layer.js +11 -8
  138. package/dist/memory-scope-stores.d.ts +73 -0
  139. package/dist/memory-scope-stores.js +86 -0
  140. package/dist/memory-scope.d.ts +33 -9
  141. package/dist/memory-scope.js +17 -5
  142. package/dist/memory-sync-client.d.ts +34 -9
  143. package/dist/memory-sync-client.js +108 -28
  144. package/dist/observability/fail-open.d.ts +30 -2
  145. package/dist/observability/fail-open.js +30 -2
  146. package/dist/observability/metrics.js +0 -1
  147. package/dist/observability/run-terminal.d.ts +6 -2
  148. package/dist/observability/run-terminal.js +25 -26
  149. package/dist/observability/secret-env-scrub.d.ts +20 -1
  150. package/dist/observability/secret-env-scrub.js +14 -0
  151. package/dist/orchestration/workflow-completion-inbox.d.ts +5 -2
  152. package/dist/orchestration/workflow-completion-inbox.js +13 -5
  153. package/dist/orchestration/workflow-notify-journal.d.ts +5 -3
  154. package/dist/orchestration/workflow-notify-journal.js +2 -1
  155. package/dist/permission-mode-admission.d.ts +68 -0
  156. package/dist/permission-mode-admission.js +23 -0
  157. package/dist/permission-rule-list.d.ts +55 -6
  158. package/dist/permission-rule-list.js +52 -5
  159. package/dist/plugins/adoption-log-sql.js +4 -4
  160. package/dist/plugins/approval-ask-store-sql.js +14 -14
  161. package/dist/plugins/approval-nonce-store.js +4 -4
  162. package/dist/plugins/blob-backend.d.ts +4 -4
  163. package/dist/plugins/blob-backend.js +7 -7
  164. package/dist/plugins/breaker-state-sql.js +2 -2
  165. package/dist/plugins/caching-session-store.d.ts +6 -0
  166. package/dist/plugins/caching-session-store.js +3 -0
  167. package/dist/plugins/checkpoint-index-columns.js +2 -2
  168. package/dist/plugins/checkpoint-store-sql.d.ts +65 -17
  169. package/dist/plugins/checkpoint-store-sql.js +90 -19
  170. package/dist/plugins/device-store-sql.d.ts +5 -2
  171. package/dist/plugins/device-store-sql.js +23 -20
  172. package/dist/plugins/ensure-index.d.ts +1 -2
  173. package/dist/plugins/execution-placement-sql.d.ts +19 -9
  174. package/dist/plugins/execution-placement-sql.js +20 -9
  175. package/dist/plugins/file-history-store-sql.d.ts +2 -2
  176. package/dist/plugins/file-history-store-sql.js +4 -4
  177. package/dist/plugins/file-run-store.d.ts +2 -2
  178. package/dist/plugins/file-run-store.js +1 -1
  179. package/dist/plugins/fork-routing-session-store.d.ts +10 -32
  180. package/dist/plugins/fork-routing-session-store.js +2 -61
  181. package/dist/plugins/image-bake-store-sql.js +9 -9
  182. package/dist/plugins/image-index-sql.js +11 -11
  183. package/dist/plugins/k8s-exec-protocol.d.ts +4 -1
  184. package/dist/plugins/k8s-exec-protocol.js +6 -1
  185. package/dist/plugins/leader-run-store-sql.js +4 -4
  186. package/dist/plugins/local-checkpoint-store.d.ts +10 -1
  187. package/dist/plugins/local-checkpoint-store.js +22 -0
  188. package/dist/plugins/local-session-store.d.ts +227 -35
  189. package/dist/plugins/local-session-store.js +518 -37
  190. package/dist/plugins/local-session-window.d.ts +17 -3
  191. package/dist/plugins/local-session-window.js +7 -13
  192. package/dist/plugins/mailbox-store-sql.d.ts +2 -1
  193. package/dist/plugins/mailbox-store-sql.js +4 -4
  194. package/dist/plugins/memory-engine-tidb.js +5 -5
  195. package/dist/plugins/memory-optout-grant-store-sql.js +1 -1
  196. package/dist/plugins/memory-run-store.d.ts +2 -2
  197. package/dist/plugins/memory-run-store.js +1 -1
  198. package/dist/plugins/memory-sync-store-tidb.js +3 -3
  199. package/dist/plugins/one-time-migrations.d.ts +52 -11
  200. package/dist/plugins/one-time-migrations.js +101 -39
  201. package/dist/plugins/org-delivery-quota-store.js +2 -2
  202. package/dist/plugins/outcome-ledger-sql.js +2 -2
  203. package/dist/plugins/permission-rule-store-sql.d.ts +4 -4
  204. package/dist/plugins/permission-rule-store-sql.js +16 -16
  205. package/dist/plugins/pg-pool.js +3 -2
  206. package/dist/plugins/pg-session-storage.d.ts +2 -0
  207. package/dist/plugins/pg-session-storage.js +3 -2
  208. package/dist/plugins/remote-env-adb.d.ts +7 -24
  209. package/dist/plugins/remote-env-adb.js +22 -5
  210. package/dist/plugins/remote-env-device.d.ts +2 -28
  211. package/dist/plugins/remote-env-device.js +2 -1
  212. package/dist/plugins/remote-env-e2b.d.ts +40 -27
  213. package/dist/plugins/remote-env-e2b.js +180 -20
  214. package/dist/plugins/remote-env-file-error.d.ts +1 -1
  215. package/dist/plugins/remote-env-file-error.js +8 -2
  216. package/dist/plugins/remote-env-host.d.ts +19 -35
  217. package/dist/plugins/remote-env-host.js +26 -3
  218. package/dist/plugins/remote-env-k8s.d.ts +22 -14
  219. package/dist/plugins/remote-env-k8s.js +150 -26
  220. package/dist/plugins/remote-env-local-docker.d.ts +27 -24
  221. package/dist/plugins/remote-env-local-docker.js +91 -24
  222. package/dist/plugins/remote-env-ssh.d.ts +4 -24
  223. package/dist/plugins/remote-env-ssh.js +4 -3
  224. package/dist/plugins/resume-anchor-store-sql.js +2 -2
  225. package/dist/plugins/retention-lane-store-sql.d.ts +1 -1
  226. package/dist/plugins/retention-lane-store-sql.js +2 -2
  227. package/dist/plugins/retention-store-sql.d.ts +1 -1
  228. package/dist/plugins/retention-store-sql.js +6 -6
  229. package/dist/plugins/run-store-sql.d.ts +3 -3
  230. package/dist/plugins/run-store-sql.js +19 -18
  231. package/dist/plugins/sandbox-read-floor.d.ts +273 -0
  232. package/dist/plugins/sandbox-read-floor.js +608 -0
  233. package/dist/plugins/schema-consistency.d.ts +6 -6
  234. package/dist/plugins/schema-consistency.js +1 -1
  235. package/dist/plugins/schema-ensure-legs.d.ts +2 -3
  236. package/dist/plugins/schema-ensure-legs.js +2 -2
  237. package/dist/plugins/schema-live-columns.d.ts +1 -1
  238. package/dist/plugins/session-capture-record-store-sql.js +1 -1
  239. package/dist/plugins/session-ownership.d.ts +2 -1
  240. package/dist/plugins/session-placement.d.ts +17 -4
  241. package/dist/plugins/session-placement.js +2 -1
  242. package/dist/plugins/session-rules-column.d.ts +1 -1
  243. package/dist/plugins/session-rules-column.js +1 -1
  244. package/dist/plugins/session-store.d.ts +18 -20
  245. package/dist/plugins/session-store.js +4 -12
  246. package/dist/plugins/session-workspace-cell.d.ts +1 -1
  247. package/dist/plugins/session-workspace-cell.js +1 -1
  248. package/dist/plugins/shared-memory-store-sql.js +2 -2
  249. package/dist/plugins/sql-driver.d.ts +10 -5
  250. package/dist/plugins/sql-driver.js +6 -6
  251. package/dist/plugins/sql-errors.js +1 -1
  252. package/dist/plugins/store-backend.d.ts +35 -6
  253. package/dist/plugins/store-backend.js +27 -10
  254. package/dist/plugins/store-contracts.d.ts +9 -2
  255. package/dist/plugins/task-list-store-sql.js +11 -11
  256. package/dist/plugins/tidb-pool.js +7 -6
  257. package/dist/plugins/tidb-session-storage.js +4 -4
  258. package/dist/plugins/tidb-session-store.d.ts +4 -1
  259. package/dist/plugins/tidb-session-store.js +16 -15
  260. package/dist/plugins/tool-result-store-sql.d.ts +2 -2
  261. package/dist/plugins/tool-result-store-sql.js +3 -3
  262. package/dist/plugins/user-input-ledger-sql.js +2 -2
  263. package/dist/plugins/workflow-journal-store-sql.js +5 -5
  264. package/dist/plugins/workflow-run-store-sql.js +19 -19
  265. package/dist/plugins/write-behind-counter.d.ts +2 -3
  266. package/dist/plugins/write-behind-counter.js +5 -5
  267. package/dist/resource-suspend.d.ts +14 -12
  268. package/dist/resource-suspend.js +1 -1
  269. package/dist/resource-window.d.ts +4 -3
  270. package/dist/run-cancel-context.d.ts +34 -0
  271. package/dist/run-cancel-context.js +7 -0
  272. package/dist/run-local.js +14 -19
  273. package/dist/runs.d.ts +47 -16
  274. package/dist/runs.js +112 -113
  275. package/dist/security.d.ts +2 -2
  276. package/dist/server-secret-env.d.ts +2 -1
  277. package/dist/server-secret-env.js +2 -4
  278. package/dist/session-error-wire.d.ts +22 -4
  279. package/dist/session-error-wire.js +16 -0
  280. package/dist/session-incarnation.d.ts +10 -4
  281. package/dist/session-incarnation.js +1 -1
  282. package/dist/session-policy-wire.d.ts +1 -1
  283. package/dist/session-sync-kernel.d.ts +42 -1
  284. package/dist/session-sync-kernel.js +17 -0
  285. package/dist/session-sync.js +16 -4
  286. package/dist/sql-engine-coordinates.d.ts +49 -0
  287. package/dist/sql-engine-coordinates.js +27 -0
  288. package/dist/sql-engine-posture.d.ts +1 -1
  289. package/dist/sql-engine-posture.js +1 -1
  290. package/dist/standing-answer-grant.d.ts +31 -2
  291. package/dist/standing-answer-grant.js +14 -2
  292. package/dist/store-posture.d.ts +1 -1
  293. package/dist/{plugins → stores-sql}/background-agent-store-sql.d.ts +2 -2
  294. package/dist/{plugins → stores-sql}/background-agent-store-sql.js +8 -8
  295. package/dist/{plugins → stores-sql}/session-policy-store-sql.d.ts +7 -5
  296. package/dist/{plugins → stores-sql}/session-policy-store-sql.js +12 -12
  297. package/dist/{plugins → stores-sql}/usage-window-store-sql.d.ts +1 -1
  298. package/dist/{plugins → stores-sql}/usage-window-store-sql.js +3 -3
  299. package/dist/task-a2a.d.ts +1 -1
  300. package/dist/task-a2a.js +5 -6
  301. package/dist/task-mcp.d.ts +21 -8
  302. package/dist/task-mcp.js +17 -9
  303. package/dist/task-settings.d.ts +52 -25
  304. package/dist/task-settings.js +91 -28
  305. package/dist/terminal.d.ts +103 -19
  306. package/dist/terminal.js +72 -20
  307. package/dist/trace/core-keyset-guard.d.ts +79 -2
  308. package/dist/trace/exit-seat.d.ts +7 -0
  309. package/dist/trace/exit-seat.js +1 -0
  310. package/dist/trace/model-segment.d.ts +2 -2
  311. package/dist/trace/redact.d.ts +3 -1
  312. package/dist/trace/redact.js +395 -110
  313. package/dist/trace/self-minted-wire.d.ts +226 -2
  314. package/dist/trace/self-minted-wire.js +79 -0
  315. package/dist/verifier-pause-card.d.ts +8 -0
  316. package/dist/verifier-pause-card.js +19 -0
  317. package/dist/web-admin-client/apply-effective.d.ts +5 -7
  318. package/dist/web-admin-client/apply-effective.js +2 -2
  319. package/dist/web-admin-client/candidate-gate.d.ts +11 -7
  320. package/dist/web-admin-client/candidate-gate.js +1 -1
  321. package/dist/web-admin-client/http-client.d.ts +5 -4
  322. package/dist/web-admin-client/http-client.js +2 -2
  323. package/dist/web-admin-client/read-warnings.d.ts +7 -17
  324. package/dist/web-admin-client/read-warnings.js +5 -9
  325. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +3 -2
  326. package/package.json +2 -2
  327. package/wire-changes/server@7.140.0.json +184 -0
  328. package/wire-changes/server@7.141.0.json +367 -0
package/dist/runs.js CHANGED
@@ -1,5 +1,7 @@
1
1
  import { runWithVerification, runCascade, defaultTaskRegistry, taskNotificationDedupKey, DURABLE_AGENT_HANDLE_RE } from "@sema-agent/core";
2
- import { failureCodeOf, sealResultForExit, outwardPlane } from "./terminal.js";
2
+ import { atHostBoundary, failureCodeOf, sealResultForExit, outwardPlane } from "./terminal.js";
3
+ import { voidVerifierPauseCard } from "./verifier-pause-card.js";
4
+ import { voidPendingCard } from "./card-void.js";
3
5
  import { NOT_IN_ROSTER, entryProblemsFacts } from "./entry-problems.js";
4
6
  import { GOVERNANCE_HTTP_STATUS } from "./governance-http-status.js";
5
7
  import { TOOL_DECLARATION_HTTP_STATUS } from "./tool-declaration-http-status.js";
@@ -19,7 +21,9 @@ import { createApprovalCardEmitter, resolveApprovalLeg } from "./tool-approval.j
19
21
  import { fleetRunResiduals, isFleetAgentTerminalNotification } from "./fleet/fleet-bus.js";
20
22
  import { defaultSubagentTailBus } from "./fleet/subagent-tail-bus.js";
21
23
  import { emitPendingWorkflowCompletions, taskNotificationInboxEntry, taskNotificationStreamKeyOf, NotifiedKeys } from "./orchestration/workflow-completion-inbox.js";
24
+ import { isParkLedgerEventType } from "./trace/ledger-events.js";
22
25
  import { recordFailOpen } from "./observability/fail-open.js";
26
+ import { BASELINE_IO_DEADLINE_MS, IoDeadlineExceeded, withinIoDeadline } from "./instruction-source-notice.js";
23
27
  import { errorText as errText } from "./observability/err-text.js";
24
28
  import { LIVENESS_HEARTBEAT_MS } from "./config-invariants.js";
25
29
  export async function backgroundAgentOutput(registry, handle, access, agentStore) {
@@ -88,6 +92,7 @@ const PREPARE_REFUSAL_HTTP_STATUS = {
88
92
  "config.project_identity_mismatch": 409,
89
93
  "config.memory_project_marker": 409,
90
94
  "config.memory_project_marker_nested": 409,
95
+ "config.read_deny_floor_unkept": 409,
91
96
  };
92
97
  export function resumeAtHttpStatus(result) {
93
98
  const code = failureCodeOf(result.terminal);
@@ -196,16 +201,25 @@ export function createPromptCommitReader() {
196
201
  return closed.commits[closed.commits.length - 1 - closed.frames];
197
202
  };
198
203
  }
204
+ export async function settleResumeAnchor(write, where) {
205
+ try {
206
+ await withinIoDeadline(write);
207
+ }
208
+ catch (err) {
209
+ const why = err instanceof IoDeadlineExceeded ? `reason=timeout boundMs=${BASELINE_IO_DEADLINE_MS}` : `reason=error error=${err instanceof Error ? err.message : String(err)}`;
210
+ recordFailOpen("server.resume-anchor.capture-failed", `leg=${where.leg} kind=${where.kind} ${why}`);
211
+ }
212
+ }
199
213
  export class TurnAnchorCapture {
200
214
  capture;
201
- onCaptureFail;
215
+ leg;
202
216
  captureUser;
203
217
  firstTextEventId;
204
218
  turnHadTool = false;
205
219
  promptCommit = createPromptCommitReader();
206
- constructor(capture, onCaptureFail, captureUser) {
220
+ constructor(capture, leg, captureUser) {
207
221
  this.capture = capture;
208
- this.onCaptureFail = onCaptureFail;
222
+ this.leg = leg;
209
223
  this.captureUser = captureUser;
210
224
  }
211
225
  onHumanInput(ev) {
@@ -214,9 +228,9 @@ export class TurnAnchorCapture {
214
228
  async onMessageCommitted(role, entryId) {
215
229
  const committed = { type: "message_committed", role, entryId };
216
230
  const prompt = this.promptCommit(committed);
217
- if (prompt !== undefined && this.captureUser) {
218
- await this.captureUser(prompt).catch(() => this.onCaptureFail?.());
219
- }
231
+ const captureUser = this.captureUser;
232
+ if (prompt !== undefined && captureUser)
233
+ await settleResumeAnchor(() => captureUser(prompt), { leg: this.leg, kind: "prompt" });
220
234
  }
221
235
  onText(eventId) {
222
236
  if (this.firstTextEventId === undefined) {
@@ -229,9 +243,10 @@ export class TurnAnchorCapture {
229
243
  }
230
244
  async onTurnEnd() {
231
245
  this.promptCommit({ type: "turn_end" });
232
- if (this.firstTextEventId && !this.turnHadTool && this.capture) {
233
- await this.capture(this.firstTextEventId).catch(() => this.onCaptureFail?.());
234
- }
246
+ const capture = this.capture;
247
+ const handle = this.firstTextEventId;
248
+ if (handle && !this.turnHadTool && capture)
249
+ await settleResumeAnchor(() => capture(handle), { leg: this.leg, kind: "turn" });
235
250
  this.firstTextEventId = undefined;
236
251
  this.turnHadTool = false;
237
252
  }
@@ -257,7 +272,12 @@ export async function graftParkToolCallId(result, cs) {
257
272
  const id = await parkToolCallId(result, cs);
258
273
  return id === undefined ? result : { ...result, toolCallId: id };
259
274
  }
260
- export async function runInBackground(runner, spec, runStore, taskId, metrics, principal, verify, cascade, instrumentDegenerate, planCacheProbe, persistThinking = false, inflight, preemptable, steerable, modelUsage, elicitation, elicitOwner, captureTurnAnchor, fleetPublisher, captureUserMessageAnchor, question, workflowCompletionInbox, subagentSteer, completionDiagLog, sendUserFile, promptManifests, approval, parkCheckpointReader, pendingEngineNotices, trustedInternals, userInputLedger, acceptance, workspaceRecorder) {
275
+ export async function voidHeldParkCard(held, hostSessionId, cards, leg) {
276
+ if (held?.terminal.kind !== "paused")
277
+ return;
278
+ await voidPendingCard({ token: held.terminal.token, sessionId: hostSessionId }, cards, "server.park.held-card-void-failed", `leg=${leg}`);
279
+ }
280
+ export async function runInBackground(runner, spec, runStore, taskId, metrics, principal, verify, cascade, instrumentDegenerate, planCacheProbe, persistThinking = false, inflight, preemptable, steerable, modelUsage, elicitation, elicitOwner, captureTurnAnchor, fleetPublisher, captureUserMessageAnchor, question, workflowCompletionInbox, subagentSteer, completionDiagLog, sendUserFile, promptManifests, approval, checkpointCards, pendingEngineNotices, trustedInternals, userInputLedger, acceptance, workspaceRecorder) {
261
281
  const startedAt = Date.now();
262
282
  metrics?.addGauge("runs_active", 1);
263
283
  fleetPublisher?.onStart();
@@ -272,7 +292,6 @@ export async function runInBackground(runner, spec, runStore, taskId, metrics, p
272
292
  ? mintCancelledResult({ taskId, sessionId: spec.sessionId ?? "", startedAtMs: startedAt, message: "cancelled by user", cause, engineResult })
273
293
  : mintCancelledResult({ taskId, sessionId: spec.sessionId ?? "", startedAtMs: startedAt, message: "cancelled by user", cause });
274
294
  const recordDone = (result, status) => {
275
- metrics?.addGauge("runs_active", -1);
276
295
  metrics?.inc("tasks_total", { status });
277
296
  metrics?.observe("task_duration_seconds", (Date.now() - startedAt) / 1000);
278
297
  if (result?.stats.tokens)
@@ -317,7 +336,7 @@ export async function runInBackground(runner, spec, runStore, taskId, metrics, p
317
336
  }
318
337
  }, HEARTBEAT_MS);
319
338
  heartbeat.unref?.();
320
- const anchor = new TurnAnchorCapture(captureTurnAnchor, () => metrics?.inc("resume_anchor_capture_failed"), captureUserMessageAnchor);
339
+ const anchor = new TurnAnchorCapture(captureTurnAnchor, "bg", captureUserMessageAnchor);
321
340
  let reached;
322
341
  let observedDone;
323
342
  let causeAtDone;
@@ -326,14 +345,35 @@ export async function runInBackground(runner, spec, runStore, taskId, metrics, p
326
345
  let legLive = true;
327
346
  const notifiedKeys = new NotifiedKeys();
328
347
  const subagentHandleEvictions = [];
329
- const parkNeedsReview = async (res) => {
330
- await sink.appendParked("needs_review", res);
348
+ const parkRowWrite = {
349
+ suspended: (sealed) => runStore.setSuspended(taskId, sealed),
350
+ needs_review: (sealed) => runStore.setNeedsReview(taskId, sealed),
351
+ };
352
+ const parkLeg = async (word, res) => {
353
+ const grafted = await graftParkToolCallId(res, checkpointCards);
354
+ await sink.appendParked(word, grafted);
331
355
  await flushModelUsage();
332
- const sealed = sealResultForExit(res);
333
- reached = { kind: "needs_review", result: sealed };
334
- await runStore.setNeedsReview(taskId, sealed);
335
- metrics?.addGauge("runs_active", -1);
336
- metrics?.inc("tasks_total", { status: "needs_review" });
356
+ const sealed = sealResultForExit(grafted);
357
+ reached = { kind: word, result: sealed };
358
+ await parkRowWrite[word](sealed);
359
+ acceptance?.settle(sealed);
360
+ };
361
+ const settleLegResult = async (engineResult) => {
362
+ const result = atHostBoundary(engineResult, spec.sessionId);
363
+ const parkWord = result.terminal.kind === "paused" ? outwardPlane(result.terminal).status : undefined;
364
+ if (parkWord !== undefined && isParkLedgerEventType(parkWord)) {
365
+ await parkLeg(parkWord, result);
366
+ return;
367
+ }
368
+ const settled = causeAtDone !== undefined ? cancelledResult(causeAtDone, result) : result;
369
+ settledAsCancel = failureCodeOf(settled.terminal) === CANCELLED_CODE;
370
+ const safe = await withModelUsage(sealResultForExit(settled));
371
+ await sink.appendDone(safe);
372
+ reached = { kind: "done", result: safe };
373
+ const plane = outwardPlane(safe.terminal);
374
+ await runStore.setTerminal(taskId, plane.status, safe, plane.errorMessage ?? null);
375
+ await releaseKeyIfZeroWorkRefusal(runStore, taskId, safe);
376
+ acceptance?.settle(safe);
337
377
  };
338
378
  const instructionLeg = createInstructionSelectionLeg();
339
379
  const sink = createLedgerSink({ appendEvent: (seq, type, data) => runStore.appendEvent(taskId, seq, type, data), persistThinking, notifiedKeys, onActivity: (kind, statusData) => recordTurnActivity(taskId, kind, statusData), legSections: () => ({ instructions: instructionLeg.segment(), skills: runSkillsOf(spec) }) });
@@ -419,32 +459,19 @@ export async function runInBackground(runner, spec, runStore, taskId, metrics, p
419
459
  void sink.appendStatus(e).catch((err) => completionDiagLog?.("status_event_append_failed", { route: "bg-run-composite", taskId, err: err instanceof Error ? err.message : String(err) }));
420
460
  },
421
461
  };
422
- if (verify !== undefined) {
423
- const result = await runWithVerification(runner, { ...spec, taskId, signal: cancelCtrl.signal }, verify, { ...fwdInternals, ...statusSeat });
462
+ const compositeLeg = verify !== undefined
463
+ ? () => runWithVerification(runner, { ...spec, taskId, signal: cancelCtrl.signal }, verify, { ...fwdInternals, ...statusSeat })
464
+ : cascade !== undefined
465
+ ? () => runCascade(runner, { ...spec, taskId, signal: cancelCtrl.signal }, cascade, { ...fwdInternals, ...statusSeat })
466
+ : undefined;
467
+ if (compositeLeg !== undefined) {
468
+ const result = await compositeLeg();
469
+ observedDone = result;
470
+ causeAtDone = cancelCtrl.cause();
471
+ await voidVerifierPauseCard(result, checkpointCards);
424
472
  await flush();
425
473
  await flushPromptManifest();
426
- const safe = await withModelUsage(sealResultForExit(result));
427
- await append("done", { result: safe });
428
- reached = { kind: "done", result: safe };
429
- const plane = outwardPlane(safe.terminal);
430
- await runStore.setTerminal(taskId, plane.status, safe, plane.errorMessage ?? null);
431
- await releaseKeyIfZeroWorkRefusal(runStore, taskId, safe);
432
- acceptance?.settle(safe);
433
- recordDone(safe, plane.status);
434
- return;
435
- }
436
- if (cascade !== undefined) {
437
- const result = await runCascade(runner, { ...spec, taskId, signal: cancelCtrl.signal }, cascade, { ...fwdInternals, ...statusSeat });
438
- await flush();
439
- await flushPromptManifest();
440
- const safe = await withModelUsage(sealResultForExit(result));
441
- await append("done", { result: safe });
442
- reached = { kind: "done", result: safe };
443
- const plane = outwardPlane(safe.terminal);
444
- await runStore.setTerminal(taskId, plane.status, safe, plane.errorMessage ?? null);
445
- await releaseKeyIfZeroWorkRefusal(runStore, taskId, safe);
446
- acceptance?.settle(safe);
447
- recordDone(safe, plane.status);
474
+ await settleLegResult(result);
448
475
  return;
449
476
  }
450
477
  const stream = runner.runTaskStream({ ...spec, taskId, signal: cancelCtrl.signal, preemptSignal: preemptCtrl.signal }, undefined, fwdInternals);
@@ -479,33 +506,7 @@ export async function runInBackground(runner, spec, runStore, taskId, metrics, p
479
506
  }
480
507
  if (ev.type === "done") {
481
508
  await flush();
482
- const parkStatus = ev.result.terminal.kind === "paused" ? outwardPlane(ev.result.terminal).status : undefined;
483
- if (parkStatus === "suspended") {
484
- const grafted = await graftParkToolCallId(ev.result, parkCheckpointReader);
485
- await sink.appendParked("suspended", grafted);
486
- await flushModelUsage();
487
- const sealed = sealResultForExit(grafted);
488
- reached = { kind: "suspended", result: sealed };
489
- await runStore.setSuspended(taskId, sealed);
490
- metrics?.addGauge("runs_active", -1);
491
- metrics?.inc("tasks_total", { status: "suspended" });
492
- continue;
493
- }
494
- if (parkStatus === "needs_review") {
495
- await parkNeedsReview(ev.result);
496
- continue;
497
- }
498
- {
499
- const settled = causeAtDone !== undefined ? cancelledResult(causeAtDone, ev.result) : ev.result;
500
- settledAsCancel = failureCodeOf(settled.terminal) === CANCELLED_CODE;
501
- const safe = await withModelUsage(sealResultForExit(settled));
502
- await sink.appendDone(safe);
503
- reached = { kind: "done", result: safe };
504
- const plane = outwardPlane(safe.terminal);
505
- await runStore.setTerminal(taskId, plane.status, safe, plane.errorMessage ?? null);
506
- await releaseKeyIfZeroWorkRefusal(runStore, taskId, safe);
507
- recordDone(safe, failureCodeOf(safe.terminal) === CANCELLED_CODE ? CANCELLED_CODE : plane.status);
508
- }
509
+ await settleLegResult(ev.result);
509
510
  continue;
510
511
  }
511
512
  await sink.onEvent(ev);
@@ -561,7 +562,6 @@ export async function runInBackground(runner, spec, runStore, taskId, metrics, p
561
562
  }
562
563
  reached = { kind: "done", result: c };
563
564
  await runStore.setTerminal(taskId, "failed", c, outwardPlane(c.terminal).errorMessage ?? null);
564
- recordDone(c, CANCELLED_CODE);
565
565
  }
566
566
  }
567
567
  catch (err) {
@@ -581,11 +581,9 @@ export async function runInBackground(runner, spec, runStore, taskId, metrics, p
581
581
  await releaseKeyIfZeroWorkRefusal(runStore, taskId, reached.result);
582
582
  acceptance?.settle(reached.result);
583
583
  }
584
- else if (reached?.kind === "suspended") {
585
- await runStore.setSuspended(taskId, reached.result);
586
- }
587
- else if (reached?.kind === "needs_review") {
588
- await runStore.setNeedsReview(taskId, reached.result);
584
+ else if (reached !== undefined) {
585
+ await parkRowWrite[reached.kind](reached.result);
586
+ acceptance?.settle(reached.result);
589
587
  }
590
588
  else if (thrownCancelCause !== undefined && observedDone === undefined) {
591
589
  const c = cancelledResult(thrownCancelCause);
@@ -601,6 +599,7 @@ export async function runInBackground(runner, spec, runStore, taskId, metrics, p
601
599
  else {
602
600
  settledAsCancel = false;
603
601
  const safeMsg = redactErrorMessage(message) ?? message;
602
+ await voidHeldParkCard(observedDone === undefined ? undefined : atHostBoundary(observedDone, spec.sessionId), spec.sessionId, checkpointCards, "bg");
604
603
  try {
605
604
  await append("failed", { errorMessage: safeMsg });
606
605
  }
@@ -612,48 +611,48 @@ export async function runInBackground(runner, spec, runStore, taskId, metrics, p
612
611
  }
613
612
  catch {
614
613
  }
615
- if (reached?.kind === "done")
616
- recordDone(reached.result, failureCodeOf(reached.result.terminal) === CANCELLED_CODE ? CANCELLED_CODE : outwardPlane(reached.result.terminal).status);
617
- else if (reached?.kind === "suspended") {
618
- metrics?.addGauge("runs_active", -1);
619
- metrics?.inc("tasks_total", { status: "suspended" });
620
- }
621
- else if (settledAsCancel) {
622
- recordDone(null, CANCELLED_CODE);
623
- }
624
- else {
625
- recordDone(null, "failed");
626
- }
627
614
  }
628
615
  finally {
629
616
  legLive = false;
630
617
  unregisterEngineNotice();
631
- if (fleetPublisher) {
632
- if (reached?.kind === "done")
633
- fleetPublisher.onTerminal(failureCodeOf(reached.result.terminal) === CANCELLED_CODE ? CANCELLED_CODE : outwardPlane(reached.result.terminal).status, fleetRunResiduals(reached.result));
634
- else if (reached?.kind === "suspended")
635
- fleetPublisher.onTerminal("suspended");
636
- else if (reached?.kind === "needs_review")
637
- fleetPublisher.onTerminal("needs_review");
618
+ const verdict = reached?.kind === "done"
619
+ ? { kind: "terminal", label: failureCodeOf(reached.result.terminal) === CANCELLED_CODE ? CANCELLED_CODE : outwardPlane(reached.result.terminal).status, result: reached.result }
620
+ : reached !== undefined
621
+ ? { kind: "park", word: reached.kind }
622
+ : { kind: "terminal", label: settledAsCancel ? CANCELLED_CODE : "failed", result: null };
623
+ try {
624
+ metrics?.addGauge("runs_active", -1);
625
+ if (verdict.kind === "park")
626
+ metrics?.inc("tasks_total", { status: verdict.word });
638
627
  else
639
- fleetPublisher.onTerminal(settledAsCancel ? CANCELLED_CODE : "failed", {
640
- ...(settledAsCancel ? { stoppedBy: "user" } : {}),
641
- ...(spec.sessionId ? { transcriptId: spec.sessionId } : {}),
642
- });
628
+ recordDone(verdict.result, verdict.label);
629
+ }
630
+ finally {
631
+ if (fleetPublisher) {
632
+ if (verdict.kind === "park")
633
+ fleetPublisher.onTerminal(verdict.word);
634
+ else if (verdict.result !== null)
635
+ fleetPublisher.onTerminal(verdict.label, fleetRunResiduals(verdict.result));
636
+ else
637
+ fleetPublisher.onTerminal(verdict.label, {
638
+ ...(verdict.label === CANCELLED_CODE ? { stoppedBy: "user" } : {}),
639
+ ...(spec.sessionId ? { transcriptId: spec.sessionId } : {}),
640
+ });
641
+ }
642
+ if (steerableStreamRef !== undefined)
643
+ await userInputLedger?.settleLeg(steerableStreamRef);
644
+ if (inflight?.get(taskId) === cancelCtrl)
645
+ inflight.delete(taskId);
646
+ if (preemptable?.get(taskId) === preemptCtrl)
647
+ preemptable.delete(taskId);
648
+ if (steerableStreamRef !== undefined && steerable?.get(taskId) === steerableStreamRef)
649
+ steerable.delete(taskId);
650
+ for (const evict of subagentHandleEvictions)
651
+ evict();
652
+ modelUsage?.clear(taskId);
653
+ promptManifests?.clear(taskId, manifestLease);
654
+ clearInterval(heartbeat);
643
655
  }
644
- if (steerableStreamRef !== undefined)
645
- await userInputLedger?.settleLeg(steerableStreamRef);
646
- if (inflight?.get(taskId) === cancelCtrl)
647
- inflight.delete(taskId);
648
- if (preemptable?.get(taskId) === preemptCtrl)
649
- preemptable.delete(taskId);
650
- if (steerableStreamRef !== undefined && steerable?.get(taskId) === steerableStreamRef)
651
- steerable.delete(taskId);
652
- for (const evict of subagentHandleEvictions)
653
- evict();
654
- modelUsage?.clear(taskId);
655
- promptManifests?.clear(taskId, manifestLease);
656
- clearInterval(heartbeat);
657
656
  }
658
657
  }
659
658
  export const HEARTBEAT_MS = LIVENESS_HEARTBEAT_MS;
@@ -176,10 +176,10 @@ export declare function legacyUnownedAmnestyOpen(deployment: DeploymentShapeInpu
176
176
  export type OwnerAwareSessionStore = SessionStore & {
177
177
  /** 属主登记(core `SessionStore.register` 的契约:对没有行的 id 建行并记下属主,首写赢;对已有的行什么都不做)。读回走 `headOf`。 */
178
178
  register?: (sessionId: string, owner: string | null) => Promise<void>;
179
- /** [ref] 委派继承律的**认领口** —— 只在记录委派树的店上(SQL 两店,只命中 placed 行;local 店没有):判断时读到的那一位住客仍是无主的 placed 行 ⇒ 写 `owner`,
179
+ /** [ref] 委派继承律的**认领口** —— 只在记录委派树的店上(三只持久会话店,只命中 placed 行;不记委派树的店没有):判断时读到的那一位住客仍是无主的 placed 行 ⇒ 写 `owner`,
180
180
  * 返回是否写成(只 CAS 子行自己)。唯一调用方 = 停驻挂点的 `settleDelegatedOwner`(`delegated-session-owner.ts`;M23 走同一句 SQL);规则写在那个模块。 */
181
181
  claimOwnerless?: OwnerlessClaim;
182
- /** [ref] placed 分区的元组读口(店的既有读口,一行 PK 读;`undefined` = 普通会话或无此行)。店不交它 = 不实现 placement = 没有 placed 行(local 店)。两个读者:
182
+ /** [ref] placed 分区的元组读口(店的既有读口,一行 PK 读;`undefined` = 普通会话或无此行)。店不交它 = 不实现 placement = 没有 placed 行(无 repo 的内存店)。两个读者:
183
183
  * ① 会话面的属主门只在属主为 NULL 时读它一次(无主的 placed 行不是公共行,`http/principal-gate.ts` 的 `sessionOwnerCellOf`);
184
184
  * ② 会话视图读它投影体顶层 `agent` 键(`audit.ts` 的 `sessionAgentOf`,只在视图读上付):店没有这一面 ⇒ 键缺席,读面不凭别处另铸子代身份。 */
185
185
  placementOf?: (sessionId: string) => Promise<SessionPlacementRecord | undefined>;
@@ -73,7 +73,8 @@ export declare function serverCredentialValueSources(modelApiKeyEnv?: ModelApiKe
73
73
  * 所以这一次抛只可能是名的问题。
74
74
  * · **值臂**:{@link serverCredentialValueSources} 的每一个变量此刻在 env 里的值,逐个交给引擎**同一只**判(不另写「什么算凭据值」的第二份判据):引擎收下 ⇒ 记进值臂;
75
75
  * 引擎拒(不是凭据值:空白 / 占位词 / 一个字符重复 / 不足 8 字符;或它是每个子进程都会拿到的变量的值)⇒ 这一个值不扣、名照扣、**不拒启**(与引擎自己的收值口
76
- * `holdOwnCredentialValues` 同一姿势:不收 + 说一次),`console.warn` 点名那个变量一次(引擎的拒句从不回显值;本句也不),能力位 `inherit_env.own_credential_values_withheld`
76
+ * `holdOwnCredentialValues` 同一姿势:不收 + 说一次),经留痕座结构化点名那个变量一次 —— `warn` 行 `own_credential_value_not_withheld { name, reason }`
77
+ * ({@link recordOwnCredentialValueNotWithheld};`reason` 是引擎拒句原文,从不回显值;起服第一步还没有 logger 时当拍经进程默认的 JSON 行日志器写 stderr,不缓冲),能力位 `inherit_env.own_credential_values_withheld`
77
78
  * 因此答 false(位真 ⇔ 值臂真扣)。引擎拒一个值时声明保持上一份,所以逐个交表的中间态都是上一份的超集。
78
79
  * 返回声明的名单(只名、不值)。
79
80
  */
@@ -1,7 +1,7 @@
1
1
  import { declareWithheldEnv, hostChildEnv, inheritedHostEnv } from "@sema-agent/core";
2
2
  import { CONFIG_CATALOG } from "./config-catalog.js";
3
3
  import { ENV_NAME_ALLOWLIST_KNOB_NAMES } from "./env-name-allowlist-knobs.js";
4
- import { recordSecretEnvScrub } from "./observability/secret-env-scrub.js";
4
+ import { recordOwnCredentialValueNotWithheld, recordSecretEnvScrub } from "./observability/secret-env-scrub.js";
5
5
  export const CATALOG_SECRET_ENV_NAMES = new Set(CONFIG_CATALOG.filter((spec) => spec.type === "secret").map((spec) => spec.name));
6
6
  const NO_MODEL_ENTRIES = Object.freeze({});
7
7
  export function serverWithheldEnvNames(modelApiKeyEnv = NO_MODEL_ENTRIES) {
@@ -31,9 +31,7 @@ export function declareServerWithheldEnv(modelApiKeyEnv = NO_MODEL_ENTRIES) {
31
31
  catch (e) {
32
32
  if (!valueRefusalAnnounced.has(source)) {
33
33
  valueRefusalAnnounced.add(source);
34
- console.warn(`[sema-server] own credential ${source}: its value is not withheld by value from spawned children — the engine refused it (${e instanceof Error ? e.message : String(e)}); ` +
35
- "the variable name is still withheld under every inheritEnv policy, but a variable holding the same string under another name is not, and GET /v1/capabilities answers inherit_env.own_credential_values_withheld: false. " +
36
- "Give it a real credential value (at least 8 characters, not a placeholder) to withhold copies of it too.");
34
+ recordOwnCredentialValueNotWithheld({ name: source, reason: e instanceof Error ? e.message : String(e) });
37
35
  }
38
36
  }
39
37
  }
@@ -37,7 +37,7 @@
37
37
  * 列表里的缺席),码位各不相同,不由本表统一。
38
38
  * · `placement_refused` —— `import` 面 **409 `conflict.session_placed`**(机读位 `detail.placement_kind`,固定句);`open` 面留白。
39
39
  * 同一个词在两面上说的不是同一件事:import 面只有「会话同步往一只**分区会话**(委派子代的转录)上写」一个来源
40
- * (SQL 两店的整段换装 / 暂存提交门:目标 id 的 `placement_kind` 在场 ⇒ 不适用),调用方没做错格式、是**目标**不收这种写;
40
+ * (三只持久会话店的整段换装 / 暂存提交门:目标 id 的 `placement_kind` 在场 ⇒ 不适用),调用方没做错格式、是**目标**不收这种写;
41
41
  * open 面上同一个词还含「声明了分区的创建撞上了分区外 / 异组的行」两形(core `TtlSessionStore.assertPlacementAdmission` 的后两条),
42
42
  * 那不是「目标是分区会话」。而 run 终局上它是引擎词汇(`terminal.code`),原样随 failed 体走 —— 若在这里分诊成 409,
43
43
  * 体上的 `errorCode` 仍是引擎词 `placement_refused`,一个状态两个名字。所以 open 面留白,`invalid_session` 是同形先例。
@@ -47,16 +47,18 @@
47
47
  * (请求 core 把种类放进错误,而不是在这里解析句子)。
48
48
  * · `invalid_entry` / `invalid_fork_target` —— 留白:本仓今天没有一条 wire 口把它们作为自己的
49
49
  * 应答(它们在 core 的追加 / 分叉面上抛,落进各站点既有的处置或通用 500)。给它们起名是另一件事、另一张票。
50
- * · `conflict` —— `import` 面 **409 `conflict`**(固定句 {@link STAGED_IMPORT_OWNER_CHANGED_SENTENCE});`open` 面留白。import 面上这个词只有一个来源:
50
+ * · `conflict` —— `import` 面 **409 `conflict`**(固定句 {@link STAGED_IMPORT_OWNER_CHANGED_SENTENCE});`open` 面留白。按码映射时 import 面上这个词只有一个来源:
51
51
  * 暂存提交的属主 CAS(`session-sync-kernel.ts` 的 `StagedCommitOwnerChangedError`,三家店在提交事务里比「此刻的属主格 = 这次导入的门判过的那一格」,
52
52
  * 不等 ⇒ 回滚)—— 码位与同族的提交期同步冲突(`SyncConflictError` ⇒ 409 `conflict`「sync conflict」)同一个,出路也相同:从第一阶段重来(重新过门)。
53
+ * 本服务自铸的同码具名拒 {@link StampedSessionArchiveError}(local 店:存档仍带更早版本写下的属主戳)不按码映射 —— 出路不同(重来照样撞),
54
+ * 由 {@link sessionErrorWireOf} 按**型**先认:两面同答 409 `conflict` + 固定句 {@link STAMPED_SESSION_ARCHIVE_SENTENCE}。
53
55
  * open 面上它是 run 的写撞上真并发写,落成那条 run 的 `failed` 终局(`terminal.code: "conflict"`),提交腿不另作处置。
54
56
  * · `storage` / `unknown` —— 留白:故障类,通用 500 就是它们今天的应答。
55
57
  *
56
58
  * core 给闭集加一个词 ⇒ 下面的 `switch` 编译红,由人裁定它在两面上的答复,而不是让它静默落进某个恰好存在的臂
57
59
  * ([ref] 词表纪律;同形先例 `session-policy-wire.ts` 的 `sessionPolicyErrorHttp`)。
58
60
  */
59
- import type { SessionError } from "@sema-agent/core";
61
+ import { SessionError } from "@sema-agent/core";
60
62
  /** core `SessionError` 的码闭集(core 不从包根导出这个型名 ⇒ 从导出类的 `code` 键派生,不手抄)。 */
61
63
  export type SessionErrorCode = SessionError["code"];
62
64
  /** 映射的面(见文件头注)。 */
@@ -76,7 +78,7 @@ export interface SessionErrorWire {
76
78
  /** import 面 `invalid_session` 的固定句(修前 session-sync 暂存导入腿那一行的原文)。 */
77
79
  export declare const STAGED_IMPORT_INVALID_SENTENCE = "staged session log failed validation";
78
80
  /**
79
- * import 面 `placement_refused` 的固定句:wire 上不发引擎原句 —— 两家 SQL 店的原句不同(措辞各自铸),都点名会话 id 与引擎内部词
81
+ * import 面 `placement_refused` 的固定句:wire 上不发引擎原句 —— 三只持久会话店的原句各不相同(措辞各自铸),都点名会话 id 与引擎内部词
80
82
  * (「placement partition」「delegation transcript」);固定句只说调用方能做什么(冻结在 `test/api-error-text-freeze.test.ts` 的常量锚格)。
81
83
  */
82
84
  export declare const PLACED_SESSION_SENTENCE = "this session holds a delegated agent's transcript, so session sync cannot overwrite it; sync into a different session id";
@@ -85,6 +87,22 @@ export declare const PLACED_SESSION_SENTENCE = "this session holds a delegated a
85
87
  * 不发店的原句(它点名会话 id);固定句只说发生了什么与出路(冻结在 `test/api-error-text-freeze.test.ts` 的常量锚格)。
86
88
  */
87
89
  export declare const STAGED_IMPORT_OWNER_CHANGED_SENTENCE = "session owner changed while the import streamed; nothing was imported \u2014 start the sync again";
90
+ /**
91
+ * local 店的具名拒(唯一铸点 `plugins/local-session-store.ts`):这只会话的引擎存档仍带更早版本写下的属主戳(一次性迁移 M33 改写不了它,或数据根跳过了带这次迁移的版本)
92
+ * ⇒ 不在它上面造新存档(fork),也不原地换它的日志(改日志的推送);零写。码是 core 闭集的 `conflict`(码不另铸);句子(给运维日志,点名会话 id)归本错,
93
+ * wire 上发固定句 {@link STAMPED_SESSION_ARCHIVE_SENTENCE}(两面同一句;映射 {@link sessionErrorWireOf})。
94
+ */
95
+ export declare class StampedSessionArchiveError extends SessionError {
96
+ readonly sessionId: string;
97
+ constructor(sessionId: string);
98
+ }
99
+ /** {@link StampedSessionArchiveError} 的 wire 固定句(fork 与改日志的推送同一句;不点名会话 id;出路在句里 —— 从头再同步照样撞)。 */
100
+ export declare const STAMPED_SESSION_ARCHIVE_SENTENCE = "this session's stored archive still carries an owner stamp written by an earlier server version, so it cannot be forked or have its log replaced; nothing was written \u2014 delete the session and push it again";
101
+ /**
102
+ * 一只 `SessionError` 在一面上的 wire 答复 —— 下发点({@link sendSessionError} 的唯一读法)经这里:本服务自铸的同码具名拒先按型认(它的出路与同码的
103
+ * 按码答复不同),其余按码({@link sessionErrorHttp})。
104
+ */
105
+ export declare function sessionErrorWireOf(e: SessionError, face: SessionErrorFace): SessionErrorWire | undefined;
88
106
  export declare function sessionErrorHttp(code: SessionErrorCode, face: SessionErrorFace): SessionErrorWire | undefined;
89
107
  export declare function isSessionErrorCode(code: string): code is SessionErrorCode;
90
108
  //# sourceMappingURL=session-error-wire.d.ts.map
@@ -1,12 +1,28 @@
1
+ import { SessionError } from "@sema-agent/core";
1
2
  export const STAGED_IMPORT_INVALID_SENTENCE = "staged session log failed validation";
2
3
  export const PLACED_SESSION_SENTENCE = "this session holds a delegated agent's transcript, so session sync cannot overwrite it; sync into a different session id";
3
4
  const PLACEMENT_KIND = "subagent";
4
5
  export const STAGED_IMPORT_OWNER_CHANGED_SENTENCE = "session owner changed while the import streamed; nothing was imported — start the sync again";
6
+ export class StampedSessionArchiveError extends SessionError {
7
+ sessionId;
8
+ constructor(sessionId) {
9
+ super("conflict", `session ${sessionId}'s stored archive still carries an owner stamp written by an earlier version (the one-time migration M33 could not rewrite it as anonymous, ` +
10
+ "or this data root skipped the versions that carry that migration) — no new session is made from it and its log is not replaced, so this write was refused and nothing was written. " +
11
+ "Reading, resuming and pulling the session are unaffected. Way out: delete the session and push it again (a push onto an absent session creates it anew).");
12
+ this.sessionId = sessionId;
13
+ this.name = "StampedSessionArchiveError";
14
+ }
15
+ }
16
+ export const STAMPED_SESSION_ARCHIVE_SENTENCE = "this session's stored archive still carries an owner stamp written by an earlier server version, so it cannot be forked or have its log replaced; nothing was written — delete the session and push it again";
5
17
  const CORRUPT_SESSION = { status: 500, errorCode: "corrupt_session" };
6
18
  const STORED_LOG_INVALID = { status: 500, errorCode: "invalid_session" };
7
19
  const INVALID_ENTRIES = { status: 422, errorCode: "invalid_entries", sentence: STAGED_IMPORT_INVALID_SENTENCE };
8
20
  const SESSION_PLACED = { status: 409, errorCode: "conflict.session_placed", sentence: PLACED_SESSION_SENTENCE, detail: { placement_kind: PLACEMENT_KIND } };
9
21
  const STAGED_IMPORT_OWNER_CHANGED = { status: 409, errorCode: "conflict", sentence: STAGED_IMPORT_OWNER_CHANGED_SENTENCE };
22
+ const STAMPED_SESSION_ARCHIVE = { status: 409, errorCode: "conflict", sentence: STAMPED_SESSION_ARCHIVE_SENTENCE };
23
+ export function sessionErrorWireOf(e, face) {
24
+ return e instanceof StampedSessionArchiveError ? STAMPED_SESSION_ARCHIVE : sessionErrorHttp(e.code, face);
25
+ }
10
26
  export function sessionErrorHttp(code, face) {
11
27
  switch (code) {
12
28
  case "corrupt_session":
@@ -6,9 +6,15 @@
6
6
  * 一个可比的答案 —— 今天只能比属主,同一个人删了重建就比不出来。
7
7
  *
8
8
  * ## 规则(全仓唯一处写)
9
- * · **铸点只在新建行**:会话店的 `register` / 无 id 创建 / fork / session-sync 的导入新行。已存在的行一个字节都不动
10
- * (与 `owner` 同一条 first-write 律);session-sync 的整段换装(replace / staged commit)换的是**同一位住客**的日志,
11
- * 化身沿用。删除随行消失,同 id 重新登记铸新的。
9
+ * · **铸点只在新建行**:会话店的 `register` / 无 id 创建 / fork / session-sync 的导入新行。已存在的行化身一个字节都不动;
10
+ * session-sync 的整段换装(replace / staged commit)换的是**同一位住客**的日志,化身沿用 —— 属主却可以被换装重戳为导入者(local 的 M10 认领同理),
11
+ * 所以化身与属主不是同一条律(读法分三种,见下「两读」)。删除随行消失,同 id 重新登记铸新的。
12
+ * · **两读(住客的三种问法,各读各的格,不混用)**:
13
+ * K1 **住客自己的记录**(工作区、记忆、空间分区……按「这一位住客」落的东西)只按化身绑 —— 同一位住客的属主换代(推送重戳 / 认领)不换住客,记录照落;
14
+ * K2 **以「这一行的这一代」为条件的写**(执行放置的换绑 / 收回、执行上下文的复核)比属主与化身整对,判官 = core 的行期望判官 `sessionRowMatches`
15
+ * (`execution-placement.ts` 的 `sameOccupant`,SQL `incarnation = ? AND owner <=> ?` 的对位)—— 读与写之间属主换了一代 ⇒ 不是判过的那一行;
16
+ * K0 **只问属主的门**(授权门、暂存提交的属主 CAS)只读属主那一格。三种都读会话**行**;local 店的行 = 会话元数据边档那一格(引擎仓里的存档是匿名内容仓,
17
+ * 存档头上的属主不是行事实 —— `plugins/local-session-store.ts` 头注)。
12
18
  * · **值是不透明随机串**(`randomUUID()`,v4:没有时间戳、没有机器信息 —— 刻意不用 uuidv7,那一形带铸造时刻)。只拿来比较。
13
19
  * · **一格、一个解码**:住客令牌是会话**行**上的一格 —— SQL `session_meta.incarnation`(两方言)、local 后端的会话元数据边档那一格
14
20
  * (`plugins/local-session-store.ts` 的 `session-incarnations.json`)。行上的值只经 {@link incarnationFromCell} 解码。
@@ -146,7 +152,7 @@ export declare function carriedRowCells(sessionId: string, prior: {
146
152
  */
147
153
  export declare const SESSION_META_INCARNATION_COLUMN: {
148
154
  readonly name: "incarnation";
149
- readonly tidb: "incarnation VARCHAR(64) NOT NULL DEFAULT ''";
155
+ readonly mysql: "incarnation VARCHAR(64) NOT NULL DEFAULT ''";
150
156
  readonly pg: "incarnation VARCHAR(64) COLLATE \"C\" NOT NULL DEFAULT ''";
151
157
  };
152
158
  //# sourceMappingURL=session-incarnation.d.ts.map
@@ -75,7 +75,7 @@ export function carriedRowCells(sessionId, prior) {
75
75
  }
76
76
  export const SESSION_META_INCARNATION_COLUMN = {
77
77
  name: "incarnation",
78
- tidb: "incarnation VARCHAR(64) NOT NULL DEFAULT ''",
78
+ mysql: "incarnation VARCHAR(64) NOT NULL DEFAULT ''",
79
79
  pg: `incarnation VARCHAR(64) COLLATE "C" NOT NULL DEFAULT ''`,
80
80
  };
81
81
  //# sourceMappingURL=session-incarnation.js.map
@@ -139,7 +139,7 @@ export declare function sessionPolicyErrorFacts(e: SessionPolicyError): Readonly
139
139
  * · `conflict`(409)—— CAS 输了,重读 `rev` 再来。**码位与状态逐字不变**。
140
140
  * · `loosen_forbidden`(403)—— 非 operator 想放松。**码位与状态逐字不变**。⚠️ 这个词还有**第二个铸点**([ref] B5,
141
141
  * 如实登记、未改):PG 后端的 SQL 双生对含 NUL / 孤代理字节的规则拒收时借用了它
142
- * (`plugins/session-policy-store-sql.ts` 的 `pgHasUnstorable` 臂)—— 那一形**既非放宽也非越权**,operator 写同样撞上;
142
+ * (`stores-sql/session-policy-store-sql.ts` 的 `pgHasUnstorable` 臂)—— 那一形**既非放宽也非越权**,operator 写同样撞上;
143
143
  * core 的闭集里没有「输入不可存」这个词,改码位要先裁定新码与 PUT / 2c 导入两条入口同判(DEBTS [ref] B5)。
144
144
  * · `corrupt`(**500**)—— 店读不出它自己该读的那份字节(记号/行被列外篡改或撕裂,或存着引擎今天的形读法读不出的规则体)。
145
145
  * 不是 4xx:调用方没做错任何事,它也改不了这件事;不是 503:重试改不了判(修复路径是**人**去改回或删掉那一行,
@@ -8,7 +8,8 @@
8
8
  * (security ↔ session-sync; lens2 §B/§E). This file imports nothing from the repo but TYPES from leaves (core types, and
9
9
  * the session-face type from `session-incarnation.ts`, itself a leaf) — a leaf, same shape as session-sync-content.ts. Its one VALUE import is
10
10
  * core's `SessionError` class (the closed-set store error the three adapters already throw). session-sync.ts re-exports everything here, so existing import sites
11
- * keep working unchanged.
11
+ * keep working unchanged. The import door's anchor judge ({@link readImportAnchors}) was written here, not moved: it is pure too, and the route and the
12
+ * tests read it from the same leaf.
12
13
  */
13
14
  import { SessionError, type SessionTreeEntry } from "@sema-agent/core";
14
15
  import type { SessionFace } from "./session-incarnation.js";
@@ -180,4 +181,44 @@ export declare class SyncConflictError extends Error {
180
181
  relation: "fork" | "stale";
181
182
  }>);
182
183
  }
184
+ /**
185
+ * §9 — one anchor of a Phase-A import body as the import door reads it: the live `eventId` the shell holds → the persisted `entryId` it resumes at.
186
+ * The wire's `owner` is not carried: every replay re-keys the anchor to the IMPORTING principal (the importer, never the exporter, owns the row),
187
+ * so nothing downstream reads the sender's value and nothing here judges it.
188
+ */
189
+ export interface ImportAnchor {
190
+ readonly eventId: string;
191
+ readonly entryId: string;
192
+ }
193
+ /** The import door's verdict on `anchors[]`: the judged list, or ONE sentence for the first violation (index + the offending `entryId`, nothing else). */
194
+ export type ImportAnchorsRead = {
195
+ readonly anchors: ImportAnchor[];
196
+ } | {
197
+ readonly problem: string;
198
+ };
199
+ /**
200
+ * §9 — THE predicate of the anchor law, and its only spelling: an anchor is sound iff the entry it resumes at is one of the entries declared next to it
201
+ * (an import body's `entryIds`; a manifest's `entryIds`). The import door ({@link readImportAnchors}) refuses an anchor that fails it, and the manifest
202
+ * export (`exportSessionManifest`) does not hand one out — one fact, one reading, on both sides of the wire.
203
+ */
204
+ export declare function anchorEntryDeclared(declared: ReadonlySet<string>, anchorEntryId: string): boolean;
205
+ /**
206
+ * §9 — the ONE judge of a Phase-A body's `anchors[]`, run at the door before anything is classified, mirrored, staged or written (a refusal
207
+ * writes no anchor, no log, no session row, opens no staging, takes no lease, and leaves the execution cell alone).
208
+ *
209
+ * The law (single source): an anchor may only point at an entry the session HOLDS once the import has landed, and what an import lands is the log
210
+ * its body declares (`entryIds`): a commit REPLACES the destination's log with the staged entries (SQL: purge + row rename in one transaction; local:
211
+ * an in-place swap), `fresh` / `fast_forward` / `identical` leave no destination entry the declaration lacks, and an `overwrite-dst` `fork` / `stale`
212
+ * removes the destination-only entries (and wipes the anchors that pointed at them). So "the entries the import declares" is the whole question: an
213
+ * `entryId` outside it names a row that is not there once the import lands (an anchor to a destination-only entry is exactly the dangling anchor the
214
+ * purge exists to prevent). An entry a concurrent writer appends after the body's ids were read is outside the declaration too, and an anchor to it is
215
+ * refused the same way — the conservative direction: the sender re-reads its manifest and pushes again (`exportSessionManifest` reads the anchors BEFORE
216
+ * the entry ids, so a manifest never carries an anchor newer than its own id list, and drops — loudly, by the same {@link anchorEntryDeclared} — an
217
+ * anchor an earlier version stored for an entry the log does not hold). The replays downstream (the `identical` arm, the post-commit leg)
218
+ * only iterate the list this function returned — they do not judge again.
219
+ *
220
+ * Shape is judged by the same reader — an element that is not an object with a string `eventId` and a string `entryId` is refused by index —
221
+ * so a `null` element is a 400 at the door, not a TypeError in a replay leg.
222
+ */
223
+ export declare function readImportAnchors(raw: readonly unknown[], declaredEntryIds: readonly string[]): ImportAnchorsRead;
183
224
  //# sourceMappingURL=session-sync-kernel.d.ts.map
@@ -55,4 +55,21 @@ export class SyncConflictError extends Error {
55
55
  this.name = "SyncConflictError";
56
56
  }
57
57
  }
58
+ export function anchorEntryDeclared(declared, anchorEntryId) {
59
+ return declared.has(anchorEntryId);
60
+ }
61
+ export function readImportAnchors(raw, declaredEntryIds) {
62
+ const declared = new Set(declaredEntryIds);
63
+ const anchors = [];
64
+ for (let i = 0; i < raw.length; i++) {
65
+ const a = raw[i];
66
+ const { eventId, entryId } = (typeof a === "object" && a !== null && !Array.isArray(a) ? a : {});
67
+ if (typeof eventId !== "string" || typeof entryId !== "string")
68
+ return { problem: `anchors: [${i}] must be an object with a string eventId and a string entryId` };
69
+ if (!anchorEntryDeclared(declared, entryId))
70
+ return { problem: `anchors: [${i}].entryId ${JSON.stringify(entryId)} is not among the entryIds this import declares` };
71
+ anchors.push({ eventId, entryId });
72
+ }
73
+ return { anchors };
74
+ }
58
75
  //# sourceMappingURL=session-sync-kernel.js.map