feat: start queued prompts when provider capacity returns - #10132
Open
saphid wants to merge 6 commits into
Open
Conversation
Contributor
ApprovabilityVerdict: Not approved Macroscope's review found this PR not approvable — This PR introduces a durable queued-prompt workflow with a background server worker, quota-based execution gate, restart recovery, attachment cleanup, and new web controls. It also changes default exhausted-provider send behavior and spans orchestration, persistence, contracts, client state, and UI, making human review appropriate. You can add or adjust custom eligibility rules. Learn more. |
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.
What Changed
When a subscription is exhausted, users currently have to return later and submit their prompt manually. This adds Start when available to web/desktop: the exhausted provider remains selectable, immediate Send and Enter are blocked, and an explicit action saves one prompt for the selected account.
The server persists the prompt, attachments, model/options and runtime mode. A visible wait survives client reload and server restart, offers cancellation, and releases only after fresh quota data confirms capacity. A reset timestamp alone does not start work. Accounts are never switched automatically.
Why
This is one queued-prompt workflow, independent of sidebar snooze (#9012) and account switching (#9181), following the request for a focused implementation in #8577. It spans persistence, orchestration, contracts, shared client state and composer controls; it is not a small diff.
Archiving or settling cancels the wait; automatic settlement cannot discard it. A wait must be cancelled before snoozing. Native provider sign-out remains available under exhaustion, and a rejected request does not overwrite an unrelated running turn.
Quota decisions use existing provider snapshots, including Codex/Claude model-specific subscription windows. Accounts without quota data keep normal sending. The new controls require the server capability; React Native receives updated contracts/shared state and server admission behavior, but no new mobile controls in this PR.
UI Changes
Before / after: exhausted quota leaves Send enabled on the base; the candidate disables it and offers an explicit wait. This GIF alternates two actual screenshots, three seconds each. It compares states, not motion.
Recorded controls: queue → reload → cancel → queue again → fresh capacity. The detail keeps the action labels readable in a narrow PR column; the overview below shows the saved prompt and resulting response in context.
Evidence uses isolated local servers and a synthetic Codex app-server fixture at 1280×800, dark appearance. Base:
6270a6f; candidate capture:66a76bf1a. The final head isaea4c1070. Later fixes affect auth commands, omitted model selections, image cleanup and cancellation across threads; this captured flow uses an explicit model, a text-only prompt and one thread, so those changes do not alter the demonstrated flow.The interaction GIFs are 15 fps derivatives of the real recording: 70.7 seconds trimmed to 16.0 seconds by removing idle intervals, with no playback-speed change (GIF frame-delay rounding yields 16.07 seconds). They demonstrate interaction, not a latency guarantee. Provider request logs confirmed one fixture dispatch. No real model call was made. Desktop shares the tested web UI; native shell and remote/tunnel transport were not separately exercised.
Supporting screenshots and full-resolution videos
Before: Send enabled under exhausted quota
After: usage notice, disabled Send, and wait action
Annotated interaction MP4 · Clean interaction MP4
Validation
MERGEABLE/CLEAN. Approvability is neutral and requests human review for feature scope.claude --model claude-opus-5 --effort high --print --permission-mode plan --tools Read,Grep,Glob --output-format json; exit 1, expired OAuth session. No Claude review occurred.Exact focused verification commands
CI=true vp test run \ apps/server/src/orchestration/Layers/OrchestrationEngine.test.ts \ apps/server/src/orchestration/Layers/ProviderCommandReactor.test.ts \ apps/server/src/orchestration/Layers/ProjectionPipeline.test.ts \ apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.test.ts \ apps/server/integration/orchestrationEngine.integration.test.ts \ packages/shared/src/modelUsageAvailability.test.ts \ packages/client-runtime/src/state/threadReducer.test.ts \ packages/client-runtime/src/state/entities.test.ts CI=true vp run --filter t3 --filter @t3tools/web \ --filter @t3tools/shared --filter @t3tools/contracts \ --filter @t3tools/client-runtime typecheckThe Claude rollback regression failed during review when implicit model selections became explicit and restarted sessions. Preserving the original dispatch semantics fixed it; the final combined run above passes that test.
Checklist
Implemented with OpenAI GPT-6 in the Codex harness.