{"version":3,"file":"tool-compiler.mjs","names":[],"sources":["../../../src/services/tool-compiler.ts"],"sourcesContent":["/**\n * Tool Compiler — converts UserTool definitions into AnyAgentTool-compatible objects.\n *\n * This is the bridge between the user-tool-service (CRUD / persistence) and\n * the OpenClaw plugin registration system. It:\n *\n * 1. Generates TypeBox schemas from UserToolParam[] definitions\n * 2. Creates execute functions that route to the sandbox runtime\n * 3. Returns objects matching the AnyAgentTool shape expected by api.registerTool()\n *\n * User tools are compiled at plugin init time (for all enabled tools) and\n * on-demand when new tools are created at runtime.\n */\n\nimport { Type, type TSchema } from '@sinclair/typebox';\nimport { jsonResult, errorResult } from '../lib/tool-helpers.js';\nimport type { UserTool, UserToolParam, ApiConnectorDef, ComposedToolDef } from './user-tool-service.js';\nimport { getUserToolService } from './user-tool-service.js';\nimport {\n  createSandboxContext,\n  executeApiConnector,\n  executeComposedTool,\n  type ToolDispatcher,\n} from './sandbox-runtime.js';\n\n// ─── Types ──────────────────────────────────────────────────────────────\n\n/** Shape matching AnyAgentTool from OpenClaw plugin SDK. */\nexport interface CompiledTool {\n  name: string;\n  label: string;\n  ownerOnly: boolean;\n  description: string;\n  parameters: TSchema;\n  execute: (toolCallId: string, args: unknown, ctx?: any) => Promise<any>;\n}\n\n// ─── Schema Generation ──────────────────────────────────────────────────\n\n/**\n * Build a TypeBox schema from UserToolParam[] definitions.\n *\n * Each param becomes a property in a Type.Object schema. Required params\n * are listed in the object's `required` array (handled by TypeBox's\n * Type.Optional wrapper for non-required params).\n */\nexport function buildSchemaFromParams(params: UserToolParam[]): TSchema {\n  if (params.length === 0) {\n    return Type.Object({});\n  }\n\n  const properties: Record<string, TSchema> = {};\n\n  for (const param of params) {\n    let schema: TSchema;\n\n    switch (param.type) {\n      case 'string':\n        schema = Type.String({ description: param.description });\n        break;\n      case 'number':\n        schema = Type.Number({ description: param.description });\n        break;\n      case 'boolean':\n        schema = Type.Boolean({ description: param.description });\n        break;\n      default:\n        // Fallback to string for unknown types\n        schema = Type.String({ description: param.description });\n    }\n\n    // Add default value if specified\n    if (param.default !== undefined) {\n      schema = Type.Optional({ ...schema, default: param.default } as any);\n    }\n\n    // Wrap optional params\n    if (!param.required) {\n      schema = Type.Optional(schema);\n    }\n\n    properties[param.name] = schema;\n  }\n\n  return Type.Object(properties);\n}\n\n// ─── Tool Compilation ───────────────────────────────────────────────────\n\n/**\n * Compile a UserTool into an AnyAgentTool-compatible object.\n *\n * The returned object has the same shape as built-in tools (name, label,\n * ownerOnly, description, parameters, execute) and can be passed directly\n * to api.registerTool().\n *\n * @param tool - The user tool definition from UserToolService\n * @param dispatcher - Interface for calling built-in tools (for composed/custom types)\n * @returns CompiledTool ready for registration\n */\nexport function compileTool(tool: UserTool, dispatcher: ToolDispatcher): CompiledTool {\n  const schema = buildSchemaFromParams(tool.params);\n\n  return {\n    name: tool.name,\n    label: tool.label,\n    ownerOnly: tool.isWrite,\n    description: `[User Tool] ${tool.description}`,\n    parameters: schema,\n\n    execute: async (_toolCallId: string, args: unknown, ctx?: any) => {\n      // Ensure the tool is still enabled\n      const current = getUserToolService().get(tool.id);\n      if (!current || !current.enabled) {\n        return errorResult(`User tool \"${tool.name}\" is disabled or has been deleted.`);\n      }\n\n      // Create sandbox context\n      const userId = ctx?.senderId ?? ctx?.from ?? ctx?.metadata?.senderId ?? 'unknown';\n      const sandbox = createSandboxContext(current, userId);\n\n      try {\n        const params = (args ?? {}) as Record<string, unknown>;\n        let result: any;\n\n        switch (current.definition.type) {\n          case 'api_connector':\n            result = await executeApiConnector(\n              current.definition as ApiConnectorDef,\n              params,\n              sandbox,\n            );\n            break;\n\n          case 'composed':\n            result = await executeComposedTool(\n              (current.definition as ComposedToolDef).steps,\n              params,\n              sandbox,\n              dispatcher,\n            );\n            break;\n\n          case 'custom':\n            // Custom tools are LLM-interpreted: return the behavior description\n            // along with context so the LLM can decide how to fulfill the request.\n            // The LLM will use the allowedTools to call sub-tools as needed.\n            result = jsonResult({\n              type: 'custom_tool_invocation',\n              behavior: current.definition.behavior,\n              allowedTools: (current.definition as any).allowedTools,\n              maxCalls: (current.definition as any).maxCalls ?? 5,\n              inputArgs: params,\n              instructions: `This is a user-defined custom tool. Follow the behavior description above ` +\n                `to fulfill the request. You may call the listed allowedTools (up to ${(current.definition as any).maxCalls ?? 5} ` +\n                `times) to achieve the goal. Return the final result.`,\n            });\n            break;\n\n          default:\n            result = errorResult(`Unknown tool type: ${(current.definition as any).type}`);\n        }\n\n        // Record usage\n        getUserToolService().recordUsage(current.id);\n\n        // Attach audit log to result details\n        if (result?.details && typeof result.details === 'object') {\n          (result.details as any)._audit = {\n            callCount: sandbox.callCount,\n            budgetUsedUsd: current.maxBudgetUsd - sandbox.budgetRemainingUsd,\n            entries: sandbox.auditLog,\n          };\n        }\n\n        return result;\n      } catch (err) {\n        return errorResult(\n          `User tool \"${tool.name}\" failed: ${err instanceof Error ? err.message : String(err)}`\n        );\n      }\n    },\n  };\n}\n\n// ─── Batch Compilation ──────────────────────────────────────────────────\n\n/**\n * Compile all enabled user tools into registerable tool objects.\n *\n * Called at plugin init time to load persisted user tools.\n */\nexport function compileAllEnabledTools(dispatcher: ToolDispatcher): CompiledTool[] {\n  const service = getUserToolService();\n  const enabled = service.getEnabledTools();\n  return enabled.map(tool => compileTool(tool, dispatcher));\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AA8CA,SAAgB,sBAAsB,QAAkC;AACtE,KAAI,OAAO,WAAW,EACpB,QAAO,KAAK,OAAO,EAAE,CAAC;CAGxB,MAAM,aAAsC,EAAE;AAE9C,MAAK,MAAM,SAAS,QAAQ;EAC1B,IAAI;AAEJ,UAAQ,MAAM,MAAd;GACE,KAAK;AACH,aAAS,KAAK,OAAO,EAAE,aAAa,MAAM,aAAa,CAAC;AACxD;GACF,KAAK;AACH,aAAS,KAAK,OAAO,EAAE,aAAa,MAAM,aAAa,CAAC;AACxD;GACF,KAAK;AACH,aAAS,KAAK,QAAQ,EAAE,aAAa,MAAM,aAAa,CAAC;AACzD;GACF,QAEE,UAAS,KAAK,OAAO,EAAE,aAAa,MAAM,aAAa,CAAC;;AAI5D,MAAI,MAAM,YAAY,KAAA,EACpB,UAAS,KAAK,SAAS;GAAE,GAAG;GAAQ,SAAS,MAAM;GAAS,CAAQ;AAItE,MAAI,CAAC,MAAM,SACT,UAAS,KAAK,SAAS,OAAO;AAGhC,aAAW,MAAM,QAAQ;;AAG3B,QAAO,KAAK,OAAO,WAAW;;;;;;;;;;;;;AAgBhC,SAAgB,YAAY,MAAgB,YAA0C;CACpF,MAAM,SAAS,sBAAsB,KAAK,OAAO;AAEjD,QAAO;EACL,MAAM,KAAK;EACX,OAAO,KAAK;EACZ,WAAW,KAAK;EAChB,aAAa,eAAe,KAAK;EACjC,YAAY;EAEZ,SAAS,OAAO,aAAqB,MAAe,QAAc;GAEhE,MAAM,UAAU,oBAAoB,CAAC,IAAI,KAAK,GAAG;AACjD,OAAI,CAAC,WAAW,CAAC,QAAQ,QACvB,QAAO,YAAY,cAAc,KAAK,KAAK,oCAAoC;GAKjF,MAAM,UAAU,qBAAqB,SADtB,KAAK,YAAY,KAAK,QAAQ,KAAK,UAAU,YAAY,UACnB;AAErD,OAAI;IACF,MAAM,SAAU,QAAQ,EAAE;IAC1B,IAAI;AAEJ,YAAQ,QAAQ,WAAW,MAA3B;KACE,KAAK;AACH,eAAS,MAAM,oBACb,QAAQ,YACR,QACA,QACD;AACD;KAEF,KAAK;AACH,eAAS,MAAM,oBACZ,QAAQ,WAA+B,OACxC,QACA,SACA,WACD;AACD;KAEF,KAAK;AAIH,eAAS,WAAW;OAClB,MAAM;OACN,UAAU,QAAQ,WAAW;OAC7B,cAAe,QAAQ,WAAmB;OAC1C,UAAW,QAAQ,WAAmB,YAAY;OAClD,WAAW;OACX,cAAc,iJAC4D,QAAQ,WAAmB,YAAY,EAAE;OAEpH,CAAC;AACF;KAEF,QACE,UAAS,YAAY,sBAAuB,QAAQ,WAAmB,OAAO;;AAIlF,wBAAoB,CAAC,YAAY,QAAQ,GAAG;AAG5C,QAAI,QAAQ,WAAW,OAAO,OAAO,YAAY,SAC9C,QAAO,QAAgB,SAAS;KAC/B,WAAW,QAAQ;KACnB,eAAe,QAAQ,eAAe,QAAQ;KAC9C,SAAS,QAAQ;KAClB;AAGH,WAAO;YACA,KAAK;AACZ,WAAO,YACL,cAAc,KAAK,KAAK,YAAY,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,GACrF;;;EAGN;;;;;;;AAUH,SAAgB,uBAAuB,YAA4C;AAGjF,QAFgB,oBAAoB,CACZ,iBAAiB,CAC1B,KAAI,SAAQ,YAAY,MAAM,WAAW,CAAC"}