mirror of
https://github.com/stablyai/orca.git
synced 2026-10-31 09:07:40 +00:00
152 lines
5.7 KiB
TypeScript
152 lines
5.7 KiB
TypeScript
import { ResumeSource } from './rpc-contract/structured-agent-session-params'
|
|
import { STRUCTURED_AGENT_SESSION_CLI_SOURCE_RUNTIME_CAPABILITY } from './structured-agent-session-surface-capabilities'
|
|
import { describe, expect, it } from 'vitest'
|
|
import { RUNTIME_CAPABILITIES } from './protocol-version'
|
|
import { AGENT_SESSION_CREATE_MESSAGE_RUNTIME_CAPABILITY } from './agent-session-create-capabilities'
|
|
|
|
import {
|
|
structuredAgentSessionCreateParams,
|
|
type StructuredAgentSessionFirstMessage,
|
|
type StructuredAgentSessionResumeSource
|
|
} from './structured-agent-session-create'
|
|
import {
|
|
structuredAgentSessionCreateFingerprint,
|
|
structuredAgentSessionPayloadFingerprint
|
|
} from './structured-agent-session-mutation'
|
|
|
|
const SESSION_ID = 'codex_11111111_2222_3333_4444_555555555555'
|
|
const RESUME = { providerSessionId: 'thread-abc' }
|
|
|
|
/** Distinct per call so two envelopes never share an operation id by accident. */
|
|
let uuidCounter = 0
|
|
function nextUuid(): string {
|
|
uuidCounter += 1
|
|
return `00000000-0000-4000-8000-${String(uuidCounter).padStart(12, '0')}`
|
|
}
|
|
|
|
function createParams(
|
|
overrides: {
|
|
resumeFrom?: StructuredAgentSessionResumeSource
|
|
tabId?: string
|
|
firstMessage?: StructuredAgentSessionFirstMessage
|
|
options?: Record<string, string>
|
|
} = {}
|
|
) {
|
|
return structuredAgentSessionCreateParams({
|
|
sessionId: SESSION_ID,
|
|
worktree: 'id:repo-1::/repo/orca',
|
|
agent: 'codex',
|
|
...overrides,
|
|
randomUuid: nextUuid,
|
|
now: 1_800_000_000_000
|
|
})
|
|
}
|
|
|
|
describe('structured agent session create params', () => {
|
|
it('fingerprints the first message identity, content and opening option picks', () => {
|
|
const firstMessage: StructuredAgentSessionFirstMessage = {
|
|
clientMessageId: 'message-1',
|
|
body: { kind: 'message', role: 'user', blocks: [{ type: 'text', text: 'opening text' }] }
|
|
}
|
|
const first = createParams({ firstMessage, options: { model: 'picked-model' } })
|
|
expect(first.firstMessage).toEqual(firstMessage)
|
|
expect(first.options).toEqual({ model: 'picked-model' })
|
|
expect(first.envelope.payloadFingerprint).toBe(
|
|
createParams({ firstMessage, options: { model: 'picked-model' } }).envelope.payloadFingerprint
|
|
)
|
|
for (const changed of [
|
|
createParams(),
|
|
createParams({
|
|
firstMessage: { ...firstMessage, clientMessageId: 'message-2' },
|
|
options: { model: 'picked-model' }
|
|
}),
|
|
createParams({
|
|
firstMessage: {
|
|
...firstMessage,
|
|
body: { ...firstMessage.body, blocks: [{ type: 'text', text: 'different text' }] }
|
|
},
|
|
options: { model: 'picked-model' }
|
|
}),
|
|
createParams({ firstMessage, options: { model: 'other-model' } })
|
|
]) {
|
|
expect(changed.envelope.payloadFingerprint).not.toBe(first.envelope.payloadFingerprint)
|
|
}
|
|
expect(RUNTIME_CAPABILITIES).toContain(AGENT_SESSION_CREATE_MESSAGE_RUNTIME_CAPABILITY)
|
|
expect(AGENT_SESSION_CREATE_MESSAGE_RUNTIME_CAPABILITY).toBe('agentSession.create.message.v1')
|
|
})
|
|
|
|
it('separates an adopting create from a blank one and from another row', () => {
|
|
const blank = createParams().envelope.payloadFingerprint
|
|
const adopted = createParams({ resumeFrom: RESUME }).envelope.payloadFingerprint
|
|
const otherRow = createParams({
|
|
resumeFrom: { providerSessionId: 'thread-other' }
|
|
}).envelope.payloadFingerprint
|
|
|
|
expect(adopted).not.toBe(blank)
|
|
expect(otherRow).not.toBe(adopted)
|
|
})
|
|
|
|
it('leaves a blank create byte-identical to the pre-resume digest', () => {
|
|
// Pin the legacy no-resume digest; new fields must not break mixed-version create fingerprints.
|
|
expect(createParams().envelope.payloadFingerprint).toBe(
|
|
structuredAgentSessionPayloadFingerprint({
|
|
method: 'agentSession.create',
|
|
sessionId: SESSION_ID,
|
|
fields: { worktree: 'id:repo-1::/repo/orca', agent: 'codex' }
|
|
})
|
|
)
|
|
expect(createParams().envelope.payloadFingerprint).toBe(
|
|
'56cb15e22414c0f62fd89d77d00d2d6a0a422f16e95edee154fb8b5bf53fbbc3'
|
|
)
|
|
})
|
|
})
|
|
|
|
describe('the tab id a create reserves', () => {
|
|
it('rides the params and the fingerprint together', () => {
|
|
const params = createParams({ tabId: 'tab-1' })
|
|
|
|
expect(params.tabId).toBe('tab-1')
|
|
expect(params.envelope.payloadFingerprint).toBe(
|
|
structuredAgentSessionCreateFingerprint({
|
|
sessionId: SESSION_ID,
|
|
worktree: 'id:repo-1::/repo/orca',
|
|
agent: 'codex',
|
|
tabId: 'tab-1'
|
|
})
|
|
)
|
|
// The declared digest covers the tab; the host still replays a retry on its attach fingerprint.
|
|
expect(params.envelope.payloadFingerprint).not.toBe(createParams().envelope.payloadFingerprint)
|
|
})
|
|
})
|
|
|
|
it('negotiates a strict pane identity and includes it in the create fingerprint without accepting paths', () => {
|
|
const source = {
|
|
providerSessionId: 'thread-abc',
|
|
cliSource: { workspaceId: 'folder', paneKey: 'tab:leaf' }
|
|
}
|
|
expect(RUNTIME_CAPABILITIES).toContain(STRUCTURED_AGENT_SESSION_CLI_SOURCE_RUNTIME_CAPABILITY)
|
|
expect(ResumeSource.parse(source)).toEqual(source)
|
|
expect(ResumeSource.safeParse({ ...source, transcriptPath: '/client/path' }).success).toBe(false)
|
|
expect(
|
|
ResumeSource.safeParse({
|
|
...source,
|
|
cliSource: { ...source.cliSource, accountHome: '/client/home' }
|
|
}).success
|
|
).toBe(false)
|
|
const first = createParams({ resumeFrom: source })
|
|
expect(first.envelope.payloadFingerprint).not.toBe(
|
|
createParams({ resumeFrom: RESUME }).envelope.payloadFingerprint
|
|
)
|
|
expect(first.envelope.payloadFingerprint).toBe(
|
|
createParams({ resumeFrom: source }).envelope.payloadFingerprint
|
|
)
|
|
expect(first.envelope.payloadFingerprint).not.toBe(
|
|
createParams({
|
|
resumeFrom: {
|
|
...source,
|
|
cliSource: { ...source.cliSource, paneKey: 'other:leaf' }
|
|
}
|
|
}).envelope.payloadFingerprint
|
|
)
|
|
})
|