function isObject(v) {
    return !!v && typeof v === 'object' && !Array.isArray(v);
}
function clone(v) {
    return JSON.parse(JSON.stringify(v));
}
// Global (process-level) config – expected to be filled by host via setGlobalToolFilterConfig。
// 默认空配置：不改变行为，仅当 hints/配置显式要求时才执行过滤。
let globalConfig = {};
export function setGlobalToolFilterConfig(cfg) {
    if (!cfg || !isObject(cfg)) {
        globalConfig = {};
        return;
    }
    globalConfig = clone(cfg);
}
export function getGlobalToolFilterConfig() {
    return globalConfig;
}
// --- Vision 工具 Hook：仅在 hints/categoryOverrides 明确要求时启用 ---
function hasImageContent(messages) {
    try {
        for (const m of messages) {
            if (!m || typeof m !== 'object')
                continue;
            const c = m.content;
            if (Array.isArray(c)) {
                for (const part of c) {
                    if (part && typeof part === 'object') {
                        const t = String(part.type || '').toLowerCase();
                        if (t.includes('image'))
                            return true;
                    }
                }
            }
        }
    }
    catch {
        // non-fatal
    }
    return false;
}
function isVisionTool(tool) {
    try {
        const name = tool &&
            typeof tool === 'object' &&
            tool.function &&
            typeof tool.function.name === 'string'
            ? String(tool.function.name).toLowerCase()
            : '';
        if (!name)
            return false;
        // 保守实现：仅将 view_image 视为视觉工具；其他名称可通过上层配置扩展。
        if (name === 'view_image')
            return true;
        if (name.includes('vision'))
            return true;
    }
    catch {
        // ignore
    }
    return false;
}
const visionToolHook = ctx => {
    const { tools, messages, hints, config, recordDecision } = ctx;
    if (!tools.length)
        return;
    const hasVision = tools.some(t => isVisionTool(t));
    if (!hasVision)
        return;
    const override = hints.categoryOverrides?.vision;
    const cfgPolicy = config.categories?.vision?.mode;
    // 默认策略：require_content —— 有视觉工具但无图像内容时进行过滤提示
    const mode = override || cfgPolicy || 'require_content';
    if (mode === 'allow')
        return;
    const hasImage = hasImageContent(messages);
    const next = [];
    for (const t of tools) {
        if (isVisionTool(t)) {
            if (mode === 'block' || (mode === 'require_content' && !hasImage)) {
                const name = t &&
                    typeof t === 'object' &&
                    t.function &&
                    typeof t.function.name === 'string'
                    ? String(t.function.name)
                    : 'unknown';
                recordDecision({
                    name,
                    action: 'block',
                    category: 'vision',
                    reason: mode === 'block'
                        ? 'vision_tool_blocked_by_policy'
                        : 'vision_tool_without_image_link',
                });
                continue;
            }
        }
        next.push(t);
    }
    ctx.tools = next;
};
// --- MCP 工具 Hook：会话内阶段性暴露策略 ---
function isMcpToolName(name) {
    const lower = name.toLowerCase();
    return (lower === 'list_mcp_resources' ||
        lower === 'read_mcp_resource' ||
        lower === 'list_mcp_resource_templates');
}
function deriveMcpSessionState(messages) {
    let listRequested = false;
    let listEmpty = false;
    const extractToolContentText = (content) => {
        if (typeof content === 'string')
            return content;
        if (!Array.isArray(content))
            return '';
        const parts = [];
        for (const part of content) {
            if (!part || typeof part !== 'object')
                continue;
            const p = part;
            if (typeof p.text === 'string' && p.text.trim().length) {
                parts.push(p.text);
            }
        }
        return parts.join('\n');
    };
    const markListEmptyFromPayload = (payload) => {
        if (!payload || typeof payload !== 'object')
            return;
        const out = payload.output ?? payload;
        if (!out || typeof out !== 'object')
            return;
        if (Array.isArray(out.resources) && out.resources.length === 0) {
            listRequested = true;
            listEmpty = true;
        }
        if (Array.isArray(out.servers) && out.servers.length === 0) {
            listRequested = true;
            listEmpty = true;
        }
        const err = out.error ?? payload.error;
        if (err && typeof err === 'object') {
            const code = err.code;
            const msg = typeof err.message === 'string' ? String(err.message).toLowerCase() : '';
            if (code === -32601 || msg.includes('method not found')) {
                listRequested = true;
                listEmpty = true;
            }
        }
    };
    try {
        for (const m of messages) {
            if (!m || typeof m !== 'object')
                continue;
            const role = String(m.role || '').toLowerCase();
            // assistant.tool_calls: 视为对 list_mcp_resources 的“请求”
            if (role === 'assistant' && Array.isArray(m.tool_calls)) {
                for (const tc of m.tool_calls) {
                    try {
                        const fn = tc && tc.function;
                        const name = fn && typeof fn.name === 'string'
                            ? String(fn.name).toLowerCase()
                            : '';
                        if (name === 'list_mcp_resources') {
                            listRequested = true;
                        }
                    }
                    catch {
                        /* ignore single tool_call */
                    }
                }
            }
            // tool 角色消息：检查 rcc.tool.v1 包装的结果是否为空
            if (role === 'tool') {
                try {
                    const rawText = extractToolContentText(m.content);
                    if (!rawText || rawText.trim().length === 0) {
                        continue;
                    }
                    const lowered = rawText.toLowerCase();
                    if (lowered.includes('-32601') || (lowered.includes('method') && lowered.includes('not found'))) {
                        listRequested = true;
                        listEmpty = true;
                    }
                    const parsed = JSON.parse(rawText);
                    if (parsed &&
                        typeof parsed === 'object' &&
                        parsed.version === 'rcc.tool.v1' &&
                        parsed.tool &&
                        typeof parsed.tool.name === 'string') {
                        const nm = String(parsed.tool.name).toLowerCase();
                        if (nm === 'list_mcp_resources') {
                            listRequested = true;
                            const out = parsed.result?.output;
                            if (Array.isArray(out) && out.length === 0) {
                                listEmpty = true;
                            }
                            else if (out &&
                                typeof out === 'object' &&
                                Array.isArray(out.resources) &&
                                out.resources.length === 0) {
                                listEmpty = true;
                            }
                        }
                        continue;
                    }
                    // Non-wrapped tool output (common for Codex tool responses).
                    // If the payload looks like list_mcp_resources output and is empty/unsupported, disable MCP tools for this session.
                    markListEmptyFromPayload(parsed);
                }
                catch {
                    // ignore parse errors
                }
            }
        }
    }
    catch {
        // ignore errors
    }
    return { listRequested, listEmpty };
}
const mcpToolHook = ctx => {
    const { tools, messages, hints, recordDecision } = ctx;
    if (!tools.length)
        return;
    const mcpTools = tools.filter(t => {
        try {
            const n = t &&
                typeof t === 'object' &&
                t.function &&
                typeof t.function.name === 'string'
                ? String(t.function.name)
                : '';
            return isMcpToolName(n);
        }
        catch {
            return false;
        }
    });
    if (!mcpTools.length)
        return;
    const override = hints.categoryOverrides?.mcp;
    if (override === 'block') {
        // 请求级别完全关闭 MCP
        ctx.tools = tools.filter(t => !mcpTools.includes(t));
        for (const t of mcpTools) {
            const name = t &&
                typeof t === 'object' &&
                t.function &&
                typeof t.function.name === 'string'
                ? String(t.function.name)
                : 'unknown';
            recordDecision({
                name,
                action: 'block',
                category: 'mcp',
                reason: 'mcp_blocked_by_override',
            });
        }
        return;
    }
    // 会话内阶段性策略：
    // 1) 如果 list_mcp_resources 从未在本会话中被请求过 → 仅暴露 list_mcp_resources，过滤 read/templates。
    // 2) 如果 list_mcp_resources 已请求且结果为空 → 完全过滤所有 MCP 工具。
    const { listRequested, listEmpty } = deriveMcpSessionState(messages);
    const next = [];
    for (const t of tools) {
        let name = '';
        try {
            name =
                t &&
                    typeof t === 'object' &&
                    t.function &&
                    typeof t.function.name === 'string'
                    ? String(t.function.name)
                    : '';
        }
        catch {
            name = '';
        }
        const lower = name.toLowerCase();
        const isMcp = isMcpToolName(name);
        if (!isMcp) {
            next.push(t);
            continue;
        }
        if (!listRequested) {
            // list 未被请求过：保留 list_mcp_resources，其它 MCP 工具全部过滤
            if (lower === 'list_mcp_resources') {
                next.push(t);
                recordDecision({
                    name,
                    action: 'allow',
                    category: 'mcp',
                    reason: 'mcp_list_exposed_before_first_use',
                });
            }
            else {
                recordDecision({
                    name,
                    action: 'block',
                    category: 'mcp',
                    reason: 'mcp_non_list_blocked_until_list_called',
                });
            }
            continue;
        }
        if (listEmpty) {
            // list 已请求且结果为空：完全屏蔽所有 MCP 工具
            recordDecision({
                name,
                action: 'block',
                category: 'mcp',
                reason: 'mcp_disabled_for_session_after_empty_list',
            });
            continue;
        }
        // list 已请求且非空：保留现有行为（交由其它层/配置控制）
        next.push(t);
    }
    ctx.tools = next;
};
const requestHooks = [visionToolHook, mcpToolHook];
export class ToolFilterHookFilter {
    name = 'tool_filter_hook';
    stage = 'request_finalize';
    apply(input, ctx) {
        try {
            const out = clone(input || {});
            const tools = Array.isArray(out.tools)
                ? out.tools
                : [];
            if (!tools.length) {
                return { ok: true, data: out };
            }
            const messages = Array.isArray(out.messages)
                ? out.messages
                : [];
            const hints = (ctx.toolFilterHints && clone(ctx.toolFilterHints)) || {
                requestedDecisions: [],
            };
            const config = getGlobalToolFilterConfig();
            const decisions = [];
            const hookCtx = {
                tools,
                messages,
                hints,
                config,
                stage: 'request',
                recordDecision: d => {
                    decisions.push(d);
                },
            };
            for (const hook of requestHooks) {
                try {
                    const res = hook(hookCtx);
                    if (res && typeof res.then === 'function') {
                        // eslint-disable-next-line @typescript-eslint/no-floating-promises
                        res.catch(() => { });
                    }
                }
                catch {
                    // ignore single hook failure
                }
            }
            out.tools = hookCtx.tools;
            if (Array.isArray(out.tools) && out.tools.length === 0) {
                try {
                    if ('tool_choice' in out)
                        delete out.tool_choice;
                }
                catch {
                    /* ignore */
                }
            }
            // 记录决策（供调试/快照使用）
            if (!Array.isArray(hints.decided))
                hints.decided = [];
            hints.decided = hints.decided.concat(decisions);
            return {
                ok: true,
                data: out,
                metrics: decisions.length
                    ? { toolFilterDecisions: clone(decisions) }
                    : undefined,
            };
        }
        catch {
            return { ok: true, data: input };
        }
    }
}
