blob: 7ebb1e5d68a079d1d5205cbd8685c5beca6da93f [file]
import assert from 'node:assert/strict';
import { describe, test } from 'node:test';
import type { AgentRunHeader, RuntimeEvent } from '@maka/core';
import { projectAgentGraphRecords } from '../stream-graph-projection.js';
import {
AGENT_GRAPH_TRACE_SCHEMA_VERSION,
buildAgentGraphTraceSnapshot,
type AgentGraphTraceTopology,
} from '../stream-graph-trace.js';
const baseTs = 1_800_000_000_000;
describe('stream graph trace topology', () => {
test('materializes deterministic direct-edge routes without putting the supervisor in the path', () => {
const research = runHeader('research', baseTs);
const verify = runHeader('verify', baseTs + 1);
const synthesize = runHeader('synthesize', baseTs + 2);
const audit = runHeader('audit', baseTs + 3);
const projection = projectAgentGraphRecords({
graphId: 'graph-trace',
streams: [
stream(research, 'research', [
runtimeEvent(research, 'research-message', baseTs + 10, 'private-research-payload'),
]),
stream(verify, 'verify', [
runtimeEvent(verify, 'verify-message', baseTs + 5, 'private-verification-payload'),
]),
stream(synthesize, 'synthesize', [
runtimeEvent(synthesize, 'synthesis-message', baseTs + 20, 'private-synthesis-payload'),
]),
],
});
const topology: AgentGraphTraceTopology = {
graphId: 'graph-trace',
operators: [
binding(audit, 'audit'),
binding(synthesize, 'synthesize'),
binding(verify, 'verify'),
binding(research, 'research'),
],
edges: [
{
edgeId: 'synthesis-to-audit',
fromOperatorId: 'synthesize',
toOperatorId: 'audit',
},
{
edgeId: 'verify-to-synthesis',
fromOperatorId: 'verify',
toOperatorId: 'synthesize',
},
{
edgeId: 'research-to-synthesis',
fromOperatorId: 'research',
toOperatorId: 'synthesize',
},
],
};
const snapshot = buildAgentGraphTraceSnapshot({
topology,
records: projection.records,
});
assert.equal(snapshot.schemaVersion, AGENT_GRAPH_TRACE_SCHEMA_VERSION);
assert.deepEqual(snapshot.topologicalOrder, ['research', 'verify', 'synthesize', 'audit']);
assert.deepEqual(snapshot.rootOperatorIds, ['research', 'verify']);
assert.deepEqual(snapshot.sinkOperatorIds, ['audit']);
assert.deepEqual(
snapshot.recordIds,
projection.records.map((record) => record.recordId),
);
assert.deepEqual(
snapshot.routes.map((route) => [
route.sourceOperatorId,
route.targetOperatorId,
route.sourceRecordId,
]),
[
['verify', 'synthesize', projection.records[0]!.recordId],
['research', 'synthesize', projection.records[1]!.recordId],
['synthesize', 'audit', projection.records[2]!.recordId],
],
'records travel across direct edges only; trace routing does not recursively invent work',
);
assert.deepEqual(snapshot.operators.synthesize?.upstreamOperatorIds, ['research', 'verify']);
assert.equal(snapshot.operators.synthesize?.receivedRouteIds.length, 2);
assert.equal(snapshot.operators.audit?.receivedRouteIds.length, 1);
assert.equal(snapshot.operators.audit?.runtimeState, undefined);
assert.equal(
snapshot.operators.verify?.runtimeState?.activations[verify.runId]?.recordCount,
1,
);
assert.deepEqual(
projection.supervisorMetaStream.map((record) => record.recordId),
projection.records.map((record) => record.recordId),
'the always-on supervisor continues to observe source records independently',
);
assert.doesNotMatch(
JSON.stringify(snapshot),
/private-(research|verification|synthesis)-payload/,
);
});
test('is deterministic and idempotent for reordered duplicate observations', () => {
const source = runHeader('source', baseTs);
const target = runHeader('target', baseTs + 1);
const projection = projectAgentGraphRecords({
graphId: 'graph-replay',
streams: [
stream(source, 'source', [
runtimeEvent(source, 'first', baseTs + 1, 'first'),
runtimeEvent(source, 'second', baseTs + 2, 'second'),
]),
],
});
const topology: AgentGraphTraceTopology = {
graphId: 'graph-replay',
operators: [binding(target, 'target'), binding(source, 'source')],
edges: [
{
edgeId: 'source-to-target',
fromOperatorId: 'source',
toOperatorId: 'target',
},
],
};
const canonical = buildAgentGraphTraceSnapshot({
topology,
records: projection.records,
});
const replayed = buildAgentGraphTraceSnapshot({
topology: {
...topology,
operators: [...topology.operators].reverse(),
edges: [...topology.edges].reverse(),
},
records: [
projection.records[1]!,
projection.records[0]!,
projection.records[1]!,
projection.records[0]!,
],
});
assert.deepEqual(replayed, canonical);
assert.equal(new Set(canonical.routes.map((route) => route.routeId)).size, 2);
});
test('fingerprints only declared topology fields in raw identity order', () => {
const precomposed = runHeader('unicode-precomposed', baseTs);
const decomposed = runHeader('unicode-decomposed', baseTs + 1);
const target = runHeader('unicode-target', baseTs + 2);
const precomposedId = '\u00e9';
const decomposedId = 'e\u0301';
assert.equal(precomposedId.localeCompare(decomposedId), 0);
const canonical = buildAgentGraphTraceSnapshot({
topology: {
graphId: 'graph-canonical-fingerprint',
operators: [
binding(precomposed, precomposedId),
binding(decomposed, decomposedId),
binding(target, 'target'),
],
edges: [
{
edgeId: 'precomposed-target',
fromOperatorId: precomposedId,
toOperatorId: 'target',
},
{
edgeId: 'decomposed-target',
fromOperatorId: decomposedId,
toOperatorId: 'target',
},
],
},
records: [],
});
const noisyOperators = [
{ ...binding(target, 'target'), displayName: 'ignored target' },
{
...binding(decomposed, decomposedId),
displayName: 'ignored decomposed',
},
{
...binding(precomposed, precomposedId),
displayName: 'ignored precomposed',
},
];
const noisyEdges = [
{
edgeId: 'decomposed-target',
fromOperatorId: decomposedId,
toOperatorId: 'target',
debugColor: 'blue',
},
{
edgeId: 'precomposed-target',
fromOperatorId: precomposedId,
toOperatorId: 'target',
debugColor: 'red',
},
];
const reorderedWithExtras = buildAgentGraphTraceSnapshot({
topology: {
graphId: 'graph-canonical-fingerprint',
operators: noisyOperators,
edges: noisyEdges,
},
records: [],
});
assert.deepEqual(reorderedWithExtras, canonical);
assert.equal(reorderedWithExtras.topologyFingerprint, canonical.topologyFingerprint);
});
test('keeps existing route identities stable as later observations arrive', () => {
const source = runHeader('source', baseTs);
const target = runHeader('target', baseTs + 1);
const initialProjection = projectAgentGraphRecords({
graphId: 'graph-incremental',
streams: [stream(source, 'source', [runtimeEvent(source, 'first', baseTs + 10, 'first')])],
});
const expandedProjection = projectAgentGraphRecords({
graphId: 'graph-incremental',
streams: [
stream(source, 'source', [
runtimeEvent(source, 'first', baseTs + 10, 'first'),
runtimeEvent(source, 'second', baseTs + 20, 'second'),
]),
],
});
const topology: AgentGraphTraceTopology = {
graphId: 'graph-incremental',
operators: [binding(source, 'source'), binding(target, 'target')],
edges: [
{
edgeId: 'source-to-target',
fromOperatorId: 'source',
toOperatorId: 'target',
},
],
};
const initial = buildAgentGraphTraceSnapshot({
topology,
records: initialProjection.records,
});
const expanded = buildAgentGraphTraceSnapshot({
topology,
records: expandedProjection.records,
});
assert.deepEqual(expanded.routes[0], initial.routes[0]);
assert.equal(expanded.routes.length, 2);
});
test('retains an observable topology before any runtime facts arrive', () => {
const source = runHeader('source', baseTs);
const target = runHeader('target', baseTs + 1);
const snapshot = buildAgentGraphTraceSnapshot({
topology: {
graphId: 'graph-empty-trace',
operators: [binding(target, 'target'), binding(source, 'source')],
edges: [
{
edgeId: 'source-to-target',
fromOperatorId: 'source',
toOperatorId: 'target',
},
],
},
records: [],
});
assert.deepEqual(snapshot.topologicalOrder, ['source', 'target']);
assert.deepEqual(snapshot.rootOperatorIds, ['source']);
assert.deepEqual(snapshot.sinkOperatorIds, ['target']);
assert.deepEqual(snapshot.recordIds, []);
assert.deepEqual(snapshot.routes, []);
assert.equal(snapshot.operators.source?.runtimeState, undefined);
assert.equal(snapshot.operators.target?.runtimeState, undefined);
});
test('materializes reserved JavaScript property names as own snapshot keys', () => {
const source = {
...runHeader('reserved-source', baseTs),
runId: 'constructor',
invocationId: 'reserved-invocation',
};
const target = runHeader('reserved-target', baseTs + 1);
const projection = projectAgentGraphRecords({
graphId: 'graph-reserved-keys',
streams: [
stream(source, '__proto__', [
runtimeEvent(source, 'reserved-record', baseTs + 1, 'reserved'),
]),
],
});
const snapshot = buildAgentGraphTraceSnapshot({
topology: {
graphId: 'graph-reserved-keys',
operators: [binding(source, '__proto__'), binding(target, 'toString')],
edges: [
{
edgeId: '__proto__',
fromOperatorId: '__proto__',
toOperatorId: 'toString',
},
],
},
records: projection.records,
});
assert.equal(Object.hasOwn(snapshot.operators, '__proto__'), true);
assert.equal(Object.hasOwn(snapshot.operators, 'toString'), true);
assert.equal(Object.hasOwn(snapshot.edges, '__proto__'), true);
assert.equal(snapshot.edges['__proto__']?.routeIds.length, 1);
assert.equal(
Object.hasOwn(
snapshot.operators['__proto__']?.runtimeState?.activations ?? {},
'constructor',
),
true,
);
});
test('binds route identity to immutable edge endpoints', () => {
const source = runHeader('route-source', baseTs);
const targetA = runHeader('route-target-a', baseTs + 1);
const targetB = runHeader('route-target-b', baseTs + 2);
const projection = projectAgentGraphRecords({
graphId: 'graph-edge-rebinding',
streams: [
stream(source, 'source', [runtimeEvent(source, 'source-record', baseTs + 1, 'source')]),
],
});
const operators = [
binding(source, 'source'),
binding(targetA, 'target-a'),
binding(targetB, 'target-b'),
];
const first = buildAgentGraphTraceSnapshot({
topology: {
graphId: 'graph-edge-rebinding',
operators,
edges: [
{
edgeId: 'edge',
fromOperatorId: 'source',
toOperatorId: 'target-a',
},
],
},
records: projection.records,
});
const rebound = buildAgentGraphTraceSnapshot({
topology: {
graphId: 'graph-edge-rebinding',
operators,
edges: [
{
edgeId: 'edge',
fromOperatorId: 'source',
toOperatorId: 'target-b',
},
],
},
records: projection.records,
});
assert.notEqual(first.routes[0]?.routeId, rebound.routes[0]?.routeId);
assert.equal(first.routes[0]?.targetOperatorId, 'target-a');
assert.equal(rebound.routes[0]?.targetOperatorId, 'target-b');
});
test('fails closed on invalid topology and record ownership', () => {
const one = runHeader('one', baseTs);
const two = runHeader('two', baseTs + 1);
const three = runHeader('three', baseTs + 2);
const projection = projectAgentGraphRecords({
graphId: 'graph-invalid',
streams: [stream(one, 'one', [runtimeEvent(one, 'one-message', baseTs + 1, 'one')])],
});
assert.throws(
() =>
buildAgentGraphTraceSnapshot({
topology: {
graphId: 'graph-invalid',
operators: [binding(one, 'one'), binding(two, 'two'), binding(three, 'three')],
edges: [
{ edgeId: 'one-two', fromOperatorId: 'one', toOperatorId: 'two' },
{
edgeId: 'two-three',
fromOperatorId: 'two',
toOperatorId: 'three',
},
{
edgeId: 'three-one',
fromOperatorId: 'three',
toOperatorId: 'one',
},
],
},
records: projection.records,
}),
/contains a cycle involving: one, three, two/,
);
assert.throws(
() =>
buildAgentGraphTraceSnapshot({
topology: {
graphId: 'graph-invalid',
operators: [binding(one, 'one'), binding(two, 'two')],
edges: [
{
edgeId: 'one-missing',
fromOperatorId: 'one',
toOperatorId: 'missing',
},
],
},
records: projection.records,
}),
/unknown target missing/,
);
assert.throws(
() =>
buildAgentGraphTraceSnapshot({
topology: {
graphId: 'graph-invalid',
operators: [{ operatorId: 'one', sessionId: 'different-session' }, binding(two, 'two')],
edges: [{ edgeId: 'one-two', fromOperatorId: 'one', toOperatorId: 'two' }],
},
records: projection.records,
}),
/Topology operator one is bound to different-session/,
);
assert.throws(
() =>
buildAgentGraphTraceSnapshot({
topology: {
graphId: 'different-graph',
operators: [binding(one, 'one'), binding(two, 'two')],
edges: [{ edgeId: 'one-two', fromOperatorId: 'one', toOperatorId: 'two' }],
},
records: projection.records,
}),
/cannot observe records from graph graph-invalid/,
);
assert.throws(
() =>
buildAgentGraphTraceSnapshot({
topology: {
graphId: 'graph-invalid',
operators: [binding(two, 'two')],
edges: [],
},
records: projection.records,
}),
/unknown topology operator one/,
);
});
});
function runHeader(name: string, createdAt: number): AgentRunHeader {
return {
sessionId: `session-${name}`,
runId: `run-${name}`,
turnId: `turn-${name}`,
invocationId: `invocation-${name}`,
backendKind: 'ai-sdk',
llmConnectionSlug: 'deepseek',
modelId: 'deepseek-chat',
cwd: '/workspace',
permissionMode: 'explore',
status: 'running',
createdAt,
updatedAt: createdAt + 1,
};
}
function binding(run: AgentRunHeader, operatorId: string) {
return { operatorId, sessionId: run.sessionId };
}
function stream(run: AgentRunHeader, operatorId: string, events: readonly RuntimeEvent[]) {
return {
operator: binding(run, operatorId),
run,
events,
};
}
function runtimeEvent(run: AgentRunHeader, id: string, ts: number, text: string): RuntimeEvent {
return {
id,
invocationId: run.invocationId ?? `invocation-${run.runId}`,
runId: run.runId,
sessionId: run.sessionId,
turnId: run.turnId,
ts,
partial: false,
role: 'model',
author: 'agent',
content: { kind: 'text', text },
};
}