import { s as object, z as array, u as string, v as number, _ as _enum } from './paths-2M_UxD_H.js';
import './index-IVoIz4D5.js';
import 'node:fs';
import 'node:path';
import 'node:crypto';
import 'node:fs/promises';
import './index-Ca4C2M45.js';
import { a as resolveAnthropicForRole } from './role-resolver-DF9BQ_U6.js';
import { c as checkHashDedup, a as verifyAndStore } from './extraction-gate2-DEM3Y6aw.js';
import 'node:os';
import 'node:events';
import 'node:diagnostics_channel';
import 'fs';
import 'events';
import 'util';
import 'path';
import 'assert';
import 'worker_threads';
import 'module';
import 'node:url';
import 'url';
import 'buffer';
import 'node:module';
import 'node:child_process';
import 'node:process';
import 'node:async_hooks';
import 'constants';
import 'stream';
import './config-B0nEjgSh.js';
import './json-DXl7iDYg.js';
import './lock-BoHF25mx.js';
import './credential-pool-B04qO8at.js';
import './brain-embedding-Djw9ekXu.js';
import './graph-auto-populate2-DYsUNGSL.js';
import './memory-schema-Bi7cChwo.js';
import './indexes-aYlULWG3.js';
import './utils-BvHPJOVk.js';
import './chunk-DDP5xfzh.js';
import './unique-constraint-BM-Apx1H.js';
import './sqlite-Dsbe0wec.js';
import './dual-scope-db-ATrwq0LH.js';
import './conduit-Ca_OFWR_.js';
import './agent-registry-BtE5uhMO.js';
import './nexus-sqlite2-DgTuv9BQ.js';
import './nexus-schema2-BEd-ol6e.js';
import './logger-ChCwlV5k.js';
import 'node:sqlite';
import './brain-similarity-B2HLXCge.js';
import './memory-accessor2-BzlgmY-Z.js';
import './typed-query-l1D_QElt.js';

//#region ../core/dist/memory/llm-extraction.js
/**
* LLM-driven memory extraction gate.
*
* Replaces the legacy keyword regex in `auto-extract.ts`. Takes a session
* transcript and uses an Anthropic model to extract typed, structured
* memories (decisions, patterns, learnings, constraints, corrections)
* with justification, importance score, and referenced entities.
*
* Design:
*   - Runs only when `brain.llmExtraction.enabled` is true AND an Anthropic
*     API key is available (from ANTHROPIC_API_KEY env var OR Claude Code
*     credentials at ~/.claude/.credentials.json). Otherwise returns empty.
*   - Uses structured output (JSON schema) for reliable parsing — no regex
*     post-processing.
*   - Never throws — all errors are caught and logged. The pipeline must
*     never block session end.
*   - Store routing: each extracted memory is sent through the existing
*     verify-and-store pipeline. Decisions → brain_decisions, patterns →
*     brain_patterns, learnings/constraints/corrections → brain_learnings.
*   - Source is tagged `agent-llm-extracted` so downstream dedup, quality
*     scoring, and consolidation can distinguish LLM output from other
*     pipelines.
*
* Research basis: `.cleo/agent-outputs/R-llm-memory-systems-research.md`
* (Mem0, Hindsight, Letta, Mastra Observational Memory).
*/
var ExtractedMemorySchema = object({
	type: _enum([
		"decision",
		"pattern",
		"learning",
		"constraint",
		"correction"
	]),
	content: string().min(1).max(500),
	importance: number().min(0).max(1),
	entities: array(string()).max(20),
	justification: string().min(1).max(200)
});
var ExtractionResponseSchema = object({ memories: array(ExtractedMemorySchema).max(20) });
var SYSTEM_PROMPT = `You are extracting durable knowledge from a coding session transcript.
Extract ONLY high-value items. Reject noise (greetings, meta-commentary, transient state).

For each extraction provide:
- type: decision|pattern|learning|constraint|correction
- content: the knowledge itself (<=500 chars, declarative form)
- importance: 0.0-1.0 (only >=0.6 will be stored)
- entities: code symbols, files, concepts mentioned
- justification: why this is worth remembering (<=200 chars)

Type definitions:
- decision: architectural or design choice with rationale ("We chose X because Y")
- pattern: recurring approach that works ("When X, do Y because Z")
- learning: factual knowledge gained ("X depends on Y via Z")
- constraint: rule/limitation discovered ("X must always Y")
- correction: anti-pattern to avoid ("Avoid X because Y; use Z instead")

Rules:
- Capture the WHY, not just the WHAT
- If nothing of durable value exists in the transcript, return an empty array
- Prefer fewer high-quality extractions over many low-quality ones
- Ignore: tool output noise, routine status messages, file reads without a finding
- Maximum 7 extractions per call`;
function buildUserPrompt(sessionId, transcript, maxExtractions) {
	return `Session ID: ${sessionId}

Transcript:
${transcript}

Extract up to ${maxExtractions} high-value memories. Return empty array if nothing valuable.`;
}
/**
* Trim a transcript to a maximum character budget, preserving the head and tail
* (the ends typically contain the request and the final outcome).
*/
function clipTranscript(transcript, maxChars) {
	if (transcript.length <= maxChars) return transcript;
	const half = Math.floor(maxChars / 2) - 64;
	const head = transcript.slice(0, half);
	const tail = transcript.slice(-half);
	return `${head}\n\n[... ${transcript.length - maxChars} chars omitted ...]\n\n${tail}`;
}
/**
* Store a single LLM-extracted memory, routing it through the full dedup gate.
*
* T736: All memory types now pass through the hash-dedup gate before reaching
* the underlying store function. For decisions (which have domain-specific fields
* that `verifyAndStore` cannot set), we run `checkHashDedup` first and call
* `storeDecision` only when no duplicate is found. For all other types, we
* build a `MemoryCandidate` and delegate to `verifyAndStore` which runs both
* hash-dedup (Check A) and the full embedding + confidence gate (Checks B, C).
*
* Routing:
*   - decision    → checkHashDedup(brain_decisions) → storeDecision (domain fields preserved)
*   - pattern     → verifyAndStore (procedural candidate → brain_patterns)
*   - learning    → verifyAndStore (semantic candidate → brain_learnings)
*   - constraint  → verifyAndStore (semantic candidate, higher confidence → brain_learnings)
*   - correction  → verifyAndStore (procedural candidate → brain_patterns)
*/
async function storeExtracted(projectRoot, sessionId, memory) {
	if (memory.type === "decision") {
		const dedupResult = await checkHashDedup(projectRoot, memory.content, "brain_decisions");
		if (dedupResult.matched) {
			const { getBrainNativeDb, getBrainDb } = await import('./memory-sqlite-Dg5uGJCo.js');
			getBrainDb(projectRoot).then(() => {
				getBrainNativeDb()?.prepare("UPDATE brain_decisions SET citation_count = citation_count + 1 WHERE id = ?").run(dedupResult.id);
			}).catch(() => void 0);
			return "merged";
		}
		const { storeDecision } = await import('./decisions2-BUXPTVBl.js');
		try {
			const { decisionText, rationale } = splitDecisionContent(memory.content);
			await storeDecision(projectRoot, {
				type: "technical",
				decision: decisionText,
				rationale,
				confidence: mapImportanceToConfidence(memory.importance)
			});
			return "stored";
		} catch {
			return "rejected";
		}
	}
	const confidence = memory.type === "constraint" ? Math.max(memory.importance, .8) : memory.importance;
	let candidate;
	if (memory.type === "correction" || memory.type === "pattern") candidate = {
		text: memory.content,
		title: `${memory.type === "correction" ? "Correction" : "Pattern"}: ${memory.content.slice(0, 80)}`,
		memoryType: "procedural",
		tier: "medium",
		confidence,
		source: "transcript",
		sourceSessionId: sessionId,
		sourceConfidence: "agent"
	};
	else candidate = {
		text: memory.content,
		title: `${memory.type === "constraint" ? "Constraint" : "Learning"}: ${memory.content.slice(0, 80)}`,
		memoryType: "semantic",
		tier: "medium",
		confidence,
		source: "transcript",
		sourceSessionId: sessionId,
		sourceConfidence: "agent"
	};
	try {
		const result = await verifyAndStore(projectRoot, candidate);
		if (result.action === "stored") return "stored";
		if (result.action === "merged") return "merged";
		return "rejected";
	} catch {
		return "rejected";
	}
}
/**
* Split a decision extraction into decision + rationale at the first " because "
* marker. When no marker exists, the full content becomes the decision and the
* justification becomes the rationale.
*/
function splitDecisionContent(content) {
	const idx = content.toLowerCase().indexOf(" because ");
	if (idx === -1) return {
		decisionText: content.trim(),
		rationale: "See justification"
	};
	return {
		decisionText: content.slice(0, idx).trim(),
		rationale: content.slice(idx + 1).trim()
	};
}
/**
* Map an importance score to a BrainDecisionRow confidence bucket.
* Importance is continuous 0–1; confidence is `low` | `medium` | `high`.
*/
function mapImportanceToConfidence(importance) {
	if (importance >= .8) return "high";
	if (importance >= .6) return "medium";
	return "low";
}
/**
* Resolve provider+model+credentials via `resolveAnthropicForRole('extraction')`
* (T-LLM-CRED Phase 2 DRY review P2-1) and return the wired SDK client +
* selected model name.
*
* Honors `cred.authType` so OAuth tokens (Claude Code) are routed through
* `Authorization: Bearer` instead of `x-api-key` — the registry's
* `clientForModelConfig` handles this internally via the `extraHeaders`
* channel set by the resolver.
* Returns null when no Anthropic credential is available (helper returns
* null on both missing-credential and wrong-provider paths).
*/
async function buildExtractionClient(projectRoot) {
	const llm = await resolveAnthropicForRole("extraction", { projectRoot });
	if (!llm) return null;
	return {
		client: llm.client,
		model: llm.model
	};
}
/**
* Build the zodOutputFormat helper asynchronously (dynamic import matches
* SDK's subpath export style). Returns null when the SDK cannot be loaded.
*/
async function buildZodFormat(schema) {
	try {
		return (await import('./zod-rX0tyJVM.js')).zodOutputFormat(schema);
	} catch {
		return null;
	}
}
/**
* Extract and persist structured memories from a session transcript using
* an Anthropic model.
*
* This is the sole entry point replacing the legacy keyword-regex extractor.
* Callers (e.g. the session-end hook) invoke this and the function handles:
*   1. Config lookup (brain.llmExtraction.enabled, model, thresholds)
*   2. API key detection (skips extraction if missing)
*   3. Transcript clipping to stay within token budget
*   4. Structured-output LLM call with the research-approved prompt
*   5. Importance filtering (below minImportance → dropped)
*   6. Storage routing through the existing verify-and-store pipeline
*
* Never throws. Returns an ExtractionReport describing what was stored.
*
* @example
* ```ts
* await extractFromTranscript({
*   projectRoot: '/path/to/project',
*   sessionId: 'ses_20260413192026_519188',
*   transcript: rawTranscript,
* });
* ```
*/
async function extractFromTranscript(options) {
	const report = {
		extractedCount: 0,
		storedCount: 0,
		mergedCount: 0,
		rejectedCount: 0,
		warnings: []
	};
	const { projectRoot, sessionId, transcript } = options;
	if (typeof transcript !== "string" || transcript.trim().length === 0) return report;
	const { loadConfig } = await import('./config2-CdsX487B.js');
	let config;
	try {
		config = await loadConfig(projectRoot);
	} catch {
		report.warnings.push("config load failed, using defaults");
		config = void 0;
	}
	const llmCfg = config?.brain?.llmExtraction;
	if (!(llmCfg?.enabled ?? true)) {
		report.warnings.push("llmExtraction.enabled=false");
		return report;
	}
	const minImportance = llmCfg?.minImportance ?? .6;
	const maxExtractions = llmCfg?.maxExtractions ?? 7;
	const maxTranscriptChars = llmCfg?.maxTranscriptChars ?? 6e4;
	const resolved = options.client ? null : await buildExtractionClient(projectRoot);
	const client = options.client ?? resolved?.client ?? null;
	if (!client) {
		report.warnings.push("No Anthropic API key found (checked ANTHROPIC_API_KEY env and ~/.claude/.credentials.json) — extraction skipped");
		return report;
	}
	const model = llmCfg?.model ?? resolved?.model ?? "claude-haiku-4-5-20251001";
	const userPrompt = buildUserPrompt(sessionId, clipTranscript(transcript, maxTranscriptChars), maxExtractions);
	let extracted = [];
	try {
		const format = await buildZodFormat(ExtractionResponseSchema);
		if (format) extracted = (await client.messages.parse({
			model,
			max_tokens: 4096,
			system: SYSTEM_PROMPT,
			messages: [{
				role: "user",
				content: userPrompt
			}],
			output_config: { format }
		})).parsed_output?.memories ?? [];
		else extracted = await extractViaPlainCall(client, model, userPrompt);
	} catch (err) {
		const message = err instanceof Error ? err.message : String(err);
		report.warnings.push(`extraction call failed: ${message}`);
		return report;
	}
	report.extractedCount = extracted.length;
	for (const memory of extracted) {
		if (memory.importance < minImportance) {
			report.rejectedCount += 1;
			continue;
		}
		try {
			const outcome = await storeExtracted(projectRoot, sessionId, memory);
			if (outcome === "stored") report.storedCount += 1;
			else if (outcome === "merged") report.mergedCount += 1;
			else report.rejectedCount += 1;
		} catch (err) {
			const message = err instanceof Error ? err.message : String(err);
			report.warnings.push(`store failed: ${message}`);
			report.rejectedCount += 1;
		}
	}
	return report;
}
/**
* Fallback extraction path: calls messages.create and parses the response
* content manually. Used when the zod helper cannot be loaded (e.g. SDK
* version mismatch) but the messages.create API is still available.
*/
async function extractViaPlainCall(client, model, userPrompt) {
	const text = extractTextFromResponse(await client.messages.create({
		model,
		max_tokens: 4096,
		system: `${SYSTEM_PROMPT}\n\nReturn ONLY a JSON object of shape {"memories": [...]} with no prose.`,
		messages: [{
			role: "user",
			content: userPrompt
		}]
	}));
	if (!text) return [];
	const parsed = safeJsonParse(text);
	if (!parsed) return [];
	const validated = ExtractionResponseSchema.safeParse(parsed);
	if (!validated.success) return [];
	return validated.data.memories;
}
/**
* Pull plain text from a messages.create response. Anthropic returns a
* content array; we concatenate all text blocks.
*/
function extractTextFromResponse(response) {
	const content = response?.content;
	if (!Array.isArray(content)) return "";
	return content.filter((block) => block?.type === "text" && typeof block.text === "string").map((block) => block.text ?? "").join("");
}
/**
* Parse JSON, extracting the first {...} object even if the model wrapped
* it in markdown fences or added prose. Returns null on any failure.
*/
function safeJsonParse(text) {
	const trimmed = text.trim();
	try {
		return JSON.parse(trimmed);
	} catch {}
	const first = trimmed.indexOf("{");
	const last = trimmed.lastIndexOf("}");
	if (first === -1 || last === -1 || last < first) return null;
	try {
		return JSON.parse(trimmed.slice(first, last + 1));
	} catch {
		return null;
	}
}

export { extractFromTranscript };
//# sourceMappingURL=llm-extraction-DuIdhNKI.js.map
