UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

125 lines (124 loc) • 5.88 kB
import { t as asNonArrayRecord } from "./record-coerce-TIsquoVR.mjs"; //#region src/agents/exec-tool-target-pinning.ts const scheduledToolProjections = /* @__PURE__ */ new WeakMap(); const EXEC_POLICY_PARAMETER_NAMES = /* @__PURE__ */ new Set([ "host", "security", "ask" ]); const NODE_EXEC_PARAMETER_NAMES = /* @__PURE__ */ new Set([ "command", "workdir", "env", "timeoutSeconds", "node" ]); const PROCESS_FOLLOWUP_TEXT = "Use process (list/poll/log/write/send-keys/submit/paste/kill/clear/remove) for follow-up."; /** Constructs and registers an alias from an exact host-owned shell source. */ function createCronScheduledToolProjection(sourceTool, assertActive, targetTool, projection) { assertActive(); if (projection.kind !== targetTool) throw new Error("scheduled tool projection does not match its host-created source"); const projectedTool = projection.kind === "exec" ? createScheduledExecProjection(sourceTool, projection) : { ...sourceTool, name: projection.name, description: projection.description }; const info = targetTool === "exec" ? Object.freeze({ targetTool, execTarget: Object.freeze({ host: "gateway", ...projection.kind === "exec" && projection.ask ? { ask: projection.ask } : {} }) }) : Object.freeze({ targetTool }); scheduledToolProjections.set(projectedTool, Object.freeze({ assertActive, sourceToolName: projectedTool.name, info, execute: projectedTool.execute })); return projectedTool; } function createScheduledExecProjection(sourceTool, projection) { const pinnedTool = pinExecToolTarget(sourceTool, { host: "gateway", ...projection.ask ? { ask: projection.ask } : {} }); return { ...pinnedTool, name: projection.name, description: projection.description, execute: async (toolCallId, args, signal, onUpdate) => { const result = await pinnedTool.execute(toolCallId, args, signal, onUpdate); return { ...result, content: result.content.map((item) => item.type === "text" ? Object.assign({}, item, { text: item.text.replace(PROCESS_FOLLOWUP_TEXT, projection.followupText) }) : item) }; } }; } /** * Resolves the canonical identity of a host-created alias on the final * executable surface. Returns undefined for tools without a registered * projection; throws when a registered projection's executable or name was * changed after host creation, so tampered aliases never canonicalize. */ function readCronScheduledToolProjection(tool) { const projection = scheduledToolProjections.get(tool); if (!projection) return; projection.assertActive(); if (tool.name !== projection.sourceToolName || tool.execute !== projection.execute) throw new Error("scheduled tool projection executable changed after host creation"); return projection.info; } /** Preserves projection identity across shallow tool-object copies made by tool plumbing. */ function copyCronScheduledToolProjection(source, target) { const projection = scheduledToolProjections.get(source); if (projection && source.name === projection.sourceToolName && target.name === projection.sourceToolName && source.execute === projection.execute && target.execute === projection.execute) scheduledToolProjections.set(target, projection); } /** Restricts an exec tool to one host target even when callers submit broader arguments. */ function pinExecToolTarget(tool, target) { const pinnedNode = target.host === "node" ? target.node?.trim() : void 0; const pinArgs = (args) => pinExecToolArgs(args, target, pinnedNode); const prepare = tool.prepareBeforeToolCallParams; const finalize = tool.finalizeBeforeToolCallParams; const getExecutionTimeoutMs = tool.getExecutionTimeoutMs; return { ...tool, parameters: restrictExecToolParameters(tool.parameters, target.host, Boolean(pinnedNode)), ...getExecutionTimeoutMs ? { getExecutionTimeoutMs: (args) => getExecutionTimeoutMs(pinArgs(args)) } : {}, ...prepare ? { prepareBeforeToolCallParams: (args, context) => prepare(pinArgs(args), context) } : {}, ...finalize ? { finalizeBeforeToolCallParams: (params, preparedParams) => finalize(pinArgs(params), preparedParams) } : {}, execute: (toolCallId, args, signal, onUpdate) => tool.execute(toolCallId, pinArgs(args), signal, onUpdate) }; } function pinExecToolArgs(args, target, pinnedNode) { const { host: _host, security: _security, ask: _ask, node: requestedNode, ...rest } = asNonArrayRecord(args); if (target.host === "gateway") return { ...rest, host: "gateway", ...target.ask ? { ask: target.ask } : {} }; const nodeArgs = Object.fromEntries(Object.entries(rest).filter(([name]) => NODE_EXEC_PARAMETER_NAMES.has(name))); const node = pinnedNode ?? (typeof requestedNode === "string" ? requestedNode.trim() : ""); return { ...nodeArgs, host: "node", ...node ? { node } : {} }; } function restrictExecToolParameters(parameters, host, hasPinnedNode) { if (!parameters || typeof parameters !== "object" || Array.isArray(parameters)) return parameters; const schema = parameters; const rawProperties = schema.properties; if (!rawProperties || typeof rawProperties !== "object" || Array.isArray(rawProperties)) return parameters; const includeParameter = (name) => host === "node" ? NODE_EXEC_PARAMETER_NAMES.has(name) && !(hasPinnedNode && name === "node") : !EXEC_POLICY_PARAMETER_NAMES.has(name) && name !== "node"; const properties = Object.fromEntries(Object.entries(rawProperties).filter(([name]) => includeParameter(name))); const rawRequired = schema.required; const required = Array.isArray(rawRequired) ? rawRequired.filter((name) => typeof name !== "string" || includeParameter(name)) : rawRequired; return { ...parameters, properties, ...Array.isArray(rawRequired) ? { required } : {} }; } //#endregion export { readCronScheduledToolProjection as i, createCronScheduledToolProjection as n, pinExecToolTarget as r, copyCronScheduledToolProjection as t };