Skip to content

Adapt to @kolu/surface's settled serveOverStdio (unix-socket upstreaming) - #57

Merged
srid merged 2 commits into
masterfrom
kolu-unix-socket-upstream
Jun 9, 2026
Merged

Adapt to @kolu/surface's settled serveOverStdio (unix-socket upstreaming)#57
srid merged 2 commits into
masterfrom
kolu-unix-socket-upstream

Conversation

@srid

@srid srid commented Jun 9, 2026

Copy link
Copy Markdown
Owner

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:

Upstream change Effect on drishti
serveOverStdio now resolves with ServeOverStdioEnd ({ reason: "end" | "error" }) and never rejects on a read-stream error The agent's injectable Serve type widens its resolution to Promise<unknown> — it only awaits serving's end. Behaviorally better: a peer reset no longer risks an unhandled rejection in the agent.
New exports @kolu/surface/unix-socket (serveOverUnixSocket, getRuntimeSocketPath) + @kolu/surface/links/unix-socket (unixSocketLink), and isContractVersionCompatible in /define Purely additive — nothing in drishti consumes them yet (they're the local-IPC link family member kolu-tui uses).

juspay/kolu#1084 has merged, and the kolu pin now points at master (4f8c3ce, the merge revision) — the interim r4-phase1-kolu-tui-list branch pin served only to prove compatibility pre-merge.

Verified locally: just typecheck green across all three workspace members; bun test packages/agent 27/27.

Generated by /be on Claude Code (model claude-fable-5).

…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
srid marked this pull request as ready for review June 9, 2026 21:53
@srid

srid commented Jun 9, 2026

Copy link
Copy Markdown
Owner Author

Kolu pin bumped to masterjuspay/kolu#1084 has merged, so the interim r4-phase1-kolu-tui-list branch pin is gone: npins/sources.json now points at master rev 4f8c3ce (the merge revision), as promised in the PR body.

Verified on the new pin (commit 8dd613e):

  • just typecheck — green across common/agent/app
  • bun test packages/agent — 27/27
  • Full justci pipeline in strict mode — all 18 nodes succeeded on x86_64-linux + aarch64-darwin (statuses posted on this commit)

Nothing blocks landing this now.

@srid
srid merged commit b5a1ee5 into master Jun 9, 2026
18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant