{"version":3,"file":"executable-as-tool.mjs","names":[],"sources":["../../../../../../../ai/src/tool/executable-as-tool.ts"],"sourcesContent":["import type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport type { BaseReport } from \"../contracts/result/base-report.type\";\nimport type { BaseResult } from \"../contracts/result/base-result.type\";\nimport type { Usage } from \"../contracts/result/usage.type\";\nimport { AgentExecutionError, type AIError } from \"../errors\";\nimport { compositeAsTool, type ToolContract } from \"./tool\";\n\n/**\n * Envelope every executable's `execute()` resolves to. Agent, workflow,\n * and supervisor results all satisfy this shape — `data` / `error`\n * carry the outcome while `usage` and `report` are always present —\n * which is exactly what {@link compositeAsTool} needs to nest the inner\n * run under the outer tool-call node.\n */\ntype ExecutableEnvelope<TOutput> = BaseResult & {\n  data?: TOutput;\n  error?: AIError;\n  report: BaseReport;\n};\n\n/**\n * Structural view of an executable primitive (agent / workflow /\n * supervisor) when it is dropped straight into an agent's `tools: []`\n * array WITHOUT being wrapped via `.asTool()` first.\n *\n * Only the fields the auto-adapt path reads are declared:\n * - `name` — becomes the LLM tool name (required; anonymous executables\n *   are rejected at author time, mirroring `.asTool()`).\n * - `description` — the \"when would the model pick this?\" line.\n * - `inputSchema` — opt-in Standard Schema typing the tool's arguments.\n *   Surfaced on `WorkflowInstance` / `SupervisorContract` from the new\n *   optional `inputSchema` config field. Absent for agents (which take\n *   a plain string prompt).\n * - `execute` — the dispatch entry every `ExecutableContract` exposes.\n *\n * `invoke` is declared `never` so a `ToolContract` (which HAS `invoke`)\n * can never be mistaken for an executable by the {@link isExecutableTool}\n * guard.\n */\nexport type ExecutableTool<TInput = unknown, TOutput = unknown> = {\n  readonly name: string;\n  readonly description?: string;\n  readonly inputSchema?: StandardSchemaV1<TInput>;\n  execute(input: TInput, options?: unknown): Promise<ExecutableEnvelope<TOutput>>;\n  invoke?: never;\n};\n\n/**\n * Entry accepted in an agent's `tools: []` array — either an already-\n * built `ToolContract` (the `.asTool()` / `ai.tool()` path) or a raw\n * executable primitive the framework auto-adapts on the caller's\n * behalf.\n */\nexport type AgentToolEntry<TInput = unknown, TOutput = unknown> =\n  | ToolContract<TInput, TOutput>\n  | ExecutableTool<TInput, TOutput>;\n\n/**\n * Identity passthrough schema used when an executable is registered as\n * a tool without declaring an `inputSchema`. The model's raw arguments\n * flow straight to `execute()` unchanged — the executable validates\n * internally (workflows via their steps, supervisors/agents via their\n * own input handling).\n */\nfunction passthroughSchema<TInput>(): StandardSchemaV1<TInput> {\n  return {\n    \"~standard\": {\n      version: 1,\n      vendor: \"warlock-ai\",\n      validate: (value: unknown) => ({ value: value as TInput }),\n    },\n  };\n}\n\n/**\n * Type guard distinguishing a raw executable primitive from a built\n * `ToolContract`. An executable exposes `execute()` and no `invoke()`;\n * a `ToolContract` exposes `invoke()`. The `invoke` check is the\n * load-bearing discriminator — `.asTool()`-wrapped composites keep\n * their own `execute` too, so checking `execute` alone is insufficient.\n */\nexport function isExecutableTool(entry: unknown): entry is ExecutableTool {\n  if (!entry || typeof entry !== \"object\") {\n    return false;\n  }\n\n  const candidate = entry as { execute?: unknown; invoke?: unknown };\n\n  return typeof candidate.execute === \"function\" && typeof candidate.invoke !== \"function\";\n}\n\n/**\n * Adapt a raw executable primitive (agent / workflow / supervisor) into\n * a `ToolContract` so an agent can dispatch it inside its tool-call\n * loop WITHOUT the caller writing `.asTool()`. Derives the LLM tool\n * manifest from the executable's own `name` + `description` +\n * (optional) `inputSchema`, then dispatches through the executable's\n * `execute()` — the inner report nests under the outer tool-call node\n * exactly like an explicit `.asTool()` wrapper.\n *\n * Throws `AgentExecutionError` at author time when the executable lacks\n * a usable `name` — the agent's tool surface needs a stable id, the\n * same constraint `.asTool()` enforces.\n */\nexport function executableToTool<TInput, TOutput>(\n  executable: ExecutableTool<TInput, TOutput>,\n): ToolContract<TInput, TOutput> {\n  if (!executable.name || typeof executable.name !== \"string\") {\n    throw new AgentExecutionError(\n      \"tools[]: an executable (agent/workflow/supervisor) used as a tool must have a `name`\",\n      { context: { authoring: true } },\n    );\n  }\n\n  return compositeAsTool<TInput, TOutput>({\n    name: executable.name,\n    description: executable.description ?? `Invoke \"${executable.name}\" as a tool.`,\n    input: executable.inputSchema ?? passthroughSchema<TInput>(),\n    execute: async (input, ctx) => {\n      // Relay the outer run's cancellation signal so a cancelled parent\n      // aborts this nested agent/workflow/supervisor (C2). Omit the\n      // options object entirely when there's no signal so primitives that\n      // treat any second arg as meaningful stay byte-identical.\n      const result = await executable.execute(\n        input,\n        ctx?.signal ? { signal: ctx.signal } : undefined,\n      );\n\n      if (result.error) {\n        // Surface the inner typed error so the surrounding\n        // `compositeAsTool` wrapper produces a `ToolExecutionError`\n        // with `cause` pointing back at the original subclass — the\n        // agent's tool-call loop sees one uniform error class\n        // regardless of which primitive failed.\n        throw result.error;\n      }\n\n      return {\n        data: result.data as TOutput,\n        usage: result.usage as Usage,\n        report: result.report,\n      };\n    },\n  });\n}\n\n/**\n * Normalize an agent's `tools: []` array into a uniform\n * `ToolContract[]` for the runtime. Already-built `ToolContract`s\n * (`.asTool()` / `ai.tool()`) pass through untouched; raw executable\n * primitives are auto-adapted via {@link executableToTool}.\n *\n * Returns `undefined` when no tools were supplied so the agent's\n * existing `config.tools ?? []` fallbacks stay byte-identical.\n */\nexport function normalizeAgentTools(\n  tools: ReadonlyArray<AgentToolEntry> | undefined,\n): ToolContract<unknown, unknown>[] | undefined {\n  if (!tools) {\n    return undefined;\n  }\n\n  return tools.map((entry) => {\n    if (isExecutableTool(entry)) {\n      return executableToTool(entry) as ToolContract<unknown, unknown>;\n    }\n\n    return entry as ToolContract<unknown, unknown>;\n  });\n}\n"],"mappings":";;;;;;;;;;;;AAgEA,SAAS,oBAAsD;CAC7D,OAAO,EACL,aAAa;EACX,SAAS;EACT,QAAQ;EACR,WAAW,WAAoB,EAAS,MAAgB;CAC1D,EACF;AACF;;;;;;;;AASA,SAAgB,iBAAiB,OAAyC;CACxE,IAAI,CAAC,SAAS,OAAO,UAAU,UAC7B,OAAO;CAGT,MAAM,YAAY;CAElB,OAAO,OAAO,UAAU,YAAY,cAAc,OAAO,UAAU,WAAW;AAChF;;;;;;;;;;;;;;AAeA,SAAgB,iBACd,YAC+B;CAC/B,IAAI,CAAC,WAAW,QAAQ,OAAO,WAAW,SAAS,UACjD,MAAM,IAAI,oBACR,wFACA,EAAE,SAAS,EAAE,WAAW,KAAK,EAAE,CACjC;CAGF,OAAO,gBAAiC;EACtC,MAAM,WAAW;EACjB,aAAa,WAAW,eAAe,WAAW,WAAW,KAAK;EAClE,OAAO,WAAW,eAAe,kBAA0B;EAC3D,SAAS,OAAO,OAAO,QAAQ;GAK7B,MAAM,SAAS,MAAM,WAAW,QAC9B,OACA,KAAK,SAAS,EAAE,QAAQ,IAAI,OAAO,IAAI,MACzC;GAEA,IAAI,OAAO,OAMT,MAAM,OAAO;GAGf,OAAO;IACL,MAAM,OAAO;IACb,OAAO,OAAO;IACd,QAAQ,OAAO;GACjB;EACF;CACF,CAAC;AACH;;;;;;;;;;AAWA,SAAgB,oBACd,OAC8C;CAC9C,IAAI,CAAC,OACH;CAGF,OAAO,MAAM,KAAK,UAAU;EAC1B,IAAI,iBAAiB,KAAK,GACxB,OAAO,iBAAiB,KAAK;EAG/B,OAAO;CACT,CAAC;AACH"}