Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
207 changes: 207 additions & 0 deletions packages/headless/src/__tests__/harbor-cell.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,14 @@ import {
import {
BackendRegistry,
PiAgentBackend,
buildToolResultArchiveResourceRef,
type AgentBackend,
type AiSdkBackendInput,
type BackendFactoryContext,
type InvocationResult,
type PiAgentTransport,
type ProviderRequestCaptureRecord,
type MakaTool,
type SessionStore,
type SynthesisCacheWriteInput,
type ToolResultArchiveReader,
Expand All @@ -48,6 +50,8 @@ import {
buildHarborCellContextBudgetBackendOptions,
buildHarborCellContextBudgetPolicySnapshot,
buildHarborCellTaskLedgerExperimentPolicy,
buildHarborCellToolResultArchiveResourceReader,
type RunHarborCellEnv,
harborCellMaxStepsFromEnv,
harborCellSoftTimeoutMsFromEnv,
createHarborCellLocalToolExecutor,
Expand Down Expand Up @@ -3556,6 +3560,181 @@ describe('runHarborCell', () => {
});
});

test('Harbor env entrypoint binds ArchiveRead whenever it archives tool results', async () => {
await withDirs(async ({ workspaceDir, outputDir, storageRoot }) => {
const context = await runHarborCellCapturingBackendContext({
MAKA_INSTRUCTION: 'solve',
MAKA_WORKDIR: workspaceDir,
MAKA_OUTPUT_DIR: outputDir,
MAKA_STORAGE_ROOT: storageRoot,
});

assert.ok(
buildHarborCellContextBudgetBackendOptions({ MAKA_OUTPUT_DIR: outputDir })
.archiveToolResult,
'the default Harbor output dir resolves an archive dir, so the writer is on',
);
assert.ok(
context.productToolSurface?.tools.some((tool) => tool.name === 'ArchiveRead'),
'archived placeholders instruct the model to call ArchiveRead, so it must be bound',
);
});
});

test('Harbor env entrypoint leaves ArchiveRead unbound when it archives nothing', async () => {
await withDirs(async ({ workspaceDir, outputDir, storageRoot }) => {
const env: RunHarborCellEnv = {
MAKA_INSTRUCTION: 'solve',
MAKA_WORKDIR: workspaceDir,
MAKA_OUTPUT_DIR: outputDir,
MAKA_STORAGE_ROOT: storageRoot,
MAKA_CONTEXT_BUDGET: 'off',
};
const context = await runHarborCellCapturingBackendContext(env);

assert.equal(
buildHarborCellContextBudgetBackendOptions(env).archiveToolResult,
undefined,
'context budget off disables the archive writer',
);
assert.equal(
context.productToolSurface?.tools.some((tool) => tool.name === 'ArchiveRead'),
false,
'a cell that archives nothing must not offer the model a tool with no readable refs',
);
});
});

test('Harbor ArchiveRead reads back a tool result the cell archived', async () => {
await withDirs(async ({ workspaceDir, outputDir, storageRoot }) => {
const env: RunHarborCellEnv = {
MAKA_INSTRUCTION: 'solve',
MAKA_WORKDIR: workspaceDir,
MAKA_OUTPUT_DIR: outputDir,
MAKA_STORAGE_ROOT: storageRoot,
};
const context = await runHarborCellCapturingBackendContext(env);
const archiveToolResult = buildHarborCellContextBudgetBackendOptions(env).archiveToolResult;
assert.ok(archiveToolResult);

const serializedResult = JSON.stringify({ body: 'archived tool output' });
const bodySha256 = sha256(serializedResult);
const originalBytes = Buffer.byteLength(serializedResult, 'utf8');
const archived = await archiveToolResult({
sessionId: 'session-1',
runtimeEventId: 'rt-result',
turnId: 'turn-1',
toolCallId: 'tool-1',
toolName: 'Read',
result: { body: 'archived tool output' },
serializedResult,
originalEstimatedTokens: 99,
originalBytes,
rewriteVersion: 1,
reason: 'stale_tool_result_pruned_before_compact',
bodySha256,
});
assert.ok(archived?.artifactId);

const archiveRead = context.productToolSurface?.tools.find(
(tool) => tool.name === 'ArchiveRead',
);
assert.ok(archiveRead);
const output = (await archiveRead.impl(
{
ref: buildToolResultArchiveResourceRef({
artifactId: archived.artifactId,
bodySha256,
originalBytes,
}),
operation: 'read',
},
{
sessionId: 'session-1',
turnId: 'turn-1',
cwd: workspaceDir,
toolCallId: 'archive-read-1',
abortSignal: new AbortController().signal,
emitOutput: () => {},
},
)) as { ok: boolean; content?: string };

assert.equal(output.ok, true, 'the ref the placeholder carries must resolve');
assert.match(String(output.content), /archived tool output/);
});
});

test('Harbor archive resource reader refuses every way a ref can fail to match', async () => {
await withDirs(async ({ outputDir }) => {
const env: RunHarborCellEnv = { MAKA_OUTPUT_DIR: outputDir };
const archiveToolResult = buildHarborCellContextBudgetBackendOptions(env).archiveToolResult;
const reader = buildHarborCellToolResultArchiveResourceReader(env);
assert.ok(archiveToolResult);
assert.ok(reader);

const serializedResult = JSON.stringify({ body: 'archived' });
const bodySha256 = sha256(serializedResult);
const originalBytes = Buffer.byteLength(serializedResult, 'utf8');
const archived = await archiveToolResult({
sessionId: 'session-1',
runtimeEventId: 'rt-result',
turnId: 'turn-1',
toolCallId: 'tool-1',
toolName: 'Read',
result: { body: 'archived' },
serializedResult,
originalEstimatedTokens: 9,
originalBytes,
rewriteVersion: 1,
reason: 'stale_tool_result_pruned_before_compact',
bodySha256,
});
assert.ok(archived?.artifactId);
// Mirrors the runtime resource path, which always addresses an archive by its
// exact recorded size; Harbor reads the whole record, so maxBytes never binds.
const valid = {
artifactId: archived.artifactId,
sessionId: 'session-1',
bodySha256,
originalBytes,
maxBytes: originalBytes,
};

const cases: readonly [string, typeof valid, string][] = [
['escapes the archive dir', { ...valid, artifactId: '../escape.json' }, 'not_allowed'],
[
'names an artifact that was never written',
{ ...valid, artifactId: 'absent.json' },
'not_found',
],
['belongs to another session', { ...valid, sessionId: 'session-2' }, 'session_mismatch'],
[
'claims a different size',
{ ...valid, originalBytes: originalBytes + 1 },
'size_mismatch',
],
[
'claims a different body hash',
{ ...valid, bodySha256: sha256('other') },
'source_mismatch',
],
];
for (const [label, input, reason] of cases) {
const result = await reader.readArchivedToolResultResource(input);
assert.equal(result.ok, false, label);
assert.equal(result.ok === false && result.reason, reason, label);
}

await writeFile(
join(outputDir, 'tool-result-archives', archived.artifactId),
'not json',
'utf8',
);
const corrupt = await reader.readArchivedToolResultResource(valid);
assert.equal(corrupt.ok === false && corrupt.reason, 'corrupt');
});
});

test('Harbor context budget env rejects explicit malformed positive integers', async () => {
for (const raw of ['abc', '0', '-1', '1x']) {
await withDirs(async ({ workspaceDir, artifactStore }) => {
Expand Down Expand Up @@ -4839,6 +5018,34 @@ async function registerProjectedAiSdkBackend(
});
}

/**
* Drives the real env entrypoint and returns the backend context it assembled, so
* assertions observe production wiring rather than a projection rebuilt by the test.
*/
async function runHarborCellCapturingBackendContext(
env: RunHarborCellEnv,
): Promise<HeadlessBackendContext> {
const seen: HeadlessBackendContext[] = [];
await runHarborCellFromEnv(
{ MAKA_BACKEND: 'ai-sdk', MAKA_PROVIDER: 'openai', MAKA_MODEL: 'gpt-4o-mini', ...env },
{
registerBackends: (registry, context) => {
seen.push(context);
registry.register(
'ai-sdk',
(ctx) =>
new CellReportingBackend(
{ sessionId: ctx.sessionId, header: ctx.header, store: ctx.store },
'ai-sdk',
),
);
},
},
);
assert.ok(seen[0], 'expected the env entrypoint to register a backend');
return seen[0];
}

function testIdFactory(): () => string {
let i = 0;
return () => `id-${++i}`;
Expand Down
108 changes: 75 additions & 33 deletions packages/headless/src/harbor-cell-context-budget-env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ import { join } from 'node:path';
import type {
ContextBudgetPolicy,
ToolResultArchiveReader,
ToolResultArchiveReadFailureReason,
ToolResultArchiveReadResult,
ToolResultArchiveRecorder,
ToolResultArchiveResourceReader,
} from '@maka/runtime';
import type { HarborCellContextBudgetPolicySnapshot } from './cell-output.js';
import {
Expand Down Expand Up @@ -459,42 +462,75 @@ export function buildHarborCellContextBudgetBackendOptions(
await writeFile(join(archiveDir, artifactId), `${JSON.stringify(record)}\n`, 'utf8');
return { artifactId };
},
readToolResultArchive: async (input) => {
if (!isSafeHarborCellArchiveArtifactId(input.artifactId))
return { ok: false, reason: 'not_allowed' };
let raw: string;
try {
raw = await readFile(join(archiveDir, input.artifactId), 'utf8');
} catch {
return { ok: false, reason: 'not_found' };
}
let parsed: unknown;
try {
parsed = JSON.parse(raw);
} catch {
return { ok: false, reason: 'corrupt' };
}
if (!isHarborCellToolResultArchiveRecord(parsed)) return { ok: false, reason: 'corrupt' };
if (parsed.sessionId !== input.sessionId) return { ok: false, reason: 'session_mismatch' };
if (
parsed.runtimeEventId !== input.runtimeEventId ||
parsed.toolCallId !== input.toolCallId
) {
return { ok: false, reason: 'source_mismatch' };
}
if (parsed.originalBytes !== input.originalBytes)
return { ok: false, reason: 'size_mismatch' };
if (parsed.bodySha256 !== input.bodySha256) return { ok: false, reason: 'source_mismatch' };
const actualSha = createHash('sha256').update(parsed.serializedResult).digest('hex');
if (actualSha !== input.bodySha256) return { ok: false, reason: 'corrupt' };
if (Buffer.byteLength(parsed.serializedResult, 'utf8') !== input.originalBytes) {
return { ok: false, reason: 'size_mismatch' };
}
return { ok: true, serializedResult: parsed.serializedResult };
},
// Replay hydration addresses an archive by the originating runtime event, so
// it verifies that identity on top of the shared artifact checks.
readToolResultArchive: async (input) =>
readHarborCellArchivedToolResult(archiveDir, input, {
runtimeEventId: input.runtimeEventId,
toolCallId: input.toolCallId,
}),
};
}

/**
* Ref-addressed reader backing the `ArchiveRead` tool. Unlike replay hydration it
* has no runtime event identity to check — a `maka://archive/...` ref only carries
* artifact id, hash, and size — and it must never synthesize one to satisfy the
* stricter path.
*/
export function buildHarborCellToolResultArchiveResourceReader(
env: RunHarborCellEnv = process.env,
): ToolResultArchiveResourceReader | undefined {
normalizeHarborCellContextEnv(env);
const archiveDir = harborCellToolResultArchiveDir(env);
if (!archiveDir) return undefined;
return {
readArchivedToolResultResource: async (input) =>
readHarborCellArchivedToolResult(archiveDir, input),
};
}

async function readHarborCellArchivedToolResult(
archiveDir: string,
input: {
artifactId: string;
sessionId: string;
bodySha256: string;
originalBytes: number;
},
expectedSource?: { runtimeEventId: string; toolCallId: string },
): Promise<ToolResultArchiveReadResult> {
if (!isSafeHarborCellArchiveArtifactId(input.artifactId))
return { ok: false, reason: 'not_allowed' };
let raw: string;
try {
raw = await readFile(join(archiveDir, input.artifactId), 'utf8');
} catch {
return { ok: false, reason: 'not_found' };
}
let parsed: unknown;
try {
parsed = JSON.parse(raw);
} catch {
return { ok: false, reason: 'corrupt' };
}
if (!isHarborCellToolResultArchiveRecord(parsed)) return { ok: false, reason: 'corrupt' };
if (parsed.sessionId !== input.sessionId) return { ok: false, reason: 'session_mismatch' };
if (
expectedSource &&
(parsed.runtimeEventId !== expectedSource.runtimeEventId ||
parsed.toolCallId !== expectedSource.toolCallId)
)
return { ok: false, reason: 'source_mismatch' };
if (parsed.originalBytes !== input.originalBytes) return { ok: false, reason: 'size_mismatch' };
if (parsed.bodySha256 !== input.bodySha256) return { ok: false, reason: 'source_mismatch' };
const actualSha = createHash('sha256').update(parsed.serializedResult).digest('hex');
if (actualSha !== input.bodySha256) return { ok: false, reason: 'corrupt' };
const actualBytes = Buffer.byteLength(parsed.serializedResult, 'utf8');
if (actualBytes !== input.originalBytes) return { ok: false, reason: 'size_mismatch' };
return { ok: true, serializedResult: parsed.serializedResult };
}

export function buildHarborCellTaskLedgerExperimentPolicy(
env: RunHarborCellEnv = process.env,
): HarborCellTaskLedgerExperimentPolicy | undefined {
Expand Down Expand Up @@ -799,7 +835,13 @@ function semanticCompactModeEnv(
);
}

/**
* Sole authority for whether a cell archives tool results, and where. Both the
* writer and every reader derive from it, so the model can never be told to call
* `ArchiveRead` for a run that archives nothing — nor archive what it cannot read.
*/
function harborCellToolResultArchiveDir(env: RunHarborCellEnv): string | undefined {
if (env.MAKA_CONTEXT_BUDGET === 'off') return undefined;
return (
env.MAKA_CONTEXT_TOOL_RESULT_ARCHIVE_DIR ??
env.MAKA_TOOL_RESULT_ARCHIVE_DIR ??
Expand Down
Loading
Loading