diff --git a/CLAUDE.md b/CLAUDE.md index 55c7b0556..24341ad10 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,15 +1,15 @@ # NanoClaw -Personal Claude assistant. See [README.md](README.md) for philosophy and setup. Architecture lives in `docs/v2-*.md`. +Personal Claude assistant. See [README.md](README.md) for philosophy and setup. Architecture lives in `docs/`. -## Quick Context (v2) - -v2 is the current branch and codebase. v1 still exists under `src/v1/` and `container/agent-runner/src/v1/` for reference but is no longer the runtime. If a file mentions v1 in its comments, it is probably stale. +## Quick Context The host is a single Node process that orchestrates per-session agent containers. Platform messages land via channel adapters, route through an entity model (users → messaging groups → agent groups → sessions), get written into the session's inbound DB, and wake a container. The agent-runner inside the container polls the DB, calls Claude, and writes back to the outbound DB. The host polls the outbound DB and delivers through the same adapter. **Everything is a message.** There is no IPC, no file watcher, no stdin piping between host and container. The two session DBs are the sole IO surface. +A `src/v1/` tree exists for historical reference and is not part of the runtime — ignore it unless you're explicitly working on a migration. + ## Entity Model ``` @@ -25,7 +25,7 @@ messaging_groups (one chat/channel on one platform; unknown_sender_policy) sessions (agent_group_id + messaging_group_id + thread_id → per-session container) ``` -Privilege is user-level (owner/admin), not agent-group-level. See [docs/v2-isolation-model.md](docs/v2-isolation-model.md) for the three isolation levels (`agent-shared`, `shared`, separate agents). +Privilege is user-level (owner/admin), not agent-group-level. See [docs/isolation-model.md](docs/isolation-model.md) for the three isolation levels (`agent-shared`, `shared`, separate agents). ## Two-DB Session Split @@ -56,12 +56,22 @@ Exactly one writer per file — no cross-mount lock contention. Heartbeat is a f | `src/user-dm.ts` | Cold-DM resolution + `user_dms` cache | | `src/group-init.ts` | Per-agent-group filesystem scaffold (CLAUDE.md, skills, agent-runner-src overlay) | | `src/db/` | DB layer — agent_groups, messaging_groups, sessions, user_roles, user_dms, pending_*, migrations | -| `src/channels/` | Channel adapters + Chat SDK bridge | +| `src/channels/` | Channel adapter infra (registry, Chat SDK bridge); specific channel adapters are skill-installed from the `channels` branch | +| `src/providers/` | Host-side provider container-config (`claude` baked in; `opencode` etc. installed from the `providers` branch) | | `container/agent-runner/src/` | Agent-runner: poll loop, formatter, provider abstraction, MCP tools, destinations | | `container/skills/` | Container skills mounted into every agent session | | `groups//` | Per-agent-group filesystem (CLAUDE.md, skills, per-group `agent-runner-src/` overlay) | | `scripts/init-first-agent.ts` | Bootstrap the first DM-wired agent (used by `/init-first-agent` skill) | +## Channels and Providers (skill-installed) + +Trunk does not ship any specific channel adapter or non-default agent provider. The codebase is the registry/infra; the actual adapters and providers live on long-lived sibling branches and get copied in by skills: + +- **`channels` branch** — Discord, Slack, Telegram, WhatsApp, Teams, Linear, GitHub, iMessage, Webex, Resend, Matrix, Google Chat, WhatsApp Cloud (+ helpers, tests, channel-specific setup steps). Installed via `/add-` skills. +- **`providers` branch** — OpenCode (and any future non-default agent providers). Installed via `/add-opencode`. + +Each `/add-` skill is idempotent: `git fetch origin ` → copy module(s) into the standard paths → append a self-registration import to the relevant barrel → `pnpm install @` → build. + ## Self-Modification One tier of agent self-modification today: @@ -78,10 +88,10 @@ API keys, OAuth tokens, and auth credentials are managed by the OneCLI gateway. Four types of skills. See [CONTRIBUTING.md](CONTRIBUTING.md) for the full taxonomy. -- **Feature skills** — `skill/*` branches merged via `scripts/apply-skill.ts` (e.g. `/add-discord-v2`, `/add-slack-v2`, `/add-whatsapp-v2`) -- **Utility skills** — ship code files alongside `SKILL.md` (e.g. `/claw`) -- **Operational skills** — instruction-only workflows (`/setup`, `/debug`, `/customize`, `/init-first-agent`, `/manage-channels`, `/init-onecli`, `/update-nanoclaw`) -- **Container skills** — loaded inside agent containers at runtime (`container/skills/`: `welcome`, `self-customize`, `agent-browser`, `slack-formatting`) +- **Channel/provider install skills** — copy the relevant module(s) in from the `channels` or `providers` branch, wire imports, install pinned deps (e.g. `/add-discord`, `/add-slack`, `/add-whatsapp`, `/add-opencode`). +- **Utility skills** — ship code files alongside `SKILL.md` (e.g. `/claw`). +- **Operational skills** — instruction-only workflows (`/setup`, `/debug`, `/customize`, `/init-first-agent`, `/manage-channels`, `/init-onecli`, `/update-nanoclaw`). +- **Container skills** — loaded inside agent containers at runtime (`container/skills/`: `welcome`, `self-customize`, `agent-browser`, `slack-formatting`). | Skill | When to Use | |-------|-------------| @@ -137,21 +147,21 @@ This project uses pnpm with `minimumReleaseAge: 4320` (3 days) in `pnpm-workspac - **`onlyBuiltDependencies`**: Never add packages to this list without human approval — build scripts execute arbitrary code during install. - **`pnpm install --frozen-lockfile`** should be used in CI, automation, and container builds. Never run bare `pnpm install` in those contexts. -## v2 Docs Index +## Docs Index | Doc | Purpose | |-----|---------| -| [docs/v2-architecture-draft.md](docs/v2-architecture-draft.md) | Full architecture writeup | -| [docs/v2-api-details.md](docs/v2-api-details.md) | Host API + DB schema details | -| [docs/v2-db.md](docs/v2-db.md) | DB architecture overview: three-DB model, cross-mount rules, readers/writers map | -| [docs/v2-db-central.md](docs/v2-db-central.md) | Central DB (`data/v2.db`) — every table + migration system | -| [docs/v2-db-session.md](docs/v2-db-session.md) | Per-session `inbound.db` + `outbound.db` schemas + seq parity | -| [docs/v2-agent-runner-details.md](docs/v2-agent-runner-details.md) | Agent-runner internals + MCP tool interface | -| [docs/v2-isolation-model.md](docs/v2-isolation-model.md) | Three-level channel isolation model | -| [docs/v2-setup-wiring.md](docs/v2-setup-wiring.md) | What's wired, what's open in the setup flow | -| [docs/v2-checklist.md](docs/v2-checklist.md) | Rolling status checklist across all subsystems | -| [docs/v2-architecture-diagram.md](docs/v2-architecture-diagram.md) | Diagram version of the architecture | -| [docs/v2-build-and-runtime.md](docs/v2-build-and-runtime.md) | Runtime split (Node host + Bun container), lockfiles, image build surface, CI, key invariants | +| [docs/architecture.md](docs/architecture.md) | Full architecture writeup | +| [docs/api-details.md](docs/api-details.md) | Host API + DB schema details | +| [docs/db.md](docs/db.md) | DB architecture overview: three-DB model, cross-mount rules, readers/writers map | +| [docs/db-central.md](docs/db-central.md) | Central DB (`data/v2.db`) — every table + migration system | +| [docs/db-session.md](docs/db-session.md) | Per-session `inbound.db` + `outbound.db` schemas + seq parity | +| [docs/agent-runner-details.md](docs/agent-runner-details.md) | Agent-runner internals + MCP tool interface | +| [docs/isolation-model.md](docs/isolation-model.md) | Three-level channel isolation model | +| [docs/setup-wiring.md](docs/setup-wiring.md) | What's wired, what's open in the setup flow | +| [docs/checklist.md](docs/checklist.md) | Rolling status checklist across all subsystems | +| [docs/architecture-diagram.md](docs/architecture-diagram.md) | Diagram version of the architecture | +| [docs/build-and-runtime.md](docs/build-and-runtime.md) | Runtime split (Node host + Bun container), lockfiles, image build surface, CI, key invariants | ## Container Build Cache @@ -159,7 +169,7 @@ The container buildkit caches the build context aggressively. `--no-cache` alone ## Container Runtime (Bun) -The agent container runs on **Bun**; the host runs on **Node** (pnpm). They communicate only via session DBs — no shared modules. Details and rationale: [docs/v2-build-and-runtime.md](docs/v2-build-and-runtime.md). +The agent container runs on **Bun**; the host runs on **Node** (pnpm). They communicate only via session DBs — no shared modules. Details and rationale: [docs/build-and-runtime.md](docs/build-and-runtime.md). **Gotchas — trigger + action:** diff --git a/docs/v2-agent-runner-details.md b/docs/agent-runner-details.md similarity index 94% rename from docs/v2-agent-runner-details.md rename to docs/agent-runner-details.md index 22a8744ec..c7ef0a0e7 100644 --- a/docs/v2-agent-runner-details.md +++ b/docs/agent-runner-details.md @@ -1,6 +1,6 @@ -# NanoClaw v2 Agent-Runner Details +# NanoClaw Agent-Runner Details -Implementation-level details for the agent-runner inside the container. See [v2-architecture-draft.md](v2-architecture-draft.md) for the high-level design. +Implementation-level details for the agent-runner inside the container. See [architecture.md](architecture.md) for the high-level design. ## Separation of Concerns @@ -8,7 +8,7 @@ The agent-runner has two layers: 1. **Agent-runner core** — owns the poll loop, message formatting, DB reads/writes, MCP tool implementations, routing, status management, media handling. This is NanoClaw-specific and shared across all providers. -2. **Agent provider** — owns the SDK interaction. Takes formatted prompts, pushes them to the SDK, yields events back. Each SDK (Claude, Codex, OpenCode) gets its own provider implementation. +2. **Agent provider** — owns the SDK interaction. Takes formatted prompts, pushes them to the SDK, yields events back. Trunk ships the `claude` provider; additional providers (OpenCode, Codex, etc.) are installed by `/add-` skills from the `providers` branch. The boundary: the agent-runner decides **what** to send and **what to do** with results. The provider decides **how** to talk to the SDK. @@ -91,6 +91,8 @@ type ProviderEvent = ## Provider Implementations +Only the `claude` provider ships in trunk. The Codex and OpenCode sections below document the provider interface for reference and for skills that install additional providers — they are not baked into the core image. + ### Claude Provider Wraps `@anthropic-ai/claude-agent-sdk`'s `query()`. @@ -445,7 +447,7 @@ pending → processing → completed ### MCP Tools -The agent-runner runs an MCP server (same as v1) that exposes NanoClaw tools to the agent. In v2, all tools write to the session DB instead of IPC files. +The agent-runner runs an MCP server that exposes NanoClaw tools to the agent. All tools write to the session DB. **DB path:** The MCP server receives the session DB path via environment variable. It opens a second connection to the same SQLite file (WAL mode allows concurrent access). @@ -691,8 +693,6 @@ For `task` kind messages with a `script` field in the content: 4. If `wakeAgent === false`: mark message as completed, don't invoke the provider 5. If `wakeAgent === true`: enrich the prompt with script output, then invoke the provider -Same as v1 behavior. - ### Transcript Archiving The agent-runner archives conversation transcripts before context compaction. For Claude, this is handled via the PreCompact hook (provider-internal). For other providers that don't have hooks, the agent-runner archives after each query completes based on the provider's output. @@ -721,15 +721,13 @@ The agent-runner reads config, creates the provider, and enters the poll loop. N ### Provider Factory ```typescript -type ProviderName = 'claude' | 'codex' | 'opencode'; +type ProviderName = 'claude' | string; function createProvider(name: ProviderName, config: ProviderConfig): AgentProvider { - switch (name) { - case 'claude': return new ClaudeProvider(config); - case 'codex': return new CodexProvider(config); - case 'opencode': return new OpenCodeProvider(config); - default: throw new Error(`Unknown provider: ${name}`); - } + // Trunk registers 'claude'; additional providers self-register when installed via skills. + const factory = providerRegistry.get(name); + if (!factory) throw new Error(`Unknown provider: ${name}`); + return factory(config); } ``` @@ -737,7 +735,7 @@ The provider name comes from the container's environment (`AGENT_PROVIDER` env v `ProviderConfig` contains provider-specific settings (API keys, model overrides, etc.) passed via environment variables — not via the interface. Each provider reads what it needs from `env`. -## What Stays From v1 +## Agent-Runner Properties - MCP server is a separate Node process spawned by the provider (via `mcpServers` config) - The MCP server binary is shared across providers — same tools, same DB access @@ -745,21 +743,7 @@ The provider name comes from the container's environment (`AGENT_PROVIDER` env v - Additional directories discovery (`/workspace/extra/*`) - Logging via stderr (`[agent-runner] ...`) -## What Changes From v1 - -| v1 | v2 | -|----|----| -| stdin JSON envelope | Poll session DB | -| IPC input files for follow-ups | Same DB poll + `provider.push()` | -| stdout markers for output | Write messages_out rows | -| MCP tools write IPC files | MCP tools write DB rows | -| `_close` sentinel for shutdown | Host kills container externally | -| `runQuery()` function with inline Claude SDK | `AgentProvider` interface + per-SDK implementations | -| Single provider (Claude) | Pluggable providers (Claude, Codex, OpenCode, future) | -| `ContainerInput` via stdin | Provider config via env vars + session DB for messages | -| IPC polling for follow-ups | DB polling + provider.push() | - ## Related Documents -- **[v2-architecture-draft.md](v2-architecture-draft.md)** — High-level architecture (session DB schema, central DB, channel adapters, message flow) -- **[v2-api-details.md](v2-api-details.md)** — Channel adapter interface, message content examples, host delivery logic +- **[architecture.md](architecture.md)** — High-level architecture (session DB schema, central DB, channel adapters, message flow) +- **[api-details.md](api-details.md)** — Channel adapter interface, message content examples, host delivery logic diff --git a/docs/v2-api-details.md b/docs/api-details.md similarity index 93% rename from docs/v2-api-details.md rename to docs/api-details.md index 37b318826..13d70ab37 100644 --- a/docs/v2-api-details.md +++ b/docs/api-details.md @@ -1,10 +1,10 @@ -# NanoClaw v2 API Details +# NanoClaw API Details -Implementation-level details for the v2 architecture. See [v2-architecture-draft.md](v2-architecture-draft.md) for the high-level design. +Implementation-level details for the architecture. See [architecture.md](architecture.md) for the high-level design. ## Channel Adapter Interface -### NanoClaw Channel Interface (v2) +### NanoClaw Channel Interface ```typescript interface ChannelSetup { @@ -59,7 +59,7 @@ interface OutboundMessage { ### Chat SDK Bridge -Wraps a Chat SDK adapter + Chat instance to conform to the NanoClaw ChannelAdapter interface. +Wraps a Chat SDK adapter + Chat instance to conform to the NanoClaw ChannelAdapter interface. Trunk ships the bridge and the channel registry only — platform-specific Chat SDK adapters (Discord, Slack, Telegram, etc.) and native adapters (WhatsApp/Baileys) are installed by the `/add-` skills from the `channels` branch. ```typescript function createChatSdkBridge( @@ -170,7 +170,7 @@ function createChatSdkBridge( ### Native NanoClaw Channel (no Chat SDK) -Native channels implement the ChannelAdapter interface directly. Example structure for WhatsApp/Baileys: +Native channels implement the ChannelAdapter interface directly. The WhatsApp/Baileys adapter is the canonical example — it ships via the `/add-whatsapp` skill, not in trunk: ```typescript function createWhatsAppChannel(): ChannelAdapter { @@ -293,14 +293,14 @@ function createWhatsAppChannel(): ChannelAdapter { "type": "card", "title": "Deployment Approval", "children": [ - { "type": "text", "content": "Deploy v2.1.0 to production?" }, + { "type": "text", "content": "Deploy 2.1.0 to production?" }, { "type": "actions", "children": [ { "type": "button", "id": "approve", "label": "Approve", "style": "primary" }, { "type": "button", "id": "reject", "label": "Reject", "style": "danger" } ]} ] }, - "fallbackText": "Deployment Approval: Deploy v2.1.0 to production? [Approve] [Reject]" + "fallbackText": "Deployment Approval: Deploy 2.1.0 to production? [Approve] [Reject]" } ``` diff --git a/docs/v2-architecture-diagram.html b/docs/architecture-diagram.html similarity index 98% rename from docs/v2-architecture-diagram.html rename to docs/architecture-diagram.html index 2f62957f2..a93395a4d 100644 --- a/docs/v2-architecture-diagram.html +++ b/docs/architecture-diagram.html @@ -3,7 +3,7 @@ - NanoClaw v2 Architecture + NanoClaw Architecture