Repository navigation
fix(session): start a session whose session_start asks a dialog - #1682
Merged
Merged
Conversation
…1678) An extension whose session_start awaits ctx.ui.confirm/select/input/editor deadlocked a fresh composer: /api/agent/new waited in get_state for session_start, while the dialog reached the page only through the event stream the page opens after that reply. pi's TUI starts its UI first so session_start may ask one. - get_state still waits for session_start, but not past a blocking dialog; the reply then reports the model and level as they are. - ensureNewSession() opens the event stream once it has the id, so the replayed dialog shows before get_commands, a prompt or a ! command wait on session_start. - Answering a dialog in a fresh composer reads get_state again, since the handler may pick the model next. - shutdown() gives a session_start dialog the cancelled answer and bounds the binding wait by the session_shutdown deadline, so idle cleanup can reap a wrapper nobody answered. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A fresh composer holds the event stream only while get_commands waits on session_start (withSessionStartStream()), then lets go of it: no stream is left open in a background tab that typed "/", and a session a tool change rebuilt gets its own stream with the next "/". - A deferred startup model (#1240) is switched to as the binding's last step, so a session_start dialog no longer holds startRpcSession() (new sessions and reopened ones), and what waits for the binding sees it. - closingRpcSessionWait() covers shutdown()'s two deadlines. - An extension dialog asked after destroy() is refused at once, as after Stop, instead of staying pending forever. - Answering a dialog in a fresh composer also refreshes the system prompt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#1678) - A tool change that rebuilds a session whose deferred-model switch a session_start dialog still holds rebuilds on that model and level (startupModel()), not on the stand-in. - A model picked before the switch stands, and a level picked meanwhile replaces the target's (pendingStartupModel()). - A closing wrapper skips the switch: it would write to a session being shut down. - A subagent run waits for its session_start before prompting, with or without an MCP host (prepareMcpForRun()); Stop ends the wait. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#1678) Typing "/" in a fresh composer shows a dialog session_start waits on, and the dialog's layer (z-index 90) sat under the "/" menu (120) while it took the focus: the user saw only the menu, and Enter answered the hidden dialog with Cancel. The history, "/" and "@" menus now hide while an extension dialog or panel is open, their keys pass through, and they come back once it closes, with their height measured again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ball6847
added a commit
to ball6847/pi-web
that referenced
this pull request
Nov 14, 2026
Upstream: pi 1.5.2, an Agents strip while the main chat holds a subagent (agegr#1681), a session whose session_start asks a dialog (agegr#1682), the built-in llama.cpp provider (agegr#1629), tool clocks on the cards, Settings › General's built-in tool switches (agegr#1611), and 80 fixes. Conflict resolutions: - The reveal of the open session's row is now upstream's single effect (agegr#1595), extended for the flat list (its rows are keyed by the session, not by a group) and for the keyboard chord that asks for the row with the focus. Our older reveal effect, its refs and the family map it alone used are gone. - MessageView: upstream's version (tool clocks, a call that never ran, the result footer), with our #31 naming back: the Agent card's link opens the child's chat, so the prop is `onOpenSubagentChat` and takes only the session id. - ChatWindow and SessionChatPanel: upstream's `onSessionEnded` joins our kanban props and the `onOpenSubagentChat` name. - useAgentSession: upstream's `onSessionEnded` and the `translate` dependency join our kanban pick. - subagents.ts: upstream folded SNAPSHOT_BUILTIN_TOOLS into BUILTIN_TOOLS; our kanban exception stays. - rpc-manager.ts: upstream's llama.cpp extension joins our kanban extension. - e2e/run.mjs: upstream's directory picker joins our kanban card menu. - AGENTS.md and the docs: unions — upstream's new files (unread markers, user attention, tool clock, the horizontal strip) with our kanban block, the flat view, the Quick Open palette and the built-in tools switch. Verified: tsc 0, lint 0 errors, 3951/3956 tests (the 3 failures are this machine's: two model-list assertions see the KIMI/MINIMAX/XIAOMI keys as built-in providers, and the agegr#804 chat-only refusal no longer fires under pi 1.5.x — it fails with upstream's own lib/rpc-manager.ts too).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #1678.
An extension whose
session_startawaitsctx.ui.confirm/select/input/editordeadlocked a fresh composer./api/agent/newwaited onget_state,get_statewaited for extension binding, binding waited for the dialog, and the dialog only reaches the page through the event stream, which the page opens once/api/agent/newhas answered. pi's interactive mode starts its UI beforesession_startfor exactly this reason.Start path
get_statestill waits for binding, but stops waiting once a blocking extension dialog is pending, and reports the state as it is at that moment. With no dialog, the reply is the same as before (the model aftersession_start).startRpcSession()uses the same wait. A deferred startup model (a provider an extension registers atsession_start, New session silently falls back to another model when the default model is provided by an extension provider #1240) is now switched as the last step of the first binding, not beforestartRpcSession()returns, so a session on such a model no longer hangs either, fresh or resumed. The switch is skipped when the user or asession_starthandler picked another model meanwhile, and on a closing wrapper. A tool change that rebuilds the session during the dialog carries over the pending target, not the stand-in.get_commands(typing/) is the one request a fresh composer sends that waits for binding without an open stream. It now holds the stream while it is out (withSessionStartStream()), so the dialog shows, and closes it again if the composer is still unpromoted. Prompt and!already open the stream. After a dialog is answered, a fresh composer re-reads the model, level and system prompt.Shutdown
shutdown()waited for binding with no deadline, so idle cleanup could never reap a wrapper stuck in asession_startdialog, and each retry left another one. It now gives open dialogs the cancelled answer and bounds the wait withSESSION_SHUTDOWN_DEADLINE_MS;closingRpcSessionWait()covers both deadlines.destroy()is refused at once, as after Stop, so a latesession_starthandler can't wait forever.Subagents:
prepareMcpForRun()now waits for the child'ssession_starteven without an MCP host, and Stop ends that wait.Not changed: the sidebar's awaiting-input dot for a not-yet-running wrapper, and
/api/agent/newwithtype: "prompt"(the page always sendsensure_sessionfirst).Checks
tsc --noEmit,npm run lint,npm teston this branch (3497 tests, 0 failed). The rpc-manager, subagent and hooks suites also pass on Node 22.lib/rpc-manager-session-start-dialog.test.mjs,hooks/session-start-dialog.test.mjsand a real-SDK case inlib/rpc-manager-deferred-model.integration.test.mjs. The dialog tests time out on the old code.get_stateafter start went from still pending at 3 s to about 1 ms. Shutdown with the dialog unanswered went from still pending at 8 s to 2 ms.🤖 Generated with Claude Code