@sema-agent/server 7.130.0 → 7.132.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 (294) hide show
  1. package/MIGRATION.md +396 -0
  2. package/README.md +13 -6
  3. package/README.zh-CN.md +9 -5
  4. package/USAGE.md +187 -50
  5. package/dist/approval-card.d.ts +3 -1
  6. package/dist/approval-card.js +2 -3
  7. package/dist/audit.d.ts +7 -10
  8. package/dist/audit.js +4 -1
  9. package/dist/auth-bridge.d.ts +4 -0
  10. package/dist/auth-bridge.js +10 -11
  11. package/dist/bench/s1/live-deps.js +2 -0
  12. package/dist/boot/deployment-mods.d.ts +31 -0
  13. package/dist/boot/deployment-mods.js +26 -0
  14. package/dist/boot/execution-env.d.ts +1 -1
  15. package/dist/boot/execution-env.js +19 -6
  16. package/dist/boot/memory-boundary.d.ts +2 -2
  17. package/dist/boot/read-deny-anchor-audit.d.ts +17 -0
  18. package/dist/boot/read-deny-anchor-audit.js +36 -0
  19. package/dist/boot/resolve-spec.d.ts +1 -5
  20. package/dist/boot/resolve-spec.js +56 -61
  21. package/dist/boot/runner-deps.d.ts +19 -8
  22. package/dist/boot/runner-deps.js +14 -2
  23. package/dist/boot/session-faces.d.ts +1 -7
  24. package/dist/boot/stage-02-web-admin-pull.js +9 -1
  25. package/dist/boot/stage-03-store-backend.d.ts +1 -1
  26. package/dist/boot/stage-03-store-backend.js +2 -2
  27. package/dist/boot/stage-04-stores.d.ts +1 -1
  28. package/dist/boot/stage-05-execution-env.d.ts +4 -4
  29. package/dist/boot/stage-05-execution-env.js +7 -10
  30. package/dist/boot/stage-06-runners.d.ts +2 -2
  31. package/dist/boot/stage-06-runners.js +5 -4
  32. package/dist/boot/stage-07-capability-layer.d.ts +2 -2
  33. package/dist/boot/stage-07-capability-layer.js +3 -3
  34. package/dist/boot/stage-08-reapers.d.ts +2 -2
  35. package/dist/boot/stage-09-leader.d.ts +3 -9
  36. package/dist/boot/stage-10-http-server.d.ts +3 -9
  37. package/dist/boot/stage-10-http-server.js +12 -10
  38. package/dist/boot/stores.d.ts +1 -1
  39. package/dist/boot/stores.js +5 -6
  40. package/dist/boot/web-admin.d.ts +0 -3
  41. package/dist/boot/web-admin.js +35 -35
  42. package/dist/brain.js +17 -22
  43. package/dist/budget.js +5 -4
  44. package/dist/caller-text-echo.d.ts +20 -0
  45. package/dist/caller-text-echo.js +24 -0
  46. package/dist/capabilities/brand-identity.d.ts +26 -0
  47. package/dist/capabilities/brand-identity.js +27 -0
  48. package/dist/capabilities/builtin-tools.js +2 -1
  49. package/dist/capabilities/center-plugins.js +1 -1
  50. package/dist/capabilities/center-prompts.d.ts +32 -59
  51. package/dist/capabilities/center-prompts.js +18 -32
  52. package/dist/capabilities/composite-members.d.ts +1 -0
  53. package/dist/capabilities/composite-members.js +1 -0
  54. package/dist/capabilities/engine-capabilities.js +2 -1
  55. package/dist/capabilities/hands-lane.d.ts +9 -6
  56. package/dist/capabilities/hands-lane.js +3 -3
  57. package/dist/capabilities/host-tool-names.d.ts +17 -0
  58. package/dist/capabilities/host-tool-names.js +15 -0
  59. package/dist/capabilities/mcp-probe.d.ts +14 -1
  60. package/dist/capabilities/mcp-probe.js +22 -133
  61. package/dist/capabilities/repo-tools.js +4 -3
  62. package/dist/capabilities/scenarios.d.ts +42 -47
  63. package/dist/capabilities/scenarios.js +19 -35
  64. package/dist/capabilities/select-environment-tool.js +2 -1
  65. package/dist/capabilities/send-user-file-tool.d.ts +0 -1
  66. package/dist/capabilities/send-user-file-tool.js +2 -2
  67. package/dist/capabilities/skills.d.ts +19 -4
  68. package/dist/capabilities/skills.js +30 -10
  69. package/dist/capabilities/tool-defer.js +2 -1
  70. package/dist/config-catalog.js +22 -11
  71. package/dist/config-invariants.js +2 -2
  72. package/dist/config-lkg.d.ts +9 -9
  73. package/dist/config-lkg.js +20 -4
  74. package/dist/config-provider.d.ts +3 -1
  75. package/dist/config-provider.js +14 -6
  76. package/dist/config-types.d.ts +13 -9
  77. package/dist/config.d.ts +2 -2
  78. package/dist/config.js +34 -20
  79. package/dist/device-ws-protocol.d.ts +1 -1
  80. package/dist/device-ws-protocol.js +1 -0
  81. package/dist/elicitation.d.ts +7 -0
  82. package/dist/elicitation.js +20 -5
  83. package/dist/env-name-allowlist-knobs.d.ts +12 -2
  84. package/dist/env-name-allowlist-knobs.js +5 -4
  85. package/dist/exec-bounds.d.ts +15 -3
  86. package/dist/exec-bounds.js +4 -0
  87. package/dist/execution-lane-caps.d.ts +9 -2
  88. package/dist/execution-lane-caps.js +11 -0
  89. package/dist/hooks/classic-stations.d.ts +86 -0
  90. package/dist/hooks/classic-stations.js +27 -0
  91. package/dist/hooks/hook-llm.d.ts +8 -5
  92. package/dist/hooks/hook-llm.js +80 -37
  93. package/dist/hooks/hook-runner.d.ts +273 -75
  94. package/dist/hooks/hook-runner.js +293 -317
  95. package/dist/http/admission.d.ts +8 -2
  96. package/dist/http/admission.js +103 -59
  97. package/dist/http/approval-sweeps.js +59 -18
  98. package/dist/http/checkpoint-ctx.d.ts +36 -18
  99. package/dist/http/checkpoint-ctx.js +14 -10
  100. package/dist/http/claimed-run.d.ts +13 -0
  101. package/dist/http/claimed-run.js +15 -0
  102. package/dist/http/device-placement.js +5 -10
  103. package/dist/http/dispatch.d.ts +0 -4
  104. package/dist/http/dispatch.js +6 -10
  105. package/dist/http/durable-run-leg.d.ts +2 -1
  106. package/dist/http/durable-run-leg.js +5 -3
  107. package/dist/http/health-self-report.d.ts +7 -5
  108. package/dist/http/health-self-report.js +4 -18
  109. package/dist/http/principal-gate.d.ts +23 -6
  110. package/dist/http/principal-gate.js +24 -5
  111. package/dist/http/refusal-body.d.ts +21 -0
  112. package/dist/http/refusal-body.js +4 -0
  113. package/dist/http/request-guards.d.ts +27 -0
  114. package/dist/http/request-guards.js +24 -3
  115. package/dist/http/resume-legs.d.ts +25 -0
  116. package/dist/http/resume-legs.js +48 -26
  117. package/dist/http/routes/approvals-assistant.js +1 -1
  118. package/dist/http/routes/capabilities.js +7 -17
  119. package/dist/http/routes/diagnostics.js +1 -1
  120. package/dist/http/routes/rules.js +2 -2
  121. package/dist/http/routes/runs.js +13 -17
  122. package/dist/http/routes/session-sync.js +4 -2
  123. package/dist/http/routes/sessions.js +23 -11
  124. package/dist/http/routes/shared-memory.js +2 -13
  125. package/dist/http/routes/side-query.js +2 -3
  126. package/dist/http/routes/tasks.js +34 -19
  127. package/dist/http/scenario-read-face.d.ts +14 -0
  128. package/dist/http/scenario-read-face.js +5 -0
  129. package/dist/http/send.d.ts +13 -1
  130. package/dist/http/send.js +12 -1
  131. package/dist/http/server.d.ts +37 -23
  132. package/dist/http/server.js +30 -4
  133. package/dist/http/stored-body-replay.d.ts +10 -3
  134. package/dist/http/stored-body-replay.js +27 -6
  135. package/dist/http/submit-ctx.d.ts +25 -0
  136. package/dist/http/submit-ctx.js +39 -0
  137. package/dist/http/wire-types.d.ts +18 -5
  138. package/dist/index.d.ts +3 -2
  139. package/dist/index.js +3 -2
  140. package/dist/lane-home-probe.d.ts +43 -0
  141. package/dist/lane-home-probe.js +35 -0
  142. package/dist/limit-refusal.d.ts +47 -0
  143. package/dist/limit-refusal.js +12 -0
  144. package/dist/main.js +1 -1
  145. package/dist/mcp-assertion-header.d.ts +3 -11
  146. package/dist/mcp-assertion-header.js +3 -9
  147. package/dist/mcp-tool-faces.d.ts +5 -0
  148. package/dist/mcp-tool-faces.js +5 -1
  149. package/dist/memory-scope.d.ts +1 -1
  150. package/dist/model-entry-refusal.d.ts +1 -7
  151. package/dist/model-provider.d.ts +39 -12
  152. package/dist/model-provider.js +19 -2
  153. package/dist/model-route-endpoint.d.ts +52 -12
  154. package/dist/model-route-endpoint.js +48 -26
  155. package/dist/model-route-gap.d.ts +5 -4
  156. package/dist/model-route-gap.js +1 -1
  157. package/dist/model-select.d.ts +58 -0
  158. package/dist/model-select.js +31 -0
  159. package/dist/namespaced-rule-name.d.ts +7 -4
  160. package/dist/namespaced-rule-name.js +4 -3
  161. package/dist/observability/cost-taxonomy.d.ts +7 -7
  162. package/dist/observability/fail-open.d.ts +53 -12
  163. package/dist/observability/fail-open.js +47 -7
  164. package/dist/observability/metrics.js +3 -1
  165. package/dist/observability/prompt-manifest.d.ts +47 -82
  166. package/dist/observability/prompt-manifest.js +119 -17
  167. package/dist/observability/run-terminal.js +14 -6
  168. package/dist/observability/secret-env-scrub.d.ts +13 -1
  169. package/dist/observability/secret-env-scrub.js +13 -1
  170. package/dist/observability/tool-trace.d.ts +16 -21
  171. package/dist/observability/tool-trace.js +51 -42
  172. package/dist/per-task-image.d.ts +7 -1
  173. package/dist/per-task-image.js +3 -3
  174. package/dist/plain-string-record.d.ts +25 -0
  175. package/dist/plain-string-record.js +32 -0
  176. package/dist/plugins/blob-backend.d.ts +68 -13
  177. package/dist/plugins/blob-backend.js +53 -10
  178. package/dist/plugins/checkpoint-store-sql.d.ts +14 -4
  179. package/dist/plugins/checkpoint-store-sql.js +39 -26
  180. package/dist/plugins/k8s-bg-scripts.d.ts +11 -0
  181. package/dist/plugins/k8s-bg-scripts.js +22 -0
  182. package/dist/plugins/local-checkpoint-store.js +5 -2
  183. package/dist/plugins/local-session-store.js +3 -3
  184. package/dist/plugins/one-time-migrations.d.ts +147 -91
  185. package/dist/plugins/one-time-migrations.js +372 -208
  186. package/dist/plugins/permission-rule-store-sql.d.ts +13 -7
  187. package/dist/plugins/permission-rule-store-sql.js +4 -18
  188. package/dist/plugins/pg-session-storage.d.ts +4 -5
  189. package/dist/plugins/pg-session-storage.js +7 -17
  190. package/dist/plugins/remote-env-adb.d.ts +6 -0
  191. package/dist/plugins/remote-env-adb.js +9 -4
  192. package/dist/plugins/remote-env-device.js +3 -0
  193. package/dist/plugins/remote-env-e2b.d.ts +1 -1
  194. package/dist/plugins/remote-env-e2b.js +21 -9
  195. package/dist/plugins/remote-env-host.d.ts +9 -9
  196. package/dist/plugins/remote-env-host.js +10 -32
  197. package/dist/plugins/remote-env-k8s.d.ts +95 -26
  198. package/dist/plugins/remote-env-k8s.js +265 -90
  199. package/dist/plugins/remote-env-local-docker.d.ts +20 -1
  200. package/dist/plugins/remote-env-local-docker.js +77 -9
  201. package/dist/plugins/remote-env-ssh.d.ts +15 -0
  202. package/dist/plugins/remote-env-ssh.js +109 -3
  203. package/dist/plugins/remote-shell.d.ts +0 -1
  204. package/dist/plugins/remote-shell.js +1 -2
  205. package/dist/plugins/retention-store-sql.js +6 -2
  206. package/dist/plugins/schema-consistency.d.ts +54 -0
  207. package/dist/plugins/schema-consistency.js +52 -0
  208. package/dist/plugins/session-epoch-anchor.d.ts +13 -0
  209. package/dist/plugins/session-epoch-anchor.js +23 -0
  210. package/dist/plugins/shared-memory-store-sql.d.ts +9 -3
  211. package/dist/plugins/shared-memory-store-sql.js +5 -16
  212. package/dist/plugins/sql-driver.d.ts +141 -0
  213. package/dist/plugins/sql-driver.js +115 -1
  214. package/dist/plugins/sql-errors.d.ts +41 -0
  215. package/dist/plugins/sql-errors.js +36 -0
  216. package/dist/plugins/store-backend.js +2 -1
  217. package/dist/plugins/tidb-session-storage.d.ts +7 -11
  218. package/dist/plugins/tidb-session-storage.js +6 -16
  219. package/dist/plugins/tidb-session-store.js +1 -1
  220. package/dist/prompts-domain-validate.d.ts +17 -61
  221. package/dist/prompts-domain-validate.js +9 -119
  222. package/dist/question.d.ts +18 -9
  223. package/dist/question.js +21 -13
  224. package/dist/request-key-closure.d.ts +4 -4
  225. package/dist/request-key-closure.js +18 -13
  226. package/dist/rule-entry-list.d.ts +134 -24
  227. package/dist/rule-entry-list.js +162 -30
  228. package/dist/run-local.d.ts +3 -3
  229. package/dist/run-local.js +26 -25
  230. package/dist/runs.d.ts +48 -2
  231. package/dist/runs.js +61 -2
  232. package/dist/server-secret-env.d.ts +44 -18
  233. package/dist/server-secret-env.js +49 -8
  234. package/dist/session-incarnation.d.ts +15 -5
  235. package/dist/session-incarnation.js +16 -9
  236. package/dist/session-titler.js +12 -2
  237. package/dist/spec-fields.d.ts +34 -18
  238. package/dist/spec-fields.js +46 -53
  239. package/dist/sql-engine-posture.d.ts +29 -4
  240. package/dist/sql-engine-posture.js +36 -1
  241. package/dist/stored-request-hooks-m20.d.ts +23 -0
  242. package/dist/stored-request-hooks-m20.js +124 -0
  243. package/dist/stored-request-tool-axes-m19.d.ts +20 -5
  244. package/dist/stored-request-tool-axes-m19.js +30 -2
  245. package/dist/task-a2a.d.ts +9 -1
  246. package/dist/task-a2a.js +73 -47
  247. package/dist/task-mcp.d.ts +54 -102
  248. package/dist/task-mcp.js +194 -92
  249. package/dist/task-settings.d.ts +47 -25
  250. package/dist/task-settings.js +46 -61
  251. package/dist/text-bidi.d.ts +3 -46
  252. package/dist/text-bidi.js +3 -3
  253. package/dist/tool-approval.d.ts +1 -1
  254. package/dist/trace/core-keyset-guard.d.ts +24 -4
  255. package/dist/trace/engine-notice-wire.d.ts +11 -3
  256. package/dist/trace/engine-notice-wire.js +8 -1
  257. package/dist/trace/ledger-events.d.ts +3 -1
  258. package/dist/trace/ledger-sink.d.ts +29 -0
  259. package/dist/trace/ledger-sink.js +15 -1
  260. package/dist/trace/model-segment.js +1 -1
  261. package/dist/trace/project.d.ts +25 -6
  262. package/dist/trace/project.js +68 -22
  263. package/dist/trace/wire-projection-faces.d.ts +80 -2
  264. package/dist/trace/wire-projection-faces.js +23 -1
  265. package/dist/user-authored-text.d.ts +7 -0
  266. package/dist/user-authored-text.js +3 -0
  267. package/dist/web-admin-client/apply-effective.js +10 -5
  268. package/dist/web-admin-client/candidate-gate.d.ts +6 -6
  269. package/dist/web-admin-client/candidate-gate.js +3 -3
  270. package/dist/web-admin-client/http-client.js +0 -6
  271. package/dist/web-admin-client/read-warnings.d.ts +13 -0
  272. package/dist/web-admin-client/read-warnings.js +10 -0
  273. package/dist/web-admin-client/skills-mcp.d.ts +3 -0
  274. package/dist/web-admin-client/skills-mcp.js +9 -6
  275. package/dist/web-admin-client/types.d.ts +3 -4
  276. package/dist/workspace-cache.d.ts +35 -0
  277. package/dist/workspace-cache.js +36 -0
  278. package/dist/workspace-space.d.ts +3 -2
  279. package/dist/workspace-space.js +2 -0
  280. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +8 -1
  281. package/package.json +4 -4
  282. package/scripts/drain-check-local.mjs +37 -3
  283. package/scripts/drain-check.mysql.sql +30 -2
  284. package/scripts/drain-check.pg.sql +29 -2
  285. package/dist/capabilities/memory-notice.d.ts +0 -138
  286. package/dist/capabilities/memory-notice.js +0 -64
  287. package/dist/capabilities/prompt.d.ts +0 -17
  288. package/dist/capabilities/prompt.js +0 -6
  289. package/dist/mcp-content-origin.d.ts +0 -34
  290. package/dist/mcp-content-origin.js +0 -10
  291. package/dist/tool-axis-words.d.ts +0 -27
  292. package/dist/tool-axis-words.js +0 -9
  293. package/dist/wire-name-bound.d.ts +0 -14
  294. package/dist/wire-name-bound.js +0 -13
@@ -22,7 +22,7 @@ import { callGrantBindingOf, questionGrantBindingOf } from "../../standing-answe
22
22
  import { sendResumeOutcome } from "../route-ctx.js";
23
23
  import { assertNever } from "../route-ctx.js";
24
24
  import * as facts from "../../capability-facts.js";
25
- import { readResumeCtx, resumeCtxCorruptAnswer } from "../resume-ctx-read.js";
25
+ import { readResumeCtx } from "../resume-ctx-read.js";
26
26
  import { recordFailOpen } from "../../observability/fail-open.js";
27
27
  import { MAX_KEY_ECHO, keyClosureClause, scanKeyClosure } from "../../request-key-closure.js";
28
28
  const DECIDE_BODY_DECLARED_KEYS = [
@@ -1,5 +1,5 @@
1
- import { PERMISSION_MODES, expandTiers, reasoningEffortLevelsOf, routePairingStatus } from "@sema-agent/core";
2
- import { modelImageInput, isAtMentionable, selectableCatalog } from "../../model-select.js";
1
+ import { PERMISSION_MODES, USER_ATTACHMENT_SEAT, expandTiers, reasoningEffortLevelsOf, routePairingStatus } from "@sema-agent/core";
2
+ import { modelImageInput, modelReasoningDecl, isAtMentionable, selectableCatalog } from "../../model-select.js";
3
3
  import { buildDroppedEntriesWire } from "../../web-admin-client/read-warnings.js";
4
4
  import { buildCandidateRejectedWire } from "../../web-admin-client/candidate-gate.js";
5
5
  import { modelKeyPoisonOf, resolveModelApiKey } from "../../key-resolver.js";
@@ -12,6 +12,7 @@ import { DEVICE_PROTOCOL_VERSION, DEVICE_WS_PATH } from "../../device-ws-protoco
12
12
  import { a2aInjectionHonored } from "../../task-a2a.js";
13
13
  import { buildEmptyMcpProbeFace, buildMcpInjectionRefusal, buildMcpProbeCacheKey, buildMcpProbeInflightRefusal, buildMcpProbeRateKey, buildMcpProbeRateRefusal, createMcpProbeLocal, buildMcpProbeDeclarationRefusal, buildMcpProbeIncompleteRefusal, McpProbeIncomplete, MCP_PROBE_HTTP_STATUS, MCP_PROBE_MAX_INFLIGHT, MCP_PROBE_VERDICT_TIMEOUT_MS, createMcpProbeWalk, readMcpProbeBody, } from "../../capabilities/mcp-probe.js";
14
14
  import { mcpForScenario } from "../../web-admin-client/facade.js";
15
+ import { sendScenarioNotFound } from "../scenario-read-face.js";
15
16
  import { sendJson, sendError } from "../send.js";
16
17
  import { assertNever } from "../route-ctx.js";
17
18
  import { fileHistoryCaptureOf } from "../../file-history-capture.js";
@@ -44,9 +45,6 @@ function retentionAdvertisement(config) {
44
45
  export function createCapabilitiesLocal() {
45
46
  return { mcpProbe: createMcpProbeLocal() };
46
47
  }
47
- function sendScenarioNotFound(res) {
48
- sendError(res, 404, "scenario_not_found", "scenario not found");
49
- }
50
48
  export async function handleCapabilities(req, res, match, ctx) {
51
49
  const { deps } = ctx;
52
50
  switch (match.decl.id) {
@@ -94,7 +92,7 @@ export async function handleCapabilities(req, res, match, ctx) {
94
92
  memory: false,
95
93
  memoryWrite: false,
96
94
  memoryEngine: projectMemoryEngineCapability(deps.memoryPosture),
97
- store: projectStoreCapability(deps.storePosture ?? { declared: deps.config.dbBackend, backend: "memory" }, deps.sqlEngineFacts?.()),
95
+ store: projectStoreCapability(deps.storePosture ?? { declared: deps.config.dbBackend, backend: "memory" }, deps.sqlEngine?.facts),
98
96
  centerWiring: deps.centerWiring?.(gatedPrincipal(req, deps.config)),
99
97
  webSearch: projectWebSearchCapability(deps.webSearchProvider),
100
98
  sessionBackgroundable: facts.durableRuns(deps).available,
@@ -149,6 +147,7 @@ export async function handleCapabilities(req, res, match, ctx) {
149
147
  disableSkills: true,
150
148
  userInvokedSkills: true,
151
149
  skillToolLists: true,
150
+ userAttachments: USER_ATTACHMENT_SEAT,
152
151
  projectContext: cwdHonored(deps.config),
153
152
  callerCwd: cwdHonored(deps.config) || deviceCwdHonored(deps.config),
154
153
  taskWriteFaceOpen: taskWriteFaceRefusal(deps.config) === undefined && autonomyKeepsOpenWriteFace(deps.config.autonomy),
@@ -225,16 +224,7 @@ export async function handleCapabilities(req, res, match, ctx) {
225
224
  sendError(res, 400, "request.field_invalid", "scenario must be given at most once (omit it to read this deployment's default scenario)");
226
225
  return;
227
226
  }
228
- const requestedScenario = scenarioParams[0];
229
- let scenarioName;
230
- if (requestedScenario === undefined)
231
- scenarioName = deps.config.defaultScenario;
232
- else if (deps.scenarioGate === undefined) {
233
- sendScenarioNotFound(res);
234
- return;
235
- }
236
- else
237
- scenarioName = await deps.scenarioGate(principal, requestedScenario);
227
+ const scenarioName = await deps.scenarioGate(principal, scenarioParams[0]);
238
228
  await serveMcpProbeFace(res, ctx, principal, mcpForScenario(deps.config.mcpServers, scenarioName) ?? [], "deployment");
239
229
  return;
240
230
  }
@@ -265,7 +255,7 @@ export async function handleCapabilities(req, res, match, ctx) {
265
255
  name,
266
256
  id: m.id,
267
257
  provider: m.provider,
268
- reasoning: m.reasoning,
258
+ ...(modelReasoningDecl(m) !== undefined ? { reasoning: modelReasoningDecl(m) } : {}),
269
259
  ...(modelImageInput(m) !== "undeclared" ? { vision: modelImageInput(m) === "yes" } : {}),
270
260
  ...(m.contextWindow ? { contextWindow: m.contextWindow } : {}),
271
261
  ...(m.maxTokens ? { maxOutputTokens: m.maxTokens } : {}),
@@ -109,7 +109,7 @@ export async function handleDiagnostics(req, res, match, ctx) {
109
109
  sendJson(res, 200, {
110
110
  static: deps.staticWiring,
111
111
  memoryPosture: deps.memoryPosture ?? null,
112
- sqlEngine: deps.sqlEngineFacts?.() ?? null,
112
+ sqlEngine: deps.sqlEngine?.facts ?? null,
113
113
  writeProtection: deps.writeProtection ?? null,
114
114
  readFace: buildReadFaceWiring(deps.config),
115
115
  executionLane: projectExecutionLaneCapability(deps.config),
@@ -1,5 +1,5 @@
1
1
  import { z } from "zod";
2
- import { sendJson, sendError, httpErrorCode } from "../send.js";
2
+ import { sendJson, sendError, sendHttpError } from "../send.js";
3
3
  import { gatedPrincipal, explicitOperatorOk, sendOperatorPrincipalsRequired, sendUnauthorized } from "../principal-gate.js";
4
4
  import { assertNever } from "../route-ctx.js";
5
5
  import * as facts from "../../capability-facts.js";
@@ -113,7 +113,7 @@ export async function handleRules(req, res, match, ctx) {
113
113
  }
114
114
  catch (err) {
115
115
  if (err instanceof HttpError && err.status !== 400) {
116
- sendError(res, err.status, httpErrorCode(err.status, err.code), err.message);
116
+ sendHttpError(res, err);
117
117
  return;
118
118
  }
119
119
  sendError(res, 400, "request.body_shape", "body must be valid JSON");
@@ -17,7 +17,7 @@ import { ASK_DECISION_CONSUMER_ABSENT, outcomeOfDecidedRow, resolveStreamApprova
17
17
  import { MAX_DECISION_NOTE_CHARS } from "../../approval-ask-machine.js";
18
18
  import { causeOfAskRow } from "../../approval-deny-reasons.js";
19
19
  import { normalizeRunEventType, screenedStoredEventData } from "../../trace/project.js";
20
- import { sendJson, sendError, httpErrorCode, sseHeaders } from "../send.js";
20
+ import { sendJson, sendError, sendHttpError, sseHeaders } from "../send.js";
21
21
  import { mintCancelledResult } from "../../run-cancel-context.js";
22
22
  import { acceptedInputKindOf } from "../../user-input-ledger.js";
23
23
  import { INJECTION_TIERS } from "../../trace/injection-tier.js";
@@ -405,7 +405,7 @@ export async function handleRuns(req, res, match, ctx) {
405
405
  }
406
406
  catch (e) {
407
407
  if (e instanceof HttpError) {
408
- sendError(res, e.status, httpErrorCode(e.status, e.code), e.message);
408
+ sendHttpError(res, e);
409
409
  return;
410
410
  }
411
411
  sendError(res, 400, "request.invalid_json", "invalid JSON body");
@@ -632,7 +632,7 @@ export async function handleRuns(req, res, match, ctx) {
632
632
  mintResult.noResumeCtx = true;
633
633
  return;
634
634
  }
635
- const unservable = storedBodyReplayRefusal(resumeRead.ctx, deps.config.autonomy);
635
+ const unservable = storedBodyReplayRefusal(resumeRead.ctx, deps.config);
636
636
  if (unservable !== undefined) {
637
637
  mintResult.unservable = unservable;
638
638
  return;
@@ -693,7 +693,7 @@ export async function handleRuns(req, res, match, ctx) {
693
693
  }
694
694
  if (mintResult.unservable !== undefined) {
695
695
  deps.logger?.warn?.("steer_wake_park_refused_stored_body", { taskId, sessionId: run.sessionId, ...storedBodyReplayLogFields(mintResult.unservable) });
696
- const { status, body: { error, errorCode, ...rest } } = storedBodyReplayAnswer(mintResult.unservable, serviceVersion());
696
+ const { status, body: { error, errorCode, ...rest } } = storedBodyReplayAnswer(mintResult.unservable, serviceVersion(), false);
697
697
  sendError(res, status, errorCode, error, rest);
698
698
  return;
699
699
  }
@@ -730,7 +730,7 @@ export async function handleRuns(req, res, match, ctx) {
730
730
  }
731
731
  catch (e) {
732
732
  if (e instanceof HttpError) {
733
- sendError(res, e.status, httpErrorCode(e.status, e.code), e.message);
733
+ sendHttpError(res, e);
734
734
  return;
735
735
  }
736
736
  sendError(res, 400, "request.invalid_json", "invalid JSON body");
@@ -743,10 +743,6 @@ export async function handleRuns(req, res, match, ctx) {
743
743
  }
744
744
  const interruptBody = interruptNormalized;
745
745
  const interruptBare = !("text" in interruptBody);
746
- if (interruptBare && Object.keys(interruptBody).length > 0) {
747
- sendError(res, 400, "request.body_shape", "body must be {} (bare halt: cut the in-flight turn and stop the run at this boundary) or { text: string (non-empty) } (cut + steer: the text reaches the model after the cut)");
748
- return;
749
- }
750
746
  let text = "";
751
747
  if (!interruptBare) {
752
748
  if (typeof interruptBody.text !== "string" || interruptBody.text.length === 0) {
@@ -754,11 +750,11 @@ export async function handleRuns(req, res, match, ctx) {
754
750
  return;
755
751
  }
756
752
  text = interruptBody.text;
757
- const keyIssue = steerBodyKeyIssue(interruptBody, "interrupt");
758
- if (keyIssue) {
759
- sendError(res, 400, "request.body_shape", keyIssue.message, { unknownKeys: keyIssue.unknownKeys, unsupportedKeys: keyIssue.unsupportedKeys });
760
- return;
761
- }
753
+ }
754
+ const keyIssue = steerBodyKeyIssue(interruptBody, "interrupt");
755
+ if (keyIssue) {
756
+ sendError(res, 400, "request.body_shape", keyIssue.message, { unknownKeys: keyIssue.unknownKeys, unsupportedKeys: keyIssue.unsupportedKeys });
757
+ return;
762
758
  }
763
759
  const interruptIdemRaw = headerStr(req.headers["idempotency-key"]);
764
760
  const interruptInputId = interruptIdemRaw ? `idem-${idemKeyOf(interruptIdemRaw, idemCallerOf(req, ctx), "run").slice(0, 32)}` : uuidv7();
@@ -897,7 +893,7 @@ export async function handleRuns(req, res, match, ctx) {
897
893
  }
898
894
  catch (e) {
899
895
  if (e instanceof HttpError) {
900
- sendError(res, e.status, httpErrorCode(e.status, e.code), e.message);
896
+ sendHttpError(res, e);
901
897
  return;
902
898
  }
903
899
  sendError(res, 400, "request.invalid_json", "invalid JSON body");
@@ -948,7 +944,7 @@ export async function handleRuns(req, res, match, ctx) {
948
944
  }
949
945
  catch (e) {
950
946
  if (e instanceof HttpError) {
951
- sendError(res, e.status, httpErrorCode(e.status, e.code), e.message);
947
+ sendHttpError(res, e);
952
948
  return;
953
949
  }
954
950
  sendError(res, 400, "request.invalid_json", "invalid JSON body");
@@ -1174,7 +1170,7 @@ export async function handleRunVerbs(req, res, match, ctx) {
1174
1170
  }
1175
1171
  catch (e) {
1176
1172
  if (e instanceof HttpError) {
1177
- sendError(res, e.status, httpErrorCode(e.status, e.code), e.message);
1173
+ sendHttpError(res, e);
1178
1174
  return;
1179
1175
  }
1180
1176
  sendError(res, 400, "request.invalid_json", "invalid JSON body");
@@ -6,6 +6,7 @@ import { exportSessionManifest, fileHistoryImportFace, replayFileHistory, replay
6
6
  import * as facts from "../../capability-facts.js";
7
7
  import { classifySyncRelationshipByIds, SyncConflictError } from "../../session-sync-kernel.js";
8
8
  import { isUuidShape } from "../../security.js";
9
+ import { sqlBlobCapRefusalText } from "../../plugins/blob-backend.js";
9
10
  import { sendJson, sendError, sendSessionError, sendSessionPolicyError, msg } from "../send.js";
10
11
  import { buildDisplaySafeUrl } from "../../trace/redact.js";
11
12
  import { gatedPrincipal, explicitOperatorOk, sendUnauthorized } from "../principal-gate.js";
@@ -186,8 +187,9 @@ export async function handleSessionSync(req, res, match, ctx) {
186
187
  }
187
188
  const fs = blobs.historyStore;
188
189
  const body = await readRawBody(req, SYNC_BLOB_MAX_BYTES);
189
- if (deps.snapshotBlobSqlCapBytes !== undefined && body.byteLength > deps.snapshotBlobSqlCapBytes) {
190
- sendError(res, 413, "blob_too_large_for_sql", `blob of ${body.byteLength} bytes exceeds this deployment's SQL snapshot-blob cap (${deps.snapshotBlobSqlCapBytes} bytes — TiDB's txn-entry-size-limit is the low wall at 6MiB by default; the mysql-protocol packet limit sits above it) — configure MinIO object storage (MINIO_ENDPOINT/MINIO_ACCESS_KEY/MINIO_SECRET_KEY) for large snapshot blobs, or raise SNAPSHOT_BLOB_SQL_MAX_BYTES if your deployment lifted those limits`);
190
+ const sqlCap = deps.snapshotBlobSqlCap?.();
191
+ if (sqlCap !== undefined && body.byteLength > sqlCap.capBytes) {
192
+ sendError(res, 413, "blob_too_large_for_sql", sqlBlobCapRefusalText(body.byteLength, sqlCap), { wallSource: sqlCap.source });
191
193
  return;
192
194
  }
193
195
  if (createHash("sha256").update(body).digest("hex") !== hash) {
@@ -1,6 +1,6 @@
1
1
  import { SessionError, materializeA2aTools, materializeMcpTools, sanitizePathComponent } from "@sema-agent/core";
2
2
  import { a2aForScenario, mcpForScenario } from "../../web-admin-client/facade.js";
3
- import { isUuidShape, legacyUnownedSessionOk } from "../../security.js";
3
+ import { HttpError, isUuidShape, legacyUnownedSessionOk } from "../../security.js";
4
4
  import { windowMessages, truncateMessageBlobs, redactSessionViewForRead } from "../../audit.js";
5
5
  import { redactSecrets } from "../../trace/redact.js";
6
6
  import { recordFailOpen } from "../../observability/fail-open.js";
@@ -8,7 +8,7 @@ import { MCP_DIAL_DEADLINE_MS, raceDeadline } from "../mcp-dial-deadline.js";
8
8
  import { sentMemoryComplianceFailure } from "../memory-compliance-failure-reply.js";
9
9
  import { MAX_SETTINGS_OUTPUT_STYLE_CHARS, MAX_SETTINGS_ENV_VARS, MAX_SETTINGS_ENV_KEY_CHARS, MAX_SETTINGS_ENV_VALUE_CHARS } from "../../task-settings.js";
10
10
  import { readWritableSessionPermissionRules } from "../../session-policy-wire.js";
11
- import { sendJson, sendError, sendSessionError, sendSessionPolicyError, sseHeaders } from "../send.js";
11
+ import { sendJson, sendError, sendSessionError, sendSessionPolicyError, sseHeaders, httpErrorBody } from "../send.js";
12
12
  import { gatedPrincipal, explicitOperatorOk, sessionOwnerReadable, sendPrincipalRefusal, sendUnauthorized } from "../principal-gate.js";
13
13
  import { assertNever, SESSION_NOT_FOUND_MESSAGE } from "../route-ctx.js";
14
14
  import * as facts from "../../capability-facts.js";
@@ -54,7 +54,7 @@ async function readLastLegMcp(deps, sessionId, principal) {
54
54
  const latest = await runStore.latestRunForSession(sessionId);
55
55
  if (latest === undefined)
56
56
  return undefined;
57
- if (!sessionOwnerReadable(principal, latest.owner, deps.config) && !explicitOperatorOk(principal, deps.config.operatorPrincipals))
57
+ if (!sessionOwnerReadable(principal, latest.owner, deps) && !explicitOperatorOk(principal, deps.config.operatorPrincipals))
58
58
  return undefined;
59
59
  const events = await runStore.getEvents(latest.taskId, 0);
60
60
  for (let i = events.length - 1; i >= 0; i--) {
@@ -144,10 +144,21 @@ export async function handleSessions(req, res, match, ctx) {
144
144
  return;
145
145
  }
146
146
  const lastLegMcp = await lastLegMcpFor(deps, safeDecode(match.params.sessionId), principal);
147
- const specs = mcpForScenario(deps.config.mcpServers, deps.config.defaultScenario);
148
- const cacheKey = `${deps.config.defaultScenario}::${principal ?? ""}`;
147
+ const ledgerHalf = lastLegMcp === undefined ? {} : { lastLegMcp };
148
+ let scenarioName;
149
+ try {
150
+ scenarioName = await deps.scenarioGate(principal, undefined);
151
+ }
152
+ catch (e) {
153
+ if (!(e instanceof HttpError))
154
+ throw e;
155
+ sendJson(res, 200, { asOf: new Date().toISOString(), ...ledgerHalf, scenarioRefusal: httpErrorBody(e) });
156
+ return;
157
+ }
158
+ const specs = mcpForScenario(deps.config.mcpServers, scenarioName);
159
+ const cacheKey = `${scenarioName}::${principal ?? ""}`;
149
160
  if (!specs || specs.length === 0) {
150
- sendJson(res, 200, { servers: [], asOf: new Date().toISOString(), ...(lastLegMcp === undefined ? {} : { lastLegMcp }) });
161
+ sendJson(res, 200, { servers: [], asOf: new Date().toISOString(), ...ledgerHalf });
151
162
  return;
152
163
  }
153
164
  const cached = mcpStatusCache.get(cacheKey);
@@ -179,23 +190,24 @@ export async function handleSessions(req, res, match, ctx) {
179
190
  })();
180
191
  mcpStatusCache.set(cacheKey, { at: Date.now(), panel });
181
192
  }
182
- sendJson(res, 200, { ...(await mcpStatusCache.get(cacheKey).panel), ...(lastLegMcp === undefined ? {} : { lastLegMcp }) });
193
+ sendJson(res, 200, { ...(await mcpStatusCache.get(cacheKey).panel), ...ledgerHalf });
183
194
  return;
184
195
  }
185
196
  }
186
197
  {
187
198
  if (match.decl.id === "session-a2a") {
188
- if (deps.config.requirePrincipal && !gatedPrincipal(req, deps.config)) {
199
+ const principal = gatedPrincipal(req, deps.config);
200
+ if (deps.config.requirePrincipal && !principal) {
189
201
  sendPrincipalRefusal(res, req, deps.config);
190
202
  return;
191
203
  }
192
- const specs = a2aForScenario(deps.config.a2aPeers, deps.config.defaultScenario);
204
+ const scenarioName = await deps.scenarioGate(principal, undefined);
205
+ const specs = a2aForScenario(deps.config.a2aPeers, scenarioName);
193
206
  if (!specs || specs.length === 0) {
194
207
  sendJson(res, 200, { peers: [], asOf: new Date().toISOString() });
195
208
  return;
196
209
  }
197
- const principal = gatedPrincipal(req, deps.config);
198
- const cacheKey = `${deps.config.defaultScenario}::${principal ?? ""}`;
210
+ const cacheKey = `${scenarioName}::${principal ?? ""}`;
199
211
  const cached = a2aStatusCache.get(cacheKey);
200
212
  if (!cached || Date.now() - cached.at > A2A_STATUS_TTL_MS) {
201
213
  if (a2aStatusCache.size > 1024)
@@ -1,21 +1,10 @@
1
- import { SHARED_MEMORY_READ_CAP_BYTES, SharedMemoryStoreError } from "@sema-agent/core";
1
+ import { isSharedMemoryDocumentPath, SHARED_MEMORY_READ_CAP_BYTES, SharedMemoryStoreError } from "@sema-agent/core";
2
2
  import { sendJson, sendError } from "../send.js";
3
3
  import { gatedPrincipal, sendUnauthorized } from "../principal-gate.js";
4
4
  import { assertNever } from "../route-ctx.js";
5
5
  import * as facts from "../../capability-facts.js";
6
6
  const RETRY_AFTER_SEC = 10;
7
7
  const HTTP_LIST_LIMIT = 100;
8
- const FORBIDDEN_PATH_CHARS = /[\p{Cc}\p{Cf}\p{Co}\p{Cn}\p{Default_Ignorable_Code_Point}\u2028\u2029\\]/u;
9
- const DOCUMENT_SUFFIXES = [".md", ".txt", ".json", ".jsonl"];
10
- function isDocumentPath(path) {
11
- if (!path.startsWith("/") || FORBIDDEN_PATH_CHARS.test(path))
12
- return false;
13
- const segments = path.slice(1).split("/");
14
- const last = segments.at(-1);
15
- if (last === undefined || !segments.every((s) => s !== "" && !s.startsWith(".")))
16
- return false;
17
- return DOCUMENT_SUFFIXES.some((s) => last.endsWith(s));
18
- }
19
8
  function refuseStore(res) {
20
9
  sendError(res, 404, "not_found.shared_memory_store", "not found");
21
10
  }
@@ -120,7 +109,7 @@ export async function handleSharedMemory(req, res, match, ctx) {
120
109
  return;
121
110
  }
122
111
  const path = query.get("path");
123
- if (path === null || path === "" || !isDocumentPath(path)) {
112
+ if (path === null || path === "" || !isSharedMemoryDocumentPath(path)) {
124
113
  sendError(res, 400, "request.query_invalid", "missing or malformed ?path= (absolute, .md/.txt/.json/.jsonl)");
125
114
  return;
126
115
  }
@@ -4,7 +4,7 @@ import { isAtMentionable, matchCatalogModel, refusedCatalogEntry, selectableCata
4
4
  import { modelUnavailableMessage } from "../../model-entry-refusal.js";
5
5
  import { redactSecrets } from "../../trace/redact.js";
6
6
  import { admitProviderEcho, redactErrorMessage } from "../../observability/run-terminal.js";
7
- import { sendJson, sendError, httpErrorCode, msg } from "../send.js";
7
+ import { sendJson, sendError, sendHttpError, msg } from "../send.js";
8
8
  import { gatedPrincipal, sendPrincipalRefusal } from "../principal-gate.js";
9
9
  import { assertNever } from "../route-ctx.js";
10
10
  const CORE_PREFLIGHT_VALIDATION_PREFIXES = ["Unknown model ref ", "No model for role "];
@@ -50,8 +50,7 @@ export async function handleSideQuery(req, res, match, ctx) {
50
50
  body = raw;
51
51
  }
52
52
  catch (e) {
53
- const he = e instanceof HttpError ? e : new HttpError(400, msg(e));
54
- sendError(res, he.status, httpErrorCode(he.status, he.code), he.message);
53
+ sendHttpError(res, e instanceof HttpError ? e : new HttpError(400, msg(e)));
55
54
  return;
56
55
  }
57
56
  if (!Array.isArray(body.messages) || body.messages.length === 0) {
@@ -1,20 +1,20 @@
1
1
  import { redactErrorMessage, redactHead, redactTerminalResult } from "../../observability/run-terminal.js";
2
2
  import { runWithVerification, runCascade, taskNotificationDedupKey, uuidv7 } from "@sema-agent/core";
3
- import { graftParkToolCallId, markChildrenStoppedByUserOnAbort, syncRefusalHttpStatus, releaseKeyIfZeroWorkRefusal, HEARTBEAT_MS } from "../../runs.js";
3
+ import { graftParkToolCallId, markChildrenStoppedByUserOnAbort, syncRefusalHttpStatus, zeroWorkRefusalFacts, releaseKeyIfZeroWorkRefusal, createPromptCommitReader, HEARTBEAT_MS } from "../../runs.js";
4
4
  import { sealResultForExit, outwardPlane } from "../../terminal.js";
5
5
  import { withPrincipal } from "../../observability/principal-context.js";
6
6
  import { fleetRunPublisher, fleetRunLabels, fleetRunResiduals, isFleetAgentTerminalNotification } from "../../fleet/fleet-bus.js";
7
7
  import { defaultSubagentTailBus } from "../../fleet/subagent-tail-bus.js";
8
8
  import { createReasoningSegments, reasoningStreamOfTask } from "../../trace/model-segment.js";
9
9
  import { emitPendingWorkflowCompletions, taskNotificationInboxEntry, taskNotificationStreamKeyOf, NotifiedKeys } from "../../orchestration/workflow-completion-inbox.js";
10
- import { createLedgerSink } from "../../trace/ledger-sink.js";
10
+ import { createLedgerSink, createLedgerTwinEmit } from "../../trace/ledger-sink.js";
11
11
  import { registerEngineNoticeLeg } from "../../trace/engine-notice-wire.js";
12
12
  import { clearTurnActivity, readTurnActivityMs, recordTurnActivity } from "../../turn-activity.js";
13
13
  import { buildCancelledLedgerRow, createRunAbort, mintCancelledResult } from "../../run-cancel-context.js";
14
14
  import { recordResourceWindow } from "../../resource-window.js";
15
15
  import { redactSecrets } from "../../trace/redact.js";
16
16
  import { liveToolEndForBacklog } from "../../trace/tool-output-media.js";
17
- import { contextUsageEventData, turnEndEventData, toolStartEventData, toolEndEventData, taskProgressEventData, taskStartedEventData, taskNotificationEventData, compactedEventData, diagnosticsEventData, brainStatusEventData, steeringInjectedEventData, compactionOutcomeEventData, workspaceChangedEventData, wiringManifestFaceEventData, toolRosterDeltaEventData, toolProgressEventData, toolDisclosureEventData, humanInputEventData, appendModelUsageDelta, attachModelUsage, textEndEventData, liveIdentityFields } from "../../trace/project.js";
17
+ import { contextUsageEventData, turnEndEventData, toolStartEventData, toolEndEventData, taskProgressEventData, taskStartedEventData, taskNotificationEventData, compactedEventData, diagnosticsEventData, brainStatusEventData, steeringInjectedEventData, compactionOutcomeEventData, compactionStartedEventData, workspaceChangedEventData, wiringManifestFaceEventData, toolRosterDeltaEventData, toolProgressEventData, toolDisclosureEventData, humanInputEventData, appendModelUsageDelta, attachModelUsage, textEndEventData, liveIdentityFields } from "../../trace/project.js";
18
18
  import { cascadeConfig, runMeta } from "../run-meta.js";
19
19
  import { idemCallerOf, inflightKeyOf, submissionIdemOf } from "../idempotency.js";
20
20
  import { replayOfExistingRun } from "../submit-replay.js";
@@ -30,7 +30,7 @@ import { failOpenTagForDroppedFrame, recordFailOpen } from "../../observability/
30
30
  import { buildSyncSubmitBody } from "../sync-submit-body.js";
31
31
  import { assertNever } from "../route-ctx.js";
32
32
  import * as facts from "../../capability-facts.js";
33
- import { buildCheckpointCtx } from "../checkpoint-ctx.js";
33
+ import { persistSubmitCtx } from "../submit-ctx.js";
34
34
  import { serviceVersion } from "../../service-version.js";
35
35
  import { withModelReachScope } from "../../model-reach-scope.js";
36
36
  import { createInstructionSelectionLeg } from "../../instruction-selection.js";
@@ -249,6 +249,7 @@ export async function handleTasks(req, res, match, ctx) {
249
249
  let durableHeartbeat;
250
250
  const cancelledResult = (errorMessage, cause) => mintCancelledResult({ taskId: durableTaskId ?? "", sessionId: prepared.spec.sessionId ?? "", startedAtMs: streamLegStartedAt, message: errorMessage, cause });
251
251
  let userMsgEntryId;
252
+ const promptCommit = createPromptCommitReader();
252
253
  let anchorPut = false;
253
254
  const putRewindAnchor = () => {
254
255
  if (anchorPut || !userMsgEntryId || !durableTaskId || !prepared.spec.sessionId || !deps.resumeAnchorStore)
@@ -285,14 +286,18 @@ export async function handleTasks(req, res, match, ctx) {
285
286
  }
286
287
  durableTaskId = tid;
287
288
  }
289
+ if (deps.checkpointStore && prepared.spec.sessionId) {
290
+ const ctxRefusal = await persistSubmitCtx(deps.checkpointStore, deps, durableTaskId !== undefined && deps.runStore ? { runStore: deps.runStore, taskId: durableTaskId } : undefined, prepared.spec.sessionId, { body: prepared.body, memoryScope: prepared.auth?.memoryScope, writerVersion: serviceVersion() });
291
+ if (ctxRefusal) {
292
+ sseData(res, { type: "done", result: doneResultOfOutcome(ctxRefusal) });
293
+ return ctxRefusal;
294
+ }
295
+ }
288
296
  commitSessionInheritEnv(deps, prepared.spec.sessionId, prepared.body);
289
297
  if (prepared.spec.sessionId)
290
298
  markChildrenStoppedByUserOnAbort(ac.signal, durableTaskId ?? prepared.spec.sessionId, prepared.auth?.principal);
291
299
  let ledgerSink;
292
300
  let ledgerTerminal = false;
293
- if (deps.checkpointStore && prepared.spec.sessionId) {
294
- await deps.checkpointStore.putCtx(prepared.spec.sessionId, buildCheckpointCtx({ body: prepared.body, memoryScope: prepared.auth?.memoryScope, writerVersion: serviceVersion() }));
295
- }
296
301
  if (durableTaskId && deps.runStore) {
297
302
  const rs = deps.runStore;
298
303
  const tid = durableTaskId;
@@ -457,8 +462,9 @@ export async function handleTasks(req, res, match, ctx) {
457
462
  break;
458
463
  for (const end of ev.type === "done" ? [...childReasoning.cut(), ...topReasoning.cut()] : topReasoning.onEvent(ev))
459
464
  sseData(res, end);
460
- if (ev.type === "message_committed" && ev.role === "user")
461
- userMsgEntryId = ev.entryId;
465
+ const promptEntryId = promptCommit(ev);
466
+ if (promptEntryId !== undefined)
467
+ userMsgEntryId = promptEntryId;
462
468
  const causeAtDone = ev.type === "done" ? ac.cause() : undefined;
463
469
  await deps.userInputLedger?.observeLegEvent(liveStream, ev);
464
470
  prepared.acceptance.observe(ev);
@@ -539,6 +545,9 @@ export async function handleTasks(req, res, match, ctx) {
539
545
  else if (ev.type === "compaction_outcome") {
540
546
  sseData(res, { type: "compaction_outcome", ...compactionOutcomeEventData(ev) });
541
547
  }
548
+ else if (ev.type === "compaction_started") {
549
+ sseData(res, { type: "compaction_started", ...compactionStartedEventData(ev) });
550
+ }
542
551
  else if (ev.type === "wiring_manifest") {
543
552
  const wiringFace = wiringManifestFaceEventData(ev, wiringOperatorFace, instructionLeg.segment());
544
553
  wiringReceipt ??= wiringFace;
@@ -594,6 +603,14 @@ export async function handleTasks(req, res, match, ctx) {
594
603
  }
595
604
  res.write(`event: ${frame.type}\ndata: ${JSON.stringify(frame)}\n\n`);
596
605
  };
606
+ const faceAlive = () => !res.writableEnded && !res.destroyed;
607
+ const emitElicitation = createLedgerTwinEmit({ sink: ledgerSink, writeLive: emitAsk, liveReachable: faceAlive });
608
+ const writeQuestionLive = (frame) => {
609
+ if (!faceAlive())
610
+ throw new Error("live stream ended — question frame undeliverable");
611
+ emitAsk(frame);
612
+ };
613
+ const emitQuestion = createLedgerTwinEmit({ sink: ledgerSink, writeLive: writeQuestionLive, liveReachable: faceAlive });
597
614
  await emitPendingWorkflowCompletions(deps.workflowCompletionInbox, prepared.spec.sessionId, askOwner, (frame) => {
598
615
  if (closed || res.writableEnded || res.destroyed)
599
616
  throw new Error("stream ended before the completion frame was written");
@@ -604,15 +621,10 @@ export async function handleTasks(req, res, match, ctx) {
604
621
  sseData(res, frame);
605
622
  }, { route: "sync-stream-open", connection: "live-sse", log: (m, x) => deps.logger?.info?.(m, x) });
606
623
  const withElicit = deps.elicitation
607
- ? () => deps.elicitation.runWithContext({ taskId: askTaskId, owner: askOwner, emit: emitAsk, abortSignal: ac.signal }, streamBody)
624
+ ? () => deps.elicitation.runWithContext({ taskId: askTaskId, owner: askOwner, emit: emitElicitation, abortSignal: ac.signal }, streamBody)
608
625
  : streamBody;
609
- const emitQuestion = (frame) => {
610
- if (res.writableEnded || res.destroyed)
611
- throw new Error("live stream ended — question frame undeliverable");
612
- emitAsk(frame);
613
- };
614
626
  const withQuestion = () => deps.question
615
- ? deps.question.runWithContext({ taskId: askTaskId, owner: askOwner, emit: emitQuestion, abortSignal: ac.signal, deliverable: () => !res.writableEnded && !res.destroyed }, withElicit)
627
+ ? deps.question.runWithContext({ taskId: askTaskId, owner: askOwner, emit: emitQuestion, abortSignal: ac.signal, deliverable: faceAlive }, withElicit)
616
628
  : withElicit();
617
629
  const emitApproval = (frame) => {
618
630
  if (res.writableEnded || res.destroyed)
@@ -852,10 +864,12 @@ export async function handleTasks(req, res, match, ctx) {
852
864
  return { status: placementReject.status, body: devicePlacementErrorBody(placementReject) };
853
865
  durableTaskId = tid;
854
866
  }
855
- commitSessionInheritEnv(deps, prepared.spec.sessionId, prepared.body);
856
867
  if (deps.checkpointStore && prepared.spec.sessionId) {
857
- await deps.checkpointStore.putCtx(prepared.spec.sessionId, buildCheckpointCtx({ body: prepared.body, memoryScope: prepared.auth?.memoryScope, writerVersion: serviceVersion() }));
868
+ const ctxRefusal = await persistSubmitCtx(deps.checkpointStore, deps, durableTaskId !== undefined && deps.runStore ? { runStore: deps.runStore, taskId: durableTaskId } : undefined, prepared.spec.sessionId, { body: prepared.body, memoryScope: prepared.auth?.memoryScope, writerVersion: serviceVersion() });
869
+ if (ctxRefusal)
870
+ return ctxRefusal;
858
871
  }
872
+ commitSessionInheritEnv(deps, prepared.spec.sessionId, prepared.body);
859
873
  const syncCancelCtrl = createRunAbort();
860
874
  if (prepared.spec.sessionId)
861
875
  markChildrenStoppedByUserOnAbort(syncCancelCtrl.signal, durableTaskId ?? prepared.spec.sessionId, prepared.auth?.principal);
@@ -928,7 +942,8 @@ export async function handleTasks(req, res, match, ctx) {
928
942
  const retryAfterSec = resumeAtPlane.errorCode === "memory.admission_required" && result.retryAfterMs !== undefined
929
943
  ? Math.max(1, Math.ceil(result.retryAfterMs / 1000))
930
944
  : undefined;
931
- return { status: resumeAtStatus, body: { errorCode: resumeAtPlane.errorCode, error: redactErrorMessage(resumeAtPlane.errorMessage) ?? "resume-at failed", ...(retryAfterSec !== undefined ? { retryAfterSec } : {}) } };
945
+ const refusalFacts = zeroWorkRefusalFacts(redactTerminalResult(sealResultForExit(result)).terminal);
946
+ return { status: resumeAtStatus, body: { errorCode: resumeAtPlane.errorCode, error: redactErrorMessage(resumeAtPlane.errorMessage) ?? "resume-at failed", ...(retryAfterSec !== undefined ? { retryAfterSec } : {}), ...refusalFacts } };
932
947
  }
933
948
  const exitResult = await mintSubmitExitResult(result, deps.checkpointStore);
934
949
  const syncPlane = outwardPlane(result.terminal);
@@ -0,0 +1,14 @@
1
+ /**
2
+ * 「本部署没有这个场景」的唯一答复(A9 叶:只依赖 `send.ts`)—— 场景详情读面(`GET /v1/capabilities/scenarios/:name`,名字不在 `scenarioDetails` 里)答它。
3
+ *
4
+ * 回答「此主体在此场景能看见哪些 MCP 服务器 / A2A 对端」的读口不经过这里:它们的场景名只由 `deps.scenarioGate` 给
5
+ * (`ServiceCoreDeps` 的必填件,`capabilities/scenarios.ts` `createScenarioGate` 铸的那一只),点名与缺席都问,拒就是门的 typed 错 —— 读口没有第二个场景名来源。
6
+ */
7
+ import type { ServerResponse } from "node:http";
8
+ /**
9
+ * 「本部署没有这个场景」的唯一答复。一句话、一个码,不各写一份。
10
+ * [ref] 信封统一:本答复曾是全站唯一对象形 error(`{error:{code}}`)——SDK toApiError 对它特判过一轮 TypeError 加固;统一为「字符串 error + 顶层 errorCode」邻位形
11
+ * (cancel/decide 同款),机器判据保留。
12
+ */
13
+ export declare function sendScenarioNotFound(res: ServerResponse): void;
14
+ //# sourceMappingURL=scenario-read-face.d.ts.map
@@ -0,0 +1,5 @@
1
+ import { sendError } from "./send.js";
2
+ export function sendScenarioNotFound(res) {
3
+ sendError(res, 404, "scenario_not_found", "scenario not found");
4
+ }
5
+ //# sourceMappingURL=scenario-read-face.js.map
@@ -9,6 +9,8 @@
9
9
  */
10
10
  import type { ServerResponse } from "node:http";
11
11
  import { type SessionErrorFace } from "../session-error-wire.js";
12
+ import type { HttpError } from "../security.js";
13
+ import { type Refusal, type RefusalBody } from "./refusal-body.js";
12
14
  /**
13
15
  * [ref](黑板 [ref]②)—— 一条响应「是 SSE 流」的**在场标记**。
14
16
  *
@@ -69,11 +71,21 @@ export declare function sendJson(res: ServerResponse, status: number, body: unkn
69
71
  * change), not a new response shape. Sites where the two machine-code channels genuinely disagree, or
70
72
  * where `error`/`errorCode` aren't cleanly separable from the rest of the body, are left on raw
71
73
  * `sendJson` (see the A6 migration notes for the callers still doing that on purpose). */
72
- export declare function sendError(res: ServerResponse, status: number, errorCode: string, message: string, extra?: Record<string, unknown>): void;
74
+ export declare function sendError(res: ServerResponse, status: number, errorCode: string, message: string, extra?: Readonly<Record<string, unknown>>): void;
73
75
  /** B7([ref] lens1 §C3):`HttpError` 的兜底 catch-all 站点没有自己的语义分支——`err.code` 在 42 个
74
76
  * throw 点里只有 3 个带。为了让「errorCode 恒在」在这些站点也成立(而不是靠豁免表放行),按**状态码**
75
77
  * 给一个诚实的粗码:它只声明「这类拒绝」,不冒充比 throw 点更细的语义。带 `code` 的 throw 点原样优先。 */
76
78
  export declare function httpErrorCode(status: number, code?: string): string;
79
+ /**
80
+ * 一只 `HttpError` 读成 {@link Refusal}:码经 {@link httpErrorCode}(带码的 throw 原样优先,否则按状态给粗码),句 = `message`,机读事实 = `extra`
81
+ * (如场景门的 `detail: { scenario, source }`、条数帽的 `field` / `reason` / `count` / `cap`)。**拆 `HttpError` 的唯一一处** —— 别的站点自己拆
82
+ * `status` / `code` / `message` 拼体就丢了 `extra`(`test/refusal-one-composer.test.ts` 的静态门钉着)。
83
+ */
84
+ export declare function httpErrorRefusal(e: HttpError): Refusal;
85
+ /** 一只 `HttpError` 的拒体成员 = {@link refusalBody}({@link httpErrorRefusal})。 */
86
+ export declare function httpErrorBody(e: HttpError): RefusalBody;
87
+ /** 整面拒:状态 = `e.status`,体 = {@link httpErrorBody}。手里拿着一只 `HttpError` 的路由站点一律调它。 */
88
+ export declare function sendHttpError(res: ServerResponse, e: HttpError): void;
77
89
  /**
78
90
  * 🔴 B1 / S-535 —— `SessionPolicyError` → 应答,经 {@link sessionPolicyErrorHttp} 那**一只**映射
79
91
  * (`src/session-policy-wire.ts`)。回 `true` = 已应答,调用点 `return`。
package/dist/http/send.js CHANGED
@@ -2,6 +2,7 @@ import { SessionError, SessionPolicyError } from "@sema-agent/core";
2
2
  import { sessionPolicyErrorFacts, sessionPolicyErrorHttp, sessionPolicyErrorSentence } from "../session-policy-wire.js";
3
3
  import { sessionErrorHttp } from "../session-error-wire.js";
4
4
  import { redactErrorMessage } from "../observability/run-terminal.js";
5
+ import { refusalBody } from "./refusal-body.js";
5
6
  export const SSE_RESPONSE_MARK = Symbol("sema.http.sse-response");
6
7
  export function isSseResponse(res) {
7
8
  return res[SSE_RESPONSE_MARK] === true;
@@ -31,7 +32,7 @@ export function sendJson(res, status, body) {
31
32
  res.end(JSON.stringify(body));
32
33
  }
33
34
  export function sendError(res, status, errorCode, message, extra) {
34
- sendJson(res, status, { error: message, errorCode, ...extra });
35
+ sendJson(res, status, refusalBody(extra === undefined ? { errorCode, message } : { errorCode, message, facts: extra }));
35
36
  }
36
37
  export function httpErrorCode(status, code) {
37
38
  if (code)
@@ -48,6 +49,16 @@ export function httpErrorCode(status, code) {
48
49
  default: return status >= 500 ? "internal.error" : "request.rejected";
49
50
  }
50
51
  }
52
+ export function httpErrorRefusal(e) {
53
+ const errorCode = httpErrorCode(e.status, e.code);
54
+ return e.extra === undefined ? { errorCode, message: e.message } : { errorCode, message: e.message, facts: e.extra };
55
+ }
56
+ export function httpErrorBody(e) {
57
+ return refusalBody(httpErrorRefusal(e));
58
+ }
59
+ export function sendHttpError(res, e) {
60
+ sendJson(res, e.status, httpErrorBody(e));
61
+ }
51
62
  export function sendSessionPolicyError(res, e, sentencePrefix = "") {
52
63
  if (!(e instanceof SessionPolicyError))
53
64
  return false;