{"version":3,"sources":["../../src/errors.ts","../../src/integrations/_shared.ts","../../src/integrations/anthropic.ts"],"names":["MAX_RESULT_CHARS"],"mappings":"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","file":"anthropic.cjs","sourcesContent":["import type { CardVerifyResult, ToolCallResult } from \"./types\";\n\n/** Base class for all errors thrown by the SDK. */\nexport class AxioRankError extends Error {\n  constructor(message: string) {\n    super(message);\n    this.name = \"AxioRankError\";\n  }\n}\n\n/** Thrown when the API key is missing, invalid, or revoked (HTTP 401). */\nexport class AxioRankAuthError extends AxioRankError {\n  constructor(message = \"Invalid or missing AxioRank API key\") {\n    super(message);\n    this.name = \"AxioRankAuthError\";\n  }\n}\n\n/** Thrown for malformed requests, timeouts, and other non-2xx responses. */\nexport class AxioRankRequestError extends AxioRankError {\n  status?: number;\n  constructor(message: string, status?: number) {\n    super(message);\n    this.name = \"AxioRankRequestError\";\n    this.status = status;\n  }\n}\n\n/** Thrown by {@link AxioRank.enforce} when the gateway denies the call. */\nexport class AxioRankDeniedError extends AxioRankError {\n  result: ToolCallResult;\n  constructor(result: ToolCallResult) {\n    super(`AxioRank denied tool call: ${result.reason}`);\n    this.name = \"AxioRankDeniedError\";\n    this.result = result;\n  }\n}\n\n/** Thrown by the chat guards when a prompt or completion is blocked locally. */\nexport class AxioRankGuardError extends AxioRankError {\n  /** Which side of the model call was blocked. */\n  phase: \"prompt\" | \"completion\";\n  risk: number;\n  reason: string;\n  constructor(phase: \"prompt\" | \"completion\", reason: string, risk: number) {\n    super(`AxioRank blocked the ${phase}: ${reason} (risk ${risk}).`);\n    this.name = \"AxioRankGuardError\";\n    this.phase = phase;\n    this.reason = reason;\n    this.risk = risk;\n  }\n}\n\n/** Thrown by {@link AxioRank.enforceCard} when the preflight verdict is `deny`. */\nexport class AxioRankCardDeniedError extends AxioRankError {\n  result: CardVerifyResult;\n  constructor(result: CardVerifyResult) {\n    super(`AxioRank denied card preflight: ${result.reason}`);\n    this.name = \"AxioRankCardDeniedError\";\n    this.result = result;\n  }\n}\n\n/** Thrown when the workspace is rate-limited (HTTP 429) after retries are exhausted. */\nexport class AxioRankRateLimitError extends AxioRankError {\n  retryAfterSec?: number;\n  constructor(message = \"AxioRank rate limit exceeded\", retryAfterSec?: number) {\n    super(message);\n    this.name = \"AxioRankRateLimitError\";\n    this.retryAfterSec = retryAfterSec;\n  }\n}\n\n/** Thrown by {@link constructEvent} when a webhook signature is missing, stale, or invalid. */\nexport class AxioRankWebhookSignatureError extends AxioRankError {\n  constructor(message = \"AxioRank webhook signature verification failed\") {\n    super(message);\n    this.name = \"AxioRankWebhookSignatureError\";\n  }\n}\n","/**\n * Shared core for the framework adapters. Every adapter funnels its tool calls\n * through {@link runGuarded} so the allow / deny / hold semantics stay identical\n * across frameworks - the only per-framework work is pulling out the tool name\n * and arguments and handing back the right shape.\n */\nimport { AxioRankDeniedError } from \"../errors\";\nimport type { ToolCallParams, ToolCallResult, ToolResultParams } from \"../types\";\n\n/**\n * Anything that can score a tool call: an {@link AxioRank} client or a\n * trace-bound handle from {@link AxioRank.trace}. Accepting either lets an\n * adapter share one trace id across a whole agent run - so the gateway can\n * correlate multi-step kill chains (read a secret -> exfiltrate it) - just by\n * passing `axio.trace()` instead of the bare client.\n */\nexport interface ToolCaller {\n  toolCall(params: ToolCallParams): Promise<ToolCallResult>;\n  /** Optional: report a tool result for IFC taint tracking (the client + trace handle have it). */\n  reportResult?(params: ToolResultParams): Promise<void>;\n  /** Optional: inspect a tool result and resolve with the decision (enforce-capable). */\n  inspectResult?(params: ToolResultParams): Promise<ToolCallResult>;\n}\n\n/** Tools whose OUTPUT is untrusted external data (mirrors the server classifier). */\nfunction isUntrustedSource(tool: string): boolean {\n  const t = tool.toLowerCase();\n  return (\n    /fetch|http|browse|scrape|crawl|email|gmail|imap|inbox|message/.test(t) ||\n    t.endsWith(\".read\") ||\n    t.includes(\"readfile\") ||\n    t.includes(\"file.get\") ||\n    t.startsWith(\"fs.\") ||\n    t.endsWith(\".query\") ||\n    t.endsWith(\".select\") ||\n    t.includes(\"db.read\") ||\n    t.includes(\"sql\")\n  );\n}\n\nconst MAX_RESULT_CHARS = 100_000;\n\nfunction stringifyResult(output: unknown): string {\n  let s: string;\n  if (typeof output === \"string\") s = output;\n  else {\n    try {\n      s = JSON.stringify(output) ?? String(output);\n    } catch {\n      s = String(output);\n    }\n  }\n  return s.length > MAX_RESULT_CHARS ? s.slice(0, MAX_RESULT_CHARS) : s;\n}\n\n/**\n * Fire-and-forget: after an allowed untrusted-source tool runs, report its output\n * so the gateway can taint-track it. Only when the workspace enforces IFC (the\n * `ifc` hint on the verdict) - never a wasted call otherwise. Never blocks.\n */\nfunction maybeReportResult(\n  caller: ToolCaller,\n  name: string,\n  result: ToolCallResult,\n  output: unknown,\n): void {\n  if (!result.ifc || !caller.reportResult || !isUntrustedSource(name)) return;\n  void caller.reportResult({ tool: name, resultText: stringifyResult(output) }).catch(() => {});\n}\n\n/**\n * Inspect an untrusted-source tool's OUTPUT for indirect prompt injection (and\n * leaked secrets/PII) BEFORE the agent ingests it. Unlike {@link maybeReportResult}\n * this is AWAITED - the verdict decides whether the agent sees the output - so it\n * only runs when the adapter opts in via {@link GuardOptions.inspectResults}.\n *\n * Returns the deny verdict to act on, or null to let the output through. An\n * inspection failure resolves to null: a transient gateway error must never\n * break the agent (the server is monitor-first and already audited what it saw).\n */\nasync function inspectOutput(\n  caller: ToolCaller,\n  name: string,\n  output: unknown,\n  opts: GuardOptions,\n): Promise<ToolCallResult | null> {\n  if (!opts.inspectResults || !caller.inspectResult || !isUntrustedSource(name)) {\n    return null;\n  }\n  let verdict: ToolCallResult;\n  try {\n    verdict = await caller.inspectResult({\n      tool: name,\n      resultText: stringifyResult(output),\n    });\n  } catch {\n    return null;\n  }\n  return verdict.decision === \"deny\" ? verdict : null;\n}\n\n/** What to do when AxioRank denies a tool call. */\nexport type OnDeny = \"throw\" | \"return\";\n\nexport interface GuardOptions {\n  /**\n   * On a `deny` verdict: `\"throw\"` (default) raises {@link AxioRankDeniedError}\n   * so the step fails hard; `\"return\"` returns a short, model-readable refusal\n   * string instead, so an agent sees why it was blocked and can re-plan.\n   * @default \"throw\"\n   */\n  onDeny?: OnDeny;\n  /**\n   * Observe every verdict (allow and deny) - handy for logging / metrics. Never\n   * affects control flow; an error thrown here is swallowed.\n   */\n  onDecision?: (result: ToolCallResult) => void;\n  /**\n   * Also inspect each untrusted-source tool's OUTPUT for indirect prompt\n   * injection (and leaked secrets/PII) before the agent ingests it. A `deny`\n   * verdict on the output is handled exactly like a denied call (per\n   * {@link GuardOptions.onDeny}). Adds one awaited round trip per such tool, so\n   * it's opt-in. Enforcement is gated server-side by the workspace's\n   * `inspect_tool_results` setting (monitor-first until enabled).\n   * @default false\n   */\n  inspectResults?: boolean;\n}\n\n/** The refusal a model sees when {@link GuardOptions.onDeny} is `\"return\"`. */\nexport function denialMessage(name: string, result: ToolCallResult): string {\n  return `AxioRank blocked the tool call \\`${name}\\`: ${result.reason} (risk ${result.risk}).`;\n}\n\n/**\n * Score `name(args)` through AxioRank, then either run `exec` (allow) or handle\n * the deny per `opts.onDeny`. The single chokepoint every framework adapter\n * funnels through.\n *\n * A `require_approval` hold is invisible here: {@link AxioRank.toolCall} waits\n * it out and only ever surfaces the final `allow` / `deny`.\n */\nexport async function runGuarded<T>(\n  caller: ToolCaller,\n  name: string,\n  args: Record<string, unknown>,\n  exec: () => T | Promise<T>,\n  opts: GuardOptions = {},\n): Promise<T | string> {\n  const result = await caller.toolCall({ tool: name, arguments: args });\n  if (opts.onDecision) {\n    try {\n      opts.onDecision(result);\n    } catch {\n      // An observer must never change the outcome of the call.\n    }\n  }\n  if (result.decision === \"deny\") {\n    if (opts.onDeny === \"return\") return denialMessage(name, result);\n    throw new AxioRankDeniedError(result);\n  }\n  const output = await exec();\n\n  // Opt-in: inspect the tool's OUTPUT for indirect prompt injection. A deny is\n  // handled exactly like a denied call - block the poisoned output from reaching\n  // the model (throw) or hand back a refusal string (onDeny === \"return\").\n  const outputDeny = await inspectOutput(caller, name, output, opts);\n  if (outputDeny) {\n    if (opts.onDeny === \"return\") return denialMessage(name, outputDeny);\n    throw new AxioRankDeniedError(outputDeny);\n  }\n  // inspectResult already taint-tracks server-side, so only the legacy\n  // fire-and-forget report is needed when output inspection is off.\n  if (!opts.inspectResults) maybeReportResult(caller, name, result, output);\n  return output;\n}\n\n/**\n * Wrap a tool's `execute(args, …)` handler so every invocation is scored by\n * AxioRank first. Framework-agnostic: works anywhere a tool handler takes the\n * arguments object as its first parameter (the Vercel AI SDK and the OpenAI\n * Agents SDK both fit). Any extra parameters the framework passes -\n * `toolCallId`, `context`, `abortSignal`, … - are forwarded untouched.\n */\nexport function guardExecute<Args extends Record<string, unknown>, R>(\n  name: string,\n  execute: (args: Args, ...rest: unknown[]) => R | Promise<R>,\n  caller: ToolCaller,\n  opts: GuardOptions = {},\n): (args: Args, ...rest: unknown[]) => Promise<R | string> {\n  return (args, ...rest) =>\n    runGuarded(\n      caller,\n      name,\n      (args ?? {}) as Record<string, unknown>,\n      () => execute(args, ...rest),\n      opts,\n    );\n}\n","/**\n * AxioRank adapter for the Anthropic Messages API (`@anthropic-ai/sdk`).\n *\n * Unlike the Vercel / OpenAI-Agents adapters, the Anthropic SDK does NOT run your\n * tools - your own loop does: the model returns `tool_use` content blocks, you\n * execute them and send back `tool_result` blocks. So this adapter guards your\n * dispatch: hand it each `tool_use` block plus your executor, and it scores the\n * call through AxioRank first, returning a ready-to-send `tool_result`.\n *\n * @example\n * ```ts\n * import Anthropic from \"@anthropic-ai/sdk\";\n * import { AxioRank } from \"@axiorank/sdk\";\n * import { guardToolHandlers } from \"@axiorank/sdk/anthropic\";\n *\n * const anthropic = new Anthropic();\n * const axio = new AxioRank({ apiKey: process.env.AXIORANK_KEY! });\n *\n * // One guarded dispatcher over your tool handlers.\n * const dispatch = guardToolHandlers(\n *   { deployToProd: async ({ service }) => deploy(service) },\n *   axio.trace(), // a trace handle correlates one run into a kill-chain trace\n *   { onDeny: \"return\" }, // let the model read the refusal and adapt\n * );\n *\n * const msg = await anthropic.messages.create({ model: \"claude-opus-4-8\", tools, messages });\n * const toolResults = [];\n * for (const block of msg.content) {\n *   if (block.type === \"tool_use\") toolResults.push(await dispatch(block));\n * }\n * // send `toolResults` back as the next user message\n * ```\n *\n * On a `deny` verdict the returned tool_result carries `is_error: true` and the\n * refusal text, so the model can re-plan (pass `onDeny: \"throw\"` to fail the loop\n * instead). Operate on the ASSEMBLED tool_use block, not streaming\n * `input_json_delta` events. A handler that throws propagates (so a real tool\n * failure isn't silently masked); only AxioRank denials become tool_results.\n */\nimport { AxioRankDeniedError } from \"../errors\";\nimport type { GuardOptions, ToolCaller } from \"./_shared\";\nimport { denialMessage, runGuarded } from \"./_shared\";\n\nexport { denialMessage, runGuarded } from \"./_shared\";\nexport type { GuardOptions, OnDeny, ToolCaller } from \"./_shared\";\n\n/** A `tool_use` content block from an Anthropic assistant message (structural). */\nexport interface ToolUseBlockLike {\n  id: string;\n  name: string;\n  input: unknown;\n}\n\n/** A `tool_result` content block to send back in the next user message. */\nexport interface ToolResultBlock {\n  type: \"tool_result\";\n  tool_use_id: string;\n  content: string;\n  is_error?: boolean;\n}\n\n/** Executes the named tool with its parsed input, returning the tool's output. */\nexport type ToolExecutor = (\n  name: string,\n  input: Record<string, unknown>,\n) => unknown | Promise<unknown>;\n\nconst MAX_RESULT_CHARS = 100_000;\n\nfunction asContent(output: unknown): string {\n  let s: string;\n  if (typeof output === \"string\") s = output;\n  else {\n    try {\n      s = JSON.stringify(output) ?? String(output);\n    } catch {\n      s = String(output);\n    }\n  }\n  return s.length > MAX_RESULT_CHARS ? s.slice(0, MAX_RESULT_CHARS) : s;\n}\n\n/**\n * Guard ONE Anthropic `tool_use` block: score `name(input)` through AxioRank,\n * then run `executor` (allow) or refuse (deny). Returns the `tool_result` block to\n * send back. Defaults to `onDeny: \"return\"` so the model reads the refusal and can\n * re-plan; pass `onDeny: \"throw\"` to raise {@link AxioRankDeniedError} instead.\n */\nexport async function guardToolUse(\n  block: ToolUseBlockLike,\n  caller: ToolCaller,\n  executor: ToolExecutor,\n  opts: GuardOptions = {},\n): Promise<ToolResultBlock> {\n  const input = (block.input ?? {}) as Record<string, unknown>;\n  try {\n    // Run with onDeny:\"throw\" internally so we can shape the deny into a\n    // tool_result; this reuses runGuarded's IFC result-reporting + output\n    // inspection + onDecision, keeping behavior identical to the other adapters.\n    // A denied OUTPUT throws the same AxioRankDeniedError and is shaped below.\n    const output = await runGuarded(\n      caller,\n      block.name,\n      input,\n      () => executor(block.name, input),\n      {\n        onDecision: opts.onDecision,\n        onDeny: \"throw\",\n        inspectResults: opts.inspectResults,\n      },\n    );\n    return { type: \"tool_result\", tool_use_id: block.id, content: asContent(output) };\n  } catch (err) {\n    if (err instanceof AxioRankDeniedError) {\n      if (opts.onDeny === \"throw\") throw err;\n      return {\n        type: \"tool_result\",\n        tool_use_id: block.id,\n        content: denialMessage(block.name, err.result),\n        is_error: true,\n      };\n    }\n    throw err;\n  }\n}\n\n/**\n * Build a dispatcher over a map of tool handlers: pass any `tool_use` block and it\n * scores + runs the matching handler, returning the `tool_result`. A block naming\n * an unknown tool yields an `is_error` result (and is NOT scored, since nothing\n * runs), so the agent loop keeps going.\n */\nexport function guardToolHandlers(\n  handlers: Record<string, (input: Record<string, unknown>) => unknown | Promise<unknown>>,\n  caller: ToolCaller,\n  opts: GuardOptions = {},\n): (block: ToolUseBlockLike) => Promise<ToolResultBlock> {\n  return (block) => {\n    const handler = handlers[block.name];\n    if (!handler) {\n      return Promise.resolve({\n        type: \"tool_result\",\n        tool_use_id: block.id,\n        content: `No handler registered for tool \"${block.name}\".`,\n        is_error: true,\n      });\n    }\n    return guardToolUse(block, caller, (_name, input) => handler(input), opts);\n  };\n}\n"]}