mirror of
https://github.com/stablyai/orca.git
synced 2026-11-03 10:37:35 +00:00
Each list that decides delivered / undelivered / recoverable / definitive by code now has a test naming its codes, the message fragments it relies on, and nearby codes it must not treat the same. Includes the #30146 shape: a client-minted runtime_manually_disconnected keeps a user's close for replay. Tests only; no production change.
160 lines
5.4 KiB
TypeScript
160 lines
5.4 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
import type { AgentJournalSubmission } from './agent-session-journal-types'
|
|
import type { AgentSessionWireRefusal } from './agent-session-wire'
|
|
import {
|
|
structuredAgentSessionSendEvidence,
|
|
type StructuredAgentSessionSendAnswer
|
|
} from './structured-agent-session-send-evidence'
|
|
|
|
function refused(code: string, reason?: string): StructuredAgentSessionSendAnswer {
|
|
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: test fixture names codes from the wire list.
|
|
const refusal = {
|
|
code,
|
|
message: code,
|
|
...(reason ? { details: { reason } } : {})
|
|
} as AgentSessionWireRefusal
|
|
return { kind: 'result', result: { ok: false, refusal } }
|
|
}
|
|
|
|
const recorded: StructuredAgentSessionSendAnswer = {
|
|
kind: 'result',
|
|
result: {
|
|
ok: true,
|
|
replayed: false,
|
|
fence: 1,
|
|
cursor: { epoch: 'e', sequence: 1 },
|
|
value: { clientMessageId: 'm', queued: { messageId: 'm', position: 0, state: 'waiting' } }
|
|
}
|
|
}
|
|
|
|
function withRow(fields: Partial<AgentJournalSubmission>): StructuredAgentSessionSendAnswer {
|
|
return {
|
|
kind: 'result',
|
|
result: {
|
|
ok: true,
|
|
replayed: false,
|
|
fence: 1,
|
|
cursor: { epoch: 'e', sequence: 1 },
|
|
value: {
|
|
clientMessageId: 'm',
|
|
submission: {
|
|
clientMessageId: 'm',
|
|
fence: 1,
|
|
payloadFingerprint: 'fp',
|
|
dispatchState: 'accepted',
|
|
providerItemId: null,
|
|
reason: null,
|
|
submittedAt: 1,
|
|
resolvedAt: 1,
|
|
...fields
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function thrown(rpcCode: string | undefined): StructuredAgentSessionSendAnswer {
|
|
return { kind: 'thrown', error: new Error(rpcCode ?? 'socket closed'), rpcCode }
|
|
}
|
|
|
|
describe('structuredAgentSessionSendEvidence', () => {
|
|
it('reads any ok answer as the host holding the message', () => {
|
|
expect(structuredAgentSessionSendEvidence(recorded).kind).toBe('recorded')
|
|
})
|
|
|
|
it('reads a row the host returns, in any state, as its own from then on', () => {
|
|
for (const dispatchState of ['pending', 'accepted', 'rejected', 'unknown'] as const) {
|
|
expect(structuredAgentSessionSendEvidence(withRow({ dispatchState })).kind).toBe('recorded')
|
|
}
|
|
// A restart lost its outcome, or a queued card's hand-off: a record, never a probe.
|
|
const rows: Partial<AgentJournalSubmission>[] = [
|
|
{ dispatchState: 'unknown', recovered: true, reason: 'host_restarted' },
|
|
{ dispatchState: 'rejected', clientMessageId: 'other', queuedMessageId: 'm' },
|
|
{ dispatchState: 'rejected', keptAsQueuedMessageId: 'm' }
|
|
]
|
|
for (const row of rows) {
|
|
expect(structuredAgentSessionSendEvidence(withRow(row)).kind).toBe('recorded')
|
|
}
|
|
})
|
|
|
|
it('reads a made-up row for an id the host journal lost as unconfirmed, not recorded', () => {
|
|
expect(
|
|
structuredAgentSessionSendEvidence(
|
|
withRow({
|
|
dispatchState: 'unknown',
|
|
reason: 'durable_send_submission_missing',
|
|
recovered: true
|
|
})
|
|
).kind
|
|
).toBe('uncertain')
|
|
})
|
|
|
|
it('reads no thrown error as proof: a timeout, a closed socket or a thrown refusal', () => {
|
|
for (const rpcCode of [
|
|
'runtime_timeout',
|
|
'remote_runtime_unavailable',
|
|
'runtime_unavailable',
|
|
undefined
|
|
]) {
|
|
expect(structuredAgentSessionSendEvidence(thrown(rpcCode)).kind).toBe('uncertain')
|
|
}
|
|
for (const rpcCode of [
|
|
'runtime_manually_disconnected',
|
|
'runtime_rpc_queue_overloaded',
|
|
'remote_runtime_busy',
|
|
'forbidden',
|
|
'capability_unsupported'
|
|
]) {
|
|
expect(structuredAgentSessionSendEvidence(thrown(rpcCode)).kind).toBe('uncertain')
|
|
}
|
|
const thrownRefusal = {
|
|
response: { error: { data: { refusal: { code: 'agent_session_journal_unreadable' } } } }
|
|
}
|
|
expect(
|
|
structuredAgentSessionSendEvidence({
|
|
kind: 'thrown',
|
|
error: thrownRefusal,
|
|
rpcCode: 'invalid_argument'
|
|
}).kind
|
|
).toBe('uncertain')
|
|
})
|
|
|
|
// Pins the full turned-away list (round-2 S5): every other code, client-minted ones included, is uncertain.
|
|
it('treats a call the host turned away before running it as never written', () => {
|
|
for (const rpcCode of [
|
|
'method_not_found',
|
|
'method_not_supported',
|
|
'invalid_argument',
|
|
'unauthorized'
|
|
]) {
|
|
expect(structuredAgentSessionSendEvidence(thrown(rpcCode)).kind).toBe('not-recorded')
|
|
}
|
|
})
|
|
|
|
it('reads an unknown outcome or a lost result as unconfirmed, and an unconfirmed rewind as not sent', () => {
|
|
for (const reason of ['outcomeUnknown', 'resultLost']) {
|
|
expect(
|
|
structuredAgentSessionSendEvidence(refused('agent_session_operation_unknown', reason)).kind
|
|
).toBe('uncertain')
|
|
}
|
|
expect(
|
|
structuredAgentSessionSendEvidence(
|
|
refused('agent_session_operation_unknown', 'rewindUnconfirmed')
|
|
).kind
|
|
).toBe('not-recorded')
|
|
})
|
|
|
|
// The one request is the only one that carried the id, so a refusal of it proves nothing ran.
|
|
it('reads any other refusal as never written', () => {
|
|
for (const answer of [
|
|
refused('agent_session_ownership_unknown', 'sessionNotAttached'),
|
|
refused('agent_session_conflict'),
|
|
refused('agent_session_operation_capacity'),
|
|
refused('agent_session_journal_unreadable'),
|
|
refused('agent_session_operation_expired', 'operationExpired')
|
|
]) {
|
|
expect(structuredAgentSessionSendEvidence(answer).kind).toBe('not-recorded')
|
|
}
|
|
})
|
|
})
|