Skip to content

feat(native-chat): /clear waits in line while the agent works, and runs when its turn comes - #25842

Closed
brennanb2025 wants to merge 79 commits into
mainfrom
brennanb2025/chat-clear-waits-in-line
Closed

brennanb2025 wants to merge 79 commits into
mainfrom
brennanb2025/chat-clear-waits-in-line

Conversation

@brennanb2025

@brennanb2025 brennanb2025 commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 26 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​1754 $\color{#cf222e}{\Huge{\mathbf{−}}}$​76 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​1678
Prod 61 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​1260 $\color{#cf222e}{\Huge{\mathbf{−}}}$​303 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​957

ELI5

A native chat currently refuses /clear while the agent is busy, so you have to wait and send it again. With the message queue enabled, this change makes /clear wait as a visible card; when its turn comes, Orca clears the chat and runs the messages queued after it in the fresh chat, in order.

The message queue remains disabled in shipped builds. Drafts following a cleared chat and the wording fixes below also apply to today's immediate /clear. This PR is based on main; the /compact queue work in #25704 has merged.

What Changed

Waiting /clear

  • Sending /clear during a turn, while an earlier message is unanswered, or while a question or approval is pending creates a command card immediately. It clears nothing yet and shows no extra notice.
  • The host runs the clear itself when the queue reaches it. It stops the old agent, records the replacement chat, moves the tab on desktop and phone, and closes the source. The command never becomes an agent submission or steers into a running turn.
  • Messages and command cards queued after it move in order to the replacement and normally run automatically. An immediate clear over a held queue still carries earlier messages paused and drops earlier command cards. Cards that failed to convert keep their individual hold; reopening after a crash also preserves the source cards' wait for the next accepted turn or Resume.
  • Background tasks and a lease handoff keep the card waiting. Their ending wakes the existing drain. Desktop says “Waiting for background tasks to finish” only when those tasks alone hold the next card; the phone shows Delete without that caption.
  • Command cards have no Edit. During a turn they offer Delete. At rest, Send appears when a pause, pending prompt, individual hold or earlier card requires action. A next card that will run automatically offers Delete.
  • A waiting /clear puts later text messages behind it even with queue follow-ups disabled. A card held on its own does not force that setting.
  • A genuine clear failure keeps the card with “This command didn't run. Try it again.” Later cards wait for a retry using Send or another /clear, or for Delete, so they are not sent into the wrong context.

Every clear, including today's immediate command

  • Unsent text and images follow the host's replacement-tab link into the new composer, appended after its current draft. Desktop waits for saved drafts to load. A lone /clear or /compact does not follow.
  • While any tab still shows a reopened old chat, its drafts and sends stay with it. Once that tab closes, the link can carry its remaining draft.
  • Desktop uses main's single pending send per chat. While the original request is unresolved, its bubble follows the replacement view with “Sending…” without joining the new chat's queue or Stop count. The original sender keeps its deadline and makes one request. A recorded message leaves without returning text; a refused message returns to the composer with its reason; a lost answer returns text with the existing instruction to check the chat before sending again. The carry issues no second request.
  • The replacement does not recover saved sends by reading the old chat. A message carried and executed in the fresh chat leaves no acceptance in the old outline, so that outline cannot prove the message was never sent.
  • On the phone, a send refused because the chat was cleared says “The chat was cleared before your message went out. It's back in the composer.” A /clear pressed on a chat already cleared says to open the current conversation, without adding a second command failure sentence.

How it works

The host classifies a person's /clear on the host, just as it does /compact, so internal messages cannot consume the person's reserved queue capacity. There is no new client-supplied classification field. Admission, Send and automatic draining use the existing queue gate, including main's provider-starting hold; copied follow-ups are accepted by the replacement host before delivery and wait until its agent has finished starting.

A queued clear uses the existing command card and committed clear record. The card's request ID links them, so a repeated request can answer from the completed clear without running another one. The source becomes superseded before any later send can enter it. The tab publication and source close now share one host step after the serialized clear and command latch have ended, covering immediate commands, the drain and a card's Send.

The replacement record, initial journal, carried cards, source withdrawal and tab move now share one transaction in the execution host's existing SQLite database. A copy, withdrawal or record-write failure rolls the clear back; the source card can be retried, and no partial replacement becomes visible. Cards keep their IDs and destination-scoped fingerprints, retain their source order, and land before replacement work can be admitted. There is no carry-repair path, expiring receipt used as delivery proof, or durable carry obligation. Source reopen pauses are preserved through the existing journal marks; an accepted turn or Resume releases them. The first open stays under the fresh replacement's lock, so it neither treats that new conversation as a restart nor admits a racing send ahead of its carried cards. Desktop, phone and the host use the same first-actionable-card selector: individual holds are skipped, while returned cards and queue pauses remain barriers.

Clients request queued /clear only when the host advertises both the queue capability and agent-session.queued-clear.v1. Older clients and hosts retain their existing command behavior. The execution host owns the clear, queue and tab publication for local, folder and SSH workspaces.

Why

The host already owns ordered delivery and re-derives whether the next card can run. Extending that drain gives every client the same visible command and survives a restart without a separate pending-clear flag. Only Orca can replace a conversation and move its tab, so handing /clear to the provider would not perform the requested action.

Following messages run in the replacement because they were written after the queued clear. They wait behind a failed clear because sending them into the old context would change their meaning. Racing sends are refused against their original chat and returned through the existing composer path, rather than silently redirected to another conversation.

Intentional choices are the new-chat behavior already used by Orca, waiting for observable background work or handoff completion, holding later cards behind a failed clear, and using the existing composer notice for returned text. Real failures require an explicit action; there is no timed retry with no evidence that the failure has gone away.

Temporary limits remain: the queue must launch before waiting /clear ships; older hosts must update; mobile does not show the background-task wait; a handoff has no dedicated card caption; the tab exposes only the direct replacement, so two clears before the intermediate pane renders can leave the oldest draft behind; and the text-only queue does not hold image messages behind the command. Follow-ups are the queue launch, publishing a shared wait reason and complete replacement chain, and queueing image messages. Drafts typed after reopening an old chat can also follow once its tab closes; distinguishing them requires the host to publish when the clear happened alongside the replacement link.

Linked Issue

Follow-up to #25704's remaining busy /clear refusal. #25704 is already merged and is not a dependency.

Visual Proof

Fresh coordinator review and isolated desktop/mobile QA are pending. No app was launched in this fix pass. The queue-only interaction remains disabled in shipped builds; the immediate-clear draft and wording changes also need live verification.

Testing

  • Queued Node, renderer and CLI typechecks pass.
  • Targeted root tests cover every changed test file plus queue messages and holds, mutation handling, command turns, compaction, provider startup, the main sender and legacy recovery, and record-store behavior. The round-6 fix passed all 64 root suites (816 tests). After the authorized main sync, all 10 targeted card, clear and rejection suites passed (229 tests), with two workers.
  • The two touched mobile suites pass after the main sync: 53 tests, with two workers.
  • Regression tests reproduce the receipt-expiry duplicate, carried-card order inversion, missing Sending status, false saved-send recovery and held-card eligibility before the fix. Copy, source-withdrawal and record-write faults now roll back the clear; retry preserves order and restart holds.
  • Replacement tests cover draft loading and append order, reopened history, the original request's accepted/refused/lost answers, host-card settlement, absence of cross-conversation saved-send recovery and absence of a second send.
  • Full local static checks pass 21 of 22 checks. The sole failure reports five existing mobile dependency cycles in four files identical to the tested main; lint, changed-code checks, localization and other checks pass. The authorized final main sync preserves account/provider wording and duplicate-failure suppression on both clients alongside the clear-card rule. CI passes on final head 2e98f491678: all 16 executed checks succeeded, including all five Node shards, static analysis, relay integration, wire compatibility and packaging; 16 checks were skipped. The PR remains draft.
  • Platform exercised: macOS unit and integration tests. Linux, Windows, SSH and rendered UI have not been exercised locally in this fix pass.

Agent skill upstream boundary

  • Not applicable: this change copies no upstream skill-installer material.

Notes

There are no dependency, lockfile, schema-migration, path, shortcut or git-provider changes. Cleanup failures are reported without reversing the user's completed clear. The PR remains draft for the full review and UI evidence.

…nly; a /compact behind an unsent message waits for it quietly
…mmand card offers Send while the agent is idle; /compact behind an unsent message is not taken
…mand-waits-in-line

# Conflicts:
#	src/main/native-chat/agent-session-wire/structured-agent-session-queued-mutations.ts
…y; refuse a queued command without a hand-off, with its reason; a queue send on its way reads as sending
…d at once; a spent card's wait says to run it again; only /compact or /clear give way to returned text
…mand-waits-in-line

# Conflicts:
#	src/renderer/src/components/native-chat/use-structured-agent-session.ts
… a draft follows its chat into the one /clear replaced it with; a send a /clear raced comes back to the composer
…hub.com/stablyai/orca into brennanb2025/chat-clear-waits-in-line

# Conflicts:
#	src/main/native-chat/agent-session-wire/structured-agent-session-host-types.ts
#	src/main/native-chat/agent-session-wire/structured-conversation-command.ts
…under the line limit; the send capabilities get their own module
…hub.com/stablyai/orca into brennanb2025/chat-clear-waits-in-line

# Conflicts:
#	src/main/native-chat/agent-session-wire/structured-agent-session-host-types.ts
#	src/main/native-chat/agent-session-wire/structured-agent-session-queued-compact.test.ts
#	src/main/native-chat/agent-session-wire/structured-conversation-command.ts
#	src/main/runtime/structured-agent-session-runtime.ts
#	src/shared/protocol-version.ts
#	src/shared/rpc-contract/structured-agent-session-params.ts
… own unsent message says it is still being sent
… a resent queued /clear answers from what it asked for
…nt beside the others; main's send capabilities back to its text
…off and then runs; a resent /clear answers with what ran; carried cards keep their restart pause; a send during the close meets the clear
…ive in one module; the queue's wakes are its own
…ws the host's tab link: its draft (never the /clear itself) and unsent messages come to the new chat, said once there
…mand-waits-in-line

# Conflicts:
#	src/renderer/src/components/native-chat/use-structured-agent-session.ts
…works; the phone's follow rule takes the chat tab type
…k message on screen; the in-doubt case says so
…2025/chat-clear-waits-in-line

# Conflicts:
#	mobile/src/session/mobile-structured-agent-session-host-support.test.ts
#	mobile/src/session/mobile-structured-agent-session-host-support.ts
#	mobile/src/session/use-mobile-native-chat-controller.ts
#	mobile/src/session/use-mobile-structured-agent-session-prompt-cancel.test.tsx
#	mobile/src/session/use-mobile-structured-agent-session-queued.test-fixture.ts
#	src/main/native-chat/agent-session-wire/structured-agent-session-queued-mutations.ts
#	src/renderer/src/components/native-chat/structured-agent-session-queued-cards.ts
#	src/renderer/src/components/native-chat/structured-agent-session-withdrawn-message-restore.ts
#	src/renderer/src/components/native-chat/use-structured-agent-session-outbox.ts
#	src/renderer/src/components/native-chat/use-structured-agent-session.ts
#	src/renderer/src/i18n/locales/en.json
#	src/renderer/src/i18n/locales/es.json
#	src/renderer/src/i18n/locales/fr.json
#	src/renderer/src/i18n/locales/ja.json
#	src/renderer/src/i18n/locales/ko.json
#	src/renderer/src/i18n/locales/zh.json
@brennanb2025
brennanb2025 changed the base branch from brennanb2025/chat-command-waits-in-line to main October 8, 2026 20:33
Desktop passed the queue's coming send into the card projection as the agent
working, so a /clear the host names next but holds for background tasks lost
its caption. The projection now reads the agent's own work, keeping the coming
send only for hiding Send. The phone gains the same caption, and both clients
share one rule for it beside the first-actionable-card selector.
@brennanb2025

Copy link
Copy Markdown
Contributor Author

Superseded by #26730. Main's #26579 now keeps /clear in the same conversation, so this PR's design (moving waiting messages and drafts into a new chat after /clear) no longer applies. #26730 rebuilds "/clear waits in line" on the same-chat clear, reusing this PR's /clear card, capability and failure handling.

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