fix(compaction): preserve exact request context safely

Co-authored-by: VACInc <3279061+VACInc@users.noreply.github.com>

Worked on by:
- @VACInc
This commit is contained in:
roboclaw-bot
2026-08-28 03:28:26 +00:00
parent 6ab2b77ebd
commit 3e2b9443d0
3 changed files with 409 additions and 70 deletions
@@ -143,49 +143,38 @@ export function createSummaryQualityRetentionPlan(
summary: string,
truncatedMarker: string,
params: {
auditSummary?: string;
identifiers: string[];
latestAsk: string | null;
requiredAskContext?: string;
identifierPolicy?: CompactionSummarizationInstructions["identifierPolicy"];
},
): SummaryQualityRetentionPlan | null {
const contents = parseRequiredSummarySectionContents(summary);
const requiredAskContext = params.requiredAskContext?.trim() ?? "";
const bodyHasLatestAsk = hasAskOverlap(params.auditSummary ?? summary, params.latestAsk);
const requiredContextBlock =
bodyHasLatestAsk && requiredAskContext
? `## Latest user request context\n${JSON.stringify(requiredAskContext)}`
: "";
const parsedSummary =
requiredContextBlock && summary.startsWith(`${requiredContextBlock}\n\n`)
? summary.slice(requiredContextBlock.length + 2)
: summary;
const contents = parseRequiredSummarySectionContents(parsedSummary);
if (!contents) {
return null;
}
const enforceIdentifiers = (params.identifierPolicy ?? "strict") === "strict";
const requiredAskContext = params.requiredAskContext?.trim() ?? "";
const auditedIdentifiers = enforceIdentifiers ? params.identifiers : [];
const marker = truncatedMarker.trim();
// Protected tails render after each section's optional content so the audit
// facts survive regardless of how much model text the budget keeps.
// Weak token overlap is sufficient for the final presence audit, but not for semantic
// ownership: incidental words in Decisions/Constraints must not classify an omitted ask.
// Prefer explicit pending ownership, then preserve an exact source context in the section
// selected by the model. Otherwise fail safe to Pending user asks because no trustworthy
// completion state exists.
const exactAskSectionIndex = requiredAskContext
? contents[PENDING_ASK_SECTION_INDEX]?.includes(requiredAskContext)
? PENDING_ASK_SECTION_INDEX
: contents.findIndex((content) => content.includes(requiredAskContext))
: -1;
const modelAskSectionIndex =
exactAskSectionIndex >= 0
? exactAskSectionIndex
: hasAskOverlap(contents[PENDING_ASK_SECTION_INDEX] ?? "", params.latestAsk)
? PENDING_ASK_SECTION_INDEX
: -1;
const protectedAskSectionIndex =
modelAskSectionIndex >= 0 ? modelAskSectionIndex : PENDING_ASK_SECTION_INDEX;
// Response termination cannot prove task completion. Preserve the summarizer's
// section choice instead of moving the request between completed and pending state.
const protectedAskContext = requiredAskContext
? modelAskSectionIndex >= 0
? requiredAskContext
: `${LATEST_USER_REQUEST_CONTEXT_LABEL}\n${requiredAskContext}`
: "";
// Keep the model's completed/pending classification unchanged. When it reflects the ask,
// preserve exact source text in a neutral prefix; when it omits the ask, fail safe to Pending.
const protectedAskContext =
!bodyHasLatestAsk && requiredAskContext
? `${LATEST_USER_REQUEST_CONTEXT_LABEL}\n${JSON.stringify(requiredAskContext)}`
: "";
const protectedTails = REQUIRED_SUMMARY_SECTIONS.map((_, index) =>
index === protectedAskSectionIndex
index === PENDING_ASK_SECTION_INDEX
? protectedAskContext
: index === EXACT_IDENTIFIERS_SECTION_INDEX
? auditedIdentifiers.join("\n")
@@ -194,8 +183,11 @@ export function createSummaryQualityRetentionPlan(
const bodyHasIdentifiers = auditedIdentifiers.every((identifier) =>
summaryIncludesIdentifier(summary, identifier),
);
const bodyHasLatestAsk = hasAskOverlap(summary, params.latestAsk);
const bodyHasRequiredAskContext = !requiredAskContext || modelAskSectionIndex >= 0;
const bodyHasRequiredAskContext = !requiredAskContext
? true
: requiredContextBlock
? summary.startsWith(requiredContextBlock)
: contents[PENDING_ASK_SECTION_INDEX]?.includes(protectedAskContext);
const renderSections = (sectionContents: string[]) =>
REQUIRED_SUMMARY_SECTIONS.map((heading, index) => {
const content = sectionContents[index];
@@ -206,7 +198,7 @@ export function createSummaryQualityRetentionPlan(
if (!tail) {
return optional;
}
if (index === protectedAskSectionIndex && optional.includes(tail)) {
if (index === PENDING_ASK_SECTION_INDEX && optional.includes(tail)) {
return optional;
}
if (index === EXACT_IDENTIFIERS_SECTION_INDEX) {
@@ -217,7 +209,7 @@ export function createSummaryQualityRetentionPlan(
}
const retainedOptional =
index === PENDING_ASK_SECTION_INDEX &&
modelAskSectionIndex < 0 &&
protectedAskContext &&
/^(?:none|none captured|no pending asks)[.!]?$/iu.test(optional)
? ""
: optional;
@@ -229,6 +221,7 @@ export function createSummaryQualityRetentionPlan(
(heading, index) => `${heading}\n\n${protectedTails[index] ?? ""}`,
);
const minimumSummary = [
...(requiredContextBlock ? [requiredContextBlock] : []),
...minimumBlocks.slice(0, QUALITY_PROTECTED_SECTION_START),
marker,
...minimumBlocks.slice(QUALITY_PROTECTED_SECTION_START),
@@ -298,6 +291,7 @@ export function createSummaryQualityRetentionPlan(
const blocks = renderSections(sectionContents);
return {
text: [
...(requiredContextBlock ? [requiredContextBlock] : []),
...blocks.slice(0, QUALITY_PROTECTED_SECTION_START),
...(trimmed ? [marker] : []),
...blocks.slice(QUALITY_PROTECTED_SECTION_START),
@@ -427,6 +421,7 @@ function hasAskOverlap(summary: string, latestAsk: string | null): boolean {
export function auditSummaryQuality(params: {
summary: string;
structuralSummary: string;
sourceSummaries?: string[];
identifiers: string[];
latestAsk: string | null;
identifierPolicy?: CompactionSummarizationInstructions["identifierPolicy"];
@@ -437,6 +432,13 @@ export function auditSummaryQuality(params: {
if (!lines.has(section)) {
reasons.push(`missing_section:${section}`);
}
if (
params.sourceSummaries?.some(
(source) => normalizedSummaryLines(source).filter((line) => line === section).length > 1,
)
) {
reasons.push(`duplicate_section:${section}`);
}
}
const enforceIdentifiers = (params.identifierPolicy ?? "strict") === "strict";
if (enforceIdentifiers) {
@@ -450,15 +452,5 @@ export function auditSummaryQuality(params: {
if (!hasAskOverlap(params.summary, params.latestAsk)) {
reasons.push("latest_user_ask_not_reflected");
}
if (params.structuralSummary.includes(LATEST_USER_REQUEST_CONTEXT_LABEL)) {
const contents = parseRequiredSummarySectionContents(params.structuralSummary);
const pendingAsks = contents?.[PENDING_ASK_SECTION_INDEX] ?? "";
if (
!pendingAsks.includes(LATEST_USER_REQUEST_CONTEXT_LABEL) ||
!hasAskOverlap(pendingAsks, params.latestAsk)
) {
reasons.push("latest_user_ask_context_not_pending");
}
}
return { ok: reasons.length === 0, reasons };
}
@@ -746,6 +746,107 @@ describe("compaction-safeguard summary budgets", () => {
}),
).toEqual({ ok: true, reasons: [] });
});
it("preserves real sections when recompacting encoded heading-like source context", () => {
const oldAsk = [
"keep this template:",
"## Decisions",
"old decision",
"## Open TODOs",
"old todo",
"## Constraints/Rules",
"old rule",
"## Pending user asks",
"old ask",
"## Exact identifiers",
"old id",
].join("\n");
const latestAsk = "report the current deployment status";
const body = [
`## Latest user request context\n${JSON.stringify(oldAsk)}`,
"## Decisions",
"REAL DECISION",
"## Open TODOs",
"REAL TODO",
"## Constraints/Rules",
"REAL RULE",
"## Pending user asks",
latestAsk,
"## Exact identifiers",
"None.",
].join("\n\n");
const finalized = requireRecord(
budgetCompactionSummary(body, "", 1_000, {
auditSummary: body,
identifiers: [],
latestAsk,
requiredAskContext: latestAsk,
identifierPolicy: "strict",
}),
);
const summary = String(finalized.summary);
expect(summary).toContain(`## Latest user request context\n${JSON.stringify(latestAsk)}`);
expect(summary).toContain("REAL DECISION");
expect(summary).toContain("REAL TODO");
expect(summary).toContain("REAL RULE");
});
it("preserves exact identifiers when recompacting encoded heading-like context", () => {
const latestAsk = [
"zephyr quasar template must survive:",
"## Decisions",
"alpha",
"## Open TODOs",
"beta",
"## Constraints/Rules",
"gamma",
"## Pending user asks",
"delta",
"## Exact identifiers",
"epsilon",
].join("\n");
const identifier = "REAL-OLD-ID-MUST-SURVIVE";
const body = [
"## Decisions",
"No related decision.",
"## Open TODOs",
"None.",
"## Constraints/Rules",
"Preserve exact context.",
"## Pending user asks",
"None.",
"## Exact identifiers",
identifier,
].join("\n");
const first = requireRecord(
budgetCompactionSummary(body, "", 1_000, {
auditSummary: body,
identifiers: [identifier],
latestAsk,
requiredAskContext: latestAsk,
identifierPolicy: "strict",
}),
);
expect(String(first.summary)).toContain(
`## Latest user request context\n${JSON.stringify(latestAsk)}`,
);
const second = requireRecord(
budgetCompactionSummary(String(first.summary), "", 700, {
auditSummary: String(first.summary),
identifiers: [identifier],
latestAsk,
requiredAskContext: latestAsk,
identifierPolicy: "strict",
}),
);
expect(String(second.summary)).toContain(identifier);
expect(String(second.summary)).toContain(
`## Latest user request context\n${JSON.stringify(latestAsk)}`,
);
});
});
describe("computeAdaptiveChunkRatio", () => {
@@ -1545,28 +1646,25 @@ describe("compaction-safeguard recent-turn preservation", () => {
expect(quality.reasons).toContain("latest_user_ask_not_reflected");
});
it("rejects deterministic source context outside pending asks", () => {
it("does not apply an older pending fallback marker to the current latest ask", () => {
const latestAsk = "report whether the deployment is ready";
const quality = auditSummaryQuality({
summary: [
"## Decisions",
`Latest user request context:\n${latestAsk}`,
"## Open TODOs",
"None.",
"## Constraints/Rules",
"Preserve exact context.",
"## Pending user asks",
"None.",
"## Exact identifiers",
"None.",
].join("\n"),
identifiers: [],
latestAsk,
});
const summary = [
`## Latest user request context\n${JSON.stringify(latestAsk)}`,
"## Decisions",
"The deployment readiness report was delivered.",
"## Open TODOs",
"None.",
"## Constraints/Rules",
"Preserve exact context.",
"## Pending user asks",
"Latest user request context:\narchive the previous release notes",
"## Exact identifiers",
"None.",
].join("\n\n");
expect(quality).toEqual({
ok: false,
reasons: ["latest_user_ask_context_not_pending"],
expect(auditSummaryQuality({ summary, identifiers: [], latestAsk })).toEqual({
ok: true,
reasons: [],
});
});
@@ -2474,7 +2572,7 @@ describe("compaction-safeguard recent-turn preservation", () => {
const latestAsk = "report whether the deployment is ready";
const generatedSummary = [
"## Decisions",
"The deployment is ready for inspection.",
"Inspection remains blocked.",
"## Open TODOs",
"Check the deployment status.",
"## Constraints/Rules",
@@ -2504,7 +2602,9 @@ describe("compaction-safeguard recent-turn preservation", () => {
const { result } = await runCompactionScenario({ sessionManager, event, apiKey: "test-key" });
const summary = expectCompactionResult(result).summary;
expect(summary).toContain(`## Pending user asks\nLatest user request context:\n${latestAsk}`);
expect(summary).toContain(
`## Pending user asks\nLatest user request context:\n${JSON.stringify(latestAsk)}`,
);
expect(summary).not.toContain("## Pending user asks\nNone.");
expect(summary).not.toContain(`## Decisions\nLatest user request context:\n${latestAsk}`);
expect(auditSummaryQuality({ summary, identifiers: [], latestAsk })).toEqual({
@@ -2515,6 +2615,44 @@ describe("compaction-safeguard recent-turn preservation", () => {
expect(consumeCompactionSafeguardCancelReason(sessionManager)).toBeNull();
});
it("restores exact source qualifiers when a fitting pending ask only overlaps", async () => {
mockSummarizeInStages.mockReset();
const latestAsk = "delete production only after verified backup";
const generatedSummary = [
"## Decisions",
"Keep the cleanup workflow.",
"## Open TODOs",
"None.",
"## Constraints/Rules",
"Use normal safeguards.",
"## Pending user asks",
"Delete the backup.",
"## Exact identifiers",
"None.",
].join("\n");
mockSummarizeInStages.mockResolvedValue(summaryResult(generatedSummary));
const sessionManager = stubSessionManager();
setCompactionSafeguardRuntime(sessionManager, {
model: createAnthropicModelFixture(),
recentTurnsPreserve: 0,
qualityGuardEnabled: true,
qualityGuardMaxRetries: 0,
});
const event = createCompactionEvent({ messageText: latestAsk, tokensBefore: 1_500 });
(event.preparation as { settings?: { reserveTokens: number } }).settings = {
reserveTokens: 4_000,
};
const { result } = await runCompactionScenario({ sessionManager, event, apiKey: "test-key" });
const summary = expectCompactionResult(result).summary;
expect(summary).toContain(`## Latest user request context\n${JSON.stringify(latestAsk)}`);
expect(summary).toContain("## Pending user asks\nDelete the backup.");
expect(mockSummarizeInStages).toHaveBeenCalledTimes(1);
expect(consumeCompactionSafeguardCancelReason(sessionManager)).toBeNull();
});
it("fails closed when audit-required tail sections cannot fit the artifact cap", async () => {
mockSummarizeInStages.mockReset();
const latestAsk = "preserve the pending deployment status";
@@ -2652,7 +2790,10 @@ describe("compaction-safeguard recent-turn preservation", () => {
const { result } = await runCompactionScenario({ sessionManager, event, apiKey: "test-key" });
expect(expectCompactionResult(result).summary).toBe(validRetry);
const finalSummary = expectCompactionResult(result).summary;
expect(finalSummary).toContain("## Pending user asks");
expect(finalSummary).toContain(`${latestAsk} ${identifier}`);
expect(finalSummary).toContain(`## Exact identifiers\n${identifier}`);
expect(mockSummarizeInStages).toHaveBeenCalledTimes(2);
const retry = requireRecord(mockCallArg(mockSummarizeInStages, 1));
expect(retry.customInstructions).toContain("Quality check feedback");
@@ -2713,6 +2854,9 @@ describe("compaction-safeguard recent-turn preservation", () => {
];
}).flat();
const event = createCompactionEvent({ messageText: latestAsk, tokensBefore: 90_000 });
(event.preparation as { settings?: { reserveTokens: number } }).settings = {
reserveTokens: 4_000,
};
event.preparation.messagesToSummarize = [
{ role: "user", content: latestAsk, timestamp: 1 },
...toolChain,
@@ -2787,6 +2931,152 @@ describe("compaction-safeguard recent-turn preservation", () => {
expect(mockSummarizeInStages).toHaveBeenCalledTimes(1);
});
it("keeps exact source context with a model-classified completed paraphrase", async () => {
mockSummarizeInStages.mockReset();
const latestAsk = "combine the provider boxes into one completed artifact";
const summary = [
"## Decisions",
"The provider boxes were combined into the final artifact.",
"## Open TODOs",
"None.",
"## Constraints/Rules",
"Preserve exact context.",
"## Pending user asks",
"None.",
"## Exact identifiers",
"None.",
].join("\n");
mockSummarizeInStages.mockResolvedValue(summaryResult(summary));
const sessionManager = stubSessionManager();
setCompactionSafeguardRuntime(sessionManager, {
model: createAnthropicModelFixture(),
recentTurnsPreserve: 0,
qualityGuardEnabled: true,
qualityGuardMaxRetries: 0,
});
const event = createCompactionEvent({ messageText: latestAsk, tokensBefore: 90_000 });
(event.preparation as { settings?: { reserveTokens: number } }).settings = {
reserveTokens: 4_000,
};
const { result } = await runCompactionScenario({ sessionManager, event, apiKey: "test-key" });
const finalSummary = expectCompactionResult(result).summary;
expect(finalSummary).toContain(`## Latest user request context\n${JSON.stringify(latestAsk)}`);
expect(finalSummary).toContain(`## Decisions\n${summary.split("\n")[1]}`);
expect(finalSummary).toContain("## Pending user asks\nNone.");
expect(mockSummarizeInStages).toHaveBeenCalledTimes(1);
});
it("keeps heading-like exact source context outside the structured model sections", async () => {
mockSummarizeInStages.mockReset();
const latestAsk = [
"keep these headings verbatim:",
"Latest user request context:",
"## Decisions",
"alpha",
"## Open TODOs",
"beta",
"## Constraints/Rules",
"gamma",
"## Pending user asks",
"delta",
"## Exact identifiers",
"epsilon",
].join("\n");
const summary = [
"## Decisions",
"Keep the requested headings.",
"## Open TODOs",
"None.",
"## Constraints/Rules",
"Preserve exact context.",
"## Pending user asks",
"Keep the headings verbatim.",
"## Exact identifiers",
"None.",
].join("\n");
mockSummarizeInStages.mockResolvedValue(summaryResult(summary));
const sessionManager = stubSessionManager();
setCompactionSafeguardRuntime(sessionManager, {
model: createAnthropicModelFixture(),
recentTurnsPreserve: 0,
qualityGuardEnabled: true,
qualityGuardMaxRetries: 0,
});
const event = createCompactionEvent({ messageText: latestAsk, tokensBefore: 90_000 });
(event.preparation as { settings?: { reserveTokens: number } }).settings = {
reserveTokens: 4_000,
};
const { result } = await runCompactionScenario({ sessionManager, event, apiKey: "test-key" });
const finalSummary = expectCompactionResult(result).summary;
expect(finalSummary).toContain(
`## Latest user request context\n${JSON.stringify(latestAsk)}\n\n## Decisions`,
);
expect(finalSummary).toContain("## Pending user asks\nKeep the headings verbatim.");
});
it("retries model output that copies a heading-template ask into structured sections", async () => {
mockSummarizeInStages.mockReset();
const latestAsk = [
"zephyr quasar template:",
"## Decisions",
"alpha",
"## Open TODOs",
"beta",
"## Constraints/Rules",
"gamma",
"## Pending user asks",
"delta",
"## Exact identifiers",
"epsilon",
].join("\n");
const structuredSummary = (decision: string, pending: string) =>
[
"## Decisions",
decision,
"## Open TODOs",
"None.",
"## Constraints/Rules",
"Preserve exact context.",
"## Pending user asks",
pending,
"## Exact identifiers",
"None.",
].join("\n");
mockSummarizeInStages
.mockResolvedValueOnce(summaryResult(structuredSummary(latestAsk, "None.")))
.mockResolvedValueOnce(
summaryResult(
structuredSummary("No decision yet.", "Track the zephyr quasar template request."),
),
);
const sessionManager = stubSessionManager();
setCompactionSafeguardRuntime(sessionManager, {
model: createAnthropicModelFixture(),
recentTurnsPreserve: 0,
qualityGuardEnabled: true,
qualityGuardMaxRetries: 1,
});
const event = createCompactionEvent({ messageText: latestAsk, tokensBefore: 90_000 });
(event.preparation as { settings?: { reserveTokens: number } }).settings = {
reserveTokens: 4_000,
};
const { result } = await runCompactionScenario({ sessionManager, event, apiKey: "test-key" });
const finalSummary = expectCompactionResult(result).summary;
expect(mockSummarizeInStages).toHaveBeenCalledTimes(2);
expect(finalSummary).toContain(`## Latest user request context\n${JSON.stringify(latestAsk)}`);
const retry = requireRecord(mockCallArg(mockSummarizeInStages, 1));
expect(retry.customInstructions).toContain("duplicate_section");
});
it("propagates caller abort during corrective generation", async () => {
mockSummarizeInStages.mockReset();
const controller = new AbortController();
@@ -2827,14 +3117,14 @@ describe("compaction-safeguard recent-turn preservation", () => {
expect(consumeCompactionSafeguardCancelReason(sessionManager)).toBeNull();
});
it("keeps the split-turn summary's model-classified pending state", async () => {
it("keeps the split-turn model's pending state while retaining exact source context", async () => {
mockSummarizeInStages.mockReset();
const latestAsk = "combine the provider boxes into one completed artifact";
const identifier = "/tmp/pr130620/live/marker";
const structuredSummary = (pendingAsk: string) =>
[
"## Decisions",
`${latestAsk} was completed.`,
"Keep the provider-box work active.",
"## Open TODOs",
"None.",
"## Constraints/Rules",
@@ -2856,7 +3146,12 @@ describe("compaction-safeguard recent-turn preservation", () => {
preparation: {
messagesToSummarize: [] as AgentMessage[],
turnPrefixMessages: [
{ role: "user", content: `${latestAsk} and preserve ${identifier}`, timestamp: 1 },
{ role: "user", content: latestAsk, timestamp: 1 },
castAgentMessage({
role: "assistant",
content: [{ type: "text", text: `Preserve ${identifier}.` }],
timestamp: 2,
}),
] as AgentMessage[],
firstKeptEntryId: "entry-1",
tokensBefore: 90_000,
@@ -2872,9 +3167,56 @@ describe("compaction-safeguard recent-turn preservation", () => {
const finalSummary = expectCompactionResult(result).summary;
expect(mockSummarizeInStages).toHaveBeenCalledTimes(1);
expect(finalSummary).toContain(`## Latest user request context\n${JSON.stringify(latestAsk)}`);
expect(finalSummary).toContain(`## Pending user asks\n${latestAsk}`);
});
it("does not revive a split-turn request the model classified as completed", async () => {
mockSummarizeInStages.mockReset();
const latestAsk = "combine the provider boxes into one completed artifact";
const summary = [
"## Decisions",
"The provider boxes were combined into the final artifact.",
"## Open TODOs",
"None.",
"## Constraints/Rules",
"Preserve exact context.",
"## Pending user asks",
"None.",
"## Exact identifiers",
"None.",
].join("\n");
mockSummarizeInStages.mockResolvedValue(summaryResult(summary));
const sessionManager = stubSessionManager();
setCompactionSafeguardRuntime(sessionManager, {
model: createAnthropicModelFixture(),
qualityGuardEnabled: true,
qualityGuardMaxRetries: 0,
});
const event = {
preparation: {
messagesToSummarize: [] as AgentMessage[],
turnPrefixMessages: [{ role: "user", content: latestAsk, timestamp: 1 }] as AgentMessage[],
firstKeptEntryId: "entry-1",
tokensBefore: 90_000,
fileOps: { read: [], edited: [], written: [] },
settings: { reserveTokens: 4_000 },
isSplitTurn: true,
},
customInstructions: "",
signal: new AbortController().signal,
};
const { result } = await runCompactionScenario({ sessionManager, event, apiKey: "test-key" });
const finalSummary = expectCompactionResult(result).summary;
expect(finalSummary).toContain(`## Latest user request context\n${JSON.stringify(latestAsk)}`);
expect(finalSummary).toContain("## Pending user asks\nNone.");
expect(finalSummary).not.toContain(`## Pending user asks\n${latestAsk}`);
expect(mockSummarizeInStages).toHaveBeenCalledTimes(1);
});
it("audits all-preserved fallback output against pre-partition source facts", async () => {
mockSummarizeInStages.mockReset();
const latestAsk = "report deployment status";
@@ -216,6 +216,7 @@ type CompactionSuffix = {
};
type SummaryQualityRetention = {
auditSummary?: string;
identifiers: string[];
latestAsk: string | null;
requiredAskContext: string;
@@ -1270,6 +1271,7 @@ export default function compactionSafeguardExtension(api: ExtensionAPI): void {
for (let attempt = 0; attempt < totalAttempts; attempt += 1) {
let splitTurnSectionLocal = "";
let splitTurnSummaryLocal = "";
let historySummary = "";
const producerLosses = new Set<CompactionLoss>();
try {
@@ -1292,6 +1294,7 @@ export default function compactionSafeguardExtension(api: ExtensionAPI): void {
customInstructions: `${TURN_PREFIX_INSTRUCTIONS}\n\nAdditional requirements:\n\n${currentInstructions}`,
previousSummary: undefined,
});
splitTurnSummaryLocal = prefixSummary;
splitTurnSectionLocal = formatGeneratedSplitTurnSection(prefixSummary, () => {
producerLosses.add("split-turn-tail");
});
@@ -1330,6 +1333,7 @@ export default function compactionSafeguardExtension(api: ExtensionAPI): void {
producerLosses,
qualityGuardEnabled
? {
auditSummary: unbudgetedSummary,
identifiers,
latestAsk: latestUserAsk,
requiredAskContext: formatRequiredAskContext(latestUserAsk ?? ""),
@@ -1358,6 +1362,7 @@ export default function compactionSafeguardExtension(api: ExtensionAPI): void {
const quality = auditSummaryQuality({
summary: finalized.summary,
structuralSummary: finalized.structuralSummary,
sourceSummaries: [historySummary, splitTurnSummaryLocal].filter(Boolean),
identifiers,
latestAsk: latestUserAsk,
identifierPolicy,