package compact import ( "fmt" "github.com/firebase/genkit/go/ai" ) // summarySystem replaces the agent's own system prompt in the summary request. // The agent's prompt says "you are a coding agent, run commands": sent along // with a history full of tool calls and no tools declared, it invites the model // to keep acting. The note-taker's prompt asks for the one thing wanted here. const summarySystem = `You are the note-taker of a coding agent. You write precise, structured notes about a coding session, in plain markdown, and you never invent details that are not in the conversation.` // DefaultPrompt is the summary request (CONTEXT_WINDOW.md § 4.1). The seven // fixed sections are the contract: what the model must preserve, and a shape // the agent can rely on. The ban on quoting file contents is deliberate — a // file may have changed since it was read, and the agent's system prompt // already forbids stating what a command in THIS answer did not return. const DefaultPrompt = `You are compressing the history of a coding session so that the same agent can continue the work with less context. Everything above this message is that history. Write a summary, in English, using EXACTLY the sections below, markdown headings included. Be concrete: exact file paths, exact command lines, exact error messages. Do not invent anything that is not in the history. Do not quote file contents — files may have changed since; say what was done to them instead. ## Goal What the user is trying to achieve, in one or two sentences, in their words. ## Files touched One line per file: path — created | modified | deleted | read-only — what changed. Nothing else. ## Decisions Choices made and their reason (libraries, structure, names, things the user refused). One line each. ## Errors and fixes Errors met, and what fixed them. One line each. Skip errors that are not relevant any more. ## Done The tasks completed, one line each. ## In progress / next steps What was being done when the history ends, and what remains. Be explicit about anything half-finished. ## Useful commands Commands to rebuild, test or run the project, and any background job left running (with its pid file). One per line. Keep the whole summary under 40 lines. If a section is empty, write "none".` // mergeLine is appended to the prompt when the history to summarise already // starts with an earlier summary. const mergeLine = "\n\nAn earlier summary is at the top of the history; merge it into the new one, do not repeat it as-is." // acknowledgement is the model half of the summary pair. It keeps the // user/assistant alternation that chat templates expect after a user message, // and it restates the one rule the summary must not erode. const acknowledgement = "Understood — I will re-check files before editing them." // Request builds the summary request: the note-taker's system message, the // old turns as they were sent to the model, then the prompt. `prompt` empty // means DefaultPrompt. func Request(old []*ai.Message, prompt string, merge bool) []*ai.Message { if prompt == "" { prompt = DefaultPrompt } if merge { prompt += mergeLine } req := make([]*ai.Message, 0, len(old)+2) req = append(req, ai.NewSystemTextMessage(summarySystem)) req = append(req, old...) req = append(req, ai.NewUserTextMessage(prompt)) return req } // SummaryPair wraps the model's notes into the two messages that replace the // old turns. The wrapper says what the notes are and what they are not: the // model must treat them as notes to verify, not as observations — otherwise a // wrong line in the summary becomes a fact for the rest of the session. func SummaryPair(summary string, messages, questions, commands int) []*ai.Message { meta := map[string]any{metaKey: "summary"} notes := fmt.Sprintf(`[Context summary — the %d earlier messages of this session (%d question(s), %d command(s)) were compressed into the notes below. Treat them as notes, not as observations: re-read a file or re-run a command before relying on its content.] %s`, messages, questions, commands, summary) return []*ai.Message{ ai.NewMessage(ai.RoleUser, meta, ai.NewTextPart(notes)), ai.NewMessage(ai.RoleModel, meta, ai.NewTextPart(acknowledgement)), } }