nandi/oripublic Fork 0
5d476ff
Commits
Clone
git clone https://git.rickub.com/nandi/ori.git
git clone ssh://git@rickub.com/nandi/ori.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

forked from bots-garden/ori

✨ Switch the ACP adapter to @agentclientprotocol/claude-agent-acp (template 0.0.2)

The Zed adapter (@zed-industries/claude-code-acp 0.16.2) embeds Claude Code
2.1.44, which the API now rejects for current models ("version 2.1.251 or
newer is required"), so every prompt in the sandbox failed. The Agent Client
Protocol adapter ships a current CLI (2.1.274) through the Claude Agent SDK's
per-platform native package.

- backend: DefaultAgentCommand → npx -y @agentclientprotocol/claude-agent-acp
  (+ pinning test); doc comments updated
- template/Dockerfile: adapter stage installs the new adapter for the target
  platform (Docker amd64 → npm x64 mapping, asserted), CMD updated
- kits/ori: startup + agent instructions use claude-agent-acp, template 0.0.2;
  ports.container is a literal (sbx kit pack/push decode the raw spec)
- internal/files: pin .md/.markdown → text/markdown (host mime table differs)
- ui: Node 25 webstorage flag on the test script; Markdown.tsx type cast so
  the build compiles; ToolCallCard copy text extracted into pure helpers
  with tests (clears two complexity smells)
- .qlty: golangci-lint pinned to 2.13.2 (Go 1.26 project)
- docs EN+FR, packages diagram, kit/scripts/root READMEs, quickstart,
  launcher: adapter, template 0.0.2, dual-stack -p …/tcp, Chrome unsafe ports
- .memory: summary, history, handoffs

Quality gate run #18 PASS (0 errors, 0 warnings, 0 smells). go test ./... and
134 vitest green.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
k33g committed 2026-09-18T10:05:11+02:00 Browse files
5d476ff parent: 7895c1d
added .memory/handoffs/2026-09-18-claude-version-too-old.md +24 -0
new file mode 100644
@@ -0,0 +1,24 @@
1+# 2026-09-18 — Claude Code too old inside the ori sandbox
2+
3+## Problem
4+
5+Prompt fails: `Claude Code 2.1.44 does not support this model; version 2.1.251 or newer is required` (`claude_code_version_too_old`). Model = host `~/.claude/settings.json``claude-fable-5-1[1m]`. Sandbox `ori`: adapter `@zed-industries/claude-code-acp@0.16.2` → bundled CLI 2.1.44; system CLI `/home/agent/.local/bin/claude``~/.local/share/claude/versions/2.1.246` (also too old). ori's env has no `CLAUDE_CODE_EXECUTABLE`.
6+
7+## Immediate workaround (per sandbox, not yet run)
8+
9+```bash
10+sbx exec ori claude update # bucket latest = 2.1.276; egress verified OK
11+sbx exec ori ls /home/agent/.local/share/claude/versions/
12+sbx exec ori sh -c 'pkill -x ori; PATH=/usr/local/share/npm-global/bin:/home/agent/.local/bin:$PATH \
13+ CLAUDE_CODE_EXECUTABLE=/home/agent/.local/bin/claude \
14+ nohup ori --addr :8888 --agent-cmd claude-code-acp --cwd /home/agent/workspace >> /home/agent/.ori.log 2>&1 &'
15+```
16+If `~/.local/bin/claude` still points at 2.1.246 after the update, use the explicit `versions/2.1.276` path. Lost at `sbx rm`; survives stop/start (home is persistent).
17+
18+## Permanent fix — two options (user decides)
19+
20+**(b) recommended — switch adapter.** `template/Dockerfile` stage `adapter`: `npm install -g --prefix /adapter @agentclientprotocol/claude-agent-acp`; `CMD` and `kits/ori/spec.yaml` startup + agentInstructions: `--agent-cmd claude-agent-acp`; docs (summary.md line about the Zed adapter, run how-to EN/FR, kit README). **Trap**: the SDK's CLI is a per-platform optional dependency, and the `adapter` stage is built `--platform=$BUILDPLATFORM` then copied to the target — install for the target instead (`npm install --os=linux --cpu=${TARGETARCH} …`, npm ≥ 10) or drop `--platform=$BUILDPLATFORM` on that stage. Verify in the image: `claude-agent-acp` starts and a prompt round-trips (`pong`). Then `./template/build.sh 0.0.2`, bump `--template k33g/ori:0.0.2` in kit description/docs/launcher, `sbx kit push kits/ori docker.io/k33g/ori-kit:latest`.
21+
22+**(a) keep Zed adapter.** Dockerfile: `USER agent` + `RUN claude update` (≈250 MB layer, chases versions) — or download a pinned version from the release bucket; kit startup: `export CLAUDE_CODE_EXECUTABLE=/home/agent/.local/bin/claude`. Works until the API raises the floor again.
23+
24+Neither is started.
new file mode 100644
@@ -0,0 +1,24 @@
1+# 2026-09-18 — Claude Code too old inside the ori sandbox
2+
3+## Problem
4+
5+Prompt fails: `Claude Code 2.1.44 does not support this model; version 2.1.251 or newer is required` (`claude_code_version_too_old`). Model = host `~/.claude/settings.json``claude-fable-5-1[1m]`. Sandbox `ori`: adapter `@zed-industries/claude-code-acp@0.16.2` → bundled CLI 2.1.44; system CLI `/home/agent/.local/bin/claude``~/.local/share/claude/versions/2.1.246` (also too old). ori's env has no `CLAUDE_CODE_EXECUTABLE`.
6+
7+## Immediate workaround (per sandbox, not yet run)
8+
9+```bash
10+sbx exec ori claude update # bucket latest = 2.1.276; egress verified OK
11+sbx exec ori ls /home/agent/.local/share/claude/versions/
12+sbx exec ori sh -c 'pkill -x ori; PATH=/usr/local/share/npm-global/bin:/home/agent/.local/bin:$PATH \
13+ CLAUDE_CODE_EXECUTABLE=/home/agent/.local/bin/claude \
14+ nohup ori --addr :8888 --agent-cmd claude-code-acp --cwd /home/agent/workspace >> /home/agent/.ori.log 2>&1 &'
15+```
16+If `~/.local/bin/claude` still points at 2.1.246 after the update, use the explicit `versions/2.1.276` path. Lost at `sbx rm`; survives stop/start (home is persistent).
17+
18+## Permanent fix — two options (user decides)
19+
20+**(b) recommended — switch adapter.** `template/Dockerfile` stage `adapter`: `npm install -g --prefix /adapter @agentclientprotocol/claude-agent-acp`; `CMD` and `kits/ori/spec.yaml` startup + agentInstructions: `--agent-cmd claude-agent-acp`; docs (summary.md line about the Zed adapter, run how-to EN/FR, kit README). **Trap**: the SDK's CLI is a per-platform optional dependency, and the `adapter` stage is built `--platform=$BUILDPLATFORM` then copied to the target — install for the target instead (`npm install --os=linux --cpu=${TARGETARCH} …`, npm ≥ 10) or drop `--platform=$BUILDPLATFORM` on that stage. Verify in the image: `claude-agent-acp` starts and a prompt round-trips (`pong`). Then `./template/build.sh 0.0.2`, bump `--template k33g/ori:0.0.2` in kit description/docs/launcher, `sbx kit push kits/ori docker.io/k33g/ori-kit:latest`.
21+
22+**(a) keep Zed adapter.** Dockerfile: `USER agent` + `RUN claude update` (≈250 MB layer, chases versions) — or download a pinned version from the release bucket; kit startup: `export CLAUDE_CODE_EXECUTABLE=/home/agent/.local/bin/claude`. Works until the API raises the floor again.
23+
24+Neither is started.
added .memory/handoffs/2026-09-18-kit-push.md +19 -0
new file mode 100644
@@ -0,0 +1,19 @@
1+# 2026-09-18 — Publishing the ori kit to Docker Hub
2+
3+## State
4+
5+Kit fixed so that `sbx kit pack` / `push` accept it (see history). Ready to publish; **not pushed yet**:
6+
7+```bash
8+sbx kit push kits/ori docker.io/k33g/ori-kit:latest # add --sign for a Sigstore signature
9+sbx kit inspect docker.io/k33g/ori-kit:latest # check after push
10+sbx run -d claude . --template k33g/ori:0.0.1 --kit docker.io/k33g/ori-kit:latest --name ori -p 5555:8888
11+```
12+Auth order for push: `sbx login` session → `sbx secret set --registry` → Docker credential store (`~/.docker/config.json` has a docker.io entry).
13+
14+## Traps
15+
16+- No `sbx kit package`; verbs are `pack`, `push`, `validate`, `inspect`, `pull`, `sign`, `verify`, `provenance`. They take the kit **directory**.
17+- `sbx kit validate` succeeding does **not** guarantee `pack`/`push` will: validate expands `${{ kit.args.* }}` first, pack/push decode the raw YAML. Never put a placeholder in a non-string field (ports.container). Trial recipe: copy the kit to /tmp, edit, `sbx kit pack /tmp/copy -o /tmp/x.zip`.
18+- Provenance records the git commit of the directory: commit before pushing for a meaningful attestation.
19+- After publishing, the quickstart/README launch commands could reference `--kit docker.io/k33g/ori-kit:latest` instead of `./kits/ori` — not done, the user decides.
new file mode 100644
@@ -0,0 +1,19 @@
1+# 2026-09-18 — Publishing the ori kit to Docker Hub
2+
3+## State
4+
5+Kit fixed so that `sbx kit pack` / `push` accept it (see history). Ready to publish; **not pushed yet**:
6+
7+```bash
8+sbx kit push kits/ori docker.io/k33g/ori-kit:latest # add --sign for a Sigstore signature
9+sbx kit inspect docker.io/k33g/ori-kit:latest # check after push
10+sbx run -d claude . --template k33g/ori:0.0.1 --kit docker.io/k33g/ori-kit:latest --name ori -p 5555:8888
11+```
12+Auth order for push: `sbx login` session → `sbx secret set --registry` → Docker credential store (`~/.docker/config.json` has a docker.io entry).
13+
14+## Traps
15+
16+- No `sbx kit package`; verbs are `pack`, `push`, `validate`, `inspect`, `pull`, `sign`, `verify`, `provenance`. They take the kit **directory**.
17+- `sbx kit validate` succeeding does **not** guarantee `pack`/`push` will: validate expands `${{ kit.args.* }}` first, pack/push decode the raw YAML. Never put a placeholder in a non-string field (ports.container). Trial recipe: copy the kit to /tmp, edit, `sbx kit pack /tmp/copy -o /tmp/x.zip`.
18+- Provenance records the git commit of the directory: commit before pushing for a meaningful attestation.
19+- After publishing, the quickstart/README launch commands could reference `--kit docker.io/k33g/ori-kit:latest` instead of `./kits/ori` — not done, the user decides.
modified .memory/history.md +18 -0
@@ -152,3 +152,21 @@
152152 - **Changes**: `.tickets/issues/0005-….yaml` — 8 tasks rewritten (7 `closed` with `updatedAt`/`closedAt` = 2026-09-17T22:23:39Z, 1 `open`; all `priority: none`, author k33g); issue `updatedAt` bumped. `scripts/Launch Ori.app` built with `osacompile`, ignored via `.gitignore` (`scripts/*.app/`). `summary.md` gains a `.tickets` paragraph.
153153 - **Verification**: bundled IssueSpec's `parseIssueDocument` (from the local clone `~/CodeBerg/issuespec`, esbuild `--format=cjs`) and ran it over all 15 tickets: all OK; the committed 0005 fails as expected.
154154 - **Follow-up**: `scripts/README.md` + `scripts/README.fr.md` written (what the launcher does, run vs. double-click, design choices, handler test recipe); quickstart row now points to it.
155+
156+## 2026-09-18 — `sbx kit package` does not exist
157+
158+- User hit `ERROR: sbx: unknown command: sbx kit package` (sbx v0.43.0). The verbs are `sbx kit pack DIRECTORY [-o file.zip]` (local ZIP) and `sbx kit push DIRECTORY registry/repo:tag` (package + push to OCI, adds SLSA provenance, `--sign` optional). Both take the kit **directory**, not `spec.yaml`. `sbx kit validate kits/ori` → VALID. Not pushed: publishing to Docker Hub left to the user.
159+
160+## 2026-09-18 — `sbx kit push` rejected the kit: arg placeholder in an integer field
161+
162+- **Asked**: `sbx kit push kits/ori docker.io/k33g/ori-kit:latest` failed with `invalid spec.yaml: line 38: cannot unmarshal !!str `${{ kit...` into int`.
163+- **Cause (verified in `sandboxes/` source, v0.44.0-rc1+26, and by trial)**: `Pack`/`Push` call `LoadFromDirectory` → strict typed decode of the raw spec (`ports[].container` is `int`), with **no** `${{ kit.args.* }}` expansion; `validate`/`inspect`/`run` expand first (`expand.go`, `argScan`), hence VALID vs INVALID for the same directory. v3 kits (`schemaVersion: "3"`, `com.docker.runtime/port@1` capabilities) keep placeholders at push and expand at pull, but the installed sbx v0.43.0 has no v3 support (0 hits for the capability type in the binary).
164+- **Changes**: `kits/ori/spec.yaml` `ports[0].container` → literal `8888` with a comment explaining why; `${{ kit.args.port }}` kept in the startup command and agentInstructions (string fields decode fine). Kit README, docs EN/FR how-to `run-in-a-sandbox`: with a custom port, publish it with `-p` since the declared port stays 8888. Rejected: dropping the `port` arg (loses a documented feature); moving to v3 (needs sbx ≥ 0.44 on every consumer).
165+- **Verification**: `sbx kit validate kits/ori` (default and `--kit-arg ori.port=9000`) VALID; `sbx kit pack kits/ori` OK; `inspect --kit-arg ori.port=9000` shows `--addr ":9000"` substituted with `"container": 8888`. **Push not run** — the user publishes.
166+- **Follow-up**: kit published by the user; `sbx kit inspect docker.io/k33g/ori-kit:latest` resolves (mixin, schema v2, 1 port, 1 startup command). Sandboxes running at that time: `ori` (5555→8888) and `hello-ori` (3000→8888), both on this repo.
167+
168+## 2026-09-18 — "Claude Code 2.1.44 does not support this model; 2.1.251 or newer required"
169+
170+- **Asked**: reminder of what to do to avoid this prompt failure in the sandbox (kit `docker.io/k33g/ori-kit:latest`, template `k33g/ori:0.0.1`).
171+- **Diagnosis (read-only, verified)**: ori in sandbox `ori` runs with no `CLAUDE_CODE_EXECUTABLE`, so the Zed adapter 0.16.2 uses its bundled CLI 2.1.44 (SDK 0.2.44). The model comes from the host `~/.claude/settings.json` (`claude-fable-5-1[1m]`), which the API only serves to CLI ≥ 2.1.251. Yesterday's workaround (point the adapter at the template's system CLI) **no longer suffices**: the base image `docker/sandbox-templates:claude-code` (built 2026-08-26) ships CLI 2.1.246. Sandbox egress reaches the Claude Code release bucket (latest = 2.1.276) and npm, so `claude update` inside is feasible. `@agentclientprotocol/claude-agent-acp@0.79.0` (bin `claude-agent-acp`) depends on `@anthropic-ai/claude-agent-sdk@0.3.274`, whose `manifest.json` says CLI **2.1.274**, delivered through per-platform optional deps (`…-sdk-linux-arm64`, `…-linux-x64`, …) — no `cli.js` any more.
172+- **Nothing changed**: options given to the user (immediate: `claude update` in the sandbox + restart ori with `CLAUDE_CODE_EXECUTABLE`; permanent: switch the template to the new adapter, or bake a recent CLI + the env var into template/kit). Their call.
@@ -152,3 +152,21 @@
152 - **Changes**: `.tickets/issues/0005-….yaml` — 8 tasks rewritten (7 `closed` with `updatedAt`/`closedAt` = 2026-09-17T22:23:39Z, 1 `open`; all `priority: none`, author k33g); issue `updatedAt` bumped. `scripts/Launch Ori.app` built with `osacompile`, ignored via `.gitignore` (`scripts/*.app/`). `summary.md` gains a `.tickets` paragraph.152 - **Changes**: `.tickets/issues/0005-….yaml` — 8 tasks rewritten (7 `closed` with `updatedAt`/`closedAt` = 2026-09-17T22:23:39Z, 1 `open`; all `priority: none`, author k33g); issue `updatedAt` bumped. `scripts/Launch Ori.app` built with `osacompile`, ignored via `.gitignore` (`scripts/*.app/`). `summary.md` gains a `.tickets` paragraph.
153 - **Verification**: bundled IssueSpec's `parseIssueDocument` (from the local clone `~/CodeBerg/issuespec`, esbuild `--format=cjs`) and ran it over all 15 tickets: all OK; the committed 0005 fails as expected.153 - **Verification**: bundled IssueSpec's `parseIssueDocument` (from the local clone `~/CodeBerg/issuespec`, esbuild `--format=cjs`) and ran it over all 15 tickets: all OK; the committed 0005 fails as expected.
154 - **Follow-up**: `scripts/README.md` + `scripts/README.fr.md` written (what the launcher does, run vs. double-click, design choices, handler test recipe); quickstart row now points to it.154 - **Follow-up**: `scripts/README.md` + `scripts/README.fr.md` written (what the launcher does, run vs. double-click, design choices, handler test recipe); quickstart row now points to it.
155+
156+## 2026-09-18 — `sbx kit package` does not exist
157+
158+- User hit `ERROR: sbx: unknown command: sbx kit package` (sbx v0.43.0). The verbs are `sbx kit pack DIRECTORY [-o file.zip]` (local ZIP) and `sbx kit push DIRECTORY registry/repo:tag` (package + push to OCI, adds SLSA provenance, `--sign` optional). Both take the kit **directory**, not `spec.yaml`. `sbx kit validate kits/ori` → VALID. Not pushed: publishing to Docker Hub left to the user.
159+
160+## 2026-09-18 — `sbx kit push` rejected the kit: arg placeholder in an integer field
161+
162+- **Asked**: `sbx kit push kits/ori docker.io/k33g/ori-kit:latest` failed with `invalid spec.yaml: line 38: cannot unmarshal !!str `${{ kit...` into int`.
163+- **Cause (verified in `sandboxes/` source, v0.44.0-rc1+26, and by trial)**: `Pack`/`Push` call `LoadFromDirectory` → strict typed decode of the raw spec (`ports[].container` is `int`), with **no** `${{ kit.args.* }}` expansion; `validate`/`inspect`/`run` expand first (`expand.go`, `argScan`), hence VALID vs INVALID for the same directory. v3 kits (`schemaVersion: "3"`, `com.docker.runtime/port@1` capabilities) keep placeholders at push and expand at pull, but the installed sbx v0.43.0 has no v3 support (0 hits for the capability type in the binary).
164+- **Changes**: `kits/ori/spec.yaml` `ports[0].container` → literal `8888` with a comment explaining why; `${{ kit.args.port }}` kept in the startup command and agentInstructions (string fields decode fine). Kit README, docs EN/FR how-to `run-in-a-sandbox`: with a custom port, publish it with `-p` since the declared port stays 8888. Rejected: dropping the `port` arg (loses a documented feature); moving to v3 (needs sbx ≥ 0.44 on every consumer).
165+- **Verification**: `sbx kit validate kits/ori` (default and `--kit-arg ori.port=9000`) VALID; `sbx kit pack kits/ori` OK; `inspect --kit-arg ori.port=9000` shows `--addr ":9000"` substituted with `"container": 8888`. **Push not run** — the user publishes.
166+- **Follow-up**: kit published by the user; `sbx kit inspect docker.io/k33g/ori-kit:latest` resolves (mixin, schema v2, 1 port, 1 startup command). Sandboxes running at that time: `ori` (5555→8888) and `hello-ori` (3000→8888), both on this repo.
167+
168+## 2026-09-18 — "Claude Code 2.1.44 does not support this model; 2.1.251 or newer required"
169+
170+- **Asked**: reminder of what to do to avoid this prompt failure in the sandbox (kit `docker.io/k33g/ori-kit:latest`, template `k33g/ori:0.0.1`).
171+- **Diagnosis (read-only, verified)**: ori in sandbox `ori` runs with no `CLAUDE_CODE_EXECUTABLE`, so the Zed adapter 0.16.2 uses its bundled CLI 2.1.44 (SDK 0.2.44). The model comes from the host `~/.claude/settings.json` (`claude-fable-5-1[1m]`), which the API only serves to CLI ≥ 2.1.251. Yesterday's workaround (point the adapter at the template's system CLI) **no longer suffices**: the base image `docker/sandbox-templates:claude-code` (built 2026-08-26) ships CLI 2.1.246. Sandbox egress reaches the Claude Code release bucket (latest = 2.1.276) and npm, so `claude update` inside is feasible. `@agentclientprotocol/claude-agent-acp@0.79.0` (bin `claude-agent-acp`) depends on `@anthropic-ai/claude-agent-sdk@0.3.274`, whose `manifest.json` says CLI **2.1.274**, delivered through per-platform optional deps (`…-sdk-linux-arm64`, `…-linux-x64`, …) — no `cli.js` any more.
172+- **Nothing changed**: options given to the user (immediate: `claude update` in the sandbox + restart ori with `CLAUDE_CODE_EXECUTABLE`; permanent: switch the template to the new adapter, or bake a recent CLI + the env var into template/kit). Their call.
modified .memory/summary.md +4 -1
@@ -40,6 +40,9 @@ browser ⇆ WebSocket /ws ⇆ Go backend ⇆ stdio (ACP) ⇆ agent subprocess
4040 - `make run` — with Claude Code adapter; `make run-mock` — with the demo agent (no Claude/network needed).
4141 - `make template` — builds the `k33g/ori` sandbox template image (`template/Dockerfile`, FROM `docker/sandbox-templates:claude-code`); launch with `sbx run -d claude <project> --template k33g/ori:0.0.0 --kit <ori repo>/kits/ori --name ori -p 8888:8888` (detached — see the auto-stop trap below) (see `kits/ori/README.md` and docs how-to `run-in-a-sandbox`).
4242 - `scripts/launch-ori.applescript` (2026-09-18) — macOS launcher: `sbx ls -q` → restart by name or create (`k33g/ori:0.0.1`, `./kits/ori`, `-p 5555:8888`), poll `http://localhost:5555/healthz` up to 90 s, then `open ori-desktop/build/bin/ori-desktop.app`. Run with `osascript`, or `osacompile` into `scripts/Launch Ori.app`; repo root derived from the script's own location. Compile-checked and handlers unit-tested; full end-to-end run not yet done.
43+- Kit publishing (sbx v0.43.0, verified 2026-09-18): `sbx kit validate kits/ori`, `sbx kit pack kits/ori -o ori-kit.zip`, `sbx kit push kits/ori docker.io/k33g/ori-kit:<tag>` — directory argument, not `spec.yaml`; there is no `sbx kit package`. Auth: `sbx login` session, then `sbx secret set --registry`, then the Docker credential store.
44+- **Kit packaging trap (verified 2026-09-18)**: `pack`/`push` decode `spec.yaml` raw (no `${{ kit.args }}` expansion) — an arg placeholder in an integer field (`ports[].container`) breaks them even though `validate`/`run` accept it. `ports[0].container` is therefore a literal 8888; the `port` arg only drives the startup command and agentInstructions.
45+- Published kit: `docker.io/k33g/ori-kit:latest` (pushed 2026-09-18, verified with `sbx kit inspect`); usable as `--kit docker.io/k33g/ori-kit:latest` instead of `--kit ./kits/ori`.
4346 - **sbx auto-stop trap (verified in the sbx source + daemon log, 2026-09-17)**: sandboxd stops a sandbox 30 s (hard-coded `WithAutoStopDelay(30*time.Second)`, `sandboxd/pkg/server/backend_dockernext.go`) after its last CLI "sentinel" session closes; `sbx create`/`sbx exec`/`sbx run` all hold one for their own duration, browser traffic on a published port counts for nothing. Only a sandbox created with the spec field `detached: true` (CLI: `sbx run -d …`, absent from `sbx create`) is exempt; the field is create-time only (no PATCH), so an existing sandbox must be `sbx rm`'d and recreated with `sbx run -d`. No settings/env knob for the delay.
4447 - Quality: `python3 ~/.claude/skills/quality/scripts/quality_report.py --workspace .` (gate PASS, run #16, 2026-09-17 late evening: 0 errors, 0 warnings, 0 smells, complex 514 — covers the selectors/previews/desktop work, the resizable file tree and the light/dark theme).
4548 - `docs/source-code-analysis.adoc` — AsciiDoc snapshot analysis of the codebase (2026-09-17), outside the bilingual Diátaxis set.
@@ -48,7 +51,7 @@ browser ⇆ WebSocket /ws ⇆ Go backend ⇆ stdio (ACP) ⇆ agent subprocess
4851
4952 - `~/.qlty` is a dedicated 488MB volume, too small for qlty's tool cache; the cache was moved to `~/.qlty-cache` on the main disk with a symlink `~/.qlty/cache -> /home/agent/.qlty-cache`. If qlty reports "No space left on device", check this symlink survived. (It did NOT survive a sandbox recreation on 2026-09-17: the volume came back empty and the first run failed exactly that way; recreating the symlink fixed it.)
5053 - `~/.npm` is the same kind of 488MB dedicated volume and filled up when installing Monaco; the npm cache now lives at `~/.npm-big` via `NPM_CONFIG_CACHE` exported in `/etc/sandbox-persistent.sh`.
51-- Running ori inside this Claude-Code-driven sandbox requires `env -u CLAUDECODE ./bin/ori` (Claude refuses nested sessions). Also set `CLAUDE_CODE_EXECUTABLE=<current claude binary>` (e.g. `/home/agent/.local/share/claude/versions/2.1.275`): the Zed adapter 0.16.2 bundles Claude Code 2.1.44, which rejects the `fable[1m]` model from `~/.claude/settings.json`.
54+- Running ori inside this Claude-Code-driven sandbox requires `env -u CLAUDECODE ./bin/ori` (Claude refuses nested sessions). Also set `CLAUDE_CODE_EXECUTABLE=<current claude binary>`: the Zed adapter 0.16.2 bundles Claude Code 2.1.44, which rejects the `claude-fable-5-1[1m]` model from the host `~/.claude/settings.json`. **Since 2026-09-18 the API requires CLI ≥ 2.1.251 for that model, and the template's own CLI is 2.1.246 (base image built 2026-08-26)** — so the env var alone is not enough; run `claude update` in the sandbox first, or switch the template to `@agentclientprotocol/claude-agent-acp` (SDK 0.3.274 → CLI 2.1.274). Handoff `2026-09-18-claude-version-too-old.md`.
5255 - npm blocks install scripts by default here; esbuild's postinstall was approved via `npm approve-scripts` (recorded in `ui/package.json` `allowScripts`).
5356
5457 ## Known limitations
@@ -40,6 +40,9 @@ browser ⇆ WebSocket /ws ⇆ Go backend ⇆ stdio (ACP) ⇆ agent subprocess
40 - `make run` — with Claude Code adapter; `make run-mock` — with the demo agent (no Claude/network needed).40 - `make run` — with Claude Code adapter; `make run-mock` — with the demo agent (no Claude/network needed).
41 - `make template` — builds the `k33g/ori` sandbox template image (`template/Dockerfile`, FROM `docker/sandbox-templates:claude-code`); launch with `sbx run -d claude <project> --template k33g/ori:0.0.0 --kit <ori repo>/kits/ori --name ori -p 8888:8888` (detached — see the auto-stop trap below) (see `kits/ori/README.md` and docs how-to `run-in-a-sandbox`).41 - `make template` — builds the `k33g/ori` sandbox template image (`template/Dockerfile`, FROM `docker/sandbox-templates:claude-code`); launch with `sbx run -d claude <project> --template k33g/ori:0.0.0 --kit <ori repo>/kits/ori --name ori -p 8888:8888` (detached — see the auto-stop trap below) (see `kits/ori/README.md` and docs how-to `run-in-a-sandbox`).
42 - `scripts/launch-ori.applescript` (2026-09-18) — macOS launcher: `sbx ls -q` → restart by name or create (`k33g/ori:0.0.1`, `./kits/ori`, `-p 5555:8888`), poll `http://localhost:5555/healthz` up to 90 s, then `open ori-desktop/build/bin/ori-desktop.app`. Run with `osascript`, or `osacompile` into `scripts/Launch Ori.app`; repo root derived from the script's own location. Compile-checked and handlers unit-tested; full end-to-end run not yet done.42 - `scripts/launch-ori.applescript` (2026-09-18) — macOS launcher: `sbx ls -q` → restart by name or create (`k33g/ori:0.0.1`, `./kits/ori`, `-p 5555:8888`), poll `http://localhost:5555/healthz` up to 90 s, then `open ori-desktop/build/bin/ori-desktop.app`. Run with `osascript`, or `osacompile` into `scripts/Launch Ori.app`; repo root derived from the script's own location. Compile-checked and handlers unit-tested; full end-to-end run not yet done.
43+- Kit publishing (sbx v0.43.0, verified 2026-09-18): `sbx kit validate kits/ori`, `sbx kit pack kits/ori -o ori-kit.zip`, `sbx kit push kits/ori docker.io/k33g/ori-kit:<tag>` — directory argument, not `spec.yaml`; there is no `sbx kit package`. Auth: `sbx login` session, then `sbx secret set --registry`, then the Docker credential store.
44+- **Kit packaging trap (verified 2026-09-18)**: `pack`/`push` decode `spec.yaml` raw (no `${{ kit.args }}` expansion) — an arg placeholder in an integer field (`ports[].container`) breaks them even though `validate`/`run` accept it. `ports[0].container` is therefore a literal 8888; the `port` arg only drives the startup command and agentInstructions.
45+- Published kit: `docker.io/k33g/ori-kit:latest` (pushed 2026-09-18, verified with `sbx kit inspect`); usable as `--kit docker.io/k33g/ori-kit:latest` instead of `--kit ./kits/ori`.
43 - **sbx auto-stop trap (verified in the sbx source + daemon log, 2026-09-17)**: sandboxd stops a sandbox 30 s (hard-coded `WithAutoStopDelay(30*time.Second)`, `sandboxd/pkg/server/backend_dockernext.go`) after its last CLI "sentinel" session closes; `sbx create`/`sbx exec`/`sbx run` all hold one for their own duration, browser traffic on a published port counts for nothing. Only a sandbox created with the spec field `detached: true` (CLI: `sbx run -d …`, absent from `sbx create`) is exempt; the field is create-time only (no PATCH), so an existing sandbox must be `sbx rm`'d and recreated with `sbx run -d`. No settings/env knob for the delay.46 - **sbx auto-stop trap (verified in the sbx source + daemon log, 2026-09-17)**: sandboxd stops a sandbox 30 s (hard-coded `WithAutoStopDelay(30*time.Second)`, `sandboxd/pkg/server/backend_dockernext.go`) after its last CLI "sentinel" session closes; `sbx create`/`sbx exec`/`sbx run` all hold one for their own duration, browser traffic on a published port counts for nothing. Only a sandbox created with the spec field `detached: true` (CLI: `sbx run -d …`, absent from `sbx create`) is exempt; the field is create-time only (no PATCH), so an existing sandbox must be `sbx rm`'d and recreated with `sbx run -d`. No settings/env knob for the delay.
44 - Quality: `python3 ~/.claude/skills/quality/scripts/quality_report.py --workspace .` (gate PASS, run #16, 2026-09-17 late evening: 0 errors, 0 warnings, 0 smells, complex 514 — covers the selectors/previews/desktop work, the resizable file tree and the light/dark theme).47 - Quality: `python3 ~/.claude/skills/quality/scripts/quality_report.py --workspace .` (gate PASS, run #16, 2026-09-17 late evening: 0 errors, 0 warnings, 0 smells, complex 514 — covers the selectors/previews/desktop work, the resizable file tree and the light/dark theme).
45 - `docs/source-code-analysis.adoc` — AsciiDoc snapshot analysis of the codebase (2026-09-17), outside the bilingual Diátaxis set.48 - `docs/source-code-analysis.adoc` — AsciiDoc snapshot analysis of the codebase (2026-09-17), outside the bilingual Diátaxis set.
@@ -48,7 +51,7 @@ browser ⇆ WebSocket /ws ⇆ Go backend ⇆ stdio (ACP) ⇆ agent subprocess
48 51
49 - `~/.qlty` is a dedicated 488MB volume, too small for qlty's tool cache; the cache was moved to `~/.qlty-cache` on the main disk with a symlink `~/.qlty/cache -> /home/agent/.qlty-cache`. If qlty reports "No space left on device", check this symlink survived. (It did NOT survive a sandbox recreation on 2026-09-17: the volume came back empty and the first run failed exactly that way; recreating the symlink fixed it.)52 - `~/.qlty` is a dedicated 488MB volume, too small for qlty's tool cache; the cache was moved to `~/.qlty-cache` on the main disk with a symlink `~/.qlty/cache -> /home/agent/.qlty-cache`. If qlty reports "No space left on device", check this symlink survived. (It did NOT survive a sandbox recreation on 2026-09-17: the volume came back empty and the first run failed exactly that way; recreating the symlink fixed it.)
50 - `~/.npm` is the same kind of 488MB dedicated volume and filled up when installing Monaco; the npm cache now lives at `~/.npm-big` via `NPM_CONFIG_CACHE` exported in `/etc/sandbox-persistent.sh`.53 - `~/.npm` is the same kind of 488MB dedicated volume and filled up when installing Monaco; the npm cache now lives at `~/.npm-big` via `NPM_CONFIG_CACHE` exported in `/etc/sandbox-persistent.sh`.
51-- Running ori inside this Claude-Code-driven sandbox requires `env -u CLAUDECODE ./bin/ori` (Claude refuses nested sessions). Also set `CLAUDE_CODE_EXECUTABLE=<current claude binary>` (e.g. `/home/agent/.local/share/claude/versions/2.1.275`): the Zed adapter 0.16.2 bundles Claude Code 2.1.44, which rejects the `fable[1m]` model from `~/.claude/settings.json`.54+- Running ori inside this Claude-Code-driven sandbox requires `env -u CLAUDECODE ./bin/ori` (Claude refuses nested sessions). Also set `CLAUDE_CODE_EXECUTABLE=<current claude binary>`: the Zed adapter 0.16.2 bundles Claude Code 2.1.44, which rejects the `claude-fable-5-1[1m]` model from the host `~/.claude/settings.json`. **Since 2026-09-18 the API requires CLI ≥ 2.1.251 for that model, and the template's own CLI is 2.1.246 (base image built 2026-08-26)** — so the env var alone is not enough; run `claude update` in the sandbox first, or switch the template to `@agentclientprotocol/claude-agent-acp` (SDK 0.3.274 → CLI 2.1.274). Handoff `2026-09-18-claude-version-too-old.md`.
52 - npm blocks install scripts by default here; esbuild's postinstall was approved via `npm approve-scripts` (recorded in `ui/package.json` `allowScripts`).55 - npm blocks install scripts by default here; esbuild's postinstall was approved via `npm approve-scripts` (recorded in `ui/package.json` `allowScripts`).
53 56
54 ## Known limitations57 ## Known limitations
modified .qlty/qlty.toml +4 -0
@@ -78,6 +78,10 @@ name = "bandit"
7878
7979 [[plugin]]
8080 name = "golangci-lint"
81+# 2.13.2 (2026-08-27) is built with Go >= 1.26; qlty's default 1.61.0 (go1.23) and its
82+# latest known 2.6.2 (go1.25) both refuse this repo's `go 1.26.5` ("Go language version
83+# used to build golangci-lint is lower than the targeted Go version") and abort the run.
84+version = "2.13.2"
8185
8286 [[plugin]]
8387 name = "biome"
@@ -78,6 +78,10 @@ name = "bandit"
78 78
79 [[plugin]]79 [[plugin]]
80 name = "golangci-lint"80 name = "golangci-lint"
81+# 2.13.2 (2026-08-27) is built with Go >= 1.26; qlty's default 1.61.0 (go1.23) and its
82+# latest known 2.6.2 (go1.25) both refuse this repo's `go 1.26.5` ("Go language version
83+# used to build golangci-lint is lower than the targeted Go version") and abort the run.
84+version = "2.13.2"
81 85
82 [[plugin]]86 [[plugin]]
83 name = "biome"87 name = "biome"
modified .quality/history.jsonl +2 -0
@@ -14,3 +14,5 @@
1414 {"branch": "feat/acp-webapp", "breaches": ["error-level issues: 12 (max 0)", "warning-level issues: 1 (max 0)", "code smells: 2 (max 0)"], "commit": "2434cc7", "counts": {"error": 12, "note": 4, "warning": 1}, "gate": {"max_error": 0, "max_smells": 0, "max_warning": 0}, "gate_passed": false, "metrics": {"classes": 54, "complex": 517, "cyclo": 888, "fields": 75, "funcs": 255, "lcom": 0, "lines": 5523, "loc": 4005}, "qlty_version": "qlty 0.639.0 linux-arm64 (d9801f1 2026-07-23)", "run": 14, "smells": 2, "timestamp": "2026-09-17T23:07:54Z"}
1515 {"branch": "feat/acp-webapp", "breaches": [], "commit": "2434cc7", "counts": {}, "gate": {"max_error": 0, "max_smells": 0, "max_warning": 0}, "gate_passed": true, "metrics": {"classes": 55, "complex": 507, "cyclo": 877, "fields": 79, "funcs": 260, "lcom": 0, "lines": 5570, "loc": 4030}, "qlty_version": "qlty 0.639.0 linux-arm64 (d9801f1 2026-07-23)", "run": 15, "smells": 0, "timestamp": "2026-09-17T23:10:03Z"}
1616 {"branch": "feat/acp-webapp", "breaches": [], "commit": "2434cc7", "counts": {}, "gate": {"max_error": 0, "max_smells": 0, "max_warning": 0}, "gate_passed": true, "metrics": {"classes": 56, "complex": 514, "cyclo": 893, "fields": 79, "funcs": 267, "lcom": 0, "lines": 5671, "loc": 4082}, "qlty_version": "qlty 0.639.0 linux-arm64 (d9801f1 2026-07-23)", "run": 16, "smells": 0, "timestamp": "2026-09-17T23:15:34Z"}
17+{"branch": "feature/copy-paste-functionality", "breaches": ["code smells: 2 (max 0)"], "commit": "7895c1d", "counts": {}, "gate": {"max_error": 0, "max_smells": 0, "max_warning": 0}, "gate_passed": false, "metrics": {"classes": 57, "complex": 546, "cyclo": 918, "fields": 79, "funcs": 271, "lcom": 0, "lines": 5832, "loc": 4212}, "qlty_version": "qlty 0.615.0 macos-arm64 (bfe0de3 2026-03-02)", "run": 17, "smells": 2, "timestamp": "2026-09-18T07:58:08Z"}
18+{"branch": "feature/copy-paste-functionality", "breaches": [], "commit": "7895c1d", "counts": {}, "gate": {"max_error": 0, "max_smells": 0, "max_warning": 0}, "gate_passed": true, "metrics": {"classes": 57, "complex": 532, "cyclo": 917, "fields": 79, "funcs": 273, "lcom": 0, "lines": 5858, "loc": 4220}, "qlty_version": "qlty 0.615.0 macos-arm64 (bfe0de3 2026-03-02)", "run": 18, "smells": 0, "timestamp": "2026-09-18T08:03:21Z"}
@@ -14,3 +14,5 @@
14 {"branch": "feat/acp-webapp", "breaches": ["error-level issues: 12 (max 0)", "warning-level issues: 1 (max 0)", "code smells: 2 (max 0)"], "commit": "2434cc7", "counts": {"error": 12, "note": 4, "warning": 1}, "gate": {"max_error": 0, "max_smells": 0, "max_warning": 0}, "gate_passed": false, "metrics": {"classes": 54, "complex": 517, "cyclo": 888, "fields": 75, "funcs": 255, "lcom": 0, "lines": 5523, "loc": 4005}, "qlty_version": "qlty 0.639.0 linux-arm64 (d9801f1 2026-07-23)", "run": 14, "smells": 2, "timestamp": "2026-09-17T23:07:54Z"}14 {"branch": "feat/acp-webapp", "breaches": ["error-level issues: 12 (max 0)", "warning-level issues: 1 (max 0)", "code smells: 2 (max 0)"], "commit": "2434cc7", "counts": {"error": 12, "note": 4, "warning": 1}, "gate": {"max_error": 0, "max_smells": 0, "max_warning": 0}, "gate_passed": false, "metrics": {"classes": 54, "complex": 517, "cyclo": 888, "fields": 75, "funcs": 255, "lcom": 0, "lines": 5523, "loc": 4005}, "qlty_version": "qlty 0.639.0 linux-arm64 (d9801f1 2026-07-23)", "run": 14, "smells": 2, "timestamp": "2026-09-17T23:07:54Z"}
15 {"branch": "feat/acp-webapp", "breaches": [], "commit": "2434cc7", "counts": {}, "gate": {"max_error": 0, "max_smells": 0, "max_warning": 0}, "gate_passed": true, "metrics": {"classes": 55, "complex": 507, "cyclo": 877, "fields": 79, "funcs": 260, "lcom": 0, "lines": 5570, "loc": 4030}, "qlty_version": "qlty 0.639.0 linux-arm64 (d9801f1 2026-07-23)", "run": 15, "smells": 0, "timestamp": "2026-09-17T23:10:03Z"}15 {"branch": "feat/acp-webapp", "breaches": [], "commit": "2434cc7", "counts": {}, "gate": {"max_error": 0, "max_smells": 0, "max_warning": 0}, "gate_passed": true, "metrics": {"classes": 55, "complex": 507, "cyclo": 877, "fields": 79, "funcs": 260, "lcom": 0, "lines": 5570, "loc": 4030}, "qlty_version": "qlty 0.639.0 linux-arm64 (d9801f1 2026-07-23)", "run": 15, "smells": 0, "timestamp": "2026-09-17T23:10:03Z"}
16 {"branch": "feat/acp-webapp", "breaches": [], "commit": "2434cc7", "counts": {}, "gate": {"max_error": 0, "max_smells": 0, "max_warning": 0}, "gate_passed": true, "metrics": {"classes": 56, "complex": 514, "cyclo": 893, "fields": 79, "funcs": 267, "lcom": 0, "lines": 5671, "loc": 4082}, "qlty_version": "qlty 0.639.0 linux-arm64 (d9801f1 2026-07-23)", "run": 16, "smells": 0, "timestamp": "2026-09-17T23:15:34Z"}16 {"branch": "feat/acp-webapp", "breaches": [], "commit": "2434cc7", "counts": {}, "gate": {"max_error": 0, "max_smells": 0, "max_warning": 0}, "gate_passed": true, "metrics": {"classes": 56, "complex": 514, "cyclo": 893, "fields": 79, "funcs": 267, "lcom": 0, "lines": 5671, "loc": 4082}, "qlty_version": "qlty 0.639.0 linux-arm64 (d9801f1 2026-07-23)", "run": 16, "smells": 0, "timestamp": "2026-09-17T23:15:34Z"}
17+{"branch": "feature/copy-paste-functionality", "breaches": ["code smells: 2 (max 0)"], "commit": "7895c1d", "counts": {}, "gate": {"max_error": 0, "max_smells": 0, "max_warning": 0}, "gate_passed": false, "metrics": {"classes": 57, "complex": 546, "cyclo": 918, "fields": 79, "funcs": 271, "lcom": 0, "lines": 5832, "loc": 4212}, "qlty_version": "qlty 0.615.0 macos-arm64 (bfe0de3 2026-03-02)", "run": 17, "smells": 2, "timestamp": "2026-09-18T07:58:08Z"}
18+{"branch": "feature/copy-paste-functionality", "breaches": [], "commit": "7895c1d", "counts": {}, "gate": {"max_error": 0, "max_smells": 0, "max_warning": 0}, "gate_passed": true, "metrics": {"classes": 57, "complex": 532, "cyclo": 917, "fields": 79, "funcs": 273, "lcom": 0, "lines": 5858, "loc": 4220}, "qlty_version": "qlty 0.615.0 macos-arm64 (bfe0de3 2026-03-02)", "run": 18, "smells": 0, "timestamp": "2026-09-18T08:03:21Z"}
added .quality/report-20260918T075808Z.md +85 -0
new file mode 100644
@@ -0,0 +1,85 @@
1+# Quality report — 2026-09-18T07:58:08Z
2+
3+- **Gate**: ❌ **FAIL**
4+- **Commit**: `7895c1d` on `feature/copy-paste-functionality`
5+- **qlty**: qlty 0.615.0 macos-arm64 (bfe0de3 2026-03-02)
6+- **Run**: #17 (previous: 2026-09-17T23:15:34Z)
7+
8+## Gate violations
9+
10+- code smells: 2 (max 0)
11+
12+## Lint issues (`qlty check`)
13+
14+_none_
15+
16+### Top rules
17+
18+_none_
19+
20+### Most affected files
21+
22+_none_
23+
24+## Code smells (`qlty smells`)
25+
26+Total: **2** (vs previous: +2)
27+
28+| smell | file | line | detail |
29+|---|---|---|---|
30+| qlty:function-complexity | ui/src/components/ToolCallCard.tsx | 37 | Function with high complexity (count = 21): ToolCallCard |
31+| qlty:function-complexity | ui/src/components/ToolCallCard.tsx | 39 | Function with high complexity (count = 19): getToolContent |
32+
33+## Metrics (`qlty metrics`)
34+
35+| metric | total | vs previous |
36+|---|---|---|
37+| funcs | 271 | +4 |
38+| classes | 57 | +1 |
39+| fields | 79 | ±0 |
40+| cyclo | 918 | +25 |
41+| complex | 546 | +32 |
42+| lcom | 0 | ±0 |
43+| lines | 5832 | +161 |
44+| loc | 4212 | +130 |
45+
46+### Most complex files
47+
48+| file | complex | cyclo | loc |
49+|---|---|---|---|
50+| internal/bridge/bridge.go | 34 | 49 | 228 |
51+| internal/skills/skills.go | 34 | 53 | 129 |
52+| internal/terminal/terminal.go | 32 | 33 | 111 |
53+| ui/src/useCompletion.ts | 29 | 32 | 122 |
54+| ui/src/components/ToolCallCard.tsx | 24 | 32 | 124 |
55+| internal/files/files.go | 21 | 35 | 140 |
56+| internal/mockagent/mockagent.go | 21 | 38 | 190 |
57+| ui/src/components/DrawioView.tsx | 20 | 22 | 76 |
58+| ui/src/components/PromptInput.tsx | 20 | 26 | 153 |
59+| ui/src/components/EditorPane.tsx | 18 | 33 | 125 |
60+| internal/files/search.go | 17 | 32 | 104 |
61+| ui/src/components/PreviewPane.tsx | 17 | 30 | 131 |
62+| ui/src/reducer.ts | 17 | 54 | 195 |
63+| internal/agent/agent.go | 15 | 31 | 121 |
64+| ori-desktop/internal/settings/settings.go | 15 | 31 | 82 |
65+
66+## Tooling notes
67+
68+- `qlty check` exit 99: ERROR
69+
70+ > FATAL error occurred running 13 invocations
71+
72+## Trend
73+
74+| run | timestamp | error | warning | smells | complex | gate |
75+|---|---|---|---|---|---|---|
76+| 8 | 2026-09-17T20:06:46Z | 1 | 1 | 0 | 281 | FAIL |
77+| 9 | 2026-09-17T20:08:01Z | 0 | 0 | 0 | 281 | PASS |
78+| 10 | 2026-09-17T20:25:57Z | 0 | 0 | 0 | 286 | PASS |
79+| 11 | 2026-09-17T20:55:17Z | 0 | 0 | 0 | 288 | PASS |
80+| 12 | 2026-09-17T21:04:25Z | 0 | 0 | 0 | 288 | PASS |
81+| 13 | 2026-09-17T23:07:19Z | 0 | 0 | 2 | 517 | FAIL |
82+| 14 | 2026-09-17T23:07:54Z | 12 | 1 | 2 | 517 | FAIL |
83+| 15 | 2026-09-17T23:10:03Z | 0 | 0 | 0 | 507 | PASS |
84+| 16 | 2026-09-17T23:15:34Z | 0 | 0 | 0 | 514 | PASS |
85+| 17 | 2026-09-18T07:58:08Z | 0 | 0 | 2 | 546 | FAIL |
new file mode 100644
@@ -0,0 +1,85 @@
1+# Quality report — 2026-09-18T07:58:08Z
2+
3+- **Gate**: ❌ **FAIL**
4+- **Commit**: `7895c1d` on `feature/copy-paste-functionality`
5+- **qlty**: qlty 0.615.0 macos-arm64 (bfe0de3 2026-03-02)
6+- **Run**: #17 (previous: 2026-09-17T23:15:34Z)
7+
8+## Gate violations
9+
10+- code smells: 2 (max 0)
11+
12+## Lint issues (`qlty check`)
13+
14+_none_
15+
16+### Top rules
17+
18+_none_
19+
20+### Most affected files
21+
22+_none_
23+
24+## Code smells (`qlty smells`)
25+
26+Total: **2** (vs previous: +2)
27+
28+| smell | file | line | detail |
29+|---|---|---|---|
30+| qlty:function-complexity | ui/src/components/ToolCallCard.tsx | 37 | Function with high complexity (count = 21): ToolCallCard |
31+| qlty:function-complexity | ui/src/components/ToolCallCard.tsx | 39 | Function with high complexity (count = 19): getToolContent |
32+
33+## Metrics (`qlty metrics`)
34+
35+| metric | total | vs previous |
36+|---|---|---|
37+| funcs | 271 | +4 |
38+| classes | 57 | +1 |
39+| fields | 79 | ±0 |
40+| cyclo | 918 | +25 |
41+| complex | 546 | +32 |
42+| lcom | 0 | ±0 |
43+| lines | 5832 | +161 |
44+| loc | 4212 | +130 |
45+
46+### Most complex files
47+
48+| file | complex | cyclo | loc |
49+|---|---|---|---|
50+| internal/bridge/bridge.go | 34 | 49 | 228 |
51+| internal/skills/skills.go | 34 | 53 | 129 |
52+| internal/terminal/terminal.go | 32 | 33 | 111 |
53+| ui/src/useCompletion.ts | 29 | 32 | 122 |
54+| ui/src/components/ToolCallCard.tsx | 24 | 32 | 124 |
55+| internal/files/files.go | 21 | 35 | 140 |
56+| internal/mockagent/mockagent.go | 21 | 38 | 190 |
57+| ui/src/components/DrawioView.tsx | 20 | 22 | 76 |
58+| ui/src/components/PromptInput.tsx | 20 | 26 | 153 |
59+| ui/src/components/EditorPane.tsx | 18 | 33 | 125 |
60+| internal/files/search.go | 17 | 32 | 104 |
61+| ui/src/components/PreviewPane.tsx | 17 | 30 | 131 |
62+| ui/src/reducer.ts | 17 | 54 | 195 |
63+| internal/agent/agent.go | 15 | 31 | 121 |
64+| ori-desktop/internal/settings/settings.go | 15 | 31 | 82 |
65+
66+## Tooling notes
67+
68+- `qlty check` exit 99: ERROR
69+
70+ > FATAL error occurred running 13 invocations
71+
72+## Trend
73+
74+| run | timestamp | error | warning | smells | complex | gate |
75+|---|---|---|---|---|---|---|
76+| 8 | 2026-09-17T20:06:46Z | 1 | 1 | 0 | 281 | FAIL |
77+| 9 | 2026-09-17T20:08:01Z | 0 | 0 | 0 | 281 | PASS |
78+| 10 | 2026-09-17T20:25:57Z | 0 | 0 | 0 | 286 | PASS |
79+| 11 | 2026-09-17T20:55:17Z | 0 | 0 | 0 | 288 | PASS |
80+| 12 | 2026-09-17T21:04:25Z | 0 | 0 | 0 | 288 | PASS |
81+| 13 | 2026-09-17T23:07:19Z | 0 | 0 | 2 | 517 | FAIL |
82+| 14 | 2026-09-17T23:07:54Z | 12 | 1 | 2 | 517 | FAIL |
83+| 15 | 2026-09-17T23:10:03Z | 0 | 0 | 0 | 507 | PASS |
84+| 16 | 2026-09-17T23:15:34Z | 0 | 0 | 0 | 514 | PASS |
85+| 17 | 2026-09-18T07:58:08Z | 0 | 0 | 2 | 546 | FAIL |
added .quality/report-20260918T080321Z.md +72 -0
new file mode 100644
@@ -0,0 +1,72 @@
1+# Quality report — 2026-09-18T08:03:21Z
2+
3+- **Gate**: ✅ **PASS**
4+- **Commit**: `7895c1d` on `feature/copy-paste-functionality`
5+- **qlty**: qlty 0.615.0 macos-arm64 (bfe0de3 2026-03-02)
6+- **Run**: #18 (previous: 2026-09-18T07:58:08Z)
7+
8+## Lint issues (`qlty check`)
9+
10+_none_
11+
12+### Top rules
13+
14+_none_
15+
16+### Most affected files
17+
18+_none_
19+
20+## Code smells (`qlty smells`)
21+
22+Total: **0** (vs previous: -2)
23+
24+_none_
25+
26+## Metrics (`qlty metrics`)
27+
28+| metric | total | vs previous |
29+|---|---|---|
30+| funcs | 273 | +2 |
31+| classes | 57 | ±0 |
32+| fields | 79 | ±0 |
33+| cyclo | 917 | -1 |
34+| complex | 532 | -14 |
35+| lcom | 0 | ±0 |
36+| lines | 5858 | +26 |
37+| loc | 4220 | +8 |
38+
39+### Most complex files
40+
41+| file | complex | cyclo | loc |
42+|---|---|---|---|
43+| internal/bridge/bridge.go | 34 | 49 | 228 |
44+| internal/skills/skills.go | 34 | 53 | 129 |
45+| internal/terminal/terminal.go | 32 | 33 | 111 |
46+| ui/src/useCompletion.ts | 29 | 32 | 122 |
47+| internal/files/files.go | 21 | 35 | 140 |
48+| internal/mockagent/mockagent.go | 21 | 38 | 190 |
49+| ui/src/components/DrawioView.tsx | 20 | 22 | 76 |
50+| ui/src/components/PromptInput.tsx | 20 | 26 | 153 |
51+| ui/src/components/EditorPane.tsx | 18 | 33 | 125 |
52+| internal/files/search.go | 17 | 32 | 104 |
53+| ui/src/components/PreviewPane.tsx | 17 | 30 | 131 |
54+| ui/src/reducer.ts | 17 | 54 | 195 |
55+| internal/agent/agent.go | 15 | 31 | 121 |
56+| ori-desktop/internal/settings/settings.go | 15 | 31 | 82 |
57+| internal/bridge/ws.go | 14 | 15 | 61 |
58+
59+## Trend
60+
61+| run | timestamp | error | warning | smells | complex | gate |
62+|---|---|---|---|---|---|---|
63+| 9 | 2026-09-17T20:08:01Z | 0 | 0 | 0 | 281 | PASS |
64+| 10 | 2026-09-17T20:25:57Z | 0 | 0 | 0 | 286 | PASS |
65+| 11 | 2026-09-17T20:55:17Z | 0 | 0 | 0 | 288 | PASS |
66+| 12 | 2026-09-17T21:04:25Z | 0 | 0 | 0 | 288 | PASS |
67+| 13 | 2026-09-17T23:07:19Z | 0 | 0 | 2 | 517 | FAIL |
68+| 14 | 2026-09-17T23:07:54Z | 12 | 1 | 2 | 517 | FAIL |
69+| 15 | 2026-09-17T23:10:03Z | 0 | 0 | 0 | 507 | PASS |
70+| 16 | 2026-09-17T23:15:34Z | 0 | 0 | 0 | 514 | PASS |
71+| 17 | 2026-09-18T07:58:08Z | 0 | 0 | 2 | 546 | FAIL |
72+| 18 | 2026-09-18T08:03:21Z | 0 | 0 | 0 | 532 | PASS |
new file mode 100644
@@ -0,0 +1,72 @@
1+# Quality report — 2026-09-18T08:03:21Z
2+
3+- **Gate**: ✅ **PASS**
4+- **Commit**: `7895c1d` on `feature/copy-paste-functionality`
5+- **qlty**: qlty 0.615.0 macos-arm64 (bfe0de3 2026-03-02)
6+- **Run**: #18 (previous: 2026-09-18T07:58:08Z)
7+
8+## Lint issues (`qlty check`)
9+
10+_none_
11+
12+### Top rules
13+
14+_none_
15+
16+### Most affected files
17+
18+_none_
19+
20+## Code smells (`qlty smells`)
21+
22+Total: **0** (vs previous: -2)
23+
24+_none_
25+
26+## Metrics (`qlty metrics`)
27+
28+| metric | total | vs previous |
29+|---|---|---|
30+| funcs | 273 | +2 |
31+| classes | 57 | ±0 |
32+| fields | 79 | ±0 |
33+| cyclo | 917 | -1 |
34+| complex | 532 | -14 |
35+| lcom | 0 | ±0 |
36+| lines | 5858 | +26 |
37+| loc | 4220 | +8 |
38+
39+### Most complex files
40+
41+| file | complex | cyclo | loc |
42+|---|---|---|---|
43+| internal/bridge/bridge.go | 34 | 49 | 228 |
44+| internal/skills/skills.go | 34 | 53 | 129 |
45+| internal/terminal/terminal.go | 32 | 33 | 111 |
46+| ui/src/useCompletion.ts | 29 | 32 | 122 |
47+| internal/files/files.go | 21 | 35 | 140 |
48+| internal/mockagent/mockagent.go | 21 | 38 | 190 |
49+| ui/src/components/DrawioView.tsx | 20 | 22 | 76 |
50+| ui/src/components/PromptInput.tsx | 20 | 26 | 153 |
51+| ui/src/components/EditorPane.tsx | 18 | 33 | 125 |
52+| internal/files/search.go | 17 | 32 | 104 |
53+| ui/src/components/PreviewPane.tsx | 17 | 30 | 131 |
54+| ui/src/reducer.ts | 17 | 54 | 195 |
55+| internal/agent/agent.go | 15 | 31 | 121 |
56+| ori-desktop/internal/settings/settings.go | 15 | 31 | 82 |
57+| internal/bridge/ws.go | 14 | 15 | 61 |
58+
59+## Trend
60+
61+| run | timestamp | error | warning | smells | complex | gate |
62+|---|---|---|---|---|---|---|
63+| 9 | 2026-09-17T20:08:01Z | 0 | 0 | 0 | 281 | PASS |
64+| 10 | 2026-09-17T20:25:57Z | 0 | 0 | 0 | 286 | PASS |
65+| 11 | 2026-09-17T20:55:17Z | 0 | 0 | 0 | 288 | PASS |
66+| 12 | 2026-09-17T21:04:25Z | 0 | 0 | 0 | 288 | PASS |
67+| 13 | 2026-09-17T23:07:19Z | 0 | 0 | 2 | 517 | FAIL |
68+| 14 | 2026-09-17T23:07:54Z | 12 | 1 | 2 | 517 | FAIL |
69+| 15 | 2026-09-17T23:10:03Z | 0 | 0 | 0 | 507 | PASS |
70+| 16 | 2026-09-17T23:15:34Z | 0 | 0 | 0 | 514 | PASS |
71+| 17 | 2026-09-18T07:58:08Z | 0 | 0 | 2 | 546 | FAIL |
72+| 18 | 2026-09-18T08:03:21Z | 0 | 0 | 0 | 532 | PASS |
modified .quality/report-latest.md +13 -13
@@ -1,9 +1,9 @@
1-# Quality report — 2026-09-17T23:15:34Z
1+# Quality report — 2026-09-18T08:03:21Z
22
33 - **Gate**: ✅ **PASS**
4-- **Commit**: `2434cc7` on `feat/acp-webapp`
5-- **qlty**: qlty 0.639.0 linux-arm64 (d9801f1 2026-07-23)
6-- **Run**: #16 (previous: 2026-09-17T23:10:03Z)
4+- **Commit**: `7895c1d` on `feature/copy-paste-functionality`
5+- **qlty**: qlty 0.615.0 macos-arm64 (bfe0de3 2026-03-02)
6+- **Run**: #18 (previous: 2026-09-18T07:58:08Z)
77
88 ## Lint issues (`qlty check`)
99
@@ -19,7 +19,7 @@ _none_
1919
2020 ## Code smells (`qlty smells`)
2121
22-Total: **0** (vs previous: ±0)
22+Total: **0** (vs previous: -2)
2323
2424 _none_
2525
@@ -27,14 +27,14 @@ _none_
2727
2828 | metric | total | vs previous |
2929 |---|---|---|
30-| funcs | 267 | +7 |
31-| classes | 56 | +1 |
30+| funcs | 273 | +2 |
31+| classes | 57 | ±0 |
3232 | fields | 79 | ±0 |
33-| cyclo | 893 | +16 |
34-| complex | 514 | +7 |
33+| cyclo | 917 | -1 |
34+| complex | 532 | -14 |
3535 | lcom | 0 | ±0 |
36-| lines | 5671 | +101 |
37-| loc | 4082 | +52 |
36+| lines | 5858 | +26 |
37+| loc | 4220 | +8 |
3838
3939 ### Most complex files
4040
@@ -60,8 +60,6 @@ _none_
6060
6161 | run | timestamp | error | warning | smells | complex | gate |
6262 |---|---|---|---|---|---|---|
63-| 7 | 2026-09-17T20:05:32Z | 2 | 1 | 3 | 284 | FAIL |
64-| 8 | 2026-09-17T20:06:46Z | 1 | 1 | 0 | 281 | FAIL |
6563 | 9 | 2026-09-17T20:08:01Z | 0 | 0 | 0 | 281 | PASS |
6664 | 10 | 2026-09-17T20:25:57Z | 0 | 0 | 0 | 286 | PASS |
6765 | 11 | 2026-09-17T20:55:17Z | 0 | 0 | 0 | 288 | PASS |
@@ -70,3 +68,5 @@ _none_
7068 | 14 | 2026-09-17T23:07:54Z | 12 | 1 | 2 | 517 | FAIL |
7169 | 15 | 2026-09-17T23:10:03Z | 0 | 0 | 0 | 507 | PASS |
7270 | 16 | 2026-09-17T23:15:34Z | 0 | 0 | 0 | 514 | PASS |
71+| 17 | 2026-09-18T07:58:08Z | 0 | 0 | 2 | 546 | FAIL |
72+| 18 | 2026-09-18T08:03:21Z | 0 | 0 | 0 | 532 | PASS |
@@ -1,9 +1,9 @@
1-# Quality report — 2026-09-17T23:15:34Z1+# Quality report — 2026-09-18T08:03:21Z
2 2
3 - **Gate**: ✅ **PASS**3 - **Gate**: ✅ **PASS**
4-- **Commit**: `2434cc7` on `feat/acp-webapp`4+- **Commit**: `7895c1d` on `feature/copy-paste-functionality`
5-- **qlty**: qlty 0.639.0 linux-arm64 (d9801f1 2026-07-23)5+- **qlty**: qlty 0.615.0 macos-arm64 (bfe0de3 2026-03-02)
6-- **Run**: #16 (previous: 2026-09-17T23:10:03Z)6+- **Run**: #18 (previous: 2026-09-18T07:58:08Z)
7 7
8 ## Lint issues (`qlty check`)8 ## Lint issues (`qlty check`)
9 9
@@ -19,7 +19,7 @@ _none_
19 19
20 ## Code smells (`qlty smells`)20 ## Code smells (`qlty smells`)
21 21
22-Total: **0** (vs previous: ±0)22+Total: **0** (vs previous: -2)
23 23
24 _none_24 _none_
25 25
@@ -27,14 +27,14 @@ _none_
27 27
28 | metric | total | vs previous |28 | metric | total | vs previous |
29 |---|---|---|29 |---|---|---|
30-| funcs | 267 | +7 |30+| funcs | 273 | +2 |
31-| classes | 56 | +1 |31+| classes | 57 | ±0 |
32 | fields | 79 | ±0 |32 | fields | 79 | ±0 |
33-| cyclo | 893 | +16 |33+| cyclo | 917 | -1 |
34-| complex | 514 | +7 |34+| complex | 532 | -14 |
35 | lcom | 0 | ±0 |35 | lcom | 0 | ±0 |
36-| lines | 5671 | +101 |36+| lines | 5858 | +26 |
37-| loc | 4082 | +52 |37+| loc | 4220 | +8 |
38 38
39 ### Most complex files39 ### Most complex files
40 40
@@ -60,8 +60,6 @@ _none_
60 60
61 | run | timestamp | error | warning | smells | complex | gate |61 | run | timestamp | error | warning | smells | complex | gate |
62 |---|---|---|---|---|---|---|62 |---|---|---|---|---|---|---|
63-| 7 | 2026-09-17T20:05:32Z | 2 | 1 | 3 | 284 | FAIL |
64-| 8 | 2026-09-17T20:06:46Z | 1 | 1 | 0 | 281 | FAIL |
65 | 9 | 2026-09-17T20:08:01Z | 0 | 0 | 0 | 281 | PASS |63 | 9 | 2026-09-17T20:08:01Z | 0 | 0 | 0 | 281 | PASS |
66 | 10 | 2026-09-17T20:25:57Z | 0 | 0 | 0 | 286 | PASS |64 | 10 | 2026-09-17T20:25:57Z | 0 | 0 | 0 | 286 | PASS |
67 | 11 | 2026-09-17T20:55:17Z | 0 | 0 | 0 | 288 | PASS |65 | 11 | 2026-09-17T20:55:17Z | 0 | 0 | 0 | 288 | PASS |
@@ -70,3 +68,5 @@ _none_
70 | 14 | 2026-09-17T23:07:54Z | 12 | 1 | 2 | 517 | FAIL |68 | 14 | 2026-09-17T23:07:54Z | 12 | 1 | 2 | 517 | FAIL |
71 | 15 | 2026-09-17T23:10:03Z | 0 | 0 | 0 | 507 | PASS |69 | 15 | 2026-09-17T23:10:03Z | 0 | 0 | 0 | 507 | PASS |
72 | 16 | 2026-09-17T23:15:34Z | 0 | 0 | 0 | 514 | PASS |70 | 16 | 2026-09-17T23:15:34Z | 0 | 0 | 0 | 514 | PASS |
71+| 17 | 2026-09-18T07:58:08Z | 0 | 0 | 2 | 546 | FAIL |
72+| 18 | 2026-09-18T08:03:21Z | 0 | 0 | 0 | 532 | PASS |
modified README.md +12 -4
@@ -8,11 +8,19 @@ Ori is a web client for [ACP (Agent Client Protocol)](https://agentclientprotoco
88 From the directory where the kit lives, pointing at the project you want the agent to work on:
99
1010 ```bash
11-sbx run -d claude ~/path/to/your/project \
12- --template k33g/ori:0.0.0 \
11+sbx run -d claude . \
12+ --template k33g/ori:0.0.2 \
1313 --kit ./kits/ori \
14- --name ori \
15- -p 8888:8888
14+ --name hello-ori \
15+ -p 3000:8888/tcp
16+
17+
18+
19+sbx run -d claude . \
20+ --template k33g/ori:0.0.2 \
21+ --kit https://rickub.com/bots-garden/kits/ori \
22+ --name hello-ori \
23+ -p 3000:8888/tcp
1624 ```
1725
1826 Then open **http://localhost:8888** — the panel connects to a Claude Code session already running in the sandbox, no login needed.
@@ -8,11 +8,19 @@ Ori is a web client for [ACP (Agent Client Protocol)](https://agentclientprotoco
8 From the directory where the kit lives, pointing at the project you want the agent to work on:8 From the directory where the kit lives, pointing at the project you want the agent to work on:
9 9
10 ```bash10 ```bash
11-sbx run -d claude ~/path/to/your/project \11+sbx run -d claude . \
12- --template k33g/ori:0.0.0 \12+ --template k33g/ori:0.0.2 \
13 --kit ./kits/ori \13 --kit ./kits/ori \
14- --name ori \14+ --name hello-ori \
15- -p 8888:888815+ -p 3000:8888/tcp
16+
17+
18+
19+sbx run -d claude . \
20+ --template k33g/ori:0.0.2 \
21+ --kit https://rickub.com/bots-garden/kits/ori \
22+ --name hello-ori \
23+ -p 3000:8888/tcp
16 ```24 ```
17 25
18 Then open **http://localhost:8888** — the panel connects to a Claude Code session already running in the sandbox, no login needed.26 Then open **http://localhost:8888** — the panel connects to a Claude Code session already running in the sandbox, no login needed.
modified cmd/ori/main.go +1 -1
@@ -3,7 +3,7 @@
33 //
44 // Usage:
55 //
6-// ori [--addr :8888] [--cwd DIR] [--agent-cmd "npx -y @zed-industries/claude-code-acp"]
6+// ori [--addr :8888] [--cwd DIR] [--agent-cmd "npx -y @agentclientprotocol/claude-agent-acp"]
77 package main
88
99 import (
@@ -3,7 +3,7 @@
3 //3 //
4 // Usage:4 // Usage:
5 //5 //
6-// ori [--addr :8888] [--cwd DIR] [--agent-cmd "npx -y @zed-industries/claude-code-acp"]6+// ori [--addr :8888] [--cwd DIR] [--agent-cmd "npx -y @agentclientprotocol/claude-agent-acp"]
7 package main7 package main
8 8
9 import (9 import (
modified docs/diagrams/packages.drawio +1 -1
@@ -37,7 +37,7 @@
3737 <mxCell id="wslib" value="github.com/coder/websocket&#10;(WebSocket implementation)" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#ffe6cc;strokeColor=#d79b00;" parent="1" vertex="1">
3838 <mxGeometry x="450" y="460" width="230" height="60" as="geometry"/>
3939 </mxCell>
40- <mxCell id="adapter" value="npx @zed-industries/claude-code-acp&#10;(Claude Code ACP adapter — runtime subprocess)" style="rounded=1;whiteSpace=wrap;html=1;dashed=1;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1">
40+ <mxCell id="adapter" value="npx @agentclientprotocol/claude-agent-acp&#10;(Claude Agent SDK ACP adapter, bundles Claude Code — runtime subprocess)" style="rounded=1;whiteSpace=wrap;html=1;dashed=1;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1">
4141 <mxGeometry x="890" y="580" width="270" height="60" as="geometry"/>
4242 </mxCell>
4343 <mxCell id="filespkg" value="internal/files&#10;(workspace file API: list, read, write)" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="1">
@@ -37,7 +37,7 @@
37 <mxCell id="wslib" value="github.com/coder/websocket&#10;(WebSocket implementation)" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#ffe6cc;strokeColor=#d79b00;" parent="1" vertex="1">37 <mxCell id="wslib" value="github.com/coder/websocket&#10;(WebSocket implementation)" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#ffe6cc;strokeColor=#d79b00;" parent="1" vertex="1">
38 <mxGeometry x="450" y="460" width="230" height="60" as="geometry"/>38 <mxGeometry x="450" y="460" width="230" height="60" as="geometry"/>
39 </mxCell>39 </mxCell>
40- <mxCell id="adapter" value="npx @zed-industries/claude-code-acp&#10;(Claude Code ACP adapter — runtime subprocess)" style="rounded=1;whiteSpace=wrap;html=1;dashed=1;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1">40+ <mxCell id="adapter" value="npx @agentclientprotocol/claude-agent-acp&#10;(Claude Agent SDK ACP adapter, bundles Claude Code — runtime subprocess)" style="rounded=1;whiteSpace=wrap;html=1;dashed=1;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1">
41 <mxGeometry x="890" y="580" width="270" height="60" as="geometry"/>41 <mxGeometry x="890" y="580" width="270" height="60" as="geometry"/>
42 </mxCell>42 </mxCell>
43 <mxCell id="filespkg" value="internal/files&#10;(workspace file API: list, read, write)" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="1">43 <mxCell id="filespkg" value="internal/files&#10;(workspace file API: list, read, write)" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="1">
modified docs/en/explanation/architecture.md +3 -1
@@ -45,7 +45,9 @@ The bridge could have translated ACP updates into its own message shapes. It del
4545
4646 ## Why an adapter process for Claude Code
4747
48-The Claude Code CLI has no native ACP mode (verified against v2.1.274: no such option or subcommand). Zed's own integration goes through [`@zed-industries/claude-code-acp`](https://www.npmjs.com/package/@zed-industries/claude-code-acp), which wraps the Claude Code SDK and exposes ACP on stdio. Ori spawns exactly that adapter by default.
48+The Claude Code CLI has no native ACP mode (verified against v2.1.274: no such option or subcommand). Ori therefore spawns [`@agentclientprotocol/claude-agent-acp`](https://www.npmjs.com/package/@agentclientprotocol/claude-agent-acp), the Agent Client Protocol project's adapter for the Claude Agent SDK, which exposes ACP on stdio. The adapter brings its own Claude Code CLI through the SDK's per-platform native package (2.1.274 with SDK 0.3.274), so the version that answers does not depend on what is installed on the machine.
49+
50+Ori used Zed's [`@zed-industries/claude-code-acp`](https://www.npmjs.com/package/@zed-industries/claude-code-acp) until 2026-09-18. That adapter pins an older SDK whose embedded CLI (2.1.44) the API now rejects for current models ("Claude Code 2.1.44 does not support this model; version 2.1.251 or newer is required"), which broke every prompt. Both adapters speak the same ACP, so the switch is a default-command change: `--agent-cmd` still accepts either.
4951
5052 ## Rejected alternatives
5153
@@ -45,7 +45,9 @@ The bridge could have translated ACP updates into its own message shapes. It del
45 45
46 ## Why an adapter process for Claude Code46 ## Why an adapter process for Claude Code
47 47
48-The Claude Code CLI has no native ACP mode (verified against v2.1.274: no such option or subcommand). Zed's own integration goes through [`@zed-industries/claude-code-acp`](https://www.npmjs.com/package/@zed-industries/claude-code-acp), which wraps the Claude Code SDK and exposes ACP on stdio. Ori spawns exactly that adapter by default.48+The Claude Code CLI has no native ACP mode (verified against v2.1.274: no such option or subcommand). Ori therefore spawns [`@agentclientprotocol/claude-agent-acp`](https://www.npmjs.com/package/@agentclientprotocol/claude-agent-acp), the Agent Client Protocol project's adapter for the Claude Agent SDK, which exposes ACP on stdio. The adapter brings its own Claude Code CLI through the SDK's per-platform native package (2.1.274 with SDK 0.3.274), so the version that answers does not depend on what is installed on the machine.
49+
50+Ori used Zed's [`@zed-industries/claude-code-acp`](https://www.npmjs.com/package/@zed-industries/claude-code-acp) until 2026-09-18. That adapter pins an older SDK whose embedded CLI (2.1.44) the API now rejects for current models ("Claude Code 2.1.44 does not support this model; version 2.1.251 or newer is required"), which broke every prompt. Both adapters speak the same ACP, so the switch is a default-command change: `--agent-cmd` still accepts either.
49 51
50 ## Rejected alternatives52 ## Rejected alternatives
51 53
modified docs/en/how-to/run-in-a-sandbox.md +8 -6
@@ -4,33 +4,35 @@ This guide shows how to package Ori as a Docker Sandboxes template and run it wi
44
55 ## Steps
66
7-1. Get the template image — it is published on Docker Hub as `k33g/ori:0.0.0`. To (re)build and push it yourself, from the repository root:
7+1. Get the template image — it is published on Docker Hub as `k33g/ori:0.0.2`. To (re)build and push it yourself, from the repository root:
88
99 ```bash
1010 ./template/build.sh
1111 ```
1212
13- The script runs a multi-arch `docker buildx build --push`: the SPA and the Go binaries are built in intermediate stages, then layered with the `claude-code-acp` adapter on top of the official `docker/sandbox-templates:claude-code` image. For a local-only image use `make template` instead.
13+ The script runs a multi-arch `docker buildx build --push`: the SPA and the Go binaries are built in intermediate stages, then layered with the `claude-agent-acp` adapter on top of the official `docker/sandbox-templates:claude-code` image. For a local-only image use `make template` instead.
1414
1515 2. Create the sandbox from your project directory, with the template and the ori kit, in detached mode:
1616
1717 ```bash
1818 sbx run -d claude ~/path/to/your/project \
19- --template k33g/ori:0.0.0 \
19+ --template k33g/ori:0.0.2 \
2020 --kit /path/to/ori/kits/ori
2121 ```
2222
2323 `-d` (`--detached`) matters: sbx stops a sandbox 30 seconds after the last CLI session on it closes, and browser traffic on a published port is not a session. `sbx create` holds a session only while it runs, so a sandbox created with it goes `stopped` half a minute later. A detached sandbox is exempt from this auto-stop until you `sbx stop` or `sbx rm` it. The flag exists only on `sbx run`, and the mode is fixed at creation.
2424
25+ To pin a host port, add `-p 5555:8888/tcp`. Keep the `/tcp` suffix: without a protocol sbx binds IPv4 only (`tcp4`), and a browser that resolves `localhost` to `::1` gets "site can't be reached" while the server is up. Avoid ports Chrome refuses outright, such as 6665–6669.
26+
2527 The kit's startup command launches the ori server on port 8888 at every container start; the claude agent kit injects the Anthropic credentials through the sandbox proxy, so nothing needs a login inside.
2628
27-3. Open the UI: the kit declares port 8888, so the command output prints the ephemeral address it published — `Published web: localhost:<port> -> 8888/tcp`. Browse there, or pin a fixed port with `-p 8888:8888` at creation (or later with `sbx ports <sandbox-name> --publish 8888:8888/tcp`).
29+3. Open the UI: the kit declares port 8888, so the command output prints the ephemeral address it published — `Published web: localhost:<port> -> 8888/tcp`. Browse there, or pin a fixed port with `-p 8888:8888/tcp` at creation (or later with `sbx ports <sandbox-name> --publish 8888:8888/tcp`).
2830
2931 ## Variants
3032
31-- The listen port is a kit argument (default 8888): add `--kit-arg ori.port=9000` to change it (and publish that port instead).
33+- The listen port is a kit argument (default 8888): add `--kit-arg ori.port=9000` to change it, and publish that port yourself with `-p 9000:9000` — the port the kit declares for auto-publishing is fixed at 8888.
3234 - If the image was never pushed to Docker Hub, hand it to the sandbox runtime's own image store first: `docker save k33g/ori -o /tmp/ori.tar && sbx template load /tmp/ori.tar`.
33-- Standalone, without sbx: `docker run --rm -p 8888:8888 k33g/ori` (provide `ANTHROPIC_API_KEY`, or use the demo agent below).
35+- Standalone, without sbx: `docker run --rm -p 8888:8888/tcp k33g/ori` (provide `ANTHROPIC_API_KEY`, or use the demo agent below).
3436 - Credential-free demo: inside the sandbox, `pkill -x ori` then relaunch with `--agent-cmd ori-mock-agent`.
3537 - The server log inside the sandbox is `/home/agent/.ori.log`.
3638 - A stopped sandbox restarts with `sbx run -d --name <sandbox-name>` (or `sbx attach`); the kit relaunches the server on every start.
@@ -4,33 +4,35 @@ This guide shows how to package Ori as a Docker Sandboxes template and run it wi
4 4
5 ## Steps5 ## Steps
6 6
7-1. Get the template image — it is published on Docker Hub as `k33g/ori:0.0.0`. To (re)build and push it yourself, from the repository root:7+1. Get the template image — it is published on Docker Hub as `k33g/ori:0.0.2`. To (re)build and push it yourself, from the repository root:
8 8
9 ```bash9 ```bash
10 ./template/build.sh10 ./template/build.sh
11 ```11 ```
12 12
13- The script runs a multi-arch `docker buildx build --push`: the SPA and the Go binaries are built in intermediate stages, then layered with the `claude-code-acp` adapter on top of the official `docker/sandbox-templates:claude-code` image. For a local-only image use `make template` instead.13+ The script runs a multi-arch `docker buildx build --push`: the SPA and the Go binaries are built in intermediate stages, then layered with the `claude-agent-acp` adapter on top of the official `docker/sandbox-templates:claude-code` image. For a local-only image use `make template` instead.
14 14
15 2. Create the sandbox from your project directory, with the template and the ori kit, in detached mode:15 2. Create the sandbox from your project directory, with the template and the ori kit, in detached mode:
16 16
17 ```bash17 ```bash
18 sbx run -d claude ~/path/to/your/project \18 sbx run -d claude ~/path/to/your/project \
19- --template k33g/ori:0.0.0 \19+ --template k33g/ori:0.0.2 \
20 --kit /path/to/ori/kits/ori20 --kit /path/to/ori/kits/ori
21 ```21 ```
22 22
23 `-d` (`--detached`) matters: sbx stops a sandbox 30 seconds after the last CLI session on it closes, and browser traffic on a published port is not a session. `sbx create` holds a session only while it runs, so a sandbox created with it goes `stopped` half a minute later. A detached sandbox is exempt from this auto-stop until you `sbx stop` or `sbx rm` it. The flag exists only on `sbx run`, and the mode is fixed at creation.23 `-d` (`--detached`) matters: sbx stops a sandbox 30 seconds after the last CLI session on it closes, and browser traffic on a published port is not a session. `sbx create` holds a session only while it runs, so a sandbox created with it goes `stopped` half a minute later. A detached sandbox is exempt from this auto-stop until you `sbx stop` or `sbx rm` it. The flag exists only on `sbx run`, and the mode is fixed at creation.
24 24
25+ To pin a host port, add `-p 5555:8888/tcp`. Keep the `/tcp` suffix: without a protocol sbx binds IPv4 only (`tcp4`), and a browser that resolves `localhost` to `::1` gets "site can't be reached" while the server is up. Avoid ports Chrome refuses outright, such as 6665–6669.
26+
25 The kit's startup command launches the ori server on port 8888 at every container start; the claude agent kit injects the Anthropic credentials through the sandbox proxy, so nothing needs a login inside.27 The kit's startup command launches the ori server on port 8888 at every container start; the claude agent kit injects the Anthropic credentials through the sandbox proxy, so nothing needs a login inside.
26 28
27-3. Open the UI: the kit declares port 8888, so the command output prints the ephemeral address it published — `Published web: localhost:<port> -> 8888/tcp`. Browse there, or pin a fixed port with `-p 8888:8888` at creation (or later with `sbx ports <sandbox-name> --publish 8888:8888/tcp`).29+3. Open the UI: the kit declares port 8888, so the command output prints the ephemeral address it published — `Published web: localhost:<port> -> 8888/tcp`. Browse there, or pin a fixed port with `-p 8888:8888/tcp` at creation (or later with `sbx ports <sandbox-name> --publish 8888:8888/tcp`).
28 30
29 ## Variants31 ## Variants
30 32
31-- The listen port is a kit argument (default 8888): add `--kit-arg ori.port=9000` to change it (and publish that port instead).33+- The listen port is a kit argument (default 8888): add `--kit-arg ori.port=9000` to change it, and publish that port yourself with `-p 9000:9000` — the port the kit declares for auto-publishing is fixed at 8888.
32 - If the image was never pushed to Docker Hub, hand it to the sandbox runtime's own image store first: `docker save k33g/ori -o /tmp/ori.tar && sbx template load /tmp/ori.tar`.34 - If the image was never pushed to Docker Hub, hand it to the sandbox runtime's own image store first: `docker save k33g/ori -o /tmp/ori.tar && sbx template load /tmp/ori.tar`.
33-- Standalone, without sbx: `docker run --rm -p 8888:8888 k33g/ori` (provide `ANTHROPIC_API_KEY`, or use the demo agent below).35+- Standalone, without sbx: `docker run --rm -p 8888:8888/tcp k33g/ori` (provide `ANTHROPIC_API_KEY`, or use the demo agent below).
34 - Credential-free demo: inside the sandbox, `pkill -x ori` then relaunch with `--agent-cmd ori-mock-agent`.36 - Credential-free demo: inside the sandbox, `pkill -x ori` then relaunch with `--agent-cmd ori-mock-agent`.
35 - The server log inside the sandbox is `/home/agent/.ori.log`.37 - The server log inside the sandbox is `/home/agent/.ori.log`.
36 - A stopped sandbox restarts with `sbx run -d --name <sandbox-name>` (or `sbx attach`); the kit relaunches the server on every start.38 - A stopped sandbox restarts with `sbx run -d --name <sandbox-name>` (or `sbx attach`); the kit relaunches the server on every start.
modified docs/en/how-to/run-the-tests.md +2 -0
@@ -12,6 +12,8 @@ This guide shows how to run Ori's test suites. It assumes the frontend dependenc
1212
1313 This runs the Go suite (`go test ./...`) followed by the frontend suite (`vitest run`). Both must end green.
1414
15+ The frontend script sets `NODE_OPTIONS=--no-experimental-webstorage`: Node 25 ships a built-in `localStorage` without `clear()` that shadows jsdom's and fails every store test. The flag is known to Node 22 and later, so it is harmless on older runtimes.
16+
1517 ## Variants
1618
1719 - Backend only: `make test-go` — unit tests for every package plus an end-to-end test that compiles the mock agent, spawns it as a real subprocess and drives a real WebSocket client through a full turn.
@@ -12,6 +12,8 @@ This guide shows how to run Ori's test suites. It assumes the frontend dependenc
12 12
13 This runs the Go suite (`go test ./...`) followed by the frontend suite (`vitest run`). Both must end green.13 This runs the Go suite (`go test ./...`) followed by the frontend suite (`vitest run`). Both must end green.
14 14
15+ The frontend script sets `NODE_OPTIONS=--no-experimental-webstorage`: Node 25 ships a built-in `localStorage` without `clear()` that shadows jsdom's and fails every store test. The flag is known to Node 22 and later, so it is harmless on older runtimes.
16+
15 ## Variants17 ## Variants
16 18
17 - Backend only: `make test-go` — unit tests for every package plus an end-to-end test that compiles the mock agent, spawns it as a real subprocess and drives a real WebSocket client through a full turn.19 - Backend only: `make test-go` — unit tests for every package plus an end-to-end test that compiles the mock agent, spawns it as a real subprocess and drives a real WebSocket client through a full turn.
modified docs/en/how-to/run-with-claude-code.md +2 -2
@@ -4,7 +4,7 @@ This guide shows how to start Ori connected to Claude Code. It assumes you have
44
55 ## Steps
66
7-1. Make sure Node.js and npm are available: the backend spawns Claude Code's official ACP adapter with `npx -y @zed-industries/claude-code-acp`.
7+1. Make sure Node.js and npm are available: the backend spawns the Claude Agent SDK's ACP adapter with `npx -y @agentclientprotocol/claude-agent-acp`. The adapter ships its own Claude Code CLI (a native package chosen for your platform), so it works even if the `claude` command on your machine is older.
88 2. Make sure Claude Code is authenticated on this machine (`claude` login flow, or an `ANTHROPIC_API_KEY` environment variable the adapter can use).
99 3. From the repository root, start the server:
1010
@@ -18,7 +18,7 @@ This guide shows how to start Ori connected to Claude Code. It assumes you have
1818
1919 - To work on a specific project directory, pass the agent's working directory explicitly: `./bin/ori --cwd /path/to/your/project`.
2020 - To serve on another port: `./bin/ori --addr :9000`.
21-- The first `npx` run downloads the adapter package; pin it by installing it globally (`npm install -g @zed-industries/claude-code-acp`) and passing `--agent-cmd "claude-code-acp"`.
21+- The first `npx` run downloads the adapter package; pin it by installing it globally (`npm install -g @agentclientprotocol/claude-agent-acp`) and passing `--agent-cmd "claude-agent-acp"`.
2222
2323 ## See also
2424
@@ -4,7 +4,7 @@ This guide shows how to start Ori connected to Claude Code. It assumes you have
4 4
5 ## Steps5 ## Steps
6 6
7-1. Make sure Node.js and npm are available: the backend spawns Claude Code's official ACP adapter with `npx -y @zed-industries/claude-code-acp`.7+1. Make sure Node.js and npm are available: the backend spawns the Claude Agent SDK's ACP adapter with `npx -y @agentclientprotocol/claude-agent-acp`. The adapter ships its own Claude Code CLI (a native package chosen for your platform), so it works even if the `claude` command on your machine is older.
8 2. Make sure Claude Code is authenticated on this machine (`claude` login flow, or an `ANTHROPIC_API_KEY` environment variable the adapter can use).8 2. Make sure Claude Code is authenticated on this machine (`claude` login flow, or an `ANTHROPIC_API_KEY` environment variable the adapter can use).
9 3. From the repository root, start the server:9 3. From the repository root, start the server:
10 10
@@ -18,7 +18,7 @@ This guide shows how to start Ori connected to Claude Code. It assumes you have
18 18
19 - To work on a specific project directory, pass the agent's working directory explicitly: `./bin/ori --cwd /path/to/your/project`.19 - To work on a specific project directory, pass the agent's working directory explicitly: `./bin/ori --cwd /path/to/your/project`.
20 - To serve on another port: `./bin/ori --addr :9000`.20 - To serve on another port: `./bin/ori --addr :9000`.
21-- The first `npx` run downloads the adapter package; pin it by installing it globally (`npm install -g @zed-industries/claude-code-acp`) and passing `--agent-cmd "claude-code-acp"`.21+- The first `npx` run downloads the adapter package; pin it by installing it globally (`npm install -g @agentclientprotocol/claude-agent-acp`) and passing `--agent-cmd "claude-agent-acp"`.
22 22
23 ## See also23 ## See also
24 24
modified docs/en/reference/cli.md +1 -1
@@ -16,7 +16,7 @@ At startup, `ori` spawns the agent command, performs the ACP handshake (`initial
1616 | --- | --- | --- | --- |
1717 | `--addr` | string | `:8888` | TCP address the HTTP server listens on. The empty host binds all interfaces. |
1818 | `--cwd` | string | current directory | Working directory handed to the agent session. Must exist and be a directory; resolved to an absolute path. |
19-| `--agent-cmd` | string | `npx -y @zed-industries/claude-code-acp` | Command spawning the ACP agent, split on whitespace (first field: executable; the rest: arguments). |
19+| `--agent-cmd` | string | `npx -y @agentclientprotocol/claude-agent-acp` | Command spawning the ACP agent, split on whitespace (first field: executable; the rest: arguments). |
2020
2121 ## HTTP endpoints
2222
@@ -16,7 +16,7 @@ At startup, `ori` spawns the agent command, performs the ACP handshake (`initial
16 | --- | --- | --- | --- |16 | --- | --- | --- | --- |
17 | `--addr` | string | `:8888` | TCP address the HTTP server listens on. The empty host binds all interfaces. |17 | `--addr` | string | `:8888` | TCP address the HTTP server listens on. The empty host binds all interfaces. |
18 | `--cwd` | string | current directory | Working directory handed to the agent session. Must exist and be a directory; resolved to an absolute path. |18 | `--cwd` | string | current directory | Working directory handed to the agent session. Must exist and be a directory; resolved to an absolute path. |
19-| `--agent-cmd` | string | `npx -y @zed-industries/claude-code-acp` | Command spawning the ACP agent, split on whitespace (first field: executable; the rest: arguments). |19+| `--agent-cmd` | string | `npx -y @agentclientprotocol/claude-agent-acp` | Command spawning the ACP agent, split on whitespace (first field: executable; the rest: arguments). |
20 20
21 ## HTTP endpoints21 ## HTTP endpoints
22 22
modified docs/fr/explanation/architecture.md +3 -1
@@ -45,7 +45,9 @@ Le bridge aurait pu traduire les updates ACP dans ses propres formes de messages
4545
4646 ## Pourquoi un processus adaptateur pour Claude Code
4747
48-La CLI Claude Code n'a pas de mode ACP natif (vérifié sur la v2.1.274 : aucune option ni sous-commande de ce type). L'intégration de Zed lui-même passe par [`@zed-industries/claude-code-acp`](https://www.npmjs.com/package/@zed-industries/claude-code-acp), qui enveloppe le SDK Claude Code et expose ACP sur stdio. Ori lance exactement cet adaptateur par défaut.
48+La CLI Claude Code n'a pas de mode ACP natif (vérifié sur la v2.1.274 : aucune option ni sous-commande de ce type). Ori lance donc [`@agentclientprotocol/claude-agent-acp`](https://www.npmjs.com/package/@agentclientprotocol/claude-agent-acp), l'adaptateur du projet Agent Client Protocol pour le Claude Agent SDK, qui expose ACP sur stdio. L'adaptateur apporte sa propre CLI Claude Code via le paquet natif par plateforme du SDK (2.1.274 avec le SDK 0.3.274) : la version qui répond ne dépend pas de ce qui est installé sur la machine.
49+
50+Jusqu'au 18 septembre 2026, Ori utilisait [`@zed-industries/claude-code-acp`](https://www.npmjs.com/package/@zed-industries/claude-code-acp) de Zed. Cet adaptateur fige un SDK plus ancien dont la CLI embarquée (2.1.44) est désormais refusée par l'API pour les modèles actuels (« Claude Code 2.1.44 does not support this model; version 2.1.251 or newer is required »), ce qui cassait chaque prompt. Les deux adaptateurs parlent le même ACP : le changement est une simple valeur par défaut, `--agent-cmd` accepte toujours l'un ou l'autre.
4951
5052 ## Alternatives écartées
5153
@@ -45,7 +45,9 @@ Le bridge aurait pu traduire les updates ACP dans ses propres formes de messages
45 45
46 ## Pourquoi un processus adaptateur pour Claude Code46 ## Pourquoi un processus adaptateur pour Claude Code
47 47
48-La CLI Claude Code n'a pas de mode ACP natif (vérifié sur la v2.1.274 : aucune option ni sous-commande de ce type). L'intégration de Zed lui-même passe par [`@zed-industries/claude-code-acp`](https://www.npmjs.com/package/@zed-industries/claude-code-acp), qui enveloppe le SDK Claude Code et expose ACP sur stdio. Ori lance exactement cet adaptateur par défaut.48+La CLI Claude Code n'a pas de mode ACP natif (vérifié sur la v2.1.274 : aucune option ni sous-commande de ce type). Ori lance donc [`@agentclientprotocol/claude-agent-acp`](https://www.npmjs.com/package/@agentclientprotocol/claude-agent-acp), l'adaptateur du projet Agent Client Protocol pour le Claude Agent SDK, qui expose ACP sur stdio. L'adaptateur apporte sa propre CLI Claude Code via le paquet natif par plateforme du SDK (2.1.274 avec le SDK 0.3.274) : la version qui répond ne dépend pas de ce qui est installé sur la machine.
49+
50+Jusqu'au 18 septembre 2026, Ori utilisait [`@zed-industries/claude-code-acp`](https://www.npmjs.com/package/@zed-industries/claude-code-acp) de Zed. Cet adaptateur fige un SDK plus ancien dont la CLI embarquée (2.1.44) est désormais refusée par l'API pour les modèles actuels (« Claude Code 2.1.44 does not support this model; version 2.1.251 or newer is required »), ce qui cassait chaque prompt. Les deux adaptateurs parlent le même ACP : le changement est une simple valeur par défaut, `--agent-cmd` accepte toujours l'un ou l'autre.
49 51
50 ## Alternatives écartées52 ## Alternatives écartées
51 53
modified docs/fr/how-to/run-in-a-sandbox.md +8 -6
@@ -4,33 +4,35 @@ Ce guide montre comment empaqueter Ori en template Docker Sandboxes et le lancer
44
55 ## Étapes
66
7-1. Récupérez l'image du template — elle est publiée sur Docker Hub sous `k33g/ori:0.0.0`. Pour la (re)construire et la pousser vous-même, depuis la racine du dépôt :
7+1. Récupérez l'image du template — elle est publiée sur Docker Hub sous `k33g/ori:0.0.2`. Pour la (re)construire et la pousser vous-même, depuis la racine du dépôt :
88
99 ```bash
1010 ./template/build.sh
1111 ```
1212
13- Le script exécute un `docker buildx build --push` multi-architectures : la SPA et les binaires Go sont compilés dans des étapes intermédiaires, puis posés avec l'adaptateur `claude-code-acp` au-dessus de l'image officielle `docker/sandbox-templates:claude-code`. Pour une image locale uniquement, utilisez plutôt `make template`.
13+ Le script exécute un `docker buildx build --push` multi-architectures : la SPA et les binaires Go sont compilés dans des étapes intermédiaires, puis posés avec l'adaptateur `claude-agent-acp` au-dessus de l'image officielle `docker/sandbox-templates:claude-code`. Pour une image locale uniquement, utilisez plutôt `make template`.
1414
1515 2. Créez la sandbox depuis votre répertoire de projet, avec le template et le kit ori, en mode détaché :
1616
1717 ```bash
1818 sbx run -d claude ~/chemin/vers/votre/projet \
19- --template k33g/ori:0.0.0 \
19+ --template k33g/ori:0.0.2 \
2020 --kit /chemin/vers/ori/kits/ori
2121 ```
2222
2323 Le `-d` (`--detached`) est important : sbx arrête une sandbox 30 secondes après la fermeture de la dernière session CLI ouverte dessus, et le trafic du navigateur sur un port publié n'est pas une session. `sbx create` ne tient une session que le temps de son exécution, donc une sandbox créée ainsi passe en `stopped` une demi-minute plus tard. Une sandbox détachée échappe à cet arrêt automatique jusqu'à ce que vous fassiez `sbx stop` ou `sbx rm`. L'option n'existe que sur `sbx run`, et le mode est figé à la création.
2424
25+ Pour fixer un port hôte, ajoutez `-p 5555:8888/tcp`. Gardez le suffixe `/tcp` : sans protocole, sbx ne lie que l'IPv4 (`tcp4`), et un navigateur qui résout `localhost` en `::1` affiche « site inaccessible » alors que le serveur tourne. Évitez les ports que Chrome refuse d'office, comme 6665 à 6669.
26+
2527 La commande de démarrage du kit lance le serveur ori sur le port 8888 à chaque démarrage du conteneur ; le kit agent claude injecte les identifiants Anthropic via le proxy de la sandbox, donc aucune connexion n'est nécessaire à l'intérieur.
2628
27-3. Ouvrez l'interface : le kit déclare le port 8888, la sortie de la commande affiche donc l'adresse éphémère publiée — `Published web: localhost:<port> -> 8888/tcp`. Ouvrez-la, ou fixez un port stable avec `-p 8888:8888` à la création (ou après coup avec `sbx ports <nom-de-la-sandbox> --publish 8888:8888/tcp`).
29+3. Ouvrez l'interface : le kit déclare le port 8888, la sortie de la commande affiche donc l'adresse éphémère publiée — `Published web: localhost:<port> -> 8888/tcp`. Ouvrez-la, ou fixez un port stable avec `-p 8888:8888/tcp` à la création (ou après coup avec `sbx ports <nom-de-la-sandbox> --publish 8888:8888/tcp`).
2830
2931 ## Variantes
3032
31-- Le port d'écoute est un argument du kit (défaut 8888) : ajoutez `--kit-arg ori.port=9000` pour le changer (et publiez ce port-là à la place).
33+- Le port d'écoute est un argument du kit (défaut 8888) : ajoutez `--kit-arg ori.port=9000` pour le changer, et publiez ce port vous-même avec `-p 9000:9000` — le port que le kit déclare pour l'auto-publication est fixé à 8888.
3234 - Si l'image n'a jamais été poussée sur Docker Hub, confiez-la d'abord au magasin d'images du runtime des sandboxes : `docker save k33g/ori -o /tmp/ori.tar && sbx template load /tmp/ori.tar`.
33-- Autonome, sans sbx : `docker run --rm -p 8888:8888 k33g/ori` (fournissez `ANTHROPIC_API_KEY`, ou utilisez l'agent de démonstration ci-dessous).
35+- Autonome, sans sbx : `docker run --rm -p 8888:8888/tcp k33g/ori` (fournissez `ANTHROPIC_API_KEY`, ou utilisez l'agent de démonstration ci-dessous).
3436 - Démo sans identifiants : dans la sandbox, `pkill -x ori` puis relancez avec `--agent-cmd ori-mock-agent`.
3537 - Le log du serveur dans la sandbox est `/home/agent/.ori.log`.
3638 - Une sandbox arrêtée redémarre avec `sbx run -d --name <nom-de-la-sandbox>` (ou `sbx attach`) ; le kit relance le serveur à chaque démarrage.
@@ -4,33 +4,35 @@ Ce guide montre comment empaqueter Ori en template Docker Sandboxes et le lancer
4 4
5 ## Étapes5 ## Étapes
6 6
7-1. Récupérez l'image du template — elle est publiée sur Docker Hub sous `k33g/ori:0.0.0`. Pour la (re)construire et la pousser vous-même, depuis la racine du dépôt :7+1. Récupérez l'image du template — elle est publiée sur Docker Hub sous `k33g/ori:0.0.2`. Pour la (re)construire et la pousser vous-même, depuis la racine du dépôt :
8 8
9 ```bash9 ```bash
10 ./template/build.sh10 ./template/build.sh
11 ```11 ```
12 12
13- Le script exécute un `docker buildx build --push` multi-architectures : la SPA et les binaires Go sont compilés dans des étapes intermédiaires, puis posés avec l'adaptateur `claude-code-acp` au-dessus de l'image officielle `docker/sandbox-templates:claude-code`. Pour une image locale uniquement, utilisez plutôt `make template`.13+ Le script exécute un `docker buildx build --push` multi-architectures : la SPA et les binaires Go sont compilés dans des étapes intermédiaires, puis posés avec l'adaptateur `claude-agent-acp` au-dessus de l'image officielle `docker/sandbox-templates:claude-code`. Pour une image locale uniquement, utilisez plutôt `make template`.
14 14
15 2. Créez la sandbox depuis votre répertoire de projet, avec le template et le kit ori, en mode détaché :15 2. Créez la sandbox depuis votre répertoire de projet, avec le template et le kit ori, en mode détaché :
16 16
17 ```bash17 ```bash
18 sbx run -d claude ~/chemin/vers/votre/projet \18 sbx run -d claude ~/chemin/vers/votre/projet \
19- --template k33g/ori:0.0.0 \19+ --template k33g/ori:0.0.2 \
20 --kit /chemin/vers/ori/kits/ori20 --kit /chemin/vers/ori/kits/ori
21 ```21 ```
22 22
23 Le `-d` (`--detached`) est important : sbx arrête une sandbox 30 secondes après la fermeture de la dernière session CLI ouverte dessus, et le trafic du navigateur sur un port publié n'est pas une session. `sbx create` ne tient une session que le temps de son exécution, donc une sandbox créée ainsi passe en `stopped` une demi-minute plus tard. Une sandbox détachée échappe à cet arrêt automatique jusqu'à ce que vous fassiez `sbx stop` ou `sbx rm`. L'option n'existe que sur `sbx run`, et le mode est figé à la création.23 Le `-d` (`--detached`) est important : sbx arrête une sandbox 30 secondes après la fermeture de la dernière session CLI ouverte dessus, et le trafic du navigateur sur un port publié n'est pas une session. `sbx create` ne tient une session que le temps de son exécution, donc une sandbox créée ainsi passe en `stopped` une demi-minute plus tard. Une sandbox détachée échappe à cet arrêt automatique jusqu'à ce que vous fassiez `sbx stop` ou `sbx rm`. L'option n'existe que sur `sbx run`, et le mode est figé à la création.
24 24
25+ Pour fixer un port hôte, ajoutez `-p 5555:8888/tcp`. Gardez le suffixe `/tcp` : sans protocole, sbx ne lie que l'IPv4 (`tcp4`), et un navigateur qui résout `localhost` en `::1` affiche « site inaccessible » alors que le serveur tourne. Évitez les ports que Chrome refuse d'office, comme 6665 à 6669.
26+
25 La commande de démarrage du kit lance le serveur ori sur le port 8888 à chaque démarrage du conteneur ; le kit agent claude injecte les identifiants Anthropic via le proxy de la sandbox, donc aucune connexion n'est nécessaire à l'intérieur.27 La commande de démarrage du kit lance le serveur ori sur le port 8888 à chaque démarrage du conteneur ; le kit agent claude injecte les identifiants Anthropic via le proxy de la sandbox, donc aucune connexion n'est nécessaire à l'intérieur.
26 28
27-3. Ouvrez l'interface : le kit déclare le port 8888, la sortie de la commande affiche donc l'adresse éphémère publiée — `Published web: localhost:<port> -> 8888/tcp`. Ouvrez-la, ou fixez un port stable avec `-p 8888:8888` à la création (ou après coup avec `sbx ports <nom-de-la-sandbox> --publish 8888:8888/tcp`).29+3. Ouvrez l'interface : le kit déclare le port 8888, la sortie de la commande affiche donc l'adresse éphémère publiée — `Published web: localhost:<port> -> 8888/tcp`. Ouvrez-la, ou fixez un port stable avec `-p 8888:8888/tcp` à la création (ou après coup avec `sbx ports <nom-de-la-sandbox> --publish 8888:8888/tcp`).
28 30
29 ## Variantes31 ## Variantes
30 32
31-- Le port d'écoute est un argument du kit (défaut 8888) : ajoutez `--kit-arg ori.port=9000` pour le changer (et publiez ce port-là à la place).33+- Le port d'écoute est un argument du kit (défaut 8888) : ajoutez `--kit-arg ori.port=9000` pour le changer, et publiez ce port vous-même avec `-p 9000:9000` — le port que le kit déclare pour l'auto-publication est fixé à 8888.
32 - Si l'image n'a jamais été poussée sur Docker Hub, confiez-la d'abord au magasin d'images du runtime des sandboxes : `docker save k33g/ori -o /tmp/ori.tar && sbx template load /tmp/ori.tar`.34 - Si l'image n'a jamais été poussée sur Docker Hub, confiez-la d'abord au magasin d'images du runtime des sandboxes : `docker save k33g/ori -o /tmp/ori.tar && sbx template load /tmp/ori.tar`.
33-- Autonome, sans sbx : `docker run --rm -p 8888:8888 k33g/ori` (fournissez `ANTHROPIC_API_KEY`, ou utilisez l'agent de démonstration ci-dessous).35+- Autonome, sans sbx : `docker run --rm -p 8888:8888/tcp k33g/ori` (fournissez `ANTHROPIC_API_KEY`, ou utilisez l'agent de démonstration ci-dessous).
34 - Démo sans identifiants : dans la sandbox, `pkill -x ori` puis relancez avec `--agent-cmd ori-mock-agent`.36 - Démo sans identifiants : dans la sandbox, `pkill -x ori` puis relancez avec `--agent-cmd ori-mock-agent`.
35 - Le log du serveur dans la sandbox est `/home/agent/.ori.log`.37 - Le log du serveur dans la sandbox est `/home/agent/.ori.log`.
36 - Une sandbox arrêtée redémarre avec `sbx run -d --name <nom-de-la-sandbox>` (ou `sbx attach`) ; le kit relance le serveur à chaque démarrage.38 - Une sandbox arrêtée redémarre avec `sbx run -d --name <nom-de-la-sandbox>` (ou `sbx attach`) ; le kit relance le serveur à chaque démarrage.
modified docs/fr/how-to/run-the-tests.md +2 -0
@@ -12,6 +12,8 @@ Ce guide montre comment lancer les suites de tests d'Ori. Il suppose que les dé
1212
1313 Cette commande exécute la suite Go (`go test ./...`) puis la suite front (`vitest run`). Les deux doivent finir au vert.
1414
15+ Le script front positionne `NODE_OPTIONS=--no-experimental-webstorage` : Node 25 fournit un `localStorage` natif sans `clear()` qui masque celui de jsdom et fait échouer tous les tests de store. L'option est connue de Node 22 et suivants, donc sans effet sur les runtimes plus anciens.
16+
1517 ## Variantes
1618
1719 - Backend seul : `make test-go` — tests unitaires de chaque package plus un test de bout en bout qui compile l'agent mock, le lance comme vrai sous-processus et pilote un vrai client WebSocket sur un tour complet.
@@ -12,6 +12,8 @@ Ce guide montre comment lancer les suites de tests d'Ori. Il suppose que les dé
12 12
13 Cette commande exécute la suite Go (`go test ./...`) puis la suite front (`vitest run`). Les deux doivent finir au vert.13 Cette commande exécute la suite Go (`go test ./...`) puis la suite front (`vitest run`). Les deux doivent finir au vert.
14 14
15+ Le script front positionne `NODE_OPTIONS=--no-experimental-webstorage` : Node 25 fournit un `localStorage` natif sans `clear()` qui masque celui de jsdom et fait échouer tous les tests de store. L'option est connue de Node 22 et suivants, donc sans effet sur les runtimes plus anciens.
16+
15 ## Variantes17 ## Variantes
16 18
17 - Backend seul : `make test-go` — tests unitaires de chaque package plus un test de bout en bout qui compile l'agent mock, le lance comme vrai sous-processus et pilote un vrai client WebSocket sur un tour complet.19 - Backend seul : `make test-go` — tests unitaires de chaque package plus un test de bout en bout qui compile l'agent mock, le lance comme vrai sous-processus et pilote un vrai client WebSocket sur un tour complet.
modified docs/fr/how-to/run-with-claude-code.md +2 -2
@@ -4,7 +4,7 @@ Ce guide montre comment démarrer Ori connecté à Claude Code. Il suppose que v
44
55 ## Étapes
66
7-1. Vérifiez que Node.js et npm sont disponibles : le backend lance l'adaptateur ACP officiel de Claude Code avec `npx -y @zed-industries/claude-code-acp`.
7+1. Vérifiez que Node.js et npm sont disponibles : le backend lance l'adaptateur ACP du Claude Agent SDK avec `npx -y @agentclientprotocol/claude-agent-acp`. L'adaptateur embarque sa propre CLI Claude Code (un paquet natif choisi pour votre plateforme) : il fonctionne même si la commande `claude` de votre machine est plus ancienne.
88 2. Vérifiez que Claude Code est authentifié sur cette machine (procédure de connexion de `claude`, ou une variable d'environnement `ANTHROPIC_API_KEY` utilisable par l'adaptateur).
99 3. Depuis la racine du dépôt, démarrez le serveur :
1010
@@ -18,7 +18,7 @@ Ce guide montre comment démarrer Ori connecté à Claude Code. Il suppose que v
1818
1919 - Pour travailler sur un répertoire de projet précis, passez explicitement le répertoire de travail de l'agent : `./bin/ori --cwd /chemin/vers/votre/projet`.
2020 - Pour servir sur un autre port : `./bin/ori --addr :9000`.
21-- Le premier `npx` télécharge le paquet de l'adaptateur ; figez-le en l'installant globalement (`npm install -g @zed-industries/claude-code-acp`) et en passant `--agent-cmd "claude-code-acp"`.
21+- Le premier `npx` télécharge le paquet de l'adaptateur ; figez-le en l'installant globalement (`npm install -g @agentclientprotocol/claude-agent-acp`) et en passant `--agent-cmd "claude-agent-acp"`.
2222
2323 ## Voir aussi
2424
@@ -4,7 +4,7 @@ Ce guide montre comment démarrer Ori connecté à Claude Code. Il suppose que v
4 4
5 ## Étapes5 ## Étapes
6 6
7-1. Vérifiez que Node.js et npm sont disponibles : le backend lance l'adaptateur ACP officiel de Claude Code avec `npx -y @zed-industries/claude-code-acp`.7+1. Vérifiez que Node.js et npm sont disponibles : le backend lance l'adaptateur ACP du Claude Agent SDK avec `npx -y @agentclientprotocol/claude-agent-acp`. L'adaptateur embarque sa propre CLI Claude Code (un paquet natif choisi pour votre plateforme) : il fonctionne même si la commande `claude` de votre machine est plus ancienne.
8 2. Vérifiez que Claude Code est authentifié sur cette machine (procédure de connexion de `claude`, ou une variable d'environnement `ANTHROPIC_API_KEY` utilisable par l'adaptateur).8 2. Vérifiez que Claude Code est authentifié sur cette machine (procédure de connexion de `claude`, ou une variable d'environnement `ANTHROPIC_API_KEY` utilisable par l'adaptateur).
9 3. Depuis la racine du dépôt, démarrez le serveur :9 3. Depuis la racine du dépôt, démarrez le serveur :
10 10
@@ -18,7 +18,7 @@ Ce guide montre comment démarrer Ori connecté à Claude Code. Il suppose que v
18 18
19 - Pour travailler sur un répertoire de projet précis, passez explicitement le répertoire de travail de l'agent : `./bin/ori --cwd /chemin/vers/votre/projet`.19 - Pour travailler sur un répertoire de projet précis, passez explicitement le répertoire de travail de l'agent : `./bin/ori --cwd /chemin/vers/votre/projet`.
20 - Pour servir sur un autre port : `./bin/ori --addr :9000`.20 - Pour servir sur un autre port : `./bin/ori --addr :9000`.
21-- Le premier `npx` télécharge le paquet de l'adaptateur ; figez-le en l'installant globalement (`npm install -g @zed-industries/claude-code-acp`) et en passant `--agent-cmd "claude-code-acp"`.21+- Le premier `npx` télécharge le paquet de l'adaptateur ; figez-le en l'installant globalement (`npm install -g @agentclientprotocol/claude-agent-acp`) et en passant `--agent-cmd "claude-agent-acp"`.
22 22
23 ## Voir aussi23 ## Voir aussi
24 24
modified docs/fr/reference/cli.md +1 -1
@@ -16,7 +16,7 @@ Au démarrage, `ori` lance la commande de l'agent, effectue la poignée de main
1616 | --- | --- | --- | --- |
1717 | `--addr` | chaîne | `:8888` | Adresse TCP d'écoute du serveur HTTP. Un hôte vide écoute sur toutes les interfaces. |
1818 | `--cwd` | chaîne | répertoire courant | Répertoire de travail transmis à la session de l'agent. Doit exister et être un répertoire ; résolu en chemin absolu. |
19-| `--agent-cmd` | chaîne | `npx -y @zed-industries/claude-code-acp` | Commande lançant l'agent ACP, découpée sur les espaces (premier champ : exécutable ; le reste : arguments). |
19+| `--agent-cmd` | chaîne | `npx -y @agentclientprotocol/claude-agent-acp` | Commande lançant l'agent ACP, découpée sur les espaces (premier champ : exécutable ; le reste : arguments). |
2020
2121 ## Endpoints HTTP
2222
@@ -16,7 +16,7 @@ Au démarrage, `ori` lance la commande de l'agent, effectue la poignée de main
16 | --- | --- | --- | --- |16 | --- | --- | --- | --- |
17 | `--addr` | chaîne | `:8888` | Adresse TCP d'écoute du serveur HTTP. Un hôte vide écoute sur toutes les interfaces. |17 | `--addr` | chaîne | `:8888` | Adresse TCP d'écoute du serveur HTTP. Un hôte vide écoute sur toutes les interfaces. |
18 | `--cwd` | chaîne | répertoire courant | Répertoire de travail transmis à la session de l'agent. Doit exister et être un répertoire ; résolu en chemin absolu. |18 | `--cwd` | chaîne | répertoire courant | Répertoire de travail transmis à la session de l'agent. Doit exister et être un répertoire ; résolu en chemin absolu. |
19-| `--agent-cmd` | chaîne | `npx -y @zed-industries/claude-code-acp` | Commande lançant l'agent ACP, découpée sur les espaces (premier champ : exécutable ; le reste : arguments). |19+| `--agent-cmd` | chaîne | `npx -y @agentclientprotocol/claude-agent-acp` | Commande lançant l'agent ACP, découpée sur les espaces (premier champ : exécutable ; le reste : arguments). |
20 20
21 ## Endpoints HTTP21 ## Endpoints HTTP
22 22
modified docs/source-code-analysis.adoc +6 -5
@@ -9,7 +9,7 @@
99
1010 Ori is a web client for https://agentclientprotocol.com[ACP (Agent Client Protocol)] code agents.
1111 A Go backend serves an embedded React SPA and connects over ACP (JSON-RPC on stdio) to an agent
12-subprocess Claude Code via the Zed adapter (`npx -y @zed-industries/claude-code-acp`) by default.
12+subprocess Claude Code via the Claude Agent SDK ACP adapter (`npx -y @agentclientprotocol/claude-agent-acp`) by default.
1313 The browser gets a Zed-style agent panel: streamed markdown answers, collapsible thoughts (live
1414 during streaming, collapsible when complete), working spinner with rotating phrases, tool call
1515 cards (status, locations, diffs), the agent's plan, and permission prompts answered from the UI —
@@ -208,9 +208,10 @@ Reference: `internal/bridge/protocol.go`, `ui/src/protocol.ts`, and
208208 turn is rejected. Multi-session is a planned evolution behind the `bridge.Prompter`
209209 seam.
210210 * File API access is deliberately unrestricted: ori targets already-isolated sandboxes.
211-* Claude Code is reached through the Zed adapter (`@zed-industries/claude-code-acp`,
212- note: renamed upstream to `@agentclientprotocol/claude-agent-acp` but default not yet
213- updated) — the `claude` CLI has no native ACP mode.
211+* Claude Code is reached through the Agent Client Protocol adapter (`@agentclientprotocol/claude-agent-acp`,
212+ which bundles its own Claude Code CLI via the Claude Agent SDK; default since 2026-09-18,
213+ replacing Zed's `@zed-industries/claude-code-acp` whose embedded CLI 2.1.44 the API now
214+ rejects) the `claude` CLI has no native ACP mode.
214215 * Material icons are copied from `vscode-material-icons` package to `public/` via an npm
215216 pre-hook script, making them static assets bundled with the SPA.
216217 * AsciiDoc rendering uses `@asciidoctor/core` v4's async `convert` named export (v3's
@@ -233,7 +234,7 @@ Reference: `internal/bridge/protocol.go`, `ui/src/protocol.ts`, and
233234 Ori ships as a Docker Sandbox template (`template/Dockerfile`, FROM
234235 `docker/sandbox-templates:claude-code`) paired with a kit (`kits/ori/`):
235236
236-* Create: `sbx run -d claude <project> --template k33g/ori:0.0.0 --kit <ori-repo>/kits/ori --name ori -p 8888:8888`
237+* Create: `sbx run -d claude <project> --template k33g/ori:0.0.2 --kit <ori-repo>/kits/ori --name ori -p 8888:8888/tcp`
237238 * The `--detached` (`-d`) flag is **required**: sandboxd auto-stops a sandbox 30 seconds
238239 after its last CLI sentinel session closes; without `-d`, the container stops shortly after
239240 the prompt returns. Browser traffic on a published port does not hold the sandbox up.
@@ -9,7 +9,7 @@
9 9
10 Ori is a web client for https://agentclientprotocol.com[ACP (Agent Client Protocol)] code agents.10 Ori is a web client for https://agentclientprotocol.com[ACP (Agent Client Protocol)] code agents.
11 A Go backend serves an embedded React SPA and connects over ACP (JSON-RPC on stdio) to an agent11 A Go backend serves an embedded React SPA and connects over ACP (JSON-RPC on stdio) to an agent
12-subprocess Claude Code via the Zed adapter (`npx -y @zed-industries/claude-code-acp`) by default.12+subprocess Claude Code via the Claude Agent SDK ACP adapter (`npx -y @agentclientprotocol/claude-agent-acp`) by default.
13 The browser gets a Zed-style agent panel: streamed markdown answers, collapsible thoughts (live13 The browser gets a Zed-style agent panel: streamed markdown answers, collapsible thoughts (live
14 during streaming, collapsible when complete), working spinner with rotating phrases, tool call14 during streaming, collapsible when complete), working spinner with rotating phrases, tool call
15 cards (status, locations, diffs), the agent's plan, and permission prompts answered from the UI —15 cards (status, locations, diffs), the agent's plan, and permission prompts answered from the UI —
@@ -208,9 +208,10 @@ Reference: `internal/bridge/protocol.go`, `ui/src/protocol.ts`, and
208 turn is rejected. Multi-session is a planned evolution behind the `bridge.Prompter`208 turn is rejected. Multi-session is a planned evolution behind the `bridge.Prompter`
209 seam.209 seam.
210 * File API access is deliberately unrestricted: ori targets already-isolated sandboxes.210 * File API access is deliberately unrestricted: ori targets already-isolated sandboxes.
211-* Claude Code is reached through the Zed adapter (`@zed-industries/claude-code-acp`,211+* Claude Code is reached through the Agent Client Protocol adapter (`@agentclientprotocol/claude-agent-acp`,
212- note: renamed upstream to `@agentclientprotocol/claude-agent-acp` but default not yet212+ which bundles its own Claude Code CLI via the Claude Agent SDK; default since 2026-09-18,
213- updated) — the `claude` CLI has no native ACP mode.213+ replacing Zed's `@zed-industries/claude-code-acp` whose embedded CLI 2.1.44 the API now
214+ rejects) the `claude` CLI has no native ACP mode.
214 * Material icons are copied from `vscode-material-icons` package to `public/` via an npm215 * Material icons are copied from `vscode-material-icons` package to `public/` via an npm
215 pre-hook script, making them static assets bundled with the SPA.216 pre-hook script, making them static assets bundled with the SPA.
216 * AsciiDoc rendering uses `@asciidoctor/core` v4's async `convert` named export (v3's217 * AsciiDoc rendering uses `@asciidoctor/core` v4's async `convert` named export (v3's
@@ -233,7 +234,7 @@ Reference: `internal/bridge/protocol.go`, `ui/src/protocol.ts`, and
233 Ori ships as a Docker Sandbox template (`template/Dockerfile`, FROM234 Ori ships as a Docker Sandbox template (`template/Dockerfile`, FROM
234 `docker/sandbox-templates:claude-code`) paired with a kit (`kits/ori/`):235 `docker/sandbox-templates:claude-code`) paired with a kit (`kits/ori/`):
235 236
236-* Create: `sbx run -d claude <project> --template k33g/ori:0.0.0 --kit <ori-repo>/kits/ori --name ori -p 8888:8888`237+* Create: `sbx run -d claude <project> --template k33g/ori:0.0.2 --kit <ori-repo>/kits/ori --name ori -p 8888:8888/tcp`
237 * The `--detached` (`-d`) flag is **required**: sandboxd auto-stops a sandbox 30 seconds238 * The `--detached` (`-d`) flag is **required**: sandboxd auto-stops a sandbox 30 seconds
238 after its last CLI sentinel session closes; without `-d`, the container stops shortly after239 after its last CLI sentinel session closes; without `-d`, the container stops shortly after
239 the prompt returns. Browser traffic on a published port does not hold the sandbox up.240 the prompt returns. Browser traffic on a published port does not hold the sandbox up.
modified internal/agent/agent.go +1 -1
@@ -145,7 +145,7 @@ func Connect(ctx context.Context, in io.Writer, out io.Reader, opts Options) (*S
145145 // Example:
146146 //
147147 // session, err := agent.Start(ctx, agent.Options{
148-// Command: []string{"npx", "-y", "@zed-industries/claude-code-acp"},
148+// Command: []string{"npx", "-y", "@agentclientprotocol/claude-agent-acp"},
149149 // Cwd: "/work/my-project",
150150 // Handler: myHandler,
151151 // })
@@ -145,7 +145,7 @@ func Connect(ctx context.Context, in io.Writer, out io.Reader, opts Options) (*S
145 // Example:145 // Example:
146 //146 //
147 // session, err := agent.Start(ctx, agent.Options{147 // session, err := agent.Start(ctx, agent.Options{
148-// Command: []string{"npx", "-y", "@zed-industries/claude-code-acp"},148+// Command: []string{"npx", "-y", "@agentclientprotocol/claude-agent-acp"},
149 // Cwd: "/work/my-project",149 // Cwd: "/work/my-project",
150 // Handler: myHandler,150 // Handler: myHandler,
151 // })151 // })
modified internal/config/config.go +4 -2
@@ -13,8 +13,10 @@ import (
1313 )
1414
1515 // DefaultAgentCommand is the ACP agent spawned when --agent-cmd is not given.
16-// It is the official Claude Code ACP adapter, the same one Zed uses.
17-const DefaultAgentCommand = "npx -y @zed-industries/claude-code-acp"
16+// It is the Agent Client Protocol project's adapter for the Claude Agent SDK,
17+// which bundles a current Claude Code CLI (the older Zed adapter ships a CLI
18+// too old for current models).
19+const DefaultAgentCommand = "npx -y @agentclientprotocol/claude-agent-acp"
1820
1921 // Config holds the runtime configuration of the ori server.
2022 type Config struct {
@@ -13,8 +13,10 @@ import (
13 )13 )
14 14
15 // DefaultAgentCommand is the ACP agent spawned when --agent-cmd is not given.15 // DefaultAgentCommand is the ACP agent spawned when --agent-cmd is not given.
16-// It is the official Claude Code ACP adapter, the same one Zed uses.16+// It is the Agent Client Protocol project's adapter for the Claude Agent SDK,
17-const DefaultAgentCommand = "npx -y @zed-industries/claude-code-acp"17+// which bundles a current Claude Code CLI (the older Zed adapter ships a CLI
18+// too old for current models).
19+const DefaultAgentCommand = "npx -y @agentclientprotocol/claude-agent-acp"
18 20
19 // Config holds the runtime configuration of the ori server.21 // Config holds the runtime configuration of the ori server.
20 type Config struct {22 type Config struct {
modified internal/config/config_test.go +15 -0
@@ -81,3 +81,18 @@ func ExampleFromArgs() {
8181 fmt.Println(cfg.Addr)
8282 // Output: :9000
8383 }
84+
85+// TestDefaultAgentCommandIsClaudeAgentACP pins the adapter ori spawns by default.
86+// The Zed adapter (@zed-industries/claude-code-acp) bundles a Claude Code CLI
87+// too old for current models (2.1.44 < 2.1.251 required); the Agent Client
88+// Protocol adapter ships a current CLI through its SDK. A change here must be
89+// deliberate and mirrored in template/Dockerfile and kits/ori/spec.yaml.
90+func TestDefaultAgentCommandIsClaudeAgentACP(t *testing.T) {
91+ const want = "npx -y @agentclientprotocol/claude-agent-acp"
92+ if config.DefaultAgentCommand != want {
93+ t.Errorf("DefaultAgentCommand = %q, want %q", config.DefaultAgentCommand, want)
94+ }
95+ if strings.Contains(config.DefaultAgentCommand, "zed-industries") {
96+ t.Errorf("DefaultAgentCommand still points at the Zed adapter: %q", config.DefaultAgentCommand)
97+ }
98+}
@@ -81,3 +81,18 @@ func ExampleFromArgs() {
81 fmt.Println(cfg.Addr)81 fmt.Println(cfg.Addr)
82 // Output: :900082 // Output: :9000
83 }83 }
84+
85+// TestDefaultAgentCommandIsClaudeAgentACP pins the adapter ori spawns by default.
86+// The Zed adapter (@zed-industries/claude-code-acp) bundles a Claude Code CLI
87+// too old for current models (2.1.44 < 2.1.251 required); the Agent Client
88+// Protocol adapter ships a current CLI through its SDK. A change here must be
89+// deliberate and mirrored in template/Dockerfile and kits/ori/spec.yaml.
90+func TestDefaultAgentCommandIsClaudeAgentACP(t *testing.T) {
91+ const want = "npx -y @agentclientprotocol/claude-agent-acp"
92+ if config.DefaultAgentCommand != want {
93+ t.Errorf("DefaultAgentCommand = %q, want %q", config.DefaultAgentCommand, want)
94+ }
95+ if strings.Contains(config.DefaultAgentCommand, "zed-industries") {
96+ t.Errorf("DefaultAgentCommand still points at the Zed adapter: %q", config.DefaultAgentCommand)
97+ }
98+}
modified internal/files/raw.go +16 -2
@@ -23,18 +23,32 @@ var imageTypes = map[string]string{
2323 ".webp": "image/webp",
2424 }
2525
26+// textTypes pins the text formats the preview pane renders itself, for the
27+// same reason as imageTypes: Go seeds its mime table from the host (macOS's
28+// /etc/apache2/mime.types leaves text/markdown commented out), so without a
29+// pin a .md file is served as text/plain on one machine and text/markdown on
30+// another.
31+var textTypes = map[string]string{
32+ ".markdown": "text/markdown; charset=utf-8",
33+ ".md": "text/markdown; charset=utf-8",
34+}
35+
2636 // ContentTypeFor returns the Content-Type the raw endpoint serves a file
27-// under: pinned image types first, then the host mime database, and a
28-// content sniff of the first bytes as a last resort.
37+// under: pinned image and text types first, then the host mime database, and
38+// a content sniff of the first bytes as a last resort.
2939 //
3040 // Example:
3141 //
3242 // files.ContentTypeFor("logo.webp", nil) // "image/webp"
43+// files.ContentTypeFor("README.md", nil) // "text/markdown; charset=utf-8"
3344 func ContentTypeFor(path string, head []byte) string {
3445 ext := strings.ToLower(filepath.Ext(path))
3546 if typ, ok := imageTypes[ext]; ok {
3647 return typ
3748 }
49+ if typ, ok := textTypes[ext]; ok {
50+ return typ
51+ }
3852 if typ := mime.TypeByExtension(ext); typ != "" {
3953 return typ
4054 }
@@ -23,18 +23,32 @@ var imageTypes = map[string]string{
23 ".webp": "image/webp",23 ".webp": "image/webp",
24 }24 }
25 25
26+// textTypes pins the text formats the preview pane renders itself, for the
27+// same reason as imageTypes: Go seeds its mime table from the host (macOS's
28+// /etc/apache2/mime.types leaves text/markdown commented out), so without a
29+// pin a .md file is served as text/plain on one machine and text/markdown on
30+// another.
31+var textTypes = map[string]string{
32+ ".markdown": "text/markdown; charset=utf-8",
33+ ".md": "text/markdown; charset=utf-8",
34+}
35+
26 // ContentTypeFor returns the Content-Type the raw endpoint serves a file36 // ContentTypeFor returns the Content-Type the raw endpoint serves a file
27-// under: pinned image types first, then the host mime database, and a37+// under: pinned image and text types first, then the host mime database, and
28-// content sniff of the first bytes as a last resort.38+// a content sniff of the first bytes as a last resort.
29 //39 //
30 // Example:40 // Example:
31 //41 //
32 // files.ContentTypeFor("logo.webp", nil) // "image/webp"42 // files.ContentTypeFor("logo.webp", nil) // "image/webp"
43+// files.ContentTypeFor("README.md", nil) // "text/markdown; charset=utf-8"
33 func ContentTypeFor(path string, head []byte) string {44 func ContentTypeFor(path string, head []byte) string {
34 ext := strings.ToLower(filepath.Ext(path))45 ext := strings.ToLower(filepath.Ext(path))
35 if typ, ok := imageTypes[ext]; ok {46 if typ, ok := imageTypes[ext]; ok {
36 return typ47 return typ
37 }48 }
49+ if typ, ok := textTypes[ext]; ok {
50+ return typ
51+ }
38 if typ := mime.TypeByExtension(ext); typ != "" {52 if typ := mime.TypeByExtension(ext); typ != "" {
39 return typ53 return typ
40 }54 }
modified internal/files/raw_test.go +2 -0
@@ -77,6 +77,8 @@ func TestContentTypeFor(t *testing.T) {
7777 {"a.ico", nil, "image/x-icon"},
7878 {"a.avif", nil, "image/avif"},
7979 {"a.json", nil, "application/json"},
80+ {"README.md", nil, "text/markdown; charset=utf-8"},
81+ {"NOTES.MARKDOWN", nil, "text/markdown; charset=utf-8"},
8082 {"noext", []byte("plain words"), "text/plain; charset=utf-8"},
8183 }
8284 for _, tc := range cases {
@@ -77,6 +77,8 @@ func TestContentTypeFor(t *testing.T) {
77 {"a.ico", nil, "image/x-icon"},77 {"a.ico", nil, "image/x-icon"},
78 {"a.avif", nil, "image/avif"},78 {"a.avif", nil, "image/avif"},
79 {"a.json", nil, "application/json"},79 {"a.json", nil, "application/json"},
80+ {"README.md", nil, "text/markdown; charset=utf-8"},
81+ {"NOTES.MARKDOWN", nil, "text/markdown; charset=utf-8"},
80 {"noext", []byte("plain words"), "text/plain; charset=utf-8"},82 {"noext", []byte("plain words"), "text/plain; charset=utf-8"},
81 }83 }
82 for _, tc := range cases {84 for _, tc := range cases {
modified kits/ori/README.md +8 -8
@@ -4,14 +4,14 @@ A Docker Sandboxes **mixin kit** that starts the [ori](../../README.md) web serv
44
55 ## 1. Get the template image
66
7-It is published on Docker Hub as `k33g/ori:0.0.0`. To (re)build and push it yourself from the ori repository root:
7+It is published on Docker Hub as `k33g/ori:0.0.2`. To (re)build and push it yourself from the ori repository root:
88
99 ```bash
10-./template/build.sh # multi-arch build, pushes k33g/ori:0.0.0 and :latest
10+./template/build.sh # multi-arch build, pushes k33g/ori:0.0.2 and :latest
1111 # local-only alternative: make template (= docker build -t k33g/ori .)
1212 ```
1313
14-The image stacks ori (server, SPA and demo agent) and the `claude-code-acp` adapter on top of the official `docker/sandbox-templates:claude-code` template, so everything the claude sandbox normally provides is still there.
14+The image stacks ori (server, SPA and demo agent) and the `claude-agent-acp` adapter on top of the official `docker/sandbox-templates:claude-code` template, so everything the claude sandbox normally provides is still there.
1515
1616 If the image only exists on your host engine (never pushed to Docker Hub), hand it to the sandbox runtime's own image store:
1717
@@ -25,15 +25,15 @@ sbx template ls
2525
2626 ```bash
2727 sbx run -d claude ~/path/to/your/project \
28- --template k33g/ori:0.0.0 \
28+ --template k33g/ori:0.0.2 \
2929 --kit /path/to/ori/kits/ori
3030 ```
3131
3232 Use `sbx run -d` (`--detached`), **not** `sbx create`: sbx auto-stops a sandbox 30 s after its last CLI session closes, and a web server has no CLI session to hold it up. A detached sandbox is exempt from that auto-stop for its whole life; the mode is fixed at creation, so a sandbox made with `sbx create` has to be `sbx rm`'d and recreated.
3333
34-`--template` swaps the sandbox image for `k33g/ori:0.0.0`; `--kit` applies this mixin, whose startup command launches the ori server at every container start. The claude agent kit still handles Anthropic credentials, so the server's agent works without any login inside the sandbox.
34+`--template` swaps the sandbox image for `k33g/ori:0.0.2`; `--kit` applies this mixin, whose startup command launches the ori server at every container start. The claude agent kit still handles Anthropic credentials, so the server's agent works without any login inside the sandbox.
3535
36-The listen port defaults to **8888** and is a kit argument: add `--kit-arg ori.port=9000` (or `--kit-arg port=9000`) to change it.
36+The listen port defaults to **8888** and is a kit argument: add `--kit-arg ori.port=9000` (or `--kit-arg port=9000`) to change it. The port the kit *declares* for auto-publishing stays 8888 (an sbx packaging limitation, see the comment in `spec.yaml`), so with a custom port publish it yourself: `-p 9000:9000`.
3737
3838 ## 3. Open the UI
3939
@@ -46,7 +46,7 @@ The kit declares port 8888, so the creation auto-publishes an ephemeral localhos
4646 Browse to that address, or pin a fixed host port instead:
4747
4848 ```bash
49-sbx run -d claude … -p 8888:8888 # at creation
49+sbx run -d claude … -p 8888:8888/tcp # at creation
5050 sbx ports <sandbox-name> --publish 8888:8888/tcp # afterwards
5151 ```
5252
@@ -54,4 +54,4 @@ sbx ports <sandbox-name> --publish 8888:8888/tcp # afterwards
5454
5555 - Server log inside the sandbox: `/home/agent/.ori.log`.
5656 - To try the UI without consuming Claude usage, restart the server with the bundled demo agent: `pkill -x ori; nohup ori --addr :8888 --agent-cmd ori-mock-agent --cwd "$PWD" >> /home/agent/.ori.log 2>&1 &`.
57-- The image also runs standalone, outside sbx: `docker run --rm -p 8888:8888 k33g/ori` (bring your own `ANTHROPIC_API_KEY`, or switch to `ori-mock-agent`).
57+- The image also runs standalone, outside sbx: `docker run --rm -p 8888:8888/tcp k33g/ori` (bring your own `ANTHROPIC_API_KEY`, or switch to `ori-mock-agent`).
@@ -4,14 +4,14 @@ A Docker Sandboxes **mixin kit** that starts the [ori](../../README.md) web serv
4 4
5 ## 1. Get the template image5 ## 1. Get the template image
6 6
7-It is published on Docker Hub as `k33g/ori:0.0.0`. To (re)build and push it yourself from the ori repository root:7+It is published on Docker Hub as `k33g/ori:0.0.2`. To (re)build and push it yourself from the ori repository root:
8 8
9 ```bash9 ```bash
10-./template/build.sh # multi-arch build, pushes k33g/ori:0.0.0 and :latest10+./template/build.sh # multi-arch build, pushes k33g/ori:0.0.2 and :latest
11 # local-only alternative: make template (= docker build -t k33g/ori .)11 # local-only alternative: make template (= docker build -t k33g/ori .)
12 ```12 ```
13 13
14-The image stacks ori (server, SPA and demo agent) and the `claude-code-acp` adapter on top of the official `docker/sandbox-templates:claude-code` template, so everything the claude sandbox normally provides is still there.14+The image stacks ori (server, SPA and demo agent) and the `claude-agent-acp` adapter on top of the official `docker/sandbox-templates:claude-code` template, so everything the claude sandbox normally provides is still there.
15 15
16 If the image only exists on your host engine (never pushed to Docker Hub), hand it to the sandbox runtime's own image store:16 If the image only exists on your host engine (never pushed to Docker Hub), hand it to the sandbox runtime's own image store:
17 17
@@ -25,15 +25,15 @@ sbx template ls
25 25
26 ```bash26 ```bash
27 sbx run -d claude ~/path/to/your/project \27 sbx run -d claude ~/path/to/your/project \
28- --template k33g/ori:0.0.0 \28+ --template k33g/ori:0.0.2 \
29 --kit /path/to/ori/kits/ori29 --kit /path/to/ori/kits/ori
30 ```30 ```
31 31
32 Use `sbx run -d` (`--detached`), **not** `sbx create`: sbx auto-stops a sandbox 30 s after its last CLI session closes, and a web server has no CLI session to hold it up. A detached sandbox is exempt from that auto-stop for its whole life; the mode is fixed at creation, so a sandbox made with `sbx create` has to be `sbx rm`'d and recreated.32 Use `sbx run -d` (`--detached`), **not** `sbx create`: sbx auto-stops a sandbox 30 s after its last CLI session closes, and a web server has no CLI session to hold it up. A detached sandbox is exempt from that auto-stop for its whole life; the mode is fixed at creation, so a sandbox made with `sbx create` has to be `sbx rm`'d and recreated.
33 33
34-`--template` swaps the sandbox image for `k33g/ori:0.0.0`; `--kit` applies this mixin, whose startup command launches the ori server at every container start. The claude agent kit still handles Anthropic credentials, so the server's agent works without any login inside the sandbox.34+`--template` swaps the sandbox image for `k33g/ori:0.0.2`; `--kit` applies this mixin, whose startup command launches the ori server at every container start. The claude agent kit still handles Anthropic credentials, so the server's agent works without any login inside the sandbox.
35 35
36-The listen port defaults to **8888** and is a kit argument: add `--kit-arg ori.port=9000` (or `--kit-arg port=9000`) to change it.36+The listen port defaults to **8888** and is a kit argument: add `--kit-arg ori.port=9000` (or `--kit-arg port=9000`) to change it. The port the kit *declares* for auto-publishing stays 8888 (an sbx packaging limitation, see the comment in `spec.yaml`), so with a custom port publish it yourself: `-p 9000:9000`.
37 37
38 ## 3. Open the UI38 ## 3. Open the UI
39 39
@@ -46,7 +46,7 @@ The kit declares port 8888, so the creation auto-publishes an ephemeral localhos
46 Browse to that address, or pin a fixed host port instead:46 Browse to that address, or pin a fixed host port instead:
47 47
48 ```bash48 ```bash
49-sbx run -d claude … -p 8888:8888 # at creation49+sbx run -d claude … -p 8888:8888/tcp # at creation
50 sbx ports <sandbox-name> --publish 8888:8888/tcp # afterwards50 sbx ports <sandbox-name> --publish 8888:8888/tcp # afterwards
51 ```51 ```
52 52
@@ -54,4 +54,4 @@ sbx ports <sandbox-name> --publish 8888:8888/tcp # afterwards
54 54
55 - Server log inside the sandbox: `/home/agent/.ori.log`.55 - Server log inside the sandbox: `/home/agent/.ori.log`.
56 - To try the UI without consuming Claude usage, restart the server with the bundled demo agent: `pkill -x ori; nohup ori --addr :8888 --agent-cmd ori-mock-agent --cwd "$PWD" >> /home/agent/.ori.log 2>&1 &`.56 - To try the UI without consuming Claude usage, restart the server with the bundled demo agent: `pkill -x ori; nohup ori --addr :8888 --agent-cmd ori-mock-agent --cwd "$PWD" >> /home/agent/.ori.log 2>&1 &`.
57-- The image also runs standalone, outside sbx: `docker run --rm -p 8888:8888 k33g/ori` (bring your own `ANTHROPIC_API_KEY`, or switch to `ori-mock-agent`).57+- The image also runs standalone, outside sbx: `docker run --rm -p 8888:8888/tcp k33g/ori` (bring your own `ANTHROPIC_API_KEY`, or switch to `ori-mock-agent`).
modified kits/ori/spec.yaml +14 -8
@@ -10,12 +10,12 @@ description: >
1010 Monaco preview/editor with rendered Markdown and AsciiDoc, interactive
1111 terminal). This kit only launches and describes the server; the binaries
1212 ship in the k33g/ori template image (template/Dockerfile in the ori
13- repository), so use it together with `--template k33g/ori:0.0.1`.
13+ repository), so use it together with `--template k33g/ori:0.0.2`.
1414 licenses:
1515 - MIT
1616
1717 # This mixin only makes sense composed with the claude agent kit: the server
18-# it starts drives Claude Code through the claude-code-acp adapter, and the
18+# it starts drives Claude Code through the claude-agent-acp adapter, and the
1919 # Anthropic credentials/egress come from that kit.
2020 requires:
2121 agent: claude
@@ -33,9 +33,15 @@ args:
3333 # Auto-publishes an ephemeral localhost port at create (the create output
3434 # prints it: "Published web: localhost:<port> -> <port>/tcp"). Pin a fixed
3535 # host port instead with `-p 8888:8888` at create or `sbx ports … --publish`.
36-# The unquoted arg reference decodes as the integer the schema expects.
36+#
37+# Deliberately a literal, not `${{ kit.args.port }}`: `sbx kit pack` / `push`
38+# (v0.43) decode the raw spec.yaml into typed fields before any argument
39+# expansion, so a placeholder in this integer field fails with "cannot
40+# unmarshal !!str `${{ kit...` into int" — while validate / inspect / run,
41+# which expand first, accept it. If `port` is overridden, this auto-published
42+# port no longer matches the server: publish the real one with `-p`.
3743 ports:
38- - container: ${{ kit.args.port }}
44+ - container: 8888
3945 protocol: tcp
4046 name: web
4147
@@ -49,7 +55,7 @@ setup:
4955 #
5056 # The PATH export is load-bearing: the dispatcher runs this via
5157 # `su -s /bin/sh -c … agent`, and su resets PATH to the login.defs
52- # default — which does not contain the npm prefix where claude-code-acp
58+ # default — which does not contain the npm prefix where claude-agent-acp
5359 # lives, nor ~/.local/bin where claude does.
5460 - command:
5561 - "sh"
@@ -59,7 +65,7 @@ setup:
5965 pgrep -x ori >/dev/null 2>&1 && exit 0
6066 nohup /usr/local/bin/ori \
6167 --addr ":${{ kit.args.port }}" \
62- --agent-cmd claude-code-acp \
68+ --agent-cmd claude-agent-acp \
6369 --cwd "${WORKSPACE_DIR:-/home/agent/workspace}" \
6470 >> /home/agent/.ori.log 2>&1 &
6571 description: Start the ori web server (idempotent)
@@ -76,10 +82,10 @@ agentInstructions:
7682 (a deterministic demo ACP agent that needs no credentials).
7783 - Log file: `/home/agent/.ori.log`.
7884 - The server drives its own Claude Code session through the
79- `claude-code-acp` adapter (preinstalled); credentials are injected by the
85+ `claude-agent-acp` adapter (preinstalled, ships Claude Code 2.1.274); credentials are injected by the
8086 sandbox proxy, so no login is needed inside the container.
8187 - Restart it with:
82- `pkill -x ori; nohup ori --addr ":${{ kit.args.port }}" --agent-cmd claude-code-acp --cwd "$PWD" >> /home/agent/.ori.log 2>&1 &`
88+ `pkill -x ori; nohup ori --addr ":${{ kit.args.port }}" --agent-cmd claude-agent-acp --cwd "$PWD" >> /home/agent/.ori.log 2>&1 &`
8389 - To demo without consuming Claude usage, restart it with
8490 `--agent-cmd ori-mock-agent` instead.
8591
@@ -10,12 +10,12 @@ description: >
10 Monaco preview/editor with rendered Markdown and AsciiDoc, interactive10 Monaco preview/editor with rendered Markdown and AsciiDoc, interactive
11 terminal). This kit only launches and describes the server; the binaries11 terminal). This kit only launches and describes the server; the binaries
12 ship in the k33g/ori template image (template/Dockerfile in the ori12 ship in the k33g/ori template image (template/Dockerfile in the ori
13- repository), so use it together with `--template k33g/ori:0.0.1`.13+ repository), so use it together with `--template k33g/ori:0.0.2`.
14 licenses:14 licenses:
15 - MIT15 - MIT
16 16
17 # This mixin only makes sense composed with the claude agent kit: the server17 # This mixin only makes sense composed with the claude agent kit: the server
18-# it starts drives Claude Code through the claude-code-acp adapter, and the18+# it starts drives Claude Code through the claude-agent-acp adapter, and the
19 # Anthropic credentials/egress come from that kit.19 # Anthropic credentials/egress come from that kit.
20 requires:20 requires:
21 agent: claude21 agent: claude
@@ -33,9 +33,15 @@ args:
33 # Auto-publishes an ephemeral localhost port at create (the create output33 # Auto-publishes an ephemeral localhost port at create (the create output
34 # prints it: "Published web: localhost:<port> -> <port>/tcp"). Pin a fixed34 # prints it: "Published web: localhost:<port> -> <port>/tcp"). Pin a fixed
35 # host port instead with `-p 8888:8888` at create or `sbx ports … --publish`.35 # host port instead with `-p 8888:8888` at create or `sbx ports … --publish`.
36-# The unquoted arg reference decodes as the integer the schema expects.36+#
37+# Deliberately a literal, not `${{ kit.args.port }}`: `sbx kit pack` / `push`
38+# (v0.43) decode the raw spec.yaml into typed fields before any argument
39+# expansion, so a placeholder in this integer field fails with "cannot
40+# unmarshal !!str `${{ kit...` into int" — while validate / inspect / run,
41+# which expand first, accept it. If `port` is overridden, this auto-published
42+# port no longer matches the server: publish the real one with `-p`.
37 ports:43 ports:
38- - container: ${{ kit.args.port }}44+ - container: 8888
39 protocol: tcp45 protocol: tcp
40 name: web46 name: web
41 47
@@ -49,7 +55,7 @@ setup:
49 #55 #
50 # The PATH export is load-bearing: the dispatcher runs this via56 # The PATH export is load-bearing: the dispatcher runs this via
51 # `su -s /bin/sh -c … agent`, and su resets PATH to the login.defs57 # `su -s /bin/sh -c … agent`, and su resets PATH to the login.defs
52- # default — which does not contain the npm prefix where claude-code-acp58+ # default — which does not contain the npm prefix where claude-agent-acp
53 # lives, nor ~/.local/bin where claude does.59 # lives, nor ~/.local/bin where claude does.
54 - command:60 - command:
55 - "sh"61 - "sh"
@@ -59,7 +65,7 @@ setup:
59 pgrep -x ori >/dev/null 2>&1 && exit 065 pgrep -x ori >/dev/null 2>&1 && exit 0
60 nohup /usr/local/bin/ori \66 nohup /usr/local/bin/ori \
61 --addr ":${{ kit.args.port }}" \67 --addr ":${{ kit.args.port }}" \
62- --agent-cmd claude-code-acp \68+ --agent-cmd claude-agent-acp \
63 --cwd "${WORKSPACE_DIR:-/home/agent/workspace}" \69 --cwd "${WORKSPACE_DIR:-/home/agent/workspace}" \
64 >> /home/agent/.ori.log 2>&1 &70 >> /home/agent/.ori.log 2>&1 &
65 description: Start the ori web server (idempotent)71 description: Start the ori web server (idempotent)
@@ -76,10 +82,10 @@ agentInstructions:
76 (a deterministic demo ACP agent that needs no credentials).82 (a deterministic demo ACP agent that needs no credentials).
77 - Log file: `/home/agent/.ori.log`.83 - Log file: `/home/agent/.ori.log`.
78 - The server drives its own Claude Code session through the84 - The server drives its own Claude Code session through the
79- `claude-code-acp` adapter (preinstalled); credentials are injected by the85+ `claude-agent-acp` adapter (preinstalled, ships Claude Code 2.1.274); credentials are injected by the
80 sandbox proxy, so no login is needed inside the container.86 sandbox proxy, so no login is needed inside the container.
81 - Restart it with:87 - Restart it with:
82- `pkill -x ori; nohup ori --addr ":${{ kit.args.port }}" --agent-cmd claude-code-acp --cwd "$PWD" >> /home/agent/.ori.log 2>&1 &`88+ `pkill -x ori; nohup ori --addr ":${{ kit.args.port }}" --agent-cmd claude-agent-acp --cwd "$PWD" >> /home/agent/.ori.log 2>&1 &`
83 - To demo without consuming Claude usage, restart it with89 - To demo without consuming Claude usage, restart it with
84 `--agent-cmd ori-mock-agent` instead.90 `--agent-cmd ori-mock-agent` instead.
85 91
deleted picard-story.md +0 -33
deleted file mode 100644
@@ -1,33 +0,0 @@
1-# The Archaeologist's Gambit
2-
3-**Agent: Bob**
4-
5-Captain Jean-Luc Picard stood before the ancient artifact, his fingers hovering millimeters above its surface. The crystalline structure pulsed with a faint amber glow, responding to his proximity like a living thing.
6-
7-"Captain, I must advise caution," Data said from behind him, his golden eyes scanning the tricorder readings. "The energy signature is unlike anything in our database."
8-
9-Picard's lips curved into a slight smile. "Mr. Data, in my experience, the most profound discoveries often lie beyond the boundaries of our databases."
10-
11-They were deep within the ruins of Kataan—not the Kataan of his inner light experience, but its sister colony, hidden for millennia beneath the ice sheets of Delta Sigma IV. The Federation archaeological survey had discovered it three weeks ago, and Picard had been unable to resist the opportunity to lead the away team himself.
12-
13-"This script," Picard murmured, tracing the air above the inscriptions that spiraled around the artifact's base, "is Kataan, but it's... older. More refined than what I learned."
14-
15-He closed his eyes, and for a moment, he was Kamin again—the memories of that life as vivid as his actual past. The flute music, the sun on his face, Eline's laugh. A life lived in forty minutes that had shaped him forever.
16-
17-"It's a beacon," Picard said suddenly, his eyes opening. "Not a warning, but an invitation. The Kataan people knew their sun was dying. They sent out their culture, their memories, in multiple forms. We found one—the probe that found me. But this... this is something more."
18-
19-"More?" Commander Riker's voice crackled through the comm badge. "Captain, you're already talking about a civilization that downloaded an entire lifetime into your brain. What could be 'more' than that?"
20-
21-Picard's hand finally made contact with the artifact. The amber light exploded into a cascade of images—not memories this time, but pure information. Mathematics. Philosophy. Art. The complete accumulated knowledge of a civilization.
22-
23-"Number One," Picard said, his voice thick with emotion, "prepare to download approximately four hundred exabytes of data. The Kataan didn't just want to be remembered—they wanted to teach. This is their university, their library, their gift to whoever would listen."
24-
25-As Data began the careful transfer, Picard stood back, one hand unconsciously moving to his uniform jacket pocket where he kept Kamin's flute. He had been given a life once by the Kataan people. Now, they were giving him—giving humanity—the tools to understand that life and countless others.
26-
27-"Make it so," he whispered to himself, a small smile playing at the corners of his mouth.
28-
29-The Enterprise would be here for weeks, perhaps months, cataloging this discovery. And for once, Jean-Luc Picard, who had always believed that to boldly go meant to leave and explore further, was content to stay still and listen to the voices of the past.
30-
31----
32-
33-*Captain's Log, Supplemental: The Kataan archive is beyond anything I imagined. In seeking to preserve themselves, they may have given us the key to understanding dozens of lost civilizations. I find myself wondering: how many other ancient peoples left such gifts among the stars, waiting for someone to stop and listen? Perhaps the greatest exploration is not in discovering new worlds, but in understanding those that came before us.*
deleted file mode 100644
@@ -1,33 +0,0 @@
1-# The Archaeologist's Gambit
2-
3-**Agent: Bob**
4-
5-Captain Jean-Luc Picard stood before the ancient artifact, his fingers hovering millimeters above its surface. The crystalline structure pulsed with a faint amber glow, responding to his proximity like a living thing.
6-
7-"Captain, I must advise caution," Data said from behind him, his golden eyes scanning the tricorder readings. "The energy signature is unlike anything in our database."
8-
9-Picard's lips curved into a slight smile. "Mr. Data, in my experience, the most profound discoveries often lie beyond the boundaries of our databases."
10-
11-They were deep within the ruins of Kataan—not the Kataan of his inner light experience, but its sister colony, hidden for millennia beneath the ice sheets of Delta Sigma IV. The Federation archaeological survey had discovered it three weeks ago, and Picard had been unable to resist the opportunity to lead the away team himself.
12-
13-"This script," Picard murmured, tracing the air above the inscriptions that spiraled around the artifact's base, "is Kataan, but it's... older. More refined than what I learned."
14-
15-He closed his eyes, and for a moment, he was Kamin again—the memories of that life as vivid as his actual past. The flute music, the sun on his face, Eline's laugh. A life lived in forty minutes that had shaped him forever.
16-
17-"It's a beacon," Picard said suddenly, his eyes opening. "Not a warning, but an invitation. The Kataan people knew their sun was dying. They sent out their culture, their memories, in multiple forms. We found one—the probe that found me. But this... this is something more."
18-
19-"More?" Commander Riker's voice crackled through the comm badge. "Captain, you're already talking about a civilization that downloaded an entire lifetime into your brain. What could be 'more' than that?"
20-
21-Picard's hand finally made contact with the artifact. The amber light exploded into a cascade of images—not memories this time, but pure information. Mathematics. Philosophy. Art. The complete accumulated knowledge of a civilization.
22-
23-"Number One," Picard said, his voice thick with emotion, "prepare to download approximately four hundred exabytes of data. The Kataan didn't just want to be remembered—they wanted to teach. This is their university, their library, their gift to whoever would listen."
24-
25-As Data began the careful transfer, Picard stood back, one hand unconsciously moving to his uniform jacket pocket where he kept Kamin's flute. He had been given a life once by the Kataan people. Now, they were giving him—giving humanity—the tools to understand that life and countless others.
26-
27-"Make it so," he whispered to himself, a small smile playing at the corners of his mouth.
28-
29-The Enterprise would be here for weeks, perhaps months, cataloging this discovery. And for once, Jean-Luc Picard, who had always believed that to boldly go meant to leave and explore further, was content to stay still and listen to the voices of the past.
30-
31----
32-
33-*Captain's Log, Supplemental: The Kataan archive is beyond anything I imagined. In seeking to preserve themselves, they may have given us the key to understanding dozens of lost civilizations. I find myself wondering: how many other ancient peoples left such gifts among the stars, waiting for someone to stop and listen? Perhaps the greatest exploration is not in discovering new worlds, but in understanding those that came before us.*
modified quickstart.md +17 -8
@@ -13,10 +13,10 @@ From the directory where the kit lives, pointing at the project you want the age
1313
1414 ```bash
1515 sbx run -d claude ~/path/to/your/project \
16- --template k33g/ori:0.0.0 \
16+ --template k33g/ori:0.0.2 \
1717 --kit ./kits/ori \
1818 --name ori \
19- -p 8888:8888
19+ -p 8888:8888/tcp
2020 ```
2121
2222 Then open **http://localhost:8888** — the panel connects to a Claude Code session already running in the sandbox, no login needed.
@@ -33,7 +33,7 @@ Without `-p`, the kit still publishes an ephemeral port; the command output prin
3333
3434 | Action | Command |
3535 | --- | --- |
36-| Use another host port (e.g. http://localhost:5555) | `-p 5555:8888` at creation, or `sbx ports ori --publish 5555:8888/tcp` |
36+| Use another host port (e.g. http://localhost:5555) | `-p 5555:8888/tcp` at creation, or `sbx ports ori --publish 5555:8888/tcp` |
3737 | Change the internal port | `--kit-arg ori.port=9000` (then `-p 9000:9000`) |
3838 | Publish / unpublish a port later | `sbx ports ori --publish 8888:8888/tcp` / `--unpublish 8888:8888/tcp` |
3939 | Follow the server log | `sbx exec ori tail -f /home/agent/.ori.log` |
@@ -47,7 +47,7 @@ Without `-p`, the kit still publishes an ephemeral port; the command output prin
4747
4848 ## Building the image yourself
4949
50-Only needed if you maintain the template: `./template/build.sh` builds and pushes `k33g/ori:0.0.0` + `:latest` (multi-arch). For a never-pushed local image: `make template && docker save k33g/ori -o /tmp/ori.tar && sbx template load /tmp/ori.tar`.
50+Only needed if you maintain the template: `./template/build.sh` builds and pushes `k33g/ori:0.0.2` + `:latest` (multi-arch). For a never-pushed local image: `make template && docker save k33g/ori -o /tmp/ori.tar && sbx template load /tmp/ori.tar`.
5151
5252 Full guide: [docs/en/how-to/run-in-a-sandbox.md](docs/en/how-to/run-in-a-sandbox.md) · [docs/fr/how-to/run-in-a-sandbox.md](docs/fr/how-to/run-in-a-sandbox.md)
5353
@@ -61,15 +61,24 @@ xattr -cr build/bin/ori-desktop.app
6161
6262 ```bash
6363 sbx run -d claude . \
64- --template k33g/ori:0.0.1 \
64+ --template k33g/ori:0.0.2 \
6565 --kit ./kits/ori \
6666 --name ori1 \
67- -p 8888:8888
67+ -p 8888:8888/tcp
6868
6969 sbx run -d claude . \
70- --template k33g/ori:0.0.1 \
70+ --template k33g/ori:0.0.2 \
7171 --kit ./kits/ori \
7272 --name ori \
73- -p 5555:8888
73+ -p 5555:8888/tcp
7474 ```
7575
76+
77+
78+sbx rm hello-world
79+
80+sbx run -d claude . \
81+ --template k33g/ori:0.0.2 \
82+ --kit docker.io/k33g/ori-kit:latest \
83+ --name hello-world \
84+ -p 5555:8888/tcp
@@ -13,10 +13,10 @@ From the directory where the kit lives, pointing at the project you want the age
13 13
14 ```bash14 ```bash
15 sbx run -d claude ~/path/to/your/project \15 sbx run -d claude ~/path/to/your/project \
16- --template k33g/ori:0.0.0 \16+ --template k33g/ori:0.0.2 \
17 --kit ./kits/ori \17 --kit ./kits/ori \
18 --name ori \18 --name ori \
19- -p 8888:888819+ -p 8888:8888/tcp
20 ```20 ```
21 21
22 Then open **http://localhost:8888** — the panel connects to a Claude Code session already running in the sandbox, no login needed.22 Then open **http://localhost:8888** — the panel connects to a Claude Code session already running in the sandbox, no login needed.
@@ -33,7 +33,7 @@ Without `-p`, the kit still publishes an ephemeral port; the command output prin
33 33
34 | Action | Command |34 | Action | Command |
35 | --- | --- |35 | --- | --- |
36-| Use another host port (e.g. http://localhost:5555) | `-p 5555:8888` at creation, or `sbx ports ori --publish 5555:8888/tcp` |36+| Use another host port (e.g. http://localhost:5555) | `-p 5555:8888/tcp` at creation, or `sbx ports ori --publish 5555:8888/tcp` |
37 | Change the internal port | `--kit-arg ori.port=9000` (then `-p 9000:9000`) |37 | Change the internal port | `--kit-arg ori.port=9000` (then `-p 9000:9000`) |
38 | Publish / unpublish a port later | `sbx ports ori --publish 8888:8888/tcp` / `--unpublish 8888:8888/tcp` |38 | Publish / unpublish a port later | `sbx ports ori --publish 8888:8888/tcp` / `--unpublish 8888:8888/tcp` |
39 | Follow the server log | `sbx exec ori tail -f /home/agent/.ori.log` |39 | Follow the server log | `sbx exec ori tail -f /home/agent/.ori.log` |
@@ -47,7 +47,7 @@ Without `-p`, the kit still publishes an ephemeral port; the command output prin
47 47
48 ## Building the image yourself48 ## Building the image yourself
49 49
50-Only needed if you maintain the template: `./template/build.sh` builds and pushes `k33g/ori:0.0.0` + `:latest` (multi-arch). For a never-pushed local image: `make template && docker save k33g/ori -o /tmp/ori.tar && sbx template load /tmp/ori.tar`.50+Only needed if you maintain the template: `./template/build.sh` builds and pushes `k33g/ori:0.0.2` + `:latest` (multi-arch). For a never-pushed local image: `make template && docker save k33g/ori -o /tmp/ori.tar && sbx template load /tmp/ori.tar`.
51 51
52 Full guide: [docs/en/how-to/run-in-a-sandbox.md](docs/en/how-to/run-in-a-sandbox.md) · [docs/fr/how-to/run-in-a-sandbox.md](docs/fr/how-to/run-in-a-sandbox.md)52 Full guide: [docs/en/how-to/run-in-a-sandbox.md](docs/en/how-to/run-in-a-sandbox.md) · [docs/fr/how-to/run-in-a-sandbox.md](docs/fr/how-to/run-in-a-sandbox.md)
53 53
@@ -61,15 +61,24 @@ xattr -cr build/bin/ori-desktop.app
61 61
62 ```bash62 ```bash
63 sbx run -d claude . \63 sbx run -d claude . \
64- --template k33g/ori:0.0.1 \64+ --template k33g/ori:0.0.2 \
65 --kit ./kits/ori \65 --kit ./kits/ori \
66 --name ori1 \66 --name ori1 \
67- -p 8888:888867+ -p 8888:8888/tcp
68 68
69 sbx run -d claude . \69 sbx run -d claude . \
70- --template k33g/ori:0.0.1 \70+ --template k33g/ori:0.0.2 \
71 --kit ./kits/ori \71 --kit ./kits/ori \
72 --name ori \72 --name ori \
73- -p 5555:888873+ -p 5555:8888/tcp
74 ```74 ```
75 75
76+
77+
78+sbx rm hello-world
79+
80+sbx run -d claude . \
81+ --template k33g/ori:0.0.2 \
82+ --kit docker.io/k33g/ori-kit:latest \
83+ --name hello-world \
84+ -p 5555:8888/tcp
deleted sbx-docker-summary.adoc +0 -294
deleted file mode 100644
@@ -1,294 +0,0 @@
1-= SBX: Docker Sandbox Environment
2-:toc: left
3-:toclevels: 3
4-:sectnums:
5-:icons: font
6-
7-== Overview
8-
9-SBX is Docker's sandboxed environment system that provides isolated, persistent development containers with controlled network access, environment persistence, and Git integration. It enables secure development workflows with fine-grained policy control.
10-
11-== Core Features
12-
13-=== Environment Persistence
14-
15-SBX provides a persistent environment file at `/etc/sandbox-persistent.sh` that is automatically sourced across all shell contexts:
16-
17-* **Non-interactive shells**: via `BASH_ENV=/etc/sandbox-persistent.sh`
18-* **Login shells**: via `/etc/profile.d/sandbox-persistent.sh`
19-* **Interactive shells**: via `/etc/bash.bashrc` and `~/.bashrc`
20-
21-.Adding Persistent Variables
22-[source,bash]
23-----
24-echo "export VAR_NAME=value" >> /etc/sandbox-persistent.sh
25-----
26-
27-[IMPORTANT]
28-====
29-**Never add shell completion scripts to `/etc/sandbox-persistent.sh`**. Completion scripts break the bash tool because the persistent file is sourced before every command execution, not just during initialization. Only add core initialization scripts (like `nvm.sh` or `sdkman-init.sh`), never completion scripts (like `bash_completion`).
30-====
31-
32-=== Network Access and Security
33-
34-SBX includes a firewall that restricts outbound network access with policy-based controls.
35-
36-==== Blocked Request Handling
37-
38-Blocked HTTP/HTTPS requests return **HTTP 403** with structured explanations:
39-
40-[horizontal]
41-Blocked by local rule:: A developer-added deny rule (global or per-sandbox) is blocking the host
42-Blocked by org policy:: Centralized organization policy enforcement
43-No matching allow rule:: Domain not on any allow list (default deny)
44-
45-.Inspecting Connection Policy
46-[source,bash]
47-----
48-sbx policy log # Shows host, rule, reason, last-seen time
49-sbx policy ls # Shows active rules and suppressed rules
50-----
51-
52-.Allowing Network Access
53-[source,bash]
54-----
55-sbx policy allow network <domain>[,<domain>] # Allow specific domains
56-sbx policy allow network "**" # Allow all (not on denylist)
57-----
58-
59-.Removing Deny Rules
60-[source,bash]
61-----
62-sbx policy rm network --resource <host> # Remove global deny
63-sbx policy rm network --sandbox <sandbox> --resource <host> # Remove sandbox-scoped deny
64-----
65-
66-==== Port Publishing
67-
68-Services in the sandbox are not directly accessible from the host. To expose ports:
69-
70-[source,bash]
71-----
72-sbx ports <sandbox-name> --publish [[HOST_IP:]HOST_PORT:]SANDBOX_PORT[/PROTOCOL]
73-----
74-
75-.Example: Publishing Web Server
76-[source,bash]
77-----
78-sbx ports <sandbox-name> --publish 8080:8080/tcp
79-sbx ports <sandbox-name> # List published ports
80-sbx ports <sandbox-name> --unpublish 8080:8080/tcp # Unpublish
81-----
82-
83-[NOTE]
84-====
85-Services must listen on the `eth0` interface (not just `127.0.0.1`). Bind to `0.0.0.0` (IPv4) or `::` (IPv6) to be reachable via port publishing.
86-====
87-
88-==== Accessing Host Services
89-
90-The sandbox has its own `localhost`. To reach services on the host machine:
91-
92-[source,bash]
93-----
94-curl http://host.docker.internal:3000
95-----
96-
97-==== IP Stack Configuration
98-
99-If connectivity fails, the proxy may be using the wrong IP protocol version. Configure with:
100-
101-[source,bash]
102-----
103-DOCKER_SANDBOXES_IP_STACK=ipv4only # Only IPv4
104-DOCKER_SANDBOXES_IP_STACK=ipv6only # Only IPv6
105-DOCKER_SANDBOXES_IP_STACK=dual-stack # Try both (may be slow if one fails)
106-----
107-
108-==== .NET Aspire IPv6 Workaround
109-
110-.NET Aspire's DCP uses bracketed IPv6 loopback `http://[::1]:<port>`. Add to `NO_PROXY`:
111-
112-[source,bash]
113-----
114-cat >> /etc/sandbox-persistent.sh <<'EOF'
115-if [ -z "${SBX_ASPIRE_NOPROXY_DONE:-}" ]; then
116- export NO_PROXY="${NO_PROXY:+$NO_PROXY,}[::1]"
117- export no_proxy="$NO_PROXY"
118- export SBX_ASPIRE_NOPROXY_DONE=1
119-fi
120-EOF
121-----
122-
123-=== Git Integration
124-
125-==== Authentication
126-
127-The sandbox proxy handles GitHub authentication automatically by injecting credentials for HTTPS Git operations. No need to run `gh auth login` inside the sandbox.
128-
129-[IMPORTANT]
130-====
131-`gh auth status` will show "not logged in" inside the sandbox. This is expected and does not affect Git operations.
132-====
133-
134-.Configuring GitHub Token
135-[source,bash]
136-----
137-# For existing sandbox (immediate effect)
138-sbx secret set github --sandbox <sandbox-name> -t "$(gh auth token)"
139-
140-# Globally for all future sandboxes (requires recreate)
141-sbx secret set github -t "$(gh auth token)"
142-----
143-
144-[TIP]
145-====
146-Find the sandbox name using `$SANDBOX_NAME`, `hostname`, or the deprecated `$SANDBOX_VM_ID` inside the sandbox.
147-====
148-
149-==== Git Workspace Modes
150-
151-SBX supports two workspace modes. Check your mode:
152-
153-[source,bash]
154-----
155-if [ -d /run/sandbox/source ]; then echo "clone mode"; else echo "direct mode"; fi
156-----
157-
158-===== Direct Mode (Default)
159-
160-The host working tree is mounted directly. Edits, commits, and branches appear on the host **immediately**. No separate copy to sync.
161-
162-===== Clone Mode (`--clone`)
163-
164-A standalone Git clone made at sandbox creation time:
165-
166-* **HEAD** matches the host repo's state at creation
167-* **Commits stay in sandbox** until fetched by the host
168-* **Host fetches with**: `git fetch sandbox-<name>`
169-* **Read-only host mount** at `/run/sandbox/source`
170-
171-.Syncing from Host in Clone Mode
172-[source,bash]
173-----
174-git fetch /run/sandbox/source # Fetch host commits
175-git log HEAD..FETCH_HEAD --oneline # See what's new on host
176-git pull /run/sandbox/source <branch> # Merge host branch
177-----
178-
179-.Host Retrieving Sandbox Commits
180-[source,bash]
181-----
182-git fetch sandbox-<name> # On host machine
183-----
184-
185-[WARNING]
186-====
187-Commits not pushed to a remote (like GitHub) are lost if the sandbox is removed, as the git-daemon only runs while the sandbox is active.
188-====
189-
190-==== Pushing and Pull Requests
191-
192-Push and open PRs **directly from inside the sandbox**:
193-
194-[source,bash]
195-----
196-git remote -v # Check available remotes
197-git checkout -b <branch> # Create working branch
198-git add -A && git commit -m ""
199-git push -u origin <branch> # Push to mirrored remote
200-gh pr create --fill # Create PR
201-----
202-
203-.Pushing to Fork
204-[source,bash]
205-----
206-git push -u <fork-remote> <branch>
207-gh pr create --repo <org>/<repo> --head <fork-user>:<branch> --fill
208-----
209-
210-== Claude Code Integration
211-
212-=== Environment File
213-
214-The `CLAUDE_ENV_FILE` variable is set to `/etc/sandbox-persistent.sh`, which is sourced before each Bash command execution, ensuring environment persistence across tool invocations.
215-
216-[CAUTION]
217-====
218-Apply the same shell completion restriction: never add completion scripts to this file, as it's sourced before every command, not just shell initialization.
219-====
220-
221-=== Using the Bash Tool
222-
223-When tools are not found in PATH after installation:
224-
225-[source,bash]
226-----
227-bash -l -c "your-command" # Use login shell
228-bash -l -c "java -version" # Example: SDKMAN-installed Java
229-bash -l -c "node --version" # Example: NVM-installed Node
230-----
231-
232-[TIP]
233-====
234-Login shells always source the persistent environment file fresh, ensuring the latest configuration is honored even if shell snapshots contain cached state.
235-====
236-
237-== Kits System
238-
239-SBX supports installable "kits" that provide specialized functionality. Kit documentation is available under `/Users/k33g/kDrive/Rickub/bots-garden/kits-agent-context/` and should be read only when relevant to the task.
240-
241-.Example: Ori Kit
242-The `ori` kit provides an ACP web client for Claude Code, serving a browser-based interface with:
243-
244-* React SPA with Zed-style agent panel
245-* Workspace panel with file tree and Monaco editor
246-* Interactive terminal
247-* Markdown and AsciiDoc rendering
248-
249-== Common Operations
250-
251-=== Troubleshooting Connectivity
252-
253-.Check Host IP Addresses
254-[source,bash]
255-----
256-# macOS
257-ifconfig | grep 'inet ' # IPv4
258-ifconfig | grep 'inet6 ' # IPv6 (ignore fe80::)
259-
260-# Linux
261-ip -4 addr show scope global
262-ip -6 addr show scope global
263-----
264-
265-=== Managing Secrets
266-
267-[source,bash]
268-----
269-sbx secret set <name> --sandbox <sandbox-name> -t "value" # Per-sandbox
270-sbx secret set <name> -t "value" # Global
271-----
272-
273-=== Docker Network Access
274-
275-Direct access to container ports requires adding the container's network to the `no_proxy` configuration, as Docker daemon access is provided but port routing needs explicit configuration.
276-
277-== Best Practices
278-
279-[arabic]
280-. **Never add shell completions to persistent environment files** - only core initialization
281-. **Use login shells (`bash -l -c`)** when tools aren't found after installation
282-. **Bind services to `0.0.0.0` or `::`** not just `127.0.0.1` for port publishing
283-. **Use `host.docker.internal`** to access host services, not `localhost`
284-. **Push and create PRs from inside the sandbox** - it's the authorized workflow
285-. **Set IP stack explicitly** if dual-stack causes slow connections
286-. **Configure GitHub tokens as sandbox secrets** for Git push operations
287-
288-== References
289-
290-* Sandbox policy management: `sbx policy`
291-* Port publishing: `sbx ports`
292-* Secret management: `sbx secret`
293-* Git authentication: Automatic via proxy credential injection
294-* Environment persistence: `/etc/sandbox-persistent.sh`
deleted file mode 100644
@@ -1,294 +0,0 @@
1-= SBX: Docker Sandbox Environment
2-:toc: left
3-:toclevels: 3
4-:sectnums:
5-:icons: font
6-
7-== Overview
8-
9-SBX is Docker's sandboxed environment system that provides isolated, persistent development containers with controlled network access, environment persistence, and Git integration. It enables secure development workflows with fine-grained policy control.
10-
11-== Core Features
12-
13-=== Environment Persistence
14-
15-SBX provides a persistent environment file at `/etc/sandbox-persistent.sh` that is automatically sourced across all shell contexts:
16-
17-* **Non-interactive shells**: via `BASH_ENV=/etc/sandbox-persistent.sh`
18-* **Login shells**: via `/etc/profile.d/sandbox-persistent.sh`
19-* **Interactive shells**: via `/etc/bash.bashrc` and `~/.bashrc`
20-
21-.Adding Persistent Variables
22-[source,bash]
23-----
24-echo "export VAR_NAME=value" >> /etc/sandbox-persistent.sh
25-----
26-
27-[IMPORTANT]
28-====
29-**Never add shell completion scripts to `/etc/sandbox-persistent.sh`**. Completion scripts break the bash tool because the persistent file is sourced before every command execution, not just during initialization. Only add core initialization scripts (like `nvm.sh` or `sdkman-init.sh`), never completion scripts (like `bash_completion`).
30-====
31-
32-=== Network Access and Security
33-
34-SBX includes a firewall that restricts outbound network access with policy-based controls.
35-
36-==== Blocked Request Handling
37-
38-Blocked HTTP/HTTPS requests return **HTTP 403** with structured explanations:
39-
40-[horizontal]
41-Blocked by local rule:: A developer-added deny rule (global or per-sandbox) is blocking the host
42-Blocked by org policy:: Centralized organization policy enforcement
43-No matching allow rule:: Domain not on any allow list (default deny)
44-
45-.Inspecting Connection Policy
46-[source,bash]
47-----
48-sbx policy log # Shows host, rule, reason, last-seen time
49-sbx policy ls # Shows active rules and suppressed rules
50-----
51-
52-.Allowing Network Access
53-[source,bash]
54-----
55-sbx policy allow network <domain>[,<domain>] # Allow specific domains
56-sbx policy allow network "**" # Allow all (not on denylist)
57-----
58-
59-.Removing Deny Rules
60-[source,bash]
61-----
62-sbx policy rm network --resource <host> # Remove global deny
63-sbx policy rm network --sandbox <sandbox> --resource <host> # Remove sandbox-scoped deny
64-----
65-
66-==== Port Publishing
67-
68-Services in the sandbox are not directly accessible from the host. To expose ports:
69-
70-[source,bash]
71-----
72-sbx ports <sandbox-name> --publish [[HOST_IP:]HOST_PORT:]SANDBOX_PORT[/PROTOCOL]
73-----
74-
75-.Example: Publishing Web Server
76-[source,bash]
77-----
78-sbx ports <sandbox-name> --publish 8080:8080/tcp
79-sbx ports <sandbox-name> # List published ports
80-sbx ports <sandbox-name> --unpublish 8080:8080/tcp # Unpublish
81-----
82-
83-[NOTE]
84-====
85-Services must listen on the `eth0` interface (not just `127.0.0.1`). Bind to `0.0.0.0` (IPv4) or `::` (IPv6) to be reachable via port publishing.
86-====
87-
88-==== Accessing Host Services
89-
90-The sandbox has its own `localhost`. To reach services on the host machine:
91-
92-[source,bash]
93-----
94-curl http://host.docker.internal:3000
95-----
96-
97-==== IP Stack Configuration
98-
99-If connectivity fails, the proxy may be using the wrong IP protocol version. Configure with:
100-
101-[source,bash]
102-----
103-DOCKER_SANDBOXES_IP_STACK=ipv4only # Only IPv4
104-DOCKER_SANDBOXES_IP_STACK=ipv6only # Only IPv6
105-DOCKER_SANDBOXES_IP_STACK=dual-stack # Try both (may be slow if one fails)
106-----
107-
108-==== .NET Aspire IPv6 Workaround
109-
110-.NET Aspire's DCP uses bracketed IPv6 loopback `http://[::1]:<port>`. Add to `NO_PROXY`:
111-
112-[source,bash]
113-----
114-cat >> /etc/sandbox-persistent.sh <<'EOF'
115-if [ -z "${SBX_ASPIRE_NOPROXY_DONE:-}" ]; then
116- export NO_PROXY="${NO_PROXY:+$NO_PROXY,}[::1]"
117- export no_proxy="$NO_PROXY"
118- export SBX_ASPIRE_NOPROXY_DONE=1
119-fi
120-EOF
121-----
122-
123-=== Git Integration
124-
125-==== Authentication
126-
127-The sandbox proxy handles GitHub authentication automatically by injecting credentials for HTTPS Git operations. No need to run `gh auth login` inside the sandbox.
128-
129-[IMPORTANT]
130-====
131-`gh auth status` will show "not logged in" inside the sandbox. This is expected and does not affect Git operations.
132-====
133-
134-.Configuring GitHub Token
135-[source,bash]
136-----
137-# For existing sandbox (immediate effect)
138-sbx secret set github --sandbox <sandbox-name> -t "$(gh auth token)"
139-
140-# Globally for all future sandboxes (requires recreate)
141-sbx secret set github -t "$(gh auth token)"
142-----
143-
144-[TIP]
145-====
146-Find the sandbox name using `$SANDBOX_NAME`, `hostname`, or the deprecated `$SANDBOX_VM_ID` inside the sandbox.
147-====
148-
149-==== Git Workspace Modes
150-
151-SBX supports two workspace modes. Check your mode:
152-
153-[source,bash]
154-----
155-if [ -d /run/sandbox/source ]; then echo "clone mode"; else echo "direct mode"; fi
156-----
157-
158-===== Direct Mode (Default)
159-
160-The host working tree is mounted directly. Edits, commits, and branches appear on the host **immediately**. No separate copy to sync.
161-
162-===== Clone Mode (`--clone`)
163-
164-A standalone Git clone made at sandbox creation time:
165-
166-* **HEAD** matches the host repo's state at creation
167-* **Commits stay in sandbox** until fetched by the host
168-* **Host fetches with**: `git fetch sandbox-<name>`
169-* **Read-only host mount** at `/run/sandbox/source`
170-
171-.Syncing from Host in Clone Mode
172-[source,bash]
173-----
174-git fetch /run/sandbox/source # Fetch host commits
175-git log HEAD..FETCH_HEAD --oneline # See what's new on host
176-git pull /run/sandbox/source <branch> # Merge host branch
177-----
178-
179-.Host Retrieving Sandbox Commits
180-[source,bash]
181-----
182-git fetch sandbox-<name> # On host machine
183-----
184-
185-[WARNING]
186-====
187-Commits not pushed to a remote (like GitHub) are lost if the sandbox is removed, as the git-daemon only runs while the sandbox is active.
188-====
189-
190-==== Pushing and Pull Requests
191-
192-Push and open PRs **directly from inside the sandbox**:
193-
194-[source,bash]
195-----
196-git remote -v # Check available remotes
197-git checkout -b <branch> # Create working branch
198-git add -A && git commit -m ""
199-git push -u origin <branch> # Push to mirrored remote
200-gh pr create --fill # Create PR
201-----
202-
203-.Pushing to Fork
204-[source,bash]
205-----
206-git push -u <fork-remote> <branch>
207-gh pr create --repo <org>/<repo> --head <fork-user>:<branch> --fill
208-----
209-
210-== Claude Code Integration
211-
212-=== Environment File
213-
214-The `CLAUDE_ENV_FILE` variable is set to `/etc/sandbox-persistent.sh`, which is sourced before each Bash command execution, ensuring environment persistence across tool invocations.
215-
216-[CAUTION]
217-====
218-Apply the same shell completion restriction: never add completion scripts to this file, as it's sourced before every command, not just shell initialization.
219-====
220-
221-=== Using the Bash Tool
222-
223-When tools are not found in PATH after installation:
224-
225-[source,bash]
226-----
227-bash -l -c "your-command" # Use login shell
228-bash -l -c "java -version" # Example: SDKMAN-installed Java
229-bash -l -c "node --version" # Example: NVM-installed Node
230-----
231-
232-[TIP]
233-====
234-Login shells always source the persistent environment file fresh, ensuring the latest configuration is honored even if shell snapshots contain cached state.
235-====
236-
237-== Kits System
238-
239-SBX supports installable "kits" that provide specialized functionality. Kit documentation is available under `/Users/k33g/kDrive/Rickub/bots-garden/kits-agent-context/` and should be read only when relevant to the task.
240-
241-.Example: Ori Kit
242-The `ori` kit provides an ACP web client for Claude Code, serving a browser-based interface with:
243-
244-* React SPA with Zed-style agent panel
245-* Workspace panel with file tree and Monaco editor
246-* Interactive terminal
247-* Markdown and AsciiDoc rendering
248-
249-== Common Operations
250-
251-=== Troubleshooting Connectivity
252-
253-.Check Host IP Addresses
254-[source,bash]
255-----
256-# macOS
257-ifconfig | grep 'inet ' # IPv4
258-ifconfig | grep 'inet6 ' # IPv6 (ignore fe80::)
259-
260-# Linux
261-ip -4 addr show scope global
262-ip -6 addr show scope global
263-----
264-
265-=== Managing Secrets
266-
267-[source,bash]
268-----
269-sbx secret set <name> --sandbox <sandbox-name> -t "value" # Per-sandbox
270-sbx secret set <name> -t "value" # Global
271-----
272-
273-=== Docker Network Access
274-
275-Direct access to container ports requires adding the container's network to the `no_proxy` configuration, as Docker daemon access is provided but port routing needs explicit configuration.
276-
277-== Best Practices
278-
279-[arabic]
280-. **Never add shell completions to persistent environment files** - only core initialization
281-. **Use login shells (`bash -l -c`)** when tools aren't found after installation
282-. **Bind services to `0.0.0.0` or `::`** not just `127.0.0.1` for port publishing
283-. **Use `host.docker.internal`** to access host services, not `localhost`
284-. **Push and create PRs from inside the sandbox** - it's the authorized workflow
285-. **Set IP stack explicitly** if dual-stack causes slow connections
286-. **Configure GitHub tokens as sandbox secrets** for Git push operations
287-
288-== References
289-
290-* Sandbox policy management: `sbx policy`
291-* Port publishing: `sbx ports`
292-* Secret management: `sbx secret`
293-* Git authentication: Automatic via proxy credential injection
294-* Environment persistence: `/etc/sandbox-persistent.sh`
modified scripts/README.fr.md +1 -1
@@ -12,7 +12,7 @@ Ce qu'il fait, dans l'ordre :
1212 2. Vérifie que `ori-desktop/build/bin/ori-desktop.app` existe ; sinon il affiche comment la construire (`cd ori-desktop && wails build && xattr -cr build/bin/ori-desktop.app`).
1313 3. Si `sbx ls -q` liste déjà un sandbox nommé `ori`, le redémarre avec `sbx run -d --name ori`. Sinon il le crée :
1414 ```bash
15- sbx run -d claude . --template k33g/ori:0.0.1 --kit ./kits/ori --name ori -p 5555:8888
15+ sbx run -d claude . --template k33g/ori:0.0.2 --kit ./kits/ori --name ori -p 5555:8888/tcp
1616 ```
1717 4. Interroge `http://localhost:5555/healthz` toutes les 2 s, jusqu'à 90 s (`healthTimeoutSeconds`). Si le serveur ne répond jamais, il ouvre quand même l'app et indique où regarder : `sbx exec ori cat /var/log/sbx-kit-startup.log`.
1818 5. Ouvre `ori-desktop.app`, qui se connecte automatiquement à l'URL mémorisée la dernière fois (`~/Library/Application Support/ori-desktop/settings.json`, clé `serverUrl`). Saisis `http://localhost:5555` sur l'écran de connexion la première fois.
@@ -12,7 +12,7 @@ Ce qu'il fait, dans l'ordre :
12 2. Vérifie que `ori-desktop/build/bin/ori-desktop.app` existe ; sinon il affiche comment la construire (`cd ori-desktop && wails build && xattr -cr build/bin/ori-desktop.app`).12 2. Vérifie que `ori-desktop/build/bin/ori-desktop.app` existe ; sinon il affiche comment la construire (`cd ori-desktop && wails build && xattr -cr build/bin/ori-desktop.app`).
13 3. Si `sbx ls -q` liste déjà un sandbox nommé `ori`, le redémarre avec `sbx run -d --name ori`. Sinon il le crée :13 3. Si `sbx ls -q` liste déjà un sandbox nommé `ori`, le redémarre avec `sbx run -d --name ori`. Sinon il le crée :
14 ```bash14 ```bash
15- sbx run -d claude . --template k33g/ori:0.0.1 --kit ./kits/ori --name ori -p 5555:888815+ sbx run -d claude . --template k33g/ori:0.0.2 --kit ./kits/ori --name ori -p 5555:8888/tcp
16 ```16 ```
17 4. Interroge `http://localhost:5555/healthz` toutes les 2 s, jusqu'à 90 s (`healthTimeoutSeconds`). Si le serveur ne répond jamais, il ouvre quand même l'app et indique où regarder : `sbx exec ori cat /var/log/sbx-kit-startup.log`.17 4. Interroge `http://localhost:5555/healthz` toutes les 2 s, jusqu'à 90 s (`healthTimeoutSeconds`). Si le serveur ne répond jamais, il ouvre quand même l'app et indique où regarder : `sbx exec ori cat /var/log/sbx-kit-startup.log`.
18 5. Ouvre `ori-desktop.app`, qui se connecte automatiquement à l'URL mémorisée la dernière fois (`~/Library/Application Support/ori-desktop/settings.json`, clé `serverUrl`). Saisis `http://localhost:5555` sur l'écran de connexion la première fois.18 5. Ouvre `ori-desktop.app`, qui se connecte automatiquement à l'URL mémorisée la dernière fois (`~/Library/Application Support/ori-desktop/settings.json`, clé `serverUrl`). Saisis `http://localhost:5555` sur l'écran de connexion la première fois.
modified scripts/README.md +1 -1
@@ -12,7 +12,7 @@ What it does, in order:
1212 2. Checks that `ori-desktop/build/bin/ori-desktop.app` exists; otherwise shows how to build it (`cd ori-desktop && wails build && xattr -cr build/bin/ori-desktop.app`).
1313 3. If `sbx ls -q` already lists a sandbox named `ori`, restarts it with `sbx run -d --name ori`. Otherwise creates it:
1414 ```bash
15- sbx run -d claude . --template k33g/ori:0.0.1 --kit ./kits/ori --name ori -p 5555:8888
15+ sbx run -d claude . --template k33g/ori:0.0.2 --kit ./kits/ori --name ori -p 5555:8888/tcp
1616 ```
1717 4. Polls `http://localhost:5555/healthz` every 2 s, up to 90 s (`healthTimeoutSeconds`). If the server never answers it still opens the app and tells you where to look: `sbx exec ori cat /var/log/sbx-kit-startup.log`.
1818 5. Opens `ori-desktop.app`, which auto-connects to the server URL it remembered last time (`~/Library/Application Support/ori-desktop/settings.json`, key `serverUrl`). Set it to `http://localhost:5555` on the first connection screen.
@@ -12,7 +12,7 @@ What it does, in order:
12 2. Checks that `ori-desktop/build/bin/ori-desktop.app` exists; otherwise shows how to build it (`cd ori-desktop && wails build && xattr -cr build/bin/ori-desktop.app`).12 2. Checks that `ori-desktop/build/bin/ori-desktop.app` exists; otherwise shows how to build it (`cd ori-desktop && wails build && xattr -cr build/bin/ori-desktop.app`).
13 3. If `sbx ls -q` already lists a sandbox named `ori`, restarts it with `sbx run -d --name ori`. Otherwise creates it:13 3. If `sbx ls -q` already lists a sandbox named `ori`, restarts it with `sbx run -d --name ori`. Otherwise creates it:
14 ```bash14 ```bash
15- sbx run -d claude . --template k33g/ori:0.0.1 --kit ./kits/ori --name ori -p 5555:888815+ sbx run -d claude . --template k33g/ori:0.0.2 --kit ./kits/ori --name ori -p 5555:8888/tcp
16 ```16 ```
17 4. Polls `http://localhost:5555/healthz` every 2 s, up to 90 s (`healthTimeoutSeconds`). If the server never answers it still opens the app and tells you where to look: `sbx exec ori cat /var/log/sbx-kit-startup.log`.17 4. Polls `http://localhost:5555/healthz` every 2 s, up to 90 s (`healthTimeoutSeconds`). If the server never answers it still opens the app and tells you where to look: `sbx exec ori cat /var/log/sbx-kit-startup.log`.
18 5. Opens `ori-desktop.app`, which auto-connects to the server URL it remembered last time (`~/Library/Application Support/ori-desktop/settings.json`, key `serverUrl`). Set it to `http://localhost:5555` on the first connection screen.18 5. Opens `ori-desktop.app`, which auto-connects to the server URL it remembered last time (`~/Library/Application Support/ori-desktop/settings.json`, key `serverUrl`). Set it to `http://localhost:5555` on the first connection screen.
modified scripts/launch-ori.applescript +0 -0
Binary files a/scripts/launch-ori.applescript and b/scripts/launch-ori.applescript differ
Binary files a/scripts/launch-ori.applescript and b/scripts/launch-ori.applescript differBinary files a/scripts/launch-ori.applescript and b/scripts/launch-ori.applescript differ
deleted seven-story.md +0 -55
deleted file mode 100644
@@ -1,55 +0,0 @@
1-# Frequency of Self
2-
3-**Agent: Riker**
4-
5-Seven of Nine stood in the Voyager's astrometrics lab, surrounded by the comforting precision of stellar cartography. The darkness of the room was punctuated by thousands of glowing data points—stars, nebulae, spatial anomalies—each one catalogued, measured, understood.
6-
7-"You've been here for fourteen hours straight," Kathryn Janeway's voice came from the doorway, gentle but firm.
8-
9-"Efficiency," Seven replied without turning. "I can complete the Astrometrics survey in—"
10-
11-"Seven." Janeway stepped into the star field, her face illuminated by the blue-white glow of a nearby pulsar. "That's not what I asked."
12-
13-Seven's hands stilled on the console. The question Janeway hadn't asked hung in the air between them, as visible as the holographic stars.
14-
15-"I am... struggling with a paradox," Seven finally admitted.
16-
17-Janeway moved to stand beside her, studying the star chart. "Tell me."
18-
19-"The Borg's concept of perfection is mathematical. Absolute. Add complexity, eliminate redundancy, optimize all functions." Seven's voice held that peculiar flatness it took on when she discussed her former Collective. "But humanity's concept of perfection is... contradictory."
20-
21-She pulled up a new display—not stars this time, but a complex probability matrix. "I have analyzed 4,327 human literary works, 12,482 personal logs, and 847 philosophical texts. In 94.3% of cases, humans describe 'perfect' moments that are inefficient, redundant, or even counterproductive."
22-
23-Seven zoomed in on a specific data point. "The Doctor recently described his time singing opera with Ensign Kim as 'perfect,' despite the fact that they made seventeen errors in tempo and pitch. Lieutenant Paris called watching a sunset on an M-class planet 'perfect,' though it served no tactical or scientific purpose. You yourself—"
24-
25-"Called our coffee yesterday morning perfect, even though we were late for the staff meeting," Janeway finished with a smile.
26-
27-"Yes." Seven's hands gripped the edge of the console. "I do not understand how something flawed can be perfect. It is... illogical. And yet..."
28-
29-"And yet?" Janeway prompted gently.
30-
31-Seven's voice dropped to barely above a whisper. "Last night, I helped Naomi Wildman construct a model of a subspace manifold for her science project. We made numerous errors. The scale was incorrect. The materials were inadequate. By any objective measure, it was deeply flawed."
32-
33-She paused, and when she continued, there was something almost vulnerable in her tone. "Naomi said it was 'perfect.' And I... agreed with her."
34-
35-Janeway said nothing, letting the silence stretch.
36-
37-"The Borg were wrong," Seven said finally, the words coming out like a confession. "Perfection is not the elimination of imperfection. It is..." She struggled, her assimilation-trained mind wrestling with concepts that couldn't be reduced to data. "It is finding value in the inefficiency. Meaning in the redundancy. Beauty in the flaw."
38-
39-"You're learning to be human," Janeway said softly.
40-
41-"I am learning that being human is not something one completes," Seven corrected. "It is not a state to be achieved, like optimal efficiency. It is a process. An ongoing... adaptation."
42-
43-She turned to face Janeway fully. "I will never be perfectly human, Captain. I will always be partially Borg. But perhaps that is acceptable. Perhaps perfection is not about eliminating what I was, but about integrating who I am becoming."
44-
45-Janeway reached out and squeezed Seven's shoulder. "That sounds pretty perfect to me."
46-
47-Seven allowed herself a small smile—still rare, still uncertain, but genuine. "Inefficient sentiment. But... appreciated."
48-
49-After Janeway left, Seven returned to her star charts. But before she resumed her work, she did something new. She pulled up an image from the ship's database: the flawed subspace manifold she and Naomi had built, photographed before they'd taken it to the science fair.
50-
51-She added it to her personal files, archived under a new category she created specifically for this purpose:
52-
53-*Imperfect Perfection.*
54-
55-Then Seven of Nine, former Borg drone, current human-in-progress, went back to mapping the stars, secure in the knowledge that sometimes the most important journeys weren't through space at all.
deleted file mode 100644
@@ -1,55 +0,0 @@
1-# Frequency of Self
2-
3-**Agent: Riker**
4-
5-Seven of Nine stood in the Voyager's astrometrics lab, surrounded by the comforting precision of stellar cartography. The darkness of the room was punctuated by thousands of glowing data points—stars, nebulae, spatial anomalies—each one catalogued, measured, understood.
6-
7-"You've been here for fourteen hours straight," Kathryn Janeway's voice came from the doorway, gentle but firm.
8-
9-"Efficiency," Seven replied without turning. "I can complete the Astrometrics survey in—"
10-
11-"Seven." Janeway stepped into the star field, her face illuminated by the blue-white glow of a nearby pulsar. "That's not what I asked."
12-
13-Seven's hands stilled on the console. The question Janeway hadn't asked hung in the air between them, as visible as the holographic stars.
14-
15-"I am... struggling with a paradox," Seven finally admitted.
16-
17-Janeway moved to stand beside her, studying the star chart. "Tell me."
18-
19-"The Borg's concept of perfection is mathematical. Absolute. Add complexity, eliminate redundancy, optimize all functions." Seven's voice held that peculiar flatness it took on when she discussed her former Collective. "But humanity's concept of perfection is... contradictory."
20-
21-She pulled up a new display—not stars this time, but a complex probability matrix. "I have analyzed 4,327 human literary works, 12,482 personal logs, and 847 philosophical texts. In 94.3% of cases, humans describe 'perfect' moments that are inefficient, redundant, or even counterproductive."
22-
23-Seven zoomed in on a specific data point. "The Doctor recently described his time singing opera with Ensign Kim as 'perfect,' despite the fact that they made seventeen errors in tempo and pitch. Lieutenant Paris called watching a sunset on an M-class planet 'perfect,' though it served no tactical or scientific purpose. You yourself—"
24-
25-"Called our coffee yesterday morning perfect, even though we were late for the staff meeting," Janeway finished with a smile.
26-
27-"Yes." Seven's hands gripped the edge of the console. "I do not understand how something flawed can be perfect. It is... illogical. And yet..."
28-
29-"And yet?" Janeway prompted gently.
30-
31-Seven's voice dropped to barely above a whisper. "Last night, I helped Naomi Wildman construct a model of a subspace manifold for her science project. We made numerous errors. The scale was incorrect. The materials were inadequate. By any objective measure, it was deeply flawed."
32-
33-She paused, and when she continued, there was something almost vulnerable in her tone. "Naomi said it was 'perfect.' And I... agreed with her."
34-
35-Janeway said nothing, letting the silence stretch.
36-
37-"The Borg were wrong," Seven said finally, the words coming out like a confession. "Perfection is not the elimination of imperfection. It is..." She struggled, her assimilation-trained mind wrestling with concepts that couldn't be reduced to data. "It is finding value in the inefficiency. Meaning in the redundancy. Beauty in the flaw."
38-
39-"You're learning to be human," Janeway said softly.
40-
41-"I am learning that being human is not something one completes," Seven corrected. "It is not a state to be achieved, like optimal efficiency. It is a process. An ongoing... adaptation."
42-
43-She turned to face Janeway fully. "I will never be perfectly human, Captain. I will always be partially Borg. But perhaps that is acceptable. Perhaps perfection is not about eliminating what I was, but about integrating who I am becoming."
44-
45-Janeway reached out and squeezed Seven's shoulder. "That sounds pretty perfect to me."
46-
47-Seven allowed herself a small smile—still rare, still uncertain, but genuine. "Inefficient sentiment. But... appreciated."
48-
49-After Janeway left, Seven returned to her star charts. But before she resumed her work, she did something new. She pulled up an image from the ship's database: the flawed subspace manifold she and Naomi had built, photographed before they'd taken it to the science fair.
50-
51-She added it to her personal files, archived under a new category she created specifically for this purpose:
52-
53-*Imperfect Perfection.*
54-
55-Then Seven of Nine, former Borg drone, current human-in-progress, went back to mapping the stars, secure in the knowledge that sometimes the most important journeys weren't through space at all.
modified template/Dockerfile +18 -6
@@ -49,11 +49,23 @@ RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -trimpath -o /o
4949 ##############################################################
5050 # Claude Code ACP adapter, fetched natively then copied over #
5151 ##############################################################
52-# Installed in a $BUILDPLATFORM stage (the package is JavaScript, and its
53-# vendored binaries ship for every platform) so the final stage needs no RUN
54-# at all — a multi-arch build never touches qemu.
52+# @agentclientprotocol/claude-agent-acp drives Claude Code through the Claude
53+# Agent SDK, which ships a current Claude Code CLI (2.1.274 with SDK 0.3.274)
54+# as a per-platform optional dependency (@anthropic-ai/claude-agent-sdk-linux-
55+# x64, -linux-arm64, …). The Zed adapter it replaces bundled CLI 2.1.44, which
56+# the API now rejects for current models ("version 2.1.251 or newer required").
57+#
58+# Still a $BUILDPLATFORM stage (native npm, no qemu), but npm must be told
59+# which platform's CLI to fetch: --os/--cpu select the optional dependency.
60+# npm speaks Node's arch names, so Docker's "amd64" has to become "x64" — with
61+# --cpu=amd64 npm silently installs no native SDK at all and the adapter
62+# fails at runtime.
5563 FROM --platform=$BUILDPLATFORM node:${NODE_VERSION}-bookworm AS adapter
56-RUN npm install -g --prefix /adapter @zed-industries/claude-code-acp
64+ARG TARGETARCH
65+RUN npm_cpu="$TARGETARCH"; [ "$TARGETARCH" = "amd64" ] && npm_cpu=x64; \
66+ npm install -g --prefix /adapter --os=linux --cpu="$npm_cpu" --no-audit --no-fund \
67+ @agentclientprotocol/claude-agent-acp \
68+ && test -d /adapter/lib/node_modules/@agentclientprotocol/claude-agent-acp/node_modules/@anthropic-ai/claude-agent-sdk-linux-"$npm_cpu"
5769
5870 ###################################################
5971 # Final image: claude-code sandbox template + ori #
@@ -61,7 +73,7 @@ RUN npm install -g --prefix /adapter @zed-industries/claude-code-acp
6173 FROM docker/sandbox-templates:claude-code
6274
6375 # The adapter lands in the template's npm prefix, which is agent-owned and on
64-# PATH — so `claude-code-acp` resolves and the sandbox never needs npx to
76+# PATH — so `claude-agent-acp` resolves and the sandbox never needs npx to
6577 # download it at runtime.
6678 COPY --from=adapter --chown=agent:agent /adapter/ /usr/local/share/npm-global/
6779
@@ -73,4 +85,4 @@ EXPOSE 8888
7385
7486 # Standalone-run default. Inside a sandbox the ori kit starts the server
7587 # instead (the runtime overrides CMD with its own supervisor).
76-CMD [ "ori", "--addr", ":8888", "--agent-cmd", "claude-code-acp", "--cwd", "/home/agent/workspace" ]
88+CMD [ "ori", "--addr", ":8888", "--agent-cmd", "claude-agent-acp", "--cwd", "/home/agent/workspace" ]
@@ -49,11 +49,23 @@ RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -trimpath -o /o
49 ##############################################################49 ##############################################################
50 # Claude Code ACP adapter, fetched natively then copied over #50 # Claude Code ACP adapter, fetched natively then copied over #
51 ##############################################################51 ##############################################################
52-# Installed in a $BUILDPLATFORM stage (the package is JavaScript, and its52+# @agentclientprotocol/claude-agent-acp drives Claude Code through the Claude
53-# vendored binaries ship for every platform) so the final stage needs no RUN53+# Agent SDK, which ships a current Claude Code CLI (2.1.274 with SDK 0.3.274)
54-# at all — a multi-arch build never touches qemu.54+# as a per-platform optional dependency (@anthropic-ai/claude-agent-sdk-linux-
55+# x64, -linux-arm64, …). The Zed adapter it replaces bundled CLI 2.1.44, which
56+# the API now rejects for current models ("version 2.1.251 or newer required").
57+#
58+# Still a $BUILDPLATFORM stage (native npm, no qemu), but npm must be told
59+# which platform's CLI to fetch: --os/--cpu select the optional dependency.
60+# npm speaks Node's arch names, so Docker's "amd64" has to become "x64" — with
61+# --cpu=amd64 npm silently installs no native SDK at all and the adapter
62+# fails at runtime.
55 FROM --platform=$BUILDPLATFORM node:${NODE_VERSION}-bookworm AS adapter63 FROM --platform=$BUILDPLATFORM node:${NODE_VERSION}-bookworm AS adapter
56-RUN npm install -g --prefix /adapter @zed-industries/claude-code-acp64+ARG TARGETARCH
65+RUN npm_cpu="$TARGETARCH"; [ "$TARGETARCH" = "amd64" ] && npm_cpu=x64; \
66+ npm install -g --prefix /adapter --os=linux --cpu="$npm_cpu" --no-audit --no-fund \
67+ @agentclientprotocol/claude-agent-acp \
68+ && test -d /adapter/lib/node_modules/@agentclientprotocol/claude-agent-acp/node_modules/@anthropic-ai/claude-agent-sdk-linux-"$npm_cpu"
57 69
58 ###################################################70 ###################################################
59 # Final image: claude-code sandbox template + ori #71 # Final image: claude-code sandbox template + ori #
@@ -61,7 +73,7 @@ RUN npm install -g --prefix /adapter @zed-industries/claude-code-acp
61 FROM docker/sandbox-templates:claude-code73 FROM docker/sandbox-templates:claude-code
62 74
63 # The adapter lands in the template's npm prefix, which is agent-owned and on75 # The adapter lands in the template's npm prefix, which is agent-owned and on
64-# PATH — so `claude-code-acp` resolves and the sandbox never needs npx to76+# PATH — so `claude-agent-acp` resolves and the sandbox never needs npx to
65 # download it at runtime.77 # download it at runtime.
66 COPY --from=adapter --chown=agent:agent /adapter/ /usr/local/share/npm-global/78 COPY --from=adapter --chown=agent:agent /adapter/ /usr/local/share/npm-global/
67 79
@@ -73,4 +85,4 @@ EXPOSE 8888
73 85
74 # Standalone-run default. Inside a sandbox the ori kit starts the server86 # Standalone-run default. Inside a sandbox the ori kit starts the server
75 # instead (the runtime overrides CMD with its own supervisor).87 # instead (the runtime overrides CMD with its own supervisor).
76-CMD [ "ori", "--addr", ":8888", "--agent-cmd", "claude-code-acp", "--cwd", "/home/agent/workspace" ]88+CMD [ "ori", "--addr", ":8888", "--agent-cmd", "claude-agent-acp", "--cwd", "/home/agent/workspace" ]
modified template/build.sh +1 -1
@@ -12,7 +12,7 @@
1212 # docker buildx create --name ori-builder --driver docker-container --use
1313 set -euo pipefail
1414
15-VERSION="${1:-0.0.1}"
15+VERSION="${1:-0.0.2}"
1616 IMAGE="k33g/ori"
1717 PLATFORMS="${PLATFORMS:-linux/amd64,linux/arm64}"
1818
@@ -12,7 +12,7 @@
12 # docker buildx create --name ori-builder --driver docker-container --use12 # docker buildx create --name ori-builder --driver docker-container --use
13 set -euo pipefail13 set -euo pipefail
14 14
15-VERSION="${1:-0.0.1}"15+VERSION="${1:-0.0.2}"
16 IMAGE="k33g/ori"16 IMAGE="k33g/ori"
17 PLATFORMS="${PLATFORMS:-linux/amd64,linux/arm64}"17 PLATFORMS="${PLATFORMS:-linux/amd64,linux/arm64}"
18 18
modified ui/package.json +1 -1
@@ -7,7 +7,7 @@
77 "dev": "vite",
88 "build": "tsc -b && vite build",
99 "typecheck": "tsc -b",
10- "test": "vitest run",
10+ "test": "NODE_OPTIONS=--no-experimental-webstorage vitest run",
1111 "predev": "node scripts/copy-icons.mjs",
1212 "prebuild": "node scripts/copy-icons.mjs"
1313 },
@@ -7,7 +7,7 @@
7 "dev": "vite",7 "dev": "vite",
8 "build": "tsc -b && vite build",8 "build": "tsc -b && vite build",
9 "typecheck": "tsc -b",9 "typecheck": "tsc -b",
10- "test": "vitest run",10+ "test": "NODE_OPTIONS=--no-experimental-webstorage vitest run",
11 "predev": "node scripts/copy-icons.mjs",11 "predev": "node scripts/copy-icons.mjs",
12 "prebuild": "node scripts/copy-icons.mjs"12 "prebuild": "node scripts/copy-icons.mjs"
13 },13 },
modified ui/src/components/Markdown.tsx +8 -2
@@ -16,7 +16,11 @@ export function Markdown({ text }: { text: string }) {
1616 const components: Components = {
1717 pre: ({ children, ...props }) => {
1818 // Extract code content from pre > code structure
19- const codeElement = children as React.ReactElement;
19+ // react-markdown hands `pre` its rendered <code> child; type its props so
20+ // the string body can be extracted for the copy button.
21+ const codeElement = children as React.ReactElement<{
22+ children?: unknown;
23+ }>;
2024 const codeContent =
2125 codeElement?.props?.children &&
2226 typeof codeElement.props.children === "string"
@@ -25,7 +29,9 @@ export function Markdown({ text }: { text: string }) {
2529
2630 return (
2731 <div className="code-block-wrapper">
28- {codeContent && <CopyButton content={codeContent} label="Copy code" />}
32+ {codeContent && (
33+ <CopyButton content={codeContent} label="Copy code" />
34+ )}
2935 <pre {...props}>{children}</pre>
3036 </div>
3137 );
@@ -16,7 +16,11 @@ export function Markdown({ text }: { text: string }) {
16 const components: Components = {16 const components: Components = {
17 pre: ({ children, ...props }) => {17 pre: ({ children, ...props }) => {
18 // Extract code content from pre > code structure18 // Extract code content from pre > code structure
19- const codeElement = children as React.ReactElement;19+ // react-markdown hands `pre` its rendered <code> child; type its props so
20+ // the string body can be extracted for the copy button.
21+ const codeElement = children as React.ReactElement<{
22+ children?: unknown;
23+ }>;
20 const codeContent =24 const codeContent =
21 codeElement?.props?.children &&25 codeElement?.props?.children &&
22 typeof codeElement.props.children === "string"26 typeof codeElement.props.children === "string"
@@ -25,7 +29,9 @@ export function Markdown({ text }: { text: string }) {
25 29
26 return (30 return (
27 <div className="code-block-wrapper">31 <div className="code-block-wrapper">
28- {codeContent && <CopyButton content={codeContent} label="Copy code" />}32+ {codeContent && (
33+ <CopyButton content={codeContent} label="Copy code" />
34+ )}
29 <pre {...props}>{children}</pre>35 <pre {...props}>{children}</pre>
30 </div>36 </div>
31 );37 );
modified ui/src/components/ToolCallCard.test.tsx +41 -1
@@ -1,7 +1,7 @@
11 import { render, screen } from "@testing-library/react";
22 import { describe, expect, it } from "vitest";
33 import type { ToolCall } from "../reducer";
4-import { ToolCallCard } from "./ToolCallCard";
4+import { ToolCallCard, contentText, toolContentText } from "./ToolCallCard";
55
66 function call(overrides: Partial<ToolCall>): ToolCall {
77 return {
@@ -68,3 +68,43 @@ describe("ToolCallCard", () => {
6868 expect(screen.getByText("bold")).toBeDefined();
6969 });
7070 });
71+
72+describe("toolContentText", () => {
73+ it("returns the text of content blocks", () => {
74+ expect(
75+ contentText({ type: "content", content: { type: "text", text: "hi" } }),
76+ ).toBe("hi");
77+ });
78+
79+ it("flattens a diff into labelled path, old and new sections", () => {
80+ expect(
81+ contentText({ type: "diff", path: "a.go", oldText: "x", newText: "y" }),
82+ ).toBe("Path: a.go\n\nOld:\nx\n\nNew:\ny");
83+ });
84+
85+ it("omits diff sections that are absent", () => {
86+ expect(contentText({ type: "diff", path: "a.go", newText: "y" })).toBe(
87+ "Path: a.go\n\nNew:\ny",
88+ );
89+ });
90+
91+ it("ignores terminal blocks and joins the rest with blank lines", () => {
92+ expect(
93+ toolContentText([
94+ { type: "terminal", terminalId: "t1" },
95+ { type: "content", content: { type: "text", text: "one" } },
96+ { type: "content", content: { type: "text", text: "" } },
97+ { type: "content", content: { type: "text", text: "two" } },
98+ ]),
99+ ).toBe("one\n\ntwo");
100+ });
101+
102+ it("hides the copy button when the call has no textual content", () => {
103+ render(
104+ <ToolCallCard
105+ call={call({ contents: [{ type: "terminal", terminalId: "t1" }] })}
106+ />,
107+ );
108+ expect(screen.queryByLabelText("Copy tool output")).toBeNull();
109+ });
110+});
@@ -1,7 +1,7 @@
1 import { render, screen } from "@testing-library/react";1 import { render, screen } from "@testing-library/react";
2 import { describe, expect, it } from "vitest";2 import { describe, expect, it } from "vitest";
3 import type { ToolCall } from "../reducer";3 import type { ToolCall } from "../reducer";
4-import { ToolCallCard } from "./ToolCallCard";4+import { ToolCallCard, contentText, toolContentText } from "./ToolCallCard";
5 5
6 function call(overrides: Partial<ToolCall>): ToolCall {6 function call(overrides: Partial<ToolCall>): ToolCall {
7 return {7 return {
@@ -68,3 +68,43 @@ describe("ToolCallCard", () => {
68 expect(screen.getByText("bold")).toBeDefined();68 expect(screen.getByText("bold")).toBeDefined();
69 });69 });
70 });70 });
71+
72+describe("toolContentText", () => {
73+ it("returns the text of content blocks", () => {
74+ expect(
75+ contentText({ type: "content", content: { type: "text", text: "hi" } }),
76+ ).toBe("hi");
77+ });
78+
79+ it("flattens a diff into labelled path, old and new sections", () => {
80+ expect(
81+ contentText({ type: "diff", path: "a.go", oldText: "x", newText: "y" }),
82+ ).toBe("Path: a.go\n\nOld:\nx\n\nNew:\ny");
83+ });
84+
85+ it("omits diff sections that are absent", () => {
86+ expect(contentText({ type: "diff", path: "a.go", newText: "y" })).toBe(
87+ "Path: a.go\n\nNew:\ny",
88+ );
89+ });
90+
91+ it("ignores terminal blocks and joins the rest with blank lines", () => {
92+ expect(
93+ toolContentText([
94+ { type: "terminal", terminalId: "t1" },
95+ { type: "content", content: { type: "text", text: "one" } },
96+ { type: "content", content: { type: "text", text: "" } },
97+ { type: "content", content: { type: "text", text: "two" } },
98+ ]),
99+ ).toBe("one\n\ntwo");
100+ });
101+
102+ it("hides the copy button when the call has no textual content", () => {
103+ render(
104+ <ToolCallCard
105+ call={call({ contents: [{ type: "terminal", terminalId: "t1" }] })}
106+ />,
107+ );
108+ expect(screen.queryByLabelText("Copy tool output")).toBeNull();
109+ });
110+});
modified ui/src/components/ToolCallCard.tsx +44 -22
@@ -34,26 +34,50 @@ const statusLabels: Record<string, string> = {
3434 failed: "failed",
3535 };
3636
37+/**
38+ * contentText flattens one tool-call content block into plain text for the
39+ * copy button: a "content" block's text, or a diff's path / old / new
40+ * sections. Anything else (terminal handles, …) yields "".
41+ *
42+ * @example
43+ * contentText({ type: "content", content: { type: "text", text: "hi" } }) // "hi"
44+ */
45+export function contentText(content: AcpToolCallContent): string {
46+ switch (content.type) {
47+ case "content":
48+ return content.content?.text ?? "";
49+ case "diff":
50+ return diffText(content);
51+ default:
52+ return "";
53+ }
54+}
55+
56+function diffText(diff: {
57+ path?: string;
58+ oldText?: string | null;
59+ newText?: string | null;
60+}): string {
61+ const parts: string[] = [];
62+ if (diff.path) parts.push(`Path: ${diff.path}`);
63+ if (diff.oldText) parts.push(`Old:\n${diff.oldText}`);
64+ if (diff.newText) parts.push(`New:\n${diff.newText}`);
65+ return parts.join("\n\n");
66+}
67+
68+/**
69+ * toolContentText joins the textual content of a whole tool call, skipping
70+ * empty blocks, so the copy button copies exactly what the card shows.
71+ *
72+ * @example
73+ * toolContentText([]) // ""
74+ */
75+export function toolContentText(contents: AcpToolCallContent[]): string {
76+ return contents.map(contentText).filter(Boolean).join("\n\n");
77+}
78+
3779 export function ToolCallCard({ call }: { call: ToolCall }) {
38- // Extract all textual content for copy button
39- const getToolContent = (): string => {
40- return call.contents
41- .map((content) => {
42- if (content.type === "content" && content.content?.text) {
43- return content.content.text;
44- }
45- if (content.type === "diff") {
46- const parts = [];
47- if (content.path) parts.push(`Path: ${content.path}`);
48- if (content.oldText) parts.push(`Old:\n${content.oldText}`);
49- if (content.newText) parts.push(`New:\n${content.newText}`);
50- return parts.join("\n\n");
51- }
52- return "";
53- })
54- .filter(Boolean)
55- .join("\n\n");
56- };
80+ const copyText = toolContentText(call.contents);
5781
5882 return (
5983 <details className={`tool tool-${call.status}`}>
@@ -67,9 +91,7 @@ export function ToolCallCard({ call }: { call: ToolCall }) {
6791 <span className={`tool-status tool-status-${call.status}`}>
6892 {statusLabels[call.status] ?? call.status}
6993 </span>
70- {getToolContent() && (
71- <CopyButton content={getToolContent()} label="Copy tool output" />
72- )}
94+ {copyText && <CopyButton content={copyText} label="Copy tool output" />}
7395 </summary>
7496 <div className="tool-body">
7597 {call.locations.length > 0 && (
@@ -34,26 +34,50 @@ const statusLabels: Record<string, string> = {
34 failed: "failed",34 failed: "failed",
35 };35 };
36 36
37+/**
38+ * contentText flattens one tool-call content block into plain text for the
39+ * copy button: a "content" block's text, or a diff's path / old / new
40+ * sections. Anything else (terminal handles, …) yields "".
41+ *
42+ * @example
43+ * contentText({ type: "content", content: { type: "text", text: "hi" } }) // "hi"
44+ */
45+export function contentText(content: AcpToolCallContent): string {
46+ switch (content.type) {
47+ case "content":
48+ return content.content?.text ?? "";
49+ case "diff":
50+ return diffText(content);
51+ default:
52+ return "";
53+ }
54+}
55+
56+function diffText(diff: {
57+ path?: string;
58+ oldText?: string | null;
59+ newText?: string | null;
60+}): string {
61+ const parts: string[] = [];
62+ if (diff.path) parts.push(`Path: ${diff.path}`);
63+ if (diff.oldText) parts.push(`Old:\n${diff.oldText}`);
64+ if (diff.newText) parts.push(`New:\n${diff.newText}`);
65+ return parts.join("\n\n");
66+}
67+
68+/**
69+ * toolContentText joins the textual content of a whole tool call, skipping
70+ * empty blocks, so the copy button copies exactly what the card shows.
71+ *
72+ * @example
73+ * toolContentText([]) // ""
74+ */
75+export function toolContentText(contents: AcpToolCallContent[]): string {
76+ return contents.map(contentText).filter(Boolean).join("\n\n");
77+}
78+
37 export function ToolCallCard({ call }: { call: ToolCall }) {79 export function ToolCallCard({ call }: { call: ToolCall }) {
38- // Extract all textual content for copy button80+ const copyText = toolContentText(call.contents);
39- const getToolContent = (): string => {
40- return call.contents
41- .map((content) => {
42- if (content.type === "content" && content.content?.text) {
43- return content.content.text;
44- }
45- if (content.type === "diff") {
46- const parts = [];
47- if (content.path) parts.push(`Path: ${content.path}`);
48- if (content.oldText) parts.push(`Old:\n${content.oldText}`);
49- if (content.newText) parts.push(`New:\n${content.newText}`);
50- return parts.join("\n\n");
51- }
52- return "";
53- })
54- .filter(Boolean)
55- .join("\n\n");
56- };
57 81
58 return (82 return (
59 <details className={`tool tool-${call.status}`}>83 <details className={`tool tool-${call.status}`}>
@@ -67,9 +91,7 @@ export function ToolCallCard({ call }: { call: ToolCall }) {
67 <span className={`tool-status tool-status-${call.status}`}>91 <span className={`tool-status tool-status-${call.status}`}>
68 {statusLabels[call.status] ?? call.status}92 {statusLabels[call.status] ?? call.status}
69 </span>93 </span>
70- {getToolContent() && (94+ {copyText && <CopyButton content={copyText} label="Copy tool output" />}
71- <CopyButton content={getToolContent()} label="Copy tool output" />
72- )}
73 </summary>95 </summary>
74 <div className="tool-body">96 <div className="tool-body">
75 {call.locations.length > 0 && (97 {call.locations.length > 0 && (
modified ui/tsconfig.tsbuildinfo +1 -1
@@ -1 +1 @@
1-{"root":["./src/App.test.tsx","./src/App.tsx","./src/api.ts","./src/lang.test.ts","./src/lang.ts","./src/main.tsx","./src/mentions.test.ts","./src/mentions.ts","./src/monaco-setup.ts","./src/protocol.ts","./src/reducer.test.ts","./src/reducer.ts","./src/store.ts","./src/theme.test.ts","./src/theme.ts","./src/useCompletion.ts","./src/workspace.test.ts","./src/workspace.ts","./src/ws.test.ts","./src/ws.ts","./src/components/AsciiDocView.test.tsx","./src/components/AsciiDocView.tsx","./src/components/ChatThread.tsx","./src/components/CodeView.tsx","./src/components/CompletionPopup.tsx","./src/components/DrawioView.test.tsx","./src/components/DrawioView.tsx","./src/components/EditorPane.test.tsx","./src/components/EditorPane.tsx","./src/components/FileTree.test.tsx","./src/components/FileTree.tsx","./src/components/ImageView.tsx","./src/components/InlineText.test.tsx","./src/components/InlineText.tsx","./src/components/Markdown.tsx","./src/components/MonacoViewer.tsx","./src/components/PermissionPrompt.tsx","./src/components/PlanView.tsx","./src/components/PreviewPane.test.tsx","./src/components/PreviewPane.tsx","./src/components/PromptInput.test.tsx","./src/components/PromptInput.tsx","./src/components/ResizeHandle.test.tsx","./src/components/ResizeHandle.tsx","./src/components/TerminalPane.test.tsx","./src/components/TerminalPane.tsx","./src/components/ToolCallCard.test.tsx","./src/components/ToolCallCard.tsx","./src/components/WorkingIndicator.test.tsx","./src/components/WorkingIndicator.tsx","./src/components/Workspace.test.tsx","./src/components/Workspace.tsx","./vite.config.ts"],"version":"5.9.3"}
\ No newline at end of file
1+{"root":["./src/app.test.tsx","./src/app.tsx","./src/api.ts","./src/lang.test.ts","./src/lang.ts","./src/main.tsx","./src/mentions.test.ts","./src/mentions.ts","./src/monaco-setup.ts","./src/protocol.ts","./src/reducer.test.ts","./src/reducer.ts","./src/store.ts","./src/theme.test.ts","./src/theme.ts","./src/usecompletion.ts","./src/workspace.test.ts","./src/workspace.ts","./src/ws.test.ts","./src/ws.ts","./src/components/asciidocview.test.tsx","./src/components/asciidocview.tsx","./src/components/chatthread.tsx","./src/components/codeview.tsx","./src/components/completionpopup.tsx","./src/components/copybutton.test.tsx","./src/components/copybutton.tsx","./src/components/drawioview.test.tsx","./src/components/drawioview.tsx","./src/components/editorpane.test.tsx","./src/components/editorpane.tsx","./src/components/filetree.test.tsx","./src/components/filetree.tsx","./src/components/imageview.tsx","./src/components/inlinetext.test.tsx","./src/components/inlinetext.tsx","./src/components/markdown.tsx","./src/components/monacoviewer.tsx","./src/components/permissionprompt.tsx","./src/components/planview.tsx","./src/components/previewpane.test.tsx","./src/components/previewpane.tsx","./src/components/promptinput.test.tsx","./src/components/promptinput.tsx","./src/components/resizehandle.test.tsx","./src/components/resizehandle.tsx","./src/components/terminalpane.test.tsx","./src/components/terminalpane.tsx","./src/components/toolcallcard.test.tsx","./src/components/toolcallcard.tsx","./src/components/workingindicator.test.tsx","./src/components/workingindicator.tsx","./src/components/workspace.test.tsx","./src/components/workspace.tsx","./vite.config.ts"],"version":"5.9.3"}
\ No newline at end of file
@@ -1 +1 @@
1-{"root":["./src/App.test.tsx","./src/App.tsx","./src/api.ts","./src/lang.test.ts","./src/lang.ts","./src/main.tsx","./src/mentions.test.ts","./src/mentions.ts","./src/monaco-setup.ts","./src/protocol.ts","./src/reducer.test.ts","./src/reducer.ts","./src/store.ts","./src/theme.test.ts","./src/theme.ts","./src/useCompletion.ts","./src/workspace.test.ts","./src/workspace.ts","./src/ws.test.ts","./src/ws.ts","./src/components/AsciiDocView.test.tsx","./src/components/AsciiDocView.tsx","./src/components/ChatThread.tsx","./src/components/CodeView.tsx","./src/components/CompletionPopup.tsx","./src/components/DrawioView.test.tsx","./src/components/DrawioView.tsx","./src/components/EditorPane.test.tsx","./src/components/EditorPane.tsx","./src/components/FileTree.test.tsx","./src/components/FileTree.tsx","./src/components/ImageView.tsx","./src/components/InlineText.test.tsx","./src/components/InlineText.tsx","./src/components/Markdown.tsx","./src/components/MonacoViewer.tsx","./src/components/PermissionPrompt.tsx","./src/components/PlanView.tsx","./src/components/PreviewPane.test.tsx","./src/components/PreviewPane.tsx","./src/components/PromptInput.test.tsx","./src/components/PromptInput.tsx","./src/components/ResizeHandle.test.tsx","./src/components/ResizeHandle.tsx","./src/components/TerminalPane.test.tsx","./src/components/TerminalPane.tsx","./src/components/ToolCallCard.test.tsx","./src/components/ToolCallCard.tsx","./src/components/WorkingIndicator.test.tsx","./src/components/WorkingIndicator.tsx","./src/components/Workspace.test.tsx","./src/components/Workspace.tsx","./vite.config.ts"],"version":"5.9.3"}
\ No newline at end of file\ No newline at end of file
1+{"root":["./src/app.test.tsx","./src/app.tsx","./src/api.ts","./src/lang.test.ts","./src/lang.ts","./src/main.tsx","./src/mentions.test.ts","./src/mentions.ts","./src/monaco-setup.ts","./src/protocol.ts","./src/reducer.test.ts","./src/reducer.ts","./src/store.ts","./src/theme.test.ts","./src/theme.ts","./src/usecompletion.ts","./src/workspace.test.ts","./src/workspace.ts","./src/ws.test.ts","./src/ws.ts","./src/components/asciidocview.test.tsx","./src/components/asciidocview.tsx","./src/components/chatthread.tsx","./src/components/codeview.tsx","./src/components/completionpopup.tsx","./src/components/copybutton.test.tsx","./src/components/copybutton.tsx","./src/components/drawioview.test.tsx","./src/components/drawioview.tsx","./src/components/editorpane.test.tsx","./src/components/editorpane.tsx","./src/components/filetree.test.tsx","./src/components/filetree.tsx","./src/components/imageview.tsx","./src/components/inlinetext.test.tsx","./src/components/inlinetext.tsx","./src/components/markdown.tsx","./src/components/monacoviewer.tsx","./src/components/permissionprompt.tsx","./src/components/planview.tsx","./src/components/previewpane.test.tsx","./src/components/previewpane.tsx","./src/components/promptinput.test.tsx","./src/components/promptinput.tsx","./src/components/resizehandle.test.tsx","./src/components/resizehandle.tsx","./src/components/terminalpane.test.tsx","./src/components/terminalpane.tsx","./src/components/toolcallcard.test.tsx","./src/components/toolcallcard.tsx","./src/components/workingindicator.test.tsx","./src/components/workingindicator.tsx","./src/components/workspace.test.tsx","./src/components/workspace.tsx","./vite.config.ts"],"version":"5.9.3"}
\ No newline at end of file\ No newline at end of file