@buildaharness/aielia 0.3.3 → 0.3.5

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 (111) hide show
  1. package/README.md +63 -20
  2. package/dist/PlanGraphPane.d.ts +19 -0
  3. package/dist/_memory-core-generated.d.ts +1764 -0
  4. package/dist/agent-loop.d.ts +31 -8
  5. package/dist/ambiguity-guard-flag.d.ts +12 -0
  6. package/dist/ambiguity-guard.d.ts +20 -0
  7. package/dist/answer-claim.d.ts +13 -1
  8. package/dist/ask-clarification-service.d.ts +12 -1
  9. package/dist/ask-response-format.d.ts +6 -0
  10. package/dist/assistant-session.d.ts +16 -5
  11. package/dist/assistant-source.d.ts +7 -0
  12. package/dist/assistant-types.d.ts +25 -1
  13. package/dist/assistant.d.ts +166 -5
  14. package/dist/benchmark-injected-failure.d.ts +6 -0
  15. package/dist/bin-CjKpxDxl.js +4836 -0
  16. package/dist/bin-CjKpxDxl.js.map +1 -0
  17. package/dist/cli-session.d.ts +26 -1
  18. package/dist/cli.d.ts +20 -2
  19. package/dist/cli.js +1 -1
  20. package/dist/code-plan-templates.d.ts +21 -0
  21. package/dist/config.d.ts +51 -0
  22. package/dist/constraint-check.d.ts +35 -0
  23. package/dist/contradiction-checker.d.ts +7 -0
  24. package/dist/decomposed-answer.d.ts +13 -0
  25. package/dist/decomposition-classifier.d.ts +2 -0
  26. package/dist/doctor-checks.d.ts +3 -0
  27. package/dist/episodic-digest.d.ts +89 -0
  28. package/dist/fact-extraction.d.ts +39 -0
  29. package/dist/file-tools-mcp-server.mjs +138 -12
  30. package/dist/file-tools.d.ts +4 -0
  31. package/dist/first-run.d.ts +3 -2
  32. package/dist/goal-graph-flag.d.ts +44 -0
  33. package/dist/goal-graph-reconcile.d.ts +55 -0
  34. package/dist/goal-graph-service.d.ts +48 -0
  35. package/dist/goal-graph-store.d.ts +485 -0
  36. package/dist/goal-graph-suggest-flag.d.ts +34 -0
  37. package/dist/goal-identity-matcher.d.ts +21 -0
  38. package/dist/goal-thread-identity.d.ts +32 -0
  39. package/dist/goal-thread-scheduler.d.ts +78 -0
  40. package/dist/grounding-check.d.ts +33 -0
  41. package/dist/harness-bridge.d.ts +75 -2
  42. package/dist/harness-token-budget.d.ts +9 -0
  43. package/dist/index.d.ts +30 -1
  44. package/dist/index.js +166 -80
  45. package/dist/index.js.map +1 -1
  46. package/dist/layer-policy-flag.d.ts +13 -0
  47. package/dist/layer-policy-wiring.d.ts +77 -0
  48. package/dist/layer-settings.d.ts +49 -0
  49. package/dist/layer-telemetry.d.ts +2 -0
  50. package/dist/lexical/lexical-mode.d.ts +29 -0
  51. package/dist/live-steering-channel.d.ts +31 -0
  52. package/dist/memory-consolidation.d.ts +80 -0
  53. package/dist/memory-governance.d.ts +30 -0
  54. package/dist/memory-reviewer.d.ts +114 -0
  55. package/dist/memory-service.d.ts +348 -9
  56. package/dist/model-json.d.ts +17 -0
  57. package/dist/next-step-context.d.ts +51 -0
  58. package/dist/next-step-proposer.d.ts +53 -0
  59. package/dist/plan-approval-service.d.ts +1 -1
  60. package/dist/plan-drafting-service.d.ts +1 -1
  61. package/dist/plan-graph-flag.d.ts +20 -0
  62. package/dist/plan-question.d.ts +60 -0
  63. package/dist/plan-service.d.ts +6 -2
  64. package/dist/plan-step-prompt.d.ts +15 -0
  65. package/dist/plan-store.d.ts +7 -1
  66. package/dist/plan-viz/normalize.d.ts +14 -0
  67. package/dist/plan-viz/plan-detail-lines.d.ts +3 -0
  68. package/dist/plan-viz/plan-layout.d.ts +48 -0
  69. package/dist/plan-viz/plan-nav.d.ts +29 -0
  70. package/dist/plan-viz/plan-raster.d.ts +36 -0
  71. package/dist/plan-viz/plan-routes.d.ts +6 -0
  72. package/dist/plan-viz/plan-snapshot.d.ts +73 -0
  73. package/dist/plan-viz/plan-viewport.d.ts +29 -0
  74. package/dist/plan-viz/plan-width.d.ts +15 -0
  75. package/dist/plan-viz/testkit/display-width.d.ts +12 -0
  76. package/dist/plan-viz/testkit/generators.d.ts +61 -0
  77. package/dist/plan-viz/testkit/grid-oracles.d.ts +38 -0
  78. package/dist/plan-viz/testkit/nav-oracles.d.ts +79 -0
  79. package/dist/plan-viz/testkit/types.d.ts +8 -0
  80. package/dist/plan-viz/types.d.ts +34 -0
  81. package/dist/provider-setup-LFsaTkpf.js +16606 -0
  82. package/dist/provider-setup-LFsaTkpf.js.map +1 -0
  83. package/dist/provider-setup.d.ts +51 -0
  84. package/dist/recall-tool.d.ts +43 -0
  85. package/dist/recovery-note.d.ts +22 -0
  86. package/dist/response-service.d.ts +28 -2
  87. package/dist/review-checker.d.ts +10 -4
  88. package/dist/reviewer-revision.d.ts +23 -0
  89. package/dist/risk-classifier.d.ts +7 -0
  90. package/dist/scope-urgency-classifier.d.ts +29 -0
  91. package/dist/scripted-llm-client.d.ts +26 -1
  92. package/dist/semantic-compaction.d.ts +15 -0
  93. package/dist/semantic-hypotheses.d.ts +40 -0
  94. package/dist/source-reliability.d.ts +71 -0
  95. package/dist/supervisor-flag.d.ts +20 -0
  96. package/dist/task-completion-check.d.ts +24 -0
  97. package/dist/tool-control-plane.d.ts +33 -1
  98. package/dist/tool-effect-class.d.ts +9 -0
  99. package/dist/transcript-compaction.d.ts +9 -0
  100. package/dist/{tui-app-CFfOP2DC.js → tui-app-BLR-PRFp.js} +321 -7
  101. package/dist/tui-app-BLR-PRFp.js.map +1 -0
  102. package/dist/tui-app.d.ts +38 -0
  103. package/dist/turn-intent-classifier.d.ts +75 -16
  104. package/dist/turn-interpreter.d.ts +25 -3
  105. package/dist/version.d.ts +1 -1
  106. package/package.json +7 -7
  107. package/dist/bin-Btzhiyzi.js +0 -2108
  108. package/dist/bin-Btzhiyzi.js.map +0 -1
  109. package/dist/cli-session-DnSc6Hhx.js +0 -8343
  110. package/dist/cli-session-DnSc6Hhx.js.map +0 -1
  111. package/dist/tui-app-CFfOP2DC.js.map +0 -1
@@ -10,7 +10,9 @@ import { FileToolsContext } from './file-tools.js';
10
10
  import { WebToolsContext } from './web-tools.js';
11
11
  import { ShellToolsContext } from './shell-tools.js';
12
12
  import { ActionToolsContext } from './action-tools.js';
13
+ import { DigestReader } from './recall-tool.js';
13
14
  import { AssistantToolStep } from './tool-step.js';
15
+ import { EscalationPlan } from './layer-policy-wiring.js';
14
16
  export type ToolLoopResult = {
15
17
  kind: 'final';
16
18
  content: string;
@@ -133,13 +135,6 @@ export interface BatchBudgetTrace {
133
135
  * path and both resolvePendingBatchConfirmation outcomes (see ActionApprovalService). */
134
136
  export declare function buildBatchBudgetTrace(itemCount: number, projectedTotal: number, resolutions: BatchItemResolution[]): BatchBudgetTrace;
135
137
  export declare function looksLikeUnparsedToolCall(content: string): boolean;
136
- /**
137
- * Bounded ReAct loop plus the batch-research sub-loop — the "actually call the model and its
138
- * tools" half of PersonalAssistant, split out in Phase 4d of the architecture remediation plan.
139
- * Owns tool-control-plane enforcement (checkToolPolicy, below) as part of the same loop it gates,
140
- * per Phase 4's design: a deterministic, harness-state-informed check before each call executes,
141
- * not just advisory classification checked after the fact.
142
- */
143
138
  export declare class AgentLoop {
144
139
  private readonly memory;
145
140
  private readonly llmClient;
@@ -196,6 +191,27 @@ export declare class AgentLoop {
196
191
  onToolStep?: (step: AssistantToolStep) => void;
197
192
  onUsage?: (usage: TokenUsage) => void;
198
193
  riskHint?: TurnIntentClassification['riskLevel'];
194
+ /**
195
+ * Mid-turn steering (goalGraphMode): messages the user sent while this turn runs that the
196
+ * scope×urgency classifier judged apply to the answer being produced right now. Called once
197
+ * per iteration, immediately before the LLM call; anything returned is spliced in as a user
198
+ * turn so the model itself applies it — no lexical matching, unlike the harness's
199
+ * caller-constraint output contract.
200
+ */
201
+ takeSteeringNotes?: () => string[];
202
+ /**
203
+ * Plan steps: the instruction to give the model when the harness starts working on `taskId`, or
204
+ * undefined for a task that is just "answer the user's message" (every ordinary turn). Sent once per
205
+ * task; the step's final answer is then kept in the conversation so a later step can build on it.
206
+ */
207
+ stepInstruction?: (taskId: string) => string | undefined;
208
+ /**
209
+ * True while the harness is running several subtasks that all stand for ONE answer to the user's
210
+ * message (an ordinary decomposed turn, not a plan). The first subtask to finish produces the answer;
211
+ * the rest reuse it instead of asking the model the same question again. A task the harness runs a
212
+ * second time (a retry after a rejection) gets a fresh call, never the answer that was rejected.
213
+ */
214
+ shareAnswer?: () => boolean;
199
215
  }): (toolCtx: ToolExecutorContext) => Promise<unknown>;
200
216
  /**
201
217
  * R3 of the internal plan: the wiring-level counterpart to
@@ -208,7 +224,7 @@ export declare class AgentLoop {
208
224
  * back whatever got pushed to it once the harness run using this proposer has finished (mirrors
209
225
  * `ToolLoopResult`'s `sources` field on the flag-OFF path).
210
226
  */
211
- createOneLoopProposer(sessionId: string, transcript: ChatMessage[], userMessage: string, systemPrompt: string, onToken?: (token: string) => void, onToolStep?: (step: AssistantToolStep) => void, onUsage?: (usage: TokenUsage) => void, riskHint?: TurnIntentClassification['riskLevel']): {
227
+ createOneLoopProposer(sessionId: string, transcript: ChatMessage[], userMessage: string, systemPrompt: string, onToken?: (token: string) => void, onToolStep?: (step: AssistantToolStep) => void, onUsage?: (usage: TokenUsage) => void, riskHint?: TurnIntentClassification['riskLevel'], takeSteeringNotes?: () => string[], stepInstruction?: (taskId: string) => string | undefined, shareAnswer?: () => boolean): {
212
228
  proposer: (toolCtx: ToolExecutorContext) => Promise<unknown>;
213
229
  sources: AssistantSource[];
214
230
  };
@@ -347,6 +363,13 @@ export declare class AgentLoop {
347
363
  * injection attempt) before they ever reach the model — file and reminder results
348
364
  * are not, since they're the assistant's own workspace/state, not adversarial input.
349
365
  */
366
+ /** AL8b: optional policy gate for LLM injection detection; `undefined` (default) ⇒ always detect. */
367
+ injectionDetectionGate?: () => boolean;
368
+ /** Episodic digest source for `recall_memory` (M3); set by the assistant. The tool is offered only when this is set AND AUDIT_RECALL_TOOL is on. */
369
+ digestReader?: DigestReader;
370
+ private recallTools;
371
+ /** The opt-in layers' plan for the turn in flight (adaptive mode only), set by the assistant each turn; see resolveOptInPlan. */
372
+ optInPlan?: EscalationPlan;
350
373
  private executeToolCall;
351
374
  /**
352
375
  * Trajectory Supervisor GATHER_EVIDENCE host (S5 of
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Flag for the AL3a ambiguity guard — same shape as ask-mode-flag.ts (module-level default, value-level
3
+ * normalizer that warns on a typo, env resolver). 'disabled' (the default, AL-8) keeps every turn on the
4
+ * pre-AL3a routing byte-for-byte: an under-specified consequential request still reaches `needs_approval`
5
+ * with a guessed action. 'enabled' runs one bounded scope check (ambiguity-guard.ts) on consequential
6
+ * turns and answers with a clarifying question *before* anything is staged when the request does not
7
+ * determine the action.
8
+ */
9
+ export type AmbiguityGuardMode = 'enabled' | 'disabled';
10
+ export declare const DEFAULT_AMBIGUITY_GUARD_MODE: AmbiguityGuardMode;
11
+ export declare function normalizeAmbiguityGuardMode(raw: string | undefined, varName?: string): AmbiguityGuardMode;
12
+ export declare function resolveAmbiguityGuardMode(env: NodeJS.ProcessEnv): AmbiguityGuardMode;
@@ -0,0 +1,20 @@
1
+ import { ChatMessage, ILLMClient, TokenUsage } from '@buildaharness/runtime';
2
+ /**
3
+ * AL3a — semantic pre-staging scope check. Root cause it addresses: the classifier gates a
4
+ * consequential request on *risk*, never on whether the request is *determinate*, and the tool loop
5
+ * stages whatever action the proposer guesses (write_file/run_shell_command stage unconditionally). So
6
+ * "Clear out the old ones." reached `needs_approval` with a guessed deletion — the approval prompt then
7
+ * authorizes a guess instead of resolving it. The system prompt's "ask when genuinely ambiguous" is
8
+ * advisory and the proposer ignores it once a tool is in reach.
9
+ *
10
+ * This is one bounded LLM call, made only on consequential turns and only when the guard flag is
11
+ * enabled — a semantic judgment, not a lexical gate (AL-2). It fails open to "not ambiguous": staging
12
+ * and the approval gate remain the safety net, so a failed check never blocks a clear request.
13
+ */
14
+ export interface AmbiguityCheck {
15
+ ambiguous: boolean;
16
+ /** One concrete clarifying question; only meaningful when `ambiguous`. */
17
+ question: string;
18
+ }
19
+ export declare function parseAmbiguityCheck(content: string): AmbiguityCheck | null;
20
+ export declare function checkRequestAmbiguity(message: string, llmClient: ILLMClient, recentTranscript: ChatMessage[], model?: string, onUsage?: (usage: TokenUsage) => void): Promise<AmbiguityCheck>;
@@ -1,4 +1,5 @@
1
1
  import { Evidence, VerificationResult } from '@buildaharness/harness';
2
+ import { GroundingResult } from './grounding-check.js';
2
3
  /**
3
4
  * Where the evidence backing a reply actually came from — 'tool_evidence' when at least one
4
5
  * real observation (a gather_evidence call, a real read_file/web_search result, ...) was
@@ -10,7 +11,9 @@ export type AnswerClaimSourceType = 'tool_evidence' | 'model_reasoning';
10
11
  * The four branches this plan's Validation section names explicitly — each backed by a real,
11
12
  * mechanically-derived signal (see buildAnswerClaim), never guessed from the reply text itself:
12
13
  * - 'verified': the verification layer found no critical failure and at least one layer
13
- * actually PASSed (not just SKIPPED) against real evidence.
14
+ * actually PASSed (not just SKIPPED) against real evidence — and, when the grounding check is
15
+ * on, the reply itself was compared against the raw tool results and matched them. The
16
+ * mechanical layers alone never look at the reply's content, so they cannot earn this.
14
17
  * - 'unverified_attempted': evidence was gathered, but no layer could independently confirm it
15
18
  * (all SKIPPED/no tool to check with) — an honest "couldn't verify," not a fake PASS.
16
19
  * - 'contradicted': the Contradiction layer flagged a conflict with an existing belief this turn.
@@ -32,6 +35,8 @@ export interface AnswerClaim {
32
35
  freshness: string | null;
33
36
  source_type: AnswerClaimSourceType;
34
37
  verification_status: AnswerClaimVerificationStatus;
38
+ /** One sentence naming what in the reply didn't match the tool results — set only when the grounding check found a discrepancy. */
39
+ grounding_note?: string;
35
40
  }
36
41
  /**
37
42
  * Builds an AnswerClaim from one turn's real harness output. `verification` is the last
@@ -48,4 +53,11 @@ export declare function buildAnswerClaim(input: {
48
53
  strength: number;
49
54
  feasibility: number;
50
55
  };
56
+ /**
57
+ * The reply-vs-tool-results check (grounding-check.ts). `undefined` means the check is switched
58
+ * off (`AUDIT_SEMANTIC_GROUNDING`) and `verified` keeps its mechanical-only meaning; any
59
+ * `GroundingResult` — including `not_checked` — means the check is on, so `verified` requires
60
+ * `grounded`.
61
+ */
62
+ grounding?: GroundingResult;
51
63
  }): AnswerClaim;
@@ -66,5 +66,16 @@ export declare class AskClarificationService {
66
66
  * back through `stageAndRespond` exactly like the first, via the same catch this resume goes
67
67
  * through.
68
68
  */
69
- resolvePendingClarification(sessionId: string, transcriptKey: string, pendingClarificationId: string, response: AskResponse | undefined, askModeEnabled: boolean): Promise<AssistantTurnResult>;
69
+ resolvePendingClarification(sessionId: string, transcriptKey: string, pendingClarificationId: string, response: AskResponse | undefined, askModeEnabled: boolean,
70
+ /**
71
+ * True when tool turns run through the one-loop proposer (the default). A resumed harness run has NO proposer, so it
72
+ * could only return the stored draft reply — empty for a tool turn, which is what the user got — and the answer,
73
+ * fed into the constraint pipeline, is never read by the proposer's model. In that mode the validated answer is
74
+ * handed to the ordinary turn pipeline instead (`{ fallThrough: true }`, the same shape a plan approval uses), and
75
+ * the paused run is discarded.
76
+ */
77
+ handOffToOrdinaryTurn?: boolean): Promise<AssistantTurnResult | {
78
+ fallThrough: true;
79
+ answerText: string;
80
+ }>;
70
81
  }
@@ -5,4 +5,10 @@ import { AskQuestion, AskResponse } from '@buildaharness/harness';
5
5
  * resolution (P8, continuing a plan draft), both of which fold the user's answer into a plain-text
6
6
  * follow-up message for their own "next call" rather than duplicating this formatting logic.
7
7
  */
8
+ /**
9
+ * The user-visible text of a batch of structured questions: each question, then its numbered options. Used for the
10
+ * debug log (a `needs_clarification` turn has `reply: null`, so its log line used to read "(no reply)") and by the
11
+ * eval arm, so the transcript a judge reads carries what the user was actually asked.
12
+ */
13
+ export declare function formatAskQuestions(questions: AskQuestion[]): string;
8
14
  export declare function formatAskResponse(questions: AskQuestion[], response: AskResponse): string;
@@ -64,6 +64,13 @@ export declare class AssistantSession {
64
64
  private readonly notifiedContradictions;
65
65
  constructor(memory: MemoryAdapter, checkpointStore: CheckpointStore, spendCap: SpendCapConfig | undefined, model: () => string | undefined, fileTools: FileToolsContext | undefined, shellTools: ShellToolsContext | undefined, actionTools: ActionToolsContext | undefined);
66
66
  private static notifiedContradictionsKey;
67
+ private static standingConstraintsKey;
68
+ /** Constraints the user stated earlier in this session ("Do not use tabs") — they govern later turns too. */
69
+ getStandingConstraints(sessionId: string): Promise<string[]>;
70
+ /** Drops the standing constraints at these 1-based positions (as shown to the classifier); returns what remains. */
71
+ liftStandingConstraints(sessionId: string, positions: readonly number[]): Promise<string[]>;
72
+ /** Adds constraints stated this turn to the session's standing list; returns the merged list. */
73
+ recordStandingConstraints(sessionId: string, stated: readonly string[]): Promise<string[]>;
67
74
  getNotifiedContradictions(sessionId: string): Promise<Set<string>>;
68
75
  recordNotifiedContradiction(sessionId: string, seen: Set<string>, value: string): Promise<void>;
69
76
  /** findContradictionNotice's own text, deduped once per session — see notifiedContradictions'
@@ -99,8 +106,8 @@ export declare class AssistantSession {
99
106
  * isn't set.
100
107
  */
101
108
  recordSpend(sessionId: string, usage: TokenUsage | undefined): Promise<void>;
102
- /** Read-only — null means plan mode has never been entered (or was already exited/cancelled) for this session. */
103
- getPlanModeState(sessionId: string): Promise<PlanModeState | null>;
109
+ /** Read-only — null means plan mode has never been entered (or was already exited/cancelled) for this session (or this `threadId`, once Phase 5's per-thread rescoping applies — see `planModeKey`). */
110
+ getPlanModeState(sessionId: string, threadId?: string): Promise<PlanModeState | null>;
104
111
  /**
105
112
  * The one place `planMode.active` ever flips true — a fresh `draftId` per entry, even if a
106
113
  * prior drafting session was cancelled without ever reaching approval, so a stale draftId can
@@ -108,9 +115,9 @@ export declare class AssistantSession {
108
115
  * a new state, same as a fresh entry) — callers that only ever call this once per drafting
109
116
  * session (P3's future auto-trigger) never observe that.
110
117
  */
111
- enterPlanMode(sessionId: string): Promise<PlanModeState>;
118
+ enterPlanMode(sessionId: string, threadId?: string): Promise<PlanModeState>;
112
119
  /** Clears `planMode.active` — the only two designed callers are an explicit cancel phrase (PlanDraftingService) and P2's future approval resolution. */
113
- exitPlanMode(sessionId: string): Promise<void>;
120
+ exitPlanMode(sessionId: string, threadId?: string): Promise<void>;
114
121
  /** The session's conversation transcript, oldest first — same array `turn()` reads/appends to. Used by `/export`. */
115
122
  getTranscript(sessionId: string): Promise<ChatMessage[]>;
116
123
  /**
@@ -120,7 +127,11 @@ export declare class AssistantSession {
120
127
  * (rather than the sequencer reading `memory` directly) so this remains the one place that reads
121
128
  * `transcript:${sessionId}` for a live turn.
122
129
  */
123
- loadAndCompactTranscript(sessionId: string): Promise<ChatMessage[]>;
130
+ loadAndCompactTranscript(sessionId: string,
131
+ /** AUDIT_SEMANTIC_COMPACTION: summarizes the older messages instead of truncating them (semantic-compaction.ts). */
132
+ summarize?: (older: ChatMessage[]) => Promise<string | null>,
133
+ /** AUDIT_EPISODIC_DIGEST: called with exactly the messages about to be dropped, before they are (M3 pre-compaction flush). Fail-open: a throw never blocks compaction. */
134
+ flush?: (older: ChatMessage[]) => Promise<unknown>): Promise<ChatMessage[]>;
124
135
  /**
125
136
  * Appends `message` to `transcriptKey`'s array — every site that used to call
126
137
  * `this.memory.set(transcriptKey, message, 'append')` directly now goes through here instead, so
@@ -11,4 +11,11 @@
11
11
  export interface AssistantSource {
12
12
  tool: 'read_file' | 'list_directory' | 'web_search' | 'fetch_url';
13
13
  path: string;
14
+ /**
15
+ * The (truncated) raw text the tool returned — what the answer-claim grounding check compares the reply against
16
+ * (grounding-check.ts). Internal only: ResponseService strips it before a source reaches AssistantTurnResult, so it
17
+ * never lands in a transcript, a trace or the UI. Absent when the result text isn't visible to the parent process
18
+ * (claude-cli runs the tool loop inside its own subprocess).
19
+ */
20
+ excerpt?: string;
14
21
  }
@@ -1,4 +1,5 @@
1
- import { RiskState, LayerActivityEvent, AskQuestion } from '@buildaharness/harness';
1
+ import { NextStepSuggestion } from './next-step-proposer.js';
2
+ import { RiskState, LayerActivityEvent, AskQuestion, TaskStatus } from '@buildaharness/harness';
2
3
  import { TokenUsage } from '@buildaharness/runtime';
3
4
  import { AnswerClaim } from './answer-claim.js';
4
5
  import { TurnIntentClassification } from './turn-intent-classifier.js';
@@ -63,6 +64,13 @@ export interface AssistantTurnResult {
63
64
  */
64
65
  status: 'ok' | 'needs_approval' | 'escalated' | 'needs_clarification' | 'needs_plan_approval';
65
66
  reply: string | null;
67
+ /**
68
+ * Up to three concrete next steps proposed after a full (non-trivial, `ok`) turn, most confident
69
+ * first — for the front end to show under the reply. Advisory only: never executed, never
70
+ * persisted from here. Present only when the caller enabled `goalGraphSuggestMode`
71
+ * (PersonalAssistantOptions) and the proposer returned at least one; absent otherwise.
72
+ */
73
+ nextSteps?: NextStepSuggestion[];
66
74
  reason?: string;
67
75
  /** See ProposerKind. Always set (defaults to 'posthoc'), on every status. */
68
76
  proposerKind?: ProposerKind;
@@ -93,10 +101,15 @@ export interface AssistantTurnResult {
93
101
  templateName: string | null;
94
102
  successCriteria: string;
95
103
  completionPct: number;
104
+ /** `note`: why a FAILED task did not count as done (set only by the semantic task-completion check). */
96
105
  tasks: {
97
106
  id: string;
98
107
  description: string;
99
108
  status: string;
109
+ note?: string;
110
+ dependsOn?: string[];
111
+ cancelled?: boolean;
112
+ riskLevel?: 'LOW' | 'MEDIUM' | 'HIGH';
100
113
  }[];
101
114
  };
102
115
  /**
@@ -109,6 +122,8 @@ export interface AssistantTurnResult {
109
122
  usage?: TokenUsage;
110
123
  /** Set when the Contradiction layer flagged a conflict with an existing belief this turn — see assistant-session.ts's findContradictionNotice doc comment for why this is a separate field instead of folded into `reply`. */
111
124
  contradictionNotice?: string;
125
+ /** Set when the semantic change reviewer found the request conflicts with something the user stated earlier AND the answer could not be told so directly (a tool-less turn's draft already exists, so the proposer never reads the review note). Advisory only — the turn still completed. Mirrors `contradictionNotice`: a separate field, not folded into `reply`, because the CLI streams `reply` before the check runs. */
126
+ reviewNotice?: string;
112
127
  /**
113
128
  * An epistemic-honesty signal for replies that went through the harness loop (absent on the
114
129
  * triviality fast path, same "absent when unused" convention as trace/sources — a
@@ -155,4 +170,13 @@ export interface AssistantProgress {
155
170
  currentNode?: string;
156
171
  /** Live, mid-run position within a durable plan — set only while a plan is actually driving this turn. Absent for an ad hoc single-task/decomposed turn, same "absent when unused" convention as AssistantTurnResult.planStatus. */
157
172
  planPosition?: PlanPosition;
173
+ /**
174
+ * Live per-task statuses of the plan driving this turn, for the plan graph view. Present only while a
175
+ * plan drives the turn (same "absent when unused" convention as `planPosition`). Granularity is per
176
+ * harness checkpoint (once per task), not per token: it changes when a task starts or finishes.
177
+ */
178
+ planTasks?: {
179
+ id: string;
180
+ status: TaskStatus;
181
+ }[];
158
182
  }
@@ -1,4 +1,4 @@
1
- import { ExperienceStore, CheckpointStore, AskResponse } from '@buildaharness/harness';
1
+ import { ExperienceStore, CheckpointStore, AskResponse, LayerPolicyMode } from '@buildaharness/harness';
2
2
  import { MemoryAdapter, ILLMClient, ChatMessage, ReminderStore } from '@buildaharness/runtime';
3
3
  import { FileToolsContext } from './file-tools.js';
4
4
  import { UndoLogEntry } from './action-snapshot.js';
@@ -8,19 +8,28 @@ import { ActionToolsContext } from './action-tools.js';
8
8
  import { SpendCapConfig, SpendState } from './spend-cap.js';
9
9
  import { TraceEvent } from './trace-events.js';
10
10
  import { AssistantToolStep } from './tool-step.js';
11
- import { MemorySummary, MemoryExport } from './memory-service.js';
11
+ import { MemoryService, MemorySummary, MemoryExport, AuditEntry, MemoryStatus, MemoryInjection, ConsolidationOutcome } from './memory-service.js';
12
+ import { MemoryWriteMode } from './memory-governance.js';
13
+ import { ConsolidationProposal } from './memory-consolidation.js';
12
14
  import { UserFact } from './fact-extraction.js';
13
15
  import { TranscriptSearchHit } from './assistant-session.js';
16
+ import { SessionDigest } from './episodic-digest.js';
14
17
  import { PlanDecision, PlanApprovalEdits } from './plan-approval-service.js';
15
18
  import { PlanRecord } from './plan-store.js';
19
+ import { GoalGraphRecord } from './goal-graph-store.js';
16
20
  import { OneLoopMode } from './one-loop-flag.js';
17
21
  import { AskMode } from './ask-mode-flag.js';
22
+ import { AmbiguityGuardMode } from './ambiguity-guard-flag.js';
18
23
  import { PlanRolloutMode } from './plan-mode-flag.js';
24
+ import { GoalGraphSuggestMode } from './goal-graph-suggest-flag.js';
25
+ import { GoalGraphState } from './goal-graph-service.js';
26
+ import { LiveSteeringChannel } from './live-steering-channel.js';
19
27
  import { DebugLogEntry } from './debug-log.js';
20
28
  import { AssistantTurnResult, AssistantProgress } from './assistant-types.js';
21
29
  export type { MemorySummary, MemoryExport, PendingFact } from './memory-service.js';
22
30
  export type { FactCategory } from './turn-intent-classifier.js';
23
31
  export type { IndexedMessage, TranscriptSearchHit } from './assistant-session.js';
32
+ export type { GoalGraphState, GoalThreadView, GoalThreadVisibility, GoalTaskSummary } from './goal-graph-service.js';
24
33
  export type { BatchBudgetState } from './agent-loop.js';
25
34
  export { trimmedAverage, nextItemBudget } from './agent-loop.js';
26
35
  export type { AssistantSource } from './assistant-source.js';
@@ -39,6 +48,8 @@ export type MemoryPendingOutcome = {
39
48
  };
40
49
  export interface TurnOptions {
41
50
  sessionId?: string;
51
+ /** M4: a scripted / piped / batch caller. The post-turn memory reviewer is skipped for it (it only runs for an interactive session). */
52
+ nonInteractive?: boolean;
42
53
  approved?: boolean;
43
54
  pendingActionId?: string;
44
55
  /**
@@ -93,7 +104,7 @@ export interface TurnOptions {
93
104
  */
94
105
  onToolStep?: (step: AssistantToolStep) => void;
95
106
  /**
96
- * **Eval harness only** (`packages/aielia/eval/`). Wraps the one-loop
107
+ * **Benchmark harness only.** Wraps the one-loop
97
108
  * proposer so its first `failIterations` harness iterations report a failed execution
98
109
  * and `seedFailures` recurring same-class records are seeded into the run's
99
110
  * `failureDiagnostics` — enough to trip `cannotMakeProgress()` on iteration 1 and
@@ -104,7 +115,23 @@ export interface TurnOptions {
104
115
  failIterations: number;
105
116
  seedFailures: number;
106
117
  onInjected?: () => void;
118
+ symptom?: string;
107
119
  };
120
+ /**
121
+ * Phase 4 of plans/hierarchical_goal_tree_and_steering_plan.html (R1/R4) — the session-scoped
122
+ * LiveSteeringChannel a caller (cli.ts/App.tsx, Phase 3) built for mid-task steering. When
123
+ * present, `turn()` wraps it in a scope×urgency-classifying UpdateChannel
124
+ * (goal-graph-reconcile.ts) and threads it into harnessBridge.run() so queued messages are
125
+ * absorbed at the harness's own checkCallerUpdates iteration boundary instead of waiting for
126
+ * the turn to finish. PersonalAssistant never reads goalGraphMode itself (same convention as
127
+ * oneLoopMode/askMode/planMode) — the caller decides whether to pass this at all; when absent,
128
+ * behavior is exactly today's (INV-43). Whatever this turn's harness run doesn't get around to
129
+ * classifying (it ends before the queue drains, or never calls harnessBridge.run() at all — a
130
+ * trivial turn) is pushed back onto this same channel in a `finally`, so the caller's own
131
+ * post-turn fallback drain (built in Phase 3) still picks it up — R4's "never silently drop"
132
+ * holds either way.
133
+ */
134
+ steeringChannel?: LiveSteeringChannel;
108
135
  }
109
136
  export interface PersonalAssistantOptions {
110
137
  llmClient: ILLMClient;
@@ -118,6 +145,14 @@ export interface PersonalAssistantOptions {
118
145
  * fresh install with no workspace concept at all.
119
146
  */
120
147
  activeProject?: string;
148
+ /** M1: character budget for the facts block under `AUDIT_MEMORY_BUDGETED_RENDER` — see config.ts's `memoryBudgetChars`. */
149
+ memoryBudgetChars?: number;
150
+ /** M6: where model-originated memory writes may land (`auto`/`staged`/`user_only`, default `staged`) — see memory-governance.ts. Changeable live via `setMemoryWriteMode`. */
151
+ memoryWriteMode?: MemoryWriteMode;
152
+ /**
153
+ * M5: run memory consolidation in the background on `/new` (`clearSession`). Off unless the host sets it: the CLI does; chat-ui must not until M6's pending-queue controls exist (D1 stages every proposal, and a browser user could not review one). Still gated by `AUDIT_MEMORY_CONSOLIDATION`.
154
+ */
155
+ consolidateOnNewSession?: boolean;
121
156
  /** Conversation transcript storage — defaults to an in-process Map, swap for IndexedDBAdapter in the browser. */
122
157
  memory?: MemoryAdapter;
123
158
  /** Learning-layer store — persist and pass the same instance back in across sessions to retain strategy weights. */
@@ -207,6 +242,18 @@ export interface PersonalAssistantOptions {
207
242
  * call resolveOneLoopMode(process.env) and pass the result here.
208
243
  */
209
244
  oneLoopMode?: OneLoopMode;
245
+ /** AL8a — layer policy mode ('static' default = today's behaviour). Resolved by the surface via resolveLayerPolicyMode; PersonalAssistant never reads process.env for it. */
246
+ layerPolicyMode?: LayerPolicyMode;
247
+ /**
248
+ * R7 (Tier 1.5) — next-step suggestions, one bounded LLM call each: after every full
249
+ * (non-trivial, `ok`) turn, up to three options are attached to the result as `nextSteps` for
250
+ * the front end to show under the reply; and when a GoalThread finishes its last task, its own
251
+ * suggestions are persisted on the thread for `/goals`. Defaults to 'disabled' — like
252
+ * oneLoopMode/askMode/planMode, the front end resolves the flag (cli.ts / App.tsx pass
253
+ * `config.goalGraphSuggestMode`, which defaults to enabled), so a library or benchmark caller
254
+ * that never asks for suggestions makes no extra call and sees no new field (INV-43).
255
+ */
256
+ goalGraphSuggestMode?: GoalGraphSuggestMode;
210
257
  /**
211
258
  * Q2 of the internal plan — global-flag control point (tier 1 of
212
259
  * INV-29) for the ask-question mechanism. Undefined (the default) falls back to
@@ -227,6 +274,13 @@ export interface PersonalAssistantOptions {
227
274
  * resolvePlanMode(process.env)/normalizePlanMode(import.meta.env...) and pass the result here.
228
275
  */
229
276
  planMode?: PlanRolloutMode;
277
+ /**
278
+ * AL3a — see ambiguity-guard-flag.ts. Absent/'disabled' (the default): routing is byte-identical to
279
+ * before. 'enabled': a consequential turn whose request doesn't determine the action gets a
280
+ * clarifying question instead of a staged guess. PersonalAssistant never reads process.env; the
281
+ * surface entry point resolves ASSISTANT_AMBIGUITY_GUARD / `/config set ambiguityGuardMode`.
282
+ */
283
+ ambiguityGuardMode?: AmbiguityGuardMode;
230
284
  }
231
285
  /**
232
286
  * A light, everyday-use wrapper around HarnessRuntime: one harness run per chat turn,
@@ -248,6 +302,9 @@ export declare class PersonalAssistant {
248
302
  private readonly llmClient;
249
303
  private model?;
250
304
  private activeProject?;
305
+ private memoryBudgetChars?;
306
+ private memoryWriteMode;
307
+ private consolidateOnNewSession;
251
308
  private readonly memory;
252
309
  private readonly webTools?;
253
310
  private readonly onTrace?;
@@ -257,6 +314,7 @@ export declare class PersonalAssistant {
257
314
  private readonly toolLoopWillRun;
258
315
  /** R3 of the internal plan — mirrors the same flag HarnessBridge was given at construction, kept here too so runTurn can decide whether to defer the tool loop into a harness-driven proposer instead of precomputing draftReply. See PersonalAssistantOptions.oneLoopMode's doc comment. */
259
316
  private readonly oneLoopMode;
317
+ private readonly layerPolicyMode;
260
318
  /** Q2 — global-flag tier of the ask-question mechanism's three-tier INV-29 resolution. See PersonalAssistantOptions.askMode's doc comment. */
261
319
  private readonly askMode;
262
320
  /** P11 — gates whether P3's auto-trigger below can ever enter plan mode for real traffic. See PersonalAssistantOptions.planMode's doc comment. */
@@ -269,7 +327,11 @@ export declare class PersonalAssistant {
269
327
  * the internal plan phase B1.
270
328
  */
271
329
  private lastProposerKind;
330
+ /** See PersonalAssistantOptions.goalGraphSuggestMode. */
331
+ private readonly goalGraphSuggestMode;
272
332
  private readonly memoryService;
333
+ /** M4 post-turn cross-turn memory reviewer; inert unless `AUDIT_MEMORY_REVIEWER` is on. */
334
+ private readonly memoryReviewer;
273
335
  private readonly session;
274
336
  private readonly agentLoop;
275
337
  private readonly actionApproval;
@@ -290,12 +352,29 @@ export declare class PersonalAssistant {
290
352
  * PersonalAssistant(...)` outside a browser (e.g. the CLI).
291
353
  */
292
354
  static create(options: PersonalAssistantOptions): Promise<PersonalAssistant>;
355
+ /**
356
+ * The bigger picture both next-step proposers get (see next-step-context.ts): earlier
357
+ * conversation, the steps taken this turn, and every goal thread the session tracks. Best-effort —
358
+ * any read failure just yields no extra context, never a failed turn. At turn end the focus
359
+ * thread is the one the graph currently marks active; for a thread that just finished it is that
360
+ * thread.
361
+ */
362
+ private nextStepContext;
293
363
  /**
294
364
  * Thin wrapper around runTurn(): emits turn_start/turn_end/error trace events
295
365
  * around the actual logic, so every one of runTurn's return paths gets a
296
366
  * matching turn_end without instrumenting each one individually.
297
367
  */
298
368
  turn(userMessage: string, options?: TurnOptions): Promise<AssistantTurnResult>;
369
+ /**
370
+ * M4: after a delivered, ordinary interactive reply, counts the turn and (every N turns without a memory write) starts the
371
+ * cross-turn memory review in the background. Not awaited: the reply is already on its way. Skipped for non-interactive and
372
+ * batch-research turns, and when the layer policy switched the layer off. Its spend is recorded through the same
373
+ * `recordSpend` path as the turn's own, so `/cost` and the spend cap include it.
374
+ */
375
+ private maybeStartMemoryReview;
376
+ /** Resolves when any in-flight background memory review has finished (a host about to exit, or a test). */
377
+ awaitMemoryReview(): Promise<void>;
299
378
  /** Persisted alongside transcript/facts/plan — survives a process restart, same as everything else keyed by sessionId, so the ceiling is genuinely cross-session, not just cross-turn within one process lifetime. */
300
379
  getSpendState(sessionId: string): Promise<SpendState>;
301
380
  /** The session's conversation transcript, oldest first — same array `turn()` reads/appends to. Used by `/export`. */
@@ -308,6 +387,45 @@ export declare class PersonalAssistant {
308
387
  recordDeclinedRequest(sessionId: string, userMessage: string, reason: string): Promise<void>;
309
388
  /** Ends the current conversation — see AssistantSession.clearSession's doc comment for exactly what is and isn't cleared. */
310
389
  clearSession(sessionId: string): Promise<void>;
390
+ /** M5: propose memory consolidation (applies nothing). `manual` forces the model pass past the cost gate. Usage is recorded against the spend state like any other call. */
391
+ proposeMemoryConsolidation(sessionId: string, manual?: boolean): Promise<Awaited<ReturnType<MemoryService['runConsolidation']>>>;
392
+ /** The consolidator MemoryService.consolidate() runs for `/memory consolidate` (chat-ui's button): runs the proposal pass now and reports; applying anything is still the user's accept. */
393
+ private runRegisteredConsolidation;
394
+ /** M5: staged proposals awaiting the user. */
395
+ memoryProposals(): Promise<ConsolidationProposal[]>;
396
+ /** M5: `/memory consolidate accept <n>` (1-based). */
397
+ acceptMemoryProposal(selector: string, sessionId: string): Promise<{
398
+ ok: boolean;
399
+ message: string;
400
+ }>;
401
+ /** M5: `/memory consolidate dismiss <n>` (1-based). */
402
+ dismissMemoryProposal(selector: string): Promise<{
403
+ ok: boolean;
404
+ message: string;
405
+ }>;
406
+ /** How many entries at the head of `listArchivedFacts()` were set aside by staged forgetting (M5) and can be restored; the rest were replaced by a newer statement. */
407
+ restorableArchiveCount(): Promise<number>;
408
+ /** `/memory archive restore <n>` (1-based, over the same numbered listing as `/memory archive`). */
409
+ restoreArchivedMemory(selector: string, sessionId: string): Promise<{
410
+ ok: boolean;
411
+ message: string;
412
+ }>;
413
+ /**
414
+ * M3 session edge: with AUDIT_EPISODIC_DIGEST on, writes the conversation's handoff digest (one bounded call, fail-open)
415
+ * for the current conversation (the rolling digest is refreshed, not duplicated, if the same conversation is digested again). `clearSession` (/new) calls it, then ends the conversation's digest identity; the CLI calls it on exit; never from inside a turn.
416
+ * With the flag off this does nothing, so the old behaviour is untouched.
417
+ */
418
+ endSession(sessionId: string): Promise<void>;
419
+ /** Stored episodic digests (newest first), for the memory panel and `/memory`; read-only. */
420
+ listSessionDigests(): Promise<SessionDigest[]>;
421
+ /** Which optional memory writers are switched on for this process (their `AUDIT_*` flags): hosts use it to show or hide the matching controls. A writer that is off makes no model call and no write. */
422
+ memoryWriterFlags(): {
423
+ reviewer: boolean;
424
+ consolidation: boolean;
425
+ digest: boolean;
426
+ };
427
+ /** M3 (D4): `/memory forget digest <id>` or all digests when `id` is omitted; returns how many were removed. */
428
+ forgetDigests(id?: string): Promise<number>;
311
429
  /**
312
430
  * P1 of the internal plan — explicit entry point into plan mode's
313
431
  * exclusive drafting state. No production caller yet: P3 will later set this automatically from
@@ -330,6 +448,19 @@ export declare class PersonalAssistant {
330
448
  * all). Returns `null` when no plan record exists for this session yet.
331
449
  */
332
450
  getPlanState(sessionId: string): Promise<PlanRecord | null>;
451
+ /**
452
+ * Phase 7 of plans/hierarchical_goal_tree_and_steering_plan.html (R5, "Visibility") — the shared
453
+ * read surface behind the CLI's `/goals` and chat-ui's GoalsPanel, analogous to `searchTranscript`
454
+ * above. A pure query over whatever Phases 4-5 already populate in this session's
455
+ * `GoalGraphRecord`; never mints or mutates one. Returns the empty state for a session with no
456
+ * goal graph yet, same "nothing to show" convention `getGoalGraphState` itself documents.
457
+ */
458
+ getGoalGraphState(sessionId: string): Promise<GoalGraphState>;
459
+ /**
460
+ * Read-only source for the plan graph view: the session's goal graph when it has threads, else its
461
+ * plan record, else `null`. Never throws and never creates a record.
462
+ */
463
+ getPlanGraph(sessionId: string): Promise<PlanRecord | GoalGraphRecord | null>;
333
464
  /**
334
465
  * P9 of the internal plan — the lightweight plan-sketch delegate: a
335
466
  * cheaper "just go research and draft an approach" path than plan mode's durable task-graph
@@ -388,9 +519,39 @@ export declare class PersonalAssistant {
388
519
  * facts first, then session facts — the same order `getMemorySummary()` returns). Unlike
389
520
  * confirm/reject, there's no category form: a durable/session fact carries no `category` field,
390
521
  * only pending model-inferred guesses do. Hard-deletes rather than routing through
391
- * REJECTED_FACTS_KEY — see MemoryService.forgetFact's doc comment.
522
+ * REJECTED_FACTS_KEY — see MemoryService.forgetFact's doc comment. By default the removal is recorded
523
+ * in the audit log with the fact's text (so `/memory undo` can restore it); `erase` also scrubs that text.
392
524
  */
393
- forgetFact(selector: string, sessionId: string): Promise<MemoryPendingOutcome>;
525
+ forgetFact(selector: string, sessionId: string, erase?: boolean): Promise<MemoryPendingOutcome>;
526
+ /** `/memory history` — the newest audit-log entries (M2). Empty unless `AUDIT_MEMORY_AUDIT_LOG` is on. */
527
+ memoryHistory(limit?: number): Promise<AuditEntry[]>;
528
+ /** `/memory undo <seq>` — restores the pre-image of one audit entry (M2). */
529
+ undoMemoryChange(selector: string, sessionId: string): Promise<{
530
+ ok: boolean;
531
+ message: string;
532
+ }>;
533
+ /** `/memory` governance mode (M6) — takes effect on the next write. */
534
+ getMemoryWriteMode(): MemoryWriteMode;
535
+ /** Live mode change (CLI `/config set` reloads the assistant; chat-ui calls this directly). Unknown values keep the default rather than widening write authority. */
536
+ setMemoryWriteMode(mode: MemoryWriteMode | string): void;
537
+ /** `/memory off` / `/memory on` — stop or resume all memory writes (facts, pending queue, digests, reviewer, consolidation) for this install. Existing data stays readable and removable. */
538
+ setMemoryEnabled(enabled: boolean): Promise<void>;
539
+ isMemoryEnabled(): Promise<boolean>;
540
+ /** Store size vs budget, pending/audit counts, last consolidation, governance state — `/doctor` and the memory panel. Read-only. */
541
+ getMemoryStatus(sessionId: string): Promise<MemoryStatus>;
542
+ /** The facts the last turn's prompt actually contained, plus how many were left out — the "Why?" panel / `/why` line. Undefined before the first turn. */
543
+ getLastMemoryInjection(): MemoryInjection | undefined;
544
+ /** `/memory archive` — entries set aside by staged forgetting (restorable, first) then entries a keyed update replaced (newest first). */
545
+ listArchivedFacts(): Promise<UserFact[]>;
546
+ /** `/memory archive forget <n>` — permanently erases one archived entry and its history pre-images. */
547
+ forgetArchivedFact(selector: string): Promise<MemoryPendingOutcome>;
548
+ /** `/memory consolidate` — runs the registered consolidator (M5). Until M5 registers one this reports `unavailable`. */
549
+ consolidateMemory(sessionId: string): Promise<ConsolidationOutcome | {
550
+ status: 'unavailable' | 'blocked';
551
+ message: string;
552
+ }>;
553
+ /** M5 hook: lets the consolidation phase plug in without touching the surfaces. */
554
+ registerMemoryConsolidator(fn: Parameters<MemoryService['registerConsolidator']>[0]): void;
394
555
  /** Ranked search over the per-message index — see AssistantSession.searchTranscript's doc comment. Used by `/search`. */
395
556
  searchTranscript(query: string, topK?: number): Promise<TranscriptSearchHit[]>;
396
557
  /** Changes the model used by every subsequent `turn()` call, mid-session — no reconstruction needed. Used by `/model`. Every collaborator constructed above reads this field through a getter closure, never a captured string, so this takes effect for all of them immediately. */
@@ -21,6 +21,12 @@ export interface InjectedFailureOptions {
21
21
  * the internal plan F2).
22
22
  */
23
23
  onInjected?: () => void;
24
+ /**
25
+ * The failure symptom text (a paraphrase of a known class, a missing resource, a rate limit…).
26
+ * Becomes the seeded records' description, the observation and the failed execution's error, so
27
+ * failure matching sees the task's own wording. Absent = the generic ETIMEDOUT text.
28
+ */
29
+ symptom?: string;
24
30
  }
25
31
  type Proposer = (toolCtx: ToolExecutorContext) => unknown | Promise<unknown>;
26
32
  export declare function wrapProposerWithInjectedFailure(realProposer: Proposer, opts: InjectedFailureOptions): (toolCtx: ToolExecutorContext) => Promise<unknown>;