import type { AgentMessage } from
"@mariozechner/pi-agent-core" ;
import { beforeAll, beforeEach, describe, expect, it, vi } from
"vitest" ;
import {
applyExtraParamsToAgentMock,
contextEngineCompactMock,
createOpenClawCodingToolsMock,
ensureRuntimePluginsLoaded,
estimateTokensMock,
getMemorySearchManagerMock,
hookRunner,
loadCompactHooksHarness,
maybeCompactAgentHarnessSessionMock,
registerProviderStreamForModelMock,
resolveContextEngineMock,
resolveEmbeddedAgentStreamFnMock,
resolveMemorySearchConfigMock,
resolveModelMock,
resolveSandboxContextMock,
resolveSessionAgentIdMock,
resetCompactHooksHarnessMocks,
resetCompactSessionStateMocks,
sessionAbortCompactionMock,
sessionMessages,
sessionCompactImpl,
triggerInternalHook,
} from
"./compact.hooks.harness.js" ;
let compactEmbeddedPiSessionDirect:
typeof import (
"./compact.js" ).compactEmbeddedPi
SessionDirect;
let compactEmbeddedPiSession: typeof import ("./compact.queued.js" ).compactEmbeddedPiSession;
let compactTesting: typeof import ("./compact.js" ).__testing;
let onSessionTranscriptUpdate: typeof import ("../../sessions/transcript-events.js" ).onSessionTranscriptUpdate;
const TEST_SESSION_ID = "session-1" ;
const TEST_SESSION_KEY = "agent:main:session-1" ;
const TEST_SESSION_FILE = "/tmp/session.jsonl" ;
const TEST_WORKSPACE_DIR = "/tmp" ;
const TEST_CUSTOM_INSTRUCTIONS = "focus on decisions" ;
type SessionHookEvent = {
type?: string;
action?: string;
sessionKey?: string;
context?: Record<string, unknown>;
};
type PostCompactionSyncParams = {
reason: string;
sessionFiles: string[];
};
type PostCompactionSync = (params?: unknown) => Promise<void >;
type Deferred<T> = {
promise: Promise<T>;
resolve: (value: T) => void ;
};
function createDeferred<T>(): Deferred<T> {
let resolve!: (value: T) => void ;
const promise = new Promise<T>((promiseResolve) => {
resolve = promiseResolve;
});
return { promise, resolve };
}
function mockResolvedModel() {
resolveModelMock.mockReset();
resolveModelMock.mockReturnValue({
model: { provider: "openai" , api: "responses" , id: "fake" , input: [] },
error: null ,
authStorage: { setRuntimeApiKey: vi.fn() },
modelRegistry: {},
});
}
function compactionConfig(mode: "await" | "off" | "async" ) {
return {
agents: {
defaults: {
compaction: {
postIndexSync: mode,
},
},
},
} as never;
}
function wrappedCompactionArgs(overrides: Record<string, unknown> = {}) {
return {
sessionId: TEST_SESSION_ID,
sessionKey: TEST_SESSION_KEY,
sessionFile: TEST_SESSION_FILE,
workspaceDir: TEST_WORKSPACE_DIR,
customInstructions: TEST_CUSTOM_INSTRUCTIONS,
enqueue: async <T>(task: () => Promise<T> | T) => await task(),
...overrides,
};
}
const sessionHook = (action: string): SessionHookEvent | undefined =>
triggerInternalHook.mock.calls.find((call) => {
const event = call[0 ] as SessionHookEvent | undefined;
return event?.type === "session" && event.action === action;
})?.[0 ] as SessionHookEvent | undefined;
async function runCompactionHooks(params: { sessionKey?: string; messageProvider?: string }) {
const originalMessages = sessionMessages.slice(1 ) as AgentMessage[];
const currentMessages = sessionMessages.slice(1 ) as AgentMessage[];
const beforeMetrics = compactTesting.buildBeforeCompactionHookMetrics({
originalMessages,
currentMessages,
estimateTokensFn: estimateTokensMock as (message: AgentMessage) => number,
});
const hookState = await compactTesting.runBeforeCompactionHooks({
hookRunner,
sessionId: TEST_SESSION_ID,
sessionKey: params.sessionKey,
sessionAgentId: "main" ,
workspaceDir: TEST_WORKSPACE_DIR,
messageProvider: params.messageProvider,
metrics: beforeMetrics,
});
await compactTesting.runAfterCompactionHooks({
hookRunner,
sessionId: TEST_SESSION_ID,
sessionAgentId: "main" ,
hookSessionKey: hookState.hookSessionKey,
missingSessionKey: hookState.missingSessionKey,
workspaceDir: TEST_WORKSPACE_DIR,
messageProvider: params.messageProvider,
messageCountAfter: 1 ,
tokensAfter: 10 ,
compactedCount: 1 ,
sessionFile: TEST_SESSION_FILE,
summaryLength: "summary" .length,
tokensBefore: 120 ,
firstKeptEntryId: "entry-1" ,
});
}
beforeAll(async () => {
const loaded = await loadCompactHooksHarness();
compactEmbeddedPiSessionDirect = loaded.compactEmbeddedPiSessionDirect;
compactEmbeddedPiSession = loaded.compactEmbeddedPiSession;
compactTesting = loaded.__testing;
onSessionTranscriptUpdate = loaded.onSessionTranscriptUpdate;
});
beforeEach(() => {
resetCompactHooksHarnessMocks();
});
describe("compactEmbeddedPiSessionDirect hooks" , () => {
beforeEach(() => {
ensureRuntimePluginsLoaded.mockReset();
triggerInternalHook.mockClear();
hookRunner.hasHooks.mockReset();
hookRunner.runBeforeCompaction.mockReset();
hookRunner.runAfterCompaction.mockReset();
mockResolvedModel();
sessionCompactImpl.mockReset();
sessionCompactImpl.mockResolvedValue({
summary: "summary" ,
firstKeptEntryId: "entry-1" ,
tokensBefore: 120 ,
details: { ok: true },
});
resetCompactSessionStateMocks();
});
it("bootstraps runtime plugins with the resolved workspace" , async () => {
// This assertion only cares about bootstrap wiring, so stop before the
// rest of the compaction pipeline can pull in unrelated runtime surfaces.
resolveModelMock.mockReturnValue({
model: undefined,
error: "stop after bootstrap" ,
authStorage: { setRuntimeApiKey: vi.fn() },
modelRegistry: {},
} as never);
await compactEmbeddedPiSessionDirect({
sessionId: "session-1" ,
sessionFile: "/tmp/session.jsonl" ,
workspaceDir: "/tmp/workspace" ,
});
expect(ensureRuntimePluginsLoaded).toHaveBeenCalledWith({
config: undefined,
workspaceDir: "/tmp/workspace" ,
});
});
it("forwards gateway subagent binding opt-in during compaction bootstrap" , async () => {
// Coding-tool forwarding is covered elsewhere; this compaction test only
// owns the runtime bootstrap wiring.
resolveModelMock.mockReturnValue({
model: undefined,
error: "stop after bootstrap" ,
authStorage: { setRuntimeApiKey: vi.fn() },
modelRegistry: {},
} as never);
await compactEmbeddedPiSessionDirect({
sessionId: "session-1" ,
sessionFile: "/tmp/session.jsonl" ,
workspaceDir: "/tmp/workspace" ,
allowGatewaySubagentBinding: true ,
});
expect(ensureRuntimePluginsLoaded).toHaveBeenCalledWith({
config: undefined,
workspaceDir: "/tmp/workspace" ,
allowGatewaySubagentBinding: true ,
});
});
it("uses sandboxSessionKey only for compaction sandbox resolution" , async () => {
await compactEmbeddedPiSessionDirect({
sessionId: "session-1" ,
sessionKey: "agent:main:main" ,
sandboxSessionKey: "agent:main:telegram:default:direct:12345" ,
sessionFile: "/tmp/session.jsonl" ,
workspaceDir: "/tmp/workspace" ,
});
expect(resolveSandboxContextMock).toHaveBeenCalledWith({
config: undefined,
sessionKey: "agent:main:telegram:default:direct:12345" ,
workspaceDir: "/tmp/workspace" ,
});
});
it("routes compaction through shared stream resolution and extra params" , async () => {
const resolvedStreamFn = vi.fn();
resolveEmbeddedAgentStreamFnMock.mockReturnValue(resolvedStreamFn);
applyExtraParamsToAgentMock.mockReturnValue({
effectiveExtraParams: { transport: "websocket" },
});
const session = {
agent: {
streamFn: vi.fn(),
},
messages: [{ role: "user" , content: "hello" }],
};
compactTesting.prepareCompactionSessionAgent({
session: session as never,
providerStreamFn: vi.fn(),
shouldUseWebSocketTransport: false ,
sessionId: "session-1" ,
signal: new AbortController().signal,
effectiveModel: { provider: "openai" , id: "fake" , api: "responses" , input: [] } as never,
resolvedApiKey: undefined,
authStorage: { setRuntimeApiKey: vi.fn() },
config: undefined,
provider: "openai" ,
modelId: "gpt-5.4" ,
thinkLevel: "off" ,
sessionAgentId: "main" ,
effectiveWorkspace: "/tmp/workspace" ,
agentDir: "/tmp/workspace" ,
});
expect(resolveEmbeddedAgentStreamFnMock).toHaveBeenCalledWith(
expect.objectContaining({
currentStreamFn: expect.any(Function ),
sessionId: "session-1" ,
}),
);
expect(applyExtraParamsToAgentMock).toHaveBeenCalledWith(
expect.objectContaining({
streamFn: resolvedStreamFn,
}),
undefined,
"openai" ,
"gpt-5.4" ,
undefined,
"off" ,
"main" ,
"/tmp/workspace" ,
expect.objectContaining({
provider: "openai" ,
id: "fake" ,
api: "responses" ,
}),
"/tmp/workspace" ,
undefined,
undefined,
);
});
it("preserves full sender identity when building compaction tools" , async () => {
await compactEmbeddedPiSessionDirect({
sessionId: "session-1" ,
sessionFile: "/tmp/session.jsonl" ,
workspaceDir: "/tmp/workspace" ,
senderId: "sender-1" ,
senderName: "Alice" ,
senderUsername: "alice_u" ,
senderE164: "+15551234567" ,
});
expect(createOpenClawCodingToolsMock).toHaveBeenCalledWith(
expect.objectContaining({
senderId: "sender-1" ,
senderName: "Alice" ,
senderUsername: "alice_u" ,
senderE164: "+15551234567" ,
}),
);
});
it("emits internal + plugin compaction hooks with counts" , async () => {
hookRunner.hasHooks.mockReturnValue(true );
await runCompactionHooks({
sessionKey: TEST_SESSION_KEY,
messageProvider: "telegram" ,
});
expect(sessionHook("compact:before" )).toMatchObject({
type: "session" ,
action: "compact:before" ,
});
const beforeContext = sessionHook("compact:before" )?.context;
const afterContext = sessionHook("compact:after" )?.context;
expect(beforeContext).toMatchObject({
messageCount: 2 ,
tokenCount: 20 ,
messageCountOriginal: 2 ,
tokenCountOriginal: 20 ,
});
expect(afterContext).toMatchObject({
messageCount: 1 ,
compactedCount: 1 ,
});
expect(afterContext?.compactedCount).toBe(
(beforeContext?.messageCountOriginal as number) - (afterContext?.messageCount as number),
);
expect(hookRunner.runBeforeCompaction).toHaveBeenCalledWith(
expect.objectContaining({
messageCount: 2 ,
tokenCount: 20 ,
}),
expect.objectContaining({ sessionKey: "agent:main:session-1" , messageProvider: "telegram" }),
);
expect(hookRunner.runAfterCompaction).toHaveBeenCalledWith(
{
messageCount: 1 ,
tokenCount: 10 ,
compactedCount: 1 ,
sessionFile: "/tmp/session.jsonl" ,
},
expect.objectContaining({ sessionKey: "agent:main:session-1" , messageProvider: "telegram" }),
);
});
it("uses sessionId as hook session key fallback when sessionKey is missing" , async () => {
hookRunner.hasHooks.mockReturnValue(true );
await runCompactionHooks({});
expect(sessionHook("compact:before" )?.sessionKey).toBe("session-1" );
expect(sessionHook("compact:after" )?.sessionKey).toBe("session-1" );
expect(hookRunner.runBeforeCompaction).toHaveBeenCalledWith(
expect.any(Object),
expect.objectContaining({ sessionKey: "session-1" }),
);
expect(hookRunner.runAfterCompaction).toHaveBeenCalledWith(
expect.any(Object),
expect.objectContaining({ sessionKey: "session-1" }),
);
});
it("applies validated transcript before hooks even when it becomes empty" , async () => {
hookRunner.hasHooks.mockReturnValue(true );
const beforeMetrics = compactTesting.buildBeforeCompactionHookMetrics({
originalMessages: [],
currentMessages: [],
estimateTokensFn: estimateTokensMock as (message: AgentMessage) => number,
});
await compactTesting.runBeforeCompactionHooks({
hookRunner,
sessionId: "session-1" ,
sessionKey: "agent:main:session-1" ,
sessionAgentId: "main" ,
workspaceDir: "/tmp" ,
metrics: beforeMetrics,
});
const beforeContext = sessionHook("compact:before" )?.context;
expect(beforeContext).toMatchObject({
messageCountOriginal: 0 ,
tokenCountOriginal: 0 ,
messageCount: 0 ,
tokenCount: 0 ,
});
});
it("emits a transcript update after successful compaction" , async () => {
const listener = vi.fn();
const cleanup = onSessionTranscriptUpdate(listener);
try {
await compactTesting.runPostCompactionSideEffects({
sessionKey: "agent:main:session-1" ,
sessionFile: " /tmp/session.jsonl " ,
});
expect(listener).toHaveBeenCalledTimes(1 );
expect(listener).toHaveBeenCalledWith({ sessionFile: "/tmp/session.jsonl" });
} finally {
cleanup();
}
});
it("preserves tokensAfter when full-session context exceeds result.tokensBefore" , async () => {
estimateTokensMock.mockImplementation((message: unknown) => {
const role = (message as { role?: string }).role;
if (role === "user" ) {
return 30 ;
}
if (role === "assistant" ) {
return 20 ;
}
return 5 ;
});
const tokensAfter = compactTesting.estimateTokensAfterCompaction({
messagesAfter: [{ role: "user" , content: "kept ask" }] as AgentMessage[],
fullSessionTokensBefore: 55 ,
estimateTokensFn: estimateTokensMock as (message: AgentMessage) => number,
});
expect(tokensAfter).toBe(30 );
});
it("treats pre-compaction token estimation failures as a no-op sanity check" , async () => {
estimateTokensMock.mockImplementation((message: unknown) => {
const role = (message as { role?: string }).role;
if (role === "assistant" ) {
throw new Error("legacy message" );
}
if (role === "user" ) {
return 30 ;
}
return 5 ;
});
const beforeMetrics = compactTesting.buildBeforeCompactionHookMetrics({
originalMessages: sessionMessages as AgentMessage[],
currentMessages: sessionMessages as AgentMessage[],
estimateTokensFn: estimateTokensMock as (message: AgentMessage) => number,
});
const tokensAfter = compactTesting.estimateTokensAfterCompaction({
messagesAfter: [{ role: "user" , content: "kept ask" }] as AgentMessage[],
fullSessionTokensBefore: 0 ,
estimateTokensFn: estimateTokensMock as (message: AgentMessage) => number,
});
expect(beforeMetrics.tokenCountOriginal).toBeUndefined();
expect(beforeMetrics.tokenCountBefore).toBeUndefined();
expect(tokensAfter).toBe(30 );
});
it("skips sync in await mode when postCompactionForce is false" , async () => {
const sync = vi.fn(async () => {});
getMemorySearchManagerMock.mockResolvedValue({ manager: { sync } });
resolveMemorySearchConfigMock.mockReturnValue({
sources: ["sessions" ],
sync: {
sessions: {
postCompactionForce: false ,
},
},
});
await compactTesting.runPostCompactionSideEffects({
config: compactionConfig("await" ),
sessionKey: TEST_SESSION_KEY,
sessionFile: TEST_SESSION_FILE,
});
expect(resolveSessionAgentIdMock).toHaveBeenCalledWith({
sessionKey: TEST_SESSION_KEY,
config: expect.any(Object),
});
expect(getMemorySearchManagerMock).not.toHaveBeenCalled();
expect(sync).not.toHaveBeenCalled();
});
it("awaits post-compaction memory sync in await mode when postCompactionForce is true" , async () => {
const syncStarted = createDeferred<PostCompactionSyncParams>();
const syncRelease = createDeferred<void >();
const sync = vi.fn<PostCompactionSync>(async (params) => {
syncStarted.resolve(params as PostCompactionSyncParams);
await syncRelease.promise;
});
getMemorySearchManagerMock.mockResolvedValue({ manager: { sync } });
let settled = false ;
const resultPromise = compactTesting.runPostCompactionSideEffects({
config: compactionConfig("await" ),
sessionKey: TEST_SESSION_KEY,
sessionFile: TEST_SESSION_FILE,
});
void resultPromise.then(() => {
settled = true ;
});
await expect(syncStarted.promise).resolves.toEqual({
reason: "post-compaction" ,
sessionFiles: [TEST_SESSION_FILE],
});
expect(settled).toBe(false );
syncRelease.resolve(undefined);
await resultPromise;
expect(settled).toBe(true );
});
it("skips post-compaction memory sync when the mode is off" , async () => {
const sync = vi.fn(async () => {});
getMemorySearchManagerMock.mockResolvedValue({ manager: { sync } });
await compactTesting.runPostCompactionSideEffects({
config: compactionConfig("off" ),
sessionKey: TEST_SESSION_KEY,
sessionFile: TEST_SESSION_FILE,
});
expect(resolveSessionAgentIdMock).not.toHaveBeenCalled();
expect(getMemorySearchManagerMock).not.toHaveBeenCalled();
expect(sync).not.toHaveBeenCalled();
});
it("fires post-compaction memory sync without awaiting it in async mode" , async () => {
const sync = vi.fn<PostCompactionSync>(async () => {});
const managerRequested = createDeferred<void >();
const managerGate = createDeferred<{ manager: { sync: PostCompactionSync } }>();
const syncStarted = createDeferred<PostCompactionSyncParams>();
sync.mockImplementation(async (params) => {
syncStarted.resolve(params as PostCompactionSyncParams);
});
getMemorySearchManagerMock.mockImplementation(async () => {
managerRequested.resolve(undefined);
return await managerGate.promise;
});
let settled = false ;
const resultPromise = compactTesting.runPostCompactionSideEffects({
config: compactionConfig("async" ),
sessionKey: TEST_SESSION_KEY,
sessionFile: TEST_SESSION_FILE,
});
await managerRequested.promise;
void resultPromise.then(() => {
settled = true ;
});
await resultPromise;
expect(getMemorySearchManagerMock).toHaveBeenCalledTimes(1 );
expect(settled).toBe(true );
expect(sync).not.toHaveBeenCalled();
managerGate.resolve({ manager: { sync } });
await expect(syncStarted.promise).resolves.toEqual({
reason: "post-compaction" ,
sessionFiles: [TEST_SESSION_FILE],
});
});
it("skips compaction when the transcript only contains boilerplate replies and tool output" , async () => {
const messages = [
{ role: "user" , content: "<b>HEARTBEAT_OK</b>" , timestamp: 1 },
{
role: "toolResult" ,
toolCallId: "t1" ,
toolName: "exec" ,
content: [{ type: "text" , text: "checked" }],
isError: false ,
timestamp: 2 ,
},
] as AgentMessage[];
expect(compactTesting.containsRealConversationMessages(messages)).toBe(false );
});
it("skips compaction when the transcript only contains heartbeat boilerplate and reasoning blocks" , async () => {
const messages = [
{ role: "user" , content: "<b>HEARTBEAT_OK</b>" , timestamp: 1 },
{
role: "assistant" ,
content: [{ type: "thinking" , thinking: "checking" }],
timestamp: 2 ,
},
] as AgentMessage[];
expect(compactTesting.containsRealConversationMessages(messages)).toBe(false );
});
it("does not treat assistant-only tool-call blocks as meaningful conversation" , () => {
expect(
compactTesting.hasMeaningfulConversationContent({
role: "assistant" ,
content: [{ type: "toolCall" , id: "call_1" , name: "exec" , arguments: {} }],
} as AgentMessage),
).toBe(false );
});
it("counts tool output as real only when a meaningful user ask exists in the lookback window" , () => {
const heartbeatToolResultWindow = [
{ role: "user" , content: "<b>HEARTBEAT_OK</b>" },
{
role: "toolResult" ,
toolCallId: "t1" ,
toolName: "exec" ,
content: [{ type: "text" , text: "checked" }],
},
] as AgentMessage[];
expect(
compactTesting.hasRealConversationContent(
heartbeatToolResultWindow[1 ],
heartbeatToolResultWindow,
1 ,
),
).toBe(false );
const realAskToolResultWindow = [
{ role: "assistant" , content: "NO_REPLY" },
{ role: "user" , content: "please inspect the failing PR" },
{
role: "toolResult" ,
toolCallId: "t2" ,
toolName: "exec" ,
content: [{ type: "text" , text: "checked" }],
},
] as AgentMessage[];
expect(
compactTesting.hasRealConversationContent(
realAskToolResultWindow[2 ],
realAskToolResultWindow,
2 ,
),
).toBe(true );
});
it("registers the Ollama api provider before compaction" , async () => {
const streamFn = vi.fn();
registerProviderStreamForModelMock.mockReturnValue(streamFn);
const result = compactTesting.resolveCompactionProviderStream({
effectiveModel: {
provider: "ollama" ,
api: "ollama" ,
id: "qwen3:8b" ,
input: ["text" ],
baseUrl: "http://127.0.0.1:11434 ",
headers: { Authorization: "Bearer ollama-cloud" },
} as never,
config: undefined,
agentDir: "/tmp" ,
effectiveWorkspace: "/tmp" ,
});
expect(result).toBe(streamFn);
expect(registerProviderStreamForModelMock).toHaveBeenCalledWith(
expect.objectContaining({
model: expect.objectContaining({
provider: "ollama" ,
api: "ollama" ,
id: "qwen3:8b" ,
}),
agentDir: "/tmp" ,
workspaceDir: "/tmp" ,
}),
);
});
it("aborts in-flight compaction when the caller abort signal fires" , async () => {
const { compactWithSafetyTimeout } = await vi.importActual<
typeof import ("./compaction-safety-timeout.js" )
>("./compaction-safety-timeout.js" );
const controller = new AbortController();
const compactStarted = createDeferred<void >();
const resultPromise = compactWithSafetyTimeout(
async () => {
compactStarted.resolve(undefined);
return await new Promise<never>(() => {});
},
30 _000 ,
{
abortSignal: controller.signal,
onCancel: () => {
sessionAbortCompactionMock();
},
},
);
await compactStarted.promise;
controller.abort(new Error("request timed out" ));
await expect(resultPromise).rejects.toThrow("request timed out" );
expect(sessionAbortCompactionMock).toHaveBeenCalledTimes(1 );
});
});
describe("compactEmbeddedPiSession hooks (ownsCompaction engine)" , () => {
beforeEach(() => {
hookRunner.hasHooks.mockReset();
hookRunner.runBeforeCompaction.mockReset();
hookRunner.runAfterCompaction.mockReset();
resolveContextEngineMock.mockReset();
resolveContextEngineMock.mockResolvedValue({
info: { ownsCompaction: true },
compact: contextEngineCompactMock,
});
contextEngineCompactMock.mockReset();
contextEngineCompactMock.mockResolvedValue({
ok: true ,
compacted: true ,
reason: undefined,
result: { summary: "engine-summary" , tokensAfter: 50 },
});
mockResolvedModel();
});
it("fires before_compaction with sentinel -1 and after_compaction on success" , async () => {
hookRunner.hasHooks.mockReturnValue(true );
const result = await compactEmbeddedPiSession(
wrappedCompactionArgs({
messageChannel: "telegram" ,
}),
);
expect(result.ok).toBe(true );
expect(result.compacted).toBe(true );
expect(hookRunner.runBeforeCompaction).toHaveBeenCalledWith(
{ messageCount: -1 , sessionFile: TEST_SESSION_FILE },
expect.objectContaining({
sessionKey: TEST_SESSION_KEY,
messageProvider: "telegram" ,
}),
);
expect(hookRunner.runAfterCompaction).toHaveBeenCalledWith(
{
messageCount: -1 ,
compactedCount: -1 ,
tokenCount: 50 ,
sessionFile: TEST_SESSION_FILE,
},
expect.objectContaining({
sessionKey: TEST_SESSION_KEY,
messageProvider: "telegram" ,
}),
);
});
it("emits a transcript update and post-compaction memory sync on the engine-owned path" , async () => {
const listener = vi.fn();
const cleanup = onSessionTranscriptUpdate(listener);
const sync = vi.fn(async () => {});
getMemorySearchManagerMock.mockResolvedValue({ manager: { sync } });
try {
const result = await compactEmbeddedPiSession(
wrappedCompactionArgs({
sessionFile: ` ${TEST_SESSION_FILE} `,
config: compactionConfig("await" ),
}),
);
expect(result.ok).toBe(true );
expect(listener).toHaveBeenCalledTimes(1 );
expect(listener).toHaveBeenCalledWith({ sessionFile: TEST_SESSION_FILE });
expect(sync).toHaveBeenCalledWith({
reason: "post-compaction" ,
sessionFiles: [TEST_SESSION_FILE],
});
} finally {
cleanup();
}
});
it("runs maintain after successful compaction with a transcript rewrite helper" , async () => {
const maintain = vi.fn(async (_params?: unknown) => ({
changed: false ,
bytesFreed: 0 ,
rewrittenEntries: 0 ,
}));
resolveContextEngineMock.mockResolvedValue({
info: { ownsCompaction: true },
compact: contextEngineCompactMock,
maintain,
} as never);
const result = await compactEmbeddedPiSession(wrappedCompactionArgs());
expect(result.ok).toBe(true );
expect(maintain).toHaveBeenCalledWith(
expect.objectContaining({
sessionKey: TEST_SESSION_KEY,
sessionFile: TEST_SESSION_FILE,
runtimeContext: expect.objectContaining({
workspaceDir: TEST_WORKSPACE_DIR,
}),
}),
);
const runtimeContext = (
maintain.mock.calls[0 ]?.[0 ] as { runtimeContext?: Record<string, unknown> } | undefined
)?.runtimeContext;
expect(typeof runtimeContext?.rewriteTranscriptEntries).toBe("function" );
});
it("resolves the effective compaction model before manual engine-owned compaction" , async () => {
await compactEmbeddedPiSession(
wrappedCompactionArgs({
config: {
agents: {
defaults: {
compaction: {
model: "anthropic/claude-opus-4-6" ,
},
},
},
},
provider: "openai-codex" ,
model: "gpt-5.4" ,
authProfileId: "openai:p1" ,
}),
);
expect(resolveModelMock).toHaveBeenCalledWith(
"anthropic" ,
"claude-opus-4-6" ,
expect.any(String),
expect.anything(),
);
expect(contextEngineCompactMock).toHaveBeenCalledWith(
expect.objectContaining({
runtimeContext: expect.objectContaining({
provider: "anthropic" ,
model: "claude-opus-4-6" ,
authProfileId: undefined,
}),
}),
);
});
it("passes resolved context-engine runtime context to harness compaction" , async () => {
maybeCompactAgentHarnessSessionMock.mockResolvedValueOnce({
ok: true ,
compacted: true ,
result: {
summary: "harness" ,
firstKeptEntryId: "entry-1" ,
tokensBefore: 100 ,
},
});
const result = await compactEmbeddedPiSession(
wrappedCompactionArgs({
provider: "openai-codex" ,
model: "gpt-5.4" ,
authProfileId: "openai:p1" ,
currentTokenCount: 333 ,
}),
);
expect(result.ok).toBe(true );
expect(maybeCompactAgentHarnessSessionMock).toHaveBeenCalledWith(
expect.objectContaining({
contextEngine: expect.anything(),
contextTokenBudget: expect.any(Number),
contextEngineRuntimeContext: expect.objectContaining({
sessionKey: TEST_SESSION_KEY,
workspaceDir: TEST_WORKSPACE_DIR,
provider: "openai-codex" ,
model: "gpt-5.4" ,
authProfileId: "openai:p1" ,
currentTokenCount: 333 ,
}),
}),
);
});
it("does not fire after_compaction when compaction fails" , async () => {
hookRunner.hasHooks.mockReturnValue(true );
const sync = vi.fn(async () => {});
getMemorySearchManagerMock.mockResolvedValue({ manager: { sync } });
contextEngineCompactMock.mockResolvedValue({
ok: false ,
compacted: false ,
reason: "nothing to compact" ,
result: undefined,
});
const result = await compactEmbeddedPiSession(wrappedCompactionArgs());
expect(result.ok).toBe(false );
expect(hookRunner.runBeforeCompaction).toHaveBeenCalled();
expect(hookRunner.runAfterCompaction).not.toHaveBeenCalled();
expect(sync).not.toHaveBeenCalled();
});
it("does not duplicate transcript updates or sync in the wrapper when the engine delegates compaction" , async () => {
const listener = vi.fn();
const cleanup = onSessionTranscriptUpdate(listener);
const sync = vi.fn(async () => {});
getMemorySearchManagerMock.mockResolvedValue({ manager: { sync } });
resolveContextEngineMock.mockResolvedValue({
info: { ownsCompaction: false },
compact: contextEngineCompactMock,
});
try {
const result = await compactEmbeddedPiSession(
wrappedCompactionArgs({
config: compactionConfig("await" ),
}),
);
expect(result.ok).toBe(true );
expect(listener).not.toHaveBeenCalled();
expect(sync).not.toHaveBeenCalled();
} finally {
cleanup();
}
});
it("catches and logs hook exceptions without aborting compaction" , async () => {
hookRunner.hasHooks.mockReturnValue(true );
hookRunner.runBeforeCompaction.mockRejectedValue(new Error("hook boom" ));
const result = await compactEmbeddedPiSession(wrappedCompactionArgs());
expect(result.ok).toBe(true );
expect(result.compacted).toBe(true );
expect(contextEngineCompactMock).toHaveBeenCalled();
});
});
Messung V0.5 in Prozent C=100 H=98 G=98
¤ Dauer der Verarbeitung: 0.7 Sekunden
¤
*© Formatika GbR, Deutschland