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.
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.
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.
- 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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
A send (and /compact) settles its ledger row `succeeded` in the same SQLite transaction as the
submission or queued draft that accepts it, so a row still `pending` proves nothing was written and
a resend runs it for the first time. The unknown-before-run mark and the per-row journal epoch go.
A resent id is answered from its row and the journal before preparation starts an agent and before
any write transaction: a recorded refusal with nothing opened; otherwise the conversation is opened
(no agent start for a send) and replayed, and a conversation that will not open answers unknown.
A pasted screenshot lived in the OS temp folder with a read permission held
in memory, so a draft could only bring it back as a placeholder. Local
pastes are now written to Orca's own native-chat-pastes folder under the
app's user data, and a draft keeps them as real images. On restore the
composer asks the main process to re-grant each one; main grants a read
only when the file's real path, symlinks and junctions resolved, is inside
that folder, and reports anything else as not kept, which becomes the
placeholder. Pastes older than 30 days are deleted at startup, far past
the host's 24 hour window for a resent message; the sweep never follows a
link and never blocks startup. SSH pastes and older temp-folder pastes
still come back as placeholders.
After a lost answer, the phone keeps that message's id for its text. If the
host since recorded and rejected it, sending the same text again replayed the
id, which answered with the same rejection: no banner, no bubble, and an empty
composer, so the press did nothing. Such a replay now goes out under a fresh
id, as a withdrawn one already does.
"Sent, but this phone couldn't update its record" is no longer said for a
resend the host recorded and rejected; its row says it was not sent.
The scan parsed every journal key on each update while a row showed as not
sent, about 3-17 ms per call at 3000 items. Every result row is a status
row, so the scan now skips the rest before parsing (about 0.02-0.04 ms).
A message the user retried, a second message in doubt, or one requeued over a
rejected row had a journal row that did not hold it, so it showed nothing and
looked sent. "Sending…" now stays until the row is pending or accepted.
The marker took the place of the whole meta row, so the copy button went away
while sending and the row jumped when it cleared. The row now stays mounted:
copy keeps its hover reveal and "Sending…" sits where the time goes.
A /compact the host recorded and then rejected was hidden, so on several paths
(blocked after the reply stopped waiting, rejected on restart, or seen from
another window or the phone) it vanished with nothing saying it failed. It now
stays as a not-sent row for every viewer, like any message the host recorded.
Its line says only "Your message was not sent." when a loaded host row
already says why: its turn's result row (found by the command's id) or the
failed start's row. The sender's command reply no longer repeats it when the
loaded journal shows the host recorded the command under the operation id,
and the text stays the row's rather than coming back to the composer.
The desktop pane's delivery-notice wiring moves into its own hook.
A pasted screenshot in an unsent draft vanished after a restart with no
notice, and a restored image whose file had been deleted looked fine until
the send failed. Both now come back as a placeholder chip that names the
image and says it wasn't saved with the draft. Send stays disabled, and
the send button says to attach the image again or remove it. Attaching a
file with the same name takes the placeholder's place.
Two browser tabs of the web client on one chat now follow each other: when
one sends or edits the draft, the other drops its copy and shows the new
one, unless it has an edit of its own not yet saved.
Keeps a fork issue’s details, metadata and edits bound to the repository the user opened, including same-number issues in fork and upstream. Repairs selected-assignee leakage and delayed failed edits repainting another issue.
Fixes#24378
Incorporates and cross-reviews contributor PR #24379, including its source-resolver correction and regression material. Covers the contributor PR’s Project-row identity and retained-dialog mutation findings. The final published head passes focused tests, hidden macOS rendering and current CI; the callback-timing bot thread has an evidence-based response.
Co-authored-by: Katsuma Takehisa <k.takehisa@nissogr.com>
A send whose answer was lost was resent quietly under the same id but looked
like any delivered message. Every message still in the outbox with no failure
to show now says "Sending…", muted, in place of its time, until the journal
holds a row for it. Rows that say a message did not go through keep only that
line and its Retry. The notices read the outbox through the same reconcile as
the transcript, so a row that lands clears the marker in one step.
The host now looks a resent send id up before preparing the session. A row
that settled refused answers with its refusal before the chat is opened. A
resend whose chat cannot be opened or made ready answers unknown instead of a
refusal. A /clear in flight refuses only ids the ledger does not hold.
A send row now records the journal epoch it was admitted into. An unsettled
row with nothing written in that same epoch runs for the first time; under a
later epoch the host answers unknown instead of reconstructing a submission
it never had. The host advertises agent-session.send-answers-proof.v1.
Adds a user-assignable shortcut for the existing child-workspace chip action. It stays unassigned by default on macOS, Linux and Windows. Final-source rendered checks cover Settings recording/reset, guards, scroll preservation and restart.
Fixes#24163
Continues mmarabel’s original contribution in this PR. Issue #24163 has no sibling implementation PR. Existing bot findings are fixed, withdrawn or addressed in the PR review.
Co-authored-by: mmarabel <166927047+mmarabel@users.noreply.github.com>
The line under a message that was not sent, on desktop and phone, used the
error color, as if the user had something to fix. It is a plain label now,
in the muted text color. A line that is still in doubt ("Message delivery is
unconfirmed.", or an expired id Orca can't confirm) keeps the error color,
as does the terminal chat's notice.
The phone put a rejected message's text back in the composer even when the
host had recorded it, so the same text showed in the chat as not sent and in
the composer. The host's row now holds it, as on the desktop: the send reports
that the host holds the text, so the composer and attachments are not
restored. A message a Stop withdrew still goes back with its notice.
Hosts from before the recovered marker publish only the restart reason. The
working-state rule already read that reason as recovered; the send answer now
uses the same predicate, so both agree on those hosts.
* ci: run cross-version wire suites when agent sends or orchestration change
The selector skipped the cross-version wire job for #24901, which changed the
shared agent-send path, the structured send envelope builders, and orchestration
RPC code. Route the send payload/fingerprint builders, src/main/runtime/orchestration/,
and the orchestration RPC methods to the job, and pin each rule in a test.
* ci: run cross-version wire suites only for code they execute
The agent-session suites now build their send through the shared outbox
builder and check it against the release host's schema and fingerprint,
so the send builder and outbox are selected exactly. Load-only
orchestration rules are dropped; the dispatcher-path files every suite
request runs through are selected instead.
* ci: run the release's own send admission, cover queued sends and the RPC reply builder
* test(cross-version): a wrong send fingerprint must be refused, so an agreed one is not vacuous
* Use bounded OpenCode context for vault session continuation
OpenCode database and synthetic row paths are not text transcripts. Use the
vault preview or captured pane context, preserving actual transcript paths
containing a hash and supporting both OpenCode lanes and Windows paths.
Adapted the intent of #11859 and extended it to actual installed v2 vault rows.
Co-authored-by: mrcha033 <mrcha033@users.noreply.github.com>
* Read real OpenCode sessions in terminal-backed native Chat
Reuse the bounded AI Vault SQLite worker for v1 and v2 session pages and live updates. Keep terminal input as the real execution path and pace OpenCode Stop through its two-Escape interrupt.
Co-authored-by: xodmd45-ctrl <xodmd45-ctrl@users.noreply.github.com>
* fix(opencode): publish approval cards for permission requests
* Send OpenCode native approval through its Enter selector
* Resolve mobile Chat readability for folder workspaces
* Bound OpenCode part batches and preserve v2 image attachments
* Prefer live migrated OpenCode sessions over legacy copies
* Consolidate mobile Chat eligibility test imports
* Consolidate OpenCode SQLite protocol type imports
* fix(native-chat): preserve OpenCode reasoning and patch parts
Separate genuine reasoning from answer blocks in both native SQLite schemas and retain recorded patches as completed patch tools. Keep each database row together at page boundaries so the existing raw-row cursors cannot drop half of a mixed row.
Adapted from @akhan157's OpenCode native history work in #13287 at bb661d10d716764fb472d824cd434678875b1947; retains the current bounded reader and account discovery instead of restoring the older capture and cursor implementation.
Verified against genuine private installed 2.0.16 and official 1.18.30 CLI ingestion.
Co-authored-by: Adnan Khan <adnank11427@gmail.com>
* fix(native-chat): keep split OpenCode rows intact on desktop and mobile
Preserve the native reader's bounded OpenCode row groups in paired reads and snapshot/replacement frames so a second presentation-count slice cannot drop reasoning while advancing the database cursor. Sort derived reasoning before its answer under the same provider timestamp while retaining journal order.
These two boundaries were reproduced with genuine installed 2.0.16 and official 1.18.30 sessions in a hidden desktop renderer and the current mobile view over an actual authenticated encrypted pairing.
Completes the semantic presentation from @akhan157's #13287 without importing its older clipping or cursor implementation.
Co-authored-by: Adnan Khan <adnank11427@gmail.com>
* fix(native-chat): keep reasoning and answers together in live windows
* Bound OpenCode transcript RPC pages and present omission notices
* Bound OpenCode transcript RPC pages and present omission notices
* Bound OpenCode transcript RPC pages and present omission notices
* Update native worker oversized-history notice contract
---------
Co-authored-by: mrcha033 <mrcha033@users.noreply.github.com>
Co-authored-by: xodmd45-ctrl <xodmd45-ctrl@users.noreply.github.com>
Co-authored-by: nwparker <nwparker@users.noreply.github.com>
Keep older request and reply parsers usable while current peers retain all supported history.
Co-authored-by: nwparker <nwparker@users.noreply.github.com>