diff --git a/services/computer-use-mcp/airi-cli-architecture.md b/services/computer-use-mcp/airi-cli-architecture.md new file mode 100644 index 000000000..4601a91ec --- /dev/null +++ b/services/computer-use-mcp/airi-cli-architecture.md @@ -0,0 +1,293 @@ +# AIRI Coding CLI + Chafa Architecture + +## Purpose + +Define a narrow architecture for a terminal-facing AIRI coding CLI that can +optionally render an animated avatar through `chafa`, without coupling terminal +presentation to the `computer-use-mcp` runtime. + +This document is a boundary and contract proposal. It is not an implementation +plan for a full CLI in this PR. + +## Current Status + +As of 2026-04-26, the main branch does not expose a stable coding runner event +stream. A CLI that renders real progress needs a runner contract first. Starting +with terminal animation before that contract exists would produce presentation +without reliable runtime state. + +The correct dependency order is: + +1. Define a coding runner event contract. +2. Emit deterministic runner events from the coding runner. +3. Build a text-first CLI that consumes those events. +4. Add optional `chafa` avatar rendering as a display adapter. + +## Confirmed Decisions + +1. Future CLI package location: `packages/airi-cli`. +2. First integration channel: in-process runner callback. +3. JSONL over stdin/file remains a follow-up adapter for replay, fixtures, and + tooling interoperability. +4. `chafa` is optional presentation only. + +## Hard Boundaries + +1. No `chafa` dependency belongs in `services/computer-use-mcp` core runtime. +2. `computer-use-mcp` must remain headless-safe and CI-safe. +3. The CLI consumes runner events; it must not own planning, tool execution, or + runtime policy. +4. Missing `chafa` must degrade to plain text rendering. +5. Non-TTY output must support plain text or JSONL output. +6. CLI rendering changes must not be mixed with coding memory or runner runtime + refactors. + +## Proposed Location Split + +- Core runtime: `services/computer-use-mcp`. +- CLI package: `packages/airi-cli`. +- Event contract: initially colocated with the runner that emits it, then + exported for CLI consumption. +- Text renderer: `packages/airi-cli/src/renderers/text.ts`. +- Chafa adapter: `packages/airi-cli/src/renderers/chafa-avatar.ts`. +- JSONL adapter: `packages/airi-cli/src/adapters/jsonl.ts`. + +## Runner Event Contract Draft + +Use an append-only event envelope. The first transport should be an in-process +callback. JSONL is an adapter over the same envelope. + +```ts +interface RunnerEventEnvelope { + runId: string + seq: number + at: string + kind: TKind + payload: TPayload +} +``` + +Contract rules: + +- `seq` is strictly increasing per `runId`. +- `at` is an ISO timestamp. +- events are append-only; later events must not rewrite earlier events. +- crash and timeout paths must emit deterministic terminal events. + +## Minimum Event Kinds + +- `run_started` +- `preflight_started` +- `preflight_completed` +- `step_started` +- `tool_call_started` +- `tool_call_completed` +- `assistant_message` +- `step_timeout` +- `report_status` +- `run_finished` +- `run_crashed` + +## Minimum Payloads + +`run_started`: + +```ts +interface RunStartedPayload { + workspacePath: string + taskGoal: string + maxSteps: number + stepTimeoutMs: number +} +``` + +`step_started`: + +```ts +interface StepStartedPayload { + stepIndex: number + maxSteps: number +} +``` + +`tool_call_started`: + +```ts +interface ToolCallStartedPayload { + toolName: string + argsSummary: string +} +``` + +`tool_call_completed`: + +```ts +interface ToolCallCompletedPayload { + toolName: string + ok: boolean + status?: string + summary: string + error?: string +} +``` + +`assistant_message`: + +```ts +interface AssistantMessagePayload { + text: string +} +``` + +`report_status`: + +```ts +interface ReportStatusPayload { + status: 'completed' | 'failed' | 'blocked' + summary?: string +} +``` + +`run_finished`: + +```ts +interface RunFinishedPayload { + finalStatus: 'completed' | 'failed' | 'blocked' | 'timeout' + totalSteps: number + error?: string +} +``` + +## CLI Architecture + +The CLI should have three separable layers. + +### 1. Input Adapter + +Input adapters convert transport-specific input into `RunnerEventEnvelope` +events. + +Initial adapters: + +- in-process runner callback +- stdin JSONL +- JSONL file replay + +### 2. State Reducer + +The reducer builds deterministic `CliViewState` from events. + +Rules: + +- no terminal I/O in the reducer +- no process spawning in the reducer +- no animation timing in the reducer +- reducer tests should use fixture event streams + +### 3. Renderer Adapter + +Renderer adapters consume `CliViewState`. + +Initial renderers: + +- text renderer, always available +- JSONL passthrough renderer for tooling +- optional `chafa` avatar renderer + +## Chafa Adapter + +The `chafa` adapter should: + +- probe `chafa` binary availability at startup +- disable itself when stdout is not a TTY +- convert sprite or frame assets to ANSI frames +- pace animation independently from runner event rate +- fall back to text-only rendering on failure + +Initial CLI flags: + +- `--avatar=chafa|none` +- `--no-avatar` +- `--events=runner|stdin|jsonl-file` +- `--output=pretty|jsonl` + +## Testing Strategy + +1. Contract tests for event schema and monotonic `seq`. +2. Reducer tests from fixture JSONL streams. +3. Text renderer snapshot tests. +4. Chafa adapter tests with mocked binary probing and mocked child process + execution. +5. CI defaults to text mode and does not require `chafa`. + +## Delivery Stages + +### Stage 1: Contract + +- define the runner event envelope +- add success, failure, and timeout fixture streams +- do not render terminal UI yet + +### Stage 2: Text CLI + +- scaffold `packages/airi-cli` +- consume runner events +- render run, step, tool, error, and final report state in plain text + +### Stage 2.5: JSONL Adapter + +- support stdin JSONL replay +- support JSONL file replay +- use fixtures for offline demos and tests + +### Stage 3: Chafa Renderer + +- add optional `chafa` renderer +- keep plain text as the default fallback +- keep CI and headless runs independent from `chafa` + +## Acceptance Criteria + +1. No `chafa` dependency is added under `services/computer-use-mcp`. +2. CLI works when `chafa` is not installed. +3. CI does not require `chafa`. +4. The reducer is testable from mocked event streams. +5. Terminal output degrades to plain text. +6. Runner behavior is not changed for animation-first UX. + +## Review Checklist + +Layering: + +- no renderer code in `computer-use-mcp` runtime +- no `chafa` dependency in runtime packages +- CLI code stays under `packages/airi-cli` + +Runtime: + +- runner remains headless-safe +- runner emits events without depending on terminal state +- failures and timeouts produce deterministic events + +Fallback: + +- missing `chafa` does not fail CLI execution +- non-TTY mode uses text or JSONL output + +Scope: + +- do not mix CLI rendering with coding memory changes +- do not mix CLI rendering with desktop/browser runtime changes + +## Out of Scope + +- long-term memory promotion and governance +- desktop/browser runtime refactors +- terminal animation before a runner event contract exists +- changing coding runner completion semantics for display purposes + +## Suggested Follow-up PRs + +- `feat(cli): define coding runner event contract` +- `feat(cli): scaffold airi coding cli text renderer` +- `feat(cli): add optional terminal AIRI avatar renderer with chafa`