@buildaharness/aielia 0.3.4 → 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 (97) 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 +23 -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 +12 -1
  11. package/dist/assistant-source.d.ts +7 -0
  12. package/dist/assistant-types.d.ts +17 -1
  13. package/dist/assistant.d.ts +128 -6
  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 +14 -1
  18. package/dist/cli.d.ts +17 -2
  19. package/dist/cli.js +1 -1
  20. package/dist/code-plan-templates.d.ts +21 -0
  21. package/dist/config.d.ts +29 -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 +0 -2
  32. package/dist/goal-graph-store.d.ts +18 -18
  33. package/dist/grounding-check.d.ts +33 -0
  34. package/dist/harness-bridge.d.ts +75 -2
  35. package/dist/harness-token-budget.d.ts +9 -0
  36. package/dist/index.d.ts +20 -1
  37. package/dist/index.js +165 -94
  38. package/dist/index.js.map +1 -1
  39. package/dist/layer-policy-flag.d.ts +13 -0
  40. package/dist/layer-policy-wiring.d.ts +77 -0
  41. package/dist/layer-settings.d.ts +49 -0
  42. package/dist/layer-telemetry.d.ts +2 -0
  43. package/dist/lexical/lexical-mode.d.ts +29 -0
  44. package/dist/memory-consolidation.d.ts +80 -0
  45. package/dist/memory-governance.d.ts +30 -0
  46. package/dist/memory-reviewer.d.ts +114 -0
  47. package/dist/memory-service.d.ts +348 -9
  48. package/dist/model-json.d.ts +17 -0
  49. package/dist/next-step-context.d.ts +51 -0
  50. package/dist/next-step-proposer.d.ts +3 -2
  51. package/dist/plan-graph-flag.d.ts +20 -0
  52. package/dist/plan-question.d.ts +60 -0
  53. package/dist/plan-service.d.ts +6 -2
  54. package/dist/plan-step-prompt.d.ts +15 -0
  55. package/dist/plan-store.d.ts +7 -1
  56. package/dist/plan-viz/normalize.d.ts +14 -0
  57. package/dist/plan-viz/plan-detail-lines.d.ts +3 -0
  58. package/dist/plan-viz/plan-layout.d.ts +48 -0
  59. package/dist/plan-viz/plan-nav.d.ts +29 -0
  60. package/dist/plan-viz/plan-raster.d.ts +36 -0
  61. package/dist/plan-viz/plan-routes.d.ts +6 -0
  62. package/dist/plan-viz/plan-snapshot.d.ts +73 -0
  63. package/dist/plan-viz/plan-viewport.d.ts +29 -0
  64. package/dist/plan-viz/plan-width.d.ts +15 -0
  65. package/dist/plan-viz/testkit/display-width.d.ts +12 -0
  66. package/dist/plan-viz/testkit/generators.d.ts +61 -0
  67. package/dist/plan-viz/testkit/grid-oracles.d.ts +38 -0
  68. package/dist/plan-viz/testkit/nav-oracles.d.ts +79 -0
  69. package/dist/plan-viz/testkit/types.d.ts +8 -0
  70. package/dist/plan-viz/types.d.ts +34 -0
  71. package/dist/{provider-setup-CPbB5y_V.js → provider-setup-LFsaTkpf.js} +9308 -5510
  72. package/dist/provider-setup-LFsaTkpf.js.map +1 -0
  73. package/dist/recall-tool.d.ts +43 -0
  74. package/dist/recovery-note.d.ts +22 -0
  75. package/dist/response-service.d.ts +11 -1
  76. package/dist/review-checker.d.ts +10 -4
  77. package/dist/reviewer-revision.d.ts +23 -0
  78. package/dist/risk-classifier.d.ts +7 -0
  79. package/dist/scripted-llm-client.d.ts +26 -1
  80. package/dist/semantic-compaction.d.ts +15 -0
  81. package/dist/semantic-hypotheses.d.ts +40 -0
  82. package/dist/source-reliability.d.ts +71 -0
  83. package/dist/task-completion-check.d.ts +24 -0
  84. package/dist/tool-control-plane.d.ts +33 -1
  85. package/dist/tool-effect-class.d.ts +9 -0
  86. package/dist/transcript-compaction.d.ts +9 -0
  87. package/dist/{tui-app-DeyZ4Pr9.js → tui-app-BLR-PRFp.js} +321 -7
  88. package/dist/tui-app-BLR-PRFp.js.map +1 -0
  89. package/dist/tui-app.d.ts +38 -0
  90. package/dist/turn-intent-classifier.d.ts +75 -16
  91. package/dist/turn-interpreter.d.ts +25 -3
  92. package/dist/version.d.ts +1 -1
  93. package/package.json +6 -6
  94. package/dist/bin-CdVTAe7j.js +0 -2209
  95. package/dist/bin-CdVTAe7j.js.map +0 -1
  96. package/dist/provider-setup-CPbB5y_V.js.map +0 -1
  97. package/dist/tui-app-DeyZ4Pr9.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;
@@ -204,6 +199,19 @@ export declare class AgentLoop {
204
199
  * caller-constraint output contract.
205
200
  */
206
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;
207
215
  }): (toolCtx: ToolExecutorContext) => Promise<unknown>;
208
216
  /**
209
217
  * R3 of the internal plan: the wiring-level counterpart to
@@ -216,7 +224,7 @@ export declare class AgentLoop {
216
224
  * back whatever got pushed to it once the harness run using this proposer has finished (mirrors
217
225
  * `ToolLoopResult`'s `sources` field on the flag-OFF path).
218
226
  */
219
- 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[]): {
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): {
220
228
  proposer: (toolCtx: ToolExecutorContext) => Promise<unknown>;
221
229
  sources: AssistantSource[];
222
230
  };
@@ -355,6 +363,13 @@ export declare class AgentLoop {
355
363
  * injection attempt) before they ever reach the model — file and reminder results
356
364
  * are not, since they're the assistant's own workspace/state, not adversarial input.
357
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;
358
373
  private executeToolCall;
359
374
  /**
360
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'
@@ -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,5 +1,5 @@
1
1
  import { NextStepSuggestion } from './next-step-proposer.js';
2
- import { RiskState, LayerActivityEvent, AskQuestion } from '@buildaharness/harness';
2
+ import { RiskState, LayerActivityEvent, AskQuestion, TaskStatus } from '@buildaharness/harness';
3
3
  import { TokenUsage } from '@buildaharness/runtime';
4
4
  import { AnswerClaim } from './answer-claim.js';
5
5
  import { TurnIntentClassification } from './turn-intent-classifier.js';
@@ -101,10 +101,15 @@ export interface AssistantTurnResult {
101
101
  templateName: string | null;
102
102
  successCriteria: string;
103
103
  completionPct: number;
104
+ /** `note`: why a FAILED task did not count as done (set only by the semantic task-completion check). */
104
105
  tasks: {
105
106
  id: string;
106
107
  description: string;
107
108
  status: string;
109
+ note?: string;
110
+ dependsOn?: string[];
111
+ cancelled?: boolean;
112
+ riskLevel?: 'LOW' | 'MEDIUM' | 'HIGH';
108
113
  }[];
109
114
  };
110
115
  /**
@@ -117,6 +122,8 @@ export interface AssistantTurnResult {
117
122
  usage?: TokenUsage;
118
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`. */
119
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;
120
127
  /**
121
128
  * An epistemic-honesty signal for replies that went through the harness loop (absent on the
122
129
  * triviality fast path, same "absent when unused" convention as trace/sources — a
@@ -163,4 +170,13 @@ export interface AssistantProgress {
163
170
  currentNode?: string;
164
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. */
165
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
+ }[];
166
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,13 +8,18 @@ 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';
19
24
  import { GoalGraphSuggestMode } from './goal-graph-suggest-flag.js';
20
25
  import { GoalGraphState } from './goal-graph-service.js';
@@ -43,6 +48,8 @@ export type MemoryPendingOutcome = {
43
48
  };
44
49
  export interface TurnOptions {
45
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;
46
53
  approved?: boolean;
47
54
  pendingActionId?: string;
48
55
  /**
@@ -97,7 +104,7 @@ export interface TurnOptions {
97
104
  */
98
105
  onToolStep?: (step: AssistantToolStep) => void;
99
106
  /**
100
- * **Eval harness only** (`packages/aielia/eval/`). Wraps the one-loop
107
+ * **Benchmark harness only.** Wraps the one-loop
101
108
  * proposer so its first `failIterations` harness iterations report a failed execution
102
109
  * and `seedFailures` recurring same-class records are seeded into the run's
103
110
  * `failureDiagnostics` — enough to trip `cannotMakeProgress()` on iteration 1 and
@@ -108,6 +115,7 @@ export interface TurnOptions {
108
115
  failIterations: number;
109
116
  seedFailures: number;
110
117
  onInjected?: () => void;
118
+ symptom?: string;
111
119
  };
112
120
  /**
113
121
  * Phase 4 of plans/hierarchical_goal_tree_and_steering_plan.html (R1/R4) — the session-scoped
@@ -137,6 +145,14 @@ export interface PersonalAssistantOptions {
137
145
  * fresh install with no workspace concept at all.
138
146
  */
139
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;
140
156
  /** Conversation transcript storage — defaults to an in-process Map, swap for IndexedDBAdapter in the browser. */
141
157
  memory?: MemoryAdapter;
142
158
  /** Learning-layer store — persist and pass the same instance back in across sessions to retain strategy weights. */
@@ -226,6 +242,8 @@ export interface PersonalAssistantOptions {
226
242
  * call resolveOneLoopMode(process.env) and pass the result here.
227
243
  */
228
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;
229
247
  /**
230
248
  * R7 (Tier 1.5) — next-step suggestions, one bounded LLM call each: after every full
231
249
  * (non-trivial, `ok`) turn, up to three options are attached to the result as `nextSteps` for
@@ -256,6 +274,13 @@ export interface PersonalAssistantOptions {
256
274
  * resolvePlanMode(process.env)/normalizePlanMode(import.meta.env...) and pass the result here.
257
275
  */
258
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;
259
284
  }
260
285
  /**
261
286
  * A light, everyday-use wrapper around HarnessRuntime: one harness run per chat turn,
@@ -277,6 +302,9 @@ export declare class PersonalAssistant {
277
302
  private readonly llmClient;
278
303
  private model?;
279
304
  private activeProject?;
305
+ private memoryBudgetChars?;
306
+ private memoryWriteMode;
307
+ private consolidateOnNewSession;
280
308
  private readonly memory;
281
309
  private readonly webTools?;
282
310
  private readonly onTrace?;
@@ -286,6 +314,7 @@ export declare class PersonalAssistant {
286
314
  private readonly toolLoopWillRun;
287
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. */
288
316
  private readonly oneLoopMode;
317
+ private readonly layerPolicyMode;
289
318
  /** Q2 — global-flag tier of the ask-question mechanism's three-tier INV-29 resolution. See PersonalAssistantOptions.askMode's doc comment. */
290
319
  private readonly askMode;
291
320
  /** P11 — gates whether P3's auto-trigger below can ever enter plan mode for real traffic. See PersonalAssistantOptions.planMode's doc comment. */
@@ -301,6 +330,8 @@ export declare class PersonalAssistant {
301
330
  /** See PersonalAssistantOptions.goalGraphSuggestMode. */
302
331
  private readonly goalGraphSuggestMode;
303
332
  private readonly memoryService;
333
+ /** M4 post-turn cross-turn memory reviewer; inert unless `AUDIT_MEMORY_REVIEWER` is on. */
334
+ private readonly memoryReviewer;
304
335
  private readonly session;
305
336
  private readonly agentLoop;
306
337
  private readonly actionApproval;
@@ -321,12 +352,29 @@ export declare class PersonalAssistant {
321
352
  * PersonalAssistant(...)` outside a browser (e.g. the CLI).
322
353
  */
323
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;
324
363
  /**
325
364
  * Thin wrapper around runTurn(): emits turn_start/turn_end/error trace events
326
365
  * around the actual logic, so every one of runTurn's return paths gets a
327
366
  * matching turn_end without instrumenting each one individually.
328
367
  */
329
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>;
330
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. */
331
379
  getSpendState(sessionId: string): Promise<SpendState>;
332
380
  /** The session's conversation transcript, oldest first — same array `turn()` reads/appends to. Used by `/export`. */
@@ -339,6 +387,45 @@ export declare class PersonalAssistant {
339
387
  recordDeclinedRequest(sessionId: string, userMessage: string, reason: string): Promise<void>;
340
388
  /** Ends the current conversation — see AssistantSession.clearSession's doc comment for exactly what is and isn't cleared. */
341
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>;
342
429
  /**
343
430
  * P1 of the internal plan — explicit entry point into plan mode's
344
431
  * exclusive drafting state. No production caller yet: P3 will later set this automatically from
@@ -369,6 +456,11 @@ export declare class PersonalAssistant {
369
456
  * goal graph yet, same "nothing to show" convention `getGoalGraphState` itself documents.
370
457
  */
371
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>;
372
464
  /**
373
465
  * P9 of the internal plan — the lightweight plan-sketch delegate: a
374
466
  * cheaper "just go research and draft an approach" path than plan mode's durable task-graph
@@ -427,9 +519,39 @@ export declare class PersonalAssistant {
427
519
  * facts first, then session facts — the same order `getMemorySummary()` returns). Unlike
428
520
  * confirm/reject, there's no category form: a durable/session fact carries no `category` field,
429
521
  * only pending model-inferred guesses do. Hard-deletes rather than routing through
430
- * REJECTED_FACTS_KEY — see MemoryService.forgetFact's doc comment.
431
- */
432
- forgetFact(selector: string, sessionId: string): Promise<MemoryPendingOutcome>;
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.
524
+ */
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;
433
555
  /** Ranked search over the per-message index — see AssistantSession.searchTranscript's doc comment. Used by `/search`. */
434
556
  searchTranscript(query: string, topK?: number): Promise<TranscriptSearchHit[]>;
435
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>;