import { t as createSubsystemLogger } from "./subsystem-qDUB67UD.js";
import { i as updateTrustFromImplicit, r as updateTrustFromFeedback } from "./trust-calculator-lCRAZCPt.js";
import { o as updateBanditFromFeedback, t as adaptFrequency } from "./preference-learner-CPBoPJrB.js";
//#region src/cognitive/feedback/calibration.ts
const MIN_RECORDS_FOR_RELIABLE = 10;
const MAX_HISTORY_WINDOW = 50;
const SLOPE_MIN = .3;
const SLOPE_MAX = 2;
/** Outcome types that count as "actual positive" for calibration. */
const POSITIVE_OUTCOMES = new Set(["positive", "engaged"]);
/**
* Compute the calibration slope from history using simplified linear regression.
*
* slope = Σ(predicted × actual) / Σ(predicted²)
*
* - slope < 1 → system is overconfident (predicted high, actual low)
* - slope > 1 → system is underconfident (predicted low, actual high)
* - slope = 1 → perfectly calibrated
*
* Uses the last 50 records. Returns 1.0 (no correction) when < 10 records.
*/
function computeCalibrationSlope(history) {
	if (history.length < MIN_RECORDS_FOR_RELIABLE) return 1;
	const window = history.slice(-MAX_HISTORY_WINDOW);
	let sumPredictedActual = 0;
	let sumPredictedSq = 0;
	for (const record of window) {
		const actual = POSITIVE_OUTCOMES.has(record.actualOutcome) ? 1 : 0;
		sumPredictedActual += record.predictedPAccept * actual;
		sumPredictedSq += record.predictedPAccept * record.predictedPAccept;
	}
	if (sumPredictedSq === 0) return 1;
	const slope = sumPredictedActual / sumPredictedSq;
	return Math.max(SLOPE_MIN, Math.min(SLOPE_MAX, slope));
}
/**
* Apply calibration correction to a raw pAccept value.
* adjustedPAccept = pAccept * slope, clamped to [0, 1].
*/
function applyCalibrationCorrection(pAccept, slope) {
	return Math.max(0, Math.min(1, pAccept * slope));
}
//#endregion
//#region src/cognitive/feedback/collector.ts
const log = createSubsystemLogger("cognitive/feedback-collector");
/**
* Process a feedback event and return an updated PersonaTree.
* Does NOT mutate the input.
*/
function processFeedback(persona, feedback) {
	const updatedProfile = updateBanditFromFeedback(persona.feedbackProfile, feedback);
	const updatedRapport = updateTrustFromFeedback(persona.rapport, feedback);
	const updatedFrequency = adaptFrequency(updatedProfile.optimalFrequencyHours, feedback);
	return {
		...persona,
		feedbackProfile: {
			...updatedProfile,
			optimalFrequencyHours: updatedFrequency,
			lastProactiveAt: feedback.type === "positive" || feedback.type === "engaged" ? feedback.timestamp : updatedProfile.lastProactiveAt
		},
		rapport: updatedRapport
	};
}
/**
* Process implicit feedback signals and return an updated PersonaTree.
*/
function processImplicitFeedback(persona, signals) {
	if (signals.length === 0) return persona;
	const updatedRapport = updateTrustFromImplicit(persona.rapport, signals);
	const updatedBandits = { ...persona.feedbackProfile.topicBandits };
	const now = Date.now();
	for (const signal of signals) {
		if (signal.topic && signal.type === "topic_continuation") {
			const bandit = updatedBandits[signal.topic] ?? {
				alpha: 2,
				beta: 1
			};
			updatedBandits[signal.topic] = {
				alpha: bandit.alpha + .5,
				beta: bandit.beta,
				lastUpdated: now
			};
		}
		if (signal.topic && signal.type === "topic_abandonment") {
			const bandit = updatedBandits[signal.topic] ?? {
				alpha: 2,
				beta: 1
			};
			updatedBandits[signal.topic] = {
				alpha: bandit.alpha,
				beta: bandit.beta + .3,
				lastUpdated: now
			};
		}
	}
	for (const signal of signals) if (signal.topic) {
		if (signal.type === "response_length" && signal.value > 100) {
			const bandit = updatedBandits[signal.topic] ?? {
				alpha: 2,
				beta: 1
			};
			updatedBandits[signal.topic] = {
				...bandit,
				alpha: bandit.alpha + .3,
				lastUpdated: now
			};
		}
		if (signal.type === "response_length" && signal.value < 20) {
			const bandit = updatedBandits[signal.topic] ?? {
				alpha: 2,
				beta: 1
			};
			updatedBandits[signal.topic] = {
				...bandit,
				beta: bandit.beta + .2,
				lastUpdated: now
			};
		}
		if (signal.type === "question_depth" && signal.value === 1) {
			const bandit = updatedBandits[signal.topic] ?? {
				alpha: 2,
				beta: 1
			};
			updatedBandits[signal.topic] = {
				...bandit,
				alpha: bandit.alpha + .4,
				lastUpdated: now
			};
		}
	}
	const updatedTopics = Object.keys(updatedBandits).filter((t) => !(t in persona.feedbackProfile.topicBandits) || persona.feedbackProfile.topicBandits[t].alpha !== updatedBandits[t].alpha || persona.feedbackProfile.topicBandits[t].beta !== updatedBandits[t].beta);
	if (updatedTopics.length > 0) log.info("implicit feedback bandits updated", {
		signalCount: signals.length,
		updatedTopics,
		topicProvided: signals[0]?.topic ?? false
	});
	return {
		...persona,
		rapport: updatedRapport,
		feedbackProfile: {
			...persona.feedbackProfile,
			topicBandits: updatedBandits
		}
	};
}
/**
* Infer the most likely conversation topic from LLM extraction results and user text.
* Three-level fallback: extraction domain → user text keyword match → persona recentFocus.
*/
function inferTopicFromContext(persona, extraction, userText) {
	if (extraction.domains && extraction.domains.length > 0) return extraction.domains[0].name;
	const domainKeys = Object.keys(persona.domains);
	for (const domain of domainKeys) if (userText.includes(domain)) return domain;
	if (persona.recentFocus.length > 0) return persona.recentFocus[0];
}
/**
* Extract implicit feedback signals from a conversation turn.
*/
function extractImplicitSignals(userMessage, responseLatencyMs, topic, previousTopics) {
	const signals = [];
	signals.push({
		type: "response_length",
		topic,
		value: userMessage.length,
		timestamp: Date.now()
	});
	if (responseLatencyMs !== void 0) signals.push({
		type: "response_latency",
		topic,
		value: responseLatencyMs,
		timestamp: Date.now()
	});
	if (/[为什么|如何|怎样|what if|why|how come|深层|本质]/.test(userMessage)) signals.push({
		type: "question_depth",
		topic,
		value: 1,
		timestamp: Date.now()
	});
	if (topic && previousTopics && previousTopics.length > 0) {
		const recentTopics = previousTopics.slice(-3);
		if (recentTopics.some((t) => t.toLowerCase() === topic.toLowerCase())) signals.push({
			type: "topic_continuation",
			topic,
			value: 1,
			timestamp: Date.now()
		});
		else if (recentTopics.length > 0) {
			const abandonedTopic = recentTopics[recentTopics.length - 1];
			signals.push({
				type: "topic_abandonment",
				topic: abandonedTopic,
				value: 1,
				timestamp: Date.now()
			});
		}
	}
	return signals;
}
function processNoResponse(persona, context) {
	const prev = persona.feedbackProfile.consecutiveNoResponses ?? 0;
	const updated = prev + 1;
	if (!context) {
		log.info("no-response streak incremented", {
			prev,
			updated
		});
		return {
			...persona,
			feedbackProfile: {
				...persona.feedbackProfile,
				consecutiveNoResponses: updated
			}
		};
	}
	const updatedBandits = { ...persona.feedbackProfile.topicBandits };
	const now = Date.now();
	const penalizedDomains = [];
	for (const domain of context.previousDomains) {
		const bandit = updatedBandits[domain] ?? {
			alpha: 2,
			beta: 1
		};
		updatedBandits[domain] = {
			alpha: bandit.alpha,
			beta: bandit.beta + .3,
			lastUpdated: now
		};
		penalizedDomains.push(domain);
	}
	let updatedModeBandits = persona.feedbackProfile.modeBandits ? { ...persona.feedbackProfile.modeBandits } : void 0;
	let penalizedMode;
	if (context.previousMode) {
		updatedModeBandits = updatedModeBandits ?? {};
		const modeBandit = updatedModeBandits[context.previousMode] ?? {
			alpha: 2,
			beta: 1
		};
		updatedModeBandits[context.previousMode] = {
			alpha: modeBandit.alpha,
			beta: modeBandit.beta + .2,
			lastUpdated: now
		};
		penalizedMode = context.previousMode;
	}
	log.info("no-response bandits updated", {
		domains: penalizedDomains,
		mode: penalizedMode,
		streak: updated
	});
	return {
		...persona,
		feedbackProfile: {
			...persona.feedbackProfile,
			consecutiveNoResponses: updated,
			topicBandits: updatedBandits,
			...updatedModeBandits !== void 0 ? { modeBandits: updatedModeBandits } : {}
		}
	};
}
function resetNoResponseStreak(persona) {
	if ((persona.feedbackProfile.consecutiveNoResponses ?? 0) === 0) return persona;
	log.info("no-response streak reset", { prev: persona.feedbackProfile.consecutiveNoResponses });
	return {
		...persona,
		feedbackProfile: {
			...persona.feedbackProfile,
			consecutiveNoResponses: 0
		}
	};
}
//#endregion
export { processNoResponse as a, computeCalibrationSlope as c, processImplicitFeedback as i, inferTopicFromContext as n, resetNoResponseStreak as o, processFeedback as r, applyCalibrationCorrection as s, extractImplicitSignals as t };
