@sema-agent/server 7.149.0 → 7.150.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 (194) hide show
  1. package/MIGRATION.md +122 -9
  2. package/README.md +1 -1
  3. package/README.zh-CN.md +1 -1
  4. package/USAGE.md +39 -15
  5. package/dist/approval-card.d.ts +1 -1
  6. package/dist/approval-gate-currency.d.ts +1 -1
  7. package/dist/audit.d.ts +1 -1
  8. package/dist/audit.js +2 -2
  9. package/dist/background-session-reap.d.ts +1 -1
  10. package/dist/boot/deferred-sandbox-path-env.d.ts +5 -3
  11. package/dist/boot/deferred-sandbox-path-env.js +1 -2
  12. package/dist/boot/parked-revive-gate.d.ts +5 -5
  13. package/dist/boot/parked-revive-gate.js +2 -3
  14. package/dist/boot/resolve-spec.d.ts +4 -5
  15. package/dist/boot/resolve-spec.js +61 -25
  16. package/dist/boot/retention-lane.d.ts +1 -1
  17. package/dist/boot/session-faces.js +21 -21
  18. package/dist/boot/stage-03-store-backend.js +2 -2
  19. package/dist/boot/stage-06-runners.js +1 -2
  20. package/dist/boot/stage-07-capability-layer.js +1 -1
  21. package/dist/boot/stage-10-http-server.d.ts +33 -1
  22. package/dist/boot/stage-10-http-server.js +9 -4
  23. package/dist/boot/web-admin.d.ts +1 -1
  24. package/dist/capability-facts.d.ts +65 -56
  25. package/dist/capability-facts.js +42 -35
  26. package/dist/config-catalog.js +1 -1
  27. package/dist/config-types.d.ts +12 -11
  28. package/dist/deployment-governance.d.ts +7 -28
  29. package/dist/deployment-governance.js +1 -14
  30. package/dist/device-ws-hub.d.ts +1 -0
  31. package/dist/device-ws-hub.js +64 -28
  32. package/dist/device-ws-protocol.d.ts +20 -6
  33. package/dist/device-ws-protocol.js +9 -2
  34. package/dist/engine-identity.d.ts +7 -1
  35. package/dist/engine-identity.js +5 -0
  36. package/dist/field-conflict.d.ts +2 -2
  37. package/dist/fleet/engine-signal.d.ts +14 -1
  38. package/dist/fleet/engine-signal.js +5 -1
  39. package/dist/fleet/fleet-reconciler.d.ts +1 -1
  40. package/dist/fleet/serving-lease.d.ts +18 -3
  41. package/dist/fleet/serving-lease.js +81 -35
  42. package/dist/fleet-client.d.ts +13 -11
  43. package/dist/fleet-client.js +53 -33
  44. package/dist/history-anchor.js +1 -1
  45. package/dist/hitl-delivery.d.ts +1 -3
  46. package/dist/hitl-delivery.js +1 -1
  47. package/dist/http/active-run-conflict.d.ts +3 -7
  48. package/dist/http/active-run-conflict.js +2 -2
  49. package/dist/http/admission.js +2 -2
  50. package/dist/http/approval-sweeps.js +2 -1
  51. package/dist/http/caller-trust.d.ts +2 -2
  52. package/dist/http/caller-trust.js +3 -3
  53. package/dist/http/checkpoint-ctx.d.ts +18 -63
  54. package/dist/http/checkpoint-ctx.js +1 -24
  55. package/dist/http/claim-heartbeat.d.ts +4 -4
  56. package/dist/http/claim-heartbeat.js +3 -3
  57. package/dist/http/dispatch.js +2 -2
  58. package/dist/http/durable-run-leg.js +4 -4
  59. package/dist/http/owner-access.d.ts +243 -19
  60. package/dist/http/owner-access.js +184 -5
  61. package/dist/http/readiness.d.ts +4 -2
  62. package/dist/http/resume-ctx-read.js +16 -1
  63. package/dist/http/resume-legs.js +18 -14
  64. package/dist/http/route-ctx.d.ts +23 -11
  65. package/dist/http/routes/a2a-serve.js +15 -14
  66. package/dist/http/routes/approvals-assistant.d.ts +3 -2
  67. package/dist/http/routes/approvals-assistant.js +8 -9
  68. package/dist/http/routes/capabilities.js +4 -3
  69. package/dist/http/routes/config-catalog.d.ts +1 -1
  70. package/dist/http/routes/mcp-reconnect.js +2 -2
  71. package/dist/http/routes/memory-origin.d.ts +1 -1
  72. package/dist/http/routes/notify-wake.js +2 -3
  73. package/dist/http/routes/projects.d.ts +1 -0
  74. package/dist/http/routes/projects.js +5 -3
  75. package/dist/http/routes/retention-ops.d.ts +1 -1
  76. package/dist/http/routes/run-permission-mode.d.ts +1 -1
  77. package/dist/http/routes/run-permission-mode.js +2 -2
  78. package/dist/http/routes/runs.d.ts +1 -1
  79. package/dist/http/routes/runs.js +23 -30
  80. package/dist/http/routes/session-memory-sync.js +4 -5
  81. package/dist/http/routes/session-sync.js +3 -7
  82. package/dist/http/routes/sessions.d.ts +1 -1
  83. package/dist/http/routes/sessions.js +18 -44
  84. package/dist/http/routes/tasks.js +9 -8
  85. package/dist/http/routes/trace-usage.js +14 -14
  86. package/dist/http/routes/workflows.d.ts +1 -0
  87. package/dist/http/routes/workflows.js +20 -38
  88. package/dist/http/server.d.ts +8 -4
  89. package/dist/http/server.js +5 -1
  90. package/dist/http/stored-body-replay.d.ts +3 -24
  91. package/dist/http/stored-body-replay.js +2 -12
  92. package/dist/http/submit-ctx.d.ts +1 -2
  93. package/dist/http/submit-ctx.js +3 -2
  94. package/dist/http/submit-replay.d.ts +15 -2
  95. package/dist/http/submit-replay.js +10 -4
  96. package/dist/http/wire-types.d.ts +6 -3
  97. package/dist/leader/endpoint.js +9 -8
  98. package/dist/leg-facts.d.ts +8 -2
  99. package/dist/leg-facts.js +1 -0
  100. package/dist/mcp-authorization-face.d.ts +18 -14
  101. package/dist/mcp-authorization-face.js +3 -2
  102. package/dist/memory-store-refusal.d.ts +1 -1
  103. package/dist/observability/corrupt-read-seat.d.ts +1 -1
  104. package/dist/observability/fail-open.d.ts +5 -5
  105. package/dist/observability/fail-open.js +5 -5
  106. package/dist/observability/logger.d.ts +7 -0
  107. package/dist/observability/logger.js +3 -1
  108. package/dist/observability/run-terminal.d.ts +1 -1
  109. package/dist/owner-faces.d.ts +27 -3
  110. package/dist/owner-faces.js +2 -0
  111. package/dist/peer-seat.d.ts +1 -1
  112. package/dist/peer-seat.js +52 -59
  113. package/dist/plugins/exec-path-law.d.ts +34 -0
  114. package/dist/plugins/exec-path-law.js +13 -0
  115. package/dist/plugins/file-run-store.js +1 -1
  116. package/dist/plugins/local-checkpoint-store.d.ts +4 -1
  117. package/dist/plugins/local-checkpoint-store.js +3 -0
  118. package/dist/plugins/memory-run-store.js +1 -1
  119. package/dist/plugins/one-time-migrations.d.ts +74 -49
  120. package/dist/plugins/one-time-migrations.js +134 -69
  121. package/dist/plugins/pg-pool.js +4 -0
  122. package/dist/plugins/posix-shell-fs.js +4 -2
  123. package/dist/plugins/remote-env-adb.js +3 -2
  124. package/dist/plugins/remote-env-device.js +8 -6
  125. package/dist/plugins/remote-env-e2b.d.ts +3 -1
  126. package/dist/plugins/remote-env-e2b.js +11 -8
  127. package/dist/plugins/remote-env-host.d.ts +1 -1
  128. package/dist/plugins/remote-env-host.js +20 -11
  129. package/dist/plugins/remote-env-k8s.js +3 -2
  130. package/dist/plugins/remote-env-local-docker.js +3 -2
  131. package/dist/plugins/remote-env-ssh.js +4 -3
  132. package/dist/plugins/remote-shell.d.ts +3 -1
  133. package/dist/plugins/remote-shell.js +3 -3
  134. package/dist/plugins/replica-registration.d.ts +35 -0
  135. package/dist/plugins/replica-registration.js +48 -0
  136. package/dist/plugins/retention-lane-store-sql.d.ts +1 -1
  137. package/dist/plugins/run-store-sql.d.ts +1 -1
  138. package/dist/plugins/run-store-sql.js +2 -2
  139. package/dist/plugins/scenario-prefix-switch-m42.d.ts +6 -12
  140. package/dist/plugins/scenario-prefix-switch-m42.js +7 -5
  141. package/dist/plugins/schema-consistency.d.ts +7 -5
  142. package/dist/plugins/schema-consistency.js +29 -16
  143. package/dist/plugins/schema-drift-sentence.js +1 -1
  144. package/dist/plugins/serving-requirement.d.ts +49 -0
  145. package/dist/plugins/serving-requirement.js +53 -0
  146. package/dist/plugins/stop-then-start-sentence.d.ts +42 -3
  147. package/dist/plugins/stop-then-start-sentence.js +44 -0
  148. package/dist/plugins/store-backend.d.ts +1 -1
  149. package/dist/plugins/store-contracts.d.ts +12 -2
  150. package/dist/plugins/store-contracts.js +3 -0
  151. package/dist/plugins/tidb-pool.js +4 -0
  152. package/dist/process-instance.d.ts +3 -0
  153. package/dist/process-instance.js +7 -0
  154. package/dist/project-context.d.ts +66 -0
  155. package/dist/project-context.js +70 -0
  156. package/dist/question.d.ts +9 -21
  157. package/dist/question.js +28 -19
  158. package/dist/run-cancel-card.d.ts +1 -1
  159. package/dist/run-cancel-card.js +2 -1
  160. package/dist/security.d.ts +1 -1
  161. package/dist/session-agent.d.ts +1 -10
  162. package/dist/session-agent.js +0 -6
  163. package/dist/session-incarnation.d.ts +18 -4
  164. package/dist/session-incarnation.js +4 -1
  165. package/dist/session-notify-door.d.ts +4 -4
  166. package/dist/session-notify-door.js +5 -6
  167. package/dist/session-rules-seat.d.ts +4 -3
  168. package/dist/session-rules-seat.js +3 -11
  169. package/dist/session-sync.js +3 -3
  170. package/dist/stores-sql/checkpoint-store-sql.d.ts +21 -2
  171. package/dist/stores-sql/checkpoint-store-sql.js +106 -18
  172. package/dist/stores-sql/retention-store-sql.d.ts +1 -1
  173. package/dist/task-cwd.d.ts +4 -3
  174. package/dist/task-settings.d.ts +13 -0
  175. package/dist/task-settings.js +13 -7
  176. package/dist/tool-approval.d.ts +7 -6
  177. package/dist/tool-approval.js +41 -37
  178. package/dist/trace/engine-notice-wire.d.ts +14 -0
  179. package/dist/trace/engine-notice-wire.js +18 -1
  180. package/dist/trace/project.d.ts +4 -3
  181. package/dist/trace/project.js +3 -2
  182. package/dist/trace/projection-table.d.ts +33 -2
  183. package/dist/trace/self-minted-wire.d.ts +150 -17
  184. package/dist/trace/self-minted-wire.js +54 -16
  185. package/dist/trace/tool-card.d.ts +3 -0
  186. package/dist/trace/tool-card.js +24 -0
  187. package/dist/trace/tool-output-media.d.ts +2 -1
  188. package/dist/unattended-permission-mode.d.ts +1 -1
  189. package/dist/web-admin-client/apply-effective.d.ts +1 -1
  190. package/dist/web-admin-client/effective-keys.d.ts +1 -1
  191. package/dist/web-admin-client/skills-mcp.d.ts +1 -1
  192. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +57 -29
  193. package/package.json +2 -2
  194. package/wire-changes/server@7.150.0.json +335 -0
@@ -14,8 +14,8 @@ import { readWritableSessionPermissionRules } from "../../session-policy-wire.js
14
14
  import { sendJson, sendError, sendSessionError, sendSessionPolicyError, sseHeaders, httpErrorBody } from "../send.js";
15
15
  import { gatedPerson, gatedSubject, sendPrincipalRefusal } from "../principal-gate.js";
16
16
  import { personOf } from "../../owner-brands.js";
17
- import { headReadOf, leafOfAdmittedHead, sessionFaceOfHead } from "../../session-incarnation.js";
18
- import { admitSessionById, admitSessionRow, admittedAsFleet, callerEntry, callerOf, deliverVerdict, enter, judgeSessionRow, protocolEntry, readOwnerCell, refuse, seesRun, sessionRowPresent } from "../owner-access.js";
17
+ import { leafOfAdmittedHead, sessionFaceOfHead } from "../../session-incarnation.js";
18
+ import { admitSessionById, admitSessionForDeletion, admitSessionRow, admitSessionView, admittedAsFleet, callerEntry, callerOf, deliverVerdict, enter, judgeSessionById, protocolEntry, readOwnerCell, readVisibleLatestRun, refuse, sessionOwnerUnchanged } from "../owner-access.js";
19
19
  import { assertNever } from "../route-ctx.js";
20
20
  import * as facts from "../../capability-facts.js";
21
21
  import { rosterAgentId } from "./agents-roster.js";
@@ -67,11 +67,9 @@ async function readLastLegMcp(deps, sessionId, entry) {
67
67
  const runStore = deps.runStore;
68
68
  if (!runStore || sessionId === null)
69
69
  return undefined;
70
- const latest = await runStore.latestRunForSession(sessionId);
70
+ const latest = await readVisibleLatestRun(entry, runStore, sessionId);
71
71
  if (latest === undefined)
72
72
  return undefined;
73
- if (!seesRun(entry, latest.owner))
74
- return undefined;
75
73
  const events = await runStore.getEvents(latest.taskId, 0);
76
74
  for (let i = events.length - 1; i >= 0; i--) {
77
75
  const ev = events[i];
@@ -87,26 +85,7 @@ async function readLastLegMcp(deps, sessionId, entry) {
87
85
  export async function handleSessions(req, res, match, ctx) {
88
86
  const { deps } = ctx;
89
87
  const { readJson, safeDecode } = ctx.helpers;
90
- const readOwnedSessionView = async (entry, sessionAudit, sessionId, readOpts) => {
91
- const headOf = headReadOf(deps.sessionStorage);
92
- if (headOf !== undefined) {
93
- const head = await readOwnerCell(entry, sessionId, () => headOf(sessionId));
94
- if ((await admitSessionRow(entry, res, deps.sessionStorage, sessionId, head)) === undefined)
95
- return undefined;
96
- }
97
- let audit;
98
- try {
99
- audit = await sessionAudit(sessionId, readOpts);
100
- }
101
- catch (e) {
102
- if (headOf !== undefined)
103
- throw e;
104
- throw new Error(`session view read failed before ownership could be established: ${e instanceof Error ? e.message : String(e)}`, { cause: e });
105
- }
106
- if ((await admitSessionRow(entry, res, deps.sessionStorage, sessionId, audit)) === undefined)
107
- return undefined;
108
- return audit;
109
- };
88
+ const readOwnedSessionView = (entry, sessionAudit, sessionId, readOpts) => admitSessionView(entry, res, deps.sessionStorage, sessionAudit, sessionId, readOpts);
110
89
  const sse = ctx.local.sessions;
111
90
  const { mcpStatusCache, a2aStatusCache } = ctx.local.sessions;
112
91
  {
@@ -262,8 +241,8 @@ export async function handleSessions(req, res, match, ctx) {
262
241
  }
263
242
  {
264
243
  if (match.decl.id === "session-head") {
265
- const headOf = headReadOf(deps.sessionStorage);
266
- if (!headOf) {
244
+ const headFact = facts.sessionOwnership(deps);
245
+ if (!headFact.available) {
267
246
  sendError(res, 501, "capability.session_store_required", "session head probe requires a durable session store (DB_BACKEND=mysql|pg|local)");
268
247
  return;
269
248
  }
@@ -273,8 +252,7 @@ export async function handleSessions(req, res, match, ctx) {
273
252
  return;
274
253
  }
275
254
  const headEntry = callerEntry(req, deps, match.decl.id);
276
- const headRead = await readOwnerCell(headEntry, headSession, () => headOf(headSession));
277
- const admittedHead = await admitSessionRow(headEntry, res, deps.sessionStorage, headSession, headRead);
255
+ const admittedHead = await admitSessionById(headEntry, res, headFact.sessionStorage, headSession);
278
256
  if (admittedHead === undefined)
279
257
  return;
280
258
  const leafId = leafOfAdmittedHead(admittedHead.row);
@@ -317,8 +295,6 @@ export async function handleSessions(req, res, match, ctx) {
317
295
  return;
318
296
  }
319
297
  const evEntry = callerEntry(req, deps, match.decl.id);
320
- const readHead = (id) => readOwnerCell(evEntry, id, () => evFact.sessionStorage.headOf(id));
321
- const readOwner = async (id) => (await readHead(id))?.owner;
322
298
  const watch = evFact.watch;
323
299
  const evSession = safeDecode(match.params.sessionId);
324
300
  if (evSession === null) {
@@ -354,7 +330,7 @@ export async function handleSessions(req, res, match, ctx) {
354
330
  return;
355
331
  }
356
332
  sse.sseOwnerLookups++;
357
- const admissionP = readHead(evSession).then(async (head) => (!sessionRowPresent(head) ? undefined : { head, verdict: await judgeSessionRow(evEntry, evFact.sessionStorage, evSession, head) }));
333
+ const admissionP = judgeSessionById(evEntry, evFact.sessionStorage, evSession);
358
334
  void admissionP.catch(() => undefined).then(() => { sse.sseOwnerLookups--; });
359
335
  let stallTimer;
360
336
  let admission;
@@ -381,12 +357,13 @@ export async function handleSessions(req, res, match, ctx) {
381
357
  refuse(evEntry, res, "missing");
382
358
  return;
383
359
  }
384
- if (deliverVerdict(evEntry, res, admission.verdict) === undefined) {
360
+ const evAdmitted = deliverVerdict(evEntry, res, admission);
361
+ if (evAdmitted === undefined) {
385
362
  cleanup();
386
363
  return;
387
364
  }
388
- leafOfAdmittedHead(admission.head);
389
- const owner = admission.head.owner;
365
+ leafOfAdmittedHead(evAdmitted.row);
366
+ const owner = evAdmitted.row.owner;
390
367
  const q = new URLSearchParams(match.search);
391
368
  const lastLeafId = q.get("lastLeafId");
392
369
  let first = true;
@@ -485,9 +462,9 @@ export async function handleSessions(req, res, match, ctx) {
485
462
  }
486
463
  ownerCheckPending = true;
487
464
  ownerCheckStartedAt = Date.now();
488
- void readOwner(evSession).then((o) => {
465
+ void sessionOwnerUnchanged(evEntry, evFact.sessionStorage, evSession, owner).then((same) => {
489
466
  ownerCheckPending = false;
490
- if (!closed && o !== owner)
467
+ if (!closed && !same)
491
468
  onEnd();
492
469
  }).catch(() => { ownerCheckPending = false; });
493
470
  }, hbMs);
@@ -706,14 +683,11 @@ export async function handleSessions(req, res, match, ctx) {
706
683
  const store = rowFact.sessionStorage;
707
684
  const sessionId = match.params.sessionId;
708
685
  const patchEntry = callerEntry(req, deps, match.decl.id);
709
- const head = await store.headOf(sessionId);
710
- if (head === undefined) {
711
- refuse(patchEntry, res, "missing");
686
+ const patchAdmitted = await admitSessionById(patchEntry, res, store, sessionId);
687
+ if (patchAdmitted === undefined)
712
688
  return;
713
- }
689
+ const head = patchAdmitted.row;
714
690
  const face = sessionFaceOfHead(sessionId, head);
715
- if ((await admitSessionRow(patchEntry, res, store, sessionId, face)) === undefined)
716
- return;
717
691
  leafOfAdmittedHead(head);
718
692
  const read = readSessionRowPatch(await readJson(req));
719
693
  if ("problem" in read) {
@@ -903,7 +877,7 @@ export async function handleSessions(req, res, match, ctx) {
903
877
  sendError(res, 400, "request.id_invalid", "invalid session id (must be a canonical uuid)");
904
878
  return;
905
879
  }
906
- const deleteAdmitted = await admitSessionById(deleteEntry, res, deleteFact.gateStore, sessionId);
880
+ const deleteAdmitted = await admitSessionForDeletion(deleteEntry, res, deleteFact.sessionStorage, sessionId);
907
881
  if (deleteAdmitted === undefined)
908
882
  return;
909
883
  const owner = deleteAdmitted.row.owner;
@@ -21,7 +21,8 @@ import { writeLiveFrame, writeLiveHeartbeat } from "../live-frame.js";
21
21
  import { contextUsageEventData, turnEndEventData, toolStartEventData, toolEndEventData, taskProgressEventData, taskStartedEventData, taskNotificationEventData, compactedEventData, diagnosticsEventData, brainStatusEventData, steeringInjectedEventData, compactionOutcomeEventData, compactionStartedEventData, workspaceChangedEventData, wiringManifestFaceEventData, toolRosterDeltaEventData, toolProgressEventData, toolDisclosureEventData, humanInputEventData, appendModelUsageDelta, attachModelUsage, createPromptManifestSeat, identityFields } from "../../trace/project.js";
22
22
  import { cascadeConfig, fillRunPreviewOnDelivery, runMeta } from "../run-meta.js";
23
23
  import { idemCallerOf, inflightKeyOf, submissionIdemOf } from "../idempotency.js";
24
- import { isReplayedOutcome, replayedOutcome, replayOfExistingRun } from "../submit-replay.js";
24
+ import { answerReplay, isReplayedOutcome, replayedOutcome, replayOfExistingRun } from "../submit-replay.js";
25
+ import { readReplayHit, runFactsOf } from "../owner-access.js";
25
26
  import { httpErrorCode, sendJson, sseHeaders } from "../send.js";
26
27
  import { collectSsePreamble } from "../sse-log.js";
27
28
  import { buildApprovalPreamble } from "../../approval-card.js";
@@ -109,12 +110,12 @@ export async function handleTasks(req, res, match, ctx) {
109
110
  const idem = rawIdem ? submissionIdemOf(rawIdem, idemCallerOf(req, ctx), keyedBody) : undefined;
110
111
  const inflightKey = idem ? inflightKeyOf(idem, "sync") : undefined;
111
112
  const cached = inflightKey ? idemCache.peek(inflightKey) : undefined;
112
- const keyedRow = idem && !cached && deps.runStore ? await deps.runStore.getRunByIdem(idem.key) : undefined;
113
- if (keyedRow && deps.runStore) {
114
- const replay = await replayOfExistingRun(keyedRow, { subject: submitter, idem }, "sync", deps, deps.runStore);
115
- if (liveStream && replay.status === 200) {
116
- sseHeaders(res, { "x-task-id": keyedRow.taskId });
117
- writeMetaFrame(res, keyedRow.taskId, keyedRow.sessionId);
113
+ const keyedHit = idem && !cached && deps.runStore ? await readReplayHit(submitter, deps.config, deps.runStore, { idemKey: idem.key }, undefined) : undefined;
114
+ if (keyedHit && deps.runStore) {
115
+ const replay = await answerReplay(keyedHit, { subject: submitter, idem }, "sync", deps.runStore);
116
+ if (liveStream && replay.status === 200 && keyedHit.kind === "visible") {
117
+ sseHeaders(res, { "x-task-id": keyedHit.row.taskId });
118
+ writeMetaFrame(res, keyedHit.row.taskId, keyedHit.row.sessionId);
118
119
  writeLiveFrame(res, doneFrameOfOutcome(replay));
119
120
  res.end();
120
121
  }
@@ -800,7 +801,7 @@ export async function handleTasks(req, res, match, ctx) {
800
801
  evict();
801
802
  if (!runRowSettled && durableTaskId && deps.runStore) {
802
803
  try {
803
- const row = await deps.runStore.getRun(durableTaskId);
804
+ const row = await runFactsOf(deps.runStore, durableTaskId);
804
805
  if (row?.status === "running") {
805
806
  const pending = await deps.checkpointStore?.findPendingTokenBySession(prepared.spec.sessionId, undefined, { axis: "run" });
806
807
  const backstopCause = ac.cause();
@@ -9,7 +9,7 @@ import { bindSseLifecycle } from "../sse-lifecycle.js";
9
9
  import { sendJson, sendError, sseHeaders, SSE_MAX_STREAM_MS, SSE_HEARTBEAT_IDLE_MS } from "../send.js";
10
10
  import { filterFingerprint } from "../cursor-fingerprint.js";
11
11
  import { sendUnauthorized } from "../principal-gate.js";
12
- import { admitRunById, enter, entryIsFleet, readVisibleRun, routeListFilter, runRowAt } from "../owner-access.js";
12
+ import { admitRunById, enter, entryIsFleet, listVisibleRuns, readVisibleRun, routeListFilter, runListOwnerOf, toolResultRefOwned } from "../owner-access.js";
13
13
  import { listFilterOf } from "../../owner-faces.js";
14
14
  import { assertNever } from "../route-ctx.js";
15
15
  import * as facts from "../../capability-facts.js";
@@ -42,7 +42,7 @@ export async function handleTraceUsage(req, res, match, ctx) {
42
42
  const traceEntry = enter(req, res, deps, match.decl.id);
43
43
  if (traceEntry === undefined)
44
44
  return;
45
- const gateOwner = listFilterOf(traceEntry.face, traceEntry.caller).gateOwner;
45
+ const traceFilter = listFilterOf(traceEntry.face, traceEntry.caller);
46
46
  const runs = facts.durableRuns(deps);
47
47
  if (!runs.available) {
48
48
  sendError(res, 501, "capability.run_store_required", "trace API requires a durable run store (DB_BACKEND=mysql|pg|local)");
@@ -61,20 +61,20 @@ export async function handleTraceUsage(req, res, match, ctx) {
61
61
  }
62
62
  switch (match.decl.id) {
63
63
  case "task-trace-list":
64
- await handleTaskList(res, runStore, query, gateOwner);
64
+ await handleTaskList(res, runStore, query, traceFilter);
65
65
  return;
66
66
  case "task-trace-turns":
67
67
  await handleTaskTurns(res, runStore, admittedRun, query);
68
68
  return;
69
69
  case "task-trace-stream":
70
- await streamTaskTrace(req, res, runStore, admittedRun, match.search);
70
+ await streamTaskTrace(req, res, runStore, traceEntry, admittedRun, match.search);
71
71
  return;
72
72
  case "task-trace-artifacts":
73
73
  await handleTaskArtifacts(res, runStore, admittedRun);
74
74
  return;
75
75
  case "task-tool-result": {
76
76
  const ownedRun = admittedRun;
77
- const readRun = ownedRun !== undefined ? async () => ownedRun : () => readVisibleRun(traceEntry, runStore, taskId);
77
+ const readRun = ownedRun !== undefined ? async () => ownedRun : async () => (await readVisibleRun(traceEntry, runStore, taskId))?.row;
78
78
  await handleToolResult(res, deps.toolResultStore, taskId, readRun, safeDecode(match.params.ref), query);
79
79
  return;
80
80
  }
@@ -161,11 +161,12 @@ function decodeCursor(s, expectF) {
161
161
  return undefined;
162
162
  }
163
163
  }
164
- async function handleTaskList(res, runStore, query, forceOwner) {
164
+ async function handleTaskList(res, runStore, query, filter) {
165
165
  const status = query.get("status") ?? undefined;
166
166
  const jobId = query.get("jobId") ?? undefined;
167
167
  const source = query.get("source") ?? undefined;
168
- const owner = forceOwner != null ? forceOwner : (query.get("owner") ?? undefined);
168
+ const namedOwner = query.get("owner") ?? undefined;
169
+ const owner = runListOwnerOf(filter, namedOwner);
169
170
  const limit = Math.min(100, Math.max(1, Number(query.get("limit") ?? 50) || 50));
170
171
  const f = filterFingerprint([status, jobId, source, owner]);
171
172
  const cursor = decodeCursor(query.get("cursor"), f);
@@ -173,7 +174,7 @@ async function handleTaskList(res, runStore, query, forceOwner) {
173
174
  sendError(res, 400, "request.cursor_filter_mismatch", "cursor was issued under a different filter (status/jobId/source/owner) — restart from the first page without a cursor");
174
175
  return;
175
176
  }
176
- const rows = await runStore.listRuns({ ...(status ? { status } : {}), ...(jobId ? { jobId } : {}), ...(source ? { source } : {}), ...(owner ? { owner } : {}), ...(cursor ? { cursor } : {}), limit: limit + 1 });
177
+ const rows = await listVisibleRuns(runStore, filter, namedOwner, { ...(status ? { status } : {}), ...(jobId ? { jobId } : {}), ...(source ? { source } : {}), ...(cursor ? { cursor } : {}), limit: limit + 1 });
177
178
  const hasMore = rows.length > limit;
178
179
  const page = hasMore ? rows.slice(0, limit) : rows;
179
180
  const tasks = page.map((r) => taskSummary(r));
@@ -216,8 +217,7 @@ async function handleToolResult(res, store, taskId, readRun, ref, query) {
216
217
  const run = await readRun();
217
218
  if (!run)
218
219
  return notFound();
219
- const owner = store?.ownerOf ? await store.ownerOf(ref) : undefined;
220
- const ownedByThisCaller = owner !== undefined && owner.sessionId === run.sessionId && (owner.taskId === undefined || owner.taskId === taskId);
220
+ const ownedByThisCaller = await toolResultRefOwned(store, ref, run);
221
221
  if (!ownedByThisCaller)
222
222
  return notFound();
223
223
  if (!store)
@@ -240,7 +240,7 @@ async function handleTaskTurns(res, runStore, run, query) {
240
240
  const nextCursor = eligible.length > limit && oldest ? String(oldest.seq) : undefined;
241
241
  sendJson(res, 200, { turns, ...(nextCursor ? { nextCursor } : {}), retainedFrom });
242
242
  }
243
- async function streamTaskTrace(req, res, runStore, admitted, search) {
243
+ async function streamTaskTrace(req, res, runStore, entry, admitted, search) {
244
244
  const taskId = admitted.taskId;
245
245
  const lastId = Number(req.headers["last-event-id"] ?? new URLSearchParams(search).get("from") ?? 0);
246
246
  let from = Number.isFinite(lastId) ? lastId : 0;
@@ -264,11 +264,11 @@ async function streamTaskTrace(req, res, runStore, admitted, search) {
264
264
  }
265
265
  };
266
266
  while (!closed) {
267
- const [events, raw] = await Promise.all([runStore.getEvents(taskId, from), runStore.getRun(taskId)]);
267
+ const [events, reread] = await Promise.all([runStore.getEvents(taskId, from), readVisibleRun(entry, runStore, taskId)]);
268
268
  write(events);
269
- if (!raw)
269
+ if (!reread)
270
270
  break;
271
- const run = await runRowAt("task-trace-stream", runStore, raw);
271
+ const run = reread.row;
272
272
  if (run.status !== "running") {
273
273
  write(await runStore.getEvents(taskId, from));
274
274
  if (!deliveredTerminal && isHolderDeathVerdict(run))
@@ -2,6 +2,7 @@ import type { IncomingMessage, ServerResponse } from "node:http";
2
2
  import { type WorkflowRun } from "@sema-agent/core";
3
3
  import { type Admit } from "../../trace/projection-table.js";
4
4
  import type { RouteCtx, RouteMatch, RouteIdsOf } from "../route-ctx.js";
5
+ /** SVC-5 的 steer 路——`handle()` 里位置在 tasks/runs 之后(鉴权门之后),故是**独立**的域入口。 */
5
6
  /** 路由体 = 从 `server.ts` 的 `handle()` 里**整段剪切**过来的原文(唯一改动:统一去缩进两格)。裸 `return;`
6
7
  * = 「本域已应答」;走到函数尾才是「没匹配上」。跨域的东西一律在首行解构出来。 */
7
8
  export declare function handleWorkflows(req: IncomingMessage, res: ServerResponse, match: RouteMatch<WorkflowsRouteId>, ctx: RouteCtx): Promise<void>;
@@ -1,6 +1,6 @@
1
1
  import { persistedPlaneOf } from "../../terminal.js";
2
2
  import { z } from "zod";
3
- import { getWorkflowRun, subscribeWorkflow, deriveAgentDisplayStatus, callKeyOrdinal, runParkedOf, JOURNAL_INCOMPATIBLE_ERROR_CODE, WorkflowJournalIncompatibleError } from "@sema-agent/core";
3
+ import { subscribeWorkflow, deriveAgentDisplayStatus, callKeyOrdinal, runParkedOf, JOURNAL_INCOMPATIBLE_ERROR_CODE, WorkflowJournalIncompatibleError } from "@sema-agent/core";
4
4
  import { redactSteerIn, STEER_IN_MAX_CHARS, STEER_IN_MAX_REQUEST_CHARS } from "../../orchestration/workflow-agent-steer.js";
5
5
  import { journalEntryOfCell, journalEntryPayload, journalRowPayloadOf } from "../../plugins/workflow-journal-payload.js";
6
6
  import { redactSecrets } from "../../trace/redact.js";
@@ -10,30 +10,18 @@ import { projectByTable } from "../../trace/projection-table.js";
10
10
  const JOURNAL_PROJECTION_MEMO_CAP = 1024;
11
11
  const journalProjectionMemo = new Map();
12
12
  import { sendJson, sendError, sseHeaders } from "../send.js";
13
- import { gatedSubject, explicitOperatorOk, sendPrincipalRefusal } from "../principal-gate.js";
13
+ import { explicitOperatorOk } from "../principal-gate.js";
14
14
  import { partsOf } from "../../owner-brands.js";
15
- import { taskSpecIdentityOf } from "../../engine-identity.js";
16
15
  import { assertNever } from "../route-ctx.js";
17
- import { callerOf, sessionAsserted } from "../owner-access.js";
16
+ import { admitWorkflowRun, enter, judgeWorkflowRun, listVisibleWorkflows, refuse } from "../owner-access.js";
18
17
  import * as facts from "../../capability-facts.js";
19
18
  import { steerBodyKeyIssue } from "../../request-key-closure.js";
20
- function sendWorkflowAbsent(res) {
21
- sendError(res, 404, "not_found.workflow", "workflow not found");
22
- }
23
- const WORKFLOW_SESSION_ASSERTION = {
24
- "workflow-detail": { phase: "accept", label: "workflow.detail" },
25
- "workflow-journal": { phase: "accept", label: "workflow.journal" },
26
- "workflow-stream": { phase: "accept", label: "workflow.stream" },
27
- "workflow-agent-steer": { phase: "accept", label: "workflow.agent-steer" },
28
- };
29
19
  export async function handleWorkflows(req, res, match, ctx) {
30
20
  const { deps } = ctx;
31
21
  {
32
- const subject = gatedSubject(req, deps.config);
33
- if (deps.config.requirePrincipal && !subject) {
34
- sendPrincipalRefusal(res, req, deps.config);
22
+ const wfEntry = enter(req, res, deps, match.decl.id);
23
+ if (wfEntry === undefined)
35
24
  return;
36
- }
37
25
  let journalStore;
38
26
  if (match.decl.id === "workflow-journal") {
39
27
  const journalFace = facts.workflowsJournal(deps);
@@ -49,7 +37,6 @@ export async function handleWorkflows(req, res, match, ctx) {
49
37
  return;
50
38
  }
51
39
  const runStore = runs.store;
52
- const scope = taskSpecIdentityOf(subject === undefined ? undefined : partsOf(subject)).principal ?? "default";
53
40
  const wfId = match.params.runId;
54
41
  const isStream = match.decl.id === "workflow-stream";
55
42
  if (match.decl.id === "workflows-list") {
@@ -57,7 +44,7 @@ export async function handleWorkflows(req, res, match, ctx) {
57
44
  const statusFilter = q.get("status");
58
45
  const limit = Math.min(100, Math.max(1, Number(q.get("limit") ?? 50) || 50));
59
46
  const sessionFilter = q.get("session");
60
- const sessionRuns = await runStore.listByScope(scope, {
47
+ const sessionRuns = await listVisibleWorkflows(wfEntry, runStore, {
61
48
  ...(statusFilter ? { status: statusFilter } : {}),
62
49
  limit,
63
50
  ...(sessionFilter !== null ? { session: sessionFilter } : {}),
@@ -71,15 +58,11 @@ export async function handleWorkflows(req, res, match, ctx) {
71
58
  sendJson(res, 200, { workflows: redacted });
72
59
  return;
73
60
  }
74
- const run = await getWorkflowRun(runStore, wfId, scope);
75
- if (!run || run.scope !== scope) {
76
- sendWorkflowAbsent(res);
77
- return;
78
- }
79
- if (!sessionAsserted(WORKFLOW_SESSION_ASSERTION[match.decl.id], req, run.originatingSessionId, callerOf(req, deps).operator)) {
80
- sendWorkflowAbsent(res);
61
+ const wfAdmitted = await admitWorkflowRun(wfEntry, res, runStore, wfId);
62
+ if (wfAdmitted === undefined)
81
63
  return;
82
- }
64
+ const run = wfAdmitted.row;
65
+ const scope = run.scope;
83
66
  if (match.decl.id === "workflow-journal") {
84
67
  if (journalStore === undefined)
85
68
  throw new Error("workflow-journal reached without the workflowsJournal journal witness");
@@ -182,12 +165,11 @@ export async function handleWorkflowAgentSteer(req, res, match, ctx) {
182
165
  sendWorkflowsUnavailable(res, runs.why);
183
166
  return;
184
167
  }
185
- const subject = gatedSubject(req, deps.config);
186
- const principal = subject === undefined ? undefined : partsOf(subject).person;
187
- if (deps.config.requirePrincipal && principal === undefined) {
188
- sendPrincipalRefusal(res, req, deps.config);
168
+ const steerEntry = enter(req, res, deps, match.decl.id);
169
+ if (steerEntry === undefined)
189
170
  return;
190
- }
171
+ const subject = steerEntry.caller.subject;
172
+ const principal = subject === undefined ? undefined : partsOf(subject).person;
191
173
  const wfRunId = safeDecode(match.params.runId);
192
174
  const label = safeDecode(match.params.label);
193
175
  if (wfRunId === null || label === null) {
@@ -211,14 +193,14 @@ export async function handleWorkflowAgentSteer(req, res, match, ctx) {
211
193
  return;
212
194
  }
213
195
  const trusted = explicitOperatorOk(principal, deps.config);
214
- const ownerScope = taskSpecIdentityOf(subject === undefined ? undefined : partsOf(subject)).principal ?? "default";
215
- const wfRun = await getWorkflowRun(runs.store, wfRunId, ownerScope);
216
- if (!trusted && !wfRun) {
217
- sendWorkflowAbsent(res);
196
+ const wfVerdict = await judgeWorkflowRun(steerEntry, runs.store, wfRunId);
197
+ if (wfVerdict === "session_mismatch") {
198
+ refuse(steerEntry, res, "session_mismatch");
218
199
  return;
219
200
  }
220
- if (wfRun && !sessionAsserted(WORKFLOW_SESSION_ASSERTION["workflow-agent-steer"], req, wfRun.originatingSessionId, callerOf(req, deps).operator)) {
221
- sendWorkflowAbsent(res);
201
+ const wfRun = typeof wfVerdict === "object" ? wfVerdict.row : undefined;
202
+ if (!trusted && wfRun === undefined) {
203
+ refuse(steerEntry, res, wfVerdict === "missing" ? "missing" : "not_visible");
222
204
  return;
223
205
  }
224
206
  const sendNotRunningWf = (error) => sendError(res, 409, "steering.not_running", error);
@@ -4,6 +4,7 @@ import type { SideQueryAccounting } from "../budget.js";
4
4
  import type { SessionPurgeOutcome } from "../session-deletion.js";
5
5
  import type { IncomingMessage } from "node:http";
6
6
  import { type Runner, type RunnerDeps, type TaskSpec, type TaskResult, type WorkflowRunStore, type MemoryEntry, type MemoryExportBundle, type MemoryImportReport, type SideQuerySpec, type SideQueryResult, type Brain, type EngineNotice } from "@sema-agent/core";
7
+ import type { ProjectContext } from "../project-context.js";
7
8
  import type { TaskRequestBody } from "./wire-types.js";
8
9
  import type { ServiceConfig } from "../config-types.js";
9
10
  import { type SessionMirrorRuling } from "../web-admin-client/facade.js";
@@ -130,7 +131,9 @@ export interface ServiceCoreDeps {
130
131
  * 已过期,按新行为改。
131
132
  * `opts.unattendedLeg`([ref] 候裁 18,codex r3 F1;[ref] ① 改形):续跑家族在 ctx 带无人值守标(`CheckpointCtx.unattendedLeg`)时递 `true` ——
132
133
  * 装配器在**全部折叠之后**经首跑同一只合并口 `withUnattendedLeg` 重挂那条腿(`default` + 按类作答的 onAsk)。只由续跑腿
133
- * 按服务端写的 ctx 位递,绝不从请求体读。 */
134
+ * 按服务端写的 ctx 位递,绝不从请求体读。
135
+ * `opts.projectContext`(项目上下文继承):续跑家族把提交腿冻结的那一份(`CheckpointCtx.projectContext`,服务端写)原样递回 —— 装配器在续跑腿上只重放它、不读登记簿;
136
+ * 新提交腿不递(装配器自己在头部判)。同样只由续跑腿按服务端写的 ctx 位递,绝不从请求体读。 */
134
137
  /** `opts.execution`(必填):这条腿的执行上下文(`http/execution-context.ts`;只 `fresh` / `continuation` 两形,`root` 恒在)。装配器要用这条腿的根会话
135
138
  * (场景设施的分区键、受理见证)读 `opts.execution.root` —— 它与 server 随后盖进 spec 的根是同一个值,装配器自己不写根。 */
136
139
  /** `opts.lease`(必填):这条腿的**宿主租约** —— 装配在它之下登记本腿交给引擎的对端声明(`peerDesk.declare(草稿, opts.lease)`)。
@@ -141,6 +144,7 @@ export interface ServiceCoreDeps {
141
144
  liveStream?: boolean;
142
145
  onNotice?: (notice: EngineNotice) => void;
143
146
  unattendedLeg?: true;
147
+ projectContext?: ProjectContext;
144
148
  lease: PeerLease;
145
149
  execution: ExecutionRoot;
146
150
  }) => ResolvedTaskSpec | Promise<ResolvedTaskSpec>;
@@ -755,8 +759,8 @@ export interface ServiceDeploymentDeps {
755
759
  * 缺席(嵌入方 / 夹具)= 两项恒 0,两个读数逐字同 7.145.x。
756
760
  */
757
761
  sessionResidentBackground?: () => SessionResidentCounts;
758
- /** 编排器片 2 件 3:服务租约此刻的读数(`fleet/serving-lease.ts` 的客户端交出;引擎信号铸点与计费准入判同读)。缺席 = 没装租约客户端
759
- * (非 fleet 成员 / 没接中心 / 测试夹具)⇒ `none`(与没有租约的引擎逐字同)。 */
762
+ /** 编排器片 2 件 3:服务租约此刻的读数(`fleet/serving-lease.ts` 的客户端交出;唯一读者 `fleet/engine-signal.ts` 的 `memberLeaseOf` —— 引擎信号铸点
763
+ * 与计费准入判经它同读,只在 `config.fleetGeneration` 在场〔成员〕时读)。缺席 = 没装租约客户端(非 fleet 成员 / 测试夹具)⇒ `none`。 */
760
764
  servingLease?: () => import("../fleet/engine-signal.js").ServingLeaseView;
761
765
  /** boot ready 门(b):false = 布防(`CONFIG_REQUIRE_ROSTER=true`)且首次 effective pull 尚未落 roster
762
766
  * (worker 只有占位模型)。就绪原因 `model_roster_pending`(`http/readiness.ts`):计费提交 503 + `/readyz` 503 +
@@ -800,7 +804,7 @@ export interface ServiceDeploymentDeps {
800
804
  /** [ref] 追加件:`POST /v1/admin/config/refresh` 的实现面 —— 触发**一次既有**的 refresh 拍(在飞则 join)。
801
805
  * 缺席 / 回 `"not-wired"` = 本部署没有配置管道 ⇒ 该口诚实 4xx(绝不吞成 200)。 */
802
806
  configRefreshNow?: () => Promise<"triggered" | "not-wired" | "failed">;
803
- /** DESIGN-278 §5 S2([ref]):center **当前在管**的 ServiceConfig 键集 —— `GET /v1/config/catalog`
807
+ /** [ref] §5 S2([ref]):center **当前在管**的 ServiceConfig 键集 —— `GET /v1/config/catalog`
804
808
  * 判 `effectiveLane`(env 占位/center 在管/默认)的 center 半场。缺席或返回 undefined = 本部署
805
809
  * 没有配置管道(纯 env worker / 测试夹具)⇒ 目录把每条 center 腿如实标「不在管」(additive)。 */
806
810
  webAdminManagedKeys?: () => ReadonlySet<string> | undefined;
@@ -26,6 +26,7 @@ import { createRunConcurrencyGate, liveRunIdsOf } from "./run-concurrency-cap.js
26
26
  import { sqlEngineSeatIssue } from "../sql-engine-posture.js";
27
27
  import { parkDecisionStoreRefusal } from "../orchestration/workflow-park-decision.js";
28
28
  import { CHECKPOINT_CTX_SEALING_REQUIRED } from "./checkpoint-ctx.js";
29
+ import { SERVING_LEASE_MEMBERSHIP_REQUIRED } from "../fleet/engine-signal.js";
29
30
  import { PARKED_REVIVE_EXECUTION_SEAT_REQUIRED } from "../parked-decide.js";
30
31
  import { a2aAudienceVerifiedSourceRefusal, operatorDeclarationRefusal } from "../config-invariants.js";
31
32
  import { retiredServiceDepsRefusal } from "./retired-service-deps.js";
@@ -148,6 +149,8 @@ export function createHttpServer(rawDeps) {
148
149
  throw new Error(`createHttpServer: ${CHECKPOINT_CTX_SEALING_REQUIRED}`);
149
150
  if (deps.parkedReviveSurface !== undefined && deps.sessionStorage?.executionPlacement === undefined)
150
151
  throw new Error(`createHttpServer: ${PARKED_REVIVE_EXECUTION_SEAT_REQUIRED}`);
152
+ if (deps.servingLease !== undefined && deps.config.fleetGeneration === undefined)
153
+ throw new Error(`createHttpServer: ${SERVING_LEASE_MEMBERSHIP_REQUIRED}`);
151
154
  const idemCache = new IdempotencyCache();
152
155
  const inflightRuns = new Map();
153
156
  const preemptableRuns = new DrainYieldingPreemptBook(() => deps.drainState?.draining === true);
@@ -183,7 +186,7 @@ export function createHttpServer(rawDeps) {
183
186
  if (deps.drainState)
184
187
  deps.drainState.yieldInflight = () => preemptableRuns.yieldAll();
185
188
  if (deps.hookWakeBus) {
186
- deps.hookWakeBus.deliver = async (sessionId, text) => {
189
+ const deliverHookWake = async (sessionId, text) => {
187
190
  try {
188
191
  validatePendingSteer({ text, trusted: false });
189
192
  }
@@ -218,6 +221,7 @@ export function createHttpServer(rawDeps) {
218
221
  return false;
219
222
  }
220
223
  };
224
+ deps.hookWakeBus.deliver = deliverHookWake;
221
225
  }
222
226
  const { quotaExceeded, ownerCostQuotaDenied, ownerFleetLeaseDenied, ownerLeaseDenied, ownerQuotaDenied, leaseDenied, usageWindowDenialFor, usageWindowDenied, rateLimited, safeDecode, readJson, readRawBody } = createRequestGuards({ deps });
223
227
  const { prepareSpec } = createAdmission({ deps, readJson, runnerFor });
@@ -1,29 +1,9 @@
1
- /**
2
- * 「这条停驻行的持久化体在本版上能否重放」—— **唯一**判据(四问一只,外加 `settings.env` 凭据一臂;五个问点同调:续跑驱动段 `resume-legs.ts` `resolveResumeSpecOrBlocked`、
3
- * plan_review 的 prepare 段、SLA 拒扫腿的跳过臂 `approval-sweeps.ts`、终态 run 的 steer 铸唤醒卡之前 `routes/runs.ts`;`resolveSpec` 本身留作提交腿的拒点)。
4
- * 四问的规则、记号、拒句与删除条件的属主 = `http/checkpoint-ctx.ts` 顶注;答复 = 那里的 `storedBodyReplayAnswer`。
5
- *
6
- * 为什么判据住在这里而不是 `checkpoint-ctx.ts`:模式词问要读体的**生效模式词**(`task-settings.ts` `storedBodyPermissionMode`,请求设置的解析层)与本部署此刻的
7
- * **入口地板**(`runtime-governance.ts` `autonomyModeFloor`)—— 这两只属主带着配置层的包外依赖;`checkpoint-ctx.ts` 被随包的本地排空脚本经编译产物 import,
8
- * 它的闭包只许 node 内建 / 引擎包根(`test/drain-check-local.test.ts` 的闭包门)。判据只有这一只,allow 读法问、工具名清单问与退役请求键问的谓词照旧住在各自属主里。
9
- */
10
- import type { PermissionMode } from "@sema-agent/core";
11
- import { type Autonomy } from "../runtime-governance.js";
12
1
  import { type InertToolListEntry } from "../rule-entry-list.js";
13
2
  import { type ExecutionConfigRootDeployment } from "../execution-config-root.js";
14
3
  import { type UnreadMembersDeployment } from "../unread-request-members.js";
15
4
  import { type ModelApiKeyEnvTable } from "../server-secret-env.js";
16
5
  import { type StoredBodyReplayRefusal } from "./checkpoint-ctx.js";
17
6
  import type { CheckpointCtx } from "./route-ctx.js";
18
- /**
19
- * 模式词问:本行体里的模式词能否按当前读法重放。不能 ⇔ 本部署此刻有入口地板 ∧ 行上没有当前的模式词读法记号 ∧ 体的生效词进这块地板会变
20
- * (`runtime-governance.ts` `entryPermissionMode`,与提交入口的盖章同一只函数;判的是「词变不变」而不是 `clamped` —— 体无词的旧行进地板取地板本身,
21
- * 那也是变)⇒ 返回拒因的两个事实(体的词、地板);能 ⇒ `undefined`。
22
- */
23
- export declare function permissionModeReadingUnstated(ctx: CheckpointCtx, floor: PermissionMode | undefined): {
24
- stored: PermissionMode | undefined;
25
- floor: PermissionMode;
26
- } | undefined;
27
7
  /**
28
8
  * 存体里 `settings.permissions.deny` / `ask`(规则编译器读的收紧表)有永远不生效的条目(名字层:退役名 / 短名 / 铸不出的段 / 近似名;core 的规则文法:括号 / 内容文法 / 主字段……;
29
9
  * `~/` 形而这个部署解不出 home),或三桶里 user 层的 `/` 形规则而这个部署读不出执行环境的配置根 —— 拒点是 `applyTaskSettings`(422,在 `resolveSpec` 里),那一拍 plan_review 的一次性 nonce 已烧、终态 steer 的唤醒卡已铸、SLA 拒扫跳不过;所以预判也问一遍。
@@ -36,17 +16,16 @@ export declare function permissionModeReadingUnstated(ctx: CheckpointCtx, floor:
36
16
  */
37
17
  export declare function ruleTablesRefusalWithoutTaskRoot(body: unknown, deployment: ExecutionConfigRootDeployment): InertToolListEntry | undefined;
38
18
  /**
39
- * 预判读的部署配置:`autonomy`(模式词问的模式词地板)、执行车道的 home 两位(`~/` 形规则那一问)、配置根的几位(user 设置层 `/` 规则那一问)与「请求成员读不出」那一问的
19
+ * 预判读的部署配置:执行车道的 home 两位(`~/` 形规则那一问)、配置根的几位(user 设置层 `/` 规则那一问)与「请求成员读不出」那一问的
40
20
  * 租户轴 / 两只注入闸的否决位。`ServiceConfig` 结构上满足,四个问点传 `deps.config` 整份。
41
21
  */
42
22
  export type StoredReplayDeployment = ExecutionConfigRootDeployment & UnreadMembersDeployment & {
43
- readonly autonomy?: Autonomy | undefined;
44
23
  /** 生效配置模型条目 name → `apiKeyEnv`(`settings.env` 凭据判据的名单一臂;缺席 = 没有模型条目,与 `serverCredentialValueSources` 的缺省同读)。 */
45
24
  readonly modelApiKeyEnv?: ModelApiKeyEnvTable | undefined;
46
25
  };
47
26
  /**
48
- * 四问一只判据:不能重放 ⇒ 返回拒的那条规则;能 ⇒ `undefined`。顺序:allow 读法(体里带非空 `allow` 而写者版本证不出 ≥ 7.117.0;常设,按写者版本)→ 请求设置里的条目表(**与拒点同序**:桶门〔形 / 空串 / 超帽〕→ 工具名清单读成空〔**只问限制方向**:放行方向的存量条目读成空,续跑照旧、只留一行运维 warn〕→ 请求成员读不出〔hooks / env / MCP transport / A2A headers …,判据 `unread-request-members.ts`〕→ deny / ask 的名字层 + 规则文法)→ 模式词读法记号 → 体里的退役请求键(几问同中时答先中的那一问 ——
49
- * 任一问中都不重放,句子只需一句)。`deployment` = 本部署**此刻**的 `config`(活 config:热改即生效):`autonomy` 的地板由它的唯一属主算,`~/` 形规则的 home 由 `executionLaneHomeDir` 算,问点都不自己算。
27
+ * 三问一只判据:不能重放 ⇒ 返回拒的那条规则;能 ⇒ `undefined`。顺序:allow 读法(体里带非空 `allow` 而写者版本证不出 ≥ 7.117.0;常设,按写者版本)→ 请求设置里的条目表(**与拒点同序**:桶门〔形 / 空串 / 超帽〕→ 工具名清单读成空〔**只问限制方向**:放行方向的存量条目读成空,续跑照旧、只留一行运维 warn〕→ 请求成员读不出〔hooks / env / MCP transport / A2A headers …,判据 `unread-request-members.ts`〕→ deny / ask 的名字层 + 规则文法)→ 体里的退役请求键(几问同中时答先中的那一问 ——
28
+ * 任一问中都不重放,句子只需一句)。`deployment` = 本部署**此刻**的 `config`(活 config:热改即生效):`~/` 形规则的 home 由 `executionLaneHomeDir` 算,问点都不自己算。
50
29
  */
51
30
  export declare function storedBodyReplayRefusal(ctx: CheckpointCtx, deployment: StoredReplayDeployment): StoredBodyReplayRefusal | undefined;
52
31
  //# sourceMappingURL=stored-body-replay.d.ts.map
@@ -1,7 +1,6 @@
1
- import { REQUEST_SETTINGS_REFUSAL_PREFIX, SETTINGS_PERMISSION_BUCKETS, SETTINGS_RULE_REFUSAL_CODE, neverAutoNamesRefusal, readSettingsEnv, rulePolicyBucketsRefusal, settingsEnvCredentialIssue, settingsPermissionBucketIssue, storedBodyPermissionMode } from "../task-settings.js";
1
+ import { REQUEST_SETTINGS_REFUSAL_PREFIX, SETTINGS_PERMISSION_BUCKETS, SETTINGS_RULE_REFUSAL_CODE, neverAutoNamesRefusal, readSettingsEnv, rulePolicyBucketsRefusal, settingsEnvCredentialIssue, settingsPermissionBucketIssue } from "../task-settings.js";
2
2
  import { readTightenOnlySeats, tightenOnlySeatField, tightenOnlySeatIssue } from "../tighten-only-seats.js";
3
3
  import { entryProblemsFacts } from "../entry-problems.js";
4
- import { autonomyModeFloor, entryPermissionMode } from "../runtime-governance.js";
5
4
  import { inertToolListEntryOf, replayInertEntry } from "../rule-entry-list.js";
6
5
  import { executionLaneHomeDir } from "../execution-lane-caps.js";
7
6
  import { executionConfigRootOf } from "../execution-config-root.js";
@@ -10,13 +9,7 @@ import { storedEnvCredentialsDropM38 } from "../stored-request-env-credentials-m
10
9
  import { storedToolAxesRereadM19 } from "../stored-request-tool-axes-m19.js";
11
10
  import { MAX_NAMED_MEMBERS, unreadRequestMembers, unreadRequestMembersRefusal } from "../unread-request-members.js";
12
11
  import { serverCredentialValueSources } from "../server-secret-env.js";
13
- import { allowListWriterUnproven, permissionModeReadingStated, retiredRequestKeyOf } from "./checkpoint-ctx.js";
14
- export function permissionModeReadingUnstated(ctx, floor) {
15
- if (floor === undefined || permissionModeReadingStated(ctx))
16
- return undefined;
17
- const stored = storedBodyPermissionMode(ctx.body);
18
- return entryPermissionMode(stored, floor).effective === stored ? undefined : { stored, floor };
19
- }
12
+ import { allowListWriterUnproven, retiredRequestKeyOf } from "./checkpoint-ctx.js";
20
13
  const RETIRED_KEY_REWRITE_MIGRATION = { promptProfile: "M30" };
21
14
  const BUCKET_GATE_CODE = "request.field_invalid";
22
15
  const BUCKET_SIDE = {
@@ -87,9 +80,6 @@ export function storedBodyReplayRefusal(ctx, deployment) {
87
80
  const entry = ruleTablesRefusalWithoutTaskRoot(ctx.body, deployment);
88
81
  if (entry !== undefined)
89
82
  return { kind: "inert_tool_list", entry };
90
- const mode = permissionModeReadingUnstated(ctx, autonomyModeFloor(deployment.autonomy));
91
- if (mode !== undefined)
92
- return { kind: "permission_mode_reading", ...mode, ...writer };
93
83
  const retiredKey = retiredRequestKeyOf(ctx.body);
94
84
  if (retiredKey !== undefined)
95
85
  return { kind: "retired_request_key", key: retiredKey, rewriteWindowEnd: migrationWindowEnd(RETIRED_KEY_REWRITE_MIGRATION[retiredKey]), ...writer };
@@ -31,7 +31,6 @@ export declare function persistSubmitCtx(store: SubmitCtxStore, deps: {
31
31
  readonly logger?: Logger | undefined;
32
32
  readonly metrics?: Metrics | undefined;
33
33
  readonly checkpointCtxSealing?: CheckpointCtxSealer | undefined;
34
- readonly instanceId?: string | undefined;
35
34
  }, claim: {
36
35
  readonly runStore: RunStore;
37
36
  readonly taskId: string;
@@ -41,7 +40,7 @@ export declare function persistSubmitCtx(store: SubmitCtxStore, deps: {
41
40
  * 提交腿写续跑输入的入口(三个调用点同调这一只):体与标记按受理座提交时交出的那一份定(见文件头「体里的目标按哪一份写」),只写这一笔。
42
41
  * 写不进的处置与答复 = {@link persistSubmitCtx}(本函数只定体、不另判)。
43
42
  */
44
- export declare function persistSubmissionCtx(store: SubmitCtxStore, deps: Parameters<typeof persistSubmitCtx>[1], claim: Parameters<typeof persistSubmitCtx>[2], prepared: Pick<PreparedTaskSubmission, "body" | "auth" | "verify" | "acceptance" | "bodyFromScreenedCtx"> & {
43
+ export declare function persistSubmissionCtx(store: SubmitCtxStore, deps: Parameters<typeof persistSubmitCtx>[1], claim: Parameters<typeof persistSubmitCtx>[2], prepared: Pick<PreparedTaskSubmission, "body" | "auth" | "verify" | "acceptance" | "bodyFromScreenedCtx"> & Partial<Pick<PreparedTaskSubmission, "legFacts">> & {
45
44
  readonly spec: Pick<PreparedTaskSubmission["spec"], "sessionId" | "model">;
46
45
  }): Promise<SubmitCtxRefusal | typeof SUBMIT_CTX_CLAIM_LOST | undefined>;
47
46
  //# sourceMappingURL=submit-ctx.d.ts.map
@@ -1,6 +1,6 @@
1
1
  import { StoreRowTooLargeError } from "../plugins/sql-errors.js";
2
2
  import { withdrawClaimedRun } from "./claimed-run.js";
3
- import { runProvenElsewhere } from "../plugins/store-contracts.js";
3
+ import { createLegCtxClaim } from "../plugins/store-contracts.js";
4
4
  import { buildCheckpointCtx, CheckpointCtxUnsealableError } from "./checkpoint-ctx.js";
5
5
  import { refusalBody } from "./refusal-body.js";
6
6
  import { limitFactsUnmeasured } from "../limit-refusal.js";
@@ -44,7 +44,7 @@ export async function persistSubmitCtx(store, deps, claim, sessionId, fields) {
44
44
  let landed;
45
45
  try {
46
46
  const ctx = buildCheckpointCtx(fields, deps.checkpointCtxSealing, sessionId);
47
- landed = await store.putCtx(sessionId, ctx, claim === undefined ? undefined : { fence: claim.runStore, taskId: claim.taskId, legHolds: (run) => !runProvenElsewhere(run, deps.instanceId) });
47
+ landed = await store.putCtx(sessionId, ctx, claim === undefined ? undefined : createLegCtxClaim(claim.runStore, claim.taskId, claim.legId));
48
48
  }
49
49
  catch (err) {
50
50
  const tooLarge = err instanceof StoreRowTooLargeError ? err : undefined;
@@ -76,6 +76,7 @@ export async function persistSubmissionCtx(store, deps, claim, prepared) {
76
76
  body: screened && prepared.verify === undefined ? withheldBody(prepared.body, prepared.spec.model) : prepared.body,
77
77
  memoryScope: prepared.auth?.memoryScope,
78
78
  ...(screened || prepared.bodyFromScreenedCtx === true ? { promptScreened: true } : {}),
79
+ ...(prepared.legFacts?.projectContext !== undefined ? { projectContext: prepared.legFacts.projectContext } : {}),
79
80
  writerVersion: serviceVersion(),
80
81
  });
81
82
  }