diff --git a/AGENTS.md b/AGENTS.md index 431fa7f2..b7c62d6f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -22,17 +22,10 @@ Turborepo + pnpm monorepo (`packages/shared`, `packages/llm`, `packages/core`, **cloud** (BYOK-central, Phase 2), and **managed** (Relavium's own keys via a metered egress gateway; engine stays local, Phase 2) — split across build phase 5 (managed inference) and phase 6 (cloud execution + portal); the engine is identical across all three (ADR-0012..0015). -**Status: Phase 1 in progress — milestone M1 (LLM seam proven) reached (PR #9, 2026-06-07);** -`@relavium/llm` (the seam + all three adapters) is landed and green. Phase 0 (M0) landed -the monorepo + `@relavium/shared` + CI + `@relavium/db`. The -[`@relavium/core` engine](docs/roadmap/phases/phase-1-engine-and-llm.md) has since landed the full -run-loop + node stack — parser, interpolation, DAG/`RunPlan`, the run loop + `RunEventBus`, the tool -registry, the `AgentRunner`, the six node-type handlers, the human gate, checkpoint/resume, node retry, -the expression sandbox, and the pre-egress budget governor — plus the agent-first `AgentSession` (1.V), -**completing milestone 1.m4** (PRs #13–#26); then the **1.U** end-to-end Node harness landed (**PR #27, -2026-06-16**), **reaching milestone M2** (the engine runs end-to-end) — the Phase-1 engine critical path is -complete, and Phase 2 (the CLI) is unblocked. The remaining Phase-1 work is the additive agent-first + -multimodal sub-spines; see [docs/roadmap/current.md](docs/roadmap/current.md) for live status. +**Status: Phase 1 is complete; Phase 2 (CLI) is feature-complete (v0.1.1 release cut, publish +pending); Phase 2.5 (CLI Consolidation) is complete (M2.5-4, 2026-07-08); Phase 2.6 +(Conversational Authoring and the First-Class CLI) is next up.** +See [docs/roadmap/current.md](docs/roadmap/current.md) for live status. ## The non-negotiable rules diff --git a/CLAUDE.md b/CLAUDE.md index e940bbc1..37fe48b3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -38,98 +38,25 @@ A run executes in one of **three execution modes** behind the one `LLMProvider` engine is identical across all three. See [ADR-0012](docs/decisions/0012-managed-inference-dual-mode.md) to [ADR-0015](docs/decisions/0015-managed-mode-data-handling-and-compliance.md) and [docs/architecture/managed-inference.md](docs/architecture/managed-inference.md). -**Status:** Phase 1 is **complete** (2026-06-21). The engine runs end-to-end behind -the `LLMProvider` seam: YAML→DAG parse, the multi-model run loop (agent + the six -non-agent node handlers) with live streaming, per-node-boundary checkpoint/resume -(cross-process), node retry, provider failover, cost governance, the human gate, and -multimodal media I/O (input, inline output, and the async generation job loop). All -three adapters (Anthropic, OpenAI/DeepSeek, Gemini) and the agent-first `AgentSession` -entry point (multi-turn sessions, persistence, export-to-workflow) are shipped. -**Phase 2 (CLI) is underway and milestone M3 is reached** — the CLI skeleton (2.A) and config -resolution (2.B) landed (PR #40), `relavium run` is wired to the engine -(2.D, the M3 keystone — PR #41), the `--json` CI machine-output contract -landed (2.F — PR #42, ADR-0049), the engine regression harness (2.K — PR #43) -completes M3, and durable run history landed (2.H — PR #44, ADR-0050); the provider/key -commands with OS-keychain storage landed (2.C — PR #45, behind ADR-0019 + ADR-0006); the `ink` -streaming TUI landed (2.E — PR #46, behind ADR-0047); the interactive human-gate prompt + the -out-of-band `relavium gate` cross-process resume landed (2.G — PR #47, behind ADR-0047), **fully closing -2.K's deferred gate-resume half**; and the read commands `list` / `logs` / `status` / `gate list` over durable -history landed (2.I — PR #48, no new ADR); and CLI packaging, distribution & install verification landed -(2.L — PR #49, behind ADR-0051) — the last gate-closing spine PR, **closing go/no-go #7 so all seven Phase-3 -exit criteria now hold and Phase 3 may start**; and media host-wiring landed (2.S — PR #52, behind ADR-0042–0046, -no new ADR), **the first additive lane done**; and the agent-first `relavium chat` REPL landed (2.M — PR #54, -2026-06-26, no new ADR — covered by ADR-0024/0047/0028/0050/0029; `read_media` **input** access split into a -dedicated, security-reviewed follow-up, so 2.M's REPL shipped without it); and the rest of the agent-first chat -family landed — `relavium chat-resume` (2.N), `chat-list` (2.O), `chat-export` + the in-REPL `/export` (2.P), -and `chat --json` + one-shot `agent run` (with `--fixture` cassette replay) (2.Q) — **PR #55, no new ADR**, -completing the agent-first CLI lane; and **2.R (the inbound MCP client) is ✅ Done** — the `@relavium/mcp` -foundation (the SDK-fenced package, the dependency-free JSON-Schema→Zod compiler, the fail-loud connect-all -manager) landed **PR #56**, and the host wiring (chat + run + one-shot `agent run`), the network transports -(`http`/`sse`/`websocket`) behind the SSRF floor, named secrets via the isolated `mcp-secret:*` keychain -namespace, the by-name `ref` form, and the real-spawn e2e landed **PR #57 (2026-06-27)** — behind -[ADR-0034](docs/decisions/0034-mcp-client-sdk-dependency.md) + [ADR-0052](docs/decisions/0052-inbound-mcp-client-package-lifecycle-registration.md) -+ [ADR-0053](docs/decisions/0053-mcp-network-transport-egress-security.md). 2.R was off the M3 critical path and -the Phase-3 go/no-go, so it adds capability without gating; and **2.J** (the `create`/`import`/`export` YAML -lifecycle) landed **PR #58 (2026-06-28)** — **with it every Phase-2 workstream is complete and the CLI is -feature-complete (published v0.1.1)**. **Phase 2.5 (CLI Consolidation) is now underway:** its spine's secure -base **2.5.A** — the shared `assembleToolEnv` tool-environment factory (host `fs`+`process` arms, the -advertise-filter, **EA1** `tool_unavailable`, **EA2** real failed-turn usage) wired into both the chat and -run paths — landed **PR #60 (2026-06-28)**, behind [ADR-0055](docs/decisions/0055-cli-host-capability-seam-tool-environment-factory.md), -**reaching milestone M2.5-1**; the `egress`/`os` arms + write-capable chat are deferred to 2.5.E/ADR-0057; and -**2.5.B** (the bare-invocation interactive Home — the TTY-gated bare `relavium` → a read-only management strip -over `history.db` that graduates into in-process chat, one ink tree + one SIGINT/SIGTERM lifecycle + bracketed -paste) landed **PR #61 (2026-06-29)**, behind [ADR-0054](docs/decisions/0054-cli-bare-invocation-interactive-home.md); -and **2.5.C** (the in-app command system — a curated **two-registry** model: the shell `COMMAND_MANIFEST` -(`commander` + `--help --json` + the `executeCommand` dispatch) vs the in-REPL `REPL_COMMANDS` (a filterable `/` -palette + slash commands in **both** chat and the bare Home, no command in both); `/help`, the `notice` channel, -`/workflows`, `/cost`, and `/doctor` — fast tier plus a `--deep` tier with a **redacted** provider-key probe + a -**read-only** MCP-status report that never connects/spawns (a security-review decision); the `name + args` slash -dispatch + a context-aware footer hint-bar) landed **PR #62 (2026-06-30)**, behind [ADR-0056](docs/decisions/0056-cli-in-app-slash-command-system-and-manifest.md); -and **2.5.E** (the reseat-less chat mode system — ask / plan / accept-edits / auto on `Shift+Tab` + `/mode` — the -fail-closed per-tool `confirmAction` floor (`[y]/[a]/[n]` + a session once/always cache), the `Esc` mid-turn abort -(EA7), and the host arms closing the 2.5.A deferral: a write-capable `fs` tier + **protected paths** refused in -every mode incl. `auto`, the SSRF-hardened `egress` arm shared with media, and the `os` arm as a governed action -class — wired live into `relavium chat`, one-shot `agent run`, and the Home, each activating the regime before its -first turn) is ✅ **Done (PR #63, 2026-07-03)**, behind [ADR-0057](docs/decisions/0057-cli-chat-modes-and-per-tool-approval.md) -(**Accepted** after the mandatory holistic security review); a same-PR chat-UX follow-up also landed — a host tool -EXECUTION failure on the interactive surface (a file-not-found READ) is fed back to the model to recover -(`recoverToolFailures`, scoped to IDEMPOTENT tools via a stamped `ToolExecutionError.recoverable`; a governed / -side-effecting failure stays fail-fast) plus a static secret-free `tool_failed` chat hint. **With 2.5.E the CLI -Consolidation spine (2.5.A/B/C/E) is complete.** The first experience-arm workstream **2.5.D** (chat input -ergonomics + the `@`-mention / `!`-shell **pending-attachment "chip" model**) is ✅ **Done (PR #64, 2026-07-03)**, -behind [ADR-0061](docs/decisions/0061-cli-input-layer-file-injection-and-shell-escape.md) (**Accepted** after a -two-round maintainer security review): the accepted file / command output is queued as a compact chip and expanded -into the shared UNTRUSTED nonce-fenced frame only at submit (byte-identical model context, a clean prompt); the -`[chat]` command allowlist resolves as a **coupled unit** (a project setting either the exact or glob array owns -the whole allowlist); and **2.5.F** (the ADR-0062 context-history commands) is ✅ **Done (PR #65, merged 2026-07-05)**, -behind [ADR-0062](docs/decisions/0062-context-compaction-and-cli-history-commands.md): **`/clear`** — a host-level -fresh-session lifecycle swap across `relavium chat`, `chat-resume`, and the in-Home chat, rebinding the same agent -under a new `sessionId` (TTY-interactive only, rejected under `--json`/plain per ADR-0049) — plus the two -compaction-moment UX polishes (a `session:compacting` "Summarizing…" event amending ADR-0036, and the footer -context-fullness indicator via a pure `@relavium/llm` `contextWindowForModel` helper), completing the ADR-0062 -compaction story alongside the earlier-landed model-summarised `/compact` + deterministic `/trim` + automatic -compaction. **2.5.G** (onboarding wizard + Home `/models` + the live model catalog) is ✅ **Done (PR #66, -2026-07-07)** — its scope expanded to **Option A** (a **live** model catalog + a complete model-pricing story that -governs cost) behind three ADRs ([ADR-0063](docs/decisions/0063-cli-config-write-contract.md) -config-write, [ADR-0064](docs/decisions/0064-live-model-catalog.md) live catalog, -[ADR-0065](docs/decisions/0065-provider-economics-and-extensibility.md) provider economics); all 12 steps landed, -plus the post-2.5.G model-UX follow-up ([ADR-0059](docs/decisions/0059-cli-mid-session-model-reseat.md) mid-session reseat + -[ADR-0066](docs/decisions/0066-normalized-reasoning-effort-control.md) reasoning-effort). With it **milestone -M2.5-2** is reached. The additive lane **2.5.H** (reasoning render + live-turn feedback + an actionable error taxonomy — behind -**EA6**, a dual-envelope `agent:reasoning` stream event that *amends* [ADR-0036](docs/decisions/0036-run-loop-substrate-event-bus-and-execution-host.md); -no new top-level ADR) is ✅ **Done (PR #67, 2026-07-07)**, reaching milestone **M2.5-3** with 2.5.E; and the consolidation -lanes **2.5.I** (regression harness + DB concurrency hardening: `loadFull` read-txn snapshot, `BEGIN IMMEDIATE` writes with a -deterministic `SQLITE_BUSY` retry, the concurrent chat+run + cassette-chain + perf-budget e2es, an advisory Windows CI lane) and -**2.5.J** (docs-debt: the accurate unencrypted-history posture per ADR-0050 + `NO_COLOR`/`FORCE_COLOR`/`--color` resolution) are -✅ **Done (2.5-close-out, 2026-07-08)** — **reaching milestone M2.5-4, so Phase 2.5 is complete** — landed with the doable-now -Batch A–E backlog (test-hardening; 2.5.H TUI polish; `AgentParseError` line/col; the ADR-0057 approval/security batch — `[c]` -reject-with-reason + non-TTY policy + SCOPE-denial recovery + Ctrl+T-in-approval + the Trojan-Source bidi floor, behind an -append-only ADR-0057 amendment; the profile-aware advertise-filter + the in-house `.gitignore` matcher), each implement → Opus → -Sonnet with a security-review pass on the approval batch. Deferred to a focused follow-up (both refactor the security-sensitive -`gate.ts` resume path): the `relavium budget resume` command + secret re-provide on gate resume; the session `{{ctx.*}}` -interpolation stays with the Proposed [ADR-0060](docs/decisions/0060-session-ctx-prompt-interpolation.md) (Phase-2.6). -For live status, per-PR history, milestone dates, and open obligations, see the canonical home -[docs/roadmap/current.md](docs/roadmap/current.md); [README.md](README.md) is the public overview. +**Status: Phase 1 is complete** (2026-06-21) — the engine runs end-to-end behind the +`LLMProvider` seam with all three adapters, `AgentSession`, multimodal I/O, and the full +run-loop. **Phase 2 (CLI) is feature-complete** (v0.1.1 release cut, publish pending): +`relavium chat` / `chat-resume` / `agent run`, `relavium run` with `--json`, the +inbound MCP client, and the full YAML lifecycle (`create`/`import`/`export`). +**Phase 2.5 (CLI Consolidation) is complete** (M2.5-4, PR #69, 2026-07-08): the bare-invocation +Home, the two-registry command system (`/` palette + shell commands), per-tool +approval/modes (ask/plan/accept-edits/auto), `@`-mention file injection, context +compaction, the onboarding wizard + live model catalog, reasoning rendering, and regression +hardening. **Phase 2.6 (Conversational Authoring and the First-Class CLI) is next up** +— a full-screen Home-managed CLI with conversational workflow authoring, management +browsers, competitor-breadth tools, settings/theming/`en`+`tr` localization, and the +run-ops resume follow-up. + +For live status, per-PR history, milestone dates, and open obligations, see the canonical +home [docs/roadmap/current.md](docs/roadmap/current.md); +[README.md](README.md) is the public overview; +[docs/roadmap/phases/phase-2.6-conversational-authoring.md](docs/roadmap/phases/phase-2.6-conversational-authoring.md) +has the full Phase 2.6 plan. ## Non-negotiable rules for AI agents @@ -202,7 +129,7 @@ These apply to every AI agent in this repo, regardless of model, runner, or tool ## Build, test, lint -Once Phase 0 lands the monorepo, all work goes through pnpm + Turborepo: +All work goes through pnpm + Turborepo: ```bash pnpm install diff --git a/README.md b/README.md index dc78c15f..13bf182a 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ machine unless you choose it. ## Why Relavium? -- **Four surfaces, one engine.** Desktop (Tauri), CLI, VS Code, and (Phase 2) the web +- **Four surfaces, one engine.** Desktop (Tauri), CLI, VS Code, and (planned) the web portal run the _identical_ pure-TypeScript engine. No Python sidecar, no single-tool lock-in — every surface is a first-class execution target. - **A chat-to-workflow continuum.** Other tools make every session ephemeral. Relavium @@ -23,16 +23,16 @@ machine unless you choose it. - **You own your LLM seam.** Multi-provider routing with fallback chains (`[claude → gpt-4o → gemini]`) is first-class through Relavium's own `@relavium/llm` abstraction over the official provider SDKs — no Vercel AI SDK, no LangChain. -- **Local-first by design.** Phase 1 ships with zero cloud and no account required; keys - live in your OS keychain. BYOK-local stays first-class forever; Phase 2 _adds_ optional - managed inference and cloud execution on the same engine. +- **Local-first by design.** Zero cloud, no account required. Your API keys live in your + OS keychain — never in plaintext, never in logs. Optional managed inference and cloud + execution are planned extensions on the same engine. - **Workflows are git objects.** `.relavium.yaml` files are diffable, reviewable, PR-able, and shareable — team infrastructure, not a proprietary JSON blob or buried Python. - **Multimodal, end-to-end.** Image / audio / video as input and output — including rule-driven media generation — flow through the same seam and engine. -## Highlights (Phase 1) +## Highlights - **Chat-to-workflow export** — turn a proven session into a reusable `.relavium.yaml`. - **Persistent, resumable agent sessions** — no run is ever ephemeral. @@ -41,7 +41,12 @@ machine unless you choose it. - **Checkpoint & resume** — pause and resume at any node boundary, even across processes. - **Human gates with timeout policy** — pause for an approve / reject / input decision. - **Per-node cost waterfall** — token and dollar attribution per node, per model. -- **Local-first, zero-install posture** — BYOK, OS keychain, no sign-up in Phase 1. +- **Interactive Home** — a bare `relavium` invocation opens a management center: start agents, + monitor runs, browse history, manage providers. +- **MCP client** — agents consume tools from external MCP servers over stdio, HTTP, SSE, and + WebSocket, with secrets in the keychain. +- **Live model catalog** — browse and switch models mid-session; per-model cost tracking. +- **Local-first, zero-install posture** — BYOK, OS keychain, no sign-up. ## Getting started @@ -76,7 +81,7 @@ flowchart TD D[Desktop · Tauri] C[CLI] V[VS Code extension] - P[Web portal · Phase 2] + P[Web portal · planned] end subgraph Engine["@relavium/core — one pure-TypeScript engine"] WE[WorkflowEngine] @@ -98,47 +103,39 @@ One engine, **two co-equal entry points** — `WorkflowEngine` (runs YAML pipeli `AgentSession` (runs conversational chat) — sharing the same tool registry, the same `@relavium/llm` multi-provider seam, and the same event bus. The engine has **zero platform-specific imports**, so the same source runs in the Tauri WebView, the VS Code -host, the Node CLI, and (Phase 2) a Bun server. Supporting packages: `@relavium/shared` -(Zod contracts), `@relavium/db` (Drizzle — SQLite locally, PostgreSQL in Phase 2), and +host, the Node CLI, and (planned) a Bun server. Supporting packages: `@relavium/shared` +(Zod contracts), `@relavium/db` (Drizzle — SQLite locally, PostgreSQL planned), and `@relavium/ui` (ReactFlow canvas + shadcn). See [docs/architecture/](docs/architecture/). ## Execution modes One engine, three modes behind the one `LLMProvider` seam: -- **Local (BYOK)** — Phase 1 default. Your keys, your machine, zero Relavium data. -- **Managed inference** — Phase 2, opt-in. Relavium's metered keys; the engine still runs - locally. -- **Cloud execution** — Phase 2. Run workflows on cloud workers for 24/7 automation and +- **Local (BYOK)** — the default. Your keys, your machine, zero Relavium data. +- **Managed inference** — planned. Relavium's metered keys; the engine still runs locally. +- **Cloud execution** — planned. Run workflows on cloud workers for 24/7 automation and team sharing. ## Status -**Phase 1 — Engine and LLM is complete** (2026-06-21): the engine runs end-to-end on -local-first BYOK — workflow parsing, DAG execution, live streaming, checkpoint/resume, -multi-provider failover, cost governance, and multimodal media I/O. **Phase 2 (the CLI) is -feature-complete** — the CLI skeleton, config resolution, `relavium run` (wired to the engine), its -`--json` CI machine-output contract, the engine regression harness, durable local run history, the -provider/key commands (API keys in the OS keychain), the live `ink` streaming TUI, the human-gate -prompt + out-of-band `relavium gate` resume, the read commands (`list` / `logs` / `status` / `gate list`) -over durable history, and the published, cross-OS-installable `npm i -g relavium` binary (packaging & -install verification) have landed (milestone **M3** reached; with packaging shipped, all seven Phase-3 -go/no-go exit criteria now hold). The first additive lanes have since landed too — media host-wiring -(a generative media-output fixture runs end-to-end on the CLI); the full agent-first chat family: the -`relavium chat` REPL plus session resume / list / export, a headless `chat --json` event stream, and a -one-shot `agent run` with deterministic offline `--fixture` replay (the first user-facing `AgentSession` -surface); and the **inbound MCP client** — agents consume external MCP servers' tools over stdio + the -`http`/`sse`/`websocket` network transports (behind an SSRF floor and isolated keychain-resolved secrets), -proven by a real-spawn end-to-end test; and the **YAML-authoring lifecycle** — `relavium create` (a wizard -scaffolding an agent or a minimal single-agent workflow), `import`, and a share-safe `export` (re-serialized -from the validated AST, no provider key by construction). With every in-phase workstream merged, the CLI is -cut as **v0.1.1** (the public npm publish is the pending final maintainer step). **Phase 2.5 (CLI -Consolidation) is complete** (milestone **M2.5-4**, 2026-07-08) — the conversational Home, the slash-command -system, reseat-less chat modes with per-tool approval, context compaction, a live model catalog with provider -economics, reasoning render, and the closing consolidation lanes (regression harness + DB-concurrency -hardening + documentation reconciliation) have all landed. For live status and the full roadmap, see -[docs/roadmap/current.md](docs/roadmap/current.md) and the -[roadmap](docs/roadmap/README.md). +The engine is complete and the CLI is feature-complete (cut as **v0.1.1**, npm publish pending). +What's shipped: + +- **Agent sessions** — `relavium chat` with persistent, resumable, exportable multi-turn sessions. +- **Workflow engine** — `relavium run` executes `.relavium.yaml` pipelines with live streaming, + checkpoint/resume, multi-model fallback, cost governance, and human gates. +- **Interactive Home** — the bare `relavium` invocation opens a management center with a + slash-command system, per-tool approval modes (ask/plan/accept-edits/auto), and context compaction. +- **MCP client** — agents consume tools from external MCP servers over stdio + network + transports, with secrets in the OS keychain. +- **Live model catalog** — onboard with a wizard, browse models, switch mid-session, track + per-model cost. +- **YAML authoring** — `relavium create` (wizard), `import`, and share-safe `export`. + +**Next: Phase 2.6 (Conversational Authoring and the First-Class CLI)** — a full-screen +Home-managed CLI with conversational workflow authoring, management browsers, competitor-breadth +tools, settings/theming, and `en`/`tr` localization. For live status and the full roadmap, see +[docs/roadmap/current.md](docs/roadmap/current.md) and the [roadmap](docs/roadmap/README.md). ## Documentation diff --git a/docs/roadmap/README.md b/docs/roadmap/README.md index 2292540a..65a3a128 100644 --- a/docs/roadmap/README.md +++ b/docs/roadmap/README.md @@ -89,7 +89,7 @@ flowchart LR P1E["Phase 1
engine + llm
CRITICAL PATH"] P2["Phase 2
CLI
(first engine consumer)"] P25["Phase 2.5
CLI consolidation
(Home + modes + root-cause)"] - P26["Phase 2.6
conversational authoring
(@relavium/authoring)"] + P26["Phase 2.6
conversational authoring
+ first-class CLI"] P3["Phase 3
desktop
(Tauri agent center)"] P4["Phase 4
VS Code
(standalone ext)"] P0 --> P1E --> P2 --> P25 --> P26 --> P3 --> P4 @@ -127,7 +127,7 @@ contract and a green CI gate. | **1** | Build the two engine packages everything depends on — `@relavium/llm` (the provider-agnostic `LLMProvider` seam, 3 adapters, fallback runner, cost) and `@relavium/core` (YAML→DAG, runner, checkpoint/resume, retry) — proven end-to-end from a Node harness. **The critical path.** | Phase 0 | A Node harness runs a 3-node workflow end-to-end with live streaming, canonical events, checkpoint/resume, retry, and provider fallback; all 3 adapters pass conformance (fixtures on PR, live nightly); no vendor type crosses the seam; zero platform imports. | [phases/phase-1-engine-and-llm.md](phases/phase-1-engine-and-llm.md) | | **2** | Ship the `relavium` CLI as the **first real engine consumer** and the engine's **regression harness** — `run / list / logs / gate / status`, an `ink` TUI, and a deterministic `--json` CI mode. | Phase 1 | A 3-node workflow runs via `relavium run` with the live TUI; durable history powers `list`/`logs`/`status`; human-gate pause/resume works interactively and via `relavium gate`; `--json` NDJSON is asserted by a green no-TTY CI regression harness; keys live only in the keychain (or documented fallback); `npm i -g relavium` verified on macOS/Linux/Windows. | [phases/phase-2-cli.md](phases/phase-2-cli.md) | | **2.5** | Consolidate the CLI into a conversation-first **Home** at the bare invocation, with a slash palette, reseat-less chat modes + per-tool approval, onboarding, and reasoning rendering — plus the bounded engine amendments and docs-debt Phase 2 deferred. Fixes the capability-gap root cause (advertised-but-unwired tools → bare `internal`). | Phase 2 | `relavium` (TTY) opens Home; `--json`/CI/non-TTY keep help + exit `0` (regression-harness proven); the built-in agent's tools actually work and `capability_unavailable` never surfaces as `internal`; modes + per-tool approval + mid-turn abort ship with no one-key bypass; the required ADRs are Accepted. | [phases/phase-2.5-cli-consolidation.md](phases/phase-2.5-cli-consolidation.md) | -| **2.6** | Promote the authoring core to a shared `@relavium/authoring` package, let a chat **author** standards-valid `.relavium.yaml` (model-generated, pre-flight-validated), switch models mid-session, and reach competitor-parity polish. Realises *"Start as an agent. Ship the workflow."* at the terminal. | Phase 2.5 | `@relavium/authoring` is the shared core (CLI consumes it; `create` runs the catalog-aware pre-flight); a conversational request yields a strict-valid workflow YAML; mid-session `/models` reseat works with the context-loss notice; `{{ctx.*}}` interpolation lands; the required ADRs are Accepted. | [phases/phase-2.6-conversational-authoring.md](phases/phase-2.6-conversational-authoring.md) | +| **2.6** | Make the CLI a **first-class, Home-centric product** *(re-scoped 2026-07-08)*: a full-screen Home from which everything is managed (providers/keys, models, MCP, settings, workflows, agents, gates), a three-level run drill-down over attributed durable history, competitor-breadth tools (edit/search/find/todo/ask-user/working web search), settings + theming + `en`/`tr` localization, onboarding v2 with the Relavium-account stub, the run-ops resume follow-up — plus the original spine: promote the authoring core to a shared `@relavium/authoring` package and let a chat **author** standards-valid `.relavium.yaml`. Realizes *"Start as an agent. Ship the workflow. Own every run."* at the terminal. | Phase 2.5 | `relavium` (TTY) opens a full-screen Home managing everything — no routine task needs a shell subcommand, and the ADR-0049 `--json`/CI contract is byte-unbroken; a conversational request yields a strict-valid, catalog-validated workflow YAML; the toolbelt ships mode-gated behind a security-reviewed render contract; `{{ctx.*}}` lands; run history is attributed + drillable (list → run → node); `en`+`tr` ship with CI key-parity; the required ADRs are Accepted. | [phases/phase-2.6-conversational-authoring.md](phases/phase-2.6-conversational-authoring.md) | | **3** | Ship the Tauri v2 desktop **agent-management center**: ReactFlow canvas, live run monitoring, local SQLite history, OS keychain — offline, no account. **NOT an IDE** ([ADR-0007](../decisions/0007-desktop-is-not-an-ide.md)). | Phase 2.6 | Signed builds on macOS/Windows/Linux launch to a working agent center; build + run a 3-node workflow on the canvas with live streaming and per-node cost — all offline; SQLite history supports trace/Gantt/replay/retry-from-node; gates pause/resume; no secret crosses into the WebView; ADR-0010 perf gate passes; per-platform Playwright e2e green. | [phases/phase-3-desktop.md](phases/phase-3-desktop.md) | | **4** | Ship the standalone VS Code extension that **bundles `@relavium/core` in-process**: right-click run, sidebar + status-bar monitor, human-gate webview — with **no desktop app required**. Closes Product Phase 1. | Phase 3 | With only the extension installed: right-click a file → pick a workflow → watch streaming in the sidebar/status bar → approve a gate in a webview → run completes; same canonical events + same local SQLite history as CLI/desktop; keys only in `SecretStorage`; import-zone check passes; `relavium.relavium` published and installs on macOS/Windows/Linux. | [phases/phase-4-vscode.md](phases/phase-4-vscode.md) | | **5** | **Product Phase 2 — managed inference (the first Phase-2 deliverable, Option B).** The opt-in `managed` execution mode: the engine stays **local**, only LLM egress is proxied through Relavium's gateway behind a new `ManagedGatewayProvider`, calling providers with Relavium's own keys and selling metered usage. Accounts/device-flow auth, key vault + pools, real-time metering (reserve→settle, UNIQUE `request_id`), quota/budget caps, merchant-of-record billing (prepaid + overage), cheap-default routing, abuse controls, no-prompt-logging, usage dashboard. **BYOK stays first-class.** | All of Phase 1 (0–4), shipped + battle-tested ([ADR-0008](../decisions/0008-local-first-phase-1-cloud-phase-2.md)); **plus R1**: provider-ToS confirmation + merchant-of-record + DPA/KVKK/GDPR ([ADR-0012](../decisions/0012-managed-inference-dual-mode.md)) | R1 gate met; a user logs in via device flow, **explicitly** opts into managed, and a workflow runs in `managed` mode with the engine still local and only egress proxied (no silent mode crossing); `ManagedGatewayProvider` is behind the unchanged seam (engine/types untouched); metering is idempotent (UNIQUE `request_id`) + nightly-reconciled; quota/hard-cap + merchant-of-record billing fire; no prompt logging by default; **BYOK unchanged and first-class.** | [phases/phase-5-managed-inference.md](phases/phase-5-managed-inference.md) | diff --git a/docs/roadmap/current.md b/docs/roadmap/current.md index 591a7970..a800e35b 100644 --- a/docs/roadmap/current.md +++ b/docs/roadmap/current.md @@ -7,9 +7,13 @@ - **Related**: [README.md](README.md), [phases/phase-2.5-cli-consolidation.md](phases/phase-2.5-cli-consolidation.md), [phases/phase-2-cli.md](phases/phase-2-cli.md), [deferred-tasks.md](deferred-tasks.md), [../project-structure.md](../project-structure.md), [../tech-stack.md](../tech-stack.md) This page tracks what is active **right now** and the immediate next concrete actions. -The full phase plan and the global milestone spine are in [README.md](README.md); the -granular work breakdown for the active phase (now **Phase 2.5**) is in -[phases/phase-2.5-cli-consolidation.md](phases/phase-2.5-cli-consolidation.md). +The full phase plan and the global milestone spine are in [README.md](README.md). +**Phase 2.5 (CLI Consolidation) is complete** (milestone **M2.5-4**, PR #69, 2026-07-08) — its +breakdown, now historical, is in +[phases/phase-2.5-cli-consolidation.md](phases/phase-2.5-cli-consolidation.md). The next phase, +**Phase 2.6 — Conversational Authoring and the First-Class CLI** (Planned; unblocked by the 2.5 +close; re-scoped 2026-07-08), is in +[phases/phase-2.6-conversational-authoring.md](phases/phase-2.6-conversational-authoring.md). ## Where we are @@ -205,8 +209,8 @@ scrollback elision loss; + a one-shot `agent run` hint-leak). **With 2.5.E this consolidation lanes **2.5.I** (regression harness + DB concurrency hardening — `loadFull` read-txn snapshot, `BEGIN IMMEDIATE` writes with a deterministic `SQLITE_BUSY` retry, the concurrent chat+run + cassette-chain + perf-budget e2es, an advisory Windows CI lane) and **2.5.J** (docs-debt: the accurate unencrypted-history -posture per ADR-0050, and `NO_COLOR`/`FORCE_COLOR`/`--color` resolution) are ✅ **Done (2.5-close-out, -2026-07-08)** — **reaching milestone M2.5-4, so Phase 2.5 is complete** — landed alongside the doable-now +posture per ADR-0050, and `NO_COLOR`/`FORCE_COLOR`/`--color` resolution) are ✅ **Done (2.5-close-out, PR #69, +2026-07-08)** — **reaching milestone M2.5-4, so Phase 2.5 is complete, merged to `main`** — landed alongside the doable-now Batch A–E backlog (test-hardening; 2.5.H TUI polish; `AgentParseError` line/col; the ADR-0057 approval/security batch — `[c]` reject-with-reason, non-TTY policy, SCOPE-denial recovery, Ctrl+T-in-approval, the Trojan-Source bidi floor, behind an append-only ADR-0057 amendment; the profile-aware advertise-filter + the in-house @@ -238,8 +242,15 @@ keychain no-raw-key IPC test. ## Not started yet -The surfaces and the cloud — everything after the engine critical path: the desktop app -(Phase 3) and the VS Code extension (Phase 4), then **Product Phase 2** — first **managed +The immediate next phase is **Phase 2.6 — Conversational Authoring and the First-Class CLI** +([phase-2.6-conversational-authoring.md](phases/phase-2.6-conversational-authoring.md), Planned, +unblocked by the 2.5 close and **re-scoped 2026-07-08** from maintainer UX findings + a competitor +research pass + the deferred-tasks triage): a full-screen, Home-managed CLI (browsers for +workflows/runs/agents, provider + MCP + settings management, onboarding v2 with the Relavium-account +stub), competitor-breadth tools under the governance floor, settings/theming/`en`+`tr` localization, +the run-ops resume follow-up — plus the original spine: the shared `@relavium/authoring` package and +a chat that **authors** standards-valid `.relavium.yaml`. Then the surfaces and the cloud — +everything after the engine critical path: the desktop app (Phase 3) and the VS Code extension (Phase 4), then **Product Phase 2** — first **managed inference** ([phase-5-managed-inference.md](phases/phase-5-managed-inference.md), the opt-in `managed` gateway, engine still local), then the **cloud execution layer and web portal** ([phase-6-cloud-execution-portal.md](phases/phase-6-cloud-execution-portal.md)), the two diff --git a/docs/roadmap/deferred-tasks.md b/docs/roadmap/deferred-tasks.md index 4fec707f..3fd55a36 100644 --- a/docs/roadmap/deferred-tasks.md +++ b/docs/roadmap/deferred-tasks.md @@ -2,7 +2,9 @@ > Status: Living -> Last updated: 2026-07-05 +> Last updated: 2026-07-08 — the Phase-2.6 rewrite triaged every open item; the now-doable ones carry a +> **Scheduled → 2.6.X** marker pointing at their [phase-2.6](phases/phase-2.6-conversational-authoring.md) +> workstream (they stay unchecked until the PR that lands them). - **Related**: [current.md](current.md), [README.md](README.md), [phases/phase-0-foundations.md](phases/phase-0-foundations.md) @@ -32,6 +34,7 @@ Severity is the review's verified rating. Check an item off in the PR that resol a superseding ADR**. Recommendation: ship (A) now, do (B) in its own governed PR. **Full analysis, current version table, per-dependency verdicts, migration plans, and open questions: [phases/node-runtime-upgrade.md](phases/node-runtime-upgrade.md).** Out of scope for Phase 2.5.F. + **Scheduled → 2.6.F** (both halves: the immediate dev/CI bump + the governed floor decision). *(package.json engines + .nvmrc + pnpm-workspace catalog @types/node + tech-stack.md; cross-phase)* @@ -57,10 +60,10 @@ Severity is the review's verified rating. Check an item off in the PR that resol URL validation are construction-time / seam-ingestion-time policy; they catch malformed URLs but cannot catch DNS rebinding or a public hostname resolving to a private IP. **Scope split (resolving the earlier "Phase 2" framing):** the **media** url-carrier mechanism is **pulled into 1.AF** on a new bytes-shaped media-egress capability ([ADR-0043](../decisions/0043-media-egress-failover-rematerialization-ssrf.md)); the **CLI tool** `EgressCapability.fetch` **landed in 2.5.E** ([ADR-0057](../decisions/0057-cli-chat-modes-and-per-tool-approval.md)) — `apps/cli/src/engine/tool-host/egress.ts` over the shared `connectValidated` connect-by-validated-IP mechanism (`packages/db/src/safe-egress.ts`), with the Host/`:authority`-header strip; the **desktop** surface's fetch hook still lands when the desktop implements it. *(packages/core/src/tools/types.ts; security-review.md; media → 1.AF/ADR-0043; CLI tool → 2.5.E/ADR-0057; desktop → surface fetch hook)* - [ ] **MCP SDK network transport — upgrade to connect-by-validated-IP ([ADR-0053](../decisions/0053-mcp-network-transport-egress-security.md) §2).** 2.R ships **pre-connect host validation** as the floor for the `http` (Streamable HTTP) / `websocket` MCP transports — the `@modelcontextprotocol/sdk` opens its **own** socket, architecturally distinct from the `EgressCapability.fetch` hook above. When the SDK transport exposes an injectable `fetch`/dialer hook, upgrade to **connect-by-validated-IP**: resolve DNS → validate the IP against the shared range-block primitive → connect to that IP, re-validating on each redirect hop — closing the residual DNS-rebind window. **The dialer + redirect re-validation MUST enforce the authored `host:port`** (ADR-0053 §3 / SEC-EGRESS-3), not just the host: an `allow_local_endpoint` server is permitted exactly its declared `host:port`, so a resolved/redirected target on a *different* port of the same permitted-private host (`:6379`/`:5432`/`:22`/the Docker socket) must be re-blocked. (2.R's pre-connect floor is host:port-safe by construction — the SDK dials exactly the one authored url — so this constraint binds the dialer, not the floor.) Each MCP network mechanism gets a dedicated security-review pass when it lands. *(packages/mcp/src; ADR-0053 §2/§3; ADR-0043 mechanism)* -- [ ] **MCP `stdio` spawn — import-trust/consent gate + `npx` dependency pinning ([ADR-0052](../decisions/0052-inbound-mcp-client-package-lifecycle-registration.md) §2).** Spawning a declared `stdio` MCP server runs arbitrary local code / an `npx`-installed package. 2.R treats a server declared in the user's **own** committed YAML as author trust; the **imported/shared untrusted workflow** case is out of baseline scope. When the import/share path matures, gate the first spawn of a server from an untrusted-provenance `.relavium.yaml` behind explicit consent, and pin the `npx` package version/integrity for the built-in auto-install servers. *(packages/mcp/src; apps/cli; ADR-0052 §2; ADR-0029 trust model)* +- [ ] **MCP `stdio` spawn — import-trust/consent gate + `npx` dependency pinning ([ADR-0052](../decisions/0052-inbound-mcp-client-package-lifecycle-registration.md) §2).** Spawning a declared `stdio` MCP server runs arbitrary local code / an `npx`-installed package. 2.R treats a server declared in the user's **own** committed YAML as author trust; the **imported/shared untrusted workflow** case is out of baseline scope. When the import/share path matures, gate the first spawn of a server from an untrusted-provenance `.relavium.yaml` behind explicit consent, and pin the `npx` package version/integrity for the built-in auto-install servers. **Scheduled → 2.6.B** (the authoring/import path this consent gate protects matures there). *(packages/mcp/src; apps/cli; ADR-0052 §2; ADR-0029 trust model)* - [x] **MCP host boundary — strip `McpConnectError.cause` from `--json` / event output (2.R Step 3, [ADR-0052](../decisions/0052-inbound-mcp-client-package-lifecycle-registration.md) §2).** *Resolved in the 2.R Step 3 host wiring:* `startMcpClientFailLoud` (apps/cli/src/engine/mcp-servers.ts) wraps an `McpError` into a typed `CliError` whose message is the secret-free MCP summary with **no** `{ cause }` attached, and the top-level `--json` renderer (apps/cli/src/process/render-error.ts) serializes only `{ type, code, message }` — never `cause`. Regression-locked by `run.test.ts` (`expect(err.cause).toBeUndefined()`). *(apps/cli; packages/mcp/src/errors.ts; 2.R Step 3)* -- [ ] **MCP network transport — header-based auth ([ADR-0052](../decisions/0052-inbound-mcp-client-package-lifecycle-registration.md) §6).** 2.R injects `{{secrets.*}}` only into a **stdio** child's `env`; the network (`http`/`websocket`) specs carry only `{ url }`, so a network server's `env` is **rejected at parse** (fail-closed, never silently dropped). When network MCP servers need credentials, add a host-resolved auth-header field (e.g. `Authorization: 'Bearer {{secrets.}}'`) wired through the SDK transport's `requestInit`/headers, resolved from the same isolated `mcp-secret:*` namespace and never logged/serialized. *(packages/mcp/src/sdk-http.ts; apps/cli/src/engine/mcp-servers.ts; ADR-0052 §6)* -- [ ] **MCP follow-ups (non-security).** A durable cross-invocation **tool-list cache** (mcp-integration.md ~1h per-`(command,args)`, with a transport-covering key) — 2.R re-runs discovery per process ([ADR-0052](../decisions/0052-inbound-mcp-client-package-lifecycle-registration.md) §3); and a generalized **`SecretResolver`** seam beyond the 2.R `mcp-secret:*` keychain namespace ([ADR-0052](../decisions/0052-inbound-mcp-client-package-lifecycle-registration.md) §6); and reconciling the `types.ts` `ToolId` "register dynamically" comment to "host-side assembled" when 2.R touches `packages/core`; and **mid-call abort propagation** — the engine's `AbortSignalLike` is not forwarded to the in-flight MCP `tools/call` (the SDK transport wants a DOM `AbortSignal`), so a turn cancel tears the connection down but does not cancel an in-flight call (`@relavium/mcp` `manager.ts`). *(packages/mcp/src; packages/core; Phase-3)* +- [ ] **MCP network transport — header-based auth ([ADR-0052](../decisions/0052-inbound-mcp-client-package-lifecycle-registration.md) §6).** 2.R injects `{{secrets.*}}` only into a **stdio** child's `env`; the network (`http`/`websocket`) specs carry only `{ url }`, so a network server's `env` is **rejected at parse** (fail-closed, never silently dropped). When network MCP servers need credentials, add a host-resolved auth-header field (e.g. `Authorization: 'Bearer {{secrets.}}'`) wired through the SDK transport's `requestInit`/headers, resolved from the same isolated `mcp-secret:*` namespace and never logged/serialized. **Scheduled → 2.6.I.** *(packages/mcp/src/sdk-http.ts; apps/cli/src/engine/mcp-servers.ts; ADR-0052 §6)* +- [ ] **MCP follow-ups (non-security).** A durable cross-invocation **tool-list cache** (mcp-integration.md ~1h per-`(command,args)`, with a transport-covering key) — 2.R re-runs discovery per process ([ADR-0052](../decisions/0052-inbound-mcp-client-package-lifecycle-registration.md) §3); and a generalized **`SecretResolver`** seam beyond the 2.R `mcp-secret:*` keychain namespace ([ADR-0052](../decisions/0052-inbound-mcp-client-package-lifecycle-registration.md) §6); and reconciling the `types.ts` `ToolId` "register dynamically" comment to "host-side assembled" when 2.R touches `packages/core`; and **mid-call abort propagation** — the engine's `AbortSignalLike` is not forwarded to the in-flight MCP `tools/call` (the SDK transport wants a DOM `AbortSignal`), so a turn cancel tears the connection down but does not cancel an in-flight call (`@relavium/mcp` `manager.ts`). **The tool-list-cache + mid-call-abort halves are scheduled → 2.6.I** (the rest stays opportunistic). *(packages/mcp/src; packages/core; Phase-3)* - [ ] **Streaming media triad (`media_start`/`media_delta`/`media_end`) — host-deferred ([ADR-0046](../decisions/0046-inline-media-out-via-generate-streaming-triad-deferred.md) §4).** 1.AG Section B delivers inline media-out through the non-streaming `generate()` path (the in-flight `media` `ContentPart` is de-inlined at `#emitDurable`). The **streaming** triad stays RESERVED: its Node @@ -76,13 +79,14 @@ Severity is the review's verified rating. Check an item off in the PR that resol Responses API `image_generation` built-in tool — a **separate request surface** the Chat-Completions adapter does not call. Wire the Responses-API path (request + `image_generation_call` output-item parse → the already-defined providerExecuted media shape) when a Phase-1 model needs it. *(packages/llm/src/adapters/openai.ts; post-1.AG)* -- [ ] **Per-model `media_surface` lookup — HOST WIRING deferred (1.AG Section C → 1.AH).** Section C added - `AgentRunnerDeps.resolveMediaSurface?(model) → MediaSurface` (the inline-vs-generative routing discriminator, - default `'chat'`; tests inject it). The production wiring — the host reading `model_catalog.media_surface` (the - column landed in Section A) and supplying the lookup — is **1.AH host-wiring**, exactly like the D12/D15/D17 - host-wiring obligations. Until then `resolveMediaSurface` is absent and **every** model routes inline (`'chat'`), - so no generative model is runtime-reachable in Phase 1; the engine mechanism + the OpenAI-image adapter are proven - via injected-surface tests. *(packages/core/src/engine/agent-runner.ts; host catalog read → 1.AH)* +- [x] **Per-model `media_surface` lookup — wired by the CLI (✅ PR #52, 2.S; verified + checked off 2026-07-08).** + Section C added `AgentRunnerDeps.resolveMediaSurface?(model) → MediaSurface` (the inline-vs-generative routing + discriminator, default `'chat'`; tests inject it). The production wiring — the host reading + `model_catalog.media_surface` and supplying the lookup — landed with 2.S: `media-wiring.ts` supplies + `catalog.resolveMediaSurface` (the `model_catalog` projection) and `build-engine.ts` threads it into + `AgentRunnerDeps`, so a generative model routes by its catalog surface on the CLI. The sibling D15/D17/D8 + host-wiring items were checked off at PR #52; this one was missed then. The desktop/VS Code surfaces reuse the + same injectable port (Phase-3/Phase-4). *(apps/cli/src/engine/media-wiring.ts + build-engine.ts; PR #52)* - [ ] **Verified generative model rates + `MODEL_PRICING` rows (1.AG Section C → 1.AH).** The Section C cost mechanism (pre-egress estimate + the one realized `cost:updated`) reuses `estimateMediaCost`/`mediaCost`, which **degrade to 0 on a missing rate** (H4). No generative model rows were added to `MODEL_PRICING` — fabricating @@ -291,11 +295,13 @@ Severity is the review's verified rating. Check an item off in the PR that resol - [ ] **Run-submission idempotency / request-dedup (open — evaluate carefully).** Distinct from the content-addressed media cache and any managed-mode metering `request_id`: should an identical run-create request be de-duplicated so a double-submit does not start two runs? **Open:** a Phase-1 engine run-create hook vs a - surface concern. Low-stakes; recorded so it is not lost. *(WorkflowEngine run-create; 1.N)* + surface concern. Low-stakes; recorded so it is not lost. **Scheduled → 2.6.H** (the Home can start a + workflow there, making double-submit real). *(WorkflowEngine run-create; 1.N)* - [ ] **i18n CI key-parity + data/code separation (Phase 2+ surface).** When the desktop / CLI / VS Code surfaces add i18n: a CI test that **fails** on a missing/extra translation key (parity), **zero conditional logic in translation data** (data ≠ code), and a dead/unused-string lint. Recorded now; lands with the - Phase-2/3/4 surface i18n work (no consumer yet). *(a `docs/standards/` entry or skill; Phases 2–4)* + Phase-2/3/4 surface i18n work (no consumer yet). **Scheduled → 2.6.L** (the CLI `en`+`tr` catalog is the + first consumer). *(a `docs/standards/` entry or skill; Phases 2–4)* - [ ] **Pre-egress token-estimate accuracy — watch item (1.AC).** The ADR-0028 governor blocks on `worstCaseNextEstimate(maxTokens)` from `[defaults].max_tokens_estimate`. Record the open question: does the estimate need provider-accurate token counting (from the seam's model meta / @@ -375,7 +381,8 @@ Severity is the review's verified rating. Check an item off in the PR that resol `{{ inputs }}`/`{{ ctx }}` in system fields must add a parse-time gate **rejecting** untrusted `run.outputs`/`read_file` references there (analogous to the secret-taint gate — do **not** drop the field from `nodeReferenceSites`, which would remove the existing secret-leak protection). A pinning test already - asserts an untrusted `run.outputs` value never reaches the system string. *(medium · packages/core/src/interpolation/analyze.ts, collect.ts; SEC-1)* + asserts an untrusted `run.outputs` value never reaches the system string. + **Scheduled → 2.6.D.** *(medium · packages/core/src/interpolation/analyze.ts, collect.ts; SEC-1)* - [ ] **Multimodal tool-result through the adjacent-message + redaction paths** — all 1.O coverage exercises text/JSON tool args + content; confirm image/media tool-result blocks survive the Anthropic adjacent-role merge (no dropped blocks / no double-merge with `stripReasoningParts`) and the redaction path. *(low · packages/llm/src/adapters/anthropic.ts; 1.AF)* @@ -447,17 +454,17 @@ Severity is the review's verified rating. Check an item off in the PR that resol the target gate's decision immediately, but a *remaining* pending gate (multi-gate run, crash-while-paused) is rehydrated without re-arming its timer, so its deadline is lost until the next restart. The data needed (`timeoutAction` + `expiresAt`) is now persisted on `human_gate:paused` (PR #22), so no backfill — Phase-2 - crash-reconciliation re-arms from the log against a real clock. *(low · packages/core/src/engine/engine.ts `#seedFromCheckpoint`; Phase-2)* + crash-reconciliation re-arms from the log against a real clock. **Scheduled → 2.6.K.** *(low · packages/core/src/engine/engine.ts `#seedFromCheckpoint`; Phase-2)* - [ ] **Content-level workflow-identity guard on resume** — `resumeFromCheckpoint` compares the surrogate `workflowId` (catches resuming a *different* workflow → `workflow_mismatch`), but not a *same-slug, edited-content* workflow. The stronger guard rides on the frozen `runs.workflow_definition_snapshot` column ([database-schema.md](../reference/desktop/database-schema.md)) — a Phase-2 persistence concern wired with - the real `RunStore`, not the event-derived in-memory state. *(low · packages/core/src/engine/engine.ts; Phase-2)* + the real `RunStore`, not the event-derived in-memory state. **Scheduled → 2.6.H.** *(low · packages/core/src/engine/engine.ts; Phase-2)* - [ ] **Cross-process concurrent gate-resolve (TOCTOU)** — idempotent re-delivery holds within a process (`#resolvedGates`) and across processes once the prior process's `human_gate:resumed` is persisted (the checkpoint reconstructs `resolvedGateIds`). The residual window — two processes loading the *same* still-pending gate before either persists — is closed by a store-level uniqueness constraint on `human_gate:resumed` per - `(runId, gateId)`, a Phase-2 SQLite/cloud-store guarantee, not the in-memory reference. *(low · checkpoint.ts/engine.ts; Phase-2 store)* + `(runId, gateId)`, a Phase-2 SQLite/cloud-store guarantee, not the in-memory reference. **Scheduled → 2.6.H.** *(low · checkpoint.ts/engine.ts; Phase-2 store)* ## AgentSession (1.V) follow-ups @@ -482,7 +489,8 @@ Severity is the review's verified rating. Check an item off in the PR that resol pass-through but does **not** handle a `BudgetPauseError`: a non-`AgentTurnError` throw rolls the user message back and re-raises (a session has no pause/resume gate machinery in 1.V). The run path maps a budget pause to a `paused` node outcome via the human-gate seam; a budgeted session needs the analogous suspend/resume - lifecycle. Wire it when sessions gain a budget (surface phases). *(medium · packages/core/src/engine/agent-session.ts; ADR-0028)* + lifecycle. Wire it when sessions gain a budget (surface phases). **Scheduled → 2.6.K** (with the EA4-ride + sibling below). *(medium · packages/core/src/engine/agent-session.ts; ADR-0028)* - [ ] **Per-session tool narrowing (ADR-0029 narrow-only).** 1.V grants the bound agent's `tools` verbatim; a session cannot yet **narrow** them per-session (it may only ever narrow, never widen). Add a session-level narrow when a surface needs to restrict a session's tools below the agent's grant. @@ -518,7 +526,7 @@ Severity is the review's verified rating. Check an item off in the PR that resol > untrusted injection) shipped. These bounded pieces were deliberately deferred (each is additive; the > confidentiality floor + jail + injection framing hold without them): -- [x] **Advisory `.gitignore` / `.relaviumignore` completion trim — DONE (2.5-close Step 15, Batch E).** A +- [x] **Advisory `.gitignore` / `.relaviumignore` completion trim — DONE (2.5-close Step 15, Batch E; PR #69).** A dependency-free, ReDoS-safe in-house matcher ([gitignore.ts](../../apps/cli/src/render/tui/gitignore.ts)) folds the workspace-root `.gitignore` + `.relaviumignore` into the `@`-mention candidate filter (comments/blanks, `!` negation, dir-only `/`, anchoring, `*`/`**`/`?` globs; a LINEAR two-pointer glob matcher — no regex, so no @@ -527,10 +535,10 @@ Severity is the review's verified rating. Check an item off in the PR that resol fs-capability enforcement. Documented subset limits: nested per-dir ignore files + `[a-z]` char classes deferred (they only UNDER-hide, never a security gap). *(apps/cli/src/render/tui/gitignore.ts + mention.ts; ADR-0061)* - [ ] **`@`-glob / directory expansion.** Single-file injection ships; `@src/**/*.ts` glob / whole-directory - expansion is deferred (ADR-0061). *(low · apps/cli/src/render/tui/mention.ts)* + expansion is deferred (ADR-0061). **Scheduled → 2.6.E.** *(low · apps/cli/src/render/tui/mention.ts)* - [ ] **`@`-mention of a binary / media file.** The reader fail-closes on a binary file (parity with `read_file`); a durable media-handle injection path (ADR-0031) is a follow-up. *(low · apps/cli/src/render/tui/mention.ts)* -- [x] **Strip Unicode bidi/format controls at the shared display boundary — DONE (2.5-close Step 14, D-5).** The +- [x] **Strip Unicode bidi/format controls at the shared display boundary — DONE (2.5-close Step 14, D-5; PR #69).** The shared `stripTerminalControls` (chat-projection.ts) now strips the Trojan-Source reordering family (U+202A–202E, U+2066–2069, LRM/RLM/ALM) at every display boundary; ZWJ/ZWNJ preserved; the source uses `\u` escapes (no literal bidi bytes). *(apps/cli/src/render/tui/chat-projection.ts)* @@ -541,17 +549,17 @@ Severity is the review's verified rating. Check an item off in the PR that resol > reseat-less mode system + per-tool approval + mid-turn abort + the host arms shipped. These bounded pieces > were deliberately deferred (each is additive, none blocks the mode system's security guarantees): -- [x] **`[c]` reject-with-typed-reason approval prompt — DONE (2.5-close Step 14, D-1).** A `[c]` at the approval +- [x] **`[c]` reject-with-typed-reason approval prompt — DONE (2.5-close Step 14, D-1; PR #69).** A `[c]` at the approval prompt opens a keyboard-owning reason-input sub-mode (both `relavium chat` + the Home); on submit it rejects with the sanitized + 300-char-bounded reason via the existing `ToolApprovalDecision.reject.reason` seam. The floor is unchanged. *(apps/cli/src/render/tui/chat-input.ts + chat-ink.tsx + home-controller.ts)* -- [x] **Conversationally recover from a SCOPE denial in chat — DONE (2.5-close Step 14, D-3).** The `recoverable` +- [x] **Conversationally recover from a SCOPE denial in chat — DONE (2.5-close Step 14, D-3; PR #69).** The `recoverable` flag moved to the base `ToolDispatchError`; exactly two `tool_denied`s opt in — `ToolPolicyError('media_scope_denied')` and the fs **pure scope-tier escape** (`FsScopeDeniedError` from `assertInScope`) — so on the `recoverToolFailures` surfaces the model is fed the denial and adapts to an in-bounds path. The confidentiality / protected-path / symlink / SSRF / user / guardrail denials stay FATAL. *(packages/core/src/tools/errors.ts; apps/cli/src/engine/tool-host/fs.ts; ADR-0057)* - [x] **Plain / non-TTY non-interactive approval policy — DONE (2.5.E "High 9" + consolidated in 2.5-close Step 14, - D-2).** A non-interactive driver (plain non-TTY / `--json` / one-shot `agent run`) uses the one canonical + D-2; PR #69).** A non-interactive driver (plain non-TTY / `--json` / one-shot `agent run`) uses the one canonical `nonInteractiveApprovalPrompt` — every governed dispatch is DENIED (never a hang, never an auto-approve). *(apps/cli/src/chat/chat-mode.ts; apps/cli/src/commands/chat.ts + agent-run.ts)* - [ ] **Approval-consent-line zero-width hardening (2.5-close Step 14 security-review, optional).** The shared render @@ -559,21 +567,21 @@ Severity is the review's verified rating. Check an item off in the PR that resol highest-trust surface — the approval consent line's target (`formatApprovalTarget` → `sanitizeInline`) — still passes NON-reordering zero-width chars (ZWSP U+200B / word-joiner U+2060 / BOM U+FEFF), which the provider-URL echo already strips. Running the stricter zero-width superset on the consent-line target would be defense-in-depth. Not - a CVE gap (those chars cannot reorder a command to masquerade). *(low · apps/cli/src/render/tui/chat-projection.ts)* + a CVE gap (those chars cannot reorder a command to masquerade). **Scheduled → 2.6.M** (with tool-render v2). *(low · apps/cli/src/render/tui/chat-projection.ts)* - [ ] **Extract the `[c]` reason-capture to a shared pure reducer (2.5-close Step 14 review, test-parity).** The reason-capture keystroke glue is duplicated inline in `ChatApp` (chat-ink.tsx) and the Home controller; the shared primitives (`reduceApprovalKey`, `sanitizeApprovalReason`, `reduceEditorMotion`) are unit-tested and the Home path has an integration test, but the ChatApp inline copy has no direct test (matching the existing no-ChatApp-integration boundary). Extracting the capture step to one pure reducer (like the mention/effort submodes) would let both - surfaces test the same function. *(low · apps/cli/src/render/tui/chat-ink.tsx + home-controller.ts)* + surfaces test the same function. **Scheduled → 2.6.M** (with tool-render v2). *(low · apps/cli/src/render/tui/chat-ink.tsx + home-controller.ts)* - [ ] **Live `web_search` / `http_request` egress credential resolver.** `assembleToolEnv` accepts an `egressCredentialResolver` and the egress arm attaches it host-side as a Bearer, but the chat/Home session-host does not yet wire it to the keychain — so a `web_search` needing a provider key currently 401s (surfaced, never a crash). - Wire the provider-key resolver through when the chat surface needs authenticated egress. *(low · apps/cli/src/chat/session-host.ts)* + Wire the provider-key resolver through when the chat surface needs authenticated egress. **Scheduled → 2.6.M** (the `web_search` activation). *(low · apps/cli/src/chat/session-host.ts)* - [ ] **Session-level budget pause/resume (rides the EA4 machine).** The EA4 pause/resume state landed for mid-turn abort + approval; the ADR-0028 session budget `pause_for_approval` can now ride the same machine (today a chat cost-cap trip settles the turn loudly as `budget_exceeded` — the REPL is the approval gate). See also the 1.V - session-budget follow-up above. *(medium · apps/cli/src/chat + agent-session.ts)* + session-budget follow-up above. **Scheduled → 2.6.K.** *(medium · apps/cli/src/chat + agent-session.ts)* - [ ] **`relavium budget resume` CLI command (2.5-close Step 15 / Batch E — DEFERRED to a focused follow-up).** The engine ALREADY supports resuming a budget-paused run (`engine.resume(runId, budgetGateId, decision)`, budget-governor.ts / checkpoint.ts `isBudgetGate`), and `relavium gate` deliberately EXCLUDES budget gates @@ -582,9 +590,11 @@ Severity is the review's verified rating. Check an item off in the PR that resol resume machinery (so the clean form extracts a shared resume core rather than duplicating). Low, dependency-free. **Why deferred (maintainer call, 2026-07-08):** it modifies the security-sensitive `gate.ts` cross-process resume path and is coupled to the secret-re-provide follow-up below (both refactor that path), so both are best landed - together with fresh context rather than at the tail of the 2.5-close session. *(low · apps/cli/src/commands/{gate,budget}.ts + manifest.ts + dispatch.ts; ADR-0028)* + together with fresh context rather than at the tail of the 2.5-close session. **Scheduled → 2.6.K** (that + focused follow-up). *(low · apps/cli/src/commands/{gate,budget}.ts + manifest.ts + dispatch.ts; ADR-0028)* - [ ] **`project`-tier `extraRoots` allowlist (carried from 2.5.A).** The `project` fs tier behaves as - workspace-only until the path-allowlist lands (it can only NARROW the jail, never open a hole). *(low · apps/cli/src/engine/tool-host/assemble.ts)* + workspace-only until the path-allowlist lands (it can only NARROW the jail, never open a hole). + **Scheduled → 2.6.M** (the `[chat].extra_roots` config key is the missing source). *(low · apps/cli/src/engine/tool-host/assemble.ts)* - [ ] **fs hard-link aliasing — the pnpm virtual-store read exemption (accepted residual, ADR-0057 review record).** The hard-link aliasing READ guard (`st.nlink > 1` ⇒ refused) is disabled ONLY for pnpm's `node_modules/.pnpm/…` virtual store (`isPnpmStorePath`), so dependency-source reads work on Linux (where pnpm hard-links). The bounded @@ -601,7 +611,7 @@ Severity is the review's verified rating. Check an item off in the PR that resol fs, a host for egress, a server for mcp) would make `always` track what the user actually reviewed. Pairs with: surface the MCP server/tool in `ToolActionPreview` (today `mcp_call`/`web_search` return a BLANK preview, so F3 correctly forbids caching their `always`) — a structured `{mcpServer,mcpTool}` preview would turn the - blank-check downgrade into a real, reviewable, cacheable per-server grant. *(medium · apps/cli/src/chat/chat-mode.ts + packages/core/src/tools/{types,registry,builtins}.ts + run-event.ts)* + blank-check downgrade into a real, reviewable, cacheable per-server grant. **Scheduled → 2.6.M.** *(medium · apps/cli/src/chat/chat-mode.ts + packages/core/src/tools/{types,registry,builtins}.ts + run-event.ts)* - [ ] **fs `.relavium` sensitive-read/write segment vs. the `~/.relavium/tmp` sandboxed root (latent).** Both the read floor (`SENSITIVE_READ_DIR_SEGMENTS`) and the write floor (`PROTECTED_DIR_SEGMENTS`) match a `.relavium` segment anywhere, so they would refuse the sanctioned `tmpDir` scratch root — inert today (no call site wires @@ -642,7 +652,7 @@ Severity is the review's verified rating. Check an item off in the PR that resol writer committing between them could yield a run row + event/step reads from different snapshots. Lower impact than the session case: run history is **event-sourced** and the checkpoint fold tolerates partial state, so a torn read self-heals on the next fold. If tightened, wrap the caller-level reconstruction in one - read transaction (as `loadFull` now does). *(low · packages/db run-history-store consumers + the resume + read transaction (as `loadFull` now does). **Scheduled → 2.6.H.** *(low · packages/db run-history-store consumers + the resume caller; surfaced during 2.5.I S1 review)* - [ ] **The CLI chat persister writes a turn non-atomically — messages then session totals in separate auto-committed statements.** `apps/cli/src/chat/persister.ts` appends the user + assistant messages and then @@ -651,7 +661,7 @@ Severity is the review's verified rating. Check an item off in the PR that resol guarantees *snapshot* consistency (both reads see one DB snapshot) but not *turn* atomicity — a snapshot can observe messages ahead of their totals. To make "totals always match the returned messages" hold, wrap each turn's message-appends + `updateSession` in one host-side `db.transaction` (BEGIN IMMEDIATE). Bounded, host-side. - *(low · apps/cli/src/chat/persister.ts; surfaced during 2.5.I S2 review)* + **Scheduled → 2.6.H.** *(low · apps/cli/src/chat/persister.ts; surfaced during 2.5.I S2 review)* - [ ] **`relavium run` maps any `run:paused` to exit 3 (gate-paused); revisit when media host-wiring lands.** `run.ts` returns `EXIT_CODES.gatePaused` (3) for any `run:paused`, which is correct in 2.D because a human gate is the **only** `run:paused` source (no `mediaStore`/media-job host is wired, so a media-only @@ -659,7 +669,7 @@ Severity is the review's verified rating. Check an item off in the PR that resol 1.AG §D — can never be emitted). When the media host capability lands (the same surface as the deferred media-egress work, ~2.S), a media-only park would be reported as "gate-paused" with no gate; at that point decide whether exit 3 (and the rendered message) should distinguish a gate park from a media park. - *(low · apps/cli/src/commands/run.ts; media host-wiring / 2.S)* + **Scheduled → 2.6.K.** *(low · apps/cli/src/commands/run.ts; media host-wiring / 2.S)* - [ ] **`relavium budget resume [--approve | --abort]` is documented but has no numbered workstream.** [commands.md](../reference/cli/commands.md) (canonical) specifies it as the non-interactive operator path for a run suspended at a budget cap (`budget:paused`, `on_exceed: pause_for_approval` — @@ -667,7 +677,8 @@ Severity is the review's verified rating. Check an item off in the PR that resol reuses **2.G's** cross-process resume substrate (a budget pause resolves through the same checkpoint reload + resume path as a human gate, behind a budget-specific command + flags), so it is a small follow-up once 2.G lands — candidate home: alongside 2.I, or its own short workstream. **Deliberately out of 2.G** (a - distinct ADR-0028 surface, not in 2.G's acceptance). *(low · apps/cli/src/commands/; ADR-0028)* + distinct ADR-0028 surface, not in 2.G's acceptance). **Scheduled → 2.6.K** (single tracking point: the + Batch-E entry above). *(low · apps/cli/src/commands/; ADR-0028)* - [ ] **Re-provide `secret`-typed inputs on cross-process resume.** The durable `run:started.inputs` are **masked** (a `secret` input is persisted as `{ secret: true, ref }`, never plaintext — ADR-0006/0036), so a fresh-process `relavium gate` resume cannot restore the real value. 2.G **fails closed (exit 2)** when a @@ -680,7 +691,7 @@ Severity is the review's verified rating. Check an item off in the PR that resol guarantee (allow-with-re-provisioning), demands the stdin-not-argv secret discipline (`provider set-key` pattern) + a mandatory security-review pass, and is coupled to the `budget resume` command above (both refactor the `gate.ts` resume path). Best landed together with fresh context, not at the tail of the 2.5-close session. - *(medium · apps/cli/src/commands/gate.ts; ADR-0006)* + **Scheduled → 2.6.K** (that focused follow-up). *(medium · apps/cli/src/commands/gate.ts; ADR-0006)* ### 2.I read-command follow-ups (PR #48 multi-agent review, 2026-06-24) @@ -696,7 +707,7 @@ Severity is the review's verified rating. Check an item off in the PR that resol *commands* (`relavium list` / `loadLatestRunPerWorkflow` still return the full set) — genuinely unneeded at single-user CLI scale; add a cursor API before the desktop/cloud surfaces drive these reads at volume. *(low → scale · packages/db/src/run-history-store.ts; database-schema.md)* -- [x] **`AgentParseError` line/column — DONE (2.5-close Step 13, `fix(core)`).** `agentSyntaxErrorFrom` (the +- [x] **`AgentParseError` line/column — DONE (2.5-close Step 13, `fix(core)`; PR #69).** `agentSyntaxErrorFrom` (the agent sibling of parser.ts's `syntaxErrorFrom`) now threads `LineCounter` positions into a positioned `YAMLParseError`: optional 1-based `line`/`column` fields (parity with `WorkflowSyntaxError`) plus a folded `(source — line L, column C)` locator in the message so the position reaches every `.message`-surfacing @@ -718,6 +729,7 @@ Severity is the review's verified rating. Check an item off in the PR that resol `AgentParseError` as an exit-2 invocation fault. Also relativize the `source` label at the `chat`/`agent run` call site (`agent-source.ts` passes the ABSOLUTE `source.path`, unlike the catalog's workspace-relative `rel`; no secret leak — it is the user-typed path — but it contradicts the parser docstring's "workspace-relative"). + **Scheduled → 2.6.B** (the conversational-authoring loop needs these diagnostics visible). *(medium · apps/cli/src/chat/agent-source.ts + session-host.ts + commands/agent-run.ts + run.ts)* - [ ] **Residual read-command test pins.** A few low-risk coverage gaps remain after the PR's test additions: `pendingHumanGates` with `expiresAt` present and with multiple simultaneous gates; `list --json` no-project @@ -745,10 +757,11 @@ Severity is the review's verified rating. Check an item off in the PR that resol `sandboxed`-minus-tmp). It did **not** land in 2.5.E — carried forward under the *Phase 2.5.E follow-ups* entry above (single tracking point). *(low · apps/cli/src/engine/tool-host/assemble.ts; ADR-0057; built-in-tools.md fs-tier note)* - [x] **Write-capable chat — RESOLVED in 2.5.E.** *Landed in 2.5.E ([ADR-0057](../decisions/0057-cli-chat-modes-and-per-tool-approval.md), PR #63, merged 2026-07-03):* the `relavium chat` default profile is now the full-capability `chat-read-write` host — `write_file` is wired and gated by the per-tool approval floor (denied in the default `ask` mode as `tool_denied`, not `tool_unavailable`); a declared `full` tier is still **clamped to `project`** for the chat surface (an unjailed read exfiltrates `~/.ssh` / `~/.aws`, a write-capable chat shares that risk). *(apps/cli/src/chat/session-host.ts; ADR-0057)* -- [ ] **Profile-unaware advertise-filter.** `wiredToolIds` narrows the grant by which `ToolHost` **arm** is wired, - not by the profile's *read-only* posture — `write_file` is still advertised on a read-only chat host and only - fail-closes at dispatch (`tool_unavailable`). Correct and safe (the dispatch backstop is authoritative), but a - profile-aware advertise-filter would stop offering an always-denied tool. *(low · apps/cli/src/engine/tool-host/assemble.ts)* +- [x] **Profile-unaware advertise-filter — DONE (2.5-close Step 15, Batch E; PR #69).** `wiredToolIds` now takes a + `{ readOnly }` option: on a read-only host a WRITE-class (`fsWrite`) tool is no longer advertised (its `fs` arm is + wired but always denies the write — an always-denied advertisement), the advertise-side complement to the + read-only fs arm's dispatch refusal. The default (read-write) is unchanged, so the live `chat-read-write` session + host is inert. *(apps/cli/src/engine/tool-host/assemble.ts)* - [ ] **Residual fs TOCTOU on the PARENT directory (no `openat` in Node).** The read path (`readJailedFile`) and the write paths (append + temp/rename) all open the FINAL component with `O_NOFOLLOW`, so a final-component symlink swapped in after the jail's `realpath` fails closed. The unclosable residual is a swap of a PARENT @@ -769,10 +782,12 @@ Severity is the review's verified rating. Check an item off in the PR that resol would need `toolName` threading for marginal gain (write-only messages *are* prefixed); (c) the generic `guarded()` catch-all stays **reason-only** (the I3 boundary). Re-open only if a concrete need appears. *(nit · apps/cli/src/engine/tool-host/)* -- [ ] **Two transitively-covered test gaps.** A chat-session-dispatches-`git_status` e2e (the process arm + the - session→host dispatch are each tested; the union covers it) and a persister fold of a failed-turn's **real** - tokens (the fold is error-agnostic and tested; the failed-turn flush fires `turn_completed`). Add explicit - pins opportunistically. *(low · apps/cli/src/**/*.test.ts; testing.md)* +- [x] **Two transitively-covered test gaps — DONE (2.5-close Step 11, Batch A; PR #69).** Both now have an + explicit pin: the chat-session-dispatches-`git_status` e2e was ADDED this round (session-host.test.ts — + "dispatches git_status through the process arm end-to-end", asserting the tool_result folded back with a clean + `{"exitCode":0}`); the failed-turn **real**-token persister fold was CONFIRMED already explicitly pinned + (persister.test.ts — "flushes the running cost on a failed turn so a resumed budget governor sees the true + spend"). *(apps/cli/src/chat/session-host.test.ts + persister.test.ts; testing.md)* ## Schema / validation hardening @@ -808,7 +823,9 @@ Severity is the review's verified rating. Check an item off in the PR that resol / `GEMINI_API_KEY` / `DEEPSEEK_API_KEY`) as CI secrets. Until then live coverage is a known gap. **Decided (2026-06-21, maintainer): defer to Phase-2 workstream 2.K** — enable the live-nightly lane together with the 2.K regression harness rather than as a pre-Phase-2 ops chore; fixture coverage holds - the line until then. + the line until then. **Update (2026-07-08): 2.K has shipped; this is now a Phase-2.6 in-window + maintenance obligation** (see [phase-2.6](phases/phase-2.6-conversational-authoring.md) §"In-window + maintenance obligations") — enable when CI provider keys are available. *(minor → keys · ci.yml, packages/llm/src/conformance/*.conformance.test.ts)* - [ ] **Leakwatch secret-scanning CI gate** — CI has no secret-scan step. The HodeTech standard scanner is **Leakwatch** (never gitleaks); the blocking `ci.yml` step is wired once a @@ -837,21 +854,23 @@ Severity is the review's verified rating. Check an item off in the PR that resol vs a frozen number captured at the parent `RootApp`'s render) is verified only by tracing + the type-shape guard, not by a regression test. Adding a harness is a first-of-its-kind test-architecture decision for `apps/cli` (a new devDependency; possibly an ADR) — track it, then a smoke test could tick a fake clock across two - `store.tick()`-driven renders and assert the displayed elapsed advances. *(low · apps/cli/src/render/tui/home-app.tsx + `store.tick()`-driven renders and assert the displayed elapsed advances. **Scheduled → 2.6.F** (behind the + full-screen renderer + harness ADR). *(low · apps/cli/src/render/tui/home-app.tsx + chat-ink.tsx; testing.md "every bug fix lands with a regression test")* -- [ ] **Compact abort hint during token streaming.** The `Esc to stop` affordance shows on the pre-first-token - status line but not once the answer streams (the content line needs the width) — yet `Esc` aborts throughout the - turn (EA7). A follow-up could keep a compact hint in the footer during streaming. *(low · apps/cli/src/render/tui/chat-ink.tsx)* -- [ ] **Bound the EXPANDED reasoning panel by rendered LINES, not just chars.** The panel body is bounded to - `MAX_LIVE_TOKEN_CHARS` (4000) chars, which can wrap to 60+ lines of narrow prose in the live (non-``) - region, re-painted each frame — a taller-than-viewport live block that can flicker on a short terminal (the same - risk class the `liveTokens` answer stream already carries; the panel is opt-in + collapsed by default, so LOW). - A follow-up could tail the expanded body to the last N rendered lines. *(low · apps/cli/src/render/tui/chat-ink.tsx)* -- [ ] **Allow `Ctrl+T` / `/thinking` during a pending approval.** The fail-closed approval keyboard-ownership - (ADR-0057) swallows every key except `[y]/[a]/[n]/[esc]` — including the reasoning toggle, the one moment a user - might most want to expand the thinking to inform the decision. `toggle-reasoning` is a pure view flip (zero - session effect), so it would be safe to let it bypass the swallow. Deliberately left fail-closed for now (the - approval floor is security-sensitive). *(low · apps/cli/src/render/tui/chat-input.ts)* +- [x] **Compact abort hint during token streaming — DONE (2.5-close Step 12, Batch B; PR #69).** A pure + `streamingAbortHint(busy)` renders a compact dim `Esc to stop` line beneath the streaming CONTENT (a STATUS line + already carries its inline hint), so the abort affordance persists for the whole turn (EA7). *(apps/cli/src/render/tui/chat-projection.ts + chat-ink.tsx)* +- [x] **Bound the EXPANDED reasoning panel by rendered LINES, not just chars — DONE (2.5-close Step 12, Batch B; PR #69).** + `formatReasoningPanel` now tails the expanded body to the last `MAX_REASONING_PANEL_LINES` (12) RENDERED rows + (each logical line counts as `ceil(len/columns)` wrapped rows; a single over-budget line is head-sliced), so a + full 4000-char buffer cannot wrap into a flickering, screen-filling panel on a short terminal. The live width is + threaded via a `columns` prop (ChatApp reads `process.stdout?.columns`; the Home passes its resize-tracked + `size.cols`). *(apps/cli/src/render/tui/chat-projection.ts + chat-ink.tsx + home-app.tsx)* +- [x] **Allow `Ctrl+T` / `/thinking` during a pending approval — DONE (2.5-close Step 14, D-4; PR #69).** + `reduceApprovalKey` whitelists exactly the view-only reasoning toggle (Ctrl-without-meta `t` → `toggle-reasoning`, + a pure store repaint with zero session/approval/decision effect) through the fail-closed swallow, so a user can + expand the thinking to inform the decision; every other key (mode cycle, edits, the most-permissive approve/reject + chord) stays swallowed. *(apps/cli/src/render/tui/chat-input.ts)* ## Sonar code-quality backlog diff --git a/docs/roadmap/phases/phase-2.5-cli-consolidation.md b/docs/roadmap/phases/phase-2.5-cli-consolidation.md index 939f38c1..9425a9bc 100644 --- a/docs/roadmap/phases/phase-2.5-cli-consolidation.md +++ b/docs/roadmap/phases/phase-2.5-cli-consolidation.md @@ -1,6 +1,8 @@ # Phase 2.5 — CLI Consolidation and Conversational Home -> Status: In progress. **2.5.A** (shared tool-environment factory + capability-gap root-cause fix) is +> Status: ✅ **COMPLETE** (milestone **M2.5-4**, merged to `main` via **PR #69**, 2026-07-08) — all workstreams +> **2.5.A–J** shipped; the go/no-go exit criteria hold; Phase 2.6 (conversational authoring) is next. History below. +> **2.5.A** (shared tool-environment factory + capability-gap root-cause fix) is > ✅ **Done (PR #60, 2026-06-28)**, behind [ADR-0055](../../decisions/0055-cli-host-capability-seam-tool-environment-factory.md) > — **milestone M2.5-1 (secure base) reached**. Spine continues: **2.5.B** (Home) ✅ → **2.5.C** (slash registry > + palette + `/help`/`/doctor`/`/workflows`/`/cost` + footer hint-bar) ✅ **Done (PR #62, 2026-06-30)** @@ -14,9 +16,9 @@ > **Done (PR #66, 2026-07-07)** behind [ADR-0063](../../decisions/0063-cli-config-write-contract.md)/[ADR-0064](../../decisions/0064-live-model-catalog.md)/[ADR-0065](../../decisions/0065-provider-economics-and-extensibility.md) > (+ the ADR-0059/0066 model-UX follow-up) — **milestone M2.5-2 reached**. The consolidation lanes **2.5.I** > (harness + concurrency) and **2.5.J** (docs-debt + `NO_COLOR`/`FORCE_COLOR`/`--color`) ✅ **Done (2.5-close-out, -> 2026-07-08)** — **milestone M2.5-4 reached**, alongside the doable-now Batch A–E backlog (two `gate.ts`-resume -> items — `budget resume` + secret re-provide — deferred to a focused follow-up; `extraRoots` blocked on its -> config source). **Phase 2.5 is complete.** +> PR #69, 2026-07-08)** — **milestone M2.5-4 reached**, alongside the doable-now Batch A–E backlog (two +> `gate.ts`-resume items — `budget resume` + secret re-provide — deferred to a focused follow-up; `extraRoots` +> blocked on its config source). **Phase 2.5 is complete — merged to `main` via PR #69.** - **Related**: [../README.md](../README.md), [phase-2-cli.md](phase-2-cli.md), [phase-2.6-conversational-authoring.md](phase-2.6-conversational-authoring.md), [phase-3-desktop.md](phase-3-desktop.md), [../../reference/cli/commands.md](../../reference/cli/commands.md), [../../reference/cli/chat-session.md](../../reference/cli/chat-session.md), [../../reference/cli/regression-harness.md](../../reference/cli/regression-harness.md), [../../decisions/README.md](../../decisions/README.md) (ADR-0054–0057) @@ -517,9 +519,9 @@ follow-up) lands with EA2's accuracy surface. truncation is visible; each operational error class renders an actionable recovery hint with the session intact; the seam is not modified. -### 2.5.I — Regression harness and concurrency hardening — ✅ **Done (2.5-close, 2026-07-08)** +### 2.5.I — Regression harness and concurrency hardening — ✅ **Done (2.5-close, PR #69, 2026-07-08)** -> **Status:** ✅ **Done (2.5-close-out, 2026-07-08).** `loadFull` is wrapped in a single read transaction +> **Status:** ✅ **Done (2.5-close-out, PR #69, 2026-07-08).** `loadFull` is wrapped in a single read transaction > (torn-read guard); every multi-statement write txn is `BEGIN IMMEDIATE` with a bounded, deterministic > `SQLITE_BUSY`/`SQLITE_LOCKED` retry (`withBusyRetry`, over the pre-existing WAL + `busy_timeout`); the > concurrent chat+run e2e (two-connection coexistence + two-process contention), the `Home → chat → resume → @@ -545,9 +547,9 @@ Windows. performance budgets (Home cold-open at 1000 sessions; 80×24 minimum) hold; the harness gates the backward-compatibility exit criterion. -### 2.5.J — Documentation reconciliation and dead-code cleanup — ✅ **Done (2.5-close, 2026-07-08)** +### 2.5.J — Documentation reconciliation and dead-code cleanup — ✅ **Done (2.5-close, PR #69, 2026-07-08)** -> **Status:** ✅ **Done (2.5-close-out, 2026-07-08).** The "encrypted history" wording is corrected to the +> **Status:** ✅ **Done (2.5-close-out, PR #69, 2026-07-08).** The "encrypted history" wording is corrected to the > accurate CLI posture (unencrypted, protected by `0700`/`0600` + keychain, ADR-0050) across `uvp.md` / > `vision.md` / the CLI tutorial + the README milestone spine; color resolution honors `--color` / > `--no-color` > `NO_COLOR` (any non-empty ⇒ off) > `FORCE_COLOR` (`0`/`false` ⇒ off, the supports-color @@ -575,7 +577,8 @@ correct); reconcile the roadmap status surfaces (`docs/roadmap/current.md`, [phase-2-cli.md](phase-2-cli.md), [CLAUDE.md](../../../CLAUDE.md)) now that **2.R and 2.J have both landed**, and complete the central roadmap narrative for 2.5/2.6 ([../README.md](../README.md) — the phase index and the dependency graph already include them; reconcile the milestone-spine prose); handle the -`NO_COLOR` / `FORCE_COLOR` env standards (today only the `--no-color` flag is honoured). +`NO_COLOR` / `FORCE_COLOR` env standards — now resolved per the `--color` / `--no-color` > `NO_COLOR` > `FORCE_COLOR` > default-on order, +orthogonal to `--json`/CI, with a full test matrix. **Acceptance:** no tracked doc claims the CLI `history.db` is encrypted; the roadmap reflects the landed state; `NO_COLOR` is honoured. @@ -587,7 +590,7 @@ state; `NO_COLOR` is honoured. | M2.5-1 Secure base ✅ **(PR #60, 2026-06-28)** | 2.5.A | Root-cause closed (capability gap + merge asymmetry); host seam reviewed | | M2.5-2 Home + entry + onboarding ✅ **(PR #66, 2026-07-07)** | 2.5.B + 2.5.C + 2.5.D + 2.5.F + 2.5.G | First-class entry + ergonomics + onboarding | | M2.5-3 Modes + observability ✅ **(2026-07-07)** | 2.5.E + 2.5.H | Safe reseat-less mode system + per-tool approval + reasoning render + actionable errors | -| M2.5-4 Consolidation ✅ **(2.5-close, 2026-07-08)** | 2.5.I + 2.5.J | Harness + concurrency + docs-debt (+ the doable-now Batch A–E backlog; two `gate.ts`-resume items deferred to a follow-up) | +| M2.5-4 Consolidation ✅ **(2.5-close, PR #69, 2026-07-08)** | 2.5.I + 2.5.J | Harness + concurrency + docs-debt (+ the doable-now Batch A–E backlog; two `gate.ts`-resume items deferred to a follow-up) | ## Sequencing & parallelization @@ -607,6 +610,8 @@ parallel at any point. ## Exit criteria (go / no-go → Phase 2.6) +> ✅ **All five met** at phase close (PR #69, 2026-07-08) — Phase 2.6 (conversational authoring) is unblocked. + 1. `relavium` opens Home in a TTY; the `--json` / CI / non-TTY backward-compatibility is proven by the extended regression harness. 2. The capability-gap root cause is closed (`tool_unavailable`, real usage, advertise-filter, merge diff --git a/docs/roadmap/phases/phase-2.6-conversational-authoring.md b/docs/roadmap/phases/phase-2.6-conversational-authoring.md index 2f229ea7..fd209b25 100644 --- a/docs/roadmap/phases/phase-2.6-conversational-authoring.md +++ b/docs/roadmap/phases/phase-2.6-conversational-authoring.md @@ -1,226 +1,690 @@ -# Phase 2.6 — Conversational Authoring and Parity +# Phase 2.6 — Conversational Authoring and the First-Class CLI -> Status: Planned. Depends on the Phase 2.5 spine (the wired tool-environment and the per-tool -> approval / mode system). Spine: 2.6.A (`@relavium/authoring` package) → 2.6.B (conversational -> authoring agent). Additive: 2.6.C / D / E. +> Status: Planned — **next up**. Depends on the Phase 2.5 spine (the wired tool-environment and the +> per-tool approval / mode system), which is **complete** (M2.5-4, PR #69, 2026-07-08), so this phase is +> now unblocked. +> +> **Rewritten 2026-07-08** (after the Phase-2.5 close), expanding the original authoring-and-parity scope +> into the phase that makes the CLI a **first-class, Home-centric product**. The rewrite folds three +> inputs: the maintainer's CLI-experience findings, a competitor research pass (opencode, Claude Code, +> Codex CLI, Gemini CLI, Temporal/`gh run`-class run tooling, Aider/Goose/Amp/Cursor/Copilot), and a +> line-by-line triage of [../deferred-tasks.md](../deferred-tasks.md) (the now-doable items are pulled in +> below, each mapped to a workstream). Workstreams **2.6.A–E keep their original identities** (they are +> referenced by ADR-0058/0059/0060 and the deferred-tasks doc); **2.6.F–M are new**. > > **Note (2026-07-07):** **2.6.C**'s mid-session `/models` model **reseat shipped early in 2.5.G** (ADR-0059, -> PR #66, merged 2026-07-07); the "mid-session model switching" mentions in the Goal / Outcomes / In-scope / -> Milestones / Exit-criteria below are therefore already realized — see §2.6.C. This phase is otherwise Planned. - -- **Related**: [../README.md](../README.md), [phase-2.5-cli-consolidation.md](phase-2.5-cli-consolidation.md), [phase-2-cli.md](phase-2-cli.md), [phase-3-desktop.md](phase-3-desktop.md), [phase-4-vscode.md](phase-4-vscode.md), [../../reference/contracts/workflow-yaml-spec.md](../../reference/contracts/workflow-yaml-spec.md), [../../reference/contracts/agent-yaml-spec.md](../../reference/contracts/agent-yaml-spec.md), [../../reference/shared-core/node-types.md](../../reference/shared-core/node-types.md), [../../decisions/README.md](../../decisions/README.md) (ADR-0058–0060) - -The second half of the consolidation work, split out from -[phase-2.5-cli-consolidation.md](phase-2.5-cli-consolidation.md) because it is ADR-heavy and depends on -that spine. It realises the product's tagline — *"Start as an agent. Ship the workflow."* — at the -terminal: a conversation can now **author** a standards-valid Relavium workflow/agent, switch models -mid-session, and reach competitor-parity ergonomics. +> PR #66, merged 2026-07-07); 2.6.C is retained for the residual per-model cost-breakdown read and as the +> cross-reference home. + +- **Related**: [../README.md](../README.md), [phase-2.5-cli-consolidation.md](phase-2.5-cli-consolidation.md), [phase-2-cli.md](phase-2-cli.md), [phase-3-desktop.md](phase-3-desktop.md), [phase-5-managed-inference.md](phase-5-managed-inference.md), [node-runtime-upgrade.md](node-runtime-upgrade.md), [../deferred-tasks.md](../deferred-tasks.md), [../../reference/cli/commands.md](../../reference/cli/commands.md), [../../reference/cli/home.md](../../reference/cli/home.md), [../../reference/cli/chat-session.md](../../reference/cli/chat-session.md), [../../reference/shared-core/built-in-tools.md](../../reference/shared-core/built-in-tools.md), [../../reference/contracts/config-spec.md](../../reference/contracts/config-spec.md), [../../reference/contracts/workflow-yaml-spec.md](../../reference/contracts/workflow-yaml-spec.md), [../../reference/contracts/agent-yaml-spec.md](../../reference/contracts/agent-yaml-spec.md), [../../decisions/README.md](../../decisions/README.md) (ADR-0058–0060 + the new ADRs below) + +The second half of the consolidation work begun in +[phase-2.5-cli-consolidation.md](phase-2.5-cli-consolidation.md), now widened into the phase that finishes +the CLI as a product. It realizes the tagline — *"Start as an agent. Ship the workflow. Own every run."* — +**entirely inside the terminal**: a conversation authors a standards-valid workflow, the Home starts and +monitors it, the run history is drillable to per-node detail, and every management task (providers, models, +MCP, settings, gates) is doable from the Home without dropping to a shell subcommand. When this phase +closes, `relavium` in a terminal is a first-class experience on par with the best agentic CLIs — while +keeping the postures they lack: OS-keychain-only secrets, a fail-closed approval floor, and git-committable +YAML artifacts. + +## TL;DR + +Phase 2.6 makes bare `relavium` a **full-screen, Home-centric** product. Key outcomes in 5 milestones: + +- **Conversational authoring** — a free-text request produces a strict-valid `.relavium.yaml`, + authored and run from the Home; `{{ctx.*}}` interpolation and `agent run --input` land. +- **Everything managed from the Home** — providers, MCP servers, agents, workflows, runs, and + run-ops (budget/gate resume) with no shell subcommand required. +- **Drillable, attributed run history** — list → run → node drill-down with per-node model/agent/cost + attribution, bounded tool traces, and cross-process live watch. +- **Competitor-toolbelt parity** — `edit_file`, `search_files`, `find_files`, `todo`, `ask_user`, + working `web_search`, each YAML-selectable and mode-gated. +- **Settings, theming, localization** — `/settings` over the config-write contract; three built-in + themes (default / high-contrast / colorblind-safe); `en` + `tr` i18n with CI key-parity. +- **Onboarding v2** — two auth paths: BYOK (live) + Relavium-account stub (disabled, Phase 5). +- **13 workstreams** (2.6.A–M), substrate-first: 2.6.F (full-screen TUI + Node 22 floor) runs first. ## Goal -Let a `relavium chat` conversation produce a **standards-valid** `.relavium.yaml` from a free-text -request, promote the existing authoring core into a shareable `@relavium/authoring` package so every -surface can consume it, enable mid-session model switching, and close the remaining parity polish — all -on top of the Phase 2.5 tool-environment and approval system. +Make the bare `relavium` invocation a **full-screen, Home-centric** surface from which the entire product +is used and managed; close the **tool-breadth gap** against competitor CLIs (edit, search, find, todo, +ask-user, working web search) under the existing governance floor; land **conversational authoring** on the +shared `@relavium/authoring` core; give runs a **three-level drill-down** (list → run → node) over durable, +attributed history; and ship **settings, theming, and localization** — all without breaking the `--json` / +CI / non-TTY contract ([ADR-0049](../../decisions/0049-cli-machine-output-contract.md)) or any existing +subcommand. ## Outcomes (Definition of Done) -- A `@relavium/authoring` package (`@relavium/authoring`) wraps parse / validate / serialize / scaffold - for workflows and agents; the CLI's `create` / `import` / `export` consume it; desktop and VS Code can - too. -- Every authored artifact passes a single `validateAuthoredWorkflow` pre-flight (parse **and** catalog - validation); a failure returns a field-named, secret-free error the model self-corrects against. -- A conversational authoring agent turns a free-text request into a strict-valid `.relavium.yaml`, writes - it only under accept-edits/auto with a scope-tiered host, and offers to `/run` it. -- `/models` switches the bound model **mid-session** (a host-side reseat), carrying cost/turn and - persisting per-message model attribution — with an explicit tool-context-loss notice. -- Session `{{ctx.*}}` prompt interpolation lands, unblocking `agent run --input`; parity polish - (`/rewind`, `/fork`, advanced `@`-injection, markdown render, `/theme`) ships. +- `relavium` on a TTY opens a **full-screen** (alternate-screen) Home with a scroll/auto-follow viewport; + the inline renderer remains as an escape hatch; `--json` / `CI` / non-TTY behavior is byte-identical to + today (regression-harness proven). +- **Everything is manageable from the Home**: provider + key CRUD, model picking, MCP server CRUD + + status, settings (theme/language/preferences), workflow start/monitor/history with node-level + drill-down, agent start (any catalog agent) + session resume, gate resolution (human **and** budget), + and authoring (wizard + conversational). No routine task requires a shell subcommand; subcommands stay + first-class for scripting/CI and are never removed. +- A `@relavium/authoring` package is the shared authoring core; a conversational request produces a + strict-valid, catalog-validated `.relavium.yaml`, written only under approval, offered to run it in-place. +- The **toolbelt** reaches competitor breadth — `edit_file`, `search_files`, `find_files`, todo/plan, + `ask_user`, an actually-working `web_search` — each engine-pure, YAML-selectable, mode-gated, and + rendered first-class (collapsible detail, diffs at the approval prompt) behind a security-reviewed + render contract. +- Session `{{ctx.*}}` interpolation lands (ADR-0060) and `agent run --input` is unblocked. +- Run history is **attributed and drillable**: per-node model/agent/cost durable, a bounded secret-free + tool trace, gate-resolve TOCTOU closed at the store, crashed runs reconciled, cross-process runs + watchable live at node granularity. +- The CLI speaks **`en` and `tr`** over a string catalog with CI key-parity, and ships a real theme system + (default + high-contrast + colorblind-safe) with the color-free path staying legible. +- The onboarding wizard offers **two auth paths** — BYOK (live) and *Sign in with a Relavium account* + (visible, disabled, honestly labeled as coming with managed inference) — behind an Accepted + forward-design ADR. ## Scope ### In scope -- The `@relavium/authoring` package promotion + catalog-aware pre-flight back-port; the conversational - authoring agent + its product-side knowledge pack; mid-session model reseat; session `{{ctx.*}}` - interpolation; and the parity/polish lane. +The workstreams below: the authoring spine (2.6.A/B/D), the platform + full-screen TUI foundation (2.6.F), +the Home management surfaces (2.6.G/H/I/J/K), the experience arms (2.6.C residual, 2.6.E, 2.6.L, 2.6.M), +and the deferred-tasks items each workstream absorbs (mapped in the +[pull-in table](#deferred-tasks-pulled-into-this-phase)). ### Explicitly out of scope (→ Phase 3 / later) -- Full-fidelity reseat tool-context (the persister/schema extension, 1.X/1.Z); `read_media` input (D12); in-app - scrollback/pager; a multi-pane dashboard. Tracked in [../deferred-tasks.md](../deferred-tasks.md). (`/compact` - model-summarised compaction was originally listed here as Phase 3; it **shipped in 2.5.F** per - [ADR-0062](../../decisions/0062-context-compaction-and-cli-history-commands.md) (PR #65). The **live provider - `/v1/models` fetch** was likewise listed here; it is now **built in 2.5.G** per - [ADR-0064](../../decisions/0064-live-model-catalog.md) — the Option-A live model catalog.) +- **Chat sub-agent spawn** (a child-session engine model — the largest deliberate parity gap left; it + belongs with the Phase-3 desktop multi-agent center and needs its own ADR; `invoke_agent` stays + workflow-only). +- The **`read_media` D12 cluster** (host `MediaReadAccess`, scope population, the result-shape contract) + and `@`-mention of media files — the dedicated, security-reviewed follow-up stands. +- **Full-fidelity reseat tool-context** (the 1.X/1.Z persister/schema extension) — the reseat keeps its + text-only-transcript notice. +- **File-snapshot undo** (opencode-style revert of a message *and its file changes*) — 2.6.E ships + conversation-level `/rewind`/`/fork` only. +- **retry-from-node** ([ADR-0040](../../decisions/0040-node-retry-budget-above-the-chain.md) Part B — needs the + run-attempt model). The 2.6.G run-detail browser must be designed so a later "retry from this node" + action slots in without rework, but the engine work is Phase 3. +- The **workflow-run `egress`/`os` arms** stay unwired (the recorded 2.5.E design boundary: + `build-engine.ts` wires `fs`+`process` only). Revisiting that boundary requires its own ADR — it is not + quietly reopened here. +- A multi-pane dashboard (desktop canvas territory), `output_schema` deep JSON-Schema conformance (new + validator dependency), the `plugin` ToolSource loader, and cursor pagination for the read commands + (scale-gated). Tracked in [../deferred-tasks.md](../deferred-tasks.md). + +### In-window maintenance obligations (not workstreams) + +Two items from [deferred-tasks.md](../deferred-tasks.md) fall inside the likely phase window and should be +actioned during it (maintainer calls, not workstreams): the **OpenAI Sora 2 shutdown (2026-09-24)** — +retarget or disable the 1.AH A3 Sora adapter arm before the date — and **enabling the live-nightly +conformance lane** (CI provider keys), which also unblocks the deferred media-in conformance fixtures. +Both stay tracked in their canonical [deferred-tasks.md](../deferred-tasks.md) entries. ## Work breakdown ### 2.6.A — `@relavium/authoring` package promotion + catalog-aware pre-flight -The authoring core already exists in-tree (`apps/cli/src/authoring/authoring.ts`, landed with 2.J): -it wraps `parseWorkflow` / `serializeWorkflow` / `parseAgent` / `buildAuthored` / `detectAndParse` from -`@relavium/core` and drives the `create` / `import` / `export` commands. **Decision (maintainer):** -promote it to a shared `@relavium/authoring` package so desktop ([phase-3-desktop.md](phase-3-desktop.md)) -and VS Code ([phase-4-vscode.md](phase-4-vscode.md)) can consume the same authoring core, not just the CLI. +Unchanged from the original plan. The authoring core exists in-tree (`apps/cli/src/authoring/authoring.ts`, +landed with 2.J) and is promoted to a shared `@relavium/authoring` package so desktop +([phase-3-desktop.md](phase-3-desktop.md)) and VS Code ([phase-4-vscode.md](phase-4-vscode.md)) can consume +the same core. **Tasks:** -- Scaffold `packages/authoring` (`@relavium/authoring`) — pure TS, platform-free (engine-purity holds) - — and **extract-and-decouple** the existing `apps/cli/src/authoring/` core into it (it is **not** a free - move: the core imports `CliError`, `discoverCatalog`, and `findProjectConfigDir` from `apps/cli`, which - a package may not import — a forbidden `packages → apps` back-edge). Cut those three: replace `CliError` - with a platform-free typed error the CLI maps to exit codes at the boundary; keep catalog **discovery** - and `findProjectConfigDir` CLI-side and pass the catalog **in**. Add an import-zone lint fence (Phase-0 - seam-fence pattern) banning `packages/authoring → apps/cli`. Follow the add-package procedure - ([.claude/skills/add-package/SKILL.md](../../../.claude/skills/add-package/SKILL.md)). +- Scaffold `packages/authoring` — pure TS, platform-free — and **extract-and-decouple** the existing core + (it is **not** a free move: `CliError`, `discoverCatalog`, and `findProjectConfigDir` are `apps/cli` + imports a package may not take — a forbidden `packages → apps` back-edge). Replace `CliError` with a + platform-free typed error the CLI maps to exit codes at the boundary; keep catalog **discovery** and + `findProjectConfigDir` CLI-side and pass the catalog **in**. Add an import-zone lint fence banning + `packages/authoring → apps/cli`. Follow + [.claude/skills/add-package/SKILL.md](../../../.claude/skills/add-package/SKILL.md). - Expose a single `validateAuthoredWorkflow(yaml, catalog)` = `parseWorkflow` **+** - `validateWorkflowWithCatalog` pre-flight. The existing `create` / `import` / `export` pre-flight is - **parse-only** (it does not call the catalog validator — only the run path does); **back-port** the - catalog-aware pre-flight so wizard-authored and conversationally-authored artifacts share one front - end and `create` can never accept a model/modality the run path rejects. -- Add direct unit tests for the authoring core (`detectAndParse` / `buildAuthored` / - `validateAuthoredWorkflow`) — today only the command wrappers are tested. + `validateWorkflowWithCatalog`, and **back-port** it into `create`/`import`/`export` (today those are + parse-only; only the run path catalog-validates), so `create` can never accept a model/modality the run + path rejects. +- Add direct unit tests for `detectAndParse` / `buildAuthored` / `validateAuthoredWorkflow` (today only the + command wrappers are tested). **Acceptance:** `@relavium/authoring` builds and imports **only** `@relavium/core` + `@relavium/shared` -(lint-fence enforced — no `apps/cli` back-edge); the CLI consumes it with `create` / `import` / `export` -round-tripping **unchanged** (regression-tested); `create` runs the same catalog-aware pre-flight the run -path uses; the core is directly unit-tested. **Required ADR: `@relavium/authoring` package + -conversational-authoring pre-flight contract.** +(lint-fence enforced); the CLI consumes it with `create`/`import`/`export` round-tripping **unchanged** +(regression-tested); `create` runs the same catalog pre-flight the run path uses; the core is directly +unit-tested. **Required ADR:** [ADR-0058](../../decisions/0058-relavium-authoring-package-and-conversational-authoring.md) +(Proposed → Accepted when this workstream begins). -### 2.6.B — Conversational workflow/agent authoring agent +### 2.6.B — Conversational + wizard authoring in the Home -A `relavium chat` request such as *"define a workflow with these agents…"* produces a strict-valid -`.relavium.yaml`. This is the sibling of `chat-export` (which **replays** a transcript into a workflow); -here the model **generates** the artifact. +The original conversational-authoring workstream, plus the in-Home wizard surface and two absorbed +deferred items. **Tasks:** -- Add an authoring agent (an `--agent` profile or a `/author` mode) whose system prompt references a - **product-side knowledge pack** — a model-readable cheat-sheet **derived from** the canonical specs - ([node-types.md](../../reference/shared-core/node-types.md), +- **Conversational authoring** (unchanged): an authoring agent (an `--agent` profile or a `/author` mode) + whose system prompt references a **product-side knowledge pack derived — never restated** — from + [node-types.md](../../reference/shared-core/node-types.md), [workflow-yaml-spec.md](../../reference/contracts/workflow-yaml-spec.md), - [agent-yaml-spec.md](../../reference/contracts/agent-yaml-spec.md)) and the Zod schemas, plus a minimal - valid example per node type. The knowledge is **derived, never restated** ([CLAUDE.md](../../../CLAUDE.md) - #8); a check proves no schema is duplicated. It **must not** live under `.claude/skills/` — those are - repo-development (Claude Code) procedures and the Relavium **product** agent never reads `.claude/`. -- The self-correct loop reuses the same `detectAndParse` / `validateAuthoredWorkflow` pre-flight: model → - YAML → pre-flight → field-named, secret-free error → model fixes (improve `AgentParseError` to carry - line/col, a Phase-2 follow-up). -- The artifact is written only under accept-edits/auto with the scope-tiered host (2.5.A/2.5.E), then the - Home offers *"Run it now with `/run`?"* — closing the author → run loop on one screen. -- **Discoverability of the UVP (proactive, opt-out):** because *"Start as an agent. Ship the workflow."* - is the product's promise, a chat that has run several tool turns or produced a reusable plan surfaces a - quiet, dismissible hint — *"turn this session into a workflow with `/export`"* — so the tagline is - discovered, not buried in a slash command. Opt-out via config; never interrupts a turn. - -**Acceptance:** a free-text request yields a strict-valid `.relavium.yaml` that passes the same pre-flight -as `relavium run`; an invalid draft is corrected via the secret-free error loop; the file is written only -with approval; the proactive `/export` hint appears (and is dismissible); a knowledge-restate check passes. -A security review of the write surface + the authored artifact's secret-taint gate passes. **Required ADR: -shared with 2.6.A.** - -### 2.6.C — Mid-session model reseat (`/models` mid-chat) - -> **Note (2026-07-07):** ADR-0059 was pulled forward and the `/models` mid-chat reseat **shipped early in -> 2.5.G**'s model-UX follow-up (PR #66, merged 2026-07-07). This workstream is retained for any residual -> Phase-2.6 polish and as the cross-reference home. - -A session binds one model for its lifetime ([ADR-0024](../../decisions/0024-agent-first-entry-point-agentsession.md); -the fallback plan is memoized). Switching the **model** mid-chat is therefore a host-side **reseat**: -reconstruct the transcript (`reconstructSessionState`) and start a new `AgentSession.resume` bound to the -new model/provider, carrying cost/turn. (Mode changes do **not** reseat — that is the reseat-less mode -system in 2.5.E.) - -**Tasks:** mid-chat `/models` performs the reseat; persist per-message `modelId` (the `session_messages` -schema already has the column — only the CLI persister wiring is missing); surface an explicit, shared -notice on the `chat-resume` family that **prior tool calls and file contents are not carried to the new -model** (the transcript is text-only; full-fidelity tool-context is Phase 3); show a per-model cost -breakdown. - -**Acceptance:** `/models` mid-chat continues the conversation on a new model from the next turn; cost/turn -carry; per-message model attribution persists; the context-loss notice is shown; "carries full context" is -never claimed. [ADR-0059](../../decisions/0059-cli-mid-session-model-reseat.md) is **Accepted (2026-07-06)** and -its refinement of [ADR-0024](../../decisions/0024-agent-first-entry-point-agentsession.md) is recorded **in -place** on ADR-0024 (the dated `> Amended 2026-07-06 …` note + the Related forward-link, documentation-style §7), -since it refines without reversing. **Required ADR: mid-session reseat (model-only) — refines ADR-0024 (done).** + [agent-yaml-spec.md](../../reference/contracts/agent-yaml-spec.md) and the Zod schemas (one minimal + example per node type; a no-duplication check gates acceptance; never under `.claude/` — the product + agent does not read repo-development skills). Self-correct loop: model → YAML → `detectAndParse` / + `validateAuthoredWorkflow` → field-named, secret-free error → model fixes. The artifact is written only + under accept-edits/auto with the scope-tiered host, then the surface offers *"Run it now?"* — closing + author → run on one screen. +- **In-Home authoring wizards**: bring `relavium create`'s wizard into the Home/chat palette (`/create` → + an ink-native agent/workflow wizard over the same injectable prompter seam), so authoring starts from + the Home, not only from a shell command. +- **`AgentParseError` reaches the chat surfaces** *(deferred pull-in)*: a malformed `.agent.yaml` on + `chat --agent` / `agent run` currently collapses to a generic exit-1 internal error; resolve the design + call (wrap into `CliError('invalid_invocation')` at `resolveChatAgent`, or teach the top-level renderer + to render a typed `AgentParseError` as an exit-2 invocation fault), revise the pinned test deliberately, + and relativize the echoed source path. The conversational self-correct loop depends on these diagnostics + being visible. +- **Import consent gate** *(deferred pull-in, security)*: gate the first spawn of an MCP `stdio` server + declared by an **untrusted-provenance** imported artifact behind explicit consent, and pin `npx` package + versions for auto-install servers ([ADR-0052](../../decisions/0052-inbound-mcp-client-package-lifecycle-registration.md) §2) + — the authoring/import path this phase matures is exactly the surface that makes this live. +- **Discoverability of the UVP** (unchanged): the proactive, dismissible, config-opt-out *"turn this + session into a workflow with `/export`"* hint. + +**Acceptance:** a free-text request yields a strict-valid `.relavium.yaml` passing the same pre-flight as +`relavium run`; an invalid draft is corrected via the secret-free loop; files are written only with +approval; `/create` works from the Home; a malformed agent YAML surfaces its field-named, positioned +diagnostic on every surface; the untrusted-import consent gate holds; a security review of the write +surface + the secret-taint gate + the import gate passes. **Required ADR:** shared with 2.6.A (ADR-0058). + +### 2.6.C — Mid-session model reseat (shipped early) — residual + +> **Shipped early in 2.5.G** (ADR-0059, PR #66, 2026-07-07): the `/models` mid-chat reseat, per-message +> `modelId` attribution, and the context-loss notice. Retained for the residual below and as the +> cross-reference home. + +**Tasks:** the per-model **cost breakdown** read (`/cost` gains a per-model section over the shipped +attribution columns); verify the context-loss notice covers the whole `chat-resume` family. + +**Acceptance:** `/cost` shows per-model spend for a reseated session; the notice is asserted on resume +surfaces. **Required ADR:** none (ADR-0059 is Accepted). ### 2.6.D — Session `{{ctx.*}}` prompt interpolation -The agent system prompt is passed verbatim today (no template resolution), and `agent run --input` is -reserved/rejected until this lands. The conversational authoring agent and reseat both want session -context, so the Phase-2 deferral is pulled forward. +Unchanged in intent; one absorbed deferred item makes the security mandate explicit. + +**Tasks:** resolve `{{ctx.*}}` in the session system prompt per +[ADR-0060](../../decisions/0060-session-ctx-prompt-interpolation.md) — template substitution only, with the +**per-variable provenance/taint marker** on `SessionContext` (`--input`-derived values are untrusted by +provenance and never resolve in system position); unblock `agent run --input k=v`. *(Deferred pull-in:)* +land the **parse-time gate on system-bound fields** — when trusted `{{inputs}}`/`{{ctx}}` are admitted into +system positions, untrusted `run.outputs`/`read_file` references there are rejected at parse (the analyze/ +collect gate), preserving the existing secret-taint protection. + +**Acceptance:** `{{ctx.*}}` resolves in a session prompt with the taint rule enforced and tested; +`agent run --input` is accepted and reaches the prompt; the ADR-0060 mandatory security review passes. +**Required ADR:** ADR-0060 (Proposed → Accepted). -**Tasks:** resolve `{{ctx.*}}` in the session system prompt against session-scoped variables (an engine -follow-up); unblock `agent run --input k=v`. +### 2.6.E — Chat & input parity polish -**Acceptance:** `{{ctx.*}}` resolves in a session prompt; `agent run --input` is accepted and reaches the -prompt. **Required ADR: session `{{ctx.*}}` interpolation** (engine amendment). +The competitor-parity chat ergonomics (the theme system moved to 2.6.L; tool-call rendering moved to +2.6.M). Everything here is TTY-interactive-only; the non-interactive contract is untouched. -### 2.6.E — Parity and polish +**Tasks:** + +- **Markdown + code-block rendering** in the transcript (minimal in-house renderer preferred; a markdown + dependency, if chosen, needs an ADR); syntax highlighting stays Phase 3. +- **Advanced `@`-injection**: glob / directory expansion respecting ignore files (the ADR-0061 follow-up; + the in-house matcher from the 2.5 close is the substrate). +- **`/rewind` + `/fork`** at conversation level: rewind truncates to a chosen prior message and continues + (via the `/clear`-style host-swap + `reconstructSessionState` machinery); fork branches a session into a + new `sessionId` preserving the original. File-change revert is explicitly out (see out-of-scope). +- **Type-ahead message queue**: messages typed while a turn runs are queued (navigate/edit the queue), sent + on turn end — with a steer-now affordance considered (send-as-interrupt). +- **Input history search**: `Ctrl+R` reverse search over per-project prompt history. +- **`$EDITOR` compose** (`Ctrl+G`): edit the pending prompt in the external editor. +- **`/copy`**: copy the last assistant reply (OSC-52 with a plain fallback). +- **`/help` v2**: a full-screen, sectioned help screen (commands, keybindings, modes) replacing the flat + text list, once 2.6.F's renderer lands. + +**Acceptance:** each affordance works in chat and the in-Home chat; `--json`/plain surfaces reject or +ignore them exactly as the ADR-0049 contract requires; the message queue and rewind have reducer-level +tests. **Required ADR:** none, unless a markdown dependency is chosen (then a small dedicated ADR). + +### 2.6.F — Platform floor + the full-screen TUI foundation + +The substrate workstream — it runs **first** because the browsers (2.6.G), settings screens (2.6.L), and +render-v2 (2.6.M) all build on it. + +**Tasks:** -The competitor-parity ergonomics that are valuable but not on the spine: `/rewind` + `/fork` (the engine -already has per-node-boundary checkpoints to build on); advanced `@`-injection (glob/directory, respecting -ignore files); basic markdown + code-block rendering with a table layout (syntax highlighting is Phase 3; -a markdown dependency, if chosen, needs an ADR); `/theme` with at least one **high-contrast** and one -**colorblind-safe** theme (and semantic markers — `✓`/`✗`/`⏸` — that survive `--no-color`/`NO_COLOR`, so -the experience is not colour-dependent, degrading to ASCII equivalents — `[v]`/`[x]`/`[||]` — when the -terminal lacks Unicode support so the glyphs never render as broken boxes). Note any screen-reader limitations of the raw-mode TUI as a -documented constraint with a non-TTY fallback. +- **Node dev/CI bump now**: `.nvmrc` 22 → 24 (Active LTS) — one line, non-breaking, no ADR. +- **Supported-floor decision** *(maintainer decision, recommended: take it early in-phase)*: raise the + published floor 20.12 → `>=22` — a SemVer-major for `relavium` that restores `better-sqlite3` prebuild + coverage (Node 20 is EOL) and unlocks ink 7 / `node:sqlite` / eslint 10 / vitest 5. Full analysis: + [node-runtime-upgrade.md](node-runtime-upgrade.md). This **supersedes + [ADR-0021](../../decisions/0021-node-sqlite-driver-better-sqlite3.md)** → its own governed PR behind a + superseding ADR. Ink 7 is evaluated in the same governed PR but adopted only if the floor is raised to + `>=22`; otherwise the renderer ships on the current ink major. +- **Full-screen renderer**: an alternate-screen (DECSET 1049) mode for the Home + chat with a real + viewport — scrollback inside the app (PgUp/PgDn, wheel where supported), **auto-follow** that pauses when + the user scrolls up and resumes at bottom, and the input pinned to the bottom row. This structurally + fixes the current bug where long responses clip/truncate content beyond the terminal height — scrolling + up must reveal the full response from the beginning without top-truncation. The inline (scrollback) + renderer is **retained** and byte-identical for non-TTY/CI; an explicit escape hatch (`--no-alt-screen` + or a config key) keeps the inline mode available on a TTY. If the full-screen renderer is deferred or + the inline mode is active, a separate inline-viewport fix must backfill the scrollback-preservation + behavior for long responses so that content above the visible area is never lost. Renderer choice is + orthogonal to session state (switching relaunches the view in place, conversation intact). The run TUI's + persistent plain-text exit summary is preserved on unmount. +- **TUI component test harness** *(deferred pull-in)*: the first CLI component-render harness (a new + devDependency — part of this workstream's ADR), so render-cadence bugs (the 2.5.H frozen-clock class) + get regression tests; add performance regression thresholds (frame time / render count) for the + full-screen frame loop. +- Resize/degrade behavior and the documented screen-reader constraint (raw-mode TUI) with the non-TTY + fallback — carried from the original 2.6.E and owned here. + +**Acceptance:** bare `relavium` on a TTY opens the full-screen Home; scroll + auto-follow work; the +`--json` / CI / non-TTY paths are byte-identical to today (harness-proven); the floor-bump ADR is Accepted +and released as a SemVer-major with migration notes; the component harness runs in CI with at least the +frozen-clock regression pinned. **Required ADRs:** the ADR-0021-superseding floor ADR + a full-screen +renderer & TUI-harness ADR. + +### 2.6.G — Home management browsers: workflows, runs, agents + +The interactive browsers that make the Home the management center. The interaction model follows the +proven three-level drill-down (list → detail → step detail) with `gh run`-style progressive disclosure +(every screen hints the next action) and picker-on-omitted-id semantics. The maintainer's separate-command +sketch (`/active-workflows`, `/workflow-run-history`, …) is **deliberately simplified** into two tabbed +browsers with argument deep-links — same reach, fewer commands. + +**Tasks:** + +- **`/workflows` browser** (Home + chat): tabs **Defined | Active | History**, deep-linkable as + `/workflows [defined|active|history]`. + - *Defined*: the disk catalog (slug, name, node count, last-run status/age; invalid files flagged) with + actions — **Run** (foreground: graduate into the live run view), **Run detached** (print the runId, + stay in Home), **Export**, reveal path. Starting a run from the Home is new — `run` stays shell-first + for scripting, but the Home can now launch. + - *Active*: live runs (status, current node, attempt, elapsed, cost) — including runs owned by **other + processes** via a poll-based `run_events` tail (seq + WAL make this feasible today), disclosed as + node-boundary granularity; a gate-/budget-blocked run shows the resolve affordance inline. Esc detaches + without killing; cancel is offered with its cooperative semantics. + - *History*: finished runs (status glyph, short id, workflow, relative start, duration, **cost** — the + differentiator no competitor CLI lists) with keystroke filters (status/workflow) — no query DSL. Enter + → **run detail**: header (status, timing, totals, entry point) + node table (per-node status, duration, + attempt, tokens, cost) + a "jump to first failed node" key. Enter on a node → **node detail**: the + input / output / duration / tokens / error quintet plus the bounded tool-trace and event timeline + (Scheduled/Started/Completed triplets collapsed into one expandable row). +- **`/agents` browser** (Home + chat): tabs **Defined | Sessions**. *Defined*: the agent catalog with + **"start a chat with this agent"** (closing the Home's built-in-agent-only gap). *Sessions*: recent + + in-progress sessions — Enter resumes **in place** (the in-Home chat machinery), with a detail view + (transcript summary, cost, model attribution). +- **Actionable Home strip**: the Attention/Continue rows become focusable — a gate row opens an inline + resolve card (approve / reject / input, via 2.6.K's shared resume core), a failed run opens its detail, + a session row resumes, a run row opens detail. The strip refreshes on an idle tick while the Home is + open (today it is snapshot-static). +- **Liveness**: wire `engine.reconcile()` (never invoked today) on Home open and the browser/status reads, + so crashed runs settle `run:failed{internal}` instead of showing as zombie `running` rows; expired gates + settle per their timeout policy via 2.6.K's re-arm. +- Non-interactive parity: extend the read *commands* minimally (`relavium list --runs`, `status ` + for finished runs, `logs --follow/--failed`) so scripting keeps pace with the TUI — + [ADR-0049](../../decisions/0049-cli-machine-output-contract.md)-conformant. + +**Acceptance:** every action above works from the Home without a shell command; the three-level drill-down +is complete over 2.6.H's data; a run started in another terminal is watchable live at node granularity; +zombie runs reconcile; the browsers degrade at <80×24; the machine-output contract is untouched +(harness-proven). **Required ADR:** management browsers + run drill-down contract (shared with 2.6.H). + +### 2.6.H — Durable run detail: the history data layer + +The store/engine half that 2.6.G's browsers read. Today the durable record is node-boundary-only and +unattributed (step rows never carry agent/model/input; `run_costs.modelId` is always NULL; the firehose is +never persisted; several consistency gaps are recorded in the deferred doc). This workstream makes the +durable record complete enough for a first-class drill-down — additively. + +**Tasks:** + +- **Step attribution**: populate `step_executions.agentId/agentSnapshot/modelId/inputJson` and + `run_costs.modelId` from the events the engine already has; give `node:skipped` a step row (thread + `nodeType` additively); widen the `StepRecord` projection (output/error/tokens) + a step-detail read. +- **Exact per-node cost**: an optional `nodeCostMicrocents` on `node:completed` — an additive run-event + schema field amending [ADR-0036](../../decisions/0036-run-loop-substrate-event-bus-and-execution-host.md) + append-only (optional for backward-compat with existing `--json` consumers; when present, the per-node + cost is exact, not a cumulative-delta approximation) — so parallel fan-out attributes exactly instead of + via cumulative deltas; carry final totals on `run:failed` / `run:cancelled` (closing the documented + undercount). +- **Bounded durable tool trace** *(ADR decision)*: persist secret-free per-step tool **summaries** — + toolId, the sanitized approval-preview target, outcome (ok/denied/failed), duration — never args or + result bytes; the token/reasoning firehose stays unpersisted (the ADR-0036 posture holds). This is what + the node-detail screen shows as "tool activity". +- **Store-level gate uniqueness** *(deferred pull-in)*: a uniqueness constraint on `human_gate:resumed` + per `(runId, gateId)`, closing the cross-process gate-resolve TOCTOU window at the store. +- **Consistency fixes** *(deferred pull-ins)*: wrap the run-resume reconstruction reads + (`loadRun` + `loadRunEvents` + `loadStepExecutions`) in one read transaction; make the chat persister's + per-turn writes atomic (`BEGIN IMMEDIATE` per turn); the **content-level workflow-identity guard** on + resume over the frozen `runs.workflow_definition_snapshot`. +- **Run-submission idempotency** *(deferred pull-in)*: a dedup guard for Home-launched runs (double-Enter + must not start two runs). + +**Acceptance:** a finished run's detail (attribution, exact per-node cost, tool summaries, skipped nodes) +is fully reconstructable from `history.db`; the TOCTOU constraint holds under a concurrent two-process +test; the schema/event changes are additive and `--json` consumers are unaffected; migration provided. +**Required ADR:** shared with 2.6.G (browsers + durable run-detail; amends ADR-0036 additively). + +### 2.6.I — Provider & MCP management from the Home + +Closing the two biggest "must drop to shell / must hand-edit TOML" gaps: provider/key CRUD outside the +onboarding wizard, and MCP servers, which today are managed **only** by hand-editing `[[mcp_servers]]` +config (there is no `relavium mcp` command at all; the only runtime visibility is `/doctor --deep`). + +**Tasks:** -**Acceptance:** `/rewind`/`/fork` work over the checkpoint substrate; `@`-injection handles globs/dirs; -markdown/code-blocks render; `/theme` switches themes including the high-contrast and colorblind-safe -options; the colour-free path stays legible via semantic markers. +- **`/providers`** (Home + chat palette): list providers with key status + redacted live verify; add a + provider (SSRF-validated base URL rules unchanged); set key (masked, keychain-only — the wizard's tested + `set-key` path); remove key (confirmed destructive); test. All over the existing `runProviderCommand` + cores — no new key-handling code paths. +- **`/mcp`** (Home + chat palette): list registered servers with a **read-only** status report (the + `/doctor --deep` machinery — never connects/spawns on open); add / edit / remove server registrations + (stdio + `http`/`sse`/`websocket` behind the existing SSRF floor); named secrets set via a masked prompt + into the isolated `mcp-secret:*` keychain namespace — **never** into TOML (env placeholders only). +- **`relavium mcp list/add/remove/test`** shell family for scripting parity (`--json`-conformant). +- **Config-write extension** *(ADR)*: extend the + [ADR-0063](../../decisions/0063-cli-config-write-contract.md) typed-setter contract to structured + `[[mcp_servers]]` writes (which config file owns a tool-written registration, the comment-loss caveat, + atomicity, secret-incapability by construction). +- **Deferred MCP cluster** *(pull-ins)*: the durable cross-invocation **tool-list cache** (~1h TTL, + transport-covering key — startup latency); **network header auth** + (`Authorization: Bearer {{secrets.}}` via the SDK transport's headers, resolved from + `mcp-secret:*`, never logged/serialized — [ADR-0052](../../decisions/0052-inbound-mcp-client-package-lifecycle-registration.md) §6); + **mid-call abort propagation** (the engine's `AbortSignalLike` forwarded to the in-flight `tools/call`, + so a mid-turn Esc cancels an MCP call instead of only tearing down); optionally the generalized + `SecretResolver` seam alongside header auth. + +**Acceptance:** providers and MCP servers are fully manageable from the Home and the shell; a network +server with header auth connects with its secret resolved from the keychain; discovery startup is +measurably faster with the cache; Esc aborts an in-flight MCP call; the mandatory security review of the +secret handling + config-write surface passes. **Required ADR:** MCP management surface + config-write +extension (extends ADR-0063; sits on ADR-0052/0053). + +### 2.6.J — Onboarding v2: two auth paths + the Relavium-account stub + +The first-run wizard gains the product's future shape without pulling Phase 5 forward. + +**Tasks:** + +- **Auth-path select** as the wizard's first step: + - **(a) Connect a provider (bring your own API key)** — the existing, live flow (provider select → + masked key → live validation with cause-aware retry → keychain → starter default model), unchanged. + - **(b) Sign in with a Relavium account** — **visible but disabled** ("coming with managed inference"), + rendered dimmed with an honest one-line note. Discoverable from day one, selectable never (this + phase). +- **The account-auth forward-design ADR** (Accepted in this phase; **implemented in Phase 5**, + [phase-5-managed-inference.md](phase-5-managed-inference.md), riding + [ADR-0012](../../decisions/0012-managed-inference-dual-mode.md)–[ADR-0015](../../decisions/0015-managed-mode-data-handling-and-compliance.md)). + It records the decided shape so the stub is honest: + - The account API key **determines entitlement server-side** — an individual subscription or an + organization membership activates automatically from what the key is provisioned for; the user never + picks at login. + - **No-subscription UX is a first-class state, never a dead end**: a typed, actionable message with the + BYOK path offered on the same screen. + - **BYOK always coexists**: a signed-in user with no (or an exhausted) subscription keeps full BYOK + provider use; at-limit degrades gracefully (finish the turn → wait/upgrade → BYOK fallback). + - Mechanics reserved for Phase 5: key-paste first (copy from the account portal), browser OAuth + + device-code later; keychain storage; plan-gated features; org-forced login method. +- Optional wizard polish once 2.6.L lands: a theme step; the outro points at `/help` + `/models`. + +**Acceptance:** a keyless first run shows both paths with (b) disabled and honestly labeled; path (a) is +regression-proven unchanged; the forward-design ADR is Accepted with Phase-5 ownership explicit. +**Required ADR:** onboarding auth paths + Relavium-account forward design. + +### 2.6.K — Run-ops: the resume-path follow-up (budget resume, secret re-provide, gate lifecycle) + +The focused follow-up the 2.5 close deliberately deferred — both headline items refactor the +security-sensitive `gate.ts` cross-process resume path, so they land together with fresh context. + +**Tasks:** + +- **Extract a shared cross-process resume core** from `gate.ts` (snapshot reload → checkpoint reconstruct → + `resumeFromCheckpoint` → drive) that the gate command, the budget command, and 2.6.G's inline resolve + card all consume. +- **`relavium budget resume [--approve|--abort]`** *(deferred pull-in)*: the documented command + over the engine's existing budget-gate resume; plus the Home affordance on budget-paused rows. +- **Secret re-provide on resume** *(deferred pull-in, security)*: let the operator re-supply a + `secret`-typed input on a cross-process resume (stdin-only, `provider set-key` discipline; or keychain + re-resolution keyed by the input `ref` (its stable identifier in the workflow YAML)), + relaxing today's fail-closed `MaskedSecret` exit-2 — behind a + **mandatory security review** (this deliberately relaxes a fail-closed guarantee into + allow-with-re-provisioning). +- **Gate timeout re-arm on rehydration** *(deferred pull-in)*: re-arm a still-pending gate's persisted + `expiresAt`/`timeoutAction` against a real clock on reconcile/resume, so a crash-while-paused run's + deadline is honored. +- **Exit-code fidelity** *(deferred pull-in)*: distinguish a gate park from a media-only park on + `run:paused` (media parks are reachable since 2.S) in the human message and, if decided, the exit code — + documented in [commands.md](../../reference/cli/commands.md). +- **Session budget pause/resume** *(deferred pull-in, engine)*: the chat cost-cap `pause_for_approval` + rides the EA4 pause/resume machine (today it settles the turn loudly) — the ADR-0028 session arm. + +**Acceptance:** a budget-paused run is resumable from the CLI and the Home; a secret-bearing run is +resumable via re-provide with the security review passed; timers re-arm; a chat hitting its cost cap can +pause-and-approve instead of failing the turn; the shared resume core is the only resume path (no +duplication). **Required ADR:** none new (rides ADR-0028/ADR-0006); the security review is the gate. + +### 2.6.L — `/settings`, the theme system, and localization + +The personalization arm: a settings surface over an extended config-write contract, a real theme system, +and the first localized agentic CLI (a genuine differentiator — competitor i18n is white space). + +**Tasks:** + +- **Theme system**: a palette abstraction replacing the hardcoded literal ink colors; named built-ins — + `default`, `high-contrast`, `colorblind-safe` (the accessibility pair carried from the original 2.6.E), + and a terminal-respecting `ansi` theme (`NO_COLOR`/`--no-color` overrides every theme identically: all + color codes dropped, semantic markers degraded — the `ansi` theme is not a color-free bypass); + `[preferences].theme` (schema-present, read by nothing today) finally read; **`/theme`** switcher with + live preview. Semantic markers (`✓`/`✗`/`⏸`) survive `--no-color`/`NO_COLOR` and degrade to ASCII + (`[v]`/`[x]`/`[||]`) without Unicode. +- **`/settings`** (Home + chat): a sectioned screen (appearance / language / chat defaults / update + channel) over the **extended** [ADR-0063](../../decisions/0063-cli-config-write-contract.md) typed-setter + (new keys: `theme`, `language`; still global-preferences-only, atomic, secret-incapable by construction — + project files stay hand-authored). +- **i18n foundation**: an in-house string catalog (data ≠ code — zero conditional logic in translation + data; no runtime dependency expected, else ADR); `[preferences].language`; locales **`en` + `tr`** + in-phase; a CI **key-parity test** (fails on missing/extra keys) + a dead-string lint — landing the + deferred i18n standard as a `docs/standards/` entry. Acceptance includes IME-safe input and + wide-character-aware layout, laying the groundwork for future CJK locales. +- Diagnostics/`--json` output stays English-stable (machine contract); localization applies to the + interactive surfaces. + +**Acceptance:** `/settings` edits persist atomically and round-trip; themes switch live incl. the +accessibility pair; the color-free path stays legible; interactive surfaces run fully in `tr` with +CI-enforced key parity; diagnostics and `--json` remain English-stable per +[ADR-0049](../../decisions/0049-cli-machine-output-contract.md); the machine-output contract is +character-for-character unaffected. **Required ADR:** i18n + +theming architecture. + +### 2.6.M — The first-class toolbelt: breadth + rendering + +Closing the tool gap against competitor CLIs. Today the registry has 13 built-ins with **no** file +edit/patch, **no** content search, **no** find, **no** todo, **no** ask-user, and a dormant `web_search` +(needs an unwired credential resolver). Every addition is engine-pure (Zod args, `llmVisibleParams`, +policy class, bounded results), documented in +[built-in-tools.md](../../reference/shared-core/built-in-tools.md), selectable in agent/workflow YAML, and +sits under the existing governance floor (advertise-filter + fail-closed approval + protected-path/ +sensitive-read floors). + +**Tasks:** + +- **`edit_file`** — exact old→new string replacement (+ `replace_all`, uniqueness guard, read-before-edit + safety), `fs_write`-governed with a **diff preview** at the approval prompt. The single biggest gap. +- **`search_files`** — bounded in-house content search (workspace-jailed, sensitive-read floor, + ignore-file-respecting; linear matcher — no regex-DoS surface), idempotent. +- **`find_files`** — glob file finding across the tree (mtime-sorted, bounded), idempotent — promoting the + current read_file/list_directory glob options into a first-class discovery tool. +- **Todo/plan tool** — a structured, session-scoped task list the model maintains, rendered as a live TUI + checklist (persists across compaction); ungoverned (no host arm). +- **`ask_user`** — a structured mid-turn question (options + free text) on interactive surfaces via a + keyboard-owning overlay; typed `tool_unavailable` on non-interactive surfaces; workflows keep + `human_gate`. +- **`web_search` activation** *(deferred pull-in)*: wire the `egressCredentialResolver` from the keychain + (today a configured search 401s) and document the config-pinned provider contract; `http_request` + unchanged. +- **`extra_roots`** *(deferred pull-in)*: the `[chat].extra_roots` config key + factory wiring, unblocking + the `project` fs tier's documented allowlist (narrow-only, never a jail hole). +- **Tool-call rendering v2** (with 2.6.F's renderer): keep the collapsed one-line annotation as the + default, add a **details view** (a `/details` toggle or transcript overlay) revealing *sanitized, + bounded* target/arg previews and result summaries, and **diff rendering** (width-adaptive stacked / + side-by-side) for `edit_file`/`write_file` at the approval prompt and in details. This deliberately + revises 2.5's never-render-args posture — it is sanctioned only via the ADR + a security review (every + string through the shared sanitize floor; secrets structurally excluded; bounded). +- **Target-scoped approval cache** *(deferred pull-in)*: key `[a]lways` grants by `(toolId, target class)` + (path prefix / host / MCP server) instead of tool id alone, and give `mcp_call`/`web_search` a structured + `{server, tool}`/query preview so their blank-preview once-only downgrade becomes a real reviewable + grant. +- **Default chat agent grant review**: widen the built-in agent's grant to the new idempotent read tools + (search/find/todo); write/exec/egress stay opt-in via mode + approval. + +**Acceptance:** the toolbelt covers read / edit / search / find / exec / web / todo / ask-user; each tool +is YAML-selectable and correctly mode-gated on every surface; render v2 ships with diffs and the details +toggle behind a passed security review; the approval cache is target-scoped; the tool-gap table in the +research record is closed or explicitly deferred per item. **Required ADR:** toolbelt additions + +tool-render/approval-preview contract (extends ADR-0029/ADR-0057 posture). + +## Deferred-tasks pulled into this phase + +The [deferred-tasks.md](../deferred-tasks.md) triage (2026-07-08) mapped these confirmed-open items into +workstreams — each stays checked off **only** in the PR that lands it: + +| Deferred item (deferred-tasks.md) | Lands in | +|---|---| +| Node floor: dev/CI bump + supported-floor decision (EOL Node 20) | 2.6.F | +| CLI render-layer (ink component) test harness | 2.6.F | +| `AgentParseError` invisible on `chat --agent` / `agent run` | 2.6.B | +| MCP `stdio` import-trust/consent gate + `npx` pinning (ADR-0052 §2) | 2.6.B | +| Parse-time gate on system-bound fields (trusted `{{ctx}}`) | 2.6.D | +| `@`-glob / directory expansion (ADR-0061) | 2.6.E | +| Cross-process gate-resolve TOCTOU (store uniqueness) | 2.6.H | +| Run-resume torn-read wrap · chat-persister turn atomicity | 2.6.H | +| Content-level workflow-identity guard on resume | 2.6.H | +| Run-submission idempotency / double-submit dedup | 2.6.H | +| MCP tool-list cache · network header auth (§6) · mid-call abort | 2.6.I | +| `relavium budget resume` command (documented, unimplemented) | 2.6.K | +| Re-provide `secret`-typed inputs on cross-process resume | 2.6.K | +| Re-arm a still-pending gate's timeout on rehydration | 2.6.K | +| `run:paused` gate-park vs media-park exit distinction | 2.6.K | +| Session budget pause/resume (EA4 ride; 1.V follow-up) | 2.6.K | +| i18n CI key-parity + data/code separation standard | 2.6.L | +| Live `web_search`/`http_request` egress credential resolver | 2.6.M | +| `project`-tier `extraRoots` allowlist (config source now exists) | 2.6.M | +| Target-scoped approval cache + structured MCP preview | 2.6.M | +| Approval-consent-line zero-width hardening · shared `[c]` reducer | 2.6.M (with render v2) | + +Items evaluated and **kept deferred** (Phase-3/1.AH homes, SDK-blocked, accepted residuals, or +scale-gated) remain in [deferred-tasks.md](../deferred-tasks.md) with their reasons — notably the +`read_media` D12 cluster, chat sub-agents, retry-from-node, the desktop tool host, and the media/seam +1.AH cluster. ## Milestones | In-phase | Completed by | Outcome | |----------|--------------|---------| -| M2.6-1 Authoring core shared | 2.6.A | `@relavium/authoring` + catalog-aware pre-flight | -| M2.6-2 Conversational authoring | 2.6.B + 2.6.D | "define a workflow…" produces valid YAML; `{{ctx.*}}` lands | -| M2.6-3 Reseat + parity | 2.6.C + 2.6.E | Mid-session model switch + parity polish | +| M2.6-1 Foundation | 2.6.F | Node floor decided/shipped; full-screen renderer + TUI harness — the substrate for every arm | +| M2.6-2 Authoring core shared | 2.6.A | `@relavium/authoring` + catalog-aware pre-flight | +| M2.6-3 Conversational authoring | 2.6.B + 2.6.D | "define a workflow…" → valid YAML in the Home; `{{ctx.*}}` lands | +| M2.6-4 The Home-managed CLI | 2.6.G + 2.6.H + 2.6.I + 2.6.J + 2.6.K | Every management task doable from Home; drillable, attributed run history; onboarding v2 | +| M2.6-5 First-class experience | 2.6.C + 2.6.E + 2.6.L + 2.6.M | Toolbelt parity + settings/theme/i18n + chat polish — **phase close** | ## Sequencing & parallelization -2.6.A first (the package + shared pre-flight). Then 2.6.B (conversational authoring, depends on A + the -2.5.E approval/write path) in parallel with 2.6.C (reseat) and 2.6.D ({{ctx.*}}); 2.6.E is additive -polish throughout. +```mermaid +flowchart LR + F["2.6.F
floor + full-screen
foundation"] --> G["2.6.G
browsers"] + F --> L["2.6.L
settings · theme · i18n"] + F --> E["2.6.E
chat polish"] + H["2.6.H
run-detail data"] --> G + K["2.6.K
run-ops resume core"] --> G + A["2.6.A
authoring package"] --> B["2.6.B
conversational authoring"] + D["2.6.D
ctx interpolation"] --> B + M["2.6.M
toolbelt"] -. render v2 after F .-> F + I["2.6.I
provider + MCP mgmt"] + J["2.6.J
onboarding v2"] +``` + +**2.6.F runs first** (the floor decision + renderer + harness gate every TUI-heavy arm). Then two parallel +spines: **(i) authoring** — 2.6.A → 2.6.B, with 2.6.D alongside; **(ii) experience** — 2.6.H and 2.6.K +early (data + resume core), then 2.6.G on top; 2.6.I and 2.6.J are independent and can land any time; +2.6.M's engine-pure tools can start immediately (only its render-v2 half waits on F); 2.6.E and 2.6.L +follow F. 2.6.C's residual is a small read, any time. ## Dependencies -- **Phase 2.5** complete — specifically 2.5.A (the wired write-capable tool-environment) and 2.5.E - (accept-edits per-tool approval), which gate the authoring write surface. -- **2.J** (the in-tree authoring core that 2.6.A promotes) — landed. - -## Exit criteria (go / no-go) - -1. `@relavium/authoring` is the shared authoring core; the CLI consumes it; `create` runs the catalog-aware - pre-flight. -2. A conversational request produces a strict-valid `.relavium.yaml` (pre-flight-proven, security-reviewed); - the knowledge pack is derived, not restated. -3. Mid-session `/models` reseat works with the context-loss notice and per-message attribution. -4. `{{ctx.*}}` interpolation lands and `agent run --input` is unblocked. -5. The required ADRs are Accepted. +- **Phase 2.5** complete — specifically 2.5.A (the wired tool-environment), 2.5.E (per-tool approval / + modes), 2.5.B/C (the Home + the two-registry command model), and ADR-0063 (config-write) — all met. +- **2.J** (the in-tree authoring core 2.6.A promotes) — landed. +- The Node supported-floor decision (2.6.F) gates the ink-7 evaluation but **not** the rest of the phase + (every workstream must land on the current floor if the maintainer defers the bump). + +## Exit criteria (go / no-go → Phase 3) + +1. `relavium` on a TTY opens the **full-screen Home**; `--json` / CI / non-TTY behavior is byte-identical + (regression-harness proven); the inline renderer remains available. +2. **The Home manages everything**: providers/keys, models, MCP servers, settings, workflow + start/monitor/history with node-level drill-down, agent start/resume, gate + budget resolution, and + authoring — no routine task requires a shell subcommand. (Subcommands and the ADR-0049 machine surface + are untouched and permanent; whether to de-emphasize interactive duplicates in `--help` is a phase-end + review decision, never a removal.) +3. A conversational request produces a strict-valid, catalog-validated `.relavium.yaml` + (pre-flight-proven, security-reviewed); the knowledge pack is derived, not restated; `{{ctx.*}}` lands + and `agent run --input` is unblocked. +4. The **toolbelt** ships (`edit_file`, `search_files`, `find_files`, todo, `ask_user`, working + `web_search`) — YAML-selectable, mode-gated, rendered with diffs/details behind a passed security + review. +5. Run history is attributed and drillable (list → run → node with the quintet + tool summaries); the + gate TOCTOU is store-closed; crashed runs reconcile; cross-process live watch works. +6. `/settings`, the theme system (incl. the accessibility pair), and **`en`+`tr`** localization ship with + CI key-parity. +7. The required ADRs are Accepted, and every touched spec's canonical home + ([commands.md](../../reference/cli/commands.md), [home.md](../../reference/cli/home.md), + [chat-session.md](../../reference/cli/chat-session.md), + [built-in-tools.md](../../reference/shared-core/built-in-tools.md), + [config-spec.md](../../reference/contracts/config-spec.md), + [database-schema.md](../../reference/desktop/database-schema.md)) is updated — no docs-debt carried + out of the phase. ## Required ADRs -Drafted as **Proposed** alongside this plan; each is reviewed and finalized (→ Accepted) when its -workstream begins. - -1. [ADR-0058](../../decisions/0058-relavium-authoring-package-and-conversational-authoring.md) — - `@relavium/authoring` package + conversational-authoring pre-flight contract (2.6.A/2.6.B). -2. [ADR-0059](../../decisions/0059-cli-mid-session-model-reseat.md) — mid-session model reseat; - refines [ADR-0024](../../decisions/0024-agent-first-entry-point-agentsession.md) (2.6.C). -3. [ADR-0060](../../decisions/0060-session-ctx-prompt-interpolation.md) — session `{{ctx.*}}` - interpolation, unblocking `agent run --input` (2.6.D). +> **This table is the canonical tracker for ADR status, owning workstream, and disposition across +> the phase.** Workstream sections reference it rather than restating ADR details. + +| # | ADR | Topic | Status | Workstream | +|---|-----|-------|--------|------------| +| 1 | [ADR-0058][] | `@relavium/authoring` + conversational authoring | Proposed | 2.6.A / 2.6.B | +| 2 | [ADR-0059][] | Mid-session model reseat | **Accepted** (shipped 2.5.G) | 2.6.C (residual) | +| 3 | [ADR-0060][] | Session `{{ctx.*}}` interpolation | Proposed | 2.6.D | +| 4 | *(new)* | Node supported-floor bump (`>=22`; supersedes ADR-0021) | Drafted when 2.6.F starts | 2.6.F | +| 5 | *(new)* | Full-screen TUI renderer + component test harness | Drafted when 2.6.F starts | 2.6.F | +| 6 | *(new)* | Management browsers + durable run detail (amends ADR-0036) | Drafted when 2.6.G starts | 2.6.G / 2.6.H | +| 7 | *(new)* | MCP management surface + config-write extension (extends ADR-0063) | Drafted when 2.6.I starts | 2.6.I | +| 8 | *(new)* | Onboarding auth paths + Relavium-account forward design (rides ADR-0012–0015) | Drafted when 2.6.J starts | 2.6.J | +| 9 | *(new)* | Toolbelt additions + tool-render/approval-preview contract (extends ADR-0029/ADR-0057) | Drafted when 2.6.M starts | 2.6.M | +| 10 | *(new)* | i18n + theming architecture | Drafted when 2.6.L starts | 2.6.L | + +[ADR-0058]: ../../decisions/0058-relavium-authoring-package-and-conversational-authoring.md +[ADR-0059]: ../../decisions/0059-cli-mid-session-model-reseat.md +[ADR-0060]: ../../decisions/0060-session-ctx-prompt-interpolation.md + +> **Conditional:** 2.6.E requires a small dedicated ADR **only if** a markdown rendering dependency is +> chosen (the in-house renderer is preferred); if the in-house path is taken, no 2.6.E ADR is needed. +> +> **Deferred:** A future validator-dependency ADR will be needed for `output_schema` deep JSON-Schema +> conformance (currently out of scope for this phase; tracked in +> [deferred-tasks.md](../deferred-tasks.md)). ## Risks & mitigations | Risk | Mitigation | |------|------------| -| Authoring knowledge drifts from the specs (restate) | Derive from the Zod schemas / reference docs; a no-duplication check is an acceptance gate ([CLAUDE.md](../../../CLAUDE.md) #8) | -| Authored YAML smuggles secrets | The authored artifact passes the existing `parseWorkflow` secret-taint gate; the write surface is security-reviewed | -| Reseat sells a false promise | An explicit context-loss notice; full-fidelity tool-context is Phase 3 | -| Package promotion over-engineers a CLI-only need | The maintainer decision is multi-surface (desktop/VS Code also consume); the add-package ADR records the rationale | +| **Scope breadth** — thirteen workstreams invite drift | Milestone gating (M2.6-1..5); the additive arms (E/L parts, M render-v2) can defer individual items without breaking the spine; the pull-in table keeps deferred-tasks as the single overflow home | +| Full-screen renderer performance/fragility on ink | The floor bump unlocks ink 7; the component harness carries performance regression thresholds; the inline renderer is retained as a first-class fallback, and non-TTY paths never change | +| Rendering args/diffs leaks sensitive data (reverses a 2.5 posture) | Sanctioned only by the toolbelt-additions ADR + a mandatory security review; every string passes the shared sanitize floor; bounded previews; secrets structurally excluded (keychain-only, never in tool args by construction) | +| Secret re-provide relaxes a fail-closed guarantee | stdin-only discipline, keychain re-resolution preferred, mandatory security review, and the shared resume core keeps one audited path | +| Authoring knowledge drifts from the specs (restate) | Derived from the Zod schemas / reference docs; a no-duplication check is an acceptance gate | +| Authored YAML smuggles secrets | The `parseWorkflow` secret-taint gate + the write-surface security review | +| Browser complexity balloons | Hard cap at the three-level drill-down; keystroke filters, no query DSL; tabs + deep-links instead of six commands; the desktop canvas stays the rich escape hatch | +| i18n churn destabilizes strings | Data ≠ code catalog; CI key-parity + dead-string lint land **with** the first locale, not after | +| ADR load stalls the phase | ADRs are drafted per-workstream as Proposed when the workstream starts (the 2.5 pattern), not all up front | +| Competitor-copy erodes Relavium's posture | Patterns are stolen, postures are not: keychain-only secrets, fail-closed approval, YAML-first artifacts, and the ADR-0049 machine contract are non-negotiable filters on every borrowed idea | Part of [roadmap/](../README.md). Carry-over hardening lives in [../deferred-tasks.md](../deferred-tasks.md).