Files

2151 lines
77 KiB
JavaScript
Raw Permalink Normal View History

import test from 'node:test';
import assert from 'node:assert/strict';
import {
mkdirSync,
mkdtempSync,
readFileSync,
renameSync,
statSync,
symlinkSync,
unlinkSync,
utimesSync,
writeFileSync,
} from 'node:fs';
import { tmpdir } from 'node:os';
import { dirname, join } from 'node:path';
import { DatabaseSync } from 'node:sqlite';
import {
createPiProvider,
piSessionId,
resolveDefaultPiRoot,
} from '../packages/core/src/providers/pi.ts';
import { createProviderRegistry } from '../packages/core/src/providers/registry.ts';
import { assembleSessionDetail } from '../packages/core/src/session-detail.ts';
import { persist } from '../packages/core/src/persist.ts';
import { createQueryApi } from '../packages/core/src/query.ts';
const SCHEMA = readFileSync(new URL('../packages/core/src/schema.sql', import.meta.url), 'utf8');
const FIXTURES = new URL('./fixtures/pi/', import.meta.url);
function drain(generator) {
const values = [];
let step = generator.next();
while (!step.done) {
values.push(step.value);
step = generator.next();
}
return { values, cursor: step.value };
}
function fixture(name) {
return readFileSync(new URL(name, FIXTURES), 'utf8');
}
function writeSession(content, { root, relativePath = 'project/session.jsonl' } = {}) {
const sessionRoot = root ?? mkdtempSync(join(tmpdir(), 'obelisk-pi-'));
const path = join(sessionRoot, relativePath);
mkdirSync(dirname(path), { recursive: true });
writeFileSync(path, content);
return { root: sessionRoot, path };
}
function parseOnly(root) {
const provider = createPiProvider({ rootDir: root });
const units = provider.discover({ lastCursor: () => null });
assert.equal(units.length, 1);
return { provider, unit: units[0], ...drain(provider.parse(units[0], null)) };
}
function jsonl(records, trailingNewline = true) {
return records.map(record => JSON.stringify(record)).join('\n') + (trailingNewline ? '\n' : '');
}
function header(overrides = {}) {
return {
type: 'session',
version: 3,
id: 'pi-test-session',
timestamp: '2026-08-02T10:00:00.000Z',
cwd: '/tmp/pi-test-project',
...overrides,
};
}
function userEntry(id, parentId, text, timestamp = '2026-08-02T10:00:01.000Z') {
return {
type: 'message',
id,
parentId,
timestamp,
message: { role: 'user', content: text, timestamp: Date.parse(timestamp) },
};
}
test('Pi root resolution follows official precedence and rejects ambiguous relative roots', () => {
const home = mkdtempSync(join(tmpdir(), 'obelisk-pi-home-'));
const projectCwd = join(home, 'project');
mkdirSync(projectCwd, { recursive: true });
assert.deepEqual(resolveDefaultPiRoot({ env: {}, homeDir: home, cwd: projectCwd }), {
root: join(home, '.pi', 'agent', 'sessions'),
requiresExplicitRoot: false,
});
const agentDir = join(home, 'custom-agent');
const absoluteSessions = join(home, 'absolute-sessions');
mkdirSync(agentDir, { recursive: true });
writeFileSync(join(agentDir, 'settings.json'), JSON.stringify({ sessionDir: absoluteSessions }));
assert.deepEqual(resolveDefaultPiRoot({
env: { PI_CODING_AGENT_DIR: agentDir },
homeDir: home,
cwd: projectCwd,
}), {
root: absoluteSessions,
requiresExplicitRoot: false,
});
writeFileSync(join(agentDir, 'settings.json'), JSON.stringify({ sessionDir: '.pi/relative-sessions' }));
const relativeSetting = resolveDefaultPiRoot({
env: { PI_CODING_AGENT_DIR: agentDir },
homeDir: home,
cwd: projectCwd,
});
assert.equal(relativeSetting.requiresExplicitRoot, true);
assert.equal(relativeSetting.root, join(agentDir, 'sessions'));
assert.doesNotMatch(relativeSetting.root, /relative-sessions/);
const relativeEnv = resolveDefaultPiRoot({
env: { PI_CODING_AGENT_SESSION_DIR: 'cwd-relative-sessions' },
homeDir: home,
cwd: projectCwd,
});
assert.equal(relativeEnv.requiresExplicitRoot, true);
assert.equal(relativeEnv.root, join(home, '.pi', 'agent', 'sessions'));
const envSessions = join(home, 'env-sessions');
assert.deepEqual(resolveDefaultPiRoot({
env: {
PI_CODING_AGENT_SESSION_DIR: envSessions,
PI_CODING_AGENT_DIR: agentDir,
},
homeDir: home,
cwd: projectCwd,
}), {
root: envSessions,
requiresExplicitRoot: false,
});
const originalSessionDir = process.env.PI_CODING_AGENT_SESSION_DIR;
process.env.PI_CODING_AGENT_SESSION_DIR = 'cwd-relative-sessions';
try {
const unresolved = createPiProvider();
assert.equal(unresolved.descriptor.requiresExplicitRoot, true);
assert.deepEqual(unresolved.watchRoots(unresolved.descriptor.defaultRoot), []);
assert.deepEqual(unresolved.discover({ lastCursor: () => null }), []);
const explicitlySelected = createPiProvider({
rootDir: join(process.cwd(), '.pi', 'agent', 'sessions'),
});
assert.equal(explicitlySelected.rootResolution.requiresExplicitRoot, false);
} finally {
if (originalSessionDir === undefined) delete process.env.PI_CODING_AGENT_SESSION_DIR;
else process.env.PI_CODING_AGENT_SESSION_DIR = originalSessionDir;
}
});
test('Pi project settings override global sessionDir using the official launch cwd', () => {
const home = mkdtempSync(join(tmpdir(), 'obelisk-pi-project-settings-'));
const agentDir = join(home, 'agent');
const projectCwd = join(home, 'project');
const globalSessions = join(home, 'global-sessions');
const projectSessions = join(home, 'project-sessions');
mkdirSync(join(projectCwd, '.pi'), { recursive: true });
mkdirSync(agentDir, { recursive: true });
writeFileSync(join(agentDir, 'settings.json'), JSON.stringify({ sessionDir: globalSessions }));
writeFileSync(join(projectCwd, '.pi', 'settings.json'), JSON.stringify({
sessionDir: projectSessions,
}));
assert.deepEqual(resolveDefaultPiRoot({
env: { PI_CODING_AGENT_DIR: agentDir },
homeDir: home,
cwd: projectCwd,
}), {
root: projectSessions,
requiresExplicitRoot: false,
});
writeFileSync(join(projectCwd, '.pi', 'settings.json'), JSON.stringify({
sessionDir: '.pi/project-sessions',
}));
assert.deepEqual(resolveDefaultPiRoot({
env: { PI_CODING_AGENT_DIR: agentDir },
homeDir: home,
cwd: projectCwd,
}), {
root: join(projectCwd, '.pi', 'project-sessions'),
requiresExplicitRoot: false,
});
});
test('malformed Pi settings fall back to the remaining official settings scope', () => {
const home = mkdtempSync(join(tmpdir(), 'obelisk-pi-malformed-settings-'));
const agentDir = join(home, 'agent');
const projectCwd = join(home, 'project');
const projectSettingsDir = join(projectCwd, '.pi');
const globalSessions = join(home, 'global-sessions');
const projectSessions = join(home, 'project-sessions');
mkdirSync(agentDir, { recursive: true });
mkdirSync(projectSettingsDir, { recursive: true });
writeFileSync(join(agentDir, 'settings.json'), '{broken');
assert.deepEqual(resolveDefaultPiRoot({
env: { PI_CODING_AGENT_DIR: agentDir },
homeDir: home,
cwd: projectCwd,
}), {
root: join(agentDir, 'sessions'),
requiresExplicitRoot: false,
});
writeFileSync(join(agentDir, 'settings.json'), JSON.stringify({
sessionDir: globalSessions,
}));
writeFileSync(join(projectSettingsDir, 'settings.json'), '{broken');
assert.deepEqual(resolveDefaultPiRoot({
env: { PI_CODING_AGENT_DIR: agentDir },
homeDir: home,
cwd: projectCwd,
}), {
root: globalSessions,
requiresExplicitRoot: false,
});
writeFileSync(join(agentDir, 'settings.json'), '{broken');
writeFileSync(join(projectSettingsDir, 'settings.json'), JSON.stringify({
sessionDir: projectSessions,
}));
assert.deepEqual(resolveDefaultPiRoot({
env: { PI_CODING_AGENT_DIR: agentDir },
homeDir: home,
cwd: projectCwd,
}), {
root: projectSessions,
requiresExplicitRoot: false,
});
});
test('non-string Pi sessionDir values never select the default corpus', () => {
const home = mkdtempSync(join(tmpdir(), 'obelisk-pi-invalid-settings-'));
const agentDir = join(home, 'agent');
const projectCwd = join(home, 'project');
mkdirSync(join(projectCwd, '.pi'), { recursive: true });
mkdirSync(agentDir, { recursive: true });
writeFileSync(join(projectCwd, '.pi', 'settings.json'), '{}');
for (const sessionDir of [false, 0]) {
writeFileSync(join(agentDir, 'settings.json'), JSON.stringify({ sessionDir }));
const resolved = resolveDefaultPiRoot({
env: { PI_CODING_AGENT_DIR: agentDir },
homeDir: home,
cwd: projectCwd,
});
assert.equal(resolved.requiresExplicitRoot, true);
assert.match(resolved.reason, /sessionDir must be a string/);
}
});
test('Pi discovery never certifies an invalid session root as empty', () => {
const parent = mkdtempSync(join(tmpdir(), 'obelisk-pi-invalid-root-'));
const root = join(parent, 'sessions');
writeFileSync(root, 'not a directory');
let status = 'unknown';
const units = createPiProvider({ rootDir: root }).discover({
lastCursor: () => null,
reportCompleteInventory: () => { status = 'complete'; },
reportIncompleteInventory: () => { status = 'incomplete'; },
});
assert.deepEqual(units, []);
assert.equal(status, 'incomplete');
});
test('Pi tool fixture projects canonical messages, usage, file paths, raw lines, and persistence', () => {
const { root, path } = writeSession(fixture('tool-session.jsonl'));
const { provider, unit, values, cursor } = parseOnly(root);
const byKind = kind => values.filter(record => record.kind === kind);
assert.match(cursor, /^\d+(?:\.\d+)?:0:pi-snapshot-v1:/);
assert.deepEqual(values[0], { kind: 'delete-session', sessionId: unit.sessionId });
assert.deepEqual(
byKind('message').map(record => [record.role, record.content_type, record.text]),
[
['user', 'text', 'Read probe.txt and report it'],
['assistant', 'tool_use', null],
['toolResult', 'tool_result', 'real-pi-tool-result\n'],
['assistant', 'text', 'The read tool returned real-pi-tool-result.'],
],
);
assert.equal(byKind('message')[1].input_tokens, 17);
assert.equal(byKind('message')[1].output_tokens, 5);
const toolUseMessage = byKind('message')[1];
assert.deepEqual(byKind('tool_call').map(record => ({
id: record.id,
name: record.name,
file_path: record.file_path,
})), [{
id: `${toolUseMessage.uuid}:tool`,
name: 'read',
file_path: 'probe.txt',
}]);
assert.equal(byKind('tool_result')[0].file_path, 'probe.txt');
assert.equal(byKind('session')[0].title, 'Tool probe');
assert.equal(byKind('session')[0].message_count, 4);
assert.equal(byKind('session')[0].countMode, 'total');
const raw = provider.raw({
source: 'pi',
messageUuid: byKind('message')[1].uuid,
session: { id: byKind('session')[0].id, jsonl_path: path },
agentId: null,
cursor,
});
assert.match(raw.text, /"type":"toolCall"/);
assert.equal(raw.messageText, null);
const direct = assembleSessionDetail(values);
const db = new DatabaseSync(':memory:');
db.exec(SCHEMA);
persist(db, unit, provider.parse(unit, null));
const persisted = assembleSessionDetail({
session: db.prepare('SELECT * FROM sessions').get(),
messages: db.prepare('SELECT * FROM messages ORDER BY timestamp,uuid').all(),
toolCalls: db.prepare('SELECT * FROM tool_calls').all(),
toolResults: db.prepare('SELECT * FROM tool_results').all(),
summaries: db.prepare('SELECT * FROM summaries').all(),
});
assert.deepEqual(persisted, direct);
db.close();
});
test('raw Pi lookup resolves the exact canonical block and rejects replacement provenance', () => {
const longText = 'long-block-'.repeat(1100);
const written = writeSession(jsonl([
header({ id: 'raw-block-session' }),
userEntry('raw-user', null, 'raw prompt'),
{
type: 'message',
id: 'raw-assistant',
parentId: 'raw-user',
timestamp: '2026-08-02T10:00:02.000Z',
message: {
role: 'assistant',
content: [
{ type: 'thinking', thinking: 'exact private thought' },
{ type: 'text', text: longText },
{ type: 'toolCall', id: 'raw-call', name: 'read', arguments: { path: 'raw.ts' } },
],
errorMessage: 'exact synthetic error',
responseModel: 'probe',
timestamp: Date.parse('2026-08-02T10:00:02.000Z'),
},
},
]));
const { provider, values, cursor } = parseOnly(written.root);
const session = values.find(record => record.kind === 'session');
const messages = values.filter(record => (
record.kind === 'message'
&& record.role === 'assistant'
));
const lookup = message => provider.raw({
source: 'pi',
messageUuid: message.uuid,
session: { id: session.id, jsonl_path: written.path },
agentId: null,
cursor,
});
assert.equal(lookup(messages.find(message => message.content_type === 'thinking')).messageText, 'exact private thought');
assert.equal(lookup(messages.find(message => message.content_type === 'text')).messageText, longText);
assert.equal(messages.find(message => message.content_type === 'text').text.length, 10000);
assert.equal(lookup(messages.find(message => message.content_type === 'tool_use')).messageText, null);
assert.equal(lookup(messages.find(message => message.content_type === 'error')).messageText, 'exact synthetic error');
assert.equal(provider.raw({
source: 'pi',
messageUuid: `${session.id}:entry:000002:message:block:9999`,
session: { id: session.id, jsonl_path: written.path },
agentId: null,
cursor,
}), null);
assert.equal(provider.raw({
source: 'pi',
messageUuid: messages[0].uuid,
session: { id: session.id, jsonl_path: written.root },
agentId: null,
cursor,
}), null);
writeFileSync(written.path, jsonl([
header({ id: 'raw-block-session' }),
userEntry('replacement-user', null, 'replacement lure'),
userEntry('replacement-second', 'replacement-user', 'unrelated same-header replacement'),
]));
assert.equal(lookup(messages[0]), null);
});
test('structured tools remain branch-local when Pi reuses a native tool id', () => {
const records = [
header({ id: 'branch-tool-id-reuse' }),
userEntry('root', null, 'shared prompt'),
{
type: 'message',
id: 'active-call',
parentId: 'root',
timestamp: '2026-08-02T10:00:02.000Z',
message: {
role: 'assistant',
content: [{
type: 'toolCall',
id: 'shared-native-id',
name: 'read',
arguments: { path: 'active.ts' },
}],
model: 'probe',
usage: { input: 1, output: 1, cacheRead: 0, cacheWrite: 0 },
timestamp: Date.parse('2026-08-02T10:00:02.000Z'),
},
},
{
type: 'message',
id: 'active-result',
parentId: 'active-call',
timestamp: '2026-08-02T10:00:03.000Z',
message: {
role: 'toolResult',
toolCallId: 'shared-native-id',
toolName: 'read',
content: [{ type: 'text', text: 'ACTIVE RESULT' }],
isError: false,
timestamp: Date.parse('2026-08-02T10:00:03.000Z'),
},
},
{
type: 'message',
id: 'abandoned-call',
parentId: 'root',
timestamp: '2026-08-02T10:00:04.000Z',
message: {
role: 'assistant',
content: [{
type: 'toolCall',
id: 'shared-native-id',
name: 'read',
arguments: { path: 'abandoned.ts' },
}],
model: 'probe',
usage: { input: 1, output: 1, cacheRead: 0, cacheWrite: 0 },
timestamp: Date.parse('2026-08-02T10:00:04.000Z'),
},
},
{
type: 'message',
id: 'abandoned-result',
parentId: 'abandoned-call',
timestamp: '2026-08-02T10:00:05.000Z',
message: {
role: 'toolResult',
toolCallId: 'shared-native-id',
toolName: 'read',
content: [{ type: 'text', text: 'ABANDONED RESULT' }],
isError: true,
timestamp: Date.parse('2026-08-02T10:00:05.000Z'),
},
},
{
type: 'leaf',
id: 'select-active',
parentId: 'abandoned-result',
targetId: 'active-result',
timestamp: '2026-08-02T10:00:06.000Z',
},
];
const { provider, unit, values } = parseOnly(writeSession(jsonl(records)).root);
const activeToolUse = values.find(record => (
record.kind === 'message'
&& record.content_type === 'tool_use'
&& record.visibility === 'visible'
));
const inactiveToolUse = values.find(record => (
record.kind === 'message'
&& record.content_type === 'tool_use'
&& record.visibility === 'inactive'
));
const calls = values.filter(record => record.kind === 'tool_call');
const results = values.filter(record => record.kind === 'tool_result');
const activeCall = calls.find(record => record.file_path === 'active.ts');
const inactiveCall = calls.find(record => record.file_path === 'abandoned.ts');
const activeResult = results.find(record => record.content === 'ACTIVE RESULT');
const inactiveResult = results.find(record => record.content === 'ABANDONED RESULT');
assert.equal(calls.length, 2);
assert.deepEqual(
(({ id, message_uuid, file_path }) => ({ id, message_uuid, file_path }))(activeCall),
{
id: `${activeToolUse.uuid}:tool`,
message_uuid: activeToolUse.uuid,
file_path: 'active.ts',
},
);
assert.deepEqual(
(({ id, message_uuid, file_path }) => ({ id, message_uuid, file_path }))(inactiveCall),
{
id: `${inactiveToolUse.uuid}:tool`,
message_uuid: inactiveToolUse.uuid,
file_path: 'abandoned.ts',
},
);
assert.deepEqual(
(({ tool_use_id, content, file_path, is_error }) => ({
tool_use_id,
content,
file_path,
is_error,
}))(activeResult),
{
tool_use_id: activeCall.id,
content: 'ACTIVE RESULT',
file_path: 'active.ts',
is_error: 0,
},
);
assert.deepEqual(
(({ tool_use_id, content, file_path, is_error }) => ({
tool_use_id,
content,
file_path,
is_error,
}))(inactiveResult),
{
tool_use_id: inactiveCall.id,
content: 'ABANDONED RESULT',
file_path: 'abandoned.ts',
is_error: 1,
},
);
const db = new DatabaseSync(':memory:');
db.exec(SCHEMA);
persist(db, unit, provider.parse(unit, null));
assert.equal(db.prepare('SELECT COUNT(*) AS count FROM tool_calls').get().count, 2);
assert.equal(db.prepare('SELECT COUNT(*) AS count FROM tool_results').get().count, 2);
assert.deepEqual(
db.prepare('SELECT file_path FROM tool_calls ORDER BY file_path').all().map(row => row.file_path),
['abandoned.ts', 'active.ts'],
);
const query = createQueryApi(db);
assert.deepEqual(query.fileHistory('abandoned.ts'), []);
assert.deepEqual(
query.fileHistory('abandoned.ts', { includeInactive: true })
.map(record => [record.toolCall.id, record.visibility]),
[[inactiveCall.id, 'inactive']],
);
assert.deepEqual(query.failures(unit.sessionId), []);
assert.deepEqual(
query.failures({ sessionId: unit.sessionId, includeInactive: true })
.map(record => [record.toolCall.id, record.visibility]),
[[inactiveCall.id, 'inactive']],
);
db.close();
});
test('an empty retainedTail checkpoint does not link a later result to discarded tool scope', () => {
const records = [
header({ id: 'checkpoint-tool-scope' }),
{
type: 'message',
id: 'discarded-call',
parentId: null,
timestamp: '2026-08-02T10:00:01.000Z',
message: {
role: 'assistant',
content: [{
type: 'toolCall',
id: 'discarded-native-id',
name: 'read',
arguments: { path: 'discarded.ts' },
}],
model: 'probe',
usage: { input: 1, output: 1, cacheRead: 0, cacheWrite: 0 },
timestamp: Date.parse('2026-08-02T10:00:01.000Z'),
},
},
{
type: 'compaction',
id: 'empty-checkpoint',
parentId: 'discarded-call',
timestamp: '2026-08-02T10:00:02.000Z',
summary: 'No retained tool context.',
firstKeptEntryId: 'discarded-call',
tokensBefore: 10,
retainedTail: [],
},
{
type: 'message',
id: 'unresolved-result',
parentId: 'empty-checkpoint',
timestamp: '2026-08-02T10:00:03.000Z',
message: {
role: 'toolResult',
toolCallId: 'discarded-native-id',
toolName: 'read',
content: [{ type: 'text', text: 'UNRESOLVED RESULT' }],
isError: false,
timestamp: Date.parse('2026-08-02T10:00:03.000Z'),
},
},
];
const { values } = parseOnly(writeSession(jsonl(records)).root);
const discardedCalls = values.filter(record => record.kind === 'tool_call');
assert.equal(discardedCalls.length, 1);
const discardedCall = discardedCalls[0];
assert.equal(
values.find(record => (
record.kind === 'message'
&& record.uuid === discardedCall.message_uuid
)).visibility,
'inactive',
);
assert.equal(values.filter(record => record.kind === 'tool_result').length, 0);
assert.equal(
values.find(record => record.kind === 'message' && record.text === 'UNRESOLVED RESULT').visibility,
'visible',
);
});
test('a legacy compaction does not link a visible result to an inactive tool call', () => {
const records = [
header({ id: 'legacy-tool-scope' }),
{
type: 'message',
id: 'discarded-call',
parentId: null,
timestamp: '2026-08-02T10:00:01.000Z',
message: {
role: 'assistant',
content: [{
type: 'toolCall',
id: 'discarded-native-id',
name: 'read',
arguments: { path: '/secret/inactive' },
}],
model: 'probe',
usage: { input: 1, output: 1, cacheRead: 0, cacheWrite: 0 },
timestamp: Date.parse('2026-08-02T10:00:01.000Z'),
},
},
userEntry('kept-user', 'discarded-call', 'kept context', '2026-08-02T10:00:02.000Z'),
{
type: 'compaction',
id: 'legacy-compaction',
parentId: 'kept-user',
timestamp: '2026-08-02T10:00:03.000Z',
summary: 'Discard the tool call.',
firstKeptEntryId: 'kept-user',
tokensBefore: 10,
},
{
type: 'message',
id: 'standalone-result',
parentId: 'legacy-compaction',
timestamp: '2026-08-02T10:00:04.000Z',
message: {
role: 'toolResult',
toolCallId: 'discarded-native-id',
toolName: 'read',
content: [{ type: 'text', text: 'VISIBLE STANDALONE FAILURE' }],
isError: true,
timestamp: Date.parse('2026-08-02T10:00:04.000Z'),
},
},
];
const { provider, unit, values } = parseOnly(writeSession(jsonl(records)).root);
const discardedCall = values.find(record => record.kind === 'tool_call');
const callMessage = values.find(record => (
record.kind === 'message'
&& record.uuid === discardedCall.message_uuid
));
const resultMessage = values.find(record => (
record.kind === 'message'
&& record.text === 'VISIBLE STANDALONE FAILURE'
));
assert.equal(callMessage.visibility, 'inactive');
assert.equal(resultMessage.visibility, 'visible');
assert.equal(values.filter(record => record.kind === 'tool_result').length, 0);
const db = new DatabaseSync(':memory:');
db.exec(SCHEMA);
persist(db, unit, provider.parse(unit, null));
const query = createQueryApi(db);
assert.deepEqual(query.failures(unit.sessionId), []);
assert.deepEqual(query.fileHistory('/secret/inactive'), []);
assert.deepEqual(
query.fileHistory('/secret/inactive', { includeInactive: true })
.map(record => record.visibility),
['inactive'],
);
db.close();
});
test('real-model-derived fixture preserves error, image, cache, and reasoning semantics', () => {
const { root } = writeSession(fixture('real-model-session.jsonl'));
const { values } = parseOnly(root);
const messages = values.filter(record => record.kind === 'message');
assert.deepEqual(messages.map(record => [record.role, record.content_type, record.text]), [
['user', 'text', 'Reply with the probe marker'],
['user', 'image', '[image image/png; base64 chars=8]'],
['assistant', 'error', '404 not found'],
['user', 'text', 'Try the responses protocol'],
['assistant', 'text', 'REAL_PI_PROBE_OK'],
]);
const success = messages.at(-1);
assert.equal(success.input_tokens, 4818);
assert.equal(success.output_tokens, 20);
assert.equal(messages.some(record => record.content_type === 'thinking'), false);
assert.equal(values.find(record => record.kind === 'session').message_count, 5);
});
test('durable leaf and null leaf control active visibility without deleting physical evidence', () => {
const source = writeSession(fixture('harness-source.jsonl'));
const parsed = parseOnly(source.root);
const parsedSource = parsed.values;
const sourceMessages = parsedSource.filter(record => record.kind === 'message');
assert.equal(sourceMessages.length, 6, 'complete retainedTail must not be projected twice');
assert.deepEqual(
sourceMessages.filter(record => record.visibility === 'visible').map(record => record.text),
['Harness first user turn'],
);
assert.equal(
parsedSource.find(record => record.kind === 'session').message_count,
1,
);
assert.equal(assembleSessionDetail(parsedSource).messages.length, 1);
const db = new DatabaseSync(':memory:');
db.exec(SCHEMA);
persist(db, parsed.unit, parsed.provider.parse(parsed.unit, null));
assert.equal(createQueryApi(db).sessions({ source: 'pi' })[0].message_count, 1);
db.close();
const nullLeaf = writeSession(fixture('harness-null-leaf.jsonl'));
const parsedNull = parseOnly(nullLeaf.root).values;
const physical = parsedNull.find(record => record.kind === 'message');
assert.equal(physical.visibility, 'inactive');
assert.equal(parsedNull.find(record => record.kind === 'session').message_count, 0);
assert.equal(
parsedNull.find(record => record.kind === 'session').title,
'This physical message is no longer active',
);
assert.deepEqual(assembleSessionDetail(parsedNull).messages, []);
});
test('an empty latest session name clears it and falls back to the physical first user', () => {
const records = [
header({ id: 'physical-session-title' }),
{
...userEntry('physical-user', null, ''),
message: {
role: 'user',
content: [
{ type: 'image', mimeType: 'image/png', data: 'AAAA' },
{ type: 'text', text: 'physical' },
{ type: 'text', text: 'fallback' },
],
timestamp: Date.parse('2026-08-02T10:00:01.000Z'),
},
},
{
type: 'session_info',
id: 'named',
parentId: 'physical-user',
timestamp: '2026-08-02T10:00:02.000Z',
name: ' Named session ',
},
{
type: 'session_info',
id: 'blank-name',
parentId: 'named',
timestamp: '2026-08-02T10:00:03.000Z',
name: ' ',
},
{
type: 'leaf',
id: 'null-leaf',
parentId: 'blank-name',
targetId: null,
timestamp: '2026-08-02T10:00:04.000Z',
},
];
const { values } = parseOnly(writeSession(jsonl(records)).root);
assert.equal(values.find(record => record.kind === 'session').title, 'physical fallback');
assert.equal(
values.find(record => record.kind === 'message' && record.text === 'physical').visibility,
'inactive',
);
});
test('retainedTail replaces complete ancestors when its compaction is on the active branch', () => {
const records = fixture('harness-source.jsonl')
.trim()
.split('\n')
.map(line => JSON.parse(line))
.filter(record => record.type !== 'leaf');
const { provider, unit, values } = parseOnly(writeSession(jsonl(records)).root);
const messages = values.filter(record => record.kind === 'message');
const visibleText = messages
.filter(record => record.visibility === 'visible')
.map(record => record.text);
assert.equal(messages.length, 8, 'physical ancestors and active retained context are both preserved');
assert.deepEqual(visibleText, [
'Harness retained user turn',
'Harness retained assistant turn',
'Harness post-compaction user turn',
'Harness post-compaction assistant turn',
]);
assert.equal(
messages.find(record => record.text === 'Harness first user turn').visibility,
'inactive',
);
assert.equal(
messages.find(record => record.text === 'Harness retained user turn' && !record.uuid.includes(':tail:')).visibility,
'inactive',
);
assert.equal(values.find(record => record.kind === 'summary').visibility, 'visible');
assert.deepEqual(assembleSessionDetail(values).messages.map(message => message.text), visibleText);
assert.deepEqual(
messages.reduce(
(total, message) => ({
input: total.input + (message.input_tokens ?? 0),
output: total.output + (message.output_tokens ?? 0),
}),
{ input: 0, output: 0 },
),
{ input: 369, output: 69 },
'retained context copies must not duplicate model usage',
);
const db = new DatabaseSync(':memory:');
db.exec(SCHEMA);
persist(db, unit, provider.parse(unit, null));
const query = createQueryApi(db);
assert.equal(query.search('Harness first').length, 0);
assert.deepEqual(query.thread(unit.sessionId).map(message => message.text), visibleText);
db.close();
});
test('legacy compaction exposes only firstKeptEntryId ancestors and later descendants', () => {
const records = [
header({ id: 'first-kept-context' }),
userEntry('old-root', null, 'old root'),
userEntry('old-near', 'old-root', 'old near', '2026-08-02T10:00:02.000Z'),
userEntry('kept-start', 'old-near', 'kept start', '2026-08-02T10:00:03.000Z'),
userEntry('kept-end', 'kept-start', 'kept end', '2026-08-02T10:00:04.000Z'),
{
type: 'compaction',
id: 'legacy-compaction',
parentId: 'kept-end',
timestamp: '2026-08-02T10:00:05.000Z',
summary: 'Earlier context summary',
firstKeptEntryId: 'kept-start',
tokensBefore: 100,
},
userEntry('post-compaction', 'legacy-compaction', 'post compaction', '2026-08-02T10:00:06.000Z'),
];
const { provider, unit, values } = parseOnly(writeSession(jsonl(records)).root);
const messages = values.filter(record => record.kind === 'message');
assert.deepEqual(
messages.filter(record => record.visibility === 'visible').map(record => record.text),
['kept start', 'kept end', 'post compaction'],
);
assert.deepEqual(
messages.filter(record => record.visibility === 'inactive').map(record => record.text),
['old root', 'old near'],
);
assert.equal(values.find(record => record.kind === 'summary').visibility, 'visible');
const db = new DatabaseSync(':memory:');
db.exec(SCHEMA);
persist(db, unit, provider.parse(unit, null));
const query = createQueryApi(db);
assert.equal(query.search('old near').length, 0);
assert.deepEqual(
query.thread(unit.sessionId).map(message => message.text),
['kept start', 'kept end', 'post compaction'],
);
db.close();
});
test('a later legacy compaction can retain context across an earlier compaction', () => {
const records = [
header({ id: 'nested-compaction-context' }),
userEntry('root', null, 'root'),
userEntry('near-first', 'root', 'near first', '2026-08-02T10:00:02.000Z'),
{
type: 'compaction',
id: 'first-compaction',
parentId: 'near-first',
timestamp: '2026-08-02T10:00:03.000Z',
summary: 'First summary',
firstKeptEntryId: 'near-first',
tokensBefore: 50,
},
userEntry('after-first', 'first-compaction', 'after first', '2026-08-02T10:00:04.000Z'),
{
type: 'compaction',
id: 'second-compaction',
parentId: 'after-first',
timestamp: '2026-08-02T10:00:05.000Z',
summary: 'Second summary',
firstKeptEntryId: 'root',
tokensBefore: 100,
},
userEntry('after-second', 'second-compaction', 'after second', '2026-08-02T10:00:06.000Z'),
];
const { values } = parseOnly(writeSession(jsonl(records)).root);
assert.deepEqual(
values
.filter(record => record.kind === 'message' && record.visibility === 'visible')
.map(record => record.text),
['root', 'near first', 'after first', 'after second'],
);
assert.deepEqual(
values
.filter(record => record.kind === 'summary' && record.visibility === 'visible')
.map(record => record.content),
['First summary', 'Second summary'],
);
});
test('a retained-tail checkpoint bounds a later legacy compaction like Pi 0.83 storage', () => {
const records = [
header({ id: 'mixed-compaction-context' }),
userEntry('root', null, 'discarded root'),
{
type: 'compaction',
id: 'checkpoint',
parentId: 'root',
timestamp: '2026-08-02T10:00:02.000Z',
summary: 'Checkpoint summary',
firstKeptEntryId: 'root',
tokensBefore: 50,
retainedTail: [{
role: 'user',
content: 'checkpoint tail',
timestamp: Date.parse('2026-08-02T10:00:02.000Z'),
}],
},
userEntry('after-checkpoint', 'checkpoint', 'discarded after checkpoint', '2026-08-02T10:00:03.000Z'),
{
type: 'compaction',
id: 'later-legacy',
parentId: 'after-checkpoint',
timestamp: '2026-08-02T10:00:04.000Z',
summary: 'Later legacy summary',
firstKeptEntryId: 'root',
tokensBefore: 100,
},
userEntry('head', 'later-legacy', 'active head', '2026-08-02T10:00:05.000Z'),
];
const { values } = parseOnly(writeSession(jsonl(records)).root);
assert.deepEqual(
values
.filter(record => record.kind === 'message' && record.visibility === 'visible')
.map(record => record.text),
['active head'],
);
assert.deepEqual(
values
.filter(record => record.kind === 'summary' && record.visibility === 'visible')
.map(record => record.content),
['Later legacy summary'],
);
});
test('legacy checkpoint fork accepts a truncated parent before firstKeptEntryId', () => {
const records = [
header({ id: 'legacy-checkpoint-fork' }),
userEntry('kept-start', 'omitted-parent', 'kept start'),
userEntry('kept-end', 'kept-start', 'kept end', '2026-08-02T10:00:02.000Z'),
{
type: 'compaction',
id: 'legacy-compaction',
parentId: 'kept-end',
timestamp: '2026-08-02T10:00:03.000Z',
summary: 'Omitted source context summary',
firstKeptEntryId: 'kept-start',
tokensBefore: 100,
},
userEntry('post-compaction', 'legacy-compaction', 'post compaction', '2026-08-02T10:00:04.000Z'),
];
const { values } = parseOnly(writeSession(jsonl(records)).root);
assert.deepEqual(
values
.filter(record => record.kind === 'message' && record.visibility === 'visible')
.map(record => record.text),
['kept start', 'kept end', 'post compaction'],
);
assert.equal(values.find(record => record.kind === 'summary').visibility, 'visible');
});
test('checkpoint fork materializes retainedTail exactly once and reconnects later parent identity', () => {
const { root, path } = writeSession(fixture('harness-checkpoint-fork.jsonl'));
const { provider, unit, values, cursor } = parseOnly(root);
const messages = values.filter(record => record.kind === 'message');
assert.deepEqual(messages.map(record => [record.content_type, record.text]), [
['text', 'Harness retained user turn'],
['thinking', 'retained reasoning'],
['text', 'Harness retained assistant turn'],
['text', 'Harness post-compaction user turn'],
['text', 'Harness post-compaction assistant turn'],
]);
assert.equal(messages[1].input_tokens, null);
assert.equal(messages[2].input_tokens, null);
assert.equal(messages[2].output_tokens, null);
assert.equal(messages[4].input_tokens, 123);
assert.equal(messages[4].output_tokens, 23);
assert.equal(messages[3].parent_uuid, messages[2].uuid);
assert.equal(values.filter(record => record.kind === 'summary').length, 1);
assert.deepEqual(assembleSessionDetail(values).messages.map(message => message.text), [
'Harness retained user turn',
'Harness retained assistant turn',
'Harness post-compaction user turn',
'Harness post-compaction assistant turn',
]);
const raw = message => provider.raw({
source: 'pi',
messageUuid: message.uuid,
session: { id: unit.sessionId, jsonl_path: path },
agentId: null,
cursor,
});
assert.doesNotMatch(raw(messages[1]).text, /"retainedTail"/);
assert.equal(JSON.parse(raw(messages[1]).text).role, 'assistant');
assert.equal(raw(messages[1]).messageText, 'retained reasoning');
assert.equal(raw(messages[2]).messageText, 'Harness retained assistant turn');
});
test('retained-tail raw evidence never exposes hidden sibling messages', () => {
const records = [
header({ id: 'retained-raw-visibility' }),
userEntry('root', null, 'discarded root'),
{
type: 'compaction',
id: 'checkpoint',
parentId: 'root',
timestamp: '2026-08-02T10:00:02.000Z',
summary: 'Checkpoint summary',
firstKeptEntryId: 'root',
tokensBefore: 50,
retainedTail: [
{
role: 'user',
content: 'visible retained evidence',
timestamp: Date.parse('2026-08-02T10:00:02.000Z'),
},
{
role: 'custom',
content: 'HIDDEN RETAINED SIBLING',
display: false,
timestamp: Date.parse('2026-08-02T10:00:03.000Z'),
},
],
},
];
const { root } = writeSession(jsonl(records));
const { provider, unit, values } = parseOnly(root);
const visible = values.find(record => (
record.kind === 'message'
&& record.visibility === 'visible'
&& record.text === 'visible retained evidence'
));
const hidden = values.find(record => (
record.kind === 'message'
&& record.visibility === 'hidden'
));
assert.ok(visible);
assert.ok(hidden);
const db = new DatabaseSync(':memory:');
db.exec(SCHEMA);
persist(db, unit, provider.parse(unit, null));
const query = createQueryApi(db, {
providerRegistry: createProviderRegistry([provider]),
});
const raw = query.raw(visible.uuid);
assert.equal(raw.text, JSON.stringify(records[2].retainedTail[0]));
assert.doesNotMatch(raw.text, /HIDDEN RETAINED SIBLING/);
assert.equal(query.raw(hidden.uuid), null);
db.close();
});
test('a retained tail beginning with a tool result remains standalone active evidence', () => {
const records = [
header({ id: 'retained-tool-result' }),
{
type: 'message',
id: 'physical-call',
parentId: null,
timestamp: '2026-08-02T10:00:01.000Z',
message: {
role: 'assistant',
content: [{
type: 'toolCall',
id: 'split-call',
name: 'read',
arguments: { path: 'probe.txt' },
}],
usage: { input: 5, output: 2 },
timestamp: 1785664801000,
},
},
{
type: 'compaction',
id: 'checkpoint',
parentId: 'physical-call',
timestamp: '2026-08-02T10:00:02.000Z',
summary: 'The preceding tool turn was split.',
firstKeptEntryId: 'physical-call',
tokensBefore: 100,
retainedTail: [{
role: 'toolResult',
toolCallId: 'split-call',
toolName: 'read',
content: [{ type: 'text', text: 'RETAINED RESULT' }],
isError: false,
timestamp: 1785664802000,
}],
},
userEntry('after-checkpoint', 'checkpoint', 'after', '2026-08-02T10:00:03.000Z'),
];
const { provider, unit, values } = parseOnly(writeSession(jsonl(records)).root);
assert.deepEqual(
values
.filter(record => record.kind === 'message' && record.visibility === 'visible')
.map(record => record.text),
['RETAINED RESULT', 'after'],
);
assert.deepEqual(
assembleSessionDetail(values).messages.map(message => message.text),
['RETAINED RESULT', 'after'],
);
const db = new DatabaseSync(':memory:');
db.exec(SCHEMA);
persist(db, unit, provider.parse(unit, null));
assert.deepEqual(
assembleSessionDetail({
session: db.prepare('SELECT * FROM sessions').get(),
messages: db.prepare('SELECT * FROM messages ORDER BY timestamp,uuid').all(),
toolCalls: db.prepare('SELECT * FROM tool_calls').all(),
toolResults: db.prepare('SELECT * FROM tool_results').all(),
summaries: db.prepare('SELECT * FROM summaries').all(),
}).messages.map(message => message.text),
['RETAINED RESULT', 'after'],
);
db.close();
});
test('full tree marks abandoned messages inactive while indexing every summary and explicit user bash action', () => {
const records = [
header(),
userEntry('root-user', null, 'shared root'),
userEntry('abandoned-user', 'root-user', 'abandoned branch'),
{
type: 'message',
id: 'abandoned-assistant',
parentId: 'abandoned-user',
timestamp: '2026-08-02T10:00:03.000Z',
message: {
role: 'assistant',
content: [{ type: 'text', text: 'discarded answer' }],
model: 'probe',
usage: { input: 1, output: 1, cacheRead: 0, cacheWrite: 0 },
timestamp: Date.parse('2026-08-02T10:00:03.000Z'),
},
},
{
type: 'custom_message',
id: 'inactive-custom',
parentId: 'abandoned-assistant',
timestamp: '2026-08-02T10:00:03.500Z',
customType: 'probe',
content: 'superseded extension context',
display: true,
},
{
type: 'branch_summary',
id: 'branch-summary',
parentId: 'root-user',
timestamp: '2026-08-02T10:00:04.000Z',
fromId: 'abandoned-assistant',
summary: 'The abandoned branch tried a discarded answer.',
},
{
type: 'custom_message',
id: 'hidden-custom',
parentId: 'branch-summary',
timestamp: '2026-08-02T10:00:05.000Z',
customType: 'probe',
content: 'hidden extension context',
display: false,
},
{
type: 'custom_message',
id: 'visible-custom',
parentId: 'hidden-custom',
timestamp: '2026-08-02T10:00:06.000Z',
customType: 'probe',
content: 'visible extension context',
display: true,
},
{
type: 'message',
id: 'bash',
parentId: 'visible-custom',
timestamp: '2026-08-02T10:00:07.000Z',
message: {
role: 'bashExecution',
command: 'printf probe',
output: 'x'.repeat(12_000),
exitCode: 0,
cancelled: false,
truncated: true,
fullOutputPath: '/tmp/must-not-be-read',
excludeFromContext: true,
timestamp: Date.parse('2026-08-02T10:00:07.000Z'),
},
},
{
type: 'compaction',
id: 'compaction',
parentId: 'bash',
timestamp: '2026-08-02T10:00:08.000Z',
summary: 'Earlier active work.',
firstKeptEntryId: 'root-user',
tokensBefore: 42,
},
userEntry('active-user', 'compaction', 'active request', '2026-08-02T10:00:09.000Z'),
];
const { root } = writeSession(jsonl(records));
const { provider, unit, values } = parseOnly(root);
const messages = values.filter(record => record.kind === 'message');
assert.equal(messages.find(record => record.text === 'abandoned branch').visibility, 'inactive');
assert.equal(messages.find(record => record.text === 'abandoned branch').is_meta, 0);
assert.equal(messages.find(record => record.text === 'superseded extension context').visibility, 'inactive');
assert.equal(messages.find(record => record.text === 'hidden extension context').visibility, 'hidden');
assert.equal(messages.find(record => record.text === 'visible extension context').visibility, 'visible');
const bash = messages.find(record => record.role === 'bashExecution');
assert.equal(bash.type, 'user');
assert.equal(bash.content_type, 'bash_execution');
assert.equal(bash.is_meta, 0);
assert.equal(bash.text.length, 10_000);
assert.match(bash.text, /\[Output truncated\. Full output: \/tmp\/must-not-be-read\]$/);
assert.equal(values.filter(record => record.kind === 'tool_call').length, 0);
assert.deepEqual(
values.filter(record => record.kind === 'summary').map(record => record.source),
['pi:branch_summary', 'pi:compaction'],
);
const db = new DatabaseSync(':memory:');
db.exec(SCHEMA);
persist(db, unit, provider.parse(unit, null));
const query = createQueryApi(db);
assert.equal(query.search('abandoned').length, 0);
assert.equal(query.search('abandoned', { includeInactive: true })[0].message.visibility, 'inactive');
assert.equal(query.search('superseded extension').length, 0);
assert.equal(query.search('superseded extension', { includeInactive: true }).length, 0);
assert.equal(
query.search('superseded extension', {
includeInactive: true,
includeMeta: true,
})[0].message.visibility,
'inactive',
);
assert.equal(query.search('active request').length, 1);
assert.equal(query.thread(unit.sessionId).some(message => message.text === 'abandoned branch'), false);
assert.equal(
query.thread(unit.sessionId, { includeInactive: true })
.find(message => message.text === 'abandoned branch').visibility,
'inactive',
);
db.close();
});
test('inactive Pi summaries remain available for accounting and explicit historical queries', () => {
const records = [
header({ id: 'summary-visibility' }),
userEntry('root', null, 'shared root'),
userEntry('abandoned', 'root', 'abandoned prompt'),
{
type: 'branch_summary',
id: 'abandoned-summary',
parentId: 'abandoned',
timestamp: '2026-08-02T10:00:03.000Z',
fromId: 'abandoned',
summary: 'ABANDONED SUMMARY SECRET',
usage: { input: 9, output: 2, cacheRead: 3, cacheWrite: 4 },
},
userEntry('active', 'root', 'active prompt', '2026-08-02T10:00:04.000Z'),
];
const { provider, unit, values } = parseOnly(writeSession(jsonl(records)).root);
const summary = values.find(record => record.kind === 'summary');
assert.equal(summary.visibility, 'inactive');
assert.equal(summary.input_tokens, 16);
assert.deepEqual(assembleSessionDetail(values).summaries, []);
const db = new DatabaseSync(':memory:');
db.exec(SCHEMA);
persist(db, unit, provider.parse(unit, null));
const query = createQueryApi(db);
assert.deepEqual(query.summaries(unit.sessionId), []);
assert.equal(
query.summaries({ sessionId: unit.sessionId, includeInactive: true })[0].visibility,
'inactive',
);
assert.deepEqual(
assembleSessionDetail({ summaries: db.prepare('SELECT * FROM summaries').all() }).summaries,
[],
);
assert.deepEqual(
{ ...db.prepare('SELECT content,visibility,input_tokens,output_tokens FROM summaries').get() },
{
content: 'ABANDONED SUMMARY SECRET',
visibility: 'inactive',
input_tokens: 16,
output_tokens: 2,
},
);
db.close();
});
test('lowercase read, edit, and write arguments.path populate call and matching result file paths', () => {
const records = [header()];
let parent = null;
for (const [index, name] of ['read', 'edit', 'write'].entries()) {
const callEntry = `call-entry-${index}`;
const callId = `call-${index}`;
records.push({
type: 'message',
id: callEntry,
parentId: parent,
timestamp: `2026-08-02T10:00:0${index + 1}.000Z`,
message: {
role: 'assistant',
content: [{ type: 'toolCall', id: callId, name, arguments: { path: `src/${name}.ts` } }],
model: 'probe',
usage: { input: 1, output: 1, cacheRead: 0, cacheWrite: 0 },
timestamp: 1785664800000 + index,
},
});
records.push({
type: 'message',
id: `result-entry-${index}`,
parentId: callEntry,
timestamp: `2026-08-02T10:00:1${index + 1}.000Z`,
message: {
role: 'toolResult',
toolCallId: callId,
toolName: name,
content: [{ type: 'text', text: `${name} result` }],
isError: false,
timestamp: 1785664801000 + index,
},
});
parent = `result-entry-${index}`;
}
const { root } = writeSession(jsonl(records));
const { values } = parseOnly(root);
assert.deepEqual(
values.filter(record => record.kind === 'tool_call').map(record => record.file_path),
['src/read.ts', 'src/edit.ts', 'src/write.ts'],
);
assert.deepEqual(
values.filter(record => record.kind === 'tool_result').map(record => record.file_path),
['src/read.ts', 'src/edit.ts', 'src/write.ts'],
);
});
test('tool-result nested model usage is retained on its canonical message', () => {
const records = [
header(),
{
type: 'message',
id: 'result',
parentId: null,
timestamp: '2026-08-02T10:00:01.000Z',
message: {
role: 'toolResult',
toolCallId: 'nested-call',
toolName: 'agent',
content: [{ type: 'text', text: 'nested result' }],
usage: { input: 11, output: 7, cacheRead: 3, cacheWrite: 2 },
isError: false,
timestamp: 1785664801000,
},
},
];
const { values } = parseOnly(writeSession(jsonl(records)).root);
const result = values.find(record => record.kind === 'message');
assert.equal(result.input_tokens, 16);
assert.equal(result.output_tokens, 7);
});
test('compaction and branch-summary model usage survives canonical persistence', () => {
const records = [
header({ id: 'summary-usage' }),
userEntry('user', null, 'summarize this'),
{
type: 'branch_summary',
id: 'branch-summary',
parentId: 'user',
timestamp: '2026-08-02T10:00:02.000Z',
fromId: 'user',
summary: 'Branch summary',
usage: { input: 10, output: 4, cacheRead: 2, cacheWrite: 3 },
},
{
type: 'compaction',
id: 'compaction',
parentId: 'branch-summary',
timestamp: '2026-08-02T10:00:03.000Z',
summary: 'Compaction summary',
firstKeptEntryId: 'user',
tokensBefore: 100,
usage: { input: 20, output: 5, cacheRead: 6, cacheWrite: 7 },
},
];
const { provider, unit, values } = parseOnly(writeSession(jsonl(records)).root);
assert.deepEqual(
values.filter(record => record.kind === 'summary').map(record => ({
source: record.source,
input: record.input_tokens,
output: record.output_tokens,
})),
[
{ source: 'pi:branch_summary', input: 15, output: 4 },
{ source: 'pi:compaction', input: 33, output: 5 },
],
);
const db = new DatabaseSync(':memory:');
db.exec(SCHEMA);
persist(db, unit, provider.parse(unit, null));
assert.deepEqual(
db.prepare('SELECT input_tokens,output_tokens FROM summaries ORDER BY timestamp').all()
.map(row => ({ ...row })),
[
{ input_tokens: 15, output_tokens: 4 },
{ input_tokens: 33, output_tokens: 5 },
],
);
db.close();
});
test('retained-tail summaries keep distinct canonical identities through persistence', () => {
const records = [
header({ id: 'retained-summary-identities' }),
userEntry('root', null, 'summarize retained context'),
{
type: 'compaction',
id: 'checkpoint',
parentId: 'root',
timestamp: '2026-08-02T10:00:03.000Z',
summary: 'Outer compaction',
firstKeptEntryId: 'root',
tokensBefore: 100,
retainedTail: [
{
role: 'branchSummary',
summary: 'First retained summary',
timestamp: Date.parse('2026-08-02T10:00:01.000Z'),
},
{
role: 'branchSummary',
summary: 'Second retained summary',
timestamp: Date.parse('2026-08-02T10:00:02.000Z'),
},
],
},
];
const { provider, unit, values } = parseOnly(writeSession(jsonl(records)).root);
const retained = values.filter(record => (
record.kind === 'summary'
&& record.source === 'pi:branch_summary'
));
assert.deepEqual(retained.map(record => record.content), [
'First retained summary',
'Second retained summary',
]);
assert.equal(new Set(retained.map(record => record.id)).size, 2);
const db = new DatabaseSync(':memory:');
db.exec(SCHEMA);
persist(db, unit, provider.parse(unit, null));
assert.deepEqual(
db.prepare("SELECT content FROM summaries WHERE source='pi:branch_summary' ORDER BY timestamp")
.all().map(row => row.content),
retained.map(record => record.content),
);
const byTimestamp = (left, right) => String(left.timestamp).localeCompare(String(right.timestamp));
assert.deepEqual(
assembleSessionDetail({
summaries: db.prepare('SELECT * FROM summaries ORDER BY timestamp').all(),
}).summaries.sort(byTimestamp),
assembleSessionDetail(values).summaries.sort(byTimestamp),
);
db.close();
});
test('malformed physical JSON lines follow Pi while structural errors fail atomically', () => {
const complete = jsonl([header(), userEntry('user', null, 'no newline')], false);
assert.equal(parseOnly(writeSession(complete).root).values.filter(record => record.kind === 'message').length, 1);
const completedCorruption = [
JSON.stringify(header()),
JSON.stringify(userEntry('before', null, 'before malformed line')),
'{broken}',
JSON.stringify(userEntry('after', 'before', 'after malformed line')),
'',
].join('\n');
assert.deepEqual(
parseOnly(writeSession(completedCorruption).root).values
.filter(record => record.kind === 'message')
.map(record => record.text),
['before malformed line', 'after malformed line'],
);
const terminalGarbage = `${jsonl([header(), userEntry('user', null, 'prefix')])}not-json`;
assert.deepEqual(
parseOnly(writeSession(terminalGarbage).root).values
.filter(record => record.kind === 'message')
.map(record => record.text),
['prefix'],
);
const invalidValue = writeSession(`${JSON.stringify(header())}\n42\n`);
const valueProvider = createPiProvider({ rootDir: invalidValue.root });
const valueUnit = valueProvider.discover({ lastCursor: () => null })[0];
assert.throws(
() => drain(valueProvider.parse(valueUnit, null)),
/Malformed Pi JSONL value at line 2/,
);
const invalidMessage = writeSession(jsonl([
header(),
{
type: 'message',
id: 'invalid-message',
parentId: null,
timestamp: '2026-08-02T10:00:01.000Z',
message: null,
},
]));
const messageProvider = createPiProvider({ rootDir: invalidMessage.root });
const messageUnit = messageProvider.discover({ lastCursor: () => null })[0];
assert.throws(
() => drain(messageProvider.parse(messageUnit, null)),
/Malformed Pi message/,
);
});
test('official v1 migration preserves canonical identities and v2 hookMessage becomes custom metadata', () => {
const v1 = [
{
type: 'session',
id: 'legacy-v1',
},
{
type: 'message',
timestamp: '2026-08-02T10:00:01.000Z',
message: { role: 'user', content: 'legacy user', timestamp: 1785664800000 },
},
{
type: 'compaction',
timestamp: '2026-08-02T10:00:02.000Z',
summary: 'legacy summary',
firstKeptEntryIndex: 1,
tokensBefore: 100,
},
{
type: 'message',
timestamp: '2026-08-02T10:00:03.000Z',
message: { role: 'user', content: 'legacy tail', timestamp: 1785664803000 },
},
];
const written = writeSession(`${v1.map(record => JSON.stringify(record)).join('\n\n')}\n`);
const firstParse = parseOnly(written.root);
const first = firstParse.values;
const migrated = [
{ ...v1[0], version: 3 },
{ ...v1[1], id: 'random-migrated-1', parentId: null },
{
...v1[2],
id: 'random-migrated-2',
parentId: 'random-migrated-1',
firstKeptEntryId: 'random-migrated-1',
firstKeptEntryIndex: undefined,
},
{ ...v1[3], id: 'random-migrated-3', parentId: 'random-migrated-2' },
];
writeFileSync(written.path, jsonl(migrated));
const secondParse = parseOnly(written.root);
const second = secondParse.values;
assert.deepEqual(
first.filter(record => record.kind === 'message').map(record => record.uuid),
second.filter(record => record.kind === 'message').map(record => record.uuid),
);
assert.deepEqual(
first.filter(record => record.kind === 'summary').map(record => record.id),
second.filter(record => record.kind === 'summary').map(record => record.id),
);
const firstSession = first.find(record => record.kind === 'session');
const secondSession = second.find(record => record.kind === 'session');
assert.equal(firstSession.version, 'session-v1');
assert.equal(secondSession.version, 'session-v3');
assert.equal(firstSession.started_at, null);
assert.equal(firstSession.project, null);
assert.equal(firstSession.id, secondSession.id);
assert.ok(first.filter(record => record.kind === 'message').every(record => record.cwd === null));
const rawAfterMigration = firstParse.provider.raw({
source: 'pi',
messageUuid: first.find(record => record.kind === 'message').uuid,
session: { id: firstSession.id, jsonl_path: written.path },
agentId: null,
cursor: secondParse.cursor,
});
assert.deepEqual(JSON.parse(rawAfterMigration.text), migrated[1].message);
assert.equal(rawAfterMigration.messageText, 'legacy user');
const v2 = [
header({ version: 2, id: 'legacy-v2' }),
{
type: 'message',
id: 'hook',
parentId: null,
timestamp: '2026-08-02T10:00:01.000Z',
message: {
role: 'hookMessage',
customType: 'legacy-hook',
content: 'extension context',
display: true,
timestamp: 1785664800000,
},
},
];
const hook = parseOnly(writeSession(jsonl(v2)).root).values.find(record => record.kind === 'message');
assert.deepEqual(
(({ type, role, content_type, is_meta, visibility }) => ({ type, role, content_type, is_meta, visibility }))(hook),
{ type: 'system', role: 'custom', content_type: 'custom', is_meta: 1, visibility: 'visible' },
);
});
test('invalid tree identity, cycle, and leaf target fail closed', () => {
const cases = [
{
pattern: /Duplicate Pi entry id/,
records: [header(), userEntry('same', null, 'one'), userEntry('same', 'same', 'two')],
},
{
pattern: /cycle/,
records: [header(), userEntry('a', 'b', 'a'), userEntry('b', 'a', 'b')],
},
{
pattern: /cycle/,
records: [
header(),
userEntry('a', 'b', 'a'),
userEntry('b', 'a', 'b'),
{
type: 'compaction',
id: 'unrelated-compaction',
parentId: null,
timestamp: '2026-08-02T10:00:03.000Z',
summary: 'Must not mask the cycle',
firstKeptEntryId: 'a',
tokensBefore: 10,
},
],
},
{
pattern: /leaf target .* does not exist/,
records: [
header(),
userEntry('a', null, 'a'),
{ type: 'leaf', id: 'leaf', parentId: 'a', targetId: 'missing', timestamp: '2026-08-02T10:00:02.000Z' },
],
},
];
for (const { records, pattern } of cases) {
const { root } = writeSession(jsonl(records));
const provider = createPiProvider({ rootDir: root });
const unit = provider.discover({ lastCursor: () => null })[0];
assert.throws(() => drain(provider.parse(unit, null)), pattern);
}
});
test('missing parents form official Pi orphan roots without exposing inactive branches', () => {
const records = [
header({ id: 'orphan-root' }),
userEntry('inactive-root', null, 'inactive root'),
userEntry('orphan', 'omitted-parent', 'orphan root', '2026-08-02T10:00:02.000Z'),
userEntry('orphan-child', 'orphan', 'orphan child', '2026-08-02T10:00:03.000Z'),
];
const { provider, unit, values } = parseOnly(writeSession(jsonl(records)).root);
assert.deepEqual(
values
.filter(record => record.kind === 'message' && record.visibility === 'visible')
.map(record => record.text),
['orphan root', 'orphan child'],
);
assert.equal(
values.find(record => record.kind === 'message' && record.text === 'inactive root').visibility,
'inactive',
);
const db = new DatabaseSync(':memory:');
db.exec(SCHEMA);
persist(db, unit, provider.parse(unit, null));
const query = createQueryApi(db);
assert.deepEqual(query.thread(unit.sessionId).map(message => message.text), [
'orphan root',
'orphan child',
]);
assert.deepEqual(assembleSessionDetail(values).messages.map(message => message.text), [
'orphan root',
'orphan child',
]);
db.close();
});
test('discovery deduplicates identical same-project copies and rejects divergent copies', () => {
const root = mkdtempSync(join(tmpdir(), 'obelisk-pi-copies-'));
const original = fixture('tool-session.jsonl');
writeSession(original, { root, relativePath: 'a/session.jsonl' });
writeSession(original, { root, relativePath: 'b/session.jsonl' });
const provider = createPiProvider({ rootDir: root });
assert.equal(provider.discover({ lastCursor: () => null }).length, 1);
writeSession(`${original}${JSON.stringify({
type: 'session_info',
id: 'divergent-name',
parentId: '9db96a87',
timestamp: '2026-08-02T09:42:39.000Z',
name: 'Diverged copy',
})}\n`, { root, relativePath: 'b/session.jsonl' });
const divergent = provider.discover({ lastCursor: () => null });
assert.equal(divergent.length, 2);
for (const unit of divergent) {
assert.throws(() => drain(provider.parse(unit, null)), /Divergent Pi session copies/);
}
});
test('discovery follows readable Pi JSONL file symlinks without retracting provenance', () => {
const written = writeSession(jsonl([
header({ id: 'symlink-session' }),
userEntry('symlink-user', null, 'symlink evidence'),
]));
const provider = createPiProvider({ rootDir: written.root });
const original = provider.discover({ lastCursor: () => null })[0];
const parsed = drain(provider.parse(original, null));
const targetDir = mkdtempSync(join(tmpdir(), 'obelisk-pi-symlink-target-'));
const target = join(targetDir, 'session.jsonl');
renameSync(written.path, target);
symlinkSync(target, written.path);
const units = provider.discover({
lastCursor: key => key === written.path ? parsed.cursor : null,
changedPaths: [written.path],
indexedSessions: () => [{
sessionId: original.sessionId,
jsonlPath: written.path,
}],
});
assert.equal(units.some(unit => unit.meta?.kind === 'pi-tombstone'), false);
assert.equal(units.length, 1);
assert.equal(units[0].sessionId, original.sessionId);
assert.equal(
drain(provider.parse(units[0], null)).values
.find(record => record.kind === 'message').text,
'symlink evidence',
);
});
test('a malformed Pi file does not force unrelated unchanged sessions to reparse', () => {
const root = mkdtempSync(join(tmpdir(), 'obelisk-pi-bad-file-scope-'));
const damaged = writeSession(jsonl([
header({ id: 'damaged-session' }),
userEntry('damaged-user', null, 'damaged source'),
]), { root, relativePath: 'a/session.jsonl' });
const unchanged = writeSession(jsonl([
header({ id: 'unchanged-session' }),
userEntry('unchanged-user', null, 'unchanged source'),
]), { root, relativePath: 'b/session.jsonl' });
const provider = createPiProvider({ rootDir: root });
const cursors = new Map();
const indexed = [];
for (const unit of provider.discover({ lastCursor: () => null })) {
cursors.set(unit.key, drain(provider.parse(unit, null)).cursor);
indexed.push({ sessionId: unit.sessionId, jsonlPath: unit.key });
}
writeFileSync(damaged.path, '{broken}\n');
const units = provider.discover({
lastCursor: key => cursors.get(key) ?? null,
indexedSessions: () => indexed,
});
assert.deepEqual(units.map(unit => unit.key), [damaged.path]);
assert.equal(units.some(unit => unit.key === unchanged.path), false);
assert.throws(() => drain(provider.parse(units[0], null)), /Empty Pi session/);
});
test('a moved session with an unreadable identity cannot retract its last good snapshot', () => {
const written = writeSession(jsonl([
header({ id: 'moved-torn-session' }),
userEntry('moved-user', null, 'last good evidence'),
]));
const provider = createPiProvider({ rootDir: written.root });
const original = provider.discover({ lastCursor: () => null })[0];
const parsed = drain(provider.parse(original, null));
const movedPath = join(written.root, 'moved', 'session.jsonl');
mkdirSync(dirname(movedPath), { recursive: true });
renameSync(written.path, movedPath);
writeFileSync(movedPath, '{torn header\n');
let incomplete = false;
const units = provider.discover({
lastCursor: key => key === written.path ? parsed.cursor : null,
indexedSessions: () => [{
sessionId: original.sessionId,
jsonlPath: written.path,
}],
reportIncompleteInventory: () => { incomplete = true; },
});
assert.equal(incomplete, false, 'a bad identity is not an incomplete filesystem traversal');
assert.deepEqual(units.map(unit => unit.key), [movedPath]);
assert.equal(units[0].retractSessionIds, undefined);
assert.equal(units.some(unit => unit.meta?.kind === 'pi-tombstone'), false);
assert.throws(() => drain(provider.parse(units[0], null)), /Empty Pi session/);
});
test('a bad selected copy forces an unchanged valid duplicate to refresh provenance', () => {
const root = mkdtempSync(join(tmpdir(), 'obelisk-pi-bad-copy-'));
const content = fixture('tool-session.jsonl');
const second = writeSession(content, { root, relativePath: 'b/session.jsonl' });
const provider = createPiProvider({ rootDir: root });
const initialUnit = provider.discover({ lastCursor: () => null })[0];
const initial = drain(provider.parse(initialUnit, null));
const cursors = new Map([[second.path, initial.cursor]]);
const first = writeSession(content, { root, relativePath: 'a/session.jsonl' });
const selected = provider.discover({
lastCursor: key => cursors.get(key) ?? null,
})[0];
assert.equal(selected.key, first.path);
const selectedParse = drain(provider.parse(selected, null));
cursors.set(first.path, selectedParse.cursor);
writeFileSync(first.path, '{broken}\n');
const recovery = provider.discover({
lastCursor: key => cursors.get(key) ?? null,
indexedSessions: () => [{
sessionId: initialUnit.sessionId,
jsonlPath: first.path,
}],
});
const survivor = recovery.find(unit => unit.key === second.path);
assert.ok(survivor, 'the valid copy must bypass its unchanged cursor');
assert.equal(survivor.sessionId, initialUnit.sessionId);
assert.equal(
drain(provider.parse(survivor, null)).values
.find(record => record.kind === 'session').jsonl_path,
second.path,
);
});
test('the same official project-local session id remains distinct across cwd namespaces', () => {
const root = mkdtempSync(join(tmpdir(), 'obelisk-pi-project-ids-'));
const firstHeader = header({ id: 'shared-custom-id', cwd: '/tmp/pi-project-a' });
const secondHeader = header({ id: 'shared-custom-id', cwd: '/tmp/pi-project-b' });
writeSession(jsonl([
firstHeader,
userEntry('project-a-user', null, 'project A evidence'),
]), { root, relativePath: 'a/session.jsonl' });
writeSession(jsonl([
secondHeader,
userEntry('project-b-user', null, 'project B evidence'),
]), { root, relativePath: 'b/session.jsonl' });
const provider = createPiProvider({ rootDir: root });
const units = provider.discover({ lastCursor: () => null });
assert.equal(units.length, 2);
assert.deepEqual(
new Set(units.map(unit => unit.sessionId)),
new Set([piSessionId(firstHeader), piSessionId(secondHeader)]),
);
const db = new DatabaseSync(':memory:');
db.exec(SCHEMA);
for (const unit of units) persist(db, unit, provider.parse(unit, null));
assert.deepEqual(
new Set(db.prepare("SELECT id FROM sessions WHERE source='pi'").all().map(row => row.id)),
new Set([piSessionId(firstHeader), piSessionId(secondHeader)]),
);
assert.equal(createQueryApi(db).search('project evidence').length, 2);
db.close();
});
test('identity migration retracts a legacy row attached to a non-selected identical copy', () => {
const root = mkdtempSync(join(tmpdir(), 'obelisk-pi-identity-migration-'));
const content = fixture('tool-session.jsonl');
const first = writeSession(content, { root, relativePath: 'a/session.jsonl' });
const second = writeSession(content, { root, relativePath: 'b/session.jsonl' });
const provider = createPiProvider({ rootDir: root });
const initialUnit = provider.discover({ lastCursor: () => null })[0];
const initial = drain(provider.parse(initialUnit, null));
const sourceHeader = JSON.parse(content.split('\n')[0]);
const legacyId = `pi:${sourceHeader.id}`;
const migration = provider.discover({
lastCursor: key => key === first.path ? initial.cursor : null,
indexedSessions: () => [{ sessionId: legacyId, jsonlPath: second.path }],
});
assert.equal(migration.length, 1);
assert.equal(migration[0].key, first.path);
assert.equal(migration[0].sessionId, piSessionId(sourceHeader));
assert.deepEqual(migration[0].retractSessionIds, [legacyId]);
assert.deepEqual(
drain(provider.parse(migration[0], null)).values
.filter(record => record.kind === 'delete-session')
.map(record => record.sessionId),
[legacyId, piSessionId(sourceHeader)],
);
});
test('unlinking a deduplicated Pi copy reparses the surviving source instead of deleting the session', () => {
const root = mkdtempSync(join(tmpdir(), 'obelisk-pi-copy-move-'));
const content = fixture('tool-session.jsonl');
const first = writeSession(content, { root, relativePath: 'a/session.jsonl' });
const second = writeSession(content, { root, relativePath: 'b/session.jsonl' });
const provider = createPiProvider({ rootDir: root });
const original = provider.discover({ lastCursor: () => null })[0];
const parsed = drain(provider.parse(original, null));
unlinkSync(first.path);
const survivors = provider.discover({
lastCursor: key => key === first.path ? parsed.cursor : null,
changedPaths: [first.path],
indexedSessions: () => [{ sessionId: original.sessionId, jsonlPath: first.path }],
});
assert.equal(survivors.length, 1);
assert.equal(survivors[0].key, second.path);
assert.deepEqual(survivors[0].retractSessionIds, undefined);
assert.equal(
drain(provider.parse(survivors[0], null)).values.find(record => record.kind === 'session').jsonl_path,
second.path,
);
});
test('replacing one Pi copy preserves the original identity when another copy survives', () => {
const root = mkdtempSync(join(tmpdir(), 'obelisk-pi-copy-replace-'));
const content = fixture('tool-session.jsonl');
const first = writeSession(content, { root, relativePath: 'a/session.jsonl' });
const second = writeSession(content, { root, relativePath: 'b/session.jsonl' });
const provider = createPiProvider({ rootDir: root });
const original = provider.discover({ lastCursor: () => null })[0];
drain(provider.parse(original, null));
writeFileSync(first.path, jsonl([
header({ id: 'replacement-copy' }),
userEntry('replacement-user', null, 'replacement'),
]));
const units = provider.discover({
lastCursor: () => null,
changedPaths: [first.path],
indexedSessions: () => [{ sessionId: original.sessionId, jsonlPath: first.path }],
});
assert.deepEqual(
units.map(unit => ({ path: unit.key, sessionId: unit.sessionId, retract: unit.retractSessionIds })),
[
{
path: first.path,
sessionId: piSessionId(header({ id: 'replacement-copy' })),
retract: undefined,
},
{ path: second.path, sessionId: original.sessionId, retract: undefined },
],
);
});
test('changed-path discovery bypasses cursors while an unchanged passive pull is skipped', () => {
const first = writeSession(fixture('tool-session.jsonl'));
const provider = createPiProvider({ rootDir: first.root });
const unit = provider.discover({ lastCursor: () => null })[0];
const { cursor } = drain(provider.parse(unit, null));
assert.deepEqual(provider.discover({ lastCursor: () => cursor }), []);
assert.deepEqual(
provider.discover({ lastCursor: () => cursor, changedPaths: [first.path] }).map(candidate => candidate.key),
[first.path],
);
});
test('Pi snapshot cursors detect same-identity and replacement writes that preserve mtime', () => {
const written = writeSession(jsonl([
header({ id: 'preserved-mtime' }),
userEntry('before', null, 'before'),
]));
const provider = createPiProvider({ rootDir: written.root });
const initialUnit = provider.discover({ lastCursor: () => null })[0];
const initial = drain(provider.parse(initialUnit, null));
const replaceAtSameMtime = (content) => {
const before = statSync(written.path);
const replacement = `${written.path}.replacement`;
writeFileSync(replacement, content);
utimesSync(replacement, before.atime, before.mtime);
renameSync(replacement, written.path);
utimesSync(written.path, before.atime, before.mtime);
};
replaceAtSameMtime(jsonl([
header({ id: 'preserved-mtime' }),
userEntry('after', null, 'after!'),
]));
const sameIdentity = provider.discover({
lastCursor: () => initial.cursor,
indexedSessions: () => [{ sessionId: initialUnit.sessionId, jsonlPath: written.path }],
});
assert.equal(sameIdentity.length, 1);
assert.equal(sameIdentity[0].sessionId, initialUnit.sessionId);
const updated = drain(provider.parse(sameIdentity[0], initial.cursor));
replaceAtSameMtime(jsonl([
header({ id: 'replacement-mtime' }),
userEntry('replacement', null, 'replacement'),
]));
const replacement = provider.discover({
lastCursor: () => updated.cursor,
indexedSessions: () => [{ sessionId: initialUnit.sessionId, jsonlPath: written.path }],
});
assert.equal(replacement.length, 1);
assert.equal(replacement[0].sessionId, piSessionId(header({ id: 'replacement-mtime' })));
assert.deepEqual(replacement[0].retractSessionIds, [initialUnit.sessionId]);
});
test('incomplete Pi inventories never infer deletion from changed paths', () => {
const parent = mkdtempSync(join(tmpdir(), 'obelisk-pi-incomplete-'));
const unreadableRoot = join(parent, 'sessions');
writeFileSync(unreadableRoot, 'not a directory');
const indexedPath = join(unreadableRoot, 'project', 'session.jsonl');
const provider = createPiProvider({ rootDir: unreadableRoot });
assert.deepEqual(provider.discover({
lastCursor: () => null,
changedPaths: [unreadableRoot],
indexedSessions: () => [{ sessionId: 'pi:preserve-me', jsonlPath: indexedPath }],
}), []);
});
test('a file disappearing during discovery marks the inventory incomplete without throwing', () => {
const written = writeSession(jsonl([
header({ id: 'discovery-race' }),
userEntry('message', null, 'vanishing source'),
]));
const provider = createPiProvider({ rootDir: written.root });
let incomplete = false;
const units = provider.discover({
lastCursor: () => null,
indexedSessions: () => {
unlinkSync(written.path);
return [];
},
reportIncompleteInventory: () => { incomplete = true; },
});
assert.deepEqual(units, []);
assert.equal(incomplete, true);
});
test('indexed provenance retracts a replaced identity and emits an unlink tombstone', () => {
const written = writeSession(jsonl([header({ id: 'before' }), userEntry('a', null, 'before')]));
const provider = createPiProvider({ rootDir: written.root });
const beforeUnit = provider.discover({ lastCursor: () => null })[0];
const before = drain(provider.parse(beforeUnit, null));
const beforeSession = before.values.find(record => record.kind === 'session').id;
writeFileSync(written.path, jsonl([header({ id: 'after' }), userEntry('b', null, 'after')]));
const afterUnit = provider.discover({
lastCursor: () => before.cursor,
changedPaths: [written.path],
indexedSessions: () => [{ sessionId: beforeSession, jsonlPath: written.path }],
})[0];
assert.deepEqual(afterUnit.retractSessionIds, [beforeSession]);
const after = drain(provider.parse(afterUnit, null));
const afterSession = after.values.find(record => record.kind === 'session').id;
assert.notEqual(afterSession, beforeSession);
assert.deepEqual(
after.values.filter(record => record.kind === 'delete-session').map(record => record.sessionId),
[beforeSession, afterSession],
);
unlinkSync(written.path);
const tombstones = provider.discover({
lastCursor: () => after.cursor,
changedPaths: [written.path],
indexedSessions: () => [{ sessionId: afterSession, jsonlPath: written.path }],
});
assert.equal(tombstones.length, 1);
assert.deepEqual(tombstones[0].retractSessionIds, [afterSession]);
const tombstone = drain(provider.parse(tombstones[0], after.cursor));
assert.deepEqual(tombstone.values, [{ kind: 'delete-session', sessionId: afterSession }]);
assert.equal(tombstone.cursor, '0:0');
});
test('session identity combines Pi project-local header id with stable cwd namespace', () => {
const source = JSON.parse(fixture('tool-session.jsonl').split('\n')[0]);
const migrated = {
...source,
version: source.version === 3 ? 2 : 3,
timestamp: '2030-01-01T00:00:00.000Z',
parentSession: '/tmp/parent.jsonl',
};
assert.equal(piSessionId(source), piSessionId(migrated));
assert.notEqual(piSessionId(source), piSessionId({ ...source, id: 'different-session' }));
assert.notEqual(piSessionId(source), piSessionId({ ...source, cwd: '/tmp/other-project' }));
});
test('long linear Pi trees validate without repeated prefix walks', () => {
const records = [header({ id: 'long-linear' })];
let parentId = null;
for (let index = 0; index < 5000; index++) {
const id = `entry-${index}`;
records.push(userEntry(id, parentId, `message ${index}`));
parentId = id;
}
const { values } = parseOnly(writeSession(jsonl(records)).root);
assert.equal(values.filter(record => record.kind === 'message').length, 5000);
});