import "./paths-CE7eVGHg.js";
import "./registry-CANRzFn-.js";
import "./agent-scope-DZEuQYrq.js";
import { t as createSubsystemLogger } from "./subsystem-Bzq1QhtO.js";
import "./model-selection-BisYvTBb.js";
import "./reply-prefix-i-FPYcoQ.js";
import "./plugins-CZ_mYwXq.js";
import "./replies-0nzkXt6o.js";
import { n as runAgentTurn } from "./pi-embedded-DGnpyHm-.js";
import "./deliver-f3cIWxXT.js";
import "./diagnostic-BdRnGknC.js";
import "./diagnostic-session-state-DpxCUzoM.js";
import "./chunk-CAZujdOi.js";
import "./auth-profiles-Bt3PyWkt.js";
import "./ssrf-BCSnhba8.js";
import "./boolean-BW6OTjPi.js";
import "./shell-env-DgjeObDZ.js";
import "./manifest-registry-CAWGTwb5.js";
import "./chrome-3jl2ulOE.js";
import "./skills-DO7WNsVJ.js";
import "./redact-DSOAcWMe.js";
import "./errors-BEU7IHU6.js";
import "./tokens-yWO1wD7Z.js";
import "./thinking-DgjGxdSP.js";
import "./paths-BsT3BvfH.js";
import "./manager-BHddoe4T.js";
import "./github-copilot-token-dYUr1mDQ.js";
import "./sqlite-D8iYC_CU.js";
import "./markdown-tables-jQzXAsf3.js";
import "./commands-registry-CX-kLuQz.js";
import "./skill-commands-BbMYpaLS.js";
import "./pi-model-discovery-CuZX_Q4t.js";
import "./resolve-route-0kL4oMLp.js";
import "./pi-auth-json-BBE9vt1g.js";

//#region src/memory/synthesis.ts
const log = createSubsystemLogger("memory/synthesis");
const SYNTH_TIMEOUT_MS = 6e4;
const MAX_CLUSTER_CHARS = 4e3;
const SYNTH_KINDS = [
	"belief",
	"preference",
	"fact",
	"lesson",
	"identity"
];
/**
* Build a SynthesizeFn that wraps runAgentTurn for L3 stage 4.
*
* Each invocation:
*   1. Concatenates the cluster's source texts (capped to keep the prompt bounded)
*   2. Calls runAgentTurn with tools disabled and a constrained output format
*   3. Parses the response into { kind, text }
*   4. Returns null on any failure — stage 4 treats null as "skip this cluster"
*
* Caller controls provider/model via the current agent's config. On local
* model setups the cost is free so every cluster gets a turn; on remote
* setups callers can bound frequency via `runL3CycleIfDue`'s minInterval.
*/
function createSynthesizer(params) {
	return async (cluster) => {
		if (cluster.sources.length === 0) return null;
		const prompt = buildSynthesisPrompt(formatClusterObservations(cluster.sources));
		const sessionId = `l3-synth-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
		try {
			const result = await runAgentTurn({
				sessionId,
				sessionKey: `temp:l3-synthesis:${params.agentId}`,
				workspaceDir: params.workspaceDir,
				config: params.cfg,
				prompt,
				modelId: params.modelId,
				provider: params.provider,
				timeoutMs: params.timeoutMs ?? SYNTH_TIMEOUT_MS,
				disableTools: true,
				disableMessageTool: true
			});
			if (!result.text) return null;
			const parsed = parseSynthesisOutput(result.text);
			if (!parsed) {
				log.debug("synthesis: unparseable output", { preview: result.text.slice(0, 200) });
				return null;
			}
			return parsed;
		} catch (err) {
			log.warn(`synthesis: runAgentTurn failed: ${String(err)}`);
			return null;
		}
	};
}
function formatClusterObservations(sources) {
	const lines = [];
	let chars = 0;
	for (const source of sources) {
		const body = source.text.trim().replace(/\s+/g, " ");
		if (!body) continue;
		const entry = `- ${body}`;
		if (chars + entry.length > MAX_CLUSTER_CHARS) break;
		lines.push(entry);
		chars += entry.length + 1;
	}
	return lines.join("\n");
}
function buildSynthesisPrompt(observations) {
	return `You are synthesizing long-term memory for Symi. Below are several related observations that appeared repeatedly across past sessions with this user. Collapse them into ONE durable statement that captures what is consistently true.

OBSERVATIONS:
${observations}

OUTPUT RULES (strict):
- First line: "KIND: <one of: belief | preference | fact | lesson | identity>"
- Remaining lines: the single durable statement, one to three sentences.
- No hedging ("maybe", "perhaps", "might"). If uncertain, reply with the single word SKIP.
- No meta-commentary. No quoting the observations back. No headings.
- Prefer concrete, specific language. Retain names, tools, numbers, dates.

Choose KIND by:
- identity — who Symi is to this user, the relationship itself
- preference — user's style, cadence, taste, communication preferences
- fact — stable fact about the user or their world
- lesson — "what goes wrong / what works" patterns learned from past work
- belief — any other durable synthesized truth`;
}
/**
* Parse "KIND: <k>\n<text>" responses. Returns null on malformed output,
* or when the model chose to skip.
*/
function parseSynthesisOutput(raw) {
	const trimmed = raw.trim();
	if (!trimmed) return null;
	if (/^skip\b/i.test(trimmed)) return null;
	const match = trimmed.match(/^KIND:\s*([a-z]+)\s*\n([\s\S]+)$/i);
	if (!match) return null;
	const kindRaw = match[1]?.toLowerCase().trim() ?? "";
	const textRaw = match[2]?.trim() ?? "";
	if (!textRaw) return null;
	const kind = SYNTH_KINDS.find((k) => k === kindRaw);
	if (!kind) return null;
	const text = textRaw.replace(/```[a-z]*\n?|\n?```/g, "").replace(/^\s*[-*]\s+/, "").trim();
	if (text.length < 10) return null;
	return {
		text,
		kind
	};
}

//#endregion
export { createSynthesizer, parseSynthesisOutput };