@sema-agent/server 7.142.0 → 7.143.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 (258) hide show
  1. package/MIGRATION.md +442 -0
  2. package/USAGE.md +41 -24
  3. package/dist/adoption/plan.js +22 -0
  4. package/dist/approval-ask-audit-store.d.ts +7 -5
  5. package/dist/approval-ask-audit-store.js +12 -6
  6. package/dist/approval-ask-machine.d.ts +15 -7
  7. package/dist/approval-ask-machine.js +10 -4
  8. package/dist/approval-card.d.ts +124 -25
  9. package/dist/approval-card.js +103 -23
  10. package/dist/approval-queue-kind.d.ts +27 -0
  11. package/dist/approval-queue-kind.js +6 -0
  12. package/dist/approval-reconciler.d.ts +8 -8
  13. package/dist/approval-reconciler.js +3 -3
  14. package/dist/audit.js +4 -2
  15. package/dist/boot/budget-tracing.d.ts +1 -1
  16. package/dist/boot/governance-seams.d.ts +26 -41
  17. package/dist/boot/governance-seams.js +61 -28
  18. package/dist/boot/late-binds.d.ts +7 -4
  19. package/dist/boot/late-binds.js +3 -1
  20. package/dist/boot/leader.js +2 -4
  21. package/dist/boot/memory-consolidation.d.ts +0 -8
  22. package/dist/boot/memory-consolidation.js +3 -3
  23. package/dist/boot/reapers.js +5 -4
  24. package/dist/boot/resolve-spec.d.ts +1 -1
  25. package/dist/boot/resolve-spec.js +8 -8
  26. package/dist/boot/runner-deps.d.ts +10 -7
  27. package/dist/boot/runner-deps.js +5 -17
  28. package/dist/boot/shutdown.d.ts +9 -0
  29. package/dist/boot/shutdown.js +16 -2
  30. package/dist/boot/stage-02-web-admin-pull.js +3 -0
  31. package/dist/boot/stage-03-store-backend.d.ts +4 -1
  32. package/dist/boot/stage-03-store-backend.js +1 -2
  33. package/dist/boot/stage-04-stores.d.ts +5 -2
  34. package/dist/boot/stage-04-stores.js +3 -5
  35. package/dist/boot/stage-05-execution-env.d.ts +5 -2
  36. package/dist/boot/stage-06-runners.d.ts +5 -2
  37. package/dist/boot/stage-06-runners.js +1 -3
  38. package/dist/boot/stage-07-capability-layer.d.ts +5 -2
  39. package/dist/boot/stage-07-capability-layer.js +5 -1
  40. package/dist/boot/stage-08-reapers.d.ts +5 -2
  41. package/dist/boot/stage-09-leader.d.ts +5 -2
  42. package/dist/boot/stage-10-http-server.d.ts +5 -2
  43. package/dist/boot/stage-10-http-server.js +9 -1
  44. package/dist/boot/stores.d.ts +5 -2
  45. package/dist/boot/stores.js +7 -7
  46. package/dist/boot/web-admin.d.ts +5 -1
  47. package/dist/boot/web-admin.js +31 -19
  48. package/dist/capabilities/hands-lane.d.ts +9 -39
  49. package/dist/capabilities/hands-lane.js +8 -13
  50. package/dist/capabilities/mcp-probe.d.ts +3 -2
  51. package/dist/capabilities/scenarios.d.ts +11 -1
  52. package/dist/capabilities/scenarios.js +1 -0
  53. package/dist/capabilities/skills.d.ts +1 -17
  54. package/dist/capabilities/skills.js +0 -8
  55. package/dist/capability-facts.d.ts +8 -8
  56. package/dist/capability-facts.js +4 -4
  57. package/dist/card-void.d.ts +6 -9
  58. package/dist/config-catalog.js +1 -1
  59. package/dist/config-dir-trust.js +58 -29
  60. package/dist/config-invariants.d.ts +7 -1
  61. package/dist/config-invariants.js +7 -0
  62. package/dist/config-provider.d.ts +10 -0
  63. package/dist/config-provider.js +15 -4
  64. package/dist/config-root-migration.d.ts +13 -0
  65. package/dist/config-root-migration.js +99 -0
  66. package/dist/config-types.d.ts +5 -5
  67. package/dist/config.d.ts +4 -3
  68. package/dist/config.js +1 -1
  69. package/dist/delegated-session-owner.d.ts +5 -3
  70. package/dist/delegated-session-owner.js +1 -1
  71. package/dist/device-ws-protocol.d.ts +1 -0
  72. package/dist/device-ws-protocol.js +2 -1
  73. package/dist/env-model-table-gate.d.ts +53 -0
  74. package/dist/env-model-table-gate.js +53 -0
  75. package/dist/fleet/fleet-bus.d.ts +28 -52
  76. package/dist/fleet/fleet-bus.js +35 -100
  77. package/dist/fleet/fleet-reconciler.d.ts +4 -4
  78. package/dist/fleet/fleet-reconciler.js +1 -1
  79. package/dist/fleet/subagent-tail-bus.d.ts +14 -11
  80. package/dist/fleet/subagent-tail-bus.js +15 -28
  81. package/dist/hooks/hook-runner.js +28 -7
  82. package/dist/host-decision.d.ts +54 -20
  83. package/dist/host-decision.js +42 -14
  84. package/dist/http/admission.d.ts +1 -1
  85. package/dist/http/admission.js +7 -3
  86. package/dist/http/approval-sweeps.js +11 -8
  87. package/dist/http/caller-trust.d.ts +1 -1
  88. package/dist/http/durable-run-leg.js +2 -2
  89. package/dist/http/engine-refusal.d.ts +16 -0
  90. package/dist/http/engine-refusal.js +17 -0
  91. package/dist/http/memory-compliance-failure-reply.d.ts +1 -3
  92. package/dist/http/memory-compliance-failure-reply.js +4 -6
  93. package/dist/http/owner-access.d.ts +11 -0
  94. package/dist/http/owner-access.js +13 -0
  95. package/dist/http/resume-legs.d.ts +21 -7
  96. package/dist/http/resume-legs.js +71 -49
  97. package/dist/http/resume-restatement.d.ts +39 -0
  98. package/dist/http/resume-restatement.js +25 -0
  99. package/dist/http/route-ctx.d.ts +35 -10
  100. package/dist/http/route-ctx.js +1 -1
  101. package/dist/http/routes/approvals-assistant.d.ts +9 -1
  102. package/dist/http/routes/approvals-assistant.js +142 -31
  103. package/dist/http/routes/capabilities.js +11 -2
  104. package/dist/http/routes/memory-policy.d.ts +2 -2
  105. package/dist/http/routes/memory-policy.js +1 -1
  106. package/dist/http/routes/notify-wake.js +23 -25
  107. package/dist/http/routes/rules.js +48 -13
  108. package/dist/http/routes/runs.js +9 -8
  109. package/dist/http/routes/session-memory-sync.d.ts +34 -6
  110. package/dist/http/routes/session-memory-sync.js +27 -10
  111. package/dist/http/routes/session-sync.js +13 -20
  112. package/dist/http/routes/sessions.d.ts +2 -2
  113. package/dist/http/routes/sessions.js +22 -23
  114. package/dist/http/routes/tasks.js +27 -49
  115. package/dist/http/server.d.ts +13 -7
  116. package/dist/http/server.js +2 -2
  117. package/dist/http/stored-body-replay.js +15 -4
  118. package/dist/http/wire-types.d.ts +17 -7
  119. package/dist/leader/leader.js +5 -2
  120. package/dist/leader/wire.d.ts +7 -1
  121. package/dist/leader/wire.js +6 -5
  122. package/dist/leg-facts.d.ts +37 -0
  123. package/dist/leg-facts.js +15 -0
  124. package/dist/memory-bundle-engine.d.ts +1 -4
  125. package/dist/memory-bundle-engine.js +2 -2
  126. package/dist/memory-operator-faces.d.ts +30 -66
  127. package/dist/memory-operator-faces.js +17 -88
  128. package/dist/memory-scope-stores.d.ts +38 -40
  129. package/dist/memory-scope-stores.js +51 -36
  130. package/dist/memory-scope.d.ts +0 -9
  131. package/dist/memory-scope.js +0 -9
  132. package/dist/memory-sync-client.js +14 -3
  133. package/dist/memory-sync.d.ts +20 -4
  134. package/dist/memory-sync.js +23 -4
  135. package/dist/model-plane-face.d.ts +13 -7
  136. package/dist/model-plane-face.js +2 -1
  137. package/dist/observability/fail-open.d.ts +20 -4
  138. package/dist/observability/fail-open.js +17 -4
  139. package/dist/observability/run-terminal.js +4 -0
  140. package/dist/orchestration/workflow-completion-inbox.d.ts +5 -19
  141. package/dist/orchestration/workflow-completion-inbox.js +0 -12
  142. package/dist/orchestration/workflow-notify-journal.d.ts +3 -2
  143. package/dist/orchestration/workflow-park-decision.d.ts +8 -3
  144. package/dist/orchestration/workflow-park-decision.js +5 -3
  145. package/dist/org-delivery-gate.d.ts +11 -0
  146. package/dist/org-delivery-gate.js +19 -1
  147. package/dist/parked-decide.d.ts +13 -9
  148. package/dist/parked-decide.js +7 -7
  149. package/dist/parked-revive-surface.js +1 -1
  150. package/dist/peer-inbound-face.d.ts +127 -0
  151. package/dist/peer-inbound-face.js +158 -0
  152. package/dist/permission-rule-list.d.ts +13 -9
  153. package/dist/permission-rule-list.js +7 -3
  154. package/dist/plugins/approval-ask-store-file.js +18 -6
  155. package/dist/plugins/approval-ask-store-memory.js +8 -6
  156. package/dist/plugins/approval-ask-store-sql.d.ts +28 -13
  157. package/dist/plugins/approval-ask-store-sql.js +30 -11
  158. package/dist/plugins/checkpoint-store-sql.d.ts +4 -4
  159. package/dist/plugins/checkpoint-store-sql.js +9 -7
  160. package/dist/plugins/file-history-store-sql.d.ts +24 -18
  161. package/dist/plugins/file-history-store-sql.js +24 -19
  162. package/dist/plugins/file-run-store.d.ts +29 -0
  163. package/dist/plugins/file-run-store.js +97 -8
  164. package/dist/plugins/imported-layer-root-retirement.d.ts +82 -0
  165. package/dist/plugins/imported-layer-root-retirement.js +96 -0
  166. package/dist/plugins/local-checkpoint-store.d.ts +1 -1
  167. package/dist/plugins/local-checkpoint-store.js +2 -2
  168. package/dist/plugins/local-session-store.d.ts +6 -2
  169. package/dist/plugins/local-session-store.js +8 -2
  170. package/dist/plugins/mailbox-recipient-lifecycle.d.ts +9 -31
  171. package/dist/plugins/mailbox-recipient-lifecycle.js +4 -0
  172. package/dist/plugins/mailbox-store-sql.d.ts +12 -8
  173. package/dist/plugins/mailbox-store-sql.js +76 -7
  174. package/dist/plugins/memory-sync-store-pg.d.ts +15 -0
  175. package/dist/plugins/memory-sync-store-pg.js +21 -0
  176. package/dist/plugins/memory-sync-store-tidb.d.ts +2 -0
  177. package/dist/plugins/memory-sync-store-tidb.js +13 -1
  178. package/dist/plugins/one-time-migrations.d.ts +32 -2
  179. package/dist/plugins/one-time-migrations.js +106 -3
  180. package/dist/plugins/org-delivery-quota-store.d.ts +1 -1
  181. package/dist/plugins/org-delivery-quota-store.js +2 -12
  182. package/dist/plugins/park-commit-watch.d.ts +1 -1
  183. package/dist/plugins/permission-rule-store-file.d.ts +10 -1
  184. package/dist/plugins/permission-rule-store-file.js +15 -2
  185. package/dist/plugins/permission-rule-store-sql.d.ts +31 -1
  186. package/dist/plugins/permission-rule-store-sql.js +35 -2
  187. package/dist/plugins/remote-env-device.d.ts +8 -11
  188. package/dist/plugins/remote-env-device.js +6 -3
  189. package/dist/plugins/remote-env-k8s.d.ts +37 -24
  190. package/dist/plugins/remote-env-k8s.js +50 -17
  191. package/dist/plugins/retention-store-sql.d.ts +2 -2
  192. package/dist/plugins/sandbox-read-floor.d.ts +5 -7
  193. package/dist/plugins/sandbox-read-floor.js +2 -7
  194. package/dist/plugins/sql-errors.d.ts +10 -1
  195. package/dist/plugins/sql-errors.js +13 -0
  196. package/dist/plugins/store-backend.d.ts +23 -28
  197. package/dist/plugins/store-backend.js +9 -1
  198. package/dist/plugins/store-contracts.d.ts +29 -5
  199. package/dist/plugins/store-contracts.js +4 -1
  200. package/dist/plugins/task-attachment-store.d.ts +2 -2
  201. package/dist/plugins/task-attachment-store.js +37 -52
  202. package/dist/plugins/tool-result-store-sql.d.ts +9 -8
  203. package/dist/plugins/tool-result-store-sql.js +23 -2
  204. package/dist/plugins/wiring-section-codec.d.ts +32 -0
  205. package/dist/plugins/wiring-section-codec.js +55 -0
  206. package/dist/plugins/workspace-fill.d.ts +1 -1
  207. package/dist/rule-entry-list.d.ts +3 -2
  208. package/dist/rule-entry-list.js +5 -1
  209. package/dist/rules-consent.d.ts +28 -5
  210. package/dist/rules-consent.js +40 -28
  211. package/dist/run-local.js +9 -5
  212. package/dist/runs.d.ts +12 -8
  213. package/dist/runs.js +6 -7
  214. package/dist/runtime-governance.d.ts +1 -1
  215. package/dist/server-secret-env.d.ts +4 -0
  216. package/dist/session-error-wire.d.ts +12 -0
  217. package/dist/session-error-wire.js +6 -0
  218. package/dist/session-incarnation.d.ts +1 -1
  219. package/dist/session-notify-door.d.ts +106 -0
  220. package/dist/session-notify-door.js +93 -0
  221. package/dist/session-sync.d.ts +9 -14
  222. package/dist/session-sync.js +5 -5
  223. package/dist/session-workspace.d.ts +3 -13
  224. package/dist/session-workspace.js +0 -9
  225. package/dist/standing-answer-grant.d.ts +5 -0
  226. package/dist/standing-answer-grant.js +10 -0
  227. package/dist/stores-sql/session-policy-store-sql.d.ts +4 -2
  228. package/dist/stores-sql/session-policy-store-sql.js +3 -13
  229. package/dist/task-mcp.d.ts +1 -1
  230. package/dist/task-settings.d.ts +11 -8
  231. package/dist/task-settings.js +31 -11
  232. package/dist/tool-approval.d.ts +52 -34
  233. package/dist/tool-approval.js +66 -57
  234. package/dist/trace/core-keyset-guard.d.ts +11 -8
  235. package/dist/trace/engine-notice-wire.d.ts +1 -1
  236. package/dist/trace/engine-notice-wire.js +3 -1
  237. package/dist/trace/exit-seat.d.ts +7 -1
  238. package/dist/trace/exit-seat.js +1 -0
  239. package/dist/trace/ledger-sink.d.ts +2 -2
  240. package/dist/trace/model-segment.d.ts +55 -25
  241. package/dist/trace/model-segment.js +101 -33
  242. package/dist/trace/project.d.ts +7 -4
  243. package/dist/trace/project.js +6 -0
  244. package/dist/trace/redact.d.ts +61 -9
  245. package/dist/trace/redact.js +1490 -468
  246. package/dist/trace/self-minted-wire.d.ts +510 -38
  247. package/dist/trace/self-minted-wire.js +165 -17
  248. package/dist/trace/wire-projection-faces.d.ts +1 -1
  249. package/dist/trace/wire-projection-faces.js +1 -1
  250. package/dist/verifier-pause-card.d.ts +33 -5
  251. package/dist/verifier-pause-card.js +15 -10
  252. package/dist/web-admin-client/apply-effective.d.ts +12 -16
  253. package/dist/web-admin-client/apply-effective.js +90 -10
  254. package/dist/workspace-space.d.ts +19 -3
  255. package/dist/workspace-space.js +8 -4
  256. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +3 -1
  257. package/package.json +3 -2
  258. package/wire-changes/server@7.143.0.json +1167 -0
@@ -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, PendingAction, TaskEvent, TraceEvent, MailboxMessage, MailboxAppendMessage, TaskSpec, TaskResult, SideQueryResult, WiringManifest, PermissionModeReceipt, GateOutcome, GateDisposition, Settlement, PausedCause, TerminalCause, CheckpointGate, CheckpointToken, VerificationResult, CascadeRunResult, WorkflowRun, ReadRootGrantCandidate, McpReconnectResult, McpServerStatus, NodeLspManagerOptions, RunnerDeps, SessionMemoryStatus, PromptManifestIdentity, PromptIdentityLayers, PromptManifestSelectors, PromptManifestSection, PromptChainLink, SupersededOverlay, ToolDisclosureManifest } from "@sema-agent/core";
20
+ import type { TaskNotificationPayload, SemaProvenance, BackgroundChildEvent, RosterEntry, AskRequest, CheckpointSummary, PendingAction, TaskEvent, TraceEvent, MailboxMessage, MailboxAppendMessage, TaskSpec, TaskResult, SideQueryResult, WiringManifest, PermissionModeReceipt, GateOutcome, GateDisposition, Settlement, PausedCause, TerminalCause, CheckpointGate, CheckpointToken, VerificationResult, CascadeRunResult, WorkflowRun, ReadRootGrantCandidate, McpReconnectResult, McpServerStatus, NodeLspManagerOptions, RunnerDeps, SessionMemoryStatus, DeploymentSessionMemoryStatus, PromptManifestIdentity, PromptIdentityLayers, PromptManifestSelectors, PromptManifestSection, PromptChainLink, SupersededOverlay, ToolDisclosureManifest } from "@sema-agent/core";
21
21
  import type { ToolApprovalFrame } from "../tool-approval.js";
22
22
  import type { TableCoreKeys } from "./projection-table.js";
23
23
  import type { PauseSeat } from "../terminal.js";
@@ -45,7 +45,7 @@ type _GuardBgNotifReverse = AssertAllKeysHandled<Exclude<BgNotifProjected | BgNo
45
45
  type RosterProjected = "name" | "agentId" | "sessionId" | "toolUseId" | "owner" | "scope" | "sessionScoped" | "rootSessionId" | "model" | "modelFallback" | "createdAt";
46
46
  type _GuardRoster = AssertAllKeysHandled<Exclude<keyof RosterEntry, RosterProjected>>;
47
47
  type _GuardRosterReverse = AssertAllKeysHandled<Exclude<RosterProjected, keyof RosterEntry>>;
48
- type AskProjected = "toolName" | "toolCallId" | "args" | "message" | "sourceTaskId" | "fromSubagent" | "sourceAgentName" | "delegation" | "ruleOffers" | "ruleOffersAbsence" | "allowRuleShadowed" | "probeCause" | "probeReason" | "ruleEvidence" | "requiresRealApproval" | "denialLimitFallback" | "origin" | "readRootCandidate" | "mandated" | "mandate" | "askClass" | "classes" | "boundGrantDigest" | "boundGrants" | "classifierNoRuling" | "approvalCard" | "boundGrantAbsence" | "requestedBy";
48
+ type AskProjected = "toolName" | "toolCallId" | "args" | "message" | "sourceSessionId" | "agentId" | "askReasons" | "fromSubagent" | "sourceAgentName" | "delegation" | "ruleOffers" | "ruleOffersAbsence" | "allowRuleShadowed" | "probeCause" | "probeReason" | "ruleEvidence" | "requiresRealApproval" | "denialLimitFallback" | "origin" | "readRootCandidate" | "mandated" | "mandate" | "askClass" | "classes" | "boundGrantDigest" | "boundGrants" | "classifierNoRuling" | "approvalCard" | "boundGrantAbsence" | "requestedBy";
49
49
  type AskExcluded = "preview" | "principal" | "riskAxes" | "boundInputHash" | "isDelegatedChild" | "hasBidiControls" | "previewWithheld" | "execCwd" | "authoredBy" | "editRewritten";
50
50
  type _GuardAsk = AssertAllKeysHandled<Exclude<keyof AskRequest, AskProjected | AskExcluded>>;
51
51
  type _GuardAskReverse = AssertAllKeysHandled<Exclude<AskProjected | AskExcluded, keyof AskRequest>>;
@@ -218,7 +218,7 @@ type _GuardPromptManifestKeysNonEmpty = AssertAllKeysHandled<{
218
218
  [L in keyof ManifestCore]: [ManifestDeclared<ManifestFaces, L>] extends [never] ? L : never;
219
219
  }[keyof ManifestCore]>;
220
220
  type SummaryProjected = "sessionId" | "scope" | "createdAt" | "gateKind" | "severity" | "spentMicroUsd" | "deadline" | "hasBidiControls" | "requiresRealApproval" | "denialLimitFallback" | "origin" | "mandated" | "mandate" | "boundGrantDigest" | "boundGrants" | "classifierNoRuling" | "boundGrantAbsence" | "checkpointId";
221
- type SummaryExcluded = "token" | "toolInput" | "toolName" | "toolCallId" | "preview" | "principal" | "sourceTaskId" | "approvalCard" | "restoreMode" | "previewWithheld" | "authoredBy" | "runOrigin" | "requestedByCallId";
221
+ type SummaryExcluded = "token" | "toolInput" | "toolName" | "toolCallId" | "preview" | "principal" | "sourceSessionId" | "agentId" | "approvalCard" | "restoreMode" | "previewWithheld" | "authoredBy" | "runOrigin" | "requestedByCallId";
222
222
  type _GuardInboxSummary = AssertAllKeysHandled<Exclude<keyof CheckpointSummary, SummaryProjected | SummaryExcluded>>;
223
223
  type _GuardInboxSummaryReverse = AssertAllKeysHandled<Exclude<SummaryProjected | SummaryExcluded, keyof CheckpointSummary>>;
224
224
  type ParkArmKeys = keyof Extract<PendingAction, {
@@ -234,7 +234,7 @@ type TaskSpecWired = "objective" | "principal" | "images" | "sessionId" | "requi
234
234
  export type TaskSpecUnwired = "actor" | "roles" | "lspManager" | "tracer" | "readFace" | "interactiveQuestionFallback" | "alwaysLoadTools" | "restoreGatedTools" | "deferSelfResolve" | "basePolicyForResumeEdit" | "lspDiagnostics" | "streamingToolExecution" | "toolResultThresholdChars" | "peerPresence" | "modelRole" | "enableBlockedReport" | "promptForm" | "advisorModel" | "runOrigin" | "planFile" | "classifyAllShell";
235
235
  type _GuardTaskSpec = AssertAllKeysHandled<Exclude<keyof TaskSpec, TaskSpecWired | TaskSpecUnwired>>;
236
236
  type _GuardTaskSpecReverse = AssertAllKeysHandled<Exclude<TaskSpecWired | TaskSpecUnwired, keyof TaskSpec>>;
237
- 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" | "resultEntryIds";
237
+ 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" | "resultEntryIds" | "placementRoot";
238
238
  type TaskResultStripped = never;
239
239
  type _GuardTaskResult = AssertAllKeysHandled<Exclude<keyof TaskResult, TaskResultForwarded | TaskResultStripped>>;
240
240
  type _GuardTaskResultReverse = AssertAllKeysHandled<Exclude<TaskResultForwarded | TaskResultStripped, keyof TaskResult>>;
@@ -245,7 +245,7 @@ type _GuardPausedCauseReverse = AssertAllKeysHandled<Exclude<PausedCauseForwarde
245
245
  type NonPausedTerminalKeys = Exclude<TerminalCause, {
246
246
  kind: "paused";
247
247
  }> extends infer C ? (C extends object ? keyof C : never) : never;
248
- type TerminalArmForwarded = "kind" | "code" | "message" | "nestedPause" | "detail" | "reason";
248
+ type TerminalArmForwarded = "kind" | "code" | "message" | "nestedPause" | "detail" | "reason" | "unstartedRow";
249
249
  type TerminalArmStripped = never;
250
250
  type _GuardTerminalArm = AssertAllKeysHandled<Exclude<NonPausedTerminalKeys, TerminalArmForwarded | TerminalArmStripped>>;
251
251
  type _GuardTerminalArmReverse = AssertAllKeysHandled<Exclude<TerminalArmForwarded | TerminalArmStripped, NonPausedTerminalKeys>>;
@@ -433,10 +433,12 @@ type _CoverWordsCheck = AssertAllKeysHandled<Exclude<CoveringWords<{
433
433
  }, _CoverProbeWords>> | Exclude<"text[]" | "tree", CoveringWords<readonly string[] | undefined, _CoverProbeWords>> | Extract<"verbatim" | "text[]", CoveringWords<{
434
434
  note: string;
435
435
  }[], _CoverProbeWords>>>;
436
+ type SessionMemoryStatusSourceKey = keyof SessionMemoryStatus | keyof DeploymentSessionMemoryStatus;
436
437
  type SessionMemoryStatusProjected = "captureOptedOut" | "committedCount" | "foldedCount" | "optOutSource" | "lastCaptureAt";
437
- type SessionMemoryStatusExcluded = "writeHome";
438
- type _GuardSessionMemoryStatus = AssertAllKeysHandled<Exclude<keyof SessionMemoryStatus, SessionMemoryStatusProjected | SessionMemoryStatusExcluded>>;
439
- type _GuardSessionMemoryStatusReverse = AssertAllKeysHandled<Exclude<SessionMemoryStatusProjected | SessionMemoryStatusExcluded, keyof SessionMemoryStatus>>;
438
+ type SessionMemoryStatusExcluded = "writeHome" | "planes";
439
+ type _GuardSessionMemoryStatus = AssertAllKeysHandled<Exclude<SessionMemoryStatusSourceKey, SessionMemoryStatusProjected | SessionMemoryStatusExcluded>>;
440
+ type _GuardSessionMemoryStatusReverse = AssertAllKeysHandled<Exclude<SessionMemoryStatusProjected | SessionMemoryStatusExcluded, SessionMemoryStatusSourceKey>>;
441
+ type _GuardSessionMemoryStatusDeploymentReverse = AssertAllKeysHandled<Exclude<SessionMemoryStatusProjected, keyof DeploymentSessionMemoryStatus>>;
440
442
  type SideQueryResultForwarded = "text" | "toolCalls" | "model" | "servedModel" | "degraded" | "responseModel" | "usage" | "usageMissing" | "stopReason" | "errorMessage" | "errorCode" | "errorKind" | "inputTooLongRuledOut" | "account";
441
443
  type SideQueryResultStripped = never;
442
444
  type _GuardSideQueryResult = AssertAllKeysHandled<Exclude<keyof SideQueryResult, SideQueryResultForwarded | SideQueryResultStripped>>;
@@ -525,6 +527,7 @@ export type CoreKeysetGuards = [
525
527
  _GuardTerminalArmReverse,
526
528
  _GuardSessionMemoryStatus,
527
529
  _GuardSessionMemoryStatusReverse,
530
+ _GuardSessionMemoryStatusDeploymentReverse,
528
531
  _GuardNotificationReverse,
529
532
  _GuardProvenance,
530
533
  _GuardProvenanceReverse,
@@ -16,7 +16,7 @@
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", "approval.read_root_granted", "approval.read_root_grant_rejected", "instructions.source_changed", "session.records_skipped", "server.model_unreachable", "permission.entry_mode_clamped", "classifier.unavailable", "config.permission_mode_unavailable", "permission.ask_released_by_mode", "permission.read_deny_unapplied", "permission.rule_store_unavailable", "permission.rule_row_dropped", "permission.allowed_tool_not_granted", "task.tool_turn_incomplete", "route.finish_reason_unknown", "route.stream_end_ignored", "route.credential_origin_mismatch", "mods.registration_refused", "config.session_rules_earlier_grammar", "advisor.not_attached", "inherit_env.names_withheld", "model.capability_downgraded", "context.image_removed", "mcp.authorization_required"];
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", "approval.read_root_granted", "approval.read_root_grant_rejected", "instructions.source_changed", "session.records_skipped", "server.model_unreachable", "permission.entry_mode_clamped", "classifier.unavailable", "config.permission_mode_unavailable", "permission.ask_released_by_mode", "permission.read_deny_unapplied", "permission.rule_store_unavailable", "permission.rule_row_dropped", "permission.allowed_tool_not_granted", "task.tool_turn_incomplete", "route.finish_reason_unknown", "route.stream_end_ignored", "route.credential_origin_mismatch", "mods.registration_refused", "config.session_rules_earlier_grammar", "advisor.not_attached", "inherit_env.names_withheld", "model.capability_downgraded", "context.media_removed", "mcp.authorization_required", "classifier.seat_rate_limited", "context.tool_schema_removed"];
20
20
  export type EngineNoticeWireCode = (typeof ENGINE_NOTICE_WIRE_CODES)[number];
21
21
  /** 白名单谓词(单点):路由与门都读这一个,不许第二处手抄码串。 */
22
22
  export declare function isEngineNoticeWireCode(code: string): code is EngineNoticeWireCode;
@@ -47,8 +47,10 @@ export const ENGINE_NOTICE_WIRE_CODES = [
47
47
  "advisor.not_attached",
48
48
  "inherit_env.names_withheld",
49
49
  "model.capability_downgraded",
50
- "context.image_removed",
50
+ "context.media_removed",
51
51
  "mcp.authorization_required",
52
+ "classifier.seat_rate_limited",
53
+ "context.tool_schema_removed",
52
54
  ];
53
55
  const WIRE_CODES = new Set(ENGINE_NOTICE_WIRE_CODES);
54
56
  export function isEngineNoticeWireCode(code) {
@@ -19,12 +19,15 @@ export type SafeText = string & {
19
19
  * 的 `reason` / `status.error` / `listingIncomplete.error`;引擎铸点已抹过请求回显与同主体词表(记号 `[redacted]`),这一行读同一个脱敏器(引擎记号 = 已抹的值,
20
20
  * 引擎放过的形照抹);界 200 码元,原样出门(JSON 串值,消费方自己渲染)。
21
21
  * · `peer-declaration-sentence`:引擎判官对一份 MCP / A2A 对端声明说的句子(`toolAxes` / `toolFaces` / `sessionMetaKey` / 启动界的原因句,逐字带着声明方写的工具名、键名),
22
- * 进请求面 400 拒句、续跑重放的丢弃行与重读行、名册的跳过行 —— 读者是调用方与运维,句子已是一行;不设界(拒句要说全判官的话),原样出门。
22
+ * 进请求面 400 拒句、续跑重放的丢弃行与重读行、名册的跳过行,以及能力读面 `GET /v1/capabilities/mcp` 上引擎整集拒(两台撞同一工具命名空间前缀)的拒句 —— 读者是调用方与运维,句子已是一行;不设界(拒句要说全判官的话),原样出门。
23
23
  * · `inbox-diagnostics-text`:`workflow_complete` 补投帧上的诊断教学文本(`diagnostics`)—— 引擎在 workflow 完成通知上铸的「读哪里 / 怎么续跑」文本,入会话收件时洗一次,
24
24
  * 店里(`payload` 列)只放出门安全的字节,drain 逐字带出。洗法与 live `task_notification.diagnostics` 同一个 scope(`free-text`);
25
25
  * 这一行比 `trace-frame-text` 多一条界(4000 码元,与同族条目的文本位同额),原样出门:`payload` 列是 TEXT,无界的文本一旦超过列宽,入箱写整条失败、补投连摘要一起丢(完成已被记作送达),所以先有界。
26
26
  * · `boot-refusal-line`:起服拒启的一行拒句 —— 部署座装配的拒句(`mods-declaration.ts`:声明的席名、包名、读包者 / 引擎准入交回的话);它经进程级 fatal 出口原样多行进 stderr,
27
27
  * 所以必须在这里先压成一行(一句拒因里的换行会在运维的终端里伪造出第二条日志行);界 512 码元(含截断记号)。
28
+ * · `peer-inbound-card-text`:运维审批队列 `peer_inbound` 行(`GET /v1/approvals` 的 `pending[]` 与 `/v1/approvals/stream` 的 `pending` 帧)上被扣跨会话来信的
29
+ * 存储正文(`content`,core 的规范存储形 `[summary] message`)与发信方显示名(`fromName`)—— 别的会话写的自由文本,读者是收信方本人;不设界(core 存的那一份就是
30
+ * 收信方要审的全文,截了就审不全),原样出门(JSON 串值,消费方自己渲染)。
28
31
  */
29
32
  declare const EXIT_SEATS: {
30
33
  readonly "trace-frame-text": {
@@ -75,6 +78,9 @@ declare const EXIT_SEATS: {
75
78
  readonly maxChars: 512;
76
79
  readonly emit: "log-line";
77
80
  };
81
+ readonly "peer-inbound-card-text": {
82
+ readonly scope: "free-text";
83
+ };
78
84
  };
79
85
  export type ExitSeat = keyof typeof EXIT_SEATS;
80
86
  /** 设了界的座名。 */
@@ -16,6 +16,7 @@ const EXIT_SEATS = {
16
16
  "peer-declaration-sentence": { scope: "free-text" },
17
17
  "inbox-diagnostics-text": { scope: "free-text", maxChars: 4000 },
18
18
  "boot-refusal-line": { scope: "free-text", maxChars: 512, emit: "log-line" },
19
+ "peer-inbound-card-text": { scope: "free-text" },
19
20
  };
20
21
  export function seatMaxChars(seat) {
21
22
  return EXIT_SEATS[seat].maxChars;
@@ -152,8 +152,8 @@ export declare function createLedgerSink(opts: {
152
152
  * 各传自己看得见的那个词(位①=流事件 `ev.type`,位②=账本行 `type`);拿不到类型的打点方
153
153
  * (core 的 onActivity 第五席、resume 腿手动打点)省参,`kind` 诚实缺席(见 turn-activity.ts)。 */
154
154
  onActivity?: (kind?: string, statusData?: Record<string, unknown>) => void;
155
- /** 这条腿的 `wiring_manifest` **本仓自铸段**(`LegWiringSections`:[ref] `instructions` 读腿的 `InstructionSelectionLeg.segment`、run 级 `skills` 读 spec 字段
156
- * `runSkillsOf`)。**必填**:账本行与 live 帧是同一条腿的两个载体,段必须来自同一只腿;在投帧那一刻读(core 推帧之前已 await 过 loader)。 */
155
+ /** 这条腿的 `wiring_manifest` **本仓自铸段**(`LegWiringSections`:[ref] `instructions` 读腿的 `InstructionSelectionLeg.segment`、run 级 `skills` 读这条腿的部署事实
156
+ * `LegFacts.skills`,`leg-facts.ts`)。**必填**:账本行与 live 帧是同一条腿的两个载体,段必须来自同一只腿;在投帧那一刻读(core 推帧之前已 await 过 loader)。 */
157
157
  legSections: () => LegWiringSections;
158
158
  /** 这条腿的计量席(E8 累加器 + 归因键)。在场 ⇒ {@link LedgerSink.appendParked} 在停驻行之前先把未结清的用量差落成 `model_usage` 行(状态帧最后,见 `appendParkRow`);
159
159
  * 缺席 ⇒ 这条腿没接累加器,停驻行之前无账可结(夹具、不经 `appendParked` 停驻的腿)。 */
@@ -1,4 +1,4 @@
1
- import { type SegmentEndFields } from "./project.js";
1
+ import { type LiveEventIdentity, type SegmentEndFields } from "./project.js";
2
2
  /**
3
3
  * 本帧是不是模型文本的段界。**四条消费腿共用这一只**:账本投影 switch(`trace/ledger-sink.ts`)、
4
4
  * 顶层 live 流与转发子代腿(`http/routes/tasks.ts`)、子代 tail 面(`fleet/subagent-tail-bus.ts`)。
@@ -13,43 +13,73 @@ export type ReasoningEndFrame = SegmentEndFields & {
13
13
  type: "reasoning_end";
14
14
  };
15
15
  /** 登记册**读到的全部字段**,逐个写出来(不是 `Record<string, unknown>`):`TaskEvent` 与转发腿的原始帧
16
- * 都结构性满足它,于是调用点一个宽断言都不必写;将来有人想读第七个键,得先在这里加一行。 */
17
- export interface ReasoningStreamEvent {
16
+ * 都结构性满足它,于是调用点一个宽断言都不必写;将来有人想读第八个键,得先在这里加一行。
17
+ * `type` 而不是 `interface`(与 `LiveEventIdentity` 同一条理由):`text_end` 的铸点收 `Record<string, unknown>`,别名才进得去。 */
18
+ export type ModelStreamEvent = {
18
19
  type?: string;
19
20
  delta?: unknown;
21
+ content?: unknown;
20
22
  eventId?: unknown;
21
23
  parentToolCallId?: unknown;
22
24
  sourceTaskId?: unknown;
23
25
  bgAgentId?: unknown;
24
- }
26
+ };
25
27
  /** 一条**流**的身份(不含 `eventId`:core 给每个内容事件铸各自的 eventId,拿它当流身份会把每块 delta
26
28
  * 切成一段)。三键都缺席 = 宿主自己的流。 */
27
- export interface ReasoningStreamIdent {
29
+ export interface ModelStreamIdent {
28
30
  parentToolCallId?: string;
29
31
  sourceTaskId?: string;
30
32
  bgAgentId?: string;
31
33
  }
34
+ /** live 面模型文本的帧(本登记册铸的全部四种):两族的逐块帧与各自的段末帧。 */
35
+ export type LiveModelTextFrame = (LiveEventIdentity & {
36
+ type: "text_delta" | "reasoning_delta";
37
+ delta: string;
38
+ }) | (SegmentEndFields & {
39
+ type: "text_end";
40
+ }) | ReasoningEndFrame;
32
41
  /**
33
- * 一条 live 腿上**按流身份分账**的 `reasoning` 段登记册。
42
+ * 🔴 **live 面模型文本的唯一铸点** —— 一条 live 腿上按**族 × 流身份**分账的段登记册(三条 live 腿共用:顶层流 / 转发子代腿 / 子代 tail 面)。
43
+ *
44
+ * ## 律(一条):凡出 wire 的模型文本经同一只脱敏器,不分帧
45
+ * 段末帧(`text_end.content` / `reasoning_end.content`)、`done.result`、账本 `text` / `reasoning` 行早就整段过 `redactModelText`;
46
+ * 逐块帧(`text_delta` / `reasoning_delta`)此前一个逐字、一个逐块脱敏 —— 跨块的凭据在两者上都留明文。现在逐块帧也只放**同一只脱敏器**判定过的字节:
47
+ * 每条流一只脱敏器的**流式口**(`trace/redact.ts` 的 `createRedactionStream`:喂入 / 取已判定前缀 / 收尾冲刷三口,同一只扫描器、同一张表):每块到达时喂入、
48
+ * 取出已判定的那一截放出(脱敏器自己的未决尾不出门,横跨切点的跨度整条不出);段末冲刷,余下的按整段读法放出,再发段末帧。
49
+ * ⇒ 一段的逐块帧依次拼接 = 该段段末帧的正文 —— 逐字节(凭据形换记号,不含凭据形的文本逐字节不变)。推理段 = 账本 `reasoning` 行(同一只段界谓词);
50
+ * 文字段的单位是引擎的文字块(`text_end` 收段),账本 `text` 行按 turn 拼接 —— 单文字块的 turn 上相等;锚与值分落在同一 turn 的两个文字块里时账本遮、段末帧与逐块帧不遮
51
+ * (单位之差随 `text_end` 既有,本登记册不另立单位;对抗复审 r1 medium)。
52
+ *
53
+ * ## 帧数与身份(消费端看得见的三件事)
54
+ * · 引擎的每一块 delta 仍恰好一帧、同一枚 `eventId`、原序(`delta` 可以是 `""`:那一块的字节还在窗里)—— 续流游标与 E18 的首块句柄不动;
55
+ * · 段末还在窗里的字节在段末帧**之前**一帧放出(同一族的逐块帧,带流身份三键、**不带** `eventId` ⇒ 不发 `id:` 行:它不是引擎的内容帧);
56
+ * · 延迟:一个字节在同一条流又到了脱敏器的未决尾上界(`PENDING_TAIL_UNITS` 个读法单元,由脱敏器的臂表算出)之后的下一次重扫时出门(重扫节律在流式口里);
57
+ * 盖住它的跨度还在判定(未收尾的栅栏 / 引号 / JSON 串值 …)⇒ 判定或段末。
58
+ *
59
+ * ## 段界(与账本腿同一只谓词)
60
+ * 两族都在本流的段界词({@link isModelSegmentBoundary})与腿终局 / 子代终态({@link LiveModelText.cut})收段;`text` 族另在引擎的 `text_end` 收段(它关的是散文段,
61
+ * 不是 turn 的结构边界 —— 推理段不因它收)。收段顺序:同一批里先散文、后推理(`text_end` 先于 `reasoning_end`,与契约的典型帧序同向)。
62
+ *
63
+ * 🔴 **为什么分账**(codex 对抗复审 R1 两根 [high],亲跑复现后按类修):一条腿上并发子代的逐块帧是**交错**到达的。一只累积器只有两种活法,两条都错:
64
+ * · 「换身份就收段」⇒ A 说一半、B 插一句、A 说另一半 ⇒ A 被切成两段,**跨块的凭据正好落在切口上**;
65
+ * · 「不换就不收」⇒ 两个子代的文字拼成一段,再盖上其中一个的身份键(串味 + 冤枉归属)。
66
+ * 一条流的段,只被**它自己**的段界或**它自己**的终态收走;别人说什么都与它无关。
34
67
  *
35
- * 🔴 **为什么是登记册而不是一只累积器**(codex 对抗复审 R1 两根 [high],亲跑复现后按类修):一条腿上
36
- * 并发子代的 `reasoning_delta` 是**交错**到达的。一只累积器只有两种活法,两条都错:
37
- * · 「换身份就收段」⇒ A 说一半、B 插一句、A 说另一半 ⇒ A 被切成两枚权威段,**跨块的凭据正好落在切口上**
38
- * ——本帧存在的唯一理由当场失效(实测:两枚 `reasoning_end` 拼起来仍是明文密钥);
39
- * · 「不换就不收」⇒ 两个子代的思考拼成一段,再盖上其中一个的身份键(串味 + 冤枉归属)。
40
- * 分账之后规则反而**更少**:一条流的段,只被**它自己**的段界或**它自己**的终态收走;别人说什么都与它无关。
68
+ * ## 投递态(tail 面:有没有人在听)
69
+ * `onEvent(ev, deliver)`:`deliver = false` 时只攒原文、照常按段界收段,不扫描、不出帧(tail 面的免投影快门);一条流从不投递转为投递时,放行位跳到**此刻**的段尾
70
+ * —— 之前的字节没人收,之后的字节仍按**整段**的上下文判定(晚到的订阅者在 `password=` 之后才开始听,值照样认得出)。段末帧的正文恒是整段。
41
71
  *
42
- * 状态的界:每条流的条目在它的首块 delta 上建、在收段时删;腿终局 {@link ReasoningSegments.cut} 不带
43
- * 匹配器 = 全收全删。子代终态那一次带匹配器的收段(转发腿的 `onTaskNotification`)是让**活着的子代数**
44
- * 而不是「这条 run 一共派过多少子代」成为上界的那一手。
72
+ * 状态的界:每条流的条目在它的首块非空 delta 上建、在收段时删;腿终局 {@link LiveModelText.cut} 不带匹配器 = 全收全删。
73
+ * 子代终态那一次带匹配器的收段(转发腿的 `onTaskNotification`)让**活着的子代数**成为上界。
45
74
  */
46
- export interface ReasoningSegments {
47
- /** 喂一帧 → 本帧收走的段末帧(0 或 1 枚,且**只可能是本帧自己那条流**的段)。返回数组只为让调用点
48
- * 与 {@link cut} 同形(`for (const f of …) send(f)`),不是「一帧可能收多段」。 */
49
- onEvent(ev: ReasoningStreamEvent): ReasoningEndFrame[];
50
- /** 收段:`match` 缺省 = 本腿**全部**流(腿终局 / 句柄终态);给了就只收匹配的那几条(某个子代终态)。
75
+ export interface LiveModelText {
76
+ /** 喂一帧 → 本帧要写的帧(按序):逐块帧 / 本流收段放出的余下字节 + 段末帧。`deliver` 缺省 = 投递。 */
77
+ onEvent(ev: ModelStreamEvent, deliver?: boolean): LiveModelTextFrame[];
78
+ /** 收段:`match` 缺省 = 本腿**全部**流(腿终局 / 句柄终态);给了就只收匹配的那几条(某个子代终态)。`deliver` 同 {@link onEvent}(缺省 = 投递)。
51
79
  * 与账本腿 `LedgerSink.onDone → flush()` 同位同义 —— 段界表里没有终局词,终局走这扇门。 */
52
- cut(match?: (ident: ReasoningStreamIdent) => boolean): ReasoningEndFrame[];
80
+ cut(match?: (ident: ModelStreamIdent) => boolean, deliver?: boolean): LiveModelTextFrame[];
81
+ /** 没有开着的段(状态只有开着的段:空着的登记册与新建的一只等价,持有方可以删掉它、下一帧再建)。 */
82
+ isIdle(): boolean;
53
83
  }
54
84
  /**
55
85
  * 一段推理的**段末事件 id** —— 唯一铸点,live 段末帧与账本 `reasoning` 行共用这一只。
@@ -78,8 +108,8 @@ export declare function reasoningSegmentEventId(anchor: unknown): string | undef
78
108
  export declare function reasoningDeltaBytes(ev: {
79
109
  delta?: unknown;
80
110
  }): string | undefined;
81
- /** 「这条流属于这些子代 id 之一」—— 子代终态收段的匹配器(`task_id` 与 `sessionId` 是同一个子代的两个
82
- * id 域,取并集;`undefined` 成员自动忽略)。腿上共用这一只,免得各写一遍 id 域的并集。 */
83
- export declare function reasoningStreamOfTask(...ids: ReadonlyArray<string | undefined>): (ident: ReasoningStreamIdent) => boolean;
84
- export declare function createReasoningSegments(): ReasoningSegments;
111
+ /** 「这条流属于这只后台子代」—— 子代终态收段的匹配器。一只后台子代一个 id(引擎 7.61.0:它的帧的 `sourceTaskId`、
112
+ * 转发帧的 `bgAgentId`、完成通知的 `task_id` 都是它的注册表行 id),按那一个 id 认流。 */
113
+ export declare function modelStreamOfTask(taskId: string): (ident: ModelStreamIdent) => boolean;
114
+ export declare function createLiveModelText(): LiveModelText;
85
115
  //# sourceMappingURL=model-segment.d.ts.map
@@ -1,4 +1,9 @@
1
- import { reasoningEndEventData } from "./project.js";
1
+ import { recordFailOpen } from "../observability/fail-open.js";
2
+ import { errorKindOf } from "../observability/err-text.js";
3
+ import { OVERSIZED_REDACTION_PLACEHOLDER } from "../observability/run-terminal.js";
4
+ import { liveIdentityFields, reasoningEndEventData, textEndEventData } from "./project.js";
5
+ import { dropProjectedFace } from "./projection-drop.js";
6
+ import { createRedactionStream } from "./redact.js";
2
7
  const BOUNDARY = new Set([
3
8
  "tool_start", "tool_end", "status", "context_usage", "compacted", "diagnostics",
4
9
  "steering_injected", "compaction_outcome", "workspace_changed", "wiring_manifest",
@@ -14,9 +19,8 @@ export function reasoningSegmentEventId(anchor) {
14
19
  export function reasoningDeltaBytes(ev) {
15
20
  return typeof ev.delta === "string" && ev.delta !== "" ? ev.delta : undefined;
16
21
  }
17
- export function reasoningStreamOfTask(...ids) {
18
- const set = new Set(ids.filter((v) => typeof v === "string" && v !== ""));
19
- return (ident) => (ident.sourceTaskId !== undefined && set.has(ident.sourceTaskId)) || (ident.bgAgentId !== undefined && set.has(ident.bgAgentId));
22
+ export function modelStreamOfTask(taskId) {
23
+ return (ident) => ident.sourceTaskId === taskId || ident.bgAgentId === taskId;
20
24
  }
21
25
  function streamKeyOf(ident) {
22
26
  return `${ident.parentToolCallId ?? ""}\u0000${ident.sourceTaskId ?? ""}\u0000${ident.bgAgentId ?? ""}`;
@@ -25,44 +29,108 @@ function identOf(ev) {
25
29
  const s = (v) => (typeof v === "string" ? v : undefined);
26
30
  return { parentToolCallId: s(ev.parentToolCallId), sourceTaskId: s(ev.sourceTaskId), bgAgentId: s(ev.bgAgentId) };
27
31
  }
28
- export function createReasoningSegments() {
29
- const open = new Map();
30
- const cutKey = (key) => {
31
- const seg = open.get(key);
32
- if (seg === undefined)
32
+ function release(s) {
33
+ try {
34
+ return s.text.takeSettled();
35
+ }
36
+ catch (err) {
37
+ recordFailOpen("server.run-terminal.redact-threw", `live-hold len=${s.text.fed.length} err=${errorKindOf(err)}`);
38
+ return "";
39
+ }
40
+ }
41
+ function remainderOf(family, s) {
42
+ try {
43
+ const r = s.text.flush();
44
+ if (r.diverged)
45
+ recordFailOpen("server.live-text.hold-diverged", `family=${family} len=${s.text.fed.length}`);
46
+ return r.rest;
47
+ }
48
+ catch (err) {
49
+ recordFailOpen("server.run-terminal.redact-threw", `live-hold len=${s.text.fed.length} err=${errorKindOf(err)}`);
50
+ return OVERSIZED_REDACTION_PLACEHOLDER;
51
+ }
52
+ }
53
+ export function createLiveModelText() {
54
+ const open = { text: new Map(), reasoning: new Map() };
55
+ const deltaType = (family) => (family === "text" ? "text_delta" : "reasoning_delta");
56
+ const close = (family, key, deliver) => {
57
+ const s = open[family].get(key);
58
+ if (s === undefined)
59
+ return [];
60
+ open[family].delete(key);
61
+ if (!deliver)
33
62
  return [];
34
- open.delete(key);
35
- const data = reasoningEndEventData({ ...seg.ident, eventId: seg.segmentEventId, content: seg.buf });
36
- return data === null ? [] : [{ type: "reasoning_end", ...data }];
63
+ if (!s.delivering)
64
+ s.text.skip();
65
+ const out = [];
66
+ const rest = remainderOf(family, s);
67
+ if (rest !== "")
68
+ out.push({ type: deltaType(family), delta: rest, ...liveIdentityFields(s.ident) });
69
+ if (family === "reasoning") {
70
+ const data = reasoningEndEventData({ ...s.ident, eventId: s.segmentEventId, content: s.text.fed });
71
+ if (data !== null)
72
+ out.push({ type: "reasoning_end", ...data });
73
+ }
74
+ return out;
75
+ };
76
+ const feed = (family, ev, deliver) => {
77
+ if (typeof ev.delta !== "string") {
78
+ if (family === "text")
79
+ dropProjectedFace("text_delta.delta is not a string");
80
+ else
81
+ dropProjectedFace("reasoning_delta.delta is not a string");
82
+ return [];
83
+ }
84
+ const ident = identOf(ev);
85
+ const key = streamKeyOf(ident);
86
+ let s = open[family].get(key);
87
+ if (s === undefined && ev.delta !== "") {
88
+ s = { ident, text: createRedactionStream(), delivering: deliver };
89
+ open[family].set(key, s);
90
+ }
91
+ if (s !== undefined) {
92
+ if (deliver && !s.delivering) {
93
+ s.delivering = true;
94
+ s.text.skip();
95
+ }
96
+ else if (!deliver)
97
+ s.delivering = false;
98
+ s.text.feed(ev.delta);
99
+ if (family === "reasoning" && reasoningDeltaBytes(ev) !== undefined)
100
+ s.segmentEventId ??= reasoningSegmentEventId(ev.eventId);
101
+ }
102
+ if (!deliver)
103
+ return [];
104
+ return [{ type: deltaType(family), delta: s === undefined ? "" : release(s), ...liveIdentityFields(ev) }];
37
105
  };
38
106
  return {
39
- onEvent: (ev) => {
40
- if (ev.type === "reasoning_delta") {
41
- const bytes = reasoningDeltaBytes(ev);
42
- if (bytes === undefined)
43
- return [];
44
- const ident = identOf(ev);
45
- const key = streamKeyOf(ident);
46
- const seg = open.get(key);
47
- if (seg === undefined)
48
- open.set(key, { ident, segmentEventId: reasoningSegmentEventId(ev.eventId), buf: bytes });
49
- else {
50
- seg.buf += bytes;
51
- seg.segmentEventId ??= reasoningSegmentEventId(ev.eventId);
52
- }
53
- return [];
107
+ onEvent: (ev, deliver = true) => {
108
+ if (ev.type === "text_delta")
109
+ return feed("text", ev, deliver);
110
+ if (ev.type === "reasoning_delta")
111
+ return feed("reasoning", ev, deliver);
112
+ const key = streamKeyOf(identOf(ev));
113
+ if (ev.type === "text_end") {
114
+ const out = close("text", key, deliver);
115
+ const end = deliver ? textEndEventData(ev) : null;
116
+ if (end !== null)
117
+ out.push({ type: "text_end", ...end });
118
+ return out;
54
119
  }
55
- return isModelSegmentBoundary(ev.type ?? "") ? cutKey(streamKeyOf(identOf(ev))) : [];
120
+ return isModelSegmentBoundary(ev.type ?? "") ? [...close("text", key, deliver), ...close("reasoning", key, deliver)] : [];
56
121
  },
57
- cut: (match) => {
122
+ cut: (match, deliver = true) => {
58
123
  const out = [];
59
- for (const [key, seg] of [...open]) {
60
- if (match !== undefined && !match(seg.ident))
61
- continue;
62
- out.push(...cutKey(key));
124
+ for (const family of ["text", "reasoning"]) {
125
+ for (const [key, s] of [...open[family]]) {
126
+ if (match !== undefined && !match(s.ident))
127
+ continue;
128
+ out.push(...close(family, key, deliver));
129
+ }
63
130
  }
64
131
  return out;
65
132
  },
133
+ isIdle: () => open.text.size === 0 && open.reasoning.size === 0,
66
134
  };
67
135
  }
68
136
  //# sourceMappingURL=model-segment.js.map
@@ -222,8 +222,8 @@ export declare function screenedStoredEventData(type: string, data: unknown): un
222
222
  /** `text_end` 段末帧的**唯一**铸点(三腿共用:top stream / 转发子代腿 / 子代 tail 面)。content = 段权威全文 ⇒ 与
223
223
  * durable 面(`result` / 账本 `text` 行)同一只 {@link redactModelText}(认出才改写、抛则占位、不设上限)。
224
224
  * 此前三处各自逐字透传 —— 同一份文本毫秒后在 `done.result.result` 上已脱,live 面却在凭据生成瞬间明文([ref])。
225
- * `text_delta` 仍逐字:跨 chunk 的凭据无法就地判;clay [ref] ②(2026-09-18)裁定**维持逐字成文**([ref] U-3 关票),
226
- * 权威段 `text_end.content` 已脱敏、消费端段末整段替换即得脱敏读法。
225
+ * 逐块的 `text_delta` 由 `trace/model-segment.ts` 的登记册铸(7.143.0 起经同一只脱敏器的放行窗,一段的逐块拼接 = 本帧 content);
226
+ * 本函数由登记册在段末调,三腿不再各自调。
227
227
  * 坏形(content 非串/空串)⇒ null(丢帧,不铸「权威空段」:core 契约明言空段不发本帧,消费端收到空段会清掉已攒 delta)。
228
228
  * 身份**四键**随行(live 现状带全四键,消费端可能已依赖);值经 {@link identityKey} —— 非空 string 才上 wire
229
229
  * ([ref] 起不再逐字透传:坏形身份键会在 live 写口上铸出假游标)。 */
@@ -637,6 +637,7 @@ export declare function compactionOutcomeEventData(ev: {
637
637
  trigger?: unknown;
638
638
  reason?: unknown;
639
639
  pass?: unknown;
640
+ requestedBy?: unknown;
640
641
  eventId?: unknown;
641
642
  parentToolCallId?: unknown;
642
643
  }): FrameData<{
@@ -887,8 +888,8 @@ export declare function toolRosterDeltaEventData(ev: {
887
888
  * (值可以是 `undefined`)—— 漏递的腿当场 tsc 红,而不是静默投出一个「缺席」(缺席在 wire 上是一句话:「这条腿对这件事没有陈述」)。
888
889
  * · `instructions`([ref],7.111.0)—— 这条腿的 project-memory loader 选中的指令文件链成员 `{ selected: string[] }`;值来自腿的
889
890
  * `InstructionSelectionLeg.segment()`。`undefined` ⇒ 段缺席;`{ selected: [] }` ⇒ 显式空(走了链、没选中)。
890
- * · `skills`(7.137.0)—— 这条 run 交给引擎的技能名册(`{ name, description, source }[]`,与场景卡技能清单同一张形);值来自 spec 字段 `runSkillsOf(spec)`
891
- * (唯一铸点 = resolveSpec 出口的 `buildRunSkills`)。**run 级** ⇒ 只投根腿 / 续跑腿的帧,委派子代的帧(`parentToolCallId` 在场)不投(子代的技能由它自己的定义决定);
891
+ * · `skills`(7.137.0)—— 这条 run 交给引擎的技能名册(`{ name, description, source }[]`,与场景卡技能清单同一张形);值来自这条腿的部署事实 `LegFacts.skills`
892
+ * (`leg-facts.ts`;名册由 `buildRunSkills` 合并、resolveSpec 出口登记,漏斗在复制之前读出)。**run 级** ⇒ 只投根腿 / 续跑腿的帧,委派子代的帧(`parentToolCallId` 在场)不投(子代的技能由它自己的定义决定);
892
893
  * `[]` = 零技能(含 `disableSkills`);`undefined` ⇒ 段缺席(这条腿的 spec 不是本仓装配的)。`description` 过上 wire 的自由文本出口 {@link sanitizeWireText}。
893
894
  */
894
895
  export interface LegWiringSections {
@@ -1076,6 +1077,8 @@ export declare function compactedEventData(ev: {
1076
1077
  };
1077
1078
  /** [ref](core 7.53.0 [ref]):这次落地的压缩 pass 的 id —— 与它的 `compaction_started` 帧同一个值(引擎在每次落地上都设;型上可选)。逐字。 */
1078
1079
  pass?: string;
1080
+ /** core 7.61.0:扩展经 `$.session.compact` 提的手动请求落地的那一 pass —— 谁要的(同 {@link compactionOutcomeEventData} 那一位)。 */
1081
+ requestedBy?: unknown;
1079
1082
  }): FrameData<{
1080
1083
  attachedFiles?: Array<{
1081
1084
  path: SafeText;
@@ -434,9 +434,14 @@ export function compactionOutcomeEventData(ev) {
434
434
  ...(s(ev.trigger) !== undefined ? { trigger: s(ev.trigger) } : {}),
435
435
  ...(ev.reason !== undefined ? { reason: seatText("trace-frame-text", String(ev.reason)) } : {}),
436
436
  ...(s(ev.pass) !== undefined ? { pass: s(ev.pass) } : {}),
437
+ ...requestedByField(ev.requestedBy, "compaction_outcome"),
437
438
  ...identityFields(ev),
438
439
  };
439
440
  }
441
+ function requestedByField(v, where) {
442
+ const requester = extensionRequesterOf(v, where);
443
+ return requester !== undefined ? { requestedBy: requester } : {};
444
+ }
440
445
  export function compactionStartedEventData(ev) {
441
446
  const s = (v) => (typeof v === "string" ? v : undefined);
442
447
  return {
@@ -893,6 +898,7 @@ export function compactedEventData(ev) {
893
898
  ...(num(ev.durationMs) !== undefined ? { durationMs: num(ev.durationMs) } : {}),
894
899
  ...(phaseDurations !== undefined ? { phaseDurations } : {}),
895
900
  ...(typeof ev.pass === "string" ? { pass: ev.pass } : {}),
901
+ ...requestedByField(ev.requestedBy, "compacted"),
896
902
  };
897
903
  }
898
904
  export function contextUsageEventData(ev) {
@@ -36,6 +36,8 @@ export declare function armPoolBudgetForTest(): {
36
36
  ssh2ScanBytes: number;
37
37
  mappedUnits: number;
38
38
  readings: number;
39
+ coverageVisits: number;
40
+ rowVisits: number;
39
41
  };
40
42
  export declare function resetArmPoolBudgetForTest(): void;
41
43
  /**
@@ -54,11 +56,9 @@ export declare function resetArmPoolBudgetForTest(): void;
54
56
  * 闭集:加一个档而不在 {@link armPool} 的档表里处置 ⇒ **编译**红。
55
57
  */
56
58
  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;
59
59
  /** 本模块会写进文本的全部替换 token(`«…»` 字面),从三张表现算 —— 表改这里自动跟,不另抄一份。
60
60
  * 🔴 **[ref] v2 修复轮起它只剩一个语义:闭形闭集的加载期校验**。运行期「这一段是不是一枚记号」已经由
61
- * {@link credentialSpans} 的闭**形**那一支一处答完({@link opaqueUnitEnd}),按闭**集**再认一遍的
61
+ * 记号行的闭**形**一处答完({@link tokenUnits} / {@link tokenEnd}),按闭**集**再认一遍的
62
62
  * `redactionTokenEndAt` 与它同义而更窄,已删 —— 两只「什么算记号」的判别器就是两个定义。 */
63
63
  /** {@link buildDisplaySafeUrl} 解析不动一个配置型 URL 时的**占位**([ref] 件B)——fail-closed:输出**不含原串的
64
64
  * 任何字节**(连 host 都不留),不许回落成原值。它不是任何一条臂的产物,所以要在这里**显式**进闭集
@@ -70,17 +70,22 @@ export { DISPLAY_SAFE_URL_TOKEN };
70
70
  /**
71
71
  * 🔴 **取证相的重扫模式(测试座,[ref])** —— 生产恒 `"incremental"`;`"full"` 是**参照语义** = 「没有可复用的上一次执行」:
72
72
  * 每级全部取证臂整段重跑、URL 臂每段重扫,既不读足迹也不回放(池把每一级的脏区间当作「未知」交下去,与级 0 同一个读法)。
73
- * 证据表的建法(级 0 记号档 + 逐级折入)与收敛判据(脏区间为空)两种模式是同一条,所以两种模式只差「复用与否」这一位 ——
74
- * 增量重扫的全部正确性主张只有一句:**任何输入上两种模式输出逐字相同**(`test/forensic-incremental-rescan.test.ts` 并跑比对;
75
- * 折入本身与整表重建的对拍是那个测试文件里的一份测试本地参照)。
73
+ * 证据表的建法(级 0 记号 + 逐级折入)与收敛判据(脏区间为空)两种模式是同一条,所以两种模式只差「复用与否」这一位 ——
74
+ * 增量重扫的全部正确性主张只有一句:**任何输入上两种模式输出逐字相同**(`test/forensic-incremental-rescan.test.ts` 并跑比对)。
76
75
  */
77
76
  export type ForensicRescanMode = "incremental" | "full";
78
77
  export declare function setForensicRescanModeForTest(mode: ForensicRescanMode): void;
79
- /** 测试座:折入函数的数组形(`prev` 须是合法的已并表:升序、两两不交不相接)。 */
80
- export declare function credentialSpansFoldForTest(prev: ReadonlyArray<readonly [number, number]>, add: ReadonlyArray<readonly [number, number]>): {
81
- next: Array<[number, number]>;
78
+ /** 测试座:折入函数的数组形(行 = `[起点, 终点, 族码, 锚位, 收尾位]`)。 */
79
+ export declare function evidenceRowsFoldForTest(prev: ReadonlyArray<readonly [number, number, number, number, number]>, add: ReadonlyArray<readonly [number, number, number, number, number]>): {
80
+ next: Array<[number, number, number, number, number]>;
82
81
  dirty: Array<[number, number]>;
83
82
  };
83
+ /** 测试座:{@link measureWithin} 的数组形(`table` 的每一项是一行 `[起点, 终点]` 或 `[起点, 终点, 键]`:不给键 ⇒ 键 = 起点;同一 (起点, 键) 至多一行;
84
+ * `self` 给出 ⇒ 读者 = 键为它的那些行,否则不排除任何读数)。 */
85
+ export declare function unitCountWithinForTest(table: ReadonlyArray<readonly [number, number] | readonly [number, number, number]>, from: number, to: number, max: number, self?: number): boolean;
86
+ /** 测试座:同一张表上依次问一串量问(`[from, to, max, self?]`;读法同 {@link unitCountWithinForTest})—— 终点有界的闭包链缓存在问与问之间共用,
87
+ * 生产里同一级、同一张表上各臂各读者的量问就是这样串起来的。 */
88
+ export declare function unitCountsWithinForTest(table: ReadonlyArray<readonly [number, number] | readonly [number, number, number]>, queries: ReadonlyArray<readonly [number, number, number, number | undefined]>): boolean[];
84
89
  /** 🧪 结构读数(与 {@link ppkScanBytesForTest} 同一条技法):本扫描器读过的单元数,线性钉用。 */
85
90
  export declare function anchoredScanUnitsForTest(): number;
86
91
  export declare function resetAnchoredScanUnitsForTest(): void;
@@ -226,6 +231,53 @@ export type RedactionSpan = {
226
231
  };
227
232
  export type { RedactionScope };
228
233
  export declare function proposeRedactionSpans(text: string, scope: RedactionScope, layout: RedactionLayout, extent: RedactionExtent): readonly RedactionSpan[];
234
+ /**
235
+ * 🔴 **未决尾的长度上界**(读法单元):一段还在长的文本,末尾这么多个读法单元里起头的候选,本脱敏器**还判不了**(判定要读到文本之外)。
236
+ * 它不是另立的常数,是候选池成员表的同一份读数 —— 每条臂的**判定距离**取最大({@link MemberIdentity.reach} 的意义见那里):正则臂从正则源读出
237
+ * (前缀 + 前瞻判据 + 值 run 的最少重复;熵谓词 {@link ENTROPY_SCAN_MAX} 两窗由此进来),手写扫描器陈述它起头要读全的字面锚。
238
+ * 判定在**无界**远处的结构(没收尾的 JSON 串值 / URL userinfo / 栅栏 / 带引号的值、起头不设长度上界的栅栏行 / 版本头 / 关键字分隔段)不靠它:
239
+ * head 读法把它们读成未决尾、从起点遮到文本末尾({@link RedactionExtent}),流式口在它的起点前停下,直到它判定或冲刷。
240
+ * 加一条判定距离更远的臂,这个数随之变大(钉 `test/live-model-text-hold-law.test.ts` 读每条成员的读数与最大值)。
241
+ */
242
+ export declare const PENDING_TAIL_UNITS: number;
243
+ /** 测试可见:正则臂判定距离的读法本身(读得出的几形与加载期抛的几形)。 */
244
+ export declare function regexDecisionReachForTest(re: RegExp, lexical: boolean): number;
245
+ /** 测试可见:候选池每条成员的判定距离(成员的替换串、池序、读数)。 */
246
+ export declare function pendingTailReachForTest(): ReadonlyArray<{
247
+ readonly token: string;
248
+ readonly order: number;
249
+ readonly reach: number;
250
+ }>;
251
+ /**
252
+ * 🔴 **流式口 —— 同一只脱敏器的三口**(live 面逐块到达的模型文本 `text_delta` / `reasoning_delta` 的唯一放行器;登记册在 `trace/model-segment.ts`)。
253
+ * 同一只 {@link runRedaction}、同一张表,只多一件事:知道文本还在长。
254
+ * · **喂入**({@link RedactionStream.feed}):原文追加进本段;
255
+ * · **取已判定前缀**({@link RedactionStream.takeSettled}):从上次取到的位置起,已判定的那一截的脱敏读法 —— head 读法,出门区终点 = 末尾 {@link PENDING_TAIL_UNITS}
256
+ * 个读法单元之前;切点再退到横跨它的那一条跨度的起点(一枚可能还在长、或判定在更远处的凭据整条不出)与码点边界。按重扫节律({@link streamRescanStep})才真扫;
257
+ * · **收尾冲刷**({@link RedactionStream.flush}):整段读法交出其余字节,并核对已取出的字节在整段读法下是不是同一份。
258
+ * 律:各次取出依次拼接 + 冲刷 = 整段读法(`redactSecrets(全段)`)逐字节 —— 只喂一次再冲刷就是整段口本身。前提是 {@link PENDING_TAIL_UNITS} 读对了每条臂的判定距离;
259
+ * 读错的那一形在冲刷时现形(`diverged`),调用方响亮留痕。
260
+ * 延迟上界:一个字节在它之后又到 {@link PENDING_TAIL_UNITS} 个读法单元之后的下一次重扫时交出;盖住它的跨度判定之前(或冲刷之前)不交出。
261
+ * 不喂观察者(`observe:false`,与影子重扫同口):命中只在段末那一次整段脱敏里计(段末帧的铸点),逐块重扫不重复计数。扫描器抛 ⇒ 原样抛(放行位不动)。
262
+ */
263
+ export interface RedactionStream {
264
+ /** 喂入下一块原文。 */
265
+ feed(chunk: string): void;
266
+ /** 取已判定前缀(可为 `""`)。 */
267
+ takeSettled(): string;
268
+ /** 收尾冲刷:其余字节的整段读法 + 已取出的字节与整段读法对不对得上。冲刷之后本流不再用。 */
269
+ flush(): {
270
+ readonly rest: string;
271
+ readonly diverged: boolean;
272
+ };
273
+ /** 跳过:已喂入的字节不再交出(没有收件人;tail 面没人在听时),之后的判定仍按整段上下文。 */
274
+ skip(): void;
275
+ /** 已喂入的整段原文。 */
276
+ readonly fed: string;
277
+ }
278
+ export declare function createRedactionStream(): RedactionStream;
279
+ /** 测试可见:同一只流式口,未决尾按调用方给的数(比 {@link PENDING_TAIL_UNITS} 小 ⇒ 律被打破的那一形,冲刷时 `diverged` 现形 —— 响亮臂的触发器;生产调用方只有上面那一只)。 */
280
+ export declare function createRedactionStreamWithTailForTest(units: number): RedactionStream;
229
281
  /**
230
282
  * 🔴 环 + 深度预算(2026-07-25 补)。原实现是纯递归、**既不记已访问节点也不限深度**,而它的调用位置是
231
283
  * **最不可信的边界**:`toolStart`/`toolEnd` 把工具的 `args`/`output`/`structured` 直接喂进来。后果两条: