Expose session and tree browsing/editing to RPC protocol#1762
Expose session and tree browsing/editing to RPC protocol#1762dnouri wants to merge 12 commits intobadlogic:mainfrom
Conversation
|
Here's a bit of a teaser video to show how this works out in the Emacs client: rpc-browsing-surface.mp4 |
484c521 to
45e8597
Compare
45e8597 to
899cfae
Compare
|
Setting this to draft because it has a couple of issues. I can't figure out why you split I'm afraid that won't fly. Can you explain why that split was needed? |
|
You're right, sorry for the confusion. I got confused by main's behaviour where The way this surfaced for me was that Maybe the right thing to do is to change cross-project session switching such that it updates cwd everywhere and re-creates tools? For now I'll remove |
Add five RPC commands that let external clients browse sessions and navigate the conversation tree without reimplementing the session file format or shelling out to the TUI: - list_sessions (scope: current | all) - get_tree (lightweight tree projection with optional full content) - navigate_tree (with optional branch summarization and labeling) - set_label (set or clear a label on any tree entry) - abort_branch_summary (cancel in-progress branch summarization) Tree projection filters metadata entries (label, session_info, custom) and re-parents their children to keep the tree structurally valid. Tool-result nodes are enriched with metadata from the corresponding tool_use, using branch-local resolution to prevent cross-branch contamination when the same toolCallId appears on sibling branches. Traversal is iterative to handle deep trees. Extract formatToolCall from tree-selector.ts into core/format-tool-call.ts so the RPC tree projection and TUI share the same tool call previews: [read: ~/file.ts:10-29] [bash: git status && git diff] Split setSessionFile() and syncLocation() in session-manager so session file loading no longer implicitly mutates cwd and sessionDir. allMessagesText is now always populated on session list items (the includeSearchText toggle added no meaningful savings since I/O dominates regardless).
Inline narrow *Like interfaces from rpc-command-wiring into rpc-mode.ts handlers -- the indirection did not earn its keep for what are 3-5 line case bodies. Remove wrapper type aliases (RpcListSessionsResult, RpcGetTreeResult) and inline their shapes directly into the RpcResponse union. Guard navigate_tree and set_label with try/catch so invalid entry IDs return typed RPC errors instead of falling through to the outer parse-error handler. Add assistant to toRpcMessageRole switch for robustness against future refactoring. Delete mock-based rpc-client unit tests that spied on the private send() method. The E2E tests in rpc.test.ts and the pure-function wiring unit tests provide sufficient coverage without coupling to client internals. Add comprehensive Tree Node Types reference to rpc.md documenting all 7 node variants with field descriptions and JSON examples. Add per-type unit tests for non-message tree projection (compaction, model_change, thinking_level_change, branch_summary, custom_message). Split monolithic E2E browsing test into individual per-operation tests. Reorganize RPC sections: list_sessions moves into Session, abort_branch_summary moves into Tree.
Pick navigate_tree option fields explicitly instead of spreading the full RPC command object into the domain method. Prevents leaking transport-level fields (id, type, targetId) into AgentSession.navigateTree(). Move error checking from getData() into send() so all commands -- including void methods like setLabel -- throw on server errors. Previously void methods silently swallowed error responses.
No consumer passes includeContent: true. Remove the flag, the conditional content fields on three RpcTreeNode variants, and the parameter threading through six internal functions.
…Call default case
…consistency with production code
…File + syncLocation
… after cross-project switch syncLocation updated sessionManager.getCwd() and getSessionDir() after a cross-project session switch, but left agentSession._cwd, tool cwds, and process.cwd() unchanged. This made session listing follow the switch while tool execution stayed in the original directory — worse than main where everything consistently reflects the launch directory. Remove syncLocation entirely. list_sessions scope:current now always returns sessions for the project directory pi was started in, matching the TUI session selector behaviour on main.
ffccabb to
94d7b4e
Compare
Reopening #1628 (auto-closed by bot).
Motivation
The RPC protocol already covers prompting, streaming, model/thinking configuration, compaction, forking, session switching, and message retrieval. What's missing is session discovery and tree-structured navigation, the operations the TUI's session selector and tree selector provide interactively.
This PR adds five new RPC commands that close that gap, plus a shared tool-call formatting module extracted from the TUI.
What this adds
RPC commands
Five new commands with typed client helpers, transport types, and docs:
"current"or"all". Each session item always includesallMessagesTextfor client-side search.rpc.md> Tree Node Types for node variants.AgentSession.navigateTreewith normalized options. Supports optional branch summarization and labeling.navigate_treebehavior.abortcommand only aborts the agent loop, not the separate summarizationAbortController.Command shapes live in
rpc-types.tsand are exported viamodes/index.tsfor SDK consumers.Shared
formatToolCallTool-call preview formatting was extracted from the TUI's
tree-selector.tsintocore/format-tool-call.tsand is now shared by both the TUI and RPC tree projection. Previews went from generic JSON truncation to human-readable bracket expressions:Intentionally deferred
Two session management operations that the TUI supports are not included:
rename_session: The TUI renames any session by temporarily opening the target file and writing asession_infoentry. The existingset_session_nameRPC command only works on the current session.delete_session: The TUI deletes sessions via thetrashCLI with a fallback tounlink. No RPC equivalent exists.Both are scoped out to keep this PR focused on read and navigate operations. They're straightforward to add as a follow-up.
Design decisions
Internal
toolResultsession entries carry only atoolCallId, not the tool name or arguments: those live on the originating assistant message. The projector resolves eachtool_resultagainst tool calls seen along its branch path, populating toolName, toolArgs, and formattedToolCall on the projected node. Branch-scoped resolution prevents cross-contamination when the sametoolCallIdappears in different branches.Iterative traversal: Tree projection and leaf resolution use explicit stacks instead of recursion. Deep trees (long sessions with many branches) can exceed call-stack limits under recursion.
Architecture
Command handlers are inlined in
rpc-mode.tscase bodies, matching the pattern of existing RPC commands. Two helper modules provide reusable logic:rpc-tree-projection.ts: projects the rawSessionTreeNodetree into transport-safeRpcTreeNodearrays, resolves tool-call metadata, and finds the projected leaf.rpc-command-wiring.ts: transport mapping (toRpcSessionListItem,toRpcNavigateTreeResult) and label normalization.syncLocation() is added to SessionManager, called after
setSessionFile()during session switches. The newlist_sessions(scope="current")command reads cwd and sessionDir to scope results; without this, switching sessions via RPC would leave those fields pointing at the previous project.Error handling:
navigate_treeandset_labelcatch thrown errors (invalid entry IDs) and return typed RPC errors. The remaining new commands cannot fail under normal operation and propagate exceptions to the top-level handler.