@directive-run/ai 0.4.0 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/agent-utils.ts","../src/adapters/ollama.ts"],"names":["ALLOWED_PROTOCOLS","validateBaseURL","baseURL","url","err","createRunner","options","fetchFn","buildRequest","parseResponse","parseOutput","hooks","parse","text","agent","input","runOptions","startTime","messages","init","fetchInit","response","errBody","parsed","tokenUsage","assistantMessage","allMessages","durationMs","createOllamaRunner","model","timeoutMs","_input","m","res","data","inputTokens","outputTokens"],"mappings":"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","file":"ollama.cjs","sourcesContent":["/**\n * Agent utilities — createRunner, estimateCost, state queries, URL validation.\n */\n\nimport type {\n AdapterHooks,\n AgentLike,\n AgentRunner,\n AgentState,\n ApprovalState,\n Message,\n RunOptions,\n RunResult,\n TokenUsage,\n} from \"./types.js\";\n\n// ============================================================================\n// State Query Helpers\n// ============================================================================\n\n/** Check if agent is currently running. */\nexport function isAgentRunning(state: AgentState): boolean {\n return state.status === \"running\";\n}\n\n/** Check if there are pending approvals. */\nexport function hasPendingApprovals(state: ApprovalState): boolean {\n return state.pending.length > 0;\n}\n\n// ============================================================================\n// Cost Estimation\n// ============================================================================\n\n/**\n * Get total cost estimate based on token usage.\n *\n * @param tokenUsage - Total token count\n * @param ratePerMillionTokens - Cost per million tokens (required, no default to avoid stale pricing)\n * @returns Estimated cost in dollars\n */\nexport function estimateCost(\n tokenUsage: number,\n ratePerMillionTokens: number,\n): number {\n return (tokenUsage / 1_000_000) * ratePerMillionTokens;\n}\n\n// ============================================================================\n// Validation Helpers\n// ============================================================================\n\nconst ALLOWED_PROTOCOLS = new Set([\"http:\", \"https:\"]);\n\n/**\n * Validate that a baseURL uses http or https.\n * Throws immediately at adapter creation time (not at call time) to catch config errors early.\n */\nexport function validateBaseURL(baseURL: string): void {\n try {\n const url = new URL(baseURL);\n if (!ALLOWED_PROTOCOLS.has(url.protocol)) {\n throw new Error(\n `[Directive] Invalid baseURL protocol \"${url.protocol}\" – only http: and https: are allowed`,\n );\n }\n } catch (err) {\n if (err instanceof Error && err.message.startsWith(\"[Directive]\")) {\n throw err;\n }\n\n throw new Error(\n `[Directive] Invalid baseURL \"${baseURL}\" – must be a valid URL (e.g. \"https://api.openai.com/v1\")`,\n );\n }\n}\n\n// ============================================================================\n// createRunner Helper\n// ============================================================================\n\n/** Parsed response from an LLM provider */\nexport interface ParsedResponse {\n text: string;\n totalTokens: number;\n /** Input token count, when available from the provider */\n inputTokens?: number;\n /** Output token count, when available from the provider */\n outputTokens?: number;\n}\n\n/** Options for creating an AgentRunner from buildRequest/parseResponse */\nexport interface CreateRunnerOptions {\n fetch?: typeof globalThis.fetch;\n buildRequest: (\n agent: AgentLike,\n input: string,\n messages: Message[],\n ) => { url: string; init: RequestInit };\n parseResponse: (\n response: Response,\n messages: Message[],\n ) => Promise<ParsedResponse>;\n parseOutput?: <T>(text: string) => T;\n /** Lifecycle hooks for tracing, logging, and metrics */\n hooks?: AdapterHooks;\n}\n\n/**\n * Create an AgentRunner from buildRequest/parseResponse helpers.\n * Reduces ~50 lines of fetch boilerplate to ~20 lines of configuration.\n *\n * Supports lifecycle hooks for observability:\n * - `onBeforeCall` fires before each API request\n * - `onAfterCall` fires after a successful response (includes token breakdown)\n * - `onError` fires when the request fails\n *\n * @example\n * ```typescript\n * const runClaude = createRunner({\n * buildRequest: (agent, input) => ({\n * url: \"/api/claude\",\n * init: {\n * method: \"POST\",\n * headers: { \"Content-Type\": \"application/json\" },\n * body: JSON.stringify({\n * model: agent.model ?? \"claude-haiku-4-5-20251001\",\n * system: agent.instructions ?? \"\",\n * messages: [{ role: \"user\", content: input }],\n * }),\n * },\n * }),\n * parseResponse: async (res) => {\n * const data = await res.json();\n * const inputTokens = data.usage?.input_tokens ?? 0;\n * const outputTokens = data.usage?.output_tokens ?? 0;\n * return {\n * text: data.content?.[0]?.text ?? \"\",\n * totalTokens: inputTokens + outputTokens,\n * inputTokens,\n * outputTokens,\n * };\n * },\n * hooks: {\n * onAfterCall: ({ durationMs, tokenUsage }) => {\n * console.log(`LLM call: ${durationMs}ms, ${tokenUsage.inputTokens}in/${tokenUsage.outputTokens}out`);\n * },\n * },\n * });\n * ```\n */\nexport function createRunner(options: CreateRunnerOptions): AgentRunner {\n const {\n fetch: fetchFn = globalThis.fetch,\n buildRequest,\n parseResponse,\n parseOutput,\n hooks,\n } = options;\n\n const defaultParseOutput = <T>(text: string): T => {\n try {\n return JSON.parse(text) as T;\n } catch {\n return text as unknown as T;\n }\n };\n\n const parse = parseOutput ?? defaultParseOutput;\n\n return async <T = unknown>(\n agent: AgentLike,\n input: string,\n runOptions?: RunOptions,\n ): Promise<RunResult<T>> => {\n const startTime = Date.now();\n hooks?.onBeforeCall?.({ agent, input, timestamp: startTime });\n\n const messages: Message[] = [{ role: \"user\", content: input }];\n\n try {\n const { url, init } = buildRequest(agent, input, messages);\n\n const fetchInit: RequestInit = runOptions?.signal\n ? { ...init, signal: runOptions.signal }\n : init;\n\n const response = await fetchFn(url, fetchInit);\n\n if (!response.ok) {\n const errBody = await response.text().catch(() => \"\");\n\n throw new Error(\n `[Directive] AgentRunner request failed: ${response.status} ${response.statusText}${errBody ? ` – ${errBody.slice(0, 300)}` : \"\"}`,\n );\n }\n\n const parsed = await parseResponse(response, messages);\n const tokenUsage: TokenUsage = {\n inputTokens: parsed.inputTokens ?? 0,\n outputTokens: parsed.outputTokens ?? 0,\n };\n\n const assistantMessage: Message = {\n role: \"assistant\",\n content: parsed.text,\n };\n const allMessages: Message[] = [...messages, assistantMessage];\n\n runOptions?.onMessage?.(assistantMessage);\n\n const durationMs = Date.now() - startTime;\n hooks?.onAfterCall?.({\n agent,\n input,\n output: parsed.text,\n totalTokens: parsed.totalTokens,\n tokenUsage,\n durationMs,\n timestamp: Date.now(),\n });\n\n return {\n output: parse<T>(parsed.text),\n messages: allMessages,\n toolCalls: [],\n totalTokens: parsed.totalTokens,\n tokenUsage,\n };\n } catch (err) {\n const durationMs = Date.now() - startTime;\n if (err instanceof Error) {\n hooks?.onError?.({\n agent,\n input,\n error: err,\n durationMs,\n timestamp: Date.now(),\n });\n }\n\n throw err;\n }\n };\n}\n","/**\n * @directive-run/ai/ollama\n *\n * Ollama adapter for Directive AI. Provides runners for local\n * Ollama inference. No API key required.\n *\n * Requires Ollama to be running locally. Start it with: `ollama serve`\n *\n * @example\n * ```typescript\n * import { createOllamaRunner } from '@directive-run/ai/ollama';\n *\n * const runner = createOllamaRunner({ model: 'llama3' });\n * ```\n */\n\nimport { createRunner, validateBaseURL } from \"../agent-utils.js\";\nimport type { AdapterHooks, AgentRunner } from \"../types.js\";\n\n// ============================================================================\n// Ollama Runner\n// ============================================================================\n\n/** Options for createOllamaRunner */\nexport interface OllamaRunnerOptions {\n model?: string;\n baseURL?: string;\n fetch?: typeof globalThis.fetch;\n /** @default undefined */\n timeoutMs?: number;\n /** Lifecycle hooks for tracing, logging, and metrics */\n hooks?: AdapterHooks;\n}\n\n/**\n * Create an AgentRunner for local Ollama inference.\n *\n * Ollama runs locally – no API key or cloud service needed. Default model\n * is `llama3`, default base URL is `http://localhost:11434`.\n *\n * Returns `tokenUsage` with input/output breakdown for cost tracking\n * (useful for monitoring local resource usage).\n *\n * @example\n * ```typescript\n * const runner = createOllamaRunner({ model: \"llama3\" });\n * const orchestrator = createAgentOrchestrator({ runner });\n * const result = await orchestrator.run(agent, input);\n * ```\n */\nexport function createOllamaRunner(\n options: OllamaRunnerOptions = {},\n): AgentRunner {\n const {\n model = \"llama3\",\n baseURL = \"http://localhost:11434\",\n fetch: fetchFn = globalThis.fetch,\n timeoutMs,\n hooks,\n } = options;\n\n validateBaseURL(baseURL);\n\n return createRunner({\n fetch: fetchFn,\n hooks,\n buildRequest: (agent, _input, messages) => ({\n url: `${baseURL}/api/chat`,\n init: {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n model: agent.model ?? model,\n messages: [\n ...(agent.instructions\n ? [{ role: \"system\", content: agent.instructions }]\n : []),\n ...messages.map((m) => ({ role: m.role, content: m.content })),\n ],\n stream: false,\n }),\n ...(timeoutMs != null\n ? { signal: AbortSignal.timeout(timeoutMs) }\n : {}),\n },\n }),\n parseResponse: async (res) => {\n let data: Record<string, unknown>;\n try {\n data = await res.json();\n } catch {\n throw new Error(\n `[Directive] Ollama returned non-JSON response. Is Ollama running at ${baseURL}? Start it with: ollama serve`,\n );\n }\n const text =\n ((data.message as Record<string, unknown>)?.content as string) ?? \"\";\n const inputTokens = (data.prompt_eval_count as number) ?? 0;\n const outputTokens = (data.eval_count as number) ?? 0;\n\n return {\n text,\n totalTokens: inputTokens + outputTokens,\n inputTokens,\n outputTokens,\n };\n },\n });\n}\n"]}
1
+ {"version":3,"sources":["../src/agent-utils.ts","../src/adapters/ollama.ts"],"names":["ALLOWED_PROTOCOLS","validateBaseURL","baseURL","url","err","createRunner","options","fetchFn","buildRequest","parseResponse","parseOutput","hooks","parse","text","agent","input","runOptions","startTime","messages","init","fetchInit","response","errBody","parsed","tokenUsage","assistantMessage","allMessages","durationMs","createOllamaRunner","model","timeoutMs","_input","m","res","data","inputTokens","outputTokens"],"mappings":"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","file":"ollama.cjs","sourcesContent":["/**\n * Agent utilities — createRunner, estimateCost, state queries, URL validation.\n */\n\nimport type {\n AdapterHooks,\n AgentLike,\n AgentRunner,\n AgentState,\n ApprovalState,\n Message,\n RunOptions,\n RunResult,\n TokenUsage,\n} from \"./types.js\";\n\n// ============================================================================\n// State Query Helpers\n// ============================================================================\n\n/**\n * Check whether an agent is currently executing a run.\n *\n * @param state - The current {@link AgentState} to inspect.\n * @returns `true` when the agent status is `\"running\"`.\n */\nexport function isAgentRunning(state: AgentState): boolean {\n return state.status === \"running\";\n}\n\n/**\n * Check whether there are tool-call approvals waiting for user confirmation.\n *\n * @param state - The current {@link ApprovalState} to inspect.\n * @returns `true` when one or more approvals are pending.\n */\nexport function hasPendingApprovals(state: ApprovalState): boolean {\n return state.pending.length > 0;\n}\n\n// ============================================================================\n// Cost Estimation\n// ============================================================================\n\n/**\n * Estimate the dollar cost of an agent run based on total token usage.\n *\n * @remarks\n * No default rate is provided — callers must supply the current per-million-token\n * price to avoid silently using stale pricing.\n *\n * @param tokenUsage - Total number of tokens consumed (input + output).\n * @param ratePerMillionTokens - Cost in dollars per one million tokens.\n * @returns Estimated cost in dollars.\n */\nexport function estimateCost(\n tokenUsage: number,\n ratePerMillionTokens: number,\n): number {\n return (tokenUsage / 1_000_000) * ratePerMillionTokens;\n}\n\n// ============================================================================\n// Validation Helpers\n// ============================================================================\n\nconst ALLOWED_PROTOCOLS = new Set([\"http:\", \"https:\"]);\n\n/**\n * Validate that a base URL uses the `http:` or `https:` protocol.\n * Throws immediately at adapter creation time (not at call time) to surface\n * configuration errors before any LLM requests are made.\n *\n * @param baseURL - The base URL string to validate.\n * @throws When the URL is malformed or uses a protocol other than `http:` or `https:`.\n */\nexport function validateBaseURL(baseURL: string): void {\n try {\n const url = new URL(baseURL);\n if (!ALLOWED_PROTOCOLS.has(url.protocol)) {\n throw new Error(\n `[Directive] Invalid baseURL protocol \"${url.protocol}\" – only http: and https: are allowed`,\n );\n }\n } catch (err) {\n if (err instanceof Error && err.message.startsWith(\"[Directive]\")) {\n throw err;\n }\n\n throw new Error(\n `[Directive] Invalid baseURL \"${baseURL}\" – must be a valid URL (e.g. \"https://api.openai.com/v1\")`,\n );\n }\n}\n\n// ============================================================================\n// createRunner Helper\n// ============================================================================\n\n/** Parsed response from an LLM provider */\nexport interface ParsedResponse {\n text: string;\n totalTokens: number;\n /** Input token count, when available from the provider */\n inputTokens?: number;\n /** Output token count, when available from the provider */\n outputTokens?: number;\n}\n\n/** Options for creating an AgentRunner from buildRequest/parseResponse */\nexport interface CreateRunnerOptions {\n fetch?: typeof globalThis.fetch;\n buildRequest: (\n agent: AgentLike,\n input: string,\n messages: Message[],\n ) => { url: string; init: RequestInit };\n parseResponse: (\n response: Response,\n messages: Message[],\n ) => Promise<ParsedResponse>;\n parseOutput?: <T>(text: string) => T;\n /** Lifecycle hooks for tracing, logging, and metrics */\n hooks?: AdapterHooks;\n}\n\n/**\n * Create an {@link AgentRunner} from `buildRequest`/`parseResponse` helpers, reducing\n * ~50 lines of fetch boilerplate to ~20 lines of configuration.\n *\n * @remarks\n * Supports lifecycle hooks for observability:\n * - `onBeforeCall` fires before each API request\n * - `onAfterCall` fires after a successful response (includes token breakdown)\n * - `onError` fires when the request fails\n *\n * Output parsing defaults to `JSON.parse` with a string fallback. Supply a custom\n * `parseOutput` to override (e.g. for structured output schemas).\n *\n * @param options - Configuration for the runner, including request building, response parsing, and hooks.\n * @returns An {@link AgentRunner} function that performs LLM calls via fetch.\n *\n * @example\n * ```typescript\n * const runClaude = createRunner({\n * buildRequest: (agent, input) => ({\n * url: \"/api/claude\",\n * init: {\n * method: \"POST\",\n * headers: { \"Content-Type\": \"application/json\" },\n * body: JSON.stringify({\n * model: agent.model ?? \"claude-haiku-4-5-20251001\",\n * system: agent.instructions ?? \"\",\n * messages: [{ role: \"user\", content: input }],\n * }),\n * },\n * }),\n * parseResponse: async (res) => {\n * const data = await res.json();\n * const inputTokens = data.usage?.input_tokens ?? 0;\n * const outputTokens = data.usage?.output_tokens ?? 0;\n * return {\n * text: data.content?.[0]?.text ?? \"\",\n * totalTokens: inputTokens + outputTokens,\n * inputTokens,\n * outputTokens,\n * };\n * },\n * hooks: {\n * onAfterCall: ({ durationMs, tokenUsage }) => {\n * console.log(`LLM call: ${durationMs}ms, ${tokenUsage.inputTokens}in/${tokenUsage.outputTokens}out`);\n * },\n * },\n * });\n * ```\n *\n * @public\n */\nexport function createRunner(options: CreateRunnerOptions): AgentRunner {\n const {\n fetch: fetchFn = globalThis.fetch,\n buildRequest,\n parseResponse,\n parseOutput,\n hooks,\n } = options;\n\n const defaultParseOutput = <T>(text: string): T => {\n try {\n return JSON.parse(text) as T;\n } catch {\n return text as unknown as T;\n }\n };\n\n const parse = parseOutput ?? defaultParseOutput;\n\n return async <T = unknown>(\n agent: AgentLike,\n input: string,\n runOptions?: RunOptions,\n ): Promise<RunResult<T>> => {\n const startTime = Date.now();\n hooks?.onBeforeCall?.({ agent, input, timestamp: startTime });\n\n const messages: Message[] = [{ role: \"user\", content: input }];\n\n try {\n const { url, init } = buildRequest(agent, input, messages);\n\n const fetchInit: RequestInit = runOptions?.signal\n ? { ...init, signal: runOptions.signal }\n : init;\n\n const response = await fetchFn(url, fetchInit);\n\n if (!response.ok) {\n const errBody = await response.text().catch(() => \"\");\n\n throw new Error(\n `[Directive] AgentRunner request failed: ${response.status} ${response.statusText}${errBody ? ` – ${errBody.slice(0, 300)}` : \"\"}`,\n );\n }\n\n const parsed = await parseResponse(response, messages);\n const tokenUsage: TokenUsage = {\n inputTokens: parsed.inputTokens ?? 0,\n outputTokens: parsed.outputTokens ?? 0,\n };\n\n const assistantMessage: Message = {\n role: \"assistant\",\n content: parsed.text,\n };\n const allMessages: Message[] = [...messages, assistantMessage];\n\n runOptions?.onMessage?.(assistantMessage);\n\n const durationMs = Date.now() - startTime;\n hooks?.onAfterCall?.({\n agent,\n input,\n output: parsed.text,\n totalTokens: parsed.totalTokens,\n tokenUsage,\n durationMs,\n timestamp: Date.now(),\n });\n\n return {\n output: parse<T>(parsed.text),\n messages: allMessages,\n toolCalls: [],\n totalTokens: parsed.totalTokens,\n tokenUsage,\n };\n } catch (err) {\n const durationMs = Date.now() - startTime;\n if (err instanceof Error) {\n hooks?.onError?.({\n agent,\n input,\n error: err,\n durationMs,\n timestamp: Date.now(),\n });\n }\n\n throw err;\n }\n };\n}\n","/**\n * @directive-run/ai/ollama\n *\n * Ollama adapter for Directive AI. Provides runners for local\n * Ollama inference. No API key required.\n *\n * Requires Ollama to be running locally. Start it with: `ollama serve`\n *\n * @example\n * ```typescript\n * import { createOllamaRunner } from '@directive-run/ai/ollama';\n *\n * const runner = createOllamaRunner({ model: 'llama3' });\n * ```\n */\n\nimport { createRunner, validateBaseURL } from \"../agent-utils.js\";\nimport type { AdapterHooks, AgentRunner } from \"../types.js\";\n\n// ============================================================================\n// Ollama Runner\n// ============================================================================\n\n/** Options for createOllamaRunner */\nexport interface OllamaRunnerOptions {\n model?: string;\n baseURL?: string;\n fetch?: typeof globalThis.fetch;\n /** @default undefined */\n timeoutMs?: number;\n /** Lifecycle hooks for tracing, logging, and metrics */\n hooks?: AdapterHooks;\n}\n\n/**\n * Create an AgentRunner for local Ollama inference.\n *\n * Ollama runs locally – no API key or cloud service needed. Default model\n * is `llama3`, default base URL is `http://localhost:11434`.\n *\n * Returns `tokenUsage` with input/output breakdown for cost tracking\n * (useful for monitoring local resource usage).\n *\n * @example\n * ```typescript\n * const runner = createOllamaRunner({ model: \"llama3\" });\n * const orchestrator = createAgentOrchestrator({ runner });\n * const result = await orchestrator.run(agent, input);\n * ```\n */\nexport function createOllamaRunner(\n options: OllamaRunnerOptions = {},\n): AgentRunner {\n const {\n model = \"llama3\",\n baseURL = \"http://localhost:11434\",\n fetch: fetchFn = globalThis.fetch,\n timeoutMs,\n hooks,\n } = options;\n\n validateBaseURL(baseURL);\n\n return createRunner({\n fetch: fetchFn,\n hooks,\n buildRequest: (agent, _input, messages) => ({\n url: `${baseURL}/api/chat`,\n init: {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n model: agent.model ?? model,\n messages: [\n ...(agent.instructions\n ? [{ role: \"system\", content: agent.instructions }]\n : []),\n ...messages.map((m) => ({ role: m.role, content: m.content })),\n ],\n stream: false,\n }),\n ...(timeoutMs != null\n ? { signal: AbortSignal.timeout(timeoutMs) }\n : {}),\n },\n }),\n parseResponse: async (res) => {\n let data: Record<string, unknown>;\n try {\n data = await res.json();\n } catch {\n throw new Error(\n `[Directive] Ollama returned non-JSON response. Is Ollama running at ${baseURL}? Start it with: ollama serve`,\n );\n }\n const text =\n ((data.message as Record<string, unknown>)?.content as string) ?? \"\";\n const inputTokens = (data.prompt_eval_count as number) ?? 0;\n const outputTokens = (data.eval_count as number) ?? 0;\n\n return {\n text,\n totalTokens: inputTokens + outputTokens,\n inputTokens,\n outputTokens,\n };\n },\n });\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/agent-utils.ts","../src/adapters/ollama.ts"],"names":["ALLOWED_PROTOCOLS","validateBaseURL","baseURL","url","err","createRunner","options","fetchFn","buildRequest","parseResponse","parseOutput","hooks","parse","text","agent","input","runOptions","startTime","messages","init","fetchInit","response","errBody","parsed","tokenUsage","assistantMessage","allMessages","durationMs","createOllamaRunner","model","timeoutMs","_input","m","res","data","inputTokens","outputTokens"],"mappings":"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","file":"ollama.js","sourcesContent":["/**\n * Agent utilities — createRunner, estimateCost, state queries, URL validation.\n */\n\nimport type {\n AdapterHooks,\n AgentLike,\n AgentRunner,\n AgentState,\n ApprovalState,\n Message,\n RunOptions,\n RunResult,\n TokenUsage,\n} from \"./types.js\";\n\n// ============================================================================\n// State Query Helpers\n// ============================================================================\n\n/** Check if agent is currently running. */\nexport function isAgentRunning(state: AgentState): boolean {\n return state.status === \"running\";\n}\n\n/** Check if there are pending approvals. */\nexport function hasPendingApprovals(state: ApprovalState): boolean {\n return state.pending.length > 0;\n}\n\n// ============================================================================\n// Cost Estimation\n// ============================================================================\n\n/**\n * Get total cost estimate based on token usage.\n *\n * @param tokenUsage - Total token count\n * @param ratePerMillionTokens - Cost per million tokens (required, no default to avoid stale pricing)\n * @returns Estimated cost in dollars\n */\nexport function estimateCost(\n tokenUsage: number,\n ratePerMillionTokens: number,\n): number {\n return (tokenUsage / 1_000_000) * ratePerMillionTokens;\n}\n\n// ============================================================================\n// Validation Helpers\n// ============================================================================\n\nconst ALLOWED_PROTOCOLS = new Set([\"http:\", \"https:\"]);\n\n/**\n * Validate that a baseURL uses http or https.\n * Throws immediately at adapter creation time (not at call time) to catch config errors early.\n */\nexport function validateBaseURL(baseURL: string): void {\n try {\n const url = new URL(baseURL);\n if (!ALLOWED_PROTOCOLS.has(url.protocol)) {\n throw new Error(\n `[Directive] Invalid baseURL protocol \"${url.protocol}\" – only http: and https: are allowed`,\n );\n }\n } catch (err) {\n if (err instanceof Error && err.message.startsWith(\"[Directive]\")) {\n throw err;\n }\n\n throw new Error(\n `[Directive] Invalid baseURL \"${baseURL}\" – must be a valid URL (e.g. \"https://api.openai.com/v1\")`,\n );\n }\n}\n\n// ============================================================================\n// createRunner Helper\n// ============================================================================\n\n/** Parsed response from an LLM provider */\nexport interface ParsedResponse {\n text: string;\n totalTokens: number;\n /** Input token count, when available from the provider */\n inputTokens?: number;\n /** Output token count, when available from the provider */\n outputTokens?: number;\n}\n\n/** Options for creating an AgentRunner from buildRequest/parseResponse */\nexport interface CreateRunnerOptions {\n fetch?: typeof globalThis.fetch;\n buildRequest: (\n agent: AgentLike,\n input: string,\n messages: Message[],\n ) => { url: string; init: RequestInit };\n parseResponse: (\n response: Response,\n messages: Message[],\n ) => Promise<ParsedResponse>;\n parseOutput?: <T>(text: string) => T;\n /** Lifecycle hooks for tracing, logging, and metrics */\n hooks?: AdapterHooks;\n}\n\n/**\n * Create an AgentRunner from buildRequest/parseResponse helpers.\n * Reduces ~50 lines of fetch boilerplate to ~20 lines of configuration.\n *\n * Supports lifecycle hooks for observability:\n * - `onBeforeCall` fires before each API request\n * - `onAfterCall` fires after a successful response (includes token breakdown)\n * - `onError` fires when the request fails\n *\n * @example\n * ```typescript\n * const runClaude = createRunner({\n * buildRequest: (agent, input) => ({\n * url: \"/api/claude\",\n * init: {\n * method: \"POST\",\n * headers: { \"Content-Type\": \"application/json\" },\n * body: JSON.stringify({\n * model: agent.model ?? \"claude-haiku-4-5-20251001\",\n * system: agent.instructions ?? \"\",\n * messages: [{ role: \"user\", content: input }],\n * }),\n * },\n * }),\n * parseResponse: async (res) => {\n * const data = await res.json();\n * const inputTokens = data.usage?.input_tokens ?? 0;\n * const outputTokens = data.usage?.output_tokens ?? 0;\n * return {\n * text: data.content?.[0]?.text ?? \"\",\n * totalTokens: inputTokens + outputTokens,\n * inputTokens,\n * outputTokens,\n * };\n * },\n * hooks: {\n * onAfterCall: ({ durationMs, tokenUsage }) => {\n * console.log(`LLM call: ${durationMs}ms, ${tokenUsage.inputTokens}in/${tokenUsage.outputTokens}out`);\n * },\n * },\n * });\n * ```\n */\nexport function createRunner(options: CreateRunnerOptions): AgentRunner {\n const {\n fetch: fetchFn = globalThis.fetch,\n buildRequest,\n parseResponse,\n parseOutput,\n hooks,\n } = options;\n\n const defaultParseOutput = <T>(text: string): T => {\n try {\n return JSON.parse(text) as T;\n } catch {\n return text as unknown as T;\n }\n };\n\n const parse = parseOutput ?? defaultParseOutput;\n\n return async <T = unknown>(\n agent: AgentLike,\n input: string,\n runOptions?: RunOptions,\n ): Promise<RunResult<T>> => {\n const startTime = Date.now();\n hooks?.onBeforeCall?.({ agent, input, timestamp: startTime });\n\n const messages: Message[] = [{ role: \"user\", content: input }];\n\n try {\n const { url, init } = buildRequest(agent, input, messages);\n\n const fetchInit: RequestInit = runOptions?.signal\n ? { ...init, signal: runOptions.signal }\n : init;\n\n const response = await fetchFn(url, fetchInit);\n\n if (!response.ok) {\n const errBody = await response.text().catch(() => \"\");\n\n throw new Error(\n `[Directive] AgentRunner request failed: ${response.status} ${response.statusText}${errBody ? ` – ${errBody.slice(0, 300)}` : \"\"}`,\n );\n }\n\n const parsed = await parseResponse(response, messages);\n const tokenUsage: TokenUsage = {\n inputTokens: parsed.inputTokens ?? 0,\n outputTokens: parsed.outputTokens ?? 0,\n };\n\n const assistantMessage: Message = {\n role: \"assistant\",\n content: parsed.text,\n };\n const allMessages: Message[] = [...messages, assistantMessage];\n\n runOptions?.onMessage?.(assistantMessage);\n\n const durationMs = Date.now() - startTime;\n hooks?.onAfterCall?.({\n agent,\n input,\n output: parsed.text,\n totalTokens: parsed.totalTokens,\n tokenUsage,\n durationMs,\n timestamp: Date.now(),\n });\n\n return {\n output: parse<T>(parsed.text),\n messages: allMessages,\n toolCalls: [],\n totalTokens: parsed.totalTokens,\n tokenUsage,\n };\n } catch (err) {\n const durationMs = Date.now() - startTime;\n if (err instanceof Error) {\n hooks?.onError?.({\n agent,\n input,\n error: err,\n durationMs,\n timestamp: Date.now(),\n });\n }\n\n throw err;\n }\n };\n}\n","/**\n * @directive-run/ai/ollama\n *\n * Ollama adapter for Directive AI. Provides runners for local\n * Ollama inference. No API key required.\n *\n * Requires Ollama to be running locally. Start it with: `ollama serve`\n *\n * @example\n * ```typescript\n * import { createOllamaRunner } from '@directive-run/ai/ollama';\n *\n * const runner = createOllamaRunner({ model: 'llama3' });\n * ```\n */\n\nimport { createRunner, validateBaseURL } from \"../agent-utils.js\";\nimport type { AdapterHooks, AgentRunner } from \"../types.js\";\n\n// ============================================================================\n// Ollama Runner\n// ============================================================================\n\n/** Options for createOllamaRunner */\nexport interface OllamaRunnerOptions {\n model?: string;\n baseURL?: string;\n fetch?: typeof globalThis.fetch;\n /** @default undefined */\n timeoutMs?: number;\n /** Lifecycle hooks for tracing, logging, and metrics */\n hooks?: AdapterHooks;\n}\n\n/**\n * Create an AgentRunner for local Ollama inference.\n *\n * Ollama runs locally – no API key or cloud service needed. Default model\n * is `llama3`, default base URL is `http://localhost:11434`.\n *\n * Returns `tokenUsage` with input/output breakdown for cost tracking\n * (useful for monitoring local resource usage).\n *\n * @example\n * ```typescript\n * const runner = createOllamaRunner({ model: \"llama3\" });\n * const orchestrator = createAgentOrchestrator({ runner });\n * const result = await orchestrator.run(agent, input);\n * ```\n */\nexport function createOllamaRunner(\n options: OllamaRunnerOptions = {},\n): AgentRunner {\n const {\n model = \"llama3\",\n baseURL = \"http://localhost:11434\",\n fetch: fetchFn = globalThis.fetch,\n timeoutMs,\n hooks,\n } = options;\n\n validateBaseURL(baseURL);\n\n return createRunner({\n fetch: fetchFn,\n hooks,\n buildRequest: (agent, _input, messages) => ({\n url: `${baseURL}/api/chat`,\n init: {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n model: agent.model ?? model,\n messages: [\n ...(agent.instructions\n ? [{ role: \"system\", content: agent.instructions }]\n : []),\n ...messages.map((m) => ({ role: m.role, content: m.content })),\n ],\n stream: false,\n }),\n ...(timeoutMs != null\n ? { signal: AbortSignal.timeout(timeoutMs) }\n : {}),\n },\n }),\n parseResponse: async (res) => {\n let data: Record<string, unknown>;\n try {\n data = await res.json();\n } catch {\n throw new Error(\n `[Directive] Ollama returned non-JSON response. Is Ollama running at ${baseURL}? Start it with: ollama serve`,\n );\n }\n const text =\n ((data.message as Record<string, unknown>)?.content as string) ?? \"\";\n const inputTokens = (data.prompt_eval_count as number) ?? 0;\n const outputTokens = (data.eval_count as number) ?? 0;\n\n return {\n text,\n totalTokens: inputTokens + outputTokens,\n inputTokens,\n outputTokens,\n };\n },\n });\n}\n"]}
1
+ {"version":3,"sources":["../src/agent-utils.ts","../src/adapters/ollama.ts"],"names":["ALLOWED_PROTOCOLS","validateBaseURL","baseURL","url","err","createRunner","options","fetchFn","buildRequest","parseResponse","parseOutput","hooks","parse","text","agent","input","runOptions","startTime","messages","init","fetchInit","response","errBody","parsed","tokenUsage","assistantMessage","allMessages","durationMs","createOllamaRunner","model","timeoutMs","_input","m","res","data","inputTokens","outputTokens"],"mappings":"AAkEA,IAAMA,CAAAA,CAAoB,IAAI,GAAA,CAAI,CAAC,OAAA,CAAS,QAAQ,CAAC,CAAA,CAU9C,SAASC,CAAAA,CAAgBC,CAAAA,CAAuB,CACrD,GAAI,CACF,IAAMC,CAAAA,CAAM,IAAI,GAAA,CAAID,CAAO,CAAA,CAC3B,GAAI,CAACF,CAAAA,CAAkB,GAAA,CAAIG,CAAAA,CAAI,QAAQ,CAAA,CACrC,MAAM,IAAI,KAAA,CACR,CAAA,sCAAA,EAAyCA,CAAAA,CAAI,QAAQ,CAAA,0CAAA,CACvD,CAEJ,CAAA,MAASC,CAAAA,CAAK,CACZ,MAAIA,CAAAA,YAAe,KAAA,EAASA,CAAAA,CAAI,QAAQ,UAAA,CAAW,aAAa,CAAA,CACxDA,CAAAA,CAGF,IAAI,KAAA,CACR,CAAA,6BAAA,EAAgCF,CAAO,CAAA,+DAAA,CACzC,CACF,CACF,CAqFO,SAASG,CAAAA,CAAaC,EAA2C,CACtE,GAAM,CACJ,KAAA,CAAOC,CAAAA,CAAU,UAAA,CAAW,MAC5B,YAAA,CAAAC,CAAAA,CACA,aAAA,CAAAC,CAAAA,CACA,WAAA,CAAAC,CAAAA,CACA,MAAAC,CACF,CAAA,CAAIL,CAAAA,CAUEM,CAAAA,CAAQF,CAAAA,GARiBG,CAAAA,EAAoB,CACjD,GAAI,CACF,OAAO,IAAA,CAAK,KAAA,CAAMA,CAAI,CACxB,MAAQ,CACN,OAAOA,CACT,CACF,CAAA,CAAA,CAIA,OAAO,MACLC,CAAAA,CACAC,CAAAA,CACAC,CAAAA,GAC0B,CAC1B,IAAMC,CAAAA,CAAY,IAAA,CAAK,KAAI,CAC3BN,CAAAA,EAAO,YAAA,GAAe,CAAE,KAAA,CAAAG,CAAAA,CAAO,KAAA,CAAAC,CAAAA,CAAO,SAAA,CAAWE,CAAU,CAAC,CAAA,CAE5D,IAAMC,CAAAA,CAAsB,CAAC,CAAE,IAAA,CAAM,MAAA,CAAQ,OAAA,CAASH,CAAM,CAAC,EAE7D,GAAI,CACF,GAAM,CAAE,GAAA,CAAAZ,CAAAA,CAAK,KAAAgB,CAAK,CAAA,CAAIX,CAAAA,CAAaM,CAAAA,CAAOC,CAAAA,CAAOG,CAAQ,CAAA,CAEnDE,CAAAA,CAAyBJ,CAAAA,EAAY,MAAA,CACvC,CAAE,GAAGG,CAAAA,CAAM,MAAA,CAAQH,EAAW,MAAO,CAAA,CACrCG,CAAAA,CAEEE,CAAAA,CAAW,MAAMd,CAAAA,CAAQJ,CAAAA,CAAKiB,CAAS,CAAA,CAE7C,GAAI,CAACC,CAAAA,CAAS,EAAA,CAAI,CAChB,IAAMC,CAAAA,CAAU,MAAMD,CAAAA,CAAS,IAAA,EAAK,CAAE,KAAA,CAAM,IAAM,EAAE,CAAA,CAEpD,MAAM,IAAI,KAAA,CACR,CAAA,wCAAA,EAA2CA,CAAAA,CAAS,MAAM,CAAA,CAAA,EAAIA,CAAAA,CAAS,UAAU,CAAA,EAAGC,CAAAA,CAAU,CAAA,QAAA,EAAMA,EAAQ,KAAA,CAAM,CAAA,CAAG,GAAG,CAAC,CAAA,CAAA,CAAK,EAAE,EAClI,CACF,CAEA,IAAMC,CAAAA,CAAS,MAAMd,CAAAA,CAAcY,CAAAA,CAAUH,CAAQ,CAAA,CAC/CM,CAAAA,CAAyB,CAC7B,WAAA,CAAaD,CAAAA,CAAO,WAAA,EAAe,EACnC,YAAA,CAAcA,CAAAA,CAAO,YAAA,EAAgB,CACvC,CAAA,CAEME,CAAAA,CAA4B,CAChC,IAAA,CAAM,WAAA,CACN,OAAA,CAASF,CAAAA,CAAO,IAClB,CAAA,CACMG,CAAAA,CAAyB,CAAC,GAAGR,CAAAA,CAAUO,CAAgB,CAAA,CAE7DT,CAAAA,EAAY,SAAA,GAAYS,CAAgB,CAAA,CAExC,IAAME,CAAAA,CAAa,IAAA,CAAK,GAAA,EAAI,CAAIV,CAAAA,CAChC,OAAAN,CAAAA,EAAO,WAAA,GAAc,CACnB,KAAA,CAAAG,CAAAA,CACA,KAAA,CAAAC,EACA,MAAA,CAAQQ,CAAAA,CAAO,IAAA,CACf,WAAA,CAAaA,CAAAA,CAAO,WAAA,CACpB,WAAAC,CAAAA,CACA,UAAA,CAAAG,CAAAA,CACA,SAAA,CAAW,IAAA,CAAK,GAAA,EAClB,CAAC,CAAA,CAEM,CACL,MAAA,CAAQf,CAAAA,CAASW,CAAAA,CAAO,IAAI,EAC5B,QAAA,CAAUG,CAAAA,CACV,SAAA,CAAW,EAAC,CACZ,WAAA,CAAaH,CAAAA,CAAO,WAAA,CACpB,UAAA,CAAAC,CACF,CACF,CAAA,MAASpB,CAAAA,CAAK,CACZ,IAAMuB,CAAAA,CAAa,IAAA,CAAK,GAAA,EAAI,CAAIV,CAAAA,CAChC,MAAIb,CAAAA,YAAe,KAAA,EACjBO,CAAAA,EAAO,OAAA,GAAU,CACf,KAAA,CAAAG,CAAAA,CACA,KAAA,CAAAC,EACA,KAAA,CAAOX,CAAAA,CACP,UAAA,CAAAuB,CAAAA,CACA,SAAA,CAAW,IAAA,CAAK,KAClB,CAAC,CAAA,CAGGvB,CACR,CACF,CACF,CC7NO,SAASwB,CAAAA,CACdtB,CAAAA,CAA+B,EAAC,CACnB,CACb,GAAM,CACJ,KAAA,CAAAuB,CAAAA,CAAQ,QAAA,CACR,OAAA,CAAA3B,CAAAA,CAAU,wBAAA,CACV,MAAOK,CAAAA,CAAU,UAAA,CAAW,KAAA,CAC5B,SAAA,CAAAuB,CAAAA,CACA,KAAA,CAAAnB,CACF,CAAA,CAAIL,CAAAA,CAEJ,OAAAL,CAAAA,CAAgBC,CAAO,CAAA,CAEhBG,CAAAA,CAAa,CAClB,KAAA,CAAOE,CAAAA,CACP,KAAA,CAAAI,CAAAA,CACA,YAAA,CAAc,CAACG,CAAAA,CAAOiB,CAAAA,CAAQb,CAAAA,IAAc,CAC1C,GAAA,CAAK,CAAA,EAAGhB,CAAO,CAAA,SAAA,CAAA,CACf,KAAM,CACJ,MAAA,CAAQ,MAAA,CACR,OAAA,CAAS,CAAE,cAAA,CAAgB,kBAAmB,CAAA,CAC9C,IAAA,CAAM,IAAA,CAAK,SAAA,CAAU,CACnB,KAAA,CAAOY,EAAM,KAAA,EAASe,CAAAA,CACtB,QAAA,CAAU,CACR,GAAIf,CAAAA,CAAM,YAAA,CACN,CAAC,CAAE,IAAA,CAAM,QAAA,CAAU,OAAA,CAASA,CAAAA,CAAM,YAAa,CAAC,CAAA,CAChD,EAAC,CACL,GAAGI,CAAAA,CAAS,GAAA,CAAKc,CAAAA,GAAO,CAAE,IAAA,CAAMA,CAAAA,CAAE,IAAA,CAAM,OAAA,CAASA,CAAAA,CAAE,OAAQ,EAAE,CAC/D,CAAA,CACA,MAAA,CAAQ,KACV,CAAC,CAAA,CACD,GAAIF,CAAAA,EAAa,IAAA,CACb,CAAE,MAAA,CAAQ,WAAA,CAAY,OAAA,CAAQA,CAAS,CAAE,CAAA,CACzC,EACN,CACF,CAAA,CAAA,CACA,aAAA,CAAe,MAAOG,CAAAA,EAAQ,CAC5B,IAAIC,CAAAA,CACJ,GAAI,CACFA,EAAO,MAAMD,CAAAA,CAAI,IAAA,GACnB,CAAA,KAAQ,CACN,MAAM,IAAI,KAAA,CACR,CAAA,oEAAA,EAAuE/B,CAAO,CAAA,6BAAA,CAChF,CACF,CACA,IAAMW,CAAAA,CACFqB,CAAAA,CAAK,OAAA,EAAqC,OAAA,EAAsB,EAAA,CAC9DC,CAAAA,CAAeD,CAAAA,CAAK,iBAAA,EAAgC,CAAA,CACpDE,CAAAA,CAAgBF,CAAAA,CAAK,UAAA,EAAyB,CAAA,CAEpD,OAAO,CACL,IAAA,CAAArB,CAAAA,CACA,WAAA,CAAasB,CAAAA,CAAcC,CAAAA,CAC3B,WAAA,CAAAD,CAAAA,CACA,YAAA,CAAAC,CACF,CACF,CACF,CAAC,CACH","file":"ollama.js","sourcesContent":["/**\n * Agent utilities — createRunner, estimateCost, state queries, URL validation.\n */\n\nimport type {\n AdapterHooks,\n AgentLike,\n AgentRunner,\n AgentState,\n ApprovalState,\n Message,\n RunOptions,\n RunResult,\n TokenUsage,\n} from \"./types.js\";\n\n// ============================================================================\n// State Query Helpers\n// ============================================================================\n\n/**\n * Check whether an agent is currently executing a run.\n *\n * @param state - The current {@link AgentState} to inspect.\n * @returns `true` when the agent status is `\"running\"`.\n */\nexport function isAgentRunning(state: AgentState): boolean {\n return state.status === \"running\";\n}\n\n/**\n * Check whether there are tool-call approvals waiting for user confirmation.\n *\n * @param state - The current {@link ApprovalState} to inspect.\n * @returns `true` when one or more approvals are pending.\n */\nexport function hasPendingApprovals(state: ApprovalState): boolean {\n return state.pending.length > 0;\n}\n\n// ============================================================================\n// Cost Estimation\n// ============================================================================\n\n/**\n * Estimate the dollar cost of an agent run based on total token usage.\n *\n * @remarks\n * No default rate is provided — callers must supply the current per-million-token\n * price to avoid silently using stale pricing.\n *\n * @param tokenUsage - Total number of tokens consumed (input + output).\n * @param ratePerMillionTokens - Cost in dollars per one million tokens.\n * @returns Estimated cost in dollars.\n */\nexport function estimateCost(\n tokenUsage: number,\n ratePerMillionTokens: number,\n): number {\n return (tokenUsage / 1_000_000) * ratePerMillionTokens;\n}\n\n// ============================================================================\n// Validation Helpers\n// ============================================================================\n\nconst ALLOWED_PROTOCOLS = new Set([\"http:\", \"https:\"]);\n\n/**\n * Validate that a base URL uses the `http:` or `https:` protocol.\n * Throws immediately at adapter creation time (not at call time) to surface\n * configuration errors before any LLM requests are made.\n *\n * @param baseURL - The base URL string to validate.\n * @throws When the URL is malformed or uses a protocol other than `http:` or `https:`.\n */\nexport function validateBaseURL(baseURL: string): void {\n try {\n const url = new URL(baseURL);\n if (!ALLOWED_PROTOCOLS.has(url.protocol)) {\n throw new Error(\n `[Directive] Invalid baseURL protocol \"${url.protocol}\" – only http: and https: are allowed`,\n );\n }\n } catch (err) {\n if (err instanceof Error && err.message.startsWith(\"[Directive]\")) {\n throw err;\n }\n\n throw new Error(\n `[Directive] Invalid baseURL \"${baseURL}\" – must be a valid URL (e.g. \"https://api.openai.com/v1\")`,\n );\n }\n}\n\n// ============================================================================\n// createRunner Helper\n// ============================================================================\n\n/** Parsed response from an LLM provider */\nexport interface ParsedResponse {\n text: string;\n totalTokens: number;\n /** Input token count, when available from the provider */\n inputTokens?: number;\n /** Output token count, when available from the provider */\n outputTokens?: number;\n}\n\n/** Options for creating an AgentRunner from buildRequest/parseResponse */\nexport interface CreateRunnerOptions {\n fetch?: typeof globalThis.fetch;\n buildRequest: (\n agent: AgentLike,\n input: string,\n messages: Message[],\n ) => { url: string; init: RequestInit };\n parseResponse: (\n response: Response,\n messages: Message[],\n ) => Promise<ParsedResponse>;\n parseOutput?: <T>(text: string) => T;\n /** Lifecycle hooks for tracing, logging, and metrics */\n hooks?: AdapterHooks;\n}\n\n/**\n * Create an {@link AgentRunner} from `buildRequest`/`parseResponse` helpers, reducing\n * ~50 lines of fetch boilerplate to ~20 lines of configuration.\n *\n * @remarks\n * Supports lifecycle hooks for observability:\n * - `onBeforeCall` fires before each API request\n * - `onAfterCall` fires after a successful response (includes token breakdown)\n * - `onError` fires when the request fails\n *\n * Output parsing defaults to `JSON.parse` with a string fallback. Supply a custom\n * `parseOutput` to override (e.g. for structured output schemas).\n *\n * @param options - Configuration for the runner, including request building, response parsing, and hooks.\n * @returns An {@link AgentRunner} function that performs LLM calls via fetch.\n *\n * @example\n * ```typescript\n * const runClaude = createRunner({\n * buildRequest: (agent, input) => ({\n * url: \"/api/claude\",\n * init: {\n * method: \"POST\",\n * headers: { \"Content-Type\": \"application/json\" },\n * body: JSON.stringify({\n * model: agent.model ?? \"claude-haiku-4-5-20251001\",\n * system: agent.instructions ?? \"\",\n * messages: [{ role: \"user\", content: input }],\n * }),\n * },\n * }),\n * parseResponse: async (res) => {\n * const data = await res.json();\n * const inputTokens = data.usage?.input_tokens ?? 0;\n * const outputTokens = data.usage?.output_tokens ?? 0;\n * return {\n * text: data.content?.[0]?.text ?? \"\",\n * totalTokens: inputTokens + outputTokens,\n * inputTokens,\n * outputTokens,\n * };\n * },\n * hooks: {\n * onAfterCall: ({ durationMs, tokenUsage }) => {\n * console.log(`LLM call: ${durationMs}ms, ${tokenUsage.inputTokens}in/${tokenUsage.outputTokens}out`);\n * },\n * },\n * });\n * ```\n *\n * @public\n */\nexport function createRunner(options: CreateRunnerOptions): AgentRunner {\n const {\n fetch: fetchFn = globalThis.fetch,\n buildRequest,\n parseResponse,\n parseOutput,\n hooks,\n } = options;\n\n const defaultParseOutput = <T>(text: string): T => {\n try {\n return JSON.parse(text) as T;\n } catch {\n return text as unknown as T;\n }\n };\n\n const parse = parseOutput ?? defaultParseOutput;\n\n return async <T = unknown>(\n agent: AgentLike,\n input: string,\n runOptions?: RunOptions,\n ): Promise<RunResult<T>> => {\n const startTime = Date.now();\n hooks?.onBeforeCall?.({ agent, input, timestamp: startTime });\n\n const messages: Message[] = [{ role: \"user\", content: input }];\n\n try {\n const { url, init } = buildRequest(agent, input, messages);\n\n const fetchInit: RequestInit = runOptions?.signal\n ? { ...init, signal: runOptions.signal }\n : init;\n\n const response = await fetchFn(url, fetchInit);\n\n if (!response.ok) {\n const errBody = await response.text().catch(() => \"\");\n\n throw new Error(\n `[Directive] AgentRunner request failed: ${response.status} ${response.statusText}${errBody ? ` – ${errBody.slice(0, 300)}` : \"\"}`,\n );\n }\n\n const parsed = await parseResponse(response, messages);\n const tokenUsage: TokenUsage = {\n inputTokens: parsed.inputTokens ?? 0,\n outputTokens: parsed.outputTokens ?? 0,\n };\n\n const assistantMessage: Message = {\n role: \"assistant\",\n content: parsed.text,\n };\n const allMessages: Message[] = [...messages, assistantMessage];\n\n runOptions?.onMessage?.(assistantMessage);\n\n const durationMs = Date.now() - startTime;\n hooks?.onAfterCall?.({\n agent,\n input,\n output: parsed.text,\n totalTokens: parsed.totalTokens,\n tokenUsage,\n durationMs,\n timestamp: Date.now(),\n });\n\n return {\n output: parse<T>(parsed.text),\n messages: allMessages,\n toolCalls: [],\n totalTokens: parsed.totalTokens,\n tokenUsage,\n };\n } catch (err) {\n const durationMs = Date.now() - startTime;\n if (err instanceof Error) {\n hooks?.onError?.({\n agent,\n input,\n error: err,\n durationMs,\n timestamp: Date.now(),\n });\n }\n\n throw err;\n }\n };\n}\n","/**\n * @directive-run/ai/ollama\n *\n * Ollama adapter for Directive AI. Provides runners for local\n * Ollama inference. No API key required.\n *\n * Requires Ollama to be running locally. Start it with: `ollama serve`\n *\n * @example\n * ```typescript\n * import { createOllamaRunner } from '@directive-run/ai/ollama';\n *\n * const runner = createOllamaRunner({ model: 'llama3' });\n * ```\n */\n\nimport { createRunner, validateBaseURL } from \"../agent-utils.js\";\nimport type { AdapterHooks, AgentRunner } from \"../types.js\";\n\n// ============================================================================\n// Ollama Runner\n// ============================================================================\n\n/** Options for createOllamaRunner */\nexport interface OllamaRunnerOptions {\n model?: string;\n baseURL?: string;\n fetch?: typeof globalThis.fetch;\n /** @default undefined */\n timeoutMs?: number;\n /** Lifecycle hooks for tracing, logging, and metrics */\n hooks?: AdapterHooks;\n}\n\n/**\n * Create an AgentRunner for local Ollama inference.\n *\n * Ollama runs locally – no API key or cloud service needed. Default model\n * is `llama3`, default base URL is `http://localhost:11434`.\n *\n * Returns `tokenUsage` with input/output breakdown for cost tracking\n * (useful for monitoring local resource usage).\n *\n * @example\n * ```typescript\n * const runner = createOllamaRunner({ model: \"llama3\" });\n * const orchestrator = createAgentOrchestrator({ runner });\n * const result = await orchestrator.run(agent, input);\n * ```\n */\nexport function createOllamaRunner(\n options: OllamaRunnerOptions = {},\n): AgentRunner {\n const {\n model = \"llama3\",\n baseURL = \"http://localhost:11434\",\n fetch: fetchFn = globalThis.fetch,\n timeoutMs,\n hooks,\n } = options;\n\n validateBaseURL(baseURL);\n\n return createRunner({\n fetch: fetchFn,\n hooks,\n buildRequest: (agent, _input, messages) => ({\n url: `${baseURL}/api/chat`,\n init: {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n model: agent.model ?? model,\n messages: [\n ...(agent.instructions\n ? [{ role: \"system\", content: agent.instructions }]\n : []),\n ...messages.map((m) => ({ role: m.role, content: m.content })),\n ],\n stream: false,\n }),\n ...(timeoutMs != null\n ? { signal: AbortSignal.timeout(timeoutMs) }\n : {}),\n },\n }),\n parseResponse: async (res) => {\n let data: Record<string, unknown>;\n try {\n data = await res.json();\n } catch {\n throw new Error(\n `[Directive] Ollama returned non-JSON response. Is Ollama running at ${baseURL}? Start it with: ollama serve`,\n );\n }\n const text =\n ((data.message as Record<string, unknown>)?.content as string) ?? \"\";\n const inputTokens = (data.prompt_eval_count as number) ?? 0;\n const outputTokens = (data.eval_count as number) ?? 0;\n\n return {\n text,\n totalTokens: inputTokens + outputTokens,\n inputTokens,\n outputTokens,\n };\n },\n });\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/agent-utils.ts","../src/adapters/openai.ts"],"names":["ALLOWED_PROTOCOLS","validateBaseURL","baseURL","url","err","createRunner","options","fetchFn","buildRequest","parseResponse","parseOutput","hooks","parse","text","agent","input","runOptions","startTime","messages","init","fetchInit","response","errBody","parsed","tokenUsage","assistantMessage","allMessages","durationMs","OPENAI_PRICING","createOpenAIRunner","apiKey","model","maxTokens","timeoutMs","_input","m","res","data","inputTokens","outputTokens","createOpenAIEmbedder","dimensions","entry","createOpenAIStreamingRunner","callbacks","reader","decoder","buf","fullText","promptTokens","completionTokens","done","value","lines","line","event","delta","parseErr","assistantMsg","totalTokens"],"mappings":"aAoDA,IAAMA,CAAAA,CAAoB,IAAI,GAAA,CAAI,CAAC,QAAS,QAAQ,CAAC,CAAA,CAM9C,SAASC,CAAAA,CAAgBC,CAAAA,CAAuB,CACrD,GAAI,CACF,IAAMC,CAAAA,CAAM,IAAI,GAAA,CAAID,CAAO,CAAA,CAC3B,GAAI,CAACF,CAAAA,CAAkB,GAAA,CAAIG,CAAAA,CAAI,QAAQ,CAAA,CACrC,MAAM,IAAI,KAAA,CACR,CAAA,sCAAA,EAAyCA,CAAAA,CAAI,QAAQ,CAAA,0CAAA,CACvD,CAEJ,CAAA,MAASC,CAAAA,CAAK,CACZ,MAAIA,aAAe,KAAA,EAASA,CAAAA,CAAI,OAAA,CAAQ,UAAA,CAAW,aAAa,CAAA,CACxDA,EAGF,IAAI,KAAA,CACR,CAAA,6BAAA,EAAgCF,CAAO,CAAA,+DAAA,CACzC,CACF,CACF,CA4EO,SAASG,CAAAA,CAAaC,CAAAA,CAA2C,CACtE,GAAM,CACJ,KAAA,CAAOC,CAAAA,CAAU,UAAA,CAAW,KAAA,CAC5B,YAAA,CAAAC,CAAAA,CACA,cAAAC,CAAAA,CACA,WAAA,CAAAC,CAAAA,CACA,KAAA,CAAAC,CACF,CAAA,CAAIL,EAUEM,CAAAA,CAAQF,CAAAA,GARiBG,CAAAA,EAAoB,CACjD,GAAI,CACF,OAAO,IAAA,CAAK,KAAA,CAAMA,CAAI,CACxB,CAAA,KAAQ,CACN,OAAOA,CACT,CACF,CAAA,CAAA,CAIA,OAAO,MACLC,CAAAA,CACAC,EACAC,CAAAA,GAC0B,CAC1B,IAAMC,CAAAA,CAAY,IAAA,CAAK,GAAA,GACvBN,CAAAA,EAAO,YAAA,GAAe,CAAE,KAAA,CAAAG,CAAAA,CAAO,KAAA,CAAAC,EAAO,SAAA,CAAWE,CAAU,CAAC,CAAA,CAE5D,IAAMC,EAAsB,CAAC,CAAE,IAAA,CAAM,MAAA,CAAQ,OAAA,CAASH,CAAM,CAAC,CAAA,CAE7D,GAAI,CACF,GAAM,CAAE,GAAA,CAAAZ,EAAK,IAAA,CAAAgB,CAAK,CAAA,CAAIX,CAAAA,CAAaM,CAAAA,CAAOC,CAAAA,CAAOG,CAAQ,CAAA,CAEnDE,CAAAA,CAAyBJ,CAAAA,EAAY,MAAA,CACvC,CAAE,GAAGG,EAAM,MAAA,CAAQH,CAAAA,CAAW,MAAO,CAAA,CACrCG,CAAAA,CAEEE,CAAAA,CAAW,MAAMd,CAAAA,CAAQJ,CAAAA,CAAKiB,CAAS,CAAA,CAE7C,GAAI,CAACC,EAAS,EAAA,CAAI,CAChB,IAAMC,CAAAA,CAAU,MAAMD,CAAAA,CAAS,MAAK,CAAE,KAAA,CAAM,IAAM,EAAE,CAAA,CAEpD,MAAM,IAAI,KAAA,CACR,CAAA,wCAAA,EAA2CA,CAAAA,CAAS,MAAM,CAAA,CAAA,EAAIA,CAAAA,CAAS,UAAU,CAAA,EAAGC,CAAAA,CAAU,CAAA,QAAA,EAAMA,CAAAA,CAAQ,KAAA,CAAM,CAAA,CAAG,GAAG,CAAC,CAAA,CAAA,CAAK,EAAE,CAAA,CAClI,CACF,CAEA,IAAMC,CAAAA,CAAS,MAAMd,CAAAA,CAAcY,CAAAA,CAAUH,CAAQ,CAAA,CAC/CM,EAAyB,CAC7B,WAAA,CAAaD,CAAAA,CAAO,WAAA,EAAe,CAAA,CACnC,YAAA,CAAcA,EAAO,YAAA,EAAgB,CACvC,CAAA,CAEME,CAAAA,CAA4B,CAChC,IAAA,CAAM,YACN,OAAA,CAASF,CAAAA,CAAO,IAClB,CAAA,CACMG,CAAAA,CAAyB,CAAC,GAAGR,CAAAA,CAAUO,CAAgB,EAE7DT,CAAAA,EAAY,SAAA,GAAYS,CAAgB,CAAA,CAExC,IAAME,CAAAA,CAAa,IAAA,CAAK,GAAA,EAAI,CAAIV,EAChC,OAAAN,CAAAA,EAAO,WAAA,GAAc,CACnB,KAAA,CAAAG,CAAAA,CACA,MAAAC,CAAAA,CACA,MAAA,CAAQQ,CAAAA,CAAO,IAAA,CACf,WAAA,CAAaA,CAAAA,CAAO,YACpB,UAAA,CAAAC,CAAAA,CACA,UAAA,CAAAG,CAAAA,CACA,SAAA,CAAW,IAAA,CAAK,KAClB,CAAC,CAAA,CAEM,CACL,MAAA,CAAQf,CAAAA,CAASW,EAAO,IAAI,CAAA,CAC5B,QAAA,CAAUG,CAAAA,CACV,SAAA,CAAW,GACX,WAAA,CAAaH,CAAAA,CAAO,WAAA,CACpB,UAAA,CAAAC,CACF,CACF,OAASpB,CAAAA,CAAK,CACZ,IAAMuB,CAAAA,CAAa,IAAA,CAAK,GAAA,GAAQV,CAAAA,CAChC,MAAIb,CAAAA,YAAe,KAAA,EACjBO,CAAAA,EAAO,OAAA,GAAU,CACf,KAAA,CAAAG,CAAAA,CACA,KAAA,CAAAC,CAAAA,CACA,KAAA,CAAOX,CAAAA,CACP,WAAAuB,CAAAA,CACA,SAAA,CAAW,IAAA,CAAK,GAAA,EAClB,CAAC,EAGGvB,CACR,CACF,CACF,CCvMO,IAAMwB,CAAAA,CACX,CACE,QAAA,CAAU,CAAE,KAAA,CAAO,GAAA,CAAK,MAAA,CAAQ,EAAG,EACnC,aAAA,CAAe,CAAE,KAAA,CAAO,GAAA,CAAM,MAAA,CAAQ,EAAI,EAC1C,aAAA,CAAe,CAAE,KAAA,CAAO,EAAA,CAAI,MAAA,CAAQ,EAAG,EACvC,SAAA,CAAW,CAAE,KAAA,CAAO,GAAA,CAAK,MAAA,CAAQ,GAAI,CACvC,EA0CK,SAASC,CAAAA,CAAmBvB,CAAAA,CAA2C,CAC5E,GAAM,CACJ,MAAA,CAAAwB,CAAAA,CACA,KAAA,CAAAC,CAAAA,CAAQ,QAAA,CACR,SAAA,CAAAC,EACA,OAAA,CAAA9B,CAAAA,CAAU,2BAAA,CACV,KAAA,CAAOK,CAAAA,CAAU,UAAA,CAAW,MAC5B,SAAA,CAAA0B,CAAAA,CACA,KAAA,CAAAtB,CACF,CAAA,CAAIL,CAAAA,CAEJ,OAAAL,CAAAA,CAAgBC,CAAO,CAAA,CAGrB,OAAO,OAAA,CAAY,GAAA,EACnB,QAAQ,GAAA,EAAK,QAAA,GAAa,YAAA,EAC1B,CAAC4B,CAAAA,EAED,OAAA,CAAQ,KACN,uEACF,CAAA,CAGKzB,CAAAA,CAAa,CAClB,KAAA,CAAOE,CAAAA,CACP,MAAAI,CAAAA,CACA,YAAA,CAAc,CAACG,CAAAA,CAAOoB,CAAAA,CAAQhB,CAAAA,IAAc,CAC1C,GAAA,CAAK,CAAA,EAAGhB,CAAO,CAAA,iBAAA,CAAA,CACf,IAAA,CAAM,CACJ,OAAQ,MAAA,CACR,OAAA,CAAS,CACP,cAAA,CAAgB,kBAAA,CAChB,aAAA,CAAe,UAAU4B,CAAM,CAAA,CACjC,CAAA,CACA,IAAA,CAAM,IAAA,CAAK,SAAA,CAAU,CACnB,KAAA,CAAOhB,CAAAA,CAAM,KAAA,EAASiB,CAAAA,CACtB,GAAIC,CAAAA,EAAa,KAAO,CAAE,UAAA,CAAYA,CAAU,CAAA,CAAI,EAAC,CACrD,SAAU,CACR,GAAIlB,CAAAA,CAAM,YAAA,CACN,CAAC,CAAE,KAAM,QAAA,CAAU,OAAA,CAASA,CAAAA,CAAM,YAAa,CAAC,CAAA,CAChD,EAAC,CACL,GAAGI,EAAS,GAAA,CAAKiB,CAAAA,GAAO,CAAE,IAAA,CAAMA,CAAAA,CAAE,IAAA,CAAM,OAAA,CAASA,CAAAA,CAAE,OAAQ,EAAE,CAC/D,CACF,CAAC,CAAA,CACD,GAAIF,CAAAA,EAAa,KACb,CAAE,MAAA,CAAQ,WAAA,CAAY,OAAA,CAAQA,CAAS,CAAE,EACzC,EACN,CACF,CAAA,CAAA,CACA,aAAA,CAAe,MAAOG,GAAQ,CAC5B,IAAMC,CAAAA,CAAO,MAAMD,CAAAA,CAAI,IAAA,GACjBvB,CAAAA,CAAOwB,CAAAA,CAAK,OAAA,GAAU,CAAC,CAAA,EAAG,OAAA,EAAS,SAAW,EAAA,CAC9CC,CAAAA,CAAcD,CAAAA,CAAK,KAAA,EAAO,aAAA,EAAiB,CAAA,CAC3CE,EAAeF,CAAAA,CAAK,KAAA,EAAO,iBAAA,EAAqB,CAAA,CAEtD,OAAO,CACL,KAAAxB,CAAAA,CACA,WAAA,CAAayB,CAAAA,CAAcC,CAAAA,CAC3B,WAAA,CAAAD,CAAAA,CACA,aAAAC,CACF,CACF,CACF,CAAC,CACH,CA0BO,SAASC,CAAAA,CACdlC,CAAAA,CACY,CACZ,GAAM,CACJ,MAAA,CAAAwB,EACA,KAAA,CAAAC,CAAAA,CAAQ,wBAAA,CACR,UAAA,CAAAU,CAAAA,CAAa,IAAA,CACb,QAAAvC,CAAAA,CAAU,2BAAA,CACV,KAAA,CAAOK,CAAAA,CAAU,UAAA,CAAW,KAAA,CAC5B,UAAA0B,CACF,CAAA,CAAI3B,CAAAA,CAEJ,OAAAL,CAAAA,CAAgBC,CAAO,EAGrB,OAAO,OAAA,CAAY,GAAA,EACnB,OAAA,CAAQ,GAAA,EAAK,QAAA,GAAa,cAC1B,CAAC4B,CAAAA,EAED,QAAQ,IAAA,CACN,yEACF,EAGK,MAAOjB,CAAAA,EAAqC,CACjD,IAAMQ,CAAAA,CAAW,MAAMd,EAAQ,CAAA,EAAGL,CAAO,CAAA,WAAA,CAAA,CAAe,CACtD,MAAA,CAAQ,MAAA,CACR,QAAS,CACP,cAAA,CAAgB,kBAAA,CAChB,aAAA,CAAe,CAAA,OAAA,EAAU4B,CAAM,EACjC,CAAA,CACA,IAAA,CAAM,IAAA,CAAK,SAAA,CAAU,CAAE,KAAA,CAAAC,EAAO,KAAA,CAAOlB,CAAAA,CAAM,UAAA,CAAA4B,CAAW,CAAC,CAAA,CACvD,OAAQ,WAAA,CAAY,OAAA,CAAQR,CAAAA,EAAa,GAAM,CACjD,CAAC,EAED,GAAI,CAACZ,CAAAA,CAAS,EAAA,CAAI,CAChB,IAAMC,EAAU,MAAMD,CAAAA,CAAS,IAAA,EAAK,CAAE,KAAA,CAAM,IAAM,EAAE,CAAA,CAEpD,MAAM,IAAI,KAAA,CACR,CAAA,qCAAA,EAAwCA,CAAAA,CAAS,MAAM,CAAA,EAAGC,CAAAA,CAAU,CAAA,QAAA,EAAMA,CAAAA,CAAQ,KAAA,CAAM,CAAA,CAAG,GAAG,CAAC,CAAA,CAAA,CAAK,EAAE,CAAA,CACxG,CACF,CAMA,IAAMoB,CAAAA,CAAAA,CAJQ,MAAMrB,CAAAA,CAAS,IAAA,EAAK,EAIf,IAAA,CAAK,CAAC,CAAA,CACzB,GAAI,CAACqB,CAAAA,CACH,MAAM,IAAI,MACR,iEACF,CAAA,CAGF,OAAOA,CAAAA,CAAM,SACf,CACF,CAgCO,SAASC,CAAAA,CACdrC,CAAAA,CACyB,CACzB,GAAM,CACJ,OAAAwB,CAAAA,CACA,KAAA,CAAAC,EAAQ,QAAA,CACR,SAAA,CAAAC,EACA,OAAA,CAAA9B,CAAAA,CAAU,2BAAA,CACV,KAAA,CAAOK,CAAAA,CAAU,UAAA,CAAW,MAC5B,KAAA,CAAAI,CACF,CAAA,CAAIL,CAAAA,CAEJ,OAAAL,CAAAA,CAAgBC,CAAO,CAAA,CAGrB,OAAO,OAAA,CAAY,GAAA,EACnB,OAAA,CAAQ,GAAA,EAAK,WAAa,YAAA,EAC1B,CAAC4B,CAAAA,EAED,OAAA,CAAQ,IAAA,CACN,gFACF,EAGK,MAAOhB,CAAAA,CAAOC,CAAAA,CAAO6B,CAAAA,GAAc,CACxC,IAAM3B,EAAY,IAAA,CAAK,GAAA,EAAI,CAC3BN,CAAAA,EAAO,YAAA,GAAe,CAAE,MAAAG,CAAAA,CAAO,KAAA,CAAAC,CAAAA,CAAO,SAAA,CAAWE,CAAU,CAAC,EAE5D,GAAI,CACF,IAAMI,CAAAA,CAAW,MAAMd,CAAAA,CAAQ,GAAGL,CAAO,CAAA,iBAAA,CAAA,CAAqB,CAC5D,MAAA,CAAQ,MAAA,CACR,OAAA,CAAS,CACP,cAAA,CAAgB,kBAAA,CAChB,aAAA,CAAe,CAAA,OAAA,EAAU4B,CAAM,CAAA,CACjC,EACA,IAAA,CAAM,IAAA,CAAK,SAAA,CAAU,CACnB,KAAA,CAAOhB,CAAAA,CAAM,OAASiB,CAAAA,CACtB,GAAIC,CAAAA,EAAa,IAAA,CAAO,CAAE,UAAA,CAAYA,CAAU,CAAA,CAAI,EAAC,CACrD,QAAA,CAAU,CACR,GAAIlB,EAAM,YAAA,CACN,CAAC,CAAE,IAAA,CAAM,QAAA,CAAU,OAAA,CAASA,EAAM,YAAa,CAAC,CAAA,CAChD,EAAC,CACL,CAAE,KAAM,MAAA,CAAQ,OAAA,CAASC,CAAM,CACjC,CAAA,CACA,MAAA,CAAQ,GACR,cAAA,CAAgB,CAAE,aAAA,CAAe,CAAA,CAAK,CACxC,CAAC,EACD,MAAA,CAAQ6B,CAAAA,CAAU,MACpB,CAAC,CAAA,CAED,GAAI,CAACvB,CAAAA,CAAS,EAAA,CAAI,CAChB,IAAMC,CAAAA,CAAU,MAAMD,EAAS,IAAA,EAAK,CAAE,KAAA,CAAM,IAAM,EAAE,CAAA,CAEpD,MAAM,IAAI,KAAA,CACR,CAAA,mCAAA,EAAsCA,CAAAA,CAAS,MAAM,CAAA,EAAGC,EAAU,CAAA,QAAA,EAAMA,CAAAA,CAAQ,KAAA,CAAM,CAAA,CAAG,GAAG,CAAC,GAAK,EAAE,CAAA,CACtG,CACF,CAEA,IAAMuB,CAAAA,CAASxB,EAAS,IAAA,EAAM,SAAA,EAAU,CACxC,GAAI,CAACwB,CAAAA,CACH,MAAM,IAAI,KAAA,CAAM,8BAA8B,CAAA,CAGhD,IAAMC,CAAAA,CAAU,IAAI,WAAA,CAChBC,CAAAA,CAAM,EAAA,CACNC,CAAAA,CAAW,EAAA,CACXC,CAAAA,CAAe,EACfC,CAAAA,CAAmB,CAAA,CAEvB,GAAI,CACF,OAAa,CACX,GAAM,CAAE,IAAA,CAAAC,CAAAA,CAAM,KAAA,CAAAC,CAAM,CAAA,CAAI,MAAMP,CAAAA,CAAO,IAAA,EAAK,CAC1C,GAAIM,CAAAA,CACF,MAGFJ,GAAOD,CAAAA,CAAQ,MAAA,CAAOM,CAAAA,CAAO,CAAE,MAAA,CAAQ,CAAA,CAAK,CAAC,CAAA,CAC7C,IAAMC,CAAAA,CAAQN,CAAAA,CAAI,KAAA,CAAM;AAAA,CAAI,CAAA,CAC5BA,EAAMM,CAAAA,CAAM,GAAA,IAAS,EAAA,CAErB,IAAA,IAAWC,KAAQD,CAAAA,CAAO,CACxB,GAAI,CAACC,CAAAA,CAAK,WAAW,QAAQ,CAAA,CAC3B,SAEF,IAAMjB,CAAAA,CAAOiB,EAAK,KAAA,CAAM,CAAC,EAAE,IAAA,EAAK,CAChC,GAAIjB,CAAAA,GAAS,QAAA,CAIb,GAAI,CACF,IAAMkB,EAAQ,IAAA,CAAK,KAAA,CAAMlB,CAAI,CAAA,CAGvBmB,CAAAA,CAAQD,EAAM,OAAA,GAAU,CAAC,GAAG,KAAA,CAC9BC,CAAAA,EAAO,OAAA,GACTR,CAAAA,EAAYQ,CAAAA,CAAM,OAAA,CAClBZ,EAAU,OAAA,GAAUY,CAAAA,CAAM,OAAO,CAAA,CAAA,CAI/BD,CAAAA,CAAM,QACRN,CAAAA,CAAeM,CAAAA,CAAM,MAAM,aAAA,EAAiB,CAAA,CAC5CL,EAAmBK,CAAAA,CAAM,KAAA,CAAM,mBAAqB,CAAA,EAExD,CAAA,MAASE,EAAU,CACjB,GAAIA,CAAAA,YAAoB,WAAA,CAEpB,OAAO,OAAA,CAAY,KACnB,OAAA,CAAQ,GAAA,EAAK,WAAa,aAAA,EAE1B,OAAA,CAAQ,KACN,8CAAA,CACApB,CACF,OAGF,MAAMoB,CAEV,CACF,CACF,CACF,QAAE,CACAZ,CAAAA,CAAO,QAAO,CAAE,KAAA,CAAM,IAAM,CAAC,CAAC,EAChC,CAEA,IAAMa,CAAAA,CAAwB,CAAE,IAAA,CAAM,WAAA,CAAa,QAASV,CAAS,CAAA,CACrEJ,EAAU,SAAA,GAAYc,CAAY,EAElC,IAAMlC,CAAAA,CAAyB,CAC7B,WAAA,CAAayB,CAAAA,CACb,aAAcC,CAChB,CAAA,CACMS,CAAAA,CAAcV,CAAAA,CAAeC,CAAAA,CAEnC,OAAAvC,GAAO,WAAA,GAAc,CACnB,MAAAG,CAAAA,CACA,KAAA,CAAAC,EACA,MAAA,CAAQiC,CAAAA,CACR,YAAAW,CAAAA,CACA,UAAA,CAAAnC,EACA,UAAA,CAAY,IAAA,CAAK,KAAI,CAAIP,CAAAA,CACzB,UAAW,IAAA,CAAK,GAAA,EAClB,CAAC,CAAA,CAEM,CACL,OAAQ+B,CAAAA,CACR,QAAA,CAAU,CAAC,CAAE,IAAA,CAAM,OAAiB,OAAA,CAASjC,CAAM,EAAG2C,CAAY,CAAA,CAClE,UAAW,EAAC,CACZ,YAAAC,CAAAA,CACA,UAAA,CAAAnC,CACF,CACF,CAAA,MAASpB,CAAAA,CAAK,CACZ,MAAIA,CAAAA,YAAe,OACjBO,CAAAA,EAAO,OAAA,GAAU,CACf,KAAA,CAAAG,CAAAA,CACA,MAAAC,CAAAA,CACA,KAAA,CAAOX,EACP,UAAA,CAAY,IAAA,CAAK,KAAI,CAAIa,CAAAA,CACzB,UAAW,IAAA,CAAK,GAAA,EAClB,CAAC,CAAA,CAGGb,CACR,CACF,CACF","file":"openai.cjs","sourcesContent":["/**\n * Agent utilities — createRunner, estimateCost, state queries, URL validation.\n */\n\nimport type {\n AdapterHooks,\n AgentLike,\n AgentRunner,\n AgentState,\n ApprovalState,\n Message,\n RunOptions,\n RunResult,\n TokenUsage,\n} from \"./types.js\";\n\n// ============================================================================\n// State Query Helpers\n// ============================================================================\n\n/** Check if agent is currently running. */\nexport function isAgentRunning(state: AgentState): boolean {\n return state.status === \"running\";\n}\n\n/** Check if there are pending approvals. */\nexport function hasPendingApprovals(state: ApprovalState): boolean {\n return state.pending.length > 0;\n}\n\n// ============================================================================\n// Cost Estimation\n// ============================================================================\n\n/**\n * Get total cost estimate based on token usage.\n *\n * @param tokenUsage - Total token count\n * @param ratePerMillionTokens - Cost per million tokens (required, no default to avoid stale pricing)\n * @returns Estimated cost in dollars\n */\nexport function estimateCost(\n tokenUsage: number,\n ratePerMillionTokens: number,\n): number {\n return (tokenUsage / 1_000_000) * ratePerMillionTokens;\n}\n\n// ============================================================================\n// Validation Helpers\n// ============================================================================\n\nconst ALLOWED_PROTOCOLS = new Set([\"http:\", \"https:\"]);\n\n/**\n * Validate that a baseURL uses http or https.\n * Throws immediately at adapter creation time (not at call time) to catch config errors early.\n */\nexport function validateBaseURL(baseURL: string): void {\n try {\n const url = new URL(baseURL);\n if (!ALLOWED_PROTOCOLS.has(url.protocol)) {\n throw new Error(\n `[Directive] Invalid baseURL protocol \"${url.protocol}\" – only http: and https: are allowed`,\n );\n }\n } catch (err) {\n if (err instanceof Error && err.message.startsWith(\"[Directive]\")) {\n throw err;\n }\n\n throw new Error(\n `[Directive] Invalid baseURL \"${baseURL}\" – must be a valid URL (e.g. \"https://api.openai.com/v1\")`,\n );\n }\n}\n\n// ============================================================================\n// createRunner Helper\n// ============================================================================\n\n/** Parsed response from an LLM provider */\nexport interface ParsedResponse {\n text: string;\n totalTokens: number;\n /** Input token count, when available from the provider */\n inputTokens?: number;\n /** Output token count, when available from the provider */\n outputTokens?: number;\n}\n\n/** Options for creating an AgentRunner from buildRequest/parseResponse */\nexport interface CreateRunnerOptions {\n fetch?: typeof globalThis.fetch;\n buildRequest: (\n agent: AgentLike,\n input: string,\n messages: Message[],\n ) => { url: string; init: RequestInit };\n parseResponse: (\n response: Response,\n messages: Message[],\n ) => Promise<ParsedResponse>;\n parseOutput?: <T>(text: string) => T;\n /** Lifecycle hooks for tracing, logging, and metrics */\n hooks?: AdapterHooks;\n}\n\n/**\n * Create an AgentRunner from buildRequest/parseResponse helpers.\n * Reduces ~50 lines of fetch boilerplate to ~20 lines of configuration.\n *\n * Supports lifecycle hooks for observability:\n * - `onBeforeCall` fires before each API request\n * - `onAfterCall` fires after a successful response (includes token breakdown)\n * - `onError` fires when the request fails\n *\n * @example\n * ```typescript\n * const runClaude = createRunner({\n * buildRequest: (agent, input) => ({\n * url: \"/api/claude\",\n * init: {\n * method: \"POST\",\n * headers: { \"Content-Type\": \"application/json\" },\n * body: JSON.stringify({\n * model: agent.model ?? \"claude-haiku-4-5-20251001\",\n * system: agent.instructions ?? \"\",\n * messages: [{ role: \"user\", content: input }],\n * }),\n * },\n * }),\n * parseResponse: async (res) => {\n * const data = await res.json();\n * const inputTokens = data.usage?.input_tokens ?? 0;\n * const outputTokens = data.usage?.output_tokens ?? 0;\n * return {\n * text: data.content?.[0]?.text ?? \"\",\n * totalTokens: inputTokens + outputTokens,\n * inputTokens,\n * outputTokens,\n * };\n * },\n * hooks: {\n * onAfterCall: ({ durationMs, tokenUsage }) => {\n * console.log(`LLM call: ${durationMs}ms, ${tokenUsage.inputTokens}in/${tokenUsage.outputTokens}out`);\n * },\n * },\n * });\n * ```\n */\nexport function createRunner(options: CreateRunnerOptions): AgentRunner {\n const {\n fetch: fetchFn = globalThis.fetch,\n buildRequest,\n parseResponse,\n parseOutput,\n hooks,\n } = options;\n\n const defaultParseOutput = <T>(text: string): T => {\n try {\n return JSON.parse(text) as T;\n } catch {\n return text as unknown as T;\n }\n };\n\n const parse = parseOutput ?? defaultParseOutput;\n\n return async <T = unknown>(\n agent: AgentLike,\n input: string,\n runOptions?: RunOptions,\n ): Promise<RunResult<T>> => {\n const startTime = Date.now();\n hooks?.onBeforeCall?.({ agent, input, timestamp: startTime });\n\n const messages: Message[] = [{ role: \"user\", content: input }];\n\n try {\n const { url, init } = buildRequest(agent, input, messages);\n\n const fetchInit: RequestInit = runOptions?.signal\n ? { ...init, signal: runOptions.signal }\n : init;\n\n const response = await fetchFn(url, fetchInit);\n\n if (!response.ok) {\n const errBody = await response.text().catch(() => \"\");\n\n throw new Error(\n `[Directive] AgentRunner request failed: ${response.status} ${response.statusText}${errBody ? ` – ${errBody.slice(0, 300)}` : \"\"}`,\n );\n }\n\n const parsed = await parseResponse(response, messages);\n const tokenUsage: TokenUsage = {\n inputTokens: parsed.inputTokens ?? 0,\n outputTokens: parsed.outputTokens ?? 0,\n };\n\n const assistantMessage: Message = {\n role: \"assistant\",\n content: parsed.text,\n };\n const allMessages: Message[] = [...messages, assistantMessage];\n\n runOptions?.onMessage?.(assistantMessage);\n\n const durationMs = Date.now() - startTime;\n hooks?.onAfterCall?.({\n agent,\n input,\n output: parsed.text,\n totalTokens: parsed.totalTokens,\n tokenUsage,\n durationMs,\n timestamp: Date.now(),\n });\n\n return {\n output: parse<T>(parsed.text),\n messages: allMessages,\n toolCalls: [],\n totalTokens: parsed.totalTokens,\n tokenUsage,\n };\n } catch (err) {\n const durationMs = Date.now() - startTime;\n if (err instanceof Error) {\n hooks?.onError?.({\n agent,\n input,\n error: err,\n durationMs,\n timestamp: Date.now(),\n });\n }\n\n throw err;\n }\n };\n}\n","/**\n * @directive-run/ai/openai\n *\n * OpenAI adapter for Directive AI. Provides runners and embedders\n * for OpenAI-compatible APIs (OpenAI, Azure, Together, etc.)\n *\n * @example\n * ```typescript\n * import { createOpenAIRunner, createOpenAIEmbedder } from '@directive-run/ai/openai';\n *\n * const runner = createOpenAIRunner({ apiKey: process.env.OPENAI_API_KEY! });\n * const embedder = createOpenAIEmbedder({ apiKey: process.env.OPENAI_API_KEY! });\n * ```\n */\n\nimport { createRunner, validateBaseURL } from \"../agent-utils.js\";\nimport type { EmbedderFn, Embedding } from \"../guardrails/semantic-cache.js\";\nimport type {\n AdapterHooks,\n AgentRunner,\n Message,\n TokenUsage,\n} from \"../types.js\";\nimport type { StreamingCallbackRunner } from \"../types.js\";\n\n// ============================================================================\n// Pricing Constants\n// ============================================================================\n\n/**\n * OpenAI model pricing (USD per million tokens).\n *\n * Use with `estimateCost()` for per-call cost tracking:\n * ```typescript\n * import { estimateCost } from '@directive-run/ai';\n * import { OPENAI_PRICING } from '@directive-run/ai/openai';\n *\n * const cost =\n * estimateCost(result.tokenUsage!.inputTokens, OPENAI_PRICING[\"gpt-4o\"].input) +\n * estimateCost(result.tokenUsage!.outputTokens, OPENAI_PRICING[\"gpt-4o\"].output);\n * ```\n *\n * **Note:** Pricing changes over time. These values are provided as a convenience\n * and may not reflect the latest rates. Always verify at https://openai.com/pricing\n */\nexport const OPENAI_PRICING: Record<string, { input: number; output: number }> =\n {\n \"gpt-4o\": { input: 2.5, output: 10 },\n \"gpt-4o-mini\": { input: 0.15, output: 0.6 },\n \"gpt-4-turbo\": { input: 10, output: 30 },\n \"o3-mini\": { input: 1.1, output: 4.4 },\n };\n\n// ============================================================================\n// OpenAI Runner\n// ============================================================================\n\n/** Options for createOpenAIRunner */\nexport interface OpenAIRunnerOptions {\n apiKey: string;\n model?: string;\n maxTokens?: number;\n baseURL?: string;\n fetch?: typeof globalThis.fetch;\n /** @default undefined */\n timeoutMs?: number;\n /** Lifecycle hooks for tracing, logging, and metrics */\n hooks?: AdapterHooks;\n}\n\n/**\n * Create an AgentRunner for OpenAI-compatible APIs (OpenAI, Azure, Together, etc.)\n *\n * Returns `tokenUsage` with input/output breakdown for cost tracking.\n *\n * @example\n * ```typescript\n * // OpenAI\n * const runner = createOpenAIRunner({ apiKey: process.env.OPENAI_API_KEY! });\n *\n * // Azure OpenAI\n * const azure = createOpenAIRunner({\n * apiKey: process.env.AZURE_KEY!,\n * baseURL: \"https://your-resource.openai.azure.com/v1\",\n * });\n *\n * // Together.ai (OpenAI-compatible)\n * const together = createOpenAIRunner({\n * apiKey: process.env.TOGETHER_KEY!,\n * baseURL: \"https://api.together.xyz/v1\",\n * });\n * ```\n */\nexport function createOpenAIRunner(options: OpenAIRunnerOptions): AgentRunner {\n const {\n apiKey,\n model = \"gpt-4o\",\n maxTokens,\n baseURL = \"https://api.openai.com/v1\",\n fetch: fetchFn = globalThis.fetch,\n timeoutMs,\n hooks,\n } = options;\n\n validateBaseURL(baseURL);\n\n if (\n typeof process !== \"undefined\" &&\n process.env?.NODE_ENV !== \"production\" &&\n !apiKey\n ) {\n console.warn(\n \"[Directive] createOpenAIRunner: apiKey is empty. API calls will fail.\",\n );\n }\n\n return createRunner({\n fetch: fetchFn,\n hooks,\n buildRequest: (agent, _input, messages) => ({\n url: `${baseURL}/chat/completions`,\n init: {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify({\n model: agent.model ?? model,\n ...(maxTokens != null ? { max_tokens: maxTokens } : {}),\n messages: [\n ...(agent.instructions\n ? [{ role: \"system\", content: agent.instructions }]\n : []),\n ...messages.map((m) => ({ role: m.role, content: m.content })),\n ],\n }),\n ...(timeoutMs != null\n ? { signal: AbortSignal.timeout(timeoutMs) }\n : {}),\n },\n }),\n parseResponse: async (res) => {\n const data = await res.json();\n const text = data.choices?.[0]?.message?.content ?? \"\";\n const inputTokens = data.usage?.prompt_tokens ?? 0;\n const outputTokens = data.usage?.completion_tokens ?? 0;\n\n return {\n text,\n totalTokens: inputTokens + outputTokens,\n inputTokens,\n outputTokens,\n };\n },\n });\n}\n\n// ============================================================================\n// OpenAI Embedder\n// ============================================================================\n\n/** Options for createOpenAIEmbedder */\nexport interface OpenAIEmbedderOptions {\n apiKey: string;\n model?: string;\n dimensions?: number;\n baseURL?: string;\n fetch?: typeof globalThis.fetch;\n /** @default 30000 */\n timeoutMs?: number;\n}\n\n/**\n * Create an EmbedderFn that calls the OpenAI embeddings API.\n *\n * @example\n * ```typescript\n * const embedder = createOpenAIEmbedder({ apiKey: process.env.OPENAI_API_KEY! });\n * const embedding = await embedder('How do constraints work?');\n * ```\n */\nexport function createOpenAIEmbedder(\n options: OpenAIEmbedderOptions,\n): EmbedderFn {\n const {\n apiKey,\n model = \"text-embedding-3-small\",\n dimensions = 1536,\n baseURL = \"https://api.openai.com/v1\",\n fetch: fetchFn = globalThis.fetch,\n timeoutMs,\n } = options;\n\n validateBaseURL(baseURL);\n\n if (\n typeof process !== \"undefined\" &&\n process.env?.NODE_ENV !== \"production\" &&\n !apiKey\n ) {\n console.warn(\n \"[Directive] createOpenAIEmbedder: apiKey is empty. API calls will fail.\",\n );\n }\n\n return async (text: string): Promise<Embedding> => {\n const response = await fetchFn(`${baseURL}/embeddings`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify({ model, input: text, dimensions }),\n signal: AbortSignal.timeout(timeoutMs ?? 30_000),\n });\n\n if (!response.ok) {\n const errBody = await response.text().catch(() => \"\");\n\n throw new Error(\n `[Directive] OpenAI embedding failed: ${response.status}${errBody ? ` – ${errBody.slice(0, 200)}` : \"\"}`,\n );\n }\n\n const data = (await response.json()) as {\n data: Array<{ embedding: number[] }>;\n };\n\n const entry = data.data[0];\n if (!entry) {\n throw new Error(\n \"[Directive] OpenAI embedding response contained no data entries\",\n );\n }\n\n return entry.embedding;\n };\n}\n\n// ============================================================================\n// OpenAI Streaming Runner\n// ============================================================================\n\n/** Options for createOpenAIStreamingRunner */\nexport interface OpenAIStreamingRunnerOptions {\n apiKey: string;\n model?: string;\n maxTokens?: number;\n baseURL?: string;\n fetch?: typeof globalThis.fetch;\n /** Lifecycle hooks for tracing, logging, and metrics */\n hooks?: AdapterHooks;\n}\n\n/**\n * Create a StreamingCallbackRunner for OpenAI-compatible chat completions\n * with server-sent events. Can be used standalone or paired with `createOpenAIRunner`.\n *\n * Returns `tokenUsage` with input/output breakdown for cost tracking.\n *\n * @example\n * ```typescript\n * const streamingRunner = createOpenAIStreamingRunner({\n * apiKey: process.env.OPENAI_API_KEY!,\n * });\n * const streamRunner = createStreamingRunner(streamingRunner);\n * const { stream, result } = streamRunner(agent, input);\n * ```\n */\nexport function createOpenAIStreamingRunner(\n options: OpenAIStreamingRunnerOptions,\n): StreamingCallbackRunner {\n const {\n apiKey,\n model = \"gpt-4o\",\n maxTokens,\n baseURL = \"https://api.openai.com/v1\",\n fetch: fetchFn = globalThis.fetch,\n hooks,\n } = options;\n\n validateBaseURL(baseURL);\n\n if (\n typeof process !== \"undefined\" &&\n process.env?.NODE_ENV !== \"production\" &&\n !apiKey\n ) {\n console.warn(\n \"[Directive] createOpenAIStreamingRunner: apiKey is empty. API calls will fail.\",\n );\n }\n\n return async (agent, input, callbacks) => {\n const startTime = Date.now();\n hooks?.onBeforeCall?.({ agent, input, timestamp: startTime });\n\n try {\n const response = await fetchFn(`${baseURL}/chat/completions`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify({\n model: agent.model ?? model,\n ...(maxTokens != null ? { max_tokens: maxTokens } : {}),\n messages: [\n ...(agent.instructions\n ? [{ role: \"system\", content: agent.instructions }]\n : []),\n { role: \"user\", content: input },\n ],\n stream: true,\n stream_options: { include_usage: true },\n }),\n signal: callbacks.signal,\n });\n\n if (!response.ok) {\n const errBody = await response.text().catch(() => \"\");\n\n throw new Error(\n `[Directive] OpenAI streaming error ${response.status}${errBody ? ` – ${errBody.slice(0, 200)}` : \"\"}`,\n );\n }\n\n const reader = response.body?.getReader();\n if (!reader) {\n throw new Error(\"[Directive] No response body\");\n }\n\n const decoder = new TextDecoder();\n let buf = \"\";\n let fullText = \"\";\n let promptTokens = 0;\n let completionTokens = 0;\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n\n buf += decoder.decode(value, { stream: true });\n const lines = buf.split(\"\\n\");\n buf = lines.pop() ?? \"\";\n\n for (const line of lines) {\n if (!line.startsWith(\"data: \")) {\n continue;\n }\n const data = line.slice(6).trim();\n if (data === \"[DONE]\") {\n continue;\n }\n\n try {\n const event = JSON.parse(data);\n\n // Extract token content from delta\n const delta = event.choices?.[0]?.delta;\n if (delta?.content) {\n fullText += delta.content;\n callbacks.onToken?.(delta.content);\n }\n\n // Extract usage from the final chunk (stream_options: include_usage)\n if (event.usage) {\n promptTokens = event.usage.prompt_tokens ?? 0;\n completionTokens = event.usage.completion_tokens ?? 0;\n }\n } catch (parseErr) {\n if (parseErr instanceof SyntaxError) {\n if (\n typeof process !== \"undefined\" &&\n process.env?.NODE_ENV === \"development\"\n ) {\n console.warn(\n \"[Directive] Malformed SSE event from OpenAI:\",\n data,\n );\n }\n } else {\n throw parseErr;\n }\n }\n }\n }\n } finally {\n reader.cancel().catch(() => {});\n }\n\n const assistantMsg: Message = { role: \"assistant\", content: fullText };\n callbacks.onMessage?.(assistantMsg);\n\n const tokenUsage: TokenUsage = {\n inputTokens: promptTokens,\n outputTokens: completionTokens,\n };\n const totalTokens = promptTokens + completionTokens;\n\n hooks?.onAfterCall?.({\n agent,\n input,\n output: fullText,\n totalTokens,\n tokenUsage,\n durationMs: Date.now() - startTime,\n timestamp: Date.now(),\n });\n\n return {\n output: fullText,\n messages: [{ role: \"user\" as const, content: input }, assistantMsg],\n toolCalls: [],\n totalTokens,\n tokenUsage,\n };\n } catch (err) {\n if (err instanceof Error) {\n hooks?.onError?.({\n agent,\n input,\n error: err,\n durationMs: Date.now() - startTime,\n timestamp: Date.now(),\n });\n }\n\n throw err;\n }\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/agent-utils.ts","../src/adapters/openai.ts"],"names":["ALLOWED_PROTOCOLS","validateBaseURL","baseURL","url","err","createRunner","options","fetchFn","buildRequest","parseResponse","parseOutput","hooks","parse","text","agent","input","runOptions","startTime","messages","init","fetchInit","response","errBody","parsed","tokenUsage","assistantMessage","allMessages","durationMs","OPENAI_PRICING","createOpenAIRunner","apiKey","model","maxTokens","timeoutMs","_input","m","res","data","inputTokens","outputTokens","createOpenAIEmbedder","dimensions","entry","createOpenAIStreamingRunner","callbacks","reader","decoder","buf","fullText","promptTokens","completionTokens","done","value","lines","line","event","delta","parseErr","assistantMsg","totalTokens"],"mappings":"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","file":"openai.cjs","sourcesContent":["/**\n * Agent utilities — createRunner, estimateCost, state queries, URL validation.\n */\n\nimport type {\n AdapterHooks,\n AgentLike,\n AgentRunner,\n AgentState,\n ApprovalState,\n Message,\n RunOptions,\n RunResult,\n TokenUsage,\n} from \"./types.js\";\n\n// ============================================================================\n// State Query Helpers\n// ============================================================================\n\n/**\n * Check whether an agent is currently executing a run.\n *\n * @param state - The current {@link AgentState} to inspect.\n * @returns `true` when the agent status is `\"running\"`.\n */\nexport function isAgentRunning(state: AgentState): boolean {\n return state.status === \"running\";\n}\n\n/**\n * Check whether there are tool-call approvals waiting for user confirmation.\n *\n * @param state - The current {@link ApprovalState} to inspect.\n * @returns `true` when one or more approvals are pending.\n */\nexport function hasPendingApprovals(state: ApprovalState): boolean {\n return state.pending.length > 0;\n}\n\n// ============================================================================\n// Cost Estimation\n// ============================================================================\n\n/**\n * Estimate the dollar cost of an agent run based on total token usage.\n *\n * @remarks\n * No default rate is provided — callers must supply the current per-million-token\n * price to avoid silently using stale pricing.\n *\n * @param tokenUsage - Total number of tokens consumed (input + output).\n * @param ratePerMillionTokens - Cost in dollars per one million tokens.\n * @returns Estimated cost in dollars.\n */\nexport function estimateCost(\n tokenUsage: number,\n ratePerMillionTokens: number,\n): number {\n return (tokenUsage / 1_000_000) * ratePerMillionTokens;\n}\n\n// ============================================================================\n// Validation Helpers\n// ============================================================================\n\nconst ALLOWED_PROTOCOLS = new Set([\"http:\", \"https:\"]);\n\n/**\n * Validate that a base URL uses the `http:` or `https:` protocol.\n * Throws immediately at adapter creation time (not at call time) to surface\n * configuration errors before any LLM requests are made.\n *\n * @param baseURL - The base URL string to validate.\n * @throws When the URL is malformed or uses a protocol other than `http:` or `https:`.\n */\nexport function validateBaseURL(baseURL: string): void {\n try {\n const url = new URL(baseURL);\n if (!ALLOWED_PROTOCOLS.has(url.protocol)) {\n throw new Error(\n `[Directive] Invalid baseURL protocol \"${url.protocol}\" – only http: and https: are allowed`,\n );\n }\n } catch (err) {\n if (err instanceof Error && err.message.startsWith(\"[Directive]\")) {\n throw err;\n }\n\n throw new Error(\n `[Directive] Invalid baseURL \"${baseURL}\" – must be a valid URL (e.g. \"https://api.openai.com/v1\")`,\n );\n }\n}\n\n// ============================================================================\n// createRunner Helper\n// ============================================================================\n\n/** Parsed response from an LLM provider */\nexport interface ParsedResponse {\n text: string;\n totalTokens: number;\n /** Input token count, when available from the provider */\n inputTokens?: number;\n /** Output token count, when available from the provider */\n outputTokens?: number;\n}\n\n/** Options for creating an AgentRunner from buildRequest/parseResponse */\nexport interface CreateRunnerOptions {\n fetch?: typeof globalThis.fetch;\n buildRequest: (\n agent: AgentLike,\n input: string,\n messages: Message[],\n ) => { url: string; init: RequestInit };\n parseResponse: (\n response: Response,\n messages: Message[],\n ) => Promise<ParsedResponse>;\n parseOutput?: <T>(text: string) => T;\n /** Lifecycle hooks for tracing, logging, and metrics */\n hooks?: AdapterHooks;\n}\n\n/**\n * Create an {@link AgentRunner} from `buildRequest`/`parseResponse` helpers, reducing\n * ~50 lines of fetch boilerplate to ~20 lines of configuration.\n *\n * @remarks\n * Supports lifecycle hooks for observability:\n * - `onBeforeCall` fires before each API request\n * - `onAfterCall` fires after a successful response (includes token breakdown)\n * - `onError` fires when the request fails\n *\n * Output parsing defaults to `JSON.parse` with a string fallback. Supply a custom\n * `parseOutput` to override (e.g. for structured output schemas).\n *\n * @param options - Configuration for the runner, including request building, response parsing, and hooks.\n * @returns An {@link AgentRunner} function that performs LLM calls via fetch.\n *\n * @example\n * ```typescript\n * const runClaude = createRunner({\n * buildRequest: (agent, input) => ({\n * url: \"/api/claude\",\n * init: {\n * method: \"POST\",\n * headers: { \"Content-Type\": \"application/json\" },\n * body: JSON.stringify({\n * model: agent.model ?? \"claude-haiku-4-5-20251001\",\n * system: agent.instructions ?? \"\",\n * messages: [{ role: \"user\", content: input }],\n * }),\n * },\n * }),\n * parseResponse: async (res) => {\n * const data = await res.json();\n * const inputTokens = data.usage?.input_tokens ?? 0;\n * const outputTokens = data.usage?.output_tokens ?? 0;\n * return {\n * text: data.content?.[0]?.text ?? \"\",\n * totalTokens: inputTokens + outputTokens,\n * inputTokens,\n * outputTokens,\n * };\n * },\n * hooks: {\n * onAfterCall: ({ durationMs, tokenUsage }) => {\n * console.log(`LLM call: ${durationMs}ms, ${tokenUsage.inputTokens}in/${tokenUsage.outputTokens}out`);\n * },\n * },\n * });\n * ```\n *\n * @public\n */\nexport function createRunner(options: CreateRunnerOptions): AgentRunner {\n const {\n fetch: fetchFn = globalThis.fetch,\n buildRequest,\n parseResponse,\n parseOutput,\n hooks,\n } = options;\n\n const defaultParseOutput = <T>(text: string): T => {\n try {\n return JSON.parse(text) as T;\n } catch {\n return text as unknown as T;\n }\n };\n\n const parse = parseOutput ?? defaultParseOutput;\n\n return async <T = unknown>(\n agent: AgentLike,\n input: string,\n runOptions?: RunOptions,\n ): Promise<RunResult<T>> => {\n const startTime = Date.now();\n hooks?.onBeforeCall?.({ agent, input, timestamp: startTime });\n\n const messages: Message[] = [{ role: \"user\", content: input }];\n\n try {\n const { url, init } = buildRequest(agent, input, messages);\n\n const fetchInit: RequestInit = runOptions?.signal\n ? { ...init, signal: runOptions.signal }\n : init;\n\n const response = await fetchFn(url, fetchInit);\n\n if (!response.ok) {\n const errBody = await response.text().catch(() => \"\");\n\n throw new Error(\n `[Directive] AgentRunner request failed: ${response.status} ${response.statusText}${errBody ? ` – ${errBody.slice(0, 300)}` : \"\"}`,\n );\n }\n\n const parsed = await parseResponse(response, messages);\n const tokenUsage: TokenUsage = {\n inputTokens: parsed.inputTokens ?? 0,\n outputTokens: parsed.outputTokens ?? 0,\n };\n\n const assistantMessage: Message = {\n role: \"assistant\",\n content: parsed.text,\n };\n const allMessages: Message[] = [...messages, assistantMessage];\n\n runOptions?.onMessage?.(assistantMessage);\n\n const durationMs = Date.now() - startTime;\n hooks?.onAfterCall?.({\n agent,\n input,\n output: parsed.text,\n totalTokens: parsed.totalTokens,\n tokenUsage,\n durationMs,\n timestamp: Date.now(),\n });\n\n return {\n output: parse<T>(parsed.text),\n messages: allMessages,\n toolCalls: [],\n totalTokens: parsed.totalTokens,\n tokenUsage,\n };\n } catch (err) {\n const durationMs = Date.now() - startTime;\n if (err instanceof Error) {\n hooks?.onError?.({\n agent,\n input,\n error: err,\n durationMs,\n timestamp: Date.now(),\n });\n }\n\n throw err;\n }\n };\n}\n","/**\n * @directive-run/ai/openai\n *\n * OpenAI adapter for Directive AI. Provides runners and embedders\n * for OpenAI-compatible APIs (OpenAI, Azure, Together, etc.)\n *\n * @example\n * ```typescript\n * import { createOpenAIRunner, createOpenAIEmbedder } from '@directive-run/ai/openai';\n *\n * const runner = createOpenAIRunner({ apiKey: process.env.OPENAI_API_KEY! });\n * const embedder = createOpenAIEmbedder({ apiKey: process.env.OPENAI_API_KEY! });\n * ```\n */\n\nimport { createRunner, validateBaseURL } from \"../agent-utils.js\";\nimport type { EmbedderFn, Embedding } from \"../guardrails/semantic-cache.js\";\nimport type {\n AdapterHooks,\n AgentRunner,\n Message,\n TokenUsage,\n} from \"../types.js\";\nimport type { StreamingCallbackRunner } from \"../types.js\";\n\n// ============================================================================\n// Pricing Constants\n// ============================================================================\n\n/**\n * OpenAI model pricing (USD per million tokens).\n *\n * Use with `estimateCost()` for per-call cost tracking:\n * ```typescript\n * import { estimateCost } from '@directive-run/ai';\n * import { OPENAI_PRICING } from '@directive-run/ai/openai';\n *\n * const cost =\n * estimateCost(result.tokenUsage!.inputTokens, OPENAI_PRICING[\"gpt-4o\"].input) +\n * estimateCost(result.tokenUsage!.outputTokens, OPENAI_PRICING[\"gpt-4o\"].output);\n * ```\n *\n * **Note:** Pricing changes over time. These values are provided as a convenience\n * and may not reflect the latest rates. Always verify at https://openai.com/pricing\n */\nexport const OPENAI_PRICING: Record<string, { input: number; output: number }> =\n {\n \"gpt-4o\": { input: 2.5, output: 10 },\n \"gpt-4o-mini\": { input: 0.15, output: 0.6 },\n \"gpt-4-turbo\": { input: 10, output: 30 },\n \"o3-mini\": { input: 1.1, output: 4.4 },\n };\n\n// ============================================================================\n// OpenAI Runner\n// ============================================================================\n\n/** Options for createOpenAIRunner */\nexport interface OpenAIRunnerOptions {\n apiKey: string;\n model?: string;\n maxTokens?: number;\n baseURL?: string;\n fetch?: typeof globalThis.fetch;\n /** @default undefined */\n timeoutMs?: number;\n /** Lifecycle hooks for tracing, logging, and metrics */\n hooks?: AdapterHooks;\n}\n\n/**\n * Create an AgentRunner for OpenAI-compatible APIs (OpenAI, Azure, Together, etc.)\n *\n * Returns `tokenUsage` with input/output breakdown for cost tracking.\n *\n * @example\n * ```typescript\n * // OpenAI\n * const runner = createOpenAIRunner({ apiKey: process.env.OPENAI_API_KEY! });\n *\n * // Azure OpenAI\n * const azure = createOpenAIRunner({\n * apiKey: process.env.AZURE_KEY!,\n * baseURL: \"https://your-resource.openai.azure.com/v1\",\n * });\n *\n * // Together.ai (OpenAI-compatible)\n * const together = createOpenAIRunner({\n * apiKey: process.env.TOGETHER_KEY!,\n * baseURL: \"https://api.together.xyz/v1\",\n * });\n * ```\n */\nexport function createOpenAIRunner(options: OpenAIRunnerOptions): AgentRunner {\n const {\n apiKey,\n model = \"gpt-4o\",\n maxTokens,\n baseURL = \"https://api.openai.com/v1\",\n fetch: fetchFn = globalThis.fetch,\n timeoutMs,\n hooks,\n } = options;\n\n validateBaseURL(baseURL);\n\n if (\n typeof process !== \"undefined\" &&\n process.env?.NODE_ENV !== \"production\" &&\n !apiKey\n ) {\n console.warn(\n \"[Directive] createOpenAIRunner: apiKey is empty. API calls will fail.\",\n );\n }\n\n return createRunner({\n fetch: fetchFn,\n hooks,\n buildRequest: (agent, _input, messages) => ({\n url: `${baseURL}/chat/completions`,\n init: {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify({\n model: agent.model ?? model,\n ...(maxTokens != null ? { max_tokens: maxTokens } : {}),\n messages: [\n ...(agent.instructions\n ? [{ role: \"system\", content: agent.instructions }]\n : []),\n ...messages.map((m) => ({ role: m.role, content: m.content })),\n ],\n }),\n ...(timeoutMs != null\n ? { signal: AbortSignal.timeout(timeoutMs) }\n : {}),\n },\n }),\n parseResponse: async (res) => {\n const data = await res.json();\n const text = data.choices?.[0]?.message?.content ?? \"\";\n const inputTokens = data.usage?.prompt_tokens ?? 0;\n const outputTokens = data.usage?.completion_tokens ?? 0;\n\n return {\n text,\n totalTokens: inputTokens + outputTokens,\n inputTokens,\n outputTokens,\n };\n },\n });\n}\n\n// ============================================================================\n// OpenAI Embedder\n// ============================================================================\n\n/** Options for createOpenAIEmbedder */\nexport interface OpenAIEmbedderOptions {\n apiKey: string;\n model?: string;\n dimensions?: number;\n baseURL?: string;\n fetch?: typeof globalThis.fetch;\n /** @default 30000 */\n timeoutMs?: number;\n}\n\n/**\n * Create an EmbedderFn that calls the OpenAI embeddings API.\n *\n * @example\n * ```typescript\n * const embedder = createOpenAIEmbedder({ apiKey: process.env.OPENAI_API_KEY! });\n * const embedding = await embedder('How do constraints work?');\n * ```\n */\nexport function createOpenAIEmbedder(\n options: OpenAIEmbedderOptions,\n): EmbedderFn {\n const {\n apiKey,\n model = \"text-embedding-3-small\",\n dimensions = 1536,\n baseURL = \"https://api.openai.com/v1\",\n fetch: fetchFn = globalThis.fetch,\n timeoutMs,\n } = options;\n\n validateBaseURL(baseURL);\n\n if (\n typeof process !== \"undefined\" &&\n process.env?.NODE_ENV !== \"production\" &&\n !apiKey\n ) {\n console.warn(\n \"[Directive] createOpenAIEmbedder: apiKey is empty. API calls will fail.\",\n );\n }\n\n return async (text: string): Promise<Embedding> => {\n const response = await fetchFn(`${baseURL}/embeddings`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify({ model, input: text, dimensions }),\n signal: AbortSignal.timeout(timeoutMs ?? 30_000),\n });\n\n if (!response.ok) {\n const errBody = await response.text().catch(() => \"\");\n\n throw new Error(\n `[Directive] OpenAI embedding failed: ${response.status}${errBody ? ` – ${errBody.slice(0, 200)}` : \"\"}`,\n );\n }\n\n const data = (await response.json()) as {\n data: Array<{ embedding: number[] }>;\n };\n\n const entry = data.data[0];\n if (!entry) {\n throw new Error(\n \"[Directive] OpenAI embedding response contained no data entries\",\n );\n }\n\n return entry.embedding;\n };\n}\n\n// ============================================================================\n// OpenAI Streaming Runner\n// ============================================================================\n\n/** Options for createOpenAIStreamingRunner */\nexport interface OpenAIStreamingRunnerOptions {\n apiKey: string;\n model?: string;\n maxTokens?: number;\n baseURL?: string;\n fetch?: typeof globalThis.fetch;\n /** Lifecycle hooks for tracing, logging, and metrics */\n hooks?: AdapterHooks;\n}\n\n/**\n * Create a StreamingCallbackRunner for OpenAI-compatible chat completions\n * with server-sent events. Can be used standalone or paired with `createOpenAIRunner`.\n *\n * Returns `tokenUsage` with input/output breakdown for cost tracking.\n *\n * @example\n * ```typescript\n * const streamingRunner = createOpenAIStreamingRunner({\n * apiKey: process.env.OPENAI_API_KEY!,\n * });\n * const streamRunner = createStreamingRunner(streamingRunner);\n * const { stream, result } = streamRunner(agent, input);\n * ```\n */\nexport function createOpenAIStreamingRunner(\n options: OpenAIStreamingRunnerOptions,\n): StreamingCallbackRunner {\n const {\n apiKey,\n model = \"gpt-4o\",\n maxTokens,\n baseURL = \"https://api.openai.com/v1\",\n fetch: fetchFn = globalThis.fetch,\n hooks,\n } = options;\n\n validateBaseURL(baseURL);\n\n if (\n typeof process !== \"undefined\" &&\n process.env?.NODE_ENV !== \"production\" &&\n !apiKey\n ) {\n console.warn(\n \"[Directive] createOpenAIStreamingRunner: apiKey is empty. API calls will fail.\",\n );\n }\n\n return async (agent, input, callbacks) => {\n const startTime = Date.now();\n hooks?.onBeforeCall?.({ agent, input, timestamp: startTime });\n\n try {\n const response = await fetchFn(`${baseURL}/chat/completions`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify({\n model: agent.model ?? model,\n ...(maxTokens != null ? { max_tokens: maxTokens } : {}),\n messages: [\n ...(agent.instructions\n ? [{ role: \"system\", content: agent.instructions }]\n : []),\n { role: \"user\", content: input },\n ],\n stream: true,\n stream_options: { include_usage: true },\n }),\n signal: callbacks.signal,\n });\n\n if (!response.ok) {\n const errBody = await response.text().catch(() => \"\");\n\n throw new Error(\n `[Directive] OpenAI streaming error ${response.status}${errBody ? ` – ${errBody.slice(0, 200)}` : \"\"}`,\n );\n }\n\n const reader = response.body?.getReader();\n if (!reader) {\n throw new Error(\"[Directive] No response body\");\n }\n\n const decoder = new TextDecoder();\n let buf = \"\";\n let fullText = \"\";\n let promptTokens = 0;\n let completionTokens = 0;\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n\n buf += decoder.decode(value, { stream: true });\n const lines = buf.split(\"\\n\");\n buf = lines.pop() ?? \"\";\n\n for (const line of lines) {\n if (!line.startsWith(\"data: \")) {\n continue;\n }\n const data = line.slice(6).trim();\n if (data === \"[DONE]\") {\n continue;\n }\n\n try {\n const event = JSON.parse(data);\n\n // Extract token content from delta\n const delta = event.choices?.[0]?.delta;\n if (delta?.content) {\n fullText += delta.content;\n callbacks.onToken?.(delta.content);\n }\n\n // Extract usage from the final chunk (stream_options: include_usage)\n if (event.usage) {\n promptTokens = event.usage.prompt_tokens ?? 0;\n completionTokens = event.usage.completion_tokens ?? 0;\n }\n } catch (parseErr) {\n if (parseErr instanceof SyntaxError) {\n if (\n typeof process !== \"undefined\" &&\n process.env?.NODE_ENV === \"development\"\n ) {\n console.warn(\n \"[Directive] Malformed SSE event from OpenAI:\",\n data,\n );\n }\n } else {\n throw parseErr;\n }\n }\n }\n }\n } finally {\n reader.cancel().catch(() => {});\n }\n\n const assistantMsg: Message = { role: \"assistant\", content: fullText };\n callbacks.onMessage?.(assistantMsg);\n\n const tokenUsage: TokenUsage = {\n inputTokens: promptTokens,\n outputTokens: completionTokens,\n };\n const totalTokens = promptTokens + completionTokens;\n\n hooks?.onAfterCall?.({\n agent,\n input,\n output: fullText,\n totalTokens,\n tokenUsage,\n durationMs: Date.now() - startTime,\n timestamp: Date.now(),\n });\n\n return {\n output: fullText,\n messages: [{ role: \"user\" as const, content: input }, assistantMsg],\n toolCalls: [],\n totalTokens,\n tokenUsage,\n };\n } catch (err) {\n if (err instanceof Error) {\n hooks?.onError?.({\n agent,\n input,\n error: err,\n durationMs: Date.now() - startTime,\n timestamp: Date.now(),\n });\n }\n\n throw err;\n }\n };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/agent-utils.ts","../src/adapters/openai.ts"],"names":["ALLOWED_PROTOCOLS","validateBaseURL","baseURL","url","err","createRunner","options","fetchFn","buildRequest","parseResponse","parseOutput","hooks","parse","text","agent","input","runOptions","startTime","messages","init","fetchInit","response","errBody","parsed","tokenUsage","assistantMessage","allMessages","durationMs","OPENAI_PRICING","createOpenAIRunner","apiKey","model","maxTokens","timeoutMs","_input","m","res","data","inputTokens","outputTokens","createOpenAIEmbedder","dimensions","entry","createOpenAIStreamingRunner","callbacks","reader","decoder","buf","fullText","promptTokens","completionTokens","done","value","lines","line","event","delta","parseErr","assistantMsg","totalTokens"],"mappings":"AAoDA,IAAMA,CAAAA,CAAoB,IAAI,GAAA,CAAI,CAAC,QAAS,QAAQ,CAAC,CAAA,CAM9C,SAASC,CAAAA,CAAgBC,CAAAA,CAAuB,CACrD,GAAI,CACF,IAAMC,CAAAA,CAAM,IAAI,GAAA,CAAID,CAAO,CAAA,CAC3B,GAAI,CAACF,CAAAA,CAAkB,GAAA,CAAIG,CAAAA,CAAI,QAAQ,CAAA,CACrC,MAAM,IAAI,KAAA,CACR,CAAA,sCAAA,EAAyCA,CAAAA,CAAI,QAAQ,CAAA,0CAAA,CACvD,CAEJ,CAAA,MAASC,CAAAA,CAAK,CACZ,MAAIA,aAAe,KAAA,EAASA,CAAAA,CAAI,OAAA,CAAQ,UAAA,CAAW,aAAa,CAAA,CACxDA,EAGF,IAAI,KAAA,CACR,CAAA,6BAAA,EAAgCF,CAAO,CAAA,+DAAA,CACzC,CACF,CACF,CA4EO,SAASG,CAAAA,CAAaC,CAAAA,CAA2C,CACtE,GAAM,CACJ,KAAA,CAAOC,CAAAA,CAAU,UAAA,CAAW,KAAA,CAC5B,YAAA,CAAAC,CAAAA,CACA,cAAAC,CAAAA,CACA,WAAA,CAAAC,CAAAA,CACA,KAAA,CAAAC,CACF,CAAA,CAAIL,EAUEM,CAAAA,CAAQF,CAAAA,GARiBG,CAAAA,EAAoB,CACjD,GAAI,CACF,OAAO,IAAA,CAAK,KAAA,CAAMA,CAAI,CACxB,CAAA,KAAQ,CACN,OAAOA,CACT,CACF,CAAA,CAAA,CAIA,OAAO,MACLC,CAAAA,CACAC,EACAC,CAAAA,GAC0B,CAC1B,IAAMC,CAAAA,CAAY,IAAA,CAAK,GAAA,GACvBN,CAAAA,EAAO,YAAA,GAAe,CAAE,KAAA,CAAAG,CAAAA,CAAO,KAAA,CAAAC,EAAO,SAAA,CAAWE,CAAU,CAAC,CAAA,CAE5D,IAAMC,EAAsB,CAAC,CAAE,IAAA,CAAM,MAAA,CAAQ,OAAA,CAASH,CAAM,CAAC,CAAA,CAE7D,GAAI,CACF,GAAM,CAAE,GAAA,CAAAZ,EAAK,IAAA,CAAAgB,CAAK,CAAA,CAAIX,CAAAA,CAAaM,CAAAA,CAAOC,CAAAA,CAAOG,CAAQ,CAAA,CAEnDE,CAAAA,CAAyBJ,CAAAA,EAAY,MAAA,CACvC,CAAE,GAAGG,EAAM,MAAA,CAAQH,CAAAA,CAAW,MAAO,CAAA,CACrCG,CAAAA,CAEEE,CAAAA,CAAW,MAAMd,CAAAA,CAAQJ,CAAAA,CAAKiB,CAAS,CAAA,CAE7C,GAAI,CAACC,EAAS,EAAA,CAAI,CAChB,IAAMC,CAAAA,CAAU,MAAMD,CAAAA,CAAS,MAAK,CAAE,KAAA,CAAM,IAAM,EAAE,CAAA,CAEpD,MAAM,IAAI,KAAA,CACR,CAAA,wCAAA,EAA2CA,CAAAA,CAAS,MAAM,CAAA,CAAA,EAAIA,CAAAA,CAAS,UAAU,CAAA,EAAGC,CAAAA,CAAU,CAAA,QAAA,EAAMA,CAAAA,CAAQ,KAAA,CAAM,CAAA,CAAG,GAAG,CAAC,CAAA,CAAA,CAAK,EAAE,CAAA,CAClI,CACF,CAEA,IAAMC,CAAAA,CAAS,MAAMd,CAAAA,CAAcY,CAAAA,CAAUH,CAAQ,CAAA,CAC/CM,EAAyB,CAC7B,WAAA,CAAaD,CAAAA,CAAO,WAAA,EAAe,CAAA,CACnC,YAAA,CAAcA,EAAO,YAAA,EAAgB,CACvC,CAAA,CAEME,CAAAA,CAA4B,CAChC,IAAA,CAAM,YACN,OAAA,CAASF,CAAAA,CAAO,IAClB,CAAA,CACMG,CAAAA,CAAyB,CAAC,GAAGR,CAAAA,CAAUO,CAAgB,EAE7DT,CAAAA,EAAY,SAAA,GAAYS,CAAgB,CAAA,CAExC,IAAME,CAAAA,CAAa,IAAA,CAAK,GAAA,EAAI,CAAIV,EAChC,OAAAN,CAAAA,EAAO,WAAA,GAAc,CACnB,KAAA,CAAAG,CAAAA,CACA,MAAAC,CAAAA,CACA,MAAA,CAAQQ,CAAAA,CAAO,IAAA,CACf,WAAA,CAAaA,CAAAA,CAAO,YACpB,UAAA,CAAAC,CAAAA,CACA,UAAA,CAAAG,CAAAA,CACA,SAAA,CAAW,IAAA,CAAK,KAClB,CAAC,CAAA,CAEM,CACL,MAAA,CAAQf,CAAAA,CAASW,EAAO,IAAI,CAAA,CAC5B,QAAA,CAAUG,CAAAA,CACV,SAAA,CAAW,GACX,WAAA,CAAaH,CAAAA,CAAO,WAAA,CACpB,UAAA,CAAAC,CACF,CACF,OAASpB,CAAAA,CAAK,CACZ,IAAMuB,CAAAA,CAAa,IAAA,CAAK,GAAA,GAAQV,CAAAA,CAChC,MAAIb,CAAAA,YAAe,KAAA,EACjBO,CAAAA,EAAO,OAAA,GAAU,CACf,KAAA,CAAAG,CAAAA,CACA,KAAA,CAAAC,CAAAA,CACA,KAAA,CAAOX,CAAAA,CACP,WAAAuB,CAAAA,CACA,SAAA,CAAW,IAAA,CAAK,GAAA,EAClB,CAAC,EAGGvB,CACR,CACF,CACF,CCvMO,IAAMwB,CAAAA,CACX,CACE,QAAA,CAAU,CAAE,KAAA,CAAO,GAAA,CAAK,MAAA,CAAQ,EAAG,EACnC,aAAA,CAAe,CAAE,KAAA,CAAO,GAAA,CAAM,MAAA,CAAQ,EAAI,EAC1C,aAAA,CAAe,CAAE,KAAA,CAAO,EAAA,CAAI,MAAA,CAAQ,EAAG,EACvC,SAAA,CAAW,CAAE,KAAA,CAAO,GAAA,CAAK,MAAA,CAAQ,GAAI,CACvC,EA0CK,SAASC,CAAAA,CAAmBvB,CAAAA,CAA2C,CAC5E,GAAM,CACJ,MAAA,CAAAwB,CAAAA,CACA,KAAA,CAAAC,CAAAA,CAAQ,QAAA,CACR,SAAA,CAAAC,EACA,OAAA,CAAA9B,CAAAA,CAAU,2BAAA,CACV,KAAA,CAAOK,CAAAA,CAAU,UAAA,CAAW,MAC5B,SAAA,CAAA0B,CAAAA,CACA,KAAA,CAAAtB,CACF,CAAA,CAAIL,CAAAA,CAEJ,OAAAL,CAAAA,CAAgBC,CAAO,CAAA,CAGrB,OAAO,OAAA,CAAY,GAAA,EACnB,QAAQ,GAAA,EAAK,QAAA,GAAa,YAAA,EAC1B,CAAC4B,CAAAA,EAED,OAAA,CAAQ,KACN,uEACF,CAAA,CAGKzB,CAAAA,CAAa,CAClB,KAAA,CAAOE,CAAAA,CACP,MAAAI,CAAAA,CACA,YAAA,CAAc,CAACG,CAAAA,CAAOoB,CAAAA,CAAQhB,CAAAA,IAAc,CAC1C,GAAA,CAAK,CAAA,EAAGhB,CAAO,CAAA,iBAAA,CAAA,CACf,IAAA,CAAM,CACJ,OAAQ,MAAA,CACR,OAAA,CAAS,CACP,cAAA,CAAgB,kBAAA,CAChB,aAAA,CAAe,UAAU4B,CAAM,CAAA,CACjC,CAAA,CACA,IAAA,CAAM,IAAA,CAAK,SAAA,CAAU,CACnB,KAAA,CAAOhB,CAAAA,CAAM,KAAA,EAASiB,CAAAA,CACtB,GAAIC,CAAAA,EAAa,KAAO,CAAE,UAAA,CAAYA,CAAU,CAAA,CAAI,EAAC,CACrD,SAAU,CACR,GAAIlB,CAAAA,CAAM,YAAA,CACN,CAAC,CAAE,KAAM,QAAA,CAAU,OAAA,CAASA,CAAAA,CAAM,YAAa,CAAC,CAAA,CAChD,EAAC,CACL,GAAGI,EAAS,GAAA,CAAKiB,CAAAA,GAAO,CAAE,IAAA,CAAMA,CAAAA,CAAE,IAAA,CAAM,OAAA,CAASA,CAAAA,CAAE,OAAQ,EAAE,CAC/D,CACF,CAAC,CAAA,CACD,GAAIF,CAAAA,EAAa,KACb,CAAE,MAAA,CAAQ,WAAA,CAAY,OAAA,CAAQA,CAAS,CAAE,EACzC,EACN,CACF,CAAA,CAAA,CACA,aAAA,CAAe,MAAOG,GAAQ,CAC5B,IAAMC,CAAAA,CAAO,MAAMD,CAAAA,CAAI,IAAA,GACjBvB,CAAAA,CAAOwB,CAAAA,CAAK,OAAA,GAAU,CAAC,CAAA,EAAG,OAAA,EAAS,SAAW,EAAA,CAC9CC,CAAAA,CAAcD,CAAAA,CAAK,KAAA,EAAO,aAAA,EAAiB,CAAA,CAC3CE,EAAeF,CAAAA,CAAK,KAAA,EAAO,iBAAA,EAAqB,CAAA,CAEtD,OAAO,CACL,KAAAxB,CAAAA,CACA,WAAA,CAAayB,CAAAA,CAAcC,CAAAA,CAC3B,WAAA,CAAAD,CAAAA,CACA,aAAAC,CACF,CACF,CACF,CAAC,CACH,CA0BO,SAASC,CAAAA,CACdlC,CAAAA,CACY,CACZ,GAAM,CACJ,MAAA,CAAAwB,EACA,KAAA,CAAAC,CAAAA,CAAQ,wBAAA,CACR,UAAA,CAAAU,CAAAA,CAAa,IAAA,CACb,QAAAvC,CAAAA,CAAU,2BAAA,CACV,KAAA,CAAOK,CAAAA,CAAU,UAAA,CAAW,KAAA,CAC5B,UAAA0B,CACF,CAAA,CAAI3B,CAAAA,CAEJ,OAAAL,CAAAA,CAAgBC,CAAO,EAGrB,OAAO,OAAA,CAAY,GAAA,EACnB,OAAA,CAAQ,GAAA,EAAK,QAAA,GAAa,cAC1B,CAAC4B,CAAAA,EAED,QAAQ,IAAA,CACN,yEACF,EAGK,MAAOjB,CAAAA,EAAqC,CACjD,IAAMQ,CAAAA,CAAW,MAAMd,EAAQ,CAAA,EAAGL,CAAO,CAAA,WAAA,CAAA,CAAe,CACtD,MAAA,CAAQ,MAAA,CACR,QAAS,CACP,cAAA,CAAgB,kBAAA,CAChB,aAAA,CAAe,CAAA,OAAA,EAAU4B,CAAM,EACjC,CAAA,CACA,IAAA,CAAM,IAAA,CAAK,SAAA,CAAU,CAAE,KAAA,CAAAC,EAAO,KAAA,CAAOlB,CAAAA,CAAM,UAAA,CAAA4B,CAAW,CAAC,CAAA,CACvD,OAAQ,WAAA,CAAY,OAAA,CAAQR,CAAAA,EAAa,GAAM,CACjD,CAAC,EAED,GAAI,CAACZ,CAAAA,CAAS,EAAA,CAAI,CAChB,IAAMC,EAAU,MAAMD,CAAAA,CAAS,IAAA,EAAK,CAAE,KAAA,CAAM,IAAM,EAAE,CAAA,CAEpD,MAAM,IAAI,KAAA,CACR,CAAA,qCAAA,EAAwCA,CAAAA,CAAS,MAAM,CAAA,EAAGC,CAAAA,CAAU,CAAA,QAAA,EAAMA,CAAAA,CAAQ,KAAA,CAAM,CAAA,CAAG,GAAG,CAAC,CAAA,CAAA,CAAK,EAAE,CAAA,CACxG,CACF,CAMA,IAAMoB,CAAAA,CAAAA,CAJQ,MAAMrB,CAAAA,CAAS,IAAA,EAAK,EAIf,IAAA,CAAK,CAAC,CAAA,CACzB,GAAI,CAACqB,CAAAA,CACH,MAAM,IAAI,MACR,iEACF,CAAA,CAGF,OAAOA,CAAAA,CAAM,SACf,CACF,CAgCO,SAASC,CAAAA,CACdrC,CAAAA,CACyB,CACzB,GAAM,CACJ,OAAAwB,CAAAA,CACA,KAAA,CAAAC,EAAQ,QAAA,CACR,SAAA,CAAAC,EACA,OAAA,CAAA9B,CAAAA,CAAU,2BAAA,CACV,KAAA,CAAOK,CAAAA,CAAU,UAAA,CAAW,MAC5B,KAAA,CAAAI,CACF,CAAA,CAAIL,CAAAA,CAEJ,OAAAL,CAAAA,CAAgBC,CAAO,CAAA,CAGrB,OAAO,OAAA,CAAY,GAAA,EACnB,OAAA,CAAQ,GAAA,EAAK,WAAa,YAAA,EAC1B,CAAC4B,CAAAA,EAED,OAAA,CAAQ,IAAA,CACN,gFACF,EAGK,MAAOhB,CAAAA,CAAOC,CAAAA,CAAO6B,CAAAA,GAAc,CACxC,IAAM3B,EAAY,IAAA,CAAK,GAAA,EAAI,CAC3BN,CAAAA,EAAO,YAAA,GAAe,CAAE,MAAAG,CAAAA,CAAO,KAAA,CAAAC,CAAAA,CAAO,SAAA,CAAWE,CAAU,CAAC,EAE5D,GAAI,CACF,IAAMI,CAAAA,CAAW,MAAMd,CAAAA,CAAQ,GAAGL,CAAO,CAAA,iBAAA,CAAA,CAAqB,CAC5D,MAAA,CAAQ,MAAA,CACR,OAAA,CAAS,CACP,cAAA,CAAgB,kBAAA,CAChB,aAAA,CAAe,CAAA,OAAA,EAAU4B,CAAM,CAAA,CACjC,EACA,IAAA,CAAM,IAAA,CAAK,SAAA,CAAU,CACnB,KAAA,CAAOhB,CAAAA,CAAM,OAASiB,CAAAA,CACtB,GAAIC,CAAAA,EAAa,IAAA,CAAO,CAAE,UAAA,CAAYA,CAAU,CAAA,CAAI,EAAC,CACrD,QAAA,CAAU,CACR,GAAIlB,EAAM,YAAA,CACN,CAAC,CAAE,IAAA,CAAM,QAAA,CAAU,OAAA,CAASA,EAAM,YAAa,CAAC,CAAA,CAChD,EAAC,CACL,CAAE,KAAM,MAAA,CAAQ,OAAA,CAASC,CAAM,CACjC,CAAA,CACA,MAAA,CAAQ,GACR,cAAA,CAAgB,CAAE,aAAA,CAAe,CAAA,CAAK,CACxC,CAAC,EACD,MAAA,CAAQ6B,CAAAA,CAAU,MACpB,CAAC,CAAA,CAED,GAAI,CAACvB,CAAAA,CAAS,EAAA,CAAI,CAChB,IAAMC,CAAAA,CAAU,MAAMD,EAAS,IAAA,EAAK,CAAE,KAAA,CAAM,IAAM,EAAE,CAAA,CAEpD,MAAM,IAAI,KAAA,CACR,CAAA,mCAAA,EAAsCA,CAAAA,CAAS,MAAM,CAAA,EAAGC,EAAU,CAAA,QAAA,EAAMA,CAAAA,CAAQ,KAAA,CAAM,CAAA,CAAG,GAAG,CAAC,GAAK,EAAE,CAAA,CACtG,CACF,CAEA,IAAMuB,CAAAA,CAASxB,EAAS,IAAA,EAAM,SAAA,EAAU,CACxC,GAAI,CAACwB,CAAAA,CACH,MAAM,IAAI,KAAA,CAAM,8BAA8B,CAAA,CAGhD,IAAMC,CAAAA,CAAU,IAAI,WAAA,CAChBC,CAAAA,CAAM,EAAA,CACNC,CAAAA,CAAW,EAAA,CACXC,CAAAA,CAAe,EACfC,CAAAA,CAAmB,CAAA,CAEvB,GAAI,CACF,OAAa,CACX,GAAM,CAAE,IAAA,CAAAC,CAAAA,CAAM,KAAA,CAAAC,CAAM,CAAA,CAAI,MAAMP,CAAAA,CAAO,IAAA,EAAK,CAC1C,GAAIM,CAAAA,CACF,MAGFJ,GAAOD,CAAAA,CAAQ,MAAA,CAAOM,CAAAA,CAAO,CAAE,MAAA,CAAQ,CAAA,CAAK,CAAC,CAAA,CAC7C,IAAMC,CAAAA,CAAQN,CAAAA,CAAI,KAAA,CAAM;AAAA,CAAI,CAAA,CAC5BA,EAAMM,CAAAA,CAAM,GAAA,IAAS,EAAA,CAErB,IAAA,IAAWC,KAAQD,CAAAA,CAAO,CACxB,GAAI,CAACC,CAAAA,CAAK,WAAW,QAAQ,CAAA,CAC3B,SAEF,IAAMjB,CAAAA,CAAOiB,EAAK,KAAA,CAAM,CAAC,EAAE,IAAA,EAAK,CAChC,GAAIjB,CAAAA,GAAS,QAAA,CAIb,GAAI,CACF,IAAMkB,EAAQ,IAAA,CAAK,KAAA,CAAMlB,CAAI,CAAA,CAGvBmB,CAAAA,CAAQD,EAAM,OAAA,GAAU,CAAC,GAAG,KAAA,CAC9BC,CAAAA,EAAO,OAAA,GACTR,CAAAA,EAAYQ,CAAAA,CAAM,OAAA,CAClBZ,EAAU,OAAA,GAAUY,CAAAA,CAAM,OAAO,CAAA,CAAA,CAI/BD,CAAAA,CAAM,QACRN,CAAAA,CAAeM,CAAAA,CAAM,MAAM,aAAA,EAAiB,CAAA,CAC5CL,EAAmBK,CAAAA,CAAM,KAAA,CAAM,mBAAqB,CAAA,EAExD,CAAA,MAASE,EAAU,CACjB,GAAIA,CAAAA,YAAoB,WAAA,CAEpB,OAAO,OAAA,CAAY,KACnB,OAAA,CAAQ,GAAA,EAAK,WAAa,aAAA,EAE1B,OAAA,CAAQ,KACN,8CAAA,CACApB,CACF,OAGF,MAAMoB,CAEV,CACF,CACF,CACF,QAAE,CACAZ,CAAAA,CAAO,QAAO,CAAE,KAAA,CAAM,IAAM,CAAC,CAAC,EAChC,CAEA,IAAMa,CAAAA,CAAwB,CAAE,IAAA,CAAM,WAAA,CAAa,QAASV,CAAS,CAAA,CACrEJ,EAAU,SAAA,GAAYc,CAAY,EAElC,IAAMlC,CAAAA,CAAyB,CAC7B,WAAA,CAAayB,CAAAA,CACb,aAAcC,CAChB,CAAA,CACMS,CAAAA,CAAcV,CAAAA,CAAeC,CAAAA,CAEnC,OAAAvC,GAAO,WAAA,GAAc,CACnB,MAAAG,CAAAA,CACA,KAAA,CAAAC,EACA,MAAA,CAAQiC,CAAAA,CACR,YAAAW,CAAAA,CACA,UAAA,CAAAnC,EACA,UAAA,CAAY,IAAA,CAAK,KAAI,CAAIP,CAAAA,CACzB,UAAW,IAAA,CAAK,GAAA,EAClB,CAAC,CAAA,CAEM,CACL,OAAQ+B,CAAAA,CACR,QAAA,CAAU,CAAC,CAAE,IAAA,CAAM,OAAiB,OAAA,CAASjC,CAAM,EAAG2C,CAAY,CAAA,CAClE,UAAW,EAAC,CACZ,YAAAC,CAAAA,CACA,UAAA,CAAAnC,CACF,CACF,CAAA,MAASpB,CAAAA,CAAK,CACZ,MAAIA,CAAAA,YAAe,OACjBO,CAAAA,EAAO,OAAA,GAAU,CACf,KAAA,CAAAG,CAAAA,CACA,MAAAC,CAAAA,CACA,KAAA,CAAOX,EACP,UAAA,CAAY,IAAA,CAAK,KAAI,CAAIa,CAAAA,CACzB,UAAW,IAAA,CAAK,GAAA,EAClB,CAAC,CAAA,CAGGb,CACR,CACF,CACF","file":"openai.js","sourcesContent":["/**\n * Agent utilities — createRunner, estimateCost, state queries, URL validation.\n */\n\nimport type {\n AdapterHooks,\n AgentLike,\n AgentRunner,\n AgentState,\n ApprovalState,\n Message,\n RunOptions,\n RunResult,\n TokenUsage,\n} from \"./types.js\";\n\n// ============================================================================\n// State Query Helpers\n// ============================================================================\n\n/** Check if agent is currently running. */\nexport function isAgentRunning(state: AgentState): boolean {\n return state.status === \"running\";\n}\n\n/** Check if there are pending approvals. */\nexport function hasPendingApprovals(state: ApprovalState): boolean {\n return state.pending.length > 0;\n}\n\n// ============================================================================\n// Cost Estimation\n// ============================================================================\n\n/**\n * Get total cost estimate based on token usage.\n *\n * @param tokenUsage - Total token count\n * @param ratePerMillionTokens - Cost per million tokens (required, no default to avoid stale pricing)\n * @returns Estimated cost in dollars\n */\nexport function estimateCost(\n tokenUsage: number,\n ratePerMillionTokens: number,\n): number {\n return (tokenUsage / 1_000_000) * ratePerMillionTokens;\n}\n\n// ============================================================================\n// Validation Helpers\n// ============================================================================\n\nconst ALLOWED_PROTOCOLS = new Set([\"http:\", \"https:\"]);\n\n/**\n * Validate that a baseURL uses http or https.\n * Throws immediately at adapter creation time (not at call time) to catch config errors early.\n */\nexport function validateBaseURL(baseURL: string): void {\n try {\n const url = new URL(baseURL);\n if (!ALLOWED_PROTOCOLS.has(url.protocol)) {\n throw new Error(\n `[Directive] Invalid baseURL protocol \"${url.protocol}\" – only http: and https: are allowed`,\n );\n }\n } catch (err) {\n if (err instanceof Error && err.message.startsWith(\"[Directive]\")) {\n throw err;\n }\n\n throw new Error(\n `[Directive] Invalid baseURL \"${baseURL}\" – must be a valid URL (e.g. \"https://api.openai.com/v1\")`,\n );\n }\n}\n\n// ============================================================================\n// createRunner Helper\n// ============================================================================\n\n/** Parsed response from an LLM provider */\nexport interface ParsedResponse {\n text: string;\n totalTokens: number;\n /** Input token count, when available from the provider */\n inputTokens?: number;\n /** Output token count, when available from the provider */\n outputTokens?: number;\n}\n\n/** Options for creating an AgentRunner from buildRequest/parseResponse */\nexport interface CreateRunnerOptions {\n fetch?: typeof globalThis.fetch;\n buildRequest: (\n agent: AgentLike,\n input: string,\n messages: Message[],\n ) => { url: string; init: RequestInit };\n parseResponse: (\n response: Response,\n messages: Message[],\n ) => Promise<ParsedResponse>;\n parseOutput?: <T>(text: string) => T;\n /** Lifecycle hooks for tracing, logging, and metrics */\n hooks?: AdapterHooks;\n}\n\n/**\n * Create an AgentRunner from buildRequest/parseResponse helpers.\n * Reduces ~50 lines of fetch boilerplate to ~20 lines of configuration.\n *\n * Supports lifecycle hooks for observability:\n * - `onBeforeCall` fires before each API request\n * - `onAfterCall` fires after a successful response (includes token breakdown)\n * - `onError` fires when the request fails\n *\n * @example\n * ```typescript\n * const runClaude = createRunner({\n * buildRequest: (agent, input) => ({\n * url: \"/api/claude\",\n * init: {\n * method: \"POST\",\n * headers: { \"Content-Type\": \"application/json\" },\n * body: JSON.stringify({\n * model: agent.model ?? \"claude-haiku-4-5-20251001\",\n * system: agent.instructions ?? \"\",\n * messages: [{ role: \"user\", content: input }],\n * }),\n * },\n * }),\n * parseResponse: async (res) => {\n * const data = await res.json();\n * const inputTokens = data.usage?.input_tokens ?? 0;\n * const outputTokens = data.usage?.output_tokens ?? 0;\n * return {\n * text: data.content?.[0]?.text ?? \"\",\n * totalTokens: inputTokens + outputTokens,\n * inputTokens,\n * outputTokens,\n * };\n * },\n * hooks: {\n * onAfterCall: ({ durationMs, tokenUsage }) => {\n * console.log(`LLM call: ${durationMs}ms, ${tokenUsage.inputTokens}in/${tokenUsage.outputTokens}out`);\n * },\n * },\n * });\n * ```\n */\nexport function createRunner(options: CreateRunnerOptions): AgentRunner {\n const {\n fetch: fetchFn = globalThis.fetch,\n buildRequest,\n parseResponse,\n parseOutput,\n hooks,\n } = options;\n\n const defaultParseOutput = <T>(text: string): T => {\n try {\n return JSON.parse(text) as T;\n } catch {\n return text as unknown as T;\n }\n };\n\n const parse = parseOutput ?? defaultParseOutput;\n\n return async <T = unknown>(\n agent: AgentLike,\n input: string,\n runOptions?: RunOptions,\n ): Promise<RunResult<T>> => {\n const startTime = Date.now();\n hooks?.onBeforeCall?.({ agent, input, timestamp: startTime });\n\n const messages: Message[] = [{ role: \"user\", content: input }];\n\n try {\n const { url, init } = buildRequest(agent, input, messages);\n\n const fetchInit: RequestInit = runOptions?.signal\n ? { ...init, signal: runOptions.signal }\n : init;\n\n const response = await fetchFn(url, fetchInit);\n\n if (!response.ok) {\n const errBody = await response.text().catch(() => \"\");\n\n throw new Error(\n `[Directive] AgentRunner request failed: ${response.status} ${response.statusText}${errBody ? ` – ${errBody.slice(0, 300)}` : \"\"}`,\n );\n }\n\n const parsed = await parseResponse(response, messages);\n const tokenUsage: TokenUsage = {\n inputTokens: parsed.inputTokens ?? 0,\n outputTokens: parsed.outputTokens ?? 0,\n };\n\n const assistantMessage: Message = {\n role: \"assistant\",\n content: parsed.text,\n };\n const allMessages: Message[] = [...messages, assistantMessage];\n\n runOptions?.onMessage?.(assistantMessage);\n\n const durationMs = Date.now() - startTime;\n hooks?.onAfterCall?.({\n agent,\n input,\n output: parsed.text,\n totalTokens: parsed.totalTokens,\n tokenUsage,\n durationMs,\n timestamp: Date.now(),\n });\n\n return {\n output: parse<T>(parsed.text),\n messages: allMessages,\n toolCalls: [],\n totalTokens: parsed.totalTokens,\n tokenUsage,\n };\n } catch (err) {\n const durationMs = Date.now() - startTime;\n if (err instanceof Error) {\n hooks?.onError?.({\n agent,\n input,\n error: err,\n durationMs,\n timestamp: Date.now(),\n });\n }\n\n throw err;\n }\n };\n}\n","/**\n * @directive-run/ai/openai\n *\n * OpenAI adapter for Directive AI. Provides runners and embedders\n * for OpenAI-compatible APIs (OpenAI, Azure, Together, etc.)\n *\n * @example\n * ```typescript\n * import { createOpenAIRunner, createOpenAIEmbedder } from '@directive-run/ai/openai';\n *\n * const runner = createOpenAIRunner({ apiKey: process.env.OPENAI_API_KEY! });\n * const embedder = createOpenAIEmbedder({ apiKey: process.env.OPENAI_API_KEY! });\n * ```\n */\n\nimport { createRunner, validateBaseURL } from \"../agent-utils.js\";\nimport type { EmbedderFn, Embedding } from \"../guardrails/semantic-cache.js\";\nimport type {\n AdapterHooks,\n AgentRunner,\n Message,\n TokenUsage,\n} from \"../types.js\";\nimport type { StreamingCallbackRunner } from \"../types.js\";\n\n// ============================================================================\n// Pricing Constants\n// ============================================================================\n\n/**\n * OpenAI model pricing (USD per million tokens).\n *\n * Use with `estimateCost()` for per-call cost tracking:\n * ```typescript\n * import { estimateCost } from '@directive-run/ai';\n * import { OPENAI_PRICING } from '@directive-run/ai/openai';\n *\n * const cost =\n * estimateCost(result.tokenUsage!.inputTokens, OPENAI_PRICING[\"gpt-4o\"].input) +\n * estimateCost(result.tokenUsage!.outputTokens, OPENAI_PRICING[\"gpt-4o\"].output);\n * ```\n *\n * **Note:** Pricing changes over time. These values are provided as a convenience\n * and may not reflect the latest rates. Always verify at https://openai.com/pricing\n */\nexport const OPENAI_PRICING: Record<string, { input: number; output: number }> =\n {\n \"gpt-4o\": { input: 2.5, output: 10 },\n \"gpt-4o-mini\": { input: 0.15, output: 0.6 },\n \"gpt-4-turbo\": { input: 10, output: 30 },\n \"o3-mini\": { input: 1.1, output: 4.4 },\n };\n\n// ============================================================================\n// OpenAI Runner\n// ============================================================================\n\n/** Options for createOpenAIRunner */\nexport interface OpenAIRunnerOptions {\n apiKey: string;\n model?: string;\n maxTokens?: number;\n baseURL?: string;\n fetch?: typeof globalThis.fetch;\n /** @default undefined */\n timeoutMs?: number;\n /** Lifecycle hooks for tracing, logging, and metrics */\n hooks?: AdapterHooks;\n}\n\n/**\n * Create an AgentRunner for OpenAI-compatible APIs (OpenAI, Azure, Together, etc.)\n *\n * Returns `tokenUsage` with input/output breakdown for cost tracking.\n *\n * @example\n * ```typescript\n * // OpenAI\n * const runner = createOpenAIRunner({ apiKey: process.env.OPENAI_API_KEY! });\n *\n * // Azure OpenAI\n * const azure = createOpenAIRunner({\n * apiKey: process.env.AZURE_KEY!,\n * baseURL: \"https://your-resource.openai.azure.com/v1\",\n * });\n *\n * // Together.ai (OpenAI-compatible)\n * const together = createOpenAIRunner({\n * apiKey: process.env.TOGETHER_KEY!,\n * baseURL: \"https://api.together.xyz/v1\",\n * });\n * ```\n */\nexport function createOpenAIRunner(options: OpenAIRunnerOptions): AgentRunner {\n const {\n apiKey,\n model = \"gpt-4o\",\n maxTokens,\n baseURL = \"https://api.openai.com/v1\",\n fetch: fetchFn = globalThis.fetch,\n timeoutMs,\n hooks,\n } = options;\n\n validateBaseURL(baseURL);\n\n if (\n typeof process !== \"undefined\" &&\n process.env?.NODE_ENV !== \"production\" &&\n !apiKey\n ) {\n console.warn(\n \"[Directive] createOpenAIRunner: apiKey is empty. API calls will fail.\",\n );\n }\n\n return createRunner({\n fetch: fetchFn,\n hooks,\n buildRequest: (agent, _input, messages) => ({\n url: `${baseURL}/chat/completions`,\n init: {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify({\n model: agent.model ?? model,\n ...(maxTokens != null ? { max_tokens: maxTokens } : {}),\n messages: [\n ...(agent.instructions\n ? [{ role: \"system\", content: agent.instructions }]\n : []),\n ...messages.map((m) => ({ role: m.role, content: m.content })),\n ],\n }),\n ...(timeoutMs != null\n ? { signal: AbortSignal.timeout(timeoutMs) }\n : {}),\n },\n }),\n parseResponse: async (res) => {\n const data = await res.json();\n const text = data.choices?.[0]?.message?.content ?? \"\";\n const inputTokens = data.usage?.prompt_tokens ?? 0;\n const outputTokens = data.usage?.completion_tokens ?? 0;\n\n return {\n text,\n totalTokens: inputTokens + outputTokens,\n inputTokens,\n outputTokens,\n };\n },\n });\n}\n\n// ============================================================================\n// OpenAI Embedder\n// ============================================================================\n\n/** Options for createOpenAIEmbedder */\nexport interface OpenAIEmbedderOptions {\n apiKey: string;\n model?: string;\n dimensions?: number;\n baseURL?: string;\n fetch?: typeof globalThis.fetch;\n /** @default 30000 */\n timeoutMs?: number;\n}\n\n/**\n * Create an EmbedderFn that calls the OpenAI embeddings API.\n *\n * @example\n * ```typescript\n * const embedder = createOpenAIEmbedder({ apiKey: process.env.OPENAI_API_KEY! });\n * const embedding = await embedder('How do constraints work?');\n * ```\n */\nexport function createOpenAIEmbedder(\n options: OpenAIEmbedderOptions,\n): EmbedderFn {\n const {\n apiKey,\n model = \"text-embedding-3-small\",\n dimensions = 1536,\n baseURL = \"https://api.openai.com/v1\",\n fetch: fetchFn = globalThis.fetch,\n timeoutMs,\n } = options;\n\n validateBaseURL(baseURL);\n\n if (\n typeof process !== \"undefined\" &&\n process.env?.NODE_ENV !== \"production\" &&\n !apiKey\n ) {\n console.warn(\n \"[Directive] createOpenAIEmbedder: apiKey is empty. API calls will fail.\",\n );\n }\n\n return async (text: string): Promise<Embedding> => {\n const response = await fetchFn(`${baseURL}/embeddings`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify({ model, input: text, dimensions }),\n signal: AbortSignal.timeout(timeoutMs ?? 30_000),\n });\n\n if (!response.ok) {\n const errBody = await response.text().catch(() => \"\");\n\n throw new Error(\n `[Directive] OpenAI embedding failed: ${response.status}${errBody ? ` – ${errBody.slice(0, 200)}` : \"\"}`,\n );\n }\n\n const data = (await response.json()) as {\n data: Array<{ embedding: number[] }>;\n };\n\n const entry = data.data[0];\n if (!entry) {\n throw new Error(\n \"[Directive] OpenAI embedding response contained no data entries\",\n );\n }\n\n return entry.embedding;\n };\n}\n\n// ============================================================================\n// OpenAI Streaming Runner\n// ============================================================================\n\n/** Options for createOpenAIStreamingRunner */\nexport interface OpenAIStreamingRunnerOptions {\n apiKey: string;\n model?: string;\n maxTokens?: number;\n baseURL?: string;\n fetch?: typeof globalThis.fetch;\n /** Lifecycle hooks for tracing, logging, and metrics */\n hooks?: AdapterHooks;\n}\n\n/**\n * Create a StreamingCallbackRunner for OpenAI-compatible chat completions\n * with server-sent events. Can be used standalone or paired with `createOpenAIRunner`.\n *\n * Returns `tokenUsage` with input/output breakdown for cost tracking.\n *\n * @example\n * ```typescript\n * const streamingRunner = createOpenAIStreamingRunner({\n * apiKey: process.env.OPENAI_API_KEY!,\n * });\n * const streamRunner = createStreamingRunner(streamingRunner);\n * const { stream, result } = streamRunner(agent, input);\n * ```\n */\nexport function createOpenAIStreamingRunner(\n options: OpenAIStreamingRunnerOptions,\n): StreamingCallbackRunner {\n const {\n apiKey,\n model = \"gpt-4o\",\n maxTokens,\n baseURL = \"https://api.openai.com/v1\",\n fetch: fetchFn = globalThis.fetch,\n hooks,\n } = options;\n\n validateBaseURL(baseURL);\n\n if (\n typeof process !== \"undefined\" &&\n process.env?.NODE_ENV !== \"production\" &&\n !apiKey\n ) {\n console.warn(\n \"[Directive] createOpenAIStreamingRunner: apiKey is empty. API calls will fail.\",\n );\n }\n\n return async (agent, input, callbacks) => {\n const startTime = Date.now();\n hooks?.onBeforeCall?.({ agent, input, timestamp: startTime });\n\n try {\n const response = await fetchFn(`${baseURL}/chat/completions`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify({\n model: agent.model ?? model,\n ...(maxTokens != null ? { max_tokens: maxTokens } : {}),\n messages: [\n ...(agent.instructions\n ? [{ role: \"system\", content: agent.instructions }]\n : []),\n { role: \"user\", content: input },\n ],\n stream: true,\n stream_options: { include_usage: true },\n }),\n signal: callbacks.signal,\n });\n\n if (!response.ok) {\n const errBody = await response.text().catch(() => \"\");\n\n throw new Error(\n `[Directive] OpenAI streaming error ${response.status}${errBody ? ` – ${errBody.slice(0, 200)}` : \"\"}`,\n );\n }\n\n const reader = response.body?.getReader();\n if (!reader) {\n throw new Error(\"[Directive] No response body\");\n }\n\n const decoder = new TextDecoder();\n let buf = \"\";\n let fullText = \"\";\n let promptTokens = 0;\n let completionTokens = 0;\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n\n buf += decoder.decode(value, { stream: true });\n const lines = buf.split(\"\\n\");\n buf = lines.pop() ?? \"\";\n\n for (const line of lines) {\n if (!line.startsWith(\"data: \")) {\n continue;\n }\n const data = line.slice(6).trim();\n if (data === \"[DONE]\") {\n continue;\n }\n\n try {\n const event = JSON.parse(data);\n\n // Extract token content from delta\n const delta = event.choices?.[0]?.delta;\n if (delta?.content) {\n fullText += delta.content;\n callbacks.onToken?.(delta.content);\n }\n\n // Extract usage from the final chunk (stream_options: include_usage)\n if (event.usage) {\n promptTokens = event.usage.prompt_tokens ?? 0;\n completionTokens = event.usage.completion_tokens ?? 0;\n }\n } catch (parseErr) {\n if (parseErr instanceof SyntaxError) {\n if (\n typeof process !== \"undefined\" &&\n process.env?.NODE_ENV === \"development\"\n ) {\n console.warn(\n \"[Directive] Malformed SSE event from OpenAI:\",\n data,\n );\n }\n } else {\n throw parseErr;\n }\n }\n }\n }\n } finally {\n reader.cancel().catch(() => {});\n }\n\n const assistantMsg: Message = { role: \"assistant\", content: fullText };\n callbacks.onMessage?.(assistantMsg);\n\n const tokenUsage: TokenUsage = {\n inputTokens: promptTokens,\n outputTokens: completionTokens,\n };\n const totalTokens = promptTokens + completionTokens;\n\n hooks?.onAfterCall?.({\n agent,\n input,\n output: fullText,\n totalTokens,\n tokenUsage,\n durationMs: Date.now() - startTime,\n timestamp: Date.now(),\n });\n\n return {\n output: fullText,\n messages: [{ role: \"user\" as const, content: input }, assistantMsg],\n toolCalls: [],\n totalTokens,\n tokenUsage,\n };\n } catch (err) {\n if (err instanceof Error) {\n hooks?.onError?.({\n agent,\n input,\n error: err,\n durationMs: Date.now() - startTime,\n timestamp: Date.now(),\n });\n }\n\n throw err;\n }\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/agent-utils.ts","../src/adapters/openai.ts"],"names":["ALLOWED_PROTOCOLS","validateBaseURL","baseURL","url","err","createRunner","options","fetchFn","buildRequest","parseResponse","parseOutput","hooks","parse","text","agent","input","runOptions","startTime","messages","init","fetchInit","response","errBody","parsed","tokenUsage","assistantMessage","allMessages","durationMs","OPENAI_PRICING","createOpenAIRunner","apiKey","model","maxTokens","timeoutMs","_input","m","res","data","inputTokens","outputTokens","createOpenAIEmbedder","dimensions","entry","createOpenAIStreamingRunner","callbacks","reader","decoder","buf","fullText","promptTokens","completionTokens","done","value","lines","line","event","delta","parseErr","assistantMsg","totalTokens"],"mappings":"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","file":"openai.js","sourcesContent":["/**\n * Agent utilities — createRunner, estimateCost, state queries, URL validation.\n */\n\nimport type {\n AdapterHooks,\n AgentLike,\n AgentRunner,\n AgentState,\n ApprovalState,\n Message,\n RunOptions,\n RunResult,\n TokenUsage,\n} from \"./types.js\";\n\n// ============================================================================\n// State Query Helpers\n// ============================================================================\n\n/**\n * Check whether an agent is currently executing a run.\n *\n * @param state - The current {@link AgentState} to inspect.\n * @returns `true` when the agent status is `\"running\"`.\n */\nexport function isAgentRunning(state: AgentState): boolean {\n return state.status === \"running\";\n}\n\n/**\n * Check whether there are tool-call approvals waiting for user confirmation.\n *\n * @param state - The current {@link ApprovalState} to inspect.\n * @returns `true` when one or more approvals are pending.\n */\nexport function hasPendingApprovals(state: ApprovalState): boolean {\n return state.pending.length > 0;\n}\n\n// ============================================================================\n// Cost Estimation\n// ============================================================================\n\n/**\n * Estimate the dollar cost of an agent run based on total token usage.\n *\n * @remarks\n * No default rate is provided — callers must supply the current per-million-token\n * price to avoid silently using stale pricing.\n *\n * @param tokenUsage - Total number of tokens consumed (input + output).\n * @param ratePerMillionTokens - Cost in dollars per one million tokens.\n * @returns Estimated cost in dollars.\n */\nexport function estimateCost(\n tokenUsage: number,\n ratePerMillionTokens: number,\n): number {\n return (tokenUsage / 1_000_000) * ratePerMillionTokens;\n}\n\n// ============================================================================\n// Validation Helpers\n// ============================================================================\n\nconst ALLOWED_PROTOCOLS = new Set([\"http:\", \"https:\"]);\n\n/**\n * Validate that a base URL uses the `http:` or `https:` protocol.\n * Throws immediately at adapter creation time (not at call time) to surface\n * configuration errors before any LLM requests are made.\n *\n * @param baseURL - The base URL string to validate.\n * @throws When the URL is malformed or uses a protocol other than `http:` or `https:`.\n */\nexport function validateBaseURL(baseURL: string): void {\n try {\n const url = new URL(baseURL);\n if (!ALLOWED_PROTOCOLS.has(url.protocol)) {\n throw new Error(\n `[Directive] Invalid baseURL protocol \"${url.protocol}\" – only http: and https: are allowed`,\n );\n }\n } catch (err) {\n if (err instanceof Error && err.message.startsWith(\"[Directive]\")) {\n throw err;\n }\n\n throw new Error(\n `[Directive] Invalid baseURL \"${baseURL}\" – must be a valid URL (e.g. \"https://api.openai.com/v1\")`,\n );\n }\n}\n\n// ============================================================================\n// createRunner Helper\n// ============================================================================\n\n/** Parsed response from an LLM provider */\nexport interface ParsedResponse {\n text: string;\n totalTokens: number;\n /** Input token count, when available from the provider */\n inputTokens?: number;\n /** Output token count, when available from the provider */\n outputTokens?: number;\n}\n\n/** Options for creating an AgentRunner from buildRequest/parseResponse */\nexport interface CreateRunnerOptions {\n fetch?: typeof globalThis.fetch;\n buildRequest: (\n agent: AgentLike,\n input: string,\n messages: Message[],\n ) => { url: string; init: RequestInit };\n parseResponse: (\n response: Response,\n messages: Message[],\n ) => Promise<ParsedResponse>;\n parseOutput?: <T>(text: string) => T;\n /** Lifecycle hooks for tracing, logging, and metrics */\n hooks?: AdapterHooks;\n}\n\n/**\n * Create an {@link AgentRunner} from `buildRequest`/`parseResponse` helpers, reducing\n * ~50 lines of fetch boilerplate to ~20 lines of configuration.\n *\n * @remarks\n * Supports lifecycle hooks for observability:\n * - `onBeforeCall` fires before each API request\n * - `onAfterCall` fires after a successful response (includes token breakdown)\n * - `onError` fires when the request fails\n *\n * Output parsing defaults to `JSON.parse` with a string fallback. Supply a custom\n * `parseOutput` to override (e.g. for structured output schemas).\n *\n * @param options - Configuration for the runner, including request building, response parsing, and hooks.\n * @returns An {@link AgentRunner} function that performs LLM calls via fetch.\n *\n * @example\n * ```typescript\n * const runClaude = createRunner({\n * buildRequest: (agent, input) => ({\n * url: \"/api/claude\",\n * init: {\n * method: \"POST\",\n * headers: { \"Content-Type\": \"application/json\" },\n * body: JSON.stringify({\n * model: agent.model ?? \"claude-haiku-4-5-20251001\",\n * system: agent.instructions ?? \"\",\n * messages: [{ role: \"user\", content: input }],\n * }),\n * },\n * }),\n * parseResponse: async (res) => {\n * const data = await res.json();\n * const inputTokens = data.usage?.input_tokens ?? 0;\n * const outputTokens = data.usage?.output_tokens ?? 0;\n * return {\n * text: data.content?.[0]?.text ?? \"\",\n * totalTokens: inputTokens + outputTokens,\n * inputTokens,\n * outputTokens,\n * };\n * },\n * hooks: {\n * onAfterCall: ({ durationMs, tokenUsage }) => {\n * console.log(`LLM call: ${durationMs}ms, ${tokenUsage.inputTokens}in/${tokenUsage.outputTokens}out`);\n * },\n * },\n * });\n * ```\n *\n * @public\n */\nexport function createRunner(options: CreateRunnerOptions): AgentRunner {\n const {\n fetch: fetchFn = globalThis.fetch,\n buildRequest,\n parseResponse,\n parseOutput,\n hooks,\n } = options;\n\n const defaultParseOutput = <T>(text: string): T => {\n try {\n return JSON.parse(text) as T;\n } catch {\n return text as unknown as T;\n }\n };\n\n const parse = parseOutput ?? defaultParseOutput;\n\n return async <T = unknown>(\n agent: AgentLike,\n input: string,\n runOptions?: RunOptions,\n ): Promise<RunResult<T>> => {\n const startTime = Date.now();\n hooks?.onBeforeCall?.({ agent, input, timestamp: startTime });\n\n const messages: Message[] = [{ role: \"user\", content: input }];\n\n try {\n const { url, init } = buildRequest(agent, input, messages);\n\n const fetchInit: RequestInit = runOptions?.signal\n ? { ...init, signal: runOptions.signal }\n : init;\n\n const response = await fetchFn(url, fetchInit);\n\n if (!response.ok) {\n const errBody = await response.text().catch(() => \"\");\n\n throw new Error(\n `[Directive] AgentRunner request failed: ${response.status} ${response.statusText}${errBody ? ` – ${errBody.slice(0, 300)}` : \"\"}`,\n );\n }\n\n const parsed = await parseResponse(response, messages);\n const tokenUsage: TokenUsage = {\n inputTokens: parsed.inputTokens ?? 0,\n outputTokens: parsed.outputTokens ?? 0,\n };\n\n const assistantMessage: Message = {\n role: \"assistant\",\n content: parsed.text,\n };\n const allMessages: Message[] = [...messages, assistantMessage];\n\n runOptions?.onMessage?.(assistantMessage);\n\n const durationMs = Date.now() - startTime;\n hooks?.onAfterCall?.({\n agent,\n input,\n output: parsed.text,\n totalTokens: parsed.totalTokens,\n tokenUsage,\n durationMs,\n timestamp: Date.now(),\n });\n\n return {\n output: parse<T>(parsed.text),\n messages: allMessages,\n toolCalls: [],\n totalTokens: parsed.totalTokens,\n tokenUsage,\n };\n } catch (err) {\n const durationMs = Date.now() - startTime;\n if (err instanceof Error) {\n hooks?.onError?.({\n agent,\n input,\n error: err,\n durationMs,\n timestamp: Date.now(),\n });\n }\n\n throw err;\n }\n };\n}\n","/**\n * @directive-run/ai/openai\n *\n * OpenAI adapter for Directive AI. Provides runners and embedders\n * for OpenAI-compatible APIs (OpenAI, Azure, Together, etc.)\n *\n * @example\n * ```typescript\n * import { createOpenAIRunner, createOpenAIEmbedder } from '@directive-run/ai/openai';\n *\n * const runner = createOpenAIRunner({ apiKey: process.env.OPENAI_API_KEY! });\n * const embedder = createOpenAIEmbedder({ apiKey: process.env.OPENAI_API_KEY! });\n * ```\n */\n\nimport { createRunner, validateBaseURL } from \"../agent-utils.js\";\nimport type { EmbedderFn, Embedding } from \"../guardrails/semantic-cache.js\";\nimport type {\n AdapterHooks,\n AgentRunner,\n Message,\n TokenUsage,\n} from \"../types.js\";\nimport type { StreamingCallbackRunner } from \"../types.js\";\n\n// ============================================================================\n// Pricing Constants\n// ============================================================================\n\n/**\n * OpenAI model pricing (USD per million tokens).\n *\n * Use with `estimateCost()` for per-call cost tracking:\n * ```typescript\n * import { estimateCost } from '@directive-run/ai';\n * import { OPENAI_PRICING } from '@directive-run/ai/openai';\n *\n * const cost =\n * estimateCost(result.tokenUsage!.inputTokens, OPENAI_PRICING[\"gpt-4o\"].input) +\n * estimateCost(result.tokenUsage!.outputTokens, OPENAI_PRICING[\"gpt-4o\"].output);\n * ```\n *\n * **Note:** Pricing changes over time. These values are provided as a convenience\n * and may not reflect the latest rates. Always verify at https://openai.com/pricing\n */\nexport const OPENAI_PRICING: Record<string, { input: number; output: number }> =\n {\n \"gpt-4o\": { input: 2.5, output: 10 },\n \"gpt-4o-mini\": { input: 0.15, output: 0.6 },\n \"gpt-4-turbo\": { input: 10, output: 30 },\n \"o3-mini\": { input: 1.1, output: 4.4 },\n };\n\n// ============================================================================\n// OpenAI Runner\n// ============================================================================\n\n/** Options for createOpenAIRunner */\nexport interface OpenAIRunnerOptions {\n apiKey: string;\n model?: string;\n maxTokens?: number;\n baseURL?: string;\n fetch?: typeof globalThis.fetch;\n /** @default undefined */\n timeoutMs?: number;\n /** Lifecycle hooks for tracing, logging, and metrics */\n hooks?: AdapterHooks;\n}\n\n/**\n * Create an AgentRunner for OpenAI-compatible APIs (OpenAI, Azure, Together, etc.)\n *\n * Returns `tokenUsage` with input/output breakdown for cost tracking.\n *\n * @example\n * ```typescript\n * // OpenAI\n * const runner = createOpenAIRunner({ apiKey: process.env.OPENAI_API_KEY! });\n *\n * // Azure OpenAI\n * const azure = createOpenAIRunner({\n * apiKey: process.env.AZURE_KEY!,\n * baseURL: \"https://your-resource.openai.azure.com/v1\",\n * });\n *\n * // Together.ai (OpenAI-compatible)\n * const together = createOpenAIRunner({\n * apiKey: process.env.TOGETHER_KEY!,\n * baseURL: \"https://api.together.xyz/v1\",\n * });\n * ```\n */\nexport function createOpenAIRunner(options: OpenAIRunnerOptions): AgentRunner {\n const {\n apiKey,\n model = \"gpt-4o\",\n maxTokens,\n baseURL = \"https://api.openai.com/v1\",\n fetch: fetchFn = globalThis.fetch,\n timeoutMs,\n hooks,\n } = options;\n\n validateBaseURL(baseURL);\n\n if (\n typeof process !== \"undefined\" &&\n process.env?.NODE_ENV !== \"production\" &&\n !apiKey\n ) {\n console.warn(\n \"[Directive] createOpenAIRunner: apiKey is empty. API calls will fail.\",\n );\n }\n\n return createRunner({\n fetch: fetchFn,\n hooks,\n buildRequest: (agent, _input, messages) => ({\n url: `${baseURL}/chat/completions`,\n init: {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify({\n model: agent.model ?? model,\n ...(maxTokens != null ? { max_tokens: maxTokens } : {}),\n messages: [\n ...(agent.instructions\n ? [{ role: \"system\", content: agent.instructions }]\n : []),\n ...messages.map((m) => ({ role: m.role, content: m.content })),\n ],\n }),\n ...(timeoutMs != null\n ? { signal: AbortSignal.timeout(timeoutMs) }\n : {}),\n },\n }),\n parseResponse: async (res) => {\n const data = await res.json();\n const text = data.choices?.[0]?.message?.content ?? \"\";\n const inputTokens = data.usage?.prompt_tokens ?? 0;\n const outputTokens = data.usage?.completion_tokens ?? 0;\n\n return {\n text,\n totalTokens: inputTokens + outputTokens,\n inputTokens,\n outputTokens,\n };\n },\n });\n}\n\n// ============================================================================\n// OpenAI Embedder\n// ============================================================================\n\n/** Options for createOpenAIEmbedder */\nexport interface OpenAIEmbedderOptions {\n apiKey: string;\n model?: string;\n dimensions?: number;\n baseURL?: string;\n fetch?: typeof globalThis.fetch;\n /** @default 30000 */\n timeoutMs?: number;\n}\n\n/**\n * Create an EmbedderFn that calls the OpenAI embeddings API.\n *\n * @example\n * ```typescript\n * const embedder = createOpenAIEmbedder({ apiKey: process.env.OPENAI_API_KEY! });\n * const embedding = await embedder('How do constraints work?');\n * ```\n */\nexport function createOpenAIEmbedder(\n options: OpenAIEmbedderOptions,\n): EmbedderFn {\n const {\n apiKey,\n model = \"text-embedding-3-small\",\n dimensions = 1536,\n baseURL = \"https://api.openai.com/v1\",\n fetch: fetchFn = globalThis.fetch,\n timeoutMs,\n } = options;\n\n validateBaseURL(baseURL);\n\n if (\n typeof process !== \"undefined\" &&\n process.env?.NODE_ENV !== \"production\" &&\n !apiKey\n ) {\n console.warn(\n \"[Directive] createOpenAIEmbedder: apiKey is empty. API calls will fail.\",\n );\n }\n\n return async (text: string): Promise<Embedding> => {\n const response = await fetchFn(`${baseURL}/embeddings`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify({ model, input: text, dimensions }),\n signal: AbortSignal.timeout(timeoutMs ?? 30_000),\n });\n\n if (!response.ok) {\n const errBody = await response.text().catch(() => \"\");\n\n throw new Error(\n `[Directive] OpenAI embedding failed: ${response.status}${errBody ? ` – ${errBody.slice(0, 200)}` : \"\"}`,\n );\n }\n\n const data = (await response.json()) as {\n data: Array<{ embedding: number[] }>;\n };\n\n const entry = data.data[0];\n if (!entry) {\n throw new Error(\n \"[Directive] OpenAI embedding response contained no data entries\",\n );\n }\n\n return entry.embedding;\n };\n}\n\n// ============================================================================\n// OpenAI Streaming Runner\n// ============================================================================\n\n/** Options for createOpenAIStreamingRunner */\nexport interface OpenAIStreamingRunnerOptions {\n apiKey: string;\n model?: string;\n maxTokens?: number;\n baseURL?: string;\n fetch?: typeof globalThis.fetch;\n /** Lifecycle hooks for tracing, logging, and metrics */\n hooks?: AdapterHooks;\n}\n\n/**\n * Create a StreamingCallbackRunner for OpenAI-compatible chat completions\n * with server-sent events. Can be used standalone or paired with `createOpenAIRunner`.\n *\n * Returns `tokenUsage` with input/output breakdown for cost tracking.\n *\n * @example\n * ```typescript\n * const streamingRunner = createOpenAIStreamingRunner({\n * apiKey: process.env.OPENAI_API_KEY!,\n * });\n * const streamRunner = createStreamingRunner(streamingRunner);\n * const { stream, result } = streamRunner(agent, input);\n * ```\n */\nexport function createOpenAIStreamingRunner(\n options: OpenAIStreamingRunnerOptions,\n): StreamingCallbackRunner {\n const {\n apiKey,\n model = \"gpt-4o\",\n maxTokens,\n baseURL = \"https://api.openai.com/v1\",\n fetch: fetchFn = globalThis.fetch,\n hooks,\n } = options;\n\n validateBaseURL(baseURL);\n\n if (\n typeof process !== \"undefined\" &&\n process.env?.NODE_ENV !== \"production\" &&\n !apiKey\n ) {\n console.warn(\n \"[Directive] createOpenAIStreamingRunner: apiKey is empty. API calls will fail.\",\n );\n }\n\n return async (agent, input, callbacks) => {\n const startTime = Date.now();\n hooks?.onBeforeCall?.({ agent, input, timestamp: startTime });\n\n try {\n const response = await fetchFn(`${baseURL}/chat/completions`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify({\n model: agent.model ?? model,\n ...(maxTokens != null ? { max_tokens: maxTokens } : {}),\n messages: [\n ...(agent.instructions\n ? [{ role: \"system\", content: agent.instructions }]\n : []),\n { role: \"user\", content: input },\n ],\n stream: true,\n stream_options: { include_usage: true },\n }),\n signal: callbacks.signal,\n });\n\n if (!response.ok) {\n const errBody = await response.text().catch(() => \"\");\n\n throw new Error(\n `[Directive] OpenAI streaming error ${response.status}${errBody ? ` – ${errBody.slice(0, 200)}` : \"\"}`,\n );\n }\n\n const reader = response.body?.getReader();\n if (!reader) {\n throw new Error(\"[Directive] No response body\");\n }\n\n const decoder = new TextDecoder();\n let buf = \"\";\n let fullText = \"\";\n let promptTokens = 0;\n let completionTokens = 0;\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n\n buf += decoder.decode(value, { stream: true });\n const lines = buf.split(\"\\n\");\n buf = lines.pop() ?? \"\";\n\n for (const line of lines) {\n if (!line.startsWith(\"data: \")) {\n continue;\n }\n const data = line.slice(6).trim();\n if (data === \"[DONE]\") {\n continue;\n }\n\n try {\n const event = JSON.parse(data);\n\n // Extract token content from delta\n const delta = event.choices?.[0]?.delta;\n if (delta?.content) {\n fullText += delta.content;\n callbacks.onToken?.(delta.content);\n }\n\n // Extract usage from the final chunk (stream_options: include_usage)\n if (event.usage) {\n promptTokens = event.usage.prompt_tokens ?? 0;\n completionTokens = event.usage.completion_tokens ?? 0;\n }\n } catch (parseErr) {\n if (parseErr instanceof SyntaxError) {\n if (\n typeof process !== \"undefined\" &&\n process.env?.NODE_ENV === \"development\"\n ) {\n console.warn(\n \"[Directive] Malformed SSE event from OpenAI:\",\n data,\n );\n }\n } else {\n throw parseErr;\n }\n }\n }\n }\n } finally {\n reader.cancel().catch(() => {});\n }\n\n const assistantMsg: Message = { role: \"assistant\", content: fullText };\n callbacks.onMessage?.(assistantMsg);\n\n const tokenUsage: TokenUsage = {\n inputTokens: promptTokens,\n outputTokens: completionTokens,\n };\n const totalTokens = promptTokens + completionTokens;\n\n hooks?.onAfterCall?.({\n agent,\n input,\n output: fullText,\n totalTokens,\n tokenUsage,\n durationMs: Date.now() - startTime,\n timestamp: Date.now(),\n });\n\n return {\n output: fullText,\n messages: [{ role: \"user\" as const, content: input }, assistantMsg],\n toolCalls: [],\n totalTokens,\n tokenUsage,\n };\n } catch (err) {\n if (err instanceof Error) {\n hooks?.onError?.({\n agent,\n input,\n error: err,\n durationMs: Date.now() - startTime,\n timestamp: Date.now(),\n });\n }\n\n throw err;\n }\n };\n}\n"]}
package/dist/testing.cjs CHANGED
@@ -1,4 +1,4 @@
1
- 'use strict';var core=require('@directive-run/core'),adapterUtils=require('@directive-run/core/adapter-utils');var et=class extends Error{code;guardrailName;guardrailType;userMessage;agentName;constructor(t){super(t.message,{cause:t.cause}),this.name="GuardrailError",this.code=t.code,this.guardrailName=t.guardrailName,this.guardrailType=t.guardrailType,this.userMessage=t.userMessage??t.message,this.agentName=t.agentName,Object.defineProperty(this,"input",{value:t.input,enumerable:false,writable:false,configurable:false}),Object.defineProperty(this,"data",{value:t.data,enumerable:false,writable:false,configurable:false});}toJSON(){return {name:this.name,code:this.code,message:this.message,guardrailName:this.guardrailName,guardrailType:this.guardrailType,userMessage:this.userMessage,agentName:this.agentName}}};function Hn(i){return i instanceof et}var hn="__agent",Ot="__approval",bn="__conversation",kn="__toolCalls",Mt="__breakpoints";var tt="__scratchpad",tn={facts:{[hn]:core.t.object(),[Ot]:core.t.object(),[bn]:core.t.array(),[kn]:core.t.array(),[Mt]:core.t.object()},derivations:{},events:{},requirements:{}};var Sr=new Set(["agent_start","agent_complete","agent_error","agent_retry","guardrail_check","constraint_evaluate","resolver_start","resolver_complete","resolver_error","approval_request","approval_response","handoff_start","handoff_complete","pattern_start","pattern_complete","dag_node_update","breakpoint_hit","breakpoint_resumed","derivation_update","scratchpad_update","reflection_iteration","race_start","race_winner","race_cancelled","debate_round","reroute","checkpoint_save","checkpoint_restore","task_start","task_complete","task_error","task_progress","goal_step"]),Un=new Set(["__proto__","constructor","prototype","toString","valueOf","hasOwnProperty"]);function Jt(i={}){let t=i.maxEvents??2e3,u=i.goToSnapshot;if(!Number.isFinite(t)||t<1)throw new Error("[Directive DebugTimeline] maxEvents must be >= 1");let a=[],d=0,f=new Set;return {record(S){let w={...S,id:d++};a.push(w);let v=a.length-t;v>0&&a.splice(0,v);for(let A of f)try{A(w);}catch(Q){typeof process<"u"&&process.env?.NODE_ENV!=="production"&&console.error("[Directive DebugTimeline] Listener threw:",Q instanceof Error?Q.message:Q);}return w},getEvents(){return [...a]},getEventsForAgent(S){return a.filter(w=>w.agentId===S)},getEventsByType(S){return a.filter(w=>w.type===S)},getEventsAtSnapshot(S){return a.filter(w=>w.snapshotId===S)},getEventsInRange(S,w){return a.filter(v=>v.timestamp>=S&&v.timestamp<=w)},forkFrom(S){let w=-1;for(let v=a.length-1;v>=0;v--)if(a[v].snapshotId!==null&&a[v].snapshotId<=S){w=a[v].id;break}if(w>=0){let v=a.length;for(let A=a.length-1;A>=0;A--)if(a[A].id<=w){v=A+1;break}a=a.slice(0,v);}else a=[];u&&u(S);},export(){return JSON.stringify({version:1,events:a,nextId:d})},import(S){let w;try{w=JSON.parse(S);}catch{throw new Error("[Directive DebugTimeline] Invalid JSON")}if(!w||typeof w!="object")throw new Error("[Directive DebugTimeline] Invalid timeline data");for(let Q of Object.keys(w))if(Un.has(Q))throw new Error(`[Directive DebugTimeline] Blocked key in import: ${Q}`);let v=w;if(!Array.isArray(v.events))throw new Error("[Directive DebugTimeline] Missing events array");let A=[];for(let Q of v.events){if(!Q||typeof Q!="object")continue;for(let Ce of Object.keys(Q))if(Un.has(Ce))throw new Error(`[Directive DebugTimeline] Blocked key in event: ${Ce}`);let F=Q;typeof F.id=="number"&&typeof F.type=="string"&&Sr.has(F.type)&&typeof F.timestamp=="number"&&A.push(Q);}a=A.length>t?A.slice(-t):A,d=typeof v.nextId=="number"?v.nextId:A.length;},clear(){a=[],d=0;},subscribe(S){return f.add(S),()=>{f.delete(S);}},get length(){return a.length}}}function yn(i,t){let u=new Map;return {name:"directive-ai-debug-timeline",onConstraintEvaluate(a,d){i.record({type:"constraint_evaluate",timestamp:Date.now(),snapshotId:t(),constraintId:a,fired:d});},onResolverStart(a,d){u.set(a,Date.now()),i.record({type:"resolver_start",timestamp:Date.now(),snapshotId:t(),resolverId:a,requirementType:d.requirement.type});},onResolverComplete(a){let d=u.get(a);u.delete(a),i.record({type:"resolver_complete",timestamp:Date.now(),snapshotId:t(),resolverId:a,durationMs:d?Date.now()-d:0});},onResolverError(a,d,f){let k=u.get(a);u.delete(a);let S=f instanceof Error?f.message:String(f);i.record({type:"resolver_error",timestamp:Date.now(),snapshotId:t(),resolverId:a,errorMessage:S,durationMs:k?Date.now()-k:0});}}}function gt(i,t,u){return typeof i=="function"?{name:`${u}-guardrail-${t}`,fn:i,critical:true}:i}function Er(i,t){let{backoff:u="exponential",baseDelayMs:a=100,maxDelayMs:d=5e3}=t,f;switch(u){case "exponential":f=a*Math.pow(2,i-1);break;case "linear":f=a*i;break;default:f=a;}return Math.min(f,d)}function Jn(i,t){return t?.aborted?Promise.reject(t.reason??new Error("Aborted")):new Promise((u,a)=>{if(!t){setTimeout(u,i);return}let d=()=>{clearTimeout(f),a(t.reason??new Error("Aborted"));},f=setTimeout(()=>{t.removeEventListener("abort",d),u();},i);t.addEventListener("abort",d,{once:true});})}async function ft(i,t,u,a){let{retry:d}=i,f=Math.max(d?.attempts??1,1),k;for(let S=1;S<=f;S++)try{return await i.fn(t,u)}catch(w){if(k=w instanceof Error?w:new Error(String(w)),S<f){let v=Er(S,d??{});await Jn(v,a);}}return {passed:false,reason:`Guardrail "${i.name}" failed after ${f} attempt(s): ${k.message}`}}function Ar(i,t){let{backoff:u="exponential",baseDelayMs:a=1e3,maxDelayMs:d=3e4}=t,f;switch(u){case "exponential":f=a*Math.pow(2,i-1);break;case "linear":f=a*i;break;default:f=a;}return Math.min(f,d)}async function nn(i,t,u,a,d){let f=Math.max(d?.attempts??1,1),k=d?.isRetryable??(()=>true),S=d?.onRetry,w;for(let v=1;v<=f;v++)try{return await i(t,u,a)}catch(A){if(w=A instanceof Error?A:new Error(String(A)),v<f){let Q=true;try{Q=k(w);}catch{break}if(!Q)break;let F=Ar(v,d??{});try{S?.(v,w,F);}catch{}await Jn(F,a?.signal);}else break}throw w}function Je(i){return adapterUtils.getBridgeFact(i,hn)}function We(i,t){adapterUtils.setBridgeFact(i,hn,t);}function ut(i){return adapterUtils.getBridgeFact(i,Ot)}function rt(i,t){adapterUtils.setBridgeFact(i,Ot,t);}function Wt(i){return adapterUtils.getBridgeFact(i,bn)}function mt(i,t){adapterUtils.setBridgeFact(i,bn,t);}function Kt(i){return adapterUtils.getBridgeFact(i,kn)}function ht(i,t){adapterUtils.setBridgeFact(i,kn,t);}function st(i){return adapterUtils.getBridgeFact(i,Mt)}function at(i,t){adapterUtils.setBridgeFact(i,Mt,t);}function nt(i){return {agent:Je(i),approval:ut(i),conversation:Wt(i),toolCalls:Kt(i)}}function Vt(i){let t=Object.create(null);for(let[u,a]of Object.entries(i))t[u]={priority:a.priority??0,when:d=>{let f=nt(d),k={...d,...f};return a.when(k)},require:d=>{let f=nt(d),k={...d,...f};return typeof a.require=="function"?a.require(k):a.require}};return t}function Wn(i,t,u){let a=Object.create(null);for(let[d,f]of Object.entries(i))a[d]={requirement:f.requirement,key:f.key,resolve:async(k,S)=>{let w=nt(S.facts),A={facts:{...S.facts,...w},runAgent:async(Q,F,Ce)=>t(Q,F,nt(u()),Ce),signal:S.signal};await f.resolve(k,A);}};return a}var yt=200,xr=0;function vn(){return `bp_${Date.now().toString(36)}_${(++xr).toString(36)}`}function wn(i,t,u){for(let a of i)if(a.type===t){if(!a.when)return a;try{if(a.when(u))return a}catch{}}return null}function sn(){return {pending:[],resolved:[],cancelled:[]}}var Kn=new Set(["__proto__","constructor","prototype","toString","valueOf","hasOwnProperty"]);function vt(){let i=Date.now().toString(36),t=crypto.randomUUID().slice(0,8);return `ckpt_${i}_${t}`}function an(i){if(!i||typeof i!="object")return false;for(let a of Object.keys(i))if(Kn.has(a))return false;let t=i;if(t.version!==1||typeof t.id!="string"||t.id.length===0||typeof t.createdAt!="string"||typeof t.systemExport!="string"||t.timelineExport!==null&&typeof t.timelineExport!="string"||!t.localState||typeof t.localState!="object")return false;for(let a of Object.keys(t.localState))if(Kn.has(a))return false;let u=t.localState;return !(u.type!=="single"&&u.type!=="multi"||t.orchestratorType!=="single"&&t.orchestratorType!=="multi")}var Rn=class{store=new Map;order=[];maxCheckpoints;retentionMs;preserveLabeled;constructor(t){if(this.maxCheckpoints=t?.maxCheckpoints??100,this.retentionMs=t?.retentionMs??Number.POSITIVE_INFINITY,this.preserveLabeled=t?.preserveLabeled??false,!Number.isFinite(this.maxCheckpoints)||this.maxCheckpoints<1)throw new Error(`[Directive Checkpoint] maxCheckpoints must be >= 1, got ${this.maxCheckpoints}`)}async save(t){if(!an(t))throw new Error("[Directive Checkpoint] Invalid checkpoint data");for(;this.order.length>=this.maxCheckpoints&&this.evictOldest(););let u=this.order.indexOf(t.id);return u>=0&&this.order.splice(u,1),this.store.set(t.id,t),this.order.push(t.id),t.id}async load(t){return this.store.get(t)??null}async list(){return this.order.map(t=>{let u=this.store.get(t);return {id:u.id,label:u.label,createdAt:u.createdAt}})}async delete(t){if(!this.store.has(t))return false;this.store.delete(t);let u=this.order.indexOf(t);return u>=0&&this.order.splice(u,1),true}async clear(){this.store.clear(),this.order.length=0;}async prune(){if(!Number.isFinite(this.retentionMs))return 0;let t=Date.now()-this.retentionMs,u=0,a=[];for(let d of this.order){let f=this.store.get(d);if(!f)continue;if(new Date(f.createdAt).getTime()>=t)break;this.preserveLabeled&&f.label||a.push(d);}for(let d of a){this.store.delete(d);let f=this.order.indexOf(d);f>=0&&this.order.splice(f,1),u++;}return u}evictOldest(){if(this.preserveLabeled)for(let u=0;u<this.order.length;u++){let a=this.order[u],d=this.store.get(a);if(d&&!d.label)return this.order.splice(u,1),this.store.delete(a),true}let t=this.order.shift();return t?(this.store.delete(t),true):false}};function _n(i){if(i.length>1048576)throw new Error(`[Directive] Output too large for JSON extraction (${i.length} chars, max 1048576).`);let t=i.trim();try{return JSON.parse(t)}catch{}let u=t.indexOf("{"),a=t.indexOf("["),d,f,k;if(u===-1&&a===-1)throw new Error("[Directive] No JSON object or array found in output");u===-1?(d=a,f="[",k="]"):a===-1?(d=u,f="{",k="}"):(d=Math.min(u,a),f=d===u?"{":"[",k=d===u?"}":"]");let S=0,w=false,v=false;for(let A=d;A<t.length;A++){let Q=t[A];if(v){v=false;continue}if(Q==="\\"){v=true;continue}if(Q==='"'){w=!w;continue}if(!w){if(Q===f)S++;else if(Q===k&&(S--,S===0)){let F=t.slice(d,A+1);try{return JSON.parse(F)}catch{let Ce=F.replace(/"(?:[^"\\]|\\.)*"/g,Fe=>Fe.replace(/\n/g,"\\n").replace(/\r/g,"\\r").replace(/\t/g,"\\t"));return JSON.parse(Ce)}}}}throw new Error("[Directive] No valid JSON found in output")}function Cr(i){return i?i.issues&&i.issues.length>0?i.issues.map(t=>t.message).join("; "):i.message??"Validation failed":"Validation failed"}function Tn(i,t){let{schema:u,maxRetries:a=2,extractJson:d=_n,schemaDescription:f}=t;if(!Number.isFinite(a)||a<0)throw new Error("[Directive] withStructuredOutput: maxRetries must be a non-negative finite number.");let k=f??u.description??"the specified JSON schema";return async(S,w,v)=>{let A={...S,instructions:(S.instructions??"")+`
1
+ 'use strict';var core=require('@directive-run/core'),adapterUtils=require('@directive-run/core/adapter-utils');var et=class extends Error{code;guardrailName;guardrailType;userMessage;agentName;constructor(t){super(t.message,{cause:t.cause}),this.name="GuardrailError",this.code=t.code,this.guardrailName=t.guardrailName,this.guardrailType=t.guardrailType,this.userMessage=t.userMessage??t.message,this.agentName=t.agentName,Object.defineProperty(this,"input",{value:t.input,enumerable:false,writable:false,configurable:false}),Object.defineProperty(this,"data",{value:t.data,enumerable:false,writable:false,configurable:false});}toJSON(){return {name:this.name,code:this.code,message:this.message,guardrailName:this.guardrailName,guardrailType:this.guardrailType,userMessage:this.userMessage,agentName:this.agentName}}};function Hn(i){return i instanceof et}var hn="__agent",Ot="__approval",bn="__conversation",kn="__toolCalls",Mt="__breakpoints";var tt="__scratchpad",tn={facts:{[hn]:core.t.object(),[Ot]:core.t.object(),[bn]:core.t.array(),[kn]:core.t.array(),[Mt]:core.t.object()},derivations:{},events:{},requirements:{}};var Sr=new Set(["agent_start","agent_complete","agent_error","agent_retry","guardrail_check","constraint_evaluate","resolver_start","resolver_complete","resolver_error","approval_request","approval_response","handoff_start","handoff_complete","pattern_start","pattern_complete","dag_node_update","breakpoint_hit","breakpoint_resumed","derivation_update","scratchpad_update","reflection_iteration","race_start","race_winner","race_cancelled","debate_round","reroute","checkpoint_save","checkpoint_restore","task_start","task_complete","task_error","task_progress","goal_step"]),Un=new Set(["__proto__","constructor","prototype","toString","valueOf","hasOwnProperty"]);function Jt(i={}){let t=i.maxEvents??2e3,u=i.goToSnapshot;if(!Number.isFinite(t)||t<1)throw new Error("[Directive DebugTimeline] maxEvents must be >= 1");let a=[],d=0,f=new Set;return {record(S){let w={...S,id:d++};a.push(w);let v=a.length-t;v>0&&a.splice(0,v);for(let A of f)try{A(w);}catch(Q){typeof process<"u"&&process.env?.NODE_ENV!=="production"&&console.error("[Directive DebugTimeline] Listener threw:",Q instanceof Error?Q.message:Q);}return w},getEvents(){return [...a]},getEventsForAgent(S){return a.filter(w=>w.agentId===S)},getEventsByType(S){return a.filter(w=>w.type===S)},getEventsAtSnapshot(S){return a.filter(w=>w.snapshotId===S)},getEventsInRange(S,w){return a.filter(v=>v.timestamp>=S&&v.timestamp<=w)},forkFrom(S){let w=-1;for(let v=a.length-1;v>=0;v--)if(a[v].snapshotId!==null&&a[v].snapshotId<=S){w=a[v].id;break}if(w>=0){let v=a.length;for(let A=a.length-1;A>=0;A--)if(a[A].id<=w){v=A+1;break}a=a.slice(0,v);}else a=[];u&&u(S);},export(){return JSON.stringify({version:1,events:a,nextId:d})},import(S){let w;try{w=JSON.parse(S);}catch{throw new Error("[Directive DebugTimeline] Invalid JSON")}if(!w||typeof w!="object")throw new Error("[Directive DebugTimeline] Invalid timeline data");for(let Q of Object.keys(w))if(Un.has(Q))throw new Error(`[Directive DebugTimeline] Blocked key in import: ${Q}`);let v=w;if(!Array.isArray(v.events))throw new Error("[Directive DebugTimeline] Missing events array");let A=[];for(let Q of v.events){if(!Q||typeof Q!="object")continue;for(let Ce of Object.keys(Q))if(Un.has(Ce))throw new Error(`[Directive DebugTimeline] Blocked key in event: ${Ce}`);let F=Q;typeof F.id=="number"&&typeof F.type=="string"&&Sr.has(F.type)&&typeof F.timestamp=="number"&&A.push(Q);}a=A.length>t?A.slice(-t):A,d=typeof v.nextId=="number"?v.nextId:A.length;},clear(){a=[],d=0;},subscribe(S){return f.add(S),()=>{f.delete(S);}},get length(){return a.length}}}function yn(i,t){let u=new Map;return {name:"directive-ai-debug-timeline",onConstraintEvaluate(a,d){i.record({type:"constraint_evaluate",timestamp:Date.now(),snapshotId:t(),constraintId:a,fired:d});},onResolverStart(a,d){u.set(a,Date.now()),i.record({type:"resolver_start",timestamp:Date.now(),snapshotId:t(),resolverId:a,requirementType:d.requirement.type});},onResolverComplete(a){let d=u.get(a);u.delete(a),i.record({type:"resolver_complete",timestamp:Date.now(),snapshotId:t(),resolverId:a,durationMs:d?Date.now()-d:0});},onResolverError(a,d,f){let k=u.get(a);u.delete(a);let S=f instanceof Error?f.message:String(f);i.record({type:"resolver_error",timestamp:Date.now(),snapshotId:t(),resolverId:a,errorMessage:S,durationMs:k?Date.now()-k:0});}}}function gt(i,t,u){return typeof i=="function"?{name:`${u}-guardrail-${t}`,fn:i,critical:true}:i}function Er(i,t){let{backoff:u="exponential",baseDelayMs:a=100,maxDelayMs:d=5e3}=t,f;switch(u){case "exponential":f=a*2**(i-1);break;case "linear":f=a*i;break;default:f=a;}return Math.min(f,d)}function Jn(i,t){return t?.aborted?Promise.reject(t.reason??new Error("Aborted")):new Promise((u,a)=>{if(!t){setTimeout(u,i);return}let d=()=>{clearTimeout(f),a(t.reason??new Error("Aborted"));},f=setTimeout(()=>{t.removeEventListener("abort",d),u();},i);t.addEventListener("abort",d,{once:true});})}async function ft(i,t,u,a){let{retry:d}=i,f=Math.max(d?.attempts??1,1),k;for(let S=1;S<=f;S++)try{return await i.fn(t,u)}catch(w){if(k=w instanceof Error?w:new Error(String(w)),S<f){let v=Er(S,d??{});await Jn(v,a);}}return {passed:false,reason:`Guardrail "${i.name}" failed after ${f} attempt(s): ${k.message}`}}function Ar(i,t){let{backoff:u="exponential",baseDelayMs:a=1e3,maxDelayMs:d=3e4}=t,f;switch(u){case "exponential":f=a*2**(i-1);break;case "linear":f=a*i;break;default:f=a;}return Math.min(f,d)}async function nn(i,t,u,a,d){let f=Math.max(d?.attempts??1,1),k=d?.isRetryable??(()=>true),S=d?.onRetry,w;for(let v=1;v<=f;v++)try{return await i(t,u,a)}catch(A){if(w=A instanceof Error?A:new Error(String(A)),v<f){let Q=true;try{Q=k(w);}catch{break}if(!Q)break;let F=Ar(v,d??{});try{S?.(v,w,F);}catch{}await Jn(F,a?.signal);}else break}throw w}function Je(i){return adapterUtils.getBridgeFact(i,hn)}function We(i,t){adapterUtils.setBridgeFact(i,hn,t);}function ut(i){return adapterUtils.getBridgeFact(i,Ot)}function rt(i,t){adapterUtils.setBridgeFact(i,Ot,t);}function Wt(i){return adapterUtils.getBridgeFact(i,bn)}function mt(i,t){adapterUtils.setBridgeFact(i,bn,t);}function Kt(i){return adapterUtils.getBridgeFact(i,kn)}function ht(i,t){adapterUtils.setBridgeFact(i,kn,t);}function st(i){return adapterUtils.getBridgeFact(i,Mt)}function at(i,t){adapterUtils.setBridgeFact(i,Mt,t);}function nt(i){return {agent:Je(i),approval:ut(i),conversation:Wt(i),toolCalls:Kt(i)}}function Vt(i){let t=Object.create(null);for(let[u,a]of Object.entries(i))t[u]={priority:a.priority??0,when:d=>{let f=nt(d),k={...d,...f};return a.when(k)},require:d=>{let f=nt(d),k={...d,...f};return typeof a.require=="function"?a.require(k):a.require}};return t}function Wn(i,t,u){let a=Object.create(null);for(let[d,f]of Object.entries(i))a[d]={requirement:f.requirement,key:f.key,resolve:async(k,S)=>{let w=nt(S.facts),A={facts:{...S.facts,...w},runAgent:async(Q,F,Ce)=>t(Q,F,nt(u()),Ce),signal:S.signal};await f.resolve(k,A);}};return a}var yt=200,xr=0;function vn(){return `bp_${Date.now().toString(36)}_${(++xr).toString(36)}`}function wn(i,t,u){for(let a of i)if(a.type===t){if(!a.when)return a;try{if(a.when(u))return a}catch{}}return null}function sn(){return {pending:[],resolved:[],cancelled:[]}}var Kn=new Set(["__proto__","constructor","prototype","toString","valueOf","hasOwnProperty"]);function vt(){let i=Date.now().toString(36),t=crypto.randomUUID().slice(0,8);return `ckpt_${i}_${t}`}function an(i){if(!i||typeof i!="object")return false;for(let a of Object.keys(i))if(Kn.has(a))return false;let t=i;if(t.version!==1||typeof t.id!="string"||t.id.length===0||typeof t.createdAt!="string"||typeof t.systemExport!="string"||t.timelineExport!==null&&typeof t.timelineExport!="string"||!t.localState||typeof t.localState!="object")return false;for(let a of Object.keys(t.localState))if(Kn.has(a))return false;let u=t.localState;return !(u.type!=="single"&&u.type!=="multi"||t.orchestratorType!=="single"&&t.orchestratorType!=="multi")}var Rn=class{store=new Map;order=[];maxCheckpoints;retentionMs;preserveLabeled;constructor(t){if(this.maxCheckpoints=t?.maxCheckpoints??100,this.retentionMs=t?.retentionMs??Number.POSITIVE_INFINITY,this.preserveLabeled=t?.preserveLabeled??false,!Number.isFinite(this.maxCheckpoints)||this.maxCheckpoints<1)throw new Error(`[Directive Checkpoint] maxCheckpoints must be >= 1, got ${this.maxCheckpoints}`)}async save(t){if(!an(t))throw new Error("[Directive Checkpoint] Invalid checkpoint data");for(;this.order.length>=this.maxCheckpoints&&this.evictOldest(););let u=this.order.indexOf(t.id);return u>=0&&this.order.splice(u,1),this.store.set(t.id,t),this.order.push(t.id),t.id}async load(t){return this.store.get(t)??null}async list(){return this.order.map(t=>{let u=this.store.get(t);return {id:u.id,label:u.label,createdAt:u.createdAt}})}async delete(t){if(!this.store.has(t))return false;this.store.delete(t);let u=this.order.indexOf(t);return u>=0&&this.order.splice(u,1),true}async clear(){this.store.clear(),this.order.length=0;}async prune(){if(!Number.isFinite(this.retentionMs))return 0;let t=Date.now()-this.retentionMs,u=0,a=[];for(let d of this.order){let f=this.store.get(d);if(!f)continue;if(new Date(f.createdAt).getTime()>=t)break;this.preserveLabeled&&f.label||a.push(d);}for(let d of a){this.store.delete(d);let f=this.order.indexOf(d);f>=0&&this.order.splice(f,1),u++;}return u}evictOldest(){if(this.preserveLabeled)for(let u=0;u<this.order.length;u++){let a=this.order[u],d=this.store.get(a);if(d&&!d.label)return this.order.splice(u,1),this.store.delete(a),true}let t=this.order.shift();return t?(this.store.delete(t),true):false}};function _n(i){if(i.length>1048576)throw new Error(`[Directive] Output too large for JSON extraction (${i.length} chars, max 1048576).`);let t=i.trim();try{return JSON.parse(t)}catch{}let u=t.indexOf("{"),a=t.indexOf("["),d,f,k;if(u===-1&&a===-1)throw new Error("[Directive] No JSON object or array found in output");u===-1?(d=a,f="[",k="]"):a===-1?(d=u,f="{",k="}"):(d=Math.min(u,a),f=d===u?"{":"[",k=d===u?"}":"]");let S=0,w=false,v=false;for(let A=d;A<t.length;A++){let Q=t[A];if(v){v=false;continue}if(Q==="\\"){v=true;continue}if(Q==='"'){w=!w;continue}if(!w){if(Q===f)S++;else if(Q===k&&(S--,S===0)){let F=t.slice(d,A+1);try{return JSON.parse(F)}catch{let Ce=F.replace(/"(?:[^"\\]|\\.)*"/g,Fe=>Fe.replace(/\n/g,"\\n").replace(/\r/g,"\\r").replace(/\t/g,"\\t"));return JSON.parse(Ce)}}}}throw new Error("[Directive] No valid JSON found in output")}function Cr(i){return i?i.issues&&i.issues.length>0?i.issues.map(t=>t.message).join("; "):i.message??"Validation failed":"Validation failed"}function Tn(i,t){let{schema:u,maxRetries:a=2,extractJson:d=_n,schemaDescription:f}=t;if(!Number.isFinite(a)||a<0)throw new Error("[Directive] withStructuredOutput: maxRetries must be a non-negative finite number.");let k=f??u.description??"the specified JSON schema";return async(S,w,v)=>{let A={...S,instructions:(S.instructions??"")+`
2
2
 
3
3
  IMPORTANT: Respond with valid JSON matching `+k+". Output ONLY the JSON object, no additional text or markdown formatting."},Q,F;for(let Ce=0;Ce<=a;Ce++){let Fe=Ce===0?w:`${w}
4
4