fix: tune compaction safeguard schema (#700) (thanks @thewilloftheshadow)
This commit is contained in:
@@ -5,6 +5,7 @@
|
|||||||
### Changes
|
### Changes
|
||||||
- Cron: accept ISO timestamps for one-shot schedules (UTC) and allow optional delete-after-run; wired into CLI + macOS editor.
|
- Cron: accept ISO timestamps for one-shot schedules (UTC) and allow optional delete-after-run; wired into CLI + macOS editor.
|
||||||
- Gateway: add Tailscale binary discovery, custom bind mode, and probe auth retry for password changes. (#740 — thanks @jeffersonwarrior)
|
- Gateway: add Tailscale binary discovery, custom bind mode, and probe auth retry for password changes. (#740 — thanks @jeffersonwarrior)
|
||||||
|
- Agents: add compaction mode config with optional safeguard summarization for long histories. (#700 — thanks @thewilloftheshadow)
|
||||||
|
|
||||||
## 2026.1.12-4
|
## 2026.1.12-4
|
||||||
|
|
||||||
|
|||||||
@@ -2,13 +2,9 @@ import type { AgentMessage } from "@mariozechner/pi-agent-core";
|
|||||||
import type {
|
import type {
|
||||||
ExtensionAPI,
|
ExtensionAPI,
|
||||||
ExtensionContext,
|
ExtensionContext,
|
||||||
|
FileOperations,
|
||||||
} from "@mariozechner/pi-coding-agent";
|
} from "@mariozechner/pi-coding-agent";
|
||||||
import {
|
import { estimateTokens, generateSummary } from "@mariozechner/pi-coding-agent";
|
||||||
computeFileLists,
|
|
||||||
formatFileOperations,
|
|
||||||
generateSummary,
|
|
||||||
estimateTokens,
|
|
||||||
} from "@mariozechner/pi-coding-agent";
|
|
||||||
|
|
||||||
import { DEFAULT_CONTEXT_TOKENS } from "../defaults.js";
|
import { DEFAULT_CONTEXT_TOKENS } from "../defaults.js";
|
||||||
|
|
||||||
@@ -16,10 +12,40 @@ const MAX_CHUNK_RATIO = 0.4;
|
|||||||
const FALLBACK_SUMMARY =
|
const FALLBACK_SUMMARY =
|
||||||
"Summary unavailable due to context limits. Older messages were truncated.";
|
"Summary unavailable due to context limits. Older messages were truncated.";
|
||||||
const TURN_PREFIX_INSTRUCTIONS =
|
const TURN_PREFIX_INSTRUCTIONS =
|
||||||
"This summary covers the prefix of a split turn. Focus on the original request,"
|
"This summary covers the prefix of a split turn. Focus on the original request," +
|
||||||
+ " early progress, and any details needed to understand the retained suffix.";
|
" early progress, and any details needed to understand the retained suffix.";
|
||||||
|
|
||||||
function chunkMessages(messages: AgentMessage[], maxTokens: number): AgentMessage[][] {
|
function computeFileLists(fileOps: FileOperations): {
|
||||||
|
readFiles: string[];
|
||||||
|
modifiedFiles: string[];
|
||||||
|
} {
|
||||||
|
const modified = new Set([...fileOps.edited, ...fileOps.written]);
|
||||||
|
const readFiles = [...fileOps.read].filter((f) => !modified.has(f)).sort();
|
||||||
|
const modifiedFiles = [...modified].sort();
|
||||||
|
return { readFiles, modifiedFiles };
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatFileOperations(
|
||||||
|
readFiles: string[],
|
||||||
|
modifiedFiles: string[],
|
||||||
|
): string {
|
||||||
|
const sections: string[] = [];
|
||||||
|
if (readFiles.length > 0) {
|
||||||
|
sections.push(`<read-files>\n${readFiles.join("\n")}\n</read-files>`);
|
||||||
|
}
|
||||||
|
if (modifiedFiles.length > 0) {
|
||||||
|
sections.push(
|
||||||
|
`<modified-files>\n${modifiedFiles.join("\n")}\n</modified-files>`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (sections.length === 0) return "";
|
||||||
|
return `\n\n${sections.join("\n\n")}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function chunkMessages(
|
||||||
|
messages: AgentMessage[],
|
||||||
|
maxTokens: number,
|
||||||
|
): AgentMessage[][] {
|
||||||
if (messages.length === 0) return [];
|
if (messages.length === 0) return [];
|
||||||
|
|
||||||
const chunks: AgentMessage[][] = [];
|
const chunks: AgentMessage[][] = [];
|
||||||
@@ -28,10 +54,7 @@ function chunkMessages(messages: AgentMessage[], maxTokens: number): AgentMessag
|
|||||||
|
|
||||||
for (const message of messages) {
|
for (const message of messages) {
|
||||||
const messageTokens = estimateTokens(message);
|
const messageTokens = estimateTokens(message);
|
||||||
if (
|
if (currentChunk.length > 0 && currentTokens + messageTokens > maxTokens) {
|
||||||
currentChunk.length > 0 &&
|
|
||||||
currentTokens + messageTokens > maxTokens
|
|
||||||
) {
|
|
||||||
chunks.push(currentChunk);
|
chunks.push(currentChunk);
|
||||||
currentChunk = [];
|
currentChunk = [];
|
||||||
currentTokens = 0;
|
currentTokens = 0;
|
||||||
@@ -144,7 +167,10 @@ export default function compactionSafeguardExtension(api: ExtensionAPI): void {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let summary = historySummary;
|
let summary = historySummary;
|
||||||
if (preparation.isSplitTurn && preparation.turnPrefixMessages.length > 0) {
|
if (
|
||||||
|
preparation.isSplitTurn &&
|
||||||
|
preparation.turnPrefixMessages.length > 0
|
||||||
|
) {
|
||||||
const prefixSummary = await summarizeChunks({
|
const prefixSummary = await summarizeChunks({
|
||||||
messages: preparation.turnPrefixMessages,
|
messages: preparation.turnPrefixMessages,
|
||||||
model,
|
model,
|
||||||
|
|||||||
@@ -1210,7 +1210,9 @@ const AgentDefaultsSchema = z
|
|||||||
.optional(),
|
.optional(),
|
||||||
compaction: z
|
compaction: z
|
||||||
.object({
|
.object({
|
||||||
mode: z.union([z.literal("default"), z.literal("safeguard")]).optional(),
|
mode: z
|
||||||
|
.union([z.literal("default"), z.literal("safeguard")])
|
||||||
|
.optional(),
|
||||||
reserveTokensFloor: z.number().int().nonnegative().optional(),
|
reserveTokensFloor: z.number().int().nonnegative().optional(),
|
||||||
memoryFlush: z
|
memoryFlush: z
|
||||||
.object({
|
.object({
|
||||||
|
|||||||
Reference in New Issue
Block a user