feat: append workspace critical rules to compaction summary

- Add readWorkspaceContextForSummary() to extract Session Startup + Red Lines from AGENTS.md
- Inject workspace context into compaction summary (limited to 2000 chars)
- Export extractSections() from post-compaction-context.ts for reuse
- Ensures compaction summary includes core rules needed for recovery

Part 1 of post-compaction context injection feature.
This commit is contained in:
康熙
2026-02-16 21:06:08 +08:00
committed by Peter Steinberger
parent d0b33f23eb
commit c4f829411f
2 changed files with 44 additions and 1 deletions

View File

@@ -1,3 +1,5 @@
import fs from "node:fs";
import path from "node:path";
import type { AgentMessage } from "@mariozechner/pi-agent-core"; import type { AgentMessage } from "@mariozechner/pi-agent-core";
import type { ExtensionAPI, FileOperations } from "@mariozechner/pi-coding-agent"; import type { ExtensionAPI, FileOperations } from "@mariozechner/pi-coding-agent";
import { import {
@@ -11,6 +13,7 @@ import {
resolveContextWindowTokens, resolveContextWindowTokens,
summarizeInStages, summarizeInStages,
} from "../compaction.js"; } from "../compaction.js";
import { extractSections } from "../../auto-reply/reply/post-compaction-context.js";
import { getCompactionSafeguardRuntime } from "./compaction-safeguard-runtime.js"; import { getCompactionSafeguardRuntime } from "./compaction-safeguard-runtime.js";
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.";
@@ -158,6 +161,40 @@ function formatFileOperations(readFiles: string[], modifiedFiles: string[]): str
return `\n\n${sections.join("\n\n")}`; return `\n\n${sections.join("\n\n")}`;
} }
/**
* Read and format critical workspace context for compaction summary.
* Extracts "Session Startup" and "Red Lines" from AGENTS.md.
* Limited to 2000 chars to avoid bloating the summary.
*/
async function readWorkspaceContextForSummary(): Promise<string> {
const MAX_SUMMARY_CONTEXT_CHARS = 2000;
const workspaceDir = process.cwd();
const agentsPath = path.join(workspaceDir, "AGENTS.md");
try {
if (!fs.existsSync(agentsPath)) {
return "";
}
const content = await fs.promises.readFile(agentsPath, "utf-8");
const sections = extractSections(content, ["Session Startup", "Red Lines"]);
if (sections.length === 0) {
return "";
}
const combined = sections.join("\n\n");
const safeContent =
combined.length > MAX_SUMMARY_CONTEXT_CHARS
? combined.slice(0, MAX_SUMMARY_CONTEXT_CHARS) + "\n...[truncated]..."
: combined;
return `\n\n<workspace-critical-rules>\n${safeContent}\n</workspace-critical-rules>`;
} catch {
return "";
}
}
export default function compactionSafeguardExtension(api: ExtensionAPI): void { export default function compactionSafeguardExtension(api: ExtensionAPI): void {
api.on("session_before_compact", async (event, ctx) => { api.on("session_before_compact", async (event, ctx) => {
const { preparation, customInstructions, signal } = event; const { preparation, customInstructions, signal } = event;
@@ -309,6 +346,12 @@ export default function compactionSafeguardExtension(api: ExtensionAPI): void {
summary += toolFailureSection; summary += toolFailureSection;
summary += fileOpsSummary; summary += fileOpsSummary;
// Append workspace critical context (Session Startup + Red Lines from AGENTS.md)
const workspaceContext = await readWorkspaceContextForSummary();
if (workspaceContext) {
summary += workspaceContext;
}
return { return {
compaction: { compaction: {
summary, summary,

View File

@@ -49,7 +49,7 @@ export async function readPostCompactionContext(workspaceDir: string): Promise<s
* Skips content inside fenced code blocks. * Skips content inside fenced code blocks.
* Captures until the next heading of same or higher level, or end of string. * Captures until the next heading of same or higher level, or end of string.
*/ */
function extractSections(content: string, sectionNames: string[]): string[] { export function extractSections(content: string, sectionNames: string[]): string[] {
const results: string[] = []; const results: string[] = [];
const lines = content.split("\n"); const lines = content.split("\n");