diff --git a/examples/tracing/claude-agent-sdk/claudeAgentSdkMultiAgent.ts b/examples/tracing/claude-agent-sdk/claudeAgentSdkMultiAgent.ts new file mode 100644 index 0000000..874b108 --- /dev/null +++ b/examples/tracing/claude-agent-sdk/claudeAgentSdkMultiAgent.ts @@ -0,0 +1,212 @@ +/** + * Multi-agent Openlayer tracing example for the Claude Agent SDK (TypeScript). + * + * This script builds a richer agent than the basic example: a codebase + * analyzer that orchestrates two subagents and an in-process MCP tool. It + * exercises every step type the Openlayer wrapper captures so the resulting + * trace contains: + * + * - Root AGENT step "Claude Agent SDK query" with system_prompt, + * agent_config, agents_defined, options, model, cost, tokens, rawOutput. + * - Per-turn CHAT_COMPLETION steps with prompt/completion tokens, thinking + * blocks (if any), tool_calls list, and rawOutput (full assistant + * message JSON). + * - TOOL steps for each tool call: + * * mcp__file-stats__count_files — with mcp_server / mcp_tool_name + * metadata parsed from the mcp__server__tool naming convention. + * * Glob / Read — the built-in file tools. + * * Agent (twice) — one per subagent dispatch. Each Agent ToolStep + * contains the subagent's nested CHAT_COMPLETION and TOOL steps, + * correlated via parent_tool_use_id. + * + * Run with: + * ANTHROPIC_API_KEY=... OPENLAYER_API_KEY=... \ + * OPENLAYER_INFERENCE_PIPELINE_ID=... \ + * npx tsx examples/tracing/claude-agent-sdk/claudeAgentSdkMultiAgent.ts + */ + +// Drop-in import — same shape as @anthropic-ai/claude-agent-sdk's `query`, +// auto-traced. +import { query } from 'openlayer/lib/integrations/claudeAgentSdk'; + +// Helpers from the underlying SDK that aren't routed through Openlayer. +// These are pure factory functions (no I/O), so importing directly is fine. +import { tool, createSdkMcpServer } from '@anthropic-ai/claude-agent-sdk'; +import { z } from 'zod'; + +import { promises as fs } from 'node:fs'; +import * as path from 'node:path'; + +// --------------------------------------------------------------------------- +// 1. Define a custom MCP tool: count_files +// +// Exposes an in-process function as an MCP tool. In the trace it will show +// up as a TOOL step named `mcp__file-stats__count_files` with +// `metadata.mcp_server === "file-stats"` and `metadata.mcp_tool_name === "count_files"`. +// --------------------------------------------------------------------------- + +const countFiles = tool( + 'count_files', + 'Count files in a directory grouped by extension', + // Zod schema. The SDK uses zod-to-json-schema to produce the MCP tool + // schema the model sees. + { directory: z.string().describe('Absolute path to the directory to analyze') }, + async (args) => { + const dir = path.resolve(args.directory); + let stat; + try { + stat = await fs.stat(dir); + } catch { + return { + content: [{ type: 'text', text: `No such directory: ${dir}` }], + isError: true, + }; + } + if (!stat.isDirectory()) { + return { + content: [{ type: 'text', text: `Not a directory: ${dir}` }], + isError: true, + }; + } + const counts = new Map(); + async function walk(d: string): Promise { + const entries = await fs.readdir(d, { withFileTypes: true }); + for (const e of entries) { + const p = path.join(d, e.name); + if (e.isDirectory()) await walk(p); + else if (e.isFile()) { + const ext = path.extname(e.name) || '(no ext)'; + counts.set(ext, (counts.get(ext) ?? 0) + 1); + } + } + } + await walk(dir); + const sorted = [...counts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 20); + const body = sorted.map(([ext, n]) => `${ext}: ${n}`).join('\n') || '(empty)'; + return { content: [{ type: 'text', text: body }] }; + }, +); + +const fileStatsServer = createSdkMcpServer({ + name: 'file-stats', + version: '1.0.0', + tools: [countFiles], +}); + +// --------------------------------------------------------------------------- +// 2. Define two subagents. +// +// Subagents are registered under the SDK's built-in `Agent` tool. When the +// main agent calls Agent(name="code-reviewer", ...), the SDK runs the +// subagent in its own context and the wrapper nests every message the +// subagent emits under the spawning Agent ToolStep via `parent_tool_use_id`. +// --------------------------------------------------------------------------- + +const subagents = { + 'code-reviewer': { + description: 'Briefly reviews a code file for clarity, correctness, and style.', + prompt: + 'You are a senior code reviewer. The user will tell you which file to inspect. ' + + 'Read that file once, then return exactly one observation about its quality ' + + '(strength or weakness). Be specific and concise — two sentences max.', + tools: ['Read'], + model: 'claude-haiku-4-5', + }, + 'summary-writer': { + description: "Writes a one-paragraph summary of an agent's findings.", + prompt: + 'You synthesize prior agent findings into a single one-paragraph summary ' + + '(3-5 sentences). Be specific and concise; do not invent details that ' + + "weren't reported.", + tools: [], + model: 'claude-haiku-4-5', + }, +}; + +// --------------------------------------------------------------------------- +// 3. Run it. +// --------------------------------------------------------------------------- + +async function main(): Promise { + if (!process.env['ANTHROPIC_API_KEY']) { + console.error('Set ANTHROPIC_API_KEY before running this example.'); + process.exit(1); + } + if (!process.env['OPENLAYER_INFERENCE_PIPELINE_ID']) { + console.warn( + 'OPENLAYER_INFERENCE_PIPELINE_ID is not set — the trace will be built but not published.', + ); + } + + // Point the agent at this repo's integrations directory. Change to any + // directory you'd like to analyze. + const targetDir = path.resolve(__dirname, '../../../src/lib/integrations'); + + const prompt = `Analyze the directory at: ${targetDir} + +Follow this plan exactly: + +1. Call the count_files tool with that directory to get a file-extension breakdown. +2. Use Glob to list .ts files under the directory and pick exactly ONE non-trivial file. + Dispatch the code-reviewer subagent to review that file briefly. +3. Dispatch the summary-writer subagent to produce a one-paragraph summary of + (a) the extension counts and (b) the code-reviewer's finding. + +Output a 4-line markdown report: file counts, file reviewed, reviewer's observation, +and the summary-writer's paragraph.`; + + console.log('\n=== Multi-agent codebase analyzer ===\n'); + + let result: any = null; + try { + for await (const message of query({ + prompt, + options: { + model: 'claude-haiku-4-5', + systemPrompt: + "You are a codebase analysis agent. Follow the user's three-step plan exactly, " + + 'in order. Be terse — the final answer should be a 4-line markdown report.', + allowedTools: ['Glob', 'Read', 'Agent', 'mcp__file-stats__count_files'], + mcpServers: { 'file-stats': fileStatsServer }, + agents: subagents, + maxTurns: 15, + } as any, + })) { + // Log a brief progress line so users can see the agent loop in action. + if (message.type === 'assistant') { + const blocks = message.message?.content ?? []; + for (const b of blocks as any[]) { + if (b.type === 'tool_use') console.log(`[tool] ${b.name}`); + else if (b.type === 'text' && b.text) { + console.log('[text]', b.text.slice(0, 120) + (b.text.length > 120 ? '…' : '')); + } + } + } else if (message.type === 'result') { + result = message; + } + } + } catch (err) { + // The SDK can raise a trailing exception after the ResultMessage; the + // trace itself is still observed and published, so we tolerate it. + console.log(`(SDK raised after result: ${(err as Error).message})`); + } + + if (result) { + console.log('\n=== FINAL ===\n'); + console.log(result.result ?? '(no result)'); + console.log( + `\nturns=${result.num_turns} cost=$${(result.total_cost_usd ?? 0).toFixed(4)} session=${result.session_id}`, + ); + } + + console.log('\nOpen your Openlayer dashboard to view the trace.\n'); + + // The Openlayer publish is fire-and-forget by design; give it a beat to + // flush before the process exits. + await new Promise((resolve) => setTimeout(resolve, 3000)); +} + +main().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/examples/tracing/claude-agent-sdk/claudeAgentSdkTracing.ts b/examples/tracing/claude-agent-sdk/claudeAgentSdkTracing.ts new file mode 100644 index 0000000..1ae4cfc --- /dev/null +++ b/examples/tracing/claude-agent-sdk/claudeAgentSdkTracing.ts @@ -0,0 +1,104 @@ +/** + * Openlayer tracing example for the Claude Agent SDK (TypeScript). + * + * Run with: + * npx tsx examples/tracing/claude-agent-sdk/claudeAgentSdkTracing.ts + * + * Prereqs (export as env vars): + * ANTHROPIC_API_KEY — your Anthropic API key + * OPENLAYER_API_KEY — your Openlayer ingest key + * OPENLAYER_INFERENCE_PIPELINE_ID — destination pipeline ID + * + * Install the optional peer (if you haven't already): + * npm install @anthropic-ai/claude-agent-sdk@^0.2.111 + */ + +// --------------------------------------------------------------------------- +// Option A — drop-in import (recommended). Replace +// +// import { query } from "@anthropic-ai/claude-agent-sdk"; +// +// with the Openlayer subpath: +import { query } from 'openlayer/lib/integrations/claudeAgentSdk'; + +// --------------------------------------------------------------------------- +// Option B — one-shot runtime patch (uncomment to use this style instead): +// import { query } from "@anthropic-ai/claude-agent-sdk"; +// import { traceClaudeAgentSdk } from "openlayer/lib/integrations/claudeAgentSdk"; +// traceClaudeAgentSdk({ +// inferencePipelineId: process.env.OPENLAYER_INFERENCE_PIPELINE_ID, +// truncateToolOutputChars: 8192, +// captureThinking: true, +// redactMcpEnv: true, +// }); + +async function simpleQuery() { + console.log('\n=== Scenario 1: code search with built-in tools ===\n'); + for await (const message of query({ + prompt: + 'What does the function summarizePrompt do in src/lib/integrations/claudeAgentSdk.ts? Answer in one sentence.', + options: { + model: 'claude-haiku-4-5', + allowedTools: ['Read', 'Glob', 'Grep'], + }, + })) { + if (message.type === 'assistant') { + const text = message.message?.content + ?.filter((b: any) => b.type === 'text') + .map((b: any) => b.text) + .join(''); + if (text) console.log('[assistant]', text); + } else if (message.type === 'result') { + console.log('\n[result]', { cost: message.total_cost_usd, turns: message.num_turns }); + } + } +} + +async function subagentExample() { + console.log('\n=== Scenario 2: dispatch a code-review subagent ===\n'); + // ``agents`` registers a subagent under the SDK's built-in ``Agent`` tool. + // The wrapper picks up the subagent's assistant turns and tool calls and + // nests them under the spawning Agent ``ToolStep`` automatically (via + // ``parent_tool_use_id``). + for await (const message of query({ + prompt: + 'Dispatch the code-reviewer subagent to review src/lib/integrations/claudeAgentSdk.ts and report back in one sentence.', + options: { + model: 'claude-haiku-4-5', + allowedTools: ['Read', 'Agent'], + agents: { + 'code-reviewer': { + description: 'Reviews a code file for clarity, correctness, and style.', + prompt: + 'You are a senior code reviewer. Inspect the file under review and call out one ' + + 'observation. Be concise — one paragraph.', + tools: ['Read'], + }, + }, + } as any, + })) { + if (message.type === 'result') { + console.log('[result]', { cost: message.total_cost_usd, turns: message.num_turns }); + } + } +} + +async function main() { + if (!process.env['ANTHROPIC_API_KEY']) { + console.error('Set ANTHROPIC_API_KEY before running this example.'); + process.exit(1); + } + if (!process.env['OPENLAYER_INFERENCE_PIPELINE_ID']) { + console.warn('OPENLAYER_INFERENCE_PIPELINE_ID is not set — the trace will be built but not published.'); + } + + await simpleQuery(); + await subagentExample(); + + console.log('\nDone. Open your Openlayer dashboard to view the traces.\n'); +} + +main().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/package.json b/package.json index 1dd86a2..9a7989d 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "uuid": "^9.0.1" }, "devDependencies": { + "@anthropic-ai/claude-agent-sdk": "^0.2.111", "@arethetypeswrong/cli": "^0.17.0", "@swc/core": "^1.3.102", "@swc/jest": "^0.2.29", @@ -55,6 +56,7 @@ "tsc-multi": "https://github.com/stainless-api/tsc-multi/releases/download/v1.1.9/tsc-multi.tgz", "tsconfig-paths": "^4.0.0", "tslib": "^2.8.1", + "tsx": "^4.21.0", "typescript": "5.8.3", "typescript-eslint": "8.31.1" }, @@ -86,6 +88,11 @@ "import": "./dist/index.mjs", "require": "./dist/index.js" }, + "./integrations/claude-agent-sdk": { + "import": "./dist/lib/integrations/claudeAgentSdk.mjs", + "require": "./dist/lib/integrations/claudeAgentSdk.js", + "types": "./dist/lib/integrations/claudeAgentSdk.d.ts" + }, "./*.mjs": { "default": "./dist/*.mjs" }, @@ -96,5 +103,13 @@ "import": "./dist/*.mjs", "require": "./dist/*.js" } + }, + "peerDependencies": { + "@anthropic-ai/claude-agent-sdk": "^0.2.111" + }, + "peerDependenciesMeta": { + "@anthropic-ai/claude-agent-sdk": { + "optional": true + } } } diff --git a/src/lib/integrations/claudeAgentSdk.ts b/src/lib/integrations/claudeAgentSdk.ts new file mode 100644 index 0000000..c07b40c --- /dev/null +++ b/src/lib/integrations/claudeAgentSdk.ts @@ -0,0 +1,843 @@ +/** + * Openlayer tracing integration for the Claude Agent SDK (TypeScript). + * + * Wraps ``@anthropic-ai/claude-agent-sdk``'s ``query()`` (and, via + * ``traceClaudeAgentSdk()``, ``ClaudeSDKClient``) so each call becomes + * an Openlayer trace. + * + * Trace shape (one trace per ``query()`` call): + * + * ``` + * AGENT "Claude Agent SDK query" + * |-- CHAT_COMPLETION "assistant turn 1" (text + thinking + tokens) + * |-- TOOL "" (input/output, latency) + * |-- CHAT_COMPLETION "assistant turn 2" + * |-- TOOL "Agent: code-reviewer" (subagent) + * | |-- CHAT_COMPLETION ... (nested via parent_tool_use_id) + * | `-- TOOL ... + * `-- ... + * ``` + * + * See ``docs/superpowers/specs/2026-05-12-claude-agent-sdk-integration-design.md``. + */ +import { AsyncLocalStorage } from 'node:async_hooks'; + +import { StepType } from '../tracing/steps'; +import { _internalCreateStep } from '../tracing/tracer'; + +/** Tunable per-integration configuration. */ +export interface ClaudeAgentSdkConfig { + /** Inference pipeline ID for trace publishing. Falls back to ``OPENLAYER_INFERENCE_PIPELINE_ID``. */ + inferencePipelineId?: string; + /** Max characters to keep from each tool output before truncation. */ + truncateToolOutputChars: number; + /** Whether to capture thinking blocks in the trace. */ + captureThinking: boolean; + /** Whether to strip env/headers/auth from MCP server configs in metadata. */ + redactMcpEnv: boolean; +} + +let _config: ClaudeAgentSdkConfig = { + truncateToolOutputChars: 8192, + captureThinking: true, + redactMcpEnv: true, +}; + +/** Internal trace state for a single ``tracedQuery()`` invocation. */ +interface TraceState { + rootStep: any; + endRootStep: () => void; + sessionId?: string; + model?: string; + userPrompt?: string; + turnCounter: number; + /** ``tool_use_id`` -> open tool step handle. Set on PreToolUse, deleted on + * PostToolUse/PostToolUseFailure once the step is closed. */ + pendingTools: Map void; startTime: number }>; + /** ``tool_use_id`` -> closed tool step (kept around so subagent messages + * can resolve their ``parent_tool_use_id`` to the right step if needed). */ + toolStepById: Map; +} + +/** Per-query() AsyncLocalStorage context so concurrent invocations don't + * trample each other's pending-tool bookkeeping. */ +const _als = new AsyncLocalStorage(); + +const ROOT_STEP_NAME = 'Claude Agent SDK query'; + +/** Coerce ``options.systemPrompt`` (string | preset object) into a JSON-safe + * value, truncated to 4096 chars for string forms. */ +function serializeSystemPrompt(sp: any): any { + if (sp == null) return null; + if (typeof sp === 'string') return truncateString(sp, 4096); + if (typeof sp === 'object') return sp; // preset / { type, preset, append, ... } + return String(sp); +} + +/** Capture each subagent definition's description, prompt (truncated), tools. */ +function serializeAgentDefinitions(agents: any): Record | null { + if (!agents || typeof agents !== 'object') return null; + const out: Record = {}; + for (const [name, defn] of Object.entries(agents)) { + out[name] = { + description: defn?.description, + prompt: truncateString(defn?.prompt, 4096), + tools: defn?.tools, + model: defn?.model, + }; + } + return Object.keys(out).length ? out : null; +} + +/** Snapshot user-provided options onto the root step metadata. Called once + * per query with the *original* (pre-hook-injection) options. */ +function captureOptionsMetadata(rootStep: any, options: any): void { + if (!options) return; + const metadata: Record = {}; + + const sp = serializeSystemPrompt(options.systemPrompt); + if (sp !== null && sp !== undefined) metadata.system_prompt = sp; + + const agents = serializeAgentDefinitions(options.agents); + if (agents) metadata.agents_defined = agents; + + const optKeys = [ + 'model', + 'fallbackModel', + 'maxTurns', + 'maxBudgetUsd', + 'permissionMode', + 'cwd', + 'allowedTools', + 'disallowedTools', + 'continue', + 'resume', + 'forkSession', + ]; + const optCapture: Record = {}; + for (const k of optKeys) { + const v = options[k]; + if (v == null) continue; + if (Array.isArray(v) && v.length === 0) continue; + optCapture[k] = v; + } + if (Object.keys(optCapture).length) metadata.options = optCapture; + + if (Object.keys(metadata).length) { + rootStep.log({ metadata }); + } +} + +/** Truncate a string-or-undefined value to ``maxChars``. Used for system + * prompt and subagent prompt capture. Returns undefined for null/undefined input. */ +function truncateString(value: any, maxChars: number): any { + if (value == null) return undefined; + if (typeof value !== 'string') return value; + if (value.length <= maxChars) return value; + return `${value.slice(0, maxChars)}... [truncated, full length ${value.length}]`; +} + +function redactMcpServers(servers: any): any { + if (!Array.isArray(servers)) return servers; + return servers.map((s) => { + if (s && typeof s === 'object') { + // Strip well-known secret-bearing fields. + const { env, headers, authorization, ...rest } = s; + void env; + void headers; + void authorization; + return rest; + } + return s; + }); +} + +/** Apply the SystemMessage(subtype=init) payload to the root step. */ +function observeSystemInit(msg: any, state: TraceState): void { + if (msg.subtype !== 'init') return; + state.sessionId = msg.session_id; + state.model = msg.model; + state.rootStep.log({ + metadata: { + ...(state.rootStep.metadata ?? {}), + session_id: msg.session_id, + agent_config: { + model: msg.model, + tools: msg.tools, + mcp_servers: _config.redactMcpEnv ? redactMcpServers(msg.mcp_servers) : msg.mcp_servers, + skills: msg.skills, + slash_commands: msg.slash_commands, + plugins: msg.plugins, + permission_mode: msg.permissionMode, + cwd: msg.cwd, + claude_code_version: msg.claude_code_version, + api_key_source: msg.apiKeySource, + output_style: msg.output_style, + }, + }, + }); +} + +/** Apply the terminal ResultMessage to the root step (cost/tokens/duration). */ +function observeResult(msg: any, state: TraceState): void { + const usage = msg.usage || {}; + const input = usage.input_tokens ?? 0; + const output = usage.output_tokens ?? 0; + // ``AgentStep`` doesn't declare ``cost``/``tokens`` properties, but + // ``postProcessTrace`` reads them off the root step regardless (via a + // ChatCompletionStep cast). Assign them directly so they make it into + // the published trace. + state.rootStep.cost = msg.total_cost_usd ?? null; + state.rootStep.tokens = input + output; + state.rootStep.promptTokens = input; + state.rootStep.completionTokens = output; + state.rootStep.log({ + output: msg.result ?? '', + latency: msg.duration_ms ?? null, + metadata: { + ...(state.rootStep.metadata ?? {}), + session_id: msg.session_id ?? state.sessionId, + num_turns: msg.num_turns, + stop_reason: msg.stop_reason, + subtype: msg.subtype, + is_error: msg.is_error, + duration_api_ms: msg.duration_api_ms, + model_usage: msg.modelUsage, + permission_denials: msg.permission_denials, + cache_read_input_tokens: usage.cache_read_input_tokens, + cache_creation_input_tokens: usage.cache_creation_input_tokens, + // Surface the same fields in metadata too — the base ``Step`` toJSON() + // doesn't include cost/tokens for AgentStep, so downstream consumers + // reading metadata still get the picture. + cost: msg.total_cost_usd ?? null, + tokens: input + output, + promptTokens: input, + completionTokens: output, + model: state.model ?? null, + provider: 'anthropic', + // No first-class rawOutput on AgentStep; expose via metadata. + rawOutput: serializeResultMessage(msg), + }, + }); +} + +/** Serialize a ResultMessage to a JSON-ish string for raw_output display. */ +function serializeResultMessage(msg: any): string | null { + try { + return JSON.stringify({ + subtype: msg.subtype, + result: msg.result, + session_id: msg.session_id, + duration_ms: msg.duration_ms, + duration_api_ms: msg.duration_api_ms, + num_turns: msg.num_turns, + stop_reason: msg.stop_reason, + is_error: msg.is_error, + total_cost_usd: msg.total_cost_usd, + usage: msg.usage, + modelUsage: msg.modelUsage, + permission_denials: msg.permission_denials, + }); + } catch { + return null; + } +} + +/** + * Capture an ``AssistantMessage`` as a nested ``CHAT_COMPLETION`` step under + * whichever step is currently top-of-stack. For top-level assistant turns + * that is the root ``AGENT`` step; for subagent turns it is the spawning + * Agent ``ToolStep`` (kept open across the subagent's stream by the + * PreToolUse/PostToolUse hook pair — see Task B5). + */ +function observeAssistant(msg: any, state: TraceState): void { + state.turnCounter += 1; + const blocks: any[] = msg.message?.content ?? []; + const textParts: string[] = []; + const thinkingParts: string[] = []; + const toolUseBlocks: Array<{ id: string; name: string; input: any }> = []; + for (const b of blocks) { + if (!b || typeof b !== 'object') continue; + if (b.type === 'text') textParts.push(b.text ?? ''); + else if (b.type === 'thinking') thinkingParts.push(b.thinking ?? ''); + else if (b.type === 'tool_use') + toolUseBlocks.push({ id: b.id, name: b.name, input: b.input }); + } + + const usage = msg.message?.usage ?? {}; + const text = textParts.filter(Boolean).join('\n'); + // Output: prefer text, fall back to tool-use summary, then thinking, then a + // marker so users see *something* in the UI rather than an empty step. + let output: string; + if (text) output = text; + else if (toolUseBlocks.length) + output = `[tool call: ${toolUseBlocks.map((b) => b.name).join(', ')}]`; + else if (thinkingParts.length && _config.captureThinking) + output = `[thinking]\n${thinkingParts.join('\n')}`; + else output = '[no content]'; + + // For top-level assistant turns, surface the user's original prompt as the + // step's input so reviewers see what triggered this turn. Subagent turns + // are driven by the parent's Agent tool call, not by a user prompt. + const isSubagentTurn = msg.parent_tool_use_id != null; + const stepInputs = + !isSubagentTurn && state.userPrompt != null ? { prompt: state.userPrompt } : undefined; + + const [chatStep, endChatStep] = _internalCreateStep( + `assistant turn ${state.turnCounter}`, + StepType.CHAT_COMPLETION, + stepInputs, + undefined, + null, + ); + // ChatCompletionStep has first-class fields for these; cast to ``any`` so + // ``.log`` accepts them — its ``Partial>`` signature + // is computed off the base ``Step`` type and doesn't pick up the subclass + // fields without explicit narrowing. + (chatStep as any).log({ + output, + model: msg.message?.model ?? null, + provider: 'anthropic', + promptTokens: usage.input_tokens ?? null, + completionTokens: usage.output_tokens ?? null, + tokens: (usage.input_tokens ?? 0) + (usage.output_tokens ?? 0), + metadata: { + thinking: + _config.captureThinking && thinkingParts.length ? thinkingParts.join('\n') : null, + tool_calls: toolUseBlocks.length ? toolUseBlocks : null, + stop_reason: msg.message?.stop_reason ?? null, + parent_tool_use_id: msg.parent_tool_use_id ?? null, + message_id: msg.message?.id ?? msg.uuid ?? null, + cache_read_input_tokens: usage.cache_read_input_tokens ?? null, + cache_creation_input_tokens: usage.cache_creation_input_tokens ?? null, + // No first-class rawOutput field on the TS ChatCompletionStep, so we + // stash the full assistant message in metadata so reviewers can still + // inspect the unfiltered model response. + rawOutput: serializeAssistantMessage(msg, blocks), + }, + }); + endChatStep(); +} + +/** Serialize an AssistantMessage's full content array to JSON for raw_output + * inspection. */ +function serializeAssistantMessage(msg: any, blocks: any[]): string | null { + try { + return JSON.stringify({ + model: msg.message?.model ?? null, + stop_reason: msg.message?.stop_reason ?? null, + usage: msg.message?.usage ?? null, + parent_tool_use_id: msg.parent_tool_use_id ?? null, + content: blocks.map((b) => { + if (!b || typeof b !== 'object') return { repr: String(b) }; + if (b.type === 'text') return { type: 'text', text: b.text }; + if (b.type === 'thinking') + return { type: 'thinking', thinking: b.thinking, signature: b.signature }; + if (b.type === 'tool_use') + return { type: 'tool_use', id: b.id, name: b.name, input: b.input }; + return { type: b.type ?? 'unknown', value: b }; + }), + }); + } catch { + return null; + } +} + +/** Dispatch a single message from the SDK stream to the right observer. */ +function observe(msg: any, state: TraceState): void { + if (!msg || typeof msg !== 'object') return; + if (msg.type === 'system') observeSystemInit(msg, state); + else if (msg.type === 'assistant') observeAssistant(msg, state); + else if (msg.type === 'result') observeResult(msg, state); + // user / tool observers added in later tasks +} + +// --------------------------------------------------------------------------- +// Hook callbacks — installed via ``injectHooks`` into the user's options so +// the SDK calls them around each tool invocation. They never mutate the +// agent flow: they always resolve to ``{}`` (an empty hook response) so the +// user's own hooks retain full influence over deny / defer / allow / etc. +// --------------------------------------------------------------------------- + +/** Parse an MCP-namespaced tool name (``mcp____``) into parts. + * Returns an empty object for non-MCP tool names. */ +function parseMcpName(name: string): { mcp_server?: string; mcp_tool_name?: string } { + if (typeof name !== 'string' || !name.startsWith('mcp__')) return {}; + // Names use double-underscore as separator. Split into at most three parts: + // ``mcp``, ````, ````. + const after = name.slice('mcp__'.length); + const sep = after.indexOf('__'); + if (sep < 0) return {}; + return { mcp_server: after.slice(0, sep), mcp_tool_name: after.slice(sep + 2) }; +} + +function truncateToolOutput(value: any, maxChars: number): string { + let s: string; + if (typeof value === 'string') s = value; + else { + try { + s = JSON.stringify(value); + } catch { + s = String(value); + } + } + return s.length > maxChars ? s.slice(0, maxChars) + `... [truncated, full length ${s.length}]` : s; +} + +async function preToolUseHook(input: any, toolUseID: string | undefined, _ctx: any): Promise { + void _ctx; + const state = _als.getStore(); + if (!state || !toolUseID) return {}; + const toolName: string = input?.tool_name ?? 'unknown'; + const toolInput = input?.tool_input ?? {}; + const meta: Record = { + tool_use_id: toolUseID, + ...parseMcpName(toolName), + }; + try { + const [step, endStep] = _internalCreateStep(toolName, StepType.TOOL, toolInput, undefined, meta); + state.pendingTools.set(toolUseID, { step, endStep, startTime: Date.now() }); + } catch (err) { + console.error('[openlayer] preToolUseHook failed:', err); + } + return {}; +} + +async function postToolUseHook(input: any, toolUseID: string | undefined, _ctx: any): Promise { + void _ctx; + const state = _als.getStore(); + if (!state || !toolUseID) return {}; + const handle = state.pendingTools.get(toolUseID); + if (!handle) return {}; + state.pendingTools.delete(toolUseID); + try { + const raw = input?.tool_response ?? input?.tool_output ?? input?.output; + const output = truncateToolOutput(raw, _config.truncateToolOutputChars); + handle.step.log({ + output, + metadata: { + ...(handle.step.metadata ?? {}), + is_error: false, + latency_ms: Date.now() - handle.startTime, + }, + }); + handle.endStep(); + state.toolStepById.set(toolUseID, handle.step); + } catch (err) { + console.error('[openlayer] postToolUseHook failed:', err); + // Still pop the stack to avoid corrupting future steps. + try { + handle.endStep(); + } catch { + /* noop */ + } + } + return {}; +} + +async function postToolUseFailureHook(input: any, toolUseID: string | undefined, _ctx: any): Promise { + void _ctx; + const state = _als.getStore(); + if (!state || !toolUseID) return {}; + const handle = state.pendingTools.get(toolUseID); + if (!handle) return {}; + state.pendingTools.delete(toolUseID); + try { + const errPayload = input?.error ?? input?.tool_response ?? input; + const output = truncateToolOutput(errPayload, _config.truncateToolOutputChars); + handle.step.log({ + output, + metadata: { + ...(handle.step.metadata ?? {}), + is_error: true, + latency_ms: Date.now() - handle.startTime, + }, + }); + handle.endStep(); + state.toolStepById.set(toolUseID, handle.step); + } catch (err) { + console.error('[openlayer] postToolUseFailureHook failed:', err); + try { + handle.endStep(); + } catch { + /* noop */ + } + } + return {}; +} + +/** + * Merge our internal observation hooks into the user's ``options.hooks`` + * map without replacing any existing user matchers. + * + * The SDK's hook structure is:: + * + * { hooks: { PreToolUse: [{ matcher?: string, hooks: [fn, ...] }], ... } } + * + * We append a new matcher entry per event so user matchers fire first and + * our internal observers fire alongside them. + */ +function injectHooks(options: any): any { + const opts = options ? { ...options } : {}; + const userHooks: Record = { ...((opts.hooks as Record) ?? {}) }; + const append = (event: string, fn: any) => { + userHooks[event] = [...(userHooks[event] ?? []), { hooks: [fn] }]; + }; + append('PreToolUse', preToolUseHook); + append('PostToolUse', postToolUseHook); + append('PostToolUseFailure', postToolUseFailureHook); + opts.hooks = userHooks; + return opts; +} + +let _underlyingQuery: ((opts: any) => AsyncIterable) | null = null; + +/** + * Resolve the SDK's ``query`` export. The SDK ships ESM-only, but it's + * common to consume Openlayer from CommonJS contexts (Jest with the default + * preset, etc.). Try ``require()`` first (works on Node 22.12+ for ESM, and + * always works for the virtual jest mock used in unit tests), then fall + * back to dynamic ``import()`` for older Node and pure-CJS Jest workers. + */ +async function loadUnderlyingQuery(): Promise<(opts: any) => AsyncIterable> { + if (_underlyingQuery) return _underlyingQuery; + let mod: any = null; + let requireErr: unknown; + try { + mod = require('@anthropic-ai/claude-agent-sdk'); + } catch (err) { + requireErr = err; + mod = null; + } + if (!mod || typeof mod.query !== 'function') { + try { + mod = await import('@anthropic-ai/claude-agent-sdk'); + // ``import()`` of a CJS module wraps named exports under ``default`` + // on some runtimes; unwrap if needed. + if (mod && typeof mod.query !== 'function' && mod.default && typeof mod.default.query === 'function') { + mod = mod.default; + } + } catch (err2) { + const r = requireErr instanceof Error ? requireErr.message : ''; + const i = err2 instanceof Error ? err2.message : String(err2); + throw new Error( + '@anthropic-ai/claude-agent-sdk is not installed or could not be loaded' + + ` (require: ${r || 'no error'}; import: ${i}). ` + + 'Install with: npm install @anthropic-ai/claude-agent-sdk@^0.2.111', + ); + } + } + if (!mod || typeof mod.query !== 'function') { + throw new Error('@anthropic-ai/claude-agent-sdk module is missing the expected `query` export'); + } + _underlyingQuery = mod.query; + return _underlyingQuery!; +} + +/** + * Wrap ``claude-agent-sdk`` ``query()`` and emit an Openlayer trace. + * + * The wrapper is a pure observer of the underlying message stream: every + * message yielded by the SDK is forwarded to the caller unchanged, in the + * same order. Trace steps are emitted as a side effect. + * + * @example + * ```ts + * import { tracedQuery } from "@openlayer/sdk/integrations/claude-agent-sdk"; + * for await (const message of tracedQuery({ prompt: "Plan a trip" })) { + * console.log(message); + * } + * ``` + */ +export async function* tracedQuery(params: { + prompt: string | AsyncIterable; + options?: any; + inferencePipelineId?: string; +}): AsyncGenerator { + const underlyingQuery = await loadUnderlyingQuery(); + + const name = ROOT_STEP_NAME; + const [rootStep, endRootStep] = _internalCreateStep( + name, + StepType.AGENT, + { prompt: params.prompt }, + undefined, + null, + null, + null, + params.inferencePipelineId ?? _config.inferencePipelineId, + ); + + // Snapshot user-provided options BEFORE we inject our hooks so the captured + // metadata reflects what the user actually configured, not our mutations. + captureOptionsMetadata(rootStep, params.options); + + const state: TraceState = { + rootStep, + endRootStep, + turnCounter: 0, + userPrompt: typeof params.prompt === 'string' ? params.prompt : undefined, + pendingTools: new Map(), + toolStepById: new Map(), + }; + const optionsWithHooks = injectHooks(params.options); + + // ``yield`` cannot live inside ``_als.run(() => ...)`` directly (the + // callback can't be a generator). Instead, we manually iterate the + // underlying async generator wrapped in ``_als.run`` for each ``next()`` + // call so all hook callbacks (which may be invoked during ``next()``) see + // our state via ``_als.getStore()``. + const iter = _als.run(state, () => underlyingQuery({ prompt: params.prompt, options: optionsWithHooks })); + const asyncIter = + (iter as any)[Symbol.asyncIterator] ? (iter as any)[Symbol.asyncIterator]() : (iter as any); + + try { + while (true) { + const result: IteratorResult = await _als.run(state, () => asyncIter.next()); + if (result.done) break; + const msg = result.value; + try { + await _als.run(state, async () => observe(msg, state)); + } catch (err) { + // Never break the user's stream because of a tracing bug. + + console.error('[openlayer] claude-agent-sdk observation failed:', err); + } + yield msg; + } + } finally { + // Close any tool steps that never received a PostToolUse (defensive — + // the SDK contract is that every Pre is followed by a Post or Failure). + for (const handle of state.pendingTools.values()) { + try { + handle.endStep(); + } catch { + /* noop */ + } + } + state.pendingTools.clear(); + try { + endRootStep(); + } catch (err) { + console.error('[openlayer] failed to close root trace step:', err); + } + } +} + +// --------------------------------------------------------------------------- +// Public API: drop-in ``query`` and ``traceClaudeAgentSdk`` runtime patcher. +// --------------------------------------------------------------------------- + +/** + * Drop-in replacement for ``@anthropic-ai/claude-agent-sdk``'s ``query``. + * + * @example + * ```ts + * import { query } from "@openlayer/sdk/integrations/claude-agent-sdk"; + * for await (const message of query({ prompt: "Plan a trip" })) { ... } + * ``` + */ +export function query(params: { + prompt: string | AsyncIterable; + options?: any; + inferencePipelineId?: string; +}): AsyncGenerator { + return tracedQuery(params); +} + +/** + * One-shot init for codebases that can't change their imports. + * + * Mutates the ``@anthropic-ai/claude-agent-sdk`` module's ``query`` (and + * ``ClaudeSDKClient.prototype.query`` / ``.receiveResponse`` once available) + * so every subsequent call is auto-traced. Idempotent: calling it multiple + * times only patches once but does refresh the tunable config. + * + * @example + * ```ts + * import { traceClaudeAgentSdk } from "@openlayer/sdk/integrations/claude-agent-sdk"; + * import { query } from "@anthropic-ai/claude-agent-sdk"; + * + * traceClaudeAgentSdk({ inferencePipelineId: "..." }); + * for await (const m of query({ prompt: "..." })) { ... } + * ``` + */ +export function traceClaudeAgentSdk(opts: Partial = {}): void { + // Refresh in-process config every call so users can re-tune at any time. + _config = { ..._config, ...opts }; + + // We need a SYNCHRONOUS module reference here to mutate ``sdk.query``. + // Pure-ESM modules only work via ``import()`` (async) on older Node. + // ``require()`` does work on Node 22.12+ for ESM, and is the fast path + // for the jest virtual mock and CJS consumers. + let sdk: any = null; + let cause: unknown; + try { + sdk = require('@anthropic-ai/claude-agent-sdk'); + } catch (err) { + cause = err; + sdk = null; + } + if (!sdk) { + const detail = cause instanceof Error ? ` (cause: ${cause.message})` : ''; + throw new Error( + '@anthropic-ai/claude-agent-sdk is not installed or could not be loaded' + + detail + + '. On Node <22.12 with pure-ESM SDK builds, use the drop-in `query` ' + + 'export from this module instead (which lazy-loads via dynamic import). ' + + 'Install with: npm install @anthropic-ai/claude-agent-sdk@^0.2.111', + ); + } + + if (typeof sdk.query !== 'function') { + throw new Error('@anthropic-ai/claude-agent-sdk is missing the expected `query` export'); + } + + // Already patched? Just refresh config and exit (already done above). + if ((sdk.query as any)._openlayerPatched) return; + + const original = sdk.query; + const patched: any = function patchedQuery(params: any) { + return tracedQuery(params); + }; + patched._openlayerPatched = true; + patched._openlayerOriginal = original; + try { + sdk.query = patched; + } catch (err) { + // ESM module bindings are read-only; fall back to defineProperty. + try { + Object.defineProperty(sdk, 'query', { value: patched, writable: true, configurable: true }); + } catch { + console.error('[openlayer] failed to monkey-patch @anthropic-ai/claude-agent-sdk.query', err); + return; + } + } + + // Patch ClaudeSDKClient if present (Task B12 fills this in). + patchClaudeSdkClientIfPresent(sdk); +} + +/** + * Wrap ``ClaudeSDKClient.prototype.query`` / ``.receiveResponse`` so existing + * client-based codepaths get traced too. Each ``client.query(...)`` is + * treated as one trace, with the same step shape as the standalone + * ``query()`` function. + */ +function patchClaudeSdkClientIfPresent(sdk: any): void { + const Client = sdk.ClaudeSDKClient; + if (typeof Client !== 'function' || !Client.prototype) return; + if ((Client.prototype as any)._openlayerPatched) return; + + // ``receive_response`` (snake) is the iterator the user awaits. We + // intercept it to attach our trace state via AsyncLocalStorage and emit + // the root AGENT step around the whole streamed response. We also patch + // ``query`` (which sends the user prompt) so that the prompt becomes + // visible on the root step. + const originalReceive = Client.prototype.receive_response ?? Client.prototype.receiveResponse; + const originalQuery = Client.prototype.query; + + if (typeof originalReceive === 'function') { + const patchedReceive = async function* patchedReceiveResponse(this: any, ...args: any[]) { + const name = ROOT_STEP_NAME; + const [rootStep, endRootStep] = _internalCreateStep( + name, + StepType.AGENT, + { prompt: this.__openlayerLastPrompt }, + undefined, + null, + null, + null, + _config.inferencePipelineId, + ); + // Snapshot the user's original options (stashed at construction) onto + // root metadata so users see the system prompt, subagent definitions, + // model, permission mode, etc. that drove this run. + captureOptionsMetadata(rootStep, this.__openlayerOriginalOptions); + + const state: TraceState = { + rootStep, + endRootStep, + turnCounter: 0, + userPrompt: + typeof this.__openlayerLastPrompt === 'string' ? this.__openlayerLastPrompt : undefined, + pendingTools: new Map(), + toolStepById: new Map(), + }; + const upstream = originalReceive.apply(this, args); + const asyncIter = upstream[Symbol.asyncIterator] ? upstream[Symbol.asyncIterator]() : upstream; + try { + while (true) { + const result: IteratorResult = await _als.run(state, () => asyncIter.next()); + if (result.done) break; + const msg = result.value; + try { + await _als.run(state, async () => observe(msg, state)); + } catch (err) { + console.error('[openlayer] observation failed (ClaudeSDKClient):', err); + } + yield msg; + } + } finally { + for (const handle of state.pendingTools.values()) { + try { + handle.endStep(); + } catch { + /* noop */ + } + } + state.pendingTools.clear(); + try { + endRootStep(); + } catch (err) { + console.error('[openlayer] failed to close root trace step:', err); + } + } + }; + Client.prototype.receive_response = patchedReceive; + Client.prototype.receiveResponse = patchedReceive; + } + + if (typeof originalQuery === 'function') { + const patchedClientQuery = async function patchedClientQuery(this: any, prompt: any) { + // Remember the prompt so receive_response can name the root step. + this.__openlayerLastPrompt = prompt; + // Snapshot the user's original options so receive_response can capture + // system_prompt / agents / model / etc. onto the root step BEFORE we + // inject our hooks below. The clone is shallow but our injection only + // replaces the ``hooks`` field, leaving everything else intact. + if (this.options && !this.__openlayerOriginalOptions) { + this.__openlayerOriginalOptions = { ...this.options }; + } + // Ensure our hooks are merged into the options the client was + // constructed with. The client typically holds them on + // ``this.options``; we splice ours in once. + if (this.options && !this.options.__openlayerHooksInjected) { + this.options = injectHooks(this.options); + this.options.__openlayerHooksInjected = true; + } + return originalQuery.call(this, prompt); + }; + Client.prototype.query = patchedClientQuery; + } + + (Client.prototype as any)._openlayerPatched = true; +} + +// --------------------------------------------------------------------------- +// Test-only helpers. Not part of the supported public API. +// --------------------------------------------------------------------------- +/** @internal */ +export function _setConfigForTesting(overrides: Partial): void { + _config = { ..._config, ...overrides }; +} + +/** @internal */ +export function _getConfigForTesting(): Readonly { + return _config; +} + +/** @internal — used by tests to reset the cached SDK query reference. */ +export function _resetUnderlyingQueryForTesting(): void { + _underlyingQuery = null; +} diff --git a/src/lib/integrations/index.ts b/src/lib/integrations/index.ts index 5791273..1e04388 100644 --- a/src/lib/integrations/index.ts +++ b/src/lib/integrations/index.ts @@ -1,4 +1,5 @@ export * from './bedrockAgentTracer'; +export * from './claudeAgentSdk'; export * from './langchainCallback'; export * from './openAiTracer'; export * from './tracedTool'; diff --git a/src/lib/tracing/tracer.ts b/src/lib/tracing/tracer.ts index 045377d..ce1aec8 100644 --- a/src/lib/tracing/tracer.ts +++ b/src/lib/tracing/tracer.ts @@ -409,3 +409,12 @@ export function postProcessTrace(traceObj: Trace): { traceData: any; inputVariab } export default trace; + +// ---------------------------------------------------------------------------- +// Internal helpers re-exported under prefixed names for use by first-party +// Openlayer integrations that need to drive a step's lifecycle manually +// (e.g., open a step from one callback and close it from another). These are +// NOT part of the supported public API: external callers should use the +// `add*StepToTrace` helpers above. +// ---------------------------------------------------------------------------- +export { createStep as _internalCreateStep, getCurrentStep as _internalGetCurrentStep }; diff --git a/tests/integrations/claudeAgentSdk.live.test.ts b/tests/integrations/claudeAgentSdk.live.test.ts new file mode 100644 index 0000000..0062764 --- /dev/null +++ b/tests/integrations/claudeAgentSdk.live.test.ts @@ -0,0 +1,60 @@ +/** + * Live integration test for the Claude Agent SDK Openlayer wrapper. + * + * Skipped unless ``ANTHROPIC_API_KEY`` is set in the environment. When run, + * it exercises the full real-world path: the actual ``@anthropic-ai/claude-agent-sdk`` + * (which boots its bundled Claude Code subprocess), the actual Openlayer + * publish path, and the entire wrapper end-to-end. + * + * Env it expects: + * ANTHROPIC_API_KEY — required to enable the test + * OPENLAYER_API_KEY — Openlayer ingest key + * OPENLAYER_INFERENCE_PIPELINE_ID — destination pipeline + */ + +import { tracedQuery } from '../../src/lib/integrations/claudeAgentSdk'; + +const itLive = process.env['ANTHROPIC_API_KEY'] ? it : it.skip; + +describe('claudeAgentSdk live integration', () => { + itLive( + 'produces a valid trace for a one-turn query against claude-haiku-4-5', + async () => { + // Defaults — only the API key is required from the caller; everything + // else has a sensible value for the project's test pipeline. + process.env['OPENLAYER_INFERENCE_PIPELINE_ID'] ??= 'cb47e4f7-15a0-4e70-bd6e-7b1b4b54e434'; + // Don't disable publish — this test wants to publish. + delete process.env['OPENLAYER_DISABLE_PUBLISH']; + + const messages: any[] = []; + for await (const m of tracedQuery({ + prompt: "Say the word 'banana' and nothing else.", + options: { + model: 'claude-haiku-4-5', + allowedTools: [], + systemPrompt: + 'You are a terse assistant that follows instructions exactly. ' + + 'Never add filler words, never apologize, and never add quotes ' + + 'around your answer.', + maxTurns: 2, + }, + })) { + messages.push(m); + } + + // Must terminate with a result message. + const final = messages.find((m: any) => m.type === 'result'); + expect(final).toBeDefined(); + expect(final.subtype).toBe('success'); + // And the response must contain the word we asked for. + expect(String(final.result ?? '').toLowerCase()).toContain('banana'); + + // The tracer publishes the trace via a fire-and-forget `.then()` after + // the root step ends. Give it a beat to flush before Jest tears down, + // otherwise late `console.debug` from the publish callback trips Jest's + // "Cannot log after tests are done" guard and the run exits non-zero. + await new Promise((resolve) => setTimeout(resolve, 3000)); + }, + 120_000, + ); +}); diff --git a/tests/integrations/claudeAgentSdk.test.ts b/tests/integrations/claudeAgentSdk.test.ts new file mode 100644 index 0000000..0c9c435 --- /dev/null +++ b/tests/integrations/claudeAgentSdk.test.ts @@ -0,0 +1,655 @@ +/** + * Tests for the @anthropic-ai/claude-agent-sdk Openlayer integration. + * + * The unit cases never reach the network: they mock the SDK module via + * jest's virtual-mock support and disable Openlayer trace publishing so + * no client is constructed. Trace shape is asserted by snapshotting the + * tracer's in-memory ``Trace`` object after the wrapper finishes. + * + * Live tests live alongside in ``claudeAgentSdk.live.test.ts`` and skip + * unless ``ANTHROPIC_API_KEY`` is set. + */ +import { getCurrentTrace } from '../../src/lib/tracing/tracer'; +import { + FakeTextBlock, + FakeThinkingBlock, + FakeToolUseBlock, + assistantMessage, + initSystemMessage, + makeStream, + resultMessage, +} from './claudeAgentSdkMocks'; + +// Disable Openlayer trace upload for unit tests (we assert on the in-memory +// trace, not on the publish wire). +process.env['OPENLAYER_DISABLE_PUBLISH'] = 'true'; + +// Virtual-mock the SDK so tests run without it being installed. Individual +// tests override ``query`` per-case via ``mockImplementation``. +jest.mock( + '@anthropic-ai/claude-agent-sdk', + () => ({ + query: jest.fn(), + }), + { virtual: true }, +); + +describe('claudeAgentSdk integration', () => { + beforeEach(() => { + jest.clearAllMocks(); + // Reset the integration's cached SDK reference so each test re-resolves + // the (potentially) re-mocked module. + + const mod = require('../../src/lib/integrations/claudeAgentSdk'); + mod._resetUnderlyingQueryForTesting(); + // Reset the (virtually-mocked) SDK's ``query`` to a fresh jest mock so + // any test that called ``traceClaudeAgentSdk()`` doesn't leak a patched + // function into subsequent tests. + + const sdk = require('@anthropic-ai/claude-agent-sdk'); + sdk.query = jest.fn(); + }); + + it('module imports cleanly even without the SDK installed', () => { + const mod = require('../../src/lib/integrations/claudeAgentSdk'); + expect(mod).toBeDefined(); + expect(typeof mod.tracedQuery).toBe('function'); + }); + + it('tracedQuery emits a root AGENT step with cost/tokens/session_id from ResultMessage', async () => { + const { query: mockedQuery } = require('@anthropic-ai/claude-agent-sdk'); + (mockedQuery as jest.Mock).mockImplementation(() => + makeStream([ + initSystemMessage({ session_id: 's1', model: 'claude-opus-4-7' }), + resultMessage({ + session_id: 's1', + total_cost_usd: 0.0042, + duration_ms: 1500, + num_turns: 1, + result: 'Hello back', + stop_reason: 'end_turn', + usage: { + input_tokens: 10, + output_tokens: 5, + cache_read_input_tokens: 0, + cache_creation_input_tokens: 0, + }, + }), + ]), + ); + + const { tracedQuery } = require('../../src/lib/integrations/claudeAgentSdk'); + const forwarded: any[] = []; + for await (const m of tracedQuery({ prompt: 'hi' })) { + forwarded.push(m); + } + + // The wrapper must be a pure observer: all SDK messages flow through. + expect(mockedQuery).toHaveBeenCalledTimes(1); + expect(forwarded).toHaveLength(2); + expect(forwarded[0].type).toBe('system'); + expect(forwarded[1].type).toBe('result'); + + // Assert trace shape. + const trace = getCurrentTrace(); + expect(trace).not.toBeNull(); + expect(trace!.steps).toHaveLength(1); + const root: any = trace!.steps[0]; + expect(root.stepType).toBe('agent'); + expect(root.name).toBe('Claude Agent SDK query'); + expect(root.inputs).toEqual({ prompt: 'hi' }); + expect(root.output).toBe('Hello back'); + expect(root.metadata.session_id).toBe('s1'); + expect(root.metadata.num_turns).toBe(1); + expect(root.metadata.stop_reason).toBe('end_turn'); + expect(root.metadata.subtype).toBe('success'); + expect(root.metadata.agent_config.model).toBe('claude-opus-4-7'); + expect(root.metadata.agent_config.tools).toEqual(['Read', 'Bash']); + // Root step props (set via .log → key-name match). + expect((root as any).cost).toBeCloseTo(0.0042); + expect((root as any).tokens).toBe(15); + expect(root.latency).toBe(1500); + }); + + it('captures options.systemPrompt and options.agents on the root step metadata', async () => { + const { query: mockedQuery } = require('@anthropic-ai/claude-agent-sdk'); + (mockedQuery as jest.Mock).mockImplementation(() => + makeStream([initSystemMessage({ session_id: 's1' }), resultMessage({ session_id: 's1' })]), + ); + + const { tracedQuery } = require('../../src/lib/integrations/claudeAgentSdk'); + const userOptions = { + systemPrompt: 'You are a banana expert.', + model: 'claude-haiku-4-5', + maxTurns: 3, + allowedTools: ['Read', 'Bash'], + agents: { + 'code-reviewer': { + description: 'Reviews code for bugs', + prompt: 'You are a strict reviewer. Flag anti-patterns.', + tools: ['Read', 'Grep'], + }, + }, + }; + + for await (const _ of tracedQuery({ prompt: 'hi', options: userOptions })) { + // drain + } + + const trace = getCurrentTrace(); + const root: any = trace!.steps[trace!.steps.length - 1]; + expect(root.metadata.system_prompt).toBe('You are a banana expert.'); + expect(root.metadata.agents_defined).toBeDefined(); + expect(root.metadata.agents_defined['code-reviewer']).toEqual({ + description: 'Reviews code for bugs', + prompt: 'You are a strict reviewer. Flag anti-patterns.', + tools: ['Read', 'Grep'], + model: undefined, + }); + expect(root.metadata.options.model).toBe('claude-haiku-4-5'); + expect(root.metadata.options.maxTurns).toBe(3); + expect(root.metadata.options.allowedTools).toEqual(['Read', 'Bash']); + }); + + it('captures each AssistantMessage as a nested CHAT_COMPLETION step', async () => { + const { query: mockedQuery } = require('@anthropic-ai/claude-agent-sdk'); + (mockedQuery as jest.Mock).mockImplementation(() => + makeStream([ + initSystemMessage(), + assistantMessage( + [ + new FakeThinkingBlock('planning...'), + new FakeTextBlock('answer turn 1'), + new FakeToolUseBlock('tu-1', 'Bash', { command: 'ls' }), + ], + { + message: { + content: [ + new FakeThinkingBlock('planning...'), + new FakeTextBlock('answer turn 1'), + new FakeToolUseBlock('tu-1', 'Bash', { command: 'ls' }), + ], + model: 'claude-opus-4-7', + usage: { + input_tokens: 12, + output_tokens: 4, + cache_read_input_tokens: 0, + cache_creation_input_tokens: 0, + }, + stop_reason: 'tool_use', + }, + }, + ), + assistantMessage([new FakeTextBlock('done')]), + resultMessage({}), + ]), + ); + + const { tracedQuery } = require('../../src/lib/integrations/claudeAgentSdk'); + for await (const _ of tracedQuery({ prompt: 'do stuff' })) { + void _; + } + + const trace = getCurrentTrace(); + const root: any = trace!.steps[0]; + const turns = root.steps.filter((s: any) => s.stepType === 'chat_completion'); + expect(turns).toHaveLength(2); + + const turn1: any = turns[0]; + expect(turn1.name).toBe('assistant turn 1'); + expect(turn1.output).toContain('answer turn 1'); + expect(turn1.provider).toBe('anthropic'); + expect(turn1.model).toBe('claude-opus-4-7'); + expect(turn1.promptTokens).toBe(12); + expect(turn1.completionTokens).toBe(4); + expect(turn1.tokens).toBe(16); + expect(turn1.metadata.thinking).toContain('planning'); + expect(turn1.metadata.tool_calls).toEqual([ + { id: 'tu-1', name: 'Bash', input: { command: 'ls' } }, + ]); + expect(turn1.metadata.stop_reason).toBe('tool_use'); + + const turn2: any = turns[1]; + expect(turn2.name).toBe('assistant turn 2'); + expect(turn2.output).toBe('done'); + }); + + it('captures tool calls via PreToolUse/PostToolUse hooks (TOOL step with input/output/latency)', async () => { + const { query: mockedQuery } = require('@anthropic-ai/claude-agent-sdk'); + + // The mocked SDK simulates: stream init -> assistant turn with tool_use -> + // fire PreToolUse(Bash) -> stream UserMessage(tool_result) -> fire + // PostToolUse(Bash) -> stream final assistant turn -> ResultMessage. + (mockedQuery as jest.Mock).mockImplementation(async function* (opts: any) { + const hooks = opts.options.hooks; + const pre = hooks.PreToolUse[hooks.PreToolUse.length - 1].hooks[0]; + const post = hooks.PostToolUse[hooks.PostToolUse.length - 1].hooks[0]; + + yield initSystemMessage({ session_id: 's-tool' }); + yield assistantMessage( + [new FakeTextBlock('Running...'), new FakeToolUseBlock('tu-bash-1', 'Bash', { command: 'ls' })], + { + message: { + content: [ + new FakeTextBlock('Running...'), + new FakeToolUseBlock('tu-bash-1', 'Bash', { command: 'ls' }), + ], + model: 'claude-opus-4-7', + usage: { + input_tokens: 1, + output_tokens: 1, + cache_read_input_tokens: 0, + cache_creation_input_tokens: 0, + }, + stop_reason: 'tool_use', + }, + }, + ); + await pre({ tool_name: 'Bash', tool_input: { command: 'ls' } }, 'tu-bash-1', {}); + yield { + type: 'user', + message: { + content: [{ type: 'tool_result', tool_use_id: 'tu-bash-1', content: 'file1.txt\nfile2.txt' }], + }, + }; + await post( + { + tool_name: 'Bash', + tool_input: { command: 'ls' }, + tool_response: 'file1.txt\nfile2.txt', + }, + 'tu-bash-1', + {}, + ); + yield assistantMessage([new FakeTextBlock('Done')]); + yield resultMessage({}); + }); + + const { tracedQuery } = require('../../src/lib/integrations/claudeAgentSdk'); + for await (const _ of tracedQuery({ prompt: 'run ls' })) { + void _; + } + + const trace = getCurrentTrace(); + const root: any = trace!.steps[0]; + const tools = root.steps.filter((s: any) => s.stepType === 'tool'); + expect(tools).toHaveLength(1); + const tool: any = tools[0]; + expect(tool.name).toBe('Bash'); + expect(tool.inputs).toEqual({ command: 'ls' }); + expect(tool.output).toBe('file1.txt\nfile2.txt'); + expect(tool.metadata.tool_use_id).toBe('tu-bash-1'); + expect(tool.metadata.is_error).toBe(false); + expect(typeof tool.metadata.latency_ms).toBe('number'); + }); + + it('records is_error=true and the error payload when PostToolUseFailure fires', async () => { + const { query: mockedQuery } = require('@anthropic-ai/claude-agent-sdk'); + (mockedQuery as jest.Mock).mockImplementation(async function* (opts: any) { + const hooks = opts.options.hooks; + const pre = hooks.PreToolUse[hooks.PreToolUse.length - 1].hooks[0]; + const fail = hooks.PostToolUseFailure[hooks.PostToolUseFailure.length - 1].hooks[0]; + + yield initSystemMessage(); + await pre({ tool_name: 'Bash', tool_input: { command: 'rm /' } }, 'tu-err-1', {}); + await fail({ tool_name: 'Bash', error: 'permission denied' }, 'tu-err-1', {}); + yield resultMessage({}); + }); + + const { tracedQuery } = require('../../src/lib/integrations/claudeAgentSdk'); + for await (const _ of tracedQuery({ prompt: 'rm /' })) void _; + + const root: any = getCurrentTrace()!.steps[0]; + const tools = root.steps.filter((s: any) => s.stepType === 'tool'); + expect(tools).toHaveLength(1); + expect(tools[0].metadata.is_error).toBe(true); + expect(tools[0].output).toBe('permission denied'); + }); + + it('parses mcp____ names into mcp_server / mcp_tool_name metadata', async () => { + const { query: mockedQuery } = require('@anthropic-ai/claude-agent-sdk'); + (mockedQuery as jest.Mock).mockImplementation(async function* (opts: any) { + const hooks = opts.options.hooks; + const pre = hooks.PreToolUse[hooks.PreToolUse.length - 1].hooks[0]; + const post = hooks.PostToolUse[hooks.PostToolUse.length - 1].hooks[0]; + + yield initSystemMessage(); + await pre( + { tool_name: 'mcp__playwright__browser_navigate', tool_input: { url: 'https://example.com' } }, + 'tu-mcp-1', + {}, + ); + await post( + { + tool_name: 'mcp__playwright__browser_navigate', + tool_input: { url: 'https://example.com' }, + tool_response: 'navigated', + }, + 'tu-mcp-1', + {}, + ); + yield resultMessage({}); + }); + + const { tracedQuery } = require('../../src/lib/integrations/claudeAgentSdk'); + for await (const _ of tracedQuery({ prompt: 'browse' })) void _; + + const root: any = getCurrentTrace()!.steps[0]; + const tool = root.steps.find((s: any) => s.stepType === 'tool'); + expect(tool.name).toBe('mcp__playwright__browser_navigate'); + expect(tool.metadata.mcp_server).toBe('playwright'); + expect(tool.metadata.mcp_tool_name).toBe('browser_navigate'); + }); + + it('subagent assistant turns nest under the spawning Agent ToolStep via parent_tool_use_id', async () => { + const { query: mockedQuery } = require('@anthropic-ai/claude-agent-sdk'); + (mockedQuery as jest.Mock).mockImplementation(async function* (opts: any) { + const hooks = opts.options.hooks; + const pre = hooks.PreToolUse[hooks.PreToolUse.length - 1].hooks[0]; + const post = hooks.PostToolUse[hooks.PostToolUse.length - 1].hooks[0]; + + yield initSystemMessage(); + // Top-level assistant turn delegates to a subagent via the Agent tool. + yield assistantMessage([new FakeToolUseBlock('agent-tu-1', 'Agent', { description: 'review code' })], { + message: { + content: [new FakeToolUseBlock('agent-tu-1', 'Agent', { description: 'review code' })], + model: 'claude-opus-4-7', + usage: { + input_tokens: 1, + output_tokens: 1, + cache_read_input_tokens: 0, + cache_creation_input_tokens: 0, + }, + stop_reason: 'tool_use', + }, + }); + // PreToolUse(Agent) opens the Agent tool step. Subagent's internal + // stream now arrives with ``parent_tool_use_id`` set. + await pre({ tool_name: 'Agent', tool_input: { description: 'review code' } }, 'agent-tu-1', {}); + yield assistantMessage([new FakeTextBlock('subagent thinking')], { + parent_tool_use_id: 'agent-tu-1', + }); + yield assistantMessage([new FakeTextBlock('subagent done')], { + parent_tool_use_id: 'agent-tu-1', + }); + await post( + { tool_name: 'Agent', tool_input: { description: 'review code' }, tool_response: 'subagent done' }, + 'agent-tu-1', + {}, + ); + yield resultMessage({}); + }); + + const { tracedQuery } = require('../../src/lib/integrations/claudeAgentSdk'); + for await (const _ of tracedQuery({ prompt: 'review' })) void _; + + const root: any = getCurrentTrace()!.steps[0]; + // root should have: assistant turn 1, Agent tool step + const agentTool = root.steps.find((s: any) => s.stepType === 'tool' && s.name === 'Agent'); + expect(agentTool).toBeDefined(); + // Subagent assistant turns should be nested under the Agent tool step. + const nestedChats = agentTool.steps.filter((s: any) => s.stepType === 'chat_completion'); + expect(nestedChats).toHaveLength(2); + expect(nestedChats[0].metadata.parent_tool_use_id).toBe('agent-tu-1'); + expect(nestedChats[1].metadata.parent_tool_use_id).toBe('agent-tu-1'); + // And those subagent turns should NOT also appear under root. + const rootChatNames = root.steps + .filter((s: any) => s.stepType === 'chat_completion') + .map((s: any) => s.name); + // Root has just the initial assistant turn 1. + expect(rootChatNames).toEqual(['assistant turn 1']); + }); + + it('captures error_max_turns subtype on the root step metadata', async () => { + const { query: mockedQuery } = require('@anthropic-ai/claude-agent-sdk'); + (mockedQuery as jest.Mock).mockImplementation(() => + makeStream([ + initSystemMessage(), + resultMessage({ subtype: 'error_max_turns', is_error: true, result: null }), + ]), + ); + + const { tracedQuery } = require('../../src/lib/integrations/claudeAgentSdk'); + for await (const _ of tracedQuery({ prompt: 'forever' })) void _; + + const root: any = getCurrentTrace()!.steps[0]; + expect(root.metadata.subtype).toBe('error_max_turns'); + expect(root.metadata.is_error).toBe(true); + }); + + it('composes with user-provided hooks rather than replacing them', async () => { + const userPreCalls: Array<{ input: any; toolUseID: string | undefined }> = []; + const userPreHook = jest.fn(async (input: any, toolUseID: string | undefined) => { + userPreCalls.push({ input, toolUseID }); + // User hook returns a deny decision — confirms we never clobber it. + return { hookSpecificOutput: { permissionDecision: 'deny', permissionDecisionReason: 'test' } }; + }); + + const { query: mockedQuery } = require('@anthropic-ai/claude-agent-sdk'); + (mockedQuery as jest.Mock).mockImplementation(async function* (opts: any) { + const userMatchers = opts.options.hooks.PreToolUse; + // Run every matcher's hook(s) — that's what the SDK does in practice. + for (const matcher of userMatchers) { + for (const fn of matcher.hooks) { + await fn({ tool_name: 'Bash', tool_input: { command: 'ls' } }, 'tu-comp-1', {}); + } + } + // PostToolUse: only run our internal one (user didn't provide one). + const postMatchers = opts.options.hooks.PostToolUse; + for (const matcher of postMatchers) { + for (const fn of matcher.hooks) { + await fn( + { tool_name: 'Bash', tool_input: { command: 'ls' }, tool_response: 'ok' }, + 'tu-comp-1', + {}, + ); + } + } + yield initSystemMessage(); + yield resultMessage({}); + }); + + const { tracedQuery } = require('../../src/lib/integrations/claudeAgentSdk'); + for await (const _ of tracedQuery({ + prompt: 'compose', + options: { + hooks: { + PreToolUse: [{ hooks: [userPreHook] }], + }, + }, + })) { + void _; + } + + // The user's PreToolUse hook still fired. + expect(userPreHook).toHaveBeenCalledTimes(1); + expect(userPreCalls).toHaveLength(1); + // And the Openlayer tool step was captured alongside it. + const root: any = getCurrentTrace()!.steps[0]; + const tool = root.steps.find((s: any) => s.stepType === 'tool'); + expect(tool).toBeDefined(); + expect(tool.name).toBe('Bash'); + }); + + it('redacts env / headers / authorization from mcp_servers in metadata', async () => { + const { query: mockedQuery } = require('@anthropic-ai/claude-agent-sdk'); + (mockedQuery as jest.Mock).mockImplementation(() => + makeStream([ + initSystemMessage({ + mcp_servers: [ + { + name: 'playwright', + status: 'connected', + transport: 'stdio', + env: { API_KEY: 'sk-secret', OTHER: 'also-secret' }, + headers: { Authorization: 'Bearer secret' }, + authorization: 'Bearer secret', + command: 'mcp-playwright', + }, + { + name: 'github', + status: 'connected', + transport: 'http', + url: 'https://mcp.example.com', + env: { GITHUB_TOKEN: 'ghp_secret' }, + }, + ], + }), + resultMessage({}), + ]), + ); + + const { tracedQuery } = require('../../src/lib/integrations/claudeAgentSdk'); + for await (const _ of tracedQuery({ prompt: 'mcp' })) void _; + + const root: any = getCurrentTrace()!.steps[0]; + const servers = root.metadata.agent_config.mcp_servers; + expect(servers).toHaveLength(2); + for (const srv of servers) { + expect(srv).not.toHaveProperty('env'); + expect(srv).not.toHaveProperty('headers'); + expect(srv).not.toHaveProperty('authorization'); + } + // Non-sensitive fields are preserved. + expect(servers[0].name).toBe('playwright'); + expect(servers[0].status).toBe('connected'); + expect(servers[0].transport).toBe('stdio'); + expect(servers[1].url).toBe('https://mcp.example.com'); + }); + + it('traceClaudeAgentSdk patches the SDK query symbol (idempotent)', () => { + const sdk = require('@anthropic-ai/claude-agent-sdk'); + // Reset to a fresh jest mock so we test patching from scratch. + sdk.query = jest.fn(); + const original = sdk.query; + + const { traceClaudeAgentSdk } = require('../../src/lib/integrations/claudeAgentSdk'); + traceClaudeAgentSdk({ inferencePipelineId: 'test-pipeline' }); + + expect(sdk.query).not.toBe(original); + expect((sdk.query as any)._openlayerPatched).toBe(true); + expect((sdk.query as any)._openlayerOriginal).toBe(original); + + // Idempotent — second call doesn't re-wrap. + const firstPatched = sdk.query; + traceClaudeAgentSdk(); + expect(sdk.query).toBe(firstPatched); + }); + + it('exposes `query` as a drop-in export', () => { + const { query: ourQuery } = require('../../src/lib/integrations/claudeAgentSdk'); + expect(typeof ourQuery).toBe('function'); + }); + + it('forwards every SDK message unchanged and in order (passthrough invariant)', async () => { + const messages = [ + initSystemMessage({ session_id: 'p1' }), + { type: 'assistant', message: { content: [new FakeTextBlock('hi')] } }, + resultMessage({ session_id: 'p1' }), + ]; + + const { query: mockedQuery } = require('@anthropic-ai/claude-agent-sdk'); + (mockedQuery as jest.Mock).mockImplementation(() => makeStream(messages)); + + const { tracedQuery } = require('../../src/lib/integrations/claudeAgentSdk'); + const out: any[] = []; + for await (const m of tracedQuery({ prompt: 'hi' })) out.push(m); + expect(out).toHaveLength(3); + // Same references, in identical order. + for (let i = 0; i < messages.length; i++) { + expect(out[i]).toBe(messages[i]); + } + }); + + it('traceClaudeAgentSdk patches ClaudeSDKClient.prototype when present', async () => { + // Build a minimal fake ClaudeSDKClient and re-attach it onto the + // virtually-mocked SDK module before calling ``traceClaudeAgentSdk``. + class FakeClient { + public options: any; + public __openlayerLastPrompt: any; + constructor(opts: any = {}) { + this.options = opts; + } + async query(prompt: any) { + this.__openlayerLastPrompt = prompt; + return undefined; + } + // The SDK exposes ``receive_response`` (snake) as the async iterator + // the caller awaits to get the message stream. + async *receive_response() { + yield initSystemMessage({ session_id: 'client-1' }); + yield resultMessage({ session_id: 'client-1' }); + } + } + + const sdk = require('@anthropic-ai/claude-agent-sdk'); + sdk.query = jest.fn(); + sdk.ClaudeSDKClient = FakeClient; + + const { traceClaudeAgentSdk } = require('../../src/lib/integrations/claudeAgentSdk'); + traceClaudeAgentSdk(); + + expect((FakeClient.prototype as any)._openlayerPatched).toBe(true); + + const client = new FakeClient({ hooks: {} }); + await client.query('hello from client'); + const out: any[] = []; + for await (const m of client.receive_response()) out.push(m); + expect(out).toHaveLength(2); + + // The patched receive_response should have created a fresh AGENT trace. + const trace = getCurrentTrace(); + const root: any = trace!.steps[trace!.steps.length - 1]; + expect(root.stepType).toBe('agent'); + expect(root.name).toBe('Claude Agent SDK query'); + expect(root.metadata.session_id).toBe('client-1'); + }); + + it('passthrough invariant: wrapper neither swallows messages nor mutates them, even with tools', async () => { + // The wrapper must be a pure observer: every yielded message is the same + // object reference the SDK produced, in the same order. We construct a + // realistic mixed stream (system + assistant with tool_use + user + // tool_result + final assistant + result) and check identity for each. + const initMsg = initSystemMessage({ session_id: 'passthru' }); + const turn1Content = [ + new FakeTextBlock('thinking'), + new FakeToolUseBlock('tu-passthru', 'Bash', { cmd: 'pwd' }), + ]; + const assistantTurn1 = assistantMessage(turn1Content, { + message: { content: turn1Content, model: 'm', usage: {} }, + }); + const toolResultMsg = { + type: 'user', + message: { content: [{ type: 'tool_result', tool_use_id: 'tu-passthru', content: '/tmp' }] }, + parent_tool_use_id: null, + }; + const assistantTurn2 = assistantMessage([new FakeTextBlock('done')]); + const finalMsg = resultMessage({ session_id: 'passthru' }); + + const all = [initMsg, assistantTurn1, toolResultMsg, assistantTurn2, finalMsg]; + + const { query: mockedQuery } = require('@anthropic-ai/claude-agent-sdk'); + (mockedQuery as jest.Mock).mockImplementation(async function* (opts: any) { + const hooks = opts.options.hooks; + const pre = hooks.PreToolUse[hooks.PreToolUse.length - 1].hooks[0]; + const post = hooks.PostToolUse[hooks.PostToolUse.length - 1].hooks[0]; + yield initMsg; + yield assistantTurn1; + await pre({ tool_name: 'Bash', tool_input: { cmd: 'pwd' } }, 'tu-passthru', {}); + yield toolResultMsg; + await post({ tool_name: 'Bash', tool_response: '/tmp' }, 'tu-passthru', {}); + yield assistantTurn2; + yield finalMsg; + }); + + const { tracedQuery } = require('../../src/lib/integrations/claudeAgentSdk'); + const seen: any[] = []; + for await (const m of tracedQuery({ prompt: 'passthru' })) seen.push(m); + + expect(seen).toHaveLength(all.length); + for (let i = 0; i < all.length; i++) { + // Identity, not just equality. + expect(seen[i]).toBe(all[i]); + } + }); +}); diff --git a/tests/integrations/claudeAgentSdkMocks.ts b/tests/integrations/claudeAgentSdkMocks.ts new file mode 100644 index 0000000..be242d2 --- /dev/null +++ b/tests/integrations/claudeAgentSdkMocks.ts @@ -0,0 +1,122 @@ +/** + * Test helpers for the @anthropic-ai/claude-agent-sdk integration. + * + * These mocks mirror the SDK's message shapes closely enough that the + * integration's message dispatchers cannot tell them apart. We use plain + * objects with a discriminating ``type`` field — the SDK uses tagged + * unions, not nominal classes, so structural typing is sufficient. + */ + +export class FakeTextBlock { + public readonly type = 'text' as const; + constructor(public text: string) {} +} + +export class FakeThinkingBlock { + public readonly type = 'thinking' as const; + constructor( + public thinking: string, + public signature: string = 'sig', + ) {} +} + +export class FakeToolUseBlock { + public readonly type = 'tool_use' as const; + constructor( + public id: string, + public name: string, + public input: any, + ) {} +} + +export class FakeToolResultBlock { + public readonly type = 'tool_result' as const; + constructor( + public tool_use_id: string, + public content: any, + public is_error?: boolean, + ) {} +} + +export const initSystemMessage = (overrides: Partial> = {}): Record => ({ + type: 'system' as const, + subtype: 'init' as const, + session_id: 'sess_test', + uuid: 'u-init', + model: 'claude-opus-4-7', + tools: ['Read', 'Bash'], + mcp_servers: [], + skills: [], + slash_commands: [], + plugins: [], + permissionMode: 'default', + cwd: '/tmp', + claude_code_version: 'test-0.2.139', + apiKeySource: 'ANTHROPIC_API_KEY', + output_style: 'default', + ...overrides, +}); + +export const assistantMessage = ( + content: any[], + overrides: Partial> = {}, +): Record => ({ + type: 'assistant' as const, + uuid: 'u-asst', + session_id: 'sess_test', + message: { + content, + model: 'claude-opus-4-7', + usage: { + input_tokens: 10, + output_tokens: 5, + cache_read_input_tokens: 0, + cache_creation_input_tokens: 0, + }, + stop_reason: 'end_turn', + ...((overrides['message'] as Record) || {}), + }, + parent_tool_use_id: null, + ...overrides, +}); + +export const userMessage = ( + content: any[], + overrides: Partial> = {}, +): Record => ({ + type: 'user' as const, + uuid: 'u-user', + session_id: 'sess_test', + message: { content }, + parent_tool_use_id: null, + ...overrides, +}); + +export const resultMessage = (overrides: Partial> = {}): Record => ({ + type: 'result' as const, + subtype: 'success' as const, + uuid: 'u-result', + session_id: 'sess_test', + duration_ms: 1000, + duration_api_ms: 800, + is_error: false, + num_turns: 1, + result: 'Done', + stop_reason: 'end_turn', + total_cost_usd: 0.001, + usage: { + input_tokens: 100, + output_tokens: 50, + cache_read_input_tokens: 0, + cache_creation_input_tokens: 0, + }, + modelUsage: { 'claude-opus-4-7': { inputTokens: 100, outputTokens: 50, costUSD: 0.001 } }, + permission_denials: [], + ...overrides, +}); + +/** Convert a fixed array of SDK messages to an async iterable shaped like + * the SDK's ``query()`` return value. */ +export async function* makeStream(messages: T[]): AsyncGenerator { + for (const m of messages) yield m; +} diff --git a/yarn.lock b/yarn.lock index 530cc87..e42f98e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12,6 +12,70 @@ resolved "https://registry.yarnpkg.com/@andrewbranch/untar.js/-/untar.js-1.0.3.tgz#ba9494f85eb83017c5c855763969caf1d0adea00" integrity sha512-Jh15/qVmrLGhkKJBdXlK1+9tY4lZruYjsgkDFj08ZmDiWVBLJcqkok7Z0/R0In+i1rScBpJlSvrTS2Lm41Pbnw== +"@anthropic-ai/claude-agent-sdk-darwin-arm64@0.2.139": + version "0.2.139" + resolved "https://registry.yarnpkg.com/@anthropic-ai/claude-agent-sdk-darwin-arm64/-/claude-agent-sdk-darwin-arm64-0.2.139.tgz#3568f7439c702e470290cd9f362113745d1a5b0c" + integrity sha512-dnuO2E0x6o9GAk9iZZKlEd10h+0PQFdTfr5aQU4I0W+0ReKsFEoE9LAqfomS2EvLUQ9L62X0+n0iyZQmAVi1kw== + +"@anthropic-ai/claude-agent-sdk-darwin-x64@0.2.139": + version "0.2.139" + resolved "https://registry.yarnpkg.com/@anthropic-ai/claude-agent-sdk-darwin-x64/-/claude-agent-sdk-darwin-x64-0.2.139.tgz#01b030f1996ce341ef9d89c9fb6aa3f3d2cc3348" + integrity sha512-SXyldBIwpMHDXppPGObXZ1wjSSWf/YPgD6vK4nssIXarC/DtMRnAQ419Hb3q5MaBB29vSjOPKmG0MOkMltFR/A== + +"@anthropic-ai/claude-agent-sdk-linux-arm64-musl@0.2.139": + version "0.2.139" + resolved "https://registry.yarnpkg.com/@anthropic-ai/claude-agent-sdk-linux-arm64-musl/-/claude-agent-sdk-linux-arm64-musl-0.2.139.tgz#91e64f04e607f4549c731d63635ac7d61f664f7a" + integrity sha512-gzMfit9t7Fiy5taZ+miAaP8ZmOMc+hv8Ov3UOXGwJunK6H+0F88ctBSnolDPMPQaS6s2WoMD0o8fhUbBudtMVw== + +"@anthropic-ai/claude-agent-sdk-linux-arm64@0.2.139": + version "0.2.139" + resolved "https://registry.yarnpkg.com/@anthropic-ai/claude-agent-sdk-linux-arm64/-/claude-agent-sdk-linux-arm64-0.2.139.tgz#456767af17e51c41d8d2ccd4c3785300e8aec8bf" + integrity sha512-qfnQ4SjEcq//iGAJkk25J6j4Tq+dvQe9wHks0dcaSdGOs2D96Teqrb358YJe+nke2DBKVUa9Y4ComW3aUBM29w== + +"@anthropic-ai/claude-agent-sdk-linux-x64-musl@0.2.139": + version "0.2.139" + resolved "https://registry.yarnpkg.com/@anthropic-ai/claude-agent-sdk-linux-x64-musl/-/claude-agent-sdk-linux-x64-musl-0.2.139.tgz#0000dbd4f098671c9f4a87e587449863aa2068b7" + integrity sha512-Fg/aQs1vdyqLrNXqGa1i7/ODpGxP6ud/K/2AgVarLteg2Z3ZnrHPvPQ6iQmTGI8+BhxAZ141t4Dg0CWz3CoqCQ== + +"@anthropic-ai/claude-agent-sdk-linux-x64@0.2.139": + version "0.2.139" + resolved "https://registry.yarnpkg.com/@anthropic-ai/claude-agent-sdk-linux-x64/-/claude-agent-sdk-linux-x64-0.2.139.tgz#8441fc2a99cc9bdfeec1ccd8bc01a130017ff05f" + integrity sha512-2Gqy5hV/MyObbwSyNhj5ha2cY5EZnUfDLvpEwR1eeOaU1yqnxzsdNzXWgHIyWQGKGNE2ICwgLYtt6AtOJGWpPg== + +"@anthropic-ai/claude-agent-sdk-win32-arm64@0.2.139": + version "0.2.139" + resolved "https://registry.yarnpkg.com/@anthropic-ai/claude-agent-sdk-win32-arm64/-/claude-agent-sdk-win32-arm64-0.2.139.tgz#bfc4d512075754005134f7820e73d23131c1c563" + integrity sha512-HusAU/gSQ0G0AHU+Hj/ps0Tl5JaUF2nxkp+G42tU6hpnwLMOQMdLx/yqvSQnz4WSxggxiDFmYvMDLYAmuE9Qdg== + +"@anthropic-ai/claude-agent-sdk-win32-x64@0.2.139": + version "0.2.139" + resolved "https://registry.yarnpkg.com/@anthropic-ai/claude-agent-sdk-win32-x64/-/claude-agent-sdk-win32-x64-0.2.139.tgz#0cb078019eb18ce1314884101e7cbc77375bbca5" + integrity sha512-eJjbtLvEBJcTrl4WJhmhP7FYdTVvx/XtioifH7OEnCoxQozMHhOmA0X90csplIRpttX+jX2PqnE5j2FwU20eCw== + +"@anthropic-ai/claude-agent-sdk@^0.2.111": + version "0.2.139" + resolved "https://registry.yarnpkg.com/@anthropic-ai/claude-agent-sdk/-/claude-agent-sdk-0.2.139.tgz#3dd7051485abed23408a377952a71bd110222837" + integrity sha512-9zmitYoxCQiQZsTUbm9IGC6VyZt70J3NLtkRQPQvFVfz7bKDrhlZZKzXmyl2XmqedXEIeQy2ACmwdjwzPIVIAw== + dependencies: + "@anthropic-ai/sdk" "^0.81.0" + "@modelcontextprotocol/sdk" "^1.29.0" + optionalDependencies: + "@anthropic-ai/claude-agent-sdk-darwin-arm64" "0.2.139" + "@anthropic-ai/claude-agent-sdk-darwin-x64" "0.2.139" + "@anthropic-ai/claude-agent-sdk-linux-arm64" "0.2.139" + "@anthropic-ai/claude-agent-sdk-linux-arm64-musl" "0.2.139" + "@anthropic-ai/claude-agent-sdk-linux-x64" "0.2.139" + "@anthropic-ai/claude-agent-sdk-linux-x64-musl" "0.2.139" + "@anthropic-ai/claude-agent-sdk-win32-arm64" "0.2.139" + "@anthropic-ai/claude-agent-sdk-win32-x64" "0.2.139" + +"@anthropic-ai/sdk@^0.81.0": + version "0.81.0" + resolved "https://registry.yarnpkg.com/@anthropic-ai/sdk/-/sdk-0.81.0.tgz#9c17d5796082b38780720c2ff02c755ec9ddae45" + integrity sha512-D4K5PvEV6wPiRtVlVsJHIUhHAmOZ6IT/I9rKlTf84gR7GyyAurPJK7z9BOf/AZqC5d1DhYQGJNKRmV+q8dGhgw== + dependencies: + json-schema-to-ts "^3.1.1" + "@arethetypeswrong/cli@^0.17.0": version "0.17.0" resolved "https://registry.yarnpkg.com/@arethetypeswrong/cli/-/cli-0.17.0.tgz#f97f10926b3f9f9eb5117550242d2e06c25cadac" @@ -676,6 +740,11 @@ dependencies: "@babel/helper-plugin-utils" "^7.28.6" +"@babel/runtime@^7.18.3": + version "7.29.2" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.29.2.tgz#9a6e2d05f4b6692e1801cd4fb176ad823930ed5e" + integrity sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g== + "@babel/template@^7.28.6", "@babel/template@^7.3.3": version "7.28.6" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.28.6.tgz#0e7e56ecedb78aeef66ce7972b082fce76a23e57" @@ -738,6 +807,136 @@ resolved "https://registry.yarnpkg.com/@datastructures-js/deque/-/deque-1.0.8.tgz#7ef2b655821ea24b1677ff01895a8fdb8a26d3c7" integrity sha512-PSBhJ2/SmeRPRHuBv7i/fHWIdSC3JTyq56qb+Rq0wjOagi0/fdV5/B/3Md5zFZus/W6OkSPMaxMKKMNMrSmubg== +"@esbuild/aix-ppc64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz#82b74f92aa78d720b714162939fb248c90addf53" + integrity sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg== + +"@esbuild/android-arm64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz#f78cb8a3121fc205a53285adb24972db385d185d" + integrity sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ== + +"@esbuild/android-arm@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.27.7.tgz#593e10a1450bbfcac6cb321f61f468453bac209d" + integrity sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ== + +"@esbuild/android-x64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.27.7.tgz#453143d073326033d2d22caf9e48de4bae274b07" + integrity sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg== + +"@esbuild/darwin-arm64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz#6f23000fb9b40b7e04b7d0606c0693bd0632f322" + integrity sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw== + +"@esbuild/darwin-x64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz#27393dd18bb1263c663979c5f1576e00c2d024be" + integrity sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ== + +"@esbuild/freebsd-arm64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz#22e4638fa502d1c0027077324c97640e3adf3a62" + integrity sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w== + +"@esbuild/freebsd-x64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz#9224b8e4fea924ce2194e3efc3e9aebf822192d6" + integrity sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ== + +"@esbuild/linux-arm64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz#4f5d1c27527d817b35684ae21419e57c2bda0966" + integrity sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A== + +"@esbuild/linux-arm@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz#b9e9d070c8c1c0449cf12b20eac37d70a4595921" + integrity sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA== + +"@esbuild/linux-ia32@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz#3f80fb696aa96051a94047f35c85b08b21c36f9e" + integrity sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg== + +"@esbuild/linux-loong64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz#9be1f2c28210b13ebb4156221bba356fe1675205" + integrity sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q== + +"@esbuild/linux-mips64el@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz#4ab5ee67a3dfcbcb5e8fd7883dae6e735b1163b8" + integrity sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw== + +"@esbuild/linux-ppc64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz#dac78c689f6499459c4321e5c15032c12307e7ea" + integrity sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ== + +"@esbuild/linux-riscv64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz#050f7d3b355c3a98308e935bc4d6325da91b0027" + integrity sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ== + +"@esbuild/linux-s390x@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz#d61f715ce61d43fe5844ad0d8f463f88cbe4fef6" + integrity sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw== + +"@esbuild/linux-x64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz#ca8e1aa478fc8209257bf3ac8f79c4dc2982f32a" + integrity sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA== + +"@esbuild/netbsd-arm64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz#1650f2c1b948deeb3ef948f2fc30614723c09690" + integrity sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w== + +"@esbuild/netbsd-x64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz#65772ab342c4b3319bf0705a211050aac1b6e320" + integrity sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw== + +"@esbuild/openbsd-arm64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz#37ed7cfa66549d7955852fce37d0c3de4e715ea1" + integrity sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A== + +"@esbuild/openbsd-x64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz#01bf3d385855ef50cb33db7c4b52f957c34cd179" + integrity sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg== + +"@esbuild/openharmony-arm64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz#6c1f94b34086599aabda4eac8f638294b9877410" + integrity sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw== + +"@esbuild/sunos-x64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz#4b0dd17ae0a6941d2d0fd35a906392517071a90d" + integrity sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA== + +"@esbuild/win32-arm64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz#34193ab5565d6ff68ca928ac04be75102ccb2e77" + integrity sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA== + +"@esbuild/win32-ia32@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz#eb67f0e4482515d8c1894ede631c327a4da9fc4d" + integrity sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw== + +"@esbuild/win32-x64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz#8fe30b3088b89b4873c3a6cc87597ae3920c0a8b" + integrity sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg== + "@eslint-community/eslint-utils@^4.4.0": version "4.4.0" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" @@ -836,6 +1035,11 @@ protobufjs "^7.5.3" yargs "^17.7.2" +"@hono/node-server@^1.19.9": + version "1.19.14" + resolved "https://registry.yarnpkg.com/@hono/node-server/-/node-server-1.19.14.tgz#e30f844bc77e3ce7be442aac3b1f73ad8b58d181" + integrity sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw== + "@humanfs/core@^0.19.1": version "0.19.1" resolved "https://registry.yarnpkg.com/@humanfs/core/-/core-0.19.1.tgz#17c55ca7d426733fe3c561906b8173c336b40a77" @@ -1216,6 +1420,29 @@ uuid "^10.0.0" weaviate-client "^3.5.2" +"@modelcontextprotocol/sdk@^1.29.0": + version "1.29.0" + resolved "https://registry.yarnpkg.com/@modelcontextprotocol/sdk/-/sdk-1.29.0.tgz#79786d8b525e269de850ac82b1f1f757f3915f44" + integrity sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ== + dependencies: + "@hono/node-server" "^1.19.9" + ajv "^8.17.1" + ajv-formats "^3.0.1" + content-type "^1.0.5" + cors "^2.8.5" + cross-spawn "^7.0.5" + eventsource "^3.0.2" + eventsource-parser "^3.0.0" + express "^5.2.1" + express-rate-limit "^8.2.1" + hono "^4.11.4" + jose "^6.1.3" + json-schema-typed "^8.0.2" + pkce-challenge "^5.0.0" + raw-body "^3.0.0" + zod "^3.25 || ^4.0" + zod-to-json-schema "^3.25.1" + "@nodable/entities@^2.1.0": version "2.1.0" resolved "https://registry.yarnpkg.com/@nodable/entities/-/entities-2.1.0.tgz#f543e5c6446720d4cf9e498a83019dd159973bc2" @@ -2115,6 +2342,14 @@ abort-controller@^3.0.0: dependencies: event-target-shim "^5.0.0" +accepts@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-2.0.0.tgz#bbcf4ba5075467f3f2131eab3cffc73c2f5d7895" + integrity sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng== + dependencies: + mime-types "^3.0.0" + negotiator "^1.0.0" + acorn-jsx@^5.3.2: version "5.3.2" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" @@ -2155,6 +2390,13 @@ aggregate-error@^3.0.0: clean-stack "^2.0.0" indent-string "^4.0.0" +ajv-formats@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-3.0.1.tgz#3d5dc762bca17679c3c2ea7e90ad6b7532309578" + integrity sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ== + dependencies: + ajv "^8.0.0" + ajv@^6.12.4: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" @@ -2165,6 +2407,16 @@ ajv@^6.12.4: json-schema-traverse "^0.4.1" uri-js "^4.2.2" +ajv@^8.0.0, ajv@^8.17.1: + version "8.20.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.20.0.tgz#304b3636add88ba7d936760dd50ece006dea95f9" + integrity sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA== + dependencies: + fast-deep-equal "^3.1.3" + fast-uri "^3.0.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + ansi-escapes@^4.2.1: version "4.3.2" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" @@ -2319,6 +2571,21 @@ binary-extensions@^2.2.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522" integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== +body-parser@^2.2.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-2.2.2.tgz#1a32cdb966beaf68de50a9dfbe5b58f83cb8890c" + integrity sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA== + dependencies: + bytes "^3.1.2" + content-type "^1.0.5" + debug "^4.4.3" + http-errors "^2.0.0" + iconv-lite "^0.7.0" + on-finished "^2.4.1" + qs "^6.14.1" + raw-body "^3.0.1" + type-is "^2.0.1" + bowser@^2.11.0: version "2.14.1" resolved "https://registry.yarnpkg.com/bowser/-/bowser-2.14.1.tgz#4ea39bf31e305184522d7ad7bfd91389e4f0cb79" @@ -2368,6 +2635,11 @@ buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== +bytes@^3.1.2, bytes@~3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== + call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" @@ -2376,6 +2648,14 @@ call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: es-errors "^1.3.0" function-bind "^1.1.2" +call-bound@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/call-bound/-/call-bound-1.0.4.tgz#238de935d2a2a692928c538c7ccfa91067fd062a" + integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg== + dependencies: + call-bind-apply-helpers "^1.0.2" + get-intrinsic "^1.3.0" + callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" @@ -2519,11 +2799,39 @@ console-table-printer@^2.12.1: dependencies: simple-wcswidth "^1.1.2" +content-disposition@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-1.1.0.tgz#f3db789c752d45564cc7e9e1e0b31790d4a38e17" + integrity sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g== + +content-type@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" + integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== + convert-source-map@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== +cookie-signature@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.2.2.tgz#57c7fc3cc293acab9fec54d73e15690ebe4a1793" + integrity sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg== + +cookie@^0.7.1: + version "0.7.2" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.2.tgz#556369c472a2ba910f2979891b526b3436237ed7" + integrity sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w== + +cors@^2.8.5: + version "2.8.6" + resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.6.tgz#ff5dd69bd95e547503820d29aba4f8faf8dfec96" + integrity sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw== + dependencies: + object-assign "^4" + vary "^1" + create-jest@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/create-jest/-/create-jest-29.7.0.tgz#a355c5b3cb1e1af02ba177fe7afd7feee49a5320" @@ -2549,7 +2857,7 @@ cross-fetch@^3.1.5: dependencies: node-fetch "^2.7.0" -cross-spawn@^7.0.3, cross-spawn@^7.0.6: +cross-spawn@^7.0.3, cross-spawn@^7.0.5, cross-spawn@^7.0.6: version "7.0.6" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== @@ -2572,6 +2880,13 @@ debug@^4.3.4, debug@^4.3.7: dependencies: ms "^2.1.3" +debug@^4.4.0, debug@^4.4.3: + version "4.4.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" + integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== + dependencies: + ms "^2.1.3" + decamelize@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" @@ -2597,6 +2912,11 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== +depd@^2.0.0, depd@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + detect-newline@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" @@ -2621,6 +2941,11 @@ dunder-proto@^1.0.1: es-errors "^1.3.0" gopd "^1.2.0" +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== + electron-to-chromium@^1.5.263: version "1.5.267" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz#5d84f2df8cdb6bfe7e873706bb21bd4bfb574dc7" @@ -2641,6 +2966,11 @@ emojilib@^2.4.0: resolved "https://registry.yarnpkg.com/emojilib/-/emojilib-2.4.0.tgz#ac518a8bb0d5f76dda57289ccb2fdf9d39ae721e" integrity sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw== +encodeurl@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58" + integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== + environment@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/environment/-/environment-1.1.0.tgz#8e86c66b180f363c7ab311787e0259665f45a9f1" @@ -2680,6 +3010,38 @@ es-set-tostringtag@^2.1.0: has-tostringtag "^1.0.2" hasown "^2.0.2" +esbuild@~0.27.0: + version "0.27.7" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.27.7.tgz#bcadce22b2f3fd76f257e3a64f83a64986fea11f" + integrity sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w== + optionalDependencies: + "@esbuild/aix-ppc64" "0.27.7" + "@esbuild/android-arm" "0.27.7" + "@esbuild/android-arm64" "0.27.7" + "@esbuild/android-x64" "0.27.7" + "@esbuild/darwin-arm64" "0.27.7" + "@esbuild/darwin-x64" "0.27.7" + "@esbuild/freebsd-arm64" "0.27.7" + "@esbuild/freebsd-x64" "0.27.7" + "@esbuild/linux-arm" "0.27.7" + "@esbuild/linux-arm64" "0.27.7" + "@esbuild/linux-ia32" "0.27.7" + "@esbuild/linux-loong64" "0.27.7" + "@esbuild/linux-mips64el" "0.27.7" + "@esbuild/linux-ppc64" "0.27.7" + "@esbuild/linux-riscv64" "0.27.7" + "@esbuild/linux-s390x" "0.27.7" + "@esbuild/linux-x64" "0.27.7" + "@esbuild/netbsd-arm64" "0.27.7" + "@esbuild/netbsd-x64" "0.27.7" + "@esbuild/openbsd-arm64" "0.27.7" + "@esbuild/openbsd-x64" "0.27.7" + "@esbuild/openharmony-arm64" "0.27.7" + "@esbuild/sunos-x64" "0.27.7" + "@esbuild/win32-arm64" "0.27.7" + "@esbuild/win32-ia32" "0.27.7" + "@esbuild/win32-x64" "0.27.7" + escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" @@ -2690,6 +3052,11 @@ escalade@^3.2.0: resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== +escape-html@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== + escape-string-regexp@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" @@ -2815,6 +3182,11 @@ esutils@^2.0.2: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== +etag@^1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== + event-target-shim@^5.0.0: version "5.0.1" resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" @@ -2825,6 +3197,18 @@ eventemitter3@^4.0.4: resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== +eventsource-parser@^3.0.0, eventsource-parser@^3.0.1: + version "3.0.8" + resolved "https://registry.yarnpkg.com/eventsource-parser/-/eventsource-parser-3.0.8.tgz#1c792503e4080455d00701bb1f7a1d60734d0e58" + integrity sha512-70QWGkr4snxr0OXLRWsFLeRBIRPuQOvt4s8QYjmUlmlkyTZkRqS7EDVRZtzU3TiyDbXSzaOeF0XUKy8PchzukQ== + +eventsource@^3.0.2: + version "3.0.7" + resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-3.0.7.tgz#1157622e2f5377bb6aef2114372728ba0c156989" + integrity sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA== + dependencies: + eventsource-parser "^3.0.1" + execa@^5.0.0: version "5.1.1" resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" @@ -2856,6 +3240,47 @@ expect@^29.0.0, expect@^29.7.0: jest-message-util "^29.7.0" jest-util "^29.7.0" +express-rate-limit@^8.2.1: + version "8.5.1" + resolved "https://registry.yarnpkg.com/express-rate-limit/-/express-rate-limit-8.5.1.tgz#ee62473d7b3bdf3b27b7be3d7f25c6d13308479a" + integrity sha512-5O6KYmyJEpuPJV5hNTXKbAHWRqrzyu+OI3vUnSd2kXFubIVpG7ezpgxQy76Zo5GQZtrQBg86hF+CM/NX+cioiQ== + dependencies: + ip-address "^10.2.0" + +express@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/express/-/express-5.2.1.tgz#8f21d15b6d327f92b4794ecf8cb08a72f956ac04" + integrity sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw== + dependencies: + accepts "^2.0.0" + body-parser "^2.2.1" + content-disposition "^1.0.0" + content-type "^1.0.5" + cookie "^0.7.1" + cookie-signature "^1.2.1" + debug "^4.4.0" + depd "^2.0.0" + encodeurl "^2.0.0" + escape-html "^1.0.3" + etag "^1.8.1" + finalhandler "^2.1.0" + fresh "^2.0.0" + http-errors "^2.0.0" + merge-descriptors "^2.0.0" + mime-types "^3.0.0" + on-finished "^2.4.1" + once "^1.4.0" + parseurl "^1.3.3" + proxy-addr "^2.0.7" + qs "^6.14.0" + range-parser "^1.2.1" + router "^2.2.0" + send "^1.1.0" + serve-static "^2.2.0" + statuses "^2.0.1" + type-is "^2.0.1" + vary "^1.1.2" + fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -2882,6 +3307,11 @@ fast-levenshtein@^2.0.6: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== +fast-uri@^3.0.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.1.2.tgz#8af3d4fc9d3e71b11572cc2673b514a7d1a8c8ec" + integrity sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ== + fast-xml-builder@^1.1.7: version "1.1.9" resolved "https://registry.yarnpkg.com/fast-xml-builder/-/fast-xml-builder-1.1.9.tgz#96bf8de1e3a5f560149b6092844db4e6fd0ee38f" @@ -2932,6 +3362,18 @@ fill-range@^7.1.1: dependencies: to-regex-range "^5.0.1" +finalhandler@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-2.1.1.tgz#a2c517a6559852bcdb06d1f8bd7f51b68fad8099" + integrity sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA== + dependencies: + debug "^4.4.0" + encodeurl "^2.0.0" + escape-html "^1.0.3" + on-finished "^2.4.1" + parseurl "^1.3.3" + statuses "^2.0.1" + find-up@^4.0.0, find-up@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" @@ -2990,12 +3432,22 @@ formdata-node@^4.3.2: node-domexception "1.0.0" web-streams-polyfill "4.0.0-beta.3" +forwarded@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== + +fresh@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-2.0.0.tgz#8dd7df6a1b3a1b3a5cf186c05a5dd267622635a4" + integrity sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A== + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -fsevents@^2.3.2: +fsevents@^2.3.2, fsevents@~2.3.3: version "2.3.3" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== @@ -3015,7 +3467,7 @@ get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-intrinsic@^1.2.6: +get-intrinsic@^1.2.5, get-intrinsic@^1.2.6, get-intrinsic@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== @@ -3054,6 +3506,13 @@ get-stream@^6.0.0: resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== +get-tsconfig@^4.7.5: + version "4.14.0" + resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.14.0.tgz#985d85c52a9903864280ccc2448d413fbf1efed8" + integrity sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA== + dependencies: + resolve-pkg-maps "^1.0.0" + glob-parent@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" @@ -3160,11 +3619,27 @@ highlight.js@^10.7.1: resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531" integrity sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A== +hono@^4.11.4: + version "4.12.18" + resolved "https://registry.yarnpkg.com/hono/-/hono-4.12.18.tgz#f6d301938868c3a8bdb639495f4e326a19181505" + integrity sha512-RWzP96k/yv0PQfyXnWjs6zot20TqfpfsNXhOnev8d1InAxubW93L11/oNUc3tQqn2G0bSdAOBpX+2uDFHV7kdQ== + html-escaper@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== +http-errors@^2.0.0, http-errors@^2.0.1, http-errors@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.1.tgz#36d2f65bc909c8790018dd36fb4d93da6caae06b" + integrity sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ== + dependencies: + depd "~2.0.0" + inherits "~2.0.4" + setprototypeof "~1.2.0" + statuses "~2.0.2" + toidentifier "~1.0.1" + human-signals@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" @@ -3184,6 +3659,13 @@ iconv-lite@^0.6.3: dependencies: safer-buffer ">= 2.1.2 < 3.0.0" +iconv-lite@^0.7.0, iconv-lite@~0.7.0: + version "0.7.2" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.7.2.tgz#d0bdeac3f12b4835b7359c2ad89c422a4d1cc72e" + integrity sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + ignore-walk@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-5.0.1.tgz#5f199e23e1288f518d90358d461387788a154776" @@ -3230,11 +3712,21 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.3: +inherits@2, inherits@^2.0.3, inherits@~2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== +ip-address@^10.2.0: + version "10.2.0" + resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-10.2.0.tgz#805fc178b20c518bd4c8548b24fe30892d7f3206" + integrity sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA== + +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" @@ -3274,6 +3766,11 @@ is-number@^7.0.0: resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== +is-promise@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-4.0.0.tgz#42ff9f84206c1991d26debf520dd5c01042dd2f3" + integrity sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ== + is-stream@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" @@ -3695,6 +4192,11 @@ jest@^29.4.0: import-local "^3.0.2" jest-cli "^29.7.0" +jose@^6.1.3: + version "6.2.3" + resolved "https://registry.yarnpkg.com/jose/-/jose-6.2.3.tgz#0975197ad973251221c658a3cddc4b951a250c2d" + integrity sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw== + js-tiktoken@^1.0.12: version "1.0.21" resolved "https://registry.yarnpkg.com/js-tiktoken/-/js-tiktoken-1.0.21.tgz#368a9957591a30a62997dd0c4cf30866f00f8221" @@ -3737,11 +4239,29 @@ json-parse-even-better-errors@^2.3.0: resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== +json-schema-to-ts@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/json-schema-to-ts/-/json-schema-to-ts-3.1.1.tgz#81f3acaf5a34736492f6f5f51870ef9ece1ca853" + integrity sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g== + dependencies: + "@babel/runtime" "^7.18.3" + ts-algebra "^2.0.0" + json-schema-traverse@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + +json-schema-typed@^8.0.2: + version "8.0.2" + resolved "https://registry.yarnpkg.com/json-schema-typed/-/json-schema-typed-8.0.2.tgz#e98ee7b1899ff4a184534d1f167c288c66bbeff4" + integrity sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA== + json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" @@ -3921,6 +4441,16 @@ math-intrinsics@^1.1.0: resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== +media-typer@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-1.1.0.tgz#6ab74b8f2d3320f2064b2a87a38e7931ff3a5561" + integrity sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw== + +merge-descriptors@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-2.0.0.tgz#ea922f660635a2249ee565e0449f951e6b603808" + integrity sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g== + merge-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" @@ -3944,6 +4474,11 @@ mime-db@1.52.0: resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== +mime-db@^1.54.0: + version "1.54.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.54.0.tgz#cddb3ee4f9c64530dff640236661d42cb6a314f5" + integrity sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ== + mime-types@^2.1.12: version "2.1.35" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" @@ -3951,6 +4486,13 @@ mime-types@^2.1.12: dependencies: mime-db "1.52.0" +mime-types@^3.0.0, mime-types@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-3.0.2.tgz#39002d4182575d5af036ffa118100f2524b2e2ab" + integrity sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A== + dependencies: + mime-db "^1.54.0" + mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" @@ -4002,6 +4544,11 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== +negotiator@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-1.0.0.tgz#b6c91bb47172d69f93cfd7c357bbb529019b5f6a" + integrity sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg== + nice-grpc-client-middleware-retry@^3.1.13: version "3.1.13" resolved "https://registry.yarnpkg.com/nice-grpc-client-middleware-retry/-/nice-grpc-client-middleware-retry-3.1.13.tgz#25de76d3ab86328a35e3b5c9093a4cb03d98b2a0" @@ -4092,12 +4639,24 @@ npm-run-path@^4.0.1: dependencies: path-key "^3.0.0" -object-assign@^4.0.1: +object-assign@^4, object-assign@^4.0.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== -once@^1.3.0: +object-inspect@^1.13.3, object-inspect@^1.13.4: + version "1.13.4" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213" + integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew== + +on-finished@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" + integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== + dependencies: + ee-first "1.1.1" + +once@^1.3.0, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== @@ -4255,6 +4814,11 @@ parse5@^6.0.1: resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== +parseurl@^1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + path-exists@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" @@ -4280,6 +4844,11 @@ path-parse@^1.0.7: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== +path-to-regexp@^8.0.0: + version "8.4.2" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-8.4.2.tgz#795c420c4f7ca45c5b887366f622ee0c9852cccd" + integrity sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA== + performance-now@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" @@ -4300,6 +4869,11 @@ pirates@^4.0.4: resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== +pkce-challenge@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/pkce-challenge/-/pkce-challenge-5.0.1.tgz#3b4446865b17b1745e9ace2016a31f48ddf6230d" + integrity sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ== + pkg-dir@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" @@ -4352,6 +4926,14 @@ protobufjs@^7.5.3, protobufjs@^7.5.5: "@types/node" ">=13.7.0" long "^5.0.0" +proxy-addr@^2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== + dependencies: + forwarded "0.2.0" + ipaddr.js "1.9.1" + publint@^0.2.12: version "0.2.12" resolved "https://registry.yarnpkg.com/publint/-/publint-0.2.12.tgz#d25cd6bd243d5bdd640344ecdddb3eeafdcc4059" @@ -4371,11 +4953,33 @@ pure-rand@^6.0.0: resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.0.4.tgz#50b737f6a925468679bff00ad20eade53f37d5c7" integrity sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA== +qs@^6.14.0, qs@^6.14.1: + version "6.15.1" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.15.1.tgz#bdb55aed06bfac257a90c44a446a73fba5575c8f" + integrity sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg== + dependencies: + side-channel "^1.1.0" + queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== +range-parser@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@^3.0.0, raw-body@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-3.0.2.tgz#3e3ada5ae5568f9095d84376fd3a49b8fb000a51" + integrity sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA== + dependencies: + bytes "~3.1.2" + http-errors "~2.0.1" + iconv-lite "~0.7.0" + unpipe "~1.0.0" + react-is@^18.0.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" @@ -4395,6 +4999,11 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + resolve-cwd@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" @@ -4412,6 +5021,11 @@ resolve-from@^5.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== +resolve-pkg-maps@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz#616b3dc2c57056b5588c31cdf4b3d64db133720f" + integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== + resolve.exports@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800" @@ -4436,6 +5050,17 @@ reusify@^1.0.4: resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== +router@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/router/-/router-2.2.0.tgz#019be620b711c87641167cc79b99090f00b146ef" + integrity sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ== + dependencies: + debug "^4.4.0" + depd "^2.0.0" + is-promise "^4.0.0" + parseurl "^1.3.3" + path-to-regexp "^8.0.0" + run-parallel@^1.1.9: version "1.2.0" resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" @@ -4487,6 +5112,38 @@ semver@^7.6.3: resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.4.tgz#28464e36060e991fa7a11d0279d2d3f3b57a7e8a" integrity sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA== +send@^1.1.0, send@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/send/-/send-1.2.1.tgz#9eab743b874f3550f40a26867bf286ad60d3f3ed" + integrity sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ== + dependencies: + debug "^4.4.3" + encodeurl "^2.0.0" + escape-html "^1.0.3" + etag "^1.8.1" + fresh "^2.0.0" + http-errors "^2.0.1" + mime-types "^3.0.2" + ms "^2.1.3" + on-finished "^2.4.1" + range-parser "^1.2.1" + statuses "^2.0.2" + +serve-static@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-2.2.1.tgz#7f186a4a4e5f5b663ad7a4294ff1bf37cf0e98a9" + integrity sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw== + dependencies: + encodeurl "^2.0.0" + escape-html "^1.0.3" + parseurl "^1.3.3" + send "^1.2.0" + +setprototypeof@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + shebang-command@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" @@ -4499,6 +5156,46 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== +side-channel-list@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/side-channel-list/-/side-channel-list-1.0.1.tgz#c2e0b5a14a540aebee3bbc6c3f8666cc9b509127" + integrity sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w== + dependencies: + es-errors "^1.3.0" + object-inspect "^1.13.4" + +side-channel-map@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/side-channel-map/-/side-channel-map-1.0.1.tgz#d6bb6b37902c6fef5174e5f533fab4c732a26f42" + integrity sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" + +side-channel-weakmap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz#11dda19d5368e40ce9ec2bdc1fb0ecbc0790ecea" + integrity sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" + side-channel-map "^1.0.1" + +side-channel@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.1.0.tgz#c3fcff9c4da932784873335ec9765fa94ff66bc9" + integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw== + dependencies: + es-errors "^1.3.0" + object-inspect "^1.13.3" + side-channel-list "^1.0.0" + side-channel-map "^1.0.1" + side-channel-weakmap "^1.0.2" + signal-exit@^3.0.3, signal-exit@^3.0.7: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" @@ -4551,6 +5248,11 @@ stack-utils@^2.0.3: dependencies: escape-string-regexp "^2.0.0" +statuses@^2.0.1, statuses@^2.0.2, statuses@~2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.2.tgz#8f75eecef765b5e1cfcdc080da59409ed424e382" + integrity sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw== + string-length@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" @@ -4681,11 +5383,21 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" +toidentifier@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + tr46@~0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== +ts-algebra@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ts-algebra/-/ts-algebra-2.0.0.tgz#4e3e0953878f26518fce7f6bb115064a65388b7a" + integrity sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw== + ts-api-utils@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-2.0.1.tgz#660729385b625b939aaa58054f45c058f33f10cd" @@ -4758,6 +5470,16 @@ tslib@^2.6.2, tslib@^2.8.1: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== +tsx@^4.21.0: + version "4.21.0" + resolved "https://registry.yarnpkg.com/tsx/-/tsx-4.21.0.tgz#32aa6cf17481e336f756195e6fe04dae3e6308b1" + integrity sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw== + dependencies: + esbuild "~0.27.0" + get-tsconfig "^4.7.5" + optionalDependencies: + fsevents "~2.3.3" + type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" @@ -4775,6 +5497,15 @@ type-fest@^0.21.3: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== +type-is@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-2.0.1.tgz#64f6cf03f92fce4015c2b224793f6bdd4b068c97" + integrity sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw== + dependencies: + content-type "^1.0.5" + media-typer "^1.1.0" + mime-types "^3.0.0" + typescript-eslint@8.31.1: version "8.31.1" resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.31.1.tgz#b77ab1e48ced2daab9225ff94bab54391a4af69b" @@ -4814,6 +5545,11 @@ unicode-emoji-modifier-base@^1.0.0: resolved "https://registry.yarnpkg.com/unicode-emoji-modifier-base/-/unicode-emoji-modifier-base-1.0.0.tgz#dbbd5b54ba30f287e2a8d5a249da6c0cef369459" integrity sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g== +unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== + update-browserslist-db@^1.2.0: version "1.2.3" resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz#64d76db58713136acbeb4c49114366cc6cc2e80d" @@ -4863,6 +5599,11 @@ validate-npm-package-name@^5.0.0: resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz#a316573e9b49f3ccd90dbb6eb52b3f06c6d604e8" integrity sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ== +vary@^1, vary@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== + walker@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" @@ -5003,6 +5744,16 @@ zod-to-json-schema@^3.22.3: resolved "https://registry.yarnpkg.com/zod-to-json-schema/-/zod-to-json-schema-3.25.1.tgz#7f24962101a439ddade2bf1aeab3c3bfec7d84ba" integrity sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA== +zod-to-json-schema@^3.25.1: + version "3.25.2" + resolved "https://registry.yarnpkg.com/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz#3fa799a7badd554541472fb65843fdc460b2e5aa" + integrity sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA== + +"zod@^3.25 || ^4.0": + version "4.4.3" + resolved "https://registry.yarnpkg.com/zod/-/zod-4.4.3.tgz#b680f172885d18bbebf21a834ea25e55a1bbf356" + integrity sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ== + zod@^3.25.32: version "3.25.76" resolved "https://registry.yarnpkg.com/zod/-/zod-3.25.76.tgz#26841c3f6fd22a6a2760e7ccb719179768471e34"