refactor: dedupe test and script helpers

This commit is contained in:
Peter Steinberger
2026-03-24 15:47:44 +00:00
parent 66e954858b
commit 781295c14b
56 changed files with 2277 additions and 3522 deletions

View File

@@ -13,6 +13,10 @@ import {
} from "./attempt.spawn-workspace.test-support.js";
const hoisted = getHoisted();
const embeddedSessionId = "embedded-session";
const sessionFile = "/tmp/session.jsonl";
const seedMessage = { role: "user", content: "seed", timestamp: 1 } as AgentMessage;
const doneMessage = { role: "assistant", content: "done", timestamp: 2 } as unknown as AgentMessage;
function createTestContextEngine(params: Partial<AttemptContextEngine>): AttemptContextEngine {
return {
@@ -31,6 +35,65 @@ function createTestContextEngine(params: Partial<AttemptContextEngine>): Attempt
} as AttemptContextEngine;
}
async function runBootstrap(
sessionKey: string,
contextEngine: AttemptContextEngine,
overrides: Partial<Parameters<typeof runAttemptContextEngineBootstrap>[0]> = {},
) {
await runAttemptContextEngineBootstrap({
hadSessionFile: true,
contextEngine,
sessionId: embeddedSessionId,
sessionKey,
sessionFile,
sessionManager: hoisted.sessionManager,
runtimeContext: {},
runMaintenance: hoisted.runContextEngineMaintenanceMock,
warn: () => {},
...overrides,
});
}
async function runAssemble(
sessionKey: string,
contextEngine: AttemptContextEngine,
overrides: Partial<Parameters<typeof assembleAttemptContextEngine>[0]> = {},
) {
await assembleAttemptContextEngine({
contextEngine,
sessionId: embeddedSessionId,
sessionKey,
messages: [seedMessage],
tokenBudget: 2048,
modelId: "gpt-test",
...overrides,
});
}
async function finalizeTurn(
sessionKey: string,
contextEngine: AttemptContextEngine,
overrides: Partial<Parameters<typeof finalizeAttemptContextEngineTurn>[0]> = {},
) {
await finalizeAttemptContextEngineTurn({
contextEngine,
promptError: false,
aborted: false,
yieldAborted: false,
sessionIdUsed: embeddedSessionId,
sessionKey,
sessionFile,
messagesSnapshot: [doneMessage],
prePromptMessageCount: 0,
tokenBudget: 2048,
runtimeContext: {},
runMaintenance: hoisted.runContextEngineMaintenanceMock,
sessionManager: hoisted.sessionManager,
warn: () => {},
...overrides,
});
}
describe("runEmbeddedAttempt context engine sessionKey forwarding", () => {
const sessionKey = "agent:main:discord:channel:test-ctx-engine";
@@ -47,43 +110,9 @@ describe("runEmbeddedAttempt context engine sessionKey forwarding", () => {
afterTurn,
});
await runAttemptContextEngineBootstrap({
hadSessionFile: true,
contextEngine,
sessionId: "embedded-session",
sessionKey,
sessionFile: "/tmp/session.jsonl",
sessionManager: hoisted.sessionManager,
runtimeContext: {},
runMaintenance: hoisted.runContextEngineMaintenanceMock,
warn: () => {},
});
await assembleAttemptContextEngine({
contextEngine,
sessionId: "embedded-session",
sessionKey,
messages: [{ role: "user", content: "seed", timestamp: 1 } as AgentMessage],
tokenBudget: 2048,
modelId: "gpt-test",
});
await finalizeAttemptContextEngineTurn({
contextEngine,
promptError: false,
aborted: false,
yieldAborted: false,
sessionIdUsed: "embedded-session",
sessionKey,
sessionFile: "/tmp/session.jsonl",
messagesSnapshot: [
{ role: "assistant", content: "done", timestamp: 2 } as unknown as AgentMessage,
],
prePromptMessageCount: 0,
tokenBudget: 2048,
runtimeContext: {},
runMaintenance: hoisted.runContextEngineMaintenanceMock,
sessionManager: hoisted.sessionManager,
warn: () => {},
});
await runBootstrap(sessionKey, contextEngine);
await runAssemble(sessionKey, contextEngine);
await finalizeTurn(sessionKey, contextEngine);
expectCalledWithSessionKey(bootstrap, sessionKey);
expectCalledWithSessionKey(assemble, sessionKey);
@@ -94,25 +123,8 @@ describe("runEmbeddedAttempt context engine sessionKey forwarding", () => {
const { bootstrap, assemble } = createContextEngineBootstrapAndAssemble();
const contextEngine = createTestContextEngine({ bootstrap, assemble });
await runAttemptContextEngineBootstrap({
hadSessionFile: true,
contextEngine,
sessionId: "embedded-session",
sessionKey,
sessionFile: "/tmp/session.jsonl",
sessionManager: hoisted.sessionManager,
runtimeContext: {},
runMaintenance: hoisted.runContextEngineMaintenanceMock,
warn: () => {},
});
await assembleAttemptContextEngine({
contextEngine,
sessionId: "embedded-session",
sessionKey,
messages: [{ role: "user", content: "seed", timestamp: 1 } as AgentMessage],
tokenBudget: 2048,
modelId: "gpt-test",
});
await runBootstrap(sessionKey, contextEngine);
await runAssemble(sessionKey, contextEngine);
expect(assemble).toHaveBeenCalledWith(
expect.objectContaining({
@@ -127,28 +139,9 @@ describe("runEmbeddedAttempt context engine sessionKey forwarding", () => {
async (_params: { sessionKey?: string; messages: AgentMessage[] }) => ({ ingestedCount: 1 }),
);
await finalizeAttemptContextEngineTurn({
contextEngine: createTestContextEngine({
bootstrap,
assemble,
ingestBatch,
}),
promptError: false,
aborted: false,
yieldAborted: false,
sessionIdUsed: "embedded-session",
sessionKey,
sessionFile: "/tmp/session.jsonl",
messagesSnapshot: [
{ role: "user", content: "seed", timestamp: 1 } as AgentMessage,
{ role: "assistant", content: "done", timestamp: 2 } as unknown as AgentMessage,
],
await finalizeTurn(sessionKey, createTestContextEngine({ bootstrap, assemble, ingestBatch }), {
messagesSnapshot: [seedMessage, doneMessage],
prePromptMessageCount: 1,
tokenBudget: 2048,
runtimeContext: {},
runMaintenance: hoisted.runContextEngineMaintenanceMock,
sessionManager: hoisted.sessionManager,
warn: () => {},
});
expectCalledWithSessionKey(ingestBatch, sessionKey);
@@ -160,28 +153,9 @@ describe("runEmbeddedAttempt context engine sessionKey forwarding", () => {
ingested: true,
}));
await finalizeAttemptContextEngineTurn({
contextEngine: createTestContextEngine({
bootstrap,
assemble,
ingest,
}),
promptError: false,
aborted: false,
yieldAborted: false,
sessionIdUsed: "embedded-session",
sessionKey,
sessionFile: "/tmp/session.jsonl",
messagesSnapshot: [
{ role: "user", content: "seed", timestamp: 1 } as AgentMessage,
{ role: "assistant", content: "done", timestamp: 2 } as unknown as AgentMessage,
],
await finalizeTurn(sessionKey, createTestContextEngine({ bootstrap, assemble, ingest }), {
messagesSnapshot: [seedMessage, doneMessage],
prePromptMessageCount: 1,
tokenBudget: 2048,
runtimeContext: {},
runMaintenance: hoisted.runContextEngineMaintenanceMock,
sessionManager: hoisted.sessionManager,
warn: () => {},
});
expect(ingest).toHaveBeenCalled();
@@ -199,28 +173,7 @@ describe("runEmbeddedAttempt context engine sessionKey forwarding", () => {
throw new Error("afterTurn failed");
});
await finalizeAttemptContextEngineTurn({
contextEngine: createTestContextEngine({
bootstrap,
assemble,
afterTurn,
}),
promptError: false,
aborted: false,
yieldAborted: false,
sessionIdUsed: "embedded-session",
sessionKey,
sessionFile: "/tmp/session.jsonl",
messagesSnapshot: [
{ role: "assistant", content: "done", timestamp: 2 } as unknown as AgentMessage,
],
prePromptMessageCount: 0,
tokenBudget: 2048,
runtimeContext: {},
runMaintenance: hoisted.runContextEngineMaintenanceMock,
sessionManager: hoisted.sessionManager,
warn: () => {},
});
await finalizeTurn(sessionKey, createTestContextEngine({ bootstrap, assemble, afterTurn }));
expect(afterTurn).toHaveBeenCalled();
expect(hoisted.runContextEngineMaintenanceMock).not.toHaveBeenCalledWith(
@@ -231,9 +184,9 @@ describe("runEmbeddedAttempt context engine sessionKey forwarding", () => {
it("runs startup maintenance for existing sessions even without bootstrap()", async () => {
const { assemble } = createContextEngineBootstrapAndAssemble();
await runAttemptContextEngineBootstrap({
hadSessionFile: true,
contextEngine: createTestContextEngine({
await runBootstrap(
sessionKey,
createTestContextEngine({
assemble,
maintain: async () => ({
changed: false,
@@ -242,14 +195,7 @@ describe("runEmbeddedAttempt context engine sessionKey forwarding", () => {
reason: "test maintenance",
}),
}),
sessionId: "embedded-session",
sessionKey,
sessionFile: "/tmp/session.jsonl",
sessionManager: hoisted.sessionManager,
runtimeContext: {},
runMaintenance: hoisted.runContextEngineMaintenanceMock,
warn: () => {},
});
);
expect(hoisted.runContextEngineMaintenanceMock).toHaveBeenCalledWith(
expect.objectContaining({ reason: "bootstrap" }),
@@ -262,28 +208,9 @@ describe("runEmbeddedAttempt context engine sessionKey forwarding", () => {
throw new Error("ingestBatch failed");
});
await finalizeAttemptContextEngineTurn({
contextEngine: createTestContextEngine({
bootstrap,
assemble,
ingestBatch,
}),
promptError: false,
aborted: false,
yieldAborted: false,
sessionIdUsed: "embedded-session",
sessionKey,
sessionFile: "/tmp/session.jsonl",
messagesSnapshot: [
{ role: "user", content: "seed", timestamp: 1 } as AgentMessage,
{ role: "assistant", content: "done", timestamp: 2 } as unknown as AgentMessage,
],
await finalizeTurn(sessionKey, createTestContextEngine({ bootstrap, assemble, ingestBatch }), {
messagesSnapshot: [seedMessage, doneMessage],
prePromptMessageCount: 1,
tokenBudget: 2048,
runtimeContext: {},
runMaintenance: hoisted.runContextEngineMaintenanceMock,
sessionManager: hoisted.sessionManager,
warn: () => {},
});
expect(ingestBatch).toHaveBeenCalled();

View File

@@ -40,6 +40,102 @@ function getBranchMessages(sessionManager: SessionManager): AgentMessage[] {
.map((entry) => entry.message);
}
function appendSessionMessages(
sessionManager: SessionManager,
messages: AppendMessage[],
): string[] {
return messages.map((message) => sessionManager.appendMessage(message));
}
function createTextContent(text: string) {
return [{ type: "text", text }];
}
function createReadRewriteSession(options?: { tailAssistantText?: string }) {
const sessionManager = SessionManager.inMemory();
const entryIds = appendSessionMessages(sessionManager, [
asAppendMessage({
role: "user",
content: "read file",
timestamp: 1,
}),
asAppendMessage({
role: "assistant",
content: [{ type: "toolCall", id: "call_1", name: "read", arguments: {} }],
timestamp: 2,
}),
asAppendMessage({
role: "toolResult",
toolCallId: "call_1",
toolName: "read",
content: createTextContent("x".repeat(8_000)),
isError: false,
timestamp: 3,
}),
asAppendMessage({
role: "assistant",
content: createTextContent(options?.tailAssistantText ?? "summarized"),
timestamp: 4,
}),
]);
return {
sessionManager,
toolResultEntryId: entryIds[2],
tailAssistantEntryId: entryIds[3],
};
}
function createExecRewriteSession() {
const sessionManager = SessionManager.inMemory();
const entryIds = appendSessionMessages(sessionManager, [
asAppendMessage({
role: "user",
content: "run tool",
timestamp: 1,
}),
asAppendMessage({
role: "toolResult",
toolCallId: "call_1",
toolName: "exec",
content: createTextContent("before rewrite"),
isError: false,
timestamp: 2,
}),
asAppendMessage({
role: "assistant",
content: createTextContent("summarized"),
timestamp: 3,
}),
]);
return {
sessionManager,
toolResultEntryId: entryIds[1],
};
}
function createToolResultReplacement(toolName: string, text: string, timestamp: number) {
return {
role: "toolResult",
toolCallId: "call_1",
toolName,
content: createTextContent(text),
isError: false,
timestamp,
} as AgentMessage;
}
function findAssistantEntryByText(sessionManager: SessionManager, text: string) {
return sessionManager
.getBranch()
.find(
(entry) =>
entry.type === "message" &&
entry.message.role === "assistant" &&
Array.isArray(entry.message.content) &&
entry.message.content.some((part) => part.type === "text" && part.text === text),
);
}
beforeEach(async () => {
acquireSessionWriteLockMock.mockClear();
acquireSessionWriteLockReleaseMock.mockClear();
@@ -48,57 +144,14 @@ beforeEach(async () => {
describe("rewriteTranscriptEntriesInSessionManager", () => {
it("branches from the first replaced message and re-appends the remaining suffix", () => {
const sessionManager = SessionManager.inMemory();
sessionManager.appendMessage(
asAppendMessage({
role: "user",
content: "read file",
timestamp: 1,
}),
);
sessionManager.appendMessage(
asAppendMessage({
role: "assistant",
content: [{ type: "toolCall", id: "call_1", name: "read", arguments: {} }],
timestamp: 2,
}),
);
sessionManager.appendMessage(
asAppendMessage({
role: "toolResult",
toolCallId: "call_1",
toolName: "read",
content: [{ type: "text", text: "x".repeat(8_000) }],
isError: false,
timestamp: 3,
}),
);
sessionManager.appendMessage(
asAppendMessage({
role: "assistant",
content: [{ type: "text", text: "summarized" }],
timestamp: 4,
}),
);
const toolResultEntry = sessionManager
.getBranch()
.find((entry) => entry.type === "message" && entry.message.role === "toolResult");
expect(toolResultEntry).toBeDefined();
const { sessionManager, toolResultEntryId } = createReadRewriteSession();
const result = rewriteTranscriptEntriesInSessionManager({
sessionManager,
replacements: [
{
entryId: toolResultEntry!.id,
message: {
role: "toolResult",
toolCallId: "call_1",
toolName: "read",
content: [{ type: "text", text: "[externalized file_123]" }],
isError: false,
timestamp: 3,
},
entryId: toolResultEntryId,
message: createToolResultReplacement("read", "[externalized file_123]", 3),
},
],
});
@@ -123,48 +176,8 @@ describe("rewriteTranscriptEntriesInSessionManager", () => {
});
it("preserves active-branch labels after rewritten entries are re-appended", () => {
const sessionManager = SessionManager.inMemory();
sessionManager.appendMessage(
asAppendMessage({
role: "user",
content: "read file",
timestamp: 1,
}),
);
sessionManager.appendMessage(
asAppendMessage({
role: "assistant",
content: [{ type: "toolCall", id: "call_1", name: "read", arguments: {} }],
timestamp: 2,
}),
);
const toolResultEntryId = sessionManager.appendMessage(
asAppendMessage({
role: "toolResult",
toolCallId: "call_1",
toolName: "read",
content: [{ type: "text", text: "x".repeat(8_000) }],
isError: false,
timestamp: 3,
}),
);
sessionManager.appendMessage(
asAppendMessage({
role: "assistant",
content: [{ type: "text", text: "summarized" }],
timestamp: 4,
}),
);
const summaryEntry = sessionManager
.getBranch()
.find(
(entry) =>
entry.type === "message" &&
entry.message.role === "assistant" &&
Array.isArray(entry.message.content) &&
entry.message.content.some((part) => part.type === "text" && part.text === "summarized"),
);
const { sessionManager, toolResultEntryId } = createReadRewriteSession();
const summaryEntry = findAssistantEntryByText(sessionManager, "summarized");
expect(summaryEntry).toBeDefined();
sessionManager.appendLabelChange(summaryEntry!.id, "bookmark");
@@ -173,66 +186,24 @@ describe("rewriteTranscriptEntriesInSessionManager", () => {
replacements: [
{
entryId: toolResultEntryId,
message: {
role: "toolResult",
toolCallId: "call_1",
toolName: "read",
content: [{ type: "text", text: "[externalized file_123]" }],
isError: false,
timestamp: 3,
},
message: createToolResultReplacement("read", "[externalized file_123]", 3),
},
],
});
expect(result.changed).toBe(true);
const rewrittenSummaryEntry = sessionManager
.getBranch()
.find(
(entry) =>
entry.type === "message" &&
entry.message.role === "assistant" &&
Array.isArray(entry.message.content) &&
entry.message.content.some((part) => part.type === "text" && part.text === "summarized"),
);
const rewrittenSummaryEntry = findAssistantEntryByText(sessionManager, "summarized");
expect(rewrittenSummaryEntry).toBeDefined();
expect(sessionManager.getLabel(rewrittenSummaryEntry!.id)).toBe("bookmark");
expect(sessionManager.getBranch().some((entry) => entry.type === "label")).toBe(true);
});
it("remaps compaction keep markers when rewritten entries change ids", () => {
const sessionManager = SessionManager.inMemory();
sessionManager.appendMessage(
asAppendMessage({
role: "user",
content: "read file",
timestamp: 1,
}),
);
sessionManager.appendMessage(
asAppendMessage({
role: "assistant",
content: [{ type: "toolCall", id: "call_1", name: "read", arguments: {} }],
timestamp: 2,
}),
);
const toolResultEntryId = sessionManager.appendMessage(
asAppendMessage({
role: "toolResult",
toolCallId: "call_1",
toolName: "read",
content: [{ type: "text", text: "x".repeat(8_000) }],
isError: false,
timestamp: 3,
}),
);
const keptAssistantEntryId = sessionManager.appendMessage(
asAppendMessage({
role: "assistant",
content: [{ type: "text", text: "keep me" }],
timestamp: 4,
}),
);
const {
sessionManager,
toolResultEntryId,
tailAssistantEntryId: keptAssistantEntryId,
} = createReadRewriteSession({ tailAssistantText: "keep me" });
sessionManager.appendCompaction("summary", keptAssistantEntryId, 123);
const result = rewriteTranscriptEntriesInSessionManager({
@@ -240,14 +211,7 @@ describe("rewriteTranscriptEntriesInSessionManager", () => {
replacements: [
{
entryId: toolResultEntryId,
message: {
role: "toolResult",
toolCallId: "call_1",
toolName: "read",
content: [{ type: "text", text: "[externalized file_123]" }],
isError: false,
timestamp: 3,
},
message: createToolResultReplacement("read", "[externalized file_123]", 3),
},
],
});
@@ -270,31 +234,7 @@ describe("rewriteTranscriptEntriesInSessionManager", () => {
});
it("bypasses persistence hooks when replaying rewritten messages", () => {
const sessionManager = SessionManager.inMemory();
sessionManager.appendMessage(
asAppendMessage({
role: "user",
content: "run tool",
timestamp: 1,
}),
);
const toolResultEntryId = sessionManager.appendMessage(
asAppendMessage({
role: "toolResult",
toolCallId: "call_1",
toolName: "exec",
content: [{ type: "text", text: "before rewrite" }],
isError: false,
timestamp: 2,
}),
);
sessionManager.appendMessage(
asAppendMessage({
role: "assistant",
content: [{ type: "text", text: "summarized" }],
timestamp: 3,
}),
);
const { sessionManager, toolResultEntryId } = createExecRewriteSession();
installSessionToolResultGuard(sessionManager, {
transformToolResultForPersistence: (message) => ({
...(message as Extract<AgentMessage, { role: "toolResult" }>),
@@ -309,14 +249,7 @@ describe("rewriteTranscriptEntriesInSessionManager", () => {
replacements: [
{
entryId: toolResultEntryId,
message: {
role: "toolResult",
toolCallId: "call_1",
toolName: "exec",
content: [{ type: "text", text: "[exact replacement]" }],
isError: false,
timestamp: 2,
},
message: createToolResultReplacement("exec", "[exact replacement]", 2),
},
],
});
@@ -341,29 +274,7 @@ describe("rewriteTranscriptEntriesInSessionManager", () => {
describe("rewriteTranscriptEntriesInSessionFile", () => {
it("emits transcript updates when the active branch changes", async () => {
const sessionFile = "/tmp/session.jsonl";
const sessionManager = SessionManager.inMemory();
sessionManager.appendMessage(
asAppendMessage({
role: "user",
content: "run tool",
timestamp: 1,
}),
);
sessionManager.appendMessage(
asAppendMessage({
role: "toolResult",
toolCallId: "call_1",
toolName: "exec",
content: [{ type: "text", text: "y".repeat(6_000) }],
isError: false,
timestamp: 2,
}),
);
const toolResultEntry = sessionManager
.getBranch()
.find((entry) => entry.type === "message" && entry.message.role === "toolResult");
expect(toolResultEntry).toBeDefined();
const { sessionManager, toolResultEntryId } = createExecRewriteSession();
const openSpy = vi
.spyOn(SessionManager, "open")
@@ -378,15 +289,8 @@ describe("rewriteTranscriptEntriesInSessionFile", () => {
request: {
replacements: [
{
entryId: toolResultEntry!.id,
message: {
role: "toolResult",
toolCallId: "call_1",
toolName: "exec",
content: [{ type: "text", text: "[file_ref:file_abc]" }],
isError: false,
timestamp: 2,
},
entryId: toolResultEntryId,
message: createToolResultReplacement("exec", "[file_ref:file_abc]", 2),
},
],
},