Skip to content

Commit d26feb5

Browse files
author
Eva
committed
fix(context-engine): honor assembled prompt authority
1 parent ea9f172 commit d26feb5

4 files changed

Lines changed: 78 additions & 2 deletions

File tree

src/agents/pi-embedded-runner/run/attempt.spawn-workspace.context-engine.test.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,73 @@ describe("runEmbeddedAttempt context engine sessionKey forwarding", () => {
277277
expect(contextCompiled?.data?.systemPrompt).toContain("internal heartbeat event");
278278
});
279279

280+
it("uses assembled context as the default precheck authority", async () => {
281+
let sawPrompt = false;
282+
const hugeHistory = "large raw history ".repeat(25_000);
283+
284+
const result = await createContextEngineAttemptRunner({
285+
contextEngine: createTestContextEngine({
286+
assemble: async () => ({
287+
messages: [
288+
{ role: "user", content: "small assembled context", timestamp: 1 },
289+
] as AgentMessage[],
290+
estimatedTokens: 8,
291+
}),
292+
}),
293+
sessionKey,
294+
tempPaths,
295+
sessionMessages: [{ role: "user", content: hugeHistory, timestamp: 1 }] as AgentMessage[],
296+
attemptOverrides: {
297+
contextTokenBudget: 500,
298+
},
299+
sessionPrompt: async (session) => {
300+
sawPrompt = true;
301+
session.messages = [
302+
...session.messages,
303+
{ role: "assistant", content: "done", timestamp: 2 },
304+
];
305+
},
306+
});
307+
308+
expect(sawPrompt).toBe(true);
309+
expect(result.promptError).toBeNull();
310+
expect(result.promptErrorSource).toBeNull();
311+
});
312+
313+
it("honors context engines that opt into preassembly overflow authority", async () => {
314+
let sawPrompt = false;
315+
const hugeHistory = "large raw history ".repeat(25_000);
316+
317+
const result = await createContextEngineAttemptRunner({
318+
contextEngine: createTestContextEngine({
319+
assemble: async () => ({
320+
messages: [
321+
{ role: "user", content: "small assembled context", timestamp: 1 },
322+
] as AgentMessage[],
323+
estimatedTokens: 8,
324+
promptAuthority: "preassembly_may_overflow",
325+
}),
326+
}),
327+
sessionKey,
328+
tempPaths,
329+
sessionMessages: [{ role: "user", content: hugeHistory, timestamp: 1 }] as AgentMessage[],
330+
attemptOverrides: {
331+
contextTokenBudget: 500,
332+
},
333+
sessionPrompt: async (session) => {
334+
sawPrompt = true;
335+
session.messages = [
336+
...session.messages,
337+
{ role: "assistant", content: "done", timestamp: 2 },
338+
];
339+
},
340+
});
341+
342+
expect(sawPrompt).toBe(false);
343+
expect(result.promptErrorSource).toBe("precheck");
344+
expect(result.preflightRecovery?.route).toBe("compact_only");
345+
});
346+
280347
it("keeps gateway model runs independent from agent context and session history", async () => {
281348
const bootstrap = vi.fn(async () => ({ bootstrapped: true }));
282349
const assemble = vi.fn(async ({ messages }: { messages: AgentMessage[] }) => ({

src/agents/pi-embedded-runner/run/attempt.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1457,6 +1457,7 @@ export async function runEmbeddedAttempt(
14571457
}
14581458
let prePromptMessageCount = activeSession.messages.length;
14591459
let unwindowedContextEngineMessagesForPrecheck: AgentMessage[] | undefined;
1460+
let contextEnginePromptAuthority: "assembled" | "preassembly_may_overflow" = "assembled";
14601461
abortSessionForYield = () => {
14611462
yieldAbortSettled = Promise.resolve(activeSession.abort());
14621463
};
@@ -1981,6 +1982,7 @@ export async function runEmbeddedAttempt(
19811982
if (assembled.messages !== activeSession.messages) {
19821983
activeSession.agent.state.messages = assembled.messages;
19831984
}
1985+
contextEnginePromptAuthority = assembled.promptAuthority ?? "assembled";
19841986
if (assembled.systemPromptAddition) {
19851987
systemPromptText = prependSystemPromptAddition({
19861988
systemPrompt: systemPromptText,
@@ -2620,7 +2622,9 @@ export async function runEmbeddedAttempt(
26202622

26212623
const preemptiveCompaction = shouldPreemptivelyCompactBeforePrompt({
26222624
messages: activeSession.messages,
2623-
unwindowedMessages: unwindowedContextEngineMessagesForPrecheck,
2625+
...(contextEnginePromptAuthority === "preassembly_may_overflow"
2626+
? { unwindowedMessages: unwindowedContextEngineMessagesForPrecheck }
2627+
: {}),
26242628
systemPrompt: systemPromptText,
26252629
prompt: effectivePrompt,
26262630
contextTokenBudget,

src/agents/pi-embedded-runner/run/preemptive-compaction.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ describe("preemptive-compaction", () => {
9393
expect(result.estimatedPromptTokens).toBeLessThan(result.promptBudgetBeforeReserve);
9494
});
9595

96-
it("uses the larger unwindowed message estimate when context engine assembly windows history", () => {
96+
it("uses the larger unwindowed message estimate when explicitly provided", () => {
9797
const result = shouldPreemptivelyCompactBeforePrompt({
9898
messages: [makeAssistantHistory("small assembled window")],
9999
unwindowedMessages: [makeAssistantHistory(verboseHistory.repeat(4))],

src/context-engine/types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ export type AssembleResult = {
88
messages: AgentMessage[];
99
/** Estimated total tokens in assembled context */
1010
estimatedTokens: number;
11+
/**
12+
* Declares whether the assembled messages are the authoritative prompt for
13+
* overflow prechecks. Defaults to "assembled".
14+
*/
15+
promptAuthority?: "assembled" | "preassembly_may_overflow";
1116
/** Optional context-engine-provided instructions prepended to the runtime system prompt */
1217
systemPromptAddition?: string;
1318
};

0 commit comments

Comments
 (0)