@sema-agent/server 7.145.0 → 7.146.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (275) hide show
  1. package/MIGRATION.md +492 -0
  2. package/USAGE.md +218 -47
  3. package/deploy/facts/CHANGES.json +25 -0
  4. package/deploy/facts/README.md +57 -0
  5. package/deploy/facts/lanes.json +54 -0
  6. package/deploy/facts/lanes.schema.json +57 -0
  7. package/deploy/facts/sandbox-rbac.json +113 -0
  8. package/deploy/facts/sandbox-rbac.schema.json +74 -0
  9. package/deploy/facts/sealed-key.json +9 -0
  10. package/deploy/facts/sealed-key.schema.json +52 -0
  11. package/deploy/sema-up/chart/templates/_helpers.tpl +19 -0
  12. package/deploy/sema-up/chart/templates/sandbox-rbac.yaml +47 -7
  13. package/deploy/sema-up/chart/templates/server-v4pro.yaml +4 -0
  14. package/deploy/sema-up/chart/templates/server.yaml +4 -0
  15. package/deploy/sema-up/chart/values.yaml +7 -0
  16. package/deploy/sema-up/stack/sema-stack-cluster.yaml +30 -14
  17. package/dist/adoption/import-limits.d.ts +6 -0
  18. package/dist/adoption/import-limits.js +7 -0
  19. package/dist/adoption/import.d.ts +62 -0
  20. package/dist/adoption/import.js +107 -0
  21. package/dist/adoption/wire.d.ts +52 -0
  22. package/dist/adoption/wire.js +9 -0
  23. package/dist/approval-ask-machine.d.ts +2 -2
  24. package/dist/approval-card.d.ts +3 -12
  25. package/dist/approval-card.js +0 -4
  26. package/dist/approval-gate-currency.d.ts +1 -1
  27. package/dist/approval-gate-currency.js +1 -1
  28. package/dist/approval-reconciler.d.ts +1 -1
  29. package/dist/boot/deployment-mods.d.ts +11 -1
  30. package/dist/boot/deployment-mods.js +15 -2
  31. package/dist/boot/device-lane.js +2 -0
  32. package/dist/boot/engine-lease.d.ts +193 -21
  33. package/dist/boot/engine-lease.js +146 -20
  34. package/dist/boot/execution-env.d.ts +4 -0
  35. package/dist/boot/execution-env.js +12 -0
  36. package/dist/boot/exit-boundary.d.ts +50 -0
  37. package/dist/boot/exit-boundary.js +32 -0
  38. package/dist/boot/exit-last.d.ts +20 -4
  39. package/dist/boot/exit-last.js +21 -4
  40. package/dist/boot/governance-seams.js +1 -0
  41. package/dist/boot/memory-consolidation.d.ts +29 -17
  42. package/dist/boot/memory-consolidation.js +26 -15
  43. package/dist/boot/reapers.d.ts +12 -4
  44. package/dist/boot/reapers.js +39 -15
  45. package/dist/boot/resolve-spec.d.ts +2 -0
  46. package/dist/boot/resolve-spec.js +38 -15
  47. package/dist/boot/runner-deps.js +1 -1
  48. package/dist/boot/session-faces.js +3 -1
  49. package/dist/boot/shutdown.js +27 -9
  50. package/dist/boot/stage-01-config.js +1 -1
  51. package/dist/boot/stage-03-store-backend.d.ts +1 -0
  52. package/dist/boot/stage-03-store-backend.js +2 -2
  53. package/dist/boot/stage-04-stores.d.ts +1 -0
  54. package/dist/boot/stage-05-execution-env.d.ts +3 -0
  55. package/dist/boot/stage-05-execution-env.js +3 -3
  56. package/dist/boot/stage-06-runners.d.ts +3 -0
  57. package/dist/boot/stage-06-runners.js +11 -10
  58. package/dist/boot/stage-07-capability-layer.d.ts +3 -0
  59. package/dist/boot/stage-07-capability-layer.js +7 -2
  60. package/dist/boot/stage-08-reapers.d.ts +3 -0
  61. package/dist/boot/stage-08-reapers.js +3 -3
  62. package/dist/boot/stage-09-leader.d.ts +3 -0
  63. package/dist/boot/stage-10-http-server.d.ts +3 -0
  64. package/dist/boot/stage-10-http-server.js +3 -2
  65. package/dist/boot/stores.d.ts +1 -0
  66. package/dist/boot/stores.js +35 -19
  67. package/dist/boot/workflow-orchestration.d.ts +1 -1
  68. package/dist/boot/workflow-orchestration.js +2 -1
  69. package/dist/capabilities/composite-members.d.ts +3 -1
  70. package/dist/capabilities/composite-members.js +3 -1
  71. package/dist/capabilities/scenarios.d.ts +2 -2
  72. package/dist/capability-facts.d.ts +18 -1
  73. package/dist/capability-facts.js +25 -7
  74. package/dist/checkpoint-ctx-seal-m28.d.ts +1 -1
  75. package/dist/config-catalog.js +13 -4
  76. package/dist/config-provider.d.ts +2 -2
  77. package/dist/config-types.d.ts +43 -4
  78. package/dist/config.d.ts +6 -1
  79. package/dist/config.js +67 -11
  80. package/dist/deleted-session-ids.d.ts +19 -0
  81. package/dist/deleted-session-ids.js +26 -0
  82. package/dist/deploy-facts.d.ts +155 -0
  83. package/dist/deploy-facts.js +94 -0
  84. package/dist/device-ws-hub.d.ts +48 -3
  85. package/dist/device-ws-hub.js +48 -2
  86. package/dist/error-code-family.d.ts +35 -0
  87. package/dist/error-code-family.js +26 -0
  88. package/dist/fleet/drain-entry.d.ts +39 -3
  89. package/dist/fleet/drain-entry.js +6 -0
  90. package/dist/fleet/fleet-bus.d.ts +2 -1
  91. package/dist/hooks/branch-transcript.d.ts +1 -1
  92. package/dist/hooks/branch-transcript.js +1 -0
  93. package/dist/hooks/classic-stations.d.ts +25 -8
  94. package/dist/hooks/classic-stations.js +29 -3
  95. package/dist/hooks/hook-runner.d.ts +33 -16
  96. package/dist/hooks/hook-runner.js +166 -73
  97. package/dist/hooks/hook-source.d.ts +14 -2
  98. package/dist/hooks/hook-source.js +8 -0
  99. package/dist/http/active-run-conflict.d.ts +1 -1
  100. package/dist/http/active-run-conflict.js +1 -1
  101. package/dist/http/admission.js +11 -1
  102. package/dist/http/checkpoint-ctx.d.ts +59 -71
  103. package/dist/http/checkpoint-ctx.js +34 -40
  104. package/dist/http/dispatch.js +18 -2
  105. package/dist/http/durable-run-leg.d.ts +5 -0
  106. package/dist/http/durable-run-leg.js +1 -0
  107. package/dist/http/execution-context.d.ts +4 -0
  108. package/dist/http/execution-context.js +9 -0
  109. package/dist/http/health-self-report.d.ts +6 -2
  110. package/dist/http/health-self-report.js +3 -2
  111. package/dist/http/memory-compliance-failure-reply.d.ts +1 -1
  112. package/dist/http/memory-compliance-failure-reply.js +2 -15
  113. package/dist/http/memory-control-plane-corrupt-reply.d.ts +25 -0
  114. package/dist/http/memory-control-plane-corrupt-reply.js +19 -0
  115. package/dist/http/principal-gate.d.ts +35 -12
  116. package/dist/http/principal-gate.js +16 -12
  117. package/dist/http/request-guards.d.ts +1 -1
  118. package/dist/http/request-guards.js +5 -5
  119. package/dist/http/resume-legs.d.ts +22 -0
  120. package/dist/http/resume-legs.js +26 -17
  121. package/dist/http/route-ctx.d.ts +11 -10
  122. package/dist/http/route-table.js +2 -0
  123. package/dist/http/routes/adoption.d.ts +11 -0
  124. package/dist/http/routes/adoption.js +80 -6
  125. package/dist/http/routes/agents-roster.js +1 -0
  126. package/dist/http/routes/approvals-assistant.js +2 -1
  127. package/dist/http/routes/background.js +1 -0
  128. package/dist/http/routes/capabilities.js +4 -1
  129. package/dist/http/routes/diagnostics.d.ts +9 -14
  130. package/dist/http/routes/diagnostics.js +10 -20
  131. package/dist/http/routes/engine.d.ts +31 -0
  132. package/dist/http/routes/engine.js +81 -0
  133. package/dist/http/routes/memory-bundle.js +3 -0
  134. package/dist/http/routes/memory-consolidation.js +3 -5
  135. package/dist/http/routes/memory-origin.d.ts +1 -1
  136. package/dist/http/routes/memory-origin.js +2 -4
  137. package/dist/http/routes/memory-policy.js +5 -1
  138. package/dist/http/routes/observability.js +3 -6
  139. package/dist/http/routes/runs.js +7 -3
  140. package/dist/http/routes/session-sync.js +12 -25
  141. package/dist/http/routes/sessions-list.d.ts +1 -1
  142. package/dist/http/routes/sessions.js +4 -0
  143. package/dist/http/routes/tasks.js +23 -5
  144. package/dist/http/routes/trace-usage.js +1 -1
  145. package/dist/http/run-concurrency-cap.d.ts +86 -0
  146. package/dist/http/run-concurrency-cap.js +54 -0
  147. package/dist/http/server.d.ts +15 -12
  148. package/dist/http/server.js +18 -3
  149. package/dist/http/stored-body-replay.d.ts +9 -6
  150. package/dist/http/stored-body-replay.js +19 -5
  151. package/dist/http/wire-types.d.ts +8 -3
  152. package/dist/json-body-limit.d.ts +8 -0
  153. package/dist/json-body-limit.js +2 -0
  154. package/dist/leader/diffout.d.ts +1 -1
  155. package/dist/local-data-root.d.ts +2 -2
  156. package/dist/memory-operator-faces.d.ts +9 -4
  157. package/dist/memory-scope-stores.d.ts +9 -7
  158. package/dist/memory-scope-stores.js +3 -14
  159. package/dist/memory-scope.d.ts +36 -0
  160. package/dist/memory-scope.js +32 -1
  161. package/dist/net-address.d.ts +53 -0
  162. package/dist/net-address.js +111 -0
  163. package/dist/observability/fail-open.d.ts +14 -10
  164. package/dist/observability/fail-open.js +14 -10
  165. package/dist/orchestration/workflow-notify-journal.js +2 -3
  166. package/dist/org-delivery-gate.js +1 -1
  167. package/dist/peer-directory.d.ts +6 -12
  168. package/dist/peer-directory.js +5 -1
  169. package/dist/plugins/caching-session-store.d.ts +3 -1
  170. package/dist/plugins/caching-session-store.js +3 -0
  171. package/dist/plugins/checkpoint-store-face.d.ts +426 -0
  172. package/dist/plugins/checkpoint-store-face.js +152 -0
  173. package/dist/plugins/file-run-store.d.ts +0 -6
  174. package/dist/plugins/file-run-store.js +1 -12
  175. package/dist/plugins/fork-routing-session-store.d.ts +6 -2
  176. package/dist/plugins/fork-routing-session-store.js +2 -2
  177. package/dist/plugins/imported-layer-root-retirement.d.ts +10 -8
  178. package/dist/plugins/local-checkpoint-store.d.ts +2 -2
  179. package/dist/plugins/local-checkpoint-store.js +2 -1
  180. package/dist/plugins/local-session-store.d.ts +28 -5
  181. package/dist/plugins/local-session-store.js +32 -8
  182. package/dist/plugins/memory-engine-pg.d.ts +29 -6
  183. package/dist/plugins/memory-engine-pg.js +96 -214
  184. package/dist/plugins/memory-engine-sql-apply.d.ts +121 -0
  185. package/dist/plugins/memory-engine-sql-apply.js +358 -0
  186. package/dist/plugins/memory-engine-tidb.d.ts +35 -9
  187. package/dist/plugins/memory-engine-tidb.js +97 -227
  188. package/dist/plugins/memory-key-guards.d.ts +1 -1
  189. package/dist/plugins/memory-origin-law.d.ts +3 -3
  190. package/dist/plugins/one-time-migrations.d.ts +32 -56
  191. package/dist/plugins/one-time-migrations.js +25 -117
  192. package/dist/plugins/permission-rule-store-file.d.ts +17 -3
  193. package/dist/plugins/permission-rule-store-file.js +8 -1
  194. package/dist/plugins/pg-pool.js +1 -1
  195. package/dist/plugins/pg-session-storage.d.ts +17 -4
  196. package/dist/plugins/pg-session-storage.js +8 -4
  197. package/dist/plugins/run-store-sql.js +2 -2
  198. package/dist/plugins/schema-ensure-legs.js +2 -2
  199. package/dist/plugins/session-fork.d.ts +31 -9
  200. package/dist/plugins/session-fork.js +3 -2
  201. package/dist/plugins/session-placement.d.ts +4 -2
  202. package/dist/plugins/sql-json-column.d.ts +5 -1
  203. package/dist/plugins/store-backend.d.ts +11 -8
  204. package/dist/plugins/store-backend.js +8 -7
  205. package/dist/plugins/store-contracts.d.ts +1 -11
  206. package/dist/plugins/store-contracts.js +0 -9
  207. package/dist/plugins/store-sweep.d.ts +63 -0
  208. package/dist/plugins/store-sweep.js +48 -0
  209. package/dist/plugins/tidb-pool.js +1 -1
  210. package/dist/plugins/tidb-session-store.d.ts +16 -3
  211. package/dist/plugins/tidb-session-store.js +8 -4
  212. package/dist/prompt-screen.d.ts +6 -9
  213. package/dist/prompt-screen.js +2 -5
  214. package/dist/rules-consent.d.ts +20 -27
  215. package/dist/rules-consent.js +82 -6
  216. package/dist/run-cancel-context.d.ts +4 -1
  217. package/dist/run-cancel-context.js +2 -1
  218. package/dist/runs.js +1 -1
  219. package/dist/sandbox-egress-policy.js +6 -11
  220. package/dist/sandbox-rbac-rules.d.ts +88 -0
  221. package/dist/sandbox-rbac-rules.js +80 -0
  222. package/dist/sealed-key-layout.d.ts +17 -0
  223. package/dist/sealed-key-layout.js +4 -0
  224. package/dist/sealed-key.js +5 -5
  225. package/dist/security.d.ts +38 -6
  226. package/dist/security.js +9 -2
  227. package/dist/session-notify-door.d.ts +5 -3
  228. package/dist/session-resident-background.d.ts +57 -0
  229. package/dist/session-resident-background.js +20 -0
  230. package/dist/session-sync-kernel.d.ts +53 -14
  231. package/dist/session-sync-kernel.js +70 -1
  232. package/dist/session-sync.d.ts +5 -3
  233. package/dist/session-workspace.d.ts +1 -0
  234. package/dist/session-workspace.js +1 -0
  235. package/dist/sighup-idle.d.ts +9 -7
  236. package/dist/sighup-idle.js +4 -5
  237. package/dist/spec-fields.d.ts +16 -0
  238. package/dist/spec-fields.js +7 -0
  239. package/dist/stored-request-env-credentials-m38.d.ts +3 -0
  240. package/dist/stored-request-env-credentials-m38.js +8 -0
  241. package/dist/stored-request-prompt-profile-m30.d.ts +1 -1
  242. package/dist/{plugins → stores-sql}/checkpoint-store-sql.d.ts +13 -427
  243. package/dist/{plugins → stores-sql}/checkpoint-store-sql.js +33 -194
  244. package/dist/{plugins → stores-sql}/mailbox-store-sql.d.ts +2 -2
  245. package/dist/{plugins → stores-sql}/mailbox-store-sql.js +4 -4
  246. package/dist/{plugins → stores-sql}/roster-store-sql.d.ts +2 -2
  247. package/dist/{plugins → stores-sql}/roster-store-sql.js +1 -1
  248. package/dist/{plugins → stores-sql}/tool-result-store-sql.d.ts +2 -2
  249. package/dist/{plugins → stores-sql}/tool-result-store-sql.js +6 -6
  250. package/dist/{plugins → stores-sql}/workflow-journal-store-sql.d.ts +7 -12
  251. package/dist/{plugins → stores-sql}/workflow-journal-store-sql.js +3 -8
  252. package/dist/task-settings.d.ts +59 -4
  253. package/dist/task-settings.js +50 -4
  254. package/dist/tool-approval.d.ts +2 -2
  255. package/dist/trace/core-keyset-guard.d.ts +8 -7
  256. package/dist/trace/engine-notice-wire.d.ts +1 -1
  257. package/dist/trace/engine-notice-wire.js +2 -0
  258. package/dist/trace/exit-seat.d.ts +6 -0
  259. package/dist/trace/exit-seat.js +1 -0
  260. package/dist/trace/project.js +9 -0
  261. package/dist/trace/redact.d.ts +13 -2
  262. package/dist/trace/redact.js +305 -24
  263. package/dist/trace/self-minted-wire.d.ts +326 -8
  264. package/dist/trace/self-minted-wire.js +125 -7
  265. package/dist/trace/wire-projection-faces.d.ts +1 -1
  266. package/dist/trace/wire-projection-faces.js +1 -1
  267. package/dist/usage-analytics.d.ts +4 -0
  268. package/package.json +12 -4
  269. package/scripts/drain-check-local.mjs +8 -29
  270. package/scripts/drain-check.mysql.sql +3 -24
  271. package/scripts/drain-check.pg.sql +3 -24
  272. package/skills/update-config.md +1 -1
  273. package/wire-changes/server@7.146.0.json +3221 -0
  274. package/dist/approval-batch-census.d.ts +0 -27
  275. package/dist/approval-batch-census.js +0 -27
@@ -1,6 +1,8 @@
1
1
  import { createAdoptionRunner } from "../../adoption/runner.js";
2
2
  import { AdoptionRequestSchema } from "../../adoption/wire.js";
3
3
  import { checkAdoptionPrincipalShape, checkAdoptionWidths } from "../../adoption/plan.js";
4
+ import { judgeAdoptionImportBinding, readAdoptionImportBody, runAdoptionImport } from "../../adoption/import.js";
5
+ import { limitRefusal } from "../../limit-refusal.js";
4
6
  import { sendJson, sendError } from "../send.js";
5
7
  import { gatedPrincipal, explicitOperatorOk, sendOperatorPrincipalsRequired, sendPrincipalRefusal } from "../principal-gate.js";
6
8
  import { assertNever } from "../route-ctx.js";
@@ -39,7 +41,21 @@ function sendOutcome(res, out) {
39
41
  function assertNoRejectCodeLeft(unhandled) {
40
42
  throw new Error(`adoption: unhandled reject code ${JSON.stringify(unhandled)} — the wire contract's closed set and this switch have drifted apart`);
41
43
  }
44
+ function sendAdoptionStoreRequired(res) {
45
+ sendError(res, 501, "capability.adoption_store_required", "adoption requires a SQL store backend (DB_BACKEND=mysql|pg)");
46
+ }
42
47
  export async function handleAdoption(req, res, match, ctx) {
48
+ switch (match.decl.id) {
49
+ case "adoption-submit":
50
+ case "adoption-detail":
51
+ return operatorLane(req, res, match.decl.id, match.params, ctx);
52
+ case "adoption-sessions-import":
53
+ return sourceLane(req, res, match.params, ctx);
54
+ default:
55
+ return assertNever(match.decl.id);
56
+ }
57
+ }
58
+ async function operatorLane(req, res, id, params, ctx) {
43
59
  const { deps } = ctx;
44
60
  const { readJson } = ctx.helpers;
45
61
  const principal = gatedPrincipal(req, deps.config);
@@ -59,7 +75,7 @@ export async function handleAdoption(req, res, match, ctx) {
59
75
  if (!fact.available) {
60
76
  switch (fact.why) {
61
77
  case "adoption_store_absent":
62
- sendError(res, 501, "capability.adoption_store_required", "adoption requires a SQL store backend (DB_BACKEND=mysql|pg)");
78
+ sendAdoptionStoreRequired(res);
63
79
  return;
64
80
  case "operator_principals_empty":
65
81
  sendOperatorPrincipalsRequired(res);
@@ -70,14 +86,14 @@ export async function handleAdoption(req, res, match, ctx) {
70
86
  }
71
87
  const store = fact.backend.adoptionLog();
72
88
  const runner = createAdoptionRunner({ store, dialect: deps.backend?.kind === "pg" ? "pg" : "mysql", now: Date.now, logger: deps.logger });
73
- switch (match.decl.id) {
89
+ switch (id) {
74
90
  case "adoption-detail": {
75
- const id = ctx.helpers.safeDecode(match.params.id);
76
- if (id === null) {
91
+ const adoptionId = ctx.helpers.safeDecode(params.id);
92
+ if (adoptionId === null) {
77
93
  sendError(res, 400, "request.id_invalid", "invalid adoption id");
78
94
  return;
79
95
  }
80
- const out = await runner.get(id);
96
+ const out = await runner.get(adoptionId);
81
97
  if (!out) {
82
98
  sendError(res, 404, "not_found.adoption", "adoption not found");
83
99
  return;
@@ -112,11 +128,69 @@ export async function handleAdoption(req, res, match, ctx) {
112
128
  return;
113
129
  }
114
130
  default:
115
- return assertNever(match.decl.id);
131
+ return assertNever(id);
132
+ }
133
+ }
134
+ async function sourceLane(req, res, params, ctx) {
135
+ const { deps } = ctx;
136
+ if (ctx.helpers.rateLimited(req, res))
137
+ return;
138
+ const principal = gatedPrincipal(req, deps.config);
139
+ if (principal === undefined) {
140
+ sendPrincipalRefusal(res, req, deps.config);
141
+ return;
142
+ }
143
+ const face = facts.adoptionSessionImport(deps);
144
+ if (!face.available) {
145
+ switch (face.why) {
146
+ case "adoption_store_absent":
147
+ sendAdoptionStoreRequired(res);
148
+ return;
149
+ case "session_import_staging_absent":
150
+ sendError(res, 501, "capability.session_store_required", "importing sessions requires a durable session store (DB_BACKEND=mysql|pg|local)");
151
+ return;
152
+ default:
153
+ return assertNever(face.why);
154
+ }
155
+ }
156
+ const adoptionId = ctx.helpers.safeDecode(params.id);
157
+ if (adoptionId === null) {
158
+ sendError(res, 400, "request.id_invalid", "invalid adoption id");
159
+ return;
160
+ }
161
+ const binding = judgeAdoptionImportBinding(await face.backend.adoptionLog().getById(adoptionId), principal);
162
+ switch (binding.kind) {
163
+ case "absent":
164
+ sendError(res, 404, "not_found.adoption", "adoption not found");
165
+ return;
166
+ case "not_source":
167
+ sendError(res, 403, "auth.adoption_source_only", "only this adoption's source principal may import sessions into it (verbatim; an operator cannot import on its behalf)");
168
+ return;
169
+ case "not_adopted":
170
+ sendError(res, 409, "adoption.not_adopted", "this adoption has not completed (it is still in flight, or it was rejected); sessions can be imported once the operator's adoption reaches status adopted");
171
+ return;
172
+ case "bound":
173
+ break;
174
+ default:
175
+ return assertNever(binding);
176
+ }
177
+ const sessions = readAdoptionImportBody(await ctx.helpers.readJson(req));
178
+ if (sessions === undefined) {
179
+ sendError(res, 400, "request.body_shape", "invalid import body — expected exactly {sessions: [...]} (the destination is the adoption's; the body cannot name one)");
180
+ return;
181
+ }
182
+ const cap = deps.config.adoptionImportMaxSessions;
183
+ if (sessions.length > cap) {
184
+ const refusal = limitRefusal("sessions", "entries", sessions.length, cap, { tail: "split the batch (the current cap is capabilities.adoptionImport.maxSessions), or have the operator raise ADOPTION_IMPORT_MAX_SESSIONS" });
185
+ sendError(res, 400, "request.field_invalid", refusal.message, refusal.facts);
186
+ return;
116
187
  }
188
+ const results = await runAdoptionImport({ adoptionId: binding.adoptionId, toPrincipal: binding.toPrincipal, maxSessionBytes: deps.config.adoptionImportMaxSessionBytes, sessionStorage: face.sessionStorage }, sessions);
189
+ sendJson(res, 200, { results });
117
190
  }
118
191
  export const ADOPTION_ROUTES = [
119
192
  { id: "adoption-submit", path: "/v1/adoption", credentialGated: ["POST"], methods: ["POST"] },
120
193
  { id: "adoption-detail", pattern: /^\/v1\/adoption\/(?<id>[^/]+)$/, label: "/v1/adoption/:id", methods: ["GET"] },
194
+ { id: "adoption-sessions-import", pattern: /^\/v1\/adoption\/(?<id>[^/]+)\/sessions$/, label: "/v1/adoption/:id/sessions", credentialGated: ["POST"], methods: ["POST"] },
121
195
  ];
122
196
  //# sourceMappingURL=adoption.js.map
@@ -20,6 +20,7 @@ const ROSTER_KEY_DISPOSITION = {
20
20
  parentSessionId: "strip",
21
21
  rootSessionId: "strip",
22
22
  usage: "strip",
23
+ requestedBy: "strip",
23
24
  };
24
25
  void ROSTER_KEY_DISPOSITION;
25
26
  export function rosterAgentId(row) {
@@ -3,7 +3,8 @@ import { skillCardFacts } from "../../approval-card.js";
3
3
  import { hostDecisionFacts, readDecider, retiredDecisionKeyFacts, DECIDED_BY_NOT_IN_PROOF } from "../../host-decision.js";
4
4
  import { sameExecutionRecord } from "@sema-agent/core";
5
5
  import { findParkedAgentForCheckpoint } from "../../parked-decide.js";
6
- import { DECIDED_REPLAY_SCAN_CAP, approvalPayloadFingerprint } from "../../plugins/checkpoint-store-sql.js";
6
+ import { DECIDED_REPLAY_SCAN_CAP } from "../../plugins/checkpoint-store-face.js";
7
+ import { approvalPayloadFingerprint } from "../../plugins/approval-payload-fingerprint.js";
7
8
  import { carriesDeploymentListQuestion, standingAnswerDurablyRecordable } from "../../tool-approval.js";
8
9
  import { APPROVAL_GATE_KINDS } from "../../approval-content-kind.js";
9
10
  import { isParkedRunStatus } from "../../plugins/store-contracts.js";
@@ -23,6 +23,7 @@ const ROW_KEY_DISPOSITION = {
23
23
  toolUseId: "strip",
24
24
  updatedAt: "strip",
25
25
  sessionId: "keep",
26
+ requestedBy: "strip",
26
27
  };
27
28
  void ROW_KEY_DISPOSITION;
28
29
  function projectRow(r) {
@@ -35,6 +35,7 @@ import * as facts from "../../capability-facts.js";
35
35
  import { judgeAutoModeArming } from "../../auto-mode-face.js";
36
36
  import { coercePermissionModeStrict, PERMISSION_MODE_WORDS, taskHooksHonored } from "../../task-settings.js";
37
37
  import { gatedPrincipal, sendPrincipalRefusal } from "../principal-gate.js";
38
+ import { readAdoptionImportCapability } from "../../adoption/import.js";
38
39
  import { headReadOf } from "../../session-incarnation.js";
39
40
  import { callerTrust } from "../caller-trust.js";
40
41
  import { sessionRulesNamespaceFormsHonored } from "../../session-policy-wire.js";
@@ -121,6 +122,7 @@ export async function handleCapabilities(req, res, match, ctx) {
121
122
  sharedMemory: facts.sharedMemoryRead(deps).available,
122
123
  principalInboxDelivery: facts.principalInboxDelivery(deps).available,
123
124
  adoption: facts.adoption(deps).available,
125
+ adoptionImport: await readAdoptionImportCapability(facts.adoptionSessionImport(deps), gatedPrincipal(req, deps.config), deps.config),
124
126
  memoryBundle: facts.memoryBundle(deps).available,
125
127
  memoryCompliance: facts.memoryCompliance(deps).available,
126
128
  memoryOrigin: facts.memoryOrigin(deps).available,
@@ -147,7 +149,7 @@ export async function handleCapabilities(req, res, match, ctx) {
147
149
  scratchpadDir: true,
148
150
  subagentSteer: facts.subagentSteer(deps).available,
149
151
  subagentResume: facts.subagentSteer(deps).available,
150
- taskSettings: { permissions: true, permissionMode: true, model: true, outputStyle: true, env: DEPLOYMENT_SHAPE_PREDICATES.callerCwd.holds(deps.config), hooks: taskHooksHonored(deps.config), pluginHooks: taskHooksHonored(deps.config), managedHookGroups: taskHooksHonored(deps.config), httpHookAllowlists: taskHooksHonored(deps.config), tightenOnlySeats: true, approvalFrameFamilies: true },
152
+ taskSettings: { permissions: true, permissionMode: true, model: true, outputStyle: true, env: DEPLOYMENT_SHAPE_PREDICATES.callerCwd.holds(deps.config), hooks: taskHooksHonored(deps.config), pluginHooks: taskHooksHonored(deps.config), managedHookGroups: taskHooksHonored(deps.config), httpHookAllowlists: taskHooksHonored(deps.config), tightenOnlySeats: true, approvalFrameFamilies: true, autoCompactEnabled: true },
151
153
  settings: { permissions: { allowGrant: true, allowLayer: true, layers: true, layeredAllow: true, callerTrust: callerTrust(req, deps.config).trust } },
152
154
  permissionModeAuto: {
153
155
  accepted: true,
@@ -187,6 +189,7 @@ export async function handleCapabilities(req, res, match, ctx) {
187
189
  mcpProbe: true,
188
190
  mcpAssertionHeaderSent: MCP_ASSERTION_HEADER_SENT,
189
191
  mcpTransportKinds: [...MCP_REQUEST_TRANSPORT_KINDS],
192
+ engine: { handover: facts.engineHandover(deps).available },
190
193
  mcpElicitation: facts.elicitation(deps).available,
191
194
  askUserQuestion: facts.askUserQuestion(deps).available,
192
195
  toolApproval: facts.toolApproval(deps).available,
@@ -13,6 +13,7 @@ import { type PostureKnobReading, type PostureSource } from "../../posture-sourc
13
13
  import type { IncomingMessage, ServerResponse } from "node:http";
14
14
  import type { WiringManifest } from "@sema-agent/core";
15
15
  import { type StreamApprovalGate, type StreamApprovalGateInput } from "../../tool-approval.js";
16
+ import { verifiedIdentitySource } from "../principal-gate.js";
16
17
  import type { RouteCtx, RouteMatch, RouteIdsOf } from "../route-ctx.js";
17
18
  /** 流内审批门的读数形:上场即 `"active"`,否则是那**四**个合取项里**第一个**不满足的原因词
18
19
  * (与 `resolveStreamApprovalGate` 同一闭集,词表**取型**自它 ⇒ 增删 reason 在此处自动跟随/编译红;
@@ -82,23 +83,17 @@ export declare function assertStaticWiringConsistent(facts: StaticWiringFacts, l
82
83
  warn: (msg: string, meta?: Record<string, unknown>) => void;
83
84
  } | undefined): StaticWiringAudit;
84
85
  /**
85
- * [ref]③ clay 裁 (b)(2026-08-12,core [ref]④ + cli [ref]① 双背书):**零凭证 turnkey 形**的
86
- * loopback 来源门判据(单一属主,端点与测试同吃)。
86
+ * 诊断面的**远程来源门**(单一属主,端点与测试同吃;[ref]③ clay 裁 (b) 的那只门,7.63.0 起按身份判):远程请求后面没有
87
+ * **验过的身份**(`verifiedIdentitySource`:service 凭据 / 鉴权桥登录令牌 / 直连门签名令牌)⇒ 拒;本机(loopback)照读。
87
88
  *
88
- * 只在「operator 名单空 ∧ 非多租户 ∧ 全局 service-credential 门也不在场(没配 SERVICE_AUTH_TOKEN /
89
- * AUTH_TOKENS)」的部署形上判——那正是全局门整个不设、诊断端点此前无认证可读的形。诊断面泄的是装配
90
- * 拓扑(store 接线/能力位/治理姿态),对误暴露公网端口的 turnkey 机器构成侦察面。(b) 对本机自用零摩擦
91
- * (壳缺省钉 BIND_HOST=127.0.0.1,local curl 照常),对远程侦察 fail-closed;配了 token 的部署远程读
92
- * 走认证(全局门),不进本臂。
89
+ * 名单里的名字、单用户声明放进来的「每个调用方」,都只在这次请求带可验身份时才是身份 —— 没有凭据可验的部署上,主体头是谁都能写的
90
+ * 字符串,端口误暴露到公网就是侦察面(装配拓扑 / 治理姿态 / 写保护名表)。配了 service 凭据的部署上,到得了这里的请求都过了全局凭据门、
91
+ * 必然带着验过的身份,本判不咬;不要求静态凭据在场(鉴权桥的登录令牌就是身份)。
93
92
  *
94
- * 判源=TCP 对端地址(`req.socket.remoteAddress`),**非任何头**——XFF 类头在无反代的 turnkey 形上本就
95
- * 是自造面;有反代的部署必然配 token(反代后 remoteAddress 恒 loopback,门自然放行,语义仍对:该形的
96
- * 边界由反代+token 承担)。`remoteAddress` 缺席(socket 已断等边缘)⇒ 判拒(fail-closed,安全轴方向)。
93
+ * 判源 = TCP 对端地址(`req.socket.remoteAddress`),**非任何头**(XFF 类头在无反代形上是自造面;有反代的部署必然配凭据)。
94
+ * `remoteAddress` 缺席(socket 已断等边缘)⇒ 判拒(fail-closed)。
97
95
  */
98
- export declare function zeroCredentialLoopbackDenied(remoteAddress: string | undefined, config: {
99
- authToken?: string;
100
- authTokens?: Record<string, string>;
101
- }): boolean;
96
+ export declare function remoteWithoutVerifiedIdentity(req: IncomingMessage, config: Parameters<typeof verifiedIdentitySource>[1]): boolean;
102
97
  export declare function handleDiagnostics(req: IncomingMessage, res: ServerResponse, match: RouteMatch<DiagnosticsRouteId>, ctx: RouteCtx): Promise<void>;
103
98
  export declare const DIAGNOSTICS_ROUTES: readonly [{
104
99
  readonly id: "diagnostics-wiring";
@@ -95,32 +95,22 @@ export function assertStaticWiringConsistent(facts, logger) {
95
95
  }
96
96
  return audit;
97
97
  }
98
- export function zeroCredentialLoopbackDenied(remoteAddress, config) {
99
- const anyServiceAuth = Boolean(config.authToken) || Object.keys(config.authTokens ?? {}).length > 0;
100
- if (anyServiceAuth)
101
- return false;
102
- return !isLoopbackAddress(remoteAddress);
98
+ export function remoteWithoutVerifiedIdentity(req, config) {
99
+ return !isLoopbackAddress(req.socket.remoteAddress) && verifiedIdentitySource(req, config) === undefined;
103
100
  }
104
101
  export async function handleDiagnostics(req, res, match, ctx) {
105
102
  const { deps } = ctx;
106
103
  switch (match.decl.id) {
107
104
  case "diagnostics-wiring": {
108
- if (deps.config.operatorPrincipals.length > 0 || deps.config.requirePrincipal) {
109
- if (!facts.operatorLane(deps).available) {
110
- sendOperatorPrincipalsRequired(res);
111
- return;
112
- }
113
- const principal = gatedPrincipal(req, deps.config);
114
- if (!explicitOperatorOk(principal, deps.config)) {
115
- sendError(res, 403, "auth.operator_only", "wiring diagnostics are operator-only on a multi-tenant worker");
116
- return;
117
- }
118
- if (!isLoopbackAddress(req.socket.remoteAddress) && verifiedIdentitySource(req, deps.config) === undefined) {
119
- sendError(res, 403, "auth.loopback_only", "wiring diagnostics on a zero-credential deployment are readable from loopback only — configure SERVICE_AUTH_TOKEN to read remotely");
120
- return;
121
- }
105
+ if (!facts.operatorLane(deps).available) {
106
+ sendOperatorPrincipalsRequired(res);
107
+ return;
108
+ }
109
+ if (!explicitOperatorOk(gatedPrincipal(req, deps.config), deps.config)) {
110
+ sendError(res, 403, "auth.operator_only", "wiring diagnostics are operator-only");
111
+ return;
122
112
  }
123
- else if (zeroCredentialLoopbackDenied(req.socket.remoteAddress, deps.config)) {
113
+ if (remoteWithoutVerifiedIdentity(req, deps.config)) {
124
114
  sendError(res, 403, "auth.loopback_only", "wiring diagnostics on a zero-credential deployment are readable from loopback only — configure SERVICE_AUTH_TOKEN to read remotely");
125
115
  return;
126
116
  }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * `POST /v1/engine/handover` · `POST /v1/engine/attach` —— 附着租约的交接车道([ref] v3,「Move to background and exit」)。
3
+ *
4
+ * 一只门的两扇口:交接口置交接位(壳随后自己退出、不 SIGTERM 引擎;后台 shell 在跑时引擎不退),attach 口清位(下一只壳收回)。车道、窗、
5
+ * 工作谓词全在租约腿(`boot/engine-lease.ts`);本域只读写**交接门**(`drainState.engineHandover`,收尾链装配时进盒子)。
6
+ * · 门序:身份(多租户形的主体门,与运维口同一只铸点)→ 能力事实 `engineHandover`(与 `capabilities.engine.handover` 同读;不可用 ⇒ 501
7
+ * `capability.engine_handover_unavailable`,按 why 两句)→ 验型(体坏 ⇒ 400,零状态变化)→ 写门。billable=false,**不吃排空 503**
8
+ * (排空一开始租约腿在它的下一拍收摊,能力事实随即答 501;那一拍之内的交接 / attach 无害 —— 引擎已在退)。
9
+ * · 交接体只收 `{ reason: "background_exit" }`(闭集);attach 体只收空对象或空体(字面 `null` 与空体同读 —— 共用读体器的全服务律)。
10
+ * 非对象体 ⇒ 400 `request.invalid_json`(`not_object`)。
11
+ * · 能力事实读两次、是同一只函数:读体之前读一次(决定 501 先于 400),验完体、写门之前**无 await** 再读一次 —— 读体途中腿停了(停机开始)
12
+ * 就答 501,不拿读体之前的旧事实去写一只已不在观察的门(codex r1 [medium])。
13
+ * · 两口幂等:再次交接回同一个 `since`;不在交接车道时 attach 照回此刻车道(`handedOverForMs` 缺席)。
14
+ */
15
+ import type { IncomingMessage, ServerResponse } from "node:http";
16
+ import type { RouteCtx, RouteMatch, RouteIdsOf } from "../route-ctx.js";
17
+ export declare const ENGINE_HANDOVER_PATH = "/v1/engine/handover";
18
+ export declare const ENGINE_ATTACH_PATH = "/v1/engine/attach";
19
+ export declare function handleEngine(req: IncomingMessage, res: ServerResponse, match: RouteMatch<EngineRouteId>, ctx: RouteCtx): Promise<void>;
20
+ export declare const ENGINE_ROUTES: readonly [{
21
+ readonly id: "engine-handover";
22
+ readonly path: "/v1/engine/handover";
23
+ readonly methods: readonly ["POST"];
24
+ }, {
25
+ readonly id: "engine-attach";
26
+ readonly path: "/v1/engine/attach";
27
+ readonly methods: readonly ["POST"];
28
+ }];
29
+ type EngineRouteId = RouteIdsOf<typeof ENGINE_ROUTES>;
30
+ export {};
31
+ //# sourceMappingURL=engine.d.ts.map
@@ -0,0 +1,81 @@
1
+ import { sendJson, sendError, sendHttpError, invalidJsonError } from "../send.js";
2
+ import { gatedPrincipal, sendPrincipalRefusal } from "../principal-gate.js";
3
+ import * as facts from "../../capability-facts.js";
4
+ import { HANDOVER_REASONS } from "../../boot/engine-lease.js";
5
+ import { assertNever } from "../route-ctx.js";
6
+ export const ENGINE_HANDOVER_PATH = "/v1/engine/handover";
7
+ export const ENGINE_ATTACH_PATH = "/v1/engine/attach";
8
+ const HANDOVER_REASONS_SPELLED = HANDOVER_REASONS.map((r) => `"${r}"`).join(", ");
9
+ const ATTACH_BODY_MESSAGE = "the attach request takes no fields (send {} or an empty body) — nothing was changed";
10
+ function isPlainObject(v) {
11
+ return typeof v === "object" && v !== null && !Array.isArray(v);
12
+ }
13
+ function readHandoverReason(body) {
14
+ const keys = Object.keys(body);
15
+ if (keys.length !== 1 || keys[0] !== "reason")
16
+ return undefined;
17
+ const reason = body.reason;
18
+ return HANDOVER_REASONS.find((r) => r === reason);
19
+ }
20
+ function sendHandoverUnavailable(res, why) {
21
+ switch (why) {
22
+ case "engine_handover_lane_absent":
23
+ sendError(res, 501, "capability.engine_handover_unavailable", "handover is not available on this engine: its attachment lease is not armed (neither ENGINE_AUTO_EXIT nor SEMA_PARENT_PID), or the deployment is multi-tenant (REQUIRE_PRINCIPAL=true) — GET /v1/capabilities reports engine.handover=false; exit and stop tasks instead");
24
+ return;
25
+ case "engine_lease_stopped":
26
+ sendError(res, 501, "capability.engine_handover_unavailable", "this engine is shutting down: its attachment lease has stopped observing, so there is nothing to hand over or attach to");
27
+ return;
28
+ default:
29
+ return assertNever(why);
30
+ }
31
+ }
32
+ export async function handleEngine(req, res, match, ctx) {
33
+ const { deps } = ctx;
34
+ if (deps.config.requirePrincipal && gatedPrincipal(req, deps.config) === undefined) {
35
+ sendPrincipalRefusal(res, req, deps.config);
36
+ return;
37
+ }
38
+ const before = facts.engineHandover(deps);
39
+ if (!before.available) {
40
+ sendHandoverUnavailable(res, before.why);
41
+ return;
42
+ }
43
+ const body = await ctx.helpers.readJson(req);
44
+ if (!isPlainObject(body)) {
45
+ sendHttpError(res, invalidJsonError("not_object"));
46
+ return;
47
+ }
48
+ let write;
49
+ switch (match.decl.id) {
50
+ case "engine-handover": {
51
+ const reason = readHandoverReason(body);
52
+ if (reason === undefined) {
53
+ sendError(res, 400, "request.field_invalid", `the handover request must be exactly { reason } with reason one of ${HANDOVER_REASONS_SPELLED} — nothing was changed`);
54
+ return;
55
+ }
56
+ write = (door) => door.enter(reason);
57
+ break;
58
+ }
59
+ case "engine-attach": {
60
+ if (Object.keys(body).length > 0) {
61
+ sendError(res, 400, "request.field_invalid", ATTACH_BODY_MESSAGE);
62
+ return;
63
+ }
64
+ write = (door) => door.attach();
65
+ break;
66
+ }
67
+ default:
68
+ return assertNever(match.decl.id);
69
+ }
70
+ const now = facts.engineHandover(deps);
71
+ if (!now.available) {
72
+ sendHandoverUnavailable(res, now.why);
73
+ return;
74
+ }
75
+ sendJson(res, 200, write(now.door));
76
+ }
77
+ export const ENGINE_ROUTES = [
78
+ { id: "engine-handover", path: ENGINE_HANDOVER_PATH, methods: ["POST"] },
79
+ { id: "engine-attach", path: ENGINE_ATTACH_PATH, methods: ["POST"] },
80
+ ];
81
+ //# sourceMappingURL=engine.js.map
@@ -1,6 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import { sendJson, sendError } from "../send.js";
3
3
  import { sentMemoryStoreRefusal } from "../memory-store-refusal-reply.js";
4
+ import { sentControlPlaneCorrupt } from "../memory-control-plane-corrupt-reply.js";
4
5
  import { buildOriginFencedBundle, readOriginAware } from "../../memory-origin-fence.js";
5
6
  import { gatedPrincipal, explicitOperatorOk, sendOperatorPrincipalsRequired, sendPrincipalRefusal } from "../principal-gate.js";
6
7
  import { assertNever } from "../route-ctx.js";
@@ -42,6 +43,8 @@ function sentClassifiedFailure(res, ctx, face, err) {
42
43
  return true;
43
44
  if (sentMemoryStoreRefusal(res, err, ctx.deps.logger))
44
45
  return true;
46
+ if (sentControlPlaneCorrupt(res, err, { kind: "operator" }))
47
+ return true;
45
48
  ctx.deps.logger?.warn?.(`memory_bundle_${face}_failed`, { err: String(err) });
46
49
  return false;
47
50
  }
@@ -1,7 +1,7 @@
1
1
  import { z } from "zod";
2
- import { ControlPlaneCorruptError } from "@sema-agent/core";
3
2
  import { sendJson, sendError } from "../send.js";
4
3
  import { sentMemoryLayerRefusal } from "../memory-layer-reply.js";
4
+ import { sentControlPlaneCorrupt } from "../memory-control-plane-corrupt-reply.js";
5
5
  import { gatedPrincipal, explicitOperatorOk, sendPrincipalRefusal, sendOperatorPrincipalsRequired } from "../principal-gate.js";
6
6
  import { assertNever } from "../route-ctx.js";
7
7
  import * as facts from "../../capability-facts.js";
@@ -104,7 +104,7 @@ export async function handleMemoryConsolidation(req, res, match, ctx) {
104
104
  const scope = "scope" in decided ? decided.scope : undefined;
105
105
  let row;
106
106
  try {
107
- row = scope !== undefined ? faces.lastRun(scope) : undefined;
107
+ row = scope !== undefined ? await faces.lastRun(scope) : undefined;
108
108
  }
109
109
  catch (err) {
110
110
  if (sentMemoryLayerRefusal(res, err, { kind: "operator" }))
@@ -160,10 +160,8 @@ export async function handleMemoryConsolidation(req, res, match, ctx) {
160
160
  sendError(res, 409, "memory.consolidation_refused", `memory consolidation refused (${coreCode}): ${err instanceof Error ? err.message : String(err)}`);
161
161
  return;
162
162
  }
163
- if (err instanceof ControlPlaneCorruptError) {
164
- sendError(res, 500, "internal.memory_control_plane_corrupt", err.message);
163
+ if (sentControlPlaneCorrupt(res, err, { kind: "operator" }))
165
164
  return;
166
- }
167
165
  ctx.deps.logger?.warn?.("memory_consolidation_run_failed", { scope: decided.scope, err: String(err) });
168
166
  sendError(res, 500, "internal.error", "memory consolidation run failed");
169
167
  }
@@ -78,7 +78,7 @@
78
78
  * 分层:本模块不值 import `server.ts`(那条边闭合运行时装载环),只 `import type`。
79
79
  */
80
80
  import type { IncomingMessage, ServerResponse } from "node:http";
81
- import { type OriginClearanceRow } from "@sema-agent/core";
81
+ import type { OriginClearanceRow } from "@sema-agent/core";
82
82
  import type { RouteCtx, RouteMatch, RouteIdsOf } from "../route-ctx.js";
83
83
  export declare const MEMORY_ORIGIN_EXTERNAL_PATH = "/v1/memory/origin/external";
84
84
  export declare const MEMORY_ORIGIN_CLEARANCES_PATH = "/v1/memory/origin/clearances";
@@ -1,7 +1,7 @@
1
1
  import { z } from "zod";
2
- import { ControlPlaneCorruptError } from "@sema-agent/core";
3
2
  import { sendJson, sendError } from "../send.js";
4
3
  import { sentMemoryStoreRefusal } from "../memory-store-refusal-reply.js";
4
+ import { sentControlPlaneCorrupt } from "../memory-control-plane-corrupt-reply.js";
5
5
  import { buildOriginFence, readOriginAware } from "../../memory-origin-fence.js";
6
6
  import { readLayerSeat, sentMemoryLayerRefusal } from "../memory-layer-reply.js";
7
7
  import { gatedPrincipal, explicitOperatorOk, sendOperatorPrincipalsRequired, sendPrincipalRefusal } from "../principal-gate.js";
@@ -59,10 +59,8 @@ function sentClassifiedFailure(res, ctx, face, err) {
59
59
  }
60
60
  if (face !== "clearances" && sentMemoryStoreRefusal(res, err, ctx.deps.logger))
61
61
  return true;
62
- if (err instanceof ControlPlaneCorruptError) {
63
- sendError(res, 500, "internal.memory_control_plane_corrupt", err.message);
62
+ if (sentControlPlaneCorrupt(res, err, { kind: "operator" }))
64
63
  return true;
65
- }
66
64
  ctx.deps.logger?.warn?.(`memory_origin_${face}_failed`, { err: String(err) });
67
65
  return false;
68
66
  }
@@ -1,4 +1,4 @@
1
- import { formatUserScope } from "@sema-agent/core";
1
+ import { formatUserScope, isReplicableScope } from "@sema-agent/core";
2
2
  import { parseMemorySyncRequest } from "../../memory-sync.js";
3
3
  import { memorySyncIdentityRule } from "../../memory-sync-identity.js";
4
4
  import * as facts from "../../capability-facts.js";
@@ -160,6 +160,10 @@ export async function handleMemoryPolicy(req, res, match, ctx) {
160
160
  sendError(res, 400, "memory_sync_invalid_scope", "missing :scope");
161
161
  return;
162
162
  }
163
+ if (!isReplicableScope(scope)) {
164
+ sendError(res, 400, "memory_sync_invalid_scope", "a userlocal: memory layer never leaves the store it is written in — it is not synced");
165
+ return;
166
+ }
163
167
  if (sendScopeGateRefusal(res, await gateMemoryScope(deps, principal, scope, "write")))
164
168
  return;
165
169
  const body = await readJson(req);
@@ -12,12 +12,9 @@ export async function handleObservability(req, res, match, ctx) {
12
12
  sendOperatorPrincipalsRequired(res);
13
13
  return;
14
14
  }
15
- if (deps.config.requirePrincipal) {
16
- const principal = gatedPrincipal(req, deps.config);
17
- if (!explicitOperatorOk(principal, deps.config)) {
18
- sendError(res, 403, "auth.operator_only", "outcomes view is operator-only on a multi-tenant worker");
19
- return;
20
- }
15
+ if (!explicitOperatorOk(gatedPrincipal(req, deps.config), deps.config)) {
16
+ sendError(res, 403, "auth.operator_only", "the outcomes view is operator-only");
17
+ return;
21
18
  }
22
19
  if (!fact.available) {
23
20
  switch (fact.why) {
@@ -4,7 +4,7 @@ import { z } from "zod";
4
4
  import { uuidv7, mintCheckpointToken, CheckpointError, policyCannotAnswer, validatePendingSteer, TASK_OUTPUT_REPLAY_KINDS, isSystemInjectionPriority } from "@sema-agent/core";
5
5
  import { isTerminalRunStatus, isParkedRunStatus, runProvenElsewhere } from "../../plugins/store-contracts.js";
6
6
  import { askOwnerAdmits, askRowCarriesOwner } from "../../plugins/approval-ask-store-sql.js";
7
- import { LIVE_RUN_GATE_KINDS, TERMINAL_PARK_GATE_KINDS } from "../../plugins/checkpoint-store-sql.js";
7
+ import { LIVE_RUN_GATE_KINDS, TERMINAL_PARK_GATE_KINDS } from "../../plugins/checkpoint-store-face.js";
8
8
  import { redactSecrets } from "../../trace/redact.js";
9
9
  import { isUuidV7, encodeCheckpointScope } from "../../security.js";
10
10
  import { redactSteerIn, STEER_IN_MAX_CHARS, STEER_IN_MAX_REQUEST_CHARS } from "../../orchestration/workflow-agent-steer.js";
@@ -1578,7 +1578,7 @@ export async function handleRunVerbs(req, res, match, ctx) {
1578
1578
  export async function createDurableRun(req, res, ctx, prepared, idem) {
1579
1579
  const { deps } = ctx;
1580
1580
  const { idemCache } = ctx.registry;
1581
- const { leaseDenied, usageWindowDenied } = ctx.helpers;
1581
+ const { leaseDenied, usageWindowDenied, admitRun } = ctx.helpers;
1582
1582
  const source = ctx.req.source;
1583
1583
  const runs = facts.durableRuns(deps);
1584
1584
  if (!runs.available) {
@@ -1600,6 +1600,9 @@ export async function createDurableRun(req, res, ctx, prepared, idem) {
1600
1600
  }
1601
1601
  if ((await leaseDenied(req, res)) || (await usageWindowDenied(req, res)))
1602
1602
  return null;
1603
+ const runSlot = admitRun(req, res);
1604
+ if (runSlot === undefined)
1605
+ return null;
1603
1606
  return await idemCache.run(idem !== undefined ? inflightKeyOf(idem, "durable", clientTaskId) : undefined, async () => {
1604
1607
  const out = await mintAndDriveDurableRun({ deps, registry: ctx.registry, helpers: ctx.helpers, local: ctx.local, legs: ctx.legs }, runStore, {
1605
1608
  prepared,
@@ -1609,9 +1612,10 @@ export async function createDurableRun(req, res, ctx, prepared, idem) {
1609
1612
  elicitOwner: submitter ?? null,
1610
1613
  source,
1611
1614
  identity: runIdentityOf(req, deps.config, prepared.auth?.principal ?? null),
1615
+ runSlot,
1612
1616
  });
1613
1617
  return out.existing ? await replayOfExistingRun(out.existing, caller, "durable", deps, runStore) : out;
1614
- });
1618
+ }).finally(() => runSlot.release());
1615
1619
  }
1616
1620
  function clientTaskIdOf(body) {
1617
1621
  const raw = typeof body === "object" && body !== null ? body.taskId : undefined;