From e494595d52d8ddeae88aa13cbdb3a97bac71902a Mon Sep 17 00:00:00 2001 From: ApiliumDevTeam Date: Mon, 13 Jul 2026 17:17:12 +0200 Subject: [PATCH 01/22] chore(lint): add .oxlintrc.json with type-aware rules Formalize the lint configuration that was previously running on oxlint defaults via the --type-aware flag. The config: - Activates type-aware rules (no-floating-promises, no-misused-promises, await-thenable, no-base-to-string, restrict-template-expressions). - Formalizes the repo no-explicit-any policy as a warning. - Disables noisy rules in test files where type strictness is less valuable (unbound-method, no-floating-promises, any). - Keeps correctness category as warnings, not errors, to preserve a green baseline while surfacing 364 items of technical debt for incremental cleanup. --- .oxlintrc.json | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 .oxlintrc.json diff --git a/.oxlintrc.json b/.oxlintrc.json new file mode 100644 index 00000000..0f570074 --- /dev/null +++ b/.oxlintrc.json @@ -0,0 +1,42 @@ +{ + "$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/**", + "**/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" + } + } + ] +} From 010ffea414c534589e37049849799701e71a42e2 Mon Sep 17 00:00:00 2001 From: ApiliumDevTeam Date: Mon, 13 Jul 2026 17:19:51 +0200 Subject: [PATCH 02/22] docs(security): document dependency supply-chain policy Add a Dependency Supply Chain section to SECURITY.md covering the controls the project already enforces via package.json: - The 48-hour release age floor (minimumReleaseAge: 2880). - The rationale requirement for every pnpm.overrides entry, tied to a CVE or regression. - The hard rule that patchedDependencies must use exact versions. - Lockfile discipline: frozen-lockfile in CI, deps: commits for dependency changes, reviewed transitive bumps. - The onlyBuiltDependencies allowlist limiting native build scripts. This formalizes existing practice as written policy so maintainers and contributors have a single source of truth for dependency trust decisions. --- SECURITY.md | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/SECURITY.md b/SECURITY.md index 784bdf96..fb315a71 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -123,6 +123,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. From d26e0218a957e6dfbc71e6e9d6d124984e607214 Mon Sep 17 00:00:00 2001 From: ApiliumDevTeam Date: Mon, 13 Jul 2026 17:21:29 +0200 Subject: [PATCH 03/22] =?UTF-8?q?docs(security):=20add=20trust=20model=20?= =?UTF-8?q?=E2=80=94=20OS=20isolation=20as=20the=20load-bearing=20boundary?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a Trust Model section that states the project's governing security principle: operating-system level isolation is the only load-bearing boundary, and every in-process defense (approval gate, output redaction, scanners, rate limiters, the WASM sandbox) is a heuristic that reduces attack surface but does not contain a determined adversary with in-process code execution. The section classifies real boundaries (separate users, containers, VMs, namespaces, seccomp, Landlock) against in-process defenses, and gives concrete implications for operators running untrusted skills and plugins. This sharpens the existing Plugin Trust Boundary section, which now reads as a specific case of the general model. --- SECURITY.md | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/SECURITY.md b/SECURITY.md index fb315a71..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. From f2da992691d031f5499a7bb36124ea8062b2f092 Mon Sep 17 00:00:00 2001 From: ApiliumDevTeam Date: Mon, 13 Jul 2026 17:25:16 +0200 Subject: [PATCH 04/22] ci: enforce lockfile change policy Add a CI step on pull requests that fails when pnpm-lock.yaml changes without a deps-scoped commit or a corresponding manifest change (package.json, pnpm-workspace.yaml). This implements the lockfile discipline documented in SECURITY.md as an automated gate rather than relying on review alone. The check is implemented as scripts/check-lockfile-policy.mjs so it can also run locally (node scripts/check-lockfile-policy.mjs --base ) before pushing. On CI it only runs on pull_request events, where the base ref is well-defined. --- .github/workflows/ci.yml | 6 ++ scripts/check-lockfile-policy.mjs | 134 ++++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+) create mode 100644 scripts/check-lockfile-policy.mjs 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/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(); From f49188b9d4abf1e2133d4dc5a51cb1d4979c8999 Mon Sep 17 00:00:00 2001 From: ApiliumDevTeam Date: Mon, 13 Jul 2026 17:40:32 +0200 Subject: [PATCH 05/22] refactor: replace all non-test as-any casts with proper types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove every `as any` escape hatch in production code (the 20 occurrences flagged by no-explicit-any once the rule was formalized). Each replacement uses a concrete type: - mattermost/channel.ts: params is already Record, so the casts were unnecessary — access the fields directly with typeof narrowing. - agent-mesh/index.ts: model the on-disk mayros.json and auth-profiles shapes with structural types instead of Record; forward the heterogeneous Mamoru gateway handlers through a typed callable. - feishu/bot.ts: narrow the Lark SDK response with a minimal FeishuUserResponse type; widen the logger signature to unknown[]. - feishu/docx.ts: introduce a FeishuBlock structural type for the document blocks used by sortBlocksByFirstLevel, with a type-guard filter. - mcp-server/index.ts: type registerCli's program as Command and the dashboard respond callback with its structural signature. Also extend the oxlint test override to *.test-helpers.ts so test fixtures are not held to production type strictness. --- .oxlintrc.json | 1 + extensions/agent-mesh/index.ts | 41 +++++++++----- extensions/feishu/src/bot.ts | 15 ++++-- extensions/feishu/src/docx.ts | 11 ++-- extensions/mattermost/src/channel.ts | 12 ++--- extensions/mcp-server/index.ts | 81 +++++++++++++++------------- 6 files changed, 99 insertions(+), 62 deletions(-) diff --git a/.oxlintrc.json b/.oxlintrc.json index 0f570074..cda82e50 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -26,6 +26,7 @@ "**/*.test.ts", "**/*.e2e.test.ts", "**/*.live.test.ts", + "**/*.test-helpers.ts", "**/test-helpers/**", "**/test-utils/**" ], 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/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/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 ────────────────────────────────── From 9212ff3e75c4ea17bc75ef25d0f4ef556ad6c186 Mon Sep 17 00:00:00 2001 From: ApiliumDevTeam Date: Mon, 13 Jul 2026 17:55:02 +0200 Subject: [PATCH 06/22] feat(agents): prompt cache discipline + system-prompt drift detection Formalize the project's prompt-cache invariant and add a drift signal to the cache trace so cache-breaking changes become visible. AGENTS.md: - Add a Prompt Cache Discipline section stating the governing principle: the system prompt must stay stable across runs that have no reason to change it, slash commands that mutate state invalidate only on the next turn, and hooks must be deterministic. This makes the contract explicit for contributors. cache-trace.ts: - Track the last system digest per session key in a module-level map. - When wrapStreamFn sees a different system digest for the same session on a consecutive call, emit a new cache:system-drift event carrying both the current and previous digests. This turns silent cache invalidations (which multiply token cost) into a diagnosable signal. - Propagate previousSystemDigest through the event pipeline. cache-trace.test.ts: - Unit tests covering first-call (no drift), drift on change, stability across repeated calls, and independent per-session tracking. --- AGENTS.md | 10 +++ src/agents/cache-trace.test.ts | 118 +++++++++++++++++++++++++++++++++ src/agents/cache-trace.ts | 39 ++++++++++- 3 files changed, 165 insertions(+), 2 deletions(-) create mode 100644 src/agents/cache-trace.test.ts 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/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..9da1fc3d 100644 --- a/src/agents/cache-trace.ts +++ b/src/agents/cache-trace.ts @@ -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; From 499cd764228cba8526225fe0f9f62bc0e4fe71f6 Mon Sep 17 00:00:00 2001 From: ApiliumDevTeam Date: Mon, 13 Jul 2026 18:02:58 +0200 Subject: [PATCH 07/22] docs: TUI redesign audit and roadmap Add docs/platforms/tui-redesign.md capturing the current state of the terminal UI, the gaps found during an internal audit, and a prioritized P0/P1/P2 roadmap for the next redesign pass. The audit covers rendering, input, slash commands, themes, vim mode, accessibility, and platform coverage. It documents the architectural constraints (shared terminal UI dependency as rendering floor, mandatory gateway, mutable state, two parallel TUI implementations, two palette systems) that bound the redesign, and calls out two P0 items that block trustworthy feature work: verifying the vim bridge is actually wired, and bringing the accessibility TUI to feature parity. --- docs/platforms/tui-redesign.md | 109 +++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 docs/platforms/tui-redesign.md diff --git a/docs/platforms/tui-redesign.md b/docs/platforms/tui-redesign.md new file mode 100644 index 00000000..edc609c2 --- /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.** If the editor bridge is never set from the TUI side, every vim motion that relies on it silently no-ops. Confirm or fix before any further vim work. +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. From 1a1143974b26eecbc7c96da417d3ced8a2930972 Mon Sep 17 00:00:00 2001 From: ApiliumDevTeam Date: Mon, 13 Jul 2026 19:32:07 +0200 Subject: [PATCH 08/22] deps: migrate pi agent suite to @earendil-works scope (#66) The @mariozechner/pi-{agent-core,ai,coding-agent,tui} packages were renamed and deprecated ("use @earendil-works/... going forward"); their last published version (0.73.1) is still affected by 3 open advisories with no fix. Migrate the whole suite to @earendil-works/pi-* ^0.80.2, which carries the fixes. - Repoint all import specifiers @mariozechner/pi-* -> @earendil-works/pi-* across src, extensions, scripts, skills, docs and the mac packaging script. - Bump the 4 root deps to ^0.80.2. - Adapt to the 0.54 -> 0.80 API surface: - pi-ai: legacy helpers (complete/completeSimple/stream/streamSimple/getModel/ getEnvApiKey/streamOpenAIResponses) moved to the `@earendil-works/pi-ai/compat` subpath; OAuth helpers to `@earendil-works/pi-ai/oauth`; deep `/dist/...` imports repointed (types -> root, google-shared -> /api); onPayload callback now (payload, model); ProviderHeaders allows null values; typebox re-exported from pi-ai. - pi-tui: dropped the `Editor` infix (getKeybindings/setKeybindings/ KeybindingsConfig/KeybindingsManager); KeybindingsManager(definitions, userBindings?); namespaced keybinding ids; async 4-arg AutocompleteProvider. - pi-coding-agent: codingTools -> createCodingTools(cwd); Skill.source -> Skill.sourceInfo.source; ModelRegistry.create(...)/getApiKeyForProvider; generateSummary headers param; createAgentSession tools is a name allowlist. - pi-agent-core: agent.replaceMessages/setSystemPrompt -> agent.state.*. - Add pnpm override "@apilium/mayros": "workspace:*" so the bluebubbles plugin resolves its peer to the workspace package instead of pulling the published 0.1.1, which bundled the old vulnerable pi suite. Closes the 3 remaining pi-coding-agent advisories: pnpm audit no longer reports any @mariozechner/pi or pi-coding-agent finding, and @earendil-works has none. Verified: pnpm install, pnpm tsgo (0 errors), pnpm lint (0 errors), oxfmt on changed files. Full test suite not run here (native deps + live keys); needs a CI run and a smoke test of the agent runtime before merge. --- .../Sources/Mayros/ModelCatalogLoader.swift | 2 +- docs/concepts/oauth.md | 2 +- docs/gateway/doctor.md | 2 +- docs/pi.md | 12 +- .../session-management-compaction.md | 2 +- extensions/browser-automation/index.ts | 9 +- extensions/kakeru-bridge/index.ts | 16 +- extensions/matrix/src/tool-actions.ts | 2 +- extensions/osameru-governance/index.ts | 8 +- extensions/voice-call/index.ts | 10 +- package.json | 9 +- pnpm-lock.yaml | 1531 ++++++++--------- scripts/bench-model.ts | 2 +- scripts/package-mac-app.sh | 2 +- skills/coding-agent/SKILL.md | 2 +- src/agents/anthropic-payload-log.ts | 8 +- src/agents/anthropic.setup-token.live.test.ts | 3 +- src/agents/apply-patch.ts | 2 +- src/agents/auth-profiles/oauth.ts | 8 +- src/agents/auth-profiles/store.ts | 2 +- src/agents/auth-profiles/types.ts | 2 +- src/agents/bash-tools.exec-host-gateway.ts | 2 +- src/agents/bash-tools.exec-host-node.ts | 2 +- src/agents/bash-tools.exec-runtime.ts | 2 +- src/agents/bash-tools.exec.ts | 2 +- src/agents/bash-tools.process.ts | 2 +- src/agents/byteplus.live.test.ts | 3 +- src/agents/cache-trace.ts | 2 +- src/agents/chutes-oauth.ts | 2 +- src/agents/cli-credentials.ts | 2 +- src/agents/cli-runner.ts | 2 +- src/agents/cli-runner/helpers.ts | 4 +- src/agents/compaction.e2e.test.ts | 2 +- src/agents/compaction.retry.test.ts | 13 +- ...compaction.tool-result-details.e2e.test.ts | 8 +- src/agents/compaction.ts | 7 +- src/agents/google-gemini-switch.live.test.ts | 2 +- src/agents/minimax.live.test.ts | 3 +- src/agents/model-auth.e2e.test.ts | 2 +- src/agents/model-auth.ts | 3 +- src/agents/model-compat.e2e.test.ts | 2 +- src/agents/model-compat.ts | 2 +- src/agents/model-forward-compat.test.ts | 2 +- src/agents/model-forward-compat.ts | 2 +- src/agents/model-scan.ts | 11 +- src/agents/models.profiles.live.test.ts | 3 +- src/agents/ollama-stream.ts | 6 +- .../openai-responses.reasoning-replay.test.ts | 4 +- ...lpers.formatassistanterrortext.e2e.test.ts | 2 +- ...tant-text-blocks-but-preserves.e2e.test.ts | 2 +- ...mbedded-helpers.validate-turns.e2e.test.ts | 2 +- src/agents/pi-embedded-helpers/bootstrap.ts | 2 +- src/agents/pi-embedded-helpers/errors.ts | 2 +- src/agents/pi-embedded-helpers/images.ts | 2 +- src/agents/pi-embedded-helpers/openai.ts | 2 +- src/agents/pi-embedded-helpers/turns.ts | 2 +- ...pi-embedded-runner-extraparams.e2e.test.ts | 8 +- ...i-embedded-runner-extraparams.live.test.ts | 4 +- ...ner.applygoogleturnorderingfix.e2e.test.ts | 4 +- src/agents/pi-embedded-runner.e2e.test.ts | 9 +- ...unner.google-sanitize-thinking.e2e.test.ts | 4 +- .../pi-embedded-runner.guard.e2e.test.ts | 4 +- ...ner.guard.waitforidle-before-flush.test.ts | 4 +- ...edded-runner.limithistoryturns.e2e.test.ts | 2 +- ...er.openai-tool-id-preservation.e2e.test.ts | 2 +- ...pi-agent.auth-profile-rotation.e2e.test.ts | 2 +- ...r.sanitize-session-history.test-harness.ts | 4 +- ...ed-runner.sanitize-session-history.test.ts | 2 +- ...-embedded-runner.splitsdktools.e2e.test.ts | 2 +- src/agents/pi-embedded-runner/compact.ts | 6 +- src/agents/pi-embedded-runner/extensions.ts | 4 +- ...tra-params.cache-retention-default.test.ts | 2 +- src/agents/pi-embedded-runner/extra-params.ts | 18 +- .../extra-params.zai-tool-stream.test.ts | 8 +- .../pi-embedded-runner/google.e2e.test.ts | 2 +- src/agents/pi-embedded-runner/google.ts | 6 +- src/agents/pi-embedded-runner/history.ts | 2 +- src/agents/pi-embedded-runner/model.ts | 2 +- .../run/attempt.e2e.test.ts | 4 +- src/agents/pi-embedded-runner/run/attempt.ts | 16 +- .../run/compaction-timeout.e2e.test.ts | 2 +- .../run/compaction-timeout.ts | 2 +- src/agents/pi-embedded-runner/run/images.ts | 2 +- src/agents/pi-embedded-runner/run/params.ts | 2 +- .../run/payloads.e2e.test.ts | 2 +- src/agents/pi-embedded-runner/run/payloads.ts | 2 +- src/agents/pi-embedded-runner/run/types.ts | 4 +- ...on-history.tool-result-details.e2e.test.ts | 4 +- .../pi-embedded-runner/system-prompt.ts | 6 +- src/agents/pi-embedded-runner/thinking.ts | 2 +- .../tool-result-context-guard.e2e.test.ts | 2 +- .../tool-result-context-guard.ts | 2 +- .../tool-result-truncation.e2e.test.ts | 2 +- .../tool-result-truncation.ts | 6 +- src/agents/pi-embedded-runner/tool-split.ts | 4 +- src/agents/pi-embedded-runner/utils.ts | 2 +- .../pi-embedded-subscribe.e2e-harness.ts | 2 +- ...-embedded-subscribe.handlers.compaction.ts | 2 +- ...pi-embedded-subscribe.handlers.messages.ts | 2 +- ...cribe.handlers.tools.media.test-helpers.ts | 2 +- ...-embedded-subscribe.handlers.tools.test.ts | 2 +- .../pi-embedded-subscribe.handlers.tools.ts | 2 +- .../pi-embedded-subscribe.handlers.types.ts | 2 +- ...scribe.lifecycle-billing-error.e2e.test.ts | 2 +- ...-embedded-subscribe.reply-tags.e2e.test.ts | 2 +- ...t-duplicate-block-replies-text.e2e.test.ts | 2 +- ...lock-replies-text-end-does-not.e2e.test.ts | 2 +- ...ng-as-separate-message-enabled.e2e.test.ts | 2 +- ...esses-output-without-start-tag.e2e.test.ts | 2 +- ...final-answer-block-replies-are.e2e.test.ts | 2 +- ...ngle-line-fenced-blocks-reopen.e2e.test.ts | 2 +- ...ion.subscribeembeddedpisession.e2e.test.ts | 2 +- ...end-block-replies-message-tool.e2e.test.ts | 2 +- src/agents/pi-embedded-subscribe.ts | 2 +- src/agents/pi-embedded-subscribe.types.ts | 2 +- src/agents/pi-embedded-utils.e2e.test.ts | 2 +- src/agents/pi-embedded-utils.ts | 4 +- .../compaction-safeguard.e2e.test.ts | 2 +- .../pi-extensions/compaction-safeguard.ts | 6 +- .../pi-extensions/context-pruning.e2e.test.ts | 4 +- .../context-pruning/extension.ts | 2 +- .../pi-extensions/context-pruning/pruner.ts | 6 +- src/agents/pi-model-discovery.ts | 6 +- ...nition-adapter.after-tool-call.e2e.test.ts | 2 +- .../pi-tool-definition-adapter.e2e.test.ts | 2 +- src/agents/pi-tool-definition-adapter.ts | 4 +- ...iases-schemas-without-dropping.e2e.test.ts | 2 +- src/agents/pi-tools.policy.e2e.test.ts | 2 +- src/agents/pi-tools.read.ts | 4 +- src/agents/pi-tools.ts | 9 +- src/agents/pi-tools.types.ts | 2 +- .../session-tool-result-guard-wrapper.ts | 4 +- .../session-tool-result-guard.e2e.test.ts | 4 +- ...guard.tool-result-persist-hook.e2e.test.ts | 4 +- src/agents/session-tool-result-guard.ts | 6 +- .../session-transcript-repair.e2e.test.ts | 2 +- src/agents/session-transcript-repair.ts | 2 +- src/agents/skills-status.e2e.test.ts | 7 +- src/agents/skills-status.ts | 4 +- ...ills.resolveskillspromptforrun.e2e.test.ts | 7 +- src/agents/skills/bundled-context.ts | 2 +- src/agents/skills/config.ts | 2 +- src/agents/skills/frontmatter.ts | 2 +- src/agents/skills/types.ts | 2 +- src/agents/skills/workspace.ts | 2 +- src/agents/system-prompt-report.ts | 2 +- .../assistant-message-fixtures.ts | 2 +- src/agents/tool-call-id.e2e.test.ts | 2 +- src/agents/tool-call-id.ts | 2 +- src/agents/tool-images.ts | 4 +- src/agents/tool-summaries.ts | 2 +- src/agents/tools/common.ts | 2 +- src/agents/tools/discord-actions-guild.ts | 2 +- src/agents/tools/discord-actions-messaging.ts | 2 +- .../tools/discord-actions-moderation.ts | 2 +- src/agents/tools/discord-actions-presence.ts | 2 +- src/agents/tools/discord-actions.ts | 2 +- src/agents/tools/image-tool.helpers.ts | 2 +- src/agents/tools/image-tool.ts | 3 +- src/agents/tools/memory-tool.ts | 6 +- src/agents/tools/nodes-tool.ts | 2 +- src/agents/tools/slack-actions.ts | 2 +- src/agents/tools/telegram-actions.ts | 2 +- src/agents/tools/whatsapp-actions.ts | 2 +- src/agents/zai.live.test.ts | 2 +- .../reply/commands-export-session.ts | 7 +- .../reply/commands-system-prompt.ts | 2 +- src/auto-reply/reply/session.ts | 2 +- src/auto-reply/types.ts | 2 +- .../discord/handle-action.guild-admin.ts | 2 +- .../plugins/actions/discord/handle-action.ts | 2 +- src/channels/plugins/message-actions.ts | 2 +- src/channels/plugins/types.core.ts | 2 +- src/cli/skills-cli.e2e.test.ts | 7 +- src/cli/skills-cli.test.ts | 2 +- src/commands/auth-choice.e2e.test.ts | 2 +- src/commands/chutes-oauth.ts | 2 +- src/commands/models/list.list-command.ts | 2 +- src/commands/models/list.registry.ts | 2 +- src/commands/onboard-auth.credentials.ts | 2 +- src/commands/onboard-auth.e2e.test.ts | 2 +- src/commands/openai-codex-oauth.test.ts | 2 +- src/commands/openai-codex-oauth.ts | 4 +- src/config/sessions/transcript.ts | 2 +- src/config/sessions/types.ts | 2 +- .../gateway-models.profiles.live.test.ts | 2 +- .../chat.abort-persistence.test.ts | 2 +- .../chat.inject.parentid.e2e.test.ts | 2 +- src/gateway/server-methods/chat.ts | 2 +- src/gateway/test-helpers.mocks.ts | 19 +- src/infra/outbound/message-action-runner.ts | 2 +- src/infra/outbound/outbound-send-service.ts | 2 +- src/infra/outbound/tool-payload.ts | 2 +- src/media-understanding/providers/image.ts | 4 +- src/plugin-sdk/slack-message-actions.ts | 2 +- src/plugins/types.ts | 2 +- ...unction-call-comes-after-user-turn.test.ts | 6 +- ...eserves-parameters-type-is-missing.test.ts | 4 +- src/providers/google-shared.test-helpers.ts | 2 +- src/providers/qwen-portal-oauth.ts | 2 +- src/security/audit-extra.async.ts | 2 +- src/sessions/input-provenance.ts | 2 +- src/tts/tts-core.ts | 3 +- src/tts/tts.test.ts | 8 +- src/tui/commands.ts | 2 +- src/tui/components/assistant-message.ts | 2 +- src/tui/components/chat-log.ts | 4 +- src/tui/components/custom-editor.ts | 2 +- src/tui/components/filterable-select-list.ts | 10 +- src/tui/components/searchable-select-list.ts | 8 +- src/tui/components/selectors.ts | 7 +- src/tui/components/tool-execution.ts | 2 +- src/tui/components/user-message.ts | 2 +- src/tui/components/welcome-screen.ts | 4 +- src/tui/enriched-autocomplete.test.ts | 40 +- src/tui/enriched-autocomplete.ts | 13 +- src/tui/keybinding-resolver.test.ts | 27 +- src/tui/keybinding-resolver.ts | 17 +- src/tui/theme/theme-factory.ts | 2 +- src/tui/tui-command-handlers.ts | 2 +- src/tui/tui-local-shell.ts | 2 +- src/tui/tui-overlays.test.ts | 2 +- src/tui/tui-overlays.ts | 2 +- src/tui/tui-session-actions.test.ts | 2 +- src/tui/tui-session-actions.ts | 2 +- src/tui/tui.ts | 2 +- 226 files changed, 1152 insertions(+), 1236 deletions(-) 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/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/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/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/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..d4584c93 100644 --- a/package.json +++ b/package.json @@ -164,15 +164,15 @@ "@buape/carbon": "0.0.0-beta-20260216184201", "@clack/prompts": "^1.0.1", "@discordjs/voice": "^0.19.0", + "@earendil-works/pi-agent-core": "^0.80.2", + "@earendil-works/pi-ai": "^0.80.2", + "@earendil-works/pi-coding-agent": "^0.80.2", + "@earendil-works/pi-tui": "^0.80.2", "@grammyjs/runner": "^2.0.3", "@grammyjs/transformer-throttler": "^1.2.1", "@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", - "@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", "@sinclair/typebox": "0.34.48", @@ -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", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 39bc2a0e..d458b3d5 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' @@ -37,13 +38,25 @@ importers: version: 3.1019.0 '@buape/carbon': specifier: 0.0.0-beta-20260216184201 - version: 0.0.0-beta-20260216184201(hono@4.12.7)(opusscript@0.0.8)(opusscript@0.0.8) + version: 0.0.0-beta-20260216184201(hono@4.12.7)(opusscript@0.0.8) '@clack/prompts': specifier: ^1.0.1 version: 1.0.1 '@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.2 + version: 0.80.2(ws@8.19.0)(zod@4.3.6) + '@earendil-works/pi-ai': + specifier: ^0.80.2 + version: 0.80.2(ws@8.19.0)(zod@4.3.6) + '@earendil-works/pi-coding-agent': + specifier: ^0.80.2 + version: 0.80.2(ws@8.19.0)(zod@4.3.6) + '@earendil-works/pi-tui': + specifier: ^0.80.2 + version: 0.80.2 '@grammyjs/runner': specifier: ^2.0.3 version: 2.0.3(grammy@1.40.0) @@ -59,18 +72,6 @@ importers: '@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 @@ -290,8 +291,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 +342,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 +381,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 +397,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 @@ -469,8 +470,8 @@ 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 @@ -510,8 +511,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 +526,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 @@ -610,8 +611,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,8 +623,8 @@ 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 @@ -631,8 +632,8 @@ importers: 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) @@ -759,8 +760,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 @@ -780,8 +781,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 +790,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 +820,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)(@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.7.0)(tsx@4.21.0)(yaml@2.8.3) ui: dependencies: @@ -849,17 +850,17 @@ importers: version: 0.21.1(signal-polyfill@0.2.2) vite: specifier: 7.3.1 - version: 7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3) + version: 7.3.1(@types/node@25.3.0)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.8.3) devDependencies: '@vitest/browser-playwright': specifier: 4.0.18 - version: 4.0.18(playwright@1.58.2)(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.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.21.0)(yaml@2.8.3))(vitest@4.0.18) playwright: specifier: ^1.58.2 version: 1.58.2 vitest: specifier: 4.0.18 - version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.0)(@vitest/browser-playwright@4.0.18)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.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.21.0)(yaml@2.8.3) packages: @@ -868,8 +869,8 @@ packages: peerDependencies: zod: ^3.25.0 || ^4.0.0 - '@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 @@ -877,14 +878,6 @@ packages: 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,8 +895,8 @@ 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.1019.0': @@ -914,44 +907,80 @@ packages: resolution: {integrity: sha512-TNrx7eq6nKNOO62HWPqoBqPLXEkW6nLZQGwjL6lq1jZtigWYbK1NbCnT7mKDzbLMHZfuOECUt3n6CzxjUW9HWQ==} engines: {node: '>=20.0.0'} + '@aws-sdk/core@3.974.23': + resolution: {integrity: sha512-MiWR/uWjxjFXGzrE0Ghc5lWxUxzHsUWFhV+OX7M4cR9SrmrnZs6TXavnCWnzzdwJeFri34xQo81rvGNzK3c4BQ==} + engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-env@3.972.23': resolution: {integrity: sha512-EamaclJcCEaPHp6wiVknNMM2RlsPMjAHSsYSFLNENBM8Wz92QPc6cOn3dif6vPDQt0Oo4IEghDy3NMDCzY/IvA==} 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-http@3.972.25': resolution: {integrity: sha512-qPymamdPcLp6ugoVocG1y5r69ScNiRzb0hogX25/ij+Wz7c7WnsgjLTaz7+eB5BfRxeyUwuw5hgULMuwOGOpcw==} engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-http@3.972.51': + resolution: {integrity: sha512-XET0H2oofciJ5lMRWNIvRjAP7Q3wv2XT+JtJJEdhPWUMwe3TvQ9qcxonpu7vXmNngncvFpi4E2It+Tamas/naA==} + engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-ini@3.972.26': resolution: {integrity: sha512-xKxEAMuP6GYx2y5GET+d3aGEroax3AgGfwBE65EQAUe090lzyJ/RzxPX9s8v7Z6qAk0XwfQl+LrmH05X7YvTeg==} engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-ini@3.972.56': + resolution: {integrity: sha512-IAmc61hbgQiHht9U3x0tnRwz0lzdwOwD/i9voRgdJrKamF+JtmrBOsW9GwB7mfFonNWOWL4qARWYrF8veEMe3w==} + engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-login@3.972.26': resolution: {integrity: sha512-EFcM8RM3TUxnZOfMJo++3PnyxFu1fL/huzmn3Vh+8IWRgqZawUD3cRwwOr+/4bE9DpyHaLOWFAjY0lfK5X9ZkQ==} engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-login@3.972.55': + resolution: {integrity: sha512-hBBkANo3cDn+h2qxxzER4a+J8JCO9o9Z/YYmU7iky6AcaarX5RRdRcHNC6SLdwY0vAXQygn6soUbDqPn3GghaA==} + engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-node@3.972.27': resolution: {integrity: sha512-jXpxSolfFnPVj6GCTtx3xIdWNoDR7hYC/0SbetGZxOC9UnNmipHeX1k6spVstf7eWJrMhXNQEgXC0pD1r5tXIg==} engines: {node: '>=20.0.0'} + '@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==} engines: {node: '>=20.0.0'} + '@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-sso@3.972.26': resolution: {integrity: sha512-c6ghvRb6gTlMznWhGxn/bpVCcp0HRaz4DobGVD9kI4vwHq186nU2xN/S7QGkm0lo0H2jQU8+dgpUFLxfTcwCOg==} engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-sso@3.972.55': + resolution: {integrity: sha512-1FkOz74Ea5QGS9jtIoXp55T/IkSS3spv+nLTT07fRY/+T5xmEOqaYBVIaEmX4zTNvbV6g2lrtlaVKWEoNyJt3w==} + engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-web-identity@3.972.26': resolution: {integrity: sha512-cXcS3+XD3iwhoXkM44AmxjmbcKueoLCINr1e+IceMmCySda5ysNIfiGBGe9qn5EMiQ9Jd7pP0AGFtcd6OV3Lvg==} engines: {node: '>=20.0.0'} - '@aws-sdk/eventstream-handler-node@3.972.7': - resolution: {integrity: sha512-p8k2ZWKJVrR3KIcBbI+/+FcWXdwe3LLgGnixsA7w8lDwWjzSVDHFp6uPeSqBt5PQpRxzak9EheJ1xTmOnHGf4g==} + '@aws-sdk/credential-provider-web-identity@3.972.55': + resolution: {integrity: sha512-g2BoECD1q01kTPByi56+VLVvdWDzMkKIcr77qixpqH0okw2t0U5CoPv+6S8v/D1Y2Wa6QKKtn6XAtDzP+Kfpvg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/eventstream-handler-node@3.972.22': + resolution: {integrity: sha512-tqPJv0dz4+O0hWGm1a6YekcMZyPhDFs/zH73Von7icaVT5n0Jqvm86typ3jRrG+qoUdPhALOnboRLTmnWQTlYQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-eventstream@3.972.4': - resolution: {integrity: sha512-0t+2Dn46cRE9iu5ynUXINBtR0wNHi/Jz3FbrqS5k3dGot2O7Ln1xCqXbJUAtGM5ZAqN77SbnpETAgVWC84DeoA==} + '@aws-sdk/middleware-eventstream@3.972.18': + resolution: {integrity: sha512-OHpk8YoZi3yexPq8aFt1vN1IxA2zLKvsIR5GpWYylX/ve6kQmY7wxHNSFy/D3t2apMZ16rs76Co4dJWcDyIk3A==} engines: {node: '>=20.0.0'} '@aws-sdk/middleware-host-header@3.972.8': @@ -970,24 +999,40 @@ packages: resolution: {integrity: sha512-AilFIh4rI/2hKyyGN6XrB0yN96W2o7e7wyrPWCM6QjZM1mcC/pVkW3IWWRvuBWMpVP8Fg+rMpbzeLQ6dTM4gig==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-websocket@3.972.8': - resolution: {integrity: sha512-KPUXz8lRw73Rh12/QkELxiryC9Wi9Ah1xNzFe2Vtbz2/81c2ZA0yM8er+u0iCF/SRMMhDQshLcmRNgn/ueA+gA==} + '@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==} engines: {node: '>=20.0.0'} + '@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==} engines: {node: '>=20.0.0'} + '@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.1019.0': resolution: {integrity: sha512-OF+2RfRmUKyjzrRWlDcyju3RBsuqcrYDQ8TwrJg8efcOotMzuZN4U9mpVTIdATpmEc4lWNZBMSjPzrGm6JPnAQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/token-providers@3.997.0': - resolution: {integrity: sha512-UdG36F7lU9aTqGFRieEyuRUJlgEJBqKeKKekC0esH21DbUSKhPR1kZBah214kYasIaWe1hLJLaqUigoTa5hZAQ==} + '@aws-sdk/token-providers@3.1048.0': + resolution: {integrity: sha512-k0y/GcuesuSfWyUM0WamrGyeZmltRYaPbHO82UDA6mZ/doB+FOHKutikPAtSXMn/hDz970cF+iRuuiYO9VEbAA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/token-providers@3.1074.0': + resolution: {integrity: sha512-pv80IzgGW4RnXWtft692chZOM9i6PhebVsLCcnaM4dBEPZva2fE6FXAHs76G7Rc7s3yGyX/68G0nZMrUy+Vmpg==} + 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.973.6': @@ -998,10 +1043,6 @@ packages: resolution: {integrity: sha512-Uh93L5sXFNbyR5sEPMzUU8tJ++Ku97EY4udmC01nB8Zu+xfBPwpIwJ6F7snqQeq8h2pf+8SGN5/NoytfKgYPIw==} engines: {node: '>=20.0.0'} - '@aws-sdk/util-format-url@3.972.4': - resolution: {integrity: sha512-rPm9g4WvgTz4ko5kqseIG5Vp5LUAbWBBDalm4ogHLMc0i20ChwQWqwuTUPJSu8zXn43jIM0xO2KZaYQsFJb+ew==} - engines: {node: '>=20.0.0'} - '@aws-sdk/util-locate-window@3.965.4': resolution: {integrity: sha512-H1onv5SkgPBK2P6JR2MjGgbOnttoNzSPIRoeZTNPZYyaplwGg50zS3amXvXqF0/qfXpWEC9rLWU564QTB9bSog==} engines: {node: '>=20.0.0'} @@ -1022,6 +1063,10 @@ packages: resolution: {integrity: sha512-iu2pyvaqmeatIJLURLqx9D+4jKAdTH20ntzB6BFwjyN7V960r4jK32mx0Zf7YbtOYAbmbtQfDNuL60ONinyw7A==} engines: {node: '>=20.0.0'} + '@aws-sdk/xml-builder@3.972.31': + resolution: {integrity: sha512-SzE4Pgyl+hDF+BuyuzxUSpwnuUu9lJuO1YGgteG89/4Qv0+2IQiVQqdbPV32IozLvXWQChPQcdkk/sKvb1QHiQ==} + 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'} @@ -1164,6 +1209,24 @@ packages: resolution: {integrity: sha512-UyX6rGEXzVyPzb1yvjHtPfTlnLvB5jX/stAMdiytHhfoydX+98hfympdOwsnTktzr+IRvphxTbdErgYDJkEsvw==} engines: {node: '>=22.12.0'} + '@earendil-works/pi-agent-core@0.80.2': + resolution: {integrity: sha512-BF9WPhixIFjT6Kp3Iz3H6ugkU+4AWotM8py96XE5pIK0arJbQKMWbR+dXSWWDEmR5yc/aFQODnuyowOEzMGO7Q==} + engines: {node: '>=22.19.0'} + + '@earendil-works/pi-ai@0.80.2': + resolution: {integrity: sha512-5GNKfdrRJ4uZ5Zd9iudoXggi/BbUcKnD/xfRHtdR+7q4vWqPvfx8auFuaT+ewGBVI8K4wj87eigFQ/iCSuy9RQ==} + engines: {node: '>=22.19.0'} + hasBin: true + + '@earendil-works/pi-coding-agent@0.80.2': + resolution: {integrity: sha512-m9v7OUit0s9LklWfh61ca/XY5INjUzjtYtNZwy3cNvyjOLk3IpBgghP8aAp0iH35rLaiRwuuWiJ8t88ODMWY+A==} + engines: {node: '>=22.19.0'} + hasBin: true + + '@earendil-works/pi-tui@0.80.2': + resolution: {integrity: sha512-OvOAMIbXiC9OSse17YMiXIsI9AS5XM/ZV8N/k+UzdlRpPILDQYmLElevgGW92kkXR8qHBClIdzhCjuzlBGvphA==} + engines: {node: '>=22.19.0'} + '@emnapi/core@1.8.1': resolution: {integrity: sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==} @@ -1485,8 +1548,8 @@ packages: cpu: [x64] os: [win32] - '@google/genai@1.42.0': - resolution: {integrity: sha512-+3nlMTcrQufbQ8IumGkOphxD5Pd5kKyJOzLcnY0/1IuE8upJk5aLmoexZ2BJhBp1zAjRJMEB4a2CJwKI9e2EYw==} + '@google/genai@1.52.0': + resolution: {integrity: sha512-gwSvbpiN/17O9TbsqSsE/OzZcpv5Fo4RQjdngGgogtuB9RsyJ8ZHhX5KjHj1bp5N9snN2eK8LDGXSaWW2hof8Q==} engines: {node: '>=20.0.0'} peerDependencies: '@modelcontextprotocol/sdk': ^1.25.2 @@ -1831,91 +1894,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 +1969,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 +1987,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==} @@ -2351,6 +2327,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==} @@ -2631,12 +2611,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 +2641,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==} @@ -2968,34 +2960,26 @@ packages: 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/credential-provider-imds@4.2.12': + resolution: {integrity: sha512-cr2lR792vNZcYMriSIj+Um3x9KWrjcu98kn234xA6reOAFMmbRpQMOv8KPgEmLLtx3eldU6c5wALKFqNOhugmg==} 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==} engines: {node: '>=18.0.0'} + '@smithy/fetch-http-handler@5.5.2': + resolution: {integrity: sha512-Ei/UK/QMhq0rKaMqGPlOAkE2yS9DZeYmZdk1RAKc3vp3zxgleZHZyBLlZv8yLsxljX4svCRuMTD6u3LLIcU4Bg==} + engines: {node: '>=18.0.0'} + '@smithy/hash-node@4.2.12': resolution: {integrity: sha512-QhBYbGrbxTkZ43QoTPrK72DoYviDeg6YKDrHTMJbbC+A0sml3kSjzFtXP7BtbyJnXojLfTQldGdUR0RGD8dA3w==} engines: {node: '>=18.0.0'} @@ -3040,6 +3024,14 @@ packages: resolution: {integrity: sha512-Rnq9vQWiR1+/I6NZZMNzJHV6pZYyEHt2ZnuV3MG8z2NNenC4i/8Kzttz7CjZiHSmsN5frhXhg17z3Zqjjhmz1A==} engines: {node: '>=18.0.0'} + '@smithy/node-http-handler@4.7.3': + resolution: {integrity: sha512-/jPhevcTFPMVl6KNjbaI47iOg1zxC7IsnX4PQDGVZKMFceOXtB8IEYaB7a9VvkP/3oC60WzTeKocvSI7vLT0vA==} + engines: {node: '>=18.0.0'} + + '@smithy/node-http-handler@4.8.2': + resolution: {integrity: sha512-wfl1uwrAqMH9/pi4kqBo5LBcFwrJLxuDLqL7p7qNcJIFcyZDUc6pzhYk4CYv+DP7fIUpQCZumwNnkhPKS52osQ==} + engines: {node: '>=18.0.0'} + '@smithy/property-provider@4.2.12': resolution: {integrity: sha512-jqve46eYU1v7pZ5BM+fmkbq3DerkSluPr5EhvOcHxygxzD05ByDRppRwRPPpFrsFo5yDtCYLKu+kreHKVrvc7A==} engines: {node: '>=18.0.0'} @@ -3068,6 +3060,10 @@ packages: resolution: {integrity: sha512-B/FBwO3MVOL00DaRSXfXfa/TRXRheagt/q5A2NM13u7q+sHS59EOVGQNfG7DkmVtdQm5m3vOosoKAXSqn/OEgw==} engines: {node: '>=18.0.0'} + '@smithy/signature-v4@5.5.2': + resolution: {integrity: sha512-7xHpmPY4rt0IOmeAA8EfjgEH8isT+587TCdy9H6a7d4OMi5CQ0oEHhWllunvPu4j4Cq0vTFwdxXN/kABWPjdyA==} + engines: {node: '>=18.0.0'} + '@smithy/smithy-client@4.12.7': resolution: {integrity: sha512-q3gqnwml60G44FECaEEsdQMplYhDMZYCtYhMCzadCnRnnHIobZJjegmdoUo6ieLQlPUzvrMdIJUpx6DoPmzANQ==} engines: {node: '>=18.0.0'} @@ -3076,6 +3072,10 @@ packages: resolution: {integrity: sha512-787F3yzE2UiJIQ+wYW1CVg2odHjmaWLGksnKQHUrK/lYZSEcy1msuLVvxaR/sI2/aDe9U+TBuLsXnr3vod1g0g==} engines: {node: '>=18.0.0'} + '@smithy/types@4.15.0': + resolution: {integrity: sha512-Z5TAOxygoFvybJV3igo5SloFflSokHx2hu1eFA+DxDTcn+FtKxUSui+rbTRG1pAafMA888Z3MVvCWUuvCrTXjg==} + 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'} @@ -3116,10 +3116,6 @@ packages: 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'} @@ -3132,10 +3128,6 @@ packages: 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'} @@ -3173,9 +3165,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 +3238,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==} @@ -3466,10 +3446,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'} @@ -3496,14 +3472,6 @@ packages: 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==} @@ -3570,10 +3538,6 @@ 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==} @@ -3613,10 +3577,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==} @@ -3841,10 +3801,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 +3833,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 +3849,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: @@ -4022,27 +3974,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'} @@ -4210,6 +4144,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 +4159,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==} @@ -4303,8 +4237,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: @@ -4373,10 +4307,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'} @@ -4469,6 +4399,10 @@ packages: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true + jiti@2.7.0: + resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} + hasBin: true + jose@4.15.9: resolution: {integrity: sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==} @@ -4542,12 +4476,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==} @@ -4627,9 +4562,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 +4587,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,16 +4604,16 @@ packages: resolution: {integrity: sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==} hasBin: true - marked@15.0.12: - resolution: {integrity: sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==} - engines: {node: '>= 18'} - hasBin: true - marked@17.0.3: resolution: {integrity: sha512-jt1v2ObpyOKR8p4XaUJVk3YWRJ5n+i4+rjQopxvV32rSndTJXvIzuUdWWIy/1pFQMkQmvTXawzDNqOH/CUmx6A==} 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 +4738,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,8 +4837,8 @@ 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 @@ -4925,8 +4849,8 @@ packages: 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 @@ -4991,14 +4915,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==} @@ -5156,14 +5072,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,10 +5088,6 @@ 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==} @@ -5355,6 +5267,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.8.0: + resolution: {integrity: sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==} + engines: {node: '>=10'} + hasBin: true + send@0.19.2: resolution: {integrity: sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==} engines: {node: '>= 0.8.0'} @@ -5444,18 +5361,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==} @@ -5722,6 +5627,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'} @@ -6022,9 +5930,6 @@ packages: 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==} @@ -6034,91 +5939,16 @@ snapshots: dependencies: zod: 4.3.6 - '@anthropic-ai/sdk@0.73.0(zod@4.3.6)': + '@anthropic-ai/sdk@0.91.1(zod@4.3.6)': 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 - '@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': @@ -6147,57 +5977,22 @@ snapshots: '@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.7.3 + '@smithy/types': 4.15.0 tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt '@aws-sdk/client-bedrock@3.1019.0': dependencies: @@ -6260,6 +6055,17 @@ snapshots: '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 + '@aws-sdk/core@3.974.23': + dependencies: + '@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-env@3.972.23': dependencies: '@aws-sdk/core': 3.973.25 @@ -6268,6 +6074,14 @@ snapshots: '@smithy/types': 4.13.1 tslib: 2.8.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 + '@aws-sdk/credential-provider-http@3.972.25': dependencies: '@aws-sdk/core': 3.973.25 @@ -6281,6 +6095,16 @@ snapshots: '@smithy/util-stream': 4.5.20 tslib: 2.8.1 + '@aws-sdk/credential-provider-http@3.972.51': + 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/node-http-handler': 4.8.2 + '@smithy/types': 4.15.0 + tslib: 2.8.1 + '@aws-sdk/credential-provider-ini@3.972.26': dependencies: '@aws-sdk/core': 3.973.25 @@ -6300,6 +6124,22 @@ snapshots: transitivePeerDependencies: - aws-crt + '@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 + '@aws-sdk/credential-provider-login@3.972.26': dependencies: '@aws-sdk/core': 3.973.25 @@ -6313,6 +6153,15 @@ snapshots: transitivePeerDependencies: - aws-crt + '@aws-sdk/credential-provider-login@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/credential-provider-node@3.972.27': dependencies: '@aws-sdk/credential-provider-env': 3.972.23 @@ -6330,6 +6179,20 @@ snapshots: transitivePeerDependencies: - aws-crt + '@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/credential-provider-process@3.972.23': dependencies: '@aws-sdk/core': 3.973.25 @@ -6339,6 +6202,14 @@ snapshots: '@smithy/types': 4.13.1 tslib: 2.8.1 + '@aws-sdk/credential-provider-process@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 + '@aws-sdk/credential-provider-sso@3.972.26': dependencies: '@aws-sdk/core': 3.973.25 @@ -6352,6 +6223,16 @@ snapshots: transitivePeerDependencies: - aws-crt + '@aws-sdk/credential-provider-sso@3.972.55': + dependencies: + '@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/credential-provider-web-identity@3.972.26': dependencies: '@aws-sdk/core': 3.973.25 @@ -6364,18 +6245,27 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/eventstream-handler-node@3.972.7': + '@aws-sdk/credential-provider-web-identity@3.972.55': dependencies: - '@aws-sdk/types': 3.973.6 - '@smithy/eventstream-codec': 4.2.10 - '@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-eventstream@3.972.4': + '@aws-sdk/eventstream-handler-node@3.972.22': dependencies: - '@aws-sdk/types': 3.973.6 - '@smithy/protocol-http': 5.3.12 - '@smithy/types': 4.13.1 + '@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-host-header@3.972.8': @@ -6410,19 +6300,14 @@ snapshots: '@smithy/util-retry': 4.2.12 tslib: 2.8.1 - '@aws-sdk/middleware-websocket@3.972.8': + '@aws-sdk/middleware-websocket@3.972.31': 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/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.996.16': @@ -6468,6 +6353,19 @@ snapshots: transitivePeerDependencies: - aws-crt + '@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.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 + '@aws-sdk/region-config-resolver@3.972.10': dependencies: '@aws-sdk/types': 3.973.6 @@ -6476,6 +6374,13 @@ snapshots: '@smithy/types': 4.13.1 tslib: 2.8.1 + '@aws-sdk/signature-v4-multi-region@3.996.35': + dependencies: + '@aws-sdk/types': 3.973.13 + '@smithy/signature-v4': 5.5.2 + '@smithy/types': 4.15.0 + tslib: 2.8.1 + '@aws-sdk/token-providers@3.1019.0': dependencies: '@aws-sdk/core': 3.973.25 @@ -6488,17 +6393,28 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/token-providers@3.997.0': + '@aws-sdk/token-providers@3.1048.0': dependencies: - '@aws-sdk/core': 3.973.25 - '@aws-sdk/nested-clients': 3.996.16 - '@aws-sdk/types': 3.973.6 - '@smithy/property-provider': 4.2.12 - '@smithy/shared-ini-file-loader': 4.4.7 - '@smithy/types': 4.13.1 + '@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/token-providers@3.1074.0': + 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/types@3.973.13': + dependencies: + '@smithy/types': 4.15.0 tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt '@aws-sdk/types@3.973.6': dependencies: @@ -6513,13 +6429,6 @@ snapshots: '@smithy/util-endpoints': 3.3.3 tslib: 2.8.1 - '@aws-sdk/util-format-url@3.972.4': - dependencies: - '@aws-sdk/types': 3.973.6 - '@smithy/querystring-builder': 4.2.12 - '@smithy/types': 4.13.1 - tslib: 2.8.1 - '@aws-sdk/util-locate-window@3.965.4': dependencies: tslib: 2.8.1 @@ -6546,6 +6455,11 @@ snapshots: fast-xml-parser: 5.5.9 tslib: 2.8.1 + '@aws-sdk/xml-builder@3.972.31': + dependencies: + '@smithy/types': 4.15.0 + tslib: 2.8.1 + '@aws/lambda-invoke-store@0.2.4': {} '@azure/abort-controller@2.1.2': @@ -6617,13 +6531,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 @@ -6748,11 +6662,11 @@ 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 transitivePeerDependencies: @@ -6763,6 +6677,76 @@ snapshots: - opusscript - utf-8-validate + '@earendil-works/pi-agent-core@0.80.2(ws@8.19.0)(zod@4.3.6)': + dependencies: + '@earendil-works/pi-ai': 0.80.2(ws@8.19.0)(zod@4.3.6) + 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.2(ws@8.19.0)(zod@4.3.6)': + dependencies: + '@anthropic-ai/sdk': 0.91.1(zod@4.3.6) + '@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.19.0)(zod@4.3.6) + 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.2(ws@8.19.0)(zod@4.3.6)': + dependencies: + '@earendil-works/pi-agent-core': 0.80.2(ws@8.19.0)(zod@4.3.6) + '@earendil-works/pi-ai': 0.80.2(ws@8.19.0)(zod@4.3.6) + '@earendil-works/pi-tui': 0.80.2 + '@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.2': + dependencies: + get-east-asian-width: 1.6.0 + marked: 18.0.5 + '@emnapi/core@1.8.1': dependencies: '@emnapi/wasi-threads': 1.1.0 @@ -6935,7 +6919,7 @@ snapshots: '@esbuild/win32-x64@0.27.3': optional: true - '@google/genai@1.42.0': + '@google/genai@1.52.0': dependencies: google-auth-library: 10.6.1 p-retry: 4.6.2 @@ -7249,129 +7233,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 @@ -7401,79 +7306,53 @@ snapshots: - 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.19.0 + zod: 4.3.6 + zod-to-json-schema: 3.25.1(zod@4.3.6) + 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 +7367,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 @@ -7806,6 +7671,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': @@ -7953,13 +7820,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 +7845,8 @@ snapshots: '@protobufjs/utf8@1.1.0': {} + '@protobufjs/utf8@1.1.1': {} + '@quansync/fs@1.0.0': dependencies: quansync: 1.0.0 @@ -8242,42 +8119,24 @@ snapshots: '@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': + '@smithy/core@3.26.0': dependencies: '@aws-crypto/crc32': 5.2.0 - '@smithy/types': 4.13.1 - '@smithy/util-hex-encoding': 4.2.2 + '@smithy/types': 4.15.0 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': + '@smithy/credential-provider-imds@4.2.12': dependencies: - '@smithy/eventstream-serde-universal': 4.2.10 + '@smithy/node-config-provider': 4.3.12 + '@smithy/property-provider': 4.2.12 '@smithy/types': 4.13.1 + '@smithy/url-parser': 4.2.12 tslib: 2.8.1 - '@smithy/eventstream-serde-universal@4.2.10': + '@smithy/credential-provider-imds@4.4.2': dependencies: - '@smithy/eventstream-codec': 4.2.10 - '@smithy/types': 4.13.1 + '@smithy/core': 3.26.0 + '@smithy/types': 4.15.0 tslib: 2.8.1 '@smithy/fetch-http-handler@5.3.15': @@ -8288,6 +8147,12 @@ snapshots: '@smithy/util-base64': 4.3.2 tslib: 2.8.1 + '@smithy/fetch-http-handler@5.5.2': + dependencies: + '@smithy/core': 3.26.0 + '@smithy/types': 4.15.0 + tslib: 2.8.1 + '@smithy/hash-node@4.2.12': dependencies: '@smithy/types': 4.13.1 @@ -8364,6 +8229,18 @@ snapshots: '@smithy/types': 4.13.1 tslib: 2.8.1 + '@smithy/node-http-handler@4.7.3': + dependencies: + '@smithy/core': 3.26.0 + '@smithy/types': 4.15.0 + tslib: 2.8.1 + + '@smithy/node-http-handler@4.8.2': + dependencies: + '@smithy/core': 3.26.0 + '@smithy/types': 4.15.0 + tslib: 2.8.1 + '@smithy/property-provider@4.2.12': dependencies: '@smithy/types': 4.13.1 @@ -8405,6 +8282,12 @@ snapshots: '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 + '@smithy/signature-v4@5.5.2': + dependencies: + '@smithy/core': 3.26.0 + '@smithy/types': 4.15.0 + tslib: 2.8.1 + '@smithy/smithy-client@4.12.7': dependencies: '@smithy/core': 3.23.12 @@ -8419,6 +8302,10 @@ snapshots: dependencies: tslib: 2.8.1 + '@smithy/types@4.15.0': + dependencies: + tslib: 2.8.1 + '@smithy/url-parser@4.2.12': dependencies: '@smithy/querystring-parser': 4.2.12 @@ -8476,10 +8363,6 @@ snapshots: '@smithy/types': 4.13.1 tslib: 2.8.1 - '@smithy/util-hex-encoding@4.2.1': - dependencies: - tslib: 2.8.1 - '@smithy/util-hex-encoding@4.2.2': dependencies: tslib: 2.8.1 @@ -8495,17 +8378,6 @@ snapshots: '@smithy/types': 4.13.1 tslib: 2.8.1 - '@smithy/util-stream@4.5.15': - 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': dependencies: '@smithy/fetch-http-handler': 5.3.15 @@ -8552,8 +8424,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 +8547,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 +8554,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 @@ -8831,6 +8695,20 @@ snapshots: - msw - utf-8-validate - vite + optional: true + + '@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.21.0)(yaml@2.8.3))(vitest@4.0.18)': + dependencies: + '@vitest/browser': 4.0.18(vite@7.3.1(@types/node@25.3.0)(jiti@2.7.0)(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.7.0)(tsx@4.21.0)(yaml@2.8.3)) + playwright: 1.58.2 + tinyrainbow: 3.0.3 + vitest: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.0)(@vitest/browser-playwright@4.0.18)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.8.3) + transitivePeerDependencies: + - bufferutil + - msw + - utf-8-validate + - vite '@vitest/browser@4.0.18(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.0.18)': dependencies: @@ -8848,6 +8726,24 @@ snapshots: - msw - utf-8-validate - vite + optional: true + + '@vitest/browser@4.0.18(vite@7.3.1(@types/node@25.3.0)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.0.18)': + dependencies: + '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.3.0)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.8.3)) + '@vitest/utils': 4.0.18 + magic-string: 0.30.21 + pixelmatch: 7.1.0 + pngjs: 7.0.0 + sirv: 3.0.2 + tinyrainbow: 3.0.3 + vitest: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.0)(@vitest/browser-playwright@4.0.18)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.8.3) + ws: 8.19.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)': dependencies: @@ -8882,13 +8778,13 @@ 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/mocker@3.2.4(vite@7.3.1(@types/node@25.3.0)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.8.3))': dependencies: '@vitest/spy': 3.2.4 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3) + vite: 7.3.1(@types/node@25.3.0)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.8.3) '@vitest/mocker@4.0.18(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))': dependencies: @@ -8898,6 +8794,14 @@ snapshots: optionalDependencies: 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.21.0)(yaml@2.8.3))': + dependencies: + '@vitest/spy': 4.0.18 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 7.3.1(@types/node@25.3.0)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.8.3) + '@vitest/pretty-format@3.2.4': dependencies: tinyrainbow: 2.0.0 @@ -8951,7 +8855,7 @@ snapshots: '@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 @@ -8964,11 +8868,6 @@ snapshots: - 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 @@ -8991,10 +8890,6 @@ snapshots: agent-base@7.1.4: {} - ajv-formats@3.0.1(ajv@8.18.0): - optionalDependencies: - ajv: 8.18.0 - ajv@8.18.0: dependencies: fast-deep-equal: 3.1.3 @@ -9054,10 +8949,6 @@ 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: dependencies: '@jridgewell/trace-mapping': 0.3.31 @@ -9098,8 +8989,6 @@ snapshots: dependencies: safe-buffer: 5.1.2 - basic-ftp@5.2.0: {} - bcrypt-pbkdf@1.0.2: dependencies: tweetnacl: 0.14.5 @@ -9340,8 +9229,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 +9245,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 +9253,7 @@ snapshots: detect-libc@2.1.2: {} - diff@8.0.3: {} + diff@8.0.4: {} discord-api-types@0.38.37: {} @@ -9520,24 +9401,10 @@ snapshots: 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: {} @@ -9767,6 +9634,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 +9658,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 @@ -9880,7 +9741,7 @@ snapshots: hookified@1.15.1: {} - hosted-git-info@9.0.2: + hosted-git-info@9.0.3: dependencies: lru-cache: 11.2.6 @@ -9967,8 +9828,6 @@ snapshots: ini@1.3.8: {} - ip-address@10.1.0: {} - ipaddr.js@1.9.1: {} ipull@3.9.5: @@ -10067,6 +9926,8 @@ snapshots: jiti@2.6.1: {} + jiti@2.7.0: {} + jose@4.15.9: {} js-tokens@10.0.0: {} @@ -10156,10 +10017,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 @@ -10231,8 +10095,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 +10119,6 @@ snapshots: dependencies: yallist: 4.0.0 - lru-cache@7.18.3: {} - lru-memoizer@2.3.0: dependencies: lodash.clonedeep: 4.5.0 @@ -10287,10 +10147,10 @@ snapshots: punycode.js: 2.3.1 uc.micro: 2.1.0 - marked@15.0.12: {} - marked@17.0.3: {} + marked@18.0.5: {} + math-intrinsics@1.1.0: {} mdurl@2.0.0: {} @@ -10391,8 +10251,6 @@ snapshots: negotiator@1.0.0: {} - netmask@2.0.2: {} - node-addon-api@8.7.0: {} node-api-headers@1.8.0: {} @@ -10518,12 +10376,12 @@ 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.19.0)(zod@4.3.6): optionalDependencies: ws: 8.19.0 zod: 4.3.6 - openai@6.25.0(ws@8.19.0)(zod@4.3.6): + openai@6.26.0(ws@8.19.0)(zod@4.3.6): optionalDependencies: ws: 8.19.0 zod: 4.3.6 @@ -10622,24 +10480,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: {} @@ -10754,9 +10594,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,7 +10608,7 @@ 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 @@ -10781,22 +10620,20 @@ snapshots: '@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 + '@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,19 +10657,6 @@ 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: {} psl@1.15.0: @@ -11052,6 +10876,8 @@ snapshots: semver@7.7.4: {} + semver@7.8.0: {} + send@0.19.2: dependencies: debug: 2.6.9 @@ -11213,21 +11039,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 @@ -11486,6 +11297,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 +11359,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.21.0)(yaml@2.8.3): dependencies: cac: 6.7.14 debug: 4.4.3 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3) + vite: 7.3.1(@types/node@25.3.0)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.8.3) transitivePeerDependencies: - '@types/node' - jiti @@ -11582,11 +11395,26 @@ snapshots: tsx: 4.21.0 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): + vite@7.3.1(@types/node@25.3.0)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.8.3): + dependencies: + esbuild: 0.27.3 + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + postcss: 8.5.6 + rollup: 4.59.0 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 25.3.0 + fsevents: 2.3.3 + jiti: 2.7.0 + tsx: 4.21.0 + 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.7.0)(tsx@4.21.0)(yaml@2.8.3): dependencies: '@types/chai': 5.2.3 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + '@vitest/mocker': 3.2.4(vite@7.3.1(@types/node@25.3.0)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.8.3)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 @@ -11604,8 +11432,8 @@ 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.21.0)(yaml@2.8.3) + vite-node: 3.2.4(@types/node@25.3.0)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.8.3) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 25.3.0 @@ -11663,6 +11491,45 @@ 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.7.0)(tsx@4.21.0)(yaml@2.8.3): + dependencies: + '@vitest/expect': 4.0.18 + '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.3.0)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.8.3)) + '@vitest/pretty-format': 4.0.18 + '@vitest/runner': 4.0.18 + '@vitest/snapshot': 4.0.18 + '@vitest/spy': 4.0.18 + '@vitest/utils': 4.0.18 + es-module-lexer: 1.7.0 + expect-type: 1.3.0 + magic-string: 0.30.21 + obug: 2.1.1 + pathe: 2.0.3 + picomatch: 4.0.4 + std-env: 3.10.0 + tinybench: 2.9.0 + tinyexec: 1.0.2 + tinyglobby: 0.2.15 + tinyrainbow: 3.0.3 + vite: 7.3.1(@types/node@25.3.0)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.8.3) + why-is-node-running: 2.3.0 + optionalDependencies: + '@opentelemetry/api': 1.9.0 + '@types/node': 25.3.0 + '@vitest/browser-playwright': 4.0.18(playwright@1.58.2)(vite@7.3.1(@types/node@25.3.0)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.0.18) + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - terser + - tsx + - yaml + web-streams-polyfill@3.3.3: {} webidl-conversions@3.0.1: {} @@ -11739,16 +11606,10 @@ snapshots: yoctocolors@2.1.2: {} - zod-to-json-schema@3.25.1(zod@3.25.76): - 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.3.6: {} 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/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.ts b/src/agents/cache-trace.ts index 9da1fc3d..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"; 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.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/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/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/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/commands.ts b/src/tui/commands.ts index a6244d52..4ee2c107 100644 --- a/src/tui/commands.ts +++ b/src/tui/commands.ts @@ -1,4 +1,4 @@ -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"; diff --git a/src/tui/components/assistant-message.ts b/src/tui/components/assistant-message.ts index 6afdc6ac..2d9c3c84 100644 --- a/src/tui/components/assistant-message.ts +++ b/src/tui/components/assistant-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 AssistantMessageComponent extends Container { diff --git a/src/tui/components/chat-log.ts b/src/tui/components/chat-log.ts index 79ca5adb..41d142e1 100644 --- a/src/tui/components/chat-log.ts +++ b/src/tui/components/chat-log.ts @@ -1,5 +1,5 @@ -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 { theme } from "../theme/theme.js"; import { AssistantMessageComponent } from "./assistant-message.js"; import { ToolExecutionComponent } from "./tool-execution.js"; diff --git a/src/tui/components/custom-editor.ts b/src/tui/components/custom-editor.ts index ce2f5ffd..6cf0ecbc 100644 --- a/src/tui/components/custom-editor.ts +++ b/src/tui/components/custom-editor.ts @@ -1,4 +1,4 @@ -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"; 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..931af40f 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, 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"; 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..fe692b50 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"; 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/keybinding-resolver.test.ts b/src/tui/keybinding-resolver.test.ts index 5fc874a1..db5b4c5b 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}` }, })); @@ -73,18 +74,22 @@ describe("DEFAULT_TUI_KEYBINDINGS", () => { }); 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..669c27ce 100644 --- a/src/tui/keybinding-resolver.ts +++ b/src/tui/keybinding-resolver.ts @@ -1,10 +1,11 @@ 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" @@ -58,8 +59,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 +71,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/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/tui-command-handlers.ts b/src/tui/tui-command-handlers.ts index 2b504668..3e23a5ca 100644 --- a/src/tui/tui-command-handlers.ts +++ b/src/tui/tui-command-handlers.ts @@ -1,5 +1,5 @@ import { randomUUID } from "node:crypto"; -import type { Component, TUI } from "@mariozechner/pi-tui"; +import type { Component, TUI } from "@earendil-works/pi-tui"; import { listThinkingLevelLabels, normalizeUsageDisplay, 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..b28cf92a 100644 --- a/src/tui/tui-session-actions.test.ts +++ b/src/tui/tui-session-actions.test.ts @@ -53,7 +53,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.ts b/src/tui/tui.ts index 36e7b4e3..b6618769 100644 --- a/src/tui/tui.ts +++ b/src/tui/tui.ts @@ -7,7 +7,7 @@ import { 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"; From fdf6b51491b438bf0b4ead2c27732bdeafa2b87f Mon Sep 17 00:00:00 2001 From: ApiliumDevTeam Date: Mon, 13 Jul 2026 19:49:00 +0200 Subject: [PATCH 09/22] deps: bump agent stack to 0.80.6 and refresh runtime dependencies (#68) Move the four core libraries to the latest patch on the 0.80 line (@earendil-works/pi-agent-core, pi-ai, pi-coding-agent, pi-tui: 0.80.2 -> 0.80.6). The 0.80.6 release internally consumes zod 4.4.3, so align the workspace to zod ^4.4.3 and add a pnpm override pinning zod to 4.4.3 to keep a single resolved version across the tree. Refresh 21 runtime and dev dependencies to their current minor/patch releases: @aws-sdk/client-bedrock, @clack/prompts, @grammyjs/types, @homebridge/ciao, @noble/curves, @slack/bolt, @slack/web-api, @vitest/coverage-v8, ajv, dotenv, grammy, jiti, lit, markdown-it, oxlint, semver, tslog, tsx, vitest, ws. Exact-pinned and security-overridden dependencies (hono, tar, undici, yaml, typebox, playwright-core, etc.) are left untouched by design. Skip the symlink-based config test on Windows where unprivileged symlink creation is not permitted. --- extensions/feishu/package.json | 6 +- package.json | 53 +- pnpm-lock.yaml | 2218 +++++++++++++++----------------- src/config/includes.test.ts | 6 +- 4 files changed, 1098 insertions(+), 1185 deletions(-) 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/package.json b/package.json index d4584c93..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.2", - "@earendil-works/pi-ai": "^0.80.2", - "@earendil-works/pi-coding-agent": "^0.80.2", - "@earendil-works/pi-tui": "^0.80.2", + "@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", "@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", @@ -268,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 d458b3d5..38f7aa66 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -25,6 +25,7 @@ overrides: brace-expansion: '>=5.0.5' music-metadata: '>=11.12.3' yaml: '>=2.8.3' + zod: 4.4.3 importers: @@ -32,40 +33,40 @@ 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) '@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) '@earendil-works/pi-agent-core': - specifier: ^0.80.2 - version: 0.80.2(ws@8.19.0)(zod@4.3.6) + specifier: ^0.80.6 + version: 0.80.6(ws@8.21.0)(zod@4.4.3) '@earendil-works/pi-ai': - specifier: ^0.80.2 - version: 0.80.2(ws@8.19.0)(zod@4.3.6) + specifier: ^0.80.6 + version: 0.80.6(ws@8.21.0)(zod@4.4.3) '@earendil-works/pi-coding-agent': - specifier: ^0.80.2 - version: 0.80.2(ws@8.19.0)(zod@4.3.6) + specifier: ^0.80.6 + version: 0.80.6(ws@8.21.0)(zod@4.4.3) '@earendil-works/pi-tui': - specifier: ^0.80.2 - version: 0.80.2 + 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 @@ -79,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 @@ -115,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 @@ -127,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 @@ -136,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 @@ -151,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 @@ -178,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 @@ -190,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 @@ -236,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 @@ -257,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: @@ -406,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: @@ -473,8 +474,8 @@ importers: 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: @@ -541,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: @@ -586,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:* @@ -626,8 +627,8 @@ importers: specifier: workspace:* version: link:../.. zod: - specifier: ^4.3.6 - version: 4.3.6 + specifier: 4.4.3 + version: 4.4.3 extensions/nostr: dependencies: @@ -638,8 +639,8 @@ importers: 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: @@ -750,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:* @@ -769,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: @@ -820,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.7.0)(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: @@ -850,30 +851,30 @@ 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.7.0)(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.7.0)(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.7.0)(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.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 @@ -899,82 +900,82 @@ packages: resolution: {integrity: sha512-u+NT61JZEkRFtpL0CAw1N1dwxnaLgwVXQl/zjJxTGgLyS/jTIdg2SdoEoCTHxgDyCnqa1HEi9QOoE9/pYRNpOQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/client-bedrock@3.1019.0': - resolution: {integrity: sha512-GWe/mqO8CQBmco6M0f9n7DZsItmxTarwvo1zxuVLiqFb8ondXDU+Ry/TNn6FXWHm6YRLm8DUPwaSmcckdGyZNQ==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/core@3.973.25': - resolution: {integrity: sha512-TNrx7eq6nKNOO62HWPqoBqPLXEkW6nLZQGwjL6lq1jZtigWYbK1NbCnT7mKDzbLMHZfuOECUt3n6CzxjUW9HWQ==} + '@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/credential-provider-env@3.972.23': - resolution: {integrity: sha512-EamaclJcCEaPHp6wiVknNMM2RlsPMjAHSsYSFLNENBM8Wz92QPc6cOn3dif6vPDQt0Oo4IEghDy3NMDCzY/IvA==} + '@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-http@3.972.25': - resolution: {integrity: sha512-qPymamdPcLp6ugoVocG1y5r69ScNiRzb0hogX25/ij+Wz7c7WnsgjLTaz7+eB5BfRxeyUwuw5hgULMuwOGOpcw==} + '@aws-sdk/credential-provider-env@3.972.57': + resolution: {integrity: sha512-1RfJaF7SW1TOnvNGU7kaYjwUf5H3sfm+synGH1bHhRlqcnxCt3szebH3dmKEyY4tuGcbQ6ffzUT89cRitBV8OQ==} engines: {node: '>=20.0.0'} '@aws-sdk/credential-provider-http@3.972.51': resolution: {integrity: sha512-XET0H2oofciJ5lMRWNIvRjAP7Q3wv2XT+JtJJEdhPWUMwe3TvQ9qcxonpu7vXmNngncvFpi4E2It+Tamas/naA==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-ini@3.972.26': - resolution: {integrity: sha512-xKxEAMuP6GYx2y5GET+d3aGEroax3AgGfwBE65EQAUe090lzyJ/RzxPX9s8v7Z6qAk0XwfQl+LrmH05X7YvTeg==} + '@aws-sdk/credential-provider-http@3.972.59': + resolution: {integrity: sha512-sRCkpTiFnCdQvuaRVjQ6SVoHu6i7RUpurVo1c4F81HWhPvUJ7Wdp5MNtSdX1O29CNXc8em3O5m52hCjVtAD9SA==} engines: {node: '>=20.0.0'} '@aws-sdk/credential-provider-ini@3.972.56': resolution: {integrity: sha512-IAmc61hbgQiHht9U3x0tnRwz0lzdwOwD/i9voRgdJrKamF+JtmrBOsW9GwB7mfFonNWOWL4qARWYrF8veEMe3w==} 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-ini@3.973.1': + resolution: {integrity: sha512-6d8H6ZAh3ZPKZ6fe1nG2OWeZEZPtt9ravoD1dezPdPtsSkJRoxGAnFSHwKT3E/Te6fHE30zRzjV6TD12rvF6yQ==} engines: {node: '>=20.0.0'} '@aws-sdk/credential-provider-login@3.972.55': resolution: {integrity: sha512-hBBkANo3cDn+h2qxxzER4a+J8JCO9o9Z/YYmU7iky6AcaarX5RRdRcHNC6SLdwY0vAXQygn6soUbDqPn3GghaA==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-node@3.972.27': - resolution: {integrity: sha512-jXpxSolfFnPVj6GCTtx3xIdWNoDR7hYC/0SbetGZxOC9UnNmipHeX1k6spVstf7eWJrMhXNQEgXC0pD1r5tXIg==} + '@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.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-process@3.972.49': resolution: {integrity: sha512-C8h36lBuC/RnBSsjlO+dn6xZm3KbAl5vpJaVPAfQnMmz2/OISmKOc8XZcqMQgO2ADwBYNRMM6Kf3vz9G/TulMQ==} 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.57': + resolution: {integrity: sha512-TiVQhuU0pbhIZAUZacbPHMyzrIdiH+lnx+PMY/Pu/b93dJrq3wdZwzUJ0TPpvNxaqbHsxJvQZW3/h/beLiKq7Q==} engines: {node: '>=20.0.0'} '@aws-sdk/credential-provider-sso@3.972.55': resolution: {integrity: sha512-1FkOz74Ea5QGS9jtIoXp55T/IkSS3spv+nLTT07fRY/+T5xmEOqaYBVIaEmX4zTNvbV6g2lrtlaVKWEoNyJt3w==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-web-identity@3.972.26': - resolution: {integrity: sha512-cXcS3+XD3iwhoXkM44AmxjmbcKueoLCINr1e+IceMmCySda5ysNIfiGBGe9qn5EMiQ9Jd7pP0AGFtcd6OV3Lvg==} + '@aws-sdk/credential-provider-sso@3.973.1': + resolution: {integrity: sha512-3foTZUJ4821Ij60X7K3NJroygiZLnbBmarN+T//O2cjkISan90zElN3NBmgSlDrTQ7Gs6z/yO8V7h60QNcDZHQ==} engines: {node: '>=20.0.0'} '@aws-sdk/credential-provider-web-identity@3.972.55': resolution: {integrity: sha512-g2BoECD1q01kTPByi56+VLVvdWDzMkKIcr77qixpqH0okw2t0U5CoPv+6S8v/D1Y2Wa6QKKtn6XAtDzP+Kfpvg==} engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-web-identity@3.972.63': + resolution: {integrity: sha512-8qZLFhM69eKcS37m459ctPR05Qimycm/74OPVioe6wNZabMT54GYhwBju0+J656RkMasNSawWQu+c8CmBe3TUQ==} + engines: {node: '>=20.0.0'} + '@aws-sdk/eventstream-handler-node@3.972.22': resolution: {integrity: sha512-tqPJv0dz4+O0hWGm1a6YekcMZyPhDFs/zH73Von7icaVT5n0Jqvm86typ3jRrG+qoUdPhALOnboRLTmnWQTlYQ==} engines: {node: '>=20.0.0'} @@ -983,44 +984,24 @@ packages: resolution: {integrity: sha512-OHpk8YoZi3yexPq8aFt1vN1IxA2zLKvsIR5GpWYylX/ve6kQmY7wxHNSFy/D3t2apMZ16rs76Co4dJWcDyIk3A==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-host-header@3.972.8': - resolution: {integrity: sha512-wAr2REfKsqoKQ+OkNqvOShnBoh+nkPurDKW7uAeVSu6kUECnWlSJiPvnoqxGlfousEY/v9LfS9sNc46hjSYDIQ==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/middleware-logger@3.972.8': - resolution: {integrity: sha512-CWl5UCM57WUFaFi5kB7IBY1UmOeLvNZAZ2/OZ5l20ldiJ3TiIz1pC65gYj8X0BCPWkeR1E32mpsCk1L1I4n+lA==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/middleware-recursion-detection@3.972.9': - resolution: {integrity: sha512-/Wt5+CT8dpTFQxEJ9iGy/UGrXr7p2wlIOEHvIr/YcHYByzoLjrqkYqXdJjd9UIgWjv7eqV2HnFJen93UTuwfTQ==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/middleware-user-agent@3.972.26': - resolution: {integrity: sha512-AilFIh4rI/2hKyyGN6XrB0yN96W2o7e7wyrPWCM6QjZM1mcC/pVkW3IWWRvuBWMpVP8Fg+rMpbzeLQ6dTM4gig==} - engines: {node: '>=20.0.0'} - '@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==} - engines: {node: '>=20.0.0'} - '@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/signature-v4-multi-region@3.996.35': resolution: {integrity: sha512-6L/VWs+Wch2stHemCGTmUNqKLMzURxQDK5boNG3Jn3kAOp71meDUuS5sbObpEvFxHDq0uWeSLFDNSYsjNt+Dlg==} 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.39': + resolution: {integrity: sha512-8+srXqYIF8KYMLC4FxMLEM5Ek7kUNibJu1R4m8/fUhhNYIZZz26oGtKkCr8I/HiG2fFQxBvaGgQZT4/mqRCSnA==} engines: {node: '>=20.0.0'} '@aws-sdk/token-providers@3.1048.0': @@ -1031,46 +1012,42 @@ packages: resolution: {integrity: sha512-pv80IzgGW4RnXWtft692chZOM9i6PhebVsLCcnaM4dBEPZva2fE6FXAHs76G7Rc7s3yGyX/68G0nZMrUy+Vmpg==} 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.973.6': - resolution: {integrity: sha512-Atfcy4E++beKtwJHiDln2Nby8W/mam64opFPTiHEqgsthqeydFS1pY+OUlN1ouNOmf8ArPU/6cDS65anOP3KQw==} + '@aws-sdk/token-providers@3.1083.0': + resolution: {integrity: sha512-s0woKnxuHrExLc5L2ArIH5BMkbonHPtt+5hSBM8oknp9M6QTuUmmAmJ2E0EdzCGONrO+8+ADPqvv6UX0nNcc7A==} 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.1085.0': + resolution: {integrity: sha512-+QxFprsPw7GTCuRN86OFhDYowgkoT/+Gn0L7feWY1kpIwUoOgzLi24esQ6PgG6pf3tHE/vs5qFmO7QcFMP82TQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/util-locate-window@3.965.4': - resolution: {integrity: sha512-H1onv5SkgPBK2P6JR2MjGgbOnttoNzSPIRoeZTNPZYyaplwGg50zS3amXvXqF0/qfXpWEC9rLWU564QTB9bSog==} + '@aws-sdk/types@3.973.13': + resolution: {integrity: sha512-pEHZqRkAlHfnfAU9tK+WpKv/gBNjGJrHMgA3A0iYRGyswBS2t0pfez+lWlwktb3Bqa0ovh7w/QJTFwp3fDxLNg==} 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/types@3.974.0': + resolution: {integrity: sha512-QIBrw90CDm4O0UaIIzkU6DrFdeJzEb2Va5EPEVpyldj6sHJxB6cshhStJuhZxk3wR3PmjJlYsjPmY1kNb+KGBg==} 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/util-locate-window@3.965.4': + resolution: {integrity: sha512-H1onv5SkgPBK2P6JR2MjGgbOnttoNzSPIRoeZTNPZYyaplwGg50zS3amXvXqF0/qfXpWEC9rLWU564QTB9bSog==} engines: {node: '>=20.0.0'} '@aws-sdk/xml-builder@3.972.31': resolution: {integrity: sha512-SzE4Pgyl+hDF+BuyuzxUSpwnuUu9lJuO1YGgteG89/4Qv0+2IQiVQqdbPV32IozLvXWQChPQcdkk/sKvb1QHiQ==} engines: {node: '>=20.0.0'} + '@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'} @@ -1153,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==} @@ -1209,22 +1188,22 @@ packages: resolution: {integrity: sha512-UyX6rGEXzVyPzb1yvjHtPfTlnLvB5jX/stAMdiytHhfoydX+98hfympdOwsnTktzr+IRvphxTbdErgYDJkEsvw==} engines: {node: '>=22.12.0'} - '@earendil-works/pi-agent-core@0.80.2': - resolution: {integrity: sha512-BF9WPhixIFjT6Kp3Iz3H6ugkU+4AWotM8py96XE5pIK0arJbQKMWbR+dXSWWDEmR5yc/aFQODnuyowOEzMGO7Q==} + '@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.2': - resolution: {integrity: sha512-5GNKfdrRJ4uZ5Zd9iudoXggi/BbUcKnD/xfRHtdR+7q4vWqPvfx8auFuaT+ewGBVI8K4wj87eigFQ/iCSuy9RQ==} + '@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.2': - resolution: {integrity: sha512-m9v7OUit0s9LklWfh61ca/XY5INjUzjtYtNZwy3cNvyjOLk3IpBgghP8aAp0iH35rLaiRwuuWiJ8t88ODMWY+A==} + '@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.2': - resolution: {integrity: sha512-OvOAMIbXiC9OSse17YMiXIsI9AS5XM/ZV8N/k+UzdlRpPILDQYmLElevgGW92kkXR8qHBClIdzhCjuzlBGvphA==} + '@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': @@ -1248,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'} @@ -1260,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'} @@ -1272,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'} @@ -1284,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'} @@ -1296,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'} @@ -1308,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'} @@ -1320,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'} @@ -1332,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'} @@ -1344,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'} @@ -1356,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'} @@ -1368,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'} @@ -1380,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'} @@ -1392,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'} @@ -1404,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'} @@ -1416,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'} @@ -1428,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'} @@ -1440,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'} @@ -1452,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'} @@ -1464,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'} @@ -1476,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'} @@ -1488,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'} @@ -1500,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'} @@ -1512,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'} @@ -1524,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'} @@ -1536,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'} @@ -1548,6 +1677,12 @@ packages: cpu: [x64] os: [win32] + '@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'} @@ -1569,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==} @@ -1587,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': @@ -2062,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==} @@ -2069,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'} @@ -2478,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] @@ -2922,108 +3065,60 @@ 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/core@3.26.0': resolution: {integrity: sha512-mLUktFAn+Pa2agl1J7VgtYNFWCX8/b4GMJSK1hCu4YCvtBfM6F8Os3EP4ry+DFFlXOf3wyvlgXhuUdFoy52D3g==} engines: {node: '>=18.0.0'} - '@smithy/credential-provider-imds@4.2.12': - resolution: {integrity: sha512-cr2lR792vNZcYMriSIj+Um3x9KWrjcu98kn234xA6reOAFMmbRpQMOv8KPgEmLLtx3eldU6c5wALKFqNOhugmg==} + '@smithy/core@3.29.3': + resolution: {integrity: sha512-L+Ys6ecjk5vwPMAKHBpPKlJ3DkqwNcnfEISXBZIsVvWG/XKXfsAP8mwIYlTeLcd2ElHdesPI8OuOmJSFAPhm6A==} engines: {node: '>=18.0.0'} '@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/fetch-http-handler@5.5.2': resolution: {integrity: sha512-Ei/UK/QMhq0rKaMqGPlOAkE2yS9DZeYmZdk1RAKc3vp3zxgleZHZyBLlZv8yLsxljX4svCRuMTD6u3LLIcU4Bg==} engines: {node: '>=18.0.0'} - '@smithy/hash-node@4.2.12': - resolution: {integrity: sha512-QhBYbGrbxTkZ43QoTPrK72DoYviDeg6YKDrHTMJbbC+A0sml3kSjzFtXP7BtbyJnXojLfTQldGdUR0RGD8dA3w==} - 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==} - engines: {node: '>=18.0.0'} - - '@smithy/middleware-content-length@4.2.12': - resolution: {integrity: sha512-YE58Yz+cvFInWI/wOTrB+DbvUVz/pLn5mC5MvOV4fdRUc6qGwygyngcucRQjAhiCEbmfLOXX0gntSIcgMvAjmA==} - engines: {node: '>=18.0.0'} - - '@smithy/middleware-endpoint@4.4.27': - resolution: {integrity: sha512-T3TFfUgXQlpcg+UdzcAISdZpj4Z+XECZ/cefgA6wLBd6V4lRi0svN2hBouN/be9dXQ31X4sLWz3fAQDf+nt6BA==} - engines: {node: '>=18.0.0'} - - '@smithy/middleware-retry@4.4.44': - resolution: {integrity: sha512-Y1Rav7m5CFRPQyM4CI0koD/bXjyjJu3EQxZZhtLGD88WIrBrQ7kqXM96ncd6rYnojwOo/u9MXu57JrEvu/nLrA==} - engines: {node: '>=18.0.0'} - - '@smithy/middleware-serde@4.2.15': - resolution: {integrity: sha512-ExYhcltZSli0pgAKOpQQe1DLFBLryeZ22605y/YS+mQpdNWekum9Ujb/jMKfJKgjtz1AZldtwA/wCYuKJgjjlg==} - engines: {node: '>=18.0.0'} - - '@smithy/middleware-stack@4.2.12': - resolution: {integrity: sha512-kruC5gRHwsCOuyCd4ouQxYjgRAym2uDlCvQ5acuMtRrcdfg7mFBg6blaxcJ09STpt3ziEkis6bhg1uwrWU7txw==} - 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/node-http-handler@4.7.3': resolution: {integrity: sha512-/jPhevcTFPMVl6KNjbaI47iOg1zxC7IsnX4PQDGVZKMFceOXtB8IEYaB7a9VvkP/3oC60WzTeKocvSI7vLT0vA==} engines: {node: '>=18.0.0'} @@ -3032,122 +3127,34 @@ packages: resolution: {integrity: sha512-wfl1uwrAqMH9/pi4kqBo5LBcFwrJLxuDLqL7p7qNcJIFcyZDUc6pzhYk4CYv+DP7fIUpQCZumwNnkhPKS52osQ==} 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==} + '@smithy/node-http-handler@4.9.5': + resolution: {integrity: sha512-bNqdxTQTxmLbomSmlkZFz8L6B/feQ2HHzw4L2zY7Ecp2XffYAZq2uzdWDdxJHJFbEvqd+SRuluJso0P8+xPdbw==} engines: {node: '>=18.0.0'} '@smithy/signature-v4@5.5.2': resolution: {integrity: sha512-7xHpmPY4rt0IOmeAA8EfjgEH8isT+587TCdy9H6a7d4OMi5CQ0oEHhWllunvPu4j4Cq0vTFwdxXN/kABWPjdyA==} 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==} + '@smithy/signature-v4@5.6.4': + resolution: {integrity: sha512-B89bpf2t/y/wia6LZ+4JfHXYQT9PnVftsH05rgJKKIStS7r/4XSs9HOjtPoLtgcA6HCW9jVqX5DBbq7E0PAkiQ==} engines: {node: '>=18.0.0'} '@smithy/types@4.15.0': resolution: {integrity: sha512-Z5TAOxygoFvybJV3igo5SloFflSokHx2hu1eFA+DxDTcn+FtKxUSui+rbTRG1pAafMA888Z3MVvCWUuvCrTXjg==} 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.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.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==} @@ -3363,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 @@ -3378,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: @@ -3400,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'} @@ -3468,12 +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@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==} @@ -3538,8 +3578,8 @@ packages: resolution: {integrity: sha512-trmleAnZ2PxN/loHWVhhx1qeOHSRXq4TDsBBxq3GqeJitfk3+jTQ+v/C1km/KYq9M7wKqCewMh+/NAvVH7m+bw==} engines: {node: '>=20.19.0'} - 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==} @@ -3566,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} @@ -3755,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==} @@ -3876,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: @@ -3945,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'} @@ -3963,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'} @@ -4017,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==} @@ -4083,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'} @@ -4193,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: @@ -4272,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'} @@ -4395,10 +4464,6 @@ packages: jackspeak@3.4.3: resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} - jiti@2.6.1: - resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} - hasBin: true - jiti@2.7.0: resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} hasBin: true @@ -4507,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==} @@ -4516,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==} @@ -4604,6 +4675,10 @@ packages: resolution: {integrity: sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==} hasBin: true + markdown-it@14.3.0: + resolution: {integrity: sha512-RCEsPjR+sr0x+AuYp601tKTkgFG4YEPLCzHST3cQ/fhlJkqAkz1L2/Qbp1j9qw5SBwQHFBoW8+hoN5xssOF0Tw==} + hasBin: true + marked@17.0.3: resolution: {integrity: sha512-jt1v2ObpyOKR8p4XaUJVk3YWRJ5n+i4+rjQopxvV32rSndTJXvIzuUdWWIy/1pFQMkQmvTXawzDNqOH/CUmx6A==} engines: {node: '>= 20'} @@ -4842,7 +4917,7 @@ packages: hasBin: true peerDependencies: ws: ^8.18.0 - zod: ^3.25 || ^4.0 + zod: 4.4.3 peerDependenciesMeta: ws: optional: true @@ -4854,7 +4929,7 @@ packages: hasBin: true peerDependencies: ws: ^8.18.0 - zod: ^3.25 || ^4.0 + zod: 4.4.3 peerDependenciesMeta: ws: optional: true @@ -4881,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==} @@ -4951,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'} @@ -5091,6 +5165,10 @@ packages: 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==} @@ -5272,6 +5350,11 @@ packages: 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'} @@ -5422,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'} @@ -5479,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'} @@ -5538,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'} @@ -5600,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 @@ -5831,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'} @@ -5886,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'} @@ -5925,25 +6065,22 @@ packages: zod-to-json-schema@3.25.1: resolution: {integrity: sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==} peerDependencies: - zod: ^3.25 || ^4 - - zod@3.25.75: - resolution: {integrity: sha512-OhpzAmVzabPOL6C3A3gpAifqr9MqihV/Msx3gor2b2kviCgcb+HM9SEOpMWwwNp9MRunWnhtAKUoo0AHhjyPPg==} + zod: 4.4.3 - 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.91.1(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 + zod: 4.4.3 '@aws-crypto/crc32@5.2.0': dependencies: @@ -5956,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 @@ -5964,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': @@ -5973,7 +6110,7 @@ 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 @@ -5990,69 +6127,20 @@ snapshots: '@aws-sdk/types': 3.973.13 '@smithy/core': 3.26.0 '@smithy/fetch-http-handler': 5.5.2 - '@smithy/node-http-handler': 4.7.3 + '@smithy/node-http-handler': 4.8.2 '@smithy/types': 4.15.0 tslib: 2.8.1 - '@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/core@3.974.23': @@ -6066,12 +6154,15 @@ snapshots: bowser: 2.14.1 tslib: 2.8.1 - '@aws-sdk/credential-provider-env@3.972.23': + '@aws-sdk/core@3.975.1': 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.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-env@3.972.49': @@ -6082,17 +6173,12 @@ snapshots: '@smithy/types': 4.15.0 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/credential-provider-env@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/credential-provider-http@3.972.51': @@ -6105,24 +6191,15 @@ snapshots: '@smithy/types': 4.15.0 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-http@3.972.59': + dependencies: + '@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-ini@3.972.56': dependencies: @@ -6140,18 +6217,21 @@ snapshots: '@smithy/types': 4.15.0 tslib: 2.8.1 - '@aws-sdk/credential-provider-login@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/protocol-http': 5.3.12 - '@smithy/shared-ini-file-loader': 4.4.7 - '@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 - transitivePeerDependencies: - - aws-crt '@aws-sdk/credential-provider-login@3.972.55': dependencies: @@ -6162,22 +6242,14 @@ snapshots: '@smithy/types': 4.15.0 tslib: 2.8.1 - '@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 + '@aws-sdk/credential-provider-login@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 - transitivePeerDependencies: - - aws-crt '@aws-sdk/credential-provider-node@3.972.58': dependencies: @@ -6193,13 +6265,18 @@ snapshots: '@smithy/types': 4.15.0 tslib: 2.8.1 - '@aws-sdk/credential-provider-process@3.972.23': - 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/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': @@ -6210,18 +6287,13 @@ snapshots: '@smithy/types': 4.15.0 tslib: 2.8.1 - '@aws-sdk/credential-provider-sso@3.972.26': + '@aws-sdk/credential-provider-process@3.972.57': 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/types': 4.16.1 tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt '@aws-sdk/credential-provider-sso@3.972.55': dependencies: @@ -6233,17 +6305,15 @@ snapshots: '@smithy/types': 4.15.0 tslib: 2.8.1 - '@aws-sdk/credential-provider-web-identity@3.972.26': + '@aws-sdk/credential-provider-sso@3.973.1': 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/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 - transitivePeerDependencies: - - aws-crt '@aws-sdk/credential-provider-web-identity@3.972.55': dependencies: @@ -6254,6 +6324,15 @@ snapshots: '@smithy/types': 4.15.0 tslib: 2.8.1 + '@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 @@ -6268,38 +6347,6 @@ snapshots: '@smithy/types': 4.15.0 tslib: 2.8.1 - '@aws-sdk/middleware-host-header@3.972.8': - dependencies: - '@aws-sdk/types': 3.973.6 - '@smithy/protocol-http': 5.3.12 - '@smithy/types': 4.13.1 - tslib: 2.8.1 - - '@aws-sdk/middleware-logger@3.972.8': - dependencies: - '@aws-sdk/types': 3.973.6 - '@smithy/types': 4.13.1 - tslib: 2.8.1 - - '@aws-sdk/middleware-recursion-detection@3.972.9': - 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 - tslib: 2.8.1 - - '@aws-sdk/middleware-user-agent@3.972.26': - 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 - tslib: 2.8.1 - '@aws-sdk/middleware-websocket@3.972.31': dependencies: '@aws-sdk/core': 3.974.23 @@ -6310,49 +6357,6 @@ snapshots: '@smithy/types': 4.15.0 tslib: 2.8.1 - '@aws-sdk/nested-clients@3.996.16': - dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.973.25 - '@aws-sdk/middleware-host-header': 3.972.8 - '@aws-sdk/middleware-logger': 3.972.8 - '@aws-sdk/middleware-recursion-detection': 3.972.9 - '@aws-sdk/middleware-user-agent': 3.972.26 - '@aws-sdk/region-config-resolver': 3.972.10 - '@aws-sdk/types': 3.973.6 - '@aws-sdk/util-endpoints': 3.996.5 - '@aws-sdk/util-user-agent-browser': 3.972.8 - '@aws-sdk/util-user-agent-node': 3.973.12 - '@smithy/config-resolver': 4.4.13 - '@smithy/core': 3.23.12 - '@smithy/fetch-http-handler': 5.3.15 - '@smithy/hash-node': 4.2.12 - '@smithy/invalid-dependency': 4.2.12 - '@smithy/middleware-content-length': 4.2.12 - '@smithy/middleware-endpoint': 4.4.27 - '@smithy/middleware-retry': 4.4.44 - '@smithy/middleware-serde': 4.2.15 - '@smithy/middleware-stack': 4.2.12 - '@smithy/node-config-provider': 4.3.12 - '@smithy/node-http-handler': 4.5.0 - '@smithy/protocol-http': 5.3.12 - '@smithy/smithy-client': 4.12.7 - '@smithy/types': 4.13.1 - '@smithy/url-parser': 4.2.12 - '@smithy/util-base64': 4.3.2 - '@smithy/util-body-length-browser': 4.2.2 - '@smithy/util-body-length-node': 4.2.3 - '@smithy/util-defaults-mode-browser': 4.3.43 - '@smithy/util-defaults-mode-node': 4.2.47 - '@smithy/util-endpoints': 3.3.3 - '@smithy/util-middleware': 4.2.12 - '@smithy/util-retry': 4.2.12 - '@smithy/util-utf8': 4.2.2 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - '@aws-sdk/nested-clients@3.997.23': dependencies: '@aws-crypto/sha256-browser': 5.2.0 @@ -6366,12 +6370,15 @@ snapshots: '@smithy/types': 4.15.0 tslib: 2.8.1 - '@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/signature-v4-multi-region@3.996.35': @@ -6381,17 +6388,12 @@ snapshots: '@smithy/types': 4.15.0 tslib: 2.8.1 - '@aws-sdk/token-providers@3.1019.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/token-providers@3.1048.0': dependencies: @@ -6411,57 +6413,52 @@ snapshots: '@smithy/types': 4.15.0 tslib: 2.8.1 - '@aws-sdk/types@3.973.13': - dependencies: - '@smithy/types': 4.15.0 - tslib: 2.8.1 - - '@aws-sdk/types@3.973.6': + '@aws-sdk/token-providers@3.1083.0': dependencies: - '@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-endpoints@3.996.5': + '@aws-sdk/token-providers@3.1085.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.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/types@3.973.13': dependencies: + '@smithy/types': 4.15.0 tslib: 2.8.1 - '@aws-sdk/util-user-agent-browser@3.972.8': + '@aws-sdk/types@3.974.0': dependencies: - '@aws-sdk/types': 3.973.6 - '@smithy/types': 4.13.1 - bowser: 2.14.1 + '@smithy/types': 4.16.1 tslib: 2.8.1 - '@aws-sdk/util-user-agent-node@3.973.12': + '@aws-sdk/util-locate-window@3.965.4': 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 tslib: 2.8.1 - '@aws-sdk/xml-builder@3.972.16': + '@aws-sdk/xml-builder@3.972.31': dependencies: - '@smithy/types': 4.13.1 - fast-xml-parser: 5.5.9 + '@smithy/types': 4.15.0 tslib: 2.8.1 - '@aws-sdk/xml-builder@3.972.31': + '@aws-sdk/xml-builder@3.972.34': dependencies: - '@smithy/types': 4.15.0 + '@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 @@ -6569,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': @@ -6621,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 @@ -6638,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: @@ -6668,7 +6666,7 @@ snapshots: discord-api-types: 0.38.40 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 @@ -6677,9 +6675,9 @@ snapshots: - opusscript - utf-8-validate - '@earendil-works/pi-agent-core@0.80.2(ws@8.19.0)(zod@4.3.6)': + '@earendil-works/pi-agent-core@0.80.6(ws@8.21.0)(zod@4.4.3)': dependencies: - '@earendil-works/pi-ai': 0.80.2(ws@8.19.0)(zod@4.3.6) + '@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 @@ -6691,9 +6689,9 @@ snapshots: - ws - zod - '@earendil-works/pi-ai@0.80.2(ws@8.19.0)(zod@4.3.6)': + '@earendil-works/pi-ai@0.80.6(ws@8.21.0)(zod@4.4.3)': dependencies: - '@anthropic-ai/sdk': 0.91.1(zod@4.3.6) + '@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) @@ -6701,7 +6699,7 @@ snapshots: '@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.19.0)(zod@4.3.6) + openai: 6.26.0(ws@8.21.0)(zod@4.4.3) partial-json: 0.1.7 typebox: 1.1.38 transitivePeerDependencies: @@ -6712,11 +6710,11 @@ snapshots: - ws - zod - '@earendil-works/pi-coding-agent@0.80.2(ws@8.19.0)(zod@4.3.6)': + '@earendil-works/pi-coding-agent@0.80.6(ws@8.21.0)(zod@4.4.3)': dependencies: - '@earendil-works/pi-agent-core': 0.80.2(ws@8.19.0)(zod@4.3.6) - '@earendil-works/pi-ai': 0.80.2(ws@8.19.0)(zod@4.3.6) - '@earendil-works/pi-tui': 0.80.2 + '@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 @@ -6742,7 +6740,7 @@ snapshots: - ws - zod - '@earendil-works/pi-tui@0.80.2': + '@earendil-works/pi-tui@0.80.6': dependencies: get-east-asian-width: 1.6.0 marked: 18.0.5 @@ -6769,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 + '@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: @@ -6960,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 @@ -7177,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 @@ -7193,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': {} @@ -7288,7 +7364,7 @@ snapshots: dependencies: debug: 4.4.3 uuid: 11.1.0 - zod: 3.25.75 + zod: 4.4.3 transitivePeerDependencies: - supports-color @@ -7301,7 +7377,7 @@ 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 @@ -7309,9 +7385,9 @@ snapshots: '@mistralai/mistralai@2.2.6(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/semantic-conventions': 1.41.1 - ws: 8.19.0 - zod: 4.3.6 - zod-to-json-schema: 3.25.1(zod@4.3.6) + 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: @@ -7380,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 @@ -7750,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': {} @@ -8027,13 +8109,13 @@ snapshots: '@sinclair/typebox@0.34.48': {} - '@slack/bolt@4.6.0(@types/express@5.0.6)': + '@slack/bolt@4.7.3(@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 + '@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 @@ -8046,42 +8128,44 @@ snapshots: - supports-color - utf-8-validate - '@slack/logger@4.0.0': + '@slack/logger@4.0.1': dependencies: '@types/node': 25.3.0 - '@slack/oauth@3.0.4': + '@slack/oauth@3.0.5': dependencies: - '@slack/logger': 4.0.0 - '@slack/web-api': 7.14.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 - '@slack/socket-mode@2.0.5': + '@slack/socket-mode@2.0.7': dependencies: - '@slack/logger': 4.0.0 - '@slack/web-api': 7.14.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.19.0 + ws: 8.21.0 transitivePeerDependencies: - bufferutil - debug + - supports-color - utf-8-validate - '@slack/types@2.20.0': {} + '@slack/types@2.22.0': {} - '@slack/web-api@7.14.1': + '@slack/web-api@7.19.0': dependencies: - '@slack/logger': 4.0.0 - '@slack/types': 2.20.0 + '@slack/logger': 4.0.1 + '@slack/types': 2.22.0 '@types/node': 25.3.0 '@types/retry': 0.12.0 - axios: 1.13.5 + axios: 1.18.1 eventemitter3: 5.0.4 form-data: 2.5.4 is-electron: 2.2.2 @@ -8091,33 +8175,7 @@ snapshots: 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 + - supports-color '@smithy/core@3.26.0': dependencies: @@ -8125,12 +8183,9 @@ snapshots: '@smithy/types': 4.15.0 tslib: 2.8.1 - '@smithy/credential-provider-imds@4.2.12': + '@smithy/core@3.29.3': 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 + '@smithy/types': 4.16.1 tslib: 2.8.1 '@smithy/credential-provider-imds@4.4.2': @@ -8139,12 +8194,10 @@ snapshots: '@smithy/types': 4.15.0 tslib: 2.8.1 - '@smithy/fetch-http-handler@5.3.15': + '@smithy/credential-provider-imds@4.4.8': dependencies: - '@smithy/protocol-http': 5.3.12 - '@smithy/querystring-builder': 4.2.12 - '@smithy/types': 4.13.1 - '@smithy/util-base64': 4.3.2 + '@smithy/core': 3.29.3 + '@smithy/types': 4.16.1 tslib: 2.8.1 '@smithy/fetch-http-handler@5.5.2': @@ -8153,82 +8206,16 @@ snapshots: '@smithy/types': 4.15.0 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': + '@smithy/fetch-http-handler@5.6.5': dependencies: - '@smithy/types': 4.13.1 + '@smithy/core': 3.29.3 + '@smithy/types': 4.16.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/node-http-handler@4.7.3': dependencies: '@smithy/core': 3.26.0 @@ -8241,45 +8228,10 @@ snapshots: '@smithy/types': 4.15.0 tslib: 2.8.1 - '@smithy/property-provider@4.2.12': - dependencies: - '@smithy/types': 4.13.1 - tslib: 2.8.1 - - '@smithy/protocol-http@5.3.12': - dependencies: - '@smithy/types': 4.13.1 - tslib: 2.8.1 - - '@smithy/querystring-builder@4.2.12': - dependencies: - '@smithy/types': 4.13.1 - '@smithy/util-uri-escape': 4.2.2 - tslib: 2.8.1 - - '@smithy/querystring-parser@4.2.12': - dependencies: - '@smithy/types': 4.13.1 - tslib: 2.8.1 - - '@smithy/service-error-classification@4.2.12': - dependencies: - '@smithy/types': 4.13.1 - - '@smithy/shared-ini-file-loader@4.4.7': - dependencies: - '@smithy/types': 4.13.1 - tslib: 2.8.1 - - '@smithy/signature-v4@5.3.12': + '@smithy/node-http-handler@4.9.5': 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 + '@smithy/core': 3.29.3 + '@smithy/types': 4.16.1 tslib: 2.8.1 '@smithy/signature-v4@5.5.2': @@ -8288,41 +8240,17 @@ snapshots: '@smithy/types': 4.15.0 tslib: 2.8.1 - '@smithy/smithy-client@4.12.7': - 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 - - '@smithy/types@4.13.1': + '@smithy/signature-v4@5.6.4': dependencies: + '@smithy/core': 3.29.3 + '@smithy/types': 4.16.1 tslib: 2.8.1 '@smithy/types@4.15.0': dependencies: tslib: 2.8.1 - '@smithy/url-parser@4.2.12': - dependencies: - '@smithy/querystring-parser': 4.2.12 - '@smithy/types': 4.13.1 - tslib: 2.8.1 - - '@smithy/util-base64@4.3.2': - dependencies: - '@smithy/util-buffer-from': 4.2.2 - '@smithy/util-utf8': 4.2.2 - tslib: 2.8.1 - - '@smithy/util-body-length-browser@4.2.2': - dependencies: - tslib: 2.8.1 - - '@smithy/util-body-length-node@4.2.3': + '@smithy/types@4.16.1': dependencies: tslib: 2.8.1 @@ -8331,82 +8259,11 @@ snapshots: '@smithy/is-array-buffer': 2.2.0 tslib: 2.8.1 - '@smithy/util-buffer-from@4.2.2': - dependencies: - '@smithy/is-array-buffer': 4.2.2 - tslib: 2.8.1 - - '@smithy/util-config-provider@4.2.2': - dependencies: - tslib: 2.8.1 - - '@smithy/util-defaults-mode-browser@4.3.43': - dependencies: - '@smithy/property-provider': 4.2.12 - '@smithy/smithy-client': 4.12.7 - '@smithy/types': 4.13.1 - tslib: 2.8.1 - - '@smithy/util-defaults-mode-node@4.2.47': - 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': - dependencies: - '@smithy/node-config-provider': 4.3.12 - '@smithy/types': 4.13.1 - tslib: 2.8.1 - - '@smithy/util-hex-encoding@4.2.2': - dependencies: - tslib: 2.8.1 - - '@smithy/util-middleware@4.2.12': - dependencies: - '@smithy/types': 4.13.1 - tslib: 2.8.1 - - '@smithy/util-retry@4.2.12': - dependencies: - '@smithy/service-error-classification': 4.2.12 - '@smithy/types': 4.13.1 - tslib: 2.8.1 - - '@smithy/util-stream@4.5.20': - dependencies: - '@smithy/fetch-http-handler': 5.3.15 - '@smithy/node-http-handler': 4.5.0 - '@smithy/types': 4.13.1 - '@smithy/util-base64': 4.3.2 - '@smithy/util-buffer-from': 4.2.2 - '@smithy/util-hex-encoding': 4.2.2 - '@smithy/util-utf8': 4.2.2 - tslib: 2.8.1 - - '@smithy/util-uri-escape@4.2.2': - dependencies: - tslib: 2.8.1 - '@smithy/util-utf8@2.3.0': dependencies: '@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': @@ -8683,83 +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)': - 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)) - 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) - transitivePeerDependencies: - - bufferutil - - msw - - utf-8-validate - - vite - optional: true - - '@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.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.7.0)(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.7.0)(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.7.0)(tsx@4.21.0)(yaml@2.8.3) - transitivePeerDependencies: - - bufferutil - - msw - - utf-8-validate - - vite - - '@vitest/browser@4.0.18(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.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/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) transitivePeerDependencies: - bufferutil - msw - utf-8-validate - vite - optional: true - '@vitest/browser@4.0.18(vite@7.3.1(@types/node@25.3.0)(jiti@2.7.0)(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.7.0)(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.7.0)(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: @@ -8778,29 +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.7.0)(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.7.0)(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.0.18(vite@7.3.1(@types/node@25.3.0)(jiti@2.7.0)(tsx@4.21.0)(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.0.18 + '@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.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/pretty-format@3.2.4': dependencies: @@ -8810,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 @@ -8821,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 @@ -8833,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 @@ -8850,6 +8700,12 @@ 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 @@ -8862,7 +8718,7 @@ snapshots: pino: 9.14.0 protobufjs: 7.5.4 sharp: 0.34.5 - ws: 8.19.0 + ws: 8.21.0 transitivePeerDependencies: - bufferutil - supports-color @@ -8888,9 +8744,15 @@ snapshots: acorn@8.16.0: {} + agent-base@6.0.2: + dependencies: + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + 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 @@ -8949,7 +8811,7 @@ snapshots: estree-walker: 3.0.3 pathe: 2.0.3 - 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 @@ -8981,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: {} @@ -9147,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 @@ -9191,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: {} @@ -9281,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: {} @@ -9326,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 @@ -9395,6 +9271,35 @@ 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: {} @@ -9498,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: @@ -9573,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 @@ -9700,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 @@ -9794,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 @@ -9924,8 +9838,6 @@ snapshots: optionalDependencies: '@pkgjs/parseargs': 0.11.0 - jiti@2.6.1: {} - jiti@2.7.0: {} jose@4.15.9: {} @@ -9974,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: @@ -10046,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 @@ -10062,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: {} @@ -10136,7 +10058,7 @@ snapshots: make-dir@4.0.0: dependencies: - semver: 7.7.4 + semver: 7.8.5 markdown-it@14.1.1: dependencies: @@ -10147,6 +10069,15 @@ snapshots: punycode.js: 2.3.1 uc.micro: 2.1.0 + 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: {} @@ -10262,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 @@ -10301,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 @@ -10376,15 +10307,15 @@ snapshots: dependencies: mimic-function: 5.0.1 - openai@6.25.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.26.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: {} @@ -10434,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: {} @@ -10507,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: @@ -10612,14 +10541,14 @@ snapshots: 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 @@ -10659,6 +10588,8 @@ snapshots: proxy-from-env@1.1.0: {} + proxy-from-env@2.1.0: {} + psl@1.15.0: dependencies: punycode: 2.3.1 @@ -10878,6 +10809,8 @@ snapshots: semver@7.8.0: {} + semver@7.8.5: {} + send@0.19.2: dependencies: debug: 2.6.9 @@ -10938,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 @@ -11095,6 +11028,8 @@ snapshots: std-env@3.10.0: {} + std-env@4.2.0: {} + stdin-discarder@0.3.1: {} stdout-update@4.0.1: @@ -11157,8 +11092,6 @@ snapshots: dependencies: js-tokens: 9.0.1 - strnum@2.2.2: {} - strtok3@10.3.4: dependencies: '@tokenizer/token': 0.3.0 @@ -11211,6 +11144,8 @@ snapshots: tinyrainbow@3.0.3: {} + tinyrainbow@3.1.0: {} + tinyspy@4.0.4: {} to-regex-range@5.0.1: @@ -11252,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 @@ -11269,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 @@ -11359,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.7.0)(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.7.0)(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 @@ -11380,22 +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): - dependencies: - esbuild: 0.27.3 - fdir: 6.5.0(picomatch@4.0.4) - picomatch: 4.0.4 - postcss: 8.5.6 - rollup: 4.59.0 - tinyglobby: 0.2.15 - optionalDependencies: - '@types/node': 25.3.0 - fsevents: 2.3.3 - 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.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) @@ -11407,14 +11326,14 @@ snapshots: '@types/node': 25.3.0 fsevents: 2.3.3 jiti: 2.7.0 - tsx: 4.21.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.7.0)(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.7.0)(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 @@ -11432,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.7.0)(tsx@4.21.0)(yaml@2.8.3) - vite-node: 3.2.4(@types/node@25.3.0)(jiti@2.7.0)(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 @@ -11452,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 @@ -11472,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 @@ -11491,44 +11409,34 @@ 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.7.0)(tsx@4.21.0)(yaml@2.8.3): + 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.0.18 - '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.3.0)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.8.3)) - '@vitest/pretty-format': 4.0.18 - '@vitest/runner': 4.0.18 - '@vitest/snapshot': 4.0.18 - '@vitest/spy': 4.0.18 - '@vitest/utils': 4.0.18 - es-module-lexer: 1.7.0 + '@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: 3.10.0 + std-env: 4.2.0 tinybench: 2.9.0 tinyexec: 1.0.2 tinyglobby: 0.2.15 - tinyrainbow: 3.0.3 - vite: 7.3.1(@types/node@25.3.0)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.8.3) + 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/browser-playwright': 4.0.18(playwright@1.58.2)(vite@7.3.1(@types/node@25.3.0)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.0.18) + '@vitest/coverage-v8': 4.1.10(vitest@4.1.10) transitivePeerDependencies: - - jiti - - less - - lightningcss - msw - - sass - - sass-embedded - - stylus - - sugarss - - terser - - tsx - - yaml web-streams-polyfill@3.3.3: {} @@ -11572,6 +11480,8 @@ snapshots: ws@8.19.0: {} + ws@8.21.0: {} + y18n@5.0.8: {} yallist@4.0.0: {} @@ -11606,10 +11516,8 @@ snapshots: yoctocolors@2.1.2: {} - zod-to-json-schema@3.25.1(zod@4.3.6): + zod-to-json-schema@3.25.1(zod@4.4.3): dependencies: - zod: 4.3.6 - - zod@3.25.75: {} + zod: 4.4.3 - zod@4.3.6: {} + zod@4.4.3: {} 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"); From bb5e96624096af00e793a37718d6143485816a55 Mon Sep 17 00:00:00 2001 From: ApiliumDevTeam Date: Mon, 13 Jul 2026 19:50:26 +0200 Subject: [PATCH 10/22] Terminal UI: accessible-mode parity, robustness, and visual consistency (#67) * fix(tui): resolve readline temporal-dead-zone in accessible mode The accessible TUI created its readline interface after wiring the gateway client's disconnect handler and after client.start(). If the client disconnected in that window, the handler referenced the readline binding before its initialization, throwing a ReferenceError. Move the readline creation before the disconnect wiring so the reference is always initialized. * feat(tui): make Ctrl+C abort the active run Ctrl+C now follows a three-state priority: abort an active run, clear non-empty input, or exit on a second press within one second. Escape remains a secondary abort path. This unblocks users of modal editors (where Escape is consumed to switch modes) and aligns the interrupt behavior with terminal conventions. * feat(tui): share slash-command handling between graphical and accessible modes The accessible TUI previously supported only /quit and /exit. Introduce a renderer-agnostic shared command core (shared-commands.ts) that calls the gateway client directly and reports results through an injected text sink. The accessible TUI now routes /abort, /new, /reset, /compact, /status, /sessions, /session , /model, and /help through this core, reaching feature parity with the graphical TUI for every command that does not require a visual picker. The graphical TUI continues to use its full handler for overlay-based commands. Both TUIs share logic (the gateway calls and command parsing) without sharing presentation. * fix(tui): render diffs with theme colors The diff renderer hardcoded chalk.green/red/cyan/white, so added and removed lines were hard to read under light or high-contrast themes. Thread optional DiffColors through renderDiff and formatDiffStatsLine; when the TUI calls them it passes the active theme's success/error/ accent/dim functions, keeping diff colors consistent across every theme preset. Callers without a theme fall back to the previous defaults. * refactor(terminal): align CLI palette with the TUI dark palette The CLI and the terminal UI used different accent colors (orange in the CLI, gold in the TUI), so running `mayros` then `mayros code` showed two different identities. Derive the CLI palette tokens from the TUI dark palette so both surfaces share accent, success, warn, and error values. The CLI's accentBright maps to the TUI accentSoft; accentDim is a darkened accent; info reuses accentSoft. The visual identity is now consistent across every Mayros surface. * feat(tui): enable extended keyboard protocols where supported Add term-capabilities.ts to detect and manage extended keyboard protocols. Capable terminals (kitty, WezTerm, ghostty get the kitty keyboard protocol; iTerm, vscode, tmux, windows-terminal, Apple Terminal get xterm modifyOtherKeys level 2) are upgraded at TUI startup so modifier combinations like Shift+Enter and Ctrl+Shift survive instead of being collapsed by plain raw-mode stdin. The protocol is popped on exit/SIGINT/SIGTERM so the terminal is restored for the shell. Detection is conservative: terminals outside the allow-list get no upgrade, so escape sequences are never emitted to a terminal that cannot parse them back. * chore(tui): remove unused input-history store The input-history store was never wired into the TUI main loop; it was dead code that looked like a feature. Remove it and its test to reduce confusion. Cross-session input history can be reintroduced as a real feature later, but shipping a dormant implementation invites false confidence. * test(tui): align session rename test with the new API and skip symlink on Windows The session rename API field changed from displayName to label in the 0.80 surface; update the assertion to match the implementation. The symlink-follows test requires Developer Mode or admin rights to create a symlink on Windows (EPERM otherwise), so skip it on win32; CI on Linux exercises it normally. --- src/terminal/palette.ts | 20 +-- src/tui/a11y-tui.ts | 50 +++++-- src/tui/components/tool-execution.ts | 13 +- src/tui/diff-renderer.ts | 47 +++++-- src/tui/file-mention.test.ts | 6 +- src/tui/input-history-store.test.ts | 48 ------- src/tui/input-history-store.ts | 47 ------- src/tui/session-manager.test.ts | 2 +- src/tui/shared-commands.test.ts | 159 ++++++++++++++++++++++ src/tui/shared-commands.ts | 188 +++++++++++++++++++++++++++ src/tui/term-capabilities.test.ts | 86 ++++++++++++ src/tui/term-capabilities.ts | 122 +++++++++++++++++ src/tui/tui-command-handlers.ts | 9 +- src/tui/tui.ts | 19 ++- 14 files changed, 689 insertions(+), 127 deletions(-) delete mode 100644 src/tui/input-history-store.test.ts delete mode 100644 src/tui/input-history-store.ts create mode 100644 src/tui/shared-commands.test.ts create mode 100644 src/tui/shared-commands.ts create mode 100644 src/tui/term-capabilities.test.ts create mode 100644 src/tui/term-capabilities.ts 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/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/components/tool-execution.ts b/src/tui/components/tool-execution.ts index 931af40f..80f71059 100644 --- a/src/tui/components/tool-execution.ts +++ b/src/tui/components/tool-execution.ts @@ -133,9 +133,18 @@ export class ToolExecutionComponent extends Container { const isDiff = DIFF_TOOLS.has(this.toolName) && raw && !this.isPartial; 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 statsLine = formatDiffStatsLine(stats, { + add: theme.success, + del: theme.error, + }); const maxLines = this.expanded ? Infinity : DIFF_PREVIEW_LINES; const display = colored.length > maxLines 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/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/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/term-capabilities.test.ts b/src/tui/term-capabilities.test.ts new file mode 100644 index 00000000..d75d3dcb --- /dev/null +++ b/src/tui/term-capabilities.test.ts @@ -0,0 +1,86 @@ +import { describe, expect, it } from "vitest"; +import { detectKeyboardProtocol, enableKeyboardProtocol } 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[ 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"; + +/** 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. + } + } + }, + }; +} diff --git a/src/tui/tui-command-handlers.ts b/src/tui/tui-command-handlers.ts index 3e23a5ca..b67b5091 100644 --- a/src/tui/tui-command-handlers.ts +++ b/src/tui/tui-command-handlers.ts @@ -24,6 +24,7 @@ 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"; @@ -679,7 +680,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) { diff --git a/src/tui/tui.ts b/src/tui/tui.ts index b6618769..872eb8e5 100644 --- a/src/tui/tui.ts +++ b/src/tui/tui.ts @@ -26,6 +26,7 @@ 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 { enableKeyboardProtocol } from "./term-capabilities.js"; import type { ThemePreset } from "./theme/palettes.js"; import { THEME_PRESETS } from "./theme/palettes.js"; import { editorTheme, theme, setThemePreset } from "./theme/theme.js"; @@ -904,6 +905,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"); @@ -1030,10 +1040,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); From 4e7527dd59df5a50d5a6d0df6b0b9a31109970a8 Mon Sep 17 00:00:00 2001 From: ApiliumDevTeam Date: Tue, 14 Jul 2026 00:10:07 +0200 Subject: [PATCH 11/22] Terminal UI: streaming performance, auto-theme, and per-section visibility (#69) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(tui): auto-detect dark/light theme from the terminal When config.ui.theme is unset or set to "auto", query the terminal for its color-scheme preference before the first render. The detection uses two concurrent queries with a 150 ms timeout each: the DSR color-scheme report (CSI ? 996 n, a direct dark/light answer) and the OSC 11 background-color query (parsed to RGB and classified via Rec. 709 relative luminance). The fastest signal wins; an unresponsive terminal falls back to the default dark preset without blocking startup. Add "auto" to the ui.theme config type so users can opt in explicitly, and document the query ladder in term-capabilities.ts. The manual /theme command continues to override the detected preset for the session. * test(tui): cover paste-collapse and grapheme caret behavior Add no-regression coverage for the two input features inherited from the terminal UI dependency: grapheme-aware caret movement (Intl.Segmenter) and paste-collapse markers. Both are implemented in the Editor class the TUI extends, so the tests pin the environment contract rather than the Editor internals. The grapheme tests verify that emoji with skin-tone modifiers, flag emoji, and CJK characters each count as a single grapheme — the invariant the caret relies on. The paste-marker tests verify the marker shape ([paste #N +M lines], [paste #N M chars], [paste #N]) so that cursor movement and submission expansion keep working if the format changes upstream. Also update the welcome-screen quick-start hint to reflect that Ctrl+C now aborts the active run (Esc remains a secondary path). * perf(tui): split streaming markdown at the stable block boundary Re-parsing the full markdown on every streaming delta is O(n²) over a response. Split the assistant message text at the last stable block boundary (a blank line outside any open code fence) into a stable prefix and an active suffix. Only the suffix is re-parsed per delta; the prefix reuses the Markdown component's internal render cache, which turns the per-delta cost into O(n). Add stream-boundary.ts with a pure, unit-tested findStableBoundary function that walks backwards through blank lines and skips any that sit inside an open code fence. Refactor AssistantMessageComponent to hold two Markdown instances (prefix + suffix) and only update the prefix when its text actually grows. * feat(tui): per-section detail visibility for tools and thinking Replace the binary expanded/collapsed toggle with a three-state model: hidden, collapsed, and expanded. Each section cycles independently via its keyboard shortcut. - Tools (Ctrl+O): hidden renders only the header line, collapsed shows a 12-line preview, expanded shows the full output. - Thinking (Ctrl+T): hidden suppresses thinking text, collapsed and expanded both show it (a truncated thinking mode is deferred to a follow-up that restructures the composer). The section state is the new source of truth in TuiStateAccess; the existing toolsExpanded and showThinking fields are kept as compatibility shims. Add a nextSectionState helper and unit tests for the cycle. --- src/config/types.mayros.ts | 4 +- src/tui/components/assistant-message.ts | 39 ++++++++++-- src/tui/components/chat-log.ts | 8 +++ src/tui/components/tool-execution.ts | 26 ++++++-- src/tui/components/welcome-screen.ts | 2 +- src/tui/paste-grapheme.test.ts | 82 +++++++++++++++++++++++++ src/tui/section-visibility.test.ts | 28 +++++++++ src/tui/stream-boundary.test.ts | 69 +++++++++++++++++++++ src/tui/stream-boundary.ts | 64 +++++++++++++++++++ src/tui/term-capabilities.test.ts | 67 +++++++++++++++++++- src/tui/term-capabilities.ts | 46 ++++++++++++++ src/tui/tui-event-handlers.test.ts | 2 + src/tui/tui-session-actions.test.ts | 2 + src/tui/tui-types.ts | 21 +++++++ src/tui/tui.ts | 43 +++++++++++-- 15 files changed, 484 insertions(+), 19 deletions(-) create mode 100644 src/tui/paste-grapheme.test.ts create mode 100644 src/tui/section-visibility.test.ts create mode 100644 src/tui/stream-boundary.test.ts create mode 100644 src/tui/stream-boundary.ts diff --git a/src/config/types.mayros.ts b/src/config/types.mayros.ts index 8c8bdc4e..ec33a448 100644 --- a/src/config/types.mayros.ts +++ b/src/config/types.mayros.ts @@ -68,8 +68,8 @@ export type MayrosConfig = { ui?: { /** Accent color for Mayros UI chrome (hex). */ seamColor?: string; - /** TUI color theme preset. */ - theme?: "dark" | "light" | "high-contrast"; + /** TUI color theme preset, or "auto" to detect from the terminal. */ + theme?: "auto" | "dark" | "light" | "high-contrast"; /** Enable vim editing mode in the TUI. */ vim?: boolean; /** Custom keybindings for TUI actions. */ diff --git a/src/tui/components/assistant-message.ts b/src/tui/components/assistant-message.ts index 2d9c3c84..dfe79712 100644 --- a/src/tui/components/assistant-message.ts +++ b/src/tui/components/assistant-message.ts @@ -1,21 +1,50 @@ import { Container, Markdown, Spacer } from "@earendil-works/pi-tui"; +import { findStableBoundary } from "../stream-boundary.js"; import { markdownTheme, theme } from "../theme/theme.js"; +/** + * Renders an assistant message with incremental markdown parsing. + * + * The text is split at the last stable block boundary (see findStableBoundary) + * into a stable prefix and an active suffix. Only the suffix is re-parsed on + * each streaming delta; the prefix reuses the Markdown component's internal + * render cache, turning the per-delta cost from O(n²) into O(n). + */ export class AssistantMessageComponent extends Container { - private body: Markdown; + private stablePrefix: Markdown; + private activeSuffix: Markdown; + private committedPrefixText = ""; constructor(text: string) { super(); - this.body = new Markdown(text, 1, 0, markdownTheme, { + this.addChild(new Spacer(1)); + this.stablePrefix = this.createMarkdown(""); + this.activeSuffix = this.createMarkdown(""); + this.addChild(this.stablePrefix); + this.addChild(this.activeSuffix); + if (text) this.setText(text); + } + + private createMarkdown(text: string): Markdown { + return new Markdown(text, 1, 0, markdownTheme, { // Keep assistant body text in terminal default foreground so contrast // follows the user's terminal theme (dark or light). color: (line) => 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 41d142e1..18187496 100644 --- a/src/tui/components/chat-log.ts +++ b/src/tui/components/chat-log.ts @@ -1,5 +1,6 @@ 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/tool-execution.ts b/src/tui/components/tool-execution.ts index 80f71059..df48c41e 100644 --- a/src/tui/components/tool-execution.ts +++ b/src/tui/components/tool-execution.ts @@ -64,7 +64,7 @@ export class ToolExecutionComponent extends 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; @@ -92,10 +92,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,6 +142,14 @@ 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(""); + return; + } + + const isExpanded = this.sectionState === "expanded"; + if (isDiff) { const colored = renderDiff(raw, { add: theme.success, @@ -145,7 +163,7 @@ export class ToolExecutionComponent extends Container { add: theme.success, del: theme.error, }); - const maxLines = this.expanded ? Infinity : DIFF_PREVIEW_LINES; + const maxLines = isExpanded ? Infinity : DIFF_PREVIEW_LINES; const display = colored.length > maxLines ? [...colored.slice(0, maxLines), "…", statsLine] @@ -157,7 +175,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/welcome-screen.ts b/src/tui/components/welcome-screen.ts index fe692b50..88cc23ce 100644 --- a/src/tui/components/welcome-screen.ts +++ b/src/tui/components/welcome-screen.ts @@ -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/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/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 index d75d3dcb..8cff30ca 100644 --- a/src/tui/term-capabilities.test.ts +++ b/src/tui/term-capabilities.test.ts @@ -1,5 +1,11 @@ -import { describe, expect, it } from "vitest"; -import { detectKeyboardProtocol, enableKeyboardProtocol } from "./term-capabilities.js"; +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", () => { @@ -84,3 +90,60 @@ describe("enableKeyboardProtocol", () => { } }); }); + +describe("relativeLuminance", () => { + 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 index 3d2f2c3b..6234acf4 100644 --- a/src/tui/term-capabilities.ts +++ b/src/tui/term-capabilities.ts @@ -17,6 +17,14 @@ 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", @@ -120,3 +128,41 @@ export function enableKeyboardProtocol( }, }; } + +// --------------------------------------------------------------------------- +// 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/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-session-actions.test.ts b/src/tui/tui-session-actions.test.ts index b28cf92a..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", 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 872eb8e5..ca5e3eb6 100644 --- a/src/tui/tui.ts +++ b/src/tui/tui.ts @@ -27,6 +27,7 @@ import { CustomEditor } from "./components/custom-editor.js"; import { WelcomeScreen } from "./components/welcome-screen.js"; import { GatewayChatClient } from "./gateway-chat.js"; import { enableKeyboardProtocol } from "./term-capabilities.js"; +import { detectTerminalColorScheme } from "./term-capabilities.js"; import type { ThemePreset } from "./theme/palettes.js"; import { THEME_PRESETS } from "./theme/palettes.js"; import { editorTheme, theme, setThemePreset } from "./theme/theme.js"; @@ -41,11 +42,13 @@ 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"; @@ -280,7 +283,10 @@ export async function runTui(opts: TuiOptions) { } const configTheme = config.ui?.theme; - if (configTheme && THEME_PRESETS.includes(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 ThemePreset)) { setThemePreset(configTheme as ThemePreset); } const keybindingsConfig = config.ui?.keybindings; @@ -302,6 +308,8 @@ 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; @@ -421,6 +429,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; }, @@ -518,6 +538,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); @@ -937,9 +964,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 = () => { @@ -952,7 +980,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 = () => { From ea1b5b4c99fdf7d5020e6eec9c31a978be338da7 Mon Sep 17 00:00:00 2001 From: ApiliumDevTeam Date: Tue, 14 Jul 2026 05:30:04 +0200 Subject: [PATCH 12/22] feat(tui): disable vim mode and remove dead code (#70) The vim mode implementation was structurally broken: the editor bridge that connects vim motions to the buffer was never wired, so every editing operation silently no-op'd while the mode indicator claimed vim was active. Keys were consumed without editing, leaving users with a non-functional editor. Disable vim honestly instead of leaving the broken experience: /vim now reports that the feature is unavailable, the dead handler and tests are removed (730 LOC), all vim wiring is stripped from the TUI, the config flag is deprecated, and docs are updated. Emacs/readline-style keybindings remain available. --- README.md | 19 +- docs/platforms/tui-redesign.md | 2 +- src/config/types.mayros.ts | 2 +- src/tui/commands.ts | 2 +- src/tui/components/custom-editor.ts | 12 - src/tui/keybinding-resolver.test.ts | 5 +- src/tui/keybinding-resolver.ts | 4 +- src/tui/tui-command-handlers.ts | 4 +- src/tui/tui.ts | 16 +- src/tui/vim-handler.test.ts | 302 -------------------- src/tui/vim-handler.ts | 428 ---------------------------- 11 files changed, 17 insertions(+), 779 deletions(-) delete mode 100644 src/tui/vim-handler.test.ts delete mode 100644 src/tui/vim-handler.ts 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/docs/platforms/tui-redesign.md b/docs/platforms/tui-redesign.md index edc609c2..e98a9834 100644 --- a/docs/platforms/tui-redesign.md +++ b/docs/platforms/tui-redesign.md @@ -81,7 +81,7 @@ Priorities are assigned as P0 (correctness or blocker), P1 (high-value polish), ### P0 — Correctness and blockers -1. **Verify the vim bridge is connected.** If the editor bridge is never set from the TUI side, every vim motion that relies on it silently no-ops. Confirm or fix before any further vim work. +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 diff --git a/src/config/types.mayros.ts b/src/config/types.mayros.ts index ec33a448..b77abeab 100644 --- a/src/config/types.mayros.ts +++ b/src/config/types.mayros.ts @@ -70,7 +70,7 @@ export type MayrosConfig = { seamColor?: string; /** TUI color theme preset, or "auto" to detect from the terminal. */ theme?: "auto" | "dark" | "light" | "high-contrast"; - /** Enable vim editing mode in the TUI. */ + /** Deprecated: vim mode is not available. Kept for config backward compat. */ vim?: boolean; /** Custom keybindings for TUI actions. */ keybindings?: Record; diff --git a/src/tui/commands.ts b/src/tui/commands.ts index 4ee2c107..e10f4273 100644 --- a/src/tui/commands.ts +++ b/src/tui/commands.ts @@ -148,7 +148,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/components/custom-editor.ts b/src/tui/components/custom-editor.ts index 6cf0ecbc..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 "@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/keybinding-resolver.test.ts b/src/tui/keybinding-resolver.test.ts index db5b4c5b..f02a1189 100644 --- a/src/tui/keybinding-resolver.test.ts +++ b/src/tui/keybinding-resolver.test.ts @@ -56,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); }); }); @@ -69,7 +69,6 @@ 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"); }); }); diff --git a/src/tui/keybinding-resolver.ts b/src/tui/keybinding-resolver.ts index 669c27ce..bd8c2dd3 100644 --- a/src/tui/keybinding-resolver.ts +++ b/src/tui/keybinding-resolver.ts @@ -12,8 +12,7 @@ export type TuiAction = | "selectModel" | "selectSession" | "toggleTools" - | "toggleThinking" - | "toggleVim"; + | "toggleThinking"; export const DEFAULT_TUI_KEYBINDINGS: Record = { selectAgent: "ctrl+g", @@ -21,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 { diff --git a/src/tui/tui-command-handlers.ts b/src/tui/tui-command-handlers.ts index b67b5091..aa83d21c 100644 --- a/src/tui/tui-command-handlers.ts +++ b/src/tui/tui-command-handlers.ts @@ -773,9 +773,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": { diff --git a/src/tui/tui.ts b/src/tui/tui.ts index ca5e3eb6..88438ac2 100644 --- a/src/tui/tui.ts +++ b/src/tui/tui.ts @@ -34,7 +34,6 @@ 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"; @@ -316,8 +315,7 @@ export async function runTui(opts: TuiOptions) { 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, @@ -476,12 +474,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; @@ -560,14 +552,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) => { @@ -818,7 +806,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 = [ @@ -829,7 +816,6 @@ export async function runTui(opts: TuiOptions) { verbose !== "off" ? `verbose ${verbose}` : null, reasoningLabel, tokens, - vimLabel, permLabel, fastLabel, ].filter(Boolean); 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); - } -} From 1a2b8fa0a3d02357ffe6d4e8222ae332b162f8f5 Mon Sep 17 00:00:00 2001 From: ApiliumDevTeam Date: Tue, 14 Jul 2026 13:19:44 +0200 Subject: [PATCH 13/22] Theme system v2: custom palettes, colorblind presets, and monochrome (#72) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(tui): custom themes, colorblind palettes, and monochrome mode Generalize the theme system so users can define custom palettes in ~/.mayros/themes/*.json. Each file must contain the 22 palette color tokens as #RRGGBB hex strings; the loader validates, caches by mtime, and registers them so /theme and config.ui.theme accept custom names alongside the built-ins. Add three new built-in palettes: - colorblind-dark and colorblind-light: Okabe-Ito hues that avoid the red/green pairing for error/success (error = vermilion, success = sky blue), so deuteranopic and protanopic users get distinguishable semantic colors. - monochrome: all tokens use the same foreground, for NO_COLOR environments or users who prefer a minimal terminal. Generalize ThemePreset from a closed union to string (with BuiltinPreset for the 13 built-ins), add a custom palette registry, and relax the /theme handler and config guards to accept custom names. The loader runs at TUI startup before the config theme is resolved. * chore(tui): dedupe theme presets and migrate stderr to subsystem logger Remove the duplicated THEME_PRESETS array from commands.ts (it was a local copy that had already drifted — missing the three new palettes added in the theme system update). Import the canonical list from palettes.ts instead so there is a single source of truth. Migrate the three process.stderr.write call sites in tui-command-handlers.ts (fast-mode) and compact-handler.ts to the shared subsystem logger (createSubsystemLogger("tui")), so TUI diagnostics route through the same logging infrastructure as the rest of the codebase instead of writing raw stderr. --- src/tui/commands.ts | 13 +-- src/tui/compact-handler.ts | 10 +- src/tui/theme/palettes.test.ts | 7 +- src/tui/theme/palettes.ts | 138 ++++++++++++++++++++++++++- src/tui/theme/theme-loader.test.ts | 71 ++++++++++++++ src/tui/theme/theme-loader.ts | 147 +++++++++++++++++++++++++++++ src/tui/tui-command-handlers.ts | 29 ++++-- src/tui/tui.ts | 15 ++- 8 files changed, 397 insertions(+), 33 deletions(-) create mode 100644 src/tui/theme/theme-loader.test.ts create mode 100644 src/tui/theme/theme-loader.ts diff --git a/src/tui/commands.ts b/src/tui/commands.ts index e10f4273..42b71b19 100644 --- a/src/tui/commands.ts +++ b/src/tui/commands.ts @@ -3,24 +3,13 @@ import { listChatCommands, listChatCommandsForConfig } from "../auto-reply/comma 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"]; 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 { - 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-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 aa83d21c..3cbb69ee 100644 --- a/src/tui/tui-command-handlers.ts +++ b/src/tui/tui-command-handlers.ts @@ -1,5 +1,8 @@ import { randomUUID } from "node:crypto"; import type { Component, TUI } from "@earendil-works/pi-tui"; +import { createSubsystemLogger } from "../logging/subsystem.js"; + +const logger = createSubsystemLogger("tui"); import { listThinkingLevelLabels, normalizeUsageDisplay, @@ -27,8 +30,8 @@ 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 { @@ -735,7 +738,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(); @@ -748,11 +751,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; } @@ -808,7 +816,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)"); @@ -823,9 +831,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})`); } diff --git a/src/tui/tui.ts b/src/tui/tui.ts index 88438ac2..031f2b86 100644 --- a/src/tui/tui.ts +++ b/src/tui/tui.ts @@ -28,8 +28,9 @@ import { WelcomeScreen } from "./components/welcome-screen.js"; import { GatewayChatClient } from "./gateway-chat.js"; import { enableKeyboardProtocol } from "./term-capabilities.js"; import { detectTerminalColorScheme } from "./term-capabilities.js"; -import type { ThemePreset } from "./theme/palettes.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"; @@ -281,12 +282,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; // "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 ThemePreset)) { - setThemePreset(configTheme as ThemePreset); + 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); From 583891e5bc99cac11ce9e33c48330b6ba3591b6e Mon Sep 17 00:00:00 2001 From: ApiliumDevTeam Date: Tue, 14 Jul 2026 13:58:23 +0200 Subject: [PATCH 14/22] TUI: cross-platform clipboard, inline images, and helper extraction (#74) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(tui): cross-platform clipboard support for Windows and Wayland The /copy command and teleport export hardcoded pbcopy/xclip, so they were broken on Windows (no pbcopy/xclip) and Wayland (no xclip under pure Wayland). Replace the spawn and shell-pipeline approaches with the existing cross-platform copyToClipboard helper from infra/clipboard.ts, which has a fallback chain covering pbcopy, xclip, wl-copy, clip.exe, and PowerShell Set-Clipboard. Add Windows and Wayland support to clipboard image capture: - Windows: PowerShell + System.Windows.Forms to read clipboard PNG. - Wayland: wl-paste -t image/png when WAYLAND_DISPLAY is set, with xclip fallback for XWayland sessions. * feat(tui): render image content blocks inline where supported Tool results that contain image content blocks now render inline using the terminal image component when the terminal supports it (Kitty graphics protocol or iTerm2 inline images). The Image component handles capability detection internally and falls back to a text placeholder ([image/png 12kb]) when the terminal cannot display images, so no escape sequences leak to unsupported terminals. Backend change: stop stripping the base64 data from image content blocks in sanitizeToolResult. The omitted: true flag already prevents the image from entering the model context, so retaining data does not inflate token usage — it only makes the base64 available for TUI rendering. * refactor(tui): extract pure helpers from tui.ts into tui-helpers.ts Move 7 standalone functions (createEditorSubmitHandler, shouldEnableWindowsGitBashPasteFallback, createSubmitBurstCoalescer, resolveTuiSessionKey, tryInlinePairingApproval, resolveGatewayDisconnectState, createBackspaceDeduper) out of tui.ts into a dedicated tui-helpers.ts module. These functions are pure factories and resolvers with no dependency on the TUI instance or mutable state, and are already covered by tui.test.ts. Reduces tui.ts from 1087 to 883 LOC. Further extraction of the state factory, status renderer, and connection handlers is tracked as a follow-up — the remaining sections are tightly coupled to runTui's closures and require more careful refactoring. --- src/agents/pi-embedded-subscribe.tools.ts | 7 +- src/tui/clipboard-image.ts | 77 ++++++- src/tui/components/tool-execution.ts | 27 ++- src/tui/tui-command-handlers.ts | 25 +-- src/tui/tui-helpers.ts | 228 +++++++++++++++++++++ src/tui/tui.ts | 237 ++-------------------- 6 files changed, 362 insertions(+), 239 deletions(-) create mode 100644 src/tui/tui-helpers.ts 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/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/components/tool-execution.ts b/src/tui/components/tool-execution.ts index df48c41e..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 "@earendil-works/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,6 +63,7 @@ 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; @@ -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(); } @@ -145,9 +150,29 @@ export class ToolExecutionComponent extends Container { // 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) { diff --git a/src/tui/tui-command-handlers.ts b/src/tui/tui-command-handlers.ts index 3cbb69ee..c5e830ef 100644 --- a/src/tui/tui-command-handlers.ts +++ b/src/tui/tui-command-handlers.ts @@ -1,5 +1,6 @@ import { randomUUID } from "node:crypto"; 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"); @@ -12,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"; @@ -888,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)}`); } @@ -1238,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-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.ts b/src/tui/tui.ts index 031f2b86..5718f8b1 100644 --- a/src/tui/tui.ts +++ b/src/tui/tui.ts @@ -1,9 +1,7 @@ import { CombinedAutocompleteProvider, Container, - Key, Loader, - matchesKey, ProcessTerminal, Text, TUI, @@ -13,7 +11,6 @@ import { VERSION } from "../version.js"; import { loadConfig } from "../config/config.js"; import { isLoopbackHost } from "../gateway/net.js"; import { - buildAgentMainSessionKey, normalizeAgentId, normalizeMainKey, parseAgentSessionKey, @@ -53,223 +50,23 @@ import { buildWaitingStatusMessage, defaultWaitingPhrases } from "./tui-waiting. 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(); From 410223f0c16fcfec3d606bae1cad9e8f40d8d5c6 Mon Sep 17 00:00:00 2001 From: ApiliumDevTeam Date: Tue, 14 Jul 2026 19:08:04 +0200 Subject: [PATCH 15/22] FTS5 session search and skill curator lifecycle (#77) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(search): FTS5-backed session search with BM25 ranking Replace the linear substring scan with a dedicated SQLite FTS5 index for session message search. The index lives at ~/.mayros/agents//sessions/search.sqlite and uses an external-content virtual table with triggers for automatic sync. The search now supports multi-word queries with BM25 relevance ranking, role/timestamp/session filters, and snippet generation via the FTS5 snippet() function. Indexing is incremental (lazy sync on each query, skipping unchanged files by mtime+size), so no background watchers are needed. The public searchSessions API is unchanged — it tries FTS5 first and falls back to the original linear scan when node:sqlite is unavailable, so search always works regardless of the runtime. * feat(search): FTS5-backed session search with BM25 ranking Replace the linear substring scan with a dedicated SQLite FTS5 index for session message search. The index lives at ~/.mayros/agents//sessions/search.sqlite and uses an external-content virtual table with triggers for automatic sync. The search now supports multi-word queries with BM25 relevance ranking, role/timestamp/session filters, and snippet generation via the FTS5 snippet() function. Indexing is incremental (lazy sync on each query, skipping unchanged files by mtime+size), so no background watchers are needed. The public searchSessions API is unchanged — it tries FTS5 first and falls back to the original linear scan when node:sqlite is unavailable, so search always works regardless of the runtime. Results are sorted by timestamp descending and sessionsSearched counts files scanned, matching the original contract. * feat(skills): curator lifecycle with activity tracking and auto-archive Add a skill lifecycle manager that automatically transitions skills through active → stale → archived states based on invocation activity. Activity tracking (activity-store.ts): a JSON sidecar at ~/.mayros/skills/skill-activity.json records lastInvokedAt, invocationCount, and firstInvokedAt per skill. Updated on every skill invocation. Lifecycle metadata: extend MayrosSkillMetadata with state, pinned, created, and updated fields. Parsed from SKILL.md frontmatter. Curator sweep (skill-curator.ts): a self-throttled background sweep (mirroring session-reaper.ts) that transitions active skills to stale after 30 days of inactivity and stale skills to archived after 90 days. Pinned skills are never transitioned. Only agent-created skills (source: mayros-managed) are eligible for archival — user skills can go stale but are never auto-archived. Archived skills are moved to ~/.mayros/skills.archived/. --- src/agents/skills/activity-store.test.ts | 84 +++++++ src/agents/skills/activity-store.ts | 86 +++++++ src/agents/skills/skill-curator.ts | 214 +++++++++++++++++ src/agents/skills/types.ts | 11 + src/infra/session-search-fts.test.ts | 203 +++++++++++++++++ src/infra/session-search-fts.ts | 279 +++++++++++++++++++++++ src/infra/session-search.ts | 48 ++++ 7 files changed, 925 insertions(+) create mode 100644 src/agents/skills/activity-store.test.ts create mode 100644 src/agents/skills/activity-store.ts create mode 100644 src/agents/skills/skill-curator.ts create mode 100644 src/infra/session-search-fts.test.ts create mode 100644 src/infra/session-search-fts.ts diff --git a/src/agents/skills/activity-store.test.ts b/src/agents/skills/activity-store.test.ts new file mode 100644 index 00000000..d6eabba1 --- /dev/null +++ b/src/agents/skills/activity-store.test.ts @@ -0,0 +1,84 @@ +import { afterEach, describe, expect, it } from "vitest"; +import { mkdtempSync, rmSync, existsSync, readFileSync } from "node:fs"; +import { join } from "node:path"; +import { tmpdir } from "node:os"; +import { + readActivityStore, + writeActivityStore, + recordSkillInvocation, + getSkillActivity, + daysSinceLastInvoked, +} from "./activity-store.js"; + +let tempDir: string; + +afterEach(() => { + if (tempDir) { + try { + rmSync(tempDir, { recursive: true, force: true }); + } catch { + // best effort + } + } +}); + +function tempStorePath(): string { + tempDir = mkdtempSync(join(tmpdir(), "mayros-activity-")); + return join(tempDir, "skill-activity.json"); +} + +describe("activity-store", () => { + it("returns empty record when file does not exist", () => { + const path = join(mkdtempSync(join(tmpdir(), "mayros-act-")), "missing.json"); + expect(readActivityStore(path)).toEqual({}); + }); + + it("writes and reads the store", () => { + const path = tempStorePath(); + writeActivityStore( + { "skill-a": { lastInvokedAt: 1000, invocationCount: 5, firstInvokedAt: 500 } }, + path, + ); + const store = readActivityStore(path); + expect(store["skill-a"]).toEqual({ + lastInvokedAt: 1000, + invocationCount: 5, + firstInvokedAt: 500, + }); + }); + + it("records a new invocation", () => { + const path = tempStorePath(); + const result = recordSkillInvocation("skill-a", 5000, path); + expect(result.invocationCount).toBe(1); + expect(result.lastInvokedAt).toBe(5000); + expect(result.firstInvokedAt).toBe(5000); + }); + + it("increments count on subsequent invocations", () => { + const path = tempStorePath(); + recordSkillInvocation("skill-a", 1000, path); + recordSkillInvocation("skill-a", 2000, path); + const result = recordSkillInvocation("skill-a", 3000, path); + expect(result.invocationCount).toBe(3); + expect(result.lastInvokedAt).toBe(3000); + expect(result.firstInvokedAt).toBe(1000); + }); + + it("returns null for never-invoked skills", () => { + const path = tempStorePath(); + expect(getSkillActivity("never", path)).toBeNull(); + }); + + it("calculates days since last invocation", () => { + const path = tempStorePath(); + recordSkillInvocation("skill-a", 1000, path); + const now = 1000 + 5 * 24 * 60 * 60 * 1000; // 5 days later + expect(daysSinceLastInvoked("skill-a", now, path)).toBe(5); + }); + + it("returns Infinity for never-invoked skills", () => { + const path = tempStorePath(); + expect(daysSinceLastInvoked("never", Date.now(), path)).toBe(Infinity); + }); +}); diff --git a/src/agents/skills/activity-store.ts b/src/agents/skills/activity-store.ts new file mode 100644 index 00000000..c400ad79 --- /dev/null +++ b/src/agents/skills/activity-store.ts @@ -0,0 +1,86 @@ +/** + * Skill activity tracking. + * + * A lightweight JSON sidecar at ~/.mayros/skills/skill-activity.json that + * records when each skill was last invoked and how many times. The curator + * uses this data to transition skills through lifecycle states (active → + * stale → archived). + */ + +import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs"; +import { join } from "node:path"; +import { homedir } from "node:os"; +import { resolveStateDir } from "../../config/paths.js"; + +export type SkillActivity = { + lastInvokedAt: number; + invocationCount: number; + firstInvokedAt: number; +}; + +export type SkillActivityStore = Record; + +/** Resolve the activity store path (~/.mayros/skills/skill-activity.json). */ +export function resolveActivityStorePath(): string { + return join(resolveStateDir(), "skills", "skill-activity.json"); +} + +/** Read the activity store, returning an empty record if it does not exist. */ +export function readActivityStore(path?: string): SkillActivityStore { + const filePath = path ?? resolveActivityStorePath(); + try { + if (!existsSync(filePath)) return {}; + const content = readFileSync(filePath, "utf8"); + return JSON.parse(content) as SkillActivityStore; + } catch { + return {}; + } +} + +/** Write the activity store, creating the directory if needed. */ +export function writeActivityStore(store: SkillActivityStore, path?: string): void { + const filePath = path ?? resolveActivityStorePath(); + mkdirSync(join(filePath, ".."), { recursive: true }); + writeFileSync(filePath, JSON.stringify(store, null, 2) + "\n", "utf8"); +} + +/** + * Record a skill invocation: bump the count and update timestamps. + * Returns the updated entry. + */ +export function recordSkillInvocation( + skillKey: string, + now: number = Date.now(), + path?: string, +): SkillActivity { + const store = readActivityStore(path); + const existing = store[skillKey]; + const updated: SkillActivity = { + lastInvokedAt: now, + invocationCount: (existing?.invocationCount ?? 0) + 1, + firstInvokedAt: existing?.firstInvokedAt ?? now, + }; + store[skillKey] = updated; + writeActivityStore(store, path); + return updated; +} + +/** Get activity for a specific skill, or null if never invoked. */ +export function getSkillActivity(skillKey: string, path?: string): SkillActivity | null { + const store = readActivityStore(path); + return store[skillKey] ?? null; +} + +/** + * Determine the days since last invocation for a skill. + * Returns Infinity if the skill was never invoked. + */ +export function daysSinceLastInvoked( + skillKey: string, + now: number = Date.now(), + path?: string, +): number { + const activity = getSkillActivity(skillKey, path); + if (!activity) return Infinity; + return Math.floor((now - activity.lastInvokedAt) / (1000 * 60 * 60 * 24)); +} diff --git a/src/agents/skills/skill-curator.ts b/src/agents/skills/skill-curator.ts new file mode 100644 index 00000000..0fe13cf0 --- /dev/null +++ b/src/agents/skills/skill-curator.ts @@ -0,0 +1,214 @@ +/** + * Skill curator — automatic lifecycle management. + * + * Runs as a background sweep (piggy-backed on the cron timer, mirroring + * session-reaper.ts) to transition skills through lifecycle states based + * on activity data: + * + * active → stale when not invoked for staleAfterDays (default 30) + * stale → archived when not invoked for archiveAfterDays (default 90) + * + * Pinned skills are never transitioned. Only skills with source + * "mayros-managed" (agent-created) are eligible for archival; user skills + * can go stale but are never archived automatically. + */ + +import { readFileSync, writeFileSync, existsSync, mkdirSync, renameSync } from "node:fs"; +import { join } from "node:path"; +import { resolveStateDir } from "../../config/paths.js"; +import { + daysSinceLastInvoked, + readActivityStore, + type SkillActivityStore, +} from "./activity-store.js"; + +export type CuratorConfig = { + enabled: boolean; + staleAfterDays: number; + archiveAfterDays: number; +}; + +export const DEFAULT_CURATOR_CONFIG: CuratorConfig = { + enabled: true, + staleAfterDays: 30, + archiveAfterDays: 90, +}; + +export type SkillSummary = { + skillKey: string; + name: string; + source: string; + state: string; + pinned: boolean; + filePath: string; + daysSinceInvoked: number; +}; + +export type CuratorSweepResult = { + scanned: number; + stale: number; + archived: number; + skipped: number; +}; + +const MIN_SWEEP_INTERVAL_MS = 5 * 60 * 1000; // 5 minutes +let lastSweepAt = 0; + +/** + * Discover skill files in the managed skills directory (~/.mayros/skills/). + * Returns a list of skill summaries with metadata parsed from frontmatter. + */ +export function discoverManagedSkills(skillsDir?: string): SkillSummary[] { + const dir = skillsDir ?? join(resolveStateDir(), "skills"); + if (!existsSync(dir)) return []; + + const { readdirSync } = require("node:fs") as typeof import("node:fs"); + const skills: SkillSummary[] = []; + + for (const entry of readdirSync(dir, { withFileTypes: true })) { + if (!entry.isDirectory()) continue; + const skillFile = join(dir, entry.name, "SKILL.md"); + if (!existsSync(skillFile)) continue; + + try { + const content = readFileSync(skillFile, "utf8"); + const meta = parseSkillFrontmatter(content); + skills.push({ + skillKey: meta.skillKey ?? entry.name, + name: meta.name ?? entry.name, + source: meta.source ?? "mayros-managed", + state: meta.state ?? "active", + pinned: meta.pinned ?? false, + filePath: skillFile, + daysSinceInvoked: daysSinceLastInvoked(meta.skillKey ?? entry.name), + }); + } catch { + // Skip unreadable skills. + } + } + + return skills; +} + +/** + * Run a lifecycle sweep: transition stale and archived skills based on + * activity data. Self-throttled to avoid running more than once per + * MIN_SWEEP_INTERVAL_MS. + */ +export function sweepSkillLifecycle( + config: CuratorConfig = DEFAULT_CURATOR_CONFIG, + opts?: { skillsDir?: string; now?: number; activityPath?: string }, +): CuratorSweepResult { + const now = opts?.now ?? Date.now(); + if (!config.enabled) return { scanned: 0, stale: 0, archived: 0, skipped: 0 }; + if (now - lastSweepAt < MIN_SWEEP_INTERVAL_MS) { + return { scanned: 0, stale: 0, archived: 0, skipped: 0 }; + } + lastSweepAt = now; + + const skills = discoverManagedSkills(opts?.skillsDir); + let stale = 0; + let archived = 0; + let skipped = 0; + + for (const skill of skills) { + // Pinned skills never transition. + if (skill.pinned) { + skipped++; + continue; + } + + if (skill.state === "archived") { + skipped++; + continue; + } + + const days = skill.daysSinceInvoked; + + // active → stale + if (skill.state === "active" && days > config.staleAfterDays) { + updateSkillState(skill.filePath, "stale"); + stale++; + continue; + } + + // stale → archived (only for agent-created skills) + if ( + skill.state === "stale" && + days > config.archiveAfterDays && + skill.source === "mayros-managed" + ) { + archiveSkill(skill.filePath, skill.name); + archived++; + continue; + } + } + + return { scanned: skills.length, stale, archived, skipped }; +} + +// --------------------------------------------------------------------------- +// Helpers +// --------------------------------------------------------------------------- + +type ParsedFrontmatter = { + name?: string; + skillKey?: string; + source?: string; + state?: string; + pinned?: boolean; +}; + +function parseSkillFrontmatter(content: string): ParsedFrontmatter { + const match = content.match(/^---\n([\s\S]*?)\n---/); + if (!match) return {}; + const yaml = match[1]; + const result: ParsedFrontmatter = {}; + for (const line of yaml.split("\n")) { + const m = line.match(/^(\w[\w-]*):\s*(.*)$/); + if (!m) continue; + const [, key, value] = m; + if (key === "name") result.name = value; + else if (key === "skillKey") result.skillKey = value; + else if (key === "source") result.source = value; + else if (key === "state") result.state = value; + else if (key === "pinned") result.pinned = value === "true"; + } + return result; +} + +function updateSkillState(filePath: string, newState: string): void { + try { + const content = readFileSync(filePath, "utf8"); + // Update or add the state field in the frontmatter. + if (/^state:\s*/m.test(content)) { + const updated = content.replace(/^state:\s*.*$/m, `state: ${newState}`); + writeFileSync(filePath, updated, "utf8"); + } else { + // Add state after the first line of frontmatter. + const updated = content.replace(/^---\n/, `---\nstate: ${newState}\n`); + writeFileSync(filePath, updated, "utf8"); + } + } catch { + // Best-effort — if the file is unreadable, skip. + } +} + +function archiveSkill(filePath: string, skillName: string): void { + try { + const archiveDir = join(resolveStateDir(), "skills.archived", skillName); + mkdirSync(archiveDir, { recursive: true }); + const dest = join(archiveDir, "SKILL.md"); + // Move the entire skill directory to the archive. + const skillDir = join(filePath, ".."); + renameSync(skillDir, archiveDir); + } catch { + // Best-effort — if the move fails, just mark as archived in frontmatter. + updateSkillState(filePath, "archived"); + } +} + +/** Reset the sweep throttle (for testing). */ +export function resetCuratorThrottle(): void { + lastSweepAt = 0; +} diff --git a/src/agents/skills/types.ts b/src/agents/skills/types.ts index d2a0ae62..72a7071c 100644 --- a/src/agents/skills/types.ts +++ b/src/agents/skills/types.ts @@ -27,6 +27,9 @@ export type SemanticSkillManifest = { queries: Array<{ predicate: string; scope: "agent" | "namespace" | "global" }>; }; +/** Lifecycle state for skill management by the curator. */ +export type SkillLifecycleState = "active" | "stale" | "archived"; + export type MayrosSkillMetadata = { always?: boolean; skillKey?: string; @@ -43,6 +46,14 @@ export type MayrosSkillMetadata = { install?: SkillInstallSpec[]; skillType?: "classic" | "semantic"; semanticManifest?: SemanticSkillManifest; + /** Lifecycle state managed by the curator. Defaults to "active". */ + state?: SkillLifecycleState; + /** Pinned skills are never auto-archived by the curator. */ + pinned?: boolean; + /** ISO timestamp of creation (set by the curator or manually). */ + created?: string; + /** ISO timestamp of last update. */ + updated?: string; }; export type SkillInvocationPolicy = { diff --git a/src/infra/session-search-fts.test.ts b/src/infra/session-search-fts.test.ts new file mode 100644 index 00000000..db4ab80f --- /dev/null +++ b/src/infra/session-search-fts.test.ts @@ -0,0 +1,203 @@ +import { afterEach, describe, expect, it } from "vitest"; +import { mkdtempSync, writeFileSync, rmSync } from "node:fs"; +import { join } from "node:path"; +import { tmpdir } from "node:os"; +import { openSearchDb, syncSearchIndex, searchSessionsFTS } from "./session-search-fts.js"; + +/** + * FTS5 session search tests. + * + * Exercises the full path: schema creation, file indexing, and BM25-ranked + * search with filters. Uses temp directories and real SQLite databases. + */ + +let tempDir: string; +let dbPath: string; + +function setupTempSessions(files: Record): { sessionsDir: string; dbPath: string } { + tempDir = mkdtempSync(join(tmpdir(), "mayros-fts-test-")); + const sessionsDir = join(tempDir, "sessions"); + const { mkdirSync } = require("node:fs") as typeof import("node:fs"); + mkdirSync(sessionsDir, { recursive: true }); + for (const [name, content] of Object.entries(files)) { + writeFileSync(join(sessionsDir, name), content); + } + dbPath = join(sessionsDir, "search.sqlite"); + return { sessionsDir, dbPath }; +} + +afterEach(() => { + if (tempDir) { + try { + rmSync(tempDir, { recursive: true, force: true }); + } catch { + // best effort + } + } +}); + +function makeJsonLine( + type: string, + id: string, + role: string, + content: string, + timestamp: number, +): string { + return JSON.stringify({ + type, + id, + message: { role, content, timestamp }, + }); +} + +describe("FTS5 session search", () => { + it("creates the schema and indexes a session file", () => { + const { sessionsDir, dbPath } = setupTempSessions({ + "sess-1.jsonl": + [ + makeJsonLine("session", "hdr", "", "", 0), + makeJsonLine( + "message", + "m1", + "user", + "How do I configure authentication?", + 1700000000000, + ), + makeJsonLine( + "message", + "m2", + "assistant", + "You can use JWT tokens for auth.", + 1700000001000, + ), + ].join("\n") + "\n", + }); + + const db = openSearchDb(dbPath); + try { + const count = syncSearchIndex(db, sessionsDir, "main"); + expect(count).toBe(1); // one file indexed + + const rows = db.prepare("SELECT COUNT(*) as n FROM session_messages").get() as { n: number }; + expect(rows.n).toBe(2); // user + assistant messages (session header skipped) + } finally { + db.close(); + } + }); + + it("returns BM25-ranked results for a keyword query", () => { + const { sessionsDir, dbPath } = setupTempSessions({ + "sess-1.jsonl": + [ + makeJsonLine( + "message", + "m1", + "user", + "How do I configure authentication?", + 1700000000000, + ), + makeJsonLine( + "message", + "m2", + "assistant", + "Use JWT for authentication setup.", + 1700000001000, + ), + makeJsonLine("message", "m3", "user", "What about database connections?", 1700000002000), + ].join("\n") + "\n", + }); + + const db = openSearchDb(dbPath); + try { + syncSearchIndex(db, sessionsDir, "main"); + const result = searchSessionsFTS(db, { query: "authentication" }); + expect(result.results.length).toBeGreaterThanOrEqual(1); + expect(result.results[0].content).toContain("authentication"); + expect(result.results[0].sessionId).toBe("sess-1"); + } finally { + db.close(); + } + }); + + it("filters by role", () => { + const { sessionsDir, dbPath } = setupTempSessions({ + "sess-1.jsonl": + [ + makeJsonLine("message", "m1", "user", "Tell me about authentication", 1700000000000), + makeJsonLine( + "message", + "m2", + "assistant", + "Authentication uses JWT tokens", + 1700000001000, + ), + ].join("\n") + "\n", + }); + + const db = openSearchDb(dbPath); + try { + syncSearchIndex(db, sessionsDir, "main"); + const result = searchSessionsFTS(db, { query: "authentication", role: "user" }); + expect(result.results).toHaveLength(1); + expect(result.results[0].role).toBe("user"); + } finally { + db.close(); + } + }); + + it("filters by time range", () => { + const { sessionsDir, dbPath } = setupTempSessions({ + "sess-1.jsonl": + [ + makeJsonLine("message", "m1", "user", "old authentication message", 1000), + makeJsonLine("message", "m2", "user", "new authentication message", 5000), + ].join("\n") + "\n", + }); + + const db = openSearchDb(dbPath); + try { + syncSearchIndex(db, sessionsDir, "main"); + const result = searchSessionsFTS(db, { + query: "authentication", + since: 2000, + }); + expect(result.results).toHaveLength(1); + expect(result.results[0].timestamp).toBe(5000); + } finally { + db.close(); + } + }); + + it("skips unchanged files on re-index (incremental)", () => { + const { sessionsDir, dbPath } = setupTempSessions({ + "sess-1.jsonl": + [makeJsonLine("message", "m1", "user", "authentication help", 1000)].join("\n") + "\n", + }); + + const db = openSearchDb(dbPath); + try { + const first = syncSearchIndex(db, sessionsDir, "main"); + expect(first).toBe(1); + const second = syncSearchIndex(db, sessionsDir, "main"); + expect(second).toBe(0); // no changes → skip + } finally { + db.close(); + } + }); + + it("returns empty results for no match", () => { + const { sessionsDir, dbPath } = setupTempSessions({ + "sess-1.jsonl": + [makeJsonLine("message", "m1", "user", "hello world", 1000)].join("\n") + "\n", + }); + + const db = openSearchDb(dbPath); + try { + syncSearchIndex(db, sessionsDir, "main"); + const result = searchSessionsFTS(db, { query: "nonexistent_term_xyz" }); + expect(result.results).toHaveLength(0); + } finally { + db.close(); + } + }); +}); diff --git a/src/infra/session-search-fts.ts b/src/infra/session-search-fts.ts new file mode 100644 index 00000000..f3813093 --- /dev/null +++ b/src/infra/session-search-fts.ts @@ -0,0 +1,279 @@ +/** + * FTS5-backed session search. + * + * A dedicated SQLite database with a full-text search virtual table over + * session messages. Provides BM25-ranked search with role/timestamp/session + * filters, replacing the linear substring scan for users whose Node runtime + * includes the built-in `node:sqlite` module. + * + * The index lives at `~/.mayros/agents//sessions/search.sqlite` and + * is synced lazily (on each query) by checking file mtimes against a tracking + * table — no background watchers needed. + */ + +import { DatabaseSync } from "node:sqlite"; +import { readFileSync, statSync, existsSync, mkdirSync } from "node:fs"; +import { join } from "node:path"; +import { homedir } from "node:os"; +import { createHash } from "node:crypto"; +import { requireNodeSqlite } from "../memory/sqlite.js"; +import { buildFtsQuery } from "../memory/hybrid.js"; +import type { SearchOptions, SearchResult, SearchSummary } from "./session-search.js"; +import { extractTextContent } from "./session-search.js"; + +// --------------------------------------------------------------------------- +// Schema +// --------------------------------------------------------------------------- + +const SCHEMA_SQL = ` +CREATE TABLE IF NOT EXISTS session_messages ( + rowid INTEGER PRIMARY KEY, + session_id TEXT NOT NULL, + agent_id TEXT NOT NULL, + message_id TEXT NOT NULL, + role TEXT NOT NULL, + line_index INTEGER NOT NULL, + timestamp INTEGER NOT NULL DEFAULT 0, + source_path TEXT NOT NULL, + content TEXT NOT NULL +); +CREATE INDEX IF NOT EXISTS idx_sm_session ON session_messages(session_id); +CREATE INDEX IF NOT EXISTS idx_sm_role ON session_messages(role); +CREATE INDEX IF NOT EXISTS idx_sm_time ON session_messages(timestamp); + +CREATE TABLE IF NOT EXISTS session_files_meta ( + path TEXT PRIMARY KEY, + mtime INTEGER NOT NULL, + size INTEGER NOT NULL, + hash TEXT NOT NULL, + last_indexed_line INTEGER NOT NULL +); + +CREATE VIRTUAL TABLE IF NOT EXISTS session_messages_fts USING fts5( + content, + content='session_messages', + content_rowid='rowid', + tokenize = 'unicode61' +); + +CREATE TRIGGER IF NOT EXISTS sm_ai AFTER INSERT ON session_messages BEGIN + INSERT INTO session_messages_fts(rowid, content) VALUES (new.rowid, new.content); +END; +CREATE TRIGGER IF NOT EXISTS sm_ad AFTER DELETE ON session_messages BEGIN + INSERT INTO session_messages_fts(session_messages_fts, rowid, content) + VALUES ('delete', old.rowid, old.content); +END; +CREATE TRIGGER IF NOT EXISTS sm_au AFTER UPDATE ON session_messages BEGIN + INSERT INTO session_messages_fts(session_messages_fts, rowid, content) + VALUES ('delete', old.rowid, old.content); + INSERT INTO session_messages_fts(rowid, content) VALUES (new.rowid, new.content); +END; +`; + +// --------------------------------------------------------------------------- +// DB lifecycle +// --------------------------------------------------------------------------- + +/** Resolve the search DB path for a given agent. */ +export function resolveSearchDbPath(agentId: string = "main"): string { + return join(homedir(), ".mayros", "agents", agentId, "sessions", "search.sqlite"); +} + +/** Open (or create) the search database, initializing the schema. */ +export function openSearchDb(dbPath: string): DatabaseSync { + const dir = join(dbPath, ".."); + if (!existsSync(dir)) { + mkdirSync(dir, { recursive: true }); + } + const sqlite = requireNodeSqlite(); + const db = new sqlite.DatabaseSync(dbPath); + db.exec("PRAGMA journal_mode = WAL"); + db.exec("PRAGMA synchronous = NORMAL"); + db.exec(SCHEMA_SQL); + return db; +} + +// --------------------------------------------------------------------------- +// Indexing +// --------------------------------------------------------------------------- + +type SessionFileEntry = { + type: string; + id?: string; + message?: { + role?: string; + content?: unknown; + timestamp?: number; + }; +}; + +/** + * Sync the search index for a given sessions directory. Only re-indexes files + * whose mtime or size has changed since the last sync. Returns the number of + * files that were (re)indexed. + */ +export function syncSearchIndex(db: DatabaseSync, sessionsDir: string, agentId: string): number { + if (!existsSync(sessionsDir)) return 0; + + const { readdirSync } = require("node:fs") as typeof import("node:fs"); + const files = readdirSync(sessionsDir).filter( + (f) => f.endsWith(".jsonl") && !f.endsWith(".search.sqlite"), + ); + + let reindexed = 0; + + for (const fileName of files) { + const filePath = join(sessionsDir, fileName); + const stats = statSync(filePath); + const meta = db + .prepare("SELECT mtime, size, last_indexed_line FROM session_files_meta WHERE path = ?") + .get(filePath) as { mtime: number; size: number; last_indexed_line: number } | undefined; + + if (meta && meta.mtime === stats.mtimeMs && meta.size === stats.size) { + continue; // unchanged + } + + // Parse the file and index its messages. + const sessionId = fileName.replace(/\.jsonl$/, ""); + const content = readFileSync(filePath, "utf8"); + const hash = createHash("sha256").update(content).digest("hex").slice(0, 16); + const lines = content.split("\n").filter((l) => l.trim()); + + // Delete old entries for this file, then reinsert. + db.prepare("DELETE FROM session_messages WHERE source_path = ?").run(filePath); + + const insertStmt = db.prepare( + `INSERT INTO session_messages (session_id, agent_id, message_id, role, line_index, timestamp, source_path, content) + VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, + ); + + for (let i = 0; i < lines.length; i++) { + try { + const entry = JSON.parse(lines[i]) as SessionFileEntry; + if (entry.type !== "message" || !entry.message?.role) continue; + const role = entry.message.role; + if (role !== "user" && role !== "assistant") continue; + const text = extractTextContent(entry.message.content); + if (!text.trim()) continue; + insertStmt.run( + sessionId, + agentId, + entry.id ?? `${sessionId}-${i}`, + role, + i, + entry.message.timestamp ?? 0, + filePath, + text, + ); + } catch { + // Skip malformed lines. + } + } + + db.prepare( + `INSERT INTO session_files_meta (path, mtime, size, hash, last_indexed_line) + VALUES (?, ?, ?, ?, ?) + ON CONFLICT(path) DO UPDATE SET mtime=excluded.mtime, size=excluded.size, hash=excluded.hash, last_indexed_line=excluded.last_indexed_line`, + ).run(filePath, stats.mtimeMs, stats.size, hash, lines.length); + + reindexed++; + } + + return reindexed; +} + +// --------------------------------------------------------------------------- +// Search +// --------------------------------------------------------------------------- + +/** + * Search session messages using FTS5 with BM25 ranking. + * + * Returns results shaped to match the existing SearchResult type so callers + * (search-cli.ts, TUI /search) need no changes. + */ +export function searchSessionsFTS(db: DatabaseSync, opts: SearchOptions): SearchSummary { + const ftsQuery = buildFtsQuery(opts.query); + if (!ftsQuery) { + return { results: [], totalMatches: 0, sessionsSearched: 0, durationMs: 0 }; + } + + const limit = Math.max(1, Math.min(opts.limit ?? 20, 200)); + const conditions: string[] = ["session_messages_fts MATCH ?"]; + const params: Array = [ftsQuery]; + + if (opts.role) { + conditions.push("m.role = ?"); + params.push(opts.role); + } + if (opts.since) { + conditions.push("m.timestamp >= ?"); + params.push(opts.since); + } + if (opts.before) { + conditions.push("m.timestamp <= ?"); + params.push(opts.before); + } + if (opts.sessionIds && opts.sessionIds.length > 0) { + const placeholders = opts.sessionIds.map(() => "?").join(","); + conditions.push(`m.session_id IN (${placeholders})`); + params.push(...opts.sessionIds); + } + + const whereClause = conditions.join(" AND "); + + const rows = db + .prepare( + `SELECT m.session_id, m.message_id, m.role, m.timestamp, m.line_index, + m.source_path, m.content, + bm25(session_messages_fts) AS rank, + snippet(session_messages_fts, 0, '«', '»', '…', 16) AS snippet + FROM session_messages_fts + JOIN session_messages m ON m.rowid = session_messages_fts.rowid + WHERE ${whereClause} + ORDER BY rank ASC + LIMIT ?`, + ) + .all(...params, limit) as Array<{ + session_id: string; + message_id: string; + role: string; + timestamp: number; + line_index: number; + source_path: string; + content: string; + rank: number; + snippet: string; + }>; + + const results: SearchResult[] = rows.map((row) => ({ + sessionId: row.session_id, + messageId: row.message_id, + role: row.role as "user" | "assistant", + content: row.content, + snippet: row.snippet, + timestamp: row.timestamp, + lineIndex: row.line_index, + })); + + return { + results, + totalMatches: results.length, + sessionsSearched: new Set(rows.map((r) => r.session_id)).size, + durationMs: 0, // caller fills this if needed + }; +} + +// --------------------------------------------------------------------------- +// FTS5 availability check +// --------------------------------------------------------------------------- + +/** Check whether the FTS5 search backend is available (node:sqlite present). */ +export function isFtsSearchAvailable(): boolean { + try { + requireNodeSqlite(); + return true; + } catch { + return false; + } +} diff --git a/src/infra/session-search.ts b/src/infra/session-search.ts index e54ef447..721f7ded 100644 --- a/src/infra/session-search.ts +++ b/src/infra/session-search.ts @@ -184,8 +184,56 @@ export async function searchSessionFile( /** * Search across all sessions for matching messages. + * + * Uses the FTS5 backend (SQLite full-text search with BM25 ranking) when the + * Node runtime includes `node:sqlite`. Falls back to the linear substring scan + * when the built-in SQLite module is unavailable, so search always works. */ export async function searchSessions(opts: SearchOptions): Promise { + // Try FTS5 backend first — it is dramatically faster and supports multi-word + // queries with ranking. + try { + const { + isFtsSearchAvailable, + openSearchDb, + resolveSearchDbPath, + syncSearchIndex, + searchSessionsFTS, + } = await import("./session-search-fts.js"); + if (isFtsSearchAvailable()) { + const startTime = Date.now(); + const dbPath = resolveSearchDbPath(); + const db = openSearchDb(dbPath); + try { + // Discover session files the same way the linear scan does, then + // index only those files. This handles both the default layout + // (~/.mayros/agents//sessions/*.jsonl) and test overrides. + const sessionFiles = await discoverSessionFiles(opts.basePath); + const agentId = "main"; + for (const sf of sessionFiles) { + syncSearchIndex(db, join(sf.filePath, ".."), agentId); + } + const result = searchSessionsFTS(db, opts); + // Preserve the public contract: results sorted by timestamp descending, + // and sessionsSearched counts files scanned (not just files with matches). + result.results.sort((a, b) => b.timestamp - a.timestamp); + result.sessionsSearched = sessionFiles.length; + result.durationMs = Date.now() - startTime; + return result; + } finally { + db.close(); + } + } + } catch { + // Fall through to the linear scan. + } + + // Fallback: linear substring scan (original implementation). + return searchSessionsLinear(opts); +} + +/** Linear substring scan — the original search implementation. */ +async function searchSessionsLinear(opts: SearchOptions): Promise { const startTime = Date.now(); const limit = opts.limit ?? 20; From 48af54200d498bd34c0390a3e3a5687783a303c7 Mon Sep 17 00:00:00 2001 From: ApiliumDevTeam Date: Tue, 14 Jul 2026 20:30:27 +0200 Subject: [PATCH 16/22] TUI: deep split of tui.ts and tui-command-handlers.ts (#78) * TUI: split status renderer, keybindings, and connection handlers from tui.ts * TUI: split command handlers into per-group modules with dispatcher --- src/tui/tui-cmd-clipboard.ts | 48 ++ src/tui/tui-cmd-config.ts | 308 ++++++++ src/tui/tui-cmd-display.ts | 184 +++++ src/tui/tui-cmd-ecosystem.ts | 129 ++++ src/tui/tui-cmd-misc.ts | 251 +++++++ src/tui/tui-cmd-session.ts | 153 ++++ src/tui/tui-cmd-teleport.ts | 131 ++++ src/tui/tui-cmd-types.ts | 59 ++ src/tui/tui-command-handlers.ts | 1173 ++----------------------------- src/tui/tui-connection.ts | 121 ++++ src/tui/tui-keybindings.ts | 119 ++++ src/tui/tui-status-renderer.ts | 301 ++++++++ src/tui/tui.ts | 450 +++--------- 13 files changed, 1944 insertions(+), 1483 deletions(-) create mode 100644 src/tui/tui-cmd-clipboard.ts create mode 100644 src/tui/tui-cmd-config.ts create mode 100644 src/tui/tui-cmd-display.ts create mode 100644 src/tui/tui-cmd-ecosystem.ts create mode 100644 src/tui/tui-cmd-misc.ts create mode 100644 src/tui/tui-cmd-session.ts create mode 100644 src/tui/tui-cmd-teleport.ts create mode 100644 src/tui/tui-cmd-types.ts create mode 100644 src/tui/tui-connection.ts create mode 100644 src/tui/tui-keybindings.ts create mode 100644 src/tui/tui-status-renderer.ts diff --git a/src/tui/tui-cmd-clipboard.ts b/src/tui/tui-cmd-clipboard.ts new file mode 100644 index 00000000..cbc0e227 --- /dev/null +++ b/src/tui/tui-cmd-clipboard.ts @@ -0,0 +1,48 @@ +/** + * Clipboard/export command group. + * Commands: copy, export + */ + +import { writeFileSync } from "node:fs"; +import { copyToClipboard } from "../infra/clipboard.js"; +import type { CommandGroupHandler } from "./tui-cmd-types.js"; + +export const clipboardCommands: CommandGroupHandler = async (ctx, name, args, _raw) => { + const { chatLog } = ctx; + + switch (name) { + case "copy": { + const lastText = chatLog.getLastAssistantText(); + if (!lastText) { + chatLog.addSystem("nothing to copy"); + return true; + } + try { + const ok = await copyToClipboard(lastText); + chatLog.addSystem( + ok ? "last response copied to clipboard" : "copy failed: no clipboard tool found", + ); + } catch (err) { + chatLog.addSystem(`copy failed: ${String(err)}`); + } + return true; + } + case "export": { + const lastText = chatLog.getLastAssistantText(); + if (!lastText) { + chatLog.addSystem("nothing to export"); + return true; + } + const filePath = args || `mayros-export-${Date.now()}.md`; + try { + writeFileSync(filePath, lastText, "utf-8"); + chatLog.addSystem(`exported to ${filePath}`); + } catch (err) { + chatLog.addSystem(`export failed: ${String(err)}`); + } + return true; + } + default: + return false; + } +}; diff --git a/src/tui/tui-cmd-config.ts b/src/tui/tui-cmd-config.ts new file mode 100644 index 00000000..1f1ef3d9 --- /dev/null +++ b/src/tui/tui-cmd-config.ts @@ -0,0 +1,308 @@ +/** + * Configuration command group. + * Commands: think, verbose, reasoning, elevated, activation, usage, context + */ + +import { + listThinkingLevelLabels, + normalizeUsageDisplay, + resolveResponseUsageMode, +} from "../auto-reply/thinking.js"; +import { formatContextVisualization } from "./context-visualizer.js"; +import { createSelectList } from "./components/selectors.js"; +import type { CommandGroupHandler } from "./tui-cmd-types.js"; + +export const configCommands: CommandGroupHandler = async (ctx, name, args, _raw) => { + const { + client, + chatLog, + tui, + state, + refreshSessionInfo, + loadHistory, + applySessionInfoFromPatch, + } = ctx; + + switch (name) { + case "think": + if (!args) { + const levels = listThinkingLevelLabels( + state.sessionInfo.modelProvider, + state.sessionInfo.model, + ); + const currentThink = state.sessionInfo.thinkingLevel ?? "medium"; + const thinkItems = levels.map((l) => ({ + value: l, + label: l === currentThink ? `${l} (current)` : l, + })); + const thinkSelector = createSelectList(thinkItems, thinkItems.length); + thinkSelector.onSelect = (item) => { + void (async () => { + try { + const result = await client.patchSession({ + key: state.currentSessionKey, + thinkingLevel: item.value, + }); + chatLog.addSystem(`thinking set to ${item.value}`); + applySessionInfoFromPatch(result); + await refreshSessionInfo(); + } catch (err) { + chatLog.addSystem(`think failed: ${String(err)}`); + } + ctx.closeOverlay(); + tui.requestRender(); + })(); + }; + thinkSelector.onCancel = () => { + ctx.closeOverlay(); + tui.requestRender(); + }; + ctx.openOverlay(thinkSelector); + tui.requestRender(); + return true; + } + try { + const result = await client.patchSession({ + key: state.currentSessionKey, + thinkingLevel: args, + }); + chatLog.addSystem(`thinking set to ${args}`); + applySessionInfoFromPatch(result); + await refreshSessionInfo(); + } catch (err) { + chatLog.addSystem(`think failed: ${String(err)}`); + } + return true; + case "verbose": { + if (!args) { + const verboseOpts = ["on", "off"]; + const currentVerbose = state.sessionInfo.verboseLevel ?? "off"; + const verboseItems = verboseOpts.map((v) => ({ + value: v, + label: v === currentVerbose ? `${v} (current)` : v, + })); + const verboseSelector = createSelectList(verboseItems, verboseItems.length); + verboseSelector.onSelect = (item) => { + void (async () => { + try { + const result = await client.patchSession({ + key: state.currentSessionKey, + verboseLevel: item.value, + }); + chatLog.addSystem(`verbose set to ${item.value}`); + applySessionInfoFromPatch(result); + await loadHistory(); + } catch (err) { + chatLog.addSystem(`verbose failed: ${String(err)}`); + } + ctx.closeOverlay(); + tui.requestRender(); + })(); + }; + verboseSelector.onCancel = () => { + ctx.closeOverlay(); + tui.requestRender(); + }; + ctx.openOverlay(verboseSelector); + tui.requestRender(); + return true; + } + try { + const result = await client.patchSession({ + key: state.currentSessionKey, + verboseLevel: args, + }); + chatLog.addSystem(`verbose set to ${args}`); + applySessionInfoFromPatch(result); + await loadHistory(); + } catch (err) { + chatLog.addSystem(`verbose failed: ${String(err)}`); + } + return true; + } + case "reasoning": { + if (!args) { + const reasoningOpts = ["on", "off"]; + const currentReasoning = state.sessionInfo.reasoningLevel ?? "off"; + const reasoningItems = reasoningOpts.map((r) => ({ + value: r, + label: r === currentReasoning ? `${r} (current)` : r, + })); + const reasoningSelector = createSelectList(reasoningItems, reasoningItems.length); + reasoningSelector.onSelect = (item) => { + void (async () => { + try { + const result = await client.patchSession({ + key: state.currentSessionKey, + reasoningLevel: item.value, + }); + chatLog.addSystem(`reasoning set to ${item.value}`); + applySessionInfoFromPatch(result); + await refreshSessionInfo(); + } catch (err) { + chatLog.addSystem(`reasoning failed: ${String(err)}`); + } + ctx.closeOverlay(); + tui.requestRender(); + })(); + }; + reasoningSelector.onCancel = () => { + ctx.closeOverlay(); + tui.requestRender(); + }; + ctx.openOverlay(reasoningSelector); + tui.requestRender(); + return true; + } + try { + const result = await client.patchSession({ + key: state.currentSessionKey, + reasoningLevel: args, + }); + chatLog.addSystem(`reasoning set to ${args}`); + applySessionInfoFromPatch(result); + await refreshSessionInfo(); + } catch (err) { + chatLog.addSystem(`reasoning failed: ${String(err)}`); + } + return true; + } + case "usage": { + const normalized = args ? normalizeUsageDisplay(args) : undefined; + if (args && !normalized) { + chatLog.addSystem("usage: /usage "); + return true; + } + const currentRaw = state.sessionInfo.responseUsage; + const current = resolveResponseUsageMode(currentRaw); + const next = + normalized ?? (current === "off" ? "tokens" : current === "tokens" ? "full" : "off"); + try { + const result = await client.patchSession({ + key: state.currentSessionKey, + responseUsage: next === "off" ? null : next, + }); + chatLog.addSystem(`usage footer: ${next}`); + applySessionInfoFromPatch(result); + await refreshSessionInfo(); + } catch (err) { + chatLog.addSystem(`usage failed: ${String(err)}`); + } + return true; + } + case "elevated": { + if (!args) { + const elevatedOpts = ["on", "off", "ask", "full"]; + const currentElevated = state.sessionInfo.elevatedLevel ?? "off"; + const elevatedItems = elevatedOpts.map((e) => ({ + value: e, + label: e === currentElevated ? `${e} (current)` : e, + })); + const elevatedSelector = createSelectList(elevatedItems, elevatedItems.length); + elevatedSelector.onSelect = (item) => { + void (async () => { + try { + const result = await client.patchSession({ + key: state.currentSessionKey, + elevatedLevel: item.value, + }); + chatLog.addSystem(`elevated set to ${item.value}`); + applySessionInfoFromPatch(result); + await refreshSessionInfo(); + } catch (err) { + chatLog.addSystem(`elevated failed: ${String(err)}`); + } + ctx.closeOverlay(); + tui.requestRender(); + })(); + }; + elevatedSelector.onCancel = () => { + ctx.closeOverlay(); + tui.requestRender(); + }; + ctx.openOverlay(elevatedSelector); + tui.requestRender(); + return true; + } + if (!["on", "off", "ask", "full"].includes(args)) { + chatLog.addSystem("usage: /elevated "); + return true; + } + try { + const result = await client.patchSession({ + key: state.currentSessionKey, + elevatedLevel: args, + }); + chatLog.addSystem(`elevated set to ${args}`); + applySessionInfoFromPatch(result); + await refreshSessionInfo(); + } catch (err) { + chatLog.addSystem(`elevated failed: ${String(err)}`); + } + return true; + } + case "activation": { + if (!args) { + const activationOpts = ["mention", "always"]; + const currentActivation = state.sessionInfo.groupActivation ?? "mention"; + const activationItems = activationOpts.map((a) => ({ + value: a, + label: a === currentActivation ? `${a} (current)` : a, + })); + const activationSelector = createSelectList(activationItems, activationItems.length); + activationSelector.onSelect = (item) => { + void (async () => { + try { + const result = await client.patchSession({ + key: state.currentSessionKey, + groupActivation: item.value === "always" ? "always" : "mention", + }); + chatLog.addSystem(`activation set to ${item.value}`); + applySessionInfoFromPatch(result); + await refreshSessionInfo(); + } catch (err) { + chatLog.addSystem(`activation failed: ${String(err)}`); + } + ctx.closeOverlay(); + tui.requestRender(); + })(); + }; + activationSelector.onCancel = () => { + ctx.closeOverlay(); + tui.requestRender(); + }; + ctx.openOverlay(activationSelector); + tui.requestRender(); + return true; + } + try { + const result = await client.patchSession({ + key: state.currentSessionKey, + groupActivation: args === "always" ? "always" : "mention", + }); + chatLog.addSystem(`activation set to ${args}`); + applySessionInfoFromPatch(result); + await refreshSessionInfo(); + } catch (err) { + chatLog.addSystem(`activation failed: ${String(err)}`); + } + return true; + } + case "context": { + const used = state.sessionInfo.totalTokens ?? 0; + const max = state.sessionInfo.contextTokens ?? 0; + const lines = formatContextVisualization({ + usedTokens: used, + maxTokens: max, + inputTokens: state.sessionInfo.inputTokens, + outputTokens: state.sessionInfo.outputTokens, + }); + for (const line of lines) { + chatLog.addSystem(line); + } + return true; + } + default: + return false; + } +}; diff --git a/src/tui/tui-cmd-display.ts b/src/tui/tui-cmd-display.ts new file mode 100644 index 00000000..395920d9 --- /dev/null +++ b/src/tui/tui-cmd-display.ts @@ -0,0 +1,184 @@ +/** + * Display/style command group. + * Commands: style, theme, diff, permission, fast, tools + */ + +import { execSync } from "node:child_process"; +import { renderDiff, renderDiffStats } from "./diff-renderer.js"; +import { theme } from "./theme/theme.js"; +import { isValidOutputStyle, OUTPUT_STYLE_NAMES } from "./output-styles.js"; +import type { OutputStyle } from "./output-styles.js"; +import { THEME_PRESETS, listCustomThemeNames } from "./theme/palettes.js"; +import type { BuiltinPreset } from "./theme/palettes.js"; +import { setThemePreset, getThemePreset } from "./theme/theme.js"; +import { createSelectList } from "./components/selectors.js"; +import { createSubsystemLogger } from "../logging/subsystem.js"; +import type { CommandGroupHandler } from "./tui-cmd-types.js"; + +const logger = createSubsystemLogger("tui"); + +export const displayCommands: CommandGroupHandler = async (ctx, name, args, _raw) => { + const { client, chatLog, tui, state, applySessionInfoFromPatch } = ctx; + + switch (name) { + case "diff": { + try { + const cmd = args ? `git diff -- ${args}` : "git diff"; + const raw = execSync(cmd, { encoding: "utf-8", maxBuffer: 1024 * 1024 }).trim(); + if (!raw) { + chatLog.addSystem("no changes"); + return true; + } + const stats = renderDiffStats(raw); + chatLog.addSystem( + `${stats.files} file(s) changed, +${stats.additions} -${stats.deletions}`, + ); + 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) { + chatLog.addSystem(`diff failed: ${String(err)}`); + } + return true; + } + case "style": { + const styleName = args.toLowerCase(); + if (!styleName) { + const currentStyle = state.outputStyle ?? "standard"; + const styleItems = OUTPUT_STYLE_NAMES.map((s) => ({ + value: s, + label: s === currentStyle ? `${s} (current)` : s, + })); + const styleSelector = createSelectList(styleItems, styleItems.length); + styleSelector.onSelect = (item) => { + state.outputStyle = item.value as OutputStyle; + chatLog.addSystem(`output style set to ${item.value}`); + ctx.closeOverlay(); + tui.requestRender(); + }; + styleSelector.onCancel = () => { + ctx.closeOverlay(); + tui.requestRender(); + }; + ctx.openOverlay(styleSelector); + tui.requestRender(); + return true; + } + if (!isValidOutputStyle(styleName)) { + chatLog.addSystem(`unknown style. usage: /style <${OUTPUT_STYLE_NAMES.join("|")}>`); + return true; + } + state.outputStyle = styleName; + chatLog.addSystem(`output style set to ${styleName}`); + return true; + } + case "theme": { + const preset = args.toLowerCase(); + if (!preset) { + const currentTheme = getThemePreset(); + const themeItems = THEME_PRESETS.map((t) => ({ + value: t, + label: t === currentTheme ? `${t} (current)` : t, + })); + const themeSelector = createSelectList(themeItems, themeItems.length); + themeSelector.onSelect = (item) => { + setThemePreset(item.value); + chatLog.addSystem(`theme set to ${item.value}`); + ctx.closeOverlay(); + tui.requestRender(); + }; + themeSelector.onCancel = () => { + ctx.closeOverlay(); + tui.requestRender(); + }; + ctx.openOverlay(themeSelector); + tui.requestRender(); + return true; + } + if ( + !THEME_PRESETS.includes(preset as BuiltinPreset) && + !listCustomThemeNames().includes(preset) + ) { + chatLog.addSystem( + `unknown theme. usage: /theme <${THEME_PRESETS.join("|")}> or a custom theme name`, + ); + return true; + } + setThemePreset(preset); + chatLog.addSystem(`theme set to ${preset}`); + return true; + } + case "permission": { + const MODES = ["auto", "ask", "deny"] as const; + type PermMode = (typeof MODES)[number]; + const mode = args.toLowerCase(); + if (!mode) { + const current = state.permissionMode ?? "auto"; + const idx = MODES.indexOf(current); + const next = MODES[(idx + 1) % MODES.length] as PermMode; + state.permissionMode = next; + chatLog.addSystem(`permission mode: ${next}`); + } else if (MODES.includes(mode as PermMode)) { + state.permissionMode = mode as PermMode; + chatLog.addSystem(`permission mode set to ${mode}`); + } else { + chatLog.addSystem("usage: /permission "); + } + return true; + } + case "fast": { + const isFast = !state.fastMode; + state.fastMode = isFast; + if (isFast) { + // Save current thinking level before switching + state.previousThinkingLevel = state.sessionInfo.thinkingLevel ?? "medium"; + try { + const result = await client.patchSession({ + key: state.currentSessionKey, + thinkingLevel: "off", + }); + applySessionInfoFromPatch(result); + } catch (err) { + // Best-effort — fast mode works locally even without gateway + logger.warn("fast-mode: failed to set thinking off", { error: String(err) }); + } + state.outputStyle = "standard"; + chatLog.addSystem("fast mode enabled (thinking: off, style: standard)"); + } else { + // Restore previous thinking level + const prevLevel = state.previousThinkingLevel ?? "medium"; + try { + const result = await client.patchSession({ + key: state.currentSessionKey, + thinkingLevel: prevLevel, + }); + applySessionInfoFromPatch(result); + } catch (err) { + // Best-effort — restore may fail if gateway is unreachable + logger.warn("fast-mode: failed to restore thinking", { + level: prevLevel, + error: String(err), + }); + } + chatLog.addSystem(`fast mode disabled (thinking: ${prevLevel})`); + } + return true; + } + case "tools": { + await ctx.sendMessage( + "List every tool name you have access to. Output ONLY a numbered list of tool names, nothing else. " + + "Do NOT describe them. Just the names, one per line.", + "/tools", + ); + return true; + } + default: + return false; + } +}; diff --git a/src/tui/tui-cmd-ecosystem.ts b/src/tui/tui-cmd-ecosystem.ts new file mode 100644 index 00000000..18de0bc5 --- /dev/null +++ b/src/tui/tui-cmd-ecosystem.ts @@ -0,0 +1,129 @@ +/** + * Mayros ecosystem command group. + * Commands: plan, kg, trace, team, tasks, workflow, rules, mailbox, sync, mouse + */ + +import type { CommandGroupHandler } from "./tui-cmd-types.js"; + +export const ecosystemCommands: CommandGroupHandler = async (ctx, name, args, _raw) => { + const { chatLog } = ctx; + + switch (name) { + case "plan": { + const action = args || "show"; + await ctx.sendMessage(`/plan ${action}`); + return true; + } + case "mouse": { + if (ctx.mouseHandler) { + if (ctx.mouseHandler.isEnabled()) { + ctx.mouseHandler.disable(); + chatLog.addSystem("Mouse reporting disabled — text selection enabled."); + } else { + ctx.mouseHandler.enable(); + chatLog.addSystem("Mouse reporting enabled."); + } + } else { + chatLog.addSystem("Mouse handler not available."); + } + return true; + } + case "kg": { + // Check if semantic memory tools are likely available by asking the gateway + const kgHint = + "You MUST use one of these tools (in order of preference): " + + "memory_stats, semantic_memory_query, semantic_memory_recall. " + + "If none of these tools are available to you, respond EXACTLY with: " + + '"[kg] Plugin memory-semantic is not loaded. Run `mayros plugins list` to check."'; + if (!args) { + await ctx.sendMessage( + `Show a knowledge graph summary. ${kgHint} Show categories, triple counts, and recent entries.`, + "/kg", + ); + } else { + await ctx.sendMessage(`Search the knowledge graph for: ${args}. ${kgHint}`, `/kg ${args}`); + } + return true; + } + case "trace": { + const subCmd = args || "events"; + if (subCmd === "stats") { + await ctx.sendMessage( + "Use the trace_stats tool with no arguments to show aggregated observability statistics for the current agent.", + "/trace stats", + ); + } else if (subCmd === "explain" && args.includes(" ")) { + const eventId = args.slice("explain".length).trim(); + await ctx.sendMessage( + `Use the trace_explain tool with eventId "${eventId}" to trace the causal chain for that event.`, + `/trace explain ${eventId}`, + ); + } else { + await ctx.sendMessage( + "Use the trace_query tool with no arguments to list recent trace events for the current agent.", + "/trace events", + ); + } + return true; + } + case "team": { + await ctx.sendMessage( + "Use the mesh_team_dashboard tool with no arguments to show the team dashboard with current agent status and activity.", + "/team", + ); + return true; + } + case "tasks": { + await ctx.sendMessage( + "Use the agent_list_background_tasks tool with no arguments to list all background agent tasks and their current status.", + "/tasks", + ); + return true; + } + case "workflow": { + if (!args) { + await ctx.sendMessage( + 'Use the mesh_run_workflow tool with action "list" to list available workflows and their status.', + "/workflow", + ); + } else { + await ctx.sendMessage(`/workflow ${args}`); + } + return true; + } + case "rules": { + if (args) { + await ctx.sendMessage( + `Use the semantic_memory_recall tool to search for rules matching: ${args}`, + `/rules ${args}`, + ); + } else { + await ctx.sendMessage( + 'Use the semantic_memory_recall tool with subject pattern "rule:*" to list all active rules.', + "/rules", + ); + } + return true; + } + case "mailbox": { + if (!args) { + await ctx.sendMessage( + "Use the agent_check_inbox tool with no arguments to check the inbox for new messages and show unread count.", + "/mailbox", + ); + } else { + await ctx.sendMessage(`/mailbox ${args}`); + } + return true; + } + case "sync": { + await ctx.sendMessage( + "Use the cortex_sync_status tool with no arguments to show Cortex peer sync status and statistics.", + "/sync", + ); + return true; + } + default: + return false; + } +}; diff --git a/src/tui/tui-cmd-misc.ts b/src/tui/tui-cmd-misc.ts new file mode 100644 index 00000000..09dc30cd --- /dev/null +++ b/src/tui/tui-cmd-misc.ts @@ -0,0 +1,251 @@ +/** + * Miscellaneous command group. + * Commands: help, status, undo, abort, settings, bug, init, exit, quit, + * onboard, batch, vim, compact, default (markdown/fallback) + */ + +import { execSync } from "node:child_process"; +import { expandMarkdownCommand, findMarkdownCommand } from "../commands/markdown-commands.js"; +import { helpText } from "./commands.js"; +import { undo, listUndoEntries } from "./undo-manager.js"; +import { compactMessages } from "./compact-handler.js"; +import { formatStatusSummary } from "./tui-status-summary.js"; +import type { GatewayStatusSummary } from "./tui-types.js"; +import type { CommandGroupHandler } from "./tui-cmd-types.js"; + +export const miscCommands: CommandGroupHandler = async (ctx, name, args, raw) => { + const { client, chatLog, tui, state, loadHistory, abortActive } = ctx; + + switch (name) { + case "help": + chatLog.addSystem( + helpText({ + provider: state.sessionInfo.modelProvider, + model: state.sessionInfo.model, + }), + ); + return true; + case "status": + try { + const status = await client.getStatus(); + if (typeof status === "string") { + chatLog.addSystem(status); + return true; + } + if (status && typeof status === "object") { + const lines = formatStatusSummary(status as GatewayStatusSummary); + for (const line of lines) { + chatLog.addSystem(line); + } + return true; + } + chatLog.addSystem("status: unknown response"); + } catch (err) { + chatLog.addSystem(`status failed: ${String(err)}`); + } + return true; + case "vim": + chatLog.addSystem("vim mode is not available in this build"); + return true; + case "undo": { + const cwd = process.cwd(); + if (args === "list") { + const entries = listUndoEntries(cwd); + if (entries.length === 0) { + chatLog.addSystem("No undo points available."); + } else { + const lines = entries.map( + (e) => ` [${e.index}] ${e.label}${e.timestamp ? ` (${e.timestamp})` : ""}`, + ); + chatLog.addSystem(`Undo points:\n${lines.join("\n")}`); + } + } else { + const result = undo(cwd); + chatLog.addSystem(result.message); + } + return true; + } + case "abort": + await abortActive(); + return true; + case "settings": + ctx.openSettings(); + return true; + case "bug": { + const url = "https://github.com/ApiliumCode/mayros/issues/new"; + try { + const openCmd = + process.platform === "darwin" + ? "open" + : process.platform === "win32" + ? "start" + : "xdg-open"; + execSync(`${openCmd} ${url}`, { stdio: "ignore" }); + chatLog.addSystem(`Opened ${url}`); + } catch { + chatLog.addSystem(`Report bugs at: ${url}`); + } + return true; + } + case "init": { + try { + const fs = await import("node:fs"); + const path = await import("node:path"); + const configPath = path.join(process.cwd(), "mayros.json"); + if (fs.existsSync(configPath)) { + chatLog.addSystem("mayros.json already exists in this directory"); + return true; + } + const pkg = fs.existsSync(path.join(process.cwd(), "package.json")) + ? JSON.parse(fs.readFileSync(path.join(process.cwd(), "package.json"), "utf-8")) + : null; + const projectName = pkg?.name ?? path.basename(process.cwd()); + const config = { + $schema: "https://apilium.com/schemas/mayros/v1.json", + meta: { lastTouchedVersion: "0.1.5" }, + ui: { theme: "dark" }, + agents: { defaults: { agentId: projectName } }, + }; + fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n"); + chatLog.addSystem(`Created ${configPath}`); + } catch (err) { + chatLog.addSystem(`init failed: ${String(err)}`); + } + return true; + } + case "exit": + case "quit": + client.stop(); + tui.stop(); + process.exit(0); + return true; + case "compact": { + try { + const history = (await client.loadHistory({ + sessionKey: state.currentSessionKey, + })) as { messages?: Array> }; + const rawMessages = history?.messages ?? []; + if (rawMessages.length === 0) { + chatLog.addSystem("nothing to compact"); + return true; + } + const mapped = rawMessages + .filter( + (m) => + typeof m === "object" && m !== null && (m.role === "user" || m.role === "assistant"), + ) + .map((m) => ({ + role: String(m.role ?? "user"), + content: typeof m.content === "string" ? m.content : "", + })); + const compactResult = await compactMessages({ + messages: mapped, + sessionKey: state.currentSessionKey, + }); + // Send the summary back to the gateway: truncate the transcript and + // inject the condensed summary as a system message so the agent + // retains the extracted context going forward. + await client.compactSession({ + key: state.currentSessionKey, + summaryMessage: compactResult.summary, + }); + chatLog.addSystem( + `Compacted ${compactResult.originalCount} messages \u2192 summary (${compactResult.knowledgeItems} knowledge items extracted)`, + ); + await loadHistory(); + } catch (err) { + chatLog.addSystem(`compact failed: ${String(err)}`); + } + return true; + } + case "batch": { + if (!args) { + chatLog.addSystem("usage: /batch [--concurrency N] [--thinking ]"); + return true; + } + // Parse optional flags from args: [--concurrency N] [--thinking level] + const batchArgParts = args.split(/\s+/); + const batchFile = batchArgParts[0] ?? ""; + const batchExtraArgs = batchArgParts.slice(1).join(" "); + + // Verify file exists before attempting to run + try { + const fs = await import("node:fs"); + if (!fs.existsSync(batchFile)) { + chatLog.addSystem(`batch: file not found: ${batchFile}`); + return true; + } + } catch { + chatLog.addSystem(`batch: cannot check file: ${batchFile}`); + return true; + } + + chatLog.addSystem( + `Running batch: ${batchFile}${batchExtraArgs ? " " + batchExtraArgs : ""}...`, + ); + tui.requestRender(); + + try { + const { parseInputFile } = await import("../cli/batch-cli.js"); + const fs = await import("node:fs"); + const content = fs.readFileSync(batchFile, "utf-8"); + const items = parseInputFile(content); + if (items.length === 0) { + chatLog.addSystem("batch: no valid prompts found in file"); + return true; + } + + chatLog.addSystem(`batch: processing ${items.length} prompt(s) sequentially...`); + tui.requestRender(); + + let completed = 0; + const errors: string[] = []; + for (const item of items) { + try { + chatLog.addSystem( + `batch [${completed + 1}/${items.length}]: ${item.prompt.slice(0, 60)}${item.prompt.length > 60 ? "…" : ""}`, + ); + tui.requestRender(); + await ctx.sendMessage(item.context ? `${item.context}\n\n${item.prompt}` : item.prompt); + completed++; + } catch (err) { + const msg = `batch [${completed + 1}/${items.length}] error: ${String(err)}`; + chatLog.addSystem(msg); + errors.push(msg); + completed++; + } + } + chatLog.addSystem(`batch done: ${completed - errors.length} ok, ${errors.length} errors`); + } catch (err) { + chatLog.addSystem(`batch failed: ${String(err)}`); + } + return true; + } + case "onboard": { + try { + chatLog.addSystem("Launching onboarding wizard — stopping TUI..."); + tui.requestRender(); + client.stop(); + tui.stop(); + const { onboardCommand } = await import("../commands/onboard.js"); + const { defaultRuntime } = await import("../runtime.js"); + await onboardCommand({}, defaultRuntime); + process.exit(0); + } catch (err) { + chatLog.addSystem(`onboard failed: ${String(err)}`); + } + return true; + } + default: { + // Check for user-defined markdown commands before sending raw + const mdCmd = findMarkdownCommand(name); + if (mdCmd) { + const expanded = expandMarkdownCommand(mdCmd, args); + await ctx.sendMessage(expanded); + } else { + await ctx.sendMessage(raw); + } + return true; + } + } +}; diff --git a/src/tui/tui-cmd-session.ts b/src/tui/tui-cmd-session.ts new file mode 100644 index 00000000..63f4df8d --- /dev/null +++ b/src/tui/tui-cmd-session.ts @@ -0,0 +1,153 @@ +/** + * Session/model/search command group. + * Commands: agent, agents, session, sessions, model, models, new, reset, search + */ + +import { SessionManager, formatSessionLine } from "./session-manager.js"; +import type { CommandGroupHandler } from "./tui-cmd-types.js"; + +export const sessionCommands: CommandGroupHandler = async (ctx, name, args, _raw) => { + const { client, chatLog, state, refreshSessionInfo, loadHistory, setSession, formatSessionKey } = + ctx; + + switch (name) { + case "agent": + case "agents": + if (!args) { + await ctx.openAgentSelector(); + } else { + await ctx.setAgent(args); + } + return true; + case "session": + case "sessions": { + const sessionSubCmd = args.split(/\s+/)[0]?.toLowerCase(); + const sessionArgs = args.slice((sessionSubCmd ?? "").length).trim(); + + if (!args) { + await ctx.openSessionSelector(); + } else if (sessionSubCmd === "list") { + try { + const mgr = new SessionManager({ + client, + currentAgentId: state.currentAgentId, + }); + const sessions = await mgr.listSessions({ limit: 20 }); + if (sessions.length === 0) { + chatLog.addSystem("no sessions found"); + } else { + const lines = sessions.map((s) => formatSessionLine(s, formatSessionKey)); + chatLog.addSystem( + `Sessions (${sessions.length}):\n${lines.map((l) => ` ${l}`).join("\n")}`, + ); + } + } catch (err) { + chatLog.addSystem(`session list failed: ${String(err)}`); + } + } else if (sessionSubCmd === "rename") { + if (!sessionArgs) { + chatLog.addSystem("usage: /session rename "); + } else { + try { + const mgr = new SessionManager({ + client, + currentAgentId: state.currentAgentId, + }); + await mgr.renameSession(state.currentSessionKey, sessionArgs); + chatLog.addSystem(`session renamed to "${sessionArgs}"`); + await refreshSessionInfo(); + } catch (err) { + chatLog.addSystem(`session rename failed: ${String(err)}`); + } + } + } else if (sessionSubCmd === "delete") { + if (!sessionArgs) { + chatLog.addSystem("usage: /session delete "); + } else { + try { + const mgr = new SessionManager({ + client, + currentAgentId: state.currentAgentId, + }); + await mgr.deleteSession(sessionArgs); + chatLog.addSystem(`session ${sessionArgs} deleted`); + } catch (err) { + chatLog.addSystem(`session delete failed: ${String(err)}`); + } + } + } else { + // Treat as session key for resume + await setSession(args); + } + return true; + } + case "model": + case "models": + if (!args) { + await ctx.openModelSelector(); + } else { + try { + const result = await client.patchSession({ + key: state.currentSessionKey, + model: args, + }); + chatLog.addSystem(`model set to ${args}`); + ctx.applySessionInfoFromPatch(result); + await refreshSessionInfo(); + } catch (err) { + chatLog.addSystem(`model set failed: ${String(err)}`); + } + } + return true; + case "new": + case "reset": + try { + // Clear token counts immediately to avoid stale display (#1523) + state.sessionInfo.inputTokens = null; + state.sessionInfo.outputTokens = null; + state.sessionInfo.totalTokens = null; + ctx.tui.requestRender(); + + await client.resetSession(state.currentSessionKey, name); + chatLog.addSystem(`session ${state.currentSessionKey} reset`); + await loadHistory(); + } catch (err) { + chatLog.addSystem(`reset failed: ${String(err)}`); + } + return true; + case "search": { + const query = args.trim(); + if (!query) { + chatLog.addSystem("Usage: /search "); + return true; + } + chatLog.addSystem(`Searching for "${query}"...`); + try { + const { searchSessions } = await import("../infra/session-search.js"); + const summary = await searchSessions({ query, limit: 10 }); + if (summary.results.length === 0) { + chatLog.addSystem( + `No results found for "${query}" (${summary.sessionsSearched} sessions searched)`, + ); + return true; + } + const lines = [ + `Found ${summary.totalMatches} result(s) in ${summary.sessionsSearched} sessions:`, + ]; + for (const r of summary.results) { + const date = new Date(r.timestamp).toISOString().slice(0, 16).replace("T", " "); + const tag = r.role === "user" ? "[You]" : "[AI]"; + lines.push( + `${date} ${tag} (${r.sessionId}): ${r.snippet.replace(/\n/g, " ").slice(0, 100)}`, + ); + } + chatLog.addSystem(lines.join("\n")); + } catch (err) { + chatLog.addSystem(`search failed: ${String(err)}`); + } + return true; + } + default: + return false; + } +}; diff --git a/src/tui/tui-cmd-teleport.ts b/src/tui/tui-cmd-teleport.ts new file mode 100644 index 00000000..6060c59c --- /dev/null +++ b/src/tui/tui-cmd-teleport.ts @@ -0,0 +1,131 @@ +/** + * Teleport command group (session export/import across devices). + * Command: teleport + */ + +import { copyToClipboard } from "../infra/clipboard.js"; +import { + exportSession, + importSession, + validatePayloadSize, + MAX_EXPORT_MESSAGES, + type TeleportPayload, +} from "./session-teleport.js"; +import type { CommandGroupHandler } from "./tui-cmd-types.js"; + +export const teleportCommands: CommandGroupHandler = async (ctx, name, args, _raw) => { + const { client, chatLog, tui, state } = ctx; + + if (name !== "teleport") { + return false; + } + + const subCmd = args.split(/\s+/)[0]?.toLowerCase(); + if (subCmd === "export") { + // Populate messages from actual session history + let messages: TeleportPayload["messages"] = []; + try { + const history = await client.loadHistory({ + sessionKey: state.currentSessionKey, + limit: MAX_EXPORT_MESSAGES, + }); + if (Array.isArray(history)) { + messages = history.map((m: { role?: string; content?: string; timestamp?: string }) => ({ + role: (m.role as "user" | "assistant" | "system") ?? "user", + content: typeof m.content === "string" ? m.content : JSON.stringify(m.content ?? ""), + ...(m.timestamp ? { timestamp: m.timestamp } : {}), + })); + } + } catch { + // If history load fails, export with empty messages (degraded mode) + } + + const payload: TeleportPayload = { + version: 1, + timestamp: new Date().toISOString(), + agentId: state.currentAgentId, + sessionKey: state.currentSessionKey, + messages, + metadata: {}, + }; + + const sizeError = validatePayloadSize(payload); + if (sizeError) { + chatLog.addSystem(`Export warning: ${sizeError}`); + } + + const token = exportSession(payload); + // Copy to clipboard using the cross-platform clipboard helper + // (pbcopy / xclip / wl-copy / clip.exe / PowerShell fallback). + try { + const ok = await copyToClipboard(token); + chatLog.addSystem( + 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}`); + } + return true; + } + + if (subCmd === "import") { + const token = args.slice("import".length).trim(); + if (!token) { + chatLog.addSystem("Usage: /teleport import "); + return true; + } + try { + const payload = importSession(token); + + // Render each message in the chat log display and inject + // assistant messages into the current session via the gateway. + // User and system messages are shown visually only — they are + // part of the imported context that the user brought over. + let injected = 0; + let failed = 0; + for (const msg of payload.messages) { + if (msg.role === "user") { + chatLog.addUser(msg.content); + } else if (msg.role === "system") { + chatLog.addSystem(msg.content); + } else if (msg.role === "assistant") { + // Persist to the session transcript so the model sees the history + try { + await client.injectChat({ + sessionKey: state.currentSessionKey, + message: msg.content, + label: "teleport", + }); + chatLog.finalizeAssistant(msg.content); + injected++; + } catch { + // Fall back to display-only if injection fails + chatLog.finalizeAssistant(msg.content); + failed++; + } + } + } + + const summaryParts = [`Session imported: ${payload.messages.length} messages`]; + summaryParts.push(`from agent "${payload.agentId}" (${payload.timestamp})`); + if (injected > 0) { + summaryParts.push(`${injected} assistant message(s) written to session transcript`); + } + if (failed > 0) { + summaryParts.push( + `${failed} assistant message(s) displayed only (transcript write failed)`, + ); + } + chatLog.addSystem(summaryParts.join(" — ")); + tui.requestRender(); + } catch (err) { + chatLog.addSystem(`Import failed: ${String(err)}`); + } + return true; + } + + chatLog.addSystem("Usage: /teleport export | /teleport import "); + return true; +}; diff --git a/src/tui/tui-cmd-types.ts b/src/tui/tui-cmd-types.ts new file mode 100644 index 00000000..da1b1930 --- /dev/null +++ b/src/tui/tui-cmd-types.ts @@ -0,0 +1,59 @@ +/** + * Shared types for TUI command handler groups. + * + * Each command group module exports a function matching the + * CommandGroupHandler signature. The dispatcher in tui-command-handlers.ts + * iterates over the groups until one returns true (handled). + */ + +import type { Component, TUI } from "@earendil-works/pi-tui"; +import type { SessionsPatchResult } from "../gateway/protocol/index.js"; +import type { ChatLog } from "./components/chat-log.js"; +import type { GatewayChatClient } from "./gateway-chat.js"; +import type { TuiOptions, TuiStateAccess } from "./tui-types.js"; + +/** + * Full context passed to every command group. Includes the base dependencies + * (client, chatLog, tui, state, ...) plus the shared closures (sendMessage, + * setAgent, selectors) that are constructed once in createCommandHandlers + * and reused across groups. + */ +export type CommandHandlerContext = { + client: GatewayChatClient; + chatLog: ChatLog; + tui: TUI; + opts: TuiOptions; + state: TuiStateAccess; + deliverDefault: boolean; + openOverlay: (component: Component) => void; + closeOverlay: () => void; + refreshSessionInfo: () => Promise; + loadHistory: () => Promise; + setSession: (key: string) => Promise; + refreshAgents: () => Promise; + abortActive: () => Promise; + setActivityStatus: (text: string) => void; + formatSessionKey: (key: string) => string; + applySessionInfoFromPatch: (result: SessionsPatchResult) => void; + noteLocalRunId: (runId: string) => void; + forgetLocalRunId?: (runId: string) => void; + mouseHandler?: { enable(): void; disable(): void; isEnabled(): boolean }; + // Shared closures — constructed once, passed to all groups. + sendMessage: (text: string, displayText?: string) => Promise; + setAgent: (id: string) => Promise; + openModelSelector: () => Promise; + openAgentSelector: () => Promise; + openSessionSelector: () => Promise; + openSettings: () => void; +}; + +/** + * A command group handler. Returns true if the command was handled (matched + * a case), false to let the dispatcher try the next group. + */ +export type CommandGroupHandler = ( + ctx: CommandHandlerContext, + name: string, + args: string, + raw: string, +) => Promise; diff --git a/src/tui/tui-command-handlers.ts b/src/tui/tui-command-handlers.ts index c5e830ef..a56ea6d1 100644 --- a/src/tui/tui-command-handlers.ts +++ b/src/tui/tui-command-handlers.ts @@ -1,56 +1,37 @@ import { randomUUID } from "node:crypto"; 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, - resolveResponseUsageMode, -} from "../auto-reply/thinking.js"; -import { expandMarkdownCommand, findMarkdownCommand } from "../commands/markdown-commands.js"; 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 } from "node:child_process"; -import { writeFileSync } from "node:fs"; -import { helpText, parseCommand } from "./commands.js"; -import { undo, listUndoEntries } from "./undo-manager.js"; -import { - exportSession, - importSession, - validatePayloadSize, - MAX_EXPORT_MESSAGES, - type TeleportPayload, -} from "./session-teleport.js"; -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, listCustomThemeNames } from "./theme/palettes.js"; -import type { BuiltinPreset } from "./theme/palettes.js"; -import { setThemePreset, getThemePreset } from "./theme/theme.js"; +import { parseCommand } from "./commands.js"; import type { ChatLog } from "./components/chat-log.js"; import { createFilterableSelectList, createSearchableSelectList, - createSelectList, createSettingsList, } from "./components/selectors.js"; import type { ChatAttachmentInput, GatewayChatClient } from "./gateway-chat.js"; -import { formatStatusSummary } from "./tui-status-summary.js"; -import type { - AgentSummary, - GatewayStatusSummary, - TuiOptions, - TuiStateAccess, -} from "./tui-types.js"; +import { applyOutputStyle } from "./output-styles.js"; +import type { OutputStyle } from "./output-styles.js"; +import type { AgentSummary, TuiOptions, TuiStateAccess } from "./tui-types.js"; +import type { CommandHandlerContext, CommandGroupHandler } from "./tui-cmd-types.js"; +import { sessionCommands } from "./tui-cmd-session.js"; +import { configCommands } from "./tui-cmd-config.js"; +import { displayCommands } from "./tui-cmd-display.js"; +import { clipboardCommands } from "./tui-cmd-clipboard.js"; +import { ecosystemCommands } from "./tui-cmd-ecosystem.js"; +import { teleportCommands } from "./tui-cmd-teleport.js"; +import { miscCommands } from "./tui-cmd-misc.js"; + +export type { CommandHandlerContext } from "./tui-cmd-types.js"; -type CommandHandlerContext = { +/** + * Base context shape that callers (runTui) provide. The shared closures + * (sendMessage, setAgent, selectors) are constructed inside + * createCommandHandlers and added to produce the full CommandHandlerContext + * passed to each command group. + */ +type CommandHandlerBaseContext = { client: GatewayChatClient; chatLog: ChatLog; tui: TUI; @@ -72,7 +53,7 @@ type CommandHandlerContext = { mouseHandler?: { enable(): void; disable(): void; isEnabled(): boolean }; }; -export function createCommandHandlers(context: CommandHandlerContext) { +export function createCommandHandlers(base: CommandHandlerBaseContext) { const { client, chatLog, @@ -86,13 +67,12 @@ export function createCommandHandlers(context: CommandHandlerContext) { loadHistory, setSession, refreshAgents, - abortActive, setActivityStatus, formatSessionKey, applySessionInfoFromPatch, noteLocalRunId, forgetLocalRunId, - } = context; + } = base; const setAgent = async (id: string) => { state.currentAgentId = normalizeAgentId(id); @@ -273,1075 +253,6 @@ export function createCommandHandlers(context: CommandHandlerContext) { tui.requestRender(); }; - const handleCommand = async (raw: string) => { - const { name, args } = parseCommand(raw); - if (!name) { - return; - } - switch (name) { - case "help": - chatLog.addSystem( - helpText({ - provider: state.sessionInfo.modelProvider, - model: state.sessionInfo.model, - }), - ); - break; - case "status": - try { - const status = await client.getStatus(); - if (typeof status === "string") { - chatLog.addSystem(status); - break; - } - if (status && typeof status === "object") { - const lines = formatStatusSummary(status as GatewayStatusSummary); - for (const line of lines) { - chatLog.addSystem(line); - } - break; - } - chatLog.addSystem("status: unknown response"); - } catch (err) { - chatLog.addSystem(`status failed: ${String(err)}`); - } - break; - case "agent": - case "agents": - if (!args) { - await openAgentSelector(); - } else { - await setAgent(args); - } - break; - case "session": - case "sessions": { - const sessionSubCmd = args.split(/\s+/)[0]?.toLowerCase(); - const sessionArgs = args.slice((sessionSubCmd ?? "").length).trim(); - - if (!args) { - await openSessionSelector(); - } else if (sessionSubCmd === "list") { - try { - const mgr = new SessionManager({ - client, - currentAgentId: state.currentAgentId, - }); - const sessions = await mgr.listSessions({ limit: 20 }); - if (sessions.length === 0) { - chatLog.addSystem("no sessions found"); - } else { - const lines = sessions.map((s) => formatSessionLine(s, formatSessionKey)); - chatLog.addSystem( - `Sessions (${sessions.length}):\n${lines.map((l) => ` ${l}`).join("\n")}`, - ); - } - } catch (err) { - chatLog.addSystem(`session list failed: ${String(err)}`); - } - } else if (sessionSubCmd === "rename") { - if (!sessionArgs) { - chatLog.addSystem("usage: /session rename "); - } else { - try { - const mgr = new SessionManager({ - client, - currentAgentId: state.currentAgentId, - }); - await mgr.renameSession(state.currentSessionKey, sessionArgs); - chatLog.addSystem(`session renamed to "${sessionArgs}"`); - await refreshSessionInfo(); - } catch (err) { - chatLog.addSystem(`session rename failed: ${String(err)}`); - } - } - } else if (sessionSubCmd === "delete") { - if (!sessionArgs) { - chatLog.addSystem("usage: /session delete "); - } else { - try { - const mgr = new SessionManager({ - client, - currentAgentId: state.currentAgentId, - }); - await mgr.deleteSession(sessionArgs); - chatLog.addSystem(`session ${sessionArgs} deleted`); - } catch (err) { - chatLog.addSystem(`session delete failed: ${String(err)}`); - } - } - } else { - // Treat as session key for resume - await setSession(args); - } - break; - } - case "model": - case "models": - if (!args) { - await openModelSelector(); - } else { - try { - const result = await client.patchSession({ - key: state.currentSessionKey, - model: args, - }); - chatLog.addSystem(`model set to ${args}`); - applySessionInfoFromPatch(result); - await refreshSessionInfo(); - } catch (err) { - chatLog.addSystem(`model set failed: ${String(err)}`); - } - } - break; - case "think": - if (!args) { - const levels = listThinkingLevelLabels( - state.sessionInfo.modelProvider, - state.sessionInfo.model, - ); - const currentThink = state.sessionInfo.thinkingLevel ?? "medium"; - const thinkItems = levels.map((l) => ({ - value: l, - label: l === currentThink ? `${l} (current)` : l, - })); - const thinkSelector = createSelectList(thinkItems, thinkItems.length); - thinkSelector.onSelect = (item) => { - void (async () => { - try { - const result = await client.patchSession({ - key: state.currentSessionKey, - thinkingLevel: item.value, - }); - chatLog.addSystem(`thinking set to ${item.value}`); - applySessionInfoFromPatch(result); - await refreshSessionInfo(); - } catch (err) { - chatLog.addSystem(`think failed: ${String(err)}`); - } - closeOverlay(); - tui.requestRender(); - })(); - }; - thinkSelector.onCancel = () => { - closeOverlay(); - tui.requestRender(); - }; - openOverlay(thinkSelector); - tui.requestRender(); - break; - } - try { - const result = await client.patchSession({ - key: state.currentSessionKey, - thinkingLevel: args, - }); - chatLog.addSystem(`thinking set to ${args}`); - applySessionInfoFromPatch(result); - await refreshSessionInfo(); - } catch (err) { - chatLog.addSystem(`think failed: ${String(err)}`); - } - break; - case "verbose": { - if (!args) { - const verboseOpts = ["on", "off"]; - const currentVerbose = state.sessionInfo.verboseLevel ?? "off"; - const verboseItems = verboseOpts.map((v) => ({ - value: v, - label: v === currentVerbose ? `${v} (current)` : v, - })); - const verboseSelector = createSelectList(verboseItems, verboseItems.length); - verboseSelector.onSelect = (item) => { - void (async () => { - try { - const result = await client.patchSession({ - key: state.currentSessionKey, - verboseLevel: item.value, - }); - chatLog.addSystem(`verbose set to ${item.value}`); - applySessionInfoFromPatch(result); - await loadHistory(); - } catch (err) { - chatLog.addSystem(`verbose failed: ${String(err)}`); - } - closeOverlay(); - tui.requestRender(); - })(); - }; - verboseSelector.onCancel = () => { - closeOverlay(); - tui.requestRender(); - }; - openOverlay(verboseSelector); - tui.requestRender(); - break; - } - try { - const result = await client.patchSession({ - key: state.currentSessionKey, - verboseLevel: args, - }); - chatLog.addSystem(`verbose set to ${args}`); - applySessionInfoFromPatch(result); - await loadHistory(); - } catch (err) { - chatLog.addSystem(`verbose failed: ${String(err)}`); - } - break; - } - case "reasoning": { - if (!args) { - const reasoningOpts = ["on", "off"]; - const currentReasoning = state.sessionInfo.reasoningLevel ?? "off"; - const reasoningItems = reasoningOpts.map((r) => ({ - value: r, - label: r === currentReasoning ? `${r} (current)` : r, - })); - const reasoningSelector = createSelectList(reasoningItems, reasoningItems.length); - reasoningSelector.onSelect = (item) => { - void (async () => { - try { - const result = await client.patchSession({ - key: state.currentSessionKey, - reasoningLevel: item.value, - }); - chatLog.addSystem(`reasoning set to ${item.value}`); - applySessionInfoFromPatch(result); - await refreshSessionInfo(); - } catch (err) { - chatLog.addSystem(`reasoning failed: ${String(err)}`); - } - closeOverlay(); - tui.requestRender(); - })(); - }; - reasoningSelector.onCancel = () => { - closeOverlay(); - tui.requestRender(); - }; - openOverlay(reasoningSelector); - tui.requestRender(); - break; - } - try { - const result = await client.patchSession({ - key: state.currentSessionKey, - reasoningLevel: args, - }); - chatLog.addSystem(`reasoning set to ${args}`); - applySessionInfoFromPatch(result); - await refreshSessionInfo(); - } catch (err) { - chatLog.addSystem(`reasoning failed: ${String(err)}`); - } - break; - } - case "usage": { - const normalized = args ? normalizeUsageDisplay(args) : undefined; - if (args && !normalized) { - chatLog.addSystem("usage: /usage "); - break; - } - const currentRaw = state.sessionInfo.responseUsage; - const current = resolveResponseUsageMode(currentRaw); - const next = - normalized ?? (current === "off" ? "tokens" : current === "tokens" ? "full" : "off"); - try { - const result = await client.patchSession({ - key: state.currentSessionKey, - responseUsage: next === "off" ? null : next, - }); - chatLog.addSystem(`usage footer: ${next}`); - applySessionInfoFromPatch(result); - await refreshSessionInfo(); - } catch (err) { - chatLog.addSystem(`usage failed: ${String(err)}`); - } - break; - } - case "elevated": { - if (!args) { - const elevatedOpts = ["on", "off", "ask", "full"]; - const currentElevated = state.sessionInfo.elevatedLevel ?? "off"; - const elevatedItems = elevatedOpts.map((e) => ({ - value: e, - label: e === currentElevated ? `${e} (current)` : e, - })); - const elevatedSelector = createSelectList(elevatedItems, elevatedItems.length); - elevatedSelector.onSelect = (item) => { - void (async () => { - try { - const result = await client.patchSession({ - key: state.currentSessionKey, - elevatedLevel: item.value, - }); - chatLog.addSystem(`elevated set to ${item.value}`); - applySessionInfoFromPatch(result); - await refreshSessionInfo(); - } catch (err) { - chatLog.addSystem(`elevated failed: ${String(err)}`); - } - closeOverlay(); - tui.requestRender(); - })(); - }; - elevatedSelector.onCancel = () => { - closeOverlay(); - tui.requestRender(); - }; - openOverlay(elevatedSelector); - tui.requestRender(); - break; - } - if (!["on", "off", "ask", "full"].includes(args)) { - chatLog.addSystem("usage: /elevated "); - break; - } - try { - const result = await client.patchSession({ - key: state.currentSessionKey, - elevatedLevel: args, - }); - chatLog.addSystem(`elevated set to ${args}`); - applySessionInfoFromPatch(result); - await refreshSessionInfo(); - } catch (err) { - chatLog.addSystem(`elevated failed: ${String(err)}`); - } - break; - } - case "activation": { - if (!args) { - const activationOpts = ["mention", "always"]; - const currentActivation = state.sessionInfo.groupActivation ?? "mention"; - const activationItems = activationOpts.map((a) => ({ - value: a, - label: a === currentActivation ? `${a} (current)` : a, - })); - const activationSelector = createSelectList(activationItems, activationItems.length); - activationSelector.onSelect = (item) => { - void (async () => { - try { - const result = await client.patchSession({ - key: state.currentSessionKey, - groupActivation: item.value === "always" ? "always" : "mention", - }); - chatLog.addSystem(`activation set to ${item.value}`); - applySessionInfoFromPatch(result); - await refreshSessionInfo(); - } catch (err) { - chatLog.addSystem(`activation failed: ${String(err)}`); - } - closeOverlay(); - tui.requestRender(); - })(); - }; - activationSelector.onCancel = () => { - closeOverlay(); - tui.requestRender(); - }; - openOverlay(activationSelector); - tui.requestRender(); - break; - } - try { - const result = await client.patchSession({ - key: state.currentSessionKey, - groupActivation: args === "always" ? "always" : "mention", - }); - chatLog.addSystem(`activation set to ${args}`); - applySessionInfoFromPatch(result); - await refreshSessionInfo(); - } catch (err) { - chatLog.addSystem(`activation failed: ${String(err)}`); - } - break; - } - case "context": { - const used = state.sessionInfo.totalTokens ?? 0; - const max = state.sessionInfo.contextTokens ?? 0; - const lines = formatContextVisualization({ - usedTokens: used, - maxTokens: max, - inputTokens: state.sessionInfo.inputTokens, - outputTokens: state.sessionInfo.outputTokens, - }); - for (const line of lines) { - chatLog.addSystem(line); - } - break; - } - case "diff": { - try { - const cmd = args ? `git diff -- ${args}` : "git diff"; - const raw = execSync(cmd, { encoding: "utf-8", maxBuffer: 1024 * 1024 }).trim(); - if (!raw) { - chatLog.addSystem("no changes"); - break; - } - const stats = renderDiffStats(raw); - chatLog.addSystem( - `${stats.files} file(s) changed, +${stats.additions} -${stats.deletions}`, - ); - 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) { - chatLog.addSystem(`diff failed: ${String(err)}`); - } - break; - } - case "style": { - const styleName = args.toLowerCase(); - if (!styleName) { - const currentStyle = state.outputStyle ?? "standard"; - const styleItems = OUTPUT_STYLE_NAMES.map((s) => ({ - value: s, - label: s === currentStyle ? `${s} (current)` : s, - })); - const styleSelector = createSelectList(styleItems, styleItems.length); - styleSelector.onSelect = (item) => { - state.outputStyle = item.value as OutputStyle; - chatLog.addSystem(`output style set to ${item.value}`); - closeOverlay(); - tui.requestRender(); - }; - styleSelector.onCancel = () => { - closeOverlay(); - tui.requestRender(); - }; - openOverlay(styleSelector); - tui.requestRender(); - break; - } - if (!isValidOutputStyle(styleName)) { - chatLog.addSystem(`unknown style. usage: /style <${OUTPUT_STYLE_NAMES.join("|")}>`); - break; - } - state.outputStyle = styleName; - chatLog.addSystem(`output style set to ${styleName}`); - break; - } - case "theme": { - const preset = args.toLowerCase(); - if (!preset) { - const currentTheme = getThemePreset(); - const themeItems = THEME_PRESETS.map((t) => ({ - value: t, - label: t === currentTheme ? `${t} (current)` : t, - })); - const themeSelector = createSelectList(themeItems, themeItems.length); - themeSelector.onSelect = (item) => { - setThemePreset(item.value); - chatLog.addSystem(`theme set to ${item.value}`); - closeOverlay(); - tui.requestRender(); - }; - themeSelector.onCancel = () => { - closeOverlay(); - tui.requestRender(); - }; - openOverlay(themeSelector); - tui.requestRender(); - break; - } - 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); - chatLog.addSystem(`theme set to ${preset}`); - break; - } - case "new": - case "reset": - try { - // Clear token counts immediately to avoid stale display (#1523) - state.sessionInfo.inputTokens = null; - state.sessionInfo.outputTokens = null; - state.sessionInfo.totalTokens = null; - tui.requestRender(); - - await client.resetSession(state.currentSessionKey, name); - chatLog.addSystem(`session ${state.currentSessionKey} reset`); - await loadHistory(); - } catch (err) { - chatLog.addSystem(`reset failed: ${String(err)}`); - } - break; - case "vim": { - chatLog.addSystem("vim mode is not available in this build"); - break; - } - case "permission": { - const MODES = ["auto", "ask", "deny"] as const; - type PermMode = (typeof MODES)[number]; - const mode = args.toLowerCase(); - if (!mode) { - const current = state.permissionMode ?? "auto"; - const idx = MODES.indexOf(current); - const next = MODES[(idx + 1) % MODES.length] as PermMode; - state.permissionMode = next; - chatLog.addSystem(`permission mode: ${next}`); - } else if (MODES.includes(mode as PermMode)) { - state.permissionMode = mode as PermMode; - chatLog.addSystem(`permission mode set to ${mode}`); - } else { - chatLog.addSystem("usage: /permission "); - } - break; - } - case "fast": { - const isFast = !state.fastMode; - state.fastMode = isFast; - if (isFast) { - // Save current thinking level before switching - state.previousThinkingLevel = state.sessionInfo.thinkingLevel ?? "medium"; - try { - const result = await client.patchSession({ - key: state.currentSessionKey, - thinkingLevel: "off", - }); - applySessionInfoFromPatch(result); - } catch (err) { - // Best-effort — fast mode works locally even without gateway - logger.warn("fast-mode: failed to set thinking off", { error: String(err) }); - } - state.outputStyle = "standard"; - chatLog.addSystem("fast mode enabled (thinking: off, style: standard)"); - } else { - // Restore previous thinking level - const prevLevel = state.previousThinkingLevel ?? "medium"; - try { - const result = await client.patchSession({ - key: state.currentSessionKey, - thinkingLevel: prevLevel, - }); - applySessionInfoFromPatch(result); - } catch (err) { - // Best-effort — restore may fail if gateway is unreachable - logger.warn("fast-mode: failed to restore thinking", { - level: prevLevel, - error: String(err), - }); - } - chatLog.addSystem(`fast mode disabled (thinking: ${prevLevel})`); - } - break; - } - case "compact": { - try { - const history = (await client.loadHistory({ - sessionKey: state.currentSessionKey, - })) as { messages?: Array> }; - const rawMessages = history?.messages ?? []; - if (rawMessages.length === 0) { - chatLog.addSystem("nothing to compact"); - break; - } - const mapped = rawMessages - .filter( - (m) => - typeof m === "object" && - m !== null && - (m.role === "user" || m.role === "assistant"), - ) - .map((m) => ({ - role: String(m.role ?? "user"), - content: typeof m.content === "string" ? m.content : "", - })); - const compactResult = await compactMessages({ - messages: mapped, - sessionKey: state.currentSessionKey, - }); - // Send the summary back to the gateway: truncate the transcript and - // inject the condensed summary as a system message so the agent - // retains the extracted context going forward. - await client.compactSession({ - key: state.currentSessionKey, - summaryMessage: compactResult.summary, - }); - chatLog.addSystem( - `Compacted ${compactResult.originalCount} messages \u2192 summary (${compactResult.knowledgeItems} knowledge items extracted)`, - ); - await loadHistory(); - } catch (err) { - chatLog.addSystem(`compact failed: ${String(err)}`); - } - break; - } - case "copy": { - const lastText = chatLog.getLastAssistantText(); - if (!lastText) { - chatLog.addSystem("nothing to copy"); - break; - } - try { - const ok = await copyToClipboard(lastText); - chatLog.addSystem( - ok ? "last response copied to clipboard" : "copy failed: no clipboard tool found", - ); - } catch (err) { - chatLog.addSystem(`copy failed: ${String(err)}`); - } - break; - } - case "export": { - const lastText = chatLog.getLastAssistantText(); - if (!lastText) { - chatLog.addSystem("nothing to export"); - break; - } - const filePath = args || `mayros-export-${Date.now()}.md`; - try { - writeFileSync(filePath, lastText, "utf-8"); - chatLog.addSystem(`exported to ${filePath}`); - } catch (err) { - chatLog.addSystem(`export failed: ${String(err)}`); - } - break; - } - case "undo": { - const cwd = process.cwd(); - if (args === "list") { - const entries = listUndoEntries(cwd); - if (entries.length === 0) { - chatLog.addSystem("No undo points available."); - } else { - const lines = entries.map( - (e) => ` [${e.index}] ${e.label}${e.timestamp ? ` (${e.timestamp})` : ""}`, - ); - chatLog.addSystem(`Undo points:\n${lines.join("\n")}`); - } - } else { - const result = undo(cwd); - chatLog.addSystem(result.message); - } - break; - } - case "abort": - await abortActive(); - break; - case "settings": - openSettings(); - break; - case "bug": { - const url = "https://github.com/ApiliumCode/mayros/issues/new"; - try { - const openCmd = - process.platform === "darwin" - ? "open" - : process.platform === "win32" - ? "start" - : "xdg-open"; - execSync(`${openCmd} ${url}`, { stdio: "ignore" }); - chatLog.addSystem(`Opened ${url}`); - } catch { - chatLog.addSystem(`Report bugs at: ${url}`); - } - break; - } - case "init": { - try { - const fs = await import("node:fs"); - const path = await import("node:path"); - const configPath = path.join(process.cwd(), "mayros.json"); - if (fs.existsSync(configPath)) { - chatLog.addSystem("mayros.json already exists in this directory"); - break; - } - const pkg = fs.existsSync(path.join(process.cwd(), "package.json")) - ? JSON.parse(fs.readFileSync(path.join(process.cwd(), "package.json"), "utf-8")) - : null; - const projectName = pkg?.name ?? path.basename(process.cwd()); - const config = { - $schema: "https://apilium.com/schemas/mayros/v1.json", - meta: { lastTouchedVersion: "0.1.5" }, - ui: { theme: "dark" }, - agents: { defaults: { agentId: projectName } }, - }; - fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n"); - chatLog.addSystem(`Created ${configPath}`); - } catch (err) { - chatLog.addSystem(`init failed: ${String(err)}`); - } - break; - } - case "exit": - case "quit": - client.stop(); - tui.stop(); - process.exit(0); - break; - // --- Mayros ecosystem --- - case "plan": { - const action = args || "show"; - await sendMessage(`/plan ${action}`); - break; - } - case "mouse": { - if (context.mouseHandler) { - if (context.mouseHandler.isEnabled()) { - context.mouseHandler.disable(); - chatLog.addSystem("Mouse reporting disabled — text selection enabled."); - } else { - context.mouseHandler.enable(); - chatLog.addSystem("Mouse reporting enabled."); - } - } else { - chatLog.addSystem("Mouse handler not available."); - } - break; - } - case "tools": { - await sendMessage( - "List every tool name you have access to. Output ONLY a numbered list of tool names, nothing else. " + - "Do NOT describe them. Just the names, one per line.", - "/tools", - ); - break; - } - case "kg": { - // Check if semantic memory tools are likely available by asking the gateway - const kgHint = - "You MUST use one of these tools (in order of preference): " + - "memory_stats, semantic_memory_query, semantic_memory_recall. " + - "If none of these tools are available to you, respond EXACTLY with: " + - '"[kg] Plugin memory-semantic is not loaded. Run `mayros plugins list` to check."'; - if (!args) { - await sendMessage( - `Show a knowledge graph summary. ${kgHint} Show categories, triple counts, and recent entries.`, - "/kg", - ); - } else { - await sendMessage(`Search the knowledge graph for: ${args}. ${kgHint}`, `/kg ${args}`); - } - break; - } - case "trace": { - const subCmd = args || "events"; - if (subCmd === "stats") { - await sendMessage( - "Use the trace_stats tool with no arguments to show aggregated observability statistics for the current agent.", - "/trace stats", - ); - } else if (subCmd === "explain" && args.includes(" ")) { - const eventId = args.slice("explain".length).trim(); - await sendMessage( - `Use the trace_explain tool with eventId "${eventId}" to trace the causal chain for that event.`, - `/trace explain ${eventId}`, - ); - } else { - await sendMessage( - "Use the trace_query tool with no arguments to list recent trace events for the current agent.", - "/trace events", - ); - } - break; - } - case "team": { - await sendMessage( - "Use the mesh_team_dashboard tool with no arguments to show the team dashboard with current agent status and activity.", - "/team", - ); - break; - } - case "tasks": { - await sendMessage( - "Use the agent_list_background_tasks tool with no arguments to list all background agent tasks and their current status.", - "/tasks", - ); - break; - } - case "workflow": { - if (!args) { - await sendMessage( - 'Use the mesh_run_workflow tool with action "list" to list available workflows and their status.', - "/workflow", - ); - } else { - await sendMessage(`/workflow ${args}`); - } - break; - } - case "rules": { - if (args) { - await sendMessage( - `Use the semantic_memory_recall tool to search for rules matching: ${args}`, - `/rules ${args}`, - ); - } else { - await sendMessage( - 'Use the semantic_memory_recall tool with subject pattern "rule:*" to list all active rules.', - "/rules", - ); - } - break; - } - case "mailbox": { - if (!args) { - await sendMessage( - "Use the agent_check_inbox tool with no arguments to check the inbox for new messages and show unread count.", - "/mailbox", - ); - } else { - await sendMessage(`/mailbox ${args}`); - } - break; - } - case "search": { - const query = args.trim(); - if (!query) { - chatLog.addSystem("Usage: /search "); - break; - } - chatLog.addSystem(`Searching for "${query}"...`); - try { - const { searchSessions } = await import("../infra/session-search.js"); - const summary = await searchSessions({ query, limit: 10 }); - if (summary.results.length === 0) { - chatLog.addSystem( - `No results found for "${query}" (${summary.sessionsSearched} sessions searched)`, - ); - break; - } - const lines = [ - `Found ${summary.totalMatches} result(s) in ${summary.sessionsSearched} sessions:`, - ]; - for (const r of summary.results) { - const date = new Date(r.timestamp).toISOString().slice(0, 16).replace("T", " "); - const tag = r.role === "user" ? "[You]" : "[AI]"; - lines.push( - `${date} ${tag} (${r.sessionId}): ${r.snippet.replace(/\n/g, " ").slice(0, 100)}`, - ); - } - chatLog.addSystem(lines.join("\n")); - } catch (err) { - chatLog.addSystem(`search failed: ${String(err)}`); - } - break; - } - case "batch": { - if (!args) { - chatLog.addSystem("usage: /batch [--concurrency N] [--thinking ]"); - break; - } - // Parse optional flags from args: [--concurrency N] [--thinking level] - const batchArgParts = args.split(/\s+/); - const batchFile = batchArgParts[0] ?? ""; - const batchExtraArgs = batchArgParts.slice(1).join(" "); - - // Verify file exists before attempting to run - try { - const fs = await import("node:fs"); - if (!fs.existsSync(batchFile)) { - chatLog.addSystem(`batch: file not found: ${batchFile}`); - break; - } - } catch { - chatLog.addSystem(`batch: cannot check file: ${batchFile}`); - break; - } - - chatLog.addSystem( - `Running batch: ${batchFile}${batchExtraArgs ? " " + batchExtraArgs : ""}...`, - ); - tui.requestRender(); - - try { - const { parseInputFile } = await import("../cli/batch-cli.js"); - const fs = await import("node:fs"); - const content = fs.readFileSync(batchFile, "utf-8"); - const items = parseInputFile(content); - if (items.length === 0) { - chatLog.addSystem("batch: no valid prompts found in file"); - break; - } - - chatLog.addSystem(`batch: processing ${items.length} prompt(s) sequentially...`); - tui.requestRender(); - - let completed = 0; - const errors: string[] = []; - for (const item of items) { - try { - chatLog.addSystem( - `batch [${completed + 1}/${items.length}]: ${item.prompt.slice(0, 60)}${item.prompt.length > 60 ? "…" : ""}`, - ); - tui.requestRender(); - await sendMessage(item.context ? `${item.context}\n\n${item.prompt}` : item.prompt); - completed++; - } catch (err) { - const msg = `batch [${completed + 1}/${items.length}] error: ${String(err)}`; - chatLog.addSystem(msg); - errors.push(msg); - completed++; - } - } - chatLog.addSystem(`batch done: ${completed - errors.length} ok, ${errors.length} errors`); - } catch (err) { - chatLog.addSystem(`batch failed: ${String(err)}`); - } - break; - } - case "teleport": { - const subCmd = args.split(/\s+/)[0]?.toLowerCase(); - if (subCmd === "export") { - // Populate messages from actual session history - let messages: TeleportPayload["messages"] = []; - try { - const history = await client.loadHistory({ - sessionKey: state.currentSessionKey, - limit: MAX_EXPORT_MESSAGES, - }); - if (Array.isArray(history)) { - messages = history.map( - (m: { role?: string; content?: string; timestamp?: string }) => ({ - role: (m.role as "user" | "assistant" | "system") ?? "user", - content: - typeof m.content === "string" ? m.content : JSON.stringify(m.content ?? ""), - ...(m.timestamp ? { timestamp: m.timestamp } : {}), - }), - ); - } - } catch { - // If history load fails, export with empty messages (degraded mode) - } - - const payload: TeleportPayload = { - version: 1, - timestamp: new Date().toISOString(), - agentId: state.currentAgentId, - sessionKey: state.currentSessionKey, - messages, - metadata: {}, - }; - - const sizeError = validatePayloadSize(payload); - if (sizeError) { - chatLog.addSystem(`Export warning: ${sizeError}`); - } - - const token = exportSession(payload); - // Copy to clipboard using the cross-platform clipboard helper - // (pbcopy / xclip / wl-copy / clip.exe / PowerShell fallback). - try { - const ok = await copyToClipboard(token); - chatLog.addSystem( - 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}`); - } - } else if (subCmd === "import") { - const token = args.slice("import".length).trim(); - if (!token) { - chatLog.addSystem("Usage: /teleport import "); - break; - } - try { - const payload = importSession(token); - - // Render each message in the chat log display and inject - // assistant messages into the current session via the gateway. - // User and system messages are shown visually only — they are - // part of the imported context that the user brought over. - let injected = 0; - let failed = 0; - for (const msg of payload.messages) { - if (msg.role === "user") { - chatLog.addUser(msg.content); - } else if (msg.role === "system") { - chatLog.addSystem(msg.content); - } else if (msg.role === "assistant") { - // Persist to the session transcript so the model sees the history - try { - await client.injectChat({ - sessionKey: state.currentSessionKey, - message: msg.content, - label: "teleport", - }); - chatLog.finalizeAssistant(msg.content); - injected++; - } catch { - // Fall back to display-only if injection fails - chatLog.finalizeAssistant(msg.content); - failed++; - } - } - } - - const summaryParts = [`Session imported: ${payload.messages.length} messages`]; - summaryParts.push(`from agent "${payload.agentId}" (${payload.timestamp})`); - if (injected > 0) { - summaryParts.push(`${injected} assistant message(s) written to session transcript`); - } - if (failed > 0) { - summaryParts.push( - `${failed} assistant message(s) displayed only (transcript write failed)`, - ); - } - chatLog.addSystem(summaryParts.join(" — ")); - tui.requestRender(); - } catch (err) { - chatLog.addSystem(`Import failed: ${String(err)}`); - } - } else { - chatLog.addSystem("Usage: /teleport export | /teleport import "); - } - break; - } - case "sync": { - await sendMessage( - "Use the cortex_sync_status tool with no arguments to show Cortex peer sync status and statistics.", - "/sync", - ); - break; - } - case "onboard": { - try { - chatLog.addSystem("Launching onboarding wizard — stopping TUI..."); - tui.requestRender(); - client.stop(); - tui.stop(); - const { onboardCommand } = await import("../commands/onboard.js"); - const { defaultRuntime } = await import("../runtime.js"); - await onboardCommand({}, defaultRuntime); - process.exit(0); - } catch (err) { - chatLog.addSystem(`onboard failed: ${String(err)}`); - } - break; - } - default: { - // Check for user-defined markdown commands before sending raw - const mdCmd = findMarkdownCommand(name); - if (mdCmd) { - const expanded = expandMarkdownCommand(mdCmd, args); - await sendMessage(expanded); - } else { - await sendMessage(raw); - } - break; - } - } - tui.requestRender(); - }; - const sendMessage = async (text: string, displayText?: string) => { try { chatLog.addUser(displayText ?? text); @@ -1390,6 +301,44 @@ export function createCommandHandlers(context: CommandHandlerContext) { tui.requestRender(); }; + // Full context with shared closures, passed to every command group. + const ctx: CommandHandlerContext = { + ...base, + sendMessage, + setAgent, + openModelSelector, + openAgentSelector, + openSessionSelector, + openSettings, + }; + + // Groups are tried in order; the first to return true (handled) wins. + const groups: CommandGroupHandler[] = [ + sessionCommands, + configCommands, + displayCommands, + clipboardCommands, + ecosystemCommands, + teleportCommands, + miscCommands, + ]; + + const handleCommand = async (raw: string) => { + const { name, args } = parseCommand(raw); + if (!name) { + return; + } + for (const g of groups) { + if (await g(ctx, name, args, raw)) { + tui.requestRender(); + return; + } + } + // No group matched — should not happen since miscCommands has a default + // fallback, but guard defensively. + tui.requestRender(); + }; + return { handleCommand, sendMessage, diff --git a/src/tui/tui-connection.ts b/src/tui/tui-connection.ts new file mode 100644 index 00000000..5ec6fd6b --- /dev/null +++ b/src/tui/tui-connection.ts @@ -0,0 +1,121 @@ +/** + * Gateway connection handlers extracted from tui.ts. + * + * Wires the GatewayChatClient lifecycle callbacks (onEvent, onConnected, + * onDisconnected, onGap) and starts the client + TUI. The pairing/gateway-down + * hint flags are internal to this module since they are only relevant during + * connection transitions. + */ + +import type { ChatLog } from "./components/chat-log.js"; +import type { GatewayChatClient } from "./gateway-chat.js"; +import { isLoopbackHost } from "../gateway/net.js"; +import type { TuiOptions, TuiStateAccess } from "./tui-types.js"; +import type { Component, TUI } from "@earendil-works/pi-tui"; +import { resolveGatewayDisconnectState, tryInlinePairingApproval } from "./tui-helpers.js"; + +export interface ConnectionHandlerDeps { + client: GatewayChatClient; + tui: TUI; + chatLog: ChatLog; + opts: TuiOptions; + state: TuiStateAccess; + autoMessage: string | undefined; + createWelcomeScreen: () => Component; + refreshAgents: () => Promise; + refreshSessionInfo: () => Promise; + loadHistory: () => Promise; + sendMessage: (text: string) => Promise; + updateHeader: () => void; + updateFooter: () => void; + setConnectionStatus: (text: string, ttlMs?: number) => void; + setActivityStatus: (text: string) => void; + handleChatEvent: (payload: unknown) => void; + handleAgentEvent: (payload: unknown) => void; +} + +/** + * Wires all client callbacks. Internal flags (pairingHintShown, + * gatewayDownHintShown, wasDisconnected) track hint deduplication across + * reconnect cycles. + */ +export function wireConnectionHandlers(deps: ConnectionHandlerDeps): void { + const { client, tui, chatLog, opts, state } = deps; + + let pairingHintShown = false; + let gatewayDownHintShown = false; + let wasDisconnected = false; + + client.onEvent = (evt) => { + if (evt.event === "chat") { + deps.handleChatEvent(evt.payload); + } + if (evt.event === "agent") { + deps.handleAgentEvent(evt.payload); + } + }; + + client.onConnected = () => { + state.isConnected = true; + pairingHintShown = false; + gatewayDownHintShown = false; + const reconnected = wasDisconnected; + wasDisconnected = false; + deps.setConnectionStatus("connected"); + void (async () => { + await deps.refreshAgents(); + deps.updateHeader(); + if (opts.cleanStart && !reconnected) { + chatLog.clearAll(); + chatLog.addWelcome(deps.createWelcomeScreen()); + state.historyLoaded = true; + await deps.refreshSessionInfo(); + } else { + await deps.loadHistory(); + } + deps.setConnectionStatus(reconnected ? "gateway reconnected" : "gateway connected", 4000); + tui.requestRender(); + if (!state.autoMessageSent && deps.autoMessage) { + state.autoMessageSent = true; + await deps.sendMessage(deps.autoMessage); + } + deps.updateFooter(); + tui.requestRender(); + })(); + }; + + client.onDisconnected = (reason) => { + state.isConnected = false; + wasDisconnected = true; + state.historyLoaded = false; + const disconnectState = resolveGatewayDisconnectState(reason); + deps.setConnectionStatus(disconnectState.connectionStatus, 5000); + deps.setActivityStatus(disconnectState.activityStatus); + if (disconnectState.pairingHint && !pairingHintShown) { + pairingHintShown = true; + if (isLoopbackHost(new URL(client.connection.url).hostname)) { + void tryInlinePairingApproval().then((ok) => { + if (ok) { + chatLog.addSystem("Device paired. Reconnecting..."); + } else { + chatLog.addSystem(disconnectState.pairingHint!); + } + tui.requestRender(); + }); + } else { + chatLog.addSystem(disconnectState.pairingHint); + } + } + if (disconnectState.gatewayDownHint && !gatewayDownHintShown) { + gatewayDownHintShown = true; + chatLog.addSystem(disconnectState.gatewayDownHint); + } + deps.updateFooter(); + tui.requestRender(); + }; + + client.onGap = (info) => { + deps.setConnectionStatus(`event gap: expected ${info.expected}, got ${info.received}`, 5000); + tui.requestRender(); + }; +} diff --git a/src/tui/tui-keybindings.ts b/src/tui/tui-keybindings.ts new file mode 100644 index 00000000..c68e3d00 --- /dev/null +++ b/src/tui/tui-keybindings.ts @@ -0,0 +1,119 @@ +/** + * Editor key binding wiring extracted from tui.ts. + * + * Wires the CustomEditor's onSubmit/onEscape/onCtrl-X/onShiftTab callbacks + * to their handlers. Mutable TUI state is accessed via the shared + * TuiStateAccess object so changes propagate to the rest of runTui. + */ + +import type { ChatLog } from "./components/chat-log.js"; +import type { CustomEditor } from "./components/custom-editor.js"; +import type { GatewayChatClient } from "./gateway-chat.js"; +import type { MouseHandler } from "./mouse-handler.js"; +import { nextSectionState, type SectionState, type TuiStateAccess } from "./tui-types.js"; +import type { TUI } from "@earendil-works/pi-tui"; + +export interface EditorKeyBindingDeps { + editor: CustomEditor; + tui: TUI; + chatLog: ChatLog; + client: GatewayChatClient; + state: TuiStateAccess; + mouseHandler: MouseHandler; + abortActive: () => Promise; + setActivityStatus: (text: string) => void; + loadHistory: () => Promise; + openModelSelector: () => Promise; + openAgentSelector: () => Promise; + openSessionSelector: () => Promise; +} + +/** + * Wires all editor key callbacks. The onSubmit handler must be set by the + * caller before calling this (it depends on the submit coalescer), so this + * function only wires onEscape and the Ctrl-X/ShiftTab bindings. + */ +export function wireEditorKeyBindings(deps: EditorKeyBindingDeps): void { + const { editor, tui, chatLog, client, state, mouseHandler } = deps; + + editor.onEscape = () => { + void deps.abortActive(); + }; + + 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 (state.activeChatRunId) { + void deps.abortActive(); + return; + } + if (editor.getText().trim().length > 0) { + editor.setText(""); + deps.setActivityStatus("cleared input"); + tui.requestRender(); + return; + } + if (now - state.lastCtrlCAt < 1000) { + mouseHandler.disable(); + client.stop(); + tui.stop(); + process.exit(0); + } + state.lastCtrlCAt = now; + deps.setActivityStatus("press ctrl+c again to exit"); + tui.requestRender(); + }; + + editor.onCtrlD = () => { + mouseHandler.disable(); + client.stop(); + tui.stop(); + process.exit(0); + }; + + editor.onCtrlO = () => { + const next: SectionState = nextSectionState(state.toolSectionState); + state.toolSectionState = next; + state.toolsExpanded = next === "expanded"; + chatLog.setToolSectionState(next); + deps.setActivityStatus(`tools: ${next}`); + tui.requestRender(); + }; + + editor.onCtrlL = () => { + void deps.openModelSelector(); + }; + + editor.onCtrlG = () => { + void deps.openAgentSelector(); + }; + + editor.onCtrlP = () => { + void deps.openSessionSelector(); + }; + + editor.onCtrlT = () => { + const next: SectionState = nextSectionState(state.thinkingSectionState); + state.thinkingSectionState = next; + // 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). + state.showThinking = next !== "hidden"; + void deps.loadHistory(); + }; + + editor.onShiftTab = () => { + const modes: Array<"auto" | "ask" | "deny"> = ["auto", "ask", "deny"]; + const current = state.permissionMode ?? "auto"; + const idx = modes.indexOf(current); + const nextMode = modes[(idx + 1) % modes.length] ?? "auto"; + state.permissionMode = nextMode; + deps.setActivityStatus(`permission: ${nextMode}`); + tui.requestRender(); + }; +} diff --git a/src/tui/tui-status-renderer.ts b/src/tui/tui-status-renderer.ts new file mode 100644 index 00000000..2909fcbc --- /dev/null +++ b/src/tui/tui-status-renderer.ts @@ -0,0 +1,301 @@ +/** + * Status/footer renderer extracted from tui.ts. + * + * Encapsulates all status display state and rendering logic that was + * previously inlined in runTui(). The renderer owns the busy/idle timers, + * the waiting-status animation, and the connection/activity labels. + */ + +import { Container, Loader, Text, TUI } from "@earendil-works/pi-tui"; +import { formatTokens } from "./tui-formatters.js"; +import type { SessionInfo } from "./tui-types.js"; +import { theme } from "./theme/theme.js"; +import { buildWaitingStatusMessage, defaultWaitingPhrases } from "./tui-waiting.js"; + +/** States that show a busy spinner with elapsed time. */ +const BUSY_STATES = new Set(["sending", "waiting", "streaming", "running"]); + +export interface StatusRendererDeps { + tui: TUI; + statusContainer: Container; + header: Text; + footer: Text; + gatewayUrl: string; + formatSessionKey: (key: string) => string; + formatAgentLabel: (id: string) => string; + // Lazy reads from runTui state (these change over time). + getCurrentSessionKey: () => string; + getCurrentAgentId: () => string; + getSessionInfo: () => SessionInfo; + getPermissionMode: () => "auto" | "ask" | "deny"; + getFastMode: () => boolean; +} + +export interface StatusRenderer { + /** Re-render the header line (gateway URL, agent, session). */ + updateHeader(): void; + /** Re-render the footer line (agent, session, model, tokens, flags). */ + updateFooter(): void; + /** Re-render the status area based on current activity/connection state. */ + renderStatus(): void; + /** Set the connection label, optionally with a TTL after which it reverts. */ + setConnectionStatus(text: string, ttlMs?: number): void; + /** Set the activity label and re-render the status area. */ + setActivityStatus(text: string): void; + // State delegation — the runTui `state` object proxies these. + activityStatus: string; + connectionStatus: string; + isConnected: boolean; + statusTimeout: ReturnType | null; +} + +export function createStatusRenderer(deps: StatusRendererDeps): StatusRenderer { + const { tui, statusContainer, header, footer, gatewayUrl } = deps; + + // Delegated state (read by runTui via the `state` object). + let activityStatus = "idle"; + let connectionStatus = "connecting"; + let isConnected = false; + let statusTimeout: ReturnType | null = null; + + // Internal rendering state (never accessed outside the renderer). + let statusTimer: ReturnType | null = null; + let statusStartedAt: number | null = null; + let lastActivityStatus = activityStatus; + let statusText: Text | null = null; + let statusLoader: Loader | null = null; + let waitingTick = 0; + let waitingTimer: ReturnType | null = null; + let waitingPhrase: string | null = null; + + const formatElapsed = (startMs: number) => { + const totalSeconds = Math.max(0, Math.floor((Date.now() - startMs) / 1000)); + if (totalSeconds < 60) { + return `${totalSeconds}s`; + } + const minutes = Math.floor(totalSeconds / 60); + const seconds = totalSeconds % 60; + return `${minutes}m ${seconds}s`; + }; + + const ensureStatusText = () => { + if (statusText) { + return; + } + statusContainer.clear(); + statusLoader?.stop(); + statusLoader = null; + statusText = new Text("", 1, 0); + statusContainer.addChild(statusText); + }; + + const ensureStatusLoader = () => { + if (statusLoader) { + return; + } + statusContainer.clear(); + statusText = null; + statusLoader = new Loader( + tui, + (spinner) => theme.accent(spinner), + (text) => theme.bold(theme.accentSoft(text)), + "", + ); + statusContainer.addChild(statusLoader); + }; + + const updateBusyStatusMessage = () => { + if (!statusLoader || !statusStartedAt) { + return; + } + const elapsed = formatElapsed(statusStartedAt); + + if (activityStatus === "waiting") { + waitingTick++; + statusLoader.setMessage( + buildWaitingStatusMessage({ + theme, + tick: waitingTick, + elapsed, + connectionStatus, + phrases: waitingPhrase ? [waitingPhrase] : undefined, + }), + ); + return; + } + + statusLoader.setMessage(`${activityStatus} • ${elapsed} | ${connectionStatus}`); + }; + + const startStatusTimer = () => { + if (statusTimer) { + return; + } + statusTimer = setInterval(() => { + if (!BUSY_STATES.has(activityStatus)) { + return; + } + updateBusyStatusMessage(); + }, 1000); + }; + + const stopStatusTimer = () => { + if (!statusTimer) { + return; + } + clearInterval(statusTimer); + statusTimer = null; + }; + + const startWaitingTimer = () => { + if (waitingTimer) { + return; + } + + // Pick a phrase once per waiting session. + if (!waitingPhrase) { + const idx = Math.floor(Math.random() * defaultWaitingPhrases.length); + waitingPhrase = defaultWaitingPhrases[idx] ?? defaultWaitingPhrases[0] ?? "waiting"; + } + + waitingTick = 0; + + waitingTimer = setInterval(() => { + if (activityStatus !== "waiting") { + return; + } + updateBusyStatusMessage(); + }, 120); + }; + + const stopWaitingTimer = () => { + if (!waitingTimer) { + return; + } + clearInterval(waitingTimer); + waitingTimer = null; + waitingPhrase = null; + }; + + const renderStatus = () => { + const isBusy = BUSY_STATES.has(activityStatus); + if (isBusy) { + if (!statusStartedAt || lastActivityStatus !== activityStatus) { + statusStartedAt = Date.now(); + } + ensureStatusLoader(); + if (activityStatus === "waiting") { + stopStatusTimer(); + startWaitingTimer(); + } else { + stopWaitingTimer(); + startStatusTimer(); + } + updateBusyStatusMessage(); + } else { + statusStartedAt = null; + stopStatusTimer(); + stopWaitingTimer(); + statusLoader?.stop(); + statusLoader = null; + ensureStatusText(); + const text = activityStatus ? `${connectionStatus} | ${activityStatus}` : connectionStatus; + statusText?.setText(theme.dim(text)); + } + lastActivityStatus = activityStatus; + }; + + const setConnectionStatus = (text: string, ttlMs?: number) => { + connectionStatus = text; + renderStatus(); + if (statusTimeout) { + clearTimeout(statusTimeout); + } + if (ttlMs && ttlMs > 0) { + statusTimeout = setTimeout(() => { + connectionStatus = isConnected ? "connected" : "disconnected"; + renderStatus(); + }, ttlMs); + } + }; + + const setActivityStatus = (text: string) => { + activityStatus = text; + renderStatus(); + }; + + const updateHeader = () => { + const sessionLabel = deps.formatSessionKey(deps.getCurrentSessionKey()); + const agentLabel = deps.formatAgentLabel(deps.getCurrentAgentId()); + header.setText( + theme.header(`mayros tui - ${gatewayUrl} - agent ${agentLabel} - session ${sessionLabel}`), + ); + }; + + const updateFooter = () => { + const sessionKeyLabel = deps.formatSessionKey(deps.getCurrentSessionKey()); + const sessionInfo = deps.getSessionInfo(); + const sessionLabel = sessionInfo.displayName + ? `${sessionKeyLabel} (${sessionInfo.displayName})` + : sessionKeyLabel; + const agentLabel = deps.formatAgentLabel(deps.getCurrentAgentId()); + const modelLabel = sessionInfo.model + ? sessionInfo.modelProvider + ? `${sessionInfo.modelProvider}/${sessionInfo.model}` + : sessionInfo.model + : "unknown"; + const tokens = formatTokens(sessionInfo.totalTokens ?? null, sessionInfo.contextTokens ?? null); + const think = sessionInfo.thinkingLevel ?? "off"; + const verbose = sessionInfo.verboseLevel ?? "off"; + const reasoning = sessionInfo.reasoningLevel ?? "off"; + const reasoningLabel = + reasoning === "on" ? "reasoning" : reasoning === "stream" ? "reasoning:stream" : null; + const permissionMode = deps.getPermissionMode(); + const permLabel = permissionMode !== "auto" ? `perm ${permissionMode}` : null; + const fastLabel = deps.getFastMode() ? "FAST" : null; + const footerParts = [ + `agent ${agentLabel}`, + `session ${sessionLabel}`, + modelLabel, + think !== "off" ? `think ${think}` : null, + verbose !== "off" ? `verbose ${verbose}` : null, + reasoningLabel, + tokens, + permLabel, + fastLabel, + ].filter(Boolean); + footer.setText(theme.dim(footerParts.join(" | "))); + }; + + return { + updateHeader, + updateFooter, + renderStatus, + setConnectionStatus, + setActivityStatus, + get activityStatus() { + return activityStatus; + }, + set activityStatus(value: string) { + activityStatus = value; + }, + get connectionStatus() { + return connectionStatus; + }, + set connectionStatus(value: string) { + connectionStatus = value; + }, + get isConnected() { + return isConnected; + }, + set isConnected(value: boolean) { + isConnected = value; + }, + get statusTimeout() { + return statusTimeout; + }, + set statusTimeout(value: ReturnType | null) { + statusTimeout = value; + }, + }; +} diff --git a/src/tui/tui.ts b/src/tui/tui.ts index 5718f8b1..9a4beb27 100644 --- a/src/tui/tui.ts +++ b/src/tui/tui.ts @@ -1,7 +1,6 @@ import { CombinedAutocompleteProvider, Container, - Loader, ProcessTerminal, Text, TUI, @@ -9,7 +8,6 @@ import { 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 { normalizeAgentId, normalizeMainKey, @@ -32,10 +30,12 @@ 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 { formatTokens } from "./tui-formatters.js"; import { createLocalShellRunner } from "./tui-local-shell.js"; import { createOverlayHandlers } from "./tui-overlays.js"; import { createSessionActions } from "./tui-session-actions.js"; +import { createStatusRenderer, type StatusRenderer } from "./tui-status-renderer.js"; +import { wireEditorKeyBindings } from "./tui-keybindings.js"; +import { wireConnectionHandlers } from "./tui-connection.js"; import type { AgentSummary, PendingImage, @@ -45,8 +45,6 @@ import type { 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"; @@ -63,9 +61,7 @@ import { shouldEnableWindowsGitBashPasteFallback, createSubmitBurstCoalescer, resolveTuiSessionKey, - resolveGatewayDisconnectState, createBackspaceDeduper, - tryInlinePairingApproval, } from "./tui-helpers.js"; export async function runTui(opts: TuiOptions) { @@ -109,14 +105,10 @@ export async function runTui(opts: TuiOptions) { let currentSessionId: string | null = null; let activeChatRunId: string | null = null; let historyLoaded = false; - let isConnected = false; - 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; @@ -135,12 +127,10 @@ export async function runTui(opts: TuiOptions) { let autoMessageSent = false; let sessionInfo: SessionInfo = {}; let lastCtrlCAt = 0; - let activityStatus = "idle"; - let connectionStatus = "connecting"; - let statusTimeout: NodeJS.Timeout | null = null; - let statusTimer: NodeJS.Timeout | null = null; - let statusStartedAt: number | null = null; - let lastActivityStatus = activityStatus; + // Status display state (activity/connection labels + timers) is owned by the + // status renderer created below. The `state` object delegates these fields + // to the renderer via late-bound references. + let statusRenderer: StatusRenderer | null = null; const state: TuiStateAccess = { get agentDefaultId() { @@ -210,10 +200,12 @@ export async function runTui(opts: TuiOptions) { initialSessionApplied = value; }, get isConnected() { - return isConnected; + return statusRenderer?.isConnected ?? false; }, set isConnected(value) { - isConnected = value; + if (statusRenderer) { + statusRenderer.isConnected = value; + } }, get autoMessageSent() { return autoMessageSent; @@ -246,22 +238,28 @@ export async function runTui(opts: TuiOptions) { thinkingSectionState = value; }, get connectionStatus() { - return connectionStatus; + return statusRenderer?.connectionStatus ?? "connecting"; }, set connectionStatus(value) { - connectionStatus = value; + if (statusRenderer) { + statusRenderer.connectionStatus = value; + } }, get activityStatus() { - return activityStatus; + return statusRenderer?.activityStatus ?? "idle"; }, set activityStatus(value) { - activityStatus = value; + if (statusRenderer) { + statusRenderer.activityStatus = value; + } }, get statusTimeout() { - return statusTimeout; + return statusRenderer?.statusTimeout ?? null; }, set statusTimeout(value) { - statusTimeout = value; + if (statusRenderer) { + statusRenderer.statusTimeout = value; + } }, get lastCtrlCAt() { return lastCtrlCAt; @@ -421,212 +419,30 @@ export async function runTui(opts: TuiOptions) { currentSessionKey = resolveSessionKey(initialSessionInput); - const updateHeader = () => { - const sessionLabel = formatSessionKey(currentSessionKey); - const agentLabel = formatAgentLabel(currentAgentId); - header.setText( - theme.header( - `mayros tui - ${client.connection.url} - agent ${agentLabel} - session ${sessionLabel}`, - ), - ); - }; - - const busyStates = new Set(["sending", "waiting", "streaming", "running"]); - let statusText: Text | null = null; - let statusLoader: Loader | null = null; - - const formatElapsed = (startMs: number) => { - const totalSeconds = Math.max(0, Math.floor((Date.now() - startMs) / 1000)); - if (totalSeconds < 60) { - return `${totalSeconds}s`; - } - const minutes = Math.floor(totalSeconds / 60); - const seconds = totalSeconds % 60; - return `${minutes}m ${seconds}s`; - }; - - const ensureStatusText = () => { - if (statusText) { - return; - } - statusContainer.clear(); - statusLoader?.stop(); - statusLoader = null; - statusText = new Text("", 1, 0); - statusContainer.addChild(statusText); - }; - - const ensureStatusLoader = () => { - if (statusLoader) { - return; - } - statusContainer.clear(); - statusText = null; - statusLoader = new Loader( - tui, - (spinner) => theme.accent(spinner), - (text) => theme.bold(theme.accentSoft(text)), - "", - ); - statusContainer.addChild(statusLoader); - }; - - let waitingTick = 0; - let waitingTimer: NodeJS.Timeout | null = null; - let waitingPhrase: string | null = null; - - const updateBusyStatusMessage = () => { - if (!statusLoader || !statusStartedAt) { - return; - } - const elapsed = formatElapsed(statusStartedAt); - - if (activityStatus === "waiting") { - waitingTick++; - statusLoader.setMessage( - buildWaitingStatusMessage({ - theme, - tick: waitingTick, - elapsed, - connectionStatus, - phrases: waitingPhrase ? [waitingPhrase] : undefined, - }), - ); - return; - } - - statusLoader.setMessage(`${activityStatus} • ${elapsed} | ${connectionStatus}`); - }; - - const startStatusTimer = () => { - if (statusTimer) { - return; - } - statusTimer = setInterval(() => { - if (!busyStates.has(activityStatus)) { - return; - } - updateBusyStatusMessage(); - }, 1000); - }; - - const stopStatusTimer = () => { - if (!statusTimer) { - return; - } - clearInterval(statusTimer); - statusTimer = null; - }; - - const startWaitingTimer = () => { - if (waitingTimer) { - return; - } - - // Pick a phrase once per waiting session. - if (!waitingPhrase) { - const idx = Math.floor(Math.random() * defaultWaitingPhrases.length); - waitingPhrase = defaultWaitingPhrases[idx] ?? defaultWaitingPhrases[0] ?? "waiting"; - } - - waitingTick = 0; - - waitingTimer = setInterval(() => { - if (activityStatus !== "waiting") { - return; - } - updateBusyStatusMessage(); - }, 120); - }; - - const stopWaitingTimer = () => { - if (!waitingTimer) { - return; - } - clearInterval(waitingTimer); - waitingTimer = null; - waitingPhrase = null; - }; - - const renderStatus = () => { - const isBusy = busyStates.has(activityStatus); - if (isBusy) { - if (!statusStartedAt || lastActivityStatus !== activityStatus) { - statusStartedAt = Date.now(); - } - ensureStatusLoader(); - if (activityStatus === "waiting") { - stopStatusTimer(); - startWaitingTimer(); - } else { - stopWaitingTimer(); - startStatusTimer(); - } - updateBusyStatusMessage(); - } else { - statusStartedAt = null; - stopStatusTimer(); - stopWaitingTimer(); - statusLoader?.stop(); - statusLoader = null; - ensureStatusText(); - const text = activityStatus ? `${connectionStatus} | ${activityStatus}` : connectionStatus; - statusText?.setText(theme.dim(text)); - } - lastActivityStatus = activityStatus; - }; - - const setConnectionStatus = (text: string, ttlMs?: number) => { - connectionStatus = text; - renderStatus(); - if (statusTimeout) { - clearTimeout(statusTimeout); - } - if (ttlMs && ttlMs > 0) { - statusTimeout = setTimeout(() => { - connectionStatus = isConnected ? "connected" : "disconnected"; - renderStatus(); - }, ttlMs); - } - }; - - const setActivityStatus = (text: string) => { - activityStatus = text; - renderStatus(); - }; + // Status renderer owns the connection/activity labels, busy/idle timers, + // and the waiting-status animation. Created here so it can capture the + // header/footer/statusContainer nodes; the `state` object above delegates + // activityStatus/connectionStatus/isConnected/statusTimeout to it. + statusRenderer = createStatusRenderer({ + tui, + statusContainer, + header, + footer, + gatewayUrl: client.connection.url, + formatSessionKey, + formatAgentLabel, + getCurrentSessionKey: () => currentSessionKey, + getCurrentAgentId: () => currentAgentId, + getSessionInfo: () => sessionInfo, + getPermissionMode: () => permissionMode, + getFastMode: () => fastMode, + }); - const updateFooter = () => { - const sessionKeyLabel = formatSessionKey(currentSessionKey); - const sessionLabel = sessionInfo.displayName - ? `${sessionKeyLabel} (${sessionInfo.displayName})` - : sessionKeyLabel; - const agentLabel = formatAgentLabel(currentAgentId); - const modelLabel = sessionInfo.model - ? sessionInfo.modelProvider - ? `${sessionInfo.modelProvider}/${sessionInfo.model}` - : sessionInfo.model - : "unknown"; - const tokens = formatTokens(sessionInfo.totalTokens ?? null, sessionInfo.contextTokens ?? null); - const think = sessionInfo.thinkingLevel ?? "off"; - const verbose = sessionInfo.verboseLevel ?? "off"; - const reasoning = sessionInfo.reasoningLevel ?? "off"; - const reasoningLabel = - reasoning === "on" ? "reasoning" : reasoning === "stream" ? "reasoning:stream" : null; - const permLabel = permissionMode !== "auto" ? `perm ${permissionMode}` : null; - const fastLabel = fastMode ? "FAST" : null; - const footerParts = [ - `agent ${agentLabel}`, - `session ${sessionLabel}`, - modelLabel, - think !== "off" ? `think ${think}` : null, - verbose !== "off" ? `verbose ${verbose}` : null, - reasoningLabel, - tokens, - permLabel, - fastLabel, - ].filter(Boolean); - footer.setText(theme.dim(footerParts.join(" | "))); - }; + const updateHeader = () => statusRenderer.updateHeader(); + const updateFooter = () => statusRenderer.updateFooter(); + const setConnectionStatus = (text: string, ttlMs?: number) => + statusRenderer.setConnectionStatus(text, ttlMs); + const setActivityStatus = (text: string) => statusRenderer.setActivityStatus(text); const { openOverlay, closeOverlay } = createOverlayHandlers(tui, editor); @@ -719,148 +535,40 @@ export async function runTui(opts: TuiOptions) { enabled: shouldEnableWindowsGitBashPasteFallback(), }); - editor.onEscape = () => { - void abortActive(); - }; - 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"); - tui.requestRender(); - return; - } - if (now - lastCtrlCAt < 1000) { - mouseHandler.disable(); - client.stop(); - tui.stop(); - process.exit(0); - } - lastCtrlCAt = now; - setActivityStatus("press ctrl+c again to exit"); - tui.requestRender(); - }; - editor.onCtrlD = () => { - mouseHandler.disable(); - client.stop(); - tui.stop(); - process.exit(0); - }; - editor.onCtrlO = () => { - toolSectionState = nextSectionState(toolSectionState); - toolsExpanded = toolSectionState === "expanded"; - chatLog.setToolSectionState(toolSectionState); - setActivityStatus(`tools: ${toolSectionState}`); - tui.requestRender(); - }; - editor.onCtrlL = () => { - void openModelSelector(); - }; - editor.onCtrlG = () => { - void openAgentSelector(); - }; - editor.onCtrlP = () => { - void openSessionSelector(); - }; - editor.onCtrlT = () => { - 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 = () => { - const modes: Array<"auto" | "ask" | "deny"> = ["auto", "ask", "deny"]; - const idx = modes.indexOf(permissionMode); - permissionMode = modes[(idx + 1) % modes.length] ?? "auto"; - state.permissionMode = permissionMode; - setActivityStatus(`permission: ${permissionMode}`); - tui.requestRender(); - }; - - client.onEvent = (evt) => { - if (evt.event === "chat") { - handleChatEvent(evt.payload); - } - if (evt.event === "agent") { - handleAgentEvent(evt.payload); - } - }; - - client.onConnected = () => { - isConnected = true; - pairingHintShown = false; - gatewayDownHintShown = false; - const reconnected = wasDisconnected; - wasDisconnected = false; - setConnectionStatus("connected"); - void (async () => { - await refreshAgents(); - updateHeader(); - if (opts.cleanStart && !reconnected) { - chatLog.clearAll(); - chatLog.addWelcome(createWelcomeScreen()); - historyLoaded = true; - await refreshSessionInfo(); - } else { - await loadHistory(); - } - setConnectionStatus(reconnected ? "gateway reconnected" : "gateway connected", 4000); - tui.requestRender(); - if (!autoMessageSent && autoMessage) { - autoMessageSent = true; - await sendMessage(autoMessage); - } - updateFooter(); - tui.requestRender(); - })(); - }; - - client.onDisconnected = (reason) => { - isConnected = false; - wasDisconnected = true; - historyLoaded = false; - const disconnectState = resolveGatewayDisconnectState(reason); - setConnectionStatus(disconnectState.connectionStatus, 5000); - setActivityStatus(disconnectState.activityStatus); - if (disconnectState.pairingHint && !pairingHintShown) { - pairingHintShown = true; - if (isLoopbackHost(new URL(client.connection.url).hostname)) { - void tryInlinePairingApproval().then((ok) => { - if (ok) { - chatLog.addSystem("Device paired. Reconnecting..."); - } else { - chatLog.addSystem(disconnectState.pairingHint!); - } - tui.requestRender(); - }); - } else { - chatLog.addSystem(disconnectState.pairingHint); - } - } - if (disconnectState.gatewayDownHint && !gatewayDownHintShown) { - gatewayDownHintShown = true; - chatLog.addSystem(disconnectState.gatewayDownHint); - } - updateFooter(); - tui.requestRender(); - }; + wireEditorKeyBindings({ + editor, + tui, + chatLog, + client, + state, + mouseHandler, + abortActive, + setActivityStatus, + loadHistory, + openModelSelector, + openAgentSelector, + openSessionSelector, + }); - client.onGap = (info) => { - setConnectionStatus(`event gap: expected ${info.expected}, got ${info.received}`, 5000); - tui.requestRender(); - }; + wireConnectionHandlers({ + client, + tui, + chatLog, + opts, + state, + autoMessage, + createWelcomeScreen, + refreshAgents, + refreshSessionInfo, + loadHistory, + sendMessage, + updateHeader, + updateFooter, + setConnectionStatus, + setActivityStatus, + handleChatEvent, + handleAgentEvent, + }); updateHeader(); setConnectionStatus("connecting"); From b7c4727f45695abee35b6ba6ed2f7254f9292594 Mon Sep 17 00:00:00 2001 From: ApiliumDevTeam Date: Wed, 15 Jul 2026 00:41:55 +0200 Subject: [PATCH 17/22] fix(search): break circular dependency that crashed the CLI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The FTS5 session search module imported extractTextContent and search types from session-search.ts, while session-search.ts dynamically imported session-search-fts.ts — creating a circular dependency that deadlocked the bundler's module initialization order and crashed every CLI invocation (mayros --version, --help, all commands). Extract extractTextContent and the SearchOptions/SearchResult/ SearchSummary types into a shared session-search-types.ts module that neither file imports from the other. Verified: pnpm build succeeds, node mayros.mjs --version and --help both work, all 27 search tests pass. --- src/infra/session-search-fts.ts | 8 +++-- src/infra/session-search-types.ts | 51 ++++++++++++++++++++++++++ src/infra/session-search.ts | 60 +++++-------------------------- 3 files changed, 65 insertions(+), 54 deletions(-) create mode 100644 src/infra/session-search-types.ts diff --git a/src/infra/session-search-fts.ts b/src/infra/session-search-fts.ts index f3813093..3ea31328 100644 --- a/src/infra/session-search-fts.ts +++ b/src/infra/session-search-fts.ts @@ -18,8 +18,12 @@ import { homedir } from "node:os"; import { createHash } from "node:crypto"; import { requireNodeSqlite } from "../memory/sqlite.js"; import { buildFtsQuery } from "../memory/hybrid.js"; -import type { SearchOptions, SearchResult, SearchSummary } from "./session-search.js"; -import { extractTextContent } from "./session-search.js"; +import { + extractTextContent, + type SearchOptions, + type SearchResult, + type SearchSummary, +} from "./session-search-types.js"; // --------------------------------------------------------------------------- // Schema diff --git a/src/infra/session-search-types.ts b/src/infra/session-search-types.ts new file mode 100644 index 00000000..ceced6e6 --- /dev/null +++ b/src/infra/session-search-types.ts @@ -0,0 +1,51 @@ +/** + * Shared types and helpers for session search. + * + * Extracted to break a circular dependency between session-search.ts + * (linear scan) and session-search-fts.ts (FTS5 backend). Both modules + * import from here instead of from each other. + */ + +export type SearchOptions = { + query: string; + role?: "user" | "assistant"; + since?: number; + before?: number; + limit?: number; + sessionIds?: string[]; + basePath?: string; +}; + +export type SearchResult = { + sessionId: string; + messageId: string; + role: "user" | "assistant"; + content: string; + snippet: string; + timestamp: number; + lineIndex: number; +}; + +export type SearchSummary = { + results: SearchResult[]; + totalMatches: number; + sessionsSearched: number; + durationMs: number; +}; + +/** + * Extract text from a message content field that may be a string or an + * array of content blocks. + */ +export function extractTextContent(content: unknown): string { + if (typeof content === "string") return content; + if (Array.isArray(content)) { + return content + .filter( + (block: Record) => block.type === "text" && typeof block.text === "string", + ) + .map((block: Record) => block.text as string) + .join("\n"); + } + return ""; +} diff --git a/src/infra/session-search.ts b/src/infra/session-search.ts index 721f7ded..b7c3745d 100644 --- a/src/infra/session-search.ts +++ b/src/infra/session-search.ts @@ -10,42 +10,15 @@ import { createReadStream } from "node:fs"; import { createInterface } from "node:readline"; import { join, basename } from "node:path"; import { homedir } from "node:os"; +import { + extractTextContent, + type SearchOptions, + type SearchResult, + type SearchSummary, +} from "./session-search-types.js"; -export type SearchOptions = { - /** Search query (case-insensitive substring match). */ - query: string; - /** Filter by message role. */ - role?: "user" | "assistant"; - /** Only search messages after this timestamp (ms). */ - since?: number; - /** Only search messages before this timestamp (ms). */ - before?: number; - /** Max results to return (default: 20). */ - limit?: number; - /** Specific session IDs to search (default: all). */ - sessionIds?: string[]; - /** Base directory override for sessions (default: ~/.mayros/agents). */ - basePath?: string; -}; - -export type SearchResult = { - sessionId: string; - messageId: string; - role: "user" | "assistant"; - content: string; - /** Snippet of content around the match. */ - snippet: string; - timestamp: number; - /** 0-based line index in the JSONL file. */ - lineIndex: number; -}; - -export type SearchSummary = { - results: SearchResult[]; - totalMatches: number; - sessionsSearched: number; - durationMs: number; -}; +// Re-export for backward compatibility (callers import from here). +export { extractTextContent, type SearchOptions, type SearchResult, type SearchSummary }; /** Default sessions base path. */ function defaultBasePath(): string { @@ -107,23 +80,6 @@ export function extractSnippet(text: string, query: string, contextChars = 80): return snippet; } -/** - * Extract text content from a message content field. - * Handles both string and array-of-blocks formats. - */ -export function extractTextContent(content: unknown): string { - if (typeof content === "string") return content; - if (Array.isArray(content)) { - return content - .filter( - (block: Record) => block.type === "text" && typeof block.text === "string", - ) - .map((block: Record) => block.text as string) - .join("\n"); - } - return ""; -} - /** * Search a single JSONL session file for matching messages. */ From be0f50869151d434181ff917d771021a9600a85e Mon Sep 17 00:00:00 2001 From: ApiliumDevTeam Date: Wed, 15 Jul 2026 00:42:07 +0200 Subject: [PATCH 18/22] chore(plugins): sync extension versions to 0.3.2 Align all 54 extension package.json versions and changelogs with the root package via pnpm plugins:sync. Required for pnpm release:check to pass. --- extensions/agent-mesh/package.json | 2 +- extensions/analytics/package.json | 2 +- extensions/bash-sandbox/package.json | 2 +- extensions/bluebubbles/package.json | 2 +- extensions/ci-plugin/package.json | 2 +- extensions/code-indexer/package.json | 2 +- extensions/code-tools/package.json | 2 +- extensions/copilot-proxy/package.json | 2 +- extensions/cortex-sync/package.json | 2 +- extensions/diagnostics-otel/package.json | 2 +- extensions/discord/package.json | 2 +- extensions/eruberu/package.json | 2 +- extensions/feishu/package.json | 6 +++--- extensions/google-antigravity-auth/package.json | 2 +- extensions/google-gemini-cli-auth/package.json | 2 +- extensions/googlechat/package.json | 2 +- extensions/hayameru/package.json | 2 +- extensions/imessage/package.json | 2 +- extensions/interactive-permissions/package.json | 2 +- extensions/iot-bridge/package.json | 2 +- extensions/irc/package.json | 2 +- extensions/kakeru-bridge/package.json | 2 +- extensions/line/package.json | 2 +- extensions/llm-hooks/package.json | 2 +- extensions/llm-task/package.json | 2 +- extensions/lobster/package.json | 2 +- extensions/lsp-bridge/package.json | 2 +- extensions/matrix/CHANGELOG.md | 5 +++++ extensions/matrix/package.json | 2 +- extensions/mattermost/package.json | 2 +- extensions/mcp-client/package.json | 2 +- extensions/mcp-server/package.json | 2 +- extensions/memory-core/package.json | 2 +- extensions/memory-lancedb/package.json | 2 +- extensions/memory-semantic/package.json | 2 +- extensions/minimax-portal-auth/package.json | 2 +- extensions/msteams/CHANGELOG.md | 5 +++++ extensions/msteams/package.json | 2 +- extensions/nextcloud-talk/package.json | 2 +- extensions/nostr/CHANGELOG.md | 5 +++++ extensions/nostr/package.json | 2 +- extensions/open-prose/package.json | 2 +- extensions/osameru-governance/package.json | 2 +- extensions/semantic-observability/package.json | 2 +- extensions/semantic-skills/package.json | 2 +- extensions/signal/package.json | 2 +- extensions/skill-hub/package.json | 2 +- extensions/slack/package.json | 2 +- extensions/telegram/package.json | 2 +- extensions/tlon/package.json | 2 +- extensions/token-economy/package.json | 2 +- extensions/tomeru-guard/package.json | 2 +- extensions/twitch/CHANGELOG.md | 5 +++++ extensions/twitch/package.json | 2 +- extensions/voice-call/CHANGELOG.md | 5 +++++ extensions/voice-call/package.json | 2 +- extensions/whatsapp/package.json | 2 +- extensions/zalo/CHANGELOG.md | 5 +++++ extensions/zalo/package.json | 2 +- extensions/zalouser/CHANGELOG.md | 5 +++++ extensions/zalouser/package.json | 2 +- 61 files changed, 91 insertions(+), 56 deletions(-) diff --git a/extensions/agent-mesh/package.json b/extensions/agent-mesh/package.json index 3023ae0b..2c01258b 100644 --- a/extensions/agent-mesh/package.json +++ b/extensions/agent-mesh/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-agent-mesh", - "version": "0.2.0", + "version": "0.3.2", "private": true, "description": "Mayros multi-agent coordination mesh with shared namespaces, delegation, and knowledge fusion", "type": "module", diff --git a/extensions/analytics/package.json b/extensions/analytics/package.json index b1b301fd..a062a9e9 100644 --- a/extensions/analytics/package.json +++ b/extensions/analytics/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-analytics", - "version": "0.2.0", + "version": "0.3.2", "private": true, "type": "module", "main": "index.ts", diff --git a/extensions/bash-sandbox/package.json b/extensions/bash-sandbox/package.json index 6fd29204..c746b9b9 100644 --- a/extensions/bash-sandbox/package.json +++ b/extensions/bash-sandbox/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-bash-sandbox", - "version": "0.2.0", + "version": "0.3.2", "private": true, "description": "Bash command sandbox with domain allowlist, command blocklist, and dangerous pattern detection", "type": "module", diff --git a/extensions/bluebubbles/package.json b/extensions/bluebubbles/package.json index 13bd2191..c998108b 100644 --- a/extensions/bluebubbles/package.json +++ b/extensions/bluebubbles/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-bluebubbles", - "version": "0.2.0", + "version": "0.3.2", "description": "Mayros BlueBubbles channel plugin", "license": "MIT", "type": "module", diff --git a/extensions/ci-plugin/package.json b/extensions/ci-plugin/package.json index 23549e41..6b0c6fb3 100644 --- a/extensions/ci-plugin/package.json +++ b/extensions/ci-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-ci-plugin", - "version": "0.2.0", + "version": "0.3.2", "description": "CI/CD pipeline integration for Mayros — GitHub Actions and GitLab CI providers", "type": "module", "dependencies": { diff --git a/extensions/code-indexer/package.json b/extensions/code-indexer/package.json index db07adba..49c60653 100644 --- a/extensions/code-indexer/package.json +++ b/extensions/code-indexer/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-code-indexer", - "version": "0.2.0", + "version": "0.3.2", "private": true, "description": "Mayros code indexer plugin — regex-based codebase scanning with RDF triple storage in Cortex", "type": "module", diff --git a/extensions/code-tools/package.json b/extensions/code-tools/package.json index a5c3b769..4e78380f 100644 --- a/extensions/code-tools/package.json +++ b/extensions/code-tools/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-code-tools", - "version": "0.2.0", + "version": "0.3.2", "private": true, "type": "module", "dependencies": { diff --git a/extensions/copilot-proxy/package.json b/extensions/copilot-proxy/package.json index c2b0de48..c8e3a45a 100644 --- a/extensions/copilot-proxy/package.json +++ b/extensions/copilot-proxy/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-copilot-proxy", - "version": "0.2.0", + "version": "0.3.2", "private": true, "description": "Mayros Copilot Proxy provider plugin", "type": "module", diff --git a/extensions/cortex-sync/package.json b/extensions/cortex-sync/package.json index e0d11038..784159ba 100644 --- a/extensions/cortex-sync/package.json +++ b/extensions/cortex-sync/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-cortex-sync", - "version": "0.2.0", + "version": "0.3.2", "private": true, "description": "Cortex DAG synchronization — peer discovery, delta sync, and cross-device knowledge replication", "type": "module", diff --git a/extensions/diagnostics-otel/package.json b/extensions/diagnostics-otel/package.json index 7ee87951..ba1cff1b 100644 --- a/extensions/diagnostics-otel/package.json +++ b/extensions/diagnostics-otel/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-diagnostics-otel", - "version": "0.2.0", + "version": "0.3.2", "description": "Mayros diagnostics OpenTelemetry exporter", "license": "MIT", "type": "module", diff --git a/extensions/discord/package.json b/extensions/discord/package.json index f93f8e46..ba8dbf2d 100644 --- a/extensions/discord/package.json +++ b/extensions/discord/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-discord", - "version": "0.2.0", + "version": "0.3.2", "description": "Mayros Discord channel plugin", "license": "MIT", "type": "module", diff --git a/extensions/eruberu/package.json b/extensions/eruberu/package.json index f61dbfa8..d54e9aea 100644 --- a/extensions/eruberu/package.json +++ b/extensions/eruberu/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-eruberu", - "version": "0.2.0", + "version": "0.3.2", "private": true, "description": "Mayros intelligent model routing plugin — Q-Learning adaptive provider/model selection", "type": "module", diff --git a/extensions/feishu/package.json b/extensions/feishu/package.json index 05b33b24..a655ad3e 100644 --- a/extensions/feishu/package.json +++ b/extensions/feishu/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-feishu", - "version": "0.2.0", + "version": "0.3.2", "description": "Mayros Feishu/Lark channel plugin (community maintained by @m1heng)", "license": "MIT", "type": "module", @@ -22,10 +22,10 @@ "channel": { "id": "feishu", "label": "Feishu", - "selectionLabel": "Feishu/Lark (\u98de\u4e66)", + "selectionLabel": "Feishu/Lark (飞书)", "docsPath": "/channels/feishu", "docsLabel": "feishu", - "blurb": "\u98de\u4e66/Lark enterprise messaging with doc/wiki/drive tools.", + "blurb": "飞书/Lark enterprise messaging with doc/wiki/drive tools.", "aliases": [ "lark" ], diff --git a/extensions/google-antigravity-auth/package.json b/extensions/google-antigravity-auth/package.json index 6752b8b2..84cdf2e8 100644 --- a/extensions/google-antigravity-auth/package.json +++ b/extensions/google-antigravity-auth/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-google-antigravity-auth", - "version": "0.2.0", + "version": "0.3.2", "private": true, "description": "Mayros Google Antigravity OAuth provider plugin", "type": "module", diff --git a/extensions/google-gemini-cli-auth/package.json b/extensions/google-gemini-cli-auth/package.json index 765c7c19..43416054 100644 --- a/extensions/google-gemini-cli-auth/package.json +++ b/extensions/google-gemini-cli-auth/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-google-gemini-cli-auth", - "version": "0.2.0", + "version": "0.3.2", "private": true, "description": "Mayros Gemini CLI OAuth provider plugin", "type": "module", diff --git a/extensions/googlechat/package.json b/extensions/googlechat/package.json index 84da906b..85c1cae3 100644 --- a/extensions/googlechat/package.json +++ b/extensions/googlechat/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-googlechat", - "version": "0.2.0", + "version": "0.3.2", "private": true, "description": "Mayros Google Chat channel plugin", "type": "module", diff --git a/extensions/hayameru/package.json b/extensions/hayameru/package.json index 31f216bb..48221bd4 100644 --- a/extensions/hayameru/package.json +++ b/extensions/hayameru/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-hayameru", - "version": "0.2.0", + "version": "0.3.2", "private": true, "description": "Mayros deterministic code transforms — bypass LLM for simple edits", "type": "module", diff --git a/extensions/imessage/package.json b/extensions/imessage/package.json index dc4b1310..acc34119 100644 --- a/extensions/imessage/package.json +++ b/extensions/imessage/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-imessage", - "version": "0.2.0", + "version": "0.3.2", "private": true, "description": "Mayros iMessage channel plugin", "type": "module", diff --git a/extensions/interactive-permissions/package.json b/extensions/interactive-permissions/package.json index 623ffbc4..9e052ec0 100644 --- a/extensions/interactive-permissions/package.json +++ b/extensions/interactive-permissions/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-interactive-permissions", - "version": "0.2.0", + "version": "0.3.2", "private": true, "description": "Runtime permission dialogs, bash intent classification, policy persistence, and audit trail", "type": "module", diff --git a/extensions/iot-bridge/package.json b/extensions/iot-bridge/package.json index 908b1b88..bea1833d 100644 --- a/extensions/iot-bridge/package.json +++ b/extensions/iot-bridge/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-iot-bridge", - "version": "0.2.0", + "version": "0.3.2", "private": true, "description": "IoT Bridge — connect MAYROS agents to aingle_minimal IoT nodes via REST", "type": "module", diff --git a/extensions/irc/package.json b/extensions/irc/package.json index c8e8dc85..22ef7ca8 100644 --- a/extensions/irc/package.json +++ b/extensions/irc/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-irc", - "version": "0.2.0", + "version": "0.3.2", "description": "Mayros IRC channel plugin", "license": "MIT", "type": "module", diff --git a/extensions/kakeru-bridge/package.json b/extensions/kakeru-bridge/package.json index f4af60b4..fe9bacb0 100644 --- a/extensions/kakeru-bridge/package.json +++ b/extensions/kakeru-bridge/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-kakeru-bridge", - "version": "0.2.0", + "version": "0.3.2", "private": true, "description": "Mayros dual-platform coordination bridge — Claude Code + Codex CLI", "type": "module", diff --git a/extensions/line/package.json b/extensions/line/package.json index 90a5d62c..ad658869 100644 --- a/extensions/line/package.json +++ b/extensions/line/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-line", - "version": "0.2.0", + "version": "0.3.2", "private": true, "description": "Mayros LINE channel plugin", "type": "module", diff --git a/extensions/llm-hooks/package.json b/extensions/llm-hooks/package.json index b87a13e6..b754cb38 100644 --- a/extensions/llm-hooks/package.json +++ b/extensions/llm-hooks/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-llm-hooks", - "version": "0.2.0", + "version": "0.3.2", "private": true, "description": "Markdown-defined hooks evaluated by LLM for policy enforcement", "type": "module", diff --git a/extensions/llm-task/package.json b/extensions/llm-task/package.json index fa30bfdf..47e82780 100644 --- a/extensions/llm-task/package.json +++ b/extensions/llm-task/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-llm-task", - "version": "0.2.0", + "version": "0.3.2", "private": true, "description": "Mayros JSON-only LLM task plugin", "type": "module", diff --git a/extensions/lobster/package.json b/extensions/lobster/package.json index 9d087c83..90703821 100644 --- a/extensions/lobster/package.json +++ b/extensions/lobster/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-lobster", - "version": "0.2.0", + "version": "0.3.2", "description": "Lobster workflow tool plugin (typed pipelines + resumable approvals)", "license": "MIT", "type": "module", diff --git a/extensions/lsp-bridge/package.json b/extensions/lsp-bridge/package.json index f7496fa7..917573d1 100644 --- a/extensions/lsp-bridge/package.json +++ b/extensions/lsp-bridge/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-lsp-bridge", - "version": "0.2.0", + "version": "0.3.2", "description": "Cortex-backed language server bridge for Mayros — hover, diagnostics, go-to-definition", "type": "module", "dependencies": { diff --git a/extensions/matrix/CHANGELOG.md b/extensions/matrix/CHANGELOG.md index 0f4e48c5..64623bc7 100644 --- a/extensions/matrix/CHANGELOG.md +++ b/extensions/matrix/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 0.3.2 + +### Changes +- Version alignment with core Mayros release numbers. + ## 0.1.14 ### Changes diff --git a/extensions/matrix/package.json b/extensions/matrix/package.json index 71e16710..751fd5a5 100644 --- a/extensions/matrix/package.json +++ b/extensions/matrix/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-matrix", - "version": "0.2.0", + "version": "0.3.2", "description": "Mayros Matrix channel plugin", "license": "MIT", "type": "module", diff --git a/extensions/mattermost/package.json b/extensions/mattermost/package.json index 5575182d..d2d8b6fa 100644 --- a/extensions/mattermost/package.json +++ b/extensions/mattermost/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-mattermost", - "version": "0.2.0", + "version": "0.3.2", "private": true, "description": "Mayros Mattermost channel plugin", "type": "module", diff --git a/extensions/mcp-client/package.json b/extensions/mcp-client/package.json index 1edcc042..88064499 100644 --- a/extensions/mcp-client/package.json +++ b/extensions/mcp-client/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-mcp-client", - "version": "0.2.0", + "version": "0.3.2", "private": true, "description": "MCP server client with multi-transport support and Cortex tool registry", "type": "module", diff --git a/extensions/mcp-server/package.json b/extensions/mcp-server/package.json index f71af5e5..61df3296 100644 --- a/extensions/mcp-server/package.json +++ b/extensions/mcp-server/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-mcp-server", - "version": "0.2.0", + "version": "0.3.2", "private": true, "description": "MCP server exposing Mayros tools, Cortex resources, and workflow prompts via Model Context Protocol", "type": "module", diff --git a/extensions/memory-core/package.json b/extensions/memory-core/package.json index 88a9cc09..1c44aa27 100644 --- a/extensions/memory-core/package.json +++ b/extensions/memory-core/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-memory-core", - "version": "0.2.0", + "version": "0.3.2", "private": true, "description": "Mayros core memory search plugin", "type": "module", diff --git a/extensions/memory-lancedb/package.json b/extensions/memory-lancedb/package.json index 76f8e38e..b73f5dc9 100644 --- a/extensions/memory-lancedb/package.json +++ b/extensions/memory-lancedb/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-memory-lancedb", - "version": "0.2.0", + "version": "0.3.2", "private": true, "description": "Mayros LanceDB-backed long-term memory plugin with auto-recall/capture", "type": "module", diff --git a/extensions/memory-semantic/package.json b/extensions/memory-semantic/package.json index d86f4ff3..b9c4fd55 100644 --- a/extensions/memory-semantic/package.json +++ b/extensions/memory-semantic/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-memory-semantic", - "version": "0.2.0", + "version": "0.3.2", "private": true, "description": "Mayros semantic memory plugin via AIngle Cortex sidecar (RDF triples, identity graph, Ineru STM/LTM)", "type": "module", diff --git a/extensions/minimax-portal-auth/package.json b/extensions/minimax-portal-auth/package.json index 9083e69a..1f2d9870 100644 --- a/extensions/minimax-portal-auth/package.json +++ b/extensions/minimax-portal-auth/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-minimax-portal-auth", - "version": "0.2.0", + "version": "0.3.2", "private": true, "description": "Mayros MiniMax Portal OAuth provider plugin", "type": "module", diff --git a/extensions/msteams/CHANGELOG.md b/extensions/msteams/CHANGELOG.md index a7205563..a65d9d2b 100644 --- a/extensions/msteams/CHANGELOG.md +++ b/extensions/msteams/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 0.3.2 + +### Changes +- Version alignment with core Mayros release numbers. + ## 0.1.14 ### Changes diff --git a/extensions/msteams/package.json b/extensions/msteams/package.json index a8de58ca..82609def 100644 --- a/extensions/msteams/package.json +++ b/extensions/msteams/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-msteams", - "version": "0.2.0", + "version": "0.3.2", "description": "Mayros Microsoft Teams channel plugin", "license": "MIT", "type": "module", diff --git a/extensions/nextcloud-talk/package.json b/extensions/nextcloud-talk/package.json index 3424181e..4705fcbb 100644 --- a/extensions/nextcloud-talk/package.json +++ b/extensions/nextcloud-talk/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-nextcloud-talk", - "version": "0.2.0", + "version": "0.3.2", "description": "Mayros Nextcloud Talk channel plugin", "license": "MIT", "type": "module", diff --git a/extensions/nostr/CHANGELOG.md b/extensions/nostr/CHANGELOG.md index 3db21e5e..d365d159 100644 --- a/extensions/nostr/CHANGELOG.md +++ b/extensions/nostr/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 0.3.2 + +### Changes +- Version alignment with core Mayros release numbers. + ## 0.1.14 ### Changes diff --git a/extensions/nostr/package.json b/extensions/nostr/package.json index 8c890d2c..e7326501 100644 --- a/extensions/nostr/package.json +++ b/extensions/nostr/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-nostr", - "version": "0.2.0", + "version": "0.3.2", "description": "Mayros Nostr channel plugin for NIP-04 encrypted DMs", "license": "MIT", "type": "module", diff --git a/extensions/open-prose/package.json b/extensions/open-prose/package.json index 4b676495..716a4436 100644 --- a/extensions/open-prose/package.json +++ b/extensions/open-prose/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-open-prose", - "version": "0.2.0", + "version": "0.3.2", "private": true, "description": "OpenProse VM skill pack plugin (slash command + telemetry).", "type": "module", diff --git a/extensions/osameru-governance/package.json b/extensions/osameru-governance/package.json index 733992a2..fc1b0f85 100644 --- a/extensions/osameru-governance/package.json +++ b/extensions/osameru-governance/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-osameru-governance", - "version": "0.2.0", + "version": "0.3.2", "private": true, "description": "Mayros governance control plane — policy enforcement, HMAC audit trail, trust tiers", "type": "module", diff --git a/extensions/semantic-observability/package.json b/extensions/semantic-observability/package.json index 4546a075..beab1c5c 100644 --- a/extensions/semantic-observability/package.json +++ b/extensions/semantic-observability/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-semantic-observability", - "version": "0.2.0", + "version": "0.3.2", "private": true, "description": "Mayros semantic observability plugin — structured tracing of agent decisions as RDF events", "type": "module", diff --git a/extensions/semantic-skills/package.json b/extensions/semantic-skills/package.json index a13c2208..512a5ad7 100644 --- a/extensions/semantic-skills/package.json +++ b/extensions/semantic-skills/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-semantic-skills", - "version": "0.2.0", + "version": "0.3.2", "private": true, "description": "Mayros semantic skills plugin — graph-aware skills with PoL assertions, ZK proofs, and permission gating", "type": "module", diff --git a/extensions/signal/package.json b/extensions/signal/package.json index 3aaec235..b539be8c 100644 --- a/extensions/signal/package.json +++ b/extensions/signal/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-signal", - "version": "0.2.0", + "version": "0.3.2", "private": true, "description": "Mayros Signal channel plugin", "type": "module", diff --git a/extensions/skill-hub/package.json b/extensions/skill-hub/package.json index dbeb0fea..4c25b629 100644 --- a/extensions/skill-hub/package.json +++ b/extensions/skill-hub/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-skill-hub", - "version": "0.2.0", + "version": "0.3.2", "private": true, "description": "Apilium Hub marketplace — publish, install, sign, and verify semantic skills", "type": "module", diff --git a/extensions/slack/package.json b/extensions/slack/package.json index 9783edf0..99429875 100644 --- a/extensions/slack/package.json +++ b/extensions/slack/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-slack", - "version": "0.2.0", + "version": "0.3.2", "private": true, "description": "Mayros Slack channel plugin", "type": "module", diff --git a/extensions/telegram/package.json b/extensions/telegram/package.json index 0f80a978..84edf968 100644 --- a/extensions/telegram/package.json +++ b/extensions/telegram/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-telegram", - "version": "0.2.0", + "version": "0.3.2", "private": true, "description": "Mayros Telegram channel plugin", "type": "module", diff --git a/extensions/tlon/package.json b/extensions/tlon/package.json index 2625bd9f..cbd29be8 100644 --- a/extensions/tlon/package.json +++ b/extensions/tlon/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-tlon", - "version": "0.2.0", + "version": "0.3.2", "private": true, "description": "Mayros Tlon/Urbit channel plugin", "type": "module", diff --git a/extensions/token-economy/package.json b/extensions/token-economy/package.json index 3fc756b0..ebe5fc67 100644 --- a/extensions/token-economy/package.json +++ b/extensions/token-economy/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-token-economy", - "version": "0.2.0", + "version": "0.3.2", "private": true, "description": "Mayros token economy plugin — per-session cost tracking, configurable budgets with soft-stop, and prompt-level memoization", "type": "module", diff --git a/extensions/tomeru-guard/package.json b/extensions/tomeru-guard/package.json index f10e00ed..0acb7cde 100644 --- a/extensions/tomeru-guard/package.json +++ b/extensions/tomeru-guard/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-tomeru-guard", - "version": "0.2.0", + "version": "0.3.2", "private": true, "description": "Mayros rate limiting and loop breaking plugin — prevents runaway tool execution", "type": "module", diff --git a/extensions/twitch/CHANGELOG.md b/extensions/twitch/CHANGELOG.md index 1729f8c5..d0451a82 100644 --- a/extensions/twitch/CHANGELOG.md +++ b/extensions/twitch/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 0.3.2 + +### Changes +- Version alignment with core Mayros release numbers. + ## 0.1.14 ### Changes diff --git a/extensions/twitch/package.json b/extensions/twitch/package.json index 12fc9d1a..2f618ee6 100644 --- a/extensions/twitch/package.json +++ b/extensions/twitch/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-twitch", - "version": "0.2.0", + "version": "0.3.2", "private": true, "description": "Mayros Twitch channel plugin", "type": "module", diff --git a/extensions/voice-call/CHANGELOG.md b/extensions/voice-call/CHANGELOG.md index 8c57f85e..2b360d38 100644 --- a/extensions/voice-call/CHANGELOG.md +++ b/extensions/voice-call/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 0.3.2 + +### Changes +- Version alignment with core Mayros release numbers. + ## 0.1.14 ### Changes diff --git a/extensions/voice-call/package.json b/extensions/voice-call/package.json index fc8a7b28..c6745a0e 100644 --- a/extensions/voice-call/package.json +++ b/extensions/voice-call/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-voice-call", - "version": "0.2.0", + "version": "0.3.2", "description": "Mayros voice-call plugin", "license": "MIT", "type": "module", diff --git a/extensions/whatsapp/package.json b/extensions/whatsapp/package.json index 0413b6b2..e1a85ede 100644 --- a/extensions/whatsapp/package.json +++ b/extensions/whatsapp/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-whatsapp", - "version": "0.2.0", + "version": "0.3.2", "private": true, "description": "Mayros WhatsApp channel plugin", "type": "module", diff --git a/extensions/zalo/CHANGELOG.md b/extensions/zalo/CHANGELOG.md index 391a33f3..60f246ae 100644 --- a/extensions/zalo/CHANGELOG.md +++ b/extensions/zalo/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 0.3.2 + +### Changes +- Version alignment with core Mayros release numbers. + ## 0.1.14 ### Changes diff --git a/extensions/zalo/package.json b/extensions/zalo/package.json index 767d0efc..bb9cb955 100644 --- a/extensions/zalo/package.json +++ b/extensions/zalo/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-zalo", - "version": "0.2.0", + "version": "0.3.2", "description": "Mayros Zalo channel plugin", "license": "MIT", "type": "module", diff --git a/extensions/zalouser/CHANGELOG.md b/extensions/zalouser/CHANGELOG.md index 594b0a3e..c5beb1da 100644 --- a/extensions/zalouser/CHANGELOG.md +++ b/extensions/zalouser/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 0.3.2 + +### Changes +- Version alignment with core Mayros release numbers. + ## 0.1.14 ### Changes diff --git a/extensions/zalouser/package.json b/extensions/zalouser/package.json index 412097e8..8f7c0f7a 100644 --- a/extensions/zalouser/package.json +++ b/extensions/zalouser/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-zalouser", - "version": "0.2.0", + "version": "0.3.2", "description": "Mayros Zalo Personal Account plugin via zca-cli", "license": "MIT", "type": "module", From 1110dbf5df8d3b608a4977b2ab885bae8c17deee Mon Sep 17 00:00:00 2001 From: ApiliumDevTeam Date: Wed, 15 Jul 2026 00:43:38 +0200 Subject: [PATCH 19/22] docs: complete changelog and README for 0.4.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Write CHANGELOG entries for 0.3.0 through 0.4.0 (file was two minor versions behind). The 0.4.0 entry covers all 10 merged PRs: FTS5 search, theme system v2, inline images, cross-platform clipboard, per-section visibility, streaming markdown, skill curator, keyboard protocols, prompt cache drift detection, accessible TUI parity, agent stack migration, and the TUI modularization — plus the breaking changes (vim removal, scope migration, palette accent change). Update README: theme count (13 built-in + custom, not 3), add FTS5 search, colorblind/monochrome palettes, auto-theme detection, inline images, cross-platform clipboard, per-section visibility, and keyboard protocols to the feature list. --- CHANGELOG.md | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 12 +++++--- 2 files changed, 87 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a161e954..9e961e9d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,85 @@ Product: https://apilium.com/us/products/mayros Download: https://mayros.apilium.com Docs: https://apilium.com/us/doc/mayros +## 0.4.0 (2026-07-14) + +Major release: FTS5 session search, theme system v2, complete TUI redesign, skill curator lifecycle, and migration to the official agent stack. + +### Changes + +- **FTS5 session search** — dedicated SQLite full-text index with BM25 ranking, multi-word queries, role/timestamp/session filters, and snippet generation. Falls back to linear substring scan when `node:sqlite` is unavailable. +- **Theme system v2** — 13 built-in themes including colorblind-optimized (Okabe-Ito) and monochrome palettes. Custom themes from `~/.mayros/themes/*.json`. Auto-detection of terminal dark/light preference via DSR color-scheme query. +- **Inline image rendering** — tool results with image content blocks now display inline in terminals that support the Kitty graphics or iTerm2 inline image protocols, with text fallback for unsupported terminals. +- **Cross-platform clipboard** — `/copy` and image paste now work on Windows (PowerShell), Wayland (wl-paste), and WSL (clip.exe) via a unified fallback chain. +- **Per-section detail visibility** — tools (Ctrl+O) and thinking (Ctrl+T) each cycle independently through hidden → collapsed → expanded states. +- **Incremental streaming markdown** — assistant messages split at the last stable block boundary, turning per-delta render cost from O(n²) into O(n). +- **Skill curator lifecycle** — automatic activity tracking and lifecycle transitions (active → stale after 30 days, archived after 90 days). Only agent-created skills are auto-archived; pinned skills are never touched. +- **Extended keyboard protocols** — Kitty keyboard protocol and xterm modifyOtherKeys enabled in capable terminals for reliable modifier-key combinations. +- **Prompt cache drift detection** — system-prompt digest tracked across turns; a `cache:system-drift` event is emitted when the digest changes unexpectedly. +- **Accessible TUI command parity** — `/abort`, `/new`, `/compact`, `/status`, `/model`, `/session`, and `/help` now work in accessible (screen-reader) mode via a shared command core. +- **Agent stack migrated** to the official `@earendil-works/*@0.80.6` scope with zod deduplication and 21 refreshed runtime dependencies. +- **Terminal UI modularized** — `tui.ts` split from 1087 to 591 LOC, `tui-command-handlers.ts` split from 1402 to 351 LOC across focused modules. + +### Fixes + +- Ctrl+C now aborts the active run (tri-state: abort → clear input → exit on double-press). +- Accessibility TUI readline temporal-dead-zone crash fixed. +- CLI and TUI palettes unified (same accent color across all surfaces). +- Diff renderer respects active theme colors (correct under light/high-contrast themes). +- 20 `as any` casts in production code replaced with proper types. +- Lockfile change policy enforced in CI (deps-scoped commits required for lockfile changes). + +### Internal + +- Oxlint type-aware rules formalized (`.oxlintrc.json`). +- Security trust model and dependency supply-chain policy documented in `SECURITY.md`. +- Dead code removed: vim handler (779 LOC), input-history store. +- Silent `catch {}` blocks migrated to subsystem logger. + +### Breaking changes + +- **Vim mode removed.** The bridge connecting vim motions to the editor buffer was never wired and the rendering dependency does not expose the cursor API needed to fix it cleanly. The `/vim` command now reports the feature is unavailable. Emacs/readline-style keybindings (Ctrl+A/E/W/U/K) remain active. +- **Agent dependency scope changed** from `@mariozechner/*@0.54.0` to `@earendil-works/*@0.80.6`. Extension authors importing these internals must update imports. +- **CLI palette accent changed** from orange (`#FF5A2D`) to gold (`#F6C453`) to match the TUI identity. + +--- + +## 0.3.2 (2026-03-30) + +Installer UX improvements, security patches, and CI repair. + +- macOS installer: Terminal-based setup with visible progress, branded DMG +- All installers: skip onboard for portal wizard, gateway.auth.mode=none, gateway install step +- Agent onboarding: user names their agent (default: Atlas) +- Resolve all 21 Dependabot security vulnerabilities +- Fix CI workflow and PR Review action +- Cap CI test workers to 2 to prevent runner timeout + +--- + +## 0.3.1 (2026-02-21) + +Desktop installers, Mamoru security layer, and Mayrito rebrand. + +- Desktop installers for Windows (.exe), macOS (.dmg), and Linux (.AppImage/.deb) that bundle Node.js + Cortex +- Mamoru (守る) security layer: sandbox probing, egress gate, secrets vault, API keys, local model support +- GPU detection and guided Ollama/vLLM model installation +- 50+ model catalog segmented by activity with VRAM-aware filtering + +--- + +## 0.3.0 (2026-02-16) + +Kaneru — AI venture management system. + +- Ventures, missions, squads, Q-learning routing, fuel control, and decision audit +- 24 MCP tools for venture management +- Web portal with ventures tab, mission kanban, chain of command tree +- Setup Wizard for guided venture creation +- Semantic DAG integration with Ed25519 signatures + +--- + ## 0.2.1 (2026-03-14) Memory health tools — conflict detection and digest summaries for proactive memory maintenance. diff --git a/README.md b/README.md index 360b393a..f95ae294 100644 --- a/README.md +++ b/README.md @@ -154,13 +154,17 @@ mayros -p "refactor auth flow" # headless mode (non-interactive) **Features:** -- 🎨 3 themes (dark, light, high-contrast) — `/theme` +- 🎨 13 built-in themes (dark, light, high-contrast, colorblind, monochrome, + more) + custom themes — `/theme` - 📝 3 output styles (standard, explanatory, learning) — `/style` -- 📋 `Ctrl+V` image paste from clipboard -- 📊 `/diff` inline diff viewer · `/context` token usage chart +- 🌗 Auto-detection of terminal dark/light preference +- 📋 `Ctrl+V` image paste · inline image rendering in tool results (Kitty/iTerm2) +- 📊 `/diff` inline diff viewer (theme-aware) · `/context` token usage chart - 🗺️ `/plan` semantic plan mode (Cortex-backed) -- 📎 `/copy` to clipboard · `/export [file]` to disk +- 📎 `/copy` to clipboard (cross-platform) · `/export [file]` to disk - 🔀 `/model` switch models · `/think` set thinking level · `/fast` toggle fast mode +- 🔍 `mayros search` — FTS5 full-text search across sessions with BM25 ranking +- 🎛️ Per-section detail visibility (Ctrl+O tools, Ctrl+T thinking: hidden/collapsed/expanded) +- ⌨️ Extended keyboard protocols (Kitty/modifyOtherKeys) in capable terminals **Slash commands (30+):** From 3371bf644dfa30a8b8c370a57912c6b00c53b79c Mon Sep 17 00:00:00 2001 From: ApiliumDevTeam Date: Wed, 15 Jul 2026 00:45:18 +0200 Subject: [PATCH 20/22] =?UTF-8?q?feat:=20quality=20gates=20for=200.4.0=20?= =?UTF-8?q?=E2=80=94=20remove=20vim=20from=20help,=20fix=20version=20resol?= =?UTF-8?q?ution,=20widen=20theme=20config?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the disabled /vim command from the TUI help text and command list — a non-functional command in the help listing is confusing UX. Fix CORE_PACKAGE_NAME in version.ts from "mayros" to "@apilium/mayros" so that --version resolves correctly from package.json instead of relying solely on build-info.json as a fallback. Widen the config.ui.theme type from a closed union to string, so users can set any built-in preset (colorblind-dark, monochrome, etc.) or a custom theme name from ~/.mayros/themes/*.json in their config file. --- src/config/types.mayros.ts | 9 +++++++-- src/tui/commands.ts | 5 ----- src/version.ts | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/config/types.mayros.ts b/src/config/types.mayros.ts index b77abeab..6a2abd1d 100644 --- a/src/config/types.mayros.ts +++ b/src/config/types.mayros.ts @@ -68,8 +68,13 @@ export type MayrosConfig = { ui?: { /** Accent color for Mayros UI chrome (hex). */ seamColor?: string; - /** TUI color theme preset, or "auto" to detect from the terminal. */ - theme?: "auto" | "dark" | "light" | "high-contrast"; + /** + * TUI color theme: "auto" (detect from terminal), a built-in preset + * (dark, light, high-contrast, colorblind-dark, colorblind-light, + * monochrome, dracula, github-dark, etc.), or a custom theme name + * loaded from ~/.mayros/themes/*.json. + */ + theme?: string; /** Deprecated: vim mode is not available. Kept for config backward compat. */ vim?: boolean; /** Custom keybindings for TUI actions. */ diff --git a/src/tui/commands.ts b/src/tui/commands.ts index 42b71b19..c5020799 100644 --- a/src/tui/commands.ts +++ b/src/tui/commands.ts @@ -135,10 +135,6 @@ export function getSlashCommands(options: SlashCommandOptions = {}): SlashComman label: value, })), }, - { - name: "vim", - description: "Vim mode (unavailable)", - }, { name: "permission", description: "Set permission mode", @@ -248,7 +244,6 @@ export function helpText(options: SlashCommandOptions = {}): string { "/diff [file]", "/context", "/style ", - "/vim", "/permission ", "/fast", "/compact", diff --git a/src/version.ts b/src/version.ts index e64b5a39..e3bc58b8 100644 --- a/src/version.ts +++ b/src/version.ts @@ -1,7 +1,7 @@ import { createRequire } from "node:module"; declare const __MAYROS_VERSION__: string | undefined; -const CORE_PACKAGE_NAME = "mayros"; +const CORE_PACKAGE_NAME = "@apilium/mayros"; const PACKAGE_JSON_CANDIDATES = [ "../package.json", From c4e9b029d3a1814164f28a2c6e77a0e2289c305e Mon Sep 17 00:00:00 2001 From: ApiliumDevTeam Date: Wed, 15 Jul 2026 00:47:12 +0200 Subject: [PATCH 21/22] release: 0.4.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7d7519b3..65053f3b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros", - "version": "0.3.2", + "version": "0.4.0", "description": "Multi-channel AI agent framework with knowledge graph, MCP support, and coding CLI", "keywords": [ "agent", From 737cc5cdf344f4eecfbd820b17b78d37e3af4061 Mon Sep 17 00:00:00 2001 From: ApiliumDevTeam Date: Wed, 15 Jul 2026 00:47:12 +0200 Subject: [PATCH 22/22] chore(plugins): sync extension versions to 0.4.0 --- extensions/agent-mesh/package.json | 2 +- extensions/analytics/package.json | 2 +- extensions/bash-sandbox/package.json | 2 +- extensions/bluebubbles/package.json | 2 +- extensions/ci-plugin/package.json | 2 +- extensions/code-indexer/package.json | 2 +- extensions/code-tools/package.json | 2 +- extensions/copilot-proxy/package.json | 2 +- extensions/cortex-sync/package.json | 2 +- extensions/diagnostics-otel/package.json | 2 +- extensions/discord/package.json | 2 +- extensions/eruberu/package.json | 2 +- extensions/feishu/package.json | 2 +- extensions/google-antigravity-auth/package.json | 2 +- extensions/google-gemini-cli-auth/package.json | 2 +- extensions/googlechat/package.json | 2 +- extensions/hayameru/package.json | 2 +- extensions/imessage/package.json | 2 +- extensions/interactive-permissions/package.json | 2 +- extensions/iot-bridge/package.json | 2 +- extensions/irc/package.json | 2 +- extensions/kakeru-bridge/package.json | 2 +- extensions/line/package.json | 2 +- extensions/llm-hooks/package.json | 2 +- extensions/llm-task/package.json | 2 +- extensions/lobster/package.json | 2 +- extensions/lsp-bridge/package.json | 2 +- extensions/matrix/CHANGELOG.md | 5 +++++ extensions/matrix/package.json | 2 +- extensions/mattermost/package.json | 2 +- extensions/mcp-client/package.json | 2 +- extensions/mcp-server/package.json | 2 +- extensions/memory-core/package.json | 2 +- extensions/memory-lancedb/package.json | 2 +- extensions/memory-semantic/package.json | 2 +- extensions/minimax-portal-auth/package.json | 2 +- extensions/msteams/CHANGELOG.md | 5 +++++ extensions/msteams/package.json | 2 +- extensions/nextcloud-talk/package.json | 2 +- extensions/nostr/CHANGELOG.md | 5 +++++ extensions/nostr/package.json | 2 +- extensions/open-prose/package.json | 2 +- extensions/osameru-governance/package.json | 2 +- extensions/semantic-observability/package.json | 2 +- extensions/semantic-skills/package.json | 2 +- extensions/signal/package.json | 2 +- extensions/skill-hub/package.json | 2 +- extensions/slack/package.json | 2 +- extensions/telegram/package.json | 2 +- extensions/tlon/package.json | 2 +- extensions/token-economy/package.json | 2 +- extensions/tomeru-guard/package.json | 2 +- extensions/twitch/CHANGELOG.md | 5 +++++ extensions/twitch/package.json | 2 +- extensions/voice-call/CHANGELOG.md | 5 +++++ extensions/voice-call/package.json | 2 +- extensions/whatsapp/package.json | 2 +- extensions/zalo/CHANGELOG.md | 5 +++++ extensions/zalo/package.json | 2 +- extensions/zalouser/CHANGELOG.md | 5 +++++ extensions/zalouser/package.json | 2 +- 61 files changed, 89 insertions(+), 54 deletions(-) diff --git a/extensions/agent-mesh/package.json b/extensions/agent-mesh/package.json index 2c01258b..8edd4b86 100644 --- a/extensions/agent-mesh/package.json +++ b/extensions/agent-mesh/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-agent-mesh", - "version": "0.3.2", + "version": "0.4.0", "private": true, "description": "Mayros multi-agent coordination mesh with shared namespaces, delegation, and knowledge fusion", "type": "module", diff --git a/extensions/analytics/package.json b/extensions/analytics/package.json index a062a9e9..d2109de7 100644 --- a/extensions/analytics/package.json +++ b/extensions/analytics/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-analytics", - "version": "0.3.2", + "version": "0.4.0", "private": true, "type": "module", "main": "index.ts", diff --git a/extensions/bash-sandbox/package.json b/extensions/bash-sandbox/package.json index c746b9b9..d99e6327 100644 --- a/extensions/bash-sandbox/package.json +++ b/extensions/bash-sandbox/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-bash-sandbox", - "version": "0.3.2", + "version": "0.4.0", "private": true, "description": "Bash command sandbox with domain allowlist, command blocklist, and dangerous pattern detection", "type": "module", diff --git a/extensions/bluebubbles/package.json b/extensions/bluebubbles/package.json index c998108b..72d36178 100644 --- a/extensions/bluebubbles/package.json +++ b/extensions/bluebubbles/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-bluebubbles", - "version": "0.3.2", + "version": "0.4.0", "description": "Mayros BlueBubbles channel plugin", "license": "MIT", "type": "module", diff --git a/extensions/ci-plugin/package.json b/extensions/ci-plugin/package.json index 6b0c6fb3..981796ed 100644 --- a/extensions/ci-plugin/package.json +++ b/extensions/ci-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-ci-plugin", - "version": "0.3.2", + "version": "0.4.0", "description": "CI/CD pipeline integration for Mayros — GitHub Actions and GitLab CI providers", "type": "module", "dependencies": { diff --git a/extensions/code-indexer/package.json b/extensions/code-indexer/package.json index 49c60653..7af536da 100644 --- a/extensions/code-indexer/package.json +++ b/extensions/code-indexer/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-code-indexer", - "version": "0.3.2", + "version": "0.4.0", "private": true, "description": "Mayros code indexer plugin — regex-based codebase scanning with RDF triple storage in Cortex", "type": "module", diff --git a/extensions/code-tools/package.json b/extensions/code-tools/package.json index 4e78380f..cde08c2c 100644 --- a/extensions/code-tools/package.json +++ b/extensions/code-tools/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-code-tools", - "version": "0.3.2", + "version": "0.4.0", "private": true, "type": "module", "dependencies": { diff --git a/extensions/copilot-proxy/package.json b/extensions/copilot-proxy/package.json index c8e3a45a..d71ee020 100644 --- a/extensions/copilot-proxy/package.json +++ b/extensions/copilot-proxy/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-copilot-proxy", - "version": "0.3.2", + "version": "0.4.0", "private": true, "description": "Mayros Copilot Proxy provider plugin", "type": "module", diff --git a/extensions/cortex-sync/package.json b/extensions/cortex-sync/package.json index 784159ba..5fd80ef8 100644 --- a/extensions/cortex-sync/package.json +++ b/extensions/cortex-sync/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-cortex-sync", - "version": "0.3.2", + "version": "0.4.0", "private": true, "description": "Cortex DAG synchronization — peer discovery, delta sync, and cross-device knowledge replication", "type": "module", diff --git a/extensions/diagnostics-otel/package.json b/extensions/diagnostics-otel/package.json index ba1cff1b..5fbadafe 100644 --- a/extensions/diagnostics-otel/package.json +++ b/extensions/diagnostics-otel/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-diagnostics-otel", - "version": "0.3.2", + "version": "0.4.0", "description": "Mayros diagnostics OpenTelemetry exporter", "license": "MIT", "type": "module", diff --git a/extensions/discord/package.json b/extensions/discord/package.json index ba8dbf2d..f58a3ecd 100644 --- a/extensions/discord/package.json +++ b/extensions/discord/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-discord", - "version": "0.3.2", + "version": "0.4.0", "description": "Mayros Discord channel plugin", "license": "MIT", "type": "module", diff --git a/extensions/eruberu/package.json b/extensions/eruberu/package.json index d54e9aea..cfe9d2d7 100644 --- a/extensions/eruberu/package.json +++ b/extensions/eruberu/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-eruberu", - "version": "0.3.2", + "version": "0.4.0", "private": true, "description": "Mayros intelligent model routing plugin — Q-Learning adaptive provider/model selection", "type": "module", diff --git a/extensions/feishu/package.json b/extensions/feishu/package.json index a655ad3e..d6e90cfb 100644 --- a/extensions/feishu/package.json +++ b/extensions/feishu/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-feishu", - "version": "0.3.2", + "version": "0.4.0", "description": "Mayros Feishu/Lark channel plugin (community maintained by @m1heng)", "license": "MIT", "type": "module", diff --git a/extensions/google-antigravity-auth/package.json b/extensions/google-antigravity-auth/package.json index 84cdf2e8..428f5c7a 100644 --- a/extensions/google-antigravity-auth/package.json +++ b/extensions/google-antigravity-auth/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-google-antigravity-auth", - "version": "0.3.2", + "version": "0.4.0", "private": true, "description": "Mayros Google Antigravity OAuth provider plugin", "type": "module", diff --git a/extensions/google-gemini-cli-auth/package.json b/extensions/google-gemini-cli-auth/package.json index 43416054..69e3a622 100644 --- a/extensions/google-gemini-cli-auth/package.json +++ b/extensions/google-gemini-cli-auth/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-google-gemini-cli-auth", - "version": "0.3.2", + "version": "0.4.0", "private": true, "description": "Mayros Gemini CLI OAuth provider plugin", "type": "module", diff --git a/extensions/googlechat/package.json b/extensions/googlechat/package.json index 85c1cae3..fad7093e 100644 --- a/extensions/googlechat/package.json +++ b/extensions/googlechat/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-googlechat", - "version": "0.3.2", + "version": "0.4.0", "private": true, "description": "Mayros Google Chat channel plugin", "type": "module", diff --git a/extensions/hayameru/package.json b/extensions/hayameru/package.json index 48221bd4..458267e4 100644 --- a/extensions/hayameru/package.json +++ b/extensions/hayameru/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-hayameru", - "version": "0.3.2", + "version": "0.4.0", "private": true, "description": "Mayros deterministic code transforms — bypass LLM for simple edits", "type": "module", diff --git a/extensions/imessage/package.json b/extensions/imessage/package.json index acc34119..ee873ea7 100644 --- a/extensions/imessage/package.json +++ b/extensions/imessage/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-imessage", - "version": "0.3.2", + "version": "0.4.0", "private": true, "description": "Mayros iMessage channel plugin", "type": "module", diff --git a/extensions/interactive-permissions/package.json b/extensions/interactive-permissions/package.json index 9e052ec0..fcf12fdb 100644 --- a/extensions/interactive-permissions/package.json +++ b/extensions/interactive-permissions/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-interactive-permissions", - "version": "0.3.2", + "version": "0.4.0", "private": true, "description": "Runtime permission dialogs, bash intent classification, policy persistence, and audit trail", "type": "module", diff --git a/extensions/iot-bridge/package.json b/extensions/iot-bridge/package.json index bea1833d..70405e0f 100644 --- a/extensions/iot-bridge/package.json +++ b/extensions/iot-bridge/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-iot-bridge", - "version": "0.3.2", + "version": "0.4.0", "private": true, "description": "IoT Bridge — connect MAYROS agents to aingle_minimal IoT nodes via REST", "type": "module", diff --git a/extensions/irc/package.json b/extensions/irc/package.json index 22ef7ca8..b697eb75 100644 --- a/extensions/irc/package.json +++ b/extensions/irc/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-irc", - "version": "0.3.2", + "version": "0.4.0", "description": "Mayros IRC channel plugin", "license": "MIT", "type": "module", diff --git a/extensions/kakeru-bridge/package.json b/extensions/kakeru-bridge/package.json index fe9bacb0..1a608152 100644 --- a/extensions/kakeru-bridge/package.json +++ b/extensions/kakeru-bridge/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-kakeru-bridge", - "version": "0.3.2", + "version": "0.4.0", "private": true, "description": "Mayros dual-platform coordination bridge — Claude Code + Codex CLI", "type": "module", diff --git a/extensions/line/package.json b/extensions/line/package.json index ad658869..f783badc 100644 --- a/extensions/line/package.json +++ b/extensions/line/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-line", - "version": "0.3.2", + "version": "0.4.0", "private": true, "description": "Mayros LINE channel plugin", "type": "module", diff --git a/extensions/llm-hooks/package.json b/extensions/llm-hooks/package.json index b754cb38..bb66a6d6 100644 --- a/extensions/llm-hooks/package.json +++ b/extensions/llm-hooks/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-llm-hooks", - "version": "0.3.2", + "version": "0.4.0", "private": true, "description": "Markdown-defined hooks evaluated by LLM for policy enforcement", "type": "module", diff --git a/extensions/llm-task/package.json b/extensions/llm-task/package.json index 47e82780..b5f26842 100644 --- a/extensions/llm-task/package.json +++ b/extensions/llm-task/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-llm-task", - "version": "0.3.2", + "version": "0.4.0", "private": true, "description": "Mayros JSON-only LLM task plugin", "type": "module", diff --git a/extensions/lobster/package.json b/extensions/lobster/package.json index 90703821..78089e24 100644 --- a/extensions/lobster/package.json +++ b/extensions/lobster/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-lobster", - "version": "0.3.2", + "version": "0.4.0", "description": "Lobster workflow tool plugin (typed pipelines + resumable approvals)", "license": "MIT", "type": "module", diff --git a/extensions/lsp-bridge/package.json b/extensions/lsp-bridge/package.json index 917573d1..da41269e 100644 --- a/extensions/lsp-bridge/package.json +++ b/extensions/lsp-bridge/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-lsp-bridge", - "version": "0.3.2", + "version": "0.4.0", "description": "Cortex-backed language server bridge for Mayros — hover, diagnostics, go-to-definition", "type": "module", "dependencies": { diff --git a/extensions/matrix/CHANGELOG.md b/extensions/matrix/CHANGELOG.md index 64623bc7..6177208a 100644 --- a/extensions/matrix/CHANGELOG.md +++ b/extensions/matrix/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 0.4.0 + +### Changes +- Version alignment with core Mayros release numbers. + ## 0.3.2 ### Changes diff --git a/extensions/matrix/package.json b/extensions/matrix/package.json index 751fd5a5..1fc83d00 100644 --- a/extensions/matrix/package.json +++ b/extensions/matrix/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-matrix", - "version": "0.3.2", + "version": "0.4.0", "description": "Mayros Matrix channel plugin", "license": "MIT", "type": "module", diff --git a/extensions/mattermost/package.json b/extensions/mattermost/package.json index d2d8b6fa..5ad3dc41 100644 --- a/extensions/mattermost/package.json +++ b/extensions/mattermost/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-mattermost", - "version": "0.3.2", + "version": "0.4.0", "private": true, "description": "Mayros Mattermost channel plugin", "type": "module", diff --git a/extensions/mcp-client/package.json b/extensions/mcp-client/package.json index 88064499..68055bc7 100644 --- a/extensions/mcp-client/package.json +++ b/extensions/mcp-client/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-mcp-client", - "version": "0.3.2", + "version": "0.4.0", "private": true, "description": "MCP server client with multi-transport support and Cortex tool registry", "type": "module", diff --git a/extensions/mcp-server/package.json b/extensions/mcp-server/package.json index 61df3296..50ebca9f 100644 --- a/extensions/mcp-server/package.json +++ b/extensions/mcp-server/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-mcp-server", - "version": "0.3.2", + "version": "0.4.0", "private": true, "description": "MCP server exposing Mayros tools, Cortex resources, and workflow prompts via Model Context Protocol", "type": "module", diff --git a/extensions/memory-core/package.json b/extensions/memory-core/package.json index 1c44aa27..479f8b2e 100644 --- a/extensions/memory-core/package.json +++ b/extensions/memory-core/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-memory-core", - "version": "0.3.2", + "version": "0.4.0", "private": true, "description": "Mayros core memory search plugin", "type": "module", diff --git a/extensions/memory-lancedb/package.json b/extensions/memory-lancedb/package.json index b73f5dc9..976d3063 100644 --- a/extensions/memory-lancedb/package.json +++ b/extensions/memory-lancedb/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-memory-lancedb", - "version": "0.3.2", + "version": "0.4.0", "private": true, "description": "Mayros LanceDB-backed long-term memory plugin with auto-recall/capture", "type": "module", diff --git a/extensions/memory-semantic/package.json b/extensions/memory-semantic/package.json index b9c4fd55..82562780 100644 --- a/extensions/memory-semantic/package.json +++ b/extensions/memory-semantic/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-memory-semantic", - "version": "0.3.2", + "version": "0.4.0", "private": true, "description": "Mayros semantic memory plugin via AIngle Cortex sidecar (RDF triples, identity graph, Ineru STM/LTM)", "type": "module", diff --git a/extensions/minimax-portal-auth/package.json b/extensions/minimax-portal-auth/package.json index 1f2d9870..0ce3c75a 100644 --- a/extensions/minimax-portal-auth/package.json +++ b/extensions/minimax-portal-auth/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-minimax-portal-auth", - "version": "0.3.2", + "version": "0.4.0", "private": true, "description": "Mayros MiniMax Portal OAuth provider plugin", "type": "module", diff --git a/extensions/msteams/CHANGELOG.md b/extensions/msteams/CHANGELOG.md index a65d9d2b..c73fd654 100644 --- a/extensions/msteams/CHANGELOG.md +++ b/extensions/msteams/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 0.4.0 + +### Changes +- Version alignment with core Mayros release numbers. + ## 0.3.2 ### Changes diff --git a/extensions/msteams/package.json b/extensions/msteams/package.json index 82609def..b13e909e 100644 --- a/extensions/msteams/package.json +++ b/extensions/msteams/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-msteams", - "version": "0.3.2", + "version": "0.4.0", "description": "Mayros Microsoft Teams channel plugin", "license": "MIT", "type": "module", diff --git a/extensions/nextcloud-talk/package.json b/extensions/nextcloud-talk/package.json index 4705fcbb..d22e216e 100644 --- a/extensions/nextcloud-talk/package.json +++ b/extensions/nextcloud-talk/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-nextcloud-talk", - "version": "0.3.2", + "version": "0.4.0", "description": "Mayros Nextcloud Talk channel plugin", "license": "MIT", "type": "module", diff --git a/extensions/nostr/CHANGELOG.md b/extensions/nostr/CHANGELOG.md index d365d159..d2a54519 100644 --- a/extensions/nostr/CHANGELOG.md +++ b/extensions/nostr/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 0.4.0 + +### Changes +- Version alignment with core Mayros release numbers. + ## 0.3.2 ### Changes diff --git a/extensions/nostr/package.json b/extensions/nostr/package.json index e7326501..4b2801d1 100644 --- a/extensions/nostr/package.json +++ b/extensions/nostr/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-nostr", - "version": "0.3.2", + "version": "0.4.0", "description": "Mayros Nostr channel plugin for NIP-04 encrypted DMs", "license": "MIT", "type": "module", diff --git a/extensions/open-prose/package.json b/extensions/open-prose/package.json index 716a4436..7c5775fd 100644 --- a/extensions/open-prose/package.json +++ b/extensions/open-prose/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-open-prose", - "version": "0.3.2", + "version": "0.4.0", "private": true, "description": "OpenProse VM skill pack plugin (slash command + telemetry).", "type": "module", diff --git a/extensions/osameru-governance/package.json b/extensions/osameru-governance/package.json index fc1b0f85..a8ba025d 100644 --- a/extensions/osameru-governance/package.json +++ b/extensions/osameru-governance/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-osameru-governance", - "version": "0.3.2", + "version": "0.4.0", "private": true, "description": "Mayros governance control plane — policy enforcement, HMAC audit trail, trust tiers", "type": "module", diff --git a/extensions/semantic-observability/package.json b/extensions/semantic-observability/package.json index beab1c5c..8a6a5003 100644 --- a/extensions/semantic-observability/package.json +++ b/extensions/semantic-observability/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-semantic-observability", - "version": "0.3.2", + "version": "0.4.0", "private": true, "description": "Mayros semantic observability plugin — structured tracing of agent decisions as RDF events", "type": "module", diff --git a/extensions/semantic-skills/package.json b/extensions/semantic-skills/package.json index 512a5ad7..680517e9 100644 --- a/extensions/semantic-skills/package.json +++ b/extensions/semantic-skills/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-semantic-skills", - "version": "0.3.2", + "version": "0.4.0", "private": true, "description": "Mayros semantic skills plugin — graph-aware skills with PoL assertions, ZK proofs, and permission gating", "type": "module", diff --git a/extensions/signal/package.json b/extensions/signal/package.json index b539be8c..10cc6d9b 100644 --- a/extensions/signal/package.json +++ b/extensions/signal/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-signal", - "version": "0.3.2", + "version": "0.4.0", "private": true, "description": "Mayros Signal channel plugin", "type": "module", diff --git a/extensions/skill-hub/package.json b/extensions/skill-hub/package.json index 4c25b629..8d7ad0fe 100644 --- a/extensions/skill-hub/package.json +++ b/extensions/skill-hub/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-skill-hub", - "version": "0.3.2", + "version": "0.4.0", "private": true, "description": "Apilium Hub marketplace — publish, install, sign, and verify semantic skills", "type": "module", diff --git a/extensions/slack/package.json b/extensions/slack/package.json index 99429875..071ade4a 100644 --- a/extensions/slack/package.json +++ b/extensions/slack/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-slack", - "version": "0.3.2", + "version": "0.4.0", "private": true, "description": "Mayros Slack channel plugin", "type": "module", diff --git a/extensions/telegram/package.json b/extensions/telegram/package.json index 84edf968..5fdbfee6 100644 --- a/extensions/telegram/package.json +++ b/extensions/telegram/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-telegram", - "version": "0.3.2", + "version": "0.4.0", "private": true, "description": "Mayros Telegram channel plugin", "type": "module", diff --git a/extensions/tlon/package.json b/extensions/tlon/package.json index cbd29be8..da06bf56 100644 --- a/extensions/tlon/package.json +++ b/extensions/tlon/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-tlon", - "version": "0.3.2", + "version": "0.4.0", "private": true, "description": "Mayros Tlon/Urbit channel plugin", "type": "module", diff --git a/extensions/token-economy/package.json b/extensions/token-economy/package.json index ebe5fc67..3fa5143e 100644 --- a/extensions/token-economy/package.json +++ b/extensions/token-economy/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-token-economy", - "version": "0.3.2", + "version": "0.4.0", "private": true, "description": "Mayros token economy plugin — per-session cost tracking, configurable budgets with soft-stop, and prompt-level memoization", "type": "module", diff --git a/extensions/tomeru-guard/package.json b/extensions/tomeru-guard/package.json index 0acb7cde..d32c6523 100644 --- a/extensions/tomeru-guard/package.json +++ b/extensions/tomeru-guard/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-tomeru-guard", - "version": "0.3.2", + "version": "0.4.0", "private": true, "description": "Mayros rate limiting and loop breaking plugin — prevents runaway tool execution", "type": "module", diff --git a/extensions/twitch/CHANGELOG.md b/extensions/twitch/CHANGELOG.md index d0451a82..3fec311b 100644 --- a/extensions/twitch/CHANGELOG.md +++ b/extensions/twitch/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 0.4.0 + +### Changes +- Version alignment with core Mayros release numbers. + ## 0.3.2 ### Changes diff --git a/extensions/twitch/package.json b/extensions/twitch/package.json index 2f618ee6..7d8ec133 100644 --- a/extensions/twitch/package.json +++ b/extensions/twitch/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-twitch", - "version": "0.3.2", + "version": "0.4.0", "private": true, "description": "Mayros Twitch channel plugin", "type": "module", diff --git a/extensions/voice-call/CHANGELOG.md b/extensions/voice-call/CHANGELOG.md index 2b360d38..0df7e02d 100644 --- a/extensions/voice-call/CHANGELOG.md +++ b/extensions/voice-call/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 0.4.0 + +### Changes +- Version alignment with core Mayros release numbers. + ## 0.3.2 ### Changes diff --git a/extensions/voice-call/package.json b/extensions/voice-call/package.json index c6745a0e..7131584c 100644 --- a/extensions/voice-call/package.json +++ b/extensions/voice-call/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-voice-call", - "version": "0.3.2", + "version": "0.4.0", "description": "Mayros voice-call plugin", "license": "MIT", "type": "module", diff --git a/extensions/whatsapp/package.json b/extensions/whatsapp/package.json index e1a85ede..cad10619 100644 --- a/extensions/whatsapp/package.json +++ b/extensions/whatsapp/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-whatsapp", - "version": "0.3.2", + "version": "0.4.0", "private": true, "description": "Mayros WhatsApp channel plugin", "type": "module", diff --git a/extensions/zalo/CHANGELOG.md b/extensions/zalo/CHANGELOG.md index 60f246ae..a34a24df 100644 --- a/extensions/zalo/CHANGELOG.md +++ b/extensions/zalo/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 0.4.0 + +### Changes +- Version alignment with core Mayros release numbers. + ## 0.3.2 ### Changes diff --git a/extensions/zalo/package.json b/extensions/zalo/package.json index bb9cb955..4de21e00 100644 --- a/extensions/zalo/package.json +++ b/extensions/zalo/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-zalo", - "version": "0.3.2", + "version": "0.4.0", "description": "Mayros Zalo channel plugin", "license": "MIT", "type": "module", diff --git a/extensions/zalouser/CHANGELOG.md b/extensions/zalouser/CHANGELOG.md index c5beb1da..29d5aa24 100644 --- a/extensions/zalouser/CHANGELOG.md +++ b/extensions/zalouser/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 0.4.0 + +### Changes +- Version alignment with core Mayros release numbers. + ## 0.3.2 ### Changes diff --git a/extensions/zalouser/package.json b/extensions/zalouser/package.json index 8f7c0f7a..066b991c 100644 --- a/extensions/zalouser/package.json +++ b/extensions/zalouser/package.json @@ -1,6 +1,6 @@ { "name": "@apilium/mayros-zalouser", - "version": "0.3.2", + "version": "0.4.0", "description": "Mayros Zalo Personal Account plugin via zca-cli", "license": "MIT", "type": "module",