@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
@@ -1,7 +1,11 @@
1
+ import { NextStepSuggestion } from './next-step-proposer.js';
1
2
  import { ChatMessage, TokenUsage } from '@buildaharness/runtime';
2
3
  import { AssistantConfig } from './config.js';
3
4
  import { MemorySummary, MemoryExport, TranscriptSearchHit, MemoryPendingOutcome } from './assistant.js';
5
+ import { AuditEntry, MemoryStatus, MemoryInjection } from './memory-service.js';
6
+ import { GoalGraphState } from './goal-graph-service.js';
4
7
  import { UndoLogEntry } from './action-snapshot.js';
8
+ import { UserFact } from './fact-extraction.js';
5
9
  /**
6
10
  * Pure formatting/logic backing cli.ts's session-level commands (/help, /status, /export,
7
11
  * /memory, /cost, /doctor) — split out so it's unit-testable without a real readline loop,
@@ -12,7 +16,8 @@ export interface CliCommandHelp {
12
16
  command: string;
13
17
  description: string;
14
18
  }
15
- /** Kept as a hand-maintained list rather than derived from cli.ts's dispatch table — see /help's task note on why this can drift and needs manual upkeep when a command is added. */
19
+ /** A bare `exit` / `quit` (with or without a leading slash, any case) ends the session; anything longer is an ordinary message. An exact command match, not a reading of the message. */
20
+ export declare function isQuitCommand(message: string): boolean;
16
21
  export declare const CLI_COMMANDS_HELP: CliCommandHelp[];
17
22
  export declare function formatHelp(): string;
18
23
  export interface StatusInfo {
@@ -30,10 +35,30 @@ export declare function formatStatus(info: StatusInfo): string;
30
35
  /** Renders the result of `/memory confirm`/`/memory reject` — cli.ts's handleMemoryConfirm/handleMemoryReject print this directly. */
31
36
  export declare function formatMemoryPendingOutcome(action: 'confirmed' | 'rejected' | 'forgotten', outcome: MemoryPendingOutcome): string;
32
37
  export declare function formatMemorySummary(summary: MemorySummary): string;
38
+ /** `/memory history` — one line per audit entry. */
39
+ export declare function formatMemoryHistory(entries: AuditEntry[]): string;
40
+ /** `/memory archive` — facts a newer keyed statement replaced. */
41
+ export declare function formatMemoryArchive(facts: UserFact[], restorableCount?: number): string;
42
+ /** The "Why?" / `/why` line: which stored facts the last reply could have known, and how many were left out. Says "in the prompt", not "used". */
43
+ export declare function formatMemoryInjection(injection: MemoryInjection | undefined): string;
44
+ /** `/memory status` and the memory panel header. */
45
+ export declare function formatMemoryStatus(status: MemoryStatus): string;
46
+ /** `/doctor` entries for memory (CLI via doctor-checks.ts, chat-ui directly — same pure function on both surfaces). */
47
+ export declare function memoryStatusChecks(status: MemoryStatus): DoctorCheck[];
33
48
  /** Renders `/memory export`'s full, unbounded contents as pretty-printed JSON — a plain formatting function so it's unit-testable the same way as every other cli-session.ts formatter; cli.ts does the actual file write. */
34
49
  export declare function formatMemoryExport(data: MemoryExport): string;
35
50
  /** Renders `/search <query>`'s results — each hit is one message (session id short form, timestamp, role, snippet), ranked by relevance, never the whole session it came from. An empty list is an explicit "no results" line, never a silent blank output. */
36
51
  export declare function formatSearchResults(hits: TranscriptSearchHit[], query: string): string;
52
+ /** Renders the turn-end next-step options shown under a full turn's reply — numbered so the CLI can accept a bare `1`/`2`/`3` as "run that one" (see cli.ts's dispatchOne). Only the description is shown; the rationale and confidence stay in the structured result for richer front ends. */
53
+ export declare function formatNextSteps(steps: NextStepSuggestion[]): string;
54
+ /**
55
+ * Renders `/goals`' review surface (R5, "Visibility") — every known GoalThread this session, each
56
+ * tagged with which of R5's four visibility buckets it falls in
57
+ * (freshly-computed/carried-over/done/suggested-but-not-committed), so a user can tell live state
58
+ * apart from stale carryover without re-deriving it from raw JSON. An empty graph is an explicit
59
+ * line, same convention `formatSearchResults` follows for zero hits.
60
+ */
61
+ export declare function formatGoalGraphState(state: GoalGraphState): string;
37
62
  /** Renders /undo-action's no-argument listing — newest first (see action-snapshot.ts's listUndoLogEntries), with each entry's undoable status so the user doesn't have to stage a revert just to find out it's unavailable. */
38
63
  export declare function formatUndoLogListing(entries: UndoLogEntry[]): string;
39
64
  /** Renders a transcript as markdown for /export — "You:"/"Assistant:" turns, oldest first. */
package/dist/cli.d.ts CHANGED
@@ -2,6 +2,9 @@ import { FsBackend } from '@buildaharness/runtime';
2
2
  import { PersonalAssistant } from './assistant.js';
3
3
  import { AssistantConfig, ConfigStore } from './config.js';
4
4
  import { NonInteractiveApprovalMode } from './non-interactive-mode.js';
5
+ import { VizNode } from './plan-viz/types.js';
6
+ import { TaskStatus } from '@buildaharness/harness';
7
+ import { KeyedBackend, KeyTestResult } from './provider-setup.js';
5
8
  /**
6
9
  * One choice in a structured multi-option prompt (Phase 7 of the internal plan
7
10
  * — the numbered/arrow-key approval selector). `key` is both the single-character shortcut shown
@@ -15,6 +18,16 @@ export interface SelectOption {
15
18
  label: string;
16
19
  }
17
20
  export interface RunCliOptions {
21
+ /**
22
+ * The Ink shell's seam for `/plan graph` (planGraphMode enabled): called with the drawable nodes to open the
23
+ * full-screen pane. Without it (plain REPL, piped input) `/plan graph` prints one static render instead.
24
+ */
25
+ openPlanGraph?: (nodes: VizNode[]) => void;
26
+ /** Live per-task statuses from a running turn, forwarded for the open plan graph pane. */
27
+ onPlanProgress?: (tasks: {
28
+ id: string;
29
+ status: TaskStatus;
30
+ }[]) => void;
18
31
  dataDir?: string;
19
32
  configStore?: ConfigStore;
20
33
  backend?: FsBackend;
@@ -38,10 +51,10 @@ export interface RunCliOptions {
38
51
  askSelect?: (question: string, options: SelectOption[]) => Promise<string>;
39
52
  /** Test seam: skip the interactive first-run setup regardless of TTY state (also implied when `assistant` is passed). */
40
53
  skipFirstRunSetup?: boolean;
41
- /** Test seam: stub `claude` binary detection for the first-run setup. */
42
- detectClaudeCli?: () => Promise<boolean>;
43
54
  /** Test seam: script the first-run setup's line reads. */
44
55
  firstRunAsk?: (question: string) => Promise<string>;
56
+ /** Test seam: stub the live API-key check in the first-run setup (defaults to a real call to the provider). */
57
+ firstRunTestKey?: (backend: KeyedBackend, key: string) => Promise<KeyTestResult>;
45
58
  }
46
59
  export interface CliInstance {
47
60
  /** Parses and dispatches one line of input exactly as the REPL's 'line' handler does (same dispatchQueue serialization) — the seam cli.test.ts drives command dispatch through without a live TTY. */
@@ -56,6 +69,11 @@ export interface CliInstance {
56
69
  * readline REPL or by non-interactive/piped mode — only the TUI shell polls this.
57
70
  */
58
71
  getStatusIndicators(): Promise<string[]>;
72
+ /** Read-only: the plan graph's drawable nodes (with `live` statuses overlaid), or undefined when planGraphMode is disabled or there is no plan. Feeds the TUI's open plan graph pane. */
73
+ getPlanGraphNodes(live?: {
74
+ id: string;
75
+ status: TaskStatus;
76
+ }[]): Promise<VizNode[] | undefined>;
59
77
  }
60
78
  /**
61
79
  * The CLI's REPL, parameterized so cli.test.ts can import and drive it directly instead of only
package/dist/cli.js CHANGED
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env node
2
- import "./bin-Btzhiyzi.js";
2
+ import "./bin-CjKpxDxl.js";
3
3
  //# sourceMappingURL=cli.js.map
@@ -0,0 +1,21 @@
1
+ import { ProcessConcept } from '@buildaharness/harness';
2
+ import { PlanTemplate } from './plan-templates/index.js';
3
+ /**
4
+ * `AUDIT_CODE_PLAN_TEMPLATES` gate. Default **OFF**: the plan templates are the seven general-purpose ones, exactly as
5
+ * before. On, the harness's four bundled process concepts (debug a failing test, implement a feature, review code,
6
+ * refactor a module — `packages/harness/src/concepts/`) are offered as plan templates too, so the turn-intent
7
+ * classifier can match a code-shaped request to one and the request goes through the ordinary plan path (approval,
8
+ * persistence, per-step prompts, criteria). Until now nothing in aielia could reach those concepts. A truthy value
9
+ * (`1` / `true` / `on` / `yes` / `enabled`) enables it. Read where `plan-templates` builds its list.
10
+ */
11
+ export declare function codePlanTemplatesEnabled(env?: Record<string, string | undefined>): boolean;
12
+ /**
13
+ * A process concept as a plan template. Step order, dependencies, risk and abstraction level come from the harness's
14
+ * own `seedTaskGraph` (so the level mapping is not copied here); a step's success criteria are appended to its
15
+ * text as "Done when …" because a plan task has no criteria field of its own. Dropped on purpose: the
16
+ * concept's `expectedTools` (their names — `run_tests`, … — are not aielia's tools and would mislead the model) and
17
+ * `strategyHint` (a plan task has nowhere to carry it).
18
+ */
19
+ export declare function conceptToPlanTemplate(concept: ProcessConcept): PlanTemplate;
20
+ /** The bundled process concepts as plan templates, keyed by concept id. Built once. */
21
+ export declare function codePlanTemplates(): Record<string, PlanTemplate>;
package/dist/config.d.ts CHANGED
@@ -1,8 +1,14 @@
1
1
  import { OneLoopMode } from './one-loop-flag.js';
2
2
  import { AskMode } from './ask-mode-flag.js';
3
+ import { AmbiguityGuardMode } from './ambiguity-guard-flag.js';
3
4
  import { PlanRolloutMode } from './plan-mode-flag.js';
4
5
  import { TuiMode } from './tui-mode-flag.js';
5
6
  import { UpdateCheckMode } from './update-check-flag.js';
7
+ import { GoalGraphMode } from './goal-graph-flag.js';
8
+ import { PlanGraphMode } from './plan-graph-flag.js';
9
+ import { LayerPolicyMode } from './layer-policy-flag.js';
10
+ import { GoalGraphSuggestMode } from './goal-graph-suggest-flag.js';
11
+ import { MemoryWriteMode } from './memory-governance.js';
6
12
  export interface AssistantConfig {
7
13
  llmBackend: 'proxy' | 'claude-cli' | 'anthropic' | 'openai' | 'openrouter';
8
14
  proxyUrl: string;
@@ -90,6 +96,10 @@ export interface AssistantConfig {
90
96
  sessionCostLimitUsd?: number;
91
97
  /** Secondary ceiling on completed turns this session — see SpendCapConfig's doc comment in spend-cap.ts for why this counts turns, not raw internal LLM calls. */
92
98
  sessionCallLimit?: number;
99
+ /** M1: character budget for the per-turn facts block under `AUDIT_MEMORY_BUDGETED_RENDER`. Undefined = DEFAULT_MEMORY_BUDGET_CHARS (memory-service.ts). */
100
+ memoryBudgetChars?: number;
101
+ /** M6: where model-originated memory writes may land — `auto` | `staged` (default) | `user_only`; see memory-governance.ts. */
102
+ memoryWriteMode?: MemoryWriteMode;
93
103
  /**
94
104
  * R5 of the internal plan — the rollout flag for the harness-driven
95
105
  * one-loop proposer (see one-loop-flag.ts's doc comment for the full rationale). Undefined (the
@@ -125,6 +135,8 @@ export interface AssistantConfig {
125
135
  * out of DEFAULT_CONFIG so an unset value stays undefined.
126
136
  */
127
137
  planMode?: PlanRolloutMode;
138
+ /** AL3a — ask a clarifying question before staging an under-specified consequential request. See ambiguity-guard-flag.ts. Undefined = 'disabled'. */
139
+ ambiguityGuardMode?: AmbiguityGuardMode;
128
140
  /**
129
141
  * Phase 4 of the internal plan — the rollout flag for the
130
142
  * Ink-driven pinned-bottom-input interactive shell (tui-app.ts's runTuiApp()). Undefined (the
@@ -155,6 +167,45 @@ export interface AssistantConfig {
155
167
  * `/config reset activeProject`, mirroring `/model`'s relationship to `/config set model`.
156
168
  */
157
169
  activeProject?: string;
170
+ /**
171
+ * Phase 8 of plans/hierarchical_goal_tree_and_steering_plan.html — the shared config seam for
172
+ * the hierarchical goal-tree mechanism's Tier 0/1 gate (R1's mid-task steering, R4's scope×
173
+ * urgency classifier). Undefined (the default, for the whole rollout window) means every
174
+ * surface falls back to `DEFAULT_GOAL_GRAPH_MODE` ('disabled') — today's behavior, byte-for-byte
175
+ * (INV-43). Mirrors `oneLoopMode`/`askMode`/`planMode`'s exact chain: env override
176
+ * (`ASSISTANT_GOAL_GRAPH`, cli-config.ts), build-time override (`VITE_ASSISTANT_GOAL_GRAPH`,
177
+ * chat-ui's browser-config.ts), and a package-owned default kept out of DEFAULT_CONFIG so an
178
+ * unset value stays undefined. Before this phase, cli.ts/App.tsx read this straight off
179
+ * process.env/import.meta.env instead of through this field — see goal-graph-flag.ts's doc
180
+ * comment for why that was deliberate up to now.
181
+ */
182
+ goalGraphMode?: GoalGraphMode;
183
+ /**
184
+ * Read-only plan-graph view (plans/plan_visualization_plan.html). 'disabled' (the package default,
185
+ * kept out of DEFAULT_CONFIG like goalGraphMode) leaves every surface byte-identical to before.
186
+ * Presentation only — never added to LAYER_SETTINGS.
187
+ */
188
+ planGraphMode?: PlanGraphMode;
189
+ /**
190
+ * AL7b of plans/adaptive_layer_selection_plan.html — 'static' (default; also the kill switch) |
191
+ * 'shadow' (record what adaptive would decide, execute static) | 'adaptive'. Undefined falls back
192
+ * to `DEFAULT_LAYER_POLICY_MODE`. Chain: `ASSISTANT_LAYER_POLICY`, `VITE_ASSISTANT_LAYER_POLICY`,
193
+ * `/config set layerPolicyMode`; consumed via `isAdaptivePolicyEnabled()`.
194
+ */
195
+ layerPolicyMode?: LayerPolicyMode;
196
+ /**
197
+ * Phase 8 — the same shared config seam as `goalGraphMode` above, for the next-step proposer's
198
+ * (Tier 1.5) independent gate (Q8: a regression there shouldn't block Tier 0/1). Undefined
199
+ * falls back to `DEFAULT_GOAL_GRAPH_SUGGEST_MODE` ('disabled'). Same env/build-time/`/config
200
+ * set` chain, via `ASSISTANT_GOAL_GRAPH_SUGGEST`/`VITE_ASSISTANT_GOAL_GRAPH_SUGGEST`.
201
+ */
202
+ goalGraphSuggestMode?: GoalGraphSuggestMode;
203
+ /**
204
+ * Per-layer on/off choices for the reasoning layers (layer-settings.ts's LAYER_SETTINGS), keyed by layer id. An absent id means
205
+ * "the layer's own default". Applied onto the layers' `AUDIT_*` env flags by `applyLayerSettings`; a flag already set in the real
206
+ * environment wins and renders pinned. Edited with the CLI's `/layers` and the Settings screen's Layers section, not `/config set`.
207
+ */
208
+ layers?: Record<string, boolean>;
158
209
  }
159
210
  /** Every AssistantConfig key, in the order every surface's settings UI/listing renders them. */
160
211
  export declare const CONFIG_KEYS: readonly (keyof AssistantConfig)[];
@@ -0,0 +1,35 @@
1
+ import { ILLMClient, TokenUsage } from '@buildaharness/runtime';
2
+ export interface ConstraintViolation {
3
+ constraint: string;
4
+ reason?: string;
5
+ }
6
+ /**
7
+ * `AUDIT_SEMANTIC_CONSTRAINT_CHECK` gate. Default **ON**: the harness's lexical caller-constraint check throws on any
8
+ * reply that names the constraint's subject — including one that obeys it — so this replaces it rather than adding
9
+ * to it, and only runs when the caller has set a constraint (the lexical check's own precondition). Set to a falsy
10
+ * value (`0` / `false` / `off` / `no` / `disabled`) to keep the lexical check. Read at exactly one site: harness-bridge.ts.
11
+ */
12
+ export declare function semanticConstraintCheckEnabled(env?: Record<string, string | undefined>): boolean;
13
+ /**
14
+ * What to append to a reply the judge still found breaking a constraint after the one revision (the harness hands the answer
15
+ * back with the violation instead of failing the turn). Empty when nothing is unresolved.
16
+ */
17
+ export declare function renderUnresolvedConstraintNote(violations: ReadonlyArray<ConstraintViolation> | undefined): string;
18
+ /** Most standing constraints kept per session; the oldest drop first. */
19
+ export declare const MAX_STANDING_CONSTRAINTS = 8;
20
+ /**
21
+ * Standing constraints plus the ones stated now: trimmed, de-duplicated (case-insensitive), the newest last, capped at
22
+ * MAX_STANDING_CONSTRAINTS (oldest dropped). A constraint restated moves to the end rather than appearing twice.
23
+ */
24
+ export declare function mergeStandingConstraints(existing: readonly string[], incoming: readonly string[]): string[];
25
+ /**
26
+ * One bounded LLM call: does this reply break any of these constraints? Fails open — an error, an empty reply or an
27
+ * unparseable answer is "no violations", so a broken check can never fail a turn that would have passed. Only
28
+ * constraints the caller actually set are reported (a constraint the model invented is dropped).
29
+ */
30
+ export declare function checkConstraints(input: {
31
+ constraints: string[];
32
+ reply: string;
33
+ }, llmClient: ILLMClient, model?: string, onUsage?: (usage: TokenUsage) => void): Promise<{
34
+ violated: ConstraintViolation[];
35
+ }>;
@@ -15,6 +15,13 @@ export declare function looksLikeCodingFact(statement: string): boolean;
15
15
  * site — `harness-bridge.ts`, where the `contradictionChecker` host hook is wired.
16
16
  */
17
17
  export declare function semanticContradictionEnabled(env?: Record<string, string | undefined>): boolean;
18
+ /**
19
+ * `AUDIT_SEMANTIC_CONTRADICTION_SEVERITY` (default OFF). The checker used to set no severity, so every semantic
20
+ * contradiction recorded as MEDIUM and Tier 1 (DENY) was unreachable through it. On: the judgment also grades each
21
+ * contradiction, and only "HIGH" (two claims that directly and explicitly cannot both hold) is passed on — anything
22
+ * else, or a missing/unknown value, stays MEDIUM. Never LOW or SYSTEM_BREAKING.
23
+ */
24
+ export declare function semanticContradictionSeverityEnabled(env?: Record<string, string | undefined>): boolean;
18
25
  /** A newBelief restating/reinforcing an uncertainFacts or rejectedFacts entry in different words — see checkForContradictions' doc comment. Caller resolves which pool `existingId` came from by id membership (both ids are always from this call's own known-id set, filtered below). */
19
26
  export interface Corroboration {
20
27
  existingId: string;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * `AUDIT_DECOMPOSED_ANSWER_ONCE` gate. Default **ON**: an ordinary turn the classifier split into several
3
+ * subtasks answers the user once, and the later subtasks reuse that answer. A falsy value (`0` / `false` /
4
+ * `off` / `no` / `disabled`) restores the one-answer-per-subtask behaviour. Read at exactly one site:
5
+ * assistant.ts, where the proposer is wired.
6
+ *
7
+ * Why: the harness runs each subtask through the same proposer, and every call was given only the user's
8
+ * message, so a request split into nine subtasks produced the complete answer thirteen times and returned
9
+ * the last one. (The pre-one-loop path computed one reply and handed it to every task; this restores that.)
10
+ * A plan's steps are different — each is real work and gets its own instruction (plan-step-prompt.ts) — so
11
+ * they are not affected.
12
+ */
13
+ export declare function decomposedAnswerOnceEnabled(env?: Record<string, string | undefined>): boolean;
@@ -7,6 +7,8 @@ import { ILLMClient, TokenUsage } from '@buildaharness/runtime';
7
7
  * silently create several reminders in one turn.
8
8
  */
9
9
  export declare function looksLikeEnumeratedItems(message: string): boolean;
10
+ /** The raw lexical enumeration check, used inside `classifyRisk`'s bulk-reminder gate — which is itself switched by lexicalMode's `risk` family. */
11
+ export declare function looksLikeEnumeratedItemsLexical(message: string): boolean;
10
12
  export interface DecomposedTaskSpec {
11
13
  id: string;
12
14
  description: string;
@@ -1,8 +1,11 @@
1
1
  import { FsBackend } from '@buildaharness/runtime';
2
2
  import { DoctorCheck } from './cli-session.js';
3
+ import { MemoryStatus } from './memory-service.js';
3
4
  /** GET <proxyUrl>/health, expecting {status:'ok'} (see packages/proxy/src/index.ts) — hard-timed so a dead proxy can't hang /doctor. */
4
5
  export declare function checkProxyHealth(proxyUrl: string): Promise<DoctorCheck>;
5
6
  /** Spawns `<claudePath> --version`, same path resolution ClaudeCliLLMClient uses — hard-timed so a hung/missing binary can't hang /doctor. */
6
7
  export declare function checkClaudeCli(claudePath: string): Promise<DoctorCheck>;
8
+ /** M6: memory store size vs budget, pending count, last consolidation, audit-log size. The rendering is the shared pure `memoryStatusChecks` (chat-ui calls it too). */
9
+ export declare function checkMemoryHealth(status: MemoryStatus): DoctorCheck[];
7
10
  export declare function checkWorkspaceRoot(workspaceRoot: string): Promise<DoctorCheck>;
8
11
  export declare function checkDataDirWritable(backend: FsBackend, dataDir: string): Promise<DoctorCheck>;
@@ -0,0 +1,89 @@
1
+ import { ChatMessage, ILLMClient, MemoryAdapter, TokenUsage } from '@buildaharness/runtime';
2
+ import { UserFact } from './fact-extraction.js';
3
+ /**
4
+ * Episodic tier (M3 of the agent memory framework plan): one rolling handoff digest per conversation,
5
+ * kept apart from durable facts. Never injected into the prompt; read by the recall tool.
6
+ *
7
+ * STORAGE CONTRACT (the recall tool codes against this): a digest lives at `episodic:<digestId>` as a
8
+ * `SessionDigest`. `digestId` is `<sessionId>:<conversationId>` because a CLI/chat-ui session id is
9
+ * reused across `/new`, so a bare session id would overwrite the previous conversation's digest.
10
+ * The id of every live digest is listed in `episodic-index` (the MemoryAdapter has no key listing).
11
+ */
12
+ export interface SessionDigest {
13
+ /** The digest id (the key suffix), not necessarily the raw session id: `<sessionId>:<conversationId>`. */
14
+ sessionId: string;
15
+ /** ISO time of the last write (the digest is rolling; retention counts from here). */
16
+ createdAt: string;
17
+ oneLine: string;
18
+ objective: string;
19
+ done: string[];
20
+ decisions: string[];
21
+ openItems: string[];
22
+ nextStep: string;
23
+ /** Set when the judge found the digest instruction-shaped. It is still untrusted context; the recall tool wraps it either way. */
24
+ flagged?: boolean;
25
+ }
26
+ export declare const EPISODIC_KEY_PREFIX = "episodic:";
27
+ export declare const EPISODIC_INDEX_KEY = "episodic-index";
28
+ /** D4: start at 90 days, calibrate. `AUDIT_EPISODIC_RETENTION_DAYS` overrides. */
29
+ export declare const DEFAULT_RETENTION_DAYS = 90;
30
+ /** `AUDIT_EPISODIC_DIGEST`: the digest writer and the pre-compaction flush. Default OFF; `1/true/on/yes/enabled` enables. Read where the writer is invoked (assistant.ts). */
31
+ export declare function episodicDigestEnabled(env?: Record<string, string | undefined>): boolean;
32
+ export declare function episodicRetentionDays(env?: Record<string, string | undefined>): number;
33
+ /** Read side, and the owner of the digest keys. The recall tool builds one of these over the same MemoryAdapter. */
34
+ export declare class DigestStore {
35
+ private readonly memory;
36
+ constructor(memory: MemoryAdapter);
37
+ private ids;
38
+ /** One digest by id, or undefined when absent or past retention. Never writes. */
39
+ getDigest(id: string): Promise<SessionDigest | undefined>;
40
+ /** The most recent `limit` digests (default 10), newest first, past-retention ones excluded. Never writes. */
41
+ listDigests(limit?: number): Promise<SessionDigest[]>;
42
+ /** Writes one digest and prunes past-retention ones (D4). The only place episodic keys are written. */
43
+ put(digest: SessionDigest): Promise<void>;
44
+ pruneExpired(): Promise<number>;
45
+ /** `/memory forget digest <id>` / `/memory forget digests` (no id). Returns how many were removed, expired ones included. */
46
+ forget(id?: string): Promise<number>;
47
+ /** Every stored digest, unbounded and ignoring retention, for `/memory export`. */
48
+ exportAll(): Promise<SessionDigest[]>;
49
+ }
50
+ export declare function listDigests(memory: MemoryAdapter, limit?: number): Promise<SessionDigest[]>;
51
+ export declare function getDigest(memory: MemoryAdapter, id: string): Promise<SessionDigest | undefined>;
52
+ /** The id of `sessionId`'s current conversation, minted on first need and cleared by `endConversation` (after the final digest). */
53
+ export declare function conversationDigestId(memory: MemoryAdapter, sessionId: string): Promise<string>;
54
+ export declare function endConversation(memory: MemoryAdapter, sessionId: string): Promise<void>;
55
+ /** The marker scripted-llm-client.ts's SIDE_CALL_MARKERS keys on. */
56
+ export declare const DIGEST_SYSTEM_MARKER = "You write a session handoff digest";
57
+ type DigestBody = Omit<SessionDigest, 'sessionId' | 'createdAt' | 'flagged'>;
58
+ export interface DigestCallResult {
59
+ body: DigestBody;
60
+ flagged: boolean;
61
+ facts: UserFact[];
62
+ }
63
+ /**
64
+ * One bounded call. Returns null on any error, unparseable output, empty digest, or a MISSING secret/instruction
65
+ * judgement — this write fails closed (a missed secret would reach disk), the one deliberate exception to "side
66
+ * calls fail open"; the consequence of null is only that no digest is written. The whole digest goes through
67
+ * `admitCandidate` with the gate forced on (independent of AUDIT_MEMORY_WRITE_GATE): a flagged secret is
68
+ * replaced by the model's `redactedDigest` (an unusable redaction drops the digest).
69
+ */
70
+ export declare function callDigest(llm: ILLMClient, input: {
71
+ messages: ChatMessage[];
72
+ prior?: SessionDigest;
73
+ extractFacts: boolean;
74
+ digestId: string;
75
+ sessionId: string;
76
+ }, model?: string, onUsage?: (usage: TokenUsage) => void): Promise<DigestCallResult | null>;
77
+ /**
78
+ * Writes/refreshes the conversation's digest from `messages`, folding in whatever digest already exists. Fails open for
79
+ * the caller (returns null, writes nothing, no partial entry). Never touches DURABLE_FACTS_KEY.
80
+ */
81
+ export declare function writeDigest(memory: MemoryAdapter, llm: ILLMClient, args: {
82
+ sessionId: string;
83
+ messages: ChatMessage[];
84
+ extractFacts: boolean;
85
+ }, model?: string, onUsage?: (usage: TokenUsage) => void): Promise<{
86
+ digest: SessionDigest;
87
+ facts: UserFact[];
88
+ } | null>;
89
+ export {};
@@ -70,6 +70,37 @@ export interface UserFact {
70
70
  * project is currently active.
71
71
  */
72
72
  project?: string;
73
+ /**
74
+ * M1 (agent memory framework): a model-chosen stable key for a fact naming a single-valued
75
+ * attribute ("home_city", "preferred_editor"). A new fact with the same key replaces the old
76
+ * one (see `MemoryService.recordFacts()`). Never derived from wording in code; undefined for
77
+ * a fact that names no single-valued attribute, which keeps today's accumulate behaviour.
78
+ */
79
+ key?: string;
80
+ /** M1: set on the replacing fact — the text of the entry it retired. */
81
+ supersedes?: string;
82
+ /** M1: set on an entry moved to `facts:retired` when a keyed fact replaced it. */
83
+ retiredAt?: string;
84
+ /** M1 usage fields, updated lazily (batched, flushed from `recordFacts()`) when the fact is rendered into a prompt. */
85
+ injectedCount?: number;
86
+ lastInjectedAt?: string;
87
+ /** M2: where the claim came from. Undefined = `user`. A non-`user` origin can only ever be episodic (see `tierForFact`). */
88
+ origin?: FactOrigin;
89
+ /** M2: the user's own supporting words for a `model_inferred` claim, returned by the classifier. Stored for audit/`/memory` display, never string-matched. */
90
+ evidence?: string;
91
+ /** M2: set on a pending entry the write gate's semantic injection judgement flagged as instruction-shaped; never auto-promoted. */
92
+ flagged?: boolean;
93
+ /** M2: transient classifier judgement carried from `buildTurnFacts` to the write gate; stripped before anything is persisted. */
94
+ judgement?: CandidateJudgement;
95
+ }
96
+ /** M2: structured provenance next to `FactSource`; only `user` may reach a Knowledge tier. */
97
+ export type FactOrigin = 'user' | 'agent' | 'tool' | 'web';
98
+ /** M2: the classifier's per-fact judgement. Either field undefined means the judgement is missing (the gate fails closed). */
99
+ export interface CandidateJudgement {
100
+ containsSecret?: boolean;
101
+ /** Fact text with the secret removed; empty/undefined when the whole claim is the secret. */
102
+ redactedText?: string;
103
+ looksLikeInstruction?: boolean;
73
104
  }
74
105
  /** Every fact has a certainty for `/memory` display purposes, even one with no `confidence` gradient (user_asserted/observed/externally_verified) — those are exactly as certain as a stated fact gets, so they display as 'high' rather than blank. Never used for promotion/tier logic, which still reads `fact.confidence` directly and treats undefined as "no LLM confidence signal" (see that field's own doc comment) — this is purely a rendering convenience. */
75
106
  export declare function certaintyLabel(fact: UserFact): FactConfidence;
@@ -126,6 +157,14 @@ export declare const TIER_RULES: Record<MemoryTier, TierRule>;
126
157
  * `procedural`/`commitment` are never returned (see `TIER_RULES`'s doc comment).
127
158
  */
128
159
  export declare function tierForFact(fact: UserFact): MemoryTier;
160
+ /**
161
+ * The reliability the harness's world model should give this fact's belief — 'HIGH' (confidence
162
+ * 1.0, the >= 0.8 bar the change reviewer and contradiction severity read) for a fact the user
163
+ * stated directly or that has been externally verified, and for a `model_inferred` one that
164
+ * cleared the same durable + high-confidence bar `tierForFact` uses to treat it as Knowledge;
165
+ * everything else stays 'MEDIUM' (0.5), so an unconfirmed guess never reads as an established fact.
166
+ */
167
+ export declare function factReliability(fact: UserFact): 'HIGH' | 'MEDIUM';
129
168
  /** Whether `tier` counts as the Knowledge tier for contradiction-detection purposes — see harness-bridge.ts's factExtractor and TIER_RULES.*.contradictionChecked. */
130
169
  export declare function isKnowledgeTier(tier: MemoryTier): boolean;
131
170
  /**