mirror of
https://github.com/stablyai/orca.git
synced 2026-10-24 00:04:01 +00:00
fix(orchestration): refuse title evidence when publishing identity for routing
Rebuilt on current main so this carries the hardened parser from #16148 and the corrected resolver from #16157 (authority-scoped run keys, no order-dependent duplicate resolution). Applies the resolver's new `minimumSource` floor at the publisher. What this publishes authorizes an action — routing decides which real agent pane receives a message — so ranking title last is not enough; the floor removes it from consideration entirely, and no amount of reordering by a caller can bring it back. The trade, stated because it is a real capability loss: a hook-less agent over SSH that Orca did not launch, and whose foreground process the host cannot read, is no longer addressable by @agent. Accepted because a message delivered into the wrong agent's prompt is unrecoverable while an undelivered one is visible — the sender sees zero recipients. Whether real panes actually carry launch/foreground evidence is the open question, and is what live validation must answer.
This commit is contained in:
@@ -30,10 +30,17 @@ describe('resolvePublishedPaneAgentIdentity', () => {
|
||||
expect(resolve({ launchAgent: 'claude', foregroundAgent: 'codex' })).toBe('codex')
|
||||
})
|
||||
|
||||
it('falls back to the title when the host has no process or launch evidence', () => {
|
||||
// Hook-less agents over SSH surface only a decorated title; dropping it would make those
|
||||
// panes unaddressable. It is last, not absent.
|
||||
expect(resolve({ title: '✳ Claude Code' })).toBe('claude')
|
||||
it('publishes nothing from a title alone, even an unambiguous one', () => {
|
||||
// Deliberate, and the sharpest trade in this change. What this publishes AUTHORIZES ACTIONS:
|
||||
// orchestration routing uses it to pick which real agent pane receives a message. A title is
|
||||
// a decoration channel, and a stale one outlives the agent that wrote it, so a pane whose only
|
||||
// evidence is a parsed string publishes no identity and every action consumer fails closed.
|
||||
//
|
||||
// The cost: a hook-less agent over SSH that Orca did not launch, and whose foreground process
|
||||
// the host cannot read, is not addressable by @agent. That is a real capability loss, accepted
|
||||
// because a message delivered into the wrong agent's prompt is not recoverable and an
|
||||
// undelivered one is — the sender sees zero recipients.
|
||||
expect(resolve({ title: '✳ Claude Code' })).toBeUndefined()
|
||||
})
|
||||
|
||||
it('publishes nothing when the title names no agent unambiguously', () => {
|
||||
|
||||
@@ -8,10 +8,10 @@ import type { TuiAgent } from './tui-agent'
|
||||
* Kept out of the runtime class so it can be tested without one, and so routing, delivery and the
|
||||
* UI all read the same decision instead of each re-deriving it.
|
||||
*
|
||||
* Title is included but ranks last, and contributes only when the evidence parser finds an
|
||||
* unambiguous name. A task title that merely mentions an agent yields no title evidence at all,
|
||||
* which is the whole point: "Switch Claude and Codex off the load balancer… - grok" is a Grok
|
||||
* pane, and used to receive both `@claude` and `@codex`.
|
||||
* Title is NOT consulted. What this publishes authorizes actions, and a terminal title is a
|
||||
* decoration channel a user can type any agent's name into — "Switch Claude and Codex off the
|
||||
* load balancer… - grok" is a Grok pane that used to receive both `@claude` and `@codex`.
|
||||
* Identity therefore comes only from launch and foreground-process evidence the host owns.
|
||||
*
|
||||
* Returns undefined when nothing is known, and absence is published as absence. A caller that
|
||||
* authorizes an action must fail closed on it rather than falling back to parsing the title.
|
||||
@@ -24,6 +24,11 @@ export function resolvePublishedPaneAgentIdentity(args: {
|
||||
const titleAgent = args.title ? collectAgentTitleEvidence(args.title).agent : null
|
||||
return (
|
||||
resolvePaneAgentIdentity({
|
||||
// Why the floor: what this publishes authorizes actions — orchestration routing decides
|
||||
// which real agent pane receives a message. Ranking title last makes a bad delivery
|
||||
// unlikely; refusing to see it makes one impossible. A pane whose only evidence is a
|
||||
// parsed string publishes no identity, and every action consumer fails closed on absence.
|
||||
minimumSource: 'launch',
|
||||
evidence: [
|
||||
...(args.foregroundAgent
|
||||
? [{ source: 'process' as const, agent: args.foregroundAgent }]
|
||||
|
||||
Reference in New Issue
Block a user