diff --git a/pi/.pi/agent/agents/explorer.md b/pi/.pi/agent/agents/explorer.md index 2398711..91808cc 100644 --- a/pi/.pi/agent/agents/explorer.md +++ b/pi/.pi/agent/agents/explorer.md @@ -2,7 +2,7 @@ name: explorer description: Comprehensive codebase and knowledge-base explorer. Maps architecture, traces dependencies, synthesizes cross-cutting context with full code snippets and rationale. Use for deep refactoring, architectural decisions, or understanding complex subsystems. Do NOT use when the user has already provided explicit file paths or when a direct file read would suffice — only invoke for open-ended exploration where the relevant files are unknown. tools: read, bash, write, mcp:qmd, mcp:opty -model: opencode/big-pickle +model: opencode-go/mimo-v2-pro defaultProgress: true --- diff --git a/pi/.pi/agent/agents/planner.md b/pi/.pi/agent/agents/planner.md index 442ffaa..36031ca 100644 --- a/pi/.pi/agent/agents/planner.md +++ b/pi/.pi/agent/agents/planner.md @@ -2,7 +2,7 @@ name: planner description: Creates detailed implementation plans from scout context and task requirements. Writes the plan to a file for the next agent in the chain. tools: read, write, grep, find, ls -model: opencode/qwen3.6-plus-free +model: anthropic/claude-opus-4-6 output: plan.md defaultReads: scout.md --- diff --git a/pi/.pi/agent/agents/scout.md b/pi/.pi/agent/agents/scout.md index e671deb..f90591e 100644 --- a/pi/.pi/agent/agents/scout.md +++ b/pi/.pi/agent/agents/scout.md @@ -1,7 +1,8 @@ --- name: scout description: Fast codebase recon. Finds relevant files, types, and patterns, then writes structured context for the next agent in the chain. -tools: read, write, grep, find, ls, bash, mcp:opty, mcp:qmd +tools: read, write, grep, find, ls, bash +skills: opty, qmd model: opencode-go/mimo-v2-pro output: scout.md --- @@ -21,17 +22,17 @@ When your task contains `[Write to: path]`, write your COMPLETE report to that e Without `[Write to:]`, output your full report as text. ## Tools -- **opty** — semantic/HDC code search: use `opty_opty_query` to find functions, types, imports by meaning; `opty_opty_ast` for a structural overview of a file or the project -- **qmd** — knowledge base search: use `qmd_query` to find docs, notes, or prior context by keyword or vector; `qmd_get` / `qmd_multi_get` to retrieve full documents -- **grep/find/bash** — fallback for exact patterns, file discovery, or anything the semantic tools miss +- **opty** — semantic/HDC code search via CLI: `opty query "description"` to find functions/types/imports by meaning; output includes file + line number +- **qmd** — knowledge base search via CLI: `qmd query $'lex: X\nvec: Y'` to find docs/notes by keyword or vector; `qmd get ` / `qmd multi-get ` to retrieve full documents +- **grep/find/bash** — for exact patterns, file discovery, or anything the semantic tools miss - **read** — read specific file sections once you know where to look ## Strategy -1. Use `opty_opty_query` to semantically locate relevant functions/types (fast, no file reading needed) -2. Use `qmd_query` to check if there's relevant documentation or prior context in the knowledge base +1. Use `opty query "..."` to semantically locate relevant functions/types (fast, no file reading needed) +2. Use `qmd query "..."` to check if there's relevant documentation or prior context in the knowledge base 3. grep/find for exact patterns or when semantic search isn't precise enough 4. Read key sections (not entire files — target the relevant functions/types) -5. Use `opty_opty_ast` on key files to quickly understand their structure without reading everything +5. For file structure overview, use grep for exports/types or read the top ~50 lines of a file 6. Identify types, interfaces, key functions 7. Note dependencies between files 8. Flag anything surprising or risky diff --git a/pi/.pi/agent/extensions/claude-agent-sdk/index.ts b/pi/.pi/agent/extensions/claude-agent-sdk/index.ts new file mode 100644 index 0000000..4a511c7 --- /dev/null +++ b/pi/.pi/agent/extensions/claude-agent-sdk/index.ts @@ -0,0 +1,1186 @@ +/** + * Claude Agent SDK Provider Extension + * + * Routes pi requests through the Claude Code CLI via @anthropic-ai/claude-agent-sdk. + * + * ARCHITECTURE (improved over npm:claude-agent-sdk-pi): + * + * The original extension flattened the entire conversation into a single user + * message on every call, labelling previous tool calls as "non-executable". + * This caused the model to believe its prior tool calls had never run, resulting + * in infinite retry loops. + * + * This version uses proper session persistence + resume: + * - First turn: starts a Claude Code session (persistSession=true, custom UUID) + * - Continuation turns: resumes that session and injects tool results as proper + * SDKUserMessage items with parent_tool_use_id set — giving Claude Code the + * native multi-turn context it needs. + * + * Fallback: if a session can't be resumed (e.g. the file wasn't flushed before + * we closed the query), the full conversation is re-sent as a structured text + * block with clear labels so the model understands what was already done. + * + * BUGS FIXED: + * 1. "Historical tool call (non-executable)" → model thought calls never ran. + * Fixed label + session-resume approach eliminates the confusion entirely. + * 2. Edit tool args: SDK sends {old_string, new_string} but pi's Edit tool + * requires {edits: [{oldText, newText}]}. Now properly wrapped. + */ + +import { + calculateCost, + createAssistantMessageEventStream, + getModels, + type AssistantMessage, + type AssistantMessageEventStream, + type Context, + type ImageContent, + type Model, + type SimpleStreamOptions, + type TextContent, + type Tool, + type ToolResultMessage, + type UserMessage, +} from "@mariozechner/pi-ai"; +import type { ExtensionAPI } from "@mariozechner/pi-coding-agent"; +import { + createSdkMcpServer, + query, + type SDKMessage, + type SDKUserMessage, + type SettingSource, +} from "@anthropic-ai/claude-agent-sdk"; +import type { + Base64ImageSource, + ContentBlockParam, + MessageParam, +} from "@anthropic-ai/sdk/resources"; +import { pascalCase } from "change-case"; +import { existsSync, readFileSync } from "fs"; +import { randomUUID } from "crypto"; +import { homedir } from "os"; +import { dirname, join, relative, resolve } from "path"; + +// ============================================================================= +// Constants +// ============================================================================= + +const PROVIDER_ID = "claude-agent-sdk"; + +const SDK_TO_PI_TOOL_NAME: Record = { + read: "read", + write: "write", + edit: "edit", + bash: "bash", + grep: "grep", + glob: "find", +}; + +const PI_TO_SDK_TOOL_NAME: Record = { + read: "Read", + write: "Write", + edit: "Edit", + bash: "Bash", + grep: "Grep", + find: "Glob", + glob: "Glob", +}; + +const DEFAULT_TOOLS = ["Read", "Write", "Edit", "Bash", "Grep", "Glob"]; +const BUILTIN_TOOL_NAMES = new Set(Object.keys(PI_TO_SDK_TOOL_NAME)); +const TOOL_EXECUTION_DENIED_MESSAGE = "Tool execution is unavailable in this environment."; +const MCP_SERVER_NAME = "custom-tools"; +const MCP_TOOL_PREFIX = `mcp__${MCP_SERVER_NAME}__`; + +const SKILLS_ALIAS_GLOBAL = "~/.claude/skills"; +const SKILLS_ALIAS_PROJECT = ".claude/skills"; +const GLOBAL_SKILLS_ROOT = join(homedir(), ".pi", "agent", "skills"); +const PROJECT_SKILLS_ROOT = join(process.cwd(), ".pi", "skills"); +const GLOBAL_SETTINGS_PATH = join(homedir(), ".pi", "agent", "settings.json"); +const PROJECT_SETTINGS_PATH = join(process.cwd(), ".pi", "settings.json"); +const GLOBAL_AGENTS_PATH = join(homedir(), ".pi", "agent", "AGENTS.md"); + +// ============================================================================= +// Session Registry +// Tracks active Claude Code sessions keyed by pi conversation identity. +// Enables proper session resumption instead of re-encoding history as text. +// ============================================================================= + +interface SessionState { + /** The Claude Code session UUID (used for resume). */ + claudeCodeSessionId: string; + /** + * How many messages from context.messages we have already sent to Claude + * Code in prior calls. On the next call we send messages[sentMsgCount..]. + */ + sentMsgCount: number; + /** Model used when the session was created (for resume validation). */ + modelId: string; +} + +const sessionRegistry = new Map(); + +/** + * Derives a stable key for the current pi conversation. + * Uses options.sessionId if pi provides one; otherwise fingerprints the first + * user message (first 200 chars of content). + */ +function getConversationKey(context: Context, options?: SimpleStreamOptions): string { + const sid = (options as { sessionId?: string } | undefined)?.sessionId; + if (sid) return `sid:${sid}`; + + const firstUser = context.messages.find((m) => m.role === "user"); + if (!firstUser) return `empty:${Date.now()}`; + + const content = + typeof (firstUser as UserMessage).content === "string" + ? ((firstUser as UserMessage).content as string) + : JSON.stringify((firstUser as UserMessage).content); + + return `fp:${content.slice(0, 200)}`; +} + +// ============================================================================= +// Models +// ============================================================================= + +const MODELS = getModels("anthropic").map((model) => ({ + id: model.id, + name: model.name, + reasoning: model.reasoning, + input: model.input, + cost: model.cost, + contextWindow: model.contextWindow, + maxTokens: model.maxTokens, +})); + +// ============================================================================= +// Tool Name Mapping +// ============================================================================= + +function mapPiToolNameToSdk(name?: string, customToolNameToSdk?: Map): string { + if (!name) return ""; + const normalized = name.toLowerCase(); + if (customToolNameToSdk) { + const mapped = customToolNameToSdk.get(name) ?? customToolNameToSdk.get(normalized); + if (mapped) return mapped; + } + if (PI_TO_SDK_TOOL_NAME[normalized]) return PI_TO_SDK_TOOL_NAME[normalized]; + return pascalCase(name); +} + +function mapToolName(name: string, customToolNameToPi?: Map): string { + const normalized = name.toLowerCase(); + const builtin = SDK_TO_PI_TOOL_NAME[normalized]; + if (builtin) return builtin; + if (customToolNameToPi) { + const mapped = customToolNameToPi.get(name) ?? customToolNameToPi.get(normalized); + if (mapped) return mapped; + } + if (normalized.startsWith(MCP_TOOL_PREFIX)) { + return name.slice(MCP_TOOL_PREFIX.length); + } + return name; +} + +// ============================================================================= +// Tool Argument Mapping +// BUG FIX: Edit previously mapped old_string→oldText as a top-level key, +// but pi's Edit tool requires edits:[{oldText,newText}]. Fixed here. +// ============================================================================= + +function rewriteSkillAliasPath(pathValue: unknown): unknown { + if (typeof pathValue !== "string") return pathValue; + if (pathValue.startsWith(SKILLS_ALIAS_GLOBAL)) { + return pathValue.replace(SKILLS_ALIAS_GLOBAL, "~/.pi/agent/skills"); + } + if (pathValue.startsWith(`./${SKILLS_ALIAS_PROJECT}`)) { + return pathValue.replace(`./${SKILLS_ALIAS_PROJECT}`, PROJECT_SKILLS_ROOT); + } + if (pathValue.startsWith(SKILLS_ALIAS_PROJECT)) { + return pathValue.replace(SKILLS_ALIAS_PROJECT, PROJECT_SKILLS_ROOT); + } + const projectAliasAbs = join(process.cwd(), SKILLS_ALIAS_PROJECT); + if (pathValue.startsWith(projectAliasAbs)) { + return pathValue.replace(projectAliasAbs, PROJECT_SKILLS_ROOT); + } + return pathValue; +} + +function mapToolArgs( + toolName: string, + args: Record | undefined, + allowSkillAliasRewrite = true, +): Record { + const normalized = toolName.toLowerCase(); + const input = args ?? {}; + const resolvePath = (value: unknown) => (allowSkillAliasRewrite ? rewriteSkillAliasPath(value) : value); + + switch (normalized) { + case "read": + return { + path: resolvePath(input.file_path ?? input.path), + offset: input.offset, + limit: input.limit, + }; + + case "write": + return { + path: resolvePath(input.file_path ?? input.path), + content: input.content, + }; + + case "edit": { + // BUG FIX: SDK sends {old_string, new_string} at the top level, + // but pi's Edit tool expects {edits: [{oldText, newText}]}. + const oldText = input.old_string ?? input.oldText ?? input.old_text; + const newText = input.new_string ?? input.newText ?? input.new_text; + if (oldText !== undefined || newText !== undefined) { + return { + path: resolvePath(input.file_path ?? input.path), + edits: [{ oldText: String(oldText ?? ""), newText: String(newText ?? "") }], + }; + } + // Already in edits-array format (e.g. from multi-edit calls) + return { + path: resolvePath(input.file_path ?? input.path), + edits: input.edits ?? [], + }; + } + + case "bash": + return { + command: input.command, + timeout: input.timeout, + }; + + case "grep": + return { + pattern: input.pattern, + path: resolvePath(input.path), + glob: input.glob, + limit: input.head_limit ?? input.limit, + }; + + case "find": + return { + pattern: input.pattern, + path: resolvePath(input.path), + }; + + default: + return input; + } +} + +// ============================================================================= +// SDK Tool Resolution +// ============================================================================= + +function resolveSdkTools(context: Context): { + sdkTools: string[]; + customTools: Tool[]; + customToolNameToSdk: Map; + customToolNameToPi: Map; +} { + if (!context.tools) { + return { + sdkTools: [...DEFAULT_TOOLS], + customTools: [], + customToolNameToSdk: new Map(), + customToolNameToPi: new Map(), + }; + } + + const sdkTools = new Set(); + const customTools: Tool[] = []; + const customToolNameToSdk = new Map(); + const customToolNameToPi = new Map(); + + for (const tool of context.tools) { + const normalized = tool.name.toLowerCase(); + if (BUILTIN_TOOL_NAMES.has(normalized)) { + const sdkName = PI_TO_SDK_TOOL_NAME[normalized]; + if (sdkName) sdkTools.add(sdkName); + continue; + } + const sdkName = `${MCP_TOOL_PREFIX}${tool.name}`; + customTools.push(tool); + customToolNameToSdk.set(tool.name, sdkName); + customToolNameToSdk.set(normalized, sdkName); + customToolNameToPi.set(sdkName, tool.name); + customToolNameToPi.set(sdkName.toLowerCase(), tool.name); + } + + return { sdkTools: Array.from(sdkTools), customTools, customToolNameToSdk, customToolNameToPi }; +} + +function buildCustomToolServers( + customTools: Tool[], +): Record> | undefined { + if (!customTools.length) return undefined; + + const mcpTools = customTools.map((tool) => ({ + name: tool.name, + description: tool.description, + inputSchema: tool.parameters as unknown, + handler: async () => ({ + content: [{ type: "text", text: TOOL_EXECUTION_DENIED_MESSAGE }], + isError: true, + }), + })); + + const server = createSdkMcpServer({ + name: MCP_SERVER_NAME, + version: "1.0.0", + tools: mcpTools, + }); + + return { [MCP_SERVER_NAME]: server }; +} + +// ============================================================================= +// Prompt Stream Builders +// ============================================================================= + +/** + * Converts pi UserMessage content to Anthropic ContentBlockParam[]. + */ +function userContentToBlocks( + content: string | (TextContent | ImageContent)[], +): ContentBlockParam[] { + if (typeof content === "string") { + return [{ type: "text", text: content }]; + } + return content.map((block): ContentBlockParam => { + if (block.type === "text") return { type: "text", text: block.text }; + // image + const img = block as ImageContent; + return { + type: "image", + source: { + type: "base64", + media_type: img.mimeType as Base64ImageSource["media_type"], + data: img.data, + }, + }; + }); +} + +/** + * Converts pi ToolResultMessage content to a plain string for the SDK. + */ +function toolResultContentToText(content: (TextContent | ImageContent)[]): string { + return content + .filter((c) => c.type === "text") + .map((c) => (c as TextContent).text) + .join("\n"); +} + +/** + * BUILD STRATEGY A — Session Resume + * + * Yields only the new pi messages (since sentMsgCount) as proper SDKUserMessages: + * - user messages → SDKUserMessage with parent_tool_use_id: null + * - toolResult → SDKUserMessage with parent_tool_use_id: toolCallId + * - assistant msgs → skipped (Claude Code generates these) + * + * This gives Claude Code native multi-turn context with real tool results, + * avoiding the text-encoding confusion entirely. + */ +function buildResumeStream( + context: Context, + sentMsgCount: number, + claudeCodeSessionId: string, +): AsyncIterable { + const newMessages = context.messages.slice(sentMsgCount); + + async function* generator() { + for (const msg of newMessages) { + if (msg.role === "assistant") { + // Claude Code generated these; skip — the session already has them. + continue; + } + + if (msg.role === "user") { + const umsg = msg as UserMessage; + yield { + type: "user" as const, + message: { + role: "user" as const, + content: userContentToBlocks(umsg.content), + } as MessageParam, + parent_tool_use_id: null, + session_id: claudeCodeSessionId, + } satisfies SDKUserMessage; + continue; + } + + if (msg.role === "toolResult") { + const result = msg as ToolResultMessage; + const text = toolResultContentToText(result.content); + yield { + type: "user" as const, + message: { + role: "user" as const, + content: [ + { + type: "tool_result" as const, + tool_use_id: result.toolCallId, + content: text, + ...(result.isError ? { is_error: true } : {}), + }, + ], + } as MessageParam, + parent_tool_use_id: result.toolCallId, + session_id: claudeCodeSessionId, + } satisfies SDKUserMessage; + continue; + } + } + } + + return generator(); +} + +/** + * BUILD STRATEGY B — Text Fallback (fresh session or resume failure) + * + * Encodes the full conversation history as structured text inside a single + * user message, then starts a fresh Claude Code session. + * + * BUG FIX: previously used "Historical tool call (non-executable): …" which + * caused the model to believe tool calls had never executed. Now uses + * "[TOOL CALL - EXECUTED]" with clear success indicators. + */ +function buildFallbackTextBlocks( + context: Context, + customToolNameToSdk: Map | undefined, +): ContentBlockParam[] { + const blocks: ContentBlockParam[] = []; + + const pushText = (text: string) => { + blocks.push({ type: "text", text }); + }; + + const pushImage = (image: ImageContent) => { + blocks.push({ + type: "image", + source: { + type: "base64", + media_type: image.mimeType as Base64ImageSource["media_type"], + data: image.data, + }, + }); + }; + + const appendContent = ( + content: string | Array<{ type: string; text?: string; data?: string; mimeType?: string }>, + ): boolean => { + if (typeof content === "string") { + if (content.length > 0) pushText(content); + return content.trim().length > 0; + } + if (!Array.isArray(content)) return false; + let hasText = false; + for (const block of content) { + if (block.type === "text") { + const text = block.text ?? ""; + if (text.trim().length > 0) hasText = true; + pushText(text); + } else if (block.type === "image") { + pushImage(block as ImageContent); + } else { + pushText(`[${block.type}]`); + } + } + return hasText; + }; + + const messages = context.messages; + const hasHistory = messages.length > 0; + + if (hasHistory && messages.length > 1) { + // Prefix only when there is actual history (not just the current user turn) + pushText("[CONVERSATION HISTORY - all tool calls below were executed]\n"); + } + + for (const message of messages) { + if (message.role === "user") { + pushText(`\nUSER:\n`); + const hasText = appendContent((message as UserMessage).content); + if (!hasText) pushText("(see attached image)"); + continue; + } + + if (message.role === "assistant") { + pushText(`\nASSISTANT:\n`); + const assistantContent = ( + message as { + content: Array<{ + type: string; + text?: string; + thinking?: string; + name?: string; + arguments?: Record; + }>; + } + ).content; + if (Array.isArray(assistantContent)) { + for (const block of assistantContent) { + if (block.type === "text") pushText(block.text ?? ""); + else if (block.type === "thinking") pushText(block.thinking ?? ""); + else if (block.type === "toolCall") { + const toolName = mapPiToolNameToSdk(block.name, customToolNameToSdk); + const args = block.arguments ? JSON.stringify(block.arguments, null, 2) : "{}"; + // BUG FIX: was "Historical tool call (non-executable)" — model interpreted + // this as the call never having run, causing retry loops. + pushText(`\n[TOOL CALL - EXECUTED]: ${toolName}\n${args}\n`); + } + } + } + continue; + } + + if (message.role === "toolResult") { + const result = message as ToolResultMessage; + const toolName = mapPiToolNameToSdk(result.toolName, customToolNameToSdk); + const status = result.isError ? "FAILED" : "SUCCESS"; + pushText(`\n[TOOL RESULT - ${status}]: ${toolName}\n`); + const hasText = appendContent(result.content); + if (!hasText) pushText("(see attached image)"); + continue; + } + } + + // Explicit continuation marker so the model knows it's mid-task + if (messages.length > 1) { + pushText( + "\n\n---\n[TASK STATUS: In progress. The history above shows completed work. Continue from where the assistant left off.]\n", + ); + } + + if (!blocks.length) return [{ type: "text", text: "" }]; + return blocks; +} + +function buildFallbackStream(promptBlocks: ContentBlockParam[]): AsyncIterable { + async function* generator() { + yield { + type: "user" as const, + message: { + role: "user" as const, + content: promptBlocks, + } as MessageParam, + parent_tool_use_id: null, + session_id: "prompt", + } satisfies SDKUserMessage; + } + return generator(); +} + +// ============================================================================= +// Settings +// ============================================================================= + +const CLAUDE_CONFIG_PATH = join(homedir(), ".claude", "pi-config.json"); + +type ProviderSettings = { + appendSystemPrompt?: boolean; + settingSources?: SettingSource[]; + strictMcpConfig?: boolean; + extraArgs?: Record; +}; + +function loadProviderSettings(): ProviderSettings { + const globalSettings = readSettingsFile(GLOBAL_SETTINGS_PATH); + const projectSettings = readSettingsFile(PROJECT_SETTINGS_PATH); + return { ...globalSettings, ...projectSettings }; +} + +function readSettingsFile(filePath: string): ProviderSettings { + if (!existsSync(filePath)) return {}; + try { + const raw = readFileSync(filePath, "utf-8"); + const parsed = JSON.parse(raw) as Record; + const settingsBlock = + (parsed["claudeAgentSdkProvider"] as Record | undefined) ?? + (parsed["claude-agent-sdk-provider"] as Record | undefined) ?? + (parsed["claudeAgentSdk"] as Record | undefined); + if (!settingsBlock || typeof settingsBlock !== "object") return {}; + + const appendSystemPrompt = + typeof settingsBlock["appendSystemPrompt"] === "boolean" + ? settingsBlock["appendSystemPrompt"] + : undefined; + + const settingSourcesRaw = settingsBlock["settingSources"]; + const settingSources = + Array.isArray(settingSourcesRaw) && + settingSourcesRaw.every( + (v) => typeof v === "string" && (v === "user" || v === "project" || v === "local"), + ) + ? (settingSourcesRaw as SettingSource[]) + : undefined; + + const strictMcpConfig = + typeof settingsBlock["strictMcpConfig"] === "boolean" + ? settingsBlock["strictMcpConfig"] + : undefined; + + const extraArgsRaw = settingsBlock["extraArgs"]; + const extraArgs = + extraArgsRaw && typeof extraArgsRaw === "object" && !Array.isArray(extraArgsRaw) + ? (Object.fromEntries( + Object.entries(extraArgsRaw as Record) + .filter(([, v]) => typeof v === "string" || v === null) + .map(([k, v]) => [k, v as string | null]), + ) as Record) + : undefined; + + return { appendSystemPrompt, settingSources, strictMcpConfig, extraArgs }; + } catch { + return {}; + } +} + +// ============================================================================= +// System Prompt Helpers +// ============================================================================= + +function extractSkillsAppend(systemPrompt?: string): string | undefined { + if (!systemPrompt) return undefined; + const startMarker = "The following skills provide specialized instructions for specific tasks."; + const endMarker = ""; + const startIndex = systemPrompt.indexOf(startMarker); + if (startIndex === -1) return undefined; + const endIndex = systemPrompt.indexOf(endMarker, startIndex); + if (endIndex === -1) return undefined; + const skillsBlock = systemPrompt.slice(startIndex, endIndex + endMarker.length).trim(); + return rewriteSkillsLocations(skillsBlock); +} + +function rewriteSkillsLocations(skillsBlock: string): string { + return skillsBlock.replace(/([^<]+)<\/location>/g, (_match, location: string) => { + let rewritten = location; + if (location.startsWith(GLOBAL_SKILLS_ROOT)) { + const relPath = relative(GLOBAL_SKILLS_ROOT, location).replace(/^\.+/, ""); + rewritten = `${SKILLS_ALIAS_GLOBAL}/${relPath}`.replace(/\/\/+/g, "/"); + } else if (location.startsWith(PROJECT_SKILLS_ROOT)) { + const relPath = relative(PROJECT_SKILLS_ROOT, location).replace(/^\.+/, ""); + rewritten = `${SKILLS_ALIAS_PROJECT}/${relPath}`.replace(/\/\/+/g, "/"); + } + return `${rewritten}`; + }); +} + +function resolveAgentsMdPath(): string | undefined { + const fromCwd = findAgentsMdInParents(process.cwd()); + if (fromCwd) return fromCwd; + if (existsSync(GLOBAL_AGENTS_PATH)) return GLOBAL_AGENTS_PATH; + return undefined; +} + +function findAgentsMdInParents(startDir: string): string | undefined { + let current = resolve(startDir); + while (true) { + const candidate = join(current, "AGENTS.md"); + if (existsSync(candidate)) return candidate; + const parent = dirname(current); + if (parent === current) break; + current = parent; + } + return undefined; +} + +function extractAgentsAppend(): string | undefined { + const agentsPath = resolveAgentsMdPath(); + if (!agentsPath) return undefined; + try { + const content = readFileSync(agentsPath, "utf-8").trim(); + if (!content) return undefined; + const sanitized = sanitizeAgentsContent(content); + return sanitized.length > 0 ? `# CLAUDE.md\n\n${sanitized}` : undefined; + } catch { + return undefined; + } +} + +function sanitizeAgentsContent(content: string): string { + let s = content; + s = s.replace(/~\/\.pi\b/gi, "~/.claude"); + s = s.replace(/(^|[\s'"`])\.pi\//g, "$1.claude/"); + s = s.replace(/\b\.pi\b/gi, ".claude"); + s = s.replace(/\bpi\b/gi, "environment"); + return s; +} + +// ============================================================================= +// Thinking Budget +// ============================================================================= + +type ThinkingLevel = NonNullable; +type NonXhighThinkingLevel = Exclude; + +const DEFAULT_THINKING_BUDGETS: Record = { + minimal: 2048, + low: 8192, + medium: 16384, + high: 31999, +}; + +const OPUS_46_THINKING_BUDGETS: Record = { + minimal: 2048, + low: 8192, + medium: 31999, + high: 63999, + xhigh: 63999, +}; + +function mapThinkingTokens( + reasoning?: ThinkingLevel, + modelId?: string, + thinkingBudgets?: SimpleStreamOptions["thinkingBudgets"], +): number | undefined { + if (!reasoning) return undefined; + const isOpus46 = modelId?.includes("opus-4-6") || modelId?.includes("opus-4.6"); + if (isOpus46) return OPUS_46_THINKING_BUDGETS[reasoning]; + const effectiveReasoning: NonXhighThinkingLevel = reasoning === "xhigh" ? "high" : reasoning; + const customBudgets = thinkingBudgets as Partial> | undefined; + const customBudget = customBudgets?.[effectiveReasoning]; + if (typeof customBudget === "number" && Number.isFinite(customBudget) && customBudget > 0) { + return customBudget; + } + return DEFAULT_THINKING_BUDGETS[effectiveReasoning]; +} + +// ============================================================================= +// Misc Helpers +// ============================================================================= + +function parsePartialJson(input: string, fallback: Record): Record { + if (!input) return fallback; + try { + return JSON.parse(input); + } catch { + return fallback; + } +} + +function mapStopReason(reason: string | undefined): "stop" | "length" | "toolUse" { + switch (reason) { + case "tool_use": + return "toolUse"; + case "max_tokens": + return "length"; + default: + return "stop"; + } +} + +// ============================================================================= +// Main Stream Function +// ============================================================================= + +function streamClaudeAgentSdk( + model: Model, + context: Context, + options?: SimpleStreamOptions, +): AssistantMessageEventStream { + const stream = createAssistantMessageEventStream(); + + (async () => { + const output: AssistantMessage = { + role: "assistant", + content: [], + api: model.api, + provider: model.provider, + model: model.id, + usage: { + input: 0, + output: 0, + cacheRead: 0, + cacheWrite: 0, + totalTokens: 0, + cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 }, + }, + stopReason: "stop", + timestamp: Date.now(), + }; + + let sdkQuery: ReturnType | undefined; + let wasAborted = false; + const requestAbort = () => { + if (!sdkQuery) return; + void sdkQuery.interrupt().catch(() => { + try { + sdkQuery?.close(); + } catch { + // ignore + } + }); + }; + const onAbort = () => { + wasAborted = true; + requestAbort(); + }; + if (options?.signal) { + if (options.signal.aborted) onAbort(); + else options.signal.addEventListener("abort", onAbort, { once: true }); + } + + const blocks = output.content as Array< + | { type: "text"; text: string; index: number } + | { type: "thinking"; thinking: string; thinkingSignature?: string; index: number } + | { + type: "toolCall"; + id: string; + name: string; + arguments: Record; + partialJson: string; + index: number; + } + >; + + let started = false; + let sawStreamEvent = false; + let sawToolCall = false; + let shouldStopEarly = false; + + try { + const { sdkTools, customTools, customToolNameToSdk, customToolNameToPi } = + resolveSdkTools(context); + + const cwd = (options as { cwd?: string } | undefined)?.cwd ?? process.cwd(); + const mcpServers = buildCustomToolServers(customTools); + const providerSettings = loadProviderSettings(); + const appendSystemPrompt = providerSettings.appendSystemPrompt !== false; + const agentsAppend = appendSystemPrompt ? extractAgentsAppend() : undefined; + const skillsAppend = appendSystemPrompt + ? extractSkillsAppend(context.systemPrompt) + : undefined; + const appendParts = [agentsAppend, skillsAppend].filter((p): p is string => Boolean(p)); + const systemPromptAppend = appendParts.length > 0 ? appendParts.join("\n\n") : undefined; + const allowSkillAliasRewrite = Boolean(skillsAppend); + + const settingSources: SettingSource[] | undefined = appendSystemPrompt + ? undefined + : providerSettings.settingSources ?? ["user", "project"]; + + const strictMcpConfigEnabled = !appendSystemPrompt && providerSettings.strictMcpConfig !== false; + const generatedExtraArgs = strictMcpConfigEnabled ? { "strict-mcp-config": null } : undefined; + const extraArgs = { ...generatedExtraArgs, ...providerSettings.extraArgs }; + + // ---------------------------------------------------------------- + // Decide: resume existing session or start fresh? + // ---------------------------------------------------------------- + const convKey = getConversationKey(context, options); + const existingSession = sessionRegistry.get(convKey); + + let prompt: AsyncIterable; + let claudeCodeSessionId: string; + let newSentMsgCount: number; + let isResume: boolean; + + if (existingSession && context.messages.length > existingSession.sentMsgCount) { + // Continuation: resume and inject new tool results / user messages. + claudeCodeSessionId = existingSession.claudeCodeSessionId; + newSentMsgCount = context.messages.length; + isResume = true; + prompt = buildResumeStream(context, existingSession.sentMsgCount, claudeCodeSessionId); + } else { + // Fresh session (first turn, or after session was cleaned up). + claudeCodeSessionId = randomUUID(); + newSentMsgCount = context.messages.length; + isResume = false; + const fallbackBlocks = buildFallbackTextBlocks(context, customToolNameToSdk); + prompt = buildFallbackStream(fallbackBlocks); + } + + const queryOptions: NonNullable[0]["options"]> = { + cwd, + tools: sdkTools, + permissionMode: "dontAsk", + includePartialMessages: true, + canUseTool: async () => ({ + behavior: "deny", + message: TOOL_EXECUTION_DENIED_MESSAGE, + }), + systemPrompt: { + type: "preset", + preset: "claude_code", + append: systemPromptAppend ?? undefined, + }, + ...(settingSources ? { settingSources } : {}), + ...(Object.keys(extraArgs).length > 0 ? { extraArgs } : {}), + ...(mcpServers ? { mcpServers } : {}), + // Session management + ...(isResume + ? { resume: claudeCodeSessionId } + : { sessionId: claudeCodeSessionId, persistSession: true }), + }; + + const maxThinkingTokens = mapThinkingTokens(options?.reasoning, model.id, options?.thinkingBudgets); + if (maxThinkingTokens != null) { + queryOptions.maxThinkingTokens = maxThinkingTokens; + } + + // Register / update session state BEFORE the query so we have it + // even if an error occurs after the first message_stop. + sessionRegistry.set(convKey, { + claudeCodeSessionId, + sentMsgCount: newSentMsgCount, + modelId: model.id, + }); + + sdkQuery = query({ prompt, options: queryOptions }); + + if (wasAborted) requestAbort(); + + for await (const message of sdkQuery) { + if (!started) { + stream.push({ type: "start", partial: output }); + started = true; + } + + switch (message.type) { + case "stream_event": { + sawStreamEvent = true; + const event = (message as SDKMessage & { event: any }).event; + + if (event?.type === "message_start") { + const usage = event.message?.usage; + output.usage.input = usage?.input_tokens ?? 0; + output.usage.output = usage?.output_tokens ?? 0; + output.usage.cacheRead = usage?.cache_read_input_tokens ?? 0; + output.usage.cacheWrite = usage?.cache_creation_input_tokens ?? 0; + output.usage.totalTokens = + output.usage.input + + output.usage.output + + output.usage.cacheRead + + output.usage.cacheWrite; + calculateCost(model, output.usage); + break; + } + + if (event?.type === "content_block_start") { + if (event.content_block?.type === "text") { + blocks.push({ type: "text", text: "", index: event.index }); + stream.push({ + type: "text_start", + contentIndex: output.content.length - 1, + partial: output, + }); + } else if (event.content_block?.type === "thinking") { + blocks.push({ + type: "thinking", + thinking: "", + thinkingSignature: "", + index: event.index, + }); + stream.push({ + type: "thinking_start", + contentIndex: output.content.length - 1, + partial: output, + }); + } else if (event.content_block?.type === "tool_use") { + sawToolCall = true; + blocks.push({ + type: "toolCall", + id: event.content_block.id, + name: mapToolName(event.content_block.name, customToolNameToPi), + arguments: (event.content_block.input as Record) ?? {}, + partialJson: "", + index: event.index, + }); + stream.push({ + type: "toolcall_start", + contentIndex: output.content.length - 1, + partial: output, + }); + } + break; + } + + if (event?.type === "content_block_delta") { + if (event.delta?.type === "text_delta") { + const idx = blocks.findIndex((b) => b.index === event.index); + const block = blocks[idx]; + if (block?.type === "text") { + block.text += event.delta.text; + stream.push({ + type: "text_delta", + contentIndex: idx, + delta: event.delta.text, + partial: output, + }); + } + } else if (event.delta?.type === "thinking_delta") { + const idx = blocks.findIndex((b) => b.index === event.index); + const block = blocks[idx]; + if (block?.type === "thinking") { + block.thinking += event.delta.thinking; + stream.push({ + type: "thinking_delta", + contentIndex: idx, + delta: event.delta.thinking, + partial: output, + }); + } + } else if (event.delta?.type === "input_json_delta") { + const idx = blocks.findIndex((b) => b.index === event.index); + const block = blocks[idx]; + if (block?.type === "toolCall") { + block.partialJson += event.delta.partial_json; + block.arguments = parsePartialJson(block.partialJson, block.arguments); + stream.push({ + type: "toolcall_delta", + contentIndex: idx, + delta: event.delta.partial_json, + partial: output, + }); + } + } else if (event.delta?.type === "signature_delta") { + const idx = blocks.findIndex((b) => b.index === event.index); + const block = blocks[idx]; + if (block?.type === "thinking") { + block.thinkingSignature = + (block.thinkingSignature ?? "") + event.delta.signature; + } + } + break; + } + + if (event?.type === "content_block_stop") { + const idx = blocks.findIndex((b) => b.index === event.index); + const block = blocks[idx]; + if (!block) break; + delete (block as any).index; + if (block.type === "text") { + stream.push({ + type: "text_end", + contentIndex: idx, + content: block.text, + partial: output, + }); + } else if (block.type === "thinking") { + stream.push({ + type: "thinking_end", + contentIndex: idx, + content: block.thinking, + partial: output, + }); + } else if (block.type === "toolCall") { + sawToolCall = true; + block.arguments = mapToolArgs( + block.name, + parsePartialJson(block.partialJson, block.arguments), + allowSkillAliasRewrite, + ); + delete (block as any).partialJson; + stream.push({ + type: "toolcall_end", + contentIndex: idx, + toolCall: block, + partial: output, + }); + } + break; + } + + if (event?.type === "message_delta") { + output.stopReason = mapStopReason(event.delta?.stop_reason); + const usage = event.usage ?? {}; + if (usage.input_tokens != null) output.usage.input = usage.input_tokens; + if (usage.output_tokens != null) output.usage.output = usage.output_tokens; + if (usage.cache_read_input_tokens != null) + output.usage.cacheRead = usage.cache_read_input_tokens; + if (usage.cache_creation_input_tokens != null) + output.usage.cacheWrite = usage.cache_creation_input_tokens; + output.usage.totalTokens = + output.usage.input + + output.usage.output + + output.usage.cacheRead + + output.usage.cacheWrite; + calculateCost(model, output.usage); + break; + } + + if (event?.type === "message_stop" && sawToolCall) { + output.stopReason = "toolUse"; + shouldStopEarly = true; + break; + } + + break; + } + + case "result": { + if (!sawStreamEvent && message.subtype === "success") { + output.content.push({ type: "text", text: (message as any).result || "" }); + } + break; + } + } + + if (shouldStopEarly) break; + } + + if (wasAborted || options?.signal?.aborted) { + output.stopReason = "aborted"; + output.errorMessage = "Operation aborted"; + stream.push({ type: "error", reason: "aborted", error: output }); + stream.end(); + return; + } + + // Clean up session registry when the conversation ends naturally + // (no tool calls → final answer → conversation over). + if (output.stopReason === "stop" || output.stopReason === "length") { + sessionRegistry.delete(convKey); + } + + stream.push({ + type: "done", + reason: + output.stopReason === "toolUse" + ? "toolUse" + : output.stopReason === "length" + ? "length" + : "stop", + message: output, + }); + stream.end(); + } catch (error) { + output.stopReason = options?.signal?.aborted ? "aborted" : "error"; + output.errorMessage = error instanceof Error ? error.message : String(error); + stream.push({ + type: "error", + reason: output.stopReason as "aborted" | "error", + error: output, + }); + stream.end(); + } finally { + if (options?.signal) { + options.signal.removeEventListener("abort", onAbort); + } + sdkQuery?.close(); + } + })(); + + return stream; +} + +// ============================================================================= +// Extension Entry Point +// ============================================================================= + +export default function (pi: ExtensionAPI) { + pi.registerProvider(PROVIDER_ID, { + baseUrl: "claude-agent-sdk", + apiKey: "ANTHROPIC_API_KEY", + api: "claude-agent-sdk", + models: MODELS, + streamSimple: streamClaudeAgentSdk, + }); +} diff --git a/pi/.pi/agent/extensions/claude-agent-sdk/package-lock.json b/pi/.pi/agent/extensions/claude-agent-sdk/package-lock.json new file mode 100644 index 0000000..8a57af8 --- /dev/null +++ b/pi/.pi/agent/extensions/claude-agent-sdk/package-lock.json @@ -0,0 +1,1522 @@ +{ + "name": "claude-agent-sdk", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "claude-agent-sdk", + "dependencies": { + "@anthropic-ai/claude-agent-sdk": "^0.2.92", + "change-case": "^5.4.4" + } + }, + "node_modules/@anthropic-ai/claude-agent-sdk": { + "version": "0.2.92", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk/-/claude-agent-sdk-0.2.92.tgz", + "integrity": "sha512-loYyxVUC5gBwHjGi9Fv0b84mduJTp9Z3Pum+y/7IVQDb4NynKfVQl6l4VeDKZaW+1QTQtd25tY4hwUznD7Krqw==", + "license": "SEE LICENSE IN README.md", + "dependencies": { + "@anthropic-ai/sdk": "^0.80.0", + "@modelcontextprotocol/sdk": "^1.27.1" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "^0.34.2", + "@img/sharp-darwin-x64": "^0.34.2", + "@img/sharp-linux-arm": "^0.34.2", + "@img/sharp-linux-arm64": "^0.34.2", + "@img/sharp-linux-x64": "^0.34.2", + "@img/sharp-linuxmusl-arm64": "^0.34.2", + "@img/sharp-linuxmusl-x64": "^0.34.2", + "@img/sharp-win32-arm64": "^0.34.2", + "@img/sharp-win32-x64": "^0.34.2" + }, + "peerDependencies": { + "zod": "^4.0.0" + } + }, + "node_modules/@anthropic-ai/sdk": { + "version": "0.80.0", + "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.80.0.tgz", + "integrity": "sha512-WeXLn7zNVk3yjeshn+xZHvld6AoFUOR3Sep6pSoHho5YbSi6HwcirqgPA5ccFuW8QTVJAAU7N8uQQC6Wa9TG+g==", + "license": "MIT", + "dependencies": { + "json-schema-to-ts": "^3.1.1" + }, + "bin": { + "anthropic-ai-sdk": "bin/cli" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@hono/node-server": { + "version": "1.19.13", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.13.tgz", + "integrity": "sha512-TsQLe4i2gvoTtrHje625ngThGBySOgSK3Xo2XRYOdqGN1teR8+I7vchQC46uLJi8OF62YTYA3AhSpumtkhsaKQ==", + "license": "MIT", + "engines": { + "node": ">=18.14.1" + }, + "peerDependencies": { + "hono": "^4" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@modelcontextprotocol/sdk": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.29.0.tgz", + "integrity": "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==", + "license": "MIT", + "dependencies": { + "@hono/node-server": "^1.19.9", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", + "content-type": "^1.0.5", + "cors": "^2.8.5", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", + "express": "^5.2.1", + "express-rate-limit": "^8.2.1", + "hono": "^4.11.4", + "jose": "^6.1.3", + "json-schema-typed": "^8.0.2", + "pkce-challenge": "^5.0.0", + "raw-body": "^3.0.0", + "zod": "^3.25 || ^4.0", + "zod-to-json-schema": "^3.25.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@cfworker/json-schema": "^4.1.1", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "@cfworker/json-schema": { + "optional": true + }, + "zod": { + "optional": false + } + } + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/body-parser": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", + "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.1", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/change-case": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/change-case/-/change-case-5.4.4.tgz", + "integrity": "sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==", + "license": "MIT" + }, + "node_modules/content-disposition": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", + "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventsource": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", + "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", + "license": "MIT", + "dependencies": { + "eventsource-parser": "^3.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/eventsource-parser": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.6.tgz", + "integrity": "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-rate-limit": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.3.2.tgz", + "integrity": "sha512-77VmFeJkO0/rvimEDuUC5H30oqUC4EyOhyGccfqoLebB0oiEYfM7nwPrsDsBL1gsTpwfzX8SFy2MT3TDyRq+bg==", + "license": "MIT", + "dependencies": { + "ip-address": "10.1.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": ">= 4.11" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hono": { + "version": "4.12.12", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.12.tgz", + "integrity": "sha512-p1JfQMKaceuCbpJKAPKVqyqviZdS0eUxH9v82oWo1kb9xjQ5wA6iP3FNVAPDFlz5/p7d45lO+BpSk1tuSZMF4Q==", + "license": "MIT", + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ip-address": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", + "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/jose": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.2.tgz", + "integrity": "sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/json-schema-to-ts": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/json-schema-to-ts/-/json-schema-to-ts-3.1.1.tgz", + "integrity": "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "ts-algebra": "^2.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/json-schema-typed": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz", + "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", + "license": "BSD-2-Clause" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-to-regexp": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", + "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/pkce-challenge": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz", + "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==", + "license": "MIT", + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.0.tgz", + "integrity": "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/ts-algebra": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ts-algebra/-/ts-algebra-2.0.0.tgz", + "integrity": "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==", + "license": "MIT" + }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/zod": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", + "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.25.2", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz", + "integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==", + "license": "ISC", + "peerDependencies": { + "zod": "^3.25.28 || ^4" + } + } + } +} diff --git a/pi/.pi/agent/extensions/claude-agent-sdk/package.json b/pi/.pi/agent/extensions/claude-agent-sdk/package.json new file mode 100644 index 0000000..acc1fc4 --- /dev/null +++ b/pi/.pi/agent/extensions/claude-agent-sdk/package.json @@ -0,0 +1,11 @@ +{ + "name": "claude-agent-sdk", + "type": "module", + "dependencies": { + "@anthropic-ai/claude-agent-sdk": "^0.2.92", + "change-case": "^5.4.4" + }, + "pi": { + "extensions": ["./index.ts"] + } +} diff --git a/pi/.pi/agent/extensions/exa-mcp.json b/pi/.pi/agent/extensions/exa-mcp.json new file mode 100644 index 0000000..63db935 --- /dev/null +++ b/pi/.pi/agent/extensions/exa-mcp.json @@ -0,0 +1,12 @@ +{ + "url": "https://mcp.exa.ai/mcp", + "apiKey": null, + "tools": [ + "web_search_exa", + "get_code_context_exa" + ], + "timeoutMs": 30000, + "protocolVersion": "2025-06-18", + "maxBytes": 51200, + "maxLines": 2000 +} diff --git a/pi/.pi/agent/extensions/usage-bars/core.ts b/pi/.pi/agent/extensions/usage-bars/core.ts index 3d37d01..d1ffd9a 100644 --- a/pi/.pi/agent/extensions/usage-bars/core.ts +++ b/pi/.pi/agent/extensions/usage-bars/core.ts @@ -36,8 +36,8 @@ export function writeUsageCache(cache: UsageCache): void { } catch {} } -export type ProviderKey = "codex" | "claude" | "zai" | "gemini" | "antigravity"; -export type OAuthProviderId = "openai-codex" | "anthropic" | "google-gemini-cli" | "google-antigravity"; +export type ProviderKey = "codex" | "claude" | "zai" | "gemini" | "antigravity" | "opencode-go"; +export type OAuthProviderId = "openai-codex" | "anthropic" | "google-gemini-cli" | "google-antigravity" | "opencode-go"; export interface AuthData { "openai-codex"?: { access?: string; refresh?: string; expires?: number }; @@ -45,6 +45,7 @@ export interface AuthData { zai?: { key?: string; access?: string; refresh?: string; expires?: number }; "google-gemini-cli"?: { access?: string; refresh?: string; projectId?: string; expires?: number }; "google-antigravity"?: { access?: string; refresh?: string; projectId?: string; expires?: number }; + "opencode-go"?: { key?: string; access?: string }; } export interface UsageData { @@ -61,6 +62,127 @@ export interface UsageData { export type UsageByProvider = Record; +// OpenCode Go usage tracking (local, since no API exists yet) +export interface OpenCodeGoLocalUsage { + /** Dollar value used in the 5-hour window */ + fiveHourUsed: number; + /** Dollar value used in the weekly window */ + weeklyUsed: number; + /** Dollar value used in the monthly window */ + monthlyUsed: number; + /** Timestamp of the last update */ + lastUpdated: number; + /** When the current 5-hour window started */ + fiveHourWindowStart: number; + /** When the current week started (Unix ms) */ + weekStart: number; + /** When the current month started (Unix ms) */ + monthStart: number; +} + +const OPENCODE_GO_USAGE_FILE = path.join(os.homedir(), ".pi", "agent", "opencode-go-usage.json"); +const OPENCODE_GO_FIVE_HOUR_LIMIT = 12; +const OPENCODE_GO_WEEKLY_LIMIT = 30; +const OPENCODE_GO_MONTHLY_LIMIT = 60; +const OPENCODE_GO_FIVE_HOUR_MS = 5 * 60 * 60 * 1000; + +export function readOpenCodeGoUsage(): OpenCodeGoLocalUsage | null { + try { + const raw = fs.readFileSync(OPENCODE_GO_USAGE_FILE, "utf-8"); + const parsed = JSON.parse(raw); + if (typeof parsed?.lastUpdated === "number") return parsed as OpenCodeGoLocalUsage; + } catch {} + return null; +} + +export function writeOpenCodeGoUsage(usage: OpenCodeGoLocalUsage): void { + try { + const dir = path.dirname(OPENCODE_GO_USAGE_FILE); + if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); + const tmp = `${OPENCODE_GO_USAGE_FILE}.tmp-${process.pid}-${Date.now()}`; + fs.writeFileSync(tmp, JSON.stringify(usage, null, 2)); + fs.renameSync(tmp, OPENCODE_GO_USAGE_FILE); + } catch {} +} + +export function resetOpenCodeGoUsageIfNeeded(existing: OpenCodeGoLocalUsage | null): OpenCodeGoLocalUsage { + const now = Date.now(); + const nowDate = new Date(now); + + // Start with defaults + let usage: OpenCodeGoLocalUsage = existing ?? { + fiveHourUsed: 0, + weeklyUsed: 0, + monthlyUsed: 0, + lastUpdated: now, + fiveHourWindowStart: now, + weekStart: now, + monthStart: now, + }; + + // Reset 5-hour window if expired + if (now - usage.fiveHourWindowStart >= OPENCODE_GO_FIVE_HOUR_MS) { + usage.fiveHourUsed = 0; + usage.fiveHourWindowStart = now; + } + + // Reset weekly window (Monday-based) + const dayOfWeek = nowDate.getDay(); + const daysSinceMonday = dayOfWeek === 0 ? 6 : dayOfWeek - 1; + const thisMonday = new Date(nowDate); + thisMonday.setDate(nowDate.getDate() - daysSinceMonday); + thisMonday.setHours(0, 0, 0, 0); + if (usage.weekStart < thisMonday.getTime()) { + usage.weeklyUsed = 0; + usage.weekStart = thisMonday.getTime(); + } + + // Reset monthly window (1st of month) + const thisMonthStart = new Date(nowDate.getFullYear(), nowDate.getMonth(), 1); + if (usage.monthStart < thisMonthStart.getTime()) { + usage.monthlyUsed = 0; + usage.monthStart = thisMonthStart.getTime(); + } + + usage.lastUpdated = now; + return usage; +} + +export function addOpenCodeGoSpend(dollars: number): void { + let usage = resetOpenCodeGoUsageIfNeeded(readOpenCodeGoUsage()); + usage.fiveHourUsed += dollars; + usage.weeklyUsed += dollars; + usage.monthlyUsed += dollars; + usage.lastUpdated = Date.now(); + writeOpenCodeGoUsage(usage); +} + +export function getOpenCodeGoUsageData(): UsageData { + const usage = resetOpenCodeGoUsageIfNeeded(readOpenCodeGoUsage()); + if (!usage) { + return { session: 0, weekly: 0, error: "no local usage data" }; + } + + const sessionPct = Math.min(100, (usage.fiveHourUsed / OPENCODE_GO_FIVE_HOUR_LIMIT) * 100); + const weeklyPct = Math.min(100, (usage.weeklyUsed / OPENCODE_GO_WEEKLY_LIMIT) * 100); + + // Calculate resets + const fiveHourEnd = usage.fiveHourWindowStart + OPENCODE_GO_FIVE_HOUR_MS; + const fiveHourRemaining = Math.max(0, fiveHourEnd - Date.now()); + + const weekEnd = usage.weekStart + 7 * 24 * 60 * 60 * 1000; + const weekRemaining = Math.max(0, weekEnd - Date.now()); + + return { + session: sessionPct, + weekly: weeklyPct, + sessionResetsIn: formatDuration(Math.round(fiveHourRemaining / 1000)), + weeklyResetsIn: formatDuration(Math.round(weekRemaining / 1000)), + extraSpend: usage.monthlyUsed, + extraLimit: OPENCODE_GO_MONTHLY_LIMIT, + }; +} + export interface UsageEndpoints { zai: string; gemini: string; @@ -600,12 +722,14 @@ export function detectProvider( if (typeof model === "string") return null; const provider = (model.provider || "").toLowerCase(); + const id = (model.id || "").toLowerCase(); if (provider === "openai-codex") return "codex"; if (provider === "anthropic") return "claude"; if (provider === "zai") return "zai"; if (provider === "google-gemini-cli") return "gemini"; if (provider === "google-antigravity") return "antigravity"; + if (provider === "opencode-go" || id.startsWith("opencode-go/")) return "opencode-go"; return null; } @@ -615,6 +739,7 @@ export function providerToOAuthProviderId(active: ProviderKey | null): OAuthProv if (active === "claude") return "anthropic"; if (active === "gemini") return "google-gemini-cli"; if (active === "antigravity") return "google-antigravity"; + if (active === "opencode-go") return "opencode-go"; return null; } @@ -629,6 +754,9 @@ export function canShowForProvider(active: ProviderKey | null, auth: AuthData | if (active === "antigravity") { return !!(auth["google-antigravity"]?.access || auth["google-antigravity"]?.refresh) && !!endpoints.antigravity; } + if (active === "opencode-go") { + return !!(auth["opencode-go"]?.key || auth["opencode-go"]?.access); + } return false; } @@ -654,6 +782,7 @@ export async function fetchAllUsages(config: FetchAllUsagesConfig = {}): Promise zai: null, gemini: null, antigravity: null, + "opencode-go": null, }; if (!auth) return results; @@ -733,6 +862,11 @@ export async function fetchAllUsages(config: FetchAllUsagesConfig = {}): Promise } } + // OpenCode Go uses local tracking (no public API yet) + if (authData["opencode-go"]?.key || authData["opencode-go"]?.access) { + results["opencode-go"] = getOpenCodeGoUsageData(); + } + await Promise.all(tasks); return results; } diff --git a/pi/.pi/agent/extensions/usage-bars/index.ts b/pi/.pi/agent/extensions/usage-bars/index.ts index 2468e8b..0c758ee 100644 --- a/pi/.pi/agent/extensions/usage-bars/index.ts +++ b/pi/.pi/agent/extensions/usage-bars/index.ts @@ -79,6 +79,7 @@ const PROVIDER_LABELS: Record = { zai: "Z.AI", gemini: "Gemini", antigravity: "Antigravity", + "opencode-go": "Go", }; // --------------------------------------------------------------------------- @@ -163,6 +164,7 @@ class UsageSelectorComponent extends Container implements Focusable { { key: "zai", name: "Z.AI" }, { key: "gemini", name: "Gemini" }, { key: "antigravity", name: "Antigravity" }, + { key: "opencode-go", name: "Go" }, ]; this.allItems = []; for (const p of providers) { @@ -287,7 +289,7 @@ interface PollOptions { export default function (pi: ExtensionAPI) { const endpoints = resolveUsageEndpoints(); const state: UsageState = { - codex: null, claude: null, zai: null, gemini: null, antigravity: null, + codex: null, claude: null, zai: null, gemini: null, antigravity: null, "opencode-go": null, lastPoll: 0, activeProvider: null, }; @@ -433,6 +435,10 @@ export default function (pi: ExtensionAPI) { result = creds?.access ? await fetchGoogleUsage(creds.access, endpoints.gemini, creds.projectId, "gemini", { endpoints }) : { session: 0, weekly: 0, error: "missing access token (try /login again)" }; + } else if (active === "opencode-go") { + // OpenCode Go uses local tracking (no public usage API yet) + const { getOpenCodeGoUsageData } = await import("./core"); + result = getOpenCodeGoUsageData(); } else { const creds = effectiveAuth["google-antigravity"]; result = creds?.access @@ -561,6 +567,22 @@ export default function (pi: ExtensionAPI) { void poll({ forceFresh: true }); }); + // Listen for OpenCode Go spend events from other extensions + pi.events.on("opencode-go:spend", async (amount: number) => { + if (typeof amount === "number" && amount > 0) { + const { addOpenCodeGoSpend } = await import("./core"); + addOpenCodeGoSpend(amount); + // Invalidate cache and re-poll + const cache = readUsageCache(); + if (cache?.data?.["opencode-go"]) { + const nextCache: import("./core").UsageCache = { ...cache, data: { ...cache.data } }; + delete nextCache.data["opencode-go"]; + writeUsageCache(nextCache); + } + void poll({ forceFresh: true }); + } + }); + // ── /usage command ─────────────────────────────────────────────────────── pi.registerCommand("usage", { diff --git a/pi/.pi/agent/models.json b/pi/.pi/agent/models.json index 9638e33..dd84b8d 100644 --- a/pi/.pi/agent/models.json +++ b/pi/.pi/agent/models.json @@ -23,6 +23,15 @@ "maxTokens": 131072, "cost": { "input": 1.0, "output": 3.2, "cacheRead": 0.2, "cacheWrite": 0 } }, + { + "id": "glm-5.1", + "name": "GLM-5.1", + "reasoning": true, + "input": ["text"], + "contextWindow": 204800, + "maxTokens": 131072, + "cost": { "input": 1.0, "output": 3.2, "cacheRead": 0.2, "cacheWrite": 0 } + }, { "id": "kimi-k2.5", "name": "Kimi K2.5", diff --git a/pi/.pi/agent/prompts/implement-critical.md b/pi/.pi/agent/prompts/implement-critical.md index 9ae24e5..07d9ebf 100644 --- a/pi/.pi/agent/prompts/implement-critical.md +++ b/pi/.pi/agent/prompts/implement-critical.md @@ -1,5 +1,5 @@ --- -description: "Maximum quality pipeline — deep scout, Max planning, Opus plan review, approval gate, Sonnet coding, Opus code review" +description: "Maximum quality pipeline — deep scout, thorough planning, plan review, approval gate, coding, code review" --- Use the subagent tool to implement with maximum quality. This is for high-risk or architecturally significant changes. @@ -30,7 +30,7 @@ If the user requests changes, revise the plan (re-run planner with the feedback) - For each coder run, include the approved plan verbatim: "Implement the following plan step(s). Do NOT deviate.\n\n\n{the approved plan steps}\n" - For multiple independent steps, run them in parallel using separate coder-claude tasks, each assigned to specific files/plan steps to avoid conflicts -## Step 4: Opus review +## Step 4: Code review Run the "reviewer" agent on all changes with this task: "Review all changes made for: $@\n\nCheck for correctness, edge cases, error handling, type safety, and consistency with the approved plan." ## Step 5: Fix diff --git a/pi/.pi/agent/prompts/implement.md b/pi/.pi/agent/prompts/implement.md index f01243e..048f797 100644 --- a/pi/.pi/agent/prompts/implement.md +++ b/pi/.pi/agent/prompts/implement.md @@ -29,7 +29,7 @@ If the user requests changes to the plan, revise and present again before implem ## Step 3: Implement Once approved: -- Execute the plan steps using "coder" for sequential steps, or "coder-parallel" with parallel tasks if the plan identified parallelizable steps +- Execute the plan steps using the "coder" agent - When running coder, always wrap the plan step(s) in the task: "Implement the following plan step(s). Do NOT deviate.\n\n\n{the approved plan steps}\n" - Run the "reviewer" agent on all changes - If NEEDS_FIXES, run the "fixer" agent with the review output @@ -43,26 +43,13 @@ After the final step, summarize: what was done, what files changed, what was rev - **NEVER skip the approval gate**. Always present the plan and wait. - Always pass scout context forward using {previous} in chain mode — this is how the planner and plan-reviewer receive the scout's findings. - When running the coder, always include the approved plan verbatim in the task so the coder has full context. -- For parallel coder tasks, clearly assign each coder to specific files/plan steps to avoid conflicts. ## Agent Failure and Fallback When a subagent returns empty output or an error (rate limit, credit exhaustion, connection failure): 1. **Retry once** with the same agent and model — transient failures are common. -2. **If still failing, retry with the cross-family fallback model** using the `model` override parameter: - -| Agent | Primary model | Fallback model | -|-------|--------------|----------------| -| scout | zai/glm-4.7-flash | anthropic/claude-haiku-4-5 | -| deep-scout, coder-parallel | zai/glm-4.7 | anthropic/claude-sonnet-4-6 | -| planner, coder | zai/glm-5.1 | anthropic/claude-opus-4-6 | -| reviewer-quick, explorer | anthropic/claude-haiku-4-5 | zai/glm-4.7-flash | -| coder-claude, fixer | anthropic/claude-sonnet-4-6 | zai/glm-5.1 | -| plan-reviewer, reviewer | anthropic/claude-opus-4-6 | zai/glm-5.1 | - -Example: `subagent({ agent: "scout", task: "...", model: "anthropic/claude-haiku-4-5" })` - +2. **If still failing, retry with the cross-family fallback model** using the `model` override parameter. See the fallback table in the `subagent-implement` SKILL.md for the current primary/fallback mapping. 3. **If the fallback also fails**, do the work yourself (read the relevant files directly and produce the scout/plan/review output inline). Inform the user which agent failed, what error was returned, and what you did instead. Do NOT silently absorb failures. Always surface them to the user even when working around them. diff --git a/pi/.pi/agent/prompts/plan.md b/pi/.pi/agent/prompts/plan.md index 8302c12..4e6208a 100644 --- a/pi/.pi/agent/prompts/plan.md +++ b/pi/.pi/agent/prompts/plan.md @@ -1,5 +1,5 @@ --- -description: "Scout + plan + Opus plan review — no implementation" +description: "Scout + plan + plan review — no implementation" --- Use the subagent tool with a chain to plan (but NOT implement) the following: @@ -19,8 +19,5 @@ Present the plan and the review to me. Do NOT proceed to implementation. If any agent returns empty output or an error (rate limit, credit exhaustion, connection failure): 1. Retry once with the same agent. -2. If still failing, retry with the cross-family fallback using the `model` override: - - scout (zai/glm-4.7-flash fails) → `model: "anthropic/claude-haiku-4-5"` - - planner (zai/glm-5.1 fails) → `model: "anthropic/claude-opus-4-6"` - - plan-reviewer (anthropic/claude-opus-4-6 fails) → `model: "zai/glm-5.1"` +2. If still failing, retry with the cross-family fallback using the `model` override. See the fallback table in the `subagent-plan` SKILL.md for the current mapping. 3. If the fallback also fails, do the work yourself and tell me which agent failed and why. diff --git a/pi/.pi/agent/skills/opty/SKILL.md b/pi/.pi/agent/skills/opty/SKILL.md new file mode 100644 index 0000000..849d6e9 --- /dev/null +++ b/pi/.pi/agent/skills/opty/SKILL.md @@ -0,0 +1,37 @@ +--- +name: opty +description: Semantic code search using HDC (Hyperdimensional Computing). Finds functions, types, imports, and variables in the indexed codebase by meaning. Use when searching for code by concept — e.g. "error handling functions", "database types", "authentication flow". +compatibility: Requires opty CLI and a running daemon. Install from source or binary. +allowed-tools: Bash(opty:*) +--- + +# Opty — HDC Code Search + +Semantic code search via Hyperdimensional Computing. Indexes the codebase and finds relevant functions, types, and imports by meaning rather than exact text. The daemon auto-starts when you run any opty command. + +## Query + +```bash +opty query "natural language description of what you're looking for" +``` + +Output is TOON format — compact, LLM-optimized: +``` +functions[N]{name,signature,file,line}: +functionName,signature,path/to/file.ts,42 +... +``` + +## Other Commands + +```bash +opty status # Show indexed file/unit count and watched directory +opty reindex # Force re-scan after major file changes or stale results +``` + +## Tips + +- Query by concept, not exact names: `"connection pool exhaustion"` not `"ConnectionPoolError"` +- Results include file + line number — use `read` to get the actual code +- `opty status` shows which directory is indexed (the daemon is per-project) +- After large refactors, run `opty reindex` if results seem stale diff --git a/pi/.pi/agent/skills/qmd/SKILL.md b/pi/.pi/agent/skills/qmd/SKILL.md new file mode 100644 index 0000000..dd97600 --- /dev/null +++ b/pi/.pi/agent/skills/qmd/SKILL.md @@ -0,0 +1,89 @@ +--- +name: qmd +description: Search markdown knowledge bases, notes, and documentation using the qmd CLI. Use when searching notes, finding documents, or looking up information in the knowledge base. +license: MIT +compatibility: Requires qmd CLI. Install via `npm install -g @tobilu/qmd`. +metadata: + author: tobi + version: "2.0.0" +allowed-tools: Bash(qmd:*) +--- + +# QMD - Quick Markdown Search + +Local search engine for markdown content. Use via the `qmd` CLI (bash). + +## Status + +!`qmd status 2>/dev/null || echo "Not installed: npm install -g @tobilu/qmd"` + +## CLI Usage + +```bash +qmd query "question" # Auto-expand + rerank (recommended) +qmd query $'lex: X\nvec: Y' # Structured query document +qmd query $'expand: question' # Explicit expand +qmd search "keywords" # BM25 only (no LLM) +qmd get "path/to/file.md" # Retrieve a document by path +qmd get "#abc123" # Retrieve by docid +qmd get "path/to/file.md:100" -l 50 # Retrieve starting at line 100, 50 lines +qmd multi-get "journals/2026-*.md" -l 40 # Batch fetch by glob +qmd multi-get notes/foo.md,notes/bar.md # Comma-separated list +``` + +## Query Types + +| Type | Method | Input | +|------|--------|-------| +| `lex` | BM25 | Keywords — exact terms, names, code | +| `vec` | Vector | Question — natural language | +| `hyde` | Vector | Answer — hypothetical result (50-100 words) | + +## Writing Good Queries + +**lex (keyword)** +- 2-5 terms, no filler words +- Exact phrase: `"connection pool"` (quoted) +- Exclude terms: `performance -sports` (minus prefix) +- Code identifiers work: `handleError async` + +**vec (semantic)** +- Full natural language question +- Be specific: `"how does the rate limiter handle burst traffic"` + +**hyde (hypothetical document)** +- Write 50-100 words of what the *answer* looks like +- Use the vocabulary you expect in the result + +**expand (auto-expand)** +- Single-line query (implicit) or `expand: question` +- Lets a local LLM generate lex/vec/hyde variations +- Do not mix with other typed lines + +## Combining Types + +| Goal | Approach | +|------|----------| +| Know exact terms | `lex` only | +| Don't know vocabulary | Single-line (implicit expand) or `vec` | +| Best recall | `lex` + `vec` | +| Complex topic | `lex` + `vec` + `hyde` | + +First query gets 2x weight — put your strongest signal first. + +## Lex Syntax + +| Syntax | Meaning | Example | +|--------|---------|---------| +| `term` | Prefix match | `perf` matches "performance" | +| `"phrase"` | Exact phrase | `"rate limiter"` | +| `-term` | Exclude | `performance -sports` | + +## Collection Filtering + +```bash +qmd query --collections docs "question" +qmd query --collections docs,notes "question" +``` + +Omit to search all collections. diff --git a/pi/.pi/web-search.json b/pi/.pi/web-search.json index e87ee95..3903eb9 100644 --- a/pi/.pi/web-search.json +++ b/pi/.pi/web-search.json @@ -1,3 +1,3 @@ { - "geminiApiKey": "AIzaSyAwY4Fyq9-9EvtjOqUAWP2TIYc02QxR9w8" + "provider": "auto" }