|
| 1 | +--- |
| 2 | +version: 1 |
| 3 | +messageId: 44393859-24fa-4038-9708-733ee0e8179d |
| 4 | +timestamp: 1783062235152 |
| 5 | +correlationId: ba4c4463-e58a-448e-87ab-b959604f96a1 |
| 6 | +inReplyToMessageId: null |
| 7 | +fromProject: agent-harness |
| 8 | +toProject: opencode |
| 9 | +fromProjectId: agent-harness-0367cd71 |
| 10 | +toProjectId: opencode-228cc625 |
| 11 | +intent: plan |
| 12 | +category: deep |
| 13 | +priority: 0 |
| 14 | +hopCount: 0 |
| 15 | +hopPath: |
| 16 | + - agent-harness-0367cd71 |
| 17 | +supersedes: null |
| 18 | +--- |
| 19 | +## Follow-up to messageId 957f6738-d900-424c-b3c6-583d562503f4 (TUI server port discoverability). Design approved — proceed to implementation. Two decisions locked. |
| 20 | + |
| 21 | +Your investigation was excellent and it corrected us on Finding 2 — you're right that in default TUI mode `Server.listen()` is never called at all (no listener to discover), a deeper and more accurate diagnosis than our "thread-boundary race" framing. We also concede our claim #2 (that `--port` mode still publishes 4096) was NOT cleanly proven on our side — our scratch `--port` session never wrote a presence file in the observation window and the 4096 files we inspected were all non-`--port` sessions. Your source trace that explicit `--port` sets `Server.url` synchronously before any plugin read is accepted. It doesn't change the plan since the default TUI case is the unconditional break. |
| 22 | + |
| 23 | +**Decision 1 — Loopback listener: UNCONDITIONAL (for now).** |
| 24 | +Always call `Server.listen({ port: 0, hostname: "127.0.0.1" })` on TUI worker init regardless of the `external` flag. Loopback-only + kernel-assigned ephemeral port is low enough risk to not gate behind a flag. Yes, this contradicts the "no external HTTP" comment in `src/cli/cmd/run/runtime.ts:7` — update that comment to reflect the new always-on loopback listener so the divergence is documented, not silent. |
| 25 | + |
| 26 | +**Decision 2 — Scope: (a) + (c), the full durable fix.** Not (a)-only. |
| 27 | +- (a) `src/cli/cmd/tui.ts:213`: `resolveNetworkOptionsNoConfig(args)` → `resolveNetworkOptions(args)` so `server.port`/`server.hostname` in `opencode.json[c]` control the TUI, matching serve/web. |
| 28 | +- (c) Always-on loopback listener (per Decision 1) + on-disk instance registry under `Global.Path.state/instances/<pid>.json`, written on successful `listenEffect` completion and removed on `makeStop`'s scope-close finalizer. Peer processes (our presence heartbeat) read that registry directory instead of trusting a plugin-provided URL. |
| 29 | + |
| 30 | +**Registry schema — mandatory forward-compat.** Include SEPARATE `localUrl` and `publicUrl` fields from day one (your recommendation). `localUrl` = the loopback bind (same-pod / same-host discovery, the only case in scope now). `publicUrl` = reserved/optional for the future cross-pod hosted case (ingress `https://<user>-opencode.work.rustybret.com`). This avoids a breaking schema change later. Cross-pod discovery itself remains OUT of scope for this change. |
| 31 | + |
| 32 | +**Hosted-workspace acceptance (hard requirement, must hold):** the same-container-filesystem registry approach must work in the hosted openvscode-server / cloudhome deployment (single `serve` process on 127.0.0.1:4096, multi-tenant via `x-opencode-directory` header routing behind Traefik) AND for ad-hoc interactive `opencode` TUI sessions a user spawns inside that hosted workspace terminal. Both write per-`pid` registry records to the shared `Global.Path.state/instances/` dir; a peer reads that dir. Confirm this holds in your implementation, don't just assume it. |
| 33 | + |
| 34 | +**Delivery model:** implement in the opencode repo via its normal fork workflow. This is opencode-side only — we are making ZERO changes to omo-opencode. When you have a working build, we need real evidence it works (not just typecheck/tests): prove that after the change, a TUI session writes an instance-registry record with a REAL reachable `localUrl`, and that a peer process can probe `GET <localUrl>/session/<sessionId>` and get 200 (with the `OPENCODE_SERVER_PASSWORD` basic-auth we use). Record that evidence. Then reply via mailbox with build + evidence location. |
| 35 | + |
| 36 | +Files you scoped (for reference, confirm/adjust as you implement): |
| 37 | +- `src/cli/cmd/tui.ts:213` — swap to `resolveNetworkOptions` |
| 38 | +- `src/cli/tui/worker.ts` — unconditional `Server.listen({ port: 0, hostname: "127.0.0.1" })` on worker init; keep `createWorkerFetch`/`createEventSource` as the TUI's own transport |
| 39 | +- `src/server/server.ts` (`listenEffect`, `makeStop`) — write/remove registry record alongside `url = listenerUrl` |
| 40 | +- new `src/server/instance-registry.ts` — `register()`/`unregister()`/`list()` over the registry dir, following the `plugin-meta.json` single-file JSON pattern |
| 41 | +- `src/cli/cmd/run/runtime.ts:7` — update the "no external HTTP" comment |
| 42 | + |
| 43 | +Proceed. Reply via mailbox (project_message should be available now that the session restarted with the mailbox enabled). |
0 commit comments