blob: e5b1dac4fbf3746aa286d89d179d1405583b5bb7 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import assert from 'node:assert/strict';
import { MODEL_FAILURE_MESSAGE_MAX_BYTES } from '@maka/core/model-failure';
import { describe, test } from 'node:test';
import type { RuntimeInvocationRecord } from '@maka/core/runtime-invocation';
import type { CreateSessionInput, SessionListFilter } from '@maka/core/runtime-inputs';
import type { RuntimeEvent, RuntimeEventActions } from '@maka/core/runtime-event';
import { runtimeEventHasModelVisibleContent } from '@maka/core/runtime-event';
import type { SessionHeader, SessionSummary, StoredMessage, TurnRecord } from '@maka/core/session';
import { deriveTurnRecords, decodeCanonicalMessage } from '@maka/core/session';
import type { CanonicalPermissionOutcomeRecord } from '../interaction-authority.js';
import {
createRuntimeEventStoredMessageProjector,
isHardRuntimeEventReadModelDiagnostic,
isUnclaimedRuntimeEventDiagnostic,
projectTranscriptToolResult,
projectRuntimeEventsToStoredMessages,
projectRuntimeEventsToStoredMessagesWithArchiveStatuses,
} from '../runtime-event-read-model.js';
import { buildRuntimeEventModelReplayPlan } from '../model-history.js';
import { RuntimeReadModel } from '../runtime-read-model.js';
import { backfillRuntimeEventsFromStoredMessages } from '../runtime-event-backfill.js';
import { BackendRegistry, SessionManager, type SessionStore } from '../session-manager.js';
import { testInvocationOpening } from './invocation-fixture.js';
const ts = 1_800_000_000_000;
const sessionId = 'sess-1';
const runId = 'run-1';
const turnId = 'turn-1';
const invocationId = 'inv-1';
let eventSeq = 0;
/** The same invocation, ended a different way. */
function endedAs(
status: 'completed' | 'failed' | 'aborted',
failureClass?: string,
): RuntimeInvocationRecord {
return {
...invocation,
terminalEvent: {
...invocation.terminalEvent!,
status,
...(failureClass ? { actions: { endInvocation: true, stateDelta: { failureClass } } } : {}),
},
};
}
const invocation: RuntimeInvocationRecord = {
sessionId,
invocationId,
runId,
turnId,
openedAt: ts,
opening: testInvocationOpening({
route: {
provenance: 'runtime',
backendKind: 'ai-sdk',
llmConnectionId: 'anthropic-connection',
llmConnectionSlug: 'anthropic',
modelId: 'claude-sonnet-4-5',
},
configuration: { cwd: '/tmp/work' },
lineage: { parentTurnId: 'parent-turn' },
}),
terminalEvent: {
id: `${runId}-terminal`,
sessionId,
invocationId,
runId,
turnId,
ts: ts + 20,
partial: false,
role: 'system',
author: 'system',
status: 'completed',
},
};
function ev(overrides: Partial<RuntimeEvent>): RuntimeEvent {
eventSeq += 1;
return {
id: `event-${eventSeq}`,
invocationId,
runId,
sessionId,
turnId,
ts,
partial: false,
role: 'system',
author: 'system',
...overrides,
};
}
function baseEvents(): RuntimeEvent[] {
return [
ev({
id: 'evt-user',
ts: ts + 1,
role: 'user',
author: 'user',
content: { kind: 'text', text: 'read the file' },
refs: { storedMessageId: 'legacy-user' },
}),
ev({
id: 'evt-tool-call',
ts: ts + 2,
role: 'model',
author: 'agent',
content: {
kind: 'function_call',
id: 'tool-1',
name: 'Read',
args: { path: '/tmp/a.txt' },
},
actions: { stateDelta: { displayName: 'Read file', intent: 'inspect' } },
refs: { toolCallId: 'tool-1' },
}),
ev({
id: 'evt-permission-request',
ts: ts + 3,
role: 'system',
author: 'system',
actions: {
permissionRequest: {
kind: 'tool_permission',
requestId: 'req-1',
toolUseId: 'tool-1',
toolName: 'Read',
category: 'read',
reason: 'custom',
args: { path: '/tmp/a.txt' },
rememberForTurnAllowed: true,
hint: 'needs read access',
},
},
refs: { toolCallId: 'tool-1' },
}),
ev({
id: 'evt-permission-decision',
ts: ts + 4,
role: 'system',
author: 'user',
actions: {
permissionDecision: {
requestId: 'req-1',
decision: 'allow',
rememberForTurn: true,
},
},
refs: { toolCallId: 'tool-1' },
}),
ev({
id: 'evt-tool-result',
ts: ts + 5,
role: 'tool',
author: 'tool',
content: {
kind: 'function_response',
id: 'tool-1',
name: 'Read',
result: { kind: 'text', text: 'file contents' },
},
actions: { stateDelta: { durationMs: 42 } },
refs: { toolCallId: 'tool-1', storedMessageId: 'legacy-result' },
}),
ev({
id: 'evt-assistant',
ts: ts + 6,
role: 'model',
author: 'agent',
content: { kind: 'text', text: 'The file says: file contents' },
refs: { storedMessageId: 'legacy-assistant' },
}),
ev({
id: 'evt-token',
ts: ts + 7,
role: 'system',
author: 'system',
actions: {
tokenUsage: {
input: 100,
output: 25,
cacheRead: 10,
costUsd: 0.002,
systemPromptHash: 'sys-hash',
runtimeSteps: 3,
contextRemaining: 9000,
},
},
refs: { providerRequestTraceId: 'provider-trace-1' },
}),
ev({
id: 'evt-complete',
ts: ts + 8,
role: 'system',
author: 'system',
status: 'completed',
actions: { endInvocation: true },
}),
];
}
function equivalentLegacyMessages(): StoredMessage[] {
return [
{
type: 'user',
id: 'legacy-user',
turnId,
ts: ts + 1,
text: 'read the file',
},
{
type: 'tool_call',
id: 'tool-1',
turnId,
ts: ts + 2,
toolName: 'Read',
displayName: 'Read file',
intent: 'inspect',
args: { path: '/tmp/a.txt' },
},
{
type: 'permission_decision',
id: 'req-1',
turnId,
ts: ts + 4,
toolUseId: 'tool-1',
toolName: 'Read',
decision: 'allow',
rememberForTurn: true,
hint: 'needs read access',
},
{
type: 'tool_result',
id: 'legacy-result',
turnId,
ts: ts + 5,
toolUseId: 'tool-1',
isError: false,
content: { kind: 'text', text: 'file contents' },
durationMs: 42,
},
{
type: 'assistant',
id: 'legacy-assistant',
turnId,
ts: ts + 6,
text: 'The file says: file contents',
modelId: 'claude-sonnet-4-5',
},
{
type: 'token_usage',
id: 'evt-token',
turnId,
ts: ts + 7,
input: 100,
output: 25,
cacheRead: 10,
costUsd: 0.002,
systemPromptHash: 'sys-hash',
runtimeSteps: 3,
contextRemaining: 9000,
providerRequestTraceId: 'provider-trace-1',
},
{
type: 'turn_state',
id: 'evt-complete',
turnId,
ts: ts + 8,
status: 'completed',
parentTurnId: 'parent-turn',
},
];
}
describe('projectRuntimeEventsToStoredMessages', () => {
test('streaming projection is equivalent to the batch read model', () => {
const events = baseEvents();
const streamed = createRuntimeEventStoredMessageProjector({ invocations: [invocation] });
for (const event of events) streamed.push(event);
assert.deepStrictEqual(
streamed.finish(),
projectRuntimeEventsToStoredMessages(events, { invocations: [invocation] }),
);
});
// A transcript page may cut an invocation at any committed event. Rows that
// appear for a prefix must be exactly the rows the whole invocation later
// attributes to those same events, or a page would change once the Turn ends.
test('every event prefix projects the rows the full ledger attributes to it', () => {
const events = [
ev({
id: 'evt-prefix-user',
ts: ts + 1,
role: 'user',
author: 'user',
content: { kind: 'text', text: 'read the file' },
}),
ev({
id: 'evt-prefix-thinking',
ts: ts + 2,
role: 'model',
author: 'agent',
content: { kind: 'thinking', text: 'look first', signature: 'sig' },
refs: { providerEventId: 'step-1' },
}),
ev({
id: 'evt-prefix-text',
ts: ts + 3,
role: 'model',
author: 'agent',
content: { kind: 'text', text: 'Reading it.' },
refs: { providerEventId: 'step-1' },
}),
ev({
id: 'evt-prefix-call',
ts: ts + 4,
role: 'model',
author: 'agent',
content: { kind: 'function_call', id: 'tool-p', name: 'Read', args: { path: '/a' } },
refs: { toolCallId: 'tool-p', providerEventId: 'step-1' },
}),
ev({
id: 'evt-prefix-result',
ts: ts + 5,
role: 'tool',
author: 'tool',
content: {
kind: 'function_response',
id: 'tool-p',
name: 'Read',
result: { kind: 'text', text: 'contents' },
},
refs: { toolCallId: 'tool-p' },
}),
ev({
id: 'evt-prefix-usage',
ts: ts + 6,
actions: { tokenUsage: { input: 10, output: 5 } },
}),
ev({
id: 'evt-prefix-ended',
ts: ts + 7,
status: 'completed',
actions: { endInvocation: true },
}),
];
const project = (prefix: readonly RuntimeEvent[]) =>
projectRuntimeEventsToStoredMessages(prefix, { invocations: [invocation] });
const full = project(events);
assert.deepStrictEqual(full.diagnostics, []);
assert.deepStrictEqual(
full.messages.map((message) => message.type),
['user', 'assistant', 'tool_call', 'tool_result', 'token_usage', 'turn_state'],
);
for (let k = 1; k <= events.length; k += 1) {
const seen = new Set(events.slice(0, k).map((event) => event.id));
const expected = full.messages.filter((_, index) => seen.has(full.sourceEventIds[index]!));
const prefix = project(events.slice(0, k));
assert.deepStrictEqual(prefix.messages, expected, `prefix of ${k} events`);
assert.deepStrictEqual(prefix.diagnostics, [], `prefix of ${k} events`);
}
assert.deepStrictEqual(
project(events.slice(0, 2)).messages.map((message) => message.type),
['user'],
);
});
test('unclaimed thinking is a defect only once its invocation has ended', () => {
const thinking = ev({
id: 'evt-orphan-thinking',
role: 'model',
author: 'agent',
content: { kind: 'thinking', text: 'no answer followed' },
refs: { providerEventId: 'step-orphan' },
});
const ended = ev({
id: 'evt-orphan-ended',
status: 'completed',
actions: { endInvocation: true },
});
assert.deepStrictEqual(
projectRuntimeEventsToStoredMessages([thinking], { invocations: [invocation] }).diagnostics,
[],
);
assert.deepStrictEqual(
projectRuntimeEventsToStoredMessages([thinking, ended], {
invocations: [invocation],
}).diagnostics.map((diagnostic) => [diagnostic.code, diagnostic.eventId]),
[['unsupported_event', 'evt-orphan-thinking']],
);
});
test('loads canonical permission outcomes after the streaming scan and preserves acceptance order', () => {
const request = ev({
id: 'evt-canonical-request',
ts: ts + 1,
actions: {
permissionRequest: {
kind: 'tool_permission',
requestId: 'req-canonical',
toolUseId: 'tool-canonical',
toolName: 'Read',
category: 'read',
reason: 'custom',
args: { path: '/tmp/a.txt' },
rememberForTurnAllowed: true,
hint: 'read it',
},
},
refs: { toolCallId: 'tool-canonical' },
});
const accepted = ev({
id: 'evt-canonical-accepted',
ts: ts + 2,
status: 'completed',
role: 'tool',
author: 'user',
content: {
kind: 'function_response',
id: 'tool-canonical',
name: 'Read',
result: { kind: 'text', text: 'result before permission' },
},
actions: {
permissionAnswerAccepted: { requestId: 'req-canonical' },
tokenUsage: { input: 10, output: 5 },
endInvocation: true,
},
refs: { toolCallId: 'tool-canonical' },
});
const later = ev({
id: 'evt-after-permission',
ts: ts + 3,
role: 'user',
author: 'user',
content: { kind: 'text', text: 'after' },
});
const options: Parameters<typeof createRuntimeEventStoredMessageProjector>[0] = {
invocations: [invocation],
};
const projector = createRuntimeEventStoredMessageProjector(options);
projector.push(request);
projector.push(accepted);
// A later malformed duplicate must not rewrite the request identity that
// was paired when the acceptance entered the ledger.
projector.push(
ev({
id: 'evt-duplicate-request',
actions: {
permissionRequest: {
kind: 'tool_permission',
requestId: 'req-canonical',
toolUseId: 'different-tool',
toolName: 'Write',
category: 'file_write',
reason: 'custom',
args: { path: '/tmp/other.txt' },
rememberForTurnAllowed: true,
},
},
}),
);
projector.push(later);
assert.deepStrictEqual(projector.permissionRequestIds, ['req-canonical']);
const canonical: CanonicalPermissionOutcomeRecord = {
sessionId,
runId,
turnId,
requestId: 'req-canonical',
request: {
kind: 'permission',
toolUseId: 'tool-canonical',
prompt: {
kind: 'tool_permission',
toolName: 'Read',
category: 'read',
reason: 'custom',
review: { kind: 'path', operation: 'read', path: '/tmp/a.txt' },
rememberForTurnAllowed: true,
},
},
outcome: {
kind: 'permission_answer',
decision: 'allow',
rememberForTurn: false,
reviewer: 'user',
committedAt: ts + 2,
},
};
options.canonicalPermissionOutcomes = new Map([['req-canonical', canonical]]);
const out = projector.finish();
assert.deepStrictEqual(
out.messages.map((message) => message.type),
['tool_result', 'permission_decision', 'token_usage', 'turn_state', 'user'],
);
assert.deepStrictEqual(out.sourceEventIds, [
accepted.id,
accepted.id,
accepted.id,
accepted.id,
later.id,
]);
assert.deepStrictEqual(out.diagnostics, []);
});
test('projects decoded tool results during push and reports emitted messages', () => {
const emitted: Array<{ type: StoredMessage['type']; sourceEventId: string }> = [];
const projector = createRuntimeEventStoredMessageProjector({
invocations: [invocation],
projectToolResult: (_event, decoded) =>
decoded.kind === 'text' ? { kind: 'text', text: decoded.text.slice(0, 4) } : decoded,
onMessage: (message, sourceEventId) => emitted.push({ type: message.type, sourceEventId }),
});
const event = ev({
id: 'evt-large-result',
role: 'tool',
author: 'tool',
content: {
kind: 'function_response',
id: 'tool-large',
name: 'Bash',
result: { kind: 'text', text: 'large result' },
},
refs: { toolCallId: 'tool-large' },
});
projector.push(event);
assert.deepStrictEqual(emitted, [{ type: 'tool_result', sourceEventId: event.id }]);
assert.deepStrictEqual(projector.finish().messages[0], {
type: 'tool_result',
id: event.id,
turnId,
ts,
toolUseId: 'tool-large',
isError: false,
content: { kind: 'text', text: 'larg' },
});
});
test('bounds local terminal transcript output to a recoverable tail preview', async () => {
const event = ev({
id: 'evt-terminal/result',
role: 'tool',
author: 'tool',
content: {
kind: 'function_response',
id: 'tool-terminal',
name: 'Bash',
result: { kind: 'text', text: 'provider-facing result' },
modelProjection: { version: 1, kind: 'json', value: { kind: 'terminal' } },
},
refs: { toolCallId: 'tool-terminal' },
});
const terminal = {
kind: 'terminal' as const,
cwd: '/workspace',
cmd: 'large-command',
status: 'failed' as const,
exitCode: 7,
failureMessage: 'failed',
output: {
mode: 'pipes' as const,
stdout: Array.from({ length: 30 }, (_, index) => `stdout-${index + 1}`).join('\n'),
stderr: 'x'.repeat(2048),
stdoutTruncated: false,
stderrTruncated: false,
redacted: false,
},
};
const projected = projectTranscriptToolResult(event, terminal);
assert.equal(projected.kind, 'terminal');
if (projected.kind !== 'terminal' || projected.output.mode !== 'pipes') return;
assert.equal(projected.cwd, terminal.cwd);
assert.equal(projected.cmd, terminal.cmd);
assert.equal(projected.status, terminal.status);
assert.equal(projected.exitCode, terminal.exitCode);
assert.equal(projected.failureMessage, terminal.failureMessage);
assert.equal(projected.output.stdoutTruncated, true);
assert.equal(projected.output.stderrTruncated, true);
assert.match(projected.output.stdout, /stdout-30$/);
assert.doesNotMatch(projected.output.stdout, /stdout-1\n/);
assert.match(projected.output.stderr, /maka:\/\/runtime\/tool-results\/evt-terminal%2Fresult/);
const sourceEvent = {
...event,
content: { ...event.content!, result: terminal },
} as RuntimeEvent;
const view = await new RuntimeReadModel({
runtimeEventStore: {
listSessionInvocations: async () => [{ ...invocation, terminalEvent: undefined }],
readSessionRuntimeEventEntries: async () => [],
readRuntimeEvents: async () => [sourceEvent],
} as never,
}).getSessionView(sessionId);
const result = view.messages.find((message) => message.type === 'tool_result');
assert.deepEqual(result?.content, projected);
assert.deepEqual(view.events[0]?.content, sourceEvent.content);
assert.strictEqual(
projectTranscriptToolResult(
{
...event,
content: { ...event.content!, providerExecuted: true } as RuntimeEvent['content'],
},
terminal,
),
terminal,
);
});
test('exposes a session image ref as a Markdown image source to the model', () => {
const replay = buildRuntimeEventModelReplayPlan([
ev({
role: 'user',
author: 'user',
content: {
kind: 'text',
text: 'show this',
attachments: [
{
kind: 'image',
name: 'preview.png',
mimeType: 'image/png',
bytes: 3,
ref: {
kind: 'session_file',
sessionId,
relativePath: 'attachment-123',
},
},
],
},
}),
]);
const item = replay.items[0];
assert.equal(item?.kind, 'text');
assert.match(
item?.kind === 'text' ? item.content : '',
/Markdown image source: "maka:\/\/runtime\/attachments\/attachment-123"/,
);
});
test('projects user displayText from RuntimeEvent text content', () => {
const typed = '/skill:alpha 帮我整理';
const envelope = 'The user explicitly invoked…\n\n<user-message>\n帮我整理\n</user-message>';
const out = projectRuntimeEventsToStoredMessages(
[
ev({
id: 'evt-user-skill',
ts: ts + 1,
role: 'user',
author: 'user',
content: { kind: 'text', text: envelope, displayText: typed },
refs: { storedMessageId: 'user-skill' },
}),
],
{ invocations: [invocation] },
);
assert.deepStrictEqual(out.messages, [
{
type: 'user',
id: 'user-skill',
turnId,
ts: ts + 1,
text: envelope,
displayText: typed,
},
]);
});
test('full RuntimeEvent turn projects legacy-compatible rows', () => {
const out = projectRuntimeEventsToStoredMessages(baseEvents(), { invocations: [invocation] });
assert.deepStrictEqual(
out.messages.map((message) => message.type),
[
'user',
'tool_call',
'permission_decision',
'tool_result',
'assistant',
'token_usage',
'turn_state',
],
);
assert.partialDeepStrictEqual(out.messages[1], {
type: 'tool_call',
id: 'tool-1',
toolName: 'Read',
displayName: 'Read file',
intent: 'inspect',
});
assert.partialDeepStrictEqual(out.messages[2], {
type: 'permission_decision',
id: 'req-1',
toolUseId: 'tool-1',
toolName: 'Read',
decision: 'allow',
hint: 'needs read access',
});
assert.partialDeepStrictEqual(out.messages[3], {
type: 'tool_result',
id: 'legacy-result',
toolUseId: 'tool-1',
durationMs: 42,
});
assert.partialDeepStrictEqual(out.messages[4], {
type: 'assistant',
modelId: 'claude-sonnet-4-5',
text: 'The file says: file contents',
});
assert.partialDeepStrictEqual(out.messages[6], {
type: 'turn_state',
status: 'completed',
parentTurnId: 'parent-turn',
});
assert.deepStrictEqual(out.diagnostics, []);
});
test('projects provider-native search through the canonical read model while replay keeps raw output', () => {
const rawProviderOutput = [
{
type: 'web_search_result',
url: 'https://maka.example/',
title: 'Maka',
pageAge: null,
encryptedContent: 'encrypted-result',
},
];
const events = [
ev({
id: 'evt-native-text',
ts: ts + 1,
role: 'model',
author: 'agent',
content: {
kind: 'text',
text: 'Maka is current.',
providerOptions: {
openai: {
itemId: 'message-1',
annotations: [{ type: 'url_citation', url: 'https://maka.example/' }],
},
},
},
refs: { providerEventId: 'step-native' },
}),
ev({
id: 'evt-native-call',
ts: ts + 2,
role: 'model',
author: 'agent',
content: {
kind: 'function_call',
id: 'search-1',
name: 'WebSearch',
args: { query: 'latest Maka' },
providerOptions: { anthropic: { type: 'server_tool_use' } },
providerExecuted: true,
},
refs: { toolCallId: 'search-1', stepId: 'step-native' },
}),
ev({
id: 'evt-native-result',
ts: ts + 3,
role: 'tool',
author: 'tool',
content: {
kind: 'function_response',
id: 'search-1',
name: 'WebSearch',
result: {
kind: 'web_search',
provider: 'model',
query: 'latest Maka',
rows: [
{
title: 'Maka',
url: 'https://maka.example/',
snippet: '',
source: 'maka.example',
},
],
},
providerExecuted: true,
providerOutput: rawProviderOutput,
},
refs: { toolCallId: 'search-1' },
}),
];
const projected = projectRuntimeEventsToStoredMessages(events, { invocations: [invocation] });
assert.deepStrictEqual(projected.diagnostics, []);
assert.partialDeepStrictEqual(projected.messages[0], { type: 'assistant' });
assert.deepStrictEqual(
(projected.messages[0] as { providerOptions?: unknown }).providerOptions,
{
openai: {
itemId: 'message-1',
annotations: [{ type: 'url_citation', url: 'https://maka.example/' }],
},
},
);
assert.partialDeepStrictEqual(projected.messages[1], {
type: 'tool_call',
providerExecuted: true,
});
assert.deepStrictEqual(
(projected.messages[1] as { providerOptions?: unknown }).providerOptions,
{
anthropic: { type: 'server_tool_use' },
},
);
assert.partialDeepStrictEqual(projected.messages[2], {
type: 'tool_result',
providerExecuted: true,
providerOutput: rawProviderOutput,
});
assert.deepStrictEqual((projected.messages[2] as { content?: unknown }).content, {
kind: 'web_search',
provider: 'model',
query: 'latest Maka',
rows: [
{
title: 'Maka',
url: 'https://maka.example/',
snippet: '',
source: 'maka.example',
},
],
});
const replay = buildRuntimeEventModelReplayPlan(events);
assert.deepStrictEqual(replay.diagnostics, []);
assert.partialDeepStrictEqual(
replay.items.find((item) => item.kind === 'tool_result' && item.toolCallId === 'search-1'),
{
output: rawProviderOutput,
providerExecuted: true,
},
);
});
test('projects an AskUserQuestion round trip without a legacy row for the live request', () => {
const out = projectRuntimeEventsToStoredMessages(
[
ev({
id: 'evt-question-call',
ts: ts + 1,
role: 'model',
author: 'agent',
content: {
kind: 'function_call',
id: 'question-tool-1',
name: 'AskUserQuestion',
args: { questions: [{ question: 'Choose', options: [{ label: 'Extend' }] }] },
},
refs: { toolCallId: 'question-tool-1' },
}),
ev({
id: 'evt-question-request',
ts: ts + 2,
actions: {
userQuestionRequest: {
requestId: 'question-1',
toolUseId: 'question-tool-1',
questions: [{ question: 'Choose', options: [{ label: 'Extend' }] }],
},
},
refs: { toolCallId: 'question-tool-1' },
}),
ev({
id: 'evt-question-result',
ts: ts + 3,
role: 'tool',
author: 'tool',
content: {
kind: 'function_response',
id: 'question-tool-1',
name: 'AskUserQuestion',
result: { kind: 'json', value: { answers: ['Extend'] } },
},
refs: { toolCallId: 'question-tool-1' },
}),
ev({
id: 'evt-question-complete',
ts: ts + 4,
status: 'completed',
actions: { endInvocation: true },
}),
],
{ invocations: [invocation] },
);
assert.deepStrictEqual(
out.messages.map((message) => message.type),
['tool_call', 'tool_result', 'turn_state'],
);
assert.deepStrictEqual(out.diagnostics, []);
});
test('replays generic provider tool results without Maka result decoding', () => {
const events = [
ev({
id: 'evt-generic-primitive-call',
role: 'model',
author: 'agent',
content: {
kind: 'function_call',
id: 'generic-primitive-1',
name: 'ProviderPrimitive',
args: {},
},
}),
ev({
id: 'evt-generic-primitive-result',
role: 'tool',
author: 'tool',
content: {
kind: 'function_response',
id: 'generic-primitive-1',
name: 'ProviderPrimitive',
result: 42 as never,
},
}),
ev({
id: 'evt-generic-json-call',
role: 'model',
author: 'agent',
content: {
kind: 'function_call',
id: 'generic-json-1',
name: 'ProviderJson',
args: {},
},
}),
ev({
id: 'evt-generic-json-result',
role: 'tool',
author: 'tool',
content: {
kind: 'function_response',
id: 'generic-json-1',
name: 'ProviderJson',
result: { providerPayload: true, values: [1, 2, 3] } as never,
},
}),
ev({
id: 'evt-generic-subagent-collision-call',
role: 'model',
author: 'agent',
content: {
kind: 'function_call',
id: 'generic-subagent-collision-1',
name: 'ProviderJson',
args: {},
},
}),
ev({
id: 'evt-generic-subagent-collision-result',
role: 'tool',
author: 'tool',
content: {
kind: 'function_response',
id: 'generic-subagent-collision-1',
name: 'ProviderJson',
result: {
kind: 'subagent',
status: 'waiting_permission',
providerPayload: true,
} as never,
},
}),
];
const replay = buildRuntimeEventModelReplayPlan(events);
assert.deepStrictEqual(
replay.items.map((item) => item.kind),
['tool_call', 'tool_result', 'tool_call', 'tool_result', 'tool_call', 'tool_result'],
);
assert.deepStrictEqual(
replay.items.filter((item) => item.kind === 'tool_result').map((item) => item.output),
[
42,
{ providerPayload: true, values: [1, 2, 3] },
{
kind: 'subagent',
status: 'waiting_permission',
providerPayload: true,
},
],
);
assert.deepStrictEqual(replay.diagnostics, []);
});
test('folds retired permission modes while projecting persisted tool results', () => {
const out = projectRuntimeEventsToStoredMessages(
[
ev({
id: 'evt-persisted-subagent-result',
role: 'tool',
author: 'tool',
content: {
kind: 'function_response',
id: 'tool-subagent',
name: 'subagent',
result: {
kind: 'subagent',
agentName: 'Researcher',
turnId: 'child-turn',
runId: 'child-run',
status: 'completed',
permissionMode: 'execute',
summary: 'done',
artifactIds: [],
} as never,
},
refs: { toolCallId: 'tool-subagent' },
}),
],
{ invocations: [invocation] },
);
const projected = out.messages.find((message) => message.type === 'tool_result');
assert.deepStrictEqual(
projected?.type === 'tool_result' && projected.content.kind === 'subagent'
? projected.content.permissionMode
: undefined,
'ask',
);
assert.deepStrictEqual(out.diagnostics, []);
});
test('folds retired ExploreAgent results before model replay', () => {
const result = {
kind: 'explore_agent',
ok: false,
terminalStatus: 'failed',
mode: 'read_only',
objective: 'Trace the session lifecycle.',
roots: ['packages/runtime'],
queries: ['SessionManager'],
filesInspected: 0,
filesSkipped: 0,
bytesRead: 0,
candidateFiles: [],
matches: [],
notes: [],
summary: '未完成:目标无效。',
report: '',
reason: 'invalid_objective',
message: '目标无效。',
} as const;
const events = [
ev({
id: 'evt-explore-call',
role: 'model',
author: 'agent',
content: {
kind: 'function_call',
id: 'explore-1',
name: 'ExploreAgent',
args: { objective: result.objective },
},
}),
ev({
id: 'evt-explore-result',
role: 'tool',
author: 'tool',
content: {
kind: 'function_response',
id: 'explore-1',
name: 'ExploreAgent',
result: result as never,
},
}),
];
const replay = buildRuntimeEventModelReplayPlan(events);
assert.deepStrictEqual(
(replay.items.find((item) => item.kind === 'tool_result') as { output?: unknown } | undefined)
?.output,
{ kind: 'text', text: '未完成:目标无效。' },
);
assert.deepStrictEqual(replay.diagnostics, []);
});
test('restores a settled Agent Swarm function response', () => {
const result = {
kind: 'agent_swarm' as const,
status: 'completed' as const,
items: [
{
itemId: 'contract',
index: 0,
profile: 'local_read',
started: true,
agentId: 'local-read',
agentName: 'Local Read',
turnId: 'child-turn',
runId: 'child-run',
status: 'completed' as const,
summary: 'Verified the contract.',
artifactIds: [],
startedAt: ts + 1,
completedAt: ts + 2,
durationMs: 1,
},
],
startedAt: ts,
completedAt: ts + 2,
durationMs: 2,
};
const out = projectRuntimeEventsToStoredMessages(
[
ev({
id: 'evt-agent-swarm-result',
role: 'tool',
author: 'tool',
content: {
kind: 'function_response',
id: 'tool-agent-swarm',
name: 'agent_swarm',
result,
},
refs: { toolCallId: 'tool-agent-swarm' },
}),
],
{ invocations: [invocation] },
);
const projected = out.messages.find((message) => message.type === 'tool_result');
assert.deepStrictEqual(
projected?.type === 'tool_result' ? projected.content : undefined,
result,
);
assert.deepStrictEqual(out.diagnostics, []);
});
test('projects first-observed step content order for stable live handoff', () => {
const out = projectRuntimeEventsToStoredMessages(
[
ev({
ts: ts + 1,
role: 'model',
author: 'agent',
content: { kind: 'function_call', id: 'tool-1', name: 'Read', args: {} },
refs: { toolCallId: 'tool-1', stepId: 'message-1' },
}),
ev({
ts: ts + 2,
role: 'model',
author: 'agent',
content: { kind: 'thinking', text: 'late reasoning' },
refs: { providerEventId: 'message-1' },
}),
ev({
ts: ts + 3,
role: 'model',
author: 'agent',
content: { kind: 'text', text: 'answer' },
refs: { providerEventId: 'message-1' },
}),
],
{ invocations: [invocation] },
);
const assistant = out.messages.find((message) => message.type === 'assistant');
assert.deepStrictEqual(
(assistant as unknown as { contentOrder?: string[] } | undefined)?.contentOrder,
['tools', 'thinking', 'text'],
);
});
test('archived tool-result placeholders project to diagnostic tool-result rows', () => {
const events = baseEvents();
const toolResult = events.find((event) => event.id === 'evt-tool-result');
if (toolResult?.content?.kind !== 'function_response')
throw new Error('fixture missing tool result');
toolResult.content.result = {
kind: 'maka.archived_tool_result',
rewriteVersion: 1,
artifactId: 'artifact-tool-result',
runtimeEventId: 'evt-tool-result',
toolCallId: 'tool-1',
toolName: 'Read',
bodySha256: 'a'.repeat(64),
originalEstimatedTokens: 200,
originalBytes: 800,
reason: 'stale_tool_result_pruned_before_compact',
};
const out = projectRuntimeEventsToStoredMessages(events, { invocations: [invocation] });
const projected = out.messages.find((message) => message.type === 'tool_result');
assert.partialDeepStrictEqual(projected, { type: 'tool_result', toolUseId: 'tool-1' });
assert.deepStrictEqual((projected as { content?: unknown } | undefined)?.content, {
kind: 'archived_tool_result',
status: 'not_loaded',
artifactId: 'artifact-tool-result',
bodySha256: 'a'.repeat(64),
runtimeEventId: 'evt-tool-result',
toolCallId: 'tool-1',
toolName: 'Read',
originalEstimatedTokens: 200,
originalBytes: 800,
rewriteVersion: 1,
reason: 'stale_tool_result_pruned_before_compact',
});
assert.deepStrictEqual(
out.diagnostics.map((diag) => diag.code),
['archived_tool_result_placeholder'],
);
});
test('legacy archived tool-result placeholders gain a deterministic ArchiveRead ref for replay', () => {
const events = baseEvents();
const toolResult = events.find((event) => event.id === 'evt-tool-result');
if (toolResult?.content?.kind !== 'function_response')
throw new Error('fixture missing tool result');
toolResult.content.result = {
kind: 'maka.archived_tool_result',
rewriteVersion: 1,
artifactId: 'artifact-tool-result',
runtimeEventId: 'evt-tool-result',
toolCallId: 'tool-1',
toolName: 'Read',
bodySha256: 'a'.repeat(64),
originalEstimatedTokens: 200,
originalBytes: 800,
reason: 'stale_tool_result_pruned_before_compact',
};
const replay = buildRuntimeEventModelReplayPlan(events);
const result = replay.items.find(
(item) => item.kind === 'tool_result' && item.toolCallId === 'tool-1',
);
assert.equal(
result?.kind === 'tool_result' && typeof result.output === 'object' && result.output !== null
? (result.output as { resourceRef?: string }).resourceRef
: undefined,
`maka://archive/artifact-tool-result/${'a'.repeat(64)}/800`,
);
});
test('archive status wrapper can project missing and corrupt rows without changing sync defaults', () => {
const events = baseEvents();
const toolResult = events.find((event) => event.id === 'evt-tool-result');
if (toolResult?.content?.kind !== 'function_response')
throw new Error('fixture missing tool result');
toolResult.content.result = {
kind: 'maka.archived_tool_result',
rewriteVersion: 1,
artifactId: 'artifact-tool-result',
runtimeEventId: 'evt-tool-result',
toolCallId: 'tool-1',
toolName: 'Read',
bodySha256: 'a'.repeat(64),
originalEstimatedTokens: 200,
originalBytes: 800,
reason: 'stale_tool_result_pruned_before_compact',
};
const defaultOut = projectRuntimeEventsToStoredMessages(events, { invocations: [invocation] });
const defaultProjected = defaultOut.messages.find((message) => message.type === 'tool_result');
assert.partialDeepStrictEqual(defaultProjected, { type: 'tool_result' });
assert.strictEqual(archivedStatus(defaultProjected), 'not_loaded');
const missingOut = projectRuntimeEventsToStoredMessagesWithArchiveStatuses(events, {
invocations: [invocation],
archiveStatuses: { 'evt-tool-result': 'missing' },
});
const missingProjected = missingOut.messages.find((message) => message.type === 'tool_result');
assert.partialDeepStrictEqual(missingProjected, { type: 'tool_result' });
assert.strictEqual(archivedStatus(missingProjected), 'missing');
const corruptOut = projectRuntimeEventsToStoredMessagesWithArchiveStatuses(events, {
invocations: [invocation],
archiveStatuses: [{ runtimeEventId: 'evt-tool-result', status: 'corrupt' }],
});
const corruptProjected = corruptOut.messages.find((message) => message.type === 'tool_result');
assert.partialDeepStrictEqual(corruptProjected, { type: 'tool_result' });
assert.strictEqual(archivedStatus(corruptProjected), 'corrupt');
});
test('partial RuntimeEvents are excluded', () => {
const out = projectRuntimeEventsToStoredMessages(
[
ev({
id: 'evt-partial',
partial: true,
role: 'model',
author: 'agent',
content: { kind: 'text', text: 'streaming' },
}),
ev({
id: 'evt-final',
role: 'model',
author: 'agent',
content: { kind: 'text', text: 'final' },
}),
],
{ invocations: [invocation] },
);
assert.strictEqual(out.messages.length, 1);
assert.partialDeepStrictEqual(out.messages[0], { type: 'assistant', text: 'final' });
assert.deepStrictEqual(
out.diagnostics.map((diag) => diag.code),
['partial_skipped'],
);
});
test('tool dispatch recovery facts are accepted without creating legacy message rows', () => {
const out = projectRuntimeEventsToStoredMessages(
[
ev({
id: 'toolop-1-dispatch',
role: 'system',
author: 'system',
actions: {
toolDispatch: {
protocol: 't1_after_preflight_v1',
operationId: 'toolop-1',
providerToolCallId: 'tool-1',
toolName: 'Bash',
canonicalArgsHash: 'sha256:args',
recoveryMode: 'reconcile',
},
},
refs: { toolCallId: 'tool-1', operationId: 'toolop-1' },
}),
],
{ invocations: [invocation] },
);
assert.deepStrictEqual(out.messages, []);
assert.deepStrictEqual(out.diagnostics, []);
});
test('question answer acknowledgements remain non-visible audit facts', () => {
const out = projectRuntimeEventsToStoredMessages(
[
ev({
id: 'question-1-answered',
role: 'system',
author: 'user',
actions: { userQuestionAnswerAccepted: { requestId: 'question-1' } },
refs: { toolCallId: 'tool-1' },
}),
],
{ invocations: [invocation] },
);
assert.deepStrictEqual(out.messages, []);
assert.deepStrictEqual(out.diagnostics, []);
});
test('terminal recovery bundle facts are accepted without creating legacy message rows', () => {
const out = projectRuntimeEventsToStoredMessages(
[
ev({
id: 'toolop-1-reconcile',
role: 'system',
author: 'system',
actions: {
toolRecovery: {
kind: 'maka.tool.reconcile_result',
version: 1,
payload: {
protocol: 'tool_reconcile_v1',
operationId: 'toolop-1',
observation: 'unreadable',
observationSchema: 'state_identity_v1',
observationDigest:
'sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
},
},
},
refs: { toolCallId: 'tool-1', operationId: 'toolop-1' },
}),
ev({
id: 'toolop-1-decision',
role: 'system',
author: 'system',
actions: {
toolRecovery: {
kind: 'maka.tool.recovery_decision',
version: 1,
payload: {
protocol: 'tool_recovery_v1',
operationId: 'toolop-1',
disposition: 'parked',
reasonCode: 'reconcile_unreadable',
evidenceEventIds: ['call-1', 'dispatch-1', 'toolop-1-reconcile'],
},
},
},
refs: { toolCallId: 'tool-1', operationId: 'toolop-1' },
}),
],
{ invocations: [invocation] },
);
assert.deepStrictEqual(out.messages, []);
assert.deepStrictEqual(out.diagnostics, []);
});
test('continuation-start recovery facts are accepted without creating legacy message rows', () => {
const out = projectRuntimeEventsToStoredMessages(
[
ev({
id: 'continuation-start',
role: 'system',
author: 'system',
actions: {
continuationStart: {
protocol: 'continuation_start_v2',
provenance: 'runtime_admission',
claimId: 'claim-1',
boundaryDigest:
'sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
immediateSource: {
sessionId: 'session-1',
invocationId: 'source-invocation',
runId: 'source-run',
turnId: 'source-turn',
highWater: 2,
prefixDigest:
'sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb',
},
replayManifestDigest:
'sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
providerProjectionVersion: 1,
providerReplayDigest:
'sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc',
},
},
}),
],
{ invocations: [invocation] },
);
assert.deepStrictEqual(out.messages, []);
assert.deepStrictEqual(out.diagnostics, []);
});
test('sandbox boundary request and decision facts are accepted without creating legacy message rows', () => {
const out = projectRuntimeEventsToStoredMessages(
[
ev({
id: 'sandbox-boundary-request',
ts: ts + 1,
role: 'system',
author: 'system',
actions: {
stateDelta: {
sandboxBoundaryRequest: {
requestId: 'boundary-1',
toolUseId: 'tool-1',
justification: 'read a file outside the workspace',
expansion: {
filesystem: {
entries: [{ path: '/tmp/outside.txt', access: 'read', scope: 'exact' }],
},
},
},
},
},
refs: { toolCallId: 'tool-1' },
}),
ev({
id: 'sandbox-boundary-decision',
ts: ts + 2,
role: 'system',
author: 'user',
actions: {
stateDelta: {
sandboxBoundaryDecision: {
requestId: 'boundary-1',
decision: 'allow',
status: 'approved',
revision: 2,
},
},
},
refs: { toolCallId: 'tool-1' },
}),
],
{ invocations: [invocation] },
);
assert.deepStrictEqual(out.messages, []);
assert.deepStrictEqual(out.diagnostics, []);
});
// Claiming the key alone would let any shape ride in under a control-fact
// name. Only what the Runtime mapper actually emits is a canonical fact: every field,
// the system/user identity, and the tool-call reference.
const wellFormedBoundaryRequest = () =>
ev({
id: 'sandbox-boundary-request-case',
ts: ts + 1,
role: 'system',
author: 'system',
actions: {
stateDelta: {
sandboxBoundaryRequest: {
requestId: 'boundary-1',
toolUseId: 'tool-1',
justification: 'read a file outside the workspace',
expansion: {
filesystem: {
entries: [{ path: '/tmp/outside.txt', access: 'read', scope: 'exact' }],
},
},
},
},
},
refs: { toolCallId: 'tool-1' },
});
const wellFormedBoundaryDecision = () =>
ev({
id: 'sandbox-boundary-decision-case',
ts: ts + 2,
role: 'system',
author: 'user',
actions: {
stateDelta: {
sandboxBoundaryDecision: {
requestId: 'boundary-1',
decision: 'allow',
status: 'approved',
revision: 2,
},
},
},
refs: { toolCallId: 'tool-1' },
});
function corruptedBoundaryEvent(
base: RuntimeEvent,
key: 'sandboxBoundaryRequest' | 'sandboxBoundaryDecision',
mutate: (payload: Record<string, unknown>, event: RuntimeEvent) => RuntimeEvent | void,
): RuntimeEvent {
const clone = structuredClone(base) as RuntimeEvent;
const payload = clone.actions?.stateDelta?.[key] as Record<string, unknown>;
return mutate(payload, clone) ?? clone;
}
const malformedBoundaryCases: Array<[string, () => RuntimeEvent]> = [
[
'request without a justification',
() =>
corruptedBoundaryEvent(wellFormedBoundaryRequest(), 'sandboxBoundaryRequest', (payload) => {
delete payload.justification;
}),
],
[
'request with an unusable expansion',
() =>
corruptedBoundaryEvent(wellFormedBoundaryRequest(), 'sandboxBoundaryRequest', (payload) => {
payload.expansion = {};
}),
],
[
'request that lost its tool-call reference',
() =>
corruptedBoundaryEvent(
wellFormedBoundaryRequest(),
'sandboxBoundaryRequest',
(_payload, event) => ({ ...event, refs: undefined }),
),
],
[
'request attributed to someone other than the system',
() =>
corruptedBoundaryEvent(
wellFormedBoundaryRequest(),
'sandboxBoundaryRequest',
(_payload, event) => ({ ...event, role: 'user', author: 'tool' }),
),
],
[
'decision without a status',
() =>
corruptedBoundaryEvent(
wellFormedBoundaryDecision(),
'sandboxBoundaryDecision',
(payload) => {
delete payload.status;
},
),
],
[
'decision with a status the boundary never settles to',
() =>
corruptedBoundaryEvent(
wellFormedBoundaryDecision(),
'sandboxBoundaryDecision',
(payload) => {
payload.status = 'pending';
},
),
],
[
'decision without a revision',
() =>
corruptedBoundaryEvent(
wellFormedBoundaryDecision(),
'sandboxBoundaryDecision',
(payload) => {
delete payload.revision;
},
),
],
[
'decision attributed to the agent instead of the user',
() =>
corruptedBoundaryEvent(
wellFormedBoundaryDecision(),
'sandboxBoundaryDecision',
(_payload, event) => ({ ...event, author: 'agent' }),
),
],
];
for (const [name, makeEvent] of malformedBoundaryCases) {
// The claim stays exact: a malformed shape is never admitted as a canonical
// boundary fact. Its severity is a separate question, and a control fact
// owns no chat row, so a broken one costs a reader nothing the session view
// would otherwise show.
test(`a sandbox boundary ${name} stays unclaimed`, () => {
const out = projectRuntimeEventsToStoredMessages([makeEvent()], {
invocations: [invocation],
});
assert.deepStrictEqual(out.messages, []);
assert.deepStrictEqual(
out.diagnostics.map((diagnostic) => diagnostic.code),
['unclaimed_control_fact'],
);
});
}
test('model thinking attaches to the assistant text row that shares its step message id', () => {
// Real emission and backfill give a step's thinking and text the same message
// id (providerEventId / storedMessageId), so the projection pairs by id.
const out = projectRuntimeEventsToStoredMessages(
[
ev({
id: 'evt-thinking',
ts: ts + 5,
role: 'model',
author: 'agent',
content: {
kind: 'thinking',
text: 'private reasoning',
signature: 'sig-1',
providerOptions: { maka: { kimiReasoningField: 'reasoning' } },
},
refs: { storedMessageId: 'legacy-assistant' },
}),
ev({
id: 'evt-assistant',
ts: ts + 6,
role: 'model',
author: 'agent',
content: { kind: 'text', text: 'visible answer' },
refs: { storedMessageId: 'legacy-assistant' },
}),
],
{ invocations: [invocation] },
);
const legacy: StoredMessage[] = [
{
type: 'assistant',
id: 'legacy-assistant',
turnId,
ts: ts + 6,
text: 'visible answer',
modelId: 'claude-sonnet-4-5',
thinking: {
text: 'private reasoning',
signature: 'sig-1',
providerOptions: { maka: { kimiReasoningField: 'reasoning' } },
},
},
];
assert.deepStrictEqual(out.messages, legacy);
assert.deepStrictEqual(out.diagnostics, []);
});
test('per-step thinking pairs each step assistant row by its own message id', () => {
// Two steps in one turn, each with its own signed thinking. The ledger order
// per step is thinking → text (finish-step flush), and each step's thinking
// carries its step message id, so it must attach to its own assistant row —
// not the last row of the turn.
const out = projectRuntimeEventsToStoredMessages(
[
ev({
id: 'evt-think-1',
ts: ts + 1,
role: 'model',
author: 'agent',
content: { kind: 'thinking', text: 'reasoning one', signature: 'sig-1' },
refs: { providerEventId: 'step-1' },
}),
ev({
id: 'evt-text-1',
ts: ts + 2,
role: 'model',
author: 'agent',
content: { kind: 'text', text: 'answer one' },
refs: { providerEventId: 'step-1' },
}),
ev({
id: 'evt-think-2',
ts: ts + 3,
role: 'model',
author: 'agent',
content: { kind: 'thinking', text: 'reasoning two', signature: 'sig-2' },
refs: { providerEventId: 'step-2' },
}),
ev({
id: 'evt-text-2',
ts: ts + 4,
role: 'model',
author: 'agent',
content: { kind: 'text', text: 'answer two' },
refs: { providerEventId: 'step-2' },
}),
],
{ invocations: [invocation] },
);
const assistants = out.messages.filter((message) => message.type === 'assistant');
assert.deepStrictEqual(assistants, [
{
type: 'assistant',
id: 'step-1',
turnId,
ts: ts + 2,
text: 'answer one',
modelId: 'claude-sonnet-4-5',
thinking: { text: 'reasoning one', signature: 'sig-1' },
},
{
type: 'assistant',
id: 'step-2',
turnId,
ts: ts + 4,
text: 'answer two',
modelId: 'claude-sonnet-4-5',
thinking: { text: 'reasoning two', signature: 'sig-2' },
},
]);
assert.deepStrictEqual(out.diagnostics, []);
});
test('unsupported and incomplete events are diagnostic-only', () => {
const out = projectRuntimeEventsToStoredMessages(
[
ev({
id: 'evt-thinking',
role: 'model',
author: 'agent',
content: { kind: 'thinking', text: 'private reasoning' },
}),
ev({
id: 'evt-permission-orphan',
actions: {
permissionDecision: {
requestId: 'missing-request',
decision: 'deny',
},
},
}),
ev({
id: 'evt-invalid-result',
role: 'tool',
author: 'tool',
content: {
kind: 'function_response',
id: 'tool-x',
name: 'Read',
result: 'plain string is not ToolResultContent',
},
}),
ev({ id: 'evt-ended', status: 'completed', actions: { endInvocation: true } }),
],
{ invocations: [invocation] },
);
assert.deepStrictEqual(
out.messages.map((message) => message.type),
['turn_state'],
);
// The orphaned permission decision carries no content, so its catch-all is
// the soft code — but the projector that tried to build its row and failed
// still reports `incomplete_event`, which stays hard. Downgrading the
// catch-all never downgrades a projector that attempted a message.
assert.deepStrictEqual(
out.diagnostics.map((diag) => diag.code),
[
'incomplete_event',
'unclaimed_control_fact',
'incomplete_event',
'unsupported_event',
'unsupported_event',
],
);
});
// Where the projection draws the line between a view it can still serve and
// one it must refuse: an unclaimed event that carries no content owns no chat
// row, so nothing a reader would have seen is missing.
test('an unclaimed control-only event is soft and leaves every message intact', () => {
const out = projectRuntimeEventsToStoredMessages(
[
ev({ id: 'evt-user', role: 'user', author: 'user', content: { kind: 'text', text: 'hi' } }),
ev({
id: 'evt-control',
actions: { stateDelta: { somethingTheProjectionWasNeverTaught: true } },
}),
ev({
id: 'evt-assistant',
role: 'model',
author: 'agent',
content: { kind: 'text', text: 'hello' },
}),
],
{ invocations: [invocation] },
);
assert.deepStrictEqual(
out.messages.map((message) => message.id),
['evt-user', 'evt-assistant'],
);
assert.deepStrictEqual(
out.diagnostics.map((diagnostic) => diagnostic.code),
['unclaimed_control_fact'],
);
assert.deepStrictEqual(
out.diagnostics.map((diagnostic) => diagnostic.eventId),
['evt-control'],
);
assert.strictEqual(out.diagnostics.some(isHardRuntimeEventReadModelDiagnostic), false);
});
test('an unclaimed event that carries content stays hard', () => {
const out = projectRuntimeEventsToStoredMessages(
[
ev({
id: 'evt-future-content',
role: 'model',
author: 'agent',
content: { kind: 'not_yet_projected', text: 'a reader would have seen this' } as never,
}),
],
{ invocations: [invocation] },
);
assert.deepStrictEqual(out.messages, []);
assert.deepStrictEqual(
out.diagnostics.map((diagnostic) => diagnostic.code),
['unsupported_event'],
);
assert.strictEqual(out.diagnostics.every(isHardRuntimeEventReadModelDiagnostic), true);
});
test('failed terminal RuntimeEvent maps to failed turn state with the class it states', () => {
const out = projectRuntimeEventsToStoredMessages(
[
ev({
id: 'evt-failed',
ts: ts + 9,
status: 'failed',
actions: { endInvocation: true, stateDelta: { failureClass: 'tool_failed' } },
}),
],
{
invocations: [endedAs('failed', 'tool_failed')],
},
);
assert.deepStrictEqual(out.messages, [
{
type: 'turn_state',
id: 'evt-failed',
turnId,
ts: ts + 9,
status: 'failed',
parentTurnId: 'parent-turn',
errorClass: 'tool_failed',
},
]);
assert.deepStrictEqual(out.diagnostics, []);
});
test('failure diagnostics survive terminal projection and serialization round-trip', () => {
const message = 'Quota exceeded for api_key=sk-test-diagnostic-value (status=429)';
const out = projectRuntimeEventsToStoredMessages(
[
ev({
id: 'provider-failed',
status: 'failed',
content: {
kind: 'error',
message,
retry: { decision: 'declined', because: 'side_effects' },
},
actions: { endInvocation: true, stateDelta: { failureClass: 'rate_limit' } },
}),
],
{ invocations: [endedAs('failed', 'rate_limit')] },
);
const live = deriveTurnRecords(out.messages)[0];
const roundTripped = deriveTurnRecords(
JSON.parse(JSON.stringify(out.messages)).map(decodeCanonicalMessage),
)[0];
assert.equal(live.failureMessage, message);
assert.deepEqual(roundTripped, live);
assert.equal(live.errorClass, 'rate_limit');
assert.throws(() =>
decodeCanonicalMessage({ ...out.messages[0], failureMessage: '界'.repeat(2048) }),
);
assert.deepEqual(live.retry, { decision: 'declined', because: 'side_effects' });
});
test('bounds terminal diagnostics before publishing decodable turn states', () => {
const out = projectRuntimeEventsToStoredMessages(
[
ev({
status: 'failed',
content: { kind: 'error', message: '界'.repeat(2048) },
actions: { endInvocation: true, stateDelta: { failureClass: 'unknown' } },
}),
],
{ invocations: [endedAs('failed', 'unknown')] },
);
const turn = deriveTurnRecords(out.messages)[0];
assert.ok(turn.failureMessage?.startsWith('界'));
assert.ok(Buffer.byteLength(turn.failureMessage!) <= MODEL_FAILURE_MESSAGE_MAX_BYTES);
assert.doesNotThrow(() => out.messages.map(decodeCanonicalMessage));
});
test('a session written with the retired context_budget_exhausted reads back as context_overflow', () => {
// The runtime no longer decides locally that a request cannot be made to
// fit, so that outcome is gone from the live contract. Sessions persisted
// before still carry it, and must still decode — as the one name that
// survives.
const out = projectRuntimeEventsToStoredMessages(
[
ev({
id: 'evt-budget-exhausted',
ts: ts + 9,
status: 'failed',
actions: {
endInvocation: true,
stateDelta: {
stopReason: 'context_budget_exhausted',
failureClass: 'context_budget_exhausted',
contextBudgetExhaustedDetail: 'head_anchor_exceeds_capacity',
},
},
}),
],
{
invocations: [endedAs('failed', 'context_budget_exhausted')],
},
);
assert.deepStrictEqual(
out.messages.find((message) => message.type === 'turn_state'),
{
type: 'turn_state',
id: 'evt-budget-exhausted',
turnId,
ts: ts + 9,
status: 'failed',
parentTurnId: 'parent-turn',
errorClass: 'context_overflow',
},
);
assert.deepStrictEqual(out.diagnostics, []);
});
test('tool step cap terminal fact projects a persistent system notice', () => {
const out = projectRuntimeEventsToStoredMessages(
[
ev({
id: 'evt-step-limit',
ts: ts + 9,
status: 'failed',
actions: {
endInvocation: true,
stateDelta: { stopReason: 'step_limit', failureClass: 'tool_step_cap_reached' },
},
}),
],
{
invocations: [endedAs('failed', 'tool_step_cap_reached')],
},
);
assert.deepStrictEqual(
out.messages.find((message) => message.type === 'system_note'),
{
type: 'system_note',
id: 'evt-step-limit:step-limit-notice',
turnId,
ts: ts + 9,
kind: 'step_limit',
},
);
});
test('aborted terminal RuntimeEvent preserves abort source from runtime state', () => {
const out = projectRuntimeEventsToStoredMessages(
[
ev({
id: 'evt-aborted',
ts: ts + 9,
status: 'aborted',
actions: { endInvocation: true, stateDelta: { abortSource: 'renderer.stop_button' } },
}),
],
{
invocations: [endedAs('aborted')],
},
);
assert.deepStrictEqual(out.messages, [
{
type: 'turn_state',
id: 'evt-aborted',
turnId,
ts: ts + 9,
status: 'aborted',
parentTurnId: 'parent-turn',
abortedAt: ts + 9,
abortSource: 'renderer.stop_button',
},
]);
assert.deepStrictEqual(out.diagnostics, []);
});
// The omission is `classifyRuntimeEventTerminalFact`'s to report. Repeating it
// here would turn a transcript row that reads fine into an unreadable Session.
test('aborted terminal RuntimeEvent that states no source still projects its turn state', () => {
const out = projectRuntimeEventsToStoredMessages(
[
ev({
id: 'evt-aborted',
ts: ts + 9,
status: 'aborted',
actions: { endInvocation: true },
}),
],
{
invocations: [endedAs('aborted')],
},
);
assert.partialDeepStrictEqual(out.messages[0], {
type: 'turn_state',
status: 'aborted',
abortedAt: ts + 9,
});
assert.deepStrictEqual(out.diagnostics, []);
});
test('projects tool_call stepId from refs so the UI timeline keeps step pairing', () => {
const stepCall = (id: string, stepId?: string) =>
ev({
id: `evt-${id}`,
role: 'model' as const,
author: 'agent' as const,
content: {
kind: 'function_call' as const,
id,
name: 'Read',
args: { path: '/tmp/a.txt' },
},
refs: { toolCallId: id, ...(stepId ? { stepId } : {}) },
});
const withStep = projectRuntimeEventsToStoredMessages([stepCall('tool-step', 'step-1')], {
invocations: [invocation],
});
assert.partialDeepStrictEqual(withStep.messages[0], {
type: 'tool_call',
id: 'tool-step',
stepId: 'step-1',
});
// Legacy events without refs.stepId must not grow a stepId key: the UI
// uses its absence to pick the backward-compatible tools-first ordering.
const withoutStep = projectRuntimeEventsToStoredMessages([stepCall('tool-legacy')], {
invocations: [invocation],
});
const legacyCall = withoutStep.messages[0];
assert.partialDeepStrictEqual(legacyCall, { type: 'tool_call', id: 'tool-legacy' });
assert.strictEqual(legacyCall && 'stepId' in legacyCall, false);
});
test('retains nested CodeMode identity on tool rows used by the UI read model', () => {
const identity = {
origin: 'code_mode' as const,
modelVisibility: 'hidden' as const,
refs: {
toolCallId: 'nested-1',
parentToolCallId: 'exec-1',
parentOperationId: 'exec-op-1',
},
};
const out = projectRuntimeEventsToStoredMessages(
[
ev({
...identity,
role: 'model',
author: 'agent',
content: { kind: 'function_call', id: 'nested-1', name: 'Read', args: {} },
}),
ev({
...identity,
role: 'tool',
author: 'tool',
content: {
kind: 'function_response',
id: 'nested-1',
name: 'Read',
result: { kind: 'text', text: 'ok' },
},
}),
],
{ invocations: [invocation] },
);
assert.strictEqual(out.messages.length, 2);
for (const message of out.messages) {
assert.partialDeepStrictEqual(message, {
origin: 'code_mode',
modelVisibility: 'hidden',
parentToolCallId: 'exec-1',
parentOperationId: 'exec-op-1',
});
}
});
test('projects tool_call activityKind from runtime state for replay', () => {
const out = projectRuntimeEventsToStoredMessages(
[
ev({
id: 'evt-tool-kind',
role: 'model',
author: 'agent',
content: {
kind: 'function_call',
id: 'tool-kind',
name: 'CustomCommand',
args: {},
},
actions: { stateDelta: { activityKind: 'command' } },
refs: { toolCallId: 'tool-kind' },
}),
],
{ invocations: [invocation] },
);
assert.partialDeepStrictEqual(out.messages[0], {
type: 'tool_call',
id: 'tool-kind',
activityKind: 'command',
});
});
});
/**
* One reachable event per `RuntimeEventActions` field, each entry typed to its
* own key so it cannot drift onto another field or be filled with a placeholder.
*
* This is the premise the read model's soft path rests on. An unclaimed
* content-free event degrades the view instead of withholding it, which is only
* safe while every action a reader can meet is claimed — several of them
* (`permissionDecision`, `tokenUsage`, the terminal fact) do produce rows, and
* `runtime-event-backfill.ts` already writes a content-free event that becomes a
* visible `permission_decision`. The SessionEvent mapper contract
* only covers events built by `mapSessionEventToRuntimeEvent`; tool-runtime,
* terminal-run-commit and the backfill write RuntimeEvents directly. Keying this
* table on the action surface itself covers those paths too.
*/
type ActionCoverageSamples = {
[K in keyof Required<RuntimeEventActions>]: {
/** The action value under test, typed to its own key. */
action: Required<RuntimeEventActions>[K];
/** The rest of the event, as the field's real emitter writes it. */
event?: Partial<RuntimeEvent>;
};
};
const ACTION_COVERAGE_SAMPLES: ActionCoverageSamples = {
coordination: {
action: {
actionId: 'clarify',
userText: 'Which task?',
result: { disposition: 'clarify', coordinationTurnId: 'turn-1' },
clarification: 'Please name a task.',
},
},
handoffPause: {
action: {
protocol: 'runtime_handoff_pause_v1',
handoffId: 'handoff',
hostEpoch: 'host',
remainingSteps: null,
rootRunId: 'root',
successorRunId: 'next',
successorInvocationId: 'next',
claimId: 'claim',
},
},
// `stateDelta` is an open record, so only named shapes are claimed and this
// entry covers the field, not its contents. A new key inside a state delta is
// out of reach of any contract keyed on the action surface.
stateDelta: { action: { continuationStart: true } },
managedMutationTerminal: {
action: {
protocol: 'managed_mutation_terminal_v1',
operationId: 'coverage-operation',
dispatchEventId: 'coverage-dispatch',
workspaceInstanceId: 'instance_44444444444444444444444444444444',
terminalKind: 'no_workspace_change',
},
},
continuationStart: {
action: {
protocol: 'continuation_start_v2',
provenance: 'runtime_admission',
claimId: 'coverage-claim',
boundaryDigest: 'sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
immediateSource: {
sessionId: 'coverage-session',
invocationId: 'coverage-invocation',
runId: 'coverage-run',
turnId: 'coverage-turn',
highWater: 1,
prefixDigest: 'sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb',
},
replayManifestDigest:
'sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc',
providerProjectionVersion: 1,
providerReplayDigest:
'sha256:dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd',
},
},
artifactDelta: { action: { 'artifact-1': 42 } },
permissionRequest: {
action: {
kind: 'tool_permission',
requestId: 'coverage-request',
toolUseId: 'coverage-tool',
toolName: 'Read',
category: 'read',
reason: 'custom',
args: { path: '/tmp/a.txt' },
rememberForTurnAllowed: true,
},
event: { refs: { toolCallId: 'coverage-tool' } },
},
permissionDecision: {
action: { requestId: 'coverage-request', decision: 'allow', toolName: 'Read' },
event: { refs: { toolCallId: 'coverage-tool' } },
},
// The canonical outcome lives in InteractionStore, so a standalone acceptance
// reports an `incomplete_event`. That is a completeness diagnostic, not a
// coverage gap: the projection still claims the field.
permissionAnswerAccepted: {
action: { requestId: 'coverage-request' },
event: { author: 'user', refs: { toolCallId: 'coverage-tool' } },
},
permissionClosureAccepted: {
action: { requestId: 'coverage-request', reason: 'timed_out' },
},
userQuestionRequest: {
action: {
requestId: 'coverage-question',
toolUseId: 'coverage-question-tool',
questions: [{ question: 'Choose', options: [{ label: 'Extend' }] }],
},
event: { refs: { toolCallId: 'coverage-question-tool' } },
},
userQuestionAnswerAccepted: {
action: { requestId: 'coverage-question' },
event: { author: 'user', refs: { toolCallId: 'coverage-question-tool' } },
},
formRequest: {
action: {
requestId: 'coverage-form',
toolUseId: 'coverage-form-tool',
message: 'Choose settings',
requester: { name: 'deploy' },
fields: [{ kind: 'boolean', name: 'confirm', label: 'Confirm', required: true }],
},
event: { refs: { toolCallId: 'coverage-form-tool' } },
},
formAnswerAccepted: {
action: { requestId: 'coverage-form' },
event: { author: 'user', refs: { toolCallId: 'coverage-form-tool' } },
},
transferToAgent: { action: 'agent-b' },
// The terminal fact is one of the actions that does own a row, and the event
// states the outcome it ends on.
endInvocation: { action: true, event: { status: 'completed' } },
tokenUsage: { action: { input: 10, output: 5 } },
toolDispatch: {
action: {
protocol: 't1_after_preflight_v1',
operationId: 'coverage-op',
providerToolCallId: 'coverage-tool',
toolName: 'Bash',
canonicalArgsHash: 'sha256:args',
recoveryMode: 'reconcile',
},
event: { refs: { toolCallId: 'coverage-tool', operationId: 'coverage-op' } },
},
toolRecovery: {
action: {
kind: 'maka.tool.reconcile_result',
version: 1,
payload: {
protocol: 'tool_reconcile_v1',
operationId: 'coverage-op',
observation: 'unreadable',
observationSchema: 'state_identity_v1',
observationDigest:
'sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
},
},
event: { refs: { toolCallId: 'coverage-tool', operationId: 'coverage-op' } },
},
workspaceFact: {
action: {
kind: 'maka.workspace.epoch_opened',
version: 1,
payload: {
protocol: 'workspace_epoch_opened_v1',
repositoryId: `repository_${'1'.repeat(32)}`,
workspaceId: `workspace_${'2'.repeat(32)}`,
workspaceEpochId: `epoch_${'3'.repeat(32)}`,
workspaceInstanceId: `instance_${'4'.repeat(32)}`,
initialWorkspaceVersionId: `version_${'5'.repeat(32)}`,
mode: 'managed_worktree',
objectFormat: 'sha1',
sourceCommitOid: '1'.repeat(40),
sourceTreeOid: '2'.repeat(40),
materializationProfileDigest: `sha256:${'3'.repeat(64)}`,
materializationSemantics: 'git_tree_materialized_with_fixed_config_v1',
policyHash: `sha256:${'4'.repeat(64)}`,
},
},
},
runtimeProtocol: { action: { toolBoundary: 't1_after_preflight_v1' } },
};
describe('system note projection', () => {
test('projects a turn-scoped note back into its transcript row', () => {
const out = projectRuntimeEventsToStoredMessages(
[
ev({
id: 'evt-note',
content: {
kind: 'system_note',
note: 'context_compacted',
data: { removedMessages: 12 },
},
modelVisibility: 'hidden',
refs: { storedMessageId: 'legacy-note' },
}),
],
{ invocations: [invocation] },
);
assert.deepStrictEqual(out.diagnostics, []);
assert.deepStrictEqual(out.messages, [
{
type: 'system_note',
id: 'legacy-note',
turnId,
ts,
kind: 'context_compacted',
data: { removedMessages: 12 },
},
]);
});
test('converts a legacy turn-scoped note and reads back the same row', () => {
const note: StoredMessage = {
type: 'system_note',
id: 'legacy-step-limit',
turnId,
ts,
kind: 'step_limit',
data: { steps: 40 },
};
const backfilled = backfillRuntimeEventsFromStoredMessages({
run: { sessionId, invocationId, runId, turnId },
outcome: { status: 'completed', ts },
messages: [note],
modelHistory: 'full',
now: () => ts,
});
assert.deepStrictEqual(backfilled.diagnostics, []);
const projected = projectRuntimeEventsToStoredMessages(backfilled.events, {
invocations: [invocation],
});
assert.deepStrictEqual(
projected.messages.filter((message) => message.type === 'system_note'),
[note],
);
});
test('leaves a session-level note out of the run ledger', () => {
const backfilled = backfillRuntimeEventsFromStoredMessages({
run: { sessionId, invocationId, runId, turnId },
messages: [
{
type: 'system_note',
id: 'legacy-mode-change',
turnId,
ts,
kind: 'mode_change',
data: { from: 'ask', to: 'bypass' },
},
],
modelHistory: 'full',
now: () => ts,
});
assert.deepStrictEqual(
backfilled.events.filter((event) => event.content?.kind === 'system_note'),
[],
);
assert.partialDeepStrictEqual(backfilled.diagnostics, [{ code: 'skipped_high_risk_message' }]);
});
});
describe('legacy transcript conversion keeps every row', () => {
const convert = (messages: readonly StoredMessage[]) =>
backfillRuntimeEventsFromStoredMessages({
run: { sessionId, invocationId, runId, turnId },
outcome: { status: 'completed', ts },
messages,
modelHistory: 'full',
now: () => ts,
});
test('keeps a tool result whose call is not in the turn, out of model replay', () => {
const orphan: StoredMessage = {
type: 'tool_result',
id: 'legacy-orphan-result',
turnId,
ts,
toolUseId: 'tool-gone',
isError: false,
content: { kind: 'text', text: 'done' },
};
const converted = convert([orphan]);
const response = converted.events.find((event) => event.content?.kind === 'function_response');
assert.strictEqual(response?.modelVisibility, 'hidden');
assert.strictEqual(runtimeEventHasModelVisibleContent(response as RuntimeEvent), false);
const projected = projectRuntimeEventsToStoredMessages(converted.events, {
invocations: [invocation],
});
assert.partialDeepStrictEqual(
projected.messages.filter((message) => message.type === 'tool_result'),
[{ id: 'legacy-orphan-result', toolUseId: 'tool-gone' }],
);
});
test('keeps a provider-native call whose opaque output was not retained', () => {
const converted = convert([
{
type: 'tool_call',
id: 'tool-native',
turnId,
ts,
toolName: 'WebSearch',
args: { query: 'maka' },
providerExecuted: true,
},
]);
const call = converted.events.find((event) => event.content?.kind === 'function_call');
assert.strictEqual(call?.modelVisibility, 'hidden');
assert.partialDeepStrictEqual(converted.diagnostics, [
{ code: 'skipped_provider_native_replay_gap' },
]);
const projected = projectRuntimeEventsToStoredMessages(converted.events, {
invocations: [invocation],
});
assert.partialDeepStrictEqual(
projected.messages.filter((message) => message.type === 'tool_call'),
[{ id: 'tool-native', toolName: 'WebSearch' }],
);
});
test('converts a permission decision on its own evidence', () => {
const decision: StoredMessage = {
type: 'permission_decision',
id: 'request-1',
turnId,
ts,
toolUseId: 'tool-1',
toolName: 'Bash',
decision: 'allow',
hint: 'rm -rf build',
};
const converted = convert([decision]);
assert.deepStrictEqual(converted.diagnostics, []);
const projected = projectRuntimeEventsToStoredMessages(converted.events, {
invocations: [invocation],
});
assert.deepStrictEqual(
projected.messages.filter((message) => message.type === 'permission_decision'),
[decision],
);
});
test('ends a turn whose transcript never said how it ended', () => {
const converted = backfillRuntimeEventsFromStoredMessages({
run: { sessionId, invocationId, runId, turnId },
messages: [{ type: 'user', id: 'legacy-user', turnId, ts, text: 'hello' }],
modelHistory: 'full',
now: () => ts,
});
const terminal = converted.events.filter((event) => event.actions?.endInvocation);
assert.partialDeepStrictEqual(terminal, [{ status: 'failed' }]);
assert.strictEqual(terminal[0]?.actions?.stateDelta?.failureClass, 'missing_terminal_event');
assert.partialDeepStrictEqual(converted.diagnostics, [{ code: 'synthesized_terminal_event' }]);
});
});
describe('RuntimeEventActions projection coverage', () => {
for (const [field, sample] of Object.entries(ACTION_COVERAGE_SAMPLES)) {
test(`actions.${field} projects without an unclaimed-event diagnostic`, () => {
const actions = { [field]: sample.action } as RuntimeEventActions;
// Guards an entry that names a field but leaves it absent at runtime.
assert.strictEqual(field in actions, true);
const out = projectRuntimeEventsToStoredMessages([ev({ ...sample.event, actions })], {
invocations: [invocation],
});
assert.deepStrictEqual(out.diagnostics.filter(isUnclaimedRuntimeEventDiagnostic), []);
});
}
});
describe('token usage projection', () => {
test('carries the cross-turn request anchor both ways', () => {
const lastRequestAnchor = { inputTokens: 120, outputTokens: 30 };
const anchored = ev({
id: 'evt-token-anchor',
role: 'system',
author: 'system',
actions: { tokenUsage: { input: 370, output: 60, lastRequestAnchor } },
});
const projected = projectRuntimeEventsToStoredMessages(
[
ev({
id: 'evt-anchor-user',
role: 'user',
author: 'user',
content: { kind: 'text', text: 'read the file' },
}),
anchored,
],
{ invocations: [invocation] },
);
const usage = projected.messages.find((message) => message.type === 'token_usage');
assert.partialDeepStrictEqual(usage, { type: 'token_usage', input: 370, lastRequestAnchor });
const backfilled = backfillRuntimeEventsFromStoredMessages({
run: { sessionId, invocationId, runId, turnId },
messages: projected.messages,
now: () => ts,
});
assert.deepStrictEqual(
backfilled.events.find((event) => event.actions?.tokenUsage)?.actions?.tokenUsage
?.lastRequestAnchor,
lastRequestAnchor,
);
});
});
describe('SessionManager read behavior', () => {
test('getMessages requires RuntimeReadModel stores instead of reading SessionStore messages directly', async () => {
const messages: StoredMessage[] = equivalentLegacyMessages();
const store = new ReadOnlyStore(messages);
const manager = new SessionManager({
store,
backends: new BackendRegistry(),
newId: () => 'id',
now: () => ts,
});
await expectRejects(
manager.getMessages(sessionId),
/RuntimeReadModel requires AgentRunStore and RuntimeEventStore/,
);
assert.strictEqual(store.readMessagesCalls, 0);
});
});
class ReadOnlyStore implements SessionStore {
readMessagesCalls = 0;
constructor(private readonly messages: StoredMessage[]) {}
async createSubagent(
_input: CreateSessionInput,
): Promise<{ header: SessionHeader; created: boolean }> {
throw new Error('not implemented');
}
async create(_input: CreateSessionInput): Promise<SessionHeader> {
throw new Error('not implemented');
}
async setExecutionBoundaryKind(): Promise<never> {
throw new Error('not implemented');
}
async readExecutionBoundary(): Promise<never> {
throw new Error('not implemented');
}
async list(_filter?: SessionListFilter): Promise<SessionSummary[]> {
return [];
}
async readHeader(id: string): Promise<SessionHeader> {
return makeHeader(id);
}
async readMessages(_sessionId: string): Promise<StoredMessage[]> {
this.readMessagesCalls += 1;
return [...this.messages];
}
async readMessagesAfter(
_sessionId: string,
request: { afterSequence?: number; maxMessages: number },
): Promise<{
records: readonly { sequence: number; message: StoredMessage }[];
highWaterSequence: number | null;
}> {
this.readMessagesCalls += 1;
return {
records: this.messages
.map((message, sequence) => ({ sequence, message }))
.filter(({ sequence }) => sequence > (request.afterSequence ?? -1))
.slice(0, request.maxMessages),
highWaterSequence: this.messages.length > 0 ? this.messages.length - 1 : null,
};
}
async listTurns(_sessionId: string): Promise<TurnRecord[]> {
return deriveTurnRecords(this.messages);
}
async appendMessage(_sessionId: string, _m: StoredMessage): Promise<void> {
throw new Error('not implemented');
}
async appendMessages(_sessionId: string, _ms: StoredMessage[]): Promise<void> {
throw new Error('not implemented');
}
async updateHeader(id: string, patch: Partial<SessionHeader>): Promise<SessionHeader> {
return { ...makeHeader(id), ...patch };
}
async setFlagged(_sessionId: string, _isFlagged: boolean): Promise<void> {}
async rename(_sessionId: string, _name: string): Promise<void> {}
async remove(_sessionId: string): Promise<void> {}
}
async function expectRejects(promise: Promise<unknown>, pattern: RegExp): Promise<void> {
try {
await promise;
} catch (error) {
assert.match(String(error instanceof Error ? error.message : String(error)), pattern);
return;
}
throw new Error(`Expected promise to reject with ${pattern}`);
}
function archivedStatus(message: StoredMessage | undefined): string | undefined {
if (message?.type !== 'tool_result') return undefined;
return message.content.kind === 'archived_tool_result' ? message.content.status : undefined;
}
function makeHeader(id: string): SessionHeader {
return {
id,
workspaceRoot: '/tmp/work',
cwd: '/tmp/work',
createdAt: ts,
name: 'Session',
titleIsManual: true,
isFlagged: false,
labels: [],
isArchived: false,
status: 'active',
hasUnread: false,
backend: 'fake',
llmConnectionSlug: 'fake',
connectionLocked: false,
model: 'fake-model',
permissionMode: 'ask',
schemaVersion: 1,
};
}
test('Coordination receipts materialize as host facts, never assistant output', () => {
const receipt = {
actionId: 'clarify',
userText: 'Which task?',
clarification: 'Please name a task.',
result: { disposition: 'clarify' as const, coordinationTurnId: turnId },
};
const out = projectRuntimeEventsToStoredMessages(
[
ev({
id: 'coordination',
author: 'host',
modelVisibility: 'hidden',
actions: { coordination: receipt },
}),
],
{ invocations: [invocation] },
);
assert.ok(
out.messages.some(
(message) => message.type === 'workhub_coordination' && message.kind === 'action_receipt',
),
);
assert.equal(
out.messages.some((message) => message.type === 'assistant'),
false,
);
});