mirror of
https://github.com/stablyai/orca.git
synced 2026-11-02 18:17:36 +00:00
76d1c68ab5c8b90eab7bfdf027f54e23df239461
3
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
9fdd90ffa7 |
feat(orchestration): a native chat can be a dispatch worker, like a terminal agent (#22972)
* feat(native-chat): a queued card can record a Dispatch's task as its source A chat worker's task is held in the chat's queue like any agent message, so the card needs to say which Dispatch it is from: the sender, run, task and Dispatch ids. An older build reads an unknown kind as the person's card and sends it as written. * feat(orchestration): a chat can be a dispatch worker, like a terminal agent `dispatch --to orca_session_id:<id>` and `worker-start --terminal orca_session_id:<id>` now accept a chat on this host instead of refusing it. - The chat is refused only where mail to it would be: unknown, a provider id, another host, or closed. A chat can't be its own coordinator's worker. - Its task goes through sendAgentTurn as a queued send, as mail notices do: an idle chat starts a turn, a busy one holds a card naming the Dispatch. The operation id is derived from the Dispatch, so a resend replays. - worker-start reads that outcome as the terminal path reads its write; a card held behind a running turn is handed over with its start unobserved. - The Dispatch names the chat by its /clear root, with no pane or process, and records its Orca session id, so its own sub-dispatches nest under it. - Mail to dispatch:<id> reaches the chat; worker-show/list/read read the chat's session records; stop and abandon never close the chat; closing the chat fails its Dispatch as closing a terminal does. - A dispatch preamble for a structured session names the CLI the structured mail lane names. * test(orchestration): a chat worker's reach, and its Dispatch across a /clear At rest is live, a closed chat has exited, and another host or nothing to read is unverifiable. A /clear keeps the chat's Dispatch, and the session that continues the chat reports as it. * fix(orchestration): chat worker review round 1 - dispatch --inject is a keepalive-backed wait, so a chat that takes a while to accept its task no longer reads as a dead runtime at the 30 s idle cut. - An injected task whose delivery is unknown keeps its Dispatch open, as an unknown worker-start does, instead of failing it while the chat may run it. - A busy chat's worker-start receipt says the task waits as a card in the chat's queue, and that worker-abandon does not remove that card. - A close that puts the chat's tab back no longer fails its Dispatch: the closed-chat settle runs after the close's outcome, not at its hide. - worker-start adopting a chat says it gave the task to the chat, instead of claiming it started a terminal agent; the mode value is unchanged. - Test: a chat whose agent has not taken its task reads outcome_unknown. * test(native-chat): a close's hide defers its hidden notice until the close settles The rollback suite pinned the hide's arguments; it now expects the deferred notice, and that the notice is sent once, after a rolled-back tab is back. * fix(orchestration): a chat worker whose successor is unknown is unverifiable, not exited A /clear successor this host has no record of is missing evidence, not proof the chat is gone. Only a closed chat reads exited, so only a closed chat settles its Dispatch; a lost successor reads unverifiable with that reason. * fix(orchestration): chat worker review round 2 - A chat's close notice settles only that chat's Dispatch. A scan of every chat Dispatch read another chat whose close was still in flight (its tab hidden, maybe to be put back) as closed and failed its Dispatch. - Only a dispatch --inject into a chat is a long poll; a terminal inject writes and returns, and keeps its short-RPC slot. - Receipt wording: worker-start says it gave the task to the chat, and a queued task is sent when the chat's queue reaches it. * fix(orchestration): chat worker wording, review round 3 - worker-start's mode sentence for a chat states the placement, which is true whether the task is delivered, queued or refused. - Comments and a test title no longer claim a close notice re-derives every chat worker, or that a queued task waits for the current turn. * fix(orchestration): build a structured worker's task sender from the start's own ids The minted worker's preamble names who its task is from with the run, task and Dispatch ids worker-start already holds, so it reads no Dispatch row. * fix(orchestration): point a chat worker at its held Dispatch mail again A chat worker's coordinator mail lands in its Dispatch's mailbox, but a chat's idle edge re-derived the Dispatch mailbox only for a party with a terminal handle. A pointer lost with the provider then waited for new mail after a restart or a /clear. The re-derivation now looks the Dispatch up by the party's address, which names a worker by its handle and a chat by its root. * fix(native-chat): open a task card's sender by address; a task carries no mail Opening an agent message's sender looked up the mail it carried, which only a mail notice has; with the task kind in the union that read no longer typed. A task's coordinator is found by its address alone. |
||
|
|
c893048aeb |
feat(native-chat): show which agent a chat message is from, and open it (#25888)
* feat(native-chat): show which agent a chat message is from A message another agent sends into a native chat (today the orchestration mail notice) looked exactly like the person's own: a right-hand bubble and a queued card identical to theirs. The sender was recorded only on the queue row, so a message sent to an idle chat, or a card once it was sent, had no sender at all. The sender now lives on the message itself (`from` on the message body): - The send path carries it on both deliveries; an idle chat records it too, and it survives the queue, /clear's carry, restart and history. - No fingerprint covers it: one shared payload projection leaves it out at every fingerprint site, so stored digests are unchanged and the provider's echo still folds into the same bubble. The provider never receives it. - A client cannot set it: its send params stay strict. - One reader normalizes it where it is read (journal load, queued row, client projection); a newer message kind keeps the sender an agent's. - Each sender keeps a name snapshot from names Orca controls: a chat's conversation name or agent label, a terminal's own tab title or agent, a federated sender's task name. Never an agent-set terminal title. - The separate queued-row sender column and its plumbing are removed; it never shipped. Desktop shows such a message as a left-aligned "Message from <name>" row; the name opens the sender: its chat through the existing open-chat flow (after a host lookup that follows /clear and dispatch addresses), or its terminal through the terminal-link path, with the existing feedback when it is gone. A queued card shows a plain "From <name>" line. Mobile shows the same attribution, not yet clickable. * fix(native-chat): name and open an agent's message by what Orca shows for it Review fixes for showing who an agent message is from. - Names: a sender is named by its dispatch's task (local workers too, not only federated ones), else a chat by the label its tab shows, else a terminal by its tab's stored title, else its agent. The conversation name the snapshot read before has no writer, so every chat read "<Agent> Chat". - One sender builder (agentMessageSender) and a public runtime naming object (orchestrationSenderNames) any send site can use; the terminal naming moves out of the unrelated runtime layer into its own module. - Opening a terminal sender always asks the chat's host: a terminal handle is issued per run, so the host finds an earlier run's terminal again through the pane its mail was sent from. The pane key never leaves the host. - Feedback follows what the host found: a gone chat or a gone pane by the kind it lost, "pane no longer available" only when the terminal answer proves it, and the existing "couldn't reach" / "needs a newer Orca" words otherwise. - Another agent's message is not offered rewind (it would put the agent's text in the person's composer), and a Codex rewind keeps the sender on the messages it retains. - The queued card and mobile name every sender, two that share a name included, and count in "+N" only those left out; the transcript row separates names with ", ". - The host no longer normalizes the sender when loading the journal or the queue: nothing on the host reads it, and clients read it through the one reader at their projections. * fix(native-chat): name a worker's final report by its task; clearer open-sender failures - A worker's own worker_done settles its dispatch before the mail lane names who the report is from, so a terminal worker's completion read "Codex". A local worker is now named by the dispatch it holds, else the one it last held (handles are per run, so that is this run's). The coordinator mail rig now attests the worker's process, so its report is accepted and settles as it does in production. - Opening a sender: a version block or an unknown answer says "Update Orca to open it.", since either side may be the older one; "This needs a newer Orca on the computer running this chat" stays only for a host that predates the lookup. "Orca couldn't reach the agent." is followed by "Try again.", each sentence translated on its own. No new copy. * fix(native-chat): name a worker only by work it holds or just reported A worker with no active dispatch was named by the newest dispatch ever assigned to its terminal, at any status and any age: one that failed before it ran, or a task finished long ago that its later, unrelated mail kept showing. Now a worker is named by its active dispatch, else by the dispatch its own worker_done in the announced mail names (only if that dispatch is its own), else by its tab or agent as any terminal is. * fix(native-chat): restore a rewound message's sender through its typed body; mobile card fixtures name their attribution - The rewind's held-sender restore lives with the retained rows and reads both bodies through the journal's message-body check, instead of a shared helper taking any object (anti-slop: no-object-parameters). - Mobile queued-card test fixtures carry the attribution field the card gained. * fix(native-chat): never call a sender gone without proof; the sender builder moves out of the mail lane - A federated sender (dispatch:<id>) runs on another host, which the chat's host cannot open: its name is plain text, and the host's lookup answers 'not found' rather than 'gone' for a dispatch it does not run. Not seeing a worker is no evidence it stopped. - The lookup calls a chat gone only when the identity is proven lost; any other failure reaches the client as 'could not reach'. - A sender open that throws shows 'couldn't reach the agent' instead of an unhandled rejection. - agentMessageSender, the one sender builder, lives in its own module, so the runtime's naming no longer imports from the mail lane. |
||
|
|
dd39dcba5f |
feat(orchestration): a native chat gets the orchestration pointer a CLI agent gets, through the same send as your messages (#25078)
* feat(orchestration): agent mail to a busy chat waits in the chat's own queue
A mail notice for a busy structured chat used to wait in the orchestration lane's
own invisible "until the chat is free" gate. It now goes through the queue a
person's message uses: sendAgentTurn(..., { delivery: 'queue', source }) makes
the host hold it as a draft card the person sees and can Steer or delete, and
the queue sends it when the turn ends.
- The lane's busy gate (turnRunning / awaitingHuman) is gone; the queue's own
hold decides. Its parking now only waits for its own send or card to settle.
- A queued card's hand-off (sent under a fresh id) is matched by
queuedMessageId, so it stamps the mail once and never sends a second notice.
- A card from before an Orca restart is still the lane's: no second card.
- A card the person deleted counts as handled for that batch; newer mail
notifies again. No stored flag: read off the card and delivered_at.
- `orchestration check` waits for the lane to withdraw a card whose mail it
read, so a stale notice is never sent; more mail replaces the card.
- Each card records who queued it in queued_messages.source_json (versioned,
schema-validated): the person, or Orca for agents, with every distinct
sender as an orchestration party plus host id, and the mailbox, dispatch,
run and message ids. The restart pause holds only the person's cards.
- sendAgentTurn answers with a snapshot, not the journal's live submission.
* test(orchestration): type the mail fixture as a pointer batch message
* fix(orchestration): the queue judges an agent's mail notice as it sends it
A mail notice queued in a busy chat could go out stale or twice: it was
kept true from outside the queue, by orchestration check withdrawing it,
which missed other readers, raced an attempt in flight, lost the card
when /clear carried it (a second notice), and moved it to the back when
new mail replaced it. An agent's card behind a person's paused card never
sent after a restart, so an unattended coordinator stalled.
- The drain asks the card's sender, in its own serialized step, whether
it still stands: send, restate (count and senders of the mail owed
now, written onto the card in the hand-off's transaction), or withdraw
as the host. A failing judge sends as written. Send-now restates but
never withdraws. Orchestration registers the judge on the host.
- check no longer touches any queue (back to main); onMailRead,
notifyOrchestrationMailRead and the lane's withdrawals are gone.
- One unsettled card per agent message, enforced at insert; new mail is
counted when the card sends, never replaces or moves it.
- The lane finds its card by what it is (a notice for the mailbox in the
session the mailbox reaches now); a decline is a withdrawal a person's
operation stamped. /clear moves agent cards as the host. Pointer rows
last only while a direct send is in flight and end with the session.
- Pauses (restart, Stop, /clear) hold only a person's cards, and a held
card never traps an agent's card behind it, keyed on the message kind.
- The source is named for the message (agent-session-message-source),
one payload shape per message kind, no relative host id.
* fix(orchestration): Orca's mail notice waits in the queue out of sight
The notice card read as something the person typed and came and went on
its own, the transient-state message the queue should not show. It is
hidden until B can label who sent it.
- The host leaves mail-notice cards out of the published queue, so the
list, its count, Steer/Delete/Edit, steer-newest and the paused header
never see them, on every client. Keyed on the message kind: D6's task
card will be shown, labelled, in B. They still count toward the queue
limit (temporary, until B).
- A refusal no longer returns a hidden card for the person to act on:
the host withdraws it, and the lane points again only after a turn ran
since, the rule it already had for a refused notice.
- Send-now no longer restates a card: no one can reach a hidden one.
- The stored sender drops the pane key, a mailbox credential.
- The gate covers an abandoned worker: a mailbox that reaches no session
withdraws its notice (tested).
* fix(orchestration): a hidden mail notice never waits on the person
Hiding the notice left three paths where it waited on someone who
cannot see it.
- A failed hand-off write put a send_failed hold on it, which only a
person's Send or Delete clears: that mailbox's notices stopped for
good. The host now withdraws a hidden card instead and hands its
mailbox back to the lane through the ordinary redrive, since an idle
chat gets no other edge. A write that keeps failing is retried once
per edge.
- A person's Stop while the agent started on the notice put it back to
waiting, where no pause holds it, so it was sent again at once. The
host now withdraws it; the lane points again after a turn ran or when
newer mail makes it a different notice, as on main. A restart still
puts it back.
- A notice queued before the person's message went first. One order
rule now: the person's cards go in order and never past one waiting
on them; a card they cannot see never delays one they can, and goes
only when none of theirs may.
The drain moves to its own module (structured-agent-session-queued-
drain.ts). Comments that still described the notice as visible are
corrected.
* fix(orchestration): an accepted notice hand-off stamps its mail; a judge that cannot look decides nothing
- When the agent opened its mail in the notice's own turn (the normal
flow), an open batch left nothing owed, so the lane never marked the
mail the accepted hand-off carried as delivered, unlike an accepted
direct send. The lane now stamps exactly the ids a handed-off notice
carried whenever undelivered mail remains, owed or not. A later
conversation is no longer told again about mail already opened.
- At quit the host registry is cleared before teardown, so the drain's
judge could not resolve the mailbox and withdrew the notice. A judge
that cannot read its inputs now defers: the card waits for a step
that can look. A mailbox that resolves to another session or none is
still withdrawn.
- The judge, the owed-batch selection and the notice body move to
structured-mail-notice.ts. A failing hand-back of a dropped card is
logged.
* refactor(orchestration): a chat receives the agent's mail itself, queued like a person's message
A structured chat used to get a derived "You have N orchestration messages,
run check" notice, which could go stale while it waited in the chat's queue,
so the queue judged, restated or withdrew it as it sent. It now gets the mail
itself as the turn, through sendAgentTurn with delivery 'queue', the path a
person's message takes.
- One turn per mailbox batch: the mailbox's unread mail at delivery, in mail
order, each message led by "[message from <sender>]", its type, subject,
body, payload and reply hint (what check prints). Mail arriving while that
card is still in the queue waits for the next card; a card is never edited.
- An idle chat takes it at once; a busy one queues it as an ordinary card the
person sees and can Steer or delete. Mail is marked read when the chat
accepts the turn, so check does not return it again; a deleted card leaves
its mail unread for check, and it is not pushed again.
- The card stores who it is from (source_json): every distinct sender, and
each message's id, run and sender. Kind 'mail-notice' becomes 'mail'.
- Removed: the send-time judge (QueuedAgentCardVerdict), structured-mail-
notice, structured-pointer-notice-cards, queued-message-restatement, the
hidden-card rules, the agent-card exemptions from the restart, Stop and
/clear pauses, the dropped-card hand-back, and the drain split. An agent's
card now follows the same pauses as a person's.
- Terminal agents are unchanged: they keep the typed pointer and check.
* fix(orchestration): a chat's check skips mail already queued to it; restarts hold only the person's cards
- When a structured chat runs `orchestration check` (consuming, --peek or
--wait), mail an agent's card in that chat's own queue still carries
(any card not deleted) is left out: it is on its way as a turn, so the
agent does not read it twice. Derived from the queued rows at read time;
a deleted card no longer carries it, so it comes back. Terminal callers
and every other read are unchanged. New batches pass the exclusion to
getOrCreateMailboxDelivery; peeks filter it.
- A restart's queue pause now holds only the person's cards: an unattended
coordinator's agent card sends after an app restart without waiting for a
Resume, since the mailbox is the record and reading is marked. Stop and
/clear still hold agent cards. One rule, in queuePauseHolding. An agent
card queued behind the person's held card still waits behind it: the
queue never reorders.
- The duplicated direct-mailbox snapshot routing in check-run and
check-worker becomes one helper, which keeps check-worker under its line
limit.
* fix(orchestration): the mailbox is the only record of read mail; a chat's check takes its waiting cards
A card held an exclusive claim on its mail that nothing reconciled with the
mailbox: queueing it stamped the mail delivered, and a chat's check hid every
card that was not deleted. So a chat's own check --wait in one turn could not
see a result its waiting card held, a hand-off that ended in doubt or came back
"Not sent" stranded its mail, a check racing the card's build read the mail
twice, and a card left behind by run-use still sent and marked it read.
What a card or send holds is now derived each time from the chat's queue and
its sends: an accepted send marks its mail read; a waiting card or an
unanswered send holds its mail; everything else unread is pushed again or
returned by check. A card the person deleted is recorded as not to be pushed,
at the delete. The lane withdraws returned, partly read and moved cards. A
chat's consuming check withdraws the waiting cards holding what it reads, one
at a time with the lane. The sender is stored on the sent submission too
(host-only), so read state survives the card row's prune. A refusal before
anything started ends its operation; a restart pause is raised only by the
person's cards; an unreadable agent source stays an agent's.
* refactor(orchestration): a chat gets the pointer a terminal agent gets, sent through the chat's own queue
A structured chat now receives exactly what a terminal agent is typed: "You have N orchestration
messages ... run `orca orchestration check`" (formatMessagePointer, same CLI name). It goes
through the shared sendAgentTurn with delivery 'queue', the composer's queue-if-active: an idle
chat gets it at once, a busy chat's queue holds it as a normal card and sends it when the turn
ends. The lane's idle gate is gone; the send decides, as for the person's message.
The lane queues no second pointer while one of its cards still waits, read from the queued rows;
mail arriving meanwhile, or after the card is sent, is pointed again as the terminal lane points
new unread mail. A queued pointer counts as delivered, as an accepted one does. `check` and mail
read state are main's: only `check` reads mail.
The card records who it is from (queued_messages.source_json, kind 'agent', message
'mail-notice', with its senders) for the next PR to render. A restart's pause is raised by and
holds only the person's cards; Stop and /clear still hold an agent's.
Removed from the earlier designs: the message-as-turn batching, the check exclusion and taking,
the derived claims, lock and reconciliation, the sender on submissions, and their tests.
* fix(orchestration): a chat's pointer follows a card that leaves its queue unsent
The lane holds new mail while a pointer card waits, and retried it only on the chat's next
status change. A card that leaves the queue without a turn after it (the person deletes it
while Stop holds it, or its hand-off comes back) made none, so that mail sat unpointed until
something unrelated happened. The shared queue wiring now tells its host when an agent's card
stops waiting, read only when the queue changed, and the runtime redrives that chat's parked
mail. The runtime forwards the new host dep like the others.
Tests: that case end to end, and /clear carrying an agent card keeps who it is from. Agent card
bodies in tests are the pointer text; the restart pause's header says why an agent card may send.
* refactor(orchestration): no special handling for agent notices in the chat queue
A structured chat's orchestration notice is now sent through the chat's own send
like any message: an idle chat takes it as a turn, a busy chat's queue holds it
and sends it at turn end, under the same Stop, restart and /clear pauses as the
person's cards. Native chat no longer branches on who a card is from; the card
only records it.
- Restore main's queue pause logic (no restart exemption for agent cards).
- Remove the queue watch that redrove mail when an agent card left the queue,
and the host's queued-row read the lane used for it.
- The lane reads nothing of the chat's queue. It sends no second notice while
mail it already pointed at is unread, read off the mailbox alone.
* fix(orchestration): point new mail like a terminal, even while earlier pointed mail is unread
Drops the native-chat-only rule that held back a notice while the agent had not yet read mail it
was already pointed at. Also fixes main's stop-note test, which still built a provider handle in
the shape #24991 replaced.
* test(native-chat): keep the queued-message rig fixture under the line limit
* fix(test): drop main's duplicate codexProviderHandle import (same line as #25713)
|