@sema-agent/core 5.57.0 → 5.59.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 (192) hide show
  1. package/CHANGELOG.md +96 -0
  2. package/dist/agents/cascade.d.ts +1 -1
  3. package/dist/agents/cumulative-stats.d.ts +1 -1
  4. package/dist/agents/observer.d.ts +2 -2
  5. package/dist/agents/peer-admission.d.ts +1 -1
  6. package/dist/agents/retain-ledger.d.ts +2 -2
  7. package/dist/agents/roster-store.d.ts +8 -8
  8. package/dist/agents/send-message-tool.d.ts +2 -2
  9. package/dist/agents/subagent-steps.d.ts +1 -1
  10. package/dist/agents/subagent.d.ts +13 -13
  11. package/dist/agents/team.d.ts +5 -5
  12. package/dist/agents/tool-filter.d.ts +2 -2
  13. package/dist/agents/verify.d.ts +1 -1
  14. package/dist/bench/metrics.d.ts +35 -35
  15. package/dist/brain/degrading.d.ts +1 -1
  16. package/dist/brain/errors.d.ts +3 -3
  17. package/dist/brain/reasoning.d.ts +2 -2
  18. package/dist/brain/repetition.d.ts +1 -1
  19. package/dist/brain/route-adjudicator.d.ts +8 -1
  20. package/dist/brain/route-adjudicator.js +8 -1
  21. package/dist/brain/status-sink.d.ts +2 -2
  22. package/dist/brain/stream-shared.d.ts +1 -1
  23. package/dist/config/catalog.d.ts +5 -5
  24. package/dist/core/arg-summary.d.ts +4 -4
  25. package/dist/core/ask-class.d.ts +2 -2
  26. package/dist/core/ask-question.d.ts +1 -1
  27. package/dist/core/auto-compaction.d.ts +15 -15
  28. package/dist/core/auto-mode.d.ts +5 -5
  29. package/dist/core/background-agent-store.d.ts +20 -20
  30. package/dist/core/background-shell.d.ts +4 -4
  31. package/dist/core/checkpoint-store.d.ts +35 -27
  32. package/dist/core/context-edit.d.ts +1 -1
  33. package/dist/core/context-guard.d.ts +1 -1
  34. package/dist/core/exec-output-tail.d.ts +6 -6
  35. package/dist/core/file-snapshot-store.d.ts +8 -8
  36. package/dist/core/git-worktree-env.d.ts +3 -3
  37. package/dist/core/governance-codes.d.ts +37 -10
  38. package/dist/core/governance-codes.js +54 -1
  39. package/dist/core/hooks.d.ts +73 -33
  40. package/dist/core/hooks.js +87 -25
  41. package/dist/core/image-downsample.d.ts +1 -1
  42. package/dist/core/locked-config.d.ts +1 -1
  43. package/dist/core/lsp.d.ts +1 -1
  44. package/dist/core/mailbox-store.d.ts +1 -1
  45. package/dist/core/mcp.d.ts +3 -3
  46. package/dist/core/memory-engine/consolidation-driver.d.ts +211 -0
  47. package/dist/core/memory-engine/consolidation-driver.js +427 -0
  48. package/dist/core/memory-engine/consolidation.d.ts +119 -3
  49. package/dist/core/memory-engine/consolidation.js +22 -1
  50. package/dist/core/memory-engine/data-plane.d.ts +1 -1
  51. package/dist/core/memory-engine/distiller.d.ts +550 -0
  52. package/dist/core/memory-engine/distiller.js +598 -0
  53. package/dist/core/memory-engine/dual-root.d.ts +1 -1
  54. package/dist/core/memory-engine/engine.d.ts +143 -10
  55. package/dist/core/memory-engine/engine.js +149 -23
  56. package/dist/core/memory-engine/file-backend.d.ts +14 -2
  57. package/dist/core/memory-engine/file-backend.js +3 -0
  58. package/dist/core/memory-engine/index.d.ts +6 -3
  59. package/dist/core/memory-engine/index.js +4 -2
  60. package/dist/core/memory-engine/layout.js +20 -6
  61. package/dist/core/memory-engine/origin-clearance.d.ts +1 -1
  62. package/dist/core/memory-engine/scope-contract.d.ts +4 -4
  63. package/dist/core/memory-engine/sync-client.d.ts +16 -16
  64. package/dist/core/memory-engine/sync.d.ts +4 -4
  65. package/dist/core/memory-engine/types.d.ts +17 -0
  66. package/dist/core/memory-recall.d.ts +1 -1
  67. package/dist/core/memory.d.ts +2 -2
  68. package/dist/core/permission-rule-consent.d.ts +185 -36
  69. package/dist/core/permission-rule-consent.js +219 -44
  70. package/dist/core/permission-rule-model.d.ts +235 -31
  71. package/dist/core/permission-rule-model.js +105 -35
  72. package/dist/core/permission-rules.d.ts +9 -9
  73. package/dist/core/remote-env.d.ts +8 -8
  74. package/dist/core/roles.d.ts +3 -3
  75. package/dist/core/roles.js +1 -0
  76. package/dist/core/runner/assemble-result.d.ts +2 -2
  77. package/dist/core/runner/compaction-call-options.d.ts +3 -3
  78. package/dist/core/runner/memory-consolidation-driver.d.ts +49 -0
  79. package/dist/core/runner/memory-consolidation-driver.js +60 -0
  80. package/dist/core/runner/memory-consolidation.d.ts +1 -1
  81. package/dist/core/runner/prepare-config-doors.d.ts +3 -3
  82. package/dist/core/runner/prepare-task.d.ts +21 -21
  83. package/dist/core/runner/prepare-task.js +24 -17
  84. package/dist/core/runner/prepare-workspace-restore.d.ts +2 -2
  85. package/dist/core/runner/runtask.d.ts +11 -11
  86. package/dist/core/runner/runtask.js +4 -4
  87. package/dist/core/runner/session-rule-policy.d.ts +1 -1
  88. package/dist/core/runner/teardown-bounded.d.ts +1 -1
  89. package/dist/core/runner/tool-disclosure.d.ts +2 -2
  90. package/dist/core/runner/turn-attachments.d.ts +11 -11
  91. package/dist/core/scheduler.d.ts +5 -5
  92. package/dist/core/secret-env.d.ts +1 -1
  93. package/dist/core/sensitive-path-policy.d.ts +1 -1
  94. package/dist/core/session-policy-store.d.ts +2 -2
  95. package/dist/core/session-reconcile.d.ts +2 -2
  96. package/dist/core/session-store.d.ts +3 -3
  97. package/dist/core/session.d.ts +1 -1
  98. package/dist/core/shutdown-debug.d.ts +2 -2
  99. package/dist/core/side-query.d.ts +2 -2
  100. package/dist/core/spec-contract.d.ts +1 -1
  101. package/dist/core/store-contracts/contract-harness.d.ts +2 -2
  102. package/dist/core/store-contracts/contract-kit-version.d.ts +2 -2
  103. package/dist/core/store-contracts/mailbox-store-contract.d.ts +1 -1
  104. package/dist/core/store-contracts/mailbox-store-contract.js +1 -1
  105. package/dist/core/task-notification.d.ts +5 -5
  106. package/dist/core/task-registry-agent.d.ts +12 -12
  107. package/dist/core/task-registry-monitor.d.ts +1 -1
  108. package/dist/core/task-registry-shared.d.ts +41 -41
  109. package/dist/core/task-registry.d.ts +12 -12
  110. package/dist/core/tool-detach.d.ts +2 -2
  111. package/dist/core/tool-errors.d.ts +3 -3
  112. package/dist/core/tool-policy.d.ts +113 -28
  113. package/dist/core/tool-policy.js +80 -1
  114. package/dist/core/tool-result-budget.d.ts +1 -1
  115. package/dist/core/tool-result-store.d.ts +2 -2
  116. package/dist/core/tools.d.ts +1 -1
  117. package/dist/core/trace.d.ts +26 -23
  118. package/dist/core/types.d.ts +164 -86
  119. package/dist/core/types.js +4 -3
  120. package/dist/core/untrusted-egress.d.ts +1 -1
  121. package/dist/core/untrusted-text.d.ts +7 -7
  122. package/dist/core/wiring-manifest.d.ts +5 -5
  123. package/dist/core/workflow-journal-store.d.ts +14 -14
  124. package/dist/core/workflow-run-store-contract.d.ts +1 -1
  125. package/dist/core/workflow-run-store-contract.js +1 -1
  126. package/dist/core/workflow-run-store.d.ts +4 -4
  127. package/dist/engine/compaction/compaction.d.ts +3 -3
  128. package/dist/engine/compaction/utils.d.ts +2 -2
  129. package/dist/engine/execution-env/kill-tree.d.ts +1 -1
  130. package/dist/engine/execution-env/node-execution-env.d.ts +8 -8
  131. package/dist/engine/harness/agent-harness.d.ts +6 -6
  132. package/dist/engine/harness/messages.d.ts +1 -1
  133. package/dist/engine/harness/types.d.ts +10 -10
  134. package/dist/engine/llm/types.d.ts +14 -14
  135. package/dist/engine/loop/agent-loop.d.ts +3 -3
  136. package/dist/engine/loop/types.d.ts +4 -4
  137. package/dist/engine/lsp/node-lsp-manager.d.ts +2 -2
  138. package/dist/engine/session/import-validate.d.ts +1 -1
  139. package/dist/engine/session/log-digest.d.ts +1 -1
  140. package/dist/engine/session/memory-repo.d.ts +2 -2
  141. package/dist/engine/session/session.d.ts +4 -4
  142. package/dist/fixtures/index.d.ts +4 -4
  143. package/dist/index.d.ts +6 -5
  144. package/dist/index.js +5 -4
  145. package/dist/orchestration/goal.d.ts +1 -1
  146. package/dist/orchestration/run-spec.d.ts +1 -1
  147. package/dist/orchestration/run-workflow-tool.d.ts +12 -12
  148. package/dist/orchestration/workflow-governance.d.ts +4 -4
  149. package/dist/orchestration/workflow-observe.d.ts +1 -1
  150. package/dist/orchestration/workflow-script-runner.d.ts +1 -1
  151. package/dist/orchestration/workflow-script-store.d.ts +9 -9
  152. package/dist/orchestration/workflow-size-guideline.d.ts +1 -1
  153. package/dist/orchestration/workflow-types.d.ts +5 -5
  154. package/dist/orchestration/workflow.d.ts +10 -10
  155. package/dist/prompt-assembly/artifact-store.d.ts +1 -1
  156. package/dist/prompt-assembly/artifact.d.ts +1 -1
  157. package/dist/prompt-assembly/assemble.d.ts +1 -1
  158. package/dist/prompt-assembly/composer.d.ts +2 -2
  159. package/dist/prompt-assembly/epoch.d.ts +2 -2
  160. package/dist/prompt-assembly/event-registry.d.ts +1 -1
  161. package/dist/prompt-assembly/explain.d.ts +3 -3
  162. package/dist/prompt-assembly/tool-catalog.d.ts +1 -1
  163. package/dist/prompt-assembly/turn-snapshot.d.ts +4 -4
  164. package/dist/prompt-assembly/types.d.ts +12 -12
  165. package/dist/prompts/coordinator.d.ts +1 -1
  166. package/dist/prompts/default.d.ts +8 -8
  167. package/dist/prompts/simple-sections.d.ts +3 -3
  168. package/dist/prompts/supervisor.d.ts +2 -2
  169. package/dist/scenarios/full-body.d.ts +3 -3
  170. package/dist/scenarios/scenario-registry.d.ts +1 -1
  171. package/dist/stores/cc/sidecar-transcript.d.ts +3 -3
  172. package/dist/stores/file/fs-atomic.d.ts +2 -2
  173. package/dist/stores/file/index.d.ts +1 -1
  174. package/dist/stores/file/session-store.d.ts +2 -2
  175. package/dist/stores/file/workflow-journal-store.d.ts +4 -4
  176. package/dist/tools/fs/bash-readonly-classifier.d.ts +1 -1
  177. package/dist/tools/fs/encoding.d.ts +4 -4
  178. package/dist/tools/fs/fs-bash.d.ts +3 -3
  179. package/dist/tools/fs/fs-pdf.d.ts +1 -1
  180. package/dist/tools/fs/fs-shared.d.ts +6 -6
  181. package/dist/tools/fs/index.d.ts +2 -2
  182. package/dist/tools/fs/notebook.d.ts +1 -1
  183. package/dist/tools/fs/pdf.d.ts +1 -1
  184. package/dist/tools/fs/read-deny.d.ts +1 -1
  185. package/dist/tools/fs/safety.d.ts +9 -9
  186. package/dist/tools/fs/search.d.ts +2 -2
  187. package/dist/tools/monitor.d.ts +3 -3
  188. package/dist/tools/task-list.d.ts +2 -2
  189. package/dist/tools/web.d.ts +4 -4
  190. package/dist/tools/worktree.d.ts +5 -5
  191. package/package.json +1 -1
  192. package/test/export-surface.snapshot.json +1826 -3
@@ -156,7 +156,7 @@ export declare function screenApproverAttribution(v: unknown): {
156
156
  * denied) so a deny OBSERVER (`Hooks.permissionDenied`) sees the final post-rewrite args, not the
157
157
  * stale un-redacted ones.
158
158
  * - `ask` requests human confirmation; with no approver wired it resolves deterministically to deny
159
- * (headless auto-deny, see `RunnerDeps.onAsk`). `requiresRealApproval: true` (RB-203 codex review,
159
+ * (headless auto-deny, see `RunnerDeps.onAsk`). `requiresRealApproval: true` (RB-203
160
160
  * confirmed P1) additionally refuses to let a BLANKET `onAsk: "allow"` circumvention clear it — that
161
161
  * configuration is "no approver looked at this at all", indistinguishable from headless, for a rule
162
162
  * whose whole point is that a blanket policy setting must not be the thing that clears it (see
@@ -200,7 +200,8 @@ export type PermissionResult = {
200
200
  /** #144 disclosure (additive): a persisted allow rule MATCHED this call but could not clear the
201
201
  * ask, because the ask is MANDATED (operator shellGate:"always", or the tool's own
202
202
  * egress/irreversibility marks) rather than a classifier's hesitation — "allow rules silence
203
- * the classifier's questions, never a mandated one". Carries the matched rule text so a
203
+ * the classifier's questions, never a mandated one". Carries the matched rule text a covering
204
+ * SET (conjunction arm) joins up to five sanitized member texts with an "and N more" tail — so a
204
205
  * consumer (approval card, wire frame) can tell the person their rule is alive, just outranked.
205
206
  * Absent ⇒ no rule matched, or the ask was cleared normally. */
206
207
  persistedRuleShadowed?: string;
@@ -235,6 +236,16 @@ export type PermissionResult = {
235
236
  * about itself is not evidence). Carried onto the approval request by the gate's own ask mint
236
237
  * site. Display/reconciliation metadata, never adjudication input. */
237
238
  ruleEvidence?: AskRuleEvidence;
239
+ /** design/375 §5.2②: the per-segment coverage table behind this surviving ask — which segments
240
+ * of a compound an eligible persisted rule already admits, read off the SAME store read that
241
+ * adjudicated the call (snapshot = adjudication snapshot). ENGINE-STAMPED at the gate's one
242
+ * stamp point whenever the persisted-rule lane reported one (overwriting a policy's claim);
243
+ * consumed only by the offer factory, which puts on the batch offer exactly the segments this
244
+ * table calls uncovered. Absent ⇒ all segments read as uncovered (over-offer, the safe way).
245
+ * Same display-only posture as {@link persistedRuleShadowed}: never adjudication input, and a
246
+ * value nobody stamps over can only shrink or annotate the card's ADVISORY offers — every
247
+ * offered rule still passes the validator at redemption. */
248
+ segmentCoverage?: readonly import("./permission-rule-model.js").SegmentCoverage[];
238
249
  } | {
239
250
  action: "deny";
240
251
  updatedInput?: unknown;
@@ -299,7 +310,9 @@ export interface AskRuleEvidence {
299
310
  /** Present iff {@link orgRule} is not. */
300
311
  readonly orgRuleAbsent?: AskEvidenceAbsence;
301
312
  /** The add dots of the PERSONAL allow rule that matched this call but did not clear the ask (the
302
- * #144 shadowed arm). The dots are the rule's durable identity unlike
313
+ * #144 shadowed arm) the deduplicated UNION across the covering set when the conjunction arm
314
+ * matched, so the array does not belong to one rule on those asks. The dots are the rule's durable
315
+ * identity — unlike
303
316
  * {@link AskRequest.persistedRuleShadowed}, which is a sanitized, length-capped DISPLAY value and
304
317
  * deliberately not an identity channel. A rule is a set of adds (concurrent approvals on one text
305
318
  * each redeem their own dot), so this is an array by construction: render/reconcile the entries as
@@ -425,7 +438,7 @@ export declare function checkToolPolicyProjection(projection: Pick<ToolPolicyPro
425
438
  * load-bearing — the chain digest binds the ORDERED topology (layer boundaries included), so an
426
439
  * opaque layer's position is part of what a resume re-supply must reproduce.
427
440
  *
428
- * F-012 (codex round 12): each entry ALSO carries the serializable DECISION-CHAIN METADATA posture —
441
+ * F-012: each entry ALSO carries the serializable DECISION-CHAIN METADATA posture —
429
442
  * whether the layer was classifier-armed (`autoModeArmed`) and its durable-mandate / content-mandate
430
443
  * flags. The digest binds these, so a cross-process resume re-supplying a same-shape chain whose
431
444
  * projections match but whose classifier was DROPPED (or whose mandate posture changed) — a
@@ -563,6 +576,13 @@ export declare function createAllowDenyPolicy(opts: {
563
576
  * Human-in-the-loop approval for selected tools. Tools in `requireApproval` call `approve(req)` and
564
577
  * are allowed only if it resolves true; tools in `deny` are always blocked; everything else is allowed
565
578
  * (override with `denyByDefault: true` to allow only `requireApproval` + an explicit `autoAllow`).
579
+ *
580
+ * This seat receives a bare `ToolCallRequest`, NOT the ask surface: the decision settles in-place
581
+ * inside the policy, no AskRequest is minted and `resolveAsk` is never entered — so none of the
582
+ * ask-side approval members (`hasBidiControls`, `boundInputHash`, `preview`, `riskAxes`, …) exist
583
+ * here. A surface rendering the command text for a human on this seat must render through
584
+ * {@link import("./permission-rule-model.js").renderUntrustedCommandText} (the display baseline)
585
+ * or run its own screen — absence of the warning bit is "not on this surface", never "clean".
566
586
  */
567
587
  export declare function createApprovalPolicy(opts: {
568
588
  /** Tools that need an approval decision. */
@@ -614,7 +634,7 @@ export declare function combinePolicies(...policies: ToolPolicy[]): ToolPolicy;
614
634
  * gate at all; the same substitution makes `rm -rf ~/.ai-agent/sessions` a way to erase the audit trail.
615
635
  */
616
636
  /**
617
- * RB-153 (server [1708]): EXPORTED. A downstream governance layer keeps its own copy of this list so its
637
+ * RB-153: EXPORTED. A downstream governance layer keeps its own copy of this list so its
618
638
  * per-command ask-list gates the same tools core does, and it had no way to read the real one — its guard
619
639
  * had to probe a hand-written candidate list of names, which stays green for any name nobody thought to
620
640
  * put in it. The list itself is the fact; publishing it removes the guess.
@@ -721,7 +741,7 @@ export declare function findUnverifiableRecursiveDelete(command: string, safeVar
721
741
  * (`decisionReason: "classifier"`, fully audited), an `allow` verdict still requires a live classifier to
722
742
  * have actually cleared it, and "no classifier configured" still means deny.
723
743
  *
724
- * codex review (confirmed P1): the one piece of NEW plumbing this DOES need — `requiresRealApproval: true`
744
+ * A review-confirmed P1 gap: the one piece of NEW plumbing this DOES need — `requiresRealApproval: true`
725
745
  * (see {@link PermissionResult}'s `ask` doc) — closes a real gap the first cut of this fix missed. A
726
746
  * deployment using the documented `onAsk: "allow"` circumvention mode (sandbox-is-the-boundary posture, the
727
747
  * shape a sandboxed autonomy container runs) would otherwise have this rule's `ask` rubber-stamped by that blanket
@@ -790,8 +810,11 @@ export interface AskDelegationProvenance {
790
810
  export interface AskRequest {
791
811
  toolName: string;
792
812
  /** #144: a persisted allow rule MATCHED this call but could not clear the ask (mandated — see
793
- * {@link PermissionResult}'s ask arm). The matched rule text, so the approval card renders "your
794
- * rule is alive, just outranked" instead of leaving the person to regex the message prose. */
813
+ * {@link PermissionResult}'s ask arm). The matched rule text — or, when a covering SET admitted the
814
+ * command segment-wise (the conjunction arm), a comma-joined display list of up to five sanitized
815
+ * rule texts with an "and N more" tail — so the approval card renders "your rule is alive, just
816
+ * outranked" instead of leaving the person to regex the message prose. A DISPLAY value, not an
817
+ * identity channel: per-rule identity travels on {@link AskRuleEvidence.personalRuleDots}. */
795
818
  persistedRuleShadowed?: string;
796
819
  /** backlog #239: WHY the reversibility probe did not clear this call — the account of the tighten the
797
820
  * card was showing WITHOUT until now (the gate knew the cause and dropped it at this seam, leaving the
@@ -819,7 +842,7 @@ export interface AskRequest {
819
842
  toolCallId: string;
820
843
  /** The (post-rewrite) args the tool would run with. */
821
844
  args: unknown;
822
- /** [1245] — the tool's approval display projection of {@link args} (AgentTool.approvalPreview),
845
+ /** the tool's approval display projection of {@link args} (AgentTool.approvalPreview),
823
846
  * clamped + control-char-sanitized by the gate. **UNTRUSTED, ADVISORY display metadata**: render
824
847
  * with contextual escaping and show {@link args} alongside — the preview can misrepresent the
825
848
  * executable action and never replaces args. Never adjudication input. */
@@ -834,24 +857,35 @@ export interface AskRequest {
834
857
  * invoke its approver function directly. */
835
858
  readonly boundInputHash?: string;
836
859
  /**
837
- * design/179 §4 — the persistable allow-rule forms this exact call could be covered by, so a surface can
838
- * offer "allow, and stop asking me this" with something concrete behind it. Present only when a
839
- * persisted allow-rule lane is armed AND the call is one the lane can speak for (a compound, a
840
- * redirection or a substitution yields NO suggestion, which is the honest answer rather than an option
841
- * that would be refused on redemption) AND the ask is one a persisted rule could actually clear — a
842
- * mandated ask (operator shellGate:"always", the tool's own egress/irreversibility marks, a
860
+ * design/179 §4, re-formed by design/375 §3.1 (BREAKING this seat REPLACES the retired
861
+ * `ruleSuggestions` key, which is never emitted again; the new name is the safe failure posture for
862
+ * the hard-cut window: a consumer that has not upgraded simply renders no "don't ask again" row,
863
+ * fail toward asking, instead of mis-rendering a consent surface through its old parser) the
864
+ * persistable allow-rule OFFERS this exact call could be covered by, so a surface can render
865
+ * "allow, and stop asking me this" with something concrete behind it. Present only when a persisted
866
+ * allow-rule lane is armed AND the call is one the lane can speak for (a redirection or a
867
+ * substitution yields NO offer, which is the honest answer rather than an option that would be
868
+ * refused on redemption) AND the ask is one a persisted rule could actually clear — a mandated ask
869
+ * (operator shellGate:"always", the tool's own egress/irreversibility marks, a
843
870
  * `requiresRealApproval` demand) and an ask carrying {@link persistedRuleShadowed} offer none.
844
871
  *
845
- * CONTRACT — array order is display order, narrowest first: the EXACT form is always index 0, a
846
- * broader reviewed PREFIX form (at most one) follows. Basis 2. Selection indices and redemption
847
- * tickets are keyed against this order, and the durable park row carries the same array under the
848
- * same contract.
872
+ * CONTRACT — see {@link import("./permission-rule-model.js").RuleOffer} for the closed union, the
873
+ * per-element parse/degrade rules and the original-index-keying obligation. Array order is display
874
+ * order: at most 2 offers, the whole-string EXACT single always index 0 when present, the BATCH
875
+ * offer (a compound's per-segment conjunction — choosing it is one yes to all of its 1..5 rules at
876
+ * once) always last. Selection indices (`confirmRuleApproval`'s `selectedOffer`) are keyed
877
+ * against this order; redemption TICKETS are NOT — they key on the consent record's flat
878
+ * CANDIDATE list (a batch's members each carry their own ticket), so a chosen offer is redeemed
879
+ * via `redeemRuleBatch`, never by indexing a ticket array with an offer index. The durable park
880
+ * row carries the same array under the same contract.
849
881
  *
850
882
  * ADVISORY display metadata, never adjudication input, and never a rule by itself: minting one is a
851
883
  * separate act that goes through the approval-record protocol, so a surface that ignores this field
852
- * changes nothing about what is allowed.
884
+ * changes nothing about what is allowed. The AUTHORITATIVE render source for a consent card is the
885
+ * PREPARED approval record's own offers; this member is the ask-time projection, which may drift
886
+ * from it across a coverage change — harmlessly, since the record is what gets confirmed.
853
887
  */
854
- readonly ruleSuggestions?: readonly import("./permission-rule-model.js").RuleSuggestion[];
888
+ readonly ruleOffers?: readonly import("./permission-rule-model.js").RuleOffer[];
855
889
  /** The human/model-readable prompt explaining what needs approval. */
856
890
  message: string;
857
891
  /** Runner-filled source identity (design S1d §1): the end-user {@link AskRequest} issuing task's
@@ -868,7 +902,7 @@ export interface AskRequest {
868
902
  * Runner mints it); a top-level caller MAY continue its own session id via `TaskSpec.sessionId`, so this
869
903
  * is NOT a global "never forgeable" run id — aggregation only aggregates delegated workers, the context
870
904
  * where it holds (design S1d §1.3). `readonly` + same sync-path-only / P-a caveat as `principal`.
871
- * Undefined ⇒ no source task id. **NOT a subagent discriminator** ([1546] MED-1: the HOST's own asks
905
+ * Undefined ⇒ no source task id. **NOT a subagent discriminator** (the HOST's own asks
872
906
  * carry it too — it is always the issuing task's session id); "from a background agent" attribution
873
907
  * keys on {@link fromSubagent}, never on this field's presence. */
874
908
  readonly sourceTaskId?: string;
@@ -892,7 +926,7 @@ export interface AskRequest {
892
926
  * non-fork facet of `effectiveDelegationFacts`, serving the authority/context faces, excludes it), but
893
927
  * its interaction contract is one-shot — "report once and stop … no waiting for the user"
894
928
  * (FORK_DIRECTIVE_FRAME) — so a stop-and-wait refusal would instruct it to do the impossible
895
- * (codex adversarial round, confirmed). NOT the same fact as {@link fromSubagent} either: that is
929
+ * (confirmed). NOT the same fact as {@link fromSubagent} either: that is
896
930
  * the design/153 attribution discriminator (`parentTaskId` presence), which a directly-started
897
931
  * workflow's children lack even though refusals must still speak the child posture to them.
898
932
  * `readonly`, trusted (internals never ride a TaskSpec), sync-path-only like its siblings.
@@ -903,7 +937,7 @@ export interface AskRequest {
903
937
  * - `isDelegatedChild` — REFUSAL POSTURE: "no user turn ever lands in this run's transcript";
904
938
  * drives which deny text the model is told (stop-and-wait vs adapt-or-report), forks included. */
905
939
  readonly isDelegatedChild?: true;
906
- /** RB-203 (codex review, confirmed P1) — carried from the originating {@link PermissionResult}'s ask
940
+ /** RB-203 (confirmed P1) — carried from the originating {@link PermissionResult}'s ask
907
941
  * variant of the same name: `true` ⇒ {@link resolveAsk} must not let a blanket `onAsk: "allow"`
908
942
  * clear this ask (see that field's doc). `readonly`, filled by the gate from the decision, never a
909
943
  * caller/worker-settable field. */
@@ -925,6 +959,43 @@ export interface AskRequest {
925
959
  * so this — like `principal`/`sourceTaskId` — exists only on the synchronous resolution path.
926
960
  * See {@link AskDelegationProvenance} for the trust posture of each member. */
927
961
  readonly delegation?: AskDelegationProvenance;
962
+ /**
963
+ * PRESENCE ONLY: the command/argument text this ask is about carries at least one DIRECTIONAL
964
+ * format control ({@link BIDI_CONTROL_RE} — the closed set: U+061C, U+200E/U+200F, the embeddings
965
+ * and overrides U+202A–U+202E, the isolates U+2066–U+2069). Those characters change nothing about
966
+ * what EXECUTES and everything about what a terminal or a card SHOWS: bytes that run
967
+ * `rm -rf /` can display as a benign line, which is precisely the deception an approval surface
968
+ * cannot afford to render unannotated.
969
+ *
970
+ * A WARNING BIT, not a verdict and not a transform. Core does NOT strip, reorder, refuse or rewrite
971
+ * anything on account of it — {@link args} is delivered byte-identical either way, and the decision
972
+ * stays the approver's. What a surface owes the person is a visible "the text below can display
973
+ * differently from what it runs" and a rendering that neutralizes the controls (see
974
+ * {@link import("./permission-rule-model.js").renderUntrustedCommandText} for the display baseline).
975
+ *
976
+ * Read it as PRESENCE-or-nothing: the field is either `true` or ABSENT. It is never written `false`,
977
+ * because absence means "not detected", which honestly covers both "clean" and "the bounded scan did
978
+ * not reach it" (a pathologically deep or huge argument graph stops at the scan budget) — a `false`
979
+ * would claim a proof the scan does not offer. Judged over the EXECUTING argument snapshot and the
980
+ * tool's own {@link preview} projection, i.e. the payload; {@link message} is deliberately out of
981
+ * scope (engine/policy-composed prose, not the thing that runs).
982
+ *
983
+ * Filled at the `resolveAsk` chokepoint, beside {@link boundInputHash} — every wired `onAsk`
984
+ * approver call crosses it, so an ask mint site added later is covered by construction. Optional
985
+ * on the type because a deployment may invoke its approver function directly.
986
+ *
987
+ * SCOPE, stated so absence is not read as a clean bill on the other routes: this is the
988
+ * SYNCHRONOUS `onAsk` ask, like `principal`/`sourceTaskId` — the closure claim is over AskRequest
989
+ * mint sites, not over every approval callback. Two human-decision routes never receive it:
990
+ * ① a durable park/suspend never invokes `onAsk`, so a parked approval row carries no twin of
991
+ * this bit today — an inbox rendering `RiskDescriptor` must run its own screen (or the display
992
+ * baseline above) rather than infer "no bit, no problem"; ② {@link createApprovalPolicy}'s
993
+ * `approve` seat resolves in-place inside the policy and mints no AskRequest at all — its
994
+ * callback receives a bare `ToolCallRequest` (no bit, no {@link boundInputHash}, none of this
995
+ * surface), so a deployment doing HITL through that seat must render through
996
+ * {@link import("./permission-rule-model.js").renderUntrustedCommandText} or run its own screen.
997
+ */
998
+ readonly hasBidiControls?: true;
928
999
  }
929
1000
  /**
930
1001
  * How an `ask` decision is resolved when a policy/hook requests human confirmation (design/37):
@@ -933,7 +1004,7 @@ export interface AskRequest {
933
1004
  * - `"allow"` — auto-approve every `ask` (e.g. a trusted batch run).
934
1005
  * - a function — await an operator's decision (true=allow, false=deny). `signal` fires when the task
935
1006
  * aborts; race your wait against it so an unanswered ask is released at the deadline, not hung.
936
- * G1 three-value ([871]①): the function may also return `"unavailable"` — an affirmative "no operator
1007
+ * G1 three-value: the function may also return `"unavailable"` — an affirmative "no operator
937
1008
  * is reachable for THIS ask right now" (judged PER-ASK inside the callback, not at wire time). It is a
938
1009
  * ROUTING verdict, not a decision: the gate re-routes the ask onto the durable park leg (same behavior
939
1010
  * as if no live approver were wired — checkpoint + suspend, the 1.293 fallback), and when no park
@@ -948,7 +1019,7 @@ export interface AskRequest {
948
1019
  */
949
1020
  export type OnAsk = "deny" | "allow" | ((req: AskRequest, signal?: AbortSignal) => AskOutcome | Promise<AskOutcome>);
950
1021
  /**
951
- * What an `onAsk` approver may return ([1458]/[1462] object arm):
1022
+ * What an `onAsk` approver may return (object arm):
952
1023
  * - `boolean` — plain verdict (true=allow, false=deny). STRICT fold (RB-482 #17, server bridge
953
1024
  * zero-ripple confirmed): only the literal `true` approves; an out-of-contract truthy from an
954
1025
  * untyped bridge is a fail-closed deny naming the defect (the historical truthy leniency was a
@@ -982,7 +1053,7 @@ export type OnAsk = "deny" | "allow" | ((req: AskRequest, signal?: AbortSignal)
982
1053
  * input), relayed to the model so it can follow the person's direction instead of guessing. In-place
983
1054
  * conditions, all screened at {@link resolveAsk}:
984
1055
  * - meaningful ONLY on a deny (`allow: false`); on an allow the seat is NEVER READ (accepted and
985
- * unused, literally — codex round: a junk value or a throwing accessor riding an approval cannot
1056
+ * unused, literally: a junk value or a throwing accessor riding an approval cannot
986
1057
  * destroy the approval). Same net posture as the durable leg, where a `reason` on an `allow` row
987
1058
  * is never rendered;
988
1059
  * - UNTRUSTED input (a human's free text riding a callback): it reaches the model only inside a
@@ -1128,7 +1199,7 @@ export declare function coreMintedResolutionOf(d: unknown, call: {
1128
1199
  */
1129
1200
  export type ResolvedAsk = PermissionResult & {
1130
1201
  approverUnavailable?: true;
1131
- /** [1462] r5 — the EXACT data snapshot the approver was shown (the `structuredClone` of the
1202
+ /** The EXACT data snapshot the approver was shown (the `structuredClone` of the
1132
1203
  * presented args). On a plain approval (no edit) the gate EXECUTES this snapshot, not the original
1133
1204
  * object: a stateful getter or an external alias must not be able to make the executed action
1134
1205
  * differ from the approved one (shown == executed, by construction). Set only on the
@@ -1149,6 +1220,20 @@ export type ResolvedAsk = PermissionResult & {
1149
1220
  * frame), so a consumer classifies a refusal by code instead of parsing its text. */
1150
1221
  resolution?: AskDenyResolution;
1151
1222
  };
1223
+ /**
1224
+ * Does any string reachable in `value` carry a {@link BIDI_CONTROL_RE} member? Bounded, cycle-safe,
1225
+ * and never throwing — the one caller is on the approval path, where a scan that failed must degrade
1226
+ * to "not detected" rather than turn an ask into an error (the `deliverEngineNotice` posture: a
1227
+ * derived disclosure must never become the failure of the thing it describes).
1228
+ *
1229
+ * OBJECT KEYS are scanned as well as values: a key is displayed text too, and an argument object
1230
+ * `{ "cmd<RLO>": … }` renders its own reordering in any card that prints the shape (the marker
1231
+ * is spelled out here on purpose — a literal one in this comment would reorder the comment).
1232
+ *
1233
+ * Not a public export: the contract is the {@link AskRequest.hasBidiControls} bit, and a second
1234
+ * spelling of "does this carry bidi" on the public surface would be one more thing to keep in step.
1235
+ */
1236
+ export declare function carriesBidiControls(value: unknown): boolean;
1152
1237
  /**
1153
1238
  * Resolve an `ask` decision to a terminal `allow`/`deny` via {@link OnAsk}. Centralizes the headless
1154
1239
  * auto-deny default, fail-closed error handling, and stable deny reasons so every ask site is
@@ -900,6 +900,78 @@ export function coreMintedResolutionOf(d, call) {
900
900
  return undefined;
901
901
  return isAskDenyResolution(v.resolution) ? v.resolution : undefined;
902
902
  }
903
+ const BIDI_CONTROL_RE = /[\u061C\u200E\u200F\u202A-\u202E\u2066-\u2069]/u;
904
+ const BIDI_SCAN_MAX_NODES = 5_000;
905
+ const BIDI_SCAN_MAX_CHARS = 1_000_000;
906
+ export function carriesBidiControls(value) {
907
+ let budget = BIDI_SCAN_MAX_NODES;
908
+ let charBudget = BIDI_SCAN_MAX_CHARS;
909
+ const seen = new WeakSet();
910
+ const scan = (s) => {
911
+ if (charBudget <= 0)
912
+ return false;
913
+ const window = s.length <= charBudget ? s : s.slice(0, charBudget);
914
+ charBudget -= window.length;
915
+ return BIDI_CONTROL_RE.test(window);
916
+ };
917
+ const walk = (v) => {
918
+ if (budget-- <= 0)
919
+ return false;
920
+ if (typeof v === "string")
921
+ return scan(v);
922
+ if (typeof v !== "object" || v === null)
923
+ return false;
924
+ if (seen.has(v))
925
+ return false;
926
+ seen.add(v);
927
+ if (Array.isArray(v)) {
928
+ for (const el of v) {
929
+ if (walk(el))
930
+ return true;
931
+ if (budget <= 0)
932
+ return false;
933
+ }
934
+ return false;
935
+ }
936
+ if (v instanceof Map) {
937
+ for (const [k, val] of v) {
938
+ if (walk(k) || walk(val))
939
+ return true;
940
+ if (budget <= 0)
941
+ return false;
942
+ }
943
+ return false;
944
+ }
945
+ if (v instanceof Set) {
946
+ for (const el of v) {
947
+ if (walk(el))
948
+ return true;
949
+ if (budget <= 0)
950
+ return false;
951
+ }
952
+ return false;
953
+ }
954
+ for (const k in v) {
955
+ if (budget-- <= 0)
956
+ return false;
957
+ if (!Object.prototype.hasOwnProperty.call(v, k))
958
+ continue;
959
+ if (scan(k))
960
+ return true;
961
+ if (walk(v[k]))
962
+ return true;
963
+ if (budget <= 0 || charBudget <= 0)
964
+ return false;
965
+ }
966
+ return false;
967
+ };
968
+ try {
969
+ return walk(value);
970
+ }
971
+ catch {
972
+ return false;
973
+ }
974
+ }
903
975
  export async function resolveAsk(req, onAsk, signal) {
904
976
  const r = await resolveAskArms(req, onAsk, signal);
905
977
  if (r.action === "deny" && isAskDenyResolution(r.resolution))
@@ -954,7 +1026,14 @@ async function resolveAskArms(req, onAsk, signal) {
954
1026
  settledBy: "aborted",
955
1027
  };
956
1028
  }
957
- ok = await onAsk({ ...req, boundInputHash: boundInputHashOf(presented.value), args: approverView.value }, signal);
1029
+ const bidi = carriesBidiControls(presented.value) || carriesBidiControls(req.preview);
1030
+ const { hasBidiControls: _carried, ...bare } = req;
1031
+ ok = await onAsk({
1032
+ ...bare,
1033
+ boundInputHash: boundInputHashOf(presented.value),
1034
+ args: approverView.value,
1035
+ ...(bidi ? { hasBidiControls: true } : {}),
1036
+ }, signal);
958
1037
  }
959
1038
  catch (err) {
960
1039
  return {
@@ -27,7 +27,7 @@ export interface AggregateBudgetOptions {
27
27
  store?: ToolResultStore;
28
28
  /** Namespaces the stable ref so an offloaded result is globally addressable. */
29
29
  sessionId: string;
30
- /** service [398] C8: fired once per result actually capped (offloaded OR degraded to a truncation
30
+ /** C8: fired once per result actually capped (offloaded OR degraded to a truncation
31
31
  * preview). `storeFallback` = the model LOST read-back for this result (store.put failed / no store)
32
32
  * — the silent-degradation face this callback exists for. Must not throw (caller-side contract). */
33
33
  onCapped?: (info: {
@@ -310,7 +310,7 @@ export interface ToolResultSlice {
310
310
  }
311
311
  /** Default in-memory store (no cross-process durability); write-once per ref.
312
312
  *
313
- * Blackboard 2026-07-03 (clay dogfood: `ReadToolResult(ref)` came back empty for a workflow child's
313
+ * Field-reported (`ReadToolResult(ref)` came back empty for a workflow child's
314
314
  * offloaded output): the Runner now shares ONE instance across its tasks (instead of a per-task
315
315
  * island), so a parent can deref a child's ref within the process. A shared long-lived instance
316
316
  * needs a bound — `maxTotalChars` FIFO-evicts the oldest refs past the cap (an evicted deref lands
@@ -351,7 +351,7 @@ export declare class InMemoryToolResultStore implements ToolResultStore {
351
351
  export declare class RunnerSharedToolResultStore extends InMemoryToolResultStore {
352
352
  }
353
353
  /**
354
- * Per-task view over the Runner-shared fallback (impl-review 2026-07-03, codex 2 BLOCKER):
354
+ * Per-task view over the Runner-shared fallback (impl-review 2026-07-03):
355
355
  * 1. **Tenant isolation**: refs are namespaced by the task's trust scope (principal), so tenant B's
356
356
  * `ReadToolResult` can never deref tenant A's ref — the per-task-instance era made this isolation
357
357
  * implicit; sharing one instance re-opened it. Same-scope cross-task deref (the A2 fix's whole
@@ -2,7 +2,7 @@ import type { TSchema } from "typebox";
2
2
  import type { AgentTool } from "../internal/harness.js";
3
3
  import type { ToolExecuteContext, ToolSpec } from "./types.js";
4
4
  /**
5
- * E1(黑板 [1937],test/server/cli 三方交叉核实): wrap an already-built failure message string into the
5
+ * Wrap an already-built failure message string into the
6
6
  * `ToolReturn` shape that actually signals failure. A bare `string` return is UNCONDITIONALLY
7
7
  * `isError:false` (see `ToolReturn.isError`'s own doc comment) — the toolkit-wide survey found 120+
8
8
  * sites across fs/web/sql/scheduler/worktree/etc. that build a correct `"Error (...): ..."` message
@@ -87,11 +87,11 @@ export type TraceEvent = {
87
87
  hash: string;
88
88
  }>;
89
89
  /**
90
- * Manifest v2 (campaign S1, ADDITIVE — the v1 `blocks` id wordlist above is a downstream
90
+ * Manifest v2 (stage S1, ADDITIVE — the v1 `blocks` id wordlist above is a downstream
91
91
  * contract and stays as-is): the compiled-IR view, one entry per pack section, carrying the
92
92
  * assembly protocol's semantic/lifecycle dimensions (slot/carrier/cadence/cacheClass).
93
93
  * Absent on the legacy free-form provider path (single opaque block, no section structure).
94
- * `contentHash` (digest tiering, [1050]②): UNSALTED full sha256, present ONLY on
94
+ * `contentHash` (digest tiering): UNSALTED full sha256, present ONLY on
95
95
  * operator-declared sections (typed `stableBlocks` lane) — the center-publish ↔ manifest
96
96
  * reconciliation anchor. Core-native sections stay salted-only (`hash`).
97
97
  */
@@ -106,7 +106,7 @@ export type TraceEvent = {
106
106
  contentHash?: string;
107
107
  }>;
108
108
  /**
109
- * Campaign S2 (additive): the mounted tools plane — per-tool contract identity, durable-replay
109
+ * S2 (additive): the mounted tools plane — per-tool contract identity, durable-replay
110
110
  * aliases, presentation-invariant shape digest and full wire-schema digest (initial mount face;
111
111
  * deferred activations announce via tools_delta). Content-addressed digests (schemas are code).
112
112
  */
@@ -128,7 +128,7 @@ export type TraceEvent = {
128
128
  toolDisclosure?: ToolDisclosureManifest;
129
129
  ts: number;
130
130
  } | {
131
- /** design/148 S3 (codex F3) — a mid-run identity refresh (deferred materialization / RB-31
131
+ /** design/148 S3 — a mid-run identity refresh (deferred materialization / RB-31
132
132
  * adoption / finalize thinking flip): the trace stream must never keep claiming a superseded
133
133
  * prefix identity. Digests only, never prompt text. */
134
134
  kind: "prompt.snapshot_changed";
@@ -204,7 +204,7 @@ export type TraceEvent = {
204
204
  ts: number;
205
205
  } | {
206
206
  /**
207
- * Config-catalog manifest (clay ruling 2026-07-19) — the run's EFFECTIVE task-launch config
207
+ * Config-catalog manifest (ruled 2026-07-19) — the run's EFFECTIVE task-launch config
208
208
  * with per-field provenance (default / spec / derived / host-declared), emitted once right
209
209
  * after `prompt.assembled`. Kills the "which layer set this knob" archaeology: every run
210
210
  * self-reports its true scale, and host-layer overrides arrive as DECLARED entries
@@ -266,7 +266,7 @@ export type TraceEvent = {
266
266
  * loop breathes, so lateness this size means the environment starved the process (run89: an
267
267
  * abandoned compile group froze the loop 646s; the 1200s hard abort landed 683s late). */
268
268
  timerLatenessMs?: number;
269
- /** RB-20 (TB [991] b-form): the run was stopped by the SYNCHRONOUS turn-boundary walltime
269
+ /** RB-20 (TB b-form): the run was stopped by the SYNCHRONOUS turn-boundary walltime
270
270
  * backstop, not the hard-abort timer — i.e. the deadline had passed but the setTimeout leg
271
271
  * had not fired yet (starved event loop). Pairs with `timerLatenessMs` for attribution. */
272
272
  walltimeSyncBackstop?: boolean;
@@ -314,7 +314,7 @@ export type TraceEvent = {
314
314
  callStartedAt?: number;
315
315
  /** ABSENT when THIS call's serving model was unpriced (RB-368 — no fabricated 0). */
316
316
  costMicroUsd?: number;
317
- /** TB telemetry B1 (service [397]): the NORMALIZED finish reason of this call (`StopReason`:
317
+ /** TB telemetry B1: the NORMALIZED finish reason of this call (`StopReason`:
318
318
  * `"stop"`/`"length"`/`"toolUse"`/`"error"`/`"aborted"`). Post-normalization (Anthropic
319
319
  * `end_turn`/`stop_sequence`→`"stop"`, `max_tokens`→`"length"`). */
320
320
  stopReason?: string;
@@ -345,7 +345,7 @@ export type TraceEvent = {
345
345
  effect?: ToolEffect;
346
346
  ts: number;
347
347
  } | {
348
- /** Degenerate-repetition detector telemetry (clay 2026-07-10, 2e1c161 observability): a model
348
+ /** Degenerate-repetition detector telemetry (2026-07-10, 2e1c161 observability): a model
349
349
  * stream was CUT for a degenerate loop, or a repetition landed in a detection window but was
350
350
  * SPARED by a structural allowance (code-line shape / divider run). One event per detector hit. */
351
351
  kind: "repetition.detected";
@@ -386,17 +386,20 @@ export type TraceEvent = {
386
386
  ts: number;
387
387
  } | {
388
388
  /**
389
- * design/179 — a persisted allow rule resolved an ask, so no person was asked and no checkpoint was
390
- * minted. The gate has an attribution channel for the deny side and had none for the allow side;
391
- * this is that channel, because "why did this run without asking me?" must be answerable. Metadata
392
- * only (the rule text is the canonical rule spelling, never the command's arguments).
389
+ * design/179 — a persisted allow-rule COVERAGE resolved an ask, so no person was asked and no
390
+ * checkpoint was minted. The gate has an attribution channel for the deny side and had none for
391
+ * the allow side; this is that channel, because "why did this run without asking me?" must be
392
+ * answerable. Metadata only (each rule text is the canonical rule spelling, never the command's
393
+ * arguments). version 2 (design/375, BREAKING): `rule: string` became `rules: string[]` — a
394
+ * compound may now be admitted by the CONJUNCTION of per-segment rules, and the attribution names
395
+ * every rule the decision stood on (a single-rule resolution is a one-element array).
393
396
  */
394
397
  kind: "permission.persisted_rule_allowed";
395
- version: 1;
398
+ version: 2;
396
399
  taskId: string;
397
400
  toolName: string;
398
401
  toolCallId: string;
399
- rule: string;
402
+ rules: readonly string[];
400
403
  ts: number;
401
404
  } | {
402
405
  /**
@@ -571,12 +574,12 @@ export type TraceEvent = {
571
574
  trigger: "auto" | "manual";
572
575
  ts: number;
573
576
  } | {
574
- /** MF-18 修① [496]③ — a compaction pass FAILED: either a burned summary attempt threw
577
+ /** MF-18 修① — a compaction pass FAILED: either a burned summary attempt threw
575
578
  * (summarizer error / empty summary / oversized-summary guard; counted toward the §17.4
576
579
  * breaker) or a manual /compact was drained against an ALREADY-OPEN breaker (no attempt).
577
580
  * Before this frame the only evidence was `Runner.onError(phase:"compaction")` — a service
578
581
  * that acked the manual verb (202) but saw no `compacted` event had ZERO stream/trace signal
579
- * about why (the [496]③ fingerprint). */
582
+ * about why (the fingerprint). */
580
583
  kind: "compaction.failed";
581
584
  version: 1;
582
585
  taskId: string;
@@ -586,7 +589,7 @@ export type TraceEvent = {
586
589
  reason: string;
587
590
  ts: number;
588
591
  } | {
589
- /** MF-18 修① [496]③ — a manual /compact request was PROCESSED but MOOTED: the loop is dying
592
+ /** MF-18 修① — a manual /compact request was PROCESSED but MOOTED: the loop is dying
590
593
  * (abort fired / durable suspend / plan review in flight) so no compaction can run. The
591
594
  * compact() promise resolves "mooted"; this frame is the trace-side counterpart.
592
595
  * `reason:"cancelled"` (additive) — the caller withdrew an IN-FLIGHT request via
@@ -601,7 +604,7 @@ export type TraceEvent = {
601
604
  reason: "task_ending" | "cancelled";
602
605
  ts: number;
603
606
  } | {
604
- /** MF-18 修① [496]③ — a manual /compact attempt ran but found NOTHING to compact (no valid
607
+ /** MF-18 修① — a manual /compact attempt ran but found NOTHING to compact (no valid
605
608
  * cut point / empty history): `maybeCompact` returned a structural `{compacted:false}` on a
606
609
  * forced pass. Manual-only by design — an auto pass no-ops at nearly every boundary (spam). */
607
610
  kind: "compaction.noop";
@@ -609,7 +612,7 @@ export type TraceEvent = {
609
612
  taskId: string;
610
613
  ts: number;
611
614
  } | {
612
- /** MF-18 修① [496]③ — a manual /compact was processed while compaction is DISABLED by the
615
+ /** MF-18 修① — a manual /compact was processed while compaction is DISABLED by the
613
616
  * task spec (`compaction.enabled:false`): the request can never be honored this run. */
614
617
  kind: "compaction.disabled";
615
618
  version: 1;
@@ -667,7 +670,7 @@ export type TraceEvent = {
667
670
  taskId: string;
668
671
  ts: number;
669
672
  } | {
670
- /** MF-18 修② [496]③ — the summarization INPUT was truncated to fit the compaction model's
673
+ /** MF-18 修② — the summarization INPUT was truncated to fit the compaction model's
671
674
  * context window (the 300K-session shape that previously guaranteed a prompt-too-long throw
672
675
  * at every boundary). Fidelity disclosure: oldest `droppedChars` characters of the serialized
673
676
  * conversation were omitted from the summary prompt. */
@@ -708,8 +711,8 @@ export type TraceEvent = {
708
711
  durationMs: number;
709
712
  ts: number;
710
713
  } | {
711
- /** MF-18 修③ [496]③ — a compaction landed at the run's FINAL boundary and the one-shot
712
- * post-compact announce latch (background-task snapshot, G1 [482]) was never consumed: the
714
+ /** MF-18 修③ — a compaction landed at the run's FINAL boundary and the one-shot
715
+ * post-compact announce latch (background-task snapshot, G1) was never consumed: the
713
716
  * next collected boundary it waits for never came. The announce is lost for THIS run (a
714
717
  * follow-up run on the same session re-arms it from the session tail — see the resume
715
718
  * re-arm seed in runtask). */
@@ -758,7 +761,7 @@ export type TraceEvent = {
758
761
  /** C11 — a tool requested a human plan review (`ctx.requestReview()`) but the deployment cannot
759
762
  * honor it (no checkpoint store / loop already terminating): the request was consumed + DROPPED
760
763
  * (headless degrade). The HITL ask vanished with zero signal before this. NO free-text reason
761
- * field (codex MED: `requestReview({reason})` carries tool/model-authored text — the file-header
764
+ * field (`requestReview({reason})` carries tool/model-authored text — the file-header
762
765
  * metadata-only rule forbids it here; the fact of the drop is the signal). */
763
766
  kind: "review.dropped";
764
767
  version: 1;