Adapt to @kolu/surface's settled serveOverStdio (unix-socket upstreaming) - #57
Merged
Conversation
…lu#1084) Bump the kolu pin to the r4-phase1-kolu-tui-list branch head, which upstreams the unix-socket transport into @kolu/surface and changes serveOverStdio's contract: it now resolves with a ServeOverStdioEnd ({reason: "end" | "error"}) instead of rejecting on a read-stream error (a peer reset is an ordinary lifecycle event, not an unhandled-rejection crash). The agent's injectable `Serve` type widens its resolution to `unknown` accordingly — the agent only awaits serving's end, not its value, and test fakes may still resolve void. Mirror PR per kolu's surface-sharing rule; the pin moves back to kolu master once juspay/kolu#1084 merges.
srid
added a commit
to juspay/kolu
that referenced
this pull request
Jun 9, 2026
…1084) **kolu-server now serves its in-process pty-host over a unix socket, and a new `kolu-tui` CLI lists and snapshots your live terminals from the shell — no browser.** It's the *raw*, terminal-side client of the same pty-host the browser drives over the full contract, and R-4 Phase 1 of the [kolu-tui plan](docs/atlas/src/content/atlas/pty-daemon-tui.mdx). The web path is byte-identical: **one PTY host, two transports.** ``` browser ──ws── kolu-server ──directLink (no wire)───────┐ ├── one in-process pty-host kolu-tui ──unixSocketLink ── serveOverUnixSocket ─────────┘ (servePtyHost router) ``` ### The CLI (`@kolu/pty-tui`) Read-only this phase — `attach` / `spawn` / `kill` are later phases. The CLI comes and goes; kolu-server keeps owning the PTYs. Same CLI framework as kolu-server (cleye), table rendering via columnify. | Command | Behaviour | |---|---| | `kolu-tui list [--json]` | print your live terminals — **id · pid · idle · cmd · cwd** (`cmd` = the OSC title, else the foreground command); `--json` carries the full entry incl. raw title + foreground process | | `kolu-tui snapshot <id>` | dump a terminal's current rendered scrollback to stdout, then exit | `--pty-host-socket <path>` points at a non-default server; an unreachable socket is an honest one-line error (`ECONNREFUSED`/`ENOENT` → "is kolu-server running?"), never a silent hang. Packaged as `nix run github:juspay/kolu#kolu-tui` and auto-installed by the home-manager module. See the **Evidence — video** comment for a recording. ### Upstreamed into `@kolu/surface` (mirror: srid/drishti#57) The transport work was generic, so it lives in the surface library — completing the link family (websocket · stdio · direct · **unix-socket**): - **`@kolu/surface/unix-socket`** — `serveOverUnixSocket` (outcome-based, *never-rejecting* socket serving: dir-privacy gate, live-peer probe, stale-inode clearing that refuses to unlink anything not proven a dead socket) + `getRuntimeSocketPath` (the `$XDG_RUNTIME_DIR/<app>` / `/tmp/<app>-$UID` rendezvous convention). - **`@kolu/surface/links/unix-socket`** — `unixSocketLink`, the dialing client half. - **`serveOverStdio` no longer rejects** when a peer's read stream errors — it resolves with `{ reason: "end" | "error" }`. A rejecting serve promise was an unhandled-rejection crash footgun for multi-peer hosts (it bit kolu-server twice during this PR's review); now the no-crash path is the default for every consumer. - **`isContractVersionCompatible`** in `@kolu/surface/define` — the generic `major.minor` handshake predicate; `isPtyHostContractCompatible` delegates. kolu's `socketPath.ts` / `serveOverSocket.ts` are now thin wrappers that map transport outcomes to kolu-voiced operator log lines. _Per the surface-sharing rule, srid/drishti#57 adapts drishti and pins it to this branch to prove API compatibility._ ### How it fits together - **`@kolu/pty-host`** gains `createInProcessPtyHost` — builds the host once and returns its `client` (the no-wire `directLink` web client) **and** a `servedRouter` (the contract-wrapped form serving needs), so one host backs both transports and can never be instantiated twice. `getPtyHostSocketPath` is the single resolver server and CLI share. - **Crash-safe by construction**: the socket is *additive* — kolu-server's web path doesn't depend on it — so every bind failure (a lost `EADDRINUSE` race when parallel servers share the default socket, an unwritable runtime dir) degrades to a **logged no-op, never a rejection**. _(This one bit the e2e harness, where many servers share `$XDG_RUNTIME_DIR`; caught in CI, hardened here.)_ - **Full-metadata `list`**: the `terminal.list` entry was enriched with `title` (OSC 0/2) + `foregroundProcess` (additive · optional, **contract 2.1**), so a one-shot `list` shows the `cmd` column without per-row tap subscriptions. - **`kolu-server`** instantiates the pty-host once (`ptyHost.ts`) and adds one additive socket listener in `index.ts`; `local.ts` now consumes the shared client. Nothing about the web path changes. > **Socket path decision** (the plan deferred it to Phase 1): a *stable* `$XDG_RUNTIME_DIR/kolu/pty-host.sock`, falling back to a fixed `/tmp/kolu-$UID/` off systemd — deliberately **not** `os.tmpdir()`, whose `$TMPDIR` differs by launch context on macOS (launchd server vs `nix run` CLI), so server and CLI would land on different sockets and never meet. Single-server-per-session model; `--pty-host-socket` on both sides to run more than one. ### Tests & docs - A **real unix-socket round-trip** at both layers (generic in `@kolu/surface`'s `unix-socket.test.ts` + the pty-host contract round-trip in `serveOverSocket.test.ts`), the never-rejects regression pin for `serveOverStdio`, data-loss refusal tests (regular file / unprobeable socket), rendezvous-path `$TMPDIR`-independence pins, render-helper coverage, and e2e `smoke` + `terminal` confirming the web path is unaffected. - README + `packages/surface/README.md` synced (the link family + unix-socket transport reference). The kolu-tui *announcements* were removed from README/website — it isn't ready for users yet; contributor-facing architecture rows remain. - **Docs migration rode along**: the kolu-tui plan and the remaining `docs/plans/*.html` were ported to Atlas notes (`pty-daemon-tui`, `pty-daemon`, `remote-terminals`, `pty-daemon-chrome-bar`); `docs/plans/` is retired. ### Notes - Structural review (hickey + lowy, with cross-validation) ran post-implement; the full `/be-review` gauntlet (codex-debate → lens-debate → code-police) ran on the pre-upstreaming shape — see the review comments. ### Try it locally ```sh # in one shell: a server (the socket appears once it boots) nix run github:juspay/kolu/r4-phase1-kolu-tui-list # in another: list its live terminals nix run github:juspay/kolu/r4-phase1-kolu-tui-list#kolu-tui -- list ``` 🤖 Generated with [Claude Code](https://claude.com/claude-code) _Generated by [`/do`](https://github.com/srid/agency) + [`/be`](https://github.com/srid/agency) on Claude Code (model `claude-fable-5`)._
srid
marked this pull request as ready for review
June 9, 2026 21:53
Owner
Author
|
Kolu pin bumped to master — juspay/kolu#1084 has merged, so the interim Verified on the new pin (commit 8dd613e):
Nothing blocks landing this now. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Tracks juspay/kolu#1084's upstreaming of the unix-socket transport into
@kolu/surface— the mirror PR kolu's surface-sharing rule requires for API-facing surface changes.Two things changed upstream:
serveOverStdionow resolves withServeOverStdioEnd({ reason: "end" | "error" }) and never rejects on a read-stream errorServetype widens its resolution toPromise<unknown>— it only awaits serving's end. Behaviorally better: a peer reset no longer risks an unhandled rejection in the agent.@kolu/surface/unix-socket(serveOverUnixSocket,getRuntimeSocketPath) +@kolu/surface/links/unix-socket(unixSocketLink), andisContractVersionCompatiblein/definejuspay/kolu#1084 has merged, and the kolu pin now points at master (
4f8c3ce, the merge revision) — the interimr4-phase1-kolu-tui-listbranch pin served only to prove compatibility pre-merge.Verified locally:
just typecheckgreen across all three workspace members;bun test packages/agent27/27.Generated by
/beon Claude Code (modelclaude-fable-5).