@esankhan3/anvil-agent-core 0.1.0
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.
- package/README.md +142 -0
- package/dist/__tests__/adapter-enrichment.test.d.ts +12 -0
- package/dist/__tests__/adapter-enrichment.test.d.ts.map +1 -0
- package/dist/__tests__/adapter-enrichment.test.js +199 -0
- package/dist/__tests__/adapter-enrichment.test.js.map +1 -0
- package/dist/__tests__/adk-anthropic-llm.test.d.ts +18 -0
- package/dist/__tests__/adk-anthropic-llm.test.d.ts.map +1 -0
- package/dist/__tests__/adk-anthropic-llm.test.js +124 -0
- package/dist/__tests__/adk-anthropic-llm.test.js.map +1 -0
- package/dist/__tests__/cost-breakdown.test.d.ts +11 -0
- package/dist/__tests__/cost-breakdown.test.d.ts.map +1 -0
- package/dist/__tests__/cost-breakdown.test.js +163 -0
- package/dist/__tests__/cost-breakdown.test.js.map +1 -0
- package/dist/__tests__/mcp.test.d.ts +13 -0
- package/dist/__tests__/mcp.test.d.ts.map +1 -0
- package/dist/__tests__/mcp.test.js +413 -0
- package/dist/__tests__/mcp.test.js.map +1 -0
- package/dist/__tests__/ollama-adapter-agentic.test.d.ts +15 -0
- package/dist/__tests__/ollama-adapter-agentic.test.d.ts.map +1 -0
- package/dist/__tests__/ollama-adapter-agentic.test.js +268 -0
- package/dist/__tests__/ollama-adapter-agentic.test.js.map +1 -0
- package/dist/__tests__/ollama-adapter-executor.test.d.ts +10 -0
- package/dist/__tests__/ollama-adapter-executor.test.d.ts.map +1 -0
- package/dist/__tests__/ollama-adapter-executor.test.js +95 -0
- package/dist/__tests__/ollama-adapter-executor.test.js.map +1 -0
- package/dist/__tests__/openai-adapter-output.test.d.ts +17 -0
- package/dist/__tests__/openai-adapter-output.test.d.ts.map +1 -0
- package/dist/__tests__/openai-adapter-output.test.js +127 -0
- package/dist/__tests__/openai-adapter-output.test.js.map +1 -0
- package/dist/__tests__/opencode-adapter.test.d.ts +7 -0
- package/dist/__tests__/opencode-adapter.test.d.ts.map +1 -0
- package/dist/__tests__/opencode-adapter.test.js +141 -0
- package/dist/__tests__/opencode-adapter.test.js.map +1 -0
- package/dist/__tests__/openrouter-adapter-agentic.test.d.ts +11 -0
- package/dist/__tests__/openrouter-adapter-agentic.test.d.ts.map +1 -0
- package/dist/__tests__/openrouter-adapter-agentic.test.js +297 -0
- package/dist/__tests__/openrouter-adapter-agentic.test.js.map +1 -0
- package/dist/__tests__/router-circuit-breaker.test.d.ts +6 -0
- package/dist/__tests__/router-circuit-breaker.test.d.ts.map +1 -0
- package/dist/__tests__/router-circuit-breaker.test.js +253 -0
- package/dist/__tests__/router-circuit-breaker.test.js.map +1 -0
- package/dist/__tests__/router-config-loader.test.d.ts +8 -0
- package/dist/__tests__/router-config-loader.test.d.ts.map +1 -0
- package/dist/__tests__/router-config-loader.test.js +172 -0
- package/dist/__tests__/router-config-loader.test.js.map +1 -0
- package/dist/__tests__/router-discovery.test.d.ts +5 -0
- package/dist/__tests__/router-discovery.test.d.ts.map +1 -0
- package/dist/__tests__/router-discovery.test.js +129 -0
- package/dist/__tests__/router-discovery.test.js.map +1 -0
- package/dist/__tests__/router-fallback.test.d.ts +9 -0
- package/dist/__tests__/router-fallback.test.d.ts.map +1 -0
- package/dist/__tests__/router-fallback.test.js +283 -0
- package/dist/__tests__/router-fallback.test.js.map +1 -0
- package/dist/__tests__/router-local-executor.test.d.ts +6 -0
- package/dist/__tests__/router-local-executor.test.d.ts.map +1 -0
- package/dist/__tests__/router-local-executor.test.js +193 -0
- package/dist/__tests__/router-local-executor.test.js.map +1 -0
- package/dist/__tests__/router-model-registry.test.d.ts +8 -0
- package/dist/__tests__/router-model-registry.test.d.ts.map +1 -0
- package/dist/__tests__/router-model-registry.test.js +389 -0
- package/dist/__tests__/router-model-registry.test.js.map +1 -0
- package/dist/__tests__/router-rate-limiter.test.d.ts +8 -0
- package/dist/__tests__/router-rate-limiter.test.d.ts.map +1 -0
- package/dist/__tests__/router-rate-limiter.test.js +128 -0
- package/dist/__tests__/router-rate-limiter.test.js.map +1 -0
- package/dist/__tests__/router-resolver.test.d.ts +7 -0
- package/dist/__tests__/router-resolver.test.d.ts.map +1 -0
- package/dist/__tests__/router-resolver.test.js +110 -0
- package/dist/__tests__/router-resolver.test.js.map +1 -0
- package/dist/__tests__/router-retry.test.d.ts +7 -0
- package/dist/__tests__/router-retry.test.d.ts.map +1 -0
- package/dist/__tests__/router-retry.test.js +302 -0
- package/dist/__tests__/router-retry.test.js.map +1 -0
- package/dist/__tests__/router-scaffold.test.d.ts +11 -0
- package/dist/__tests__/router-scaffold.test.d.ts.map +1 -0
- package/dist/__tests__/router-scaffold.test.js +103 -0
- package/dist/__tests__/router-scaffold.test.js.map +1 -0
- package/dist/__tests__/router-spend-ledger.test.d.ts +8 -0
- package/dist/__tests__/router-spend-ledger.test.d.ts.map +1 -0
- package/dist/__tests__/router-spend-ledger.test.js +227 -0
- package/dist/__tests__/router-spend-ledger.test.js.map +1 -0
- package/dist/__tests__/router-telemetry.test.d.ts +11 -0
- package/dist/__tests__/router-telemetry.test.d.ts.map +1 -0
- package/dist/__tests__/router-telemetry.test.js +173 -0
- package/dist/__tests__/router-telemetry.test.js.map +1 -0
- package/dist/__tests__/single-shot.test.d.ts +12 -0
- package/dist/__tests__/single-shot.test.d.ts.map +1 -0
- package/dist/__tests__/single-shot.test.js +204 -0
- package/dist/__tests__/single-shot.test.js.map +1 -0
- package/dist/__tests__/skills-prompt.test.d.ts +11 -0
- package/dist/__tests__/skills-prompt.test.d.ts.map +1 -0
- package/dist/__tests__/skills-prompt.test.js +208 -0
- package/dist/__tests__/skills-prompt.test.js.map +1 -0
- package/dist/__tests__/skills.test.d.ts +11 -0
- package/dist/__tests__/skills.test.d.ts.map +1 -0
- package/dist/__tests__/skills.test.js +161 -0
- package/dist/__tests__/skills.test.js.map +1 -0
- package/dist/__tests__/telemetry-integration.test.d.ts +12 -0
- package/dist/__tests__/telemetry-integration.test.d.ts.map +1 -0
- package/dist/__tests__/telemetry-integration.test.js +153 -0
- package/dist/__tests__/telemetry-integration.test.js.map +1 -0
- package/dist/__tests__/telemetry.test.d.ts +13 -0
- package/dist/__tests__/telemetry.test.d.ts.map +1 -0
- package/dist/__tests__/telemetry.test.js +161 -0
- package/dist/__tests__/telemetry.test.js.map +1 -0
- package/dist/__tests__/upstream-error.test.d.ts +8 -0
- package/dist/__tests__/upstream-error.test.d.ts.map +1 -0
- package/dist/__tests__/upstream-error.test.js +186 -0
- package/dist/__tests__/upstream-error.test.js.map +1 -0
- package/dist/adk-adapter.d.ts +45 -0
- package/dist/adk-adapter.d.ts.map +1 -0
- package/dist/adk-adapter.js +403 -0
- package/dist/adk-adapter.js.map +1 -0
- package/dist/adk-anthropic-llm.d.ts +59 -0
- package/dist/adk-anthropic-llm.d.ts.map +1 -0
- package/dist/adk-anthropic-llm.js +397 -0
- package/dist/adk-anthropic-llm.js.map +1 -0
- package/dist/agent/index.d.ts +14 -0
- package/dist/agent/index.d.ts.map +1 -0
- package/dist/agent/index.js +14 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/agent/session/__tests__/bridge-tool-executor.test.d.ts +10 -0
- package/dist/agent/session/__tests__/bridge-tool-executor.test.d.ts.map +1 -0
- package/dist/agent/session/__tests__/bridge-tool-executor.test.js +152 -0
- package/dist/agent/session/__tests__/bridge-tool-executor.test.js.map +1 -0
- package/dist/agent/session/__tests__/collect-trajectory.test.d.ts +18 -0
- package/dist/agent/session/__tests__/collect-trajectory.test.d.ts.map +1 -0
- package/dist/agent/session/__tests__/collect-trajectory.test.js +208 -0
- package/dist/agent/session/__tests__/collect-trajectory.test.js.map +1 -0
- package/dist/agent/session/__tests__/run-with-agent.test.d.ts +8 -0
- package/dist/agent/session/__tests__/run-with-agent.test.d.ts.map +1 -0
- package/dist/agent/session/__tests__/run-with-agent.test.js +98 -0
- package/dist/agent/session/__tests__/run-with-agent.test.js.map +1 -0
- package/dist/agent/session/__tests__/runtime.test.d.ts +7 -0
- package/dist/agent/session/__tests__/runtime.test.d.ts.map +1 -0
- package/dist/agent/session/__tests__/runtime.test.js +361 -0
- package/dist/agent/session/__tests__/runtime.test.js.map +1 -0
- package/dist/agent/session/__tests__/skills-mcp-spawn.test.d.ts +15 -0
- package/dist/agent/session/__tests__/skills-mcp-spawn.test.d.ts.map +1 -0
- package/dist/agent/session/__tests__/skills-mcp-spawn.test.js +160 -0
- package/dist/agent/session/__tests__/skills-mcp-spawn.test.js.map +1 -0
- package/dist/agent/session/__tests__/spawn-telemetry.test.d.ts +13 -0
- package/dist/agent/session/__tests__/spawn-telemetry.test.d.ts.map +1 -0
- package/dist/agent/session/__tests__/spawn-telemetry.test.js +135 -0
- package/dist/agent/session/__tests__/spawn-telemetry.test.js.map +1 -0
- package/dist/agent/session/__tests__/types.test.d.ts +9 -0
- package/dist/agent/session/__tests__/types.test.d.ts.map +1 -0
- package/dist/agent/session/__tests__/types.test.js +220 -0
- package/dist/agent/session/__tests__/types.test.js.map +1 -0
- package/dist/agent/session/adapter.d.ts +108 -0
- package/dist/agent/session/adapter.d.ts.map +1 -0
- package/dist/agent/session/adapter.js +39 -0
- package/dist/agent/session/adapter.js.map +1 -0
- package/dist/agent/session/collect-trajectory.d.ts +36 -0
- package/dist/agent/session/collect-trajectory.d.ts.map +1 -0
- package/dist/agent/session/collect-trajectory.js +193 -0
- package/dist/agent/session/collect-trajectory.js.map +1 -0
- package/dist/agent/session/default-adapter-factory.d.ts +48 -0
- package/dist/agent/session/default-adapter-factory.d.ts.map +1 -0
- package/dist/agent/session/default-adapter-factory.js +227 -0
- package/dist/agent/session/default-adapter-factory.js.map +1 -0
- package/dist/agent/session/headless-types.d.ts +85 -0
- package/dist/agent/session/headless-types.d.ts.map +1 -0
- package/dist/agent/session/headless-types.js +16 -0
- package/dist/agent/session/headless-types.js.map +1 -0
- package/dist/agent/session/index.d.ts +18 -0
- package/dist/agent/session/index.d.ts.map +1 -0
- package/dist/agent/session/index.js +20 -0
- package/dist/agent/session/index.js.map +1 -0
- package/dist/agent/session/language-model-bridge.d.ts +84 -0
- package/dist/agent/session/language-model-bridge.d.ts.map +1 -0
- package/dist/agent/session/language-model-bridge.js +454 -0
- package/dist/agent/session/language-model-bridge.js.map +1 -0
- package/dist/agent/session/legacy-adapter-types.d.ts +63 -0
- package/dist/agent/session/legacy-adapter-types.d.ts.map +1 -0
- package/dist/agent/session/legacy-adapter-types.js +11 -0
- package/dist/agent/session/legacy-adapter-types.js.map +1 -0
- package/dist/agent/session/run-with-agent.d.ts +36 -0
- package/dist/agent/session/run-with-agent.d.ts.map +1 -0
- package/dist/agent/session/run-with-agent.js +81 -0
- package/dist/agent/session/run-with-agent.js.map +1 -0
- package/dist/agent/session/session-registry.d.ts +71 -0
- package/dist/agent/session/session-registry.d.ts.map +1 -0
- package/dist/agent/session/session-registry.js +209 -0
- package/dist/agent/session/session-registry.js.map +1 -0
- package/dist/agent/session/session.d.ts +76 -0
- package/dist/agent/session/session.d.ts.map +1 -0
- package/dist/agent/session/session.js +327 -0
- package/dist/agent/session/session.js.map +1 -0
- package/dist/agent/session/types.d.ts +230 -0
- package/dist/agent/session/types.d.ts.map +1 -0
- package/dist/agent/session/types.js +44 -0
- package/dist/agent/session/types.js.map +1 -0
- package/dist/checkpoint/__tests__/blob-store.test.d.ts +5 -0
- package/dist/checkpoint/__tests__/blob-store.test.d.ts.map +1 -0
- package/dist/checkpoint/__tests__/blob-store.test.js +82 -0
- package/dist/checkpoint/__tests__/blob-store.test.js.map +1 -0
- package/dist/checkpoint/__tests__/key.test.d.ts +7 -0
- package/dist/checkpoint/__tests__/key.test.d.ts.map +1 -0
- package/dist/checkpoint/__tests__/key.test.js +130 -0
- package/dist/checkpoint/__tests__/key.test.js.map +1 -0
- package/dist/checkpoint/__tests__/runner.test.d.ts +12 -0
- package/dist/checkpoint/__tests__/runner.test.d.ts.map +1 -0
- package/dist/checkpoint/__tests__/runner.test.js +201 -0
- package/dist/checkpoint/__tests__/runner.test.js.map +1 -0
- package/dist/checkpoint/__tests__/store.test.d.ts +6 -0
- package/dist/checkpoint/__tests__/store.test.d.ts.map +1 -0
- package/dist/checkpoint/__tests__/store.test.js +150 -0
- package/dist/checkpoint/__tests__/store.test.js.map +1 -0
- package/dist/checkpoint/blob-store.d.ts +41 -0
- package/dist/checkpoint/blob-store.d.ts.map +1 -0
- package/dist/checkpoint/blob-store.js +119 -0
- package/dist/checkpoint/blob-store.js.map +1 -0
- package/dist/checkpoint/index.d.ts +15 -0
- package/dist/checkpoint/index.d.ts.map +1 -0
- package/dist/checkpoint/index.js +14 -0
- package/dist/checkpoint/index.js.map +1 -0
- package/dist/checkpoint/key.d.ts +39 -0
- package/dist/checkpoint/key.d.ts.map +1 -0
- package/dist/checkpoint/key.js +112 -0
- package/dist/checkpoint/key.js.map +1 -0
- package/dist/checkpoint/runner.d.ts +83 -0
- package/dist/checkpoint/runner.d.ts.map +1 -0
- package/dist/checkpoint/runner.js +146 -0
- package/dist/checkpoint/runner.js.map +1 -0
- package/dist/checkpoint/store.d.ts +91 -0
- package/dist/checkpoint/store.d.ts.map +1 -0
- package/dist/checkpoint/store.js +292 -0
- package/dist/checkpoint/store.js.map +1 -0
- package/dist/checkpoint/types.d.ts +74 -0
- package/dist/checkpoint/types.d.ts.map +1 -0
- package/dist/checkpoint/types.js +16 -0
- package/dist/checkpoint/types.js.map +1 -0
- package/dist/claude-adapter.d.ts +23 -0
- package/dist/claude-adapter.d.ts.map +1 -0
- package/dist/claude-adapter.js +233 -0
- package/dist/claude-adapter.js.map +1 -0
- package/dist/cost.d.ts +67 -0
- package/dist/cost.d.ts.map +1 -0
- package/dist/cost.js +148 -0
- package/dist/cost.js.map +1 -0
- package/dist/data/model-prices.json +39681 -0
- package/dist/fallback-adapter.d.ts +51 -0
- package/dist/fallback-adapter.d.ts.map +1 -0
- package/dist/fallback-adapter.js +116 -0
- package/dist/fallback-adapter.js.map +1 -0
- package/dist/gemini-adapter.d.ts +22 -0
- package/dist/gemini-adapter.d.ts.map +1 -0
- package/dist/gemini-adapter.js +185 -0
- package/dist/gemini-adapter.js.map +1 -0
- package/dist/gemini-cli-adapter.d.ts +24 -0
- package/dist/gemini-cli-adapter.d.ts.map +1 -0
- package/dist/gemini-cli-adapter.js +169 -0
- package/dist/gemini-cli-adapter.js.map +1 -0
- package/dist/index.d.ts +39 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +39 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/client.d.ts +32 -0
- package/dist/mcp/client.d.ts.map +1 -0
- package/dist/mcp/client.js +92 -0
- package/dist/mcp/client.js.map +1 -0
- package/dist/mcp/config-loader.d.ts +36 -0
- package/dist/mcp/config-loader.d.ts.map +1 -0
- package/dist/mcp/config-loader.js +129 -0
- package/dist/mcp/config-loader.js.map +1 -0
- package/dist/mcp/index.d.ts +12 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +11 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/tool-merger.d.ts +18 -0
- package/dist/mcp/tool-merger.d.ts.map +1 -0
- package/dist/mcp/tool-merger.js +34 -0
- package/dist/mcp/tool-merger.js.map +1 -0
- package/dist/mcp/types.d.ts +35 -0
- package/dist/mcp/types.d.ts.map +1 -0
- package/dist/mcp/types.js +10 -0
- package/dist/mcp/types.js.map +1 -0
- package/dist/ollama-adapter.d.ts +95 -0
- package/dist/ollama-adapter.d.ts.map +1 -0
- package/dist/ollama-adapter.js +404 -0
- package/dist/ollama-adapter.js.map +1 -0
- package/dist/openai-adapter.d.ts +40 -0
- package/dist/openai-adapter.d.ts.map +1 -0
- package/dist/openai-adapter.js +81 -0
- package/dist/openai-adapter.js.map +1 -0
- package/dist/opencode-adapter.d.ts +39 -0
- package/dist/opencode-adapter.d.ts.map +1 -0
- package/dist/opencode-adapter.js +105 -0
- package/dist/opencode-adapter.js.map +1 -0
- package/dist/openrouter-adapter.d.ts +69 -0
- package/dist/openrouter-adapter.d.ts.map +1 -0
- package/dist/openrouter-adapter.js +481 -0
- package/dist/openrouter-adapter.js.map +1 -0
- package/dist/registry.d.ts +55 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +191 -0
- package/dist/registry.js.map +1 -0
- package/dist/router/circuit-breaker.d.ts +57 -0
- package/dist/router/circuit-breaker.d.ts.map +1 -0
- package/dist/router/circuit-breaker.js +110 -0
- package/dist/router/circuit-breaker.js.map +1 -0
- package/dist/router/config-loader.d.ts +43 -0
- package/dist/router/config-loader.d.ts.map +1 -0
- package/dist/router/config-loader.js +155 -0
- package/dist/router/config-loader.js.map +1 -0
- package/dist/router/discovery.d.ts +37 -0
- package/dist/router/discovery.d.ts.map +1 -0
- package/dist/router/discovery.js +54 -0
- package/dist/router/discovery.js.map +1 -0
- package/dist/router/errors.d.ts +39 -0
- package/dist/router/errors.d.ts.map +1 -0
- package/dist/router/errors.js +103 -0
- package/dist/router/errors.js.map +1 -0
- package/dist/router/index.d.ts +31 -0
- package/dist/router/index.d.ts.map +1 -0
- package/dist/router/index.js +20 -0
- package/dist/router/index.js.map +1 -0
- package/dist/router/local-executor.d.ts +62 -0
- package/dist/router/local-executor.d.ts.map +1 -0
- package/dist/router/local-executor.js +161 -0
- package/dist/router/local-executor.js.map +1 -0
- package/dist/router/model-registry.d.ts +90 -0
- package/dist/router/model-registry.d.ts.map +1 -0
- package/dist/router/model-registry.js +242 -0
- package/dist/router/model-registry.js.map +1 -0
- package/dist/router/rate-limiter.d.ts +63 -0
- package/dist/router/rate-limiter.d.ts.map +1 -0
- package/dist/router/rate-limiter.js +136 -0
- package/dist/router/rate-limiter.js.map +1 -0
- package/dist/router/resolver.d.ts +45 -0
- package/dist/router/resolver.d.ts.map +1 -0
- package/dist/router/resolver.js +71 -0
- package/dist/router/resolver.js.map +1 -0
- package/dist/router/retry.d.ts +58 -0
- package/dist/router/retry.d.ts.map +1 -0
- package/dist/router/retry.js +119 -0
- package/dist/router/retry.js.map +1 -0
- package/dist/router/router.d.ts +100 -0
- package/dist/router/router.d.ts.map +1 -0
- package/dist/router/router.js +306 -0
- package/dist/router/router.js.map +1 -0
- package/dist/router/spend-ledger.d.ts +69 -0
- package/dist/router/spend-ledger.d.ts.map +1 -0
- package/dist/router/spend-ledger.js +191 -0
- package/dist/router/spend-ledger.js.map +1 -0
- package/dist/router/telemetry.d.ts +48 -0
- package/dist/router/telemetry.d.ts.map +1 -0
- package/dist/router/telemetry.js +112 -0
- package/dist/router/telemetry.js.map +1 -0
- package/dist/router/types.d.ts +95 -0
- package/dist/router/types.d.ts.map +1 -0
- package/dist/router/types.js +16 -0
- package/dist/router/types.js.map +1 -0
- package/dist/single-shot.d.ts +60 -0
- package/dist/single-shot.d.ts.map +1 -0
- package/dist/single-shot.js +385 -0
- package/dist/single-shot.js.map +1 -0
- package/dist/skills/activator.d.ts +17 -0
- package/dist/skills/activator.d.ts.map +1 -0
- package/dist/skills/activator.js +24 -0
- package/dist/skills/activator.js.map +1 -0
- package/dist/skills/compose.d.ts +43 -0
- package/dist/skills/compose.d.ts.map +1 -0
- package/dist/skills/compose.js +53 -0
- package/dist/skills/compose.js.map +1 -0
- package/dist/skills/index.d.ts +17 -0
- package/dist/skills/index.d.ts.map +1 -0
- package/dist/skills/index.js +16 -0
- package/dist/skills/index.js.map +1 -0
- package/dist/skills/loader.d.ts +9 -0
- package/dist/skills/loader.d.ts.map +1 -0
- package/dist/skills/loader.js +49 -0
- package/dist/skills/loader.js.map +1 -0
- package/dist/skills/parser.d.ts +13 -0
- package/dist/skills/parser.d.ts.map +1 -0
- package/dist/skills/parser.js +60 -0
- package/dist/skills/parser.js.map +1 -0
- package/dist/skills/render.d.ts +11 -0
- package/dist/skills/render.d.ts.map +1 -0
- package/dist/skills/render.js +21 -0
- package/dist/skills/render.js.map +1 -0
- package/dist/skills/resolve-dir.d.ts +20 -0
- package/dist/skills/resolve-dir.d.ts.map +1 -0
- package/dist/skills/resolve-dir.js +30 -0
- package/dist/skills/resolve-dir.js.map +1 -0
- package/dist/skills/tool-policy.d.ts +26 -0
- package/dist/skills/tool-policy.d.ts.map +1 -0
- package/dist/skills/tool-policy.js +33 -0
- package/dist/skills/tool-policy.js.map +1 -0
- package/dist/skills/types.d.ts +42 -0
- package/dist/skills/types.d.ts.map +1 -0
- package/dist/skills/types.js +8 -0
- package/dist/skills/types.js.map +1 -0
- package/dist/stream-format.d.ts +100 -0
- package/dist/stream-format.d.ts.map +1 -0
- package/dist/stream-format.js +90 -0
- package/dist/stream-format.js.map +1 -0
- package/dist/telemetry/attributes.d.ts +38 -0
- package/dist/telemetry/attributes.d.ts.map +1 -0
- package/dist/telemetry/attributes.js +39 -0
- package/dist/telemetry/attributes.js.map +1 -0
- package/dist/telemetry/config.d.ts +30 -0
- package/dist/telemetry/config.d.ts.map +1 -0
- package/dist/telemetry/config.js +54 -0
- package/dist/telemetry/config.js.map +1 -0
- package/dist/telemetry/exporters.d.ts +10 -0
- package/dist/telemetry/exporters.d.ts.map +1 -0
- package/dist/telemetry/exporters.js +46 -0
- package/dist/telemetry/exporters.js.map +1 -0
- package/dist/telemetry/index.d.ts +15 -0
- package/dist/telemetry/index.d.ts.map +1 -0
- package/dist/telemetry/index.js +12 -0
- package/dist/telemetry/index.js.map +1 -0
- package/dist/telemetry/instrument.d.ts +36 -0
- package/dist/telemetry/instrument.d.ts.map +1 -0
- package/dist/telemetry/instrument.js +215 -0
- package/dist/telemetry/instrument.js.map +1 -0
- package/dist/telemetry/metrics.d.ts +45 -0
- package/dist/telemetry/metrics.d.ts.map +1 -0
- package/dist/telemetry/metrics.js +186 -0
- package/dist/telemetry/metrics.js.map +1 -0
- package/dist/telemetry/tracer.d.ts +33 -0
- package/dist/telemetry/tracer.d.ts.map +1 -0
- package/dist/telemetry/tracer.js +162 -0
- package/dist/telemetry/tracer.js.map +1 -0
- package/dist/tools/__tests__/builtin.test.d.ts +7 -0
- package/dist/tools/__tests__/builtin.test.d.ts.map +1 -0
- package/dist/tools/__tests__/builtin.test.js +242 -0
- package/dist/tools/__tests__/builtin.test.js.map +1 -0
- package/dist/tools/builtin.d.ts +29 -0
- package/dist/tools/builtin.d.ts.map +1 -0
- package/dist/tools/builtin.js +367 -0
- package/dist/tools/builtin.js.map +1 -0
- package/dist/tools/index.d.ts +4 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +3 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/path-guard.d.ts +23 -0
- package/dist/tools/path-guard.d.ts.map +1 -0
- package/dist/tools/path-guard.js +66 -0
- package/dist/tools/path-guard.js.map +1 -0
- package/dist/tools/types.d.ts +45 -0
- package/dist/tools/types.d.ts.map +1 -0
- package/dist/tools/types.js +11 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/types.d.ts +249 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +22 -0
- package/dist/types.js.map +1 -0
- package/dist/upstream-error.d.ts +64 -0
- package/dist/upstream-error.d.ts.map +1 -0
- package/dist/upstream-error.js +105 -0
- package/dist/upstream-error.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +2 -0
- package/dist/version.js.map +1 -0
- package/package.json +49 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenCode Go adapter — agentic HTTPS proxy for OpenCode's subscription
|
|
3
|
+
* service (https://opencode.ai/docs/go).
|
|
4
|
+
*
|
|
5
|
+
* OpenCode Go is a $10/month subscription that exposes a curated set of
|
|
6
|
+
* open coding models (Qwen3.5/3.6 Plus, Kimi K2.5/K2.6, GLM-5/5.1,
|
|
7
|
+
* DeepSeek V4, MiniMax M2.5/M2.7, MiMo …) via an OpenAI-compatible API
|
|
8
|
+
* at `https://opencode.ai/zen/go/v1/`. From our perspective it's just
|
|
9
|
+
* another OpenAI-compatible endpoint — same SSE format, same tool-call
|
|
10
|
+
* delta protocol — so we inherit `OpenRouterAdapter`'s agentic loop
|
|
11
|
+
* and override the config.
|
|
12
|
+
*
|
|
13
|
+
* Models in the registry are addressed as `opencode/<model>` (e.g.
|
|
14
|
+
* `opencode/qwen3.5-plus`). The adapter strips the `opencode/` prefix
|
|
15
|
+
* before sending to the upstream API. No binary, no daemon, no local
|
|
16
|
+
* SDK — just HTTP + an API key from the user's Go subscription.
|
|
17
|
+
*
|
|
18
|
+
* Replaces Ollama as the cheap local-tier provider when the user has
|
|
19
|
+
* a Go subscription: VRAM cost drops from ~12 GB (Ollama hosting
|
|
20
|
+
* qwen3:14b) to 0 GB, RAM cost stays nominal. Inference happens on
|
|
21
|
+
* OpenCode's hosted infrastructure.
|
|
22
|
+
*/
|
|
23
|
+
import { OpenRouterAdapter } from './openrouter-adapter.js';
|
|
24
|
+
const PREFIX = 'opencode/';
|
|
25
|
+
/**
|
|
26
|
+
* Pricing fallbacks for known OpenCode Go models. The proxy also
|
|
27
|
+
* returns `usage.cost` directly (mirrors OpenRouter's behavior); when
|
|
28
|
+
* present the inherited base class prefers that over this table. These
|
|
29
|
+
* entries are for the offline path / cost estimates before a call
|
|
30
|
+
* runs. Pricing reflects model-card list rates as of 2026-05.
|
|
31
|
+
*/
|
|
32
|
+
const OPENCODE_GO_PRICING = {
|
|
33
|
+
// GLM family
|
|
34
|
+
'glm-5': [0.50, 2.00],
|
|
35
|
+
'glm-5.1': [0.60, 2.40],
|
|
36
|
+
// Kimi family
|
|
37
|
+
'kimi-k2.5': [0.30, 1.20],
|
|
38
|
+
'kimi-k2.6': [0.50, 2.00],
|
|
39
|
+
// MiMo
|
|
40
|
+
'mimo-v2-pro': [0.40, 1.60],
|
|
41
|
+
'mimo-v2-omni': [0.20, 0.80],
|
|
42
|
+
'mimo-v2.5-pro': [0.40, 1.60],
|
|
43
|
+
'mimo-v2.5': [0.20, 0.80],
|
|
44
|
+
// MiniMax
|
|
45
|
+
'minimax-m2.5': [0.10, 0.40],
|
|
46
|
+
'minimax-m2.7': [0.20, 0.80],
|
|
47
|
+
// Qwen
|
|
48
|
+
'qwen3.5-plus': [0.05, 0.20],
|
|
49
|
+
'qwen3.6-plus': [0.20, 0.80],
|
|
50
|
+
// DeepSeek
|
|
51
|
+
'deepseek-v4-pro': [0.20, 0.80],
|
|
52
|
+
'deepseek-v4-flash': [0.02, 0.08],
|
|
53
|
+
};
|
|
54
|
+
export class OpenCodeAdapter extends OpenRouterAdapter {
|
|
55
|
+
provider = 'opencode';
|
|
56
|
+
// -- Configuration overrides ----------------------------------------------
|
|
57
|
+
getApiKey() {
|
|
58
|
+
return process.env.OPENCODE_API_KEY;
|
|
59
|
+
}
|
|
60
|
+
getBaseUrl() {
|
|
61
|
+
// Default endpoint per https://opencode.ai/docs/go. Allow override
|
|
62
|
+
// via OPENCODE_BASE_URL so users on different regions or testing
|
|
63
|
+
// against a local opencode serve instance can redirect.
|
|
64
|
+
return (process.env.OPENCODE_BASE_URL ?? 'https://opencode.ai/zen/go/v1').replace(/\/+$/, '');
|
|
65
|
+
}
|
|
66
|
+
getExtraHeaders() {
|
|
67
|
+
return {
|
|
68
|
+
// X-Title shows up in OpenCode's usage console; HTTP-Referer is
|
|
69
|
+
// OpenRouter-specific and not required here.
|
|
70
|
+
'X-Title': 'Anvil',
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
// -- Model id handling ----------------------------------------------------
|
|
74
|
+
supportsModel(modelId) {
|
|
75
|
+
// Registry uses the `opencode/` prefix to disambiguate from
|
|
76
|
+
// OpenRouter's `<provider>/<model>` slugs (which pass the same
|
|
77
|
+
// `includes('/')` test the parent uses).
|
|
78
|
+
return modelId.startsWith(PREFIX);
|
|
79
|
+
}
|
|
80
|
+
getModelPricing(modelId) {
|
|
81
|
+
const stripped = modelId.startsWith(PREFIX) ? modelId.slice(PREFIX.length) : modelId;
|
|
82
|
+
return OPENCODE_GO_PRICING[stripped] ?? null;
|
|
83
|
+
}
|
|
84
|
+
// -- Run override: strip prefix before calling upstream -------------------
|
|
85
|
+
async run(config, output) {
|
|
86
|
+
if (!config.model.startsWith(PREFIX)) {
|
|
87
|
+
// Safety net — caller should always send a properly-prefixed id,
|
|
88
|
+
// but if they don't we tolerate it rather than 500'ing.
|
|
89
|
+
return super.run(config, output);
|
|
90
|
+
}
|
|
91
|
+
const upstreamModel = config.model.slice(PREFIX.length);
|
|
92
|
+
const result = await super.run({ ...config, model: upstreamModel }, output);
|
|
93
|
+
// Re-stamp the result with our prefixed id so downstream telemetry
|
|
94
|
+
// attributes the call to the OpenCode adapter, not OpenRouter.
|
|
95
|
+
return { ...result, provider: this.provider, model: config.model };
|
|
96
|
+
}
|
|
97
|
+
// -- Availability ---------------------------------------------------------
|
|
98
|
+
async checkAvailability() {
|
|
99
|
+
const key = this.getApiKey();
|
|
100
|
+
if (!key)
|
|
101
|
+
return { available: false, error: 'OPENCODE_API_KEY is not set (subscribe at https://opencode.ai/zen)' };
|
|
102
|
+
return { available: true };
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=opencode-adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"opencode-adapter.js","sourceRoot":"","sources":["../src/opencode-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAGH,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAE5D,MAAM,MAAM,GAAG,WAAW,CAAC;AAE3B;;;;;;GAMG;AACH,MAAM,mBAAmB,GAAqC;IAC5D,aAAa;IACb,OAAO,EAAa,CAAC,IAAI,EAAE,IAAI,CAAC;IAChC,SAAS,EAAW,CAAC,IAAI,EAAE,IAAI,CAAC;IAChC,cAAc;IACd,WAAW,EAAS,CAAC,IAAI,EAAE,IAAI,CAAC;IAChC,WAAW,EAAS,CAAC,IAAI,EAAE,IAAI,CAAC;IAChC,OAAO;IACP,aAAa,EAAO,CAAC,IAAI,EAAE,IAAI,CAAC;IAChC,cAAc,EAAM,CAAC,IAAI,EAAE,IAAI,CAAC;IAChC,eAAe,EAAK,CAAC,IAAI,EAAE,IAAI,CAAC;IAChC,WAAW,EAAS,CAAC,IAAI,EAAE,IAAI,CAAC;IAChC,UAAU;IACV,cAAc,EAAM,CAAC,IAAI,EAAE,IAAI,CAAC;IAChC,cAAc,EAAM,CAAC,IAAI,EAAE,IAAI,CAAC;IAChC,OAAO;IACP,cAAc,EAAM,CAAC,IAAI,EAAE,IAAI,CAAC;IAChC,cAAc,EAAM,CAAC,IAAI,EAAE,IAAI,CAAC;IAChC,WAAW;IACX,iBAAiB,EAAG,CAAC,IAAI,EAAE,IAAI,CAAC;IAChC,mBAAmB,EAAC,CAAC,IAAI,EAAE,IAAI,CAAC;CACjC,CAAC;AAEF,MAAM,OAAO,eAAgB,SAAQ,iBAAiB;IAClC,QAAQ,GAAiB,UAAU,CAAC;IAEtD,4EAA4E;IAEzD,SAAS;QAC1B,OAAO,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;IACtC,CAAC;IAEkB,UAAU;QAC3B,mEAAmE;QACnE,iEAAiE;QACjE,wDAAwD;QACxD,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,+BAA+B,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAChG,CAAC;IAEkB,eAAe;QAChC,OAAO;YACL,gEAAgE;YAChE,6CAA6C;YAC7C,SAAS,EAAE,OAAO;SACnB,CAAC;IACJ,CAAC;IAED,4EAA4E;IAEnE,aAAa,CAAC,OAAe;QACpC,4DAA4D;QAC5D,+DAA+D;QAC/D,yCAAyC;QACzC,OAAO,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;IACpC,CAAC;IAEQ,eAAe,CAAC,OAAe;QACtC,MAAM,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;QACrF,OAAO,mBAAmB,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC;IAC/C,CAAC;IAED,4EAA4E;IAEnE,KAAK,CAAC,GAAG,CAChB,MAA0B,EAC1B,MAA6B;QAE7B,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;YACrC,iEAAiE;YACjE,wDAAwD;YACxD,OAAO,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACnC,CAAC;QACD,MAAM,aAAa,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACxD,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,EAAE,MAAM,CAAC,CAAC;QAC5E,mEAAmE;QACnE,+DAA+D;QAC/D,OAAO,EAAE,GAAG,MAAM,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC;IACrE,CAAC;IAED,4EAA4E;IAEnE,KAAK,CAAC,iBAAiB;QAC9B,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QAC7B,IAAI,CAAC,GAAG;YAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,oEAAoE,EAAE,CAAC;QACnH,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;IAC7B,CAAC;CACF"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenRouter adapter — agentic OpenAI-compatible client.
|
|
3
|
+
*
|
|
4
|
+
* OpenRouter aggregates many providers (Anthropic / Google / Meta / OpenAI…)
|
|
5
|
+
* behind one OpenAI-compatible API. Models advertise via the `provider/model`
|
|
6
|
+
* id format (e.g. `anthropic/claude-sonnet-4-6`).
|
|
7
|
+
*
|
|
8
|
+
* Two modes, picked at runtime by the presence of `config.toolExecutor`:
|
|
9
|
+
*
|
|
10
|
+
* 1. Single-shot chat — no executor. Drains one /v1/chat/completions
|
|
11
|
+
* response, emits text + result. Backwards-compatible with every
|
|
12
|
+
* caller that existed before agentic support landed.
|
|
13
|
+
*
|
|
14
|
+
* 2. Agentic loop — executor present. Each turn the adapter sends
|
|
15
|
+
* `tools: [...]` (OpenAI function-calling shape), reassembles
|
|
16
|
+
* `tool_calls` from streaming deltas (OpenAI streams arguments
|
|
17
|
+
* piecemeal, indexed), executes via the executor, appends the
|
|
18
|
+
* assistant + tool messages to history, and re-POSTs. Loops up
|
|
19
|
+
* to maxToolIterations (default 32).
|
|
20
|
+
*
|
|
21
|
+
* Capabilities flip to `tier: 'agentic'` because the adapter now drives
|
|
22
|
+
* a full agentic loop, same as OllamaAdapter. Stage-policy.yaml's
|
|
23
|
+
* build/validate/ship can route here when ANVIL_LLM_PROVIDER=openrouter
|
|
24
|
+
* or via per-stage models.yaml entries.
|
|
25
|
+
*/
|
|
26
|
+
import type { ModelAdapter, ModelAdapterConfig, ModelAdapterResult, ProviderCapabilities, ProviderName } from './types.js';
|
|
27
|
+
/**
|
|
28
|
+
* Re-export the shared `UpstreamError` so existing imports
|
|
29
|
+
* (`import { UpstreamError } from '@esankhan3/anvil-agent-core/openrouter-adapter'`)
|
|
30
|
+
* keep working. The class itself lives in `upstream-error.ts` and is
|
|
31
|
+
* shared by every adapter that talks to a remote provider.
|
|
32
|
+
*/
|
|
33
|
+
export { UpstreamError } from './upstream-error.js';
|
|
34
|
+
export declare class OpenRouterAdapter implements ModelAdapter {
|
|
35
|
+
readonly provider: ProviderName;
|
|
36
|
+
readonly capabilities: ProviderCapabilities;
|
|
37
|
+
/**
|
|
38
|
+
* In-flight AbortControllers — one per concurrent run() invocation.
|
|
39
|
+
* The adapter is a singleton (registered once with ProviderRegistry),
|
|
40
|
+
* but per-repo stages run multiple repos in parallel against it. A
|
|
41
|
+
* single instance-level field would let one call's `finally` block
|
|
42
|
+
* null out the controller while another call is still mid-fetch,
|
|
43
|
+
* causing "Cannot read properties of null (reading 'signal')". The
|
|
44
|
+
* Set lets every concurrent call own its own AbortController without
|
|
45
|
+
* trampling, and `kill()` aborts ALL of them (matches legacy
|
|
46
|
+
* "stop the run" semantics).
|
|
47
|
+
*/
|
|
48
|
+
private activeControllers;
|
|
49
|
+
protected getApiKey(): string | undefined;
|
|
50
|
+
protected getBaseUrl(): string;
|
|
51
|
+
protected getExtraHeaders(): Record<string, string>;
|
|
52
|
+
supportsModel(modelId: string): boolean;
|
|
53
|
+
getModelPricing(modelId: string): [number, number] | null;
|
|
54
|
+
checkAvailability(): Promise<{
|
|
55
|
+
available: boolean;
|
|
56
|
+
version?: string;
|
|
57
|
+
error?: string;
|
|
58
|
+
}>;
|
|
59
|
+
kill(): void;
|
|
60
|
+
run(config: ModelAdapterConfig, output: NodeJS.WritableStream): Promise<ModelAdapterResult>;
|
|
61
|
+
private runOneTurn;
|
|
62
|
+
/**
|
|
63
|
+
* Drain the SSE stream. Reassembles `tool_calls` from streamed deltas
|
|
64
|
+
* (OpenAI sends arguments as multiple chunks indexed by tool-call
|
|
65
|
+
* `index`). Emits text deltas to the output stream as they arrive.
|
|
66
|
+
*/
|
|
67
|
+
private consumeSSE;
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=openrouter-adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openrouter-adapter.d.ts","sourceRoot":"","sources":["../src/openrouter-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAGH,OAAO,KAAK,EACV,YAAY,EACZ,kBAAkB,EAClB,kBAAkB,EAClB,oBAAoB,EACpB,YAAY,EAIb,MAAM,YAAY,CAAC;AAKpB;;;;;GAKG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAoHpD,qBAAa,iBAAkB,YAAW,YAAY;IACpD,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAgB;IAE/C,QAAQ,CAAC,YAAY,EAAE,oBAAoB,CAYzC;IAEF;;;;;;;;;;OAUG;IACH,OAAO,CAAC,iBAAiB,CAA8B;IAIvD,SAAS,CAAC,SAAS,IAAI,MAAM,GAAG,SAAS;IAIzC,SAAS,CAAC,UAAU,IAAI,MAAM;IAI9B,SAAS,CAAC,eAAe,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAWnD,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAMvC,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI;IAInD,iBAAiB,IAAI,OAAO,CAAC;QAAE,SAAS,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAM5F,IAAI,IAAI,IAAI;IAON,GAAG,CAAC,MAAM,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM,CAAC,cAAc,GAAG,OAAO,CAAC,kBAAkB,CAAC;YA+HnF,UAAU;IAsDxB;;;;OAIG;YACW,UAAU;CAgKzB"}
|
|
@@ -0,0 +1,481 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenRouter adapter — agentic OpenAI-compatible client.
|
|
3
|
+
*
|
|
4
|
+
* OpenRouter aggregates many providers (Anthropic / Google / Meta / OpenAI…)
|
|
5
|
+
* behind one OpenAI-compatible API. Models advertise via the `provider/model`
|
|
6
|
+
* id format (e.g. `anthropic/claude-sonnet-4-6`).
|
|
7
|
+
*
|
|
8
|
+
* Two modes, picked at runtime by the presence of `config.toolExecutor`:
|
|
9
|
+
*
|
|
10
|
+
* 1. Single-shot chat — no executor. Drains one /v1/chat/completions
|
|
11
|
+
* response, emits text + result. Backwards-compatible with every
|
|
12
|
+
* caller that existed before agentic support landed.
|
|
13
|
+
*
|
|
14
|
+
* 2. Agentic loop — executor present. Each turn the adapter sends
|
|
15
|
+
* `tools: [...]` (OpenAI function-calling shape), reassembles
|
|
16
|
+
* `tool_calls` from streaming deltas (OpenAI streams arguments
|
|
17
|
+
* piecemeal, indexed), executes via the executor, appends the
|
|
18
|
+
* assistant + tool messages to history, and re-POSTs. Loops up
|
|
19
|
+
* to maxToolIterations (default 32).
|
|
20
|
+
*
|
|
21
|
+
* Capabilities flip to `tier: 'agentic'` because the adapter now drives
|
|
22
|
+
* a full agentic loop, same as OllamaAdapter. Stage-policy.yaml's
|
|
23
|
+
* build/validate/ship can route here when ANVIL_LLM_PROVIDER=openrouter
|
|
24
|
+
* or via per-stage models.yaml entries.
|
|
25
|
+
*/
|
|
26
|
+
import { randomUUID } from 'node:crypto';
|
|
27
|
+
import { emitContent, emitResult, emitToolResult, emitToolUse } from './stream-format.js';
|
|
28
|
+
const DEFAULT_MAX_ITERATIONS = 32;
|
|
29
|
+
/**
|
|
30
|
+
* Re-export the shared `UpstreamError` so existing imports
|
|
31
|
+
* (`import { UpstreamError } from '@esankhan3/anvil-agent-core/openrouter-adapter'`)
|
|
32
|
+
* keep working. The class itself lives in `upstream-error.ts` and is
|
|
33
|
+
* shared by every adapter that talks to a remote provider.
|
|
34
|
+
*/
|
|
35
|
+
export { UpstreamError } from './upstream-error.js';
|
|
36
|
+
import { UpstreamError as _UpstreamError } from './upstream-error.js';
|
|
37
|
+
// ───────────────────────────────────────────────────────────────────────
|
|
38
|
+
// Pricing fallbacks for well-known OpenRouter slugs. OpenRouter itself
|
|
39
|
+
// returns `usage.cost` in the response when available — that wins. This
|
|
40
|
+
// table is just a fallback for older API versions that don't report it.
|
|
41
|
+
// Price = [inputPer1MTokens, outputPer1MTokens]
|
|
42
|
+
// ───────────────────────────────────────────────────────────────────────
|
|
43
|
+
const OPENROUTER_PRICING = {
|
|
44
|
+
// OpenAI
|
|
45
|
+
'openai/gpt-4o': [2.5, 10.0],
|
|
46
|
+
'openai/gpt-4o-mini': [0.15, 0.60],
|
|
47
|
+
'openai/gpt-4-turbo': [10.0, 30.0],
|
|
48
|
+
'openai/o1': [15.0, 60.0],
|
|
49
|
+
'openai/o3': [10.0, 40.0],
|
|
50
|
+
'openai/o3-mini': [1.1, 4.4],
|
|
51
|
+
// Anthropic — current generation
|
|
52
|
+
'anthropic/claude-haiku-4-5': [1.0, 5.0],
|
|
53
|
+
'anthropic/claude-sonnet-4-6': [3.0, 15.0],
|
|
54
|
+
'anthropic/claude-sonnet-4-7': [3.0, 15.0],
|
|
55
|
+
'anthropic/claude-opus-4-7': [15.0, 75.0],
|
|
56
|
+
// Google
|
|
57
|
+
'google/gemini-2.5-flash': [0.30, 2.50],
|
|
58
|
+
'google/gemini-2.5-pro': [1.25, 10.0],
|
|
59
|
+
// Meta
|
|
60
|
+
'meta-llama/llama-3.3-70b-instruct': [0.59, 0.79],
|
|
61
|
+
// DeepSeek
|
|
62
|
+
'deepseek/deepseek-r1': [0.55, 2.19],
|
|
63
|
+
};
|
|
64
|
+
// ───────────────────────────────────────────────────────────────────────
|
|
65
|
+
// Adapter
|
|
66
|
+
// ───────────────────────────────────────────────────────────────────────
|
|
67
|
+
export class OpenRouterAdapter {
|
|
68
|
+
provider = 'openrouter';
|
|
69
|
+
capabilities = {
|
|
70
|
+
tier: 'agentic',
|
|
71
|
+
streaming: true,
|
|
72
|
+
toolUse: true,
|
|
73
|
+
fileSystem: true,
|
|
74
|
+
shellExecution: true,
|
|
75
|
+
sessionResume: false,
|
|
76
|
+
promptCaching: true, // OpenRouter passes through provider caching
|
|
77
|
+
cache: 'auto',
|
|
78
|
+
cacheTtlSeconds: 600,
|
|
79
|
+
structuredOutput: 'strict',
|
|
80
|
+
maxOutputTokens: true,
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* In-flight AbortControllers — one per concurrent run() invocation.
|
|
84
|
+
* The adapter is a singleton (registered once with ProviderRegistry),
|
|
85
|
+
* but per-repo stages run multiple repos in parallel against it. A
|
|
86
|
+
* single instance-level field would let one call's `finally` block
|
|
87
|
+
* null out the controller while another call is still mid-fetch,
|
|
88
|
+
* causing "Cannot read properties of null (reading 'signal')". The
|
|
89
|
+
* Set lets every concurrent call own its own AbortController without
|
|
90
|
+
* trampling, and `kill()` aborts ALL of them (matches legacy
|
|
91
|
+
* "stop the run" semantics).
|
|
92
|
+
*/
|
|
93
|
+
activeControllers = new Set();
|
|
94
|
+
// -- Configuration --------------------------------------------------------
|
|
95
|
+
getApiKey() {
|
|
96
|
+
return process.env.OPENROUTER_API_KEY;
|
|
97
|
+
}
|
|
98
|
+
getBaseUrl() {
|
|
99
|
+
return (process.env.OPENROUTER_BASE_URL ?? 'https://openrouter.ai/api/v1').replace(/\/+$/, '');
|
|
100
|
+
}
|
|
101
|
+
getExtraHeaders() {
|
|
102
|
+
return {
|
|
103
|
+
// OpenRouter-recommended attribution headers — surface in their
|
|
104
|
+
// analytics and rankings dashboards.
|
|
105
|
+
'HTTP-Referer': 'https://anvil.dev',
|
|
106
|
+
'X-Title': 'Anvil',
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
// -- ModelAdapter interface -----------------------------------------------
|
|
110
|
+
supportsModel(modelId) {
|
|
111
|
+
// OpenRouter slugs are `provider/model`. Catches Anthropic, OpenAI,
|
|
112
|
+
// Google, Meta, etc. without ambiguity vs other adapters.
|
|
113
|
+
return modelId.includes('/');
|
|
114
|
+
}
|
|
115
|
+
getModelPricing(modelId) {
|
|
116
|
+
return OPENROUTER_PRICING[modelId] ?? null;
|
|
117
|
+
}
|
|
118
|
+
async checkAvailability() {
|
|
119
|
+
const key = this.getApiKey();
|
|
120
|
+
if (!key)
|
|
121
|
+
return { available: false, error: 'OPENROUTER_API_KEY is not set' };
|
|
122
|
+
return { available: true };
|
|
123
|
+
}
|
|
124
|
+
kill() {
|
|
125
|
+
for (const ac of this.activeControllers) {
|
|
126
|
+
try {
|
|
127
|
+
ac.abort();
|
|
128
|
+
}
|
|
129
|
+
catch { /* already aborted */ }
|
|
130
|
+
}
|
|
131
|
+
this.activeControllers.clear();
|
|
132
|
+
}
|
|
133
|
+
async run(config, output) {
|
|
134
|
+
const apiKey = this.getApiKey();
|
|
135
|
+
if (!apiKey)
|
|
136
|
+
throw new Error('OPENROUTER_API_KEY is not set');
|
|
137
|
+
const abortController = new AbortController();
|
|
138
|
+
this.activeControllers.add(abortController);
|
|
139
|
+
const startMs = Date.now();
|
|
140
|
+
const messages = [];
|
|
141
|
+
if (config.projectPrompt)
|
|
142
|
+
messages.push({ role: 'system', content: config.projectPrompt });
|
|
143
|
+
messages.push({ role: 'user', content: config.userPrompt });
|
|
144
|
+
const tools = config.toolExecutor ? mapSchemasToOpenAI(config.toolExecutor.listSchemas()) : undefined;
|
|
145
|
+
const maxIter = config.maxToolIterations ?? DEFAULT_MAX_ITERATIONS;
|
|
146
|
+
let aggregatedText = '';
|
|
147
|
+
let totalIn = 0;
|
|
148
|
+
let totalOut = 0;
|
|
149
|
+
let totalCachedReadTokens = 0;
|
|
150
|
+
let totalReasoningTokens = 0;
|
|
151
|
+
let openRouterCost = 0;
|
|
152
|
+
let stopReason;
|
|
153
|
+
let providerFinishReason;
|
|
154
|
+
try {
|
|
155
|
+
for (let iter = 0; iter < maxIter; iter++) {
|
|
156
|
+
const turn = await this.runOneTurn(apiKey, messages, tools, config, output, abortController.signal);
|
|
157
|
+
aggregatedText += turn.text;
|
|
158
|
+
totalIn += turn.inputTokens;
|
|
159
|
+
totalOut += turn.outputTokens;
|
|
160
|
+
totalCachedReadTokens += turn.cachedReadTokens;
|
|
161
|
+
totalReasoningTokens += turn.reasoningTokens;
|
|
162
|
+
openRouterCost += turn.cost;
|
|
163
|
+
if (turn.finishReason)
|
|
164
|
+
providerFinishReason = turn.finishReason;
|
|
165
|
+
if (turn.toolCalls.length === 0 || !config.toolExecutor) {
|
|
166
|
+
stopReason = turn.toolCalls.length === 0 ? 'end_turn' : 'tools_unsupported';
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
// Append assistant turn to history so the model sees its own
|
|
170
|
+
// tool_calls when we re-prompt with results. Thinking models
|
|
171
|
+
// (DeepSeek V4, Kimi K2, GLM thinking variants…) require the
|
|
172
|
+
// reasoning trace echoed back — without it they 400 with
|
|
173
|
+
// "reasoning_content is missing in assistant tool call".
|
|
174
|
+
// Send both `reasoning` (string) and `reasoning_details`
|
|
175
|
+
// (structured) so OpenRouter can map to whichever shape the
|
|
176
|
+
// upstream provider expects.
|
|
177
|
+
messages.push({
|
|
178
|
+
role: 'assistant',
|
|
179
|
+
content: turn.text || null,
|
|
180
|
+
...(turn.reasoning ? { reasoning: turn.reasoning } : {}),
|
|
181
|
+
...(turn.reasoningDetails.length > 0 ? { reasoning_details: turn.reasoningDetails } : {}),
|
|
182
|
+
tool_calls: turn.toolCalls,
|
|
183
|
+
});
|
|
184
|
+
for (const tc of turn.toolCalls) {
|
|
185
|
+
const args = parseArgs(tc.function.arguments);
|
|
186
|
+
const call = { id: tc.id, name: tc.function.name, arguments: args };
|
|
187
|
+
emitToolUse(output, call.name, args, tc.id);
|
|
188
|
+
const result = await invokeTool(config.toolExecutor, call, config.workingDir, abortController.signal);
|
|
189
|
+
emitToolResult(output, { toolUseId: tc.id, content: result.content, isError: result.isError });
|
|
190
|
+
messages.push({
|
|
191
|
+
role: 'tool',
|
|
192
|
+
content: result.content,
|
|
193
|
+
tool_call_id: tc.id,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
if (abortController.signal.aborted) {
|
|
197
|
+
stopReason = 'aborted';
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
if (stopReason === undefined)
|
|
202
|
+
stopReason = 'iteration_limit';
|
|
203
|
+
}
|
|
204
|
+
finally {
|
|
205
|
+
this.activeControllers.delete(abortController);
|
|
206
|
+
}
|
|
207
|
+
const durationMs = Date.now() - startMs;
|
|
208
|
+
// Cost: prefer OpenRouter's reported cost (covers all providers it
|
|
209
|
+
// knows). Fall back to our pricing table when absent. When the table
|
|
210
|
+
// also misses, costUsd = 0 and downstream telemetry will catch the
|
|
211
|
+
// gap via the central cost table in cost.ts.
|
|
212
|
+
let costUsd = openRouterCost;
|
|
213
|
+
if (costUsd === 0) {
|
|
214
|
+
const pricing = this.getModelPricing(config.model);
|
|
215
|
+
if (pricing) {
|
|
216
|
+
costUsd = (totalIn / 1_000_000) * pricing[0] + (totalOut / 1_000_000) * pricing[1];
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
// Normalize length finish-reason like OpenAIAdapter does so callers
|
|
220
|
+
// can detect truncation provider-agnostically.
|
|
221
|
+
const normalizedStopReason = stopReason === 'end_turn' && providerFinishReason === 'length'
|
|
222
|
+
? 'max_tokens'
|
|
223
|
+
: stopReason;
|
|
224
|
+
emitResult(output, {
|
|
225
|
+
text: aggregatedText,
|
|
226
|
+
costUsd,
|
|
227
|
+
inputTokens: totalIn,
|
|
228
|
+
outputTokens: totalOut,
|
|
229
|
+
durationMs,
|
|
230
|
+
cacheReadTokens: totalCachedReadTokens,
|
|
231
|
+
});
|
|
232
|
+
return {
|
|
233
|
+
output: aggregatedText,
|
|
234
|
+
inputTokens: totalIn,
|
|
235
|
+
outputTokens: totalOut,
|
|
236
|
+
costUsd,
|
|
237
|
+
durationMs,
|
|
238
|
+
provider: this.provider,
|
|
239
|
+
model: config.model,
|
|
240
|
+
stopReason: normalizedStopReason,
|
|
241
|
+
cacheReadTokens: totalCachedReadTokens || undefined,
|
|
242
|
+
reasoningTokens: totalReasoningTokens || undefined,
|
|
243
|
+
toolCallCount: countToolCalls(messages),
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
// -- Per-turn round-trip --------------------------------------------------
|
|
247
|
+
async runOneTurn(apiKey, messages, tools, config, output, signal) {
|
|
248
|
+
const url = `${this.getBaseUrl()}/chat/completions`;
|
|
249
|
+
const body = {
|
|
250
|
+
model: config.model,
|
|
251
|
+
messages,
|
|
252
|
+
stream: true,
|
|
253
|
+
stream_options: { include_usage: true },
|
|
254
|
+
};
|
|
255
|
+
if (tools && tools.length > 0) {
|
|
256
|
+
body.tools = tools;
|
|
257
|
+
body.tool_choice = 'auto';
|
|
258
|
+
}
|
|
259
|
+
if (typeof config.maxOutputTokens === 'number' && config.maxOutputTokens > 0) {
|
|
260
|
+
body.max_tokens = config.maxOutputTokens;
|
|
261
|
+
}
|
|
262
|
+
const response = await fetch(url, {
|
|
263
|
+
method: 'POST',
|
|
264
|
+
headers: {
|
|
265
|
+
'Content-Type': 'application/json',
|
|
266
|
+
Authorization: `Bearer ${apiKey}`,
|
|
267
|
+
...this.getExtraHeaders(),
|
|
268
|
+
},
|
|
269
|
+
body: JSON.stringify(body),
|
|
270
|
+
signal,
|
|
271
|
+
});
|
|
272
|
+
if (!response.ok) {
|
|
273
|
+
const errBody = await response.text();
|
|
274
|
+
throw new _UpstreamError(response.status, errBody, { provider: this.provider });
|
|
275
|
+
}
|
|
276
|
+
if (!response.body)
|
|
277
|
+
throw new _UpstreamError(0, `${this.provider} returned no response body`, { provider: this.provider });
|
|
278
|
+
return this.consumeSSE(response, output);
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Drain the SSE stream. Reassembles `tool_calls` from streamed deltas
|
|
282
|
+
* (OpenAI sends arguments as multiple chunks indexed by tool-call
|
|
283
|
+
* `index`). Emits text deltas to the output stream as they arrive.
|
|
284
|
+
*/
|
|
285
|
+
async consumeSSE(response, output) {
|
|
286
|
+
let text = '';
|
|
287
|
+
let reasoning = '';
|
|
288
|
+
const reasoningDetails = [];
|
|
289
|
+
let inputTokens = 0;
|
|
290
|
+
let outputTokens = 0;
|
|
291
|
+
let cachedReadTokens = 0;
|
|
292
|
+
let reasoningTokens = 0;
|
|
293
|
+
let cost = 0;
|
|
294
|
+
let finishReason;
|
|
295
|
+
const toolCallsByIndex = new Map();
|
|
296
|
+
const reader = response.body.getReader();
|
|
297
|
+
const decoder = new TextDecoder();
|
|
298
|
+
let buffer = '';
|
|
299
|
+
// Buffer text deltas and flush at natural breaks (newline OR every
|
|
300
|
+
// ~80 chars). Without this, OpenAI-compatible streams that emit
|
|
301
|
+
// one token per chunk produce one activity card per word in the
|
|
302
|
+
// dashboard — every "passes", "(", "all", ")" gets its own row.
|
|
303
|
+
// Flushing on `\n` or threshold gives readable line-sized chunks.
|
|
304
|
+
let pendingText = '';
|
|
305
|
+
const FLUSH_THRESHOLD = 80;
|
|
306
|
+
const flushPending = () => {
|
|
307
|
+
if (pendingText.length === 0)
|
|
308
|
+
return;
|
|
309
|
+
emitContent(output, pendingText);
|
|
310
|
+
pendingText = '';
|
|
311
|
+
};
|
|
312
|
+
while (true) {
|
|
313
|
+
const { done, value } = await reader.read();
|
|
314
|
+
if (done)
|
|
315
|
+
break;
|
|
316
|
+
buffer += decoder.decode(value, { stream: true });
|
|
317
|
+
const lines = buffer.split('\n');
|
|
318
|
+
buffer = lines.pop() ?? '';
|
|
319
|
+
for (const line of lines) {
|
|
320
|
+
const trimmed = line.trim();
|
|
321
|
+
if (!trimmed || trimmed.startsWith(':'))
|
|
322
|
+
continue; // empty / SSE comment
|
|
323
|
+
if (!trimmed.startsWith('data: '))
|
|
324
|
+
continue;
|
|
325
|
+
const payload = trimmed.slice(6);
|
|
326
|
+
if (payload === '[DONE]')
|
|
327
|
+
continue;
|
|
328
|
+
let chunk;
|
|
329
|
+
try {
|
|
330
|
+
chunk = JSON.parse(payload);
|
|
331
|
+
}
|
|
332
|
+
catch {
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
const choice = chunk.choices?.[0];
|
|
336
|
+
const delta = choice?.delta;
|
|
337
|
+
if (delta?.content) {
|
|
338
|
+
text += delta.content;
|
|
339
|
+
pendingText += delta.content;
|
|
340
|
+
// Split the accumulated buffer on newlines so the flush is
|
|
341
|
+
// always anchored at a natural line boundary; emit each
|
|
342
|
+
// complete line, retain the trailing partial line.
|
|
343
|
+
while (pendingText.includes('\n')) {
|
|
344
|
+
const nl = pendingText.indexOf('\n');
|
|
345
|
+
const line = pendingText.slice(0, nl + 1);
|
|
346
|
+
emitContent(output, line);
|
|
347
|
+
pendingText = pendingText.slice(nl + 1);
|
|
348
|
+
}
|
|
349
|
+
// No newline yet but buffer is large — flush so very long
|
|
350
|
+
// single-line outputs (e.g. one paragraph) don't sit
|
|
351
|
+
// pending until the very end of the stream.
|
|
352
|
+
if (pendingText.length >= FLUSH_THRESHOLD) {
|
|
353
|
+
emitContent(output, pendingText);
|
|
354
|
+
pendingText = '';
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
if (delta?.reasoning) {
|
|
358
|
+
// Accumulate but don't stream to user — reasoning is for the
|
|
359
|
+
// upstream's continuation, not for the dashboard activity log.
|
|
360
|
+
reasoning += delta.reasoning;
|
|
361
|
+
}
|
|
362
|
+
if (delta?.reasoning_details && Array.isArray(delta.reasoning_details)) {
|
|
363
|
+
for (const rd of delta.reasoning_details) {
|
|
364
|
+
// Merge by index — like tool_calls, reasoning_details are
|
|
365
|
+
// streamed piecewise and grouped by `index`.
|
|
366
|
+
const idx = typeof rd.index === 'number' ? rd.index : reasoningDetails.length;
|
|
367
|
+
const existing = reasoningDetails[idx];
|
|
368
|
+
if (!existing) {
|
|
369
|
+
reasoningDetails[idx] = { ...rd, text: rd.text ?? '' };
|
|
370
|
+
}
|
|
371
|
+
else if (rd.text) {
|
|
372
|
+
existing.text = (existing.text ?? '') + rd.text;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
if (delta?.tool_calls) {
|
|
377
|
+
for (const tcDelta of delta.tool_calls) {
|
|
378
|
+
const existing = toolCallsByIndex.get(tcDelta.index);
|
|
379
|
+
if (!existing) {
|
|
380
|
+
toolCallsByIndex.set(tcDelta.index, {
|
|
381
|
+
id: tcDelta.id ?? randomUUID(),
|
|
382
|
+
type: 'function',
|
|
383
|
+
function: {
|
|
384
|
+
name: tcDelta.function?.name ?? '',
|
|
385
|
+
arguments: tcDelta.function?.arguments ?? '',
|
|
386
|
+
},
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
else {
|
|
390
|
+
if (tcDelta.id)
|
|
391
|
+
existing.id = tcDelta.id;
|
|
392
|
+
if (tcDelta.function?.name)
|
|
393
|
+
existing.function.name += tcDelta.function.name;
|
|
394
|
+
if (tcDelta.function?.arguments)
|
|
395
|
+
existing.function.arguments += tcDelta.function.arguments;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
if (typeof choice?.finish_reason === 'string') {
|
|
400
|
+
finishReason = choice.finish_reason;
|
|
401
|
+
}
|
|
402
|
+
if (chunk.usage) {
|
|
403
|
+
inputTokens = chunk.usage.prompt_tokens ?? inputTokens;
|
|
404
|
+
outputTokens = chunk.usage.completion_tokens ?? outputTokens;
|
|
405
|
+
cachedReadTokens = chunk.usage.prompt_tokens_details?.cached_tokens ?? cachedReadTokens;
|
|
406
|
+
reasoningTokens = chunk.usage.completion_tokens_details?.reasoning_tokens ?? reasoningTokens;
|
|
407
|
+
if (typeof chunk.usage.cost === 'number')
|
|
408
|
+
cost = chunk.usage.cost;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
// Flush any trailing content that didn't end on a newline so the
|
|
413
|
+
// last line of the response surfaces in the activity log.
|
|
414
|
+
flushPending();
|
|
415
|
+
// Sort tool_calls by their original index so loop dispatch order
|
|
416
|
+
// mirrors what the model intended.
|
|
417
|
+
const toolCalls = [...toolCallsByIndex.entries()]
|
|
418
|
+
.sort(([a], [b]) => a - b)
|
|
419
|
+
.map(([, v]) => v);
|
|
420
|
+
return {
|
|
421
|
+
text,
|
|
422
|
+
reasoning,
|
|
423
|
+
reasoningDetails: reasoningDetails.filter(Boolean),
|
|
424
|
+
toolCalls,
|
|
425
|
+
inputTokens,
|
|
426
|
+
outputTokens,
|
|
427
|
+
cachedReadTokens,
|
|
428
|
+
reasoningTokens,
|
|
429
|
+
cost,
|
|
430
|
+
finishReason,
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
// ───────────────────────────────────────────────────────────────────────
|
|
435
|
+
// Helpers
|
|
436
|
+
// ───────────────────────────────────────────────────────────────────────
|
|
437
|
+
function mapSchemasToOpenAI(schemas) {
|
|
438
|
+
return schemas.map((s) => ({
|
|
439
|
+
type: 'function',
|
|
440
|
+
function: {
|
|
441
|
+
name: s.name,
|
|
442
|
+
description: s.description,
|
|
443
|
+
parameters: s.inputSchema,
|
|
444
|
+
},
|
|
445
|
+
}));
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* OpenAI streams `tool_calls.arguments` as a JSON-encoded string built
|
|
449
|
+
* up across many deltas. Parse it, fall back to `{ _raw }` when the
|
|
450
|
+
* model emitted malformed JSON so the executor still gets a chance to
|
|
451
|
+
* react.
|
|
452
|
+
*/
|
|
453
|
+
function parseArgs(raw) {
|
|
454
|
+
if (!raw)
|
|
455
|
+
return {};
|
|
456
|
+
try {
|
|
457
|
+
const parsed = JSON.parse(raw);
|
|
458
|
+
return typeof parsed === 'object' && parsed !== null ? parsed : { _raw: raw };
|
|
459
|
+
}
|
|
460
|
+
catch {
|
|
461
|
+
return { _raw: raw };
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
async function invokeTool(executor, call, workingDir, abortSignal) {
|
|
465
|
+
try {
|
|
466
|
+
return await executor.execute(call, { workingDir, abortSignal });
|
|
467
|
+
}
|
|
468
|
+
catch (err) {
|
|
469
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
470
|
+
return { content: `Tool execution threw: ${msg}`, isError: true };
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
function countToolCalls(messages) {
|
|
474
|
+
let n = 0;
|
|
475
|
+
for (const m of messages) {
|
|
476
|
+
if (m.role === 'assistant' && m.tool_calls)
|
|
477
|
+
n += m.tool_calls.length;
|
|
478
|
+
}
|
|
479
|
+
return n;
|
|
480
|
+
}
|
|
481
|
+
//# sourceMappingURL=openrouter-adapter.js.map
|