blob: d20b16e42262acf0cde730f6827abbfaa4c3c5d1 [file]
import assert from 'node:assert/strict';
import { test } from 'node:test';
import {
decodeClientFrame,
decodeHostFrame,
RuntimeHostProtocolError,
SESSION_EFFECT_OPERATION_SPECS,
SESSION_RECAP_RAW_MAX_BYTES,
} from '../protocol/index.js';
test('Session recap is one ready command with stable effect identity', () => {
assert.deepEqual(Object.keys(SESSION_EFFECT_OPERATION_SPECS), ['session.recap.generate']);
assert.deepEqual(
{
mode: SESSION_EFFECT_OPERATION_SPECS['session.recap.generate'].mode,
availability: SESSION_EFFECT_OPERATION_SPECS['session.recap.generate'].availability,
errors: SESSION_EFFECT_OPERATION_SPECS['session.recap.generate'].errors,
},
{
mode: 'command',
availability: 'ready',
errors: [
'host_not_ready',
'host_draining',
'operation_unavailable',
'not_found',
'session_archived',
'operation_conflict',
'persistence_failed',
'outcome_unknown',
'internal_failure',
],
},
);
const request = {
requestId: 'request-1',
operation: 'session.recap.generate',
input: { sessionId: 'session-1', effectId: 'effect-1', reason: 'manual' },
};
assert.deepEqual(decodeClientFrame(request), request);
});
test('Session recap exposes only bounded generated text or a closed failure class', () => {
for (const result of [
{
kind: 'generated',
effectId: 'effect-1',
reason: 'manual',
text: 'We wired Host-owned Session effects.',
raw: 'We wired Host-owned Session effects.',
},
{
kind: 'failed',
effectId: 'effect-2',
reason: 'idle',
errorClass: 'timeout',
},
]) {
const response = {
requestId: 'request-1',
operation: 'session.recap.generate',
ok: true,
result,
};
assert.deepEqual(decodeHostFrame(response), response);
}
assert.throws(
() =>
decodeHostFrame({
requestId: 'request-1',
operation: 'session.recap.generate',
ok: true,
result: {
kind: 'generated',
effectId: 'effect-1',
reason: 'manual',
text: 'bounded',
raw: 'x'.repeat(SESSION_RECAP_RAW_MAX_BYTES + 1),
},
}),
isProtocolError,
);
assert.throws(
() =>
decodeHostFrame({
requestId: 'request-1',
operation: 'session.recap.generate',
ok: true,
result: {
kind: 'failed',
effectId: 'effect-1',
reason: 'manual',
errorClass: 'raw_provider_error',
},
}),
isProtocolError,
);
});
test('Session recap rejects open or uncorrelated wire values', () => {
assert.throws(
() =>
decodeClientFrame({
requestId: 'request-1',
operation: 'session.recap.generate',
input: {
sessionId: 'session-1',
effectId: 'effect-1',
reason: 'manual',
model: 'client-selected-model',
},
}),
isProtocolError,
);
assert.throws(
() =>
SESSION_EFFECT_OPERATION_SPECS['session.recap.generate'].assertOutputForInput?.(
{ sessionId: 'session-1', effectId: 'effect-1', reason: 'manual' },
{
kind: 'failed',
effectId: 'effect-other',
reason: 'manual',
errorClass: 'provider',
},
),
isProtocolError,
);
});
function isProtocolError(error: unknown): boolean {
return error instanceof RuntimeHostProtocolError && error.code === 'invalid_frame';
}