mirror of
https://github.com/stablyai/orca.git
synced 2026-11-13 09:27:29 +00:00
* refactor(shared): organize session contracts and projections * chore: tighten dropped-path flaky pattern allowance * docs: preserve current agent instructions with valid formatting
119 lines
5.1 KiB
TypeScript
119 lines
5.1 KiB
TypeScript
// Run current outbox-built sends against a real release; both schema admission and operation fingerprints must preserve the user’s message.
|
||
|
||
import { beforeAll, describe, expect, it } from 'vitest'
|
||
import { AGENT_SESSION_SEND_OPTIONS_RUNTIME_CAPABILITY } from '../../../src/shared/agent-session/agent-session-create-capabilities'
|
||
import { resolveBaselineReleaseRef } from './release-checkout'
|
||
import { installableHost, structuredHostStub } from './structured-agent-session-host-fixture'
|
||
import {
|
||
resetOperationIds,
|
||
sendParams,
|
||
SESSION,
|
||
WORKSPACE
|
||
} from './structured-agent-session-surface-manifest'
|
||
import {
|
||
loadAgentSessionWireBuild,
|
||
WORKING_TREE,
|
||
type AgentSessionWireBuild,
|
||
type RpcReply,
|
||
type SentMessage
|
||
} from './versioned-agent-session-wire'
|
||
|
||
// The first published release whose host advertises `agent-session.send-options.v1` (#27826).
|
||
const SEND_OPTIONS_RELEASE_REF = 'v1.4.228'
|
||
|
||
const SUITE_TIMEOUT_MS = 180_000
|
||
|
||
let current: AgentSessionWireBuild
|
||
let baseline: AgentSessionWireBuild
|
||
|
||
beforeAll(async () => {
|
||
current = await loadAgentSessionWireBuild(WORKING_TREE)
|
||
baseline = await loadAgentSessionWireBuild(resolveBaselineReleaseRef())
|
||
}, SUITE_TIMEOUT_MS)
|
||
|
||
async function send(build: AgentSessionWireBuild, params: unknown): Promise<RpcReply[]> {
|
||
const replies: RpcReply[] = []
|
||
await build
|
||
// Releases before the setting gate was dropped admit structured calls only with it on.
|
||
.createDispatcher({
|
||
getRuntimeId: () => 'runtime-1',
|
||
getClientSettings: () => ({ experimentalStructuredNativeChat: true })
|
||
})
|
||
.dispatchStreaming(
|
||
{ id: 'request-send', authToken: 'cross-version-token', method: 'agentSession.send', params },
|
||
(raw) => replies.push(JSON.parse(raw)),
|
||
{ clientKind: 'runtime', clientCapabilities: current.capabilities }
|
||
)
|
||
return replies
|
||
}
|
||
|
||
describe('a current client sending a message', () => {
|
||
it.each([
|
||
['a plain message', undefined],
|
||
// `delivery` joins the operation fingerprint, so a builder that drops it is refused by a host
|
||
// that digests it.
|
||
['a message held as a draft while a turn runs', 'queue-if-active' as const]
|
||
])('is accepted for %s, under the fingerprint each build re-derives', async (_case, delivery) => {
|
||
// Anti-vacuous: the release must have the method, or there is no older host to send to.
|
||
expect(baseline.methodNames).toContain('agentSession.send')
|
||
for (const build of [current, baseline]) {
|
||
resetOperationIds()
|
||
const hostCalls = structuredHostStub(SESSION, WORKSPACE)
|
||
await build.installStructuredHost(installableHost(hostCalls))
|
||
try {
|
||
const replies = await send(build, sendParams('hi', 1, delivery))
|
||
expect(replies, `${build.label}: ${JSON.stringify(replies)}`).toMatchObject([{ ok: true }])
|
||
expect(hostCalls.send, `${build.label}: the send reached the host`).toHaveBeenCalledTimes(1)
|
||
const sent: SentMessage = hostCalls.send.mock.calls[0]?.[1]
|
||
expect(sent.delivery, `${build.label}: delivery reached the host`).toBe(delivery)
|
||
// Past the fingerprint check, admission stops at the journal this harness never opens.
|
||
expect(await build.admitSend(sent), `${build.label}: send admission`).toMatchObject({
|
||
ok: false,
|
||
refusal: { details: { reason: 'sessionNotAttached' } }
|
||
})
|
||
// Negative control: proves the fingerprint check ran before admission reached the journal.
|
||
const wrong = { ...sent.envelope, payloadFingerprint: '0'.repeat(64) }
|
||
expect(await build.admitSend({ ...sent, envelope: wrong }), build.label).toMatchObject({
|
||
refusal: { details: { reason: 'fingerprintMismatch' } }
|
||
})
|
||
} finally {
|
||
await build.installStructuredHost(null)
|
||
}
|
||
}
|
||
})
|
||
})
|
||
|
||
describe('a current client sending a message with its choices', () => {
|
||
it(
|
||
'is accepted, and the choices reach the host, on a host that advertises them',
|
||
async () => {
|
||
const release = await loadAgentSessionWireBuild(SEND_OPTIONS_RELEASE_REF)
|
||
const options = { model: 'sonnet', effort: 'high' }
|
||
for (const build of [current, release]) {
|
||
expect(build.capabilities, build.label).toContain(
|
||
AGENT_SESSION_SEND_OPTIONS_RUNTIME_CAPABILITY
|
||
)
|
||
resetOperationIds()
|
||
const hostCalls = structuredHostStub(SESSION, WORKSPACE)
|
||
await build.installStructuredHost(installableHost(hostCalls))
|
||
try {
|
||
const replies = await send(build, sendParams('hi', 1, undefined, options))
|
||
expect(replies, `${build.label}: ${JSON.stringify(replies)}`).toMatchObject([
|
||
{ ok: true }
|
||
])
|
||
const sent: SentMessage = hostCalls.send.mock.calls[0]?.[1]
|
||
expect(sent.options, `${build.label}: choices reached the host`).toEqual(options)
|
||
// `options` joins the operation fingerprint on both builds, so admission gets past it.
|
||
expect(await build.admitSend(sent), `${build.label}: send admission`).toMatchObject({
|
||
ok: false,
|
||
refusal: { details: { reason: 'sessionNotAttached' } }
|
||
})
|
||
} finally {
|
||
await build.installStructuredHost(null)
|
||
}
|
||
}
|
||
},
|
||
SUITE_TIMEOUT_MS
|
||
)
|
||
})
|