| 1 | # Codewhale Architecture |
| 2 | |
| 3 | > 阅读简体中文版:[zh_hans/ARCHITECTURE.md](zh_hans/ARCHITECTURE.md)。 |
| 4 | |
| 5 | Codewhale Engine is the existing Rust execution runtime. The |
| 6 | [Runtime API](RUNTIME_API.md) is its client interface, and |
| 7 | [TypeScript mods](EXTENSIONS.md) contribute reviewed extensions. |
| 8 | |
| 9 | Current boundary note (read the workspace version from `Cargo.toml`; this |
| 10 | boundary has held since v0.9.1): |
| 11 | - `crates/tui` is still the live end-user runtime for the TUI, runtime API, task manager, and tool execution loop. |
| 12 | - Other workspace crates are being split out incrementally, but they are not yet the sole runtime source of truth. |
| 13 | - The runtime is moving into `crates/runtime` (`codewhale-runtime`) in the |
| 14 | order `docs/design/TUI_DECONSTRUCTION.md` records: engine, tools, config, |
| 15 | client and stores move there together, never into `crates/core`, and the |
| 16 | TUI stays the only crate that writes to the terminal. Until a module has |
| 17 | moved, its path under `crates/tui/src` is still where it lives. |
| 18 | - The LSP subsystem (`crates/tui/src/lsp/`) is fully wired into the engine's |
| 19 | post-tool-execution path (`core/engine/lsp_hooks.rs`), providing inline |
| 20 | diagnostics after `File` write, edit, and patch actions. |
| 21 | - The swarm agent system was removed in v0.8.5. The active sub-agent surface is |
| 22 | the single `agent` tool; persistent RLM sessions are available through the |
| 23 | deferred `rlm` action family. |
| 24 | No model-visible swarm tool remains in the active codebase. |
| 25 | |
| 26 | ## High-Level Overview |
| 27 | |
| 28 | ``` |
| 29 | ┌─────────────────────────────────────────────────────────────────┐ |
| 30 | │ User Interface │ |
| 31 | │ ┌─────────────────┐ ┌─────────────────┐ ┌────────────────┐ │ |
| 32 | │ │ TUI (ratatui) │ │ One-shot Mode │ │ Config/CLI │ │ |
| 33 | │ └────────┬────────┘ └────────┬────────┘ └────────┬───────┘ │ |
| 34 | └───────────┼─────────────────────┼────────────────────┼──────────┘ |
| 35 | │ │ │ |
| 36 | ▼ ▼ ▼ |
| 37 | ┌─────────────────────────────────────────────────────────────────┐ |
| 38 | │ Core Engine │ |
| 39 | │ ┌─────────────────────────────────────────────────────────┐ │ |
| 40 | │ │ Agent Loop (core/engine.rs) │ │ |
| 41 | │ │ ┌─────────┐ ┌─────────────┐ ┌──────────────────────┐ │ │ |
| 42 | │ │ │ Session │ │ Turn Mgmt │ │ Tool Orchestration │ │ │ |
| 43 | │ │ └─────────┘ └─────────────┘ └──────────────────────┘ │ │ |
| 44 | │ └─────────────────────────────────────────────────────────┘ │ |
| 45 | └─────────────────────────────────────────────────────────────────┘ |
| 46 | │ │ │ |
| 47 | ▼ ▼ ▼ |
| 48 | ┌─────────────────────────────────────────────────────────────────┐ |
| 49 | │ Tool & Extension Layer │ |
| 50 | │ ┌──────────┐ ┌──────────┐ ┌─────────┐ ┌────────────────┐ │ |
| 51 | │ │ Tools │ │ Skills │ │ Hooks │ │ MCP Servers │ │ |
| 52 | │ │ (shell, │ │ (plugins)│ │ (pre/ │ │ (external) │ │ |
| 53 | │ │ file) │ │ │ │ post) │ │ │ │ |
| 54 | │ └──────────┘ └──────────┘ └─────────┘ └────────────────┘ │ |
| 55 | └─────────────────────────────────────────────────────────────────┘ |
| 56 | │ │ │ |
| 57 | ▼ ▼ ▼ |
| 58 | ┌─────────────────────────────────────────────────────────────────┐ |
| 59 | │ Runtime API + Task Management │ |
| 60 | │ ┌─────────────────────────────┐ ┌──────────────────────────┐ │ |
| 61 | │ │ HTTP/SSE Runtime API │ │ Persistent Task Manager │ │ |
| 62 | │ │ (runtime_api.rs) │ │ (task_manager.rs) │ │ |
| 63 | │ └─────────────────────────────┘ └──────────────────────────┘ │ |
| 64 | └─────────────────────────────────────────────────────────────────┘ |
| 65 | │ │ |
| 66 | ▼ ▼ |
| 67 | ┌─────────────────────────────────────────────────────────────────┐ |
| 68 | │ LLM Layer │ |
| 69 | │ ┌──────────────────────────────────────────────────────────┐ │ |
| 70 | │ │ LLM Client Layer (client.rs) │ │ |
| 71 | │ │ ┌──────────────────┐ ┌─────────────────────────────┐ │ │ |
| 72 | │ │ │ OpenAI-compatible │ │ Anthropic / Responses │ │ │ |
| 73 | │ │ │ (chat adapter) │ │ (adapters) │ │ │ |
| 74 | │ │ └──────────────────┘ └─────────────────────────────┘ │ │ |
| 75 | │ └──────────────────────────────────────────────────────────┘ │ |
| 76 | └─────────────────────────────────────────────────────────────────┘ |
| 77 | ``` |
| 78 | |
| 79 | ## Module Organization |
| 80 | |
| 81 | ### Entry Point |
| 82 | |
| 83 | - **`crates/cli/src/main.rs`** - The canonical executable entry point. `crates/cli/src/lib.rs` owns its command interface; terminal and headless runtime startup runs in process through the `codewhale_tui` library in `crates/tui/src/lib.rs`. |
| 84 | |
| 85 | ### Core Components |
| 86 | |
| 87 | - **`core/`** - Main engine components |
| 88 | - `engine.rs` - Engine state, operation handling, message processing |
| 89 | - `engine/turn_loop.rs` - The existing Engine outer turn loop, shared tool |
| 90 | planner/executor/result handling, and stream decoder. Its private |
| 91 | `turn_loop/` phases contain request preparation (`preparation.rs`), model |
| 92 | dispatch and admission (`model_step.rs`), the ordered continuation ladder |
| 93 | (`continuation.rs`), inline REPL orchestration (`inline_repl.rs`), and the |
| 94 | direct model tool batch (`tool_batch.rs`). They borrow the same Engine and |
| 95 | TurnContext; they introduce no runtime, session, prompt, approval, event, |
| 96 | or persistence authority. Retry, loop termination, and immediate return |
| 97 | stay distinct, and only the existing productive paths advance the step. |
| 98 | - `session.rs` - Session state management |
| 99 | - `turn.rs` - Turn-based conversation handling |
| 100 | - `events.rs` - Event system for UI updates |
| 101 | - `ops.rs` - Core operations |
| 102 | |
| 103 | ### Configuration |
| 104 | |
| 105 | - **`config.rs`** - Configuration loading, profiles, environment variables |
| 106 | - **`settings.rs`** - Runtime settings management |
| 107 | |
| 108 | ### Workspace Crates |
| 109 | |
| 110 | - **`crates/cli`** - The canonical `codewhale` executable and command interface. |
| 111 | It owns commands such as `auth`, `metrics` and `update`, and invokes terminal |
| 112 | and headless modes (`run`, `exec`, `doctor`, `sessions`, ...) in process via |
| 113 | `codewhale_tui::run(RuntimeOptions, args)`. `crates/tui` is a library; the |
| 114 | `codew` and legacy release filename aliases contain the same executable. |
| 115 | - **`crates/tools`** - Shared tool invocation primitives, including tool result/error/capability types used by the TUI runtime. |
| 116 | - **`crates/agent`** - Model/provider registry (ModelRegistry) for resolving model IDs to provider endpoints. |
| 117 | - **`crates/app-server`** - HTTP/SSE + JSON-RPC app server transport for |
| 118 | headless agent workflows. The canonical executable dispatches |
| 119 | `app-server --http`/`--mobile` in process to the runtime API hosted by the |
| 120 | `codewhale_tui` library. |
| 121 | - **`crates/config`** - Config loading, profiles, environment variable precedence, CLI runtime overrides. |
| 122 | - **`crates/cloud-facts`** - Fetches the signed Codewhale cloud facts channel |
| 123 | (`facts/v1`), verifies its Ed25519 envelope, and keeps a verified disk cache; |
| 124 | never a startup dependency. |
| 125 | - **`crates/command-contract`** - Prototype command capability and dispatch |
| 126 | shapes for the staged extraction of TUI commands; shapes only, not yet the |
| 127 | production dispatch path. |
| 128 | - **`crates/core`** - Provider-neutral request construction (`request.rs`), |
| 129 | bounded context fragments, the tool-call parser, and thread/session types. |
| 130 | It does **not** own the agent loop: the live turn loop is |
| 131 | `Engine::run_turn` in `crates/tui/src/core/engine/turn_loop.rs`, and |
| 132 | `crates/tui/src/core/` is a module inside the TUI crate, not this crate. A |
| 133 | placeholder `engine/` tree here once suggested otherwise — it had no callers |
| 134 | and emitted `TurnComplete` without contacting a model — and was removed in |
| 135 | v0.9.11. The source guard follows resolved local phase calls and still |
| 136 | rejects unlisted loop owners. ACP stdio now projects the existing Runtime |
| 137 | manager and Engine; it keeps no provider/tool round loop or separate history. |
| 138 | Recursive RLM and mounted Python RPCs now project captured caller authority |
| 139 | onto the same Engine producer and Session; no RLM loop exception remains. |
| 140 | Python retains its context and variables, while each round borrows the |
| 141 | captured route, Native selection, original code gate, cancellation and |
| 142 | deadline. Task guidance is bounded and additive to Core policy. Recursive |
| 143 | history is retained whole; overflow refuses rather than compacting it. |
| 144 | Persistent `rlm` contexts remain caller-session scoped and `share_session=true` |
| 145 | explicitly refuses. Child workers also use their captured admission in the |
| 146 | same Engine; neither nested host retains a turn-loop exception. |
| 147 | - **`crates/execpolicy`** - Approval/sandbox policy engine for tool execution decisions. |
| 148 | - **`crates/hooks`** - Event sinks (stdout, JSONL file, webhook, Unix socket) |
| 149 | for response, tool, job and approval lifecycle events, plus the opt-in |
| 150 | lifecycle outbox. User-configured shell hooks that run commands around tool |
| 151 | calls are a separate system in `crates/tui/src/hooks.rs`. |
| 152 | - **`crates/localization`** - Locale registry for user-facing UI chrome strings |
| 153 | (`crates/localization/locales/*.json`); it never changes prompts or model |
| 154 | output language. |
| 155 | - **`crates/mcp`** - MCP client + stdio server for Model Context Protocol tool servers. |
| 156 | - **`crates/memory`** - Local, scoped, provenance-bearing memory and |
| 157 | resumable state (a library, not a second agent loop). |
| 158 | - **`crates/models`** - Provider request/response models and the offline model |
| 159 | metadata catalog. |
| 160 | - **`crates/palette`** - Colour tokens, themes, and contrast math for the |
| 161 | terminal UI. Its `ratatui` feature (on by default) gates everything that |
| 162 | renders; theme ids, setting normalizers and hex parsing compile without it, |
| 163 | which is how the runtime links it. |
| 164 | - **`crates/paths`** - User-scoped runtime path authority (`CODEWHALE_HOME` |
| 165 | and platform home resolution). |
| 166 | - **`crates/protocol`** - Request/response framing and protocol types. |
| 167 | - **`crates/runtime`** - `codewhale-runtime`, the headless runtime being |
| 168 | split out of `crates/tui` (`docs/design/TUI_DECONSTRUCTION.md`). Today it |
| 169 | holds the leaf modules that moved first (retry status, safe labels, sleep |
| 170 | guard, session tree, ...) and `host_terminal`, the one port through which |
| 171 | runtime code asks the terminal UI for a terminal effect. It never depends on |
| 172 | the TUI, `ratatui` or `crossterm`; `scripts/check-command-crate-boundaries.py` |
| 173 | enforces that and ratchets the runtime -> UI references still in `crates/tui`. |
| 174 | - **`crates/secrets`** - OS keyring integration for API key storage, plus the |
| 175 | shared output sanitizer (`sanitize`) and redaction (`redact`) that UI and |
| 176 | runtime code both call. |
| 177 | - **`crates/state`** - SQLite thread/session persistence layer. |
| 178 | - **`crates/telemetry`** - Anonymous, user-disableable aggregate usage |
| 179 | counting; the only crate allowed to build or send a telemetry payload |
| 180 | (`docs/TELEMETRY.md`). |
| 181 | - **`crates/workflow`** / **`crates/workflow-js`** - Workflow engine and its |
| 182 | QuickJS scripting layer (renamed from the whaleflow crates). |
| 183 | - **`crates/lane`** - Lane runtime: durable, attachable running instances of |
| 184 | Fleet/Workflow work (`codewhale lane list/status/attach/logs/stop`). |
| 185 | - **`crates/release`** / **`crates/build-support`** - Release checks and build |
| 186 | plumbing. |
| 187 | |
| 188 | ### LLM Integration |
| 189 | |
| 190 | - **`client.rs`** - The live HTTP client layer: OpenAI-compatible, Anthropic, |
| 191 | and Responses wire adapters, DeepSeek request-boundary handling, retry |
| 192 | policy, and streaming. Provider routes land here through the shared config |
| 193 | and catalog layers. |
| 194 | - **`llm_client/`** - LLM client trait, retry logic, and error classification |
| 195 | (`LlmClient`, `RetryConfig`, `with_retry`) consumed by `client.rs`; `mock.rs` |
| 196 | is test-only (`#[cfg(test)]`). |
| 197 | - **`crates/models`** (`codewhale_models`) - Data structures for API |
| 198 | requests/responses; the TUI crate has no local `models.rs`. |
| 199 | |
| 200 | #### DeepSeek API Endpoints |
| 201 | |
| 202 | DeepSeek exposes OpenAI-compatible endpoints. The first-party route uses: |
| 203 | - `https://api.deepseek.com/beta` - default DeepSeek base URL (`provider_defaults.rs`) |
| 204 | - `https://api.deepseek.com/beta/models` - live model discovery and health checks |
| 205 | |
| 206 | `https://api.deepseek.com/v1` is accepted for OpenAI SDK compatibility, and |
| 207 | can still be configured explicitly to opt out of beta-only features such as |
| 208 | strict tool mode, chat prefix completion, and FIM completion. The public |
| 209 | DeepSeek docs do not document a Responses API path for this workflow; the engine |
| 210 | drives turns through Chat Completions. |
| 211 | |
| 212 | ### Tool System |
| 213 | |
| 214 | - **`tools/`** - Built-in tool implementations |
| 215 | - `mod.rs` - Tool registry and common types |
| 216 | - `shell.rs` - Shell command execution |
| 217 | - `file.rs` - File read/write operations |
| 218 | - `todo.rs` - Checklist tools plus legacy todo aliases |
| 219 | - `tasks.rs` - Model-visible durable task, gate, background shell, and PR-attempt tools |
| 220 | - `git.rs` - Read-only `git_status` / `git_diff` inspection wrappers |
| 221 | - `git_tool.rs` - The canonical action-based `Git` tool (`status | diff | log | show | blame`); per-action legacy aliases were removed in v0.9.3 |
| 222 | - `git_history.rs` - Read-only `git_log` / `git_show` / `git_blame` |
| 223 | - `github/` - Unified `github` tool family (read-only context plus guarded |
| 224 | comment/closure actions backed by `gh`); deferred by default and |
| 225 | discoverable through `tool_search` |
| 226 | - `automation.rs` - Model-visible scheduling tools over `AutomationManager` |
| 227 | - `plan.rs` - Planning tools |
| 228 | - `subagent/` - Sub-agent launch and supervision. `agent` is the one |
| 229 | creation surface; `subagent/coord.rs` adds narrow coordination tools |
| 230 | (`agents/list`, `agents/message`, `agents/followup`, `agents/interrupt`, |
| 231 | `agents/wait`, `agents/coordinate`) over the existing manager. The |
| 232 | `agent_open`/`agent_eval`/`agent_close` lifecycle surface was retired |
| 233 | (see the `subagent/coord.rs` module doc) |
| 234 | - `spec.rs` - Tool specifications |
| 235 | - `rlm.rs` - Persistent Recursive Language Model (RLM) sessions — persistent local Python REPL subprocesses (environment-scrubbed, not OS-sandboxed) with semantic helper calls and `var_handle` output support |
| 236 | |
| 237 | ### Extension Systems |
| 238 | |
| 239 | - **`mcp.rs`** - Model Context Protocol client for external tool servers |
| 240 | - **`skills/`** - Skill discovery and registry for local `SKILL.md` files, plus install and audit |
| 241 | - **`hooks.rs`** - Pre/post execution hooks with conditions |
| 242 | |
| 243 | ### User Interface |
| 244 | |
| 245 | - **`tui/`** - Terminal UI components (ratatui-based; this is a representative |
| 246 | list, not exhaustive - the module has grown to 80+ focused files): |
| 247 | - `app.rs` - Application state and message handling |
| 248 | - `ui.rs` - Event handling, streaming state, and rendering logic |
| 249 | - `approval.rs` - Tool approval dialog |
| 250 | - `clipboard.rs` - Clipboard handling |
| 251 | - `underwater.rs` - Main shell chrome: status chips, mode labels, phase rail |
| 252 | |
| 253 | ### LSP Integration |
| 254 | |
| 255 | - **`lsp/`** - Post-edit diagnostics injection (#136) |
| 256 | - `mod.rs` - `LspManager` — lazy per-language transport pool + config |
| 257 | - `client.rs` - `StdioLspTransport` — JSON-RPC over stdio with `didOpen`/`didChange`/`publishDiagnostics` |
| 258 | - `diagnostics.rs` - Diagnostic types, severity, and HTML-block renderer |
| 259 | - `registry.rs` - Language detection and the default server map: `rust-analyzer`, |
| 260 | `gopls`, `pyright-langserver`, `typescript-language-server`, `jdtls`, |
| 261 | `intelephense` (PHP), `vue-language-server`, `clangd` (`lsp/registry.rs:98-110`) |
| 262 | - Wired into the engine via `core/engine/lsp_hooks.rs` — called after every successful edit |
| 263 | |
| 264 | ### Security |
| 265 | |
| 266 | - **`sandbox/`** - platform sandbox policy preparation and denial reporting |
| 267 | - `mod.rs` - Sandbox type definitions |
| 268 | - `backend.rs` - Pluggable sandbox backend abstraction (routes shell |
| 269 | execution to a remote service, e.g. Alibaba OpenSandbox) |
| 270 | - `policy.rs` - Sandbox policy configuration |
| 271 | - `opensandbox.rs` - Alibaba OpenSandbox HTTP backend adapter |
| 272 | - `seatbelt.rs` - macOS Seatbelt profile generation |
| 273 | - `bwrap.rs` - opt-in Linux bubblewrap command wrapper |
| 274 | - `seccomp.rs` - dormant Linux seccomp implementation; not wired into commands |
| 275 | - `process_hardening.rs` - Linux kernel-level hardening for the TUI process |
| 276 | itself (defense-in-depth; not a child-command sandbox) |
| 277 | - `windows.rs` - Windows helper contract; not advertised until a Job |
| 278 | Object process-containment helper exists |
| 279 | |
| 280 | ### Utilities |
| 281 | |
| 282 | - **`utils.rs`** - Common utilities |
| 283 | - **`logging.rs`** - Logging infrastructure |
| 284 | - **`compaction.rs`** - Context compaction for long conversations |
| 285 | - **`purge.rs`** - Agent-driven context purging (surgical message removal/rewriting) |
| 286 | - **`pricing.rs`** - Cost estimation |
| 287 | - **`prompts.rs`** - System prompt templates |
| 288 | - **`runtime_api.rs`** - HTTP/SSE runtime API (`codewhale serve --http`) |
| 289 | - **`runtime_threads.rs`** - Durable thread/turn/item store + replayable event timeline |
| 290 | - **`task_manager.rs`** - Durable queue, worker pool, task timelines and artifacts |
| 291 | |
| 292 | ## Data Flow |
| 293 | |
| 294 | ### Interactive Session |
| 295 | |
| 296 | 1. User input received in TUI |
| 297 | 2. Input processed by `core/engine.rs` |
| 298 | 3. Message sent to LLM via `client.rs` |
| 299 | 4. Response streamed back, parsed in `client.rs` |
| 300 | 5. Tool calls extracted and executed via `tools/` |
| 301 | 6. Hooks triggered before/after tool execution |
| 302 | 7. Results aggregated and sent back to LLM |
| 303 | 8. Final response rendered in TUI |
| 304 | |
| 305 | ### Crash Recovery + Offline Queue |
| 306 | |
| 307 | 1. Before sending user input, the TUI writes a checkpoint snapshot to `~/.codewhale/sessions/checkpoints/latest.json` |
| 308 | 2. Startup remains fresh by default; prior sessions are resumed explicitly via `--resume`/`--continue` (or `Ctrl+R` in TUI) |
| 309 | 3. While degraded/offline, new prompts are queued in-memory and mirrored to `~/.codewhale/sessions/checkpoints/offline_queue.json` |
| 310 | 4. Queue edits (`/queue ...`) are persisted continuously so drafts and queued prompts survive restarts |
| 311 | 5. Successful turn completion clears the active checkpoint and writes a durable session snapshot |
| 312 | 6. Action-capable turns also take pre/post-turn side-git workspace snapshots under `~/.codewhale/snapshots/<project_hash>/<worktree_hash>/.git`; `/restore N` and `revert_turn` restore file state without changing conversation history or the user's `.git` |
| 313 | |
| 314 | ### Tool Execution |
| 315 | |
| 316 | 1. LLM requests tool via `tool_use` content block |
| 317 | 2. Tool registry looks up handler |
| 318 | 3. Pre-execution hooks run |
| 319 | 4. Approval requested when the effective permission posture and policy require it |
| 320 | 5. Tool executed (possibly wrapped by Seatbelt on macOS or opt-in bubblewrap on Linux) |
| 321 | 6. Post-execution hooks run |
| 322 | 7. Result metadata is retained on runtime item records |
| 323 | 8. **LSP post-edit hook**: after a `File` write, edit, or patch action (including a replay-only legacy alias), the engine runs `run_post_edit_lsp_hook()` when LSP is enabled to collect diagnostics |
| 324 | 9. **Diagnostics flush**: before the next API request, `flush_pending_lsp_diagnostics()` injects any collected errors as a synthetic user message |
| 325 | 10. Result returned to agent loop |
| 326 | |
| 327 | ### Background Tasks |
| 328 | |
| 329 | 1. Client enqueues task (`/task add ...` or `POST /v1/tasks`) |
| 330 | 2. `task_manager.rs` persists task + queue entry under `~/.codewhale/tasks` |
| 331 | 3. Worker picks queued task (bounded pool), transitions to `running` |
| 332 | 4. Task creates/uses a runtime thread and starts a runtime turn |
| 333 | 5. `runtime_threads.rs` persists thread/turn/item records + monotonic event sequence |
| 334 | 6. Timeline/tool summaries/artifact references are persisted incrementally |
| 335 | 7. Checklist state, verifier gates, PR attempts, and guarded GitHub events are applied from tool metadata to the active task |
| 336 | 8. Final state (`completed|failed|canceled`) is durable and queryable via TUI/API |
| 337 | |
| 338 | Model-visible durable task tools are a surface over this same manager. They do |
| 339 | not introduce a parallel work system: `task_create` enqueues normal tasks, |
| 340 | `checklist_*` updates task-local progress, `task_gate_run` and completed |
| 341 | `task_shell_wait` attach verification evidence, and automation runs enqueue |
| 342 | ordinary durable tasks. |
| 343 | |
| 344 | ### Runtime Thread/Turn Timeline |
| 345 | |
| 346 | 1. API/TUI creates or resumes a thread (`/v1/threads*`) |
| 347 | 2. Turn starts on the thread (`/v1/threads/{id}/turns`) |
| 348 | 3. Engine events are mapped to item lifecycle events (`item.started|item.delta|item.completed`) |
| 349 | 4. Interrupt/steer operations apply to the active turn only |
| 350 | 5. Compaction (auto/manual) is emitted as `context_compaction` item lifecycle |
| 351 | 6. Purge (agent-driven) is emitted as `context_purge` item lifecycle |
| 352 | 7. Clients replay history and resume with `/v1/threads/{id}/events?since_seq=<n>` |
| 353 | |
| 354 | ### Durable Schema Gates |
| 355 | |
| 356 | - `session_manager.rs`, `runtime_threads.rs`, and `task_manager.rs` embed `schema_version` on persisted records. |
| 357 | - On load, newer schema versions are rejected with explicit errors instead of silently truncating/overwriting data. |
| 358 | - This allows safe forward migrations and prevents corruption when binaries and stored state are out of sync. |
| 359 | |
| 360 | ## Extension Points |
| 361 | |
| 362 | ### Adding a New Tool |
| 363 | |
| 364 | 1. Create handler in `tools/` |
| 365 | 2. Register in `tools/registry.rs` |
| 366 | 3. Add tool specification (name, description, input schema) |
| 367 | |
| 368 | ### Adding an MCP Server |
| 369 | |
| 370 | 1. Configure in `~/.codewhale/mcp.json` |
| 371 | 2. Server auto-discovered at startup |
| 372 | 3. Tools exposed to LLM automatically |
| 373 | |
| 374 | ### Creating a Skill |
| 375 | |
| 376 | 1. Create skill directory with `SKILL.md` |
| 377 | 2. Define skill prompt and optional scripts |
| 378 | 3. Place in a Codewhale-owned root (`~/.codewhale/skills/` or |
| 379 | `<workspace>/.codewhale/skills/`), or import from a compatible harness root |
| 380 | through `/skills` |
| 381 | |
| 382 | See [SKILLS.md](SKILLS.md) for the Skills Manager, audit inventory, and the |
| 383 | rule that compatible roots (`.claude`, `.agents`, …) are never mutated in place. |
| 384 | |
| 385 | ### Adding Hooks |
| 386 | |
| 387 | Configure in `~/.codewhale/config.toml`: |
| 388 | |
| 389 | ```toml |
| 390 | [[hooks]] |
| 391 | event = "tool_call_before" |
| 392 | command = "echo 'Running tool: $TOOL_NAME'" |
| 393 | ``` |
| 394 | |
| 395 | ## Key Design Decisions |
| 396 | |
| 397 | 1. **Streaming-first**: All LLM responses stream for responsiveness |
| 398 | 2. **Tool safety**: Ask and Auto-Review require approval according to tool and |
| 399 | managed policy; Full Access removes ordinary prompts but not hard safety |
| 400 | holds. Side-effectful MCP tools use the same boundary. |
| 401 | 3. **Extensibility**: MCP, skills, and hooks allow customization without code changes |
| 402 | 4. **Cross-platform**: Core works on Linux/macOS/Windows. Sandbox guarantees |
| 403 | are platform-specific: macOS uses Seatbelt when available; Linux uses an |
| 404 | installed bubblewrap executable only when explicitly enabled; Windows has |
| 405 | no advertised OS command sandbox. Seccomp and the Windows helper contract |
| 406 | are not wired into command execution. |
| 407 | 5. **Minimal dependencies**: Careful dependency selection for build speed |
| 408 | 6. **Local-first runtime API**: HTTP/SSE endpoints are intended for trusted localhost access and are served by the `crates/tui` runtime today |
| 409 | 7. **Lock poison**: fail-stop by default. A poisoned lock means a holder |
| 410 | panicked mid-mutation, so `.expect()` with a message naming the lock is |
| 411 | the standard posture — never serve half-updated state. Recover with |
| 412 | `into_inner()` only where stale state is safe (caches, idempotent |
| 413 | rebuilds), with a comment saying why. |
| 414 | |
| 415 | ## Configuration Files |
| 416 | |
| 417 | - `~/.codewhale/config.toml` - Main configuration (`~/.deepseek/config.toml` is still read as a legacy fallback) |
| 418 | - `/etc/deepseek/managed_config.toml` - Optional managed defaults layer (Unix) |
| 419 | - `/etc/deepseek/requirements.toml` - Optional allowed-policy constraints (Unix) |
| 420 | - `~/.codewhale/mcp.json` - MCP server configuration |
| 421 | - `~/.codewhale/skills/` - User skills directory |
| 422 | - `~/.codewhale/sessions/` - Session history |
| 423 | - `~/.codewhale/sessions/checkpoints/` - Crash checkpoint + offline queue persistence |
| 424 | - `~/.codewhale/snapshots/` - Side-git pre/post-turn workspace snapshots for `/restore` and `revert_turn` |
| 425 | - `~/.codewhale/tasks/` - Background task records, queue, timelines, artifacts |
| 426 | - `~/.codewhale/audit.log` - Append-only security events: credential saves and clears, hook environment key names, compaction passes, goal completions, the terminal's approval routing, Auto-Review verdicts, and outbound network decisions when `[network]` auditing is on. Not an action record: it holds no commands or file changes, and app or `serve` turns write no approvals there. See `docs/RECEIPTS.md` for what a session did |
| 427 | - `~/.codewhale/sessions/<id>/approval_receipts.jsonl` - Every approval ask and decision for a session, including who decided |
| 428 |