/** * ToolGatewayPolicy — blocks unregistered write tools and document reader misuse. * * Checkpoints: pre-tool * * Logic migrated from: * - src/harness/tool-gateway.ts: unregisteredWriteToolBlockReason, documentReadBlockReason */ import type { GateContext, GateFinding } from "../findings.ts"; export const TOOL_GATEWAY_POLICY_NAME = "tool-gateway-policy"; const WRITE_LIKE_TOOLS = new Set(["write", "edit"]); const POTENTIAL_WRITE_TOOL_PATTERN = /(^|[_-])(write|edit|patch|apply|replace|delete|remove|move|copy|rename|create|save)([_-]|$)|notebook[_-]?edit/i; const DOCUMENT_EXTENSIONS = new Set(["pdf", "docx", "doc", "xlsx", "xls", "csv"]); export function evaluateToolGatewayPolicy(ctx: GateContext): GateFinding[] { const findings: GateFinding[] = []; const { toolName, path } = ctx; if (!toolName) return findings; // Check for unregistered write tools const unregisteredReason = unregisteredWriteToolBlockReason(toolName, path); if (unregisteredReason) { findings.push({ id: "TGP-001", severity: "soft-deny", policy: TOOL_GATEWAY_POLICY_NAME, message: unregisteredReason, nextAction: "新增写类工具必须先接入 isWriteLikeToolCall、Write Transaction、Source Anchor 和后置条件校验;未接入前禁止执行该工具。", }); } // Check for document reader redirect const documentReason = documentReadBlockReason(toolName, path); if (documentReason) { findings.push({ id: "TGP-002", severity: "warning", policy: TOOL_GATEWAY_POLICY_NAME, message: documentReason, nextAction: `改用 kd_doc_read 工具读取此文件,参数 path="${path}"。`, }); } return findings; } // ── Utility functions (reimplemented from src/harness/tool-gateway.ts) ─────── function unregisteredWriteToolBlockReason(toolName: string, path: string | undefined): string | undefined { if (!path || isWriteLikeTool(toolName)) return undefined; if (!POTENTIAL_WRITE_TOOL_PATTERN.test(toolName)) return undefined; return `拒绝未登记写类工具 ${toolName} 写入 ${path}。`; } function documentReadBlockReason(toolName: string, path: string | undefined): string | undefined { if (toolName !== "read" || !path) return undefined; const ext = path.toLowerCase().split(".").pop(); if (!ext || !DOCUMENT_EXTENSIONS.has(ext)) return undefined; return `read 工具无法解析 .${ext} 二进制格式。`; } function isWriteLikeTool(toolName: string): boolean { return WRITE_LIKE_TOOLS.has(toolName); }