nandi/oripublic Fork 0
3d1f70c3b1e0d8c9a0b27adcc1bd1a9f053c050e
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

2026-09-17-acp-webapp.md · 56 lines · 7.0 KBmarkdown
Blame HistoryOpen raw

Handoff — 2026-09-17 — ACP web client (ticket 0004)

State

The full v1 works and is verified: make run-mock serves the panel on :8888 with the bundled demo agent (smoke-tested live over a real WebSocket: hello → prompt → thought/plan/tool calls → permission → turn_ended). All test suites green (make test, plus go test -race), quality gate PASS, bilingual docs written, .memory/ created. Two commits on branch feat/acp-webapp (main holds only the initial repo state); a final commit with docs/memory/module-rename is proposed but not yet made — the user decides.

In flight — READ THIS FIRST (updated at session end)

Open bug: the UI shows "offline" in the user's real sbx sandbox (sbx create claude . --template k33g/ori:0.0.0 --kit ./kits/ori -p 5555:8888; page loads, so ori runs and HTTP forwards — the /ws WebSocket is what fails or drops). Already established:

  • ori's WS origin handling is CORRECT: curl handshakes against the image give 101 for Origin: http://localhost:5555 (and any localhost:*), 403 for foreign origins. Node-based smoke tests send no Origin header, so they never exercised this.
  • sbx port publishing is raw L4 (gvisor MapPorts in sandboxes/sandboxd/pkg/server/backend_dockernext.go), so the upgrade should traverse.
  • Three discriminating diagnostics were given to the user, ANSWERS PENDING: (1) sbx exec ori tail -20 /home/agent/.ori.log (an origin rejection logs "websocket accept failed"); (2) curl WS handshake from INSIDE the sandbox to :8888/ws; (3) same from the host through :5555. 101/101 → look at the browser console; inside-101/forward-not-101 → the forward blocks upgrades; inside-403 → origin, ask the exact browser URL. If (1) shows nothing at all, check /var/log/sbx-kit-startup.log.
  • Possible follow-up angle if 101/101: default publish protocol is tcp4 (IPv4-only host listener) while browsers try ::1 first for localhost — page fallback works, some WS stacks may not; retry publishing with explicit /tcp (dual-stack) or have the browser use http://127.0.0.1:5555.

In flight (rest)

Nothing half-written. The final commit of docs + .memory/ + the module rename is the only pending mechanical action.

Workspace panel (added later in the session)

The user requested and approved a workspace evolution, delivered complete: internal/files + internal/terminal on the backend; file tree, Monaco preview/editor, Markdown/AsciiDoc rendering, xterm.js terminal on the front; execute tool output in monospace. Gate PASS again, 46 Go + 66 front tests. Server redeployed live with it. New traps recorded in "Watch out for".

Next steps

  1. Let the user try the UI in a real browser (in the sandbox this needs sbx ports <sandbox> --publish 8888:8888/tcp on the host; the server already binds 0.0.0.0).
  2. Try against real Claude Code (make run) — needs claude auth in the environment; never exercised here.
  3. Tickets 0002 (architecture diagram — largely satisfied by docs/diagrams/packages.drawio, consider closing) and 0003 (how to run outside the sandbox — the docs' how-to pages are most of the answer).
  4. Natural evolutions, in design order: render non-text content blocks (images/resources), session modes (current_mode_update is currently ignored), multi-session/tabs via the bridge.Prompter seam, file preview / file list panes (the ticket names them as future needs).

Open questions / blockers

  • No git remote is configured; where this repo is hosted (rickub.com?) is unknown here.
  • Whether make run works with the user's Claude auth has not been verified in this sandbox.

Verified against real Claude Code (later in the session)

make run failed at first inside this sandbox: Claude Code refuses to launch inside another Claude Code session (the CLAUDECODE env var is inherited from the sandbox's own session). Workaround that works: env -u CLAUDECODE ./bin/ori. With that, the full chain was verified live: real session created (UUID id), prompt over WebSocket, streamed answer, turn_ended end_turn. Decision pending: whether to strip CLAUDECODE in spawnProcess, in the Makefile, or only document it. Also noted: the adapter package was renamed @zed-industries/claude-code-acp@agentclientprotocol/claude-agent-acp (old name still works, deprecated) — consider updating config.DefaultAgentCommand.

Watch out for

  • The adapter inherits the user's ~/.claude/settings.json default model. A /model toggle in another session saved "claude-fable-5[1m]" (the 1M-context variant), which the adapter's session cannot access → every prompt failed with -32603 "issue with the selected model (fable[1m])". Fix in force: launch ori with ANTHROPIC_MODEL=claude-fable-5 (plain id) so the adapter is decoupled from the UI's saved default.

  • ~/.npm is a full 488MB dedicated volume — npm installs of big packages fail with ENOSPC; the cache now lives at ~/.npm-big via NPM_CONFIG_CACHE in /etc/sandbox-persistent.sh.

  • monaco-editor 0.56 exports map: import workers as monaco-editor/editor/editor.worker.js?worker (no esm/vs/ prefix — the map adds it); ?worker imports need "vite/client" in tsconfig types.

  • biome 1.9 suppressions: lint/a11y/noStaticElementInteractions does not exist in 1.9 (suppression = parse error); a suppression for a JSX attribute must sit on the line directly above that attribute, inside the opening tag.

  • @asciidoctor/core v4 API: convert is a NAMED export and returns a Promise — there is no default export and no asciidoctor() factory (that was v3). Never mock this module in the only test covering it: the real-render test in AsciiDocView.test.tsx exists precisely because a mocked test let a production breakage through.

  • Material icons: served from /material-icons (copied from vscode-material-icons into ui/public/ by ui/scripts/copy-icons.mjs, hooked on npm predev/prebuild, gitignored). A fresh checkout gets them on the first make build/make dev.

  • Module path is rickub.com/bots-garden/ori — do not "fix" it back to github; the user explicitly corrected this mid-session.

  • ~/.qlty is a dedicated 488MB volume; the qlty tool cache lives at ~/.qlty-cache on the main disk behind a symlink. If that symlink disappears (sandbox recreation), qlty installs will fail with ENOSPC again; recreate it before running the quality skill.

  • The coder acp-go-sdk does NOT auto-inject the connection into an agent implementation: call SetAgentConnection manually (bit us once; the mock in internal/agent/agent_test.go and mockagent.Run both do it).

  • qlty's return-statements smell counts returns inside nested closures as part of the enclosing function — extract named functions, don't fight it with inline funcs.

  • qlty fmt (biome) reformats TS/CSS/JSON to tabs; run it rather than hand-formatting.

  • Frontend tests: jsdom lacks scrollIntoView (call it optionally) and store dispatches outside React need act().

  • Vitest is pinned to major 3 in ui/package.json (^3.0.0) — fine today, but remember it's a floor choice made for install determinism, not a hard requirement.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# Handoff — 2026-09-17 — ACP web client (ticket 0004)

## State

The full v1 works and is verified: `make run-mock` serves the panel on :8888 with the bundled demo agent (smoke-tested live over a real WebSocket: hello → prompt → thought/plan/tool calls → permission → turn_ended). All test suites green (`make test`, plus `go test -race`), quality gate PASS, bilingual docs written, `.memory/` created. Two commits on branch `feat/acp-webapp` (`main` holds only the initial repo state); a final commit with docs/memory/module-rename is proposed but not yet made — the user decides.

## In flight — READ THIS FIRST (updated at session end)

**Open bug: the UI shows "offline" in the user's real sbx sandbox** (`sbx create claude . --template k33g/ori:0.0.0 --kit ./kits/ori -p 5555:8888`; page loads, so ori runs and HTTP forwards — the /ws WebSocket is what fails or drops). Already established:

- ori's WS origin handling is CORRECT: curl handshakes against the image give 101 for `Origin: http://localhost:5555` (and any localhost:*), 403 for foreign origins. Node-based smoke tests send no Origin header, so they never exercised this.
- sbx port publishing is raw L4 (gvisor MapPorts in `sandboxes/sandboxd/pkg/server/backend_dockernext.go`), so the upgrade should traverse.
- Three discriminating diagnostics were given to the user, ANSWERS PENDING: (1) `sbx exec ori tail -20 /home/agent/.ori.log` (an origin rejection logs "websocket accept failed"); (2) curl WS handshake from INSIDE the sandbox to :8888/ws; (3) same from the host through :5555. 101/101 → look at the browser console; inside-101/forward-not-101 → the forward blocks upgrades; inside-403 → origin, ask the exact browser URL. If (1) shows nothing at all, check `/var/log/sbx-kit-startup.log`.
- Possible follow-up angle if 101/101: default publish protocol is tcp4 (IPv4-only host listener) while browsers try ::1 first for localhost — page fallback works, some WS stacks may not; retry publishing with explicit `/tcp` (dual-stack) or have the browser use http://127.0.0.1:5555.

## In flight (rest)

Nothing half-written. The final commit of docs + `.memory/` + the module rename is the only pending mechanical action.

## Workspace panel (added later in the session)

The user requested and approved a workspace evolution, delivered complete: `internal/files` + `internal/terminal` on the backend; file tree, Monaco preview/editor, Markdown/AsciiDoc rendering, xterm.js terminal on the front; `execute` tool output in monospace. Gate PASS again, 46 Go + 66 front tests. Server redeployed live with it. New traps recorded in "Watch out for".

## Next steps

1. Let the user try the UI in a real browser (in the sandbox this needs `sbx ports <sandbox> --publish 8888:8888/tcp` on the host; the server already binds `0.0.0.0`).
2. Try against real Claude Code (`make run`) — needs claude auth in the environment; never exercised here.
3. Tickets 0002 (architecture diagram — largely satisfied by `docs/diagrams/packages.drawio`, consider closing) and 0003 (how to run outside the sandbox — the docs' how-to pages are most of the answer).
4. Natural evolutions, in design order: render non-text content blocks (images/resources), session modes (`current_mode_update` is currently ignored), multi-session/tabs via the `bridge.Prompter` seam, file preview / file list panes (the ticket names them as future needs).

## Open questions / blockers

- No git remote is configured; where this repo is hosted (rickub.com?) is unknown here.
- Whether `make run` works with the user's Claude auth has not been verified in this sandbox.

## Verified against real Claude Code (later in the session)

`make run` failed at first inside this sandbox: **Claude Code refuses to launch inside another Claude Code session** (the `CLAUDECODE` env var is inherited from the sandbox's own session). Workaround that works: `env -u CLAUDECODE ./bin/ori`. With that, the full chain was verified live: real session created (UUID id), prompt over WebSocket, streamed answer, `turn_ended end_turn`. Decision pending: whether to strip `CLAUDECODE` in `spawnProcess`, in the Makefile, or only document it. Also noted: the adapter package was renamed `@zed-industries/claude-code-acp``@agentclientprotocol/claude-agent-acp` (old name still works, deprecated) — consider updating `config.DefaultAgentCommand`.

## Watch out for

- **The adapter inherits the user's `~/.claude/settings.json` default model.** A `/model` toggle in another session saved `"claude-fable-5[1m]"` (the 1M-context variant), which the adapter's session cannot access → every prompt failed with -32603 "issue with the selected model (fable[1m])". Fix in force: launch ori with `ANTHROPIC_MODEL=claude-fable-5` (plain id) so the adapter is decoupled from the UI's saved default.

- **`~/.npm` is a full 488MB dedicated volume** — npm installs of big packages fail with ENOSPC; the cache now lives at `~/.npm-big` via `NPM_CONFIG_CACHE` in `/etc/sandbox-persistent.sh`.
- **monaco-editor 0.56 exports map**: import workers as `monaco-editor/editor/editor.worker.js?worker` (no `esm/vs/` prefix — the map adds it); `?worker` imports need `"vite/client"` in tsconfig types.
- **biome 1.9 suppressions**: `lint/a11y/noStaticElementInteractions` does not exist in 1.9 (suppression = parse error); a suppression for a JSX attribute must sit on the line directly above that attribute, inside the opening tag.
- **@asciidoctor/core v4 API**: `convert` is a NAMED export and returns a **Promise** — there is no default export and no `asciidoctor()` factory (that was v3). Never mock this module in the only test covering it: the real-render test in `AsciiDocView.test.tsx` exists precisely because a mocked test let a production breakage through.
- **Material icons**: served from `/material-icons` (copied from `vscode-material-icons` into `ui/public/` by `ui/scripts/copy-icons.mjs`, hooked on npm `predev`/`prebuild`, gitignored). A fresh checkout gets them on the first `make build`/`make dev`.

- **Module path is `rickub.com/bots-garden/ori`** — do not "fix" it back to github; the user explicitly corrected this mid-session.
- `~/.qlty` is a dedicated 488MB volume; the qlty tool cache lives at `~/.qlty-cache` on the main disk behind a symlink. If that symlink disappears (sandbox recreation), qlty installs will fail with ENOSPC again; recreate it before running the quality skill.
- The coder acp-go-sdk does NOT auto-inject the connection into an agent implementation: call `SetAgentConnection` manually (bit us once; the mock in `internal/agent/agent_test.go` and `mockagent.Run` both do it).
- qlty's `return-statements` smell counts returns inside nested closures as part of the enclosing function — extract named functions, don't fight it with inline funcs.
- `qlty fmt` (biome) reformats TS/CSS/JSON to tabs; run it rather than hand-formatting.
- Frontend tests: jsdom lacks `scrollIntoView` (call it optionally) and store dispatches outside React need `act()`.
- Vitest is pinned to major 3 in `ui/package.json` (`^3.0.0`) — fine today, but remember it's a floor choice made for install determinism, not a hard requirement.