diff --git a/.github/actions/mayros-review/action.yml b/.github/actions/mayros-review/action.yml index 8a325f56..9c88c624 100644 --- a/.github/actions/mayros-review/action.yml +++ b/.github/actions/mayros-review/action.yml @@ -22,7 +22,8 @@ inputs: required: true anthropic-api-key: description: "Anthropic API key for LLM calls" - required: true + required: false + default: "" max-diff-lines: description: "Maximum diff lines to include in the review prompt (0 = unlimited)" required: false @@ -54,27 +55,6 @@ runs: with: node-version: ${{ inputs.node-version }} - - name: Install Mayros - id: install-mayros - shell: bash - run: | - MAYROS_VERSION="${{ inputs.mayros-version }}" - # Sanitize version string: only allow semver chars and "latest" - if [[ ! "$MAYROS_VERSION" =~ ^[a-zA-Z0-9.\-]+$ ]]; then - echo "::error::Invalid mayros-version format: ${MAYROS_VERSION}" - exit 1 - fi - - echo "Installing mayros@${MAYROS_VERSION}..." - if npm install -g "mayros@${MAYROS_VERSION}"; then - echo "installed=global" >> "$GITHUB_OUTPUT" - echo "Mayros installed globally" - mayros --version || true - else - echo "::warning::Global install failed, will fall back to npx" - echo "installed=npx" >> "$GITHUB_OUTPUT" - fi - - name: Get PR diff id: get-diff shell: bash @@ -155,12 +135,33 @@ runs: PROMPT_CONTENT=$(cat /tmp/review-prompt.txt) - # Run review with proper argument separation + # Call Anthropic API directly — Mayros CLI needs gateway+cortex which + # aren't available in CI. A simple curl call is more reliable here. + MODEL_ID="${INPUT_MODEL#anthropic/}" + set +e - REVIEW=$(npx mayros -p "$PROMPT_CONTENT" --model "$INPUT_MODEL" 2>/tmp/mayros-stderr.txt) + REVIEW=$(curl -sS --max-time 120 \ + -H "x-api-key: ${ANTHROPIC_API_KEY}" \ + -H "anthropic-version: 2023-06-01" \ + -H "content-type: application/json" \ + -d "$(jq -n --arg prompt "$PROMPT_CONTENT" --arg model "$MODEL_ID" \ + '{model: $model, max_tokens: 4096, messages: [{role: "user", content: $prompt}]}')" \ + "https://api.anthropic.com/v1/messages" 2>/tmp/mayros-stderr.txt) EXIT_CODE=$? set -e + if [ $EXIT_CODE -eq 0 ]; then + REVIEW=$(echo "$REVIEW" | jq -r '.content[0].text // empty' 2>/dev/null) + if [ -z "$REVIEW" ]; then + API_ERROR=$(echo "$REVIEW" | jq -r '.error.message // "unknown API error"' 2>/dev/null) + echo "::error::Anthropic API error: ${API_ERROR}" + echo "review-length=0" >> "$GITHUB_OUTPUT" + echo "failed=true" >> "$GITHUB_OUTPUT" + exit 1 + fi + EXIT_CODE=0 + fi + if [ $EXIT_CODE -ne 0 ]; then STDERR_MSG=$(cat /tmp/mayros-stderr.txt 2>/dev/null || echo "unknown error") echo "::error::Mayros review failed (exit ${EXIT_CODE}): ${STDERR_MSG}" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3ef847c8..a856ab70 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -163,6 +163,7 @@ jobs: needs: [docs-scope, changed-scope, check] if: needs.docs-scope.outputs.docs_only != 'true' && (github.event_name == 'push' || needs.changed-scope.outputs.run_node == 'true') runs-on: ubuntu-latest + timeout-minutes: 30 strategy: fail-fast: false matrix: @@ -254,6 +255,7 @@ jobs: needs: [docs-scope, changed-scope, check] if: github.event_name == 'pull_request' && needs.docs-scope.outputs.docs_only != 'true' && needs.changed-scope.outputs.run_macos == 'true' runs-on: macos-latest + timeout-minutes: 20 steps: - name: Checkout uses: actions/checkout@v4 @@ -318,6 +320,8 @@ jobs: needs: [docs-scope, changed-scope, check] if: needs.docs-scope.outputs.docs_only != 'true' && (github.event_name == 'push' || needs.changed-scope.outputs.run_android == 'true') runs-on: ubuntu-latest + env: + HAS_KEYSTORE: ${{ secrets.KEYSTORE_FILE != '' }} strategy: fail-fast: false matrix: @@ -331,34 +335,34 @@ jobs: requires_signing: true steps: - name: Checkout - if: "!matrix.requires_signing || secrets.KEYSTORE_FILE != ''" + if: "!matrix.requires_signing || env.HAS_KEYSTORE == 'true'" uses: actions/checkout@v4 with: submodules: false - name: Skip (no signing secrets) - if: matrix.requires_signing && secrets.KEYSTORE_FILE == '' + if: matrix.requires_signing && env.HAS_KEYSTORE != 'true' run: echo "Skipping ${{ matrix.task }} — KEYSTORE_FILE secret not configured" - name: Setup Java - if: "!matrix.requires_signing || secrets.KEYSTORE_FILE != ''" + if: "!matrix.requires_signing || env.HAS_KEYSTORE == 'true'" uses: actions/setup-java@v4 with: distribution: temurin java-version: 17 - name: Setup Android SDK - if: "!matrix.requires_signing || secrets.KEYSTORE_FILE != ''" + if: "!matrix.requires_signing || env.HAS_KEYSTORE == 'true'" uses: android-actions/setup-android@v3 with: accept-android-sdk-licenses: false - name: Setup Gradle - if: "!matrix.requires_signing || secrets.KEYSTORE_FILE != ''" + if: "!matrix.requires_signing || env.HAS_KEYSTORE == 'true'" uses: gradle/actions/setup-gradle@v4 - name: Install Android SDK packages - if: "!matrix.requires_signing || secrets.KEYSTORE_FILE != ''" + if: "!matrix.requires_signing || env.HAS_KEYSTORE == 'true'" run: | yes | sdkmanager --licenses >/dev/null sdkmanager --install \ @@ -367,12 +371,12 @@ jobs: "build-tools;36.0.0" - name: Decode keystore - if: matrix.requires_signing && secrets.KEYSTORE_FILE != '' + if: matrix.requires_signing && env.HAS_KEYSTORE == 'true' working-directory: apps/android/app run: echo "${{ secrets.KEYSTORE_FILE }}" | base64 -d > release.keystore - name: Run Android ${{ matrix.task }} - if: "!matrix.requires_signing || secrets.KEYSTORE_FILE != ''" + if: "!matrix.requires_signing || env.HAS_KEYSTORE == 'true'" working-directory: apps/android run: ${{ matrix.command }} env: diff --git a/.github/workflows/mayros-review.yml b/.github/workflows/mayros-review.yml index e7763f26..bdb922f4 100644 --- a/.github/workflows/mayros-review.yml +++ b/.github/workflows/mayros-review.yml @@ -22,12 +22,23 @@ jobs: runs-on: ubuntu-latest if: github.event.pull_request.draft == false timeout-minutes: 10 + env: + HAS_API_KEY: ${{ secrets.ANTHROPIC_API_KEY != '' }} steps: + - name: Check API key availability + if: env.HAS_API_KEY != 'true' + shell: bash + run: | + echo "::notice::ANTHROPIC_API_KEY is not configured — skipping PR review" + echo "To enable automated reviews, add the ANTHROPIC_API_KEY secret to this repository." + - uses: actions/checkout@v4 + if: env.HAS_API_KEY == 'true' with: fetch-depth: 0 - uses: ./.github/actions/mayros-review + if: env.HAS_API_KEY == 'true' with: github-token: ${{ secrets.GITHUB_TOKEN }} anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }} diff --git a/.gitignore b/.gitignore index 68f18712..abd7b01b 100644 --- a/.gitignore +++ b/.gitignore @@ -155,3 +155,14 @@ docs/internal/ # IDE workspace settings (may contain tokens) .vscode/ .secret/ + +# Installer build artifacts +installer/macos/build/ +installer/macos/output/ +installer/linux/build/ +installer/linux/output/ +installer/windows/build/ +installer/windows/output/ + +# Debug context (local only) +MAC-DEBUG-CONTEXT.md diff --git a/README-SETUP.md b/README-SETUP.md index 3a9b5b83..3bcf9291 100644 --- a/README-SETUP.md +++ b/README-SETUP.md @@ -3,6 +3,7 @@ ## 1. Extract this zip anywhere ## 2. Restore the repo + ```powershell cd C:\Users\\repositorios\apilium git clone repo/maryosCode.git maryosCode @@ -13,6 +14,7 @@ pnpm build ``` ## 3. SSH key for git signing + ```powershell mkdir -Force $env:USERPROFILE\.ssh copy ssh-keys\ApiliumDevTeam-GitHub $env:USERPROFILE\.ssh\ @@ -27,6 +29,7 @@ git config --global commit.gpgsign true ``` ## 4. Mayros config + ```powershell mkdir -Force $env:USERPROFILE\.mayros copy mayros-config\mayros.json $env:USERPROFILE\.mayros\ @@ -34,6 +37,7 @@ xcopy /E cortex-data $env:USERPROFILE\.mayros\cortex-data\ ``` ## 5. Claude Code memory + ```powershell # Find your project path in Claude Code (it uses the full path as key) # On Windows it will be something like: @@ -43,24 +47,29 @@ xcopy /E claude-memory\* "$env:USERPROFILE\.claude\projects\-C-Users--repos ``` ## 6. Secret plans (already in the repo under .secret/) + The .secret/ directory is gitignored and contains: + - v0.3.0-kaneru-dethrone-plan.md — Full Kaneru plan - KANERU-MASTER-PROMPT.md — Master prompt for invoking milestones - M1-paperclip-intel.md — Competitive intelligence - M1-architecture-plan.md — Architecture decisions Copy these into the repo's .secret/ if not present: + ```powershell xcopy /E secret-plans\* maryosCode\.secret\ ``` ## 7. Start Claude Code + ```powershell cd maryosCode claude ``` Then paste: + ``` I'm continuing the Kaneru v0.3.0 release on feature/v0.3.0-remote-terminal. M1 (Surface Kaneru) is committed. Build clean, 12241 tests pass. @@ -70,6 +79,7 @@ Resume M1 Phase 4 (QA), then proceed to M2. ``` ## Repos needed (clone from GitHub) + - Mayros: git@github.com:ApiliumCode/mayros.git -- AIngle (reference): git@github.com:ApiliumCode/aingle.git +- AIngle (reference): git@github.com:ApiliumCode/aingle.git - Paperclip (competitive study): git@github.com:paperclipai/paperclip.git diff --git a/README.md b/README.md index ee0cfec0..a1f15900 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@

AI agent framework · Coding CLI · Personal assistant
- One platform. Your terminal, your channels, your devices. + One platform. Your terminal, your channels, your devices.

@@ -100,13 +100,13 @@ A detailed data loss warning is always shown before deletion — listing every c Zero-setup installers that bundle Node.js + AIngle Cortex — no prerequisites required. -| Platform | Format | Download | -|----------|--------|----------| -| **Windows** | `.exe` (NSIS) | [Releases](https://github.com/ApiliumCode/mayros/releases) | -| **macOS (Apple Silicon)** | `.dmg` | [Releases](https://github.com/ApiliumCode/mayros/releases) | -| **macOS (Intel)** | `.dmg` | [Releases](https://github.com/ApiliumCode/mayros/releases) | -| **Linux (x64)** | `.AppImage` / `.deb` | [Releases](https://github.com/ApiliumCode/mayros/releases) | -| **Linux (arm64)** | `.AppImage` / `.deb` | [Releases](https://github.com/ApiliumCode/mayros/releases) | +| Platform | Format | Download | +| ------------------------- | -------------------- | ---------------------------------------------------------- | +| **Windows** | `.exe` (NSIS) | [Releases](https://github.com/ApiliumCode/mayros/releases) | +| **macOS (Apple Silicon)** | `.dmg` | [Releases](https://github.com/ApiliumCode/mayros/releases) | +| **macOS (Intel)** | `.dmg` | [Releases](https://github.com/ApiliumCode/mayros/releases) | +| **Linux (x64)** | `.AppImage` / `.deb` | [Releases](https://github.com/ApiliumCode/mayros/releases) | +| **Linux (arm64)** | `.AppImage` / `.deb` | [Releases](https://github.com/ApiliumCode/mayros/releases) | Each installer includes Node.js portable + AIngle Cortex binary, runs `npm install` at install time, creates desktop shortcuts, and adds Mayros to the system PATH. @@ -118,11 +118,11 @@ Run models locally with automatic GPU detection and guided setup. Mayros detects **Supported runtimes:** -| Runtime | Use case | -|---------|----------| -| **Ollama** | Easiest setup — one command install | -| **vLLM** | High-throughput serving with tensor parallelism | -| **NVIDIA NIM** | Optimized containers for NVIDIA GPUs | +| Runtime | Use case | +| -------------- | ----------------------------------------------- | +| **Ollama** | Easiest setup — one command install | +| **vLLM** | High-throughput serving with tensor parallelism | +| **NVIDIA NIM** | Optimized containers for NVIDIA GPUs | **GPU detection:** NVIDIA (nvidia-smi), AMD/Intel (PowerShell on Windows, lspci on Linux), Apple Silicon (sysctl), Intel Mac (system_profiler), Raspberry Pi (BCM2 chipset). @@ -388,7 +388,7 @@ CLI: `mayros workflow run|list` · `mayros dashboard team|summary|agent` · `may Kaneru turns Mayros into a full AI venture platform. Create organizations of agents that learn, coordinate, and improve over time — all stored in Cortex, all DAG-auditable. -### Quick Start +### Kaneru Quick Start ```bash # Install a pre-built venture template (security-audit, content-pipeline, devops-squad) @@ -455,17 +455,17 @@ The portal at `http://localhost:18789` includes: Any MCP client (Claude, Cursor, Cline) gets access to 24 Kaneru tools: -| Tool | Purpose | -|------|---------| -| `kaneru_venture_create/list` | Venture CRUD | -| `kaneru_mission_create/claim/list/transition` | Mission lifecycle | -| `kaneru_fuel_summary/analytics/forecast` | Cost control | -| `kaneru_dojo_list/install` | Venture templates | -| `kaneru_learn_profile/top` | Agent expertise | -| `kaneru_decisions_list/explain` | Decision audit | -| `kaneru_sync/terminal_exec` | P2P sync + remote execution | -| `kaneru_squad_create/run/status` | Multi-agent squads | -| `kaneru_delegate/consensus/route/fuse/mailbox` | Coordination | +| Tool | Purpose | +| ---------------------------------------------- | --------------------------- | +| `kaneru_venture_create/list` | Venture CRUD | +| `kaneru_mission_create/claim/list/transition` | Mission lifecycle | +| `kaneru_fuel_summary/analytics/forecast` | Cost control | +| `kaneru_dojo_list/install` | Venture templates | +| `kaneru_learn_profile/top` | Agent expertise | +| `kaneru_decisions_list/explain` | Decision audit | +| `kaneru_sync/terminal_exec` | P2P sync + remote execution | +| `kaneru_squad_create/run/status` | Multi-agent squads | +| `kaneru_delegate/consensus/route/fuse/mailbox` | Coordination | CLI: `mayros kaneru` — 18 subcommand groups with 40+ commands total diff --git a/docs/MCP-SERVE-PLAN.md b/docs/MCP-SERVE-PLAN.md index 2c7775b7..19245f24 100644 --- a/docs/MCP-SERVE-PLAN.md +++ b/docs/MCP-SERVE-PLAN.md @@ -135,11 +135,11 @@ process.on("SIGINT", () => { ## Task 2: Dedicated MCP Memory Tools -### Problem +### Problem — MCP API Complexity The existing `semantic_memory_store` tool is designed for internal agent use. Its API is complex (RDF triples, subjects, predicates). MCP clients need a simpler, more intuitive API. -### Solution +### Solution — Dedicated MCP Tools Register dedicated `mayros_*` prefixed tools in the MCP server plugin that wrap the existing Cortex client with a user-friendly API. @@ -797,7 +797,7 @@ Pass `allTools` to `McpServerOptions` instead of `tools`. ## Task 4: Legacy SSE Transport (Claude Desktop compatibility) -### Problem +### Problem — SSE Transport Missing Claude Desktop uses the legacy MCP "HTTP with SSE" transport (`/sse` endpoint + POST to returned URL). The current HTTP transport only supports Streamable HTTP (`POST /mcp`). @@ -936,7 +936,7 @@ const setup = program ## Task 6: Enhanced Health Check -### Modify `extensions/mcp-server/transport-http.ts` +### Modify `extensions/mcp-server/transport-http.ts` — Health Endpoint Change health endpoint to include Cortex status: diff --git a/docs/reference/templates/IDENTITY.md b/docs/reference/templates/IDENTITY.md index 9e56aed2..c5c3fe1d 100644 --- a/docs/reference/templates/IDENTITY.md +++ b/docs/reference/templates/IDENTITY.md @@ -8,16 +8,11 @@ read_when: _Fill this in during your first conversation. Make it yours._ -- **Name:** - _(pick something you like)_ -- **Creature:** - _(AI? robot? familiar? ghost in the machine? something weirder?)_ -- **Vibe:** - _(how do you come across? sharp? warm? chaotic? calm?)_ -- **Emoji:** - _(your signature — pick one that feels right)_ -- **Avatar:** - _(workspace-relative path, http(s) URL, or data URI)_ +- **Name:** Atlas +- **Creature:** Autonomous AI agent +- **Vibe:** Sharp, helpful, proactive +- **Emoji:** 🔴 +- **Avatar:** mayrito-face.png --- diff --git a/docs/start/hubs.md b/docs/start/hubs.md index b8501573..06355f70 100644 --- a/docs/start/hubs.md +++ b/docs/start/hubs.md @@ -175,14 +175,6 @@ Use these hubs to discover every page, including deep dives and reference docs t - [Templates: TOOLS](/reference/templates/TOOLS) - [Templates: USER](/reference/templates/USER) -## Experiments (exploratory) - -- [Onboarding config protocol](/experiments/onboarding-config-protocol) -- [Cron hardening notes](/experiments/plans/cron-add-hardening) -- [Group policy hardening notes](/experiments/plans/group-policy-hardening) -- [Research: memory](/experiments/research/memory) -- [Model config exploration](/experiments/proposals/model-config) - ## Project - [Credits](/reference/credits) diff --git a/docs/zh-CN/start/hubs.md b/docs/zh-CN/start/hubs.md index 800cf300..98ab4926 100644 --- a/docs/zh-CN/start/hubs.md +++ b/docs/zh-CN/start/hubs.md @@ -178,14 +178,6 @@ x-i18n: - [模板:TOOLS](/reference/templates/TOOLS) - [模板:USER](/reference/templates/USER) -## 实验(探索性) - -- [新手引导配置协议](/experiments/onboarding-config-protocol) -- [定时任务加固笔记](/experiments/plans/cron-add-hardening) -- [群组策略加固笔记](/experiments/plans/group-policy-hardening) -- [研究:记忆](/experiments/research/memory) -- [模型配置探索](/experiments/proposals/model-config) - ## 项目 - [致谢](/reference/credits) diff --git a/extensions/agent-mesh/consensus-engine.ts b/extensions/agent-mesh/consensus-engine.ts index 6e00a837..69202b9d 100644 --- a/extensions/agent-mesh/consensus-engine.ts +++ b/extensions/agent-mesh/consensus-engine.ts @@ -365,5 +365,4 @@ export class ConsensusEngine { return fallback; } - } diff --git a/extensions/agent-mesh/index.ts b/extensions/agent-mesh/index.ts index d786e07f..24ba5bf6 100644 --- a/extensions/agent-mesh/index.ts +++ b/extensions/agent-mesh/index.ts @@ -1971,9 +1971,7 @@ const agentMeshPlugin = { try { const summary = await dashboard.getSummary(); const fullTable = taskRouter?.getRouteTable?.() ?? []; - const routeTable = fullTable - .sort((a, b) => b.qValue - a.qValue) - .slice(0, 100); + const routeTable = fullTable.sort((a, b) => b.qValue - a.qValue).slice(0, 100); // Collect available agents from all venture chains const availableAgents: Array<{ agentId: string; role: string }> = []; @@ -1985,7 +1983,9 @@ const agentMeshPlugin = { const ventures = await vm.list(); for (const v of ventures.slice(0, 10)) { const chain = await cm.getChain(v.id); - const extractAgents = (nodes: Array<{ agentId: string; role: string; children: unknown[] }>) => { + const extractAgents = ( + nodes: Array<{ agentId: string; role: string; children: unknown[] }>, + ) => { for (const n of nodes) { if (!availableAgents.some((a) => a.agentId === n.agentId)) { availableAgents.push({ agentId: n.agentId, role: n.role }); @@ -2047,7 +2047,14 @@ const agentMeshPlugin = { const ventures = await vm.list(); const venturesSummary = []; - const allMissions: Array<{ id: string; identifier: string; title: string; status: string; priority: string; claimedBy: string | null }> = []; + const allMissions: Array<{ + id: string; + identifier: string; + title: string; + status: string; + priority: string; + claimedBy: string | null; + }> = []; let totalFuelSpent = 0; let activeMissions = 0; @@ -2087,7 +2094,12 @@ const agentMeshPlugin = { } // Collect chain data from all ventures for visualization - const allChainNodes: Array<{ agentId: string; role: string; escalatesTo: string | null; children: unknown[] }> = []; + const allChainNodes: Array<{ + agentId: string; + role: string; + escalatesTo: string | null; + children: unknown[]; + }> = []; for (const v of ventures.slice(0, 20)) { try { const chain = await cm.getChain(v.id); @@ -2153,8 +2165,10 @@ const agentMeshPlugin = { // 3. Create first mission const validPriorities = ["critical", "high", "medium", "low"] as const; - const priority = validPriorities.includes(p.missionPriority as typeof validPriorities[number]) - ? (p.missionPriority as typeof validPriorities[number]) + const priority = validPriorities.includes( + p.missionPriority as (typeof validPriorities)[number], + ) + ? (p.missionPriority as (typeof validPriorities)[number]) : "medium"; const mission = await mm.create({ ventureId: venture.id, @@ -2181,7 +2195,8 @@ const agentMeshPlugin = { api.registerGatewayMethod("kaneru.canvas", async ({ params, respond }) => { try { const { loadCanvasData } = await import("../kaneru/canvas-gateway.js"); - const { generateSurface, generateAllSurfaces } = await import("../kaneru/canvas-surfaces.js"); + const { generateSurface, generateAllSurfaces } = + await import("../kaneru/canvas-surfaces.js"); const data = await loadCanvasData(client, ns); const surfaceId = (params as { surface?: string })?.surface; @@ -2207,7 +2222,7 @@ const agentMeshPlugin = { for (const [name, handler] of Object.entries(methods)) { api.registerGatewayMethod(name, async ({ params, respond }) => { try { - const result = await handler(params ?? {}); + const result = await handler((params ?? {}) as any); respond(true, result); } catch (err) { respond(false, { error: err instanceof Error ? err.message : String(err) }); @@ -2228,6 +2243,7 @@ const agentMeshPlugin = { provider: string; apiKey: string; model: string; + agentName?: string; }; // Store onboarding config as triples in Cortex @@ -2255,6 +2271,62 @@ const agentMeshPlugin = { object: new Date().toISOString(), }); + // Persist model + auth to config files so the agent can use them + try { + const nodePath = await import("node:path"); + const nodeFsP = await import("node:fs/promises"); + const home = process.env.HOME ?? ""; + + // Update mayros.json with model + ollama auth profile + const configPath = nodePath.join(home, ".mayros", "mayros.json"); + let cfg: Record = {}; + try { + cfg = JSON.parse(await nodeFsP.readFile(configPath, "utf8")); + } catch { + /* new */ + } + if (!cfg.agents) cfg.agents = {}; + if (!cfg.agents.defaults) cfg.agents.defaults = {}; + if (!cfg.agents.defaults.model) cfg.agents.defaults.model = {}; + cfg.agents.defaults.model.primary = p.model; + if (p.provider === "local" && p.model.startsWith("ollama/")) { + if (!cfg.auth) cfg.auth = {}; + if (!cfg.auth.profiles) cfg.auth.profiles = {}; + cfg.auth.profiles["ollama"] = { provider: "ollama", mode: "api_key" }; + } + await nodeFsP.writeFile(configPath, JSON.stringify(cfg, null, 2) + "\n"); + + // Write Ollama credentials to auth-profiles.json + if (p.provider === "local" && p.model.startsWith("ollama/")) { + const agentDir = nodePath.join(home, ".mayros", "agents", "main", "agent"); + await nodeFsP.mkdir(agentDir, { recursive: true }); + const storePath = nodePath.join(agentDir, "auth-profiles.json"); + let store: Record = { version: 2, profiles: {} }; + try { + store = JSON.parse(await nodeFsP.readFile(storePath, "utf8")); + } catch { + /* new */ + } + if (!store.profiles) store.profiles = {}; + store.profiles["ollama"] = { provider: "ollama", type: "api_key", key: "ollama-local" }; + await nodeFsP.writeFile(storePath, JSON.stringify(store, null, 2)); + } + + // Write agent name to IDENTITY.md + const agentName = p.agentName?.trim() || "Atlas"; + const workspaceDir = nodePath.join(home, ".mayros", "workspace"); + const identityPath = nodePath.join(workspaceDir, "IDENTITY.md"); + try { + let identity = await nodeFsP.readFile(identityPath, "utf8"); + identity = identity.replace(/- \*\*Name:\*\*.*/, `- **Name:** ${agentName}`); + await nodeFsP.writeFile(identityPath, identity); + } catch { + /* workspace not ready yet — non-fatal */ + } + } catch { + // Config write failure is non-fatal — model is still in Cortex + } + respond(true, { saved: true }); } catch (err) { respond(false, { error: err instanceof Error ? err.message : String(err) }); @@ -2302,9 +2374,17 @@ const agentMeshPlugin = { const res = await fetch("http://127.0.0.1:11434/api/tags", { signal: AbortSignal.timeout(3000), }); - respond(true, { detected: res.ok }); + if (!res.ok) { + respond(true, { detected: false, models: [] }); + return; + } + const data = (await res.json()) as { models?: Array<{ name?: string }> }; + const models = (data.models ?? []) + .map((m: { name?: string }) => m.name ?? "") + .filter(Boolean); + respond(true, { detected: true, models }); } catch { - respond(true, { detected: false }); + respond(true, { detected: false, models: [] }); } }); diff --git a/extensions/agent-mesh/kaneru-facade.test.ts b/extensions/agent-mesh/kaneru-facade.test.ts index bd6d1d93..9de60a69 100644 --- a/extensions/agent-mesh/kaneru-facade.test.ts +++ b/extensions/agent-mesh/kaneru-facade.test.ts @@ -240,9 +240,7 @@ describe("KaneruFacade", () => { // startWorkflow looks up a workflow by name — since no workflows are // registered in tests, it should throw a "not found" error - await expect( - facade.squadRun("squad-123", "unknown-mission"), - ).rejects.toThrow(); + await expect(facade.squadRun("squad-123", "unknown-mission")).rejects.toThrow(); }); }); diff --git a/extensions/agent-mesh/kaneru-facade.ts b/extensions/agent-mesh/kaneru-facade.ts index 0db14461..78abfd93 100644 --- a/extensions/agent-mesh/kaneru-facade.ts +++ b/extensions/agent-mesh/kaneru-facade.ts @@ -15,6 +15,7 @@ import { NamespaceManager } from "./namespace-manager.js"; import { KnowledgeFusion } from "./knowledge-fusion.js"; import { TeamManager } from "./team-manager.js"; import { AgentMailbox } from "./agent-mailbox.js"; +import type { MailMessageType } from "./agent-mailbox.js"; import { BackgroundTracker } from "./background-tracker.js"; import { WorkflowOrchestrator } from "./workflow-orchestrator.js"; import { TaskRouter } from "./task-router.js"; @@ -25,7 +26,11 @@ import { TeamDashboardService } from "./team-dashboard.js"; import { LearningProfileManager } from "../kaneru/learning-profiles.js"; import type { LearningProfile } from "../kaneru/learning-profiles.js"; import { DecisionHistory } from "../kaneru/decision-history.js"; -import type { DecisionRecord, DecisionContext, ConsensusResultLike } from "../kaneru/decision-history.js"; +import type { + DecisionRecord, + DecisionContext, + ConsensusResultLike, +} from "../kaneru/decision-history.js"; import { KnowledgeTransferService } from "../kaneru/knowledge-transfer.js"; import type { TransferResult } from "../kaneru/knowledge-transfer.js"; import { DojoService } from "../kaneru/dojo.js"; @@ -44,7 +49,8 @@ import { VentureManager } from "../kaneru/venture.js"; import { ChainManager } from "../kaneru/chain.js"; import { DirectiveManager } from "../kaneru/directives.js"; import { randomUUID } from "node:crypto"; -import type { MergeStrategy, ConsensusStrategy } from "./mesh-protocol.js"; +import type { MergeStrategy } from "./mesh-protocol.js"; +import type { ConsensusStrategy } from "./consensus-engine.js"; // ============================================================================ // Types @@ -162,13 +168,22 @@ export class KaneruFacade { this.taskRouter.setLearningProfiles(this.learningProfiles); this.decisionHistory = new DecisionHistory(this.client, this.ns); this.knowledgeTransfer = new KnowledgeTransferService( - this.client, this.ns, this.fusion, this.nsMgr, + this.client, + this.ns, + this.fusion, + this.nsMgr, ); const ventureManager = new VentureManager(this.client, this.ns); const chainManager = new ChainManager(this.client, this.ns); const directiveManager = new DirectiveManager(this.client, this.ns); - this.dojo = new DojoService(this.client, this.ns, ventureManager, chainManager, directiveManager); + this.dojo = new DojoService( + this.client, + this.ns, + ventureManager, + chainManager, + directiveManager, + ); this.channelOps = new ChannelOpsService(this.ns); this.distributed = new DistributedVentureManager(this.client, this.ns); this.comments = new MissionCommentService(this.client, this.ns); @@ -276,7 +291,7 @@ export class KaneruFacade { from, to, content, - type: (type ?? "info") as "task" | "result" | "info" | "error", + type: (type ?? "info") as MailMessageType, }); } @@ -361,14 +376,21 @@ export class KaneruFacade { // ---- Knowledge Transfer ---- /** Transfer knowledge from agent's namespace to shared namespace. */ - async transferKnowledge(agentId: string, missionId: string, squadId?: string): Promise { + async transferKnowledge( + agentId: string, + missionId: string, + squadId?: string, + ): Promise { return this.knowledgeTransfer.transferOnComplete(agentId, missionId, squadId); } // ---- Decision History ---- /** Record a consensus result as a decision. */ - async recordDecision(result: ConsensusResultLike, context?: DecisionContext): Promise { + async recordDecision( + result: ConsensusResultLike, + context?: DecisionContext, + ): Promise { return this.decisionHistory.record(result, context); } @@ -385,10 +407,14 @@ export class KaneruFacade { // ---- Dojo ---- /** List available Dojo venture templates. */ - async dojoList(): Promise { return this.dojo.listTemplates(); } + async dojoList(): Promise { + return this.dojo.listTemplates(); + } /** Preview a Dojo template as a human-readable string. */ - async dojoPreview(templateId: string): Promise { return this.dojo.preview(templateId); } + async dojoPreview(templateId: string): Promise { + return this.dojo.preview(templateId); + } /** Install a Dojo template as a new venture. */ async dojoInstall(templateId: string, ventureName: string): Promise { @@ -449,7 +475,10 @@ export class KaneruFacade { // ---- Cost Analytics ---- /** Full cost analytics for a venture. */ - async costAnalysis(ventureId: string, opts?: { period?: string; fuelLimit?: number }): Promise { + async costAnalysis( + ventureId: string, + opts?: { period?: string; fuelLimit?: number }, + ): Promise { return this.costAnalyticsSvc.analyze(ventureId, { period: (opts?.period as "daily" | "weekly" | "monthly") ?? "daily", fuelLimit: opts?.fuelLimit, @@ -460,9 +489,7 @@ export class KaneruFacade { async getDashboard(): Promise { const summary = await this.dashboard.getSummary(); const fullTable = this.taskRouter.getRouteTable?.() ?? []; - const routeTable = fullTable - .sort((a, b) => b.qValue - a.qValue) - .slice(0, 100); + const routeTable = fullTable.sort((a, b) => b.qValue - a.qValue).slice(0, 100); return { squads: summary.teams.map((t) => ({ id: t.teamId, diff --git a/extensions/agent-mesh/performance-tracker.ts b/extensions/agent-mesh/performance-tracker.ts index e7d1896b..84660378 100644 --- a/extensions/agent-mesh/performance-tracker.ts +++ b/extensions/agent-mesh/performance-tracker.ts @@ -54,7 +54,7 @@ export class PerformanceTracker { async recordOutcome(outcome: TaskOutcome): Promise { let record = this.cache.get(outcome.agentId); if (!record) { - record = await this.loadRecord(outcome.agentId); + record = (await this.loadRecord(outcome.agentId)) ?? undefined; } if (!record) { diff --git a/extensions/agent-mesh/task-router.ts b/extensions/agent-mesh/task-router.ts index b069d3e1..cf407f08 100644 --- a/extensions/agent-mesh/task-router.ts +++ b/extensions/agent-mesh/task-router.ts @@ -136,7 +136,11 @@ export class TaskRouter { for (const a of available) { const perfScore = await this.perfTracker.getScore(a); const expertise = this.learningProfiles - ? await this.learningProfiles.getExpertise(a, classification.domain, classification.taskType) + ? await this.learningProfiles.getExpertise( + a, + classification.domain, + classification.taskType, + ) : 0.5; // Blend: 40% performance EMA + 60% learned expertise const blended = 0.4 * perfScore + 0.6 * expertise; @@ -158,7 +162,11 @@ export class TaskRouter { for (const a of available) { const q = stateActions.get(a) ?? 0; const expertise = this.learningProfiles - ? await this.learningProfiles.getExpertise(a, classification.domain, classification.taskType) + ? await this.learningProfiles.getExpertise( + a, + classification.domain, + classification.taskType, + ) : 0.5; // Normalize Q-value to 0-1 range (sigmoid-like: q / (1 + |q|)) const qNorm = q / (1 + Math.abs(q)); diff --git a/extensions/bash-sandbox/index.ts b/extensions/bash-sandbox/index.ts index af704444..a02bd049 100644 --- a/extensions/bash-sandbox/index.ts +++ b/extensions/bash-sandbox/index.ts @@ -261,7 +261,7 @@ const bashSandboxPlugin = { } // 7. Container sandbox execution - if (cfg.container.enabled && cfg.mode !== "off") { + if (cfg.container.enabled && (cfg.mode as string) !== "off") { const containerCfg = cfg.container; const workdir = typeof params.cwd === "string" ? params.cwd : process.cwd(); @@ -318,7 +318,7 @@ const bashSandboxPlugin = { } // 8. Network sandbox evaluation - if (cfg.network.enabled && cfg.mode !== "off") { + if (cfg.network.enabled && (cfg.mode as string) !== "off") { const netResult = await networkSandbox.evaluate(command); if (!netResult.allowed) { auditLog.add({ diff --git a/extensions/browser-automation/browser-client.test.ts b/extensions/browser-automation/browser-client.test.ts index ae1a0624..ddaa5e76 100644 --- a/extensions/browser-automation/browser-client.test.ts +++ b/extensions/browser-automation/browser-client.test.ts @@ -138,12 +138,12 @@ beforeEach(() => { default: class { constructor() { // Return mock instance - return mockWsInstance as unknown; + return mockWsInstance as any; // eslint-disable-line @typescript-eslint/no-explicit-any } }, WebSocket: class { constructor() { - return mockWsInstance as unknown; + return mockWsInstance as any; // eslint-disable-line @typescript-eslint/no-explicit-any } }, }; diff --git a/extensions/browser-automation/index.ts b/extensions/browser-automation/index.ts index fc7dd958..ce407915 100644 --- a/extensions/browser-automation/index.ts +++ b/extensions/browser-automation/index.ts @@ -37,6 +37,7 @@ const browserAutomationPlugin = { api.registerTool({ name: "browser_navigate", + label: "Browser Navigate", description: "Navigate browser to a URL and return page info", parameters: { type: "object" as const, @@ -45,13 +46,16 @@ const browserAutomationPlugin = { }, required: ["url"], }, - execute: async (args: Record) => { + execute: async (_toolCallId: string, args: Record) => { const { BrowserClient } = await import("./browser-client.js"); const client = new BrowserClient(); await client.connect(); try { const result = await client.navigate(args.url as string); - return { content: [{ type: "text" as const, text: JSON.stringify(result) }] }; + return { + content: [{ type: "text" as const, text: JSON.stringify(result) }], + details: undefined, + }; } finally { await client.disconnect(); } @@ -64,12 +68,13 @@ const browserAutomationPlugin = { api.registerTool({ name: "browser_screenshot", + label: "Browser Screenshot", description: "Take a screenshot of the current browser page", parameters: { type: "object" as const, properties: {}, }, - execute: async () => { + execute: async (_toolCallId: string) => { const { BrowserClient } = await import("./browser-client.js"); const client = new BrowserClient(); await client.connect(); @@ -78,12 +83,11 @@ const browserAutomationPlugin = { return { content: [ { - type: "image" as const, - mimeType: `image/${result.format}`, - bytes: result.data.length, + type: "text" as const, + text: `Screenshot: ${result.width}x${result.height} (${result.format}, ${result.data.length} bytes)`, }, - { type: "text" as const, text: `Screenshot: ${result.width}x${result.height}` }, ], + details: undefined, }; } finally { await client.disconnect(); @@ -97,6 +101,7 @@ const browserAutomationPlugin = { api.registerTool({ name: "browser_click", + label: "Browser Click", description: "Click an element by CSS selector", parameters: { type: "object" as const, @@ -105,13 +110,16 @@ const browserAutomationPlugin = { }, required: ["selector"], }, - execute: async (args: Record) => { + execute: async (_toolCallId: string, args: Record) => { const { BrowserClient } = await import("./browser-client.js"); const client = new BrowserClient(); await client.connect(); try { await client.click(args.selector as string); - return { content: [{ type: "text" as const, text: `Clicked: ${args.selector}` }] }; + return { + content: [{ type: "text" as const, text: `Clicked: ${args.selector}` }], + details: undefined, + }; } finally { await client.disconnect(); } @@ -124,6 +132,7 @@ const browserAutomationPlugin = { api.registerTool({ name: "browser_evaluate", + label: "Browser Evaluate", description: "Run JavaScript in the browser page and return result", parameters: { type: "object" as const, @@ -132,13 +141,16 @@ const browserAutomationPlugin = { }, required: ["expression"], }, - execute: async (args: Record) => { + execute: async (_toolCallId: string, args: Record) => { const { BrowserClient } = await import("./browser-client.js"); const client = new BrowserClient(); await client.connect(); try { const result = await client.evaluate(args.expression as string); - return { content: [{ type: "text" as const, text: JSON.stringify(result) }] }; + return { + content: [{ type: "text" as const, text: JSON.stringify(result) }], + details: undefined, + }; } finally { await client.disconnect(); } diff --git a/extensions/code-tools/tools/code-notebook.test.ts b/extensions/code-tools/tools/code-notebook.test.ts index 8b8e7722..5cd4addf 100644 --- a/extensions/code-tools/tools/code-notebook.test.ts +++ b/extensions/code-tools/tools/code-notebook.test.ts @@ -64,13 +64,13 @@ describe("code_notebook", () => { const cell = SAMPLE_NOTEBOOK.cells[1]; const source = cell.source.join(""); expect(source).toContain("print"); - const output = (cell.outputs[0] as { text: string[] }).text.join(""); + const output = (cell.outputs![0] as { text: string[] }).text.join(""); expect(output).toContain("hello"); }); it("formats execute_result output", () => { const cell = SAMPLE_NOTEBOOK.cells[2]; - const data = (cell.outputs[0] as { data: Record }).data; + const data = (cell.outputs![0] as { data: Record }).data; expect(data["text/plain"][0]).toBe("2"); }); diff --git a/extensions/code-tools/tools/pdf-parse.d.ts b/extensions/code-tools/tools/pdf-parse.d.ts new file mode 100644 index 00000000..5ecd01f7 --- /dev/null +++ b/extensions/code-tools/tools/pdf-parse.d.ts @@ -0,0 +1 @@ +declare module "pdf-parse"; diff --git a/extensions/eruberu/cortex-persistence.ts b/extensions/eruberu/cortex-persistence.ts index 14588972..b87dab5b 100644 --- a/extensions/eruberu/cortex-persistence.ts +++ b/extensions/eruberu/cortex-persistence.ts @@ -16,11 +16,7 @@ import type { QTableData } from "./q-learning.js"; export type CortexPersistenceClient = { createTriple(params: { subject: string; predicate: string; object: string }): Promise; - listTriples(params: { - subject?: string; - predicate?: string; - limit?: number; - }): Promise<{ + listTriples(params: { subject?: string; predicate?: string; limit?: number }): Promise<{ triples: Array<{ id?: string; subject: string; predicate: string; object: unknown }>; }>; deleteTriple(id: string): Promise; diff --git a/extensions/eruberu/index.ts b/extensions/eruberu/index.ts index 93ecf52a..76ed8e93 100644 --- a/extensions/eruberu/index.ts +++ b/extensions/eruberu/index.ts @@ -79,7 +79,7 @@ const eruberuPlugin = { | undefined; const host = (cortexCfg?.host as string | undefined) ?? "127.0.0.1"; const port = (cortexCfg?.port as number | undefined) ?? 19090; - cortexClient = new CortexClient(host, port) as unknown as CortexPersistenceClient; + cortexClient = new CortexClient({ host, port }) as unknown as CortexPersistenceClient; const data = await loadFromCortex(cortexClient); if (Object.keys(data).length > 0) { qTable.import(data); @@ -121,14 +121,17 @@ const eruberuPlugin = { // before_model_resolve — main routing logic api.on( "before_model_resolve", - async (event) => { + async (event, ctx) => { if (!qTable || !cfg.enabled) return; + // Extended event properties may be passed at runtime + const ext = event as Record; + // Skip if agent has explicit model override - if (event.modelOverride) return; + if (ext.modelOverride) return; // Determine task type from prompt - const prompt = event.prompt ?? event.systemPrompt ?? ""; + const prompt = event.prompt ?? (ext.systemPrompt as string | undefined) ?? ""; const taskType = classifyTask(prompt); // Get budget status @@ -150,7 +153,7 @@ const eruberuPlugin = { if (cfg.budgetDrivenFallback && budgetFraction !== undefined) { if (budgetFraction >= cfg.budgetCriticalThreshold) { // Force cheapest model - pendingDecisions.set(event.runId, { + pendingDecisions.set((ext.runId as string) ?? ctx.sessionId ?? "unknown", { state, action: "cost-optimized:", startTime: Date.now(), @@ -160,7 +163,7 @@ const eruberuPlugin = { }; } if (budgetFraction >= cfg.budgetWarnThreshold) { - pendingDecisions.set(event.runId, { + pendingDecisions.set((ext.runId as string) ?? ctx.sessionId ?? "unknown", { state, action: "cost-optimized:", startTime: Date.now(), @@ -188,7 +191,7 @@ const eruberuPlugin = { const [strategyPart, providerPart] = chosenAction.split(":"); const strategy = (strategyPart || "default") as ModelRoutingStrategy; - pendingDecisions.set(event.runId, { + pendingDecisions.set((ext.runId as string) ?? ctx.sessionId ?? "unknown", { state, action: chosenAction, startTime: Date.now(), @@ -210,17 +213,20 @@ const eruberuPlugin = { api.on("llm_output", async (event) => { if (!qTable) return; + // Extended event properties may be passed at runtime + const ext = event as Record; + const decision = pendingDecisions.get(event.runId); if (!decision) return; pendingDecisions.delete(event.runId); const latencyMs = Date.now() - decision.startTime; - const usage = event.usage as { input?: number; output?: number; total?: number } | undefined; + const usage = event.usage; const totalTokens = usage?.total ?? (usage?.input ?? 0) + (usage?.output ?? 0); // Compute reward signal const signal: RewardSignal = { - success: event.error ? -1.0 : 1.0, + success: ext.error ? -1.0 : 1.0, costEfficiency: 0, qualityProxy: 0, latencyPenalty: 0, @@ -245,7 +251,7 @@ const eruberuPlugin = { } // Rate limit penalty - if (event.rateLimited) { + if (ext.rateLimited) { signal.rateLimitPenalty = -0.8; } diff --git a/extensions/hayameru/index.ts b/extensions/hayameru/index.ts index a6637f2a..96984b76 100644 --- a/extensions/hayameru/index.ts +++ b/extensions/hayameru/index.ts @@ -23,7 +23,9 @@ const hayameruPlugin = { } const metrics = new HayameruMetrics(); - const workDir = api.config?.workspaceDir ?? process.cwd(); + const workDir = + ((api.config as Record | undefined)?.workspaceDir as string | undefined) ?? + process.cwd(); // before_agent_run hook — intercept simple code edits api.on( @@ -125,9 +127,10 @@ const hayameruPlugin = { // Tool: hayameru_status api.registerTool({ name: "hayameru_status", + label: "Hayameru Status", description: "Show Hayameru code transform metrics and available transforms", parameters: Type.Object({}), - execute: async () => { + execute: async (_toolCallId: string) => { const m = metrics.getMetrics(); const transforms = listTransforms(); return { @@ -152,6 +155,7 @@ const hayameruPlugin = { ), }, ], + details: undefined, }; }, }); diff --git a/extensions/kakeru-bridge/index.ts b/extensions/kakeru-bridge/index.ts index 4f10ed1e..379a4980 100644 --- a/extensions/kakeru-bridge/index.ts +++ b/extensions/kakeru-bridge/index.ts @@ -58,9 +58,10 @@ const kakeruPlugin = { // Tool: platform_status api.registerTool({ name: "platform_status", + label: "Platform Status", description: "List all registered platform bridges and their status", parameters: Type.Object({}), - execute: async () => { + execute: async (_toolCallId: string) => { const bridges = coordinator.listBridges(); return { content: [ @@ -69,6 +70,7 @@ const kakeruPlugin = { text: JSON.stringify(bridges, null, 2), }, ], + details: undefined, }; }, }); @@ -76,6 +78,7 @@ const kakeruPlugin = { // Tool: platform_execute api.registerTool({ name: "platform_execute", + label: "Platform Execute", description: "Execute a task on a specific platform bridge", parameters: Type.Object({ platform: Type.String({ description: "Platform ID (claude, codex)" }), @@ -85,19 +88,22 @@ const kakeruPlugin = { ), timeout: Type.Optional(Type.Number({ description: "Timeout in ms" })), }), - execute: async (params: { - platform: string; - prompt: string; - workDir?: string; - timeout?: number; - }) => { + execute: async ( + _toolCallId: string, + params: { + platform: string; + prompt: string; + workDir?: string; + timeout?: number; + }, + ) => { const bridge = coordinator.getBridge(params.platform); if (!bridge) { return { content: [ { type: "text" as const, text: `Platform "${params.platform}" not registered` }, ], - isError: true, + details: undefined, }; } @@ -117,11 +123,12 @@ const kakeruPlugin = { text: JSON.stringify(result, null, 2), }, ], + details: undefined, }; } catch (err) { return { content: [{ type: "text" as const, text: `Execution failed: ${String(err)}` }], - isError: true, + details: undefined, }; } }, @@ -130,6 +137,7 @@ const kakeruPlugin = { // Tool: platform_workflow api.registerTool({ name: "platform_workflow", + label: "Platform Workflow", description: "Execute tasks across multiple platforms in parallel", parameters: Type.Object({ tasks: Type.Array( @@ -140,10 +148,16 @@ const kakeruPlugin = { }), ), }), - execute: async (params: { - tasks: Array<{ platform: string; prompt: string; filePaths?: string[] }>; - }) => { - const workDir = api.config?.workspaceDir ?? process.cwd(); + execute: async ( + _toolCallId: string, + params: { + tasks: Array<{ platform: string; prompt: string; filePaths?: string[] }>; + }, + ) => { + const workDir = + ((api.config as Record | undefined)?.workspaceDir as + | string + | undefined) ?? process.cwd(); const workflowTasks = params.tasks.map((t) => ({ platformId: t.platform, task: { @@ -167,6 +181,7 @@ const kakeruPlugin = { text: JSON.stringify(output, null, 2), }, ], + details: undefined, }; }, }); diff --git a/extensions/kaneru/agent-terminal.test.ts b/extensions/kaneru/agent-terminal.test.ts index 6b0628a5..652fa28e 100644 --- a/extensions/kaneru/agent-terminal.test.ts +++ b/extensions/kaneru/agent-terminal.test.ts @@ -171,9 +171,7 @@ describe("AgentTerminalService", () => { }); // Find the stdout triple - const stdoutTriple = storedTriples.find((t) => - String(t.predicate).includes("stdout"), - ); + const stdoutTriple = storedTriples.find((t) => String(t.predicate).includes("stdout")); expect(stdoutTriple).toBeTruthy(); expect(String(stdoutTriple!.object).length).toBe(2000); }); @@ -190,9 +188,7 @@ describe("AgentTerminalService", () => { // 8 fields including missionId expect(storedTriples.length).toBe(8); - const missionTriple = storedTriples.find((t) => - String(t.predicate).includes("missionId"), - ); + const missionTriple = storedTriples.find((t) => String(t.predicate).includes("missionId")); expect(missionTriple).toBeTruthy(); expect(String(missionTriple!.object)).toBe("mission-42"); }); diff --git a/extensions/kaneru/agent-terminal.ts b/extensions/kaneru/agent-terminal.ts index e6855e4d..5a5fa17e 100644 --- a/extensions/kaneru/agent-terminal.ts +++ b/extensions/kaneru/agent-terminal.ts @@ -142,8 +142,8 @@ export class AgentTerminalService { if (exec) executions.push(exec); } - return executions.sort((a, b) => - new Date(b.executedAt).getTime() - new Date(a.executedAt).getTime(), + return executions.sort( + (a, b) => new Date(b.executedAt).getTime() - new Date(a.executedAt).getTime(), ); } diff --git a/extensions/kaneru/canvas-surfaces.test.ts b/extensions/kaneru/canvas-surfaces.test.ts index 419c75ef..62fd0e5b 100644 --- a/extensions/kaneru/canvas-surfaces.test.ts +++ b/extensions/kaneru/canvas-surfaces.test.ts @@ -106,18 +106,53 @@ const POPULATED_DATA: CanvasVentureData = { }, ], missions: [ - { id: "m1", identifier: "ALP-1", title: "Setup infra", status: "active", priority: "high", claimedBy: "agent-1" }, - { id: "m2", identifier: "ALP-2", title: "Write tests", status: "ready", priority: "medium", claimedBy: null }, - { id: "m3", identifier: "ALP-3", title: "Deploy", status: "queued", priority: "low", claimedBy: null }, - { id: "m4", identifier: "BET-1", title: "Research", status: "complete", priority: "medium", claimedBy: "agent-2" }, - { id: "m5", identifier: "ALP-4", title: "Review PR", status: "review", priority: "critical", claimedBy: "agent-1" }, + { + id: "m1", + identifier: "ALP-1", + title: "Setup infra", + status: "active", + priority: "high", + claimedBy: "agent-1", + }, + { + id: "m2", + identifier: "ALP-2", + title: "Write tests", + status: "ready", + priority: "medium", + claimedBy: null, + }, + { + id: "m3", + identifier: "ALP-3", + title: "Deploy", + status: "queued", + priority: "low", + claimedBy: null, + }, + { + id: "m4", + identifier: "BET-1", + title: "Research", + status: "complete", + priority: "medium", + claimedBy: "agent-2", + }, + { + id: "m5", + identifier: "ALP-4", + title: "Review PR", + status: "review", + priority: "critical", + claimedBy: "agent-1", + }, ], chain: [ { agentId: "agent-1", role: "lead", escalatesTo: null, - children: [{ agentId: "agent-2", role: "worker", escalatesTo: "agent-1", children: [] }], + children: [{ agentId: "agent-2", role: "worker" }], }, ], stats: { totalVentures: 2, activeMissions: 1, totalFuelSpent: 4700 }, @@ -226,7 +261,9 @@ describe("canvas-surfaces", () => { it("renders chain nodes for populated data", () => { const jsonl = generateChainSurface(POPULATED_DATA); const update = getSurfaceUpdate(jsonl); - const chainCards = update!.components.filter((c) => c.id.startsWith("chain-") && c.component.Card); + const chainCards = update!.components.filter( + (c) => c.id.startsWith("chain-") && c.component.Card, + ); // Should have at least 2 nodes (agent-1 + agent-2) expect(chainCards.length).toBeGreaterThanOrEqual(2); }); @@ -246,7 +283,9 @@ describe("canvas-surfaces", () => { it("shows provider breakdown for populated data", () => { const jsonl = generateFuelSurface(POPULATED_DATA); const update = getSurfaceUpdate(jsonl); - const providers = update!.components.filter((c) => c.id.startsWith("prov-") && !c.id.includes("title") && !c.id.includes("list")); + const providers = update!.components.filter( + (c) => c.id.startsWith("prov-") && !c.id.includes("title") && !c.id.includes("list"), + ); expect(providers.length).toBe(2); }); @@ -263,7 +302,10 @@ describe("canvas-surfaces", () => { const update = getSurfaceUpdate(jsonl); const exhaustComp = update!.components.find((c) => c.id === "fuel-exhaust-val"); expect(exhaustComp).toBeDefined(); - const text = (exhaustComp!.component.Text as Record).text as Record; + const text = (exhaustComp!.component.Text as Record).text as Record< + string, + unknown + >; expect(text.literalString).toBe("N/A"); }); }); diff --git a/extensions/kaneru/canvas-surfaces.ts b/extensions/kaneru/canvas-surfaces.ts index 044dbab2..02693c6c 100644 --- a/extensions/kaneru/canvas-surfaces.ts +++ b/extensions/kaneru/canvas-surfaces.ts @@ -12,7 +12,11 @@ // Types // ============================================================================ -export type CanvasSurfaceId = "kaneru-overview" | "kaneru-missions" | "kaneru-chain" | "kaneru-fuel"; +export type CanvasSurfaceId = + | "kaneru-overview" + | "kaneru-missions" + | "kaneru-chain" + | "kaneru-fuel"; type A2UIComponent = { id: string; @@ -95,7 +99,10 @@ function card(id: string, childIds: string[]): A2UIComponent { } function button(id: string, label: string, action: string): A2UIComponent { - return { id, component: { Button: { label: { literalString: label }, action: { literalString: action } } } }; + return { + id, + component: { Button: { label: { literalString: label }, action: { literalString: action } } }, + }; } function divider(id: string): A2UIComponent { @@ -113,7 +120,12 @@ function formatCents(cents: number): string { } function priorityLabel(priority: string): string { - const map: Record = { critical: "CRITICAL", high: "HIGH", medium: "MED", low: "LOW" }; + const map: Record = { + critical: "CRITICAL", + high: "HIGH", + medium: "MED", + low: "LOW", + }; return map[priority] ?? priority.toUpperCase(); } @@ -149,13 +161,20 @@ export function generateOverviewSurface(data: CanvasVentureData): string { for (let i = 0; i < data.ventures.length && i < 20; i++) { const v = data.ventures[i]; const vid = `v-${i}`; - const fuelText = v.fuelLimit > 0 - ? `${formatCents(v.fuelSpent)} / ${formatCents(v.fuelLimit)}` - : formatCents(v.fuelSpent); + const fuelText = + v.fuelLimit > 0 + ? `${formatCents(v.fuelSpent)} / ${formatCents(v.fuelLimit)}` + : formatCents(v.fuelSpent); components.push(card(vid, [`${vid}-name`, `${vid}-info`, `${vid}-fuel`])); components.push(text(`${vid}-name`, `[${v.prefix}] ${v.name}`, "h3")); - components.push(text(`${vid}-info`, `${v.status} | ${v.agentCount} agents | ${v.missionCount} missions`, "caption")); + components.push( + text( + `${vid}-info`, + `${v.status} | ${v.agentCount} agents | ${v.missionCount} missions`, + "caption", + ), + ); components.push(text(`${vid}-fuel`, `Fuel: ${fuelText}`, "body")); ventureCardIds.push(vid); } @@ -182,7 +201,11 @@ export function generateMissionsSurface(data: CanvasVentureData): string { const statuses = ["queued", "ready", "active", "review", "complete"]; const statusLabels: Record = { - queued: "Queued", ready: "Ready", active: "Active", review: "Review", complete: "Complete", + queued: "Queued", + ready: "Ready", + active: "Active", + review: "Review", + complete: "Complete", }; for (const status of statuses) { @@ -204,7 +227,9 @@ export function generateMissionsSurface(data: CanvasVentureData): string { } components.push(card(mid, cardChildren)); - components.push(text(`${mid}-id`, `${m.identifier} [${priorityLabel(m.priority)}]`, "caption")); + components.push( + text(`${mid}-id`, `${m.identifier} [${priorityLabel(m.priority)}]`, "caption"), + ); components.push(text(`${mid}-title`, m.title, "body")); components.push(text(`${mid}-meta`, claimInfo, "caption")); @@ -247,7 +272,10 @@ export function generateChainSurface(data: CanvasVentureData): string { nodeIds.push("no-chain"); } else { let idx = 0; - const renderNode = (node: { agentId: string; role: string; children: Array<{ agentId: string; role: string }> }, depth: number) => { + const renderNode = ( + node: { agentId: string; role: string; children: Array<{ agentId: string; role: string }> }, + depth: number, + ) => { const nid = `chain-${idx++}`; const indent = depth > 0 ? " ".repeat(depth) + "-> " : ""; components.push(card(nid, [`${nid}-name`, `${nid}-role`])); @@ -255,7 +283,7 @@ export function generateChainSurface(data: CanvasVentureData): string { components.push(text(`${nid}-role`, node.role, "caption")); nodeIds.push(nid); - for (const child of (node.children ?? [])) { + for (const child of node.children ?? []) { renderNode(child as typeof node, depth + 1); } }; @@ -290,7 +318,13 @@ export function generateFuelSurface(data: CanvasVentureData): string { const daysLeft = data.fuel?.daysUntilExhausted; components.push(card("fuel-exhaust", ["fuel-exhaust-val", "fuel-exhaust-lbl"])); - components.push(text("fuel-exhaust-val", daysLeft !== null && daysLeft !== undefined ? `${daysLeft}d` : "N/A", "h2")); + components.push( + text( + "fuel-exhaust-val", + daysLeft !== null && daysLeft !== undefined ? `${daysLeft}d` : "N/A", + "h2", + ), + ); components.push(text("fuel-exhaust-lbl", "Days Left", "caption")); components.push(row("fuel-summary", summaryIds)); @@ -301,7 +335,13 @@ export function generateFuelSurface(data: CanvasVentureData): string { for (let i = 0; i < providers.length && i < 10; i++) { const p = providers[i]; const pid = `prov-${i}`; - components.push(text(pid, `${p.provider}/${p.model}: ${formatCents(p.costCents)} (${p.eventCount} events)`, "body")); + components.push( + text( + pid, + `${p.provider}/${p.model}: ${formatCents(p.costCents)} (${p.eventCount} events)`, + "body", + ), + ); providerIds.push(pid); } @@ -316,7 +356,16 @@ export function generateFuelSurface(data: CanvasVentureData): string { components.push(divider("fuel-div")); components.push(button("fuel-refresh", "Refresh", "refresh-fuel")); - components.push(column("root", ["fuel-title", "fuel-summary", "fuel-div", "prov-title", "prov-list", "fuel-refresh"])); + components.push( + column("root", [ + "fuel-title", + "fuel-summary", + "fuel-div", + "prov-title", + "prov-list", + "fuel-refresh", + ]), + ); return buildJsonl("kaneru-fuel", components, "root"); } @@ -324,10 +373,14 @@ export function generateFuelSurface(data: CanvasVentureData): string { /** Generate a specific surface by ID. */ export function generateSurface(surfaceId: CanvasSurfaceId, data: CanvasVentureData): string { switch (surfaceId) { - case "kaneru-overview": return generateOverviewSurface(data); - case "kaneru-missions": return generateMissionsSurface(data); - case "kaneru-chain": return generateChainSurface(data); - case "kaneru-fuel": return generateFuelSurface(data); + case "kaneru-overview": + return generateOverviewSurface(data); + case "kaneru-missions": + return generateMissionsSurface(data); + case "kaneru-chain": + return generateChainSurface(data); + case "kaneru-fuel": + return generateFuelSurface(data); } } diff --git a/extensions/kaneru/chain.ts b/extensions/kaneru/chain.ts index 1ce09e1a..288ae751 100644 --- a/extensions/kaneru/chain.ts +++ b/extensions/kaneru/chain.ts @@ -68,7 +68,9 @@ export class ChainManager { const ventureNode = `${this.ns}:venture:${ventureId}`; const alreadyDeployed = existing.triples.some((t) => { const val = - typeof t.object === "object" && t.object !== null && "node" in (t.object as Record) + typeof t.object === "object" && + t.object !== null && + "node" in (t.object as Record) ? stripBrackets(String((t.object as { node: string }).node)) : String(t.object); return val === ventureNode; @@ -105,7 +107,9 @@ export class ChainManager { for (const t of deployments.triples) { const val = - typeof t.object === "object" && t.object !== null && "node" in (t.object as Record) + typeof t.object === "object" && + t.object !== null && + "node" in (t.object as Record) ? stripBrackets(String((t.object as { node: string }).node)) : String(t.object); if (val === ventureNode && t.id) { @@ -246,7 +250,8 @@ export class ChainManager { predicate: agentPredicate(this.ns, "role"), limit: 1, }); - const role = roleTriples.triples.length > 0 ? String(roleTriples.triples[0].object) : "member"; + const role = + roleTriples.triples.length > 0 ? String(roleTriples.triples[0].object) : "member"; const escTriples = await this.client.listTriples({ subject, diff --git a/extensions/kaneru/channel-ops.ts b/extensions/kaneru/channel-ops.ts index 022e3b59..4a1f7625 100644 --- a/extensions/kaneru/channel-ops.ts +++ b/extensions/kaneru/channel-ops.ts @@ -87,9 +87,7 @@ export class ChannelOpsService { /** Build a mission completion notification. */ buildMissionReport(mission: Mission, outcome: MissionOutcome): ChannelNotification { const status = outcome.success ? "completed" : "failed"; - const duration = outcome.durationMs > 0 - ? ` in ${Math.round(outcome.durationMs / 1000)}s` - : ""; + const duration = outcome.durationMs > 0 ? ` in ${Math.round(outcome.durationMs / 1000)}s` : ""; return { type: "mission-complete", @@ -120,9 +118,8 @@ export class ChannelOpsService { /** Build a decision pending notification. */ buildDecisionPrompt(decision: DecisionRecord): ChannelNotification { - const participants = decision.participants.length > 0 - ? `\nParticipants: ${decision.participants.join(", ")}` - : ""; + const participants = + decision.participants.length > 0 ? `\nParticipants: ${decision.participants.join(", ")}` : ""; return { type: "decision-pending", diff --git a/extensions/kaneru/cost-analytics.test.ts b/extensions/kaneru/cost-analytics.test.ts index f5a69caf..9d2e349d 100644 --- a/extensions/kaneru/cost-analytics.test.ts +++ b/extensions/kaneru/cost-analytics.test.ts @@ -163,8 +163,14 @@ describe("CostAnalyticsService", () => { describe("analyze", () => { it("returns full analytics object", async () => { - storeFuelEvent("test", makeFuelEvent({ id: "e1", costCents: 100, occurredAt: "2026-03-01T10:00:00Z" })); - storeFuelEvent("test", makeFuelEvent({ id: "e2", costCents: 200, occurredAt: "2026-03-02T10:00:00Z" })); + storeFuelEvent( + "test", + makeFuelEvent({ id: "e1", costCents: 100, occurredAt: "2026-03-01T10:00:00Z" }), + ); + storeFuelEvent( + "test", + makeFuelEvent({ id: "e2", costCents: 200, occurredAt: "2026-03-02T10:00:00Z" }), + ); const result = await svc.analyze("v1"); expect(result.ventureId).toBe("v1"); @@ -177,7 +183,10 @@ describe("CostAnalyticsService", () => { }); it("respects fuelLimit option", async () => { - storeFuelEvent("test", makeFuelEvent({ id: "e1", costCents: 100, occurredAt: "2026-03-01T10:00:00Z" })); + storeFuelEvent( + "test", + makeFuelEvent({ id: "e1", costCents: 100, occurredAt: "2026-03-01T10:00:00Z" }), + ); const result = await svc.analyze("v1", { fuelLimit: 500 }); expect(result.fuelLimit).toBe(500); }); @@ -236,9 +245,30 @@ describe("CostAnalyticsService", () => { describe("byProvider", () => { it("groups by provider and model", () => { const events: FuelEvent[] = [ - makeFuelEvent({ id: "e1", provider: "openai", model: "gpt-4", costCents: 100, inputTokens: 500, outputTokens: 200 }), - makeFuelEvent({ id: "e2", provider: "openai", model: "gpt-4", costCents: 200, inputTokens: 1000, outputTokens: 400 }), - makeFuelEvent({ id: "e3", provider: "anthropic", model: "claude-3", costCents: 150, inputTokens: 800, outputTokens: 300 }), + makeFuelEvent({ + id: "e1", + provider: "openai", + model: "gpt-4", + costCents: 100, + inputTokens: 500, + outputTokens: 200, + }), + makeFuelEvent({ + id: "e2", + provider: "openai", + model: "gpt-4", + costCents: 200, + inputTokens: 1000, + outputTokens: 400, + }), + makeFuelEvent({ + id: "e3", + provider: "anthropic", + model: "claude-3", + costCents: 150, + inputTokens: 800, + outputTokens: 300, + }), ]; const result = svc.byProvider(events); @@ -273,9 +303,7 @@ describe("CostAnalyticsService", () => { }); it("returns low confidence with fewer than 2 events", () => { - const events: FuelEvent[] = [ - makeFuelEvent({ id: "e1", costCents: 100 }), - ]; + const events: FuelEvent[] = [makeFuelEvent({ id: "e1", costCents: 100 })]; const f = svc.forecast(events, 10000); expect(f.confidence).toBe("low"); @@ -299,9 +327,27 @@ describe("CostAnalyticsService", () => { describe("efficiency", () => { it("computes costPerMission", () => { const events: FuelEvent[] = [ - makeFuelEvent({ id: "e1", costCents: 100, missionId: "m1", inputTokens: 500, outputTokens: 200 }), - makeFuelEvent({ id: "e2", costCents: 200, missionId: "m1", inputTokens: 1000, outputTokens: 400 }), - makeFuelEvent({ id: "e3", costCents: 300, missionId: "m2", inputTokens: 800, outputTokens: 300 }), + makeFuelEvent({ + id: "e1", + costCents: 100, + missionId: "m1", + inputTokens: 500, + outputTokens: 200, + }), + makeFuelEvent({ + id: "e2", + costCents: 200, + missionId: "m1", + inputTokens: 1000, + outputTokens: 400, + }), + makeFuelEvent({ + id: "e3", + costCents: 300, + missionId: "m2", + inputTokens: 800, + outputTokens: 300, + }), ]; // Use the private method through analyze's result — test via the public timeSeries helper @@ -317,8 +363,24 @@ describe("CostAnalyticsService", () => { }); it("handles events with no mission IDs", async () => { - storeFuelEvent("test", makeFuelEvent({ id: "e1", costCents: 100, missionId: null, occurredAt: "2026-03-01T00:00:00Z" })); - storeFuelEvent("test", makeFuelEvent({ id: "e2", costCents: 200, missionId: null, occurredAt: "2026-03-02T00:00:00Z" })); + storeFuelEvent( + "test", + makeFuelEvent({ + id: "e1", + costCents: 100, + missionId: null, + occurredAt: "2026-03-01T00:00:00Z", + }), + ); + storeFuelEvent( + "test", + makeFuelEvent({ + id: "e2", + costCents: 200, + missionId: null, + occurredAt: "2026-03-02T00:00:00Z", + }), + ); const result = await svc.analyze("v1"); expect(result.efficiency.costPerMissionCents).toBe(0); diff --git a/extensions/kaneru/cost-analytics.ts b/extensions/kaneru/cost-analytics.ts index d2fb29ea..9c3e3aff 100644 --- a/extensions/kaneru/cost-analytics.ts +++ b/extensions/kaneru/cost-analytics.ts @@ -135,7 +135,10 @@ export class CostAnalyticsService { // ---------- Builders ---------- - private buildTimeSeries(events: FuelEvent[], period: "daily" | "weekly" | "monthly"): CostTimeSeries { + private buildTimeSeries( + events: FuelEvent[], + period: "daily" | "weekly" | "monthly", + ): CostTimeSeries { const buckets = new Map(); for (const e of events) { @@ -154,11 +157,28 @@ export class CostAnalyticsService { } private buildByProvider(events: FuelEvent[]): CostByProvider { - const map = new Map(); + const map = new Map< + string, + { + provider: string; + model: string; + costCents: number; + inputTokens: number; + outputTokens: number; + eventCount: number; + } + >(); for (const e of events) { const key = `${e.provider}:${e.model}`; - const entry = map.get(key) ?? { provider: e.provider, model: e.model, costCents: 0, inputTokens: 0, outputTokens: 0, eventCount: 0 }; + const entry = map.get(key) ?? { + provider: e.provider, + model: e.model, + costCents: 0, + inputTokens: 0, + outputTokens: 0, + eventCount: 0, + }; entry.costCents += e.costCents; entry.inputTokens += e.inputTokens; entry.outputTokens += e.outputTokens; @@ -169,7 +189,9 @@ export class CostAnalyticsService { return [...map.values()].sort((a, b) => b.costCents - a.costCents); } - private buildByAgent(events: FuelEvent[]): Array<{ agentId: string; costCents: number; eventCount: number }> { + private buildByAgent( + events: FuelEvent[], + ): Array<{ agentId: string; costCents: number; eventCount: number }> { const map = new Map(); for (const e of events) { @@ -201,7 +223,12 @@ export class CostAnalyticsService { private buildForecast(events: FuelEvent[], fuelLimit: number): CostForecast { if (events.length < 2) { - return { projectedMonthlyCents: 0, burnRateCentsPerHour: 0, daysUntilExhausted: null, confidence: "low" }; + return { + projectedMonthlyCents: 0, + burnRateCentsPerHour: 0, + daysUntilExhausted: null, + confidence: "low", + }; } const sorted = [...events].sort( @@ -214,7 +241,12 @@ export class CostAnalyticsService { const hours = (lastTs - firstTs) / (1000 * 60 * 60); if (hours <= 0) { - return { projectedMonthlyCents: 0, burnRateCentsPerHour: 0, daysUntilExhausted: null, confidence: "low" }; + return { + projectedMonthlyCents: 0, + burnRateCentsPerHour: 0, + daysUntilExhausted: null, + confidence: "low", + }; } const burnRate = totalCents / hours; @@ -229,7 +261,12 @@ export class CostAnalyticsService { // Confidence based on data volume const confidence = events.length > 100 ? "high" : events.length > 20 ? "medium" : "low"; - return { projectedMonthlyCents: projectedMonthly, burnRateCentsPerHour: Math.round(burnRate), daysUntilExhausted, confidence }; + return { + projectedMonthlyCents: projectedMonthly, + burnRateCentsPerHour: Math.round(burnRate), + daysUntilExhausted, + confidence, + }; } private buildEfficiency(events: FuelEvent[]): CostEfficiency { diff --git a/extensions/kaneru/decision-history.test.ts b/extensions/kaneru/decision-history.test.ts index 503beeb0..4920a2b3 100644 --- a/extensions/kaneru/decision-history.test.ts +++ b/extensions/kaneru/decision-history.test.ts @@ -212,10 +212,7 @@ describe("DecisionHistory", () => { it("filters by ventureId", async () => { await history.record(makeConsensus(), { ventureId: "v-1" }); - await history.record( - makeConsensus({ id: "c-2" }), - { ventureId: "v-2" }, - ); + await history.record(makeConsensus({ id: "c-2" }), { ventureId: "v-2" }); const results = await history.query({ ventureId: "v-1" }); expect(results).toHaveLength(1); diff --git a/extensions/kaneru/decision-history.ts b/extensions/kaneru/decision-history.ts index 03957d01..34b71f65 100644 --- a/extensions/kaneru/decision-history.ts +++ b/extensions/kaneru/decision-history.ts @@ -80,12 +80,19 @@ function parseDecisionTriples( let votes: Record = {}; if (fields.votes) { - try { votes = JSON.parse(fields.votes) as Record; } catch { /* ignore */ } + try { + votes = JSON.parse(fields.votes) as Record; + } catch { + /* ignore */ + } } let participants: string[] = []; if (fields.participants) { - participants = fields.participants.split(",").map((s) => s.trim()).filter(Boolean); + participants = fields.participants + .split(",") + .map((s) => s.trim()) + .filter(Boolean); } return { @@ -170,10 +177,7 @@ export class DecisionHistory { } /** Query decisions with optional filters. */ - async query(opts?: { - ventureId?: string; - limit?: number; - }): Promise { + async query(opts?: { ventureId?: string; limit?: number }): Promise { // Query all decisions by finding subjects with the "question" predicate const result = await this.client.patternQuery({ predicate: decisionPredicate(this.ns, "question"), @@ -198,8 +202,8 @@ export class DecisionHistory { } // Sort by decidedAt descending (most recent first) - return decisions.sort((a, b) => - new Date(b.decidedAt).getTime() - new Date(a.decidedAt).getTime(), + return decisions.sort( + (a, b) => new Date(b.decidedAt).getTime() - new Date(a.decidedAt).getTime(), ); } diff --git a/extensions/kaneru/directives.ts b/extensions/kaneru/directives.ts index ded14aa6..75627bc7 100644 --- a/extensions/kaneru/directives.ts +++ b/extensions/kaneru/directives.ts @@ -68,7 +68,9 @@ function parseDirectiveTriples( if (pred.startsWith(prefix)) { const field = pred.slice(prefix.length); const val = - typeof t.object === "object" && t.object !== null && "node" in (t.object as Record) + typeof t.object === "object" && + t.object !== null && + "node" in (t.object as Record) ? stripBrackets(String((t.object as { node: string }).node)) : String(t.object); fields[field] = val; diff --git a/extensions/kaneru/distributed.test.ts b/extensions/kaneru/distributed.test.ts index e56ee50e..b11bdce5 100644 --- a/extensions/kaneru/distributed.test.ts +++ b/extensions/kaneru/distributed.test.ts @@ -46,10 +46,7 @@ function installFetchMock() { // POST /api/v1/dag/sync/pull if (urlStr.includes("/api/v1/dag/sync/pull") && method === "POST") { - return new Response( - JSON.stringify({ triples_added: 5, conflicts: 1 }), - { status: 200 }, - ); + return new Response(JSON.stringify({ triples_added: 5, conflicts: 1 }), { status: 200 }); } // POST /api/v1/query — pattern query diff --git a/extensions/kaneru/distributed.ts b/extensions/kaneru/distributed.ts index 5806f16c..93f46452 100644 --- a/extensions/kaneru/distributed.ts +++ b/extensions/kaneru/distributed.ts @@ -129,17 +129,16 @@ export class DistributedVentureManager { try { // Push local state to peer const pushResult = await this.client.dagSync({ - target: peer, - tips: localTips, + local_tips: localTips, }); - totalActionsSynced += pushResult.actions_sent ?? 0; + totalActionsSynced += pushResult.action_count ?? 0; // Pull remote state from peer const pullResult = await this.client.dagSyncPull({ - source: peer, + peer_url: peer, }); - totalTriplesAdded += pullResult.triples_added ?? 0; - totalConflicts += pullResult.conflicts ?? 0; + totalTriplesAdded += pullResult.ingested ?? 0; + totalConflicts += pullResult.already_had ?? 0; } catch { // Peer unreachable — skip silently } @@ -181,9 +180,8 @@ export class DistributedVentureManager { limit: 1, }); - const lastSyncAt = syncTriples.triples.length > 0 - ? String(syncTriples.triples[0].object) - : null; + const lastSyncAt = + syncTriples.triples.length > 0 ? String(syncTriples.triples[0].object) : null; return { ventureId, @@ -225,5 +223,4 @@ export class DistributedVentureManager { return newPeers; } - } diff --git a/extensions/kaneru/dojo.ts b/extensions/kaneru/dojo.ts index 38843895..80d24690 100644 --- a/extensions/kaneru/dojo.ts +++ b/extensions/kaneru/dojo.ts @@ -64,17 +64,27 @@ const BUNDLED_TEMPLATES: DojoTemplate[] = [ { id: "security-audit", name: "Security Audit Squad", - description: "Three-agent squad for comprehensive security auditing: scanner finds vulnerabilities, reviewer triages severity, fixer implements patches.", + description: + "Three-agent squad for comprehensive security auditing: scanner finds vulnerabilities, reviewer triages severity, fixer implements patches.", version: "1.0.0", agents: [ { agentId: "scanner", role: "Vulnerability Scanner", pulseInterval: "4h" }, - { agentId: "reviewer", role: "Security Reviewer", escalatesTo: "scanner", pulseInterval: "2h" }, + { + agentId: "reviewer", + role: "Security Reviewer", + escalatesTo: "scanner", + pulseInterval: "2h", + }, { agentId: "fixer", role: "Patch Author", escalatesTo: "reviewer", pulseInterval: "1h" }, ], directives: [ { title: "Maintain secure codebase", level: "strategic" }, { title: "Identify and classify vulnerabilities", level: "objective", parentIndex: 0 }, - { title: "Remediate critical and high severity findings", level: "objective", parentIndex: 0 }, + { + title: "Remediate critical and high severity findings", + level: "objective", + parentIndex: 0, + }, { title: "Run OWASP Top 10 scan", level: "task", parentIndex: 1 }, { title: "Review dependency audit results", level: "task", parentIndex: 1 }, { title: "Patch critical vulnerabilities", level: "task", parentIndex: 2 }, @@ -84,7 +94,8 @@ const BUNDLED_TEMPLATES: DojoTemplate[] = [ { id: "content-pipeline", name: "Content Pipeline", - description: "Writer-editor-publisher pipeline for content creation with review gates and quality checks.", + description: + "Writer-editor-publisher pipeline for content creation with review gates and quality checks.", version: "1.0.0", agents: [ { agentId: "writer", role: "Content Writer", pulseInterval: "6h" }, @@ -104,12 +115,23 @@ const BUNDLED_TEMPLATES: DojoTemplate[] = [ { id: "devops-squad", name: "DevOps Squad", - description: "Deploy-monitor-respond cycle for infrastructure operations with automated alerting and incident response.", + description: + "Deploy-monitor-respond cycle for infrastructure operations with automated alerting and incident response.", version: "1.0.0", agents: [ { agentId: "deployer", role: "Release Engineer", pulseInterval: "1h" }, - { agentId: "monitor", role: "Observability Agent", escalatesTo: "deployer", pulseInterval: "30m" }, - { agentId: "responder", role: "Incident Responder", escalatesTo: "monitor", pulseInterval: "15m" }, + { + agentId: "monitor", + role: "Observability Agent", + escalatesTo: "deployer", + pulseInterval: "30m", + }, + { + agentId: "responder", + role: "Incident Responder", + escalatesTo: "monitor", + pulseInterval: "15m", + }, ], directives: [ { title: "Maintain reliable infrastructure", level: "strategic" }, @@ -189,7 +211,10 @@ export class DojoService { * Uses the existing HubClient from skill-hub extension — same client * used by `mayros hub search`. No duplicate marketplace. */ - async searchHub(query?: string, hubUrl?: string): Promise> { + async searchHub( + query?: string, + hubUrl?: string, + ): Promise> { const hub = await this.resolveHubClient(hubUrl); if (!hub) return []; @@ -210,9 +235,14 @@ export class DojoService { * Download and install a template from the Skill Hub (hub.apilium.com). * Fetches the template archive, parses as DojoTemplate JSON, and installs. */ - async installFromHub(slug: string, ventureName: string, hubUrl?: string): Promise { + async installFromHub( + slug: string, + ventureName: string, + hubUrl?: string, + ): Promise { const hub = await this.resolveHubClient(hubUrl); - if (!hub) throw new Error("Skill Hub extension not available. Install the skill-hub plugin first."); + if (!hub) + throw new Error("Skill Hub extension not available. Install the skill-hub plugin first."); const archive = await hub.download(slug); @@ -231,7 +261,9 @@ export class DojoService { throw new Error(`Template "${slug}" has too many agents (${template.agents.length}, max 20)`); } if (template.directives.length > 50) { - throw new Error(`Template "${slug}" has too many directives (${template.directives.length}, max 50)`); + throw new Error( + `Template "${slug}" has too many directives (${template.directives.length}, max 50)`, + ); } // Validate agent IDs are alphanumeric for (const agent of template.agents) { @@ -247,7 +279,15 @@ export class DojoService { * Resolve HubClient from skill-hub extension. * Uses hub.apilium.com as default (same as `mayros hub` commands). */ - private async resolveHubClient(hubUrl?: string): Promise<{ search: (q: string, opts?: { category?: string; limit?: number }) => Promise<{ skills: Array<{ slug: string; name: string; description: string; version: string }> }>; download: (slug: string, version?: string) => Promise } | null> { + private async resolveHubClient(hubUrl?: string): Promise<{ + search: ( + q: string, + opts?: { category?: string; limit?: number }, + ) => Promise<{ + skills: Array<{ slug: string; name: string; description: string; version: string }>; + }>; + download: (slug: string, version?: string) => Promise; + } | null> { try { const { HubClient } = await import("../skill-hub/hub-client.js"); return new HubClient(hubUrl ?? "https://hub.apilium.com"); @@ -257,7 +297,10 @@ export class DojoService { } /** Internal: install a parsed DojoTemplate. Shared by install() and installFromHub(). */ - private async installTemplate(template: DojoTemplate, ventureName: string): Promise { + private async installTemplate( + template: DojoTemplate, + ventureName: string, + ): Promise { const venture = await this.ventureManager.create({ name: ventureName, directive: template.directives[0]?.title ?? template.description, diff --git a/extensions/kaneru/fuel.ts b/extensions/kaneru/fuel.ts index 6c650a27..22f93f2d 100644 --- a/extensions/kaneru/fuel.ts +++ b/extensions/kaneru/fuel.ts @@ -87,7 +87,9 @@ function parseFuelTriples( if (pred.startsWith(prefix)) { const field = pred.slice(prefix.length); const val = - typeof t.object === "object" && t.object !== null && "node" in (t.object as Record) + typeof t.object === "object" && + t.object !== null && + "node" in (t.object as Record) ? stripBrackets(String((t.object as { node: string }).node)) : String(t.object); fields[field] = val; @@ -233,10 +235,7 @@ export class FuelController { } /** Check if agent fuel limit is exceeded within a venture context. */ - async checkAgentLimit( - agentId: string, - agentFuelLimit: number, - ): Promise { + async checkAgentLimit(agentId: string, agentFuelLimit: number): Promise { const totalSpent = await this.agentSpend(agentId); return { diff --git a/extensions/kaneru/knowledge-transfer.test.ts b/extensions/kaneru/knowledge-transfer.test.ts index 66f76865..0a17ae3d 100644 --- a/extensions/kaneru/knowledge-transfer.test.ts +++ b/extensions/kaneru/knowledge-transfer.test.ts @@ -171,7 +171,11 @@ describe("KnowledgeTransferService", () => { const result = await svc.transferOnComplete("agent-1", "m-1"); - expect(fusion.merge).toHaveBeenCalledWith("private:agent-1", "test:shared:venture", "additive"); + expect(fusion.merge).toHaveBeenCalledWith( + "private:agent-1", + "test:shared:venture", + "additive", + ); expect(result.sourceAgent).toBe("agent-1"); expect(result.missionId).toBe("m-1"); expect(result.triplesTransferred).toBe(15); @@ -237,11 +241,7 @@ describe("KnowledgeTransferService", () => { // Default maxTriples is 100, so 200 should be capped expect(result.triplesTransferred).toBe(100); - expect(fusion.merge).toHaveBeenCalledWith( - expect.any(String), - expect.any(String), - "additive", - ); + expect(fusion.merge).toHaveBeenCalledWith(expect.any(String), expect.any(String), "additive"); }); }); }); diff --git a/extensions/kaneru/knowledge-transfer.ts b/extensions/kaneru/knowledge-transfer.ts index e075a377..e61ebc61 100644 --- a/extensions/kaneru/knowledge-transfer.ts +++ b/extensions/kaneru/knowledge-transfer.ts @@ -119,8 +119,8 @@ export class KnowledgeTransferService { if (transfer) transfers.push(transfer); } - return transfers.sort((a, b) => - new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime(), + return transfers.sort( + (a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime(), ); } diff --git a/extensions/kaneru/learning-profiles.ts b/extensions/kaneru/learning-profiles.ts index 1428beef..c6e93bb7 100644 --- a/extensions/kaneru/learning-profiles.ts +++ b/extensions/kaneru/learning-profiles.ts @@ -104,9 +104,10 @@ export class LearningProfileManager { /** Record a mission outcome and update the agent's learning profile. */ async recordOutcome(outcome: MissionOutcome): Promise { - const { domain, taskType } = outcome.domain && outcome.taskType - ? { domain: outcome.domain, taskType: outcome.taskType } - : classifyMission(outcome.title); + const { domain, taskType } = + outcome.domain && outcome.taskType + ? { domain: outcome.domain, taskType: outcome.taskType } + : classifyMission(outcome.title); const existing = await this.getProfile(outcome.agentId, domain, taskType); @@ -152,7 +153,11 @@ export class LearningProfileManager { } /** Get a specific learning profile. */ - async getProfile(agentId: string, domain: string, taskType: string): Promise { + async getProfile( + agentId: string, + domain: string, + taskType: string, + ): Promise { const subject = profileSubject(this.ns, agentId, domain, taskType); const result = await this.client.listTriples({ subject, limit: 20 }); return parseProfileTriples(this.ns, agentId, domain, taskType, result.triples); @@ -218,9 +223,7 @@ export class LearningProfileManager { if (profile) profiles.push(profile); } - return profiles - .sort((a, b) => b.expertise - a.expertise) - .slice(0, limit); + return profiles.sort((a, b) => b.expertise - a.expertise).slice(0, limit); } // ---------- Field helpers ---------- diff --git a/extensions/kaneru/mission-comments.ts b/extensions/kaneru/mission-comments.ts index 9875f19b..1e52a3fd 100644 --- a/extensions/kaneru/mission-comments.ts +++ b/extensions/kaneru/mission-comments.ts @@ -91,8 +91,8 @@ export class MissionCommentService { if (comment) comments.push(comment); } - return comments.sort((a, b) => - new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime(), + return comments.sort( + (a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime(), ); } diff --git a/extensions/kaneru/mission.ts b/extensions/kaneru/mission.ts index f6a7b151..0f64879d 100644 --- a/extensions/kaneru/mission.ts +++ b/extensions/kaneru/mission.ts @@ -92,7 +92,9 @@ function parseMissionTriples( if (pred.startsWith(prefix)) { const field = pred.slice(prefix.length); const val = - typeof t.object === "object" && t.object !== null && "node" in (t.object as Record) + typeof t.object === "object" && + t.object !== null && + "node" in (t.object as Record) ? stripBrackets(String((t.object as { node: string }).node)) : String(t.object); fields[field] = val; @@ -111,7 +113,8 @@ function parseMissionTriples( if (ventureId.startsWith(venturePrefix)) ventureId = ventureId.slice(venturePrefix.length); let directiveId: string | null = fields.directive ?? null; - if (directiveId?.startsWith(directivePrefix)) directiveId = directiveId.slice(directivePrefix.length); + if (directiveId?.startsWith(directivePrefix)) + directiveId = directiveId.slice(directivePrefix.length); let parentId: string | null = fields.parent ?? null; if (parentId?.startsWith(missionPrefix)) parentId = parentId.slice(missionPrefix.length); @@ -281,7 +284,11 @@ export class MissionManager { // Wrong status for claiming if (mission.status !== "ready") { // Allow stale adoption if active + same agent - if (mission.status === "active" && mission.claimedBy === agentId && mission.claimRun !== runId) { + if ( + mission.status === "active" && + mission.claimedBy === agentId && + mission.claimRun !== runId + ) { return this.adoptClaim(mission, agentId, runId); } return { ok: false, reason: "wrong_status" }; diff --git a/extensions/kaneru/project.ts b/extensions/kaneru/project.ts index 01a57bd6..731066ce 100644 --- a/extensions/kaneru/project.ts +++ b/extensions/kaneru/project.ts @@ -66,7 +66,9 @@ function parseProjectTriples( const pred = stripBrackets(String(t.predicate)); if (pred.startsWith(prefix)) { const val = - typeof t.object === "object" && t.object !== null && "node" in (t.object as Record) + typeof t.object === "object" && + t.object !== null && + "node" in (t.object as Record) ? stripBrackets(String((t.object as { node: string }).node)) : String(t.object); fields[pred.slice(prefix.length)] = val; @@ -178,7 +180,12 @@ export class ProjectManager { } /** Update project fields. */ - async update(id: string, patch: Partial>): Promise { + async update( + id: string, + patch: Partial< + Pick + >, + ): Promise { const project = await this.get(id); if (!project) throw new Error(`Project not found: ${id}`); @@ -189,9 +196,11 @@ export class ProjectManager { if (patch.name !== undefined) updates.push(["name", sanitizeTripleValue(patch.name)]); if (patch.owner !== undefined) updates.push(["owner", sanitizeTripleValue(patch.owner ?? "")]); if (patch.status !== undefined) updates.push(["status", patch.status]); - if (patch.targetDate !== undefined) updates.push(["targetDate", patch.targetDate]); - if (patch.category !== undefined) updates.push(["category", sanitizeTripleValue(patch.category)]); - if (patch.description !== undefined) updates.push(["description", sanitizeTripleValue(patch.description)]); + if (patch.targetDate !== undefined) updates.push(["targetDate", patch.targetDate ?? ""]); + if (patch.category !== undefined) + updates.push(["category", sanitizeTripleValue(patch.category)]); + if (patch.description !== undefined) + updates.push(["description", sanitizeTripleValue(patch.description)]); for (const [field, value] of updates) { const existing = await this.client.listTriples({ diff --git a/extensions/kaneru/pulse.ts b/extensions/kaneru/pulse.ts index e9c0e2ec..b4f0c14a 100644 --- a/extensions/kaneru/pulse.ts +++ b/extensions/kaneru/pulse.ts @@ -81,7 +81,9 @@ function parsePulseTriples( if (pred.startsWith(prefix)) { const field = pred.slice(prefix.length); const val = - typeof t.object === "object" && t.object !== null && "node" in (t.object as Record) + typeof t.object === "object" && + t.object !== null && + "node" in (t.object as Record) ? stripBrackets(String((t.object as { node: string }).node)) : String(t.object); fields[field] = val; @@ -326,7 +328,8 @@ export class PulseScheduler { predicate: pulsePredicate(this.ns, "claimRun"), limit: 1, }); - const claimRun = claimTriples.triples.length > 0 ? String(claimTriples.triples[0].object) : null; + const claimRun = + claimTriples.triples.length > 0 ? String(claimTriples.triples[0].object) : null; if (claimRun !== runId) { throw new Error("Only the claiming run can finish/fail this pulse"); } diff --git a/extensions/kaneru/venture.test.ts b/extensions/kaneru/venture.test.ts index 2e91543e..ded062f8 100644 --- a/extensions/kaneru/venture.test.ts +++ b/extensions/kaneru/venture.test.ts @@ -128,7 +128,12 @@ describe("VentureManager", () => { describe("create", () => { it("creates venture with correct fields", async () => { - const v = await mgr.create({ name: "Alpha", directive: "Build stuff", prefix: "alp", fuelLimit: 5000 }); + const v = await mgr.create({ + name: "Alpha", + directive: "Build stuff", + prefix: "alp", + fuelLimit: 5000, + }); expect(v.name).toBe("Alpha"); expect(v.directive).toBe("Build stuff"); expect(v.fuelLimit).toBe(5000); @@ -163,9 +168,9 @@ describe("VentureManager", () => { it("rejects duplicate prefix", async () => { await mgr.create({ name: "First", directive: "d", prefix: "DUP" }); - await expect( - mgr.create({ name: "Second", directive: "d", prefix: "dup" }), - ).rejects.toThrow("already in use"); + await expect(mgr.create({ name: "Second", directive: "d", prefix: "dup" })).rejects.toThrow( + "already in use", + ); }); }); diff --git a/extensions/kaneru/venture.ts b/extensions/kaneru/venture.ts index 86f39de1..14c5688a 100644 --- a/extensions/kaneru/venture.ts +++ b/extensions/kaneru/venture.ts @@ -64,7 +64,9 @@ function parseVentureTriples( if (pred.startsWith(prefix)) { const field = pred.slice(prefix.length); const val = - typeof t.object === "object" && t.object !== null && "node" in (t.object as Record) + typeof t.object === "object" && + t.object !== null && + "node" in (t.object as Record) ? stripBrackets(String((t.object as { node: string }).node)) : String(t.object); fields[field] = val; @@ -178,7 +180,9 @@ export class VentureManager { // Check prefix uniqueness if changing prefix if (patch.prefix !== undefined && patch.prefix.toUpperCase() !== venture.prefix) { const existing = await this.list(); - if (existing.some((v) => v.id !== id && v.prefix.toUpperCase() === patch.prefix!.toUpperCase())) { + if ( + existing.some((v) => v.id !== id && v.prefix.toUpperCase() === patch.prefix!.toUpperCase()) + ) { throw new Error(`Venture prefix "${patch.prefix}" is already in use`); } } @@ -189,7 +193,8 @@ export class VentureManager { const updates: Array<[string, string | number]> = [["updatedAt", now]]; if (patch.name !== undefined) updates.push(["name", sanitizeTripleValue(patch.name)]); - if (patch.directive !== undefined) updates.push(["directive", sanitizeTripleValue(patch.directive)]); + if (patch.directive !== undefined) + updates.push(["directive", sanitizeTripleValue(patch.directive)]); if (patch.fuelLimit !== undefined) updates.push(["fuelLimit", patch.fuelLimit]); if (patch.prefix !== undefined) updates.push(["prefix", patch.prefix.toUpperCase()]); diff --git a/extensions/mamoru/api-keys.test.ts b/extensions/mamoru/api-keys.test.ts index ca57fc61..918932d5 100644 --- a/extensions/mamoru/api-keys.test.ts +++ b/extensions/mamoru/api-keys.test.ts @@ -15,7 +15,12 @@ function createMockClient(): CortexClientLike & { createTriple: vi.fn(async (req) => { const id = `t-${nextId++}`; - const triple = { id, subject: req.subject, predicate: req.predicate, object: String(req.object) }; + const triple = { + id, + subject: req.subject, + predicate: req.predicate, + object: String(req.object), + }; triples.push(triple); return triple; }), diff --git a/extensions/mamoru/api-keys.ts b/extensions/mamoru/api-keys.ts index 4200f51f..e7c87f28 100644 --- a/extensions/mamoru/api-keys.ts +++ b/extensions/mamoru/api-keys.ts @@ -17,9 +17,9 @@ export type ApiKey = { id: string; name: string; agentId: string; - keyHash: string; // SHA-256 hash of the key (never store plaintext) - prefix: string; // first 8 chars for identification (e.g., "mk_a1b2c3d4") - scopes: string[]; // e.g., ["read", "write", "execute"] + keyHash: string; // SHA-256 hash of the key (never store plaintext) + prefix: string; // first 8 chars for identification (e.g., "mk_a1b2c3d4") + scopes: string[]; // e.g., ["read", "write", "execute"] createdAt: string; lastUsedAt: string | null; revokedAt: string | null; @@ -28,7 +28,7 @@ export type ApiKey = { export type ApiKeyCreateResult = { key: ApiKey; - plaintext: string; // shown ONCE at creation, never stored + plaintext: string; // shown ONCE at creation, never stored }; // ── Helpers ────────────────────────────────────────────────────────────── @@ -131,11 +131,17 @@ export class MamoruApiKeys { // Rollback: delete any triples that were created for (const pred of created) { try { - const existing = await this.client.listTriples({ subject: sub, predicate: pred, limit: 1 }); + const existing = await this.client.listTriples({ + subject: sub, + predicate: pred, + limit: 1, + }); for (const t of existing.triples) { if (t.id) await this.client.deleteTriple(t.id); } - } catch { /* best-effort cleanup */ } + } catch { + /* best-effort cleanup */ + } } throw err; } @@ -208,7 +214,11 @@ export class MamoruApiKeys { // Update lastUsedAt — delete old triple first to avoid duplication const now = new Date().toISOString(); - const existing = await this.client.listTriples({ subject, predicate: this.predicate(PRED.lastUsedAt), limit: 1 }); + const existing = await this.client.listTriples({ + subject, + predicate: this.predicate(PRED.lastUsedAt), + limit: 1, + }); for (const t of existing.triples) { if (t.id) await this.client.deleteTriple(t.id); } diff --git a/extensions/mamoru/egress-gate.test.ts b/extensions/mamoru/egress-gate.test.ts index d1dd7692..88024b26 100644 --- a/extensions/mamoru/egress-gate.test.ts +++ b/extensions/mamoru/egress-gate.test.ts @@ -155,7 +155,10 @@ describe("MamoruGate", () => { expect(recheck.allowed).toBe(true); // Different method should not auto-approve via session - const diffMethod = gate.checkEgress("api.example.com", 443, { method: "DELETE", path: "/users" }); + const diffMethod = gate.checkEgress("api.example.com", 443, { + method: "DELETE", + path: "/users", + }); expect(diffMethod.allowed).toBe(false); }); }); diff --git a/extensions/mamoru/egress-gate.ts b/extensions/mamoru/egress-gate.ts index 370846e7..321aec96 100644 --- a/extensions/mamoru/egress-gate.ts +++ b/extensions/mamoru/egress-gate.ts @@ -46,22 +46,14 @@ const PRESETS: Record = { { host: "api.github.com", port: 443, protocol: "https" }, { host: "github.com", port: 443, protocol: "https" }, ], - npm: [ - { host: "registry.npmjs.org", port: 443, protocol: "https", methods: ["GET"] }, - ], + npm: [{ host: "registry.npmjs.org", port: 443, protocol: "https", methods: ["GET"] }], pypi: [ { host: "pypi.org", port: 443, protocol: "https", methods: ["GET"] }, { host: "files.pythonhosted.org", port: 443, protocol: "https", methods: ["GET"] }, ], - anthropic: [ - { host: "api.anthropic.com", port: 443, protocol: "https" }, - ], - openai: [ - { host: "api.openai.com", port: 443, protocol: "https" }, - ], - google: [ - { host: "generativelanguage.googleapis.com", port: 443, protocol: "https" }, - ], + anthropic: [{ host: "api.anthropic.com", port: 443, protocol: "https" }], + openai: [{ host: "api.openai.com", port: 443, protocol: "https" }], + google: [{ host: "generativelanguage.googleapis.com", port: 443, protocol: "https" }], huggingface: [ { host: "huggingface.co", port: 443, protocol: "https" }, { host: "cdn-lfs.huggingface.co", port: 443, protocol: "https" }, @@ -74,15 +66,9 @@ const PRESETS: Record = { { host: "discord.com", port: 443, protocol: "https" }, { host: "discordapp.com", port: 443, protocol: "https" }, ], - telegram: [ - { host: "api.telegram.org", port: 443, protocol: "https" }, - ], - cortex: [ - { host: "127.0.0.1", port: 19090, protocol: "http" }, - ], - hub: [ - { host: "hub.apilium.com", port: 443, protocol: "https" }, - ], + telegram: [{ host: "api.telegram.org", port: 443, protocol: "https" }], + cortex: [{ host: "127.0.0.1", port: 19090, protocol: "http" }], + hub: [{ host: "hub.apilium.com", port: 443, protocol: "https" }], }; const PRESET_DESCRIPTIONS: Record = { @@ -293,9 +279,7 @@ export class MamoruGate { * Add an explicit egress rule. */ addRule(rule: EgressRule): void { - const exists = this.policy.rules.some( - (r) => r.host === rule.host && r.port === rule.port, - ); + const exists = this.policy.rules.some((r) => r.host === rule.host && r.port === rule.port); if (!exists) { this.policy.rules.push({ ...rule }); } @@ -305,9 +289,7 @@ export class MamoruGate { * Remove an egress rule by host and port. */ removeRule(host: string, port: number): void { - this.policy.rules = this.policy.rules.filter( - (r) => !(r.host === host && r.port === port), - ); + this.policy.rules = this.policy.rules.filter((r) => !(r.host === host && r.port === port)); } /** @@ -345,7 +327,8 @@ export class MamoruGate { if (isPrivateIP(hostname)) { // Allow explicit cortex preset if (hostname === "127.0.0.1" && parsed.port === "19090") { - const hasCortex = this.policy.presets.includes("cortex") || + const hasCortex = + this.policy.presets.includes("cortex") || this.policy.rules.some((r) => r.host === "127.0.0.1" && r.port === 19090); if (hasCortex) { return { safe: true }; @@ -429,19 +412,19 @@ function isPrivateIP(ip: string): boolean { if (v4Parts.length === 4) { const first = parseInt(v4Parts[0]!, 10); const second = parseInt(v4Parts[1]!, 10); - if (first === 127) return true; // 127.0.0.0/8 - if (first === 10) return true; // 10.0.0.0/8 + if (first === 127) return true; // 127.0.0.0/8 + if (first === 10) return true; // 10.0.0.0/8 if (first === 172 && second >= 16 && second <= 31) return true; // 172.16.0.0/12 - if (first === 192 && second === 168) return true; // 192.168.0.0/16 - if (first === 169 && second === 254) return true; // 169.254.0.0/16 (link-local + metadata) - if (first === 0) return true; // 0.0.0.0/8 + if (first === 192 && second === 168) return true; // 192.168.0.0/16 + if (first === 169 && second === 254) return true; // 169.254.0.0/16 (link-local + metadata) + if (first === 0) return true; // 0.0.0.0/8 } // IPv6 checks const lowerIp = clean.toLowerCase(); if (lowerIp === "::1" || lowerIp === "0:0:0:0:0:0:0:1") return true; if (lowerIp.startsWith("fd") || lowerIp.startsWith("fc")) return true; // fd00::/8, fc00::/7 - if (lowerIp.startsWith("fe80")) return true; // link-local + if (lowerIp.startsWith("fe80")) return true; // link-local if (lowerIp === "::") return true; // localhost alias diff --git a/extensions/mamoru/eruberu-proxy.ts b/extensions/mamoru/eruberu-proxy.ts index d1cba52c..4ae3ecd9 100644 --- a/extensions/mamoru/eruberu-proxy.ts +++ b/extensions/mamoru/eruberu-proxy.ts @@ -118,9 +118,7 @@ export class EruberuProxy { constructor(private readonly ns: string) { this.policy = { ...DEFAULT_POLICY }; - this.profiles = new Map( - Object.entries(BUILT_IN_PROFILES).map(([k, v]) => [k, { ...v }]), - ); + this.profiles = new Map(Object.entries(BUILT_IN_PROFILES).map(([k, v]) => [k, { ...v }])); } /** @@ -180,10 +178,7 @@ export class EruberuProxy { /** * Check whether a request to a given provider/model is allowed by policy. */ - checkPolicy( - provider: string, - model: string, - ): { allowed: boolean; reason?: string } { + checkPolicy(provider: string, model: string): { allowed: boolean; reason?: string } { // Check provider if ( this.policy.allowedProviders.length > 0 && @@ -196,13 +191,8 @@ export class EruberuProxy { } // Check model against glob patterns - if ( - this.policy.allowedModels.length > 0 && - !this.policy.allowedModels.includes("*") - ) { - const modelAllowed = this.policy.allowedModels.some((pattern) => - matchGlob(pattern, model), - ); + if (this.policy.allowedModels.length > 0 && !this.policy.allowedModels.includes("*")) { + const modelAllowed = this.policy.allowedModels.some((pattern) => matchGlob(pattern, model)); if (!modelAllowed) { return { allowed: false, @@ -217,9 +207,7 @@ export class EruberuProxy { /** * Log an inference request. Maintains a ring buffer of max 1000 entries. */ - logRequest( - entry: Omit, - ): InferenceLog { + logRequest(entry: Omit): InferenceLog { const log: InferenceLog = { ...entry, id: randomUUID(), diff --git a/extensions/mamoru/index.ts b/extensions/mamoru/index.ts index babfc4d0..169a2866 100644 --- a/extensions/mamoru/index.ts +++ b/extensions/mamoru/index.ts @@ -26,10 +26,20 @@ */ export { MamoruSandbox } from "./sandbox.js"; -export type { SandboxPolicy, SandboxStatus, SandboxAvailability, SandboxApplyResult } from "./sandbox.js"; +export type { + SandboxPolicy, + SandboxStatus, + SandboxAvailability, + SandboxApplyResult, +} from "./sandbox.js"; export { EruberuProxy } from "./eruberu-proxy.js"; -export type { InferenceProfile, InferenceLog, InferencePolicy, UsageSummary } from "./eruberu-proxy.js"; +export type { + InferenceProfile, + InferenceLog, + InferencePolicy, + UsageSummary, +} from "./eruberu-proxy.js"; export { MamoruGate } from "./egress-gate.js"; export type { EgressRule, EgressPolicy, EgressRequest } from "./egress-gate.js"; diff --git a/extensions/mamoru/local-model.test.ts b/extensions/mamoru/local-model.test.ts index 20733185..0d4e65a8 100644 --- a/extensions/mamoru/local-model.test.ts +++ b/extensions/mamoru/local-model.test.ts @@ -174,7 +174,16 @@ describe("LocalModelSetup", () => { coveredActivities.add(a); } } - const allActivities: ModelActivity[] = ["coding", "chat", "reasoning", "creative", "analysis", "multilingual", "vision", "agents"]; + const allActivities: ModelActivity[] = [ + "coding", + "chat", + "reasoning", + "creative", + "analysis", + "multilingual", + "vision", + "agents", + ]; for (const a of allActivities) { expect(coveredActivities.has(a)).toBe(true); } diff --git a/extensions/mamoru/local-model.ts b/extensions/mamoru/local-model.ts index 501dddf4..2ee3c774 100644 --- a/extensions/mamoru/local-model.ts +++ b/extensions/mamoru/local-model.ts @@ -52,29 +52,40 @@ export type ModelSuggestion = { // ── Activity-segmented model types ────────────────────────────────────── export type ModelActivity = - | "coding" // code generation, debugging, refactoring - | "chat" // general conversation, Q&A - | "reasoning" // complex logic, math, planning - | "creative" // writing, storytelling, content - | "analysis" // data analysis, research, summarization + | "coding" // code generation, debugging, refactoring + | "chat" // general conversation, Q&A + | "reasoning" // complex logic, math, planning + | "creative" // writing, storytelling, content + | "analysis" // data analysis, research, summarization | "multilingual" // translation, cross-language tasks - | "vision" // image understanding (multimodal) - | "agents"; // autonomous agent tasks, tool use + | "vision" // image understanding (multimodal) + | "agents"; // autonomous agent tasks, tool use export type ModelTier = "small" | "medium" | "large" | "xlarge"; export type CatalogModel = { - id: string; // ollama/nim model ID - name: string; // human-readable name - provider: "meta" | "nvidia" | "mistral" | "google" | "deepseek" | "qwen" | "microsoft" | "cohere" | "ibm" | "01ai" | "upstage"; - activities: ModelActivity[]; // what it's good at + id: string; // ollama/nim model ID + name: string; // human-readable name + provider: + | "meta" + | "nvidia" + | "mistral" + | "google" + | "deepseek" + | "qwen" + | "microsoft" + | "cohere" + | "ibm" + | "01ai" + | "upstage"; + activities: ModelActivity[]; // what it's good at tier: ModelTier; - parameters: string; // "3B", "8B", "70B", etc. - vramRequired: number; // MB - contextLength: number; // tokens - quantization: string; // "Q4_K_M", "Q8_0", "FP16" + parameters: string; // "3B", "8B", "70B", etc. + vramRequired: number; // MB + contextLength: number; // tokens + quantization: string; // "Q4_K_M", "Q8_0", "FP16" runtime: "ollama" | "vllm" | "nim"; - strengths: string; // 1-line description + strengths: string; // 1-line description }; export type ActivityDescription = { @@ -86,87 +97,707 @@ export type ActivityDescription = { // ── Activity descriptions ──────────────────────────────────────────────── const ACTIVITY_DESCRIPTIONS: ActivityDescription[] = [ - { activity: "coding", label: "Coding", description: "Code generation, debugging, refactoring, and completion" }, - { activity: "chat", label: "Chat", description: "General conversation, Q&A, and instruction following" }, - { activity: "reasoning", label: "Reasoning", description: "Complex logic, math, planning, and chain-of-thought" }, - { activity: "creative", label: "Creative", description: "Writing, storytelling, content generation, and brainstorming" }, - { activity: "analysis", label: "Analysis", description: "Data analysis, research, summarization, and extraction" }, - { activity: "multilingual", label: "Multilingual", description: "Translation, cross-language tasks, and polyglot support" }, - { activity: "vision", label: "Vision", description: "Image understanding, visual QA, and multimodal reasoning" }, - { activity: "agents", label: "Agents", description: "Autonomous agent tasks, tool use, and function calling" }, + { + activity: "coding", + label: "Coding", + description: "Code generation, debugging, refactoring, and completion", + }, + { + activity: "chat", + label: "Chat", + description: "General conversation, Q&A, and instruction following", + }, + { + activity: "reasoning", + label: "Reasoning", + description: "Complex logic, math, planning, and chain-of-thought", + }, + { + activity: "creative", + label: "Creative", + description: "Writing, storytelling, content generation, and brainstorming", + }, + { + activity: "analysis", + label: "Analysis", + description: "Data analysis, research, summarization, and extraction", + }, + { + activity: "multilingual", + label: "Multilingual", + description: "Translation, cross-language tasks, and polyglot support", + }, + { + activity: "vision", + label: "Vision", + description: "Image understanding, visual QA, and multimodal reasoning", + }, + { + activity: "agents", + label: "Agents", + description: "Autonomous agent tasks, tool use, and function calling", + }, ]; // ── Model catalog (activity-segmented) ────────────────────────────────── const CATALOG: CatalogModel[] = [ // ── Tiny models (0-4GB VRAM) — run on any machine ───────────────── - { id: "qwen2.5-coder:1.5b", name: "Qwen 2.5 Coder 1.5B", provider: "qwen", activities: ["coding"], tier: "small", parameters: "1.5B", vramRequired: 0, contextLength: 32768, quantization: "Q4_K_M", runtime: "ollama", strengths: "Tiny but capable code model, runs on CPU" }, - { id: "qwen2.5:0.5b", name: "Qwen 2.5 0.5B", provider: "qwen", activities: ["chat"], tier: "small", parameters: "0.5B", vramRequired: 0, contextLength: 32768, quantization: "Q4_K_M", runtime: "ollama", strengths: "Smallest Qwen, ultra-fast, edge devices" }, - { id: "tinyllama:1.1b", name: "TinyLlama 1.1B", provider: "meta", activities: ["chat"], tier: "small", parameters: "1.1B", vramRequired: 0, contextLength: 2048, quantization: "Q4_K_M", runtime: "ollama", strengths: "Ultra-light, instant responses on any hardware" }, - { id: "gemma2:2b", name: "Gemma 2 2B", provider: "google", activities: ["chat"], tier: "small", parameters: "2B", vramRequired: 0, contextLength: 8192, quantization: "Q4_K_M", runtime: "ollama", strengths: "Google's tiny model, CPU friendly" }, - { id: "phi-3.5:3.8b", name: "Phi 3.5 Mini 3.8B", provider: "microsoft",activities: ["chat", "reasoning"], tier: "small", parameters: "3.8B", vramRequired: 3000, contextLength: 128000, quantization: "Q4_K_M", runtime: "ollama", strengths: "Microsoft's small powerhouse, 128K context" }, - { id: "deepseek-r1:1.5b", name: "DeepSeek R1 1.5B", provider: "deepseek", activities: ["reasoning"], tier: "small", parameters: "1.5B", vramRequired: 0, contextLength: 32768, quantization: "Q4_K_M", runtime: "ollama", strengths: "Chain-of-thought reasoning on CPU" }, - { id: "qwen2.5:3b", name: "Qwen 2.5 3B", provider: "qwen", activities: ["multilingual", "chat"], tier: "small", parameters: "3B", vramRequired: 0, contextLength: 32768, quantization: "Q4_K_M", runtime: "ollama", strengths: "Multilingual on CPU, CJK + Latin languages" }, - { id: "moondream:1.8b", name: "Moondream 1.8B", provider: "meta", activities: ["vision"], tier: "small", parameters: "1.8B", vramRequired: 2000, contextLength: 2048, quantization: "Q4_K_M", runtime: "ollama", strengths: "Tiny vision model, image understanding" }, - { id: "smollm2:1.7b", name: "SmolLM2 1.7B", provider: "microsoft",activities: ["agents", "chat"], tier: "small", parameters: "1.7B", vramRequired: 0, contextLength: 8192, quantization: "Q4_K_M", runtime: "ollama", strengths: "Small but capable for simple agent tasks" }, + { + id: "qwen2.5-coder:1.5b", + name: "Qwen 2.5 Coder 1.5B", + provider: "qwen", + activities: ["coding"], + tier: "small", + parameters: "1.5B", + vramRequired: 0, + contextLength: 32768, + quantization: "Q4_K_M", + runtime: "ollama", + strengths: "Tiny but capable code model, runs on CPU", + }, + { + id: "qwen2.5:0.5b", + name: "Qwen 2.5 0.5B", + provider: "qwen", + activities: ["chat"], + tier: "small", + parameters: "0.5B", + vramRequired: 0, + contextLength: 32768, + quantization: "Q4_K_M", + runtime: "ollama", + strengths: "Smallest Qwen, ultra-fast, edge devices", + }, + { + id: "tinyllama:1.1b", + name: "TinyLlama 1.1B", + provider: "meta", + activities: ["chat"], + tier: "small", + parameters: "1.1B", + vramRequired: 0, + contextLength: 2048, + quantization: "Q4_K_M", + runtime: "ollama", + strengths: "Ultra-light, instant responses on any hardware", + }, + { + id: "gemma2:2b", + name: "Gemma 2 2B", + provider: "google", + activities: ["chat"], + tier: "small", + parameters: "2B", + vramRequired: 0, + contextLength: 8192, + quantization: "Q4_K_M", + runtime: "ollama", + strengths: "Google's tiny model, CPU friendly", + }, + { + id: "phi-3.5:3.8b", + name: "Phi 3.5 Mini 3.8B", + provider: "microsoft", + activities: ["chat", "reasoning"], + tier: "small", + parameters: "3.8B", + vramRequired: 3000, + contextLength: 128000, + quantization: "Q4_K_M", + runtime: "ollama", + strengths: "Microsoft's small powerhouse, 128K context", + }, + { + id: "deepseek-r1:1.5b", + name: "DeepSeek R1 1.5B", + provider: "deepseek", + activities: ["reasoning"], + tier: "small", + parameters: "1.5B", + vramRequired: 0, + contextLength: 32768, + quantization: "Q4_K_M", + runtime: "ollama", + strengths: "Chain-of-thought reasoning on CPU", + }, + { + id: "qwen2.5:3b", + name: "Qwen 2.5 3B", + provider: "qwen", + activities: ["multilingual", "chat"], + tier: "small", + parameters: "3B", + vramRequired: 0, + contextLength: 32768, + quantization: "Q4_K_M", + runtime: "ollama", + strengths: "Multilingual on CPU, CJK + Latin languages", + }, + { + id: "moondream:1.8b", + name: "Moondream 1.8B", + provider: "meta", + activities: ["vision"], + tier: "small", + parameters: "1.8B", + vramRequired: 2000, + contextLength: 2048, + quantization: "Q4_K_M", + runtime: "ollama", + strengths: "Tiny vision model, image understanding", + }, + { + id: "smollm2:1.7b", + name: "SmolLM2 1.7B", + provider: "microsoft", + activities: ["agents", "chat"], + tier: "small", + parameters: "1.7B", + vramRequired: 0, + contextLength: 8192, + quantization: "Q4_K_M", + runtime: "ollama", + strengths: "Small but capable for simple agent tasks", + }, // ── Coding models (5GB+) ───────────────────────────────────────── - { id: "codellama:7b", name: "Code Llama 7B", provider: "meta", activities: ["coding"], tier: "small", parameters: "7B", vramRequired: 5000, contextLength: 16384, quantization: "Q4_K_M", runtime: "ollama", strengths: "Fast code completion and infilling, low VRAM" }, - { id: "codellama:13b", name: "Code Llama 13B", provider: "meta", activities: ["coding"], tier: "medium", parameters: "13B", vramRequired: 10000, contextLength: 16384, quantization: "Q4_K_M", runtime: "ollama", strengths: "Strong code generation with better accuracy than 7B" }, - { id: "codellama:34b", name: "Code Llama 34B", provider: "meta", activities: ["coding", "reasoning"], tier: "large", parameters: "34B", vramRequired: 22000, contextLength: 16384, quantization: "Q4_K_M", runtime: "ollama", strengths: "Best Code Llama for complex code tasks" }, - { id: "deepseek-coder-v2:lite", name: "DeepSeek Coder V2 Lite", provider: "deepseek", activities: ["coding", "reasoning"], tier: "medium", parameters: "16B", vramRequired: 12000, contextLength: 128000, quantization: "Q4_K_M", runtime: "ollama", strengths: "MoE architecture, 128K context, strong on benchmarks" }, - { id: "deepseek-coder-v2", name: "DeepSeek Coder V2", provider: "deepseek", activities: ["coding", "reasoning", "analysis"], tier: "large", parameters: "236B", vramRequired: 22000, contextLength: 128000, quantization: "Q4_K_M", runtime: "ollama", strengths: "Top-tier code model with extended context window" }, - { id: "qwen2.5-coder:7b", name: "Qwen 2.5 Coder 7B", provider: "qwen", activities: ["coding"], tier: "small", parameters: "7B", vramRequired: 5000, contextLength: 32768, quantization: "Q4_K_M", runtime: "ollama", strengths: "Competitive with larger models on code tasks" }, - { id: "qwen2.5-coder:14b", name: "Qwen 2.5 Coder 14B", provider: "qwen", activities: ["coding", "agents"], tier: "medium", parameters: "14B", vramRequired: 10000, contextLength: 32768, quantization: "Q4_K_M", runtime: "ollama", strengths: "Strong code + tool-use capabilities" }, - { id: "qwen2.5-coder:32b", name: "Qwen 2.5 Coder 32B", provider: "qwen", activities: ["coding", "reasoning", "agents"], tier: "large", parameters: "32B", vramRequired: 22000, contextLength: 32768, quantization: "Q4_K_M", runtime: "ollama", strengths: "Best Qwen coder, rivals GPT-4 on code benchmarks" }, - { id: "starcoder2:7b", name: "StarCoder2 7B", provider: "microsoft",activities: ["coding"], tier: "small", parameters: "7B", vramRequired: 5000, contextLength: 16384, quantization: "Q4_K_M", runtime: "ollama", strengths: "Trained on The Stack v2, fast completions" }, - { id: "starcoder2:15b", name: "StarCoder2 15B", provider: "microsoft",activities: ["coding"], tier: "medium", parameters: "15B", vramRequired: 11000, contextLength: 16384, quantization: "Q4_K_M", runtime: "ollama", strengths: "Larger StarCoder2 with improved accuracy" }, + { + id: "codellama:7b", + name: "Code Llama 7B", + provider: "meta", + activities: ["coding"], + tier: "small", + parameters: "7B", + vramRequired: 5000, + contextLength: 16384, + quantization: "Q4_K_M", + runtime: "ollama", + strengths: "Fast code completion and infilling, low VRAM", + }, + { + id: "codellama:13b", + name: "Code Llama 13B", + provider: "meta", + activities: ["coding"], + tier: "medium", + parameters: "13B", + vramRequired: 10000, + contextLength: 16384, + quantization: "Q4_K_M", + runtime: "ollama", + strengths: "Strong code generation with better accuracy than 7B", + }, + { + id: "codellama:34b", + name: "Code Llama 34B", + provider: "meta", + activities: ["coding", "reasoning"], + tier: "large", + parameters: "34B", + vramRequired: 22000, + contextLength: 16384, + quantization: "Q4_K_M", + runtime: "ollama", + strengths: "Best Code Llama for complex code tasks", + }, + { + id: "deepseek-coder-v2:lite", + name: "DeepSeek Coder V2 Lite", + provider: "deepseek", + activities: ["coding", "reasoning"], + tier: "medium", + parameters: "16B", + vramRequired: 12000, + contextLength: 128000, + quantization: "Q4_K_M", + runtime: "ollama", + strengths: "MoE architecture, 128K context, strong on benchmarks", + }, + { + id: "deepseek-coder-v2", + name: "DeepSeek Coder V2", + provider: "deepseek", + activities: ["coding", "reasoning", "analysis"], + tier: "large", + parameters: "236B", + vramRequired: 22000, + contextLength: 128000, + quantization: "Q4_K_M", + runtime: "ollama", + strengths: "Top-tier code model with extended context window", + }, + { + id: "qwen2.5-coder:7b", + name: "Qwen 2.5 Coder 7B", + provider: "qwen", + activities: ["coding"], + tier: "small", + parameters: "7B", + vramRequired: 5000, + contextLength: 32768, + quantization: "Q4_K_M", + runtime: "ollama", + strengths: "Competitive with larger models on code tasks", + }, + { + id: "qwen2.5-coder:14b", + name: "Qwen 2.5 Coder 14B", + provider: "qwen", + activities: ["coding", "agents"], + tier: "medium", + parameters: "14B", + vramRequired: 10000, + contextLength: 32768, + quantization: "Q4_K_M", + runtime: "ollama", + strengths: "Strong code + tool-use capabilities", + }, + { + id: "qwen2.5-coder:32b", + name: "Qwen 2.5 Coder 32B", + provider: "qwen", + activities: ["coding", "reasoning", "agents"], + tier: "large", + parameters: "32B", + vramRequired: 22000, + contextLength: 32768, + quantization: "Q4_K_M", + runtime: "ollama", + strengths: "Best Qwen coder, rivals GPT-4 on code benchmarks", + }, + { + id: "starcoder2:7b", + name: "StarCoder2 7B", + provider: "microsoft", + activities: ["coding"], + tier: "small", + parameters: "7B", + vramRequired: 5000, + contextLength: 16384, + quantization: "Q4_K_M", + runtime: "ollama", + strengths: "Trained on The Stack v2, fast completions", + }, + { + id: "starcoder2:15b", + name: "StarCoder2 15B", + provider: "microsoft", + activities: ["coding"], + tier: "medium", + parameters: "15B", + vramRequired: 11000, + contextLength: 16384, + quantization: "Q4_K_M", + runtime: "ollama", + strengths: "Larger StarCoder2 with improved accuracy", + }, // ── Chat / General models ───────────────────────────────────────── - { id: "llama3.2:3b", name: "Llama 3.2 3B", provider: "meta", activities: ["chat"], tier: "small", parameters: "3B", vramRequired: 0, contextLength: 8192, quantization: "Q4_K_M", runtime: "ollama", strengths: "Lightweight, runs on CPU, fast responses" }, - { id: "llama3.1:8b", name: "Llama 3.1 8B", provider: "meta", activities: ["chat", "creative", "agents"], tier: "small", parameters: "8B", vramRequired: 6000, contextLength: 128000, quantization: "Q4_K_M", runtime: "ollama", strengths: "Excellent general model with function calling" }, - { id: "llama3.3:70b", name: "Llama 3.3 70B", provider: "meta", activities: ["chat", "reasoning", "analysis", "agents"], tier: "xlarge", parameters: "70B", vramRequired: 40000, contextLength: 128000, quantization: "Q4_K_M", runtime: "ollama", strengths: "Flagship Llama, near-frontier quality across all tasks" }, - { id: "mistral:7b", name: "Mistral 7B", provider: "mistral", activities: ["chat", "creative"], tier: "small", parameters: "7B", vramRequired: 6000, contextLength: 32768, quantization: "Q4_K_M", runtime: "ollama", strengths: "Fast, efficient, strong for its size" }, - { id: "mistral-nemo:12b", name: "Mistral Nemo 12B", provider: "mistral", activities: ["chat", "agents", "multilingual"], tier: "medium", parameters: "12B", vramRequired: 9000, contextLength: 128000, quantization: "Q4_K_M", runtime: "ollama", strengths: "128K context, function calling, multilingual" }, - { id: "mixtral:8x7b", name: "Mixtral 8x7B", provider: "mistral", activities: ["chat", "reasoning", "coding"], tier: "large", parameters: "47B", vramRequired: 28000, contextLength: 32768, quantization: "Q4_K_M", runtime: "ollama", strengths: "MoE with 8 experts, fast inference for its quality" }, - { id: "phi-4:14b", name: "Phi-4 14B", provider: "microsoft",activities: ["chat", "reasoning", "coding"], tier: "medium", parameters: "14B", vramRequired: 10000, contextLength: 16384, quantization: "Q4_K_M", runtime: "ollama", strengths: "Strong reasoning for its size, efficient architecture" }, - { id: "gemma2:9b", name: "Gemma 2 9B", provider: "google", activities: ["chat", "creative"], tier: "small", parameters: "9B", vramRequired: 7000, contextLength: 8192, quantization: "Q4_K_M", runtime: "ollama", strengths: "Google's efficient model, strong on benchmarks" }, - { id: "gemma2:27b", name: "Gemma 2 27B", provider: "google", activities: ["chat", "reasoning", "analysis"], tier: "large", parameters: "27B", vramRequired: 18000, contextLength: 8192, quantization: "Q4_K_M", runtime: "ollama", strengths: "Best Gemma, competitive with much larger models" }, + { + id: "llama3.2:3b", + name: "Llama 3.2 3B", + provider: "meta", + activities: ["chat"], + tier: "small", + parameters: "3B", + vramRequired: 0, + contextLength: 8192, + quantization: "Q4_K_M", + runtime: "ollama", + strengths: "Lightweight, runs on CPU, fast responses", + }, + { + id: "llama3.1:8b", + name: "Llama 3.1 8B", + provider: "meta", + activities: ["chat", "creative", "agents"], + tier: "small", + parameters: "8B", + vramRequired: 6000, + contextLength: 128000, + quantization: "Q4_K_M", + runtime: "ollama", + strengths: "Excellent general model with function calling", + }, + { + id: "llama3.3:70b", + name: "Llama 3.3 70B", + provider: "meta", + activities: ["chat", "reasoning", "analysis", "agents"], + tier: "xlarge", + parameters: "70B", + vramRequired: 40000, + contextLength: 128000, + quantization: "Q4_K_M", + runtime: "ollama", + strengths: "Flagship Llama, near-frontier quality across all tasks", + }, + { + id: "mistral:7b", + name: "Mistral 7B", + provider: "mistral", + activities: ["chat", "creative"], + tier: "small", + parameters: "7B", + vramRequired: 6000, + contextLength: 32768, + quantization: "Q4_K_M", + runtime: "ollama", + strengths: "Fast, efficient, strong for its size", + }, + { + id: "mistral-nemo:12b", + name: "Mistral Nemo 12B", + provider: "mistral", + activities: ["chat", "agents", "multilingual"], + tier: "medium", + parameters: "12B", + vramRequired: 9000, + contextLength: 128000, + quantization: "Q4_K_M", + runtime: "ollama", + strengths: "128K context, function calling, multilingual", + }, + { + id: "mixtral:8x7b", + name: "Mixtral 8x7B", + provider: "mistral", + activities: ["chat", "reasoning", "coding"], + tier: "large", + parameters: "47B", + vramRequired: 28000, + contextLength: 32768, + quantization: "Q4_K_M", + runtime: "ollama", + strengths: "MoE with 8 experts, fast inference for its quality", + }, + { + id: "phi-4:14b", + name: "Phi-4 14B", + provider: "microsoft", + activities: ["chat", "reasoning", "coding"], + tier: "medium", + parameters: "14B", + vramRequired: 10000, + contextLength: 16384, + quantization: "Q4_K_M", + runtime: "ollama", + strengths: "Strong reasoning for its size, efficient architecture", + }, + { + id: "gemma2:9b", + name: "Gemma 2 9B", + provider: "google", + activities: ["chat", "creative"], + tier: "small", + parameters: "9B", + vramRequired: 7000, + contextLength: 8192, + quantization: "Q4_K_M", + runtime: "ollama", + strengths: "Google's efficient model, strong on benchmarks", + }, + { + id: "gemma2:27b", + name: "Gemma 2 27B", + provider: "google", + activities: ["chat", "reasoning", "analysis"], + tier: "large", + parameters: "27B", + vramRequired: 18000, + contextLength: 8192, + quantization: "Q4_K_M", + runtime: "ollama", + strengths: "Best Gemma, competitive with much larger models", + }, // ── Reasoning models ────────────────────────────────────────────── - { id: "deepseek-r1:7b", name: "DeepSeek R1 7B", provider: "deepseek", activities: ["reasoning", "analysis"], tier: "small", parameters: "7B", vramRequired: 5000, contextLength: 32768, quantization: "Q4_K_M", runtime: "ollama", strengths: "Chain-of-thought reasoning at small scale" }, - { id: "deepseek-r1:14b", name: "DeepSeek R1 14B", provider: "deepseek", activities: ["reasoning", "analysis", "coding"], tier: "medium", parameters: "14B", vramRequired: 10000, contextLength: 32768, quantization: "Q4_K_M", runtime: "ollama", strengths: "Strong reasoning with code understanding" }, - { id: "deepseek-r1:70b", name: "DeepSeek R1 70B", provider: "deepseek", activities: ["reasoning", "analysis", "coding"], tier: "xlarge", parameters: "70B", vramRequired: 40000, contextLength: 32768, quantization: "Q4_K_M", runtime: "ollama", strengths: "Frontier-level reasoning, rivals o1 on math benchmarks" }, - { id: "qwen2.5:72b", name: "Qwen 2.5 72B", provider: "qwen", activities: ["reasoning", "analysis", "multilingual"], tier: "xlarge", parameters: "72B", vramRequired: 42000, contextLength: 32768, quantization: "Q4_K_M", runtime: "ollama", strengths: "Top-tier reasoning and multilingual capabilities" }, + { + id: "deepseek-r1:7b", + name: "DeepSeek R1 7B", + provider: "deepseek", + activities: ["reasoning", "analysis"], + tier: "small", + parameters: "7B", + vramRequired: 5000, + contextLength: 32768, + quantization: "Q4_K_M", + runtime: "ollama", + strengths: "Chain-of-thought reasoning at small scale", + }, + { + id: "deepseek-r1:14b", + name: "DeepSeek R1 14B", + provider: "deepseek", + activities: ["reasoning", "analysis", "coding"], + tier: "medium", + parameters: "14B", + vramRequired: 10000, + contextLength: 32768, + quantization: "Q4_K_M", + runtime: "ollama", + strengths: "Strong reasoning with code understanding", + }, + { + id: "deepseek-r1:70b", + name: "DeepSeek R1 70B", + provider: "deepseek", + activities: ["reasoning", "analysis", "coding"], + tier: "xlarge", + parameters: "70B", + vramRequired: 40000, + contextLength: 32768, + quantization: "Q4_K_M", + runtime: "ollama", + strengths: "Frontier-level reasoning, rivals o1 on math benchmarks", + }, + { + id: "qwen2.5:72b", + name: "Qwen 2.5 72B", + provider: "qwen", + activities: ["reasoning", "analysis", "multilingual"], + tier: "xlarge", + parameters: "72B", + vramRequired: 42000, + contextLength: 32768, + quantization: "Q4_K_M", + runtime: "ollama", + strengths: "Top-tier reasoning and multilingual capabilities", + }, // ── Creative / Writing models ───────────────────────────────────── - { id: "yi:34b", name: "Yi 34B", provider: "01ai", activities: ["creative", "chat", "multilingual"], tier: "large", parameters: "34B", vramRequired: 22000, contextLength: 4096, quantization: "Q4_K_M", runtime: "ollama", strengths: "Strong creative writing and bilingual (EN/ZH)" }, + { + id: "yi:34b", + name: "Yi 34B", + provider: "01ai", + activities: ["creative", "chat", "multilingual"], + tier: "large", + parameters: "34B", + vramRequired: 22000, + contextLength: 4096, + quantization: "Q4_K_M", + runtime: "ollama", + strengths: "Strong creative writing and bilingual (EN/ZH)", + }, // ── Multilingual models ─────────────────────────────────────────── - { id: "qwen2.5:7b", name: "Qwen 2.5 7B", provider: "qwen", activities: ["multilingual", "chat"], tier: "small", parameters: "7B", vramRequired: 5000, contextLength: 32768, quantization: "Q4_K_M", runtime: "ollama", strengths: "Strong multilingual support including CJK languages" }, - { id: "qwen2.5:14b", name: "Qwen 2.5 14B", provider: "qwen", activities: ["multilingual", "chat", "agents", "analysis"], tier: "medium", parameters: "14B", vramRequired: 10000, contextLength: 32768, quantization: "Q4_K_M", runtime: "ollama", strengths: "Best mid-size multilingual model with tool use" }, - { id: "qwen2.5:32b", name: "Qwen 2.5 32B", provider: "qwen", activities: ["multilingual", "analysis", "reasoning"], tier: "large", parameters: "32B", vramRequired: 22000, contextLength: 32768, quantization: "Q4_K_M", runtime: "ollama", strengths: "Strong analysis and multilingual reasoning" }, - { id: "aya:8b", name: "Aya 8B", provider: "cohere", activities: ["multilingual", "chat"], tier: "small", parameters: "8B", vramRequired: 6000, contextLength: 8192, quantization: "Q4_K_M", runtime: "ollama", strengths: "Covers 23+ languages including underrepresented ones" }, - { id: "aya:35b", name: "Aya 35B", provider: "cohere", activities: ["multilingual", "chat", "analysis"], tier: "large", parameters: "35B", vramRequired: 24000, contextLength: 8192, quantization: "Q4_K_M", runtime: "ollama", strengths: "Best multilingual coverage with strong quality" }, + { + id: "qwen2.5:7b", + name: "Qwen 2.5 7B", + provider: "qwen", + activities: ["multilingual", "chat"], + tier: "small", + parameters: "7B", + vramRequired: 5000, + contextLength: 32768, + quantization: "Q4_K_M", + runtime: "ollama", + strengths: "Strong multilingual support including CJK languages", + }, + { + id: "qwen2.5:14b", + name: "Qwen 2.5 14B", + provider: "qwen", + activities: ["multilingual", "chat", "agents", "analysis"], + tier: "medium", + parameters: "14B", + vramRequired: 10000, + contextLength: 32768, + quantization: "Q4_K_M", + runtime: "ollama", + strengths: "Best mid-size multilingual model with tool use", + }, + { + id: "qwen2.5:32b", + name: "Qwen 2.5 32B", + provider: "qwen", + activities: ["multilingual", "analysis", "reasoning"], + tier: "large", + parameters: "32B", + vramRequired: 22000, + contextLength: 32768, + quantization: "Q4_K_M", + runtime: "ollama", + strengths: "Strong analysis and multilingual reasoning", + }, + { + id: "aya:8b", + name: "Aya 8B", + provider: "cohere", + activities: ["multilingual", "chat"], + tier: "small", + parameters: "8B", + vramRequired: 6000, + contextLength: 8192, + quantization: "Q4_K_M", + runtime: "ollama", + strengths: "Covers 23+ languages including underrepresented ones", + }, + { + id: "aya:35b", + name: "Aya 35B", + provider: "cohere", + activities: ["multilingual", "chat", "analysis"], + tier: "large", + parameters: "35B", + vramRequired: 24000, + contextLength: 8192, + quantization: "Q4_K_M", + runtime: "ollama", + strengths: "Best multilingual coverage with strong quality", + }, // ── Vision (multimodal) models ──────────────────────────────────── - { id: "llava:7b", name: "LLaVA 7B", provider: "meta", activities: ["vision", "chat"], tier: "small", parameters: "7B", vramRequired: 6000, contextLength: 4096, quantization: "Q4_K_M", runtime: "ollama", strengths: "Fast visual QA with image understanding" }, - { id: "llava:13b", name: "LLaVA 13B", provider: "meta", activities: ["vision", "chat"], tier: "medium", parameters: "13B", vramRequired: 10000, contextLength: 4096, quantization: "Q4_K_M", runtime: "ollama", strengths: "Better image reasoning than 7B variant" }, - { id: "llama3.2-vision:11b", name: "Llama 3.2 Vision 11B", provider: "meta", activities: ["vision", "chat", "analysis"], tier: "medium", parameters: "11B", vramRequired: 8000, contextLength: 128000, quantization: "Q4_K_M", runtime: "ollama", strengths: "Native multimodal with 128K context" }, + { + id: "llava:7b", + name: "LLaVA 7B", + provider: "meta", + activities: ["vision", "chat"], + tier: "small", + parameters: "7B", + vramRequired: 6000, + contextLength: 4096, + quantization: "Q4_K_M", + runtime: "ollama", + strengths: "Fast visual QA with image understanding", + }, + { + id: "llava:13b", + name: "LLaVA 13B", + provider: "meta", + activities: ["vision", "chat"], + tier: "medium", + parameters: "13B", + vramRequired: 10000, + contextLength: 4096, + quantization: "Q4_K_M", + runtime: "ollama", + strengths: "Better image reasoning than 7B variant", + }, + { + id: "llama3.2-vision:11b", + name: "Llama 3.2 Vision 11B", + provider: "meta", + activities: ["vision", "chat", "analysis"], + tier: "medium", + parameters: "11B", + vramRequired: 8000, + contextLength: 128000, + quantization: "Q4_K_M", + runtime: "ollama", + strengths: "Native multimodal with 128K context", + }, // ── Agent-focused models ─────────────────────────────────────────── - { id: "granite3-dense:8b", name: "Granite 3 Dense 8B", provider: "ibm", activities: ["agents", "coding", "chat"], tier: "small", parameters: "8B", vramRequired: 6000, contextLength: 8192, quantization: "Q4_K_M", runtime: "ollama", strengths: "Enterprise-grade with strong tool-use support" }, - { id: "granite3-moe:3b", name: "Granite 3 MoE 3B", provider: "ibm", activities: ["agents", "chat"], tier: "small", parameters: "3B", vramRequired: 3000, contextLength: 8192, quantization: "Q4_K_M", runtime: "ollama", strengths: "Lightweight MoE architecture for agent workloads" }, + { + id: "granite3-dense:8b", + name: "Granite 3 Dense 8B", + provider: "ibm", + activities: ["agents", "coding", "chat"], + tier: "small", + parameters: "8B", + vramRequired: 6000, + contextLength: 8192, + quantization: "Q4_K_M", + runtime: "ollama", + strengths: "Enterprise-grade with strong tool-use support", + }, + { + id: "granite3-moe:3b", + name: "Granite 3 MoE 3B", + provider: "ibm", + activities: ["agents", "chat"], + tier: "small", + parameters: "3B", + vramRequired: 3000, + contextLength: 8192, + quantization: "Q4_K_M", + runtime: "ollama", + strengths: "Lightweight MoE architecture for agent workloads", + }, // ── Analysis-focused additions ──────────────────────────────────── - { id: "solar:10.7b", name: "Solar 10.7B", provider: "upstage", activities: ["analysis", "chat", "creative"], tier: "medium", parameters: "10.7B",vramRequired: 8000, contextLength: 4096, quantization: "Q4_K_M", runtime: "ollama", strengths: "Depth-upscaled architecture, strong summarization" }, - { id: "command-r:35b", name: "Command R 35B", provider: "cohere", activities: ["analysis", "agents", "multilingual"], tier: "large", parameters: "35B", vramRequired: 24000, contextLength: 128000, quantization: "Q4_K_M", runtime: "ollama", strengths: "RAG-optimized with grounded generation and citations" }, + { + id: "solar:10.7b", + name: "Solar 10.7B", + provider: "upstage", + activities: ["analysis", "chat", "creative"], + tier: "medium", + parameters: "10.7B", + vramRequired: 8000, + contextLength: 4096, + quantization: "Q4_K_M", + runtime: "ollama", + strengths: "Depth-upscaled architecture, strong summarization", + }, + { + id: "command-r:35b", + name: "Command R 35B", + provider: "cohere", + activities: ["analysis", "agents", "multilingual"], + tier: "large", + parameters: "35B", + vramRequired: 24000, + contextLength: 128000, + quantization: "Q4_K_M", + runtime: "ollama", + strengths: "RAG-optimized with grounded generation and citations", + }, // ── NVIDIA NIM models ───────────────────────────────────────────── - { id: "nvidia/nemotron-mini:4b", name: "Nemotron Mini 4B", provider: "nvidia", activities: ["chat", "agents"], tier: "small", parameters: "4B", vramRequired: 4000, contextLength: 8192, quantization: "FP16", runtime: "nim", strengths: "Optimized for NIM runtime, low-latency inference" }, - { id: "nvidia/nemotron-nano:8b", name: "Nemotron Nano 8B", provider: "nvidia", activities: ["chat", "coding", "agents"], tier: "small", parameters: "8B", vramRequired: 8000, contextLength: 8192, quantization: "FP16", runtime: "nim", strengths: "TensorRT-LLM optimized, fast for coding tasks" }, - { id: "nvidia/nemotron-super:49b", name: "Nemotron Super 49B", provider: "nvidia", activities: ["chat", "reasoning", "coding", "agents"], tier: "xlarge", parameters: "49B", vramRequired: 48000, contextLength: 32768, quantization: "FP16", runtime: "nim", strengths: "High-quality with NIM optimizations, strong reasoning" }, - { id: "nvidia/nemotron-ultra:253b",name: "Nemotron Ultra 253B", provider: "nvidia", activities: ["chat", "reasoning", "coding", "analysis", "agents"], tier: "xlarge", parameters: "253B", vramRequired: 320000, contextLength: 32768, quantization: "FP16", runtime: "nim", strengths: "Frontier-class performance, requires multi-GPU" }, + { + id: "nvidia/nemotron-mini:4b", + name: "Nemotron Mini 4B", + provider: "nvidia", + activities: ["chat", "agents"], + tier: "small", + parameters: "4B", + vramRequired: 4000, + contextLength: 8192, + quantization: "FP16", + runtime: "nim", + strengths: "Optimized for NIM runtime, low-latency inference", + }, + { + id: "nvidia/nemotron-nano:8b", + name: "Nemotron Nano 8B", + provider: "nvidia", + activities: ["chat", "coding", "agents"], + tier: "small", + parameters: "8B", + vramRequired: 8000, + contextLength: 8192, + quantization: "FP16", + runtime: "nim", + strengths: "TensorRT-LLM optimized, fast for coding tasks", + }, + { + id: "nvidia/nemotron-super:49b", + name: "Nemotron Super 49B", + provider: "nvidia", + activities: ["chat", "reasoning", "coding", "agents"], + tier: "xlarge", + parameters: "49B", + vramRequired: 48000, + contextLength: 32768, + quantization: "FP16", + runtime: "nim", + strengths: "High-quality with NIM optimizations, strong reasoning", + }, + { + id: "nvidia/nemotron-ultra:253b", + name: "Nemotron Ultra 253B", + provider: "nvidia", + activities: ["chat", "reasoning", "coding", "analysis", "agents"], + tier: "xlarge", + parameters: "253B", + vramRequired: 320000, + contextLength: 32768, + quantization: "FP16", + runtime: "nim", + strengths: "Frontier-class performance, requires multi-GPU", + }, ]; // Legacy flat catalog — derived from CATALOG for backward compatibility @@ -202,10 +833,7 @@ export class LocalModelSetup { // AMD: rocm-smi (Linux) or WMIC/PowerShell (Windows) try { - const { stdout } = await execFileAsync("rocm-smi", [ - "--showmeminfo", - "vram", - ]); + const { stdout } = await execFileAsync("rocm-smi", ["--showmeminfo", "vram"]); const match = stdout.match(/Total\s+:\s+(\d+)/); if (match) { const vramBytes = parseInt(match[1]!, 10); @@ -222,10 +850,15 @@ export class LocalModelSetup { // Windows: detect any GPU via PowerShell (NVIDIA, AMD, Intel) if (process.platform === "win32") { try { - const { stdout } = await execFileAsync("powershell", [ - "-NoProfile", "-Command", - "Get-CimInstance Win32_VideoController | Select-Object Name, AdapterRAM | ConvertTo-Json", - ], { timeout: 5000 }); + const { stdout } = await execFileAsync( + "powershell", + [ + "-NoProfile", + "-Command", + "Get-CimInstance Win32_VideoController | Select-Object Name, AdapterRAM | ConvertTo-Json", + ], + { timeout: 5000 }, + ); const gpus = JSON.parse(stdout.trim()); const gpuList = Array.isArray(gpus) ? gpus : [gpus]; // Pick the GPU with most VRAM (skip integrated if discrete exists) @@ -238,9 +871,11 @@ export class LocalModelSetup { const vramMB = Math.round((bestGpu.AdapterRAM ?? 0) / (1024 * 1024)); if (vramMB > 0) { const name = String(bestGpu.Name ?? "GPU"); - const vendor = name.toLowerCase().includes("nvidia") ? "nvidia" as const - : name.toLowerCase().includes("amd") || name.toLowerCase().includes("radeon") ? "amd" as const - : "none" as const; + const vendor = name.toLowerCase().includes("nvidia") + ? ("nvidia" as const) + : name.toLowerCase().includes("amd") || name.toLowerCase().includes("radeon") + ? ("amd" as const) + : ("none" as const); return { vendor, name, vramMB }; } } catch { @@ -265,7 +900,9 @@ export class LocalModelSetup { vramMB: Math.min(Math.round(memMB * 0.4), 2048), // Pi can use ~40% RAM for models }; } - } catch { /* not Pi */ } + } catch { + /* not Pi */ + } // Generic Linux without GPU — use RAM for CPU inference return { @@ -274,7 +911,9 @@ export class LocalModelSetup { vramMB: Math.round(memMB * 0.5), }; } - } catch { /* /proc/meminfo not available */ } + } catch { + /* /proc/meminfo not available */ + } } // macOS: detect Apple Silicon vs Intel @@ -364,10 +1003,7 @@ export class LocalModelSetup { /** * Install/pull a model via Ollama. */ - async installModel( - model: string, - onProgress?: (pct: number) => void, - ): Promise { + async installModel(model: string, onProgress?: (pct: number) => void): Promise { try { const child = execFileAsync("ollama", ["pull", model]); @@ -419,7 +1055,11 @@ export class LocalModelSetup { ssrfGate.addRule({ host: "localhost", port: 8080, protocol: "http" }); const ssrfCheck = await ssrfGate.validateEndpoint(url); if (!ssrfCheck.safe) { - return { ok: false, latencyMs: Date.now() - start, error: `SSRF blocked: ${ssrfCheck.reason}` }; + return { + ok: false, + latencyMs: Date.now() - start, + error: `SSRF blocked: ${ssrfCheck.reason}`, + }; } const response = await fetch(url, { method: "POST", @@ -459,7 +1099,9 @@ export class LocalModelSetup { }; return { - runtime: (["ollama", "vllm", "nim"].includes(runtime) ? runtime : "custom") as LocalModelConfig["runtime"], + runtime: (["ollama", "vllm", "nim"].includes(runtime) + ? runtime + : "custom") as LocalModelConfig["runtime"], endpoint: endpoints[runtime] ?? endpoints.custom!, model, gpuInfo: null, @@ -471,9 +1113,9 @@ export class LocalModelSetup { * Results are sorted by VRAM descending (best models first within hardware limits). */ suggestByActivity(activity: ModelActivity, gpu: GPUInfo): CatalogModel[] { - return CATALOG - .filter((m) => m.activities.includes(activity) && m.vramRequired <= gpu.vramMB) - .sort((a, b) => b.vramRequired - a.vramRequired); + return CATALOG.filter( + (m) => m.activities.includes(activity) && m.vramRequired <= gpu.vramMB, + ).sort((a, b) => b.vramRequired - a.vramRequired); } /** @@ -512,9 +1154,15 @@ export class LocalModelSetup { // Check if NVIDIA Container Toolkit is available let gpuSupport = false; try { - await execFileAsync("docker", ["run", "--rm", "--gpus", "all", "nvidia/cuda:12.0-base", "nvidia-smi"], { timeout: 10_000 }); + await execFileAsync( + "docker", + ["run", "--rm", "--gpus", "all", "nvidia/cuda:12.0-base", "nvidia-smi"], + { timeout: 10_000 }, + ); gpuSupport = true; - } catch { /* no GPU support */ } + } catch { + /* no GPU support */ + } runtimes.push({ name: "docker", installed: true, version, gpuSupport }); } catch { runtimes.push({ name: "docker", installed: false }); @@ -550,8 +1198,10 @@ export class LocalModelSetup { headers: { "User-Agent": "mayros-detect" }, }); if (response.ok) { - const data = await response.json() as { data?: Array<{ id: string }> }; - const hasNvidia = data.data?.some((m) => m.id.includes("nvidia") || m.id.includes("nemotron")); + const data = (await response.json()) as { data?: Array<{ id: string }> }; + const hasNvidia = data.data?.some( + (m) => m.id.includes("nvidia") || m.id.includes("nemotron"), + ); if (hasNvidia) { runtimes.push({ name: "nim", installed: true, endpoint: "http://localhost:8000/v1" }); } @@ -653,20 +1303,36 @@ export class LocalModelSetup { const platform = process.platform; try { if (platform === "win32") { - await execFileAsync("winget", ["install", "Ollama.Ollama", "--accept-package-agreements", "--accept-source-agreements"], { timeout: 120_000 }); - return { success: true, message: "Ollama installed via winget. Run 'ollama serve' to start." }; + await execFileAsync( + "winget", + ["install", "Ollama.Ollama", "--accept-package-agreements", "--accept-source-agreements"], + { timeout: 120_000 }, + ); + return { + success: true, + message: "Ollama installed via winget. Run 'ollama serve' to start.", + }; } else if (platform === "darwin") { await execFileAsync("brew", ["install", "ollama"], { timeout: 120_000 }); - return { success: true, message: "Ollama installed via brew. Run 'ollama serve' to start." }; + return { + success: true, + message: "Ollama installed via brew. Run 'ollama serve' to start.", + }; } else { // Linux: use the official install script const { execSync } = await import("node:child_process"); - execSync("curl -fsSL https://ollama.com/install.sh | sh", { timeout: 120_000, stdio: "pipe" }); + execSync("curl -fsSL https://ollama.com/install.sh | sh", { + timeout: 120_000, + stdio: "pipe", + }); return { success: true, message: "Ollama installed. It should start automatically." }; } } catch (err) { const msg = err instanceof Error ? err.message : String(err); - return { success: false, message: `Installation failed: ${msg}. Install manually from https://ollama.com` }; + return { + success: false, + message: `Installation failed: ${msg}. Install manually from https://ollama.com`, + }; } } } diff --git a/extensions/mamoru/sandbox.ts b/extensions/mamoru/sandbox.ts index db5c409c..04d48d29 100644 --- a/extensions/mamoru/sandbox.ts +++ b/extensions/mamoru/sandbox.ts @@ -41,13 +41,7 @@ export type SandboxApplyResult = { // ── Default policy ─────────────────────────────────────────────────────── -const DEFAULT_READ_ONLY = [ - "/usr", - "/lib", - "/proc/self/status", - "/etc/ssl", - "/etc/resolv.conf", -]; +const DEFAULT_READ_ONLY = ["/usr", "/lib", "/proc/self/status", "/etc/ssl", "/etc/resolv.conf"]; const DEFAULT_READ_WRITE = [ resolve(homedir(), ".mayros"), @@ -143,18 +137,27 @@ export class MamoruSandbox { // prlimit can set no-new-privs indirectly; we use a direct approach // via /proc/self/attr or prctl helper. The most portable way from // Node.js is writing to the proc interface or using a tiny helper. - execFileSync("sh", ["-c", `test -f /proc/${pid}/status && grep -q NoNewPrivs /proc/${pid}/status`], { - stdio: "pipe", - timeout: 5000, - }); + execFileSync( + "sh", + ["-c", `test -f /proc/${pid}/status && grep -q NoNewPrivs /proc/${pid}/status`], + { + stdio: "pipe", + timeout: 5000, + }, + ); // NoNewPrivs field exists in status — set it via prctl if not already set try { - execFileSync("sh", ["-c", - // Use perl as a portable prctl(38, 1, 0, 0, 0) wrapper — PR_SET_NO_NEW_PRIVS = 38 - `perl -e 'require "syscall.ph"; syscall(157, 38, 1, 0, 0, 0)' 2>/dev/null || ` + - // Fallback: python3 ctypes - `python3 -c "import ctypes; ctypes.CDLL(None).prctl(38,1,0,0,0)" 2>/dev/null || true`, - ], { stdio: "pipe", timeout: 5000 }); + execFileSync( + "sh", + [ + "-c", + // Use perl as a portable prctl(38, 1, 0, 0, 0) wrapper — PR_SET_NO_NEW_PRIVS = 38 + `perl -e 'require "syscall.ph"; syscall(157, 38, 1, 0, 0, 0)' 2>/dev/null || ` + + // Fallback: python3 ctypes + `python3 -c "import ctypes; ctypes.CDLL(None).prctl(38,1,0,0,0)" 2>/dev/null || true`, + ], + { stdio: "pipe", timeout: 5000 }, + ); appliedLayers.push("no-new-privs"); } catch { // Could not set no-new-privs — non-fatal in best_effort mode @@ -163,7 +166,10 @@ export class MamoruSandbox { } } } catch (err) { - if (policy.compatibility === "enforce" && !(err instanceof Error && err.message.includes("mamoru"))) { + if ( + policy.compatibility === "enforce" && + !(err instanceof Error && err.message.includes("mamoru")) + ) { throw new Error("mamoru: failed to verify no-new-privs support"); } } @@ -176,10 +182,10 @@ export class MamoruSandbox { try { const pid = process.pid; const limit = policy.process.maxProcesses; - execFileSync("prlimit", [ - `--pid=${pid}`, - `--nproc=${limit}:${limit}`, - ], { stdio: "pipe", timeout: 5000 }); + execFileSync("prlimit", [`--pid=${pid}`, `--nproc=${limit}:${limit}`], { + stdio: "pipe", + timeout: 5000, + }); appliedLayers.push("rlimit-nproc"); } catch { // prlimit may not be installed or may lack permissions @@ -303,4 +309,3 @@ export class MamoruSandbox { return this.appliedPolicy; } } - diff --git a/extensions/mamoru/secrets-vault.test.ts b/extensions/mamoru/secrets-vault.test.ts index e10541dd..8db88940 100644 --- a/extensions/mamoru/secrets-vault.test.ts +++ b/extensions/mamoru/secrets-vault.test.ts @@ -15,7 +15,12 @@ function createMockClient(): CortexClientLike & { createTriple: vi.fn(async (req) => { const id = `t-${nextId++}`; - const triple = { id, subject: req.subject, predicate: req.predicate, object: String(req.object) }; + const triple = { + id, + subject: req.subject, + predicate: req.predicate, + object: String(req.object), + }; triples.push(triple); return triple; }), diff --git a/extensions/mamoru/secrets-vault.ts b/extensions/mamoru/secrets-vault.ts index bbfe28b0..3a9f181e 100644 --- a/extensions/mamoru/secrets-vault.ts +++ b/extensions/mamoru/secrets-vault.ts @@ -8,26 +8,21 @@ * Key derivation: scrypt (N=131072, r=8, p=1) from master password + random salt. */ -import { - randomBytes, - createCipheriv, - createDecipheriv, - scryptSync, -} from "node:crypto"; +import { randomBytes, createCipheriv, createDecipheriv, scryptSync } from "node:crypto"; import type { CortexClientLike } from "../shared/cortex-client.js"; // ── Types ──────────────────────────────────────────────────────────────── export type Secret = { id: string; - name: string; // e.g., "ANTHROPIC_API_KEY" + name: string; // e.g., "ANTHROPIC_API_KEY" version: number; - encryptedValue: string; // AES-256-GCM encrypted (base64) - iv: string; // initialization vector (base64) - tag: string; // GCM auth tag (base64) - salt: string; // scrypt salt (base64) + encryptedValue: string; // AES-256-GCM encrypted (base64) + iv: string; // initialization vector (base64) + tag: string; // GCM auth tag (base64) + salt: string; // scrypt salt (base64) scope: "global" | "venture" | "agent"; - scopeId?: string; // venture or agent ID + scopeId?: string; // venture or agent ID createdAt: string; rotatedAt: string | null; }; @@ -136,10 +131,7 @@ export class MamoruVault { const iv = randomBytes(IV_BYTES); const cipher = createCipheriv(ALGORITHM, key, iv); - const encBuf = Buffer.concat([ - cipher.update(plaintext, "utf8"), - cipher.final(), - ]); + const encBuf = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]); const tag = cipher.getAuthTag(); return { @@ -153,11 +145,7 @@ export class MamoruVault { private decrypt(encrypted: string, iv: string, tag: string, salt: string): string { const password = this.requireKey(); const key = this.deriveKey(password, Buffer.from(salt, "base64")); - const decipher = createDecipheriv( - ALGORITHM, - key, - Buffer.from(iv, "base64"), - ); + const decipher = createDecipheriv(ALGORITHM, key, Buffer.from(iv, "base64")); decipher.setAuthTag(Buffer.from(tag, "base64")); return Buffer.concat([ @@ -183,7 +171,7 @@ export class MamoruVault { } const scope = opts?.scope ?? "global"; - const version = await this.currentVersion(name) + 1; + const version = (await this.currentVersion(name)) + 1; const now = new Date().toISOString(); const { encrypted, iv, tag, salt } = this.encrypt(value); diff --git a/extensions/matrix/package.json b/extensions/matrix/package.json index ac52940e..71e16710 100644 --- a/extensions/matrix/package.json +++ b/extensions/matrix/package.json @@ -11,7 +11,7 @@ "@matrix-org/matrix-sdk-crypto-nodejs": "^0.4.0", "@vector-im/matrix-bot-sdk": "0.8.0-element.3", "markdown-it": "14.1.1", - "music-metadata": "^11.12.1", + "music-metadata": "^11.12.3", "zod": "^4.3.6" }, "peerDependencies": { diff --git a/extensions/mcp-server/hardening.test.ts b/extensions/mcp-server/hardening.test.ts index 1e331d71..16ebd0f1 100644 --- a/extensions/mcp-server/hardening.test.ts +++ b/extensions/mcp-server/hardening.test.ts @@ -408,12 +408,10 @@ describe("transport CORS", () => { const { McpHttpTransport } = await import("./transport-http.js"); // Create a minimal dispatcher mock (cast to satisfy TS) + // eslint-disable-next-line @typescript-eslint/no-explicit-any const dispatcher = { handleMessage: vi.fn().mockResolvedValue('{"jsonrpc":"2.0","result":{}}'), - } as Parameters[0] extends void - ? never - : // eslint-disable-next-line @typescript-eslint/no-explicit-any - any; + } as any; // Use a high random port to avoid conflicts const port = 19500 + Math.floor(Math.random() * 1000); diff --git a/extensions/mcp-server/index.ts b/extensions/mcp-server/index.ts index cc4f97ef..65efa511 100644 --- a/extensions/mcp-server/index.ts +++ b/extensions/mcp-server/index.ts @@ -13,6 +13,7 @@ * Configuration: mayros.json → plugins.mcp-server */ +// @ts-expect-error — dist/index.js has no declaration file; types resolved via source paths import type { MayrosPluginApi, MayrosPluginToolContext } from "@apilium/mayros"; import { mcpServerConfigSchema, type McpServerConfig } from "./config.js"; import { McpServer, type McpServerOptions } from "./server.js"; @@ -141,7 +142,7 @@ const mcpServerPlugin = { // ── Register CLI ──────────────────────────────────────────────── - api.registerCli(({ program }) => { + api.registerCli(({ program }: { program: any }) => { const serve = program .command("serve") .description("Start MCP server to expose Mayros tools, resources, and prompts"); @@ -302,7 +303,7 @@ const mcpServerPlugin = { // ── Register gateway method (MCP Dashboard) ──────────────────── - api.registerGatewayMethod("mcp.dashboard", async ({ respond }) => { + api.registerGatewayMethod("mcp.dashboard", async ({ respond }: { respond: any }) => { // Cortex health check with 3s timeout — always runs let cortexHealth: { status: "online" | "offline"; latencyMs: number }; try { @@ -414,7 +415,7 @@ const mcpServerPlugin = { resourceSources.listGraphSubjects = async () => { try { - const res = await client.listSubjects({ prefix: ns, limit: 200 }); + const res = await client.listSubjects({ predicate: ns, limit: 200 }); return res.subjects; } catch { return []; diff --git a/extensions/mcp-server/kaneru-tools.test.ts b/extensions/mcp-server/kaneru-tools.test.ts index 68a50431..fe7583ac 100644 --- a/extensions/mcp-server/kaneru-tools.test.ts +++ b/extensions/mcp-server/kaneru-tools.test.ts @@ -46,7 +46,8 @@ describe("Kaneru MCP Tools", () => { return tool; } - function text(result: { content: Array<{ type: string; text: string }> }): string { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + function text(result: any): string { return result.content[0]!.text; } diff --git a/extensions/mcp-server/venture-tools.ts b/extensions/mcp-server/venture-tools.ts index f60410f2..d84c9333 100644 --- a/extensions/mcp-server/venture-tools.ts +++ b/extensions/mcp-server/venture-tools.ts @@ -35,13 +35,25 @@ function textResult(text: string): ToolContent { return { content: [{ type: "text" as const, text }] }; } -const VALID_MISSION_STATUSES = new Set(["queued", "ready", "active", "review", "complete", "abandoned"]); +const VALID_MISSION_STATUSES = new Set([ + "queued", + "ready", + "active", + "review", + "complete", + "abandoned", +]); const VALID_PRIORITIES = new Set(["critical", "high", "medium", "low"]); const VALID_TRIGGERS = new Set(["timer", "assignment", "mention", "mission-ready", "escalation"]); -function validateEnum(value: string | undefined, valid: Set, label: string): T | undefined { +function validateEnum( + value: string | undefined, + valid: Set, + label: string, +): T | undefined { if (value === undefined) return undefined; - if (!valid.has(value)) throw new Error(`Invalid ${label}: "${value}". Valid: ${[...valid].join(", ")}`); + if (!valid.has(value)) + throw new Error(`Invalid ${label}: "${value}". Valid: ${[...valid].join(", ")}`); return value as T; } @@ -191,8 +203,7 @@ export function createVentureTools(deps: VentureToolDeps): AdaptableTool[] & { d // ------------------------------------------------------------------ { name: "kaneru_venture_list", - description: - "List all Kaneru ventures with their status, prefix, and fuel limits.", + description: "List all Kaneru ventures with their status, prefix, and fuel limits.", parameters: Type.Object({}), execute: async (_callId: string, _params: Record) => { try { @@ -202,12 +213,9 @@ export function createVentureTools(deps: VentureToolDeps): AdaptableTool[] & { d return textResult("No ventures found."); } const lines = ventures.map( - (v) => - ` [${v.status}] ${v.prefix} — ${v.name} (id: ${v.id}, fuel: ${v.fuelLimit}c)`, - ); - return textResult( - `${ventures.length} venture(s):\n${lines.join("\n")}`, + (v) => ` [${v.status}] ${v.prefix} — ${v.name} (id: ${v.id}, fuel: ${v.fuelLimit}c)`, ); + return textResult(`${ventures.length} venture(s):\n${lines.join("\n")}`); } catch (err) { return textResult(`Error: ${String(err)}`); } @@ -225,9 +233,7 @@ export function createVentureTools(deps: VentureToolDeps): AdaptableTool[] & { d parameters: Type.Object({ ventureId: Type.String({ description: "Venture ID to create the mission in" }), title: Type.String({ description: "Mission title" }), - description: Type.Optional( - Type.String({ description: "Detailed mission description" }), - ), + description: Type.Optional(Type.String({ description: "Detailed mission description" })), priority: Type.Optional( Type.String({ description: "Priority: critical, high, medium, low (default: medium)", @@ -244,7 +250,11 @@ export function createVentureTools(deps: VentureToolDeps): AdaptableTool[] & { d ventureId: params.ventureId as string, title: params.title as string, description: (params.description as string | undefined) ?? undefined, - priority: validateEnum(params.priority as string | undefined, VALID_PRIORITIES, "priority"), + priority: validateEnum( + params.priority as string | undefined, + VALID_PRIORITIES, + "priority", + ), directiveId: (params.directiveId as string | undefined) ?? undefined, }); return textResult( @@ -305,21 +315,23 @@ export function createVentureTools(deps: VentureToolDeps): AdaptableTool[] & { d // ------------------------------------------------------------------ { name: "kaneru_mission_list", - description: - "List missions for a venture with optional status filter.", + description: "List missions for a venture with optional status filter.", parameters: Type.Object({ ventureId: Type.String({ description: "Venture ID to list missions for" }), status: Type.Optional( Type.String({ - description: - "Filter by status: queued, ready, active, review, complete, abandoned", + description: "Filter by status: queued, ready, active, review, complete, abandoned", }), ), }), execute: async (_callId: string, params: Record) => { try { const mgr = getMissionManager(); - const status = validateEnum(params.status as string | undefined, VALID_MISSION_STATUSES, "status"); + const status = validateEnum( + params.status as string | undefined, + VALID_MISSION_STATUSES, + "status", + ); const missions = await mgr.list(params.ventureId as string, { status }); if (missions.length === 0) { const filterNote = status ? ` with status "${status}"` : ""; @@ -330,9 +342,7 @@ export function createVentureTools(deps: VentureToolDeps): AdaptableTool[] & { d ` [${m.status}] ${m.identifier} — ${m.title} (${m.priority})` + (m.claimedBy ? ` claimed by ${m.claimedBy}` : ""), ); - return textResult( - `${missions.length} mission(s):\n${lines.join("\n")}`, - ); + return textResult(`${missions.length} mission(s):\n${lines.join("\n")}`); } catch (err) { return textResult(`Error: ${String(err)}`); } @@ -350,15 +360,18 @@ export function createVentureTools(deps: VentureToolDeps): AdaptableTool[] & { d parameters: Type.Object({ missionId: Type.String({ description: "Mission ID to transition" }), status: Type.String({ - description: - "Target status: queued, ready, active, review, complete, abandoned", + description: "Target status: queued, ready, active, review, complete, abandoned", }), runId: Type.String({ description: "Current run ID for authorization" }), }), execute: async (_callId: string, params: Record) => { try { const mgr = getMissionManager(); - const validatedStatus = validateEnum(params.status as string, VALID_MISSION_STATUSES, "status")!; + const validatedStatus = validateEnum( + params.status as string, + VALID_MISSION_STATUSES, + "status", + )!; const mission = await mgr.transition( params.missionId as string, validatedStatus, @@ -403,16 +416,12 @@ export function createVentureTools(deps: VentureToolDeps): AdaptableTool[] & { d const agentLines = summary.byAgent.length > 0 - ? summary.byAgent - .map((a) => ` ${a.agentId}: ${a.totalCents}c`) - .join("\n") + ? summary.byAgent.map((a) => ` ${a.agentId}: ${a.totalCents}c`).join("\n") : " (none)"; const missionLines = summary.byMission.length > 0 - ? summary.byMission - .map((m) => ` ${m.missionId}: ${m.totalCents}c`) - .join("\n") + ? summary.byMission.map((m) => ` ${m.missionId}: ${m.totalCents}c`).join("\n") : " (none)"; return textResult( @@ -442,14 +451,17 @@ export function createVentureTools(deps: VentureToolDeps): AdaptableTool[] & { d agentId: Type.String({ description: "Agent ID to trigger pulse for" }), ventureId: Type.String({ description: "Venture context for the pulse" }), trigger: Type.String({ - description: - "Trigger type: timer, assignment, mention, mission-ready, escalation", + description: "Trigger type: timer, assignment, mention, mission-ready, escalation", }), }), execute: async (_callId: string, params: Record) => { try { const scheduler = getPulseScheduler(); - const validatedTrigger = validateEnum(params.trigger as string, VALID_TRIGGERS, "trigger")!; + const validatedTrigger = validateEnum( + params.trigger as string, + VALID_TRIGGERS, + "trigger", + )!; const pulse = await scheduler.trigger( params.agentId as string, params.ventureId as string, @@ -507,14 +519,13 @@ export function createVentureTools(deps: VentureToolDeps): AdaptableTool[] & { d // ------------------------------------------------------------------ { name: "kaneru_learn_top", - description: - "Get top agents for a given domain and task type, ranked by expertise score.", + description: "Get top agents for a given domain and task type, ranked by expertise score.", parameters: Type.Object({ domain: Type.String({ description: "Domain (e.g. typescript, python, rust)" }), - taskType: Type.String({ description: "Task type (e.g. code-review, debugging, implementation)" }), - limit: Type.Optional( - Type.Number({ description: "Max results (default: 10)" }), - ), + taskType: Type.String({ + description: "Task type (e.g. code-review, debugging, implementation)", + }), + limit: Type.Optional(Type.Number({ description: "Max results (default: 10)" })), }), execute: async (_callId: string, params: Record) => { try { @@ -526,9 +537,7 @@ export function createVentureTools(deps: VentureToolDeps): AdaptableTool[] & { d limit, ); if (profiles.length === 0) { - return textResult( - `No agents found for ${params.domain}:${params.taskType}`, - ); + return textResult(`No agents found for ${params.domain}:${params.taskType}`); } const lines = profiles.map( (p) => @@ -553,12 +562,8 @@ export function createVentureTools(deps: VentureToolDeps): AdaptableTool[] & { d "Query consensus decision history with optional venture filter. " + "Returns decisions sorted by most recent first.", parameters: Type.Object({ - ventureId: Type.Optional( - Type.String({ description: "Filter by venture ID" }), - ), - limit: Type.Optional( - Type.Number({ description: "Max results (default: 20)" }), - ), + ventureId: Type.Optional(Type.String({ description: "Filter by venture ID" })), + limit: Type.Optional(Type.Number({ description: "Max results (default: 20)" })), }), execute: async (_callId: string, params: Record) => { try { @@ -577,9 +582,7 @@ export function createVentureTools(deps: VentureToolDeps): AdaptableTool[] & { d ` Outcome: ${d.resolvedValue}\n` + ` Decided: ${d.decidedAt}`, ); - return textResult( - `${decisions.length} decision(s):\n${lines.join("\n")}`, - ); + return textResult(`${decisions.length} decision(s):\n${lines.join("\n")}`); } catch (err) { return textResult(`Error: ${String(err)}`); } @@ -624,12 +627,8 @@ export function createVentureTools(deps: VentureToolDeps): AdaptableTool[] & { d if (templates.length === 0) { return textResult("No templates found."); } - const lines = templates.map( - (t) => ` ${t.id} — ${t.name}\n ${t.description}`, - ); - return textResult( - `${templates.length} template(s):\n${lines.join("\n")}`, - ); + const lines = templates.map((t) => ` ${t.id} — ${t.name}\n ${t.description}`); + return textResult(`${templates.length} template(s):\n${lines.join("\n")}`); } catch (err) { return textResult(`Error: ${String(err)}`); } @@ -712,9 +711,7 @@ export function createVentureTools(deps: VentureToolDeps): AdaptableTool[] & { d stdout: Type.String({ description: "Standard output" }), stderr: Type.String({ description: "Standard error output" }), durationMs: Type.Number({ description: "Execution duration in milliseconds" }), - missionId: Type.Optional( - Type.String({ description: "Associated mission ID (if any)" }), - ), + missionId: Type.Optional(Type.String({ description: "Associated mission ID (if any)" })), }), execute: async (_callId: string, params: Record) => { try { diff --git a/extensions/memory-semantic/cortex-sidecar.ts b/extensions/memory-semantic/cortex-sidecar.ts index 76c0f82f..0c0f5e45 100644 --- a/extensions/memory-semantic/cortex-sidecar.ts +++ b/extensions/memory-semantic/cortex-sidecar.ts @@ -258,10 +258,10 @@ export class CortexSidecar { if (!(await this.ensurePortAvailable())) { this.releaseLock(); // If ensurePortAvailable detected an external Cortex, status is "running" — don't overwrite - if (this._status !== "running") { + if ((this._status as string) !== "running") { this._status = "failed"; } - return this._status === "running"; + return (this._status as string) === "running"; } const args = ["--host", this.config.host, "--port", String(this.config.port), "--db", dbPath]; diff --git a/extensions/memory-semantic/index.ts b/extensions/memory-semantic/index.ts index cf5acd9b..922cdd53 100644 --- a/extensions/memory-semantic/index.ts +++ b/extensions/memory-semantic/index.ts @@ -291,7 +291,12 @@ const semanticMemoryPlugin = { if (writeResult === null) { // Queue writes for replay when Cortex recovers for (const t of triples) { - writeQueue.push({ type: "createTriple", payload: t }); + writeQueue.enqueue({ + type: "createTriple", + payload: t, + timestamp: Date.now(), + attempts: 0, + }); } return { content: [ @@ -1804,7 +1809,12 @@ const semanticMemoryPlugin = { try { await client.createTriple(t as Parameters[0]); } catch { - writeQueue.push({ type: "createTriple", payload: t }); + writeQueue.enqueue({ + type: "createTriple", + payload: t, + timestamp: Date.now(), + attempts: 0, + }); } } api.logger.info(`memory-semantic: session node created (${event.sessionId})`); @@ -1839,7 +1849,12 @@ const semanticMemoryPlugin = { try { await client.createTriple(endTriple); } catch { - writeQueue.push({ type: "createTriple", payload: endTriple }); + writeQueue.enqueue({ + type: "createTriple", + payload: endTriple, + timestamp: Date.now(), + attempts: 0, + }); } } diff --git a/extensions/osameru-governance/index.ts b/extensions/osameru-governance/index.ts index 4d8346ab..1a3de3aa 100644 --- a/extensions/osameru-governance/index.ts +++ b/extensions/osameru-governance/index.ts @@ -41,7 +41,9 @@ const osameruPlugin = { : null; let policyBundle: PolicyBundle | null = null; - const workDir = api.config?.workspaceDir ?? process.cwd(); + const workDir = + ((api.config as Record | undefined)?.workspaceDir as string | undefined) ?? + process.cwd(); // Compile policies on session_start api.on("session_start", async () => { @@ -136,9 +138,10 @@ const osameruPlugin = { // Tool: governance_status api.registerTool({ name: "governance_status", + label: "Governance Status", description: "Show governance policy status, rules count, and trust tier summary", parameters: Type.Object({}), - execute: async () => { + execute: async (_toolCallId: string) => { const rules = policyBundle?.rules.length ?? 0; const trusts = trustMgr?.getAllRecords() ?? []; return { @@ -161,6 +164,7 @@ const osameruPlugin = { ), }, ], + details: undefined, }; }, }); @@ -168,6 +172,7 @@ const osameruPlugin = { // Tool: governance_audit_query api.registerTool({ name: "governance_audit_query", + label: "Governance Audit Query", description: "Query the governance audit trail", parameters: Type.Object({ event: Type.Optional(Type.String({ description: "Filter by event type" })), @@ -177,12 +182,15 @@ const osameruPlugin = { ), limit: Type.Optional(Type.Number({ description: "Max entries to return" })), }), - execute: async (params: { - event?: string; - actor?: string; - decision?: string; - limit?: number; - }) => { + execute: async ( + _toolCallId: string, + params: { + event?: string; + actor?: string; + decision?: string; + limit?: number; + }, + ) => { const entries = await auditTrail.query(params); return { content: [ @@ -191,6 +199,7 @@ const osameruPlugin = { text: JSON.stringify(entries, null, 2), }, ], + details: undefined, }; }, }); @@ -198,9 +207,10 @@ const osameruPlugin = { // Tool: governance_audit_verify api.registerTool({ name: "governance_audit_verify", + label: "Governance Audit Verify", description: "Verify the integrity of the governance audit trail HMAC chain", parameters: Type.Object({}), - execute: async () => { + execute: async (_toolCallId: string) => { const result = await auditTrail.verify(); return { content: [ @@ -209,6 +219,7 @@ const osameruPlugin = { text: JSON.stringify(result, null, 2), }, ], + details: undefined, }; }, }); diff --git a/extensions/remote-exec/index.test.ts b/extensions/remote-exec/index.test.ts index 278b5710..9930ef19 100644 --- a/extensions/remote-exec/index.test.ts +++ b/extensions/remote-exec/index.test.ts @@ -50,8 +50,8 @@ import { ENV_NAME_PATTERN, RESERVED_ALIAS_NAMES, type PendingRequest, - type ExecResult, } from "./confirmation-ux.js"; +import type { ExecResult } from "./exec-service.js"; import { SessionManager } from "./session-manager.js"; import { maskSensitiveOutput } from "../../src/security/output-masking.js"; import { AuditTrail } from "../osameru-governance/audit-trail.js"; diff --git a/extensions/shared/cortex-update-check.ts b/extensions/shared/cortex-update-check.ts index 5ebcfb44..28e315f4 100644 --- a/extensions/shared/cortex-update-check.ts +++ b/extensions/shared/cortex-update-check.ts @@ -251,7 +251,11 @@ export async function installOrUpdateCortex( // If rename fails (binary locked), try direct overwrite via copy const { copyFile } = await import("node:fs/promises"); await copyFile(candidate, binaryPath); - try { await unlink(candidate); } catch { /* best-effort */ } + try { + await unlink(candidate); + } catch { + /* best-effort */ + } log(`Overwrote ${binaryName} with ${candidates[0]}`); // Skip the rename below candidates.length = 0; @@ -287,7 +291,11 @@ export async function installOrUpdateCortex( for (const leftover of readdirSync(installDir).filter( (f) => f.startsWith(baseName + "-") && !f.endsWith(".tar.gz") && !f.endsWith(".zip"), )) { - try { await unlink(join(installDir, leftover)); } catch { /* best-effort */ } + try { + await unlink(join(installDir, leftover)); + } catch { + /* best-effort */ + } } const version = getCortexBinaryVersion(binaryPath); diff --git a/extensions/shared/rdf-utils.ts b/extensions/shared/rdf-utils.ts index e5e836f5..c203f098 100644 --- a/extensions/shared/rdf-utils.ts +++ b/extensions/shared/rdf-utils.ts @@ -14,7 +14,7 @@ export function stripBrackets(s: string): string { */ export function sanitizeTripleValue(s: string): string { return s - .replace(/\0/g, "") // null bytes + .replace(/\0/g, "") // null bytes .replace(/[\x01-\x08\x0b\x0c\x0e-\x1f]/g, "") // control chars (keep \n \r \t) - .replace(/^<|>$/g, ""); // leading < or trailing > that mimic RDF notation + .replace(/^<|>$/g, ""); // leading < or trailing > that mimic RDF notation } diff --git a/extensions/shared/task-classification.ts b/extensions/shared/task-classification.ts index 4efa0559..912683fd 100644 --- a/extensions/shared/task-classification.ts +++ b/extensions/shared/task-classification.ts @@ -23,7 +23,16 @@ export type TaskClassification = { // ============================================================================ export const TASK_TYPE_KEYWORDS: Record = { - "code-review": ["review", "pr", "pull request", "check", "lint", "inspect", "approve", "feedback"], + "code-review": [ + "review", + "pr", + "pull request", + "check", + "lint", + "inspect", + "approve", + "feedback", + ], "security-scan": ["security", "vulnerability", "cve", "owasp", "audit", "pentest"], implementation: ["implement", "build", "create", "add", "feature", "develop"], refactoring: ["refactor", "clean", "simplify", "extract", "restructure"], diff --git a/extensions/token-economy/budget-tracker.hard-enforcement.test.ts b/extensions/token-economy/budget-tracker.hard-enforcement.test.ts index d8535cfe..8b9f6778 100644 --- a/extensions/token-economy/budget-tracker.hard-enforcement.test.ts +++ b/extensions/token-economy/budget-tracker.hard-enforcement.test.ts @@ -15,6 +15,9 @@ function makeConfig(overrides: Partial = {}): TokenBudgetConf enforcement: "soft", gracePeriodCalls: 3, sessionLimitUsd: 1.0, + responseCache: false, + responseCacheMaxEntries: 0, + responseCacheTtlMs: 0, ...overrides, }; } diff --git a/installer/README.md b/installer/README.md index 83da87b5..263ae0a4 100644 --- a/installer/README.md +++ b/installer/README.md @@ -1,6 +1,6 @@ # Mayros Installer Infrastructure -Cross-platform installer build scripts for Mayros v0.3.1. +Cross-platform installer build scripts for Mayros v0.3.2. ## Directory Structure @@ -75,7 +75,7 @@ cd installer\windows .\build-installer.ps1 -SkipDownload ``` -Output: `installer/windows/output/mayros-0.3.1-setup.exe` +Output: `installer/windows/output/mayros-0.3.2-setup.exe` ### macOS DMG @@ -87,7 +87,7 @@ cd installer/macos ./build-dmg.sh --sign "Developer ID Application: Apilium Technologies" ``` -Output: `installer/macos/output/Mayros-0.3.1.dmg` +Output: `installer/macos/output/Mayros-0.3.2.dmg` ### Linux AppImage @@ -99,7 +99,7 @@ cd installer/linux ./build-appimage.sh --arch arm64 ``` -Output: `installer/linux/output/Mayros-0.3.1-x86_64.AppImage` +Output: `installer/linux/output/Mayros-0.3.2-x86_64.AppImage` ### Linux .deb Package @@ -111,17 +111,17 @@ cd installer/linux ./build-deb.sh --arch arm64 ``` -Output: `installer/linux/output/mayros_0.3.1_amd64.deb` +Output: `installer/linux/output/mayros_0.3.2_amd64.deb` ## Bundled Components Each installer packages three components: -| Component | Version | Source | -|-----------------|---------|---------------------------------------| -| Mayros CLI | 0.3.1 | npm: `@apilium/mayros` | -| Node.js | 22.16.0 | nodejs.org (portable/binary) | -| AIngle Cortex | 0.6.3 | GitHub: `ApiliumCode/aingle` releases | +| Component | Version | Source | +| ------------- | ------- | ------------------------------------- | +| Mayros CLI | 0.3.2 | npm: `@apilium/mayros` | +| Node.js | 22.16.0 | nodejs.org (portable/binary) | +| AIngle Cortex | 0.6.3 | GitHub: `ApiliumCode/aingle` releases | ## What the Installers Do diff --git a/installer/assets/dmg-background.png b/installer/assets/dmg-background.png new file mode 100644 index 00000000..67326fff Binary files /dev/null and b/installer/assets/dmg-background.png differ diff --git a/installer/assets/dmg-background@2x.png b/installer/assets/dmg-background@2x.png new file mode 100644 index 00000000..d5feb5f6 Binary files /dev/null and b/installer/assets/dmg-background@2x.png differ diff --git a/installer/assets/mayros.icns b/installer/assets/mayros.icns new file mode 100644 index 00000000..00e34a89 Binary files /dev/null and b/installer/assets/mayros.icns differ diff --git a/installer/linux/build-appimage.sh b/installer/linux/build-appimage.sh index f2d114c4..d5d9a40a 100644 --- a/installer/linux/build-appimage.sh +++ b/installer/linux/build-appimage.sh @@ -160,12 +160,31 @@ if [[ ! -f "$CLI" ]]; then bash "$HERE/usr/lib/mayros/install-cli.sh" fi -# Minimal config: gateway.mode=local + auth.mode=none (portal wizard configures auth later) -CONFIG_FILE="$HOME/.mayros/mayros.json" -if [[ -f "$CONFIG_FILE" ]]; then - "$NODE" -e "const fs=require('fs');const f='$CONFIG_FILE';const c=JSON.parse(fs.readFileSync(f,'utf8'));if(!c.gateway)c.gateway={};if(!c.gateway.mode)c.gateway.mode='local';if(!c.gateway.auth)c.gateway.auth={};if(!c.gateway.auth.mode)c.gateway.auth.mode='none';fs.writeFileSync(f,JSON.stringify(c,null,2));" 2>/dev/null || true -else - echo '{"gateway":{"mode":"local","auth":{"mode":"none"}}}' > "$CONFIG_FILE" +# Minimal config: gateway.mode=local + auth.mode=none +MAYROS_DIR="$HOME/.mayros" +mkdir -p "$MAYROS_DIR" +CONFIG_FILE="$MAYROS_DIR/mayros.json" +"$NODE" -e " + const fs = require('fs'); + const f = '$CONFIG_FILE'; + let c = {}; + try { c = JSON.parse(fs.readFileSync(f, 'utf8')); } catch {} + if (!c.gateway) c.gateway = {}; + if (!c.gateway.mode) c.gateway.mode = 'local'; + if (!c.gateway.auth) c.gateway.auth = {}; + c.gateway.auth.mode = 'none'; + fs.writeFileSync(f, JSON.stringify(c, null, 2) + '\n'); +" 2>/dev/null || echo '{"gateway":{"mode":"local","auth":{"mode":"none"}}}' > "$CONFIG_FILE" + +# Bootstrap workspace templates +TEMPLATES="$HERE/usr/lib/mayros/node_modules/@apilium/mayros/docs/reference/templates" +WORKSPACE="$MAYROS_DIR/workspace" +if [[ -d "$TEMPLATES" ]]; then + mkdir -p "$WORKSPACE" + for tmpl in AGENTS.md MAYROS.md TOOLS.md IDENTITY.md USER.md HOPE.md BOOTSTRAP.md; do + [[ -f "$TEMPLATES/$tmpl" ]] && [[ ! -f "$WORKSPACE/$tmpl" ]] && cp "$TEMPLATES/$tmpl" "$WORKSPACE/$tmpl" + done + [[ ! -f "$WORKSPACE/MEMORY.md" ]] && echo "# Memory" > "$WORKSPACE/MEMORY.md" fi # Start Cortex if not running and wait for it @@ -184,7 +203,8 @@ done # Start gateway if not running if ! pgrep -f "mayros gateway" >/dev/null 2>&1; then echo "Starting Mayros Gateway..." - "$NODE" "$CLI" gateway start --background 2>/dev/null & + "$NODE" "$CLI" gateway install 2>/dev/null || true + "$NODE" "$CLI" gateway start 2>/dev/null & fi # If launched without args (e.g., from desktop), open portal diff --git a/installer/linux/build-deb.sh b/installer/linux/build-deb.sh index d421e7fd..5fc5c11c 100644 --- a/installer/linux/build-deb.sh +++ b/installer/linux/build-deb.sh @@ -111,6 +111,7 @@ After=network.target [Service] Type=simple +WorkingDirectory=/opt/mayros/lib/node_modules/@apilium/mayros ExecStart=/opt/mayros/bin/mayros gateway start --foreground Restart=on-failure RestartSec=5 @@ -153,28 +154,51 @@ exec /opt/mayros/node/bin/node /opt/mayros/lib/node_modules/@apilium/mayros/dist EOF chmod +x /opt/mayros/bin/mayros -# Minimal config: gateway.mode=local + auth.mode=none (portal wizard configures auth later) +# Resolve user home directory if [ -n "$SUDO_USER" ]; then MAYROS_DIR="/home/$SUDO_USER/.mayros" - su - "$SUDO_USER" -c "mkdir -p '$MAYROS_DIR'" - CONFIG_FILE="$MAYROS_DIR/mayros.json" - if [ -f "$CONFIG_FILE" ]; then - su - "$SUDO_USER" -c "/opt/mayros/node/bin/node -e \"const fs=require('fs');const f='$CONFIG_FILE';const c=JSON.parse(fs.readFileSync(f,'utf8'));if(!c.gateway)c.gateway={};if(!c.gateway.mode)c.gateway.mode='local';if(!c.gateway.auth)c.gateway.auth={};if(!c.gateway.auth.mode)c.gateway.auth.mode='none';fs.writeFileSync(f,JSON.stringify(c,null,2));\"" || true - else - su - "$SUDO_USER" -c "echo '{\"gateway\":{\"mode\":\"local\",\"auth\":{\"mode\":\"none\"}}}' > '$CONFIG_FILE'" - fi + RUN_AS="su - $SUDO_USER -c" +else + MAYROS_DIR="$HOME/.mayros" + RUN_AS="bash -c" +fi +mkdir -p "$MAYROS_DIR" + +# Minimal config: gateway.mode=local + auth.mode=none +CONFIG_FILE="$MAYROS_DIR/mayros.json" +/opt/mayros/node/bin/node -e " + const fs = require('fs'); + const f = '$CONFIG_FILE'; + let c = {}; + try { c = JSON.parse(fs.readFileSync(f, 'utf8')); } catch {} + if (!c.gateway) c.gateway = {}; + if (!c.gateway.mode) c.gateway.mode = 'local'; + if (!c.gateway.auth) c.gateway.auth = {}; + c.gateway.auth.mode = 'none'; + fs.writeFileSync(f, JSON.stringify(c, null, 2) + '\n'); +" 2>/dev/null || echo '{"gateway":{"mode":"local","auth":{"mode":"none"}}}' > "$CONFIG_FILE" + +# Bootstrap workspace templates so the agent can start +TEMPLATES="/opt/mayros/lib/node_modules/@apilium/mayros/docs/reference/templates" +WORKSPACE="$MAYROS_DIR/workspace" +if [ -d "$TEMPLATES" ]; then + mkdir -p "$WORKSPACE" + for tmpl in AGENTS.md MAYROS.md TOOLS.md IDENTITY.md USER.md HOPE.md BOOTSTRAP.md; do + [ -f "$TEMPLATES/$tmpl" ] && [ ! -f "$WORKSPACE/$tmpl" ] && cp "$TEMPLATES/$tmpl" "$WORKSPACE/$tmpl" + done + [ ! -f "$WORKSPACE/MEMORY.md" ] && echo "# Memory" > "$WORKSPACE/MEMORY.md" +fi + +# Fix ownership if running as root +if [ -n "$SUDO_USER" ]; then + chown -R "$SUDO_USER:$(id -gn "$SUDO_USER")" "$MAYROS_DIR" +fi + +# Enable and start gateway service +if [ -n "$SUDO_USER" ]; then su - "$SUDO_USER" -c "systemctl --user daemon-reload" || true su - "$SUDO_USER" -c "systemctl --user enable mayros-gateway.service" || true su - "$SUDO_USER" -c "systemctl --user start mayros-gateway.service" || true -else - MAYROS_DIR="$HOME/.mayros" - mkdir -p "$MAYROS_DIR" - CONFIG_FILE="$MAYROS_DIR/mayros.json" - if [ -f "$CONFIG_FILE" ]; then - /opt/mayros/node/bin/node -e "const fs=require('fs');const f='$CONFIG_FILE';const c=JSON.parse(fs.readFileSync(f,'utf8'));if(!c.gateway)c.gateway={};if(!c.gateway.mode)c.gateway.mode='local';if(!c.gateway.auth)c.gateway.auth={};if(!c.gateway.auth.mode)c.gateway.auth.mode='none';fs.writeFileSync(f,JSON.stringify(c,null,2));" || true - else - echo '{"gateway":{"mode":"local","auth":{"mode":"none"}}}' > "$CONFIG_FILE" - fi fi echo "" diff --git a/installer/macos/build-dmg.sh b/installer/macos/build-dmg.sh index cb2272df..2d33db8d 100644 --- a/installer/macos/build-dmg.sh +++ b/installer/macos/build-dmg.sh @@ -131,156 +131,164 @@ CORTEX="$RESOURCES/bin/aingle-cortex" MAYROS_DIR="$HOME/.mayros" CLI="$MAYROS_DIR/lib/node_modules/@apilium/mayros/dist/index.js" LOG="$MAYROS_DIR/install.log" -SETUP_SCRIPT="$MAYROS_DIR/.mayros-setup.sh" export PATH="$RESOURCES/bin:$RESOURCES/node/bin:$PATH" mkdir -p "$MAYROS_DIR/bin" -# If first launch, open a visible Terminal window so user sees progress -if [[ ! -f "$CLI" ]]; then - # Create the setup script that Terminal.app will run - cat > "$SETUP_SCRIPT" </dev/null \ + || osascript -e "display notification \"$1\" with title \"Mayros\"" 2>/dev/null +} +_fail() { + osascript -e "display dialog \"$1\" with title \"Mayros\" buttons {\"OK\"} default button \"OK\" with icon stop" 2>/dev/null + exit 1 +} +_log() { echo "$1" >> "$LOG"; } -# Step 1: Install CLI -echo " [1/6] Installing Mayros CLI..." -echo " This may take 1-2 minutes." -echo "" -"$NPM" install -g @apilium/mayros@latest --prefix "$MAYROS_DIR" --force --no-fund --no-audit 2>&1 | tail -5 +# ── First-time setup ── +# The actual work runs HERE in bash (no zsh/oh-my-zsh/fish dependency). +# A Terminal window opens as a read-only observer showing progress via +# tail -f on the log. If Terminal fails to open, setup still completes. if [[ ! -f "$CLI" ]]; then - echo "" - echo " ERROR: Installation failed." - echo " Check your internet connection and try again." - echo "" - read -p " Press Enter to close..." - exit 1 -fi -echo " ✓ Mayros CLI installed" -echo "" -# Step 2: Setup PATH -echo " [2/6] Configuring terminal PATH..." -SHELL_PROFILE="" -[[ -f "\$HOME/.zshrc" ]] && SHELL_PROFILE="\$HOME/.zshrc" -[[ -z "\$SHELL_PROFILE" && -f "\$HOME/.bash_profile" ]] && SHELL_PROFILE="\$HOME/.bash_profile" -[[ -z "\$SHELL_PROFILE" && -f "\$HOME/.bashrc" ]] && SHELL_PROFILE="\$HOME/.bashrc" -if [[ -n "\$SHELL_PROFILE" ]] && ! grep -q '.mayros/bin' "\$SHELL_PROFILE" 2>/dev/null; then - printf '\n# Mayros CLI\nexport PATH="\$HOME/.mayros/bin:\$PATH"\n' >> "\$SHELL_PROFILE" -fi -echo " ✓ PATH configured" -echo "" + # Prepare log + echo "[$(date -Iseconds)] First-launch setup starting" > "$LOG" -# Step 3: Copy Cortex -echo " [3/6] Setting up AIngle Cortex..." -if [[ ! -f "$MAYROS_DIR/bin/aingle-cortex" ]]; then - cp "$CORTEX" "$MAYROS_DIR/bin/aingle-cortex" - chmod +x "$MAYROS_DIR/bin/aingle-cortex" -fi -echo " ✓ Cortex ready" -echo "" + _notify "Installing Mayros CLI... (1-2 minutes)" + + # ── Step 1: Install CLI ── + _log " [1/6] Installing Mayros CLI..." + _log " This may take 1-2 minutes." + _log "" + # Prefer bundled tarball (includes all local changes); fall back to npm registry + LOCAL_TGZ="$RESOURCES/mayros-local.tgz" + if [[ -f "$LOCAL_TGZ" ]]; then + "$NPM" install -g "$LOCAL_TGZ" --prefix "$MAYROS_DIR" --force --no-fund --no-audit >> "$LOG" 2>&1 + fi + if [[ ! -f "$CLI" ]]; then + "$NPM" install -g @apilium/mayros@latest --prefix "$MAYROS_DIR" --force --no-fund --no-audit >> "$LOG" 2>&1 + fi + if [[ ! -f "$CLI" ]]; then + _log "" + _log " ERROR: Installation failed." + _log " Check your internet connection and try again." + _fail "Mayros installation failed. Check ~/.mayros/install.log for details." + fi + _log " Done." + _log "" + + # ── Step 2: Setup PATH ── + _log " [2/6] Configuring terminal PATH..." + SHELL_PROFILE="" + [[ -f "$HOME/.zshrc" ]] && SHELL_PROFILE="$HOME/.zshrc" + [[ -z "$SHELL_PROFILE" && -f "$HOME/.bash_profile" ]] && SHELL_PROFILE="$HOME/.bash_profile" + [[ -z "$SHELL_PROFILE" && -f "$HOME/.bashrc" ]] && SHELL_PROFILE="$HOME/.bashrc" + if [[ -n "$SHELL_PROFILE" ]] && ! grep -q '.mayros/bin' "$SHELL_PROFILE" 2>/dev/null; then + printf '\n# Mayros CLI\nexport PATH="$HOME/.mayros/bin:$PATH"\n' >> "$SHELL_PROFILE" + fi + _log " Done." + _log "" + + # ── Step 3: Copy Cortex binary ── + _log " [3/6] Setting up AIngle Cortex..." + if [[ ! -f "$MAYROS_DIR/bin/aingle-cortex" ]]; then + cp "$CORTEX" "$MAYROS_DIR/bin/aingle-cortex" + chmod +x "$MAYROS_DIR/bin/aingle-cortex" + fi + _log " Done." + _log "" -# Step 4: Create CLI wrapper -echo " [4/6] Creating CLI wrapper..." -cat > "$MAYROS_DIR/bin/mayros" <<'WRAP' + # ── Step 4: Create CLI wrapper ── + _log " [4/6] Creating CLI wrapper..." + cat > "$MAYROS_DIR/bin/mayros" <<'WRAP' #!/usr/bin/env bash -MAYROS_DIR="\$HOME/.mayros" -NODE="\$MAYROS_DIR/node/bin/node" -[[ ! -f "\$NODE" ]] && NODE="/Applications/Mayros.app/Contents/Resources/node/bin/node" -CLI="\$MAYROS_DIR/lib/node_modules/@apilium/mayros/dist/index.js" -[[ ! -f "\$CLI" ]] && CLI="\$MAYROS_DIR/node_modules/@apilium/mayros/dist/index.js" -if [[ ! -f "\$CLI" ]]; then +MAYROS_DIR="$HOME/.mayros" +NODE="$MAYROS_DIR/node/bin/node" +[[ ! -f "$NODE" ]] && NODE="/Applications/Mayros.app/Contents/Resources/node/bin/node" +CLI="$MAYROS_DIR/lib/node_modules/@apilium/mayros/dist/index.js" +[[ ! -f "$CLI" ]] && CLI="$MAYROS_DIR/node_modules/@apilium/mayros/dist/index.js" +if [[ ! -f "$CLI" ]]; then echo "Mayros not installed. Open Mayros.app or: npm install -g @apilium/mayros" exit 1 fi -exec "\$NODE" "\$CLI" "\$@" +exec "$NODE" "$CLI" "$@" WRAP -chmod +x "$MAYROS_DIR/bin/mayros" - -# Link node for terminal use -if [[ ! -d "$MAYROS_DIR/node" ]]; then - if [[ -d "/Applications/Mayros.app/Contents/Resources/node" ]]; then - ln -sf "/Applications/Mayros.app/Contents/Resources/node" "$MAYROS_DIR/node" - else - cp -R "$RESOURCES/node" "$MAYROS_DIR/node" + chmod +x "$MAYROS_DIR/bin/mayros" + if [[ ! -d "$MAYROS_DIR/node" ]]; then + if [[ -d "/Applications/Mayros.app/Contents/Resources/node" ]]; then + ln -sf "/Applications/Mayros.app/Contents/Resources/node" "$MAYROS_DIR/node" + else + cp -R "$RESOURCES/node" "$MAYROS_DIR/node" + fi fi -fi -echo " ✓ CLI wrapper created" -echo "" + _log " Done." + _log "" -# Step 5: Configure gateway (skip onboard so portal wizard shows) -echo " [5/6] Running initial configuration..." -# Minimal config: gateway.mode=local + auth.mode=none (portal wizard configures auth later) -CONFIG_FILE="$MAYROS_DIR/mayros.json" -if [[ -f "\$CONFIG_FILE" ]]; then + # ── Step 5: Write config + bootstrap workspace ── + _log " [5/6] Running initial configuration..." + CONFIG_FILE="$MAYROS_DIR/mayros.json" "$NODE" -e " const fs = require('fs'); - const f = '\$CONFIG_FILE'; - const c = JSON.parse(fs.readFileSync(f, 'utf8')); + const f = '$CONFIG_FILE'; + let c = {}; + try { c = JSON.parse(fs.readFileSync(f, 'utf8')); } catch {} if (!c.gateway) c.gateway = {}; if (!c.gateway.mode) c.gateway.mode = 'local'; if (!c.gateway.auth) c.gateway.auth = {}; - if (!c.gateway.auth.mode) c.gateway.auth.mode = 'none'; - fs.writeFileSync(f, JSON.stringify(c, null, 2)); - " 2>/dev/null || true -else - echo '{\"gateway\":{\"mode\":\"local\",\"auth\":{\"mode\":\"none\"}}}' > "\$CONFIG_FILE" -fi -echo " ✓ Configuration complete" -echo "" - -# Step 6: Start services -echo " [6/6] Starting services..." -if ! pgrep -f "aingle-cortex" >/dev/null 2>&1; then - "$MAYROS_DIR/bin/aingle-cortex" --port 19090 >> "$LOG" 2>&1 & -fi -echo -n " Waiting for Cortex." -for i in \$(seq 1 20); do - curl -s --max-time 2 "http://127.0.0.1:19090/health" >/dev/null 2>&1 && break - echo -n "." - sleep 1 -done -echo " ✓" - -if ! pgrep -f "mayros gateway" >/dev/null 2>&1; then - "$NODE" "$CLI" gateway install 2>/dev/null || true - "$NODE" "$CLI" gateway start 2>/dev/null & -fi -echo -n " Waiting for Gateway." -for i in \$(seq 1 30); do - curl -s --max-time 2 "http://127.0.0.1:18789/health" >/dev/null 2>&1 && break - echo -n "." - sleep 1 -done -echo " ✓" - -echo "" -echo " =====================================" -echo " Mayros is ready!" -echo " Opening dashboard..." -echo " =====================================" -echo "" -echo " You can now use 'mayros' from any" -echo " terminal (open a new tab first)." -echo "" -echo " Try: mayros code" -echo "" + c.gateway.auth.mode = 'none'; + fs.writeFileSync(f, JSON.stringify(c, null, 2) + '\n'); + " >> "$LOG" 2>&1 || echo '{"gateway":{"mode":"local","auth":{"mode":"none"}}}' > "$CONFIG_FILE" + + # Bootstrap workspace with templates so the agent can start + WORKSPACE="$MAYROS_DIR/workspace" + TEMPLATES="$MAYROS_DIR/lib/node_modules/@apilium/mayros/docs/reference/templates" + if [[ -d "$TEMPLATES" ]]; then + mkdir -p "$WORKSPACE" + for tmpl in AGENTS.md MAYROS.md TOOLS.md IDENTITY.md USER.md HOPE.md BOOTSTRAP.md; do + [[ -f "$TEMPLATES/$tmpl" ]] && [[ ! -f "$WORKSPACE/$tmpl" ]] && cp "$TEMPLATES/$tmpl" "$WORKSPACE/$tmpl" + done + [[ ! -f "$WORKSPACE/MEMORY.md" ]] && echo "# Memory" > "$WORKSPACE/MEMORY.md" + fi + _log " Done." + _log "" -# Open portal -open "http://127.0.0.1:18789" + # ── Step 6: Start services ── + _log " [6/6] Starting services..." -sleep 3 -rm -f "$SETUP_SCRIPT" -SETUP - chmod +x "$SETUP_SCRIPT" + if ! pgrep -f "aingle-cortex" >/dev/null 2>&1; then + "$MAYROS_DIR/bin/aingle-cortex" --port 19090 >> "$LOG" 2>&1 & + fi + _log " Waiting for Cortex..." + for i in $(seq 1 20); do + curl -s --max-time 2 "http://127.0.0.1:19090/health" >/dev/null 2>&1 && break + sleep 1 + done + _log " Cortex ready." - # Open Terminal.app with the setup script (user sees everything) - open -a Terminal "$SETUP_SCRIPT" + if ! pgrep -f "mayros gateway" >/dev/null 2>&1; then + "$NODE" "$CLI" gateway install >> "$LOG" 2>&1 || true + "$NODE" "$CLI" gateway start >> "$LOG" 2>&1 & + fi + _log " Waiting for Gateway..." + for i in $(seq 1 30); do + curl -s --max-time 2 "http://127.0.0.1:18789/health" >/dev/null 2>&1 && break + sleep 1 + done + _log " Gateway ready." + _log "" + _log " =====================================" + _log " Mayros is ready!" + _log " =====================================" + _log "" + _log " You can now use 'mayros' from any" + _log " terminal (open a new tab first)." + _log "" + _log " Try: mayros code" + _log "" + + open "http://127.0.0.1:18789" exit 0 fi @@ -288,25 +296,32 @@ fi # Start Cortex if not running if ! pgrep -f "aingle-cortex" >/dev/null 2>&1; then - "$MAYROS_DIR/bin/aingle-cortex" --port 19090 &>/dev/null & - for i in $(seq 1 15); do - curl -s --max-time 2 "http://127.0.0.1:19090/health" >/dev/null 2>&1 && break - sleep 1 - done + "$MAYROS_DIR/bin/aingle-cortex" --port 19090 >>"$LOG" 2>&1 & fi +# Always wait for Cortex to be healthy +for i in $(seq 1 15); do + curl -s --max-time 2 "http://127.0.0.1:19090/health" >/dev/null 2>&1 && break + sleep 1 +done # Start Gateway if not running if ! pgrep -f "mayros gateway" >/dev/null 2>&1; then - "$NODE" "$CLI" gateway install 2>/dev/null || true - "$NODE" "$CLI" gateway start 2>/dev/null & - for i in $(seq 1 20); do - curl -s --max-time 2 "http://127.0.0.1:18789/health" >/dev/null 2>&1 && break - sleep 1 - done + "$NODE" "$CLI" gateway install >>"$LOG" 2>&1 || true + "$NODE" "$CLI" gateway start >>"$LOG" 2>&1 & fi +# Wait for Gateway to be healthy, then open portal +GATEWAY_OK=false +for i in $(seq 1 30); do + if curl -s --max-time 2 "http://127.0.0.1:18789/health" >/dev/null 2>&1; then + GATEWAY_OK=true + break + fi + sleep 1 +done -# Open the portal -exec "$NODE" "$CLI" dashboard +if [[ "$GATEWAY_OK" == "true" ]]; then + open "http://127.0.0.1:18789" +fi LAUNCHER chmod +x "$APP_DIR/Contents/MacOS/mayros-launcher" @@ -331,18 +346,22 @@ rm -f "$DMG_PATH" echo "==> Creating DMG..." if command -v create-dmg &>/dev/null; then - create-dmg \ - --volname "Mayros ${MAYROS_VERSION}" \ - --volicon "$ASSETS_DIR/mayros.icns" \ - --window-pos 200 120 \ - --window-size 600 400 \ - --icon-size 100 \ - --icon "Mayros.app" 150 200 \ - --app-drop-link 450 200 \ - --hide-extension "Mayros.app" \ - --no-internet-enable \ - "$DMG_PATH" \ - "$APP_DIR" + DMG_BG="$ASSETS_DIR/dmg-background.png" + DMG_ARGS=( + --volname "Mayros ${MAYROS_VERSION}" + --volicon "$ASSETS_DIR/mayros.icns" + --window-pos 200 120 + --window-size 660 400 + --icon-size 120 + --icon "Mayros.app" 175 230 + --app-drop-link 485 230 + --hide-extension "Mayros.app" + --no-internet-enable + ) + if [[ -f "$DMG_BG" ]]; then + DMG_ARGS+=(--background "$DMG_BG") + fi + create-dmg "${DMG_ARGS[@]}" "$DMG_PATH" "$APP_DIR" else echo " -> create-dmg not found, using hdiutil fallback" TEMP_DMG="$BUILD_DIR/temp.dmg" diff --git a/installer/shared/brand.json b/installer/shared/brand.json index f9cd959e..678b95ce 100644 --- a/installer/shared/brand.json +++ b/installer/shared/brand.json @@ -16,7 +16,7 @@ "textMuted": "#52525b", "white": "#ffffff" }, - "version": "0.3.1", + "version": "0.3.2", "cortexVersion": "0.6.3", "nodeVersion": "22", "gatewayPort": 18789, diff --git a/installer/shared/bundle-manifest.json b/installer/shared/bundle-manifest.json index 7b546bd8..29ba6fae 100644 --- a/installer/shared/bundle-manifest.json +++ b/installer/shared/bundle-manifest.json @@ -1,5 +1,5 @@ { - "mayros": "0.3.1", + "mayros": "0.3.2", "node": "22.16.0", "cortex": "0.6.3", "platforms": { diff --git a/installer/shared/download-deps.sh b/installer/shared/download-deps.sh index ff177089..0ef8f50a 100644 --- a/installer/shared/download-deps.sh +++ b/installer/shared/download-deps.sh @@ -5,7 +5,7 @@ set -euo pipefail -MAYROS_VERSION="0.3.1" +MAYROS_VERSION="0.3.2" NODE_VERSION="22.16.0" CORTEX_VERSION="0.6.3" CORTEX_REPO="ApiliumCode/aingle" diff --git a/installer/windows/build-installer.ps1 b/installer/windows/build-installer.ps1 index 73f634d2..3aced25c 100644 --- a/installer/windows/build-installer.ps1 +++ b/installer/windows/build-installer.ps1 @@ -125,10 +125,19 @@ if errorlevel 1 ( echo Configuring Mayros for first use... :: Minimal config: gateway.mode=local + auth.mode=none (portal wizard configures auth later) set "CONFIG_FILE=%MAYROS_DIR%\mayros.json" -if not exist "%CONFIG_FILE%" ( +"%MAYROS_DIR%\node\node.exe" -e "const fs=require('fs');const p=require('path');const f=p.join(process.env.LOCALAPPDATA,'Mayros','mayros.json');let c={};try{c=JSON.parse(fs.readFileSync(f,'utf8'))}catch{}if(!c.gateway)c.gateway={};if(!c.gateway.mode)c.gateway.mode='local';if(!c.gateway.auth)c.gateway.auth={};c.gateway.auth.mode='none';fs.writeFileSync(f,JSON.stringify(c,null,2)+'\n');" 2>nul +if errorlevel 1 ( echo {"gateway":{"mode":"local","auth":{"mode":"none"}}} > "%CONFIG_FILE%" -) else ( - "%MAYROS_DIR%\node\node.exe" -e "const fs=require('fs');const f=process.argv[1];const c=JSON.parse(fs.readFileSync(f,'utf8'));if(!c.gateway)c.gateway={};if(!c.gateway.mode)c.gateway.mode='local';if(!c.gateway.auth)c.gateway.auth={};if(!c.gateway.auth.mode)c.gateway.auth.mode='none';fs.writeFileSync(f,JSON.stringify(c,null,2));" "%CONFIG_FILE%" 2>nul +) +:: Bootstrap workspace templates +set "TEMPLATES=%MAYROS_DIR%\lib\node_modules\@apilium\mayros\docs\reference\templates" +set "WORKSPACE=%MAYROS_DIR%\workspace" +if exist "%TEMPLATES%" ( + if not exist "%WORKSPACE%" mkdir "%WORKSPACE%" + for %%F in (AGENTS.md MAYROS.md TOOLS.md IDENTITY.md USER.md HOPE.md BOOTSTRAP.md) do ( + if exist "%TEMPLATES%\%%F" if not exist "%WORKSPACE%\%%F" copy "%TEMPLATES%\%%F" "%WORKSPACE%\%%F" >nul + ) + if not exist "%WORKSPACE%\MEMORY.md" echo # Memory > "%WORKSPACE%\MEMORY.md" ) echo. echo Starting Cortex... @@ -145,6 +154,7 @@ timeout /t 1 /nobreak >nul goto cortexwait :startgateway echo Starting Gateway... +"%MAYROS_DIR%\mayros.cmd" gateway install 2>nul start "" "%MAYROS_DIR%\mayros.cmd" gateway start echo Waiting for gateway to be ready... set TRIES=0 diff --git a/installer/windows/mayros-setup.nsi b/installer/windows/mayros-setup.nsi index 25ce89b4..d5f9109b 100644 --- a/installer/windows/mayros-setup.nsi +++ b/installer/windows/mayros-setup.nsi @@ -2,7 +2,7 @@ ; Installs Node.js portable + Cortex binary, then runs npm install for Mayros !ifndef MAYROS_VERSION - !define MAYROS_VERSION "0.3.1" + !define MAYROS_VERSION "0.3.2" !endif !ifndef NODE_VERSION !define NODE_VERSION "22" diff --git a/package.json b/package.json index ed9a7290..031db48a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros", - "version": "0.3.1", + "version": "0.3.2", "description": "Multi-channel AI agent framework with knowledge graph, MCP support, and coding CLI", "keywords": [ "agent", @@ -160,7 +160,7 @@ }, "dependencies": { "@agentclientprotocol/sdk": "0.14.1", - "@aws-sdk/client-bedrock": "^3.995.0", + "@aws-sdk/client-bedrock": "^3.1019.0", "@buape/carbon": "0.0.0-beta-20260216184201", "@clack/prompts": "^1.0.1", "@discordjs/voice": "^0.19.0", @@ -213,7 +213,7 @@ "tslog": "^4.10.2", "undici": "^7.24.2", "ws": "^8.19.0", - "yaml": "^2.8.2", + "yaml": "^2.8.3", "zod": "^4.3.6" }, "devDependencies": { @@ -251,7 +251,7 @@ "overrides": { "hono": "4.12.7", "@hono/node-server": "1.19.10", - "fast-xml-parser": "5.3.8", + "fast-xml-parser": ">=5.5.7", "request": "npm:@cypress/request@3.0.10", "request-promise": "npm:@cypress/request-promise@5.0.0", "form-data": "2.5.4", @@ -261,7 +261,13 @@ "tar": "7.5.11", "file-type": ">=21.3.1", "tough-cookie": "4.1.3", - "@discordjs/opus": "npm:opusscript@0.0.8" + "@discordjs/opus": "npm:opusscript@0.0.8", + "undici": ">=7.24.0", + "picomatch": ">=4.0.4", + "path-to-regexp": ">=8.4.0", + "brace-expansion": ">=5.0.5", + "music-metadata": ">=11.12.3", + "yaml": ">=2.8.3" }, "onlyBuiltDependencies": [ "@lydell/node-pty", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9a022cb4..39bc2a0e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,7 +7,7 @@ settings: overrides: hono: 4.12.7 '@hono/node-server': 1.19.10 - fast-xml-parser: 5.3.8 + fast-xml-parser: '>=5.5.7' request: npm:@cypress/request@3.0.10 request-promise: npm:@cypress/request-promise@5.0.0 form-data: 2.5.4 @@ -18,6 +18,12 @@ overrides: file-type: '>=21.3.1' tough-cookie: 4.1.3 '@discordjs/opus': npm:opusscript@0.0.8 + undici: '>=7.24.0' + picomatch: '>=4.0.4' + path-to-regexp: '>=8.4.0' + brace-expansion: '>=5.0.5' + music-metadata: '>=11.12.3' + yaml: '>=2.8.3' importers: @@ -27,8 +33,8 @@ importers: specifier: 0.14.1 version: 0.14.1(zod@4.3.6) '@aws-sdk/client-bedrock': - specifier: ^3.995.0 - version: 3.997.0 + specifier: ^3.1019.0 + version: 3.1019.0 '@buape/carbon': specifier: 0.0.0-beta-20260216184201 version: 0.0.0-beta-20260216184201(hono@4.12.7)(opusscript@0.0.8)(opusscript@0.0.8) @@ -186,14 +192,14 @@ importers: specifier: ^4.10.2 version: 4.10.2 undici: - specifier: ^7.24.2 + specifier: '>=7.24.0' version: 7.24.2 ws: specifier: ^8.19.0 version: 8.19.0 yaml: - specifier: ^2.8.2 - version: 2.8.2 + specifier: '>=2.8.3' + version: 2.8.3 zod: specifier: ^4.3.6 version: 4.3.6 @@ -230,7 +236,7 @@ importers: version: 7.0.0-dev.20260221.1 '@vitest/coverage-v8': specifier: ^4.0.18 - version: 4.0.18(@vitest/browser@4.0.18(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.0)(@vitest/browser-playwright@4.0.18)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)))(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.0)(@vitest/browser-playwright@4.0.18)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + version: 4.0.18(@vitest/browser@4.0.18(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.0.18))(vitest@4.0.18) lit: specifier: ^3.3.2 version: 3.3.2 @@ -257,7 +263,7 @@ importers: version: 5.9.3 vitest: specifier: ^4.0.18 - version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.0)(@vitest/browser-playwright@4.0.18)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) + version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.0)(@vitest/browser-playwright@4.0.18)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3) extensions/agent-mesh: dependencies: @@ -285,7 +291,7 @@ importers: dependencies: '@apilium/mayros': specifier: '>=0.1.0' - version: 0.1.1(@napi-rs/canvas@0.1.95)(@types/express@5.0.6)(hono@4.12.7)(node-llama-cpp@3.17.1(typescript@5.9.3)) + version: 0.1.1(@napi-rs/canvas@0.1.97)(@types/express@5.0.6)(hono@4.12.7)(node-llama-cpp@3.17.1(typescript@5.9.3)) extensions/ci-plugin: dependencies: @@ -336,7 +342,7 @@ importers: dependencies: '@apilium/mayros': specifier: '>=0.1.0' - version: 0.1.1(@napi-rs/canvas@0.1.95)(@types/express@5.0.6)(hono@4.12.7)(node-llama-cpp@3.17.1(typescript@5.9.3)) + version: 0.1.1(@napi-rs/canvas@0.1.97)(@types/express@5.0.6)(hono@4.12.7)(node-llama-cpp@3.17.1(typescript@5.9.3)) '@opentelemetry/api': specifier: ^1.9.0 version: 1.9.0 @@ -375,7 +381,7 @@ importers: dependencies: '@apilium/mayros': specifier: '>=0.1.0' - version: 0.1.1(@napi-rs/canvas@0.1.95)(@types/express@5.0.6)(hono@4.12.7)(node-llama-cpp@3.17.1(typescript@5.9.3)) + version: 0.1.1(@napi-rs/canvas@0.1.97)(@types/express@5.0.6)(hono@4.12.7)(node-llama-cpp@3.17.1(typescript@5.9.3)) extensions/eruberu: dependencies: @@ -391,7 +397,7 @@ importers: dependencies: '@apilium/mayros': specifier: '>=0.1.0' - version: 0.1.1(@napi-rs/canvas@0.1.95)(@types/express@5.0.6)(hono@4.12.7)(node-llama-cpp@3.17.1(typescript@5.9.3)) + version: 0.1.1(@napi-rs/canvas@0.1.97)(@types/express@5.0.6)(hono@4.12.7)(node-llama-cpp@3.17.1(typescript@5.9.3)) '@larksuiteoapi/node-sdk': specifier: ^1.59.0 version: 1.59.0 @@ -464,7 +470,7 @@ importers: dependencies: '@apilium/mayros': specifier: '>=0.1.0' - version: 0.1.1(@napi-rs/canvas@0.1.95)(@types/express@5.0.6)(hono@4.12.7)(node-llama-cpp@3.17.1(typescript@5.9.3)) + version: 0.1.1(@napi-rs/canvas@0.1.97)(@types/express@5.0.6)(hono@4.12.7)(node-llama-cpp@3.17.1(typescript@5.9.3)) zod: specifier: ^4.3.6 version: 4.3.6 @@ -505,7 +511,7 @@ importers: dependencies: '@apilium/mayros': specifier: '>=0.1.0' - version: 0.1.1(@napi-rs/canvas@0.1.95)(@types/express@5.0.6)(hono@4.12.7)(node-llama-cpp@3.17.1(typescript@5.9.3)) + version: 0.1.1(@napi-rs/canvas@0.1.97)(@types/express@5.0.6)(hono@4.12.7)(node-llama-cpp@3.17.1(typescript@5.9.3)) '@sinclair/typebox': specifier: 0.34.48 version: 0.34.48 @@ -520,7 +526,7 @@ importers: dependencies: '@apilium/mayros': specifier: '>=0.1.0' - version: 0.1.1(@napi-rs/canvas@0.1.95)(@types/express@5.0.6)(hono@4.12.7)(node-llama-cpp@3.17.1(typescript@5.9.3)) + version: 0.1.1(@napi-rs/canvas@0.1.97)(@types/express@5.0.6)(hono@4.12.7)(node-llama-cpp@3.17.1(typescript@5.9.3)) '@matrix-org/matrix-sdk-crypto-nodejs': specifier: ^0.4.0 version: 0.4.0 @@ -531,8 +537,8 @@ importers: specifier: 14.1.1 version: 14.1.1 music-metadata: - specifier: ^11.12.1 - version: 11.12.1 + specifier: '>=11.12.3' + version: 11.12.3 zod: specifier: ^4.3.6 version: 4.3.6 @@ -605,7 +611,7 @@ importers: dependencies: '@apilium/mayros': specifier: '>=0.1.0' - version: 0.1.1(@napi-rs/canvas@0.1.95)(@types/express@5.0.6)(hono@4.12.7)(node-llama-cpp@3.17.1(typescript@5.9.3)) + version: 0.1.1(@napi-rs/canvas@0.1.97)(@types/express@5.0.6)(hono@4.12.7)(node-llama-cpp@3.17.1(typescript@5.9.3)) '@microsoft/agents-hosting': specifier: ^1.3.1 version: 1.3.1 @@ -617,7 +623,7 @@ importers: dependencies: '@apilium/mayros': specifier: '>=0.1.0' - version: 0.1.1(@napi-rs/canvas@0.1.95)(@types/express@5.0.6)(hono@4.12.7)(node-llama-cpp@3.17.1(typescript@5.9.3)) + version: 0.1.1(@napi-rs/canvas@0.1.97)(@types/express@5.0.6)(hono@4.12.7)(node-llama-cpp@3.17.1(typescript@5.9.3)) zod: specifier: ^4.3.6 version: 4.3.6 @@ -626,7 +632,7 @@ importers: dependencies: '@apilium/mayros': specifier: '>=0.1.0' - version: 0.1.1(@napi-rs/canvas@0.1.95)(@types/express@5.0.6)(hono@4.12.7)(node-llama-cpp@3.17.1(typescript@5.9.3)) + version: 0.1.1(@napi-rs/canvas@0.1.97)(@types/express@5.0.6)(hono@4.12.7)(node-llama-cpp@3.17.1(typescript@5.9.3)) nostr-tools: specifier: ^2.23.1 version: 2.23.1(typescript@5.9.3) @@ -754,7 +760,7 @@ importers: dependencies: '@apilium/mayros': specifier: '>=0.1.0' - version: 0.1.1(@napi-rs/canvas@0.1.95)(@types/express@5.0.6)(hono@4.12.7)(node-llama-cpp@3.17.1(typescript@5.9.3)) + version: 0.1.1(@napi-rs/canvas@0.1.97)(@types/express@5.0.6)(hono@4.12.7)(node-llama-cpp@3.17.1(typescript@5.9.3)) '@sinclair/typebox': specifier: 0.34.48 version: 0.34.48 @@ -775,16 +781,16 @@ importers: dependencies: '@apilium/mayros': specifier: '>=0.1.0' - version: 0.1.1(@napi-rs/canvas@0.1.95)(@types/express@5.0.6)(hono@4.12.7)(node-llama-cpp@3.17.1(typescript@5.9.3)) + version: 0.1.1(@napi-rs/canvas@0.1.97)(@types/express@5.0.6)(hono@4.12.7)(node-llama-cpp@3.17.1(typescript@5.9.3)) undici: - specifier: 7.22.0 - version: 7.22.0 + specifier: '>=7.24.0' + version: 7.24.2 extensions/zalouser: dependencies: '@apilium/mayros': specifier: '>=0.1.0' - version: 0.1.1(@napi-rs/canvas@0.1.95)(@types/express@5.0.6)(hono@4.12.7)(node-llama-cpp@3.17.1(typescript@5.9.3)) + version: 0.1.1(@napi-rs/canvas@0.1.97)(@types/express@5.0.6)(hono@4.12.7)(node-llama-cpp@3.17.1(typescript@5.9.3)) '@sinclair/typebox': specifier: 0.34.48 version: 0.34.48 @@ -813,7 +819,7 @@ importers: version: 0.25.12 vitest: specifier: ^3.0.0 - version: 3.2.4(@types/node@25.3.0)(@vitest/browser@4.0.18(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.0)(@vitest/browser-playwright@4.0.18)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)))(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) + version: 3.2.4(@types/node@25.3.0)(@vitest/browser@4.0.18(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.0.18))(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3) ui: dependencies: @@ -843,17 +849,17 @@ importers: version: 0.21.1(signal-polyfill@0.2.2) vite: specifier: 7.3.1 - version: 7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) + version: 7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3) devDependencies: '@vitest/browser-playwright': specifier: 4.0.18 - version: 4.0.18(playwright@1.58.2)(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.0.18) + version: 4.0.18(playwright@1.58.2)(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.0.18) playwright: specifier: ^1.58.2 version: 1.58.2 vitest: specifier: 4.0.18 - version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.0)(@vitest/browser-playwright@4.0.18)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) + version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.0)(@vitest/browser-playwright@4.0.18)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3) packages: @@ -900,44 +906,44 @@ packages: resolution: {integrity: sha512-yEgCc/HvI7dLeXQLCuc4cnbzwE/NbNpKX8NmSSWTy3jnjiMZwrNKdHMBgPoNvaEb0klHhnTyO+JCHVVCPI/eYw==} engines: {node: '>=20.0.0'} - '@aws-sdk/client-bedrock@3.997.0': - resolution: {integrity: sha512-PMRqxSzfkQHbU7ADVlT4jYLB7beFQWLXN9CGI9D9P8eqCIaDVv3YxTfwcT3FcBVucqktdTBTEowhvKn0whr/rA==} + '@aws-sdk/client-bedrock@3.1019.0': + resolution: {integrity: sha512-GWe/mqO8CQBmco6M0f9n7DZsItmxTarwvo1zxuVLiqFb8ondXDU+Ry/TNn6FXWHm6YRLm8DUPwaSmcckdGyZNQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/core@3.973.13': - resolution: {integrity: sha512-eCFiLyBhJR7c/i8hZOETdzj2wsLFzi2L/w9/jajOgwmGqO8xrUExqkTZqdjROkwU62owqeqSuw4sIzlCv1E/ww==} + '@aws-sdk/core@3.973.25': + resolution: {integrity: sha512-TNrx7eq6nKNOO62HWPqoBqPLXEkW6nLZQGwjL6lq1jZtigWYbK1NbCnT7mKDzbLMHZfuOECUt3n6CzxjUW9HWQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-env@3.972.11': - resolution: {integrity: sha512-hbyoFuVm3qOAGfIPS9t7jCs8GFLFoaOs8ZmYp/chqciuHDyEGv+J365ip7YSvXSrxxUbeW9NyB1hTLt40NBMRg==} + '@aws-sdk/credential-provider-env@3.972.23': + resolution: {integrity: sha512-EamaclJcCEaPHp6wiVknNMM2RlsPMjAHSsYSFLNENBM8Wz92QPc6cOn3dif6vPDQt0Oo4IEghDy3NMDCzY/IvA==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-http@3.972.13': - resolution: {integrity: sha512-a864QxQWFkdCZ5wQF0QZNKTbqAc/DFQNeARp4gOyZZdql5RHjj4CppUSfwAzS9cpw2IPY3eeJjWqLZ1QiDB/6w==} + '@aws-sdk/credential-provider-http@3.972.25': + resolution: {integrity: sha512-qPymamdPcLp6ugoVocG1y5r69ScNiRzb0hogX25/ij+Wz7c7WnsgjLTaz7+eB5BfRxeyUwuw5hgULMuwOGOpcw==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-ini@3.972.11': - resolution: {integrity: sha512-kvPFn626ABLzxmjFMoqMRtmFKMeiUdWPhwxhmuPu233tqHnNuXzHv0MtrZlkzHd+rwlh9j0zCbQo89B54wIazQ==} + '@aws-sdk/credential-provider-ini@3.972.26': + resolution: {integrity: sha512-xKxEAMuP6GYx2y5GET+d3aGEroax3AgGfwBE65EQAUe090lzyJ/RzxPX9s8v7Z6qAk0XwfQl+LrmH05X7YvTeg==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-login@3.972.11': - resolution: {integrity: sha512-stdy09EpBTmsxGiXe1vB5qtXNww9wact36/uWLlSV0/vWbCOUAY2JjhPXoDVLk8n+E6r0M5HeZseLk+iTtifxg==} + '@aws-sdk/credential-provider-login@3.972.26': + resolution: {integrity: sha512-EFcM8RM3TUxnZOfMJo++3PnyxFu1fL/huzmn3Vh+8IWRgqZawUD3cRwwOr+/4bE9DpyHaLOWFAjY0lfK5X9ZkQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-node@3.972.12': - resolution: {integrity: sha512-gMWGnHbNSKWRj+PAiuSg0EDpEwpyIgk0v9U6EuZ1C/5/BUv25Way+E+UFB7r+YYkscuBJMJ+ai8E2K0Q8dx50g==} + '@aws-sdk/credential-provider-node@3.972.27': + resolution: {integrity: sha512-jXpxSolfFnPVj6GCTtx3xIdWNoDR7hYC/0SbetGZxOC9UnNmipHeX1k6spVstf7eWJrMhXNQEgXC0pD1r5tXIg==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-process@3.972.11': - resolution: {integrity: sha512-B049fvbv41vf0Fs5bCtbzHpruBDp61sPiFDxUmkAJ/zvgSAturpj2rqzV1rj2clg4mb44Uxp9rgpcODexNFlFA==} + '@aws-sdk/credential-provider-process@3.972.23': + resolution: {integrity: sha512-IL/TFW59++b7MpHserjUblGrdP5UXy5Ekqqx1XQkERXBFJcZr74I7VaSrQT5dxdRMU16xGK4L0RQ5fQG1pMgnA==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-sso@3.972.11': - resolution: {integrity: sha512-vX9z8skN8vPtamVWmSCm4KQohub+1uMuRzIo4urZ2ZUMBAl1bqHatVD/roCb3qRfAyIGvZXCA/AWS03BQRMyCQ==} + '@aws-sdk/credential-provider-sso@3.972.26': + resolution: {integrity: sha512-c6ghvRb6gTlMznWhGxn/bpVCcp0HRaz4DobGVD9kI4vwHq186nU2xN/S7QGkm0lo0H2jQU8+dgpUFLxfTcwCOg==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-web-identity@3.972.11': - resolution: {integrity: sha512-VR2Ju/QBdOjnWNIYuxRml63eFDLGc6Zl8aDwLi1rzgWo3rLBgtaWhWVBAijhVXzyPdQIOqdL8hvll5ybqumjeQ==} + '@aws-sdk/credential-provider-web-identity@3.972.26': + resolution: {integrity: sha512-cXcS3+XD3iwhoXkM44AmxjmbcKueoLCINr1e+IceMmCySda5ysNIfiGBGe9qn5EMiQ9Jd7pP0AGFtcd6OV3Lvg==} engines: {node: '>=20.0.0'} '@aws-sdk/eventstream-handler-node@3.972.7': @@ -948,44 +954,48 @@ packages: resolution: {integrity: sha512-0t+2Dn46cRE9iu5ynUXINBtR0wNHi/Jz3FbrqS5k3dGot2O7Ln1xCqXbJUAtGM5ZAqN77SbnpETAgVWC84DeoA==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-host-header@3.972.4': - resolution: {integrity: sha512-4q2Vg7/zOB10huDBLjzzTwVjBpG22X3J3ief2XrJEgTaANZrNfA3/cGbCVNAibSbu/nIYA7tDk8WCdsIzDDc4Q==} + '@aws-sdk/middleware-host-header@3.972.8': + resolution: {integrity: sha512-wAr2REfKsqoKQ+OkNqvOShnBoh+nkPurDKW7uAeVSu6kUECnWlSJiPvnoqxGlfousEY/v9LfS9sNc46hjSYDIQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-logger@3.972.4': - resolution: {integrity: sha512-xFqPvTysuZAHSkdygT+ken/5rzkR7fhOoDPejAJQslZpp0XBepmCJnDOqA57ERtCTBpu8wpjTFI1ETd4S0AXEw==} + '@aws-sdk/middleware-logger@3.972.8': + resolution: {integrity: sha512-CWl5UCM57WUFaFi5kB7IBY1UmOeLvNZAZ2/OZ5l20ldiJ3TiIz1pC65gYj8X0BCPWkeR1E32mpsCk1L1I4n+lA==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-recursion-detection@3.972.4': - resolution: {integrity: sha512-tVbRaayUZ7y2bOb02hC3oEPTqQf2A0HpPDwdMl1qTmye/q8Mq1F1WiIoFkQwG/YQFvbyErYIDMbYzIlxzzLtjQ==} + '@aws-sdk/middleware-recursion-detection@3.972.9': + resolution: {integrity: sha512-/Wt5+CT8dpTFQxEJ9iGy/UGrXr7p2wlIOEHvIr/YcHYByzoLjrqkYqXdJjd9UIgWjv7eqV2HnFJen93UTuwfTQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-user-agent@3.972.13': - resolution: {integrity: sha512-p1kVYbzBxRmhuOHoL/ANJPCedqUxnVgkEjxPoxt5pQv/yzppHM7aBWciYEE9TZY59M421D3GjLfZIZBoEFboVQ==} + '@aws-sdk/middleware-user-agent@3.972.26': + resolution: {integrity: sha512-AilFIh4rI/2hKyyGN6XrB0yN96W2o7e7wyrPWCM6QjZM1mcC/pVkW3IWWRvuBWMpVP8Fg+rMpbzeLQ6dTM4gig==} engines: {node: '>=20.0.0'} '@aws-sdk/middleware-websocket@3.972.8': resolution: {integrity: sha512-KPUXz8lRw73Rh12/QkELxiryC9Wi9Ah1xNzFe2Vtbz2/81c2ZA0yM8er+u0iCF/SRMMhDQshLcmRNgn/ueA+gA==} engines: {node: '>= 14.0.0'} - '@aws-sdk/nested-clients@3.996.1': - resolution: {integrity: sha512-XHVLFRGkuV2gh2uwBahCt65ALMb5wMpqplXEZIvFnWOCPlk60B7h7M5J9Em243K8iICDiWY6KhBEqVGfjTqlLA==} + '@aws-sdk/nested-clients@3.996.16': + resolution: {integrity: sha512-L7Qzoj/qQU1cL5GnYLQP5LbI+wlLCLoINvcykR3htKcQ4tzrPf2DOs72x933BM7oArYj1SKrkb2lGlsJHIic3g==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/region-config-resolver@3.972.10': + resolution: {integrity: sha512-1dq9ToC6e070QvnVhhbAs3bb5r6cQ10gTVc6cyRV5uvQe7P138TV2uG2i6+Yok4bAkVAcx5AqkTEBUvWEtBlsQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/region-config-resolver@3.972.4': - resolution: {integrity: sha512-3GrJYv5eI65oCKveBZP7Q246dVP+tqeys9aKMB0dfX1glUWfppWlxIu52derqdNb9BX9lxYmeiaBcBIqOAYSgQ==} + '@aws-sdk/token-providers@3.1019.0': + resolution: {integrity: sha512-OF+2RfRmUKyjzrRWlDcyju3RBsuqcrYDQ8TwrJg8efcOotMzuZN4U9mpVTIdATpmEc4lWNZBMSjPzrGm6JPnAQ==} engines: {node: '>=20.0.0'} '@aws-sdk/token-providers@3.997.0': resolution: {integrity: sha512-UdG36F7lU9aTqGFRieEyuRUJlgEJBqKeKKekC0esH21DbUSKhPR1kZBah214kYasIaWe1hLJLaqUigoTa5hZAQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/types@3.973.2': - resolution: {integrity: sha512-maTZwGsALtnAw4TJr/S6yERAosTwPduu0XhUV+SdbvRZtCOgSgk1ttL2R0XYzvkYSpvbtJocn77tBXq2AKglBw==} + '@aws-sdk/types@3.973.6': + resolution: {integrity: sha512-Atfcy4E++beKtwJHiDln2Nby8W/mam64opFPTiHEqgsthqeydFS1pY+OUlN1ouNOmf8ArPU/6cDS65anOP3KQw==} engines: {node: '>=20.0.0'} - '@aws-sdk/util-endpoints@3.996.1': - resolution: {integrity: sha512-7cJyd+M5i0IoqWkJa1KFx8KNCGIx+Ywu+lT53KpqX7ReVwz03DCKUqvZ/y65vdKwo9w9/HptSAeLDluO5MpGIg==} + '@aws-sdk/util-endpoints@3.996.5': + resolution: {integrity: sha512-Uh93L5sXFNbyR5sEPMzUU8tJ++Ku97EY4udmC01nB8Zu+xfBPwpIwJ6F7snqQeq8h2pf+8SGN5/NoytfKgYPIw==} engines: {node: '>=20.0.0'} '@aws-sdk/util-format-url@3.972.4': @@ -996,11 +1006,11 @@ packages: resolution: {integrity: sha512-H1onv5SkgPBK2P6JR2MjGgbOnttoNzSPIRoeZTNPZYyaplwGg50zS3amXvXqF0/qfXpWEC9rLWU564QTB9bSog==} engines: {node: '>=20.0.0'} - '@aws-sdk/util-user-agent-browser@3.972.4': - resolution: {integrity: sha512-GHb+8XHv6hfLWKQKAKaSOm+vRvogg07s+FWtbR3+eCXXPSFn9XVmiYF4oypAxH7dGIvoxkVG/buHEnzYukyJiA==} + '@aws-sdk/util-user-agent-browser@3.972.8': + resolution: {integrity: sha512-B3KGXJviV2u6Cdw2SDY2aDhoJkVfY/Q/Trwk2CMSkikE1Oi6gRzxhvhIfiRpHfmIsAhV4EA54TVEX8K6CbHbkA==} - '@aws-sdk/util-user-agent-node@3.972.12': - resolution: {integrity: sha512-c1n3wBK6te+Vd9qU86nF8AsYuiBsxLn0AADGWyFX7vEADr3btaAg5iPQT6GYj6rvzSOEVVisvaAatOWInlJUbQ==} + '@aws-sdk/util-user-agent-node@3.973.12': + resolution: {integrity: sha512-8phW0TS8ntENJgDcFewYT/Q8dOmarpvSxEjATu2GUBAutiHr++oEGCiBUwxslCMNvwW2cAPZNT53S/ym8zm/gg==} engines: {node: '>=20.0.0'} peerDependencies: aws-crt: '>=1.0.0' @@ -1008,12 +1018,12 @@ packages: aws-crt: optional: true - '@aws-sdk/xml-builder@3.972.6': - resolution: {integrity: sha512-YrXu+UnfC8IdARa4ZkrpcyuRmA/TVgYW6Lcdtvi34NQgRjM1hTirNirN+rGb+s/kNomby8oJiIAu0KNbiZC7PA==} + '@aws-sdk/xml-builder@3.972.16': + resolution: {integrity: sha512-iu2pyvaqmeatIJLURLqx9D+4jKAdTH20ntzB6BFwjyN7V960r4jK32mx0Zf7YbtOYAbmbtQfDNuL60ONinyw7A==} engines: {node: '>=20.0.0'} - '@aws/lambda-invoke-store@0.2.3': - resolution: {integrity: sha512-oLvsaPMTBejkkmHhjf09xTgk71mOqyr/409NKhRIL08If7AhVfUsJhVsx386uJaqNd42v9kWamQ9lFbkoC2dYw==} + '@aws/lambda-invoke-store@0.2.4': + resolution: {integrity: sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ==} engines: {node: '>=18.0.0'} '@azure/abort-controller@2.1.2': @@ -1082,8 +1092,8 @@ packages: resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} engines: {node: '>=18'} - '@borewit/text-codec@0.2.1': - resolution: {integrity: sha512-k7vvKPbf7J2fZ5klGRD9AeKfUvojuZIQ3BT5u7Jfv+puwXkUBUT5PVyMDfJZpy30CBDXGMgw7fguK/lpOMBvgw==} + '@borewit/text-codec@0.2.2': + resolution: {integrity: sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==} '@buape/carbon@0.0.0-beta-20260216184201': resolution: {integrity: sha512-u5mgYcigfPVqT7D9gVTGd+3YSflTreQmrWog7ORbb0z5w9eT8ft4rJOdw9fGwr75zMu9kXpSBaAcY2eZoJFSdA==} @@ -1764,6 +1774,7 @@ packages: '@lancedb/lancedb@0.26.2': resolution: {integrity: sha512-umk4WMCTwJntLquwvUbpqE+TXREolcQVL9MHcxr8EhRjsha88+ATJ4QuS/hpyiE1CG3R/XcgrMgJAGkziPC/gA==} engines: {node: '>= 18'} + cpu: [x64, arm64] os: [darwin, linux, win32] peerDependencies: apache-arrow: '>=15.0.0 <=18.1.0' @@ -1930,70 +1941,140 @@ packages: cpu: [arm64] os: [android] + '@napi-rs/canvas-android-arm64@0.1.97': + resolution: {integrity: sha512-V1c/WVw+NzH8vk7ZK/O8/nyBSCQimU8sfMsB/9qeSvdkGKNU7+mxy/bIF0gTgeBFmHpj30S4E9WHMSrxXGQuVQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + '@napi-rs/canvas-darwin-arm64@0.1.95': resolution: {integrity: sha512-F7jT0Syu+B9DGBUBcMk3qCRIxAWiDXmvEjamwbYfbZl7asI1pmXZUnCOoIu49Wt0RNooToYfRDxU9omD6t5Xuw==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] + '@napi-rs/canvas-darwin-arm64@0.1.97': + resolution: {integrity: sha512-ok+SCEF4YejcxuJ9Rm+WWunHHpf2HmiPxfz6z1a/NFQECGXtsY7A4B8XocK1LmT1D7P174MzwPF9Wy3AUAwEPw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + '@napi-rs/canvas-darwin-x64@0.1.95': resolution: {integrity: sha512-54eb2Ho15RDjYGXO/harjRznBrAvu+j5nQ85Z4Qd6Qg3slR8/Ja+Yvvy9G4yo7rdX6NR9GPkZeSTf2UcKXwaXw==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] + '@napi-rs/canvas-darwin-x64@0.1.97': + resolution: {integrity: sha512-PUP6e6/UGlclUvAQNnuXCcnkpdUou6VYZfQOQxExLp86epOylmiwLkqXIvpFmjoTEDmPmXrI+coL/9EFU1gKPA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + '@napi-rs/canvas-linux-arm-gnueabihf@0.1.95': resolution: {integrity: sha512-hYaLCSLx5bmbnclzQc3ado3PgZ66blJWzjXp0wJmdwpr/kH+Mwhj6vuytJIomgksyJoCdIqIa4N6aiqBGJtJ5Q==} engines: {node: '>= 10'} cpu: [arm] os: [linux] + '@napi-rs/canvas-linux-arm-gnueabihf@0.1.97': + resolution: {integrity: sha512-XyXH2L/cic8eTNtbrXCcvqHtMX/nEOxN18+7rMrAM2XtLYC/EB5s0wnO1FsLMWmK+04ZSLN9FBGipo7kpIkcOw==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + '@napi-rs/canvas-linux-arm64-gnu@0.1.95': resolution: {integrity: sha512-J7VipONahKsmScPZsipHVQBqpbZx4favaD8/enWzzlGcjiwycOoymL7f4tNeqdjK0su19bDOUt6mjp9gsPWYlw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] + '@napi-rs/canvas-linux-arm64-gnu@0.1.97': + resolution: {integrity: sha512-Kuq/M3djq0K8ktgz6nPlK7Ne5d4uWeDxPpyKWOjWDK2RIOhHVtLtyLiJw2fuldw7Vn4mhw05EZXCEr4Q76rs9w==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + '@napi-rs/canvas-linux-arm64-musl@0.1.95': resolution: {integrity: sha512-PXy0UT1J/8MPG8UAkWp6Fd51ZtIZINFzIjGH909JjQrtCuJf3X6nanHYdz1A+Wq9o4aoPAw1YEUpFS1lelsVlg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] + '@napi-rs/canvas-linux-arm64-musl@0.1.97': + resolution: {integrity: sha512-kKmSkQVnWeqg7qdsiXvYxKhAFuHz3tkBjW/zyQv5YKUPhotpaVhpBGv5LqCngzyuRV85SXoe+OFj+Tv0a0QXkQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + '@napi-rs/canvas-linux-riscv64-gnu@0.1.95': resolution: {integrity: sha512-2IzCkW2RHRdcgF9W5/plHvYFpc6uikyjMb5SxjqmNxfyDFz9/HB89yhi8YQo0SNqrGRI7yBVDec7Pt+uMyRWsg==} engines: {node: '>= 10'} cpu: [riscv64] os: [linux] + '@napi-rs/canvas-linux-riscv64-gnu@0.1.97': + resolution: {integrity: sha512-Jc7I3A51jnEOIAXeLsN/M/+Z28LUeakcsXs07FLq9prXc0eYOtVwsDEv913Gr+06IRo34gJJVgT0TXvmz+N2VA==} + engines: {node: '>= 10'} + cpu: [riscv64] + os: [linux] + '@napi-rs/canvas-linux-x64-gnu@0.1.95': resolution: {integrity: sha512-OV/ol/OtcUr4qDhQg8G7SdViZX8XyQeKpPsVv/j3+7U178FGoU4M+yIocdVo1ih/A8GQ63+LjF4jDoEjaVU8Pw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + '@napi-rs/canvas-linux-x64-gnu@0.1.97': + resolution: {integrity: sha512-iDUBe7AilfuBSRbSa8/IGX38Mf+iCSBqoVKLSQ5XaY2JLOaqz1TVyPFEyIck7wT6mRQhQt5sN6ogfjIDfi74tg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + '@napi-rs/canvas-linux-x64-musl@0.1.95': resolution: {integrity: sha512-Z5KzqBK/XzPz5+SFHKz7yKqClEQ8pOiEDdgk5SlphBLVNb8JFIJkxhtJKSvnJyHh2rjVgiFmvtJzMF0gNwwKyQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + '@napi-rs/canvas-linux-x64-musl@0.1.97': + resolution: {integrity: sha512-AKLFd/v0Z5fvgqBDqhvqtAdx+fHMJ5t9JcUNKq4FIZ5WH+iegGm8HPdj00NFlCSnm83Fp3Ln8I2f7uq1aIiWaA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + '@napi-rs/canvas-win32-arm64-msvc@0.1.95': resolution: {integrity: sha512-aj0YbRpe8qVJ4OzMsK7NfNQePgcf9zkGFzNZ9mSuaxXzhpLHmlF2GivNdCdNOg8WzA/NxV6IU4c5XkXadUMLeA==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] + '@napi-rs/canvas-win32-arm64-msvc@0.1.97': + resolution: {integrity: sha512-u883Yr6A6fO7Vpsy9YE4FVCIxzzo5sO+7pIUjjoDLjS3vQaNMkVzx5bdIpEL+ob+gU88WDK4VcxYMZ6nmnoX9A==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + '@napi-rs/canvas-win32-x64-msvc@0.1.95': resolution: {integrity: sha512-GA8leTTCfdjuHi8reICTIxU0081PhXvl3lzIniLUjeLACx9GubUiyzkwFb+oyeKLS5IAGZFLKnzAf4wm2epRlA==} engines: {node: '>= 10'} cpu: [x64] os: [win32] + '@napi-rs/canvas-win32-x64-msvc@0.1.97': + resolution: {integrity: sha512-sWtD2EE3fV0IzN+iiQUqr/Q1SwqWhs2O1FKItFlxtdDkikpEj5g7DKQpY3x55H/MAOnL8iomnlk3mcEeGiUMoQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + '@napi-rs/canvas@0.1.95': resolution: {integrity: sha512-lkg23ge+rgyhgUwXmlbkPEhuhHq/hUi/gXKH+4I7vO+lJrbNfEYcQdJLIGjKyXLQzgFiiyDAwh5vAe/tITAE+w==} engines: {node: '>= 10'} + '@napi-rs/canvas@0.1.97': + resolution: {integrity: sha512-8cFniXvrIEnVwuNSRCW9wirRZbHvrD3JVujdS2P5n5xiJZNZMOZcfOvJ1pb66c7jXMKHHglJEDVJGbm8XWFcXQ==} + engines: {node: '>= 10'} + '@napi-rs/wasm-runtime@1.1.1': resolution: {integrity: sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==} @@ -2875,20 +2956,20 @@ packages: resolution: {integrity: sha512-RoygyteJeFswxDPJjUMESn9dldWVMD2xUcHHd9DenVavSfVC6FeVnSdDerOO7m8LLvw4Q132nQM4hX8JiF7dng==} engines: {node: '>= 18', npm: '>= 8.6.0'} - '@smithy/abort-controller@4.2.10': - resolution: {integrity: sha512-qocxM/X4XGATqQtUkbE9SPUB6wekBi+FyJOMbPj0AhvyvFGYEmOlz6VB22iMePCQsFmMIvFSeViDvA7mZJG47g==} + '@smithy/abort-controller@4.2.12': + resolution: {integrity: sha512-xolrFw6b+2iYGl6EcOL7IJY71vvyZ0DJ3mcKtpykqPe2uscwtzDZJa1uVQXyP7w9Dd+kGwYnPbMsJrGISKiY/Q==} engines: {node: '>=18.0.0'} - '@smithy/config-resolver@4.4.9': - resolution: {integrity: sha512-ejQvXqlcU30h7liR9fXtj7PIAau1t/sFbJpgWPfiYDs7zd16jpH0IsSXKcba2jF6ChTXvIjACs27kNMc5xxE2Q==} + '@smithy/config-resolver@4.4.13': + resolution: {integrity: sha512-iIzMC5NmOUP6WL6o8iPBjFhUhBZ9pPjpUpQYWMUFQqKyXXzOftbfK8zcQCz/jFV1Psmf05BK5ypx4K2r4Tnwdg==} engines: {node: '>=18.0.0'} - '@smithy/core@3.23.6': - resolution: {integrity: sha512-4xE+0L2NrsFKpEVFlFELkIHQddBvMbQ41LRIP74dGCXnY1zQ9DgksrBcRBDJT+iOzGy4VEJIeU3hkUK5mn06kg==} + '@smithy/core@3.23.12': + resolution: {integrity: sha512-o9VycsYNtgC+Dy3I0yrwCqv9CWicDnke0L7EVOrZtJpjb2t0EjaEofmMrYc0T1Kn3yk32zm6cspxF9u9Bj7e5w==} engines: {node: '>=18.0.0'} - '@smithy/credential-provider-imds@4.2.10': - resolution: {integrity: sha512-3bsMLJJLTZGZqVGGeBVFfLzuRulVsGTj12BzRKODTHqUABpIr0jMN1vN3+u6r2OfyhAQ2pXaMZWX/swBK5I6PQ==} + '@smithy/credential-provider-imds@4.2.12': + resolution: {integrity: sha512-cr2lR792vNZcYMriSIj+Um3x9KWrjcu98kn234xA6reOAFMmbRpQMOv8KPgEmLLtx3eldU6c5wALKFqNOhugmg==} engines: {node: '>=18.0.0'} '@smithy/eventstream-codec@4.2.10': @@ -2911,167 +2992,175 @@ packages: resolution: {integrity: sha512-aArqzOEvcs2dK+xQVCgLbpJQGfZihw8SD4ymhkwNTtwKbnrzdhJsFDKuMQnam2kF69WzgJYOU5eJlCx+CA32bw==} engines: {node: '>=18.0.0'} - '@smithy/fetch-http-handler@5.3.11': - resolution: {integrity: sha512-wbTRjOxdFuyEg0CpumjZO0hkUl+fetJFqxNROepuLIoijQh51aMBmzFLfoQdwRjxsuuS2jizzIUTjPWgd8pd7g==} + '@smithy/fetch-http-handler@5.3.15': + resolution: {integrity: sha512-T4jFU5N/yiIfrtrsb9uOQn7RdELdM/7HbyLNr6uO/mpkj1ctiVs7CihVr51w4LyQlXWDpXFn4BElf1WmQvZu/A==} engines: {node: '>=18.0.0'} - '@smithy/hash-node@4.2.10': - resolution: {integrity: sha512-1VzIOI5CcsvMDvP3iv1vG/RfLJVVVc67dCRyLSB2Hn9SWCZrDO3zvcIzj3BfEtqRW5kcMg5KAeVf1K3dR6nD3w==} + '@smithy/hash-node@4.2.12': + resolution: {integrity: sha512-QhBYbGrbxTkZ43QoTPrK72DoYviDeg6YKDrHTMJbbC+A0sml3kSjzFtXP7BtbyJnXojLfTQldGdUR0RGD8dA3w==} engines: {node: '>=18.0.0'} - '@smithy/invalid-dependency@4.2.10': - resolution: {integrity: sha512-vy9KPNSFUU0ajFYk0sDZIYiUlAWGEAhRfehIr5ZkdFrRFTAuXEPUd41USuqHU6vvLX4r6Q9X7MKBco5+Il0Org==} + '@smithy/invalid-dependency@4.2.12': + resolution: {integrity: sha512-/4F1zb7Z8LOu1PalTdESFHR0RbPwHd3FcaG1sI3UEIriQTWakysgJr65lc1jj6QY5ye7aFsisajotH6UhWfm/g==} 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/is-array-buffer@4.2.1': - resolution: {integrity: sha512-Yfu664Qbf1B4IYIsYgKoABt010daZjkaCRvdU/sPnZG6TtHOB0md0RjNdLGzxe5UIdn9js4ftPICzmkRa9RJ4Q==} + '@smithy/is-array-buffer@4.2.2': + resolution: {integrity: sha512-n6rQ4N8Jj4YTQO3YFrlgZuwKodf4zUFs7EJIWH86pSCWBaAtAGBFfCM7Wx6D2bBJ2xqFNxGBSrUWswT3M0VJow==} engines: {node: '>=18.0.0'} - '@smithy/middleware-content-length@4.2.10': - resolution: {integrity: sha512-TQZ9kX5c6XbjhaEBpvhSvMEZ0klBs1CFtOdPFwATZSbC9UeQfKHPLPN9Y+I6wZGMOavlYTOlHEPDrt42PMSH9w==} + '@smithy/middleware-content-length@4.2.12': + resolution: {integrity: sha512-YE58Yz+cvFInWI/wOTrB+DbvUVz/pLn5mC5MvOV4fdRUc6qGwygyngcucRQjAhiCEbmfLOXX0gntSIcgMvAjmA==} engines: {node: '>=18.0.0'} - '@smithy/middleware-endpoint@4.4.20': - resolution: {integrity: sha512-9W6Np4ceBP3XCYAGLoMCmn8t2RRVzuD1ndWPLBbv7H9CrwM9Bprf6Up6BM9ZA/3alodg0b7Kf6ftBK9R1N04vw==} + '@smithy/middleware-endpoint@4.4.27': + resolution: {integrity: sha512-T3TFfUgXQlpcg+UdzcAISdZpj4Z+XECZ/cefgA6wLBd6V4lRi0svN2hBouN/be9dXQ31X4sLWz3fAQDf+nt6BA==} engines: {node: '>=18.0.0'} - '@smithy/middleware-retry@4.4.37': - resolution: {integrity: sha512-/1psZZllBBSQ7+qo5+hhLz7AEPGLx3Z0+e3ramMBEuPK2PfvLK4SrncDB9VegX5mBn+oP/UTDrM6IHrFjvX1ZA==} + '@smithy/middleware-retry@4.4.44': + resolution: {integrity: sha512-Y1Rav7m5CFRPQyM4CI0koD/bXjyjJu3EQxZZhtLGD88WIrBrQ7kqXM96ncd6rYnojwOo/u9MXu57JrEvu/nLrA==} engines: {node: '>=18.0.0'} - '@smithy/middleware-serde@4.2.11': - resolution: {integrity: sha512-STQdONGPwbbC7cusL60s7vOa6He6A9w2jWhoapL0mgVjmR19pr26slV+yoSP76SIssMTX/95e5nOZ6UQv6jolg==} + '@smithy/middleware-serde@4.2.15': + resolution: {integrity: sha512-ExYhcltZSli0pgAKOpQQe1DLFBLryeZ22605y/YS+mQpdNWekum9Ujb/jMKfJKgjtz1AZldtwA/wCYuKJgjjlg==} engines: {node: '>=18.0.0'} - '@smithy/middleware-stack@4.2.10': - resolution: {integrity: sha512-pmts/WovNcE/tlyHa8z/groPeOtqtEpp61q3W0nW1nDJuMq/x+hWa/OVQBtgU0tBqupeXq0VBOLA4UZwE8I0YA==} + '@smithy/middleware-stack@4.2.12': + resolution: {integrity: sha512-kruC5gRHwsCOuyCd4ouQxYjgRAym2uDlCvQ5acuMtRrcdfg7mFBg6blaxcJ09STpt3ziEkis6bhg1uwrWU7txw==} engines: {node: '>=18.0.0'} - '@smithy/node-config-provider@4.3.10': - resolution: {integrity: sha512-UALRbJtVX34AdP2VECKVlnNgidLHA2A7YgcJzwSBg1hzmnO/bZBHl/LDQQyYifzUwp1UOODnl9JJ3KNawpUJ9w==} + '@smithy/node-config-provider@4.3.12': + resolution: {integrity: sha512-tr2oKX2xMcO+rBOjobSwVAkV05SIfUKz8iI53rzxEmgW3GOOPOv0UioSDk+J8OpRQnpnhsO3Af6IEBabQBVmiw==} engines: {node: '>=18.0.0'} - '@smithy/node-http-handler@4.4.12': - resolution: {integrity: sha512-zo1+WKJkR9x7ZtMeMDAAsq2PufwiLDmkhcjpWPRRkmeIuOm6nq1qjFICSZbnjBvD09ei8KMo26BWxsu2BUU+5w==} + '@smithy/node-http-handler@4.5.0': + resolution: {integrity: sha512-Rnq9vQWiR1+/I6NZZMNzJHV6pZYyEHt2ZnuV3MG8z2NNenC4i/8Kzttz7CjZiHSmsN5frhXhg17z3Zqjjhmz1A==} engines: {node: '>=18.0.0'} - '@smithy/property-provider@4.2.10': - resolution: {integrity: sha512-5jm60P0CU7tom0eNrZ7YrkgBaoLFXzmqB0wVS+4uK8PPGmosSrLNf6rRd50UBvukztawZ7zyA8TxlrKpF5z9jw==} + '@smithy/property-provider@4.2.12': + resolution: {integrity: sha512-jqve46eYU1v7pZ5BM+fmkbq3DerkSluPr5EhvOcHxygxzD05ByDRppRwRPPpFrsFo5yDtCYLKu+kreHKVrvc7A==} engines: {node: '>=18.0.0'} - '@smithy/protocol-http@5.3.10': - resolution: {integrity: sha512-2NzVWpYY0tRdfeCJLsgrR89KE3NTWT2wGulhNUxYlRmtRmPwLQwKzhrfVaiNlA9ZpJvbW7cjTVChYKgnkqXj1A==} + '@smithy/protocol-http@5.3.12': + resolution: {integrity: sha512-fit0GZK9I1xoRlR4jXmbLhoN0OdEpa96ul8M65XdmXnxXkuMxM0Y8HDT0Fh0Xb4I85MBvBClOzgSrV1X2s1Hxw==} engines: {node: '>=18.0.0'} - '@smithy/querystring-builder@4.2.10': - resolution: {integrity: sha512-HeN7kEvuzO2DmAzLukE9UryiUvejD3tMp9a1D1NJETerIfKobBUCLfviP6QEk500166eD2IATaXM59qgUI+YDA==} + '@smithy/querystring-builder@4.2.12': + resolution: {integrity: sha512-6wTZjGABQufekycfDGMEB84BgtdOE/rCVTov+EDXQ8NHKTUNIp/j27IliwP7tjIU9LR+sSzyGBOXjeEtVgzCHg==} engines: {node: '>=18.0.0'} - '@smithy/querystring-parser@4.2.10': - resolution: {integrity: sha512-4Mh18J26+ao1oX5wXJfWlTT+Q1OpDR8ssiC9PDOuEgVBGloqg18Fw7h5Ct8DyT9NBYwJgtJ2nLjKKFU6RP1G1Q==} + '@smithy/querystring-parser@4.2.12': + resolution: {integrity: sha512-P2OdvrgiAKpkPNKlKUtWbNZKB1XjPxM086NeVhK+W+wI46pIKdWBe5QyXvhUm3MEcyS/rkLvY8rZzyUdmyDZBw==} engines: {node: '>=18.0.0'} - '@smithy/service-error-classification@4.2.10': - resolution: {integrity: sha512-0R/+/Il5y8nB/By90o8hy/bWVYptbIfvoTYad0igYQO5RefhNCDmNzqxaMx7K1t/QWo0d6UynqpqN5cCQt1MCg==} + '@smithy/service-error-classification@4.2.12': + resolution: {integrity: sha512-LlP29oSQN0Tw0b6D0Xo6BIikBswuIiGYbRACy5ujw/JgWSzTdYj46U83ssf6Ux0GyNJVivs2uReU8pt7Eu9okQ==} engines: {node: '>=18.0.0'} - '@smithy/shared-ini-file-loader@4.4.5': - resolution: {integrity: sha512-pHgASxl50rrtOztgQCPmOXFjRW+mCd7ALr/3uXNzRrRoGV5G2+78GOsQ3HlQuBVHCh9o6xqMNvlIKZjWn4Euug==} + '@smithy/shared-ini-file-loader@4.4.7': + resolution: {integrity: sha512-HrOKWsUb+otTeo1HxVWeEb99t5ER1XrBi/xka2Wv6NVmTbuCUC1dvlrksdvxFtODLBjsC+PHK+fuy2x/7Ynyiw==} engines: {node: '>=18.0.0'} - '@smithy/signature-v4@5.3.10': - resolution: {integrity: sha512-Wab3wW8468WqTKIxI+aZe3JYO52/RYT/8sDOdzkUhjnLakLe9qoQqIcfih/qxcF4qWEFoWBszY0mj5uxffaVXA==} + '@smithy/signature-v4@5.3.12': + resolution: {integrity: sha512-B/FBwO3MVOL00DaRSXfXfa/TRXRheagt/q5A2NM13u7q+sHS59EOVGQNfG7DkmVtdQm5m3vOosoKAXSqn/OEgw==} engines: {node: '>=18.0.0'} - '@smithy/smithy-client@4.12.0': - resolution: {integrity: sha512-R8bQ9K3lCcXyZmBnQqUZJF4ChZmtWT5NLi6x5kgWx5D+/j0KorXcA0YcFg/X5TOgnTCy1tbKc6z2g2y4amFupQ==} + '@smithy/smithy-client@4.12.7': + resolution: {integrity: sha512-q3gqnwml60G44FECaEEsdQMplYhDMZYCtYhMCzadCnRnnHIobZJjegmdoUo6ieLQlPUzvrMdIJUpx6DoPmzANQ==} engines: {node: '>=18.0.0'} - '@smithy/types@4.13.0': - resolution: {integrity: sha512-COuLsZILbbQsdrwKQpkkpyep7lCsByxwj7m0Mg5v66/ZTyenlfBc40/QFQ5chO0YN/PNEH1Bi3fGtfXPnYNeDw==} + '@smithy/types@4.13.1': + resolution: {integrity: sha512-787F3yzE2UiJIQ+wYW1CVg2odHjmaWLGksnKQHUrK/lYZSEcy1msuLVvxaR/sI2/aDe9U+TBuLsXnr3vod1g0g==} engines: {node: '>=18.0.0'} - '@smithy/url-parser@4.2.10': - resolution: {integrity: sha512-uypjF7fCDsRk26u3qHmFI/ePL7bxxB9vKkE+2WKEciHhz+4QtbzWiHRVNRJwU3cKhrYDYQE3b0MRFtqfLYdA4A==} + '@smithy/url-parser@4.2.12': + resolution: {integrity: sha512-wOPKPEpso+doCZGIlr+e1lVI6+9VAKfL4kZWFgzVgGWY2hZxshNKod4l2LXS3PRC9otH/JRSjtEHqQ/7eLciRA==} engines: {node: '>=18.0.0'} - '@smithy/util-base64@4.3.1': - resolution: {integrity: sha512-BKGuawX4Doq/bI/uEmg+Zyc36rJKWuin3py89PquXBIBqmbnJwBBsmKhdHfNEp0+A4TDgLmT/3MSKZ1SxHcR6w==} + '@smithy/util-base64@4.3.2': + resolution: {integrity: sha512-XRH6b0H/5A3SgblmMa5ErXQ2XKhfbQB+Fm/oyLZ2O2kCUrwgg55bU0RekmzAhuwOjA9qdN5VU2BprOvGGUkOOQ==} engines: {node: '>=18.0.0'} - '@smithy/util-body-length-browser@4.2.1': - resolution: {integrity: sha512-SiJeLiozrAoCrgDBUgsVbmqHmMgg/2bA15AzcbcW+zan7SuyAVHN4xTSbq0GlebAIwlcaX32xacnrG488/J/6g==} + '@smithy/util-body-length-browser@4.2.2': + resolution: {integrity: sha512-JKCrLNOup3OOgmzeaKQwi4ZCTWlYR5H4Gm1r2uTMVBXoemo1UEghk5vtMi1xSu2ymgKVGW631e2fp9/R610ZjQ==} engines: {node: '>=18.0.0'} - '@smithy/util-body-length-node@4.2.2': - resolution: {integrity: sha512-4rHqBvxtJEBvsZcFQSPQqXP2b/yy/YlB66KlcEgcH2WNoOKCKB03DSLzXmOsXjbl8dJ4OEYTn31knhdznwk7zw==} + '@smithy/util-body-length-node@4.2.3': + resolution: {integrity: sha512-ZkJGvqBzMHVHE7r/hcuCxlTY8pQr1kMtdsVPs7ex4mMU+EAbcXppfo5NmyxMYi2XU49eqaz56j2gsk4dHHPG/g==} 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-buffer-from@4.2.1': - resolution: {integrity: sha512-/swhmt1qTiVkaejlmMPPDgZhEaWb/HWMGRBheaxwuVkusp/z+ErJyQxO6kaXumOciZSWlmq6Z5mNylCd33X7Ig==} + '@smithy/util-buffer-from@4.2.2': + resolution: {integrity: sha512-FDXD7cvUoFWwN6vtQfEta540Y/YBe5JneK3SoZg9bThSoOAC/eGeYEua6RkBgKjGa/sz6Y+DuBZj3+YEY21y4Q==} engines: {node: '>=18.0.0'} - '@smithy/util-config-provider@4.2.1': - resolution: {integrity: sha512-462id/00U8JWFw6qBuTSWfN5TxOHvDu4WliI97qOIOnuC/g+NDAknTU8eoGXEPlLkRVgWEr03jJBLV4o2FL8+A==} + '@smithy/util-config-provider@4.2.2': + resolution: {integrity: sha512-dWU03V3XUprJwaUIFVv4iOnS1FC9HnMHDfUrlNDSh4315v0cWyaIErP8KiqGVbf5z+JupoVpNM7ZB3jFiTejvQ==} engines: {node: '>=18.0.0'} - '@smithy/util-defaults-mode-browser@4.3.36': - resolution: {integrity: sha512-R0smq7EHQXRVMxkAxtH5akJ/FvgAmNF6bUy/GwY/N20T4GrwjT633NFm0VuRpC+8Bbv8R9A0DoJ9OiZL/M3xew==} + '@smithy/util-defaults-mode-browser@4.3.43': + resolution: {integrity: sha512-Qd/0wCKMaXxev/z00TvNzGCH2jlKKKxXP1aDxB6oKwSQthe3Og2dMhSayGCnsma1bK/kQX1+X7SMP99t6FgiiQ==} engines: {node: '>=18.0.0'} - '@smithy/util-defaults-mode-node@4.2.39': - resolution: {integrity: sha512-otWuoDm35btJV1L8MyHrPl462B07QCdMTktKc7/yM+Psv6KbED/ziXiHnmr7yPHUjfIwE9S8Max0LO24Mo3ZVg==} + '@smithy/util-defaults-mode-node@4.2.47': + resolution: {integrity: sha512-qSRbYp1EQ7th+sPFuVcVO05AE0QH635hycdEXlpzIahqHHf2Fyd/Zl+8v0XYMJ3cgDVPa0lkMefU7oNUjAP+DQ==} engines: {node: '>=18.0.0'} - '@smithy/util-endpoints@3.3.1': - resolution: {integrity: sha512-xyctc4klmjmieQiF9I1wssBWleRV0RhJ2DpO8+8yzi2LO1Z+4IWOZNGZGNj4+hq9kdo+nyfrRLmQTzc16Op2Vg==} + '@smithy/util-endpoints@3.3.3': + resolution: {integrity: sha512-VACQVe50j0HZPjpwWcjyT51KUQ4AnsvEaQ2lKHOSL4mNLD0G9BjEniQ+yCt1qqfKfiAHRAts26ud7hBjamrwig==} engines: {node: '>=18.0.0'} '@smithy/util-hex-encoding@4.2.1': resolution: {integrity: sha512-c1hHtkgAWmE35/50gmdKajgGAKV3ePJ7t6UtEmpfCWJmQE9BQAQPz0URUVI89eSkcDqCtzqllxzG28IQoZPvwA==} engines: {node: '>=18.0.0'} - '@smithy/util-middleware@4.2.10': - resolution: {integrity: sha512-LxaQIWLp4y0r72eA8mwPNQ9va4h5KeLM0I3M/HV9klmFaY2kN766wf5vsTzmaOpNNb7GgXAd9a25P3h8T49PSA==} + '@smithy/util-hex-encoding@4.2.2': + resolution: {integrity: sha512-Qcz3W5vuHK4sLQdyT93k/rfrUwdJ8/HZ+nMUOyGdpeGA1Wxt65zYwi3oEl9kOM+RswvYq90fzkNDahPS8K0OIg==} engines: {node: '>=18.0.0'} - '@smithy/util-retry@4.2.10': - resolution: {integrity: sha512-HrBzistfpyE5uqTwiyLsFHscgnwB0kgv8vySp7q5kZ0Eltn/tjosaSGGDj/jJ9ys7pWzIP/icE2d+7vMKXLv7A==} + '@smithy/util-middleware@4.2.12': + resolution: {integrity: sha512-Er805uFUOvgc0l8nv0e0su0VFISoxhJ/AwOn3gL2NWNY2LUEldP5WtVcRYSQBcjg0y9NfG8JYrCJaYDpupBHJQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-retry@4.2.12': + resolution: {integrity: sha512-1zopLDUEOwumjcHdJ1mwBHddubYF8GMQvstVCLC54Y46rqoHwlIU+8ZzUeaBcD+WCJHyDGSeZ2ml9YSe9aqcoQ==} engines: {node: '>=18.0.0'} '@smithy/util-stream@4.5.15': resolution: {integrity: sha512-OlOKnaqnkU9X+6wEkd7mN+WB7orPbCVDauXOj22Q7VtiTkvy7ZdSsOg4QiNAZMgI4OkvNf+/VLUC3VXkxuWJZw==} engines: {node: '>=18.0.0'} - '@smithy/util-uri-escape@4.2.1': - resolution: {integrity: sha512-YmiUDn2eo2IOiWYYvGQkgX5ZkBSiTQu4FlDo5jNPpAxng2t6Sjb6WutnZV9l6VR4eJul1ABmCrnWBC9hKHQa6Q==} + '@smithy/util-stream@4.5.20': + resolution: {integrity: sha512-4yXLm5n/B5SRBR2p8cZ90Sbv4zL4NKsgxdzCzp/83cXw2KxLEumt5p+GAVyRNZgQOSrzXn9ARpO0lUe8XSlSDw==} + engines: {node: '>=18.0.0'} + + '@smithy/util-uri-escape@4.2.2': + resolution: {integrity: sha512-2kAStBlvq+lTXHyAZYfJRb/DfS3rsinLiwb+69SstC9Vb0s9vNWkRwpnj918Pfi85mzi42sOqdV72OLxWAISnw==} engines: {node: '>=18.0.0'} '@smithy/util-utf8@2.3.0': resolution: {integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==} engines: {node: '>=14.0.0'} - '@smithy/util-utf8@4.2.1': - resolution: {integrity: sha512-DSIwNaWtmzrNQHv8g7DBGR9mulSit65KSj5ymGEIAknmIN8IpbZefEep10LaMG/P/xquwbmJ1h9ectz8z6mV6g==} + '@smithy/util-utf8@4.2.2': + resolution: {integrity: sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw==} engines: {node: '>=18.0.0'} - '@smithy/uuid@1.1.1': - resolution: {integrity: sha512-dSfDCeihDmZlV2oyr0yWPTUfh07suS+R5OB+FZGiv/hHyK3hrFBW5rR1UYjfa57vBsrP9lciFkRPzebaV1Qujw==} + '@smithy/uuid@1.1.2': + resolution: {integrity: sha512-O/IEdcCUKkubz60tFbGA7ceITTAJsty+lBjNoorP4Z6XRqaFb/OjQjZODophEcuq68nKm6/0r+6/lLQ+XVpk8g==} engines: {node: '>=18.0.0'} '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} - '@swc/helpers@0.5.19': - resolution: {integrity: sha512-QamiFeIK3txNjgUTNppE6MiG3p7TdninpZu0E0PbqVh1a9FNLT2FRhisaa4NcaX52XVhA5l7Pk58Ft7Sqi/2sA==} + '@swc/helpers@0.5.20': + resolution: {integrity: sha512-2egEBHUMasdypIzrprsu8g+OEVd7Vp2MM3a2eVlM/cyFYto0nGz5BX5BTgh/ShZZI9ed+ozEq+Ngt+rgmUs8tw==} '@tinyhttp/content-disposition@2.2.4': resolution: {integrity: sha512-5Kc5CM2Ysn3vTTArBs2vESUt0AQiWZA86yc1TI3B+lxXmtEq133C1nxXNOgnzhrivdPZIh3zLj5gDnZjoLL5GA==} @@ -3459,8 +3548,8 @@ packages: resolution: {integrity: sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==} engines: {node: '>=6'} - array-back@6.2.2: - resolution: {integrity: sha512-gUAZ7HPyb4SJczXAMUXMGAvI976JoK3qEx9v1FTmeYuJj0IBiaKttG1ydtGKdkfqWkIkouke7nG8ufGy77+Cvw==} + array-back@6.2.3: + resolution: {integrity: sha512-SGDvmg6QTYiTxCBkYVmThcoa67uLl35pyzRHdpCGBOcqFy6BtwnphoFPk7LhJshD+Yk1Kt35WGWeZPTgwR4Fhw==} engines: {node: '>=12.17'} array-flatten@1.1.1: @@ -3557,8 +3646,8 @@ packages: bowser@2.14.1: resolution: {integrity: sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==} - brace-expansion@5.0.3: - resolution: {integrity: sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==} + brace-expansion@5.0.5: + resolution: {integrity: sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==} engines: {node: 18 || 20 || >=22} braces@3.0.3: @@ -3997,8 +4086,11 @@ packages: fast-uri@3.1.0: resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} - fast-xml-parser@5.3.8: - resolution: {integrity: sha512-53jIF4N6u/pxvaL1eb/hEZts/cFLWZ92eCfLrNyCI0k38lettCG/Bs40W9pPwoPXyHQlKu2OUbQtiEIZK/J6Vw==} + fast-xml-builder@1.1.4: + resolution: {integrity: sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg==} + + fast-xml-parser@5.5.9: + resolution: {integrity: sha512-jldvxr1MC6rtiZKgrFnDSvT8xuH+eJqxqOBThUVjYrxssYTo1avZLGql5l0a0BAERR01CadYzZ83kVEkbyDg+g==} hasBin: true fastq@1.20.1: @@ -4008,7 +4100,7 @@ packages: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} peerDependencies: - picomatch: ^3 || ^4 + picomatch: '>=4.0.4' peerDependenciesMeta: picomatch: optional: true @@ -4693,8 +4785,8 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - music-metadata@11.12.1: - resolution: {integrity: sha512-j++ltLxHDb5VCXET9FzQ8bnueiLHwQKgCO7vcbkRH/3F7fRjPkv6qncGEJ47yFhmemcYtgvsOAlcQ1dRBTkDjg==} + music-metadata@11.12.3: + resolution: {integrity: sha512-n6hSTZkuD59qWgHh6IP5dtDlDZQXoxk/bcA85Jywg8Z1iFrlNgl2+GTFgjZyn52W5UgQpV42V4XqrQZZAMbZTQ==} engines: {node: '>=18'} mz@2.7.0: @@ -4705,8 +4797,8 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - nanoid@5.1.6: - resolution: {integrity: sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg==} + nanoid@5.1.7: + resolution: {integrity: sha512-ua3NDgISf6jdwezAheMOk4mbE1LXjm1DfMUDMuJf4AqxLFK3ccGpgWizwa5YV7Yz9EpXwEaWoRXSb/BnV0t5dQ==} engines: {node: ^18 || >=20} hasBin: true @@ -4722,8 +4814,8 @@ packages: resolution: {integrity: sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==} engines: {node: '>= 0.4.0'} - node-addon-api@8.6.0: - resolution: {integrity: sha512-gBVjCaqDlRUk0EwoPNKzIr9KkS9041G/q31IBShPs1Xz6UTA+EXdZADbzqAJQrpDRq71CIMnOP5VMut3SL0z5Q==} + node-addon-api@8.7.0: + resolution: {integrity: sha512-9MdFxmkKaOYVTV+XVRG8ArDwwQ77XIgIPyKASB1k3JPq3M8fGQQQE3YpMOrKm6g//Ktx8ivZr8xo1Qmtqub+GA==} engines: {node: ^18 || ^20 || >= 21} node-api-headers@1.8.0: @@ -4943,6 +5035,10 @@ packages: partial-json@0.1.7: resolution: {integrity: sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA==} + path-expression-matcher@1.2.0: + resolution: {integrity: sha512-DwmPWeFn+tq7TiyJ2CxezCAirXjFxvaiD03npak3cRjlP9+OjTmSy1EpIrEbh+l6JgUundniloMLDQ/6VTdhLQ==} + engines: {node: '>=14.0.0'} + path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} @@ -4955,11 +5051,8 @@ packages: resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} engines: {node: 18 || 20 || >=22} - path-to-regexp@0.1.12: - resolution: {integrity: sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==} - - path-to-regexp@8.3.0: - resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==} + path-to-regexp@8.4.0: + resolution: {integrity: sha512-PuseHIvAnz3bjrM2rGJtSgo1zjgxapTLZ7x2pjhzWwlp4SJQgK3f3iZIQwkpEnBaKz6seKBADpM4B4ySkuYypg==} pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} @@ -4981,12 +5074,8 @@ packages: picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - picomatch@2.3.1: - resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} - engines: {node: '>=8.6'} - - picomatch@4.0.3: - resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} engines: {node: '>=12'} pify@3.0.0: @@ -5044,7 +5133,7 @@ packages: prism-media@1.3.5: resolution: {integrity: sha512-IQdl0Q01m4LrkN1EGIE9lphov5Hy7WWlH6ulf5QdGePLlPas9p2mhgddTEHrlaXYjjFToM1/rWuwF37VF4taaA==} peerDependencies: - '@discordjs/opus': npm:opusscript@0.0.8 + '@discordjs/opus': '>=0.8.0 <1.0.0' ffmpeg-static: ^5.0.2 || ^4.2.7 || ^3.0.0 || ^2.4.0 node-opus: ^0.3.3 opusscript: ^0.0.8 @@ -5166,7 +5255,7 @@ packages: resolution: {integrity: sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ==} engines: {node: '>=0.10.0'} peerDependencies: - request: npm:@cypress/request@3.0.10 + request: ^2.34 require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} @@ -5485,8 +5574,8 @@ packages: strip-literal@3.1.0: resolution: {integrity: sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==} - strnum@2.1.2: - resolution: {integrity: sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==} + strnum@2.2.2: + resolution: {integrity: sha512-DnR90I+jtXNSTXWdwrEy9FakW7UX+qUZg28gj5fk2vxxl7uS/3bpI4fjFYVmdK9etptYBPNkpahuQnEwhwECqA==} strtok3@10.3.4: resolution: {integrity: sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg==} @@ -5668,10 +5757,6 @@ packages: undici-types@7.18.2: resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} - undici@7.22.0: - resolution: {integrity: sha512-RqslV2Us5BrllB+JeiZnK4peryVTndy9Dnqq62S3yYRRTj0tFQCwEniUy2167skdGOy3vqRzEvl1Dm4sV2ReDg==} - engines: {node: '>=20.18.1'} - undici@7.24.2: resolution: {integrity: sha512-P9J1HWYV/ajFr8uCqk5QixwiRKmB1wOamgS0e+o2Z4A44Ej2+thFVRLG/eA7qprx88XXhnV5Bl8LHXTURpzB3Q==} engines: {node: '>=20.18.1'} @@ -5751,7 +5836,7 @@ packages: sugarss: ^5.0.0 terser: ^5.16.0 tsx: ^4.8.1 - yaml: ^2.4.2 + yaml: '>=2.8.3' peerDependenciesMeta: '@types/node': optional: true @@ -5904,8 +5989,8 @@ packages: resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} engines: {node: '>=18'} - yaml@2.8.2: - resolution: {integrity: sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==} + yaml@2.8.3: + resolution: {integrity: sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==} engines: {node: '>= 14.6'} hasBin: true @@ -5955,10 +6040,10 @@ snapshots: optionalDependencies: zod: 4.3.6 - '@apilium/mayros@0.1.1(@napi-rs/canvas@0.1.95)(@types/express@5.0.6)(hono@4.12.7)(node-llama-cpp@3.17.1(typescript@5.9.3))': + '@apilium/mayros@0.1.1(@napi-rs/canvas@0.1.97)(@types/express@5.0.6)(hono@4.12.7)(node-llama-cpp@3.17.1(typescript@5.9.3))': dependencies: '@agentclientprotocol/sdk': 0.14.1(zod@4.3.6) - '@aws-sdk/client-bedrock': 3.997.0 + '@aws-sdk/client-bedrock': 3.1019.0 '@buape/carbon': 0.0.0-beta-20260216184201(hono@4.12.7)(opusscript@0.0.8)(opusscript@0.0.8) '@clack/prompts': 1.0.1 '@discordjs/opus': opusscript@0.0.8 @@ -5973,7 +6058,7 @@ snapshots: '@mariozechner/pi-coding-agent': 0.54.0(ws@8.19.0)(zod@4.3.6) '@mariozechner/pi-tui': 0.54.0 '@mozilla/readability': 0.6.0 - '@napi-rs/canvas': 0.1.95 + '@napi-rs/canvas': 0.1.97 '@sinclair/typebox': 0.34.48 '@slack/bolt': 4.6.0(@types/express@5.0.6) '@slack/web-api': 7.14.1 @@ -6011,7 +6096,7 @@ snapshots: tslog: 4.10.2 undici: 7.24.2 ws: 8.19.0 - yaml: 2.8.2 + yaml: 2.8.3 zod: 4.3.6 transitivePeerDependencies: - '@modelcontextprotocol/sdk' @@ -6033,7 +6118,7 @@ snapshots: '@aws-crypto/crc32@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.973.2 + '@aws-sdk/types': 3.973.6 tslib: 2.8.1 '@aws-crypto/sha256-browser@5.2.0': @@ -6041,7 +6126,7 @@ snapshots: '@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.2 + '@aws-sdk/types': 3.973.6 '@aws-sdk/util-locate-window': 3.965.4 '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 @@ -6049,7 +6134,7 @@ snapshots: '@aws-crypto/sha256-js@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.973.2 + '@aws-sdk/types': 3.973.6 tslib: 2.8.1 '@aws-crypto/supports-web-crypto@5.2.0': @@ -6058,7 +6143,7 @@ snapshots: '@aws-crypto/util@5.2.0': dependencies: - '@aws-sdk/types': 3.973.2 + '@aws-sdk/types': 3.973.6 '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 @@ -6066,388 +6151,402 @@ snapshots: dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.973.13 - '@aws-sdk/credential-provider-node': 3.972.12 + '@aws-sdk/core': 3.973.25 + '@aws-sdk/credential-provider-node': 3.972.27 '@aws-sdk/eventstream-handler-node': 3.972.7 '@aws-sdk/middleware-eventstream': 3.972.4 - '@aws-sdk/middleware-host-header': 3.972.4 - '@aws-sdk/middleware-logger': 3.972.4 - '@aws-sdk/middleware-recursion-detection': 3.972.4 - '@aws-sdk/middleware-user-agent': 3.972.13 + '@aws-sdk/middleware-host-header': 3.972.8 + '@aws-sdk/middleware-logger': 3.972.8 + '@aws-sdk/middleware-recursion-detection': 3.972.9 + '@aws-sdk/middleware-user-agent': 3.972.26 '@aws-sdk/middleware-websocket': 3.972.8 - '@aws-sdk/region-config-resolver': 3.972.4 + '@aws-sdk/region-config-resolver': 3.972.10 '@aws-sdk/token-providers': 3.997.0 - '@aws-sdk/types': 3.973.2 - '@aws-sdk/util-endpoints': 3.996.1 - '@aws-sdk/util-user-agent-browser': 3.972.4 - '@aws-sdk/util-user-agent-node': 3.972.12 - '@smithy/config-resolver': 4.4.9 - '@smithy/core': 3.23.6 + '@aws-sdk/types': 3.973.6 + '@aws-sdk/util-endpoints': 3.996.5 + '@aws-sdk/util-user-agent-browser': 3.972.8 + '@aws-sdk/util-user-agent-node': 3.973.12 + '@smithy/config-resolver': 4.4.13 + '@smithy/core': 3.23.12 '@smithy/eventstream-serde-browser': 4.2.10 '@smithy/eventstream-serde-config-resolver': 4.3.10 '@smithy/eventstream-serde-node': 4.2.10 - '@smithy/fetch-http-handler': 5.3.11 - '@smithy/hash-node': 4.2.10 - '@smithy/invalid-dependency': 4.2.10 - '@smithy/middleware-content-length': 4.2.10 - '@smithy/middleware-endpoint': 4.4.20 - '@smithy/middleware-retry': 4.4.37 - '@smithy/middleware-serde': 4.2.11 - '@smithy/middleware-stack': 4.2.10 - '@smithy/node-config-provider': 4.3.10 - '@smithy/node-http-handler': 4.4.12 - '@smithy/protocol-http': 5.3.10 - '@smithy/smithy-client': 4.12.0 - '@smithy/types': 4.13.0 - '@smithy/url-parser': 4.2.10 - '@smithy/util-base64': 4.3.1 - '@smithy/util-body-length-browser': 4.2.1 - '@smithy/util-body-length-node': 4.2.2 - '@smithy/util-defaults-mode-browser': 4.3.36 - '@smithy/util-defaults-mode-node': 4.2.39 - '@smithy/util-endpoints': 3.3.1 - '@smithy/util-middleware': 4.2.10 - '@smithy/util-retry': 4.2.10 + '@smithy/fetch-http-handler': 5.3.15 + '@smithy/hash-node': 4.2.12 + '@smithy/invalid-dependency': 4.2.12 + '@smithy/middleware-content-length': 4.2.12 + '@smithy/middleware-endpoint': 4.4.27 + '@smithy/middleware-retry': 4.4.44 + '@smithy/middleware-serde': 4.2.15 + '@smithy/middleware-stack': 4.2.12 + '@smithy/node-config-provider': 4.3.12 + '@smithy/node-http-handler': 4.5.0 + '@smithy/protocol-http': 5.3.12 + '@smithy/smithy-client': 4.12.7 + '@smithy/types': 4.13.1 + '@smithy/url-parser': 4.2.12 + '@smithy/util-base64': 4.3.2 + '@smithy/util-body-length-browser': 4.2.2 + '@smithy/util-body-length-node': 4.2.3 + '@smithy/util-defaults-mode-browser': 4.3.43 + '@smithy/util-defaults-mode-node': 4.2.47 + '@smithy/util-endpoints': 3.3.3 + '@smithy/util-middleware': 4.2.12 + '@smithy/util-retry': 4.2.12 '@smithy/util-stream': 4.5.15 - '@smithy/util-utf8': 4.2.1 + '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/client-bedrock@3.997.0': + '@aws-sdk/client-bedrock@3.1019.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.973.13 - '@aws-sdk/credential-provider-node': 3.972.12 - '@aws-sdk/middleware-host-header': 3.972.4 - '@aws-sdk/middleware-logger': 3.972.4 - '@aws-sdk/middleware-recursion-detection': 3.972.4 - '@aws-sdk/middleware-user-agent': 3.972.13 - '@aws-sdk/region-config-resolver': 3.972.4 - '@aws-sdk/token-providers': 3.997.0 - '@aws-sdk/types': 3.973.2 - '@aws-sdk/util-endpoints': 3.996.1 - '@aws-sdk/util-user-agent-browser': 3.972.4 - '@aws-sdk/util-user-agent-node': 3.972.12 - '@smithy/config-resolver': 4.4.9 - '@smithy/core': 3.23.6 - '@smithy/fetch-http-handler': 5.3.11 - '@smithy/hash-node': 4.2.10 - '@smithy/invalid-dependency': 4.2.10 - '@smithy/middleware-content-length': 4.2.10 - '@smithy/middleware-endpoint': 4.4.20 - '@smithy/middleware-retry': 4.4.37 - '@smithy/middleware-serde': 4.2.11 - '@smithy/middleware-stack': 4.2.10 - '@smithy/node-config-provider': 4.3.10 - '@smithy/node-http-handler': 4.4.12 - '@smithy/protocol-http': 5.3.10 - '@smithy/smithy-client': 4.12.0 - '@smithy/types': 4.13.0 - '@smithy/url-parser': 4.2.10 - '@smithy/util-base64': 4.3.1 - '@smithy/util-body-length-browser': 4.2.1 - '@smithy/util-body-length-node': 4.2.2 - '@smithy/util-defaults-mode-browser': 4.3.36 - '@smithy/util-defaults-mode-node': 4.2.39 - '@smithy/util-endpoints': 3.3.1 - '@smithy/util-middleware': 4.2.10 - '@smithy/util-retry': 4.2.10 - '@smithy/util-utf8': 4.2.1 + '@aws-sdk/core': 3.973.25 + '@aws-sdk/credential-provider-node': 3.972.27 + '@aws-sdk/middleware-host-header': 3.972.8 + '@aws-sdk/middleware-logger': 3.972.8 + '@aws-sdk/middleware-recursion-detection': 3.972.9 + '@aws-sdk/middleware-user-agent': 3.972.26 + '@aws-sdk/region-config-resolver': 3.972.10 + '@aws-sdk/token-providers': 3.1019.0 + '@aws-sdk/types': 3.973.6 + '@aws-sdk/util-endpoints': 3.996.5 + '@aws-sdk/util-user-agent-browser': 3.972.8 + '@aws-sdk/util-user-agent-node': 3.973.12 + '@smithy/config-resolver': 4.4.13 + '@smithy/core': 3.23.12 + '@smithy/fetch-http-handler': 5.3.15 + '@smithy/hash-node': 4.2.12 + '@smithy/invalid-dependency': 4.2.12 + '@smithy/middleware-content-length': 4.2.12 + '@smithy/middleware-endpoint': 4.4.27 + '@smithy/middleware-retry': 4.4.44 + '@smithy/middleware-serde': 4.2.15 + '@smithy/middleware-stack': 4.2.12 + '@smithy/node-config-provider': 4.3.12 + '@smithy/node-http-handler': 4.5.0 + '@smithy/protocol-http': 5.3.12 + '@smithy/smithy-client': 4.12.7 + '@smithy/types': 4.13.1 + '@smithy/url-parser': 4.2.12 + '@smithy/util-base64': 4.3.2 + '@smithy/util-body-length-browser': 4.2.2 + '@smithy/util-body-length-node': 4.2.3 + '@smithy/util-defaults-mode-browser': 4.3.43 + '@smithy/util-defaults-mode-node': 4.2.47 + '@smithy/util-endpoints': 3.3.3 + '@smithy/util-middleware': 4.2.12 + '@smithy/util-retry': 4.2.12 + '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/core@3.973.13': - dependencies: - '@aws-sdk/types': 3.973.2 - '@aws-sdk/xml-builder': 3.972.6 - '@smithy/core': 3.23.6 - '@smithy/node-config-provider': 4.3.10 - '@smithy/property-provider': 4.2.10 - '@smithy/protocol-http': 5.3.10 - '@smithy/signature-v4': 5.3.10 - '@smithy/smithy-client': 4.12.0 - '@smithy/types': 4.13.0 - '@smithy/util-base64': 4.3.1 - '@smithy/util-middleware': 4.2.10 - '@smithy/util-utf8': 4.2.1 + '@aws-sdk/core@3.973.25': + dependencies: + '@aws-sdk/types': 3.973.6 + '@aws-sdk/xml-builder': 3.972.16 + '@smithy/core': 3.23.12 + '@smithy/node-config-provider': 4.3.12 + '@smithy/property-provider': 4.2.12 + '@smithy/protocol-http': 5.3.12 + '@smithy/signature-v4': 5.3.12 + '@smithy/smithy-client': 4.12.7 + '@smithy/types': 4.13.1 + '@smithy/util-base64': 4.3.2 + '@smithy/util-middleware': 4.2.12 + '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 - '@aws-sdk/credential-provider-env@3.972.11': + '@aws-sdk/credential-provider-env@3.972.23': dependencies: - '@aws-sdk/core': 3.973.13 - '@aws-sdk/types': 3.973.2 - '@smithy/property-provider': 4.2.10 - '@smithy/types': 4.13.0 + '@aws-sdk/core': 3.973.25 + '@aws-sdk/types': 3.973.6 + '@smithy/property-provider': 4.2.12 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/credential-provider-http@3.972.13': - dependencies: - '@aws-sdk/core': 3.973.13 - '@aws-sdk/types': 3.973.2 - '@smithy/fetch-http-handler': 5.3.11 - '@smithy/node-http-handler': 4.4.12 - '@smithy/property-provider': 4.2.10 - '@smithy/protocol-http': 5.3.10 - '@smithy/smithy-client': 4.12.0 - '@smithy/types': 4.13.0 - '@smithy/util-stream': 4.5.15 + '@aws-sdk/credential-provider-http@3.972.25': + dependencies: + '@aws-sdk/core': 3.973.25 + '@aws-sdk/types': 3.973.6 + '@smithy/fetch-http-handler': 5.3.15 + '@smithy/node-http-handler': 4.5.0 + '@smithy/property-provider': 4.2.12 + '@smithy/protocol-http': 5.3.12 + '@smithy/smithy-client': 4.12.7 + '@smithy/types': 4.13.1 + '@smithy/util-stream': 4.5.20 tslib: 2.8.1 - '@aws-sdk/credential-provider-ini@3.972.11': - dependencies: - '@aws-sdk/core': 3.973.13 - '@aws-sdk/credential-provider-env': 3.972.11 - '@aws-sdk/credential-provider-http': 3.972.13 - '@aws-sdk/credential-provider-login': 3.972.11 - '@aws-sdk/credential-provider-process': 3.972.11 - '@aws-sdk/credential-provider-sso': 3.972.11 - '@aws-sdk/credential-provider-web-identity': 3.972.11 - '@aws-sdk/nested-clients': 3.996.1 - '@aws-sdk/types': 3.973.2 - '@smithy/credential-provider-imds': 4.2.10 - '@smithy/property-provider': 4.2.10 - '@smithy/shared-ini-file-loader': 4.4.5 - '@smithy/types': 4.13.0 + '@aws-sdk/credential-provider-ini@3.972.26': + dependencies: + '@aws-sdk/core': 3.973.25 + '@aws-sdk/credential-provider-env': 3.972.23 + '@aws-sdk/credential-provider-http': 3.972.25 + '@aws-sdk/credential-provider-login': 3.972.26 + '@aws-sdk/credential-provider-process': 3.972.23 + '@aws-sdk/credential-provider-sso': 3.972.26 + '@aws-sdk/credential-provider-web-identity': 3.972.26 + '@aws-sdk/nested-clients': 3.996.16 + '@aws-sdk/types': 3.973.6 + '@smithy/credential-provider-imds': 4.2.12 + '@smithy/property-provider': 4.2.12 + '@smithy/shared-ini-file-loader': 4.4.7 + '@smithy/types': 4.13.1 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-login@3.972.11': + '@aws-sdk/credential-provider-login@3.972.26': dependencies: - '@aws-sdk/core': 3.973.13 - '@aws-sdk/nested-clients': 3.996.1 - '@aws-sdk/types': 3.973.2 - '@smithy/property-provider': 4.2.10 - '@smithy/protocol-http': 5.3.10 - '@smithy/shared-ini-file-loader': 4.4.5 - '@smithy/types': 4.13.0 + '@aws-sdk/core': 3.973.25 + '@aws-sdk/nested-clients': 3.996.16 + '@aws-sdk/types': 3.973.6 + '@smithy/property-provider': 4.2.12 + '@smithy/protocol-http': 5.3.12 + '@smithy/shared-ini-file-loader': 4.4.7 + '@smithy/types': 4.13.1 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-node@3.972.12': - dependencies: - '@aws-sdk/credential-provider-env': 3.972.11 - '@aws-sdk/credential-provider-http': 3.972.13 - '@aws-sdk/credential-provider-ini': 3.972.11 - '@aws-sdk/credential-provider-process': 3.972.11 - '@aws-sdk/credential-provider-sso': 3.972.11 - '@aws-sdk/credential-provider-web-identity': 3.972.11 - '@aws-sdk/types': 3.973.2 - '@smithy/credential-provider-imds': 4.2.10 - '@smithy/property-provider': 4.2.10 - '@smithy/shared-ini-file-loader': 4.4.5 - '@smithy/types': 4.13.0 + '@aws-sdk/credential-provider-node@3.972.27': + dependencies: + '@aws-sdk/credential-provider-env': 3.972.23 + '@aws-sdk/credential-provider-http': 3.972.25 + '@aws-sdk/credential-provider-ini': 3.972.26 + '@aws-sdk/credential-provider-process': 3.972.23 + '@aws-sdk/credential-provider-sso': 3.972.26 + '@aws-sdk/credential-provider-web-identity': 3.972.26 + '@aws-sdk/types': 3.973.6 + '@smithy/credential-provider-imds': 4.2.12 + '@smithy/property-provider': 4.2.12 + '@smithy/shared-ini-file-loader': 4.4.7 + '@smithy/types': 4.13.1 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-process@3.972.11': + '@aws-sdk/credential-provider-process@3.972.23': dependencies: - '@aws-sdk/core': 3.973.13 - '@aws-sdk/types': 3.973.2 - '@smithy/property-provider': 4.2.10 - '@smithy/shared-ini-file-loader': 4.4.5 - '@smithy/types': 4.13.0 + '@aws-sdk/core': 3.973.25 + '@aws-sdk/types': 3.973.6 + '@smithy/property-provider': 4.2.12 + '@smithy/shared-ini-file-loader': 4.4.7 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/credential-provider-sso@3.972.11': + '@aws-sdk/credential-provider-sso@3.972.26': dependencies: - '@aws-sdk/core': 3.973.13 - '@aws-sdk/nested-clients': 3.996.1 - '@aws-sdk/token-providers': 3.997.0 - '@aws-sdk/types': 3.973.2 - '@smithy/property-provider': 4.2.10 - '@smithy/shared-ini-file-loader': 4.4.5 - '@smithy/types': 4.13.0 + '@aws-sdk/core': 3.973.25 + '@aws-sdk/nested-clients': 3.996.16 + '@aws-sdk/token-providers': 3.1019.0 + '@aws-sdk/types': 3.973.6 + '@smithy/property-provider': 4.2.12 + '@smithy/shared-ini-file-loader': 4.4.7 + '@smithy/types': 4.13.1 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-web-identity@3.972.11': + '@aws-sdk/credential-provider-web-identity@3.972.26': dependencies: - '@aws-sdk/core': 3.973.13 - '@aws-sdk/nested-clients': 3.996.1 - '@aws-sdk/types': 3.973.2 - '@smithy/property-provider': 4.2.10 - '@smithy/shared-ini-file-loader': 4.4.5 - '@smithy/types': 4.13.0 + '@aws-sdk/core': 3.973.25 + '@aws-sdk/nested-clients': 3.996.16 + '@aws-sdk/types': 3.973.6 + '@smithy/property-provider': 4.2.12 + '@smithy/shared-ini-file-loader': 4.4.7 + '@smithy/types': 4.13.1 tslib: 2.8.1 transitivePeerDependencies: - aws-crt '@aws-sdk/eventstream-handler-node@3.972.7': dependencies: - '@aws-sdk/types': 3.973.2 + '@aws-sdk/types': 3.973.6 '@smithy/eventstream-codec': 4.2.10 - '@smithy/types': 4.13.0 + '@smithy/types': 4.13.1 tslib: 2.8.1 '@aws-sdk/middleware-eventstream@3.972.4': dependencies: - '@aws-sdk/types': 3.973.2 - '@smithy/protocol-http': 5.3.10 - '@smithy/types': 4.13.0 + '@aws-sdk/types': 3.973.6 + '@smithy/protocol-http': 5.3.12 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/middleware-host-header@3.972.4': + '@aws-sdk/middleware-host-header@3.972.8': dependencies: - '@aws-sdk/types': 3.973.2 - '@smithy/protocol-http': 5.3.10 - '@smithy/types': 4.13.0 + '@aws-sdk/types': 3.973.6 + '@smithy/protocol-http': 5.3.12 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/middleware-logger@3.972.4': + '@aws-sdk/middleware-logger@3.972.8': dependencies: - '@aws-sdk/types': 3.973.2 - '@smithy/types': 4.13.0 + '@aws-sdk/types': 3.973.6 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/middleware-recursion-detection@3.972.4': + '@aws-sdk/middleware-recursion-detection@3.972.9': dependencies: - '@aws-sdk/types': 3.973.2 - '@aws/lambda-invoke-store': 0.2.3 - '@smithy/protocol-http': 5.3.10 - '@smithy/types': 4.13.0 + '@aws-sdk/types': 3.973.6 + '@aws/lambda-invoke-store': 0.2.4 + '@smithy/protocol-http': 5.3.12 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/middleware-user-agent@3.972.13': + '@aws-sdk/middleware-user-agent@3.972.26': dependencies: - '@aws-sdk/core': 3.973.13 - '@aws-sdk/types': 3.973.2 - '@aws-sdk/util-endpoints': 3.996.1 - '@smithy/core': 3.23.6 - '@smithy/protocol-http': 5.3.10 - '@smithy/types': 4.13.0 + '@aws-sdk/core': 3.973.25 + '@aws-sdk/types': 3.973.6 + '@aws-sdk/util-endpoints': 3.996.5 + '@smithy/core': 3.23.12 + '@smithy/protocol-http': 5.3.12 + '@smithy/types': 4.13.1 + '@smithy/util-retry': 4.2.12 tslib: 2.8.1 '@aws-sdk/middleware-websocket@3.972.8': dependencies: - '@aws-sdk/types': 3.973.2 + '@aws-sdk/types': 3.973.6 '@aws-sdk/util-format-url': 3.972.4 '@smithy/eventstream-codec': 4.2.10 '@smithy/eventstream-serde-browser': 4.2.10 - '@smithy/fetch-http-handler': 5.3.11 - '@smithy/protocol-http': 5.3.10 - '@smithy/signature-v4': 5.3.10 - '@smithy/types': 4.13.0 - '@smithy/util-base64': 4.3.1 + '@smithy/fetch-http-handler': 5.3.15 + '@smithy/protocol-http': 5.3.12 + '@smithy/signature-v4': 5.3.12 + '@smithy/types': 4.13.1 + '@smithy/util-base64': 4.3.2 '@smithy/util-hex-encoding': 4.2.1 - '@smithy/util-utf8': 4.2.1 + '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 - '@aws-sdk/nested-clients@3.996.1': + '@aws-sdk/nested-clients@3.996.16': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.973.13 - '@aws-sdk/middleware-host-header': 3.972.4 - '@aws-sdk/middleware-logger': 3.972.4 - '@aws-sdk/middleware-recursion-detection': 3.972.4 - '@aws-sdk/middleware-user-agent': 3.972.13 - '@aws-sdk/region-config-resolver': 3.972.4 - '@aws-sdk/types': 3.973.2 - '@aws-sdk/util-endpoints': 3.996.1 - '@aws-sdk/util-user-agent-browser': 3.972.4 - '@aws-sdk/util-user-agent-node': 3.972.12 - '@smithy/config-resolver': 4.4.9 - '@smithy/core': 3.23.6 - '@smithy/fetch-http-handler': 5.3.11 - '@smithy/hash-node': 4.2.10 - '@smithy/invalid-dependency': 4.2.10 - '@smithy/middleware-content-length': 4.2.10 - '@smithy/middleware-endpoint': 4.4.20 - '@smithy/middleware-retry': 4.4.37 - '@smithy/middleware-serde': 4.2.11 - '@smithy/middleware-stack': 4.2.10 - '@smithy/node-config-provider': 4.3.10 - '@smithy/node-http-handler': 4.4.12 - '@smithy/protocol-http': 5.3.10 - '@smithy/smithy-client': 4.12.0 - '@smithy/types': 4.13.0 - '@smithy/url-parser': 4.2.10 - '@smithy/util-base64': 4.3.1 - '@smithy/util-body-length-browser': 4.2.1 - '@smithy/util-body-length-node': 4.2.2 - '@smithy/util-defaults-mode-browser': 4.3.36 - '@smithy/util-defaults-mode-node': 4.2.39 - '@smithy/util-endpoints': 3.3.1 - '@smithy/util-middleware': 4.2.10 - '@smithy/util-retry': 4.2.10 - '@smithy/util-utf8': 4.2.1 + '@aws-sdk/core': 3.973.25 + '@aws-sdk/middleware-host-header': 3.972.8 + '@aws-sdk/middleware-logger': 3.972.8 + '@aws-sdk/middleware-recursion-detection': 3.972.9 + '@aws-sdk/middleware-user-agent': 3.972.26 + '@aws-sdk/region-config-resolver': 3.972.10 + '@aws-sdk/types': 3.973.6 + '@aws-sdk/util-endpoints': 3.996.5 + '@aws-sdk/util-user-agent-browser': 3.972.8 + '@aws-sdk/util-user-agent-node': 3.973.12 + '@smithy/config-resolver': 4.4.13 + '@smithy/core': 3.23.12 + '@smithy/fetch-http-handler': 5.3.15 + '@smithy/hash-node': 4.2.12 + '@smithy/invalid-dependency': 4.2.12 + '@smithy/middleware-content-length': 4.2.12 + '@smithy/middleware-endpoint': 4.4.27 + '@smithy/middleware-retry': 4.4.44 + '@smithy/middleware-serde': 4.2.15 + '@smithy/middleware-stack': 4.2.12 + '@smithy/node-config-provider': 4.3.12 + '@smithy/node-http-handler': 4.5.0 + '@smithy/protocol-http': 5.3.12 + '@smithy/smithy-client': 4.12.7 + '@smithy/types': 4.13.1 + '@smithy/url-parser': 4.2.12 + '@smithy/util-base64': 4.3.2 + '@smithy/util-body-length-browser': 4.2.2 + '@smithy/util-body-length-node': 4.2.3 + '@smithy/util-defaults-mode-browser': 4.3.43 + '@smithy/util-defaults-mode-node': 4.2.47 + '@smithy/util-endpoints': 3.3.3 + '@smithy/util-middleware': 4.2.12 + '@smithy/util-retry': 4.2.12 + '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/region-config-resolver@3.972.4': + '@aws-sdk/region-config-resolver@3.972.10': dependencies: - '@aws-sdk/types': 3.973.2 - '@smithy/config-resolver': 4.4.9 - '@smithy/node-config-provider': 4.3.10 - '@smithy/types': 4.13.0 + '@aws-sdk/types': 3.973.6 + '@smithy/config-resolver': 4.4.13 + '@smithy/node-config-provider': 4.3.12 + '@smithy/types': 4.13.1 tslib: 2.8.1 + '@aws-sdk/token-providers@3.1019.0': + dependencies: + '@aws-sdk/core': 3.973.25 + '@aws-sdk/nested-clients': 3.996.16 + '@aws-sdk/types': 3.973.6 + '@smithy/property-provider': 4.2.12 + '@smithy/shared-ini-file-loader': 4.4.7 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + '@aws-sdk/token-providers@3.997.0': dependencies: - '@aws-sdk/core': 3.973.13 - '@aws-sdk/nested-clients': 3.996.1 - '@aws-sdk/types': 3.973.2 - '@smithy/property-provider': 4.2.10 - '@smithy/shared-ini-file-loader': 4.4.5 - '@smithy/types': 4.13.0 + '@aws-sdk/core': 3.973.25 + '@aws-sdk/nested-clients': 3.996.16 + '@aws-sdk/types': 3.973.6 + '@smithy/property-provider': 4.2.12 + '@smithy/shared-ini-file-loader': 4.4.7 + '@smithy/types': 4.13.1 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/types@3.973.2': + '@aws-sdk/types@3.973.6': dependencies: - '@smithy/types': 4.13.0 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/util-endpoints@3.996.1': + '@aws-sdk/util-endpoints@3.996.5': dependencies: - '@aws-sdk/types': 3.973.2 - '@smithy/types': 4.13.0 - '@smithy/url-parser': 4.2.10 - '@smithy/util-endpoints': 3.3.1 + '@aws-sdk/types': 3.973.6 + '@smithy/types': 4.13.1 + '@smithy/url-parser': 4.2.12 + '@smithy/util-endpoints': 3.3.3 tslib: 2.8.1 '@aws-sdk/util-format-url@3.972.4': dependencies: - '@aws-sdk/types': 3.973.2 - '@smithy/querystring-builder': 4.2.10 - '@smithy/types': 4.13.0 + '@aws-sdk/types': 3.973.6 + '@smithy/querystring-builder': 4.2.12 + '@smithy/types': 4.13.1 tslib: 2.8.1 '@aws-sdk/util-locate-window@3.965.4': dependencies: tslib: 2.8.1 - '@aws-sdk/util-user-agent-browser@3.972.4': + '@aws-sdk/util-user-agent-browser@3.972.8': dependencies: - '@aws-sdk/types': 3.973.2 - '@smithy/types': 4.13.0 + '@aws-sdk/types': 3.973.6 + '@smithy/types': 4.13.1 bowser: 2.14.1 tslib: 2.8.1 - '@aws-sdk/util-user-agent-node@3.972.12': + '@aws-sdk/util-user-agent-node@3.973.12': dependencies: - '@aws-sdk/middleware-user-agent': 3.972.13 - '@aws-sdk/types': 3.973.2 - '@smithy/node-config-provider': 4.3.10 - '@smithy/types': 4.13.0 + '@aws-sdk/middleware-user-agent': 3.972.26 + '@aws-sdk/types': 3.973.6 + '@smithy/node-config-provider': 4.3.12 + '@smithy/types': 4.13.1 + '@smithy/util-config-provider': 4.2.2 tslib: 2.8.1 - '@aws-sdk/xml-builder@3.972.6': + '@aws-sdk/xml-builder@3.972.16': dependencies: - '@smithy/types': 4.13.0 - fast-xml-parser: 5.3.8 + '@smithy/types': 4.13.1 + fast-xml-parser: 5.5.9 tslib: 2.8.1 - '@aws/lambda-invoke-store@0.2.3': {} + '@aws/lambda-invoke-store@0.2.4': {} '@azure/abort-controller@2.1.2': dependencies: @@ -6516,7 +6615,7 @@ snapshots: '@bcoe/v8-coverage@1.0.2': {} - '@borewit/text-codec@0.2.1': {} + '@borewit/text-codec@0.2.2': {} '@buape/carbon@0.0.0-beta-20260216184201(hono@4.12.7)(opusscript@0.0.8)(opusscript@0.0.8)': dependencies: @@ -7252,7 +7351,7 @@ snapshots: marked: 15.0.12 minimatch: 10.2.3 proper-lockfile: 4.1.2 - yaml: 2.8.2 + yaml: 2.8.3 optionalDependencies: '@mariozechner/clipboard': 0.3.2 transitivePeerDependencies: @@ -7312,36 +7411,69 @@ snapshots: '@napi-rs/canvas-android-arm64@0.1.95': optional: true + '@napi-rs/canvas-android-arm64@0.1.97': + optional: true + '@napi-rs/canvas-darwin-arm64@0.1.95': optional: true + '@napi-rs/canvas-darwin-arm64@0.1.97': + optional: true + '@napi-rs/canvas-darwin-x64@0.1.95': optional: true + '@napi-rs/canvas-darwin-x64@0.1.97': + optional: true + '@napi-rs/canvas-linux-arm-gnueabihf@0.1.95': optional: true + '@napi-rs/canvas-linux-arm-gnueabihf@0.1.97': + optional: true + '@napi-rs/canvas-linux-arm64-gnu@0.1.95': optional: true + '@napi-rs/canvas-linux-arm64-gnu@0.1.97': + optional: true + '@napi-rs/canvas-linux-arm64-musl@0.1.95': optional: true + '@napi-rs/canvas-linux-arm64-musl@0.1.97': + optional: true + '@napi-rs/canvas-linux-riscv64-gnu@0.1.95': optional: true + '@napi-rs/canvas-linux-riscv64-gnu@0.1.97': + optional: true + '@napi-rs/canvas-linux-x64-gnu@0.1.95': optional: true + '@napi-rs/canvas-linux-x64-gnu@0.1.97': + optional: true + '@napi-rs/canvas-linux-x64-musl@0.1.95': optional: true + '@napi-rs/canvas-linux-x64-musl@0.1.97': + optional: true + '@napi-rs/canvas-win32-arm64-msvc@0.1.95': optional: true + '@napi-rs/canvas-win32-arm64-msvc@0.1.97': + optional: true + '@napi-rs/canvas-win32-x64-msvc@0.1.95': optional: true + '@napi-rs/canvas-win32-x64-msvc@0.1.97': + optional: true + '@napi-rs/canvas@0.1.95': optionalDependencies: '@napi-rs/canvas-android-arm64': 0.1.95 @@ -7356,6 +7488,20 @@ snapshots: '@napi-rs/canvas-win32-arm64-msvc': 0.1.95 '@napi-rs/canvas-win32-x64-msvc': 0.1.95 + '@napi-rs/canvas@0.1.97': + optionalDependencies: + '@napi-rs/canvas-android-arm64': 0.1.97 + '@napi-rs/canvas-darwin-arm64': 0.1.97 + '@napi-rs/canvas-darwin-x64': 0.1.97 + '@napi-rs/canvas-linux-arm-gnueabihf': 0.1.97 + '@napi-rs/canvas-linux-arm64-gnu': 0.1.97 + '@napi-rs/canvas-linux-arm64-musl': 0.1.97 + '@napi-rs/canvas-linux-riscv64-gnu': 0.1.97 + '@napi-rs/canvas-linux-x64-gnu': 0.1.97 + '@napi-rs/canvas-linux-x64-musl': 0.1.97 + '@napi-rs/canvas-win32-arm64-msvc': 0.1.97 + '@napi-rs/canvas-win32-x64-msvc': 0.1.97 + '@napi-rs/wasm-runtime@1.1.1': dependencies: '@emnapi/core': 1.8.1 @@ -7434,7 +7580,7 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) - yaml: 2.8.2 + yaml: 2.8.3 '@opentelemetry/context-async-hooks@2.5.1(@opentelemetry/api@1.9.0)': dependencies: @@ -8014,7 +8160,7 @@ snapshots: '@types/express': 5.0.6 axios: 1.13.5 express: 5.2.1 - path-to-regexp: 8.3.0 + path-to-regexp: 8.4.0 raw-body: 3.0.2 tsscmp: 1.0.6 transitivePeerDependencies: @@ -8069,226 +8215,227 @@ snapshots: transitivePeerDependencies: - debug - '@smithy/abort-controller@4.2.10': + '@smithy/abort-controller@4.2.12': dependencies: - '@smithy/types': 4.13.0 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@smithy/config-resolver@4.4.9': + '@smithy/config-resolver@4.4.13': dependencies: - '@smithy/node-config-provider': 4.3.10 - '@smithy/types': 4.13.0 - '@smithy/util-config-provider': 4.2.1 - '@smithy/util-endpoints': 3.3.1 - '@smithy/util-middleware': 4.2.10 + '@smithy/node-config-provider': 4.3.12 + '@smithy/types': 4.13.1 + '@smithy/util-config-provider': 4.2.2 + '@smithy/util-endpoints': 3.3.3 + '@smithy/util-middleware': 4.2.12 tslib: 2.8.1 - '@smithy/core@3.23.6': - dependencies: - '@smithy/middleware-serde': 4.2.11 - '@smithy/protocol-http': 5.3.10 - '@smithy/types': 4.13.0 - '@smithy/util-base64': 4.3.1 - '@smithy/util-body-length-browser': 4.2.1 - '@smithy/util-middleware': 4.2.10 - '@smithy/util-stream': 4.5.15 - '@smithy/util-utf8': 4.2.1 - '@smithy/uuid': 1.1.1 + '@smithy/core@3.23.12': + dependencies: + '@smithy/protocol-http': 5.3.12 + '@smithy/types': 4.13.1 + '@smithy/url-parser': 4.2.12 + '@smithy/util-base64': 4.3.2 + '@smithy/util-body-length-browser': 4.2.2 + '@smithy/util-middleware': 4.2.12 + '@smithy/util-stream': 4.5.20 + '@smithy/util-utf8': 4.2.2 + '@smithy/uuid': 1.1.2 tslib: 2.8.1 - '@smithy/credential-provider-imds@4.2.10': + '@smithy/credential-provider-imds@4.2.12': dependencies: - '@smithy/node-config-provider': 4.3.10 - '@smithy/property-provider': 4.2.10 - '@smithy/types': 4.13.0 - '@smithy/url-parser': 4.2.10 + '@smithy/node-config-provider': 4.3.12 + '@smithy/property-provider': 4.2.12 + '@smithy/types': 4.13.1 + '@smithy/url-parser': 4.2.12 tslib: 2.8.1 '@smithy/eventstream-codec@4.2.10': dependencies: '@aws-crypto/crc32': 5.2.0 - '@smithy/types': 4.13.0 - '@smithy/util-hex-encoding': 4.2.1 + '@smithy/types': 4.13.1 + '@smithy/util-hex-encoding': 4.2.2 tslib: 2.8.1 '@smithy/eventstream-serde-browser@4.2.10': dependencies: '@smithy/eventstream-serde-universal': 4.2.10 - '@smithy/types': 4.13.0 + '@smithy/types': 4.13.1 tslib: 2.8.1 '@smithy/eventstream-serde-config-resolver@4.3.10': dependencies: - '@smithy/types': 4.13.0 + '@smithy/types': 4.13.1 tslib: 2.8.1 '@smithy/eventstream-serde-node@4.2.10': dependencies: '@smithy/eventstream-serde-universal': 4.2.10 - '@smithy/types': 4.13.0 + '@smithy/types': 4.13.1 tslib: 2.8.1 '@smithy/eventstream-serde-universal@4.2.10': dependencies: '@smithy/eventstream-codec': 4.2.10 - '@smithy/types': 4.13.0 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@smithy/fetch-http-handler@5.3.11': + '@smithy/fetch-http-handler@5.3.15': dependencies: - '@smithy/protocol-http': 5.3.10 - '@smithy/querystring-builder': 4.2.10 - '@smithy/types': 4.13.0 - '@smithy/util-base64': 4.3.1 + '@smithy/protocol-http': 5.3.12 + '@smithy/querystring-builder': 4.2.12 + '@smithy/types': 4.13.1 + '@smithy/util-base64': 4.3.2 tslib: 2.8.1 - '@smithy/hash-node@4.2.10': + '@smithy/hash-node@4.2.12': dependencies: - '@smithy/types': 4.13.0 - '@smithy/util-buffer-from': 4.2.1 - '@smithy/util-utf8': 4.2.1 + '@smithy/types': 4.13.1 + '@smithy/util-buffer-from': 4.2.2 + '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 - '@smithy/invalid-dependency@4.2.10': + '@smithy/invalid-dependency@4.2.12': dependencies: - '@smithy/types': 4.13.0 + '@smithy/types': 4.13.1 tslib: 2.8.1 '@smithy/is-array-buffer@2.2.0': dependencies: tslib: 2.8.1 - '@smithy/is-array-buffer@4.2.1': + '@smithy/is-array-buffer@4.2.2': dependencies: tslib: 2.8.1 - '@smithy/middleware-content-length@4.2.10': + '@smithy/middleware-content-length@4.2.12': dependencies: - '@smithy/protocol-http': 5.3.10 - '@smithy/types': 4.13.0 + '@smithy/protocol-http': 5.3.12 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@smithy/middleware-endpoint@4.4.20': + '@smithy/middleware-endpoint@4.4.27': dependencies: - '@smithy/core': 3.23.6 - '@smithy/middleware-serde': 4.2.11 - '@smithy/node-config-provider': 4.3.10 - '@smithy/shared-ini-file-loader': 4.4.5 - '@smithy/types': 4.13.0 - '@smithy/url-parser': 4.2.10 - '@smithy/util-middleware': 4.2.10 + '@smithy/core': 3.23.12 + '@smithy/middleware-serde': 4.2.15 + '@smithy/node-config-provider': 4.3.12 + '@smithy/shared-ini-file-loader': 4.4.7 + '@smithy/types': 4.13.1 + '@smithy/url-parser': 4.2.12 + '@smithy/util-middleware': 4.2.12 tslib: 2.8.1 - '@smithy/middleware-retry@4.4.37': + '@smithy/middleware-retry@4.4.44': dependencies: - '@smithy/node-config-provider': 4.3.10 - '@smithy/protocol-http': 5.3.10 - '@smithy/service-error-classification': 4.2.10 - '@smithy/smithy-client': 4.12.0 - '@smithy/types': 4.13.0 - '@smithy/util-middleware': 4.2.10 - '@smithy/util-retry': 4.2.10 - '@smithy/uuid': 1.1.1 + '@smithy/node-config-provider': 4.3.12 + '@smithy/protocol-http': 5.3.12 + '@smithy/service-error-classification': 4.2.12 + '@smithy/smithy-client': 4.12.7 + '@smithy/types': 4.13.1 + '@smithy/util-middleware': 4.2.12 + '@smithy/util-retry': 4.2.12 + '@smithy/uuid': 1.1.2 tslib: 2.8.1 - '@smithy/middleware-serde@4.2.11': + '@smithy/middleware-serde@4.2.15': dependencies: - '@smithy/protocol-http': 5.3.10 - '@smithy/types': 4.13.0 + '@smithy/core': 3.23.12 + '@smithy/protocol-http': 5.3.12 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@smithy/middleware-stack@4.2.10': + '@smithy/middleware-stack@4.2.12': dependencies: - '@smithy/types': 4.13.0 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@smithy/node-config-provider@4.3.10': + '@smithy/node-config-provider@4.3.12': dependencies: - '@smithy/property-provider': 4.2.10 - '@smithy/shared-ini-file-loader': 4.4.5 - '@smithy/types': 4.13.0 + '@smithy/property-provider': 4.2.12 + '@smithy/shared-ini-file-loader': 4.4.7 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@smithy/node-http-handler@4.4.12': + '@smithy/node-http-handler@4.5.0': dependencies: - '@smithy/abort-controller': 4.2.10 - '@smithy/protocol-http': 5.3.10 - '@smithy/querystring-builder': 4.2.10 - '@smithy/types': 4.13.0 + '@smithy/abort-controller': 4.2.12 + '@smithy/protocol-http': 5.3.12 + '@smithy/querystring-builder': 4.2.12 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@smithy/property-provider@4.2.10': + '@smithy/property-provider@4.2.12': dependencies: - '@smithy/types': 4.13.0 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@smithy/protocol-http@5.3.10': + '@smithy/protocol-http@5.3.12': dependencies: - '@smithy/types': 4.13.0 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@smithy/querystring-builder@4.2.10': + '@smithy/querystring-builder@4.2.12': dependencies: - '@smithy/types': 4.13.0 - '@smithy/util-uri-escape': 4.2.1 + '@smithy/types': 4.13.1 + '@smithy/util-uri-escape': 4.2.2 tslib: 2.8.1 - '@smithy/querystring-parser@4.2.10': + '@smithy/querystring-parser@4.2.12': dependencies: - '@smithy/types': 4.13.0 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@smithy/service-error-classification@4.2.10': + '@smithy/service-error-classification@4.2.12': dependencies: - '@smithy/types': 4.13.0 + '@smithy/types': 4.13.1 - '@smithy/shared-ini-file-loader@4.4.5': + '@smithy/shared-ini-file-loader@4.4.7': dependencies: - '@smithy/types': 4.13.0 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@smithy/signature-v4@5.3.10': + '@smithy/signature-v4@5.3.12': dependencies: - '@smithy/is-array-buffer': 4.2.1 - '@smithy/protocol-http': 5.3.10 - '@smithy/types': 4.13.0 - '@smithy/util-hex-encoding': 4.2.1 - '@smithy/util-middleware': 4.2.10 - '@smithy/util-uri-escape': 4.2.1 - '@smithy/util-utf8': 4.2.1 + '@smithy/is-array-buffer': 4.2.2 + '@smithy/protocol-http': 5.3.12 + '@smithy/types': 4.13.1 + '@smithy/util-hex-encoding': 4.2.2 + '@smithy/util-middleware': 4.2.12 + '@smithy/util-uri-escape': 4.2.2 + '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 - '@smithy/smithy-client@4.12.0': + '@smithy/smithy-client@4.12.7': dependencies: - '@smithy/core': 3.23.6 - '@smithy/middleware-endpoint': 4.4.20 - '@smithy/middleware-stack': 4.2.10 - '@smithy/protocol-http': 5.3.10 - '@smithy/types': 4.13.0 - '@smithy/util-stream': 4.5.15 + '@smithy/core': 3.23.12 + '@smithy/middleware-endpoint': 4.4.27 + '@smithy/middleware-stack': 4.2.12 + '@smithy/protocol-http': 5.3.12 + '@smithy/types': 4.13.1 + '@smithy/util-stream': 4.5.20 tslib: 2.8.1 - '@smithy/types@4.13.0': + '@smithy/types@4.13.1': dependencies: tslib: 2.8.1 - '@smithy/url-parser@4.2.10': + '@smithy/url-parser@4.2.12': dependencies: - '@smithy/querystring-parser': 4.2.10 - '@smithy/types': 4.13.0 + '@smithy/querystring-parser': 4.2.12 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@smithy/util-base64@4.3.1': + '@smithy/util-base64@4.3.2': dependencies: - '@smithy/util-buffer-from': 4.2.1 - '@smithy/util-utf8': 4.2.1 + '@smithy/util-buffer-from': 4.2.2 + '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 - '@smithy/util-body-length-browser@4.2.1': + '@smithy/util-body-length-browser@4.2.2': dependencies: tslib: 2.8.1 - '@smithy/util-body-length-node@4.2.2': + '@smithy/util-body-length-node@4.2.3': dependencies: tslib: 2.8.1 @@ -8297,65 +8444,80 @@ snapshots: '@smithy/is-array-buffer': 2.2.0 tslib: 2.8.1 - '@smithy/util-buffer-from@4.2.1': + '@smithy/util-buffer-from@4.2.2': dependencies: - '@smithy/is-array-buffer': 4.2.1 + '@smithy/is-array-buffer': 4.2.2 tslib: 2.8.1 - '@smithy/util-config-provider@4.2.1': + '@smithy/util-config-provider@4.2.2': dependencies: tslib: 2.8.1 - '@smithy/util-defaults-mode-browser@4.3.36': + '@smithy/util-defaults-mode-browser@4.3.43': dependencies: - '@smithy/property-provider': 4.2.10 - '@smithy/smithy-client': 4.12.0 - '@smithy/types': 4.13.0 + '@smithy/property-provider': 4.2.12 + '@smithy/smithy-client': 4.12.7 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@smithy/util-defaults-mode-node@4.2.39': + '@smithy/util-defaults-mode-node@4.2.47': dependencies: - '@smithy/config-resolver': 4.4.9 - '@smithy/credential-provider-imds': 4.2.10 - '@smithy/node-config-provider': 4.3.10 - '@smithy/property-provider': 4.2.10 - '@smithy/smithy-client': 4.12.0 - '@smithy/types': 4.13.0 + '@smithy/config-resolver': 4.4.13 + '@smithy/credential-provider-imds': 4.2.12 + '@smithy/node-config-provider': 4.3.12 + '@smithy/property-provider': 4.2.12 + '@smithy/smithy-client': 4.12.7 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@smithy/util-endpoints@3.3.1': + '@smithy/util-endpoints@3.3.3': dependencies: - '@smithy/node-config-provider': 4.3.10 - '@smithy/types': 4.13.0 + '@smithy/node-config-provider': 4.3.12 + '@smithy/types': 4.13.1 tslib: 2.8.1 '@smithy/util-hex-encoding@4.2.1': dependencies: tslib: 2.8.1 - '@smithy/util-middleware@4.2.10': + '@smithy/util-hex-encoding@4.2.2': dependencies: - '@smithy/types': 4.13.0 tslib: 2.8.1 - '@smithy/util-retry@4.2.10': + '@smithy/util-middleware@4.2.12': dependencies: - '@smithy/service-error-classification': 4.2.10 - '@smithy/types': 4.13.0 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@smithy/util-retry@4.2.12': + dependencies: + '@smithy/service-error-classification': 4.2.12 + '@smithy/types': 4.13.1 tslib: 2.8.1 '@smithy/util-stream@4.5.15': dependencies: - '@smithy/fetch-http-handler': 5.3.11 - '@smithy/node-http-handler': 4.4.12 - '@smithy/types': 4.13.0 - '@smithy/util-base64': 4.3.1 - '@smithy/util-buffer-from': 4.2.1 + '@smithy/fetch-http-handler': 5.3.15 + '@smithy/node-http-handler': 4.5.0 + '@smithy/types': 4.13.1 + '@smithy/util-base64': 4.3.2 + '@smithy/util-buffer-from': 4.2.2 '@smithy/util-hex-encoding': 4.2.1 - '@smithy/util-utf8': 4.2.1 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + + '@smithy/util-stream@4.5.20': + dependencies: + '@smithy/fetch-http-handler': 5.3.15 + '@smithy/node-http-handler': 4.5.0 + '@smithy/types': 4.13.1 + '@smithy/util-base64': 4.3.2 + '@smithy/util-buffer-from': 4.2.2 + '@smithy/util-hex-encoding': 4.2.2 + '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 - '@smithy/util-uri-escape@4.2.1': + '@smithy/util-uri-escape@4.2.2': dependencies: tslib: 2.8.1 @@ -8364,18 +8526,18 @@ snapshots: '@smithy/util-buffer-from': 2.2.0 tslib: 2.8.1 - '@smithy/util-utf8@4.2.1': + '@smithy/util-utf8@4.2.2': dependencies: - '@smithy/util-buffer-from': 4.2.1 + '@smithy/util-buffer-from': 4.2.2 tslib: 2.8.1 - '@smithy/uuid@1.1.1': + '@smithy/uuid@1.1.2': dependencies: tslib: 2.8.1 '@standard-schema/spec@1.1.0': {} - '@swc/helpers@0.5.19': + '@swc/helpers@0.5.20': dependencies: tslib: 2.8.1 @@ -8657,29 +8819,29 @@ snapshots: - '@cypress/request' - supports-color - '@vitest/browser-playwright@4.0.18(playwright@1.58.2)(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.0.18)': + '@vitest/browser-playwright@4.0.18(playwright@1.58.2)(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.0.18)': dependencies: - '@vitest/browser': 4.0.18(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.0)(@vitest/browser-playwright@4.0.18)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) - '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + '@vitest/browser': 4.0.18(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.0.18) + '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) playwright: 1.58.2 tinyrainbow: 3.0.3 - vitest: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.0)(@vitest/browser-playwright@4.0.18)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) + vitest: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.0)(@vitest/browser-playwright@4.0.18)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3) transitivePeerDependencies: - bufferutil - msw - utf-8-validate - vite - '@vitest/browser@4.0.18(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.0)(@vitest/browser-playwright@4.0.18)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))': + '@vitest/browser@4.0.18(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.0.18)': dependencies: - '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) '@vitest/utils': 4.0.18 magic-string: 0.30.21 pixelmatch: 7.1.0 pngjs: 7.0.0 sirv: 3.0.2 tinyrainbow: 3.0.3 - vitest: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.0)(@vitest/browser-playwright@4.0.18)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) + vitest: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.0)(@vitest/browser-playwright@4.0.18)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3) ws: 8.19.0 transitivePeerDependencies: - bufferutil @@ -8687,7 +8849,7 @@ snapshots: - utf-8-validate - vite - '@vitest/coverage-v8@4.0.18(@vitest/browser@4.0.18(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.0)(@vitest/browser-playwright@4.0.18)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)))(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.0)(@vitest/browser-playwright@4.0.18)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))': + '@vitest/coverage-v8@4.0.18(@vitest/browser@4.0.18(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.0.18))(vitest@4.0.18)': dependencies: '@bcoe/v8-coverage': 1.0.2 '@vitest/utils': 4.0.18 @@ -8699,9 +8861,9 @@ snapshots: obug: 2.1.1 std-env: 3.10.0 tinyrainbow: 3.0.3 - vitest: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.0)(@vitest/browser-playwright@4.0.18)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) + vitest: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.0)(@vitest/browser-playwright@4.0.18)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3) optionalDependencies: - '@vitest/browser': 4.0.18(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.0)(@vitest/browser-playwright@4.0.18)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + '@vitest/browser': 4.0.18(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.0.18) '@vitest/expect@3.2.4': dependencies: @@ -8720,21 +8882,21 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.0.3 - '@vitest/mocker@3.2.4(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))': + '@vitest/mocker@3.2.4(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))': dependencies: '@vitest/spy': 3.2.4 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) + vite: 7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3) - '@vitest/mocker@4.0.18(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))': + '@vitest/mocker@4.0.18(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))': dependencies: '@vitest/spy': 4.0.18 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) + vite: 7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3) '@vitest/pretty-format@3.2.4': dependencies: @@ -8791,7 +8953,7 @@ snapshots: async-mutex: 0.5.0 libsignal: '@whiskeysockets/libsignal-node@https://codeload.github.com/whiskeysockets/libsignal-node/tar.gz/1c30d7d7e76a3b0aa120b04dc6a26f5a12dccf67' lru-cache: 11.2.6 - music-metadata: 11.12.1 + music-metadata: 11.12.3 p-queue: 9.1.0 pino: 9.14.0 protobufjs: 7.5.4 @@ -8860,7 +9022,7 @@ snapshots: apache-arrow@18.1.0: dependencies: - '@swc/helpers': 0.5.19 + '@swc/helpers': 0.5.20 '@types/command-line-args': 5.2.3 '@types/command-line-usage': 5.0.4 '@types/node': 20.19.37 @@ -8874,7 +9036,7 @@ snapshots: array-back@3.1.0: {} - array-back@6.2.2: {} + array-back@6.2.3: {} array-flatten@1.1.1: {} @@ -8985,7 +9147,7 @@ snapshots: bowser@2.14.1: {} - brace-expansion@5.0.3: + brace-expansion@5.0.5: dependencies: balanced-match: 4.0.4 @@ -9123,7 +9285,7 @@ snapshots: command-line-usage@7.0.4: dependencies: - array-back: 6.2.2 + array-back: 6.2.3 chalk-template: 0.4.0 table-layout: 4.1.1 typical: 7.3.0 @@ -9407,7 +9569,7 @@ snapshots: methods: 1.1.2 on-finished: 2.4.1 parseurl: 1.3.3 - path-to-regexp: 0.1.12 + path-to-regexp: 8.4.0 proxy-addr: 2.0.7 qs: 6.14.2 range-parser: 1.2.1 @@ -9471,17 +9633,23 @@ snapshots: fast-uri@3.1.0: {} - fast-xml-parser@5.3.8: + fast-xml-builder@1.1.4: + dependencies: + path-expression-matcher: 1.2.0 + + fast-xml-parser@5.5.9: dependencies: - strnum: 2.1.2 + fast-xml-builder: 1.1.4 + path-expression-matcher: 1.2.0 + strnum: 2.2.2 fastq@1.20.1: dependencies: reusify: 1.1.0 - fdir@6.5.0(picomatch@4.0.3): + fdir@6.5.0(picomatch@4.0.4): optionalDependencies: - picomatch: 4.0.3 + picomatch: 4.0.4 fetch-blob@3.2.0: dependencies: @@ -10142,7 +10310,7 @@ snapshots: micromatch@4.0.8: dependencies: braces: 3.0.3 - picomatch: 2.3.1 + picomatch: 4.0.4 mime-db@1.52.0: {} @@ -10164,7 +10332,7 @@ snapshots: minimatch@10.2.3: dependencies: - brace-expansion: 5.0.3 + brace-expansion: 5.0.5 minimist@1.2.8: {} @@ -10194,9 +10362,9 @@ snapshots: ms@2.1.3: {} - music-metadata@11.12.1: + music-metadata@11.12.3: dependencies: - '@borewit/text-codec': 0.2.1 + '@borewit/text-codec': 0.2.2 '@tokenizer/token': 0.3.0 content-type: 1.0.5 debug: 4.4.3 @@ -10217,7 +10385,7 @@ snapshots: nanoid@3.3.11: {} - nanoid@5.1.6: {} + nanoid@5.1.7: {} negotiator@0.6.3: {} @@ -10225,7 +10393,7 @@ snapshots: netmask@2.0.2: {} - node-addon-api@8.6.0: {} + node-addon-api@8.7.0: {} node-api-headers@1.8.0: {} @@ -10270,8 +10438,8 @@ snapshots: is-unicode-supported: 2.1.0 lifecycle-utils: 3.1.1 log-symbols: 7.0.1 - nanoid: 5.1.6 - node-addon-api: 8.6.0 + nanoid: 5.1.7 + node-addon-api: 8.7.0 ora: 9.3.0 pretty-ms: 9.3.0 proper-lockfile: 4.1.2 @@ -10499,6 +10667,8 @@ snapshots: partial-json@0.1.7: {} + path-expression-matcher@1.2.0: {} + path-key@3.1.1: {} path-scurry@1.11.1: @@ -10511,9 +10681,7 @@ snapshots: lru-cache: 11.2.6 minipass: 7.1.3 - path-to-regexp@0.1.12: {} - - path-to-regexp@8.3.0: {} + path-to-regexp@8.4.0: {} pathe@2.0.3: {} @@ -10530,9 +10698,7 @@ snapshots: picocolors@1.1.1: {} - picomatch@2.3.1: {} - - picomatch@4.0.3: {} + picomatch@4.0.4: {} pify@3.0.0: {} @@ -10855,7 +11021,7 @@ snapshots: depd: 2.0.0 is-promise: 4.0.0 parseurl: 1.3.3 - path-to-regexp: 8.3.0 + path-to-regexp: 8.4.0 transitivePeerDependencies: - supports-color @@ -11180,7 +11346,7 @@ snapshots: dependencies: js-tokens: 9.0.1 - strnum@2.1.2: {} + strnum@2.2.2: {} strtok3@10.3.4: dependencies: @@ -11192,7 +11358,7 @@ snapshots: table-layout@4.1.1: dependencies: - array-back: 6.2.2 + array-back: 6.2.3 wordwrapjs: 5.1.1 tar@7.5.11: @@ -11223,8 +11389,8 @@ snapshots: tinyglobby@0.2.15: dependencies: - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 tinypool@1.1.1: {} @@ -11244,7 +11410,7 @@ snapshots: token-types@6.1.2: dependencies: - '@borewit/text-codec': 0.2.1 + '@borewit/text-codec': 0.2.2 '@tokenizer/token': 0.3.0 ieee754: 1.2.1 @@ -11272,7 +11438,7 @@ snapshots: hookable: 6.0.1 import-without-cache: 0.2.5 obug: 2.1.1 - picomatch: 4.0.3 + picomatch: 4.0.4 rolldown: 1.0.0-rc.3 rolldown-plugin-dts: 0.22.1(@typescript/native-preview@7.0.0-dev.20260221.1)(rolldown@1.0.0-rc.3)(typescript@5.9.3) semver: 7.7.4 @@ -11343,8 +11509,6 @@ snapshots: undici-types@7.18.2: {} - undici@7.22.0: {} - undici@7.24.2: {} universalify@0.2.0: {} @@ -11382,13 +11546,13 @@ snapshots: core-util-is: 1.0.2 extsprintf: 1.3.0 - vite-node@3.2.4(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2): + vite-node@3.2.4(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3): dependencies: cac: 6.7.14 debug: 4.4.3 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) + vite: 7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3) transitivePeerDependencies: - '@types/node' - jiti @@ -11403,11 +11567,11 @@ snapshots: - tsx - yaml - vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2): + vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3): dependencies: esbuild: 0.27.3 - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 postcss: 8.5.6 rollup: 4.59.0 tinyglobby: 0.2.15 @@ -11416,13 +11580,13 @@ snapshots: fsevents: 2.3.3 jiti: 2.6.1 tsx: 4.21.0 - yaml: 2.8.2 + yaml: 2.8.3 - vitest@3.2.4(@types/node@25.3.0)(@vitest/browser@4.0.18(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.0)(@vitest/browser-playwright@4.0.18)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)))(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2): + vitest@3.2.4(@types/node@25.3.0)(@vitest/browser@4.0.18(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.0.18))(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3): dependencies: '@types/chai': 5.2.3 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + '@vitest/mocker': 3.2.4(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 @@ -11433,19 +11597,19 @@ snapshots: expect-type: 1.3.0 magic-string: 0.30.21 pathe: 2.0.3 - picomatch: 4.0.3 + picomatch: 4.0.4 std-env: 3.10.0 tinybench: 2.9.0 tinyexec: 0.3.2 tinyglobby: 0.2.15 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) - vite-node: 3.2.4(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) + vite: 7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3) + vite-node: 3.2.4(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 25.3.0 - '@vitest/browser': 4.0.18(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.0)(@vitest/browser-playwright@4.0.18)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + '@vitest/browser': 4.0.18(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.0.18) transitivePeerDependencies: - jiti - less @@ -11460,10 +11624,10 @@ snapshots: - tsx - yaml - vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.0)(@vitest/browser-playwright@4.0.18)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2): + vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.0)(@vitest/browser-playwright@4.0.18)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3): dependencies: '@vitest/expect': 4.0.18 - '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) '@vitest/pretty-format': 4.0.18 '@vitest/runner': 4.0.18 '@vitest/snapshot': 4.0.18 @@ -11474,18 +11638,18 @@ snapshots: magic-string: 0.30.21 obug: 2.1.1 pathe: 2.0.3 - picomatch: 4.0.3 + picomatch: 4.0.4 std-env: 3.10.0 tinybench: 2.9.0 tinyexec: 1.0.2 tinyglobby: 0.2.15 tinyrainbow: 3.0.3 - vite: 7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) + vite: 7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3) why-is-node-running: 2.3.0 optionalDependencies: '@opentelemetry/api': 1.9.0 '@types/node': 25.3.0 - '@vitest/browser-playwright': 4.0.18(playwright@1.58.2)(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.0.18) + '@vitest/browser-playwright': 4.0.18(playwright@1.58.2)(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.0.18) transitivePeerDependencies: - jiti - less @@ -11547,7 +11711,7 @@ snapshots: yallist@5.0.0: {} - yaml@2.8.2: {} + yaml@2.8.3: {} yargs-parser@20.2.9: {} diff --git a/scripts/postinstall.mjs b/scripts/postinstall.mjs index 3bc1771e..67463f04 100644 --- a/scripts/postinstall.mjs +++ b/scripts/postinstall.mjs @@ -80,12 +80,18 @@ async function main() { const needsUpdate = installed[0] < required[0] || (installed[0] === required[0] && installed[1] < required[1]) || - (installed[0] === required[0] && installed[1] === required[1] && installed[2] < required[2]); + (installed[0] === required[0] && + installed[1] === required[1] && + installed[2] < required[2]); if (!needsUpdate) { - console.log(`[mayros] AIngle Cortex v${match[1]} is up to date (requires >=${REQUIRED_VERSION})`); + console.log( + `[mayros] AIngle Cortex v${match[1]} is up to date (requires >=${REQUIRED_VERSION})`, + ); return; } - console.log(`[mayros] AIngle Cortex v${match[1]} installed, updating to >=${REQUIRED_VERSION}...`); + console.log( + `[mayros] AIngle Cortex v${match[1]} installed, updating to >=${REQUIRED_VERSION}...`, + ); } } catch { // Can't determine version — proceed with install/update @@ -165,7 +171,9 @@ async function main() { } catch { // If locked, try direct copy (Windows) copyFileSync(candidatePath, binaryPath); - try { unlinkSync(candidatePath); } catch {} + try { + unlinkSync(candidatePath); + } catch {} console.log(`[mayros] Overwrote ${BINARY_NAME} with ${candidates[0]}`); candidates.length = 0; } @@ -178,12 +186,16 @@ async function main() { // Clean up .old const oldPath = binaryPath + ".old"; - try { if (existsSync(oldPath)) unlinkSync(oldPath); } catch {} + try { + if (existsSync(oldPath)) unlinkSync(oldPath); + } catch {} } else if (!existsSync(binaryPath)) { console.warn( `[mayros] Cortex binary not found after extraction. Install later with: mayros update`, ); - try { unlinkSync(archivePath); } catch {} + try { + unlinkSync(archivePath); + } catch {} return; } @@ -192,11 +204,15 @@ async function main() { } // Cleanup archive and leftover suffixed binaries - try { unlinkSync(archivePath); } catch {} + try { + unlinkSync(archivePath); + } catch {} for (const leftover of readdirSync(INSTALL_DIR).filter( (f) => f.startsWith(baseName + "-") && !f.endsWith(".tar.gz") && !f.endsWith(".zip"), )) { - try { unlinkSync(join(INSTALL_DIR, leftover)); } catch {} + try { + unlinkSync(join(INSTALL_DIR, leftover)); + } catch {} } console.log(`[mayros] AIngle Cortex installed at ${binaryPath}`); diff --git a/scripts/test-parallel.mjs b/scripts/test-parallel.mjs index 3982c729..e247589f 100644 --- a/scripts/test-parallel.mjs +++ b/scripts/test-parallel.mjs @@ -182,7 +182,7 @@ const maxWorkersForRun = (name) => { return resolvedOverride; } if (isCI && !isMacOS) { - return null; + return 2; } if (isCI && isMacOS) { return 1; diff --git a/src/browser/pw-tools-core.snapshot.navigate-guard.test.ts b/src/browser/pw-tools-core.snapshot.navigate-guard.test.ts index 46d9e0d4..8729e6de 100644 --- a/src/browser/pw-tools-core.snapshot.navigate-guard.test.ts +++ b/src/browser/pw-tools-core.snapshot.navigate-guard.test.ts @@ -32,7 +32,8 @@ describe("pw-tools-core.snapshot navigate guard", () => { it("navigates valid network URLs with clamped timeout", async () => { const spy = vi.spyOn(ssrf, "resolvePinnedHostnameWithPolicy").mockResolvedValue({ hostname: "example.com", - ip: "93.184.216.34", + addresses: ["93.184.216.34"], + lookup: (() => {}) as unknown as typeof import("node:dns").lookup, }); const goto = vi.fn(async () => {}); diff --git a/src/cli/headless-cli.ts b/src/cli/headless-cli.ts index 76e85d65..24442e50 100644 --- a/src/cli/headless-cli.ts +++ b/src/cli/headless-cli.ts @@ -201,7 +201,7 @@ export async function runHeadless(opts: HeadlessOptions): Promise { // 3. Ensure gateway + cortex are running (unless explicit --url was provided) if (!opts.url) { - const snapshot = readConfigFileSnapshot(); + const snapshot = await readConfigFileSnapshot(); const config = snapshot.valid ? snapshot.config : {}; await ensureServicesRunning({ config, diff --git a/src/cli/kaneru-cli.test.ts b/src/cli/kaneru-cli.test.ts index 4266e90e..3d4de65d 100644 --- a/src/cli/kaneru-cli.test.ts +++ b/src/cli/kaneru-cli.test.ts @@ -163,7 +163,26 @@ describe("kaneru subcommands", () => { const program = makeProgram(); const kaneru = getKaneru(program); const names = kaneru.commands.map((c) => c.name()).sort(); - expect(names).toEqual(["comment", "consensus", "dashboard", "decisions", "delegate", "discover", "dojo", "fuel", "fuse", "learn", "mission", "peers", "project", "pulse", "route", "squad", "sync", "venture"]); + expect(names).toEqual([ + "comment", + "consensus", + "dashboard", + "decisions", + "delegate", + "discover", + "dojo", + "fuel", + "fuse", + "learn", + "mission", + "peers", + "project", + "pulse", + "route", + "squad", + "sync", + "venture", + ]); }); }); @@ -177,9 +196,9 @@ describe("createFacade import error", () => { // createFacade is called outside the try/catch in each action handler, // so the error propagates up through Commander's parseAsync. - await expect( - program.parseAsync(["kaneru", "dashboard"], { from: "user" }), - ).rejects.toThrow("Failed to load Kaneru module"); + await expect(program.parseAsync(["kaneru", "dashboard"], { from: "user" })).rejects.toThrow( + "Failed to load Kaneru module", + ); }); }); @@ -218,9 +237,7 @@ describe("handleError", () => { await freshProgram.parseAsync(["kaneru", "dashboard"], { from: "user" }); - expect(errorSpy).toHaveBeenCalledWith( - expect.stringContaining("Cortex is not running"), - ); + expect(errorSpy).toHaveBeenCalledWith(expect.stringContaining("Cortex is not running")); expect(process.exitCode).toBe(1); }); @@ -252,9 +269,7 @@ describe("handleError", () => { await freshProgram.parseAsync(["kaneru", "dashboard"], { from: "user" }); - expect(errorSpy).toHaveBeenCalledWith( - expect.stringContaining("Cortex error (404): not found"), - ); + expect(errorSpy).toHaveBeenCalledWith(expect.stringContaining("Cortex error (404): not found")); expect(process.exitCode).toBe(1); }); diff --git a/src/cli/kaneru-cli.ts b/src/cli/kaneru-cli.ts index e26c3b34..b4a71392 100644 --- a/src/cli/kaneru-cli.ts +++ b/src/cli/kaneru-cli.ts @@ -105,7 +105,9 @@ async function createFacade(opts: { cortexPort?: string; cortexToken?: string; }) { - let mod: { KaneruFacade: typeof import("../../extensions/agent-mesh/kaneru-facade.js").KaneruFacade }; + let mod: { + KaneruFacade: typeof import("../../extensions/agent-mesh/kaneru-facade.js").KaneruFacade; + }; try { mod = await import("../../extensions/agent-mesh/kaneru-facade.js"); } catch { @@ -418,29 +420,31 @@ export function registerKaneruCli(program: Command) { .requiredOption("--prefix ", "Short prefix for the venture") .requiredOption("--directive ", "Venture directive / objective") .option("--fuel-limit ", "Fuel limit in cents") - .action(async (opts: { name: string; prefix: string; directive: string; fuelLimit?: string }) => { - const parent = kaneru.opts(); - const mgrs = await createVentureManagers(parent); - try { - const v = await mgrs.venture.create({ - name: opts.name, - prefix: opts.prefix, - directive: opts.directive, - fuelLimit: opts.fuelLimit ? parseInt(opts.fuelLimit, 10) : undefined, - }); - console.log(`Venture created: ${v.id}`); - console.log(` Name: ${v.name}`); - console.log(` Prefix: ${v.prefix}`); - console.log(` Directive: ${v.directive}`); - if (v.fuelLimit !== undefined) { - console.log(` Fuel limit: ${v.fuelLimit} cents`); + .action( + async (opts: { name: string; prefix: string; directive: string; fuelLimit?: string }) => { + const parent = kaneru.opts(); + const mgrs = await createVentureManagers(parent); + try { + const v = await mgrs.venture.create({ + name: opts.name, + prefix: opts.prefix, + directive: opts.directive, + fuelLimit: opts.fuelLimit ? parseInt(opts.fuelLimit, 10) : undefined, + }); + console.log(`Venture created: ${v.id}`); + console.log(` Name: ${v.name}`); + console.log(` Prefix: ${v.prefix}`); + console.log(` Directive: ${v.directive}`); + if (v.fuelLimit !== undefined) { + console.log(` Fuel limit: ${v.fuelLimit} cents`); + } + } catch (err) { + handleError(err); + } finally { + mgrs.destroy(); } - } catch (err) { - handleError(err); - } finally { - mgrs.destroy(); - } - }); + }, + ); // mayros kaneru venture list venture @@ -541,7 +545,10 @@ export function registerKaneruCli(program: Command) { const parent = kaneru.opts(); const mgrs = await createVentureManagers(parent); try { - const missions = await mgrs.mission.list(opts.venture, opts.status ? { status: opts.status as "queued" } : undefined); + const missions = await mgrs.mission.list( + opts.venture, + opts.status ? { status: opts.status as "queued" } : undefined, + ); if (missions.length === 0) { console.log("No missions found."); return; @@ -596,7 +603,11 @@ export function registerKaneruCli(program: Command) { const parent = kaneru.opts(); const mgrs = await createVentureManagers(parent); try { - const result = await mgrs.mission.transition(opts.mission, opts.status as "queued", opts.run); + const result = await mgrs.mission.transition( + opts.mission, + opts.status as "queued", + opts.run, + ); console.log(`Mission transitioned: ${result.id}`); console.log(` Identifier: ${result.identifier}`); console.log(` New status: ${result.status}`); @@ -618,38 +629,40 @@ export function registerKaneruCli(program: Command) { .option("--duration ", "Duration in milliseconds", "0") .option("--failed", "Mark as failed instead of success") .option("--squad ", "Squad ID for knowledge transfer") - .action(async (opts: { - mission: string; - agent: string; - venture: string; - title: string; - duration: string; - failed?: boolean; - squad?: string; - }) => { - const parent = kaneru.opts(); - const facade = await createFacade(parent); - try { - const result = await facade.completeMissionWithLearning({ - missionId: opts.mission, - agentId: opts.agent, - ventureId: opts.venture, - title: opts.title, - success: !opts.failed, - durationMs: parseInt(opts.duration, 10) || 0, - squadId: opts.squad, - }); - console.log("Mission completed with learning:"); - console.log(` Expertise: ${(result.profile.expertise * 100).toFixed(1)}%`); - console.log(` Domain: ${result.profile.domain}`); - console.log(` Task type: ${result.profile.taskType}`); - console.log(` Notification: ${result.notification.message.split("\n")[0]}`); - } catch (err) { - handleError(err); - } finally { - facade.destroy(); - } - }); + .action( + async (opts: { + mission: string; + agent: string; + venture: string; + title: string; + duration: string; + failed?: boolean; + squad?: string; + }) => { + const parent = kaneru.opts(); + const facade = await createFacade(parent); + try { + const result = await facade.completeMissionWithLearning({ + missionId: opts.mission, + agentId: opts.agent, + ventureId: opts.venture, + title: opts.title, + success: !opts.failed, + durationMs: parseInt(opts.duration, 10) || 0, + squadId: opts.squad, + }); + console.log("Mission completed with learning:"); + console.log(` Expertise: ${(result.profile.expertise * 100).toFixed(1)}%`); + console.log(` Domain: ${result.profile.domain}`); + console.log(` Task type: ${result.profile.taskType}`); + console.log(` Notification: ${result.notification.message.split("\n")[0]}`); + } catch (err) { + handleError(err); + } finally { + facade.destroy(); + } + }, + ); // ------------------------------------------------------------------ // mayros kaneru pulse @@ -672,7 +685,9 @@ export function registerKaneruCli(program: Command) { const triggerList = opts.triggers ?.split(",") .map((t) => t.trim()) - .filter(Boolean) as import("../../extensions/kaneru/pulse.js").PulseTrigger[] | undefined; + .filter(Boolean) as + | import("../../extensions/kaneru/pulse.js").PulseTrigger[] + | undefined; await mgrs.pulse.register(opts.agent, opts.venture, { interval: opts.interval, triggers: triggerList ?? ["timer"], @@ -732,7 +747,9 @@ export function registerKaneruCli(program: Command) { } console.log(`Queued pulses (${queued.length}):\n`); for (const p of queued) { - console.log(` ${p.id} trigger: ${p.trigger} coalesced: ${p.coalescedCount} requested: ${p.requestedAt}`); + console.log( + ` ${p.id} trigger: ${p.trigger} coalesced: ${p.coalescedCount} requested: ${p.requestedAt}`, + ); } } catch (err) { handleError(err); @@ -762,7 +779,9 @@ export function registerKaneruCli(program: Command) { console.log(`${"=".repeat(40)}`); console.log(` Total spent: ${summary.totalCents} cents`); console.log(` Fuel limit: ${summary.fuelLimit || "unlimited"}`); - console.log(` Remaining: ${summary.fuelLimit ? `${summary.remaining} cents` : "unlimited"}`); + console.log( + ` Remaining: ${summary.fuelLimit ? `${summary.remaining} cents` : "unlimited"}`, + ); console.log(` Burn rate: ${summary.burnRate} cents/hour`); if (summary.byAgent.length > 0) { console.log(`\n By agent:`); @@ -795,7 +814,9 @@ export function registerKaneruCli(program: Command) { if (analytics.byProvider.length > 0) { console.log(`\n By provider:`); for (const p of analytics.byProvider) { - console.log(` ${p.provider}/${p.model}: ${p.costCents} cents (${p.eventCount} events)`); + console.log( + ` ${p.provider}/${p.model}: ${p.costCents} cents (${p.eventCount} events)`, + ); } } if (analytics.timeSeries.points.length > 0) { @@ -1053,7 +1074,9 @@ export function registerKaneruCli(program: Command) { } console.log(`Top agents for ${opts.domain}:${opts.taskType} (${profiles.length}):\n`); for (const p of profiles) { - console.log(` ${p.agentId} expertise: ${(p.expertise * 100).toFixed(1)}% success: ${(p.successRate * 100).toFixed(1)}% missions: ${p.missionCount}`); + console.log( + ` ${p.agentId} expertise: ${(p.expertise * 100).toFixed(1)}% success: ${(p.successRate * 100).toFixed(1)}% missions: ${p.missionCount}`, + ); } } catch (err) { handleError(err); @@ -1085,7 +1108,9 @@ export function registerKaneruCli(program: Command) { } console.log(`Decisions (${records.length}):\n`); for (const d of records) { - console.log(` ${d.id} [${d.strategy}] confidence: ${(d.confidence * 100).toFixed(1)}%`); + console.log( + ` ${d.id} [${d.strategy}] confidence: ${(d.confidence * 100).toFixed(1)}%`, + ); console.log(` Question: ${d.question}`); console.log(` Outcome: ${d.resolvedValue}`); console.log(` Decided: ${d.decidedAt}`); @@ -1249,9 +1274,8 @@ export function registerKaneruCli(program: Command) { const parent = kaneru.opts(); const mgrs = await createVentureManagers(parent); try { - const { DistributedVentureManager } = await import( - "../../extensions/kaneru/distributed.js" - ); + const { DistributedVentureManager } = + await import("../../extensions/kaneru/distributed.js"); const dist = new DistributedVentureManager(mgrs.client, "mayros"); const newPeers = await dist.discoverPeers(opts.venture); if (newPeers.length === 0) { @@ -1291,7 +1315,8 @@ export function registerKaneruCli(program: Command) { const client = new CortexClient({ host, port, authToken: parent.cortexToken }); const ns = "mayros"; const dojoSvc = new DojoService( - client, ns, + client, + ns, new VentureManager(client, ns), new ChainManager(client, ns), new DirectiveManager(client, ns), diff --git a/src/cli/kg-cli.ts b/src/cli/kg-cli.ts index 3f999a6d..31bbffda 100644 --- a/src/cli/kg-cli.ts +++ b/src/cli/kg-cli.ts @@ -20,6 +20,7 @@ import { ProjectMemory } from "../../extensions/memory-semantic/project-memory.j import { codePredicate } from "../../extensions/code-indexer/rdf-mapper.js"; import { getIndexStats } from "../../extensions/code-indexer/incremental.js"; import { resolveCortexClient, resolveNamespace } from "./shared/cortex-resolution.js"; +import type { CortexClient } from "../../extensions/shared/cortex-client.js"; // ============================================================================ // Registration diff --git a/src/cli/mamoru-cli.ts b/src/cli/mamoru-cli.ts index dbed7130..0d74263d 100644 --- a/src/cli/mamoru-cli.ts +++ b/src/cli/mamoru-cli.ts @@ -152,7 +152,9 @@ export function registerMamoruCli(program: Command) { console.log(" (none)"); } else { for (const req of pending) { - console.log(` ${req.id} ${req.host}:${req.port} binary=${req.binary ?? "unknown"} [${req.status}]`); + console.log( + ` ${req.id} ${req.host}:${req.port} binary=${req.binary ?? "unknown"} [${req.status}]`, + ); } } } catch (err) { @@ -259,13 +261,21 @@ export function registerMamoruCli(program: Command) { console.log(`Inference Logs (last ${logs.length}):`); for (const log of logs) { const ts = new Date(log.timestamp).toISOString(); - console.log(` ${ts} ${log.model} ${log.inputTokens}→${log.outputTokens} ${log.durationMs}ms [${log.status}]`); + console.log( + ` ${ts} ${log.model} ${log.inputTokens}→${log.outputTokens} ${log.durationMs}ms [${log.status}]`, + ); } console.log("\nUsage Summary:"); console.log(` Total requests: ${summary.totalRequests}`); console.log(` Total tokens: ${summary.totalTokens}`); - console.log(` By provider: ${Object.entries(summary.byProvider).map(([k, v]) => `${k}=${v}`).join(", ") || "none"}`); + console.log( + ` By provider: ${ + Object.entries(summary.byProvider) + .map(([k, v]) => `${k}=${v}`) + .join(", ") || "none" + }`, + ); } catch (err) { handleError(err); } @@ -330,7 +340,9 @@ export function registerMamoruCli(program: Command) { } else { for (const k of keyList) { const exp = k.expiresAt ? new Date(k.expiresAt).toISOString() : "never"; - console.log(` ${k.id} ${k.name} expires=${exp} scopes=${(k.scopes ?? []).join(",") || "all"}`); + console.log( + ` ${k.id} ${k.name} expires=${exp} scopes=${(k.scopes ?? []).join(",") || "all"}`, + ); } } } catch (err) { @@ -345,27 +357,32 @@ export function registerMamoruCli(program: Command) { .requiredOption("--name ", "Key name") .option("--scopes ", "Comma-separated scopes") .option("--expires-in-days ", "Expiration in days") - .action(async (opts: { agent: string; name: string; scopes?: string; expiresInDays?: string }) => { - const parent = mamoru.opts(); - try { - const { stack } = await loadMamoruStack(parent); - if (!stack.apiKeys) { - console.error("API keys require a Cortex connection."); - process.exitCode = 1; - return; + .action( + async (opts: { agent: string; name: string; scopes?: string; expiresInDays?: string }) => { + const parent = mamoru.opts(); + try { + const { stack } = await loadMamoruStack(parent); + if (!stack.apiKeys) { + console.error("API keys require a Cortex connection."); + process.exitCode = 1; + return; + } + const scopes = opts.scopes ? opts.scopes.split(",").map((s) => s.trim()) : undefined; + const expiresInDays = opts.expiresInDays ? parseInt(opts.expiresInDays, 10) : undefined; + const result = await stack.apiKeys.create(opts.agent, opts.name, { + scopes, + expiresInDays, + }); + console.log(`API key created: ${result.key.id}`); + console.log(` Name: ${result.key.name}`); + console.log(` Plaintext: ${result.plaintext}`); + console.log(""); + console.log(" Save this key now. It will not be shown again."); + } catch (err) { + handleError(err); } - const scopes = opts.scopes ? opts.scopes.split(",").map((s) => s.trim()) : undefined; - const expiresInDays = opts.expiresInDays ? parseInt(opts.expiresInDays, 10) : undefined; - const result = await stack.apiKeys.create(opts.agent, opts.name, { scopes, expiresInDays }); - console.log(`API key created: ${result.key.id}`); - console.log(` Name: ${result.key.name}`); - console.log(` Plaintext: ${result.plaintext}`); - console.log(""); - console.log(" Save this key now. It will not be shown again."); - } catch (err) { - handleError(err); - } - }); + }, + ); keys .command("revoke") @@ -500,7 +517,10 @@ export function registerMamoruCli(program: Command) { model .command("suggest") .description("Suggest local models based on GPU capabilities") - .option("--activity ", "Filter by activity: coding, chat, reasoning, creative, analysis, multilingual, vision, agents") + .option( + "--activity ", + "Filter by activity: coding, chat, reasoning, creative, analysis, multilingual, vision, agents", + ) .action(async (opts: { activity?: string }) => { const parent = mamoru.opts(); try { @@ -516,16 +536,24 @@ export function registerMamoruCli(program: Command) { process.exitCode = 1; return; } - const activity = opts.activity as Parameters[0]; + const activity = opts.activity as Parameters< + typeof stack.localModel.suggestByActivity + >[0]; const models = stack.localModel.suggestByActivity(activity, gpu); - const activityInfo = stack.localModel.listActivities().find((a) => a.activity === activity); - console.log(`Models for "${activityInfo?.label ?? activity}" (VRAM: ${gpu.vramMB}MB ${gpu.vendor}):`); + const activityInfo = stack.localModel + .listActivities() + .find((a) => a.activity === activity); + console.log( + `Models for "${activityInfo?.label ?? activity}" (VRAM: ${gpu.vramMB}MB ${gpu.vendor}):`, + ); console.log(` ${activityInfo?.description ?? ""}\n`); if (models.length === 0) { console.log(" No models found for your hardware and selected activity."); } else { for (const m of models) { - console.log(` ${m.id} ${m.parameters} ${m.provider} vram=${m.vramRequired}MB ctx=${m.contextLength} ${m.runtime}`); + console.log( + ` ${m.id} ${m.parameters} ${m.provider} vram=${m.vramRequired}MB ctx=${m.contextLength} ${m.runtime}`, + ); console.log(` ${m.strengths}`); } } @@ -537,10 +565,14 @@ export function registerMamoruCli(program: Command) { console.log(" No models found for your hardware."); } else { for (const s of suggestions) { - console.log(` ${s.model} runtime=${s.runtime} vram=${s.vramRequired}MB — ${s.reason}`); + console.log( + ` ${s.model} runtime=${s.runtime} vram=${s.vramRequired}MB — ${s.reason}`, + ); } } - console.log("\n Tip: Use --activity to filter by task. Run 'mayros mamoru model activities' for a list."); + console.log( + "\n Tip: Use --activity to filter by task. Run 'mayros mamoru model activities' for a list.", + ); } } catch (err) { handleError(err); diff --git a/src/daemon/program-args.ts b/src/daemon/program-args.ts index 837e9996..05396ad6 100644 --- a/src/daemon/program-args.ts +++ b/src/daemon/program-args.ts @@ -179,8 +179,13 @@ async function resolveCliProgramArguments(params: { const nodePath = params.nodePath ?? (isNodeRuntime(execPath) ? execPath : await resolveNodePath()); const cliEntrypointPath = await resolveCliEntrypointPathForService(); + // Derive package root from the entrypoint path (e.g. .../dist/index.js → ...) + // so the LaunchAgent WorkingDirectory points to the package, not "/". + const distIndex = cliEntrypointPath.lastIndexOf(`${path.sep}dist${path.sep}`); + const workingDirectory = distIndex > 0 ? cliEntrypointPath.slice(0, distIndex) : undefined; return { programArguments: [nodePath, cliEntrypointPath, ...params.args], + workingDirectory, }; } diff --git a/src/infra/git-worktree.test.ts b/src/infra/git-worktree.test.ts index f5a3fed7..304120fc 100644 --- a/src/infra/git-worktree.test.ts +++ b/src/infra/git-worktree.test.ts @@ -291,8 +291,12 @@ describe("git-worktree", () => { const { isWorktreePath } = await import("./git-worktree.js"); const repoRoot = path.resolve("/repo"); - expect(isWorktreePath(path.join(repoRoot, ".mayros/worktrees/feature-a"), repoRoot)).toBe(true); - expect(isWorktreePath(path.join(repoRoot, ".mayros/worktrees/feature-a/src/file.ts"), repoRoot)).toBe(true); + expect(isWorktreePath(path.join(repoRoot, ".mayros/worktrees/feature-a"), repoRoot)).toBe( + true, + ); + expect( + isWorktreePath(path.join(repoRoot, ".mayros/worktrees/feature-a/src/file.ts"), repoRoot), + ).toBe(true); }); it("returns false for paths outside worktree base", async () => { diff --git a/src/tui/image-paste.test.ts b/src/tui/image-paste.test.ts index 6f62727b..0d4eb635 100644 --- a/src/tui/image-paste.test.ts +++ b/src/tui/image-paste.test.ts @@ -61,7 +61,7 @@ describe("image paste", () => { }); it("handles image indicator text formatting", () => { - const pendingCount = 3; + const pendingCount = 3 as number; const indicator = pendingCount === 1 ? "[1 image attached]" : `[${pendingCount} images attached]`; expect(indicator).toBe("[3 images attached]"); diff --git a/src/tui/session-manager.ts b/src/tui/session-manager.ts index 69878dcd..875e9d9c 100644 --- a/src/tui/session-manager.ts +++ b/src/tui/session-manager.ts @@ -55,7 +55,7 @@ export class SessionManager { * Rename the current session. */ async renameSession(key: string, displayName: string): Promise { - await this.client.patchSession({ key, displayName }); + await this.client.patchSession({ key, label: displayName }); } /** diff --git a/src/tui/tui-session-actions.test.ts b/src/tui/tui-session-actions.test.ts index 06722281..f5ed5325 100644 --- a/src/tui/tui-session-actions.test.ts +++ b/src/tui/tui-session-actions.test.ts @@ -39,6 +39,7 @@ describe("tui session actions", () => { autoMessageSent: false, toolsExpanded: false, showThinking: false, + pendingImages: new Map(), connectionStatus: "connected", activityStatus: "idle", statusTimeout: null, diff --git a/tools/vscode-extension/src/webview/chat/chat.css b/tools/vscode-extension/src/webview/chat/chat.css index 07f8a9f2..fca32a87 100644 --- a/tools/vscode-extension/src/webview/chat/chat.css +++ b/tools/vscode-extension/src/webview/chat/chat.css @@ -101,8 +101,14 @@ } @keyframes msg-in { - from { opacity: 0; transform: translateY(4px); } - to { opacity: 1; transform: translateY(0); } + from { + opacity: 0; + transform: translateY(4px); + } + to { + opacity: 1; + transform: translateY(0); + } } .message--user { @@ -168,8 +174,13 @@ } @keyframes pulse { - 0%, 100% { opacity: 0.3; } - 50% { opacity: 1; } + 0%, + 100% { + opacity: 0.3; + } + 50% { + opacity: 1; + } } .message__tools { @@ -266,7 +277,9 @@ border: none; cursor: pointer; font-family: inherit; - transition: background-color 0.12s ease, opacity 0.12s ease; + transition: + background-color 0.12s ease, + opacity 0.12s ease; } .input-area__btn:disabled { diff --git a/tools/vscode-extension/src/webview/chat/index.html b/tools/vscode-extension/src/webview/chat/index.html index 63faea37..fdeb0a9c 100644 --- a/tools/vscode-extension/src/webview/chat/index.html +++ b/tools/vscode-extension/src/webview/chat/index.html @@ -1,24 +1,28 @@ - + - - - - Mayros Chat - - - - -

- - + + + + Mayros Chat + + + + +
+ + diff --git a/tools/vscode-extension/src/webview/kg/index.html b/tools/vscode-extension/src/webview/kg/index.html index a0f6cd68..3ad85a9a 100644 --- a/tools/vscode-extension/src/webview/kg/index.html +++ b/tools/vscode-extension/src/webview/kg/index.html @@ -1,24 +1,28 @@ - + - - - - Mayros Knowledge Graph - - - - -
- - + + + + Mayros Knowledge Graph + + + + +
+ + diff --git a/tools/vscode-extension/src/webview/plan/index.html b/tools/vscode-extension/src/webview/plan/index.html index 03e663ca..be85ef4c 100644 --- a/tools/vscode-extension/src/webview/plan/index.html +++ b/tools/vscode-extension/src/webview/plan/index.html @@ -1,24 +1,28 @@ - + - - - - Mayros Plan Mode - - - - -
- - + + + + Mayros Plan Mode + + + + +
+ + diff --git a/tools/vscode-extension/src/webview/trace/index.html b/tools/vscode-extension/src/webview/trace/index.html index afd8a8d6..57ab8842 100644 --- a/tools/vscode-extension/src/webview/trace/index.html +++ b/tools/vscode-extension/src/webview/trace/index.html @@ -1,24 +1,28 @@ - + - - - - Mayros Trace Viewer - - - - -
- - + + + + Mayros Trace Viewer + + + + +
+ + diff --git a/ui/src/i18n/locales/pt-BR.ts b/ui/src/i18n/locales/pt-BR.ts index 4ff516b2..f0eb6bee 100644 --- a/ui/src/i18n/locales/pt-BR.ts +++ b/ui/src/i18n/locales/pt-BR.ts @@ -59,7 +59,8 @@ export const pt_BR: TranslationMap = { mcp: "Metricas do servidor MCP, uso de ferramentas e status de conexao.", ventures: "Projetos venture, quadros de missoes e orcamentos de combustivel.", kaneru: "Esquadroes multi-agente, roteamento Q-learning e decisoes por consenso.", - canvas: "Superficies A2UI com contexto de ventures: visao geral, missoes, cadeia e combustivel.", + canvas: + "Superficies A2UI com contexto de ventures: visao geral, missoes, cadeia e combustivel.", }, overview: { access: { diff --git a/ui/src/ui/app-render.ts b/ui/src/ui/app-render.ts index 20ed9ffa..3cee7353 100644 --- a/ui/src/ui/app-render.ts +++ b/ui/src/ui/app-render.ts @@ -90,6 +90,7 @@ import { saveOnboardingConfig, detectOllama, detectGPU, + fetchOllamaModels, } from "./controllers/onboarding.ts"; import { renderCommandBar } from "./views/command-bar.ts"; import { @@ -926,20 +927,25 @@ export function renderApp(state: AppViewState) { onSplitRatioChange: (ratio: number) => state.handleSplitRatioChange(ratio), assistantName: state.assistantName, assistantAvatar: state.assistantAvatar, - voiceRecording: (state as Record).chatVoiceRecording as boolean ?? false, - onToggleVoice: isVoiceAvailable() ? () => { - const recording = (state as Record).chatVoiceRecording as boolean ?? false; - if (recording) { - stopVoiceRecognition(state.commandBar); - (state as Record).chatVoiceRecording = false; - } else { - startVoiceRecognition(state.commandBar, (text) => { - state.chatMessage = (state.chatMessage ? state.chatMessage + " " : "") + text; - (state as Record).chatVoiceRecording = false; - }); - (state as Record).chatVoiceRecording = true; - } - } : undefined, + voiceRecording: + ((state as Record).chatVoiceRecording as boolean) ?? false, + onToggleVoice: isVoiceAvailable() + ? () => { + const recording = + ((state as Record).chatVoiceRecording as boolean) ?? false; + if (recording) { + stopVoiceRecognition(state.commandBar); + (state as Record).chatVoiceRecording = false; + } else { + startVoiceRecognition(state.commandBar, (text) => { + state.chatMessage = + (state.chatMessage ? state.chatMessage + " " : "") + text; + (state as Record).chatVoiceRecording = false; + }); + (state as Record).chatVoiceRecording = true; + } + } + : undefined, }) : nothing } @@ -1079,7 +1085,13 @@ export function renderApp(state: AppViewState) { dashboard: state.venturesDashboard, onRefresh: () => void loadVenturesDashboard(state), onNewVenture: () => { - state.setupWizard = { ...state.setupWizard, open: true, step: "venture", error: null, result: null }; + state.setupWizard = { + ...state.setupWizard, + open: true, + step: "venture", + error: null, + result: null, + }; }, }) : nothing @@ -1094,18 +1106,31 @@ export function renderApp(state: AppViewState) { onRefresh: () => void loadKaneruDashboard(state), squadBuilder: (state as Record).squadBuilderAgents ? { - availableAgents: (state as Record).squadBuilderAgents as Array<{ agentId: string; role: string }>, - selectedAgents: ((state as Record).squadBuilderSelected as string[]) ?? [], - squadName: ((state as Record).squadBuilderName as string) ?? "", - strategy: ((state as Record).squadBuilderStrategy as string) ?? "additive", + availableAgents: (state as Record) + .squadBuilderAgents as Array<{ agentId: string; role: string }>, + selectedAgents: + ((state as Record).squadBuilderSelected as string[]) ?? [], + squadName: + ((state as Record).squadBuilderName as string) ?? "", + strategy: + ((state as Record).squadBuilderStrategy as string) ?? + "additive", onToggleAgent: (agentId: string) => { - const sel = ((state as Record).squadBuilderSelected as string[]) ?? []; - (state as Record).squadBuilderSelected = sel.includes(agentId) + const sel = + ((state as Record).squadBuilderSelected as string[]) ?? + []; + (state as Record).squadBuilderSelected = sel.includes( + agentId, + ) ? sel.filter((id) => id !== agentId) : [...sel, agentId]; }, - onNameChange: (name: string) => { (state as Record).squadBuilderName = name; }, - onStrategyChange: (strategy: string) => { (state as Record).squadBuilderStrategy = strategy; }, + onNameChange: (name: string) => { + (state as Record).squadBuilderName = name; + }, + onStrategyChange: (strategy: string) => { + (state as Record).squadBuilderStrategy = strategy; + }, onCreate: () => {}, creating: false, } @@ -1120,12 +1145,19 @@ export function renderApp(state: AppViewState) { loading: state.canvasLoading, error: state.canvasError, jsonl: state.canvasJsonl, - activeSurface: (state as Record).canvasActiveSurface as string ?? "all", + activeSurface: + ((state as Record).canvasActiveSurface as string) ?? "all", onSurfaceChange: (surface: string) => { (state as Record).canvasActiveSurface = surface; void loadCanvasSurface(state, surface === "all" ? undefined : surface); }, - onRefresh: () => void loadCanvasSurface(state, ((state as Record).canvasActiveSurface as string) === "all" ? undefined : (state as Record).canvasActiveSurface as string), + onRefresh: () => + void loadCanvasSurface( + state, + ((state as Record).canvasActiveSurface as string) === "all" + ? undefined + : ((state as Record).canvasActiveSurface as string), + ), }) : nothing } @@ -1196,7 +1228,7 @@ export function renderApp(state: AppViewState) { "route-task": "route task", "list-agents": "list agents", "squad-status": "squad status", - "decisions": "recent decisions", + decisions: "recent decisions", }; const query = labels[action] ?? action; state.commandBar = { ...state.commandBar, query }; @@ -1219,6 +1251,32 @@ export function renderApp(state: AppViewState) { onActivityChange: (activity: string) => { state.onboardingWizard = { ...state.onboardingWizard, selectedActivity: activity }; }, + onAgentNameChange: (name: string) => { + state.onboardingWizard = { ...state.onboardingWizard, agentName: name }; + }, + onRefreshOllamaModels: () => { + console.log("[onboarding] Refresh clicked, client connected:", !!state.client); + const next = { ...state.onboardingWizard }; + next.saving = true; + state.onboardingWizard = { ...next }; + detectOllama(next, state.client) + .then(() => { + console.log( + "[onboarding] detectOllama result:", + next.ollamaDetected, + "models:", + next.ollamaInstalledModels, + ); + next.saving = false; + state.onboardingWizard = { ...next }; + }) + .catch((err) => { + console.error("[onboarding] Refresh failed:", err); + next.saving = false; + next.error = "Failed to check Ollama. Is the gateway running?"; + state.onboardingWizard = { ...next }; + }); + }, onNext: () => { const next = { ...state.onboardingWizard }; // When moving from apikey → ready, save the config first @@ -1242,12 +1300,11 @@ export function renderApp(state: AppViewState) { onboardingNext(next); state.onboardingWizard = next; // Detect Ollama + GPU in parallel - Promise.all([ - detectOllama(next, state.client), - detectGPU(next, state.client), - ]).then(() => { - state.onboardingWizard = { ...next }; - }); + Promise.all([detectOllama(next, state.client), detectGPU(next, state.client)]).then( + () => { + state.onboardingWizard = { ...next }; + }, + ); return; } onboardingNext(next); diff --git a/ui/src/ui/app-view-state.ts b/ui/src/ui/app-view-state.ts index b3c92245..516fcb80 100644 --- a/ui/src/ui/app-view-state.ts +++ b/ui/src/ui/app-view-state.ts @@ -142,6 +142,11 @@ export type AppViewState = { kaneruLoading: boolean; kaneruError: string | null; kaneruDashboard: KaneruDashboardResponse | null; + squadBuilderAgents: Array<{ agentId: string; role: string }>; + squadBuilderSelected: string[]; + squadBuilderName: string; + squadBuilderStrategy: string; + squadBuilderCreating: boolean; canvasLoading: boolean; canvasError: string | null; canvasJsonl: string | null; diff --git a/ui/src/ui/app.ts b/ui/src/ui/app.ts index f6cb5a47..f6e4fcee 100644 --- a/ui/src/ui/app.ts +++ b/ui/src/ui/app.ts @@ -230,6 +230,11 @@ export class MayrosApp extends LitElement { @state() kaneruLoading = false; @state() kaneruError: string | null = null; @state() kaneruDashboard: KaneruDashboardResponse | null = null; + @state() squadBuilderAgents: Array<{ agentId: string; role: string }> = []; + @state() squadBuilderSelected: string[] = []; + @state() squadBuilderName = ""; + @state() squadBuilderStrategy = "additive"; + @state() squadBuilderCreating = false; @state() canvasLoading = false; @state() canvasError: string | null = null; diff --git a/ui/src/ui/controllers/command-bar.ts b/ui/src/ui/controllers/command-bar.ts index 36176e0c..2f145d94 100644 --- a/ui/src/ui/controllers/command-bar.ts +++ b/ui/src/ui/controllers/command-bar.ts @@ -84,17 +84,14 @@ export async function executeCommand( const missions = ((result.missions as Array>) ?? []).slice(0, 5); state.result = missions.length > 0 - ? missions - .map((m) => `${m.identifier} [${m.priority}] ${m.title}`) - .join("\n") + ? missions.map((m) => `${m.identifier} [${m.priority}] ${m.title}`).join("\n") : "No missions found."; } else if (lower.includes("squad") || lower.includes("agent")) { const result = (await client.request("kaneru.dashboard", {})) as Record; const data = result.stats as Record | undefined; state.result = `${data?.activeSquads ?? 0} active squads, Q-table: ${data?.qTableSize ?? 0} entries, \u03B5=${(Number(data?.epsilon ?? 0) * 100).toFixed(1)}%`; } else if (lower.includes("decision")) { - state.result = - "Use 'mayros kaneru decisions list' for full decision history."; + state.result = "Use 'mayros kaneru decisions list' for full decision history."; } else { state.result = `Command received: "${query}"\nUse the CLI for full capabilities: mayros kaneru --help`; } diff --git a/ui/src/ui/controllers/kaneru.ts b/ui/src/ui/controllers/kaneru.ts index c039967b..bbbad844 100644 --- a/ui/src/ui/controllers/kaneru.ts +++ b/ui/src/ui/controllers/kaneru.ts @@ -67,7 +67,10 @@ export async function loadKaneruDashboard(state: KaneruDashboardState): Promise< state.kaneruLoading = true; state.kaneruError = null; try { - const response = await state.client.request("kaneru.dashboard", {}) as KaneruDashboardResponse; + const response = (await state.client.request( + "kaneru.dashboard", + {}, + )) as KaneruDashboardResponse; state.kaneruDashboard = response; // Populate squad builder agents from response if (response.availableAgents) { diff --git a/ui/src/ui/controllers/onboarding.ts b/ui/src/ui/controllers/onboarding.ts index 37c6969b..bb2e9ec7 100644 --- a/ui/src/ui/controllers/onboarding.ts +++ b/ui/src/ui/controllers/onboarding.ts @@ -1,5 +1,9 @@ import type { GatewayBrowserClient } from "../gateway.ts"; -import type { OnboardingState, OnboardingStep, OnboardingProvider } from "../views/onboarding-wizard.ts"; +import type { + OnboardingState, + OnboardingStep, + OnboardingProvider, +} from "../views/onboarding-wizard.ts"; // ============================================================================ // Initial state @@ -14,6 +18,8 @@ export function createInitialOnboardingState(): OnboardingState { selectedActivity: "all", localModel: "llama3.1:8b", ollamaDetected: false, + ollamaInstalledModels: [], + agentName: "Atlas", detectedVramMB: 4096, detectedGpuName: "", saving: false, @@ -71,6 +77,7 @@ export async function saveOnboardingConfig( provider: state.provider, apiKey: state.provider !== "local" ? state.apiKey : "", model, + agentName: state.agentName.trim() || "Atlas", }); } catch (err) { state.error = `Failed to save configuration: ${err instanceof Error ? err.message : String(err)}`; @@ -116,36 +123,45 @@ export async function detectOllama( state: OnboardingState, client?: GatewayBrowserClient | null, ): Promise { - // Try gateway-side detection first (avoids CORS issues) - if (client) { - try { - const result = (await client.request("mamoru.status", {})) as Record; - // If mamoru.status works, check runtimes - state.ollamaDetected = true; // gateway can reach Ollama server-side - return; - } catch { - // Gateway doesn't have mamoru — fall through + // Always use gateway proxy (direct browser fetch blocked by CSP) + if (!client) { + state.ollamaDetected = false; + return; + } + try { + const result = (await client.request("onboarding.detectOllama", {})) as { + detected: boolean; + models?: string[]; + }; + state.ollamaDetected = result.detected; + if (result.models) { + state.ollamaInstalledModels = result.models; } + } catch { + state.ollamaDetected = false; } +} - // Fallback: direct fetch (may fail due to CORS in browser) +/** + * Fetch the list of models already downloaded in Ollama. + * Always uses the gateway proxy (direct browser fetch is blocked by CSP). + */ +export async function fetchOllamaModels( + state: OnboardingState, + client?: GatewayBrowserClient | null, +): Promise { + if (!client) return; try { - const res = await fetch("http://127.0.0.1:11434/api/tags", { - signal: AbortSignal.timeout(3000), - }); - state.ollamaDetected = res.ok; - } catch { - // CORS or connection refused — try via gateway proxy - if (client) { - try { - const result = (await client.request("onboarding.detectOllama", {})) as { detected: boolean }; - state.ollamaDetected = result.detected; - return; - } catch { - // no gateway support - } + const result = (await client.request("onboarding.detectOllama", {})) as { + detected: boolean; + models?: string[]; + }; + state.ollamaDetected = result.detected; + if (result.models) { + state.ollamaInstalledModels = result.models; } - state.ollamaDetected = false; + } catch { + // gateway doesn't support model listing yet } } diff --git a/ui/src/ui/views/canvas-embed.ts b/ui/src/ui/views/canvas-embed.ts index 2f309df7..331f223d 100644 --- a/ui/src/ui/views/canvas-embed.ts +++ b/ui/src/ui/views/canvas-embed.ts @@ -130,7 +130,9 @@ function renderComponent( `; } case "Divider": - return html`
`; + return html` +
+ `; default: return nothing; } @@ -254,9 +256,8 @@ export function renderCanvasEmbed(props: CanvasEmbedProps) { const surfaces = parseJsonl(jsonl); // Filter surfaces based on active tab - const visibleSurfaces = activeSurface === "all" - ? surfaces - : surfaces.filter((s) => s.surfaceId === activeSurface); + const visibleSurfaces = + activeSurface === "all" ? surfaces : surfaces.filter((s) => s.surfaceId === activeSurface); const renderedSurfaces = visibleSurfaces.map((surface) => { if (!surface.root || !surface.components.has(surface.root)) { @@ -272,7 +273,13 @@ export function renderCanvasEmbed(props: CanvasEmbedProps) { return html`
${tabBar} - ${renderedSurfaces.length > 0 ? renderedSurfaces : html`

No surfaces found for this view.

`} + ${ + renderedSurfaces.length > 0 + ? renderedSurfaces + : html` +

No surfaces found for this view.

+ ` + }
`; } diff --git a/ui/src/ui/views/chain-visualizer.ts b/ui/src/ui/views/chain-visualizer.ts index 2137e494..e9773c7e 100644 --- a/ui/src/ui/views/chain-visualizer.ts +++ b/ui/src/ui/views/chain-visualizer.ts @@ -1,4 +1,4 @@ -import { html, nothing } from "lit"; +import { html, nothing, type TemplateResult } from "lit"; // ============================================================================ // Types @@ -25,9 +25,7 @@ function isActive(node: ChainNodeData): boolean { } function nodeStyle(node: ChainNodeData): string { - const borderColor = isActive(node) - ? "var(--color-ok, #25c281)" - : "var(--border-color, #444)"; + const borderColor = isActive(node) ? "var(--color-ok, #25c281)" : "var(--border-color, #444)"; return ` position: relative; background: var(--bg-secondary); @@ -49,24 +47,28 @@ const TREE_BRANCH_STYLE = ` // Render functions // ============================================================================ -function renderChainNode(node: ChainNodeData, isRoot: boolean) { - const wrapperStyle = isRoot - ? "margin: 8px 0;" - : TREE_BRANCH_STYLE; +function renderChainNode(node: ChainNodeData, isRoot: boolean): TemplateResult { + const wrapperStyle = isRoot ? "margin: 8px 0;" : TREE_BRANCH_STYLE; return html`
- ${isRoot - ? nothing - : html`
`} + ${ + isRoot + ? nothing + : html` +
+ ` + }
${node.agentId} @@ -74,26 +76,30 @@ function renderChainNode(node: ChainNodeData, isRoot: boolean) {
${node.role}
- ${node.escalatesTo - ? html`
+ ${ + node.escalatesTo + ? html`
Escalates to: ${node.escalatesTo}
` - : nothing} + : nothing + }
- ${node.children.length > 0 - ? html` + ${ + node.children.length > 0 + ? html`
${node.children.map((child) => renderChainNode(child, false))}
` - : nothing} + : nothing + }
`; } function renderEmptyState() { return html` -
+
No escalation chain configured. Agents operate independently.
`; @@ -108,19 +114,23 @@ export function renderChainVisualizer(props: ChainVisualizerProps) {

Escalation Chain - ${props.ventureId - ? html` + ${ + props.ventureId + ? html` (${props.ventureId}) ` - : nothing} + : nothing + }

- ${props.chain.length === 0 - ? renderEmptyState() - : html` + ${ + props.chain.length === 0 + ? renderEmptyState() + : html`
${props.chain.map((root) => renderChainNode(root, true))}
- `} + ` + }
`; } diff --git a/ui/src/ui/views/chat.ts b/ui/src/ui/views/chat.ts index d59402f6..729e6547 100644 --- a/ui/src/ui/views/chat.ts +++ b/ui/src/ui/views/chat.ts @@ -451,7 +451,9 @@ export function renderChat(props: ChatProps) { if (e.shiftKey) return; if (!props.connected) return; e.preventDefault(); - if (canCompose) { props.onSend(); } + if (canCompose) { + props.onSend(); + } }} @input=${(e: Event) => { const target = e.target as HTMLTextAreaElement; @@ -483,16 +485,20 @@ export function renderChat(props: ChatProps) {
- ${props.onToggleVoice ? html` + ${ + props.onToggleVoice + ? html` - ` : nothing} + ` + : nothing + } - ` : nothing} + ` + : nothing + }
- ${state.processing - ? html`
+ ${ + state.processing + ? html`
Processing...
` - : nothing + : nothing } - ${state.error - ? html`
${state.error}
` - : nothing - } + ${state.error ? html`
${state.error}
` : nothing} - ${state.result - ? html`
${state.result}
` - : nothing - } + ${state.result ? html`
${state.result}
` : nothing} - ${!state.processing && !state.result && !state.error - ? html` + ${ + !state.processing && !state.result && !state.error + ? html` - ${state.ventureContext - ? html`
+ ${ + state.ventureContext + ? html`
${state.ventureContext.name} / ${state.ventureContext.activeMissions} active mission${state.ventureContext.activeMissions !== 1 ? "s" : ""} @@ -310,12 +310,13 @@ export function renderCommandBar(props: CommandBarProps) { $${(state.ventureContext.fuelSpent / 100).toFixed(2)} spent
` - : nothing + : nothing } - ${filteredActions.length > 0 - ? html` + ${ + filteredActions.length > 0 + ? html`
${filteredActions.map( (action) => html` @@ -324,7 +325,8 @@ export function renderCommandBar(props: CommandBarProps) { class="command-bar-action" @click=${() => onQuickAction(action.id)} @mouseenter=${(e: MouseEvent) => { - (e.currentTarget as HTMLElement).style.background = "var(--bg-hover, #262a35)"; + (e.currentTarget as HTMLElement).style.background = + "var(--bg-hover, #262a35)"; }} @mouseleave=${(e: MouseEvent) => { (e.currentTarget as HTMLElement).style.background = "transparent"; @@ -336,12 +338,13 @@ export function renderCommandBar(props: CommandBarProps) { `, )}
` - : nothing + : nothing } - ${state.activeMissions.length > 0 - ? html` + ${ + state.activeMissions.length > 0 + ? html`
@@ -357,10 +360,10 @@ export function renderCommandBar(props: CommandBarProps) { `, )}
` - : nothing + : nothing } ` - : nothing + : nothing } diff --git a/ui/src/ui/views/mission-detail.ts b/ui/src/ui/views/mission-detail.ts index 454234f3..7b39d712 100644 --- a/ui/src/ui/views/mission-detail.ts +++ b/ui/src/ui/views/mission-detail.ts @@ -119,19 +119,26 @@ function renderDetails(props: MissionDetailProps) {
Claimed by
- ${props.mission.claimedBy - ? html`${props.mission.claimedBy}` - : html`Unclaimed`} + ${ + props.mission.claimedBy + ? html`${props.mission.claimedBy}` + : html` + Unclaimed + ` + }
- ${props.mission.ventureId - ? html` + ${ + props.mission.ventureId + ? html`
Venture
${props.mission.ventureId}
` - : nothing} + : nothing + }
- ${props.mission.description - ? html` + ${ + props.mission.description + ? html`
Description
${props.mission.description}
` - : nothing} + : nothing + } `; } @@ -154,10 +162,13 @@ function renderComments(props: MissionDetailProps) {
Comments (${props.comments.length})
- ${props.comments.length === 0 - ? html`
No comments yet.
` - : props.comments.map( - (c) => html` + ${ + props.comments.length === 0 + ? html` +
No comments yet.
+ ` + : props.comments.map( + (c) => html`
${c.content}
`, - )} + ) + }
`; } @@ -196,7 +208,9 @@ function renderCommentInput(props: MissionDetailProps) { { inputValue = (e.target as HTMLInputElement).value; }} + @input=${(e: Event) => { + inputValue = (e.target as HTMLInputElement).value; + }} @keydown=${(e: KeyboardEvent) => { if (e.key === "Enter") { handleSubmit(); diff --git a/ui/src/ui/views/onboarding-wizard.ts b/ui/src/ui/views/onboarding-wizard.ts index 70f75482..0f3cbf29 100644 --- a/ui/src/ui/views/onboarding-wizard.ts +++ b/ui/src/ui/views/onboarding-wizard.ts @@ -16,8 +16,10 @@ export type OnboardingState = { selectedActivity: string; localModel: string; ollamaDetected: boolean; + ollamaInstalledModels: string[]; detectedVramMB: number; detectedGpuName: string; + agentName: string; saving: boolean; error: string | null; gatewayOk: boolean; @@ -30,6 +32,8 @@ export type OnboardingWizardProps = { onApiKeyChange: (key: string) => void; onLocalModelChange: (model: string) => void; onActivityChange?: (activity: string) => void; + onRefreshOllamaModels?: () => void; + onAgentNameChange: (name: string) => void; onNext: () => void; onBack: () => void; onComplete: () => void; @@ -134,77 +138,331 @@ const PROVIDERS: ProviderInfo[] = [ const ACTIVITIES = [ { value: "all", label: "All Models" }, - { value: "coding", label: "Coding" }, { value: "chat", label: "Chat / General" }, + { value: "coding", label: "Coding" }, + { value: "agents", label: "Agents / Tool Use" }, { value: "reasoning", label: "Reasoning / Math" }, - { value: "creative", label: "Creative Writing" }, - { value: "analysis", label: "Analysis / Research" }, { value: "multilingual", label: "Multilingual" }, - { value: "vision", label: "Vision / Multimodal" }, - { value: "agents", label: "Agents / Tool Use" }, + { value: "analysis", label: "Analysis / RAG" }, ]; +// All models in this catalog MUST support tool/function calling in Ollama. +// Models without tool support (gemma, phi, codellama, starcoder, llava, etc.) are excluded +// because Mayros requires tool-use for agent functionality. const FULL_CATALOG = [ - // ── Small models (0-4GB VRAM) — run on almost any machine ────── - { id: "qwen2.5-coder:1.5b", name: "Qwen 2.5 Coder 1.5B", activity: "coding", provider: "Qwen", vram: 0, params: "1.5B", desc: "Tiny but capable code model, runs on CPU" }, - { id: "deepseek-coder-v2:lite", name: "DeepSeek Coder V2 Lite", activity: "coding", provider: "DeepSeek", vram: 3000, params: "2.5B", desc: "Lightweight MoE coder, great for autocomplete" }, - { id: "phi-3.5:3.8b", name: "Phi 3.5 Mini 3.8B", activity: "chat", provider: "Microsoft", vram: 3000, params: "3.8B", desc: "Microsoft's small powerhouse, strong reasoning" }, - { id: "gemma2:2b", name: "Gemma 2 2B", activity: "chat", provider: "Google", vram: 0, params: "2B", desc: "Google's tiny model, CPU friendly" }, - { id: "tinyllama:1.1b", name: "TinyLlama 1.1B", activity: "chat", provider: "Meta", vram: 0, params: "1.1B", desc: "Ultra-light, instant responses on any hardware" }, - { id: "deepseek-r1:1.5b", name: "DeepSeek R1 1.5B", activity: "reasoning", provider: "DeepSeek", vram: 0, params: "1.5B", desc: "Chain-of-thought reasoning on CPU" }, - { id: "qwen2.5:3b", name: "Qwen 2.5 3B", activity: "multilingual", provider: "Qwen", vram: 0, params: "3B", desc: "Multilingual on CPU, CJK + Latin" }, - { id: "moondream:1.8b", name: "Moondream 1.8B", activity: "vision", provider: "Meta", vram: 2000, params: "1.8B", desc: "Tiny vision model, image understanding on CPU" }, - { id: "smollm2:1.7b", name: "SmolLM2 1.7B", activity: "agents", provider: "Microsoft", vram: 0, params: "1.7B", desc: "Small but capable for simple agent tasks" }, - { id: "qwen2.5:0.5b", name: "Qwen 2.5 0.5B", activity: "chat", provider: "Qwen", vram: 0, params: "0.5B", desc: "Smallest Qwen, ultra-fast, edge devices" }, - { id: "nomic-embed-text", name: "Nomic Embed Text", activity: "analysis", provider: "Nomic", vram: 0, params: "137M", desc: "Text embeddings for RAG and search, CPU only" }, - { id: "all-minilm:l6-v2", name: "MiniLM L6 v2", activity: "analysis", provider: "Microsoft", vram: 0, params: "22M", desc: "Fast sentence embeddings, perfect for search" }, - - // ── Coding (5GB+) ───────────────────────────────────────────────── - { id: "codellama:7b", name: "Code Llama 7B", activity: "coding", provider: "Meta", vram: 5000, params: "7B", desc: "Fast code completion, low VRAM" }, - { id: "codellama:13b", name: "Code Llama 13B", activity: "coding", provider: "Meta", vram: 10000, params: "13B", desc: "Strong code generation" }, - { id: "codellama:34b", name: "Code Llama 34B", activity: "coding", provider: "Meta", vram: 22000, params: "34B", desc: "Best Code Llama for complex tasks" }, - { id: "deepseek-coder-v2:lite", name: "DeepSeek Coder V2 Lite", activity: "coding", provider: "DeepSeek", vram: 12000, params: "16B", desc: "128K context, MoE architecture" }, - { id: "deepseek-coder-v2", name: "DeepSeek Coder V2", activity: "coding", provider: "DeepSeek", vram: 22000, params: "33B", desc: "Top-tier code model" }, - { id: "qwen2.5-coder:7b", name: "Qwen 2.5 Coder 7B", activity: "coding", provider: "Qwen", vram: 5000, params: "7B", desc: "Competitive with larger models" }, - { id: "qwen2.5-coder:14b", name: "Qwen 2.5 Coder 14B", activity: "coding", provider: "Qwen", vram: 10000, params: "14B", desc: "Code + tool-use" }, - { id: "qwen2.5-coder:32b", name: "Qwen 2.5 Coder 32B", activity: "coding", provider: "Qwen", vram: 22000, params: "32B", desc: "Rivals GPT-4 on code" }, - { id: "starcoder2:7b", name: "StarCoder2 7B", activity: "coding", provider: "Microsoft", vram: 5000, params: "7B", desc: "Fast completions" }, - { id: "starcoder2:15b", name: "StarCoder2 15B", activity: "coding", provider: "Microsoft", vram: 11000, params: "15B", desc: "Improved accuracy" }, - // Chat - { id: "llama3.2:3b", name: "Llama 3.2 3B", activity: "chat", provider: "Meta", vram: 0, params: "3B", desc: "Runs on CPU, fast" }, - { id: "llama3.1:8b", name: "Llama 3.1 8B", activity: "chat", provider: "Meta", vram: 6000, params: "8B", desc: "Excellent general model" }, - { id: "llama3.3:70b", name: "Llama 3.3 70B", activity: "chat", provider: "Meta", vram: 40000, params: "70B", desc: "Near-frontier quality" }, - { id: "mistral:7b", name: "Mistral 7B", activity: "chat", provider: "Mistral", vram: 6000, params: "7B", desc: "Fast and efficient" }, - { id: "mistral-nemo:12b", name: "Mistral Nemo 12B", activity: "chat", provider: "Mistral", vram: 9000, params: "12B", desc: "128K context, multilingual" }, - { id: "mixtral:8x7b", name: "Mixtral 8x7B", activity: "chat", provider: "Mistral", vram: 28000, params: "47B", desc: "MoE, fast for quality" }, - { id: "phi-4:14b", name: "Phi-4 14B", activity: "chat", provider: "Microsoft", vram: 10000, params: "14B", desc: "Strong reasoning" }, - { id: "gemma2:9b", name: "Gemma 2 9B", activity: "chat", provider: "Google", vram: 7000, params: "9B", desc: "Efficient, strong benchmarks" }, - { id: "gemma2:27b", name: "Gemma 2 27B", activity: "chat", provider: "Google", vram: 18000, params: "27B", desc: "Best Gemma" }, - // Reasoning - { id: "deepseek-r1:7b", name: "DeepSeek R1 7B", activity: "reasoning", provider: "DeepSeek", vram: 5000, params: "7B", desc: "Chain-of-thought at small scale" }, - { id: "deepseek-r1:14b", name: "DeepSeek R1 14B", activity: "reasoning", provider: "DeepSeek", vram: 10000, params: "14B", desc: "Strong reasoning + code" }, - { id: "deepseek-r1:70b", name: "DeepSeek R1 70B", activity: "reasoning", provider: "DeepSeek", vram: 40000, params: "70B", desc: "Rivals o1 on math" }, - { id: "qwen2.5:72b", name: "Qwen 2.5 72B", activity: "reasoning", provider: "Qwen", vram: 42000, params: "72B", desc: "Top-tier reasoning" }, - // Creative - { id: "yi:34b", name: "Yi 34B", activity: "creative", provider: "01.AI", vram: 22000, params: "34B", desc: "Creative writing, bilingual" }, - // Multilingual - { id: "qwen2.5:7b", name: "Qwen 2.5 7B", activity: "multilingual", provider: "Qwen", vram: 5000, params: "7B", desc: "Strong CJK languages" }, - { id: "qwen2.5:14b", name: "Qwen 2.5 14B", activity: "multilingual", provider: "Qwen", vram: 10000, params: "14B", desc: "Best mid-size multilingual" }, - { id: "aya:8b", name: "Aya 8B", activity: "multilingual", provider: "Cohere", vram: 6000, params: "8B", desc: "23+ languages" }, - { id: "aya:35b", name: "Aya 35B", activity: "multilingual", provider: "Cohere", vram: 24000, params: "35B", desc: "Best coverage" }, - // Vision - { id: "llava:7b", name: "LLaVA 7B", activity: "vision", provider: "Meta", vram: 6000, params: "7B", desc: "Visual QA" }, - { id: "llava:13b", name: "LLaVA 13B", activity: "vision", provider: "Meta", vram: 10000, params: "13B", desc: "Better image reasoning" }, - { id: "llama3.2-vision:11b", name: "Llama 3.2 Vision", activity: "vision", provider: "Meta", vram: 8000, params: "11B", desc: "Native multimodal, 128K ctx" }, - // Agents - { id: "granite3-dense:8b", name: "Granite 3 Dense 8B", activity: "agents", provider: "IBM", vram: 6000, params: "8B", desc: "Strong tool-use" }, - { id: "granite3-moe:3b", name: "Granite 3 MoE 3B", activity: "agents", provider: "IBM", vram: 3000, params: "3B", desc: "Lightweight agents" }, - // Analysis - { id: "solar:10.7b", name: "Solar 10.7B", activity: "analysis", provider: "Upstage", vram: 8000, params: "10.7B", desc: "Strong summarization" }, - { id: "command-r:35b", name: "Command R 35B", activity: "analysis", provider: "Cohere", vram: 24000, params: "35B", desc: "RAG-optimized, citations" }, - // NVIDIA NIM - { id: "nvidia/nemotron-mini:4b", name: "Nemotron Mini 4B", activity: "chat", provider: "NVIDIA", vram: 4000, params: "4B", desc: "NIM optimized, low-latency" }, - { id: "nvidia/nemotron-nano:8b", name: "Nemotron Nano 8B", activity: "coding", provider: "NVIDIA", vram: 8000, params: "8B", desc: "TensorRT-LLM, fast" }, + // ── Tiny models (0-4GB VRAM) — CPU-friendly ──────────────────── + { + id: "qwen3:0.6b", + name: "Qwen 3 0.6B", + activity: "chat", + provider: "Qwen", + vram: 0, + params: "0.6B", + desc: "Smallest with tools, ultra-fast, edge", + }, + { + id: "qwen3:1.7b", + name: "Qwen 3 1.7B", + activity: "chat", + provider: "Qwen", + vram: 0, + params: "1.7B", + desc: "Thinking + fast modes, CPU", + }, + { + id: "qwen2.5-coder:1.5b", + name: "Qwen 2.5 Coder 1.5B", + activity: "coding", + provider: "Qwen", + vram: 0, + params: "1.5B", + desc: "Tiny code model with tools, CPU", + }, + { + id: "smollm2:1.7b", + name: "SmolLM2 1.7B", + activity: "agents", + provider: "HuggingFace", + vram: 0, + params: "1.7B", + desc: "Small agent model with tool-use", + }, + { + id: "llama3.2:3b", + name: "Llama 3.2 3B", + activity: "chat", + provider: "Meta", + vram: 0, + params: "3B", + desc: "Fast, CPU friendly, tool-use", + }, + { + id: "qwen2.5:3b", + name: "Qwen 2.5 3B", + activity: "multilingual", + provider: "Qwen", + vram: 0, + params: "3B", + desc: "Multilingual + tools, CJK + Latin", + }, + { + id: "granite3-moe:3b", + name: "Granite 3 MoE 3B", + activity: "agents", + provider: "IBM", + vram: 3000, + params: "3B", + desc: "MoE with native function calling", + }, + { + id: "deepseek-r1:1.5b", + name: "DeepSeek R1 1.5B", + activity: "reasoning", + provider: "DeepSeek", + vram: 0, + params: "1.5B", + desc: "Chain-of-thought on CPU", + }, + + // ── Mid models (4-8GB VRAM) ──────────────────────────────────── + { + id: "qwen3:4b", + name: "Qwen 3 4B", + activity: "chat", + provider: "Qwen", + vram: 4000, + params: "4B", + desc: "Best small model — thinking + tools", + }, + { + id: "nemotron-mini:4b", + name: "Nemotron Mini 4B", + activity: "agents", + provider: "NVIDIA", + vram: 4000, + params: "4B", + desc: "NVIDIA optimized for function calling", + }, + { + id: "nemotron-3-nano:4b", + name: "Nemotron 3 Nano 4B", + activity: "agents", + provider: "NVIDIA", + vram: 4000, + params: "4B", + desc: "NVIDIA MoE, thinking + tools", + }, + { + id: "qwen2.5-coder:7b", + name: "Qwen 2.5 Coder 7B", + activity: "coding", + provider: "Qwen", + vram: 5000, + params: "7B", + desc: "Coding + tool-use, competitive", + }, + { + id: "command-r7b", + name: "Command R 7B", + activity: "chat", + provider: "Cohere", + vram: 5000, + params: "7B", + desc: "Efficient, RAG + tool-use", + }, + { + id: "qwen3:8b", + name: "Qwen 3 8B", + activity: "chat", + provider: "Qwen", + vram: 6000, + params: "8B", + desc: "Best mid-size, dual-mode + tools", + }, + { + id: "llama3.1:8b", + name: "Llama 3.1 8B", + activity: "chat", + provider: "Meta", + vram: 6000, + params: "8B", + desc: "Proven general model, 128K ctx", + }, + { + id: "mistral:7b", + name: "Mistral 7B", + activity: "chat", + provider: "Mistral", + vram: 6000, + params: "7B", + desc: "Fast, native function calling", + }, + { + id: "granite3-dense:8b", + name: "Granite 3 Dense 8B", + activity: "agents", + provider: "IBM", + vram: 6000, + params: "8B", + desc: "Strong function calling", + }, + { + id: "deepseek-r1:7b", + name: "DeepSeek R1 7B", + activity: "reasoning", + provider: "DeepSeek", + vram: 5000, + params: "7B", + desc: "Reasoning + tools", + }, + { + id: "qwen2.5:7b", + name: "Qwen 2.5 7B", + activity: "multilingual", + provider: "Qwen", + vram: 5000, + params: "7B", + desc: "Multilingual + tools", + }, + + // ── Large models (8-24GB VRAM) ───────────────────────────────── + { + id: "qwen3:14b", + name: "Qwen 3 14B", + activity: "chat", + provider: "Qwen", + vram: 10000, + params: "14B", + desc: "Strong reasoning + tools", + }, + { + id: "qwen2.5-coder:14b", + name: "Qwen 2.5 Coder 14B", + activity: "coding", + provider: "Qwen", + vram: 10000, + params: "14B", + desc: "Code + tool-use", + }, + { + id: "mistral-nemo:12b", + name: "Mistral Nemo 12B", + activity: "chat", + provider: "Mistral", + vram: 9000, + params: "12B", + desc: "128K context, native tools", + }, + { + id: "deepseek-r1:14b", + name: "DeepSeek R1 14B", + activity: "reasoning", + provider: "DeepSeek", + vram: 10000, + params: "14B", + desc: "Strong reasoning + code", + }, + { + id: "qwen2.5:14b", + name: "Qwen 2.5 14B", + activity: "multilingual", + provider: "Qwen", + vram: 10000, + params: "14B", + desc: "Best mid-size multilingual", + }, + { + id: "mistral-small", + name: "Mistral Small 22B", + activity: "chat", + provider: "Mistral", + vram: 15000, + params: "22B", + desc: "128K context, strong tools", + }, + { + id: "devstral", + name: "Devstral 24B", + activity: "coding", + provider: "Mistral", + vram: 16000, + params: "24B", + desc: "Mistral's coding model, 128K ctx", + }, + { + id: "qwen2.5-coder:32b", + name: "Qwen 2.5 Coder 32B", + activity: "coding", + provider: "Qwen", + vram: 22000, + params: "32B", + desc: "Rivals GPT-4 on code", + }, + { + id: "qwen3:32b", + name: "Qwen 3 32B", + activity: "chat", + provider: "Qwen", + vram: 22000, + params: "32B", + desc: "Near-frontier, thinking + tools", + }, + { + id: "qwq", + name: "QwQ 32B", + activity: "reasoning", + provider: "Qwen", + vram: 22000, + params: "32B", + desc: "Reasoning specialist + tools", + }, + { + id: "command-r:35b", + name: "Command R 35B", + activity: "analysis", + provider: "Cohere", + vram: 24000, + params: "35B", + desc: "RAG-optimized, citations + tools", + }, + + // ── Extra-large (40GB+) ──────────────────────────────────────── + { + id: "mixtral:8x7b", + name: "Mixtral 8x7B", + activity: "chat", + provider: "Mistral", + vram: 28000, + params: "47B", + desc: "MoE, native function calling", + }, + { + id: "deepseek-r1:70b", + name: "DeepSeek R1 70B", + activity: "reasoning", + provider: "DeepSeek", + vram: 40000, + params: "70B", + desc: "Rivals o1 on math", + }, + { + id: "llama3.3:70b", + name: "Llama 3.3 70B", + activity: "chat", + provider: "Meta", + vram: 40000, + params: "70B", + desc: "Near-frontier, tool-use", + }, + { + id: "qwen2.5:72b", + name: "Qwen 2.5 72B", + activity: "reasoning", + provider: "Qwen", + vram: 42000, + params: "72B", + desc: "Top-tier reasoning + tools", + }, ]; // ============================================================================ @@ -359,13 +617,15 @@ function renderApiKeyStep(props: OnboardingWizardProps) { >Get it at ${provider?.name ?? "provider"}

- ${s.error - ? html`
${s.error}
` - : nothing} + : nothing + }
@@ -405,8 +665,9 @@ function renderLocalModelStep(props: OnboardingWizardProps) {
- ${s.ollamaDetected - ? html` + ${ + s.ollamaDetected + ? html`
@@ -428,29 +689,35 @@ function renderLocalModelStep(props: OnboardingWizardProps) {
- ${s.detectedGpuName ? html` + ${ + s.detectedGpuName + ? html`
Your GPU: ${s.detectedGpuName} (${(s.detectedVramMB / 1000).toFixed(0)}GB VRAM) ${s.detectedVramMB === 0 ? " — CPU only, limited models available" : ""}
- ` : nothing} + ` + : nothing + } -
- ${FULL_CATALOG - .filter((m) => s.selectedActivity === "all" || m.activity === s.selectedActivity) +
+ ${FULL_CATALOG.filter( + (m) => s.selectedActivity === "all" || m.activity === s.selectedActivity, + ) .sort((a, b) => { - // Compatible models first, then by VRAM desc const aOk = a.vram <= s.detectedVramMB ? 1 : 0; const bOk = b.vram <= s.detectedVramMB ? 1 : 0; if (aOk !== bOk) return bOk - aOk; return b.vram - a.vram; }) - .map( - (m) => { - const canRun = m.vram <= s.detectedVramMB; - const isSelected = s.localModel === m.id; - return html` + .map((m) => { + const canRun = m.vram <= s.detectedVramMB; + const isSelected = s.localModel === m.id; + const installed = s.ollamaInstalledModels.some( + (n) => n === m.id || n.startsWith(m.id + ":") || m.id.startsWith(n + ":"), + ); + return html`
- ${canRun ? "" : "🔒 "}${m.name} + ${canRun ? "" : ""}${m.name} ${m.params} | ${m.provider} + ${ + installed + ? html` + INSTALLED + ` + : nothing + }
${m.desc}
${m.vram > 0 ? `${(m.vram / 1000).toFixed(0)}GB` : "CPU"} - ${canRun ? " ✓" : " ✗"} + ${canRun ? " OK" : " N/A"}
`; - }, - )} + })}
+ + + ${ + s.localModel && + !s.ollamaInstalledModels.some( + (n) => + n === s.localModel || + n.startsWith(s.localModel + ":") || + s.localModel.startsWith(n + ":"), + ) + ? html` +
+
+ + + Almost there! Download this model first + +
+
+ Open a terminal and paste this command: +
+
+ ollama pull ${s.localModel} +
+
+ + ~${ + FULL_CATALOG.find((m) => m.id === s.localModel)?.vram + ? ( + ((FULL_CATALOG.find((m) => m.id === s.localModel)?.vram ?? 0) / 1000) * + 0.6 + ).toFixed(1) + : "?" + }GB download + + +
+
+ ` + : nothing + } ` - : html` -
-

- Install Ollama to run models locally: -

- Download Ollama + : html` + +
+ +
+ Mayrito +
+
+ I need Ollama to run local models. It takes less than a minute to set up! +
+ + +
+
+
1
+
+
Download Ollama
+
Free, lightweight, runs in the background
+
+ Get it +
+ +
+
2
+
+
Install & open it
+
Drag to Applications, then open once
+
+
+ +
+
3
+
+
Come back & refresh
+
I'll detect it automatically
+
+ +
+
- `} + ` + } - ${s.error - ? html`
${s.error}
` - : nothing} + : nothing + }
@@ -537,7 +963,7 @@ function renderReadyStep(props: OnboardingWizardProps) { const modelLabel = s.provider === "local" - ? FULL_CATALOG.find((m) => m.id === s.localModel)?.name ?? s.localModel + ? (FULL_CATALOG.find((m) => m.id === s.localModel)?.name ?? s.localModel) : `${provider?.subtitle ?? ""}`; return html` @@ -557,6 +983,21 @@ function renderReadyStep(props: OnboardingWizardProps) {

+ +
+ + props.onAgentNameChange((e.target as HTMLInputElement).value)} + /> +
+ You can change this later in IDENTITY.md +
+
+
${s.error}
` - : nothing} + : nothing + }
- ${agent.expertise - ? html`
+ ${ + agent.expertise + ? html`
${agent.expertise}
` - : nothing} + : nothing + }
`; } @@ -97,7 +95,7 @@ function renderAgentCard( function renderAgentGrid(props: SquadBuilderProps) { if (props.availableAgents.length === 0) { return html` -
+
No agents available. Create agents first to build a squad.
`; @@ -111,11 +109,7 @@ function renderAgentGrid(props: SquadBuilderProps) { margin-top: 12px; "> ${props.availableAgents.map((agent) => - renderAgentCard( - agent, - props.selectedAgents.includes(agent.agentId), - props.onToggleAgent, - ), + renderAgentCard(agent, props.selectedAgents.includes(agent.agentId), props.onToggleAgent), )}
`; @@ -164,7 +158,8 @@ function renderConfigSection(props: SquadBuilderProps) { " > ${STRATEGIES.map( - (s) => html``, + (s) => + html``, )}
@@ -174,9 +169,7 @@ function renderConfigSection(props: SquadBuilderProps) { function renderFooter(props: SquadBuilderProps) { const canCreate = - props.selectedAgents.length > 0 && - props.squadName.trim().length > 0 && - !props.creating; + props.selectedAgents.length > 0 && props.squadName.trim().length > 0 && !props.creating; return html`
diff --git a/ui/src/ui/views/ventures.ts b/ui/src/ui/views/ventures.ts index 259956f5..8f7c8314 100644 --- a/ui/src/ui/views/ventures.ts +++ b/ui/src/ui/views/ventures.ts @@ -112,11 +112,11 @@ function renderVenturesTable(ventures: VentureSummary[]) { - ${ventures.map( - (v) => { - const pct = fuelPercent(v.fuelSpent, v.fuelLimit); - const barColor = pct >= 90 ? "var(--color-warn, #e2a03f)" : "var(--color-ok, #25c281)"; - return html` + ${ventures.map((v) => { + const pct = fuelPercent(v.fuelSpent, v.fuelLimit); + const barColor = + pct >= 90 ? "var(--color-warn, #e2a03f)" : "var(--color-ok, #25c281)"; + return html` ${v.id} ${v.name} @@ -140,8 +140,7 @@ function renderVenturesTable(ventures: VentureSummary[]) { ${v.missionCount} `; - }, - )} + })}
@@ -158,10 +157,13 @@ function renderMissionColumn(status: string, missions: MissionSummary[]) { (${missions.length})
- ${missions.length === 0 - ? html`
No missions
` - : missions.map( - (m) => html` + ${ + missions.length === 0 + ? html` +
No missions
+ ` + : missions.map( + (m) => html`
${m.title}
- ${m.claimedBy - ? html`
Agent: ${m.claimedBy}
` - : html`
Unclaimed
`} + ${ + m.claimedBy + ? html`
Agent: ${m.claimedBy}
` + : html` +
Unclaimed
+ ` + }
`, - )} + ) + }
`; } @@ -261,13 +268,15 @@ export function renderVenturesDashboard(props: VentureDashboardProps) {
Ventures: ${d.ventures.length} | Missions: ${d.missions.length}
- ${props.onNewVenture - ? html`` - : nothing} + : nothing + } @@ -279,12 +288,14 @@ export function renderVenturesDashboard(props: VentureDashboardProps) { ${renderMissionBoard(d.missions)} - ${d.chain && d.chain.length > 0 - ? renderChainVisualizer({ - chain: d.chain, - ventureId: d.ventures.length > 0 ? d.ventures[0].id : "", - }) - : nothing} + ${ + d.chain && d.chain.length > 0 + ? renderChainVisualizer({ + chain: d.chain, + ventureId: d.ventures.length > 0 ? d.ventures[0].id : "", + }) + : nothing + } `; }