@sema-agent/server 7.148.0 → 7.149.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 (247) hide show
  1. package/MIGRATION.md +429 -0
  2. package/README.md +16 -1
  3. package/README.zh-CN.md +4 -0
  4. package/USAGE.md +89 -55
  5. package/dist/adoption/wire.d.ts +3 -3
  6. package/dist/approval-card.d.ts +32 -32
  7. package/dist/approval-leg-origin.d.ts +33 -0
  8. package/dist/approval-leg-origin.js +4 -0
  9. package/dist/auth-bridge.d.ts +9 -1
  10. package/dist/auth-bridge.js +4 -4
  11. package/dist/auth-keys.d.ts +10 -1
  12. package/dist/auth-keys.js +8 -0
  13. package/dist/boot/execution-env.js +10 -11
  14. package/dist/boot/execution-lane.d.ts +6 -4
  15. package/dist/boot/execution-lane.js +3 -1
  16. package/dist/boot/org-delivery.d.ts +3 -2
  17. package/dist/boot/resolve-spec.js +6 -4
  18. package/dist/boot/runner-deps.js +3 -1
  19. package/dist/boot/stage-01-config.d.ts +1 -0
  20. package/dist/boot/stage-01-config.js +4 -3
  21. package/dist/boot/stage-02-web-admin-pull.d.ts +1 -0
  22. package/dist/boot/stage-02-web-admin-pull.js +10 -2
  23. package/dist/boot/stage-03-store-backend.d.ts +1 -0
  24. package/dist/boot/stage-03-store-backend.js +2 -2
  25. package/dist/boot/stage-04-stores.d.ts +1 -0
  26. package/dist/boot/stage-05-execution-env.d.ts +1 -0
  27. package/dist/boot/stage-06-runners.d.ts +2 -1
  28. package/dist/boot/stage-07-capability-layer.d.ts +2 -1
  29. package/dist/boot/stage-08-reapers.d.ts +2 -1
  30. package/dist/boot/stage-09-leader.d.ts +2 -1
  31. package/dist/boot/stage-10-http-server.d.ts +2 -1
  32. package/dist/boot/stores.d.ts +3 -0
  33. package/dist/boot/stores.js +22 -19
  34. package/dist/capabilities/tool-defer.d.ts +1 -1
  35. package/dist/capability-facts.d.ts +5 -4
  36. package/dist/capability-facts.js +3 -14
  37. package/dist/card-settle-stored-scope.d.ts +56 -0
  38. package/dist/card-settle-stored-scope.js +27 -0
  39. package/dist/card-void.d.ts +7 -12
  40. package/dist/card-void.js +2 -7
  41. package/dist/config-catalog.d.ts +12 -0
  42. package/dist/config-catalog.js +36 -32
  43. package/dist/config-invariants.d.ts +5 -4
  44. package/dist/config-invariants.js +4 -4
  45. package/dist/config-types.d.ts +37 -5
  46. package/dist/config.d.ts +6 -5
  47. package/dist/config.js +35 -24
  48. package/dist/context-window-notice.d.ts +31 -0
  49. package/dist/context-window-notice.js +28 -0
  50. package/dist/deployment-shape.d.ts +20 -11
  51. package/dist/deployment-shape.js +1 -0
  52. package/dist/env-name-allowlist-knobs.d.ts +6 -5
  53. package/dist/env-name-allowlist-knobs.js +5 -0
  54. package/dist/execution-lane-caps.d.ts +20 -5
  55. package/dist/execution-lane-caps.js +18 -4
  56. package/dist/hooks/command-spawn.d.ts +7 -2
  57. package/dist/hooks/command-spawn.js +13 -6
  58. package/dist/hooks/hook-runner.d.ts +5 -0
  59. package/dist/hooks/hook-runner.js +11 -7
  60. package/dist/http/admission.js +3 -3
  61. package/dist/http/approval-sweeps.js +3 -2
  62. package/dist/http/claim-heartbeat.d.ts +3 -3
  63. package/dist/http/claim-heartbeat.js +3 -3
  64. package/dist/http/claimed-run.d.ts +16 -9
  65. package/dist/http/claimed-run.js +8 -0
  66. package/dist/http/dispatch.js +5 -3
  67. package/dist/http/durable-run-leg.d.ts +8 -1
  68. package/dist/http/durable-run-leg.js +30 -11
  69. package/dist/http/execution-context.d.ts +115 -14
  70. package/dist/http/execution-context.js +130 -40
  71. package/dist/http/owner-access.d.ts +3 -2
  72. package/dist/http/owner-access.js +2 -3
  73. package/dist/http/principal-gate.d.ts +18 -11
  74. package/dist/http/principal-gate.js +3 -1
  75. package/dist/http/resume-ctx-read.d.ts +14 -3
  76. package/dist/http/resume-ctx-read.js +8 -2
  77. package/dist/http/resume-legs.d.ts +35 -3
  78. package/dist/http/resume-legs.js +244 -92
  79. package/dist/http/route-ctx.d.ts +6 -0
  80. package/dist/http/routes/a2a-serve.js +2 -0
  81. package/dist/http/routes/approvals-assistant.js +3 -2
  82. package/dist/http/routes/capabilities.js +1 -1
  83. package/dist/http/routes/diagnostics.d.ts +3 -1
  84. package/dist/http/routes/diagnostics.js +30 -5
  85. package/dist/http/routes/principal-inbox.d.ts +3 -1
  86. package/dist/http/routes/principal-inbox.js +3 -2
  87. package/dist/http/routes/runs.js +10 -7
  88. package/dist/http/routes/session-sync.d.ts +24 -5
  89. package/dist/http/routes/session-sync.js +46 -34
  90. package/dist/http/routes/sessions-list.d.ts +1 -1
  91. package/dist/http/routes/tasks.js +127 -93
  92. package/dist/http/server.d.ts +4 -5
  93. package/dist/http/server.js +30 -5
  94. package/dist/http/submit-ctx.d.ts +1 -0
  95. package/dist/http/submit-ctx.js +2 -3
  96. package/dist/index.d.ts +3 -1
  97. package/dist/index.js +1 -0
  98. package/dist/leg-facts.d.ts +7 -2
  99. package/dist/leg-facts.js +1 -0
  100. package/dist/local-store-declaration.d.ts +54 -0
  101. package/dist/local-store-declaration.js +59 -0
  102. package/dist/mcp-assertion-header.d.ts +1 -1
  103. package/dist/mcp-assertion-header.js +1 -1
  104. package/dist/memory-project-layer.js +1 -1
  105. package/dist/memory-scope.d.ts +4 -4
  106. package/dist/memory-scope.js +4 -4
  107. package/dist/memory-sync-client.js +2 -2
  108. package/dist/model-context-window.d.ts +50 -11
  109. package/dist/model-context-window.js +20 -1
  110. package/dist/model-entry-refusal.d.ts +5 -4
  111. package/dist/model-entry-refusal.js +17 -3
  112. package/dist/model-reach-scope.d.ts +2 -1
  113. package/dist/observability/fail-open.d.ts +14 -2
  114. package/dist/observability/fail-open.js +14 -2
  115. package/dist/observability/metrics.js +1 -1
  116. package/dist/observability/run-terminal.js +1 -0
  117. package/dist/observability/secret-env-scrub.d.ts +1 -1
  118. package/dist/observability/secret-env-scrub.js +1 -1
  119. package/dist/org-delivery-gate.d.ts +31 -6
  120. package/dist/org-delivery-gate.js +7 -4
  121. package/dist/owner-faces.d.ts +3 -2
  122. package/dist/park-row-commit.d.ts +67 -0
  123. package/dist/park-row-commit.js +76 -0
  124. package/dist/parked-decide.d.ts +9 -7
  125. package/dist/parked-decide.js +25 -6
  126. package/dist/peer-directory.d.ts +1 -1
  127. package/dist/plugins/adoption-log-sql.d.ts +3 -3
  128. package/dist/plugins/adoption-log-sql.js +19 -19
  129. package/dist/plugins/approval-ask-store-sql.d.ts +1 -1
  130. package/dist/plugins/approval-ask-store-sql.js +3 -5
  131. package/dist/plugins/approval-exemption-skill-cutoff.d.ts +64 -0
  132. package/dist/plugins/approval-exemption-skill-cutoff.js +122 -0
  133. package/dist/plugins/approval-exemption-skill-m46.d.ts +0 -6
  134. package/dist/plugins/approval-exemption-skill-m46.js +6 -10
  135. package/dist/plugins/approval-exemption-store.d.ts +26 -1
  136. package/dist/plugins/approval-exemption-store.js +28 -19
  137. package/dist/plugins/ensure-schema-lock-policy.d.ts +36 -3
  138. package/dist/plugins/ensure-schema-lock-policy.js +75 -9
  139. package/dist/plugins/file-run-store.d.ts +22 -9
  140. package/dist/plugins/file-run-store.js +50 -22
  141. package/dist/plugins/local-checkpoint-store.d.ts +4 -0
  142. package/dist/plugins/local-checkpoint-store.js +2 -1
  143. package/dist/plugins/local-data-root-admission.d.ts +52 -0
  144. package/dist/plugins/local-data-root-admission.js +100 -0
  145. package/dist/plugins/local-engine-high-water.d.ts +28 -7
  146. package/dist/plugins/local-engine-high-water.js +27 -22
  147. package/dist/plugins/local-session-store.d.ts +134 -49
  148. package/dist/plugins/local-session-store.js +149 -108
  149. package/dist/plugins/memory-run-store.d.ts +14 -9
  150. package/dist/plugins/memory-run-store.js +47 -21
  151. package/dist/plugins/one-time-migration-marker.d.ts +19 -3
  152. package/dist/plugins/one-time-migration-marker.js +7 -1
  153. package/dist/plugins/one-time-migrations.d.ts +46 -20
  154. package/dist/plugins/one-time-migrations.js +102 -79
  155. package/dist/plugins/org-delivery-quota-store.d.ts +34 -11
  156. package/dist/plugins/org-delivery-quota-store.js +29 -14
  157. package/dist/plugins/permission-rule-store-sql.d.ts +1 -1
  158. package/dist/plugins/permission-rule-store-sql.js +5 -9
  159. package/dist/plugins/pg-pool.d.ts +1 -1
  160. package/dist/plugins/pg-pool.js +10 -12
  161. package/dist/plugins/remote-env-device.d.ts +3 -2
  162. package/dist/plugins/remote-env-e2b.d.ts +1 -7
  163. package/dist/plugins/remote-env-e2b.js +10 -7
  164. package/dist/plugins/remote-env-host.d.ts +50 -9
  165. package/dist/plugins/remote-env-host.js +77 -20
  166. package/dist/plugins/remote-env-k8s.js +4 -2
  167. package/dist/plugins/remote-env-local-docker.d.ts +6 -3
  168. package/dist/plugins/remote-env-local-docker.js +8 -23
  169. package/dist/plugins/run-store-sql.d.ts +26 -7
  170. package/dist/plugins/run-store-sql.js +44 -16
  171. package/dist/plugins/scenario-prefix-switch-m42.d.ts +3 -1
  172. package/dist/plugins/scenario-prefix-switch-m42.js +4 -3
  173. package/dist/plugins/schema-consistency.d.ts +89 -5
  174. package/dist/plugins/schema-consistency.js +176 -7
  175. package/dist/plugins/schema-drift-sentence.d.ts +6 -5
  176. package/dist/plugins/schema-drift-sentence.js +13 -5
  177. package/dist/plugins/session-placement.d.ts +5 -4
  178. package/dist/plugins/shell-sentinel-owner-m48.d.ts +22 -0
  179. package/dist/plugins/shell-sentinel-owner-m48.js +6 -0
  180. package/dist/plugins/stop-then-start-sentence.d.ts +16 -0
  181. package/dist/plugins/stop-then-start-sentence.js +7 -0
  182. package/dist/plugins/store-backend.d.ts +22 -8
  183. package/dist/plugins/store-backend.js +58 -24
  184. package/dist/plugins/store-contracts.d.ts +33 -3
  185. package/dist/plugins/store-contracts.js +16 -0
  186. package/dist/plugins/tidb-pool.d.ts +11 -6
  187. package/dist/plugins/tidb-pool.js +18 -12
  188. package/dist/plugins/workspace-custody.d.ts +44 -0
  189. package/dist/plugins/workspace-custody.js +47 -0
  190. package/dist/project-identity.d.ts +8 -7
  191. package/dist/project-identity.js +40 -17
  192. package/dist/project-layer-key.d.ts +13 -2
  193. package/dist/project-layer-key.js +9 -1
  194. package/dist/read-deny-floor-notice.d.ts +36 -0
  195. package/dist/read-deny-floor-notice.js +40 -0
  196. package/dist/{plain-string-record.d.ts → request-declaration-disposition.d.ts} +9 -23
  197. package/dist/request-declaration-disposition.js +39 -0
  198. package/dist/run-cancel-card.d.ts +28 -0
  199. package/dist/run-cancel-card.js +19 -0
  200. package/dist/run-cancel-context.d.ts +10 -0
  201. package/dist/run-cancel-context.js +1 -0
  202. package/dist/run-local.js +28 -13
  203. package/dist/runs.d.ts +31 -7
  204. package/dist/runs.js +34 -16
  205. package/dist/security.d.ts +2 -2
  206. package/dist/server-secret-env.d.ts +34 -20
  207. package/dist/server-secret-env.js +61 -18
  208. package/dist/session-agent.d.ts +3 -2
  209. package/dist/session-children-retire.d.ts +6 -3
  210. package/dist/session-children-retire.js +2 -5
  211. package/dist/session-workspace.d.ts +3 -3
  212. package/dist/store-admission.d.ts +33 -0
  213. package/dist/store-admission.js +49 -0
  214. package/dist/store-posture.d.ts +5 -0
  215. package/dist/store-posture.js +18 -1
  216. package/dist/stores-sql/checkpoint-store-sql.d.ts +1 -1
  217. package/dist/stores-sql/checkpoint-store-sql.js +3 -5
  218. package/dist/stores-sql/file-history-store-sql.d.ts +1 -1
  219. package/dist/stores-sql/file-history-store-sql.js +3 -5
  220. package/dist/stores-sql/tool-result-store-sql.d.ts +1 -1
  221. package/dist/stores-sql/tool-result-store-sql.js +4 -6
  222. package/dist/task-a2a.d.ts +3 -8
  223. package/dist/task-a2a.js +25 -27
  224. package/dist/task-cwd.d.ts +23 -7
  225. package/dist/task-cwd.js +3 -0
  226. package/dist/task-mcp.d.ts +3 -3
  227. package/dist/task-mcp.js +1 -1
  228. package/dist/task-settings.js +1 -0
  229. package/dist/terminal.d.ts +2 -0
  230. package/dist/terminal.js +21 -6
  231. package/dist/tool-approval.d.ts +44 -36
  232. package/dist/tool-approval.js +66 -47
  233. package/dist/trace/engine-notice-wire.d.ts +24 -21
  234. package/dist/trace/engine-notice-wire.js +7 -3
  235. package/dist/trace/ledger-sink.d.ts +3 -1
  236. package/dist/trace/project.d.ts +4 -2
  237. package/dist/trace/project.js +8 -7
  238. package/dist/trace/redact.d.ts +12 -0
  239. package/dist/trace/redact.js +5 -1
  240. package/dist/trace/self-minted-wire.d.ts +131 -52
  241. package/dist/trace/self-minted-wire.js +71 -39
  242. package/dist/web-admin-client/apply-effective.js +22 -9
  243. package/docs/error-text-lexicon.json +317 -0
  244. package/package.json +3 -1
  245. package/scripts/embed-minimal.mjs +183 -0
  246. package/wire-changes/server@7.149.0.json +372 -0
  247. package/dist/plain-string-record.js +0 -70
@@ -3,13 +3,16 @@ import { HttpError, createCodedHttpError, ssoVerifiedScope } from "../security.j
3
3
  import { gatedPerson } from "./principal-gate.js";
4
4
  import { DEVICE_ERROR_CODES, assertDeviceIdShape } from "../device-store.js";
5
5
  import { buildDeviceRejection } from "../device-enrollment.js";
6
- import { sameOccupant } from "../execution-placement.js";
6
+ import { SessionForkInProgressError, sameOccupant } from "../execution-placement.js";
7
7
  import { deployedExecutionLanes } from "../execution-lane-caps.js";
8
8
  import { gateExecutionLane } from "../runtime-caps-resolver.js";
9
- import { releaseClaimedRun } from "./claimed-run.js";
9
+ import { releaseClaimedRun, withdrawClaimedRun } from "./claimed-run.js";
10
10
  import { sessionIdRetiredFor } from "../deleted-session-ids.js";
11
11
  import { refusalBody } from "./refusal-body.js";
12
12
  import { timeClaim } from "./claim-latency.js";
13
+ import { storeFaultClassOf } from "../plugins/store-fault.js";
14
+ import { resumeClaimFaultAnswer } from "./resume-ctx-read.js";
15
+ import { resumeAssemblyRefusalAnswer, resumeRefusalWithCard } from "./checkpoint-ctx.js";
13
16
  export async function resolveExecutionContext(deps, entry) {
14
17
  const lanes = deployedExecutionLanes(deps.config);
15
18
  switch (entry.kind) {
@@ -70,8 +73,7 @@ function laneOf(root, cell, lanes) {
70
73
  }
71
74
  }
72
75
  function sessionMirrorError(root) {
73
- const answer = sessionMirrorAnswer(root);
74
- return createCodedHttpError(answer.status, answer.body.errorCode, answer.body.error, { forkPath: forkPathOf(root) });
76
+ return refusalHttpError(sessionMirrorAnswer(root));
75
77
  }
76
78
  export function forkPathOf(root) {
77
79
  return `/v1/sessions/${encodeURIComponent(root)}/fork`;
@@ -87,8 +89,7 @@ function sessionMirrorAnswer(root) {
87
89
  };
88
90
  }
89
91
  function rootSessionGoneError() {
90
- const absent = rootSessionAbsentAnswer();
91
- return createCodedHttpError(absent.status, absent.body.errorCode, absent.body.error);
92
+ return refusalHttpError(rootSessionAbsentAnswer());
92
93
  }
93
94
  export function isRootSessionGoneError(err) {
94
95
  return err instanceof HttpError && err.code === rootSessionAbsentAnswer().body.errorCode;
@@ -142,8 +143,8 @@ async function admitDevicePlacement(deps, entry, lane, seat, reading) {
142
143
  try {
143
144
  admitted = await deps.deviceEnrollment.admitSession({ rootSessionId: entry.root, placement: reading, requester: owner, ...(requestedDeviceId === undefined ? {} : { requestedDeviceId }) });
144
145
  }
145
- catch {
146
- throw asHttpError(registryUnavailable());
146
+ catch (e) {
147
+ throw asHttpError(faultedAnswerOf(e, registryUnavailable));
147
148
  }
148
149
  if (admitted.ok)
149
150
  return { rootSessionId: entry.root, occupant: reading.occupant, owner, admittedDeviceId: admitted.value.deviceId, firstBind: false };
@@ -153,8 +154,8 @@ async function admitDevicePlacement(deps, entry, lane, seat, reading) {
153
154
  try {
154
155
  view = await deps.deviceStore.readDeviceForAdmission(requestedDeviceId, owner);
155
156
  }
156
- catch {
157
- throw asHttpError(registryUnavailable());
157
+ catch (e) {
158
+ throw asHttpError(faultedAnswerOf(e, registryUnavailable));
158
159
  }
159
160
  if (!view)
160
161
  throw asHttpError(buildDeviceRejection(DEVICE_ERROR_CODES.NOT_FOUND, "device not found"));
@@ -169,8 +170,20 @@ export function stampSpecRoot(spec, ctx, submission) {
169
170
  if (named !== undefined && named !== ctx.root) {
170
171
  throw createCodedHttpError(400, "request.precondition_unmet", "the deployment's spec resolver named a session other than the one this submission was authorized for — a submission's root session comes only from the authorizer, so resolveSpec must not set sessionId (read opts.execution.root instead)");
171
172
  }
172
- const requireExisting = submission.cascade !== true && (ctx.occupant !== undefined || submission.requireExistingSession === true);
173
- return requireExisting ? { ...spec, sessionId: ctx.root, requireExistingSession: true } : { ...spec, sessionId: ctx.root };
173
+ switch (submission.use) {
174
+ case "session": {
175
+ const mustExist = ctx.occupant !== undefined || submission.requireExistingSession === true;
176
+ return mustExist ? { ...spec, sessionId: ctx.root, requireExistingSession: true } : { ...spec, sessionId: ctx.root };
177
+ }
178
+ case "ladder_host": {
179
+ const { requireExistingSession: _noAcquireOnALadder, ...ladderSpec } = spec;
180
+ return { ...ladderSpec, sessionId: ctx.root };
181
+ }
182
+ default: {
183
+ const unhandled = submission;
184
+ throw new Error(`stampSpecRoot: unhandled root use ${JSON.stringify(unhandled)}`);
185
+ }
186
+ }
174
187
  }
175
188
  export function rootSessionAbsentAnswer() {
176
189
  return {
@@ -181,6 +194,62 @@ export function rootSessionAbsentAnswer() {
181
194
  }),
182
195
  };
183
196
  }
197
+ export function isRootSessionGoneRefusal(refusal) {
198
+ return refusal.body.errorCode === rootSessionAbsentAnswer().body.errorCode;
199
+ }
200
+ export function faultedAnswerOf(e, answer) {
201
+ const fault = storeFaultClassOf(e);
202
+ switch (fault) {
203
+ case "corrupt":
204
+ case "connection":
205
+ case "deployment":
206
+ case "unclassified":
207
+ return answer();
208
+ case "defect":
209
+ throw e;
210
+ default: {
211
+ const unhandled = fault;
212
+ throw new Error(`faultedAnswerOf: the store-fault judge answered a class this reader does not place: ${String(unhandled)}`);
213
+ }
214
+ }
215
+ }
216
+ export function runStoreUnavailableAnswer() {
217
+ return {
218
+ status: 503,
219
+ body: refusalBody({ errorCode: "state.run_store_unavailable", message: "this task could not be recorded just now (the store did not answer), so nothing was started. Send it again later; if this keeps happening, ask whoever operates this deployment to check its database." }),
220
+ };
221
+ }
222
+ export const SUBMISSION_CLAIM_FACE = { refused: (refusal) => refusal, faulted: runStoreUnavailableAnswer };
223
+ export const CONTINUATION_CLAIM_FACE = {
224
+ refused: (refusal) => (isRootSessionGoneRefusal(refusal) ? refusal : cardWaitingRefusalAnswer(refusal)),
225
+ faulted: resumeClaimFaultAnswer,
226
+ };
227
+ export function cardWaitingRefusalAnswer(refusal) {
228
+ const { error, errorCode, ...facts } = refusal.body;
229
+ return resumeRefusalWithCard(resumeAssemblyRefusalAnswer(refusal.status, { errorCode, message: error, facts }), true);
230
+ }
231
+ export function refusalHttpError(refusal) {
232
+ const { error, errorCode, ...facts } = refusal.body;
233
+ return createCodedHttpError(refusal.status, errorCode, error, Object.keys(facts).length > 0 ? facts : undefined);
234
+ }
235
+ export async function resolveBeforeClaim(deps, entry, face) {
236
+ try {
237
+ return await resolveExecutionContext(deps, entry);
238
+ }
239
+ catch (e) {
240
+ if (e instanceof HttpError)
241
+ throw e;
242
+ throw new ClaimFaultAnswer(faultedAnswerOf(e, face.faulted));
243
+ }
244
+ }
245
+ export class ClaimFaultAnswer extends HttpError {
246
+ answer;
247
+ constructor(answer) {
248
+ const { error, errorCode: code, ...facts } = answer.body;
249
+ super(answer.status, error, Object.keys(facts).length > 0 ? { code, extra: facts } : { code });
250
+ this.answer = answer;
251
+ }
252
+ }
184
253
  export function placementWriteDeadlineMs(runStaleSec) {
185
254
  const stale = typeof runStaleSec === "number" && Number.isFinite(runStaleSec) && runStaleSec > 0 ? runStaleSec : RUN_STALE_SEC_DEFAULT;
186
255
  return Math.min(15_000, Math.max(1_000, Math.floor((stale * 1000) / 4)));
@@ -209,7 +278,7 @@ export async function commitExecutionContext(deps, claim, ctx) {
209
278
  return timeClaim(deps.metrics, "placement_commit", commitPlacement(deps, claim, ctx, seat, occupant));
210
279
  }
211
280
  async function commitPlacement(deps, claim, ctx, seat, occupant) {
212
- const release = (why) => releaseClaim(deps, claim, why);
281
+ const release = (why, ending) => withdrawClaim(deps, claim, why, ending);
213
282
  const deadlineMs = placementWriteDeadlineMs(deps.config?.runStaleSec);
214
283
  const withDeadline = async (work) => {
215
284
  let timer;
@@ -227,56 +296,64 @@ async function commitPlacement(deps, claim, ctx, seat, occupant) {
227
296
  clearTimeout(timer);
228
297
  }
229
298
  };
230
- const goneAnswer = () => (claim.kind === "parked" ? claim.goneAnswer : rootSessionAbsentAnswer());
231
299
  let refusal;
232
300
  try {
233
301
  refusal =
234
302
  ctx.device !== undefined
235
- ? await commitDevice(deps, seat, ctx.device, withDeadline, goneAnswer)
303
+ ? await commitDevice(deps, seat, ctx.device, withDeadline)
236
304
  : ctx.placement === "absent"
237
- ? await commitPlace(seat, ctx, occupant, withDeadline, goneAnswer)
238
- : await reassertWriter(seat, ctx, occupant, withDeadline, goneAnswer);
305
+ ? await commitPlace(seat, ctx, occupant, withDeadline)
306
+ : await reassertWriter(seat, ctx, occupant, withDeadline);
239
307
  }
240
308
  catch (err) {
241
- deps.logger?.warn("execution_placement_commit_failed", { root: ctx.root, lane: ctx.lane, placement: ctx.placement, deadlineMs, err: err instanceof Error ? err.message : String(err) });
242
- await release(`execution placement could not be committed: ${err instanceof Error ? err.message : String(err)}`);
243
- if (ctx.device !== undefined) {
244
- refusal = deviceRefusal(registryUnavailable());
309
+ if (err instanceof SessionForkInProgressError) {
310
+ refusal = { status: 409, body: refusalBody({ errorCode: err.code, message: err.message }) };
245
311
  deps.metrics?.inc("execution_placement_rejects_total", { errorCode: refusal.body.errorCode });
312
+ await release(`execution placement rejected: [${refusal.body.errorCode}] ${refusal.body.error}`, "refused");
246
313
  return refusal;
247
314
  }
315
+ deps.logger?.warn("execution_placement_commit_failed", { root: ctx.root, lane: ctx.lane, placement: ctx.placement, deadlineMs, err: err instanceof Error ? err.message : String(err) });
316
+ await release(`execution placement could not be committed: ${err instanceof Error ? err.message : String(err)}`, "faulted");
248
317
  throw err;
249
318
  }
250
319
  if (refusal === null)
251
320
  return null;
252
321
  deps.metrics?.inc("execution_placement_rejects_total", { errorCode: refusal.body.errorCode });
253
- await release(`execution placement rejected: [${refusal.body.errorCode}] ${refusal.body.error}`);
322
+ await release(`execution placement rejected: [${refusal.body.errorCode}] ${refusal.body.error}`, isRootSessionGoneRefusal(refusal) ? "gone" : "refused");
254
323
  return refusal;
255
324
  }
256
- async function releaseClaim(deps, claim, why) {
325
+ export async function withdrawClaim(deps, claim, why, ending) {
326
+ const loud = (taskId, step, err) => {
327
+ deps.logger?.warn("execution_placement_run_release_failed", { taskId, step, err: err instanceof Error ? err.message : String(err) });
328
+ deps.metrics?.inc("execution_placement_run_release_failed_total");
329
+ };
257
330
  switch (claim.kind) {
258
331
  case "run":
259
- await releaseClaimedRun(claim.runStore, claim.taskId, why, (err, step) => {
260
- deps.logger?.warn("execution_placement_run_release_failed", { taskId: claim.taskId, step, err: err instanceof Error ? err.message : String(err) });
261
- deps.metrics?.inc("execution_placement_run_release_failed_total");
262
- });
332
+ await withdrawClaimedRun(claim.runStore, claim.taskId, claim.legId, (err, step) => loud(claim.taskId, step, err));
333
+ return;
334
+ case "resumed":
335
+ if (ending !== "gone") {
336
+ await claim.restore();
337
+ return;
338
+ }
339
+ await releaseClaimedRun(claim.runStore, claim.taskId, why, (err, step) => loud(claim.taskId, step, err));
263
340
  return;
264
341
  case "parked":
265
342
  await claim.rollback(why);
266
343
  return;
267
344
  default: {
268
345
  const unhandled = claim;
269
- throw new Error(`commitExecutionContext: unknown claim ${JSON.stringify(unhandled)}`);
346
+ throw new Error(`withdrawClaim: unknown claim ${JSON.stringify(unhandled)}`);
270
347
  }
271
348
  }
272
349
  }
273
- async function commitPlace(seat, ctx, occupant, withDeadline, goneAnswer) {
350
+ async function commitPlace(seat, ctx, occupant, withDeadline) {
274
351
  if (ctx.lane === "device") {
275
352
  return deviceRefusal(buildDeviceRejection(DEVICE_ERROR_CODES.NOT_BOUND, "this session has no device binding — nothing was started"));
276
353
  }
277
- return placeOutcomeAnswer(ctx.root, await withDeadline(seat.place(ctx.root, occupant, { role: "writer", lane: ctx.lane })), goneAnswer);
354
+ return placeOutcomeAnswer(ctx.root, await withDeadline(seat.place(ctx.root, occupant, { role: "writer", lane: ctx.lane })));
278
355
  }
279
- function placeOutcomeAnswer(root, outcome, goneAnswer) {
356
+ function placeOutcomeAnswer(root, outcome) {
280
357
  switch (outcome.outcome) {
281
358
  case "placed":
282
359
  case "same":
@@ -286,17 +363,17 @@ function placeOutcomeAnswer(root, outcome, goneAnswer) {
286
363
  case "mirror":
287
364
  return sessionMirrorAnswer(root);
288
365
  case "gone":
289
- return goneAnswer();
366
+ return rootSessionAbsentAnswer();
290
367
  default: {
291
368
  const unhandled = outcome;
292
369
  throw new Error(`commitExecutionContext: no answer for ${JSON.stringify(unhandled)}`);
293
370
  }
294
371
  }
295
372
  }
296
- async function reassertWriter(seat, ctx, occupant, withDeadline, goneAnswer) {
373
+ async function reassertWriter(seat, ctx, occupant, withDeadline) {
297
374
  const now = await withDeadline(seat.read(ctx.root));
298
375
  if (now === undefined || !sameOccupant(now.occupant, occupant))
299
- return goneAnswer();
376
+ return rootSessionAbsentAnswer();
300
377
  const cell = now.cell;
301
378
  switch (cell.role) {
302
379
  case "writer":
@@ -312,20 +389,31 @@ async function reassertWriter(seat, ctx, occupant, withDeadline, goneAnswer) {
312
389
  }
313
390
  }
314
391
  }
315
- async function commitDevice(deps, seat, intent, withDeadline, goneAnswer) {
392
+ async function commitDevice(deps, seat, intent, withDeadline) {
316
393
  const enrollment = deps.deviceEnrollment;
317
394
  if (!enrollment) {
318
395
  throw new Error("commitExecutionContext: a device placement intent was minted without a DeviceEnrollment — assembly bug (both are read from the same ServiceDeps)");
319
396
  }
397
+ const registry = async (work) => {
398
+ try {
399
+ return { ok: true, value: await withDeadline(work) };
400
+ }
401
+ catch (e) {
402
+ return { ok: false, refusal: deviceRefusal(faultedAnswerOf(e, registryUnavailable)) };
403
+ }
404
+ };
320
405
  if (intent.firstBind) {
321
- const bound = await withDeadline(enrollment.bindPlacement({ rootSessionId: intent.rootSessionId, occupant: intent.occupant, deviceId: intent.admittedDeviceId, requester: intent.owner }));
406
+ const binding = await registry(enrollment.bindPlacement({ rootSessionId: intent.rootSessionId, occupant: intent.occupant, deviceId: intent.admittedDeviceId, requester: intent.owner }));
407
+ if (!binding.ok)
408
+ return binding.refusal;
409
+ const bound = binding.value;
322
410
  if (bound.ok)
323
411
  return null;
324
412
  if ("reject" in bound)
325
413
  return deviceRefusal(bound.reject);
326
414
  switch (bound.placement) {
327
415
  case "gone":
328
- return goneAnswer();
416
+ return rootSessionAbsentAnswer();
329
417
  case "mirror":
330
418
  return sessionMirrorAnswer(intent.rootSessionId);
331
419
  case "tree_bound":
@@ -338,8 +426,10 @@ async function commitDevice(deps, seat, intent, withDeadline, goneAnswer) {
338
426
  }
339
427
  const placement = await withDeadline(seat.read(intent.rootSessionId));
340
428
  if (placement === undefined || !sameOccupant(placement.occupant, intent.occupant))
341
- return goneAnswer();
342
- const readmitted = await withDeadline(enrollment.admitSession({ rootSessionId: intent.rootSessionId, placement, requester: intent.owner, requestedDeviceId: intent.admittedDeviceId }));
343
- return readmitted.ok ? null : deviceRefusal(readmitted.reject);
429
+ return rootSessionAbsentAnswer();
430
+ const readmission = await registry(enrollment.admitSession({ rootSessionId: intent.rootSessionId, placement, requester: intent.owner, requestedDeviceId: intent.admittedDeviceId }));
431
+ if (!readmission.ok)
432
+ return readmission.refusal;
433
+ return readmission.value.ok ? null : deviceRefusal(readmission.value.reject);
344
434
  }
345
435
  //# sourceMappingURL=execution-context.js.map
@@ -43,8 +43,9 @@ export interface OwnerAccessWiring {
43
43
  }
44
44
  /**
45
45
  * 🔴 **一次请求的调用方**(唯一铸点之一;另一只是决定口的 {@link callerOfDecision}、提交挂接的 `security.ts` `claimCallerOf`)。
46
- * 主体 = `gatedSubject`(验身份的那一只;带场景头 ⇒ 这个人的那一格分区);运维 = `explicitOperatorOk(personOf(主体))`(唯一读法,读人);fleet = 部署没有身份边界(`devOpenDeployment`)或出示 service 凭据、
47
- * 不报主体、部署没声明关掉(`actsForDeployment`),两者互斥(前者要求没配凭据);只读 trace 凭据单列(它只在读档算 fleet,见面表 `fleet` 列)。
46
+ * 主体 = `gatedSubject`(验身份的那一只;带场景头 ⇒ 这个人的那一格分区);运维 = `explicitOperatorOk(personOf(主体))`(唯一读法,读人);fleet = `actsForDeployment`
47
+ * (唯一读法:报了主体 ⇒ 不是 fleet;不报主体 ⇒ 部署没有身份边界时 `dev_open`、出示部署让它代自己说话的 service 凭据时 `deployment_token`,两者互斥 —— 前者要求没配凭据);
48
+ * 只读 trace 凭据单列(它只在读档算 fleet,见面表 `fleet` 列)。报了主体的调用方因此在每一口都是那位主体,与提交挂接的 `claimCallerOf`(fleet 恒缺)同读。
48
49
  */
49
50
  export declare function callerOf(req: IncomingMessage, w: Pick<OwnerAccessWiring, "config">): Caller;
50
51
  /**
@@ -5,16 +5,15 @@ import { mintOwnedRow, partsOf, personOf, subjectOf } from "../owner-brands.js";
5
5
  import { sessionCellFor } from "../security.js";
6
6
  import { openFaceFaultWire, SessionOwnerUnreadableError } from "../session-error-wire.js";
7
7
  import { seatText } from "../trace/exit-seat.js";
8
- import { actsForDeployment, devOpenDeployment, explicitOperatorOk, gatedSubject, sendPrincipalRefusal, sendUnauthorized, traceTokenPresented } from "./principal-gate.js";
8
+ import { actsForDeployment, explicitOperatorOk, gatedSubject, sendPrincipalRefusal, sendUnauthorized, traceTokenPresented } from "./principal-gate.js";
9
9
  import { RUN_NOT_FOUND_MESSAGE, runNotFoundMessage, SESSION_NOT_FOUND_MESSAGE } from "./route-ctx.js";
10
10
  import { sendError, sendJson } from "./send.js";
11
11
  export function callerOf(req, w) {
12
12
  const subject = gatedSubject(req, w.config);
13
- const fleet = devOpenDeployment(w.config) ? "dev_open" : actsForDeployment(req, w.config, () => subject) ? "deployment_token" : undefined;
14
13
  return {
15
14
  subject,
16
15
  operator: explicitOperatorOk(subject === undefined ? undefined : personOf(subject), w.config),
17
- fleet,
16
+ fleet: actsForDeployment(req, w.config, () => subject),
18
17
  traceRead: traceTokenPresented(req, w.config),
19
18
  principalRequired: DEPLOYMENT_SHAPE_PREDICATES.principalRequired.holds(w.config),
20
19
  multiTenant: DEPLOYMENT_SHAPE_PREDICATES.multiTenant.holds(w.config),
@@ -1,6 +1,7 @@
1
1
  import type { IncomingMessage, ServerResponse } from "node:http";
2
2
  import type { ServiceConfig } from "../config-types.js";
3
3
  import { type Person, type Subject } from "../owner-brands.js";
4
+ import type { Caller } from "../owner-faces.js";
4
5
  import { type IdentitySource, type RunIdentity } from "../identity-source.js";
5
6
  import type { Logger } from "../observability/logger.js";
6
7
  /** Coerce a possibly-repeated request header to a single string (or undefined). */
@@ -96,22 +97,28 @@ export declare function sendUnauthorized(res: ServerResponse, face: Unauthorized
96
97
  /**
97
98
  * 🔴 **dev-open 的判据** = 部署形表行 `identityBoundary` 不成立(`deployment-shape.ts`;「这台部署有没有身份边界」的唯一答者):
98
99
  * 没声明 `REQUIRE_PRINCIPAL`、配置没接任何已验身份来源(没配 service token、没配鉴权桥 issuer —— 含中心回退、没开直连门)、也没声明只读 trace token:
99
- * 请求里没有任何东西替主体头作保,头是谁都能自报的**标签**(过滤 / 展示用),不是身份。只读配置:鉴权桥的验签器由起服第 9 段按同一个配置读数建
100
+ * 请求里没有任何东西替主体头作保,头是谁都能自报的(不是认证)—— 但报了它的请求仍是「为那位主体」,不是为部署本人({@link actsForDeployment})。只读配置:鉴权桥的验签器由起服第 9 段按同一个配置读数建
100
101
  * (配置里有桥 ⇔ 装了验签器;验签器与配置里的桥两者不一致的装配(任一方向)在 `createHttpServer` 拒),所以这里与先于验签器的一次性迁移段读到同一个答案。
101
- * 两处读它、不各写一份:调用方的 fleet 一格(属主存取器 `owner-access.ts` 的 `callerOf` —— 面表的 `dev_open` 资格与 fleet 列表面都经它)、
102
- * `/metrics` 族的免凭据臂(`dispatch.ts`)—— 于是「调用方是谁」在列表、单行面与指标面上只有一只读法。
102
+ * 这是部署事实,不是调用方事实:「这位调用方代不代部署本人说话」只有 {@link actsForDeployment} 一只读法(本函数是它的一臂);另一个读者 `dispatch.ts` 的 `/metrics` 族
103
+ * 只拿它判「配了只读凭据的这台部署上免凭据臂关着,不必读调用方」(凭据先于主体判),不判调用方是谁。
103
104
  */
104
105
  export declare function devOpenDeployment(config: ServiceConfig): boolean;
106
+ /** 代部署本人说话的那一格 = 属主存取器 `Caller.fleet` 的词(闭集只在 `owner-faces.ts` 拼一次):`dev_open` = 没有身份边界的部署上不报主体的调用方;`deployment_token` = 出示部署让它代自己说话的 service 凭据、不报主体的调用方。 */
107
+ export type DeploymentVoice = NonNullable<Caller["fleet"]>;
105
108
  /**
106
- * 🔴 **律(一句,一个参数)**:出示这台部署的 service 凭据且不报主体的调用方代部署本人说话(fleet),**除非部署声明 `SERVICE_TOKEN_ACTS_FOR_DEPLOYMENT=false`**
107
- * —— 此时它是无身份调用方,按各面既有的「过了凭据门、没有主体」那一臂答。带主体头的 service 凭据请求是前端(BFF)代那位主体发的 —— 它读那位主体,
108
- * 不读全舰队(两形同读);部署姿态(单用户 / 多租)不改这一句(单用户形上的主体头同样是「这次请求为谁」)。
109
- * 本函数是这条律的**唯一实施点**:声明位({@link ServiceConfig.serviceTokenActsForDeployment},env 一处)是这里的一个合取项,两个读者一次继承 ——
110
- * 调用方的 fleet 一格(属主存取器 `owner-access.ts` 的 `callerOf`)与 `/metrics` 族的 service 臂(`dispatch.ts`);不加拒码、不加闸点。自报位 `/health.auth.serviceTokenActsForDeployment`
111
- * 投的是同一只读法({@link serviceTokenDeclaredActsForDeployment})与同一只武装谓词(`http/health-self-report.ts`)。先判声明与凭据、后取主体:`subject` 只在两者都成立时才求值(调用点那一次 {@link gatedSubject}),
112
- * 没带凭据的请求不因主体头的形而改答。
109
+ * 🔴 **律(一句)**:**报了主体的调用方恒是那位主体** —— 每一口、每一种部署形(单用户 / 多租、有没有身份边界)都不改这一句;只有**不报主体**的调用方才代部署本人说话(fleet):
110
+ * 部署没有身份边界({@link devOpenDeployment} ⇒ `dev_open`:整台一人,不报主体的就是那一位 —— 「不报」= 没有主体头,也没出示一枚这台部署验不过的凭据
111
+ * ({@link bearerPresentedButUnverified}:出示了验不过 ≠ 没出示 —— 那枚凭据把主体头抹成「没有可信身份」,不许再落回最宽的那一格)),或出示这台部署的 service 凭据且部署没声明
112
+ * `SERVICE_TOKEN_ACTS_FOR_DEPLOYMENT=false`(⇒ `deployment_token`;声明了 ⇒ 它是无身份调用方,按各面既有的「过了凭据门、没有主体」那一臂答)。
113
+ * 带主体头的请求是前端(BFF)代那位主体发的、或调用方自报的「这次请求为谁」—— 它读那位主体、动那位主体的,不读全舰队;dev-open 上主体头谁都能自报,
114
+ * 那是部署选了不验身份(要隔离就给部署接一个身份来源),不是把头读成「为部署本人」的理由:提交的挂接(`security.ts` 的认领面)一向按它判属主,读面、列表与存续面同读这一句。
115
+ * 本函数是这条律的**唯一实施点**,两个读者一次继承 —— 调用方的 fleet 一格(属主存取器 `owner-access.ts` 的 `callerOf`)与 `/metrics` 族(`dispatch.ts`:`dev_open` 臂在没配
116
+ * `METRICS_TOKEN` 时免凭据,`deployment_token` 臂即 service 臂);不加拒码、不加闸点。自报位 `/health.auth.serviceTokenActsForDeployment` 投的是声明位的同一只读法
117
+ * ({@link serviceTokenDeclaredActsForDeployment})与同一只武装谓词(`http/health-self-report.ts`)。
118
+ * 主体只在部署那一格成立时求值(调用点那一次 {@link gatedSubject}):有身份边界的部署上没带 service 凭据的请求不因主体头的形而改答;dev-open 上要判「报没报主体」,
119
+ * 主体头形不合的请求与其余各口同答那一只 400。
113
120
  */
114
- export declare function actsForDeployment(req: IncomingMessage, config: ServiceConfig, subject: () => Subject | undefined): boolean;
121
+ export declare function actsForDeployment(req: IncomingMessage, config: ServiceConfig, subject: () => Subject | undefined): DeploymentVoice | undefined;
115
122
  /**
116
123
  * 声明位 `SERVICE_TOKEN_ACTS_FOR_DEPLOYMENT` 的**唯一读法**:门({@link actsForDeployment})、自报位(`http/health-self-report.ts`)、起服行
117
124
  * ({@link buildServiceTokenPostureNotice})三处都经它,不各写一份真值判断。只有字面 `true` 是「代部署本人」:`loadConfig` 恒写这一格(缺省 `true`),
@@ -152,7 +152,9 @@ export function devOpenDeployment(config) {
152
152
  return !DEPLOYMENT_SHAPE_PREDICATES.identityBoundary.holds(config);
153
153
  }
154
154
  export function actsForDeployment(req, config, subject) {
155
- return serviceTokenDeclaredActsForDeployment(config) && systemFor(req, config) !== undefined && subject() === undefined;
155
+ if (devOpenDeployment(config))
156
+ return subject() === undefined && !bearerPresentedButUnverified(req, config) ? "dev_open" : undefined;
157
+ return serviceTokenDeclaredActsForDeployment(config) && systemFor(req, config) !== undefined && subject() === undefined ? "deployment_token" : undefined;
156
158
  }
157
159
  export function serviceTokenDeclaredActsForDeployment(config) {
158
160
  return config.serviceTokenActsForDeployment === true;
@@ -58,10 +58,11 @@ export interface CheckpointCtxOpening {
58
58
  }
59
59
  export declare function readResumeCtx(load: Promise<StoredCheckpointCtx | null>, opening: CheckpointCtxOpening): Promise<ResumeCtxRead>;
60
60
  /**
61
- * 续跑家族一次店读失败的落点 —— 判官的五词逐词表态,**只在这一处**(本口读续跑上下文那一读,与续跑腿读「这张卡归哪条 run」那一读同读它):
62
- * `corrupt` ⇒ 坏体(带坐标);`connection` / `deployment` / `unclassified` ⇒ 读不出;`defect` ⇒ 原样上抛。
61
+ * 续跑家族一次店往返失败的落点 —— 判官的五词逐词表态,**只在这一处**。三个读者同读它:本口读续跑上下文那一读、续跑腿读「这张卡归哪条 run」那一读,
62
+ * 与续跑腿的认领(`resume-legs.ts` 的 `resumeClaimFaultAnswerOf`:认领那一笔,或认领之后执行席那一次读 / 写失败 —— 后者的认领已由写口撤回)。
63
+ * `corrupt` ⇒ 坏体(带坐标);`connection` / `deployment` / `unclassified` ⇒ 读不出 / 写不进;`defect` ⇒ 原样上抛。
63
64
  */
64
- export declare function resumeStoreReadFailureOf(e: unknown): {
65
+ export declare function resumeStoreFailureOf(e: unknown): {
65
66
  readonly kind: "corrupt";
66
67
  readonly where: string;
67
68
  } | {
@@ -77,6 +78,16 @@ export declare function resumeCtxUnreadableAnswer(): {
77
78
  status: number;
78
79
  body: RefusalBody;
79
80
  };
81
+ /**
82
+ * 续跑腿**认领**撞店故障的对外答复 —— 认领那一笔(`markResuming` / wake 现建行的 `createRun`)或认领之后那一次提交(执行席的再读 / 落格)。
83
+ * 与 {@link resumeCtxUnreadableAnswer} **同码同状态**(503 `state.resume_context_unreadable`:店这一拍没答出来,这一请求没有执行,稍后原样重发;一直如此 ⇒ 请运维查库),
84
+ * 句子说的是这一形自己的事:读或写这条任务的存储状态失败了,卡仍待决(认领之后那一形的认领已由写口撤回到取来时的样子)。句子不带店层 detail。
85
+ * 🔴 形是给冻结门看的(与 {@link resumeCtxCorruptAnswer} 同一条):`status: <三位数>, body: refusalBody({ errorCode: "<码>", message: "<整句双引号字面>"`,中间只许有空白。
86
+ */
87
+ export declare function resumeClaimFaultAnswer(): {
88
+ status: number;
89
+ body: RefusalBody;
90
+ };
80
91
  /**
81
92
  * 开不开的对外答复([ref])—— 409 `conflict.checkpoint_ctx_unsealable`,机读 `field`(哪一个凭据位,提交体路径;键名不是秘密,值从不进来)+ `reason`
82
93
  * (闭集 `CHECKPOINT_CTX_UNSEALABLE_REASONS`)。句子说「这条任务的续跑上下文里带的凭据本服务开不开」、五种成因与出路(按 `reason` 修好保管目录后原样重发,
@@ -14,11 +14,11 @@ async function readStoredCtx(load) {
14
14
  stored = await load;
15
15
  }
16
16
  catch (e) {
17
- return resumeStoreReadFailureOf(e);
17
+ return resumeStoreFailureOf(e);
18
18
  }
19
19
  return stored == null ? { kind: "absent" } : { kind: "ok", ctx: stored };
20
20
  }
21
- export function resumeStoreReadFailureOf(e) {
21
+ export function resumeStoreFailureOf(e) {
22
22
  const fault = storeFaultClassOf(e);
23
23
  switch (fault) {
24
24
  case "corrupt":
@@ -41,6 +41,12 @@ export function resumeCtxUnreadableAnswer() {
41
41
  body: refusalBody({ errorCode: "state.resume_context_unreadable", message: "this session's stored resume context could not be read just now (reading it failed), so this request was not carried out. Send it again later; if this keeps happening, ask whoever operates this deployment to check its database." }),
42
42
  };
43
43
  }
44
+ export function resumeClaimFaultAnswer() {
45
+ return {
46
+ status: 503,
47
+ body: refusalBody({ errorCode: "state.resume_context_unreadable", message: "the stored state of this task could not be read or written just now (the store failed), so this request was not carried out and the task is still waiting for it. Send it again later; if this keeps happening, ask whoever operates this deployment to check its database." }),
48
+ };
49
+ }
44
50
  export function resumeCtxUnsealableAnswer(read) {
45
51
  return {
46
52
  status: 409,
@@ -12,13 +12,14 @@
12
12
  * (`RunRegistry` / `RouteHelpers` / `RouteLegs` / `ReturnType<typeof createRequestGuards>`),不手抄签名 ⇒ 不会漂。
13
13
  */
14
14
  import type { IncomingMessage } from "node:http";
15
- import { CheckpointError, type QuestionAnswer, type CheckpointToken, type PendingSteerInput } from "@sema-agent/core";
15
+ import { CheckpointError, type QuestionAnswer, type CheckpointToken, type CheckpointRowVerdict, type PendingSteerInput } from "@sema-agent/core";
16
16
  import type { DecideBinding, DecidedAction } from "./wire-types.js";
17
17
  import { type KeyCaller } from "../owner-faces.js";
18
18
  import { type Subject } from "../owner-brands.js";
19
19
  import type { CheckpointStoreFull } from "../plugins/store-backend.js";
20
20
  import type { HostDecision } from "../host-decision.js";
21
21
  import { type GateSighting } from "../approval-gate-currency.js";
22
+ import { type CancelRunReader } from "../run-cancel-card.js";
22
23
  import { type PlanApprovePermissionModeAfter } from "../task-settings.js";
23
24
  import type { RouteCtxBase, DriveResumeArgs, PreparedPlanReview, RememberSessionRequest, ResumeCheckpointOutcome, ResumeLegOutcome, WorkflowDecisionDelivery } from "./route-ctx.js";
24
25
  import type { RunRegistry, RouteHelpers, RouteLegs } from "./route-ctx.js";
@@ -54,6 +55,34 @@ export declare const CHECKPOINT_ERROR_DISPOSITION: Readonly<Record<CheckpointErr
54
55
  * ⇒ `"terminal"`:与 `by_reason` 认不得的词同一条 fail-closed 方向(不宽容默认成可重试),也与修前手列串的缺省逐字相同。
55
56
  */
56
57
  export declare function resumeRefusalDisposition(e: CheckpointError): "cancelled" | "retriable" | "terminal";
58
+ type NotOkRowVerdict = Exclude<CheckpointRowVerdict, {
59
+ readonly kind: "ok";
60
+ }>;
61
+ /**
62
+ * 🔴 **判官判「本引擎续不了」的一张卡,人从哪儿出去 —— 闭集**(这一行在本引擎上不收任何决定,批 / 拒 / 驳 / 改同拒;出路只看判决与这张卡本身,不看门的哪一扇、也不看决定词)。
63
+ * 唯一铸法 {@link rowWayOutOf};读面 = 决断类续跑拒体上的 `wayOut`(`/decide` 的这一行自己的拒 · `/plan_review` 与 `/resume` 上引擎对这张卡的拒 · 读行臂)。
64
+ * · `cancel_run` —— 判决 `damaged`,且取消动词会结算这一张卡:`POST /v1/runs/:id/cancel` 按店 CAS 把卡 expire、再驱 run 终局(不经判官),之后另起任务;
65
+ * · `newer_worker` —— 判决 `lacking`:更新引擎停驻的行,不低于停驻它那一版的 worker 续得了它,同一决定在那里落得下;卡留 pending(cancel 在这里是放弃它)。
66
+ * 缺席 = 本服务说不出一个客户端落得下的出路(判 `damaged` 而取消动词不会结算这张卡 —— 它停在已终局的 run 的纯 park 上、归属说不出的老行,或店此刻答不出这一问)。
67
+ */
68
+ export type RowWayOut = "cancel_run" | "newer_worker";
69
+ /**
70
+ * 出路铸法要问店的那一问的座:`cs` + run 店 + 这张卡的令牌 + 铸不成时留痕的日志口(铸法不抛 —— 出路是给客户端的指路,店答不出这一问时拒本身一字不变、只是不指路,并响亮留一行)。
71
+ * 读行抛的五个读者(读行臂)与载入行的三扇门各自在自己的位置上拿着 `cs` 与令牌,同交一座。
72
+ */
73
+ export interface WayOutSeat {
74
+ readonly cs: Pick<CheckpointStoreFull, "hostSessionOfCard" | "findPendingTokenBySession">;
75
+ /** 取消动词那一问也要看 run 行(只对停着的 run 结算卡);没有耐久 run 店 ⇒ 缺席(取消口恒 501)。 */
76
+ readonly runs: CancelRunReader | undefined;
77
+ readonly token: CheckpointToken;
78
+ readonly logger: FlatServiceDeps["logger"];
79
+ }
80
+ /**
81
+ * {@link RowWayOut} 的唯一铸法:穷尽 core 的非 ok 判决 kind(core 加一种 ⇒ tsc 红,先来这里定它的出路)。
82
+ * `damaged` ⇒ `cancel_run` 当且仅当**取消动词会结算这一张卡**({@link cancelSettlesCard}:问的就是取消动词自己那一问,只读店的列,与行体坏成什么样无关 ——
83
+ * 判坏的卡的行体可以读不成检查点、属主格可以丢,取消照样落得下;投影若另按载入的行体判,就是同一件事的第二个读者)。
84
+ */
85
+ export declare function rowWayOutOf(seat: WayOutSeat, kind: NotOkRowVerdict["kind"]): Promise<RowWayOut | undefined>;
57
86
  /**
58
87
  * 🔴 **停驻行读者上「读行抛」的答复 —— 五个读者(决断口含停驻赎回与 SLA 拒扫的 `resumeCheckpoint` · 续跑 `resumePreempted` · 唤醒 `resumeWake` · 计划审阅
59
88
  * `preparePlanReview` · 活卡迟到回决 `redeemParkedAsk`)在各自的披露闸(`mayDiscloseRowError` / `decisionRowAdmits`)与各自的 404 句之后,catch 尾同调这一只**([ref])。
@@ -64,14 +93,16 @@ export declare function resumeRefusalDisposition(e: CheckpointError): "cancelled
64
93
  *
65
94
  * 写者位交空:版本键只长在 `checkpoint.unsupported_version` 上,而一份读不成检查点的字节,判官(core 全函数,读不动的值落 `damaged`)恒判 `damaged`,到不了那一码。
66
95
  * 店日后若交出一只 `lacking` 判决,键缺席(不编值);不为这条到不了的臂再开一条读续跑上下文的口(`getCtx` 的结果只交 `readResumeCtx`,读口律;多一个读者就是多一处要记得)。
67
- * 门型守卫那一臂(行已读出、判官判 `lacking` 可达)自带写者位,见 {@link createResumeLegs} 决断口的 `rowOwnRefusal`。
96
+ * 载入行那一问(行已读出、判官判 `lacking` 可达)自带写者位,见 {@link loadedRowRefusal}。
97
+ * `seat` 缺席 ⇒ 这个读者不带出路词(`/wake`:成文律,它的拒从不带 `wayOut`)。
68
98
  */
69
- export declare function rowReadRefusalAnswer(e: unknown): ResumeRefusalAnswer;
99
+ export declare function rowReadRefusalAnswer(e: unknown, seat: WayOutSeat | undefined): Promise<ResumeRefusalAnswer>;
70
100
  export declare function checkpointRefusalAnswer(e: CheckpointError, at: {
71
101
  readonly gate: string;
72
102
  readonly retriable: boolean;
73
103
  readonly taskId?: string;
74
104
  readonly versionKeys: Readonly<Record<string, string>>;
105
+ readonly wayOut?: RowWayOut;
75
106
  }): ResumeRefusalAnswer;
76
107
  /**
77
108
  * 一个答案只属于它被展示的那一个动作 —— 决断绑定的**唯一判官**:决断方点名它看到的那一对(`boundCallId` + `boundInputHash`,
@@ -116,4 +147,5 @@ export declare function createResumeLegs(ctx: {
116
147
  drivePlanReview: (prepared: PreparedPlanReview, decision: "approve" | "edit" | "reject", editedPlan: string | undefined, reason: string | undefined, hostDecision: HostDecision, req: IncomingMessage | undefined, acceptEarly?: boolean, permissionModeAfter?: PlanApprovePermissionModeAfter) => Promise<ResumeLegOutcome>;
117
148
  sessionNotifyDoor: import("../session-notify-door.js").SessionNotifyDoor;
118
149
  };
150
+ export {};
119
151
  //# sourceMappingURL=resume-legs.d.ts.map