Skip to content

NOJIRA: Extend ACP support: list and resume sessions#205

Open
pmateusz wants to merge 8 commits into
masterfrom
nojira-list-resume-acp-sessions
Open

NOJIRA: Extend ACP support: list and resume sessions#205
pmateusz wants to merge 8 commits into
masterfrom
nojira-list-resume-acp-sessions

Conversation

@pmateusz
Copy link
Copy Markdown
Contributor

@pmateusz pmateusz commented May 10, 2026

Why

ACP clients reconnect frequently and need to resume prior conversations with full context. Without listSessions/loadSession, every reconnect is a blank slate.

What

  • listSessions: enumerates persisted sessions, newest-first, title fallback.
  • loadSession: validates request (rejects non-empty mcpServers, already-live IDs, mismatched sessionId/cwd), registers session,
    replays transcript.
  • Replay emits user_message_chunk, agent_message_chunk, agent_thought_chunk, tool_call, tool_call_update as direct connection
    notifications (bypasses session emitter so extensions don't double-count). ANSI stripped; thinking gated by live hide-thinking
    setting.
  • initialize advertises loadSession: true and sessionCapabilities.list.
  • scripts/verify-acp-load.mjs: end-to-end test driving a real LLM via JSON-RPC; verifies reconnect-and-resume and ACP↔CLI persistence

Kimchi Summary

What changed

Adds listSessions and loadSession support to the ACP server so clients can enumerate and resume persisted sessions with full transcript replay (text, thinking, and tool calls). Includes a new integration test that validates session continuity across ACP reconnects and ACP↔CLI round-trips.

Why

ACP clients need to discover existing conversation threads and resume them with historical context intact, rather than starting a fresh session on every connection.

Key changes

  • src/modes/acp/server.ts: Implemented listSessions (sorts newest-first with title fallback), loadSession (validates request, replays transcript, registers session), and replayTranscript/replayAssistantBlocks (emits user_message_chunk, agent_message_chunk, agent_thought_chunk, tool_call, tool_call_update). Added toAcpSessionInfo, stripAnsi, collectToolResults, and default disk-backed sessionLister/sessionLoader.
  • src/modes/acp/server.ts: initialize now advertises loadSession: true and sessionCapabilities.list.
  • src/modes/acp/server.test.ts: Added comprehensive tests for listing, loading, replay behaviors (text coalescing, ANSI stripping, hide-thinking redaction, tool-call kind matrix), and helper utilities (shouldEmitThinking, stripAnsi, userMessageText, assistantMessageText).
  • src/extensions/hide-thinking.ts: Exported isHideThinkingEnabled() so the ACP replay path shares the live TUI's hide-thinking setting without relying on fragile synthetic probes.
  • scripts/verify-acp-load.mjs: New end-to-end integration test driving a real LLM via JSON-RPC to verify reconnect-and-resume and cross-client session persistence.

Impact

  • loadSession rejects non-empty mcpServers (invalidParams) and already-live session IDs (invalidRequest).
  • Loaded sessions validate on-disk sessionId and cwd against the request; mismatches are rejected and the session is disposed to prevent accidental re-rooting or double-opens.
  • Historical turns are replayed as direct connection notifications, bypassing the session event emitter so extensions do not double-count old messages.
  • Replayed text and thinking blocks are stripped of ANSI escape codes before transmission.

Kimchi Summary

What changed

Adds ACP listSessions and loadSession support so clients can enumerate and resume persisted disk sessions. Historical transcripts are replayed as session/update notifications covering user text, assistant text/thinking, and tool calls (paired with their results). A new integration test verifies reconnect-and-resume and ACP↔CLI round-trip behavior end-to-end.

Why

ACP clients (e.g., Zed) need thread persistence and resume to avoid losing conversation context across reconnects or when switching between ACP and CLI modes.

Key changes

  • src/modes/acp/server.ts: Implements listSessions with deduplication across additionalDirectories, newest-first sorting by updatedAt, and explicit nextCursor: null. Implements loadSession with atomic registration, header sessionId validation, cwd verification, and full transcript replay via replayTranscript/replayAssistantBlocks.
  • src/modes/acp/server.ts: Adds defaultSessionLister and defaultSessionLoader to bridge pi's SessionManager with ACP requests, including resolveSessionPathById to locate timestamp-prefixed JSONL files.
  • src/modes/acp/server.ts: Adds stripAnsi, userMessageText, assistantMessageText, collectToolResults, and shouldEmitThinking helpers for sanitizing and structuring replayed content.
  • src/extensions/hide-thinking.ts: Exports isHideThinkingEnabled() so the ACP replay path respects the hide-thinking setting without probing filterThinkingForDisplay with a fragile synthetic wrapper.
  • scripts/verify-acp-load.mjs: New integration test script that validates ACP reconnect-and-resume and CLI round-trip session sharing using real LLM prompts.
  • src/modes/acp/server.test.ts: Extensive unit tests added for listSessions, loadSession, replay semantics (text coalescing, ANSI stripping, thinking redaction, tool-call pairing), and helper functions.

Impact

  • initialize now advertises agentCapabilities.loadSession: true and sessionCapabilities.list.
  • loadSession rejects requests with non-empty mcpServers or already-loaded session IDs to prevent JSONL interleaving and double-registration.
  • Interrupted historical tool calls without persisted results replay with status failed and empty content instead of hanging as in_progress.
  • Contiguous assistant text blocks are coalesced into single agent_message_chunk updates during replay.

pmateusz added 5 commits May 9, 2026 17:21
Advertise sessionCapabilities.list, dispatch listSessions through a
SessionManager.list/listAll seam, and map pi SessionInfo to ACP. Pi's
default lookup reads PI_CODING_AGENT_DIR, not KIMCHI_CODING_AGENT_DIR,
so the cwd-scoped path threads kimchi's agentDir explicitly via an
inlined copy of pi's getDefaultSessionDir encoding (not re-exported).
phase 3: replay walker emits tool_call + tool_call_update for historical
calls (status from persisted result, failed for orphans), agent_thought_chunk
for non-redacted thinking gated by hideThinkingBlock, no-ops for
compaction/branch_summary/model_change/custom entries.

fixes from review:
- toAcpSessionInfo: empty `name` falls through to firstMessage (?? skipped "")
- replay: coalesce contiguous text blocks into one chunk per natural segment
- replay: strip ANSI escapes from text/thought content (ACP can't render)
- shouldEmitThinking: read setting directly; wrapper probe broke on inner </think>
- collectToolResults: capture details/toolName so rawOutput shape matches live
- loadSession: reject + dispose on header-id vs requested-id mismatch
Pi writes `<isoTimestamp>_<sessionId>.jsonl`; loader was looking up bare
`<sessionId>.jsonl` and never resolved a real session. Scan the cwd dir
for the suffix (with bare-form forward-compat fallback). Distinguish
ENOENT (→ "not found") from other errno (→ surface as invalidParams
with the underlying message) so EACCES/EMFILE don't masquerade as
missing sessions. Found via verify-acp-load.mjs.
Two scenarios driving real LLM via stdio JSON-RPC:
- A) reconnect-and-resume: ACP creates session, kill, reconnect,
  session/load, follow-up recalls token from prior context.
- B) ACP↔CLI round-trip: ACP turn 1, CLI `--session <id>` turn 2 via
  piped stdin, ACP reload+turn 3 recalls both tokens, fresh ACP reload
  asserts ≥3 user/agent chunks persisted on disk.

Auto-rejects permission prompts so unexpected tool use fails loudly
instead of hanging. Watchdog sized off PROMPT_TIMEOUT_MS so worst-case
slow LLM doesn't trip before per-prompt timeouts can flag the slow call.
@kimchi-review
Copy link
Copy Markdown

kimchi-review Bot commented May 10, 2026

Kimchi Code Review

Property Value
Commit acdeb47
Author @pmateusz
Files changed 0
Review status Completed
Comments 6 (1 info, 5 warning)
Duration 191s

Summary

📊 Review Score: 78/100 (overall code quality — 0 lowest, 100 highest)
⏱️ Estimated effort to review: 4/5 (1 = trivial, 5 = very complex)

🧪 Tests: yes — Extensive unit tests added for listSessions, loadSession, replay logic, and helper functions in server.test.ts. A new integration test script verify-acp-load.mjs covers end-to-end reconnect-and-resume and cross-tool round-trip scenarios. Coverage is thorough for both happy paths and edge cases (tool calls, thinking blocks, ANSI stripping, concurrent cancel, error unwinding).

📝 Found 6 issue(s). See inline comments for details.

What to expect

Kimchi will analyze the changes in this pull request and post:

  • A summary of the overall changes
  • Inline comments on specific lines with findings categorized by issue type

The review typically completes within a few minutes. This comment will be updated once the review is ready.

Interact with Kimchi
  • @kimchi review — re-trigger a full review on the latest commit
  • @kimchi summary — regenerate the PR summary
  • @kimchi ignore — skip this PR (no review will be posted)
  • Reply to any inline comment to ask follow-up questions or request clarification
Configuration

Reviews are configured by your organization admin.
Review instructions, excluded directories, and severity thresholds can be adjusted per repository in the Kimchi dashboard.


Powered by Kimchi — AI-powered code review by CAST AI

Copy link
Copy Markdown

@kimchi-review kimchi-review Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📊 Review Score: 78/100 (overall code quality — 0 lowest, 100 highest)
⏱️ Estimated effort to review: 4/5 (1 = trivial, 5 = very complex)

🧪 Tests: yes — Extensive unit tests added for listSessions, loadSession, replay logic, and helper functions in server.test.ts. A new integration test script verify-acp-load.mjs covers end-to-end reconnect-and-resume and cross-tool round-trip scenarios. Coverage is thorough for both happy paths and edge cases (tool calls, thinking blocks, ANSI stripping, concurrent cancel, error unwinding).

📝 Found 6 issue(s). See inline comments for details.

Comment thread scripts/verify-acp-load.mjs
await delay(200)
} finally {
await killProcess(a.proc)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️⚠️ Error Handling

The CLI leg does not handle 'error' events on cli or cli.stdin. If the child exits before reading stdin, the subsequent cli.stdin.end(cliPrompt) emits an EPIPE error that becomes an uncaught exception and aborts the test.

💡 Suggestion: Add cli.on("error", (e) => { process.stderr.write("cli error: " + e + "\n"); }) and cli.stdin.on("error", () => {}) immediately after spawn, before calling cli.stdin.end(cliPrompt).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added cli.on("error", ...) and cli.stdin.on("error", () => {}) before end(cliPrompt). Exit-code check remains the source of truth.

Comment thread src/modes/acp/server.ts
Comment thread src/modes/acp/server.ts Outdated
Comment thread src/modes/acp/server.ts
Comment thread src/modes/acp/server.test.ts
pmateusz added 3 commits May 10, 2026 21:19
- loadSession: guard concurrent loads of same id via loadingSessions Set
- replayTranscript: hoist isHideThinkingEnabled() out of per-block loop
- replayTranscript: null-entry guard, mirroring collectToolResults
- perf test: relax 1s -> 3s, retitle as regression guard
- verify-acp-load.mjs: catch on conn.prompt before race; cli error/EPIPE handlers
- comments: strip phase/PRD references throughout
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