blob: e2ee16c1e9f83e2a49a2f4d7b1dec675ac9e2e34 [file]
import assert from 'node:assert/strict';
import { describe, test } from 'node:test';
import type { AgentRunHeader, RuntimeEvent, RuntimeEventStore } from '@maka/core';
import {
projectAgentGraphRecords,
readCommittedAgentGraphProjection,
replayAgentGraphRecords,
} from '../stream-graph-projection.js';
const baseTs = 1_800_000_000_000;
describe('committed stream graph projection', () => {
test('projects immutable child-session events into a stable reference-only graph trace', async () => {
const runA = runHeader({
sessionId: 'child-a',
runId: 'run-a',
turnId: 'turn-a',
status: 'completed',
createdAt: baseTs,
});
const runB = runHeader({
sessionId: 'child-b',
runId: 'run-b',
turnId: 'turn-b',
status: 'failed',
createdAt: baseTs + 1,
});
const eventsByRun = new Map<string, RuntimeEvent[]>([
[
runA.runId,
[
runtimeEvent(runA, {
id: 'a-message',
ts: baseTs + 1,
role: 'model',
author: 'agent',
content: { kind: 'text', text: 'payload-must-not-be-copied' },
}),
runtimeEvent(runA, {
id: 'a-partial',
ts: baseTs + 2,
partial: true,
role: 'model',
author: 'agent',
content: { kind: 'text', text: 'mutable-stream-chunk' },
}),
runtimeEvent(runA, {
id: 'a-permission',
ts: baseTs + 4,
actions: {
permissionRequest: {
kind: 'tool_permission',
requestId: 'permission-a',
toolUseId: 'tool-a',
toolName: 'Read',
category: 'read',
reason: 'custom',
args: {},
rememberForTurnAllowed: true,
},
},
}),
runtimeEvent(runA, {
id: 'a-permission-decision',
ts: baseTs + 5,
author: 'user',
actions: {
permissionDecision: {
requestId: 'permission-a',
decision: 'allow',
rememberForTurn: false,
},
},
}),
runtimeEvent(runA, {
id: 'a-complete',
ts: baseTs + 8,
status: 'completed',
actions: { endInvocation: true },
}),
],
],
[
runB.runId,
[
runtimeEvent(runB, {
id: 'b-tool-call',
ts: baseTs + 2,
role: 'model',
author: 'agent',
content: { kind: 'function_call', id: 'tool-b', name: 'Grep', args: {} },
}),
runtimeEvent(runB, {
id: 'b-tool-result',
ts: baseTs + 6,
role: 'tool',
author: 'tool',
content: {
kind: 'function_response',
id: 'tool-b',
name: 'Grep',
result: { matches: 0 },
},
}),
runtimeEvent(runB, {
id: 'b-failed',
ts: baseTs + 7,
status: 'failed',
content: { kind: 'error', message: 'provider failed' },
}),
],
],
]);
const projection = await readCommittedAgentGraphProjection({
graphId: 'graph-1',
operators: [
{ operatorId: 'research', sessionId: runA.sessionId },
{ operatorId: 'verify', sessionId: runB.sessionId },
],
runStore: {
async listSessionRuns(sessionId) {
return sessionId === runA.sessionId ? [runA] : [runB];
},
},
runtimeEventStore: {
async readImmutableRuntimeEvents(_sessionId, runId) {
return eventsByRun.get(runId) ?? [];
},
},
});
assert.equal(projection.ignoredPartialEvents, 1);
assert.deepEqual(
projection.records.map((record) => record.source.runtimeEventId),
[
'a-message',
'b-tool-call',
'a-permission',
'a-permission-decision',
'b-tool-result',
'b-failed',
'a-complete',
],
);
assert.deepEqual(
projection.records.map((record) => record.eventTime),
[baseTs + 1, baseTs + 2, baseTs + 4, baseTs + 5, baseTs + 6, baseTs + 7, baseTs + 8],
);
assert.deepEqual(
projection.records
.filter((record) => record.operatorId === 'research')
.map((record) => record.orderKey.committedEventOrdinal),
[0, 1, 2, 3],
'partial events do not renumber committed source facts',
);
assert.deepEqual(projection.records[0]?.facets, ['message']);
assert.deepEqual(projection.records[1]?.facets, ['tool_call']);
assert.deepEqual(projection.records[5]?.facets, ['error', 'failed']);
assert.deepEqual(projection.records[2]?.supervisorSignals, [
{ kind: 'attention', reason: 'permission_request' },
]);
assert.deepEqual(projection.records[5]?.supervisorSignals, [
{ kind: 'terminal', status: 'failed' },
]);
assert.deepEqual(
projection.supervisorMetaStream.map((record) => record.recordId),
projection.records.map((record) => record.recordId),
'the supervisor observes every graph record, not only attention records',
);
assert.deepEqual(projection.supervisorMetaStream[0]?.signals, []);
assert.deepEqual(projection.supervisorMetaStream[2]?.signals, [
{ kind: 'attention', reason: 'permission_request' },
]);
assert.equal(
replayAgentGraphRecords(projection.records.slice(0, 3)).operators.research?.status,
'running',
'supervisor attention is orthogonal to graph lifecycle',
);
assert.equal(projection.state.operators.research?.status, 'completed');
assert.equal(projection.state.operators.verify?.status, 'failed');
assert.equal(projection.state.operators.research?.activations['run-a']?.recordCount, 4);
assert.equal(projection.records[0]?.previousRecordId, undefined);
assert.equal(
projection.records[2]?.previousRecordId,
projection.records[0]?.recordId,
'predecessors are activation-local rather than global',
);
assert.doesNotMatch(JSON.stringify(projection.records), /payload-must-not-be-copied/);
assert.doesNotMatch(JSON.stringify(projection.records), /mutable-stream-chunk/);
});
test('replay is deterministic for reordered delivery and idempotent duplicates', () => {
const run = runHeader({
sessionId: 'child-a',
runId: 'run-a',
turnId: 'turn-a',
status: 'completed',
createdAt: baseTs,
});
const { records } = projectAgentGraphRecords({
graphId: 'graph-replay',
streams: [
{
operator: { operatorId: 'research', sessionId: run.sessionId },
run,
events: [
runtimeEvent(run, {
id: 'message',
ts: baseTs + 1,
role: 'model',
author: 'agent',
content: { kind: 'text', text: 'result' },
}),
runtimeEvent(run, {
id: 'complete',
ts: baseTs + 2,
status: 'completed',
}),
],
},
],
});
const canonical = replayAgentGraphRecords(records);
const reorderedWithDuplicates = replayAgentGraphRecords([
records[1]!,
records[0]!,
records[0]!,
records[1]!,
]);
assert.deepEqual(reorderedWithDuplicates, canonical);
});
test('replays reserved JavaScript property names as ordinary graph identities', () => {
const run = runHeader({
sessionId: 'reserved-session',
runId: 'constructor',
turnId: 'reserved-turn',
status: 'running',
createdAt: baseTs,
});
const { records } = projectAgentGraphRecords({
graphId: 'graph-reserved-identities',
streams: [
{
operator: { operatorId: '__proto__', sessionId: run.sessionId },
run,
events: [runtimeEvent(run, { id: 'toString', ts: baseTs + 1 })],
},
],
});
const state = replayAgentGraphRecords(records);
assert.equal(Object.hasOwn(state.operators, '__proto__'), true);
assert.equal(state.operators['__proto__']?.operatorId, '__proto__');
assert.equal(
Object.hasOwn(state.operators['__proto__']?.activations ?? {}, 'constructor'),
true,
);
const reservedActivation = Object.entries(state.operators['__proto__']?.activations ?? {}).find(
([activationId]) => activationId === 'constructor',
)?.[1];
assert.equal(reservedActivation?.agentRunId, 'constructor');
});
test('rejects one Session projected under different operators across observations', () => {
const run = runHeader({
sessionId: 'child-a',
runId: 'run-a',
turnId: 'turn-a',
status: 'running',
createdAt: baseTs,
});
const event = runtimeEvent(run, {
id: 'shared-event',
ts: baseTs + 1,
role: 'model',
author: 'agent',
content: { kind: 'text', text: 'one durable fact' },
});
const first = projectAgentGraphRecords({
graphId: 'graph-session-owner',
streams: [
{
operator: { operatorId: 'one', sessionId: run.sessionId },
run,
events: [event],
},
],
});
const second = projectAgentGraphRecords({
graphId: 'graph-session-owner',
streams: [
{
operator: { operatorId: 'two', sessionId: run.sessionId },
run,
events: [event],
},
],
});
assert.notEqual(first.records[0]?.recordId, second.records[0]?.recordId);
assert.throws(
() => replayAgentGraphRecords([...first.records, ...second.records]),
/Session child-a is bound to both one and two/,
);
});
test('keeps existing records byte-stable when a late operator contributes earlier event time', () => {
const runA = runHeader({
sessionId: 'child-a',
runId: 'run-a',
turnId: 'turn-a',
status: 'running',
createdAt: baseTs,
});
const runB = runHeader({
sessionId: 'child-b',
runId: 'run-b',
turnId: 'turn-b',
status: 'running',
createdAt: baseTs + 1,
});
const streamA = {
operator: { operatorId: 'research', sessionId: runA.sessionId },
run: runA,
events: [
runtimeEvent(runA, {
id: 'event-a',
ts: baseTs + 100,
role: 'model' as const,
author: 'agent' as const,
content: { kind: 'text' as const, text: 'A' },
}),
],
};
const streamB = {
operator: { operatorId: 'verify', sessionId: runB.sessionId },
run: runB,
events: [
runtimeEvent(runB, {
id: 'event-b',
ts: baseTs + 90,
role: 'model' as const,
author: 'agent' as const,
content: { kind: 'text' as const, text: 'B' },
}),
],
};
const initial = projectAgentGraphRecords({
graphId: 'graph-incremental',
streams: [streamA],
});
const expanded = projectAgentGraphRecords({
graphId: 'graph-incremental',
streams: [streamA, streamB],
});
const initialA = initial.records.find((record) => record.source.runtimeEventId === 'event-a');
const expandedA = expanded.records.find((record) => record.source.runtimeEventId === 'event-a');
assert.deepEqual(expandedA, initialA);
assert.deepEqual(
expanded.records.map((record) => record.source.runtimeEventId),
['event-b', 'event-a'],
);
const replayed = replayAgentGraphRecords([...initial.records, ...expanded.records]);
assert.equal(replayed.appliedRecordIds.length, 2);
});
test('allows equal event times and resolves them with the stable source order key', () => {
const first = runHeader({
sessionId: 'child-z',
runId: 'run-z',
turnId: 'turn-z',
status: 'running',
createdAt: baseTs,
});
const second = runHeader({
sessionId: 'child-a',
runId: 'run-a',
turnId: 'turn-a',
status: 'running',
createdAt: baseTs + 1,
});
const { records } = projectAgentGraphRecords({
graphId: 'graph-equal-time',
streams: [
{
operator: { operatorId: 'z-operator', sessionId: first.sessionId },
run: first,
events: [runtimeEvent(first, { id: 'event-z', ts: baseTs + 10 })],
},
{
operator: { operatorId: 'a-operator', sessionId: second.sessionId },
run: second,
events: [runtimeEvent(second, { id: 'event-a', ts: baseTs + 10 })],
},
],
});
assert.deepEqual(
records.map((record) => record.source.runtimeEventId),
['event-z', 'event-a'],
'run creation time wins before lexical operator identity',
);
assert.deepEqual(
records.map((record) => record.eventTime),
[baseTs + 10, baseTs + 10],
);
assert.equal(replayAgentGraphRecords(records).appliedRecordIds.length, 2);
});
test('projects concurrent tool commits whose immutable event times are not commit-monotonic', () => {
const run = runHeader({
sessionId: 'child-concurrent',
runId: 'run-concurrent',
turnId: 'turn-concurrent',
status: 'running',
createdAt: baseTs,
});
const { records } = projectAgentGraphRecords({
graphId: 'graph-concurrent-tools',
streams: [
{
operator: { operatorId: 'implementation', sessionId: run.sessionId },
run,
// Canonical stores return commit/event-sequence order. A later-started
// concurrent tool may commit after a newer progress fact.
events: [
runtimeEvent(run, { id: 'newer-progress', ts: baseTs + 20 }),
runtimeEvent(run, {
id: 'earlier-tool-call',
ts: baseTs + 10,
role: 'model',
author: 'agent',
content: { kind: 'function_call', id: 'call-1', name: 'Read', args: {} },
}),
],
},
],
});
assert.deepEqual(
records.map((record) => record.source.runtimeEventId),
['earlier-tool-call', 'newer-progress'],
);
assert.deepEqual(
records.map((record) => record.orderKey.committedEventOrdinal),
[1, 0],
'the stable source ordinal continues to describe canonical commit order',
);
});
test('orders locale-equivalent Unicode source identities independently of stream input order', () => {
const precomposedId = '\u00e9';
const decomposedId = 'e\u0301';
assert.equal(precomposedId.localeCompare(decomposedId), 0);
const precomposed = runHeader({
sessionId: 'child-precomposed',
runId: precomposedId,
turnId: 'turn-precomposed',
status: 'running',
createdAt: baseTs,
});
const decomposed = runHeader({
sessionId: 'child-decomposed',
runId: decomposedId,
turnId: 'turn-decomposed',
status: 'running',
createdAt: baseTs,
});
const streams = [
{
operator: { operatorId: precomposedId, sessionId: precomposed.sessionId },
run: precomposed,
events: [runtimeEvent(precomposed, { id: precomposedId, ts: baseTs + 1 })],
},
{
operator: { operatorId: decomposedId, sessionId: decomposed.sessionId },
run: decomposed,
events: [runtimeEvent(decomposed, { id: decomposedId, ts: baseTs + 1 })],
},
];
const canonical = projectAgentGraphRecords({
graphId: 'graph-unicode-order',
streams,
});
const reversed = projectAgentGraphRecords({
graphId: 'graph-unicode-order',
streams: [...streams].reverse(),
});
assert.deepEqual(reversed, canonical);
});
test('routes human-interaction facts to the always-on supervisor without blocking lifecycle', () => {
const run = runHeader({
sessionId: 'child-a',
runId: 'run-a',
turnId: 'turn-a',
status: 'running',
createdAt: baseTs,
});
const { records } = projectAgentGraphRecords({
graphId: 'graph-supervisor',
streams: [
{
operator: { operatorId: 'research', sessionId: run.sessionId },
run,
events: [
runtimeEvent(run, {
id: 'permission-request',
ts: baseTs + 1,
actions: {
permissionRequest: {
kind: 'tool_permission',
requestId: 'permission-1',
toolUseId: 'tool-1',
toolName: 'Read',
category: 'read',
reason: 'custom',
args: {},
rememberForTurnAllowed: true,
},
},
}),
runtimeEvent(run, {
id: 'question-request',
ts: baseTs + 2,
actions: {
userQuestionRequest: {
requestId: 'question-1',
toolUseId: 'tool-2',
questions: [{ question: 'Choose', options: [{ label: 'Continue' }] }],
},
},
}),
runtimeEvent(run, {
id: 'question-answer',
ts: baseTs + 3,
actions: {
userQuestionAnswerAccepted: { requestId: 'question-1' },
},
}),
],
},
],
});
assert.deepEqual(
records.map((record) => record.supervisorSignals),
[
[{ kind: 'attention', reason: 'permission_request' }],
[{ kind: 'attention', reason: 'user_question_request' }],
[],
],
);
assert.deepEqual(records[2]?.facets, ['runtime_fact']);
assert.equal(replayAgentGraphRecords(records).operators.research?.status, 'running');
});
test('keeps later session-inline runs as distinct activations of one operator', () => {
const first = runHeader({
sessionId: 'child-a',
runId: 'run-1',
turnId: 'turn-1',
status: 'completed',
createdAt: baseTs,
});
const followup = runHeader({
sessionId: 'child-a',
runId: 'run-2',
turnId: 'turn-2',
status: 'completed',
createdAt: baseTs + 10,
});
const { records } = projectAgentGraphRecords({
graphId: 'graph-followup',
streams: [
{
operator: { operatorId: 'research', sessionId: first.sessionId },
run: followup,
events: [
runtimeEvent(followup, {
id: 'followup-complete',
ts: baseTs + 11,
status: 'completed',
}),
],
},
{
operator: { operatorId: 'research', sessionId: first.sessionId },
run: first,
events: [
runtimeEvent(first, {
id: 'initial-complete',
ts: baseTs + 1,
status: 'completed',
}),
],
},
],
});
const state = replayAgentGraphRecords(records);
assert.deepEqual(Object.keys(state.operators.research?.activations ?? {}), ['run-1', 'run-2']);
assert.equal(state.operators.research?.currentActivationId, 'run-2');
});
test('reads only session-inline activations while legacy child runs remain compatible', async () => {
const inline = runHeader({
sessionId: 'child-a',
runId: 'inline-run',
turnId: 'inline-turn',
status: 'completed',
createdAt: baseTs,
});
const legacy = {
...runHeader({
sessionId: 'child-a',
runId: 'legacy-child-run',
turnId: 'legacy-turn',
status: 'completed',
createdAt: baseTs + 1,
}),
parentRunId: 'legacy-parent-run',
};
const reads: string[] = [];
const projection = await readCommittedAgentGraphProjection({
graphId: 'graph-inline-only',
operators: [{ operatorId: 'research', sessionId: inline.sessionId }],
runStore: {
async listSessionRuns() {
return [legacy, inline];
},
},
runtimeEventStore: {
async readImmutableRuntimeEvents(_sessionId, runId) {
reads.push(runId);
return [
runtimeEvent(inline, {
id: 'inline-complete',
ts: baseTs + 2,
status: 'completed',
}),
];
},
},
});
assert.deepEqual(reads, ['inline-run']);
assert.deepEqual(Object.keys(projection.state.operators.research?.activations ?? {}), [
'inline-run',
]);
assert.throws(
() =>
projectAgentGraphRecords({
graphId: 'graph-reject-legacy',
streams: [
{
operator: { operatorId: 'research', sessionId: legacy.sessionId },
run: legacy,
events: [],
},
],
}),
/must be a session-inline AgentRun/,
);
});
test('fails closed on ambiguous authority or impossible replay order', async () => {
const run = runHeader({
sessionId: 'child-a',
runId: 'run-a',
turnId: 'turn-a',
status: 'completed',
createdAt: baseTs,
});
const runtimeEventStore: Pick<RuntimeEventStore, 'readImmutableRuntimeEvents'> = {};
await assert.rejects(
readCommittedAgentGraphProjection({
graphId: 'graph-no-immutable-reader',
operators: [{ operatorId: 'research', sessionId: run.sessionId }],
runStore: {
async listSessionRuns() {
return [run];
},
},
runtimeEventStore,
}),
/requires immutable RuntimeEvent reads/,
);
const { records } = projectAgentGraphRecords({
graphId: 'graph-terminal',
streams: [
{
operator: { operatorId: 'research', sessionId: run.sessionId },
run,
events: [
runtimeEvent(run, { id: 'terminal', ts: baseTs + 1, status: 'completed' }),
runtimeEvent(run, {
id: 'after-terminal',
ts: baseTs + 2,
role: 'model',
author: 'agent',
content: { kind: 'text', text: 'impossible' },
}),
],
},
],
});
assert.throws(() => replayAgentGraphRecords(records), /appears after terminal record/);
});
test('preserves graph identity when no committed operator facts exist yet', async () => {
const projection = await readCommittedAgentGraphProjection({
graphId: 'graph-empty',
operators: [],
runStore: {
async listSessionRuns() {
return [];
},
},
runtimeEventStore: {
async readImmutableRuntimeEvents() {
return [];
},
},
});
assert.deepEqual(projection.state, {
graphId: 'graph-empty',
appliedRecordIds: [],
operators: {},
});
});
});
function runHeader(input: {
sessionId: string;
runId: string;
turnId: string;
status: AgentRunHeader['status'];
createdAt: number;
}): AgentRunHeader {
return {
...input,
invocationId: `invocation-${input.runId}`,
backendKind: 'ai-sdk',
llmConnectionSlug: 'deepseek',
modelId: 'deepseek-chat',
cwd: '/workspace',
permissionMode: 'explore',
updatedAt: input.createdAt + 1,
...(input.status === 'completed' || input.status === 'failed' || input.status === 'cancelled'
? { completedAt: input.createdAt + 1 }
: {}),
};
}
function runtimeEvent(
run: AgentRunHeader,
overrides: Partial<RuntimeEvent> & Pick<RuntimeEvent, 'id' | 'ts'>,
): RuntimeEvent {
return {
invocationId: run.invocationId ?? `invocation-${run.runId}`,
runId: run.runId,
sessionId: run.sessionId,
turnId: run.turnId,
partial: false,
role: 'system',
author: 'system',
...overrides,
};
}