@buildaharness/aielia 0.3.4 → 0.3.6

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 (113) hide show
  1. package/README.md +156 -35
  2. package/dist/PlanGraphPane.d.ts +19 -0
  3. package/dist/_memory-core-generated.d.ts +1764 -0
  4. package/dist/action-approval-service.d.ts +34 -2
  5. package/dist/action-record.d.ts +13 -0
  6. package/dist/activity-log.d.ts +4 -0
  7. package/dist/agent-loop.d.ts +106 -9
  8. package/dist/ambiguity-guard-flag.d.ts +12 -0
  9. package/dist/ambiguity-guard.d.ts +20 -0
  10. package/dist/answer-claim.d.ts +13 -1
  11. package/dist/ask-clarification-service.d.ts +12 -1
  12. package/dist/ask-response-format.d.ts +6 -0
  13. package/dist/assistant-session.d.ts +32 -2
  14. package/dist/assistant-source.d.ts +7 -0
  15. package/dist/assistant-types.d.ts +20 -2
  16. package/dist/assistant.d.ts +160 -6
  17. package/dist/benchmark-injected-failure.d.ts +6 -0
  18. package/dist/bin-BYRl4xXM.js +4985 -0
  19. package/dist/bin-BYRl4xXM.js.map +1 -0
  20. package/dist/cli-config.d.ts +3 -0
  21. package/dist/cli-session.d.ts +19 -5
  22. package/dist/cli.d.ts +26 -2
  23. package/dist/cli.js +1 -1
  24. package/dist/code-plan-templates.d.ts +21 -0
  25. package/dist/config.d.ts +31 -0
  26. package/dist/constraint-check.d.ts +35 -0
  27. package/dist/contradiction-checker.d.ts +7 -0
  28. package/dist/debug-log.d.ts +10 -1
  29. package/dist/decomposed-answer.d.ts +13 -0
  30. package/dist/decomposition-classifier.d.ts +2 -0
  31. package/dist/doctor-checks.d.ts +3 -0
  32. package/dist/email.d.ts +5 -3
  33. package/dist/episodic-digest.d.ts +89 -0
  34. package/dist/fact-extraction.d.ts +39 -0
  35. package/dist/file-tools-mcp-server.mjs +288 -37
  36. package/dist/file-tools.d.ts +6 -0
  37. package/dist/first-run.d.ts +0 -2
  38. package/dist/git-exclude.d.ts +9 -0
  39. package/dist/goal-graph-store.d.ts +18 -18
  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 +21 -2
  44. package/dist/index.js +166 -94
  45. package/dist/index.js.map +1 -1
  46. package/dist/ink-select-prompt.d.ts +2 -0
  47. package/dist/ink-test-render.d.ts +2 -0
  48. package/dist/layer-policy-flag.d.ts +13 -0
  49. package/dist/layer-policy-wiring.d.ts +77 -0
  50. package/dist/layer-settings.d.ts +49 -0
  51. package/dist/layer-telemetry.d.ts +2 -0
  52. package/dist/lexical/lexical-mode.d.ts +29 -0
  53. package/dist/memory-consolidation.d.ts +80 -0
  54. package/dist/memory-governance.d.ts +30 -0
  55. package/dist/memory-reviewer.d.ts +114 -0
  56. package/dist/memory-service.d.ts +356 -9
  57. package/dist/model-json.d.ts +17 -0
  58. package/dist/next-step-context.d.ts +51 -0
  59. package/dist/next-step-proposer.d.ts +10 -2
  60. package/dist/plan-drafting-service.d.ts +3 -0
  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 +12 -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-CPbB5y_V.js → provider-setup-CV3S_TAS.js} +10792 -5731
  82. package/dist/provider-setup-CV3S_TAS.js.map +1 -0
  83. package/dist/recall-tool.d.ts +43 -0
  84. package/dist/recovery-note.d.ts +22 -0
  85. package/dist/reply-audit.d.ts +67 -0
  86. package/dist/response-service.d.ts +11 -1
  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/scripted-llm-client.d.ts +26 -1
  91. package/dist/semantic-compaction.d.ts +15 -0
  92. package/dist/semantic-hypotheses.d.ts +40 -0
  93. package/dist/side-call-policy.d.ts +30 -0
  94. package/dist/source-reliability.d.ts +71 -0
  95. package/dist/task-completion-check.d.ts +24 -0
  96. package/dist/tool-control-plane.d.ts +33 -1
  97. package/dist/tool-effect-class.d.ts +9 -0
  98. package/dist/tool-step.d.ts +6 -0
  99. package/dist/trace-events.d.ts +1 -1
  100. package/dist/transcript-compaction.d.ts +9 -0
  101. package/dist/trust-tagging.d.ts +2 -0
  102. package/dist/{tui-app-DeyZ4Pr9.js → tui-app-CJq6YaiP.js} +436 -21
  103. package/dist/tui-app-CJq6YaiP.js.map +1 -0
  104. package/dist/tui-app.d.ts +73 -0
  105. package/dist/turn-abort.d.ts +18 -0
  106. package/dist/turn-intent-classifier.d.ts +81 -14
  107. package/dist/turn-interpreter.d.ts +27 -3
  108. package/dist/version.d.ts +1 -1
  109. package/package.json +6 -6
  110. package/dist/bin-CdVTAe7j.js +0 -2209
  111. package/dist/bin-CdVTAe7j.js.map +0 -1
  112. package/dist/provider-setup-CPbB5y_V.js.map +0 -1
  113. package/dist/tui-app-DeyZ4Pr9.js.map +0 -1
package/README.md CHANGED
@@ -7,6 +7,30 @@ what it may do, what it may believe, and what a job may cost — light enough fo
7
7
  "what's the weather", and it stages anything that can't be undone (`write_file`,
8
8
  `run_shell_command`, `send_email`) for your approval. Website: https://myaielia.com.
9
9
 
10
+ ## Install
11
+
12
+ ```bash
13
+ # macOS / Linux — standalone binary, installed to ~/.local/bin (override with AIELIA_INSTALL_DIR)
14
+ curl -fsSL https://myaielia.com/install.sh | sh
15
+ curl -fsSL https://myaielia.com/install.sh | sh -s -- --force # replace a non-aielia file at the target path
16
+
17
+ # Windows (PowerShell) — installs to %LOCALAPPDATA%\Aielia\aielia.exe and adds it to your user PATH
18
+ irm https://myaielia.com/install.ps1 | iex # set $env:AIELIA_FORCE = '1' first to force
19
+
20
+ # or from npm
21
+ npm install -g @buildaharness/aielia
22
+ ```
23
+
24
+ Both installers read the manifest at `https://myaielia.com/aielia-latest.json`, download
25
+ the binary for your platform over HTTPS only, verify its SHA-256 against the manifest and
26
+ install it; re-running upgrades in place, or says it is already up to date. They refuse
27
+ to overwrite an unrelated file (or a symlink, such as an npm-linked `aielia`) without
28
+ `--force`. `aielia update` does the same upgrade later (see "Updates and the update check"
29
+ below). On the first interactive launch with nothing configured, the CLI asks which
30
+ provider to use and for its API key (live-checked with a free read-only call) and saves
31
+ the choice to the persisted config; piped/scripted stdin skips this and keeps the
32
+ `proxy` default.
33
+
10
34
  ## Design
11
35
 
12
36
  The harness here is a governance and reliability control plane, not just a
@@ -142,7 +166,7 @@ contradiction detection considers it:
142
166
 
143
167
  | Tier | Answers | Allowed `FactSource` | Retention | Contradiction-checked | Where it lives |
144
168
  |---|---|---|---|---|---|
145
- | **Episodic** | "What was said or mused, unconfirmed?" | any | session | no | `facts:<sessionId>` — also every `model_inferred`/`observed` `UserFact` lands here regardless of its `durable` bit; an unconfirmed model guess is never Knowledge |
169
+ | **Episodic** | "What was said or mused, unconfirmed?" | any | session | no | `facts:<sessionId>`, plus `episodic:<sessionId>` session digests when `AUDIT_EPISODIC_DIGEST` is on (see below) — also every `model_inferred`/`observed` `UserFact` lands here regardless of its `durable` bit; an unconfirmed model guess is never Knowledge |
146
170
  | **Semantic** | "What's currently stated as true?" | `user_asserted`, `externally_verified` | durable (by tier policy) | yes | `facts:<sessionId>` or `facts:durable`, depending on the existing promotion policy (`durable` bit) — the tier a fact belongs to and whether it's actually promoted are tracked separately today |
147
171
  | **Identity** | "Who is this?" (name, "call me X") | `user_asserted` | durable | yes | `facts:durable` |
148
172
  | **Preference** | "What does the user want?" (stated preference) | `user_asserted` | durable | yes | `facts:durable`; a *configured* preference (backend, model, `enableShell`) is separately `AssistantConfig` (`config.ts`), not a `UserFact` at all |
@@ -158,6 +182,40 @@ the other half is that `isKnowledgeTier()` only returns true for `semantic`,
158
182
  `identity`, and `preference` — episodic entries, procedural weights, and
159
183
  commitments never enter contradiction detection as if they were beliefs.
160
184
 
185
+ **Episodic memory now has a store and a writer, behind a flag.** With
186
+ `AUDIT_EPISODIC_DIGEST` on (off by default; the cross-session scenario improved on 2 of 2 seeds, one scenario only), the assistant
187
+ writes one short handoff digest per session (objective, what was done,
188
+ decisions, open items, next step) at the session edge (`/new`, exit), never
189
+ inside a turn, to `episodic:<sessionId>`. A second use of the same writer runs
190
+ just before transcript compaction drops old messages, so facts in those
191
+ messages are queued for confirmation instead of being lost. Digests are kept
192
+ apart from durable facts and are never injected into the prompt: they reach the
193
+ model only through the read-only `recall_memory` tool (`AUDIT_RECALL_TOOL`, off
194
+ by default), which returns an index of recent digests and then one digest by id,
195
+ wrapped as untrusted context. The model picks which digest to open; there is no
196
+ keyword search. Digest text passes the same secret judgement as any other
197
+ memory write, and a digest can never populate a Knowledge tier (the extended
198
+ INV-16: a non-user origin is episodic only). Digests are kept for a retention
199
+ window (default 90 days, `AUDIT_EPISODIC_RETENTION_DAYS`) and are covered by
200
+ `/memory forget` and `/memory export`. This changes what `/new` keeps: durable
201
+ facts as before, plus digests when the flag is on. Evidence is one scenario and two seeds; it also adds one model call per session edge.
202
+
203
+ Related write-path controls: `AUDIT_MEMORY_BUDGETED_RENDER` is **on by default**
204
+ (character-budgeted, priority-ordered facts block with keyed supersession, so a
205
+ corrected fact replaces the old one; `=0` restores the old 20-fact slice; budget
206
+ `memoryBudgetChars`, default 4000). In a six-week corrections scenario it fixed a
207
+ stale-fact failure on 3 of 3 seeds. Off by default: `AUDIT_MEMORY_WRITE_GATE`
208
+ (semantic secret redaction and instruction-shaped-text flagging before a fact is
209
+ promoted; a missing judgement means the fact is not promoted; no outcome
210
+ difference was seen because the base model already resisted the planted secret),
211
+ `AUDIT_MEMORY_AUDIT_LOG` (`/memory history`, `/memory undo <seq>`),
212
+ `AUDIT_MEMORY_REVIEWER` (post-turn reviewer; its writes are staged for
213
+ `/memory confirm` unless `memoryWriteMode` is `auto`) and
214
+ `AUDIT_MEMORY_CONSOLIDATION` (staged merge and archive proposals; not measured as
215
+ an outcome). Known gaps: the model sometimes keys the same attribute two ways, so
216
+ a correction can miss the old fact, and a natural-language "forget that" request
217
+ is staged for approval rather than applied.
218
+
161
219
  The conversation transcript itself (`transcript:<sessionId>`, "what was
162
220
  said" verbatim) and a single turn's `AnswerClaim` (evidence vs. claim for one
163
221
  reply, see below) sit outside this tier system — they're the raw material a
@@ -273,8 +331,9 @@ await assistant.turn('Summarize this into notes.md', { approved: true, pendingAc
273
331
 
274
332
  Declining (`{ approved: false, pendingActionId }`) discards the staged record
275
333
  without writing. A pending action left over from a crashed/abandoned turn sits
276
- in `.pending-actions/` indefinitely — harmless (never applied without an explicit
277
- `approved: true` with the matching ID) but not currently auto-swept. This same
334
+ in `.pending-actions/` — harmless (never applied without an explicit
335
+ `approved: true` with the matching ID), and swept on the next startup unless a
336
+ session still has a resumable checkpoint (`sweepAbandonedPendingActionsOnStartup`). This same
278
337
  staging record shape (a `kind` discriminator) is shared with `run_shell_command`
279
338
  (see "Shell access via tools" below) and `send_email`.
280
339
 
@@ -314,7 +373,7 @@ Both backends enforce the same "never write inline" rule, by different
314
373
  mechanisms:
315
374
 
316
375
  - **Proxy/Anthropic backend** (`LLMClient`): `PersonalAssistant`'s tool loop
317
- (capped at 5 iterations) calls `callChatStructured` directly, executes
376
+ (bounded by `maxSteps`, default 15, minus a few steps held back for recovery) calls `callChatStructured` directly, executes
318
377
  non-mutating tool calls for real, and intercepts `write_file` itself before
319
378
  it ever reaches `file-tools.ts`'s staging code.
320
379
  - **Claude CLI backend** (`ClaudeCliLLMClient`): Claude Code's own agentic loop
@@ -331,8 +390,10 @@ mechanisms:
331
390
 
332
391
  v1 is deliberately read/list/write only — no delete/move tool (higher
333
392
  consequence than a write, no "undo" via re-approval). One workspace root per
334
- assistant instance; no multi-root or per-request override. chat-ui doesn't have
335
- a write-approval UI yet — file tools are CLI/desktop-only for now.
393
+ assistant instance; no multi-root or per-request override. File tools need a
394
+ real workspace directory, so they are CLI/desktop-only; chat-ui's approval card
395
+ (with the exact change shown) is what resolves a staged write or shell command
396
+ on the desktop build.
336
397
 
337
398
  ## Clarifying questions
338
399
 
@@ -370,8 +431,8 @@ follow-up `needs_clarification` batch if it's still unresolved once the first ba
370
431
  folded in, resolved through this exact same path.
371
432
 
372
433
  Two kinds of sites can raise a `questions` batch: the Trajectory Supervisor's `ASK_USER` directive
373
- (see ADR-005 — currently inert by default, since
374
- `HARNESS_TRAJECTORY_SUPERVISOR` stays off) and a handful of deterministic sites with a genuinely
434
+ (see ADR-005 — the supervisor is on by default in Aielia;
435
+ `HARNESS_TRAJECTORY_SUPERVISOR=0` turns it off) and a handful of deterministic sites with a genuinely
375
436
  enumerable option set, e.g. a batch-research budget running out ("continue with N more steps" /
376
437
  "stop and summarize" / "let me clarify the goal"). A site with no discrete option set keeps
377
438
  today's plain `missing_info` halt unchanged — this mechanism never forces a multiple-choice
@@ -448,6 +509,9 @@ without ever exposing the filesystem or shell.
448
509
 
449
510
  ## Batch research budget
450
511
 
512
+ > The list detector below is a lexical check (the `batch-list` family), **off by default**: set
513
+ > `ASSISTANT_LEXICAL_ON=batch-list` (or `ASSISTANT_LEXICAL_MODE=enabled`) to use batch research.
514
+
451
515
  The flat `maxSteps` cap that governs an ordinary chat turn (see below) doesn't
452
516
  know the difference between a one-question turn and a turn asking for the same
453
517
  lookup across many items — a 7-item batch and a 1-item question get the same
@@ -705,18 +769,9 @@ non-functional `web_search`. The active state is shown in the startup banner
705
769
  running session with `/config set` instead of an env var — see
706
770
  "Configuration" below.
707
771
 
708
- Set `ASSISTANT_LLM_BACKEND=claude-cli` to skip the proxy entirely and run turns
709
- through a local `claude -p` subprocess instead, using your already-authenticated
710
- Claude Code CLI session rather than an API key (`CLAUDE_PATH` overrides the
711
- `claude` binary path if it's not on `PATH`):
712
-
713
- ```bash
714
- ASSISTANT_LLM_BACKEND=claude-cli npm run cli --workspace=packages/aielia
715
- ```
716
-
717
- Or skip both the proxy and claude-cli and call a provider directly with your
718
- own API key — `ASSISTANT_LLM_BACKEND=anthropic|openai|openrouter` plus
719
- `ASSISTANT_API_KEY`:
772
+ `ASSISTANT_LLM_BACKEND` also accepts `claude-cli` (shells out to the `claude` CLI already on
773
+ your PATH, no API key needed; CLI and desktop only). Skip the proxy and call a provider directly with your own API
774
+ key — `ASSISTANT_LLM_BACKEND=anthropic|openai|openrouter` plus `ASSISTANT_API_KEY`:
720
775
 
721
776
  ```bash
722
777
  ASSISTANT_LLM_BACKEND=anthropic ASSISTANT_API_KEY=sk-ant-... \
@@ -734,7 +789,8 @@ These three go straight from this process to the provider's own API
734
789
  — no proxy deployment needed, but unlike `authToken` (a self-hosted proxy's
735
790
  own bearer token), `ASSISTANT_API_KEY`/`apiKey` is a *real* provider key.
736
791
  It's stored the same way as every other secret field here — plaintext in
737
- `config.json`, not an OS keychain — so treat that file accordingly. When
792
+ `config.json` (written with mode `0600`, in a `0700` directory), not an OS
793
+ keychain — so treat that file accordingly. When
738
794
  `ASSISTANT_MODEL`/`/config set model` isn't set, each backend falls back to the
739
795
  current-generation default id exported from `@buildaharness/runtime`'s
740
796
  `model-defaults.ts` (`openai` → `gpt-5-mini`, `openrouter` →
@@ -767,9 +823,13 @@ you> /config
767
823
  model (not set)
768
824
  enableWeb false
769
825
  braveApiKey (not set)
826
+ webBackend direct
770
827
  enableShell false
771
828
  shellTimeoutMs (not set)
829
+ shellNetworkAllowlist (not set)
772
830
  workspaceRoot (not set)
831
+ enableEmail false
832
+ ... (one line per key; the full list is `CONFIG_KEYS` in config.ts)
773
833
  dangerouslySkipPermissions false
774
834
 
775
835
  you> /config set enableWeb true
@@ -785,7 +845,9 @@ you> /config reset enableWeb
785
845
  ✓ Reset enableWeb to default
786
846
  ```
787
847
 
788
- - `/config` lists every field's current (resolved) value. A field currently
848
+ - `/config` lists every field's current (resolved) value; secret fields
849
+ (`authToken`, `apiKey`, `braveApiKey`, `resendApiKey`, `smtpPass`) print as
850
+ `********`, and credentials or query strings inside `proxyUrl` are masked. A field currently
789
851
  pinned by an env var shows `(env-pinned: VAR_NAME)` and cannot be changed
790
852
  with `/config set` — unset the env var first.
791
853
  - `/config set <key> <value>` validates the change (e.g. `enableWeb true`
@@ -798,15 +860,67 @@ you> /config reset enableWeb
798
860
 
799
861
  **Precedence**: env var > persisted config > built-in default, evaluated
800
862
  independently per field. Settings persist as plain JSON at
801
- `~/.buildaharness/personal-assistant/config.json` — like the rest of this
802
- package's persistence, it's a real file, not encrypted, so `authToken` and
803
- `braveApiKey` are stored in plaintext there. This is the same trust boundary
863
+ `~/.buildaharness/personal-assistant/config.json` (created `0600` inside a
864
+ `0700` directory) — like the rest of this
865
+ package's persistence, it's a real file, not encrypted, so `authToken`,
866
+ `apiKey`, `braveApiKey` and the email credentials are stored in plaintext there. This is the same trust boundary
804
867
  the repo's root `.env` already has, not a new one.
805
868
 
869
+ Settings beyond those above (each has an env var, a `/config set` key, and a built-in default;
870
+ mode flags take `enabled`/`disabled`):
871
+
872
+ | Key (env var) | Default | What it does |
873
+ |---|---|---|
874
+ | `webBackend` | `direct` | Browser build only (see chat-ui README); the CLI always calls Brave directly |
875
+ | `shellNetworkAllowlist` (`ASSISTANT_SHELL_NETWORK_ALLOWLIST`) | none (deny all) | Hosts an approved shell command may reach |
876
+ | `enableEmail`, `emailProvider` (`resend`/`smtp`), `emailFrom`, `resendApiKey`, `smtpHost`/`smtpPort`/`smtpUser`/`smtpPass` (`ASSISTANT_ENABLE_EMAIL`, `ASSISTANT_EMAIL_*`, `ASSISTANT_RESEND_API_KEY`, `ASSISTANT_SMTP_*`) | off | Registers the staged `send_email` tool; `enableEmail` needs `emailFrom` and the provider's credentials |
877
+ | `sessionCostLimitUsd`, `sessionCallLimit` (`ASSISTANT_SESSION_COST_LIMIT_USD`, `ASSISTANT_SESSION_CALL_LIMIT`) | unbounded | Refuses a new turn once the cross-session estimated spend or completed-turn count reaches the ceiling; shown in `/status` and `/cost` |
878
+ | `memoryBudgetChars` (`ASSISTANT_MEMORY_BUDGET_CHARS`) | 4000 | Character budget for the facts block in the prompt |
879
+ | `memoryWriteMode` (`ASSISTANT_MEMORY_WRITE_MODE`) | `staged` | `auto`, `staged` or `user_only`; see "Memory tiers" |
880
+ | `oneLoopMode` (`ASSISTANT_ONE_LOOP`) | `enabled` | See "Design" |
881
+ | `askMode` (`ASSISTANT_ASK_MODE`) | `disabled` | See "Clarifying questions" |
882
+ | `planMode` (`ASSISTANT_PLAN_MODE`) | `legacy` | `gated` or `legacy` plan rollout |
883
+ | `ambiguityGuardMode` (`ASSISTANT_AMBIGUITY_GUARD`) | `disabled` | Ambiguity guard |
884
+ | `tuiMode` (`ASSISTANT_TUI`) | `disabled` | Full-screen terminal UI (Ink); only on a real TTY |
885
+ | `updateCheck` (`ASSISTANT_UPDATE_CHECK`) | `enabled` | Passive update check |
886
+ | `activeProject` (`ASSISTANT_ACTIVE_PROJECT`) | none | Project new project-scoped facts are tagged with (`/project`) |
887
+ | `goalGraphMode`, `goalGraphSuggestMode` (`ASSISTANT_GOAL_GRAPH`, `ASSISTANT_GOAL_GRAPH_SUGGEST`) | `enabled` in the CLI | Goal tree, steering and next-step options |
888
+ | `planGraphMode` (`ASSISTANT_PLAN_GRAPH`) | `disabled` | `/plan graph` |
889
+ | `layerPolicyMode` (`ASSISTANT_LAYER_POLICY`) | `static` | `static`, `shadow` or `adaptive` |
890
+ | `layers` | `{}` | Per-layer on/off, edited with `/layers`, not `/config set` |
891
+
892
+ `ASSISTANT_ACTIVITY_LOG=1` additionally appends a debug activity log to
893
+ `activity-log.jsonl` in the data directory (created `0600`). All CLI output has
894
+ terminal escape sequences stripped, so text echoed from a fetched page or file
895
+ cannot rewrite what an approval prompt shows.
896
+
897
+ ### Lexical checks (off by default)
898
+
899
+ Every lexical pass in this package (regex / keyword / phrase-list checks over natural-language text) sits
900
+ behind one switch, `lexicalMode`, and **every one is off by default** in the CLI, the browser tab and the
901
+ desktop app — the LLM-based checks do the work instead. The ten families (`fact-markers`, `coding-fact`,
902
+ `injection`, `enumeration`, `risk`, `task-cancel`, `plan-mode`, `batch-list`, `tool-yield`,
903
+ `template-keywords`) and what "off" means for each are listed in `src/lexical/lexical-mode.ts`.
904
+
905
+ - `ASSISTANT_LEXICAL_MODE=enabled` turns every family back on (the pre-2026-09-28 behaviour).
906
+ - `ASSISTANT_LEXICAL_ON=batch-list,task-cancel` turns only those families back on (`all` for every one).
907
+ - `ASSISTANT_LEXICAL_OFF=<families>` turns families off even under `enabled`; it wins over ON.
908
+
909
+ The mode is handed to `@buildaharness/harness`'s own lexical switch at the start of every run, so
910
+ `enabled` turns the harness's checks back on too (see that package's README).
911
+
912
+ Some families have no LLM replacement, so with them off the feature they drive does not run: batch
913
+ research (`batch-list`), matching "skip that step" to one step of an active plan (`task-cancel`), leaving
914
+ plan drafting by phrase (`plan-mode`; `/plan` still works), and the batch sub-loop's lexical dead-end
915
+ detection (`tool-yield`; the web tool's own "No results found." still counts). With `risk` off, a durable
916
+ plan step saved without its own risk level is treated as HIGH.
917
+
806
918
  ## REPL commands
807
919
 
808
- Type `/help` inside a running CLI session for this list. All of them read or
809
- change local session/config state — none of them make an LLM call themselves.
920
+ Type `/help` inside a running CLI session for this list. Almost all of them read or
921
+ change local session/config state without an LLM call; the exceptions are `/plan sketch`
922
+ and `/memory consolidate`, each of which makes one. `/exit` (also `/quit`, `exit`, `quit`)
923
+ ends the session.
810
924
 
811
925
  | Command | What it does |
812
926
  |---|---|
@@ -815,15 +929,28 @@ change local session/config state — none of them make an LLM call themselves.
815
929
  | `/status` | Show the resolved config (model, backend, workspace, enabled capabilities — same as the startup banner) plus this session's transcript length and whether a plan is active |
816
930
  | `/export [file]` | Save this session's transcript to a markdown file (default: `assistant-transcript-<timestamp>.md` in the current directory) |
817
931
  | `/undo` | Remove the last exchange from conversation history — a completed turn drops both the user message and the reply; a turn still awaiting approval drops just the pending message. Only affects what the model remembers: a real `write_file`/`run_shell_command` effect from that turn is **not** reversed |
818
- | `/memory` | Show facts learned about you, reminders created so far, and the learning-layer `ExperienceStore`'s real content — every strategy weight, plus the 20 most recently learned decompositions/recovery sequences (newest first), not just counts |
932
+ | `/undo-action [id]` | List the real filesystem effects of approved actions that can still be reverted (see `/status`'s undo-log line), or stage a revert of one for your approval |
933
+ | `/memory` | Show facts learned about you, pending-confirmation guesses, reminders created so far, and the learning-layer `ExperienceStore`'s real content — every strategy weight, plus the 20 most recently learned decompositions/recovery sequences (newest first), not just counts |
934
+ | `/memory confirm <n\|category>` / `/memory reject <n\|category>` | Promote a pending-confirmation guess (or a whole category) to durable memory, or discard it |
935
+ | `/memory forget <n> [erase]` | Remove a learned fact by its number in "Facts I know". It stays restorable through the audit log (`/memory undo`) unless you add `erase`, which scrubs its text from the log too. `/memory forget digest [id]` erases one stored session digest, or all of them |
936
+ | `/memory history` / `/memory undo <seq>` | Show recent memory changes with their sequence numbers, and restore one change's pre-image (needs `AUDIT_MEMORY_AUDIT_LOG`) |
937
+ | `/memory status` | Store size vs budget, pending count, write mode, last consolidation |
938
+ | `/memory archive [restore <n> \| forget <n>]` | List facts set aside or replaced by a newer statement; restore one, or erase one for good |
939
+ | `/memory consolidate [accept\|dismiss <n>]` | Propose merging or retiring overlapping facts; nothing changes until you accept (needs `AUDIT_MEMORY_CONSOLIDATION`) |
940
+ | `/memory off` / `/memory on` | Stop or resume all memory writes for this install; existing facts stay readable and removable |
819
941
  | `/memory export [file]` | Write the full, unbounded `ExperienceStore` contents (every strategy weight/decomposition/recovery sequence, not the 20-entry preview `/memory` prints) plus facts/reminders to a JSON file (default: `assistant-memory-<timestamp>.json`). Read-only: there's no matching import path, so exported data can't be hand-edited and loaded back in |
820
942
  | `/search <query>` | Ranked search over past messages, across every session in this install — a hit is the one message that matched, not the whole session transcript around it. Read-only: never an LLM call or network request. Scoring is tokenized/graduated, not exact-substring-only (see "Conversation history" above), and a query matching nothing returns an explicit "no results" line |
943
+ | `/project [name]` | Show or switch the project new project-scoped facts are tagged with; `/project clear` reverts to the workspace default |
821
944
  | `/model [name]` | Show the active model, or switch it — a thin alias over `/config set model <name>` (see "Configuration" above); rejected the same way if `model` is pinned by `ASSISTANT_MODEL` |
822
945
  | `/cost` | Show token usage for the last turn and the running session total |
823
946
  | `/doctor` | Check proxy reachability (proxy backend) or the `claude` binary (claude-cli backend), plus workspace root and data dir health — no dedicated check yet for the anthropic/openai/openrouter backends, only the backend-agnostic checks run for those |
824
947
  | `/why` | Explain the harness path the last turn took (verification confidence + node sequence) |
948
+ | `/layers` | Show all 11 harness layers: fired or skipped, and why, for the last turn. `/layers settings` lists the reasoning layers with their cost/evidence; `/layers on\|off\|reset <id>` changes a layer's persisted on/off choice (an operator-set `AUDIT_*` env flag wins and shows as pinned) |
825
949
  | `/sources` | List files/URLs the last turn actually consulted |
826
950
  | `/plan` | Show the active structured plan's task status |
951
+ | `/plan sketch <request>` | One-shot, advisory plan sketch: no plan record, nothing staged, cannot execute |
952
+ | `/goals` | Review every known goal thread this session: status, tasks and visibility (freshly computed, carried over, done, suggested) |
953
+ | `/plan graph [thread-id]` | Draw the active plan as a read-only dependency graph (needs `planGraphMode` enabled; full-screen pane in the terminal UI with arrows/Tab/Enter/q, one static render in the plain REPL). Glyphs, mapping and keys: `docs/plan-visualization.md` |
827
954
  | `/checkpoint [clear]` | Inspect a stuck in-progress harness checkpoint (step, node, failed-resume count so far), or `/checkpoint clear` to discard it — see "Recovering a stuck checkpoint" above. Scoped to just the checkpoint: unlike `/clear`, transcript/facts/plan are untouched |
828
955
 
829
956
  ### `/cost` and real vs. estimated dollar figures
@@ -831,13 +958,7 @@ change local session/config state — none of them make an LLM call themselves.
831
958
  Token counts are always real, on every backend. The dollar figure attached to
832
959
  them is not always the same kind of number:
833
960
 
834
- - **claude-cli backend**: `costUsd` comes straight from `claude
835
- --output-format json`'s own `total_cost_usd` field — real Anthropic
836
- accounting. It may read `$0` if the underlying `claude` session is
837
- authenticated against a Pro/Max subscription rather than API billing, in
838
- which case `$0` does *not* mean "this turn was free" — `/cost`'s output
839
- says so explicitly.
840
- - **every other backend** (`proxy`, `anthropic`, `openai`, `openrouter`):
961
+ - **every backend** (`proxy`, `anthropic`, `openai`, `openrouter`):
841
962
  none of these compute a dollar cost themselves — only the raw token counts
842
963
  each provider's response already includes (`@buildaharness/proxy` is a thin
843
964
  pass-through, see `packages/proxy/src/forward.ts`; the three direct clients
@@ -0,0 +1,19 @@
1
+ import { VizNode } from './plan-viz/types.js';
2
+ /** Width at which the detail drawer docks to the right instead of being toggled. */
3
+ export declare const DRAWER_DOCK_COLUMNS = 120;
4
+ export interface PlanGraphPaneProps {
5
+ nodes: readonly VizNode[];
6
+ columns: number;
7
+ rows: number;
8
+ /** False while an approval prompt owns the keyboard: the pane stays drawn but ignores keys. */
9
+ active?: boolean;
10
+ color?: boolean;
11
+ ascii?: boolean;
12
+ onClose: () => void;
13
+ }
14
+ /**
15
+ * Read-only plan graph pane (plans/plan_visualization_plan.html, R2). Keys: arrows (or [ ]) move along edges and ranks, j/k and PgUp/PgDn scroll the detail drawer,
16
+ * Tab/Shift+Tab walk task order, Enter or d toggles the detail drawer below 120 columns, q or Esc closes.
17
+ * No key edits anything: the plan changes only through the approval gate.
18
+ */
19
+ export declare function PlanGraphPane({ nodes, columns, rows, active, color, ascii, onClose }: PlanGraphPaneProps): React.JSX.Element;