@sema-agent/server 7.137.0 → 7.139.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (328) hide show
  1. package/MIGRATION.md +600 -142
  2. package/README.md +7 -3
  3. package/README.zh-CN.md +6 -3
  4. package/USAGE.md +132 -41
  5. package/deploy/sema-up/README.md +1 -1
  6. package/deploy/sema-up/lib/lanes.json +3 -2
  7. package/dist/adoption/wire.d.ts +3 -3
  8. package/dist/approval-card.d.ts +23 -16
  9. package/dist/approval-card.js +1 -0
  10. package/dist/audit.js +1 -0
  11. package/dist/background-agent-name.d.ts +5 -0
  12. package/dist/background-agent-name.js +5 -0
  13. package/dist/bench/s1/arms.d.ts +1 -1
  14. package/dist/bench/s1/arms.js +1 -3
  15. package/dist/bench/s1/run-firm.js +2 -2
  16. package/dist/boot/budget-tracing.d.ts +1 -12
  17. package/dist/boot/deployment-mods.d.ts +46 -16
  18. package/dist/boot/deployment-mods.js +82 -6
  19. package/dist/boot/device-lane.d.ts +8 -3
  20. package/dist/boot/device-lane.js +7 -2
  21. package/dist/boot/execution-env.d.ts +4 -2
  22. package/dist/boot/execution-env.js +16 -24
  23. package/dist/boot/execution-lane.d.ts +1 -1
  24. package/dist/boot/governance-seams.d.ts +12 -18
  25. package/dist/boot/governance-seams.js +10 -24
  26. package/dist/boot/leader.d.ts +3 -0
  27. package/dist/boot/leader.js +1 -0
  28. package/dist/boot/memory-consolidation.js +2 -2
  29. package/dist/boot/org-delivery.js +15 -2
  30. package/dist/boot/resolve-spec.d.ts +4 -1
  31. package/dist/boot/resolve-spec.js +29 -19
  32. package/dist/boot/retention-lane.d.ts +14 -8
  33. package/dist/boot/retention-lane.js +2 -8
  34. package/dist/boot/runner-deps.d.ts +10 -4
  35. package/dist/boot/runner-deps.js +2 -0
  36. package/dist/boot/session-faces.d.ts +0 -3
  37. package/dist/boot/session-faces.js +15 -8
  38. package/dist/boot/stage-02-web-admin-pull.js +2 -2
  39. package/dist/boot/stage-04-stores.d.ts +1 -12
  40. package/dist/boot/stage-05-execution-env.d.ts +3 -12
  41. package/dist/boot/stage-05-execution-env.js +8 -5
  42. package/dist/boot/stage-06-runners.d.ts +3 -12
  43. package/dist/boot/stage-06-runners.js +5 -3
  44. package/dist/boot/stage-07-capability-layer.d.ts +3 -12
  45. package/dist/boot/stage-07-capability-layer.js +2 -2
  46. package/dist/boot/stage-08-reapers.d.ts +3 -12
  47. package/dist/boot/stage-09-leader.d.ts +3 -12
  48. package/dist/boot/stage-09-leader.js +2 -3
  49. package/dist/boot/stage-10-http-server.d.ts +3 -12
  50. package/dist/boot/stage-10-http-server.js +5 -3
  51. package/dist/boot/stores.js +4 -4
  52. package/dist/boot/web-admin.d.ts +5 -2
  53. package/dist/boot/web-admin.js +5 -5
  54. package/dist/brain.js +5 -5
  55. package/dist/budget.d.ts +17 -15
  56. package/dist/budget.js +2 -2
  57. package/dist/capabilities/mcp-probe.d.ts +36 -11
  58. package/dist/capabilities/mcp-probe.js +44 -5
  59. package/dist/capabilities/scenarios.d.ts +1 -1
  60. package/dist/capability-facts.d.ts +21 -7
  61. package/dist/capability-facts.js +13 -3
  62. package/dist/checkpoint-identity.d.ts +13 -6
  63. package/dist/config-catalog.js +14 -8
  64. package/dist/config-invariants.d.ts +23 -3
  65. package/dist/config-invariants.js +42 -0
  66. package/dist/config-provider.d.ts +19 -24
  67. package/dist/config-provider.js +27 -12
  68. package/dist/config-types.d.ts +25 -12
  69. package/dist/config.d.ts +33 -0
  70. package/dist/config.js +52 -20
  71. package/dist/delegated-session-owner.d.ts +2 -2
  72. package/dist/deployment-shape.d.ts +7 -7
  73. package/dist/device-enrollment.d.ts +41 -2
  74. package/dist/device-enrollment.js +27 -12
  75. package/dist/device-store.d.ts +58 -85
  76. package/dist/device-store.js +19 -49
  77. package/dist/device-ws-hub.js +2 -1
  78. package/dist/env-name-allowlist-knobs.d.ts +5 -4
  79. package/dist/exec-cut.d.ts +60 -0
  80. package/dist/exec-cut.js +36 -0
  81. package/dist/execution-lane-caps.d.ts +19 -0
  82. package/dist/execution-lane-caps.js +10 -0
  83. package/dist/execution-placement.d.ts +259 -0
  84. package/dist/execution-placement.js +199 -0
  85. package/dist/field-conflict.d.ts +3 -2
  86. package/dist/field-conflict.js +1 -0
  87. package/dist/gate-settlement.d.ts +71 -0
  88. package/dist/gate-settlement.js +40 -0
  89. package/dist/hooks/classic-stations.d.ts +1 -0
  90. package/dist/hooks/classic-stations.js +1 -0
  91. package/dist/hooks/command-spawn.d.ts +2 -2
  92. package/dist/hooks/hook-runner.js +14 -14
  93. package/dist/hosted-posture.d.ts +2 -2
  94. package/dist/http/admission.js +10 -19
  95. package/dist/http/checkpoint-ctx.d.ts +21 -12
  96. package/dist/http/checkpoint-ctx.js +4 -2
  97. package/dist/http/claimed-run.d.ts +1 -1
  98. package/dist/http/dispatch.d.ts +4 -0
  99. package/dist/http/dispatch.js +3 -0
  100. package/dist/http/durable-run-leg.js +12 -6
  101. package/dist/http/execution-context.d.ts +218 -0
  102. package/dist/http/execution-context.js +330 -0
  103. package/dist/http/memory-compliance-failure-reply.js +3 -0
  104. package/dist/http/resume-legs.d.ts +5 -11
  105. package/dist/http/resume-legs.js +179 -104
  106. package/dist/http/route-ctx.d.ts +44 -59
  107. package/dist/http/route-ctx.js +2 -5
  108. package/dist/http/routes/a2a-serve.js +10 -4
  109. package/dist/http/routes/agents-roster.d.ts +1 -1
  110. package/dist/http/routes/agents-roster.js +5 -1
  111. package/dist/http/routes/approvals-assistant.js +6 -6
  112. package/dist/http/routes/background.d.ts +29 -1
  113. package/dist/http/routes/background.js +29 -2
  114. package/dist/http/routes/capabilities.js +12 -10
  115. package/dist/http/routes/devices.d.ts +13 -19
  116. package/dist/http/routes/devices.js +21 -26
  117. package/dist/http/routes/mcp-reconnect.d.ts +3 -2
  118. package/dist/http/routes/mcp-reconnect.js +7 -5
  119. package/dist/http/routes/memory-consolidation.d.ts +1 -1
  120. package/dist/http/routes/memory-origin.d.ts +1 -1
  121. package/dist/http/routes/rules.js +3 -3
  122. package/dist/http/routes/runs.js +11 -7
  123. package/dist/http/routes/session-sync.js +110 -15
  124. package/dist/http/routes/sessions.d.ts +6 -0
  125. package/dist/http/routes/sessions.js +119 -13
  126. package/dist/http/routes/side-query.d.ts +12 -4
  127. package/dist/http/routes/side-query.js +6 -8
  128. package/dist/http/routes/tasks.js +800 -782
  129. package/dist/http/server.d.ts +61 -39
  130. package/dist/http/server.js +11 -0
  131. package/dist/http/session-inherit-env.d.ts +3 -4
  132. package/dist/http/session-inherit-env.js +1 -1
  133. package/dist/http/wire-types.d.ts +22 -18
  134. package/dist/identity-source.d.ts +1 -1
  135. package/dist/index.d.ts +3 -0
  136. package/dist/index.js +1 -0
  137. package/dist/leader/wire.d.ts +7 -0
  138. package/dist/leader/wire.js +5 -4
  139. package/dist/mcp-assertion-header.d.ts +51 -40
  140. package/dist/mcp-assertion-header.js +10 -11
  141. package/dist/mcp-authorization-face.d.ts +27 -0
  142. package/dist/mcp-authorization-face.js +15 -0
  143. package/dist/memory-operator-faces.d.ts +13 -4
  144. package/dist/memory-operator-faces.js +1 -17
  145. package/dist/memory-sync-client.d.ts +53 -14
  146. package/dist/memory-sync-client.js +187 -92
  147. package/dist/memory-sync.d.ts +1 -1
  148. package/dist/memory-sync.js +3 -2
  149. package/dist/model-route-gap.d.ts +6 -22
  150. package/dist/model-route-gap.js +7 -5
  151. package/dist/mods-declaration.d.ts +110 -0
  152. package/dist/mods-declaration.js +109 -0
  153. package/dist/observability/cost-taxonomy.d.ts +2 -2
  154. package/dist/observability/err-text.d.ts +5 -0
  155. package/dist/observability/err-text.js +9 -0
  156. package/dist/observability/fail-open.d.ts +17 -1
  157. package/dist/observability/fail-open.js +17 -1
  158. package/dist/observability/run-terminal.js +0 -1
  159. package/dist/observability/secret-env-scrub.d.ts +1 -1
  160. package/dist/observability/secret-env-scrub.js +1 -1
  161. package/dist/orchestration/workflow-notify-journal.js +4 -4
  162. package/dist/org-memory-admission.d.ts +4 -3
  163. package/dist/org-memory-admission.js +6 -6
  164. package/dist/parked-decide.d.ts +49 -11
  165. package/dist/parked-decide.js +25 -26
  166. package/dist/parked-revive-surface.d.ts +12 -15
  167. package/dist/parked-revive-surface.js +3 -6
  168. package/dist/peer-seat.d.ts +156 -0
  169. package/dist/peer-seat.js +273 -0
  170. package/dist/permission-rule-vocab.d.ts +7 -7
  171. package/dist/permission-rule-vocab.js +3 -3
  172. package/dist/plain-string-record.d.ts +38 -0
  173. package/dist/plain-string-record.js +38 -0
  174. package/dist/plugins/approval-ask-store-file.d.ts +7 -0
  175. package/dist/plugins/approval-ask-store-file.js +11 -0
  176. package/dist/plugins/approval-ask-store-memory.d.ts +6 -0
  177. package/dist/plugins/approval-ask-store-memory.js +12 -0
  178. package/dist/plugins/approval-ask-store-sql.d.ts +22 -5
  179. package/dist/plugins/approval-ask-store-sql.js +30 -12
  180. package/dist/plugins/background-agent-store-sql.js +6 -3
  181. package/dist/plugins/caching-session-store.d.ts +11 -2
  182. package/dist/plugins/caching-session-store.js +15 -3
  183. package/dist/plugins/checkpoint-store-sql.d.ts +21 -25
  184. package/dist/plugins/checkpoint-store-sql.js +13 -15
  185. package/dist/plugins/device-store-sql.d.ts +25 -51
  186. package/dist/plugins/device-store-sql.js +49 -103
  187. package/dist/plugins/execution-placement-sql.d.ts +151 -0
  188. package/dist/plugins/execution-placement-sql.js +223 -0
  189. package/dist/plugins/file-history-store-sql.d.ts +7 -6
  190. package/dist/plugins/file-history-store-sql.js +8 -7
  191. package/dist/plugins/file-run-store.d.ts +6 -1
  192. package/dist/plugins/file-run-store.js +4 -0
  193. package/dist/plugins/fork-routing-session-store.d.ts +6 -0
  194. package/dist/plugins/fork-routing-session-store.js +16 -0
  195. package/dist/plugins/k8s-pod-reaper.d.ts +2 -8
  196. package/dist/plugins/k8s-pod-reaper.js +3 -3
  197. package/dist/plugins/local-checkpoint-store.d.ts +1 -11
  198. package/dist/plugins/local-checkpoint-store.js +0 -1
  199. package/dist/plugins/local-session-store.d.ts +183 -45
  200. package/dist/plugins/local-session-store.js +412 -105
  201. package/dist/plugins/mailbox-store-sql.d.ts +11 -3
  202. package/dist/plugins/mailbox-store-sql.js +103 -15
  203. package/dist/plugins/memory-key-guards.d.ts +4 -0
  204. package/dist/plugins/memory-key-guards.js +1 -0
  205. package/dist/plugins/memory-run-store.d.ts +1 -1
  206. package/dist/plugins/memory-sync-store-pg.d.ts +16 -5
  207. package/dist/plugins/memory-sync-store-tidb.d.ts +3 -3
  208. package/dist/plugins/one-time-migrations.d.ts +117 -6
  209. package/dist/plugins/one-time-migrations.js +383 -14
  210. package/dist/plugins/outcome-ledger-sql.d.ts +2 -2
  211. package/dist/plugins/permission-rule-store-sql.d.ts +13 -15
  212. package/dist/plugins/permission-rule-store-sql.js +34 -29
  213. package/dist/plugins/pg-pool.d.ts +7 -3
  214. package/dist/plugins/pg-session-storage.d.ts +41 -22
  215. package/dist/plugins/pg-session-storage.js +136 -55
  216. package/dist/plugins/remote-env-adb.d.ts +22 -4
  217. package/dist/plugins/remote-env-adb.js +70 -62
  218. package/dist/plugins/remote-env-device.d.ts +32 -12
  219. package/dist/plugins/remote-env-device.js +18 -17
  220. package/dist/plugins/remote-env-e2b.d.ts +19 -3
  221. package/dist/plugins/remote-env-e2b.js +41 -42
  222. package/dist/plugins/remote-env-file-error.d.ts +2 -0
  223. package/dist/plugins/remote-env-file-error.js +1 -1
  224. package/dist/plugins/remote-env-host.d.ts +23 -8
  225. package/dist/plugins/remote-env-host.js +66 -70
  226. package/dist/plugins/remote-env-k8s.d.ts +13 -16
  227. package/dist/plugins/remote-env-k8s.js +61 -54
  228. package/dist/plugins/remote-env-local-docker.d.ts +30 -5
  229. package/dist/plugins/remote-env-local-docker.js +46 -59
  230. package/dist/plugins/remote-env-ssh.d.ts +18 -4
  231. package/dist/plugins/remote-env-ssh.js +72 -56
  232. package/dist/plugins/remote-shell.d.ts +100 -3
  233. package/dist/plugins/remote-shell.js +166 -2
  234. package/dist/plugins/retention-store-sql.d.ts +23 -8
  235. package/dist/plugins/retention-store-sql.js +13 -1
  236. package/dist/plugins/run-store-sql.d.ts +13 -3
  237. package/dist/plugins/run-store-sql.js +5 -0
  238. package/dist/plugins/schema-consistency.d.ts +15 -5
  239. package/dist/plugins/schema-consistency.js +14 -8
  240. package/dist/plugins/schema-drift-sentence.d.ts +27 -0
  241. package/dist/plugins/schema-drift-sentence.js +38 -0
  242. package/dist/plugins/session-fork.d.ts +63 -12
  243. package/dist/plugins/session-fork.js +38 -21
  244. package/dist/plugins/session-ownership.d.ts +7 -0
  245. package/dist/plugins/session-ownership.js +5 -0
  246. package/dist/plugins/session-row-sql.d.ts +22 -0
  247. package/dist/plugins/session-row-sql.js +81 -0
  248. package/dist/plugins/session-store.d.ts +1 -1
  249. package/dist/plugins/session-workspace-cell.d.ts +17 -0
  250. package/dist/plugins/session-workspace-cell.js +38 -0
  251. package/dist/plugins/store-backend.d.ts +1 -1
  252. package/dist/plugins/store-backend.js +26 -14
  253. package/dist/plugins/store-contracts.d.ts +25 -5
  254. package/dist/plugins/tidb-pool.d.ts +12 -7
  255. package/dist/plugins/tidb-pool.js +15 -3
  256. package/dist/plugins/tidb-session-store.d.ts +74 -28
  257. package/dist/plugins/tidb-session-store.js +132 -53
  258. package/dist/plugins/tool-result-store-sql.d.ts +16 -8
  259. package/dist/plugins/tool-result-store-sql.js +26 -17
  260. package/dist/plugins/worktree-isolation.d.ts +3 -2
  261. package/dist/plugins/worktree-isolation.js +1 -7
  262. package/dist/project-identity.d.ts +1 -14
  263. package/dist/project-identity.js +0 -4
  264. package/dist/project-memory.d.ts +9 -15
  265. package/dist/project-memory.js +5 -4
  266. package/dist/request-key-closure.js +1 -1
  267. package/dist/request-value-closure.d.ts +6 -7
  268. package/dist/request-value-closure.js +1 -3
  269. package/dist/rules-consent.js +6 -4
  270. package/dist/run-local.d.ts +6 -0
  271. package/dist/run-local.js +36 -26
  272. package/dist/runs.d.ts +9 -2
  273. package/dist/runs.js +9 -2
  274. package/dist/sandbox-git-plane.d.ts +2 -2
  275. package/dist/security.d.ts +38 -12
  276. package/dist/security.js +8 -2
  277. package/dist/server-secret-env.d.ts +15 -8
  278. package/dist/server-secret-env.js +4 -2
  279. package/dist/session-children-retire.d.ts +27 -9
  280. package/dist/session-children-retire.js +9 -5
  281. package/dist/session-incarnation.d.ts +19 -10
  282. package/dist/session-incarnation.js +5 -3
  283. package/dist/session-row.d.ts +114 -5
  284. package/dist/session-row.js +55 -5
  285. package/dist/session-titler.d.ts +2 -2
  286. package/dist/session-workspace.d.ts +130 -0
  287. package/dist/session-workspace.js +83 -0
  288. package/dist/spec-fields.d.ts +1 -1
  289. package/dist/standing-answer-grant.d.ts +2 -13
  290. package/dist/standing-answer-grant.js +0 -7
  291. package/dist/task-a2a.d.ts +44 -14
  292. package/dist/task-a2a.js +46 -20
  293. package/dist/task-cwd.d.ts +6 -1
  294. package/dist/task-mcp.d.ts +33 -20
  295. package/dist/task-mcp.js +46 -22
  296. package/dist/tool-approval.d.ts +38 -8
  297. package/dist/tool-approval.js +57 -13
  298. package/dist/trace/core-keyset-guard.d.ts +33 -18
  299. package/dist/trace/engine-notice-wire.d.ts +1 -1
  300. package/dist/trace/engine-notice-wire.js +2 -0
  301. package/dist/trace/exit-seat.d.ts +20 -1
  302. package/dist/trace/exit-seat.js +3 -0
  303. package/dist/trace/extension-call-face.d.ts +33 -0
  304. package/dist/trace/extension-call-face.js +39 -0
  305. package/dist/trace/project.d.ts +26 -7
  306. package/dist/trace/project.js +30 -3
  307. package/dist/trace/redact.js +42 -13
  308. package/dist/trace/self-minted-wire.d.ts +240 -12
  309. package/dist/trace/self-minted-wire.js +100 -4
  310. package/dist/trace/wire-projection-faces.d.ts +6 -6
  311. package/dist/trace/wire-projection-faces.js +6 -5
  312. package/dist/url-refusal.d.ts +1 -1
  313. package/dist/user-authored-text.d.ts +2 -2
  314. package/dist/user-authored-text.js +16 -1
  315. package/dist/web-admin-client/apply-effective.d.ts +17 -3
  316. package/dist/web-admin-client/apply-effective.js +20 -10
  317. package/dist/web-admin-client/center-request.d.ts +3 -2
  318. package/dist/web-admin-client/center-request.js +2 -3
  319. package/dist/web-admin-client/skills-mcp.d.ts +8 -4
  320. package/dist/web-admin-client/skills-mcp.js +32 -18
  321. package/dist/web-admin-client/types.d.ts +11 -0
  322. package/package.json +3 -3
  323. package/wire-changes/server@7.138.0.json +446 -0
  324. package/wire-changes/server@7.139.0.json +348 -0
  325. package/dist/http/device-placement.d.ts +0 -175
  326. package/dist/http/device-placement.js +0 -145
  327. package/dist/http/mcp-remote-text.d.ts +0 -5
  328. package/dist/http/mcp-remote-text.js +0 -7
@@ -3,7 +3,7 @@ import { a2aForScenario, mcpForScenario } from "../../web-admin-client/facade.js
3
3
  import { HttpError, isUuidShape, legacyUnownedSessionOk, sessionOwnerCellOf } from "../../security.js";
4
4
  import { windowMessages, truncateMessageBlobs, redactSessionViewForRead } from "../../audit.js";
5
5
  import { redactSecrets } from "../../trace/redact.js";
6
- import { redactMcpRemoteText } from "../mcp-remote-text.js";
6
+ import { seatText } from "../../trace/exit-seat.js";
7
7
  import { recordFailOpen } from "../../observability/fail-open.js";
8
8
  import { MCP_DIAL_DEADLINE_MS, raceDeadline } from "../mcp-dial-deadline.js";
9
9
  import { sentMemoryComplianceFailure } from "../memory-compliance-failure-reply.js";
@@ -18,7 +18,12 @@ import * as facts from "../../capability-facts.js";
18
18
  import { rosterAgentId } from "./agents-roster.js";
19
19
  import { projectSessionListRow } from "./sessions-list.js";
20
20
  import { readSessionRowPatch } from "../../session-row.js";
21
+ import { forkPathOf } from "../execution-context.js";
22
+ import { executionPlacementView } from "../../execution-placement.js";
23
+ import { ForkTargetUnsettledError, forkKeyOf } from "../../plugins/session-fork.js";
24
+ import { fieldConflictFacts } from "../../field-conflict.js";
21
25
  import { buildBoundedToolNames } from "../../panel-tool-names.js";
26
+ import { projectMcpAuthorization } from "../../mcp-authorization-face.js";
22
27
  const MCP_STATUS_TTL_MS = 5_000;
23
28
  const A2A_STATUS_TTL_MS = 5_000;
24
29
  const A2A_MATERIALIZE_TIMEOUT_MS = 10_000;
@@ -171,7 +176,7 @@ export async function handleSessions(req, res, match, ctx) {
171
176
  mcpStatusCache.clear();
172
177
  const panel = (async () => {
173
178
  const stopDial = new AbortController();
174
- const matP = materializeMcpTools(specs, principal, undefined, undefined, undefined, deps.mcpRevocations, undefined, undefined, undefined, stopDial.signal);
179
+ const matP = materializeMcpTools(specs, principal, undefined, undefined, undefined, deps.mcpRevocations, undefined, undefined, undefined, stopDial.signal, undefined, deps.peerDesk.seat);
175
180
  const mat = await raceDeadline(matP, MCP_DIAL_DEADLINE_MS).catch(() => null);
176
181
  if (!mat) {
177
182
  stopDial.abort();
@@ -186,7 +191,8 @@ export async function handleSessions(req, res, match, ctx) {
186
191
  status: s.status,
187
192
  ...(s.serverInfo ? { serverInfo: s.serverInfo } : {}),
188
193
  ...(s.toolNames ? { toolNames: s.toolNames } : {}),
189
- ...(s.error ? { error: redactMcpRemoteText(String(s.error)) } : {}),
194
+ ...((a) => (a !== undefined ? { authorization: a } : {}))(projectMcpAuthorization(s.authorization)),
195
+ ...(s.error ? { error: seatText("mcp-remote-text", String(s.error)) } : {}),
190
196
  })),
191
197
  };
192
198
  }
@@ -219,7 +225,7 @@ export async function handleSessions(req, res, match, ctx) {
219
225
  if (a2aStatusCache.size > 1024)
220
226
  a2aStatusCache.clear();
221
227
  const panel = (async () => {
222
- const matP = materializeA2aTools(specs, principal);
228
+ const matP = materializeA2aTools(specs, principal, undefined, undefined, deps.peerDesk.seat);
223
229
  let timer;
224
230
  const mat = await Promise.race([matP.catch(() => null), new Promise((r) => { timer = setTimeout(() => r(null), A2A_MATERIALIZE_TIMEOUT_MS); })]);
225
231
  if (timer)
@@ -284,6 +290,29 @@ export async function handleSessions(req, res, match, ctx) {
284
290
  return;
285
291
  }
286
292
  }
293
+ {
294
+ if (match.decl.id === "session-execution-placement") {
295
+ const placementFact = facts.executionPlacementRead(deps);
296
+ if (!placementFact.available) {
297
+ sendError(res, 501, "capability.session_store_required", "the execution placement read requires a durable session store (DB_BACKEND=mysql|pg|local)");
298
+ return;
299
+ }
300
+ const placementSession = safeDecode(match.params.sessionId);
301
+ if (placementSession === null) {
302
+ sendError(res, 400, "request.path_malformed", "session id segment is not valid percent-encoding");
303
+ return;
304
+ }
305
+ const reading = await placementFact.seat.read(placementSession);
306
+ if (reading === undefined) {
307
+ sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
308
+ return;
309
+ }
310
+ if (!explicitOperatorOk(gatedPrincipal(req, deps.config), deps.config) && !sessionOwnerOk(req, res, await sessionOwnerCellOf(deps.sessionStorage, placementSession, reading.occupant.owner)))
311
+ return;
312
+ sendJson(res, 200, executionPlacementView(reading.cell));
313
+ return;
314
+ }
315
+ }
287
316
  {
288
317
  if (match.decl.id === "session-events") {
289
318
  const evFact = facts.sessionEventStream(deps);
@@ -673,13 +702,25 @@ export async function handleSessions(req, res, match, ctx) {
673
702
  sendError(res, 400, read.problem.code, read.problem.message);
674
703
  return;
675
704
  }
676
- const row = await store.patchSessionMeta(sessionId, read.patch, face);
677
- if (row === undefined) {
678
- sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
679
- return;
705
+ const outcome = await store.writeSessionRowCells(sessionId, read.patch, { writer: "client", row: { row: face }, ...(read.expectedRowTag !== undefined ? { rowTag: read.expectedRowTag } : {}) });
706
+ switch (outcome.kind) {
707
+ case "written":
708
+ sendJson(res, 200, projectSessionListRow(outcome.row));
709
+ return;
710
+ case "changed":
711
+ sendError(res, 409, "conflict.session_row_changed", "the session row changed since the rowTag you sent was read — nothing was written; currentRow is the row as it stands now: decide again, then send its rowTag", { currentRow: projectSessionListRow(outcome.row) });
712
+ return;
713
+ case "mirror":
714
+ sendError(res, 409, "conflict.session_mirror", "this session is a read-only mirror of a session whose writer is elsewhere — it cannot be renamed or pinned here; fork it (forkPath) to continue here; nothing was written", { forkPath: forkPathOf(sessionId) });
715
+ return;
716
+ case "absent":
717
+ sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
718
+ return;
719
+ default: {
720
+ const unknownOutcome = outcome;
721
+ throw new Error(`PATCH /v1/sessions/:id: no answer for the row write outcome ${JSON.stringify(unknownOutcome)}`);
722
+ }
680
723
  }
681
- sendJson(res, 200, projectSessionListRow(row));
682
- return;
683
724
  }
684
725
  {
685
726
  if (match.decl.id === "session-fork") {
@@ -710,12 +751,57 @@ export async function handleSessions(req, res, match, ctx) {
710
751
  return;
711
752
  }
712
753
  const newOwner = scope.fleetWide ? (owner ?? null) : scope.gateOwner;
713
- const newId = await fork(sourceId, newOwner);
714
- if (newId === null) {
754
+ const keyed = readForkTarget(await readJson(req), sourceId);
755
+ if (keyed.kind === "refused") {
756
+ sendError(res, 400, keyed.errorCode, keyed.message, keyed.facts);
757
+ return;
758
+ }
759
+ if (keyed.kind === "none") {
760
+ const newId = await fork(sourceId, newOwner);
761
+ if (newId === null) {
762
+ sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
763
+ return;
764
+ }
765
+ sendJson(res, 200, { sessionId: newId });
766
+ return;
767
+ }
768
+ const forkTo = forkFact.sessionStorage.forkTo?.bind(forkFact.sessionStorage);
769
+ if (forkTo === undefined) {
770
+ sendError(res, 501, "capability.session_store_required", "a keyed fork (forkId + forkNonce) requires a session store that creates the target row atomically (DB_BACKEND=mysql|pg|local)");
771
+ return;
772
+ }
773
+ let outcome;
774
+ try {
775
+ outcome = await forkTo(sourceId, newOwner, { id: keyed.forkId, key: forkKeyOf(sourceId, keyed.forkNonce) });
776
+ }
777
+ catch (e) {
778
+ if (e instanceof ForkTargetUnsettledError) {
779
+ sendError(res, 503, "state.fork_target_unsettled", e.message, { permanence: "transient" });
780
+ return;
781
+ }
782
+ throw e;
783
+ }
784
+ if (outcome === null) {
715
785
  sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
716
786
  return;
717
787
  }
718
- sendJson(res, 200, { sessionId: newId });
788
+ switch (outcome.outcome) {
789
+ case "created":
790
+ sendJson(res, 200, { sessionId: outcome.id });
791
+ return;
792
+ case "replayed":
793
+ sendJson(res, 200, { sessionId: outcome.id, replayed: true });
794
+ return;
795
+ case "occupied":
796
+ if (!outcome.sameOwner) {
797
+ sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
798
+ return;
799
+ }
800
+ sendError(res, 409, "conflict.fork_id_taken", "forkId already names another session of yours (a different fork, or a session that was not created by this fork request) — pick a new forkId and forkNonce; nothing was forked", { forkId: keyed.forkId });
801
+ return;
802
+ default:
803
+ assertNever(outcome);
804
+ }
719
805
  return;
720
806
  }
721
807
  }
@@ -851,6 +937,7 @@ export const SESSIONS_ROUTES = [
851
937
  { id: "session-mcp", pattern: /^\/v1\/sessions\/(?<sessionId>[^/]+)\/mcp$/, label: "/v1/sessions/:id/$verb", methods: ["GET"] },
852
938
  { id: "session-a2a", pattern: /^\/v1\/sessions\/(?<sessionId>[^/]+)\/a2a$/, label: "/v1/sessions/:id/$verb", methods: ["GET"] },
853
939
  { id: "session-head", pattern: /^\/v1\/sessions\/(?<sessionId>[^/]+)\/head$/, label: "/v1/sessions/:id/$verb", methods: ["GET"] },
940
+ { id: "session-execution-placement", pattern: /^\/v1\/sessions\/(?<sessionId>[^/]+)\/execution-placement$/, label: "/v1/sessions/:id/execution-placement", credentialGated: ["GET"], methods: ["GET"] },
854
941
  { id: "session-events", pattern: /^\/v1\/sessions\/(?<sessionId>[^/]+)\/events$/, label: "/v1/sessions/:id/$verb", methods: ["GET"], subscription: true },
855
942
  { id: "session-fork", pattern: /^\/v1\/sessions\/(?<sessionId>[^/]+)\/fork$/, label: "/v1/sessions/:id/$verb", methods: ["POST"] },
856
943
  { id: "session-policy-get", pattern: /^\/v1\/sessions\/(?<sessionId>[^/]+)\/policy$/, label: "/v1/sessions/:id/$verb", methods: ["GET"] },
@@ -862,4 +949,23 @@ export const SESSIONS_ROUTES = [
862
949
  { id: "session-patch", pattern: /^\/v1\/sessions\/(?<sessionId>[^/]+)$/, label: "/v1/sessions/:id", methods: ["PATCH"] },
863
950
  { id: "session-delete", pattern: /^\/v1\/sessions\/(?<sessionId>[^/]+)$/, label: "/v1/sessions/:id", methods: ["DELETE"] },
864
951
  ];
952
+ function readForkTarget(raw, sourceId) {
953
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw))
954
+ return { kind: "refused", errorCode: "request.field_invalid", message: "the fork body must be an object { forkId?, forkNonce? }" };
955
+ const { forkId, forkNonce } = raw;
956
+ if (forkId === undefined && forkNonce === undefined)
957
+ return { kind: "none" };
958
+ if (forkId === undefined || forkNonce === undefined) {
959
+ return { kind: "refused", errorCode: "request.field_conflict", message: "forkId and forkNonce come together — send both (a keyed, retryable fork) or neither (a fork under a freshly minted id)", facts: fieldConflictFacts("fork_target", ["forkId", "forkNonce"]) };
960
+ }
961
+ if (typeof forkId !== "string" || !isUuidShape(forkId))
962
+ return { kind: "refused", errorCode: "request.id_invalid", message: "forkId must be a canonical uuid (the id the forked session will have)" };
963
+ if (forkId === sourceId)
964
+ return { kind: "refused", errorCode: "request.field_invalid", message: "forkId must name a new session — it equals the session being forked" };
965
+ if (typeof forkNonce !== "string" || forkNonce.length === 0 || forkNonce.length > FORK_NONCE_MAX_CHARS) {
966
+ return { kind: "refused", errorCode: "request.field_invalid", message: `forkNonce must be a non-empty string of at most ${FORK_NONCE_MAX_CHARS} characters (the caller's key for this fork request; a retry sends the same one)` };
967
+ }
968
+ return { kind: "keyed", forkId, forkNonce };
969
+ }
970
+ const FORK_NONCE_MAX_CHARS = 255;
865
971
  //# sourceMappingURL=sessions.js.map
@@ -8,10 +8,18 @@
8
8
  */
9
9
  import type { IncomingMessage, ServerResponse } from "node:http";
10
10
  import type { RouteCtx, RouteMatch, RouteIdsOf } from "../route-ctx.js";
11
- /** 表内=core 的前置校验形(客户端可改请求)⇒ 400;表外一律 500。 */
12
- export declare function isCorePreflightValidationError(message: string): boolean;
13
- /** 供跨仓锚门读:门要拿这张表去装机的 core 里逐条对账(见上方 doc)。 */
14
- export declare const CORE_PREFLIGHT_VALIDATION_ANCHORS: readonly string[];
11
+ /**
12
+ * [ref] —— core 前置校验异常的分类(400 客户端可改 / 500 我方缺陷):**只读引擎的码**。
13
+ * 引擎角色族的拒(未知模型引用 / 不是模型引用 / 角色词出表 / 角色链给不出模型)一律带码 `config.unknown_role`(`detail.reason` 分臂;
14
+ * core 7.60.0 [ref] 起最后一臂也带码)—— 这是引擎写明的判别位,本路由的 400 只认它;句子不读(修前是一张按英文开头匹配的两行前缀表,
15
+ * 引擎改一个字就会把客户端错误静默翻成 500)。失败方向:别的码 / 没有码一律 500(响亮、可告警、可重试),绝不把不认识的异常猜成客户端错误。
16
+ * 跨仓锚:test/side-query-http.test.ts 调装机 core 的两个导出解析器真抛,拿真错误过本谓词。
17
+ *
18
+ * 🪦 原表还有第三臂 `requires a non-empty messages array`(core `core/side-query.js`)—— 不可达(本路由在调 core 之前就有
19
+ * `!Array.isArray(body.messages) || body.messages.length === 0 ⇒ 400 request.body_shape` 的前拦),已删;前拦一旦被摘,
20
+ * side-query-http 的「空/非数组 messages → 400 request.body_shape」两格先红。
21
+ */
22
+ export declare function isCorePreflightValidationError(err: unknown): boolean;
15
23
  /** 路由体 = 从 `server.ts` 的 `handle()` 里**整段剪切**过来的原文(唯一改动:统一去缩进两格)。 */
16
24
  export declare function handleSideQuery(req: IncomingMessage, res: ServerResponse, match: RouteMatch<SideQueryRouteId>, ctx: RouteCtx): Promise<void>;
17
25
  export declare const SIDE_QUERY_ROUTES: readonly [{
@@ -4,15 +4,14 @@ import { echoCallerText } from "../../caller-text-echo.js";
4
4
  import { isAtMentionable, matchCatalogModel, refusedCatalogEntry, selectableCatalog } from "../../model-select.js";
5
5
  import { echoModelRef, modelUnavailableMessage } from "../../model-entry-refusal.js";
6
6
  import { redactSecrets } from "../../trace/redact.js";
7
+ import { errorCodeOf } from "../../observability/err-text.js";
7
8
  import { admitProviderEcho, redactErrorMessage } from "../../observability/run-terminal.js";
8
9
  import { sendJson, sendError, sendHttpError, msg } from "../send.js";
9
10
  import { gatedPrincipal, sendPrincipalRefusal } from "../principal-gate.js";
10
11
  import { assertNever } from "../route-ctx.js";
11
- const CORE_PREFLIGHT_VALIDATION_PREFIXES = ["Unknown model ref ", "No model for role "];
12
- export function isCorePreflightValidationError(message) {
13
- return CORE_PREFLIGHT_VALIDATION_PREFIXES.some((prefix) => message.startsWith(prefix));
12
+ export function isCorePreflightValidationError(err) {
13
+ return errorCodeOf(err) === "config.unknown_role";
14
14
  }
15
- export const CORE_PREFLIGHT_VALIDATION_ANCHORS = CORE_PREFLIGHT_VALIDATION_PREFIXES;
16
15
  const MODEL_ROLE_ECHO_CHARS = 80;
17
16
  function isModelRole(value) {
18
17
  return typeof value === "string" && MODEL_ROLES.includes(value);
@@ -95,9 +94,8 @@ export async function handleSideQuery(req, res, match, ctx) {
95
94
  };
96
95
  try {
97
96
  const result = await deps.sideQuery(spec);
98
- const rr = result;
99
- const modelId = typeof rr.model === "string" ? rr.model : "unknown";
100
- deps.sideQueryAccounting?.(principal, { model: modelId, usage: rr.usage });
97
+ const modelId = typeof result.model === "string" ? result.model : "unknown";
98
+ deps.sideQueryAccounting?.(principal, { model: modelId, usage: result.usage, account: result.account });
101
99
  deps.metrics?.inc?.("side_query_total", { result: "ok" });
102
100
  if (!res.writableEnded)
103
101
  sendJson(res, 200, projectSideQueryResult(result));
@@ -106,7 +104,7 @@ export async function handleSideQuery(req, res, match, ctx) {
106
104
  deps.metrics?.inc?.("side_query_total", { result: "error" });
107
105
  if (!res.writableEnded && !ac.signal.aborted) {
108
106
  const m = msg(e);
109
- const isValidation = isCorePreflightValidationError(m);
107
+ const isValidation = isCorePreflightValidationError(e);
110
108
  sendError(res, isValidation ? 400 : 500, isValidation ? "request.field_invalid" : "internal.error", redactSecrets(m));
111
109
  }
112
110
  }