Skip to content

feat(acp): source /resume picker from agent session/list#518

Merged
CSRessel merged 2 commits into
mainfrom
feat/acp-session-list-resume-picker
Jun 25, 2026
Merged

feat(acp): source /resume picker from agent session/list#518
CSRessel merged 2 commits into
mainfrom
feat/acp-session-list-resume-picker

Conversation

@nori-sessions

@nori-sessions nori-sessions Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Summary

  • When the live ACP agent advertises both session/list and load_session, the in-session /resume command now sources its picker rows from the agent (via ACP session/list) and resumes the selected session over ACP session/load, instead of the local transcript store. This is generic to any agent advertising the capability — not Nori/Codex-specific.
  • Columns map title→name (session_id fallback), cwd→description, updated_at→relative time. Empty/error results surface a clear cell instead of opening an empty picker.
  • Agents that advertise session/list but not load_session fall back to the existing transcript-backed picker. An agent-sourced resume carries no local transcript and relies entirely on server-side session/load replay; without load_session the agent would silently start a blank session.
  • AcpConnection::list_sessions() drains cursor pagination internally under a page cap, so a misbehaving agent that keeps returning next_cursor cannot pin the picker in an unbounded loop.
  • The standalone nori resume startup picker is unchanged (still transcript-backed).

session/resume, session/close, and session/delete are stabilized in the ACP spec; this PR is deliberately scoped to session/list + load_session-based resume and leaves those verbs as follow-ups. session/resume is the natural next one: it reconnects to a listed session without replaying history (unlike session/load), so adding it would widen the session_list && load_session gate above to also cover agents that can reconnect but don't implement load_session (today those fall through to the transcript-backed picker). session/close (free an active session's resources) and session/delete (remove from session/list history) are session-lifecycle operations, not /resume picker extensions, and belong on their own surface. Cloud-mode gating is a separate follow-up.

Test plan

  • cargo test -p nori-tui --lib resume — 23 passing (incl. new acp_resume_session_picker_snapshot insta snapshot and acp_resume_session_picker_* unit tests)
  • cargo test -p nori-acp --lib list_sessionstest_list_sessions_maps_agent_session_info passing
  • cargo build --bin nori && cargo build -p mock-acp-agent
  • cargo test -p tui-pty-e2e --test acp_resume_session_list — drives the real nori binary against the mock agent: lists the agent's two sessions and selects + resumes one (exercises the load_session path end-to-end)
  • cargo fmt + cargo clippy --all-features --tests -p nori-acp -p nori-tui clean
  • docs.md updated (acp, acp/connection, tui, mock-acp-agent)

When the live ACP agent advertises both `session/list` and `load_session`,
the in-session `/resume` command sources its picker rows from the agent
(via `session/list`) and resumes the selected session over ACP
`session/load`, instead of the local transcript store. This is generic to
any agent that advertises the capability.

Agents that advertise `session/list` but not `load_session` fall back to
the transcript-backed picker, since an agent-sourced resume carries no
local transcript and depends entirely on server-side `session/load`
replay -- without it the agent would silently start a blank session.
`list_sessions()` drains cursor pagination under a page cap so a
misbehaving agent cannot pin the picker in an unbounded loop. The
standalone `nori resume` startup picker is unchanged.
🤖 Generated with [Nori](https://noriagentic.com)

Co-Authored-By: Nori <contact@tilework.tech>
@CSRessel CSRessel changed the title feat(tui,acp): source /resume picker from agent session/list feat(acp): source /resume picker from agent session/list Jun 25, 2026
acp_resume_session_picker_params never used its AppEventSender argument --
each row's SelectionAction receives the sender when invoked, not at build
time. Remove the dead parameter along with its call-site and test plumbing.
🤖 Generated with [Nori](https://noriagentic.com)

Co-Authored-By: Nori <contact@tilework.tech>
@CSRessel CSRessel merged commit de3476a into main Jun 25, 2026
2 checks passed
@CSRessel CSRessel deleted the feat/acp-session-list-resume-picker branch June 25, 2026 21:50
CSRessel added a commit that referenced this pull request Jul 6, 2026
…se and errors (#536)

## Summary
🤖 Generated with [Nori](https://noriagentic.com/)

- **`session/resume` support**: `AcpBackend::resume_session` gains a
third branch — when the agent advertises `sessionCapabilities.resume`
but not `session/load` (the nori-cloud contract), reattach via
`session/resume` with **no** silent `session/new` fallback (on a cloud
agent that would claim a brand-new VM). Branch order: `load_session`
(replay) > `session_resume` (live reattach) > client-side transcript
replay. The shared MCP-registration block was deduped into one helper
across the four branches.
- **`/close` command**: capability-gated on `sessionCapabilities.close`;
releases the live session over `session/close` and starts a fresh chat
**only on success**. While the close is in flight, session-switching
commands (`/new`, `/resume`, `/agent`, …) are blocked so the deferred
`NewSession` can't clobber a conversation the user switched to; failures
surface via `AppEvent::SessionCloseFailed` with the enhanced message and
unblock the widget.
- **Structured ACP error mapping**: `categorize_acp_error_chain` walks
the anyhow chain for the structured `acp::Error`, maps by JSON-RPC code
(`-32000` auth_required, `-32002` session-not-found, `-32010`
unreachable (retryable), `-32012` not-resumable, `-32014`
no-active-session, `-32015` already-active), code wins over substring
heuristics, and surfaces `error.data.detail` (e.g. the deployment's
exact login hint) — used by spawn/resume/close and prompt-failure paths.
- **Picker fixes**: the /resume picker's agent-list gate is now
`session_list && (load_session || session_resume)` — cloud sessions
actually appear (PR #518's feature was gated off for the one agent that
needed it). The cloud sentinel cwd `/` is hidden from row descriptions
and search.
- **Capabilities plumbing**: `AgentCapabilitiesView` gains
serde-defaulted `session_resume`/`session_close`; mock-acp-agent gains
env-gated resume/close handlers + failure switches for tests.

This is the client half of `nori-cloud-cli-followup.md` (agent/router
half landed in nori-sessions PR #1045). Capability-gated throughout —
agents that don't advertise the new capabilities behave exactly as
before.

## Decisions to review / known follow-ups
- **`/close` = swap, not just release**: on success we start a new chat,
which (for cloud) immediately claims a fresh VM — consistent with
existing `/new` semantics, but "release and idle" isn't expressible
today. Flagging the UX question.
- `/close` shows in the slash popup for all agents (gated with an
explanation on invoke); wiring it into the `disabled_builtin_commands`
greying mechanism (like `/goal`) is a clean follow-up.
- Prompt-path errors show `data.detail` as raw JSON via `{err:#}` rather
than the curated treatment; cosmetic inconsistency.
- The runtime `CloseSession` command arm is covered indirectly
(connection tests below, widget tests above, harness close-failure test
through `close_active_session`).

## Test Plan
- [x] TDD: failing tests first (compile-RED for new API, runtime-RED for
picker gate/resume branch), then green
- [x] New coverage: structured categorization unit tests; connection
resume/close round-trip + failure tests against the real mock-agent
subprocess; harness resume-not-load / no-fallback /
enhanced-close-failure / capabilities-projection tests; TUI picker gate
(positive + negative), /close ordering + failure + gating tests; `/cl`
slash-popup snapshot
- [x] `cargo test` (full workspace) green; `cargo test -p tui-pty-e2e`
green; `just fmt` + `just fix` clean
- [x] Live TUI drive (tmux): `/resume` shows agent-sourced sessions
under the cloud contract → select reattaches via `session/resume` →
`/close` prints "Session closed (released)." and starts a fresh chat
- [ ] Live loop against a real `nori-handroll acp --type cloud` + broker
(needs Sessions auth; verified against the mock contract only)

Share Nori with your team: https://www.npmjs.com/package/nori-skillsets

---------

Co-authored-by: Nori <contact@tilework.tech>
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