@sema-agent/server 7.99.0 → 7.101.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 (174) hide show
  1. package/MIGRATION.md +35 -1
  2. package/README.md +12 -1
  3. package/README.zh-CN.md +9 -1
  4. package/USAGE.md +150 -56
  5. package/dist/approval-ask-machine.d.ts +10 -0
  6. package/dist/approval-ask-machine.js +3 -0
  7. package/dist/approval-card.d.ts +86 -1
  8. package/dist/approval-card.js +42 -8
  9. package/dist/approval-policy-settlement.d.ts +31 -0
  10. package/dist/approval-policy-settlement.js +16 -0
  11. package/dist/approval-reconciler.d.ts +8 -1
  12. package/dist/approval-reconciler.js +5 -4
  13. package/dist/approval.d.ts +44 -20
  14. package/dist/approval.js +14 -6
  15. package/dist/boot/config-center.d.ts +16 -8
  16. package/dist/boot/config-center.js +114 -90
  17. package/dist/boot/execution-env.d.ts +1 -2
  18. package/dist/boot/execution-env.js +21 -3
  19. package/dist/boot/leader.js +12 -9
  20. package/dist/boot/memory-consolidation.d.ts +38 -0
  21. package/dist/boot/memory-consolidation.js +35 -0
  22. package/dist/boot/parked-revive-gate.js +1 -0
  23. package/dist/boot/reapers.js +1 -0
  24. package/dist/boot/resolve-spec.d.ts +0 -1
  25. package/dist/boot/resolve-spec.js +28 -10
  26. package/dist/boot/runner-deps.d.ts +2 -2
  27. package/dist/boot/runner-deps.js +9 -3
  28. package/dist/boot/stage-02-registry-pull.js +9 -0
  29. package/dist/boot/stage-05-execution-env.d.ts +1 -1
  30. package/dist/boot/stage-06-runners.d.ts +2 -2
  31. package/dist/boot/stage-06-runners.js +3 -3
  32. package/dist/boot/stage-07-capability-layer.d.ts +2 -3
  33. package/dist/boot/stage-07-capability-layer.js +6 -6
  34. package/dist/boot/stage-08-reapers.d.ts +3 -4
  35. package/dist/boot/stage-08-reapers.js +2 -2
  36. package/dist/boot/stage-09-leader.d.ts +2 -3
  37. package/dist/boot/stage-10-http-server.d.ts +2 -3
  38. package/dist/boot/stage-10-http-server.js +2 -2
  39. package/dist/boot/stores.js +4 -0
  40. package/dist/capabilities/center-plugins.d.ts +14 -2
  41. package/dist/capabilities/center-plugins.js +13 -5
  42. package/dist/capabilities/hands-lane.js +2 -1
  43. package/dist/capabilities/scenarios.js +18 -5
  44. package/dist/capabilities/tool-defer.d.ts +2 -2
  45. package/dist/config-catalog.d.ts +2 -2
  46. package/dist/config-catalog.js +16 -15
  47. package/dist/config-center/apply-effective.d.ts +59 -39
  48. package/dist/config-center/apply-effective.js +231 -120
  49. package/dist/config-center/apply-ledger.d.ts +45 -16
  50. package/dist/config-center/apply-ledger.js +28 -18
  51. package/dist/config-center/facade.d.ts +12 -6
  52. package/dist/config-center/facade.js +2 -2
  53. package/dist/config-center/hot-keys-registry.d.ts +11 -4
  54. package/dist/config-center/hot-keys-registry.js +11 -7
  55. package/dist/config-center/http-client.d.ts +4 -1
  56. package/dist/config-center/http-client.js +15 -10
  57. package/dist/config-center/read-warnings.d.ts +115 -6
  58. package/dist/config-center/read-warnings.js +115 -5
  59. package/dist/config-center/restart-signal.d.ts +21 -14
  60. package/dist/config-center/restart-signal.js +16 -22
  61. package/dist/config-center/types.d.ts +9 -3
  62. package/dist/config-invariants.d.ts +2 -2
  63. package/dist/config-invariants.js +15 -0
  64. package/dist/config-provider.d.ts +4 -0
  65. package/dist/config-provider.js +64 -9
  66. package/dist/config-types.d.ts +57 -13
  67. package/dist/config.js +87 -22
  68. package/dist/cross-session-settings.js +1 -1
  69. package/dist/deployment-governance.d.ts +45 -12
  70. package/dist/deployment-governance.js +37 -9
  71. package/dist/env-name-allowlist-knobs.d.ts +37 -0
  72. package/dist/env-name-allowlist-knobs.js +24 -0
  73. package/dist/fleet-client.d.ts +2 -1
  74. package/dist/hooks/hook-llm.js +13 -4
  75. package/dist/host-lsp-manager.d.ts +41 -0
  76. package/dist/host-lsp-manager.js +15 -0
  77. package/dist/http/active-run-conflict.d.ts +2 -12
  78. package/dist/http/active-run-conflict.js +1 -6
  79. package/dist/http/admission.js +30 -7
  80. package/dist/http/memory-store-refusal-reply.d.ts +24 -0
  81. package/dist/http/memory-store-refusal-reply.js +10 -0
  82. package/dist/http/resume-legs.d.ts +1 -1
  83. package/dist/http/resume-legs.js +14 -10
  84. package/dist/http/route-ctx.d.ts +2 -2
  85. package/dist/http/routes/approvals-assistant.d.ts +1 -1
  86. package/dist/http/routes/approvals-assistant.js +3 -2
  87. package/dist/http/routes/capabilities.js +3 -3
  88. package/dist/http/routes/memory-bundle.js +3 -0
  89. package/dist/http/routes/memory-compliance.js +3 -0
  90. package/dist/http/routes/memory-origin.js +3 -0
  91. package/dist/http/routes/sessions.js +7 -0
  92. package/dist/http/routes/side-query.js +10 -4
  93. package/dist/http/routes/tasks.js +7 -6
  94. package/dist/http/server.d.ts +6 -5
  95. package/dist/http/wire-types.d.ts +6 -3
  96. package/dist/instruction-source-notice.d.ts +97 -0
  97. package/dist/instruction-source-notice.js +78 -0
  98. package/dist/leader/wire.d.ts +20 -12
  99. package/dist/leader/wire.js +7 -6
  100. package/dist/memory-layer-legacy-lock.d.ts +14 -0
  101. package/dist/memory-layer-legacy-lock.js +74 -0
  102. package/dist/memory-store-refusal.d.ts +37 -0
  103. package/dist/memory-store-refusal.js +14 -0
  104. package/dist/model-entry-refusal.d.ts +130 -0
  105. package/dist/model-entry-refusal.js +157 -0
  106. package/dist/model-provider.d.ts +9 -3
  107. package/dist/model-route-endpoint.d.ts +1 -1
  108. package/dist/model-select.d.ts +49 -4
  109. package/dist/model-select.js +48 -17
  110. package/dist/observability/corrupt-read-seat.d.ts +21 -4
  111. package/dist/observability/corrupt-read-seat.js +31 -0
  112. package/dist/observability/fail-open.d.ts +16 -4
  113. package/dist/observability/fail-open.js +16 -4
  114. package/dist/observability/metrics.js +1 -0
  115. package/dist/observability/run-terminal.js +1 -0
  116. package/dist/observability/secret-env-scrub.d.ts +27 -3
  117. package/dist/observability/secret-env-scrub.js +22 -6
  118. package/dist/orchestration/workflow-completion-inbox.d.ts +31 -23
  119. package/dist/orchestration/workflow-completion-inbox.js +8 -3
  120. package/dist/plugins/approval-ask-store-file.d.ts +20 -2
  121. package/dist/plugins/approval-ask-store-file.js +21 -3
  122. package/dist/plugins/approval-ask-store-memory.d.ts +2 -0
  123. package/dist/plugins/approval-ask-store-memory.js +39 -13
  124. package/dist/plugins/approval-ask-store-sql.d.ts +24 -5
  125. package/dist/plugins/approval-ask-store-sql.js +27 -3
  126. package/dist/plugins/caching-session-store.d.ts +6 -0
  127. package/dist/plugins/caching-session-store.js +6 -0
  128. package/dist/plugins/checkpoint-store-sql.d.ts +9 -3
  129. package/dist/plugins/checkpoint-store-sql.js +19 -2
  130. package/dist/plugins/local-checkpoint-store.js +2 -0
  131. package/dist/plugins/local-session-store.d.ts +20 -25
  132. package/dist/plugins/local-session-store.js +111 -64
  133. package/dist/plugins/mailbox-store-sql.d.ts +7 -5
  134. package/dist/plugins/mailbox-store-sql.js +6 -4
  135. package/dist/plugins/permission-rule-store-file.js +3 -2
  136. package/dist/plugins/pg-session-storage.d.ts +7 -0
  137. package/dist/plugins/pg-session-storage.js +19 -0
  138. package/dist/plugins/remote-env-host.d.ts +3 -1
  139. package/dist/plugins/remote-env-host.js +3 -6
  140. package/dist/plugins/sql-json-column.d.ts +16 -3
  141. package/dist/plugins/sql-json-column.js +15 -4
  142. package/dist/plugins/tidb-pool.js +5 -0
  143. package/dist/plugins/tidb-session-store.d.ts +13 -0
  144. package/dist/plugins/tidb-session-store.js +14 -0
  145. package/dist/plugins/web-search.d.ts +44 -23
  146. package/dist/plugins/web-search.js +161 -50
  147. package/dist/project-memory.d.ts +10 -0
  148. package/dist/project-memory.js +28 -14
  149. package/dist/run-local.d.ts +17 -15
  150. package/dist/run-local.js +119 -18
  151. package/dist/runs.js +6 -5
  152. package/dist/runtime-governance.d.ts +50 -3
  153. package/dist/runtime-governance.js +5 -0
  154. package/dist/sealed-key.d.ts +5 -2
  155. package/dist/server-secret-env.d.ts +26 -2
  156. package/dist/server-secret-env.js +9 -0
  157. package/dist/task-settings.d.ts +1 -1
  158. package/dist/tool-approval.d.ts +150 -28
  159. package/dist/tool-approval.js +150 -54
  160. package/dist/trace/core-keyset-guard.d.ts +30 -8
  161. package/dist/trace/engine-notice-wire.d.ts +27 -1
  162. package/dist/trace/engine-notice-wire.js +8 -0
  163. package/dist/trace/ledger-sink.js +2 -2
  164. package/dist/trace/project.d.ts +13 -0
  165. package/dist/trace/project.js +22 -5
  166. package/dist/trace/projection-drop.d.ts +2 -0
  167. package/dist/trace/projection-drop.js +6 -0
  168. package/dist/trace/sema-provenance.d.ts +4 -1
  169. package/dist/trace/sema-provenance.js +1 -1
  170. package/dist/trace/task-notification-facets.d.ts +65 -0
  171. package/dist/trace/task-notification-facets.js +31 -0
  172. package/dist/trace/wire-projection-faces.d.ts +3 -3
  173. package/dist/trace/wire-projection-faces.js +3 -3
  174. package/package.json +3 -3
@@ -7,10 +7,12 @@ import { hasBidiControls } from "./text-bidi.js";
7
7
  import { approvalPayloadFingerprint } from "./plugins/approval-payload-fingerprint.js";
8
8
  import { createLogger } from "./observability/logger.js";
9
9
  import { recordFailOpen } from "./observability/fail-open.js";
10
- import { deriveAskId, deriveBatchId, MAX_DECISION_NOTE_CHARS } from "./approval-ask-machine.js";
11
- import { ApprovalCardEnvelopeSchema, MAX_RULE_OFFERS, buildApprovalCard, buildApprovalCardEnvelope, buildApprovalRequestFrame, buildRevokeFrame, readDenialLimitFallback, readProbeCause, readReadRootCandidate, readRuleEvidence, readRuleOffersAbsence, RuleOfferRawSchema, backfillLegacyBatchMemberKind, screenUncoveredDetailSeat, } from "./approval-card.js";
12
- import { projectAskTerminalClaimTruth } from "./plugins/approval-ask-store-sql.js";
10
+ import { deriveAskId, deriveAskScopedBatchId, deriveBatchId, MAX_DECISION_NOTE_CHARS } from "./approval-ask-machine.js";
11
+ import { ApprovalCardEnvelopeSchema, MAX_RULE_OFFERS, buildApprovalCard, buildApprovalCardEnvelope, buildApprovalRequestFrame, buildRevokeFrame, readDenialLimitFallback, readProbeCause, readReadRootCandidate, readRuleEvidence, readRuleOffersAbsence, readRuleOffersAbsenceSeat, readParkRowTrueSeat, RuleOfferRawSchema, backfillLegacyBatchMemberKind, screenUncoveredDetailSeat, } from "./approval-card.js";
12
+ import { POLICY_REFUSAL_DECISION, projectAskTerminalClaimTruth } from "./plugins/approval-ask-store-sql.js";
13
+ import { settleByPolicy } from "./approval-policy-settlement.js";
13
14
  import { governanceAskMarksFor, runWithGovernanceAskScope } from "./governance-ask-marks.js";
15
+ import { governanceOriginOf } from "./runtime-governance.js";
14
16
  const defaultLogger = createLogger();
15
17
  export const APPROVAL_GATE_KINDS = ["human", "irreversible_ask"];
16
18
  export function isApprovalGateKind(k) {
@@ -82,6 +84,24 @@ function sessionAllowKey(owner, sessionId, category) {
82
84
  export function standingAnswerMayRetire(facts) {
83
85
  return !facts.governanceForced && !facts.requiresRealApproval && !facts.mandated;
84
86
  }
87
+ export function parkRowStandingAnswerFacts(pending, gate, governance) {
88
+ const riskDescriptor = "riskDescriptor" in gate ? gate.riskDescriptor : undefined;
89
+ return {
90
+ governanceForced: governanceOriginOf({ riskDescriptor }, governance) === true,
91
+ requiresRealApproval: readParkRowTrueSeat(pending, "requiresRealApproval"),
92
+ mandated: readParkRowTrueSeat(pending, "mandated") || mandatedFromSeat(readRuleOffersAbsenceSeat(pending)),
93
+ };
94
+ }
95
+ function mandatedFromSeat(seat) {
96
+ switch (seat.state) {
97
+ case "word":
98
+ return seat.word === "mandated";
99
+ case "unreadable":
100
+ return true;
101
+ case "absent":
102
+ return false;
103
+ }
104
+ }
85
105
  export const PERSIST_RULE_TEXT_ERROR = `persistRule.rule must be a non-empty string of at most ${MAX_RULE_TEXT_CHARS} characters`;
86
106
  export const PERSIST_RULE_EDITED_FLAG_ERROR = "persistRule.edited must be a boolean when present";
87
107
  export const PERSIST_RULE_BATCH_INDEX_ERROR = "persistRule.batchOfferIndex must be a non-negative integer when present";
@@ -152,6 +172,10 @@ export function parseToolApprovalResponse(body) {
152
172
  }
153
173
  return { ok: false, error: 'decision must be one of "allow" | "allow_session" | "deny"' };
154
174
  }
175
+ function unattendedRefusalReason(arm) {
176
+ return ("UNATTENDED_APPROVAL_POLICY=deny: this deployment declares that no one attends its approvals, so an approval " +
177
+ `request no one can answer is refused here instead of being parked for a person (arm: ${arm}).`);
178
+ }
155
179
  function isChildAsk(req) {
156
180
  return req.fromSubagent === true;
157
181
  }
@@ -321,11 +345,26 @@ export class ToolApprovalCoordinator {
321
345
  this.askAudit = opts?.askAudit;
322
346
  }
323
347
  unattendedOutcome(site) {
324
- const outcome = this.unattendedPolicy === "deny" ? false : "unavailable";
348
+ let outcome;
349
+ let outcomeWord;
350
+ switch (this.unattendedPolicy) {
351
+ case "park":
352
+ outcome = "unavailable";
353
+ outcomeWord = "unavailable";
354
+ break;
355
+ case "deny":
356
+ outcome = { allow: false, settledBy: POLICY_REFUSAL_DECISION.settledBy, reason: unattendedRefusalReason(site.arm) };
357
+ outcomeWord = "deny";
358
+ break;
359
+ default: {
360
+ const unreachable = this.unattendedPolicy;
361
+ throw new Error(`unattendedOutcome: unknown UNATTENDED_APPROVAL_POLICY word ${String(unreachable)}`);
362
+ }
363
+ }
325
364
  defaultLogger.warn("approver_unavailable", {
326
365
  arm: site.arm,
327
366
  policy: this.unattendedPolicy,
328
- outcome: outcome === false ? "deny" : "unavailable",
367
+ outcome: outcomeWord,
329
368
  owner: site.owner ?? null,
330
369
  taskId: site.taskId ?? null,
331
370
  toolName: site.toolName ?? null,
@@ -336,6 +375,9 @@ export class ToolApprovalCoordinator {
336
375
  get unattendedRoutesToPark() {
337
376
  return this.unattendedPolicy !== "deny";
338
377
  }
378
+ batchIdFor(askId, sourceTaskId, runId, legKey) {
379
+ return this.unattendedRoutesToPark ? deriveBatchId(sourceTaskId, runId, legKey) : deriveAskScopedBatchId(askId);
380
+ }
339
381
  unattendedAskOutcome(site) {
340
382
  return this.unattendedOutcome({
341
383
  arm: "window_zero",
@@ -344,6 +386,32 @@ export class ToolApprovalCoordinator {
344
386
  ...(site.toolName !== undefined ? { toolName: site.toolName } : {}),
345
387
  });
346
388
  }
389
+ async landUnattendedOutcome(outcome, row) {
390
+ const store = this.askStore;
391
+ if (outcome === "unavailable" || !store)
392
+ return outcome;
393
+ const dispatch = (res, where) => {
394
+ if (res.claimed) {
395
+ this.settleSameAskIdParkRoute(row.askId);
396
+ return undefined;
397
+ }
398
+ const read = this.classifyClaimLoss(row.askId, res.current, where);
399
+ this.dispatchClaimTruth(row.askId, read);
400
+ return read;
401
+ };
402
+ let res;
403
+ try {
404
+ res = await this.withStoreDeadline(settleByPolicy(store, row, this.unattendedPolicy), "claimTerminal(unattended-fallback)", DURABLE_CALL_TIMEOUT_MS, (late) => {
405
+ dispatch(late, "late:unattended-fallback");
406
+ });
407
+ }
408
+ catch (err) {
409
+ this.noteStoreError(err, "claimTerminal(unattended-fallback)");
410
+ return outcome;
411
+ }
412
+ const read = dispatch(res, "claim:unattended-fallback");
413
+ return read?.kind === "decided" ? read.outcome : outcome;
414
+ }
347
415
  admit(taskId, owner) {
348
416
  if (!this.askStore)
349
417
  return () => undefined;
@@ -657,6 +725,23 @@ export class ToolApprovalCoordinator {
657
725
  for (const entry of [...entries])
658
726
  entry.settleParkRoute();
659
727
  }
728
+ dispatchClaimTruth(askId, read) {
729
+ switch (read.kind) {
730
+ case "decided":
731
+ this.notifyExternalDecision(askId, read.outcome);
732
+ return;
733
+ case "parked":
734
+ this.settleSameAskIdParkRoute(askId);
735
+ return;
736
+ case "absent":
737
+ case "edit-in-flight":
738
+ return;
739
+ default: {
740
+ const unreachable = read;
741
+ throw new Error(`dispatchClaimTruth: unknown recovery read ${String(unreachable)}`);
742
+ }
743
+ }
744
+ }
660
745
  recordParkTombstone(approvalId, tomb) {
661
746
  const existing = this.parkTombstones.get(approvalId);
662
747
  if (existing !== undefined) {
@@ -901,23 +986,38 @@ export class ToolApprovalCoordinator {
901
986
  let wireApprovalId = id;
902
987
  let cardForFrame = card;
903
988
  let persistedExpiresAtMs = expiresAtMs;
904
- let lateEnsuredAskId;
905
- let abandonedEnsure = false;
989
+ let lateEnsuredRow;
990
+ let lateTerminalRow;
991
+ let abandonedAs;
906
992
  const attemptCreatedAtMs = Date.now();
907
- const voidAbandonedRow = (orphanAskId) => {
993
+ const finishAbandonedRow = (orphan) => {
908
994
  const store = this.askStore;
909
- if (!store)
910
- return;
911
- void store
912
- .transitionAsk(orphanAskId, "STREAM_PENDING", "VOID", { updatedAtMs: Date.now() })
913
- .catch((err) => this.noteStoreError(err, "transitionAsk(abandon-late-ensure)"));
995
+ if (!store || abandonedAs === undefined)
996
+ return undefined;
997
+ if (abandonedAs === "void") {
998
+ void store
999
+ .transitionAsk(orphan.askId, "STREAM_PENDING", "VOID", { updatedAtMs: Date.now() })
1000
+ .catch((err) => this.noteStoreError(err, "transitionAsk(abandon-late-ensure)"));
1001
+ return undefined;
1002
+ }
1003
+ return this.landUnattendedOutcome(abandonedAs, orphan);
1004
+ };
1005
+ const abandonRow = (handed) => {
1006
+ abandonedAs = handed === undefined || handed === "unavailable" ? "void" : handed;
1007
+ return lateEnsuredRow !== undefined ? finishAbandonedRow(lateEnsuredRow) : undefined;
1008
+ };
1009
+ const replayTerminalRow = (row) => {
1010
+ const replayed = replayTerminalAskRow(row);
1011
+ return replayed === "unavailable"
1012
+ ? this.unattendedOutcome({ arm: "replayed_parked", taskId: origin.taskId, ...(origin.owner !== null ? { owner: origin.owner } : {}), toolName: req.toolName, reachability })
1013
+ : replayed;
914
1014
  };
915
1015
  if (this.askStore && card) {
916
1016
  const sourceTaskId = req.sourceTaskId ?? origin.taskId;
917
1017
  const runId = origin.taskId;
918
1018
  const legKey = origin.legKey;
919
1019
  askId = deriveAskId(sourceTaskId, runId, req.toolCallId, legKey, req.delegation?.parentToolCallId);
920
- batchId = deriveBatchId(sourceTaskId, runId, legKey);
1020
+ batchId = this.batchIdFor(askId, sourceTaskId, runId, legKey);
921
1021
  try {
922
1022
  const existingOrCreated = await this.withStoreDeadline(this.askStore.ensureAsk({
923
1023
  askId,
@@ -936,16 +1036,19 @@ export class ToolApprovalCoordinator {
936
1036
  expiresAtMs,
937
1037
  createdAtMs: attemptCreatedAtMs,
938
1038
  }), "ensureAsk", DURABLE_CALL_TIMEOUT_MS, (late) => {
939
- lateEnsuredAskId = late.inserted && late.row.state === "STREAM_PENDING" ? late.row.askId : undefined;
940
- if (abandonedEnsure && lateEnsuredAskId !== undefined)
941
- voidAbandonedRow(lateEnsuredAskId);
1039
+ if (late.row.state !== "STREAM_PENDING") {
1040
+ lateTerminalRow = late.row;
1041
+ this.dispatchClaimTruth(late.row.askId, this.classifyClaimLoss(late.row.askId, projectAskTerminalClaimTruth(late.row), "late:ensureAsk"));
1042
+ return;
1043
+ }
1044
+ lateEnsuredRow = late.inserted ? { askId: late.row.askId, batchId: late.row.batchId } : undefined;
1045
+ if (lateEnsuredRow !== undefined)
1046
+ void finishAbandonedRow(lateEnsuredRow);
942
1047
  });
1048
+ batchId = existingOrCreated.row.batchId;
943
1049
  if (existingOrCreated.row.state !== "STREAM_PENDING") {
944
1050
  releaseAdmission();
945
- const replayed = replayTerminalAskRow(existingOrCreated.row);
946
- return replayed === "unavailable"
947
- ? this.unattendedOutcome({ arm: "replayed_parked", taskId: origin.taskId, ...(origin.owner !== null ? { owner: origin.owner } : {}), toolName: req.toolName, reachability })
948
- : replayed;
1051
+ return replayTerminalRow(existingOrCreated.row);
949
1052
  }
950
1053
  const persistedEnvelope = ApprovalCardEnvelopeSchema.safeParse(existingOrCreated.row.cardJson);
951
1054
  const maxMintableWindowMs = Math.max(effectiveWindowMs, this.unreachedTtlMs);
@@ -954,7 +1057,8 @@ export class ToolApprovalCoordinator {
954
1057
  this.noteStoreError(new Error(`persisted approval row is unusable (envelopeOk=${persistedEnvelope.success}, deadlineOk=${sanePersistedDeadline}, ` +
955
1058
  `origin=${existingOrCreated.inserted ? "row-this-call-just-inserted" : "row-hit-idempotently"})`), "ensureAsk(persisted-row-unusable)");
956
1059
  releaseAdmission();
957
- return "unavailable";
1060
+ const unattended = this.unattendedOutcome({ arm: "persisted_row_unusable", taskId: origin.taskId, ...(origin.owner !== null ? { owner: origin.owner } : {}), toolName: req.toolName, reachability });
1061
+ return existingOrCreated.inserted ? this.landUnattendedOutcome(unattended, existingOrCreated.row) : unattended;
958
1062
  }
959
1063
  persistedExpiresAtMs = existingOrCreated.row.expiresAtMs;
960
1064
  wireApprovalId = persistedEnvelope.data.approvalId;
@@ -1001,7 +1105,10 @@ export class ToolApprovalCoordinator {
1001
1105
  let indeterminate = true;
1002
1106
  for (let attempt = 0; attempt <= DURABLE_CONVERGE_BACKOFF_MS.length; attempt++) {
1003
1107
  try {
1004
- confirmedRowExists = (await this.withStoreDeadline(this.askStore.getAsk(askId), "getAsk(ensureAsk-confirm)")) !== null;
1108
+ const confirmedRow = await this.withStoreDeadline(this.askStore.getAsk(askId), "getAsk(ensureAsk-confirm)");
1109
+ confirmedRowExists = confirmedRow !== null;
1110
+ if (confirmedRow !== null)
1111
+ batchId = confirmedRow.batchId;
1005
1112
  indeterminate = false;
1006
1113
  break;
1007
1114
  }
@@ -1011,23 +1118,26 @@ export class ToolApprovalCoordinator {
1011
1118
  await new Promise((r) => setTimeout(r, DURABLE_CONVERGE_BACKOFF_MS[attempt]).unref?.());
1012
1119
  }
1013
1120
  }
1121
+ if (lateTerminalRow !== undefined) {
1122
+ releaseAdmission();
1123
+ return replayTerminalRow(lateTerminalRow);
1124
+ }
1014
1125
  if (indeterminate) {
1015
- abandonedEnsure = true;
1016
- if (lateEnsuredAskId !== undefined)
1017
- voidAbandonedRow(lateEnsuredAskId);
1126
+ const unattended = this.unattendedOutcome({ arm: "ensure_indeterminate", taskId: origin.taskId, ...(origin.owner !== null ? { owner: origin.owner } : {}), toolName: req.toolName, reachability });
1127
+ const landed = abandonRow(unattended);
1018
1128
  releaseAdmission();
1019
- return "unavailable";
1129
+ return landed ?? unattended;
1020
1130
  }
1021
1131
  if (!confirmedRowExists) {
1022
1132
  askId = undefined;
1023
1133
  batchId = undefined;
1024
- abandonedEnsure = true;
1025
- if (lateEnsuredAskId !== undefined)
1026
- voidAbandonedRow(lateEnsuredAskId);
1027
1134
  if (reachability === "unreached") {
1135
+ const unattended = this.unattendedOutcome({ arm: "unreached_no_row", taskId: origin.taskId, ...(origin.owner !== null ? { owner: origin.owner } : {}), toolName: req.toolName, reachability });
1136
+ const landed = abandonRow(unattended);
1028
1137
  releaseAdmission();
1029
- return this.unattendedOutcome({ arm: "unreached_no_row", taskId: origin.taskId, ...(origin.owner !== null ? { owner: origin.owner } : {}), toolName: req.toolName, reachability });
1138
+ return landed ?? unattended;
1030
1139
  }
1140
+ void abandonRow(undefined);
1031
1141
  }
1032
1142
  }
1033
1143
  }
@@ -1044,7 +1154,7 @@ export class ToolApprovalCoordinator {
1044
1154
  let selfEntry;
1045
1155
  let hostSettled;
1046
1156
  let denyReasonSettled;
1047
- const shapeDeny = () => {
1157
+ const shapeDeny = (bare) => {
1048
1158
  if (hostSettled !== undefined || (denyReasonSettled !== undefined && denyReasonSettled !== "")) {
1049
1159
  return {
1050
1160
  allow: false,
@@ -1052,18 +1162,17 @@ export class ToolApprovalCoordinator {
1052
1162
  ...(denyReasonSettled !== undefined && denyReasonSettled !== "" ? { reason: denyReasonSettled } : {}),
1053
1163
  };
1054
1164
  }
1055
- return false;
1165
+ return bare;
1056
1166
  };
1057
1167
  const shapeOutcome = (settled) => {
1058
1168
  if (windowRouteUnavailable) {
1059
- return this.unattendedOutcome({ arm: "window_expired", taskId: origin.taskId, ...(origin.owner !== null ? { owner: origin.owner } : {}), toolName: req.toolName, reachability }) === false
1060
- ? shapeDeny()
1061
- : "unavailable";
1169
+ const unattended = this.unattendedOutcome({ arm: "window_expired", taskId: origin.taskId, ...(origin.owner !== null ? { owner: origin.owner } : {}), toolName: req.toolName, reachability });
1170
+ return unattended === "unavailable" ? unattended : shapeDeny(unattended);
1062
1171
  }
1063
1172
  if (settled.allowed) {
1064
1173
  return settled.updatedInput !== undefined ? { allow: true, updatedInput: settled.updatedInput } : true;
1065
1174
  }
1066
- return shapeDeny();
1175
+ return shapeDeny(false);
1067
1176
  };
1068
1177
  const settle = (allowed, outcome, updatedInput, hostSettledBy, denyReason) => {
1069
1178
  if (done)
@@ -1104,21 +1213,7 @@ export class ToolApprovalCoordinator {
1104
1213
  const broadcastClaimTruth = (read) => {
1105
1214
  if (askId === undefined)
1106
1215
  return;
1107
- switch (read.kind) {
1108
- case "decided":
1109
- this.notifyExternalDecision(askId, read.outcome);
1110
- return;
1111
- case "parked":
1112
- this.settleSameAskIdParkRoute(askId);
1113
- return;
1114
- case "absent":
1115
- case "edit-in-flight":
1116
- return;
1117
- default: {
1118
- const unreachable = read;
1119
- throw new Error(`broadcastClaimTruth: unknown recovery read ${String(unreachable)}`);
1120
- }
1121
- }
1216
+ this.dispatchClaimTruth(askId, read);
1122
1217
  };
1123
1218
  const settleFromClaimLoss = (read) => {
1124
1219
  switch (read.kind) {
@@ -1271,7 +1366,7 @@ export class ToolApprovalCoordinator {
1271
1366
  await resolveLocalOutcome({
1272
1367
  intent: "expire",
1273
1368
  storeLabel: "claimTerminal(window)",
1274
- claim: (timeoutMs) => boundedClaim(() => store.claimTerminal(pinnedAskId, pinnedBatchId, "expire"), "claimTerminal(window)", timeoutMs, (late) => {
1369
+ claim: (timeoutMs) => boundedClaim(() => settleByPolicy(store, { askId: pinnedAskId, batchId: pinnedBatchId }, this.unattendedPolicy), "claimTerminal(window)", timeoutMs, (late) => {
1275
1370
  if (!late.claimed) {
1276
1371
  lateClaimLossSettle(late.current, "expire");
1277
1372
  return;
@@ -1459,7 +1554,7 @@ export class ToolApprovalCoordinator {
1459
1554
  await resolveLocalOutcome({
1460
1555
  intent: "unreachable",
1461
1556
  storeLabel: "claimTerminal(emit-failed)",
1462
- claim: (timeoutMs) => boundedClaim(() => store.claimTerminal(pinnedAskId, pinnedBatchId, "expire"), "claimTerminal(emit-failed)", timeoutMs, (late) => {
1557
+ claim: (timeoutMs) => boundedClaim(() => settleByPolicy(store, { askId: pinnedAskId, batchId: pinnedBatchId }, this.unattendedPolicy), "claimTerminal(emit-failed)", timeoutMs, (late) => {
1463
1558
  if (!late.claimed) {
1464
1559
  lateClaimLossSettle(late.current, "unreachable");
1465
1560
  return;
@@ -2011,6 +2106,7 @@ export class ToolApprovalCoordinator {
2011
2106
  ...(e.sessionId !== undefined ? { sessionId: e.sessionId } : {}),
2012
2107
  ...(e.requiresRealApproval ? { requiresRealApproval: true } : {}),
2013
2108
  ...(e.governanceForced ? { governanceForced: true } : {}),
2109
+ ...(e.mandated ? { mandated: true } : {}),
2014
2110
  ...(e.originTaskId !== undefined ? { fromSubagent: true, originTaskId: e.originTaskId } : {}),
2015
2111
  ...(contentKind !== undefined ? { contentKind } : {}),
2016
2112
  frame: e.frame.approvalId === id ? e.frame : { ...e.frame, approvalId: id },
@@ -17,7 +17,7 @@
17
17
  * ⑨⑩([ref],2026-08-31)把两个最大的 wire 面也纳入:TaskSpec 按「server 有无写点」记 Wired/Unwired,
18
18
  * TaskResult 按「转发/摘除」记 Forwarded/Stripped —— 谁改 resolve-spec.ts / runs.ts 的发射点谁同步本文件。
19
19
  */
20
- import type { TaskNotificationPayload, SemaProvenance, BackgroundChildEvent, RosterEntry, AskRequest, CheckpointSummary, TaskEvent, TraceEvent, MailboxMessage, MailboxAppendMessage, TaskSpec, TaskResult, WiringManifest, GateOutcome, GateDisposition, Settlement, PausedCause, WorkflowRun, ReadRootGrantCandidate, McpReconnectResult, McpServerStatus } from "@sema-agent/core";
20
+ import type { TaskNotificationPayload, SemaProvenance, BackgroundChildEvent, RosterEntry, AskRequest, CheckpointSummary, PendingAction, TaskEvent, TraceEvent, MailboxMessage, MailboxAppendMessage, TaskSpec, TaskResult, WiringManifest, GateOutcome, GateDisposition, Settlement, PausedCause, WorkflowRun, ReadRootGrantCandidate, McpReconnectResult, McpServerStatus, NodeLspManagerOptions, RunnerDeps } from "@sema-agent/core";
21
21
  import type { ToolApprovalFrame } from "../tool-approval.js";
22
22
  /** 编译期断言:T 必须收敛到 never(有残余键 = tsc 红)。
23
23
  * 🔴 **导出**([ref] U1/U2):本仓「词表闭集,未处置成员=编译红」的钉法只许有**一个**形 ——
@@ -25,7 +25,7 @@ import type { ToolApprovalFrame } from "../tool-approval.js";
25
25
  * `type X<T extends never> = T`(抄一份就是第二个写者,两处哪天分岔谁都发现不了)。
26
26
  * 本文件只有类型、不产 JS,所以被 `import type` 引用不新增任何运行期边。 */
27
27
  export type AssertAllKeysHandled<T extends never> = T;
28
- type NotificationProjected = "task_id" | "task_type" | "toolUseId" | "status" | "summary" | "result" | "output_file" | "usage" | "sessionId" | "seq" | "lines" | "stoppedBy" | "source" | "exitCode" | "partial" | "diagnostics" | "recentSteps" | "editedFiles" | "resumable" | "completionId" | "error" | "errorCode" | "apiFailure" | "agentMessage" | "_sema_provenance";
28
+ type NotificationProjected = "task_id" | "task_type" | "toolUseId" | "status" | "summary" | "result" | "output_file" | "usage" | "sessionId" | "seq" | "lines" | "stoppedBy" | "source" | "exitCode" | "partial" | "diagnostics" | "recentSteps" | "editedFiles" | "resumable" | "completionId" | "error" | "errorCode" | "apiFailure" | "agentMessage" | "_sema_provenance" | "spawnName" | "handback" | "compaction" | "resupplied" | "attempt";
29
29
  type NotificationExcluded = (typeof import("./wire-projection-faces.js").WIRE_PROJECTION_FACES)["task_notification"]["excluded"][number];
30
30
  type _GuardNotification = AssertAllKeysHandled<Exclude<keyof TaskNotificationPayload, NotificationProjected | NotificationExcluded>>;
31
31
  type _GuardNotificationReverse = AssertAllKeysHandled<Exclude<NotificationProjected | NotificationExcluded, keyof TaskNotificationPayload>>;
@@ -59,8 +59,8 @@ type _GuardMailboxReverse = AssertAllKeysHandled<Exclude<MailboxProjected, keyof
59
59
  type MailboxAppendProjected = "from" | "content" | "sentAt" | "hopChain" | "peerMeta";
60
60
  type _GuardMailboxAppend = AssertAllKeysHandled<Exclude<keyof MailboxAppendMessage, MailboxAppendProjected>>;
61
61
  type _GuardMailboxAppendReverse = AssertAllKeysHandled<Exclude<MailboxAppendProjected, keyof MailboxAppendMessage>>;
62
- type WiringSectionProjected = "schemaVersion" | "leg" | "ask" | "question" | "interaction" | "elicit" | "parkLane" | "session" | "fleet" | "permissionRules" | "modelGate" | "autoMode" | "mcp" | "tools" | "hooks" | "lsp" | "writeProtection";
63
- type WiringSectionExcluded = "governance" | "configFingerprint" | "autoConsolidation" | "readDeny";
62
+ type WiringSectionProjected = "schemaVersion" | "leg" | "ask" | "question" | "interaction" | "elicit" | "parkLane" | "session" | "fleet" | "permissionRules" | "modelGate" | "autoMode" | "mcp" | "tools" | "hooks" | "lsp" | "writeProtection" | "teamRole";
63
+ type WiringSectionExcluded = "governance" | "configFingerprint" | "autoConsolidation" | "readDeny" | "peerPresence" | "fileHistory";
64
64
  type _GuardWiringSections = AssertAllKeysHandled<Exclude<keyof WiringManifest, WiringSectionProjected | WiringSectionExcluded>>;
65
65
  type _GuardWiringSectionsReverse = AssertAllKeysHandled<Exclude<WiringSectionProjected | WiringSectionExcluded, keyof WiringManifest>>;
66
66
  type WiringSection<K extends keyof WiringManifest> = NonNullable<WiringManifest[K]>;
@@ -130,15 +130,23 @@ type _GuardPermissionTraceReverse = AssertAllKeysHandled<Exclude<PermissionTrace
130
130
  /** `T` 非 `never` 时收敛到 `never`(空集 ⇒ 残留 `"EMPTY"` ⇒ tsc 红)。 */
131
131
  type AssertNonEmpty<T> = [T] extends [never] ? "EMPTY" : never;
132
132
  type _GuardPermissionTraceNonEmpty = AssertAllKeysHandled<AssertNonEmpty<PermissionTraceKind>>;
133
- type SummaryProjected = "sessionId" | "scope" | "createdAt" | "gateKind" | "severity" | "spentMicroUsd" | "deadline" | "hasBidiControls" | "requiresRealApproval" | "denialLimitFallback" | "origin" | "ruleStoreUnreadable";
133
+ type SummaryProjected = "sessionId" | "scope" | "createdAt" | "gateKind" | "severity" | "spentMicroUsd" | "deadline" | "hasBidiControls" | "requiresRealApproval" | "denialLimitFallback" | "origin" | "ruleStoreUnreadable" | "mandated";
134
134
  type SummaryExcluded = "token" | "toolInput" | "toolName" | "toolCallId" | "preview" | "principal" | "sourceTaskId" | "contentKind" | "restoreMode" | "checkpointId" | "previewWithheld";
135
135
  type _GuardInboxSummary = AssertAllKeysHandled<Exclude<keyof CheckpointSummary, SummaryProjected | SummaryExcluded>>;
136
136
  type _GuardInboxSummaryReverse = AssertAllKeysHandled<Exclude<SummaryProjected | SummaryExcluded, keyof CheckpointSummary>>;
137
+ type ParkArmKeys = keyof Extract<PendingAction, {
138
+ kind: "tool_approval";
139
+ }>;
140
+ type ParkArmQueueProjected = "toolName" | "toolCallId" | "boundInputHash" | "ruleOffers" | "hasBidiControls" | keyof import("../approval-card.js").ParkRowFacts;
141
+ type ParkArmQueueExcluded = "kind" | "args" | "batchToolCallIds" | "completedCallIds" | "capabilityId" | "contractId" | "shapeDigest" | "implementationRevision" | "rosterDigest" | "family" | "pathTarget" | "preview" | "previewWithheld" | "execCwd" | "denialLimitFallback" | "origin" | "ruleStoreUnreadable";
142
+ type _GuardParkArmQueue = AssertAllKeysHandled<Exclude<ParkArmKeys, ParkArmQueueProjected | ParkArmQueueExcluded>>;
143
+ type _GuardParkArmQueueReverse = AssertAllKeysHandled<Exclude<ParkArmQueueProjected | ParkArmQueueExcluded, ParkArmKeys>>;
144
+ type _NoDoubleBookParkArmQueue = AssertAllKeysHandled<Extract<ParkArmQueueProjected, ParkArmQueueExcluded>>;
137
145
  type TaskSpecWired = "objective" | "principal" | "images" | "sessionId" | "requireExistingSession" | "oneShot" | "clientContext" | "resumeAt" | "resumeAtMode" | "model" | "compactionModel" | "thinking" | "systemPrompt" | "appendSystemPrompt" | "promptProvider" | "memory" | "suggestNextPrompts" | "restoreFiles" | "acceptPartialRestore" | "rewindFilesTo" | "tools" | "excludeTools" | "deferTools" | "toolMaterializeStrategy" | "promptProfile" | "toolPolicy" | "onAsk" | "onQuestion" | "hooks" | "durableApproval" | "resourceSuspend" | "resilience" | "finalVerification" | "checkpointStore" | "handsReadOnly" | "memoryPersistenceCapable" | "additionalDirectories" | "additionalReadDirectories" | "enablePlanMode" | "interactiveTools" | "enableFork" | "shellGate" | "writeFace" | "mcp" | "a2a" | "skills" | "backgroundScope" | "envFacts" | "limits" | "configOverrides" | "forwardSubagentEvents" | "retainSubagentSessions" | "retainBackgroundProcesses" | "outputSchema" | "outputRetries" | "compaction" | "attachments" | "selfOrchestration" | "getApiKeyAndHeaders" | "interactionPosture" | "agents" | "approverPosture" | "taskId" | "signal" | "preemptSignal" | "modelRole" | "enableBlockedReport" | "maxSuspends" | "autoModeRequested" | "skillListingBudgetFraction" | "skillListingMaxDescChars" | "excludeAllTools";
138
- type TaskSpecUnwired = "actor" | "roles" | "lspManager" | "tracer" | "readFace" | "readDenyPatterns" | "interactiveQuestionFallback" | "alwaysLoadTools" | "restoreGatedTools" | "deferSelfResolve" | "basePolicyForResumeEdit" | "lspDiagnostics" | "streamingToolExecution" | "toolResultThresholdChars";
146
+ type TaskSpecUnwired = "actor" | "roles" | "lspManager" | "tracer" | "readFace" | "readDenyPatterns" | "interactiveQuestionFallback" | "alwaysLoadTools" | "restoreGatedTools" | "deferSelfResolve" | "basePolicyForResumeEdit" | "lspDiagnostics" | "streamingToolExecution" | "toolResultThresholdChars" | "peerPresence" | "fileHistory";
139
147
  type _GuardTaskSpec = AssertAllKeysHandled<Exclude<keyof TaskSpec, TaskSpecWired | TaskSpecUnwired>>;
140
148
  type _GuardTaskSpecReverse = AssertAllKeysHandled<Exclude<TaskSpecWired | TaskSpecUnwired, keyof TaskSpec>>;
141
- type TaskResultForwarded = "taskId" | "runId" | "sessionId" | "terminal" | "model" | "effectiveReasoning" | "result" | "salvagedOutput" | "remoteEnvFailures" | "apiFailure" | "retryAfterMs" | "degraded" | "structuredOutput" | "rewindNotes" | "haltedOnUserRejection" | "haltedByUser" | "strandedHumanAnswers" | "pendingApproval" | "approvalWaitedMs" | "effectiveReadFace" | "effectiveReadDenyPatterns" | "effectiveWriteFace" | "effectiveMemoryScopes" | "stats" | "editedFiles" | "responseModel";
149
+ type TaskResultForwarded = "taskId" | "runId" | "sessionId" | "terminal" | "model" | "effectiveReasoning" | "result" | "salvagedOutput" | "remoteEnvFailures" | "apiFailure" | "retryAfterMs" | "degraded" | "structuredOutput" | "rewindNotes" | "haltedOnUserRejection" | "haltedByUser" | "strandedHumanAnswers" | "pendingApproval" | "approvalWaitedMs" | "effectiveReadFace" | "effectiveReadDenyPatterns" | "effectiveWriteFace" | "effectiveMemoryScopes" | "stats" | "editedFiles" | "responseModel" | "compaction";
142
150
  type TaskResultStripped = never;
143
151
  type _GuardTaskResult = AssertAllKeysHandled<Exclude<keyof TaskResult, TaskResultForwarded | TaskResultStripped>>;
144
152
  type _GuardTaskResultReverse = AssertAllKeysHandled<Exclude<TaskResultForwarded | TaskResultStripped, keyof TaskResult>>;
@@ -197,6 +205,13 @@ type McpListingIncompleteKeys = keyof NonNullable<McpReconnectResult["listingInc
197
205
  type McpListingIncompleteProjected = "reason" | "pages" | "budgetMs" | "error";
198
206
  type _GuardMcpListingIncomplete = AssertAllKeysHandled<Exclude<McpListingIncompleteKeys, McpListingIncompleteProjected>>;
199
207
  type _GuardMcpListingIncompleteReverse = AssertAllKeysHandled<Exclude<McpListingIncompleteProjected, McpListingIncompleteKeys>>;
208
+ type LspOptionsWired = "env" | "log";
209
+ type LspOptionsUnwired = "onNotice" | "servers" | "spawn" | "readText" | "resolveRoot" | "warmup" | "maxSessions" | "idleTtlMs" | "diagnostics";
210
+ type _GuardLspOptions = AssertAllKeysHandled<Exclude<keyof NodeLspManagerOptions, LspOptionsWired | LspOptionsUnwired>>;
211
+ type _GuardLspOptionsReverse = AssertAllKeysHandled<Exclude<LspOptionsWired | LspOptionsUnwired, keyof NodeLspManagerOptions>>;
212
+ type _NoDoubleBookLspOptions = AssertAllKeysHandled<Extract<LspOptionsWired, LspOptionsUnwired>>;
213
+ type RunnerDepsDroppedPendingS724 = "fileHistoryCapture";
214
+ type _GuardFacelessRunnerDepsReverse = AssertAllKeysHandled<Exclude<RunnerDepsDroppedPendingS724, keyof RunnerDeps>>;
200
215
  export type CoreKeysetGuards = [
201
216
  _GuardNotification,
202
217
  _GuardNotificationReverse,
@@ -259,6 +274,9 @@ export type CoreKeysetGuards = [
259
274
  _GuardPermissionTraceNonEmpty,
260
275
  _GuardInboxSummary,
261
276
  _GuardInboxSummaryReverse,
277
+ _GuardParkArmQueue,
278
+ _NoDoubleBookParkArmQueue,
279
+ _GuardParkArmQueueReverse,
262
280
  _GuardTaskSpec,
263
281
  _GuardTaskSpecReverse,
264
282
  _GuardTaskResult,
@@ -294,7 +312,11 @@ export type CoreKeysetGuards = [
294
312
  _GuardMcpListingIncomplete,
295
313
  _GuardMcpListingIncompleteReverse,
296
314
  _GuardWiringMcpLiveness,
297
- _GuardWiringMcpLivenessReverse
315
+ _GuardWiringMcpLivenessReverse,
316
+ _GuardLspOptions,
317
+ _NoDoubleBookLspOptions,
318
+ _GuardLspOptionsReverse,
319
+ _GuardFacelessRunnerDepsReverse
298
320
  ];
299
321
  export {};
300
322
  //# sourceMappingURL=core-keyset-guard.d.ts.map
@@ -16,8 +16,34 @@
16
16
  * 同文件 G1 e2e 的全码扇出夹具、以及契约文档附录 D.3 那张表(含**小节标题里的码数** —— 那道门本批
17
17
  * 刚立,立完就在下一次加码时自己咬住了)。
18
18
  */
19
- export declare const ENGINE_NOTICE_WIRE_CODES: readonly ["memory.session_polluted", "memory.harvest_quarantined", "memory.delegation_static_mark_waived", "memory.hold_opened", "memory.hold_released", "memory.hold_disposed", "task.turn_interrupted", "steering.parked_input_blocked", "task.user_steer_undrained", "task.user_followup_undrained", "memory.capture_opted_out", "memory.capture_optout_unpersisted", "task.halt_unconsumed", "task.late_approval", "config.durable_gate_unavailable", "peer.inbound_disposition", "peer.held_settled", "peer.idle_subscription", "classifier.denial_limit", "mcp.injection_dropped", "delegation.ask_unresolvable", "task.interrupt_unconsumed"];
19
+ export declare const ENGINE_NOTICE_WIRE_CODES: readonly ["memory.session_polluted", "memory.harvest_quarantined", "memory.delegation_static_mark_waived", "memory.hold_opened", "memory.hold_released", "memory.hold_disposed", "task.turn_interrupted", "steering.parked_input_blocked", "task.user_steer_undrained", "task.user_followup_undrained", "memory.capture_opted_out", "memory.capture_optout_unpersisted", "task.halt_unconsumed", "task.late_approval", "config.durable_gate_unavailable", "peer.inbound_disposition", "peer.held_settled", "peer.idle_subscription", "classifier.denial_limit", "mcp.injection_dropped", "delegation.ask_unresolvable", "task.interrupt_unconsumed", "instructions.source_changed"];
20
20
  export type EngineNoticeWireCode = (typeof ENGINE_NOTICE_WIRE_CODES)[number];
21
+ /**
22
+ * S-690 —— **本仓自铸**的通告码与它们的受众(码 / 铸点 / 受众三者的属主都是本仓)。
23
+ *
24
+ * 为什么可以有这一族:core 的 `EngineNotice.code` 刻意是开放 `string`,顶注逐字「a host forwarding its own notices
25
+ * through the same sink is a supported shape」—— 部署经同一只 `onNotice` 转发体投自己的通告,白名单 / 归属 / 消毒 /
26
+ * 隔离四件判据一个不绕。为什么受众写在这里:core 的 `NOTICE_AUDIENCE` 只收录 core 自己铸的码,对本仓的码
27
+ * `noticeAudienceOf` 只会读保守默认 `"operator"` —— 那是「不认识」,不是判决。顶注的「本仓不许自造第二份受众语义」
28
+ * 说的是 **core 的码**;本表只收 core 码册里**没有**的码,这里是它们唯一的一份受众语义。
29
+ *
30
+ * 🔴 机器门(`test/engine-notice-wire.test.ts` 的 server 自铸码属主门):① 本表的码与 core 运行期码册 `ENGINE_NOTICE_CODES`
31
+ * **零交集**(core 哪天铸了同名码 ⇒ 属主变成两个,当场红:销本表的行、改走 core 的受众与工厂);② 判 `"user"` 的码必须
32
+ * 入白名单;③ 白名单里的码,受众一律由「本表 ∪ core 注册表」给,判出来必须是 `"user"`。
33
+ */
34
+ export declare const SERVER_NOTICE_AUDIENCE: {
35
+ readonly "instructions.source_changed": "user";
36
+ /**
37
+ * O2(7.101.0 合并复审)—— boot 期探测到 ≤7.30.x 引擎留下的锁住的记忆层(唯一铸点 `src/memory-layer-legacy-lock.ts`,
38
+ * 调用点 = `boot/stores.ts` 的 file 记忆引擎装配段)。受众 **operator**:恢复动词是运维在数据根上执行一次 `chmod -R u+w`,
39
+ * 而且铸在 boot 期、没有任何会话 ⇒ 不上 wire、不进白名单,只走转发体的日志终点(`engine_notice` warn)。
40
+ * `detail: { layers, remedy }` —— 层名(挂载面的直接子目录名)与那一句补救;条目名 / 内容不进。
41
+ */
42
+ readonly "memory.layer_locked_legacy": "operator";
43
+ };
44
+ export type ServerNoticeCode = keyof typeof SERVER_NOTICE_AUDIENCE;
45
+ /** 该码是否本仓自铸(属主判据单点:门与投递面都读这一个)。 */
46
+ export declare function isServerNoticeCode(code: string): code is ServerNoticeCode;
21
47
  /** 白名单谓词(单点):路由与门都读这一个,不许第二处手抄码串。 */
22
48
  export declare function isEngineNoticeWireCode(code: string): code is EngineNoticeWireCode;
23
49
  /**
@@ -23,7 +23,15 @@ export const ENGINE_NOTICE_WIRE_CODES = [
23
23
  "mcp.injection_dropped",
24
24
  "delegation.ask_unresolvable",
25
25
  "task.interrupt_unconsumed",
26
+ "instructions.source_changed",
26
27
  ];
28
+ export const SERVER_NOTICE_AUDIENCE = {
29
+ "instructions.source_changed": "user",
30
+ "memory.layer_locked_legacy": "operator",
31
+ };
32
+ export function isServerNoticeCode(code) {
33
+ return Object.hasOwn(SERVER_NOTICE_AUDIENCE, code);
34
+ }
27
35
  const WIRE_CODES = new Set(ENGINE_NOTICE_WIRE_CODES);
28
36
  export function isEngineNoticeWireCode(code) {
29
37
  return WIRE_CODES.has(code);
@@ -1,7 +1,7 @@
1
1
  import {} from "@sema-agent/core";
2
2
  import { outwardPlane } from "../terminal.js";
3
3
  import { modelTextEventData, toolStartEventData, toolEndEventData, taskProgressEventData, taskNotificationEventData, compactedEventData, diagnosticsEventData, brainStatusEventData, steeringInjectedEventData, compactionOutcomeEventData, workspaceChangedEventData, wiringManifestEventData, toolRosterDeltaEventData, toolDisclosureEventData, humanInputEventData, turnEndEventData, contextUsageEventData } from "./project.js";
4
- import { NotifiedKeys, taskNotificationStreamKey } from "../orchestration/workflow-completion-inbox.js";
4
+ import { NotifiedKeys, taskNotificationStreamKeyOf } from "../orchestration/workflow-completion-inbox.js";
5
5
  import { isParkLedgerEventType } from "./ledger-events.js";
6
6
  import { isModelSegmentBoundary, reasoningDeltaBytes, reasoningSegmentEventId } from "./model-segment.js";
7
7
  export function createSerialLedgerAppend(appendEvent, startSeq) {
@@ -119,7 +119,7 @@ export function createLedgerSink(opts) {
119
119
  break;
120
120
  case "task_notification": {
121
121
  const tn = ev.notification;
122
- const tnKey = tn?.task_id ? taskNotificationStreamKey({ task_type: tn.task_type, task_id: tn.task_id, status: String(tn.status), seq: tn.seq }) : undefined;
122
+ const tnKey = taskNotificationStreamKeyOf(tn);
123
123
  const tnPrior = tnKey !== undefined ? keys.get(tnKey) : undefined;
124
124
  if (tnPrior === undefined || !(await tnPrior)) {
125
125
  const write = append("task_notification", taskNotificationEventData(ev));
@@ -422,6 +422,17 @@ export declare function taskNotificationEventData(ev: {
422
422
  * `kind` 过闭集(`trace/sema-provenance.ts` 单点,park 半场同一判据);`agentType` 本 mount 恒缺席(工具无
423
423
  * agent-type 席),在场即透传(fleet spawn 帧按 taskId 可 join 时上游给)。 */
424
424
  _sema_provenance?: SemaProvenanceInput;
425
+ /** core 7.30.0 [ref] S1 / #1034 / #1036(S-616):委派终态帧三枚 facet + 重供两键 —— 判据单点在
426
+ * `trace/task-notification-facets.ts`(与 park 半场同一只;入参按 `unknown` 收、判过形才成 typed)。 */
427
+ spawnName?: string;
428
+ handback?: string;
429
+ compaction?: {
430
+ passes: number;
431
+ breakerOpen: boolean;
432
+ kind?: string;
433
+ };
434
+ resupplied?: boolean;
435
+ attempt?: number;
425
436
  };
426
437
  eventId?: string;
427
438
  parentToolCallId?: string;
@@ -840,6 +851,8 @@ export declare function compactedEventData(ev: {
840
851
  * 的数、post 恒是结构和 + 固定提示词开销,两者的差混坐标,实测在普通 run 上是负数)。本仓的处置因此
841
852
  * 是**三条都逐字透传、一条都不合成**:谁要画上下文表就读 `freedTokens`,读不到就渲染 unknown。 */
842
853
  freedTokens?: number;
854
+ /** core 7.30.0 #1036(S-616):这一次压缩**摘要掉**了几条之前已交给模型的跨会话消息(在场 iff ≥1)。 */
855
+ summarizedCrossSessionMessages?: number;
843
856
  durationMs?: number;
844
857
  phaseDurations?: {
845
858
  prepareMs?: number;
@@ -1,10 +1,12 @@
1
1
  import { redactErrorMessage, redactModelText } from "../observability/run-terminal.js";
2
- import { MCP_DELIVERY_VERDICTS, MCP_LIVENESS_STATES, screenGateOutcome, ToolRoster, ToolRosterDelta, WRITE_PROTECTION_TARGET_VIEWS } from "@sema-agent/core";
2
+ import { MCP_DELIVERY_VERDICTS, MCP_LIVENESS_STATES, screenGateOutcome, TEAM_ROLES, ToolRoster, ToolRosterDelta, WRITE_PROTECTION_TARGET_VIEWS } from "@sema-agent/core";
3
3
  import { Value } from "typebox/value";
4
4
  import { recordFailOpen } from "../observability/fail-open.js";
5
5
  import { redactSecrets, redactDeep } from "./redact.js";
6
6
  import { injectionTierOrAbsent } from "./injection-tier.js";
7
7
  import { semaProvenanceOrAbsent } from "./sema-provenance.js";
8
+ import { resupplyFields, terminalFrameFacetFields } from "./task-notification-facets.js";
9
+ import { dropProjectedFace } from "./projection-drop.js";
8
10
  export async function appendModelUsageDelta(append, modelUsage, taskId) {
9
11
  const delta = modelUsage?.drain(taskId);
10
12
  if (delta) {
@@ -229,10 +231,6 @@ export function toolEndEventData(ev) {
229
231
  ...identityFields(ev),
230
232
  };
231
233
  }
232
- const dropProjectedFace = (what) => {
233
- recordFailOpen("server.trace.projection-input-malformed", what);
234
- return undefined;
235
- };
236
234
  export function toolProgressEventData(ev) {
237
235
  const id = typeof ev.toolCallId === "string" && ev.toolCallId !== "" ? ev.toolCallId : undefined;
238
236
  const name = typeof ev.toolName === "string" && ev.toolName !== "" ? ev.toolName : undefined;
@@ -326,6 +324,8 @@ export function taskNotificationEventData(ev) {
326
324
  const p = semaProvenanceOrAbsent(n._sema_provenance);
327
325
  return p !== undefined ? { _sema_provenance: p } : {};
328
326
  })(),
327
+ ...terminalFrameFacetFields(n),
328
+ ...resupplyFields(n),
329
329
  ...(() => {
330
330
  const tier = injectionTierOrAbsent(ev.priority);
331
331
  return tier !== undefined ? { priority: tier } : {};
@@ -535,9 +535,17 @@ export function wiringManifestEventData(ev) {
535
535
  return { targetView };
536
536
  })();
537
537
  const mcp = projectWiringManifestMcpRows(m.mcp);
538
+ const teamRole = (() => {
539
+ if (m.teamRole === undefined)
540
+ return undefined;
541
+ if (!TEAM_ROLES.includes(m.teamRole))
542
+ return dropProjectedFace("wiring_manifest.teamRole is outside core TEAM_ROLES");
543
+ return m.teamRole;
544
+ })();
538
545
  return {
539
546
  ...(num(m.schemaVersion) !== undefined ? { schemaVersion: m.schemaVersion } : {}),
540
547
  ...(str(legIn.kind) !== undefined ? { leg: { kind: legIn.kind } } : {}),
548
+ ...(teamRole !== undefined ? { teamRole } : {}),
541
549
  ...(ask !== undefined ? { ask } : {}),
542
550
  ...(question !== undefined ? { question } : {}),
543
551
  ...(str(interactionIn.posture) !== undefined ? { interaction: { posture: interactionIn.posture } } : {}),
@@ -676,6 +684,15 @@ export function compactedEventData(ev) {
676
684
  ...(num(ev.tokensAfter) !== undefined ? { tokensAfter: num(ev.tokensAfter) } : {}),
677
685
  ...(num(ev.triggerTokensBefore) !== undefined ? { triggerTokensBefore: num(ev.triggerTokensBefore) } : {}),
678
686
  ...(num(ev.freedTokens) !== undefined ? { freedTokens: num(ev.freedTokens) } : {}),
687
+ ...(() => {
688
+ const v = ev.summarizedCrossSessionMessages;
689
+ if (v === undefined)
690
+ return {};
691
+ if (Number.isInteger(v) && v >= 1)
692
+ return { summarizedCrossSessionMessages: v };
693
+ dropProjectedFace("compacted.summarizedCrossSessionMessages is not an integer >= 1");
694
+ return {};
695
+ })(),
679
696
  ...(num(ev.durationMs) !== undefined ? { durationMs: num(ev.durationMs) } : {}),
680
697
  ...(phaseDurations !== undefined ? { phaseDurations } : {}),
681
698
  };
@@ -0,0 +1,2 @@
1
+ export declare function dropProjectedFace(what: string): undefined;
2
+ //# sourceMappingURL=projection-drop.d.ts.map