@sema-agent/core 7.6.0 → 7.6.1

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 (75) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/dist/agents/agent-transcript-tool.d.ts +2 -2
  3. package/dist/agents/cascade.d.ts +2 -3
  4. package/dist/agents/repair-loop.d.ts +2 -2
  5. package/dist/agents/retain-ledger.d.ts +2 -3
  6. package/dist/agents/send-message-tool.d.ts +2 -2
  7. package/dist/agents/session-util.d.ts +2 -2
  8. package/dist/agents/subagent.d.ts +3 -4
  9. package/dist/agents/teacher.d.ts +2 -2
  10. package/dist/agents/team.d.ts +2 -2
  11. package/dist/agents/verify.d.ts +5 -6
  12. package/dist/core/agent-definition.d.ts +172 -0
  13. package/dist/core/agent-definition.js +1 -0
  14. package/dist/core/delegation-frames.d.ts +298 -0
  15. package/dist/core/delegation-frames.js +21 -0
  16. package/dist/core/engine-notice.d.ts +555 -0
  17. package/dist/core/engine-notice.js +55 -0
  18. package/dist/core/gate-fold.d.ts +12 -0
  19. package/dist/core/gate-fold.js +158 -0
  20. package/dist/core/gate-lanes.d.ts +93 -0
  21. package/dist/core/gate-lanes.js +626 -0
  22. package/dist/core/hands-band.d.ts +134 -0
  23. package/dist/core/hands-band.js +1 -0
  24. package/dist/core/hooks.d.ts +20 -101
  25. package/dist/core/hooks.js +53 -854
  26. package/dist/core/mcp-failure.d.ts +43 -5
  27. package/dist/core/mcp-failure.js +31 -14
  28. package/dist/core/mcp-server-spec.d.ts +217 -0
  29. package/dist/core/mcp-server-spec.js +1 -0
  30. package/dist/core/model-seat.d.ts +99 -0
  31. package/dist/core/model-seat.js +1 -0
  32. package/dist/core/reminder-mint.d.ts +10 -0
  33. package/dist/core/reminder-mint.js +3 -0
  34. package/dist/core/runner/contracts.d.ts +382 -6
  35. package/dist/core/runner/gate-exit.d.ts +177 -9
  36. package/dist/core/runner/gate-exit.js +70 -1
  37. package/dist/core/runner/prepare-caps-and-workflow.d.ts +2 -7
  38. package/dist/core/runner/prepare-delegation-surface.d.ts +2 -7
  39. package/dist/core/runner/prepare-task.d.ts +2 -2
  40. package/dist/core/runner/runtask.d.ts +4 -71
  41. package/dist/core/runner/runtask.js +14 -5
  42. package/dist/core/runner-deps.d.ts +1416 -0
  43. package/dist/core/runner-deps.js +1 -0
  44. package/dist/core/runtime-caps.d.ts +164 -0
  45. package/dist/core/runtime-caps.js +1 -0
  46. package/dist/core/task-event.d.ts +910 -0
  47. package/dist/core/task-event.js +1 -0
  48. package/dist/core/task-limits.d.ts +110 -0
  49. package/dist/core/task-limits.js +1 -0
  50. package/dist/core/task-result.d.ts +809 -0
  51. package/dist/core/task-result.js +1 -0
  52. package/dist/core/task-spec.d.ts +1370 -0
  53. package/dist/core/task-spec.js +1 -0
  54. package/dist/core/task-stream.d.ts +382 -0
  55. package/dist/core/task-stream.js +1 -0
  56. package/dist/core/tool-spec.d.ts +1174 -0
  57. package/dist/core/tool-spec.js +1 -0
  58. package/dist/core/types.d.ts +26 -7691
  59. package/dist/core/types.js +2 -76
  60. package/dist/core/warm-resume.d.ts +2 -2
  61. package/dist/index.d.ts +2 -1
  62. package/dist/index.js +1 -1
  63. package/dist/orchestration/goal.d.ts +2 -2
  64. package/dist/orchestration/run-spec.d.ts +2 -2
  65. package/dist/orchestration/run-workflow-tool.d.ts +3 -3
  66. package/dist/orchestration/workflow.d.ts +4 -4
  67. package/dist/scenarios/scenario-registry.d.ts +3 -3
  68. package/dist/scenarios/teacher-quickstart.d.ts +2 -2
  69. package/dist/server/http.d.ts +2 -2
  70. package/dist/stores/file/fs-atomic.d.ts +88 -12
  71. package/dist/stores/file/fs-atomic.js +184 -55
  72. package/dist/stores/file/index.d.ts +1 -0
  73. package/dist/stores/file/index.js +1 -0
  74. package/package.json +1 -1
  75. package/test/export-surface.snapshot.json +9 -1
@@ -22,29 +22,35 @@ import type { WorkflowSizeGuideline } from "../../orchestration/workflow-size-gu
22
22
  import type { ToolManifestRow } from "../../prompt-assembly/tool-catalog.js";
23
23
  import type { CwdRef, ReadFace } from "../../tools/fs/index.js";
24
24
  import type { MaterializedA2a } from "../a2a.js";
25
- import type { CompactionForkContext } from "../auto-compaction.js";
25
+ import type { CompactionForkContext, MaybeCompactOptions, RapidRefillState } from "../auto-compaction.js";
26
26
  import type { AutoModeArmingRecipe } from "../auto-mode-arming.js";
27
27
  import type { AutoModeDecider, AutoModeDenialTracker } from "../auto-mode.js";
28
28
  import type { CacheBreakDetector, ToolFingerprintInput } from "../cache-break-detector.js";
29
- import type { Checkpoint, CheckpointState, CheckpointToken, PlatformLimitReason, ResourceLedger, ResourceLimitReason, SerializedCheckpointState } from "../checkpoint-store.js";
29
+ import type { ActorAssertion, Checkpoint, CheckpointState, CheckpointStore, CheckpointToken, PendingSteerEntry, PlatformLimitReason, ReopenReason, ResourceLedger, ResourceLimitReason, ResumeOutcome, SerializedCheckpointState } from "../checkpoint-store.js";
30
30
  import type { ClearedProjectionLedger, ContextEditMachine, OccurrenceIndex } from "../context-edit.js";
31
- import type { HookInvocationIdentity, Hooks, OrgGateVerdict, PermissionDeniedPayload, ToolGateInput } from "../hooks.js";
31
+ import type { HookInvocationIdentity, Hooks, OrgGateVerdict, PermissionDeniedPayload, PostToolBatchCall, PostToolBatchResult, ToolGateInput } from "../hooks.js";
32
32
  import type { MaterializedMcp } from "../mcp.js";
33
33
  import type { MemoryEngine } from "../memory-engine/engine.js";
34
34
  import type { HarvestReport, MemorySessionHandle } from "../memory-engine/types.js";
35
35
  import type { SessionPermissionRules } from "../session-policy-store.js";
36
36
  import type { RecoveredOrphan } from "../session-reconcile.js";
37
- import type { StoredSession } from "../session.js";
38
- import type { TaskNotificationPayload } from "../task-notification.js";
37
+ import type { SessionStore, StoredSession } from "../session.js";
38
+ import type { TaskOutcome } from "../task-outcome.js";
39
+ import type { ModelPricing } from "../pricing.js";
40
+ import type { PushQueue } from "../push-queue.js";
41
+ import type { SystemInjectionPriority, TaskNotificationPayload } from "../task-notification.js";
39
42
  import type { OnAsk, PermissionResult, ResolvedAsk, ToolCallRequest, ToolPolicy } from "../tool-policy.js";
40
43
  import type { RemoteExecutionEnv, SnapshotId, WorkspaceHandle } from "../remote-env.js";
41
44
  import type { ToolDisclosureManifest } from "../trace.js";
42
- import type { NestedUsageAccum, RunnerDeps, TaskEvent, TaskResult, TaskSpec, ToolActivity, ToolEffect } from "../types.js";
45
+ import type { AgentDefinition, Brain, CompactOutcome, NestedUsageAccum, RunnerDeps, TaskEvent, TaskResult, TaskSpec, TaskStream, ToolActivity, ToolEffect } from "../types.js";
43
46
  import type { WiringManifest } from "../wiring-manifest.js";
44
47
  import type { ActiveSkillFrame } from "./active-skill-scope.js";
48
+ import type { BudgetAxis } from "./assemble-result.js";
45
49
  import type { GitStatusLaneRef } from "./git-status-frame.js";
46
50
  import type { SessionReadFileStates } from "./prepare-hands-readface.js";
47
51
  import type { BlockedRef, OutputRef, SkillListingEntry } from "./synthetic-tools.js";
52
+ import type { createAttachmentState, WriteFamily } from "./turn-attachments.js";
53
+ import type { cacheFamilyOf } from "./usage-accounting.js";
48
54
  /**
49
55
  * The frozen task-start snapshot of the caller's tool-face control arrays: produced by the
50
56
  * config-doors phase, read by the hands/read-face phase. Named here rather than reached for as
@@ -1970,3 +1976,373 @@ export interface ParkAsk {
1970
1976
  * lane is not armed (the gate then keeps the synchronous onAsk path). */
1971
1977
  suspendAsk: ToolGateInput["suspendAsk"];
1972
1978
  }
1979
+ /**
1980
+ * design/157 B15 二期 — `runLocked`'s per-run mutable state, EXPLICIT. These fields started as loose
1981
+ * `let`/`const` bindings in the R2 declaration hub; the extraction kept every assignment at its
1982
+ * original site and order (binding-form-only rewrite — the event-snapshot pin held the run
1983
+ * byte-identical), and later work has since added fields directly here (see the per-field notes).
1984
+ * Grouped by lifecycle semantics per the B15 pre-recon partition. Function members start as inert
1985
+ * placeholders and are assigned at their ORIGINAL declaration sites (the original code could not
1986
+ * call them earlier either — TDZ proved it). Not on the npm face; this extraction is what enabled
1987
+ * makeTurnBoundary / makeHarnessHandlers (三期, both in runtask.ts).
1988
+ */
1989
+ export interface RunState {
1990
+ telemetry: {
1991
+ cacheFamily: ReturnType<typeof cacheFamilyOf>;
1992
+ pricing: ModelPricing;
1993
+ /** RB-368 ([2076]): whether the CURRENT serving model has a configured price table (a
1994
+ * `RunnerDeps.pricing` entry or a `Model.cost` declaration). When neither exists,
1995
+ * `modelCostToPricing(undefined)` yields an all-zero table and every computed cost is a
1996
+ * fabricated 0 — indistinguishable from "declared free". This flag keeps the two apart so
1997
+ * the DISCLOSURE faces (trace frames, public stats) can go absent instead of lying; the
1998
+ * internal budget coordinate (`stats.costMicroUsd`) stays numeric either way. */
1999
+ pricingConfigured: boolean;
2000
+ /** RB-368: sticky — some spend was ACCOUNTED while `pricingConfigured` was false (a mid-task
2001
+ * model switch can flip pricedness, so per-call state isn't enough for the task total). Once
2002
+ * true, the task-level cost total is an undercount and its faces are omitted. */
2003
+ unpricedSpend: boolean;
2004
+ tracer: TaskSpec["tracer"];
2005
+ taskId: string;
2006
+ /** #499 — the run's engine-minted identity, so the task.start/task.end pair carries it from ONE
2007
+ * seat (the same place taskId is published). Empty only before the run body assigns it. */
2008
+ runId: string;
2009
+ taskStart: number;
2010
+ taskStartMonotonic: number;
2011
+ cacheBreakReported: boolean;
2012
+ };
2013
+ degrade: {
2014
+ degradeToModel: Model | undefined;
2015
+ degraded: TaskResult["degraded"];
2016
+ outputErrorStreak: number;
2017
+ outputInvalid: boolean;
2018
+ recordDegraded: (info: NonNullable<TaskResult["degraded"]>, toModel?: Model) => void;
2019
+ };
2020
+ limits: {
2021
+ turnsExceeded: boolean;
2022
+ budgetHit: "exceeded" | "precall" | undefined;
2023
+ /** design/164 — WHICH budget axis tripped, set at the same instant as {@link budgetHit} so the
2024
+ * terminal can name it (`limits.max_tokens_exceeded` vs `limits.max_cost_exceeded`). Tokens
2025
+ * outrank cost when both are over at the same boundary (the documented attribution order). */
2026
+ budgetAxis: BudgetAxis | undefined;
2027
+ /** design/164 件四/件五 — the TYPED terminal an EXTERNAL cause (a dying execution environment, an
2028
+ * exhausted governance window) recorded when it could not suspend the run durably. Set together with
2029
+ * the abort it triggers, and adopted as the run's `threw` once the loop settles, so the caller gets
2030
+ * `env.lifetime_expired` / `usage.window_exhausted` instead of the anonymous error an abort produces.
2031
+ * Same carrier shape as `brainCallGuardrailRef.timedOut`, and for the same reason: the harness turns a
2032
+ * loop throw into an error assistant message that strips the error object. */
2033
+ platformTerminal: (Error & {
2034
+ code?: string;
2035
+ retryAfterMs?: number;
2036
+ }) | undefined;
2037
+ outputRetryCap: number;
2038
+ effectiveMaxTurns: number | undefined;
2039
+ };
2040
+ budget: {
2041
+ remainingMicroUsd: number | undefined;
2042
+ maxCostMicroUsd: number | undefined;
2043
+ /** design/164 — tokens still available on the cross-slice allocation (`resourceSuspend.totalTokens`
2044
+ * minus everything earlier slices spent), or `undefined` when no total is set. */
2045
+ remainingTokens: number | undefined;
2046
+ /** This slice's EFFECTIVE token ceiling = `min(limits.maxTokens, remainingTokens)`. The exact mirror
2047
+ * of `maxCostMicroUsd`. `undefined` = no token ceiling; `0` is a real, exhausted ceiling. */
2048
+ maxTokensWindow: number | undefined;
2049
+ /** The axis that is over its ceiling right now, or `undefined` when neither is — a predicate that
2050
+ * ALSO carries the attribution, so no caller can set `budgetHit` without an axis. */
2051
+ overBudget: () => BudgetAxis | undefined;
2052
+ streamCancel: boolean;
2053
+ callOutputChars: number;
2054
+ lastStreamBudgetCheck: number;
2055
+ projectedOverBudget: () => BudgetAxis | undefined;
2056
+ };
2057
+ turn: {
2058
+ callStartAt: number | undefined;
2059
+ firstTokenAt: number | undefined;
2060
+ turnUsage: {
2061
+ inputTokens: number;
2062
+ totalInputTokens: number;
2063
+ outputTokens: number;
2064
+ cacheReadTokens: number;
2065
+ cacheWriteTokens: number;
2066
+ costMicroUsd: number;
2067
+ } | undefined;
2068
+ turnUsageMissing: boolean;
2069
+ turnStopReason: string | undefined;
2070
+ lastTurnHadToolCalls: boolean;
2071
+ /** B15 三期使能:design/134 §3.1b postToolBatch 的本回合聚集批(每回合边界消费并重置 — 回合语义,故归 turn)。 */
2072
+ toolBatch: PostToolBatchCall[];
2073
+ };
2074
+ counters: {
2075
+ /** design/164 — how many LIMIT-APPROACH frames have been injected this run (0..2). Also the
2076
+ * one-shot latch itself: each frame fires when the count is still below its index. */
2077
+ approachNoticesSent: number;
2078
+ /** B15 三期使能:R9 引用的 R3/压缩区 latch(walltime 收尾 latch — preemptIgnoredReported 同族)。 */
2079
+ walltimeSyncBackstopFired: boolean;
2080
+ /** B15 三期终件补遗:压缩地板/背压 latch — rs.counters.compactionFloor 跨 drainManualCompact(写)/R9(读写)/收尾(读)三方共享,rs.counters.trimForceBackoff 为 R9 跨调用 latch(同款只改绑定不改时机)。 */
2081
+ compactionFloor: number;
2082
+ trimForceBackoff: boolean;
2083
+ repetitionCuts: number;
2084
+ repetitionSpared: number;
2085
+ repetitionEvents: Array<{
2086
+ turn: number;
2087
+ action: "cut" | "spared";
2088
+ rule: "char-run" | "unit-loop";
2089
+ period: number;
2090
+ reps: number;
2091
+ segment: string;
2092
+ }>;
2093
+ REPETITION_EVENTS_CAP: number;
2094
+ preemptIgnoredReported: boolean;
2095
+ wroteThisRun: boolean;
2096
+ finalVerifyInjections: number;
2097
+ groundingSignalPreR9: boolean;
2098
+ groundingSignalPostR9: boolean;
2099
+ cadenceTurns: number;
2100
+ };
2101
+ attach: {
2102
+ attachmentsCfg: TaskSpec["attachments"];
2103
+ agentListingOn: boolean;
2104
+ skillsListingOn: boolean;
2105
+ attachState: ReturnType<typeof createAttachmentState> | undefined;
2106
+ dateState: {
2107
+ announcedDate: string;
2108
+ } | undefined;
2109
+ instrProbe: RunnerDeps["probeInstructionSources"];
2110
+ instrState: {
2111
+ lastAnnouncedHash: Map<string, string | null>;
2112
+ } | undefined;
2113
+ /** #20 lane — the Workflow tool card's advisory size guideline as last ANNOUNCED to the model,
2114
+ * plus the live boundary-time read (Prepared.workflowSizeGuideline). Same deliberate posture as
2115
+ * `dateState`/`instrState`: NOT an AttachmentState member, one field, zero reducers. */
2116
+ sizeGuidelineState: {
2117
+ announcedGuideline: WorkflowSizeGuideline;
2118
+ current: () => WorkflowSizeGuideline;
2119
+ } | undefined;
2120
+ /** B15 三期使能:boundary 注入计数(→ stats.mechanisms.attachmentsInjected;attachState 注入族语义,故归 attach)。 */
2121
+ attachmentsInjected: number;
2122
+ };
2123
+ }
2124
+ /** design/171 §6.3 parity, LIVE leg — one accepted live steer's REPLAY identity, the twin of the parked
2125
+ * queue's `samePendingSteerPayload`. `payload` is the exact string handed to the harness, so the text,
2126
+ * the speaker envelope and the trust framing all fold into it; `trusted` is kept SEPARATELY from those
2127
+ * framed bytes so a text that spells its own `<system-reminder>` wrapper can never read as the trusted
2128
+ * arm's byte-equal twin. `actor` carries the attribution the ledger frame reports. */
2129
+ export interface AcceptedSteerInput {
2130
+ payload: string;
2131
+ trusted: boolean;
2132
+ /** design/373 (R2-F3) — the NORMALIZED priority the call delivered under (absent input ⇒ "next"),
2133
+ * the replay identity's sixth axis: same id + same text at a DIFFERENT tier is not a replay —
2134
+ * a `next` input re-sent as `now` answering "idempotent success" without the interrupt would be
2135
+ * a disposition lie, so it refuses `duplicate_input_id` like any other identity mismatch. */
2136
+ priority: SystemInjectionPriority;
2137
+ actor?: ActorAssertion;
2138
+ }
2139
+ /**
2140
+ * Config re-supplied to {@link Runner.resume} (design/45). A suspended task's tools / model / policy /
2141
+ * hooks cannot be reconstructed from a checkpoint token (the session stores neither tool implementations
2142
+ * nor the hand band), so the caller's trusted control plane re-supplies the same {@link TaskSpec} it ran
2143
+ * with — minus the conversation bits: `sessionId` comes from the checkpoint and `objective` is replaced by
2144
+ * an internally-generated continuation, so both are omitted.
2145
+ */
2146
+ export type ResumeTaskConfig = Omit<TaskSpec, "objective" | "sessionId">;
2147
+ /** design/45 resume plan threaded from {@link Runner.resume} into the shared run loop. */
2148
+ export interface ResumeRun {
2149
+ cp: Checkpoint;
2150
+ /**
2151
+ * RB-152 (2026-07-25, 按面收口): did the APPROVED ACTION actually begin executing?
2152
+ *
2153
+ * The reopen compensation used to key on an error-code whitelist (`resume.env_failed` /
2154
+ * `resume.session_not_found` / `resume.tool_unavailable`). That is a proxy for the thing that actually
2155
+ * matters, and every time a NEW way to exit without running the action appeared, the whitelist did not
2156
+ * know about it: a caller's abort landing anywhere between the CAS and `tool.execute` — across
2157
+ * `SessionStore.acquire` (pluggable, cross-network on a durable backend), the MCP reconnect, the env
2158
+ * rebuild, `resumeVM`, tool materialization — produced a `failed` result with NO errorCode, no reopen,
2159
+ * and a human approval consumed for work that never happened. Two rounds of fixes (RB-77, RB-109) each
2160
+ * moved that window rather than closing it, because both extended the whitelist.
2161
+ *
2162
+ * This binds the compensation to the FACT instead: set the instant before `tool.execute` for the gated
2163
+ * call (the same `onExecuteStart` signal the orphan-reconcile split already trusts). Every terminal —
2164
+ * throw OR returned result — asks one question: was the checkpoint consumed while this stayed false?
2165
+ */
2166
+ pendingActionStarted?: boolean;
2167
+ /** Validated against `cp.gate.kind` at the resume entry: human/irreversible_ask→`policy_ask`,
2168
+ * resource_limit→`resource_limit` (design/74), needs_review→`dry_run_review` (design/76 §2.5),
2169
+ * plan_review→`plan_review` (design/80 D-B). The gate-match guard in `resumeStream` enforces the
2170
+ * correlation. design/144 §3: `wake` is the NON-GATE arm — only a checkpoint awaiting NO gate
2171
+ * decision passes the resume entry with it (gate purity, `wake.gate_pending`); the run loop skips
2172
+ * `applyResumeDecision` for it (no pending action to resolve) and re-enters via the continuation +
2173
+ * pendingSteer tail alone. */
2174
+ outcome: Extract<ResumeOutcome, {
2175
+ gate: "policy_ask" | "resource_limit" | "dry_run_review" | "plan_review" | "wake";
2176
+ }>;
2177
+ /** design/144 §3 (X5) — the wake's own operator message, validated (`validatePendingSteer`) at the
2178
+ * resume entry and carried SEPARATELY from the checkpoint's parked `pendingSteer`: a message-bearing
2179
+ * wake of a checkpoint that ALSO holds a parked steer must deliver BOTH (park order: parked first,
2180
+ * wake message second), each under its own trusted framing — the old merge-into-the-slot shape
2181
+ * silently DISPLACED the parked (undelivered) supervisor steer. Wake outcomes only. */
2182
+ wakeMessage?: Omit<PendingSteerEntry, "seq">;
2183
+ /** design/373 §4.3 (D2) — the userPromptSubmit screen's `additionalContext` for {@link wakeMessage},
2184
+ * captured at the resume ENTRY (the message is screened once, pre-CAS, on the resuming process's
2185
+ * hook) and delivered by the drain as the engine's own reminder AHEAD of the wake frame — carrying
2186
+ * it forward is what keeps the hook single-run (re-screening at the drain would be the double-run
2187
+ * §4.3-3 reserves for the cross-process parked leg). Present only when a wake message passed a
2188
+ * screen that supplied context. */
2189
+ wakeMessageHookContext?: string;
2190
+ /** Compensation hook (design/45/49): called iff the resumed run fails with `resume.env_failed` (post-CAS
2191
+ * workspace `resumeVM` failed) OR `resume.tool_unavailable` (P-7: the approved tool vanished) — in both
2192
+ * the CAS already consumed the checkpoint but the pending action never ran. `resumeStream` supplies a
2193
+ * closure that reopens the checkpoint (`resolved → pending`) so a retry re-resumes the SAME suspended work
2194
+ * instead of losing it to a forced "re-initiate". design/80 D-1 (reopen-by-reason): the `reason` is
2195
+ * recorded on the reopened row so the next re-resume validates per reason — an `env_failed` reopen must
2196
+ * replay the persisted winner (a system retry of the approved action), while a `tool_unavailable` reopen
2197
+ * lets a human re-decide with the tool present (a fresh decision is allowed — preserves P-7). */
2198
+ onEnvRestoreFailed?: (reason: ReopenReason) => Promise<void>;
2199
+ /** RB-471/FR-C1 — set in the run body right after `applyResumeDecision` completes: the negative-
2200
+ * decision twin of `pendingActionStarted`. A reject/deny consumes its gate BY BEING DELIVERED, and
2201
+ * this bit is the delivery fact — every throw-arm cause (prepare failure, `session_not_found`, a
2202
+ * pre-delivery abort) and the walltime-exhausted settle fire BEFORE it is set, so an undelivered
2203
+ * negative decision still reopens as `env_failed` (the retry replays the persisted decision; it
2204
+ * never re-asks — design/80 D-1), closing the RB-152/RB-70 loss class the first RB-471 cut reopened. */
2205
+ decisionDelivered?: boolean;
2206
+ }
2207
+ /**
2208
+ * What {@link TaskStream.destroy} needs to reap a SUSPENDED task (design/51 §4). Captured (stashed) onto
2209
+ * the stream handle the moment a run assembles `status:"suspended"` — `finish()` deliberately left the
2210
+ * paused env + committed checkpoint alive for resume, so without this stash they'd be unreachable (the
2211
+ * `Prepared`, and its `ownedEnv`, are GC'd once the run promise settles). `env` is `undefined` for a
2212
+ * process-local suspend (no remote env to reap — only the checkpoint is expired).
2213
+ */
2214
+ export interface SuspendReap {
2215
+ env: Prepared["ownedEnv"];
2216
+ token: CheckpointToken;
2217
+ scope: string;
2218
+ store: CheckpointStore;
2219
+ /** The suspended task's session — for onError correlation only (the `token` is a capability and must
2220
+ * NEVER be logged, checkpoint-store.ts §6). */
2221
+ sessionId: string;
2222
+ }
2223
+ /** B15 三期终件 — R9(回合边界全域:压缩/attachments 注入/提醒节律/预算判决)工厂。
2224
+ * 正文自 runLocked 逐字搬迁(this 面经 runnerHooks 闭包打包,B13 #coreView 同款);模块级符号
2225
+ * 同文件直见不经 deps;可变运行态全在 rs(B15 二期+使能车),deps 全部为 const 稳定引用。 */
2226
+ export interface TurnBoundaryDeps {
2227
+ spec: TaskSpec;
2228
+ queue: PushQueue<TaskEvent>;
2229
+ /** The run loop's own latch — read for `userHalted` (the public `TaskStream.halt()` verb, which sets
2230
+ * the harness stop latch directly, not through the ctx primitive `Prepared.stopRequestedRef`
2231
+ * mirrors): a halted boundary has no next request either, so the bundle is not collected there. */
2232
+ loopLatch: {
2233
+ readonly userHalted: boolean;
2234
+ };
2235
+ manualCompactRef: {
2236
+ requested: boolean;
2237
+ waiters: Array<{
2238
+ resolve: (outcome: CompactOutcome) => void;
2239
+ signal?: AbortSignal;
2240
+ instructions?: string;
2241
+ }>;
2242
+ };
2243
+ todoToolMounted: boolean;
2244
+ taskToolsMounted: boolean;
2245
+ walltimeMonotonicDeadline: number | undefined;
2246
+ timeout: {
2247
+ fired: boolean;
2248
+ latenessMs?: number;
2249
+ clear: () => void;
2250
+ };
2251
+ ident: () => {
2252
+ eventId: string;
2253
+ parentToolCallId?: string;
2254
+ sourceTaskId?: string;
2255
+ };
2256
+ postToolBatchHook: ((batch: PostToolBatchCall[], meta?: {
2257
+ injectedThisTurn: "final_verification" | "finalize";
2258
+ }, ctx?: import("../hooks.js").PostToolBatchContext) => PostToolBatchResult | undefined | Promise<PostToolBatchResult | undefined>) | undefined;
2259
+ batchArgs: Map<string, unknown> | undefined;
2260
+ compactionBrain: Brain;
2261
+ withinTaskCompaction: boolean;
2262
+ compactionBreaker: {
2263
+ failures: number;
2264
+ };
2265
+ windowSafetyOptions: (mainModel: Model) => Pick<MaybeCompactOptions, "fallbackBudget" | "onWindowSafety">;
2266
+ rapidRefill: RapidRefillState;
2267
+ drainManualCompact: (outcome: CompactOutcome) => void;
2268
+ runnerHooks: {
2269
+ onError: RunnerDeps["onError"];
2270
+ seamCCompactionOptions: (prepared: Prepared) => Pick<MaybeCompactOptions, "summaryProvider" | "onCompaction" | "maxConsecutiveProviderReuse" | "consecutiveProviderReuse"> | undefined;
2271
+ compactionHookOptions: (spec: TaskSpec, sessionId: string, trigger: "auto" | "manual" | "forced") => Pick<MaybeCompactOptions, "trigger" | "preCompact" | "postCompact">;
2272
+ recordCompactionReuse: (prepared: Prepared, comp: {
2273
+ compacted: boolean;
2274
+ reused?: boolean;
2275
+ }) => void;
2276
+ };
2277
+ }
2278
+ /** design/157 B15 尾件 — R5(harness 事件处理器族)的依赖包。全部为 runLocked 内声明顺序早于
2279
+ * 工厂调用点的 const 稳定引用 + 三个 runLocked 形参;this 面(deps.onError)经 runnerHooks 打包
2280
+ * (B13 #coreView / R9 TurnBoundaryDeps 同款)。*/
2281
+ export interface HarnessHandlersDeps {
2282
+ spec: TaskSpec;
2283
+ queue: PushQueue<TaskEvent>;
2284
+ internals: RunInternals | undefined;
2285
+ ident: () => {
2286
+ eventId: string;
2287
+ parentToolCallId?: string;
2288
+ sourceTaskId?: string;
2289
+ };
2290
+ parentToolCallId: string | undefined;
2291
+ subagentName: string | undefined;
2292
+ pushContent: (e: TaskEvent) => void;
2293
+ emitCommitted: (entryId: string, role: "user" | "assistant" | "toolResult", toolCallId?: string) => void;
2294
+ startedToolCallIds: Set<string>;
2295
+ toolStartAt: Map<string, number>;
2296
+ writeFamilyOf: (name: string) => WriteFamily | undefined;
2297
+ toolLabels: Map<string, string>;
2298
+ postToolBatchHook: ((batch: PostToolBatchCall[], meta?: {
2299
+ injectedThisTurn: "final_verification" | "finalize";
2300
+ }, ctx?: import("../hooks.js").PostToolBatchContext) => PostToolBatchResult | undefined | Promise<PostToolBatchResult | undefined>) | undefined;
2301
+ batchArgs: Map<string, unknown> | undefined;
2302
+ runnerHooks: {
2303
+ onError: RunnerDeps["onError"];
2304
+ };
2305
+ }
2306
+ /**
2307
+ * The Runner as the modules it drives and the compositions over it see it — the delegation tools
2308
+ * (Agent / SendMessage / AgentTranscript), the repair, verify, cascade, teacher and team loops, the
2309
+ * workflow and goal orchestrations, the scenario registry, the HTTP server, `warmResume`, and the
2310
+ * orchestrator's own trusted `runnerSelf` seat. Every one of them used to name the `Runner` CLASS
2311
+ * through `import type`, which is a type edge from below the runner back up into it — a cycle the
2312
+ * direction gate could only carry as an exemption, and one that reached the orchestrator through the
2313
+ * delegation tools (prepare-task → send-message-tool → runtask).
2314
+ *
2315
+ * THE MEMBER SET IS MEASURED, NOT DESIGNED: it is exactly the union of what those consumers read off a
2316
+ * Runner today (a TS-Program census over `src/`, pinned in test/runner-self-seat.test.ts so a member
2317
+ * nobody reads reds as dead weight and a consumer reaching for a member outside it reds as a widening).
2318
+ * `swapModels`, `sideQuery` and the constructor are NOT here — no consumer reaches them; a host that
2319
+ * needs them holds the class. The class implements this interface, so the two cannot drift apart.
2320
+ *
2321
+ * Signatures are spelled out (not `Pick<Runner, …>`), because naming the class from here would be the
2322
+ * very edge this seat exists to remove.
2323
+ */
2324
+ export interface RunnerSelfSeat {
2325
+ /** The session store this Runner was constructed over — the delegation tools acquire / release / fork through it. */
2326
+ readonly sessions: SessionStore;
2327
+ /** The deployment agent + model catalog, READ-ONLY (a fresh copy per read; the definitions are the shared objects). */
2328
+ readonly agentCatalog: {
2329
+ agents?: AgentDefinition[];
2330
+ builtinAgents?: boolean;
2331
+ models?: Record<string, Model>;
2332
+ };
2333
+ /** The deployment-level gating baseline (deps toolPolicy / hooks) as delegate wrappers — for assemblers that compose with it. */
2334
+ readonly gateBaseline: {
2335
+ toolPolicy?: RunnerDeps["toolPolicy"];
2336
+ hooks?: RunnerDeps["hooks"];
2337
+ };
2338
+ /** Fire `RunnerDeps.onTaskOutcome` through the swallow-guarded chokepoint (thin compositions emit at their terminal state). */
2339
+ emitTaskOutcome(outcome: TaskOutcome): void;
2340
+ /** Run a task to completion. `internals` is the TRUSTED run-scoped channel (never a TaskSpec field). */
2341
+ runTask(spec: TaskSpec, internals?: RunInternals): Promise<TaskResult>;
2342
+ /** Streaming form of {@link RunnerSelfSeat.runTask}; `resume` is the run loop's resume plan, `internals` the trusted channel. */
2343
+ runTaskStream(spec: TaskSpec, resume?: ResumeRun, internals?: RunInternals): TaskStream;
2344
+ /** Resume a suspended task from its checkpoint token with the caller's decision, to completion. */
2345
+ resume(token: CheckpointToken, outcome: ResumeOutcome, taskConfig: ResumeTaskConfig, internals?: RunInternals): Promise<TaskResult>;
2346
+ /** Streaming form of {@link RunnerSelfSeat.resume}: the pre-CAS guards and the CAS run first, then the live stream is returned. */
2347
+ resumeStream(token: CheckpointToken, outcome: ResumeOutcome, taskConfig: ResumeTaskConfig, internals?: RunInternals): Promise<TaskStream>;
2348
+ }
@@ -1,16 +1,31 @@
1
1
  /**
2
- * The ONE mint of a {@link GateOutcome} the record of what a tool-gate pass decided, what ask it settled
3
- * and who asked. The gate accumulates two facts on its way to an exit (WHO refused, written once at the
4
- * refusing site; WHAT was settled, written at the resolution site) and hands them here; this module
5
- * assembles the record, runs the invariant screen, and refuses a record the engine composed wrong. Every
6
- * projection of the outcome (the deny observer's payload, the call's `tool_end` frame, the engine's
7
- * per-call sideband) is the object minted here, never a re-derivation.
2
+ * The tool gate's RECORD FLOOReverything about how one gate pass is recorded and how it ends:
8
3
  *
9
- * A machinery module on the prepare path's first stratum: it reaches down for the vocabulary only.
4
+ * - the ONE mint of a {@link GateOutcome} ({@link mintGateOutcome}) the record of what a tool-gate pass
5
+ * decided, what ask it settled and who asked. The gate accumulates two facts on its way to an exit (WHO
6
+ * refused, written once at the refusing site; WHAT was settled, written at the resolution site) and hands
7
+ * them here; the mint assembles the record, runs the invariant screen, and refuses a record the engine
8
+ * composed wrong. Every projection of the outcome (the deny observer's payload, the call's `tool_end`
9
+ * frame, the engine's per-call sideband) is the object minted here, never a re-derivation;
10
+ * - the settlement LEDGER of a pass ({@link createSettlementLedger}) — the one rule every attestation read
11
+ * goes through;
12
+ * - the PASS RECORD ({@link GatePass}) — the per-call cells the gate's station groups read and write, and
13
+ * the hook-machinery seats they borrow, built once per call by `runToolGate` and handed to every station;
14
+ * - the EXITS — the two phase-1 short-circuits ({@link engineFailClosedExit}, {@link hookDenyExit}) and the
15
+ * single deny / allow exit of the adjudicate chain ({@link exitGate}), the only places a pass turns into a
16
+ * `ToolGateResult`; plus the two observer-facing helpers every exit uses (the payload isolation clone and
17
+ * the deployment-lane crash report).
18
+ *
19
+ * A floor module (layer 0 of the prepare/run path): the two station modules (`gate-fold.ts`, `gate-lanes.ts`)
20
+ * and the gate's dispatch in `hooks.ts` all reach DOWN for it, and it reaches down only for the vocabulary,
21
+ * the reminder envelope and the text helpers. Its `import type` edges up to `hooks.ts` name the gate's public
22
+ * contract (`ToolGateInput`, `ToolGateResult`, the observer payload), which stays declared there.
10
23
  */
11
24
  import { type DeniedBy, type GateOutcome, type Settlement } from "../gate-outcome.js";
12
- import type { AskOrigin } from "../ask-origin.js";
13
- import { type PermissionResult, type ResolvedAsk } from "../tool-policy.js";
25
+ import type { AskOrigin, AskOriginFacts } from "../ask-origin.js";
26
+ import { type PermissionResult, type ResolvedAsk, type ToolCallRequest } from "../tool-policy.js";
27
+ import type { SafeNotifier } from "../safe-notify.js";
28
+ import type { HookSeatOutcome, PermissionDeniedPayload, PreToolUseResult, ToolGateInput, ToolGateResult } from "../hooks.js";
14
29
  /** The ask a gate pass settled, as the gate captured it at its resolution site: the settlement the
15
30
  * resolver composed and the origin word the ask carried. Both or neither (invariant I1). */
16
31
  export interface SettledAsk {
@@ -72,3 +87,156 @@ export declare function createSettlementLedger(call: {
72
87
  toolCallId: string;
73
88
  toolName: string;
74
89
  }): SettlementLedger;
90
+ /**
91
+ * 1.256 复审 MED-1 — observe-only payload isolation for {@link import("../hooks.js").Hooks.permissionDenied}:
92
+ * clone the tool args before they ride the observer payload, so a hook mutating `payload.input` can never
93
+ * pollute the LIVE args object (later events / audit records share it). Same posture as the postToolUseFailure
94
+ * details clone (the gate-stations phase): `structuredClone` first; a non-structured-cloneable graph
95
+ * (functions/handles) falls back to a SHALLOW plain object/array copy (top-level mutation isolated);
96
+ * a non-object primitive passes through as-is (immutable anyway).
97
+ */
98
+ export declare function cloneObserverInput(input: unknown): unknown;
99
+ /** RB-442: hand the crash itself to the deployment's trace lane (`onError(phase:"hook")`), never letting a
100
+ * throwing sink alter the fail-closed outcome — the same swallow posture as every other observer here.
101
+ * Shared by the gate's dispatch (the phase-1 crash arm, the seat-bound refusal), the reversibility probe's
102
+ * two failure arms and the approved-edit re-screen. */
103
+ export declare function traceHookCrash(input: ToolGateInput, err: unknown, notifier: SafeNotifier): void;
104
+ /**
105
+ * The PreToolUse screening face as the gate's stations see it — the hook machinery of `runToolGate` (the seat
106
+ * runner, the per-call seat bound, the fresh per-screening context, the result screen, the two fail-closed
107
+ * reason texts) behind three methods, so the approved-edit re-screen in the lanes runs the SAME screening the
108
+ * phase-1 pass ran without the lanes module reaching into the hook machinery. Present on the pass exactly when
109
+ * a `preToolUse` face is wired.
110
+ */
111
+ export interface PreToolUseScreening {
112
+ /** Run the deployment's PreToolUse face over `args` under the seat bound. The answer is the SEAT's, unscreened —
113
+ * the caller passes it through {@link PreToolUseScreening.screened} in the seat's own continuation. The seat
114
+ * promise is returned as is (no wrapping `async`): what the gate does after the face answers — the policy in
115
+ * phase 1, the re-check in the edit chain — runs on the tick the seat settles, as it did when this call was the
116
+ * seat runner's own; a wrapper that screened the answer inside a second `async` cost one more tick there. An
117
+ * expired seat is reported, not thrown; a crashing face rejects. */
118
+ screen(args: unknown): Promise<HookSeatOutcome<PreToolUseResult | undefined>>;
119
+ /** The result screen over an answered seat's value: an out-of-contract decision becomes the ordinary hook deny. */
120
+ screened(answer: PreToolUseResult | undefined): PreToolUseResult | undefined;
121
+ /** The model-facing text for a face that THREW while screening `subject`. */
122
+ crashReason(subject: string, err: unknown): string;
123
+ /** The model-facing text for a face that never ANSWERED while screening `subject` (the timeout arm also
124
+ * reports on the deployment lane; a task-abort expiry does not). */
125
+ unansweredReason(subject: string, cause: "timeout" | "aborted"): Promise<string>;
126
+ }
127
+ /**
128
+ * ONE gate pass — the record the station groups (`runGateFold`, `runGateLanes`) and the exits read and write,
129
+ * built once per call by `runToolGate` before the screening pass. What used to be the closure-captured `let`
130
+ * cells of a 1 500-line function are its MUTABLE cells (each written by the station named on it, read by the
131
+ * ones after); what the stations borrow from the hook machinery are its SEATS (fixed for the pass). Ownership
132
+ * classes follow the phase-Input convention: `borrowed-readonly` = fixed for the pass, the stations only read
133
+ * it; `borrowed-mutable` = a cell a station writes.
134
+ */
135
+ export interface GatePass {
136
+ /** borrowed-readonly — the gate's input: every deployment seat the stations read by name (`egress`,
137
+ * `orgRules`, `persistedRules`, `autoMode`, …), and the identity / reminder mark the exits stamp. */
138
+ readonly input: ToolGateInput;
139
+ /** borrowed-readonly — the gate's event, read ONCE at entry (the dispatch's entry destructure): the exit compares the
140
+ * final args against THIS event's input, never against a re-read of the input record. */
141
+ readonly event: ToolGateInput["event"];
142
+ /** borrowed-readonly — the deployment's policy face, captured ONCE at entry before the screening pass. The fold
143
+ * adjudicates through it and the edit chain re-adjudicates through the SAME binding: a stateful accessor on the
144
+ * input cannot hand the fold one policy and the approved-edit re-check another (or none). */
145
+ readonly adjudicate: ToolGateInput["adjudicate"];
146
+ /** borrowed-readonly — the ask resolver, captured ONCE at entry (same rule: the first-round resolution and the edit
147
+ * round's re-ask resolve through one binding). */
148
+ readonly resolveAsk: ToolGateInput["resolveAsk"];
149
+ /** borrowed-readonly — the durable park closure, captured ONCE at entry (the three park sites hand it the same
150
+ * thirteen seats; presence is judged off this one read). */
151
+ readonly suspendAsk: ToolGateInput["suspendAsk"];
152
+ /** borrowed-readonly — `input.event.toolName`, read once. */
153
+ readonly toolName: string;
154
+ /** borrowed-readonly — `input.event.toolCallId`, read once. */
155
+ readonly toolCallId: string;
156
+ /** borrowed-readonly — design/384: ONE read of the per-call signal for the whole gate call (the one-read
157
+ * discipline the settlement screen already states): every conditional seat invocation (the three wait
158
+ * seats plus the three suspendAsk park sites) judges presence and passes the value from this same cell, so
159
+ * a stateful accessor cannot answer the arity check with a signal and the call with something else. */
160
+ readonly callSignal: AbortSignal | undefined;
161
+ /** borrowed-readonly — RB-463: the one host-callback isolation scope of this gate call (the deny observer,
162
+ * the hook-error trace sink, the org and rule lanes' disclosure sinks). */
163
+ readonly notifier: SafeNotifier;
164
+ /** borrowed-readonly — the settlement LEDGER of this pass (the binding is fixed; the ledger's own methods
165
+ * are how the resolution sites write it — one rule per read, see {@link createSettlementLedger}). */
166
+ readonly ledger: SettlementLedger;
167
+ /** borrowed-readonly — the ONE emission seat of the deny observer for this pass, bounded and contained like
168
+ * every other seat (the hook machinery owns the seat runner; the exits call this). */
169
+ readonly notifyPermissionDenied: (payload: Omit<PermissionDeniedPayload, "signal">) => Promise<void>;
170
+ /** borrowed-readonly — the PreToolUse screening face ({@link PreToolUseScreening}), or undefined when no
171
+ * `preToolUse` face is wired (the edit chain then re-screens nothing, as before). */
172
+ readonly screening: PreToolUseScreening | undefined;
173
+ /** borrowed-readonly — the station group that runs AFTER the fold (the lanes: org layer, rule lane, classifier,
174
+ * parks, resolution, edit chain, and the exit they end in), handed the pass by the fold's LAST station on the
175
+ * fold's own completing tick. A seat rather than an import because the two station modules sit on one layer
176
+ * and may not name each other; a seat rather than a second `await` in the dispatch because that await is a
177
+ * suspension point of its own: the org face and the rule store read HOST state the host may refresh
178
+ * concurrently, and the tick on which they are consulted is the contract — when the station bodies were one
179
+ * function, the org face was invoked in the same continuation that ran the last tighten, and a refresh queued
180
+ * behind the policy's answer could not land in between. Returns the gate result (a park's suspension or the
181
+ * exit's record). Writer: the dispatch (`runToolGate`, which wires `runGateLanes`). Reader: the fold's tail. */
182
+ readonly afterFold: (pass: GatePass) => Promise<ToolGateResult>;
183
+ /** borrowed-mutable — the FINAL args: the model's input, rewritten by the phase-1 hook, by the policy
184
+ * rewrite on the ask path, by the content-ask lane's presented snapshot and by the exits' rewrite fold.
185
+ * Writers: the dispatch (phase 1), the lanes, the exits. */
186
+ currentInput: unknown;
187
+ /** borrowed-mutable — the adjudication request. Minted over the model's input so the record is whole from
188
+ * the first station, and RE-MINTED by the adjudication station over the FINAL post-screen args (the
189
+ * pre-existing `const req` site); `args` is re-pointed at the policy rewrite on the ask path and at the
190
+ * content-ask lane's presented snapshot. Writers: the fold (mint), the lanes (`args`). */
191
+ req: ToolCallRequest;
192
+ /** borrowed-mutable — the PreToolUse `additionalContext` collected for this call (pushed into; the binding
193
+ * is fixed). Writers: the dispatch (phase 1), the lanes (sandbox admission note, the edit re-screen). */
194
+ readonly preToolContext: string[];
195
+ /** borrowed-mutable — the phase-1 hook's ASK, remembered for the fold. Only ever an ask (assigned under the
196
+ * `r.action === "ask"` arm); typed as the narrowed member so the fold's reconstruction reads the ask-only
197
+ * fields without a runtime re-test that could, if it ever failed, silently skip the promotion (i.e. loosen).
198
+ * Writer: the dispatch (phase 1). Readers: the fold, the sandbox-admission conjunct. */
199
+ hookAsk: Extract<PermissionResult, {
200
+ action: "ask";
201
+ }> | undefined;
202
+ /** borrowed-mutable — set by a park attempt that FAILED (see `ParkAttemptFailed`). Read at the single deny
203
+ * exit — whatever the fallback chain then decides to say, it says it knowing the park was tried first.
204
+ * Writers: the three park sites. */
205
+ parkFailed: string | undefined;
206
+ /** borrowed-mutable — WHO refused, written ONCE by the refusing site (a direct policy / org / classifier
207
+ * deny, a hook re-screen refusal, the write-protection re-judge, the edit chain's cap, an ask resolution
208
+ * that ended as a refusal). The exit mints the outcome from it beside the ledger's `settled`; a refusal
209
+ * that reaches the exit without it is an engine defect and the mint throws. */
210
+ deniedBy: DeniedBy | undefined;
211
+ /** borrowed-mutable — the standing verdict. The no-policy default until the adjudication station writes the
212
+ * policy's answer; then rewritten by every station that changes the verdict (fold, tightens, org, rule
213
+ * lane, stamp, classifier, sandbox, content lane, resolution, edit chain). */
214
+ decision: PermissionResult;
215
+ /** borrowed-mutable — which of the gate's OWN post-fold tightens minted the standing ask, if one did — an
216
+ * input of the ask-origin derivation (`AskOriginFacts.tightened`). Written at most once per pass: every
217
+ * tighten fires only over a surviving `allow`, and the first one to fire leaves an `ask` the others do not
218
+ * touch. Recorded by the gate rather than read off `decisionReason` because that member is a policy's to
219
+ * compose (a policy stamping `"safety"` on its own ask must read as `policy`). Writer: the fold. */
220
+ tightenedBy: AskOriginFacts["tightened"];
221
+ /** borrowed-mutable — the rewrite remembered BEFORE a hook-ask promotion / tighten, re-applied on the
222
+ * approved path (a redaction is not cancelled because a person confirmed). Writer: the fold. Readers: the
223
+ * fold's probe and write-protection judges, the lanes, the allow exit. */
224
+ policyRewrite: unknown;
225
+ }
226
+ /**
227
+ * The engine's fail-closed block of phase 1 — the PreToolUse face THREW or never ANSWERED, so the engine
228
+ * decides, on its own rule, that the call does not run. design/134 R5 BOUNDARY (deliberate asymmetry with
229
+ * {@link hookDenyExit} — do NOT unify the two): a hook `deny` is the HOOK's decision and the observer
230
+ * exclusion covers it; a crash / an unanswered seat produces no decision at all, so the deny OBSERVER fires
231
+ * here — an attribution channel whose job is "why was this call denied" must see an engine deny, or the
232
+ * deployment's only evidence of a broken hook is a tool that mysteriously stops working.
233
+ */
234
+ export declare function engineFailClosedExit(pass: GatePass, reason: string): Promise<ToolGateResult>;
235
+ /** The hook's OWN deny in phase 1. design/134 R5: a PreToolUse hook deny does NOT fire `permissionDenied`
236
+ * (the exclusion recorded on `Hooks.permissionDenied`) — this return stays observer-free. */
237
+ export declare function hookDenyExit(pass: GatePass, r: PreToolUseResult): ToolGateResult;
238
+ /**
239
+ * The exit of the adjudicate chain — the pass has a standing `deny` or `allow` (every ask was resolved or
240
+ * parked before this), and this is where it becomes the gate result and the ONE record of the pass.
241
+ */
242
+ export declare function exitGate(pass: GatePass): Promise<ToolGateResult>;