mirror of
https://github.com/stablyai/orca.git
synced 2026-10-01 08:01:56 +00:00
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.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import type { CommandHandler } from '../../dispatch'
|
||||
import { printResult } from '../../format'
|
||||
import { getOptionalJsonFlag, getOptionalStringFlag, getRequiredStringFlag } from '../../flags'
|
||||
import { refuseUnsentSessionAddress } from '../../session-caller-flags'
|
||||
import { callOrchestrationMutation } from './mutation-request'
|
||||
import { resolveCoordinatorTerminalHandle } from './terminal-identity'
|
||||
|
||||
@@ -36,6 +37,9 @@ export const ORCHESTRATION_GATE_HANDLERS: Record<string, CommandHandler> = {
|
||||
|
||||
'orchestration gate-list': async ({ flags, client, cwd, json }) => {
|
||||
const run = getOptionalStringFlag(flags, 'run')
|
||||
if (run) {
|
||||
refuseUnsentSessionAddress(flags, 'from')
|
||||
}
|
||||
// Why: named runs remain inspectable without a pane; only implicit runs resolve identity.
|
||||
const from = run ? undefined : await resolveCoordinatorTerminalHandle(flags, cwd, client)
|
||||
const result = await client.call<{
|
||||
|
||||
@@ -3,6 +3,7 @@ import { printResult } from '../../format'
|
||||
import { getOptionalStringFlag, getRequiredStringFlag } from '../../flags'
|
||||
import { RuntimeClientError } from '../../runtime-client'
|
||||
import { abbreviateOrchestrationTasks } from '../../../shared/orchestration-task-summary'
|
||||
import { refuseUnsentSessionAddress } from '../../session-caller-flags'
|
||||
import { callOrchestrationMutation } from './mutation-request'
|
||||
import { resolveCoordinatorTerminalHandle } from './terminal-identity'
|
||||
|
||||
@@ -38,6 +39,9 @@ export const ORCHESTRATION_TASK_HANDLERS: Record<string, CommandHandler> = {
|
||||
'orchestration task-list': async ({ flags, client, cwd, json }) => {
|
||||
const brief = flags.has('brief')
|
||||
const run = getOptionalStringFlag(flags, 'run')
|
||||
if (run) {
|
||||
refuseUnsentSessionAddress(flags, 'from')
|
||||
}
|
||||
const callerTerminalHandle = run
|
||||
? undefined
|
||||
: await resolveCoordinatorTerminalHandle(flags, cwd, client)
|
||||
|
||||
@@ -360,6 +360,17 @@ describe.each([
|
||||
await invoke(command, flagMap({ run: 'run_1', from: `session:${SESSION}` }))
|
||||
expect(callsTo(method)[0]?.[callerParam]).toBeUndefined()
|
||||
})
|
||||
|
||||
it('refuses a session address it would not send for the host to check, before any request', async () => {
|
||||
// With --run no caller is sent, so a `session:` address the host alone could place (another
|
||||
// chat's, or this chat's pre-/clear root) would be dropped unchecked.
|
||||
for (const other of [`session:${ROOT}`, 'session:0b5e2d7c-9a41-4c3e-8f62-7d1a3e5b9c08']) {
|
||||
await expect(invoke(command, flagMap({ run: 'run_1', from: other }))).rejects.toMatchObject({
|
||||
code: 'consumer_fenced'
|
||||
})
|
||||
}
|
||||
expect(callMock).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
describe('the identity a session presents', () => {
|
||||
|
||||
@@ -36,16 +36,44 @@ export function refuseConflictingSessionCallerFlags(
|
||||
!namesInjectedSession(declared, sessionId, env) &&
|
||||
!sessionAddressForHost(declared, sessionId, env)
|
||||
) {
|
||||
throw new RuntimeClientError(
|
||||
'consumer_fenced',
|
||||
`This command runs as agent session ${sessionId}, so --${flagName} ${declared} would act as a ` +
|
||||
`different caller. Drop --${flagName}: this session's orchestration commands already act ` +
|
||||
`as this session. No request was sent.`
|
||||
)
|
||||
throw conflictingCallerFlag(sessionId, flagName, declared)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For a verb that sends no caller (`--run` names the Run), a session address the host would
|
||||
* otherwise have checked is refused here, rather than dropped unchecked.
|
||||
*/
|
||||
export function refuseUnsentSessionAddress(
|
||||
flags: ReadonlyMap<string, string | boolean>,
|
||||
flagName: IdentityFlag,
|
||||
env: NodeJS.ProcessEnv = process.env
|
||||
): void {
|
||||
const sessionId = readInjectedAgentSessionId(env)
|
||||
const declared = flags.get(flagName)
|
||||
if (
|
||||
sessionId &&
|
||||
typeof declared === 'string' &&
|
||||
sessionAddressForHost(declared, sessionId, env)
|
||||
) {
|
||||
throw conflictingCallerFlag(sessionId, flagName, declared)
|
||||
}
|
||||
}
|
||||
|
||||
function conflictingCallerFlag(
|
||||
sessionId: string,
|
||||
flagName: IdentityFlag,
|
||||
declared: string
|
||||
): RuntimeClientError {
|
||||
return new RuntimeClientError(
|
||||
'consumer_fenced',
|
||||
`This command runs as agent session ${sessionId}, so --${flagName} ${declared} would act as a ` +
|
||||
`different caller. Drop --${flagName}: this session's orchestration commands already act ` +
|
||||
`as this session. No request was sent.`
|
||||
)
|
||||
}
|
||||
|
||||
const IDENTITY_FLAGS: readonly IdentityFlag[] = ['from', 'terminal']
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user