Squashed 'third_party/pi/' content from commit d981de122
git-subtree-dir: third_party/pi git-subtree-split: d981de1229ef899957bbe968bc8dcda02a21f477
This commit is contained in:
289
packages/agent/test/harness/runtime/drive-terminal.test.ts
Normal file
289
packages/agent/test/harness/runtime/drive-terminal.test.ts
Normal file
@@ -0,0 +1,289 @@
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { BACKGROUND_CONTEXT } from "../../../src/harness/context.ts";
|
||||
import { operationCleanupWrites, operationResultRecord } from "../../../src/harness/runtime/drive/terminal.ts";
|
||||
import { MemoryStorage } from "../../../src/harness/session/memory.ts";
|
||||
import { StorageBackedSession } from "../../../src/harness/session/session.ts";
|
||||
import type {
|
||||
AssistantEffectPendingOperation,
|
||||
LaneConfiguration,
|
||||
NavigationReadyToCommitOperation,
|
||||
OperationMeta,
|
||||
OperationScope,
|
||||
OperationState,
|
||||
Session,
|
||||
SummaryDecidingOperation,
|
||||
ToolsOperation,
|
||||
Write,
|
||||
} from "../../../src/harness/session/types.ts";
|
||||
import * as storedValues from "../../../src/harness/session/values.ts";
|
||||
|
||||
const sessions: Session[] = [];
|
||||
const configuration: LaneConfiguration = {
|
||||
model: { provider: "test", modelId: "model" },
|
||||
thinkingLevel: "off",
|
||||
activeToolNames: [],
|
||||
};
|
||||
|
||||
function runScope(): OperationScope {
|
||||
return {
|
||||
control: { status: "running" },
|
||||
settings: {
|
||||
compaction: { enabled: true, reserveTokens: 1_000, keepRecentTokens: 2_000 },
|
||||
steeringMode: "all",
|
||||
followUpMode: "all",
|
||||
toolExecution: "parallel",
|
||||
},
|
||||
latestAssistantEntryId: null,
|
||||
};
|
||||
}
|
||||
|
||||
function meta(operationId: string, state: OperationState): OperationMeta {
|
||||
const intent: OperationMeta["intent"] =
|
||||
state.at === "summary.deciding"
|
||||
? { kind: "compaction" }
|
||||
: state.at === "navigation.ready_to_commit"
|
||||
? { kind: "navigation", targetId: state.targetId, summarize: false }
|
||||
: { kind: "run", promptEntryIds: [] };
|
||||
return { operationId, lane: "main", sourceTipId: null, startedAt: 1, intent };
|
||||
}
|
||||
|
||||
async function createSession(): Promise<{ session: Session; storage: MemoryStorage }> {
|
||||
const storage = new MemoryStorage();
|
||||
const session = new StorageBackedSession(
|
||||
{ id: `terminal-${sessions.length}`, createdAt: 1, storageVersion: 1 },
|
||||
storage,
|
||||
);
|
||||
sessions.push(session);
|
||||
return { session, storage };
|
||||
}
|
||||
|
||||
async function commit(session: Session, writes: Write[]): Promise<void> {
|
||||
await session.mutate(async (mutator) => {
|
||||
await mutator.commit(writes, BACKGROUND_CONTEXT);
|
||||
}, BACKGROUND_CONTEXT);
|
||||
}
|
||||
|
||||
function address(write: Write): string {
|
||||
if (write.kind === "entry") return `entry:${write.entry.id}`;
|
||||
if (write.kind === "usage") return `usage:${write.row.id}`;
|
||||
return `${write.kind}:${write.op}:${write.namespace}:${write.key}`;
|
||||
}
|
||||
|
||||
async function seedLeftovers(session: Session, operationId: string, state: OperationState): Promise<void> {
|
||||
await commit(session, [
|
||||
storedValues.setValue(storedValues.operationMeta(operationId), meta(operationId, state)),
|
||||
storedValues.setValue(storedValues.operationState(operationId), state),
|
||||
storedValues.setValue(storedValues.operationToolArgs(operationId, "step", 0), { value: true }),
|
||||
storedValues.setValue(storedValues.operationToolMemo(operationId, "invocation", "memo"), { value: true }),
|
||||
storedValues.setValue(storedValues.operationPreparation(operationId, "task"), {
|
||||
kind: "branch_summary",
|
||||
messages: [],
|
||||
fileOps: { read: [], written: [], edited: [] },
|
||||
totalTokens: 0,
|
||||
}),
|
||||
storedValues.setValue(storedValues.pendingToolOutput(operationId, "invocation"), {
|
||||
content: [{ type: "text", text: "partial" }],
|
||||
details: {},
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
afterEach(async () => {
|
||||
vi.restoreAllMocks();
|
||||
for (const session of sessions.splice(0)) await session.close(BACKGROUND_CONTEXT);
|
||||
});
|
||||
|
||||
describe("runtime terminal cleanup mechanics", () => {
|
||||
it("deletes every operation-owned family and exact live frame list but preserves the lane inbox", async () => {
|
||||
const { session } = await createSession();
|
||||
const operationId = "run";
|
||||
const responseEntryId = "response";
|
||||
const state: AssistantEffectPendingOperation = {
|
||||
...runScope(),
|
||||
at: "assistant.effect_pending",
|
||||
generationContext: {
|
||||
stepId: "step",
|
||||
triggerEntryId: "trigger",
|
||||
configuration,
|
||||
streamOptions: {},
|
||||
retryPolicy: { maxAttempts: 2, baseDelayMs: 1 },
|
||||
overflowRecoveryUsed: false,
|
||||
},
|
||||
attempt: 1,
|
||||
responseEntryId,
|
||||
usageId: "usage",
|
||||
intendedOutputLimit: 100,
|
||||
contextWindow: 1_000,
|
||||
control: { status: "cancel_requested", requestedAt: 2 },
|
||||
};
|
||||
await seedLeftovers(session, operationId, state);
|
||||
await commit(session, [
|
||||
...["steer", "follow", "write", "next"].map((id) =>
|
||||
storedValues.setValue(storedValues.pendingEntry(id), {
|
||||
type: "custom",
|
||||
customType: "test",
|
||||
payload: { id },
|
||||
}),
|
||||
),
|
||||
storedValues.appendList(storedValues.pendingAssistantFrames(operationId, responseEntryId), {
|
||||
type: "text_delta",
|
||||
contentIndex: 0,
|
||||
delta: "partial",
|
||||
}),
|
||||
storedValues.setValue(storedValues.laneState("main"), {
|
||||
currentOperationId: operationId,
|
||||
lastOperationId: null,
|
||||
inbox: [
|
||||
{ entryId: "steer", kind: "steer" },
|
||||
{ entryId: "follow", kind: "followUp" },
|
||||
{ entryId: "write", kind: "write" },
|
||||
{ entryId: "next", kind: "nextRun" },
|
||||
],
|
||||
}),
|
||||
]);
|
||||
|
||||
const writes = await operationCleanupWrites(session, operationId, state, BACKGROUND_CONTEXT);
|
||||
|
||||
expect(writes.map(address)).toEqual([
|
||||
"value:delete:pi.op.meta:run",
|
||||
"value:delete:pi.op.state:run",
|
||||
"value:delete:pi.op.tool_args:run:step:0",
|
||||
"value:delete:pi.op.tool_memo:run:invocation:memo",
|
||||
"value:delete:pi.op.preparation:run:task",
|
||||
"value:delete:pi.pending.tool_output:run:invocation",
|
||||
"list:delete:pi.pending.assistant_frame:run:response",
|
||||
]);
|
||||
await commit(session, writes);
|
||||
for (const id of ["steer", "follow", "write", "next"]) {
|
||||
expect(await session.getValue(storedValues.pendingEntry(id), BACKGROUND_CONTEXT)).toBeDefined();
|
||||
}
|
||||
expect((await session.getValue(storedValues.laneState("main"), BACKGROUND_CONTEXT))?.value.inbox).toEqual([
|
||||
{ entryId: "steer", kind: "steer" },
|
||||
{ entryId: "follow", kind: "followUp" },
|
||||
{ entryId: "write", kind: "write" },
|
||||
{ entryId: "next", kind: "nextRun" },
|
||||
]);
|
||||
expect(
|
||||
await session.readList(
|
||||
storedValues.pendingAssistantFrames(operationId, responseEntryId),
|
||||
undefined,
|
||||
BACKGROUND_CONTEXT,
|
||||
),
|
||||
).toEqual([]);
|
||||
});
|
||||
|
||||
it("deletes staged tool outcomes and leaves completed results alone", async () => {
|
||||
const { session } = await createSession();
|
||||
const operationId = "tools";
|
||||
const state: ToolsOperation = {
|
||||
...runScope(),
|
||||
at: "tools",
|
||||
batch: {
|
||||
assistantEntryId: "assistant",
|
||||
configuration,
|
||||
turnId: "step",
|
||||
calls: [
|
||||
{ status: "outcome_ready", sourceIndex: 0, resultEntryId: "staged", terminate: false },
|
||||
{ status: "completed", sourceIndex: 1, resultEntryId: "placed", terminate: false },
|
||||
],
|
||||
},
|
||||
};
|
||||
await seedLeftovers(session, operationId, state);
|
||||
await commit(session, [
|
||||
storedValues.setValue(storedValues.pendingEntry("staged"), {
|
||||
type: "message",
|
||||
payload: {
|
||||
role: "toolResult",
|
||||
toolCallId: "call",
|
||||
toolName: "tool",
|
||||
content: [],
|
||||
isError: false,
|
||||
timestamp: 1,
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
const writes = await operationCleanupWrites(session, operationId, state, BACKGROUND_CONTEXT);
|
||||
|
||||
expect(writes.map(address)).toContain("value:delete:pi.pending.entry:staged");
|
||||
expect(writes.map(address)).not.toContain("value:delete:pi.pending.entry:placed");
|
||||
});
|
||||
|
||||
it.each([
|
||||
[
|
||||
"compaction",
|
||||
{
|
||||
...runScope(),
|
||||
at: "summary.deciding",
|
||||
task: { taskId: "task", reason: "manual", boundary: { kind: "finish" } },
|
||||
} satisfies SummaryDecidingOperation,
|
||||
],
|
||||
[
|
||||
"navigation",
|
||||
{
|
||||
...runScope(),
|
||||
at: "navigation.ready_to_commit",
|
||||
targetId: null,
|
||||
} satisfies NavigationReadyToCommitOperation,
|
||||
],
|
||||
] as const)("defensively deletes leftover %s operation families", async (operationId, state) => {
|
||||
const { session } = await createSession();
|
||||
await seedLeftovers(session, operationId, state);
|
||||
|
||||
const writes = await operationCleanupWrites(session, operationId, state, BACKGROUND_CONTEXT);
|
||||
|
||||
expect(writes.map(address)).toEqual([
|
||||
`value:delete:pi.op.meta:${operationId}`,
|
||||
`value:delete:pi.op.state:${operationId}`,
|
||||
`value:delete:pi.op.tool_args:${operationId}:step:0`,
|
||||
`value:delete:pi.op.tool_memo:${operationId}:invocation:memo`,
|
||||
`value:delete:pi.op.preparation:${operationId}:task`,
|
||||
`value:delete:pi.pending.tool_output:${operationId}:invocation`,
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("runtime operation result records", () => {
|
||||
it("constructs one flat immutable observation from terminal metadata", () => {
|
||||
vi.spyOn(Date, "now").mockReturnValue(20);
|
||||
const record = operationResultRecord(
|
||||
{
|
||||
operationId: "run",
|
||||
lane: "main",
|
||||
sourceTipId: "source",
|
||||
startedAt: 10,
|
||||
intent: { kind: "run", promptEntryIds: ["prompt"] },
|
||||
},
|
||||
"failed",
|
||||
"tip",
|
||||
{ code: "provider", message: "failed" },
|
||||
);
|
||||
|
||||
expect(record).toEqual({
|
||||
operationId: "run",
|
||||
kind: "run",
|
||||
status: "failed",
|
||||
error: { code: "provider", message: "failed" },
|
||||
fromTipId: "source",
|
||||
tipId: "tip",
|
||||
startedAt: 10,
|
||||
endedAt: 20,
|
||||
});
|
||||
});
|
||||
|
||||
it("rejects errors on non-failed records and missing errors on failed records", () => {
|
||||
const metadata: OperationMeta = {
|
||||
operationId: "run",
|
||||
lane: "main",
|
||||
sourceTipId: null,
|
||||
startedAt: 1,
|
||||
intent: { kind: "run", promptEntryIds: [] },
|
||||
};
|
||||
expect(() => operationResultRecord(metadata, "completed", "tip", { code: "x", message: "x" })).toThrow(
|
||||
"Only a failed operation result may carry an error",
|
||||
);
|
||||
expect(() => operationResultRecord(metadata, "failed", "tip")).toThrow(
|
||||
"Only a failed operation result may carry an error",
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user