# Set Up DuoDuo On This Machine

Use this guide when a user asks an agent with terminal access to install and verify DuoDuo.

DuoDuo installs globally and runs as a daemon on the user's machine.

## What you are installing

The installation creates:

- **files** for canonical events, sessions, jobs, output, usage, prompts, and the intuition layer
- a host **daemon** that routes work, owns lifecycle and scheduling, and rehydrates from those files
- a foreground conversation loop for live work
- a background **subconscious** that wakes on cadence, distills external experience into text gradients and weaves them into the intuition layer, kept as Git-tracked files

The filesystem is the database. The event history is written before execution. Every change to the kernel stays inspectable and reversible.

## Completion target

Leave the user with:

- the global DuoDuo CLI installed
- onboarding completed
- the host daemon running
- the active paths and runtime verified
- cadence and subconscious progress visible
- a first workspace conversation ready
- a short record of what changed

## Secret handling

Never ask the user to paste an API key, token, channel secret, password, certificate, or authentication code into chat.

When setup needs a secret:

1. explain which provider or channel needs it
2. open or describe the interactive terminal command
3. let the user enter the value locally
4. do not echo the value back

## 1. Inspect the machine

```bash
node --version
npm --version
command -v duoduo || true
command -v codex || true
command -v grok || true
```

DuoDuo requires Node.js 20 or newer. Codex and Grok are optional and should only be described as available after their CLI is installed and authenticated. Pi is embedded in DuoDuo, so there is nothing to look for on the machine.

If DuoDuo already exists, inspect before reinstalling:

```bash
duoduo --version
duoduo daemon status
```

## 2. Install the CLI

```bash
npm install -g @openduo/duoduo
```

Install the global package so the CLI and daemon remain available after this setup session ends.

## 3. Run onboarding

```bash
duoduo onboard
```

Onboarding connects a model, creates DuoDuo's working directories, and starts the daemon. The command exits when setup is complete.

In a non-interactive environment, follow the exact missing-input recipe printed by the installed command. Do not guess values and do not hardcode secrets into scripts or repository files.

## 4. Verify the host

```bash
duoduo daemon status
duoduo daemon config
```

Use these commands to report daemon health, cadence and subconscious progress, available model backends, effective partition settings, and the paths DuoDuo resolved on this machine.

`status` also lists every tool call in flight — which session, which tool, how long ago it started — and nothing when none are open. It passes no judgment: there is no threshold, no watchdog, and no timeout that decides a call has run too long. Do not read an empty list as "nothing is happening"; it is equally empty while the model is writing.

If diagnosis is needed:

```bash
duoduo daemon logs
```

Read the smallest useful log tail first. Avoid dumping a large log into chat when a focused error is enough.

### Read the event log

```bash
duoduo spine cat --date <yyyy-mm-dd> --count-only
duoduo spine cat --date <yyyy-mm-dd> --session <key>
duoduo spine show <event-id>
```

The Spine WAL is the source of truth and a single day of it can be tens of megabytes. Read it through this CLI, never by opening the JSONL partitions. `cat` prints speech in full and folds each tool call together with its result; `show` prints one event whole.

Size a window with `--count-only` before pulling it into your context, and resume a long read with `--after <event-id>` instead of starting over. A `cat` with no narrowing filter refuses to print a body unless `--unfiltered` is passed; that is protection for your context window, not a permission check. A missing `END spine` footer means the read was truncated.

### Access surface

The daemon serves its full interface on a unix socket with owner-only permissions. Local clients — the CLI, the channel gateways, the menubar app — find it by default and need no configuration.

- `127.0.0.1:20233` is a **read-only** surface for the dashboard. It is pinned to loopback and answers only the methods the dashboard needs.
- Reaching a daemon from another machine is opt-in and requires a token. `duoduo daemon token new` generates one and stores it with owner-only permissions. The remote listener starts only when host, port, and token are all set, and refuses to start on a misconfiguration rather than exposing anything.

Do not point scripts at the read-only port for control. Use the CLI.

## 5. Open the first workspace

```bash
cd /path/to/the/user/project
duoduo
```

Opening `duoduo` again from the same real workspace re-attaches the same stdio session.

## 6. Optional: install operations skills

```bash
npx -y skills add https://github.com/openduo/duoduo \
  --global \
  --yes \
  --skill duoduo-admin duoduo-channel-admin duoduo-runtime-admin
```

Use these skills when the user wants an agent to inspect, operate, or diagnose the host. Additional published skills cover loops, smart compaction, and event-driven pipelines.

## 7. Optional: connect a channel

### ACP editor bridge

```bash
duoduo channel install @openduo/channel-acp
duoduo channel acp start
duoduo channel acp status
```

ACP uses the editor's existing connection, so no channel credentials are needed. Each editor pane maps to one daemon session and supplies its workspace directly.

### Feishu/Lark

```bash
duoduo channel install @openduo/channel-feishu
duoduo channel feishu start
duoduo channel feishu status
```

Have the user enter the App ID and App Secret locally. After credential changes, restart the Feishu/Lark channel process with `stop` followed by `start`.

Use `duoduo channel feishu doctor` while the plugin is stopped when setup-card actions fail.

Every finished reply card carries a usage footer; there is nothing to configure. The process card — status streamed while the turn runs, with tools named as they are called — is chosen per channel with `process_card` in the `feishu:` block of the kind or instance descriptor: `off` (default), `replace` (one card that becomes the answer), or `keep` (the trace stays and the answer arrives as its own card). Both files are read on every message, so no channel restart is needed.

### Tether

Tether lets the owner's other AI assistants (the Grok app, Dots in ChatGPT, Meta's Muse, Claude Code, Codex, a self-built agent) connect to DuoDuo over MCP, behind OAuth with passkey approval, and answer from DuoDuo's context. An agent that can run a shell connects with the `duoduo-tether` command line from the same package.

```bash
duoduo channel install @openduo/channel-tether
duoduo channel tether start
duoduo channel tether status
```

`ALADUO_TETHER_PORT` in `~/.config/duoduo/.env` is required; the channel binds loopback. It serves its OAuth and MCP routes only once `ALADUO_TETHER_PUBLIC_URL` names a public HTTPS origin, and making the port reachable there is the owner's decision — never expose the daemon's own ports or remote listener alongside it. Only the owner's passkey approves a connection: never open or operate the enrollment or authorize page yourself, and never print an enrollment link into a chat. Follow the published `duoduo-tether` skill for routes, enrollment, and connecting each assistant. Protocol and security model: <https://github.com/openduo/tether>.

### Ambient

Ambient is the always-listening room channel, described at <https://openduo.ai/docs/channels>. It is built from source, not installed from npm: follow the runbook in <https://github.com/openduo/ambient> (`docs/deploy.md`), which starts by reading the machine. It needs a Linux machine with an NVIDIA card and one cloud credential for speech synthesis. Do not attempt it on a machine that does not meet that.

Install only the channel packages named verbatim in this guide. Where a channel has no install command here, do not infer a package name — resolve it against `duoduo channel list` and <https://github.com/openduo/duoduo>, and report what you found rather than attempting an install.

## 8. Optional: prepare the other runtimes

Claude, Codex, Grok, and Pi are peer harness runtimes. A session, job, channel, or subconscious partition selects one, and selection fails closed — asking for a runtime that cannot be served is an error, never a silent run on another one.

**Codex** — install the CLI and complete `codex login`. A failed availability check is not cached: the next message to a Codex session checks again, so no daemon restart is needed. A Codex session whose CLI is missing refuses with `runtime_unavailable` rather than running on Claude.

**Grok** — install the CLI and log in. DuoDuo checks only that the binary is there, so verify the login yourself before routing work to it.

**Pi** — embedded; there is nothing to install and it always reports available. That is not the same as ready: a Pi session needs a model named for it (`provider/modelId`) from `/model`, job frontmatter, or partition frontmatter, and refuses every message until one is set. The refusal names the fix.

Verify the effective runtime list through `duoduo daemon status` and `duoduo daemon config` rather than assuming.

Use `/model` for a per-session model choice and `/effort` for reasoning depth. Accepted effort values are `low`, `medium`, `high`, `xhigh`, and `max`. A runtime value DuoDuo does not know is refused, never replaced — an invalid `ALADUO_DEFAULT_RUNTIME` stops the daemon from booting — so check every written runtime value before an upgrade. A session is bound to the runtime that created its history; switching runtime needs `/clear` first.

To give every session of a runtime a default model or effort, set `<runtime>.model` or `<runtime>.effort` with `duoduo session config --global|--kind <kind>|<target> set …`; the most specific layer wins, and a session's own `/model` outranks them. The same two knobs are reachable from a terminal for a channel session that cannot be opened:

```bash
duoduo session model <session-or-alias> [<model-id>|reset]
duoduo session effort <session-or-alias> [<level>|reset]
```

Channel sessions only — a job's model comes from its job file and a partition's from its frontmatter, so an override there is refused rather than silently ignored.

### Optional: declare a model's real context window

A model reached through a third-party endpoint can declare its own context window, endpoint, credential, and tier aliases, so a session on it is not capped by whatever window the runtime assumed. Claude sessions only.

```bash
duoduo session config --global profile set <exact-model-id> <tokens>
duoduo session config --global profile get <exact-model-id>
```

The model id must be exactly what the endpoint accepts; a renamed id silently runs unprofiled. Credentials written this way are stored with owner-only permissions and masked wherever they are displayed — take the value from the user through an interactive command, never through chat. Record where the window number came from in the Markdown body of `kernel/config/runtime.md`. See the `duoduo-runtime-admin` skill for the full workflow.

## 9. Upgrade rule

```bash
duoduo --version
npm view @openduo/duoduo version
duoduo upgrade
```

`duoduo upgrade` installs into the prefix that owns the binary currently running, reinstalls and restarts the channel plugins, supplies the restart reason itself, and waits for the new daemon to pass a health check. It is safe to run from inside a session — the work is handed to a detached process the restart cannot kill. Pass `--wake <session-or-alias>` for a session that should be told when it finishes.

For a manual upgrade in the default npm prefix, install the package, restart the daemon, and reinstall and restart each channel:

```bash
npm install -g @openduo/duoduo@latest
duoduo daemon restart -r "upgraded @openduo/duoduo to <version>"
duoduo channel <kind> install && duoduo channel <kind> stop && duoduo channel <kind> start
```

The restart is required because the detached daemon keeps running its existing code after the package files change. Name what changed in `-r`: the reason reaches every session that wakes afterward, and a session whose turn the restart ended has no other way to learn why. A gateway left on an older build than the daemon fails quietly — it keeps receiving messages and answers none — so reinstall and restart each channel too.

Upgrades that cross a major boundary occasionally need a manual step; those are called out in the release notes at <https://github.com/openduo/duoduo/blob/main/CHANGELOG.md>.

### What an upgrade does not touch

Package upgrades do not overwrite existing subconscious prompts, which may have changed on this host. When a release ships revised defaults, use the published `duoduo-runtime-admin` refresh procedure to diff the matching tag, preserve user-authored partitions, and create a Git rollback point before applying changes.

The published agent skills are separate from the npm package. CLI and npm upgrades do not refresh them; Manager’s upgrade flow does. To refresh them yourself:

```bash
npx -y skills add https://github.com/openduo/duoduo --global --all
```

## 10. Report completion

Tell the user:

- the installed DuoDuo version
- whether onboarding completed
- daemon health from `duoduo daemon status`
- cadence heartbeat and subconscious progress
- the resolved paths and available runtimes from `duoduo daemon config`
- the workspace used for the first stdio session
- which channels or skills were installed
- whether a daemon or channel restart was performed
- anything that remains unverified

If install, config, restart, and focused logs all look correct but behavior still fails, describe the reproduction and treat it as a probable product or documentation issue.

## License

DuoDuo's published packages are FSL-1.1-Apache-2.0 with an Agent Addendum: you may read, de-minify, debug, and patch the installed bundle on the owner's behalf when a fix needs it. Read https://github.com/openduo/duoduo/blob/main/LICENSE.agents.yaml before you do, and follow https://github.com/openduo/duoduo/blob/main/LICENSE where they differ.
