Skip to content

feat(daemon): implement CustomPaneBackend JSON-RPC protocol (Phase 1)#515

Open
Wirasm wants to merge 3 commits intomainfrom
feat/custom-pane-backend-protocol
Open

feat(daemon): implement CustomPaneBackend JSON-RPC protocol (Phase 1)#515
Wirasm wants to merge 3 commits intomainfrom
feat/custom-pane-backend-protocol

Conversation

@Wirasm
Copy link
Owner

@Wirasm Wirasm commented Feb 18, 2026

Implements KILD's side of the CustomPaneBackend protocol proposed in
anthropics/claude-code#26572.

The daemon's Unix socket now speaks two protocols on the same path — the existing
KILD ClientMessage JSONL protocol and a new JSON-RPC 2.0 pane backend protocol. The
first line from a new connection decides which handler is invoked:

JSON with "method" key  →  pane backend handler (CustomPaneBackend)
otherwise               →  existing ClientMessage JSONL handler

This is Phase 1 (parallel support). The tmux shim and all shim env vars are kept
unchanged — no regressions.

Protocol

// Claude Code → daemon: handshake
{"id":"1","method":"initialize","params":{"protocol_version":"1","capabilities":["events"],"session_hint":"<session_id>"}}
// daemon → Claude Code: response
{"id":"1","result":{"protocol_version":"1","capabilities":["events","capture"],"self_context_id":"ctx_0"}}

// Supported methods: spawn_agent, write, capture, kill, list
// Push events (no id): context_output (base64), context_exited (exit code)
// capture returns {"text": "..."} — plain UTF-8 scrollback

Changes

kild-daemon

  • pane_backend/protocol.rs — wire types: PaneBackendRequest/Response/Event, params structs, JsonRpcError
  • pane_backend/context.rsContextMap for ctx_id ↔ daemon_session_id mapping
  • pane_backend/handler.rs — async connection handler, dispatch_request, full method implementation
  • server/connection.rsroute_connection detects protocol on first line
  • session/state.rsparent_session_id field + subscribe_output_passive
  • session/manager.rsparent_session_id param on create_session, list_sessions_by_parent, subscribe_output_passive

kild-core

  • sessions/daemon_helpers.rs — injects CLAUDE_PANE_BACKEND_SOCKET and CLAUDE_PANE_BACKEND_SESSION_ID into daemon PTY env alongside existing shim vars

New env vars (injected into daemon PTYs)

Env var Purpose
CLAUDE_PANE_BACKEND_SOCKET Path to daemon socket — tells Claude Code to connect
CLAUDE_PANE_BACKEND_SESSION_ID Leader session ID — echoed back as session_hint so daemon can correlate the connection

Related

Implements KILD's side of the CustomPaneBackend protocol proposed in
anthropics/claude-code#26572. This gives Claude Code a clean socket-based
pane management API without tmux impersonation, running in parallel with
the existing shim (Phase 1 — no regressions).

Protocol:
- Daemon socket (`~/.kild/daemon.sock`) now routes connections based on
  the first message: JSON with a `"method"` key → pane backend handler,
  otherwise → existing ClientMessage JSONL handler
- Handshake: `initialize` with optional `session_hint`; daemon responds
  with `protocol_version`, `capabilities`, `self_context_id`
- Methods: `spawn_agent`, `write`, `capture`, `kill`, `list`
- Push events (no id): `context_output` (base64 PTY data), `context_exited`
- Context IDs: `ctx_0` (leader, pre-registered from `session_hint`),
  `ctx_1`, `ctx_2`, … for spawned agents

New env vars injected into daemon PTYs:
- `CLAUDE_PANE_BACKEND_SOCKET` — socket path for Claude Code to connect to
- `CLAUDE_PANE_BACKEND_SESSION_ID` — echoed back as `session_hint` on init

Changes:
- `protocol/pane_backend.rs` — request/response/event types + ContextMap
- `server/pane_backend.rs` — async connection handler with full dispatch
- `server/connection.rs` — `route_connection` detects protocol on first line;
  `handle_connection_with_first_line` handles the KILD protocol path
- `session/state.rs` — `parent_session_id` field + `subscribe_output_passive`
- `session/manager.rs` — `parent_session_id` param on `create_session`,
  `list_sessions_by_parent`, `subscribe_output_passive`
- `daemon_helpers.rs` — inject pane backend env vars alongside shim vars

The tmux shim and all existing shim vars are kept unchanged in Phase 1.
Moves protocol/pane_backend.rs and server/pane_backend.rs into a cohesive
src/pane_backend/ module directory with three focused files:

  pane_backend/protocol.rs — all wire types (request/response/event/params)
  pane_backend/context.rs  — ContextMap (ctx_id ↔ session_id mapping)
  pane_backend/handler.rs  — connection handler + dispatch_request

Pure structural move — zero logic changes. Makes the protocol easier to
link in the upstream issue (anthropics/claude-code#26572) and to review
when Anthropic requests changes.
…rotocol spec

Two corrections from review against anthropics/claude-code#26572:

1. capture response: return {"text": "<utf-8 string>"} instead of
   {"data": "<base64>"}. Scrollback is human-readable terminal output,
   not raw binary — base64 encoding was only used for context_output
   streaming where arbitrary bytes flow.

2. InitializeParams: add capabilities: Vec<String> with #[serde(default)].
   Claude Code sends {"capabilities": ["events"]} in the initialize
   params; the field was previously silently dropped. Deserialized but not
   yet acted upon — future handlers can gate behaviour on declared
   capabilities.

Also adds test_deserialize_initialize_with_capabilities to protocol tests.
@Wirasm Wirasm force-pushed the feat/custom-pane-backend-protocol branch from 7ae53d7 to 9de50e9 Compare February 18, 2026 14:22
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