"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.createKnowledgeServer = createKnowledgeServer;
const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
const zod_1 = require("zod");
const logger_1 = require("../../lib/logger");
const knowledge = __importStar(require("../activities/knowledge"));
const storeSchema = zod_1.z.object({
    domain: zod_1.z.string().describe('Namespace grouping related entries (e.g. "screenshots", "config", "analysis").'),
    key: zod_1.z.string().describe('Unique identifier within the domain (e.g. "google", "user_profile").'),
    field: zod_1.z.string().optional().describe('RECOMMENDED. Names a specific field to set within the entry (e.g. "2026-05-09", "url", "score"). Using field is additive — it merges into existing data without clobbering other fields. Without field, the entire data object is merged at the top level, which can overwrite existing fields with the same keys. Always use field when adding to an entry that may already have data.'),
    data: zod_1.z.any().describe('The value to store. When field is provided, this can be any type (string, number, boolean, object, array) and is stored as { [field]: data }. When field is omitted, data must be a JSON object whose keys merge into existing data.'),
    tags: zod_1.z.array(zod_1.z.string()).optional().describe('Categorization tags (unioned on upsert)'),
});
const getSchema = zod_1.z.object({
    domain: zod_1.z.string().describe('Knowledge domain'),
    key: zod_1.z.string().describe('Document key'),
});
const searchSchema = zod_1.z.object({
    domain: zod_1.z.string().describe('Knowledge domain to search'),
    query: zod_1.z.record(zod_1.z.any()).describe('JSONB containment query (e.g. {"type":"screenshot"})'),
    tags: zod_1.z.array(zod_1.z.string()).optional().describe('Filter by tags (any match)'),
    limit: zod_1.z.number().optional().describe('Max results (default 50, max 200)'),
    offset: zod_1.z.number().optional().describe('Pagination offset'),
});
const listSchema = zod_1.z.object({
    domain: zod_1.z.string().describe('Knowledge domain'),
    search: zod_1.z.string().optional().describe('Search by key name or tag (partial match)'),
    tags: zod_1.z.array(zod_1.z.string()).optional().describe('Filter by tags (any match)'),
    limit: zod_1.z.number().optional().describe('Max results (default 50, max 200)'),
    offset: zod_1.z.number().optional().describe('Pagination offset'),
});
const deleteSchema = zod_1.z.object({
    domain: zod_1.z.string().describe('Knowledge domain'),
    key: zod_1.z.string().describe('Document key to delete'),
});
const setFieldSchema = zod_1.z.object({
    domain: zod_1.z.string().describe('Knowledge domain'),
    key: zod_1.z.string().describe('Document key'),
    path: zod_1.z.string().describe('Dot-notation path to the field (e.g. "google.holiday", "config.retries"). Sets the value at this exact path without overwriting sibling fields.'),
    value: zod_1.z.any().refine((v) => v !== undefined, 'value is required').describe('Value to set — any JSON type (string, number, boolean, object, array)'),
    tags: zod_1.z.array(zod_1.z.string()).optional().describe('Tags to union with existing tags'),
});
const removeFieldSchema = zod_1.z.object({
    domain: zod_1.z.string().describe('Knowledge domain'),
    key: zod_1.z.string().describe('Document key'),
    path: zod_1.z.string().describe('Dot-notation path to remove (e.g. "google.legacy_data", "temp.cache")'),
});
const appendSchema = zod_1.z.object({
    domain: zod_1.z.string().describe('Knowledge domain'),
    key: zod_1.z.string().describe('Document key'),
    path: zod_1.z.string().describe('JSONB path to array field (e.g. "screenshots" or "analysis.variations")'),
    value: zod_1.z.any().refine((v) => v !== undefined, 'value is required').describe('Value to append to the array'),
});
function registerTools(server) {
    server.registerTool('store_knowledge', {
        title: 'Store Knowledge',
        description: 'Store a value in the knowledge base using a 3-level hierarchy: domain > key > field. ' +
            'IMPORTANT: Always use the field parameter when adding data to an existing entry — ' +
            'it merges additively without clobbering other fields. Without field, top-level keys ' +
            'in data overwrite existing keys with the same name. ' +
            'Example: domain="screenshots", key="google", field="2026-05-10", data="description text".',
        inputSchema: storeSchema,
    }, async (args) => {
        // When field is provided, wrap the data value as { [field]: data }
        // so it merges into the JSONB column at the field level.
        // When field is omitted, data must be a plain object — reject strings,
        // arrays, and primitives that would corrupt the JSONB merge
        // (Postgres `object || string` produces an array, not a merge).
        let dataObj;
        if (args.field != null) {
            dataObj = { [args.field]: args.data };
        }
        else {
            if (args.data == null || typeof args.data !== 'object' || Array.isArray(args.data)) {
                return {
                    content: [{
                            type: 'text',
                            text: JSON.stringify({
                                error: 'When field is omitted, data must be a JSON object. Use the field parameter to store strings and other primitives.',
                            }),
                        }],
                };
            }
            dataObj = args.data;
        }
        const result = await knowledge.storeKnowledge({
            domain: args.domain, key: args.key, data: dataObj, tags: args.tags,
        });
        return { content: [{ type: 'text', text: JSON.stringify(result) }] };
    });
    server.registerTool('get_knowledge', {
        title: 'Get Knowledge',
        description: 'Retrieve a single knowledge entry by domain and key.',
        inputSchema: getSchema,
    }, async (args) => {
        const result = await knowledge.getKnowledge(args);
        return { content: [{ type: 'text', text: JSON.stringify(result) }] };
    });
    server.registerTool('search_knowledge', {
        title: 'Search Knowledge',
        description: 'Search knowledge entries using JSONB containment queries. The query object matches entries whose data contains the specified key-value pairs.',
        inputSchema: searchSchema,
    }, async (args) => {
        const result = await knowledge.searchKnowledge(args);
        return { content: [{ type: 'text', text: JSON.stringify(result) }] };
    });
    server.registerTool('list_knowledge', {
        title: 'List Knowledge',
        description: 'List knowledge entries in a domain, optionally filtered by tags. Returns most recently updated first.',
        inputSchema: listSchema,
    }, async (args) => {
        const result = await knowledge.listKnowledge(args);
        return { content: [{ type: 'text', text: JSON.stringify(result) }] };
    });
    server.registerTool('delete_knowledge', {
        title: 'Delete Knowledge',
        description: 'Delete a knowledge entry by domain and key.',
        inputSchema: deleteSchema,
    }, async (args) => {
        const result = await knowledge.deleteKnowledge(args);
        return { content: [{ type: 'text', text: JSON.stringify(result) }] };
    });
    server.registerTool('list_domains', {
        title: 'List Knowledge Domains',
        description: 'List all knowledge domains with entry counts and last-updated timestamps.',
        inputSchema: zod_1.z.object({}),
    }, async () => {
        const result = await knowledge.listDomains();
        return { content: [{ type: 'text', text: JSON.stringify(result) }] };
    });
    server.registerTool('append_knowledge', {
        title: 'Append to Knowledge',
        description: 'Append a value to an array field within a knowledge entry. Creates the entry and array if they do not exist.',
        inputSchema: appendSchema,
    }, async (args) => {
        const result = await knowledge.appendKnowledge(args);
        return { content: [{ type: 'text', text: JSON.stringify(result) }] };
    });
    server.registerTool('set_knowledge_field', {
        title: 'Set Knowledge Field',
        description: 'RECOMMENDED for updating existing entries. Set a value at a specific path within a knowledge entry ' +
            'without overwriting sibling fields. Use dot-notation for nested paths (e.g. "google.holiday" sets ' +
            'data.google.holiday while preserving data.google.description). Creates the entry if it does not exist. ' +
            'Use this instead of store_knowledge when the entry may already have data you want to preserve.',
        inputSchema: setFieldSchema,
    }, async (args) => {
        const result = await knowledge.setKnowledgeField(args);
        return { content: [{ type: 'text', text: JSON.stringify(result) }] };
    });
    server.registerTool('remove_knowledge_field', {
        title: 'Remove Knowledge Field',
        description: 'Remove a specific field from a knowledge entry by dot-path. The entry survives — only the targeted ' +
            'path is deleted. Use this for intentional cleanup of obsolete or incorrect fields.',
        inputSchema: removeFieldSchema,
    }, async (args) => {
        const result = await knowledge.removeKnowledgeField(args);
        return { content: [{ type: 'text', text: JSON.stringify(result) }] };
    });
}
async function createKnowledgeServer(options) {
    const name = options?.name || 'long-tail-knowledge';
    const instance = new mcp_js_1.McpServer({ name, version: '1.0.0' });
    registerTools(instance);
    logger_1.loggerRegistry.info(`[lt-mcp:knowledge] ${name} ready`);
    return instance;
}
