diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a856ab70..8a769815 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -106,6 +106,8 @@ jobs: - name: Checkout uses: actions/checkout@v4 with: + # Full history so the lockfile policy check can read commit messages. + fetch-depth: 0 submodules: false - name: Setup Node environment @@ -114,6 +116,10 @@ jobs: - name: Check types, lint, and format run: pnpm check + - name: Enforce lockfile change policy + if: github.event_name == 'pull_request' + run: node scripts/check-lockfile-policy.mjs --base "origin/${{ github.base_ref }}" --head "${{ github.event.pull_request.head.sha }}" + build-artifacts: 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') diff --git a/.oxlintrc.json b/.oxlintrc.json new file mode 100644 index 00000000..cda82e50 --- /dev/null +++ b/.oxlintrc.json @@ -0,0 +1,43 @@ +{ + "$schema": "./node_modules/oxlint/configuration_schema.json", + "categories": { + "correctness": "warn" + }, + "rules": { + "no-explicit-any": "warn", + "typescript/no-floating-promises": "warn", + "typescript/no-misused-promises": "warn", + "typescript/await-thenable": "warn", + "typescript/no-base-to-string": "warn", + "typescript/restrict-template-expressions": "warn", + "typescript/no-misused-spread": "warn", + "typescript/no-this-alias": "warn", + "no-unused-vars": "warn", + "no-unassigned-vars": "warn", + "no-dupe-keys": "warn", + "no-unused-expressions": "warn", + "no-misleading-character-class": "warn", + "no-control-regex": "warn", + "no-new-array": "off" + }, + "overrides": [ + { + "files": [ + "**/*.test.ts", + "**/*.e2e.test.ts", + "**/*.live.test.ts", + "**/*.test-helpers.ts", + "**/test-helpers/**", + "**/test-utils/**" + ], + "rules": { + "typescript/no-floating-promises": "off", + "typescript/no-misused-promises": "off", + "typescript/unbound-method": "off", + "no-explicit-any": "off", + "typescript/no-base-to-string": "off", + "typescript/restrict-template-expressions": "off" + } + } + ] +} diff --git a/AGENTS.md b/AGENTS.md index 7a543e1d..6f603a8f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -79,6 +79,16 @@ - Aim to keep files under ~700 LOC; guideline only (not a hard guardrail). Split/refactor when it improves clarity or testability. - Naming: use **Mayros** for product/app/docs headings; use `mayros` for CLI command, package/binary, paths, and config keys. +## Prompt Cache Discipline + +Per-conversation prompt caching is sacred. A long-lived session reuses a cached prefix on every turn; anything that mutates past context, swaps toolsets, or rebuilds the system prompt with volatile content invalidates that cache and multiplies token cost. The project enforces caching via the `cacheRetention` stream option (handled by the provider layer), and the cost of an invalidation is real and silent. + +- The system prompt is rebuilt on every run. Its content must be **stable across runs that have no reason to change it**. Do not add volatile data (timestamps that tick every second, random IDs, unordered maps) to the system prompt or to context files that feed it. The `Runtime:` line is the sanctioned place for run-scoped info and is designed to change only when the operator changes model, channel, or thinking level. +- Slash commands that mutate prompt-relevant state (`/model`, `/think`, `/reasoning`, `/elevated`, `/fast`, `/new`) are expected to invalidate the cache on the **next** turn by design. That is the only acceptable invalidation. A change that invalidates the cache mid-turn, or on every turn regardless of whether the operator touched anything, is a bug. +- When adding context to the prompt (new sections, hook output, injected memory), prefer **append-only** structure. Reordering existing sections or inserting in the middle invalidates everything after the insertion point. +- Hooks (`before_prompt_build`, `before_agent_start`) that override `systemPrompt` or `prependContext` must be deterministic for a given input. A hook that returns different text on consecutive calls with the same inputs breaks the cache and is a high-priority bug. +- The `diagnostics.cacheTrace` subsystem records a SHA-256 of the system prompt and messages at each pipeline stage. When investigating cache regressions, compare `systemDigest` across consecutive turns of a session where nothing should have changed β€” a drift there is the signature of a cache-breaking change. + ## Release Channels (Naming) - stable: tagged releases only (e.g. `vYYYY.M.D`), npm dist-tag `latest`. diff --git a/README.md b/README.md index a1f15900..360b393a 100644 --- a/README.md +++ b/README.md @@ -156,7 +156,6 @@ mayros -p "refactor auth flow" # headless mode (non-interactive) - 🎨 3 themes (dark, light, high-contrast) β€” `/theme` - πŸ“ 3 output styles (standard, explanatory, learning) β€” `/style` -- ⌨️ Vim mode with motions, operators, undo β€” `/vim` - πŸ“‹ `Ctrl+V` image paste from clipboard - πŸ“Š `/diff` inline diff viewer Β· `/context` token usage chart - πŸ—ΊοΈ `/plan` semantic plan mode (Cortex-backed) @@ -165,15 +164,15 @@ mayros -p "refactor auth flow" # headless mode (non-interactive) **Slash commands (30+):** -| Command | Description | Command | Description | -| ---------------- | ----------------- | ---------------- | -------------------- | -| `/help` | List all commands | `/plan` | Semantic plan mode | -| `/new` | Reset session | `/diff` | Show pending changes | -| `/compact` | Compact context | `/context` | Token usage chart | -| `/think ` | Set thinking | `/theme` | Cycle themes | -| `/model ` | Switch model | `/vim` | Toggle vim mode | -| `/permission` | Permission mode | `/copy` | Copy last response | -| `/fast` | Fast mode | `/export [file]` | Export session | +| Command | Description | Command | Description | +| ---------------- | ----------------- | ------------- | -------------------- | +| `/help` | List all commands | `/plan` | Semantic plan mode | +| `/new` | Reset session | `/diff` | Show pending changes | +| `/compact` | Compact context | `/context` | Token usage chart | +| `/think ` | Set thinking | `/theme` | Cycle themes | +| `/model ` | Switch model | `/permission` | Permission mode | +| `/fast` | Fast mode | `/copy` | Copy last response | +| `/export [file]` | Export session | `/context` | Token usage chart | **Markdown-driven extensibility:** diff --git a/SECURITY.md b/SECURITY.md index 784bdf96..74786ce3 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -57,6 +57,48 @@ Mayros security guidance assumes: - Anyone who can modify `~/.mayros` state/config (including `mayros.json`) is effectively a trusted operator. - A single Gateway shared by mutually untrusted people is **not a recommended setup**. Use separate gateways (or at minimum separate OS users/hosts) per trust boundary. +## Trust Model + +The single load-bearing security boundary in Mayros is **operating-system level +isolation**. Everything else is a heuristic or a convenience, not a containment +boundary. + +This distinction matters because it is easy to over-trust in-process defenses. +The rule below governs how each layer should be treated: + +### What is and is not a boundary + +- **Real boundaries** β€” mechanisms the OS or kernel enforces against a + malicious process running inside them: + - Separate OS users, containers, VMs, namespaces, seccomp, Landlock. + - A sandboxed Docker container with `--cap-drop=ALL --read-only`. + - A separate host entirely (the strongest option). +- **In-process defenses** β€” useful, but **not containment**. A sufficiently + resourced attacker who has code execution inside the process can bypass them: + - The approval gate and interactive permission prompts. + - Output redaction and secret masking (`src/security/output-masking.ts`). + - The static skill scanner and bash command blocklist. + - Rate limiters, query/write caps, and the enrichment timeout. + - The WASM skill sandbox (it narrows the attack surface of untrusted skill + code, but the host process can still be reached via bugs in the runtime). + +Treat the in-process defenses as **defense in depth and attack-surface +reduction**, never as the thing that stops a determined adversary. The thing +that stops a determined adversary is running the untrusted workload under a +real OS boundary. + +### Practical implications + +- For fully untrusted skills, plugins, or tool output, run the Gateway (or the + session) inside a container or separate OS user. Do not rely on the approval + gate alone. +- The default loopback-only bind is a real boundary against remote network + attackers; keep it unless you have a replacement boundary (Tailscale, SSH + tunnel, firewall). +- When reviewing a security finding, classify it by which boundary it crosses. + A bug that lets a plugin read another plugin's memory is contained by the OS + user; a bug that lets a remotely-reached endpoint execute code is not. + ## Plugin Trust Boundary Plugins/extensions are loaded **in-process** with the Gateway and are treated as trusted code. @@ -123,6 +165,56 @@ docker run --read-only --cap-drop=ALL \ mayros/mayros:latest ``` +## Dependency Supply Chain + +Mayros treats its dependency tree as part of its trust boundary. A malicious or +broken transitive package reaches users the moment it enters the lockfile, so the +project enforces the following controls in `package.json`: + +### Release age floor + +`pnpm.minimumReleaseAge: 2880` (48 hours) gates every resolution. A package +published less than 48 hours ago is not eligible for install, which buffers +against compromised or yanked releases that get quarantined shortly after +publish. + +### Forced security overrides + +The `pnpm.overrides` map forces specific versions of transitive dependencies +known to carry security regressions. Each override exists because a transitive +in the tree resolved to a vulnerable version and the override is the most +durable way to pin the patched floor without waiting for upstreams to bump. + +When adding or changing an override, record why in the commit message: the CVE +or GHSA it addresses (or the regression it prevents). Overrides without a +recorded rationale are rejected in review. + +### Patched dependencies + +Any dependency declared in `pnpm.patchedDependencies` must use an exact version +(no `^` or `~`). This is a hard rule: a range on a patched package lets a future +resolve drift past the patch, silently reverting the fix. + +### Lockfile discipline + +The npm/pnpm lockfile pins the full transitive tree by content hash. Treat the +lockfile as a reviewed artifact, not an auto-generated one: + +- A change to `pnpm-lock.yaml` without a corresponding dependency change in + `package.json` is suspicious and must be explained in the commit. +- Dependabot/Renovate PRs that bump a transitive without a recorded CVE/fix are + held until the change is understood. +- `pnpm install --frozen-lockfile` is the canonical install command in CI; + `pnpm install` (which can mutate the lockfile) is reserved for local + dependency changes that are committed alongside a `deps:` commit. + +### Native build scripts + +`pnpm.onlyBuiltDependencies` enumerates the packages allowed to run native +build scripts (node-gyp, prebuilt binaries). Packages not in this list cannot +execute install scripts, which limits the blast radius of a compromised native +dependency. + ## Security Scanning This project uses `detect-secrets` for automated secret detection in CI/CD. diff --git a/apps/macos/Sources/Mayros/ModelCatalogLoader.swift b/apps/macos/Sources/Mayros/ModelCatalogLoader.swift index 0432b328..18bfaf72 100644 --- a/apps/macos/Sources/Mayros/ModelCatalogLoader.swift +++ b/apps/macos/Sources/Mayros/ModelCatalogLoader.swift @@ -114,7 +114,7 @@ enum ModelCatalogLoader { ] for root in roots { let candidate = root - .appendingPathComponent("node_modules/@mariozechner/pi-ai/dist/models.generated.js") + .appendingPathComponent("node_modules/@earendil-works/pi-ai/dist/models.generated.js") if FileManager().isReadableFile(atPath: candidate.path) { return candidate.path } diff --git a/docs/concepts/oauth.md b/docs/concepts/oauth.md index fe11ab4c..da93712e 100644 --- a/docs/concepts/oauth.md +++ b/docs/concepts/oauth.md @@ -71,7 +71,7 @@ mayros models status ## OAuth exchange (how login works) -Mayros’s interactive login flows are implemented in `@mariozechner/pi-ai` and wired into the wizards/commands. +Mayros’s interactive login flows are implemented in `@earendil-works/pi-ai` and wired into the wizards/commands. ### Anthropic (Claude Pro/Max) setup-token diff --git a/docs/gateway/doctor.md b/docs/gateway/doctor.md index 04a4ae45..fae508d7 100644 --- a/docs/gateway/doctor.md +++ b/docs/gateway/doctor.md @@ -129,7 +129,7 @@ Current migrations: ### 2b) OpenCode Zen provider overrides If you’ve added `models.providers.opencode` (or `opencode-zen`) manually, it -overrides the built-in OpenCode Zen catalog from `@mariozechner/pi-ai`. That can +overrides the built-in OpenCode Zen catalog from `@earendil-works/pi-ai`. That can force every model onto a single API or zero out costs. Doctor warns so you can remove the override and restore per-model API routing + costs. diff --git a/docs/pi.md b/docs/pi.md index f5fc0c74..3944e4c4 100644 --- a/docs/pi.md +++ b/docs/pi.md @@ -21,10 +21,10 @@ Mayros uses the pi SDK to embed an AI coding agent into its messaging gateway ar ```json { - "@mariozechner/pi-agent-core": "0.49.3", - "@mariozechner/pi-ai": "0.49.3", - "@mariozechner/pi-coding-agent": "0.49.3", - "@mariozechner/pi-tui": "0.49.3" + "@earendil-works/pi-agent-core": "0.49.3", + "@earendil-works/pi-ai": "0.49.3", + "@earendil-works/pi-coding-agent": "0.49.3", + "@earendil-works/pi-tui": "0.49.3" } ``` @@ -164,7 +164,7 @@ import { DefaultResourceLoader, SessionManager, SettingsManager, -} from "@mariozechner/pi-coding-agent"; +} from "@earendil-works/pi-coding-agent"; const resourceLoader = new DefaultResourceLoader({ cwd: resolvedWorkspace, @@ -504,7 +504,7 @@ Mayros also has a local TUI mode that uses pi-tui components directly: ```typescript // src/tui/tui.ts -import { ... } from "@mariozechner/pi-tui"; +import { ... } from "@earendil-works/pi-tui"; ``` This provides the interactive terminal experience similar to pi's native mode. diff --git a/docs/platforms/tui-redesign.md b/docs/platforms/tui-redesign.md new file mode 100644 index 00000000..e98a9834 --- /dev/null +++ b/docs/platforms/tui-redesign.md @@ -0,0 +1,109 @@ +--- +title: TUI Redesign Roadmap +description: Audit of the current terminal UI and prioritized plan for reaching a polished, best-in-class experience. +--- + +# TUI Redesign Roadmap + +This document captures the current state of the Mayros terminal UI (`mayros code` / `mayros tui`), the gaps identified during an internal audit, and a prioritized roadmap for the next redesign pass. It is a planning artifact; implementation is tracked separately. + +## Current state at a glance + +The TUI is a gateway client: it renders in the terminal and talks to a running `mayros gateway` daemon over WebSocket. The rendering, input, and component layer comes from the shared terminal UI dependency; Mayros adds the slash-command surface, themes, vim mode, mouse scrolling, accessibility fallback, and the gateway chat client. + +| Area | Maturity | +| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | +| Rendering and streaming | Mature. Event-driven render loop, full assistant-message re-render on each delta. | +| Slash commands | Mature in count (40+ built-in), uneven in depth. Core session commands are first-class; ecosystem commands are prompt-injection shims. | +| Themes | Mature. Ten palettes with 22 color tokens each, live hot-swap. | +| Vim mode | Basic. Custom implementation; operators partially stubbed, no visual or command-line mode. | +| Accessibility | Split. A linear text-mode TUI exists but is a separate, feature-poor implementation. | +| Platform coverage | Gaps. Clipboard image paste and `/copy` work on macOS and Linux only; Windows and Wayland are unsupported. | +| Architecture | Workable but strained. Two monolithic files (`tui.ts`, `tui-command-handlers.ts`), a mutable god-object state, and two parallel palette systems. | + +## Architectural constraints + +These shape what a redesign can and cannot do without larger upstream work. + +- **The terminal UI dependency is the rendering floor.** Layout capabilities (vertical-stack containers, select-list overlays) bound what Mayros can render. The tightest couplings are a `CustomEditor` that subclasses the shared editor and a `ChatLog` that subclasses the shared container. Refactoring either to composition requires an adapter layer or upstream changes. +- **The gateway is mandatory.** The TUI cannot run without a reachable gateway daemon. There is no in-process fallback, which affects offline use and dev ergonomics. +- **Theme hot-swap relies on singleton mutation.** Active theme objects are mutated in place so existing references stay live. Any snapshotting or caching of theme values would break live switching. +- **Mutable state object.** The TUI threads a single state object through every handler with manual render requests. There is no reactive store, although the signal libraries used elsewhere in the repo are available as a foundation. +- **Two parallel TUI implementations.** The graphical TUI and the accessibility TUI share almost no code. Every new feature currently needs to be implemented twice or skipped in one of them. +- **Two palette systems.** Terminal output (onboarding, prompts, CLI tables) and the TUI use separate palette modules with different accent colors. Visual consistency across the CLI experience requires unifying them. + +## Gaps by category + +### Rendering + +- No inline image rendering despite the shared terminal dependency shipping the capability. Image content blocks currently render as text placeholders. +- The diff renderer hardcodes colors instead of consuming the active theme, so diffs look wrong under light or high-contrast themes. +- Only vertical-stack layout; no split panes or resizable regions beyond the select-list overlay. +- Full assistant-text re-render on every streaming delta; long messages may degrade. + +### Input + +- Vim operator-plus-motion combinations are a simplified stub (non-linewise motions collapse to single-character operations). No visual mode, no command-line mode, no search, no repeat, no registers beyond a single yank buffer. +- The vim bridge does not appear to be wired from the TUI side; motions may silently no-op if the bridge is never connected. This needs verification before any vim work. +- Ctrl+C does not abort the active run; only Escape does. This is surprising and should align with user expectations. +- A cross-session input history store exists in code but is not connected to the main loop. + +### Commands + +- Ecosystem commands (`/team`, `/tasks`, `/workflow`, `/rules`, `/mailbox`, `/sync`, `/trace`, `/kg`, `/plan`) inject a prompt that asks the model to call a tool, rather than being first-class features. Results depend on the model. +- Several commands lack argument autocomplete. +- The theme preset list is duplicated in two files and can drift. +- The README command list is stale relative to the implementation. + +### Themes + +- The terminal palette and the TUI palette are separate modules with different accent colors. +- No user-defined custom themes. +- No colorblind-optimized palettes beyond the existing high-contrast option. +- No explicit `NO_COLOR` or `FORCE_COLOR` handling in the TUI. + +### Accessibility + +- The accessibility TUI is a stripped-down parallel implementation: no slash commands beyond exit, no session switching, no abort, no model selection. It excludes screen-reader users from most functionality. +- No screen-reader announcements in the graphical TUI. +- No monochrome or no-color mode in the graphical TUI. + +### Robustness and platform + +- Clipboard image paste and `/copy` support macOS and Linux (xclip) only. Windows and Wayland are unsupported. +- Several silent `catch` blocks swallow errors without logging. +- Disconnect handling shows hints but does not auto-reconnect with backoff. +- Window resize handling is delegated entirely to the shared terminal dependency and is not verified end-to-end in Mayros. + +## Roadmap + +Priorities are assigned as P0 (correctness or blocker), P1 (high-value polish), and P2 (nice to have). + +### P0 β€” Correctness and blockers + +1. ~~**Verify the vim bridge is connected.**~~ **Resolved: vim mode has been disabled.** The bridge was never wired and the rendering dependency does not expose the cursor API needed to fix it cleanly. The `/vim` command now reports that the feature is unavailable, the dead handler and its tests have been removed, and the config flag is deprecated. Emacs/readline-style keybindings (Ctrl+A/E/W/U/K) remain available via the editor. +2. **Accessibility TUI feature parity.** Either linearize the graphical renderer for screen readers or bring slash commands, session switching, and abort into the accessibility TUI. The current split excludes users from most functionality. + +### P1 β€” High-value polish + +3. **Inline image rendering.** Use the shared terminal image component to render image content blocks inline instead of text placeholders. +4. **Unify the palettes.** Merge the terminal palette and the TUI palette into one source of truth; make the diff renderer and every component consume the active theme. +5. **Complete vim mode.** Implement real operator-plus-motion semantics and add visual mode, or adopt a proven keymap pattern. +6. **Windows and Wayland clipboard support.** Add the platform-native clipboard commands for `/copy` and image paste. +7. **Make Ctrl+C abort the active run** with a double-press-to-exit fallback, keeping Escape as a secondary abort. +8. **Refresh the README** to reflect the actual theme count and command list. + +### P2 β€” Nice to have + +9. Terminal color-scheme auto-detection to pick dark or light theme automatically. +10. Wire the cross-session input history store into the main loop, or remove the dead code. +11. User-defined custom themes loaded from a config directory. +12. Argument autocomplete for every command; deduplicate the theme preset list. +13. Colorblind-optimized palettes and a true monochrome mode. +14. Incremental streaming render for long assistant messages. +15. Replace silent error swallowing with structured logging behind a debug flag. +16. Split the two monolithic TUI files to bring them under the file-size guideline. + +## Next steps + +The first implementation pass should focus on the P0 items, since both block a trustworthy feature surface: vim cannot be extended on top of an unwired bridge, and the accessibility TUI cannot ship as the screen-reader path while it lacks core commands. The P1 items are the bulk of the visible polish work and can be sequenced independently once the P0 verifications land. diff --git a/docs/reference/session-management-compaction.md b/docs/reference/session-management-compaction.md index 8f1c43a3..0080299f 100644 --- a/docs/reference/session-management-compaction.md +++ b/docs/reference/session-management-compaction.md @@ -123,7 +123,7 @@ The store is safe to edit, but the Gateway is the authority: it may rewrite or r ## Transcript structure (`*.jsonl`) -Transcripts are managed by `@mariozechner/pi-coding-agent`’s `SessionManager`. +Transcripts are managed by `@earendil-works/pi-coding-agent`’s `SessionManager`. The file is JSONL: diff --git a/extensions/agent-mesh/index.ts b/extensions/agent-mesh/index.ts index 24ba5bf6..76eb5f12 100644 --- a/extensions/agent-mesh/index.ts +++ b/extensions/agent-mesh/index.ts @@ -2222,7 +2222,12 @@ const agentMeshPlugin = { for (const [name, handler] of Object.entries(methods)) { api.registerGatewayMethod(name, async ({ params, respond }) => { try { - const result = await handler((params ?? {}) as any); + // Handlers in the Mamoru gateway map are heterogeneous (each + // declares its own params shape). The runtime contract is that + // params arrive validated by the gateway dispatcher, so we forward + // the resolved object typed as the handler's first parameter. + const fn = handler as (input: Record) => Promise; + const result = await fn((params ?? {}) as Record); respond(true, result); } catch (err) { respond(false, { error: err instanceof Error ? err.message : String(err) }); @@ -2277,21 +2282,28 @@ const agentMeshPlugin = { const nodeFsP = await import("node:fs/promises"); const home = process.env.HOME ?? ""; - // Update mayros.json with model + ollama auth profile + // Update mayros.json with model + ollama auth profile. The config is + // read from disk and mutated, so we model the shape we touch rather + // than widening to Record. + type MayrosJsonFile = { + agents?: { defaults?: { model?: { primary?: string; [k: string]: unknown } } }; + auth?: { profiles?: Record }; + [k: string]: unknown; + }; const configPath = nodePath.join(home, ".mayros", "mayros.json"); - let cfg: Record = {}; + let cfg: MayrosJsonFile = {}; try { - cfg = JSON.parse(await nodeFsP.readFile(configPath, "utf8")); + cfg = JSON.parse(await nodeFsP.readFile(configPath, "utf8")) as MayrosJsonFile; } 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 ??= {}; + cfg.agents.defaults ??= {}; + 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 ??= {}; + cfg.auth.profiles ??= {}; cfg.auth.profiles["ollama"] = { provider: "ollama", mode: "api_key" }; } await nodeFsP.writeFile(configPath, JSON.stringify(cfg, null, 2) + "\n"); @@ -2301,13 +2313,18 @@ const agentMeshPlugin = { 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: {} }; + type AuthProfileStore = { + version?: number; + profiles?: Record; + [k: string]: unknown; + }; + let store: AuthProfileStore = { version: 2, profiles: {} }; try { - store = JSON.parse(await nodeFsP.readFile(storePath, "utf8")); + store = JSON.parse(await nodeFsP.readFile(storePath, "utf8")) as AuthProfileStore; } catch { /* new */ } - if (!store.profiles) store.profiles = {}; + store.profiles ??= {}; store.profiles["ollama"] = { provider: "ollama", type: "api_key", key: "ollama-local" }; await nodeFsP.writeFile(storePath, JSON.stringify(store, null, 2)); } diff --git a/extensions/browser-automation/index.ts b/extensions/browser-automation/index.ts index ce407915..835d50f5 100644 --- a/extensions/browser-automation/index.ts +++ b/extensions/browser-automation/index.ts @@ -46,7 +46,8 @@ const browserAutomationPlugin = { }, required: ["url"], }, - execute: async (_toolCallId: string, args: Record) => { + execute: async (_toolCallId: string, rawArgs: unknown) => { + const args = rawArgs as Record; const { BrowserClient } = await import("./browser-client.js"); const client = new BrowserClient(); await client.connect(); @@ -110,7 +111,8 @@ const browserAutomationPlugin = { }, required: ["selector"], }, - execute: async (_toolCallId: string, args: Record) => { + execute: async (_toolCallId: string, rawArgs: unknown) => { + const args = rawArgs as Record; const { BrowserClient } = await import("./browser-client.js"); const client = new BrowserClient(); await client.connect(); @@ -141,7 +143,8 @@ const browserAutomationPlugin = { }, required: ["expression"], }, - execute: async (_toolCallId: string, args: Record) => { + execute: async (_toolCallId: string, rawArgs: unknown) => { + const args = rawArgs as Record; const { BrowserClient } = await import("./browser-client.js"); const client = new BrowserClient(); await client.connect(); diff --git a/extensions/feishu/package.json b/extensions/feishu/package.json index 970f57c8..05b33b24 100644 --- a/extensions/feishu/package.json +++ b/extensions/feishu/package.json @@ -10,7 +10,7 @@ "dependencies": { "@larksuiteoapi/node-sdk": "^1.59.0", "@sinclair/typebox": "0.34.48", - "zod": "^4.3.6" + "zod": "^4.4.3" }, "peerDependencies": { "@apilium/mayros": ">=0.1.0" @@ -22,10 +22,10 @@ "channel": { "id": "feishu", "label": "Feishu", - "selectionLabel": "Feishu/Lark (飞书)", + "selectionLabel": "Feishu/Lark (\u98de\u4e66)", "docsPath": "/channels/feishu", "docsLabel": "feishu", - "blurb": "飞书/Lark enterprise messaging with doc/wiki/drive tools.", + "blurb": "\u98de\u4e66/Lark enterprise messaging with doc/wiki/drive tools.", "aliases": [ "lark" ], diff --git a/extensions/feishu/src/bot.ts b/extensions/feishu/src/bot.ts index 95af28eb..83215694 100644 --- a/extensions/feishu/src/bot.ts +++ b/extensions/feishu/src/bot.ts @@ -86,7 +86,7 @@ type SenderNameResult = { async function resolveFeishuSenderName(params: { account: ResolvedFeishuAccount; senderOpenId: string; - log: (...args: any[]) => void; + log: (...args: unknown[]) => void; }): Promise { const { account, senderOpenId, log } = params; if (!account.configured) return {}; @@ -100,10 +100,19 @@ async function resolveFeishuSenderName(params: { const client = createFeishuClient(account); // contact/v3/users/:user_id?user_id_type=open_id - const res: any = await client.contact.user.get({ + // The Feishu SDK returns an untyped response envelope; narrow the fields + // we actually read instead of widening the whole value to `any`. + type FeishuUser = { + name?: string; + display_name?: string; + nickname?: string; + en_name?: string; + }; + type FeishuUserResponse = { data?: { user?: FeishuUser } }; + const res = (await client.contact.user.get({ path: { user_id: senderOpenId }, params: { user_id_type: "open_id" }, - }); + })) as FeishuUserResponse; const name: string | undefined = res?.data?.user?.name || diff --git a/extensions/feishu/src/docx.ts b/extensions/feishu/src/docx.ts index 52f90dbb..f4f5d048 100644 --- a/extensions/feishu/src/docx.ts +++ b/extensions/feishu/src/docx.ts @@ -93,11 +93,16 @@ async function convertMarkdown(client: Lark.Client, markdown: string) { }; } -function sortBlocksByFirstLevel(blocks: any[], firstLevelIds: string[]): any[] { +/** Minimal structural shape of a Feishu/Lark document block used for sorting. */ +type FeishuBlock = { block_id?: string; [key: string]: unknown }; + +function sortBlocksByFirstLevel(blocks: FeishuBlock[], firstLevelIds: string[]): FeishuBlock[] { if (!firstLevelIds || firstLevelIds.length === 0) return blocks; - const sorted = firstLevelIds.map((id) => blocks.find((b) => b.block_id === id)).filter(Boolean); + const sorted = firstLevelIds + .map((id) => blocks.find((b) => b.block_id === id)) + .filter((b): b is FeishuBlock => Boolean(b)); const sortedIds = new Set(firstLevelIds); - const remaining = blocks.filter((b) => !sortedIds.has(b.block_id)); + const remaining = blocks.filter((b) => !sortedIds.has(b.block_id ?? "")); return [...sorted, ...remaining]; } diff --git a/extensions/kakeru-bridge/index.ts b/extensions/kakeru-bridge/index.ts index 379a4980..163d6fc6 100644 --- a/extensions/kakeru-bridge/index.ts +++ b/extensions/kakeru-bridge/index.ts @@ -88,15 +88,13 @@ const kakeruPlugin = { ), timeout: Type.Optional(Type.Number({ description: "Timeout in ms" })), }), - execute: async ( - _toolCallId: string, - params: { + execute: async (_toolCallId: string, rawParams: unknown) => { + const params = rawParams as { platform: string; prompt: string; workDir?: string; timeout?: number; - }, - ) => { + }; const bridge = coordinator.getBridge(params.platform); if (!bridge) { return { @@ -148,12 +146,10 @@ const kakeruPlugin = { }), ), }), - execute: async ( - _toolCallId: string, - params: { + execute: async (_toolCallId: string, rawParams: unknown) => { + const params = rawParams as { tasks: Array<{ platform: string; prompt: string; filePaths?: string[] }>; - }, - ) => { + }; const workDir = ((api.config as Record | undefined)?.workspaceDir as | string diff --git a/extensions/matrix/src/tool-actions.ts b/extensions/matrix/src/tool-actions.ts index cc187e7b..d08f512e 100644 --- a/extensions/matrix/src/tool-actions.ts +++ b/extensions/matrix/src/tool-actions.ts @@ -1,4 +1,4 @@ -import type { AgentToolResult } from "@mariozechner/pi-agent-core"; +import type { AgentToolResult } from "@earendil-works/pi-agent-core"; import { createActionGate, jsonResult, diff --git a/extensions/mattermost/src/channel.ts b/extensions/mattermost/src/channel.ts index ed979461..37ce4591 100644 --- a/extensions/mattermost/src/channel.ts +++ b/extensions/mattermost/src/channel.ts @@ -67,23 +67,23 @@ const mattermostMessageActions: ChannelMessageActionAdapter = { } const postIdRaw = - typeof (params as any)?.messageId === "string" - ? (params as any).messageId - : typeof (params as any)?.postId === "string" - ? (params as any).postId + typeof params.messageId === "string" + ? params.messageId + : typeof params.postId === "string" + ? params.postId : ""; const postId = postIdRaw.trim(); if (!postId) { throw new Error("Mattermost react requires messageId (post id)"); } - const emojiRaw = typeof (params as any)?.emoji === "string" ? (params as any).emoji : ""; + const emojiRaw = typeof params.emoji === "string" ? params.emoji : ""; const emojiName = emojiRaw.trim().replace(/^:+|:+$/g, ""); if (!emojiName) { throw new Error("Mattermost react requires emoji"); } - const remove = (params as any)?.remove === true; + const remove = params.remove === true; if (remove) { const result = await removeMattermostReaction({ cfg, diff --git a/extensions/mcp-server/index.ts b/extensions/mcp-server/index.ts index 65efa511..299f8016 100644 --- a/extensions/mcp-server/index.ts +++ b/extensions/mcp-server/index.ts @@ -15,6 +15,7 @@ // @ts-expect-error β€” dist/index.js has no declaration file; types resolved via source paths import type { MayrosPluginApi, MayrosPluginToolContext } from "@apilium/mayros"; +import type { Command } from "commander"; import { mcpServerConfigSchema, type McpServerConfig } from "./config.js"; import { McpServer, type McpServerOptions } from "./server.js"; import type { AdaptableTool } from "./tool-adapter.js"; @@ -142,7 +143,7 @@ const mcpServerPlugin = { // ── Register CLI ──────────────────────────────────────────────── - api.registerCli(({ program }: { program: any }) => { + api.registerCli(({ program }: { program: Command }) => { const serve = program .command("serve") .description("Start MCP server to expose Mayros tools, resources, and prompts"); @@ -303,45 +304,49 @@ const mcpServerPlugin = { // ── Register gateway method (MCP Dashboard) ──────────────────── - api.registerGatewayMethod("mcp.dashboard", async ({ respond }: { respond: any }) => { - // Cortex health check with 3s timeout β€” always runs - let cortexHealth: { status: "online" | "offline"; latencyMs: number }; - try { - const cortexPort = cfg.cortex?.port ?? 19090; - const cortexUrl = `http://127.0.0.1:${cortexPort}/api/v1/health`; - const start = Date.now(); - const cortexRes = await fetch(cortexUrl, { - signal: AbortSignal.timeout(3000), - }); - const latencyMs = Date.now() - start; - cortexHealth = { - status: cortexRes.ok ? "online" : "offline", - latencyMs, - }; - } catch { - cortexHealth = { status: "offline", latencyMs: 0 }; - } + type DashboardRespond = (ok: boolean, payload?: unknown) => void; + api.registerGatewayMethod( + "mcp.dashboard", + async ({ respond }: { respond: DashboardRespond }) => { + // Cortex health check with 3s timeout β€” always runs + let cortexHealth: { status: "online" | "offline"; latencyMs: number }; + try { + const cortexPort = cfg.cortex?.port ?? 19090; + const cortexUrl = `http://127.0.0.1:${cortexPort}/api/v1/health`; + const start = Date.now(); + const cortexRes = await fetch(cortexUrl, { + signal: AbortSignal.timeout(3000), + }); + const latencyMs = Date.now() - start; + cortexHealth = { + status: cortexRes.ok ? "online" : "offline", + latencyMs, + }; + } catch { + cortexHealth = { status: "offline", latencyMs: 0 }; + } - if (!server) { - respond(true, { - status: { - running: false, - transport: cfg.transport ?? "http", - toolCount: 0, - initialized: false, - uptimeMs: 0, - sseSessionCount: 0, - }, - metrics: null, - cortexHealth, - }); - return; - } + if (!server) { + respond(true, { + status: { + running: false, + transport: cfg.transport ?? "http", + toolCount: 0, + initialized: false, + uptimeMs: 0, + sseSessionCount: 0, + }, + metrics: null, + cortexHealth, + }); + return; + } - const status = server.status(); - const metrics = server.getMetrics(); - respond(true, { status, metrics, cortexHealth }); - }); + const status = server.status(); + const metrics = server.getMetrics(); + respond(true, { status, metrics, cortexHealth }); + }, + ); // ── Register service lifecycle ────────────────────────────────── diff --git a/extensions/osameru-governance/index.ts b/extensions/osameru-governance/index.ts index 1a3de3aa..5f360dd0 100644 --- a/extensions/osameru-governance/index.ts +++ b/extensions/osameru-governance/index.ts @@ -182,15 +182,13 @@ const osameruPlugin = { ), limit: Type.Optional(Type.Number({ description: "Max entries to return" })), }), - execute: async ( - _toolCallId: string, - params: { + execute: async (_toolCallId: string, rawParams: unknown) => { + const params = rawParams as { event?: string; actor?: string; decision?: string; limit?: number; - }, - ) => { + }; const entries = await auditTrail.query(params); return { content: [ diff --git a/extensions/voice-call/index.ts b/extensions/voice-call/index.ts index 645eed5f..f18b30ed 100644 --- a/extensions/voice-call/index.ts +++ b/extensions/voice-call/index.ts @@ -349,7 +349,15 @@ const voiceCallPlugin = { label: "Voice Call", description: "Make phone calls and have voice conversations via the voice-call plugin.", parameters: VoiceCallToolSchema, - async execute(_toolCallId, params) { + async execute(_toolCallId, rawParams) { + const params = rawParams as { + action?: string; + message?: string; + to?: string; + mode?: string; + callId?: string; + sid?: string; + }; const json = (payload: unknown) => ({ content: [{ type: "text" as const, text: JSON.stringify(payload, null, 2) }], details: payload, diff --git a/package.json b/package.json index 031db48a..7d7519b3 100644 --- a/package.json +++ b/package.json @@ -160,45 +160,45 @@ }, "dependencies": { "@agentclientprotocol/sdk": "0.14.1", - "@aws-sdk/client-bedrock": "^3.1019.0", + "@aws-sdk/client-bedrock": "^3.1085.0", "@buape/carbon": "0.0.0-beta-20260216184201", - "@clack/prompts": "^1.0.1", + "@clack/prompts": "^1.7.0", "@discordjs/voice": "^0.19.0", + "@earendil-works/pi-agent-core": "^0.80.6", + "@earendil-works/pi-ai": "^0.80.6", + "@earendil-works/pi-coding-agent": "^0.80.6", + "@earendil-works/pi-tui": "^0.80.6", "@grammyjs/runner": "^2.0.3", "@grammyjs/transformer-throttler": "^1.2.1", - "@homebridge/ciao": "^1.3.5", + "@homebridge/ciao": "^1.3.10", "@line/bot-sdk": "^10.6.0", "@lydell/node-pty": "1.2.0-beta.3", - "@mariozechner/pi-agent-core": "0.54.0", - "@mariozechner/pi-ai": "0.54.0", - "@mariozechner/pi-coding-agent": "0.54.0", - "@mariozechner/pi-tui": "0.54.0", "@mozilla/readability": "^0.6.0", - "@noble/curves": "^2.0.1", + "@noble/curves": "^2.2.0", "@sinclair/typebox": "0.34.48", - "@slack/bolt": "^4.6.0", - "@slack/web-api": "^7.14.1", + "@slack/bolt": "^4.7.3", + "@slack/web-api": "^7.19.0", "@whiskeysockets/baileys": "7.0.0-rc.9", - "ajv": "^8.18.0", + "ajv": "^8.20.0", "chalk": "^5.6.2", "chokidar": "^5.0.0", "cli-highlight": "^2.1.11", "commander": "^14.0.3", "croner": "^10.0.1", "discord-api-types": "^0.38.40", - "dotenv": "^17.3.1", + "dotenv": "^17.4.2", "express": "^5.2.1", "fast-glob": "^3.3.3", "file-type": "^21.3.2", - "grammy": "^1.40.0", + "grammy": "^1.44.0", "hono": "4.12.7", "https-proxy-agent": "^7.0.6", - "jiti": "^2.6.1", + "jiti": "^2.7.0", "json5": "^2.2.3", "jszip": "^3.10.1", "linkedom": "^0.18.12", "long": "^5.3.2", - "markdown-it": "^14.1.1", + "markdown-it": "^14.3.0", "node-edge-tts": "^1.2.10", "opusscript": "^0.0.8", "osc-progress": "^0.3.0", @@ -206,18 +206,18 @@ "playwright-core": "1.58.2", "qrcode-terminal": "^0.12.0", "quickjs-emscripten": "^0.31.0", - "semver": "^7.7.4", + "semver": "^7.8.5", "sharp": "^0.34.5", "sqlite-vec": "0.1.7-alpha.2", "tar": "7.5.11", - "tslog": "^4.10.2", + "tslog": "^4.11.0", "undici": "^7.24.2", - "ws": "^8.19.0", + "ws": "^8.21.0", "yaml": "^2.8.3", - "zod": "^4.3.6" + "zod": "^4.4.3" }, "devDependencies": { - "@grammyjs/types": "^3.24.0", + "@grammyjs/types": "^3.28.0", "@lit-labs/signals": "^0.2.0", "@lit/context": "^1.1.6", "@types/express": "^5.0.6", @@ -227,16 +227,16 @@ "@types/semver": "^7.7.1", "@types/ws": "^8.18.1", "@typescript/native-preview": "7.0.0-dev.20260221.1", - "@vitest/coverage-v8": "^4.0.18", - "lit": "^3.3.2", + "@vitest/coverage-v8": "^4.1.10", + "lit": "^3.3.3", "oxfmt": "0.34.0", - "oxlint": "^1.49.0", + "oxlint": "^1.73.0", "oxlint-tsgolint": "^0.14.2", "signal-utils": "0.21.1", "tsdown": "^0.20.3", - "tsx": "^4.21.0", + "tsx": "^4.23.1", "typescript": "^5.9.3", - "vitest": "^4.0.18" + "vitest": "^4.1.10" }, "peerDependencies": { "@napi-rs/canvas": "^0.1.89", @@ -249,6 +249,7 @@ "pnpm": { "minimumReleaseAge": 2880, "overrides": { + "@apilium/mayros": "workspace:*", "hono": "4.12.7", "@hono/node-server": "1.19.10", "fast-xml-parser": ">=5.5.7", @@ -267,7 +268,8 @@ "path-to-regexp": ">=8.4.0", "brace-expansion": ">=5.0.5", "music-metadata": ">=11.12.3", - "yaml": ">=2.8.3" + "yaml": ">=2.8.3", + "zod": "4.4.3" }, "onlyBuiltDependencies": [ "@lydell/node-pty", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 39bc2a0e..38f7aa66 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,6 +5,7 @@ settings: excludeLinksFromLockfile: false overrides: + '@apilium/mayros': workspace:* hono: 4.12.7 '@hono/node-server': 1.19.10 fast-xml-parser: '>=5.5.7' @@ -24,6 +25,7 @@ overrides: brace-expansion: '>=5.0.5' music-metadata: '>=11.12.3' yaml: '>=2.8.3' + zod: 4.4.3 importers: @@ -31,46 +33,46 @@ importers: dependencies: '@agentclientprotocol/sdk': specifier: 0.14.1 - version: 0.14.1(zod@4.3.6) + version: 0.14.1(zod@4.4.3) '@aws-sdk/client-bedrock': - specifier: ^3.1019.0 - version: 3.1019.0 + specifier: ^3.1085.0 + version: 3.1085.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) + version: 0.0.0-beta-20260216184201(hono@4.12.7)(opusscript@0.0.8) '@clack/prompts': - specifier: ^1.0.1 - version: 1.0.1 + specifier: ^1.7.0 + version: 1.7.0 '@discordjs/voice': specifier: ^0.19.0 - version: 0.19.0(opusscript@0.0.8)(opusscript@0.0.8) + version: 0.19.0(opusscript@0.0.8) + '@earendil-works/pi-agent-core': + specifier: ^0.80.6 + version: 0.80.6(ws@8.21.0)(zod@4.4.3) + '@earendil-works/pi-ai': + specifier: ^0.80.6 + version: 0.80.6(ws@8.21.0)(zod@4.4.3) + '@earendil-works/pi-coding-agent': + specifier: ^0.80.6 + version: 0.80.6(ws@8.21.0)(zod@4.4.3) + '@earendil-works/pi-tui': + specifier: ^0.80.6 + version: 0.80.6 '@grammyjs/runner': specifier: ^2.0.3 - version: 2.0.3(grammy@1.40.0) + version: 2.0.3(grammy@1.44.0) '@grammyjs/transformer-throttler': specifier: ^1.2.1 - version: 1.2.1(grammy@1.40.0) + version: 1.2.1(grammy@1.44.0) '@homebridge/ciao': - specifier: ^1.3.5 - version: 1.3.5 + specifier: ^1.3.10 + version: 1.3.10 '@line/bot-sdk': specifier: ^10.6.0 version: 10.6.0 '@lydell/node-pty': specifier: 1.2.0-beta.3 version: 1.2.0-beta.3 - '@mariozechner/pi-agent-core': - specifier: 0.54.0 - version: 0.54.0(ws@8.19.0)(zod@4.3.6) - '@mariozechner/pi-ai': - specifier: 0.54.0 - version: 0.54.0(ws@8.19.0)(zod@4.3.6) - '@mariozechner/pi-coding-agent': - specifier: 0.54.0 - version: 0.54.0(ws@8.19.0)(zod@4.3.6) - '@mariozechner/pi-tui': - specifier: 0.54.0 - version: 0.54.0 '@mozilla/readability': specifier: ^0.6.0 version: 0.6.0 @@ -78,23 +80,23 @@ importers: specifier: ^0.1.89 version: 0.1.95 '@noble/curves': - specifier: ^2.0.1 - version: 2.0.1 + specifier: ^2.2.0 + version: 2.2.0 '@sinclair/typebox': specifier: 0.34.48 version: 0.34.48 '@slack/bolt': - specifier: ^4.6.0 - version: 4.6.0(@types/express@5.0.6) + specifier: ^4.7.3 + version: 4.7.3(@types/express@5.0.6) '@slack/web-api': - specifier: ^7.14.1 - version: 7.14.1 + specifier: ^7.19.0 + version: 7.19.0 '@whiskeysockets/baileys': specifier: 7.0.0-rc.9 version: 7.0.0-rc.9(sharp@0.34.5) ajv: - specifier: ^8.18.0 - version: 8.18.0 + specifier: ^8.20.0 + version: 8.20.0 chalk: specifier: ^5.6.2 version: 5.6.2 @@ -114,8 +116,8 @@ importers: specifier: ^0.38.40 version: 0.38.40 dotenv: - specifier: ^17.3.1 - version: 17.3.1 + specifier: ^17.4.2 + version: 17.4.2 express: specifier: ^5.2.1 version: 5.2.1 @@ -126,8 +128,8 @@ importers: specifier: '>=21.3.1' version: 21.3.2 grammy: - specifier: ^1.40.0 - version: 1.40.0 + specifier: ^1.44.0 + version: 1.44.0 hono: specifier: 4.12.7 version: 4.12.7 @@ -135,8 +137,8 @@ importers: specifier: ^7.0.6 version: 7.0.6 jiti: - specifier: ^2.6.1 - version: 2.6.1 + specifier: ^2.7.0 + version: 2.7.0 json5: specifier: ^2.2.3 version: 2.2.3 @@ -150,8 +152,8 @@ importers: specifier: ^5.3.2 version: 5.3.2 markdown-it: - specifier: ^14.1.1 - version: 14.1.1 + specifier: ^14.3.0 + version: 14.3.0 node-edge-tts: specifier: ^1.2.10 version: 1.2.10 @@ -177,8 +179,8 @@ importers: specifier: ^0.31.0 version: 0.31.0 semver: - specifier: ^7.7.4 - version: 7.7.4 + specifier: ^7.8.5 + version: 7.8.5 sharp: specifier: ^0.34.5 version: 0.34.5 @@ -189,24 +191,24 @@ importers: specifier: 7.5.11 version: 7.5.11 tslog: - specifier: ^4.10.2 - version: 4.10.2 + specifier: ^4.11.0 + version: 4.11.0 undici: specifier: '>=7.24.0' version: 7.24.2 ws: - specifier: ^8.19.0 - version: 8.19.0 + specifier: ^8.21.0 + version: 8.21.0 yaml: specifier: '>=2.8.3' version: 2.8.3 zod: - specifier: ^4.3.6 - version: 4.3.6 + specifier: 4.4.3 + version: 4.4.3 devDependencies: '@grammyjs/types': - specifier: ^3.24.0 - version: 3.24.0 + specifier: ^3.28.0 + version: 3.28.0 '@lit-labs/signals': specifier: ^0.2.0 version: 0.2.0 @@ -235,17 +237,17 @@ importers: specifier: 7.0.0-dev.20260221.1 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.3))(vitest@4.0.18))(vitest@4.0.18) + specifier: ^4.1.10 + version: 4.1.10(vitest@4.1.10) lit: - specifier: ^3.3.2 - version: 3.3.2 + specifier: ^3.3.3 + version: 3.3.3 oxfmt: specifier: 0.34.0 version: 0.34.0 oxlint: - specifier: ^1.49.0 - version: 1.50.0(oxlint-tsgolint@0.14.2) + specifier: ^1.73.0 + version: 1.73.0(oxlint-tsgolint@0.14.2) oxlint-tsgolint: specifier: ^0.14.2 version: 0.14.2 @@ -256,14 +258,14 @@ importers: specifier: ^0.20.3 version: 0.20.3(@typescript/native-preview@7.0.0-dev.20260221.1)(typescript@5.9.3) tsx: - specifier: ^4.21.0 - version: 4.21.0 + specifier: ^4.23.1 + version: 4.23.1 typescript: specifier: ^5.9.3 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.3) + specifier: ^4.1.10 + version: 4.1.10(@opentelemetry/api@1.9.0)(@types/node@25.3.0)(@vitest/coverage-v8@4.1.10)(vite@7.3.1(@types/node@25.3.0)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.8.3)) extensions/agent-mesh: dependencies: @@ -290,8 +292,8 @@ importers: extensions/bluebubbles: dependencies: '@apilium/mayros': - specifier: '>=0.1.0' - 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)) + specifier: workspace:* + version: link:../.. extensions/ci-plugin: dependencies: @@ -341,8 +343,8 @@ importers: extensions/diagnostics-otel: dependencies: '@apilium/mayros': - specifier: '>=0.1.0' - 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)) + specifier: workspace:* + version: link:../.. '@opentelemetry/api': specifier: ^1.9.0 version: 1.9.0 @@ -380,8 +382,8 @@ importers: extensions/discord: dependencies: '@apilium/mayros': - specifier: '>=0.1.0' - 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)) + specifier: workspace:* + version: link:../.. extensions/eruberu: dependencies: @@ -396,8 +398,8 @@ importers: extensions/feishu: dependencies: '@apilium/mayros': - specifier: '>=0.1.0' - 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)) + specifier: workspace:* + version: link:../.. '@larksuiteoapi/node-sdk': specifier: ^1.59.0 version: 1.59.0 @@ -405,8 +407,8 @@ importers: specifier: 0.34.48 version: 0.34.48 zod: - specifier: ^4.3.6 - version: 4.3.6 + specifier: 4.4.3 + version: 4.4.3 extensions/google-antigravity-auth: devDependencies: @@ -469,11 +471,11 @@ importers: extensions/irc: dependencies: '@apilium/mayros': - specifier: '>=0.1.0' - 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)) + specifier: workspace:* + version: link:../.. zod: - specifier: ^4.3.6 - version: 4.3.6 + specifier: 4.4.3 + version: 4.4.3 extensions/kakeru-bridge: dependencies: @@ -510,8 +512,8 @@ importers: extensions/lobster: dependencies: '@apilium/mayros': - specifier: '>=0.1.0' - 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)) + specifier: workspace:* + version: link:../.. '@sinclair/typebox': specifier: 0.34.48 version: 0.34.48 @@ -525,8 +527,8 @@ importers: extensions/matrix: dependencies: '@apilium/mayros': - specifier: '>=0.1.0' - 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)) + specifier: workspace:* + version: link:../.. '@matrix-org/matrix-sdk-crypto-nodejs': specifier: ^0.4.0 version: 0.4.0 @@ -540,8 +542,8 @@ importers: specifier: '>=11.12.3' version: 11.12.3 zod: - specifier: ^4.3.6 - version: 4.3.6 + specifier: 4.4.3 + version: 4.4.3 extensions/mattermost: devDependencies: @@ -585,7 +587,7 @@ importers: version: 0.34.48 openai: specifier: ^6.22.0 - version: 6.25.0(ws@8.19.0)(zod@4.3.6) + version: 6.25.0(ws@8.21.0)(zod@4.4.3) devDependencies: '@apilium/mayros': specifier: workspace:* @@ -610,8 +612,8 @@ importers: extensions/msteams: dependencies: '@apilium/mayros': - specifier: '>=0.1.0' - 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)) + specifier: workspace:* + version: link:../.. '@microsoft/agents-hosting': specifier: ^1.3.1 version: 1.3.1 @@ -622,23 +624,23 @@ importers: extensions/nextcloud-talk: dependencies: '@apilium/mayros': - specifier: '>=0.1.0' - 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)) + specifier: workspace:* + version: link:../.. zod: - specifier: ^4.3.6 - version: 4.3.6 + specifier: 4.4.3 + version: 4.4.3 extensions/nostr: dependencies: '@apilium/mayros': - specifier: '>=0.1.0' - 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)) + specifier: workspace:* + version: link:../.. nostr-tools: specifier: ^2.23.1 version: 2.23.1(typescript@5.9.3) zod: - specifier: ^4.3.6 - version: 4.3.6 + specifier: 4.4.3 + version: 4.4.3 extensions/open-prose: devDependencies: @@ -749,8 +751,8 @@ importers: specifier: ^8.0.3 version: 8.0.3(@twurple/auth@8.0.3) zod: - specifier: ^4.3.6 - version: 4.3.6 + specifier: 4.4.3 + version: 4.4.3 devDependencies: '@apilium/mayros': specifier: workspace:* @@ -759,8 +761,8 @@ importers: extensions/voice-call: dependencies: '@apilium/mayros': - specifier: '>=0.1.0' - 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)) + specifier: workspace:* + version: link:../.. '@sinclair/typebox': specifier: 0.34.48 version: 0.34.48 @@ -768,8 +770,8 @@ importers: specifier: ^8.19.0 version: 8.19.0 zod: - specifier: ^4.3.6 - version: 4.3.6 + specifier: 4.4.3 + version: 4.4.3 extensions/whatsapp: devDependencies: @@ -780,8 +782,8 @@ importers: extensions/zalo: dependencies: '@apilium/mayros': - specifier: '>=0.1.0' - 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)) + specifier: workspace:* + version: link:../.. undici: specifier: '>=7.24.0' version: 7.24.2 @@ -789,8 +791,8 @@ importers: extensions/zalouser: dependencies: '@apilium/mayros': - specifier: '>=0.1.0' - 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)) + specifier: workspace:* + version: link:../.. '@sinclair/typebox': specifier: 0.34.48 version: 0.34.48 @@ -819,7 +821,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.3))(vitest@4.0.18))(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3) + version: 3.2.4(@types/node@25.3.0)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.8.3) ui: dependencies: @@ -849,42 +851,34 @@ 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.3) + version: 7.3.1(@types/node@25.3.0)(jiti@2.7.0)(tsx@4.23.1)(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.3))(vitest@4.0.18) + version: 4.0.18(playwright@1.58.2)(vite@7.3.1(@types/node@25.3.0)(jiti@2.7.0)(tsx@4.23.1)(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.3) + version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.0)(@vitest/browser-playwright@4.0.18)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.8.3) packages: '@agentclientprotocol/sdk@0.14.1': resolution: {integrity: sha512-b6r3PS3Nly+Wyw9U+0nOr47bV8tfS476EgyEMhoKvJCZLbgqoDFN7DJwkxL88RR0aiOqOYV1ZnESHqb+RmdH8w==} peerDependencies: - zod: ^3.25.0 || ^4.0.0 + zod: 4.4.3 - '@anthropic-ai/sdk@0.73.0': - resolution: {integrity: sha512-URURVzhxXGJDGUGFunIOtBlSl7KWvZiAAKY/ttTkZAkXT9bTPqdk2eK0b8qqSxXpikh3QKPnPYpiyX98zf5ebw==} + '@anthropic-ai/sdk@0.91.1': + resolution: {integrity: sha512-LAmu761tSN9r66ixvmciswUj/ZC+1Q4iAfpedTfSVLeswRwnY3n2Nb6Tsk+cLPP28aLOPWeMgIuTuCcMC6W/iw==} hasBin: true peerDependencies: - zod: ^3.25.0 || ^4.0.0 + zod: 4.4.3 peerDependenciesMeta: zod: optional: true - '@apilium/mayros@0.1.1': - resolution: {integrity: sha512-mtW1hseokxhgGsx5fvekJ+HeDngsyIV9iPC3Btk0r6hfzG+UGx5gc0vX5+aM0YShk0bC1k3lbYs6GrpeC6Dyng==} - engines: {node: '>=22.12.0'} - hasBin: true - peerDependencies: - '@napi-rs/canvas': ^0.1.89 - node-llama-cpp: 3.15.1 - '@aws-crypto/crc32@5.2.0': resolution: {integrity: sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==} engines: {node: '>=16.0.0'} @@ -902,130 +896,158 @@ packages: '@aws-crypto/util@5.2.0': resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} - '@aws-sdk/client-bedrock-runtime@3.997.0': - resolution: {integrity: sha512-yEgCc/HvI7dLeXQLCuc4cnbzwE/NbNpKX8NmSSWTy3jnjiMZwrNKdHMBgPoNvaEb0klHhnTyO+JCHVVCPI/eYw==} + '@aws-sdk/client-bedrock-runtime@3.1048.0': + resolution: {integrity: sha512-u+NT61JZEkRFtpL0CAw1N1dwxnaLgwVXQl/zjJxTGgLyS/jTIdg2SdoEoCTHxgDyCnqa1HEi9QOoE9/pYRNpOQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/client-bedrock@3.1085.0': + resolution: {integrity: sha512-unzpjV2M++lBK6QN35vj2t6sQQsUm52i3SmdsZX/kz1kN5qlKaCZIwv3rJmKi8gLMRec9PowBvrRkzw3n72Fsg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/core@3.974.23': + resolution: {integrity: sha512-MiWR/uWjxjFXGzrE0Ghc5lWxUxzHsUWFhV+OX7M4cR9SrmrnZs6TXavnCWnzzdwJeFri34xQo81rvGNzK3c4BQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/core@3.975.1': + resolution: {integrity: sha512-8qh/6EYb7hl/ZwVfQufhbMEZs1gQIc7GbdrIf4eprQJ7cv042+74nE6l3YDfyWNzb9iPXb8fRyYSHkNIk5eE6Q==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-env@3.972.49': + resolution: {integrity: sha512-liB3yQNHCM9k/gu/w36XHMKPluT7HTlnGUhRbBGSISDQkcr/Sy1zsZabiuvQj8WG5yW573u9RehrBvvnIQ9OEQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-env@3.972.57': + resolution: {integrity: sha512-1RfJaF7SW1TOnvNGU7kaYjwUf5H3sfm+synGH1bHhRlqcnxCt3szebH3dmKEyY4tuGcbQ6ffzUT89cRitBV8OQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/client-bedrock@3.1019.0': - resolution: {integrity: sha512-GWe/mqO8CQBmco6M0f9n7DZsItmxTarwvo1zxuVLiqFb8ondXDU+Ry/TNn6FXWHm6YRLm8DUPwaSmcckdGyZNQ==} + '@aws-sdk/credential-provider-http@3.972.51': + resolution: {integrity: sha512-XET0H2oofciJ5lMRWNIvRjAP7Q3wv2XT+JtJJEdhPWUMwe3TvQ9qcxonpu7vXmNngncvFpi4E2It+Tamas/naA==} engines: {node: '>=20.0.0'} - '@aws-sdk/core@3.973.25': - resolution: {integrity: sha512-TNrx7eq6nKNOO62HWPqoBqPLXEkW6nLZQGwjL6lq1jZtigWYbK1NbCnT7mKDzbLMHZfuOECUt3n6CzxjUW9HWQ==} + '@aws-sdk/credential-provider-http@3.972.59': + resolution: {integrity: sha512-sRCkpTiFnCdQvuaRVjQ6SVoHu6i7RUpurVo1c4F81HWhPvUJ7Wdp5MNtSdX1O29CNXc8em3O5m52hCjVtAD9SA==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-env@3.972.23': - resolution: {integrity: sha512-EamaclJcCEaPHp6wiVknNMM2RlsPMjAHSsYSFLNENBM8Wz92QPc6cOn3dif6vPDQt0Oo4IEghDy3NMDCzY/IvA==} + '@aws-sdk/credential-provider-ini@3.972.56': + resolution: {integrity: sha512-IAmc61hbgQiHht9U3x0tnRwz0lzdwOwD/i9voRgdJrKamF+JtmrBOsW9GwB7mfFonNWOWL4qARWYrF8veEMe3w==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-http@3.972.25': - resolution: {integrity: sha512-qPymamdPcLp6ugoVocG1y5r69ScNiRzb0hogX25/ij+Wz7c7WnsgjLTaz7+eB5BfRxeyUwuw5hgULMuwOGOpcw==} + '@aws-sdk/credential-provider-ini@3.973.1': + resolution: {integrity: sha512-6d8H6ZAh3ZPKZ6fe1nG2OWeZEZPtt9ravoD1dezPdPtsSkJRoxGAnFSHwKT3E/Te6fHE30zRzjV6TD12rvF6yQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-ini@3.972.26': - resolution: {integrity: sha512-xKxEAMuP6GYx2y5GET+d3aGEroax3AgGfwBE65EQAUe090lzyJ/RzxPX9s8v7Z6qAk0XwfQl+LrmH05X7YvTeg==} + '@aws-sdk/credential-provider-login@3.972.55': + resolution: {integrity: sha512-hBBkANo3cDn+h2qxxzER4a+J8JCO9o9Z/YYmU7iky6AcaarX5RRdRcHNC6SLdwY0vAXQygn6soUbDqPn3GghaA==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-login@3.972.26': - resolution: {integrity: sha512-EFcM8RM3TUxnZOfMJo++3PnyxFu1fL/huzmn3Vh+8IWRgqZawUD3cRwwOr+/4bE9DpyHaLOWFAjY0lfK5X9ZkQ==} + '@aws-sdk/credential-provider-login@3.972.63': + resolution: {integrity: sha512-GREWRrMj0XnNKMaVa/Mauoaui26qBEHu71WWqXbwZOu/jFQOnPZjTf7u0KtGKC8VGa6VUs9kDWGgocrKNLS9vw==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-node@3.972.27': - resolution: {integrity: sha512-jXpxSolfFnPVj6GCTtx3xIdWNoDR7hYC/0SbetGZxOC9UnNmipHeX1k6spVstf7eWJrMhXNQEgXC0pD1r5tXIg==} + '@aws-sdk/credential-provider-node@3.972.58': + resolution: {integrity: sha512-OyCLVmSI7pZO8hxwNVX6pXhTVlJqRBTp+ijdEfJSUj0RyjHnF602OfAarOzGq6wkGodeFkYBt8MmJ6A6ycRgWw==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-process@3.972.23': - resolution: {integrity: sha512-IL/TFW59++b7MpHserjUblGrdP5UXy5Ekqqx1XQkERXBFJcZr74I7VaSrQT5dxdRMU16xGK4L0RQ5fQG1pMgnA==} + '@aws-sdk/credential-provider-node@3.972.66': + resolution: {integrity: sha512-f+qjRXZpz7sgzbc4QB+6nLKfyKFgRRXzWdXbsKPv/VhVRyHsDyq4yBWC/B75BAJpFIcUeI2XR/3gdWJ677zB4A==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-sso@3.972.26': - resolution: {integrity: sha512-c6ghvRb6gTlMznWhGxn/bpVCcp0HRaz4DobGVD9kI4vwHq186nU2xN/S7QGkm0lo0H2jQU8+dgpUFLxfTcwCOg==} + '@aws-sdk/credential-provider-process@3.972.49': + resolution: {integrity: sha512-C8h36lBuC/RnBSsjlO+dn6xZm3KbAl5vpJaVPAfQnMmz2/OISmKOc8XZcqMQgO2ADwBYNRMM6Kf3vz9G/TulMQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-web-identity@3.972.26': - resolution: {integrity: sha512-cXcS3+XD3iwhoXkM44AmxjmbcKueoLCINr1e+IceMmCySda5ysNIfiGBGe9qn5EMiQ9Jd7pP0AGFtcd6OV3Lvg==} + '@aws-sdk/credential-provider-process@3.972.57': + resolution: {integrity: sha512-TiVQhuU0pbhIZAUZacbPHMyzrIdiH+lnx+PMY/Pu/b93dJrq3wdZwzUJ0TPpvNxaqbHsxJvQZW3/h/beLiKq7Q==} engines: {node: '>=20.0.0'} - '@aws-sdk/eventstream-handler-node@3.972.7': - resolution: {integrity: sha512-p8k2ZWKJVrR3KIcBbI+/+FcWXdwe3LLgGnixsA7w8lDwWjzSVDHFp6uPeSqBt5PQpRxzak9EheJ1xTmOnHGf4g==} + '@aws-sdk/credential-provider-sso@3.972.55': + resolution: {integrity: sha512-1FkOz74Ea5QGS9jtIoXp55T/IkSS3spv+nLTT07fRY/+T5xmEOqaYBVIaEmX4zTNvbV6g2lrtlaVKWEoNyJt3w==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-eventstream@3.972.4': - resolution: {integrity: sha512-0t+2Dn46cRE9iu5ynUXINBtR0wNHi/Jz3FbrqS5k3dGot2O7Ln1xCqXbJUAtGM5ZAqN77SbnpETAgVWC84DeoA==} + '@aws-sdk/credential-provider-sso@3.973.1': + resolution: {integrity: sha512-3foTZUJ4821Ij60X7K3NJroygiZLnbBmarN+T//O2cjkISan90zElN3NBmgSlDrTQ7Gs6z/yO8V7h60QNcDZHQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-host-header@3.972.8': - resolution: {integrity: sha512-wAr2REfKsqoKQ+OkNqvOShnBoh+nkPurDKW7uAeVSu6kUECnWlSJiPvnoqxGlfousEY/v9LfS9sNc46hjSYDIQ==} + '@aws-sdk/credential-provider-web-identity@3.972.55': + resolution: {integrity: sha512-g2BoECD1q01kTPByi56+VLVvdWDzMkKIcr77qixpqH0okw2t0U5CoPv+6S8v/D1Y2Wa6QKKtn6XAtDzP+Kfpvg==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-logger@3.972.8': - resolution: {integrity: sha512-CWl5UCM57WUFaFi5kB7IBY1UmOeLvNZAZ2/OZ5l20ldiJ3TiIz1pC65gYj8X0BCPWkeR1E32mpsCk1L1I4n+lA==} + '@aws-sdk/credential-provider-web-identity@3.972.63': + resolution: {integrity: sha512-8qZLFhM69eKcS37m459ctPR05Qimycm/74OPVioe6wNZabMT54GYhwBju0+J656RkMasNSawWQu+c8CmBe3TUQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-recursion-detection@3.972.9': - resolution: {integrity: sha512-/Wt5+CT8dpTFQxEJ9iGy/UGrXr7p2wlIOEHvIr/YcHYByzoLjrqkYqXdJjd9UIgWjv7eqV2HnFJen93UTuwfTQ==} + '@aws-sdk/eventstream-handler-node@3.972.22': + resolution: {integrity: sha512-tqPJv0dz4+O0hWGm1a6YekcMZyPhDFs/zH73Von7icaVT5n0Jqvm86typ3jRrG+qoUdPhALOnboRLTmnWQTlYQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-user-agent@3.972.26': - resolution: {integrity: sha512-AilFIh4rI/2hKyyGN6XrB0yN96W2o7e7wyrPWCM6QjZM1mcC/pVkW3IWWRvuBWMpVP8Fg+rMpbzeLQ6dTM4gig==} + '@aws-sdk/middleware-eventstream@3.972.18': + resolution: {integrity: sha512-OHpk8YoZi3yexPq8aFt1vN1IxA2zLKvsIR5GpWYylX/ve6kQmY7wxHNSFy/D3t2apMZ16rs76Co4dJWcDyIk3A==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-websocket@3.972.8': - resolution: {integrity: sha512-KPUXz8lRw73Rh12/QkELxiryC9Wi9Ah1xNzFe2Vtbz2/81c2ZA0yM8er+u0iCF/SRMMhDQshLcmRNgn/ueA+gA==} + '@aws-sdk/middleware-websocket@3.972.31': + resolution: {integrity: sha512-ps1rumU1LybSFHaW9dTDgkhCMJLVaedEY78kKSzUDDY+b9974/g6aiaYYA0U9WV0oL4CJCJrVWG+EZ/qr4or7g==} engines: {node: '>= 14.0.0'} - '@aws-sdk/nested-clients@3.996.16': - resolution: {integrity: sha512-L7Qzoj/qQU1cL5GnYLQP5LbI+wlLCLoINvcykR3htKcQ4tzrPf2DOs72x933BM7oArYj1SKrkb2lGlsJHIic3g==} + '@aws-sdk/nested-clients@3.997.23': + resolution: {integrity: sha512-gO93ZPsI2bxeFZD42f1/qjDw6FAZkNZcKRO94LIiT03fzOmcJ9e/tunxjVjA1Rl69ClmVJzz8H3G9CdKef10PA==} engines: {node: '>=20.0.0'} - '@aws-sdk/region-config-resolver@3.972.10': - resolution: {integrity: sha512-1dq9ToC6e070QvnVhhbAs3bb5r6cQ10gTVc6cyRV5uvQe7P138TV2uG2i6+Yok4bAkVAcx5AqkTEBUvWEtBlsQ==} + '@aws-sdk/nested-clients@3.997.31': + resolution: {integrity: sha512-BDHTpwcsZHEBNEJzOg/B1BkFYJxAXY50dau/NyVWs3d51F0WgIUGSWZot/Os+N3KpDhXeaXnz37mWffAvduREw==} engines: {node: '>=20.0.0'} - '@aws-sdk/token-providers@3.1019.0': - resolution: {integrity: sha512-OF+2RfRmUKyjzrRWlDcyju3RBsuqcrYDQ8TwrJg8efcOotMzuZN4U9mpVTIdATpmEc4lWNZBMSjPzrGm6JPnAQ==} + '@aws-sdk/signature-v4-multi-region@3.996.35': + resolution: {integrity: sha512-6L/VWs+Wch2stHemCGTmUNqKLMzURxQDK5boNG3Jn3kAOp71meDUuS5sbObpEvFxHDq0uWeSLFDNSYsjNt+Dlg==} engines: {node: '>=20.0.0'} - '@aws-sdk/token-providers@3.997.0': - resolution: {integrity: sha512-UdG36F7lU9aTqGFRieEyuRUJlgEJBqKeKKekC0esH21DbUSKhPR1kZBah214kYasIaWe1hLJLaqUigoTa5hZAQ==} + '@aws-sdk/signature-v4-multi-region@3.996.39': + resolution: {integrity: sha512-8+srXqYIF8KYMLC4FxMLEM5Ek7kUNibJu1R4m8/fUhhNYIZZz26oGtKkCr8I/HiG2fFQxBvaGgQZT4/mqRCSnA==} engines: {node: '>=20.0.0'} - '@aws-sdk/types@3.973.6': - resolution: {integrity: sha512-Atfcy4E++beKtwJHiDln2Nby8W/mam64opFPTiHEqgsthqeydFS1pY+OUlN1ouNOmf8ArPU/6cDS65anOP3KQw==} + '@aws-sdk/token-providers@3.1048.0': + resolution: {integrity: sha512-k0y/GcuesuSfWyUM0WamrGyeZmltRYaPbHO82UDA6mZ/doB+FOHKutikPAtSXMn/hDz970cF+iRuuiYO9VEbAA==} engines: {node: '>=20.0.0'} - '@aws-sdk/util-endpoints@3.996.5': - resolution: {integrity: sha512-Uh93L5sXFNbyR5sEPMzUU8tJ++Ku97EY4udmC01nB8Zu+xfBPwpIwJ6F7snqQeq8h2pf+8SGN5/NoytfKgYPIw==} + '@aws-sdk/token-providers@3.1074.0': + resolution: {integrity: sha512-pv80IzgGW4RnXWtft692chZOM9i6PhebVsLCcnaM4dBEPZva2fE6FXAHs76G7Rc7s3yGyX/68G0nZMrUy+Vmpg==} engines: {node: '>=20.0.0'} - '@aws-sdk/util-format-url@3.972.4': - resolution: {integrity: sha512-rPm9g4WvgTz4ko5kqseIG5Vp5LUAbWBBDalm4ogHLMc0i20ChwQWqwuTUPJSu8zXn43jIM0xO2KZaYQsFJb+ew==} + '@aws-sdk/token-providers@3.1083.0': + resolution: {integrity: sha512-s0woKnxuHrExLc5L2ArIH5BMkbonHPtt+5hSBM8oknp9M6QTuUmmAmJ2E0EdzCGONrO+8+ADPqvv6UX0nNcc7A==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/token-providers@3.1085.0': + resolution: {integrity: sha512-+QxFprsPw7GTCuRN86OFhDYowgkoT/+Gn0L7feWY1kpIwUoOgzLi24esQ6PgG6pf3tHE/vs5qFmO7QcFMP82TQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/types@3.973.13': + resolution: {integrity: sha512-pEHZqRkAlHfnfAU9tK+WpKv/gBNjGJrHMgA3A0iYRGyswBS2t0pfez+lWlwktb3Bqa0ovh7w/QJTFwp3fDxLNg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/types@3.974.0': + resolution: {integrity: sha512-QIBrw90CDm4O0UaIIzkU6DrFdeJzEb2Va5EPEVpyldj6sHJxB6cshhStJuhZxk3wR3PmjJlYsjPmY1kNb+KGBg==} engines: {node: '>=20.0.0'} '@aws-sdk/util-locate-window@3.965.4': resolution: {integrity: sha512-H1onv5SkgPBK2P6JR2MjGgbOnttoNzSPIRoeZTNPZYyaplwGg50zS3amXvXqF0/qfXpWEC9rLWU564QTB9bSog==} engines: {node: '>=20.0.0'} - '@aws-sdk/util-user-agent-browser@3.972.8': - resolution: {integrity: sha512-B3KGXJviV2u6Cdw2SDY2aDhoJkVfY/Q/Trwk2CMSkikE1Oi6gRzxhvhIfiRpHfmIsAhV4EA54TVEX8K6CbHbkA==} - - '@aws-sdk/util-user-agent-node@3.973.12': - resolution: {integrity: sha512-8phW0TS8ntENJgDcFewYT/Q8dOmarpvSxEjATu2GUBAutiHr++oEGCiBUwxslCMNvwW2cAPZNT53S/ym8zm/gg==} + '@aws-sdk/xml-builder@3.972.31': + resolution: {integrity: sha512-SzE4Pgyl+hDF+BuyuzxUSpwnuUu9lJuO1YGgteG89/4Qv0+2IQiVQqdbPV32IozLvXWQChPQcdkk/sKvb1QHiQ==} engines: {node: '>=20.0.0'} - peerDependencies: - aws-crt: '>=1.0.0' - peerDependenciesMeta: - aws-crt: - optional: true - '@aws-sdk/xml-builder@3.972.16': - resolution: {integrity: sha512-iu2pyvaqmeatIJLURLqx9D+4jKAdTH20ntzB6BFwjyN7V960r4jK32mx0Zf7YbtOYAbmbtQfDNuL60ONinyw7A==} + '@aws-sdk/xml-builder@3.972.34': + resolution: {integrity: sha512-wHhWL1y7sN3enBA8POrPpQM5jCcmu2ozyhbRei4c8OjVcEaEs6yLucLa/pla457ggS/ysuy7bosagz3HaJkZXA==} engines: {node: '>=20.0.0'} '@aws/lambda-invoke-store@0.2.4': resolution: {integrity: sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ==} engines: {node: '>=18.0.0'} + '@aws/lambda-invoke-store@0.3.0': + resolution: {integrity: sha512-sl4Bm6yiMNYrZKkqqDFWN0UfnWhlS8ivKxrYl+6t0gCLrqr8y3B2IqZZbFRkfaVVp7C/baApyh71P+LeE1A2sQ==} + engines: {node: '>=18.0.0'} + '@azure/abort-controller@2.1.2': resolution: {integrity: sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==} engines: {node: '>=18.0.0'} @@ -1108,11 +1130,13 @@ packages: '@cacheable/utils@2.3.4': resolution: {integrity: sha512-knwKUJEYgIfwShABS1BX6JyJJTglAFcEU7EXqzTdiGCXur4voqkiJkdgZIQtWNFhynzDWERcTYv/sETMu3uJWA==} - '@clack/core@1.0.1': - resolution: {integrity: sha512-WKeyK3NOBwDOzagPR5H08rFk9D/WuN705yEbuZvKqlkmoLM2woKtXb10OO2k1NoSU4SFG947i2/SCYh+2u5e4g==} + '@clack/core@1.4.3': + resolution: {integrity: sha512-/kr3UWNtdJfxZtPgDqUOmG2pvwlmcLGheex5yiZKdwbzZJxhV+HMNR9QNmyY5cGwTNV6LrR7Jtp+KjhUAP1qBQ==} + engines: {node: '>= 20.12.0'} - '@clack/prompts@1.0.1': - resolution: {integrity: sha512-/42G73JkuYdyWZ6m8d/CJtBrGl1Hegyc7Fy78m5Ob+jF85TOUmLR5XLce/U3LxYAw0kJ8CT5aI99RIvPHcGp/Q==} + '@clack/prompts@1.7.0': + resolution: {integrity: sha512-y7/yvZ2TPAnR9+jnc00klvNNLkJiXFFrQA/hlLCcxA9a2A4zQIOimyFQ9XfwYKiGD1fb5GY8vbKIIgO8d5Tb2A==} + engines: {node: '>= 20.12.0'} '@cloudflare/workers-types@4.20260120.0': resolution: {integrity: sha512-B8pueG+a5S+mdK3z8oKu1ShcxloZ7qWb68IEyLLaepvdryIbNC7JVPcY0bWsjS56UQVKc5fnyRge3yZIwc9bxw==} @@ -1164,6 +1188,24 @@ packages: resolution: {integrity: sha512-UyX6rGEXzVyPzb1yvjHtPfTlnLvB5jX/stAMdiytHhfoydX+98hfympdOwsnTktzr+IRvphxTbdErgYDJkEsvw==} engines: {node: '>=22.12.0'} + '@earendil-works/pi-agent-core@0.80.6': + resolution: {integrity: sha512-Lvn89ko42h5ETUb6Z0Ku6ldskEqXaTdQBYvSa0+7bdG9V6rUEpXptv5e0OVZ1HDcvi8s6/2lGCQWsxKX+DFHNw==} + engines: {node: '>=22.19.0'} + + '@earendil-works/pi-ai@0.80.6': + resolution: {integrity: sha512-7xfLk8sANBp+bpPEbjoOZTbPxsa+++b1JXAoSJsNa3vbs9AHHEclmvg54XLQcxH+fuwaeti/g2jeIfJ+mVYLpA==} + engines: {node: '>=22.19.0'} + hasBin: true + + '@earendil-works/pi-coding-agent@0.80.6': + resolution: {integrity: sha512-vcfD6tOk402isLl3Cm/qbn2O10TvgroMp1+/fEGM24ZdvETFCdOYv5VZ7m59EI5fPsjfSJh+CpQ5bhBrhfOg7g==} + engines: {node: '>=22.19.0'} + hasBin: true + + '@earendil-works/pi-tui@0.80.6': + resolution: {integrity: sha512-bSuzS4EVSqEPj/Qr/p9eqCESfKsGuDNbl77EGci8Iaqqt/C/XCBZL1MjXaxSWW1NsT5afjp/Cb0NTPzOLv/aPA==} + engines: {node: '>=22.19.0'} + '@emnapi/core@1.8.1': resolution: {integrity: sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==} @@ -1185,6 +1227,12 @@ packages: cpu: [ppc64] os: [aix] + '@esbuild/aix-ppc64@0.28.1': + resolution: {integrity: sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + '@esbuild/android-arm64@0.25.12': resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} engines: {node: '>=18'} @@ -1197,6 +1245,12 @@ packages: cpu: [arm64] os: [android] + '@esbuild/android-arm64@0.28.1': + resolution: {integrity: sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + '@esbuild/android-arm@0.25.12': resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} engines: {node: '>=18'} @@ -1209,6 +1263,12 @@ packages: cpu: [arm] os: [android] + '@esbuild/android-arm@0.28.1': + resolution: {integrity: sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + '@esbuild/android-x64@0.25.12': resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} engines: {node: '>=18'} @@ -1221,6 +1281,12 @@ packages: cpu: [x64] os: [android] + '@esbuild/android-x64@0.28.1': + resolution: {integrity: sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + '@esbuild/darwin-arm64@0.25.12': resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} engines: {node: '>=18'} @@ -1233,6 +1299,12 @@ packages: cpu: [arm64] os: [darwin] + '@esbuild/darwin-arm64@0.28.1': + resolution: {integrity: sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + '@esbuild/darwin-x64@0.25.12': resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} engines: {node: '>=18'} @@ -1245,6 +1317,12 @@ packages: cpu: [x64] os: [darwin] + '@esbuild/darwin-x64@0.28.1': + resolution: {integrity: sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + '@esbuild/freebsd-arm64@0.25.12': resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} engines: {node: '>=18'} @@ -1257,6 +1335,12 @@ packages: cpu: [arm64] os: [freebsd] + '@esbuild/freebsd-arm64@0.28.1': + resolution: {integrity: sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + '@esbuild/freebsd-x64@0.25.12': resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} engines: {node: '>=18'} @@ -1269,6 +1353,12 @@ packages: cpu: [x64] os: [freebsd] + '@esbuild/freebsd-x64@0.28.1': + resolution: {integrity: sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + '@esbuild/linux-arm64@0.25.12': resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} engines: {node: '>=18'} @@ -1281,6 +1371,12 @@ packages: cpu: [arm64] os: [linux] + '@esbuild/linux-arm64@0.28.1': + resolution: {integrity: sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + '@esbuild/linux-arm@0.25.12': resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} engines: {node: '>=18'} @@ -1293,6 +1389,12 @@ packages: cpu: [arm] os: [linux] + '@esbuild/linux-arm@0.28.1': + resolution: {integrity: sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + '@esbuild/linux-ia32@0.25.12': resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} engines: {node: '>=18'} @@ -1305,6 +1407,12 @@ packages: cpu: [ia32] os: [linux] + '@esbuild/linux-ia32@0.28.1': + resolution: {integrity: sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + '@esbuild/linux-loong64@0.25.12': resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} engines: {node: '>=18'} @@ -1317,6 +1425,12 @@ packages: cpu: [loong64] os: [linux] + '@esbuild/linux-loong64@0.28.1': + resolution: {integrity: sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + '@esbuild/linux-mips64el@0.25.12': resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} engines: {node: '>=18'} @@ -1329,6 +1443,12 @@ packages: cpu: [mips64el] os: [linux] + '@esbuild/linux-mips64el@0.28.1': + resolution: {integrity: sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + '@esbuild/linux-ppc64@0.25.12': resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} engines: {node: '>=18'} @@ -1341,6 +1461,12 @@ packages: cpu: [ppc64] os: [linux] + '@esbuild/linux-ppc64@0.28.1': + resolution: {integrity: sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + '@esbuild/linux-riscv64@0.25.12': resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} engines: {node: '>=18'} @@ -1353,6 +1479,12 @@ packages: cpu: [riscv64] os: [linux] + '@esbuild/linux-riscv64@0.28.1': + resolution: {integrity: sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + '@esbuild/linux-s390x@0.25.12': resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} engines: {node: '>=18'} @@ -1365,6 +1497,12 @@ packages: cpu: [s390x] os: [linux] + '@esbuild/linux-s390x@0.28.1': + resolution: {integrity: sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + '@esbuild/linux-x64@0.25.12': resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} engines: {node: '>=18'} @@ -1377,6 +1515,12 @@ packages: cpu: [x64] os: [linux] + '@esbuild/linux-x64@0.28.1': + resolution: {integrity: sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + '@esbuild/netbsd-arm64@0.25.12': resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} engines: {node: '>=18'} @@ -1389,6 +1533,12 @@ packages: cpu: [arm64] os: [netbsd] + '@esbuild/netbsd-arm64@0.28.1': + resolution: {integrity: sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + '@esbuild/netbsd-x64@0.25.12': resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} engines: {node: '>=18'} @@ -1401,6 +1551,12 @@ packages: cpu: [x64] os: [netbsd] + '@esbuild/netbsd-x64@0.28.1': + resolution: {integrity: sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + '@esbuild/openbsd-arm64@0.25.12': resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} engines: {node: '>=18'} @@ -1413,6 +1569,12 @@ packages: cpu: [arm64] os: [openbsd] + '@esbuild/openbsd-arm64@0.28.1': + resolution: {integrity: sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + '@esbuild/openbsd-x64@0.25.12': resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} engines: {node: '>=18'} @@ -1425,6 +1587,12 @@ packages: cpu: [x64] os: [openbsd] + '@esbuild/openbsd-x64@0.28.1': + resolution: {integrity: sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + '@esbuild/openharmony-arm64@0.25.12': resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} engines: {node: '>=18'} @@ -1437,6 +1605,12 @@ packages: cpu: [arm64] os: [openharmony] + '@esbuild/openharmony-arm64@0.28.1': + resolution: {integrity: sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + '@esbuild/sunos-x64@0.25.12': resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} engines: {node: '>=18'} @@ -1449,6 +1623,12 @@ packages: cpu: [x64] os: [sunos] + '@esbuild/sunos-x64@0.28.1': + resolution: {integrity: sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + '@esbuild/win32-arm64@0.25.12': resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} engines: {node: '>=18'} @@ -1461,6 +1641,12 @@ packages: cpu: [arm64] os: [win32] + '@esbuild/win32-arm64@0.28.1': + resolution: {integrity: sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + '@esbuild/win32-ia32@0.25.12': resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} engines: {node: '>=18'} @@ -1473,6 +1659,12 @@ packages: cpu: [ia32] os: [win32] + '@esbuild/win32-ia32@0.28.1': + resolution: {integrity: sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + '@esbuild/win32-x64@0.25.12': resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} engines: {node: '>=18'} @@ -1485,8 +1677,14 @@ packages: cpu: [x64] os: [win32] - '@google/genai@1.42.0': - resolution: {integrity: sha512-+3nlMTcrQufbQ8IumGkOphxD5Pd5kKyJOzLcnY0/1IuE8upJk5aLmoexZ2BJhBp1zAjRJMEB4a2CJwKI9e2EYw==} + '@esbuild/win32-x64@0.28.1': + resolution: {integrity: sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@google/genai@1.52.0': + resolution: {integrity: sha512-gwSvbpiN/17O9TbsqSsE/OzZcpv5Fo4RQjdngGgogtuB9RsyJ8ZHhX5KjHj1bp5N9snN2eK8LDGXSaWW2hof8Q==} engines: {node: '>=20.0.0'} peerDependencies: '@modelcontextprotocol/sdk': ^1.25.2 @@ -1506,8 +1704,8 @@ packages: peerDependencies: grammy: ^1.0.0 - '@grammyjs/types@3.24.0': - resolution: {integrity: sha512-qQIEs4lN5WqUdr4aT8MeU6UFpMbGYAvcvYSW1A4OO1PABGJQHz/KLON6qvpf+5RxaNDQBxiY2k2otIhg/AG7RQ==} + '@grammyjs/types@3.28.0': + resolution: {integrity: sha512-4JvXCdxRZHCje0M4gHzLwtB4bLno3WD28xd8CNfk4POWIu73BFnSvGeW6OQ5gPem4eYTEwkD9yDaXssixl6tMQ==} '@grpc/grpc-js@1.14.3': resolution: {integrity: sha512-Iq8QQQ/7X3Sac15oB6p0FmUg/klxQvXLeileoqrTRGJYLV+/9tubbr9ipz0GKHjmXVsgFPo/+W+2cA8eNcR+XA==} @@ -1524,8 +1722,8 @@ packages: '@hapi/hoek@9.3.0': resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==} - '@homebridge/ciao@1.3.5': - resolution: {integrity: sha512-f7MAw7YuoEYgJEQ1VyRcLHGuVmCpmXi65GVR8CAtPWPqIZf/HFr4vHzVpOfQMpEQw9Pt5uh07guuLt5HE8ruog==} + '@homebridge/ciao@1.3.10': + resolution: {integrity: sha512-ogAVdPZ9Fo3kSaULpq+acxbtbIvMtd2TDYEkqHIsqvQ9QHJnRNcjm8NQzegaBzJ3gUK1mVX07UWs2/B0NmabSw==} hasBin: true '@hono/node-server@1.19.10': @@ -1831,91 +2029,69 @@ packages: '@lydell/node-pty@1.2.0-beta.3': resolution: {integrity: sha512-ngGAItlRhmJXrhspxt8kX13n1dVFqzETOq0m/+gqSkO8NJBvNMwP7FZckMwps2UFySdr4yxCXNGu/bumg5at6A==} - '@mariozechner/clipboard-darwin-arm64@0.3.2': - resolution: {integrity: sha512-uBf6K7Je1ihsgvmWxA8UCGCeI+nbRVRXoarZdLjl6slz94Zs1tNKFZqx7aCI5O1i3e0B6ja82zZ06BWrl0MCVw==} + '@mariozechner/clipboard-darwin-arm64@0.3.9': + resolution: {integrity: sha512-BfgV7vCEWZwJwZJw03r6bP5+tf0iI/ANuQYCxi9RNn7FrWB3yzGuMKCrNLRl6V761vXRdL8+OqZ0wd4TqlsNOQ==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@mariozechner/clipboard-darwin-universal@0.3.2': - resolution: {integrity: sha512-mxSheKTW2U9LsBdXy0SdmdCAE5HqNS9QUmpNHLnfJ+SsbFKALjEZc5oRrVMXxGQSirDvYf5bjmRyT0QYYonnlg==} + '@mariozechner/clipboard-darwin-universal@0.3.9': + resolution: {integrity: sha512-BGGR4iA9Z2shAjI65eI5xtyb3LYNlDW9X3gxKxDbqtbnREohsrqznov6zpKoIrsRWpzlYVEdKphS7ksJ0/ndSQ==} engines: {node: '>= 10'} os: [darwin] - '@mariozechner/clipboard-darwin-x64@0.3.2': - resolution: {integrity: sha512-U1BcVEoidvwIp95+HJswSW+xr28EQiHR7rZjH6pn8Sja5yO4Yoe3yCN0Zm8Lo72BbSOK/fTSq0je7CJpaPCspg==} + '@mariozechner/clipboard-darwin-x64@0.3.9': + resolution: {integrity: sha512-4kURmCbS6nt8uYhtmWpUcJWyPHfmAr5dTpXD1nO3pIfa+TSQ9DbrGOYCKH+aEFW47XhQ4Vp8ZTszie+wfFvDKg==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@mariozechner/clipboard-linux-arm64-gnu@0.3.2': - resolution: {integrity: sha512-BsinwG3yWTIjdgNCxsFlip7LkfwPk+ruw/aFCXHUg/fb5XC/Ksp+YMQ7u0LUtiKzIv/7LMXgZInJQH6gxbAaqQ==} + '@mariozechner/clipboard-linux-arm64-gnu@0.3.9': + resolution: {integrity: sha512-g59OkUGP2DDfCOIKypHeYgv2M55u/cKvXa5dSxFbEJ34XvIQMdcVmpKCkGUro3ZgefXiGVdwguvTMQGpHWzIXw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@mariozechner/clipboard-linux-arm64-musl@0.3.2': - resolution: {integrity: sha512-0/Gi5Xq2V6goXBop19ePoHvXsmJD9SzFlO3S+d6+T2b+BlPcpOu3Oa0wTjl+cZrLAAEzA86aPNBI+VVAFDFPKw==} + '@mariozechner/clipboard-linux-arm64-musl@0.3.9': + resolution: {integrity: sha512-AGuJdgKsmJdm4Pych7kv3sqe591ERRaAHW3xjLooiFzn8J+PxUyof++7YZrB5Y5tpnTO+K18Og3taj2NpluCRQ==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@mariozechner/clipboard-linux-riscv64-gnu@0.3.2': - resolution: {integrity: sha512-2AFFiXB24qf0zOZsxI1GJGb9wQGlOJyN6UwoXqmKS3dpQi/l6ix30IzDDA4c4ZcCcx4D+9HLYXhC1w7Sov8pXA==} + '@mariozechner/clipboard-linux-riscv64-gnu@0.3.9': + resolution: {integrity: sha512-DXBEAiuMpk7dhS1a9NzNxVAFi1vaKoPu7rQNgY8LIDLGrK3lnIp3nT10DUum+PKVJoJppIP+NAA8IZe4DMNDPw==} engines: {node: '>= 10'} cpu: [riscv64] os: [linux] - '@mariozechner/clipboard-linux-x64-gnu@0.3.2': - resolution: {integrity: sha512-v6fVnsn7WMGg73Dab8QMwyFce7tzGfgEixKgzLP8f1GJqkJZi5zO4k4FOHzSgUufgLil63gnxvMpjWkgfeQN7A==} + '@mariozechner/clipboard-linux-x64-gnu@0.3.9': + resolution: {integrity: sha512-WORrMLd6EpElEME7JRKfSaY34nW1P5LbdgK5YNCS1ncG2LqmITsSMEJ8nh2mpvxb3TxqbOOKgY7k9eMJYlW9Mw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@mariozechner/clipboard-linux-x64-musl@0.3.2': - resolution: {integrity: sha512-xVUtnoMQ8v2JVyfJLKKXACA6avdnchdbBkTsZs8BgJQo29qwCp5NIHAUO8gbJ40iaEGToW5RlmVk2M9V0HsHEw==} + '@mariozechner/clipboard-linux-x64-musl@0.3.9': + resolution: {integrity: sha512-/DHn+1DrfL6oRaPPWXaOKvonFFrni666fxd+zFqiQEfvBH0tsHVWjq9iqBk0oDp0qaPA72lIMy5BptxISBEhZQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@mariozechner/clipboard-win32-arm64-msvc@0.3.2': - resolution: {integrity: sha512-AEgg95TNi8TGgak2wSXZkXKCvAUTjWoU1Pqb0ON7JHrX78p616XUFNTJohtIon3e0w6k0pYPZeCuqRCza/Tqeg==} + '@mariozechner/clipboard-win32-arm64-msvc@0.3.9': + resolution: {integrity: sha512-O5FHD3ErkMwMhNzAfu3ggy0ug4z7btZuoQgwwxlzPrwV2bxlD6WDpqBY4NCgICAgZdDKdp+loUEKVAVt8aYnhQ==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@mariozechner/clipboard-win32-x64-msvc@0.3.2': - resolution: {integrity: sha512-tGRuYpZwDOD7HBrCpyRuhGnHHSCknELvqwKKUG4JSfSB7JIU7LKRh6zx6fMUOQd8uISK35TjFg5UcNih+vJhFA==} + '@mariozechner/clipboard-win32-x64-msvc@0.3.9': + resolution: {integrity: sha512-ihQC3EufqEY81vhXBgVBtK4prL+wc62zJsSvxrgz7K1hsdt6OObz6v9p3Rn1OG3GJksTTKMJF0u/guMISHPhSA==} engines: {node: '>= 10'} cpu: [x64] os: [win32] - '@mariozechner/clipboard@0.3.2': - resolution: {integrity: sha512-IHQpksNjo7EAtGuHFU+tbWDp5LarH3HU/8WiB9O70ZEoBPHOg0/6afwSLK0QyNMMmx4Bpi/zl6+DcBXe95nWYA==} + '@mariozechner/clipboard@0.3.9': + resolution: {integrity: sha512-ABnA53mdfkGZwOFUdZNv2S0CWGO/EIuPj8Vv9xmBFmSYg/qFc7ihO6q5FcQjvoE67kZpWkEc4AhD6B/os04yuA==} engines: {node: '>= 10'} - '@mariozechner/jiti@2.6.5': - resolution: {integrity: sha512-faGUlTcXka5l7rv0lP3K3vGW/ejRuOS24RR2aSFWREUQqzjgdsuWNo/IiPqL3kWRGt6Ahl2+qcDAwtdeWeuGUw==} - hasBin: true - - '@mariozechner/pi-agent-core@0.54.0': - resolution: {integrity: sha512-LsPoudpOJLj7JjSpjlAdLM5uA2iy8nP+4nA6Si1ASD3tMqXdjHzNaKNloGSODKJO+3O3yhwPMSbuk78CCnZteQ==} - engines: {node: '>=20.0.0'} - - '@mariozechner/pi-ai@0.54.0': - resolution: {integrity: sha512-XHhMIbFFHCa4mbiYdttfhVg6r3VmFD5tAiW4tjnmf33FhLUCRd76bGMQRc4kLWXPKCi/U4nqAErvaGiZUY4B8A==} - engines: {node: '>=20.0.0'} - hasBin: true - - '@mariozechner/pi-coding-agent@0.54.0': - resolution: {integrity: sha512-CO8uLmigLzzep2i5/f05dchyywDYDsqykLxpaMXbwDa/dDzsBRbuWoGQBOAsiGbcCMya6AT5nAggFFo4Aqy/+g==} - engines: {node: '>=20.0.0'} - hasBin: true - - '@mariozechner/pi-tui@0.54.0': - resolution: {integrity: sha512-bvFlUohdxDvKcFeQM2xsd5twCGKWxVaYSlHCFljIW0KqMC4vU+/Ts4A1i9iDnm6Xe/MlueKvC0V09YeC8fLIHA==} - engines: {node: '>=20.0.0'} - '@matrix-org/matrix-sdk-crypto-nodejs@0.4.0': resolution: {integrity: sha512-+qqgpn39XFSbsD0dFjssGO9vHEP7sTyfs8yTpt8vuqWpUpF20QMwpCZi0jpYw7GxjErNTsMshopuo8677DfGEA==} engines: {node: '>= 22'} @@ -1928,8 +2104,13 @@ packages: resolution: {integrity: sha512-570oJr93l1RcCNNaMVpOm+PgQkRgno/F65nH1aCWLIKLnw0o7iPoj+8Z5b7mnLMidg9lldVSCcf0dBxqTGE1/w==} engines: {node: '>=20.0.0'} - '@mistralai/mistralai@1.10.0': - resolution: {integrity: sha512-tdIgWs4Le8vpvPiUEWne6tK0qbVc+jMenujnvTqOjogrJUsCSQhus0tHTU1avDDh5//Rq2dFgP9mWRAdIEoBqg==} + '@mistralai/mistralai@2.2.6': + resolution: {integrity: sha512-W8pX7zHxjJvMIpw8JMxeJEleapXX0Q9NPszdNzqkM3MIEoIGPObdodujj+WHteXEvGfaP/AMwlNyRfEzSY6dQQ==} + peerDependencies: + '@opentelemetry/api': ^1.9.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true '@mozilla/readability@0.6.0': resolution: {integrity: sha512-juG5VWh4qAivzTAeMzvY9xs9HY5rAcr2E4I7tiSSCokRFi7XIZCAu92ZkSTsIj1OPceCifL3cpfteP3pDT9/QQ==} @@ -1941,140 +2122,70 @@ 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==} @@ -2086,6 +2197,10 @@ packages: resolution: {integrity: sha512-vs1Az2OOTBiP4q0pwjW5aF0xp9n4MxVrmkFBxc6EKZc6ddYx5gaZiAsZoq0uRRXWbi3AT/sBqn05eRPtn1JCPw==} engines: {node: '>= 20.19.0'} + '@noble/curves@2.2.0': + resolution: {integrity: sha512-T/BoHgFXirb0ENSPBquzX0rcjXeM6Lo892a2jlYJkqk83LqZx0l1Of7DzlKJ6jkpvMrkHSnAcgb5JegL8SeIkQ==} + engines: {node: '>= 20.19.0'} + '@noble/ed25519@3.0.0': resolution: {integrity: sha512-QyteqMNm0GLqfa5SoYbSC3+Pvykwpn95Zgth4MFVSMKBB75ELl9tX1LAVsN4c3HXOrakHsF2gL4zWDAYCcsnzg==} @@ -2093,6 +2208,10 @@ packages: resolution: {integrity: sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw==} engines: {node: '>= 20.19.0'} + '@noble/hashes@2.2.0': + resolution: {integrity: sha512-IYqDGiTXab6FniAgnSdZwgWbomxpy9FtYvLKs7wCUs2a8RkITG+DFGO1DM9cr+E3/RgADRpFjrKVaJ1z6sjtEg==} + engines: {node: '>= 20.19.0'} + '@node-llama-cpp/linux-arm64@3.17.1': resolution: {integrity: sha512-QK8opgd/AnGHvSQwIDXukrMNa760PLOxcu4OrxJn/CsHxiKucch0L4bDfu17XvlKiMoK41lXa6CKFm1kjVaGIQ==} engines: {node: '>=20.0.0'} @@ -2351,6 +2470,10 @@ packages: resolution: {integrity: sha512-R5R9tb2AXs2IRLNKLBJDynhkfmx7mX0vi8NkhZb3gUkPWHn6HXk5J8iQ/dql0U3ApfWym4kXXmBDRGO+oeOfjg==} engines: {node: '>=14'} + '@opentelemetry/semantic-conventions@1.41.1': + resolution: {integrity: sha512-/UhIkaZgPutTFmQ7RnIJGgDXZmtEJ7Dvi86xNTFWcnRxVRNk/aotsqDJYeEvDP+FSMB2SdW+pQzNMcWP0rwuNA==} + engines: {node: '>=14'} + '@oxc-project/types@0.112.0': resolution: {integrity: sha512-m6RebKHIRsax2iCwVpYW2ErQwa4ywHJrE4sCK3/8JK8ZZAWOKXaRJFl/uP51gaVyyXlaS4+chU1nSCdzYf6QqQ==} @@ -2498,116 +2621,116 @@ packages: cpu: [x64] os: [win32] - '@oxlint/binding-android-arm-eabi@1.50.0': - resolution: {integrity: sha512-G7MRGk/6NCe+L8ntonRdZP7IkBfEpiZ/he3buLK6JkLgMHgJShXZ+BeOwADmspXez7U7F7L1Anf4xLSkLHiGTg==} + '@oxlint/binding-android-arm-eabi@1.73.0': + resolution: {integrity: sha512-HZQRN/UMBu+Ut+/9MiAChkbP4qZqrNOWBcNI45vOT40GVhbGR0JgHB87L48D4iAqFQIdVmeQYtV9RF89AjTKkg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@oxlint/binding-android-arm64@1.50.0': - resolution: {integrity: sha512-GeSuMoJWCVpovJi/e3xDSNgjeR8WEZ6MCXL6EtPiCIM2NTzv7LbflARINTXTJy2oFBYyvdf/l2PwHzYo6EdXvg==} + '@oxlint/binding-android-arm64@1.73.0': + resolution: {integrity: sha512-Gp+KJRylv2aW7thRpG5p1KTxZq4ZJFbWowrKzufNq9d3ssl3r3JviYV45/+p+7CN1Nv0zDd1e8Ex0b/HUDq4TQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@oxlint/binding-darwin-arm64@1.50.0': - resolution: {integrity: sha512-w3SY5YtxGnxCHPJ8Twl3KmS9oja1gERYk3AMoZ7Hv8P43ZtB6HVfs02TxvarxfL214Tm3uzvc2vn+DhtUNeKnw==} + '@oxlint/binding-darwin-arm64@1.73.0': + resolution: {integrity: sha512-3de96NdtXhxERMjIz7wsp2HYMY6pMQycGxFWac2mFecAx6VeARF/IqFb1QIaqiCRIdfzBwzTed+pCTCoiS+CYA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@oxlint/binding-darwin-x64@1.50.0': - resolution: {integrity: sha512-hNfogDqy7tvmllXKBSlHo6k5x7dhTUVOHbMSE15CCAcXzmqf5883aPvBYPOq9AE7DpDUQUZ1kVE22YbiGW+tuw==} + '@oxlint/binding-darwin-x64@1.73.0': + resolution: {integrity: sha512-5zx/uPW32TiaOeVY1dQ/H5iOf0K1HOdFKOJhLqGl4o63+i1fpzoqqu/mKtd7OFgFjNCdhlyTGgjVkQTZm1ELcg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@oxlint/binding-freebsd-x64@1.50.0': - resolution: {integrity: sha512-ykZevOWEyu0nsxolA911ucxpEv0ahw8jfEeGWOwwb/VPoE4xoexuTOAiPNlWZNJqANlJl7yp8OyzCtXTUAxotw==} + '@oxlint/binding-freebsd-x64@1.73.0': + resolution: {integrity: sha512-qNe4gKHaGnLuZJ8toUg90JAa0S2vTVvDw+0bRi3q1avXZXDT4u5mMeECf3nD4HYrbdn1O7dXqWut4onY/yx/Xg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@oxlint/binding-linux-arm-gnueabihf@1.50.0': - resolution: {integrity: sha512-hif3iDk7vo5GGJ4OLCCZAf2vjnU9FztGw4L0MbQL0M2iY9LKFtDMMiQAHmkF0PQGQMVbTYtPdXCLKVgdkiqWXQ==} + '@oxlint/binding-linux-arm-gnueabihf@1.73.0': + resolution: {integrity: sha512-cCehYh5hTbfShm/fxTD6wwrGUWIpvX+N5OxmAMhFhDeTGXvw+BeNj889tpxsFQ9ZLatQ6wImuY8tsKLZ+FMz7w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxlint/binding-linux-arm-musleabihf@1.50.0': - resolution: {integrity: sha512-dVp9iSssiGAnTNey2Ruf6xUaQhdnvcFOJyRWd/mu5o2jVbFK15E5fbWGeFRfmuobu5QXuROtFga44+7DOS3PLg==} + '@oxlint/binding-linux-arm-musleabihf@1.73.0': + resolution: {integrity: sha512-d5j5GDU/2dMgjVhw7TQT9ITrsIr1Y02KEXKyVGIXUkD+KiaxE9TP65FS2ZdgTBemQvoRL+gSBdbrIm3cQIeacg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxlint/binding-linux-arm64-gnu@1.50.0': - resolution: {integrity: sha512-1cT7yz2HA910CKA9NkH1ZJo50vTtmND2fkoW1oyiSb0j6WvNtJ0Wx2zoySfXWc/c+7HFoqRK5AbEoL41LOn9oA==} + '@oxlint/binding-linux-arm64-gnu@1.73.0': + resolution: {integrity: sha512-Eyf1SrP3+yR1DI3OJgOY2Pvrr9dWP9TK37xPaDYycwTtlGlI45erJAVIfH5/m/xosDt6BupJYEFi47bvbTuuyw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] - '@oxlint/binding-linux-arm64-musl@1.50.0': - resolution: {integrity: sha512-++B3k/HEPFVlj89cOz8kWfQccMZB/aWL9AhsW7jPIkG++63Mpwb2cE9XOEsd0PATbIan78k2Gky+09uWM1d/gQ==} + '@oxlint/binding-linux-arm64-musl@1.73.0': + resolution: {integrity: sha512-IlT/OJApEDKaMmCooHuncgJZbbCe7T5QIWmTZBEtYscWvzPQuuEinVcid6kwQRVQOUdb7PUCz4jQHnaYXdfJXw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] - '@oxlint/binding-linux-ppc64-gnu@1.50.0': - resolution: {integrity: sha512-Z9b/KpFMkx66w3gVBqjIC1AJBTZAGoI9+U+K5L4QM0CB/G0JSNC1es9b3Y0Vcrlvtdn8A+IQTkYjd/Q0uCSaZw==} + '@oxlint/binding-linux-ppc64-gnu@1.73.0': + resolution: {integrity: sha512-L+JYcb/vdg5fmcH08V6o0YYLU28cTH1SPNulwJdvK9NK49aXSkYy6oNpKBmddArVOXYqNepriDGiZ04G54kh1Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] - '@oxlint/binding-linux-riscv64-gnu@1.50.0': - resolution: {integrity: sha512-jvmuIw8wRSohsQlFNIST5uUwkEtEJmOQYr33bf/K2FrFPXHhM4KqGekI3ShYJemFS/gARVacQFgBzzJKCAyJjg==} + '@oxlint/binding-linux-riscv64-gnu@1.73.0': + resolution: {integrity: sha512-Qtk0g3bKV6OwWjIm7R8kQN1uOZRKQt/MODK2a8QfkwhTpXBD53ozx5XLVWLGDQAVyp2otLW4D2wB98XfAfMPGA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] - '@oxlint/binding-linux-riscv64-musl@1.50.0': - resolution: {integrity: sha512-x+UrN47oYNh90nmAAyql8eQaaRpHbDPu5guasDg10+OpszUQ3/1+1J6zFMmV4xfIEgTcUXG/oI5fxJhF4eWCNA==} + '@oxlint/binding-linux-riscv64-musl@1.73.0': + resolution: {integrity: sha512-wX0NQKZVxltkAOVmzFcpOaMpdaUvsq1Eqpx9tkAfl71UdkTlSo1R4AdAnGccR1Fm2+TzFgZ22CyyGuZ41RDr/A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] - '@oxlint/binding-linux-s390x-gnu@1.50.0': - resolution: {integrity: sha512-i/JLi2ljLUIVfekMj4ISmdt+Hn11wzYUdRRrkVUYsCWw7zAy5xV7X9iA+KMyM156LTFympa7s3oKBjuCLoTAUQ==} + '@oxlint/binding-linux-s390x-gnu@1.73.0': + resolution: {integrity: sha512-vPe7UGBMWyiLTtnqS4xxgMQFSFGmtQwhwCxuiw6lXygaO6bVt0D8dFVg8Xv05eaiN3ybC0HXXHUAohFMFvqoCQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] - '@oxlint/binding-linux-x64-gnu@1.50.0': - resolution: {integrity: sha512-/C7brhn6c6UUPccgSPCcpLQXcp+xKIW/3sji/5VZ8/OItL3tQ2U7KalHz887UxxSQeEOmd1kY6lrpuwFnmNqOA==} + '@oxlint/binding-linux-x64-gnu@1.73.0': + resolution: {integrity: sha512-2CwIWr9cemFC/CbRBWZvuk5mffz6ObmfFkfcC/9rTQ7f+icNhYr2kOjf9Rt8lLvugvkdGDOmkoVoFFHh6ClCTw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] - '@oxlint/binding-linux-x64-musl@1.50.0': - resolution: {integrity: sha512-oDR1f+bGOYU8LfgtEW8XtotWGB63ghtcxk5Jm6IDTCk++rTA/IRMsjOid2iMd+1bW+nP9Mdsmcdc7VbPD3+iyQ==} + '@oxlint/binding-linux-x64-musl@1.73.0': + resolution: {integrity: sha512-nDadfJgg7NBBxG0N560wOe7LLX5QiYp6qBaI7viuk5EUORFBktU/NfV0MbTqU3gTqQDCh4VyxKdo5VADxk9w8Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] - '@oxlint/binding-openharmony-arm64@1.50.0': - resolution: {integrity: sha512-4CmRGPp5UpvXyu4jjP9Tey/SrXDQLRvZXm4pb4vdZBxAzbFZkCyh0KyRy4txld/kZKTJlW4TO8N1JKrNEk+mWw==} + '@oxlint/binding-openharmony-arm64@1.73.0': + resolution: {integrity: sha512-wGjJC+NLH9xP+IKGn9RDW94ojJR/wPbg5WCnQjj/oReaOtCQthr8ws1zICe77JFmo4ouUdeTHHZL/ESGiF6Pmw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@oxlint/binding-win32-arm64-msvc@1.50.0': - resolution: {integrity: sha512-Fq0M6vsGcFsSfeuWAACDhd5KJrO85ckbEfe1EGuBj+KPyJz7KeWte2fSFrFGmNKNXyhEMyx4tbgxiWRujBM2KQ==} + '@oxlint/binding-win32-arm64-msvc@1.73.0': + resolution: {integrity: sha512-I7X47GPGljw225YUQ5SbC/rb1Kkdrd0yQf0x+hYxeKS6DpfjMbo9ccQPQ6LNY6BoJQ1sHhgDUGuMn5Vg5gHT6w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@oxlint/binding-win32-ia32-msvc@1.50.0': - resolution: {integrity: sha512-qTdWR9KwY/vxJGhHVIZG2eBOhidOQvOwzDxnX+jhW/zIVacal1nAhR8GLkiywW8BIFDkQKXo/zOfT+/DY+ns/w==} + '@oxlint/binding-win32-ia32-msvc@1.73.0': + resolution: {integrity: sha512-5lWj+3h+74Fm1jYOO9qkJA4xkAlZA099DkXppuXsk7UpnpZLttsefrZU469vChGaG6hcSqrkKXQOvMTZtbjeNg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@oxlint/binding-win32-x64-msvc@1.50.0': - resolution: {integrity: sha512-682t7npLC4G2Ca+iNlI9fhAKTcFPYYXJjwoa88H4q+u5HHHlsnL/gHULapX3iqp+A8FIJbgdylL5KMYo2LaluQ==} + '@oxlint/binding-win32-x64-msvc@1.73.0': + resolution: {integrity: sha512-WaNRvh4f6zY9CvUQk2YoA1O90ieWrIklI84+HXFr9Isjz9CSESrdqo/RtIYt4Dll/cAchqGDMehfaZd0vqEFZw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] @@ -2631,12 +2754,21 @@ packages: '@protobufjs/codegen@2.0.4': resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==} + '@protobufjs/codegen@2.0.5': + resolution: {integrity: sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==} + '@protobufjs/eventemitter@1.1.0': resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==} + '@protobufjs/eventemitter@1.1.1': + resolution: {integrity: sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==} + '@protobufjs/fetch@1.1.0': resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==} + '@protobufjs/fetch@1.1.1': + resolution: {integrity: sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==} + '@protobufjs/float@1.0.2': resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} @@ -2652,6 +2784,9 @@ packages: '@protobufjs/utf8@1.1.0': resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} + '@protobufjs/utf8@1.1.1': + resolution: {integrity: sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg==} + '@quansync/fs@1.0.0': resolution: {integrity: sha512-4TJ3DFtlf1L5LDMaM6CanJ/0lckGNtJcMjQ1NAV6zDmA0tEHKZtxNKin8EgPaVX1YzljbxckyT2tJrpQKAtngQ==} @@ -2930,232 +3065,96 @@ packages: '@sinclair/typebox@0.34.48': resolution: {integrity: sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==} - '@slack/bolt@4.6.0': - resolution: {integrity: sha512-xPgfUs2+OXSugz54Ky07pA890+Qydk22SYToi8uGpXeHSt1JWwFJkRyd/9Vlg5I1AdfdpGXExDpwnbuN9Q/2dQ==} + '@slack/bolt@4.7.3': + resolution: {integrity: sha512-bODs8q/yNDWUPoxmQhFrRqLMA5vhB/PDizYWqb6CkQhLWEUo5JFtfJcmeU4ElGl6qSt++OKjSYNa4MPc77CleQ==} engines: {node: '>=18', npm: '>=8.6.0'} peerDependencies: '@types/express': ^5.0.0 - '@slack/logger@4.0.0': - resolution: {integrity: sha512-Wz7QYfPAlG/DR+DfABddUZeNgoeY7d1J39OCR2jR+v7VBsB8ezulDK5szTnDDPDwLH5IWhLvXIHlCFZV7MSKgA==} + '@slack/logger@4.0.1': + resolution: {integrity: sha512-6cmdPrV/RYfd2U0mDGiMK8S7OJqpCTm7enMLRR3edccsPX8j7zXTLnaEF4fhxxJJTAIOil6+qZrnUPTuaLvwrQ==} engines: {node: '>= 18', npm: '>= 8.6.0'} - '@slack/oauth@3.0.4': - resolution: {integrity: sha512-+8H0g7mbrHndEUbYCP7uYyBCbwqmm3E6Mo3nfsDvZZW74zKk1ochfH/fWSvGInYNCVvaBUbg3RZBbTp0j8yJCg==} + '@slack/oauth@3.0.5': + resolution: {integrity: sha512-exqFQySKhNDptWYSWhvRUJ4/+ndu2gayIy7vg/JfmJq3wGtGdHk531P96fAZyBm5c1Le3yaPYqv92rL4COlU3A==} engines: {node: '>=18', npm: '>=8.6.0'} - '@slack/socket-mode@2.0.5': - resolution: {integrity: sha512-VaapvmrAifeFLAFaDPfGhEwwunTKsI6bQhYzxRXw7BSujZUae5sANO76WqlVsLXuhVtCVrBWPiS2snAQR2RHJQ==} + '@slack/socket-mode@2.0.7': + resolution: {integrity: sha512-qYy07je71WnEHgRwmw12DlAnZLi5HXmdlI2WUzUK2LH/rYXQpP6uEg462S5CwfE8FoCKUdIigHtYnOOfzZH1lQ==} engines: {node: '>= 18', npm: '>= 8.6.0'} - '@slack/types@2.20.0': - resolution: {integrity: sha512-PVF6P6nxzDMrzPC8fSCsnwaI+kF8YfEpxf3MqXmdyjyWTYsZQURpkK7WWUWvP5QpH55pB7zyYL9Qem/xSgc5VA==} + '@slack/types@2.22.0': + resolution: {integrity: sha512-sZ9lIgJhPX2qft/tKWiklFlc0o1FWeI7QtciZJfW1+ErH1eGGHvOZ8e73sleTCFEFJp1q/R0WeS8Oa7AsiDprg==} engines: {node: '>= 12.13.0', npm: '>= 6.12.0'} - '@slack/web-api@7.14.1': - resolution: {integrity: sha512-RoygyteJeFswxDPJjUMESn9dldWVMD2xUcHHd9DenVavSfVC6FeVnSdDerOO7m8LLvw4Q132nQM4hX8JiF7dng==} + '@slack/web-api@7.19.0': + resolution: {integrity: sha512-ItjyjEZml+LDH8CjcCLRLJHh7VZtevPKExrRN3l5KWyBliyDnGAeoO4Y+K+fFBmRpKLYVPgqWMX4THldv2HVtA==} engines: {node: '>= 18', npm: '>= 8.6.0'} - '@smithy/abort-controller@4.2.12': - resolution: {integrity: sha512-xolrFw6b+2iYGl6EcOL7IJY71vvyZ0DJ3mcKtpykqPe2uscwtzDZJa1uVQXyP7w9Dd+kGwYnPbMsJrGISKiY/Q==} - engines: {node: '>=18.0.0'} - - '@smithy/config-resolver@4.4.13': - resolution: {integrity: sha512-iIzMC5NmOUP6WL6o8iPBjFhUhBZ9pPjpUpQYWMUFQqKyXXzOftbfK8zcQCz/jFV1Psmf05BK5ypx4K2r4Tnwdg==} - engines: {node: '>=18.0.0'} - - '@smithy/core@3.23.12': - resolution: {integrity: sha512-o9VycsYNtgC+Dy3I0yrwCqv9CWicDnke0L7EVOrZtJpjb2t0EjaEofmMrYc0T1Kn3yk32zm6cspxF9u9Bj7e5w==} - engines: {node: '>=18.0.0'} - - '@smithy/credential-provider-imds@4.2.12': - resolution: {integrity: sha512-cr2lR792vNZcYMriSIj+Um3x9KWrjcu98kn234xA6reOAFMmbRpQMOv8KPgEmLLtx3eldU6c5wALKFqNOhugmg==} - engines: {node: '>=18.0.0'} - - '@smithy/eventstream-codec@4.2.10': - resolution: {integrity: sha512-A4ynrsFFfSXUHicfTcRehytppFBcY3HQxEGYiyGktPIOye3Ot7fxpiy4VR42WmtGI4Wfo6OXt/c1Ky1nUFxYYQ==} - engines: {node: '>=18.0.0'} - - '@smithy/eventstream-serde-browser@4.2.10': - resolution: {integrity: sha512-0xupsu9yj9oDVuQ50YCTS9nuSYhGlrwqdaKQel9y2Fz7LU9fNErVlw9N0o4pm4qqvWEGbSTI4HKc6XJfB30MVw==} - engines: {node: '>=18.0.0'} - - '@smithy/eventstream-serde-config-resolver@4.3.10': - resolution: {integrity: sha512-8kn6sinrduk0yaYHMJDsNuiFpXwQwibR7n/4CDUqn4UgaG+SeBHu5jHGFdU9BLFAM7Q4/gvr9RYxBHz9/jKrhA==} + '@smithy/core@3.26.0': + resolution: {integrity: sha512-mLUktFAn+Pa2agl1J7VgtYNFWCX8/b4GMJSK1hCu4YCvtBfM6F8Os3EP4ry+DFFlXOf3wyvlgXhuUdFoy52D3g==} engines: {node: '>=18.0.0'} - '@smithy/eventstream-serde-node@4.2.10': - resolution: {integrity: sha512-uUrxPGgIffnYfvIOUmBM5i+USdEBRTdh7mLPttjphgtooxQ8CtdO1p6K5+Q4BBAZvKlvtJ9jWyrWpBJYzBKsyQ==} + '@smithy/core@3.29.3': + resolution: {integrity: sha512-L+Ys6ecjk5vwPMAKHBpPKlJ3DkqwNcnfEISXBZIsVvWG/XKXfsAP8mwIYlTeLcd2ElHdesPI8OuOmJSFAPhm6A==} engines: {node: '>=18.0.0'} - '@smithy/eventstream-serde-universal@4.2.10': - resolution: {integrity: sha512-aArqzOEvcs2dK+xQVCgLbpJQGfZihw8SD4ymhkwNTtwKbnrzdhJsFDKuMQnam2kF69WzgJYOU5eJlCx+CA32bw==} + '@smithy/credential-provider-imds@4.4.2': + resolution: {integrity: sha512-18UMDMyrAbDcpmL1gLUA7ww0fRTcdCrSjSJOi2Sbld+tVjwD/pW+OAwjlScFLR7vvBnhZrIPQ7kVuTf1mnJLug==} engines: {node: '>=18.0.0'} - '@smithy/fetch-http-handler@5.3.15': - resolution: {integrity: sha512-T4jFU5N/yiIfrtrsb9uOQn7RdELdM/7HbyLNr6uO/mpkj1ctiVs7CihVr51w4LyQlXWDpXFn4BElf1WmQvZu/A==} + '@smithy/credential-provider-imds@4.4.8': + resolution: {integrity: sha512-q9J7JTiXrAhB8sDp4px97uEPT7CwKH61Co78grdNQvU8QZAdiuaSRhP0tUVf2ogy36RZTrlMU1rBmDEH+cnkiA==} engines: {node: '>=18.0.0'} - '@smithy/hash-node@4.2.12': - resolution: {integrity: sha512-QhBYbGrbxTkZ43QoTPrK72DoYviDeg6YKDrHTMJbbC+A0sml3kSjzFtXP7BtbyJnXojLfTQldGdUR0RGD8dA3w==} + '@smithy/fetch-http-handler@5.5.2': + resolution: {integrity: sha512-Ei/UK/QMhq0rKaMqGPlOAkE2yS9DZeYmZdk1RAKc3vp3zxgleZHZyBLlZv8yLsxljX4svCRuMTD6u3LLIcU4Bg==} engines: {node: '>=18.0.0'} - '@smithy/invalid-dependency@4.2.12': - resolution: {integrity: sha512-/4F1zb7Z8LOu1PalTdESFHR0RbPwHd3FcaG1sI3UEIriQTWakysgJr65lc1jj6QY5ye7aFsisajotH6UhWfm/g==} + '@smithy/fetch-http-handler@5.6.5': + resolution: {integrity: sha512-SuqeisTyPoiIPtIYru/sGxGyXzmZ+8nnFOhC+qRPglt06Ebd1yH//CDltZB2J/3WBNVhwfUaZ0EtHB3cm2X32g==} 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.2': - resolution: {integrity: sha512-n6rQ4N8Jj4YTQO3YFrlgZuwKodf4zUFs7EJIWH86pSCWBaAtAGBFfCM7Wx6D2bBJ2xqFNxGBSrUWswT3M0VJow==} + '@smithy/node-http-handler@4.7.3': + resolution: {integrity: sha512-/jPhevcTFPMVl6KNjbaI47iOg1zxC7IsnX4PQDGVZKMFceOXtB8IEYaB7a9VvkP/3oC60WzTeKocvSI7vLT0vA==} engines: {node: '>=18.0.0'} - '@smithy/middleware-content-length@4.2.12': - resolution: {integrity: sha512-YE58Yz+cvFInWI/wOTrB+DbvUVz/pLn5mC5MvOV4fdRUc6qGwygyngcucRQjAhiCEbmfLOXX0gntSIcgMvAjmA==} + '@smithy/node-http-handler@4.8.2': + resolution: {integrity: sha512-wfl1uwrAqMH9/pi4kqBo5LBcFwrJLxuDLqL7p7qNcJIFcyZDUc6pzhYk4CYv+DP7fIUpQCZumwNnkhPKS52osQ==} engines: {node: '>=18.0.0'} - '@smithy/middleware-endpoint@4.4.27': - resolution: {integrity: sha512-T3TFfUgXQlpcg+UdzcAISdZpj4Z+XECZ/cefgA6wLBd6V4lRi0svN2hBouN/be9dXQ31X4sLWz3fAQDf+nt6BA==} + '@smithy/node-http-handler@4.9.5': + resolution: {integrity: sha512-bNqdxTQTxmLbomSmlkZFz8L6B/feQ2HHzw4L2zY7Ecp2XffYAZq2uzdWDdxJHJFbEvqd+SRuluJso0P8+xPdbw==} engines: {node: '>=18.0.0'} - '@smithy/middleware-retry@4.4.44': - resolution: {integrity: sha512-Y1Rav7m5CFRPQyM4CI0koD/bXjyjJu3EQxZZhtLGD88WIrBrQ7kqXM96ncd6rYnojwOo/u9MXu57JrEvu/nLrA==} + '@smithy/signature-v4@5.5.2': + resolution: {integrity: sha512-7xHpmPY4rt0IOmeAA8EfjgEH8isT+587TCdy9H6a7d4OMi5CQ0oEHhWllunvPu4j4Cq0vTFwdxXN/kABWPjdyA==} engines: {node: '>=18.0.0'} - '@smithy/middleware-serde@4.2.15': - resolution: {integrity: sha512-ExYhcltZSli0pgAKOpQQe1DLFBLryeZ22605y/YS+mQpdNWekum9Ujb/jMKfJKgjtz1AZldtwA/wCYuKJgjjlg==} + '@smithy/signature-v4@5.6.4': + resolution: {integrity: sha512-B89bpf2t/y/wia6LZ+4JfHXYQT9PnVftsH05rgJKKIStS7r/4XSs9HOjtPoLtgcA6HCW9jVqX5DBbq7E0PAkiQ==} engines: {node: '>=18.0.0'} - '@smithy/middleware-stack@4.2.12': - resolution: {integrity: sha512-kruC5gRHwsCOuyCd4ouQxYjgRAym2uDlCvQ5acuMtRrcdfg7mFBg6blaxcJ09STpt3ziEkis6bhg1uwrWU7txw==} + '@smithy/types@4.15.0': + resolution: {integrity: sha512-Z5TAOxygoFvybJV3igo5SloFflSokHx2hu1eFA+DxDTcn+FtKxUSui+rbTRG1pAafMA888Z3MVvCWUuvCrTXjg==} engines: {node: '>=18.0.0'} - '@smithy/node-config-provider@4.3.12': - resolution: {integrity: sha512-tr2oKX2xMcO+rBOjobSwVAkV05SIfUKz8iI53rzxEmgW3GOOPOv0UioSDk+J8OpRQnpnhsO3Af6IEBabQBVmiw==} - engines: {node: '>=18.0.0'} - - '@smithy/node-http-handler@4.5.0': - resolution: {integrity: sha512-Rnq9vQWiR1+/I6NZZMNzJHV6pZYyEHt2ZnuV3MG8z2NNenC4i/8Kzttz7CjZiHSmsN5frhXhg17z3Zqjjhmz1A==} - engines: {node: '>=18.0.0'} - - '@smithy/property-provider@4.2.12': - resolution: {integrity: sha512-jqve46eYU1v7pZ5BM+fmkbq3DerkSluPr5EhvOcHxygxzD05ByDRppRwRPPpFrsFo5yDtCYLKu+kreHKVrvc7A==} - engines: {node: '>=18.0.0'} - - '@smithy/protocol-http@5.3.12': - resolution: {integrity: sha512-fit0GZK9I1xoRlR4jXmbLhoN0OdEpa96ul8M65XdmXnxXkuMxM0Y8HDT0Fh0Xb4I85MBvBClOzgSrV1X2s1Hxw==} - engines: {node: '>=18.0.0'} - - '@smithy/querystring-builder@4.2.12': - resolution: {integrity: sha512-6wTZjGABQufekycfDGMEB84BgtdOE/rCVTov+EDXQ8NHKTUNIp/j27IliwP7tjIU9LR+sSzyGBOXjeEtVgzCHg==} - engines: {node: '>=18.0.0'} - - '@smithy/querystring-parser@4.2.12': - resolution: {integrity: sha512-P2OdvrgiAKpkPNKlKUtWbNZKB1XjPxM086NeVhK+W+wI46pIKdWBe5QyXvhUm3MEcyS/rkLvY8rZzyUdmyDZBw==} - engines: {node: '>=18.0.0'} - - '@smithy/service-error-classification@4.2.12': - resolution: {integrity: sha512-LlP29oSQN0Tw0b6D0Xo6BIikBswuIiGYbRACy5ujw/JgWSzTdYj46U83ssf6Ux0GyNJVivs2uReU8pt7Eu9okQ==} - engines: {node: '>=18.0.0'} - - '@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.12': - resolution: {integrity: sha512-B/FBwO3MVOL00DaRSXfXfa/TRXRheagt/q5A2NM13u7q+sHS59EOVGQNfG7DkmVtdQm5m3vOosoKAXSqn/OEgw==} - engines: {node: '>=18.0.0'} - - '@smithy/smithy-client@4.12.7': - resolution: {integrity: sha512-q3gqnwml60G44FECaEEsdQMplYhDMZYCtYhMCzadCnRnnHIobZJjegmdoUo6ieLQlPUzvrMdIJUpx6DoPmzANQ==} - engines: {node: '>=18.0.0'} - - '@smithy/types@4.13.1': - resolution: {integrity: sha512-787F3yzE2UiJIQ+wYW1CVg2odHjmaWLGksnKQHUrK/lYZSEcy1msuLVvxaR/sI2/aDe9U+TBuLsXnr3vod1g0g==} - engines: {node: '>=18.0.0'} - - '@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.2': - resolution: {integrity: sha512-XRH6b0H/5A3SgblmMa5ErXQ2XKhfbQB+Fm/oyLZ2O2kCUrwgg55bU0RekmzAhuwOjA9qdN5VU2BprOvGGUkOOQ==} - engines: {node: '>=18.0.0'} - - '@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.3': - resolution: {integrity: sha512-ZkJGvqBzMHVHE7r/hcuCxlTY8pQr1kMtdsVPs7ex4mMU+EAbcXppfo5NmyxMYi2XU49eqaz56j2gsk4dHHPG/g==} + '@smithy/types@4.16.1': + resolution: {integrity: sha512-0JFs3V2y2M9tKW5na/qxe69Zv+uxLMO7QBbhxF/FHu/Gp2NFZAAL9tWl9PU02xxo07pb3G9FTyjNc6D5uZrJIg==} 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.2': - resolution: {integrity: sha512-FDXD7cvUoFWwN6vtQfEta540Y/YBe5JneK3SoZg9bThSoOAC/eGeYEua6RkBgKjGa/sz6Y+DuBZj3+YEY21y4Q==} - engines: {node: '>=18.0.0'} - - '@smithy/util-config-provider@4.2.2': - resolution: {integrity: sha512-dWU03V3XUprJwaUIFVv4iOnS1FC9HnMHDfUrlNDSh4315v0cWyaIErP8KiqGVbf5z+JupoVpNM7ZB3jFiTejvQ==} - engines: {node: '>=18.0.0'} - - '@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.47': - resolution: {integrity: sha512-qSRbYp1EQ7th+sPFuVcVO05AE0QH635hycdEXlpzIahqHHf2Fyd/Zl+8v0XYMJ3cgDVPa0lkMefU7oNUjAP+DQ==} - engines: {node: '>=18.0.0'} - - '@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-hex-encoding@4.2.2': - resolution: {integrity: sha512-Qcz3W5vuHK4sLQdyT93k/rfrUwdJ8/HZ+nMUOyGdpeGA1Wxt65zYwi3oEl9kOM+RswvYq90fzkNDahPS8K0OIg==} - engines: {node: '>=18.0.0'} - - '@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-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.2': - resolution: {integrity: sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw==} - engines: {node: '>=18.0.0'} - - '@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==} @@ -3173,9 +3172,6 @@ packages: '@tokenizer/token@0.3.0': resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} - '@tootallnate/quickjs-emscripten@0.23.0': - resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==} - '@twurple/api-call@8.0.3': resolution: {integrity: sha512-/5DBTqFjpYB+qqOkkFzoTWE79a7+I8uLXmBIIIYjGoq/CIPxKcHnlemXlU8cQhTr87PVa3th8zJXGYiNkpRx8w==} @@ -3249,27 +3245,18 @@ packages: '@types/linkify-it@5.0.0': resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==} - '@types/long@4.0.2': - resolution: {integrity: sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==} - '@types/markdown-it@14.1.2': resolution: {integrity: sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==} '@types/mdurl@2.0.0': resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==} - '@types/mime-types@2.1.4': - resolution: {integrity: sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w==} - '@types/mime@1.3.5': resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} '@types/ms@2.1.0': resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} - '@types/node@10.17.60': - resolution: {integrity: sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==} - '@types/node@20.19.37': resolution: {integrity: sha512-8kzdPJ3FsNsVIurqBs7oodNnCEVbni9yUEkaHbgptDACOPW04jimGagZ51E6+lXUwJjgnBw+hyko/lkFWCldqw==} @@ -3383,11 +3370,11 @@ packages: peerDependencies: vitest: 4.0.18 - '@vitest/coverage-v8@4.0.18': - resolution: {integrity: sha512-7i+N2i0+ME+2JFZhfuz7Tg/FqKtilHjGyGvoHYQ6iLV0zahbsJ9sljC9OcFcPDbhYKCet+sG8SsVqlyGvPflZg==} + '@vitest/coverage-v8@4.1.10': + resolution: {integrity: sha512-IM49HmthevbgAO4anp1hwtoT9wYe59w0LR00gr+eagHE+ZJ5lK4sLPeO0ubgoJcwLk6dehU3R24N+FbEEKDc8g==} peerDependencies: - '@vitest/browser': 4.0.18 - vitest: 4.0.18 + '@vitest/browser': 4.1.10 + vitest: 4.1.10 peerDependenciesMeta: '@vitest/browser': optional: true @@ -3398,6 +3385,9 @@ packages: '@vitest/expect@4.0.18': resolution: {integrity: sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ==} + '@vitest/expect@4.1.10': + resolution: {integrity: sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==} + '@vitest/mocker@3.2.4': resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==} peerDependencies: @@ -3420,36 +3410,62 @@ packages: vite: optional: true + '@vitest/mocker@4.1.10': + resolution: {integrity: sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==} + peerDependencies: + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + '@vitest/pretty-format@3.2.4': resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} '@vitest/pretty-format@4.0.18': resolution: {integrity: sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw==} + '@vitest/pretty-format@4.1.10': + resolution: {integrity: sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==} + '@vitest/runner@3.2.4': resolution: {integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==} '@vitest/runner@4.0.18': resolution: {integrity: sha512-rpk9y12PGa22Jg6g5M3UVVnTS7+zycIGk9ZNGN+m6tZHKQb7jrP7/77WfZy13Y/EUDd52NDsLRQhYKtv7XfPQw==} + '@vitest/runner@4.1.10': + resolution: {integrity: sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==} + '@vitest/snapshot@3.2.4': resolution: {integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==} '@vitest/snapshot@4.0.18': resolution: {integrity: sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA==} + '@vitest/snapshot@4.1.10': + resolution: {integrity: sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==} + '@vitest/spy@3.2.4': resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} '@vitest/spy@4.0.18': resolution: {integrity: sha512-cbQt3PTSD7P2OARdVW3qWER5EGq7PHlvE+QfzSC0lbwO+xnt7+XH06ZzFjFRgzUX//JmpxrCu92VdwvEPlWSNw==} + '@vitest/spy@4.1.10': + resolution: {integrity: sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==} + '@vitest/utils@3.2.4': resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} '@vitest/utils@4.0.18': resolution: {integrity: sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA==} + '@vitest/utils@4.1.10': + resolution: {integrity: sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==} + '@whiskeysockets/baileys@7.0.0-rc.9': resolution: {integrity: sha512-YFm5gKXfDP9byCXCW3OPHKXLzrAKzolzgVUlRosHHgwbnf2YOO3XknkMm6J7+F0ns8OA0uuSBhgkRHTDtqkacw==} engines: {node: '>=20.0.0'} @@ -3466,10 +3482,6 @@ packages: link-preview-js: optional: true - '@whiskeysockets/libsignal-node@https://codeload.github.com/whiskeysockets/libsignal-node/tar.gz/1c30d7d7e76a3b0aa120b04dc6a26f5a12dccf67': - resolution: {tarball: https://codeload.github.com/whiskeysockets/libsignal-node/tar.gz/1c30d7d7e76a3b0aa120b04dc6a26f5a12dccf67} - version: 2.0.1 - abort-controller@3.0.0: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} engines: {node: '>=6.5'} @@ -3492,20 +3504,16 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + agent-base@6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + agent-base@7.1.4: resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} engines: {node: '>= 14'} - ajv-formats@3.0.1: - resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} - peerDependencies: - ajv: ^8.0.0 - peerDependenciesMeta: - ajv: - optional: true - - ajv@8.18.0: - resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} + ajv@8.20.0: + resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} another-json@0.2.0: resolution: {integrity: sha512-/Ndrl68UQLhnCdsAzEXLMFuOR546o2qbYRqCglaNHbjXrwG1ayTcdwr3zkSGOGtGXDyR5X9nCFfnyG2AFJIsqg==} @@ -3570,12 +3578,8 @@ packages: resolution: {integrity: sha512-trmleAnZ2PxN/loHWVhhx1qeOHSRXq4TDsBBxq3GqeJitfk3+jTQ+v/C1km/KYq9M7wKqCewMh+/NAvVH7m+bw==} engines: {node: '>=20.19.0'} - ast-types@0.13.4: - resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==} - engines: {node: '>=4'} - - ast-v8-to-istanbul@0.3.11: - resolution: {integrity: sha512-Qya9fkoofMjCBNVdWINMjB5KZvkYfaO9/anwkWnjxibpWUxo5iHl2sOdP7/uAqaRuUYuoo8rDwnbaaKVFxoUvw==} + ast-v8-to-istanbul@1.0.4: + resolution: {integrity: sha512-0bC0/4bTSrnwdhU3IsZDwEdojvuPrSg59OYZfKsLRtJZ0u8VBx9DebfqqG8bRdCC0I7vjgxmPi41P0lpkhJHtA==} async-lock@1.4.1: resolution: {integrity: sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==} @@ -3602,6 +3606,9 @@ packages: axios@1.13.5: resolution: {integrity: sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==} + axios@1.18.1: + resolution: {integrity: sha512-3nTvFlvpn9Zu/RkHUqtc7/+al4UpRW5az71ap5zccp6e8RAYEzhMTecX8Dz1wWDYrPpUoB1HAQEGEAEvUr7S9g==} + balanced-match@4.0.4: resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} engines: {node: 18 || 20 || >=22} @@ -3613,10 +3620,6 @@ packages: resolution: {integrity: sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==} engines: {node: '>= 0.8'} - basic-ftp@5.2.0: - resolution: {integrity: sha512-VoMINM2rqJwJgfdHq6RiUudKt2BV+FY5ZFezP/ypmwayk68+NzzAQy4XXLlqsGD4MCzq3DrmNFD/uUmBJuGoXw==} - engines: {node: '>=10.0.0'} - bcrypt-pbkdf@1.0.2: resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==} @@ -3795,6 +3798,9 @@ packages: resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} engines: {node: '>= 0.6'} + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + cookie-signature@1.0.7: resolution: {integrity: sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==} @@ -3841,10 +3847,6 @@ packages: resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} engines: {node: '>= 12'} - data-uri-to-buffer@6.0.2: - resolution: {integrity: sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==} - engines: {node: '>= 14'} - debug@2.6.9: resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} peerDependencies: @@ -3877,10 +3879,6 @@ packages: defu@6.1.4: resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} - degenerator@5.0.1: - resolution: {integrity: sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==} - engines: {node: '>= 14'} - delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} @@ -3897,8 +3895,8 @@ packages: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} - diff@8.0.3: - resolution: {integrity: sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==} + diff@8.0.4: + resolution: {integrity: sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==} engines: {node: '>=0.3.1'} discord-api-types@0.38.37: @@ -3924,8 +3922,8 @@ packages: domutils@3.2.2: resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} - dotenv@17.3.1: - resolution: {integrity: sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==} + dotenv@17.4.2: + resolution: {integrity: sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==} engines: {node: '>=12'} dts-resolver@2.1.3: @@ -3993,6 +3991,9 @@ packages: es-module-lexer@1.7.0: resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + es-module-lexer@2.3.1: + resolution: {integrity: sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==} + es-object-atoms@1.1.1: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} @@ -4011,6 +4012,11 @@ packages: engines: {node: '>=18'} hasBin: true + esbuild@0.28.1: + resolution: {integrity: sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==} + engines: {node: '>=18'} + hasBin: true + escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} @@ -4022,27 +4028,9 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} - escodegen@2.1.0: - resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} - engines: {node: '>=6.0'} - hasBin: true - - esprima@4.0.1: - resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} - engines: {node: '>=4'} - hasBin: true - - estraverse@5.3.0: - resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} - engines: {node: '>=4.0'} - estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} - esutils@2.0.3: - resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} - engines: {node: '>=0.10.0'} - etag@1.8.1: resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} engines: {node: '>= 0.6'} @@ -4083,15 +4071,17 @@ packages: resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} engines: {node: '>=8.6.0'} + fast-string-truncated-width@3.0.3: + resolution: {integrity: sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==} + + fast-string-width@3.0.2: + resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==} + fast-uri@3.1.0: resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} - 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 + fast-wrap-ansi@0.2.2: + resolution: {integrity: sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==} fastq@1.20.1: resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} @@ -4149,6 +4139,15 @@ packages: debug: optional: true + follow-redirects@1.16.0: + resolution: {integrity: sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + foreground-child@3.3.1: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} @@ -4210,6 +4209,10 @@ packages: resolution: {integrity: sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==} engines: {node: '>=18'} + get-east-asian-width@1.6.0: + resolution: {integrity: sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==} + engines: {node: '>=18'} + get-intrinsic@1.3.0: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} @@ -4221,10 +4224,6 @@ packages: get-tsconfig@4.13.6: resolution: {integrity: sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==} - get-uri@6.0.5: - resolution: {integrity: sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==} - engines: {node: '>= 14'} - getpass@0.1.7: resolution: {integrity: sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==} @@ -4259,8 +4258,8 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - grammy@1.40.0: - resolution: {integrity: sha512-ssuE7fc1AwqlUxHr931OCVW3fU+oFDjHZGgvIedPKXfTdjXvzP19xifvVGCnPtYVUig1Kz+gwxe4A9M5WdkT4Q==} + grammy@1.44.0: + resolution: {integrity: sha512-gGVykS5+c5f1tPV97LuU6IDRMawE2NzpwM9pNz58HQ35IZXDnYL3VOLvNzYognPSeBIOSzQXRu5w96V0aY8y8A==} engines: {node: ^12.20.0 || >=14.13.1} has-flag@4.0.0: @@ -4303,8 +4302,8 @@ packages: hookified@1.15.1: resolution: {integrity: sha512-MvG/clsADq1GPM2KGo2nyfaWVyn9naPiXrqIe4jYjXNZQt238kWyOGrsyc/DmRAQ+Re6yeo6yX/yoNCG5KAEVg==} - hosted-git-info@9.0.2: - resolution: {integrity: sha512-M422h7o/BR3rmCQ8UHi7cyyMqKltdP9Uo+J2fXK+RSAY+wTcKOIRyhTuKv4qn+DJf3g+PL890AzId5KZpX+CBg==} + hosted-git-info@9.0.3: + resolution: {integrity: sha512-Hc+ghLoSt6QaYZUv0WBiIvmMDZuZZ7oaDvdH8MbfOO4lOsxdXLEvuC6ePoGs9H1X9oCLyq6+NVN0MKqD+ydxyg==} engines: {node: ^20.17.0 || >=22.9.0} html-escaper@2.0.2: @@ -4338,6 +4337,10 @@ packages: resolution: {integrity: sha512-G5akfn7eKbpDN+8nPS/cb57YeA1jLTVxjpCj7tmm3QKPdyDy7T+qSC40e9ptydSWvkwjSXw1VbkpyEm39ukeAg==} engines: {node: '>=0.10'} + https-proxy-agent@5.0.1: + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} + https-proxy-agent@7.0.6: resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} engines: {node: '>= 14'} @@ -4373,10 +4376,6 @@ packages: ini@1.3.8: resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} - ip-address@10.1.0: - resolution: {integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==} - engines: {node: '>= 12'} - ipaddr.js@1.9.1: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} @@ -4465,8 +4464,8 @@ packages: jackspeak@3.4.3: resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} - jiti@2.6.1: - resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} + jiti@2.7.0: + resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} hasBin: true jose@4.15.9: @@ -4542,12 +4541,13 @@ packages: resolution: {integrity: sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==} engines: {node: '>= 8'} - koffi@2.15.1: - resolution: {integrity: sha512-mnc0C0crx/xMSljb5s9QbnLrlFHprioFO1hkXyuSuO/QtbpLDa0l/uM21944UfQunMKmp3/r789DTDxVyyH6aA==} - leac@0.6.0: resolution: {integrity: sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==} + libsignal@https://codeload.github.com/whiskeysockets/libsignal-node/tar.gz/bcea72df9ec34d9d9140ab30619cf479c7c144c7: + resolution: {tarball: https://codeload.github.com/whiskeysockets/libsignal-node/tar.gz/bcea72df9ec34d9d9140ab30619cf479c7c144c7} + version: 6.0.0 + lie@3.3.0: resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} @@ -4572,6 +4572,9 @@ packages: linkify-it@5.0.0: resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} + linkify-it@5.0.2: + resolution: {integrity: sha512-ONTm2jCMAVZjgQa/Fy1kScXsuOoF5NPTsoFBdE1KVIZ2vAh/r9+Bqo+0jINCBYnavTPQZz38QzFTme79ENoN3Q==} + lit-element@4.2.2: resolution: {integrity: sha512-aFKhNToWxoyhkNDmWZwEva2SlQia+jfG0fjIWV//YeTaWrVnOxD89dPKfigCUspXFmjzOEUQpOkejH5Ly6sG0w==} @@ -4581,6 +4584,9 @@ packages: lit@3.3.2: resolution: {integrity: sha512-NF9zbsP79l4ao2SNrH3NkfmFgN/hBYSQo90saIVI1o5GpjAdCPVstVzO1MrLOakHoEhYkrtRjPK6Ob521aoYWQ==} + lit@3.3.3: + resolution: {integrity: sha512-fycuvZg/hkpozL00lm1pEJH5nN/lr9ZXd6mJI2HSN4+Bzc+LDNdEApJ6HFbPkdFNHLvOplIIuJvxkS4XUxqirw==} + lodash.camelcase@4.3.0: resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} @@ -4627,9 +4633,6 @@ packages: resolution: {integrity: sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==} engines: {node: '>=18'} - long@4.0.0: - resolution: {integrity: sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==} - long@5.3.2: resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} @@ -4655,10 +4658,6 @@ packages: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} engines: {node: '>=10'} - lru-cache@7.18.3: - resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} - engines: {node: '>=12'} - lru-memoizer@2.3.0: resolution: {integrity: sha512-GXn7gyHAMhO13WSKrIiNfztwxodVsP8IoZ3XfrJV4yH2x0/OeTO/FIaAHTY5YekdGgW94njfuKmyyt1E0mR6Ug==} @@ -4676,9 +4675,8 @@ packages: resolution: {integrity: sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==} hasBin: true - marked@15.0.12: - resolution: {integrity: sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==} - engines: {node: '>= 18'} + markdown-it@14.3.0: + resolution: {integrity: sha512-RCEsPjR+sr0x+AuYp601tKTkgFG4YEPLCzHST3cQ/fhlJkqAkz1L2/Qbp1j9qw5SBwQHFBoW8+hoN5xssOF0Tw==} hasBin: true marked@17.0.3: @@ -4686,6 +4684,11 @@ packages: engines: {node: '>= 20'} hasBin: true + marked@18.0.5: + resolution: {integrity: sha512-S6GcvALHg6K4ohtu4E7x0a1AqhAjp6cV8KhLSyN9qVapnzJkusVBxZRcIU9AeYsbe6P1hKDusSbEOzGyyuce6w==} + engines: {node: '>= 20'} + hasBin: true + math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} @@ -4810,10 +4813,6 @@ packages: resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} engines: {node: '>= 0.6'} - netmask@2.0.2: - resolution: {integrity: sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==} - engines: {node: '>= 0.4.0'} - node-addon-api@8.7.0: resolution: {integrity: sha512-9MdFxmkKaOYVTV+XVRG8ArDwwQ77XIgIPyKASB1k3JPq3M8fGQQQE3YpMOrKm6g//Ktx8ivZr8xo1Qmtqub+GA==} engines: {node: ^18 || ^20 || >= 21} @@ -4913,24 +4912,24 @@ packages: resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} engines: {node: '>=18'} - openai@6.10.0: - resolution: {integrity: sha512-ITxOGo7rO3XRMiKA5l7tQ43iNNu+iXGFAcf2t+aWVzzqRaS0i7m1K2BhxNdaveB+5eENhO0VY1FkiZzhBk4v3A==} + openai@6.25.0: + resolution: {integrity: sha512-mEh6VZ2ds2AGGokWARo18aPISI1OhlgdEIC1ewhkZr8pSIT31dec0ecr9Nhxx0JlybyOgoAT1sWeKtwPZzJyww==} hasBin: true peerDependencies: ws: ^8.18.0 - zod: ^3.25 || ^4.0 + zod: 4.4.3 peerDependenciesMeta: ws: optional: true zod: optional: true - openai@6.25.0: - resolution: {integrity: sha512-mEh6VZ2ds2AGGokWARo18aPISI1OhlgdEIC1ewhkZr8pSIT31dec0ecr9Nhxx0JlybyOgoAT1sWeKtwPZzJyww==} + openai@6.26.0: + resolution: {integrity: sha512-zd23dbWTjiJ6sSAX6s0HrCZi41JwTA1bQVs0wLQPZ2/5o2gxOJA5wh7yOAUgwYybfhDXyhwlpeQf7Mlgx8EOCA==} hasBin: true peerDependencies: ws: ^8.18.0 - zod: ^3.25 || ^4.0 + zod: 4.4.3 peerDependenciesMeta: ws: optional: true @@ -4957,15 +4956,18 @@ packages: resolution: {integrity: sha512-XJsFIQwnYJgXFlNDz2MncQMWYxwnfy4BCy73mdiFN/P13gEZrAfBU4Jmz2XXFf9UG0wPILdi7hYa6t0KmKQLhw==} hasBin: true - oxlint@1.50.0: - resolution: {integrity: sha512-iSJ4IZEICBma8cZX7kxIIz9PzsYLF2FaLAYN6RKu7VwRVKdu7RIgpP99bTZaGl//Yao7fsaGZLSEo5xBrI5ReQ==} + oxlint@1.73.0: + resolution: {integrity: sha512-u91G9TJzU6yqKWNZUYprQB07W7YvntZXaRxQ6CkoytepYhLWUXWsr1M8zUJ34VatNPuUAr3Z8GH+O2A331CluQ==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: - oxlint-tsgolint: '>=0.14.1' + oxlint-tsgolint: '>=0.24.0' + vite-plus: '*' peerDependenciesMeta: oxlint-tsgolint: optional: true + vite-plus: + optional: true p-finally@1.0.0: resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==} @@ -4991,14 +4993,6 @@ packages: resolution: {integrity: sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg==} engines: {node: '>=20'} - pac-proxy-agent@7.2.0: - resolution: {integrity: sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==} - engines: {node: '>= 14'} - - pac-resolver@7.0.1: - resolution: {integrity: sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==} - engines: {node: '>= 14'} - package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} @@ -5035,10 +5029,6 @@ 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'} @@ -5156,14 +5146,14 @@ packages: proper-lockfile@4.1.2: resolution: {integrity: sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==} - protobufjs@6.8.8: - resolution: {integrity: sha512-AAmHtD5pXgZfi7GMpllpO3q1Xw1OYldr+dMUlAnffGTAhqkg72WdmSY71uKBF/JuyiKs8psYbtKrhi0ASCD8qw==} - hasBin: true - protobufjs@7.5.4: resolution: {integrity: sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==} engines: {node: '>=12.0.0'} + protobufjs@7.6.4: + resolution: {integrity: sha512-RJJPTTpvFfHcWLkIa2JFWK4XvtSzS0yEWDmunqHXli1h3JlkbcQZXDZdcWxv+JK3Xsl5/UFDPZ0iGm7DAengYw==} + engines: {node: '>=12.0.0'} + protobufjs@8.0.0: resolution: {integrity: sha512-jx6+sE9h/UryaCZhsJWbJtTEy47yXoGNYI4z8ZaRncM0zBKeRqjO2JEcOUYwrYGb1WLhXM1FfMzW3annvFv0rw==} engines: {node: '>=12.0.0'} @@ -5172,13 +5162,13 @@ packages: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} engines: {node: '>= 0.10'} - proxy-agent@6.5.0: - resolution: {integrity: sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==} - engines: {node: '>= 14'} - proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + proxy-from-env@2.1.0: + resolution: {integrity: sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==} + engines: {node: '>=10'} + psl@1.15.0: resolution: {integrity: sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==} @@ -5355,6 +5345,16 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.8.0: + resolution: {integrity: sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==} + engines: {node: '>=10'} + hasBin: true + + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} + engines: {node: '>=10'} + hasBin: true + send@0.19.2: resolution: {integrity: sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==} engines: {node: '>= 0.8.0'} @@ -5444,18 +5444,6 @@ packages: resolution: {integrity: sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==} engines: {node: '>=20'} - smart-buffer@4.2.0: - resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} - engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} - - socks-proxy-agent@8.0.5: - resolution: {integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==} - engines: {node: '>= 14'} - - socks@2.8.7: - resolution: {integrity: sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==} - engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} - sonic-boom@4.2.1: resolution: {integrity: sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==} @@ -5517,6 +5505,9 @@ packages: std-env@3.10.0: resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + std-env@4.2.0: + resolution: {integrity: sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==} + stdin-discarder@0.3.1: resolution: {integrity: sha512-reExS1kSGoElkextOcPkel4NE99S0BWxjUHQeDFnR8S993JxpPX7KU4MNmO19NXhlJp+8dmdCbKQVNgLJh2teA==} engines: {node: '>=18'} @@ -5574,9 +5565,6 @@ packages: strip-literal@3.1.0: resolution: {integrity: sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==} - strnum@2.2.2: - resolution: {integrity: sha512-DnR90I+jtXNSTXWdwrEy9FakW7UX+qUZg28gj5fk2vxxl7uS/3bpI4fjFYVmdK9etptYBPNkpahuQnEwhwECqA==} - strtok3@10.3.4: resolution: {integrity: sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg==} engines: {node: '>=18'} @@ -5633,6 +5621,10 @@ packages: resolution: {integrity: sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==} engines: {node: '>=14.0.0'} + tinyrainbow@3.1.0: + resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} + engines: {node: '>=14.0.0'} + tinyspy@4.0.4: resolution: {integrity: sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==} engines: {node: '>=14.0.0'} @@ -5695,16 +5687,16 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - tslog@4.10.2: - resolution: {integrity: sha512-XuELoRpMR+sq8fuWwX7P0bcj+PRNiicOKDEb3fGNURhxWVyykCi9BNq7c4uVz7h7P0sj8qgBsr5SWS6yBClq3g==} + tslog@4.11.0: + resolution: {integrity: sha512-gBe0FTKkRpOv3DenGipjQWQe073jNd1cOLj3nYfVM0/NP29sQzAIx6kfyfTTsQOiWMC+B+tSr289lNXtu7HCmQ==} engines: {node: '>=16'} tsscmp@1.0.6: resolution: {integrity: sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==} engines: {node: '>=0.6.x'} - tsx@4.21.0: - resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==} + tsx@4.23.1: + resolution: {integrity: sha512-GQHnkIfxyx1wYCOS/wonik5MVRZU9hi1TEZmzGZSCJB1y9YgoZ8H6itNE/u4suE+yLmOzuE4E5S4TZ/ZX2wcWQ==} engines: {node: '>=18.0.0'} hasBin: true @@ -5722,6 +5714,9 @@ packages: resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} engines: {node: '>= 0.6'} + typebox@1.1.38: + resolution: {integrity: sha512-pZ0aQPmMmXoUvSbeuWf/Hzsc+avNw/Zd6VeE8CFgkVGWyuHPJvqeJJDeJqLve+K70LvjYIoleGcoJHPT17cWoA==} + typescript@5.9.3: resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} engines: {node: '>=14.17'} @@ -5923,6 +5918,47 @@ packages: jsdom: optional: true + vitest@4.1.10: + resolution: {integrity: sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@opentelemetry/api': ^1.9.0 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.1.10 + '@vitest/browser-preview': 4.1.10 + '@vitest/browser-webdriverio': 4.1.10 + '@vitest/coverage-istanbul': 4.1.10 + '@vitest/coverage-v8': 4.1.10 + '@vitest/ui': 4.1.10 + happy-dom: '*' + jsdom: '*' + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@opentelemetry/api': + optional: true + '@types/node': + optional: true + '@vitest/browser-playwright': + optional: true + '@vitest/browser-preview': + optional: true + '@vitest/browser-webdriverio': + optional: true + '@vitest/coverage-istanbul': + optional: true + '@vitest/coverage-v8': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + web-streams-polyfill@3.3.3: resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} engines: {node: '>= 8'} @@ -5978,6 +6014,18 @@ packages: utf-8-validate: optional: true + ws@8.21.0: + resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} @@ -6017,108 +6065,27 @@ packages: zod-to-json-schema@3.25.1: resolution: {integrity: sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==} peerDependencies: - zod: ^3.25 || ^4 + zod: 4.4.3 - zod@3.25.75: - resolution: {integrity: sha512-OhpzAmVzabPOL6C3A3gpAifqr9MqihV/Msx3gor2b2kviCgcb+HM9SEOpMWwwNp9MRunWnhtAKUoo0AHhjyPPg==} - - zod@3.25.76: - resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} - - zod@4.3.6: - resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} + zod@4.4.3: + resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} snapshots: - '@agentclientprotocol/sdk@0.14.1(zod@4.3.6)': + '@agentclientprotocol/sdk@0.14.1(zod@4.4.3)': dependencies: - zod: 4.3.6 + zod: 4.4.3 - '@anthropic-ai/sdk@0.73.0(zod@4.3.6)': + '@anthropic-ai/sdk@0.91.1(zod@4.4.3)': dependencies: json-schema-to-ts: 3.1.1 optionalDependencies: - zod: 4.3.6 - - '@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.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 - '@discordjs/voice': 0.19.0(opusscript@0.0.8)(opusscript@0.0.8) - '@grammyjs/runner': 2.0.3(grammy@1.40.0) - '@grammyjs/transformer-throttler': 1.2.1(grammy@1.40.0) - '@homebridge/ciao': 1.3.5 - '@line/bot-sdk': 10.6.0 - '@lydell/node-pty': 1.2.0-beta.3 - '@mariozechner/pi-agent-core': 0.54.0(ws@8.19.0)(zod@4.3.6) - '@mariozechner/pi-ai': 0.54.0(ws@8.19.0)(zod@4.3.6) - '@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.97 - '@sinclair/typebox': 0.34.48 - '@slack/bolt': 4.6.0(@types/express@5.0.6) - '@slack/web-api': 7.14.1 - '@whiskeysockets/baileys': 7.0.0-rc.9(sharp@0.34.5) - ajv: 8.18.0 - chalk: 5.6.2 - chokidar: 5.0.0 - cli-highlight: 2.1.11 - commander: 14.0.3 - croner: 10.0.1 - discord-api-types: 0.38.40 - dotenv: 17.3.1 - express: 5.2.1 - file-type: 21.3.2 - grammy: 1.40.0 - https-proxy-agent: 7.0.6 - jiti: 2.6.1 - json5: 2.2.3 - jszip: 3.10.1 - linkedom: 0.18.12 - long: 5.3.2 - markdown-it: 14.1.1 - node-edge-tts: 1.2.10 - node-llama-cpp: 3.17.1(typescript@5.9.3) - opusscript: 0.0.8 - osc-progress: 0.3.0 - pdfjs-dist: 5.4.624 - playwright-core: 1.58.2 - qrcode-terminal: 0.12.0 - quickjs-emscripten: 0.31.0 - semver: 7.7.4 - sharp: 0.34.5 - sqlite-vec: 0.1.7-alpha.2 - tar: 7.5.11 - tslog: 4.10.2 - undici: 7.24.2 - ws: 8.19.0 - yaml: 2.8.3 - zod: 4.3.6 - transitivePeerDependencies: - - '@modelcontextprotocol/sdk' - - '@types/express' - - audio-decode - - aws-crt - - bufferutil - - canvas - - debug - - encoding - - ffmpeg-static - - hono - - jimp - - link-preview-js - - node-opus - - supports-color - - utf-8-validate + zod: 4.4.3 '@aws-crypto/crc32@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.973.6 + '@aws-sdk/types': 3.973.13 tslib: 2.8.1 '@aws-crypto/sha256-browser@5.2.0': @@ -6126,7 +6093,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.6 + '@aws-sdk/types': 3.973.13 '@aws-sdk/util-locate-window': 3.965.4 '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 @@ -6134,7 +6101,7 @@ snapshots: '@aws-crypto/sha256-js@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.973.6 + '@aws-sdk/types': 3.973.13 tslib: 2.8.1 '@aws-crypto/supports-web-crypto@5.2.0': @@ -6143,411 +6110,355 @@ snapshots: '@aws-crypto/util@5.2.0': dependencies: - '@aws-sdk/types': 3.973.6 + '@aws-sdk/types': 3.973.13 '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 - '@aws-sdk/client-bedrock-runtime@3.997.0': + '@aws-sdk/client-bedrock-runtime@3.1048.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@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.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.10 - '@aws-sdk/token-providers': 3.997.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/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.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.2 + '@aws-sdk/core': 3.974.23 + '@aws-sdk/credential-provider-node': 3.972.58 + '@aws-sdk/eventstream-handler-node': 3.972.22 + '@aws-sdk/middleware-eventstream': 3.972.18 + '@aws-sdk/middleware-websocket': 3.972.31 + '@aws-sdk/token-providers': 3.1048.0 + '@aws-sdk/types': 3.973.13 + '@smithy/core': 3.26.0 + '@smithy/fetch-http-handler': 5.5.2 + '@smithy/node-http-handler': 4.8.2 + '@smithy/types': 4.15.0 tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - '@aws-sdk/client-bedrock@3.1019.0': + '@aws-sdk/client-bedrock@3.1085.0': dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@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.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 + '@aws-sdk/core': 3.975.1 + '@aws-sdk/credential-provider-node': 3.972.66 + '@aws-sdk/token-providers': 3.1085.0 + '@aws-sdk/types': 3.974.0 + '@smithy/core': 3.29.3 + '@smithy/fetch-http-handler': 5.6.5 + '@smithy/node-http-handler': 4.9.5 + '@smithy/types': 4.16.1 tslib: 2.8.1 - '@aws-sdk/credential-provider-env@3.972.23': + '@aws-sdk/core@3.974.23': dependencies: - '@aws-sdk/core': 3.973.25 - '@aws-sdk/types': 3.973.6 - '@smithy/property-provider': 4.2.12 - '@smithy/types': 4.13.1 + '@aws-sdk/types': 3.973.13 + '@aws-sdk/xml-builder': 3.972.31 + '@aws/lambda-invoke-store': 0.2.4 + '@smithy/core': 3.26.0 + '@smithy/signature-v4': 5.5.2 + '@smithy/types': 4.15.0 + bowser: 2.14.1 tslib: 2.8.1 - '@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 + '@aws-sdk/core@3.975.1': + dependencies: + '@aws-sdk/types': 3.974.0 + '@aws-sdk/xml-builder': 3.972.34 + '@aws/lambda-invoke-store': 0.3.0 + '@smithy/core': 3.29.3 + '@smithy/signature-v4': 5.6.4 + '@smithy/types': 4.16.1 + bowser: 2.14.1 tslib: 2.8.1 - '@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 + '@aws-sdk/credential-provider-env@3.972.49': + dependencies: + '@aws-sdk/core': 3.974.23 + '@aws-sdk/types': 3.973.13 + '@smithy/core': 3.26.0 + '@smithy/types': 4.15.0 tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - '@aws-sdk/credential-provider-login@3.972.26': + '@aws-sdk/credential-provider-env@3.972.57': 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/protocol-http': 5.3.12 - '@smithy/shared-ini-file-loader': 4.4.7 - '@smithy/types': 4.13.1 + '@aws-sdk/core': 3.975.1 + '@aws-sdk/types': 3.974.0 + '@smithy/core': 3.29.3 + '@smithy/types': 4.16.1 tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - - '@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.23': + '@aws-sdk/credential-provider-http@3.972.51': dependencies: - '@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 + '@aws-sdk/core': 3.974.23 + '@aws-sdk/types': 3.973.13 + '@smithy/core': 3.26.0 + '@smithy/fetch-http-handler': 5.5.2 + '@smithy/node-http-handler': 4.8.2 + '@smithy/types': 4.15.0 tslib: 2.8.1 - '@aws-sdk/credential-provider-sso@3.972.26': + '@aws-sdk/credential-provider-http@3.972.59': dependencies: - '@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 + '@aws-sdk/core': 3.975.1 + '@aws-sdk/types': 3.974.0 + '@smithy/core': 3.29.3 + '@smithy/fetch-http-handler': 5.6.5 + '@smithy/node-http-handler': 4.9.5 + '@smithy/types': 4.16.1 tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - '@aws-sdk/credential-provider-web-identity@3.972.26': - 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 + '@aws-sdk/credential-provider-ini@3.972.56': + dependencies: + '@aws-sdk/core': 3.974.23 + '@aws-sdk/credential-provider-env': 3.972.49 + '@aws-sdk/credential-provider-http': 3.972.51 + '@aws-sdk/credential-provider-login': 3.972.55 + '@aws-sdk/credential-provider-process': 3.972.49 + '@aws-sdk/credential-provider-sso': 3.972.55 + '@aws-sdk/credential-provider-web-identity': 3.972.55 + '@aws-sdk/nested-clients': 3.997.23 + '@aws-sdk/types': 3.973.13 + '@smithy/core': 3.26.0 + '@smithy/credential-provider-imds': 4.4.2 + '@smithy/types': 4.15.0 tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - '@aws-sdk/eventstream-handler-node@3.972.7': - dependencies: - '@aws-sdk/types': 3.973.6 - '@smithy/eventstream-codec': 4.2.10 - '@smithy/types': 4.13.1 + '@aws-sdk/credential-provider-ini@3.973.1': + dependencies: + '@aws-sdk/core': 3.975.1 + '@aws-sdk/credential-provider-env': 3.972.57 + '@aws-sdk/credential-provider-http': 3.972.59 + '@aws-sdk/credential-provider-login': 3.972.63 + '@aws-sdk/credential-provider-process': 3.972.57 + '@aws-sdk/credential-provider-sso': 3.973.1 + '@aws-sdk/credential-provider-web-identity': 3.972.63 + '@aws-sdk/nested-clients': 3.997.31 + '@aws-sdk/types': 3.974.0 + '@smithy/core': 3.29.3 + '@smithy/credential-provider-imds': 4.4.8 + '@smithy/types': 4.16.1 tslib: 2.8.1 - '@aws-sdk/middleware-eventstream@3.972.4': + '@aws-sdk/credential-provider-login@3.972.55': dependencies: - '@aws-sdk/types': 3.973.6 - '@smithy/protocol-http': 5.3.12 - '@smithy/types': 4.13.1 + '@aws-sdk/core': 3.974.23 + '@aws-sdk/nested-clients': 3.997.23 + '@aws-sdk/types': 3.973.13 + '@smithy/core': 3.26.0 + '@smithy/types': 4.15.0 tslib: 2.8.1 - '@aws-sdk/middleware-host-header@3.972.8': + '@aws-sdk/credential-provider-login@3.972.63': dependencies: - '@aws-sdk/types': 3.973.6 - '@smithy/protocol-http': 5.3.12 - '@smithy/types': 4.13.1 + '@aws-sdk/core': 3.975.1 + '@aws-sdk/nested-clients': 3.997.31 + '@aws-sdk/types': 3.974.0 + '@smithy/core': 3.29.3 + '@smithy/types': 4.16.1 tslib: 2.8.1 - '@aws-sdk/middleware-logger@3.972.8': - dependencies: - '@aws-sdk/types': 3.973.6 - '@smithy/types': 4.13.1 + '@aws-sdk/credential-provider-node@3.972.58': + dependencies: + '@aws-sdk/credential-provider-env': 3.972.49 + '@aws-sdk/credential-provider-http': 3.972.51 + '@aws-sdk/credential-provider-ini': 3.972.56 + '@aws-sdk/credential-provider-process': 3.972.49 + '@aws-sdk/credential-provider-sso': 3.972.55 + '@aws-sdk/credential-provider-web-identity': 3.972.55 + '@aws-sdk/types': 3.973.13 + '@smithy/core': 3.26.0 + '@smithy/credential-provider-imds': 4.4.2 + '@smithy/types': 4.15.0 tslib: 2.8.1 - '@aws-sdk/middleware-recursion-detection@3.972.9': + '@aws-sdk/credential-provider-node@3.972.66': + dependencies: + '@aws-sdk/credential-provider-env': 3.972.57 + '@aws-sdk/credential-provider-http': 3.972.59 + '@aws-sdk/credential-provider-ini': 3.973.1 + '@aws-sdk/credential-provider-process': 3.972.57 + '@aws-sdk/credential-provider-sso': 3.973.1 + '@aws-sdk/credential-provider-web-identity': 3.972.63 + '@aws-sdk/types': 3.974.0 + '@smithy/core': 3.29.3 + '@smithy/credential-provider-imds': 4.4.8 + '@smithy/types': 4.16.1 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-process@3.972.49': dependencies: - '@aws-sdk/types': 3.973.6 - '@aws/lambda-invoke-store': 0.2.4 - '@smithy/protocol-http': 5.3.12 - '@smithy/types': 4.13.1 + '@aws-sdk/core': 3.974.23 + '@aws-sdk/types': 3.973.13 + '@smithy/core': 3.26.0 + '@smithy/types': 4.15.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-process@3.972.57': + dependencies: + '@aws-sdk/core': 3.975.1 + '@aws-sdk/types': 3.974.0 + '@smithy/core': 3.29.3 + '@smithy/types': 4.16.1 tslib: 2.8.1 - '@aws-sdk/middleware-user-agent@3.972.26': + '@aws-sdk/credential-provider-sso@3.972.55': dependencies: - '@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 + '@aws-sdk/core': 3.974.23 + '@aws-sdk/nested-clients': 3.997.23 + '@aws-sdk/token-providers': 3.1074.0 + '@aws-sdk/types': 3.973.13 + '@smithy/core': 3.26.0 + '@smithy/types': 4.15.0 tslib: 2.8.1 - '@aws-sdk/middleware-websocket@3.972.8': - dependencies: - '@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.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.2 + '@aws-sdk/credential-provider-sso@3.973.1': + dependencies: + '@aws-sdk/core': 3.975.1 + '@aws-sdk/nested-clients': 3.997.31 + '@aws-sdk/token-providers': 3.1083.0 + '@aws-sdk/types': 3.974.0 + '@smithy/core': 3.29.3 + '@smithy/types': 4.16.1 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-web-identity@3.972.55': + dependencies: + '@aws-sdk/core': 3.974.23 + '@aws-sdk/nested-clients': 3.997.23 + '@aws-sdk/types': 3.973.13 + '@smithy/core': 3.26.0 + '@smithy/types': 4.15.0 tslib: 2.8.1 - '@aws-sdk/nested-clients@3.996.16': + '@aws-sdk/credential-provider-web-identity@3.972.63': + dependencies: + '@aws-sdk/core': 3.975.1 + '@aws-sdk/nested-clients': 3.997.31 + '@aws-sdk/types': 3.974.0 + '@smithy/core': 3.29.3 + '@smithy/types': 4.16.1 + tslib: 2.8.1 + + '@aws-sdk/eventstream-handler-node@3.972.22': + dependencies: + '@aws-sdk/types': 3.973.13 + '@smithy/core': 3.26.0 + '@smithy/types': 4.15.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-eventstream@3.972.18': + dependencies: + '@aws-sdk/types': 3.973.13 + '@smithy/core': 3.26.0 + '@smithy/types': 4.15.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-websocket@3.972.31': + dependencies: + '@aws-sdk/core': 3.974.23 + '@aws-sdk/types': 3.973.13 + '@smithy/core': 3.26.0 + '@smithy/fetch-http-handler': 5.5.2 + '@smithy/signature-v4': 5.5.2 + '@smithy/types': 4.15.0 + tslib: 2.8.1 + + '@aws-sdk/nested-clients@3.997.23': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@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 + '@aws-sdk/core': 3.974.23 + '@aws-sdk/signature-v4-multi-region': 3.996.35 + '@aws-sdk/types': 3.973.13 + '@smithy/core': 3.26.0 + '@smithy/fetch-http-handler': 5.5.2 + '@smithy/node-http-handler': 4.8.2 + '@smithy/types': 4.15.0 tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - '@aws-sdk/region-config-resolver@3.972.10': + '@aws-sdk/nested-clients@3.997.31': dependencies: - '@aws-sdk/types': 3.973.6 - '@smithy/config-resolver': 4.4.13 - '@smithy/node-config-provider': 4.3.12 - '@smithy/types': 4.13.1 + '@aws-sdk/core': 3.975.1 + '@aws-sdk/signature-v4-multi-region': 3.996.39 + '@aws-sdk/types': 3.974.0 + '@smithy/core': 3.29.3 + '@smithy/fetch-http-handler': 5.6.5 + '@smithy/node-http-handler': 4.9.5 + '@smithy/types': 4.16.1 tslib: 2.8.1 - '@aws-sdk/token-providers@3.1019.0': + '@aws-sdk/signature-v4-multi-region@3.996.35': 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 + '@aws-sdk/types': 3.973.13 + '@smithy/signature-v4': 5.5.2 + '@smithy/types': 4.15.0 tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - '@aws-sdk/token-providers@3.997.0': + '@aws-sdk/signature-v4-multi-region@3.996.39': 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 + '@aws-sdk/types': 3.974.0 + '@smithy/signature-v4': 5.6.4 + '@smithy/types': 4.16.1 tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - '@aws-sdk/types@3.973.6': + '@aws-sdk/token-providers@3.1048.0': dependencies: - '@smithy/types': 4.13.1 + '@aws-sdk/core': 3.974.23 + '@aws-sdk/nested-clients': 3.997.23 + '@aws-sdk/types': 3.973.13 + '@smithy/core': 3.26.0 + '@smithy/types': 4.15.0 tslib: 2.8.1 - '@aws-sdk/util-endpoints@3.996.5': + '@aws-sdk/token-providers@3.1074.0': dependencies: - '@aws-sdk/types': 3.973.6 - '@smithy/types': 4.13.1 - '@smithy/url-parser': 4.2.12 - '@smithy/util-endpoints': 3.3.3 + '@aws-sdk/core': 3.974.23 + '@aws-sdk/nested-clients': 3.997.23 + '@aws-sdk/types': 3.973.13 + '@smithy/core': 3.26.0 + '@smithy/types': 4.15.0 tslib: 2.8.1 - '@aws-sdk/util-format-url@3.972.4': + '@aws-sdk/token-providers@3.1083.0': dependencies: - '@aws-sdk/types': 3.973.6 - '@smithy/querystring-builder': 4.2.12 - '@smithy/types': 4.13.1 + '@aws-sdk/core': 3.975.1 + '@aws-sdk/nested-clients': 3.997.31 + '@aws-sdk/types': 3.974.0 + '@smithy/core': 3.29.3 + '@smithy/types': 4.16.1 tslib: 2.8.1 - '@aws-sdk/util-locate-window@3.965.4': + '@aws-sdk/token-providers@3.1085.0': dependencies: + '@aws-sdk/core': 3.975.1 + '@aws-sdk/nested-clients': 3.997.31 + '@aws-sdk/types': 3.974.0 + '@smithy/core': 3.29.3 + '@smithy/types': 4.16.1 tslib: 2.8.1 - '@aws-sdk/util-user-agent-browser@3.972.8': + '@aws-sdk/types@3.973.13': + dependencies: + '@smithy/types': 4.15.0 + tslib: 2.8.1 + + '@aws-sdk/types@3.974.0': + dependencies: + '@smithy/types': 4.16.1 + tslib: 2.8.1 + + '@aws-sdk/util-locate-window@3.965.4': dependencies: - '@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.973.12': + '@aws-sdk/xml-builder@3.972.31': dependencies: - '@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 + '@smithy/types': 4.15.0 tslib: 2.8.1 - '@aws-sdk/xml-builder@3.972.16': + '@aws-sdk/xml-builder@3.972.34': dependencies: - '@smithy/types': 4.13.1 - fast-xml-parser: 5.5.9 + '@smithy/types': 4.16.1 tslib: 2.8.1 '@aws/lambda-invoke-store@0.2.4': {} + '@aws/lambda-invoke-store@0.3.0': {} + '@azure/abort-controller@2.1.2': dependencies: tslib: 2.8.1 @@ -6617,13 +6528,13 @@ snapshots: '@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)': + '@buape/carbon@0.0.0-beta-20260216184201(hono@4.12.7)(opusscript@0.0.8)': dependencies: '@types/node': 25.3.0 discord-api-types: 0.38.37 optionalDependencies: '@cloudflare/workers-types': 4.20260120.0 - '@discordjs/voice': 0.19.0(opusscript@0.0.8)(opusscript@0.0.8) + '@discordjs/voice': 0.19.0(opusscript@0.0.8) '@hono/node-server': 1.19.10(hono@4.12.7) '@types/bun': 1.3.9 '@types/ws': 8.18.1 @@ -6655,15 +6566,16 @@ snapshots: hashery: 1.5.0 keyv: 5.6.0 - '@clack/core@1.0.1': + '@clack/core@1.4.3': dependencies: - picocolors: 1.1.1 + fast-wrap-ansi: 0.2.2 sisteransi: 1.0.5 - '@clack/prompts@1.0.1': + '@clack/prompts@1.7.0': dependencies: - '@clack/core': 1.0.1 - picocolors: 1.1.1 + '@clack/core': 1.4.3 + fast-string-width: 3.0.2 + fast-wrap-ansi: 0.2.2 sisteransi: 1.0.5 '@cloudflare/workers-types@4.20260120.0': @@ -6707,13 +6619,13 @@ snapshots: '@d-fischer/connection@9.0.0': dependencies: - '@d-fischer/isomorphic-ws': 7.0.2(ws@8.19.0) + '@d-fischer/isomorphic-ws': 7.0.2(ws@8.21.0) '@d-fischer/logger': 4.2.4 '@d-fischer/shared-utils': 3.6.4 '@d-fischer/typed-event-emitter': 3.3.3 '@types/ws': 8.18.1 tslib: 2.8.1 - ws: 8.19.0 + ws: 8.21.0 transitivePeerDependencies: - bufferutil - utf-8-validate @@ -6724,9 +6636,9 @@ snapshots: '@d-fischer/escape-string-regexp@5.0.0': {} - '@d-fischer/isomorphic-ws@7.0.2(ws@8.19.0)': + '@d-fischer/isomorphic-ws@7.0.2(ws@8.21.0)': dependencies: - ws: 8.19.0 + ws: 8.21.0 '@d-fischer/logger@4.2.4': dependencies: @@ -6748,13 +6660,13 @@ snapshots: dependencies: tslib: 2.8.1 - '@discordjs/voice@0.19.0(opusscript@0.0.8)(opusscript@0.0.8)': + '@discordjs/voice@0.19.0(opusscript@0.0.8)': dependencies: '@types/ws': 8.18.1 discord-api-types: 0.38.40 - prism-media: 1.3.5(opusscript@0.0.8)(opusscript@0.0.8) + prism-media: 1.3.5(opusscript@0.0.8) tslib: 2.8.1 - ws: 8.19.0 + ws: 8.21.0 transitivePeerDependencies: - '@discordjs/opus' - bufferutil @@ -6763,6 +6675,76 @@ snapshots: - opusscript - utf-8-validate + '@earendil-works/pi-agent-core@0.80.6(ws@8.21.0)(zod@4.4.3)': + dependencies: + '@earendil-works/pi-ai': 0.80.6(ws@8.21.0)(zod@4.4.3) + ignore: 7.0.5 + typebox: 1.1.38 + yaml: 2.8.3 + transitivePeerDependencies: + - '@modelcontextprotocol/sdk' + - bufferutil + - supports-color + - utf-8-validate + - ws + - zod + + '@earendil-works/pi-ai@0.80.6(ws@8.21.0)(zod@4.4.3)': + dependencies: + '@anthropic-ai/sdk': 0.91.1(zod@4.4.3) + '@aws-sdk/client-bedrock-runtime': 3.1048.0 + '@google/genai': 1.52.0 + '@mistralai/mistralai': 2.2.6(@opentelemetry/api@1.9.0) + '@opentelemetry/api': 1.9.0 + '@smithy/node-http-handler': 4.7.3 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + openai: 6.26.0(ws@8.21.0)(zod@4.4.3) + partial-json: 0.1.7 + typebox: 1.1.38 + transitivePeerDependencies: + - '@modelcontextprotocol/sdk' + - bufferutil + - supports-color + - utf-8-validate + - ws + - zod + + '@earendil-works/pi-coding-agent@0.80.6(ws@8.21.0)(zod@4.4.3)': + dependencies: + '@earendil-works/pi-agent-core': 0.80.6(ws@8.21.0)(zod@4.4.3) + '@earendil-works/pi-ai': 0.80.6(ws@8.21.0)(zod@4.4.3) + '@earendil-works/pi-tui': 0.80.6 + '@silvia-odwyer/photon-node': 0.3.4 + chalk: 5.6.2 + cross-spawn: 7.0.6 + diff: 8.0.4 + glob: 13.0.6 + highlight.js: 10.7.3 + hosted-git-info: 9.0.3 + ignore: 7.0.5 + jiti: 2.7.0 + minimatch: 10.2.3 + proper-lockfile: 4.1.2 + semver: 7.8.0 + typebox: 1.1.38 + undici: 7.24.2 + yaml: 2.8.3 + optionalDependencies: + '@mariozechner/clipboard': 0.3.9 + transitivePeerDependencies: + - '@modelcontextprotocol/sdk' + - bufferutil + - supports-color + - utf-8-validate + - ws + - zod + + '@earendil-works/pi-tui@0.80.6': + dependencies: + get-east-asian-width: 1.6.0 + marked: 18.0.5 + '@emnapi/core@1.8.1': dependencies: '@emnapi/wasi-threads': 1.1.0 @@ -6785,178 +6767,256 @@ snapshots: '@esbuild/aix-ppc64@0.27.3': optional: true + '@esbuild/aix-ppc64@0.28.1': + optional: true + '@esbuild/android-arm64@0.25.12': optional: true '@esbuild/android-arm64@0.27.3': optional: true + '@esbuild/android-arm64@0.28.1': + optional: true + '@esbuild/android-arm@0.25.12': optional: true '@esbuild/android-arm@0.27.3': optional: true + '@esbuild/android-arm@0.28.1': + optional: true + '@esbuild/android-x64@0.25.12': optional: true '@esbuild/android-x64@0.27.3': optional: true + '@esbuild/android-x64@0.28.1': + optional: true + '@esbuild/darwin-arm64@0.25.12': optional: true '@esbuild/darwin-arm64@0.27.3': optional: true + '@esbuild/darwin-arm64@0.28.1': + optional: true + '@esbuild/darwin-x64@0.25.12': optional: true '@esbuild/darwin-x64@0.27.3': optional: true + '@esbuild/darwin-x64@0.28.1': + optional: true + '@esbuild/freebsd-arm64@0.25.12': optional: true '@esbuild/freebsd-arm64@0.27.3': optional: true + '@esbuild/freebsd-arm64@0.28.1': + optional: true + '@esbuild/freebsd-x64@0.25.12': optional: true '@esbuild/freebsd-x64@0.27.3': optional: true + '@esbuild/freebsd-x64@0.28.1': + optional: true + '@esbuild/linux-arm64@0.25.12': optional: true '@esbuild/linux-arm64@0.27.3': optional: true + '@esbuild/linux-arm64@0.28.1': + optional: true + '@esbuild/linux-arm@0.25.12': optional: true '@esbuild/linux-arm@0.27.3': optional: true + '@esbuild/linux-arm@0.28.1': + optional: true + '@esbuild/linux-ia32@0.25.12': optional: true '@esbuild/linux-ia32@0.27.3': optional: true + '@esbuild/linux-ia32@0.28.1': + optional: true + '@esbuild/linux-loong64@0.25.12': optional: true '@esbuild/linux-loong64@0.27.3': optional: true + '@esbuild/linux-loong64@0.28.1': + optional: true + '@esbuild/linux-mips64el@0.25.12': optional: true '@esbuild/linux-mips64el@0.27.3': optional: true + '@esbuild/linux-mips64el@0.28.1': + optional: true + '@esbuild/linux-ppc64@0.25.12': optional: true '@esbuild/linux-ppc64@0.27.3': optional: true + '@esbuild/linux-ppc64@0.28.1': + optional: true + '@esbuild/linux-riscv64@0.25.12': optional: true '@esbuild/linux-riscv64@0.27.3': optional: true + '@esbuild/linux-riscv64@0.28.1': + optional: true + '@esbuild/linux-s390x@0.25.12': optional: true '@esbuild/linux-s390x@0.27.3': optional: true + '@esbuild/linux-s390x@0.28.1': + optional: true + '@esbuild/linux-x64@0.25.12': optional: true '@esbuild/linux-x64@0.27.3': optional: true + '@esbuild/linux-x64@0.28.1': + optional: true + '@esbuild/netbsd-arm64@0.25.12': optional: true '@esbuild/netbsd-arm64@0.27.3': optional: true + '@esbuild/netbsd-arm64@0.28.1': + optional: true + '@esbuild/netbsd-x64@0.25.12': optional: true '@esbuild/netbsd-x64@0.27.3': optional: true + '@esbuild/netbsd-x64@0.28.1': + optional: true + '@esbuild/openbsd-arm64@0.25.12': optional: true '@esbuild/openbsd-arm64@0.27.3': optional: true + '@esbuild/openbsd-arm64@0.28.1': + optional: true + '@esbuild/openbsd-x64@0.25.12': optional: true '@esbuild/openbsd-x64@0.27.3': optional: true + '@esbuild/openbsd-x64@0.28.1': + optional: true + '@esbuild/openharmony-arm64@0.25.12': optional: true '@esbuild/openharmony-arm64@0.27.3': optional: true + '@esbuild/openharmony-arm64@0.28.1': + optional: true + '@esbuild/sunos-x64@0.25.12': optional: true '@esbuild/sunos-x64@0.27.3': optional: true + '@esbuild/sunos-x64@0.28.1': + optional: true + '@esbuild/win32-arm64@0.25.12': optional: true '@esbuild/win32-arm64@0.27.3': optional: true + '@esbuild/win32-arm64@0.28.1': + optional: true + '@esbuild/win32-ia32@0.25.12': optional: true '@esbuild/win32-ia32@0.27.3': optional: true + '@esbuild/win32-ia32@0.28.1': + optional: true + '@esbuild/win32-x64@0.25.12': optional: true '@esbuild/win32-x64@0.27.3': optional: true - '@google/genai@1.42.0': + '@esbuild/win32-x64@0.28.1': + optional: true + + '@google/genai@1.52.0': dependencies: google-auth-library: 10.6.1 p-retry: 4.6.2 - protobufjs: 7.5.4 - ws: 8.19.0 + protobufjs: 7.6.4 + ws: 8.21.0 transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - '@grammyjs/runner@2.0.3(grammy@1.40.0)': + '@grammyjs/runner@2.0.3(grammy@1.44.0)': dependencies: abort-controller: 3.0.0 - grammy: 1.40.0 + grammy: 1.44.0 - '@grammyjs/transformer-throttler@1.2.1(grammy@1.40.0)': + '@grammyjs/transformer-throttler@1.2.1(grammy@1.44.0)': dependencies: bottleneck: 2.19.5 - grammy: 1.40.0 + grammy: 1.44.0 - '@grammyjs/types@3.24.0': {} + '@grammyjs/types@3.28.0': {} '@grpc/grpc-js@1.14.3': dependencies: @@ -6976,7 +7036,7 @@ snapshots: '@hapi/hoek@9.3.0': {} - '@homebridge/ciao@1.3.5': + '@homebridge/ciao@1.3.10': dependencies: debug: 4.4.3 fast-deep-equal: 3.1.3 @@ -7193,7 +7253,7 @@ snapshots: lodash.pickby: 4.6.0 protobufjs: 7.5.4 qs: 6.14.2 - ws: 8.19.0 + ws: 8.21.0 transitivePeerDependencies: - bufferutil - debug @@ -7209,7 +7269,7 @@ snapshots: '@lit-labs/signals@0.2.0': dependencies: - lit: 3.3.2 + lit: 3.3.3 signal-polyfill: 0.2.2 '@lit-labs/ssr-dom-shim@1.5.1': {} @@ -7249,129 +7309,50 @@ snapshots: '@lydell/node-pty-win32-arm64': 1.2.0-beta.3 '@lydell/node-pty-win32-x64': 1.2.0-beta.3 - '@mariozechner/clipboard-darwin-arm64@0.3.2': + '@mariozechner/clipboard-darwin-arm64@0.3.9': optional: true - '@mariozechner/clipboard-darwin-universal@0.3.2': + '@mariozechner/clipboard-darwin-universal@0.3.9': optional: true - '@mariozechner/clipboard-darwin-x64@0.3.2': + '@mariozechner/clipboard-darwin-x64@0.3.9': optional: true - '@mariozechner/clipboard-linux-arm64-gnu@0.3.2': + '@mariozechner/clipboard-linux-arm64-gnu@0.3.9': optional: true - '@mariozechner/clipboard-linux-arm64-musl@0.3.2': + '@mariozechner/clipboard-linux-arm64-musl@0.3.9': optional: true - '@mariozechner/clipboard-linux-riscv64-gnu@0.3.2': + '@mariozechner/clipboard-linux-riscv64-gnu@0.3.9': optional: true - '@mariozechner/clipboard-linux-x64-gnu@0.3.2': + '@mariozechner/clipboard-linux-x64-gnu@0.3.9': optional: true - '@mariozechner/clipboard-linux-x64-musl@0.3.2': + '@mariozechner/clipboard-linux-x64-musl@0.3.9': optional: true - '@mariozechner/clipboard-win32-arm64-msvc@0.3.2': + '@mariozechner/clipboard-win32-arm64-msvc@0.3.9': optional: true - '@mariozechner/clipboard-win32-x64-msvc@0.3.2': + '@mariozechner/clipboard-win32-x64-msvc@0.3.9': optional: true - '@mariozechner/clipboard@0.3.2': + '@mariozechner/clipboard@0.3.9': optionalDependencies: - '@mariozechner/clipboard-darwin-arm64': 0.3.2 - '@mariozechner/clipboard-darwin-universal': 0.3.2 - '@mariozechner/clipboard-darwin-x64': 0.3.2 - '@mariozechner/clipboard-linux-arm64-gnu': 0.3.2 - '@mariozechner/clipboard-linux-arm64-musl': 0.3.2 - '@mariozechner/clipboard-linux-riscv64-gnu': 0.3.2 - '@mariozechner/clipboard-linux-x64-gnu': 0.3.2 - '@mariozechner/clipboard-linux-x64-musl': 0.3.2 - '@mariozechner/clipboard-win32-arm64-msvc': 0.3.2 - '@mariozechner/clipboard-win32-x64-msvc': 0.3.2 + '@mariozechner/clipboard-darwin-arm64': 0.3.9 + '@mariozechner/clipboard-darwin-universal': 0.3.9 + '@mariozechner/clipboard-darwin-x64': 0.3.9 + '@mariozechner/clipboard-linux-arm64-gnu': 0.3.9 + '@mariozechner/clipboard-linux-arm64-musl': 0.3.9 + '@mariozechner/clipboard-linux-riscv64-gnu': 0.3.9 + '@mariozechner/clipboard-linux-x64-gnu': 0.3.9 + '@mariozechner/clipboard-linux-x64-musl': 0.3.9 + '@mariozechner/clipboard-win32-arm64-msvc': 0.3.9 + '@mariozechner/clipboard-win32-x64-msvc': 0.3.9 optional: true - '@mariozechner/jiti@2.6.5': - dependencies: - std-env: 3.10.0 - yoctocolors: 2.1.2 - - '@mariozechner/pi-agent-core@0.54.0(ws@8.19.0)(zod@4.3.6)': - dependencies: - '@mariozechner/pi-ai': 0.54.0(ws@8.19.0)(zod@4.3.6) - transitivePeerDependencies: - - '@modelcontextprotocol/sdk' - - aws-crt - - bufferutil - - supports-color - - utf-8-validate - - ws - - zod - - '@mariozechner/pi-ai@0.54.0(ws@8.19.0)(zod@4.3.6)': - dependencies: - '@anthropic-ai/sdk': 0.73.0(zod@4.3.6) - '@aws-sdk/client-bedrock-runtime': 3.997.0 - '@google/genai': 1.42.0 - '@mistralai/mistralai': 1.10.0 - '@sinclair/typebox': 0.34.48 - ajv: 8.18.0 - ajv-formats: 3.0.1(ajv@8.18.0) - chalk: 5.6.2 - openai: 6.10.0(ws@8.19.0)(zod@4.3.6) - partial-json: 0.1.7 - proxy-agent: 6.5.0 - undici: 7.24.2 - zod-to-json-schema: 3.25.1(zod@4.3.6) - transitivePeerDependencies: - - '@modelcontextprotocol/sdk' - - aws-crt - - bufferutil - - supports-color - - utf-8-validate - - ws - - zod - - '@mariozechner/pi-coding-agent@0.54.0(ws@8.19.0)(zod@4.3.6)': - dependencies: - '@mariozechner/jiti': 2.6.5 - '@mariozechner/pi-agent-core': 0.54.0(ws@8.19.0)(zod@4.3.6) - '@mariozechner/pi-ai': 0.54.0(ws@8.19.0)(zod@4.3.6) - '@mariozechner/pi-tui': 0.54.0 - '@silvia-odwyer/photon-node': 0.3.4 - chalk: 5.6.2 - cli-highlight: 2.1.11 - diff: 8.0.3 - file-type: 21.3.2 - glob: 13.0.6 - hosted-git-info: 9.0.2 - ignore: 7.0.5 - marked: 15.0.12 - minimatch: 10.2.3 - proper-lockfile: 4.1.2 - yaml: 2.8.3 - optionalDependencies: - '@mariozechner/clipboard': 0.3.2 - transitivePeerDependencies: - - '@modelcontextprotocol/sdk' - - aws-crt - - bufferutil - - supports-color - - utf-8-validate - - ws - - zod - - '@mariozechner/pi-tui@0.54.0': - dependencies: - '@types/mime-types': 2.1.4 - chalk: 5.6.2 - get-east-asian-width: 1.5.0 - koffi: 2.15.1 - marked: 15.0.12 - mime-types: 3.0.2 - '@matrix-org/matrix-sdk-crypto-nodejs@0.4.0': dependencies: https-proxy-agent: 7.0.6 @@ -7383,7 +7364,7 @@ snapshots: dependencies: debug: 4.4.3 uuid: 11.1.0 - zod: 3.25.75 + zod: 4.4.3 transitivePeerDependencies: - supports-color @@ -7396,84 +7377,58 @@ snapshots: jsonwebtoken: 9.0.3 jwks-rsa: 3.2.2 object-path: 0.11.8 - zod: 3.25.75 + zod: 4.4.3 transitivePeerDependencies: - debug - supports-color - '@mistralai/mistralai@1.10.0': + '@mistralai/mistralai@2.2.6(@opentelemetry/api@1.9.0)': dependencies: - zod: 3.25.76 - zod-to-json-schema: 3.25.1(zod@3.25.76) + '@opentelemetry/semantic-conventions': 1.41.1 + ws: 8.21.0 + zod: 4.4.3 + zod-to-json-schema: 3.25.1(zod@4.4.3) + optionalDependencies: + '@opentelemetry/api': 1.9.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate '@mozilla/readability@0.6.0': {} '@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 @@ -7488,20 +7443,6 @@ 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 @@ -7515,10 +7456,16 @@ snapshots: dependencies: '@noble/hashes': 2.0.1 + '@noble/curves@2.2.0': + dependencies: + '@noble/hashes': 2.2.0 + '@noble/ed25519@3.0.0': {} '@noble/hashes@2.0.1': {} + '@noble/hashes@2.2.0': {} + '@node-llama-cpp/linux-arm64@3.17.1': optional: true @@ -7806,6 +7753,8 @@ snapshots: '@opentelemetry/semantic-conventions@1.39.0': {} + '@opentelemetry/semantic-conventions@1.41.1': {} + '@oxc-project/types@0.112.0': {} '@oxfmt/binding-android-arm-eabi@0.34.0': @@ -7883,61 +7832,61 @@ snapshots: '@oxlint-tsgolint/win32-x64@0.14.2': optional: true - '@oxlint/binding-android-arm-eabi@1.50.0': + '@oxlint/binding-android-arm-eabi@1.73.0': optional: true - '@oxlint/binding-android-arm64@1.50.0': + '@oxlint/binding-android-arm64@1.73.0': optional: true - '@oxlint/binding-darwin-arm64@1.50.0': + '@oxlint/binding-darwin-arm64@1.73.0': optional: true - '@oxlint/binding-darwin-x64@1.50.0': + '@oxlint/binding-darwin-x64@1.73.0': optional: true - '@oxlint/binding-freebsd-x64@1.50.0': + '@oxlint/binding-freebsd-x64@1.73.0': optional: true - '@oxlint/binding-linux-arm-gnueabihf@1.50.0': + '@oxlint/binding-linux-arm-gnueabihf@1.73.0': optional: true - '@oxlint/binding-linux-arm-musleabihf@1.50.0': + '@oxlint/binding-linux-arm-musleabihf@1.73.0': optional: true - '@oxlint/binding-linux-arm64-gnu@1.50.0': + '@oxlint/binding-linux-arm64-gnu@1.73.0': optional: true - '@oxlint/binding-linux-arm64-musl@1.50.0': + '@oxlint/binding-linux-arm64-musl@1.73.0': optional: true - '@oxlint/binding-linux-ppc64-gnu@1.50.0': + '@oxlint/binding-linux-ppc64-gnu@1.73.0': optional: true - '@oxlint/binding-linux-riscv64-gnu@1.50.0': + '@oxlint/binding-linux-riscv64-gnu@1.73.0': optional: true - '@oxlint/binding-linux-riscv64-musl@1.50.0': + '@oxlint/binding-linux-riscv64-musl@1.73.0': optional: true - '@oxlint/binding-linux-s390x-gnu@1.50.0': + '@oxlint/binding-linux-s390x-gnu@1.73.0': optional: true - '@oxlint/binding-linux-x64-gnu@1.50.0': + '@oxlint/binding-linux-x64-gnu@1.73.0': optional: true - '@oxlint/binding-linux-x64-musl@1.50.0': + '@oxlint/binding-linux-x64-musl@1.73.0': optional: true - '@oxlint/binding-openharmony-arm64@1.50.0': + '@oxlint/binding-openharmony-arm64@1.73.0': optional: true - '@oxlint/binding-win32-arm64-msvc@1.50.0': + '@oxlint/binding-win32-arm64-msvc@1.73.0': optional: true - '@oxlint/binding-win32-ia32-msvc@1.50.0': + '@oxlint/binding-win32-ia32-msvc@1.73.0': optional: true - '@oxlint/binding-win32-x64-msvc@1.50.0': + '@oxlint/binding-win32-x64-msvc@1.73.0': optional: true '@pinojs/redact@0.4.0': {} @@ -7953,13 +7902,21 @@ snapshots: '@protobufjs/codegen@2.0.4': {} + '@protobufjs/codegen@2.0.5': {} + '@protobufjs/eventemitter@1.1.0': {} + '@protobufjs/eventemitter@1.1.1': {} + '@protobufjs/fetch@1.1.0': dependencies: '@protobufjs/aspromise': 1.1.2 '@protobufjs/inquire': 1.1.0 + '@protobufjs/fetch@1.1.1': + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/float@1.0.2': {} '@protobufjs/inquire@1.1.0': {} @@ -7970,6 +7927,8 @@ snapshots: '@protobufjs/utf8@1.1.0': {} + '@protobufjs/utf8@1.1.1': {} + '@quansync/fs@1.0.0': dependencies: quansync: 1.0.0 @@ -8128,397 +8087,176 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.59.0': optional: true - '@scure/base@2.0.0': {} - - '@scure/bip32@2.0.1': - dependencies: - '@noble/curves': 2.0.1 - '@noble/hashes': 2.0.1 - '@scure/base': 2.0.0 - - '@scure/bip39@2.0.1': - dependencies: - '@noble/hashes': 2.0.1 - '@scure/base': 2.0.0 - - '@selderee/plugin-htmlparser2@0.11.0': - dependencies: - domhandler: 5.0.3 - selderee: 0.11.0 - - '@silvia-odwyer/photon-node@0.3.4': {} - - '@sinclair/typebox@0.34.48': {} - - '@slack/bolt@4.6.0(@types/express@5.0.6)': - dependencies: - '@slack/logger': 4.0.0 - '@slack/oauth': 3.0.4 - '@slack/socket-mode': 2.0.5 - '@slack/types': 2.20.0 - '@slack/web-api': 7.14.1 - '@types/express': 5.0.6 - axios: 1.13.5 - express: 5.2.1 - path-to-regexp: 8.4.0 - raw-body: 3.0.2 - tsscmp: 1.0.6 - transitivePeerDependencies: - - bufferutil - - debug - - supports-color - - utf-8-validate - - '@slack/logger@4.0.0': - dependencies: - '@types/node': 25.3.0 - - '@slack/oauth@3.0.4': - dependencies: - '@slack/logger': 4.0.0 - '@slack/web-api': 7.14.1 - '@types/jsonwebtoken': 9.0.10 - '@types/node': 25.3.0 - jsonwebtoken: 9.0.3 - transitivePeerDependencies: - - debug - - '@slack/socket-mode@2.0.5': - dependencies: - '@slack/logger': 4.0.0 - '@slack/web-api': 7.14.1 - '@types/node': 25.3.0 - '@types/ws': 8.18.1 - eventemitter3: 5.0.4 - ws: 8.19.0 - transitivePeerDependencies: - - bufferutil - - debug - - utf-8-validate - - '@slack/types@2.20.0': {} - - '@slack/web-api@7.14.1': - dependencies: - '@slack/logger': 4.0.0 - '@slack/types': 2.20.0 - '@types/node': 25.3.0 - '@types/retry': 0.12.0 - axios: 1.13.5 - eventemitter3: 5.0.4 - form-data: 2.5.4 - is-electron: 2.2.2 - is-stream: 2.0.1 - p-queue: 6.6.2 - p-retry: 4.6.2 - retry: 0.13.1 - transitivePeerDependencies: - - debug - - '@smithy/abort-controller@4.2.12': - dependencies: - '@smithy/types': 4.13.1 - tslib: 2.8.1 - - '@smithy/config-resolver@4.4.13': - dependencies: - '@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.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.12': - dependencies: - '@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.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.1 - tslib: 2.8.1 - - '@smithy/eventstream-serde-config-resolver@4.3.10': - dependencies: - '@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.1 - tslib: 2.8.1 - - '@smithy/eventstream-serde-universal@4.2.10': - dependencies: - '@smithy/eventstream-codec': 4.2.10 - '@smithy/types': 4.13.1 - tslib: 2.8.1 - - '@smithy/fetch-http-handler@5.3.15': - dependencies: - '@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.12': - dependencies: - '@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.12': - dependencies: - '@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.2': - dependencies: - tslib: 2.8.1 - - '@smithy/middleware-content-length@4.2.12': - dependencies: - '@smithy/protocol-http': 5.3.12 - '@smithy/types': 4.13.1 - tslib: 2.8.1 - - '@smithy/middleware-endpoint@4.4.27': - dependencies: - '@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.44': - dependencies: - '@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.15': - dependencies: - '@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.12': - dependencies: - '@smithy/types': 4.13.1 - tslib: 2.8.1 - - '@smithy/node-config-provider@4.3.12': - dependencies: - '@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.5.0': - dependencies: - '@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.12': - dependencies: - '@smithy/types': 4.13.1 - tslib: 2.8.1 + '@scure/base@2.0.0': {} - '@smithy/protocol-http@5.3.12': + '@scure/bip32@2.0.1': dependencies: - '@smithy/types': 4.13.1 - tslib: 2.8.1 + '@noble/curves': 2.0.1 + '@noble/hashes': 2.0.1 + '@scure/base': 2.0.0 - '@smithy/querystring-builder@4.2.12': + '@scure/bip39@2.0.1': dependencies: - '@smithy/types': 4.13.1 - '@smithy/util-uri-escape': 4.2.2 - tslib: 2.8.1 + '@noble/hashes': 2.0.1 + '@scure/base': 2.0.0 - '@smithy/querystring-parser@4.2.12': + '@selderee/plugin-htmlparser2@0.11.0': dependencies: - '@smithy/types': 4.13.1 - tslib: 2.8.1 + domhandler: 5.0.3 + selderee: 0.11.0 - '@smithy/service-error-classification@4.2.12': - dependencies: - '@smithy/types': 4.13.1 + '@silvia-odwyer/photon-node@0.3.4': {} - '@smithy/shared-ini-file-loader@4.4.7': - dependencies: - '@smithy/types': 4.13.1 - tslib: 2.8.1 + '@sinclair/typebox@0.34.48': {} - '@smithy/signature-v4@5.3.12': + '@slack/bolt@4.7.3(@types/express@5.0.6)': dependencies: - '@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 + '@slack/logger': 4.0.1 + '@slack/oauth': 3.0.5 + '@slack/socket-mode': 2.0.7 + '@slack/types': 2.22.0 + '@slack/web-api': 7.19.0 + '@types/express': 5.0.6 + axios: 1.13.5 + express: 5.2.1 + path-to-regexp: 8.4.0 + raw-body: 3.0.2 + tsscmp: 1.0.6 + transitivePeerDependencies: + - bufferutil + - debug + - supports-color + - utf-8-validate - '@smithy/smithy-client@4.12.7': + '@slack/logger@4.0.1': dependencies: - '@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 + '@types/node': 25.3.0 - '@smithy/types@4.13.1': + '@slack/oauth@3.0.5': dependencies: - tslib: 2.8.1 + '@slack/logger': 4.0.1 + '@slack/web-api': 7.19.0 + '@types/jsonwebtoken': 9.0.10 + '@types/node': 25.3.0 + jsonwebtoken: 9.0.3 + transitivePeerDependencies: + - debug + - supports-color - '@smithy/url-parser@4.2.12': + '@slack/socket-mode@2.0.7': dependencies: - '@smithy/querystring-parser': 4.2.12 - '@smithy/types': 4.13.1 - tslib: 2.8.1 + '@slack/logger': 4.0.1 + '@slack/web-api': 7.19.0 + '@types/node': 25.3.0 + '@types/ws': 8.18.1 + eventemitter3: 5.0.4 + ws: 8.21.0 + transitivePeerDependencies: + - bufferutil + - debug + - supports-color + - utf-8-validate + + '@slack/types@2.22.0': {} - '@smithy/util-base64@4.3.2': + '@slack/web-api@7.19.0': dependencies: - '@smithy/util-buffer-from': 4.2.2 - '@smithy/util-utf8': 4.2.2 - tslib: 2.8.1 + '@slack/logger': 4.0.1 + '@slack/types': 2.22.0 + '@types/node': 25.3.0 + '@types/retry': 0.12.0 + axios: 1.18.1 + eventemitter3: 5.0.4 + form-data: 2.5.4 + is-electron: 2.2.2 + is-stream: 2.0.1 + p-queue: 6.6.2 + p-retry: 4.6.2 + retry: 0.13.1 + transitivePeerDependencies: + - debug + - supports-color - '@smithy/util-body-length-browser@4.2.2': + '@smithy/core@3.26.0': dependencies: + '@aws-crypto/crc32': 5.2.0 + '@smithy/types': 4.15.0 tslib: 2.8.1 - '@smithy/util-body-length-node@4.2.3': + '@smithy/core@3.29.3': dependencies: + '@smithy/types': 4.16.1 tslib: 2.8.1 - '@smithy/util-buffer-from@2.2.0': + '@smithy/credential-provider-imds@4.4.2': dependencies: - '@smithy/is-array-buffer': 2.2.0 + '@smithy/core': 3.26.0 + '@smithy/types': 4.15.0 tslib: 2.8.1 - '@smithy/util-buffer-from@4.2.2': + '@smithy/credential-provider-imds@4.4.8': dependencies: - '@smithy/is-array-buffer': 4.2.2 + '@smithy/core': 3.29.3 + '@smithy/types': 4.16.1 tslib: 2.8.1 - '@smithy/util-config-provider@4.2.2': + '@smithy/fetch-http-handler@5.5.2': dependencies: + '@smithy/core': 3.26.0 + '@smithy/types': 4.15.0 tslib: 2.8.1 - '@smithy/util-defaults-mode-browser@4.3.43': + '@smithy/fetch-http-handler@5.6.5': dependencies: - '@smithy/property-provider': 4.2.12 - '@smithy/smithy-client': 4.12.7 - '@smithy/types': 4.13.1 + '@smithy/core': 3.29.3 + '@smithy/types': 4.16.1 tslib: 2.8.1 - '@smithy/util-defaults-mode-node@4.2.47': + '@smithy/is-array-buffer@2.2.0': dependencies: - '@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.3': + '@smithy/node-http-handler@4.7.3': dependencies: - '@smithy/node-config-provider': 4.3.12 - '@smithy/types': 4.13.1 + '@smithy/core': 3.26.0 + '@smithy/types': 4.15.0 tslib: 2.8.1 - '@smithy/util-hex-encoding@4.2.1': + '@smithy/node-http-handler@4.8.2': dependencies: + '@smithy/core': 3.26.0 + '@smithy/types': 4.15.0 tslib: 2.8.1 - '@smithy/util-hex-encoding@4.2.2': + '@smithy/node-http-handler@4.9.5': dependencies: + '@smithy/core': 3.29.3 + '@smithy/types': 4.16.1 tslib: 2.8.1 - '@smithy/util-middleware@4.2.12': + '@smithy/signature-v4@5.5.2': dependencies: - '@smithy/types': 4.13.1 + '@smithy/core': 3.26.0 + '@smithy/types': 4.15.0 tslib: 2.8.1 - '@smithy/util-retry@4.2.12': + '@smithy/signature-v4@5.6.4': dependencies: - '@smithy/service-error-classification': 4.2.12 - '@smithy/types': 4.13.1 + '@smithy/core': 3.29.3 + '@smithy/types': 4.16.1 tslib: 2.8.1 - '@smithy/util-stream@4.5.15': + '@smithy/types@4.15.0': 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.1 - '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 - '@smithy/util-stream@4.5.20': + '@smithy/types@4.16.1': 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.2': + '@smithy/util-buffer-from@2.2.0': dependencies: + '@smithy/is-array-buffer': 2.2.0 tslib: 2.8.1 '@smithy/util-utf8@2.3.0': @@ -8526,15 +8264,6 @@ snapshots: '@smithy/util-buffer-from': 2.2.0 tslib: 2.8.1 - '@smithy/util-utf8@4.2.2': - dependencies: - '@smithy/util-buffer-from': 4.2.2 - tslib: 2.8.1 - - '@smithy/uuid@1.1.2': - dependencies: - tslib: 2.8.1 - '@standard-schema/spec@1.1.0': {} '@swc/helpers@0.5.20': @@ -8552,8 +8281,6 @@ snapshots: '@tokenizer/token@0.3.0': {} - '@tootallnate/quickjs-emscripten@0.23.0': {} - '@twurple/api-call@8.0.3': dependencies: '@d-fischer/shared-utils': 3.6.4 @@ -8677,8 +8404,6 @@ snapshots: '@types/linkify-it@5.0.0': {} - '@types/long@4.0.2': {} - '@types/markdown-it@14.1.2': dependencies: '@types/linkify-it': 5.0.0 @@ -8686,14 +8411,10 @@ snapshots: '@types/mdurl@2.0.0': {} - '@types/mime-types@2.1.4': {} - '@types/mime@1.3.5': {} '@types/ms@2.1.0': {} - '@types/node@10.17.60': {} - '@types/node@20.19.37': dependencies: undici-types: 6.21.0 @@ -8819,51 +8540,49 @@ 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.3))(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.7.0)(tsx@4.23.1)(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.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)) + '@vitest/browser': 4.0.18(vite@7.3.1(@types/node@25.3.0)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.8.3))(vitest@4.0.18) + '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.3.0)(jiti@2.7.0)(tsx@4.23.1)(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.3) + vitest: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.0)(@vitest/browser-playwright@4.0.18)(jiti@2.7.0)(tsx@4.23.1)(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.3))(vitest@4.0.18)': + '@vitest/browser@4.0.18(vite@7.3.1(@types/node@25.3.0)(jiti@2.7.0)(tsx@4.23.1)(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.3)) + '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.3.0)(jiti@2.7.0)(tsx@4.23.1)(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.3) - ws: 8.19.0 + vitest: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.0)(@vitest/browser-playwright@4.0.18)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.8.3) + ws: 8.21.0 transitivePeerDependencies: - bufferutil - msw - 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.3))(vitest@4.0.18))(vitest@4.0.18)': + '@vitest/coverage-v8@4.1.10(vitest@4.1.10)': dependencies: '@bcoe/v8-coverage': 1.0.2 - '@vitest/utils': 4.0.18 - ast-v8-to-istanbul: 0.3.11 + '@vitest/utils': 4.1.10 + ast-v8-to-istanbul: 1.0.4 istanbul-lib-coverage: 3.2.2 istanbul-lib-report: 3.0.1 istanbul-reports: 3.2.0 magicast: 0.5.2 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.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.3))(vitest@4.0.18) + std-env: 4.2.0 + tinyrainbow: 3.1.0 + vitest: 4.1.10(@opentelemetry/api@1.9.0)(@types/node@25.3.0)(@vitest/coverage-v8@4.1.10)(vite@7.3.1(@types/node@25.3.0)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.8.3)) '@vitest/expect@3.2.4': dependencies: @@ -8882,21 +8601,38 @@ 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.3))': + '@vitest/expect@4.1.10': + dependencies: + '@standard-schema/spec': 1.1.0 + '@types/chai': 5.2.3 + '@vitest/spy': 4.1.10 + '@vitest/utils': 4.1.10 + chai: 6.2.2 + tinyrainbow: 3.1.0 + + '@vitest/mocker@3.2.4(vite@7.3.1(@types/node@25.3.0)(jiti@2.7.0)(tsx@4.23.1)(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.3) + vite: 7.3.1(@types/node@25.3.0)(jiti@2.7.0)(tsx@4.23.1)(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.3))': + '@vitest/mocker@4.0.18(vite@7.3.1(@types/node@25.3.0)(jiti@2.7.0)(tsx@4.23.1)(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.3) + vite: 7.3.1(@types/node@25.3.0)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.8.3) + + '@vitest/mocker@4.1.10(vite@7.3.1(@types/node@25.3.0)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.8.3))': + dependencies: + '@vitest/spy': 4.1.10 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 7.3.1(@types/node@25.3.0)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.8.3) '@vitest/pretty-format@3.2.4': dependencies: @@ -8906,6 +8642,10 @@ snapshots: dependencies: tinyrainbow: 3.0.3 + '@vitest/pretty-format@4.1.10': + dependencies: + tinyrainbow: 3.1.0 + '@vitest/runner@3.2.4': dependencies: '@vitest/utils': 3.2.4 @@ -8917,6 +8657,11 @@ snapshots: '@vitest/utils': 4.0.18 pathe: 2.0.3 + '@vitest/runner@4.1.10': + dependencies: + '@vitest/utils': 4.1.10 + pathe: 2.0.3 + '@vitest/snapshot@3.2.4': dependencies: '@vitest/pretty-format': 3.2.4 @@ -8929,12 +8674,21 @@ snapshots: magic-string: 0.30.21 pathe: 2.0.3 + '@vitest/snapshot@4.1.10': + dependencies: + '@vitest/pretty-format': 4.1.10 + '@vitest/utils': 4.1.10 + magic-string: 0.30.21 + pathe: 2.0.3 + '@vitest/spy@3.2.4': dependencies: tinyspy: 4.0.4 '@vitest/spy@4.0.18': {} + '@vitest/spy@4.1.10': {} + '@vitest/utils@3.2.4': dependencies: '@vitest/pretty-format': 3.2.4 @@ -8946,29 +8700,30 @@ snapshots: '@vitest/pretty-format': 4.0.18 tinyrainbow: 3.0.3 + '@vitest/utils@4.1.10': + dependencies: + '@vitest/pretty-format': 4.1.10 + convert-source-map: 2.0.0 + tinyrainbow: 3.1.0 + '@whiskeysockets/baileys@7.0.0-rc.9(sharp@0.34.5)': dependencies: '@cacheable/node-cache': 1.7.6 '@hapi/boom': 9.1.4 async-mutex: 0.5.0 - libsignal: '@whiskeysockets/libsignal-node@https://codeload.github.com/whiskeysockets/libsignal-node/tar.gz/1c30d7d7e76a3b0aa120b04dc6a26f5a12dccf67' + libsignal: https://codeload.github.com/whiskeysockets/libsignal-node/tar.gz/bcea72df9ec34d9d9140ab30619cf479c7c144c7 lru-cache: 11.2.6 music-metadata: 11.12.3 p-queue: 9.1.0 pino: 9.14.0 protobufjs: 7.5.4 sharp: 0.34.5 - ws: 8.19.0 + ws: 8.21.0 transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - '@whiskeysockets/libsignal-node@https://codeload.github.com/whiskeysockets/libsignal-node/tar.gz/1c30d7d7e76a3b0aa120b04dc6a26f5a12dccf67': - dependencies: - curve25519-js: 0.0.4 - protobufjs: 6.8.8 - abort-controller@3.0.0: dependencies: event-target-shim: 5.0.1 @@ -8989,13 +8744,15 @@ snapshots: acorn@8.16.0: {} - agent-base@7.1.4: {} + agent-base@6.0.2: + dependencies: + debug: 4.4.3 + transitivePeerDependencies: + - supports-color - ajv-formats@3.0.1(ajv@8.18.0): - optionalDependencies: - ajv: 8.18.0 + agent-base@7.1.4: {} - ajv@8.18.0: + ajv@8.20.0: dependencies: fast-deep-equal: 3.1.3 fast-uri: 3.1.0 @@ -9054,11 +8811,7 @@ snapshots: estree-walker: 3.0.3 pathe: 2.0.3 - ast-types@0.13.4: - dependencies: - tslib: 2.8.1 - - ast-v8-to-istanbul@0.3.11: + ast-v8-to-istanbul@1.0.4: dependencies: '@jridgewell/trace-mapping': 0.3.31 estree-walker: 3.0.3 @@ -9090,6 +8843,16 @@ snapshots: transitivePeerDependencies: - debug + axios@1.18.1: + dependencies: + follow-redirects: 1.16.0 + form-data: 2.5.4 + https-proxy-agent: 5.0.1 + proxy-from-env: 2.1.0 + transitivePeerDependencies: + - debug + - supports-color + balanced-match@4.0.4: {} base64-js@1.5.1: {} @@ -9098,8 +8861,6 @@ snapshots: dependencies: safe-buffer: 5.1.2 - basic-ftp@5.2.0: {} - bcrypt-pbkdf@1.0.2: dependencies: tweetnacl: 0.14.5 @@ -9258,7 +9019,7 @@ snapshots: fs-extra: 11.3.4 node-api-headers: 1.8.0 rc: 1.2.8 - semver: 7.7.4 + semver: 7.8.5 tar: 7.5.11 url-join: 4.0.1 which: 6.0.1 @@ -9302,6 +9063,8 @@ snapshots: content-type@1.0.5: {} + convert-source-map@2.0.0: {} + cookie-signature@1.0.7: {} cookie-signature@1.2.2: {} @@ -9340,8 +9103,6 @@ snapshots: data-uri-to-buffer@4.0.1: {} - data-uri-to-buffer@6.0.2: {} - debug@2.6.9: dependencies: ms: 2.0.0 @@ -9358,12 +9119,6 @@ snapshots: defu@6.1.4: {} - degenerator@5.0.1: - dependencies: - ast-types: 0.13.4 - escodegen: 2.1.0 - esprima: 4.0.1 - delayed-stream@1.0.0: {} depd@2.0.0: {} @@ -9372,7 +9127,7 @@ snapshots: detect-libc@2.1.2: {} - diff@8.0.3: {} + diff@8.0.4: {} discord-api-types@0.38.37: {} @@ -9400,7 +9155,7 @@ snapshots: domelementtype: 2.3.0 domhandler: 5.0.3 - dotenv@17.3.1: {} + dotenv@17.4.2: {} dts-resolver@2.1.3: {} @@ -9445,6 +9200,8 @@ snapshots: es-module-lexer@1.7.0: {} + es-module-lexer@2.3.1: {} + es-object-atoms@1.1.1: dependencies: es-errors: 1.3.0 @@ -9514,30 +9271,45 @@ snapshots: '@esbuild/win32-ia32': 0.27.3 '@esbuild/win32-x64': 0.27.3 + esbuild@0.28.1: + optionalDependencies: + '@esbuild/aix-ppc64': 0.28.1 + '@esbuild/android-arm': 0.28.1 + '@esbuild/android-arm64': 0.28.1 + '@esbuild/android-x64': 0.28.1 + '@esbuild/darwin-arm64': 0.28.1 + '@esbuild/darwin-x64': 0.28.1 + '@esbuild/freebsd-arm64': 0.28.1 + '@esbuild/freebsd-x64': 0.28.1 + '@esbuild/linux-arm': 0.28.1 + '@esbuild/linux-arm64': 0.28.1 + '@esbuild/linux-ia32': 0.28.1 + '@esbuild/linux-loong64': 0.28.1 + '@esbuild/linux-mips64el': 0.28.1 + '@esbuild/linux-ppc64': 0.28.1 + '@esbuild/linux-riscv64': 0.28.1 + '@esbuild/linux-s390x': 0.28.1 + '@esbuild/linux-x64': 0.28.1 + '@esbuild/netbsd-arm64': 0.28.1 + '@esbuild/netbsd-x64': 0.28.1 + '@esbuild/openbsd-arm64': 0.28.1 + '@esbuild/openbsd-x64': 0.28.1 + '@esbuild/openharmony-arm64': 0.28.1 + '@esbuild/sunos-x64': 0.28.1 + '@esbuild/win32-arm64': 0.28.1 + '@esbuild/win32-ia32': 0.28.1 + '@esbuild/win32-x64': 0.28.1 + escalade@3.2.0: {} escape-html@1.0.3: {} escape-string-regexp@4.0.0: {} - escodegen@2.1.0: - dependencies: - esprima: 4.0.1 - estraverse: 5.3.0 - esutils: 2.0.3 - optionalDependencies: - source-map: 0.6.1 - - esprima@4.0.1: {} - - estraverse@5.3.0: {} - estree-walker@3.0.3: dependencies: '@types/estree': 1.0.8 - esutils@2.0.3: {} - etag@1.8.1: {} event-target-shim@5.0.1: {} @@ -9631,17 +9403,17 @@ snapshots: merge2: 1.4.1 micromatch: 4.0.8 - fast-uri@3.1.0: {} + fast-string-truncated-width@3.0.3: {} - fast-xml-builder@1.1.4: + fast-string-width@3.0.2: dependencies: - path-expression-matcher: 1.2.0 + fast-string-truncated-width: 3.0.3 - fast-xml-parser@5.5.9: + fast-uri@3.1.0: {} + + fast-wrap-ansi@0.2.2: dependencies: - fast-xml-builder: 1.1.4 - path-expression-matcher: 1.2.0 - strnum: 2.2.2 + fast-string-width: 3.0.2 fastq@1.20.1: dependencies: @@ -9706,6 +9478,8 @@ snapshots: follow-redirects@1.15.11: {} + follow-redirects@1.16.0: {} + foreground-child@3.3.1: dependencies: cross-spawn: 7.0.6 @@ -9767,6 +9541,8 @@ snapshots: get-east-asian-width@1.5.0: {} + get-east-asian-width@1.6.0: {} + get-intrinsic@1.3.0: dependencies: call-bind-apply-helpers: 1.0.2 @@ -9789,14 +9565,6 @@ snapshots: dependencies: resolve-pkg-maps: 1.0.0 - get-uri@6.0.5: - dependencies: - basic-ftp: 5.2.0 - data-uri-to-buffer: 6.0.2 - debug: 4.4.3 - transitivePeerDependencies: - - supports-color - getpass@0.1.7: dependencies: assert-plus: 1.0.0 @@ -9839,9 +9607,9 @@ snapshots: graceful-fs@4.2.11: {} - grammy@1.40.0: + grammy@1.44.0: dependencies: - '@grammyjs/types': 3.24.0 + '@grammyjs/types': 3.28.0 abort-controller: 3.0.0 debug: 4.4.3 node-fetch: 2.7.0 @@ -9880,7 +9648,7 @@ snapshots: hookified@1.15.1: {} - hosted-git-info@9.0.2: + hosted-git-info@9.0.3: dependencies: lru-cache: 11.2.6 @@ -9933,6 +9701,13 @@ snapshots: jsprim: 2.0.2 sshpk: 1.18.0 + https-proxy-agent@5.0.1: + dependencies: + agent-base: 6.0.2 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + https-proxy-agent@7.0.6: dependencies: agent-base: 7.1.4 @@ -9967,8 +9742,6 @@ snapshots: ini@1.3.8: {} - ip-address@10.1.0: {} - ipaddr.js@1.9.1: {} ipull@3.9.5: @@ -10065,7 +9838,7 @@ snapshots: optionalDependencies: '@pkgjs/parseargs': 0.11.0 - jiti@2.6.1: {} + jiti@2.7.0: {} jose@4.15.9: {} @@ -10113,7 +9886,7 @@ snapshots: lodash.isstring: 4.0.1 lodash.once: 4.1.1 ms: 2.1.3 - semver: 7.7.4 + semver: 7.8.5 jsprim@2.0.2: dependencies: @@ -10156,10 +9929,13 @@ snapshots: klona@2.0.6: {} - koffi@2.15.1: {} - leac@0.6.0: {} + libsignal@https://codeload.github.com/whiskeysockets/libsignal-node/tar.gz/bcea72df9ec34d9d9140ab30619cf479c7c144c7: + dependencies: + curve25519-js: 0.0.4 + protobufjs: 7.6.4 + lie@3.3.0: dependencies: immediate: 3.0.6 @@ -10182,6 +9958,10 @@ snapshots: dependencies: uc.micro: 2.1.0 + linkify-it@5.0.2: + dependencies: + uc.micro: 2.1.0 + lit-element@4.2.2: dependencies: '@lit-labs/ssr-dom-shim': 1.5.1 @@ -10198,6 +9978,12 @@ snapshots: lit-element: 4.2.2 lit-html: 3.3.2 + lit@3.3.3: + dependencies: + '@lit/reactive-element': 2.1.2 + lit-element: 4.2.2 + lit-html: 3.3.2 + lodash.camelcase@4.3.0: {} lodash.clonedeep@4.5.0: {} @@ -10231,8 +10017,6 @@ snapshots: is-unicode-supported: 2.1.0 yoctocolors: 2.1.2 - long@4.0.0: {} - long@5.3.2: {} loupe@3.2.1: {} @@ -10257,8 +10041,6 @@ snapshots: dependencies: yallist: 4.0.0 - lru-cache@7.18.3: {} - lru-memoizer@2.3.0: dependencies: lodash.clonedeep: 4.5.0 @@ -10276,7 +10058,7 @@ snapshots: make-dir@4.0.0: dependencies: - semver: 7.7.4 + semver: 7.8.5 markdown-it@14.1.1: dependencies: @@ -10287,10 +10069,19 @@ snapshots: punycode.js: 2.3.1 uc.micro: 2.1.0 - marked@15.0.12: {} + markdown-it@14.3.0: + dependencies: + argparse: 2.0.1 + entities: 4.5.0 + linkify-it: 5.0.2 + mdurl: 2.0.0 + punycode.js: 2.3.1 + uc.micro: 2.1.0 marked@17.0.3: {} + marked@18.0.5: {} + math-intrinsics@1.1.0: {} mdurl@2.0.0: {} @@ -10391,8 +10182,6 @@ snapshots: negotiator@1.0.0: {} - netmask@2.0.2: {} - node-addon-api@8.7.0: {} node-api-headers@1.8.0: {} @@ -10404,7 +10193,7 @@ snapshots: node-edge-tts@1.2.10: dependencies: https-proxy-agent: 7.0.6 - ws: 8.19.0 + ws: 8.21.0 yargs: 17.7.2 transitivePeerDependencies: - bufferutil @@ -10443,7 +10232,7 @@ snapshots: ora: 9.3.0 pretty-ms: 9.3.0 proper-lockfile: 4.1.2 - semver: 7.7.4 + semver: 7.8.5 simple-git: 3.33.0 slice-ansi: 8.0.0 stdout-update: 4.0.1 @@ -10518,15 +10307,15 @@ snapshots: dependencies: mimic-function: 5.0.1 - openai@6.10.0(ws@8.19.0)(zod@4.3.6): + openai@6.25.0(ws@8.21.0)(zod@4.4.3): optionalDependencies: - ws: 8.19.0 - zod: 4.3.6 + ws: 8.21.0 + zod: 4.4.3 - openai@6.25.0(ws@8.19.0)(zod@4.3.6): + openai@6.26.0(ws@8.21.0)(zod@4.4.3): optionalDependencies: - ws: 8.19.0 - zod: 4.3.6 + ws: 8.21.0 + zod: 4.4.3 opusscript@0.0.8: {} @@ -10576,27 +10365,27 @@ snapshots: '@oxlint-tsgolint/win32-arm64': 0.14.2 '@oxlint-tsgolint/win32-x64': 0.14.2 - oxlint@1.50.0(oxlint-tsgolint@0.14.2): + oxlint@1.73.0(oxlint-tsgolint@0.14.2): optionalDependencies: - '@oxlint/binding-android-arm-eabi': 1.50.0 - '@oxlint/binding-android-arm64': 1.50.0 - '@oxlint/binding-darwin-arm64': 1.50.0 - '@oxlint/binding-darwin-x64': 1.50.0 - '@oxlint/binding-freebsd-x64': 1.50.0 - '@oxlint/binding-linux-arm-gnueabihf': 1.50.0 - '@oxlint/binding-linux-arm-musleabihf': 1.50.0 - '@oxlint/binding-linux-arm64-gnu': 1.50.0 - '@oxlint/binding-linux-arm64-musl': 1.50.0 - '@oxlint/binding-linux-ppc64-gnu': 1.50.0 - '@oxlint/binding-linux-riscv64-gnu': 1.50.0 - '@oxlint/binding-linux-riscv64-musl': 1.50.0 - '@oxlint/binding-linux-s390x-gnu': 1.50.0 - '@oxlint/binding-linux-x64-gnu': 1.50.0 - '@oxlint/binding-linux-x64-musl': 1.50.0 - '@oxlint/binding-openharmony-arm64': 1.50.0 - '@oxlint/binding-win32-arm64-msvc': 1.50.0 - '@oxlint/binding-win32-ia32-msvc': 1.50.0 - '@oxlint/binding-win32-x64-msvc': 1.50.0 + '@oxlint/binding-android-arm-eabi': 1.73.0 + '@oxlint/binding-android-arm64': 1.73.0 + '@oxlint/binding-darwin-arm64': 1.73.0 + '@oxlint/binding-darwin-x64': 1.73.0 + '@oxlint/binding-freebsd-x64': 1.73.0 + '@oxlint/binding-linux-arm-gnueabihf': 1.73.0 + '@oxlint/binding-linux-arm-musleabihf': 1.73.0 + '@oxlint/binding-linux-arm64-gnu': 1.73.0 + '@oxlint/binding-linux-arm64-musl': 1.73.0 + '@oxlint/binding-linux-ppc64-gnu': 1.73.0 + '@oxlint/binding-linux-riscv64-gnu': 1.73.0 + '@oxlint/binding-linux-riscv64-musl': 1.73.0 + '@oxlint/binding-linux-s390x-gnu': 1.73.0 + '@oxlint/binding-linux-x64-gnu': 1.73.0 + '@oxlint/binding-linux-x64-musl': 1.73.0 + '@oxlint/binding-openharmony-arm64': 1.73.0 + '@oxlint/binding-win32-arm64-msvc': 1.73.0 + '@oxlint/binding-win32-ia32-msvc': 1.73.0 + '@oxlint/binding-win32-x64-msvc': 1.73.0 oxlint-tsgolint: 0.14.2 p-finally@1.0.0: {} @@ -10622,24 +10411,6 @@ snapshots: p-timeout@7.0.1: {} - pac-proxy-agent@7.2.0: - dependencies: - '@tootallnate/quickjs-emscripten': 0.23.0 - agent-base: 7.1.4 - debug: 4.4.3 - get-uri: 6.0.5 - http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.6 - pac-resolver: 7.0.1 - socks-proxy-agent: 8.0.5 - transitivePeerDependencies: - - supports-color - - pac-resolver@7.0.1: - dependencies: - degenerator: 5.0.1 - netmask: 2.0.2 - package-json-from-dist@1.0.1: {} pako@1.0.11: {} @@ -10667,8 +10438,6 @@ snapshots: partial-json@0.1.7: {} - path-expression-matcher@1.2.0: {} - path-key@3.1.1: {} path-scurry@1.11.1: @@ -10754,9 +10523,8 @@ snapshots: dependencies: parse-ms: 4.0.0 - prism-media@1.3.5(opusscript@0.0.8)(opusscript@0.0.8): + prism-media@1.3.5(opusscript@0.0.8): optionalDependencies: - '@discordjs/opus': opusscript@0.0.8 opusscript: 0.0.8 process-nextick-args@2.0.1: {} @@ -10769,34 +10537,32 @@ snapshots: retry: 0.12.0 signal-exit: 3.0.7 - protobufjs@6.8.8: + protobufjs@7.5.4: dependencies: '@protobufjs/aspromise': 1.1.2 '@protobufjs/base64': 1.1.2 - '@protobufjs/codegen': 2.0.4 - '@protobufjs/eventemitter': 1.1.0 - '@protobufjs/fetch': 1.1.0 + '@protobufjs/codegen': 2.0.5 + '@protobufjs/eventemitter': 1.1.1 + '@protobufjs/fetch': 1.1.1 '@protobufjs/float': 1.0.2 '@protobufjs/inquire': 1.1.0 '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 - '@protobufjs/utf8': 1.1.0 - '@types/long': 4.0.2 - '@types/node': 10.17.60 - long: 4.0.0 + '@protobufjs/utf8': 1.1.1 + '@types/node': 25.3.0 + long: 5.3.2 - protobufjs@7.5.4: + protobufjs@7.6.4: dependencies: '@protobufjs/aspromise': 1.1.2 '@protobufjs/base64': 1.1.2 - '@protobufjs/codegen': 2.0.4 - '@protobufjs/eventemitter': 1.1.0 - '@protobufjs/fetch': 1.1.0 + '@protobufjs/codegen': 2.0.5 + '@protobufjs/eventemitter': 1.1.1 + '@protobufjs/fetch': 1.1.1 '@protobufjs/float': 1.0.2 - '@protobufjs/inquire': 1.1.0 '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 - '@protobufjs/utf8': 1.1.0 + '@protobufjs/utf8': 1.1.1 '@types/node': 25.3.0 long: 5.3.2 @@ -10820,21 +10586,10 @@ snapshots: forwarded: 0.2.0 ipaddr.js: 1.9.1 - proxy-agent@6.5.0: - dependencies: - agent-base: 7.1.4 - debug: 4.4.3 - http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.6 - lru-cache: 7.18.3 - pac-proxy-agent: 7.2.0 - proxy-from-env: 1.1.0 - socks-proxy-agent: 8.0.5 - transitivePeerDependencies: - - supports-color - proxy-from-env@1.1.0: {} + proxy-from-env@2.1.0: {} + psl@1.15.0: dependencies: punycode: 2.3.1 @@ -11052,6 +10807,10 @@ snapshots: semver@7.7.4: {} + semver@7.8.0: {} + + semver@7.8.5: {} + send@0.19.2: dependencies: debug: 2.6.9 @@ -11112,7 +10871,7 @@ snapshots: dependencies: '@img/colour': 1.0.0 detect-libc: 2.1.2 - semver: 7.7.4 + semver: 7.8.5 optionalDependencies: '@img/sharp-darwin-arm64': 0.34.5 '@img/sharp-darwin-x64': 0.34.5 @@ -11213,21 +10972,6 @@ snapshots: ansi-styles: 6.2.3 is-fullwidth-code-point: 5.1.0 - smart-buffer@4.2.0: {} - - socks-proxy-agent@8.0.5: - dependencies: - agent-base: 7.1.4 - debug: 4.4.3 - socks: 2.8.7 - transitivePeerDependencies: - - supports-color - - socks@2.8.7: - dependencies: - ip-address: 10.1.0 - smart-buffer: 4.2.0 - sonic-boom@4.2.1: dependencies: atomic-sleep: 1.0.0 @@ -11284,6 +11028,8 @@ snapshots: std-env@3.10.0: {} + std-env@4.2.0: {} + stdin-discarder@0.3.1: {} stdout-update@4.0.1: @@ -11346,8 +11092,6 @@ snapshots: dependencies: js-tokens: 9.0.1 - strnum@2.2.2: {} - strtok3@10.3.4: dependencies: '@tokenizer/token': 0.3.0 @@ -11400,6 +11144,8 @@ snapshots: tinyrainbow@3.0.3: {} + tinyrainbow@3.1.0: {} + tinyspy@4.0.4: {} to-regex-range@5.0.1: @@ -11441,7 +11187,7 @@ snapshots: 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 + semver: 7.8.5 tinyexec: 1.0.2 tinyglobby: 0.2.15 tree-kill: 1.2.2 @@ -11458,14 +11204,13 @@ snapshots: tslib@2.8.1: {} - tslog@4.10.2: {} + tslog@4.11.0: {} tsscmp@1.0.6: {} - tsx@4.21.0: + tsx@4.23.1: dependencies: - esbuild: 0.27.3 - get-tsconfig: 4.13.6 + esbuild: 0.28.1 optionalDependencies: fsevents: 2.3.3 @@ -11486,6 +11231,8 @@ snapshots: media-typer: 1.1.0 mime-types: 3.0.2 + typebox@1.1.38: {} + typescript@5.9.3: {} typical@4.0.0: {} @@ -11546,13 +11293,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.3): + vite-node@3.2.4(@types/node@25.3.0)(jiti@2.7.0)(tsx@4.23.1)(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.3) + vite: 7.3.1(@types/node@25.3.0)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.8.3) transitivePeerDependencies: - '@types/node' - jiti @@ -11567,7 +11314,7 @@ snapshots: - tsx - yaml - vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3): + vite@7.3.1(@types/node@25.3.0)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.8.3): dependencies: esbuild: 0.27.3 fdir: 6.5.0(picomatch@4.0.4) @@ -11578,15 +11325,15 @@ snapshots: optionalDependencies: '@types/node': 25.3.0 fsevents: 2.3.3 - jiti: 2.6.1 - tsx: 4.21.0 + jiti: 2.7.0 + tsx: 4.23.1 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.3))(vitest@4.0.18))(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3): + vitest@3.2.4(@types/node@25.3.0)(jiti@2.7.0)(tsx@4.23.1)(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.3)) + '@vitest/mocker': 3.2.4(vite@7.3.1(@types/node@25.3.0)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.8.3)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 @@ -11604,12 +11351,11 @@ snapshots: 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.3) - vite-node: 3.2.4(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3) + vite: 7.3.1(@types/node@25.3.0)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.8.3) + vite-node: 3.2.4(@types/node@25.3.0)(jiti@2.7.0)(tsx@4.23.1)(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.3))(vitest@4.0.18) transitivePeerDependencies: - jiti - less @@ -11624,10 +11370,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.3): + vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.0)(@vitest/browser-playwright@4.0.18)(jiti@2.7.0)(tsx@4.23.1)(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.3)) + '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.3.0)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.8.3)) '@vitest/pretty-format': 4.0.18 '@vitest/runner': 4.0.18 '@vitest/snapshot': 4.0.18 @@ -11644,12 +11390,12 @@ snapshots: 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.3) + vite: 7.3.1(@types/node@25.3.0)(jiti@2.7.0)(tsx@4.23.1)(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.3))(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.7.0)(tsx@4.23.1)(yaml@2.8.3))(vitest@4.0.18) transitivePeerDependencies: - jiti - less @@ -11663,6 +11409,35 @@ snapshots: - tsx - yaml + vitest@4.1.10(@opentelemetry/api@1.9.0)(@types/node@25.3.0)(@vitest/coverage-v8@4.1.10)(vite@7.3.1(@types/node@25.3.0)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.8.3)): + dependencies: + '@vitest/expect': 4.1.10 + '@vitest/mocker': 4.1.10(vite@7.3.1(@types/node@25.3.0)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.8.3)) + '@vitest/pretty-format': 4.1.10 + '@vitest/runner': 4.1.10 + '@vitest/snapshot': 4.1.10 + '@vitest/spy': 4.1.10 + '@vitest/utils': 4.1.10 + es-module-lexer: 2.3.1 + expect-type: 1.3.0 + magic-string: 0.30.21 + obug: 2.1.1 + pathe: 2.0.3 + picomatch: 4.0.4 + std-env: 4.2.0 + tinybench: 2.9.0 + tinyexec: 1.0.2 + tinyglobby: 0.2.15 + tinyrainbow: 3.1.0 + vite: 7.3.1(@types/node@25.3.0)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.8.3) + why-is-node-running: 2.3.0 + optionalDependencies: + '@opentelemetry/api': 1.9.0 + '@types/node': 25.3.0 + '@vitest/coverage-v8': 4.1.10(vitest@4.1.10) + transitivePeerDependencies: + - msw + web-streams-polyfill@3.3.3: {} webidl-conversions@3.0.1: {} @@ -11705,6 +11480,8 @@ snapshots: ws@8.19.0: {} + ws@8.21.0: {} + y18n@5.0.8: {} yallist@4.0.0: {} @@ -11739,16 +11516,8 @@ snapshots: yoctocolors@2.1.2: {} - zod-to-json-schema@3.25.1(zod@3.25.76): + zod-to-json-schema@3.25.1(zod@4.4.3): dependencies: - zod: 3.25.76 - - zod-to-json-schema@3.25.1(zod@4.3.6): - dependencies: - zod: 4.3.6 - - zod@3.25.75: {} - - zod@3.25.76: {} + zod: 4.4.3 - zod@4.3.6: {} + zod@4.4.3: {} diff --git a/scripts/bench-model.ts b/scripts/bench-model.ts index f1698737..b5d42dd8 100644 --- a/scripts/bench-model.ts +++ b/scripts/bench-model.ts @@ -1,4 +1,4 @@ -import { completeSimple, getModel, type Model } from "@mariozechner/pi-ai"; +import { completeSimple, getModel, type Model } from "@earendil-works/pi-ai"; type Usage = { input?: number; diff --git a/scripts/check-lockfile-policy.mjs b/scripts/check-lockfile-policy.mjs new file mode 100644 index 00000000..59e95005 --- /dev/null +++ b/scripts/check-lockfile-policy.mjs @@ -0,0 +1,134 @@ +#!/usr/bin/env node +/** + * Lockfile change policy check. + * + * Enforces the dependency supply-chain policy documented in SECURITY.md: + * a change to pnpm-lock.yaml should arrive alongside a dependency change in + * package.json, and the commit carrying it should be scoped as `deps:`. + * + * The check compares the lockfile against a base. On CI it reads the base + * from the GITHUB_BASE_REF / commit range; locally it can be invoked with + * --base to run the same gate before pushing. + * + * Exit codes: + * 0 β€” policy satisfied (or lockfile unchanged) + * 1 β€” policy violated (lockfile changed without a deps-scoped commit) + * 2 β€” unable to determine the range (soft-fail with a warning) + */ +import { execSync } from "node:child_process"; +import process from "node:process"; + +const LOCKFILE = "pnpm-lock.yaml"; +const DEPS_SCOPE = /^deps:/; +const DEPS_FILES = new Set(["package.json", "pnpm-workspace.yaml"]); + +function parseArgs(argv) { + const args = { base: null, head: null }; + for (let i = 2; i < argv.length; i++) { + const a = argv[i]; + if (a === "--base") args.base = argv[++i]; + else if (a === "--head") args.head = argv[++i]; + else if (a === "-h" || a === "--help") { + process.stdout.write( + "Usage: check-lockfile-policy.mjs [--base ] [--head ]\n" + + " Checks that pnpm-lock.yaml changes arrive in deps-scoped commits.\n", + ); + process.exit(0); + } + } + return args; +} + +function run(cmd) { + return execSync(cmd, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim(); +} + +function tryRun(cmd) { + try { + return run(cmd); + } catch { + return null; + } +} + +function resolveRange(args) { + const head = args.head || process.env.GITHUB_SHA || "HEAD"; + let base = args.base; + if (!base) { + if (process.env.GITHUB_BASE_REF) { + base = `origin/${process.env.GITHUB_BASE_REF}`; + } else if (process.env.GITHUB_EVENT_BEFORE) { + base = process.env.GITHUB_EVENT_BEFORE; + } else { + // local default: compare against the default branch tip + base = + tryRun("git rev-parse --verify origin/main >/dev/null 2>&1 && echo origin/main") || null; + } + } + return { base, head }; +} + +function changedFiles(base, head) { + const out = tryRun(`git diff --name-only ${base}..${head}`); + if (!out) return null; + return new Set(out.split(/\r?\n/).filter(Boolean)); +} + +function commitSubjects(base, head) { + const out = tryRun(`git log --format=%s ${base}..${head}`); + if (!out) return []; + return out.split(/\r?\n/).filter(Boolean); +} + +function main() { + const args = parseArgs(process.argv); + const { base, head } = resolveRange(args); + + if (!base) { + console.warn("[lockfile-policy] could not determine a base ref; skipping."); + process.exit(2); + } + + // Make sure the base is locally resolvable (CI shallow clones). + const resolvedBase = tryRun(`git rev-parse --verify "${base}^{commit}"`); + if (!resolvedBase) { + const fetched = tryRun(`git fetch --no-tags --depth=1 origin ${base}`); + if (!fetched === false) { + console.warn(`[lockfile-policy] base ref ${base} not resolvable; skipping.`); + process.exit(2); + } + } + + const files = changedFiles(base, head); + if (!files) { + console.warn("[lockfile-policy] could not compute diff; skipping."); + process.exit(2); + } + + if (!files.has(LOCKFILE)) { + console.log(`[lockfile-policy] ${LOCKFILE} unchanged β€” policy satisfied.`); + process.exit(0); + } + + // Lockfile changed. Require either a deps-scoped commit or a change to a + // recognized dependency manifest in the same range. + const subjects = commitSubjects(base, head); + const hasDepsScope = subjects.some((s) => DEPS_SCOPE.test(s)); + const hasManifestChange = [...files].some((f) => DEPS_FILES.has(f)); + + if (hasDepsScope || hasManifestChange) { + console.log(`[lockfile-policy] ${LOCKFILE} changed with deps-scoped commit β€” OK.`); + process.exit(0); + } + + console.error( + `[lockfile-policy] ${LOCKFILE} changed but no commit in range is scoped "deps:" ` + + `and no manifest (${[...DEPS_FILES].join(", ")}) changed.\n` + + `Commit subjects in range:\n` + + subjects.map((s) => ` - ${s}`).join("\n") + + '\nEither re-scope the commit to start with "deps:" or explain the lockfile drift.', + ); + process.exit(1); +} + +main(); diff --git a/scripts/package-mac-app.sh b/scripts/package-mac-app.sh index fa422c8e..4188048d 100755 --- a/scripts/package-mac-app.sh +++ b/scripts/package-mac-app.sh @@ -208,7 +208,7 @@ rm -rf "$APP_ROOT/Contents/Resources/DeviceModels" cp -R "$ROOT_DIR/apps/macos/Sources/Mayros/Resources/DeviceModels" "$APP_ROOT/Contents/Resources/DeviceModels" echo "πŸ“¦ Copying model catalog" -MODEL_CATALOG_SRC="$ROOT_DIR/node_modules/@mariozechner/pi-ai/dist/models.generated.js" +MODEL_CATALOG_SRC="$ROOT_DIR/node_modules/@earendil-works/pi-ai/dist/models.generated.js" MODEL_CATALOG_DEST="$APP_ROOT/Contents/Resources/models.generated.js" if [ -f "$MODEL_CATALOG_SRC" ]; then cp "$MODEL_CATALOG_SRC" "$MODEL_CATALOG_DEST" diff --git a/skills/coding-agent/SKILL.md b/skills/coding-agent/SKILL.md index d7894afc..36effceb 100644 --- a/skills/coding-agent/SKILL.md +++ b/skills/coding-agent/SKILL.md @@ -176,7 +176,7 @@ bash pty:true workdir:~/project command:"opencode run 'Your task'" ## Pi Coding Agent ```bash -# Install: npm install -g @mariozechner/pi-coding-agent +# Install: npm install -g @earendil-works/pi-coding-agent bash pty:true workdir:~/project command:"pi 'Your task'" # Non-interactive mode (PTY still recommended) diff --git a/src/agents/anthropic-payload-log.ts b/src/agents/anthropic-payload-log.ts index e663cad1..ff05fcfc 100644 --- a/src/agents/anthropic-payload-log.ts +++ b/src/agents/anthropic-payload-log.ts @@ -1,7 +1,7 @@ import crypto from "node:crypto"; import path from "node:path"; -import type { AgentMessage, StreamFn } from "@mariozechner/pi-agent-core"; -import type { Api, Model } from "@mariozechner/pi-ai"; +import type { AgentMessage, StreamFn } from "@earendil-works/pi-agent-core"; +import type { Api, Model } from "@earendil-works/pi-ai"; import { resolveStateDir } from "../config/paths.js"; import { createSubsystemLogger } from "../logging/subsystem.js"; import { resolveUserPath } from "../utils.js"; @@ -134,7 +134,7 @@ export function createAnthropicPayloadLogger(params: { if (!isAnthropicModel(model)) { return streamFn(model, context, options); } - const nextOnPayload = (payload: unknown) => { + const nextOnPayload: NonNullable["onPayload"] = (payload, payloadModel) => { record({ ...base, ts: new Date().toISOString(), @@ -142,7 +142,7 @@ export function createAnthropicPayloadLogger(params: { payload, payloadDigest: digest(payload), }); - options?.onPayload?.(payload); + return options?.onPayload?.(payload, payloadModel); }; return streamFn(model, context, { ...options, diff --git a/src/agents/anthropic.setup-token.live.test.ts b/src/agents/anthropic.setup-token.live.test.ts index bfd211c0..ee0e046e 100644 --- a/src/agents/anthropic.setup-token.live.test.ts +++ b/src/agents/anthropic.setup-token.live.test.ts @@ -2,7 +2,8 @@ import { randomUUID } from "node:crypto"; import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; -import { type Api, completeSimple, type Model } from "@mariozechner/pi-ai"; +import type { Api, Model } from "@earendil-works/pi-ai"; +import { completeSimple } from "@earendil-works/pi-ai/compat"; import { describe, expect, it } from "vitest"; import { ANTHROPIC_SETUP_TOKEN_PREFIX, diff --git a/src/agents/apply-patch.ts b/src/agents/apply-patch.ts index ef756b37..818df6dc 100644 --- a/src/agents/apply-patch.ts +++ b/src/agents/apply-patch.ts @@ -1,6 +1,6 @@ import fs from "node:fs/promises"; import path from "node:path"; -import type { AgentTool } from "@mariozechner/pi-agent-core"; +import type { AgentTool } from "@earendil-works/pi-agent-core"; import { Type } from "@sinclair/typebox"; import { applyUpdateHunk } from "./apply-patch-update.js"; import { assertSandboxPath, resolveSandboxInputPath } from "./sandbox-paths.js"; diff --git a/src/agents/auth-profiles/oauth.ts b/src/agents/auth-profiles/oauth.ts index 0855e629..a1b9d8e7 100644 --- a/src/agents/auth-profiles/oauth.ts +++ b/src/agents/auth-profiles/oauth.ts @@ -1,9 +1,5 @@ -import { - getOAuthApiKey, - getOAuthProviders, - type OAuthCredentials, - type OAuthProvider, -} from "@mariozechner/pi-ai"; +import type { OAuthCredentials, OAuthProvider } from "@earendil-works/pi-ai"; +import { getOAuthApiKey, getOAuthProviders } from "@earendil-works/pi-ai/oauth"; import type { MayrosConfig } from "../../config/config.js"; import { withFileLock } from "../../infra/file-lock.js"; import { refreshQwenPortalCredentials } from "../../providers/qwen-portal-oauth.js"; diff --git a/src/agents/auth-profiles/store.ts b/src/agents/auth-profiles/store.ts index 4e6b1f91..1c6759d3 100644 --- a/src/agents/auth-profiles/store.ts +++ b/src/agents/auth-profiles/store.ts @@ -1,5 +1,5 @@ import fs from "node:fs"; -import type { OAuthCredentials } from "@mariozechner/pi-ai"; +import type { OAuthCredentials } from "@earendil-works/pi-ai"; import { resolveOAuthPath } from "../../config/paths.js"; import { withFileLock } from "../../infra/file-lock.js"; import { loadJsonFile, saveJsonFile } from "../../infra/json-file.js"; diff --git a/src/agents/auth-profiles/types.ts b/src/agents/auth-profiles/types.ts index c44467d0..9c14a6f8 100644 --- a/src/agents/auth-profiles/types.ts +++ b/src/agents/auth-profiles/types.ts @@ -1,4 +1,4 @@ -import type { OAuthCredentials } from "@mariozechner/pi-ai"; +import type { OAuthCredentials } from "@earendil-works/pi-ai"; import type { MayrosConfig } from "../../config/config.js"; export type ApiKeyCredential = { diff --git a/src/agents/bash-tools.exec-host-gateway.ts b/src/agents/bash-tools.exec-host-gateway.ts index d3cc26c4..ebffc67a 100644 --- a/src/agents/bash-tools.exec-host-gateway.ts +++ b/src/agents/bash-tools.exec-host-gateway.ts @@ -1,5 +1,5 @@ import crypto from "node:crypto"; -import type { AgentToolResult } from "@mariozechner/pi-agent-core"; +import type { AgentToolResult } from "@earendil-works/pi-agent-core"; import { addAllowlistEntry, type ExecAsk, diff --git a/src/agents/bash-tools.exec-host-node.ts b/src/agents/bash-tools.exec-host-node.ts index 3cca1bc1..11bcd210 100644 --- a/src/agents/bash-tools.exec-host-node.ts +++ b/src/agents/bash-tools.exec-host-node.ts @@ -1,5 +1,5 @@ import crypto from "node:crypto"; -import type { AgentToolResult } from "@mariozechner/pi-agent-core"; +import type { AgentToolResult } from "@earendil-works/pi-agent-core"; import { type ExecApprovalsFile, type ExecAsk, diff --git a/src/agents/bash-tools.exec-runtime.ts b/src/agents/bash-tools.exec-runtime.ts index caa63d0e..e459360e 100644 --- a/src/agents/bash-tools.exec-runtime.ts +++ b/src/agents/bash-tools.exec-runtime.ts @@ -1,5 +1,5 @@ import path from "node:path"; -import type { AgentToolResult } from "@mariozechner/pi-agent-core"; +import type { AgentToolResult } from "@earendil-works/pi-agent-core"; import { Type } from "@sinclair/typebox"; import type { ExecAsk, ExecHost, ExecSecurity } from "../infra/exec-approvals.js"; import { requestHeartbeatNow } from "../infra/heartbeat-wake.js"; diff --git a/src/agents/bash-tools.exec.ts b/src/agents/bash-tools.exec.ts index e5b9c5eb..84c23c71 100644 --- a/src/agents/bash-tools.exec.ts +++ b/src/agents/bash-tools.exec.ts @@ -1,6 +1,6 @@ import fs from "node:fs/promises"; import path from "node:path"; -import type { AgentTool, AgentToolResult } from "@mariozechner/pi-agent-core"; +import type { AgentTool, AgentToolResult } from "@earendil-works/pi-agent-core"; import { type ExecHost, maxAsk, minSecurity, resolveSafeBins } from "../infra/exec-approvals.js"; import { getTrustedSafeBinDirs } from "../infra/exec-safe-bin-trust.js"; import { diff --git a/src/agents/bash-tools.process.ts b/src/agents/bash-tools.process.ts index dbdb6f99..1ef1b0ab 100644 --- a/src/agents/bash-tools.process.ts +++ b/src/agents/bash-tools.process.ts @@ -1,4 +1,4 @@ -import type { AgentTool, AgentToolResult } from "@mariozechner/pi-agent-core"; +import type { AgentTool, AgentToolResult } from "@earendil-works/pi-agent-core"; import { Type } from "@sinclair/typebox"; import { formatDurationCompact } from "../infra/format-time/format-duration.ts"; import { getDiagnosticSessionState } from "../logging/diagnostic-session-state.js"; diff --git a/src/agents/byteplus.live.test.ts b/src/agents/byteplus.live.test.ts index 1c1b730a..15546d4f 100644 --- a/src/agents/byteplus.live.test.ts +++ b/src/agents/byteplus.live.test.ts @@ -1,4 +1,5 @@ -import { completeSimple, type Model } from "@mariozechner/pi-ai"; +import type { Model } from "@earendil-works/pi-ai"; +import { completeSimple } from "@earendil-works/pi-ai/compat"; import { describe, expect, it } from "vitest"; import { isTruthyEnvValue } from "../infra/env.js"; import { BYTEPLUS_CODING_BASE_URL, BYTEPLUS_DEFAULT_COST } from "./byteplus-models.js"; diff --git a/src/agents/cache-trace.test.ts b/src/agents/cache-trace.test.ts new file mode 100644 index 00000000..c6edcaab --- /dev/null +++ b/src/agents/cache-trace.test.ts @@ -0,0 +1,118 @@ +import { describe, expect, it } from "vitest"; +import type { MayrosConfig } from "../config/config.js"; +import { createCacheTrace } from "./cache-trace.js"; + +/** + * cache-trace drift detection unit tests. + * + * These cover the system-prompt drift signal added to wrapStreamFn: when the + * same session sees a different system digest between consecutive stream calls, + * a `cache:system-drift` event is emitted so cache-breaking changes become + * visible instead of silently inflating token cost. + */ + +type TraceLine = { stage: string; systemDigest?: string; previousSystemDigest?: string }; + +function makeTrace(sessionKey: string, lines: TraceLine[]) { + return createCacheTrace({ + cfg: { + diagnostics: { + cacheTrace: { + enabled: true, + includeSystem: true, + }, + }, + } as MayrosConfig, + env: {}, + sessionKey, + writer: { + filePath: "memory", + write: (line: string) => { + const parsed = JSON.parse(line) as TraceLine; + lines.push(parsed); + }, + }, + }); +} + +// Minimal StreamFn stub: returns an empty async iterable and records nothing. +function noopStreamFn() { + return (async function* () { + /* empty */ + })(); +} + +// The wrapper invokes streamFn(model, context, options). Build a minimal model +// stub and a typed context so tsgo accepts the call without constructing a full +// Model (which carries many fields irrelevant to drift detection). +type StreamCallContext = { system?: unknown; messages: unknown[] }; +function callWrapped( + wrapped: ReturnType>["wrapStreamFn"]> | undefined, + system: unknown, +) { + const model = { id: "m", provider: "anthropic" } as unknown as Parameters< + NonNullable + >[0]; + const context = { system, messages: [] } as StreamCallContext as Parameters< + NonNullable + >[1]; + wrapped?.(model, context, {}); +} + +describe("cache-trace drift detection", () => { + it("does not emit a drift event on the first stream call for a session", () => { + const lines: TraceLine[] = []; + const trace = makeTrace("sess-first", lines); + const wrapped = trace?.wrapStreamFn(noopStreamFn as never); + + callWrapped(wrapped, "system-A"); + + const drifts = lines.filter((l) => l.stage === "cache:system-drift"); + expect(drifts).toHaveLength(0); + }); + + it("emits a drift event when the system prompt changes between two calls of the same session", () => { + const lines: TraceLine[] = []; + const trace = makeTrace("sess-drift", lines); + const wrapped = trace?.wrapStreamFn(noopStreamFn as never); + + callWrapped(wrapped, "system-A"); + callWrapped(wrapped, "system-B"); + + const drifts = lines.filter((l) => l.stage === "cache:system-drift"); + expect(drifts).toHaveLength(1); + expect(drifts[0]?.previousSystemDigest).toBeDefined(); + expect(drifts[0]?.systemDigest).toBeDefined(); + expect(drifts[0]?.previousSystemDigest).not.toBe(drifts[0]?.systemDigest); + }); + + it("does not emit a drift event when the system prompt stays stable across calls", () => { + const lines: TraceLine[] = []; + const trace = makeTrace("sess-stable", lines); + const wrapped = trace?.wrapStreamFn(noopStreamFn as never); + + callWrapped(wrapped, "same-system"); + callWrapped(wrapped, "same-system"); + callWrapped(wrapped, "same-system"); + + const drifts = lines.filter((l) => l.stage === "cache:system-drift"); + expect(drifts).toHaveLength(0); + }); + + it("tracks sessions independently", () => { + const linesA: TraceLine[] = []; + const linesB: TraceLine[] = []; + const traceA = makeTrace("sess-a", linesA); + const traceB = makeTrace("sess-b", linesB); + + // Session A and B both start with the same system, then A changes. + callWrapped(traceA?.wrapStreamFn(noopStreamFn as never), "shared-system"); + callWrapped(traceB?.wrapStreamFn(noopStreamFn as never), "shared-system"); + callWrapped(traceA?.wrapStreamFn(noopStreamFn as never), "changed-system"); + + const driftsA = linesA.filter((l) => l.stage === "cache:system-drift"); + const driftsB = linesB.filter((l) => l.stage === "cache:system-drift"); + expect(driftsA).toHaveLength(1); + expect(driftsB).toHaveLength(0); + }); +}); diff --git a/src/agents/cache-trace.ts b/src/agents/cache-trace.ts index cc9a6786..8e0a2a9d 100644 --- a/src/agents/cache-trace.ts +++ b/src/agents/cache-trace.ts @@ -1,6 +1,6 @@ import crypto from "node:crypto"; import path from "node:path"; -import type { AgentMessage, StreamFn } from "@mariozechner/pi-agent-core"; +import type { AgentMessage, StreamFn } from "@earendil-works/pi-agent-core"; import type { MayrosConfig } from "../config/config.js"; import { resolveStateDir } from "../config/paths.js"; import { resolveUserPath } from "../utils.js"; @@ -15,6 +15,7 @@ export type CacheTraceStage = | "prompt:before" | "prompt:images" | "stream:context" + | "cache:system-drift" | "session:after"; export type CacheTraceEvent = { @@ -38,6 +39,8 @@ export type CacheTraceEvent = { messageFingerprints?: string[]; messagesDigest?: string; systemDigest?: string; + /** Previous system digest for the same session, when a drift is detected. */ + previousSystemDigest?: string; note?: string; error?: string; }; @@ -74,6 +77,14 @@ type CacheTraceWriter = QueuedFileWriter; const writers = new Map(); +/** + * Last system digest seen per session key, so we can detect when the system + * prompt drifts between consecutive turns of the same session. A drift with no + * operator-triggered change (model, thinking, channel) is the signature of a + * cache-breaking bug. Keyed by sessionKey (or sessionId as fallback). + */ +const lastSystemDigestBySession = new Map(); + function resolveCacheTraceConfig(params: CacheTraceInit): CacheTraceConfig { const env = params.env ?? process.env; const config = params.cfg?.diagnostics?.cacheTrace; @@ -222,6 +233,9 @@ export function createCacheTrace(params: CacheTraceInit): CacheTrace | null { if (payload.error) { event.error = payload.error; } + if (payload.previousSystemDigest !== undefined) { + event.previousSystemDigest = payload.previousSystemDigest; + } const line = safeJsonStringify(event); if (!line) { @@ -232,16 +246,37 @@ export function createCacheTrace(params: CacheTraceInit): CacheTrace | null { const wrapStreamFn: CacheTrace["wrapStreamFn"] = (streamFn) => { const wrapped: StreamFn = (model, context, options) => { + const system = (context as { system?: unknown }).system; + const messages = (context as { messages?: AgentMessage[] }).messages ?? []; recordStage("stream:context", { model: { id: model?.id, provider: model?.provider, api: model?.api, }, - system: (context as { system?: unknown }).system, - messages: (context as { messages?: AgentMessage[] }).messages ?? [], + system, + messages, options: (options ?? {}) as Record, }); + + // Detect system-prompt drift across turns of the same session. A + // different digest with no operator-triggered change (model/thinking) + // is the signature of a cache-breaking bug, per the Prompt Cache + // Discipline policy in AGENTS.md. + if (system !== undefined && params.sessionKey) { + const currentDigest = digest(system); + const previous = lastSystemDigestBySession.get(params.sessionKey); + if (previous !== undefined && previous !== currentDigest) { + recordStage("cache:system-drift", { + system, + systemDigest: currentDigest, + previousSystemDigest: previous, + note: "system prompt changed between turns without a session restart; investigate if no operator change triggered this", + }); + } + lastSystemDigestBySession.set(params.sessionKey, currentDigest); + } + return streamFn(model, context, options); }; return wrapped; diff --git a/src/agents/chutes-oauth.ts b/src/agents/chutes-oauth.ts index 02adf10c..a5d7c5d5 100644 --- a/src/agents/chutes-oauth.ts +++ b/src/agents/chutes-oauth.ts @@ -1,5 +1,5 @@ import { createHash, randomBytes } from "node:crypto"; -import type { OAuthCredentials } from "@mariozechner/pi-ai"; +import type { OAuthCredentials } from "@earendil-works/pi-ai"; export const CHUTES_OAUTH_ISSUER = "https://api.chutes.ai"; export const CHUTES_AUTHORIZE_ENDPOINT = `${CHUTES_OAUTH_ISSUER}/idp/authorize`; diff --git a/src/agents/cli-credentials.ts b/src/agents/cli-credentials.ts index 0d6d7c28..529d69d3 100644 --- a/src/agents/cli-credentials.ts +++ b/src/agents/cli-credentials.ts @@ -2,7 +2,7 @@ import { execFileSync, execSync } from "node:child_process"; import { createHash } from "node:crypto"; import fs from "node:fs"; import path from "node:path"; -import type { OAuthCredentials, OAuthProvider } from "@mariozechner/pi-ai"; +import type { OAuthCredentials, OAuthProvider } from "@earendil-works/pi-ai"; import { loadJsonFile, saveJsonFile } from "../infra/json-file.js"; import { createSubsystemLogger } from "../logging/subsystem.js"; import { resolveUserPath } from "../utils.js"; diff --git a/src/agents/cli-runner.ts b/src/agents/cli-runner.ts index b97cc3f1..45d6f37e 100644 --- a/src/agents/cli-runner.ts +++ b/src/agents/cli-runner.ts @@ -1,4 +1,4 @@ -import type { ImageContent } from "@mariozechner/pi-ai"; +import type { ImageContent } from "@earendil-works/pi-ai"; import { resolveHeartbeatPrompt } from "../auto-reply/heartbeat.js"; import type { ThinkLevel } from "../auto-reply/thinking.js"; import type { MayrosConfig } from "../config/config.js"; diff --git a/src/agents/cli-runner/helpers.ts b/src/agents/cli-runner/helpers.ts index 8acb1eb3..544d1ccd 100644 --- a/src/agents/cli-runner/helpers.ts +++ b/src/agents/cli-runner/helpers.ts @@ -2,8 +2,8 @@ import crypto from "node:crypto"; import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; -import type { AgentTool } from "@mariozechner/pi-agent-core"; -import type { ImageContent } from "@mariozechner/pi-ai"; +import type { AgentTool } from "@earendil-works/pi-agent-core"; +import type { ImageContent } from "@earendil-works/pi-ai"; import type { ThinkLevel } from "../../auto-reply/thinking.js"; import type { MayrosConfig } from "../../config/config.js"; import type { CliBackendConfig } from "../../config/types.js"; diff --git a/src/agents/compaction.e2e.test.ts b/src/agents/compaction.e2e.test.ts index de5f4ec4..5a7f7609 100644 --- a/src/agents/compaction.e2e.test.ts +++ b/src/agents/compaction.e2e.test.ts @@ -1,4 +1,4 @@ -import type { AgentMessage } from "@mariozechner/pi-agent-core"; +import type { AgentMessage } from "@earendil-works/pi-agent-core"; import { describe, expect, it } from "vitest"; import { estimateMessagesTokens, diff --git a/src/agents/compaction.retry.test.ts b/src/agents/compaction.retry.test.ts index 50fe043c..a4e8b83b 100644 --- a/src/agents/compaction.retry.test.ts +++ b/src/agents/compaction.retry.test.ts @@ -1,11 +1,11 @@ -import type { AgentMessage } from "@mariozechner/pi-agent-core"; -import type { ExtensionContext } from "@mariozechner/pi-coding-agent"; -import * as piCodingAgent from "@mariozechner/pi-coding-agent"; +import type { AgentMessage } from "@earendil-works/pi-agent-core"; +import type { ExtensionContext } from "@earendil-works/pi-coding-agent"; +import * as piCodingAgent from "@earendil-works/pi-coding-agent"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { retryAsync } from "../infra/retry.js"; // Mock the external generateSummary function -vi.mock("@mariozechner/pi-coding-agent", async (importOriginal) => { +vi.mock("@earendil-works/pi-coding-agent", async (importOriginal) => { const actual = await importOriginal(); return { ...actual, @@ -44,6 +44,7 @@ describe("compaction retry integration", () => { testModel, 1000, "test-api-key", + undefined, new AbortController().signal, ), { @@ -71,6 +72,7 @@ describe("compaction retry integration", () => { testModel, 1000, "test-api-key", + undefined, new AbortController().signal, ), { @@ -100,6 +102,7 @@ describe("compaction retry integration", () => { testModel, 1000, "test-api-key", + undefined, new AbortController().signal, ), { @@ -126,6 +129,7 @@ describe("compaction retry integration", () => { testModel, 1000, "test-api-key", + undefined, new AbortController().signal, ), { @@ -156,6 +160,7 @@ describe("compaction retry integration", () => { testModel, 1000, "test-api-key", + undefined, new AbortController().signal, ), { diff --git a/src/agents/compaction.tool-result-details.e2e.test.ts b/src/agents/compaction.tool-result-details.e2e.test.ts index 79c883a7..60dfda77 100644 --- a/src/agents/compaction.tool-result-details.e2e.test.ts +++ b/src/agents/compaction.tool-result-details.e2e.test.ts @@ -1,4 +1,4 @@ -import type { AgentMessage } from "@mariozechner/pi-agent-core"; +import type { AgentMessage } from "@earendil-works/pi-agent-core"; import { beforeEach, describe, expect, it, vi } from "vitest"; const piCodingAgentMocks = vi.hoisted(() => ({ @@ -6,9 +6,9 @@ const piCodingAgentMocks = vi.hoisted(() => ({ estimateTokens: vi.fn(() => 1), })); -vi.mock("@mariozechner/pi-coding-agent", async () => { - const actual = await vi.importActual( - "@mariozechner/pi-coding-agent", +vi.mock("@earendil-works/pi-coding-agent", async () => { + const actual = await vi.importActual( + "@earendil-works/pi-coding-agent", ); return { ...actual, diff --git a/src/agents/compaction.ts b/src/agents/compaction.ts index d60d1af2..04546bb3 100644 --- a/src/agents/compaction.ts +++ b/src/agents/compaction.ts @@ -1,6 +1,6 @@ -import type { AgentMessage } from "@mariozechner/pi-agent-core"; -import type { ExtensionContext } from "@mariozechner/pi-coding-agent"; -import { estimateTokens, generateSummary } from "@mariozechner/pi-coding-agent"; +import type { AgentMessage } from "@earendil-works/pi-agent-core"; +import type { ExtensionContext } from "@earendil-works/pi-coding-agent"; +import { estimateTokens, generateSummary } from "@earendil-works/pi-coding-agent"; import { retryAsync } from "../infra/retry.js"; import { DEFAULT_CONTEXT_TOKENS } from "./defaults.js"; import { repairToolUseResultPairing, stripToolResultDetails } from "./session-transcript-repair.js"; @@ -167,6 +167,7 @@ async function summarizeChunks(params: { params.model, params.reserveTokens, params.apiKey, + undefined, params.signal, params.customInstructions, summary, diff --git a/src/agents/google-gemini-switch.live.test.ts b/src/agents/google-gemini-switch.live.test.ts index 7c253b03..ad1f393e 100644 --- a/src/agents/google-gemini-switch.live.test.ts +++ b/src/agents/google-gemini-switch.live.test.ts @@ -1,4 +1,4 @@ -import { completeSimple, getModel } from "@mariozechner/pi-ai"; +import { completeSimple, getModel } from "@earendil-works/pi-ai/compat"; import { Type } from "@sinclair/typebox"; import { describe, expect, it } from "vitest"; import { isTruthyEnvValue } from "../infra/env.js"; diff --git a/src/agents/minimax.live.test.ts b/src/agents/minimax.live.test.ts index ca380f2c..e3b76adb 100644 --- a/src/agents/minimax.live.test.ts +++ b/src/agents/minimax.live.test.ts @@ -1,4 +1,5 @@ -import { completeSimple, type Model } from "@mariozechner/pi-ai"; +import type { Model } from "@earendil-works/pi-ai"; +import { completeSimple } from "@earendil-works/pi-ai/compat"; import { describe, expect, it } from "vitest"; import { isTruthyEnvValue } from "../infra/env.js"; diff --git a/src/agents/model-auth.e2e.test.ts b/src/agents/model-auth.e2e.test.ts index 4c6b4cc6..2d2b8527 100644 --- a/src/agents/model-auth.e2e.test.ts +++ b/src/agents/model-auth.e2e.test.ts @@ -1,7 +1,7 @@ import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; -import type { Api, Model } from "@mariozechner/pi-ai"; +import type { Api, Model } from "@earendil-works/pi-ai"; import { describe, expect, it } from "vitest"; import { captureEnv } from "../test-utils/env.js"; import { ensureAuthProfileStore } from "./auth-profiles.js"; diff --git a/src/agents/model-auth.ts b/src/agents/model-auth.ts index c1c4a510..8eb8ba98 100644 --- a/src/agents/model-auth.ts +++ b/src/agents/model-auth.ts @@ -1,5 +1,6 @@ import path from "node:path"; -import { type Api, getEnvApiKey, type Model } from "@mariozechner/pi-ai"; +import type { Api, Model } from "@earendil-works/pi-ai"; +import { getEnvApiKey } from "@earendil-works/pi-ai/compat"; import { formatCliCommand } from "../cli/command-format.js"; import type { MayrosConfig } from "../config/config.js"; import type { ModelProviderAuthMode, ModelProviderConfig } from "../config/types.js"; diff --git a/src/agents/model-compat.e2e.test.ts b/src/agents/model-compat.e2e.test.ts index 95b4a0eb..2e44f413 100644 --- a/src/agents/model-compat.e2e.test.ts +++ b/src/agents/model-compat.e2e.test.ts @@ -1,4 +1,4 @@ -import type { Api, Model } from "@mariozechner/pi-ai"; +import type { Api, Model } from "@earendil-works/pi-ai"; import { describe, expect, it } from "vitest"; import { normalizeModelCompat } from "./model-compat.js"; diff --git a/src/agents/model-compat.ts b/src/agents/model-compat.ts index e7b428e8..6047c7c0 100644 --- a/src/agents/model-compat.ts +++ b/src/agents/model-compat.ts @@ -1,4 +1,4 @@ -import type { Api, Model } from "@mariozechner/pi-ai"; +import type { Api, Model } from "@earendil-works/pi-ai"; function isOpenAiCompletionsModel(model: Model): model is Model<"openai-completions"> { return model.api === "openai-completions"; diff --git a/src/agents/model-forward-compat.test.ts b/src/agents/model-forward-compat.test.ts index b2017213..915caaa4 100644 --- a/src/agents/model-forward-compat.test.ts +++ b/src/agents/model-forward-compat.test.ts @@ -1,4 +1,4 @@ -import type { Api, Model } from "@mariozechner/pi-ai"; +import type { Api, Model } from "@earendil-works/pi-ai"; import { describe, expect, it } from "vitest"; import { resolveForwardCompatModel } from "./model-forward-compat.js"; import type { ModelRegistry } from "./pi-model-discovery.js"; diff --git a/src/agents/model-forward-compat.ts b/src/agents/model-forward-compat.ts index 600b52a0..9671e52a 100644 --- a/src/agents/model-forward-compat.ts +++ b/src/agents/model-forward-compat.ts @@ -1,4 +1,4 @@ -import type { Api, Model } from "@mariozechner/pi-ai"; +import type { Api, Model } from "@earendil-works/pi-ai"; import { DEFAULT_CONTEXT_TOKENS } from "./defaults.js"; import { normalizeModelCompat } from "./model-compat.js"; import { normalizeProviderId } from "./model-selection.js"; diff --git a/src/agents/model-scan.ts b/src/agents/model-scan.ts index 3fe131d9..610eb9f9 100644 --- a/src/agents/model-scan.ts +++ b/src/agents/model-scan.ts @@ -1,12 +1,5 @@ -import { - type Context, - complete, - getEnvApiKey, - getModel, - type Model, - type OpenAICompletionsOptions, - type Tool, -} from "@mariozechner/pi-ai"; +import type { Context, Model, OpenAICompletionsOptions, Tool } from "@earendil-works/pi-ai"; +import { complete, getEnvApiKey, getModel } from "@earendil-works/pi-ai/compat"; import { Type } from "@sinclair/typebox"; import { inferParamBFromIdOrName } from "../shared/model-param-b.js"; diff --git a/src/agents/models.profiles.live.test.ts b/src/agents/models.profiles.live.test.ts index cb04fe6c..e4965ebb 100644 --- a/src/agents/models.profiles.live.test.ts +++ b/src/agents/models.profiles.live.test.ts @@ -1,4 +1,5 @@ -import { type Api, completeSimple, type Model } from "@mariozechner/pi-ai"; +import type { Api, Model } from "@earendil-works/pi-ai"; +import { completeSimple } from "@earendil-works/pi-ai/compat"; import { Type } from "@sinclair/typebox"; import { describe, expect, it } from "vitest"; import { loadConfig } from "../config/config.js"; diff --git a/src/agents/ollama-stream.ts b/src/agents/ollama-stream.ts index 39a19769..30059141 100644 --- a/src/agents/ollama-stream.ts +++ b/src/agents/ollama-stream.ts @@ -1,5 +1,5 @@ import { randomUUID } from "node:crypto"; -import type { StreamFn } from "@mariozechner/pi-agent-core"; +import type { StreamFn } from "@earendil-works/pi-agent-core"; import type { AssistantMessage, StopReason, @@ -7,8 +7,8 @@ import type { ToolCall, Tool, Usage, -} from "@mariozechner/pi-ai"; -import { createAssistantMessageEventStream } from "@mariozechner/pi-ai"; +} from "@earendil-works/pi-ai"; +import { createAssistantMessageEventStream } from "@earendil-works/pi-ai"; export const OLLAMA_NATIVE_BASE_URL = "http://127.0.0.1:11434"; diff --git a/src/agents/openai-responses.reasoning-replay.test.ts b/src/agents/openai-responses.reasoning-replay.test.ts index b5ccc50e..41efd555 100644 --- a/src/agents/openai-responses.reasoning-replay.test.ts +++ b/src/agents/openai-responses.reasoning-replay.test.ts @@ -1,5 +1,5 @@ -import type { AssistantMessage, Model, ToolResultMessage } from "@mariozechner/pi-ai"; -import { streamOpenAIResponses } from "@mariozechner/pi-ai"; +import type { AssistantMessage, Model, ToolResultMessage } from "@earendil-works/pi-ai"; +import { streamOpenAIResponses } from "@earendil-works/pi-ai/compat"; import { Type } from "@sinclair/typebox"; import { describe, expect, it } from "vitest"; diff --git a/src/agents/pi-embedded-helpers.formatassistanterrortext.e2e.test.ts b/src/agents/pi-embedded-helpers.formatassistanterrortext.e2e.test.ts index 1087d1b7..16ee72de 100644 --- a/src/agents/pi-embedded-helpers.formatassistanterrortext.e2e.test.ts +++ b/src/agents/pi-embedded-helpers.formatassistanterrortext.e2e.test.ts @@ -1,4 +1,4 @@ -import type { AssistantMessage } from "@mariozechner/pi-ai"; +import type { AssistantMessage } from "@earendil-works/pi-ai"; import { describe, expect, it } from "vitest"; import { BILLING_ERROR_USER_MESSAGE, diff --git a/src/agents/pi-embedded-helpers.sanitize-session-messages-images.removes-empty-assistant-text-blocks-but-preserves.e2e.test.ts b/src/agents/pi-embedded-helpers.sanitize-session-messages-images.removes-empty-assistant-text-blocks-but-preserves.e2e.test.ts index 878b1199..7e784f95 100644 --- a/src/agents/pi-embedded-helpers.sanitize-session-messages-images.removes-empty-assistant-text-blocks-but-preserves.e2e.test.ts +++ b/src/agents/pi-embedded-helpers.sanitize-session-messages-images.removes-empty-assistant-text-blocks-but-preserves.e2e.test.ts @@ -1,4 +1,4 @@ -import type { AgentMessage } from "@mariozechner/pi-agent-core"; +import type { AgentMessage } from "@earendil-works/pi-agent-core"; import { describe, expect, it } from "vitest"; import { sanitizeGoogleTurnOrdering, diff --git a/src/agents/pi-embedded-helpers.validate-turns.e2e.test.ts b/src/agents/pi-embedded-helpers.validate-turns.e2e.test.ts index ff1f9628..f143ed8c 100644 --- a/src/agents/pi-embedded-helpers.validate-turns.e2e.test.ts +++ b/src/agents/pi-embedded-helpers.validate-turns.e2e.test.ts @@ -1,4 +1,4 @@ -import type { AgentMessage } from "@mariozechner/pi-agent-core"; +import type { AgentMessage } from "@earendil-works/pi-agent-core"; import { describe, expect, it } from "vitest"; import { mergeConsecutiveUserTurns, diff --git a/src/agents/pi-embedded-helpers/bootstrap.ts b/src/agents/pi-embedded-helpers/bootstrap.ts index 3b544435..1220c21f 100644 --- a/src/agents/pi-embedded-helpers/bootstrap.ts +++ b/src/agents/pi-embedded-helpers/bootstrap.ts @@ -1,6 +1,6 @@ import fs from "node:fs/promises"; import path from "node:path"; -import type { AgentMessage } from "@mariozechner/pi-agent-core"; +import type { AgentMessage } from "@earendil-works/pi-agent-core"; import type { MayrosConfig } from "../../config/config.js"; import { truncateUtf16Safe } from "../../utils.js"; import type { WorkspaceBootstrapFile } from "../workspace.js"; diff --git a/src/agents/pi-embedded-helpers/errors.ts b/src/agents/pi-embedded-helpers/errors.ts index b7644660..d58d1ad9 100644 --- a/src/agents/pi-embedded-helpers/errors.ts +++ b/src/agents/pi-embedded-helpers/errors.ts @@ -1,4 +1,4 @@ -import type { AssistantMessage } from "@mariozechner/pi-ai"; +import type { AssistantMessage } from "@earendil-works/pi-ai"; import type { MayrosConfig } from "../../config/config.js"; import { formatSandboxToolPolicyBlockedMessage } from "../sandbox.js"; import { stableStringify } from "../stable-stringify.js"; diff --git a/src/agents/pi-embedded-helpers/images.ts b/src/agents/pi-embedded-helpers/images.ts index c3b4d0a3..2f289115 100644 --- a/src/agents/pi-embedded-helpers/images.ts +++ b/src/agents/pi-embedded-helpers/images.ts @@ -1,4 +1,4 @@ -import type { AgentMessage, AgentToolResult } from "@mariozechner/pi-agent-core"; +import type { AgentMessage, AgentToolResult } from "@earendil-works/pi-agent-core"; import type { ImageSanitizationLimits } from "../image-sanitization.js"; import type { ToolCallIdMode } from "../tool-call-id.js"; import { sanitizeToolCallIdsForCloudCodeAssist } from "../tool-call-id.js"; diff --git a/src/agents/pi-embedded-helpers/openai.ts b/src/agents/pi-embedded-helpers/openai.ts index c6a317aa..ddbf1f2e 100644 --- a/src/agents/pi-embedded-helpers/openai.ts +++ b/src/agents/pi-embedded-helpers/openai.ts @@ -1,4 +1,4 @@ -import type { AgentMessage } from "@mariozechner/pi-agent-core"; +import type { AgentMessage } from "@earendil-works/pi-agent-core"; type OpenAIThinkingBlock = { type?: unknown; diff --git a/src/agents/pi-embedded-helpers/turns.ts b/src/agents/pi-embedded-helpers/turns.ts index f6dddb20..3a01c801 100644 --- a/src/agents/pi-embedded-helpers/turns.ts +++ b/src/agents/pi-embedded-helpers/turns.ts @@ -1,4 +1,4 @@ -import type { AgentMessage } from "@mariozechner/pi-agent-core"; +import type { AgentMessage } from "@earendil-works/pi-agent-core"; function validateTurnsWithConsecutiveMerge(params: { messages: AgentMessage[]; diff --git a/src/agents/pi-embedded-runner-extraparams.e2e.test.ts b/src/agents/pi-embedded-runner-extraparams.e2e.test.ts index 57140582..5ab1663b 100644 --- a/src/agents/pi-embedded-runner-extraparams.e2e.test.ts +++ b/src/agents/pi-embedded-runner-extraparams.e2e.test.ts @@ -1,5 +1,5 @@ -import type { StreamFn } from "@mariozechner/pi-agent-core"; -import type { Context, Model, SimpleStreamOptions } from "@mariozechner/pi-ai"; +import type { StreamFn } from "@earendil-works/pi-agent-core"; +import type { Context, Model, SimpleStreamOptions } from "@earendil-works/pi-ai"; import { describe, expect, it } from "vitest"; import { applyExtraParamsToAgent, resolveExtraParams } from "./pi-embedded-runner.js"; @@ -99,7 +99,7 @@ describe("applyExtraParamsToAgent", () => { }) { const payload = { store: false }; const baseStreamFn: StreamFn = (_model, _context, options) => { - options?.onPayload?.(payload); + options?.onPayload?.(payload, _model); return {} as ReturnType; }; const agent = { streamFn: baseStreamFn }; @@ -295,7 +295,7 @@ describe("applyExtraParamsToAgent", () => { it("does not force store=true for Codex responses (Codex requires store=false)", () => { const payload = { store: false }; const baseStreamFn: StreamFn = (_model, _context, options) => { - options?.onPayload?.(payload); + options?.onPayload?.(payload, _model); return {} as ReturnType; }; const agent = { streamFn: baseStreamFn }; diff --git a/src/agents/pi-embedded-runner-extraparams.live.test.ts b/src/agents/pi-embedded-runner-extraparams.live.test.ts index 602d79dd..3af3b122 100644 --- a/src/agents/pi-embedded-runner-extraparams.live.test.ts +++ b/src/agents/pi-embedded-runner-extraparams.live.test.ts @@ -1,5 +1,5 @@ -import type { Model } from "@mariozechner/pi-ai"; -import { getModel, streamSimple } from "@mariozechner/pi-ai"; +import type { Model } from "@earendil-works/pi-ai"; +import { getModel, streamSimple } from "@earendil-works/pi-ai/compat"; import { describe, expect, it } from "vitest"; import type { MayrosConfig } from "../config/config.js"; import { isTruthyEnvValue } from "../infra/env.js"; diff --git a/src/agents/pi-embedded-runner.applygoogleturnorderingfix.e2e.test.ts b/src/agents/pi-embedded-runner.applygoogleturnorderingfix.e2e.test.ts index f4807b7d..7fb80157 100644 --- a/src/agents/pi-embedded-runner.applygoogleturnorderingfix.e2e.test.ts +++ b/src/agents/pi-embedded-runner.applygoogleturnorderingfix.e2e.test.ts @@ -1,5 +1,5 @@ -import type { AgentMessage } from "@mariozechner/pi-agent-core"; -import { SessionManager } from "@mariozechner/pi-coding-agent"; +import type { AgentMessage } from "@earendil-works/pi-agent-core"; +import { SessionManager } from "@earendil-works/pi-coding-agent"; import { describe, expect, it, vi } from "vitest"; import { applyGoogleTurnOrderingFix } from "./pi-embedded-runner.js"; diff --git a/src/agents/pi-embedded-runner.e2e.test.ts b/src/agents/pi-embedded-runner.e2e.test.ts index 8d92aab5..9896bce0 100644 --- a/src/agents/pi-embedded-runner.e2e.test.ts +++ b/src/agents/pi-embedded-runner.e2e.test.ts @@ -6,8 +6,9 @@ import "./test-helpers/fast-coding-tools.js"; import type { MayrosConfig } from "../config/config.js"; import { ensureMayrosModelsJson } from "./models-config.js"; -vi.mock("@mariozechner/pi-ai", async () => { - const actual = await vi.importActual("@mariozechner/pi-ai"); +vi.mock("@earendil-works/pi-ai", async () => { + const actual = + await vi.importActual("@earendil-works/pi-ai"); const buildAssistantMessage = (model: { api: string; provider: string; id: string }) => ({ role: "assistant" as const, @@ -152,7 +153,7 @@ const testSessionKey = "agent:test:embedded"; const immediateEnqueue = async (task: () => Promise) => task(); const runWithOrphanedSingleUserMessage = async (text: string) => { - const { SessionManager } = await import("@mariozechner/pi-coding-agent"); + const { SessionManager } = await import("@earendil-works/pi-coding-agent"); const sessionFile = nextSessionFile(); const sessionManager = SessionManager.open(sessionFile); sessionManager.appendMessage({ @@ -418,7 +419,7 @@ describe("runEmbeddedPiAgent", () => { "appends new user + assistant after existing transcript entries", { timeout: 90_000 }, async () => { - const { SessionManager } = await import("@mariozechner/pi-coding-agent"); + const { SessionManager } = await import("@earendil-works/pi-coding-agent"); const sessionFile = nextSessionFile(); const sessionManager = SessionManager.open(sessionFile); diff --git a/src/agents/pi-embedded-runner.google-sanitize-thinking.e2e.test.ts b/src/agents/pi-embedded-runner.google-sanitize-thinking.e2e.test.ts index f716ff32..b811f1c9 100644 --- a/src/agents/pi-embedded-runner.google-sanitize-thinking.e2e.test.ts +++ b/src/agents/pi-embedded-runner.google-sanitize-thinking.e2e.test.ts @@ -1,5 +1,5 @@ -import type { AgentMessage } from "@mariozechner/pi-agent-core"; -import { SessionManager } from "@mariozechner/pi-coding-agent"; +import type { AgentMessage } from "@earendil-works/pi-agent-core"; +import { SessionManager } from "@earendil-works/pi-coding-agent"; import { describe, expect, it } from "vitest"; import { sanitizeSessionHistory } from "./pi-embedded-runner/google.js"; diff --git a/src/agents/pi-embedded-runner.guard.e2e.test.ts b/src/agents/pi-embedded-runner.guard.e2e.test.ts index d93a3520..1b239b4c 100644 --- a/src/agents/pi-embedded-runner.guard.e2e.test.ts +++ b/src/agents/pi-embedded-runner.guard.e2e.test.ts @@ -1,5 +1,5 @@ -import type { AgentMessage } from "@mariozechner/pi-agent-core"; -import { SessionManager } from "@mariozechner/pi-coding-agent"; +import type { AgentMessage } from "@earendil-works/pi-agent-core"; +import { SessionManager } from "@earendil-works/pi-coding-agent"; import { describe, expect, it } from "vitest"; import { guardSessionManager } from "./session-tool-result-guard-wrapper.js"; import { sanitizeToolUseResultPairing } from "./session-transcript-repair.js"; diff --git a/src/agents/pi-embedded-runner.guard.waitforidle-before-flush.test.ts b/src/agents/pi-embedded-runner.guard.waitforidle-before-flush.test.ts index d0396039..3cbcc91b 100644 --- a/src/agents/pi-embedded-runner.guard.waitforidle-before-flush.test.ts +++ b/src/agents/pi-embedded-runner.guard.waitforidle-before-flush.test.ts @@ -1,5 +1,5 @@ -import type { AgentMessage } from "@mariozechner/pi-agent-core"; -import { SessionManager } from "@mariozechner/pi-coding-agent"; +import type { AgentMessage } from "@earendil-works/pi-agent-core"; +import { SessionManager } from "@earendil-works/pi-coding-agent"; import { afterEach, describe, expect, it, vi } from "vitest"; import { flushPendingToolResultsAfterIdle } from "./pi-embedded-runner/wait-for-idle-before-flush.js"; import { guardSessionManager } from "./session-tool-result-guard-wrapper.js"; diff --git a/src/agents/pi-embedded-runner.limithistoryturns.e2e.test.ts b/src/agents/pi-embedded-runner.limithistoryturns.e2e.test.ts index e9cbbc5e..f1be64b1 100644 --- a/src/agents/pi-embedded-runner.limithistoryturns.e2e.test.ts +++ b/src/agents/pi-embedded-runner.limithistoryturns.e2e.test.ts @@ -1,4 +1,4 @@ -import type { AgentMessage } from "@mariozechner/pi-agent-core"; +import type { AgentMessage } from "@earendil-works/pi-agent-core"; import { describe, expect, it } from "vitest"; import { limitHistoryTurns } from "./pi-embedded-runner.js"; diff --git a/src/agents/pi-embedded-runner.openai-tool-id-preservation.e2e.test.ts b/src/agents/pi-embedded-runner.openai-tool-id-preservation.e2e.test.ts index 5d3a86ee..cc2fab2b 100644 --- a/src/agents/pi-embedded-runner.openai-tool-id-preservation.e2e.test.ts +++ b/src/agents/pi-embedded-runner.openai-tool-id-preservation.e2e.test.ts @@ -1,4 +1,4 @@ -import type { AgentMessage } from "@mariozechner/pi-agent-core"; +import type { AgentMessage } from "@earendil-works/pi-agent-core"; import { describe, expect, it } from "vitest"; import { makeInMemorySessionManager, diff --git a/src/agents/pi-embedded-runner.run-embedded-pi-agent.auth-profile-rotation.e2e.test.ts b/src/agents/pi-embedded-runner.run-embedded-pi-agent.auth-profile-rotation.e2e.test.ts index 5baa7954..6ad01c93 100644 --- a/src/agents/pi-embedded-runner.run-embedded-pi-agent.auth-profile-rotation.e2e.test.ts +++ b/src/agents/pi-embedded-runner.run-embedded-pi-agent.auth-profile-rotation.e2e.test.ts @@ -1,7 +1,7 @@ import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; -import type { AssistantMessage } from "@mariozechner/pi-ai"; +import type { AssistantMessage } from "@earendil-works/pi-ai"; import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import type { MayrosConfig } from "../config/config.js"; import type { EmbeddedRunAttemptResult } from "./pi-embedded-runner/run/types.js"; diff --git a/src/agents/pi-embedded-runner.sanitize-session-history.test-harness.ts b/src/agents/pi-embedded-runner.sanitize-session-history.test-harness.ts index bb371798..8990e14d 100644 --- a/src/agents/pi-embedded-runner.sanitize-session-history.test-harness.ts +++ b/src/agents/pi-embedded-runner.sanitize-session-history.test-harness.ts @@ -1,5 +1,5 @@ -import type { AgentMessage } from "@mariozechner/pi-agent-core"; -import type { SessionManager } from "@mariozechner/pi-coding-agent"; +import type { AgentMessage } from "@earendil-works/pi-agent-core"; +import type { SessionManager } from "@earendil-works/pi-coding-agent"; import { expect, vi } from "vitest"; import * as helpers from "./pi-embedded-helpers.js"; diff --git a/src/agents/pi-embedded-runner.sanitize-session-history.test.ts b/src/agents/pi-embedded-runner.sanitize-session-history.test.ts index 665e9879..e48979ca 100644 --- a/src/agents/pi-embedded-runner.sanitize-session-history.test.ts +++ b/src/agents/pi-embedded-runner.sanitize-session-history.test.ts @@ -1,4 +1,4 @@ -import type { AgentMessage } from "@mariozechner/pi-agent-core"; +import type { AgentMessage } from "@earendil-works/pi-agent-core"; import { beforeEach, describe, expect, it, vi } from "vitest"; import * as helpers from "./pi-embedded-helpers.js"; import { diff --git a/src/agents/pi-embedded-runner.splitsdktools.e2e.test.ts b/src/agents/pi-embedded-runner.splitsdktools.e2e.test.ts index 9a376ebf..4ee6fcd1 100644 --- a/src/agents/pi-embedded-runner.splitsdktools.e2e.test.ts +++ b/src/agents/pi-embedded-runner.splitsdktools.e2e.test.ts @@ -1,4 +1,4 @@ -import type { AgentTool, AgentToolResult } from "@mariozechner/pi-agent-core"; +import type { AgentTool, AgentToolResult } from "@earendil-works/pi-agent-core"; import { Type } from "@sinclair/typebox"; import { describe, expect, it } from "vitest"; import { splitSdkTools } from "./pi-embedded-runner.js"; diff --git a/src/agents/pi-embedded-runner/compact.ts b/src/agents/pi-embedded-runner/compact.ts index 1c656b92..ceae1117 100644 --- a/src/agents/pi-embedded-runner/compact.ts +++ b/src/agents/pi-embedded-runner/compact.ts @@ -1,13 +1,13 @@ import fs from "node:fs/promises"; import os from "node:os"; -import type { AgentMessage } from "@mariozechner/pi-agent-core"; +import type { AgentMessage } from "@earendil-works/pi-agent-core"; import { createAgentSession, DefaultResourceLoader, estimateTokens, SessionManager, SettingsManager, -} from "@mariozechner/pi-coding-agent"; +} from "@earendil-works/pi-coding-agent"; import { resolveHeartbeatPrompt } from "../../auto-reply/heartbeat.js"; import type { ReasoningLevel, ThinkLevel } from "../../auto-reply/thinking.js"; import { resolveChannelCapabilities } from "../../config/channel-capabilities.js"; @@ -611,7 +611,7 @@ export async function compactEmbeddedPiSessionDirect( ? sanitizeToolUseResultPairing(truncated) : truncated; if (limited.length > 0) { - session.agent.replaceMessages(limited); + session.agent.state.messages = limited; } // Run before_compaction hooks (fire-and-forget). // The session JSONL already contains all messages on disk, so plugins diff --git a/src/agents/pi-embedded-runner/extensions.ts b/src/agents/pi-embedded-runner/extensions.ts index a418bf68..ce4bdb0b 100644 --- a/src/agents/pi-embedded-runner/extensions.ts +++ b/src/agents/pi-embedded-runner/extensions.ts @@ -1,5 +1,5 @@ -import type { Api, Model } from "@mariozechner/pi-ai"; -import type { ExtensionFactory, SessionManager } from "@mariozechner/pi-coding-agent"; +import type { Api, Model } from "@earendil-works/pi-ai"; +import type { ExtensionFactory, SessionManager } from "@earendil-works/pi-coding-agent"; import type { MayrosConfig } from "../../config/config.js"; import { resolveContextWindowInfo } from "../context-window-guard.js"; import { DEFAULT_CONTEXT_TOKENS } from "../defaults.js"; diff --git a/src/agents/pi-embedded-runner/extra-params.cache-retention-default.test.ts b/src/agents/pi-embedded-runner/extra-params.cache-retention-default.test.ts index cd093a86..640f4e29 100644 --- a/src/agents/pi-embedded-runner/extra-params.cache-retention-default.test.ts +++ b/src/agents/pi-embedded-runner/extra-params.cache-retention-default.test.ts @@ -1,4 +1,4 @@ -import type { StreamFn } from "@mariozechner/pi-agent-core"; +import type { StreamFn } from "@earendil-works/pi-agent-core"; import { describe, expect, it, vi } from "vitest"; import { applyExtraParamsToAgent } from "../pi-embedded-runner.js"; diff --git a/src/agents/pi-embedded-runner/extra-params.ts b/src/agents/pi-embedded-runner/extra-params.ts index 5addc333..470effae 100644 --- a/src/agents/pi-embedded-runner/extra-params.ts +++ b/src/agents/pi-embedded-runner/extra-params.ts @@ -1,6 +1,6 @@ -import type { StreamFn } from "@mariozechner/pi-agent-core"; -import type { SimpleStreamOptions } from "@mariozechner/pi-ai"; -import { streamSimple } from "@mariozechner/pi-ai"; +import type { StreamFn } from "@earendil-works/pi-agent-core"; +import type { ProviderHeaders, SimpleStreamOptions } from "@earendil-works/pi-ai"; +import { streamSimple } from "@earendil-works/pi-ai/compat"; import type { MayrosConfig } from "../../config/config.js"; import { log } from "./logger.js"; @@ -152,11 +152,11 @@ function createOpenAIResponsesStoreWrapper(baseStreamFn: StreamFn | undefined): const originalOnPayload = options?.onPayload; return underlying(model, context, { ...options, - onPayload: (payload) => { + onPayload: (payload, payloadModel) => { if (payload && typeof payload === "object") { (payload as { store?: unknown }).store = true; } - originalOnPayload?.(payload); + return originalOnPayload?.(payload, payloadModel); }, }); }; @@ -210,9 +210,9 @@ function resolveAnthropicBetas( } function mergeAnthropicBetaHeader( - headers: Record | undefined, + headers: ProviderHeaders | undefined, betas: string[], -): Record { +): ProviderHeaders { const merged = { ...headers }; const existingKey = Object.keys(merged).find((key) => key.toLowerCase() === "anthropic-beta"); const existing = existingKey ? parseHeaderList(merged[existingKey]) : []; @@ -302,12 +302,12 @@ function createZaiToolStreamWrapper( const originalOnPayload = options?.onPayload; return underlying(model, context, { ...options, - onPayload: (payload) => { + onPayload: (payload, payloadModel) => { if (payload && typeof payload === "object") { // Inject tool_stream: true for Z.AI API (payload as Record).tool_stream = true; } - originalOnPayload?.(payload); + return originalOnPayload?.(payload, payloadModel); }, }); }; diff --git a/src/agents/pi-embedded-runner/extra-params.zai-tool-stream.test.ts b/src/agents/pi-embedded-runner/extra-params.zai-tool-stream.test.ts index 3a757cea..59dd83d5 100644 --- a/src/agents/pi-embedded-runner/extra-params.zai-tool-stream.test.ts +++ b/src/agents/pi-embedded-runner/extra-params.zai-tool-stream.test.ts @@ -1,10 +1,10 @@ -import type { StreamFn } from "@mariozechner/pi-agent-core"; -import type { Context, Model, SimpleStreamOptions } from "@mariozechner/pi-ai"; +import type { StreamFn } from "@earendil-works/pi-agent-core"; +import type { Context, Model, SimpleStreamOptions } from "@earendil-works/pi-ai"; import { describe, expect, it, vi } from "vitest"; import { applyExtraParamsToAgent } from "./extra-params.js"; // Mock streamSimple for testing -vi.mock("@mariozechner/pi-ai", () => ({ +vi.mock("@earendil-works/pi-ai", () => ({ streamSimple: vi.fn(() => ({ push: vi.fn(), result: vi.fn(), @@ -22,7 +22,7 @@ type ToolStreamCase = { function runToolStreamCase(params: ToolStreamCase) { const payload: Record = { model: params.model.id, messages: [] }; const baseStreamFn: StreamFn = (_model, _context, options) => { - options?.onPayload?.(payload); + options?.onPayload?.(payload, _model); return {} as ReturnType; }; const agent = { streamFn: baseStreamFn }; diff --git a/src/agents/pi-embedded-runner/google.e2e.test.ts b/src/agents/pi-embedded-runner/google.e2e.test.ts index f5e331b1..e5d41dc5 100644 --- a/src/agents/pi-embedded-runner/google.e2e.test.ts +++ b/src/agents/pi-embedded-runner/google.e2e.test.ts @@ -1,4 +1,4 @@ -import type { AgentTool } from "@mariozechner/pi-agent-core"; +import type { AgentTool } from "@earendil-works/pi-agent-core"; import { describe, expect, it } from "vitest"; import { sanitizeToolsForGoogle } from "./google.js"; diff --git a/src/agents/pi-embedded-runner/google.ts b/src/agents/pi-embedded-runner/google.ts index 394dddf7..5633f43c 100644 --- a/src/agents/pi-embedded-runner/google.ts +++ b/src/agents/pi-embedded-runner/google.ts @@ -1,7 +1,7 @@ import { EventEmitter } from "node:events"; -import type { AgentMessage, AgentTool } from "@mariozechner/pi-agent-core"; -import type { SessionManager } from "@mariozechner/pi-coding-agent"; -import type { TSchema } from "@sinclair/typebox"; +import type { AgentMessage, AgentTool } from "@earendil-works/pi-agent-core"; +import type { SessionManager } from "@earendil-works/pi-coding-agent"; +import type { TSchema } from "@earendil-works/pi-ai"; import type { MayrosConfig } from "../../config/config.js"; import { registerUnhandledRejectionHandler } from "../../infra/unhandled-rejections.js"; import { diff --git a/src/agents/pi-embedded-runner/history.ts b/src/agents/pi-embedded-runner/history.ts index bf3228f9..2113341e 100644 --- a/src/agents/pi-embedded-runner/history.ts +++ b/src/agents/pi-embedded-runner/history.ts @@ -1,4 +1,4 @@ -import type { AgentMessage } from "@mariozechner/pi-agent-core"; +import type { AgentMessage } from "@earendil-works/pi-agent-core"; import type { MayrosConfig } from "../../config/config.js"; const THREAD_SUFFIX_REGEX = /^(.*)(?::(?:thread|topic):\d+)$/i; diff --git a/src/agents/pi-embedded-runner/model.ts b/src/agents/pi-embedded-runner/model.ts index 7fa32f02..fb08c083 100644 --- a/src/agents/pi-embedded-runner/model.ts +++ b/src/agents/pi-embedded-runner/model.ts @@ -1,4 +1,4 @@ -import type { Api, Model } from "@mariozechner/pi-ai"; +import type { Api, Model } from "@earendil-works/pi-ai"; import type { MayrosConfig } from "../../config/config.js"; import type { ModelDefinitionConfig } from "../../config/types.js"; import { resolveMayrosAgentDir } from "../agent-paths.js"; diff --git a/src/agents/pi-embedded-runner/run/attempt.e2e.test.ts b/src/agents/pi-embedded-runner/run/attempt.e2e.test.ts index ca931138..11a4cb3a 100644 --- a/src/agents/pi-embedded-runner/run/attempt.e2e.test.ts +++ b/src/agents/pi-embedded-runner/run/attempt.e2e.test.ts @@ -1,5 +1,5 @@ -import type { AgentMessage } from "@mariozechner/pi-agent-core"; -import type { ImageContent } from "@mariozechner/pi-ai"; +import type { AgentMessage } from "@earendil-works/pi-agent-core"; +import type { ImageContent } from "@earendil-works/pi-ai"; import { describe, expect, it } from "vitest"; import { injectHistoryImagesIntoMessages } from "./attempt.js"; diff --git a/src/agents/pi-embedded-runner/run/attempt.ts b/src/agents/pi-embedded-runner/run/attempt.ts index ebf3e740..0f36626b 100644 --- a/src/agents/pi-embedded-runner/run/attempt.ts +++ b/src/agents/pi-embedded-runner/run/attempt.ts @@ -1,14 +1,14 @@ import fs from "node:fs/promises"; import os from "node:os"; -import type { AgentMessage } from "@mariozechner/pi-agent-core"; -import type { ImageContent } from "@mariozechner/pi-ai"; -import { streamSimple } from "@mariozechner/pi-ai"; +import type { AgentMessage } from "@earendil-works/pi-agent-core"; +import type { ImageContent } from "@earendil-works/pi-ai"; +import { streamSimple } from "@earendil-works/pi-ai/compat"; import { createAgentSession, DefaultResourceLoader, SessionManager, SettingsManager, -} from "@mariozechner/pi-coding-agent"; +} from "@earendil-works/pi-coding-agent"; import { resolveHeartbeatPrompt } from "../../../auto-reply/heartbeat.js"; import { resolveChannelCapabilities } from "../../../config/channel-capabilities.js"; import { getMachineDisplayName } from "../../../infra/machine-name.js"; @@ -657,7 +657,7 @@ export async function runEmbeddedAttempt( const ollamaBaseUrl = modelBaseUrl || providerBaseUrl || OLLAMA_NATIVE_BASE_URL; activeSession.agent.streamFn = createOllamaStreamFn(ollamaBaseUrl); } else { - // Force a stable streamFn reference so vitest can reliably mock @mariozechner/pi-ai. + // Force a stable streamFn reference so vitest can reliably mock @earendil-works/pi-ai. activeSession.agent.streamFn = streamSimple; } @@ -737,7 +737,7 @@ export async function runEmbeddedAttempt( : truncated; cacheTrace?.recordStage("session:limited", { messages: limited }); if (limited.length > 0) { - activeSession.agent.replaceMessages(limited); + activeSession.agent.state.messages = limited; } } catch (err) { await flushPendingToolResultsAfterIdle({ @@ -1002,7 +1002,7 @@ export async function runEmbeddedAttempt( const sanitizedOrphan = transcriptPolicy.sanitizeThinkingSignatures ? sanitizeAntigravityThinkingBlocks(sessionContext.messages) : sessionContext.messages; - activeSession.agent.replaceMessages(sanitizedOrphan); + activeSession.agent.state.messages = sanitizedOrphan; log.warn( `Removed orphaned user message to prevent consecutive user turns. ` + `runId=${params.runId} sessionId=${params.sessionId}`, @@ -1037,7 +1037,7 @@ export async function runEmbeddedAttempt( ); if (didMutate) { // Persist message mutations (e.g., injected history images) so we don't re-scan/reload. - activeSession.agent.replaceMessages(activeSession.messages); + activeSession.agent.state.messages = activeSession.messages; } cacheTrace?.recordStage("prompt:images", { diff --git a/src/agents/pi-embedded-runner/run/compaction-timeout.e2e.test.ts b/src/agents/pi-embedded-runner/run/compaction-timeout.e2e.test.ts index 7258a33b..75d20e32 100644 --- a/src/agents/pi-embedded-runner/run/compaction-timeout.e2e.test.ts +++ b/src/agents/pi-embedded-runner/run/compaction-timeout.e2e.test.ts @@ -1,4 +1,4 @@ -import type { AgentMessage } from "@mariozechner/pi-agent-core"; +import type { AgentMessage } from "@earendil-works/pi-agent-core"; import { describe, expect, it } from "vitest"; import { selectCompactionTimeoutSnapshot, diff --git a/src/agents/pi-embedded-runner/run/compaction-timeout.ts b/src/agents/pi-embedded-runner/run/compaction-timeout.ts index 45a94525..1dde57df 100644 --- a/src/agents/pi-embedded-runner/run/compaction-timeout.ts +++ b/src/agents/pi-embedded-runner/run/compaction-timeout.ts @@ -1,4 +1,4 @@ -import type { AgentMessage } from "@mariozechner/pi-agent-core"; +import type { AgentMessage } from "@earendil-works/pi-agent-core"; export type CompactionTimeoutSignal = { isTimeout: boolean; diff --git a/src/agents/pi-embedded-runner/run/images.ts b/src/agents/pi-embedded-runner/run/images.ts index c11f191e..f486bdd2 100644 --- a/src/agents/pi-embedded-runner/run/images.ts +++ b/src/agents/pi-embedded-runner/run/images.ts @@ -1,6 +1,6 @@ import path from "node:path"; import { fileURLToPath } from "node:url"; -import type { ImageContent } from "@mariozechner/pi-ai"; +import type { ImageContent } from "@earendil-works/pi-ai"; import { resolveUserPath } from "../../../utils.js"; import { loadWebMedia } from "../../../web/media.js"; import type { ImageSanitizationLimits } from "../../image-sanitization.js"; diff --git a/src/agents/pi-embedded-runner/run/params.ts b/src/agents/pi-embedded-runner/run/params.ts index 29e0a244..95c2ff37 100644 --- a/src/agents/pi-embedded-runner/run/params.ts +++ b/src/agents/pi-embedded-runner/run/params.ts @@ -1,4 +1,4 @@ -import type { ImageContent } from "@mariozechner/pi-ai"; +import type { ImageContent } from "@earendil-works/pi-ai"; import type { ReasoningLevel, ThinkLevel, VerboseLevel } from "../../../auto-reply/thinking.js"; import type { AgentStreamParams } from "../../../commands/agent/types.js"; import type { MayrosConfig } from "../../../config/config.js"; diff --git a/src/agents/pi-embedded-runner/run/payloads.e2e.test.ts b/src/agents/pi-embedded-runner/run/payloads.e2e.test.ts index 39767e8f..0c6a3e52 100644 --- a/src/agents/pi-embedded-runner/run/payloads.e2e.test.ts +++ b/src/agents/pi-embedded-runner/run/payloads.e2e.test.ts @@ -1,4 +1,4 @@ -import type { AssistantMessage } from "@mariozechner/pi-ai"; +import type { AssistantMessage } from "@earendil-works/pi-ai"; import { describe, expect, it } from "vitest"; import { formatBillingErrorMessage } from "../../pi-embedded-helpers.js"; import { makeAssistantMessageFixture } from "../../test-helpers/assistant-message-fixtures.js"; diff --git a/src/agents/pi-embedded-runner/run/payloads.ts b/src/agents/pi-embedded-runner/run/payloads.ts index 7d65bfc2..849d88e1 100644 --- a/src/agents/pi-embedded-runner/run/payloads.ts +++ b/src/agents/pi-embedded-runner/run/payloads.ts @@ -1,4 +1,4 @@ -import type { AssistantMessage } from "@mariozechner/pi-ai"; +import type { AssistantMessage } from "@earendil-works/pi-ai"; import { parseReplyDirectives } from "../../../auto-reply/reply/reply-directives.js"; import type { ReasoningLevel, VerboseLevel } from "../../../auto-reply/thinking.js"; import { stripReasoningTagsFromText } from "../../../shared/text/reasoning-tags.js"; diff --git a/src/agents/pi-embedded-runner/run/types.ts b/src/agents/pi-embedded-runner/run/types.ts index f0d12348..f2f69003 100644 --- a/src/agents/pi-embedded-runner/run/types.ts +++ b/src/agents/pi-embedded-runner/run/types.ts @@ -1,5 +1,5 @@ -import type { AgentMessage } from "@mariozechner/pi-agent-core"; -import type { Api, AssistantMessage, Model } from "@mariozechner/pi-ai"; +import type { AgentMessage } from "@earendil-works/pi-agent-core"; +import type { Api, AssistantMessage, Model } from "@earendil-works/pi-ai"; import type { ThinkLevel } from "../../../auto-reply/thinking.js"; import type { SessionSystemPromptReport } from "../../../config/sessions/types.js"; import type { MessagingToolSend } from "../../pi-embedded-messaging.js"; diff --git a/src/agents/pi-embedded-runner/sanitize-session-history.tool-result-details.e2e.test.ts b/src/agents/pi-embedded-runner/sanitize-session-history.tool-result-details.e2e.test.ts index 53c97356..50c63bb5 100644 --- a/src/agents/pi-embedded-runner/sanitize-session-history.tool-result-details.e2e.test.ts +++ b/src/agents/pi-embedded-runner/sanitize-session-history.tool-result-details.e2e.test.ts @@ -1,5 +1,5 @@ -import type { AgentMessage } from "@mariozechner/pi-agent-core"; -import { SessionManager } from "@mariozechner/pi-coding-agent"; +import type { AgentMessage } from "@earendil-works/pi-agent-core"; +import { SessionManager } from "@earendil-works/pi-coding-agent"; import { describe, expect, it } from "vitest"; import { sanitizeSessionHistory } from "./google.js"; diff --git a/src/agents/pi-embedded-runner/system-prompt.ts b/src/agents/pi-embedded-runner/system-prompt.ts index 67df4493..e9a88054 100644 --- a/src/agents/pi-embedded-runner/system-prompt.ts +++ b/src/agents/pi-embedded-runner/system-prompt.ts @@ -1,5 +1,5 @@ -import type { AgentTool } from "@mariozechner/pi-agent-core"; -import type { AgentSession } from "@mariozechner/pi-coding-agent"; +import type { AgentTool } from "@earendil-works/pi-agent-core"; +import type { AgentSession } from "@earendil-works/pi-coding-agent"; import type { MemoryCitationsMode } from "../../config/types.memory.js"; import type { ResolvedTimeFormat } from "../date-time.js"; import type { EmbeddedContextFile } from "../pi-embedded-helpers.js"; @@ -93,7 +93,7 @@ export function applySystemPromptOverrideToSession( override: string | ((defaultPrompt?: string) => string), ) { const prompt = typeof override === "function" ? override() : override.trim(); - session.agent.setSystemPrompt(prompt); + session.agent.state.systemPrompt = prompt; const mutableSession = session as unknown as { _baseSystemPrompt?: string; _rebuildSystemPrompt?: (toolNames: string[]) => string; diff --git a/src/agents/pi-embedded-runner/thinking.ts b/src/agents/pi-embedded-runner/thinking.ts index 5cd7ba7d..38780b70 100644 --- a/src/agents/pi-embedded-runner/thinking.ts +++ b/src/agents/pi-embedded-runner/thinking.ts @@ -1,4 +1,4 @@ -import type { AgentMessage } from "@mariozechner/pi-agent-core"; +import type { AgentMessage } from "@earendil-works/pi-agent-core"; type AssistantContentBlock = Extract["content"][number]; diff --git a/src/agents/pi-embedded-runner/tool-result-context-guard.e2e.test.ts b/src/agents/pi-embedded-runner/tool-result-context-guard.e2e.test.ts index 00915be4..9cdfd6c9 100644 --- a/src/agents/pi-embedded-runner/tool-result-context-guard.e2e.test.ts +++ b/src/agents/pi-embedded-runner/tool-result-context-guard.e2e.test.ts @@ -1,4 +1,4 @@ -import type { AgentMessage } from "@mariozechner/pi-agent-core"; +import type { AgentMessage } from "@earendil-works/pi-agent-core"; import { describe, expect, it } from "vitest"; import { CONTEXT_LIMIT_TRUNCATION_NOTICE, diff --git a/src/agents/pi-embedded-runner/tool-result-context-guard.ts b/src/agents/pi-embedded-runner/tool-result-context-guard.ts index 2cc8d1ba..19ed6a69 100644 --- a/src/agents/pi-embedded-runner/tool-result-context-guard.ts +++ b/src/agents/pi-embedded-runner/tool-result-context-guard.ts @@ -1,4 +1,4 @@ -import type { AgentMessage } from "@mariozechner/pi-agent-core"; +import type { AgentMessage } from "@earendil-works/pi-agent-core"; const CHARS_PER_TOKEN_ESTIMATE = 4; // Keep a conservative input budget to absorb tokenizer variance and provider framing overhead. diff --git a/src/agents/pi-embedded-runner/tool-result-truncation.e2e.test.ts b/src/agents/pi-embedded-runner/tool-result-truncation.e2e.test.ts index 6b7bbcf4..b75020ab 100644 --- a/src/agents/pi-embedded-runner/tool-result-truncation.e2e.test.ts +++ b/src/agents/pi-embedded-runner/tool-result-truncation.e2e.test.ts @@ -1,4 +1,4 @@ -import type { AgentMessage } from "@mariozechner/pi-agent-core"; +import type { AgentMessage } from "@earendil-works/pi-agent-core"; import { describe, expect, it } from "vitest"; import { truncateToolResultText, diff --git a/src/agents/pi-embedded-runner/tool-result-truncation.ts b/src/agents/pi-embedded-runner/tool-result-truncation.ts index 5d54cbf8..811315ec 100644 --- a/src/agents/pi-embedded-runner/tool-result-truncation.ts +++ b/src/agents/pi-embedded-runner/tool-result-truncation.ts @@ -1,6 +1,6 @@ -import type { AgentMessage } from "@mariozechner/pi-agent-core"; -import type { TextContent } from "@mariozechner/pi-ai"; -import { SessionManager } from "@mariozechner/pi-coding-agent"; +import type { AgentMessage } from "@earendil-works/pi-agent-core"; +import type { TextContent } from "@earendil-works/pi-ai"; +import { SessionManager } from "@earendil-works/pi-coding-agent"; import { log } from "./logger.js"; /** diff --git a/src/agents/pi-embedded-runner/tool-split.ts b/src/agents/pi-embedded-runner/tool-split.ts index 26eb0866..9370f07b 100644 --- a/src/agents/pi-embedded-runner/tool-split.ts +++ b/src/agents/pi-embedded-runner/tool-split.ts @@ -1,4 +1,4 @@ -import type { AgentTool } from "@mariozechner/pi-agent-core"; +import type { AgentTool } from "@earendil-works/pi-agent-core"; import { toToolDefinitions } from "../pi-tool-definition-adapter.js"; // We always pass tools via `customTools` so our policy filtering, sandbox integration, @@ -6,7 +6,7 @@ import { toToolDefinitions } from "../pi-tool-definition-adapter.js"; type AnyAgentTool = AgentTool; export function splitSdkTools(options: { tools: AnyAgentTool[]; sandboxEnabled: boolean }): { - builtInTools: AnyAgentTool[]; + builtInTools: string[]; customTools: ReturnType; } { const { tools } = options; diff --git a/src/agents/pi-embedded-runner/utils.ts b/src/agents/pi-embedded-runner/utils.ts index 9450998e..c35cb47e 100644 --- a/src/agents/pi-embedded-runner/utils.ts +++ b/src/agents/pi-embedded-runner/utils.ts @@ -1,4 +1,4 @@ -import type { ThinkingLevel } from "@mariozechner/pi-agent-core"; +import type { ThinkingLevel } from "@earendil-works/pi-agent-core"; import type { ReasoningLevel, ThinkLevel } from "../../auto-reply/thinking.js"; export function mapThinkingLevel(level?: ThinkLevel): ThinkingLevel { diff --git a/src/agents/pi-embedded-subscribe.e2e-harness.ts b/src/agents/pi-embedded-subscribe.e2e-harness.ts index 918685a1..25d75cd0 100644 --- a/src/agents/pi-embedded-subscribe.e2e-harness.ts +++ b/src/agents/pi-embedded-subscribe.e2e-harness.ts @@ -1,4 +1,4 @@ -import type { AssistantMessage } from "@mariozechner/pi-ai"; +import type { AssistantMessage } from "@earendil-works/pi-ai"; import { expect } from "vitest"; import { subscribeEmbeddedPiSession } from "./pi-embedded-subscribe.js"; diff --git a/src/agents/pi-embedded-subscribe.handlers.compaction.ts b/src/agents/pi-embedded-subscribe.handlers.compaction.ts index f28e47d1..018863f8 100644 --- a/src/agents/pi-embedded-subscribe.handlers.compaction.ts +++ b/src/agents/pi-embedded-subscribe.handlers.compaction.ts @@ -1,4 +1,4 @@ -import type { AgentEvent } from "@mariozechner/pi-agent-core"; +import type { AgentEvent } from "@earendil-works/pi-agent-core"; import { emitAgentEvent } from "../infra/agent-events.js"; import { getGlobalHookRunner } from "../plugins/hook-runner-global.js"; import type { EmbeddedPiSubscribeContext } from "./pi-embedded-subscribe.handlers.types.js"; diff --git a/src/agents/pi-embedded-subscribe.handlers.messages.ts b/src/agents/pi-embedded-subscribe.handlers.messages.ts index 845ded9f..2a5a79c3 100644 --- a/src/agents/pi-embedded-subscribe.handlers.messages.ts +++ b/src/agents/pi-embedded-subscribe.handlers.messages.ts @@ -1,4 +1,4 @@ -import type { AgentEvent, AgentMessage } from "@mariozechner/pi-agent-core"; +import type { AgentEvent, AgentMessage } from "@earendil-works/pi-agent-core"; import { parseReplyDirectives } from "../auto-reply/reply/reply-directives.js"; import { SILENT_REPLY_TOKEN } from "../auto-reply/tokens.js"; import { emitAgentEvent } from "../infra/agent-events.js"; diff --git a/src/agents/pi-embedded-subscribe.handlers.tools.media.test-helpers.ts b/src/agents/pi-embedded-subscribe.handlers.tools.media.test-helpers.ts index 378ae575..36816045 100644 --- a/src/agents/pi-embedded-subscribe.handlers.tools.media.test-helpers.ts +++ b/src/agents/pi-embedded-subscribe.handlers.tools.media.test-helpers.ts @@ -1,4 +1,4 @@ -import type { AgentEvent } from "@mariozechner/pi-agent-core"; +import type { AgentEvent } from "@earendil-works/pi-agent-core"; import type { Mock } from "vitest"; import { handleToolExecutionEnd, diff --git a/src/agents/pi-embedded-subscribe.handlers.tools.test.ts b/src/agents/pi-embedded-subscribe.handlers.tools.test.ts index c03eb00d..d116af50 100644 --- a/src/agents/pi-embedded-subscribe.handlers.tools.test.ts +++ b/src/agents/pi-embedded-subscribe.handlers.tools.test.ts @@ -1,4 +1,4 @@ -import type { AgentEvent } from "@mariozechner/pi-agent-core"; +import type { AgentEvent } from "@earendil-works/pi-agent-core"; import { describe, expect, it, vi } from "vitest"; import type { MessagingToolSend } from "./pi-embedded-messaging.js"; import { diff --git a/src/agents/pi-embedded-subscribe.handlers.tools.ts b/src/agents/pi-embedded-subscribe.handlers.tools.ts index 17d6eabf..61c7ccf0 100644 --- a/src/agents/pi-embedded-subscribe.handlers.tools.ts +++ b/src/agents/pi-embedded-subscribe.handlers.tools.ts @@ -1,4 +1,4 @@ -import type { AgentEvent } from "@mariozechner/pi-agent-core"; +import type { AgentEvent } from "@earendil-works/pi-agent-core"; import { emitAgentEvent } from "../infra/agent-events.js"; import { getGlobalHookRunner } from "../plugins/hook-runner-global.js"; import type { PluginHookAfterToolCallEvent } from "../plugins/types.js"; diff --git a/src/agents/pi-embedded-subscribe.handlers.types.ts b/src/agents/pi-embedded-subscribe.handlers.types.ts index d5c72552..9d9a4eea 100644 --- a/src/agents/pi-embedded-subscribe.handlers.types.ts +++ b/src/agents/pi-embedded-subscribe.handlers.types.ts @@ -1,4 +1,4 @@ -import type { AgentEvent, AgentMessage } from "@mariozechner/pi-agent-core"; +import type { AgentEvent, AgentMessage } from "@earendil-works/pi-agent-core"; import type { ReplyDirectiveParseResult } from "../auto-reply/reply/reply-directives.js"; import type { ReasoningLevel } from "../auto-reply/thinking.js"; import type { InlineCodeState } from "../markdown/code-spans.js"; diff --git a/src/agents/pi-embedded-subscribe.lifecycle-billing-error.e2e.test.ts b/src/agents/pi-embedded-subscribe.lifecycle-billing-error.e2e.test.ts index 669bb50c..8773645c 100644 --- a/src/agents/pi-embedded-subscribe.lifecycle-billing-error.e2e.test.ts +++ b/src/agents/pi-embedded-subscribe.lifecycle-billing-error.e2e.test.ts @@ -1,4 +1,4 @@ -import type { AssistantMessage } from "@mariozechner/pi-ai"; +import type { AssistantMessage } from "@earendil-works/pi-ai"; import { describe, expect, it, vi } from "vitest"; import { createStubSessionHarness } from "./pi-embedded-subscribe.e2e-harness.js"; import { subscribeEmbeddedPiSession } from "./pi-embedded-subscribe.js"; diff --git a/src/agents/pi-embedded-subscribe.reply-tags.e2e.test.ts b/src/agents/pi-embedded-subscribe.reply-tags.e2e.test.ts index a76db602..961ec557 100644 --- a/src/agents/pi-embedded-subscribe.reply-tags.e2e.test.ts +++ b/src/agents/pi-embedded-subscribe.reply-tags.e2e.test.ts @@ -1,4 +1,4 @@ -import type { AssistantMessage } from "@mariozechner/pi-ai"; +import type { AssistantMessage } from "@earendil-works/pi-ai"; import { describe, expect, it, vi } from "vitest"; import { createStubSessionHarness, diff --git a/src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.does-not-emit-duplicate-block-replies-text.e2e.test.ts b/src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.does-not-emit-duplicate-block-replies-text.e2e.test.ts index 44f829af..dd778b27 100644 --- a/src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.does-not-emit-duplicate-block-replies-text.e2e.test.ts +++ b/src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.does-not-emit-duplicate-block-replies-text.e2e.test.ts @@ -1,4 +1,4 @@ -import type { AssistantMessage } from "@mariozechner/pi-ai"; +import type { AssistantMessage } from "@earendil-works/pi-ai"; import { describe, expect, it, vi } from "vitest"; import { createStubSessionHarness, diff --git a/src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.emits-block-replies-text-end-does-not.e2e.test.ts b/src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.emits-block-replies-text-end-does-not.e2e.test.ts index cb383b04..e5097781 100644 --- a/src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.emits-block-replies-text-end-does-not.e2e.test.ts +++ b/src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.emits-block-replies-text-end-does-not.e2e.test.ts @@ -1,4 +1,4 @@ -import type { AssistantMessage } from "@mariozechner/pi-ai"; +import type { AssistantMessage } from "@earendil-works/pi-ai"; import { describe, expect, it, vi } from "vitest"; import { createTextEndBlockReplyHarness, diff --git a/src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.emits-reasoning-as-separate-message-enabled.e2e.test.ts b/src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.emits-reasoning-as-separate-message-enabled.e2e.test.ts index 98b4ce09..2da43ccb 100644 --- a/src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.emits-reasoning-as-separate-message-enabled.e2e.test.ts +++ b/src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.emits-reasoning-as-separate-message-enabled.e2e.test.ts @@ -1,4 +1,4 @@ -import type { AssistantMessage } from "@mariozechner/pi-ai"; +import type { AssistantMessage } from "@earendil-works/pi-ai"; import { describe, expect, it, vi } from "vitest"; import { THINKING_TAG_CASES, diff --git a/src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.filters-final-suppresses-output-without-start-tag.e2e.test.ts b/src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.filters-final-suppresses-output-without-start-tag.e2e.test.ts index 9ccb7860..87fc886e 100644 --- a/src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.filters-final-suppresses-output-without-start-tag.e2e.test.ts +++ b/src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.filters-final-suppresses-output-without-start-tag.e2e.test.ts @@ -1,4 +1,4 @@ -import type { AssistantMessage } from "@mariozechner/pi-ai"; +import type { AssistantMessage } from "@earendil-works/pi-ai"; import { describe, expect, it, vi } from "vitest"; import { createStubSessionHarness, diff --git a/src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.keeps-assistanttexts-final-answer-block-replies-are.e2e.test.ts b/src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.keeps-assistanttexts-final-answer-block-replies-are.e2e.test.ts index 710b1f28..a902d920 100644 --- a/src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.keeps-assistanttexts-final-answer-block-replies-are.e2e.test.ts +++ b/src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.keeps-assistanttexts-final-answer-block-replies-are.e2e.test.ts @@ -1,4 +1,4 @@ -import type { AssistantMessage } from "@mariozechner/pi-ai"; +import type { AssistantMessage } from "@earendil-works/pi-ai"; import { describe, expect, it, vi } from "vitest"; import { createStubSessionHarness, diff --git a/src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.splits-long-single-line-fenced-blocks-reopen.e2e.test.ts b/src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.splits-long-single-line-fenced-blocks-reopen.e2e.test.ts index bbc2a019..869f9529 100644 --- a/src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.splits-long-single-line-fenced-blocks-reopen.e2e.test.ts +++ b/src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.splits-long-single-line-fenced-blocks-reopen.e2e.test.ts @@ -1,4 +1,4 @@ -import type { AssistantMessage } from "@mariozechner/pi-ai"; +import type { AssistantMessage } from "@earendil-works/pi-ai"; import { describe, expect, it, vi } from "vitest"; import { createParagraphChunkedBlockReplyHarness, diff --git a/src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.subscribeembeddedpisession.e2e.test.ts b/src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.subscribeembeddedpisession.e2e.test.ts index a048ab2d..22f01904 100644 --- a/src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.subscribeembeddedpisession.e2e.test.ts +++ b/src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.subscribeembeddedpisession.e2e.test.ts @@ -1,4 +1,4 @@ -import type { AssistantMessage } from "@mariozechner/pi-ai"; +import type { AssistantMessage } from "@earendil-works/pi-ai"; import { describe, expect, it, vi } from "vitest"; import { THINKING_TAG_CASES, diff --git a/src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.suppresses-message-end-block-replies-message-tool.e2e.test.ts b/src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.suppresses-message-end-block-replies-message-tool.e2e.test.ts index 737edc7f..ffd8e672 100644 --- a/src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.suppresses-message-end-block-replies-message-tool.e2e.test.ts +++ b/src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.suppresses-message-end-block-replies-message-tool.e2e.test.ts @@ -1,4 +1,4 @@ -import type { AssistantMessage } from "@mariozechner/pi-ai"; +import type { AssistantMessage } from "@earendil-works/pi-ai"; import { describe, expect, it, vi } from "vitest"; import { createStubSessionHarness, diff --git a/src/agents/pi-embedded-subscribe.tools.ts b/src/agents/pi-embedded-subscribe.tools.ts index ea398338..e40f617e 100644 --- a/src/agents/pi-embedded-subscribe.tools.ts +++ b/src/agents/pi-embedded-subscribe.tools.ts @@ -104,9 +104,10 @@ export function sanitizeToolResult(result: unknown): unknown { if (type === "image") { const data = typeof entry.data === "string" ? entry.data : undefined; const bytes = data ? data.length : undefined; - const cleaned = { ...entry }; - delete cleaned.data; - return { ...cleaned, bytes, omitted: true }; + // Retain `data` so the TUI can render images inline. The `omitted: true` + // flag tells the model layer to skip this block when building the prompt, + // so the base64 never inflates the token context. + return { ...entry, bytes, omitted: true }; } return entry; }); diff --git a/src/agents/pi-embedded-subscribe.ts b/src/agents/pi-embedded-subscribe.ts index b3326c39..267a63df 100644 --- a/src/agents/pi-embedded-subscribe.ts +++ b/src/agents/pi-embedded-subscribe.ts @@ -1,4 +1,4 @@ -import type { AgentMessage } from "@mariozechner/pi-agent-core"; +import type { AgentMessage } from "@earendil-works/pi-agent-core"; import { parseReplyDirectives } from "../auto-reply/reply/reply-directives.js"; import { createStreamingDirectiveAccumulator } from "../auto-reply/reply/streaming-directives.js"; import { formatToolAggregate } from "../auto-reply/tool-meta.js"; diff --git a/src/agents/pi-embedded-subscribe.types.ts b/src/agents/pi-embedded-subscribe.types.ts index e9b6ebd3..e536832d 100644 --- a/src/agents/pi-embedded-subscribe.types.ts +++ b/src/agents/pi-embedded-subscribe.types.ts @@ -1,4 +1,4 @@ -import type { AgentSession } from "@mariozechner/pi-coding-agent"; +import type { AgentSession } from "@earendil-works/pi-coding-agent"; import type { ReasoningLevel, VerboseLevel } from "../auto-reply/thinking.js"; import type { MayrosConfig } from "../config/types.mayros.js"; import type { HookRunner } from "../plugins/hooks.js"; diff --git a/src/agents/pi-embedded-utils.e2e.test.ts b/src/agents/pi-embedded-utils.e2e.test.ts index ecb8dace..a5c972ba 100644 --- a/src/agents/pi-embedded-utils.e2e.test.ts +++ b/src/agents/pi-embedded-utils.e2e.test.ts @@ -1,4 +1,4 @@ -import type { AssistantMessage } from "@mariozechner/pi-ai"; +import type { AssistantMessage } from "@earendil-works/pi-ai"; import { describe, expect, it } from "vitest"; import { extractAssistantText, diff --git a/src/agents/pi-embedded-utils.ts b/src/agents/pi-embedded-utils.ts index 82ad3efc..88e317f1 100644 --- a/src/agents/pi-embedded-utils.ts +++ b/src/agents/pi-embedded-utils.ts @@ -1,5 +1,5 @@ -import type { AgentMessage } from "@mariozechner/pi-agent-core"; -import type { AssistantMessage } from "@mariozechner/pi-ai"; +import type { AgentMessage } from "@earendil-works/pi-agent-core"; +import type { AssistantMessage } from "@earendil-works/pi-ai"; import { extractTextFromChatContent } from "../shared/chat-content.js"; import { stripReasoningTagsFromText } from "../shared/text/reasoning-tags.js"; import { sanitizeUserFacingText } from "./pi-embedded-helpers.js"; diff --git a/src/agents/pi-extensions/compaction-safeguard.e2e.test.ts b/src/agents/pi-extensions/compaction-safeguard.e2e.test.ts index 3d5fab42..c9f7c12b 100644 --- a/src/agents/pi-extensions/compaction-safeguard.e2e.test.ts +++ b/src/agents/pi-extensions/compaction-safeguard.e2e.test.ts @@ -1,4 +1,4 @@ -import type { AgentMessage } from "@mariozechner/pi-agent-core"; +import type { AgentMessage } from "@earendil-works/pi-agent-core"; import { describe, expect, it } from "vitest"; import { getCompactionSafeguardRuntime, diff --git a/src/agents/pi-extensions/compaction-safeguard.ts b/src/agents/pi-extensions/compaction-safeguard.ts index 12c6627e..b0a7a6b0 100644 --- a/src/agents/pi-extensions/compaction-safeguard.ts +++ b/src/agents/pi-extensions/compaction-safeguard.ts @@ -1,7 +1,7 @@ import fs from "node:fs"; import path from "node:path"; -import type { AgentMessage } from "@mariozechner/pi-agent-core"; -import type { ExtensionAPI, FileOperations } from "@mariozechner/pi-coding-agent"; +import type { AgentMessage } from "@earendil-works/pi-agent-core"; +import type { ExtensionAPI, FileOperations } from "@earendil-works/pi-coding-agent"; import { extractSections } from "../../auto-reply/reply/post-compaction-context.js"; import { BASE_CHUNK_RATIO, @@ -207,7 +207,7 @@ export default function compactionSafeguardExtension(api: ExtensionAPI): void { }; } - const apiKey = await ctx.modelRegistry.getApiKey(model); + const apiKey = await ctx.modelRegistry.getApiKeyForProvider(model.provider); if (!apiKey) { return { compaction: { diff --git a/src/agents/pi-extensions/context-pruning.e2e.test.ts b/src/agents/pi-extensions/context-pruning.e2e.test.ts index c71591d7..649d113b 100644 --- a/src/agents/pi-extensions/context-pruning.e2e.test.ts +++ b/src/agents/pi-extensions/context-pruning.e2e.test.ts @@ -1,5 +1,5 @@ -import type { AgentMessage } from "@mariozechner/pi-agent-core"; -import type { ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent"; +import type { AgentMessage } from "@earendil-works/pi-agent-core"; +import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent"; import { describe, expect, it } from "vitest"; import { computeEffectiveSettings, diff --git a/src/agents/pi-extensions/context-pruning/extension.ts b/src/agents/pi-extensions/context-pruning/extension.ts index 2a4063ae..dc86e6ed 100644 --- a/src/agents/pi-extensions/context-pruning/extension.ts +++ b/src/agents/pi-extensions/context-pruning/extension.ts @@ -1,4 +1,4 @@ -import type { ContextEvent, ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent"; +import type { ContextEvent, ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent"; import { pruneContextMessages } from "./pruner.js"; import { getContextPruningRuntime } from "./runtime.js"; diff --git a/src/agents/pi-extensions/context-pruning/pruner.ts b/src/agents/pi-extensions/context-pruning/pruner.ts index acfa6316..406793e1 100644 --- a/src/agents/pi-extensions/context-pruning/pruner.ts +++ b/src/agents/pi-extensions/context-pruning/pruner.ts @@ -1,6 +1,6 @@ -import type { AgentMessage } from "@mariozechner/pi-agent-core"; -import type { ImageContent, TextContent, ToolResultMessage } from "@mariozechner/pi-ai"; -import type { ExtensionContext } from "@mariozechner/pi-coding-agent"; +import type { AgentMessage } from "@earendil-works/pi-agent-core"; +import type { ImageContent, TextContent, ToolResultMessage } from "@earendil-works/pi-ai"; +import type { ExtensionContext } from "@earendil-works/pi-coding-agent"; import type { EffectiveContextPruningSettings } from "./settings.js"; import { makeToolPrunablePredicate } from "./tools.js"; diff --git a/src/agents/pi-model-discovery.ts b/src/agents/pi-model-discovery.ts index 51ac1aeb..96bdcc88 100644 --- a/src/agents/pi-model-discovery.ts +++ b/src/agents/pi-model-discovery.ts @@ -1,7 +1,7 @@ import path from "node:path"; -import { AuthStorage, ModelRegistry } from "@mariozechner/pi-coding-agent"; +import { AuthStorage, ModelRegistry } from "@earendil-works/pi-coding-agent"; -export { AuthStorage, ModelRegistry } from "@mariozechner/pi-coding-agent"; +export { AuthStorage, ModelRegistry } from "@earendil-works/pi-coding-agent"; function createAuthStorage(AuthStorageLike: unknown, path: string) { const withFactory = AuthStorageLike as { create?: (path: string) => unknown }; @@ -17,5 +17,5 @@ export function discoverAuthStorage(agentDir: string): AuthStorage { } export function discoverModels(authStorage: AuthStorage, agentDir: string): ModelRegistry { - return new ModelRegistry(authStorage, path.join(agentDir, "models.json")); + return ModelRegistry.create(authStorage, path.join(agentDir, "models.json")); } diff --git a/src/agents/pi-tool-definition-adapter.after-tool-call.e2e.test.ts b/src/agents/pi-tool-definition-adapter.after-tool-call.e2e.test.ts index 5d442fc6..f6f8a374 100644 --- a/src/agents/pi-tool-definition-adapter.after-tool-call.e2e.test.ts +++ b/src/agents/pi-tool-definition-adapter.after-tool-call.e2e.test.ts @@ -1,4 +1,4 @@ -import type { AgentTool } from "@mariozechner/pi-agent-core"; +import type { AgentTool } from "@earendil-works/pi-agent-core"; import { Type } from "@sinclair/typebox"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { toToolDefinitions } from "./pi-tool-definition-adapter.js"; diff --git a/src/agents/pi-tool-definition-adapter.e2e.test.ts b/src/agents/pi-tool-definition-adapter.e2e.test.ts index 1b11bbf4..161dbe7c 100644 --- a/src/agents/pi-tool-definition-adapter.e2e.test.ts +++ b/src/agents/pi-tool-definition-adapter.e2e.test.ts @@ -1,4 +1,4 @@ -import type { AgentTool } from "@mariozechner/pi-agent-core"; +import type { AgentTool } from "@earendil-works/pi-agent-core"; import { Type } from "@sinclair/typebox"; import { describe, expect, it } from "vitest"; import { toToolDefinitions } from "./pi-tool-definition-adapter.js"; diff --git a/src/agents/pi-tool-definition-adapter.ts b/src/agents/pi-tool-definition-adapter.ts index f3963600..4e70485e 100644 --- a/src/agents/pi-tool-definition-adapter.ts +++ b/src/agents/pi-tool-definition-adapter.ts @@ -2,8 +2,8 @@ import type { AgentTool, AgentToolResult, AgentToolUpdateCallback, -} from "@mariozechner/pi-agent-core"; -import type { ToolDefinition } from "@mariozechner/pi-coding-agent"; +} from "@earendil-works/pi-agent-core"; +import type { ToolDefinition } from "@earendil-works/pi-coding-agent"; import { logDebug, logError } from "../logger.js"; import { getGlobalHookRunner } from "../plugins/hook-runner-global.js"; import { isPlainObject } from "../utils.js"; diff --git a/src/agents/pi-tools.create-mayros-coding-tools.adds-claude-style-aliases-schemas-without-dropping.e2e.test.ts b/src/agents/pi-tools.create-mayros-coding-tools.adds-claude-style-aliases-schemas-without-dropping.e2e.test.ts index 68483d73..a57c4395 100644 --- a/src/agents/pi-tools.create-mayros-coding-tools.adds-claude-style-aliases-schemas-without-dropping.e2e.test.ts +++ b/src/agents/pi-tools.create-mayros-coding-tools.adds-claude-style-aliases-schemas-without-dropping.e2e.test.ts @@ -1,7 +1,7 @@ import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; -import type { AgentTool, AgentToolResult } from "@mariozechner/pi-agent-core"; +import type { AgentTool, AgentToolResult } from "@earendil-works/pi-agent-core"; import { Type } from "@sinclair/typebox"; import { describe, expect, it, vi } from "vitest"; import "./test-helpers/fast-coding-tools.js"; diff --git a/src/agents/pi-tools.policy.e2e.test.ts b/src/agents/pi-tools.policy.e2e.test.ts index b36fb18c..5206e550 100644 --- a/src/agents/pi-tools.policy.e2e.test.ts +++ b/src/agents/pi-tools.policy.e2e.test.ts @@ -1,4 +1,4 @@ -import type { AgentTool, AgentToolResult } from "@mariozechner/pi-agent-core"; +import type { AgentTool, AgentToolResult } from "@earendil-works/pi-agent-core"; import { Type } from "@sinclair/typebox"; import { describe, expect, it } from "vitest"; import type { MayrosConfig } from "../config/config.js"; diff --git a/src/agents/pi-tools.read.ts b/src/agents/pi-tools.read.ts index 385d25a4..397a3c29 100644 --- a/src/agents/pi-tools.read.ts +++ b/src/agents/pi-tools.read.ts @@ -1,5 +1,5 @@ -import type { AgentToolResult } from "@mariozechner/pi-agent-core"; -import { createEditTool, createReadTool, createWriteTool } from "@mariozechner/pi-coding-agent"; +import type { AgentToolResult } from "@earendil-works/pi-agent-core"; +import { createEditTool, createReadTool, createWriteTool } from "@earendil-works/pi-coding-agent"; import { detectMime } from "../media/mime.js"; import { sniffMimeFromBase64 } from "../media/sniff-mime-from-base64.js"; import type { ImageSanitizationLimits } from "./image-sanitization.js"; diff --git a/src/agents/pi-tools.ts b/src/agents/pi-tools.ts index a6ca4386..1f9659fc 100644 --- a/src/agents/pi-tools.ts +++ b/src/agents/pi-tools.ts @@ -1,10 +1,9 @@ import { - codingTools, + createCodingTools, createEditTool, createReadTool, createWriteTool, - readTool, -} from "@mariozechner/pi-coding-agent"; +} from "@earendil-works/pi-coding-agent"; import type { MayrosConfig } from "../config/config.js"; import type { ToolLoopDetectionConfig } from "../config/types.tools.js"; import { logWarn } from "../logger.js"; @@ -303,8 +302,8 @@ export function createMayrosCodingTools(options?: { } const imageSanitization = resolveImageSanitizationLimits(options?.config); - const base = (codingTools as unknown as AnyAgentTool[]).flatMap((tool) => { - if (tool.name === readTool.name) { + const base = (createCodingTools(workspaceRoot) as unknown as AnyAgentTool[]).flatMap((tool) => { + if (tool.name === "read") { if (sandboxRoot) { const sandboxed = createSandboxedReadTool({ root: sandboxRoot, diff --git a/src/agents/pi-tools.types.ts b/src/agents/pi-tools.types.ts index ad2bd85f..3c8aa6fc 100644 --- a/src/agents/pi-tools.types.ts +++ b/src/agents/pi-tools.types.ts @@ -1,4 +1,4 @@ -import type { AgentTool } from "@mariozechner/pi-agent-core"; +import type { AgentTool } from "@earendil-works/pi-agent-core"; // oxlint-disable-next-line typescript/no-explicit-any export type AnyAgentTool = AgentTool; diff --git a/src/agents/session-tool-result-guard-wrapper.ts b/src/agents/session-tool-result-guard-wrapper.ts index 89668023..7ae8539b 100644 --- a/src/agents/session-tool-result-guard-wrapper.ts +++ b/src/agents/session-tool-result-guard-wrapper.ts @@ -1,4 +1,4 @@ -import type { SessionManager } from "@mariozechner/pi-coding-agent"; +import type { SessionManager } from "@earendil-works/pi-coding-agent"; import { getGlobalHookRunner } from "../plugins/hook-runner-global.js"; import { applyInputProvenanceToUserMessage, @@ -30,7 +30,7 @@ export function guardSessionManager( const hookRunner = getGlobalHookRunner(); const beforeMessageWrite = hookRunner?.hasHooks("before_message_write") - ? (event: { message: import("@mariozechner/pi-agent-core").AgentMessage }) => { + ? (event: { message: import("@earendil-works/pi-agent-core").AgentMessage }) => { return hookRunner.runBeforeMessageWrite(event, { agentId: opts?.agentId, sessionKey: opts?.sessionKey, diff --git a/src/agents/session-tool-result-guard.e2e.test.ts b/src/agents/session-tool-result-guard.e2e.test.ts index 37cf5c96..ce2f69ce 100644 --- a/src/agents/session-tool-result-guard.e2e.test.ts +++ b/src/agents/session-tool-result-guard.e2e.test.ts @@ -1,5 +1,5 @@ -import type { AgentMessage } from "@mariozechner/pi-agent-core"; -import { SessionManager } from "@mariozechner/pi-coding-agent"; +import type { AgentMessage } from "@earendil-works/pi-agent-core"; +import { SessionManager } from "@earendil-works/pi-coding-agent"; import { describe, expect, it } from "vitest"; import { installSessionToolResultGuard } from "./session-tool-result-guard.js"; diff --git a/src/agents/session-tool-result-guard.tool-result-persist-hook.e2e.test.ts b/src/agents/session-tool-result-guard.tool-result-persist-hook.e2e.test.ts index 7c38e73b..b8f3e289 100644 --- a/src/agents/session-tool-result-guard.tool-result-persist-hook.e2e.test.ts +++ b/src/agents/session-tool-result-guard.tool-result-persist-hook.e2e.test.ts @@ -1,8 +1,8 @@ import fs from "node:fs"; import os from "node:os"; import path from "node:path"; -import type { AgentMessage } from "@mariozechner/pi-agent-core"; -import { SessionManager } from "@mariozechner/pi-coding-agent"; +import type { AgentMessage } from "@earendil-works/pi-agent-core"; +import { SessionManager } from "@earendil-works/pi-coding-agent"; import { describe, expect, it, afterEach } from "vitest"; import { initializeGlobalHookRunner, diff --git a/src/agents/session-tool-result-guard.ts b/src/agents/session-tool-result-guard.ts index 0f82cd2d..ba1d16ae 100644 --- a/src/agents/session-tool-result-guard.ts +++ b/src/agents/session-tool-result-guard.ts @@ -1,6 +1,6 @@ -import type { AgentMessage } from "@mariozechner/pi-agent-core"; -import type { TextContent } from "@mariozechner/pi-ai"; -import type { SessionManager } from "@mariozechner/pi-coding-agent"; +import type { AgentMessage } from "@earendil-works/pi-agent-core"; +import type { TextContent } from "@earendil-works/pi-ai"; +import type { SessionManager } from "@earendil-works/pi-coding-agent"; import type { PluginHookBeforeMessageWriteEvent, PluginHookBeforeMessageWriteResult, diff --git a/src/agents/session-transcript-repair.e2e.test.ts b/src/agents/session-transcript-repair.e2e.test.ts index de988edf..8f0e2715 100644 --- a/src/agents/session-transcript-repair.e2e.test.ts +++ b/src/agents/session-transcript-repair.e2e.test.ts @@ -1,4 +1,4 @@ -import type { AgentMessage } from "@mariozechner/pi-agent-core"; +import type { AgentMessage } from "@earendil-works/pi-agent-core"; import { describe, expect, it } from "vitest"; import { sanitizeToolCallInputs, diff --git a/src/agents/session-transcript-repair.ts b/src/agents/session-transcript-repair.ts index 4741914e..f9e27e1f 100644 --- a/src/agents/session-transcript-repair.ts +++ b/src/agents/session-transcript-repair.ts @@ -1,4 +1,4 @@ -import type { AgentMessage } from "@mariozechner/pi-agent-core"; +import type { AgentMessage } from "@earendil-works/pi-agent-core"; import { extractToolCallsFromAssistant, extractToolResultId } from "./tool-call-id.js"; type ToolCallBlock = { diff --git a/src/agents/skills-status.e2e.test.ts b/src/agents/skills-status.e2e.test.ts index 38fa50e8..f4a9e907 100644 --- a/src/agents/skills-status.e2e.test.ts +++ b/src/agents/skills-status.e2e.test.ts @@ -15,7 +15,12 @@ describe("buildWorkspaceSkillStatus", () => { skill: { name: "os-scoped", description: "test", - source: "test", + sourceInfo: { + path: "/tmp/os-scoped", + source: "test", + scope: "project", + origin: "top-level", + }, filePath: "/tmp/os-scoped", baseDir: "/tmp", disableModelInvocation: false, diff --git a/src/agents/skills-status.ts b/src/agents/skills-status.ts index 96dabfa3..0c187630 100644 --- a/src/agents/skills-status.ts +++ b/src/agents/skills-status.ts @@ -189,7 +189,7 @@ function buildSkillStatus( const bundled = bundledNames && bundledNames.size > 0 ? bundledNames.has(entry.skill.name) - : entry.skill.source === "mayros-bundled"; + : entry.skill.sourceInfo.source === "mayros-bundled"; const requirementStatus = evaluateEntryMetadataRequirementsForCurrentPlatform({ always, @@ -207,7 +207,7 @@ function buildSkillStatus( return { name: entry.skill.name, description: entry.skill.description, - source: entry.skill.source, + source: entry.skill.sourceInfo.source, bundled, filePath: entry.skill.filePath, baseDir: entry.skill.baseDir, diff --git a/src/agents/skills.resolveskillspromptforrun.e2e.test.ts b/src/agents/skills.resolveskillspromptforrun.e2e.test.ts index a5c2acfe..3a546231 100644 --- a/src/agents/skills.resolveskillspromptforrun.e2e.test.ts +++ b/src/agents/skills.resolveskillspromptforrun.e2e.test.ts @@ -17,7 +17,12 @@ describe("resolveSkillsPromptForRun", () => { description: "Demo", filePath: "/app/skills/demo-skill/SKILL.md", baseDir: "/app/skills/demo-skill", - source: "mayros-bundled", + sourceInfo: { + path: "/app/skills/demo-skill/SKILL.md", + source: "mayros-bundled", + scope: "project", + origin: "top-level", + }, disableModelInvocation: false, }, frontmatter: {}, diff --git a/src/agents/skills/bundled-context.ts b/src/agents/skills/bundled-context.ts index 06576497..b5f3d4a3 100644 --- a/src/agents/skills/bundled-context.ts +++ b/src/agents/skills/bundled-context.ts @@ -1,4 +1,4 @@ -import { loadSkillsFromDir } from "@mariozechner/pi-coding-agent"; +import { loadSkillsFromDir } from "@earendil-works/pi-coding-agent"; import { createSubsystemLogger } from "../../logging/subsystem.js"; import { resolveBundledSkillsDir, type BundledSkillsResolveOptions } from "./bundled-dir.js"; diff --git a/src/agents/skills/config.ts b/src/agents/skills/config.ts index ebbad246..097506dc 100644 --- a/src/agents/skills/config.ts +++ b/src/agents/skills/config.ts @@ -49,7 +49,7 @@ function normalizeAllowlist(input: unknown): string[] | undefined { const BUNDLED_SOURCES = new Set(["mayros-bundled"]); function isBundledSkill(entry: SkillEntry): boolean { - return BUNDLED_SOURCES.has(entry.skill.source); + return BUNDLED_SOURCES.has(entry.skill.sourceInfo.source); } export function resolveBundledAllowlist(config?: MayrosConfig): string[] | undefined { diff --git a/src/agents/skills/frontmatter.ts b/src/agents/skills/frontmatter.ts index 7575fbb3..abf46fd9 100644 --- a/src/agents/skills/frontmatter.ts +++ b/src/agents/skills/frontmatter.ts @@ -1,4 +1,4 @@ -import type { Skill } from "@mariozechner/pi-coding-agent"; +import type { Skill } from "@earendil-works/pi-coding-agent"; import { parseFrontmatterBlock } from "../../markdown/frontmatter.js"; import { getFrontmatterString, diff --git a/src/agents/skills/types.ts b/src/agents/skills/types.ts index ca93f42e..d2a0ae62 100644 --- a/src/agents/skills/types.ts +++ b/src/agents/skills/types.ts @@ -1,4 +1,4 @@ -import type { Skill } from "@mariozechner/pi-coding-agent"; +import type { Skill } from "@earendil-works/pi-coding-agent"; export type SkillInstallSpec = { id?: string; diff --git a/src/agents/skills/workspace.ts b/src/agents/skills/workspace.ts index 02901c0c..b69ad4f2 100644 --- a/src/agents/skills/workspace.ts +++ b/src/agents/skills/workspace.ts @@ -5,7 +5,7 @@ import { formatSkillsForPrompt, loadSkillsFromDir, type Skill, -} from "@mariozechner/pi-coding-agent"; +} from "@earendil-works/pi-coding-agent"; import type { MayrosConfig } from "../../config/config.js"; import { createSubsystemLogger } from "../../logging/subsystem.js"; import { CONFIG_DIR, resolveUserPath } from "../../utils.js"; diff --git a/src/agents/system-prompt-report.ts b/src/agents/system-prompt-report.ts index 71d77f47..f1077d75 100644 --- a/src/agents/system-prompt-report.ts +++ b/src/agents/system-prompt-report.ts @@ -1,5 +1,5 @@ import path from "node:path"; -import type { AgentTool } from "@mariozechner/pi-agent-core"; +import type { AgentTool } from "@earendil-works/pi-agent-core"; import type { SessionSystemPromptReport } from "../config/sessions/types.js"; import type { EmbeddedContextFile } from "./pi-embedded-helpers.js"; import type { WorkspaceBootstrapFile } from "./workspace.js"; diff --git a/src/agents/test-helpers/assistant-message-fixtures.ts b/src/agents/test-helpers/assistant-message-fixtures.ts index edf26770..ebac953b 100644 --- a/src/agents/test-helpers/assistant-message-fixtures.ts +++ b/src/agents/test-helpers/assistant-message-fixtures.ts @@ -1,4 +1,4 @@ -import type { AssistantMessage } from "@mariozechner/pi-ai"; +import type { AssistantMessage } from "@earendil-works/pi-ai"; const ZERO_USAGE: AssistantMessage["usage"] = { input: 0, diff --git a/src/agents/tool-call-id.e2e.test.ts b/src/agents/tool-call-id.e2e.test.ts index ce4e5dd6..69480307 100644 --- a/src/agents/tool-call-id.e2e.test.ts +++ b/src/agents/tool-call-id.e2e.test.ts @@ -1,4 +1,4 @@ -import type { AgentMessage } from "@mariozechner/pi-agent-core"; +import type { AgentMessage } from "@earendil-works/pi-agent-core"; import { describe, expect, it } from "vitest"; import { isValidCloudCodeAssistToolId, diff --git a/src/agents/tool-call-id.ts b/src/agents/tool-call-id.ts index e30236e6..6a39360c 100644 --- a/src/agents/tool-call-id.ts +++ b/src/agents/tool-call-id.ts @@ -1,5 +1,5 @@ import { createHash } from "node:crypto"; -import type { AgentMessage } from "@mariozechner/pi-agent-core"; +import type { AgentMessage } from "@earendil-works/pi-agent-core"; export type ToolCallIdMode = "strict" | "strict9"; diff --git a/src/agents/tool-images.ts b/src/agents/tool-images.ts index 0059578e..f4a1f6b7 100644 --- a/src/agents/tool-images.ts +++ b/src/agents/tool-images.ts @@ -1,5 +1,5 @@ -import type { AgentToolResult } from "@mariozechner/pi-agent-core"; -import type { ImageContent } from "@mariozechner/pi-ai"; +import type { AgentToolResult } from "@earendil-works/pi-agent-core"; +import type { ImageContent } from "@earendil-works/pi-ai"; import { createSubsystemLogger } from "../logging/subsystem.js"; import { buildImageResizeSideGrid, diff --git a/src/agents/tool-summaries.ts b/src/agents/tool-summaries.ts index bca325e4..009e6868 100644 --- a/src/agents/tool-summaries.ts +++ b/src/agents/tool-summaries.ts @@ -1,4 +1,4 @@ -import type { AgentTool } from "@mariozechner/pi-agent-core"; +import type { AgentTool } from "@earendil-works/pi-agent-core"; export function buildToolSummaryMap(tools: AgentTool[]): Record { const summaries: Record = {}; diff --git a/src/agents/tools/common.ts b/src/agents/tools/common.ts index 1aea6dd3..25556822 100644 --- a/src/agents/tools/common.ts +++ b/src/agents/tools/common.ts @@ -1,5 +1,5 @@ import fs from "node:fs/promises"; -import type { AgentTool, AgentToolResult } from "@mariozechner/pi-agent-core"; +import type { AgentTool, AgentToolResult } from "@earendil-works/pi-agent-core"; import { detectMime } from "../../media/mime.js"; import type { ImageSanitizationLimits } from "../image-sanitization.js"; import { sanitizeToolResultImages } from "../tool-images.js"; diff --git a/src/agents/tools/discord-actions-guild.ts b/src/agents/tools/discord-actions-guild.ts index 630c6e9a..fc026680 100644 --- a/src/agents/tools/discord-actions-guild.ts +++ b/src/agents/tools/discord-actions-guild.ts @@ -1,4 +1,4 @@ -import type { AgentToolResult } from "@mariozechner/pi-agent-core"; +import type { AgentToolResult } from "@earendil-works/pi-agent-core"; import type { DiscordActionConfig } from "../../config/config.js"; import { getPresence } from "../../discord/monitor/presence-cache.js"; import { diff --git a/src/agents/tools/discord-actions-messaging.ts b/src/agents/tools/discord-actions-messaging.ts index 144992ac..f73a6be5 100644 --- a/src/agents/tools/discord-actions-messaging.ts +++ b/src/agents/tools/discord-actions-messaging.ts @@ -1,4 +1,4 @@ -import type { AgentToolResult } from "@mariozechner/pi-agent-core"; +import type { AgentToolResult } from "@earendil-works/pi-agent-core"; import type { DiscordActionConfig } from "../../config/config.js"; import { readDiscordComponentSpec } from "../../discord/components.js"; import { diff --git a/src/agents/tools/discord-actions-moderation.ts b/src/agents/tools/discord-actions-moderation.ts index c2dd5ebc..ba981e43 100644 --- a/src/agents/tools/discord-actions-moderation.ts +++ b/src/agents/tools/discord-actions-moderation.ts @@ -1,4 +1,4 @@ -import type { AgentToolResult } from "@mariozechner/pi-agent-core"; +import type { AgentToolResult } from "@earendil-works/pi-agent-core"; import type { DiscordActionConfig } from "../../config/config.js"; import { banMemberDiscord, diff --git a/src/agents/tools/discord-actions-presence.ts b/src/agents/tools/discord-actions-presence.ts index 90639aa6..5a0bc2f2 100644 --- a/src/agents/tools/discord-actions-presence.ts +++ b/src/agents/tools/discord-actions-presence.ts @@ -1,5 +1,5 @@ import type { Activity, UpdatePresenceData } from "@buape/carbon/gateway"; -import type { AgentToolResult } from "@mariozechner/pi-agent-core"; +import type { AgentToolResult } from "@earendil-works/pi-agent-core"; import type { DiscordActionConfig } from "../../config/config.js"; import { getGateway } from "../../discord/monitor/gateway-registry.js"; import { type ActionGate, jsonResult, readStringParam } from "./common.js"; diff --git a/src/agents/tools/discord-actions.ts b/src/agents/tools/discord-actions.ts index 3b33f91e..109b3767 100644 --- a/src/agents/tools/discord-actions.ts +++ b/src/agents/tools/discord-actions.ts @@ -1,4 +1,4 @@ -import type { AgentToolResult } from "@mariozechner/pi-agent-core"; +import type { AgentToolResult } from "@earendil-works/pi-agent-core"; import type { MayrosConfig } from "../../config/config.js"; import { createDiscordActionGate } from "../../discord/accounts.js"; import { readStringParam } from "./common.js"; diff --git a/src/agents/tools/image-tool.helpers.ts b/src/agents/tools/image-tool.helpers.ts index 61943a4b..d294dbbc 100644 --- a/src/agents/tools/image-tool.helpers.ts +++ b/src/agents/tools/image-tool.helpers.ts @@ -1,4 +1,4 @@ -import type { AssistantMessage } from "@mariozechner/pi-ai"; +import type { AssistantMessage } from "@earendil-works/pi-ai"; import type { MayrosConfig } from "../../config/config.js"; import { extractAssistantText } from "../pi-embedded-utils.js"; diff --git a/src/agents/tools/image-tool.ts b/src/agents/tools/image-tool.ts index e6b520a7..693bf922 100644 --- a/src/agents/tools/image-tool.ts +++ b/src/agents/tools/image-tool.ts @@ -1,5 +1,6 @@ import path from "node:path"; -import { type Api, type Context, complete, type Model } from "@mariozechner/pi-ai"; +import type { Api, Context, Model } from "@earendil-works/pi-ai"; +import { complete } from "@earendil-works/pi-ai/compat"; import { Type } from "@sinclair/typebox"; import type { MayrosConfig } from "../../config/config.js"; import { resolveUserPath } from "../../utils.js"; diff --git a/src/agents/tools/memory-tool.ts b/src/agents/tools/memory-tool.ts index 2ba91da6..32ee0ac8 100644 --- a/src/agents/tools/memory-tool.ts +++ b/src/agents/tools/memory-tool.ts @@ -52,7 +52,8 @@ export function createMemorySearchTool(options: { description: "Mandatory recall step: semantically search MEMORY.md + memory/*.md (and optional session transcripts) before answering questions about prior work, decisions, dates, people, preferences, or todos; returns top snippets with path + lines. If response has disabled=true, memory retrieval is unavailable and should be surfaced to the user.", parameters: MemorySearchSchema, - execute: async (_toolCallId, params) => { + execute: async (_toolCallId, rawParams) => { + const params = rawParams as Record; const query = readStringParam(params, "query", { required: true }); const maxResults = readNumberParam(params, "maxResults"); const minScore = readNumberParam(params, "minScore"); @@ -113,7 +114,8 @@ export function createMemoryGetTool(options: { description: "Safe snippet read from MEMORY.md or memory/*.md with optional from/lines; use after memory_search to pull only the needed lines and keep context small.", parameters: MemoryGetSchema, - execute: async (_toolCallId, params) => { + execute: async (_toolCallId, rawParams) => { + const params = rawParams as Record; const relPath = readStringParam(params, "path", { required: true }); const from = readNumberParam(params, "from", { integer: true }); const lines = readNumberParam(params, "lines", { integer: true }); diff --git a/src/agents/tools/nodes-tool.ts b/src/agents/tools/nodes-tool.ts index d3d85872..6cf7d6e4 100644 --- a/src/agents/tools/nodes-tool.ts +++ b/src/agents/tools/nodes-tool.ts @@ -1,5 +1,5 @@ import crypto from "node:crypto"; -import type { AgentToolResult } from "@mariozechner/pi-agent-core"; +import type { AgentToolResult } from "@earendil-works/pi-agent-core"; import { Type } from "@sinclair/typebox"; import { type CameraFacing, diff --git a/src/agents/tools/slack-actions.ts b/src/agents/tools/slack-actions.ts index e3b08227..bcdb50cf 100644 --- a/src/agents/tools/slack-actions.ts +++ b/src/agents/tools/slack-actions.ts @@ -1,4 +1,4 @@ -import type { AgentToolResult } from "@mariozechner/pi-agent-core"; +import type { AgentToolResult } from "@earendil-works/pi-agent-core"; import type { MayrosConfig } from "../../config/config.js"; import { resolveSlackAccount } from "../../slack/accounts.js"; import { diff --git a/src/agents/tools/telegram-actions.ts b/src/agents/tools/telegram-actions.ts index f2d5f941..057fd9e9 100644 --- a/src/agents/tools/telegram-actions.ts +++ b/src/agents/tools/telegram-actions.ts @@ -1,4 +1,4 @@ -import type { AgentToolResult } from "@mariozechner/pi-agent-core"; +import type { AgentToolResult } from "@earendil-works/pi-agent-core"; import type { MayrosConfig } from "../../config/config.js"; import { createTelegramActionGate } from "../../telegram/accounts.js"; import type { TelegramButtonStyle, TelegramInlineButtons } from "../../telegram/button-types.js"; diff --git a/src/agents/tools/whatsapp-actions.ts b/src/agents/tools/whatsapp-actions.ts index 2957d419..cbaf0d8d 100644 --- a/src/agents/tools/whatsapp-actions.ts +++ b/src/agents/tools/whatsapp-actions.ts @@ -1,4 +1,4 @@ -import type { AgentToolResult } from "@mariozechner/pi-agent-core"; +import type { AgentToolResult } from "@earendil-works/pi-agent-core"; import type { MayrosConfig } from "../../config/config.js"; import { sendReactionWhatsApp } from "../../web/outbound.js"; import { createActionGate, jsonResult, readReactionParams, readStringParam } from "./common.js"; diff --git a/src/agents/zai.live.test.ts b/src/agents/zai.live.test.ts index fbca5a07..428c821d 100644 --- a/src/agents/zai.live.test.ts +++ b/src/agents/zai.live.test.ts @@ -1,4 +1,4 @@ -import { completeSimple, getModel } from "@mariozechner/pi-ai"; +import { completeSimple, getModel } from "@earendil-works/pi-ai/compat"; import { describe, expect, it } from "vitest"; import { isTruthyEnvValue } from "../infra/env.js"; diff --git a/src/auto-reply/reply/commands-export-session.ts b/src/auto-reply/reply/commands-export-session.ts index b05d059d..ead4f437 100644 --- a/src/auto-reply/reply/commands-export-session.ts +++ b/src/auto-reply/reply/commands-export-session.ts @@ -1,8 +1,11 @@ import fs from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; -import type { SessionEntry as PiSessionEntry, SessionHeader } from "@mariozechner/pi-coding-agent"; -import { SessionManager } from "@mariozechner/pi-coding-agent"; +import type { + SessionEntry as PiSessionEntry, + SessionHeader, +} from "@earendil-works/pi-coding-agent"; +import { SessionManager } from "@earendil-works/pi-coding-agent"; import { resolveDefaultSessionStorePath, resolveSessionFilePath, diff --git a/src/auto-reply/reply/commands-system-prompt.ts b/src/auto-reply/reply/commands-system-prompt.ts index a8c4fbe3..159bd4e8 100644 --- a/src/auto-reply/reply/commands-system-prompt.ts +++ b/src/auto-reply/reply/commands-system-prompt.ts @@ -1,4 +1,4 @@ -import type { AgentTool } from "@mariozechner/pi-agent-core"; +import type { AgentTool } from "@earendil-works/pi-agent-core"; import { resolveSessionAgentIds } from "../../agents/agent-scope.js"; import { resolveBootstrapContextForRun } from "../../agents/bootstrap-files.js"; import { resolveDefaultModelForAgent } from "../../agents/model-selection.js"; diff --git a/src/auto-reply/reply/session.ts b/src/auto-reply/reply/session.ts index 01c445f3..2e9def31 100644 --- a/src/auto-reply/reply/session.ts +++ b/src/auto-reply/reply/session.ts @@ -1,7 +1,7 @@ import crypto from "node:crypto"; import fs from "node:fs"; import path from "node:path"; -import { CURRENT_SESSION_VERSION, SessionManager } from "@mariozechner/pi-coding-agent"; +import { CURRENT_SESSION_VERSION, SessionManager } from "@earendil-works/pi-coding-agent"; import { resolveSessionAgentId } from "../../agents/agent-scope.js"; import { normalizeChatType } from "../../channels/chat-type.js"; import type { MayrosConfig } from "../../config/config.js"; diff --git a/src/auto-reply/types.ts b/src/auto-reply/types.ts index 839fac55..34f5ce64 100644 --- a/src/auto-reply/types.ts +++ b/src/auto-reply/types.ts @@ -1,4 +1,4 @@ -import type { ImageContent } from "@mariozechner/pi-ai"; +import type { ImageContent } from "@earendil-works/pi-ai"; import type { TypingController } from "./reply/typing.js"; export type BlockReplyContext = { diff --git a/src/channels/plugins/actions/discord/handle-action.guild-admin.ts b/src/channels/plugins/actions/discord/handle-action.guild-admin.ts index 18c3bfd0..eca79687 100644 --- a/src/channels/plugins/actions/discord/handle-action.guild-admin.ts +++ b/src/channels/plugins/actions/discord/handle-action.guild-admin.ts @@ -1,4 +1,4 @@ -import type { AgentToolResult } from "@mariozechner/pi-agent-core"; +import type { AgentToolResult } from "@earendil-works/pi-agent-core"; import { parseAvailableTags, readNumberParam, diff --git a/src/channels/plugins/actions/discord/handle-action.ts b/src/channels/plugins/actions/discord/handle-action.ts index a2711dc0..4579ed6d 100644 --- a/src/channels/plugins/actions/discord/handle-action.ts +++ b/src/channels/plugins/actions/discord/handle-action.ts @@ -1,4 +1,4 @@ -import type { AgentToolResult } from "@mariozechner/pi-agent-core"; +import type { AgentToolResult } from "@earendil-works/pi-agent-core"; import { readNumberParam, readStringArrayParam, diff --git a/src/channels/plugins/message-actions.ts b/src/channels/plugins/message-actions.ts index 86834661..3e360bcd 100644 --- a/src/channels/plugins/message-actions.ts +++ b/src/channels/plugins/message-actions.ts @@ -1,4 +1,4 @@ -import type { AgentToolResult } from "@mariozechner/pi-agent-core"; +import type { AgentToolResult } from "@earendil-works/pi-agent-core"; import type { MayrosConfig } from "../../config/config.js"; import { getChannelPlugin, listChannelPlugins } from "./index.js"; import type { ChannelMessageActionContext, ChannelMessageActionName } from "./types.js"; diff --git a/src/channels/plugins/types.core.ts b/src/channels/plugins/types.core.ts index f1e163f2..f580de83 100644 --- a/src/channels/plugins/types.core.ts +++ b/src/channels/plugins/types.core.ts @@ -1,4 +1,4 @@ -import type { AgentTool, AgentToolResult } from "@mariozechner/pi-agent-core"; +import type { AgentTool, AgentToolResult } from "@earendil-works/pi-agent-core"; import type { TSchema } from "@sinclair/typebox"; import type { MsgContext } from "../../auto-reply/templating.js"; import type { MayrosConfig } from "../../config/config.js"; diff --git a/src/cli/skills-cli.e2e.test.ts b/src/cli/skills-cli.e2e.test.ts index 345587a0..2f700f45 100644 --- a/src/cli/skills-cli.e2e.test.ts +++ b/src/cli/skills-cli.e2e.test.ts @@ -36,7 +36,12 @@ describe("skills-cli (e2e)", () => { skill: { name: "peekaboo", description: "Capture UI screenshots", - source: "mayros-bundled", + sourceInfo: { + path: path.join(baseDir, "SKILL.md"), + source: "mayros-bundled", + scope: "project", + origin: "top-level", + }, filePath: path.join(baseDir, "SKILL.md"), baseDir, } as SkillEntry["skill"], diff --git a/src/cli/skills-cli.test.ts b/src/cli/skills-cli.test.ts index 5546ed9e..44e4e72c 100644 --- a/src/cli/skills-cli.test.ts +++ b/src/cli/skills-cli.test.ts @@ -4,7 +4,7 @@ import { createEmptyInstallChecks } from "./requirements-test-fixtures.js"; import { formatSkillInfo, formatSkillsCheck, formatSkillsList } from "./skills-cli.format.js"; // Unit tests: don't pay the runtime cost of loading/parsing the real skills loader. -vi.mock("@mariozechner/pi-coding-agent", () => ({ +vi.mock("@earendil-works/pi-coding-agent", () => ({ loadSkillsFromDir: () => ({ skills: [] }), formatSkillsForPrompt: () => "", })); diff --git a/src/commands/auth-choice.e2e.test.ts b/src/commands/auth-choice.e2e.test.ts index 5718187e..aa7fbba8 100644 --- a/src/commands/auth-choice.e2e.test.ts +++ b/src/commands/auth-choice.e2e.test.ts @@ -1,5 +1,5 @@ import fs from "node:fs/promises"; -import type { OAuthCredentials } from "@mariozechner/pi-ai"; +import type { OAuthCredentials } from "@earendil-works/pi-ai"; import { afterEach, describe, expect, it, vi } from "vitest"; import type { WizardPrompter } from "../wizard/prompts.js"; import { applyAuthChoice, resolvePreferredProviderForAuthChoice } from "./auth-choice.js"; diff --git a/src/commands/chutes-oauth.ts b/src/commands/chutes-oauth.ts index cc4d88b2..0e686c74 100644 --- a/src/commands/chutes-oauth.ts +++ b/src/commands/chutes-oauth.ts @@ -1,6 +1,6 @@ import { randomBytes } from "node:crypto"; import { createServer } from "node:http"; -import type { OAuthCredentials } from "@mariozechner/pi-ai"; +import type { OAuthCredentials } from "@earendil-works/pi-ai"; import type { ChutesOAuthAppConfig } from "../agents/chutes-oauth.js"; import { CHUTES_AUTHORIZE_ENDPOINT, diff --git a/src/commands/models/list.list-command.ts b/src/commands/models/list.list-command.ts index dc195985..2b30157f 100644 --- a/src/commands/models/list.list-command.ts +++ b/src/commands/models/list.list-command.ts @@ -1,4 +1,4 @@ -import type { Api, Model } from "@mariozechner/pi-ai"; +import type { Api, Model } from "@earendil-works/pi-ai"; import { resolveForwardCompatModel } from "../../agents/model-forward-compat.js"; import { parseModelRef } from "../../agents/model-selection.js"; import type { ModelRegistry } from "../../agents/pi-model-discovery.js"; diff --git a/src/commands/models/list.registry.ts b/src/commands/models/list.registry.ts index 27d9f29f..aad52655 100644 --- a/src/commands/models/list.registry.ts +++ b/src/commands/models/list.registry.ts @@ -1,4 +1,4 @@ -import type { Api, Model } from "@mariozechner/pi-ai"; +import type { Api, Model } from "@earendil-works/pi-ai"; import { resolveMayrosAgentDir } from "../../agents/agent-paths.js"; import type { AuthProfileStore } from "../../agents/auth-profiles.js"; import { listProfilesForProvider } from "../../agents/auth-profiles.js"; diff --git a/src/commands/onboard-auth.credentials.ts b/src/commands/onboard-auth.credentials.ts index e224ff8f..f00c4e59 100644 --- a/src/commands/onboard-auth.credentials.ts +++ b/src/commands/onboard-auth.credentials.ts @@ -1,6 +1,6 @@ import fs from "node:fs"; import path from "node:path"; -import type { OAuthCredentials } from "@mariozechner/pi-ai"; +import type { OAuthCredentials } from "@earendil-works/pi-ai"; import { resolveMayrosAgentDir } from "../agents/agent-paths.js"; import { upsertAuthProfile } from "../agents/auth-profiles.js"; import { resolveStateDir } from "../config/paths.js"; diff --git a/src/commands/onboard-auth.e2e.test.ts b/src/commands/onboard-auth.e2e.test.ts index 92a59142..0643866c 100644 --- a/src/commands/onboard-auth.e2e.test.ts +++ b/src/commands/onboard-auth.e2e.test.ts @@ -1,7 +1,7 @@ import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; -import type { OAuthCredentials } from "@mariozechner/pi-ai"; +import type { OAuthCredentials } from "@earendil-works/pi-ai"; import { afterEach, describe, expect, it } from "vitest"; import type { MayrosConfig } from "../config/config.js"; import { diff --git a/src/commands/openai-codex-oauth.test.ts b/src/commands/openai-codex-oauth.test.ts index d68645d9..167e2b80 100644 --- a/src/commands/openai-codex-oauth.test.ts +++ b/src/commands/openai-codex-oauth.test.ts @@ -7,7 +7,7 @@ const mocks = vi.hoisted(() => ({ createVpsAwareOAuthHandlers: vi.fn(), })); -vi.mock("@mariozechner/pi-ai", () => ({ +vi.mock("@earendil-works/pi-ai", () => ({ loginOpenAICodex: mocks.loginOpenAICodex, })); diff --git a/src/commands/openai-codex-oauth.ts b/src/commands/openai-codex-oauth.ts index 7c7e4a68..e0a0f022 100644 --- a/src/commands/openai-codex-oauth.ts +++ b/src/commands/openai-codex-oauth.ts @@ -1,5 +1,5 @@ -import type { OAuthCredentials } from "@mariozechner/pi-ai"; -import { loginOpenAICodex } from "@mariozechner/pi-ai"; +import type { OAuthCredentials } from "@earendil-works/pi-ai"; +import { loginOpenAICodex } from "@earendil-works/pi-ai/oauth"; import type { RuntimeEnv } from "../runtime.js"; import type { WizardPrompter } from "../wizard/prompts.js"; import { createVpsAwareOAuthHandlers } from "./oauth-flow.js"; diff --git a/src/config/includes.test.ts b/src/config/includes.test.ts index 064198e4..de3ed0c5 100644 --- a/src/config/includes.test.ts +++ b/src/config/includes.test.ts @@ -565,7 +565,11 @@ describe("security: path traversal protection (CWE-22)", () => { expect(resolve(obj, files, rootConfigPath)).toEqual({ root: true }); }); - it("allows include files when the config root path is a symlink", async () => { + // Creating symlinks on Windows requires Developer Mode or admin rights + // (EPERM otherwise), so skip this case there. CI runs on Linux where + // unprivileged symlink creation works. + const itSymlink = process.platform === "win32" ? it.skip : it; + itSymlink("allows include files when the config root path is a symlink", async () => { const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "mayros-includes-symlink-")); try { const realRoot = path.join(tempRoot, "real"); diff --git a/src/config/sessions/transcript.ts b/src/config/sessions/transcript.ts index 720fd69e..85cc21d1 100644 --- a/src/config/sessions/transcript.ts +++ b/src/config/sessions/transcript.ts @@ -1,6 +1,6 @@ import fs from "node:fs"; import path from "node:path"; -import { CURRENT_SESSION_VERSION, SessionManager } from "@mariozechner/pi-coding-agent"; +import { CURRENT_SESSION_VERSION, SessionManager } from "@earendil-works/pi-coding-agent"; import { emitSessionTranscriptUpdate } from "../../sessions/transcript-events.js"; import { resolveDefaultSessionStorePath } from "./paths.js"; import { resolveAndPersistSessionFile } from "./session-file.js"; diff --git a/src/config/sessions/types.ts b/src/config/sessions/types.ts index 10d1d3bc..1e2093ca 100644 --- a/src/config/sessions/types.ts +++ b/src/config/sessions/types.ts @@ -1,5 +1,5 @@ import crypto from "node:crypto"; -import type { Skill } from "@mariozechner/pi-coding-agent"; +import type { Skill } from "@earendil-works/pi-coding-agent"; import type { ChatType } from "../../channels/chat-type.js"; import type { ChannelId } from "../../channels/plugins/types.js"; import type { DeliveryContext } from "../../utils/delivery-context.js"; diff --git a/src/config/types.mayros.ts b/src/config/types.mayros.ts index 8c8bdc4e..b77abeab 100644 --- a/src/config/types.mayros.ts +++ b/src/config/types.mayros.ts @@ -68,9 +68,9 @@ export type MayrosConfig = { ui?: { /** Accent color for Mayros UI chrome (hex). */ seamColor?: string; - /** TUI color theme preset. */ - theme?: "dark" | "light" | "high-contrast"; - /** Enable vim editing mode in the TUI. */ + /** TUI color theme preset, or "auto" to detect from the terminal. */ + theme?: "auto" | "dark" | "light" | "high-contrast"; + /** Deprecated: vim mode is not available. Kept for config backward compat. */ vim?: boolean; /** Custom keybindings for TUI actions. */ keybindings?: Record; diff --git a/src/gateway/gateway-models.profiles.live.test.ts b/src/gateway/gateway-models.profiles.live.test.ts index 214a63ba..d23af5d9 100644 --- a/src/gateway/gateway-models.profiles.live.test.ts +++ b/src/gateway/gateway-models.profiles.live.test.ts @@ -3,7 +3,7 @@ import fs from "node:fs/promises"; import { createServer } from "node:net"; import os from "node:os"; import path from "node:path"; -import type { Api, Model } from "@mariozechner/pi-ai"; +import type { Api, Model } from "@earendil-works/pi-ai"; import { describe, it } from "vitest"; import { resolveMayrosAgentDir } from "../agents/agent-paths.js"; import { resolveAgentWorkspaceDir } from "../agents/agent-scope.js"; diff --git a/src/gateway/server-methods/chat.abort-persistence.test.ts b/src/gateway/server-methods/chat.abort-persistence.test.ts index 5453fdbd..a81aa1c4 100644 --- a/src/gateway/server-methods/chat.abort-persistence.test.ts +++ b/src/gateway/server-methods/chat.abort-persistence.test.ts @@ -1,7 +1,7 @@ import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; -import { CURRENT_SESSION_VERSION } from "@mariozechner/pi-coding-agent"; +import { CURRENT_SESSION_VERSION } from "@earendil-works/pi-coding-agent"; import { afterEach, describe, expect, it, vi } from "vitest"; type TranscriptLine = { diff --git a/src/gateway/server-methods/chat.inject.parentid.e2e.test.ts b/src/gateway/server-methods/chat.inject.parentid.e2e.test.ts index 60fbec4c..9c21afb3 100644 --- a/src/gateway/server-methods/chat.inject.parentid.e2e.test.ts +++ b/src/gateway/server-methods/chat.inject.parentid.e2e.test.ts @@ -1,7 +1,7 @@ import fs from "node:fs"; import os from "node:os"; import path from "node:path"; -import { CURRENT_SESSION_VERSION } from "@mariozechner/pi-coding-agent"; +import { CURRENT_SESSION_VERSION } from "@earendil-works/pi-coding-agent"; import { describe, expect, it, vi } from "vitest"; import type { GatewayRequestContext } from "./types.js"; diff --git a/src/gateway/server-methods/chat.ts b/src/gateway/server-methods/chat.ts index cb040350..22ead1ff 100644 --- a/src/gateway/server-methods/chat.ts +++ b/src/gateway/server-methods/chat.ts @@ -1,6 +1,6 @@ import fs from "node:fs"; import path from "node:path"; -import { CURRENT_SESSION_VERSION, SessionManager } from "@mariozechner/pi-coding-agent"; +import { CURRENT_SESSION_VERSION, SessionManager } from "@earendil-works/pi-coding-agent"; import { resolveSessionAgentId } from "../../agents/agent-scope.js"; import { resolveThinkingDefault } from "../../agents/model-selection.js"; import { resolveAgentTimeoutMs } from "../../agents/timeout.js"; diff --git a/src/gateway/test-helpers.mocks.ts b/src/gateway/test-helpers.mocks.ts index 3f1501bd..61d16411 100644 --- a/src/gateway/test-helpers.mocks.ts +++ b/src/gateway/test-helpers.mocks.ts @@ -243,20 +243,25 @@ vi.mock("../agents/pi-model-discovery.js", async () => { "../agents/pi-model-discovery.js", ); - class MockModelRegistry extends actual.ModelRegistry { - override getAll(): ReturnType { + // ModelRegistry now has a private constructor and cannot be subclassed. + // Patch the instance returned by discoverModels to intercept getAll(). + const mockedDiscoverModels = ((authStorage, agentDir) => { + const registry = actual.discoverModels(authStorage, agentDir); + const originalGetAll = registry.getAll.bind(registry); + registry.getAll = () => { if (!piSdkMock.enabled) { - return super.getAll(); + return originalGetAll(); } piSdkMock.discoverCalls += 1; // Cast to expected type for testing purposes - return piSdkMock.models as ReturnType; - } - } + return piSdkMock.models as ReturnType; + }; + return registry; + }) as typeof actual.discoverModels; return { ...actual, - ModelRegistry: MockModelRegistry, + discoverModels: mockedDiscoverModels, }; }); diff --git a/src/infra/outbound/message-action-runner.ts b/src/infra/outbound/message-action-runner.ts index 6fe0ea87..3ef9001c 100644 --- a/src/infra/outbound/message-action-runner.ts +++ b/src/infra/outbound/message-action-runner.ts @@ -1,4 +1,4 @@ -import type { AgentToolResult } from "@mariozechner/pi-agent-core"; +import type { AgentToolResult } from "@earendil-works/pi-agent-core"; import { resolveSessionAgentId } from "../../agents/agent-scope.js"; import { readNumberParam, diff --git a/src/infra/outbound/outbound-send-service.ts b/src/infra/outbound/outbound-send-service.ts index 1ece9de6..94689734 100644 --- a/src/infra/outbound/outbound-send-service.ts +++ b/src/infra/outbound/outbound-send-service.ts @@ -1,4 +1,4 @@ -import type { AgentToolResult } from "@mariozechner/pi-agent-core"; +import type { AgentToolResult } from "@earendil-works/pi-agent-core"; import { dispatchChannelMessageAction } from "../../channels/plugins/message-actions.js"; import type { ChannelId, ChannelThreadingToolContext } from "../../channels/plugins/types.js"; import type { MayrosConfig } from "../../config/config.js"; diff --git a/src/infra/outbound/tool-payload.ts b/src/infra/outbound/tool-payload.ts index 33a8d1fd..23712a65 100644 --- a/src/infra/outbound/tool-payload.ts +++ b/src/infra/outbound/tool-payload.ts @@ -1,4 +1,4 @@ -import type { AgentToolResult } from "@mariozechner/pi-agent-core"; +import type { AgentToolResult } from "@earendil-works/pi-agent-core"; export function extractToolPayload(result: AgentToolResult): unknown { if (result.details !== undefined) { diff --git a/src/media-understanding/providers/image.ts b/src/media-understanding/providers/image.ts index 9665a0ef..60ec8326 100644 --- a/src/media-understanding/providers/image.ts +++ b/src/media-understanding/providers/image.ts @@ -1,5 +1,5 @@ -import type { Api, Context, Model } from "@mariozechner/pi-ai"; -import { complete } from "@mariozechner/pi-ai"; +import type { Api, Context, Model } from "@earendil-works/pi-ai"; +import { complete } from "@earendil-works/pi-ai/compat"; import { minimaxUnderstandImage } from "../../agents/minimax-vlm.js"; import { getApiKeyForModel, requireApiKey } from "../../agents/model-auth.js"; import { ensureMayrosModelsJson } from "../../agents/models-config.js"; diff --git a/src/plugin-sdk/slack-message-actions.ts b/src/plugin-sdk/slack-message-actions.ts index 77b24b95..d1522e6c 100644 --- a/src/plugin-sdk/slack-message-actions.ts +++ b/src/plugin-sdk/slack-message-actions.ts @@ -1,4 +1,4 @@ -import type { AgentToolResult } from "@mariozechner/pi-agent-core"; +import type { AgentToolResult } from "@earendil-works/pi-agent-core"; import { readNumberParam, readStringParam } from "../agents/tools/common.js"; import type { ChannelMessageActionContext } from "../channels/plugins/types.js"; import { parseSlackBlocksInput } from "../slack/blocks-input.js"; diff --git a/src/plugins/types.ts b/src/plugins/types.ts index de30f31d..56d0e6b9 100644 --- a/src/plugins/types.ts +++ b/src/plugins/types.ts @@ -1,5 +1,5 @@ import type { IncomingMessage, ServerResponse } from "node:http"; -import type { AgentMessage } from "@mariozechner/pi-agent-core"; +import type { AgentMessage } from "@earendil-works/pi-agent-core"; import type { Command } from "commander"; import type { AuthProfileCredential, OAuthCredential } from "../agents/auth-profiles/types.js"; import type { AnyAgentTool } from "../agents/tools/common.js"; diff --git a/src/providers/google-shared.ensures-function-call-comes-after-user-turn.test.ts b/src/providers/google-shared.ensures-function-call-comes-after-user-turn.test.ts index 9f209f3b..a6603e91 100644 --- a/src/providers/google-shared.ensures-function-call-comes-after-user-turn.test.ts +++ b/src/providers/google-shared.ensures-function-call-comes-after-user-turn.test.ts @@ -1,5 +1,5 @@ -import { convertMessages } from "@mariozechner/pi-ai/dist/providers/google-shared.js"; -import type { Context } from "@mariozechner/pi-ai/dist/types.js"; +import { convertMessages } from "@earendil-works/pi-ai/api/google-shared"; +import type { Context, Model } from "@earendil-works/pi-ai"; import { describe, expect, it } from "vitest"; import { asRecord, @@ -70,7 +70,7 @@ describe("google-shared convertTools", () => { ], } as unknown as Context; - const contents = convertMessages(model, context); + const contents = convertMessages(model as unknown as Model<"google-generative-ai">, context); const parts = contents.flatMap((content) => content.parts ?? []); const toolCallPart = parts.find( (part) => typeof part === "object" && part !== null && "functionCall" in part, diff --git a/src/providers/google-shared.preserves-parameters-type-is-missing.test.ts b/src/providers/google-shared.preserves-parameters-type-is-missing.test.ts index 3dc27a4c..6b5c4265 100644 --- a/src/providers/google-shared.preserves-parameters-type-is-missing.test.ts +++ b/src/providers/google-shared.preserves-parameters-type-is-missing.test.ts @@ -1,5 +1,5 @@ -import { convertMessages, convertTools } from "@mariozechner/pi-ai/dist/providers/google-shared.js"; -import type { Context, Tool } from "@mariozechner/pi-ai/dist/types.js"; +import { convertMessages, convertTools } from "@earendil-works/pi-ai/api/google-shared"; +import type { Context, Tool } from "@earendil-works/pi-ai"; import { describe, expect, it } from "vitest"; import { asRecord, diff --git a/src/providers/google-shared.test-helpers.ts b/src/providers/google-shared.test-helpers.ts index c98fad72..a4985426 100644 --- a/src/providers/google-shared.test-helpers.ts +++ b/src/providers/google-shared.test-helpers.ts @@ -1,4 +1,4 @@ -import type { Model } from "@mariozechner/pi-ai/dist/types.js"; +import type { Model } from "@earendil-works/pi-ai"; import { expect } from "vitest"; export const asRecord = (value: unknown): Record => { diff --git a/src/providers/qwen-portal-oauth.ts b/src/providers/qwen-portal-oauth.ts index 276b2e34..f5f1e982 100644 --- a/src/providers/qwen-portal-oauth.ts +++ b/src/providers/qwen-portal-oauth.ts @@ -1,4 +1,4 @@ -import type { OAuthCredentials } from "@mariozechner/pi-ai"; +import type { OAuthCredentials } from "@earendil-works/pi-ai"; import { formatCliCommand } from "../cli/command-format.js"; const QWEN_OAUTH_BASE_URL = "https://chat.qwen.ai"; diff --git a/src/security/audit-extra.async.ts b/src/security/audit-extra.async.ts index 8b26fd2a..f87a7072 100644 --- a/src/security/audit-extra.async.ts +++ b/src/security/audit-extra.async.ts @@ -1076,7 +1076,7 @@ export async function collectInstalledSkillsCodeSafetyFindings(params: { for (const workspaceDir of workspaceDirs) { const entries = loadWorkspaceSkillEntries(workspaceDir, { config: params.cfg }); for (const entry of entries) { - if (entry.skill.source === "mayros-bundled") { + if (entry.skill.sourceInfo.source === "mayros-bundled") { continue; } diff --git a/src/sessions/input-provenance.ts b/src/sessions/input-provenance.ts index 4540e680..6ba32fc4 100644 --- a/src/sessions/input-provenance.ts +++ b/src/sessions/input-provenance.ts @@ -1,4 +1,4 @@ -import type { AgentMessage } from "@mariozechner/pi-agent-core"; +import type { AgentMessage } from "@earendil-works/pi-agent-core"; export const INPUT_PROVENANCE_KIND_VALUES = [ "external_user", diff --git a/src/terminal/palette.ts b/src/terminal/palette.ts index 44fb5947..637c8582 100644 --- a/src/terminal/palette.ts +++ b/src/terminal/palette.ts @@ -1,12 +1,14 @@ // Mayros palette tokens for CLI/UI theming. "mayros seam" == use this palette. -// Keep in sync with docs/cli/index.md (CLI palette section). +// Values are derived from the TUI dark palette (src/tui/theme/palettes.ts) so +// the CLI and the terminal UI share the same identity. Keep in sync with +// docs/cli/index.md (CLI palette section). export const MAYROS_PALETTE = { - accent: "#FF5A2D", - accentBright: "#FF7A3D", - accentDim: "#D14A22", - info: "#FF8A5B", - success: "#2FBF71", - warn: "#FFB020", - error: "#E23D2D", - muted: "#8B7F77", + accent: "#F6C453", + accentBright: "#F2A65A", + accentDim: "#C49A3E", + info: "#F2A65A", + success: "#7DD3A5", + warn: "#F0C987", + error: "#F97066", + muted: "#7B7F87", } as const; diff --git a/src/tts/tts-core.ts b/src/tts/tts-core.ts index 92572565..0fd4121a 100644 --- a/src/tts/tts-core.ts +++ b/src/tts/tts-core.ts @@ -1,5 +1,6 @@ import { rmSync } from "node:fs"; -import { completeSimple, type TextContent } from "@mariozechner/pi-ai"; +import type { TextContent } from "@earendil-works/pi-ai"; +import { completeSimple } from "@earendil-works/pi-ai/compat"; import { EdgeTTS } from "node-edge-tts"; import { getApiKeyForModel, requireApiKey } from "../agents/model-auth.js"; import { diff --git a/src/tts/tts.test.ts b/src/tts/tts.test.ts index 38a2e762..546f12bc 100644 --- a/src/tts/tts.test.ts +++ b/src/tts/tts.test.ts @@ -1,4 +1,5 @@ -import { completeSimple, type AssistantMessage } from "@mariozechner/pi-ai"; +import type { AssistantMessage } from "@earendil-works/pi-ai"; +import { completeSimple } from "@earendil-works/pi-ai/compat"; import { describe, expect, it, vi, beforeEach } from "vitest"; import { getApiKeyForModel } from "../agents/model-auth.js"; import { resolveModel } from "../agents/pi-embedded-runner/model.js"; @@ -6,8 +7,11 @@ import type { MayrosConfig } from "../config/config.js"; import { withEnv } from "../test-utils/env.js"; import * as tts from "./tts.js"; -vi.mock("@mariozechner/pi-ai", () => ({ +vi.mock("@earendil-works/pi-ai/compat", () => ({ completeSimple: vi.fn(), +})); + +vi.mock("@earendil-works/pi-ai/oauth", () => ({ // Some auth helpers import oauth provider metadata at module load time. getOAuthProviders: () => [], getOAuthApiKey: vi.fn(async () => null), diff --git a/src/tui/a11y-tui.ts b/src/tui/a11y-tui.ts index dbeaaf25..0873775d 100644 --- a/src/tui/a11y-tui.ts +++ b/src/tui/a11y-tui.ts @@ -2,6 +2,7 @@ import process from "node:process"; import readline from "node:readline"; import { randomUUID } from "node:crypto"; import { GatewayChatClient, resolveGatewayConnection, type GatewayEvent } from "./gateway-chat.js"; +import { handleSharedCommand } from "./shared-commands.js"; import { TuiStreamAssembler } from "./tui-stream-assembler.js"; import { A11yRenderer } from "./a11y-renderer.js"; import type { TuiOptions } from "./tui-types.js"; @@ -22,7 +23,7 @@ export async function runA11yTui(opts: TuiOptions): Promise { password: connection.password, }); - const sessionKey = opts.session ?? "main"; + let sessionKey = opts.session ?? "main"; const assembler = new TuiStreamAssembler(); const showThinking = opts.thinking === "on" || opts.thinking === "verbose"; @@ -95,6 +96,15 @@ export async function runA11yTui(opts: TuiOptions): Promise { } }; + // Declare the readline interface before wiring client disconnect handling, + // so a disconnect between client.start() and the readline creation cannot + // reference `rl` in its temporal dead zone. + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + prompt: "> ", + }); + client.onDisconnected = (reason: string) => { renderer.emit({ type: "system", text: `Disconnected: ${reason}` }); rl.close(); @@ -125,15 +135,9 @@ export async function runA11yTui(opts: TuiOptions): Promise { }); } - const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout, - prompt: "> ", - }); - rl.prompt(); - rl.on("line", (line) => { + rl.on("line", async (line) => { const text = line.trim(); if (!text) { rl.prompt(); @@ -147,6 +151,36 @@ export async function runA11yTui(opts: TuiOptions): Promise { return; } + // Route slash commands through the shared command core so the accessible + // TUI reaches feature parity with the graphical TUI for the commands that + // do not require a visual picker (abort, reset, compact, status, etc.). + if (text.startsWith("/")) { + const [name, ...rest] = text.slice(1).split(/\s+/); + if (name) { + const sink = { + info: (msg: string) => renderer.emit({ type: "system", text: msg }), + error: (msg: string) => renderer.emit({ type: "system", text: msg }), + }; + const handled = await handleSharedCommand( + { + client, + sessionKey, + getActiveRunId: () => currentRunId, + onSessionChanged: (key) => { + sessionKey = key; + }, + }, + name.toLowerCase(), + rest.join(" ").trim(), + sink, + ); + if (handled.handled) { + rl.prompt(); + return; + } + } + } + const runId = randomUUID(); currentRunId = runId; lastOutputLength = 0; diff --git a/src/tui/clipboard-image.ts b/src/tui/clipboard-image.ts index edefa664..ff4c1fc4 100644 --- a/src/tui/clipboard-image.ts +++ b/src/tui/clipboard-image.ts @@ -13,15 +13,23 @@ export type ClipboardImage = { * Attempts to capture an image from the system clipboard. * Returns the image as base64 + mimeType, or null if no image is on the clipboard. * - * Supports macOS (pbpaste/osascript) and Linux (xclip). + * Supports macOS (osascript), Linux (xclip or wl-paste on Wayland), and + * Windows (PowerShell + System.Windows.Forms). */ export function captureClipboardImage(): ClipboardImage | null { if (process.platform === "darwin") { return captureMacOS(); } if (process.platform === "linux") { + // Wayland: prefer wl-paste when WAYLAND_DISPLAY is set. + if (process.env.WAYLAND_DISPLAY) { + return captureWayland() ?? captureLinux(); + } return captureLinux(); } + if (process.platform === "win32") { + return captureWindows(); + } return null; } @@ -124,3 +132,70 @@ function captureLinux(): ClipboardImage | null { return null; } } + +/** Capture clipboard image on Wayland via wl-paste. */ +function captureWayland(): ClipboardImage | null { + try { + // Check available MIME types + const types = execSync("wl-paste -l", { + encoding: "utf-8", + timeout: 3000, + stdio: ["pipe", "pipe", "pipe"], + }); + if (!types.includes("image/png")) { + return null; + } + } catch { + return null; + } + + try { + const buf = execSync("wl-paste -t image/png", { + timeout: 5000, + stdio: ["pipe", "pipe", "pipe"], + maxBuffer: 50 * 1024 * 1024, + }); + if (buf.length === 0) { + return null; + } + return { + base64: buf.toString("base64"), + mimeType: "image/png", + }; + } catch { + return null; + } +} + +/** Capture clipboard image on Windows via PowerShell + System.Windows.Forms. */ +function captureWindows(): ClipboardImage | null { + // PowerShell script: check if clipboard has an image, get it as PNG bytes, + // and write base64 to stdout. + const script = [ + "Add-Type -AssemblyName System.Windows.Forms", + "Add-Type -AssemblyName System.Drawing", + "$img = [System.Windows.Forms.Clipboard]::GetImage()", + "if ($null -eq $img) { exit 1 }", + "$ms = New-Object System.IO.MemoryStream", + "$img.Save($ms, [System.Drawing.Imaging.ImageFormat]::Png)", + "[Console]::OpenStandardOutput().Write($ms.ToArray(), 0, $ms.Length)", + ].join("; "); + + try { + const buf = execSync(`powershell -NoProfile -Command "${script}"`, { + timeout: 5000, + stdio: ["pipe", "pipe", "pipe"], + maxBuffer: 50 * 1024 * 1024, + windowsHide: true, + }); + if (buf.length === 0) { + return null; + } + return { + base64: buf.toString("base64"), + mimeType: "image/png", + }; + } catch { + return null; + } +} diff --git a/src/tui/commands.ts b/src/tui/commands.ts index a6244d52..42b71b19 100644 --- a/src/tui/commands.ts +++ b/src/tui/commands.ts @@ -1,26 +1,15 @@ -import type { SlashCommand } from "@mariozechner/pi-tui"; +import type { SlashCommand } from "@earendil-works/pi-tui"; import { listChatCommands, listChatCommandsForConfig } from "../auto-reply/commands-registry.js"; import { formatThinkingLevels, listThinkingLevelLabels } from "../auto-reply/thinking.js"; import { discoverMarkdownCommands } from "../commands/markdown-commands.js"; import type { MayrosConfig } from "../config/types.js"; +import { THEME_PRESETS } from "./theme/palettes.js"; const VERBOSE_LEVELS = ["on", "off"]; const REASONING_LEVELS = ["on", "off"]; const ELEVATED_LEVELS = ["on", "off", "ask", "full"]; const ACTIVATION_LEVELS = ["mention", "always"]; const USAGE_FOOTER_LEVELS = ["off", "tokens", "full"]; -const THEME_PRESETS = [ - "dark", - "light", - "high-contrast", - "dracula", - "github-dark", - "github-light", - "solarized-dark", - "solarized-light", - "atom-one-dark", - "ayu-dark", -]; const OUTPUT_STYLES = ["standard", "explanatory", "learning"]; const PERMISSION_MODES = ["auto", "ask", "deny"]; @@ -148,7 +137,7 @@ export function getSlashCommands(options: SlashCommandOptions = {}): SlashComman }, { name: "vim", - description: "Toggle vim editing mode", + description: "Vim mode (unavailable)", }, { name: "permission", diff --git a/src/tui/compact-handler.ts b/src/tui/compact-handler.ts index f3a2ec89..83c561e4 100644 --- a/src/tui/compact-handler.ts +++ b/src/tui/compact-handler.ts @@ -5,6 +5,10 @@ * then creates a summary replacing the history. */ +import { createSubsystemLogger } from "../logging/subsystem.js"; + +const logger = createSubsystemLogger("tui"); + export type CompactOptions = { messages: Array<{ role: string; content: string }>; sessionKey: string; @@ -184,9 +188,9 @@ export async function compactMessages(options: CompactOptions): Promise theme.assistantText(line), }); - this.addChild(new Spacer(1)); - this.addChild(this.body); } setText(text: string) { - this.body.setText(text); + const boundary = findStableBoundary(text); + const prefixText = boundary > 0 ? text.slice(0, boundary) : ""; + const suffixText = boundary > 0 ? text.slice(boundary) : text; + + // Only update the prefix when it actually grew β€” this is what lets the + // Markdown cache hit on every delta once a block is committed. + if (prefixText !== this.committedPrefixText) { + this.committedPrefixText = prefixText; + this.stablePrefix.setText(prefixText); + } + // The suffix always changes on each delta. + this.activeSuffix.setText(suffixText); } } diff --git a/src/tui/components/chat-log.ts b/src/tui/components/chat-log.ts index 79ca5adb..18187496 100644 --- a/src/tui/components/chat-log.ts +++ b/src/tui/components/chat-log.ts @@ -1,5 +1,6 @@ -import type { Component } from "@mariozechner/pi-tui"; -import { Container, Spacer, Text } from "@mariozechner/pi-tui"; +import type { Component } from "@earendil-works/pi-tui"; +import { Container, Spacer, Text } from "@earendil-works/pi-tui"; +import type { SectionState } from "../tui-types.js"; import { theme } from "../theme/theme.js"; import { AssistantMessageComponent } from "./assistant-message.js"; import { ToolExecutionComponent } from "./tool-execution.js"; @@ -207,4 +208,11 @@ export class ChatLog extends Container { tool.setExpanded(expanded); } } + + setToolSectionState(state: SectionState) { + this.toolsExpanded = state === "expanded"; + for (const tool of this.toolById.values()) { + tool.setSectionState(state); + } + } } diff --git a/src/tui/components/custom-editor.ts b/src/tui/components/custom-editor.ts index ce2f5ffd..02095400 100644 --- a/src/tui/components/custom-editor.ts +++ b/src/tui/components/custom-editor.ts @@ -1,7 +1,6 @@ -import { Editor, Key, matchesKey } from "@mariozechner/pi-tui"; +import { Editor, Key, matchesKey } from "@earendil-works/pi-tui"; import type { ClipboardImage } from "../clipboard-image.js"; import type { TuiKeybindingResolver } from "../keybinding-resolver.js"; -import type { VimHandler } from "../vim-handler.js"; const BRACKET_PASTE_START = "\x1b[200~"; const BRACKET_PASTE_END = "\x1b[201~"; @@ -25,7 +24,6 @@ export class CustomEditor extends Editor { onAltEnter?: () => void; onImagePaste?: (image: ImagePasteEvent) => void; tuiResolver?: TuiKeybindingResolver; - vimHandler?: VimHandler; private imageCounter = 0; captureClipboardImage: (() => ClipboardImage | null) | null = null; @@ -58,12 +56,6 @@ export class CustomEditor extends Editor { // No image on clipboard β€” ignore the empty paste return; } - // Vim mode intercept: if vim is in normal mode, consume keys there. - if (this.vimHandler?.isNormalMode()) { - if (this.vimHandler.handleKey(data)) { - return; - } - } if (matchesKey(data, Key.alt("enter")) && this.onAltEnter) { this.onAltEnter(); return; @@ -118,10 +110,6 @@ export class CustomEditor extends Editor { return; } if (matchesKey(data, Key.escape) && !this.isShowingAutocomplete()) { - // In vim insert mode, Escape switches to normal mode - if (this.vimHandler && this.vimHandler.handleKey(data)) { - return; - } if (this.onEscape) { this.onEscape(); return; diff --git a/src/tui/components/filterable-select-list.ts b/src/tui/components/filterable-select-list.ts index 7a283487..49730607 100644 --- a/src/tui/components/filterable-select-list.ts +++ b/src/tui/components/filterable-select-list.ts @@ -1,12 +1,12 @@ -import type { Component } from "@mariozechner/pi-tui"; +import type { Component } from "@earendil-works/pi-tui"; import { Input, matchesKey, type SelectItem, SelectList, type SelectListTheme, - getEditorKeybindings, -} from "@mariozechner/pi-tui"; + getKeybindings, +} from "@earendil-works/pi-tui"; import chalk from "chalk"; import { fuzzyFilterLower, prepareSearchItems } from "./fuzzy-filter.js"; @@ -110,8 +110,8 @@ export class FilterableSelectList implements Component { } // Escape: clear filter or cancel - const kb = getEditorKeybindings(); - if (kb.matches(keyData, "selectCancel")) { + const kb = getKeybindings(); + if (kb.matches(keyData, "tui.select.cancel")) { if (this.filterText) { this.filterText = ""; this.input.setValue(""); diff --git a/src/tui/components/searchable-select-list.ts b/src/tui/components/searchable-select-list.ts index 8a27858e..5e55b157 100644 --- a/src/tui/components/searchable-select-list.ts +++ b/src/tui/components/searchable-select-list.ts @@ -1,13 +1,13 @@ import { type Component, - getEditorKeybindings, + getKeybindings, Input, isKeyRelease, matchesKey, type SelectItem, type SelectListTheme, truncateToWidth, -} from "@mariozechner/pi-tui"; +} from "@earendil-works/pi-tui"; import { stripAnsi, visibleWidth } from "../../terminal/ansi.js"; import { findWordBoundaryIndex, fuzzyFilterLower } from "./fuzzy-filter.js"; @@ -362,8 +362,8 @@ export class SearchableSelectList implements Component { return; } - const kb = getEditorKeybindings(); - if (kb.matches(keyData, "selectCancel")) { + const kb = getKeybindings(); + if (kb.matches(keyData, "tui.select.cancel")) { if (this.onCancel) { this.onCancel(); } diff --git a/src/tui/components/selectors.ts b/src/tui/components/selectors.ts index 46073fbc..aae9c4d1 100644 --- a/src/tui/components/selectors.ts +++ b/src/tui/components/selectors.ts @@ -1,4 +1,9 @@ -import { type SelectItem, SelectList, type SettingItem, SettingsList } from "@mariozechner/pi-tui"; +import { + type SelectItem, + SelectList, + type SettingItem, + SettingsList, +} from "@earendil-works/pi-tui"; import { filterableSelectListTheme, searchableSelectListTheme, diff --git a/src/tui/components/tool-execution.ts b/src/tui/components/tool-execution.ts index 4e98d2bc..200c6792 100644 --- a/src/tui/components/tool-execution.ts +++ b/src/tui/components/tool-execution.ts @@ -1,4 +1,4 @@ -import { Box, Container, Markdown, Spacer, Text } from "@mariozechner/pi-tui"; +import { Box, Container, Image, Markdown, Spacer, Text } from "@earendil-works/pi-tui"; import { formatToolDetail, resolveToolDisplay } from "../../agents/tool-display.js"; import { renderDiff, parseDiffStats, formatDiffStatsLine } from "../diff-renderer.js"; import { linkifyFilePaths } from "../linkify-paths.js"; @@ -11,6 +11,8 @@ type ToolResultContent = { mimeType?: string; bytes?: number; omitted?: boolean; + /** Base64-encoded image data, retained for inline rendering. */ + data?: string; }; type ToolResult = { @@ -61,10 +63,11 @@ export class ToolExecutionComponent extends Container { private header: Text; private argsLine: Text; private output: Markdown; + private imageContainer: Container; private toolName: string; private args: unknown; private result?: ToolResult; - private expanded = false; + private sectionState: import("../tui-types.js").SectionState = "collapsed"; private isError = false; private isPartial = true; @@ -78,11 +81,13 @@ export class ToolExecutionComponent extends Container { this.output = new Markdown("", 0, 0, markdownTheme, { color: (line) => theme.toolOutput(line), }); + this.imageContainer = new Container(); this.addChild(new Spacer(1)); this.addChild(this.box); this.box.addChild(this.header); this.box.addChild(this.argsLine); this.box.addChild(this.output); + this.box.addChild(this.imageContainer); this.refresh(); } @@ -92,10 +97,20 @@ export class ToolExecutionComponent extends Container { } setExpanded(expanded: boolean) { - this.expanded = expanded; + this.sectionState = expanded ? "expanded" : "collapsed"; this.refresh(); } + setSectionState(state: import("../tui-types.js").SectionState) { + this.sectionState = state; + this.refresh(); + } + + /** When hidden, the component renders only the header line. */ + get isHidden(): boolean { + return this.sectionState === "hidden"; + } + setResult(result: ToolResult | undefined, opts?: { isError?: boolean }) { this.result = result; this.isPartial = false; @@ -132,11 +147,48 @@ export class ToolExecutionComponent extends Container { const raw = extractText(this.result); const isDiff = DIFF_TOOLS.has(this.toolName) && raw && !this.isPartial; + // Hidden: only the header line, no output body. + if (this.sectionState === "hidden") { + this.output.setText(""); + this.imageContainer.clear(); + return; + } + + // Render inline images from image content blocks that carry base64 data. + // The Image component handles terminal capability detection internally + // (Kitty/iTerm2) and falls back to a text placeholder when unsupported. + this.imageContainer.clear(); + if (this.result?.content && !this.isPartial) { + for (const entry of this.result.content) { + if (entry.type === "image" && entry.data) { + const mime = entry.mimeType ?? "image/png"; + try { + const img = new Image(entry.data, mime, { fallbackColor: theme.dim }); + this.imageContainer.addChild(img); + } catch { + // If Image construction fails, the text placeholder from + // extractText already covers this block. + } + } + } + } + + const isExpanded = this.sectionState === "expanded"; + if (isDiff) { - const colored = renderDiff(raw); + const colored = renderDiff(raw, { + add: theme.success, + del: theme.error, + header: (t) => theme.bold(t), + hunk: theme.accent, + context: theme.dim, + }); const stats = parseDiffStats(raw); - const statsLine = formatDiffStatsLine(stats); - const maxLines = this.expanded ? Infinity : DIFF_PREVIEW_LINES; + const statsLine = formatDiffStatsLine(stats, { + add: theme.success, + del: theme.error, + }); + const maxLines = isExpanded ? Infinity : DIFF_PREVIEW_LINES; const display = colored.length > maxLines ? [...colored.slice(0, maxLines), "…", statsLine] @@ -148,7 +200,7 @@ export class ToolExecutionComponent extends Container { : this.isPartial ? "…" : ""; - if (!this.expanded && text) { + if (!isExpanded && text) { const lines = text.split("\n"); const preview = lines.length > PREVIEW_LINES ? `${lines.slice(0, PREVIEW_LINES).join("\n")}\n…` : text; diff --git a/src/tui/components/user-message.ts b/src/tui/components/user-message.ts index fad77f42..8b879a92 100644 --- a/src/tui/components/user-message.ts +++ b/src/tui/components/user-message.ts @@ -1,4 +1,4 @@ -import { Container, Markdown, Spacer } from "@mariozechner/pi-tui"; +import { Container, Markdown, Spacer } from "@earendil-works/pi-tui"; import { markdownTheme, theme } from "../theme/theme.js"; export class UserMessageComponent extends Container { diff --git a/src/tui/components/welcome-screen.ts b/src/tui/components/welcome-screen.ts index 84bf4511..88cc23ce 100644 --- a/src/tui/components/welcome-screen.ts +++ b/src/tui/components/welcome-screen.ts @@ -1,5 +1,5 @@ -import type { Component } from "@mariozechner/pi-tui"; -import { truncateToWidth } from "@mariozechner/pi-tui"; +import type { Component } from "@earendil-works/pi-tui"; +import { truncateToWidth } from "@earendil-works/pi-tui"; import chalk from "chalk"; import { visibleWidth } from "../../terminal/ansi.js"; import type { TuiStateAccess } from "../tui-types.js"; @@ -137,7 +137,7 @@ export class WelcomeScreen implements Component { ["/help", "all commands"], ["/agents", "switch agents"], ["Ctrl+V", "paste images"], - ["Esc", "abort run"], + ["Ctrl+C", "abort run / exit"], ]; const sessionHeader = bold(accent("Session")); diff --git a/src/tui/diff-renderer.ts b/src/tui/diff-renderer.ts index 08955eee..08ea1889 100644 --- a/src/tui/diff-renderer.ts +++ b/src/tui/diff-renderer.ts @@ -13,6 +13,29 @@ export type DiffStats = { deletions: number; }; +/** + * Color functions for diff rendering, each taking text and returning it styled. + * Matches the shape of the TUI theme color functions so the active palette can + * be passed straight through, keeping diff colors consistent with the theme + * (correct under light and high-contrast presets). + */ +export type DiffColors = { + add: (text: string) => string; + del: (text: string) => string; + header: (text: string) => string; + hunk: (text: string) => string; + context: (text: string) => string; +}; + +/** Default (terminal-default) colors used when no theme is supplied. */ +const DEFAULT_DIFF_COLORS: DiffColors = { + add: (t) => chalk.green(t), + del: (t) => chalk.red(t), + header: (t) => chalk.bold(chalk.white(t)), + hunk: (t) => chalk.cyan(t), + context: (t) => chalk.dim(t), +}; + export function parseDiffLines(raw: string): DiffLine[] { const lines: DiffLine[] = []; for (const text of raw.split("\n")) { @@ -36,20 +59,21 @@ export function parseDiffLines(raw: string): DiffLine[] { return lines; } -export function renderDiff(raw: string): string[] { +export function renderDiff(raw: string, colors?: Partial): string[] { + const c = { ...DEFAULT_DIFF_COLORS, ...colors }; const parsed = parseDiffLines(raw); return parsed.map((line) => { switch (line.type) { case "add": - return chalk.green(line.text); + return c.add(line.text); case "del": - return chalk.red(line.text); + return c.del(line.text); case "header": - return chalk.bold(chalk.white(line.text)); + return c.header(line.text); case "hunk": - return chalk.cyan(line.text); + return c.hunk(line.text); default: - return chalk.dim(line.text); + return c.context(line.text); } }); } @@ -80,10 +104,15 @@ export function renderDiffStats(raw: string): DiffStats { * Format a single-line summary of diff stats with colors. * Example: "+5 -3 (2 files)" */ -export function formatDiffStatsLine(stats: DiffStats): string { +export function formatDiffStatsLine( + stats: DiffStats, + colors?: { add?: (text: string) => string; del?: (text: string) => string }, +): string { + const add = colors?.add ?? ((t: string) => chalk.green(t)); + const del = colors?.del ?? ((t: string) => chalk.red(t)); const parts: string[] = []; - if (stats.additions > 0) parts.push(chalk.green(`+${stats.additions}`)); - if (stats.deletions > 0) parts.push(chalk.red(`-${stats.deletions}`)); + if (stats.additions > 0) parts.push(add(`+${stats.additions}`)); + if (stats.deletions > 0) parts.push(del(`-${stats.deletions}`)); const fileLabel = stats.files === 1 ? "1 file" : `${stats.files} files`; return parts.length > 0 ? `${parts.join(" ")} (${fileLabel})` : `(${fileLabel})`; } diff --git a/src/tui/enriched-autocomplete.test.ts b/src/tui/enriched-autocomplete.test.ts index 2ba76ac8..585ec789 100644 --- a/src/tui/enriched-autocomplete.test.ts +++ b/src/tui/enriched-autocomplete.test.ts @@ -1,7 +1,9 @@ import { describe, expect, it, vi } from "vitest"; -import type { AutocompleteItem, AutocompleteProvider } from "@mariozechner/pi-tui"; +import type { AutocompleteItem, AutocompleteProvider } from "@earendil-works/pi-tui"; import { EnrichedAutocompleteProvider, createEnrichedProvider } from "./enriched-autocomplete.js"; +const options = { signal: new AbortController().signal }; + function createMockBase( suggestions: { items: AutocompleteItem[]; prefix: string } | null = { items: [{ value: "file.ts", label: "file.ts" }], @@ -9,7 +11,7 @@ function createMockBase( }, ): AutocompleteProvider { return { - getSuggestions: vi.fn(() => suggestions), + getSuggestions: vi.fn(async () => suggestions), applyCompletion: vi.fn((_lines, cursorLine, cursorCol, _item, _prefix) => ({ lines: [], cursorLine, @@ -19,26 +21,26 @@ function createMockBase( } describe("EnrichedAutocompleteProvider", () => { - it("delegates to base when no queryFn provided", () => { + it("delegates to base when no queryFn provided", async () => { const base = createMockBase(); const provider = new EnrichedAutocompleteProvider(base); - const result = provider.getSuggestions(["@fi"], 0, 3); + const result = await provider.getSuggestions(["@fi"], 0, 3, options); expect(result).toEqual({ items: [{ value: "file.ts", label: "file.ts" }], prefix: "@" }); }); - it("delegates to base when base returns null", () => { + it("delegates to base when base returns null", async () => { const base = createMockBase(null); const queryFn = vi.fn(async () => []); const provider = new EnrichedAutocompleteProvider(base, queryFn); - const result = provider.getSuggestions(["@fi"], 0, 3); + const result = await provider.getSuggestions(["@fi"], 0, 3, options); expect(result).toBeNull(); }); - it("fires async query on @ prefix", () => { + it("fires async query on @ prefix", async () => { const base = createMockBase(); const queryFn = vi.fn(async () => [{ value: "sym:Foo", label: "Foo", description: "class" }]); const provider = new EnrichedAutocompleteProvider(base, queryFn); - provider.getSuggestions(["@fi"], 0, 3); + await provider.getSuggestions(["@fi"], 0, 3, options); expect(queryFn).toHaveBeenCalledWith("fi"); }); @@ -49,14 +51,14 @@ describe("EnrichedAutocompleteProvider", () => { const provider = new EnrichedAutocompleteProvider(base, queryFn); // First call triggers async fetch - provider.getSuggestions(["@fi"], 0, 3); + await provider.getSuggestions(["@fi"], 0, 3, options); // Wait for the query to resolve await vi.waitFor(() => expect(queryFn).toHaveBeenCalled()); // Small delay to allow cache to be set await new Promise((r) => setTimeout(r, 10)); // Second call should use cache - const result = provider.getSuggestions(["@fi"], 0, 3); + const result = await provider.getSuggestions(["@fi"], 0, 3, options); expect(result?.items).toHaveLength(2); expect(result?.items[1]?.value).toBe("sym:Foo"); }); @@ -72,21 +74,21 @@ describe("EnrichedAutocompleteProvider", () => { ]); const provider = new EnrichedAutocompleteProvider(base, queryFn); - provider.getSuggestions(["@fi"], 0, 3); + await provider.getSuggestions(["@fi"], 0, 3, options); await new Promise((r) => setTimeout(r, 10)); - const result = provider.getSuggestions(["@fi"], 0, 3); + const result = await provider.getSuggestions(["@fi"], 0, 3, options); expect(result?.items).toHaveLength(2); const values = result?.items.map((i) => i.value); expect(values).toContain("file.ts"); expect(values).toContain("other.ts"); }); - it("does not fire query when no @ prefix", () => { + it("does not fire query when no @ prefix", async () => { const base = createMockBase({ items: [], prefix: "/" }); const queryFn = vi.fn(async () => []); const provider = new EnrichedAutocompleteProvider(base, queryFn); - provider.getSuggestions(["hello"], 0, 5); + await provider.getSuggestions(["hello"], 0, 5, options); expect(queryFn).not.toHaveBeenCalled(); }); @@ -96,7 +98,7 @@ describe("EnrichedAutocompleteProvider", () => { throw new Error("network error"); }); const provider = new EnrichedAutocompleteProvider(base, queryFn); - const result = provider.getSuggestions(["@fi"], 0, 3); + const result = await provider.getSuggestions(["@fi"], 0, 3, options); expect(result).not.toBeNull(); // Wait for the rejected promise to settle await new Promise((r) => setTimeout(r, 10)); @@ -112,18 +114,18 @@ describe("EnrichedAutocompleteProvider", () => { const provider = new EnrichedAutocompleteProvider(base, queryFn, { maxCache: 2 }); // Fill cache with 2 entries - provider.getSuggestions(["@a"], 0, 2); + await provider.getSuggestions(["@a"], 0, 2, options); await new Promise((r) => setTimeout(r, 10)); - provider.getSuggestions(["@b"], 0, 2); + await provider.getSuggestions(["@b"], 0, 2, options); await new Promise((r) => setTimeout(r, 10)); // This should evict "a" - provider.getSuggestions(["@c"], 0, 2); + await provider.getSuggestions(["@c"], 0, 2, options); await new Promise((r) => setTimeout(r, 10)); // "a" should re-trigger query queryFn.mockClear(); - provider.getSuggestions(["@a"], 0, 2); + await provider.getSuggestions(["@a"], 0, 2, options); expect(queryFn).toHaveBeenCalledWith("a"); }); diff --git a/src/tui/enriched-autocomplete.ts b/src/tui/enriched-autocomplete.ts index 076f15a2..511ae21a 100644 --- a/src/tui/enriched-autocomplete.ts +++ b/src/tui/enriched-autocomplete.ts @@ -1,4 +1,8 @@ -import type { AutocompleteItem, AutocompleteProvider } from "@mariozechner/pi-tui"; +import type { + AutocompleteItem, + AutocompleteProvider, + AutocompleteSuggestions, +} from "@earendil-works/pi-tui"; type CachedEntry = { items: AutocompleteItem[]; @@ -27,12 +31,13 @@ export class EnrichedAutocompleteProvider implements AutocompleteProvider { this.ttlMs = opts?.ttlMs ?? 30_000; } - getSuggestions( + async getSuggestions( lines: string[], cursorLine: number, cursorCol: number, - ): { items: AutocompleteItem[]; prefix: string } | null { - const baseSuggestions = this.base.getSuggestions(lines, cursorLine, cursorCol); + options: { signal: AbortSignal; force?: boolean }, + ): Promise { + const baseSuggestions = await this.base.getSuggestions(lines, cursorLine, cursorCol, options); if (!this.queryFn || !baseSuggestions) { return baseSuggestions; } diff --git a/src/tui/file-mention.test.ts b/src/tui/file-mention.test.ts index 25db18f4..03375616 100644 --- a/src/tui/file-mention.test.ts +++ b/src/tui/file-mention.test.ts @@ -108,7 +108,11 @@ describe("file-mention", () => { expect(result.mediaAttachments).toHaveLength(0); }); - it("follows symlinks to regular files", async () => { + // Creating symlinks on Windows requires Developer Mode or admin rights + // (EPERM otherwise), so skip this case there. CI runs on Linux where + // unprivileged symlink creation works. + const itSymlink = process.platform === "win32" ? it.skip : it; + itSymlink("follows symlinks to regular files", async () => { const realPath = path.join(tmpDir, "real.txt"); const linkPath = path.join(tmpDir, "link.txt"); await fs.writeFile(realPath, "linked content"); diff --git a/src/tui/input-history-store.test.ts b/src/tui/input-history-store.test.ts deleted file mode 100644 index 91b6ae13..00000000 --- a/src/tui/input-history-store.test.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { describe, it, expect, beforeEach, afterEach } from "vitest"; -import { createInputHistoryStore } from "./input-history-store.js"; -import { mkdirSync, rmSync } from "node:fs"; -import { join } from "node:path"; -import { tmpdir } from "node:os"; - -describe("InputHistoryStore", () => { - const testDir = join(tmpdir(), "mayros-history-test-" + Date.now()); - let originalHome: string | undefined; - - beforeEach(() => { - originalHome = process.env.HOME; - // We test the module functions directly without HOME override since - // the store uses homedir() at call time - mkdirSync(testDir, { recursive: true }); - }); - - afterEach(() => { - process.env.HOME = originalHome; - try { - rmSync(testDir, { recursive: true }); - } catch {} - }); - - it("load returns empty array when file does not exist", () => { - const store = createInputHistoryStore(); - // Default file may or may not exist, but function should not throw - expect(() => store.load()).not.toThrow(); - }); - - it("load returns empty array for invalid JSON", () => { - const store = createInputHistoryStore(); - expect(() => store.load()).not.toThrow(); - }); - - it("createInputHistoryStore returns object with load/save/append", () => { - const store = createInputHistoryStore(); - expect(typeof store.load).toBe("function"); - expect(typeof store.save).toBe("function"); - expect(typeof store.append).toBe("function"); - }); - - it("append does not throw for empty strings", () => { - const store = createInputHistoryStore(); - expect(() => store.append("")).not.toThrow(); - expect(() => store.append(" ")).not.toThrow(); - }); -}); diff --git a/src/tui/input-history-store.ts b/src/tui/input-history-store.ts deleted file mode 100644 index 18cc6a4c..00000000 --- a/src/tui/input-history-store.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { readFileSync, writeFileSync, mkdirSync } from "node:fs"; -import { join, dirname } from "node:path"; -import { homedir } from "node:os"; - -const HISTORY_FILE = join(homedir(), ".mayros", "input-history.json"); -const MAX_ENTRIES = 500; - -export type InputHistoryStore = { - load(): string[]; - save(entries: string[]): void; - append(entry: string): void; -}; - -export function createInputHistoryStore(): InputHistoryStore { - function load(): string[] { - try { - const data = readFileSync(HISTORY_FILE, "utf-8"); - const parsed = JSON.parse(data); - if (Array.isArray(parsed)) - return parsed.filter((e): e is string => typeof e === "string").slice(-MAX_ENTRIES); - } catch { - /* first run or corrupted */ - } - return []; - } - - function save(entries: string[]): void { - const trimmed = entries.slice(-MAX_ENTRIES); - try { - mkdirSync(dirname(HISTORY_FILE), { recursive: true }); - writeFileSync(HISTORY_FILE, JSON.stringify(trimmed), "utf-8"); - } catch { - /* read-only fs, ignore */ - } - } - - function append(entry: string): void { - if (!entry.trim()) return; - const current = load(); - // Deduplicate: remove previous occurrence - const deduped = current.filter((e) => e !== entry); - deduped.push(entry); - save(deduped); - } - - return { load, save, append }; -} diff --git a/src/tui/keybinding-resolver.test.ts b/src/tui/keybinding-resolver.test.ts index 5fc874a1..f02a1189 100644 --- a/src/tui/keybinding-resolver.test.ts +++ b/src/tui/keybinding-resolver.test.ts @@ -6,16 +6,17 @@ const piTuiMocks = vi.hoisted(() => { config?: Record, ) { this._config = config; - }) as unknown as typeof import("@mariozechner/pi-tui").EditorKeybindingsManager & + }) as unknown as typeof import("@earendil-works/pi-tui").KeybindingsManager & ReturnType; return { matchesKey: vi.fn((_data: string, _key: string) => false), - setEditorKeybindings: vi.fn(), - EditorKeybindingsManager: MockManager, + setKeybindings: vi.fn(), + KeybindingsManager: MockManager, + TUI_KEYBINDINGS: {} as Record, }; }); -vi.mock("@mariozechner/pi-tui", () => ({ +vi.mock("@earendil-works/pi-tui", () => ({ ...piTuiMocks, Key: { ctrl: (k: string) => `ctrl+${k}`, shift: (k: string) => `shift+${k}` }, })); @@ -55,9 +56,9 @@ describe("TuiKeybindingResolver", () => { expect(piTuiMocks.matchesKey).toHaveBeenCalledWith("\x07", "ctrl+g"); }); - it("returns false when action has no keys", () => { + it("returns false when the data does not match any action", () => { const resolver = new TuiKeybindingResolver(); - expect(resolver.matches("\x00", "toggleVim")).toBe(false); + expect(resolver.matches("\x00", "toggleTools")).toBe(false); }); }); @@ -68,23 +69,26 @@ describe("DEFAULT_TUI_KEYBINDINGS", () => { expect(DEFAULT_TUI_KEYBINDINGS).toHaveProperty("selectSession"); expect(DEFAULT_TUI_KEYBINDINGS).toHaveProperty("toggleTools"); expect(DEFAULT_TUI_KEYBINDINGS).toHaveProperty("toggleThinking"); - expect(DEFAULT_TUI_KEYBINDINGS).toHaveProperty("toggleVim"); }); }); describe("applyKeybindingsFromConfig", () => { - it("creates and sets an EditorKeybindingsManager", () => { - piTuiMocks.setEditorKeybindings.mockClear(); - piTuiMocks.EditorKeybindingsManager.mockClear(); + it("creates and sets an KeybindingsManager", () => { + piTuiMocks.setKeybindings.mockClear(); + piTuiMocks.KeybindingsManager.mockClear(); applyKeybindingsFromConfig({ cursorUp: "ctrl+k" }); - expect(piTuiMocks.EditorKeybindingsManager).toHaveBeenCalledWith({ cursorUp: "ctrl+k" }); - expect(piTuiMocks.setEditorKeybindings).toHaveBeenCalled(); + expect(piTuiMocks.KeybindingsManager).toHaveBeenCalledWith(piTuiMocks.TUI_KEYBINDINGS, { + cursorUp: "ctrl+k", + }); + expect(piTuiMocks.setKeybindings).toHaveBeenCalled(); }); it("filters out TUI-specific actions from editor config", () => { - piTuiMocks.EditorKeybindingsManager.mockClear(); + piTuiMocks.KeybindingsManager.mockClear(); applyKeybindingsFromConfig({ selectAgent: "ctrl+a", cursorDown: "ctrl+j" }); - expect(piTuiMocks.EditorKeybindingsManager).toHaveBeenCalledWith({ cursorDown: "ctrl+j" }); + expect(piTuiMocks.KeybindingsManager).toHaveBeenCalledWith(piTuiMocks.TUI_KEYBINDINGS, { + cursorDown: "ctrl+j", + }); }); }); diff --git a/src/tui/keybinding-resolver.ts b/src/tui/keybinding-resolver.ts index 192984dc..bd8c2dd3 100644 --- a/src/tui/keybinding-resolver.ts +++ b/src/tui/keybinding-resolver.ts @@ -1,18 +1,18 @@ import { - type EditorKeybindingsConfig, - EditorKeybindingsManager, + type KeybindingsConfig, + KeybindingsManager, type KeyId, matchesKey, - setEditorKeybindings, -} from "@mariozechner/pi-tui"; + setKeybindings, + TUI_KEYBINDINGS, +} from "@earendil-works/pi-tui"; export type TuiAction = | "selectAgent" | "selectModel" | "selectSession" | "toggleTools" - | "toggleThinking" - | "toggleVim"; + | "toggleThinking"; export const DEFAULT_TUI_KEYBINDINGS: Record = { selectAgent: "ctrl+g", @@ -20,7 +20,6 @@ export const DEFAULT_TUI_KEYBINDINGS: Record = { selectSession: "ctrl+p", toggleTools: "ctrl+o", toggleThinking: "ctrl+t", - toggleVim: "ctrl+shift+v", }; export class TuiKeybindingResolver { @@ -58,8 +57,8 @@ export class TuiKeybindingResolver { export function applyKeybindingsFromConfig( config?: Record, -): EditorKeybindingsManager { - const editorConfig: EditorKeybindingsConfig = {}; +): KeybindingsManager { + const editorConfig: KeybindingsConfig = {}; if (config) { for (const [action, keys] of Object.entries(config)) { if (action in DEFAULT_TUI_KEYBINDINGS) { @@ -70,8 +69,8 @@ export function applyKeybindingsFromConfig( : (keys as KeyId); } } - const manager = new EditorKeybindingsManager(editorConfig); - setEditorKeybindings(manager); + const manager = new KeybindingsManager(TUI_KEYBINDINGS, editorConfig); + setKeybindings(manager); return manager; } diff --git a/src/tui/paste-grapheme.test.ts b/src/tui/paste-grapheme.test.ts new file mode 100644 index 00000000..767b8e2e --- /dev/null +++ b/src/tui/paste-grapheme.test.ts @@ -0,0 +1,82 @@ +import { describe, expect, it } from "vitest"; + +/** + * No-regression coverage for the paste-collapse and grapheme-aware caret + * behavior inherited from the terminal UI dependency. + * + * Both features are implemented inside the Editor class the TUI extends, so + * these tests pin the environment contract (Intl.Segmenter availability and + * the paste-marker shape) rather than the Editor internals. If either + * contract breaks, the TUI's caret or paste handling degrades silently. + */ + +describe("grapheme-aware caret contract", () => { + it("Intl.Segmenter is available with grapheme granularity", () => { + const segmenter = new Intl.Segmenter("en", { granularity: "grapheme" }); + expect(segmenter).toBeDefined(); + const segments = [...segmenter.segment("ab")]; + expect(segments.length).toBe(2); + }); + + it("treats emoji with skin-tone modifiers as a single grapheme", () => { + // πŸ‘©πŸ» is woman (U+1F469) + light skin tone (U+1F3FB) β€” two code points, + // one user-perceived character. The caret must jump over it atomically. + const segmenter = new Intl.Segmenter("en", { granularity: "grapheme" }); + const segments = [...segmenter.segment("πŸ‘©πŸ»")]; + expect(segments.length).toBe(1); + }); + + it("treats a flag emoji (regional indicator pair) as a single grapheme", () => { + // πŸ‡²πŸ‡½ is U+1F1F2 + U+1F1FD β€” two regional indicator symbols, one flag. + const segmenter = new Intl.Segmenter("en", { granularity: "grapheme" }); + const segments = [...segmenter.segment("πŸ‡²πŸ‡½")]; + expect(segments.length).toBe(1); + }); + + it("treats CJK characters as one grapheme each", () => { + const segmenter = new Intl.Segmenter("en", { granularity: "grapheme" }); + const segments = [...segmenter.segment("δ½ ε₯½")]; + expect(segments.length).toBe(2); + }); +}); + +/** + * The paste-marker shape used by the Editor to collapse large pastes into a + * single atomic token. The regex must match the formats the Editor emits so + * that cursor movement and submission expansion keep working. If the format + * ever changes upstream, this test surfaces the break. + */ +const PASTE_MARKER_REGEX = /\[paste #(\d+)( (\+\d+ lines|\d+ chars))?\]/; + +describe("paste-collapse marker contract", () => { + it("matches a multi-line paste marker", () => { + expect(PASTE_MARKER_REGEX.test("[paste #1 +42 lines]")).toBe(true); + }); + + it("matches a character-count paste marker", () => { + expect(PASTE_MARKER_REGEX.test("[paste #2 1500 chars]")).toBe(true); + }); + + it("matches a bare paste marker (no count)", () => { + expect(PASTE_MARKER_REGEX.test("[paste #3]")).toBe(true); + }); + + it("does not match arbitrary bracketed text", () => { + expect(PASTE_MARKER_REGEX.test("[not a paste]")).toBe(false); + expect(PASTE_MARKER_REGEX.test("[paste foo]")).toBe(false); + }); + + it("a paste marker is a single atomic grapheme for cursor movement", () => { + // When the Editor segments text containing a paste marker, the marker + // must be treated as one unit so arrow keys jump over it in one press. + // We approximate this by checking the marker has no internal whitespace + // graphemes that a naive split would create. + const marker = "[paste #1 +42 lines]"; + const segmenter = new Intl.Segmenter("en", { granularity: "grapheme" }); + const segments = [...segmenter.segment(marker)]; + // The marker contains spaces, so it has multiple graphemes β€” the Editor + // applies its own marker-aware segmentation on top. This test documents + // that the marker is a compact, single-line token (no embedded newlines). + expect(segments.some((s) => s.segment.includes("\n"))).toBe(false); + }); +}); diff --git a/src/tui/section-visibility.test.ts b/src/tui/section-visibility.test.ts new file mode 100644 index 00000000..d0f273ff --- /dev/null +++ b/src/tui/section-visibility.test.ts @@ -0,0 +1,28 @@ +import { describe, expect, it } from "vitest"; +import { SECTION_CYCLE, nextSectionState } from "./tui-types.js"; + +describe("nextSectionState", () => { + it("cycles hidden β†’ collapsed", () => { + expect(nextSectionState("hidden")).toBe("collapsed"); + }); + + it("cycles collapsed β†’ expanded", () => { + expect(nextSectionState("collapsed")).toBe("expanded"); + }); + + it("cycles expanded β†’ hidden (wraps around)", () => { + expect(nextSectionState("expanded")).toBe("hidden"); + }); + + it("the cycle covers all three states in order", () => { + expect(SECTION_CYCLE).toEqual(["hidden", "collapsed", "expanded"]); + }); + + it("produces a full cycle back to the start after 3 steps", () => { + let state: "hidden" | "collapsed" | "expanded" = "hidden"; + state = nextSectionState(state); + state = nextSectionState(state); + state = nextSectionState(state); + expect(state).toBe("hidden"); + }); +}); diff --git a/src/tui/session-manager.test.ts b/src/tui/session-manager.test.ts index a792a19a..51c8cc6f 100644 --- a/src/tui/session-manager.test.ts +++ b/src/tui/session-manager.test.ts @@ -62,7 +62,7 @@ describe("SessionManager", () => { const client = createMockClient(); const mgr = new SessionManager({ client: client as never, currentAgentId: "default" }); await mgr.renameSession("s-abc", "New Name"); - expect(client.patchSession).toHaveBeenCalledWith({ key: "s-abc", displayName: "New Name" }); + expect(client.patchSession).toHaveBeenCalledWith({ key: "s-abc", label: "New Name" }); }); it("deletes a session via reset", async () => { diff --git a/src/tui/shared-commands.test.ts b/src/tui/shared-commands.test.ts new file mode 100644 index 00000000..762e9328 --- /dev/null +++ b/src/tui/shared-commands.test.ts @@ -0,0 +1,159 @@ +import { describe, expect, it, vi } from "vitest"; +import { handleSharedCommand } from "./shared-commands.js"; +import type { GatewayChatClient } from "./gateway-chat.js"; + +/** + * Unit tests for the shared slash-command core. + * + * The shared handler lets the accessible TUI reach parity with the graphical + * TUI for commands that do not require a visual picker. These tests pin the + * contract: each recognized command calls the right gateway method and + * reports through the injected sink, while unrecognized commands fall through. + */ + +function makeClientStub(overrides: Partial = {}): GatewayChatClient { + const base = { + abortChat: vi.fn().mockResolvedValue({ ok: true, aborted: true }), + resetSession: vi.fn().mockResolvedValue({ ok: true }), + compactSession: vi.fn().mockResolvedValue({ ok: true, compacted: true }), + getStatus: vi.fn().mockResolvedValue({ version: "1.2.3", sessions: 2 }), + listSessions: vi.fn().mockResolvedValue({ + sessions: [{ key: "main", title: "Main" }], + }), + listModels: vi + .fn() + .mockResolvedValue([{ id: "anthropic/claude", name: "Claude", provider: "anthropic" }]), + }; + return { ...base, ...overrides } as unknown as GatewayChatClient; +} + +function makeSink() { + const messages: { kind: "info" | "error"; text: string }[] = []; + return { + sink: { + info: (text: string) => messages.push({ kind: "info", text }), + error: (text: string) => messages.push({ kind: "error", text }), + }, + messages, + }; +} + +describe("handleSharedCommand", () => { + it("aborts the active run when one exists", async () => { + const client = makeClientStub(); + const { sink, messages } = makeSink(); + const result = await handleSharedCommand( + { client, sessionKey: "main", getActiveRunId: () => "run-1" }, + "abort", + "", + sink, + ); + expect(result.handled).toBe(true); + expect(client.abortChat).toHaveBeenCalledWith({ sessionKey: "main", runId: "run-1" }); + expect(messages).toContainEqual({ kind: "info", text: "aborted" }); + }); + + it("reports no active run when getActiveRunId returns null", async () => { + const client = makeClientStub(); + const { sink, messages } = makeSink(); + const result = await handleSharedCommand( + { client, sessionKey: "main", getActiveRunId: () => null }, + "abort", + "", + sink, + ); + expect(result.handled).toBe(true); + expect(client.abortChat).not.toHaveBeenCalled(); + expect(messages[0]?.text).toBe("no active run"); + }); + + it("resets the session on /new", async () => { + const client = makeClientStub(); + const onSessionChanged = vi.fn(); + const { sink, messages } = makeSink(); + const result = await handleSharedCommand( + { client, sessionKey: "main", onSessionChanged }, + "new", + "", + sink, + ); + expect(result.handled).toBe(true); + expect(client.resetSession).toHaveBeenCalledWith("main", "new"); + expect(messages[0]?.text).toBe("session reset"); + expect(onSessionChanged).toHaveBeenCalledWith("main"); + }); + + it("compacts the session context", async () => { + const client = makeClientStub(); + const { sink, messages } = makeSink(); + const result = await handleSharedCommand({ client, sessionKey: "main" }, "compact", "", sink); + expect(result.handled).toBe(true); + expect(client.compactSession).toHaveBeenCalledWith({ key: "main" }); + expect(messages[0]?.text).toBe("context compacted"); + }); + + it("switches session when given an argument", async () => { + const client = makeClientStub(); + const onSessionChanged = vi.fn(); + const { sink, messages } = makeSink(); + const result = await handleSharedCommand( + { client, sessionKey: "main", onSessionChanged }, + "session", + "work", + sink, + ); + expect(result.handled).toBe(true); + expect(onSessionChanged).toHaveBeenCalledWith("work"); + expect(messages[0]?.text).toContain("work"); + }); + + it("lists sessions when called without an argument", async () => { + const client = makeClientStub(); + const { sink, messages } = makeSink(); + const result = await handleSharedCommand({ client, sessionKey: "main" }, "sessions", "", sink); + expect(result.handled).toBe(true); + expect(client.listSessions).toHaveBeenCalled(); + expect(messages[0]?.text).toContain("main"); + }); + + it("lists available models", async () => { + const client = makeClientStub(); + const { sink, messages } = makeSink(); + const result = await handleSharedCommand({ client, sessionKey: "main" }, "model", "", sink); + expect(result.handled).toBe(true); + expect(client.listModels).toHaveBeenCalled(); + expect(messages[0]?.text).toContain("anthropic/claude"); + }); + + it("returns help text on /help", async () => { + const client = makeClientStub(); + const { sink, messages } = makeSink(); + const result = await handleSharedCommand({ client, sessionKey: "main" }, "help", "", sink); + expect(result.handled).toBe(true); + expect(messages[0]?.text).toContain("/abort"); + expect(messages[0]?.text).toContain("/compact"); + }); + + it("reports errors through the sink when a gateway call fails", async () => { + const client = makeClientStub({ + getStatus: vi.fn().mockRejectedValue(new Error("gateway down")), + }); + const { sink, messages } = makeSink(); + const result = await handleSharedCommand({ client, sessionKey: "main" }, "status", "", sink); + expect(result.handled).toBe(true); + expect(messages[0]?.kind).toBe("error"); + expect(messages[0]?.text).toContain("status failed"); + }); + + it("falls through (handled: false) for unknown commands", async () => { + const client = makeClientStub(); + const { sink } = makeSink(); + const result = await handleSharedCommand( + { client, sessionKey: "main" }, + "totally-unknown-command", + "", + sink, + ); + expect(result.handled).toBe(false); + }); +}); diff --git a/src/tui/shared-commands.ts b/src/tui/shared-commands.ts new file mode 100644 index 00000000..fcea0a27 --- /dev/null +++ b/src/tui/shared-commands.ts @@ -0,0 +1,188 @@ +/** + * Renderer-agnostic slash-command handling shared between the graphical TUI + * and the accessible (text-mode) TUI. + * + * The graphical TUI dispatches most commands through its own full handler + * (tui-command-handlers.ts) because it has overlays, pickers, and rich + * rendering. The accessible TUI has none of that, so it routes a focused set + * of commands through this shared core, which calls the gateway client + * directly and reports results as plain text via the injected render sink. + * + * This closes the feature-parity gap without forcing both TUIs to share a + * renderer: the logic is shared, the presentation is not. + */ + +import type { GatewayChatClient } from "./gateway-chat.js"; + +/** Sink for status/system messages produced by shared commands. */ +export type SharedCommandSink = { + /** Surface a normal informational message. */ + info: (text: string) => void; + /** Surface an error message. */ + error: (text: string) => void; +}; + +export type SharedCommandContext = { + client: GatewayChatClient; + sessionKey: string; + /** Active run id, if any, for abort. */ + getActiveRunId?: () => string | null; + /** Called after a session reset/switch so the caller can update its state. */ + onSessionChanged?: (sessionKey: string) => void; +}; + +export type HandleSharedCommandResult = { + /** true if the command was recognized and handled here. */ + handled: boolean; +}; + +/** + * Handle a focused set of slash commands against the gateway client. + * Returns { handled: true } when the command was recognized; the caller + * should not fall through to sending it as a chat message in that case. + */ +export async function handleSharedCommand( + ctx: SharedCommandContext, + name: string, + args: string, + sink: SharedCommandSink, +): Promise { + switch (name) { + case "abort": { + const runId = ctx.getActiveRunId?.() ?? null; + if (!runId) { + sink.info("no active run"); + return { handled: true }; + } + try { + await ctx.client.abortChat({ sessionKey: ctx.sessionKey, runId }); + sink.info("aborted"); + } catch (err) { + sink.error(`abort failed: ${String(err)}`); + } + return { handled: true }; + } + + case "new": + case "reset": { + try { + await ctx.client.resetSession(ctx.sessionKey, name === "new" ? "new" : "reset"); + sink.info("session reset"); + ctx.onSessionChanged?.(ctx.sessionKey); + } catch (err) { + sink.error(`reset failed: ${String(err)}`); + } + return { handled: true }; + } + + case "compact": { + try { + const result = await ctx.client.compactSession({ key: ctx.sessionKey }); + if (result.compacted) { + sink.info("context compacted"); + } else { + sink.info(`compaction skipped: ${result.reason ?? "no change"}`); + } + } catch (err) { + sink.error(`compact failed: ${String(err)}`); + } + return { handled: true }; + } + + case "status": { + try { + const status = await ctx.client.getStatus(); + const summary = formatStatusSummary(status); + sink.info(summary); + } catch (err) { + sink.error(`status failed: ${String(err)}`); + } + return { handled: true }; + } + + case "sessions": + case "session": { + if (name === "sessions" || !args) { + try { + const result = await ctx.client.listSessions({ limit: 20 }); + const lines = formatSessionList(result); + sink.info(lines || "no sessions"); + } catch (err) { + sink.error(`session list failed: ${String(err)}`); + } + return { handled: true }; + } + // `/session ` β€” switch by setting currentSessionKey on the caller. + ctx.onSessionChanged?.(args.trim()); + sink.info(`switched to ${args.trim()}`); + return { handled: true }; + } + + case "models": + case "model": { + try { + const models = await ctx.client.listModels(); + if (models.length === 0) { + sink.info("no models configured"); + } else { + const lines = models.map((m) => ` ${m.id}${m.name ? ` β€” ${m.name}` : ""}`).join("\n"); + sink.info(`Available models:\n${lines}`); + } + } catch (err) { + sink.error(`model list failed: ${String(err)}`); + } + return { handled: true }; + } + + case "help": { + sink.info(SHARED_HELP_TEXT); + return { handled: true }; + } + + default: + return { handled: false }; + } +} + +function formatStatusSummary(status: unknown): string { + if (!status || typeof status !== "object") return String(status); + const s = status as Record; + const parts: string[] = []; + if (typeof s.version === "string") parts.push(`gateway ${s.version}`); + if (typeof s.uptime === "number") parts.push(`uptime ${Math.round(s.uptime)}s`); + if (s.model && typeof s.model === "object") { + const m = s.model as Record; + if (typeof m.id === "string") parts.push(`model ${m.id}`); + } + if (typeof s.sessions === "number") parts.push(`${s.sessions} sessions`); + return parts.length > 0 ? parts.join(" Β· ") : "status unavailable"; +} + +function formatSessionList(result: unknown): string { + if (!result || typeof result !== "object") return ""; + const r = result as Record; + const sessions = Array.isArray(r.sessions) ? r.sessions : []; + if (sessions.length === 0) return ""; + return sessions + .map((entry, i) => { + const e = entry as Record; + const key = typeof e.key === "string" ? e.key : `session-${i}`; + const title = typeof e.title === "string" ? e.title : ""; + return ` ${key}${title ? ` β€” ${title}` : ""}`; + }) + .join("\n"); +} + +const SHARED_HELP_TEXT = `Available commands in accessible mode: + /abort Abort the active run + /new Reset the session (new conversation) + /reset Reset the session + /compact Compact session context + /status Show gateway status + /sessions List recent sessions + /session Switch to a session by key + /model List available models + /help Show this help + /quit, /exit Exit + +Type a message and press Enter to send.`; diff --git a/src/tui/stream-boundary.test.ts b/src/tui/stream-boundary.test.ts new file mode 100644 index 00000000..f948eeec --- /dev/null +++ b/src/tui/stream-boundary.test.ts @@ -0,0 +1,69 @@ +import { describe, expect, it } from "vitest"; +import { findStableBoundary } from "./stream-boundary.js"; + +describe("findStableBoundary", () => { + it("returns 0 for empty text", () => { + expect(findStableBoundary("")).toBe(0); + }); + + it("returns 0 for a single paragraph with no blank line", () => { + expect(findStableBoundary("hello world")).toBe(0); + }); + + it("splits at the last blank line between paragraphs", () => { + const text = "first paragraph\n\nsecond paragraph"; + const boundary = findStableBoundary(text); + expect(text.slice(0, boundary)).toBe("first paragraph\n\n"); + expect(text.slice(boundary)).toBe("second paragraph"); + }); + + it("splits at the last of multiple blank lines", () => { + const text = "para one\n\npara two\n\npara three"; + const boundary = findStableBoundary(text); + expect(text.slice(boundary)).toBe("para three"); + }); + + it("keeps an open code fence in the suffix (does not split mid-fence)", () => { + const text = "intro\n\n```ts\nconst x = 1;\n// still in fence"; + const boundary = findStableBoundary(text); + // The blank line before the fence opens is a stable boundary (fence count + // is 0 there), so "intro" becomes the stable prefix and the open fence + // stays in the suffix β€” which is correct, the prefix is a complete block. + expect(text.slice(0, boundary)).toBe("intro\n\n"); + expect(text.slice(boundary)).toBe("```ts\nconst x = 1;\n// still in fence"); + }); + + it("splits after a closed code fence", () => { + const text = "intro\n\n```ts\nconst x = 1;\n```\n\nnext paragraph"; + const boundary = findStableBoundary(text); + expect(text.slice(boundary)).toBe("next paragraph"); + expect(text.slice(0, boundary)).toContain("```"); + expect(text.slice(0, boundary)).toContain("```ts"); + }); + + it("handles multiple closed fences", () => { + const text = "a\n\n```ts\ncode1\n```\n\nb\n\n```py\ncode2\n```\n\nc"; + const boundary = findStableBoundary(text); + expect(text.slice(boundary)).toBe("c"); + }); + + it("treats tilde fences the same as backtick fences", () => { + const text = "intro\n\n~~~ts\nconst x = 1;\n~~~\n\nafter"; + const boundary = findStableBoundary(text); + expect(text.slice(boundary)).toBe("after"); + }); + + it("returns 0 when the only blank line is inside an open fence", () => { + const text = "```ts\n\nconst x = 1;\n// open"; + const boundary = findStableBoundary(text); + expect(boundary).toBe(0); + }); + + it("handles trailing newlines", () => { + const text = "paragraph\n\n"; + const boundary = findStableBoundary(text); + // The boundary is after the blank line; suffix is empty. + expect(text.slice(0, boundary)).toBe("paragraph\n\n"); + expect(text.slice(boundary)).toBe(""); + }); +}); diff --git a/src/tui/stream-boundary.ts b/src/tui/stream-boundary.ts new file mode 100644 index 00000000..e8fe78c6 --- /dev/null +++ b/src/tui/stream-boundary.ts @@ -0,0 +1,64 @@ +/** + * Stable-boundary detection for incremental markdown streaming. + * + * When streaming a model response, re-parsing the full markdown on every delta + * is O(nΒ²). Splitting the text at the last stable block boundary lets the + * prefix be cached (it never changes once committed) and only the suffix is + * re-parsed per delta, turning the cost into O(n). + * + * A "stable boundary" is a blank line (`\n\n`) that sits outside any open code + * fence, because that is where a markdown block (paragraph, heading, code + * block, list) is guaranteed to be complete. Splitting inside an open fence or + * mid-paragraph would orphan partial tokens and change the rendering. + */ + +const FENCE_LINE = /^\s*(`{3,}|~{3,})/; + +/** + * Count the number of code-fence lines in the text up to (but not including) + * the given end index. An odd count means a fence is currently open. + */ +function fenceCountUpTo(text: string, endIndex: number): number { + let count = 0; + let pos = 0; + while (pos < endIndex) { + const eol = text.indexOf("\n", pos); + const lineEnd = eol === -1 || eol >= endIndex ? endIndex : eol; + const line = text.slice(pos, lineEnd); + if (FENCE_LINE.test(line)) count++; + if (eol === -1 || eol >= endIndex) break; + pos = eol + 1; + } + return count; +} + +/** + * Find the index of the last stable block boundary in the text β€” the position + * just after a `\n\n` that is outside any open code fence. Returns 0 when no + * stable boundary exists (the entire text is a single in-flight block). + * + * The return value is the start index of the suffix (the unstable part); + * `text.slice(0, result)` is the stable prefix, `text.slice(result)` is the + * suffix. + */ +export function findStableBoundary(text: string): number { + if (!text) return 0; + + // Walk backwards through `\n\n` occurrences, returning the first one that + // sits in an even-fence-count region (i.e. outside any open fence). + let searchFrom = text.length; + while (searchFrom > 0) { + const boundary = text.lastIndexOf("\n\n", searchFrom - 1); + if (boundary === -1) return 0; + + // The boundary is stable only if fences are balanced before it. + const fencesBefore = fenceCountUpTo(text, boundary); + if (fencesBefore % 2 === 0) { + return boundary + 2; // start of the block after the blank line + } + + // Inside an open fence β€” keep walking back. + searchFrom = boundary; + } + return 0; +} diff --git a/src/tui/term-capabilities.test.ts b/src/tui/term-capabilities.test.ts new file mode 100644 index 00000000..8cff30ca --- /dev/null +++ b/src/tui/term-capabilities.test.ts @@ -0,0 +1,149 @@ +import { describe, expect, it, vi } from "vitest"; +import { + detectKeyboardProtocol, + detectTerminalColorScheme, + enableKeyboardProtocol, + relativeLuminance, + type TerminalColorQuerier, +} from "./term-capabilities.js"; + +describe("detectKeyboardProtocol", () => { + it("returns kitty for kitty/WezTerm/ghostty terminals", () => { + expect(detectKeyboardProtocol({ TERM_PROGRAM: "kitty" } as NodeJS.ProcessEnv)).toBe("kitty"); + expect(detectKeyboardProtocol({ TERM_PROGRAM: "WezTerm" } as NodeJS.ProcessEnv)).toBe("kitty"); + expect(detectKeyboardProtocol({ TERM_PROGRAM: "ghostty" } as NodeJS.ProcessEnv)).toBe("kitty"); + }); + + it("returns modify-other-keys for iTerm/vscode/tmux/windows-terminal", () => { + expect(detectKeyboardProtocol({ TERM_PROGRAM: "iTerm.app" } as NodeJS.ProcessEnv)).toBe( + "modify-other-keys", + ); + expect(detectKeyboardProtocol({ TERM_PROGRAM: "vscode" } as NodeJS.ProcessEnv)).toBe( + "modify-other-keys", + ); + expect(detectKeyboardProtocol({ TERM_PROGRAM: "tmux" } as NodeJS.ProcessEnv)).toBe( + "modify-other-keys", + ); + expect(detectKeyboardProtocol({ TERM_PROGRAM: "windows-terminal" } as NodeJS.ProcessEnv)).toBe( + "modify-other-keys", + ); + }); + + it("prefers LC_TERMINAL over TERM_PROGRAM (survives tmux on macOS)", () => { + expect( + detectKeyboardProtocol({ + LC_TERMINAL: "iTerm.app", + TERM_PROGRAM: "tmux", + } as NodeJS.ProcessEnv), + ).toBe("modify-other-keys"); + }); + + it("returns none for terminals not in the allow-list", () => { + expect(detectKeyboardProtocol({ TERM_PROGRAM: "xterm-256color" } as NodeJS.ProcessEnv)).toBe( + "none", + ); + expect(detectKeyboardProtocol({} as NodeJS.ProcessEnv)).toBe("none"); + }); +}); + +describe("enableKeyboardProtocol", () => { + it("returns a none-handle and writes nothing when protocol is none", () => { + const written: string[] = []; + const stream = { write: (s: string) => written.push(s) } as unknown as NodeJS.WritableStream; + const handle = enableKeyboardProtocol(stream); + expect(handle.protocol).toBe("none"); + expect(written).toHaveLength(0); + handle.disable(); // no-op, does not throw + }); + + it("writes the kitty push sequence for kitty terminals", () => { + const written: string[] = []; + const stream = { write: (s: string) => written.push(s) } as unknown as NodeJS.WritableStream; + const original = process.env.TERM_PROGRAM; + process.env.TERM_PROGRAM = "kitty"; + try { + const handle = enableKeyboardProtocol(stream); + expect(handle.protocol).toBe("kitty"); + expect(written.join("")).toContain("\x1b[>1u"); + handle.disable(); + expect(written.join("")).toContain("\x1b[ { + const written: string[] = []; + const stream = { write: (s: string) => written.push(s) } as unknown as NodeJS.WritableStream; + const original = process.env.TERM_PROGRAM; + process.env.TERM_PROGRAM = "kitty"; + try { + const handle = enableKeyboardProtocol(stream); + handle.disable(); + handle.disable(); + const popCount = written.filter((s) => s.includes("\x1b[ { + it("returns ~0 for black", () => { + expect(relativeLuminance({ r: 0, g: 0, b: 0 })).toBeCloseTo(0, 1); + }); + + it("returns ~1 for white", () => { + expect(relativeLuminance({ r: 255, g: 255, b: 255 })).toBeCloseTo(1, 1); + }); + + it("weights green more than red and blue (Rec. 709)", () => { + const lum = relativeLuminance({ r: 0, g: 255, b: 0 }); + expect(lum).toBeGreaterThan(0.7); + }); + + it("classifies a dark navy background as dark (< 0.5)", () => { + expect(relativeLuminance({ r: 30, g: 30, b: 40 })).toBeLessThan(0.5); + }); +}); + +describe("detectTerminalColorScheme", () => { + function makeQuerier( + scheme: "dark" | "light" | undefined, + bg: { r: number; g: number; b: number } | undefined, + ): TerminalColorQuerier { + return { + queryTerminalColorScheme: vi.fn().mockResolvedValue(scheme), + queryTerminalBackgroundColor: vi.fn().mockResolvedValue(bg), + }; + } + + it("returns the DSR scheme when available (dark)", async () => { + const tui = makeQuerier("dark", undefined); + expect(await detectTerminalColorScheme(tui)).toBe("dark"); + }); + + it("returns the DSR scheme when available (light)", async () => { + const tui = makeQuerier("light", undefined); + expect(await detectTerminalColorScheme(tui)).toBe("light"); + }); + + it("falls back to OSC 11 luminance when DSR is unsupported", async () => { + // DSR returns undefined; bright background β†’ light + const tui = makeQuerier(undefined, { r: 250, g: 250, b: 250 }); + expect(await detectTerminalColorScheme(tui)).toBe("light"); + }); + + it("classifies a dark background via OSC 11", async () => { + const tui = makeQuerier(undefined, { r: 20, g: 20, b: 25 }); + expect(await detectTerminalColorScheme(tui)).toBe("dark"); + }); + + it("falls back to dark when neither query responds", async () => { + const tui = makeQuerier(undefined, undefined); + expect(await detectTerminalColorScheme(tui)).toBe("dark"); + }); +}); diff --git a/src/tui/term-capabilities.ts b/src/tui/term-capabilities.ts new file mode 100644 index 00000000..6234acf4 --- /dev/null +++ b/src/tui/term-capabilities.ts @@ -0,0 +1,168 @@ +/** + * Terminal extended-keyboard capability detection and lifecycle. + * + * Modern terminals support extended keyboard protocols that preserve modifier + * combinations (Shift+Enter, Ctrl+Shift+key, Cmd-modifiers) which a plain + * raw-mode stdin collapses. This module enables the best supported protocol at + * startup and disables it on exit, with terminal-cleanup hygiene: leaving the + * protocol armed after exit corrupts subsequent shell input. + * + * Supported protocols, in preference order: + * 1. Kitty keyboard protocol (CSI > 1 u push / CSI < u pop) + * 2. xterm modifyOtherKeys level 2 (CSI > 4 ; 2 u / CSI < 4 u) + * + * Detection is conservative: only terminals in the known-good allow-list are + * upgraded, so we never emit escape sequences a terminal cannot parse back. + */ + +import process from "node:process"; + +/** Minimal surface of the TUI instance needed for terminal color queries. */ +export type TerminalColorQuerier = { + queryTerminalColorScheme(opts: { timeoutMs: number }): Promise<"dark" | "light" | undefined>; + queryTerminalBackgroundColor(opts: { + timeoutMs: number; + }): Promise<{ r: number; g: number; b: number } | undefined>; +}; + +/** Terminal identifiers that reliably support an extended keyboard protocol. */ +const EXTENDED_KEY_TERMINALS = new Set([ + "iTerm.app", + "kitty", + "WezTerm", + "ghostty", + "tmux", + "vscode", + "windows-terminal", + "Apple_Terminal", +]); + +type Protocol = "kitty" | "modify-other-keys" | "none"; + +function detectTerminal(): string { + // LC_TERMINAL survives tmux on macOS where TERM_PROGRAM is overwritten. + return ( + process.env.LC_TERMINAL || process.env.TERM_PROGRAM || process.env.TERM_PROGRAM_VERSION || "" + ); +} + +/** Decide which protocol to enable, if any, for the current terminal. */ +export function detectKeyboardProtocol(env: NodeJS.ProcessEnv = process.env): Protocol { + const id = env.LC_TERMINAL || env.TERM_PROGRAM || ""; + if (!id || !EXTENDED_KEY_TERMINALS.has(id)) { + return "none"; + } + // Kitty, WezTerm, Ghostty implement the kitty protocol natively. + // iTerm, vscode, Apple Terminal, tmux, windows-terminal support modifyOtherKeys. + if (id === "kitty" || id === "WezTerm" || id === "ghostty") { + return "kitty"; + } + return "modify-other-keys"; +} + +/** Escape: CSI */ +const CSI = "\x1b["; + +function pushSequence(protocol: Protocol): string { + switch (protocol) { + case "kitty": + // Kitty keyboard protocol: push flags 1 (disambiguate escape keys). + return `${CSI}>1u`; + case "modify-other-keys": + // xterm modifyOtherKeys level 2. + return `${CSI}>4;2u`; + default: + return ""; + } +} + +function popSequence(protocol: Protocol): string { + switch (protocol) { + case "kitty": + return `${CSI} void; +}; + +/** + * Enable the best supported extended keyboard protocol on the given stream + * (defaults to stdout). Returns a handle whose `disable()` pops the protocol; + * call it on exit, on every signal, and on uncaught errors. + */ +export function enableKeyboardProtocol( + stream: NodeJS.WritableStream = process.stdout, +): KeyboardProtocolHandle { + const protocol = detectKeyboardProtocol(); + const push = pushSequence(protocol); + if (push) { + try { + stream.write(push); + } catch { + // Writing to a closed stream must never break startup. + } + } + let disabled = false; + return { + protocol, + disable: () => { + if (disabled || protocol === "none") return; + disabled = true; + const pop = popSequence(protocol); + if (pop) { + try { + stream.write(pop); + } catch { + // Best-effort cleanup. + } + } + }, + }; +} + +// --------------------------------------------------------------------------- +// Terminal color-scheme detection (dark/light auto-theme) +// --------------------------------------------------------------------------- + +/** + * Compute the relative luminance of an RGB color using the Rec. 709 weighting. + * Values >= 0.5 are considered "light" backgrounds. + */ +export function relativeLuminance(rgb: { r: number; g: number; b: number }): number { + return (0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b) / 255; +} + +/** + * Detect whether the terminal has a dark or light background, using the best + * available signal. Returns "dark" when no signal is available (the prior + * default). + * + * Query ladder, fastest signal wins: + * 1. DSR color-scheme (`CSI ? 996 n`) β€” terminals reply with a direct + * dark/light answer. Cleanest signal when supported. + * 2. OSC 11 background color β€” parse the RGB reply and compute luminance. + * 3. Fallback to "dark". + * + * Both queries are run concurrently and each has a 150 ms timeout, so an + * unresponsive terminal adds at most ~150 ms to startup before falling back. + */ +export async function detectTerminalColorScheme( + tui: TerminalColorQuerier, +): Promise<"dark" | "light"> { + const [scheme, bg] = await Promise.all([ + tui.queryTerminalColorScheme({ timeoutMs: 150 }), + tui.queryTerminalBackgroundColor({ timeoutMs: 150 }), + ]); + if (scheme === "dark" || scheme === "light") return scheme; + if (bg) return relativeLuminance(bg) >= 0.5 ? "light" : "dark"; + return "dark"; +} diff --git a/src/tui/theme/palettes.test.ts b/src/tui/theme/palettes.test.ts index 61f74354..cb3d8728 100644 --- a/src/tui/theme/palettes.test.ts +++ b/src/tui/theme/palettes.test.ts @@ -27,8 +27,8 @@ const PALETTE_KEYS: (keyof Palette)[] = [ ]; describe("Theme Palettes", () => { - it("has 10 theme presets", () => { - expect(THEME_PRESETS).toHaveLength(10); + it("has the expected number of theme presets", () => { + expect(THEME_PRESETS).toHaveLength(13); }); it("includes all expected presets", () => { @@ -40,6 +40,9 @@ describe("Theme Palettes", () => { expect(THEME_PRESETS).toContain("solarized-light"); expect(THEME_PRESETS).toContain("atom-one-dark"); expect(THEME_PRESETS).toContain("ayu-dark"); + expect(THEME_PRESETS).toContain("colorblind-dark"); + expect(THEME_PRESETS).toContain("colorblind-light"); + expect(THEME_PRESETS).toContain("monochrome"); }); for (const preset of [ diff --git a/src/tui/theme/palettes.ts b/src/tui/theme/palettes.ts index 7cb0997d..e388739d 100644 --- a/src/tui/theme/palettes.ts +++ b/src/tui/theme/palettes.ts @@ -1,7 +1,11 @@ -export type ThemePreset = +/** Built-in theme presets shipped with the TUI. */ +export type BuiltinPreset = | "dark" | "light" | "high-contrast" + | "colorblind-dark" + | "colorblind-light" + | "monochrome" | "dracula" | "github-dark" | "github-light" @@ -10,6 +14,13 @@ export type ThemePreset = | "atom-one-dark" | "ayu-dark"; +/** + * A theme preset name. Either a built-in or a custom theme loaded from + * `~/.mayros/themes/*.json`. The string is resolved through the palette + * registry (builtin switch β†’ custom registry β†’ dark fallback). + */ +export type ThemePreset = string; + export type Palette = { text: string; dim: string; @@ -285,10 +296,99 @@ export const AYU_DARK_PALETTE: Palette = { success: "#AAD94C", }; -export const THEME_PRESETS: ThemePreset[] = [ +/** + * Colorblind-optimized dark palette using Okabe-Ito hues. + * Avoids red/green pairing for error/success β€” error uses vermilion + * (orange-red), success uses sky blue. Accent is amber. + */ +export const COLORBLIND_DARK_PALETTE: Palette = { + text: "#E8E3D5", + dim: "#7B7F87", + accent: "#E69F00", + accentSoft: "#F0E442", + border: "#3C414B", + userBg: "#2B2F36", + userText: "#F3EEE0", + systemText: "#9BA3B2", + toolPendingBg: "#1F2A2F", + toolSuccessBg: "#1E2D23", + toolErrorBg: "#2F1F1F", + toolTitle: "#E69F00", + toolOutput: "#E1DACB", + quote: "#56B4E9", + quoteBorder: "#3B4D6B", + code: "#F0E442", + codeBlock: "#1E232A", + codeBorder: "#343A45", + link: "#56B4E9", + filePath: "#56B4E9", + error: "#D55E00", + success: "#56B4E9", +}; + +/** Colorblind-optimized light palette (Okabe-Ito on light background). */ +export const COLORBLIND_LIGHT_PALETTE: Palette = { + text: "#2B2F36", + dim: "#6B7080", + accent: "#0072B2", + accentSoft: "#009E73", + border: "#D0D5DD", + userBg: "#F0F1F3", + userText: "#2B2F36", + systemText: "#6B7080", + toolPendingBg: "#EDF0F3", + toolSuccessBg: "#E6F4EA", + toolErrorBg: "#FCE8E6", + toolTitle: "#0072B2", + toolOutput: "#3C414B", + quote: "#0072B2", + quoteBorder: "#B8CCE0", + code: "#D55E00", + codeBlock: "#EDF0F3", + codeBorder: "#D0D5DD", + link: "#0072B2", + filePath: "#0072B2", + error: "#D55E00", + success: "#0072B2", +}; + +/** + * Monochrome palette β€” all tokens use the same foreground. Semantic + * differentiation is lost (no color), useful for `NO_COLOR` environments + * or users who prefer a minimal terminal. + */ +export const MONOCHROME_PALETTE: Palette = { + text: "#FFFFFF", + dim: "#AAAAAA", + accent: "#FFFFFF", + accentSoft: "#CCCCCC", + border: "#888888", + userBg: "#666666", + userText: "#FFFFFF", + systemText: "#CCCCCC", + toolPendingBg: "#444444", + toolSuccessBg: "#444444", + toolErrorBg: "#444444", + toolTitle: "#FFFFFF", + toolOutput: "#CCCCCC", + quote: "#CCCCCC", + quoteBorder: "#888888", + code: "#FFFFFF", + codeBlock: "#333333", + codeBorder: "#666666", + link: "#FFFFFF", + filePath: "#FFFFFF", + error: "#FFFFFF", + success: "#FFFFFF", +}; + +export const BUILTIN_THEME_PRESETS: BuiltinPreset[] = [ "dark", "light", "high-contrast", + "colorblind-dark", + "colorblind-light", + "monochrome", "dracula", "github-dark", "github-light", @@ -298,12 +398,41 @@ export const THEME_PRESETS: ThemePreset[] = [ "ayu-dark", ]; +/** Backward-compatible alias for the built-in preset list. */ +export const THEME_PRESETS: BuiltinPreset[] = BUILTIN_THEME_PRESETS; + +// --- Custom theme registry ------------------------------------------------- + +/** Registry of custom palettes loaded from `~/.mayros/themes/*.json`. */ +const customPaletteRegistry = new Map(); + +/** Register a custom palette under a name. */ +export function registerCustomPalette(name: string, palette: Palette): void { + customPaletteRegistry.set(name, palette); +} + +/** Clear all custom palettes (used by the theme loader before re-scanning). */ +export function clearCustomPalettes(): void { + customPaletteRegistry.clear(); +} + +/** List all currently registered custom theme names. */ +export function listCustomThemeNames(): string[] { + return [...customPaletteRegistry.keys()]; +} + export function resolvePalette(preset: ThemePreset): Palette { switch (preset) { case "light": return LIGHT_PALETTE; case "high-contrast": return HIGH_CONTRAST_PALETTE; + case "colorblind-dark": + return COLORBLIND_DARK_PALETTE; + case "colorblind-light": + return COLORBLIND_LIGHT_PALETTE; + case "monochrome": + return MONOCHROME_PALETTE; case "dracula": return DRACULA_PALETTE; case "github-dark": @@ -318,7 +447,10 @@ export function resolvePalette(preset: ThemePreset): Palette { return ATOM_ONE_DARK_PALETTE; case "ayu-dark": return AYU_DARK_PALETTE; - default: + default: { + const custom = customPaletteRegistry.get(preset); + if (custom) return custom; return DARK_PALETTE; + } } } diff --git a/src/tui/theme/theme-factory.ts b/src/tui/theme/theme-factory.ts index 444580fe..edb80ab5 100644 --- a/src/tui/theme/theme-factory.ts +++ b/src/tui/theme/theme-factory.ts @@ -3,7 +3,7 @@ import type { MarkdownTheme, SelectListTheme, SettingsListTheme, -} from "@mariozechner/pi-tui"; +} from "@earendil-works/pi-tui"; import chalk from "chalk"; import { highlight, supportsLanguage } from "cli-highlight"; import { formatTerminalLink } from "../../utils.js"; diff --git a/src/tui/theme/theme-loader.test.ts b/src/tui/theme/theme-loader.test.ts new file mode 100644 index 00000000..4abe5db8 --- /dev/null +++ b/src/tui/theme/theme-loader.test.ts @@ -0,0 +1,71 @@ +import { describe, expect, it } from "vitest"; +import { validatePalette } from "./theme-loader.js"; +import type { Palette } from "./palettes.js"; + +/** + * Theme loader unit tests. + * + * The discoverCustomThemes function hits the filesystem and is tested + * implicitly via integration; these tests cover the pure validation logic + * that gates which JSON files become registered palettes. + */ + +const VALID_PALETTE: Palette = { + text: "#E8E3D5", + dim: "#7B7F87", + accent: "#F6C453", + accentSoft: "#F2A65A", + border: "#3C414B", + userBg: "#2B2F36", + userText: "#F3EEE0", + systemText: "#9BA3B2", + toolPendingBg: "#1F2A2F", + toolSuccessBg: "#1E2D23", + toolErrorBg: "#2F1F1F", + toolTitle: "#F6C453", + toolOutput: "#E1DACB", + quote: "#8CC8FF", + quoteBorder: "#3B4D6B", + code: "#F0C987", + codeBlock: "#1E232A", + codeBorder: "#343A45", + link: "#7DD3A5", + filePath: "#87CEEB", + error: "#F97066", + success: "#7DD3A5", +}; + +describe("validatePalette", () => { + it("accepts a valid palette with all 22 hex tokens", () => { + expect(validatePalette(VALID_PALETTE)).not.toBeNull(); + }); + + it("rejects null", () => { + expect(validatePalette(null)).toBeNull(); + }); + + it("rejects a non-object", () => { + expect(validatePalette("not a palette")).toBeNull(); + }); + + it("rejects a palette missing a key", () => { + const incomplete = { ...VALID_PALETTE } as Partial; + delete incomplete.success; + expect(validatePalette(incomplete)).toBeNull(); + }); + + it("rejects a palette with a non-hex color", () => { + const bad = { ...VALID_PALETTE, accent: "red" }; + expect(validatePalette(bad)).toBeNull(); + }); + + it("rejects a palette with a 3-digit hex", () => { + const bad = { ...VALID_PALETTE, accent: "#F63" }; + expect(validatePalette(bad)).toBeNull(); + }); + + it("accepts lowercase hex", () => { + const lower = { ...VALID_PALETTE, accent: "#f6c453" }; + expect(validatePalette(lower)).not.toBeNull(); + }); +}); diff --git a/src/tui/theme/theme-loader.ts b/src/tui/theme/theme-loader.ts new file mode 100644 index 00000000..d4d6d546 --- /dev/null +++ b/src/tui/theme/theme-loader.ts @@ -0,0 +1,147 @@ +/** + * Custom theme loader. + * + * Discovers user-defined theme palettes from `~/.mayros/themes/*.json` and + * registers them so they can be selected via `/theme ` or configured + * as `config.ui.theme = ""`. Each file must contain a JSON object with + * the 22 palette color tokens (all `#RRGGBB` hex strings). + * + * The loader caches by file mtime and rescans on demand, mirroring the + * markdown-commands discovery pattern. + */ + +import { readFile, stat } from "node:fs/promises"; +import path from "node:path"; +import { resolveStateDir } from "../../config/paths.js"; +import { + clearCustomPalettes, + listCustomThemeNames, + registerCustomPalette, + type Palette, +} from "./palettes.js"; + +const HEX_COLOR = /^#[0-9A-Fa-f]{6}$/; + +const PALETTE_KEYS: ReadonlyArray = [ + "text", + "dim", + "accent", + "accentSoft", + "border", + "userBg", + "userText", + "systemText", + "toolPendingBg", + "toolSuccessBg", + "toolErrorBg", + "toolTitle", + "toolOutput", + "quote", + "quoteBorder", + "code", + "codeBlock", + "codeBorder", + "link", + "filePath", + "error", + "success", +]; + +/** Cache entry: the parsed palette + the file mtime that produced it. */ +type CacheEntry = { palette: Palette; mtimeMs: number }; + +const cache = new Map(); +let lastScanMtime = 0; + +/** Resolve the user themes directory (`~/.mayros/themes`). */ +export function resolveUserThemesDir(): string { + return path.join(resolveStateDir(), "themes"); +} + +/** Validate that a value is a valid palette object with all 22 hex tokens. */ +export function validatePalette(value: unknown): Palette | null { + if (typeof value !== "object" || value === null) return null; + const obj = value as Record; + const palette = {} as Palette; + for (const key of PALETTE_KEYS) { + const val = obj[key]; + if (typeof val !== "string" || !HEX_COLOR.test(val)) return null; + palette[key] = val; + } + return palette; +} + +/** + * Scan `~/.mayros/themes/*.json`, validate each file, and register valid + * palettes. Returns the list of names that were registered. Invalid files + * are silently skipped (best-effort, like other config discovery). + */ +export async function discoverCustomThemes(): Promise { + const dir = resolveUserThemesDir(); + let entries: import("node:fs").Dirent[]; + try { + const { readdir } = await import("node:fs/promises"); + entries = await readdir(dir, { withFileTypes: true }); + } catch { + // Directory doesn't exist or is unreadable β€” no custom themes. + clearCustomPalettes(); + cache.clear(); + return []; + } + + // Track the newest mtime to know if a rescan is needed. + let newestMtime = 0; + const validFiles = entries.filter((e) => e.isFile() && e.name.endsWith(".json")); + + // Clear and re-register from scratch (simplest correct approach). + clearCustomPalettes(); + const registered: string[] = []; + + for (const entry of validFiles) { + const filePath = path.join(dir, entry.name); + const themeName = entry.name.slice(0, -5); // strip .json + + try { + const stats = await stat(filePath); + newestMtime = Math.max(newestMtime, stats.mtimeMs); + + // Use cache if the file hasn't changed. + const cached = cache.get(themeName); + if (cached && cached.mtimeMs === stats.mtimeMs) { + registerCustomPalette(themeName, cached.palette); + registered.push(themeName); + continue; + } + + const content = await readFile(filePath, "utf8"); + const parsed: unknown = JSON.parse(content); + const palette = validatePalette(parsed); + if (palette) { + cache.set(themeName, { palette, mtimeMs: stats.mtimeMs }); + registerCustomPalette(themeName, palette); + registered.push(themeName); + } + } catch { + // Invalid JSON, unreadable file, or validation failure β€” skip. + } + } + + // Clean stale cache entries for files that no longer exist. + const validNames = new Set(registered); + for (const name of cache.keys()) { + if (!validNames.has(name)) cache.delete(name); + } + + lastScanMtime = newestMtime; + return registered; +} + +/** List all theme names available: builtins + custom. */ +export function allThemeNames(builtins: readonly string[]): string[] { + return [...builtins, ...listCustomThemeNames()]; +} + +/** Check if a rescan is needed by comparing directory mtime. */ +export function needsRescan(): boolean { + return lastScanMtime === 0; +} diff --git a/src/tui/tui-command-handlers.ts b/src/tui/tui-command-handlers.ts index 2b504668..c5e830ef 100644 --- a/src/tui/tui-command-handlers.ts +++ b/src/tui/tui-command-handlers.ts @@ -1,5 +1,9 @@ import { randomUUID } from "node:crypto"; -import type { Component, TUI } from "@mariozechner/pi-tui"; +import type { Component, TUI } from "@earendil-works/pi-tui"; +import { copyToClipboard } from "../infra/clipboard.js"; +import { createSubsystemLogger } from "../logging/subsystem.js"; + +const logger = createSubsystemLogger("tui"); import { listThinkingLevelLabels, normalizeUsageDisplay, @@ -9,7 +13,7 @@ import { expandMarkdownCommand, findMarkdownCommand } from "../commands/markdown import type { SessionsPatchResult } from "../gateway/protocol/index.js"; import { formatRelativeTimestamp } from "../infra/format-time/format-relative.ts"; import { normalizeAgentId } from "../routing/session-key.js"; -import { execSync, spawn } from "node:child_process"; +import { execSync } from "node:child_process"; import { writeFileSync } from "node:fs"; import { helpText, parseCommand } from "./commands.js"; import { undo, listUndoEntries } from "./undo-manager.js"; @@ -24,10 +28,11 @@ import { formatContextVisualization } from "./context-visualizer.js"; import { renderDiff, renderDiffStats } from "./diff-renderer.js"; import { compactMessages } from "./compact-handler.js"; import { SessionManager, formatSessionLine } from "./session-manager.js"; +import { theme } from "./theme/theme.js"; import { applyOutputStyle, isValidOutputStyle, OUTPUT_STYLE_NAMES } from "./output-styles.js"; import type { OutputStyle } from "./output-styles.js"; -import { THEME_PRESETS } from "./theme/palettes.js"; -import type { ThemePreset } from "./theme/palettes.js"; +import { THEME_PRESETS, listCustomThemeNames } from "./theme/palettes.js"; +import type { BuiltinPreset } from "./theme/palettes.js"; import { setThemePreset, getThemePreset } from "./theme/theme.js"; import type { ChatLog } from "./components/chat-log.js"; import { @@ -679,7 +684,13 @@ export function createCommandHandlers(context: CommandHandlerContext) { chatLog.addSystem( `${stats.files} file(s) changed, +${stats.additions} -${stats.deletions}`, ); - for (const line of renderDiff(raw)) { + for (const line of renderDiff(raw, { + add: theme.success, + del: theme.error, + header: (t) => theme.bold(t), + hunk: theme.accent, + context: theme.dim, + })) { chatLog.addSystem(line); } } catch (err) { @@ -728,7 +739,7 @@ export function createCommandHandlers(context: CommandHandlerContext) { })); const themeSelector = createSelectList(themeItems, themeItems.length); themeSelector.onSelect = (item) => { - setThemePreset(item.value as ThemePreset); + setThemePreset(item.value); chatLog.addSystem(`theme set to ${item.value}`); closeOverlay(); tui.requestRender(); @@ -741,11 +752,16 @@ export function createCommandHandlers(context: CommandHandlerContext) { tui.requestRender(); break; } - if (!THEME_PRESETS.includes(preset as ThemePreset)) { - chatLog.addSystem(`unknown theme. usage: /theme <${THEME_PRESETS.join("|")}>`); + if ( + !THEME_PRESETS.includes(preset as BuiltinPreset) && + !listCustomThemeNames().includes(preset) + ) { + chatLog.addSystem( + `unknown theme. usage: /theme <${THEME_PRESETS.join("|")}> or a custom theme name`, + ); break; } - setThemePreset(preset as ThemePreset); + setThemePreset(preset); chatLog.addSystem(`theme set to ${preset}`); break; } @@ -766,9 +782,7 @@ export function createCommandHandlers(context: CommandHandlerContext) { } break; case "vim": { - const enabled = !state.vimEnabled; - state.vimEnabled = enabled; - chatLog.addSystem(`vim mode ${enabled ? "enabled" : "disabled"}`); + chatLog.addSystem("vim mode is not available in this build"); break; } case "permission": { @@ -803,7 +817,7 @@ export function createCommandHandlers(context: CommandHandlerContext) { applySessionInfoFromPatch(result); } catch (err) { // Best-effort β€” fast mode works locally even without gateway - process.stderr.write(`[fast-mode] failed to set thinking off: ${String(err)}\n`); + logger.warn("fast-mode: failed to set thinking off", { error: String(err) }); } state.outputStyle = "standard"; chatLog.addSystem("fast mode enabled (thinking: off, style: standard)"); @@ -818,9 +832,10 @@ export function createCommandHandlers(context: CommandHandlerContext) { applySessionInfoFromPatch(result); } catch (err) { // Best-effort β€” restore may fail if gateway is unreachable - process.stderr.write( - `[fast-mode] failed to restore thinking ${prevLevel}: ${String(err)}\n`, - ); + logger.warn("fast-mode: failed to restore thinking", { + level: prevLevel, + error: String(err), + }); } chatLog.addSystem(`fast mode disabled (thinking: ${prevLevel})`); } @@ -874,14 +889,10 @@ export function createCommandHandlers(context: CommandHandlerContext) { break; } try { - const proc = spawn( - process.platform === "darwin" ? "pbcopy" : "xclip", - process.platform === "darwin" ? [] : ["-selection", "clipboard"], - { stdio: ["pipe", "ignore", "ignore"] }, + const ok = await copyToClipboard(lastText); + chatLog.addSystem( + ok ? "last response copied to clipboard" : "copy failed: no clipboard tool found", ); - proc.stdin?.write(lastText); - proc.stdin?.end(); - chatLog.addSystem("last response copied to clipboard"); } catch (err) { chatLog.addSystem(`copy failed: ${String(err)}`); } @@ -1224,14 +1235,14 @@ export function createCommandHandlers(context: CommandHandlerContext) { } const token = exportSession(payload); - // Copy to clipboard + // Copy to clipboard using the cross-platform clipboard helper + // (pbcopy / xclip / wl-copy / clip.exe / PowerShell fallback). try { - execSync( - `echo -n "${token}" | pbcopy 2>/dev/null || echo -n "${token}" | xclip -sel clipboard 2>/dev/null || echo -n "${token}" | xsel --clipboard 2>/dev/null`, - { encoding: "utf-8" }, - ); + const ok = await copyToClipboard(token); chatLog.addSystem( - `Session exported to clipboard (${token.length} chars, ${messages.length} messages). Share this token to import on another device.`, + ok + ? `Session exported to clipboard (${token.length} chars, ${messages.length} messages). Share this token to import on another device.` + : `Session token (no clipboard tool found):\n${token}`, ); } catch { chatLog.addSystem(`Session token:\n${token}`); diff --git a/src/tui/tui-event-handlers.test.ts b/src/tui/tui-event-handlers.test.ts index 11409889..fe1c60a0 100644 --- a/src/tui/tui-event-handlers.test.ts +++ b/src/tui/tui-event-handlers.test.ts @@ -40,6 +40,8 @@ describe("tui-event-handlers: handleAgentEvent", () => { autoMessageSent: false, toolsExpanded: false, showThinking: false, + toolSectionState: "collapsed", + thinkingSectionState: "collapsed", connectionStatus: "connected", activityStatus: "idle", statusTimeout: null, diff --git a/src/tui/tui-helpers.ts b/src/tui/tui-helpers.ts new file mode 100644 index 00000000..2af978fb --- /dev/null +++ b/src/tui/tui-helpers.ts @@ -0,0 +1,228 @@ +/** + * Pure helper functions extracted from tui.ts. + * + * These functions have no dependency on the TUI instance, the gateway client, + * or any mutable state β€” they are pure factories and resolvers tested in + * tui.test.ts. Extracting them keeps tui.ts focused on wiring. + */ + +import { Key, matchesKey } from "@earendil-works/pi-tui"; +import { buildAgentMainSessionKey } from "../routing/session-key.js"; +import type { SessionScope } from "./tui-types.js"; + +export function createEditorSubmitHandler(params: { + editor: { + setText: (value: string) => void; + addToHistory: (value: string) => void; + }; + handleCommand: (value: string) => Promise | void; + sendMessage: (value: string) => Promise | void; + handleBangLine: (value: string) => Promise | void; +}) { + return (text: string) => { + const raw = text; + const value = raw.trim(); + params.editor.setText(""); + + // Keep previous behavior: ignore empty/whitespace-only submissions. + if (!value) { + return; + } + + // Bash mode: only if the very first character is '!' and it's not just '!'. + // IMPORTANT: use the raw (untrimmed) text so leading spaces do NOT trigger. + // Per requirement: a lone '!' should be treated as a normal message. + if (raw.startsWith("!") && raw !== "!") { + params.editor.addToHistory(raw); + void params.handleBangLine(raw); + return; + } + + // Enable built-in editor prompt history navigation (up/down). + params.editor.addToHistory(value); + + if (value.startsWith("/")) { + void params.handleCommand(value); + return; + } + + void params.sendMessage(value); + }; +} + +export function shouldEnableWindowsGitBashPasteFallback(params?: { + platform?: string; + env?: NodeJS.ProcessEnv; +}): boolean { + const platform = params?.platform ?? process.platform; + if (platform !== "win32") { + return false; + } + const env = params?.env ?? process.env; + const msystem = (env.MSYSTEM ?? "").toUpperCase(); + const shell = env.SHELL ?? ""; + const termProgram = (env.TERM_PROGRAM ?? "").toLowerCase(); + if (msystem.startsWith("MINGW") || msystem.startsWith("MSYS")) { + return true; + } + if (shell.toLowerCase().includes("bash")) { + return true; + } + return termProgram.includes("mintty"); +} + +export function createSubmitBurstCoalescer(params: { + submit: (value: string) => void; + enabled: boolean; + burstWindowMs?: number; + now?: () => number; + setTimer?: typeof setTimeout; + clearTimer?: typeof clearTimeout; +}) { + const windowMs = Math.max(1, params.burstWindowMs ?? 50); + const now = params.now ?? (() => Date.now()); + const setTimer = params.setTimer ?? setTimeout; + const clearTimer = params.clearTimer ?? clearTimeout; + let pending: string | null = null; + let pendingAt = 0; + let flushTimer: ReturnType | null = null; + + const clearFlushTimer = () => { + if (!flushTimer) { + return; + } + clearTimer(flushTimer); + flushTimer = null; + }; + + const flushPending = () => { + if (pending === null) { + return; + } + const value = pending; + pending = null; + pendingAt = 0; + clearFlushTimer(); + params.submit(value); + }; + + const scheduleFlush = () => { + clearFlushTimer(); + flushTimer = setTimer(() => { + flushPending(); + }, windowMs); + }; + + return (value: string) => { + if (!params.enabled) { + params.submit(value); + return; + } + if (value.includes("\n")) { + flushPending(); + params.submit(value); + return; + } + const ts = now(); + if (pending === null) { + pending = value; + pendingAt = ts; + scheduleFlush(); + return; + } + if (ts - pendingAt <= windowMs) { + pending = `${pending}\n${value}`; + pendingAt = ts; + scheduleFlush(); + return; + } + flushPending(); + pending = value; + pendingAt = ts; + scheduleFlush(); + }; +} + +export function resolveTuiSessionKey(params: { + raw?: string; + sessionScope: SessionScope; + currentAgentId: string; + sessionMainKey: string; +}) { + const trimmed = (params.raw ?? "").trim(); + if (!trimmed) { + if (params.sessionScope === "global") { + return "global"; + } + return buildAgentMainSessionKey({ + agentId: params.currentAgentId, + mainKey: params.sessionMainKey, + }); + } + if (trimmed === "global" || trimmed === "unknown") { + return trimmed; + } + if (trimmed.startsWith("agent:")) { + return trimmed; + } + return `agent:${params.currentAgentId}:${trimmed}`; +} + +export async function tryInlinePairingApproval(): Promise { + try { + const { listDevicePairing, approveDevicePairing } = await import("../infra/device-pairing.js"); + const list = await listDevicePairing(); + if (!list.pending.length) return false; + const latest = list.pending.reduce((a, b) => (b.ts > a.ts ? b : a)); + return (await approveDevicePairing(latest.requestId)) !== null; + } catch { + return false; + } +} + +export function resolveGatewayDisconnectState(reason?: string): { + connectionStatus: string; + activityStatus: string; + pairingHint?: string; + gatewayDownHint?: string; +} { + const reasonLabel = reason?.trim() ? reason.trim() : "closed"; + if (/pairing required/i.test(reasonLabel)) { + return { + connectionStatus: `gateway disconnected: ${reasonLabel}`, + activityStatus: "pairing required: run mayros devices list", + pairingHint: + "Pairing required. Run `mayros devices list`, approve your request ID, then reconnect.", + }; + } + if (/ECONNREFUSED|connect failed/i.test(reasonLabel)) { + return { + connectionStatus: "gateway not running", + activityStatus: "start gateway with: mayros gateway run", + gatewayDownHint: + "Gateway is not responding. Start it with `mayros gateway run` or `mayros onboard`.", + }; + } + return { + connectionStatus: `gateway disconnected: ${reasonLabel}`, + activityStatus: "idle", + }; +} + +export function createBackspaceDeduper(params?: { dedupeWindowMs?: number; now?: () => number }) { + const dedupeWindowMs = Math.max(0, Math.floor(params?.dedupeWindowMs ?? 8)); + const now = params?.now ?? (() => Date.now()); + let lastBackspaceAt = -1; + + return (data: string): string => { + if (!matchesKey(data, Key.backspace)) { + return data; + } + const ts = now(); + if (lastBackspaceAt >= 0 && ts - lastBackspaceAt <= dedupeWindowMs) { + return ""; + } + lastBackspaceAt = ts; + return data; + }; +} diff --git a/src/tui/tui-local-shell.ts b/src/tui/tui-local-shell.ts index 94c850ca..9a2be368 100644 --- a/src/tui/tui-local-shell.ts +++ b/src/tui/tui-local-shell.ts @@ -1,5 +1,5 @@ import { spawn } from "node:child_process"; -import type { Component, SelectItem } from "@mariozechner/pi-tui"; +import type { Component, SelectItem } from "@earendil-works/pi-tui"; import { createSearchableSelectList } from "./components/selectors.js"; type LocalShellDeps = { diff --git a/src/tui/tui-overlays.test.ts b/src/tui/tui-overlays.test.ts index 070eb0a8..b2bcbd9d 100644 --- a/src/tui/tui-overlays.test.ts +++ b/src/tui/tui-overlays.test.ts @@ -1,4 +1,4 @@ -import type { Component } from "@mariozechner/pi-tui"; +import type { Component } from "@earendil-works/pi-tui"; import { describe, expect, it, vi } from "vitest"; import { createOverlayHandlers } from "./tui-overlays.js"; diff --git a/src/tui/tui-overlays.ts b/src/tui/tui-overlays.ts index 51ba45a9..fc9fa428 100644 --- a/src/tui/tui-overlays.ts +++ b/src/tui/tui-overlays.ts @@ -1,4 +1,4 @@ -import type { Component, TUI } from "@mariozechner/pi-tui"; +import type { Component, TUI } from "@earendil-works/pi-tui"; type OverlayHost = Pick; diff --git a/src/tui/tui-session-actions.test.ts b/src/tui/tui-session-actions.test.ts index f5ed5325..47f96d3c 100644 --- a/src/tui/tui-session-actions.test.ts +++ b/src/tui/tui-session-actions.test.ts @@ -39,6 +39,8 @@ describe("tui session actions", () => { autoMessageSent: false, toolsExpanded: false, showThinking: false, + toolSectionState: "collapsed", + thinkingSectionState: "collapsed", pendingImages: new Map(), connectionStatus: "connected", activityStatus: "idle", @@ -53,7 +55,7 @@ describe("tui session actions", () => { const { refreshSessionInfo } = createSessionActions({ client: { listSessions } as unknown as GatewayChatClient, chatLog: { addSystem: vi.fn() } as unknown as import("./components/chat-log.js").ChatLog, - tui: { requestRender } as unknown as import("@mariozechner/pi-tui").TUI, + tui: { requestRender } as unknown as import("@earendil-works/pi-tui").TUI, opts: {}, state, agentNames: new Map(), diff --git a/src/tui/tui-session-actions.ts b/src/tui/tui-session-actions.ts index 528fbbe3..80d46c9b 100644 --- a/src/tui/tui-session-actions.ts +++ b/src/tui/tui-session-actions.ts @@ -1,4 +1,4 @@ -import type { Component, TUI } from "@mariozechner/pi-tui"; +import type { Component, TUI } from "@earendil-works/pi-tui"; import type { SessionsPatchResult } from "../gateway/protocol/index.js"; import { normalizeAgentId, diff --git a/src/tui/tui-types.ts b/src/tui/tui-types.ts index a9c27d6c..e3bb69c3 100644 --- a/src/tui/tui-types.ts +++ b/src/tui/tui-types.ts @@ -1,3 +1,21 @@ +/** + * Per-section detail visibility. Replaces the previous binary expanded/not + * with a three-state model: + * - "hidden": the section renders only a header line (or nothing). + * - "collapsed": a bounded preview (tool output: first N lines). + * - "expanded": full content. + */ +export type SectionState = "hidden" | "collapsed" | "expanded"; + +export type SectionKey = "thinking" | "tools"; + +export const SECTION_CYCLE: SectionState[] = ["hidden", "collapsed", "expanded"]; + +export function nextSectionState(current: SectionState): SectionState { + const idx = SECTION_CYCLE.indexOf(current); + return SECTION_CYCLE[(idx + 1) % SECTION_CYCLE.length]; +} + export type TuiOptions = { url?: string; token?: string; @@ -109,6 +127,9 @@ export type TuiStateAccess = { autoMessageSent: boolean; toolsExpanded: boolean; showThinking: boolean; + /** Per-section detail visibility (the source of truth; toolsExpanded/showThinking are shims). */ + toolSectionState: SectionState; + thinkingSectionState: SectionState; connectionStatus: string; activityStatus: string; statusTimeout: ReturnType | null; diff --git a/src/tui/tui.ts b/src/tui/tui.ts index 36e7b4e3..5718f8b1 100644 --- a/src/tui/tui.ts +++ b/src/tui/tui.ts @@ -1,19 +1,16 @@ import { CombinedAutocompleteProvider, Container, - Key, Loader, - matchesKey, ProcessTerminal, Text, TUI, -} from "@mariozechner/pi-tui"; +} from "@earendil-works/pi-tui"; import { resolveDefaultAgentId } from "../agents/agent-scope.js"; import { VERSION } from "../version.js"; import { loadConfig } from "../config/config.js"; import { isLoopbackHost } from "../gateway/net.js"; import { - buildAgentMainSessionKey, normalizeAgentId, normalizeMainKey, parseAgentSessionKey, @@ -26,13 +23,15 @@ import { ChatLog } from "./components/chat-log.js"; import { CustomEditor } from "./components/custom-editor.js"; import { WelcomeScreen } from "./components/welcome-screen.js"; import { GatewayChatClient } from "./gateway-chat.js"; -import type { ThemePreset } from "./theme/palettes.js"; +import { enableKeyboardProtocol } from "./term-capabilities.js"; +import { detectTerminalColorScheme } from "./term-capabilities.js"; +import type { BuiltinPreset } from "./theme/palettes.js"; import { THEME_PRESETS } from "./theme/palettes.js"; +import { discoverCustomThemes } from "./theme/theme-loader.js"; import { editorTheme, theme, setThemePreset } from "./theme/theme.js"; import { createCommandHandlers } from "./tui-command-handlers.js"; import { createEventHandlers } from "./tui-event-handlers.js"; import { MouseHandler, createMouseInputListener } from "./mouse-handler.js"; -import { VimHandler } from "./vim-handler.js"; import { formatTokens } from "./tui-formatters.js"; import { createLocalShellRunner } from "./tui-local-shell.js"; import { createOverlayHandlers } from "./tui-overlays.js"; @@ -40,232 +39,34 @@ import { createSessionActions } from "./tui-session-actions.js"; import type { AgentSummary, PendingImage, + SectionState, SessionInfo, SessionScope, TuiOptions, TuiStateAccess, } from "./tui-types.js"; +import { nextSectionState } from "./tui-types.js"; import { buildWaitingStatusMessage, defaultWaitingPhrases } from "./tui-waiting.js"; export { resolveFinalAssistantText } from "./tui-formatters.js"; export type { TuiOptions } from "./tui-types.js"; - -export function createEditorSubmitHandler(params: { - editor: { - setText: (value: string) => void; - addToHistory: (value: string) => void; - }; - handleCommand: (value: string) => Promise | void; - sendMessage: (value: string) => Promise | void; - handleBangLine: (value: string) => Promise | void; -}) { - return (text: string) => { - const raw = text; - const value = raw.trim(); - params.editor.setText(""); - - // Keep previous behavior: ignore empty/whitespace-only submissions. - if (!value) { - return; - } - - // Bash mode: only if the very first character is '!' and it's not just '!'. - // IMPORTANT: use the raw (untrimmed) text so leading spaces do NOT trigger. - // Per requirement: a lone '!' should be treated as a normal message. - if (raw.startsWith("!") && raw !== "!") { - params.editor.addToHistory(raw); - void params.handleBangLine(raw); - return; - } - - // Enable built-in editor prompt history navigation (up/down). - params.editor.addToHistory(value); - - if (value.startsWith("/")) { - void params.handleCommand(value); - return; - } - - void params.sendMessage(value); - }; -} - -export function shouldEnableWindowsGitBashPasteFallback(params?: { - platform?: string; - env?: NodeJS.ProcessEnv; -}): boolean { - const platform = params?.platform ?? process.platform; - if (platform !== "win32") { - return false; - } - const env = params?.env ?? process.env; - const msystem = (env.MSYSTEM ?? "").toUpperCase(); - const shell = env.SHELL ?? ""; - const termProgram = (env.TERM_PROGRAM ?? "").toLowerCase(); - if (msystem.startsWith("MINGW") || msystem.startsWith("MSYS")) { - return true; - } - if (shell.toLowerCase().includes("bash")) { - return true; - } - return termProgram.includes("mintty"); -} - -export function createSubmitBurstCoalescer(params: { - submit: (value: string) => void; - enabled: boolean; - burstWindowMs?: number; - now?: () => number; - setTimer?: typeof setTimeout; - clearTimer?: typeof clearTimeout; -}) { - const windowMs = Math.max(1, params.burstWindowMs ?? 50); - const now = params.now ?? (() => Date.now()); - const setTimer = params.setTimer ?? setTimeout; - const clearTimer = params.clearTimer ?? clearTimeout; - let pending: string | null = null; - let pendingAt = 0; - let flushTimer: ReturnType | null = null; - - const clearFlushTimer = () => { - if (!flushTimer) { - return; - } - clearTimer(flushTimer); - flushTimer = null; - }; - - const flushPending = () => { - if (pending === null) { - return; - } - const value = pending; - pending = null; - pendingAt = 0; - clearFlushTimer(); - params.submit(value); - }; - - const scheduleFlush = () => { - clearFlushTimer(); - flushTimer = setTimer(() => { - flushPending(); - }, windowMs); - }; - - return (value: string) => { - if (!params.enabled) { - params.submit(value); - return; - } - if (value.includes("\n")) { - flushPending(); - params.submit(value); - return; - } - const ts = now(); - if (pending === null) { - pending = value; - pendingAt = ts; - scheduleFlush(); - return; - } - if (ts - pendingAt <= windowMs) { - pending = `${pending}\n${value}`; - pendingAt = ts; - scheduleFlush(); - return; - } - flushPending(); - pending = value; - pendingAt = ts; - scheduleFlush(); - }; -} - -export function resolveTuiSessionKey(params: { - raw?: string; - sessionScope: SessionScope; - currentAgentId: string; - sessionMainKey: string; -}) { - const trimmed = (params.raw ?? "").trim(); - if (!trimmed) { - if (params.sessionScope === "global") { - return "global"; - } - return buildAgentMainSessionKey({ - agentId: params.currentAgentId, - mainKey: params.sessionMainKey, - }); - } - if (trimmed === "global" || trimmed === "unknown") { - return trimmed; - } - if (trimmed.startsWith("agent:")) { - return trimmed; - } - return `agent:${params.currentAgentId}:${trimmed}`; -} - -async function tryInlinePairingApproval(): Promise { - try { - const { listDevicePairing, approveDevicePairing } = await import("../infra/device-pairing.js"); - const list = await listDevicePairing(); - if (!list.pending.length) return false; - const latest = list.pending.reduce((a, b) => (b.ts > a.ts ? b : a)); - return (await approveDevicePairing(latest.requestId)) !== null; - } catch { - return false; - } -} - -export function resolveGatewayDisconnectState(reason?: string): { - connectionStatus: string; - activityStatus: string; - pairingHint?: string; - gatewayDownHint?: string; -} { - const reasonLabel = reason?.trim() ? reason.trim() : "closed"; - if (/pairing required/i.test(reasonLabel)) { - return { - connectionStatus: `gateway disconnected: ${reasonLabel}`, - activityStatus: "pairing required: run mayros devices list", - pairingHint: - "Pairing required. Run `mayros devices list`, approve your request ID, then reconnect.", - }; - } - if (/ECONNREFUSED|connect failed/i.test(reasonLabel)) { - return { - connectionStatus: "gateway not running", - activityStatus: "start gateway with: mayros gateway run", - gatewayDownHint: - "Gateway is not responding. Start it with `mayros gateway run` or `mayros onboard`.", - }; - } - return { - connectionStatus: `gateway disconnected: ${reasonLabel}`, - activityStatus: "idle", - }; -} - -export function createBackspaceDeduper(params?: { dedupeWindowMs?: number; now?: () => number }) { - const dedupeWindowMs = Math.max(0, Math.floor(params?.dedupeWindowMs ?? 8)); - const now = params?.now ?? (() => Date.now()); - let lastBackspaceAt = -1; - - return (data: string): string => { - if (!matchesKey(data, Key.backspace)) { - return data; - } - const ts = now(); - if (lastBackspaceAt >= 0 && ts - lastBackspaceAt <= dedupeWindowMs) { - return ""; - } - lastBackspaceAt = ts; - return data; - }; -} +export { + createEditorSubmitHandler, + shouldEnableWindowsGitBashPasteFallback, + createSubmitBurstCoalescer, + resolveTuiSessionKey, + resolveGatewayDisconnectState, + createBackspaceDeduper, +} from "./tui-helpers.js"; +import { + createEditorSubmitHandler, + shouldEnableWindowsGitBashPasteFallback, + createSubmitBurstCoalescer, + resolveTuiSessionKey, + resolveGatewayDisconnectState, + createBackspaceDeduper, + tryInlinePairingApproval, +} from "./tui-helpers.js"; export async function runTui(opts: TuiOptions) { const config = loadConfig(); @@ -278,9 +79,20 @@ export async function runTui(opts: TuiOptions) { return; } + // Discover custom themes from ~/.mayros/themes/*.json before resolving the + // config theme, so a custom theme name in config.ui.theme works on startup. + await discoverCustomThemes(); + const configTheme = config.ui?.theme; - if (configTheme && THEME_PRESETS.includes(configTheme as ThemePreset)) { - setThemePreset(configTheme as ThemePreset); + // "auto" (or unset) defers to terminal color-scheme detection after the TUI + // instance is created; until then the default "dark" preset is in place. + const themeIsAuto = !configTheme || configTheme === "auto"; + if (!themeIsAuto && THEME_PRESETS.includes(configTheme as BuiltinPreset)) { + setThemePreset(configTheme); + } else if (!themeIsAuto) { + // Could be a custom theme name β€” set it; resolvePalette falls back to + // dark if the name isn't registered yet. + setThemePreset(configTheme); } const keybindingsConfig = config.ui?.keybindings; applyKeybindingsFromConfig(keybindingsConfig); @@ -301,14 +113,15 @@ export async function runTui(opts: TuiOptions) { let wasDisconnected = false; let toolsExpanded = false; let showThinking = false; + let toolSectionState: SectionState = "collapsed"; + let thinkingSectionState: SectionState = "collapsed"; let pairingHintShown = false; let gatewayDownHintShown = false; let outputStyle: string | undefined; let permissionMode: "auto" | "ask" | "deny" = "auto"; let fastMode = false; let previousThinkingLevel: string | undefined; - let vimEnabled = config.ui?.vim ?? false; - const vimHandler = new VimHandler(); + let vimEnabled = false; const mouseHandler = new MouseHandler({ scrollLines: 3, scrollAcceleration: true, @@ -420,6 +233,18 @@ export async function runTui(opts: TuiOptions) { set showThinking(value) { showThinking = value; }, + get toolSectionState() { + return toolSectionState; + }, + set toolSectionState(value) { + toolSectionState = value; + }, + get thinkingSectionState() { + return thinkingSectionState; + }, + set thinkingSectionState(value) { + thinkingSectionState = value; + }, get connectionStatus() { return connectionStatus; }, @@ -455,12 +280,6 @@ export async function runTui(opts: TuiOptions) { }, set vimEnabled(value) { vimEnabled = value ?? false; - if (vimEnabled) { - vimHandler.enable(); - } else { - vimHandler.disable(); - } - updateFooter(); }, get permissionMode() { return permissionMode; @@ -517,6 +336,13 @@ export async function runTui(opts: TuiOptions) { }); const tui = new TUI(new ProcessTerminal()); + // Auto-detect the terminal's dark/light preference before the first render. + // Queries run concurrently with a short timeout, so an unresponsive terminal + // falls back to the default "dark" preset without blocking startup. + if (themeIsAuto) { + const detected = await detectTerminalColorScheme(tui); + setThemePreset(detected); + } const dedupeBackspace = createBackspaceDeduper(); tui.addInputListener((data) => { const next = dedupeBackspace(data); @@ -532,14 +358,10 @@ export async function runTui(opts: TuiOptions) { const chatLog = new ChatLog(); const editor = new CustomEditor(tui, editorTheme); editor.tuiResolver = tuiResolver; - editor.vimHandler = vimHandler; editor.captureClipboardImage = captureClipboardImage; editor.onImagePaste = (img) => { pendingImages.set(img.marker, { base64: img.base64, mimeType: img.mimeType }); }; - if (vimEnabled) { - vimHandler.enable(); - } // Mouse scroll β†’ ChatLog scroll + re-render mouseHandler.onScroll((direction, lines) => { @@ -790,7 +612,6 @@ export async function runTui(opts: TuiOptions) { const reasoning = sessionInfo.reasoningLevel ?? "off"; const reasoningLabel = reasoning === "on" ? "reasoning" : reasoning === "stream" ? "reasoning:stream" : null; - const vimLabel = vimEnabled ? vimHandler.getModeIndicator() : null; const permLabel = permissionMode !== "auto" ? `perm ${permissionMode}` : null; const fastLabel = fastMode ? "FAST" : null; const footerParts = [ @@ -801,7 +622,6 @@ export async function runTui(opts: TuiOptions) { verbose !== "off" ? `verbose ${verbose}` : null, reasoningLabel, tokens, - vimLabel, permLabel, fastLabel, ].filter(Boolean); @@ -904,6 +724,15 @@ export async function runTui(opts: TuiOptions) { }; editor.onCtrlC = () => { const now = Date.now(); + // Tri-state, in priority order: + // 1. an active run β†’ abort it + // 2. non-empty input β†’ clear it + // 3. double Ctrl+C within 1s β†’ exit + // Escape remains a secondary abort path. + if (activeChatRunId) { + void abortActive(); + return; + } if (editor.getText().trim().length > 0) { editor.setText(""); setActivityStatus("cleared input"); @@ -927,9 +756,10 @@ export async function runTui(opts: TuiOptions) { process.exit(0); }; editor.onCtrlO = () => { - toolsExpanded = !toolsExpanded; - chatLog.setToolsExpanded(toolsExpanded); - setActivityStatus(toolsExpanded ? "tools expanded" : "tools collapsed"); + toolSectionState = nextSectionState(toolSectionState); + toolsExpanded = toolSectionState === "expanded"; + chatLog.setToolSectionState(toolSectionState); + setActivityStatus(`tools: ${toolSectionState}`); tui.requestRender(); }; editor.onCtrlL = () => { @@ -942,7 +772,12 @@ export async function runTui(opts: TuiOptions) { void openSessionSelector(); }; editor.onCtrlT = () => { - showThinking = !showThinking; + thinkingSectionState = nextSectionState(thinkingSectionState); + // Thinking text is baked into the assistant message at compose time, so + // toggling requires reloading history. "hidden" suppresses thinking; + // "collapsed" and "expanded" both show it (a truncated mode would require + // restructuring the composer β€” deferred to a follow-up). + showThinking = thinkingSectionState !== "hidden"; void loadHistory(); }; editor.onShiftTab = () => { @@ -1030,10 +865,17 @@ export async function runTui(opts: TuiOptions) { updateHeader(); setConnectionStatus("connecting"); updateFooter(); + // Enable extended keyboard protocols (kitty / modifyOtherKeys) so modifier + // combinations like Shift+Enter survive in capable terminals. The handle + // restores the terminal on exit. + const keyboardProtocol = enableKeyboardProtocol(); tui.start(); client.start(); await new Promise((resolve) => { - const finish = () => resolve(); + const finish = () => { + keyboardProtocol.disable(); + resolve(); + }; process.once("exit", finish); process.once("SIGINT", finish); process.once("SIGTERM", finish); diff --git a/src/tui/vim-handler.test.ts b/src/tui/vim-handler.test.ts deleted file mode 100644 index 978639c9..00000000 --- a/src/tui/vim-handler.test.ts +++ /dev/null @@ -1,302 +0,0 @@ -import { describe, expect, it, beforeEach } from "vitest"; -import { VimHandler } from "./vim-handler.js"; -import type { EditorBridge } from "./vim-handler.js"; - -function createMockBridge(initialText = ""): EditorBridge & { - text: string; - line: number; - col: number; -} { - const state = { text: initialText, line: 0, col: 0 }; - return { - get text() { - return state.text; - }, - set text(v) { - state.text = v; - }, - get line() { - return state.line; - }, - set line(v) { - state.line = v; - }, - get col() { - return state.col; - }, - set col(v) { - state.col = v; - }, - getText: () => state.text, - setText: (v) => { - state.text = v; - }, - getCursorPosition: () => ({ line: state.line, col: state.col }), - setCursorPosition: (l, c) => { - state.line = l; - state.col = c; - }, - }; -} - -describe("VimHandler", () => { - let vim: VimHandler; - let bridge: ReturnType; - - beforeEach(() => { - vim = new VimHandler(); - bridge = createMockBridge("hello world"); - vim.setBridge(bridge); - }); - - describe("mode management", () => { - it("starts in insert mode", () => { - expect(vim.getMode()).toBe("insert"); - expect(vim.isNormalMode()).toBe(false); - }); - - it("switches to normal mode on enable()", () => { - vim.enable(); - expect(vim.getMode()).toBe("normal"); - expect(vim.isNormalMode()).toBe(true); - }); - - it("switches to insert mode on disable()", () => { - vim.enable(); - vim.disable(); - expect(vim.getMode()).toBe("insert"); - }); - - it("shows correct mode indicator", () => { - expect(vim.getModeIndicator()).toBe("-- INSERT --"); - vim.enable(); - expect(vim.getModeIndicator()).toBe("-- NORMAL --"); - }); - }); - - describe("insert mode", () => { - it("does not consume regular keys", () => { - expect(vim.handleKey("a")).toBe(false); - expect(vim.handleKey("x")).toBe(false); - }); - - it("consumes Escape to switch to normal", () => { - expect(vim.handleKey("\x1b")).toBe(true); - expect(vim.getMode()).toBe("normal"); - }); - }); - - describe("normal mode β€” mode switching", () => { - beforeEach(() => { - vim.enable(); - }); - - it("i switches to insert", () => { - expect(vim.handleKey("i")).toBe(true); - expect(vim.getMode()).toBe("insert"); - }); - - it("a switches to insert and moves cursor right", () => { - bridge.col = 2; - vim.handleKey("a"); - expect(vim.getMode()).toBe("insert"); - expect(bridge.col).toBe(3); - }); - - it("I switches to insert and moves to line start", () => { - bridge.col = 5; - vim.handleKey("I"); - expect(vim.getMode()).toBe("insert"); - expect(bridge.col).toBe(0); - }); - - it("A switches to insert and moves to line end", () => { - vim.handleKey("A"); - expect(vim.getMode()).toBe("insert"); - expect(bridge.col).toBe(11); // "hello world".length - }); - - it("o inserts line below and enters insert mode", () => { - vim.handleKey("o"); - expect(vim.getMode()).toBe("insert"); - expect(bridge.text).toBe("hello world\n"); - expect(bridge.line).toBe(1); - }); - - it("O inserts line above and enters insert mode", () => { - vim.handleKey("O"); - expect(vim.getMode()).toBe("insert"); - expect(bridge.text).toBe("\nhello world"); - expect(bridge.line).toBe(0); - }); - }); - - describe("normal mode β€” cursor motions", () => { - beforeEach(() => { - vim.enable(); - bridge.col = 5; - }); - - it("h moves cursor left", () => { - vim.handleKey("h"); - expect(bridge.col).toBe(4); - }); - - it("l moves cursor right", () => { - vim.handleKey("l"); - expect(bridge.col).toBe(6); - }); - - it("0 moves to line start", () => { - vim.handleKey("0"); - expect(bridge.col).toBe(0); - }); - - it("$ moves to line end", () => { - vim.handleKey("$"); - expect(bridge.col).toBe(11); - }); - - it("j moves down", () => { - bridge.text = "line1\nline2"; - bridge.line = 0; - bridge.col = 2; - vim.handleKey("j"); - expect(bridge.line).toBe(1); - }); - - it("k moves up", () => { - bridge.text = "line1\nline2"; - bridge.line = 1; - bridge.col = 0; - vim.handleKey("k"); - expect(bridge.line).toBe(0); - }); - - it("w moves to next word", () => { - bridge.col = 0; - vim.handleKey("w"); - expect(bridge.col).toBe(6); // start of "world" - }); - - it("b moves to previous word start", () => { - bridge.col = 8; - vim.handleKey("b"); - expect(bridge.col).toBe(6); // start of "world" - }); - }); - - describe("normal mode β€” count prefix", () => { - beforeEach(() => { - vim.enable(); - bridge.col = 0; - }); - - it("3l moves cursor 3 right", () => { - vim.handleKey("3"); - vim.handleKey("l"); - expect(bridge.col).toBe(3); - }); - - it("2h moves cursor 2 left", () => { - bridge.col = 5; - vim.handleKey("2"); - vim.handleKey("h"); - expect(bridge.col).toBe(3); - }); - }); - - describe("normal mode β€” editing", () => { - beforeEach(() => { - vim.enable(); - }); - - it("x deletes char at cursor", () => { - bridge.col = 0; - vim.handleKey("x"); - expect(bridge.text).toBe("ello world"); - }); - - it("D deletes to end of line", () => { - bridge.col = 5; - vim.handleKey("D"); - expect(bridge.text).toBe("hello"); - }); - - it("C deletes to end and enters insert", () => { - bridge.col = 5; - vim.handleKey("C"); - expect(bridge.text).toBe("hello"); - expect(vim.getMode()).toBe("insert"); - }); - - it("dd deletes entire line", () => { - bridge.text = "first\nsecond\nthird"; - bridge.line = 1; - vim.handleKey("d"); - vim.handleKey("d"); - expect(bridge.text).toBe("first\nthird"); - }); - - it("cc deletes line and enters insert", () => { - bridge.text = "first\nsecond\nthird"; - bridge.line = 1; - vim.handleKey("c"); - vim.handleKey("c"); - expect(bridge.text).toBe("first\nthird"); - expect(vim.getMode()).toBe("insert"); - }); - - it("yy + p yanks and pastes line", () => { - bridge.text = "hello"; - bridge.col = 2; - vim.handleKey("y"); - vim.handleKey("y"); - vim.handleKey("p"); - // Paste inserts after cursor (col 2): "hel" + "hello" + "lo" - expect(bridge.text).toBe("helhellolo"); - }); - - it("u undoes last edit", () => { - bridge.col = 0; - vim.handleKey("x"); - expect(bridge.text).toBe("ello world"); - vim.handleKey("u"); - expect(bridge.text).toBe("hello world"); - }); - }); - - describe("normal mode β€” key consumption", () => { - beforeEach(() => { - vim.enable(); - }); - - it("all known keys are consumed", () => { - for (const key of [ - "h", - "j", - "k", - "l", - "w", - "b", - "0", - "$", - "i", - "a", - "x", - "u", - "p", - "D", - "C", - ]) { - const result = vim.handleKey(key); - expect(result, `key '${key}' should be consumed`).toBe(true); - vim.enable(); // Reset to normal mode - } - }); - - it("unknown keys are still consumed in normal mode", () => { - expect(vim.handleKey("z")).toBe(true); - }); - }); -}); diff --git a/src/tui/vim-handler.ts b/src/tui/vim-handler.ts deleted file mode 100644 index fa0d2938..00000000 --- a/src/tui/vim-handler.ts +++ /dev/null @@ -1,428 +0,0 @@ -export type VimMode = "normal" | "insert"; - -type MotionResult = - | { - type: "cursor"; - delta: number; - } - | { - type: "line-start" | "line-end" | "word-forward" | "word-backward"; - }; - -type OperatorPending = "d" | "c" | "y" | null; - -export type VimState = { - mode: VimMode; - countPrefix: string; - operator: OperatorPending; - lastYank: string; -}; - -export type EditorBridge = { - getText: () => string; - setText: (value: string) => void; - getCursorPosition: () => { line: number; col: number }; - setCursorPosition: (line: number, col: number) => void; -}; - -export class VimHandler { - private state: VimState; - private bridge: EditorBridge | null = null; - private undoStack: string[] = []; - private maxUndo = 50; - - constructor() { - this.state = { - mode: "insert", - countPrefix: "", - operator: null, - lastYank: "", - }; - } - - setBridge(bridge: EditorBridge): void { - this.bridge = bridge; - } - - getMode(): VimMode { - return this.state.mode; - } - - getModeIndicator(): string { - return this.state.mode === "normal" ? "-- NORMAL --" : "-- INSERT --"; - } - - isNormalMode(): boolean { - return this.state.mode === "normal"; - } - - enable(): void { - this.state.mode = "normal"; - this.state.countPrefix = ""; - this.state.operator = null; - } - - disable(): void { - this.state.mode = "insert"; - this.state.countPrefix = ""; - this.state.operator = null; - } - - /** - * Handle a keypress in vim mode. - * Returns true if the key was consumed (not passed to editor). - */ - handleKey(key: string): boolean { - if (this.state.mode === "insert") { - // Escape β†’ switch to normal mode - if (key === "\x1b" || key === "\u001b") { - this.state.mode = "normal"; - this.state.countPrefix = ""; - this.state.operator = null; - return true; - } - return false; - } - - // Normal mode - return this.handleNormalMode(key); - } - - private getCount(): number { - const n = parseInt(this.state.countPrefix, 10); - this.state.countPrefix = ""; - return isNaN(n) || n < 1 ? 1 : Math.min(n, 999); - } - - private pushUndo(): void { - if (!this.bridge) return; - const text = this.bridge.getText(); - this.undoStack.push(text); - if (this.undoStack.length > this.maxUndo) { - this.undoStack.shift(); - } - } - - private handleNormalMode(key: string): boolean { - // Count prefix accumulation - if (/^[1-9]$/.test(key) || (this.state.countPrefix.length > 0 && /^[0-9]$/.test(key))) { - this.state.countPrefix += key; - return true; - } - - // Operator pending (d, c, y) - if (this.state.operator === null && (key === "d" || key === "c" || key === "y")) { - this.state.operator = key; - return true; - } - - // dd, cc, yy β€” whole-line operations - if (this.state.operator && key === this.state.operator) { - const count = this.getCount(); - this.handleLinewiseOp(this.state.operator, count); - this.state.operator = null; - return true; - } - - // operator + motion - if (this.state.operator) { - const motion = this.resolveMotion(key); - if (motion) { - this.handleOperatorMotion(this.state.operator, motion); - this.state.operator = null; - return true; - } - // Invalid motion β€” cancel operator - this.state.operator = null; - this.state.countPrefix = ""; - return true; - } - - const count = this.getCount(); - - // Mode switches - switch (key) { - case "i": - this.state.mode = "insert"; - return true; - case "a": - this.state.mode = "insert"; - this.moveCursor(1); - return true; - case "I": - this.state.mode = "insert"; - this.moveToLineStart(); - return true; - case "A": - this.state.mode = "insert"; - this.moveToLineEnd(); - return true; - case "o": - this.state.mode = "insert"; - this.insertLineBelow(); - return true; - case "O": - this.state.mode = "insert"; - this.insertLineAbove(); - return true; - } - - // Motions - switch (key) { - case "h": - this.moveCursor(-count); - return true; - case "l": - this.moveCursor(count); - return true; - case "j": - this.moveVertical(count); - return true; - case "k": - this.moveVertical(-count); - return true; - case "w": - for (let i = 0; i < count; i++) this.moveWordForward(); - return true; - case "b": - for (let i = 0; i < count; i++) this.moveWordBackward(); - return true; - case "0": - this.moveToLineStart(); - return true; - case "$": - this.moveToLineEnd(); - return true; - } - - // Editing commands - switch (key) { - case "x": - this.deleteCharsAtCursor(count); - return true; - case "D": - this.deleteToEndOfLine(); - return true; - case "C": - this.deleteToEndOfLine(); - this.state.mode = "insert"; - return true; - case "p": - this.paste(); - return true; - case "u": - this.undo(); - return true; - } - - return true; - } - - private resolveMotion(key: string): MotionResult | null { - switch (key) { - case "h": - return { type: "cursor", delta: -1 }; - case "l": - return { type: "cursor", delta: 1 }; - case "w": - return { type: "word-forward" }; - case "b": - return { type: "word-backward" }; - case "0": - return { type: "line-start" }; - case "$": - return { type: "line-end" }; - default: - return null; - } - } - - private handleLinewiseOp(op: OperatorPending, count: number): void { - if (!this.bridge) return; - this.pushUndo(); - const text = this.bridge.getText(); - const lines = text.split("\n"); - const { line } = this.bridge.getCursorPosition(); - const start = Math.min(line, lines.length - 1); - const end = Math.min(start + count, lines.length); - const deleted = lines.splice(start, end - start); - this.state.lastYank = deleted.join("\n"); - - if (op === "d" || op === "c") { - this.bridge.setText(lines.join("\n")); - const newLine = Math.min(start, Math.max(0, lines.length - 1)); - this.bridge.setCursorPosition(newLine, 0); - } - if (op === "c") { - this.state.mode = "insert"; - } - } - - private handleOperatorMotion(op: OperatorPending, _motion: MotionResult): void { - // Simplified: for non-linewise motions, behave like single-char operations - if (!this.bridge) return; - if (op === "d") { - this.deleteCharsAtCursor(1); - } else if (op === "c") { - this.deleteCharsAtCursor(1); - this.state.mode = "insert"; - } else if (op === "y") { - const text = this.bridge.getText(); - const lines = text.split("\n"); - const { line } = this.bridge.getCursorPosition(); - this.state.lastYank = lines[line] ?? ""; - } - } - - private moveCursor(delta: number): void { - if (!this.bridge) return; - const { line, col } = this.bridge.getCursorPosition(); - const text = this.bridge.getText(); - const lines = text.split("\n"); - const currentLine = lines[line] ?? ""; - const newCol = Math.max(0, Math.min(currentLine.length, col + delta)); - this.bridge.setCursorPosition(line, newCol); - } - - private moveVertical(delta: number): void { - if (!this.bridge) return; - const { line, col } = this.bridge.getCursorPosition(); - const text = this.bridge.getText(); - const lines = text.split("\n"); - const newLine = Math.max(0, Math.min(lines.length - 1, line + delta)); - const targetLine = lines[newLine] ?? ""; - const newCol = Math.min(col, targetLine.length); - this.bridge.setCursorPosition(newLine, newCol); - } - - private moveToLineStart(): void { - if (!this.bridge) return; - const { line } = this.bridge.getCursorPosition(); - this.bridge.setCursorPosition(line, 0); - } - - private moveToLineEnd(): void { - if (!this.bridge) return; - const { line } = this.bridge.getCursorPosition(); - const text = this.bridge.getText(); - const lines = text.split("\n"); - const currentLine = lines[line] ?? ""; - this.bridge.setCursorPosition(line, currentLine.length); - } - - private moveWordForward(): void { - if (!this.bridge) return; - const { line, col } = this.bridge.getCursorPosition(); - const text = this.bridge.getText(); - const lines = text.split("\n"); - const currentLine = lines[line] ?? ""; - - let newCol = col; - // Skip current word chars - while (newCol < currentLine.length && /\w/.test(currentLine[newCol] ?? "")) { - newCol++; - } - // Skip whitespace - while (newCol < currentLine.length && /\s/.test(currentLine[newCol] ?? "")) { - newCol++; - } - if (newCol >= currentLine.length && line < lines.length - 1) { - this.bridge.setCursorPosition(line + 1, 0); - } else { - this.bridge.setCursorPosition(line, newCol); - } - } - - private moveWordBackward(): void { - if (!this.bridge) return; - const { line, col } = this.bridge.getCursorPosition(); - const text = this.bridge.getText(); - const lines = text.split("\n"); - const currentLine = lines[line] ?? ""; - - let newCol = col; - if (newCol > 0) newCol--; - // Skip whitespace backwards - while (newCol > 0 && /\s/.test(currentLine[newCol] ?? "")) { - newCol--; - } - // Skip word chars backwards - while (newCol > 0 && /\w/.test(currentLine[newCol - 1] ?? "")) { - newCol--; - } - if (newCol <= 0 && col === 0 && line > 0) { - const prevLine = lines[line - 1] ?? ""; - this.bridge.setCursorPosition(line - 1, prevLine.length); - } else { - this.bridge.setCursorPosition(line, newCol); - } - } - - private deleteCharsAtCursor(count: number): void { - if (!this.bridge) return; - this.pushUndo(); - const text = this.bridge.getText(); - const lines = text.split("\n"); - const { line, col } = this.bridge.getCursorPosition(); - const currentLine = lines[line] ?? ""; - const deleted = currentLine.slice(col, col + count); - this.state.lastYank = deleted; - lines[line] = currentLine.slice(0, col) + currentLine.slice(col + count); - this.bridge.setText(lines.join("\n")); - this.bridge.setCursorPosition(line, Math.min(col, (lines[line] ?? "").length)); - } - - private deleteToEndOfLine(): void { - if (!this.bridge) return; - this.pushUndo(); - const text = this.bridge.getText(); - const lines = text.split("\n"); - const { line, col } = this.bridge.getCursorPosition(); - const currentLine = lines[line] ?? ""; - this.state.lastYank = currentLine.slice(col); - lines[line] = currentLine.slice(0, col); - this.bridge.setText(lines.join("\n")); - } - - private insertLineBelow(): void { - if (!this.bridge) return; - this.pushUndo(); - const text = this.bridge.getText(); - const lines = text.split("\n"); - const { line } = this.bridge.getCursorPosition(); - lines.splice(line + 1, 0, ""); - this.bridge.setText(lines.join("\n")); - this.bridge.setCursorPosition(line + 1, 0); - } - - private insertLineAbove(): void { - if (!this.bridge) return; - this.pushUndo(); - const text = this.bridge.getText(); - const lines = text.split("\n"); - const { line } = this.bridge.getCursorPosition(); - lines.splice(line, 0, ""); - this.bridge.setText(lines.join("\n")); - this.bridge.setCursorPosition(line, 0); - } - - private paste(): void { - if (!this.bridge || !this.state.lastYank) return; - this.pushUndo(); - const text = this.bridge.getText(); - const lines = text.split("\n"); - const { line, col } = this.bridge.getCursorPosition(); - const currentLine = lines[line] ?? ""; - lines[line] = currentLine.slice(0, col + 1) + this.state.lastYank + currentLine.slice(col + 1); - this.bridge.setText(lines.join("\n")); - this.bridge.setCursorPosition(line, col + this.state.lastYank.length); - } - - private undo(): void { - if (!this.bridge || this.undoStack.length === 0) return; - const prev = this.undoStack.pop()!; - this.bridge.setText(prev); - const lines = prev.split("\n"); - this.bridge.setCursorPosition(Math.min(lines.length - 1, 0), 0); - } -}