torus-ai 0.1.0 → 0.2.1

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/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/tools.ts","../src/permissions.ts","../src/loop.ts","../src/pipeline.ts","../src/context.ts","../src/builtins.ts","../src/subagents.ts","../src/providers/mock.ts","../src/router.ts","../src/providers/anthropic.ts","../src/providers/gemini.ts","../src/index.ts"],"sourcesContent":["import type {\n JSONSchema,\n SdkMcpServer,\n ToolContext,\n ToolDefinition,\n ToolResultPayload,\n ToolSchema,\n} from \"./types.ts\";\n\n/**\n * Define a custom tool. Mirrors `tool()` from the Claude Agent SDK.\n * tool(\"get_temp\", \"Get temperature\", { type:\"object\", ... }, async (input, ctx) => ...)\n */\nexport function tool(\n name: string,\n description: string,\n inputSchema: JSONSchema,\n handler: (input: any, ctx: ToolContext) => Promise<ToolResultPayload> | ToolResultPayload,\n): ToolDefinition {\n return { name, description, inputSchema, handler };\n}\n\n/**\n * Bundle tools into an in-process MCP server. Mirrors `createSdkMcpServer()`.\n * Tools become namespaced `mcp__<name>__<tool>` when registered.\n */\nexport function createSdkMcpServer(opts: {\n name: string;\n version?: string;\n tools: ToolDefinition[];\n}): SdkMcpServer {\n return { kind: \"sdk-mcp\", name: opts.name, version: opts.version ?? \"1.0.0\", tools: opts.tools };\n}\n\nexport interface RegisteredTool {\n fullName: string; // \"mcp__research__lookup\" or built-in \"read_file\"\n def: ToolDefinition;\n}\n\n/** Holds the model-facing tool catalog and executes calls by namespaced name. */\nexport class ToolRegistry {\n private map = new Map<string, ToolDefinition>();\n\n /** Built-ins register under their bare name (no namespace). */\n addBuiltins(defs: ToolDefinition[]): this {\n for (const d of defs) this.map.set(d.name, d);\n return this;\n }\n\n /** SDK MCP server tools register as mcp__<server>__<tool>. */\n addServer(server: SdkMcpServer): this {\n for (const t of server.tools) this.map.set(`mcp__${server.name}__${t.name}`, t);\n return this;\n }\n\n has(fullName: string): boolean {\n return this.map.has(fullName);\n }\n\n list(): RegisteredTool[] {\n return [...this.map.entries()].map(([fullName, def]) => ({ fullName, def }));\n }\n\n /** Tool schemas to hand the model, optionally filtered to a stage's allowlist. */\n schemas(filter?: (fullName: string) => boolean): ToolSchema[] {\n return this.list()\n .filter((t) => !filter || filter(t.fullName))\n .map((t) => ({ name: t.fullName, description: t.def.description, inputSchema: t.def.inputSchema }));\n }\n\n async execute(\n fullName: string,\n input: Record<string, unknown>,\n ctx: ToolContext,\n ): Promise<ToolResultPayload> {\n const def = this.map.get(fullName);\n if (!def) return { content: `Unknown tool: ${fullName}`, isError: true };\n try {\n return await def.handler(input, ctx);\n } catch (err) {\n return { content: `Tool ${fullName} threw: ${(err as Error).message}`, isError: true };\n }\n }\n}\n","import type { CanUseTool, PermissionDecision } from \"./types.ts\";\n\n/** Match a tool name against patterns supporting a trailing \"*\" wildcard. */\nexport function matchesAllow(name: string, patterns: string[]): boolean {\n return patterns.some((p) => {\n if (p === \"*\") return true;\n if (p.endsWith(\"*\")) return name.startsWith(p.slice(0, -1));\n return p === name;\n });\n}\n\nexport interface PermissionConfig {\n /** Allowlist (wildcards ok). If omitted, all tools allowed unless canUseTool vetoes. */\n allowedTools?: string[];\n /** Explicit denials, evaluated first. */\n disallowedTools?: string[];\n /** Final custom gate — can allow non-allowlisted tools, veto allowlisted ones, or rewrite input. */\n canUseTool?: CanUseTool;\n}\n\n/**\n * Evaluation order (mirrors the Agent SDK):\n * 1. disallowedTools → deny\n * 2. allowedTools → allow (if no canUseTool)\n * 3. canUseTool → final say\n * 4. default → allow when no allowlist, deny when allowlist set and unmatched\n */\nexport class PermissionEngine {\n private cfg: PermissionConfig;\n constructor(cfg: PermissionConfig = {}) {\n this.cfg = cfg;\n }\n\n async check(name: string, input: Record<string, unknown>): Promise<PermissionDecision> {\n const { allowedTools, disallowedTools, canUseTool } = this.cfg;\n\n if (disallowedTools && matchesAllow(name, disallowedTools)) {\n return { behavior: \"deny\", message: `${name} is in disallowedTools.` };\n }\n\n const onAllowlist = allowedTools ? matchesAllow(name, allowedTools) : true;\n\n if (canUseTool) return canUseTool(name, input); // callback has the final word\n\n if (onAllowlist) return { behavior: \"allow\" };\n return {\n behavior: \"deny\",\n message: `${name} is not in allowedTools and no canUseTool callback is set.`,\n };\n }\n}\n","import type { PermissionEngine } from \"./permissions.ts\";\nimport type { ToolRegistry } from \"./tools.ts\";\nimport type {\n AgentEvent,\n Message,\n ModelProvider,\n ToolContext,\n ToolResultBlock,\n} from \"./types.ts\";\n\n// The core agentic loop: gather context → call model → if it wants tools, run them\n// under the permission gate and feed results back → repeat until the model stops\n// asking for tools (or we hit maxTurns). This is the same contract the Claude Agent\n// SDK runs; everything else in this package just shapes what enters and leaves it.\n\nexport interface LoopOptions {\n provider: ModelProvider;\n registry: ToolRegistry;\n permissions: PermissionEngine;\n system: string;\n messages: Message[]; // seeded with the user turn\n toolContext: ToolContext;\n toolFilter?: (fullName: string) => boolean; // which tools to offer this run\n maxTurns?: number;\n stage?: string;\n}\n\nexport interface LoopResult {\n finalText: string;\n turns: number;\n messages: Message[];\n}\n\nlet counter = 0;\nconst genId = () => `tu_${++counter}`;\n\nexport async function* runLoop(opts: LoopOptions): AsyncGenerator<AgentEvent, LoopResult> {\n const { provider, registry, permissions, system, messages, toolContext } = opts;\n const maxTurns = opts.maxTurns ?? 8;\n const tools = registry.schemas(opts.toolFilter);\n\n let turns = 0;\n let finalText = \"\";\n\n while (turns < maxTurns) {\n turns++;\n const res = await provider.generate({ system, messages, tools });\n\n // Ensure every tool_use has an id (mock providers may omit it).\n for (const b of res.content) if (b.type === \"tool_use\" && !b.id) b.id = genId();\n messages.push({ role: \"assistant\", content: res.content });\n\n for (const b of res.content) {\n if (b.type === \"text\" && b.text.trim()) {\n yield { type: \"assistant_text\", text: b.text, stage: opts.stage };\n }\n }\n\n if (res.stopReason !== \"tool_use\") {\n finalText = res.content\n .filter((b): b is Extract<typeof b, { type: \"text\" }> => b.type === \"text\")\n .map((b) => b.text)\n .join(\"\\n\")\n .trim();\n return { finalText, turns, messages };\n }\n\n const toolResults: ToolResultBlock[] = [];\n for (const b of res.content) {\n if (b.type !== \"tool_use\") continue;\n yield { type: \"tool_use\", name: b.name, input: b.input, stage: opts.stage };\n\n const decision = await permissions.check(b.name, b.input);\n if (decision.behavior === \"deny\") {\n yield { type: \"permission_denied\", name: b.name, message: decision.message, stage: opts.stage };\n toolResults.push({\n type: \"tool_result\",\n toolUseId: b.id,\n content: `Permission denied: ${decision.message}`,\n isError: true,\n });\n continue;\n }\n\n const input = decision.updatedInput ?? b.input;\n const result = await registry.execute(b.name, input, toolContext);\n yield {\n type: \"tool_result\",\n name: b.name,\n content: result.content,\n isError: !!result.isError,\n stage: opts.stage,\n };\n toolResults.push({\n type: \"tool_result\",\n toolUseId: b.id,\n content: result.content,\n isError: result.isError,\n });\n }\n\n messages.push({ role: \"user\", content: toolResults });\n }\n\n return { finalText: finalText || \"[max turns reached]\", turns, messages };\n}\n","import { mkdir, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { loadStageContext } from \"./context.ts\";\nimport { runLoop } from \"./loop.ts\";\nimport { PermissionEngine, type PermissionConfig } from \"./permissions.ts\";\nimport { builtinTools } from \"./builtins.ts\";\nimport { loadStages, type StageContract } from \"./subagents.ts\";\nimport { ToolRegistry } from \"./tools.ts\";\nimport type { AgentEvent, Message, ModelProvider, SdkMcpServer } from \"./types.ts\";\n\n// The ICM pipeline runner: walk numbered stages in order, give each stage only the\n// context its contract names, run the agent loop, write the named artifact to the\n// stage's output/ (the handoff point), then pause at a human review gate.\n\nexport interface PipelineOptions {\n workspaceDir: string;\n provider: ModelProvider;\n mcpServers?: SdkMcpServer[];\n /** Global permission overlay. A stage's own \"## Tools\" list is the primary allowlist. */\n permissions?: Pick<PermissionConfig, \"canUseTool\" | \"disallowedTools\">;\n /** Called after each stage writes output. Return false to halt the pipeline. */\n reviewGate?: (\n stage: StageContract,\n outputs: { artifact: string; path: string; text: string }[],\n ) => Promise<boolean> | boolean;\n maxTurnsPerStage?: number;\n contextBudgetTokens?: number; // ICM target ceiling, default 8000\n}\n\nexport async function* runPipeline(opts: PipelineOptions): AsyncGenerator<AgentEvent, void> {\n const registry = new ToolRegistry().addBuiltins(builtinTools);\n for (const s of opts.mcpServers ?? []) registry.addServer(s);\n\n const budget = opts.contextBudgetTokens ?? 8000;\n const stages = await loadStages(opts.workspaceDir);\n\n for (const stage of stages) {\n yield { type: \"stage_start\", stage: stage.name };\n\n // ── Layered context (ICM Layers 0–4, scoped to the contract) ──\n const ctx = await loadStageContext(opts.workspaceDir, stage);\n yield { type: \"context_loaded\", stage: stage.name, tokensEstimated: ctx.tokensEstimated, files: ctx.files };\n if (ctx.tokensEstimated > budget) {\n yield {\n type: \"assistant_text\",\n stage: stage.name,\n text: `⚠ context ~${ctx.tokensEstimated} tok exceeds budget ${budget} — trim this stage's Inputs (ICM principle 3).`,\n };\n }\n\n // ── The contract's \"## Tools\" list is the source of truth for availability ──\n const perm = new PermissionEngine({\n allowedTools: stage.tools, // [] ⇒ a pure prose transform, no tools offered\n disallowedTools: opts.permissions?.disallowedTools,\n canUseTool: opts.permissions?.canUseTool,\n });\n const toolFilter = (n: string) =>\n stage.tools.some((p) => (p.endsWith(\"*\") ? n.startsWith(p.slice(0, -1)) : p === n));\n\n const userPrompt =\n `Execute this stage.\\n\\n## Process\\n${stage.process}\\n\\n` +\n `Produce: ${stage.outputs.join(\", \") || \"a single markdown artifact\"}.`;\n const messages: Message[] = [{ role: \"user\", content: [{ type: \"text\", text: userPrompt }] }];\n\n const result = yield* runLoop({\n provider: opts.provider,\n registry,\n permissions: perm,\n system: ctx.system,\n messages,\n toolFilter,\n toolContext: { workspaceDir: opts.workspaceDir, stageDir: stage.stageDir },\n maxTurns: opts.maxTurnsPerStage,\n stage: stage.name,\n });\n\n // ── Persist the deliverable to output/ per the Outputs contract (the handoff) ──\n const outDir = join(stage.stageDir, \"output\");\n await mkdir(outDir, { recursive: true });\n const primary = stage.outputs[0] ?? \"output.md\";\n const path = join(outDir, primary);\n await writeFile(path, result.finalText + \"\\n\", \"utf8\");\n yield { type: \"stage_output\", stage: stage.name, artifact: primary, path };\n yield { type: \"result\", stage: stage.name, finalText: result.finalText, turns: result.turns };\n\n // ── Review gate: every stage boundary is an edit surface (ICM principle 4) ──\n const proceed = opts.reviewGate\n ? await opts.reviewGate(stage, [{ artifact: primary, path, text: result.finalText }])\n : true;\n if (!proceed) return;\n }\n}\n","import { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type { StageContract } from \"./subagents.ts\";\n\n// ICM principle 3 — \"layered context loading\": assemble the system prompt from\n// exactly the layers a stage names, nothing more. The control point is the\n// contract's Inputs list; this loader never loads \"everything just in case\".\n\nexport interface LoadedContext {\n system: string;\n files: string[];\n tokensEstimated: number;\n}\n\nconst estimateTokens = (s: string) => Math.ceil(s.length / 4); // ~4 chars/token heuristic\n\nasync function readIfExists(path: string): Promise<string | null> {\n if (!existsSync(path)) return null;\n return readFile(path, \"utf8\");\n}\n\nfunction relativeName(root: string, path: string): string {\n return path.replace(root, \"\").replace(/^[\\\\/]/, \"\").replace(/\\\\/g, \"/\");\n}\n\n/**\n * Build a stage's system prompt from the ICM layer hierarchy:\n * Layer 0 AGENT.md (identity + map)\n * Layer 1 CONTEXT.md (routing)\n * Layer 2 stage CONTEXT.md (this stage's contract)\n * Layer 3 scoped references (constraints — only files the contract names)\n * Layer 4 scoped working (prior stage output — only files the contract names)\n */\nexport async function loadStageContext(\n workspaceDir: string,\n contract: StageContract,\n): Promise<LoadedContext> {\n const parts: string[] = [];\n const files: string[] = [];\n\n const push = async (label: string, path: string) => {\n const text = await readIfExists(path);\n if (text == null) return;\n const src = relativeName(workspaceDir, path);\n parts.push(`<context layer=\"${label}\" src=\"${src}\">\\n${text.trim()}\\n</context>`);\n files.push(src);\n };\n\n await push(\"0 identity\", join(workspaceDir, \"AGENT.md\"));\n await push(\"1 routing\", join(workspaceDir, \"CONTEXT.md\"));\n await push(\"2 contract\", contract.contractPath);\n\n for (const input of contract.inputs) {\n const abs = join(contract.stageDir, input.path);\n await push(input.layer === 3 ? \"3 reference\" : \"4 working\", abs);\n }\n\n const system = parts.join(\"\\n\\n\");\n return { system, files, tokensEstimated: estimateTokens(system) };\n}\n","import { mkdir, readdir, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname, relative, resolve } from \"node:path\";\nimport { tool } from \"./tools.ts\";\nimport type { ToolDefinition } from \"./types.ts\";\n\n// Confine all built-in file access to the workspace directory (defence in depth;\n// the permission layer is the primary gate).\nfunction safeResolve(workspaceDir: string, p: string): string {\n const root = resolve(workspaceDir);\n const full = resolve(root, p);\n const rel = relative(root, full);\n if (rel.startsWith(\"..\") || resolve(root, rel) !== full) {\n throw new Error(`Path escapes workspace: ${p}`);\n }\n return full;\n}\n\nexport const readFileTool: ToolDefinition = tool(\n \"read_file\",\n \"Read a UTF-8 text file relative to the workspace root.\",\n { type: \"object\", properties: { path: { type: \"string\" } }, required: [\"path\"] },\n async (input: { path: string }, ctx) => {\n const full = safeResolve(ctx.workspaceDir, input.path);\n return { content: await readFile(full, \"utf8\") };\n },\n);\n\nexport const writeFileTool: ToolDefinition = tool(\n \"write_file\",\n \"Write a UTF-8 text file relative to the workspace root (creates parent dirs).\",\n {\n type: \"object\",\n properties: { path: { type: \"string\" }, content: { type: \"string\" } },\n required: [\"path\", \"content\"],\n },\n async (input: { path: string; content: string }, ctx) => {\n const full = safeResolve(ctx.workspaceDir, input.path);\n await mkdir(dirname(full), { recursive: true });\n await writeFile(full, input.content, \"utf8\");\n return { content: `Wrote ${input.content.length} chars to ${input.path}` };\n },\n);\n\nexport const listDirTool: ToolDefinition = tool(\n \"list_dir\",\n \"List entries of a directory relative to the workspace root.\",\n { type: \"object\", properties: { path: { type: \"string\" } }, required: [\"path\"] },\n async (input: { path: string }, ctx) => {\n const full = safeResolve(ctx.workspaceDir, input.path);\n const entries = await readdir(full, { withFileTypes: true });\n return { content: entries.map((e) => (e.isDirectory() ? e.name + \"/\" : e.name)).join(\"\\n\") };\n },\n);\n\nexport const builtinTools: ToolDefinition[] = [readFileTool, writeFileTool, listDirTool];\n","import { readFile, readdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\n// A \"subagent\" in this SDK is a stage: a markdown contract (Layer 2 CONTEXT.md)\n// that declares its Inputs / Process / Outputs / Tools. Parsing the contract turns\n// folder structure into agent architecture — the whole ICM premise.\n\nexport interface StageInput {\n layer: 3 | 4; // 3 = reference (constraints), 4 = working (input to process)\n path: string; // relative to the stage dir, exactly as the contract names it\n note?: string;\n}\n\nexport interface StageContract {\n name: string; // \"01_research\"\n order: number;\n stageDir: string;\n contractPath: string;\n inputs: StageInput[];\n process: string;\n outputs: string[]; // artifact filenames, written to the stage's output/\n tools: string[]; // allowlist patterns from the optional \"## Tools\" section\n}\n\n/** Pull the body of a \"## <Name>\" markdown section (up to the next \"## \" or EOF). */\nfunction section(body: string, name: string): string {\n const re = new RegExp(`(?:^|\\\\n)##\\\\s+${name}\\\\s*\\\\n([\\\\s\\\\S]*?)(?=\\\\n##\\\\s|$)`, \"i\");\n const m = body.match(re);\n return m ? m[1].trim() : \"\";\n}\n\nexport function parseContract(\n name: string,\n stageDir: string,\n contractPath: string,\n body: string,\n): StageContract {\n const order = parseInt(name.slice(0, 2), 10) || 0;\n\n // Inputs: \"- Layer 3 (reference): ../../_config/voice.md # optional note\"\n const inputs: StageInput[] = [];\n for (const line of section(body, \"Inputs\").split(\"\\n\")) {\n const m = line.match(/Layer\\s+([34])\\b.*?:\\s*([^\\s#]+)\\s*(?:#\\s*(.*))?$/i);\n if (m) inputs.push({ layer: Number(m[1]) as 3 | 4, path: m[2], note: m[3]?.trim() });\n }\n\n // Outputs: \"- research-output.md -> output/\"\n const outputs: string[] = [];\n for (const line of section(body, \"Outputs\").split(\"\\n\")) {\n const m = line.match(/-\\s*([A-Za-z0-9._-]+\\.(?:md|json|txt))/);\n if (m) outputs.push(m[1]);\n }\n\n // Tools (optional): the stage declares exactly which tools it may use.\n const toolsRaw = section(body, \"Tools\");\n const tools = toolsRaw\n ? toolsRaw\n .split(/[\\n,]/)\n .map((s) => s.replace(/^[-*]\\s*/, \"\").trim())\n .filter(Boolean)\n : [];\n\n return {\n name,\n order,\n stageDir,\n contractPath,\n inputs,\n process: section(body, \"Process\"),\n outputs,\n tools,\n };\n}\n\n/** Discover and parse every numbered stage folder, in execution order. */\nexport async function loadStages(workspaceDir: string): Promise<StageContract[]> {\n const stagesRoot = join(workspaceDir, \"stages\");\n const entries = await readdir(stagesRoot, { withFileTypes: true });\n const dirs = entries\n .filter((e) => e.isDirectory() && /^\\d{2}_/.test(e.name))\n .map((e) => e.name)\n .sort();\n\n const contracts: StageContract[] = [];\n for (const name of dirs) {\n const stageDir = join(stagesRoot, name);\n const contractPath = join(stageDir, \"CONTEXT.md\");\n const body = await readFile(contractPath, \"utf8\");\n contracts.push(parseContract(name, stageDir, contractPath, body));\n }\n return contracts;\n}\n","import type {\n ModelProvider,\n ModelRequest,\n ModelResponse,\n ToolSchema,\n} from \"../types.ts\";\n\nexport interface MockOptions {\n /** Label stamped into outputs so mock-generated content is unmistakable. */\n label?: string;\n}\n\n/**\n * A deterministic, offline provider that exercises the full agent loop with no API\n * key. Strategy: if tools are offered and none have been used yet, call the first\n * tool once; otherwise synthesize a final answer from the system context + any tool\n * results. It is intentionally dumb — its job is to prove the harness wiring, not to\n * write good prose. Swap in AnthropicProvider for real output.\n */\nexport class MockProvider implements ModelProvider {\n readonly name = \"mock\";\n private opts: MockOptions;\n constructor(opts: MockOptions = {}) {\n this.opts = opts;\n }\n\n async generate(req: ModelRequest): Promise<ModelResponse> {\n const alreadyUsedTool = req.messages.some((m) =>\n m.content.some((b) => b.type === \"tool_use\"),\n );\n if (req.tools.length > 0 && !alreadyUsedTool) {\n const t = req.tools[0];\n return {\n stopReason: \"tool_use\",\n content: [{ type: \"tool_use\", id: \"\", name: t.name, input: this.sampleInput(t) }],\n };\n }\n return { stopReason: \"end_turn\", content: [{ type: \"text\", text: this.synthesize(req) }] };\n }\n\n private sampleInput(t: ToolSchema): Record<string, unknown> {\n const props = (t.inputSchema.properties ?? {}) as Record<string, { type?: string }>;\n const topic = \"the requested topic\";\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(props)) {\n out[k] =\n v.type === \"number\"\n ? 3\n : v.type === \"boolean\"\n ? true\n : k === \"path\"\n ? \"shared/notes.md\"\n : topic;\n }\n return out;\n }\n\n private synthesize(req: ModelRequest): string {\n const toolData = req.messages\n .flatMap((m) => m.content)\n .filter((b) => b.type === \"tool_result\")\n .map((b) => (b as { content: string }).content)\n .join(\"\\n\");\n\n const contract = extractLayer(req.system, \"2 contract\");\n const label = this.opts.label ? ` ${this.opts.label}` : \"\";\n\n const lines = [\n `<!-- mock model output${label} -->`,\n \"\",\n \"## Result\",\n \"\",\n \"Produced by the Torus MockProvider — proof that layered context →\",\n \"agent loop → output handoff works end to end. Replace with AnthropicProvider\",\n \"for real generation.\",\n ];\n if (toolData) {\n lines.push(\"\", \"### Tool-sourced material\", \"\", \"```\", toolData.slice(0, 600), \"```\");\n }\n if (contract) {\n lines.push(\"\", \"### Stage focus (read from the Layer 2 contract)\", \"\", firstLines(contract, 6));\n }\n return lines.join(\"\\n\");\n }\n}\n\nfunction extractLayer(system: string, layer: string): string {\n const m = system.match(new RegExp(`<context layer=\"${layer}\"[^>]*>([\\\\s\\\\S]*?)</context>`));\n return m ? m[1].trim() : \"\";\n}\nfunction firstLines(s: string, n: number): string {\n return s.split(\"\\n\").slice(0, n).join(\"\\n\");\n}\n","// ─────────────────────────────────────────────────────────────────────────\n// Intelligent LLM router — picks CHEAP vs EXPENSIVE per query to cut API cost.\n//\n// Hybrid strategy (provider-agnostic):\n// 1. Fast heuristics (no API call) for the obvious cases.\n// 2. Otherwise a structured \"judge\" call to the CHEAP model that classifies\n// the query as SIMPLE | COMPLEX.\n// SIMPLE → cheap model, COMPLEX → expensive model.\n//\n// The same mechanism is provided for two provider families:\n// - Anthropic: selectModel() (Claude Haiku judge → Haiku / Sonnet)\n// - Gemini: selectGeminiModel() (Gemini Flash-Lite judge → Flash-Lite / Pro)\n//\n// Safety: the select* functions never throw — on any failure they default to\n// the EXPENSIVE model so the user experience never breaks.\n// ─────────────────────────────────────────────────────────────────────────\n\nimport type { Message } from \"./types.ts\";\n\n// ── Target models ──────────────────────────────────────────────────────────\n// Change these in one place per provider.\nexport const CHEAP_MODEL = \"claude-haiku-4-5\"; // $1 / $5 per MTok\nexport const EXPENSIVE_MODEL = \"claude-sonnet-4-6\"; // current default — $3 / $15 per MTok\n\n// Gemini defaults to the stable 2.5 family. Swap to newer IDs (e.g.\n// \"gemini-3.1-flash-lite\" / \"gemini-3.1-pro-preview\") if your key has access.\nexport const GEMINI_CHEAP_MODEL = \"gemini-2.5-flash-lite\";\nexport const GEMINI_EXPENSIVE_MODEL = \"gemini-2.5-pro\";\n\nexport type Complexity = \"SIMPLE\" | \"COMPLEX\";\n\nexport interface RouterOptions {\n /** Reuse an existing provider SDK client (avoids a second client init). */\n client?: any;\n /** API key for a lazily-created client (defaults to the provider's env var). */\n apiKey?: string;\n /** Model used as the complexity judge. Defaults to the provider's cheap model. */\n judgeModel?: string;\n}\n\n// ── 1. Fast heuristics (no API call, provider-agnostic) ─────────────────────\n\nconst SIMPLE_KEYWORDS = [\n \"hello\", \"hi \", \"hey\", \"thanks\", \"thank you\", \"yes\", \"no\",\n \"format\", \"json\", \"yaml\", \"uppercase\", \"lowercase\", \"capitalize\",\n \"translate\", \"spell\", \"reverse\", \"echo\", \"greeting\",\n];\n\nconst estimateTokens = (s: string) => Math.ceil(s.length / 4); // ~4 chars/token\n\n/**\n * Cheap, deterministic pre-classification. Returns a verdict only when it's\n * confident; otherwise null (defer to the judge).\n */\nexport function fastHeuristic(prompt: string): Complexity | null {\n const tokens = estimateTokens(prompt);\n const lower = prompt.toLowerCase();\n\n // Short prompt that mentions a trivial operation → SIMPLE, route immediately.\n if (tokens <= 30 && SIMPLE_KEYWORDS.some((k) => lower.includes(k))) return \"SIMPLE\";\n\n // Very long prompts are almost always real work → skip the judge call.\n if (tokens >= 400) return \"COMPLEX\";\n\n return null;\n}\n\n// ── 2. LLM judges (structured output on each provider's cheap model) ─────────\n\nconst JUDGE_SYSTEM =\n \"You are a routing classifier. Decide whether a user query needs a powerful \" +\n \"model or can be handled by a small, fast one. Classify as SIMPLE (greetings, \" +\n \"formatting, short factual lookups, simple rewrites, single-step tasks) or \" +\n \"COMPLEX (multi-step reasoning, coding, analysis, planning, nuanced judgment). \" +\n 'Respond ONLY with the required JSON: {\"complexity\": \"SIMPLE\" | \"COMPLEX\"}.';\n\nconst COMPLEXITY_SCHEMA = {\n type: \"object\",\n properties: { complexity: { type: \"string\", enum: [\"SIMPLE\", \"COMPLEX\"] } },\n required: [\"complexity\"],\n additionalProperties: false,\n};\n\n/** Robustly extract SIMPLE/COMPLEX from a judge response. Throws if neither. */\nfunction parseComplexity(text: string): Complexity {\n try {\n const parsed = JSON.parse(text) as { complexity?: string };\n if (parsed.complexity === \"SIMPLE\" || parsed.complexity === \"COMPLEX\") return parsed.complexity;\n } catch {\n // fall through to text scan\n }\n const m = text.toUpperCase().match(/\\b(SIMPLE|COMPLEX)\\b/);\n if (m) return m[1] as Complexity;\n throw new Error(`judge returned unparseable complexity: ${text.slice(0, 80)}`);\n}\n\n// -- Anthropic judge --\nlet sharedAnthropic: any;\nasync function getAnthropic(opts: RouterOptions): Promise<any> {\n if (opts.client) return opts.client;\n if (sharedAnthropic) return sharedAnthropic;\n const mod = await import(\"@anthropic-ai/sdk\").catch(() => {\n throw new Error(\"Anthropic judge needs @anthropic-ai/sdk (npm i @anthropic-ai/sdk).\");\n });\n const Anthropic = (mod as any).default ?? (mod as any).Anthropic;\n sharedAnthropic = new Anthropic({ apiKey: opts.apiKey ?? process.env.ANTHROPIC_API_KEY });\n return sharedAnthropic;\n}\n\n/** Grade complexity with Claude (structured output). May throw. */\nexport async function judgeComplexity(prompt: string, opts: RouterOptions = {}): Promise<Complexity> {\n const client = await getAnthropic(opts);\n const res = await client.messages.create({\n model: opts.judgeModel ?? CHEAP_MODEL,\n max_tokens: 64,\n system: JUDGE_SYSTEM,\n output_config: { format: { type: \"json_schema\", schema: COMPLEXITY_SCHEMA } },\n messages: [{ role: \"user\", content: prompt }],\n });\n const text: string = res.content.find((b: any) => b.type === \"text\")?.text ?? \"\";\n return parseComplexity(text);\n}\n\n// -- Gemini judge --\nlet sharedGemini: any;\nasync function getGemini(opts: RouterOptions): Promise<any> {\n if (opts.client) return opts.client;\n if (sharedGemini) return sharedGemini;\n const mod = await import(\"@google/genai\").catch(() => {\n throw new Error(\"Gemini judge needs @google/genai (npm i @google/genai).\");\n });\n const GoogleGenAI = (mod as any).GoogleGenAI;\n sharedGemini = new GoogleGenAI({\n apiKey: opts.apiKey ?? process.env.GOOGLE_API_KEY ?? process.env.GEMINI_API_KEY,\n });\n return sharedGemini;\n}\n\n/** Grade complexity with Gemini (JSON structured output). May throw. */\nexport async function judgeComplexityGemini(prompt: string, opts: RouterOptions = {}): Promise<Complexity> {\n const client = await getGemini(opts);\n const res = await client.models.generateContent({\n model: opts.judgeModel ?? GEMINI_CHEAP_MODEL,\n contents: prompt,\n config: {\n systemInstruction: JUDGE_SYSTEM,\n responseMimeType: \"application/json\",\n responseSchema: {\n type: \"object\",\n properties: { complexity: { type: \"string\", enum: [\"SIMPLE\", \"COMPLEX\"] } },\n required: [\"complexity\"],\n },\n },\n });\n return parseComplexity(res.text ?? \"\");\n}\n\n// ── 3. Classification + routing ─────────────────────────────────────────────\n\ntype Judge = (prompt: string, opts: RouterOptions) => Promise<Complexity>;\n\nasync function classifyWith(prompt: string, judge: Judge, opts: RouterOptions): Promise<Complexity> {\n return fastHeuristic(prompt) ?? judge(prompt, opts);\n}\n\n/** Heuristics first, Claude judge second. May throw. */\nexport function classifyComplexity(prompt: string, opts: RouterOptions = {}): Promise<Complexity> {\n return classifyWith(prompt, judgeComplexity, opts);\n}\n\n/** Heuristics first, Gemini judge second. May throw. */\nexport function classifyComplexityGemini(prompt: string, opts: RouterOptions = {}): Promise<Complexity> {\n return classifyWith(prompt, judgeComplexityGemini, opts);\n}\n\ninterface RouteConfig {\n cheapModel: string;\n expensiveModel: string;\n judge: Judge;\n}\n\nasync function routeWith(prompt: string, cfg: RouteConfig, opts: RouterOptions): Promise<string> {\n let model = cfg.expensiveModel;\n try {\n const complexity = await classifyWith(prompt, cfg.judge, opts);\n model = complexity === \"SIMPLE\" ? cfg.cheapModel : cfg.expensiveModel;\n } catch (err) {\n console.warn(\n `[router] classification failed — defaulting to expensive model. Reason: ${(err as Error).message}`,\n );\n model = cfg.expensiveModel;\n }\n record(model, model === cfg.cheapModel);\n return model;\n}\n\n/** Pick a Claude model for a prompt. Never throws (falls back to expensive). */\nexport function selectModel(prompt: string, opts: RouterOptions = {}): Promise<string> {\n return routeWith(prompt, { cheapModel: CHEAP_MODEL, expensiveModel: EXPENSIVE_MODEL, judge: judgeComplexity }, opts);\n}\n\n/** Pick a Gemini model for a prompt. Never throws (falls back to expensive). */\nexport function selectGeminiModel(prompt: string, opts: RouterOptions = {}): Promise<string> {\n return routeWith(\n prompt,\n { cheapModel: GEMINI_CHEAP_MODEL, expensiveModel: GEMINI_EXPENSIVE_MODEL, judge: judgeComplexityGemini },\n opts,\n );\n}\n\n// ── 4. Observability ─────────────────────────────────────────────────────────\n\nlet cheapCount = 0;\nlet expensiveCount = 0;\n\nfunction record(model: string, isCheap: boolean): void {\n if (isCheap) cheapCount++;\n else expensiveCount++;\n const total = cheapCount + expensiveCount;\n const pct = (n: number) => ((n / total) * 100).toFixed(0);\n console.log(\n `[router] → ${isCheap ? \"CHEAP\" : \"EXPENSIVE\"} (${model}) | cheap ${pct(cheapCount)}% / expensive ${pct(expensiveCount)}% (n=${total})`,\n );\n}\n\nexport interface RoutingStats {\n cheap: number;\n expensive: number;\n total: number;\n cheapPct: number;\n expensivePct: number;\n}\n\nexport function getRoutingStats(): RoutingStats {\n const total = cheapCount + expensiveCount;\n return {\n cheap: cheapCount,\n expensive: expensiveCount,\n total,\n cheapPct: total ? (cheapCount / total) * 100 : 0,\n expensivePct: total ? (expensiveCount / total) * 100 : 0,\n };\n}\n\n// ── Shared message util ──────────────────────────────────────────────────────\n\n/** Extract the most recent user turn's text — what the router classifies on. */\nexport function latestUserText(messages: Message[]): string {\n for (let i = messages.length - 1; i >= 0; i--) {\n const m = messages[i];\n if (m.role !== \"user\") continue;\n const text = m.content\n .filter((b): b is Extract<typeof b, { type: \"text\" }> => b.type === \"text\")\n .map((b) => b.text)\n .join(\"\\n\")\n .trim();\n if (text) return text;\n }\n return \"\";\n}\n","import { latestUserText, selectModel } from \"../router.ts\";\nimport type {\n ContentBlock,\n Message,\n ModelProvider,\n ModelRequest,\n ModelResponse,\n} from \"../types.ts\";\n\nexport interface AnthropicOptions {\n model?: string;\n apiKey?: string;\n maxTokens?: number;\n /**\n * When true, the model is chosen per-request by the cost router (cheap vs\n * expensive) based on query complexity, instead of using a fixed `model`.\n */\n route?: boolean;\n}\n\n/**\n * Real provider backed by the Anthropic Messages API. Requires the optional\n * `@anthropic-ai/sdk` dependency and an ANTHROPIC_API_KEY. The SDK is imported\n * lazily so the package (and the mock demo) work without it installed.\n */\nexport class AnthropicProvider implements ModelProvider {\n readonly name = \"anthropic\";\n private client: any;\n private model: string;\n private maxTokens: number;\n private apiKey?: string;\n private route: boolean;\n\n constructor(opts: AnthropicOptions = {}) {\n this.model = opts.model ?? \"claude-sonnet-4-6\";\n this.maxTokens = opts.maxTokens ?? 2048;\n this.apiKey = opts.apiKey ?? process.env.ANTHROPIC_API_KEY;\n this.route = opts.route ?? false;\n }\n\n private async ensureClient(): Promise<void> {\n if (this.client) return;\n const mod = await import(\"@anthropic-ai/sdk\").catch(() => {\n throw new Error(\n \"AnthropicProvider needs the @anthropic-ai/sdk package: run `npm i @anthropic-ai/sdk`.\",\n );\n });\n const Anthropic = (mod as any).default ?? (mod as any).Anthropic;\n this.client = new Anthropic({ apiKey: this.apiKey });\n }\n\n async generate(req: ModelRequest): Promise<ModelResponse> {\n await this.ensureClient();\n\n // Cost routing: pick cheap vs expensive per request from the latest user\n // turn. selectModel never throws — it falls back to the expensive model.\n const model = this.route\n ? await selectModel(latestUserText(req.messages), {\n client: this.client,\n apiKey: this.apiKey,\n })\n : this.model;\n\n const res = await this.client.messages.create({\n model,\n max_tokens: this.maxTokens,\n system: req.system,\n tools: req.tools.map((t) => ({\n name: t.name,\n description: t.description,\n input_schema: t.inputSchema,\n })),\n messages: req.messages.map(toApiMessage),\n });\n\n const content: ContentBlock[] = res.content.map((b: any): ContentBlock => {\n if (b.type === \"tool_use\") return { type: \"tool_use\", id: b.id, name: b.name, input: b.input };\n return { type: \"text\", text: b.type === \"text\" ? b.text : \"\" };\n });\n const stopReason = res.stop_reason === \"tool_use\" ? \"tool_use\" : \"end_turn\";\n return { content, stopReason };\n }\n}\n\nfunction toApiMessage(m: Message): any {\n return {\n role: m.role,\n content: m.content.map((b) => {\n if (b.type === \"text\") return { type: \"text\", text: b.text };\n if (b.type === \"tool_use\") return { type: \"tool_use\", id: b.id, name: b.name, input: b.input };\n return { type: \"tool_result\", tool_use_id: b.toolUseId, content: b.content, is_error: b.isError };\n }),\n };\n}\n","import { latestUserText, selectGeminiModel } from \"../router.ts\";\nimport type {\n ContentBlock,\n Message,\n ModelProvider,\n ModelRequest,\n ModelResponse,\n} from \"../types.ts\";\n\nexport interface GeminiOptions {\n model?: string;\n apiKey?: string;\n /**\n * When true, the model is chosen per-request by the cost router (cheap vs\n * expensive Gemini) based on query complexity, instead of a fixed `model`.\n */\n route?: boolean;\n}\n\n/**\n * Provider backed by the Google Gemini API (@google/genai). Requires the\n * optional `@google/genai` dependency and a GOOGLE_API_KEY (or GEMINI_API_KEY).\n * The SDK is imported lazily so the package works without it installed.\n */\nexport class GeminiProvider implements ModelProvider {\n readonly name = \"gemini\";\n private client: any;\n private model: string;\n private apiKey?: string;\n private route: boolean;\n\n constructor(opts: GeminiOptions = {}) {\n this.model = opts.model ?? \"gemini-2.5-flash\";\n this.apiKey = opts.apiKey ?? process.env.GOOGLE_API_KEY ?? process.env.GEMINI_API_KEY;\n this.route = opts.route ?? false;\n }\n\n private async ensureClient(): Promise<void> {\n if (this.client) return;\n const mod = await import(\"@google/genai\").catch(() => {\n throw new Error(\"GeminiProvider needs the @google/genai package: run `npm i @google/genai`.\");\n });\n const GoogleGenAI = (mod as any).GoogleGenAI;\n this.client = new GoogleGenAI({ apiKey: this.apiKey });\n }\n\n async generate(req: ModelRequest): Promise<ModelResponse> {\n await this.ensureClient();\n\n const model = this.route\n ? await selectGeminiModel(latestUserText(req.messages), {\n client: this.client,\n apiKey: this.apiKey,\n })\n : this.model;\n\n const idToName = toolUseNames(req.messages);\n\n const config: any = {};\n if (req.system) config.systemInstruction = req.system;\n if (req.tools.length) {\n config.tools = [\n {\n functionDeclarations: req.tools.map((t) => ({\n name: t.name,\n description: t.description,\n parameters: t.inputSchema,\n })),\n },\n ];\n }\n\n const res = await this.client.models.generateContent({\n model,\n contents: req.messages.map((m) => toGeminiContent(m, idToName)),\n config,\n });\n\n const content: ContentBlock[] = [];\n const text: string | undefined = res.text;\n if (text && text.trim()) content.push({ type: \"text\", text });\n\n const calls: any[] = res.functionCalls ?? [];\n for (const fc of calls) {\n content.push({ type: \"tool_use\", id: fc.id ?? \"\", name: fc.name, input: fc.args ?? {} });\n }\n if (content.length === 0) content.push({ type: \"text\", text: \"\" });\n\n return { content, stopReason: calls.length ? \"tool_use\" : \"end_turn\" };\n }\n}\n\n/** Map tool_use id -> tool name (Gemini matches function responses by name). */\nfunction toolUseNames(messages: Message[]): Map<string, string> {\n const map = new Map<string, string>();\n for (const m of messages) {\n for (const b of m.content) {\n if (b.type === \"tool_use\") map.set(b.id, b.name);\n }\n }\n return map;\n}\n\n/** Translate one of our Messages into a Gemini `Content` (role + parts). */\nfunction toGeminiContent(m: Message, idToName: Map<string, string>): any {\n const role = m.role === \"assistant\" ? \"model\" : \"user\";\n const parts = m.content.map((b) => {\n if (b.type === \"text\") return { text: b.text };\n if (b.type === \"tool_use\") return { functionCall: { id: b.id, name: b.name, args: b.input } };\n // tool_result -> functionResponse\n return {\n functionResponse: {\n id: b.toolUseId,\n name: idToName.get(b.toolUseId) ?? b.toolUseId,\n response: b.isError ? { error: b.content } : { result: b.content },\n },\n };\n });\n return { role, parts };\n}\n","// Public API for Torus.\n\nexport * from \"./types.ts\";\nexport { tool, createSdkMcpServer, ToolRegistry, type RegisteredTool } from \"./tools.ts\";\nexport { PermissionEngine, matchesAllow, type PermissionConfig } from \"./permissions.ts\";\nexport { runLoop, type LoopOptions, type LoopResult } from \"./loop.ts\";\nexport { runPipeline, type PipelineOptions } from \"./pipeline.ts\";\nexport { builtinTools, readFileTool, writeFileTool, listDirTool } from \"./builtins.ts\";\nexport { loadStages, parseContract, type StageContract, type StageInput } from \"./subagents.ts\";\nexport { loadStageContext, type LoadedContext } from \"./context.ts\";\nexport { MockProvider, type MockOptions } from \"./providers/mock.ts\";\nexport { AnthropicProvider, type AnthropicOptions } from \"./providers/anthropic.ts\";\nexport { GeminiProvider, type GeminiOptions } from \"./providers/gemini.ts\";\nexport {\n // Anthropic (Claude) routing\n CHEAP_MODEL,\n EXPENSIVE_MODEL,\n selectModel,\n classifyComplexity,\n judgeComplexity,\n // Gemini routing — same mechanism, Gemini models\n GEMINI_CHEAP_MODEL,\n GEMINI_EXPENSIVE_MODEL,\n selectGeminiModel,\n classifyComplexityGemini,\n judgeComplexityGemini,\n // Shared\n fastHeuristic,\n getRoutingStats,\n latestUserText,\n type Complexity,\n type RouterOptions,\n type RoutingStats,\n} from \"./router.ts\";\n\nimport { builtinTools } from \"./builtins.ts\";\nimport { runLoop } from \"./loop.ts\";\nimport { PermissionEngine, type PermissionConfig } from \"./permissions.ts\";\nimport { ToolRegistry } from \"./tools.ts\";\nimport type { AgentEvent, Message, ModelProvider, SdkMcpServer } from \"./types.ts\";\n\nexport interface QueryOptions {\n provider: ModelProvider;\n system?: string;\n mcpServers?: SdkMcpServer[];\n includeBuiltins?: boolean; // default true\n permissions?: PermissionConfig;\n workspaceDir?: string;\n maxTurns?: number;\n}\n\n/**\n * Single-shot agent run (no pipeline). Mirrors the Claude Agent SDK's streaming\n * `query()`: yields events as they happen and a final `result` event.\n *\n * for await (const ev of query(\"Summarize X\", { provider, mcpServers: [srv] })) { ... }\n */\nexport async function* query(\n prompt: string,\n options: QueryOptions,\n): AsyncGenerator<AgentEvent> {\n const registry = new ToolRegistry();\n if (options.includeBuiltins ?? true) registry.addBuiltins(builtinTools);\n for (const s of options.mcpServers ?? []) registry.addServer(s);\n\n const messages: Message[] = [{ role: \"user\", content: [{ type: \"text\", text: prompt }] }];\n const result = yield* runLoop({\n provider: options.provider,\n registry,\n permissions: new PermissionEngine(options.permissions ?? {}),\n system: options.system ?? \"You are a helpful agent.\",\n messages,\n toolContext: { workspaceDir: options.workspaceDir ?? process.cwd() },\n maxTurns: options.maxTurns,\n });\n yield { type: \"result\", finalText: result.finalText, turns: result.turns };\n}\n"],"mappings":"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","names":["mkdir","writeFile","join","readFile","readFile","readFile","readdir","join","join","mkdir","writeFile","estimateTokens"]}
1
+ {"version":3,"sources":["../src/types.ts","../src/tools.ts","../src/permissions.ts","../src/loop.ts","../src/pipeline.ts","../src/context.ts","../src/builtins.ts","../src/subagents.ts","../src/providers/mock.ts","../src/router.ts","../src/providers/anthropic.ts","../src/providers/gemini.ts","../src/providers/nvidia.ts","../src/providers/cascade.ts","../src/index.ts"],"sourcesContent":["// ─────────────────────────────────────────────────────────────────────────\n// Core wire types — the plain-data interface every layer speaks (ICM principle 2:\n// \"plain text/structured data is the interface\"). No provider-specific shapes leak\n// past this file; the AnthropicProvider/MockProvider translate to and from these.\n// ─────────────────────────────────────────────────────────────────────────\n\nexport type Role = \"user\" | \"assistant\";\n\nexport interface TextBlock {\n type: \"text\";\n text: string;\n}\nexport interface ToolUseBlock {\n type: \"tool_use\";\n id: string;\n name: string; // namespaced name as offered to the model, e.g. \"mcp__research__lookup\"\n input: Record<string, unknown>;\n}\nexport interface ToolResultBlock {\n type: \"tool_result\";\n toolUseId: string;\n content: string;\n isError?: boolean;\n}\n/**\n * Multimodal input. Provide either a remote `url` or base64 `data` (+ `mimeType`).\n * Image is broadly supported; video is experimental and model-dependent (routed\n * to a video-capable model like Kimi K2.6).\n */\nexport interface MediaBlock {\n type: \"image\" | \"video\";\n url?: string; // remote URL or a data: URL\n data?: string; // raw base64 (paired with mimeType)\n mimeType?: string; // e.g. \"image/png\", \"image/jpeg\", \"video/mp4\"\n}\nexport type ContentBlock = TextBlock | ToolUseBlock | ToolResultBlock | MediaBlock;\n\n/** True if a message list carries any image/video content (drives vision routing). */\nexport function hasMedia(messages: Message[]): boolean {\n return messages.some((m) => m.content.some((b) => b.type === \"image\" || b.type === \"video\"));\n}\n\nexport interface Message {\n role: Role;\n content: ContentBlock[];\n}\n\nexport type StopReason = \"end_turn\" | \"tool_use\" | \"max_turns\";\n\nexport interface ModelResponse {\n content: ContentBlock[];\n stopReason: StopReason;\n}\n\nexport type JSONSchema = Record<string, unknown>;\n\nexport interface ToolSchema {\n name: string;\n description: string;\n inputSchema: JSONSchema;\n}\n\nexport interface ModelRequest {\n system: string;\n messages: Message[];\n tools: ToolSchema[];\n}\n\n/** The one capability the SDK needs from any model backend. Swap freely. */\nexport interface ModelProvider {\n readonly name: string;\n generate(req: ModelRequest): Promise<ModelResponse>;\n}\n\n// ── Tools ────────────────────────────────────────────────────────────────\n\nexport interface ToolResultPayload {\n content: string;\n isError?: boolean;\n}\n\nexport interface ToolContext {\n workspaceDir: string;\n stageDir?: string;\n signal?: AbortSignal;\n}\n\nexport interface ToolDefinition {\n name: string; // bare name, e.g. \"lookup\"; namespacing is applied at registration\n description: string;\n inputSchema: JSONSchema;\n handler: (\n input: any,\n ctx: ToolContext,\n ) => Promise<ToolResultPayload> | ToolResultPayload;\n}\n\n/** An in-process MCP server: tools that run in this same process, no subprocess. */\nexport interface SdkMcpServer {\n kind: \"sdk-mcp\";\n name: string; // becomes the mcp__<name>__ namespace prefix\n version: string;\n tools: ToolDefinition[];\n}\n\n// ── Permissions ────────────────────────────────────────────────────────────\n\nexport type PermissionDecision =\n | { behavior: \"allow\"; updatedInput?: Record<string, unknown> }\n | { behavior: \"deny\"; message: string };\n\nexport type CanUseTool = (\n toolName: string,\n input: Record<string, unknown>,\n) => Promise<PermissionDecision> | PermissionDecision;\n\n// ── Streaming events ───────────────────────────────────────────────────────\n\nexport type AgentEvent =\n | { type: \"assistant_text\"; text: string; stage?: string }\n | { type: \"tool_use\"; name: string; input: Record<string, unknown>; stage?: string }\n | { type: \"tool_result\"; name: string; content: string; isError: boolean; stage?: string }\n | { type: \"permission_denied\"; name: string; message: string; stage?: string }\n | { type: \"stage_start\"; stage: string }\n | { type: \"stage_output\"; stage: string; artifact: string; path: string }\n | { type: \"context_loaded\"; stage?: string; tokensEstimated: number; files: string[] }\n | { type: \"result\"; finalText: string; turns: number; stage?: string };\n","import type {\n JSONSchema,\n SdkMcpServer,\n ToolContext,\n ToolDefinition,\n ToolResultPayload,\n ToolSchema,\n} from \"./types.ts\";\n\n/**\n * Define a custom tool. Mirrors `tool()` from the Claude Agent SDK.\n * tool(\"get_temp\", \"Get temperature\", { type:\"object\", ... }, async (input, ctx) => ...)\n */\nexport function tool(\n name: string,\n description: string,\n inputSchema: JSONSchema,\n handler: (input: any, ctx: ToolContext) => Promise<ToolResultPayload> | ToolResultPayload,\n): ToolDefinition {\n return { name, description, inputSchema, handler };\n}\n\n/**\n * Bundle tools into an in-process MCP server. Mirrors `createSdkMcpServer()`.\n * Tools become namespaced `mcp__<name>__<tool>` when registered.\n */\nexport function createSdkMcpServer(opts: {\n name: string;\n version?: string;\n tools: ToolDefinition[];\n}): SdkMcpServer {\n return { kind: \"sdk-mcp\", name: opts.name, version: opts.version ?? \"1.0.0\", tools: opts.tools };\n}\n\nexport interface RegisteredTool {\n fullName: string; // \"mcp__research__lookup\" or built-in \"read_file\"\n def: ToolDefinition;\n}\n\n/** Holds the model-facing tool catalog and executes calls by namespaced name. */\nexport class ToolRegistry {\n private map = new Map<string, ToolDefinition>();\n\n /** Built-ins register under their bare name (no namespace). */\n addBuiltins(defs: ToolDefinition[]): this {\n for (const d of defs) this.map.set(d.name, d);\n return this;\n }\n\n /** SDK MCP server tools register as mcp__<server>__<tool>. */\n addServer(server: SdkMcpServer): this {\n for (const t of server.tools) this.map.set(`mcp__${server.name}__${t.name}`, t);\n return this;\n }\n\n has(fullName: string): boolean {\n return this.map.has(fullName);\n }\n\n list(): RegisteredTool[] {\n return [...this.map.entries()].map(([fullName, def]) => ({ fullName, def }));\n }\n\n /** Tool schemas to hand the model, optionally filtered to a stage's allowlist. */\n schemas(filter?: (fullName: string) => boolean): ToolSchema[] {\n return this.list()\n .filter((t) => !filter || filter(t.fullName))\n .map((t) => ({ name: t.fullName, description: t.def.description, inputSchema: t.def.inputSchema }));\n }\n\n async execute(\n fullName: string,\n input: Record<string, unknown>,\n ctx: ToolContext,\n ): Promise<ToolResultPayload> {\n const def = this.map.get(fullName);\n if (!def) return { content: `Unknown tool: ${fullName}`, isError: true };\n try {\n return await def.handler(input, ctx);\n } catch (err) {\n return { content: `Tool ${fullName} threw: ${(err as Error).message}`, isError: true };\n }\n }\n}\n","import type { CanUseTool, PermissionDecision } from \"./types.ts\";\n\n/** Match a tool name against patterns supporting a trailing \"*\" wildcard. */\nexport function matchesAllow(name: string, patterns: string[]): boolean {\n return patterns.some((p) => {\n if (p === \"*\") return true;\n if (p.endsWith(\"*\")) return name.startsWith(p.slice(0, -1));\n return p === name;\n });\n}\n\nexport interface PermissionConfig {\n /** Allowlist (wildcards ok). If omitted, all tools allowed unless canUseTool vetoes. */\n allowedTools?: string[];\n /** Explicit denials, evaluated first. */\n disallowedTools?: string[];\n /** Final custom gate — can allow non-allowlisted tools, veto allowlisted ones, or rewrite input. */\n canUseTool?: CanUseTool;\n}\n\n/**\n * Evaluation order (mirrors the Agent SDK):\n * 1. disallowedTools → deny\n * 2. allowedTools → allow (if no canUseTool)\n * 3. canUseTool → final say\n * 4. default → allow when no allowlist, deny when allowlist set and unmatched\n */\nexport class PermissionEngine {\n private cfg: PermissionConfig;\n constructor(cfg: PermissionConfig = {}) {\n this.cfg = cfg;\n }\n\n async check(name: string, input: Record<string, unknown>): Promise<PermissionDecision> {\n const { allowedTools, disallowedTools, canUseTool } = this.cfg;\n\n if (disallowedTools && matchesAllow(name, disallowedTools)) {\n return { behavior: \"deny\", message: `${name} is in disallowedTools.` };\n }\n\n const onAllowlist = allowedTools ? matchesAllow(name, allowedTools) : true;\n\n if (canUseTool) return canUseTool(name, input); // callback has the final word\n\n if (onAllowlist) return { behavior: \"allow\" };\n return {\n behavior: \"deny\",\n message: `${name} is not in allowedTools and no canUseTool callback is set.`,\n };\n }\n}\n","import type { PermissionEngine } from \"./permissions.ts\";\nimport type { ToolRegistry } from \"./tools.ts\";\nimport type {\n AgentEvent,\n Message,\n ModelProvider,\n ToolContext,\n ToolResultBlock,\n} from \"./types.ts\";\n\n// The core agentic loop: gather context → call model → if it wants tools, run them\n// under the permission gate and feed results back → repeat until the model stops\n// asking for tools (or we hit maxTurns). This is the same contract the Claude Agent\n// SDK runs; everything else in this package just shapes what enters and leaves it.\n\nexport interface LoopOptions {\n provider: ModelProvider;\n registry: ToolRegistry;\n permissions: PermissionEngine;\n system: string;\n messages: Message[]; // seeded with the user turn\n toolContext: ToolContext;\n toolFilter?: (fullName: string) => boolean; // which tools to offer this run\n maxTurns?: number;\n stage?: string;\n}\n\nexport interface LoopResult {\n finalText: string;\n turns: number;\n messages: Message[];\n}\n\nlet counter = 0;\nconst genId = () => `tu_${++counter}`;\n\nexport async function* runLoop(opts: LoopOptions): AsyncGenerator<AgentEvent, LoopResult> {\n const { provider, registry, permissions, system, messages, toolContext } = opts;\n const maxTurns = opts.maxTurns ?? 8;\n const tools = registry.schemas(opts.toolFilter);\n\n let turns = 0;\n let finalText = \"\";\n\n while (turns < maxTurns) {\n turns++;\n const res = await provider.generate({ system, messages, tools });\n\n // Ensure every tool_use has an id (mock providers may omit it).\n for (const b of res.content) if (b.type === \"tool_use\" && !b.id) b.id = genId();\n messages.push({ role: \"assistant\", content: res.content });\n\n for (const b of res.content) {\n if (b.type === \"text\" && b.text.trim()) {\n yield { type: \"assistant_text\", text: b.text, stage: opts.stage };\n }\n }\n\n if (res.stopReason !== \"tool_use\") {\n finalText = res.content\n .filter((b): b is Extract<typeof b, { type: \"text\" }> => b.type === \"text\")\n .map((b) => b.text)\n .join(\"\\n\")\n .trim();\n return { finalText, turns, messages };\n }\n\n const toolResults: ToolResultBlock[] = [];\n for (const b of res.content) {\n if (b.type !== \"tool_use\") continue;\n yield { type: \"tool_use\", name: b.name, input: b.input, stage: opts.stage };\n\n const decision = await permissions.check(b.name, b.input);\n if (decision.behavior === \"deny\") {\n yield { type: \"permission_denied\", name: b.name, message: decision.message, stage: opts.stage };\n toolResults.push({\n type: \"tool_result\",\n toolUseId: b.id,\n content: `Permission denied: ${decision.message}`,\n isError: true,\n });\n continue;\n }\n\n const input = decision.updatedInput ?? b.input;\n const result = await registry.execute(b.name, input, toolContext);\n yield {\n type: \"tool_result\",\n name: b.name,\n content: result.content,\n isError: !!result.isError,\n stage: opts.stage,\n };\n toolResults.push({\n type: \"tool_result\",\n toolUseId: b.id,\n content: result.content,\n isError: result.isError,\n });\n }\n\n messages.push({ role: \"user\", content: toolResults });\n }\n\n return { finalText: finalText || \"[max turns reached]\", turns, messages };\n}\n","import { mkdir, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { loadStageContext } from \"./context.ts\";\nimport { runLoop } from \"./loop.ts\";\nimport { PermissionEngine, type PermissionConfig } from \"./permissions.ts\";\nimport { builtinTools } from \"./builtins.ts\";\nimport { loadStages, type StageContract } from \"./subagents.ts\";\nimport { ToolRegistry } from \"./tools.ts\";\nimport type { AgentEvent, Message, ModelProvider, SdkMcpServer } from \"./types.ts\";\n\n// The ICM pipeline runner: walk numbered stages in order, give each stage only the\n// context its contract names, run the agent loop, write the named artifact to the\n// stage's output/ (the handoff point), then pause at a human review gate.\n\nexport interface PipelineOptions {\n workspaceDir: string;\n provider: ModelProvider;\n mcpServers?: SdkMcpServer[];\n /** Global permission overlay. A stage's own \"## Tools\" list is the primary allowlist. */\n permissions?: Pick<PermissionConfig, \"canUseTool\" | \"disallowedTools\">;\n /** Called after each stage writes output. Return false to halt the pipeline. */\n reviewGate?: (\n stage: StageContract,\n outputs: { artifact: string; path: string; text: string }[],\n ) => Promise<boolean> | boolean;\n maxTurnsPerStage?: number;\n contextBudgetTokens?: number; // ICM target ceiling, default 8000\n}\n\nexport async function* runPipeline(opts: PipelineOptions): AsyncGenerator<AgentEvent, void> {\n const registry = new ToolRegistry().addBuiltins(builtinTools);\n for (const s of opts.mcpServers ?? []) registry.addServer(s);\n\n const budget = opts.contextBudgetTokens ?? 8000;\n const stages = await loadStages(opts.workspaceDir);\n\n for (const stage of stages) {\n yield { type: \"stage_start\", stage: stage.name };\n\n // ── Layered context (ICM Layers 0–4, scoped to the contract) ──\n const ctx = await loadStageContext(opts.workspaceDir, stage);\n yield { type: \"context_loaded\", stage: stage.name, tokensEstimated: ctx.tokensEstimated, files: ctx.files };\n if (ctx.tokensEstimated > budget) {\n yield {\n type: \"assistant_text\",\n stage: stage.name,\n text: `⚠ context ~${ctx.tokensEstimated} tok exceeds budget ${budget} — trim this stage's Inputs (ICM principle 3).`,\n };\n }\n\n // ── The contract's \"## Tools\" list is the source of truth for availability ──\n const perm = new PermissionEngine({\n allowedTools: stage.tools, // [] ⇒ a pure prose transform, no tools offered\n disallowedTools: opts.permissions?.disallowedTools,\n canUseTool: opts.permissions?.canUseTool,\n });\n const toolFilter = (n: string) =>\n stage.tools.some((p) => (p.endsWith(\"*\") ? n.startsWith(p.slice(0, -1)) : p === n));\n\n const userPrompt =\n `Execute this stage.\\n\\n## Process\\n${stage.process}\\n\\n` +\n `Produce: ${stage.outputs.join(\", \") || \"a single markdown artifact\"}.`;\n const messages: Message[] = [{ role: \"user\", content: [{ type: \"text\", text: userPrompt }] }];\n\n const result = yield* runLoop({\n provider: opts.provider,\n registry,\n permissions: perm,\n system: ctx.system,\n messages,\n toolFilter,\n toolContext: { workspaceDir: opts.workspaceDir, stageDir: stage.stageDir },\n maxTurns: opts.maxTurnsPerStage,\n stage: stage.name,\n });\n\n // ── Persist the deliverable to output/ per the Outputs contract (the handoff) ──\n const outDir = join(stage.stageDir, \"output\");\n await mkdir(outDir, { recursive: true });\n const primary = stage.outputs[0] ?? \"output.md\";\n const path = join(outDir, primary);\n await writeFile(path, result.finalText + \"\\n\", \"utf8\");\n yield { type: \"stage_output\", stage: stage.name, artifact: primary, path };\n yield { type: \"result\", stage: stage.name, finalText: result.finalText, turns: result.turns };\n\n // ── Review gate: every stage boundary is an edit surface (ICM principle 4) ──\n const proceed = opts.reviewGate\n ? await opts.reviewGate(stage, [{ artifact: primary, path, text: result.finalText }])\n : true;\n if (!proceed) return;\n }\n}\n","import { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type { StageContract } from \"./subagents.ts\";\n\n// ICM principle 3 — \"layered context loading\": assemble the system prompt from\n// exactly the layers a stage names, nothing more. The control point is the\n// contract's Inputs list; this loader never loads \"everything just in case\".\n\nexport interface LoadedContext {\n system: string;\n files: string[];\n tokensEstimated: number;\n}\n\nconst estimateTokens = (s: string) => Math.ceil(s.length / 4); // ~4 chars/token heuristic\n\nasync function readIfExists(path: string): Promise<string | null> {\n if (!existsSync(path)) return null;\n return readFile(path, \"utf8\");\n}\n\nfunction relativeName(root: string, path: string): string {\n return path.replace(root, \"\").replace(/^[\\\\/]/, \"\").replace(/\\\\/g, \"/\");\n}\n\n/**\n * Build a stage's system prompt from the ICM layer hierarchy:\n * Layer 0 AGENT.md (identity + map)\n * Layer 1 CONTEXT.md (routing)\n * Layer 2 stage CONTEXT.md (this stage's contract)\n * Layer 3 scoped references (constraints — only files the contract names)\n * Layer 4 scoped working (prior stage output — only files the contract names)\n */\nexport async function loadStageContext(\n workspaceDir: string,\n contract: StageContract,\n): Promise<LoadedContext> {\n const parts: string[] = [];\n const files: string[] = [];\n\n const push = async (label: string, path: string) => {\n const text = await readIfExists(path);\n if (text == null) return;\n const src = relativeName(workspaceDir, path);\n parts.push(`<context layer=\"${label}\" src=\"${src}\">\\n${text.trim()}\\n</context>`);\n files.push(src);\n };\n\n await push(\"0 identity\", join(workspaceDir, \"AGENT.md\"));\n await push(\"1 routing\", join(workspaceDir, \"CONTEXT.md\"));\n await push(\"2 contract\", contract.contractPath);\n\n for (const input of contract.inputs) {\n const abs = join(contract.stageDir, input.path);\n await push(input.layer === 3 ? \"3 reference\" : \"4 working\", abs);\n }\n\n const system = parts.join(\"\\n\\n\");\n return { system, files, tokensEstimated: estimateTokens(system) };\n}\n","import { mkdir, readdir, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname, relative, resolve } from \"node:path\";\nimport { tool } from \"./tools.ts\";\nimport type { ToolDefinition } from \"./types.ts\";\n\n// Confine all built-in file access to the workspace directory (defence in depth;\n// the permission layer is the primary gate).\nfunction safeResolve(workspaceDir: string, p: string): string {\n const root = resolve(workspaceDir);\n const full = resolve(root, p);\n const rel = relative(root, full);\n if (rel.startsWith(\"..\") || resolve(root, rel) !== full) {\n throw new Error(`Path escapes workspace: ${p}`);\n }\n return full;\n}\n\nexport const readFileTool: ToolDefinition = tool(\n \"read_file\",\n \"Read a UTF-8 text file relative to the workspace root.\",\n { type: \"object\", properties: { path: { type: \"string\" } }, required: [\"path\"] },\n async (input: { path: string }, ctx) => {\n const full = safeResolve(ctx.workspaceDir, input.path);\n return { content: await readFile(full, \"utf8\") };\n },\n);\n\nexport const writeFileTool: ToolDefinition = tool(\n \"write_file\",\n \"Write a UTF-8 text file relative to the workspace root (creates parent dirs).\",\n {\n type: \"object\",\n properties: { path: { type: \"string\" }, content: { type: \"string\" } },\n required: [\"path\", \"content\"],\n },\n async (input: { path: string; content: string }, ctx) => {\n const full = safeResolve(ctx.workspaceDir, input.path);\n await mkdir(dirname(full), { recursive: true });\n await writeFile(full, input.content, \"utf8\");\n return { content: `Wrote ${input.content.length} chars to ${input.path}` };\n },\n);\n\nexport const listDirTool: ToolDefinition = tool(\n \"list_dir\",\n \"List entries of a directory relative to the workspace root.\",\n { type: \"object\", properties: { path: { type: \"string\" } }, required: [\"path\"] },\n async (input: { path: string }, ctx) => {\n const full = safeResolve(ctx.workspaceDir, input.path);\n const entries = await readdir(full, { withFileTypes: true });\n return { content: entries.map((e) => (e.isDirectory() ? e.name + \"/\" : e.name)).join(\"\\n\") };\n },\n);\n\nexport const builtinTools: ToolDefinition[] = [readFileTool, writeFileTool, listDirTool];\n","import { readFile, readdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\n// A \"subagent\" in this SDK is a stage: a markdown contract (Layer 2 CONTEXT.md)\n// that declares its Inputs / Process / Outputs / Tools. Parsing the contract turns\n// folder structure into agent architecture — the whole ICM premise.\n\nexport interface StageInput {\n layer: 3 | 4; // 3 = reference (constraints), 4 = working (input to process)\n path: string; // relative to the stage dir, exactly as the contract names it\n note?: string;\n}\n\nexport interface StageContract {\n name: string; // \"01_research\"\n order: number;\n stageDir: string;\n contractPath: string;\n inputs: StageInput[];\n process: string;\n outputs: string[]; // artifact filenames, written to the stage's output/\n tools: string[]; // allowlist patterns from the optional \"## Tools\" section\n}\n\n/** Pull the body of a \"## <Name>\" markdown section (up to the next \"## \" or EOF). */\nfunction section(body: string, name: string): string {\n const re = new RegExp(`(?:^|\\\\n)##\\\\s+${name}\\\\s*\\\\n([\\\\s\\\\S]*?)(?=\\\\n##\\\\s|$)`, \"i\");\n const m = body.match(re);\n return m ? m[1].trim() : \"\";\n}\n\nexport function parseContract(\n name: string,\n stageDir: string,\n contractPath: string,\n body: string,\n): StageContract {\n const order = parseInt(name.slice(0, 2), 10) || 0;\n\n // Inputs: \"- Layer 3 (reference): ../../_config/voice.md # optional note\"\n const inputs: StageInput[] = [];\n for (const line of section(body, \"Inputs\").split(\"\\n\")) {\n const m = line.match(/Layer\\s+([34])\\b.*?:\\s*([^\\s#]+)\\s*(?:#\\s*(.*))?$/i);\n if (m) inputs.push({ layer: Number(m[1]) as 3 | 4, path: m[2], note: m[3]?.trim() });\n }\n\n // Outputs: \"- research-output.md -> output/\"\n const outputs: string[] = [];\n for (const line of section(body, \"Outputs\").split(\"\\n\")) {\n const m = line.match(/-\\s*([A-Za-z0-9._-]+\\.(?:md|json|txt))/);\n if (m) outputs.push(m[1]);\n }\n\n // Tools (optional): the stage declares exactly which tools it may use.\n const toolsRaw = section(body, \"Tools\");\n const tools = toolsRaw\n ? toolsRaw\n .split(/[\\n,]/)\n .map((s) => s.replace(/^[-*]\\s*/, \"\").trim())\n .filter(Boolean)\n : [];\n\n return {\n name,\n order,\n stageDir,\n contractPath,\n inputs,\n process: section(body, \"Process\"),\n outputs,\n tools,\n };\n}\n\n/** Discover and parse every numbered stage folder, in execution order. */\nexport async function loadStages(workspaceDir: string): Promise<StageContract[]> {\n const stagesRoot = join(workspaceDir, \"stages\");\n const entries = await readdir(stagesRoot, { withFileTypes: true });\n const dirs = entries\n .filter((e) => e.isDirectory() && /^\\d{2}_/.test(e.name))\n .map((e) => e.name)\n .sort();\n\n const contracts: StageContract[] = [];\n for (const name of dirs) {\n const stageDir = join(stagesRoot, name);\n const contractPath = join(stageDir, \"CONTEXT.md\");\n const body = await readFile(contractPath, \"utf8\");\n contracts.push(parseContract(name, stageDir, contractPath, body));\n }\n return contracts;\n}\n","import type {\n ModelProvider,\n ModelRequest,\n ModelResponse,\n ToolSchema,\n} from \"../types.ts\";\n\nexport interface MockOptions {\n /** Label stamped into outputs so mock-generated content is unmistakable. */\n label?: string;\n}\n\n/**\n * A deterministic, offline provider that exercises the full agent loop with no API\n * key. Strategy: if tools are offered and none have been used yet, call the first\n * tool once; otherwise synthesize a final answer from the system context + any tool\n * results. It is intentionally dumb — its job is to prove the harness wiring, not to\n * write good prose. Swap in AnthropicProvider for real output.\n */\nexport class MockProvider implements ModelProvider {\n readonly name = \"mock\";\n private opts: MockOptions;\n constructor(opts: MockOptions = {}) {\n this.opts = opts;\n }\n\n async generate(req: ModelRequest): Promise<ModelResponse> {\n const alreadyUsedTool = req.messages.some((m) =>\n m.content.some((b) => b.type === \"tool_use\"),\n );\n if (req.tools.length > 0 && !alreadyUsedTool) {\n const t = req.tools[0];\n return {\n stopReason: \"tool_use\",\n content: [{ type: \"tool_use\", id: \"\", name: t.name, input: this.sampleInput(t) }],\n };\n }\n return { stopReason: \"end_turn\", content: [{ type: \"text\", text: this.synthesize(req) }] };\n }\n\n private sampleInput(t: ToolSchema): Record<string, unknown> {\n const props = (t.inputSchema.properties ?? {}) as Record<string, { type?: string }>;\n const topic = \"the requested topic\";\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(props)) {\n out[k] =\n v.type === \"number\"\n ? 3\n : v.type === \"boolean\"\n ? true\n : k === \"path\"\n ? \"shared/notes.md\"\n : topic;\n }\n return out;\n }\n\n private synthesize(req: ModelRequest): string {\n const toolData = req.messages\n .flatMap((m) => m.content)\n .filter((b) => b.type === \"tool_result\")\n .map((b) => (b as { content: string }).content)\n .join(\"\\n\");\n\n const contract = extractLayer(req.system, \"2 contract\");\n const label = this.opts.label ? ` ${this.opts.label}` : \"\";\n\n const lines = [\n `<!-- mock model output${label} -->`,\n \"\",\n \"## Result\",\n \"\",\n \"Produced by the Torus MockProvider — proof that layered context →\",\n \"agent loop → output handoff works end to end. Replace with AnthropicProvider\",\n \"for real generation.\",\n ];\n if (toolData) {\n lines.push(\"\", \"### Tool-sourced material\", \"\", \"```\", toolData.slice(0, 600), \"```\");\n }\n if (contract) {\n lines.push(\"\", \"### Stage focus (read from the Layer 2 contract)\", \"\", firstLines(contract, 6));\n }\n return lines.join(\"\\n\");\n }\n}\n\nfunction extractLayer(system: string, layer: string): string {\n const m = system.match(new RegExp(`<context layer=\"${layer}\"[^>]*>([\\\\s\\\\S]*?)</context>`));\n return m ? m[1].trim() : \"\";\n}\nfunction firstLines(s: string, n: number): string {\n return s.split(\"\\n\").slice(0, n).join(\"\\n\");\n}\n","// ─────────────────────────────────────────────────────────────────────────\n// Intelligent LLM router — picks CHEAP vs EXPENSIVE per query to cut API cost.\n//\n// Hybrid strategy (provider-agnostic):\n// 1. Fast heuristics (no API call) for the obvious cases.\n// 2. Otherwise a structured \"judge\" call to the CHEAP model that classifies\n// the query as SIMPLE | COMPLEX.\n// SIMPLE → cheap model, COMPLEX → expensive model.\n//\n// The same mechanism is provided for two provider families:\n// - Anthropic: selectModel() (Claude Haiku judge → Haiku / Sonnet)\n// - Gemini: selectGeminiModel() (Gemini Flash-Lite judge → Flash-Lite / Pro)\n//\n// Safety: the select* functions never throw — on any failure they default to\n// the EXPENSIVE model so the user experience never breaks.\n// ─────────────────────────────────────────────────────────────────────────\n\nimport type { Message } from \"./types.ts\";\n\n// ── Target models ──────────────────────────────────────────────────────────\n// Change these in one place per provider.\nexport const CHEAP_MODEL = \"claude-haiku-4-5\"; // $1 / $5 per MTok\nexport const EXPENSIVE_MODEL = \"claude-sonnet-4-6\"; // current default — $3 / $15 per MTok\n\n// Gemini defaults to the stable 2.5 family. Swap to newer IDs (e.g.\n// \"gemini-3.1-flash-lite\" / \"gemini-3.1-pro-preview\") if your key has access.\nexport const GEMINI_CHEAP_MODEL = \"gemini-2.5-flash-lite\";\nexport const GEMINI_EXPENSIVE_MODEL = \"gemini-2.5-pro\";\n\nexport type Complexity = \"SIMPLE\" | \"COMPLEX\";\n\nexport interface RouterOptions {\n /** Reuse an existing provider SDK client (avoids a second client init). */\n client?: any;\n /** API key for a lazily-created client (defaults to the provider's env var). */\n apiKey?: string;\n /** Model used as the complexity judge. Defaults to the provider's cheap model. */\n judgeModel?: string;\n}\n\n// ── 1. Fast heuristics (no API call, provider-agnostic) ─────────────────────\n\nconst SIMPLE_KEYWORDS = [\n \"hello\", \"hi \", \"hey\", \"thanks\", \"thank you\", \"yes\", \"no\",\n \"format\", \"json\", \"yaml\", \"uppercase\", \"lowercase\", \"capitalize\",\n \"translate\", \"spell\", \"reverse\", \"echo\", \"greeting\",\n];\n\nconst estimateTokens = (s: string) => Math.ceil(s.length / 4); // ~4 chars/token\n\n/**\n * Cheap, deterministic pre-classification. Returns a verdict only when it's\n * confident; otherwise null (defer to the judge).\n */\nexport function fastHeuristic(prompt: string): Complexity | null {\n const tokens = estimateTokens(prompt);\n const lower = prompt.toLowerCase();\n\n // Short prompt that mentions a trivial operation → SIMPLE, route immediately.\n if (tokens <= 30 && SIMPLE_KEYWORDS.some((k) => lower.includes(k))) return \"SIMPLE\";\n\n // Very long prompts are almost always real work → skip the judge call.\n if (tokens >= 400) return \"COMPLEX\";\n\n return null;\n}\n\n// ── 2. LLM judges (structured output on each provider's cheap model) ─────────\n\nconst JUDGE_SYSTEM =\n \"You are a routing classifier. Decide whether a user query needs a powerful \" +\n \"model or can be handled by a small, fast one. Classify as SIMPLE (greetings, \" +\n \"formatting, short factual lookups, simple rewrites, single-step tasks) or \" +\n \"COMPLEX (multi-step reasoning, coding, analysis, planning, nuanced judgment). \" +\n 'Respond ONLY with the required JSON: {\"complexity\": \"SIMPLE\" | \"COMPLEX\"}.';\n\nconst COMPLEXITY_SCHEMA = {\n type: \"object\",\n properties: { complexity: { type: \"string\", enum: [\"SIMPLE\", \"COMPLEX\"] } },\n required: [\"complexity\"],\n additionalProperties: false,\n};\n\n/** Robustly extract SIMPLE/COMPLEX from a judge response. Throws if neither. */\nfunction parseComplexity(text: string): Complexity {\n try {\n const parsed = JSON.parse(text) as { complexity?: string };\n if (parsed.complexity === \"SIMPLE\" || parsed.complexity === \"COMPLEX\") return parsed.complexity;\n } catch {\n // fall through to text scan\n }\n const m = text.toUpperCase().match(/\\b(SIMPLE|COMPLEX)\\b/);\n if (m) return m[1] as Complexity;\n throw new Error(`judge returned unparseable complexity: ${text.slice(0, 80)}`);\n}\n\n// -- Anthropic judge --\nlet sharedAnthropic: any;\nasync function getAnthropic(opts: RouterOptions): Promise<any> {\n if (opts.client) return opts.client;\n if (sharedAnthropic) return sharedAnthropic;\n const mod = await import(\"@anthropic-ai/sdk\").catch(() => {\n throw new Error(\"Anthropic judge needs @anthropic-ai/sdk (npm i @anthropic-ai/sdk).\");\n });\n const Anthropic = (mod as any).default ?? (mod as any).Anthropic;\n sharedAnthropic = new Anthropic({ apiKey: opts.apiKey ?? process.env.ANTHROPIC_API_KEY });\n return sharedAnthropic;\n}\n\n/** Grade complexity with Claude (structured output). May throw. */\nexport async function judgeComplexity(prompt: string, opts: RouterOptions = {}): Promise<Complexity> {\n const client = await getAnthropic(opts);\n const res = await client.messages.create({\n model: opts.judgeModel ?? CHEAP_MODEL,\n max_tokens: 64,\n system: JUDGE_SYSTEM,\n output_config: { format: { type: \"json_schema\", schema: COMPLEXITY_SCHEMA } },\n messages: [{ role: \"user\", content: prompt }],\n });\n const text: string = res.content.find((b: any) => b.type === \"text\")?.text ?? \"\";\n return parseComplexity(text);\n}\n\n// -- Gemini judge --\nlet sharedGemini: any;\nasync function getGemini(opts: RouterOptions): Promise<any> {\n if (opts.client) return opts.client;\n if (sharedGemini) return sharedGemini;\n const mod = await import(\"@google/genai\").catch(() => {\n throw new Error(\"Gemini judge needs @google/genai (npm i @google/genai).\");\n });\n const GoogleGenAI = (mod as any).GoogleGenAI;\n sharedGemini = new GoogleGenAI({\n apiKey: opts.apiKey ?? process.env.GOOGLE_API_KEY ?? process.env.GEMINI_API_KEY,\n });\n return sharedGemini;\n}\n\n/** Grade complexity with Gemini (JSON structured output). May throw. */\nexport async function judgeComplexityGemini(prompt: string, opts: RouterOptions = {}): Promise<Complexity> {\n const client = await getGemini(opts);\n const res = await client.models.generateContent({\n model: opts.judgeModel ?? GEMINI_CHEAP_MODEL,\n contents: prompt,\n config: {\n systemInstruction: JUDGE_SYSTEM,\n responseMimeType: \"application/json\",\n responseSchema: {\n type: \"object\",\n properties: { complexity: { type: \"string\", enum: [\"SIMPLE\", \"COMPLEX\"] } },\n required: [\"complexity\"],\n },\n },\n });\n return parseComplexity(res.text ?? \"\");\n}\n\n// ── 3. Classification + routing ─────────────────────────────────────────────\n\ntype Judge = (prompt: string, opts: RouterOptions) => Promise<Complexity>;\n\nasync function classifyWith(prompt: string, judge: Judge, opts: RouterOptions): Promise<Complexity> {\n return fastHeuristic(prompt) ?? judge(prompt, opts);\n}\n\n/** Heuristics first, Claude judge second. May throw. */\nexport function classifyComplexity(prompt: string, opts: RouterOptions = {}): Promise<Complexity> {\n return classifyWith(prompt, judgeComplexity, opts);\n}\n\n/** Heuristics first, Gemini judge second. May throw. */\nexport function classifyComplexityGemini(prompt: string, opts: RouterOptions = {}): Promise<Complexity> {\n return classifyWith(prompt, judgeComplexityGemini, opts);\n}\n\ninterface RouteConfig {\n cheapModel: string;\n expensiveModel: string;\n judge: Judge;\n}\n\nasync function routeWith(prompt: string, cfg: RouteConfig, opts: RouterOptions): Promise<string> {\n let model = cfg.expensiveModel;\n try {\n const complexity = await classifyWith(prompt, cfg.judge, opts);\n model = complexity === \"SIMPLE\" ? cfg.cheapModel : cfg.expensiveModel;\n } catch (err) {\n console.warn(\n `[router] classification failed — defaulting to expensive model. Reason: ${(err as Error).message}`,\n );\n model = cfg.expensiveModel;\n }\n record(model, model === cfg.cheapModel);\n return model;\n}\n\n/** Pick a Claude model for a prompt. Never throws (falls back to expensive). */\nexport function selectModel(prompt: string, opts: RouterOptions = {}): Promise<string> {\n return routeWith(prompt, { cheapModel: CHEAP_MODEL, expensiveModel: EXPENSIVE_MODEL, judge: judgeComplexity }, opts);\n}\n\n/** Pick a Gemini model for a prompt. Never throws (falls back to expensive). */\nexport function selectGeminiModel(prompt: string, opts: RouterOptions = {}): Promise<string> {\n return routeWith(\n prompt,\n { cheapModel: GEMINI_CHEAP_MODEL, expensiveModel: GEMINI_EXPENSIVE_MODEL, judge: judgeComplexityGemini },\n opts,\n );\n}\n\n// ── 4. Observability ─────────────────────────────────────────────────────────\n\nlet cheapCount = 0;\nlet expensiveCount = 0;\n\nfunction record(model: string, isCheap: boolean): void {\n if (isCheap) cheapCount++;\n else expensiveCount++;\n const total = cheapCount + expensiveCount;\n const pct = (n: number) => ((n / total) * 100).toFixed(0);\n console.log(\n `[router] → ${isCheap ? \"CHEAP\" : \"EXPENSIVE\"} (${model}) | cheap ${pct(cheapCount)}% / expensive ${pct(expensiveCount)}% (n=${total})`,\n );\n}\n\nexport interface RoutingStats {\n cheap: number;\n expensive: number;\n total: number;\n cheapPct: number;\n expensivePct: number;\n}\n\nexport function getRoutingStats(): RoutingStats {\n const total = cheapCount + expensiveCount;\n return {\n cheap: cheapCount,\n expensive: expensiveCount,\n total,\n cheapPct: total ? (cheapCount / total) * 100 : 0,\n expensivePct: total ? (expensiveCount / total) * 100 : 0,\n };\n}\n\n// ── Shared message util ──────────────────────────────────────────────────────\n\n/** Extract the most recent user turn's text — what the router classifies on. */\nexport function latestUserText(messages: Message[]): string {\n for (let i = messages.length - 1; i >= 0; i--) {\n const m = messages[i];\n if (m.role !== \"user\") continue;\n const text = m.content\n .filter((b): b is Extract<typeof b, { type: \"text\" }> => b.type === \"text\")\n .map((b) => b.text)\n .join(\"\\n\")\n .trim();\n if (text) return text;\n }\n return \"\";\n}\n","import { latestUserText, selectModel } from \"../router.ts\";\nimport type {\n ContentBlock,\n Message,\n ModelProvider,\n ModelRequest,\n ModelResponse,\n} from \"../types.ts\";\n\nexport interface AnthropicOptions {\n model?: string;\n apiKey?: string;\n maxTokens?: number;\n /**\n * When true, the model is chosen per-request by the cost router (cheap vs\n * expensive) based on query complexity, instead of using a fixed `model`.\n */\n route?: boolean;\n}\n\n/**\n * Real provider backed by the Anthropic Messages API. Requires the optional\n * `@anthropic-ai/sdk` dependency and an ANTHROPIC_API_KEY. The SDK is imported\n * lazily so the package (and the mock demo) work without it installed.\n */\nexport class AnthropicProvider implements ModelProvider {\n readonly name = \"anthropic\";\n private client: any;\n private model: string;\n private maxTokens: number;\n private apiKey?: string;\n private route: boolean;\n\n constructor(opts: AnthropicOptions = {}) {\n this.model = opts.model ?? \"claude-sonnet-4-6\";\n this.maxTokens = opts.maxTokens ?? 2048;\n this.apiKey = opts.apiKey ?? process.env.ANTHROPIC_API_KEY;\n this.route = opts.route ?? false;\n }\n\n private async ensureClient(): Promise<void> {\n if (this.client) return;\n const mod = await import(\"@anthropic-ai/sdk\").catch(() => {\n throw new Error(\n \"AnthropicProvider needs the @anthropic-ai/sdk package: run `npm i @anthropic-ai/sdk`.\",\n );\n });\n const Anthropic = (mod as any).default ?? (mod as any).Anthropic;\n this.client = new Anthropic({ apiKey: this.apiKey });\n }\n\n async generate(req: ModelRequest): Promise<ModelResponse> {\n await this.ensureClient();\n\n // Cost routing: pick cheap vs expensive per request from the latest user\n // turn. selectModel never throws — it falls back to the expensive model.\n const model = this.route\n ? await selectModel(latestUserText(req.messages), {\n client: this.client,\n apiKey: this.apiKey,\n })\n : this.model;\n\n const res = await this.client.messages.create({\n model,\n max_tokens: this.maxTokens,\n system: req.system,\n tools: req.tools.map((t) => ({\n name: t.name,\n description: t.description,\n input_schema: t.inputSchema,\n })),\n messages: req.messages.map(toApiMessage),\n });\n\n const content: ContentBlock[] = res.content.map((b: any): ContentBlock => {\n if (b.type === \"tool_use\") return { type: \"tool_use\", id: b.id, name: b.name, input: b.input };\n return { type: \"text\", text: b.type === \"text\" ? b.text : \"\" };\n });\n const stopReason = res.stop_reason === \"tool_use\" ? \"tool_use\" : \"end_turn\";\n return { content, stopReason };\n }\n}\n\nfunction toApiMessage(m: Message): any {\n return {\n role: m.role,\n content: m.content.flatMap((b): any[] => {\n if (b.type === \"text\") return [{ type: \"text\", text: b.text }];\n if (b.type === \"tool_use\") return [{ type: \"tool_use\", id: b.id, name: b.name, input: b.input }];\n if (b.type === \"tool_result\") {\n return [{ type: \"tool_result\", tool_use_id: b.toolUseId, content: b.content, is_error: b.isError }];\n }\n if (b.type === \"image\") {\n const source = b.data\n ? { type: \"base64\", media_type: b.mimeType ?? \"image/png\", data: b.data }\n : { type: \"url\", url: b.url };\n return [{ type: \"image\", source }];\n }\n return []; // video unsupported on Anthropic — drop the block\n }),\n };\n}\n","import { latestUserText, selectGeminiModel } from \"../router.ts\";\nimport type {\n ContentBlock,\n Message,\n ModelProvider,\n ModelRequest,\n ModelResponse,\n} from \"../types.ts\";\n\nexport interface GeminiOptions {\n model?: string;\n apiKey?: string;\n /**\n * When true, the model is chosen per-request by the cost router (cheap vs\n * expensive Gemini) based on query complexity, instead of a fixed `model`.\n */\n route?: boolean;\n}\n\n/**\n * Provider backed by the Google Gemini API (@google/genai). Requires the\n * optional `@google/genai` dependency and a GOOGLE_API_KEY (or GEMINI_API_KEY).\n * The SDK is imported lazily so the package works without it installed.\n */\nexport class GeminiProvider implements ModelProvider {\n readonly name = \"gemini\";\n private client: any;\n private model: string;\n private apiKey?: string;\n private route: boolean;\n\n constructor(opts: GeminiOptions = {}) {\n this.model = opts.model ?? \"gemini-2.5-flash\";\n this.apiKey = opts.apiKey ?? process.env.GOOGLE_API_KEY ?? process.env.GEMINI_API_KEY;\n this.route = opts.route ?? false;\n }\n\n private async ensureClient(): Promise<void> {\n if (this.client) return;\n const mod = await import(\"@google/genai\").catch(() => {\n throw new Error(\"GeminiProvider needs the @google/genai package: run `npm i @google/genai`.\");\n });\n const GoogleGenAI = (mod as any).GoogleGenAI;\n this.client = new GoogleGenAI({ apiKey: this.apiKey });\n }\n\n async generate(req: ModelRequest): Promise<ModelResponse> {\n await this.ensureClient();\n\n const model = this.route\n ? await selectGeminiModel(latestUserText(req.messages), {\n client: this.client,\n apiKey: this.apiKey,\n })\n : this.model;\n\n const idToName = toolUseNames(req.messages);\n\n const config: any = {};\n if (req.system) config.systemInstruction = req.system;\n if (req.tools.length) {\n config.tools = [\n {\n functionDeclarations: req.tools.map((t) => ({\n name: t.name,\n description: t.description,\n parameters: t.inputSchema,\n })),\n },\n ];\n }\n\n const res = await this.client.models.generateContent({\n model,\n contents: req.messages.map((m) => toGeminiContent(m, idToName)),\n config,\n });\n\n const content: ContentBlock[] = [];\n const text: string | undefined = res.text;\n if (text && text.trim()) content.push({ type: \"text\", text });\n\n const calls: any[] = res.functionCalls ?? [];\n for (const fc of calls) {\n content.push({ type: \"tool_use\", id: fc.id ?? \"\", name: fc.name, input: fc.args ?? {} });\n }\n if (content.length === 0) content.push({ type: \"text\", text: \"\" });\n\n return { content, stopReason: calls.length ? \"tool_use\" : \"end_turn\" };\n }\n}\n\n/** Map tool_use id -> tool name (Gemini matches function responses by name). */\nfunction toolUseNames(messages: Message[]): Map<string, string> {\n const map = new Map<string, string>();\n for (const m of messages) {\n for (const b of m.content) {\n if (b.type === \"tool_use\") map.set(b.id, b.name);\n }\n }\n return map;\n}\n\n/** Translate one of our Messages into a Gemini `Content` (role + parts). */\nfunction toGeminiContent(m: Message, idToName: Map<string, string>): any {\n const role = m.role === \"assistant\" ? \"model\" : \"user\";\n const parts = m.content.map((b): any => {\n switch (b.type) {\n case \"text\":\n return { text: b.text };\n case \"image\":\n case \"video\":\n // base64 -> inlineData; remote URL -> fileData\n return b.data\n ? { inlineData: { mimeType: b.mimeType ?? \"image/png\", data: b.data } }\n : { fileData: { mimeType: b.mimeType ?? \"image/png\", fileUri: b.url ?? \"\" } };\n case \"tool_use\":\n return { functionCall: { id: b.id, name: b.name, args: b.input } };\n case \"tool_result\":\n return {\n functionResponse: {\n id: b.toolUseId,\n name: idToName.get(b.toolUseId) ?? b.toolUseId,\n response: b.isError ? { error: b.content } : { result: b.content },\n },\n };\n }\n });\n return { role, parts };\n}\n","import type {\n ContentBlock,\n MediaBlock,\n ModelProvider,\n ModelRequest,\n ModelResponse,\n} from \"../types.ts\";\n\n// NVIDIA NIM exposes an OpenAI-compatible Chat Completions API, so this provider\n// talks to it with plain `fetch` — no extra SDK dependency. Free hosted endpoints\n// (e.g. Kimi K2.6, DeepSeek V4) are the SDK's default models via the cascade.\n\nexport const NVIDIA_BASE_URL = \"https://integrate.api.nvidia.com/v1\";\n\n// Exact IDs confirmed against GET /v1/models.\nexport const KIMI_K2_6 = \"moonshotai/kimi-k2.6\"; // 256K ctx, tools, agentic — text-only on NIM (verified)\nexport const DEEPSEEK_V4_PRO = \"deepseek-ai/deepseek-v4-pro\"; // 1M ctx, tools, text-only\nexport const DEEPSEEK_V4_FLASH = \"deepseek-ai/deepseek-v4-flash\"; // faster/cheaper, text-only\nexport const LLAMA_VISION = \"meta/llama-3.2-90b-vision-instruct\"; // free NVIDIA vision model (image), verified\n\nexport interface NvidiaOptions {\n model?: string;\n apiKey?: string;\n baseURL?: string;\n maxTokens?: number;\n temperature?: number;\n}\n\nexport class NvidiaProvider implements ModelProvider {\n readonly name = \"nvidia\";\n private model: string;\n private apiKey?: string;\n private baseURL: string;\n private maxTokens: number;\n private temperature: number;\n\n constructor(opts: NvidiaOptions = {}) {\n this.model = opts.model ?? KIMI_K2_6;\n this.apiKey = opts.apiKey ?? process.env.NVIDIA_API_KEY;\n this.baseURL = opts.baseURL ?? NVIDIA_BASE_URL;\n this.maxTokens = opts.maxTokens ?? 2048;\n this.temperature = opts.temperature ?? 0.2; // low default for deterministic agent behavior\n }\n\n async generate(req: ModelRequest): Promise<ModelResponse> {\n if (!this.apiKey) throw new Error(\"NvidiaProvider needs NVIDIA_API_KEY (nvapi-...).\");\n\n const body: Record<string, unknown> = {\n model: this.model,\n messages: toOpenAIMessages(req),\n max_tokens: this.maxTokens,\n temperature: this.temperature,\n };\n if (req.tools.length) {\n body.tools = req.tools.map((t) => ({\n type: \"function\",\n function: { name: t.name, description: t.description, parameters: t.inputSchema },\n }));\n body.tool_choice = \"auto\";\n }\n\n const res = await fetch(`${this.baseURL}/chat/completions`, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n },\n body: JSON.stringify(body),\n });\n\n if (!res.ok) {\n throw new Error(`NVIDIA ${this.model} ${res.status}: ${(await res.text()).slice(0, 300)}`);\n }\n\n const json: any = await res.json();\n const choice = json.choices?.[0];\n const msg = choice?.message ?? {};\n\n const content: ContentBlock[] = [];\n if (typeof msg.content === \"string\" && msg.content.trim()) {\n content.push({ type: \"text\", text: msg.content });\n }\n const toolCalls: any[] = msg.tool_calls ?? [];\n for (const tc of toolCalls) {\n content.push({\n type: \"tool_use\",\n id: tc.id ?? \"\",\n name: tc.function?.name ?? \"\",\n input: safeParse(tc.function?.arguments),\n });\n }\n if (content.length === 0) content.push({ type: \"text\", text: \"\" });\n\n const stopReason =\n choice?.finish_reason === \"tool_calls\" || toolCalls.length ? \"tool_use\" : \"end_turn\";\n return { content, stopReason };\n }\n}\n\nfunction safeParse(args: unknown): Record<string, unknown> {\n if (typeof args !== \"string\") return (args as Record<string, unknown>) ?? {};\n try {\n return JSON.parse(args);\n } catch {\n return {};\n }\n}\n\n/** Map our Messages into OpenAI-style chat messages (tool calls + tool results + media). */\nfunction toOpenAIMessages(req: ModelRequest): any[] {\n const out: any[] = [];\n if (req.system) out.push({ role: \"system\", content: req.system });\n\n for (const m of req.messages) {\n if (m.role === \"user\") {\n // tool_result blocks become individual {role:\"tool\"} messages\n for (const b of m.content) {\n if (b.type === \"tool_result\") {\n out.push({ role: \"tool\", tool_call_id: b.toolUseId, content: b.content });\n }\n }\n const parts = m.content.filter(\n (b) => b.type === \"text\" || b.type === \"image\" || b.type === \"video\",\n );\n if (parts.length) {\n const multimodal = parts.some((b) => b.type !== \"text\");\n out.push({\n role: \"user\",\n content: multimodal\n ? parts.map(toOpenAIPart)\n : parts.map((b) => (b as { text: string }).text).join(\"\\n\"),\n });\n }\n } else {\n // assistant\n const text = m.content\n .filter((b) => b.type === \"text\")\n .map((b) => (b as { text: string }).text)\n .join(\"\\n\");\n const toolUses = m.content.filter((b) => b.type === \"tool_use\");\n const msg: any = { role: \"assistant\", content: text || null };\n if (toolUses.length) {\n msg.tool_calls = toolUses.map((b: any) => ({\n id: b.id,\n type: \"function\",\n function: { name: b.name, arguments: JSON.stringify(b.input) },\n }));\n }\n out.push(msg);\n }\n }\n return out;\n}\n\nfunction toOpenAIPart(b: ContentBlock): any {\n if (b.type === \"text\") return { type: \"text\", text: b.text };\n const media = b as MediaBlock;\n const url =\n media.url ??\n (media.data ? `data:${media.mimeType ?? \"application/octet-stream\"};base64,${media.data}` : \"\");\n if (media.type === \"video\") return { type: \"video_url\", video_url: { url } }; // experimental\n return { type: \"image_url\", image_url: { url } };\n}\n","import type { ModelProvider, ModelRequest, ModelResponse } from \"../types.ts\";\nimport { DEEPSEEK_V4_PRO, KIMI_K2_6, LLAMA_VISION, NvidiaProvider } from \"./nvidia.ts\";\nimport { GeminiProvider } from \"./gemini.ts\";\n\n// Orchestration: try a prioritized list of (provider, model) steps, falling\n// through to the next on failure (rate limit, error, or capability mismatch).\n// Capability-aware: image requests only go to vision steps; video requests only\n// to video steps — text-only models (Kimi, DeepSeek) are skipped for those.\n\nexport interface CascadeStep {\n provider: ModelProvider;\n label: string; // e.g. \"nvidia:kimi-k2.6\"\n vision: boolean; // accepts image input?\n video?: boolean; // accepts video input? (default false)\n}\n\nexport interface CascadeOptions {\n steps: CascadeStep[];\n /** Called when a step is skipped or fails and the cascade falls through. */\n onFallback?: (info: { from: string; reason: string; needsVision: boolean }) => void;\n}\n\nexport class CascadeProvider implements ModelProvider {\n readonly name = \"cascade\";\n private steps: CascadeStep[];\n private onFallback?: CascadeOptions[\"onFallback\"];\n\n constructor(opts: CascadeOptions) {\n if (!opts.steps.length) throw new Error(\"CascadeProvider needs at least one step.\");\n this.steps = opts.steps;\n this.onFallback = opts.onFallback;\n }\n\n async generate(req: ModelRequest): Promise<ModelResponse> {\n const has = (t: \"image\" | \"video\") =>\n req.messages.some((m) => m.content.some((b) => b.type === t));\n const needsVideo = has(\"video\");\n const needsImage = has(\"image\");\n const needsVision = needsImage || needsVideo;\n\n const eligible = needsVideo\n ? this.steps.filter((s) => s.video)\n : needsImage\n ? this.steps.filter((s) => s.vision)\n : this.steps;\n\n if (!eligible.length) {\n throw new Error(\n `Cascade: request needs ${needsVideo ? \"video\" : \"image\"} input but no step supports it.`,\n );\n }\n\n let lastErr: unknown;\n for (const step of eligible) {\n try {\n return await step.provider.generate(req);\n } catch (err) {\n lastErr = err;\n this.onFallback?.({\n from: step.label,\n reason: (err as Error).message?.slice(0, 200) ?? \"unknown\",\n needsVision,\n });\n }\n }\n throw new Error(`Cascade exhausted all steps. Last error: ${(lastErr as Error)?.message}`);\n }\n}\n\nexport interface DefaultProviderOptions {\n nvidiaApiKey?: string;\n googleApiKey?: string;\n /** Override the main NVIDIA model (default Kimi K2.6). */\n mainModel?: string;\n /** Override the secondary NVIDIA model (default DeepSeek V4 Pro). */\n secondaryModel?: string;\n /** NVIDIA vision model for image requests (default llama-3.2-90b-vision). */\n visionModel?: string;\n /** Gemini model used as the final fallback option (default gemini-2.5-flash). */\n geminiModel?: string;\n onFallback?: CascadeOptions[\"onFallback\"];\n}\n\n/**\n * The SDK's recommended default: free NVIDIA endpoints first, Google as one\n * fallback option. Capability-aware — image/video requests skip the text-only\n * steps automatically.\n *\n * 1. NVIDIA Kimi K2.6 — main; agentic + tools (text)\n * 2. NVIDIA DeepSeek V4 Pro — 1M-ctx text; skipped for media\n * 3. NVIDIA Llama-3.2-90B-Vision — image requests\n * 4. Gemini 2.5 Flash — final fallback; image + video\n */\nexport function createDefaultProvider(opts: DefaultProviderOptions = {}): CascadeProvider {\n const main = opts.mainModel ?? KIMI_K2_6;\n const secondary = opts.secondaryModel ?? DEEPSEEK_V4_PRO;\n const vision = opts.visionModel ?? LLAMA_VISION;\n const gemini = opts.geminiModel ?? \"gemini-2.5-flash\";\n const nv = (model: string) => new NvidiaProvider({ model, apiKey: opts.nvidiaApiKey });\n\n return new CascadeProvider({\n onFallback:\n opts.onFallback ??\n ((info) =>\n console.warn(`[cascade] ${info.from} failed (${info.reason}); trying next`)),\n steps: [\n { provider: nv(main), label: `nvidia:${main}`, vision: false, video: false },\n { provider: nv(secondary), label: `nvidia:${secondary}`, vision: false, video: false },\n { provider: nv(vision), label: `nvidia:${vision}`, vision: true, video: false },\n {\n provider: new GeminiProvider({ model: gemini, apiKey: opts.googleApiKey }),\n label: `gemini:${gemini}`,\n vision: true,\n video: true,\n },\n ],\n });\n}\n","// Public API for Torus.\n\nexport * from \"./types.ts\";\nexport { tool, createSdkMcpServer, ToolRegistry, type RegisteredTool } from \"./tools.ts\";\nexport { PermissionEngine, matchesAllow, type PermissionConfig } from \"./permissions.ts\";\nexport { runLoop, type LoopOptions, type LoopResult } from \"./loop.ts\";\nexport { runPipeline, type PipelineOptions } from \"./pipeline.ts\";\nexport { builtinTools, readFileTool, writeFileTool, listDirTool } from \"./builtins.ts\";\nexport { loadStages, parseContract, type StageContract, type StageInput } from \"./subagents.ts\";\nexport { loadStageContext, type LoadedContext } from \"./context.ts\";\nexport { MockProvider, type MockOptions } from \"./providers/mock.ts\";\nexport { AnthropicProvider, type AnthropicOptions } from \"./providers/anthropic.ts\";\nexport { GeminiProvider, type GeminiOptions } from \"./providers/gemini.ts\";\nexport {\n NvidiaProvider,\n type NvidiaOptions,\n NVIDIA_BASE_URL,\n KIMI_K2_6,\n DEEPSEEK_V4_PRO,\n DEEPSEEK_V4_FLASH,\n LLAMA_VISION,\n} from \"./providers/nvidia.ts\";\nexport {\n CascadeProvider,\n createDefaultProvider,\n type CascadeStep,\n type CascadeOptions,\n type DefaultProviderOptions,\n} from \"./providers/cascade.ts\";\nexport {\n // Anthropic (Claude) routing\n CHEAP_MODEL,\n EXPENSIVE_MODEL,\n selectModel,\n classifyComplexity,\n judgeComplexity,\n // Gemini routing — same mechanism, Gemini models\n GEMINI_CHEAP_MODEL,\n GEMINI_EXPENSIVE_MODEL,\n selectGeminiModel,\n classifyComplexityGemini,\n judgeComplexityGemini,\n // Shared\n fastHeuristic,\n getRoutingStats,\n latestUserText,\n type Complexity,\n type RouterOptions,\n type RoutingStats,\n} from \"./router.ts\";\n\nimport { builtinTools } from \"./builtins.ts\";\nimport { createDefaultProvider } from \"./providers/cascade.ts\";\nimport { runLoop } from \"./loop.ts\";\nimport { PermissionEngine, type PermissionConfig } from \"./permissions.ts\";\nimport { ToolRegistry } from \"./tools.ts\";\nimport type { AgentEvent, ContentBlock, Message, ModelProvider, SdkMcpServer } from \"./types.ts\";\n\nexport interface QueryOptions {\n /** Defaults to the NVIDIA-first cascade (Kimi K2.6 → DeepSeek V4 → Gemini). */\n provider?: ModelProvider;\n system?: string;\n mcpServers?: SdkMcpServer[];\n includeBuiltins?: boolean; // default true\n permissions?: PermissionConfig;\n workspaceDir?: string;\n maxTurns?: number;\n}\n\n/**\n * Single-shot agent run (no pipeline). Mirrors the Claude Agent SDK's streaming\n * `query()`: yields events as they happen and a final `result` event. The prompt\n * may be a string or an array of content blocks (e.g. text + image for vision).\n *\n * for await (const ev of query(\"Summarize X\", { mcpServers: [srv] })) { ... }\n * for await (const ev of query([{ type: \"text\", text: \"What's this?\" },\n * { type: \"image\", url: \"https://...\" }])) { ... }\n */\nexport async function* query(\n prompt: string | ContentBlock[],\n options: QueryOptions = {},\n): AsyncGenerator<AgentEvent> {\n const registry = new ToolRegistry();\n if (options.includeBuiltins ?? true) registry.addBuiltins(builtinTools);\n for (const s of options.mcpServers ?? []) registry.addServer(s);\n\n const content: ContentBlock[] =\n typeof prompt === \"string\" ? [{ type: \"text\", text: prompt }] : prompt;\n const messages: Message[] = [{ role: \"user\", content }];\n const result = yield* runLoop({\n provider: options.provider ?? createDefaultProvider(),\n registry,\n permissions: new PermissionEngine(options.permissions ?? {}),\n system: options.system ?? \"You are a helpful agent.\",\n messages,\n toolContext: { workspaceDir: options.workspaceDir ?? process.cwd() },\n maxTurns: options.maxTurns,\n });\n yield { type: \"result\", finalText: result.finalText, turns: result.turns };\n}\n"],"mappings":"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","names":["mkdir","writeFile","join","readFile","readFile","readFile","readdir","join","join","mkdir","writeFile","estimateTokens"]}
@@ -0,0 +1,58 @@
1
+ # Model Integration Policy
2
+
3
+ The internal rule for what models Torus runs by default and how a new one earns a
4
+ place in the cascade. The weekly watcher (`scripts/model-watch.ts`,
5
+ [.github/workflows/model-watch.yml](../.github/workflows/model-watch.yml)) proposes
6
+ changes; a human approves them against this policy via PR.
7
+
8
+ ## Default order (the cascade)
9
+
10
+ 1. **NVIDIA free endpoints first.** Free hosted NIM models carry the load.
11
+ - `moonshotai/kimi-k2.6` — **main**: agentic + multimodal (image/video).
12
+ - `deepseek-ai/deepseek-v4-pro` — **secondary**: 1M context, text-only.
13
+ 2. **Google as one fallback option** — `gemini-2.5-flash` (multimodal), used when
14
+ the NVIDIA steps fail or are unavailable.
15
+
16
+ Capability-aware: image/video requests skip text-only models automatically.
17
+
18
+ ## Hard criteria (must meet ALL to enter the cascade)
19
+
20
+ A candidate model is only promoted from `candidates` to `cascade` if it meets every one:
21
+
22
+ - [ ] **Reachable** — a working hosted endpoint we already have an adapter for
23
+ (NVIDIA NIM `/v1/chat/completions`, Anthropic, or Gemini).
24
+ - [ ] **Free or already-paid** — NVIDIA NIM free tier, or a provider whose key we
25
+ already hold. Cost must not regress the "free by default" promise.
26
+ - [ ] **Tool calling** — supports OpenAI-style function/tool calls (the agent loop
27
+ depends on it). Pure-completion models are not eligible for the agentic path.
28
+ - [ ] **Context ≥ 128K tokens.**
29
+ - [ ] **Stable** — not preview/deprecated for a load-bearing slot. Preview models
30
+ may sit in `candidates` only.
31
+
32
+ ## Ranking (when multiple qualify)
33
+
34
+ Order by, in priority: (1) free over paid, (2) capability fit for the slot
35
+ (vision slot needs vision; agentic slot rewards strong tool use + long context),
36
+ (3) larger context, (4) lower latency / smaller when quality is comparable.
37
+
38
+ ## Slots
39
+
40
+ | Slot | Needs | Current |
41
+ |------|-------|---------|
42
+ | `main` | tools + vision + strong agentic | `moonshotai/kimi-k2.6` |
43
+ | `secondary` | tools + long context (text) | `deepseek-ai/deepseek-v4-pro` |
44
+ | `fallback` | tools + vision, different provider (resilience) | `gemini-2.5-flash` |
45
+
46
+ ## Promotion process
47
+
48
+ 1. Weekly job pulls NVIDIA `GET /v1/models`, marks `available` on known models,
49
+ and appends any new watch-listed family members to `candidates`.
50
+ 2. If `models/registry.json` changes, the job opens a PR.
51
+ 3. A human checks each candidate against the hard criteria above. To integrate:
52
+ - move it from `candidates` to `models` with a `role`,
53
+ - add its ID to `cascade` (and, if it changes a default slot, update the
54
+ constants in `src/providers/nvidia.ts` / `createDefaultProvider`),
55
+ - confirm tests pass, then merge.
56
+
57
+ Removals: if a model goes `available: false` for two consecutive weeks, drop it
58
+ from `cascade` and pick the best remaining candidate for its slot.
@@ -0,0 +1,63 @@
1
+ {
2
+ "updatedAt": "2026-06-14",
3
+ "note": "Source of truth for the default model cascade. Maintained by scripts/model-watch.ts (weekly) + human review per models/POLICY.md.",
4
+ "providers": {
5
+ "nvidia": { "baseUrl": "https://integrate.api.nvidia.com/v1", "free": true },
6
+ "google": { "env": "GOOGLE_API_KEY", "free": false }
7
+ },
8
+ "cascade": [
9
+ "moonshotai/kimi-k2.6",
10
+ "deepseek-ai/deepseek-v4-pro",
11
+ "meta/llama-3.2-90b-vision-instruct",
12
+ "gemini-2.5-flash"
13
+ ],
14
+ "models": [
15
+ {
16
+ "id": "moonshotai/kimi-k2.6",
17
+ "provider": "nvidia",
18
+ "role": "main",
19
+ "free": true,
20
+ "available": true,
21
+ "context": 262144,
22
+ "note": "Agentic + tools. Vision claimed in docs but text-only on this NIM endpoint (verified 2026-06-14).",
23
+ "capabilities": { "tools": true, "vision": false, "video": false, "reasoning": false }
24
+ },
25
+ {
26
+ "id": "meta/llama-3.2-90b-vision-instruct",
27
+ "provider": "nvidia",
28
+ "role": "vision",
29
+ "free": true,
30
+ "available": true,
31
+ "context": 128000,
32
+ "capabilities": { "tools": false, "vision": true, "video": false, "reasoning": false }
33
+ },
34
+ {
35
+ "id": "deepseek-ai/deepseek-v4-pro",
36
+ "provider": "nvidia",
37
+ "role": "secondary",
38
+ "free": true,
39
+ "available": true,
40
+ "context": 1000000,
41
+ "capabilities": { "tools": true, "vision": false, "video": false, "reasoning": true }
42
+ },
43
+ {
44
+ "id": "deepseek-ai/deepseek-v4-flash",
45
+ "provider": "nvidia",
46
+ "role": "candidate",
47
+ "free": true,
48
+ "available": true,
49
+ "context": 1000000,
50
+ "capabilities": { "tools": true, "vision": false, "video": false, "reasoning": true }
51
+ },
52
+ {
53
+ "id": "gemini-2.5-flash",
54
+ "provider": "google",
55
+ "role": "fallback",
56
+ "free": false,
57
+ "available": true,
58
+ "context": 1000000,
59
+ "capabilities": { "tools": true, "vision": true, "video": true, "reasoning": true }
60
+ }
61
+ ],
62
+ "candidates": []
63
+ }
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "torus-ai",
3
- "version": "0.1.0",
4
- "description": "Torus — a minimal, ICM-structured Agent SDK: agent loop, tools, in-process MCP, markdown-contract subagents, layered context, permissions, and cost routing across Claude and Gemini.",
3
+ "version": "0.2.1",
4
+ "description": "Torus — a minimal, ICM-structured Agent SDK: agent loop, tools, in-process MCP, markdown-contract subagents, multimodal input, and a free-first model cascade (NVIDIA Kimi K2.6 / DeepSeek V4 → Gemini → Claude) with cost routing.",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "author": "aenfr",
8
8
  "repository": { "type": "git", "url": "git+https://github.com/aenfr/torus-ai.git" },
9
9
  "homepage": "https://github.com/aenfr/torus-ai#readme",
10
10
  "bugs": { "url": "https://github.com/aenfr/torus-ai/issues" },
11
- "keywords": ["agent", "sdk", "llm", "mcp", "anthropic", "claude", "gemini", "google", "icm", "pipeline", "router"],
11
+ "keywords": ["agent", "sdk", "llm", "mcp", "nvidia", "nim", "kimi", "deepseek", "anthropic", "claude", "gemini", "google", "multimodal", "icm", "pipeline", "router", "cascade"],
12
12
  "engines": { "node": ">=22.6" },
13
13
  "main": "./dist/index.js",
14
14
  "module": "./dist/index.js",
@@ -19,10 +19,11 @@
19
19
  "import": "./dist/index.js"
20
20
  }
21
21
  },
22
- "files": ["dist", "src", "AGENT.md", "CONTEXT.md", "LICENSE", "README.md"],
22
+ "files": ["dist", "src", "models", "AGENT.md", "CONTEXT.md", "LICENSE", "README.md"],
23
23
  "publishConfig": { "access": "public" },
24
24
  "scripts": {
25
25
  "demo": "node examples/blog-pipeline/run.ts",
26
+ "model-watch": "node scripts/model-watch.ts",
26
27
  "typecheck": "tsc --noEmit",
27
28
  "build": "tsup",
28
29
  "prepublishOnly": "npm run typecheck && npm run build"
package/src/index.ts CHANGED
@@ -11,6 +11,22 @@ export { loadStageContext, type LoadedContext } from "./context.ts";
11
11
  export { MockProvider, type MockOptions } from "./providers/mock.ts";
12
12
  export { AnthropicProvider, type AnthropicOptions } from "./providers/anthropic.ts";
13
13
  export { GeminiProvider, type GeminiOptions } from "./providers/gemini.ts";
14
+ export {
15
+ NvidiaProvider,
16
+ type NvidiaOptions,
17
+ NVIDIA_BASE_URL,
18
+ KIMI_K2_6,
19
+ DEEPSEEK_V4_PRO,
20
+ DEEPSEEK_V4_FLASH,
21
+ LLAMA_VISION,
22
+ } from "./providers/nvidia.ts";
23
+ export {
24
+ CascadeProvider,
25
+ createDefaultProvider,
26
+ type CascadeStep,
27
+ type CascadeOptions,
28
+ type DefaultProviderOptions,
29
+ } from "./providers/cascade.ts";
14
30
  export {
15
31
  // Anthropic (Claude) routing
16
32
  CHEAP_MODEL,
@@ -34,13 +50,15 @@ export {
34
50
  } from "./router.ts";
35
51
 
36
52
  import { builtinTools } from "./builtins.ts";
53
+ import { createDefaultProvider } from "./providers/cascade.ts";
37
54
  import { runLoop } from "./loop.ts";
38
55
  import { PermissionEngine, type PermissionConfig } from "./permissions.ts";
39
56
  import { ToolRegistry } from "./tools.ts";
40
- import type { AgentEvent, Message, ModelProvider, SdkMcpServer } from "./types.ts";
57
+ import type { AgentEvent, ContentBlock, Message, ModelProvider, SdkMcpServer } from "./types.ts";
41
58
 
42
59
  export interface QueryOptions {
43
- provider: ModelProvider;
60
+ /** Defaults to the NVIDIA-first cascade (Kimi K2.6 → DeepSeek V4 → Gemini). */
61
+ provider?: ModelProvider;
44
62
  system?: string;
45
63
  mcpServers?: SdkMcpServer[];
46
64
  includeBuiltins?: boolean; // default true
@@ -51,21 +69,26 @@ export interface QueryOptions {
51
69
 
52
70
  /**
53
71
  * Single-shot agent run (no pipeline). Mirrors the Claude Agent SDK's streaming
54
- * `query()`: yields events as they happen and a final `result` event.
72
+ * `query()`: yields events as they happen and a final `result` event. The prompt
73
+ * may be a string or an array of content blocks (e.g. text + image for vision).
55
74
  *
56
- * for await (const ev of query("Summarize X", { provider, mcpServers: [srv] })) { ... }
75
+ * for await (const ev of query("Summarize X", { mcpServers: [srv] })) { ... }
76
+ * for await (const ev of query([{ type: "text", text: "What's this?" },
77
+ * { type: "image", url: "https://..." }])) { ... }
57
78
  */
58
79
  export async function* query(
59
- prompt: string,
60
- options: QueryOptions,
80
+ prompt: string | ContentBlock[],
81
+ options: QueryOptions = {},
61
82
  ): AsyncGenerator<AgentEvent> {
62
83
  const registry = new ToolRegistry();
63
84
  if (options.includeBuiltins ?? true) registry.addBuiltins(builtinTools);
64
85
  for (const s of options.mcpServers ?? []) registry.addServer(s);
65
86
 
66
- const messages: Message[] = [{ role: "user", content: [{ type: "text", text: prompt }] }];
87
+ const content: ContentBlock[] =
88
+ typeof prompt === "string" ? [{ type: "text", text: prompt }] : prompt;
89
+ const messages: Message[] = [{ role: "user", content }];
67
90
  const result = yield* runLoop({
68
- provider: options.provider,
91
+ provider: options.provider ?? createDefaultProvider(),
69
92
  registry,
70
93
  permissions: new PermissionEngine(options.permissions ?? {}),
71
94
  system: options.system ?? "You are a helpful agent.",