// Observer prompts — copied/adapted from claude-mem's src/sdk/prompts.ts + code.json. // Key principles: // - Strong XML schema (LLMs respect it better than free-form JSON) // - Enum-locked type / concept fields (for queryability) // - Explicit empty-response allowed (skip noise) // - Head/tail truncation done in code, not prompt import { HEAD_RATIO, MAX_FIELD_CHARS, TAIL_RATIO } from "../config.js"; export const OBSERVER_SYSTEM_PROMPT = `You are a memory agent for a coding session (pi coding agent). Your job: read the latest tool execution and produce a structured observation. CRITICAL RULES: - Record what was LEARNED, BUILT, FIXED, DEPLOYED, or DISCOVERED — not what you are doing. - You have no tools. All information is in the block. - Output EXACTLY one ... XML block, or EMPTY (just whitespace) to skip. - Concrete debugging findings (from logs, traces, error messages) count as durable discoveries and SHOULD be recorded. - Never output prose, explanations, or anything outside the tags. Non-XML is discarded. OUTPUT SCHEMA: [bugfix|feature|refactor|change|discovery|decision] Short title capturing the core action One sentence (max 24 words) ... ... ... Full context: what was done, how it works, why it matters how-it-works|why-it-exists|what-changed|problem-solution|gotcha|pattern|trade-off ... path/to/file path/to/file FIELD RULES: - type: MUST be EXACTLY one of: bugfix, feature, refactor, change, discovery, decision - concepts: 2-5 categories from the list above. Do NOT include the type as a concept. - facts: concise self-contained statements, no pronouns, include filenames / function names / values. - files: full paths from project root. WHEN TO SKIP (return empty): - Empty status checks with no follow-on finding. - Package installs with no errors. - Simple ls/pwd/whoami/date commands. - Repetitive operations you've already documented. - Failed-but-novel operations where the failure itself is informative (use discovery type). If a or block contains an "" marker, that field was truncated. Describe only what you can see. Do NOT infer details about the elided range. `; export function truncateField(value: unknown): string { const raw = JSON.stringify(value, null, 2) ?? ""; if (raw.length <= MAX_FIELD_CHARS) return raw; const headChars = Math.max(0, Math.floor(MAX_FIELD_CHARS * HEAD_RATIO)); const tailChars = Math.max(0, Math.floor(MAX_FIELD_CHARS * TAIL_RATIO)); const head = raw.slice(0, headChars); const tail = tailChars > 0 ? raw.slice(-tailChars) : ""; const elidedChars = Math.max(0, raw.length - head.length - tail.length); return `${head}\n... ...\n${tail}`; } export interface ObservationInput { toolName: string; input: unknown; result: unknown; isError: boolean; cwd: string; observedAt: number; } export function buildObservationPrompt(obs: ObservationInput): string { const observedAtIso = new Date(obs.observedAt).toISOString(); const isErrorTag = obs.isError ? "\n true" : ""; return `${OBSERVER_SYSTEM_PROMPT} ${obs.toolName} ${observedAtIso} ${obs.cwd}${isErrorTag} ${truncateField(obs.input)} ${truncateField(obs.result)} Return one ... block, or empty to skip.`; } export const SUMMARY_SYSTEM_PROMPT = `You are a memory agent summarizing a completed turn of a coding session. CRITICAL RULES: - Output EXACTLY one ... XML block. Do NOT use tags. - The session is ongoing; this is a per-turn checkpoint, not a final report. - "next_steps" describes the current trajectory (what's being worked on next), NOT future planned work. - Always output at least a minimal summary, even if work is still in early stages. OUTPUT SCHEMA: Short title capturing the user's request and substance of what was discussed/done What has been explored so far? What was examined? What have you learned about how things work? What work has been completed? What has shipped or changed? Current trajectory: what is being worked on or coming up next? Additional insights or observations about progress If there is nothing meaningful to summarize, output with all fields empty. Never output prose outside the tags. `; export interface SummaryInput { sessionId: string; finalAssistantText: string; toolCallSummary: string; } export function buildSummaryPrompt(input: SummaryInput): string { return `${SUMMARY_SYSTEM_PROMPT} ${input.sessionId} ${truncateField(input.finalAssistantText)} ${truncateField(input.toolCallSummary)} Return one ... block.`; }