/**
 * MCP tools for searching and retrieving transcript-based memory.
 *
 * Two tools are exposed:
 *  - memory_transcript_search: keyword/semantic/hybrid search over wake cycle summaries
 *  - memory_transcript_detail: retrieve key excerpts or full source data for a wake cycle
 *
 * Note: the underlying source data comes from two different file types:
 *  - Live cycles: Claude SDK transcripts (~/.claude/projects/...)
 *  - Backfilled cycles: VisionClaw runtime logs (~/.visionclaw/.../logs/)
 */
import { z } from "zod";
import { tool } from "@anthropic-ai/claude-agent-sdk";
import { searchWakeCycles, searchSemanticAsync, searchHybridAsync, isSemanticSearchAvailable, getWakeCycleExcerpts, getWakeCycleFullTranscript, } from "../agent/transcript/transcript-indexer.js";
import { loadConfig } from "../config/index.js";
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
function ok(text) {
    return { content: [{ type: "text", text }] };
}
function err(text) {
    return { content: [{ type: "text", text }], isError: true };
}
// ---------------------------------------------------------------------------
// memory_transcript_search
// ---------------------------------------------------------------------------
export const transcriptSearchTool = tool("memory_transcript_search", `Search past wake cycle summaries to recall previous conversations, decisions, and actions.
This searches an indexed database of wake cycle transcripts — use it when you need to remember something from a previous session that may not be in your current context or memory files.

Returns lightweight summaries only (Layer 1). Use memory_transcript_detail to get more information about a specific result.

Search modes:
- hybrid (recommended): combines keyword + semantic for best results. Use for most queries. Example: "discussion about auth middleware rewrite"
- keyword: FTS5 full-text search with BM25 ranking. Best for exact terms, identifiers, or error messages. Example: "ECONNREFUSED", "deployScript.ts"
- semantic: vector similarity search (requires embeddings). Best for conceptual/meaning-based queries. Example: "when did we set up the CI pipeline"

Tips:
- Start with hybrid search for most queries
- Use keyword only when searching for exact strings, rephrase key words if needed.
- Filter by session_type ("general" or "coding") and time_range to narrow results`, {
    query: z
        .string()
        .describe("Search query: keywords or natural language description of what you are looking for"),
    type: z
        .enum(["keyword", "semantic", "hybrid"])
        .optional()
        .describe("Search type. Default: hybrid"),
    time_range: z
        .object({
        start: z.string().describe("Start time (ISO8601)"),
        end: z.string().describe("End time (ISO8601)"),
    })
        .optional()
        .describe("Optional time range filter"),
    session_type: z
        .enum(["general", "coding"])
        .optional()
        .describe("Optional session type filter"),
    limit: z
        .number()
        .int()
        .min(1)
        .max(20)
        .optional()
        .describe("Max results to return. Default: 5"),
}, async (args) => {
    try {
        const searchType = args.type ?? "hybrid";
        const limit = args.limit ?? 5;
        const baseOpts = {
            query: args.query,
            timeRange: args.time_range,
            sessionType: args.session_type,
            limit,
        };
        let results = [];
        let actualSearchType = searchType;
        if (searchType === "semantic" || searchType === "hybrid") {
            // Semantic / hybrid require async embedding generation + config
            let config;
            try {
                config = loadConfig();
            }
            catch {
                // Fall back to keyword if config unavailable
            }
            if (config) {
                const asyncOpts = { ...baseOpts, type: searchType, config };
                results =
                    searchType === "hybrid"
                        ? await searchHybridAsync(asyncOpts)
                        : await searchSemanticAsync(asyncOpts);
            }
            else {
                actualSearchType = "keyword";
                results = searchWakeCycles({ ...baseOpts, type: "keyword" });
            }
        }
        else {
            results = searchWakeCycles({ ...baseOpts, type: "keyword" });
        }
        // Auto-fallback: if keyword search returns 0 results, try hybrid
        if (results.length === 0 &&
            actualSearchType === "keyword" &&
            isSemanticSearchAvailable()) {
            let config;
            try {
                config = loadConfig();
            }
            catch {
                // ignore
            }
            if (config) {
                actualSearchType = "hybrid";
                results = await searchHybridAsync({
                    ...baseOpts,
                    type: "hybrid",
                    config,
                });
            }
        }
        if (results.length === 0) {
            return ok(`No matching wake cycles found for query: "${args.query}"\n\nTip: Try different keywords, rephrase your query, or broaden the time range.`);
        }
        // Normalize relevance scores to 0-1 range for consistent display
        const maxScore = Math.max(...results.map((r) => r.relevance_score));
        if (maxScore > 0) {
            for (const r of results) {
                r.relevance_score = r.relevance_score / maxScore;
            }
        }
        const lines = [];
        const fallbackNote = actualSearchType !== searchType
            ? ` [auto-fallback from ${searchType} → ${actualSearchType}]`
            : "";
        const modeLabel = actualSearchType === "hybrid"
            ? ` (hybrid: keyword + semantic)${fallbackNote}`
            : actualSearchType === "semantic"
                ? ` (semantic)${fallbackNote}`
                : fallbackNote;
        lines.push(`Found ${results.length} matching wake cycle(s)${modeLabel}:\n`);
        for (const r of results) {
            lines.push(`### ${r.wake_cycle_id}`);
            lines.push(`- **Session**: ${r.session_type}`);
            lines.push(`- **Time**: ${r.timestamp_start} → ${r.timestamp_end}`);
            lines.push(`- **Relevance**: ${r.relevance_score.toFixed(3)}`);
            lines.push(`- **Summary**: ${r.summary}`);
            lines.push("");
        }
        lines.push("Use `memory_transcript_detail` with a wake_cycle_id to get key excerpts or the full source content.");
        return ok(lines.join("\n"));
    }
    catch (e) {
        return err(`Transcript search error: ${e instanceof Error ? e.message : String(e)}`);
    }
});
// ---------------------------------------------------------------------------
// memory_transcript_detail
// ---------------------------------------------------------------------------
export const transcriptDetailTool = tool("memory_transcript_detail", `Retrieve detailed information about a specific wake cycle found via memory_transcript_search.

Levels:
- excerpts: Returns key excerpts (important quotes from the wake cycle). Fast and lightweight (~2000 tokens).
- full: Returns the complete original source content for the wake cycle (Claude transcript or runtime log). Can be large — use only when excerpts are insufficient.`, {
    wake_cycle_id: z
        .string()
        .describe("The wake_cycle_id from a memory_transcript_search result (e.g. 2026-03-31_general_003)"),
    level: z
        .enum(["excerpts", "full"])
        .describe("Detail level: 'excerpts' for key quotes, 'full' for the complete original source content"),
}, async (args) => {
    try {
        if (args.level === "excerpts") {
            const excerpts = getWakeCycleExcerpts(args.wake_cycle_id);
            if (excerpts === null) {
                return err(`Wake cycle not found: ${args.wake_cycle_id}. Use memory_transcript_search first to find valid IDs.`);
            }
            if (!excerpts) {
                return ok(`No key excerpts were extracted for wake cycle ${args.wake_cycle_id}. Try level="full" to read the original source content.`);
            }
            try {
                const parsed = JSON.parse(excerpts);
                if (Array.isArray(parsed) && parsed.length > 0) {
                    const lines = [];
                    lines.push(`Key excerpts from wake cycle ${args.wake_cycle_id}:\n`);
                    for (let i = 0; i < parsed.length; i++) {
                        lines.push(`${i + 1}. ${parsed[i]}`);
                    }
                    return ok(lines.join("\n"));
                }
            }
            catch {
                // If not valid JSON array, return raw
            }
            return ok(`Key excerpts from wake cycle ${args.wake_cycle_id}:\n\n${excerpts}`);
        }
        // level === "full"
        const transcript = await getWakeCycleFullTranscript(args.wake_cycle_id);
        if (transcript === null) {
            return err(`Wake cycle not found: ${args.wake_cycle_id}. Use memory_transcript_search first to find valid IDs.`);
        }
        // Truncate very large source content to avoid blowing up context
        const maxLength = 50_000;
        const truncated = transcript.length > maxLength
            ? transcript.slice(0, maxLength) +
                `\n\n...[Content truncated at ${maxLength} chars. ${transcript.length - maxLength} chars omitted.]`
            : transcript;
        return ok(`Full source content for wake cycle ${args.wake_cycle_id}:\n\n${truncated}`);
    }
    catch (e) {
        return err(`Transcript detail error: ${e instanceof Error ? e.message : String(e)}`);
    }
});
//# sourceMappingURL=transcript-memory.js.map