{"version":3,"file":"reflector.d.ts","sourceRoot":"","sources":["../../src/reflect/reflector.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAE7D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAI5C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAEN,KAAK,cAAc,EAGnB,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAMrE,MAAM,WAAW,gBAAgB;IAChC,KAAK,EAAE,QAAQ,CAAC;IAChB,MAAM,EAAE,WAAW,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,oBAAoB,GAAG,MAAM,CAAC;CACvC;AAED,MAAM,WAAW,eAAe;IAC/B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,mBAAmB,EAAE,iBAAiB,EAAE,CAAC;IACzC,SAAS,EAAE,EAAE,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,cAAc,CAAC;IACvB,GAAG,EAAE,cAAc,CAAC;CACpB;AA0DD,wBAAsB,SAAS,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,CAAC,CA2CnF","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type { ThinkingLevel } from \"@ch1nyzzz/pi-agent-core\";\nimport { loadCompiledBundle } from \"../bundle/compile.ts\";\nimport type { EvoPaths } from \"../paths.ts\";\nimport { parseReflectorOutput } from \"../proposal.ts\";\nimport { BundleRegistry } from \"../registry/registry.ts\";\nimport { atomicWriteFile } from \"../storage.ts\";\nimport type { EvidenceReference } from \"../types.ts\";\nimport {\n\tcollectEvidenceCorpus,\n\ttype EvidenceCorpus,\n\treadEvidenceReviewCursor,\n\tvalidateEvidenceReferences,\n} from \"./evidence.ts\";\nimport type { ModelRunner, ModelRunResult } from \"./model-runner.ts\";\nimport { recordModelUsage, renderModelUsageSummary, summarizeModelUsage } from \"./usage.ts\";\n\nconst REFLECTOR_PROMPT_URL = new URL(\"../prompts/reflector.md\", import.meta.url);\nconst WEEK_MS = 7 * 24 * 60 * 60 * 1000;\n\nexport interface RunReportOptions {\n\tpaths: EvoPaths;\n\trunner: ModelRunner;\n\tcwd?: string;\n\tagentDir?: string;\n\tmodel?: string;\n\tthinkingLevel?: ThinkingLevel;\n\tmaxCorpusBytes?: number;\n\twindow?: \"since-last-improve\" | \"full\";\n}\n\nexport interface ReportRunResult {\n\tobservationsMarkdown: string;\n\tobservationEvidence: EvidenceReference[];\n\tproposals: [];\n\tfile: string;\n\tcorpus: EvidenceCorpus;\n\trun: ModelRunResult;\n}\n\nfunction corpusPrompt(corpus: EvidenceCorpus, instruction: string): string {\n\treturn [\n\t\tinstruction,\n\t\t`Corpus bytes: ${corpus.bytes}/${corpus.maxBytes}; truncated: ${String(corpus.truncated)}.`,\n\t\t`Evidence window: ${corpus.mode === \"incremental\" ? \"new inbox and session evidence since the last successful review, plus current bundle and history context\" : \"full available evidence\"}.`,\n\t\t\"\",\n\t\t\"<evidence_corpus>\",\n\t\tcorpus.text,\n\t\t\"</evidence_corpus>\",\n\t].join(\"\\n\");\n}\n\nasync function groundObservationEvidence(\n\tpaths: EvoPaths,\n\tcorpus: EvidenceCorpus,\n\treferences: readonly EvidenceReference[],\n): Promise<EvidenceReference[]> {\n\tif (corpus.sessionIds.length > 0 && references.length === 0) {\n\t\tthrow new Error(\"Reflector observations must cite recorded session evidence\");\n\t}\n\treturn validateEvidenceReferences(paths, references);\n}\n\nasync function writeObservationsFile(options: {\n\tpaths: EvoPaths;\n\tprefix: \"observations\" | \"report\";\n\tobservationsMarkdown: string;\n\tobservationEvidence: readonly EvidenceReference[];\n\tcorpus: EvidenceCorpus;\n}): Promise<string> {\n\tconst file = join(\n\t\toptions.paths.reports,\n\t\t`${options.prefix}-${new Date().toISOString().replace(/[:.]/g, \"-\")}-${randomUUID().slice(0, 8)}.md`,\n\t);\n\tawait atomicWriteFile(\n\t\tfile,\n\t\t[\n\t\t\toptions.observationsMarkdown.trim(),\n\t\t\t\"\",\n\t\t\t\"## Evidence\",\n\t\t\t\"\",\n\t\t\t...(options.observationEvidence.length > 0\n\t\t\t\t? options.observationEvidence.map((reference) => `- ${reference.sessionId}:${reference.sequence}`)\n\t\t\t\t: [\"- No recorded session evidence was available.\"]),\n\t\t\t\"\",\n\t\t\t\"## Evidence corpus\",\n\t\t\t\"\",\n\t\t\t`- Digest: ${options.corpus.evidenceDigest}`,\n\t\t\t`- Mode: ${options.corpus.mode}`,\n\t\t\t`- Truncated: ${String(options.corpus.truncated)}`,\n\t\t\t\"\",\n\t\t].join(\"\\n\"),\n\t);\n\treturn file;\n}\n\nexport async function runReport(options: RunReportOptions): Promise<ReportRunResult> {\n\tconst registry = new BundleRegistry(options.paths);\n\tconst stableDigest = await registry.readStableDigest();\n\tconst stableBundle = stableDigest ? await loadCompiledBundle(options.paths, stableDigest) : undefined;\n\tconst reflectorModel = options.model ?? stableBundle?.policy.modelRouting?.reflector;\n\t// Reports default to the evidence recorded since the last successful improve\n\t// run (the review cursor is read but never advanced), so the report window\n\t// matches the configured reflection cadence instead of all recorded history.\n\tconst window = options.window ?? \"since-last-improve\";\n\tconst reviewCursor = window === \"since-last-improve\" ? await readEvidenceReviewCursor(options.paths) : undefined;\n\tconst corpus = await collectEvidenceCorpus(options.paths, {\n\t\tmaxBytes: options.maxCorpusBytes,\n\t\tmode: window === \"since-last-improve\" ? \"incremental\" : \"full\",\n\t\t...(reviewCursor ? { reviewCursor } : {}),\n\t});\n\tconst rolePrompt = `${await readFile(REFLECTOR_PROMPT_URL, \"utf8\")}\\n\\nREPORT MODE: proposals must be an empty array. Write observationsMarkdown plus its observationEvidence citations; do not suggest or stage changes.`;\n\tconst run = await options.runner.run({\n\t\tcwd: options.cwd ?? process.cwd(),\n\t\t...(options.agentDir ? { agentDir: options.agentDir } : {}),\n\t\tsystemPrompt: rolePrompt,\n\t\tprompt: corpusPrompt(corpus, \"Summarize the recorded work and recurring issues without proposing changes.\"),\n\t\t...(reflectorModel ? { model: reflectorModel } : {}),\n\t\t...(options.thinkingLevel ? { thinkingLevel: options.thinkingLevel } : {}),\n\t});\n\tawait recordModelUsage(options.paths, \"report\", run);\n\tconst output = parseReflectorOutput(run.text);\n\tconst observationEvidence = await groundObservationEvidence(options.paths, corpus, output.observationEvidence);\n\tconst now = new Date();\n\tconst usage = await summarizeModelUsage(options.paths, {\n\t\tsince: new Date(now.getTime() - WEEK_MS),\n\t\tuntil: now,\n\t});\n\tconst observationsMarkdown = [output.observationsMarkdown.trim(), \"\", renderModelUsageSummary(usage).trim()].join(\n\t\t\"\\n\",\n\t);\n\tconst file = await writeObservationsFile({\n\t\tpaths: options.paths,\n\t\tprefix: \"report\",\n\t\tobservationsMarkdown,\n\t\tobservationEvidence,\n\t\tcorpus,\n\t});\n\treturn { observationsMarkdown, observationEvidence, proposals: [], file, corpus, run };\n}\n"]}