@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
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * [ref] A9 域模块 —— memory 导出/同步 + 只读策略面。
3
3
  *
4
- * 路由:`GET /v1/memory/export`、`POST /v1/memory/sync/:scope`、`GET /v1/policy`。
4
+ * 路由:`GET /v1/memory/export`、`POST /v2/memory/sync/:scope`、`GET /v1/policy`。
5
5
  * owner 门 = 「本人 user scope / 本人的项目层 / 显式 operator / `org:` 键的目录成员」(见 {@link gateMemoryScope}),
6
6
  * 越界一律 404(零存在性 oracle)。本域零可变状态。
7
7
  */
@@ -35,7 +35,7 @@ export declare const MEMORY_POLICY_ROUTES: readonly [{
35
35
  }, {
36
36
  readonly id: "memory-sync-scope";
37
37
  readonly pattern: RegExp;
38
- readonly label: "/v1/memory/sync/:scope";
38
+ readonly label: "/v2/memory/sync/:scope";
39
39
  readonly methods: readonly ["POST"];
40
40
  }];
41
41
  /** 本域可分派行的 `id` 闭集 —— handler 的 `switch` 按它判穷尽(漏一口 = 编译红)。 */
@@ -1,5 +1,7 @@
1
1
  import { formatUserScope } from "@sema-agent/core";
2
2
  import { parseMemorySyncRequest } from "../../memory-sync.js";
3
+ import { memorySyncIdentityRule } from "../../memory-sync-identity.js";
4
+ import * as facts from "../../capability-facts.js";
3
5
  import { sendJson, sendError } from "../send.js";
4
6
  import { buildOriginFence, readOriginAware } from "../../memory-origin-fence.js";
5
7
  import { parseProjectLayerKey } from "../../project-layer-key.js";
@@ -73,7 +75,8 @@ export async function handleMemoryPolicy(req, res, match, ctx) {
73
75
  sendUnauthorized(res, "identity");
74
76
  return;
75
77
  }
76
- if (deps.config.memoryEngineBackend === "file" || !deps.memoryExport) {
78
+ const exportFact = facts.memoryExport(deps);
79
+ if (!exportFact.available) {
77
80
  sendError(res, 501, "capability.memory_store_required", "memory export requires a DB memory backend (MEMORY_ENGINE_BACKEND=pg|mysql)");
78
81
  return;
79
82
  }
@@ -101,7 +104,7 @@ export async function handleMemoryPolicy(req, res, match, ctx) {
101
104
  return;
102
105
  }
103
106
  try {
104
- const fence = buildOriginFence(await deps.memoryExport(scope), readOriginAware(new URLSearchParams(match.search)));
107
+ const fence = buildOriginFence(await exportFact.read(scope), readOriginAware(new URLSearchParams(match.search)));
105
108
  const entries = fence.entries;
106
109
  const withheld = fence.withheldOriginTagged !== undefined ? { withheldOriginTagged: fence.withheldOriginTagged } : {};
107
110
  if (limitRaw === null) {
@@ -135,9 +138,18 @@ export async function handleMemoryPolicy(req, res, match, ctx) {
135
138
  sendUnauthorized(res, "identity");
136
139
  return;
137
140
  }
138
- if (deps.config.memoryEngineBackend === "file" || !deps.memorySync) {
139
- sendError(res, 501, "capability.memory_store_required", "memory sync requires a DB memory backend (MEMORY_ENGINE_BACKEND=pg|mysql)");
140
- return;
141
+ const serve = facts.memorySyncServe(deps);
142
+ if (!serve.available) {
143
+ switch (serve.why) {
144
+ case "memory_db_plane_absent":
145
+ sendError(res, 501, "capability.memory_store_required", "memory sync requires a DB memory backend (MEMORY_ENGINE_BACKEND=pg|mysql)");
146
+ return;
147
+ case "memory_sync_identity_undeclared":
148
+ sendError(res, 501, "feature.memory_sync_serve_disabled", "this center serves no memory sync — it declares no sync identity: set MEMORY_SYNC_SERVE_IDENTITY to the base address clients reach this center at");
149
+ return;
150
+ default:
151
+ return assertNever(serve.why);
152
+ }
141
153
  }
142
154
  const scope = safeDecode(match.params.scope);
143
155
  if (scope === null) {
@@ -153,11 +165,19 @@ export async function handleMemoryPolicy(req, res, match, ctx) {
153
165
  const body = await readJson(req);
154
166
  const parsed = parseMemorySyncRequest(body, scope);
155
167
  if (!parsed.ok) {
168
+ if ("peerFlaw" in parsed) {
169
+ sendError(res, 400, "request.field_invalid", `peer ${memorySyncIdentityRule(parsed.peerFlaw)} — it must be the sync identity this client reaches the center at (the center's GET /v1/capabilities memorySyncServe.identity)`, { field: "peer", reason: parsed.peerFlaw });
170
+ return;
171
+ }
156
172
  sendError(res, 422, "memory_sync_invalid_body", parsed.error);
157
173
  return;
158
174
  }
175
+ if (parsed.value.peer !== serve.identity) {
176
+ sendError(res, 409, "config.memory_sync_target_mismatch", `this center's sync identity is ${JSON.stringify(serve.identity)} and the round names another peer — nothing was read or written: set the client's MEMORY_SYNC_URL to that address (the client reaches this center under another address — a different hostname, port or path)`);
177
+ return;
178
+ }
159
179
  try {
160
- const out = await deps.memorySync(scope, parsed.value);
180
+ const out = await serve.perform(scope, parsed.value);
161
181
  sendJson(res, 200, out);
162
182
  }
163
183
  catch (err) {
@@ -194,6 +214,6 @@ export async function handleMemoryPolicy(req, res, match, ctx) {
194
214
  export const MEMORY_POLICY_ROUTES = [
195
215
  { id: "memory-export", path: "/v1/memory/export", methods: ["GET"] },
196
216
  { id: "policy", path: "/v1/policy", methods: ["GET"] },
197
- { id: "memory-sync-scope", pattern: /^\/v1\/memory\/sync\/(?<scope>.*)$/, label: "/v1/memory/sync/:scope", methods: ["POST"] },
217
+ { id: "memory-sync-scope", pattern: /^\/v2\/memory\/sync\/(?<scope>.*)$/, label: "/v2/memory/sync/:scope", methods: ["POST"] },
198
218
  ];
199
219
  //# sourceMappingURL=memory-policy.js.map
@@ -1,13 +1,12 @@
1
- import { externalNotificationInboxEntry } from "../../orchestration/workflow-completion-inbox.js";
2
1
  import { sendJson, sendError } from "../send.js";
3
2
  import { gatedPrincipal, explicitOperatorOk, sendPrincipalRefusal, sessionCallerOf } from "../principal-gate.js";
4
3
  import { ownerlessRowOpen, sessionOwnerCellOf } from "../../security.js";
5
4
  import { sendResumeOutcome } from "../route-ctx.js";
6
5
  import { assertNever, SESSION_NOT_FOUND_MESSAGE } from "../route-ctx.js";
7
6
  import * as facts from "../../capability-facts.js";
7
+ import { readResumeRestatement } from "../resume-restatement.js";
8
8
  export async function handleNotifyWake(req, res, match, ctx) {
9
9
  const { deps } = ctx;
10
- const { steerableRuns } = ctx.registry;
11
10
  const { readJson, rateLimited, quotaExceeded, leaseDenied, safeDecode } = ctx.helpers;
12
11
  const { resumeWake } = ctx.legs;
13
12
  switch (match.decl.id) {
@@ -57,11 +56,12 @@ export async function handleNotifyWake(req, res, match, ctx) {
57
56
  sendError(res, 501, "capability.session_ownership_required", "external notify requires the session-ownership face (sessionStorage.headOf)");
58
57
  return;
59
58
  }
60
- const notifyOwner = (await owned.sessionStorage.headOf(notifySession))?.owner;
61
- if (notifyOwner === undefined) {
59
+ const notifyHead = await owned.sessionStorage.headOf(notifySession);
60
+ if (notifyHead === undefined) {
62
61
  sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
63
62
  return;
64
63
  }
64
+ const notifyOwner = notifyHead.owner;
65
65
  const notifyOperator = explicitOperatorOk(principal, deps.config);
66
66
  if (!notifyOperator && notifyOwner !== principal && !ownerlessRowOpen(await sessionOwnerCellOf(owned.sessionStorage, notifySession, notifyOwner), sessionCallerOf(req, deps))) {
67
67
  sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
@@ -75,29 +75,26 @@ export async function handleNotifyWake(req, res, match, ctx) {
75
75
  ...(nb.seq !== undefined ? { seq: nb.seq } : {}),
76
76
  ...(nb.source !== undefined ? { source: nb.source } : {}),
77
77
  };
78
- const liveTaskId = await deps.runStore?.getActiveTaskId?.(notifySession).catch(() => undefined);
79
- const liveStream = liveTaskId !== undefined && liveTaskId !== null ? steerableRuns.get(liveTaskId) : undefined;
80
- if (liveStream) {
81
- try {
82
- await liveStream.notify(payload, { priority: "next" });
78
+ const out = await ctx.legs.sessionNotify.deliver({ sessionId: notifySession, occupant: notifyHead, ownerlessEntryOwner: principal ?? null }, payload);
79
+ switch (out.delivery) {
80
+ case "live":
83
81
  sendJson(res, 200, { sessionId: notifySession, delivery: "live" });
84
82
  return;
85
- }
86
- catch (e) {
87
- const code = e.code;
88
- if (typeof code === "string" && code.startsWith("notify.")) {
89
- sendError(res, 400, code, e instanceof Error ? e.message : "invalid notification");
90
- return;
91
- }
92
- }
93
- }
94
- if (!deps.workflowCompletionInbox) {
95
- sendError(res, 501, "capability.workflow_store_required", "external notify requires the workflow completion inbox (WORKFLOW_RUN_STORE)");
96
- return;
83
+ case "refused":
84
+ sendError(res, 400, out.code, out.message);
85
+ return;
86
+ case "no_inbox":
87
+ sendError(res, 501, "capability.workflow_store_required", "external notify requires the workflow completion inbox (WORKFLOW_RUN_STORE)");
88
+ return;
89
+ case "parked":
90
+ sendJson(res, 202, { sessionId: notifySession, delivery: "parked", note: "no live stream — parked in the session inbox; drained as a task_notification on the next stream open" });
91
+ return;
92
+ case "withdrawn":
93
+ sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
94
+ return;
95
+ default:
96
+ return assertNever(out);
97
97
  }
98
- await deps.workflowCompletionInbox.enqueue(externalNotificationInboxEntry(notifySession, notifyOwner ?? principal ?? null, { task_type: "external", ...payload }, Date.now()));
99
- sendJson(res, 202, { sessionId: notifySession, delivery: "parked", note: "no live stream — parked in the session inbox; drained as a task_notification on the next stream open" });
100
- return;
101
98
  }
102
99
  case "session-wake": {
103
100
  if (!facts.checkpointPark(deps).available) {
@@ -121,7 +118,8 @@ export async function handleNotifyWake(req, res, match, ctx) {
121
118
  sendError(res, 400, "request.field_invalid", "message must be a non-empty string when present");
122
119
  return;
123
120
  }
124
- const out = await resumeWake(wakeSession, wakeBody.message, principal, req, true);
121
+ const restatement = readResumeRestatement(wakeBody);
122
+ const out = await resumeWake(wakeSession, wakeBody.message, principal, req, true, undefined, undefined, restatement);
125
123
  sendResumeOutcome(res, out, deps.logger);
126
124
  return;
127
125
  }
@@ -18,23 +18,33 @@ const RULES_PAGE_DEFAULT = 50;
18
18
  const RULES_PAGE_MAX = 200;
19
19
  const MAX_LAYER_CONTENT_CHARS = 16_384;
20
20
  const MAX_LAYERS = 3;
21
+ const CC_IMPORT_ROOTED_LAYERS = IMPORTED_SETTINGS_LAYERS.filter((l) => l !== "userSettings");
21
22
  const CcImportPrepareBodySchema = z
22
23
  .object({
23
24
  layers: z
24
- .array(z
25
- .object({
26
- layer: z.enum(IMPORTED_SETTINGS_LAYERS),
27
- path: z.string().min(1).max(1024),
28
- root: z.string().max(1024),
29
- content: z.string().max(MAX_LAYER_CONTENT_CHARS),
30
- })
31
- .strict())
25
+ .array(z.discriminatedUnion("layer", [
26
+ z
27
+ .object({
28
+ layer: z.literal("userSettings"),
29
+ path: z.string().min(1).max(1024),
30
+ content: z.string().max(MAX_LAYER_CONTENT_CHARS),
31
+ })
32
+ .strict(),
33
+ z
34
+ .object({
35
+ layer: z.enum(CC_IMPORT_ROOTED_LAYERS),
36
+ path: z.string().min(1).max(1024),
37
+ root: z.string().max(1024),
38
+ content: z.string().max(MAX_LAYER_CONTENT_CHARS),
39
+ })
40
+ .strict(),
41
+ ]))
32
42
  .min(1)
33
43
  .max(MAX_LAYERS),
34
44
  })
35
45
  .strict();
36
46
  function ccImportRootRefused(layer) {
37
- return !isValidDurableScope({ kind: "project", root: layer.root });
47
+ return layer.root !== undefined && !isValidDurableScope({ kind: "project", root: layer.root });
38
48
  }
39
49
  const TicketStringSchema = z.string().min(1).max(190).refine((t) => !hasControlOrBidi(t));
40
50
  const CcImportRedeemBodySchema = z.object({ ticket: TicketStringSchema }).strict();
@@ -122,9 +132,21 @@ export async function handleRules(req, res, match, ctx) {
122
132
  }
123
133
  const prepared = await lane.prepareLocalImport(principal, parsed.data.rows);
124
134
  if (!prepared.ok) {
125
- logger.info("local rule import batch refused", prepared.reason === "too-many-rows" ? { reason: prepared.reason, rows: prepared.rows } : { reason: prepared.reason, bytes: prepared.bytes });
126
- sendError(res, 413, "request.payload_too_large", "too many rule rows in this batch — split it and import again");
127
- return;
135
+ switch (prepared.reason) {
136
+ case "too-many-rows":
137
+ case "batch-too-large":
138
+ logger.info("local rule import batch refused", prepared.reason === "too-many-rows" ? { reason: prepared.reason, rows: prepared.rows } : { reason: prepared.reason, bytes: prepared.bytes });
139
+ sendError(res, 413, "request.payload_too_large", "too many rule rows in this batch — split it and import again");
140
+ return;
141
+ case "tightening-write-failed":
142
+ logger.info("local rule import tightening write failed", { detail: prepared.detail, committed: prepared.committed });
143
+ sendRuleWriteFailure(res, "import-tightening", prepared.committed);
144
+ return;
145
+ default: {
146
+ const unknown = prepared;
147
+ throw new Error(`unknown local import refusal: ${JSON.stringify(unknown)}`);
148
+ }
149
+ }
128
150
  }
129
151
  sendJson(res, 200, { preview: prepared.preview, ...(prepared.ticket !== undefined ? { ticket: prepared.ticket, expiresAtMs: prepared.expiresAtMs } : {}) });
130
152
  return;
@@ -157,7 +179,7 @@ export async function handleRules(req, res, match, ctx) {
157
179
  if (match.decl.id === "rules-cc-import-prepare") {
158
180
  const parsed = CcImportPrepareBodySchema.safeParse(rawBody);
159
181
  if (!parsed.success) {
160
- sendError(res, 400, "request.body_shape", "body must be {layers:[{layer,path,root,content}]}");
182
+ sendError(res, 400, "request.body_shape", "body must be {layers:[{layer:\"userSettings\",path,content} | {layer:\"projectSettings\"|\"localSettings\",path,root,content}]}");
161
183
  return;
162
184
  }
163
185
  const refusedLayer = parsed.data.layers.findIndex(ccImportRootRefused);
@@ -268,6 +290,19 @@ function sendRuleWriteFailure(res, verb, committed) {
268
290
  throw new Error(`unknown rule-write committed word: ${JSON.stringify(unknownWord)}`);
269
291
  }
270
292
  }
293
+ case "import-tightening":
294
+ switch (committed) {
295
+ case "no":
296
+ sendError(res, 503, "state.rule_write_failed", "a deny / ask row of this batch was not written (that row is not in the store); deny / ask rows before it in the batch may already be in force, and the batch's allow rows were withheld (no ticket) — send the same batch again (rows already in force answer no-op)", { committed });
297
+ return;
298
+ case "unknown":
299
+ sendError(res, 503, "state.rule_write_failed", "a deny / ask row of this batch may or may not have landed; deny / ask rows before it in the batch may already be in force, and the batch's allow rows were withheld (no ticket) — reconcile with GET /v1/rules, or send the same batch again (rows already in force answer no-op)", { committed });
300
+ return;
301
+ default: {
302
+ const unknownWord = committed;
303
+ throw new Error(`unknown rule-write committed word: ${JSON.stringify(unknownWord)}`);
304
+ }
305
+ }
271
306
  case "revoke":
272
307
  switch (committed) {
273
308
  case "no":
@@ -13,10 +13,10 @@ import { idemCallerOf, idemKeyOf, inflightKeyOf, submissionIdemOf } from "../ide
13
13
  import { streamSseLog } from "../sse-log.js";
14
14
  import { bindSseLifecycle } from "../sse-lifecycle.js";
15
15
  import { buildApprovalPreamble, buildApprovalPreambleSseFrames, readClosedWordSeat, storedCardDemandsAPerson } from "../../approval-card.js";
16
- import { heldAskOf, liveSettledBy, readDecidedBy, retiredDecisionKeyFacts, DECIDED_BY_NOT_IN_PROOF, DECIDED_BY_OUTSIDE_CLOSED_SET, POLICY_CANNOT_ALLOW_PERSON_ASK } from "../../host-decision.js";
16
+ import { heldAskOf, liveHostSource, readDecider, retiredDecisionKeyFacts, DECIDED_BY_NOT_IN_PROOF, POLICY_CANNOT_ALLOW_PERSON_ASK } from "../../host-decision.js";
17
17
  import { ASK_DECISION_CONSUMER_ABSENT, approvalPreambleReplays, outcomeOfDecidedRow, presentedApprovalFrameFamily, resolveStreamApprovalGate } from "../../tool-approval.js";
18
18
  import { MAX_DECISION_NOTE_CHARS, noteLandedOnRow, noteRecordedReceipt } from "../../approval-ask-machine.js";
19
- import { causeOfAskRow } from "../../approval-deny-reasons.js";
19
+ import { askRowEndingCause } from "../../approval-deny-reasons.js";
20
20
  import { normalizeRunEventType, screenedStoredEventData } from "../../trace/project.js";
21
21
  import { invalidJsonError, sendJson, sendError, sendHttpError, sseHeaders } from "../send.js";
22
22
  import { mintCancelledResult } from "../../run-cancel-context.js";
@@ -57,6 +57,7 @@ const AskDecisionBodySchema = z
57
57
  .optional(),
58
58
  actor: z.object({ label: z.string().max(ASK_ACTOR_LABEL_MAX) }).strict().optional(),
59
59
  decidedBy: z.unknown().optional(),
60
+ decidedVia: z.unknown().optional(),
60
61
  })
61
62
  .strict();
62
63
  const ASK_DECISION_CONFIRM_TIMEOUT_MS = 2_000;
@@ -658,7 +659,7 @@ export async function handleRuns(req, res, match, ctx) {
658
659
  state: { activeTools: [], nestedStats: { tokens: 0, turns: 0, tasks: 0, costMicroUsd: 0, anyUnpriced: false } },
659
660
  status: "pending",
660
661
  createdAt: Date.now(),
661
- sourceTaskId: taskId,
662
+ sourceSessionId: run.sessionId,
662
663
  });
663
664
  const stillRead = await readResumeCtx(cs.getCtx(run.sessionId), { sessionId: run.sessionId, opener: deps.checkpointCtxSealing });
664
665
  if (stillRead.kind === "corrupt" || stillRead.kind === "unsealable") {
@@ -1320,17 +1321,17 @@ export async function handleRunVerbs(req, res, match, ctx) {
1320
1321
  sendError(res, 400, "request.body_shape", retired.error, retired.extra);
1321
1322
  return;
1322
1323
  }
1323
- sendError(res, 400, "request.body_shape", "invalid ask decision body — expected decision=approve|deny plus optional updatedInput / note / idempotencyKey / actor.label / decidedBy");
1324
+ sendError(res, 400, "request.body_shape", "invalid ask decision body — expected decision=approve|deny plus optional updatedInput / note / idempotencyKey / actor.label / decidedBy / decidedVia");
1324
1325
  return;
1325
1326
  }
1326
1327
  const body = parsedBody.data;
1327
- const decidedByReading = readDecidedBy(body.decidedBy, deps.config.directDoorActive);
1328
+ const decidedByReading = readDecider(body, deps.config.directDoorActive);
1328
1329
  switch (decidedByReading.kind) {
1329
1330
  case "not_in_proof":
1330
1331
  sendError(res, 400, "decided_by_not_in_proof", DECIDED_BY_NOT_IN_PROOF);
1331
1332
  return;
1332
1333
  case "outside_closed_set":
1333
- sendError(res, 400, "request.field_invalid", DECIDED_BY_OUTSIDE_CLOSED_SET);
1334
+ sendError(res, 400, "request.field_invalid", decidedByReading.message);
1334
1335
  return;
1335
1336
  case "accepted":
1336
1337
  break;
@@ -1339,7 +1340,7 @@ export async function handleRunVerbs(req, res, match, ctx) {
1339
1340
  throw new Error(`unhandled decidedBy reading: ${JSON.stringify(unreachable)}`);
1340
1341
  }
1341
1342
  }
1342
- const decidedBy = decidedByReading.decidedBy;
1343
+ const decider = decidedByReading.decider;
1343
1344
  const hasUpdatedInput = typeof rawBody === "object" && rawBody !== null && "updatedInput" in rawBody;
1344
1345
  const taskId = match.params.taskId;
1345
1346
  const askId = match.params.askId;
@@ -1429,10 +1430,10 @@ export async function handleRunVerbs(req, res, match, ctx) {
1429
1430
  return;
1430
1431
  case "DENIED":
1431
1432
  case "VOID": {
1432
- const rowCause = causeOfAskRow(row);
1433
+ const endingCause = askRowEndingCause(row);
1433
1434
  sendError(res, 410, "gone.ask", "this ask reached a terminal state without a human decision (cause says why: a routing failure, a cancellation or batch withdrawal or abort, or the process that held it ended) and can no longer be decided", {
1434
1435
  state: row.state,
1435
- ...(rowCause?.kind === "ending" ? { cause: rowCause.cause } : {}),
1436
+ ...(endingCause !== undefined ? { cause: endingCause } : {}),
1436
1437
  });
1437
1438
  return;
1438
1439
  }
@@ -1473,7 +1474,7 @@ export async function handleRunVerbs(req, res, match, ctx) {
1473
1474
  sendTerminal(row0);
1474
1475
  return;
1475
1476
  }
1476
- if (body.decision === "approve" && policyCannotAnswer(heldAskOf(storedCardDemandsAPerson(row0.cardJson)), decidedBy)) {
1477
+ if (body.decision === "approve" && policyCannotAnswer(heldAskOf(storedCardDemandsAPerson(row0.cardJson)), decider.decidedBy)) {
1477
1478
  sendError(res, 400, "request.field_conflict", POLICY_CANNOT_ALLOW_PERSON_ASK, fieldConflictFacts("policy_person_ask", ["decidedBy", "decision"]));
1478
1479
  return;
1479
1480
  }
@@ -1485,7 +1486,7 @@ export async function handleRunVerbs(req, res, match, ctx) {
1485
1486
  decisionActor: actor,
1486
1487
  decisionNote: body.note ?? null,
1487
1488
  idempotencyKey: body.idempotencyKey ?? null,
1488
- ...((sb) => (sb !== undefined ? { settledBy: sb } : {}))(liveSettledBy(decidedBy)),
1489
+ ...liveHostSource(decider),
1489
1490
  ...(body.decision === "approve" && hasUpdatedInput ? { updatedInput: body.updatedInput } : {}),
1490
1491
  });
1491
1492
  }
@@ -0,0 +1,91 @@
1
+ /**
2
+ * [ref] —— **会话记忆同步触发口**:`POST /v1/sessions/:id/memory/sync` body `{ target: string }`
3
+ * → 200 引擎的同步报告(core `memorySyncReportOf` 的产物,原样)
4
+ *
5
+ * 一口一件事:让本机的记忆同步腿**现在**对会话要去的那台引擎同步一轮,只回报这个会话读的那几层。壳在把会话推上云(fork)之前调它。
6
+ *
7
+ * ── 规则(规则账只减)────────────────────────────────────────────────────────────────────────────
8
+ * R1 一轮 = 同步腿的一轮(`MemorySyncRunner.syncOnce`):交回每层每次调用如它落定的那样,从不整轮 reject —— 部分层失败落在报告的逐层结局里。
9
+ * R2 同步身份一只铸点(`memory-sync-identity.ts`):`target` 必须**就是**目标引擎能力面公开的 `memorySyncServe.identity`(规范形、不带凭据),
10
+ * 与本机腿的 `peer` 按串相等比。壳不拼、本口不规范化。
11
+ * R4 门之后的一切答复都是 core 报告:200 带 `layers`(每层恰一个结局)或整轮 `refused`(目标不符 / 会话有在飞的 run)。
12
+ * HTTP 错误只留给报告产生之前的门(能力 / 身份 / id / 属主 / 体)与「读集不可取得」。本口不铸汇总键、不加第五种结局。
13
+ * R5 可用性 = 能力事实 `sessionMemorySync`(本机同步腿 ∧ 持久 run 账面 ∧ 会话头读),与能力位同读一只;不从后端名推。
14
+ *
15
+ * ── 读集(这个会话读哪几层)────────────────────────────────────────────────────────────────────
16
+ * = 会话**最近一条已结腿**的记忆观测(run 店 `latestMemoryObservationForSession`;规则属主 `store-contracts.ts` 的
17
+ * `SettledLegMemoryObservation`,四态闭集 `LegMemoryObservation`):`mounted` ⇒ `scopes[].scope` 按服务序;`none`(那一腿没配记忆:`no-spec` / `disabled`)⇒ `[]` —— 是值;
18
+ * `memoryless`(S-2855B:那一腿配了记忆而没挂上 —— 它本该读哪几层在挂载失败时不可知,答 `[]` 就是把故障洗成「不读记忆」)与
19
+ * `absent`([ref] 空观测:那一腿的记录里没有观测 —— 因由只说记录里有什么,不推断它停在哪一步)⇒ 读集取不到。**不往更早的腿找**(一条律,一只读法)。
20
+ * 读集取不到 ⇒ 409 `conflict.session_memory_unobserved`,体恒带 `reason`(闭集 {@link SESSION_MEMORY_UNOBSERVED_REASONS},判序 = 读的先后):
21
+ * 会话还没有已结腿 ⇒ `no_settled_run`;那一腿是空观测 ⇒ 它的因由(`run_result_missing` / `run_result_unobserved`);那一腿没挂上记忆 ⇒ `memory_not_mounted`
22
+ * (引擎的两只因由 `mount-failed` / `no-backend` 不在本体上复述 —— 它们在那条 run 的结果里,`GET /v1/runs/:id` 的 `result.effectiveMemoryScopes.reason`);观测在、会话没有叶子 ⇒ `no_leaf`。
23
+ * 句子由 `reason` 定(一张表 {@link SESSION_MEMORY_UNOBSERVED_MESSAGES});有在飞 run 时另带 `activeTaskId`。
24
+ * 路由不重铸 spec:spec 只在受理时铸、不落会话行,重铸 = 重启前后两种答案。
25
+ *
26
+ * ── 门序(编号即执行序)──────────────────────────────────────────────────────────────────────────
27
+ * 1. 事实 `sessionMemorySync` 为假 ⇒ 501(码按 why)。先于身份:「这台部署有没有这一口」本由能力位公开。
28
+ * 2. 多租户部署上没有验过的身份 ⇒ 401/403(`sendPrincipalRefusal`,会话面同一只)。
29
+ * 3. 限流(本口会向远端发请求)。
30
+ * 4. 会话 id 段解码不了 ⇒ 400 `request.path_malformed`。
31
+ * 5. 一次头读:没有这只会话 / 不是你的 ⇒ 404 `not_found.session`(同码同串,无存在性 oracle;属主判据 = 会话面唯一谓词 `sessionOwnerOk`);
32
+ * 同一次头读的叶子 = `sourceRevision`(与 `GET /v1/sessions/:id/head` 的 `leafId` 同一读法)。
33
+ * 6. 体:闭集一键 `{ target }`,否则 400 `request.body_shape`;`target` 不是声明的同步身份 ⇒ 400 `request.field_invalid`(`field` / `reason`,不回显值)。
34
+ * 7. 读集(上一节)。
35
+ * 8. 目标比对:`target` ≠ 本机腿的 `peer` ⇒ 报告 `refused: config.memory_sync_target_mismatch`(句由 core 铸;零请求零本地写)。先于空读集出口。
36
+ * 9. 收割栅栏(前检):会话**不静** ⇒ 报告 `refused: session_active`(句由本服务铸)。零请求。
37
+ * **静** = 一只谓词,前检与后检同读,三格合取:会话没有认领 ∧ 最近受理的腿**就是**观测所在的那一条(读完观测之后没有新 run 被受理)∧ 会话叶子仍是 `sourceRevision`
38
+ * (会话的记录没有前进)。次序 = 认领 → 最近受理的腿 → 叶子:run 行在受理时同步落库、消息在那之后才追加,所以认领读之后才受理的 run 在第二只读里看得见;
39
+ * 第三格不靠建行时刻 —— 「最近受理」按建行时刻排序,副本之间钟偏 / 时钟回拨时一条后受理的 run 可以排在观测腿之前,而它只要跑过就挪动了叶子。
40
+ * 10. 一轮:读集为空 ⇒ 空报告(同一只铸点);否则 `syncOnce("session-trigger", { reading })`,只挑读集里的层的调用交给引擎出报告。
41
+ * 11. 收割栅栏(后检):轮落定后会话不静 ⇒ 第 9 步的报告 —— 这一轮的请求与写已经发生(幂等,下次触发重放),报告只是不再为这份读集作保。
42
+ * 报告之后、fork 之前的源前进不由本口封闭:壳拿 `sourceRevision` 与它推上云的会话头比。
43
+ * 12. 引擎的报告铸点自己抛 `TypeError`(名字带凭据 / 空串)⇒ 500 + 运维 warn(拒句不引原值)。同步层名起服时已过同一只凭据判官(`config.ts`),这一臂按构造不可达。
44
+ *
45
+ * ── 并发 ───────────────────────────────────────────────────────────────────────────────────────
46
+ * 同一会话、同一 `sourceRevision`、同一观测腿、同一 `target` 的请求各自走完 1–9,再**并入**在飞的那一轮,拿同一份报告(只减少重复工作,不延长等待)。
47
+ * 别的会话 / 后台轮经腿的同一条串行链(`exclusive`):会话触发的轮起跑晚于本请求到达。请求方断开不取消轮。本口不设自己的截止(= 各调用的传输超时之和)。
48
+ *
49
+ * ── 计费 / 门 ──────────────────────────────────────────────────────────────────────────────────
50
+ * 零模型工作(不 billable、不吃 drain 503);写的是调用方自己的记忆到它自己配置的中心 ⇒ 属主门兜底,不进无凭据改写门(会话自助抹除同判)。
51
+ */
52
+ import type { IncomingMessage, ServerResponse } from "node:http";
53
+ import { type RouteCtx, type RouteMatch, type RouteIdsOf } from "../route-ctx.js";
54
+ import { type SettledLegMemoryObservation } from "../../plugins/store-contracts.js";
55
+ /** 收割栅栏的句(本服务铸;码 `session_active` 是引擎报告闭集的成员)。 */
56
+ export declare 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) \u2014 wait for it to finish on its own, then sync again (do not cancel it to sync sooner)";
57
+ /**
58
+ * 409 `conflict.session_memory_unobserved` 体上的 `reason`(闭集;判序 = 读的先后):会话没有已结腿 → 那一腿的观测取不出读集(空观测的因由,词表属主 = `store-contracts.ts`
59
+ * 的 `MEMORY_OBSERVATION_ABSENT_REASONS`,本表不另写;或引擎观测说配了记忆而没挂上)→ 观测在、会话没有叶子。自铸 wire 值登记 = `trace/self-minted-wire.ts` 的 `sessionMemoryUnobservedReason`。
60
+ */
61
+ export declare const SESSION_MEMORY_UNOBSERVED_REASONS: readonly ["no_settled_run", "run_result_missing", "run_result_unobserved", "memory_not_mounted", "no_leaf"];
62
+ export type SessionMemoryUnobservedReason = (typeof SESSION_MEMORY_UNOBSERVED_REASONS)[number];
63
+ /** 每个 `reason` 的句子(wire 文案,冻结在 `test/api-error-text-freeze.test.ts` 的常量锚格;句子由 `reason` 定,调用点不另写一句)。 */
64
+ export declare const SESSION_MEMORY_UNOBSERVED_MESSAGES: {
65
+ readonly 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";
66
+ readonly run_result_missing: "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";
67
+ readonly run_result_unobserved: "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";
68
+ readonly 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";
69
+ readonly 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";
70
+ };
71
+ /** 第 7 步的读数:读集(观测所在那一腿 + 层键),或取不到的那一臂(叶子臂在调用点判 —— 它读的是头,不是观测)。 */
72
+ export type SessionReadSet = {
73
+ readonly kind: "read";
74
+ readonly taskId: string;
75
+ readonly scopes: readonly string[];
76
+ } | {
77
+ readonly kind: "unobserved";
78
+ readonly reason: Exclude<SessionMemoryUnobservedReason, "no_leaf">;
79
+ };
80
+ /** 读集 = 最近一条已结腿的记忆观测(四态穷举;引擎加一态 / 本服务加一态 ⇒ 编译红):只有 `mounted` 与 `none` 说得出读集。 */
81
+ export declare function sessionReadSetOf(observed: SettledLegMemoryObservation | undefined): SessionReadSet;
82
+ export declare function handleSessionMemorySync(req: IncomingMessage, res: ServerResponse, match: RouteMatch<SessionMemorySyncRouteId>, ctx: RouteCtx): Promise<void>;
83
+ export declare const SESSION_MEMORY_SYNC_ROUTES: readonly [{
84
+ readonly id: "session-memory-sync";
85
+ readonly pattern: RegExp;
86
+ readonly label: "/v1/sessions/:id/memory/sync";
87
+ readonly methods: readonly ["POST"];
88
+ }];
89
+ /** 本域可分派行的 `id` 闭集 —— handler 的 `switch` 按它判穷尽(漏一口 = 编译红)。 */
90
+ export type SessionMemorySyncRouteId = RouteIdsOf<typeof SESSION_MEMORY_SYNC_ROUTES>;
91
+ //# sourceMappingURL=session-memory-sync.d.ts.map