Conversation
Pending permission/question prompts live only in server memory (a Deferred the tool blocks on) and are announced via a single fire-and-forget permission.asked/question.asked event. The SSE stream never replays them to a client that connects after the ask, and the TUI's bootstrap() hydrated everything except these. So detaching the TUI while a prompt was pending (or one raised while detached) left the agent blocked with no prompt shown and no way to answer short of forking the session. bootstrap() now calls hydratePending(), which fetches GET /permission and GET /question and seeds the stores via idempotent upserts that mirror the live *.asked event handlers (binary-search by id, skip if already present, honour auto-approve mode). Subagent prompts surface through the existing root-session aggregation in routes/session/index.tsx.
|
Thanks for your contribution! This PR doesn't have a linked issue. All PRs must reference an existing issue. Please:
See CONTRIBUTING.md for details. |
Reproduces the detach/reattach bug: with a pending permission and question on the server and no live *.asked event, the reattached TUI must fetch them on connect. This test fails on the pre-fix sync.tsx (store stays undefined) and passes with hydratePending(). Adds /permission and /question defaults to the sync fetch fixture.
|
Thanks for updating your PR! It now meets our contributing guidelines. 👍 |
Trim the two added comments to the file's sentence-case, why-focused convention: drop the server-internal Deferred detail and keep the client-side framing.
|
Automated PR Cleanup Thank you for contributing to opencode. Due to the high volume of PRs from users and AI agents, we periodically close older PRs using automated criteria so maintainers can focus review time on the most active and community-supported contributions. This PR was closed because it matched the following cleanup criteria:
PRs created within the last month are not affected by this cleanup. If you believe this PR was closed incorrectly, or if you are still actively working on it, please leave a comment explaining why it should be reopened. A maintainer can review and reopen it if appropriate. Thanks again for taking the time to contribute. |
Issue for this PR
Closes #36604
Type of change
What does this PR do?
Pending permission/question prompts only live in server memory (a
Deferredthe tool blocks on) and are announced with a singlepermission.asked/question.askedevent at ask time. The SSE stream only forwards events emitted after a client subscribes and never replays what's already pending, and the TUI'sbootstrap()hydrated everything except these two stores. So if you detach the TUI while a prompt is pending — or one is raised while you're detached — re-attaching shows no prompt even though the agent is still blocked, and the only way out is to fork the session.This adds
hydratePending()tobootstrap(), which fetchesGET /permissionandGET /questionon connect and seeds the stores. Inserts go through smallupserthelpers that mirror the existing*.askedevent handlers (binary-search by id, skip if already present), so hydration and any live events that arrive during bootstrap interleave without duplicating or clobbering. Auto-approve mode is honoured the same way the live handler does it. Subagent prompts surface via the existing root-session aggregation, so no extra work there.How did you verify your code works?
Added a test in
sync-live-hydration.test.tsxthat seeds a pending permission and question on the mock server, emits no live event, and asserts the store is hydrated on connect. It fails against the currentsync.tsx(sync.data.permission[sessionID]isundefined) and passes withhydratePending(), so it pins the exact regression. Fullpackages/tuisuite passes (192 pass / 0 fail),bun run typecheck(tsgo) is clean, andoxlintreports 0 errors on the changed files. Both list calls are wrapped in.catch()so an older server without these endpoints falls back to current behaviour.Screenshots / recordings
N/A — no visual change; this only re-populates prompts that already render today.
Checklist