import { a as getDb } from './sqlite-Dsbe0wec.js';
import { g as getBrainAccessor } from './memory-accessor2-BzlgmY-Z.js';
import { s as sessionExistsInTasksDb } from './cross-db-cleanup-C_FLyd-6.js';
import { isEmbeddingAvailable, embedText } from './brain-embedding-Djw9ekXu.js';
import { u as upsertGraphNode, a as addGraphEdge } from './graph-auto-populate2-DYsUNGSL.js';
import { d as computeObservationQuality } from './quality-scoring-CCPuRQmJ.js';
import { createHash } from 'node:crypto';

//#region ../core/dist/memory/retrieval/observe.js
/**
* BRAIN Observe — unified write path for observations + embedding backfill.
*
* @task T5134
* @epic T5149
*/
/** Keyword patterns for auto-classifying observation type from text. */
var TYPE_KEYWORDS = [
	{
		keywords: [
			"bug",
			"fix",
			"error",
			"crash"
		],
		type: "bugfix"
	},
	{
		keywords: [
			"refactor",
			"rename",
			"extract",
			"move"
		],
		type: "refactor"
	},
	{
		keywords: [
			"add",
			"create",
			"implement",
			"new"
		],
		type: "feature"
	},
	{
		keywords: [
			"decide",
			"chose",
			"pick",
			"instead"
		],
		type: "decision"
	},
	{
		keywords: [
			"update",
			"change",
			"modify",
			"upgrade"
		],
		type: "change"
	}
];
/** Auto-classify observation type from text using keyword matching. */
function classifyObservationType(text) {
	const lower = text.toLowerCase();
	for (const { keywords, type } of TYPE_KEYWORDS) for (const keyword of keywords) if (lower.includes(keyword)) return type;
	return "discovery";
}
/** Monotonic counter to prevent ID collisions within the same millisecond. */
var observeSeq = 0;
/**
* Auto-link a newly created observation to the currently focused task.
*
* Queries the active session via sessionStatus() and reads taskWork.taskId.
* If a task is focused, inserts a brain_memory_links row linking the
* observation to that task with linkType 'produced_by'.
*
* All failures are silently swallowed — this is a best-effort side effect.
*
* @param projectRoot - Project root directory
* @param observationId - ID of the newly created observation
* @param accessor - BrainDataAccessor to use for the link insert
*/
async function autoLinkObservationToTask(projectRoot, observationId, accessor) {
	const { sessionStatus } = await import('./sessions2-B5eAcSUJ.js');
	const session = await sessionStatus(projectRoot, {});
	if (!session) return;
	const taskId = session.taskWork?.taskId;
	if (!taskId) return;
	await accessor.addLink({
		memoryType: "observation",
		memoryId: observationId,
		taskId,
		linkType: "produced_by"
	});
}
/**
* Save an observation to the BRAIN observations table.
* Replaces the external claude-mem save_observation pattern.
*
* Auto-classifies type from text if not provided. Generates a
* unique ID with O- prefix + base36 timestamp.
*
* @param projectRoot - Project root directory
* @param params - Observation data
* @returns Created observation ID, type, and timestamp
*
* @example
* ```ts
* // Save a decision observation to the BRAIN.
* // The result contains the auto-generated ID, classified type, and timestamp.
* const result = await observeBrain('/path/to/project', {
*   text: 'Decided to use ESM-only imports for better tree-shaking.',
*   title: 'ESM-only import decision',
*   type: 'decision',
*   sourceType: 'session-debrief',
* });
*
* console.assert(result.id.startsWith('O-'), 'ID uses O- prefix');
* console.assert(result.type === 'decision', 'type preserved from params');
* console.assert(typeof result.createdAt === 'string', 'createdAt is ISO timestamp');
* ```
*/
async function observeBrain(projectRoot, params) {
	const { text, title: titleParam, type: typeParam, project, sourceSessionId, sourceType, agent, sourceConfidence: sourceConfidenceParam, crossRef, attachmentRefs, origin, provenanceChain, _skipGate, _skipQueue } = params;
	if (!text?.trim()) throw new Error("Observation text is required");
	if (!_skipQueue) {
		const { enqueueBrainWrite } = await import('./brain-writer-thread-Ddrc33cA.js');
		const result = await enqueueBrainWrite({
			kind: "observe",
			projectRoot,
			params
		});
		if (result.kind !== "observe") throw new Error(`Unexpected writer result kind: ${result.kind}`);
		return result.result;
	}
	if (!_skipGate) {
		const { verifyCandidate } = await import('./extraction-gate-Ctn6QtD_.js');
		const title = titleParam ?? text.slice(0, 120);
		const resolvedSourceConf = sourceConfidenceParam ?? (sourceType === "manual" ? "owner" : sourceType === "session-debrief" ? "task-outcome" : "agent");
		const gateResult = await verifyCandidate(projectRoot, {
			text,
			title,
			memoryType: "episodic",
			tier: "short",
			confidence: .6,
			source: sourceType === "manual" ? "manual" : "transcript",
			sourceSessionId,
			sourceConfidence: resolvedSourceConf,
			trusted: resolvedSourceConf === "owner" || resolvedSourceConf === "task-outcome"
		});
		if (gateResult.action !== "stored") {
			const now = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19);
			return {
				id: gateResult.id ?? `O-gate-${Date.now().toString(36)}`,
				type: typeParam ?? "observation",
				createdAt: now
			};
		}
	}
	const type = typeParam ?? classifyObservationType(text);
	const title = titleParam ?? text.slice(0, 120);
	const now = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19);
	const resolvedSourceConfidence = sourceConfidenceParam ?? (sourceType === "manual" ? "owner" : sourceType === "session-debrief" ? "task-outcome" : "agent");
	const taskIdMatches = text.match(/T\d+/g) ?? [];
	const hasMultipleTaskRefs = new Set(taskIdMatches).size >= 2;
	const hasCrossRef = Array.isArray(crossRef) && crossRef.length >= 1;
	const memoryTier = hasMultipleTaskRefs || hasCrossRef ? "medium" : "short";
	const memoryType = "episodic";
	const verified = resolvedSourceConfidence === "owner" || resolvedSourceConfidence === "task-outcome";
	const contentHash = createHash("sha256").update(text.trim().toLowerCase()).digest("hex").slice(0, 16);
	const { getBrainNativeDb } = await import('./memory-sqlite-Dg5uGJCo.js');
	const nativeDb = getBrainNativeDb();
	let validSessionId = sourceSessionId ?? null;
	if (validSessionId) try {
		const tasksDb = await getDb(projectRoot);
		if (!await sessionExistsInTasksDb(validSessionId, tasksDb)) validSessionId = null;
	} catch {
		validSessionId = null;
	}
	const qualityScore = computeObservationQuality({
		text,
		title,
		sourceConfidence: resolvedSourceConfidence,
		memoryTier
	});
	const id = `O-${Date.now().toString(36)}-${(observeSeq++ % 1e3).toString(36)}`;
	const accessor = await getBrainAccessor(projectRoot);
	const row = await accessor.addObservation({
		id,
		type,
		title,
		narrative: text,
		contentHash,
		project: project ?? null,
		sourceSessionId: validSessionId,
		sourceType: sourceType ?? "agent",
		agent: agent ?? null,
		qualityScore,
		createdAt: now,
		memoryTier,
		memoryType,
		sourceConfidence: resolvedSourceConfidence,
		verified,
		...attachmentRefs && attachmentRefs.length > 0 ? { attachmentsJson: JSON.stringify(attachmentRefs) } : {},
		...origin != null ? { origin } : {},
		...provenanceChain && provenanceChain.length > 0 ? { provenanceChain: JSON.stringify(provenanceChain) } : {}
	});
	if (isEmbeddingAvailable()) setImmediate(() => {
		embedText(text).then((vector) => {
			if (vector && nativeDb) nativeDb.prepare("INSERT OR REPLACE INTO brain_embeddings (id, embedding) VALUES (?, ?)").run(id, Buffer.from(vector.buffer));
		}).catch(() => {});
	});
	if (type === "decision") import('./memory-bridge-C_YgF01K.js').then(({ refreshMemoryBridge }) => refreshMemoryBridge(projectRoot)).catch(() => {});
	if (validSessionId) autoLinkObservationToTask(projectRoot, row.id, accessor).catch(() => {});
	try {
		await upsertGraphNode(projectRoot, `observation:${row.id}`, "observation", row.title.substring(0, 200), row.qualityScore ?? .5, row.narrative ?? row.title, {
			sourceType: row.sourceType,
			agent: row.agent ?? void 0
		});
		if (validSessionId) {
			await upsertGraphNode(projectRoot, `session:${validSessionId}`, "session", validSessionId, .8, "");
			await addGraphEdge(projectRoot, `observation:${row.id}`, `session:${validSessionId}`, "produced_by", 1, "auto:observe");
		}
	} catch {}
	return {
		id: row.id,
		type: row.type,
		createdAt: row.createdAt
	};
}

export { observeBrain as o };
//# sourceMappingURL=observe2-hCg9xf6C.js.map
