Add an Atomic SDK harness for the hosted Planner #210
- LA
## Summary
Adds `HARNESS=atomic`, a `HarnessV1` adapter (`apps/server/src/harness/atomic/adapter.ts`) that embeds `@bastani/atomic` 0.9.23 in the Chopin server process through its headless SDK: `createAgentSession`, `ModelRuntime`, `DefaultResourceLoader`, and in-memory session and settings managers. It does not spawn the `atomic` CLI, use RPC mode, or alias Pi. The `copilot-sdk` and `pi` harnesses are unchanged.
- `07c5852` adds the adapter, its unit tests (`atomic/adapter.test.ts`), the shared contract suite (`harness/atomic.contract.test.ts`), `harnesses.ts`/`config.ts` wiring and tests, and docs (`docs/hosted-agent.md`, `docs/self-hosting.md`, `docs/architecture.md`, `README.md`). - `878147a` keeps the research notes that map the SDK to `HarnessV1` and list the caveats. - `4c685ef` documents that `auto` also honors the legacy `PI_CODING_AGENT_DIR`. Atomic checks `ATOMIC_CODING_AGENT_DIR` first, then `PI_CODING_AGENT_DIR`, and a set variable disables the `~/.pi` fallback.
This branch was rebased onto `main` (99204aa). The only conflict was `bun.lock`. I regenerated it from main's lockfile, and it matches the branch's original lockfile except for main's `impeccable` entries.
## Security boundary
- Only Chopin's host tools are exposed. Every shipped Atomic package is disabled (workflows, subagents, MCP, web access, Intercom), and Atomic's coding tools are not registered. - The resource loader discovers no context files, skills, prompt templates, themes, or extensions. The working and configuration directory is an empty private temporary directory. - A per-turn hook replaces the whole system prompt, so Atomic's coding-agent preamble never reaches the model. - The active tool set is re-checked before every model request, and any unexpected tool fails the turn. The session is also checked before the first request for extensions, context files, skills, templates, or prompt drift. - An unknown `MODEL` fails the turn before any model request. Atomic would otherwise silently pick another model. - Structured output uses a terminating result tool, enabled only when the turn requests a JSON response format and blocked otherwise. It does not use Atomic's `createStructuredOutputTool`, which makes a second model call.
## Auth modes
`HARNESS=atomic` requires an explicit `HARNESS_AUTH`:
- `auto` copies the host's Atomic login into memory (`$ATOMIC_CODING_AGENT_DIR`, then `$PI_CODING_AGENT_DIR`, otherwise `~/.atomic/agent` over `~/.pi/agent`), plus env keys and ambient cloud credentials. Startup refuses it unless `SERVER_HOST` is loopback-only. - `ai-gateway` reads `AI_GATEWAY_API_KEY` and accepts only `vercel-ai-gateway` models. It is the only mode allowed on a public bind.
No credentials, `models.json`, or `models-store.json` are written to disk.
## Documented caveats
- Atomic's SDK defaults suit a local coding agent. The adapter overrides them and fails closed on the ones it can observe, but a future release could add a default the suite does not check. Run the contract suite before bumping `@bastani/atomic`. - Sessions live only in memory and cannot resume across restarts. Compaction, turn suspension, and skills are unsupported. Atomic's automatic retries stay on. - Under `auto`, an in-memory OAuth refresh can rotate the host CLI's refresh token. `!command` API-key entries in `auth.json` are executed. - There is no per-session credit limit, so a worker's `maxAiCredits` does not apply. - `@bastani/atomic` adds more than 250 MB of dependencies. The rebuilt `chopin-atomic` image is 2.1 GB. - Atomic uses `typebox` 1.3.27 while Chopin pins 1.3.7. Host tool schemas cross as plain JSON Schema, so the two versions never meet.
## Verification on this machine
These checks ran on a new checkout after the rebase. Earlier results from the original machine are in the `07c5852` commit trailers.
The Playwright suite does not exercise `HARNESS=atomic`; its agent servers use `AGENT=off` or the fake harness. I tested the Atomic harness separately in a real browser against a real model.
### Browser test of the Atomic harness with a live model
I started a server on loopback with `AGENT=on HARNESS=atomic HARNESS_AUTH=auto MODEL=github-copilot/gpt-6-luna`, using the E2E preloads (`e2e/github.ts` and `e2e/harness/preload.ts`). Those preloads fake only GitHub's REST and OAuth endpoints and the GitHub MCP URL. Model traffic went to GitHub Copilot through the host's Atomic login. With agent-browser I signed in through the server's real OAuth callback, added `octo-org/score` in the UI, created a document, typed a sentence, and sent two `@chopin` turns.
1. **Read turn.** The Planner called `read_plan` and `list_pull_requests`, then answered with the document's sentence and the fixture PR title "Restring the harp section". The fake MCP log shows `list_pull_requests` was called for `octo-org/score` with a bearer token, `readonly=true`, and `toolsets=pull_requests`.

2. **Edit turn.** The Planner called `read_plan` and then `edit_plan` twice to append a three-item list and keep the existing sentence. The document and the transcript were unchanged after a page reload.

The server's `[agent]` diagnostic listed exactly Chopin's 16 host tools plus `chopin_submit_atomic_result`. It showed no `bash`, filesystem, fetch, or Intercom tool. Nothing in `~/.atomic/agent` was modified during the run.
Assistant-verification: docker build passed: `docker build --no-cache -t chopin-atomic .` completed; both `bun install --frozen-lockfile` stages fetched from registry.npmjs.org (build 995 packages, production 908 packages) Assistant-verification: docker runtime import passed: `docker run --rm chopin-atomic bun -e "await import('./apps/server/src/harness/harnesses.ts')"` and a direct import of `atomic/adapter.ts` both loaded `@bastani/atomic` in the production image Assistant-verification: bun run fix passed: no changes beyond the doc edit; one existing oxlint warning in the untouched `github-tools.test.ts` Assistant-verification: bun test passed: 1688 pass, 2 PostgreSQL skips, 0 fail across 190 files; `apps/server/src/harness` alone 127 pass, covering atomic, copilot-sdk and pi Assistant-verification: bun run types passed: all workspaces and E2E Assistant-verification: bun run ci passed: dprint, oxlint (0 errors), tokens, type scale, Impeccable design check Assistant-verification: bun run e2e passed: 236 Chromium tests after confirming ports 8788, 8789, 8791, 8792 and 8797 were free Assistant-verification: agent-browser E2E passed: live github-copilot/gpt-6-luna turns through HARNESS=atomic HARNESS_AUTH=auto ran read_plan, list_pull_requests over the fake GitHub MCP (bearer, readonly, pull_requests toolset) and edit_plan; the edit and transcript persisted across reload; ~/.atomic/agent was not modified Assistant-verification: atomic source check passed: `@bastani/atomic` 0.9.23 `dist/config.js` reads `ATOMIC_CODING_AGENT_DIR`, then `PI_CODING_AGENT_DIR`
Assistant-model: Claude Opus 5.5 Assistant-workflow: inline Assistant-duration: 12m converged, estimated none given User-preference: Build the Atomic harness on Atomic's headless SDK, which separates runtime from hosts Co-authored-by: Alex Lavaee <lavaman131@github.com>
## Merge preparation
Rebased onto main after #236 and #212. The database lifecycle test now allows 20 seconds for its three server starts, rather than Bun’s default five seconds. Its server-readiness checks and all assertions are unchanged. The previously passing Atomic run took 4.986 seconds; the rebased run hit the five-second limit. Fresh CI passed on the updated head: validation, browser integration, and container build.
lavaman131referenced this in Empty paragraphs make room projection non-canonical, failing document descriptions #211last month on GitHub
lavaman131head-ref-force-pushed 115df2598d14last month on GitHub
lavaman131referenced this in Run the Planner as a full Atomic session with questions routed to Decisions #215last month on GitHub
lavaman131added-to-stacklast month on GitHub
- MA
No review summary.
- MAHidden as resolved.
- MA
No review summary.
lavaman131head-ref-force-pushed 7544ed030aa0last month on GitHub
- LAHidden as resolved.
- LA
No review summary.
- MA
all good
MaggieAppletonhead-ref-force-pushed 6df7db0b4b75last month on GitHub
MaggieAppletonhead-ref-force-pushed a390374b3c64last month on GitHub
MaggieAppletonmerged 5ac2f30cc60alast month on GitHub
MaggieAppletonhead-ref-deletedlast month on GitHub