@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
@@ -36,12 +36,15 @@ const MIN_HOOK_CONTEXT_PARTIAL_CHARS = 256;
36
36
  const MAX_TOOL_RESPONSE_CHARS = 32_768;
37
37
  const MAX_TOOL_INPUT_CHARS = 262_144;
38
38
  const HOOK_ENV_ALLOWLIST = ["PATH", "HOME", "SHELL", "USER", "LOGNAME", "TERM", "TZ", "LANG", "LC_ALL", "LC_CTYPE", "LC_MESSAGES", "TMPDIR"];
39
- function hookChildEnv(layer, spawning) {
40
- const o = { policy: [...HOOK_ENV_ALLOWLIST], allow: [], grants: [layer] };
39
+ function hookChildEnv(layer, putBack, spawning) {
40
+ const o = { policy: [...HOOK_ENV_ALLOWLIST], allow: putBack, grants: [layer] };
41
41
  return spawning ? hostSubprocessEnv({ site: "server.hooks.spawn-env", ...o }) : composeHostSubprocessEnv(o);
42
42
  }
43
+ function hookChildView(ctx) {
44
+ return (layer) => hookChildEnv(layer, ctx.hookEnvAllow, false);
45
+ }
43
46
  function hookEnv(ctx) {
44
- return hookChildEnv(composeHookEnvLayer(ctx.shellEnv, ctx.cwd, undefined), false);
47
+ return hookChildView(ctx)(composeHookEnvLayer(ctx.shellEnv, ctx.cwd, undefined));
45
48
  }
46
49
  export const SETTINGS_HOOK_STATIONS = {
47
50
  PreToolUse: "classic.PreToolUse",
@@ -861,7 +864,7 @@ function readBelowPreToolUse(below) {
861
864
  };
862
865
  }
863
866
  function spawnPlannedCommand(plan, ctx, stdio) {
864
- const env = hookChildEnv(plan.env, true);
867
+ const env = hookChildEnv(plan.env, ctx.hookEnvAllow, true);
865
868
  if (plan.kind === "exec")
866
869
  return spawn(plan.file, [...plan.args], { cwd: ctx.cwd, env, stdio, detached: true });
867
870
  const sh = hostShell();
@@ -926,7 +929,7 @@ function createHookTreeLadder(child, ctx, event) {
926
929
  };
927
930
  }
928
931
  async function runCommandHook(entry, payload, ctx, halt, event) {
929
- const plan = await planCommandSpawn(entry, ctx.cwd, ctx.shellEnv);
932
+ const plan = await planCommandSpawn(entry, ctx.cwd, ctx.shellEnv, hookChildView(ctx));
930
933
  if (plan.kind === "refused")
931
934
  return { code: null, stdout: "", stderr: "", timedOut: false, spawnError: plan.error };
932
935
  if (plan.kind === "shell") {
@@ -1011,7 +1014,7 @@ async function fireAsyncCommandHook(entry, payload, ctx, event, halt) {
1011
1014
  try {
1012
1015
  const rewake = entry.asyncRewake === true;
1013
1016
  const timeoutMs = Math.min(entry.timeout ?? DEFAULT_HOOK_TIMEOUT_SECONDS, MAX_HOOK_TIMEOUT_SECONDS) * 1000;
1014
- const plan = await planCommandSpawn(entry, ctx.cwd, ctx.shellEnv);
1017
+ const plan = await planCommandSpawn(entry, ctx.cwd, ctx.shellEnv, hookChildView(ctx));
1015
1018
  if (plan.kind === "refused")
1016
1019
  throw new Error(plan.error);
1017
1020
  if (plan.kind === "shell")
@@ -1740,7 +1743,8 @@ function createLaneRegistration(lane, config, ctx, budgetMs) {
1740
1743
  asked = alsoAsks(lane, prior.asked ?? prior.unreadable?.message, clipDataText(typeof h.permissionDecisionReason === "string" ? h.permissionDecisionReason : "a PreToolUse hook requires approval", MAX_HOOK_FEEDBACK_CHARS));
1741
1744
  spoke = true;
1742
1745
  }
1743
- if (decision === "allow" || decision === undefined) {
1746
+ const counted = decision === "allow" || decision === "ask" || decision === undefined;
1747
+ if (counted) {
1744
1748
  if (ran.distrust !== undefined && hookLaneOf(ran.source) === "unmanaged" && (decision === "allow" || proposesRewrite)) {
1745
1749
  noteVerdictUntrusted(ctx, { event: "PreToolUse", reason: ran.distrust, entry, source: ran.source, toolName });
1746
1750
  }
@@ -25,7 +25,7 @@ import { legFactsOf } from "../leg-facts.js";
25
25
  import { createSubmissionSeat } from "../submission-exit.js";
26
26
  import { assertRequestFormBeforeMint } from "../request-form.js";
27
27
  import { headerStr } from "./principal-gate.js";
28
- import { resolveExecutionContext, stampSpecRoot } from "./execution-context.js";
28
+ import { SUBMISSION_CLAIM_FACE, resolveBeforeClaim, stampSpecRoot } from "./execution-context.js";
29
29
  import { callerTrust, requestTrustRefusal } from "./caller-trust.js";
30
30
  import { createRunAcceptance } from "../run-acceptance.js";
31
31
  import { promptScreenDeclared } from "../prompt-screen.js";
@@ -749,12 +749,12 @@ export function createAdmission(ctx) {
749
749
  const seat = createSubmissionSeat({ deployment: deps.config, metrics: deps.metrics, dropSessionScopedOf: deps.dropSessionScopedOf, sessionWatch: deps.sessionWatch });
750
750
  try {
751
751
  const auth = await deps.authorize({ req, body: admitted, onBirth: (birth, face) => seat.noteBirth(birth, face) });
752
- const execution = await resolveExecutionContext(deps, { kind: "fresh", root: auth.sessionId, principal: auth.subject === undefined ? undefined : personOf(recoveredSubject(auth.subject)), judged: auth.head, req, deviceId: admitted.deviceId });
752
+ const execution = await resolveBeforeClaim(deps, { kind: "fresh", root: auth.sessionId, principal: auth.subject === undefined ? undefined : personOf(recoveredSubject(auth.subject)), judged: auth.head, req, deviceId: admitted.deviceId }, SUBMISSION_CLAIM_FACE);
753
753
  const engineNotices = [];
754
754
  const resolved = await deps.resolveSpec(admitted, req, auth, { leg: "fresh", ...(legRoute?.liveStream === true ? { liveStream: true } : {}), onNotice: (n) => engineNotices.push(n), lease, execution });
755
755
  const facts = legFactsOf(resolved);
756
756
  const runner = runnerFor(resolved);
757
- const spec = stampSpecRoot(resolved, execution, admitted);
757
+ const spec = stampSpecRoot(resolved, execution, cascade ? { use: "ladder_host" } : { use: "session", requireExistingSession: admitted.requireExistingSession });
758
758
  const model = modelIdOf(spec.model);
759
759
  const acceptance = createRunAcceptance({ sessionTitler: deps.sessionTitler }, { sessionId: spec.sessionId, objective: spec.objective, screened: promptScreenDeclared([deps.deploymentMods, spec.mods]), ...(model !== undefined ? { model } : {}) });
760
760
  seat.attachAcceptance(acceptance);
@@ -16,6 +16,7 @@ import { readResumeCtx } from "./resume-ctx-read.js";
16
16
  import { isResumeBlockedByPolicy } from "./checkpoint-ctx.js";
17
17
  import { storedBodyReplayRefusal } from "./stored-body-replay.js";
18
18
  import { refusalBody } from "./refusal-body.js";
19
+ import { CARD_EXPIRE_OUTCOME, expireCardUnderStoredScope } from "../card-settle-stored-scope.js";
19
20
  import { DENY_SWEEP_PAGE_ROWS } from "../plugins/checkpoint-store-face.js";
20
21
  const BLOCKED_SCALAR_KEYS = ["errorCode", "blockedBy", "field", "reason", "count", "cap", "index"];
21
22
  function blockedScalars(body) {
@@ -141,7 +142,7 @@ export function createApprovalSweeps(ctx) {
141
142
  deps.logger?.warn?.("deny_sweep_policy_blocked_expire_skipped", { sessionId, skipReason: cp.status !== "pending" ? "not_pending" : "card_changed", ...blockedScalars(body) });
142
143
  return;
143
144
  }
144
- won = await cs.expire(cardToken, cp.scope);
145
+ won = (await expireCardUnderStoredScope(cs, cardToken)) === CARD_EXPIRE_OUTCOME.expired;
145
146
  }
146
147
  catch (e) {
147
148
  deps.logger?.warn?.("deny_sweep_policy_blocked_expire_failed", { sessionId, err: errText(e) });
@@ -240,7 +241,7 @@ export function createApprovalSweeps(ctx) {
240
241
  deps.logger?.warn?.("parked_late_decision_unreadable_row_withheld", { askId: req.askId });
241
242
  return { status: 404, body: refusalBody({ errorCode: "not_found.approval", message: "approval not found" }) };
242
243
  }
243
- return rowReadRefusalAnswer(e);
244
+ return rowReadRefusalAnswer(e, { cs, runs: deps.runStore, token: req.gateToken, logger: deps.logger });
244
245
  }
245
246
  if (!cp) {
246
247
  return { status: 404, body: refusalBody({ errorCode: "not_found.approval", message: "approval not found" }) };
@@ -23,15 +23,15 @@ export declare function startClaimHeartbeat(runStore: RunHeartbeatStore, taskId:
23
23
  export declare function claimStillHeld(runStore: ClaimFenceStore, sessionId: string, taskId: string): Promise<boolean>;
24
24
  /**
25
25
  * 三条提交腿起跑之前的那一只复核(唯一调用形):持有 ⇒ `undefined`(照起);没持有 ⇒ {@link refusalAfterLostClaim} 的答复;栅栏自己抛(店一时不通)⇒
26
- * 先按 {@link releaseClaimedRun} 撤掉本请求刚认领的这条 run(终结行 + 放幂等键,不留一条没起跑的 `running` 行占着会话等收割),再原样上抛。
26
+ * 先按 {@link withdrawClaimedRun} 撤掉本请求刚认领的这条 run(删掉本请求铸的这一行 —— `legId` = 建行交回的持有腿,不留一条没起跑的 `running` 行占着会话等收割),再原样上抛。
27
27
  */
28
- export declare function recheckClaimBeforeLaunch(deps: Pick<FlatServiceDeps, "checkpointStore" | "config" | "logger"> & LostClaimSessionFace, runStore: ClaimFenceStore & LostClaimStore, sessionId: string, taskId: string): Promise<Awaited<ReturnType<typeof refusalAfterLostClaim>> | undefined>;
28
+ export declare function recheckClaimBeforeLaunch(deps: Pick<FlatServiceDeps, "checkpointStore" | "config" | "logger"> & LostClaimSessionFace, runStore: ClaimFenceStore & LostClaimStore, sessionId: string, taskId: string, legId: string): Promise<Awaited<ReturnType<typeof refusalAfterLostClaim>> | undefined>;
29
29
  /** 失认领答复读会话店的那一面:只问头读(`headReadOf` 判店有没有它)—— 不要整只会话店。 */
30
30
  export interface LostClaimSessionFace {
31
31
  readonly sessionStorage?: Parameters<typeof headReadOf>[0];
32
32
  }
33
33
  /** 失认领答复与撤回本次 run 读写的那几面。 */
34
- export type LostClaimStore = Pick<RunStore, "getActiveTaskId" | "getRun" | "setTerminal" | "releaseIdemKey">;
34
+ export type LostClaimStore = Pick<RunStore, "getActiveTaskId" | "getRun" | "withdrawMintedRun">;
35
35
  /**
36
36
  * 起跑之前认领复核没过的答复(三条提交腿同一只;三臂按「此刻是谁的事实」分,各读各的):
37
37
  * · 认领此刻在别的 run 上 ⇒ 409 冲突体(与建行时认领输同一只材料,`conflict.session_active_run`);
@@ -3,7 +3,7 @@ import { FENCE_LANDED } from "../plugins/store-contracts.js";
3
3
  import { buildActiveRunConflict } from "./active-run-conflict.js";
4
4
  import { rootSessionAbsentAnswer } from "./execution-context.js";
5
5
  import { readTurnActivityMs } from "../turn-activity.js";
6
- import { releaseClaimedRun } from "./claimed-run.js";
6
+ import { withdrawClaimedRun } from "./claimed-run.js";
7
7
  import { refusalBody } from "./refusal-body.js";
8
8
  import { headReadOf } from "../session-incarnation.js";
9
9
  export function startClaimHeartbeat(runStore, taskId, owner, metrics) {
@@ -20,13 +20,13 @@ export async function claimStillHeld(runStore, sessionId, taskId) {
20
20
  return (await runStore.whileHoldingClaim(sessionId, taskId, async () => undefined)).held;
21
21
  return runStore.whileHoldingClaim(sessionId, taskId, () => FENCE_LANDED);
22
22
  }
23
- export async function recheckClaimBeforeLaunch(deps, runStore, sessionId, taskId) {
23
+ export async function recheckClaimBeforeLaunch(deps, runStore, sessionId, taskId, legId) {
24
24
  let held;
25
25
  try {
26
26
  held = await claimStillHeld(runStore, sessionId, taskId);
27
27
  }
28
28
  catch (err) {
29
- await releaseClaimedRun(runStore, taskId, `the claim re-check before launch failed: ${err instanceof Error ? err.message : String(err)}`, (releaseErr, step) => {
29
+ await withdrawClaimedRun(runStore, taskId, legId, (releaseErr, step) => {
30
30
  deps.logger?.warn?.("claim_recheck_release_failed", { taskId, sessionId, step, err: releaseErr instanceof Error ? releaseErr.message : String(releaseErr) });
31
31
  });
32
32
  throw err;
@@ -1,15 +1,22 @@
1
1
  /**
2
- * 一条提交腿**刚认领**的 run 在起跑之前就收场 —— 撤掉本请求建的东西的**唯一**写法。四个调用点:device 落点拒
3
- * (`execution-context.ts` `commitExecutionContext`)、续跑输入写不进(`submit-ctx.ts` `persistSubmitCtx`)、workflow 决定行没落成
4
- * (`durable-run-leg.ts` 的 fresh 投递腿;此刻认领可能已被收割归了别人 —— 两步都按这一跑自己的 taskId,碰不到别人的认领)与
5
- * 起跑之前的认领复核答不出(`claim-heartbeat.ts` `recheckClaimBeforeLaunch`,同理按自己的 taskId)。
2
+ * 一条提交腿认领的 run 在起跑之前就收场 —— 撤回这次认领,撤回到**它取来时的样子**(撤回表的唯一属主 = `execution-context.ts` 的 `withdrawClaim`),按认领种类两种写法:
6
3
  *
7
- * 撤两步,**各自独立**(一步失败不拦另一步,残余最小):
8
- * · 终结这条 run 行(`setTerminal` 是按 taskId 的 CAS,首写者赢 ⇒ 重复调用是 no-op)—— 随之释放它的 `task_active` 认领;
9
- * · 放开它占的提交幂等键(拒绝答复邀请重试;同键重试必须真跑,不是回放这次拒绝 —— `RunStore.releaseIdemKey`)。
10
- * 撤不掉不致命、但要响:每一步的失败各交给调用方按自己的名字留痕(`onFailure`)。终结没落下 ⇒ 起跑前的心跳随调用腿收场停下(或从没起过),`reapStale`
11
- * 在 `REAP_RUN_STALE_SEC` 之后把这条行翻 failed 并释放会话;幂等键没放开 ⇒ 同键重提回放这条行的回执(两形都是修前每一次的样子)。
4
+ * {@link withdrawClaimedRun}(本模块)—— **本请求铸的行**(四个建行点 / wake 的现建行 / workflow 决定在宿主会话上的新一跑):取来之前没有这一行 ⇒ 删掉它
5
+ * (店的 `withdrawMintedRun`:本请求的行 = 这一个 `taskId` 上建行交回的那一只持有腿 —— 别的腿持着的行一个字节不动;行仍在跑 ⇒ 删行、它的事件与它的认领,
6
+ * 幂等键在行上随行去;已被收割 / 别的终局写先到 ⇒ 零删,那一份就是它的结局)。
7
+ * 调用点:撤回表的 `run` 臂(认领之后的提交拒 / 店故障、续跑腿交给引擎之前的抛)、续跑输入写不进(`submit-ctx.ts` `persistSubmitCtx`)、
8
+ * workflow 决定行没落成(`durable-run-leg.ts` 的 fresh 投递腿)与起跑之前的认领复核答不出(`claim-heartbeat.ts` `recheckClaimBeforeLaunch`)。
9
+ * 行从没交给引擎、提交没放行 ⇒ 同一个自带 `taskId` / 同一把 `Idempotency-Key` 的重发是一次新提交(真跑),不是这次拒的回放。
10
+ * 撤不掉(店整个不可达)不致命、但要响:失败交给调用方按自己的名字留痕(`onFailure`);行留在 `running`、起跑前的心跳随调用腿收场停下(或从没起过),
11
+ * `reapStale` 在 `REAP_RUN_STALE_SEC` 之后把它翻 failed 并释放会话(幂等键随那条终局行 —— 同键重提回放它,与撤不掉的终结写同一残余)。
12
+ *
13
+ * {@link releaseClaimedRun}(本模块)—— **续跑认领的停驻行续不了**(店答出它的会话已不在):这一行取来之前就在,不删 ⇒ 终结、放开认领与提交幂等键。撤两步,**各自独立**
14
+ * (一步失败不拦另一步,残余最小):终结(`setTerminal` 按 taskId 的 CAS,首写者赢 ⇒ 重复调用是 no-op,随之释放 `task_active` 认领)· 放开幂等键(`RunStore.releaseIdemKey`)。
15
+ *
16
+ * **还续得了**(续跑认领的停驻行,会话还在、它等的那张卡仍待决)⇒ 行回到认领之前的停驻词,认领、幂等键原样留着,同一张卡可以再决 ——
17
+ * 这是一次停驻,写法是停驻的唯一提交点 `park-row-commit.ts` 的 `commitParkRow`(续跑腿经 `commitResumePark`),不在本模块。
12
18
  */
13
19
  import type { RunStore } from "../plugins/store-backend.js";
20
+ export declare function withdrawClaimedRun(runStore: Pick<RunStore, "withdrawMintedRun">, taskId: string, legId: string, onFailure: (err: unknown, step: "withdraw") => void): Promise<void>;
14
21
  export declare function releaseClaimedRun(runStore: Pick<RunStore, "setTerminal" | "releaseIdemKey">, taskId: string, why: string, onFailure: (err: unknown, step: "terminal" | "idem_key") => void): Promise<void>;
15
22
  //# sourceMappingURL=claimed-run.d.ts.map
@@ -1,3 +1,11 @@
1
+ export async function withdrawClaimedRun(runStore, taskId, legId, onFailure) {
2
+ try {
3
+ await runStore.withdrawMintedRun(taskId, legId);
4
+ }
5
+ catch (err) {
6
+ onFailure(err, "withdraw");
7
+ }
8
+ }
1
9
  export async function releaseClaimedRun(runStore, taskId, why, onFailure) {
2
10
  try {
3
11
  await runStore.setTerminal(taskId, "failed", null, why);
@@ -15,7 +15,7 @@ import { projectHealthBody } from "./health-reader.js";
15
15
  import { projectHealthStoreSection, unwiredStorePosture } from "../store-posture.js";
16
16
  import { autoBackgroundOnTimeout as autoBackgroundOnTimeoutFact, autoBackgroundOnTimeoutWord, deploymentModPackages as deploymentModPackagesFact } from "../capability-facts.js";
17
17
  import { hostBackgroundShellEnabled } from "../config.js";
18
- import { actsForDeployment, admitRunIdentity, authorized, bearerPresentedButUnverified, systemFor, gatedSubject, sendPrincipalRefusal, sendUnauthorized, traceTokenPresented, verifiedIdentitySource, devOpenDeployment } from "./principal-gate.js";
18
+ import { actsForDeployment, admitRunIdentity, authorized, bearerPresentedButUnverified, devOpenDeployment, systemFor, gatedSubject, sendPrincipalRefusal, sendUnauthorized, traceTokenPresented, verifiedIdentitySource } from "./principal-gate.js";
19
19
  import { hasServiceAuth } from "../identity-source.js";
20
20
  import { inflightTotal } from "../fleet/drain-entry.js";
21
21
  const PROCESS_STARTED_AT_MS = Date.now() - Math.round(process.uptime() * 1000);
@@ -274,8 +274,10 @@ export function createDispatch(ctx) {
274
274
  return;
275
275
  }
276
276
  const mt = deps.config.metricsToken;
277
- const devOpen = devOpenDeployment(deps.config) && !mt;
278
- const ok = devOpen || (!!mt && authorized(req, mt)) || actsForDeployment(req, deps.config, () => gatedSubject(req, deps.config));
277
+ const readOnlyToken = !!mt && authorized(req, mt);
278
+ const freeArmOpen = !mt;
279
+ const voice = readOnlyToken || (!freeArmOpen && devOpenDeployment(deps.config)) ? undefined : actsForDeployment(req, deps.config, () => gatedSubject(req, deps.config));
280
+ const ok = readOnlyToken || voice === "deployment_token" || (voice === "dev_open" && freeArmOpen);
279
281
  if (!ok) {
280
282
  sendUnauthorized(res, "operator");
281
283
  return;
@@ -1,3 +1,4 @@
1
+ import { type ClaimFace } from "./execution-context.js";
1
2
  import type { RunStore } from "../plugins/store-backend.js";
2
3
  import type { RunIdemBinding, RunRecord } from "../plugins/store-contracts.js";
3
4
  import type { PreparedTaskSubmission, RouteCtxBase, WorkflowDecisionDelivery } from "./route-ctx.js";
@@ -32,6 +33,12 @@ export interface DurableRunLegArgs {
32
33
  * 与 `POST /v1/tasks` 两口同形,在飞集里的这一条不再叠一枚席位。落点拒 / 续跑输入拒 / 409 / 抛 等没走到登记的出口不放,由提交门的 `finally` 兜底(`release` 幂等)。
33
34
  * 缺席 = 没有帽可放(`/decide` 第三条车道〔延续,不判帽〕、帽关着的部署)。 */
34
35
  runSlot?: RunSlot;
36
+ /**
37
+ * 这一面的两只答法(`execution-context.ts` 的 `ClaimFace`):认领之后写口交回的店的拒怎么答(`refused`),认领这一段(建行认领 → 执行放置 → 续跑输入 → workflow 决定落行 →
38
+ * 起跑之前的认领复核)有一次店往返没答出来怎么答(`faulted`,经 `faultedAnswerOf`;程序错照旧上抛)—— 与这一面认领之前那一读同答。认领已在出事的那一笔撤回(本请求铸的行删掉)。
39
+ * 提交面(`POST /v1/runs` / A2A)交 `SUBMISSION_CLAIM_FACE`;`/decide` 第三条车道交 `CONTINUATION_CLAIM_FACE`(子代的卡仍待决;决定落行那一笔的结局路由按同一只答法答)。
40
+ */
41
+ face: ClaimFace;
35
42
  }
36
43
  /**
37
44
  * 本腿的结局:一份收据,或「这次提交的一把幂等钥匙已经指向一条行」(店的原子赢或观察的**观察**臂 —— 本腿不碰
@@ -54,7 +61,7 @@ export type DurableRunLegOutcome = {
54
61
  * 建行 + 驱动。返回 `202` = 新行已建并在跑(体带 `taskId`/`sessionId`);`409` = 会话被别的 run 占着,
55
62
  * **没有**建行(体 = `buildActiveRunConflict` 的既有形,含 `errorCode: "conflict.session_active_run"`);
56
63
  * `existing` = 这次提交的钥匙(`idem.key` / `clientTaskId`)已经指向一条行,没建新行;落点拒(device 车道)与续跑输入一行装不下
57
- * (`413 request.payload_too_large`,`submit-ctx.ts`)= 行建了又撤了(终结 + 放认领 + 放幂等键),什么都没跑。
64
+ * (`413 request.payload_too_large`,`submit-ctx.ts`)= 行建了又撤了(删掉,认领与幂等键随行去),什么都没跑。
58
65
  * 协议渲染归调用方(HTTP `sendJson` / A2A 协议对象 / `/decide` 的受理体)。
59
66
  */
60
67
  export declare function mintAndDriveDurableRun(ctx: RouteCtxBase, runStore: RunStore, args: DurableRunLegArgs): Promise<DurableRunLegOutcome>;
@@ -5,24 +5,38 @@ import { recheckClaimBeforeLaunch, refusalAfterLostClaim, startClaimHeartbeat }
5
5
  import { heldFor } from "../peer-seat.js";
6
6
  import { fleetRunPublisher, fleetRunLabels, nameFleetRowOnDelivery } from "../fleet/fleet-bus.js";
7
7
  import { cascadeConfig, fillRunPreviewOnDelivery, runMeta } from "./run-meta.js";
8
+ import { approvalLegOriginOf } from "../approval-leg-origin.js";
8
9
  import { declaredApprovalFrameFamilies, resolveStreamApprovalGate } from "../tool-approval.js";
9
10
  import { buildActiveRunConflict } from "./active-run-conflict.js";
10
- import { commitExecutionContext } from "./execution-context.js";
11
+ import { commitExecutionContext, faultedAnswerOf } from "./execution-context.js";
11
12
  import { rootSessionAbsentAnswer } from "./execution-context.js";
12
13
  import { timeClaim } from "./claim-latency.js";
13
14
  import { commitSessionInheritEnv } from "./session-inherit-env.js";
14
15
  import { clearTurnActivity, readTurnActivityMs } from "../turn-activity.js";
15
16
  import { recordResourceWindow } from "../resource-window.js";
16
17
  import { persistSubmissionCtx, SUBMIT_CTX_CLAIM_LOST } from "./submit-ctx.js";
17
- import { releaseClaimedRun } from "./claimed-run.js";
18
+ import { withdrawClaimedRun } from "./claimed-run.js";
18
19
  import { headReadOf } from "../session-incarnation.js";
19
20
  import { createSessionWorkspaceRecorder, sessionWorkspaceStoreOf } from "../session-workspace.js";
20
21
  export async function mintAndDriveDurableRun(ctx, runStore, args) {
21
22
  const { deps } = ctx;
22
- const { inflightRuns, preemptableRuns, steerableRuns } = ctx.registry;
23
23
  const sessionId = args.prepared.spec.sessionId;
24
24
  const taskId = args.clientTaskId ?? uuidv7();
25
25
  const trustedInternals = await heldWorkflowParkDecisions(deps, sessionId, args.workflowDecision?.carried);
26
+ let legId;
27
+ try {
28
+ const claimed = await claimAndCommit(ctx, runStore, args, sessionId, taskId);
29
+ if (!("holdingLeg" in claimed))
30
+ return claimed;
31
+ legId = claimed.holdingLeg;
32
+ }
33
+ catch (e) {
34
+ return faultedAnswerOf(e, args.face.faulted);
35
+ }
36
+ return launchDurableRun(ctx, runStore, args, sessionId, taskId, trustedInternals, legId);
37
+ }
38
+ async function claimAndCommit(ctx, runStore, args, sessionId, taskId) {
39
+ const { deps } = ctx;
26
40
  const created = await timeClaim(deps.metrics, "run_claim", runStore.createRun(taskId, sessionId, args.prepared.auth?.subject ?? null, deps.instanceId ?? "default", { ...runMeta(args.prepared, args.source), identity: args.identity, idem: args.idem ?? null }));
27
41
  if (created.ok)
28
42
  clearTurnActivity(taskId);
@@ -40,11 +54,11 @@ export async function mintAndDriveDurableRun(ctx, runStore, args) {
40
54
  const owner = args.prepared.auth?.subject ?? null;
41
55
  const stopPreLaunchHeartbeat = startClaimHeartbeat(runStore, taskId, owner, deps.metrics);
42
56
  try {
43
- const placementReject = await commitExecutionContext(deps, { kind: "run", runStore, taskId }, args.prepared.execution);
57
+ const placementReject = await commitExecutionContext(deps, { kind: "run", runStore, taskId, legId: created.legId }, args.prepared.execution);
44
58
  if (placementReject)
45
- return placementReject;
59
+ return args.face.refused(placementReject);
46
60
  if (deps.checkpointStore) {
47
- const ctxRefusal = await persistSubmissionCtx(deps.checkpointStore, deps, { runStore, taskId }, args.prepared);
61
+ const ctxRefusal = await persistSubmissionCtx(deps.checkpointStore, deps, { runStore, taskId, legId: created.legId }, args.prepared);
48
62
  if (ctxRefusal === SUBMIT_CTX_CLAIM_LOST)
49
63
  return await refusalAfterLostClaim(deps, runStore, sessionId, taskId);
50
64
  if (ctxRefusal)
@@ -55,13 +69,13 @@ export async function mintAndDriveDurableRun(ctx, runStore, args) {
55
69
  await args.workflowDecision.commitBeforeLaunch(taskId);
56
70
  }
57
71
  catch (err) {
58
- await releaseClaimedRun(runStore, taskId, `the workflow decision this run delivers could not be recorded: ${err instanceof Error ? err.message : String(err)}`, (releaseErr, step) => {
72
+ await withdrawClaimedRun(runStore, taskId, created.legId, (releaseErr, step) => {
59
73
  deps.logger?.warn?.("workflow_decision_run_release_failed", { taskId, sessionId, step, err: releaseErr instanceof Error ? releaseErr.message : String(releaseErr) });
60
74
  });
61
75
  throw err;
62
76
  }
63
77
  }
64
- const lostClaim = await recheckClaimBeforeLaunch(deps, runStore, sessionId, taskId);
78
+ const lostClaim = await recheckClaimBeforeLaunch(deps, runStore, sessionId, taskId, created.legId);
65
79
  if (lostClaim !== undefined)
66
80
  return lostClaim;
67
81
  }
@@ -69,6 +83,11 @@ export async function mintAndDriveDurableRun(ctx, runStore, args) {
69
83
  stopPreLaunchHeartbeat();
70
84
  }
71
85
  commitSessionInheritEnv(deps, args.prepared.spec.sessionId, args.prepared.body);
86
+ return { holdingLeg: created.legId };
87
+ }
88
+ function launchDurableRun(ctx, runStore, args, sessionId, taskId, trustedInternals, legId) {
89
+ const { deps } = ctx;
90
+ const { inflightRuns, preemptableRuns, steerableRuns } = ctx.registry;
72
91
  const anchorOwner = args.prepared.auth?.subject ?? null;
73
92
  const headOf = headReadOf(deps.sessionStorage);
74
93
  const captureTurnAnchor = deps.resumeAnchorStore && headOf
@@ -89,7 +108,7 @@ export async function mintAndDriveDurableRun(ctx, runStore, args) {
89
108
  ...fleetRunLabels(args.prepared.acceptance.objectiveAtSubmit),
90
109
  });
91
110
  nameFleetRowOnDelivery(args.prepared.acceptance, fleetPub);
92
- void heldFor(args.prepared.peerLease, () => runInBackground(args.prepared.runner, args.prepared.spec, runStore, taskId, deps.metrics, args.prepared.auth?.subject, args.prepared.verify, args.prepared.cascade ? cascadeConfig(deps.config.cascadeLadder, args.prepared.spec.limits?.maxCostUsd) : undefined, deps.instrumentDegenerate, deps.planCacheProbe, deps.config.traceThinking, inflightRuns, preemptableRuns, steerableRuns, deps.modelUsage, deps.elicitation, args.elicitOwner, captureTurnAnchor, fleetPub, captureUserMessageAnchor, deps.question, deps.workflowCompletionInbox, deps.subagentSteerRegistry, (m, x) => deps.logger?.info?.(m, x), deps.sendUserFile, deps.promptManifests, deps.toolApproval
111
+ void heldFor(args.prepared.peerLease, () => runInBackground(args.prepared.runner, args.prepared.spec, runStore, taskId, { legId, runStaleSec: deps.config.runStaleSec }, deps.metrics, args.prepared.auth?.subject, args.prepared.verify, args.prepared.cascade ? cascadeConfig(deps.config.cascadeLadder, args.prepared.spec.limits?.maxCostUsd) : undefined, deps.instrumentDegenerate, deps.planCacheProbe, deps.config.traceThinking, inflightRuns, preemptableRuns, steerableRuns, deps.modelUsage, deps.elicitation, args.elicitOwner, captureTurnAnchor, fleetPub, captureUserMessageAnchor, deps.question, deps.workflowCompletionInbox, deps.subagentSteerRegistry, (m, x) => deps.logger?.info?.(m, x), deps.sendUserFile, deps.promptManifests, deps.toolApproval
93
112
  ? {
94
113
  coordinator: deps.toolApproval,
95
114
  streamApprovalOn: resolveStreamApprovalGate({
@@ -101,9 +120,9 @@ export async function mintAndDriveDurableRun(ctx, runStore, args) {
101
120
  windowMs: deps.config.streamApproval?.windowMs,
102
121
  windowMarginMs: deps.config.streamAskWindowMarginMs,
103
122
  declaredFamilies: declaredApprovalFrameFamilies(args.prepared.body.settings),
104
- executionLane: args.prepared.execution.lane,
123
+ origin: approvalLegOriginOf(args.prepared.execution),
105
124
  }
106
- : undefined, deps.checkpointStore, args.prepared.engineNotices, trustedInternals, deps.userInputLedger, args.prepared.acceptance, createSessionWorkspaceRecorder(sessionWorkspaceStoreOf(deps.sessionStorage), args.prepared.legFacts?.sessionWitness), args.prepared.legFacts?.skills));
125
+ : undefined, deps.checkpointStore, args.prepared.engineNotices, trustedInternals, deps.userInputLedger, args.prepared.acceptance, createSessionWorkspaceRecorder(sessionWorkspaceStoreOf(deps.sessionStorage), args.prepared.legFacts?.sessionWitness), args.prepared.legFacts?.skills, args.prepared.legFacts?.registeredCwd));
107
126
  args.runSlot?.release();
108
127
  args.prepared.seat.markRecord();
109
128
  return { status: 202, body: { taskId, sessionId, status: "running", ...(args.prepared.warnings !== undefined ? { warnings: args.prepared.warnings } : {}) } };
@@ -1,5 +1,6 @@
1
1
  import type { IncomingMessage } from "node:http";
2
2
  import type { SessionHead, TaskSpec } from "@sema-agent/core";
3
+ import { HttpError } from "../security.js";
3
4
  import { type DeviceOwner, type DeviceStore } from "../device-store.js";
4
5
  import { type DeviceEnrollment, type DeviceRejection } from "../device-enrollment.js";
5
6
  import type { ServiceConfig } from "../config.js";
@@ -123,19 +124,30 @@ export type RootedTaskSpec = TaskSpec & {
123
124
  * 走到这里只能是擦型或 JS 形;拒在建任何行之前)。续跑家族照它自己的折叠律把这只拒答成 409 `resume_blocked_by_policy`(`blockedBy` = 这只码,
124
125
  * `http/resume-legs.ts` 的 `resolveResumeSpecOrBlocked`:装配期的每一只 `HttpError` 在续跑腿上都这么折),同样什么都没续。
125
126
  *
126
- * **根「必须已在」的唯一写者也是这里**(`requireExistingSession`;装配器不写它):根是宿主的 ⇒ 引擎取会话时不许「缺行即建」——
127
- * 解析器步 ① 读到过这一行(`occupant` 在场),或调用方自己要求了它(`requireExistingSession: true`;店不带执行席时这是唯一的来源)⇒ 盖 `true`:
128
- * 门与引擎取会话之间这一行被删(属主删除 / 受理前拒的撤回),引擎答 `resume.session_not_found`,不在原 id 上重建一只无主的空根。
129
- * **例外只有一条,来自引擎的派生形**:`cascade: true` 的提交不盖 —— 引擎派生每一级时只删 `sessionId`、其余字段照展开,带着这一位的级就成了
130
- * 「没有会话 id 却要求会话已在」,被引擎拒掉(每一级都跑不起来)。引擎把这一位随 `sessionId` 一并剥掉之后,cascade 也盖,这一条例外删去。
131
- * 没读到行、调用方也没要求 ⇒ 不盖(与引擎缺省同:没有宿主见过的根可护)。
127
+ * **根「必须已在」(`requireExistingSession`)的唯一写者也是这里**(装配器不写它),按调用方交来的 {@link RootUse} 穷举 —— 用法由**选引擎入口的那条腿**交
128
+ * (受理漏斗:与它交给提交腿的 `cascade` 同一个值;续跑重建:`session`;续跑的梯子臂:`ladder_host`),不从提交体再读一遍:
129
+ * · `session` —— 它是引擎在根上取会话时的「只接、不建」:解析器步 ① 读到过这一行(`occupant` 在场),或调用方自己要求了它(`requireExistingSession: true`;
130
+ * 店不带执行席时这是唯一的来源)⇒ 盖 `true`:门与引擎取会话之间这一行被删(属主删除 / 受理前拒的撤回),引擎答 `resume.session_not_found`,不在原 id 上重建一只
131
+ * 无主的空根;没读到行、调用方也没要求 ⇒ 不盖(只加不减:装配器交来的那一位原样留着);
132
+ * · `ladder_host` —— 引擎在梯子上不取根,没有可盖的取会话 ⇒ 盖出的 spec 上**没有**这一位(交来的 spec 带着也剥掉:续跑的梯子臂从重建的那一份再盖);
133
+ * 根在不在、门后被删由宿主判 —— 受理口的属主门(调用方要求了它而行不在 ⇒ 404)与执行起点的认领(带 run 店的部署:根行锁之后判在不在 / 放置提交判行在 ⇒ 404)。
132
134
  */
133
135
  export declare function stampSpecRoot(spec: ResolvedTaskSpec, ctx: ExecutionRoot, submission: SpecRootSubmission): RootedTaskSpec;
134
- /** {@link stampSpecRoot} 读的那两位提交事实(受理漏斗交这次的体;续跑家族交 checkpoint 上存的那一份体)。 */
135
- export interface SpecRootSubmission {
136
- readonly cascade?: unknown;
136
+ /**
137
+ * 引擎拿这次提交的根做什么(闭集;{@link stampSpecRoot} 按它穷举 —— 加一种用法 ⇒ 那只 `switch` 编译红):
138
+ * · `session` —— 引擎在根上取会话跑(单腿;验证门的实现腿与修复腿;续跑家族 —— 续跑、wake、宿主会话上新铸的一跑);
139
+ * · `ladder_host` —— 根是梯子的宿主(`cascade: true` 的组合入口):每一级在引擎为它现铸的会话里冷跑,引擎不取根;根只是这条梯子停驻卡的归属(组合腿的 `hostSessionId`)。
140
+ */
141
+ export type RootUse = "session" | "ladder_host";
142
+ /**
143
+ * {@link stampSpecRoot} 的第三参:选引擎入口的那条腿交来的用法;调用方「根必须已在」的要求只属于 `session` 用法(梯子上它由宿主的门与认领判,型上没有这一键)。
144
+ */
145
+ export type SpecRootSubmission = {
146
+ readonly use: "session";
137
147
  readonly requireExistingSession?: unknown;
138
- }
148
+ } | {
149
+ readonly use: "ladder_host";
150
+ };
139
151
  /**
140
152
  * run 店建行时根会话行已经不在(`CreateRunOutcome.sessionAbsent`:受理之后、认领之前会话被删)的**唯一**答复 ——
141
153
  * 四个建行点(同步 / 流式 / durable / wake)、解析期的「行不在 / 住客换了」与提交期的 `gone` 同答 404 `resume.session_not_found`,什么都没建、什么都没跑。
@@ -150,19 +162,100 @@ export interface ExecutionRefusal {
150
162
  readonly body: RefusalBody;
151
163
  }
152
164
  /**
153
- * 本次认领 —— 提交拒时要释放的东西。`run` = 四个建行点与续跑家族认领的 run 行(释放 = 终结 + 放认领 + 放幂等键,唯一写法 `releaseClaimedRun`);
154
- * `parked` = 停驻赎回的认领(释放 = 回滚认领;「会话不在」答那条腿既有的 `decide.not_parked`,不是提交腿的 404)。
165
+ * 本次认领 —— 提交没放行时要撤回的东西。撤回只有一张表({@link withdrawClaim}),按认领种类把认领撤回到**它取来时的样子**:
166
+ * · `run` = 本请求铸的行(四个建行点 / wake 的现建行 / workflow 决定在宿主会话上的新一跑;`legId` = 建行交回的持有腿):取来之前没有这一行 ⇒ 每一种收场都删掉它
167
+ * (唯一写法 `withdrawClaimedRun`:只认本腿持有的那一行,行仍在跑才删,认领与幂等键随行去;已被收割 / 终局 ⇒ 零删)—— 同一个 `taskId` / 同一把幂等键的重发是一次新提交;
168
+ * · `resumed` = 续跑家族 `markResuming` 认领的停驻行:取来之前它停在等一张待决的卡 ⇒ 放回停驻(`restore`:认领方的写法 = 停驻的唯一提交点 `park-row-commit.ts` 的 `commitParkRow`;
169
+ * 认领与幂等键留着,卡仍待决,同一个决定可以再发;认领窗里落下的取消由认领方按取消收场 —— 停驻态会让下一次认领重置取消旗)。
170
+ * 唯一的例外是 `gone`:店答出这条 run 的会话已经不在,它再也续不了 ⇒ 终结(同 `run` 那一只写法);
171
+ * · `parked` = 停驻赎回的认领:每一种收场都回滚认领。
172
+ * 答复不在认领上:写口交回店的拒答,各面经自己的 {@link ClaimFace} 答它。
155
173
  */
156
174
  export type ExecutionClaim = {
157
175
  readonly kind: "run";
158
176
  readonly runStore: RunStore;
159
177
  readonly taskId: string;
178
+ readonly legId: string;
179
+ } | {
180
+ readonly kind: "resumed";
181
+ readonly runStore: RunStore;
182
+ readonly taskId: string;
183
+ readonly restore: () => Promise<void>;
160
184
  } | {
161
185
  readonly kind: "parked";
162
186
  readonly taskId: string;
163
187
  readonly rollback: (why: string) => Promise<unknown>;
164
- readonly goneAnswer: ExecutionRefusal;
165
188
  };
189
+ /**
190
+ * 认领之后、交给引擎之前那一段没放行的收场 —— **唯一**的类别判定。规则一句:**店答出的拒是答案,店的故障不是**。判在 {@link commitExecutionContext} 的两只出口上
191
+ * (结构判:交回拒答 vs 抛,不读错误文案);续跑腿「提交放行之后、交给引擎之前」那一段的抛同属 `faulted`(`resume-legs.ts`,经同一张撤回表):
192
+ * · `gone` —— 店答出这条会话已经不在(行没了 / 住客换了,{@link isRootSessionGoneRefusal}):确定的答案,这条会话上什么都再也续不了;
193
+ * · `refused` —— 店答出这条会话此刻不能在本处执行(已放在别的车道、写者在别处、设备拒):确定的答案,但会话还在 —— 与认领之前解析器读出同一个事实是同一个命运;
194
+ * · `faulted` —— 店没答出来(故障 / 超时 / 程序错):证不出这条会话不能执行,不当答案用。答复只看(这一面 × 店有没有答出来):各面的瞬时答复({@link faultedAnswerOf}),
195
+ * 本服务自己的程序错照旧响亮 500。
196
+ */
197
+ export type CommitEnding = "gone" | "refused" | "faulted";
198
+ /**
199
+ * 提交点的拒答是不是「会话不在」那一只({@link rootSessionAbsentAnswer};唯一读者 —— 撤回表据它分 `gone` / `refused`,续跑家族与停驻赎回的 {@link ClaimFace} 据它决定那一只拒怎么答)。
200
+ * 只认这一只答复的码(与 {@link isRootSessionGoneError} 同一个判据,一个读拒答、一个读解析器的抛)。
201
+ */
202
+ export declare function isRootSessionGoneRefusal(refusal: ExecutionRefusal): boolean;
203
+ /**
204
+ * `faulted` 那一形的答复 —— 认领这一段(认领那一笔、认领之后的提交、续跑腿交给引擎之前的店往返、新提交起跑之前的店写)的一抛**只在这里**分两种:
205
+ * 店没答出来(判官的 `corrupt` / `connection` / `deployment` / `unclassified`)⇒ 这一面的瞬时答复(`answer`:新提交 {@link runStoreUnavailableAnswer} · 续跑家族
206
+ * `resume-ctx-read.ts` 的 `resumeClaimFaultAnswer` · 停驻赎回 `decide.store_unreachable`;每一面与它认领那一笔撞店故障同答);本服务自己的程序错(`defect`)⇒ 原样上抛,响亮成 500。
207
+ * 两种都不改撤回:认领已按 `faulted` 撤回(调用方在调本函数之前撤,或写口在抛之前撤)。判官加词而这里不表态 = 编译错。
208
+ * 设备注册簿那一次往返(设备店 / 准入链)也只经这里读:店没答出来 ⇒ 设备闭集的 {@link registryUnavailable},认领之前的准入与认领之后的首绑 / 再断言同答。
209
+ */
210
+ export declare function faultedAnswerOf<A>(e: unknown, answer: () => A): A;
211
+ /**
212
+ * 新提交(`POST /v1/tasks` 两腿、`POST /v1/runs` 与 A2A)撞店故障的**唯一**答复({@link SUBMISSION_CLAIM_FACE} 的 `faulted`)—— 503 `state.run_store_unavailable`:
213
+ * 认领之前那一次执行席读、认领那一笔(`createRun`)、认领之后的执行放置、续跑输入的写或起跑之前的认领复核,有一次店往返没答出来;本请求铸的行已撤回(删掉,认领与幂等键随行去),
214
+ * 什么都没起跑。稍后原样再发(同一个自带 `taskId` 也行);一直如此 ⇒ 请运维查库。句子不带店层 detail。🔴 形是给冻结门看的:`status: <三位数>, body: refusalBody({ errorCode: "<码>", message: "<整句双引号字面>"`,中间只许有空白。
215
+ */
216
+ export declare function runStoreUnavailableAnswer(): {
217
+ status: 503;
218
+ body: RefusalBody;
219
+ };
220
+ /**
221
+ * **一面的两只答法**(规则一句见文件头「一面一只答法」):这一面在认领之前那一读(解析器)、认领那一笔、认领之后每一笔、交给引擎之前的店往返上,
222
+ * 店答出的拒与店的故障各怎么答 —— 每一面只声明一次,各调用点只经它答,不各自选码:
223
+ * · `refused` —— 写口交回的店的拒答(会话不在 / 本处此刻不能执行)在这一面的答复:与这一面认领之前解析器读出同一个事实同一只答;
224
+ * · `faulted` —— 店没答出来在这一面的瞬时答复(经 {@link faultedAnswerOf} 读;本服务的程序错照旧上抛)。
225
+ * 三面:{@link SUBMISSION_CLAIM_FACE}(新提交)· {@link CONTINUATION_CLAIM_FACE}(续跑家族:决断 / `/resume` / `plan_review` / `/wake` / `/decide` 在宿主会话上起的新一跑)·
226
+ * 停驻赎回(`parked-decide.ts` 的 `parkedRedeemFace`,按子代句柄铸)。
227
+ */
228
+ export interface ClaimFace {
229
+ readonly refused: (refusal: ExecutionRefusal) => ExecutionRefusal;
230
+ readonly faulted: () => ExecutionRefusal;
231
+ }
232
+ /** 新提交这一面:店答出的拒原样答(认领之前解析器那一只 typed 拒同码同体);店没答出来 ⇒ {@link runStoreUnavailableAnswer}。 */
233
+ export declare const SUBMISSION_CLAIM_FACE: ClaimFace;
234
+ /**
235
+ * 续跑家族这一面:会话不在({@link isRootSessionGoneRefusal})⇒ 原答 404 `resume.session_not_found`;其余店答出的拒 ⇒ {@link cardWaitingRefusalAnswer}(与认领之前同一只折法);
236
+ * 店没答出来 ⇒ `resume-ctx-read.ts` 的 `resumeClaimFaultAnswer`(503 `state.resume_context_unreadable`,卡仍待决)。
237
+ */
238
+ export declare const CONTINUATION_CLAIM_FACE: ClaimFace;
239
+ /**
240
+ * 一只拒在「有一张卡仍在等」的那几面上的答复(续跑家族与停驻赎回;认领之前的解析器拒与认领之后写口交回的拒同经这里)—— 认领之前那一只折法
241
+ * (`checkpoint-ctx.ts` 的 `resumeAssemblyRefusalAnswer`:409 `resume_blocked_by_policy`,`blockedBy` = 原码,原事实随体;自报瞬时的拒原码原状态)+ `retriable`(卡仍待决)。
242
+ */
243
+ export declare function cardWaitingRefusalAnswer(refusal: ExecutionRefusal): ExecutionRefusal;
244
+ /** 一只拒答 → typed 拒(`HttpError`;体经 `httpErrorRefusal` 读回同一份:码、句、机读事实)。解析器的拒与提交门认领之前的拒只认这一种载体。 */
245
+ export declare function refusalHttpError(refusal: ExecutionRefusal): HttpError;
246
+ /**
247
+ * **认领之前那一读**在一面上的唯一读法(提交门 / 续跑家族的重建 / 停驻赎回同调唯一的解析器 {@link resolveExecutionContext}):解析器的拒(typed `HttpError`)是答案,
248
+ * 原样上抛给这一面自己的折法;店没答出来 ⇒ 抛这一面的瞬时答复(`face.faulted`,载体 {@link refusalHttpError} 外套 {@link ClaimFaultAnswer} —— 与这一面认领那一笔撞店故障同答,
249
+ * 续跑家族的折叠口认它、不折);本服务的程序错原样上抛(响亮 500)。认领之前没有认领可撤,状态零改动。
250
+ */
251
+ export declare function resolveBeforeClaim(deps: ExecutionContextDeps, entry: Extract<ExecutionEntry, {
252
+ readonly root: string;
253
+ }>, face: ClaimFace): Promise<RootedExecutionContext>;
254
+ /** {@link resolveBeforeClaim} 抛出的「店没答出来」答复(typed 拒;`answer` = 这一面的瞬时答复原体)。只有折叠口要认它(续跑家族不把它折成 409);提交门按 typed 拒原样答。 */
255
+ export declare class ClaimFaultAnswer extends HttpError {
256
+ readonly answer: ExecutionRefusal;
257
+ constructor(answer: ExecutionRefusal);
258
+ }
166
259
  /** {@link commitExecutionContext} 的依赖 —— 逐字取自 `ServiceDeps` 的同名键(同一只 deps 对象喂进来)。 */
167
260
  export interface ExecutionCommitDeps {
168
261
  readonly sessionStorage?: {
@@ -189,10 +282,18 @@ export declare function placementWriteDeadlineMs(runStaleSec: number | undefined
189
282
  export declare function devicePlacementErrorBody(rej: DeviceRejection): RefusalBody;
190
283
  /**
191
284
  * **认领之后的唯一写口**(四个 `createRun` 点、续跑家族 `markResuming` 之后、停驻赎回 `claimParkedAgent` 之后)。返回 `null` = 放行;
192
- * 否则本次认领已释放,调用方交回拒答。店故障 / 超时 ⇒ 释放后原样上抛(响亮,不当答案);设备意图那一支的注册簿故障答 {@link registryUnavailable}(设备闭集,同准入阶段)。
285
+ * 否则本次认领已按 {@link withdrawClaim} 撤回(店答出的拒 = `gone` / `refused`),调用方交回拒答。店故障 / 超时 ⇒ 认领按 `faulted` 撤回后原样上抛
286
+ * (响亮,不当答案:各调用方经 {@link faultedAnswerOf} 落自己这一面的瞬时码);设备意图那一支的注册簿往返(首绑 / 再断言)撞故障 ⇒ 交回设备闭集的 {@link registryUnavailable}
287
+ * (经 {@link faultedAnswerOf},与认领之前的准入同一只读法;程序错照旧上抛),执行席那一读撞故障与认领之前那一读同答(上抛)。
193
288
  *
194
289
  * 不变量(一句):**离开本函数的那一刻,这条会话(解析器那一位住客)的格就是这条执行的车道(device 车道:准入批准的那台设备);否则本次认领已释放、调用方拿到拒答。**
195
290
  * 它不说「此后不变」:认领持有期间根行住客与放置不被换绑 / 导入改动,那是各写者写事务内锁根行后读认领的保证(规则句),不在本函数重复判。
196
291
  */
197
292
  export declare function commitExecutionContext(deps: ExecutionCommitDeps, claim: ExecutionClaim, ctx: RootedExecutionContext): Promise<ExecutionRefusal | null>;
293
+ /**
294
+ * **撤回本次认领的唯一一张表**(规则与各种类的落点见 {@link ExecutionClaim};类别见 {@link CommitEnding})。两个调用处:{@link commitExecutionContext} 的两只出口,
295
+ * 与续跑腿「提交放行之后、交给引擎之前」那一段的抛(`resume-legs.ts`,`faulted`)。`ending` 只在 `resumed` 上分叉(会话不在 ⇒ 终结;其余 ⇒ 放回停驻),另两种认领取来时就只有一个去处。
296
+ * 认领之后的其余几笔(续跑输入、决定落行、起跑之前的复核)撤本请求铸的行用 `run` 那一臂的同一只写法(`withdrawClaimedRun`)。
297
+ */
298
+ export declare function withdrawClaim(deps: ExecutionCommitDeps, claim: ExecutionClaim, why: string, ending: CommitEnding): Promise<void>;
198
299
  //# sourceMappingURL=execution-context.d.ts.map