@sema-agent/server 7.139.0 → 7.140.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 +181 -0
  2. package/USAGE.md +19 -13
  3. package/dist/adoption/runner.js +1 -1
  4. package/dist/adoption/sql.js +1 -1
  5. package/dist/approval-card.d.ts +6 -6
  6. package/dist/bench/s1/live-deps.js +3 -2
  7. package/dist/bench/s1/row.d.ts +5 -2
  8. package/dist/bench/s1/row.js +2 -22
  9. package/dist/boot/adoption.js +1 -1
  10. package/dist/boot/execution-env.js +12 -10
  11. package/dist/boot/execution-lane.d.ts +8 -0
  12. package/dist/boot/execution-lane.js +6 -1
  13. package/dist/boot/memory-boundary.d.ts +14 -51
  14. package/dist/boot/memory-boundary.js +10 -20
  15. package/dist/boot/memory-consolidation.d.ts +15 -11
  16. package/dist/boot/memory-consolidation.js +23 -12
  17. package/dist/boot/resolve-spec.d.ts +3 -5
  18. package/dist/boot/resolve-spec.js +2 -4
  19. package/dist/boot/runner-deps.d.ts +3 -3
  20. package/dist/boot/runner-deps.js +1 -0
  21. package/dist/boot/stage-03-store-backend.d.ts +2 -6
  22. package/dist/boot/stage-03-store-backend.js +2 -3
  23. package/dist/boot/stage-04-stores.d.ts +2 -6
  24. package/dist/boot/stage-04-stores.js +5 -5
  25. package/dist/boot/stage-05-execution-env.d.ts +2 -6
  26. package/dist/boot/stage-06-runners.d.ts +2 -6
  27. package/dist/boot/stage-06-runners.js +2 -2
  28. package/dist/boot/stage-07-capability-layer.d.ts +2 -6
  29. package/dist/boot/stage-08-reapers.d.ts +2 -6
  30. package/dist/boot/stage-09-leader.d.ts +2 -6
  31. package/dist/boot/stage-10-http-server.d.ts +2 -6
  32. package/dist/boot/stage-10-http-server.js +5 -4
  33. package/dist/boot/stores.d.ts +3 -6
  34. package/dist/boot/stores.js +27 -40
  35. package/dist/boot/task-list-lane.d.ts +1 -1
  36. package/dist/boot/task-list-lane.js +1 -1
  37. package/dist/boot/web-admin.js +35 -9
  38. package/dist/capabilities/execution-lane.d.ts +2 -2
  39. package/dist/config-catalog.js +2 -1
  40. package/dist/config-provider.d.ts +14 -13
  41. package/dist/config-provider.js +24 -18
  42. package/dist/config-types.d.ts +11 -2
  43. package/dist/config.js +2 -1
  44. package/dist/delegated-session-owner.d.ts +2 -1
  45. package/dist/delegated-session-owner.js +1 -1
  46. package/dist/deployment-shape.d.ts +9 -8
  47. package/dist/deployment-shape.js +4 -4
  48. package/dist/execution-lane-caps.d.ts +97 -106
  49. package/dist/execution-lane-caps.js +67 -83
  50. package/dist/http/memory-compliance-failure-reply.d.ts +3 -1
  51. package/dist/http/memory-compliance-failure-reply.js +5 -0
  52. package/dist/http/memory-layer-reply.d.ts +30 -0
  53. package/dist/http/memory-layer-reply.js +24 -0
  54. package/dist/http/resume-legs.js +13 -16
  55. package/dist/http/retired-service-deps.d.ts +1 -1
  56. package/dist/http/retired-service-deps.js +1 -1
  57. package/dist/http/routes/adoption.js +1 -1
  58. package/dist/http/routes/memory-bundle.js +3 -0
  59. package/dist/http/routes/memory-compliance.js +8 -1
  60. package/dist/http/routes/memory-consolidation.js +12 -1
  61. package/dist/http/routes/memory-origin.js +14 -2
  62. package/dist/http/routes/memory-policy.js +5 -0
  63. package/dist/http/routes/sessions.js +28 -11
  64. package/dist/http/routes/tasks.js +4 -2
  65. package/dist/http/server.d.ts +5 -25
  66. package/dist/http/server.js +2 -2
  67. package/dist/identity-source.d.ts +1 -1
  68. package/dist/identity-source.js +1 -1
  69. package/dist/lane-home-probe.d.ts +68 -42
  70. package/dist/lane-home-probe.js +53 -30
  71. package/dist/leader/wire.js +5 -4
  72. package/dist/memory-bundle-engine.d.ts +16 -9
  73. package/dist/memory-bundle-engine.js +29 -6
  74. package/dist/memory-operator-faces.d.ts +85 -30
  75. package/dist/memory-operator-faces.js +136 -30
  76. package/dist/memory-project-layer.d.ts +3 -21
  77. package/dist/memory-project-layer.js +11 -8
  78. package/dist/memory-scope-stores.d.ts +73 -0
  79. package/dist/memory-scope-stores.js +86 -0
  80. package/dist/memory-scope.d.ts +29 -5
  81. package/dist/memory-scope.js +16 -4
  82. package/dist/memory-sync-client.d.ts +20 -7
  83. package/dist/memory-sync-client.js +31 -13
  84. package/dist/plugins/adoption-log-sql.js +4 -4
  85. package/dist/plugins/approval-ask-store-sql.js +14 -14
  86. package/dist/plugins/approval-nonce-store.js +4 -4
  87. package/dist/plugins/blob-backend.d.ts +4 -4
  88. package/dist/plugins/blob-backend.js +7 -7
  89. package/dist/plugins/breaker-state-sql.js +2 -2
  90. package/dist/plugins/checkpoint-index-columns.js +2 -2
  91. package/dist/plugins/checkpoint-store-sql.js +16 -16
  92. package/dist/plugins/device-store-sql.js +19 -19
  93. package/dist/plugins/ensure-index.d.ts +1 -2
  94. package/dist/plugins/execution-placement-sql.d.ts +8 -8
  95. package/dist/plugins/execution-placement-sql.js +8 -8
  96. package/dist/plugins/file-history-store-sql.d.ts +2 -2
  97. package/dist/plugins/file-history-store-sql.js +4 -4
  98. package/dist/plugins/image-bake-store-sql.js +9 -9
  99. package/dist/plugins/image-index-sql.js +11 -11
  100. package/dist/plugins/leader-run-store-sql.js +4 -4
  101. package/dist/plugins/mailbox-store-sql.d.ts +2 -1
  102. package/dist/plugins/mailbox-store-sql.js +4 -4
  103. package/dist/plugins/memory-engine-tidb.js +5 -5
  104. package/dist/plugins/memory-optout-grant-store-sql.js +1 -1
  105. package/dist/plugins/memory-sync-store-tidb.js +3 -3
  106. package/dist/plugins/one-time-migrations.d.ts +10 -9
  107. package/dist/plugins/one-time-migrations.js +36 -36
  108. package/dist/plugins/org-delivery-quota-store.js +2 -2
  109. package/dist/plugins/outcome-ledger-sql.js +2 -2
  110. package/dist/plugins/permission-rule-store-sql.d.ts +4 -4
  111. package/dist/plugins/permission-rule-store-sql.js +16 -16
  112. package/dist/plugins/remote-env-adb.d.ts +7 -24
  113. package/dist/plugins/remote-env-adb.js +22 -5
  114. package/dist/plugins/remote-env-device.d.ts +2 -28
  115. package/dist/plugins/remote-env-device.js +2 -1
  116. package/dist/plugins/remote-env-e2b.d.ts +18 -26
  117. package/dist/plugins/remote-env-e2b.js +51 -4
  118. package/dist/plugins/remote-env-file-error.d.ts +1 -1
  119. package/dist/plugins/remote-env-file-error.js +8 -2
  120. package/dist/plugins/remote-env-host.d.ts +19 -35
  121. package/dist/plugins/remote-env-host.js +26 -3
  122. package/dist/plugins/remote-env-k8s.d.ts +3 -14
  123. package/dist/plugins/remote-env-k8s.js +3 -2
  124. package/dist/plugins/remote-env-local-docker.d.ts +4 -15
  125. package/dist/plugins/remote-env-local-docker.js +5 -4
  126. package/dist/plugins/remote-env-ssh.d.ts +4 -24
  127. package/dist/plugins/remote-env-ssh.js +4 -3
  128. package/dist/plugins/resume-anchor-store-sql.js +2 -2
  129. package/dist/plugins/retention-lane-store-sql.d.ts +1 -1
  130. package/dist/plugins/retention-lane-store-sql.js +2 -2
  131. package/dist/plugins/retention-store-sql.d.ts +1 -1
  132. package/dist/plugins/retention-store-sql.js +6 -6
  133. package/dist/plugins/run-store-sql.js +15 -15
  134. package/dist/plugins/schema-consistency.d.ts +6 -6
  135. package/dist/plugins/schema-consistency.js +1 -1
  136. package/dist/plugins/schema-ensure-legs.d.ts +2 -3
  137. package/dist/plugins/schema-ensure-legs.js +2 -2
  138. package/dist/plugins/schema-live-columns.d.ts +1 -1
  139. package/dist/plugins/session-capture-record-store-sql.js +1 -1
  140. package/dist/plugins/session-rules-column.d.ts +1 -1
  141. package/dist/plugins/session-rules-column.js +1 -1
  142. package/dist/plugins/session-workspace-cell.d.ts +1 -1
  143. package/dist/plugins/session-workspace-cell.js +1 -1
  144. package/dist/plugins/shared-memory-store-sql.js +2 -2
  145. package/dist/plugins/sql-driver.d.ts +10 -5
  146. package/dist/plugins/sql-driver.js +6 -6
  147. package/dist/plugins/sql-errors.js +1 -1
  148. package/dist/plugins/store-backend.d.ts +4 -6
  149. package/dist/plugins/store-backend.js +6 -6
  150. package/dist/plugins/task-list-store-sql.js +11 -11
  151. package/dist/plugins/tidb-pool.js +5 -5
  152. package/dist/plugins/tidb-session-storage.js +4 -4
  153. package/dist/plugins/tidb-session-store.js +13 -13
  154. package/dist/plugins/tool-result-store-sql.d.ts +2 -2
  155. package/dist/plugins/tool-result-store-sql.js +3 -3
  156. package/dist/plugins/user-input-ledger-sql.js +2 -2
  157. package/dist/plugins/workflow-journal-store-sql.js +5 -5
  158. package/dist/plugins/workflow-run-store-sql.js +19 -19
  159. package/dist/plugins/write-behind-counter.d.ts +2 -3
  160. package/dist/plugins/write-behind-counter.js +5 -5
  161. package/dist/resource-suspend.d.ts +14 -12
  162. package/dist/resource-suspend.js +1 -1
  163. package/dist/resource-window.d.ts +4 -3
  164. package/dist/run-local.js +14 -19
  165. package/dist/runs.d.ts +8 -4
  166. package/dist/runs.js +49 -65
  167. package/dist/session-incarnation.d.ts +1 -1
  168. package/dist/session-incarnation.js +1 -1
  169. package/dist/session-policy-wire.d.ts +1 -1
  170. package/dist/sql-engine-posture.d.ts +1 -1
  171. package/dist/sql-engine-posture.js +1 -1
  172. package/dist/{plugins → stores-sql}/background-agent-store-sql.d.ts +2 -2
  173. package/dist/{plugins → stores-sql}/background-agent-store-sql.js +8 -8
  174. package/dist/{plugins → stores-sql}/session-policy-store-sql.d.ts +7 -5
  175. package/dist/{plugins → stores-sql}/session-policy-store-sql.js +12 -12
  176. package/dist/{plugins → stores-sql}/usage-window-store-sql.d.ts +1 -1
  177. package/dist/{plugins → stores-sql}/usage-window-store-sql.js +3 -3
  178. package/dist/task-a2a.js +5 -6
  179. package/dist/task-mcp.d.ts +20 -7
  180. package/dist/task-mcp.js +17 -9
  181. package/dist/terminal.d.ts +27 -0
  182. package/dist/terminal.js +6 -0
  183. package/dist/trace/redact.d.ts +3 -1
  184. package/dist/trace/redact.js +80 -28
  185. package/dist/trace/self-minted-wire.d.ts +58 -0
  186. package/dist/trace/self-minted-wire.js +16 -0
  187. package/dist/web-admin-client/candidate-gate.d.ts +11 -7
  188. package/dist/web-admin-client/candidate-gate.js +1 -1
  189. package/dist/web-admin-client/http-client.d.ts +5 -4
  190. package/dist/web-admin-client/http-client.js +2 -2
  191. package/dist/web-admin-client/read-warnings.d.ts +7 -17
  192. package/dist/web-admin-client/read-warnings.js +5 -9
  193. package/package.json +2 -2
  194. package/wire-changes/server@7.140.0.json +184 -0
package/dist/runs.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { runWithVerification, runCascade, defaultTaskRegistry, taskNotificationDedupKey, DURABLE_AGENT_HANDLE_RE } from "@sema-agent/core";
2
- import { failureCodeOf, sealResultForExit, outwardPlane } from "./terminal.js";
2
+ import { atHostBoundary, failureCodeOf, sealResultForExit, outwardPlane } from "./terminal.js";
3
3
  import { NOT_IN_ROSTER, entryProblemsFacts } from "./entry-problems.js";
4
4
  import { GOVERNANCE_HTTP_STATUS } from "./governance-http-status.js";
5
5
  import { TOOL_DECLARATION_HTTP_STATUS } from "./tool-declaration-http-status.js";
@@ -19,6 +19,7 @@ import { createApprovalCardEmitter, resolveApprovalLeg } from "./tool-approval.j
19
19
  import { fleetRunResiduals, isFleetAgentTerminalNotification } from "./fleet/fleet-bus.js";
20
20
  import { defaultSubagentTailBus } from "./fleet/subagent-tail-bus.js";
21
21
  import { emitPendingWorkflowCompletions, taskNotificationInboxEntry, taskNotificationStreamKeyOf, NotifiedKeys } from "./orchestration/workflow-completion-inbox.js";
22
+ import { isParkLedgerEventType } from "./trace/ledger-events.js";
22
23
  import { recordFailOpen } from "./observability/fail-open.js";
23
24
  import { errorText as errText } from "./observability/err-text.js";
24
25
  import { LIVENESS_HEARTBEAT_MS } from "./config-invariants.js";
@@ -88,6 +89,7 @@ const PREPARE_REFUSAL_HTTP_STATUS = {
88
89
  "config.project_identity_mismatch": 409,
89
90
  "config.memory_project_marker": 409,
90
91
  "config.memory_project_marker_nested": 409,
92
+ "config.read_deny_floor_unkept": 409,
91
93
  };
92
94
  export function resumeAtHttpStatus(result) {
93
95
  const code = failureCodeOf(result.terminal);
@@ -326,14 +328,38 @@ export async function runInBackground(runner, spec, runStore, taskId, metrics, p
326
328
  let legLive = true;
327
329
  const notifiedKeys = new NotifiedKeys();
328
330
  const subagentHandleEvictions = [];
329
- const parkNeedsReview = async (res) => {
330
- await sink.appendParked("needs_review", res);
331
+ const parkRowWrite = {
332
+ suspended: (sealed) => runStore.setSuspended(taskId, sealed),
333
+ needs_review: (sealed) => runStore.setNeedsReview(taskId, sealed),
334
+ };
335
+ const parkLeg = async (word, res) => {
336
+ const grafted = await graftParkToolCallId(res, parkCheckpointReader);
337
+ await sink.appendParked(word, grafted);
331
338
  await flushModelUsage();
332
- const sealed = sealResultForExit(res);
333
- reached = { kind: "needs_review", result: sealed };
334
- await runStore.setNeedsReview(taskId, sealed);
339
+ const sealed = sealResultForExit(grafted);
340
+ reached = { kind: word, result: sealed };
341
+ await parkRowWrite[word](sealed);
342
+ acceptance?.settle(sealed);
335
343
  metrics?.addGauge("runs_active", -1);
336
- metrics?.inc("tasks_total", { status: "needs_review" });
344
+ metrics?.inc("tasks_total", { status: word });
345
+ };
346
+ const settleLegResult = async (engineResult) => {
347
+ const result = atHostBoundary(engineResult, spec.sessionId);
348
+ const parkWord = result.terminal.kind === "paused" ? outwardPlane(result.terminal).status : undefined;
349
+ if (parkWord !== undefined && isParkLedgerEventType(parkWord)) {
350
+ await parkLeg(parkWord, result);
351
+ return;
352
+ }
353
+ const settled = causeAtDone !== undefined ? cancelledResult(causeAtDone, result) : result;
354
+ settledAsCancel = failureCodeOf(settled.terminal) === CANCELLED_CODE;
355
+ const safe = await withModelUsage(sealResultForExit(settled));
356
+ await sink.appendDone(safe);
357
+ reached = { kind: "done", result: safe };
358
+ const plane = outwardPlane(safe.terminal);
359
+ await runStore.setTerminal(taskId, plane.status, safe, plane.errorMessage ?? null);
360
+ await releaseKeyIfZeroWorkRefusal(runStore, taskId, safe);
361
+ acceptance?.settle(safe);
362
+ recordDone(safe, settledAsCancel ? CANCELLED_CODE : plane.status);
337
363
  };
338
364
  const instructionLeg = createInstructionSelectionLeg();
339
365
  const sink = createLedgerSink({ appendEvent: (seq, type, data) => runStore.appendEvent(taskId, seq, type, data), persistThinking, notifiedKeys, onActivity: (kind, statusData) => recordTurnActivity(taskId, kind, statusData), legSections: () => ({ instructions: instructionLeg.segment(), skills: runSkillsOf(spec) }) });
@@ -419,32 +445,18 @@ export async function runInBackground(runner, spec, runStore, taskId, metrics, p
419
445
  void sink.appendStatus(e).catch((err) => completionDiagLog?.("status_event_append_failed", { route: "bg-run-composite", taskId, err: err instanceof Error ? err.message : String(err) }));
420
446
  },
421
447
  };
422
- if (verify !== undefined) {
423
- const result = await runWithVerification(runner, { ...spec, taskId, signal: cancelCtrl.signal }, verify, { ...fwdInternals, ...statusSeat });
424
- await flush();
425
- await flushPromptManifest();
426
- const safe = await withModelUsage(sealResultForExit(result));
427
- await append("done", { result: safe });
428
- reached = { kind: "done", result: safe };
429
- const plane = outwardPlane(safe.terminal);
430
- await runStore.setTerminal(taskId, plane.status, safe, plane.errorMessage ?? null);
431
- await releaseKeyIfZeroWorkRefusal(runStore, taskId, safe);
432
- acceptance?.settle(safe);
433
- recordDone(safe, plane.status);
434
- return;
435
- }
436
- if (cascade !== undefined) {
437
- const result = await runCascade(runner, { ...spec, taskId, signal: cancelCtrl.signal }, cascade, { ...fwdInternals, ...statusSeat });
448
+ const compositeLeg = verify !== undefined
449
+ ? () => runWithVerification(runner, { ...spec, taskId, signal: cancelCtrl.signal }, verify, { ...fwdInternals, ...statusSeat })
450
+ : cascade !== undefined
451
+ ? () => runCascade(runner, { ...spec, taskId, signal: cancelCtrl.signal }, cascade, { ...fwdInternals, ...statusSeat })
452
+ : undefined;
453
+ if (compositeLeg !== undefined) {
454
+ const result = await compositeLeg();
455
+ observedDone = result;
456
+ causeAtDone = cancelCtrl.cause();
438
457
  await flush();
439
458
  await flushPromptManifest();
440
- const safe = await withModelUsage(sealResultForExit(result));
441
- await append("done", { result: safe });
442
- reached = { kind: "done", result: safe };
443
- const plane = outwardPlane(safe.terminal);
444
- await runStore.setTerminal(taskId, plane.status, safe, plane.errorMessage ?? null);
445
- await releaseKeyIfZeroWorkRefusal(runStore, taskId, safe);
446
- acceptance?.settle(safe);
447
- recordDone(safe, plane.status);
459
+ await settleLegResult(result);
448
460
  return;
449
461
  }
450
462
  const stream = runner.runTaskStream({ ...spec, taskId, signal: cancelCtrl.signal, preemptSignal: preemptCtrl.signal }, undefined, fwdInternals);
@@ -479,33 +491,7 @@ export async function runInBackground(runner, spec, runStore, taskId, metrics, p
479
491
  }
480
492
  if (ev.type === "done") {
481
493
  await flush();
482
- const parkStatus = ev.result.terminal.kind === "paused" ? outwardPlane(ev.result.terminal).status : undefined;
483
- if (parkStatus === "suspended") {
484
- const grafted = await graftParkToolCallId(ev.result, parkCheckpointReader);
485
- await sink.appendParked("suspended", grafted);
486
- await flushModelUsage();
487
- const sealed = sealResultForExit(grafted);
488
- reached = { kind: "suspended", result: sealed };
489
- await runStore.setSuspended(taskId, sealed);
490
- metrics?.addGauge("runs_active", -1);
491
- metrics?.inc("tasks_total", { status: "suspended" });
492
- continue;
493
- }
494
- if (parkStatus === "needs_review") {
495
- await parkNeedsReview(ev.result);
496
- continue;
497
- }
498
- {
499
- const settled = causeAtDone !== undefined ? cancelledResult(causeAtDone, ev.result) : ev.result;
500
- settledAsCancel = failureCodeOf(settled.terminal) === CANCELLED_CODE;
501
- const safe = await withModelUsage(sealResultForExit(settled));
502
- await sink.appendDone(safe);
503
- reached = { kind: "done", result: safe };
504
- const plane = outwardPlane(safe.terminal);
505
- await runStore.setTerminal(taskId, plane.status, safe, plane.errorMessage ?? null);
506
- await releaseKeyIfZeroWorkRefusal(runStore, taskId, safe);
507
- recordDone(safe, failureCodeOf(safe.terminal) === CANCELLED_CODE ? CANCELLED_CODE : plane.status);
508
- }
494
+ await settleLegResult(ev.result);
509
495
  continue;
510
496
  }
511
497
  await sink.onEvent(ev);
@@ -581,11 +567,9 @@ export async function runInBackground(runner, spec, runStore, taskId, metrics, p
581
567
  await releaseKeyIfZeroWorkRefusal(runStore, taskId, reached.result);
582
568
  acceptance?.settle(reached.result);
583
569
  }
584
- else if (reached?.kind === "suspended") {
585
- await runStore.setSuspended(taskId, reached.result);
586
- }
587
- else if (reached?.kind === "needs_review") {
588
- await runStore.setNeedsReview(taskId, reached.result);
570
+ else if (reached !== undefined) {
571
+ await parkRowWrite[reached.kind](reached.result);
572
+ acceptance?.settle(reached.result);
589
573
  }
590
574
  else if (thrownCancelCause !== undefined && observedDone === undefined) {
591
575
  const c = cancelledResult(thrownCancelCause);
@@ -614,9 +598,9 @@ export async function runInBackground(runner, spec, runStore, taskId, metrics, p
614
598
  }
615
599
  if (reached?.kind === "done")
616
600
  recordDone(reached.result, failureCodeOf(reached.result.terminal) === CANCELLED_CODE ? CANCELLED_CODE : outwardPlane(reached.result.terminal).status);
617
- else if (reached?.kind === "suspended") {
601
+ else if (reached !== undefined) {
618
602
  metrics?.addGauge("runs_active", -1);
619
- metrics?.inc("tasks_total", { status: "suspended" });
603
+ metrics?.inc("tasks_total", { status: reached.kind });
620
604
  }
621
605
  else if (settledAsCancel) {
622
606
  recordDone(null, CANCELLED_CODE);
@@ -146,7 +146,7 @@ export declare function carriedRowCells(sessionId: string, prior: {
146
146
  */
147
147
  export declare const SESSION_META_INCARNATION_COLUMN: {
148
148
  readonly name: "incarnation";
149
- readonly tidb: "incarnation VARCHAR(64) NOT NULL DEFAULT ''";
149
+ readonly mysql: "incarnation VARCHAR(64) NOT NULL DEFAULT ''";
150
150
  readonly pg: "incarnation VARCHAR(64) COLLATE \"C\" NOT NULL DEFAULT ''";
151
151
  };
152
152
  //# sourceMappingURL=session-incarnation.d.ts.map
@@ -75,7 +75,7 @@ export function carriedRowCells(sessionId, prior) {
75
75
  }
76
76
  export const SESSION_META_INCARNATION_COLUMN = {
77
77
  name: "incarnation",
78
- tidb: "incarnation VARCHAR(64) NOT NULL DEFAULT ''",
78
+ mysql: "incarnation VARCHAR(64) NOT NULL DEFAULT ''",
79
79
  pg: `incarnation VARCHAR(64) COLLATE "C" NOT NULL DEFAULT ''`,
80
80
  };
81
81
  //# sourceMappingURL=session-incarnation.js.map
@@ -139,7 +139,7 @@ export declare function sessionPolicyErrorFacts(e: SessionPolicyError): Readonly
139
139
  * · `conflict`(409)—— CAS 输了,重读 `rev` 再来。**码位与状态逐字不变**。
140
140
  * · `loosen_forbidden`(403)—— 非 operator 想放松。**码位与状态逐字不变**。⚠️ 这个词还有**第二个铸点**([ref] B5,
141
141
  * 如实登记、未改):PG 后端的 SQL 双生对含 NUL / 孤代理字节的规则拒收时借用了它
142
- * (`plugins/session-policy-store-sql.ts` 的 `pgHasUnstorable` 臂)—— 那一形**既非放宽也非越权**,operator 写同样撞上;
142
+ * (`stores-sql/session-policy-store-sql.ts` 的 `pgHasUnstorable` 臂)—— 那一形**既非放宽也非越权**,operator 写同样撞上;
143
143
  * core 的闭集里没有「输入不可存」这个词,改码位要先裁定新码与 PUT / 2c 导入两条入口同判(DEBTS [ref] B5)。
144
144
  * · `corrupt`(**500**)—— 店读不出它自己该读的那份字节(记号/行被列外篡改或撕裂,或存着引擎今天的形读法读不出的规则体)。
145
145
  * 不是 4xx:调用方没做错任何事,它也改不了这件事;不是 503:重试改不了判(修复路径是**人**去改回或删掉那一行,
@@ -38,7 +38,7 @@ export declare function sqlPoolsOf(backend: Pick<StoreBackend, "mysqlPool" | "pg
38
38
  pgPool: PgPool | undefined;
39
39
  };
40
40
  /**
41
- * `ServiceDeps.sqlEngine` 座的**值形**判:`{ dialect: "tidb" | "pg", facts }`,`facts` 是 `null`(池上连接初始化还没成功)或一份引擎事实 —— 判到墙判官 `sqlWallsOf` 读的那几位
41
+ * `ServiceDeps.sqlEngine` 座的**值形**判:`{ dialect: "mysql" | "pg", facts }`,`facts` 是 `null`(池上连接初始化还没成功)或一份引擎事实 —— 判到墙判官 `sqlWallsOf` 读的那几位
42
42
  * (`maxRowBytes` / `maxPacketBytes` 是 `null` 或数,`wallSources` 两位是出处词)。一个函数(7.131.x 的事实读口 `() => facts` 只改了键名)、缺 `facts` 的对象、错方言词都不是它。
43
43
  * `facts` 是活读(getter):这里读一次,判的是装配这一刻看得到的那一份。
44
44
  */
@@ -2,7 +2,7 @@ import { mysqlDriver, pgDriver } from "./plugins/sql-driver.js";
2
2
  export function sqlPoolsOf(backend) {
3
3
  return { mysqlPool: backend?.mysqlPool?.(), pgPool: backend?.pgPool?.() };
4
4
  }
5
- const DIALECT_WORDS = { tidb: true, pg: true };
5
+ const DIALECT_WORDS = { mysql: true, pg: true };
6
6
  const WALL_SOURCE_WORDS = { reported: true, assumed: true };
7
7
  const isWallBytes = (v) => v === null || (typeof v === "number" && Number.isFinite(v) && v >= 0);
8
8
  const isWallSourceWord = (v) => typeof v === "string" && Object.hasOwn(WALL_SOURCE_WORDS, v);
@@ -31,8 +31,8 @@
31
31
  */
32
32
  import type { Pool } from "mysql2/promise";
33
33
  import { type BackgroundAgentRecord, type BackgroundAgentRowSummary, type BackgroundAgentStore } from "@sema-agent/core";
34
- import type { PgQueryFn } from "./pg-query.js";
35
- import { type IndexSpec } from "./ensure-index.js";
34
+ import type { PgQueryFn } from "../plugins/pg-query.js";
35
+ import { type IndexSpec } from "../plugins/ensure-index.js";
36
36
  export declare const BACKGROUND_AGENT_TABLE = "background_agent";
37
37
  type ReapOpts = {
38
38
  maxAgeMs?: number;
@@ -1,8 +1,8 @@
1
1
  import { BackgroundAgentStoreError, STALE_RUNNING_REAP_ATTRIBUTION, } from "@sema-agent/core";
2
- import { pgSafeJsonStringify, pgSanitizeText } from "./pg-safe-json.js";
3
- import { isMysqlDupKeyError } from "./sql-errors.js";
4
- import { ensureIndexes } from "./ensure-index.js";
5
- import { createObjectColumnReader, decodeJsonColumn, decodeNullableJsonColumn, foldJsonColumn, jsonColumnText } from "./sql-json-column.js";
2
+ import { pgSafeJsonStringify, pgSanitizeText } from "../plugins/pg-safe-json.js";
3
+ import { isMysqlDupKeyError } from "../plugins/sql-errors.js";
4
+ import { ensureIndexes } from "../plugins/ensure-index.js";
5
+ import { createObjectColumnReader, decodeJsonColumn, decodeNullableJsonColumn, foldJsonColumn, jsonColumnText } from "../plugins/sql-json-column.js";
6
6
  import { backgroundAgentNameOf } from "../background-agent-name.js";
7
7
  export const BACKGROUND_AGENT_TABLE = "background_agent";
8
8
  function summaryCols(dialect) {
@@ -215,7 +215,7 @@ export class TiDBBackgroundAgentStore {
215
215
  }
216
216
  }
217
217
  async get(handle, scope) {
218
- const [rows] = (await this.pool.query(`SELECT ${jsonColumnText("tidb", "record_json")} FROM ${BACKGROUND_AGENT_TABLE} WHERE scope_key = ? AND handle = ?`, [scope, handle]));
218
+ const [rows] = (await this.pool.query(`SELECT ${jsonColumnText("mysql", "record_json")} FROM ${BACKGROUND_AGENT_TABLE} WHERE scope_key = ? AND handle = ?`, [scope, handle]));
219
219
  return rows.length > 0 ? readRecordColumn(rows[0].record_json, handle) : null;
220
220
  }
221
221
  async update(handle, scope, record, expect) {
@@ -234,12 +234,12 @@ export class TiDBBackgroundAgentStore {
234
234
  return res.affectedRows === 1;
235
235
  }
236
236
  async listBySession(scope, sessionId, opts) {
237
- const [rows] = (await this.pool.query(`SELECT ${summaryCols("tidb")} FROM ${BACKGROUND_AGENT_TABLE} WHERE scope_key = ? AND (session_anchor = ? OR root_session_id = ?)${opts?.status !== undefined ? " AND status = ?" : ""}
237
+ const [rows] = (await this.pool.query(`SELECT ${summaryCols("mysql")} FROM ${BACKGROUND_AGENT_TABLE} WHERE scope_key = ? AND (session_anchor = ? OR root_session_id = ?)${opts?.status !== undefined ? " AND status = ?" : ""}
238
238
  ORDER BY spawned_at_ms DESC, handle DESC${limitClause(opts?.limit)}`, opts?.status !== undefined ? [scope, sessionId, sessionId, opts.status] : [scope, sessionId, sessionId]));
239
239
  return rows.map(rowToSummary);
240
240
  }
241
241
  async listByScope(scope, opts) {
242
- const [rows] = (await this.pool.query(`SELECT ${summaryCols("tidb")} FROM ${BACKGROUND_AGENT_TABLE} WHERE scope_key = ?${opts?.status !== undefined ? " AND status = ?" : ""}
242
+ const [rows] = (await this.pool.query(`SELECT ${summaryCols("mysql")} FROM ${BACKGROUND_AGENT_TABLE} WHERE scope_key = ?${opts?.status !== undefined ? " AND status = ?" : ""}
243
243
  ORDER BY spawned_at_ms DESC, handle DESC${limitClause(opts?.limit)}`, opts?.status !== undefined ? [scope, opts.status] : [scope]));
244
244
  return rows.map(rowToSummary);
245
245
  }
@@ -252,7 +252,7 @@ export class TiDBBackgroundAgentStore {
252
252
  return 0;
253
253
  const flipped = new Set();
254
254
  if (opts.staleRunningMaxAgeMs !== undefined) {
255
- const [stale] = (await this.pool.query(`SELECT handle, ${jsonColumnText("tidb", "record_json")} FROM ${BACKGROUND_AGENT_TABLE} WHERE scope_key = ? AND status = 'running' AND updated_at_ms < ?`, [scope, now - opts.staleRunningMaxAgeMs]));
255
+ const [stale] = (await this.pool.query(`SELECT handle, ${jsonColumnText("mysql", "record_json")} FROM ${BACKGROUND_AGENT_TABLE} WHERE scope_key = ? AND status = 'running' AND updated_at_ms < ?`, [scope, now - opts.staleRunningMaxAgeMs]));
256
256
  for (const row of stale) {
257
257
  const live = readRecordColumn(row.record_json, String(row.handle));
258
258
  if (await this.update(String(row.handle), scope, flippedForm(live, now), { rev: live.rev }))
@@ -1,9 +1,8 @@
1
1
  import { type SessionPolicyStore, type SessionPermissionRules, type StoredSessionRules, type SessionRulesRecord, type PutRulesOptions } from "@sema-agent/core";
2
2
  import type { Pool as MySqlPool } from "mysql2/promise";
3
3
  import type { Pool as PgPool } from "pg";
4
- import { type JsonColumnShape } from "./sql-json-column.js";
5
- import { type SqlDriver } from "./sql-driver.js";
6
- import type { Logger } from "../observability/logger.js";
4
+ import { type JsonColumnShape } from "../plugins/sql-json-column.js";
5
+ import { type SqlDriver } from "../plugins/sql-driver.js";
7
6
  /** `session_policy.policy_key` 的**唯一铸造点**(core 复合键的 sha256)。
8
7
  *
9
8
  * 🔴 导出而不是让收编腿再抄一份(clay 宪法「schema 单一属主禁复制」):[ref] form b 的身份重绑要
@@ -45,8 +44,11 @@ export type StoredSessionRulesBody = SessionPermissionRules & Pick<StoredSession
45
44
  * + 恰好解一次 `decodeJsonColumn` + 折码 `foldJsonColumn`);本店只留「形判据是哪一只」与「坏体折成哪个码」。
46
45
  */
47
46
  export declare function readSessionRulesBody(body: unknown): JsonColumnShape<StoredSessionRulesBody>;
48
- /** 本店唯一的日志口:抹除封键那一臂([ref])的点名。缺席 ⇒ 退回 `console.warn`(不静默)。 */
49
- export type SessionPolicyStoreLogger = Pick<Logger, "warn">;
47
+ /** 本店唯一的日志口:抹除封键那一臂([ref])的点名。缺席 ⇒ 退回 `console.warn`(不静默)。
48
+ * 结构型,与服务日志口的 `warn` 逐位同形:本目录对服务的观测模块零依赖(目录的可搬性),服务的日志口照样直接交进来。 */
49
+ export type SessionPolicyStoreLogger = {
50
+ warn(msg: string, fields?: Record<string, unknown>): void;
51
+ };
50
52
  /** Dual-dialect durable SessionPolicyStore. See the file header for the dialect-delta ledger. */
51
53
  export declare class SqlSessionPolicyStore implements SessionPolicyStore {
52
54
  private readonly db;
@@ -1,10 +1,10 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { judgeSessionRulesWrite, readSessionRulesShape, SessionPolicyError } from "@sema-agent/core";
3
- import { SESSION_RULES_COLUMN } from "./session-rules-column.js";
4
- import { decodeJsonColumn, isJsonColumnBodyProblem, jsonColumnText } from "./sql-json-column.js";
5
- import { pgHasUnstorable } from "./pg-safe-json.js";
6
- import { mysqlDriver, pgDriver } from "./sql-driver.js";
7
- import { isDeadlockError, isDupKeyError } from "./sql-errors.js";
3
+ import { SESSION_RULES_COLUMN } from "../plugins/session-rules-column.js";
4
+ import { decodeJsonColumn, isJsonColumnBodyProblem, jsonColumnText } from "../plugins/sql-json-column.js";
5
+ import { pgHasUnstorable } from "../plugins/pg-safe-json.js";
6
+ import { mysqlDriver, pgDriver } from "../plugins/sql-driver.js";
7
+ import { isDeadlockError, isDupKeyError } from "../plugins/sql-errors.js";
8
8
  export const policyKeyFor = (sessionId, principal) => createHash("sha256").update(JSON.stringify([sessionId, principal ?? null])).digest("hex");
9
9
  const policyKey = policyKeyFor;
10
10
  const RETRY_LIMIT = 8;
@@ -63,15 +63,15 @@ export class SqlSessionPolicyStore {
63
63
  this.db = db;
64
64
  this.logger = logger;
65
65
  }
66
- q(tidb, pg) {
67
- return this.db.dialect === "tidb" ? tidb : pg;
66
+ q(mysql, pg) {
67
+ return this.db.dialect === "mysql" ? mysql : pg;
68
68
  }
69
69
  isDupKey(e) {
70
70
  return isDupKeyError(this.db.dialect, e);
71
71
  }
72
72
  async getRules(sessionId, principal) {
73
73
  const key = policyKey(sessionId, principal);
74
- const { rows } = await this.db.query(this.q(`SELECT 'row' AS src, ${jsonColumnText("tidb", RULES)}, rev, gen FROM session_policy WHERE policy_key = ?` +
74
+ const { rows } = await this.db.query(this.q(`SELECT 'row' AS src, ${jsonColumnText("mysql", RULES)}, rev, gen FROM session_policy WHERE policy_key = ?` +
75
75
  " UNION ALL SELECT 'seal', NULL, rev, gen FROM session_policy_rev_mark WHERE policy_key = ? AND rev = ?", `SELECT 'row' AS src, ${jsonColumnText("pg", RULES)}, rev, gen FROM session_policy WHERE policy_key = $1` +
76
76
  " UNION ALL SELECT 'seal', NULL, rev, gen FROM session_policy_rev_mark WHERE policy_key = $2 AND rev = $3"), [key, key, SEALED_MARK_REV]);
77
77
  if (rows.some((r) => r.src === "seal"))
@@ -82,7 +82,7 @@ export class SqlSessionPolicyStore {
82
82
  return rowToStored(row, `for session ${JSON.stringify(sessionId)}`);
83
83
  }
84
84
  async listBySession(sessionId) {
85
- const { rows } = await this.db.query(this.q(`SELECT 'row' AS src, principal, ${jsonColumnText("tidb", RULES)}, rev, gen FROM session_policy WHERE session_id = ?` +
85
+ const { rows } = await this.db.query(this.q(`SELECT 'row' AS src, principal, ${jsonColumnText("mysql", RULES)}, rev, gen FROM session_policy WHERE session_id = ?` +
86
86
  " UNION ALL SELECT 'seal', NULL, NULL, rev, gen FROM session_policy_rev_mark WHERE session_id = ? AND rev = ?", `SELECT 'row' AS src, principal, ${jsonColumnText("pg", RULES)}, rev, gen FROM session_policy WHERE session_id = $1` +
87
87
  " UNION ALL SELECT 'seal', NULL, NULL, rev, gen FROM session_policy_rev_mark WHERE session_id = $2 AND rev = $3"), [sessionId, sessionId, SEALED_MARK_REV]);
88
88
  if (rows.some((r) => r.src === "seal"))
@@ -95,7 +95,7 @@ export class SqlSessionPolicyStore {
95
95
  async putRules(sessionId, principal, rules, opts) {
96
96
  const key = policyKey(sessionId, principal);
97
97
  return this.retrying(() => this.tx(async (conn) => {
98
- const { rows } = await conn.query(this.q(`SELECT ${jsonColumnText("tidb", RULES)}, rev, gen FROM session_policy WHERE policy_key = ? FOR UPDATE`, `SELECT ${jsonColumnText("pg", RULES)}, rev, gen FROM session_policy WHERE policy_key = $1 FOR UPDATE`), [key]);
98
+ const { rows } = await conn.query(this.q(`SELECT ${jsonColumnText("mysql", RULES)}, rev, gen FROM session_policy WHERE policy_key = ? FOR UPDATE`, `SELECT ${jsonColumnText("pg", RULES)}, rev, gen FROM session_policy WHERE policy_key = $1 FOR UPDATE`), [key]);
99
99
  const prior = rows[0] ? rowToStored(rows[0], `for session ${JSON.stringify(sessionId)} (pre-write snapshot)`) : undefined;
100
100
  const priorRev = prior?.rev ?? 0;
101
101
  if (opts?.expectedRev !== undefined && priorRev !== opts.expectedRev) {
@@ -157,12 +157,12 @@ export class SqlSessionPolicyStore {
157
157
  sealedKeys.push({ policyKey: String(r.policy_key), rev: String(r.rev), gen: String(r.gen) });
158
158
  params.push(String(r.policy_key), sessionId, readable ? counters.rev : SEALED_MARK_REV, readable ? counters.gen + 1 : 0, now);
159
159
  const base = params.length - 5;
160
- return this.db.dialect === "tidb" ? "(?,?,?,?,?)" : `($${base + 1},$${base + 2},$${base + 3},$${base + 4},$${base + 5})`;
160
+ return this.db.dialect === "mysql" ? "(?,?,?,?,?)" : `($${base + 1},$${base + 2},$${base + 3},$${base + 4},$${base + 5})`;
161
161
  });
162
162
  await conn.query(this.q(`INSERT INTO session_policy_rev_mark (policy_key, session_id, rev, gen, erased_at) VALUES ${tuples.join(",")}` +
163
163
  " ON DUPLICATE KEY UPDATE rev = GREATEST(session_policy_rev_mark.rev, VALUES(rev)), gen = GREATEST(session_policy_rev_mark.gen + 1, VALUES(gen)), erased_at = VALUES(erased_at)", `INSERT INTO session_policy_rev_mark (policy_key, session_id, rev, gen, erased_at) VALUES ${tuples.join(",")}` +
164
164
  " ON CONFLICT (policy_key) DO UPDATE SET rev = GREATEST(session_policy_rev_mark.rev, EXCLUDED.rev), gen = GREATEST(session_policy_rev_mark.gen + 1, EXCLUDED.gen), erased_at = EXCLUDED.erased_at"), params);
165
- const holes = keys.map((_, i) => (this.db.dialect === "tidb" ? "?" : `$${i + 1}`)).join(",");
165
+ const holes = keys.map((_, i) => (this.db.dialect === "mysql" ? "?" : `$${i + 1}`)).join(",");
166
166
  await conn.query(`DELETE FROM session_policy WHERE policy_key IN (${holes})`, keys);
167
167
  return sealedKeys;
168
168
  }));
@@ -26,7 +26,7 @@
26
26
  import type { Pool as MySqlPool } from "mysql2/promise";
27
27
  import type { Pool as PgPool } from "pg";
28
28
  import type { UsageWindow, UsageWindowReading, UsageWindowRecord, UsageWindowStore } from "@sema-agent/core";
29
- import { type JsonColumnShape } from "./sql-json-column.js";
29
+ import { type JsonColumnShape } from "../plugins/sql-json-column.js";
30
30
  export declare const USAGE_WINDOW_TABLE = "usage_window";
31
31
  /**
32
32
  * `usage_window.record` 的**形读者**([ref] 车③a:本店既有的那一份深度判据逐字搬进来,一个字没改判)。
@@ -1,5 +1,5 @@
1
1
  import { EMPTY_USAGE_WINDOW_RECORD, chargeUsageRecord, readUsageRecord } from "@sema-agent/core";
2
- import { decodeJsonColumn, foldJsonColumn, jsonColumnText } from "./sql-json-column.js";
2
+ import { decodeJsonColumn, foldJsonColumn, jsonColumnText } from "../plugins/sql-json-column.js";
3
3
  export const USAGE_WINDOW_TABLE = "usage_window";
4
4
  class UsageRecordShapeProblem extends Error {
5
5
  }
@@ -84,7 +84,7 @@ export class TiDBUsageWindowStore {
84
84
  try {
85
85
  await c.beginTransaction();
86
86
  await c.query(`INSERT INTO ${USAGE_WINDOW_TABLE} (usage_key, record, updated_at_ms) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE usage_key = usage_key`, [key, JSON.stringify(EMPTY_USAGE_WINDOW_RECORD), at]);
87
- const [rows] = (await c.query(`SELECT ${jsonColumnText("tidb", "record")} FROM ${USAGE_WINDOW_TABLE} WHERE usage_key = ? FOR UPDATE`, [key]));
87
+ const [rows] = (await c.query(`SELECT ${jsonColumnText("mysql", "record")} FROM ${USAGE_WINDOW_TABLE} WHERE usage_key = ? FOR UPDATE`, [key]));
88
88
  if (rows.length === 0) {
89
89
  throw new Error(`usage_window row for key "${key}" vanished between ensure and lock — refusing to drop a governance charge`);
90
90
  }
@@ -102,7 +102,7 @@ export class TiDBUsageWindowStore {
102
102
  }
103
103
  }
104
104
  async read(key, windows, now) {
105
- const [rows] = (await this.pool.query(`SELECT ${jsonColumnText("tidb", "record")} FROM ${USAGE_WINDOW_TABLE} WHERE usage_key = ?`, [key]));
105
+ const [rows] = (await this.pool.query(`SELECT ${jsonColumnText("mysql", "record")} FROM ${USAGE_WINDOW_TABLE} WHERE usage_key = ?`, [key]));
106
106
  const record = rows.length === 0 ? EMPTY_USAGE_WINDOW_RECORD : parseRecord(rows[0].record, key);
107
107
  return readUsageRecord(record, windows, now);
108
108
  }
package/dist/task-a2a.js CHANGED
@@ -8,7 +8,7 @@ import { readRequestToolAxes } from "./request-tool-axes.js";
8
8
  import { MAX_KEY_ECHO, echoKeyName, keyClosureClause, scanKeyNames } from "./request-key-closure.js";
9
9
  import { urlCredentialHeaderExit, urlCredentialRefusalReason } from "./url-credential-refusal.js";
10
10
  import { declarationEntryNotObject, declarationIssueFacts, declarationNameIssue, declarationOverCapFacts, declaredName, judgeDeclarationKeys, requestDeclarationRefusal, throwDeclarationRefusal, } from "./request-declaration-refusal.js";
11
- import { principalHeaderOwnershipIssue } from "./task-mcp.js";
11
+ import { sendHeaderOwnershipIssue } from "./task-mcp.js";
12
12
  import { DEPLOYMENT_SHAPE_PREDICATES } from "./deployment-shape.js";
13
13
  export const MAX_REQUEST_A2A_PEERS = 32;
14
14
  export function a2aInjectionHonored(config) {
@@ -52,6 +52,7 @@ function peerUrlIssue(key) {
52
52
  return found === undefined ? undefined : { path: key, reason: urlCredentialRefusalReason(found, urlCredentialHeaderExit(`${at}.headers`)), credential: found };
53
53
  };
54
54
  }
55
+ const A2A_SEND_HEADER_PATHS = { headers: "headers", principalHeader: "principalHeader" };
55
56
  const HEADERS_RESERVED_MEMBER_PATH = "headers.__proto__";
56
57
  const PEER_SNAPSHOT = Symbol("a2aPeerSnapshot");
57
58
  function openCloneNode(v, ancestors) {
@@ -170,11 +171,9 @@ const A2A_PEER_KEY_JUDGES = {
170
171
  const reserved = read.stripped[0];
171
172
  return reserved === undefined ? undefined : { path: HEADERS_RESERVED_MEMBER_PATH, reason: reserved.fault };
172
173
  },
173
- principalHeader: (o) => o.principalHeader === undefined
174
- ? undefined
175
- : typeof o.principalHeader !== "string"
176
- ? { path: "principalHeader", reason: `must be a string (a header name) — got ${describeValueShape(o.principalHeader)}` }
177
- : principalHeaderOwnershipIssue(o.principalHeader, "a2a", "principalHeader"),
174
+ principalHeader: (o) => o.principalHeader !== undefined && typeof o.principalHeader !== "string"
175
+ ? { path: "principalHeader", reason: `must be a string (a header name) — got ${describeValueShape(o.principalHeader)}` }
176
+ : sendHeaderOwnershipIssue({ headers: o.headers?.ok === true ? o.headers.record : undefined, principalHeader: o.principalHeader }, "a2a", A2A_SEND_HEADER_PATHS),
178
177
  allowSkills: (o) => o.allowSkills === undefined || isStringArray(o.allowSkills)
179
178
  ? undefined
180
179
  : { path: "allowSkills", reason: `must be an array of strings (omit the key to leave the peer's skills unrestricted) — got ${describeNotStringArray(o.allowSkills)}` },
@@ -158,7 +158,7 @@ export declare function requestEntryIdentityIssue(entry: unknown): McpIdentityHe
158
158
  * `name` = `isUsableWireName`;`allowTools` / `transport.args` = {@link isStringArray};`transport.env` / `headers` = 契约包读器 {@link readRequestMcpTransport} 的纯数据记录读法;`elicitation` = {@link isElicitationFlag};
159
159
  * `transport` 的硬字段(`kind` / `command` / `url`)与软字段(`args` / `env` / `headers` / `principalHeader`)= {@link readRequestMcpTransport}(读器投影 transport 的那一只函数,门读它报的 `lost`;读器在契约包,本仓只据 `lost[0]` 拒、据 `lost` 在重放腿逐处点名);
160
160
  * 读器交回的 http `url` 再问「带不带凭据」= 契约包根的 `findUrlCredential`([ref];拒体另带机读 `field` + `reason`,见 {@link transportUrlCredentialIssue});
161
- * http `principalHeader` 再问「这个名有没有别的属主」= 引擎包根的 `headerOwnershipCollision`(见 {@link principalHeaderOwnershipIssue})。
161
+ * http 这次发送的头集合(件头 + `principalHeader`)再问「有没有一个头名两个属主」= 引擎包根的 `headerOwnershipCollision`(见 {@link sendHeaderOwnershipIssue})。
162
162
  * 于是判官受理的条目,读器({@link normalizeServer})交回的就是申报本身(只差判官自己的合法归一:空容器 / `skillScopeEligible:false` 折成缺席、`toolAxes` 取引擎的拷贝)——
163
163
  * 探测口拨的那一份因此就是提交腿会挂的那一份,不另比一遍(另比就是第二只回答者)。唯一按门分的后果是「声明了 `assertionHeader`」这一个事实(本部署不发送断言,
164
164
  * 承载、不发送):新提交照挂 + 运维 info,探测口整条拒 —— 判据是同一只谓词 `specWithholdsMcpAssertionHeader`(开关 `MCP_ASSERTION_HEADER_SENT` 假时「声明了」即「被扣下」),不是第二份判法(见 `readMcpProbeBody`)。
@@ -185,14 +185,27 @@ export declare function requestMcpDeclarationRefusal(list: unknown): Declaration
185
185
  * 交出去的剔掉 `__proto__` 名成员),server 照挂,运维 warn `mcp_transport_members_dropped` 逐处点名 —— 续跑重放腿不静默丢声明。
186
186
  */
187
187
  export declare function assertRequestMcpEntries(bodyMcp: unknown): void;
188
+ /** 一次发送从一份声明里带出的头:件头(调用方原样写的名 → 值,由对端座按原字节作答)与身份头名(引擎按主体写值)。 */
189
+ export interface DeclaredSendHeaders {
190
+ readonly headers?: Readonly<Record<string, string>> | undefined;
191
+ readonly principalHeader?: string | undefined;
192
+ }
188
193
  /**
189
- * `principalHeader` 的**名**有没有别的属主:判官是引擎包根的 `headerOwnershipCollision`(拨号时判同一条的那一只;这里只问身份头这一个名,件头名由引擎在发送时判)——
190
- * 名(任何大小写)是这一协议的传输自己写的头(MCP:`mcp-session-id` · `mcp-protocol-version` · `content-type` · `accept` · `last-event-id`;A2A 没有),或是引擎的断言头
191
- * (`connector-assertion`)⇒ 这一台拨号时必被拒,新提交在门上按名拒。原因句只点名**属主的拼写**(引擎固定表里的规范化小写头名),不回显调用方写的原串 / 大小写形(调用方写的恰是小写时,句里的名与所写相同)。
192
- * 新提交腿上按名 400(MCP 门表的 `transport`、A2A 判官表的 `principalHeader`)。续跑重放腿不 4xx:MCP 的读器不问它 —— 存量体里这一形照挂,由引擎在拨号时拒那一台、兄弟照挂;
193
- * A2A 的读器问同一张判官表 —— 这一条对端整条不进、`a2a_injection_dropped` 点名原因(引擎物化时同样拒那一台;结局同,原因说在本仓这一行)。
194
+ * 一次发送的**头属主** —— 一条规则:这次发送会带的头集合(件头 + 身份头名)只有一只属主判官,引擎包根的 `headerOwnershipCollision`(引擎每次发送时判同一组的那一只);
195
+ * 受理口用**全部**问它一次。MCP 与 A2A 两座都调这一只:MCP 门表的 `transport` 格,A2A 判官表的 `principalHeader` 格(两键里判序靠后的那一格,读两键的读数)。
196
+ * 判官答撞 ⇒ 这一台拨号即拒(`config.protocol_header_collision`)或此后每一次发送都被拒(`credential_unavailable`)⇒ 新提交在门上按格 400:
197
+ * · 身份头名本身是这一协议的传输自己写的头(MCP:`mcp-session-id` · `mcp-protocol-version` · `content-type` · `accept` · `last-event-id`,任何大小写;A2A 没有),或是引擎的断言头
198
+ * (`connector-assertion`)⇒ `paths.principalHeader`;
199
+ * · 件头名是传输自己写的头 ⇒ `paths.headers`;件里同一头名两个拼写、件头名撞身份头名 ⇒ `paths.headers`。
200
+ * 件里名叫 `connector-assertion` 的头不撞(断言头在件里是座的;引擎只在它作身份头名时保留这个名)。原因句只点名**规范化小写**的头名(属主拼写,或调用方所写名字的小写形、经回显清洗),
201
+ * 不回显调用方的大小写形,从不带值。
202
+ * 不在本规则里的件形 —— 件值字节(CR / LF / NUL)、头名不是 HTTP token、stdio 变量名 —— 只由引擎内部的答案读器判(包根不导出),受理口没有同一只判官可问,照旧运行期 `credential_unavailable`。
203
+ * 续跑重放腿不 4xx:MCP 的读器不问门表 —— 存量体里撞属主的那一台照挂,由引擎拨号 / 发送时拒、兄弟照挂;A2A 的读器问同一张判官表 —— 这一条对端整条不进、`a2a_injection_dropped` 点名原因。
194
204
  */
195
- export declare function principalHeaderOwnershipIssue(principalHeader: string | undefined, protocol: "mcp" | "a2a", path: string): DeclarationKeyIssue | undefined;
205
+ export declare function sendHeaderOwnershipIssue(send: DeclaredSendHeaders, protocol: "mcp" | "a2a", paths: {
206
+ readonly headers: string;
207
+ readonly principalHeader: string;
208
+ }): DeclarationKeyIssue | undefined;
196
209
  /**
197
210
  * The ONE predicate for "this MCP server name belongs to the deployment": the scenario baseline (what mounted for
198
211
  * this scenario) ∪ every name the deployment DECLARED (`CenterMcp.reservedNames` — mounted or not, any scenario).
package/dist/task-mcp.js CHANGED
@@ -171,17 +171,25 @@ function transportUrlCredentialIssue(url) {
171
171
  return undefined;
172
172
  return { path: "transport.url", reason: urlCredentialRefusalReason(found, urlCredentialHeaderExit("transport.headers")), credential: found };
173
173
  }
174
- export function principalHeaderOwnershipIssue(principalHeader, protocol, path) {
175
- if (principalHeader === undefined)
176
- return undefined;
177
- const collision = headerOwnershipCollision({ principalHeader }, protocol);
174
+ export function sendHeaderOwnershipIssue(send, protocol, paths) {
175
+ const collision = headerOwnershipCollision({ headers: send.headers === undefined ? undefined : { ...send.headers }, principalHeader: send.principalHeader }, protocol);
178
176
  if (collision === undefined)
179
177
  return undefined;
180
- return {
181
- path,
182
- reason: `names the header "${collision.first}" (header names are case-insensitive), which is not the principal header's to own — it is ${protocol === "mcp" && collision.first !== MCP_ASSERTION_HEADER ? "a header the MCP transport writes itself" : "the engine's connector assertion header"}; name the principal header something of its own`,
183
- };
178
+ if (collision.engineOwned) {
179
+ const owner = echoKeyName("", collision.first);
180
+ const ownedBy = collision.first === MCP_ASSERTION_HEADER ? "the engine's connector assertion header" : "a header the MCP transport writes itself";
181
+ if (collision.second === send.principalHeader) {
182
+ return { path: paths.principalHeader, reason: `names the header "${owner}" (header names are case-insensitive), which is not the principal header's to own — it is ${ownedBy}; name the principal header something of its own` };
183
+ }
184
+ return { path: paths.headers, reason: `carries the header "${owner}" (header names are case-insensitive), which is not this declaration's to send — it is ${ownedBy}; leave it out` };
185
+ }
186
+ const name = echoKeyName("", collision.first.toLowerCase());
187
+ if (collision.first === send.principalHeader || collision.second === send.principalHeader) {
188
+ return { path: paths.headers, reason: `carries a header named as the principal header "${name}" (header names are case-insensitive) — the engine writes the principal under that name; rename one of them` };
189
+ }
190
+ return { path: paths.headers, reason: `carries the header "${name}" under two spellings (header names are case-insensitive) — one send carries one value per header name; keep one` };
184
191
  }
192
+ const MCP_SEND_HEADER_PATHS = { headers: "transport.headers", principalHeader: "transport.principalHeader" };
185
193
  const ENTRY_KEY_JUDGES = {
186
194
  allowTools: (o) => o.allowTools === undefined || isStringArray(o.allowTools)
187
195
  ? undefined
@@ -231,7 +239,7 @@ const ENTRY_KEY_JUDGES = {
231
239
  return read.lost[0];
232
240
  if (read.transport.kind !== "http")
233
241
  return undefined;
234
- return transportUrlCredentialIssue(read.transport.url) ?? principalHeaderOwnershipIssue(read.transport.principalHeader, "mcp", "transport.principalHeader");
242
+ return transportUrlCredentialIssue(read.transport.url) ?? sendHeaderOwnershipIssue({ headers: read.transport.headers, principalHeader: read.transport.principalHeader }, "mcp", MCP_SEND_HEADER_PATHS);
235
243
  },
236
244
  };
237
245
  function requestEntryRefusal(entry, at) {
@@ -129,6 +129,33 @@ export declare function nestedPauseOf(terminal: TerminalCause): PausedCause | un
129
129
  * status 恒 `"failed"`)。因由形下两条路径合成一句话,规则从二变一。
130
130
  */
131
131
  export declare function pausedCauseOf(terminal: TerminalCause): PausedCause | undefined;
132
+ /**
133
+ * **宿主边界上的诚实终局** —— 宿主 run 行停不停、停在哪个词上,只问这一只判官。
134
+ *
135
+ * · 本腿自己的 `paused` ⇒ 原样(它就是宿主腿的停驻);
136
+ * · `failed.nestedPause` 且 `result.sessionId === hostSessionId` ⇒ 把那次停驻**整只**提成本腿的终局因由
137
+ * (门 / 凭据 / `checkpointId` 同址;结果的其余格 —— `stats` / `verification` / … —— 原样);
138
+ * · 其余 ⇒ **同一引用**原样(真失败;或嵌套停驻的卡不在宿主会话上)。
139
+ *
140
+ * 为什么判据是「停驻的那条腿跑在宿主会话上」:宿主的续跑链按会话把一张卡连回 run —— 决定腿按卡的会话找行
141
+ * (`getActiveTaskId`)与续跑输入(`getCtx`),取消按 run 的会话找待决卡,过期收割按会话相等连行与卡。卡在宿主会话上,
142
+ * 这条 run 才兑现得了它;判官只把兑现得了的停驻判成停驻。verify 的实现腿与修复轮跑在宿主会话上 ⇒ 提;cascade
143
+ * 每级被引擎剥掉会话、级上的卡落在级自己现铸的会话上 ⇒ 不提(照常终局)。这不是按组合器分的特例:引擎让级上的卡落到
144
+ * 宿主可连的会话上的那一天,答案随事实翻,本函数一个字不改。读 `result.sessionId` 而不去店里读卡:它与卡行的会话是同一次
145
+ * `runTask` 的同一个量,零 I/O。
146
+ *
147
+ * 前提:结果出自 core 的组合器(可信产者)—— `failed.nestedPause` 只由引擎的嵌套边界包停驻铸,`paused` 只在卡行写入确认之后返回。
148
+ * 判官**不**验证那张卡在不在、是不是这一张、此刻什么态;会话相等不是对卡的校验。验证器**自己**的停驻
149
+ * (`verification.verifierPause`)不在结果因由里,本函数看不见它 —— 那一形照常终局。
150
+ *
151
+ * 纯函数,不读店、不抛。凭据随提升上来,摘除仍归 {@link sealResultForExit}(调用方照旧先 graft 后封口)。
152
+ * 判官只答「该不该停驻」;认领保不保得住是停驻写口**写成功**与否的事(行写口对 `running` / `suspended` 行 CAS)。
153
+ * `hostSessionId` 缺席 = 没有宿主会话 ⇒ 没有续跑链兑现得了嵌套停驻 ⇒ 原样(相等判自然为假,不另设臂)。
154
+ */
155
+ export declare function atHostBoundary<T extends {
156
+ readonly terminal: TerminalCause;
157
+ readonly sessionId: string;
158
+ }>(result: T, hostSessionId: string | undefined): T;
132
159
  /**
133
160
  * **持久 blob 的平面读面** —— 把一条**任何年代**的持久终局结果读成五词 `status` + 三个平面文本格。
134
161
  *
package/dist/terminal.js CHANGED
@@ -45,6 +45,12 @@ export function nestedPauseOf(terminal) {
45
45
  export function pausedCauseOf(terminal) {
46
46
  return terminal.kind === "paused" ? terminal : nestedPauseOf(terminal);
47
47
  }
48
+ export function atHostBoundary(result, hostSessionId) {
49
+ const parked = nestedPauseOf(result.terminal);
50
+ if (parked === undefined || result.sessionId !== hostSessionId)
51
+ return result;
52
+ return { ...result, terminal: parked };
53
+ }
48
54
  export function persistedPlaneOf(result) {
49
55
  if (result === null || typeof result !== "object")
50
56
  return {};
@@ -54,6 +54,8 @@ export declare function resetArmPoolBudgetForTest(): void;
54
54
  * 闭集:加一个档而不在 {@link armPool} 的档表里处置 ⇒ **编译**红。
55
55
  */
56
56
  type RedactionScope = "free-text" | "bounded-unit";
57
+ /** 测试座:{@link unitCountWithin} 的数组形(`table` 须是合法的已并表:升序、两两不交不相接)。 */
58
+ export declare function unitCountWithinForTest(table: ReadonlyArray<readonly [number, number]>, from: number, to: number, max: number): boolean;
57
59
  /** 本模块会写进文本的全部替换 token(`«…»` 字面),从三张表现算 —— 表改这里自动跟,不另抄一份。
58
60
  * 🔴 **[ref] v2 修复轮起它只剩一个语义:闭形闭集的加载期校验**。运行期「这一段是不是一枚记号」已经由
59
61
  * {@link credentialSpans} 的闭**形**那一支一处答完({@link opaqueUnitEnd}),按闭**集**再认一遍的
@@ -67,7 +69,7 @@ declare const DISPLAY_SAFE_URL_TOKEN = "\u00ABredacted:url\u00BB";
67
69
  export { DISPLAY_SAFE_URL_TOKEN };
68
70
  /**
69
71
  * 🔴 **取证相的重扫模式(测试座,[ref])** —— 生产恒 `"incremental"`;`"full"` 是**参照语义** = 「没有可复用的上一次执行」:
70
- * 每级五条取证臂整段重跑、URL 臂每段重扫,既不读足迹也不回放(池把每一级的脏区间当作「未知」交下去,与级 0 同一个读法)。
72
+ * 每级全部取证臂整段重跑、URL 臂每段重扫,既不读足迹也不回放(池把每一级的脏区间当作「未知」交下去,与级 0 同一个读法)。
71
73
  * 证据表的建法(级 0 记号档 + 逐级折入)与收敛判据(脏区间为空)两种模式是同一条,所以两种模式只差「复用与否」这一位 ——
72
74
  * 增量重扫的全部正确性主张只有一句:**任何输入上两种模式输出逐字相同**(`test/forensic-incremental-rescan.test.ts` 并跑比对;
73
75
  * 折入本身与整表重建的对拍是那个测试文件里的一份测试本地参照)。