bots-garden/mini-mepublic Fork 0
main
Commits
Clone
git clone https://git.rickub.com/bots-garden/mini-me.git
git clone ssh://git@rickub.com/bots-garden/mini-me.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

💾 Saved. d722711 · on main · k33g · 3h ago
prompt.go · 95 lines · 4.2 KBGo Blame HistoryRaw
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
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)),
	}
}