diff --git a/.pnpm-approve-builds.json b/.pnpm-approve-builds.json new file mode 100644 index 0000000..cde46e8 --- /dev/null +++ b/.pnpm-approve-builds.json @@ -0,0 +1 @@ +{"@google/genai": true, "koffi": true, "protobufjs": true} diff --git a/CLAUDE.md b/CLAUDE.md index d39c787..6677d33 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -6,7 +6,12 @@ This is the primary instruction file for Claude Code working on the MosaicIQ pro **Required:** ```bash -export PI_API_KEY=your_pi_api_key_here +export ANTHROPIC_API_KEY=sk-ant-... +# or another supported provider: +# export OPENAI_API_KEY=sk-... +# export DEEPSEEK_API_KEY=... +# export GOOGLE_API_KEY=... +# export ZAI_API_KEY=... ``` See **[agent.md](./agent.md)** for complete project documentation, including: @@ -51,7 +56,7 @@ local_research_app/ | LLM Client Reference | [agent.md # LLM Client Reference](./agent.md#llm-client-reference) | | Architecture | [docs/architecture.md](./docs/architecture.md) | | RPC Contracts | [`packages/contracts/src/rpc.ts`](./packages/contracts/src/rpc.ts) | -| Pi Client | [`packages/shared/src/llm/client.ts`](./packages/shared/src/llm/client.ts) | +| LLM Client | [`packages/shared/src/llm/piSdk.ts`](./packages/shared/src/llm/piSdk.ts) | ## Coding Style diff --git a/agent.md b/agent.md index ddbb10f..0beede4 100644 --- a/agent.md +++ b/agent.md @@ -6,21 +6,29 @@ Use the attached **MosaicIQ Design Document v3** as the primary reference for pr ## Required Environment Variables -**`PI_API_KEY` is required.** This application uses the Pi API (Inflection AI) for all LLM operations. The application will not function without this key set. +**An LLM provider API key is required.** This application uses the Pi Coding Agent SDK for all LLM operations. Set at least one of the following: ```bash -export PI_API_KEY=your_pi_api_key_here +export ANTHROPIC_API_KEY=sk-ant-... +# or +export OPENAI_API_KEY=sk-... +# or +export DEEPSEEK_API_KEY=... +# or +export GOOGLE_API_KEY=... +# or +export ZAI_API_KEY=... ``` ### API Details -- **Provider**: Pi (Inflection AI) -- **Base URL**: `https://api.pi.ai/v1/chat/completions` -- **Client**: Custom fetch-based client in `packages/shared/src/llm/client.ts` -- **Model**: `pi` (default) -- **Features**: Streaming and non-streaming completions, structured JSON responses +- **SDK**: `@earendil-works/pi-coding-agent` — multi-provider agent framework +- **Providers**: Anthropic, OpenAI, DeepSeek, Google Gemini, xAI/ZAI, and more +- **Client**: SDK-backed client in `packages/shared/src/llm/piSdk.ts` +- **Default model**: First available from configured provider (prefers Anthropic) +- **Features**: Streaming, tool use, structured output, multi-agent pipelines -All agent execution, memo generation, and AI-powered features depend on this API key. +All agent execution, memo generation, and AI-powered features depend on at least one provider API key. ## Core Direction diff --git a/apps/web/src/lib/styles.ts b/apps/web/src/lib/styles.ts index a563d8f..40e509b 100644 --- a/apps/web/src/lib/styles.ts +++ b/apps/web/src/lib/styles.ts @@ -27,7 +27,7 @@ export const ui = { rightPanel: "w-[300px] shrink-0 overflow-y-auto border-l border-[var(--border)] bg-[var(--surface)] p-5", center: "min-w-0 flex-1 overflow-y-auto p-8 pl-10 pr-10", spreadsheet: "w-full border-collapse bg-[var(--surface)] font-[var(--font-mono)] text-xs", - command: "flex items-center justify-between gap-6 border-t border-[var(--border)] bg-[var(--surface)] px-5", + command: "grid grid-cols-[1fr_2fr] items-center gap-6 border-t border-[var(--border)] bg-[var(--surface)] px-5", overlay: "fixed inset-0 z-[500] grid place-items-center bg-black/20", overlayWide: "fixed inset-0 z-[600] grid place-items-center bg-black/20", toast: "fixed top-14 right-4 z-[1000] flex flex-col gap-2 max-w-[380px] pointer-events-none", diff --git a/apps/web/src/ui/app/AppShell.tsx b/apps/web/src/ui/app/AppShell.tsx index e438f84..439c09e 100644 --- a/apps/web/src/ui/app/AppShell.tsx +++ b/apps/web/src/ui/app/AppShell.tsx @@ -68,6 +68,7 @@ type Toast = { }; type SettingsPanel = | "display" + | "llm" | "data-sources" | "agents" | "export" @@ -135,6 +136,9 @@ export function App() { const [agentRunIdsByAgent, setAgentRunIdsByAgent] = useState>({}); const [agentTrace, setAgentTrace] = useState([]); const [serverSettings, setServerSettings] = useState(null); + const [llmModels, setLlmModels] = useState>([]); + const [llmLoading, setLlmLoading] = useState(false); + const [selectedLlmModel, setSelectedLlmModel] = useState(""); const [keybindingsDraft, setKeybindingsDraft] = useState>({}); const [workspaceSection, setWorkspaceSection] = useState(null); const [workspaceSources, setWorkspaceSources] = useState([]); @@ -292,7 +296,13 @@ export function App() { setKeybindingsDraft(settings.keybindings ?? {}); } const server = await rpc.call("settings.get", { scope: "server" }); - if (server.ok) setServerSettings(server.data.settings as ServerSettings); + if (server.ok) { + const s = server.data.settings as ServerSettings; + setServerSettings(s); + if (s.llm?.provider && s.llm?.modelId) { + setSelectedLlmModel(`${s.llm.provider}/${s.llm.modelId}`); + } + } } void loadSettings(); }, []); @@ -315,6 +325,35 @@ export function App() { }); }, []); + // Load LLM models from backend + const loadLlmModels = useCallback(async () => { + setLlmLoading(true); + try { + const result = await rpc.call("model.list", undefined); + if (result.ok) setLlmModels(result.data.models); + } finally { + setLlmLoading(false); + } + }, []); + + // Persist LLM model selection + const handleLlmModelChange = useCallback(async (providerModel: string) => { + setSelectedLlmModel(providerModel); + const [provider, ...rest] = providerModel.split("/"); + const modelId = rest.join("/"); + const llmSettings = { provider, modelId }; + setServerSettings((prev) => { + const next = { ...(prev ?? { agentConfigs: {}, dataSources: {}, exportPipelines: {} }), llm: llmSettings } as ServerSettings; + return next; + }); + await rpc.call("settings.update", { + scope: "server", + changes: { llm: llmSettings }, + }); + // Also tell the backend model RPC so it takes effect immediately + await rpc.call("model.set", { provider, modelId }); + }, []); + useEffect(() => { let cancelled = false; async function load() { @@ -808,13 +847,20 @@ export function App() { open={settingsOpen} onClose={() => setSettingsOpen(false)} panel={settingsPanel} - onPanelChange={setSettingsPanel} + onPanelChange={(p) => { + setSettingsPanel(p); + if (p === "llm" && llmModels.length === 0) void loadLlmModels(); + }} theme={theme} onThemeChange={handleThemeChange} density={density} onDensityChange={handleDensityChange} serverSettings={serverSettings} keybindings={keybindingsDraft} + llmModels={llmModels} + llmLoading={llmLoading} + selectedLlmModel={selectedLlmModel} + onLlmModelChange={handleLlmModelChange} onServerSettingsChange={async (changes) => { const next = { ...(serverSettings ?? { agentConfigs: {}, dataSources: {}, exportPipelines: {} }), ...changes } as ServerSettings; setServerSettings(next); @@ -3012,6 +3058,10 @@ function SettingsOverlay({ onDensityChange, serverSettings, keybindings, + llmModels, + llmLoading, + selectedLlmModel, + onLlmModelChange, onServerSettingsChange, onKeybindingsChange, }: { @@ -3025,12 +3075,17 @@ function SettingsOverlay({ onDensityChange: (d: Density) => void; serverSettings: ServerSettings | null; keybindings: Record; + llmModels: Array<{ provider: string; modelId: string; name: string; available: boolean }>; + llmLoading: boolean; + selectedLlmModel: string; + onLlmModelChange: (providerModel: string) => void | Promise; onServerSettingsChange: (changes: Partial) => void | Promise; onKeybindingsChange: (keybindings: Record) => void | Promise; }) { if (!open) return null; const panels: Array<{ key: SettingsPanel; label: string }> = [ { key: "display", label: "Display" }, + { key: "llm", label: "LLM Provider" }, { key: "data-sources", label: "Data Sources" }, { key: "agents", label: "Agents" }, { key: "export", label: "Export" }, @@ -3106,6 +3161,88 @@ function SettingsOverlay({ )} + {panel === "llm" && ( + <> +

+ Select the LLM provider and model used for all agent operations. + Models marked as available have a configured API key. +

+ {llmLoading && ( +

Loading models…

+ )} + {!llmLoading && llmModels.length === 0 && ( +
+ +
+ )} + {!llmLoading && llmModels.length > 0 && ( +
+ {(() => { + // Group models by provider + const groups: Record = {}; + for (const m of llmModels) { + (groups[m.provider] ??= []).push(m); + } + return Object.entries(groups).map(([provider, models]) => ( +
+
+ {provider} +
+ {models.map((m) => { + const key = `${m.provider}/${m.modelId}`; + const selected = selectedLlmModel === key; + return ( + + ); + })} +
+ )); + })()} +
+ )} + {serverSettings?.llm && ( +
+

Current Selection

+

+ {serverSettings.llm.provider} + / + {serverSettings.llm.modelId} +

+
+ )} + + )} {panel === "data-sources" && ( <> {Object.entries(serverSettings?.dataSources ?? {}).map(([key, enabled]) => ( @@ -3841,7 +3978,7 @@ function CommandBar({ > -
+
> **Note:** "Pi" in "Pi Coding Agent SDK" is the name of the coding agent framework (the tool we use to build agents). It is NOT the same as the Pi/Inflection AI chatbot API (`api.pi.ai`). The SDK supports many LLM providers — we are removing the Inflection AI dependency entirely. + +The migration is structured as **7 phases**, each independently shippable. No phase should break existing functionality. + +--- + +## Phase 1: Install SDK & Create Adapter Layer + +**Goal:** Install the package and build a thin adapter that wraps the SDK, keeping the existing `llm/` API surface working. + +### Files to create/modify + +| Action | File | Description | +|--------|------|-------------| +| Install | `packages/shared/package.json` | Add `@earendil-works/pi-coding-agent` dependency | +| Create | `packages/shared/src/llm/piSdk.ts` | SDK initialization, session factory, auth setup | +| Modify | `packages/shared/src/llm/client.ts` | Replace raw `fetch` calls with SDK-backed implementation | + +### `packages/shared/src/llm/piSdk.ts` — SDK singleton + +```typescript +import { + AuthStorage, + createAgentSession, + ModelRegistry, + SessionManager, + SettingsManager, + DefaultResourceLoader, +} from "@earendil-works/pi-coding-agent"; + +// Lazy-initialized SDK state +let authStorage: AuthStorage; +let modelRegistry: ModelRegistry; + +export function getAuthStorage(): AuthStorage { + if (!authStorage) { + authStorage = AuthStorage.create(); + } + return authStorage; +} + +export function getModelRegistry(): ModelRegistry { + if (!modelRegistry) { + modelRegistry = ModelRegistry.create(getAuthStorage()); + } + return modelRegistry; +} + +export async function createSession(options?: { + systemPrompt?: string; + customTools?: any[]; + onEvent?: (event: any) => void; +}) { + const session = await createAgentSession({ + sessionManager: SessionManager.inMemory(), + authStorage: getAuthStorage(), + modelRegistry: getModelRegistry(), + settingsManager: SettingsManager.inMemory({ + compaction: { enabled: false }, // We manage context ourselves + retry: { enabled: true, maxRetries: 3 }, + }), + // No built-in tools for research agents — only custom tools + tools: [], + customTools: options?.customTools ?? [], + }); + + if (options?.onEvent) { + session.subscribe(options.onEvent); + } + + return session; +} + +export function isConfigured(): boolean { + // Check if any supported provider has an API key configured + return !!( + process.env.ANTHROPIC_API_KEY || + process.env.OPENAI_API_KEY || + process.env.DEEPSEEK_API_KEY || + process.env.GOOGLE_API_KEY + ); +} +``` + +### `packages/shared/src/llm/client.ts` — Updated to use SDK + +```typescript +// Keep the same exported API surface (streamResponse, complete, etc.) +// but delegate to SDK sessions internally + +export async function complete( + prompt: string, + options: CompletionOptions = {} +): Promise { + const session = await createSession({ + systemPrompt: options.systemPrompt, + }); + + let result = ""; + session.subscribe((event) => { + if (event.type === "message_update" && + event.assistantMessageEvent.type === "text_delta") { + result += event.assistantMessageEvent.delta; + } + }); + + await session.prompt(prompt); + return result; +} + +export async function streamResponse( + prompt: string, + options: StreamOptions & CompletionOptions = {} +): Promise { + const session = await createSession(); + let fullResponse = ""; + + session.subscribe((event) => { + if (event.type === "message_update" && + event.assistantMessageEvent.type === "text_delta") { + const delta = event.assistantMessageEvent.delta; + fullResponse += delta; + options.onProgress?.(delta); + } + }); + + await session.prompt(prompt); + return fullResponse; +} +``` + +### Acceptance criteria +- [ ] `pnpm install` succeeds with new SDK dependency +- [ ] Existing `agent.chat`, `agent.start`, `validation.run` RPC methods still work +- [ ] `ANTHROPIC_API_KEY` or `OPENAI_API_KEY` are picked up from env +- [ ] No references to `PI_API_KEY` or `api.pi.ai` remain +- [ ] Streaming still works end-to-end (RPC → event emitter → renderer) + +--- + +## Phase 2: Custom Tools for Database Access + +**Goal:** Build Pi SDK custom tools that give the LLM direct access to MosaicIQ's SQLite data. This replaces the "build context → embed in prompt" pattern with tool-based access. + +### Files to create + +| Action | File | Description | +|--------|------|-------------| +| Create | `packages/shared/src/tools/index.ts` | Tool barrel export | +| Create | `packages/shared/src/tools/companyTools.ts` | Company, filings, earnings tools | +| Create | `packages/shared/src/tools/modelTools.ts` | Financial model read/write tools | +| Create | `packages/shared/src/tools/memoTools.ts` | Memo read/write tools | +| Create | `packages/shared/src/tools/validationTools.ts` | Validation status tools | + +### `packages/shared/src/tools/companyTools.ts` — Example + +```typescript +import { Type } from "typebox"; +import { defineTool } from "@earendil-works/pi-coding-agent"; +import type { Db } from "../db/database.js"; +import { + getCompany, + listFilings, + getEarningsSchedule, +} from "../db/queries.js"; + +export function createCompanyTools(db: Db) { + const getCompanyInfo = defineTool({ + name: "get_company_info", + label: "Get Company Info", + description: "Get company details including name, ticker, sector, and price", + parameters: Type.Object({ + companyId: Type.String({ description: "Company ID or ticker" }), + }), + execute: async (_, params) => { + const company = getCompany(db, params.companyId); + if (!company) { + return { + content: [{ type: "text", text: `Company "${params.companyId}" not found.` }], + isError: true, + details: {}, + }; + } + return { + content: [{ type: "text", text: JSON.stringify(company, null, 2) }], + details: {}, + }; + }, + }); + + const getFilings = defineTool({ + name: "get_filings", + label: "Get SEC Filings", + description: "List SEC filings for a company (10-K, 10-Q, 8-K, etc.)", + parameters: Type.Object({ + companyId: Type.String({ description: "Company ID" }), + limit: Type.Optional(Type.Number({ description: "Max filings to return", default: 20 })), + }), + execute: async (_, params) => { + const filings = listFilings(db, params.companyId, params.limit ?? 20); + return { + content: [{ type: "text", text: JSON.stringify(filings, null, 2) }], + details: {}, + }; + }, + }); + + const getEarnings = defineTool({ + name: "get_earnings", + label: "Get Earnings Schedule", + description: "Get upcoming and past earnings dates and results", + parameters: Type.Object({ + companyId: Type.String({ description: "Company ID" }), + }), + execute: async (_, params) => { + const schedule = getEarningsSchedule(db, params.companyId); + return { + content: [{ type: "text", text: JSON.stringify(schedule, null, 2) }], + details: {}, + }; + }, + }); + + return [getCompanyInfo, getFilings, getEarnings]; +} +``` + +### `packages/shared/src/tools/modelTools.ts` + +```typescript +export function createModelTools(db: Db) { + const getModelData = defineTool({ + name: "get_financial_model", + label: "Get Financial Model", + description: "Get the financial model for a company (income, balance, operating tabs)", + parameters: Type.Object({ + companyId: Type.String(), + tab: Type.Union([Type.Literal("income"), Type.Literal("balance"), Type.Literal("operating")]), + }), + execute: async (_, params) => { + const model = getModel(db, params.companyId, params.tab); + return { + content: [{ type: "text", text: JSON.stringify(model, null, 2) }], + details: {}, + }; + }, + }); + + const updateModelCell = defineTool({ + name: "update_model_cell", + label: "Update Model Cell", + description: "Update a single cell in the financial model", + parameters: Type.Object({ + companyId: Type.String(), + tab: Type.String(), + row: Type.Number(), + col: Type.Number(), + value: Type.String(), + }), + execute: async (_, params) => { + const result = updateModelCell(db, params.companyId, params.tab, params.row, params.col, params.value); + return { + content: [{ type: "text", text: `Updated cell [${params.row}, ${params.col}] = ${params.value}` }], + details: result, + }; + }, + }); + + return [getModelData, updateModelCell]; +} +``` + +### `packages/shared/src/tools/memoTools.ts` + +```typescript +export function createMemoTools(db: Db) { + const getMemo = defineTool({ + name: "get_memo", + label: "Get Investment Memo", + description: "Get the full investment memo with all sections, annotations, and review status", + parameters: Type.Object({ + companyId: Type.String(), + }), + execute: async (_, params) => { + const memo = getMemo(db, params.companyId); + return { + content: [{ type: "text", text: JSON.stringify(memo, null, 2) }], + details: {}, + }; + }, + }); + + const updateMemoSection = defineTool({ + name: "update_memo_section", + label: "Update Memo Section", + description: "Update a specific section of the investment memo", + parameters: Type.Object({ + companyId: Type.String(), + sectionId: Type.String(), + content: Type.String({ description: "New section content (markdown)" }), + }), + execute: async (_, params) => { + const section = updateMemoSection(db, params.companyId, params.sectionId, { content: params.content }); + return { + content: [{ type: "text", text: `Updated section "${params.sectionId}"` }], + details: { section }, + }; + }, + }); + + return [getMemo, updateMemoSection]; +} +``` + +### `packages/shared/src/tools/index.ts` + +```typescript +import type { Db } from "../db/database.js"; +import { createCompanyTools } from "./companyTools.js"; +import { createModelTools } from "./modelTools.js"; +import { createMemoTools } from "./memoTools.js"; +import { createValidationTools } from "./validationTools.js"; + +export function createAllTools(db: Db) { + return [ + ...createCompanyTools(db), + ...createModelTools(db), + ...createMemoTools(db), + ...createValidationTools(db), + ]; +} + +export { createCompanyTools, createModelTools, createMemoTools, createValidationTools }; +``` + +### Acceptance criteria +- [ ] All tools have TypeBox schemas (parameters validated by SDK) +- [ ] Each tool has a clear description for the LLM +- [ ] Tools can access the DB (test with a simple session.prompt()) +- [ ] Error cases return `isError: true` + +--- + +## Phase 3: Migrate Agent Runner to SDK Sessions + +**Goal:** Rewrite `runner.ts` and `validationAgents.ts` to use SDK sessions with custom tools instead of the "build context → embed in prompt" pattern. + +### Files to modify + +| Action | File | Description | +|--------|------|-------------| +| Modify | `packages/shared/src/agents/runner.ts` | Use `createSession()` with custom tools | +| Modify | `packages/shared/src/agents/validationAgents.ts` | Use SDK sessions | +| Modify | `packages/shared/src/rpc/agentRpc.ts` | Pass streaming events through | + +### New `runner.ts` approach + +**Before (current):** +1. Build huge context object from DB +2. Interpolate context into a giant prompt string +3. Send prompt to LLM +4. Parse JSON from response + +**After (SDK):** +1. Create SDK session with company-specific custom tools +2. Send a focused prompt (the agent's instructions + user request) +3. LLM calls tools as needed to get data +4. LLM returns structured response + +```typescript +// New runner.ts (simplified) +export async function executeAgent( + db: Db, + agentId: string, + companyId: string, + options: AgentRunOptions = {} +): Promise { + const tools = createAllTools(db); + const metadata = getAgentMetadata(agentId); + const systemPrompt = getAgentSystemPrompt(agentId); // Just the role, not the data + + const session = await createSession({ + systemPrompt, + customTools: tools, + onEvent: (event) => { + // Forward SDK events to our EventEmitter + if (event.type === "message_update" && event.assistantMessageEvent.type === "text_delta") { + emitAgentStreaming(runId, agentId, companyId, event.assistantMessageEvent.delta); + } + if (event.type === "tool_execution_start") { + emitAgentProgress(runId, agentId, companyId, 50, `Running tool: ${event.toolName}`); + } + }, + }); + + // Focused prompt — the LLM will use tools to gather data + const prompt = `Analyze company ${companyId} using your available tools. ${options.userMessage ?? ""}`; + + await session.prompt(prompt); + + // Get the final assistant message + const messages = session.agent.state.messages; + const lastAssistant = messages.filter(m => m.role === "assistant").pop(); + + // ... store results, emit events, etc. +} +``` + +### Key insight: Agent prompts change + +The current `prompts.ts` embeds all data in the prompt. After migration, prompts become **instructions only** — the LLM fetches data via tools: + +```typescript +// BEFORE (current): Giant prompt with all data baked in +const prompt = `You are the SEC Filings Agent for ${ctx.company.name}. +Available Filings: +${ctx.filings.map(f => `- ${f.formType}: ${f.title}`).join("\n")} +...hundreds of lines of embedded data... +`; + +// AFTER: Focused system prompt, tools provide data +const systemPrompt = `You are the SEC Filings Agent. +Use the get_filings and get_company_info tools to gather data. +Extract segment revenue, identify risk factors, and note accounting changes. +Cite specific sections when making claims. +Respond with structured JSON.`; + +// The LLM calls tools as needed during generation +``` + +### Acceptance criteria +- [ ] `agent.start` RPC works end-to-end with SDK +- [ ] `agent.chat` RPC works with SDK (streaming response) +- [ ] Tool calls appear in agent progress events +- [ ] Validation agents (sv, qa, rt) still return `ValidationResult` +- [ ] Pipeline executor still works + +--- + +## Phase 4: Excel/File Custom Tools + +**Goal:** Add custom tools for reading and writing Excel, CSV, and PDF files. The Pi SDK's built-in `read` tool only handles text and images. + +### Files to create + +| Action | File | Description | +|--------|------|-------------| +| Create | `packages/shared/src/tools/fileTools.ts` | Excel read/write, CSV, PDF tools | +| Modify | `packages/shared/src/tools/index.ts` | Export new tools | + +### `packages/shared/src/tools/fileTools.ts` + +```typescript +import ExcelJS from "exceljs"; +import { Type } from "typebox"; +import { defineTool } from "@earendil-works/pi-coding-agent"; + +export function createFileTools() { + const readExcel = defineTool({ + name: "read_excel", + label: "Read Excel File", + description: "Read an Excel (.xlsx) file and return sheet data as JSON. Supports sheet selection and row ranges.", + parameters: Type.Object({ + path: Type.String({ description: "Path to .xlsx file" }), + sheetName: Type.Optional(Type.String({ description: "Sheet name (default: first sheet)" })), + maxRows: Type.Optional(Type.Number({ description: "Max rows to return (default: 100)" })), + }), + execute: async (_, params) => { + const workbook = new ExcelJS.Workbook(); + await workbook.xlsx.readFile(params.path); + const sheet = params.sheetName + ? workbook.getWorksheet(params.sheetName) + : workbook.worksheets[0]; + + if (!sheet) { + return { + content: [{ type: "text", text: `Sheet not found: ${params.sheetName ?? "(first sheet)"}` }], + isError: true, + details: {}, + }; + } + + const rows: unknown[] = []; + const headers: string[] = []; + const limit = params.maxRows ?? 100; + + sheet.eachRow((row, rowNumber) => { + if (rowNumber === 1) { + row.eachCell((cell) => headers.push(String(cell.value ?? ""))); + } else if (rowNumber <= limit + 1) { + const values: Record = {}; + row.eachCell((cell, colNumber) => { + values[headers[colNumber - 1] ?? `col_${colNumber}`] = cell.value; + }); + rows.push(values); + } + }); + + return { + content: [{ + type: "text", + text: JSON.stringify({ sheetName: sheet.name, headers, rowCount: rows.length, rows }, null, 2), + }], + details: {}, + }; + }, + }); + + const writeExcel = defineTool({ + name: "write_excel", + label: "Write Excel File", + description: "Write structured data to a new Excel (.xlsx) file", + parameters: Type.Object({ + path: Type.String({ description: "Output file path" }), + sheetName: Type.String({ description: "Sheet name", default: "Sheet1" }), + data: Type.Array(Type.Record(Type.String(), Type.Unknown()), { + description: "Array of row objects", + }), + }), + execute: async (_, params) => { + const workbook = new ExcelJS.Workbook(); + const sheet = workbook.addWorksheet(params.sheetName); + + if (params.data.length > 0) { + const headers = Object.keys(params.data[0]); + sheet.addRow(headers); + for (const row of params.data) { + sheet.addRow(headers.map(h => row[h] ?? "")); + } + } + + await workbook.xlsx.writeFile(params.path); + + return { + content: [{ type: "text", text: `Wrote ${params.data.length} rows to ${params.path}` }], + details: {}, + }; + }, + }); + + const readCsv = defineTool({ + name: "read_csv", + label: "Read CSV File", + description: "Parse a CSV file and return rows as JSON", + parameters: Type.Object({ + path: Type.String({ description: "Path to CSV file" }), + delimiter: Type.Optional(Type.String({ description: "Delimiter (default: comma)", default: "," })), + maxRows: Type.Optional(Type.Number({ description: "Max rows (default: 500)" })), + }), + execute: async (_, params) => { + const fs = await import("fs/promises"); + const content = await fs.readFile(params.path, "utf-8"); + const delimiter = params.delimiter ?? ","; + const lines = content.trim().split("\n"); + const headers = lines[0].split(delimiter).map(h => h.trim().replace(/^"|"$/g, "")); + const maxRows = params.maxRows ?? 500; + const rows = lines.slice(1, maxRows + 1).map(line => { + const values = line.split(delimiter).map(v => v.trim().replace(/^"|"$/g, "")); + const obj: Record = {}; + headers.forEach((h, i) => { obj[h] = values[i] ?? ""; }); + return obj; + }); + + return { + content: [{ type: "text", text: JSON.stringify({ headers, rowCount: rows.length, rows }, null, 2) }], + details: {}, + }; + }, + }); + + return [readExcel, writeExcel, readCsv]; +} +``` + +### Acceptance criteria +- [ ] `read_excel` reads `.xlsx` files and returns JSON +- [ ] `write_excel` creates `.xlsx` files from structured data +- [ ] `read_csv` parses CSV files +- [ ] Tools are available in agent sessions +- [ ] Export pipeline can be triggered via agent (export agent can write Excel) + +--- + +## Phase 5: Multi-Model Support & Model Configuration + +**Goal:** Expose the SDK's multi-model capabilities through the existing RPC layer. Let users switch between providers/models. + +### Files to modify + +| Action | File | Description | +|--------|------|-------------| +| Modify | `packages/shared/src/llm/piSdk.ts` | Expose `ModelRegistry`, model switching | +| Modify | `packages/shared/src/rpc/modelRpc.ts` | Add model listing/switching RPC methods | +| Modify | `packages/contracts/src/rpc.ts` | Add new RPC methods for model management | +| Modify | `packages/shared/src/rpc/settingsRpc.ts` | Persist model preferences | + +### New RPC methods + +```typescript +// Add to contracts/rpc.ts +"model.list": undefined; +"model.set": { provider: string; modelId: string }; +"model.cycle": undefined; +``` + +### Settings integration + +```typescript +// Store model preference in settings +interface ServerSettings { + // ...existing... + llm: { + provider: string; + modelId: string; + thinkingLevel: "off" | "minimal" | "low" | "medium" | "high"; + }; +} +``` + +### Acceptance criteria +- [ ] `model.list` returns available models with valid API keys +- [ ] `model.set` switches the active model for all agents +- [ ] Model preference persists across restarts +- [ ] Thinking level is configurable +- [ ] Works with Anthropic, OpenAI, and other providers + +--- + +## Phase 6: Improved Streaming & Event Bridge + +**Goal:** Connect SDK's event system to the existing EventEmitter → IPC → React pipeline properly. Add abort support. + +### Files to modify + +| Action | File | Description | +|--------|------|-------------| +| Modify | `packages/shared/src/agents/eventEmitter.ts` | Add abort event types | +| Modify | `packages/shared/src/agents/runner.ts` | Forward SDK events to our EventEmitter | +| Modify | `apps/desktop/src/main.ts` | Handle new event types | +| Modify | `apps/web/src/hooks/useServerEvents.ts` | Handle tool_execution events | +| Modify | `packages/contracts/src/rpc.ts` | Add tool execution event types | + +### New event types to bridge + +```typescript +// SDK events → MosaicIQ events +"tool_execution_start" → "agent.progress" (with tool name) +"tool_execution_update" → "agent.streaming" (tool output) +"tool_execution_end" → "agent.progress" (tool complete) +"message_update" → "agent.streaming" (text delta) +"agent_start" → "agent.started" +"agent_end" → "agent.completed" +``` + +### Abort support + +```typescript +// In runner.ts +const controller = new AbortController(); + +// Store for cancellation +activeSessions.set(runId, { session, controller }); + +// In agent.pause RPC handler +const active = activeSessions.get(runId); +if (active) { + await active.session.abort(); +} +``` + +### Acceptance criteria +- [ ] Tool calls appear in real-time in the UI +- [ ] Text streaming works without character loss +- [ ] Agent can be aborted mid-run +- [ ] Error events are surfaced to the UI +- [ ] Queue (steer/follow-up) works for chat + +--- + +## Phase 7: System Prompts & Agent Specialization + +**Goal:** Refactor agent prompts from "data-embedded" to "instruction-only" now that tools provide data. Add proper system prompt management. + +### Files to modify + +| Action | File | Description | +|--------|------|-------------| +| Modify | `packages/shared/src/llm/prompts.ts` | Rewrite all agent prompts as instructions | +| Modify | `packages/shared/src/llm/context.ts` | Simplify (tools handle data now) | +| Create | `packages/shared/src/llm/systemPrompts.ts` | Centralized system prompt management | + +### Prompt refactoring example + +```typescript +// BEFORE: Data embedded (current) +const AGENT_PROMPTS = { + sf: (ctx) => `You are the SEC Filings Agent for ${ctx.company.name} (${ctx.company.ticker}). +Available Filings: +${ctx.filings.map(f => `- ${f.formType}: ${f.title} (${f.filedDate})`).join("\n")} +... 50+ lines of data ...`, + +// AFTER: Instruction-only with tool access +const SYSTEM_PROMPTS = { + sf: `You are the SEC Filings Agent for MosaicIQ equity research. +Your role: Analyze SEC filings and extract structured financial information. + +TOOLS AVAILABLE: +- get_company_info: Get company details +- get_filings: List SEC filings +- get_financial_model: Get financial model data + +WORKFLOW: +1. Use get_company_info to identify the company +2. Use get_filings to retrieve recent filings +3. Analyze the filings for: + - Segment revenue data from 10-K + - Key risk factors + - Changes in accounting policies +4. Cite specific sections when making claims + +OUTPUT: +Respond with structured JSON containing: +- extractedData: Key findings +- sources: Filing references with sections +- assumptions: Any assumptions made +- confidence: "high" | "medium" | "low" + +RULES: +- Always cite sources +- Flag uncertainty +- Never fabricate filing content`, +}; +``` + +### Acceptance criteria +- [ ] All 15 agent prompts rewritten as instruction-only +- [ ] Agents successfully use tools to fetch data instead of relying on embedded context +- [ ] Responses maintain same quality and structure +- [ ] `context.ts` simplified (no more giant context builders) + +--- + +## Migration Strategy + +### Order of execution + +1. **Phase 1** → Non-breaking. SDK installed alongside existing client. +2. **Phase 2** → Additive. New tools, nothing removed. +3. **Phase 3** → **Breaking change point.** Runner switches to SDK. + - Before merging: run full manual test of all agent RPCs +4. **Phase 4** → Additive. File tools enhance export capabilities. +5. **Phase 5** → Additive. New RPC methods, existing ones unchanged. +6. **Phase 6** → Enhancement. Better streaming, abort support. +7. **Phase 7** → Refactor. Prompts rewritten after everything works. + +### What to delete after migration + +After Phase 3 is verified: +- **Delete `client.ts` entirely** — remove `PI_API_URL`, `PI_API_KEY` references, raw `fetch` calls +- Remove `streamStructuredResponse` / `completeStructured` (the SDK handles structured output better via tool responses) +- Simplify `context.ts` (no more `buildAgentContext` with all data) +- Remove `PI_API_KEY` from `agent.md`, `CLAUDE.md`, and all documentation +- Update error messages in `runner.ts` and `agentRpc.ts` (currently say "Set PI_API_KEY") + +After Phase 7: +- Remove `AgentContext` type from `prompts.ts` (no longer needed) +- Remove `buildAgentContext`, `buildChatContext`, `buildMinimalContext` from `context.ts` + +### Files to delete (eventually) + +| File | When | Why | +|------|------|-----| +| `packages/shared/src/llm/client.ts` | Phase 3 complete | Replaced by `piSdk.ts` | +| `packages/shared/src/llm/context.ts` | Phase 7 complete | Tools replace context building | + +### Risk mitigation + +1. **Keep old client as fallback**: During Phase 1-2, keep the old `client.ts` working. Add a feature flag or env var to switch between old and new. +2. **Test with one agent first**: Migrate `agent.chat` first (simplest), verify end-to-end, then migrate the rest. +3. **Type safety**: The SDK is fully typed. Every tool, event, and session method is typed. Use this. +4. **Electron compatibility**: Test that `@earendil-works/pi-coding-agent` works in Electron's main process (Node.js). It should — it's a Node.js package. + +--- + +## Dependency Changes + +### `packages/shared/package.json` + +```diff + "dependencies": { + "@mosaiciq/contracts": "workspace:*", ++ "@earendil-works/pi-coding-agent": "^latest", + "better-sqlite3": "^12.10.0", + "exceljs": "^4.4.0", + "pptxgenjs": "^4.0.1" + } +``` + +Note: The SDK brings in its own dependencies (typebox, etc.). These should not conflict with existing deps. + +### Environment variables + +```bash +# Supported providers (SDK picks up automatically) +ANTHROPIC_API_KEY=sk-ant-... +OPENAI_API_KEY=sk-... +DEEPSEEK_API_KEY=... +GOOGLE_API_KEY=... + +# Or configure via settings file (Phase 5) +``` + +> **Removed:** `PI_API_KEY` and the `api.pi.ai` endpoint are no longer used. + +--- + +## Estimated Effort + +| Phase | Description | Effort | +|-------|-------------|--------| +| 1 | SDK install + adapter layer | 1-2 hours | +| 2 | Custom DB tools | 2-3 hours | +| 3 | Runner migration (breaking) | 3-4 hours | +| 4 | File tools (Excel, CSV) | 1-2 hours | +| 5 | Multi-model support | 2-3 hours | +| 6 | Streaming & event bridge | 2-3 hours | +| 7 | Prompt refactoring | 2-3 hours | +| **Total** | | **13-20 hours** | + +Phase 3 is the critical path. Everything before it is additive; everything after it is polish. diff --git a/package.json b/package.json index da2884d..ff7981d 100644 --- a/package.json +++ b/package.json @@ -45,4 +45,4 @@ "vitest": "^3.2.4", "wait-on": "^8.0.3" } -} +} \ No newline at end of file diff --git a/packages/contracts/src/rpc.ts b/packages/contracts/src/rpc.ts index f1d59c5..2ed0db0 100644 --- a/packages/contracts/src/rpc.ts +++ b/packages/contracts/src/rpc.ts @@ -204,6 +204,8 @@ export type RpcRequestMap = { "earnings.getSchedule": { companyId: string }; "filing.list": { companyId: string; since?: string }; "model.get": { companyId: string; tab: string }; + "model.list": undefined; + "model.set": { provider: string; modelId: string; thinkingLevel?: "off" | "minimal" | "low" | "medium" | "high" }; "model.updateCell": { companyId: string; tab: string; row: number; col: number; value: string }; "model.createRow": { companyId: string; tab: string; label: string; kind: "actual" | "forecast" | "total"; values?: string[] }; "model.deleteRow": { companyId: string; tab: string; row: number }; @@ -267,6 +269,8 @@ export type RpcResponseMap = { "earnings.getSchedule": { schedule: EarningsSchedule[] }; "filing.list": { filings: Filing[] }; "model.get": { headers: string[]; rows: ModelRow[] }; + "model.list": { models: Array<{ provider: string; modelId: string; name: string; available: boolean }> }; + "model.set": { ok: boolean; provider: string; modelId: string }; "model.updateCell": { ok: boolean; affectedCells: string[] }; "model.createRow": { row: ModelRow; position: number }; "model.deleteRow": { ok: boolean }; diff --git a/packages/contracts/src/rpcSchemas.ts b/packages/contracts/src/rpcSchemas.ts index 50d38a1..ac9a045 100644 --- a/packages/contracts/src/rpcSchemas.ts +++ b/packages/contracts/src/rpcSchemas.ts @@ -40,6 +40,11 @@ export const ServerSettingsSchema = z.object({ agentConfigs: z.record(z.unknown()), dataSources: z.record(z.boolean()), exportPipelines: z.record(z.unknown()), + llm: z.object({ + provider: z.string().optional(), + modelId: z.string().optional(), + thinkingLevel: z.enum(["off", "minimal", "low", "medium", "high"]).optional(), + }).optional(), }); const RiskInputSchema = RiskSchema.omit({ id: true, companyId: true }); @@ -65,6 +70,8 @@ export const RpcRequestSchemas = { "earnings.getSchedule": z.object({ companyId: idString }), "filing.list": z.object({ companyId: idString, since: z.string().optional() }), "model.get": z.object({ companyId: idString, tab: nonEmptyString }), + "model.list": z.undefined(), + "model.set": z.object({ provider: nonEmptyString, modelId: nonEmptyString, thinkingLevel: z.enum(["off", "minimal", "low", "medium", "high"]).optional() }), "model.updateCell": z.object({ companyId: idString, tab: nonEmptyString, @@ -158,6 +165,8 @@ export const RpcResponseSchemas = { "earnings.getSchedule": z.object({ schedule: z.array(EarningsScheduleSchema) }), "filing.list": z.object({ filings: z.array(FilingSchema) }), "model.get": z.object({ headers: z.array(z.string()), rows: z.array(ModelRowSchema) }), + "model.list": z.object({ models: z.array(z.object({ provider: z.string(), modelId: z.string(), name: z.string(), available: z.boolean() })) }), + "model.set": z.object({ ok: z.boolean(), provider: z.string(), modelId: z.string() }), "model.updateCell": z.object({ ok: z.boolean(), affectedCells: z.array(z.string()) }), "model.createRow": z.object({ row: ModelRowSchema, position: z.number().int().min(0) }), "model.deleteRow": z.object({ ok: z.boolean() }), diff --git a/packages/shared/package.json b/packages/shared/package.json index 52d4bad..e01664f 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -25,10 +25,12 @@ } }, "dependencies": { + "@earendil-works/pi-coding-agent": "^0.74.0", "@mosaiciq/contracts": "workspace:*", "better-sqlite3": "^12.10.0", "exceljs": "^4.4.0", - "pptxgenjs": "^4.0.1" + "pptxgenjs": "^4.0.1", + "typebox": "^1.1.38" }, "devDependencies": { "@types/better-sqlite3": "^7.6.13" diff --git a/packages/shared/src/agents/runner.ts b/packages/shared/src/agents/runner.ts index 5d1ccef..88c25e9 100644 --- a/packages/shared/src/agents/runner.ts +++ b/packages/shared/src/agents/runner.ts @@ -1,12 +1,15 @@ /** * Individual agent execution * Runs a single agent with proper context and error handling + * + * Now uses the Pi Coding Agent SDK for LLM interaction with custom tools + * instead of the old "build context → embed in prompt" pattern. */ import type { Db } from "../db/database.js"; -import { streamResponse, streamStructuredResponse, isConfigured } from "../llm/client.js"; -import { getAgentPrompt, getAgentMetadata } from "../llm/prompts.js"; -import { buildAgentContext } from "../llm/context.js"; +import { createSession, registerSession, unregisterSession, isConfigured as sdkIsConfigured } from "../llm/piSdk.js"; +import { getAgentMetadata } from "../llm/prompts.js"; +import { createAllTools } from "../tools/index.js"; import { emitAgentProgress, emitAgentStarted, @@ -14,13 +17,21 @@ import { emitAgentFailed, emitAgentStreaming, } from "./eventEmitter.js"; +import { + addAgentRunStep, + createAgentRun, + getServerSettings, + listAgentRunSteps, + pauseAgent, + storeAgentOutput, + updateAgentRunCompletion, +} from "../db/queries.js"; export interface AgentRunOptions { runId?: string; onProgress?: (progress: number, action: string) => void; signal?: AbortSignal; - structured?: boolean; - schema?: Record; + userMessage?: string; } export interface AgentRunResult { @@ -36,31 +47,185 @@ export interface AgentRunResult { } /** - * Execute a single agent + * Agent system prompts — instruction-only, no data embedded. + * The LLM fetches data via custom tools. + */ +const AGENT_SYSTEM_PROMPTS: Record = { + sf: `You are the SEC Filings Agent for MosaicIQ equity research. +Your role: Analyze SEC filings and extract structured financial information. + +Use the available tools to gather data: +- get_company_info: Get company details +- get_filings: List SEC filings (10-K, 10-Q, 8-K, etc.) + +Workflow: +1. Use get_company_info to identify the company +2. Use get_filings to retrieve recent filings +3. Analyze the filings for segment revenue data, risk factors, accounting policy changes +4. Cite specific sections when making claims + +Output JSON with: +- extractedData: Key findings from filings +- sources: List of filings and sections referenced +- assumptions: Any assumptions made during analysis +- confidence: "high" | "medium" | "low" + +Rules: Always cite sources. Flag uncertainty. Never fabricate filing content.`, + + cr: `You are the Company Research Agent for MosaicIQ equity research. +Analyze the company's business model and competitive position. + +Use tools to gather data about the company, filings, earnings, catalysts, and risks. +Provide a structured analysis covering: +- businessModel: How the company makes money +- competitiveAdvantages: Key advantages +- marketPosition: Assessment of market position +- recentDevelopments: Important recent changes +- sources: Citations for your claims`, + + fm: `You are the Financial Modeling Agent for MosaicIQ equity research. +Build and analyze financial projections for the company. + +Use tools to get the financial model, company info, filings, and earnings data. +1. Review historical data from the model +2. Project revenue for the next 3 years +3. Model key margin drivers +4. Document assumptions clearly +5. Flag sensitive assumptions + +If the model needs updates, use update_model_cell or create_model_row tools. + +Output JSON with: projections, keyAssumptions, sensitivityAnalysis, confidence.`, + + va: `You are the Valuation Agent for MosaicIQ equity research. +Provide a comprehensive valuation analysis. + +Use tools to get the memo, financial model, and company data. +1. Apply appropriate valuation methodologies (DCF, multiples, etc.) +2. Provide a fair value estimate with range +3. Identify key value drivers +4. Discuss upside/downside scenarios + +Output JSON with: methodologies, fairValue, keyDrivers, scenarios, assumptions.`, + + mw: `You are the Memo Writing Agent for MosaicIQ equity research. +Synthesize research into a clear investment memo. + +Use tools to get the current memo, company data, filings, and model. +Review and refine the thesis statement. Ensure coherence across sections. +If improvements are needed, use update_memo_section to revise content. + +Output JSON with: refinedSections, suggestions, consistencyCheck, overallQuality.`, + + pa: `You are the Presentation Agent for MosaicIQ equity research. +Create investment committee presentation materials. + +Use tools to get the memo and company data. +Structure presentation for investment committee. Create slide outlines. + +Output JSON with: slideOutline, keyCharts, talkingPoints, timing.`, + + ec: `You are the Earnings Call Agent for MosaicIQ equity research. +Analyze earnings call data for insights. + +Use tools to get earnings schedule, filings, and company data. +Extract management guidance, tone changes, and key Q&A insights. + +Output JSON with: guidance, keyInsights, toneAnalysis, qAHighlights, actionItems.`, + + ci: `You are the Competitive Intelligence Agent for MosaicIQ equity research. +Analyze competitive positioning and peer dynamics. + +Use tools to get company data, risks, and alerts. +Identify key competitors, assess market share trends. + +Output JSON with: competitors, marketPosition, recentMoves, threats, opportunities.`, + + rk: `You are the Risk Assessment Agent for MosaicIQ equity research. +Identify and analyze key investment risks. + +Use tools to get the memo, filings, risks, and alerts. +Categorize risks by type, assess likelihood and impact. + +Output JSON with: risks, categories, highPriorityRisks, mitigationStrategies.`, + + rt: `You are the Red Team Agent for MosaicIQ equity research. +Challenge the investment thesis and identify weaknesses. + +Use tools to get the memo (especially thesis section), model, risks, and alerts. +1. Identify weaknesses in the thesis +2. Uncover unstated assumptions +3. Develop bear case scenarios +4. Challenge key projections + +Output JSON with: counterArguments, unstatedAssumptions, bearCase, redFlags, recommendation.`, + + mn: `You are the Monitoring Agent for MosaicIQ equity research. +Set up ongoing monitoring of key indicators for the company. + +Use tools to get company data, catalysts, risks, and alerts. +Identify key metrics to monitor with thresholds. + +Output JSON with: metricsToMonitor, alertConditions, dataSources, monitoringCadence.`, + + sv: `You are the Source Verification Agent for MosaicIQ equity research. +Verify the accuracy of citations and sources in the memo. + +Use tools to get the memo, filings, and validation status. +Check that claims are properly cited. Flag uncited assertions. + +Output JSON with: verificationResults, flaggedClaims, sourceQuality, recommendations.`, + + ex: `You are the Export Agent for MosaicIQ equity research. +Prepare data for export in various formats. + +Use tools to get memo, model, and company data. +If writing Excel files, use the write_excel tool. + +Output JSON with: pdfReady, excelReady, pptReady, metadata.`, + + qa: `You are the Model QA Agent for MosaicIQ equity research. +Audit the financial model for errors and inconsistencies. + +Use tools to get the financial model, company data, and filings. +Check for formula errors, verify consistency, run sanity checks. + +Output JSON with: formulaErrors, consistencyIssues, sanityCheckResults, recommendations, overallStatus.`, + + chat: `You are a research assistant for MosaicIQ equity research. +Help the analyst with questions about the company. + +Use the available tools to look up company data, filings, model, memo, earnings, etc. +Answer questions based on available data. Cite sources when possible. +Admit when you don't have enough information. +Respond in a helpful, professional tone.`, +}; + +/** + * Get the system prompt for an agent + */ +function getAgentSystemPrompt(agentId: string): string { + return AGENT_SYSTEM_PROMPTS[agentId] ?? AGENT_SYSTEM_PROMPTS.chat; +} + +/** + * Execute a single agent using the Pi SDK */ export async function executeAgent( db: Db, agentId: string, companyId: string, - options: AgentRunOptions = {} + options: AgentRunOptions = {}, ): Promise { - const { - onProgress, - signal, - structured = false, - schema, - } = options; - - const runId = options.runId ?? `run-${Date.now()}`; + const { onProgress, userMessage } = options; + const runId = options.runId ?? `run-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`; const startedAt = new Date().toISOString(); // Check if LLM is configured - if (!isConfigured()) { - const errorMsg = "LLM API key is not configured. Please set PI_API_KEY."; + if (!sdkIsConfigured()) { + const errorMsg = "LLM API key is not configured. Set ANTHROPIC_API_KEY or another supported provider key."; console.error(`[Agent] ${errorMsg}`); - - // Update agent run status in database - updateAgentRunStatus(db, runId, agentId, companyId, "failed", errorMsg); + updateAgentRunCompletion(db, runId, "failed", "Configuration error", errorMsg); return { runId, @@ -76,61 +241,88 @@ export async function executeAgent( } try { - // Update agent run status to running - updateAgentRunStatus(db, runId, agentId, companyId, "running"); - - // Get agent metadata const metadata = getAgentMetadata(agentId); const action = metadata?.description || "Running agent"; + // Update agent run status to running + updateAgentRunCompletion(db, runId, "running", action); + // Emit agent started event emitAgentStarted(runId, agentId, companyId); - onProgress?.(0, action); emitAgentProgress(runId, agentId, companyId, 0, action); - // Build agent context - const context = await buildAgentContext(db, agentId, companyId, { - includeHistoricalData: true, - includeModel: true, + // Create custom tools with DB access for this agent + const tools = createAllTools(db); + const llmSettings = getServerSettings(db).llm; + + // Create SDK session with tools + const session = await createSession({ + systemPrompt: getAgentSystemPrompt(agentId), + modelSelection: llmSettings?.provider && llmSettings.modelId + ? { + provider: llmSettings.provider, + modelId: llmSettings.modelId, + thinkingLevel: llmSettings.thinkingLevel, + } + : undefined, + customTools: tools, + onEvent: (event) => { + // Bridge SDK events to our EventEmitter + switch (event.type) { + case "message_update": + if (event.assistantMessageEvent.type === "text_delta") { + emitAgentStreaming(runId, agentId, companyId, event.assistantMessageEvent.delta); + } + break; + case "tool_execution_start": + emitAgentProgress(runId, agentId, companyId, 50, `Running tool: ${event.toolName}`); + onProgress?.(50, `Running tool: ${event.toolName}`); + break; + case "tool_execution_end": + emitAgentProgress(runId, agentId, companyId, 70, `Tool complete: ${event.toolName}`); + break; + case "agent_start": + emitAgentProgress(runId, agentId, companyId, 20, `Executing ${action}`); + onProgress?.(20, `Executing ${action}`); + break; + case "agent_end": + emitAgentProgress(runId, agentId, companyId, 90, `Finalizing ${action}`); + onProgress?.(90, `Finalizing ${action}`); + break; + } + }, }); - onProgress?.(20, `Gathering data for ${action}`); - emitAgentProgress(runId, agentId, companyId, 20, `Gathering data for ${action}`); + // Register for abort/cancellation support + registerSession(runId, session); - // Get agent prompt - const prompt = getAgentPrompt(agentId, context); + // Build the prompt — focused, not data-heavy + const prompt = userMessage + ? `Analyze company "${companyId}". ${userMessage}` + : `Analyze company "${companyId}" using your available tools. Provide a comprehensive analysis with structured output.`; - onProgress?.(40, `Executing ${action}`); - emitAgentProgress(runId, agentId, companyId, 40, `Executing ${action}`); + // Send prompt and wait for completion + await session.prompt(prompt); + + // Extract the response + const messages = session.agent.state.messages; + const lastAssistant = messages.filter((m) => m.role === "assistant").pop(); - // Execute LLM call let rawResponse = ""; let output: unknown; - if (structured && schema) { - output = await streamStructuredResponse(prompt, schema, { - onProgress: (text) => { - rawResponse += text; - // Emit streaming event for live output - emitAgentStreaming(runId, agentId, companyId, text); - const progress = 40 + Math.min(40, (rawResponse.length / 4000) * 40); - onProgress?.(progress, `Processing ${action}`); - emitAgentProgress(runId, agentId, companyId, progress, `Processing ${action}`); - }, - signal, - }); - } else { - rawResponse = await streamResponse(prompt, { - onProgress: (text) => { - // Emit streaming event for live output - emitAgentStreaming(runId, agentId, companyId, text); - const progress = 40 + Math.min(40, (rawResponse.length / 4000) * 40); - onProgress?.(progress, `Processing ${action}`); - emitAgentProgress(runId, agentId, companyId, progress, `Processing ${action}`); - }, - signal, - }); + if (lastAssistant && lastAssistant.content) { + // Extract text from content blocks + if (typeof lastAssistant.content === "string") { + rawResponse = lastAssistant.content; + } else if (Array.isArray(lastAssistant.content)) { + for (const block of lastAssistant.content) { + if (block.type === "text") { + rawResponse += block.text; + } + } + } // Try to parse as JSON try { @@ -140,24 +332,20 @@ export async function executeAgent( } } - onProgress?.(90, `Finalizing ${action}`); - emitAgentProgress(runId, agentId, companyId, 90, `Finalizing ${action}`); - - // Store agent output in database + // Store agent output storeAgentOutput(db, runId, agentId, companyId, output, rawResponse); + updateAgentRunCompletion(db, runId, "completed", `Completed ${action}`); - // Update agent run status to completed - updateAgentRunStatus(db, runId, agentId, companyId, "completed"); - - onProgress?.(100, `Completed ${action}`); emitAgentProgress(runId, agentId, companyId, 100, `Completed ${action}`); + onProgress?.(100, `Completed ${action}`); - // Calculate duration const duration = Date.now() - new Date(startedAt).getTime(); - - // Emit completion event emitAgentCompleted(runId, agentId, companyId, output, duration); + // Unregister session + unregisterSession(runId); + session.dispose(); + return { runId, agentId, @@ -172,9 +360,11 @@ export async function executeAgent( const errorMsg = error instanceof Error ? error.message : String(error); // Check if cancelled - if (signal?.aborted) { - updateAgentRunStatus(db, runId, agentId, companyId, "cancelled"); + if (options.signal?.aborted) { + updateAgentRunCompletion(db, runId, "cancelled", "Cancelled by user"); emitAgentFailed(runId, agentId, companyId, "Cancelled by user"); + unregisterSession(runId); + return { runId, agentId, @@ -188,11 +378,9 @@ export async function executeAgent( }; } - // Update agent run status to failed - updateAgentRunStatus(db, runId, agentId, companyId, "failed", errorMsg); - - // Emit failed event + updateAgentRunCompletion(db, runId, "failed", "Agent failed", errorMsg); emitAgentFailed(runId, agentId, companyId, errorMsg); + unregisterSession(runId); return { runId, @@ -208,59 +396,6 @@ export async function executeAgent( } } -/** - * Update agent run status in database - */ -function updateAgentRunStatus( - db: Db, - runId: string, - agentId: string, - companyId: string, - status: "running" | "completed" | "failed" | "cancelled", - error?: string -): void { - const stmt = db.prepare(` - INSERT INTO agent_runs (id, agent_id, company_id, status, started_at${error ? ", error" : ""}) - VALUES (?, ?, ?, ?, datetime('now')${error ? ", ?" : ""}) - ON CONFLICT(id) DO UPDATE SET - status = excluded.status, - completed_at = CASE WHEN excluded.status IN ('completed', 'failed', 'cancelled') THEN datetime('now') ELSE completed_at END, - error = COALESCE(excluded.error, error) - `); - - if (error) { - stmt.run(runId, agentId, companyId, status, error); - } else { - stmt.run(runId, agentId, companyId, status); - } -} - -/** - * Store agent output in database - */ -function storeAgentOutput( - db: Db, - runId: string, - agentId: string, - companyId: string, - output: unknown, - rawResponse: string -): void { - // Store output as JSON in the action field for now - // In a production system, you'd have a separate table for agent outputs - const stmt = db.prepare(` - UPDATE agent_runs - SET action = ?, progress = 100 - WHERE id = ? - `); - - stmt.run(JSON.stringify({ output, rawResponse: rawResponse.slice(0, 1000) }), runId); - db.prepare(` - INSERT OR REPLACE INTO agent_outputs (run_id, agent_id, company_id, output_json, raw_response) - VALUES (?, ?, ?, ?, ?) - `).run(runId, agentId, companyId, JSON.stringify(output), rawResponse); -} - /** * Execute agent with simple callback interface */ @@ -268,7 +403,7 @@ export async function executeAgentSimple( db: Db, agentId: string, companyId: string, - onProgress?: (message: string) => void + onProgress?: (message: string) => void, ): Promise { const result = await executeAgent(db, agentId, companyId, { onProgress: (progress, action) => { diff --git a/packages/shared/src/agents/validationAgents.ts b/packages/shared/src/agents/validationAgents.ts index c1b6228..0d8c483 100644 --- a/packages/shared/src/agents/validationAgents.ts +++ b/packages/shared/src/agents/validationAgents.ts @@ -4,7 +4,7 @@ */ import type { Db } from "../db/database.js"; -import { streamStructuredResponse, completeStructured, isConfigured } from "../llm/client.js"; +import { complete, isConfigured } from "../llm/client.js"; import { getAgentPrompt, type AgentContext } from "../llm/prompts.js"; import { buildAgentContext } from "../llm/context.js"; import { emitValidationUpdated } from "./eventEmitter.js"; @@ -69,7 +69,14 @@ export async function executeSourceVerification( recommendations: ["string"], }; - const result = await completeStructured(prompt, schema); + const rawResult = await complete(prompt); + let result: any; + try { + result = JSON.parse(rawResult); + } catch { + const jsonMatch = rawResult.match(/\{[\s\S]*\}/); + result = jsonMatch ? JSON.parse(jsonMatch[0]) : {}; + } // Extract issues from the result const issues: ValidationResult["issues"] = []; @@ -172,7 +179,14 @@ export async function executeModelQA( overallStatus: ["pass", "warn", "fail"], }; - const result = await completeStructured(prompt, schema); + const rawResult = await complete(prompt); + let result: any; + try { + result = JSON.parse(rawResult); + } catch { + const jsonMatch = rawResult.match(/\{[\s\S]*\}/); + result = jsonMatch ? JSON.parse(jsonMatch[0]) : {}; + } // Extract issues from the result const issues: ValidationResult["issues"] = []; @@ -288,7 +302,14 @@ export async function executeRedTeam( recommendation: ["proceed_with_caution", "acceptable", "needs_revision"], }; - const result = await completeStructured(prompt, schema); + const rawResult = await complete(prompt); + let result: any; + try { + result = JSON.parse(rawResult); + } catch { + const jsonMatch = rawResult.match(/\{[\s\S]*\}/); + result = jsonMatch ? JSON.parse(jsonMatch[0]) : {}; + } // Extract issues from the result const issues: ValidationResult["issues"] = []; diff --git a/packages/shared/src/db/queries.ts b/packages/shared/src/db/queries.ts index 88bacd7..62c903e 100644 --- a/packages/shared/src/db/queries.ts +++ b/packages/shared/src/db/queries.ts @@ -830,7 +830,7 @@ export function storeAgentOutput(db: Db, runId: string, agentId: string, company export function updateAgentRunCompletion( db: Db, runId: string, - status: "completed" | "failed" | "cancelled", + status: "queued" | "running" | "completed" | "failed" | "cancelled", action: string, error?: string ): void { @@ -1056,7 +1056,7 @@ export function getServerSettings(db: Db): ServerSettings { } } - const settingsStmt = db.prepare("SELECT key, value FROM client_settings WHERE key IN ('server.dataSources', 'server.exportPipelines')"); + const settingsStmt = db.prepare("SELECT key, value FROM client_settings WHERE key IN ('server.dataSources', 'server.exportPipelines', 'server.llm')"); const settingsRows = settingsStmt.all() as Array<{ key: string; value: string }>; let dataSources: Record = { sec_filings: true, @@ -1066,6 +1066,7 @@ export function getServerSettings(db: Db): ServerSettings { press_releases: true, }; let exportPipelines: Record = {}; + let llm: ServerSettings["llm"] = undefined; for (const row of settingsRows) { try { const parsed = JSON.parse(row.value) as unknown; @@ -1075,6 +1076,9 @@ export function getServerSettings(db: Db): ServerSettings { } else if (row.key === "server.exportPipelines") { const value = z.record(z.unknown()).safeParse(parsed); if (value.success) exportPipelines = value.data; + } else if (row.key === "server.llm") { + const value = ServerSettingsSchema.shape.llm.safeParse(parsed); + if (value.success) llm = value.data; } } catch { // Ignore malformed server settings. @@ -1085,6 +1089,7 @@ export function getServerSettings(db: Db): ServerSettings { agentConfigs, dataSources, exportPipelines, + ...(llm ? { llm } : {}), }); } @@ -1114,4 +1119,7 @@ export function updateServerSettings( if (settings.exportPipelines !== undefined) { updateClientSetting(db, "server.exportPipelines", settings.exportPipelines); } + if (settings.llm !== undefined) { + updateClientSetting(db, "server.llm", settings.llm); + } } diff --git a/packages/shared/src/llm/client.ts b/packages/shared/src/llm/client.ts index 6b844e0..6f2f18a 100644 --- a/packages/shared/src/llm/client.ts +++ b/packages/shared/src/llm/client.ts @@ -1,12 +1,18 @@ /** - * LLM client for Pi API (Inflection AI) - * Provides streaming and non-streaming completion functions + * LLM client — delegates to the Pi Coding Agent SDK + * + * This module keeps the same public API surface (streamResponse, complete, + * isConfigured) but delegates all LLM interaction to the SDK adapter in + * `piSdk.ts`. + * + * The old raw-fetch-to-api.pi.ai implementation has been removed. */ -const PI_API_URL = "https://api.pi.ai/v1/chat/completions"; -const client = { - apiKey: process.env.PI_API_KEY || "", -}; +import { createSession, isConfigured as sdkIsConfigured } from "./piSdk.js"; + +// --------------------------------------------------------------------------- +// Types (kept for backward compatibility) +// --------------------------------------------------------------------------- export interface StreamOptions { onProgress?: (text: string) => void; @@ -19,189 +25,79 @@ export interface CompletionOptions { temperature?: number; } -const DEFAULT_MODEL = "pi"; -const DEFAULT_MAX_TOKENS = 4096; +// --------------------------------------------------------------------------- +// Public API +// --------------------------------------------------------------------------- /** - * Stream a response from Pi + * Check if an API key is configured for any supported provider. + * Delegates to the SDK adapter which checks both env vars and auth.json. + */ +export function isConfigured(): boolean { + return sdkIsConfigured(); +} + +/** + * Stream a response from the LLM. + * + * Creates a one-shot in-memory SDK session, sends the prompt, and collects + * the streamed text via the event subscription. */ export async function streamResponse( prompt: string, - options: StreamOptions & CompletionOptions = {} + options: StreamOptions & CompletionOptions = {}, ): Promise { - const { - model = DEFAULT_MODEL, - maxTokens = DEFAULT_MAX_TOKENS, - temperature = 0, - onProgress, - signal, - } = options; + const { onProgress } = options; - if (!client.apiKey) { - throw new Error("PI_API_KEY is not set"); + if (!isConfigured()) { + throw new Error( + "No LLM API key configured. Set ANTHROPIC_API_KEY, OPENAI_API_KEY, ZAI_API_KEY, or another provider key.", + ); } - const response = await fetch(PI_API_URL, { - method: "POST", - headers: { - "Content-Type": "application/json", - "Authorization": `Bearer ${client.apiKey}`, - }, - body: JSON.stringify({ - model, - messages: [{ role: "user", content: prompt }], - max_tokens: maxTokens, - temperature, - stream: true, - }), - signal, - }); - - if (!response.ok) { - const error = await response.text(); - throw new Error(`Pi API error: ${response.status} ${error}`); - } - - const reader = response.body?.getReader(); - if (!reader) { - throw new Error("Failed to get response body reader"); - } - - const decoder = new TextDecoder(); + const session = await createSession(); let fullResponse = ""; - while (true) { - const { done, value } = await reader.read(); - if (done) break; - - const chunk = decoder.decode(value, { stream: true }); - const lines = chunk.split("\n"); - - for (const line of lines) { - if (line.startsWith("data: ") && line !== "data: [DONE]") { - try { - const data = JSON.parse(line.slice(6)); - const content = data.choices?.[0]?.delta?.content; - if (content) { - fullResponse += content; - onProgress?.(content); - } - } catch { - // Skip invalid JSON - } - } + session.subscribe((event) => { + if ( + event.type === "message_update" && + event.assistantMessageEvent.type === "text_delta" + ) { + const delta = event.assistantMessageEvent.delta; + fullResponse += delta; + onProgress?.(delta); } - } + }); + await session.prompt(prompt); return fullResponse; } /** - * Get a complete response from Pi (non-streaming) + * Get a complete (non-streaming) response from the LLM. */ export async function complete( prompt: string, - options: CompletionOptions = {} + _options: CompletionOptions = {}, ): Promise { - const { - model = DEFAULT_MODEL, - maxTokens = DEFAULT_MAX_TOKENS, - temperature = 0, - } = options; - - if (!client.apiKey) { - throw new Error("PI_API_KEY is not set"); + if (!isConfigured()) { + throw new Error( + "No LLM API key configured. Set ANTHROPIC_API_KEY, OPENAI_API_KEY, ZAI_API_KEY, or another provider key.", + ); } - const response = await fetch(PI_API_URL, { - method: "POST", - headers: { - "Content-Type": "application/json", - "Authorization": `Bearer ${client.apiKey}`, - }, - body: JSON.stringify({ - model, - messages: [{ role: "user", content: prompt }], - max_tokens: maxTokens, - temperature, - }), + const session = await createSession(); + let result = ""; + + session.subscribe((event) => { + if ( + event.type === "message_update" && + event.assistantMessageEvent.type === "text_delta" + ) { + result += event.assistantMessageEvent.delta; + } }); - if (!response.ok) { - const error = await response.text(); - throw new Error(`Pi API error: ${response.status} ${error}`); - } - - const data = await response.json(); - return data.choices?.[0]?.message?.content ?? ""; + await session.prompt(prompt); + return result; } - -/** - * Check if API key is configured - */ -export function isConfigured(): boolean { - return !!client.apiKey && client.apiKey.length > 0; -} - -/** - * Stream structured JSON response - * Useful for agents that need to return structured data - */ -export async function streamStructuredResponse( - prompt: string, - schema: Record, - options: StreamOptions & CompletionOptions = {} -): Promise { - const structuredPrompt = `${prompt} - -Please respond with a JSON object that follows this structure: -${JSON.stringify(schema, null, 2)} - -Your response must be valid JSON only, with no additional text or explanation.`; - - const response = await streamResponse(structuredPrompt, options); - - // Try to parse as JSON - try { - return JSON.parse(response) as T; - } catch (error) { - // If the response isn't valid JSON, try to extract JSON from the response - const jsonMatch = response.match(/\{[\s\S]*\}/); - if (jsonMatch) { - return JSON.parse(jsonMatch[0]) as T; - } - throw new Error("Failed to parse structured response as JSON"); - } -} - -/** - * Complete with structured JSON response (non-streaming) - */ -export async function completeStructured( - prompt: string, - schema: Record, - options: CompletionOptions = {} -): Promise { - const structuredPrompt = `${prompt} - -Please respond with a JSON object that follows this structure: -${JSON.stringify(schema, null, 2)} - -Your response must be valid JSON only, with no additional text or explanation.`; - - const response = await complete(structuredPrompt, options); - - // Try to parse as JSON - try { - return JSON.parse(response) as T; - } catch (error) { - // If the response isn't valid JSON, try to extract JSON from the response - const jsonMatch = response.match(/\{[\s\S]*\}/); - if (jsonMatch) { - return JSON.parse(jsonMatch[0]) as T; - } - throw new Error("Failed to parse structured response as JSON"); - } -} - -export default client; diff --git a/packages/shared/src/llm/index.ts b/packages/shared/src/llm/index.ts index fdf4e6a..6c7fa25 100644 --- a/packages/shared/src/llm/index.ts +++ b/packages/shared/src/llm/index.ts @@ -1,7 +1,22 @@ /** * LLM module exports + * + * isConfigured is exported from client.ts (which has its own implementation). + * The piSdk.ts also has isConfigured but it's only exported here for direct + * access to SDK internals. */ -export * from "./client.js"; +export { complete, streamResponse, isConfigured } from "./client.js"; +export { + createSession, + getAuthStorage, + getModelRegistry, + setActiveModelSelection, + getActiveModelSelection, + registerSession, + unregisterSession, + abortSession, +} from "./piSdk.js"; +export type { CreateSessionOptions, ModelSelection } from "./piSdk.js"; export * from "./prompts.js"; export * from "./context.js"; diff --git a/packages/shared/src/llm/piSdk.test.ts b/packages/shared/src/llm/piSdk.test.ts new file mode 100644 index 0000000..e2f33cf --- /dev/null +++ b/packages/shared/src/llm/piSdk.test.ts @@ -0,0 +1,25 @@ +import { afterEach, describe, expect, it } from "vitest"; +import { createSession, setActiveModelSelection } from "./piSdk.js"; + +describe("Pi SDK adapter", () => { + afterEach(() => { + delete process.env.ZAI_API_KEY; + setActiveModelSelection(undefined); + }); + + it("creates sessions with the selected ZAI GLM model and system prompt", async () => { + process.env.ZAI_API_KEY = "test-zai-key"; + + const session = await createSession({ + systemPrompt: "You are a MosaicIQ test agent.", + modelSelection: { provider: "zai", modelId: "glm-5.1", thinkingLevel: "medium" }, + }); + + expect(session.model?.provider).toBe("zai"); + expect(session.model?.id).toBe("glm-5.1"); + expect(session.thinkingLevel).toBe("medium"); + expect(session.systemPrompt).toBe("You are a MosaicIQ test agent."); + + session.dispose(); + }); +}); diff --git a/packages/shared/src/llm/piSdk.ts b/packages/shared/src/llm/piSdk.ts new file mode 100644 index 0000000..68f657e --- /dev/null +++ b/packages/shared/src/llm/piSdk.ts @@ -0,0 +1,188 @@ +/** + * Pi Coding Agent SDK adapter for MosaicIQ + * + * This module wraps the Pi Coding Agent SDK (NOT the Pi/Inflection AI chatbot API). + * The SDK is an agent framework that supports multiple LLM providers (Anthropic, + * OpenAI, DeepSeek, Google, etc.). We do NOT use the Pi/Inflection AI service. + */ + +import { + AuthStorage, + createAgentSession, + ModelRegistry, + SessionManager, + SettingsManager, + type AgentSession, + type AgentSessionEvent, +} from "@earendil-works/pi-coding-agent"; + +type ThinkingLevel = "off" | "minimal" | "low" | "medium" | "high"; + +// --------------------------------------------------------------------------- +// Lazy-initialized singletons +// --------------------------------------------------------------------------- + +let authStorage: AuthStorage | undefined; +let modelRegistry: ModelRegistry | undefined; +let activeModelSelection: ModelSelection | undefined; + +export interface ModelSelection { + provider: string; + modelId: string; + thinkingLevel?: ThinkingLevel; +} + +export function getAuthStorage(): AuthStorage { + if (!authStorage) { + authStorage = AuthStorage.create(); + } + return authStorage; +} + +export function getModelRegistry(): ModelRegistry { + if (!modelRegistry) { + modelRegistry = ModelRegistry.create(getAuthStorage()); + } + return modelRegistry; +} + +// --------------------------------------------------------------------------- +// Check configuration +// --------------------------------------------------------------------------- + +/** + * Returns true if at least one supported provider has an API key configured + * (via env var or auth storage / auth.json). + * + * Checks environment variables first (fast, synchronous), then falls back to + * the SDK's ModelRegistry which inspects auth.json. + */ +export function isConfigured(): boolean { + // Fast path: check env vars synchronously + if ( + process.env.ANTHROPIC_API_KEY || + process.env.OPENAI_API_KEY || + process.env.DEEPSEEK_API_KEY || + process.env.GEMINI_API_KEY || + process.env.ZAI_API_KEY + ) { + return true; + } + + // Slow path: check auth.json via the SDK registry + try { + const registry = getModelRegistry(); + return registry.getAvailable().length > 0; + } catch { + return false; + } +} + +// --------------------------------------------------------------------------- +// Session factory +// --------------------------------------------------------------------------- + +export interface CreateSessionOptions { + /** System prompt injected at session creation */ + systemPrompt?: string; + /** Explicit model selection. Falls back to runtime selection, then SDK defaults. */ + modelSelection?: ModelSelection; + /** Custom tools available to the agent */ + customTools?: unknown[]; + /** Event listener for streaming / lifecycle events */ + onEvent?: (event: AgentSessionEvent) => void; +} + +export function setActiveModelSelection(selection: ModelSelection | undefined): void { + activeModelSelection = selection; +} + +export function getActiveModelSelection(): ModelSelection | undefined { + return activeModelSelection; +} + +/** + * Create an in-memory agent session with custom tools. + * + * We deliberately: + * - Use `SessionManager.inMemory()` — MosaicIQ has its own SQLite persistence. + * - Set `compaction: { enabled: false }` — agents are short-lived per run. + * - Pass `tools: []` — no built-in coding tools (read/write/edit/bash). + * Only our domain-specific custom tools are provided. + */ +export async function createSession( + options: CreateSessionOptions = {}, +): Promise { + const registry = getModelRegistry(); + const modelSelection = options.modelSelection ?? activeModelSelection; + const selectedModel = modelSelection + ? registry.find(modelSelection.provider, modelSelection.modelId) + : undefined; + + if (modelSelection && !selectedModel) { + throw new Error(`Model "${modelSelection.provider}/${modelSelection.modelId}" is not known to the Pi SDK.`); + } + + if (selectedModel && !registry.hasConfiguredAuth(selectedModel)) { + throw new Error( + `Model "${selectedModel.provider}/${selectedModel.id}" is selected, but no API key is configured for provider "${selectedModel.provider}".`, + ); + } + + const { session } = await createAgentSession({ + model: selectedModel, + thinkingLevel: modelSelection?.thinkingLevel, + sessionManager: SessionManager.inMemory(), + authStorage: getAuthStorage(), + modelRegistry: registry, + settingsManager: SettingsManager.inMemory({ + ...(modelSelection + ? { + defaultProvider: modelSelection.provider, + defaultModel: modelSelection.modelId, + defaultThinkingLevel: modelSelection.thinkingLevel, + } + : {}), + compaction: { enabled: false }, + retry: { enabled: true, maxRetries: 3 }, + }), + tools: [], + customTools: (options.customTools ?? []) as any[], + }); + + if (options.onEvent) { + session.subscribe(options.onEvent); + } + + if (options.systemPrompt) { + const agentSession = session as unknown as { _baseSystemPrompt?: string }; + agentSession._baseSystemPrompt = options.systemPrompt; + session.agent.state.systemPrompt = options.systemPrompt; + } + + return session; +} + +// --------------------------------------------------------------------------- +// Active sessions (for abort / cancellation) +// --------------------------------------------------------------------------- + +const activeSessions = new Map(); + +export function registerSession(runId: string, session: AgentSession): void { + activeSessions.set(runId, session); +} + +export function unregisterSession(runId: string): void { + activeSessions.delete(runId); +} + +export async function abortSession(runId: string): Promise { + const session = activeSessions.get(runId); + if (session) { + await session.abort(); + activeSessions.delete(runId); + return true; + } + return false; +} diff --git a/packages/shared/src/rpc/agentRpc.ts b/packages/shared/src/rpc/agentRpc.ts index 2ff59a4..b3afb2b 100644 --- a/packages/shared/src/rpc/agentRpc.ts +++ b/packages/shared/src/rpc/agentRpc.ts @@ -1,9 +1,17 @@ import type { Db } from "../db/database.js"; -import { addAgentRunStep, createAgentRun, getPortfolio, listAgentRunSteps, listAgents, pauseAgent, storeAgentOutput, updateAgentConfig, updateAgentRunCompletion } from "../db/queries.js"; +import { + addAgentRunStep, + createAgentRun, + getPortfolio, + listAgentRunSteps, + listAgents, + pauseAgent, + storeAgentOutput, + updateAgentConfig, + updateAgentRunCompletion, +} from "../db/queries.js"; import { executeAgent } from "../agents/runner.js"; -import { buildChatContext } from "../llm/context.js"; -import { complete, isConfigured } from "../llm/client.js"; -import { getAgentPrompt } from "../llm/prompts.js"; +import { isConfigured } from "../llm/client.js"; import { fail, ok } from "./result.js"; import type { RpcHandlers } from "./types.js"; @@ -20,6 +28,7 @@ type AgentMethod = export function agentHandlers(db: Db): RpcHandlers { return { "agent.list": ({ companyId }) => ok("agent.list", { agents: listAgents(db, companyId) }), + "agent.start": ({ agentId, companyId }) => { const { runId } = createAgentRun(db, agentId, companyId, "queued"); void executeAgent(db, agentId, companyId, { runId }).catch((error) => { @@ -27,7 +36,9 @@ export function agentHandlers(db: Db): RpcHandlers { }); return ok("agent.start", { runId }); }, + "agent.pause": ({ agentId }) => ok("agent.pause", pauseAgent(db, agentId)), + "agent.restart": ({ agentId }) => { const companyId = getPortfolio(db)?.activeCompanyId; if (!companyId) return fail("VALIDATION_ERROR", "Select or add a company before restarting an agent."); @@ -37,32 +48,48 @@ export function agentHandlers(db: Db): RpcHandlers { }); return ok("agent.restart", { runId }); }, + "agent.chat": async ({ agentId, message }) => { const portfolio = getPortfolio(db); const companyId = portfolio?.activeCompanyId; if (!companyId) return fail("VALIDATION_ERROR", "Select or add a company before chatting with an agent."); - if (!isConfigured()) return fail("AGENT_FAILED", "LLM API key is not configured. Set PI_API_KEY to use agent chat."); + if (!isConfigured()) return fail("AGENT_FAILED", "LLM API key is not configured. Set ANTHROPIC_API_KEY or another supported provider key to use agent chat."); + const { runId } = createAgentRun(db, agentId, companyId, "running"); - addAgentRunStep(db, runId, 1, "Build context", "Loaded local company, memo, filings, and model context."); + addAgentRunStep(db, runId, 1, "Build context", "Using SDK session with custom tools for company data."); + try { - const context = await buildChatContext(db, companyId, message); + // Use the new SDK-based runner for chat too addAgentRunStep(db, runId, 2, "Generate response", `Sent analyst message to ${agentId}.`); - const response = await complete(getAgentPrompt("chat", context)); - storeAgentOutput(db, runId, agentId, companyId, { response }, response); - updateAgentRunCompletion(db, runId, "completed", "Chat response generated"); - addAgentRunStep(db, runId, 3, "Persist response", "Saved chat response to the local run log."); - return ok("agent.chat", { response }); + const result = await executeAgent(db, agentId, companyId, { + runId, + userMessage: message, + }); + + if (result.status === "completed" && typeof result.output === "object" && result.output !== null) { + const chatOutput = result.output as { response?: string; text?: string }; + const response = chatOutput.response ?? chatOutput.text ?? result.rawResponse; + storeAgentOutput(db, runId, agentId, companyId, { response }, result.rawResponse); + updateAgentRunCompletion(db, runId, "completed", "Chat response generated"); + addAgentRunStep(db, runId, 3, "Persist response", "Saved chat response to the local run log."); + return ok("agent.chat", { response }); + } else { + throw new Error(result.error || "Agent chat failed"); + } } catch (error) { - const message = error instanceof Error ? error.message : String(error); - updateAgentRunCompletion(db, runId, "failed", "Chat failed", message); - return fail("AGENT_FAILED", message, error); + const msg = error instanceof Error ? error.message : String(error); + updateAgentRunCompletion(db, runId, "failed", "Chat failed", msg); + return fail("AGENT_FAILED", msg, error); } }, + "agent.configure": ({ agentId, config }) => { updateAgentConfig(db, agentId, config); return ok("agent.configure", { ok: true }); }, + "agent.getTrace": ({ runId }) => ok("agent.getTrace", { steps: listAgentRunSteps(db, runId) }), + "agent.runPipeline": ({ companyId, pipeline }) => { const pipelineAgents: Record = { research: ["sf", "cr", "fm", "va", "mw", "pa"], @@ -71,6 +98,7 @@ export function agentHandlers(db: Db): RpcHandlers { }; const agentIds = pipelineAgents[pipeline]; if (!agentIds) return fail("VALIDATION_ERROR", `Unknown pipeline: ${pipeline}`); + const runIds = agentIds.map((pipelineAgentId) => { const { runId } = createAgentRun(db, pipelineAgentId, companyId, "queued"); void executeAgent(db, pipelineAgentId, companyId, { runId }).catch((error) => { @@ -78,6 +106,7 @@ export function agentHandlers(db: Db): RpcHandlers { }); return runId; }); + return ok("agent.runPipeline", { runIds }); }, }; diff --git a/packages/shared/src/rpc/modelRpc.test.ts b/packages/shared/src/rpc/modelRpc.test.ts index f9c05b8..cdb2ad6 100644 --- a/packages/shared/src/rpc/modelRpc.test.ts +++ b/packages/shared/src/rpc/modelRpc.test.ts @@ -1,6 +1,7 @@ import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { closeDatabase, type Db, initDatabase } from "../db/database.js"; import { createRpcHandler } from "../db/rpcHandler.js"; +import { getActiveModelSelection, setActiveModelSelection } from "../llm/piSdk.js"; describe("model RPC", () => { let db: Db; @@ -13,6 +14,8 @@ describe("model RPC", () => { }); afterEach(() => { + delete process.env.ZAI_API_KEY; + setActiveModelSelection(undefined); closeDatabase(db); }); @@ -42,4 +45,30 @@ describe("model RPC", () => { expect(result.error.code).toBe("INTERNAL_ERROR"); expect(result.error.message).toBe("Could not load model for company."); }); + + it("lists SDK-discovered ZAI GLM models", async () => { + process.env.ZAI_API_KEY = "test-zai-key"; + + const result = await rpc("model.list", undefined); + + expect(result.ok).toBe(true); + if (!result.ok) return; + expect(result.data.models).toContainEqual( + expect.objectContaining({ + provider: "zai", + modelId: "glm-5.1", + name: "GLM-5.1", + available: true, + }), + ); + }); + + it("sets the runtime SDK model selection", async () => { + process.env.ZAI_API_KEY = "test-zai-key"; + + const result = await rpc("model.set", { provider: "zai", modelId: "glm-5.1" }); + + expect(result).toEqual({ ok: true, data: { ok: true, provider: "zai", modelId: "glm-5.1" } }); + expect(getActiveModelSelection()).toEqual({ provider: "zai", modelId: "glm-5.1" }); + }); }); diff --git a/packages/shared/src/rpc/modelRpc.ts b/packages/shared/src/rpc/modelRpc.ts index bc2cf4f..01a11c3 100644 --- a/packages/shared/src/rpc/modelRpc.ts +++ b/packages/shared/src/rpc/modelRpc.ts @@ -1,5 +1,6 @@ import type { Db } from "../db/database.js"; import { createModelRow, deleteModelRow, getModel, resolveCompany, updateModelCell } from "../db/queries.js"; +import { getModelRegistry, setActiveModelSelection } from "../llm/piSdk.js"; import { fail, ok } from "./result.js"; import type { RpcHandlers } from "./types.js"; @@ -10,7 +11,17 @@ function errorDetail(operation: string, error: unknown): { operation: string; me }; } -export function modelHandlers(db: Db): RpcHandlers<"model.get" | "model.updateCell" | "model.createRow" | "model.deleteRow" | "model.runScenario"> { +const DISPLAYED_LLM_PROVIDERS = new Set(["anthropic", "openai", "deepseek", "google", "zai"]); + +export function modelHandlers(db: Db): RpcHandlers< + "model.get" | + "model.list" | + "model.set" | + "model.updateCell" | + "model.createRow" | + "model.deleteRow" | + "model.runScenario" +> { return { "model.get": ({ companyId, tab }) => { const company = resolveCompany(db, companyId); @@ -21,21 +32,54 @@ export function modelHandlers(db: Db): RpcHandlers<"model.get" | "model.updateCe return fail("INTERNAL_ERROR", "Could not load model for company.", errorDetail("getModel", error)); } }, + + "model.list": async () => { + const registry = getModelRegistry(); + const available = await registry.getAvailable(); + const availableIds = new Set(available.map((m) => `${m.provider}/${m.id}`)); + + const models = registry + .getAll() + .filter((model) => DISPLAYED_LLM_PROVIDERS.has(model.provider)) + .map((model) => ({ + provider: model.provider, + modelId: model.id, + name: model.name ?? model.id, + available: availableIds.has(`${model.provider}/${model.id}`), + })); + + return ok("model.list", { models }); + }, + + "model.set": ({ provider, modelId, thinkingLevel }) => { + const registry = getModelRegistry(); + const model = registry.find(provider, modelId); + if (!model) return fail("VALIDATION_ERROR", `Model "${provider}/${modelId}" is not supported by the Pi SDK.`); + if (!registry.hasConfiguredAuth(model)) { + return fail("VALIDATION_ERROR", `No API key is configured for provider "${provider}".`); + } + setActiveModelSelection({ provider, modelId, thinkingLevel }); + return ok("model.set", { ok: true, provider, modelId }); + }, + "model.updateCell": ({ companyId, tab, row, col, value }) => { const company = resolveCompany(db, companyId); if (!company) return fail("NOT_FOUND", `Company "${companyId}" not found.`); return ok("model.updateCell", updateModelCell(db, company.id, tab, row, col, value)); }, + "model.createRow": ({ companyId, tab, label, kind, values }) => { const company = resolveCompany(db, companyId); if (!company) return fail("NOT_FOUND", `Company "${companyId}" not found.`); return ok("model.createRow", createModelRow(db, company.id, tab, { label, kind, values: values ?? [] })); }, + "model.deleteRow": ({ companyId, tab, row }) => { const company = resolveCompany(db, companyId); if (!company) return fail("NOT_FOUND", `Company "${companyId}" not found.`); return ok("model.deleteRow", { ok: deleteModelRow(db, company.id, tab, row) }); }, + "model.runScenario": ({ companyId, overrides }) => { const company = resolveCompany(db, companyId); if (!company) return fail("NOT_FOUND", `Company "${companyId}" not found.`); diff --git a/packages/shared/src/rpc/settingsRpc.test.ts b/packages/shared/src/rpc/settingsRpc.test.ts index b69171e..367244e 100644 --- a/packages/shared/src/rpc/settingsRpc.test.ts +++ b/packages/shared/src/rpc/settingsRpc.test.ts @@ -1,6 +1,7 @@ import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { closeDatabase, type Db, initDatabase } from "../db/database.js"; import { createRpcHandler } from "../db/rpcHandler.js"; +import { getActiveModelSelection, setActiveModelSelection } from "../llm/piSdk.js"; describe("settings RPC", () => { let db: Db; @@ -12,6 +13,7 @@ describe("settings RPC", () => { }); afterEach(() => { + setActiveModelSelection(undefined); closeDatabase(db); }); @@ -40,4 +42,18 @@ describe("settings RPC", () => { if (!get.ok) return; expect(get.data.settings).toMatchObject({ agentConfigs: { "agent-1": { enabled: true } } }); }); + + it("updates runtime LLM model selection from server settings", async () => { + const update = await rpc("settings.update", { + scope: "server", + changes: { llm: { provider: "zai", modelId: "glm-5.1", thinkingLevel: "medium" } }, + }); + + expect(update).toEqual({ ok: true, data: { ok: true } }); + expect(getActiveModelSelection()).toEqual({ + provider: "zai", + modelId: "glm-5.1", + thinkingLevel: "medium", + }); + }); }); diff --git a/packages/shared/src/rpc/settingsRpc.ts b/packages/shared/src/rpc/settingsRpc.ts index 1bfb527..cd3602c 100644 --- a/packages/shared/src/rpc/settingsRpc.ts +++ b/packages/shared/src/rpc/settingsRpc.ts @@ -1,6 +1,7 @@ import type { ClientSettings, ServerSettings } from "@mosaiciq/contracts/rpc"; import type { Db } from "../db/database.js"; import { getClientSettings, getServerSettings, updateClientSettings, updateServerSettings } from "../db/queries.js"; +import { setActiveModelSelection } from "../llm/piSdk.js"; import { ok } from "./result.js"; import type { RpcHandlers } from "./types.js"; @@ -14,7 +15,15 @@ export function settingsHandlers(db: Db): RpcHandlers<"settings.get" | "settings if (scope === "client") { updateClientSettings(db, changes as Partial); } else { - updateServerSettings(db, changes as Partial); + const serverChanges = changes as Partial; + updateServerSettings(db, serverChanges); + if (serverChanges.llm?.provider && serverChanges.llm.modelId) { + setActiveModelSelection({ + provider: serverChanges.llm.provider, + modelId: serverChanges.llm.modelId, + thinkingLevel: serverChanges.llm.thinkingLevel, + }); + } } return ok("settings.update", { ok: true }); }, diff --git a/packages/shared/src/tools/companyTools.ts b/packages/shared/src/tools/companyTools.ts new file mode 100644 index 0000000..e0ab25d --- /dev/null +++ b/packages/shared/src/tools/companyTools.ts @@ -0,0 +1,208 @@ +/** + * Custom tools: Company, filings, and earnings data access + */ + +import { Type } from "typebox"; +import { defineTool } from "@earendil-works/pi-coding-agent"; +import type { Db } from "../db/database.js"; +import { + getCompany, + getCompanyByTicker, + resolveCompany, + listFilings, + getEarningsSchedule, + listCatalysts, + listRisks, + listAlerts, +} from "../db/queries.js"; + +export function createCompanyTools(db: Db) { + const getCompanyInfo = defineTool({ + name: "get_company_info", + label: "Get Company Info", + description: + "Get company details including name, ticker, sector, price, thesis, and metadata. Accepts either a company ID or ticker symbol.", + parameters: Type.Object({ + companyIdOrTicker: Type.String({ + description: "Company ID or ticker symbol (e.g. 'AAPL' or 'company-123')", + }), + }), + execute: async (_, params) => { + const company = resolveCompany(db, params.companyIdOrTicker); + if (!company) { + return { + content: [ + { + type: "text" as const, + text: `Company "${params.companyIdOrTicker}" not found.`, + }, + ], + isError: true, + details: {}, + }; + } + return { + content: [ + { + type: "text" as const, + text: JSON.stringify(company, null, 2), + }, + ], + details: {}, + }; + }, + }); + + const getFilingsTool = defineTool({ + name: "get_filings", + label: "Get SEC Filings", + description: + "List SEC filings for a company (10-K, 10-Q, 8-K, DEF 14A, etc.). Returns form type, title, filed date, and key changes.", + parameters: Type.Object({ + companyIdOrTicker: Type.String({ + description: "Company ID or ticker symbol", + }), + limit: Type.Optional( + Type.Number({ description: "Max filings to return (default 20)", default: 20 }), + ), + since: Type.Optional( + Type.String({ description: "Only filings after this date (YYYY-MM-DD)" }), + ), + }), + execute: async (_, params) => { + const company = resolveCompany(db, params.companyIdOrTicker); + if (!company) { + return { + content: [{ type: "text" as const, text: `Company "${params.companyIdOrTicker}" not found.` }], + isError: true, + details: {}, + }; + } + const filings = listFilings(db, company.id, params.since); + const limited = filings.slice(0, params.limit ?? 20); + return { + content: [ + { + type: "text" as const, + text: JSON.stringify( + { companyId: company.id, ticker: company.ticker, count: limited.length, filings: limited }, + null, + 2, + ), + }, + ], + details: {}, + }; + }, + }); + + const getEarningsTool = defineTool({ + name: "get_earnings", + label: "Get Earnings Schedule", + description: + "Get upcoming and historical earnings dates, actual vs expected revenue and EPS.", + parameters: Type.Object({ + companyIdOrTicker: Type.String({ + description: "Company ID or ticker symbol", + }), + }), + execute: async (_, params) => { + const company = resolveCompany(db, params.companyIdOrTicker); + if (!company) { + return { + content: [{ type: "text" as const, text: `Company "${params.companyIdOrTicker}" not found.` }], + isError: true, + details: {}, + }; + } + const schedule = getEarningsSchedule(db, company.id); + return { + content: [ + { + type: "text" as const, + text: JSON.stringify(schedule, null, 2), + }, + ], + details: {}, + }; + }, + }); + + const getCatalystsTool = defineTool({ + name: "get_catalysts", + label: "Get Catalysts", + description: "List catalysts (upcoming events) for a company with impact assessment and thesis relevance.", + parameters: Type.Object({ + companyIdOrTicker: Type.String({ + description: "Company ID or ticker symbol", + }), + }), + execute: async (_, params) => { + const company = resolveCompany(db, params.companyIdOrTicker); + if (!company) { + return { + content: [{ type: "text" as const, text: `Company "${params.companyIdOrTicker}" not found.` }], + isError: true, + details: {}, + }; + } + const catalysts = listCatalysts(db, company.id); + return { + content: [{ type: "text" as const, text: JSON.stringify(catalysts, null, 2) }], + details: {}, + }; + }, + }); + + const getRisksTool = defineTool({ + name: "get_risks", + label: "Get Risks", + description: "List identified risks for a company, categorized by type with severity and likelihood.", + parameters: Type.Object({ + companyIdOrTicker: Type.String({ + description: "Company ID or ticker symbol", + }), + }), + execute: async (_, params) => { + const company = resolveCompany(db, params.companyIdOrTicker); + if (!company) { + return { + content: [{ type: "text" as const, text: `Company "${params.companyIdOrTicker}" not found.` }], + isError: true, + details: {}, + }; + } + const risks = listRisks(db, company.id); + return { + content: [{ type: "text" as const, text: JSON.stringify(risks, null, 2) }], + details: {}, + }; + }, + }); + + const getAlertsTool = defineTool({ + name: "get_alerts", + label: "Get Alerts", + description: "List recent alerts (filing, price move, earnings surprise, peer event) for a company.", + parameters: Type.Object({ + companyIdOrTicker: Type.Optional( + Type.String({ description: "Company ID or ticker symbol (omit for all companies)" }), + ), + since: Type.Optional( + Type.String({ description: "Only alerts after this timestamp" }), + ), + }), + execute: async (_, params) => { + const company = params.companyIdOrTicker + ? resolveCompany(db, params.companyIdOrTicker) + : undefined; + const alerts = listAlerts(db, company?.id, params.since); + return { + content: [{ type: "text" as const, text: JSON.stringify(alerts, null, 2) }], + details: {}, + }; + }, + }); + + return [getCompanyInfo, getFilingsTool, getEarningsTool, getCatalystsTool, getRisksTool, getAlertsTool]; +} diff --git a/packages/shared/src/tools/fileTools.ts b/packages/shared/src/tools/fileTools.ts new file mode 100644 index 0000000..9514cd7 --- /dev/null +++ b/packages/shared/src/tools/fileTools.ts @@ -0,0 +1,250 @@ +/** + * Custom tools: File manipulation (Excel, CSV) + * + * The Pi SDK's built-in read/write tools only handle text files. + * These tools let agents read and write Excel, CSV, and other financial file formats. + */ + +import { Type } from "typebox"; +import { defineTool } from "@earendil-works/pi-coding-agent"; +import ExcelJS from "exceljs"; + +export function createFileTools() { + const readExcel = defineTool({ + name: "read_excel", + label: "Read Excel File", + description: + "Read an Excel (.xlsx) file and return sheet data as JSON. Supports sheet selection and row limits.", + parameters: Type.Object({ + path: Type.String({ description: "Path to the .xlsx file" }), + sheetName: Type.Optional( + Type.String({ description: "Sheet name (default: first sheet)" }), + ), + maxRows: Type.Optional( + Type.Number({ description: "Max rows to return (default: 100)", default: 100 }), + ), + }), + execute: async (_, params) => { + try { + const workbook = new ExcelJS.Workbook(); + await workbook.xlsx.readFile(params.path); + + const sheet = params.sheetName + ? workbook.getWorksheet(params.sheetName) + : workbook.worksheets[0]; + + if (!sheet) { + return { + content: [ + { + type: "text" as const, + text: `Sheet not found: ${params.sheetName ?? "(first sheet)"}`, + }, + ], + isError: true, + details: {}, + }; + } + + const headers: string[] = []; + const rows: Record[] = []; + const limit = params.maxRows ?? 100; + + sheet.eachRow((row, rowNumber) => { + if (rowNumber === 1) { + row.eachCell((cell) => { + headers.push(String(cell.value ?? `col_${headers.length}`)); + }); + } else if (rowNumber <= limit + 1) { + const values: Record = {}; + row.eachCell((cell, colNumber) => { + values[headers[colNumber - 1] ?? `col_${colNumber}`] = cell.value; + }); + rows.push(values); + } + }); + + return { + content: [ + { + type: "text" as const, + text: JSON.stringify( + { file: params.path, sheetName: sheet.name, headers, rowCount: rows.length, rows }, + null, + 2, + ), + }, + ], + details: {}, + }; + } catch (error) { + return { + content: [ + { + type: "text" as const, + text: `Failed to read Excel file: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + isError: true, + details: {}, + }; + } + }, + }); + + const writeExcel = defineTool({ + name: "write_excel", + label: "Write Excel File", + description: + "Write structured data to a new Excel (.xlsx) file. Data is an array of objects (each object is a row, keys are column headers).", + parameters: Type.Object({ + path: Type.String({ description: "Output file path (must end in .xlsx)" }), + sheetName: Type.String({ + description: "Sheet name", + default: "Sheet1", + }), + data: Type.Array(Type.Record(Type.String(), Type.Unknown()), { + description: "Array of row objects. Keys become column headers.", + }), + }), + execute: async (_, params) => { + try { + const workbook = new ExcelJS.Workbook(); + workbook.creator = "MosaicIQ"; + workbook.created = new Date(); + + const sheet = workbook.addWorksheet(params.sheetName); + + if (params.data.length > 0) { + const headers = Object.keys(params.data[0]); + sheet.addRow(headers); + + // Style header row + const headerRow = sheet.getRow(1); + headerRow.font = { bold: true }; + + for (const rowData of params.data) { + sheet.addRow(headers.map((h) => rowData[h] ?? "")); + } + + // Auto-fit column widths (approximate) + for (let i = 0; i < headers.length; i++) { + const maxLen = Math.max( + headers[i].length, + ...params.data.slice(0, 20).map((r: Record) => String(r[headers[i]] ?? "").length), + ); + sheet.getColumn(i + 1).width = Math.min(maxLen + 2, 40); + } + } + + await workbook.xlsx.writeFile(params.path); + + return { + content: [ + { + type: "text" as const, + text: `Wrote ${params.data.length} rows to ${params.path} (sheet: ${params.sheetName})`, + }, + ], + details: {}, + }; + } catch (error) { + return { + content: [ + { + type: "text" as const, + text: `Failed to write Excel file: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + isError: true, + details: {}, + }; + } + }, + }); + + const readCsv = defineTool({ + name: "read_csv", + label: "Read CSV File", + description: + "Parse a CSV file and return rows as JSON objects. Handles quoted fields and custom delimiters.", + parameters: Type.Object({ + path: Type.String({ description: "Path to the CSV file" }), + delimiter: Type.Optional( + Type.String({ description: "Field delimiter (default: comma)", default: "," }), + ), + maxRows: Type.Optional( + Type.Number({ description: "Max rows to return (default: 500)", default: 500 }), + ), + }), + execute: async (_, params) => { + try { + const fs = await import("fs/promises"); + const content = await fs.readFile(params.path, "utf-8"); + const delimiter = params.delimiter ?? ","; + const lines = content.trim().split("\n"); + const headers = lines[0].split(delimiter).map((h) => h.trim().replace(/^"|"$/g, "")); + const maxRows = params.maxRows ?? 500; + const rows = lines.slice(1, maxRows + 1).map((line) => { + const values = splitCsvLine(line, delimiter); + const obj: Record = {}; + headers.forEach((h, i) => { + obj[h] = (values[i] ?? "").trim().replace(/^"|"$/g, ""); + }); + return obj; + }); + + return { + content: [ + { + type: "text" as const, + text: JSON.stringify({ file: params.path, headers, rowCount: rows.length, rows }, null, 2), + }, + ], + details: {}, + }; + } catch (error) { + return { + content: [ + { + type: "text" as const, + text: `Failed to read CSV file: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + isError: true, + details: {}, + }; + } + }, + }); + + return [readExcel, writeExcel, readCsv]; +} + +/** + * Simple CSV line splitter that respects quoted fields. + */ +function splitCsvLine(line: string, delimiter: string): string[] { + const result: string[] = []; + let current = ""; + let inQuotes = false; + + for (let i = 0; i < line.length; i++) { + const char = line[i]; + if (char === '"') { + if (inQuotes && line[i + 1] === '"') { + current += '"'; + i++; + } else { + inQuotes = !inQuotes; + } + } else if (char === delimiter && !inQuotes) { + result.push(current); + current = ""; + } else { + current += char; + } + } + result.push(current); + return result; +} diff --git a/packages/shared/src/tools/index.ts b/packages/shared/src/tools/index.ts new file mode 100644 index 0000000..fcb2e26 --- /dev/null +++ b/packages/shared/src/tools/index.ts @@ -0,0 +1,27 @@ +/** + * Tool barrel export + */ + +import type { Db } from "../db/database.js"; +import { createCompanyTools } from "./companyTools.js"; +import { createModelTools } from "./modelTools.js"; +import { createMemoTools } from "./memoTools.js"; +import { createValidationTools } from "./validationTools.js"; +import { createFileTools } from "./fileTools.js"; + +/** + * Create all custom tools for a given DB instance. + * These tools are passed to the Pi SDK session so the LLM can access + * MosaicIQ's SQLite data and manipulate files directly. + */ +export function createAllTools(db: Db) { + return [ + ...createCompanyTools(db), + ...createModelTools(db), + ...createMemoTools(db), + ...createValidationTools(db), + ...createFileTools(), + ]; +} + +export { createCompanyTools, createModelTools, createMemoTools, createValidationTools, createFileTools }; diff --git a/packages/shared/src/tools/memoTools.ts b/packages/shared/src/tools/memoTools.ts new file mode 100644 index 0000000..dc8dd5d --- /dev/null +++ b/packages/shared/src/tools/memoTools.ts @@ -0,0 +1,165 @@ +/** + * Custom tools: Investment memo read/write + */ + +import { Type } from "typebox"; +import { defineTool } from "@earendil-works/pi-coding-agent"; +import type { Db } from "../db/database.js"; +import { + resolveCompany, + getMemo, + updateMemoSection, + addMemoAnnotation, + resolveMemoAnnotation, + updateMemoSectionReview, +} from "../db/queries.js"; + +export function createMemoTools(db: Db) { + const getMemoTool = defineTool({ + name: "get_memo", + label: "Get Investment Memo", + description: + "Get the full investment memo for a company including all sections, citations, annotations, and review status.", + parameters: Type.Object({ + companyIdOrTicker: Type.String({ description: "Company ID or ticker symbol" }), + }), + execute: async (_, params) => { + const company = resolveCompany(db, params.companyIdOrTicker); + if (!company) { + return { + content: [{ type: "text" as const, text: `Company "${params.companyIdOrTicker}" not found.` }], + isError: true, + details: {} as any, + }; + } + const memo = getMemo(db, company.id); + return { + content: [{ type: "text" as const, text: JSON.stringify(memo, null, 2) }], + details: {} as any, + }; + }, + }); + + const updateSectionTool = defineTool({ + name: "update_memo_section", + label: "Update Memo Section", + description: + "Update a specific section of the investment memo. Use this to write or revise content.", + parameters: Type.Object({ + companyIdOrTicker: Type.String({ description: "Company ID or ticker symbol" }), + sectionId: Type.String({ description: "Section ID (e.g. 'thesis', 'variant-perception', 'valuation')" }), + content: Type.String({ description: "New section content in markdown" }), + }), + execute: async (_, params) => { + const company = resolveCompany(db, params.companyIdOrTicker); + if (!company) { + return { + content: [{ type: "text" as const, text: `Company "${params.companyIdOrTicker}" not found.` }], + isError: true, + details: {} as any, + }; + } + try { + const section = updateMemoSection(db, company.id, params.sectionId, { + content: params.content, + }); + return { + content: [ + { type: "text" as const, text: `Updated section "${params.sectionId}"` }, + ], + details: { section } as any, + }; + } catch (error) { + return { + content: [ + { + type: "text" as const, + text: `Failed to update section: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + isError: true, + details: {} as any, + }; + } + }, + }); + + const addAnnotationTool = defineTool({ + name: "add_memo_annotation", + label: "Add Memo Annotation", + description: + "Add an annotation (highlight, comment, or strike) to a memo section for review.", + parameters: Type.Object({ + companyIdOrTicker: Type.String({ description: "Company ID or ticker symbol" }), + sectionId: Type.String({ description: "Section ID" }), + kind: Type.Union( + [Type.Literal("highlight"), Type.Literal("comment"), Type.Literal("strike")], + { description: "Annotation kind" }, + ), + selectedText: Type.String({ description: "The text being annotated" }), + comment: Type.Optional(Type.String({ description: "Comment text (required for comment kind)" })), + }), + execute: async (_, params) => { + const company = resolveCompany(db, params.companyIdOrTicker); + if (!company) { + return { + content: [{ type: "text" as const, text: `Company "${params.companyIdOrTicker}" not found.` }], + isError: true, + details: {} as any, + }; + } + const annotation = addMemoAnnotation(db, company.id, { + sectionId: params.sectionId, + kind: params.kind, + selectedText: params.selectedText, + comment: params.comment, + createdBy: "AI", + createdAt: new Date().toISOString(), + status: "open", + }); + return { + content: [{ type: "text" as const, text: `Added ${params.kind} annotation` }], + details: { annotation } as any, + }; + }, + }); + + const reviewSectionTool = defineTool({ + name: "review_memo_section", + label: "Update Memo Section Review", + description: + "Set the review status of a memo section (pending, in_review, approved, changes_requested).", + parameters: Type.Object({ + companyIdOrTicker: Type.String({ description: "Company ID or ticker symbol" }), + sectionId: Type.String({ description: "Section ID" }), + status: Type.Union( + [ + Type.Literal("pending"), + Type.Literal("in_review"), + Type.Literal("approved"), + Type.Literal("changes_requested"), + ], + { description: "New review status" }, + ), + }), + execute: async (_, params) => { + const company = resolveCompany(db, params.companyIdOrTicker); + if (!company) { + return { + content: [{ type: "text" as const, text: `Company "${params.companyIdOrTicker}" not found.` }], + isError: true, + details: {} as any, + }; + } + const review = updateMemoSectionReview(db, company.id, params.sectionId, params.status); + return { + content: [ + { type: "text" as const, text: `Section "${params.sectionId}" review status set to "${params.status}"` }, + ], + details: { review } as any, + }; + }, + }); + + return [getMemoTool, updateSectionTool, addAnnotationTool, reviewSectionTool]; +} diff --git a/packages/shared/src/tools/modelTools.ts b/packages/shared/src/tools/modelTools.ts new file mode 100644 index 0000000..8fc399f --- /dev/null +++ b/packages/shared/src/tools/modelTools.ts @@ -0,0 +1,153 @@ +/** + * Custom tools: Financial model read/write + */ + +import { Type } from "typebox"; +import { defineTool } from "@earendil-works/pi-coding-agent"; +import type { Db } from "../db/database.js"; +import { resolveCompany, getModel, updateModelCell, createModelRow, deleteModelRow } from "../db/queries.js"; + +export function createModelTools(db: Db) { + const getModelTool = defineTool({ + name: "get_financial_model", + label: "Get Financial Model", + description: + "Get the financial model for a company. Returns headers (time periods) and rows (line items) with actual/forecast/total kinds.", + parameters: Type.Object({ + companyIdOrTicker: Type.String({ + description: "Company ID or ticker symbol", + }), + tab: Type.Union( + [Type.Literal("income"), Type.Literal("balance"), Type.Literal("operating")], + { description: "Which model tab to retrieve" }, + ), + }), + execute: async (_, params) => { + const company = resolveCompany(db, params.companyIdOrTicker); + if (!company) { + return { + content: [{ type: "text" as const, text: `Company "${params.companyIdOrTicker}" not found.` }], + isError: true, + details: {} as any, + }; + } + const model = getModel(db, company.id, params.tab); + return { + content: [{ type: "text" as const, text: JSON.stringify(model, null, 2) }], + details: {} as any, + }; + }, + }); + + const updateCellTool = defineTool({ + name: "update_model_cell", + label: "Update Model Cell", + description: "Update a single cell in the financial model by row index and column index.", + parameters: Type.Object({ + companyIdOrTicker: Type.String({ description: "Company ID or ticker symbol" }), + tab: Type.String({ description: "Model tab (income, balance, operating)" }), + row: Type.Number({ description: "Row index (0-based)" }), + col: Type.Number({ description: "Column index (0-based)" }), + value: Type.String({ description: "New cell value" }), + }), + execute: async (_, params) => { + const company = resolveCompany(db, params.companyIdOrTicker); + if (!company) { + return { + content: [{ type: "text" as const, text: `Company "${params.companyIdOrTicker}" not found.` }], + isError: true, + details: {} as any, + }; + } + const result = updateModelCell(db, company.id, params.tab, params.row, params.col, params.value); + return { + content: [ + { + type: "text" as const, + text: result.ok + ? `Updated cell [${params.row}, ${params.col}] = ${params.value}` + : `Failed to update cell. Row or column out of range.`, + }, + ], + isError: !result.ok, + details: result as any, + }; + }, + }); + + const createRowTool = defineTool({ + name: "create_model_row", + label: "Create Model Row", + description: "Add a new row to the financial model (e.g. a new line item or forecast row).", + parameters: Type.Object({ + companyIdOrTicker: Type.String({ description: "Company ID or ticker symbol" }), + tab: Type.String({ description: "Model tab" }), + label: Type.String({ description: "Row label (e.g. 'Revenue', 'COGS')" }), + kind: Type.Union( + [Type.Literal("actual"), Type.Literal("forecast"), Type.Literal("total")], + { description: "Row kind" }, + ), + values: Type.Optional( + Type.Array(Type.String(), { description: "Initial cell values" }), + ), + }), + execute: async (_, params) => { + const company = resolveCompany(db, params.companyIdOrTicker); + if (!company) { + return { + content: [{ type: "text" as const, text: `Company "${params.companyIdOrTicker}" not found.` }], + isError: true, + details: {} as any, + }; + } + const result = createModelRow(db, company.id, params.tab, { + label: params.label, + kind: params.kind, + values: params.values ?? [], + }); + return { + content: [ + { + type: "text" as const, + text: `Created row "${params.label}" (${params.kind}) at position ${result.position}`, + }, + ], + details: result as any, + }; + }, + }); + + const deleteRowTool = defineTool({ + name: "delete_model_row", + label: "Delete Model Row", + description: "Delete a row from the financial model by index.", + parameters: Type.Object({ + companyIdOrTicker: Type.String({ description: "Company ID or ticker symbol" }), + tab: Type.String({ description: "Model tab" }), + row: Type.Number({ description: "Row index to delete (0-based)" }), + }), + execute: async (_, params) => { + const company = resolveCompany(db, params.companyIdOrTicker); + if (!company) { + return { + content: [{ type: "text" as const, text: `Company "${params.companyIdOrTicker}" not found.` }], + isError: true, + details: {} as any, + }; + } + const ok = deleteModelRow(db, company.id, params.tab, params.row); + return { + content: [ + { + type: "text" as const, + text: ok ? `Deleted row ${params.row}` : `Row ${params.row} not found or could not be deleted`, + }, + ], + isError: !ok, + details: {} as any, + }; + }, + }); + + return [getModelTool, updateCellTool, createRowTool, deleteRowTool]; +} diff --git a/packages/shared/src/tools/validationTools.ts b/packages/shared/src/tools/validationTools.ts new file mode 100644 index 0000000..708bf37 --- /dev/null +++ b/packages/shared/src/tools/validationTools.ts @@ -0,0 +1,73 @@ +/** + * Custom tools: Validation status queries + */ + +import { Type } from "typebox"; +import { defineTool } from "@earendil-works/pi-coding-agent"; +import type { Db } from "../db/database.js"; +import { resolveCompany, getMemo } from "../db/queries.js"; + +export function createValidationTools(db: Db) { + const getValidationStatusTool = defineTool({ + name: "get_validation_status", + label: "Get Validation Status", + description: + "Get the current validation/review status of all memo sections for a company. Shows which sections are verified, flagged, or unverified.", + parameters: Type.Object({ + companyIdOrTicker: Type.String({ description: "Company ID or ticker symbol" }), + }), + execute: async (_, params) => { + const company = resolveCompany(db, params.companyIdOrTicker); + if (!company) { + return { + content: [{ type: "text" as const, text: `Company "${params.companyIdOrTicker}" not found.` }], + isError: true, + details: {}, + }; + } + const memo = getMemo(db, company.id); + const reviewStatus = memo.sectionReviews.map((r) => ({ + sectionId: r.sectionId, + status: r.status, + updatedAt: r.updatedAt, + })); + return { + content: [{ type: "text" as const, text: JSON.stringify(reviewStatus, null, 2) }], + details: {}, + }; + }, + }); + + const getAnnotationsTool = defineTool({ + name: "get_memo_annotations", + label: "Get Memo Annotations", + description: + "List all open annotations (comments, highlights, strikes) on the investment memo.", + parameters: Type.Object({ + companyIdOrTicker: Type.String({ description: "Company ID or ticker symbol" }), + openOnly: Type.Optional( + Type.Boolean({ description: "Only return open (unresolved) annotations", default: true }), + ), + }), + execute: async (_, params) => { + const company = resolveCompany(db, params.companyIdOrTicker); + if (!company) { + return { + content: [{ type: "text" as const, text: `Company "${params.companyIdOrTicker}" not found.` }], + isError: true, + details: {}, + }; + } + const memo = getMemo(db, company.id); + const annotations = params.openOnly ?? true + ? memo.annotations.filter((a) => a.status === "open") + : memo.annotations; + return { + content: [{ type: "text" as const, text: JSON.stringify(annotations, null, 2) }], + details: {}, + }; + }, + }); + + return [getValidationStatusTool, getAnnotationsTool]; +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 588ea95..b68c812 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,7 +23,7 @@ importers: version: 4.0.4 '@tailwindcss/vite': specifier: ^4.2.4 - version: 4.2.4(vite@7.3.3(@types/node@22.19.18)(jiti@2.7.0)(lightningcss@1.32.0)) + version: 4.2.4(vite@7.3.3(@types/node@22.19.18)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0)) '@types/node': specifier: ^22.15.3 version: 22.19.18 @@ -35,7 +35,7 @@ importers: version: 19.2.3(@types/react@19.2.14) '@vitejs/plugin-react': specifier: ^5.0.0 - version: 5.2.0(vite@7.3.3(@types/node@22.19.18)(jiti@2.7.0)(lightningcss@1.32.0)) + version: 5.2.0(vite@7.3.3(@types/node@22.19.18)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0)) concurrently: specifier: ^9.1.2 version: 9.2.1 @@ -53,10 +53,10 @@ importers: version: 5.9.3 vite: specifier: ^7.0.0 - version: 7.3.3(@types/node@22.19.18)(jiti@2.7.0)(lightningcss@1.32.0) + version: 7.3.3(@types/node@22.19.18)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0) vitest: specifier: ^3.2.4 - version: 3.2.4(@types/node@22.19.18)(jiti@2.7.0)(lightningcss@1.32.0) + version: 3.2.4(@types/node@22.19.18)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0) wait-on: specifier: ^8.0.3 version: 8.0.5 @@ -93,6 +93,9 @@ importers: packages/shared: dependencies: + '@earendil-works/pi-coding-agent': + specifier: ^0.74.0 + version: 0.74.0(ws@8.20.1)(zod@3.25.76) '@mosaiciq/contracts': specifier: workspace:* version: link:../contracts @@ -105,6 +108,9 @@ importers: pptxgenjs: specifier: ^4.0.1 version: 4.0.1 + typebox: + specifier: ^1.1.38 + version: 1.1.38 devDependencies: '@types/better-sqlite3': specifier: ^7.6.13 @@ -112,6 +118,148 @@ importers: packages: + '@anthropic-ai/sdk@0.91.1': + resolution: {integrity: sha512-LAmu761tSN9r66ixvmciswUj/ZC+1Q4iAfpedTfSVLeswRwnY3n2Nb6Tsk+cLPP28aLOPWeMgIuTuCcMC6W/iw==} + hasBin: true + peerDependencies: + zod: ^3.25.0 || ^4.0.0 + peerDependenciesMeta: + zod: + optional: true + + '@aws-crypto/crc32@5.2.0': + resolution: {integrity: sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==} + engines: {node: '>=16.0.0'} + + '@aws-crypto/sha256-browser@5.2.0': + resolution: {integrity: sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==} + + '@aws-crypto/sha256-js@5.2.0': + resolution: {integrity: sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==} + engines: {node: '>=16.0.0'} + + '@aws-crypto/supports-web-crypto@5.2.0': + resolution: {integrity: sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==} + + '@aws-crypto/util@5.2.0': + resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} + + '@aws-sdk/client-bedrock-runtime@3.1046.0': + resolution: {integrity: sha512-MT+nf7bna9gEohYFqUbPivR/XFPq7K8PmvgZY0h+kC/4k9SYDjQjsuA+sGH2AaZwJmMGXhmL4FpWRsYMc5gOQw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/core@3.974.9': + resolution: {integrity: sha512-bXxosFunr+v/kqNb99r1NRkrVBha7CG036fRSpWGbC1A/e363XFQN6wcZMx7MYTdRr1tYwNnkrWX2xc1rT3BCQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-env@3.972.35': + resolution: {integrity: sha512-WkFQ8BedszVomhh/Zzs8WwnE/XBmTqZjoQVB8u/4zH6kZCjouXZpPpb93gD8m0EZmzAl7dxHE/y+yDpuKzNCMw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-http@3.972.37': + resolution: {integrity: sha512-ylx0ZJTU+2eNcvXQ69VNR3TVSYa/ibpvdK717/NxqR9aXRMn2QRWZaiI8aa5yY/fOWZ5mknSmxGaVxxtdwv3EA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-ini@3.972.39': + resolution: {integrity: sha512-QhRSrdkk+Gq0AFIylpiI0N6lcJqFYV9Jtr4Luz5FpYOYbjJSfyTG6iLhnK/UPIgN1Jnon8WAmSC//16XYGvwkA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-login@3.972.39': + resolution: {integrity: sha512-1hU0NtC04QbFIuoBuF4aQ2A97GsSE5/A0ZJpDijwexsBREIQ4KPRYl3v/FfKCPBYsaTeGjkOFx5nLhWHY24LOw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-node@3.972.40': + resolution: {integrity: sha512-ZgrQaGkpyTlVSCCsffzijVg+KgftTAWYvI5Otc36J/4jNiHb+7MmBiJIR0a5AHLvifC92PiYHt5pijP0dswd1w==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-process@3.972.35': + resolution: {integrity: sha512-hNj1rAwZWT1vfz54BwH8FUWxZuqStrM25Q5LEIwn2erHPMRVAjLlpZqEbCEEqS99eEEOhdeetnS0WeNa3iYeEQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-sso@3.972.39': + resolution: {integrity: sha512-mwIPNPldyCZkvHozb6E0X/vuQLN1UCjcA6MwUf1gaO7EwghCmuNZXatq0L3zptKFvPC4Nds7+WFUkifI1XmbSw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-web-identity@3.972.39': + resolution: {integrity: sha512-b9HT8CnpyPVn1hU14Q7ihjwSPlRzToYmRYJxRd5jNHEZ43lrIhoLaTT8JmfQQt5j5M8rTX1iN1X8mvu0SM1dXA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/eventstream-handler-node@3.972.15': + resolution: {integrity: sha512-Al7z1qKPUIRILnB3Ggd1Tz88wjSkQjDErajR7YY33mquTbeN0gTDtJtclNtyhQMWr7ZRpQk0v5/xop4fjT0sug==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-eventstream@3.972.11': + resolution: {integrity: sha512-A0Z45YInBwsAabF8jf9hEQjXDuq4gFHNNqxCYuk8iREFZ7hw0NZ6+7FFlYa11gJ+i6y79C4J6giQ7fa1EDRYxw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-host-header@3.972.11': + resolution: {integrity: sha512-CBC6+tVYaOJo7QXgN1zJ4Ba2f3/Cpy4eRViYFimXW/O5Mn8hBmgXXzHu4vy4ubT80YWnp8aCFygr7dTOa14yQg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-logger@3.972.10': + resolution: {integrity: sha512-OOuGvvz1Dm20SjZo5oEBePFqxt5nf8AwkNDSyUHvD9/bfNASmstcYxFAHUowy4n6Io7mWUZ04JURZwSBvyQanQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-recursion-detection@3.972.12': + resolution: {integrity: sha512-5eltYxKB4MfdQv7/VhWxRbAVQKow5dz9votRFigTYrWJHMQXwLMltlbk7KFWSZh5NDBySfmjT7Jv/DWfYCmDng==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-user-agent@3.972.39': + resolution: {integrity: sha512-MlNSvNsSVlMKKWaCzA0GP1nS4Cuq3WCXUN1vmMvd+Ctztib5kmRcpmTtKx9kikN8szAc+gcdp7uqJJervV2nQg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-websocket@3.972.17': + resolution: {integrity: sha512-0PRAeIunuJRAweM/YWATe0jCHx4BMzSuwry461/TgcwMc8mNShwTdXiG6eEQBD7u+nNPC8Inp9KXjSB6D7uNYg==} + engines: {node: '>= 14.0.0'} + + '@aws-sdk/nested-clients@3.997.7': + resolution: {integrity: sha512-jT2AXOODobQfTYGC2SChMSnZ/voIcRV/LHlY1suyhY1bdgP/voKkhEg8Ci1jiGQ4lBiaso5BEAV3ZWWpPTfmYA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/region-config-resolver@3.972.14': + resolution: {integrity: sha512-VuLXVmm7+lKVxqFcOItPkXhjbJ02iUfxkxheRu41SfWf6/xrZup2A2SwHZos/LeQGu3SBHeqTQht80Uo3ienPA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/signature-v4-multi-region@3.996.26': + resolution: {integrity: sha512-2N62veqdMZBCwQUHsbhtnaovOFjOa5Dn3dAD1nRqFTUXR4QmirT3HZnfus/L1DS08Vm5CkoKmL0iMVt6YbqEag==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/token-providers@3.1046.0': + resolution: {integrity: sha512-9je8nZt+ntB8IjhpGNayU/AkBgvq/f4aFO2bH1LSNC5JX6K8zY4LUnr/ymqunePrwq+B5OVBpL7ILjYzMFSZAw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/types@3.973.8': + resolution: {integrity: sha512-gjlAdtHMbtR9X5iIhVUvbVcy55KnznpC6bkDUWW9z915bi0ckdUr5cjf16Kp6xq0bP5HBD2xzgbL9F9Quv5vUw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/util-endpoints@3.996.9': + resolution: {integrity: sha512-ibx8Vd73rCTHekNGeXX8cpGWoBKbNAlwKHL3yjSxxttu5QnNDaSAM7/0MFYDjU31/F4lyrPoQcGirT0ew61xcg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/util-locate-window@3.965.5': + resolution: {integrity: sha512-WhlJNNINQB+9qtLtZJcpQdgZw3SCDCpXdUJP7cToGwHbCWCnRckGlc6Bx/OhWwIYFNAn+FIydY8SZ0QmVu3xTQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/util-user-agent-browser@3.972.11': + resolution: {integrity: sha512-kq3RS6XQtHMrLFShbkem6h+8fxazB3jEIsbMC6aaSInOciRGE+eGAqTgJ+obO7Euo/pjM8thVqLiLISEH9X9DA==} + + '@aws-sdk/util-user-agent-node@3.973.25': + resolution: {integrity: sha512-066hKH/0nvV7x4ofV/iK9kz8r/qNfcR6rzuEOFqI2vQL/fcTTsDAbTw0jmXkyMzANK8ltQdALj19ns3zuOJiUw==} + engines: {node: '>=20.0.0'} + peerDependencies: + aws-crt: '>=1.0.0' + peerDependenciesMeta: + aws-crt: + optional: true + + '@aws-sdk/xml-builder@3.972.23': + resolution: {integrity: sha512-A0YmgYFv+hTI9c17Ntvd2hSehm9bmJfkb+ggADBwVKA8H/3+Jx94SzR2qOB9bAA9WFeDqnfz9PKKQ+D+YAKomA==} + engines: {node: '>=20.0.0'} + + '@aws/lambda-invoke-store@0.2.4': + resolution: {integrity: sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ==} + engines: {node: '>=18.0.0'} + '@babel/code-frame@7.29.0': resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} engines: {node: '>=6.9.0'} @@ -204,6 +352,10 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/runtime@7.29.2': + resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} + engines: {node: '>=6.9.0'} + '@babel/template@7.28.6': resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} engines: {node: '>=6.9.0'} @@ -220,6 +372,27 @@ packages: resolution: {integrity: sha512-JeSVu/m8x/zpp4CLjYHVNXuhEyOkhPXuxM8YOXjh6L4LlvQNKuUNOTo5KdBuKAcTDHw8DquToTaEkhsBqPXOaA==} engines: {node: ^22.18.0 || >=24.11.0} + '@borewit/text-codec@0.2.2': + resolution: {integrity: sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==} + + '@earendil-works/pi-agent-core@0.74.0': + resolution: {integrity: sha512-6GMR7/wwjEJ1EsXLWEz03QOWin4AMrJ/AZoMpgm5DJ6GHsF6q6GOhQbj5Zip4dow3vo/TmBAVqM+vmGfrjGAFQ==} + engines: {node: '>=20.0.0'} + + '@earendil-works/pi-ai@0.74.0': + resolution: {integrity: sha512-7M7qcrZY/KEkH4wFkX3eqzvmKru4O88wezNKoN0KD2m4aAOmp9tdW2xCmUgSTSWlKB7b2Xw9QtAgrzHtg6t6iw==} + engines: {node: '>=20.0.0'} + hasBin: true + + '@earendil-works/pi-coding-agent@0.74.0': + resolution: {integrity: sha512-Q5GikbB5vRBrsrrf/uvet53rPSQ1sn5I5mO+l7sIobdXYpS04/X2oOc2UHFm90fNdkl3yU+ANTZL0zOtHbnqRw==} + engines: {node: '>=20.6.0'} + hasBin: true + + '@earendil-works/pi-tui@0.74.0': + resolution: {integrity: sha512-1aIfXZp7D/z+1VlZX8BZcs6pgO8rjmil7kwyhctNDsWvce3Yfl8GVgu4eq+I0Mjhr8Cj+ipBiv9CLIzdoyCOIQ==} + engines: {node: '>=20.0.0'} + '@electron/get@2.0.3': resolution: {integrity: sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==} engines: {node: '>=12'} @@ -400,6 +573,15 @@ packages: '@fast-csv/parse@4.3.6': resolution: {integrity: sha512-uRsLYksqpbDmWaSmzvJcuApSEe38+6NQZBUsuAyMZKqHxH0g1wcJgsKUvN3WC8tewaqFjBMMGrkHmC+T7k8LvA==} + '@google/genai@1.52.0': + resolution: {integrity: sha512-gwSvbpiN/17O9TbsqSsE/OzZcpv5Fo4RQjdngGgogtuB9RsyJ8ZHhX5KjHj1bp5N9snN2eK8LDGXSaWW2hof8Q==} + engines: {node: '>=20.0.0'} + peerDependencies: + '@modelcontextprotocol/sdk': ^1.25.2 + peerDependenciesMeta: + '@modelcontextprotocol/sdk': + optional: true + '@hapi/address@5.1.1': resolution: {integrity: sha512-A+po2d/dVoY7cYajycYI43ZbYMXukuopIsqCjh5QzsBCipDtdofHntljDlpccMjIfTy6UOkg+5KPriwYch2bXA==} engines: {node: '>=14.0.0'} @@ -444,15 +626,119 @@ packages: resolution: {integrity: sha512-1DpKU0Z5ThltBwjNySMC14g0CkbyhCaz9FkhxqNsZI6uAPJXFS8cMXlBKo26FJ8ZuW6S9GCMcR9IO5k2X5/9Fg==} engines: {node: '>= 12.13.0'} + '@mariozechner/clipboard-darwin-arm64@0.3.2': + resolution: {integrity: sha512-uBf6K7Je1ihsgvmWxA8UCGCeI+nbRVRXoarZdLjl6slz94Zs1tNKFZqx7aCI5O1i3e0B6ja82zZ06BWrl0MCVw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@mariozechner/clipboard-darwin-universal@0.3.2': + resolution: {integrity: sha512-mxSheKTW2U9LsBdXy0SdmdCAE5HqNS9QUmpNHLnfJ+SsbFKALjEZc5oRrVMXxGQSirDvYf5bjmRyT0QYYonnlg==} + engines: {node: '>= 10'} + os: [darwin] + + '@mariozechner/clipboard-darwin-x64@0.3.2': + resolution: {integrity: sha512-U1BcVEoidvwIp95+HJswSW+xr28EQiHR7rZjH6pn8Sja5yO4Yoe3yCN0Zm8Lo72BbSOK/fTSq0je7CJpaPCspg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@mariozechner/clipboard-linux-arm64-gnu@0.3.2': + resolution: {integrity: sha512-BsinwG3yWTIjdgNCxsFlip7LkfwPk+ruw/aFCXHUg/fb5XC/Ksp+YMQ7u0LUtiKzIv/7LMXgZInJQH6gxbAaqQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@mariozechner/clipboard-linux-arm64-musl@0.3.2': + resolution: {integrity: sha512-0/Gi5Xq2V6goXBop19ePoHvXsmJD9SzFlO3S+d6+T2b+BlPcpOu3Oa0wTjl+cZrLAAEzA86aPNBI+VVAFDFPKw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@mariozechner/clipboard-linux-riscv64-gnu@0.3.2': + resolution: {integrity: sha512-2AFFiXB24qf0zOZsxI1GJGb9wQGlOJyN6UwoXqmKS3dpQi/l6ix30IzDDA4c4ZcCcx4D+9HLYXhC1w7Sov8pXA==} + engines: {node: '>= 10'} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@mariozechner/clipboard-linux-x64-gnu@0.3.2': + resolution: {integrity: sha512-v6fVnsn7WMGg73Dab8QMwyFce7tzGfgEixKgzLP8f1GJqkJZi5zO4k4FOHzSgUufgLil63gnxvMpjWkgfeQN7A==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@mariozechner/clipboard-linux-x64-musl@0.3.2': + resolution: {integrity: sha512-xVUtnoMQ8v2JVyfJLKKXACA6avdnchdbBkTsZs8BgJQo29qwCp5NIHAUO8gbJ40iaEGToW5RlmVk2M9V0HsHEw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@mariozechner/clipboard-win32-arm64-msvc@0.3.2': + resolution: {integrity: sha512-AEgg95TNi8TGgak2wSXZkXKCvAUTjWoU1Pqb0ON7JHrX78p616XUFNTJohtIon3e0w6k0pYPZeCuqRCza/Tqeg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@mariozechner/clipboard-win32-x64-msvc@0.3.2': + resolution: {integrity: sha512-tGRuYpZwDOD7HBrCpyRuhGnHHSCknELvqwKKUG4JSfSB7JIU7LKRh6zx6fMUOQd8uISK35TjFg5UcNih+vJhFA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@mariozechner/clipboard@0.3.5': + resolution: {integrity: sha512-D3F+UrU9CR7roJt0zDLp6Oc+4/KlLDIrN4frH+6V90SJNW2KKUec1oCQIPaaDjCqeOsQyX9dyqYbImIQIM45PA==} + engines: {node: '>= 10'} + + '@mistralai/mistralai@2.2.1': + resolution: {integrity: sha512-uKU8CZmL2RzYKmplsU01hii4p3pe4HqJefpWNRWXm1Tcm0Sm4xXfwSLIy4k7ZCPlbETCGcp69E7hZs+WOJ5itQ==} + '@napi-rs/wasm-runtime@1.1.4': resolution: {integrity: sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==} peerDependencies: '@emnapi/core': ^1.7.1 '@emnapi/runtime': ^1.7.1 + '@nodable/entities@2.1.0': + resolution: {integrity: sha512-nyT7T3nbMyBI/lvr6L5TyWbFJAI9FTgVRakNoBqCD+PmID8DzFrrNdLLtHMwMszOtqZa8PAOV24ZqDnQrhQINA==} + '@oxc-project/types@0.130.0': resolution: {integrity: sha512-ibD2usx9JRu7f5pu2tMKMI4cpA4NgXJQoYRP4pQ7Pxmn1l6k/53qWtQWZayhYy3X4QZkt90Ot+mJEaeXouio6Q==} + '@protobufjs/aspromise@1.1.2': + resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} + + '@protobufjs/base64@1.1.2': + resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} + + '@protobufjs/codegen@2.0.5': + resolution: {integrity: sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==} + + '@protobufjs/eventemitter@1.1.0': + resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==} + + '@protobufjs/fetch@1.1.0': + resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==} + + '@protobufjs/float@1.0.2': + resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} + + '@protobufjs/inquire@1.1.1': + resolution: {integrity: sha512-mnzgDV26ueAvk7rsbt9L7bE0SuAoqyuys/sMMrmVcN5x9VsxpcG3rqAUSgDyLp0UZlmNfIbQ4fHfCtreVBk8Ew==} + + '@protobufjs/path@1.1.2': + resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} + + '@protobufjs/pool@1.1.0': + resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} + + '@protobufjs/utf8@1.1.1': + resolution: {integrity: sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg==} + '@quansync/fs@1.0.0': resolution: {integrity: sha512-4TJ3DFtlf1L5LDMaM6CanJ/0lckGNtJcMjQ1NAV6zDmA0tEHKZtxNKin8EgPaVX1YzljbxckyT2tJrpQKAtngQ==} @@ -695,10 +981,49 @@ packages: cpu: [x64] os: [win32] + '@silvia-odwyer/photon-node@0.3.4': + resolution: {integrity: sha512-bnly4BKB3KDTFxrUIcgCLbaeVVS8lrAkri1pEzskpmxu9MdfGQTy8b8EgcD83ywD3RPMsIulY8xJH5Awa+t9fA==} + '@sindresorhus/is@4.6.0': resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} engines: {node: '>=10'} + '@smithy/core@3.24.2': + resolution: {integrity: sha512-IKS7qX59fAGCYBmt5JChcDswQDupZqT2Yn2ZBA3UgTlsjRNNkQzZobbn95xoAAdtTyJmBiJB3Y02qR3rgy3Zog==} + engines: {node: '>=18.0.0'} + + '@smithy/credential-provider-imds@4.3.2': + resolution: {integrity: sha512-iYr9ekBjmZ+FwkiHEopqGscBbl78X62cq3p5Dd0eC+gNd7fybNZFQQdDuOQjTVmFymleuA8YRWZnuXWZ8B3kKA==} + engines: {node: '>=18.0.0'} + + '@smithy/fetch-http-handler@5.4.2': + resolution: {integrity: sha512-3wF40g8OOCA5BnwQUvwtzZqYBbWWftDjpAlWIUo6Yld3ZzJaMAKqg7MWQBPjE8oLaqvZQUE7tVGlZPsae6A4bQ==} + engines: {node: '>=18.0.0'} + + '@smithy/is-array-buffer@2.2.0': + resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==} + engines: {node: '>=14.0.0'} + + '@smithy/node-http-handler@4.7.2': + resolution: {integrity: sha512-EdksTZ8UXYxGUgQ4mpIKrHoaj9WVGsp66TpZuixLAz1Jex8YDLnS4RH9ktGED5aOpN0OJlEtrsC9IGt76go1eA==} + engines: {node: '>=18.0.0'} + + '@smithy/signature-v4@5.4.2': + resolution: {integrity: sha512-1km1OjdLRFuITWpCPofjFqzZ+tbeWuB72ZhcYjbjkCxZ21tTPfIs4GUxRrelMyKMLxLghGD58RENnXorU/O8cw==} + engines: {node: '>=18.0.0'} + + '@smithy/types@4.14.1': + resolution: {integrity: sha512-59b5HtSVrVR/eYNei3BUj3DCPKD/G7EtDDe7OEJE7i7FtQFugYo6MxbotS8mVJkLNVf8gYaAlEBwwtJ9HzhWSg==} + engines: {node: '>=18.0.0'} + + '@smithy/util-buffer-from@2.2.0': + resolution: {integrity: sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==} + engines: {node: '>=14.0.0'} + + '@smithy/util-utf8@2.3.0': + resolution: {integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==} + engines: {node: '>=14.0.0'} + '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} @@ -800,6 +1125,16 @@ packages: peerDependencies: vite: ^5.2.0 || ^6 || ^7 || ^8 + '@tokenizer/inflate@0.4.1': + resolution: {integrity: sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==} + engines: {node: '>=18'} + + '@tokenizer/token@0.3.0': + resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} + + '@tootallnate/quickjs-emscripten@0.23.0': + resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==} + '@tybys/wasm-util@0.10.2': resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==} @@ -839,6 +1174,9 @@ packages: '@types/keyv@3.1.4': resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} + '@types/mime-types@2.1.4': + resolution: {integrity: sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w==} + '@types/node@14.18.63': resolution: {integrity: sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==} @@ -856,6 +1194,9 @@ packages: '@types/responselike@1.0.3': resolution: {integrity: sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==} + '@types/retry@0.12.0': + resolution: {integrity: sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==} + '@types/yauzl@2.10.3': resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} @@ -898,10 +1239,18 @@ packages: resolution: {integrity: sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA==} engines: {node: ^20.17.0 || >=22.9.0} + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} + ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} + engines: {node: '>=12'} + ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} @@ -910,6 +1259,9 @@ packages: resolution: {integrity: sha512-44mvgtPvohuU/70DdY5Oz2AIrLJ9k6/5x4KmoSvPwO+5Moijo0+N9D0fKbbYZQWP1hNm5CpOf+E01jhxG/r8xg==} engines: {node: '>=14'} + any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + archiver-utils@2.1.0: resolution: {integrity: sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==} engines: {node: '>= 6'} @@ -930,6 +1282,10 @@ packages: resolution: {integrity: sha512-trmleAnZ2PxN/loHWVhhx1qeOHSRXq4TDsBBxq3GqeJitfk3+jTQ+v/C1km/KYq9M7wKqCewMh+/NAvVH7m+bw==} engines: {node: '>=20.19.0'} + ast-types@0.13.4: + resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==} + engines: {node: '>=4'} + async@3.2.6: resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} @@ -942,6 +1298,10 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} @@ -950,6 +1310,10 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + basic-ftp@5.3.1: + resolution: {integrity: sha512-bopVNp6ugyA150DDuZfPFdt1KZ5a94ZDiwX4hMgZDzF+GttD80lEy8kj98kbyhLXnPvhtIo93mdnLIjpCAeeOw==} + engines: {node: '>=10.0.0'} + better-sqlite3@12.10.0: resolution: {integrity: sha512-CyzaZRQKyHkB2ZInfTTl2nvT33EbDpjkLEbE8/Zck3Ll6O0qqvuGdrJ45HgtH+HykRg88ITY3AdreBGN70aBSQ==} engines: {node: 20.x || 22.x || 23.x || 24.x || 25.x || 26.x} @@ -958,6 +1322,9 @@ packages: resolution: {integrity: sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==} engines: {node: '>=0.6'} + bignumber.js@9.3.1: + resolution: {integrity: sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==} + binary@0.3.0: resolution: {integrity: sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==} @@ -977,12 +1344,19 @@ packages: resolution: {integrity: sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==} deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. + bowser@2.14.1: + resolution: {integrity: sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==} + brace-expansion@1.1.14: resolution: {integrity: sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==} brace-expansion@2.1.0: resolution: {integrity: sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==} + brace-expansion@5.0.6: + resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==} + engines: {node: 18 || 20 || >=22} + browserslist@4.28.2: resolution: {integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -991,6 +1365,9 @@ packages: buffer-crc32@0.2.13: resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} + buffer-equal-constant-time@1.0.1: + resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} + buffer-indexof-polyfill@1.0.2: resolution: {integrity: sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==} engines: {node: '>=0.10'} @@ -1036,6 +1413,10 @@ packages: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} + chalk@5.6.2: + resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + check-error@2.1.3: resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==} engines: {node: '>= 16'} @@ -1047,6 +1428,14 @@ packages: resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} engines: {node: '>=18'} + cli-highlight@2.1.11: + resolution: {integrity: sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==} + engines: {node: '>=8.0.0', npm: '>=5.0.0'} + hasBin: true + + cliui@7.0.4: + resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} + cliui@8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} @@ -1099,6 +1488,14 @@ packages: csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + data-uri-to-buffer@4.0.1: + resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} + engines: {node: '>= 12'} + + data-uri-to-buffer@6.0.2: + resolution: {integrity: sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==} + engines: {node: '>= 14'} + dayjs@1.11.20: resolution: {integrity: sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==} @@ -1138,6 +1535,10 @@ packages: defu@6.1.7: resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==} + degenerator@5.0.1: + resolution: {integrity: sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==} + engines: {node: '>= 14'} + delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} @@ -1149,6 +1550,10 @@ packages: detect-node@2.1.0: resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==} + diff@8.0.4: + resolution: {integrity: sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==} + engines: {node: '>=0.3.1'} + dts-resolver@3.0.0: resolution: {integrity: sha512-1T1f+z+4tl9XD+m+0HBgWoL/nm0bOIffyWaUuUSBlFg/86IWvfx+wjNaO/ybU0AJzG9/Mi5hBUgGV6zCmWEN7Q==} engines: {node: ^22.18.0 || >=24.0.0} @@ -1165,6 +1570,9 @@ packages: duplexer2@0.1.4: resolution: {integrity: sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==} + ecdsa-sig-formatter@1.0.11: + resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} + electron-to-chromium@1.5.353: resolution: {integrity: sha512-kOrWphBi8TOZyiJZqsgqIle0lw+tzmnQK83pV9dZUd01Nm2POECSyFQMAuarzZdYqQW7FH9RaYOuaRo3h+bQ3w==} @@ -1226,9 +1634,27 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} + escodegen@2.1.0: + resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} + engines: {node: '>=6.0'} + hasBin: true + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + exceljs@4.4.0: resolution: {integrity: sha512-XctvKaEMaj1Ii9oDOqbW/6e1gXknSY4g/aLCDicOXqBE4M0nRWkUu0PTp++UPNzoFY12BNHMfs/VadKIS6llvg==} engines: {node: '>=8.3.0'} @@ -1244,6 +1670,9 @@ packages: exponential-backoff@3.1.3: resolution: {integrity: sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==} + extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + extract-zip@2.0.1: resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} engines: {node: '>= 10.17.0'} @@ -1253,6 +1682,13 @@ packages: resolution: {integrity: sha512-2RNSpuwwsJGP0frGsOmTb9oUF+VkFSM4SyLTDgwf2ciHWTarN0lQTC+F2f/t5J9QjW+c65VFIAAu85GsvMIusw==} engines: {node: '>=10.0.0'} + fast-xml-builder@1.2.0: + resolution: {integrity: sha512-00aAWieqff+ZJhsXA4g1g7M8k+7AYoMUUHF+/zFb5U6Uv/P0Vl4QZo84/IcufzYalLuEj9928bXN9PbbFzMF0Q==} + + fast-xml-parser@5.7.2: + resolution: {integrity: sha512-P7oW7tLbYnhOLQk/Gv7cZgzgMPP/XN03K02/Jy6Y/NHzyIAIpxuZIM/YqAkfiXFPxA2CTm7NtCijK9EDu09u2w==} + hasBin: true + fd-slicer@1.1.0: resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} @@ -1265,6 +1701,14 @@ packages: picomatch: optional: true + fetch-blob@3.2.0: + resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} + engines: {node: ^12.20 || >= 14.13} + + file-type@21.3.4: + resolution: {integrity: sha512-Ievi/yy8DS3ygGvT47PjSfdFoX+2isQueoYP1cntFW1JLYAuS4GD7NUPGg4zv2iZfV52uDyk5w5Z0TdpRS6Q1g==} + engines: {node: '>=20'} + file-uri-to-path@1.0.0: resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} @@ -1281,6 +1725,10 @@ packages: resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} engines: {node: '>= 6'} + formdata-polyfill@4.0.10: + resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} + engines: {node: '>=12.20.0'} + fs-constants@1.0.0: resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} @@ -1304,6 +1752,14 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + gaxios@7.1.4: + resolution: {integrity: sha512-bTIgTsM2bWn3XklZISBTQX7ZSddGW+IO3bMdGaemHZ3tbqExMENHLx6kKZ/KlejgrMtj8q7wBItt51yegqalrA==} + engines: {node: '>=18'} + + gcp-metadata@8.1.2: + resolution: {integrity: sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==} + engines: {node: '>=18'} + gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} @@ -1312,6 +1768,10 @@ packages: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} + get-east-asian-width@1.6.0: + resolution: {integrity: sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==} + engines: {node: '>=18'} + get-intrinsic@1.3.0: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} @@ -1328,9 +1788,17 @@ packages: resolution: {integrity: sha512-/6gFNr0N04nob252sTQxyFLi3eKFRqIg1I87YcqAMT1i6SQrSF6KujUEQrtrjMV0H/eejTCltLdDSTEMzHbnsQ==} engines: {node: '>=20.20.0'} + get-uri@6.0.5: + resolution: {integrity: sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==} + engines: {node: '>= 14'} + github-from-package@0.0.0: resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} + glob@13.0.6: + resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==} + engines: {node: 18 || 20 || >=22} + glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me @@ -1343,6 +1811,14 @@ packages: resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} engines: {node: '>= 0.4'} + google-auth-library@10.6.2: + resolution: {integrity: sha512-e27Z6EThmVNNvtYASwQxose/G57rkRuaRbQyxM2bvYLLX/GqWZ5chWq2EBoUchJbCc57eC9ArzO5wMsEmWftCw==} + engines: {node: '>=18'} + + google-logging-utils@1.1.3: + resolution: {integrity: sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==} + engines: {node: '>=14'} + gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} @@ -1373,22 +1849,41 @@ packages: resolution: {integrity: sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==} engines: {node: '>= 0.4'} + highlight.js@10.7.3: + resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} + hookable@6.1.1: resolution: {integrity: sha512-U9LYDy1CwhMCnprUfeAZWZGByVbhd54hwepegYTK7Pi5NvqEj63ifz5z+xukznehT7i6NIZRu89Ay1AZmRsLEQ==} + hosted-git-info@9.0.3: + resolution: {integrity: sha512-Hc+ghLoSt6QaYZUv0WBiIvmMDZuZZ7oaDvdH8MbfOO4lOsxdXLEvuC6ePoGs9H1X9oCLyq6+NVN0MKqD+ydxyg==} + engines: {node: ^20.17.0 || >=22.9.0} + http-cache-semantics@4.2.0: resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==} + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + http2-wrapper@1.0.3: resolution: {integrity: sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==} engines: {node: '>=10.19.0'} + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + https@1.0.0: resolution: {integrity: sha512-4EC57ddXrkaF0x83Oj8sM6SLQHAWXw90Skqu2M4AEWENZ3F02dFJE/GARA8igO79tcgYqGrD7ae4f5L3um2lgg==} ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + image-size@1.2.1: resolution: {integrity: sha512-rH+46sQJ2dlwfjfhCyNx5thzrv+dtmBIhPHk0zgRUukHzZ/kRueTJXoYYsclBaKcSMBWuGbOFXtioLpzTb5euw==} engines: {node: '>=16.x'} @@ -1411,6 +1906,10 @@ packages: ini@1.3.8: resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + ip-address@10.2.0: + resolution: {integrity: sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==} + engines: {node: '>= 12'} + is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} @@ -1444,9 +1943,16 @@ packages: engines: {node: '>=6'} hasBin: true + json-bigint@1.0.0: + resolution: {integrity: sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==} + json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + json-schema-to-ts@3.1.1: + resolution: {integrity: sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==} + engines: {node: '>=16'} + json-stringify-safe@5.0.1: resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} @@ -1461,9 +1967,18 @@ packages: jszip@3.10.1: resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==} + jwa@2.0.1: + resolution: {integrity: sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==} + + jws@4.0.1: + resolution: {integrity: sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==} + keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + koffi@2.16.2: + resolution: {integrity: sha512-owU0MRwv6xkrVqCd+33uw6BaYppkTRXbO/rVdJNI2dvZG0gzyRhYwW25eWtc5pauwK8TGh3AbkFONSezdykfSA==} + lazystream@1.0.1: resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==} engines: {node: '>= 0.6.3'} @@ -1591,6 +2106,9 @@ packages: lodash@4.18.1: resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} + long@5.3.2: + resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} + loupe@3.2.1: resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} @@ -1598,12 +2116,25 @@ packages: resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==} engines: {node: '>=8'} + lru-cache@11.3.6: + resolution: {integrity: sha512-Gf/KoL3C/MlI7Bt0PGI9I+TeTC/I6r/csU58N4BSNc4lppLBeKsOdFYkK+dX0ABDUMJNfCHTyPpzwwO21Awd3A==} + engines: {node: 20 || >=22} + lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + lru-cache@7.18.3: + resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} + engines: {node: '>=12'} + magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + marked@15.0.12: + resolution: {integrity: sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==} + engines: {node: '>= 18'} + hasBin: true + matcher@3.0.0: resolution: {integrity: sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==} engines: {node: '>=10'} @@ -1616,10 +2147,18 @@ packages: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} + mime-db@1.54.0: + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} + mime-types@2.1.35: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} + mime-types@3.0.2: + resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} + engines: {node: '>=18'} + mimic-response@1.0.1: resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==} engines: {node: '>=4'} @@ -1628,6 +2167,10 @@ packages: resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} engines: {node: '>=10'} + minimatch@10.2.5: + resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} + engines: {node: 18 || 20 || >=22} + minimatch@3.1.5: resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} @@ -1656,6 +2199,9 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + nanoid@3.3.12: resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -1664,6 +2210,10 @@ packages: napi-build-utils@2.0.0: resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==} + netmask@2.1.1: + resolution: {integrity: sha512-eonl3sLUha+S1GzTPxychyhnUzKyeQkZ7jLjKrBagJgPla13F+uQ71HgpFefyHgqrjEbCPkDArxYsjY8/+gLKA==} + engines: {node: '>= 0.4.0'} + node-abi@3.92.0: resolution: {integrity: sha512-KdHvFWZjEKDf0cakgFjebl371GPsISX2oZHcuyKqM7DtogIsHrqKeLTo8wBHxaXRAQlY2PsPlZmfo+9ZCxEREQ==} engines: {node: '>=10'} @@ -1675,6 +2225,15 @@ packages: node-api-version@0.2.1: resolution: {integrity: sha512-2xP/IGGMmmSQpI1+O/k72jF/ykvZ89JeuKX3TLJAYPDVLUalrshrLHkeVcCCZqG/eEa635cr8IBYzgnDvM2O8Q==} + node-domexception@1.0.0: + resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} + engines: {node: '>=10.5.0'} + deprecated: Use your platform's native DOMException instead + + node-fetch@3.3.2: + resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + node-gyp@12.3.0: resolution: {integrity: sha512-QNcUWM+HgJplcPzBvFBZ9VXacyGZ4+VTOb80PwWR+TlVzoHbRKULNEzpRsnaoxG3Wzr7Qh7BYxGDU3CbKib2Yg==} engines: {node: ^20.17.0 || >=22.9.0} @@ -1696,6 +2255,10 @@ packages: resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==} engines: {node: '>=10'} + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + object-keys@1.1.1: resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} engines: {node: '>= 0.4'} @@ -1706,13 +2269,53 @@ packages: once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + openai@6.26.0: + resolution: {integrity: sha512-zd23dbWTjiJ6sSAX6s0HrCZi41JwTA1bQVs0wLQPZ2/5o2gxOJA5wh7yOAUgwYybfhDXyhwlpeQf7Mlgx8EOCA==} + hasBin: true + peerDependencies: + ws: ^8.18.0 + zod: ^3.25 || ^4.0 + peerDependenciesMeta: + ws: + optional: true + zod: + optional: true + p-cancelable@2.1.1: resolution: {integrity: sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==} engines: {node: '>=8'} + p-retry@4.6.2: + resolution: {integrity: sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==} + engines: {node: '>=8'} + + pac-proxy-agent@7.2.0: + resolution: {integrity: sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==} + engines: {node: '>= 14'} + + pac-resolver@7.0.1: + resolution: {integrity: sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==} + engines: {node: '>= 14'} + pako@1.0.11: resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + parse5-htmlparser2-tree-adapter@6.0.1: + resolution: {integrity: sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==} + + parse5@5.1.1: + resolution: {integrity: sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==} + + parse5@6.0.1: + resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==} + + partial-json@0.1.7: + resolution: {integrity: sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA==} + + path-expression-matcher@1.5.0: + resolution: {integrity: sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ==} + engines: {node: '>=14.0.0'} + path-is-absolute@1.0.1: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} engines: {node: '>=0.10.0'} @@ -1721,6 +2324,10 @@ packages: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} + path-scurry@2.0.2: + resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} + engines: {node: 18 || 20 || >=22} + pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} @@ -1767,6 +2374,20 @@ packages: resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} engines: {node: '>=0.4.0'} + proper-lockfile@4.1.2: + resolution: {integrity: sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==} + + protobufjs@7.5.8: + resolution: {integrity: sha512-dvpCIeLPbXZS/Ete7yLaO7RenOdken2NHKykBXbsaGxZT0UTltcarBciw+A78SRQs9iMAAVpsYA+l8b1hTePIA==} + engines: {node: '>=12.0.0'} + + proxy-agent@6.5.0: + resolution: {integrity: sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==} + engines: {node: '>= 14'} + + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + proxy-from-env@2.1.0: resolution: {integrity: sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==} engines: {node: '>=10'} @@ -1828,6 +2449,14 @@ packages: responselike@2.0.1: resolution: {integrity: sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==} + retry@0.12.0: + resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} + engines: {node: '>= 4'} + + retry@0.13.1: + resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} + engines: {node: '>= 4'} + rimraf@2.7.1: resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} deprecated: Rimraf versions prior to v4 are no longer supported @@ -1916,16 +2545,35 @@ packages: siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + simple-concat@1.0.1: resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} simple-get@4.0.1: resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==} + smart-buffer@4.2.0: + resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} + engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + + socks-proxy-agent@8.0.5: + resolution: {integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==} + engines: {node: '>= 14'} + + socks@2.8.9: + resolution: {integrity: sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw==} + engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} + source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + sprintf-js@1.1.3: resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} @@ -1949,6 +2597,10 @@ packages: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} + strip-ansi@7.2.0: + resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} + engines: {node: '>=12'} + strip-json-comments@2.0.1: resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} engines: {node: '>=0.10.0'} @@ -1956,6 +2608,13 @@ packages: strip-literal@3.1.0: resolution: {integrity: sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==} + strnum@2.3.0: + resolution: {integrity: sha512-ums3KNd42PGyx5xaoVTO1mjU1bH3NpY4vsrVlnv9PNGqQj8wd7rJ6nEypLrJ7z5vxK5RP0yMLo6J/Gsm62DI5Q==} + + strtok3@10.3.5: + resolution: {integrity: sha512-ki4hZQfh5rX0QDLLkOCj+h+CVNkqmp/CMf8v8kZpkNVK6jGQooMytqzLZYUVYIZcFZ6yDB70EfD8POcFXiF5oA==} + engines: {node: '>=18'} + sumchecker@3.0.1: resolution: {integrity: sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==} engines: {node: '>= 8.0'} @@ -1986,6 +2645,13 @@ packages: resolution: {integrity: sha512-dzGK0boVlC4W5QFuQN1EFSl3bIDYsk7Tj40U6eIBnK2k/8ml7TZ5agbI5j5+qnoVcAA+rNtBml8SEiLxZpNqRQ==} engines: {node: '>=18'} + thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + + thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} @@ -2016,6 +2682,10 @@ packages: resolution: {integrity: sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==} engines: {node: '>=14.14'} + token-types@6.1.2: + resolution: {integrity: sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==} + engines: {node: '>=14.16'} + traverse@0.3.9: resolution: {integrity: sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==} @@ -2023,6 +2693,9 @@ packages: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true + ts-algebra@2.0.0: + resolution: {integrity: sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==} + tsdown@0.22.0: resolution: {integrity: sha512-FgW0hHb27nGQA/+F3d5+U9wKXkfilk9DVkc5+7x/ZqF03g+Hoz/eeApT32jqxATt9eRoR+1jxk7MUMON+O4CXw==} engines: {node: ^22.18.0 || >=24.0.0} @@ -2067,11 +2740,18 @@ packages: resolution: {integrity: sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==} engines: {node: '>=10'} + typebox@1.1.38: + resolution: {integrity: sha512-pZ0aQPmMmXoUvSbeuWf/Hzsc+avNw/Zd6VeE8CFgkVGWyuHPJvqeJJDeJqLve+K70LvjYIoleGcoJHPT17cWoA==} + typescript@5.9.3: resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} engines: {node: '>=14.17'} hasBin: true + uint8array-extras@1.5.0: + resolution: {integrity: sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==} + engines: {node: '>=18'} + unconfig-core@7.5.0: resolution: {integrity: sha512-Su3FauozOGP44ZmKdHy2oE6LPjk51M/TRRjHv2HNCWiDvfvCoxC2lno6jevMA91MYAdCdwP05QnWdWpSbncX/w==} @@ -2082,6 +2762,10 @@ packages: resolution: {integrity: sha512-ZgpWDC5gmNiuY9CnLVXEH8rl50xhRCuLNA97fAUnKi8RRuV4E6KG31pDTsLVUKnohJE0I3XDrTeEydAXRw47xg==} engines: {node: '>=18.17'} + undici@7.25.0: + resolution: {integrity: sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ==} + engines: {node: '>=20.18.1'} + universalify@0.1.2: resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} engines: {node: '>= 4.0.0'} @@ -2098,6 +2782,10 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + uuid@14.0.0: + resolution: {integrity: sha512-Qo+uWgilfSmAhXCMav1uYFynlQO7fMFiMVZsQqZRMIXp0O7rR7qjkj+cPvBHLgBqi960QCoo/PH2/6ZtVqKvrg==} + hasBin: true + uuid@8.3.2: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). @@ -2181,6 +2869,10 @@ packages: engines: {node: '>=12.0.0'} hasBin: true + web-streams-polyfill@3.3.3: + resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} + engines: {node: '>= 8'} + which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -2203,6 +2895,22 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + ws@8.20.1: + resolution: {integrity: sha512-It4dO0K5v//JtTXuPkfEOaI3uUN87iYPnqo/ZzqCoG3g8uhA66QUMs/SrM0YK7/NAu+r4LMh/9dq2A7k+rHs+w==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + xml-naming@0.1.0: + resolution: {integrity: sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw==} + engines: {node: '>=16.0.0'} + xmlchars@2.2.0: resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} @@ -2217,10 +2925,23 @@ packages: resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} engines: {node: '>=18'} + yaml@2.9.0: + resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==} + engines: {node: '>= 14.6'} + hasBin: true + + yargs-parser@20.2.9: + resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} + engines: {node: '>=10'} + yargs-parser@21.1.1: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} + yargs@16.2.0: + resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} + engines: {node: '>=10'} + yargs@17.7.2: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} @@ -2232,11 +2953,327 @@ packages: resolution: {integrity: sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==} engines: {node: '>= 10'} + zod-to-json-schema@3.25.2: + resolution: {integrity: sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==} + peerDependencies: + zod: ^3.25.28 || ^4 + zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} snapshots: + '@anthropic-ai/sdk@0.91.1(zod@3.25.76)': + dependencies: + json-schema-to-ts: 3.1.1 + optionalDependencies: + zod: 3.25.76 + + '@aws-crypto/crc32@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.973.8 + tslib: 2.8.1 + + '@aws-crypto/sha256-browser@5.2.0': + dependencies: + '@aws-crypto/sha256-js': 5.2.0 + '@aws-crypto/supports-web-crypto': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.973.8 + '@aws-sdk/util-locate-window': 3.965.5 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + + '@aws-crypto/sha256-js@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.973.8 + tslib: 2.8.1 + + '@aws-crypto/supports-web-crypto@5.2.0': + dependencies: + tslib: 2.8.1 + + '@aws-crypto/util@5.2.0': + dependencies: + '@aws-sdk/types': 3.973.8 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + + '@aws-sdk/client-bedrock-runtime@3.1046.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.974.9 + '@aws-sdk/credential-provider-node': 3.972.40 + '@aws-sdk/eventstream-handler-node': 3.972.15 + '@aws-sdk/middleware-eventstream': 3.972.11 + '@aws-sdk/middleware-host-header': 3.972.11 + '@aws-sdk/middleware-logger': 3.972.10 + '@aws-sdk/middleware-recursion-detection': 3.972.12 + '@aws-sdk/middleware-user-agent': 3.972.39 + '@aws-sdk/middleware-websocket': 3.972.17 + '@aws-sdk/region-config-resolver': 3.972.14 + '@aws-sdk/token-providers': 3.1046.0 + '@aws-sdk/types': 3.973.8 + '@aws-sdk/util-endpoints': 3.996.9 + '@aws-sdk/util-user-agent-browser': 3.972.11 + '@aws-sdk/util-user-agent-node': 3.973.25 + '@smithy/core': 3.24.2 + '@smithy/fetch-http-handler': 5.4.2 + '@smithy/node-http-handler': 4.7.2 + '@smithy/types': 4.14.1 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/core@3.974.9': + dependencies: + '@aws-sdk/types': 3.973.8 + '@aws-sdk/xml-builder': 3.972.23 + '@smithy/core': 3.24.2 + '@smithy/signature-v4': 5.4.2 + '@smithy/types': 4.14.1 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-env@3.972.35': + dependencies: + '@aws-sdk/core': 3.974.9 + '@aws-sdk/types': 3.973.8 + '@smithy/core': 3.24.2 + '@smithy/types': 4.14.1 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-http@3.972.37': + dependencies: + '@aws-sdk/core': 3.974.9 + '@aws-sdk/types': 3.973.8 + '@smithy/core': 3.24.2 + '@smithy/fetch-http-handler': 5.4.2 + '@smithy/node-http-handler': 4.7.2 + '@smithy/types': 4.14.1 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-ini@3.972.39': + dependencies: + '@aws-sdk/core': 3.974.9 + '@aws-sdk/credential-provider-env': 3.972.35 + '@aws-sdk/credential-provider-http': 3.972.37 + '@aws-sdk/credential-provider-login': 3.972.39 + '@aws-sdk/credential-provider-process': 3.972.35 + '@aws-sdk/credential-provider-sso': 3.972.39 + '@aws-sdk/credential-provider-web-identity': 3.972.39 + '@aws-sdk/nested-clients': 3.997.7 + '@aws-sdk/types': 3.973.8 + '@smithy/core': 3.24.2 + '@smithy/credential-provider-imds': 4.3.2 + '@smithy/types': 4.14.1 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-login@3.972.39': + dependencies: + '@aws-sdk/core': 3.974.9 + '@aws-sdk/nested-clients': 3.997.7 + '@aws-sdk/types': 3.973.8 + '@smithy/core': 3.24.2 + '@smithy/types': 4.14.1 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-node@3.972.40': + dependencies: + '@aws-sdk/credential-provider-env': 3.972.35 + '@aws-sdk/credential-provider-http': 3.972.37 + '@aws-sdk/credential-provider-ini': 3.972.39 + '@aws-sdk/credential-provider-process': 3.972.35 + '@aws-sdk/credential-provider-sso': 3.972.39 + '@aws-sdk/credential-provider-web-identity': 3.972.39 + '@aws-sdk/types': 3.973.8 + '@smithy/core': 3.24.2 + '@smithy/credential-provider-imds': 4.3.2 + '@smithy/types': 4.14.1 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-process@3.972.35': + dependencies: + '@aws-sdk/core': 3.974.9 + '@aws-sdk/types': 3.973.8 + '@smithy/core': 3.24.2 + '@smithy/types': 4.14.1 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-sso@3.972.39': + dependencies: + '@aws-sdk/core': 3.974.9 + '@aws-sdk/nested-clients': 3.997.7 + '@aws-sdk/token-providers': 3.1046.0 + '@aws-sdk/types': 3.973.8 + '@smithy/core': 3.24.2 + '@smithy/types': 4.14.1 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-web-identity@3.972.39': + dependencies: + '@aws-sdk/core': 3.974.9 + '@aws-sdk/nested-clients': 3.997.7 + '@aws-sdk/types': 3.973.8 + '@smithy/core': 3.24.2 + '@smithy/types': 4.14.1 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/eventstream-handler-node@3.972.15': + dependencies: + '@aws-sdk/types': 3.973.8 + '@smithy/core': 3.24.2 + '@smithy/types': 4.14.1 + tslib: 2.8.1 + + '@aws-sdk/middleware-eventstream@3.972.11': + dependencies: + '@aws-sdk/types': 3.973.8 + '@smithy/core': 3.24.2 + '@smithy/types': 4.14.1 + tslib: 2.8.1 + + '@aws-sdk/middleware-host-header@3.972.11': + dependencies: + '@aws-sdk/types': 3.973.8 + '@smithy/core': 3.24.2 + '@smithy/types': 4.14.1 + tslib: 2.8.1 + + '@aws-sdk/middleware-logger@3.972.10': + dependencies: + '@aws-sdk/types': 3.973.8 + '@smithy/types': 4.14.1 + tslib: 2.8.1 + + '@aws-sdk/middleware-recursion-detection@3.972.12': + dependencies: + '@aws-sdk/types': 3.973.8 + '@aws/lambda-invoke-store': 0.2.4 + '@smithy/core': 3.24.2 + '@smithy/types': 4.14.1 + tslib: 2.8.1 + + '@aws-sdk/middleware-user-agent@3.972.39': + dependencies: + '@aws-sdk/core': 3.974.9 + '@aws-sdk/types': 3.973.8 + '@aws-sdk/util-endpoints': 3.996.9 + '@smithy/core': 3.24.2 + '@smithy/types': 4.14.1 + tslib: 2.8.1 + + '@aws-sdk/middleware-websocket@3.972.17': + dependencies: + '@aws-sdk/core': 3.974.9 + '@aws-sdk/types': 3.973.8 + '@smithy/core': 3.24.2 + '@smithy/fetch-http-handler': 5.4.2 + '@smithy/signature-v4': 5.4.2 + '@smithy/types': 4.14.1 + tslib: 2.8.1 + + '@aws-sdk/nested-clients@3.997.7': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.974.9 + '@aws-sdk/middleware-host-header': 3.972.11 + '@aws-sdk/middleware-logger': 3.972.10 + '@aws-sdk/middleware-recursion-detection': 3.972.12 + '@aws-sdk/middleware-user-agent': 3.972.39 + '@aws-sdk/region-config-resolver': 3.972.14 + '@aws-sdk/signature-v4-multi-region': 3.996.26 + '@aws-sdk/types': 3.973.8 + '@aws-sdk/util-endpoints': 3.996.9 + '@aws-sdk/util-user-agent-browser': 3.972.11 + '@aws-sdk/util-user-agent-node': 3.973.25 + '@smithy/core': 3.24.2 + '@smithy/fetch-http-handler': 5.4.2 + '@smithy/node-http-handler': 4.7.2 + '@smithy/types': 4.14.1 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/region-config-resolver@3.972.14': + dependencies: + '@aws-sdk/types': 3.973.8 + '@smithy/core': 3.24.2 + '@smithy/types': 4.14.1 + tslib: 2.8.1 + + '@aws-sdk/signature-v4-multi-region@3.996.26': + dependencies: + '@aws-sdk/types': 3.973.8 + '@smithy/core': 3.24.2 + '@smithy/signature-v4': 5.4.2 + '@smithy/types': 4.14.1 + tslib: 2.8.1 + + '@aws-sdk/token-providers@3.1046.0': + dependencies: + '@aws-sdk/core': 3.974.9 + '@aws-sdk/nested-clients': 3.997.7 + '@aws-sdk/types': 3.973.8 + '@smithy/core': 3.24.2 + '@smithy/types': 4.14.1 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/types@3.973.8': + dependencies: + '@smithy/types': 4.14.1 + tslib: 2.8.1 + + '@aws-sdk/util-endpoints@3.996.9': + dependencies: + '@aws-sdk/types': 3.973.8 + '@smithy/core': 3.24.2 + '@smithy/types': 4.14.1 + tslib: 2.8.1 + + '@aws-sdk/util-locate-window@3.965.5': + dependencies: + tslib: 2.8.1 + + '@aws-sdk/util-user-agent-browser@3.972.11': + dependencies: + '@aws-sdk/types': 3.973.8 + '@smithy/types': 4.14.1 + bowser: 2.14.1 + tslib: 2.8.1 + + '@aws-sdk/util-user-agent-node@3.973.25': + dependencies: + '@aws-sdk/middleware-user-agent': 3.972.39 + '@aws-sdk/types': 3.973.8 + '@smithy/core': 3.24.2 + '@smithy/types': 4.14.1 + tslib: 2.8.1 + + '@aws-sdk/xml-builder@3.972.23': + dependencies: + '@nodable/entities': 2.1.0 + '@smithy/types': 4.14.1 + fast-xml-parser: 5.7.2 + tslib: 2.8.1 + + '@aws/lambda-invoke-store@0.2.4': {} + '@babel/code-frame@7.29.0': dependencies: '@babel/helper-validator-identifier': 7.28.5 @@ -2345,6 +3382,8 @@ snapshots: '@babel/core': 7.29.0 '@babel/helper-plugin-utils': 7.28.6 + '@babel/runtime@7.29.2': {} + '@babel/template@7.28.6': dependencies: '@babel/code-frame': 7.29.0 @@ -2373,6 +3412,87 @@ snapshots: '@babel/helper-string-parser': 8.0.0-rc.5 '@babel/helper-validator-identifier': 8.0.0-rc.5 + '@borewit/text-codec@0.2.2': {} + + '@earendil-works/pi-agent-core@0.74.0(ws@8.20.1)(zod@3.25.76)': + dependencies: + '@earendil-works/pi-ai': 0.74.0(ws@8.20.1)(zod@3.25.76) + typebox: 1.1.38 + transitivePeerDependencies: + - '@modelcontextprotocol/sdk' + - aws-crt + - bufferutil + - supports-color + - utf-8-validate + - ws + - zod + + '@earendil-works/pi-ai@0.74.0(ws@8.20.1)(zod@3.25.76)': + dependencies: + '@anthropic-ai/sdk': 0.91.1(zod@3.25.76) + '@aws-sdk/client-bedrock-runtime': 3.1046.0 + '@google/genai': 1.52.0 + '@mistralai/mistralai': 2.2.1 + chalk: 5.6.2 + openai: 6.26.0(ws@8.20.1)(zod@3.25.76) + partial-json: 0.1.7 + proxy-agent: 6.5.0 + typebox: 1.1.38 + undici: 7.25.0 + zod-to-json-schema: 3.25.2(zod@3.25.76) + transitivePeerDependencies: + - '@modelcontextprotocol/sdk' + - aws-crt + - bufferutil + - supports-color + - utf-8-validate + - ws + - zod + + '@earendil-works/pi-coding-agent@0.74.0(ws@8.20.1)(zod@3.25.76)': + dependencies: + '@earendil-works/pi-agent-core': 0.74.0(ws@8.20.1)(zod@3.25.76) + '@earendil-works/pi-ai': 0.74.0(ws@8.20.1)(zod@3.25.76) + '@earendil-works/pi-tui': 0.74.0 + '@silvia-odwyer/photon-node': 0.3.4 + chalk: 5.6.2 + cli-highlight: 2.1.11 + diff: 8.0.4 + extract-zip: 2.0.1 + file-type: 21.3.4 + glob: 13.0.6 + hosted-git-info: 9.0.3 + ignore: 7.0.5 + jiti: 2.7.0 + marked: 15.0.12 + minimatch: 10.2.5 + proper-lockfile: 4.1.2 + strip-ansi: 7.2.0 + typebox: 1.1.38 + undici: 7.25.0 + uuid: 14.0.0 + yaml: 2.9.0 + optionalDependencies: + '@mariozechner/clipboard': 0.3.5 + transitivePeerDependencies: + - '@modelcontextprotocol/sdk' + - aws-crt + - bufferutil + - supports-color + - utf-8-validate + - ws + - zod + + '@earendil-works/pi-tui@0.74.0': + dependencies: + '@types/mime-types': 2.1.4 + chalk: 5.6.2 + get-east-asian-width: 1.6.0 + marked: 15.0.12 + mime-types: 3.0.2 + optionalDependencies: + koffi: 2.16.2 + '@electron/get@2.0.3': dependencies: debug: 4.4.3 @@ -2511,6 +3631,17 @@ snapshots: lodash.isundefined: 3.0.1 lodash.uniq: 4.5.0 + '@google/genai@1.52.0': + dependencies: + google-auth-library: 10.6.2 + p-retry: 4.6.2 + protobufjs: 7.5.8 + ws: 8.20.1 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + '@hapi/address@5.1.1': dependencies: '@hapi/hoek': 11.0.7 @@ -2554,6 +3685,59 @@ snapshots: dependencies: cross-spawn: 7.0.6 + '@mariozechner/clipboard-darwin-arm64@0.3.2': + optional: true + + '@mariozechner/clipboard-darwin-universal@0.3.2': + optional: true + + '@mariozechner/clipboard-darwin-x64@0.3.2': + optional: true + + '@mariozechner/clipboard-linux-arm64-gnu@0.3.2': + optional: true + + '@mariozechner/clipboard-linux-arm64-musl@0.3.2': + optional: true + + '@mariozechner/clipboard-linux-riscv64-gnu@0.3.2': + optional: true + + '@mariozechner/clipboard-linux-x64-gnu@0.3.2': + optional: true + + '@mariozechner/clipboard-linux-x64-musl@0.3.2': + optional: true + + '@mariozechner/clipboard-win32-arm64-msvc@0.3.2': + optional: true + + '@mariozechner/clipboard-win32-x64-msvc@0.3.2': + optional: true + + '@mariozechner/clipboard@0.3.5': + optionalDependencies: + '@mariozechner/clipboard-darwin-arm64': 0.3.2 + '@mariozechner/clipboard-darwin-universal': 0.3.2 + '@mariozechner/clipboard-darwin-x64': 0.3.2 + '@mariozechner/clipboard-linux-arm64-gnu': 0.3.2 + '@mariozechner/clipboard-linux-arm64-musl': 0.3.2 + '@mariozechner/clipboard-linux-riscv64-gnu': 0.3.2 + '@mariozechner/clipboard-linux-x64-gnu': 0.3.2 + '@mariozechner/clipboard-linux-x64-musl': 0.3.2 + '@mariozechner/clipboard-win32-arm64-msvc': 0.3.2 + '@mariozechner/clipboard-win32-x64-msvc': 0.3.2 + optional: true + + '@mistralai/mistralai@2.2.1': + dependencies: + ws: 8.20.1 + zod: 3.25.76 + zod-to-json-schema: 3.25.2(zod@3.25.76) + transitivePeerDependencies: + - bufferutil + - utf-8-validate + '@napi-rs/wasm-runtime@1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': dependencies: '@emnapi/core': 1.10.0 @@ -2561,8 +3745,33 @@ snapshots: '@tybys/wasm-util': 0.10.2 optional: true + '@nodable/entities@2.1.0': {} + '@oxc-project/types@0.130.0': {} + '@protobufjs/aspromise@1.1.2': {} + + '@protobufjs/base64@1.1.2': {} + + '@protobufjs/codegen@2.0.5': {} + + '@protobufjs/eventemitter@1.1.0': {} + + '@protobufjs/fetch@1.1.0': + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/inquire': 1.1.1 + + '@protobufjs/float@1.0.2': {} + + '@protobufjs/inquire@1.1.1': {} + + '@protobufjs/path@1.1.2': {} + + '@protobufjs/pool@1.1.0': {} + + '@protobufjs/utf8@1.1.1': {} + '@quansync/fs@1.0.0': dependencies: quansync: 1.0.0 @@ -2695,8 +3904,58 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.60.3': optional: true + '@silvia-odwyer/photon-node@0.3.4': {} + '@sindresorhus/is@4.6.0': {} + '@smithy/core@3.24.2': + dependencies: + '@aws-crypto/crc32': 5.2.0 + '@smithy/types': 4.14.1 + tslib: 2.8.1 + + '@smithy/credential-provider-imds@4.3.2': + dependencies: + '@smithy/core': 3.24.2 + '@smithy/types': 4.14.1 + tslib: 2.8.1 + + '@smithy/fetch-http-handler@5.4.2': + dependencies: + '@smithy/core': 3.24.2 + '@smithy/types': 4.14.1 + tslib: 2.8.1 + + '@smithy/is-array-buffer@2.2.0': + dependencies: + tslib: 2.8.1 + + '@smithy/node-http-handler@4.7.2': + dependencies: + '@smithy/core': 3.24.2 + '@smithy/types': 4.14.1 + tslib: 2.8.1 + + '@smithy/signature-v4@5.4.2': + dependencies: + '@smithy/core': 3.24.2 + '@smithy/types': 4.14.1 + tslib: 2.8.1 + + '@smithy/types@4.14.1': + dependencies: + tslib: 2.8.1 + + '@smithy/util-buffer-from@2.2.0': + dependencies: + '@smithy/is-array-buffer': 2.2.0 + tslib: 2.8.1 + + '@smithy/util-utf8@2.3.0': + dependencies: + '@smithy/util-buffer-from': 2.2.0 + tslib: 2.8.1 + '@standard-schema/spec@1.1.0': {} '@szmarczak/http-timer@4.0.6': @@ -2764,12 +4023,23 @@ snapshots: '@tailwindcss/oxide-win32-arm64-msvc': 4.2.4 '@tailwindcss/oxide-win32-x64-msvc': 4.2.4 - '@tailwindcss/vite@4.2.4(vite@7.3.3(@types/node@22.19.18)(jiti@2.7.0)(lightningcss@1.32.0))': + '@tailwindcss/vite@4.2.4(vite@7.3.3(@types/node@22.19.18)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0))': dependencies: '@tailwindcss/node': 4.2.4 '@tailwindcss/oxide': 4.2.4 tailwindcss: 4.2.4 - vite: 7.3.3(@types/node@22.19.18)(jiti@2.7.0)(lightningcss@1.32.0) + vite: 7.3.3(@types/node@22.19.18)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0) + + '@tokenizer/inflate@0.4.1': + dependencies: + debug: 4.4.3 + token-types: 6.1.2 + transitivePeerDependencies: + - supports-color + + '@tokenizer/token@0.3.0': {} + + '@tootallnate/quickjs-emscripten@0.23.0': {} '@tybys/wasm-util@0.10.2': dependencies: @@ -2825,6 +4095,8 @@ snapshots: dependencies: '@types/node': 22.19.18 + '@types/mime-types@2.1.4': {} + '@types/node@14.18.63': {} '@types/node@22.19.18': @@ -2843,12 +4115,14 @@ snapshots: dependencies: '@types/node': 22.19.18 + '@types/retry@0.12.0': {} + '@types/yauzl@2.10.3': dependencies: '@types/node': 22.19.18 optional: true - '@vitejs/plugin-react@5.2.0(vite@7.3.3(@types/node@22.19.18)(jiti@2.7.0)(lightningcss@1.32.0))': + '@vitejs/plugin-react@5.2.0(vite@7.3.3(@types/node@22.19.18)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0))': dependencies: '@babel/core': 7.29.0 '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0) @@ -2856,7 +4130,7 @@ snapshots: '@rolldown/pluginutils': 1.0.0-rc.3 '@types/babel__core': 7.20.5 react-refresh: 0.18.0 - vite: 7.3.3(@types/node@22.19.18)(jiti@2.7.0)(lightningcss@1.32.0) + vite: 7.3.3(@types/node@22.19.18)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0) transitivePeerDependencies: - supports-color @@ -2868,13 +4142,13 @@ snapshots: chai: 5.3.3 tinyrainbow: 2.0.0 - '@vitest/mocker@3.2.4(vite@7.3.3(@types/node@22.19.18)(jiti@2.7.0)(lightningcss@1.32.0))': + '@vitest/mocker@3.2.4(vite@7.3.3(@types/node@22.19.18)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0))': dependencies: '@vitest/spy': 3.2.4 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.3.3(@types/node@22.19.18)(jiti@2.7.0)(lightningcss@1.32.0) + vite: 7.3.3(@types/node@22.19.18)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0) '@vitest/pretty-format@3.2.4': dependencies: @@ -2904,14 +4178,20 @@ snapshots: abbrev@4.0.0: {} + agent-base@7.1.4: {} + ansi-regex@5.0.1: {} + ansi-regex@6.2.2: {} + ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 ansis@4.3.0: {} + any-promise@1.3.0: {} + archiver-utils@2.1.0: dependencies: glob: 7.2.3 @@ -2956,6 +4236,10 @@ snapshots: estree-walker: 3.0.3 pathe: 2.0.3 + ast-types@0.13.4: + dependencies: + tslib: 2.8.1 + async@3.2.6: {} asynckit@0.4.0: {} @@ -2970,10 +4254,14 @@ snapshots: balanced-match@1.0.2: {} + balanced-match@4.0.4: {} + base64-js@1.5.1: {} baseline-browser-mapping@2.10.28: {} + basic-ftp@5.3.1: {} + better-sqlite3@12.10.0: dependencies: bindings: 1.5.0 @@ -2981,6 +4269,8 @@ snapshots: big-integer@1.6.52: {} + bignumber.js@9.3.1: {} + binary@0.3.0: dependencies: buffers: 0.1.1 @@ -3003,6 +4293,8 @@ snapshots: boolean@3.2.0: optional: true + bowser@2.14.1: {} + brace-expansion@1.1.14: dependencies: balanced-match: 1.0.2 @@ -3012,6 +4304,10 @@ snapshots: dependencies: balanced-match: 1.0.2 + brace-expansion@5.0.6: + dependencies: + balanced-match: 4.0.4 + browserslist@4.28.2: dependencies: baseline-browser-mapping: 2.10.28 @@ -3022,6 +4318,8 @@ snapshots: buffer-crc32@0.2.13: {} + buffer-equal-constant-time@1.0.1: {} + buffer-indexof-polyfill@1.0.2: {} buffer@5.7.1: @@ -3071,12 +4369,29 @@ snapshots: ansi-styles: 4.3.0 supports-color: 7.2.0 + chalk@5.6.2: {} + check-error@2.1.3: {} chownr@1.1.4: {} chownr@3.0.0: {} + cli-highlight@2.1.11: + dependencies: + chalk: 4.1.2 + highlight.js: 10.7.3 + mz: 2.7.0 + parse5: 5.1.1 + parse5-htmlparser2-tree-adapter: 6.0.1 + yargs: 16.2.0 + + cliui@7.0.4: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + cliui@8.0.1: dependencies: string-width: 4.2.3 @@ -3134,6 +4449,10 @@ snapshots: csstype@3.2.3: {} + data-uri-to-buffer@4.0.1: {} + + data-uri-to-buffer@6.0.2: {} + dayjs@1.11.20: {} debug@4.4.3: @@ -3166,6 +4485,12 @@ snapshots: defu@6.1.7: {} + degenerator@5.0.1: + dependencies: + ast-types: 0.13.4 + escodegen: 2.1.0 + esprima: 4.0.1 + delayed-stream@1.0.0: {} detect-libc@2.1.2: {} @@ -3173,6 +4498,8 @@ snapshots: detect-node@2.1.0: optional: true + diff@8.0.4: {} + dts-resolver@3.0.0: {} dunder-proto@1.0.1: @@ -3185,6 +4512,10 @@ snapshots: dependencies: readable-stream: 2.3.8 + ecdsa-sig-formatter@1.0.11: + dependencies: + safe-buffer: 5.2.1 + electron-to-chromium@1.5.353: {} electron@36.9.5: @@ -3264,10 +4595,24 @@ snapshots: escape-string-regexp@4.0.0: optional: true + escodegen@2.1.0: + dependencies: + esprima: 4.0.1 + estraverse: 5.3.0 + esutils: 2.0.3 + optionalDependencies: + source-map: 0.6.1 + + esprima@4.0.1: {} + + estraverse@5.3.0: {} + estree-walker@3.0.3: dependencies: '@types/estree': 1.0.8 + esutils@2.0.3: {} + exceljs@4.4.0: dependencies: archiver: 5.3.2 @@ -3286,6 +4631,8 @@ snapshots: exponential-backoff@3.1.3: {} + extend@3.0.2: {} + extract-zip@2.0.1: dependencies: debug: 4.4.3 @@ -3301,6 +4648,18 @@ snapshots: '@fast-csv/format': 4.3.5 '@fast-csv/parse': 4.3.6 + fast-xml-builder@1.2.0: + dependencies: + path-expression-matcher: 1.5.0 + xml-naming: 0.1.0 + + fast-xml-parser@5.7.2: + dependencies: + '@nodable/entities': 2.1.0 + fast-xml-builder: 1.2.0 + path-expression-matcher: 1.5.0 + strnum: 2.3.0 + fd-slicer@1.1.0: dependencies: pend: 1.2.0 @@ -3309,6 +4668,20 @@ snapshots: optionalDependencies: picomatch: 4.0.4 + fetch-blob@3.2.0: + dependencies: + node-domexception: 1.0.0 + web-streams-polyfill: 3.3.3 + + file-type@21.3.4: + dependencies: + '@tokenizer/inflate': 0.4.1 + strtok3: 10.3.5 + token-types: 6.1.2 + uint8array-extras: 1.5.0 + transitivePeerDependencies: + - supports-color + file-uri-to-path@1.0.0: {} follow-redirects@1.16.0: {} @@ -3321,6 +4694,10 @@ snapshots: hasown: 2.0.3 mime-types: 2.1.35 + formdata-polyfill@4.0.10: + dependencies: + fetch-blob: 3.2.0 + fs-constants@1.0.0: {} fs-extra@8.1.0: @@ -3343,10 +4720,28 @@ snapshots: function-bind@1.1.2: {} + gaxios@7.1.4: + dependencies: + extend: 3.0.2 + https-proxy-agent: 7.0.6 + node-fetch: 3.3.2 + transitivePeerDependencies: + - supports-color + + gcp-metadata@8.1.2: + dependencies: + gaxios: 7.1.4 + google-logging-utils: 1.1.3 + json-bigint: 1.0.0 + transitivePeerDependencies: + - supports-color + gensync@1.0.0-beta.2: {} get-caller-file@2.0.5: {} + get-east-asian-width@1.6.0: {} + get-intrinsic@1.3.0: dependencies: call-bind-apply-helpers: 1.0.2 @@ -3373,8 +4768,22 @@ snapshots: dependencies: resolve-pkg-maps: 1.0.0 + get-uri@6.0.5: + dependencies: + basic-ftp: 5.3.1 + data-uri-to-buffer: 6.0.2 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + github-from-package@0.0.0: {} + glob@13.0.6: + dependencies: + minimatch: 10.2.5 + minipass: 7.1.3 + path-scurry: 2.0.2 + glob@7.2.3: dependencies: fs.realpath: 1.0.0 @@ -3400,6 +4809,19 @@ snapshots: gopd: 1.2.0 optional: true + google-auth-library@10.6.2: + dependencies: + base64-js: 1.5.1 + ecdsa-sig-formatter: 1.0.11 + gaxios: 7.1.4 + gcp-metadata: 8.1.2 + google-logging-utils: 1.1.3 + jws: 4.0.1 + transitivePeerDependencies: + - supports-color + + google-logging-utils@1.1.3: {} + gopd@1.2.0: {} got@11.8.6: @@ -3435,19 +4857,41 @@ snapshots: dependencies: function-bind: 1.1.2 + highlight.js@10.7.3: {} + hookable@6.1.1: {} + hosted-git-info@9.0.3: + dependencies: + lru-cache: 11.3.6 + http-cache-semantics@4.2.0: {} + http-proxy-agent@7.0.2: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + http2-wrapper@1.0.3: dependencies: quick-lru: 5.1.1 resolve-alpn: 1.2.1 + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + https@1.0.0: {} ieee754@1.2.1: {} + ignore@7.0.5: {} + image-size@1.2.1: dependencies: queue: 6.0.2 @@ -3465,6 +4909,8 @@ snapshots: ini@1.3.8: {} + ip-address@10.2.0: {} + is-fullwidth-code-point@3.0.0: {} isarray@1.0.0: {} @@ -3491,8 +4937,17 @@ snapshots: jsesc@3.1.0: {} + json-bigint@1.0.0: + dependencies: + bignumber.js: 9.3.1 + json-buffer@3.0.1: {} + json-schema-to-ts@3.1.1: + dependencies: + '@babel/runtime': 7.29.2 + ts-algebra: 2.0.0 + json-stringify-safe@5.0.1: optional: true @@ -3509,10 +4964,24 @@ snapshots: readable-stream: 2.3.8 setimmediate: 1.0.5 + jwa@2.0.1: + dependencies: + buffer-equal-constant-time: 1.0.1 + ecdsa-sig-formatter: 1.0.11 + safe-buffer: 5.2.1 + + jws@4.0.1: + dependencies: + jwa: 2.0.1 + safe-buffer: 5.2.1 + keyv@4.5.4: dependencies: json-buffer: 3.0.1 + koffi@2.16.2: + optional: true + lazystream@1.0.1: dependencies: readable-stream: 2.3.8 @@ -3600,18 +5069,26 @@ snapshots: lodash@4.18.1: {} + long@5.3.2: {} + loupe@3.2.1: {} lowercase-keys@2.0.0: {} + lru-cache@11.3.6: {} + lru-cache@5.1.1: dependencies: yallist: 3.1.1 + lru-cache@7.18.3: {} + magic-string@0.30.21: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + marked@15.0.12: {} + matcher@3.0.0: dependencies: escape-string-regexp: 4.0.0 @@ -3621,14 +5098,24 @@ snapshots: mime-db@1.52.0: {} + mime-db@1.54.0: {} + mime-types@2.1.35: dependencies: mime-db: 1.52.0 + mime-types@3.0.2: + dependencies: + mime-db: 1.54.0 + mimic-response@1.0.1: {} mimic-response@3.1.0: {} + minimatch@10.2.5: + dependencies: + brace-expansion: 5.0.6 + minimatch@3.1.5: dependencies: brace-expansion: 1.1.14 @@ -3653,10 +5140,18 @@ snapshots: ms@2.1.3: {} + mz@2.7.0: + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + nanoid@3.3.12: {} napi-build-utils@2.0.0: {} + netmask@2.1.1: {} + node-abi@3.92.0: dependencies: semver: 7.8.0 @@ -3669,6 +5164,14 @@ snapshots: dependencies: semver: 7.8.0 + node-domexception@1.0.0: {} + + node-fetch@3.3.2: + dependencies: + data-uri-to-buffer: 4.0.1 + fetch-blob: 3.2.0 + formdata-polyfill: 4.0.10 + node-gyp@12.3.0: dependencies: env-paths: 2.2.1 @@ -3692,6 +5195,8 @@ snapshots: normalize-url@6.1.0: {} + object-assign@4.1.1: {} + object-keys@1.1.1: optional: true @@ -3701,14 +5206,59 @@ snapshots: dependencies: wrappy: 1.0.2 + openai@6.26.0(ws@8.20.1)(zod@3.25.76): + optionalDependencies: + ws: 8.20.1 + zod: 3.25.76 + p-cancelable@2.1.1: {} + p-retry@4.6.2: + dependencies: + '@types/retry': 0.12.0 + retry: 0.13.1 + + pac-proxy-agent@7.2.0: + dependencies: + '@tootallnate/quickjs-emscripten': 0.23.0 + agent-base: 7.1.4 + debug: 4.4.3 + get-uri: 6.0.5 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + pac-resolver: 7.0.1 + socks-proxy-agent: 8.0.5 + transitivePeerDependencies: + - supports-color + + pac-resolver@7.0.1: + dependencies: + degenerator: 5.0.1 + netmask: 2.1.1 + pako@1.0.11: {} + parse5-htmlparser2-tree-adapter@6.0.1: + dependencies: + parse5: 6.0.1 + + parse5@5.1.1: {} + + parse5@6.0.1: {} + + partial-json@0.1.7: {} + + path-expression-matcher@1.5.0: {} + path-is-absolute@1.0.1: {} path-key@3.1.1: {} + path-scurry@2.0.2: + dependencies: + lru-cache: 11.3.6 + minipass: 7.1.3 + pathe@2.0.3: {} pathval@2.0.1: {} @@ -3755,6 +5305,42 @@ snapshots: progress@2.0.3: {} + proper-lockfile@4.1.2: + dependencies: + graceful-fs: 4.2.11 + retry: 0.12.0 + signal-exit: 3.0.7 + + protobufjs@7.5.8: + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/base64': 1.1.2 + '@protobufjs/codegen': 2.0.5 + '@protobufjs/eventemitter': 1.1.0 + '@protobufjs/fetch': 1.1.0 + '@protobufjs/float': 1.0.2 + '@protobufjs/inquire': 1.1.1 + '@protobufjs/path': 1.1.2 + '@protobufjs/pool': 1.1.0 + '@protobufjs/utf8': 1.1.1 + '@types/node': 22.19.18 + long: 5.3.2 + + proxy-agent@6.5.0: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + lru-cache: 7.18.3 + pac-proxy-agent: 7.2.0 + proxy-from-env: 1.1.0 + socks-proxy-agent: 8.0.5 + transitivePeerDependencies: + - supports-color + + proxy-from-env@1.1.0: {} + proxy-from-env@2.1.0: {} pump@3.0.4: @@ -3822,6 +5408,10 @@ snapshots: dependencies: lowercase-keys: 2.0.0 + retry@0.12.0: {} + + retry@0.13.1: {} + rimraf@2.7.1: dependencies: glob: 7.2.3 @@ -3942,6 +5532,8 @@ snapshots: siginfo@2.0.0: {} + signal-exit@3.0.7: {} + simple-concat@1.0.1: {} simple-get@4.0.1: @@ -3950,8 +5542,26 @@ snapshots: once: 1.4.0 simple-concat: 1.0.1 + smart-buffer@4.2.0: {} + + socks-proxy-agent@8.0.5: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + socks: 2.8.9 + transitivePeerDependencies: + - supports-color + + socks@2.8.9: + dependencies: + ip-address: 10.2.0 + smart-buffer: 4.2.0 + source-map-js@1.2.1: {} + source-map@0.6.1: + optional: true + sprintf-js@1.1.3: optional: true @@ -3977,12 +5587,22 @@ snapshots: dependencies: ansi-regex: 5.0.1 + strip-ansi@7.2.0: + dependencies: + ansi-regex: 6.2.2 + strip-json-comments@2.0.1: {} strip-literal@3.1.0: dependencies: js-tokens: 9.0.1 + strnum@2.3.0: {} + + strtok3@10.3.5: + dependencies: + '@tokenizer/token': 0.3.0 + sumchecker@3.0.1: dependencies: debug: 4.4.3 @@ -4024,6 +5644,14 @@ snapshots: minizlib: 3.1.0 yallist: 5.0.0 + thenify-all@1.6.0: + dependencies: + thenify: 3.3.1 + + thenify@3.3.1: + dependencies: + any-promise: 1.3.0 + tinybench@2.9.0: {} tinyexec@0.3.2: {} @@ -4043,10 +5671,18 @@ snapshots: tmp@0.2.5: {} + token-types@6.1.2: + dependencies: + '@borewit/text-codec': 0.2.2 + '@tokenizer/token': 0.3.0 + ieee754: 1.2.1 + traverse@0.3.9: {} tree-kill@1.2.2: {} + ts-algebra@2.0.0: {} + tsdown@0.22.0(typescript@5.9.3): dependencies: ansis: 4.3.0 @@ -4081,8 +5717,12 @@ snapshots: type-fest@0.13.1: optional: true + typebox@1.1.38: {} + typescript@5.9.3: {} + uint8array-extras@1.5.0: {} + unconfig-core@7.5.0: dependencies: '@quansync/fs': 1.0.0 @@ -4092,6 +5732,8 @@ snapshots: undici@6.25.0: {} + undici@7.25.0: {} + universalify@0.1.2: {} unzipper@0.10.14: @@ -4115,15 +5757,17 @@ snapshots: util-deprecate@1.0.2: {} + uuid@14.0.0: {} + uuid@8.3.2: {} - vite-node@3.2.4(@types/node@22.19.18)(jiti@2.7.0)(lightningcss@1.32.0): + vite-node@3.2.4(@types/node@22.19.18)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0): dependencies: cac: 6.7.14 debug: 4.4.3 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 7.3.3(@types/node@22.19.18)(jiti@2.7.0)(lightningcss@1.32.0) + vite: 7.3.3(@types/node@22.19.18)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0) transitivePeerDependencies: - '@types/node' - jiti @@ -4138,7 +5782,7 @@ snapshots: - tsx - yaml - vite@7.3.3(@types/node@22.19.18)(jiti@2.7.0)(lightningcss@1.32.0): + vite@7.3.3(@types/node@22.19.18)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0): dependencies: esbuild: 0.27.7 fdir: 6.5.0(picomatch@4.0.4) @@ -4151,12 +5795,13 @@ snapshots: fsevents: 2.3.3 jiti: 2.7.0 lightningcss: 1.32.0 + yaml: 2.9.0 - vitest@3.2.4(@types/node@22.19.18)(jiti@2.7.0)(lightningcss@1.32.0): + vitest@3.2.4(@types/node@22.19.18)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0): dependencies: '@types/chai': 5.2.3 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@7.3.3(@types/node@22.19.18)(jiti@2.7.0)(lightningcss@1.32.0)) + '@vitest/mocker': 3.2.4(vite@7.3.3(@types/node@22.19.18)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 @@ -4174,8 +5819,8 @@ snapshots: tinyglobby: 0.2.16 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 7.3.3(@types/node@22.19.18)(jiti@2.7.0)(lightningcss@1.32.0) - vite-node: 3.2.4(@types/node@22.19.18)(jiti@2.7.0)(lightningcss@1.32.0) + vite: 7.3.3(@types/node@22.19.18)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0) + vite-node: 3.2.4(@types/node@22.19.18)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 22.19.18 @@ -4203,6 +5848,8 @@ snapshots: transitivePeerDependencies: - debug + web-streams-polyfill@3.3.3: {} + which@2.0.2: dependencies: isexe: 2.0.0 @@ -4224,6 +5871,10 @@ snapshots: wrappy@1.0.2: {} + ws@8.20.1: {} + + xml-naming@0.1.0: {} + xmlchars@2.2.0: {} y18n@5.0.8: {} @@ -4232,8 +5883,22 @@ snapshots: yallist@5.0.0: {} + yaml@2.9.0: {} + + yargs-parser@20.2.9: {} + yargs-parser@21.1.1: {} + yargs@16.2.0: + dependencies: + cliui: 7.0.4 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 20.2.9 + yargs@17.7.2: dependencies: cliui: 8.0.1 @@ -4255,4 +5920,8 @@ snapshots: compress-commons: 4.1.2 readable-stream: 3.6.2 + zod-to-json-schema@3.25.2(zod@3.25.76): + dependencies: + zod: 3.25.76 + zod@3.25.76: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 0cd7ed7..eeb34e8 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -3,7 +3,10 @@ packages: - "packages/*" allowBuilds: + '@google/genai': true better-sqlite3: true electron: true esbuild: true - puppeteer: set this to true or false + koffi: true + protobufjs: true + puppeteer: true