mirror of
https://github.com/stablyai/orca.git
synced 2026-10-31 09:07:40 +00:00
7aa91f730a3962aebcf7b87a75e26ea628c19d71
3
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
e42c0b3630 |
fix(orchestration): call the Orca session ID orca_session_id everywhere agents see it (#24230)
* refactor(orchestration): spell the Orca session address orca_session_id:<id> The database already names this identity orca_session_id, but agents saw and typed session:<id>, which collides with the Claude/Codex session ID, Session History and terminal sessions. The prefix is now orca_session_id:. No alias, migration or version handshake for the old session: spelling: restructured native chat is experimental. The coordinator-address triggers compile the prefix into their body, and migrate-v42 stamped them once, so an existing database would keep remembering session:<id> for new Runs. They are now recreated on every open, like the mail routing trigger. Provider-id refusals now name the "Orca session ID" instead of "Orca address"/"Orca id". * fix(orchestration): name the Orca session ID the same way in both provider-id refusals The send refusal handed back orca_session_id:<id> as the Orca session ID while the caller refusal called the bare id by that name. Both now say "This session's Orca session ID is orca_session_id:<id>"; the caller refusal also names the bare value ORCA_AGENT_SESSION_ID accepts, since that input takes only the bare id. Tests pin both refusal texts, that session:<id> no longer parses as a session, and that the older-build trigger rewrite actually changes the trigger SQL. * fix(orchestration): keep chats reachable at their session:<id> address after the rename Existing native chats were addressed as session:<id>, so agents and stored mail still use that spelling. Input now accepts session:<id> and treats it as orca_session_id:<id> (the codec parses both; nothing writes the old one), and schema v43 rewrites every stored session:<id> address once: message senders and recipients, remembered Run coordinator addresses, structured pointer operation keys, and coordinator loop handles. Subjects, bodies and payloads are left as written. * fix(orchestration): refuse session:<id> input again; stored addresses still migrate once The old prefix is not accepted as input: no respelling helper, no special case. A session:<id> recipient is refused like any unknown terminal handle (terminal_not_found), including after v43 rewrote a Run's remembered session:<id> coordinator address, so the old spelling no longer routes. Schema v43 still rewrites stored session:<id> addresses once. |
||
|
|
c1628b4e65 |
feat(orchestration): deliver worker results to a structured chat coordinator (#22631)
* feat(orchestration): deliver worker results to a structured chat coordinator
Resolve a Run's handle-less session coordinator and a session:<id> mailbox to
the live session, wake an evicted session for the delivery, redrive a session's
own mail on its idle edge, route a terminal view's pointer through its PTY, and
accept session:<id> (or a bare Orca session id) as a recipient.
* test(orchestration): pin coordinator delivery through the real session host, wake, idempotence and session addresses
* test(orchestration): type the coordinator mail fixture's attach params
* fix(orchestration): refuse session recipients with the caller codes, and treat a worker without its identity as undeliverable
* test(orchestration): pin a chat's terminal view reading the chat's coordinator mail
* test(orchestration): read coordinator mail fixtures through checked guards instead of assertions
* fix(orchestration): name a structured session's CLI by $ORCA_CLI_COMMAND in its pointer turn
* fix(orchestration): render the pointer's CLI invocation for the shell the session runs in
* fix(orchestration): address a session recipient where its check reads, so a structured worker gets its mail
* test(orchestration): pin the runtime's own idle-edge redrive wiring; say a released session is not running, not ended
* fix(orchestration): point mail that has not been pointed, not mail nobody has acked, naming the ack a held batch needs
* feat(orchestration): hand a /clear-replaced chat's Runs and unread mail to the session that replaced it
* feat(orchestration): adopt a /clear predecessor's Runs at the clear's commit, the edge its replacement's own status misses
* fix(orchestration): log a wake that could not resume a session, instead of retaining silently
* test(orchestration): give coordinator mail waits a budget that holds under a loaded parallel run
* refactor(orchestration): narrow a retained pointer's dispatch state by type guard instead of a cast
* fix(orchestration): give back a pointer whose admitted turn never ran
A pending send stamped its rows delivered and dropped its operation row, so a
provider that died before echoing left the last result pointed at nobody. The
lane now awaits the admitted turn's settlement: accepted consumes the claim,
anything else returns the rows and drops this send's operation row, so the
re-point on the next edge is a new send rather than a replay of unknown.
* fix(native-chat): keep a committed /clear from failing on its replacement observer
The observer runs after the clear's durable commit; a throwing adoption turned a
committed clear into a failed RPC. It is now best-effort and logged, like the
status feed's observer, and the successor's idle edges re-derive the adoption.
* test(orchestration): pin /clear adoption across a chain of clears and a predecessor's check
A session cleared twice before any edge hands both predecessors' Runs and mail
to the end of the chain. A predecessor still live in the clear's tail reads
none of the re-addressed mail: a session's direct mailbox is consume-on-read
and holds no replayable batch.
* test(orchestration): type the pending-settlement host test's send input without an assertion
* fix(orchestration): keep a chat's orchestration address across /clear by deriving its lineage
A chat's orchestration address is now the first session of its /clear lineage. Every session of the
lineage resolves to that one actor when it acts and when it is reached, and delivery goes to the
lineage's live session. Nothing is rewritten at a clear, so the predecessor-adoption path is gone:
the commit-edge observer, the idle-edge rebind, and the unread-mail re-address. That path could
unbind the successor's own Run and orphan all but one Run of a chain.
The idle edge now opens the orchestration database through its lazy getter and logs when it cannot,
instead of reading a field that stays null until the first orchestration call after a restart.
* fix(orchestration): give back a structured pointer claim an earlier process left open
A pointer the host admits as pending stamps its batch delivered, and only an in-memory settlement
waiter gives it back if no turn ran. A process that died in that window left the batch stamped with
nothing to release it, so the last result on that mailbox was never pointed again. When the
database opens, every surviving pointer operation row is from an earlier process: its stamped batch
is found by the row's fingerprint, released, and the row dropped, before the restored-mailbox scan
points it again. A row whose batch was never stamped keeps its id for the retry.
* test(orchestration): pin that a cleared chat's sends carry its conversation's address
* fix(orchestration): open the orchestration database at an idle edge only when it already exists
A profile with no orchestration database has no mail to redrive, so a structured chat's idle edge no
longer creates one, and says nothing. An existing database is still opened lazily there.
* fix(orchestration): read a chat-coordinated Run's session through the actor's generation
A handle-less Run names its coordinator session only by an actor that still counts at the Run's
current generation, the same rule every other binding read uses; an actor an older binary's rebind
or unbind left behind no longer routes the Run's mail. A test pins that a cleared chat's run-create
and run-use write its conversation's root actor at the Run's current generation.
* refactor(orchestration): address a session's conversation by its bare root Orca session id
Carries the Orca session id rename into coordinator delivery. A session's orchestration
identity holds its conversation's bare Orca session id, the /clear lineage root's, and
its mail address is derived from it by formatOrcaSessionAddress; a Run a cleared chat
creates or uses stores that bare root id. A session recipient carries the parsed id and
its address as distinct types, a handle-less coordinator's session is read through
currentRunCoordinatorOrcaSessionId, and session ids read from records or PTY bindings
are checked with isOrcaSessionId before they become an identity. The session address
prefix comes from the one exported constant.
* refactor(orchestration): canonicalize a cleared session through the one id hook and the party resolver
- canonicalOrcaSessionId now walks a session's /clear lineage to its root; the
parallel session identity and lost-worker rule are deleted, so the caller
resolver, recipient routing, reach and idle-edge mailboxes all resolve a
session through resolveOrcaSessionParty.
- A Dispatch row's assignee_orca_session_id goes through the same hook.
- The terminal-view delivery lane is gone with the terminal handoff: no PTY is
bound to a session, so a chat's mail is always a session turn.
- Pins a send to a Run-less chat's session address after a restart, when the
send must start the agent-session host before routing reads its record.
* fix(orchestration): point a structured session with the PTY lane's exact text
A chat or structured worker is now told what a terminal agent is told: the pointer is
formatMessagePointer with the CLI name the PTY lane resolves for a local terminal (orca, or
orca-dev in a dev build), with no shell-specific invocation and no ack lesson. The lane still
excludes the batch a reader holds unacknowledged and points newer mail; that stays host-side,
and the reader's own check replays the held batch and names its ack as it does for a terminal.
* fix(orchestration): deliver a cleared structured worker's mail to its live successor
A terminal keeps its handle across /clear; a structured worker's successor now does the same.
Mail at the worker's handle, its dispatch mailbox, and a Run it coordinates resolved to the
session minted for the worker, which /clear replaced. Each now walks the /clear lineage forward
to the live session, which the caller resolver already treats as the worker.
* test(orchestration): compare a chat's pointer turn to the PTY lane's text for this build's CLI name
* refactor(orchestration): resolve the local CLI name once, for the PTY lane and the structured lane alike
* fix(orchestration): let a /clear-ed chat restate its address, placed by the host's lineage
The CLI entry compared a restated --from/--terminal with the injected session id as a
plain string, so a cleared chat restating the address it had before the clear (its lineage
root, the address it keeps) was refused. Only the host's session records know the lineage,
so a session address that is not this session's own spelling is now sent as the caller
param, where the host's canonical-id check accepts it or refuses it before any effect.
Plain restatements are still dropped and any other name is still refused at the entry.
* test(orchestration): read the coordinator journal through the async snapshot, and wait for the held turn's handover
Main made journalSnapshot async and delivers an accepted send once the host hands it over, so the fixture awaits the snapshot and waits for the provider's turn before echoing it.
* refactor(orchestration): point a chat whose agent is not running through the plain send
Main's host no longer has hold/release: an accepted send starts the agent itself. The
pointer lane's wake step called host.hold, which no longer exists, so it is deleted
from the pointer host, the delivery lane and their tests. An idle or evicted chat gets
its pointer through the same send a user message takes; the claim is still consumed
only on accepted and given back otherwise.
* fix(orchestration): leave a chat whose provider died stopped instead of respawning it for mail
A pointer whose provider died before echoing it is given back. The death's own status
edge then redrove the mail, and since a send starts the agent, a provider that died on
every turn was restarted about once a second for as long as the mail was unread. The
pointer lane now reads, on every attempt, whether the session's latest send never ran
because its provider exited or could not start, and holds the mail until a later send
runs. Every trigger passes through that gate: a parked retry, the idle edge's re-derive
and new mail. A rejection for any other reason still points at the next idle edge.
* fix(orchestration): hold mail after a start the person must fix, placing every failure kind
A start refused for a reason only the person can fix (not signed in, history too large,
a managed-account problem) or one the host stopped because it never came is held like a
failed start: the mail waits for the person's next message, which also retries the start.
An account switch still in progress is transient, so it stays ungated and the first edge
after the switch settles points the mail. One exhaustive record places every rejection
kind, so a new kind does not compile until it is placed.
* fix(orchestration): retry a structured pointer under its own id instead of gating on the failure reason
A pointer send that failed was given back and re-sent under a fresh operation id,
so every status edge after a provider death was a new send that started the provider
again. A reason-string gate held some of those deaths, but missed a Codex crash with
turn/start in flight (it settles unknown with the connection's error), latched all later
mail after one transient death, and did not keep a user's Stop.
A retry now reuses the mailbox's operation id, which the host answers by replaying the
recorded verdict without reaching the provider. The id is re-minted only for new mail,
after a later send ran, for a row an earlier process left, or once an account switch
settles. Rows are stamped only on accepted, so the admitted-stage claim, its give-back
and the restart claim-release scan are gone.
* test(orchestration): pin that a Stop keeps a pointer unsent before the next status edge, too
* fix(orchestration): point a structured chat's mail by the same rule as a terminal's
The chat lane pointed newer mail past a batch its reader had checked and not
acknowledged, while the terminal lane skips a mailbox until that batch is acked. A chat
now waits for the ack the same way a terminal does, and the lookup that let the chat lane
filter the held batch out is removed.
* fix(orchestration): refuse a session address that gate-list or task-list --run would drop
The CLI entry lets a `session:` address that is not the session's own spelling through
for the host to place, but gate-list and task-list send no caller when --run names the
Run, so `--from session:<other>` was silently ignored. With --run they now refuse it
(consumer_fenced) before any request, the same as a conflicting terminal handle.
* fix(orchestration): keep a failed mail redrive from skipping a chat's first-turn workspace rename
A structured session's status callback redrives its mail before the first-turn workspace
auto-rename, outside any try, so a database error there threw past the rename. The redrive
now logs its failure and returns.
* chore: take main's pnpm-lock.yaml the merge of origin/main left stale
* fix(orchestration): give a stamp or park decision its own variant so the send branch narrows
* fix(orchestration): re-mint a held structured pointer from facts that cannot strand it
A pointer row whose id had no submission in the journal was always resent under that id,
before any re-mint test ran. After a rewind rebuilt the journal, or a send refused before it
was recorded aged past the host's 24h admission window, the mail was held forever: neither
the person's next turn nor a restart pointed it again.
The re-mint tests now run first. "The agent has run since" is any accepted send submitted
after the row was minted; "an earlier process minted it" is a row whose id this lane did not
send, not a wall-clock comparison a clock step could fool; and a row the host never recorded
is re-minted once it is too old for the host to admit. The account-switch exception is gone:
nothing tells the lane when a switch ends, and each outside edge during one added another
pointer and failure to the chat. A parked pointer now retains as turn-unsettled.
* fix(orchestration): let the host check a session caller that gate-list or task-list --run names
|
||
|
|
2ca4ecbc61 |
feat(orchestration): let a structured chat run orchestration as itself (#22568)
* feat(orchestration): inject the Orca session id into structured children and let the CLI act as it Every structured session's child (native Claude, native Codex, and the terminal view) carries ORCA_AGENT_SESSION_ID and reaches the Orca CLI. The CLI sends the id in the orchestration envelope; when present it is the caller, and a caller flag naming anyone else is refused before any request. The id is stripped from inherited PTY env and from the SSH host-CLI passthrough, and crosses into WSL so the host can refuse the cross-host claim. * test(orchestration): pin session id injection for native Claude, native Codex, the terminal view, WSL, PTY inheritance and SSH * test(orchestration): pin one caller precedence rule across every CLI verb that names its caller Adds the per-verb table (flagless acts as the session; a conflicting --from or --terminal is refused before any request; the session's own spellings are accepted), the enumerated guess population with its positive control, the structured worker's own handle, the identity-less refusal for an older child, the unchanged terminal agent, and the envelope. dispatch-show's --from only fills preview text, so it passes through unfenced and a session's flagless preview names the address the real dispatch writes. * refactor(orchestration): keep the identity-less marker reader to the marker; the id is checked first * test(orchestration): pin that a host refusal of the session surfaces verbatim from the CLI * fix(orchestration): keep the identity-less marker beside the id for CLIs that predate it A CLI older than the id, reached through a global install when a shell rc resets PATH, would otherwise guess a sibling's terminal in a chat that no longer carries the marker. It refuses on the marker instead; a current CLI checks the id first, so the marker never makes a session with an id identity-less. * fix(orchestration): refuse a conflicting --from on gate-list and task-list scoped by --run A --run listing needs no caller, so both handlers skipped the resolver and a --from naming another actor was dropped silently under a session. The conflict check now runs on that branch too; terminal callers are unchanged. * fix(orchestration): name this app's CLI by absolute path for a structured session's login shells A provider can run each command in a login shell: Codex runs zsh -lc, and the profile rebuilds PATH, putting a global install (possibly an older Orca) ahead of the directory Orca prepended. ORCA_CLI_COMMAND, which an agent resolves the CLI from first, is now the absolute launcher in that directory (the native launcher on Windows), so no shell's startup files can swap it. The PATH prepend stays for shells that read no profile. Found by the live coordinator run of the next PR. * test(orchestration): pin a structured worker's CLI command as this app's absolute launcher * test(orchestration): run the zsh login-shell arm in the real-shell lane that installs zsh The ordinary Linux unit lane has no /bin/zsh, so the zsh arm failed there with ENOENT. It moves to a live-shell file registered in the shell-contracts lane; the bash arm keeps running in every lane. The lane guard's detector now also sees a zsh spawned through the ProcessSpec program field, which is how this test escaped it. * fix(orchestration): omit a structured child's CLI command when no launcher resolves, and pin its instance A bare `orca` fallback named GNOME's screen reader on packaged Linux, and an inherited value named another app's CLI. The builder now deletes any inherited value, sets the absolute launcher only when one resolved, and pins ORCA_USER_DATA_PATH so a current CLI dials the instance that minted the id. Renames the marker reader to hasStructuredSessionMarker and records why the terminal view carries the id without the marker. * fix(terminal): name this app's CLI launcher by absolute path in every local terminal ORCA_CLI_COMMAND meant three things by lane: an absolute launcher for a structured session, a bare name for WSL, and nothing for any other terminal, so a structured session's terminal view lost it. Local terminals now get the same absolute launcher the structured lane gets; WSL keeps its guest command name, and a terminal whose launcher does not resolve still gets none. * feat(cli): hand a command to the session's own CLI when another Orca CLI was invoked A login shell can reorder PATH behind a global install, and an agent or its helper script can run bare `orca`, so the binary that answered depended on the agent following instructions. Orca's packaged launchers and bare-orca shims now export ORCA_CLI_SELF (outermost wins). At the CLI entry, when it names a different launcher than ORCA_CLI_COMMAND, the command re-runs once through the named launcher with ORCA_CLI_REEXEC=1 and exits with its status; both variables are consumed so no child inherits them. Dev launchers export no self on purpose, WSL and SSH names never qualify, and a launcher that cannot start leaves the command to run here. The Windows launcher no longer rewrites ORCA_CLI_COMMAND; the legacy ask protocol normalizes its resume command itself. * refactor(orchestration): declare which flag names the caller on each spec and refuse at the CLI entry Each handler hand-classified its --from/--terminal as the caller or a target, and the refusal of a conflicting caller flag ran inside the caller resolver plus two standalone calls for --run listings, so a new verb that read its flag raw would pass a sibling's handle to a pre-session host. Specs now declare identityFlagRoles, the CLI entry refuses a conflicting caller flag once from the spec, the resolver only applies the id-wins rule, and a test fails any orchestration verb that accepts --from or --terminal without classifying it. * perf(cli): keep the session caller check off the actor codec's module graph The check runs at the CLI entry for every command, and the actor codec pulls zod through the session record. Compare the session's own spellings as plain strings instead. * refactor(cli): spell a session's address from the one prefix constant, off the codec's module graph The Orca session address prefix moves to a leaf module with no imports, re-exported by the address codec, so the CLI entry check derives `session:<id>` from that constant instead of re-typing it and still stays off the codec's zod graph. Prose and test names say caller or Orca session id, not actor. * refactor(orchestration): drop the session id's terminal-view spawn now that the handoff is gone The terminal handoff was removed, so no terminal is ever a structured session: - delete the terminal-view identity env and its WSL passthrough, and their tests; - strip the session caller keys from every terminal's env unconditionally; - the CLI's own-address spelling moves beside the injected id in src/shared, with a test pinning it to the address the host's party resolver gives that session. * fix(terminal): run the Codex launch preflight through the CLI the terminal names Packaged Linux names the userData shim in ORCA_CLI_COMMAND, while the preflight ran the bundled launcher behind it. The CLI saw a different launcher and handed the preflight off to the shim, booting Electron twice before every codex launch. * revert(terminal): keep terminals on main's ORCA_CLI_COMMAND and Codex preflight Only a structured session needs an absolute ORCA_CLI_COMMAND; local terminals go back to naming none (WSL keeps its guest command), and the Codex launch preflight goes back to the bundled launcher. The CLI handoff is scoped to sessions, so a terminal's preflight can no longer be handed off and start Electron twice. This reverts commit |