@buildaharness/aielia 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/README.md +832 -0
  2. package/dist/action-approval-service.d.ts +52 -0
  3. package/dist/action-snapshot.d.ts +173 -0
  4. package/dist/action-tools.d.ts +38 -0
  5. package/dist/agent-loop.d.ts +372 -0
  6. package/dist/answer-claim.d.ts +51 -0
  7. package/dist/ask-clarification-service.d.ts +70 -0
  8. package/dist/ask-mode-flag.d.ts +25 -0
  9. package/dist/ask-response-format.d.ts +8 -0
  10. package/dist/assistant-session.d.ts +267 -0
  11. package/dist/assistant-source.d.ts +14 -0
  12. package/dist/assistant-types.d.ts +158 -0
  13. package/dist/assistant.d.ts +419 -0
  14. package/dist/batch-list-detector.d.ts +15 -0
  15. package/dist/batch-research-fixtures.d.ts +50 -0
  16. package/dist/benchmark-injected-failure.d.ts +27 -0
  17. package/dist/claude-cli-llm-client.d.ts +139 -0
  18. package/dist/claude-cli-prompt.d.ts +86 -0
  19. package/dist/cli-config.d.ts +27 -0
  20. package/dist/cli-session-D0uLdT2A.js +7863 -0
  21. package/dist/cli-session-D0uLdT2A.js.map +1 -0
  22. package/dist/cli-session.d.ts +63 -0
  23. package/dist/cli.d.ts +65 -0
  24. package/dist/cli.js +1693 -0
  25. package/dist/cli.js.map +1 -0
  26. package/dist/config.d.ts +175 -0
  27. package/dist/contradiction-checker.d.ts +27 -0
  28. package/dist/debug-log.d.ts +16 -0
  29. package/dist/decomposition-classifier.d.ts +38 -0
  30. package/dist/doctor-checks.d.ts +8 -0
  31. package/dist/email-smtp.d.ts +19 -0
  32. package/dist/email.d.ts +60 -0
  33. package/dist/error-classifier.d.ts +13 -0
  34. package/dist/execution-mode.d.ts +85 -0
  35. package/dist/fact-extraction.d.ts +143 -0
  36. package/dist/failure-mode-matcher.d.ts +32 -0
  37. package/dist/file-tools-mcp-server.mjs +1027 -0
  38. package/dist/file-tools.d.ts +179 -0
  39. package/dist/first-run.d.ts +22 -0
  40. package/dist/harness-bridge.d.ts +103 -0
  41. package/dist/index.d.ts +58 -0
  42. package/dist/index.js +212 -0
  43. package/dist/index.js.map +1 -0
  44. package/dist/ink-test-render.d.ts +24 -0
  45. package/dist/lexical/patterns/batch-list-markers.json +8 -0
  46. package/dist/lexical/patterns/batch-list-markers.json.d.ts +11 -0
  47. package/dist/lexical/patterns/coding-fact-markers.json +8 -0
  48. package/dist/lexical/patterns/coding-fact-markers.json.d.ts +11 -0
  49. package/dist/lexical/patterns/enumeration-markers.json +20 -0
  50. package/dist/lexical/patterns/enumeration-markers.json.d.ts +23 -0
  51. package/dist/lexical/patterns/fact-markers.json +16 -0
  52. package/dist/lexical/patterns/fact-markers.json.d.ts +19 -0
  53. package/dist/lexical/patterns/injection-patterns.json +20 -0
  54. package/dist/lexical/patterns/injection-patterns.json.d.ts +23 -0
  55. package/dist/lexical/patterns/plan-mode-markers.json +10 -0
  56. package/dist/lexical/patterns/plan-mode-markers.json.d.ts +13 -0
  57. package/dist/lexical/patterns/risk-patterns.json +122 -0
  58. package/dist/lexical/patterns/risk-patterns.json.d.ts +125 -0
  59. package/dist/lexical/patterns/task-cancel-markers.json +22 -0
  60. package/dist/lexical/patterns/task-cancel-markers.json.d.ts +25 -0
  61. package/dist/lexical/patterns/template-keywords.json +20 -0
  62. package/dist/lexical/patterns/template-keywords.json.d.ts +23 -0
  63. package/dist/lexical/patterns/tool-yield-markers.json +22 -0
  64. package/dist/lexical/patterns/tool-yield-markers.json.d.ts +25 -0
  65. package/dist/lexical/patterns.d.ts +64 -0
  66. package/dist/memory-service.d.ts +219 -0
  67. package/dist/model-pricing.d.ts +19 -0
  68. package/dist/network-containment.d.ts +21 -0
  69. package/dist/node-config-store.d.ts +29 -0
  70. package/dist/node-display-names.d.ts +26 -0
  71. package/dist/node-fs-backend.d.ts +8 -0
  72. package/dist/non-interactive-mode.d.ts +22 -0
  73. package/dist/one-loop-flag.d.ts +38 -0
  74. package/dist/plan-approval-service.d.ts +62 -0
  75. package/dist/plan-builder.d.ts +32 -0
  76. package/dist/plan-drafting-service.d.ts +87 -0
  77. package/dist/plan-drafting.d.ts +58 -0
  78. package/dist/plan-mode-flag.d.ts +34 -0
  79. package/dist/plan-service.d.ts +61 -0
  80. package/dist/plan-sketch-service.d.ts +24 -0
  81. package/dist/plan-sketch.d.ts +20 -0
  82. package/dist/plan-store.d.ts +232 -0
  83. package/dist/plan-templates/data/content_creation.json.d.ts +75 -0
  84. package/dist/plan-templates/data/decision_making.json.d.ts +66 -0
  85. package/dist/plan-templates/data/problem_solving.json.d.ts +75 -0
  86. package/dist/plan-templates/data/process_improvement.json.d.ts +57 -0
  87. package/dist/plan-templates/data/project_planning.json.d.ts +66 -0
  88. package/dist/plan-templates/data/research_analysis.json.d.ts +66 -0
  89. package/dist/plan-templates/data/trip_planning.json.d.ts +66 -0
  90. package/dist/plan-templates/index.d.ts +33 -0
  91. package/dist/plan-verification.d.ts +31 -0
  92. package/dist/reminder-tools.d.ts +13 -0
  93. package/dist/response-service.d.ts +73 -0
  94. package/dist/review-checker.d.ts +22 -0
  95. package/dist/risk-classifier.d.ts +7 -0
  96. package/dist/scripted-llm-client.d.ts +37 -0
  97. package/dist/semantic-criterion-coverage.d.ts +30 -0
  98. package/dist/shell-executor.d.ts +14 -0
  99. package/dist/shell-tools.d.ts +84 -0
  100. package/dist/spend-cap.d.ts +41 -0
  101. package/dist/spike.d.ts +1 -0
  102. package/dist/spike.js +18 -0
  103. package/dist/spike.js.map +1 -0
  104. package/dist/supervisor-decider.d.ts +3 -0
  105. package/dist/system-prompt.d.ts +8 -0
  106. package/dist/task-mapping.d.ts +42 -0
  107. package/dist/tool-control-plane.d.ts +67 -0
  108. package/dist/tool-policy.d.ts +45 -0
  109. package/dist/tool-step.d.ts +23 -0
  110. package/dist/tool-yield-classifier.d.ts +7 -0
  111. package/dist/trace-events.d.ts +126 -0
  112. package/dist/transcript-compaction.d.ts +13 -0
  113. package/dist/trust-tagging.d.ts +38 -0
  114. package/dist/tui-app-DxLPSGh2.js +481 -0
  115. package/dist/tui-app-DxLPSGh2.js.map +1 -0
  116. package/dist/tui-app.d.ts +125 -0
  117. package/dist/tui-input.d.ts +51 -0
  118. package/dist/tui-mode-flag.d.ts +36 -0
  119. package/dist/tui-output-capture.d.ts +30 -0
  120. package/dist/tui-spike.d.ts +1 -0
  121. package/dist/turn-intent-classifier.d.ts +96 -0
  122. package/dist/turn-interpreter.d.ts +71 -0
  123. package/dist/turn-policy.d.ts +45 -0
  124. package/dist/web-fetch-core.d.ts +65 -0
  125. package/dist/web-search-provider.d.ts +12 -0
  126. package/dist/web-tools.d.ts +26 -0
  127. package/package.json +69 -0
@@ -0,0 +1,419 @@
1
+ import { ExperienceStore, CheckpointStore, AskResponse } from '@buildaharness/harness';
2
+ import { MemoryAdapter, ILLMClient, ChatMessage, ReminderStore } from '@buildaharness/runtime';
3
+ import { FileToolsContext } from './file-tools.js';
4
+ import { UndoLogEntry } from './action-snapshot.js';
5
+ import { WebToolsContext } from './web-tools.js';
6
+ import { ShellToolsContext } from './shell-tools.js';
7
+ import { ActionToolsContext } from './action-tools.js';
8
+ import { SpendCapConfig, SpendState } from './spend-cap.js';
9
+ import { TraceEvent } from './trace-events.js';
10
+ import { AssistantToolStep } from './tool-step.js';
11
+ import { MemorySummary, MemoryExport } from './memory-service.js';
12
+ import { UserFact } from './fact-extraction.js';
13
+ import { TranscriptSearchHit } from './assistant-session.js';
14
+ import { PlanDecision, PlanApprovalEdits } from './plan-approval-service.js';
15
+ import { PlanRecord } from './plan-store.js';
16
+ import { OneLoopMode } from './one-loop-flag.js';
17
+ import { AskMode } from './ask-mode-flag.js';
18
+ import { PlanRolloutMode } from './plan-mode-flag.js';
19
+ import { DebugLogEntry } from './debug-log.js';
20
+ import { AssistantTurnResult, AssistantProgress } from './assistant-types.js';
21
+ export type { MemorySummary, MemoryExport, PendingFact } from './memory-service.js';
22
+ export type { FactCategory } from './turn-intent-classifier.js';
23
+ export type { IndexedMessage, TranscriptSearchHit } from './assistant-session.js';
24
+ export type { BatchBudgetState } from './agent-loop.js';
25
+ export { trimmedAverage, nextItemBudget } from './agent-loop.js';
26
+ export type { AssistantSource } from './assistant-source.js';
27
+ export type { DebugLogEntry } from './debug-log.js';
28
+ export type { AssistantTrace, AssistantTurnResult, AssistantProgress, ProposerKind } from './assistant-types.js';
29
+ export type { PlanDecision, PlanApprovalEdits } from './plan-approval-service.js';
30
+ export type { PlanMode } from './plan-store.js';
31
+ /** Result of `/memory confirm`/`/memory reject` (single index or bulk category) — see PersonalAssistant.confirmPendingFact/rejectPendingFact. */
32
+ export type MemoryPendingOutcome = {
33
+ ok: true;
34
+ facts: UserFact[];
35
+ conflictNotices: string[];
36
+ } | {
37
+ ok: false;
38
+ error: string;
39
+ };
40
+ export interface TurnOptions {
41
+ sessionId?: string;
42
+ approved?: boolean;
43
+ pendingActionId?: string;
44
+ /**
45
+ * Q2 of plans/ask_question_and_plan_mode_plan.html — resolves a staged `needs_clarification`
46
+ * result (see AssistantTurnResult.pendingClarificationId) with the user's answer. Mirrors
47
+ * `pendingActionId`/`approved`'s own "resolved by ID, never re-derived" shape:
48
+ * `clarificationAnswer` must answer exactly the questions that were staged, validated
49
+ * server-side (INV-28) regardless of what the client already checked.
50
+ */
51
+ pendingClarificationId?: string;
52
+ clarificationAnswer?: AskResponse;
53
+ /**
54
+ * P2 of plans/ask_question_and_plan_mode_plan.html — resolves a staged `needs_plan_approval`
55
+ * result (see AssistantTurnResult.planApprovalId) with the user's decision. Mirrors
56
+ * `pendingActionId`/`approved`'s "resolved by ID, never re-derived" shape: what gets
57
+ * approved/declined is exactly the plan that was staged, never re-drafted.
58
+ */
59
+ planApprovalId?: string;
60
+ /** `'approve'`/`'approve_with_edits'`/`'decline'` — required to resolve `planApprovalId`; omitting it leaves the plan `awaiting_approval` (fail closed), same as an unanswered clarification. */
61
+ planDecision?: PlanDecision;
62
+ /** Only meaningful with `planDecision: 'approve_with_edits'` — cancels/edits applied before the plan is activated. */
63
+ planEdits?: PlanApprovalEdits;
64
+ /**
65
+ * Per-session override — tier 2 of Q1's three-tier INV-29 resolution. `'disabled'` forces
66
+ * plain free-text-fallback escalations for this turn even when the global `askMode` config is
67
+ * `'enabled'`. Absent/`'enabled'` defers to the global flag. Never widens the global flag: an
68
+ * override can only turn structured questions off relative to it, never on when the global
69
+ * flag is off.
70
+ */
71
+ askMode?: AskMode;
72
+ onProgress?: (progress: AssistantProgress) => void;
73
+ /**
74
+ * Called with each token as the model's reply streams in. On the plain chat
75
+ * path (no tool loop active) this is the turn's one real LLM call, read via
76
+ * callChat. On the tool-loop path (fileTools/webTools configured), every
77
+ * tool-bearing round trip stays non-streaming — callChatStructured isn't a
78
+ * streaming call for either backend — but once the model stops calling tools,
79
+ * one extra callChat request re-asks for that same final answer as a real
80
+ * streamed completion, *only when `onToken` is supplied* (so a caller who
81
+ * doesn't listen never pays for it). ClaudeCliLLMClient's callChat isn't real
82
+ * per-token streaming either way (it yields the whole reply as one chunk) —
83
+ * this only reads token-by-token on the proxy backend.
84
+ */
85
+ onToken?: (token: string) => void;
86
+ /**
87
+ * Called once per tool call as it happens, with a human-readable summary — the "what
88
+ * step is the assistant on right now" signal, distinct from onTrace's name/status-only
89
+ * telemetry. Fires for every backend: the proxy backend's tool loop reports each call it
90
+ * dispatches directly; the claude-cli backend reports calls its own agentic loop makes
91
+ * autonomously inside a single subprocess call, via ChatOptions.onToolStep (see
92
+ * ClaudeCliLLMClient).
93
+ */
94
+ onToolStep?: (step: AssistantToolStep) => void;
95
+ /**
96
+ * **Eval harness only** (`packages/aielia/eval/`). Wraps the one-loop
97
+ * proposer so its first `failIterations` harness iterations report a failed execution
98
+ * and `seedFailures` recurring same-class records are seeded into the run's
99
+ * `failureDiagnostics` — enough to trip `cannotMakeProgress()` on iteration 1 and
100
+ * exercise the Trajectory Supervisor's stall edge inside a single benchmark turn. Never
101
+ * set by a real caller; ignored unless the one-loop proposer path is active.
102
+ */
103
+ __benchmarkInjectedFailure?: {
104
+ failIterations: number;
105
+ seedFailures: number;
106
+ onInjected?: () => void;
107
+ };
108
+ }
109
+ export interface PersonalAssistantOptions {
110
+ llmClient: ILLMClient;
111
+ model?: string;
112
+ /**
113
+ * The already-resolved project label a project-scoped fact gets tagged with and filtered
114
+ * against — see UserFact.project's doc comment. Callers resolve `config.activeProject ??
115
+ * workspaceRoot` themselves (cli.ts's buildAssistant) before passing it in; PersonalAssistant
116
+ * doesn't know about workspaceRoot itself (it's a per-tool option, not assistant-wide — see
117
+ * fileTools/shellTools/actionTools). Undefined/empty leaves every fact unscoped, matching a
118
+ * fresh install with no workspace concept at all.
119
+ */
120
+ activeProject?: string;
121
+ /** Conversation transcript storage — defaults to an in-process Map, swap for IndexedDBAdapter in the browser. */
122
+ memory?: MemoryAdapter;
123
+ /** Learning-layer store — persist and pass the same instance back in across sessions to retain strategy weights. */
124
+ experienceStore?: ExperienceStore;
125
+ /** Stores an in-flight harness run's checkpoint so a crash/reload mid-turn can resume instead of losing the turn. */
126
+ checkpointStore?: CheckpointStore;
127
+ /**
128
+ * Caps both the harness's plan-driven main loop (auto-raised there via
129
+ * `Math.max(maxSteps, initialTasks.length)` so a decomposed plan is never starved) and the
130
+ * ReAct-style tool loop's round-trips (AgentLoop.runToolLoop) — one shared per-turn step
131
+ * budget rather than two independently-tuned constants. Defaults to 15: high enough that a
132
+ * legitimate multi-query research task (e.g. "find primary schools near me", which can
133
+ * easily take 5+ real search round-trips) doesn't get cut off mid-work, while still bounding
134
+ * a stuck/looping model.
135
+ */
136
+ maxSteps?: number;
137
+ /**
138
+ * When set, `turn()` gives the model real read_file/list_directory/write_file
139
+ * tools scoped to `workspaceRoot` instead of a single plain chat call. Absent
140
+ * by default — behavior is byte-for-byte unchanged from before this option existed.
141
+ * `write_file` never executes inline; it always stages a proposal and the turn
142
+ * returns `needs_approval` with a `pendingWriteId`.
143
+ */
144
+ fileTools?: FileToolsContext;
145
+ /**
146
+ * When set, `turn()` also gives the model web_search/fetch_url tools, grounding replies in external content.
147
+ * Absent by default — behavior is unchanged when unset. Results from these two tools are wrapped in
148
+ * `<untrusted_external_content>` before they reach the model (see trust-tagging.ts) — unlike file tools,
149
+ * this is content the assistant does not vouch for.
150
+ */
151
+ webTools?: WebToolsContext;
152
+ /**
153
+ * When set, `turn()` gives the model a real run_shell_command tool scoped to `workspaceRoot`.
154
+ * Every call is gated on approval, full stop — there is no "safe subset" the way `read_file` is
155
+ * safe within `write_file`'s tool group (a shell command has no structural split between "reads"
156
+ * and "mutates"). Once approved, the command's stdout+stderr is wrapped in
157
+ * `<untrusted_external_content>` (same trust boundary as web_search/fetch_url — see
158
+ * trust-tagging.ts) before it's saved into the transcript, since it can carry the same kind of
159
+ * injection-shaped content a fetched web page can. Independent of `fileTools`/`webTools` so a
160
+ * caller can enable file/web access without ever exposing shell.
161
+ */
162
+ shellTools?: ShellToolsContext;
163
+ /**
164
+ * When set, `turn()` gives the model real "effect" tools — today `send_email` — scoped to
165
+ * `workspaceRoot` for staging. Every call is approval-gated exactly like `write_file` /
166
+ * `run_shell_command`: the model can only propose a message, never deliver one. On approval,
167
+ * `applyPendingAction` calls `sendEmail` (email.ts's Resend transport / email-smtp.ts's SMTP
168
+ * transport — wired in by a Node caller, never imported here). Independent of
169
+ * `fileTools`/`webTools`/`shellTools`. Absent by default — no `send_email` tool exists unless set.
170
+ */
171
+ actionTools?: ActionToolsContext;
172
+ /** Stores reminders detected from "remind me"/"set a reminder"-shaped requests — defaults to an in-process store. See ReminderStore's `dueAt` doc: v1 stores raw text only, no time parsing, so `listDue()` won't return these yet. */
173
+ reminderStore?: ReminderStore;
174
+ /** Structured turn telemetry — turn/risk/triviality/harness-node/tool-call/escalation/error events. Purely additive instrumentation; no behavior change when unset. */
175
+ onTrace?: (event: TraceEvent) => void;
176
+ /**
177
+ * Full message/tool content for live debugging — deliberately separate from `onTrace`
178
+ * (which is name/status-only by design, safe to hand to an arbitrary sink; see its own doc
179
+ * comment) since this one carries the actual conversation. Off by default: nothing is
180
+ * logged anywhere unless a caller wires this in. See DebugLogEntry.
181
+ */
182
+ onDebugLog?: (entry: DebugLogEntry) => void;
183
+ /**
184
+ * Equivalent of Claude Code's own --dangerously-skip-permissions (see AssistantConfig's doc
185
+ * comment in config.ts). When true, both the message-level risk gate and write_file/
186
+ * run_shell_command's per-call staging resolve as if the user had already said yes, instead
187
+ * of returning `needs_approval` — turn() auto-applies a staged action the same way a second
188
+ * turn() call with `approved: true` would. The underlying sandboxing (path validation, SSRF
189
+ * guard, shell env allowlist, output truncation, timeout) is never skipped — only the ask.
190
+ * Off by default.
191
+ */
192
+ dangerouslySkipPermissions?: boolean;
193
+ /**
194
+ * Opt-in session spend/turn-count ceilings — see spend-cap.ts. Undefined by default, same as
195
+ * every other field here that changes behavior only when a caller sets it: no ceiling means
196
+ * exactly today's unbounded behavior. Checked once per turn, before any LLM call that turn
197
+ * would make (see turn()) — never mid-turn.
198
+ */
199
+ spendCap?: SpendCapConfig;
200
+ /**
201
+ * R2 of plans/harness_d2_one_loop_rewire_plan.html — see one-loop-flag.ts's doc comment for the
202
+ * full rollout rationale. 'disabled' (the default, for the whole R2-R4 rollout window): today's
203
+ * behavior, byte-for-byte — HarnessBridge.run() always uses `() => draftReply` as its toolFn,
204
+ * regardless of whether a caller happens to supply a proposer. 'enabled' lets a supplied
205
+ * proposer (see HarnessRunParams.oneLoopProposer) actually be used. PersonalAssistant itself
206
+ * never touches process.env — only cli.ts (or an equivalent surface entry point) is expected to
207
+ * call resolveOneLoopMode(process.env) and pass the result here.
208
+ */
209
+ oneLoopMode?: OneLoopMode;
210
+ /**
211
+ * Q2 of plans/ask_question_and_plan_mode_plan.html — global-flag control point (tier 1 of
212
+ * INV-29) for the ask-question mechanism. Undefined (the default) falls back to
213
+ * `DEFAULT_ASK_MODE` ('disabled') — today's behavior, byte-for-byte: every escalation stays on
214
+ * the plain `escalated`/`reply: null` path. Only cli.ts/chat-ui's App.tsx (or an equivalent
215
+ * surface entry point) is expected to resolve this from ASSISTANT_ASK_MODE/
216
+ * VITE_ASSISTANT_ASK_MODE and pass it down — PersonalAssistant itself never touches
217
+ * process.env, mirroring oneLoopMode's own convention.
218
+ */
219
+ askMode?: AskMode;
220
+ /**
221
+ * P11 of plans/ask_question_and_plan_mode_plan.html — see plan-mode-flag.ts's doc comment for
222
+ * the full rollout rationale. 'legacy' (the default, for the whole rollout window): P3's
223
+ * judgment-based auto-trigger below never fires, so `turn()` never auto-enters plan mode's
224
+ * exclusive drafting+approval loop — today's behavior, byte-for-byte, for every real user turn.
225
+ * 'gated' lets that auto-trigger run. PersonalAssistant itself never touches process.env — only
226
+ * cli.ts/chat-ui's App.tsx (or an equivalent surface entry point) is expected to call
227
+ * resolvePlanMode(process.env)/normalizePlanMode(import.meta.env...) and pass the result here.
228
+ */
229
+ planMode?: PlanRolloutMode;
230
+ }
231
+ /**
232
+ * A light, everyday-use wrapper around HarnessRuntime: one harness run per chat turn,
233
+ * one real LLM call per turn (skipped entirely while a HIGH-risk action awaits approval).
234
+ * Conversation history lives beside the harness run (in `memory`), not inside it — each
235
+ * turn's WorldModel/TaskGraph/etc. are scratch state for that single turn, same as the
236
+ * rest of the 11-layer runtime; only the transcript, the ExperienceStore, and (while a
237
+ * turn is actually in flight) a HarnessRunState checkpoint persist.
238
+ *
239
+ * Phase 4d of the architecture remediation plan split this class's internals into 8
240
+ * single-purpose collaborators (MemoryService, AssistantSession, AgentLoop,
241
+ * ActionApprovalService, PlanService, TurnInterpreter, HarnessBridge, ResponseService), each
242
+ * constructed once here and wired together. This class itself is now a thin facade + sequencer:
243
+ * its public constructor signature and every public method below are unchanged, so every
244
+ * existing caller (cli.ts, chat-ui's App.tsx, assistant.test.ts's ~89 `new PersonalAssistant(...)`
245
+ * call sites) keeps working with zero call-site changes.
246
+ */
247
+ export declare class PersonalAssistant {
248
+ private readonly llmClient;
249
+ private model?;
250
+ private activeProject?;
251
+ private readonly memory;
252
+ private readonly webTools?;
253
+ private readonly onTrace?;
254
+ private readonly onDebugLog?;
255
+ private readonly dangerouslySkipPermissions;
256
+ /** Whenever any of fileTools/webTools/shellTools is configured, `turn()` routes through AgentLoop instead of a single plain chat call — computed once here since it never changes for the lifetime of an instance. */
257
+ private readonly toolLoopWillRun;
258
+ /** R3 of plans/harness_d2_one_loop_rewire_plan.html — 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
+ private readonly oneLoopMode;
260
+ /** Q2 — global-flag tier of the ask-question mechanism's three-tier INV-29 resolution. See PersonalAssistantOptions.askMode's doc comment. */
261
+ private readonly askMode;
262
+ /** P11 — gates whether P3's auto-trigger below can ever enter plan mode for real traffic. See PersonalAssistantOptions.planMode's doc comment. */
263
+ private readonly planMode;
264
+ /**
265
+ * Scratch slot for which proposer drove the most recent runTurn — read by `turn()` to stamp
266
+ * AssistantTurnResult.proposerKind, and emitted as a 'proposer_selected' trace event from
267
+ * runTurn itself. A single mutable field is safe because `turn()` is awaited end to end (turns
268
+ * never overlap within one instance). See AssistantTurnResult.proposerKind and
269
+ * plans/chat_ui_browser_e2e_plan.html phase B1.
270
+ */
271
+ private lastProposerKind;
272
+ private readonly memoryService;
273
+ private readonly session;
274
+ private readonly agentLoop;
275
+ private readonly actionApproval;
276
+ private readonly planService;
277
+ private readonly planApproval;
278
+ private readonly planDrafting;
279
+ private readonly planSketch;
280
+ private readonly turnInterpreter;
281
+ private readonly harnessBridge;
282
+ private readonly responseService;
283
+ private readonly askClarification;
284
+ constructor(options: PersonalAssistantOptions);
285
+ /**
286
+ * Preferred entry point in a browser: defaults transcript, learning, and
287
+ * checkpoint storage to their IndexedDB/Dexie-backed implementations so all
288
+ * three survive a page reload, instead of the in-process defaults the plain
289
+ * constructor uses. Falls back to the same in-memory defaults as `new
290
+ * PersonalAssistant(...)` outside a browser (e.g. the CLI).
291
+ */
292
+ static create(options: PersonalAssistantOptions): Promise<PersonalAssistant>;
293
+ /**
294
+ * Thin wrapper around runTurn(): emits turn_start/turn_end/error trace events
295
+ * around the actual logic, so every one of runTurn's return paths gets a
296
+ * matching turn_end without instrumenting each one individually.
297
+ */
298
+ turn(userMessage: string, options?: TurnOptions): Promise<AssistantTurnResult>;
299
+ /** 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
+ getSpendState(sessionId: string): Promise<SpendState>;
301
+ /** The session's conversation transcript, oldest first — same array `turn()` reads/appends to. Used by `/export`. */
302
+ getTranscript(sessionId: string): Promise<ChatMessage[]>;
303
+ /**
304
+ * Records a message-level risk-gate decline as a resolved, paired exchange, once the caller
305
+ * (cli.ts) knows the final answer was "no". See AssistantSession.recordDeclinedRequest's doc
306
+ * comment for the full reasoning.
307
+ */
308
+ recordDeclinedRequest(sessionId: string, userMessage: string, reason: string): Promise<void>;
309
+ /** Ends the current conversation — see AssistantSession.clearSession's doc comment for exactly what is and isn't cleared. */
310
+ clearSession(sessionId: string): Promise<void>;
311
+ /**
312
+ * P1 of plans/ask_question_and_plan_mode_plan.html — explicit entry point into plan mode's
313
+ * exclusive drafting state. No production caller yet: P3 will later set this automatically from
314
+ * a judgment-based trigger ahead of TurnInterpreter's classification. Until then this is reached
315
+ * only by a caller (a future `/plan` CLI command, or a test) that wants to exercise the drafting
316
+ * loop directly. Once active, every `turn()` call for this session routes to
317
+ * `PlanDraftingService` instead of the normal pipeline until the user says an explicit
318
+ * cancel/abort phrase.
319
+ */
320
+ enterPlanMode(sessionId: string): Promise<{
321
+ active: boolean;
322
+ draftId: string;
323
+ }>;
324
+ /**
325
+ * P7 of plans/ask_question_and_plan_mode_plan.html — the raw current `PlanRecord` for this
326
+ * session, regardless of `mode`, so a caller (chat-ui's persistent banner, the CLI's `/plan
327
+ * show`) can display live drafting/awaiting-approval state independent of any single turn's
328
+ * own result (a `needs_plan_approval` result only carries a snapshot at the moment it's
329
+ * staged; a plain `status: 'ok'` drafting reply doesn't carry `rationale`/`reviewNotes` at
330
+ * all). Returns `null` when no plan record exists for this session yet.
331
+ */
332
+ getPlanState(sessionId: string): Promise<PlanRecord | null>;
333
+ /**
334
+ * P9 of plans/ask_question_and_plan_mode_plan.html — the lightweight plan-sketch delegate: a
335
+ * cheaper "just go research and draft an approach" path than plan mode's durable task-graph
336
+ * machinery (P0-P8), explicitly invoked (a CLI `/plan sketch <request>` command, a chat-ui
337
+ * "Sketch a plan" action) rather than auto-triggered. Returns advice in the reply text — it
338
+ * never creates a `PlanRecord`, never sets `planMode.active` (`enterPlanMode` above), never
339
+ * stages anything for `PlanApprovalService`, and cannot execute a single task (INV-35). A real,
340
+ * cost-incurring LLM call, so it still goes through the same session spend-cap check/record as
341
+ * an ordinary `turn()` — just none of `turn()`'s classification/tool-loop/plan-mode machinery.
342
+ */
343
+ sketchPlan(sessionId: string, request: string): Promise<AssistantTurnResult>;
344
+ /** Scoped recovery for a stuck harness checkpoint — see AssistantSession.clearCheckpoint's doc comment. */
345
+ clearCheckpoint(sessionId: string): Promise<{
346
+ cleared: boolean;
347
+ stepsUsed?: number;
348
+ currentNode?: string;
349
+ }>;
350
+ /** Read-only counterpart to clearCheckpoint — see AssistantSession.getCheckpointStatus's doc comment. */
351
+ getCheckpointStatus(sessionId: string): Promise<{
352
+ present: boolean;
353
+ stepsUsed?: number;
354
+ currentNode?: string;
355
+ failedResumeAttempts: number;
356
+ }>;
357
+ /** Removes the most recent exchange from conversation history — see AssistantSession.undoLastTurn's doc comment. */
358
+ undoLastTurn(sessionId: string): Promise<{
359
+ undone: boolean;
360
+ }>;
361
+ /** Real filesystem effects still on record as revertible, newest first — see AssistantSession.listUndoLogEntries's doc comment. */
362
+ listUndoLogEntries(): Promise<UndoLogEntry[]>;
363
+ /** Stages a revert of undo-log entry `id` as its own approval-gated pending action — see AssistantSession.stageUndoAction's doc comment. */
364
+ stageUndoAction(id: string): Promise<{
365
+ status: 'staged';
366
+ pendingActionId: string;
367
+ reason: string;
368
+ } | {
369
+ status: 'error';
370
+ message: string;
371
+ }>;
372
+ /** Read-only snapshot of what this session/assistant has learned — see MemoryService.getMemorySummary's doc comment. Used by `/memory`. */
373
+ getMemorySummary(sessionId: string): Promise<MemorySummary>;
374
+ /** Full, unbounded snapshot of everything learned so far — see MemoryService.exportMemory's doc comment. Used by `/memory export`. */
375
+ exportMemory(sessionId: string): Promise<MemoryExport>;
376
+ /**
377
+ * `/memory confirm <n|category>` — `selector` is either a 1-based index into `/memory`'s
378
+ * flat, display-order "Pending confirmation" listing, or one of FactCategory's names for a
379
+ * bulk confirm. Phase 3 of plans/personal_assistant_fact_extraction_llm_confidence_plan.html.
380
+ * `conflictNotices` (if any) are advisory only — every confirmed fact is promoted regardless,
381
+ * matching how every other contradiction check in this codebase never gates belief admission.
382
+ */
383
+ confirmPendingFact(selector: string): Promise<MemoryPendingOutcome>;
384
+ /** `/memory reject <n|category>` — mirror of confirmPendingFact, see its doc comment for `selector`'s shape. */
385
+ rejectPendingFact(selector: string): Promise<MemoryPendingOutcome>;
386
+ /**
387
+ * `/memory forget <n>` — `n` is a 1-based index into `/memory`'s "Facts I know" listing (durable
388
+ * facts first, then session facts — the same order `getMemorySummary()` returns). Unlike
389
+ * confirm/reject, there's no category form: a durable/session fact carries no `category` field,
390
+ * only pending model-inferred guesses do. Hard-deletes rather than routing through
391
+ * REJECTED_FACTS_KEY — see MemoryService.forgetFact's doc comment.
392
+ */
393
+ forgetFact(selector: string, sessionId: string): Promise<MemoryPendingOutcome>;
394
+ /** Ranked search over the per-message index — see AssistantSession.searchTranscript's doc comment. Used by `/search`. */
395
+ searchTranscript(query: string, topK?: number): Promise<TranscriptSearchHit[]>;
396
+ /** 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. */
397
+ setModel(model: string | undefined): void;
398
+ /** The project label new project-scoped facts are tagged with and existing ones are filtered against this session — see UserFact.project's doc comment. Empty string when none is set (cli.ts's buildAssistant always resolves one from workspaceRoot, but a bare `new PersonalAssistant()` with no `activeProject` option should read as "no project concept" rather than `undefined`). */
399
+ getActiveProject(): string;
400
+ /** Mid-session override for `activeProject`, mirroring setModel — takes effect on the very next turn via the same getter-closure MemoryService already reads through. Used by `/project <name>` outside the CLI's own /config-set-and-reload path (e.g. a future non-CLI embedder). */
401
+ setActiveProject(project: string | undefined): void;
402
+ /**
403
+ * The sequencer: constructs no state of its own beyond what a single turn needs
404
+ * (transcript/facts/system prompt, the turn-scoped usage accumulator), and otherwise just
405
+ * calls each collaborator in the same order the pre-split code ran their logic inline, wiring
406
+ * each one's output into the next. See turn-interpreter.ts/agent-loop.ts/harness-bridge.ts/
407
+ * response-service.ts for where the real control-flow subtlety (the batch-research path, the
408
+ * plan-cancel bypass, the triviality fast path) actually lives now.
409
+ */
410
+ private runTurn;
411
+ /**
412
+ * Shared by the flag-OFF flat/batch tool loop's own needs_approval/escalated ToolLoopResult and
413
+ * the flag-ON harness-driven proposer's equivalent OneLoopPause (R3 of
414
+ * plans/harness_d2_one_loop_rewire_plan.html, see runTurn's two call sites) — "the model wants
415
+ * to write/run/send something" or "the tool loop gave up" means the same thing to the caller
416
+ * regardless of which loop discovered it.
417
+ */
418
+ private buildToolLoopPauseResult;
419
+ }
@@ -0,0 +1,15 @@
1
+ export interface BatchListDetection {
2
+ /** Exact strings as they appeared in the list (marker/bullet stripped), in message order. */
3
+ items: string[];
4
+ }
5
+ /**
6
+ * Zero-LLM-call gate for the one task shape this plan targets: an explicit, syntactically obvious
7
+ * list of ≥3 lookup targets (newline-separated names, markdown bullets, or a numbered list).
8
+ * Deliberately narrower than decomposition-classifier.ts's enumeration detection — no
9
+ * comma-enumeration, no sequencing-word detection, no word-count fallback. Those stay that file's
10
+ * problem; this one only fires on a shape unambiguous enough that a false positive should be
11
+ * essentially impossible. Fails closed (returns null) for anything else, including a list where
12
+ * most lines don't read as name-shaped (task instructions, not lookup targets) and a run below the
13
+ * 3-item floor.
14
+ */
15
+ export declare function detectHomogeneousBatchList(message: string): BatchListDetection | null;
@@ -0,0 +1,50 @@
1
+ import { LLMStructuredResponse } from '@buildaharness/runtime';
2
+ import { WebSearchResult } from './web-tools.js';
3
+ /**
4
+ * The real school-dates comparison this plan is built on (see
5
+ * plans/personal_assistant_dynamic_tool_budget_plan.html's Reference Transcripts tab), encoded as
6
+ * a reusable scripted-LLM-response fixture instead of duplicated inline in assistant.test.ts —
7
+ * so the integration replay test stays traceable back to the actual incident that motivated this
8
+ * plan, and any recalibration of thresholds later has the real shape to check against.
9
+ *
10
+ * `searchYields` lists one entry per web_search call this item's own per-item sub-loop makes, in
11
+ * order. An item whose last 3 entries are all `'dead_end'` (Halensee-Grundschule below) trips
12
+ * resolveBatchItem's item-scoped dead-end window (BATCH_DEAD_END_WINDOW = 3) — that item never
13
+ * reaches a final LLM call at all, so `finalAnswer` is omitted for it; every other item needs one.
14
+ */
15
+ export interface BatchFixtureItem {
16
+ /** Exact list-item text, as it will appear (one per line) in the batch request message. */
17
+ item: string;
18
+ searchYields: ReadonlyArray<'productive' | 'dead_end'>;
19
+ /** The model's own final per-item answer, once every scripted search yield above has been
20
+ * consumed. Omitted only for an item whose sub-loop resolves via the dead-end window instead
21
+ * (see the doc comment above). */
22
+ finalAnswer?: string;
23
+ }
24
+ /**
25
+ * The actual 7-school request from the comparison transcript, in the same order the user pasted
26
+ * them back. Grunewald-Grundschule (cheap — resolved without needing a single search) and
27
+ * Erich-Kästner-Grundschule (expensive — needed several searches before the Terminplan PDF turned
28
+ * up a confirmed date) are items[0]/items[1], so a 7-item batch's probe phase (probeCount = 2)
29
+ * naturally covers both the cheap and the expensive case. Halensee-Grundschule (the genuinely
30
+ * dead 404'd page) sits among the remaining items, proving the item-scoped dead-end window
31
+ * doesn't drag down the other, perfectly findable schools queued behind it.
32
+ */
33
+ export declare const SCHOOL_DATES_BATCH_FIXTURE: readonly BatchFixtureItem[];
34
+ /** The batch request message a user would actually send — each item on its own line, matching
35
+ * detectHomogeneousBatchList's expected shape. */
36
+ export declare function fixtureUserMessage(items?: readonly BatchFixtureItem[]): string;
37
+ /**
38
+ * The ordered callChatStructured script every item's own sub-loop consumes, in the exact order
39
+ * runBatchToolLoop/resolveRemainingBatchItems resolve them (probe items first, in array order,
40
+ * then the rest in array order) — a ScriptedToolLLMClient (see assistant.test.ts) plays this back
41
+ * verbatim regardless of what the mocked web_search calls actually return.
42
+ */
43
+ export declare function fixtureStructuredResponses(items?: readonly BatchFixtureItem[]): LLMStructuredResponse[];
44
+ /**
45
+ * A stateful web_search mock consuming `searchYields` in the same flattened order
46
+ * fixtureStructuredResponses schedules its tool calls in — 'dead_end' yields an empty result
47
+ * array (web-tools.ts's executor turns that into the literal "No results found."); 'productive'
48
+ * yields one real-looking result.
49
+ */
50
+ export declare function fixtureWebSearch(items?: readonly BatchFixtureItem[]): () => Promise<WebSearchResult[]>;
@@ -0,0 +1,27 @@
1
+ import { ToolExecutorContext } from '@buildaharness/harness';
2
+ export interface InjectedFailureOptions {
3
+ /** How many leading harness iterations report a failed execution. */
4
+ failIterations: number;
5
+ /** Recurring same-class `failure_history` records seeded on the first call. */
6
+ seedFailures: number;
7
+ /**
8
+ * How many times the *real* proposer may run after the failure phase (default 2).
9
+ * The first real call is the derailed attempt; a supervisor recovery (a re-queued
10
+ * leaf after GATHER_EVIDENCE / REDIRECT_STRATEGY, S8) gets a second, fresh call so
11
+ * newly gathered evidence in the WorldModel can actually change the answer. Beyond
12
+ * this the last real result is replayed so a thrashing run still converges without
13
+ * unbounded backend cost.
14
+ */
15
+ maxRealCalls?: number;
16
+ /**
17
+ * Called exactly once, the first time the wrapper actually runs (i.e. the failure was really
18
+ * injected into this turn). The benchmark reads it back as `ArmTurnOutput.injectedFailureFired`
19
+ * so `recoveryRate` only counts tasks where the failure genuinely fired for this arm — not
20
+ * every task that merely *declares* an `injectedFailure` (see
21
+ * plans/feature_audit_fair_comparison_plan.html F2).
22
+ */
23
+ onInjected?: () => void;
24
+ }
25
+ type Proposer = (toolCtx: ToolExecutorContext) => unknown | Promise<unknown>;
26
+ export declare function wrapProposerWithInjectedFailure(realProposer: Proposer, opts: InjectedFailureOptions): (toolCtx: ToolExecutorContext) => Promise<unknown>;
27
+ export {};