Commit Graph
12747 Commits
Author SHA1 Message Date
Brennan Benson b4ee251273 refactor(native-chat): the client's outbox settlement gets a name the host's send settlement doesn't share 2026-10-04 05:08:01 -07:00
Brennan Benson 862a444064 fix(notes): returned text reaches the draft before its notes are cleared
When a message carrying notes came back to the composer, the notes were cleared first and the
text written to the draft after. A crash between the two could lose both. The text is now saved
to the draft first, on every path that hands it back (the host's answer, the journal, a Stop).
2026-10-04 05:07:41 -07:00
Brennan Benson dac2cac7c8 fix(notes): a cleared send stops waiting for notes that can no longer appear
Keys whose notes were not in the store yet stayed pending forever when the note never showed up:
a second "delivered" ending for notes already cleared, a page closed or a workspace removed, or a
note edited while its send was on the way. Each kept a store listener that scanned on every store
write for the rest of the run. A key now waits only while its workspace has not loaded; once it
has, a key with no note is dropped, and the listener detaches as soon as nothing is waiting.
2026-10-04 05:07:41 -07:00
Brennan Benson b1c804b8d9 fix(native-chat): a message an older build saved behind its Retry no longer flashes "Sending…"
A message an older build left waiting on its Retry is never sent again; once the chat's journal
loads it either comes back to the composer or the host's row shows it. Until then it was drawn as
a message on its way and read "Sending…", so QA saw it flash for a moment before its text went back
to the composer. It is now never drawn as one of this client's sends: no bubble and no notice. The
host's row, when there is one, or the composer is where it shows.
2026-10-04 04:49:54 -07:00
Brennan Benson 7a17d14216 fix(notes): notes follow their text, and a send's notes are cleared even before they load
Notes sent to a chat had two owners when the message came back: its text went to the composer
and the notes returned to the shelf, so sending both delivered them twice. Notes now follow their
text: once the host has the message, or its text goes back to the composer (turned away, or taken
back by a Stop), the notes are used and leave the shelf. Only a message thrown away with nothing
handed back (a cancelled launch, or this window closing a failed chat) puts them back.

A send that ended before its workspace's or browser page's notes were in the store cleared
nothing, so those notes showed as unsent once they loaded. The keys are now kept until their
owner's notes load, then cleared.

The test that only called a mocked tab refresh now drives the real host-frame entry points (a
paired host's frame, and a local snapshot through the cancelled-launch filter). Stale "Retry"
wording is gone from a test name and a comment.
2026-10-04 04:45:33 -07:00
Brennan Benson 2787955fdf test(notes): the open-chat notes test targets a structured session by its real kind 2026-10-04 04:30:18 -07:00
Brennan Benson f13846f5e0 fix(notes): notes sent to a chat stay held across a reload, by the message that carries them
Notes handed to a chat were kept out of the next "Send notes" only in memory. After a reload,
while the chat still held the unsent message built from them, the notes were offered again and a
second send delivered them twice. A chat send also cleared its notes the moment it was queued,
so a message that came back to the composer left its notes gone from the shelf.

Now the chat's saved message carries the notes' keys (each naming its workspace or browser page),
for "Send notes to > New agent" and for a chat already open. A note stays off the shelf while a
message this client still holds carries its key and the host can still deliver that message; past
the host's window for it the hold lapses on its own. The message's own ending decides the rest:
once the host has it the notes are cleared as sent, from any send or resend and after a reload;
if it comes back, is withdrawn or is discarded, the notes return to the shelf (a returned
message's text is also in the composer). Nothing is released or deleted because a tab list or a
host sync no longer shows the chat.

One mechanism instead of two: the per-message watch and outcome promise are removed; the outbox's
entry endings drive both the launch prompt's result and the notes.
2026-10-04 04:28:52 -07:00
Brennan Benson 7ce5e58da3 Merge #24917 (a new chat after a failed start; notes held while their send is on its way) into C2
Both add a hook where the outbox commits an entry's end; both are kept here and folded into one next.
2026-10-04 04:14:43 -07:00
Brennan Benson 701f567c63 fix(native-chat): a Stop given up after a quiet press still tells the person
A press whose answer was lost stays quiet because Orca will send the Stop again. If a newer
message then makes Orca give the Stop up before that resend (or a resend is dropped), nothing was
ever said. The press's own notice ("The agent wasn't stopped.") is now shown when the Stop is
given up; it is honest, since the outcome is unknown.
2026-10-04 04:12:37 -07:00
Brennan Benson a3c1d7c8a0 Revert "fix(notes): a new chat's saved message keeps its notes out of another send across a reload"
This reverts commit b88d0dc2cc.
2026-10-04 04:12:05 -07:00
Brennan Benson 19cabc72b0 Revert "fix(notes): release a gone chat's notes, and clear sent ones in the web client too"
This reverts commit ae0b9f3fea.
2026-10-04 04:12:04 -07:00
Brennan Benson 0564a310d5 fix(native-chat): a message held behind a lasting refusal still says what holds it
Since the line under a message Orca keeps resending dropped every step, a refusal whose reason is
a failure fact (signed out, history too large, the agent stopped while starting, a Claude account
problem) or an older host lost its cause too, and said only "Orca will keep trying to send it."
for as long as a day. Each now keeps a cause-only sentence ("The agent is not signed in for the
selected account.", "This needs a newer Orca on the computer running this chat.") with no step,
in all six languages.
2026-10-04 04:11:48 -07:00
Brennan Benson ab1c713c38 test(native-chat): build every refusal through the checked reader in the still-sending words test 2026-10-04 04:05:00 -07:00
Brennan Benson 4882ab956b docs(native-chat): comments say returned messages go to the composer on desktop too, and two identical ones come back as one 2026-10-04 04:02:52 -07:00
Brennan Benson cf317d53b7 fix(native-chat): a launch prompt settled before the launch looked resolves from that answer
If the open chat's send had already settled the prompt when the launch picked up the shared send,
no ending reached the launch, which then waited forever. It now takes how that shared send ended.
2026-10-04 04:02:52 -07:00
Brennan Benson 8fa5974453 fix(native-chat): a refused Stop is said even when the refusal answers a resend
When the first Stop's answer was lost, the press stayed quiet because Orca would send the Stop
again, but a refusal answering that resend was never said either, so the person was never told
the agent wasn't stopped. A refusal is now said on any attempt; a lost answer stays quiet only
while a resend is still owed.
2026-10-04 04:02:52 -07:00
Brennan Benson 7cfc4b3d08 fix(native-chat): while Orca keeps sending a message, the chat line names no step
The line under a message Orca keeps resending said things like "Send your message again" or
"Start a new chat" beside "Orca will keep trying to send it." Following that step while Orca
resends could send the message twice. The line now keeps only what stopped it (when the refusal
names a cause) and that Orca keeps trying:
- an "outcome unknown" the request threw says nothing, as the same answer returned does;
- a stage that couldn't be saved also says Orca keeps trying, which it does;
- a Stop that takes back the message the line is about clears the line.
Also corrects a comment: this build's replay makes the same stand-in record as an older host's,
for an accepted send whose row a new journal epoch dropped.
2026-10-04 04:02:51 -07:00
Brennan Benson ae0b9f3fea fix(notes): release a gone chat's notes, and clear sent ones in the web client too
Two gaps in keeping notes out of another send while a saved chat message
carries them:

- The web client never installed the clearing that removes notes once a
  chat sends them on, so after a Retry there the notes stayed listed. It is
  now installed, once per renderer, by the background services both the
  desktop and the web client load with App.
- A saved message carrying notes was thrown away only by this window's own
  close. A chat closed from the phone, another window or while Orca was off,
  or closed here without a known host, kept its notes held for good. A chat
  the host stops listing (affirmed, and not a launch still starting or
  failed) now has its queued messages thrown away once that sync lands, as
  does a close that can name no host, so the notes come back.
2026-10-04 04:01:07 -07:00
Brennan Benson ee4dbf021c test(native-chat): read sent requests through checked helpers instead of casts 2026-10-04 03:48:38 -07:00
Brennan Benson 0f7a1eb4c7 fix(native-chat): a command blocked behind an unsettled message says to wait, not retry
Desktop and the phone have no Retry on a message any more. Also passes the journal rows on every
render in the conversation Stop tests.
2026-10-04 03:47:08 -07:00
Brennan Benson b88d0dc2cc fix(notes): a new chat's saved message keeps its notes out of another send across a reload
The hold that keeps notes sent to a new agent out of the next send lived only
in memory. A reload while that chat had not yet sent them put the notes back
on the shelf while the chat's saved message still carried their text, so a
second "Send notes" sent them twice.

The staged message now saves the send keys of the notes it was built from.
The shelf treats a note as on its way while any saved message carries its key,
read from the saved outboxes on first use and kept current by the outbox's one
write funnel. When a message carrying notes is sent on (its own start, a Retry
or the re-check), those notes are cleared from their shelf; when it is thrown
away with its chat, they come back. Browser annotations sent to a new chat use
the same keys while the app runs. Running-agent sends keep their in-memory hold.

This replaces the per-message watch and outcome promise from the previous
change.
2026-10-04 03:45:41 -07:00
Brennan Benson e05dbe7943 fix(native-chat): a launch prompt reports delivery only once the chat finally settles it
The launch caller was answered after the prompt's first attempt. When that attempt got no answer,
the chat kept resending the prompt, but the caller had already given up: the notes stayed on the
shelf and could be sent a second time. Each outbox entry's final ending (the host holds it, or it
came back, was withdrawn or was discarded) is now published per entry, and the launch settlement
waits through resends for it. Delivered fires onPromptDelivered once; anything else reports that
the chat already said what happened.
2026-10-04 03:43:41 -07:00
Brennan Benson dc0a426d0a refactor(native-chat): the launch prompt's in-flight sends get their own module
The outbox sender imported the launch prompt's shared in-flight map from the launch prompt module,
which imports the sender: a dependency cycle CI's lint rejects. The map now lives in
structured-agent-launch-prompt-in-flight-dispatches, which both import. No behaviour change.
2026-10-04 03:42:58 -07:00
Brennan Benson 5bd5879ab7 fix(native-chat): the host-window timer waits only for windows still open
A window that had already closed while the journal was still loading would arm a zero-delay timer
that re-armed itself every tick. Only windows still open arm one now; a closed one is settled when
the journal loads. Adds a test that a Stop-outrun send comes back once its window closes.
2026-10-04 03:35:56 -07:00
Brennan Benson 04f3468f15 test(native-chat): two older tests follow the new hand-back and launch prompt rules
A repeated hand-back is the same text, so the repeat no longer adds leading spaces (the first
line's indentation is now kept). A launch prompt the host refused comes back to the chat, which
says why, so its caller is told the failure was already shown.
2026-10-04 03:34:28 -07:00
Brennan Benson 565b740c25 test(native-chat): "Not confirmed" waits while the agent works or the chat is still starting 2026-10-04 03:33:25 -07:00
Brennan Benson 589bfad798 docs(native-chat): comments no longer describe the removed Retry or id rotation 2026-10-04 03:32:26 -07:00
Brennan Benson 937e79ea29 fix(native-chat): an answer that proves nothing never hands a message back, and every held send ends
What one send attempt's answer may conclude, tightened where it could duplicate, lose, or strand
a message:
- A request the host turned away (unknown method, bad params, not authorized) proves no record
  only on a first attempt; on a resend an earlier attempt may have landed, so it is sent again.
- "First attempt" is checked again when the answer arrives: another view that staged the same id
  meanwhile makes it a resend.
- A reused message id on a resend is settled by the journal, as a conflict is.
- An older host's made-up record for a row its journal lost is the chat's only when a loaded row
  shows it; otherwise the message comes back with "couldn't confirm" words instead of vanishing.
- A stage that can't be saved no longer hands the message back (an earlier attempt may have
  landed): it waits, says "Couldn't save your message." once, and is tried again.
- When a resend stays unanswered because of a refusal Orca can't trust, the chat line says why
  once, followed by "Orca will keep trying to send it.", and clears once the message lands.
- Sends a Stop outran, and messages an older build left, end when the host's window for their id
  closes, even if nothing else moves by then.
- The queue stays held through the capability check, so a later send can't overtake the head.
- A launch prompt the chat handed back or keeps sending no longer also makes the caller toast
  "could not be sent" with a copy button.
2026-10-04 03:31:23 -07:00
Brennan Benson 6d120ea9e2 fix(native-chat): a Stop gives up on its outrun sends only once its request ends
- A newer message used to make Orca give up on a Stop's outrun sends even while that Stop's own
  request was still out, so a send could come back as "couldn't confirm" before the Stop's real
  answer arrived. It now waits for the request to end.
- The first press no longer shows "The agent wasn't stopped." for a lost answer that Orca is about
  to resend; it still does when nothing will resend it, and always for a refusal.
- This app's own sends are compared with the press by when they were queued, on this machine's
  clock; the comment now says another client's are compared by the time in their id.
2026-10-04 03:20:01 -07:00
Brennan Benson a688ed4ca2 fix(native-chat): sends a Stop outran no longer stall the chat
Two ways a Stop could leave messages stuck on "Sending…":
- the resend timer picked the first message in doubt even when it was one the Stop outran (never
  resent), so a later message in doubt behind it was never resent either; it now resends the
  message the queue is actually waiting on;
- a Stop answered while the agent was idle writes nothing to the journal, so its answer's position
  was one the chat had already read, and the settling step only ran when the journal moved. It now
  also runs when the outbox changes, so the outrun send comes back to the composer at once.
2026-10-04 03:18:30 -07:00
Brennan Benson 1cd798685b fix(native-chat): returned text is skipped only when the draft already ends with it
A message handed back to the composer was dropped whenever its text appeared anywhere in the
draft ("go" inside a longer sentence), and its first line lost its indentation. It is now skipped
only when the draft is that text or ends with it after a blank line, and only the end is trimmed.
2026-10-04 03:17:04 -07:00
Brennan Benson 62ef8e7804 fix(native-chat): every chat send ends one of three ways, decided by the host's answer
A send now ends only by what the host said: it holds a record (the host's row shows the message
from then on), it proved there is no record (the text goes back into the conversation's draft and
the reason is said once), or nothing answered yet (the same id goes again, quietly, as "Sending…").
One shared settlement decides it for the open chat and a launch prompt alike.

Removed: the Retry control and its id rotation, the rejected and held-for-Retry outbox states, the
Stop latch, the per-window failure memory, the parked "unknown" entry, the launch prompt's own send
path, and the error-text regex (errors are read by code). A Stop stamps a send already on its way
with the Stop's own id; the Stop's answer settles it. Drafts of a structured chat are keyed by the
conversation and survive their tab closing. Entries older builds left waiting for a Retry are
settled once the journal loads, never sent again.
2026-10-04 02:58:21 -07:00
Brennan Benson e10d88d6e8 Merge #24918 (recorded rejections stay in the chat) into C2, resolving its conflict with #24606
The row keeps #24606's "Sending…" in the time's slot and #24918's muted not-sent line; the notices
keep both: an entry still sending reads as sending, and a recorded rejection no entry carries reads
as not sent.
2026-10-04 02:01:22 -07:00
Brennan Benson 8c10bc8511 Merge remote-tracking branch 'origin/brennanb2025/chat-drafts-survive-reload' into brennanb2025/chat-send-settles-once 2026-10-04 02:00:08 -07:00
Brennan Benson 138466e7e8 Merge remote-tracking branch 'origin/brennanb2025/chat-failed-send-returns-to-composer' into brennanb2025/chat-send-settles-once 2026-10-04 02:00:08 -07:00
Brennan Benson 60d7e010e5 fix(notes): a new chat's staged prompt decides when its notes leave the shelf
Notes sent to a new agent were released back to the shelf as soon as the
chat's start failed, while that failed chat kept the same text staged for its
own Retry. Re-sending and pressing Retry put the notes in two chats, and Retry
alone left delivered notes listed as unsent.

For a new chat, the notes now follow the prompt it staged: held while that
message is in the chat's outbox, cleared from the shelf when any dispatch
(Retry or re-check included) sends it on, and back on the shelf when the
chat is closed and its outbox thrown away. A paired server's chat is found
once its start settles. Running-agent sends keep their own result.

While notes are only on their way, the send button reads "Sending…" rather
than "All notes sent".
2026-10-04 01:52:02 -07:00
Brennan Benson ccbfffb0e3 fix(notes): offer no send when every note is already on its way
When every note or annotation in a send is held by an earlier send still in
flight, the built prompt is empty. The notes menu already disabled its
trigger then, but its New agent rows still started an agent with no text,
the annotation Send buttons still opened, and a sidebar agent row could be
sent an empty prompt. The New agent rows, the annotation Send buttons and the
sidebar send now offer nothing when there is nothing to send.
2026-10-04 01:34:34 -07:00
Brennan Benson 40e3975b42 test: give the annotation tray fixture its hand-off callback 2026-10-04 01:32:14 -07:00
Brennan Benson 0fd16b7d93 fix(notes): notes handed to a send leave the next send until it settles
Notes sent to an agent stayed in the notes shelf until their chat delivered
them, so a second "Send notes" made while the first new chat was still
starting collected the first notes again. With every different request now
opening its own chat, those notes reached two chats.

When notes or browser annotations are handed to a send (a new agent, a
running agent from the menu, or a sidebar agent row), they are held in
memory against that send's own delivery result and left out of the next
send. Delivered notes are removed as before; a failed, refused or
undelivered send releases the hold, so they come back for the next send.
The notes menu now builds each scope's prompt from the notes it will send.
2026-10-04 01:30:19 -07:00
Brennan Benson f63fb59f61 docs(native-chat): say what a replay's preparation does for every plan 2026-10-04 00:57:13 -07:00
Brennan Benson 4b72d035de fix(native-chat): only a chat its user has not used yet takes another request's text
A blank chat that is still starting was claimable by the first request with
text even after its user had sent a message into it or typed into its
composer, so notes or a Fix with AI prompt could land in a conversation the
user had already started. Emptiness is now read from what the chat holds:
nothing in its outbox and no text in its composer draft. A chat its user has
used stays theirs, and the request opens a new chat.

If the claiming text cannot be saved, the claim is not recorded: the request
falls through to a new launch, whose save failure shows on that chat as for
any new launch, instead of reporting a failure nothing showed.
2026-10-04 00:55:35 -07:00
Brennan Benson 4b9f91a5a0 test(native-chat): check the outside file by its real path too 2026-10-04 00:46:30 -07:00
Brennan Benson 16c61f6a8d fix(native-chat): grant a paste's stored spelling only when it names the same file
A grant also covers its path's own real path, so a stored spelling that is
itself a link inside the paste folder could have granted an outside file
while the path as restored reached a real paste. The stored spelling is
now granted only when it resolves to the same file; otherwise only the real
path is, and the preview falls back to the generic icon.
2026-10-04 00:46:15 -07:00
Brennan Benson 83dc6b2590 fix(native-chat): a ledger refusal is answered first, and a /clear refuses only a send's first run
An id the ledger refuses (expired, conflict, invalid, capacity) is answered as admission would,
with no journal read, preparation or write, so a closed chat or a read-only store answers it too.
A re-read after the replay open that comes back refused returns that refusal.

Whether a /clear is in flight is read when a send arrives and applied in the send's preparation
for a first run only: an id the ledger holds by the send's turn, including one whose earlier
attempt was queued ahead of the clear, is answered from its record. MutationPlan makes
settlesWithWrite and settledOutcome exclusive; the capability text no longer promises a refused
id never sends.
2026-10-04 00:45:51 -07:00
Brennan Benson c640f75300 fix(native-chat): a restored paste is readable by the path its draft stored
Restore granted only a paste's real path, but the composer reads the
preview by the path its draft stored, so with user data reached through a
link the restored chip showed a generic icon. The stored spelling is
granted too; it is already proven to sit inside the paste folder. A test
also pins that a composer paste asks for the paste folder.
2026-10-04 00:44:40 -07:00
Brennan Benson b04f371b35 test: an empty chat delivers the claiming text the way its request asked 2026-10-04 00:41:24 -07:00
Brennan Benson 9279746b16 fix(native-chat): an empty chat still starting takes the first text sent to it
A blank chat that is still starting (a + pick, the empty-workspace default
chat) is empty, so the first request with text, such as notes sent to a new
agent, now goes into it instead of opening a second chat beside it. That
request becomes the chat's own request: an identical repeat joins and is sent
once, and any other request opens a new chat.

Move the logic that decides which launch a start joins out of the launch
registry into its own module, so the registry stays under the line limit
once #24904 lands beside it. Pin that a repeat arriving while the opening
text is still sending is sent once.
2026-10-04 00:40:43 -07:00
Brennan Benson f6c19952b9 Merge remote-tracking branch 'origin/main' into brennanb2025/chat-recorded-outcomes-from-journal
# Conflicts:
#	mobile/src/session/MobileNativeChatMessage.test.ts
2026-10-04 00:33:03 -07:00
Brennan Benson 0971ba9445 fix(native-chat): only composer pastes use the paste folder, and restore grants only real paths
Every local clipboard image save had moved into the paste folder, whose
macOS path has a space, so a screenshot pasted into a terminal or sent to
a terminal-backed agent no longer attached. Only a native-chat composer
paste goes there now, through an optional field on the existing save
call; terminal, editor and phone pastes stay in the system temp folder as
before. An image path sent to an agent's input is escaped the way a
dropped image is.

The restore re-grant now grants only the file's real path, and only when
both the real path and the path as written are inside the paste folder,
which must not itself be a link. The sweep deletes only Orca's paste
files and skips a linked folder.
2026-10-04 00:32:49 -07:00
Brennan Benson 5ff07e6730 fix(native-chat): placeholder chips say what happened and only a user attach replaces one
A placeholder now shows the image's name and a short hint on the chip
itself, "Attach again" for a file and "Not kept" for a pasted image, with
the longer explanation in its tooltip. The copy no longer says the image
wasn't saved: it says it couldn't be brought back, and for a pasted image
it asks only for removal, since a new paste can't match it. The send
button now says to remove the image to send.

Only an image the user attaches (picking, dropping or pasting) takes the
place of a placeholder with its file name. An image Stop gives back is
added beside it, so a different file with the same name no longer hides
the reminder.
2026-10-04 00:23:49 -07:00