v0.3.0: the app tools bridge — the bundled Codex app tools work in the shared server #5
Loading…
Reference in a new issue
No description provided.
Delete branch "oss-oo/appserver-tools-bridge"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
v0.3.0 — the app tools bridge
The owner's requirement (2026-10-28, verbatim via the neighbour session): the bundled "Codex app tools" (
send_message_to_thread,read_thread,create_thread,fork_thread,handoff_thread, …) must work in the shared app-server — Mama (OSS-OO · 🧠 · Mama-Astra) and Omi (OSS-OO · 💐 · Omi-Astra) coordinate through them from inside the app, in both directions and mostly from code mode (tools.mcp__codex_app__*). The plugin's tool server reaches the ChatGPT app over a Unix socket the app creates per launch (/tmp/codex-app-tools/<uuid>.sock) and hands only to its own app-server child asCODEX_APP_TOOLS_PIPE_PATH; the shared server never saw that path, so v0.1–v0.2 ran with the plugin off. Measured (neighbour, read-only; oneread_threadon the real app socket with the owner's explicit yes): the app opens that socket even while attached to the shared server and answers a foreign process correctly (57 tools in the catalogue); the tool server reuses its pipe connection and reopens it after the app closes it; one tool-server instance per loaded thread; a dead socket answers at once, a silent one would hang the tool server. Z's design word (steer 39): (A) a bridge now, rig-measured; (C) family-own tools over the shared server as the target (with and without the app; draft in PR #6); (B) a symlink rejected.What ships (design A)
oo-appserver-bridge.service+libexec/app_tools_bridge.py: a stable Unix socket$XDG_RUNTIME_DIR/oo-appserver-bridge/app-tools.sock(0600). Every connection the shared server's tool server opens there is forwarded, byte for byte, to the tools socket of the ChatGPT app running right now — resolved per connection: a listening socket under/tmp/codex-app-tools/owned by aChatGPTprocess (ss -xlp), else the newest connectable socket file (stale files of earlier launches are skipped). An app restart means a new socket; the next connection finds it. The bridge never accepts-and-hangs: no live app socket → closed at once (receiptno-app), the upstream connect has a timeout, the app's end is propagated; a half-closing client still gets a late answer (window 120 s,app_side_openrecorded).oo-appserver.service:Wants/Afterthe bridge, passes the stable path asCODEX_APP_TOOLS_PIPE_PATH, runs the plugin again (codex_app.enabled=true).~/.local/state/oo-appserver/bridge.jsonl(target,bytes_to_app,bytes_to_tools,ms,outcomeclosed / no-app / app-unreachable / resolver-error).oo-appserver tools(bridge unit, stable socket, the app's socket, plugin state in the unit file AND in the running process — they differ between an install and the gated restart);status/doctorshow the line, say "restart pending (gated)" on a mismatch and warn when the unit offers the tools but the bridge is down, or when the attached app owns no tools socket.tests/test_bridge.py(14 legs): resolver (app-owned listener wins; stale files skipped; fallback to the newest connectable file; none without app), forwarding both ways with receipts, a new target per connection (app restart), no-app / dead-target / resolver-error outcomes, the app's end propagated, eight concurrent connections plus an idle client, late answers to half-closing clients and the bounded window, stale socket file replaced on start, the probe CLI.oo-appserver sendfor Mama and Omi (the owner's order), known gaps updated (the bundled tools need the running app; design C next).Not done here (by design)
oo-appserver releaseorrestartwhen the owner's chats are idle, then relaunch ChatGPT from the menu);doctorsays "restart pending"._metato the tool server is verified at the first realread_threadfrom a chat after the restart (with the owner's yes), together withALL_TOOLSin code mode.Independent review
Fresh read-only subagent on
dac3e8a(bridge suite 11/11 twice, send suite 100/100, unit verify, livessline, scratchpad experiments with concurrent/idle/half-closing clients and an app ignoring the FIN): "mergeable after fixing 1 and 2" — plugin state read from the loaded unit file instead of the running process; bridge not restarted on a module-only change; plus three low findings (tool-server reconnect wording, the 5 s late-answer window, the accept loop dying on EMFILE) and nits. All folded ina9b9473; the reviewer's confirmation: "delta clean — mergeable as is" (13/13 twice), three nits folded in42a15c3(14 legs). CI green on every commit since the python-path fix (runs 21–24, and the run on42a15c3). Review texts:v030-review/in the evidence folder.🤖 Generated with Claude Code
oo-appserver sendas the family way (the owner's order of 2026-10-28)- helper: the app-tools line and `oo-appserver tools` read the plugin state from the RUNNING server process (/proc/<MainPID>/cmdline) and from the unit file as loaded, and say when they differ ("restart pending (gated)") — after an install the loaded unit is new while the process is old; `OO_APPSERVER_TOOLS_SOCK` is honoured. - installer: the bridge is restarted when its unit OR its module changed, and only if it was running before. - bridge: a half-closing client still gets a late answer (window 120 s, `app_side_open` in the receipt), sockets are shut down both ways before closing (wakes a pump parked on a peer that ignores the FIN), the accept loop survives EMFILE/ECONNABORTED, the resolver is guarded against the app vanishing mid-resolution (safe mtimes, `resolver-error` receipt). - unit: back to /usr/bin/python3 (CI stubs it like the bundle binary). - tests: receipts polled instead of a fixed sleep; legs for eight concurrent connections plus an idle client and for a late answer to a half-closing client (13 legs). - README: the tool server reconnects after the app closes the pipe (receipts count connections). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>