From 11462ee134c2d2f95e401541c87ca45847ecd646 Mon Sep 17 00:00:00 2001 From: Ra's al Ghul Date: Fri, 27 Feb 2026 21:55:27 -0500 Subject: [PATCH 01/14] feat(models): set requested gemini/codex defaults and document grok lane --- extensions/braintrust/README.md | 7 +++++++ extensions/braintrust/openclaw.plugin.json | 6 +++--- extensions/braintrust/src/settings.ts | 6 +++--- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/extensions/braintrust/README.md b/extensions/braintrust/README.md index 1b53677..1e76d4b 100644 --- a/extensions/braintrust/README.md +++ b/extensions/braintrust/README.md @@ -25,3 +25,10 @@ See `src/policy.ts` and `src/policy.test.ts`. ```bash pnpm vitest run extensions/braintrust/src/policy.test.ts extensions/braintrust/src/settings.test.ts ``` + + +## Default model routing +- solver (`model`): `gemini-3-flash-preview` +- critic (`criticModel`): `openai-codex/gpt-5.3-codex` +- synthesizer (`synthModel`): `gemini-3.1-pro-preview` +- additional small-model lane (runtime wiring target): `grok-4-1-fast-reasoning` diff --git a/extensions/braintrust/openclaw.plugin.json b/extensions/braintrust/openclaw.plugin.json index 0c343a8..23e11a4 100644 --- a/extensions/braintrust/openclaw.plugin.json +++ b/extensions/braintrust/openclaw.plugin.json @@ -7,9 +7,9 @@ "enabled": { "type": "boolean", "default": false }, "teamSize": { "type": "integer", "minimum": 1, "maximum": 4, "default": 3 }, "strategy": { "type": "string", "enum": ["independent", "debate", "panel"], "default": "panel" }, - "model": { "type": "string", "default": "sonnet" }, - "criticModel": { "type": "string", "default": "sonnet" }, - "synthModel": { "type": "string", "default": "opus" }, + "model": { "type": "string", "default": "gemini-3-flash-preview" }, + "criticModel": { "type": "string", "default": "openai-codex/gpt-5.3-codex" }, + "synthModel": { "type": "string", "default": "gemini-3.1-pro-preview" }, "timeoutSeconds": { "type": "integer", "minimum": 10, "maximum": 300, "default": 90 }, "minParticipatingAgents": { "type": "integer", "minimum": 1, "maximum": 4, "default": 2 }, "minAnsweringAgents": { "type": "integer", "minimum": 1, "maximum": 4, "default": 2 } diff --git a/extensions/braintrust/src/settings.ts b/extensions/braintrust/src/settings.ts index d1fb495..32b26ae 100644 --- a/extensions/braintrust/src/settings.ts +++ b/extensions/braintrust/src/settings.ts @@ -18,9 +18,9 @@ export const DEFAULT_SETTINGS: BraintrustSettings = { enabled: false, teamSize: 3, strategy: "panel", - model: "sonnet", - criticModel: "sonnet", - synthModel: "opus", + model: "gemini-3-flash-preview", + criticModel: "openai-codex/gpt-5.3-codex", + synthModel: "gemini-3.1-pro-preview", timeoutSeconds: 90, minParticipatingAgents: DEFAULT_QUORUM.minParticipatingAgents, minAnsweringAgents: DEFAULT_QUORUM.minAnsweringAgents, From a568a935d9b5e16cdd25fc65922ba5b3db05f657 Mon Sep 17 00:00:00 2001 From: Ra's al Ghul Date: Fri, 27 Feb 2026 21:58:20 -0500 Subject: [PATCH 02/14] feat(models): activate all four model lanes in runtime assignment --- extensions/braintrust/README.md | 2 +- extensions/braintrust/index.ts | 1 + extensions/braintrust/openclaw.plugin.json | 1 + .../braintrust/src/runtime-bridge.test.ts | 30 +++++++++++++++++++ extensions/braintrust/src/runtime-bridge.ts | 1 + extensions/braintrust/src/settings.test.ts | 1 + extensions/braintrust/src/settings.ts | 3 ++ 7 files changed, 38 insertions(+), 1 deletion(-) diff --git a/extensions/braintrust/README.md b/extensions/braintrust/README.md index 1e76d4b..9ec95b3 100644 --- a/extensions/braintrust/README.md +++ b/extensions/braintrust/README.md @@ -31,4 +31,4 @@ pnpm vitest run extensions/braintrust/src/policy.test.ts extensions/braintrust/s - solver (`model`): `gemini-3-flash-preview` - critic (`criticModel`): `openai-codex/gpt-5.3-codex` - synthesizer (`synthModel`): `gemini-3.1-pro-preview` -- additional small-model lane (runtime wiring target): `grok-4-1-fast-reasoning` +- researcher (`researcherModel`): `grok-4-1-fast-reasoning` diff --git a/extensions/braintrust/index.ts b/extensions/braintrust/index.ts index 0306739..6645b7e 100644 --- a/extensions/braintrust/index.ts +++ b/extensions/braintrust/index.ts @@ -140,6 +140,7 @@ export default { `teamSize=${settings.teamSize}`, `solver=${settings.model}`, `critic=${settings.criticModel}`, + `researcher=${settings.researcherModel}`, `synth=${settings.synthModel}`, `timeout=${settings.timeoutSeconds}s`, `minParticipating=${settings.minParticipatingAgents}`, diff --git a/extensions/braintrust/openclaw.plugin.json b/extensions/braintrust/openclaw.plugin.json index 23e11a4..0fb0685 100644 --- a/extensions/braintrust/openclaw.plugin.json +++ b/extensions/braintrust/openclaw.plugin.json @@ -9,6 +9,7 @@ "strategy": { "type": "string", "enum": ["independent", "debate", "panel"], "default": "panel" }, "model": { "type": "string", "default": "gemini-3-flash-preview" }, "criticModel": { "type": "string", "default": "openai-codex/gpt-5.3-codex" }, + "researcherModel": { "type": "string", "default": "grok-4-1-fast-reasoning" }, "synthModel": { "type": "string", "default": "gemini-3.1-pro-preview" }, "timeoutSeconds": { "type": "integer", "minimum": 10, "maximum": 300, "default": 90 }, "minParticipatingAgents": { "type": "integer", "minimum": 1, "maximum": 4, "default": 2 }, diff --git a/extensions/braintrust/src/runtime-bridge.test.ts b/extensions/braintrust/src/runtime-bridge.test.ts index a5353dd..36bce91 100644 --- a/extensions/braintrust/src/runtime-bridge.test.ts +++ b/extensions/braintrust/src/runtime-bridge.test.ts @@ -78,3 +78,33 @@ describe("runRuntimeBridge", () => { expect(result.unavailable).toBe(true); }); }); + + + it("uses four distinct models when teamSize=4", async () => { + const settings = { + ...DEFAULT_SETTINGS, + enabled: true, + teamSize: 4, + model: "gemini-3-flash-preview", + criticModel: "openai-codex/gpt-5.3-codex", + researcherModel: "grok-4-1-fast-reasoning", + synthModel: "gemini-3.1-pro-preview", + minParticipatingAgents: 2, + minAnsweringAgents: 2, + }; + const seen = []; + const result = await runRuntimeBridge( + { prompt: "hello", settings }, + async ({ model }) => { + seen.push(model); + return { text: `ok-${model}`, refusal: false }; + }, + ); + expect(result.unavailable).toBe(false); + expect(new Set(seen)).toEqual(new Set([ + "gemini-3-flash-preview", + "openai-codex/gpt-5.3-codex", + "grok-4-1-fast-reasoning", + "gemini-3.1-pro-preview", + ])); + }); diff --git a/extensions/braintrust/src/runtime-bridge.ts b/extensions/braintrust/src/runtime-bridge.ts index 77efbeb..20a6c1d 100644 --- a/extensions/braintrust/src/runtime-bridge.ts +++ b/extensions/braintrust/src/runtime-bridge.ts @@ -39,6 +39,7 @@ function roleFor(index: number): CandidateRunnerInput["role"] { function modelFor(index: number, settings: BraintrustSettings): string { if (index === settings.teamSize - 1) return settings.synthModel; if (index === 1) return settings.criticModel; + if (index === 2) return settings.researcherModel; return settings.model; } diff --git a/extensions/braintrust/src/settings.test.ts b/extensions/braintrust/src/settings.test.ts index cd2e794..9a4c437 100644 --- a/extensions/braintrust/src/settings.test.ts +++ b/extensions/braintrust/src/settings.test.ts @@ -18,5 +18,6 @@ describe("readSettings", () => { const got = readSettings({ strategy: "debate", enabled: true }); expect(got.strategy).toBe("debate"); expect(got.enabled).toBe(true); + expect(got.researcherModel).toBe("grok-4-1-fast-reasoning"); }); }); diff --git a/extensions/braintrust/src/settings.ts b/extensions/braintrust/src/settings.ts index 32b26ae..197b256 100644 --- a/extensions/braintrust/src/settings.ts +++ b/extensions/braintrust/src/settings.ts @@ -8,6 +8,7 @@ export type BraintrustSettings = { strategy: BraintrustStrategy; model: string; criticModel: string; + researcherModel: string; synthModel: string; timeoutSeconds: number; minParticipatingAgents: number; @@ -20,6 +21,7 @@ export const DEFAULT_SETTINGS: BraintrustSettings = { strategy: "panel", model: "gemini-3-flash-preview", criticModel: "openai-codex/gpt-5.3-codex", + researcherModel: "grok-4-1-fast-reasoning", synthModel: "gemini-3.1-pro-preview", timeoutSeconds: 90, minParticipatingAgents: DEFAULT_QUORUM.minParticipatingAgents, @@ -59,6 +61,7 @@ export function readSettings(raw: Record | undefined): Braintru strategy, model: String(p.model ?? DEFAULT_SETTINGS.model), criticModel: String(p.criticModel ?? DEFAULT_SETTINGS.criticModel), + researcherModel: String(p.researcherModel ?? DEFAULT_SETTINGS.researcherModel), synthModel: String(p.synthModel ?? DEFAULT_SETTINGS.synthModel), timeoutSeconds: clampInt(p.timeoutSeconds, 10, 300, DEFAULT_SETTINGS.timeoutSeconds), minParticipatingAgents, From a23d3ae5e65648dbaf16d60843ae2cb8074e0caf Mon Sep 17 00:00:00 2001 From: Ra's al Ghul Date: Fri, 27 Feb 2026 22:02:46 -0500 Subject: [PATCH 03/14] fix(plugin): align plugin id with package hint so command registers --- extensions/braintrust/README.md | 3 +++ extensions/braintrust/index.ts | 2 +- extensions/braintrust/openclaw.plugin.json | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/extensions/braintrust/README.md b/extensions/braintrust/README.md index 9ec95b3..df2ef06 100644 --- a/extensions/braintrust/README.md +++ b/extensions/braintrust/README.md @@ -32,3 +32,6 @@ pnpm vitest run extensions/braintrust/src/policy.test.ts extensions/braintrust/s - critic (`criticModel`): `openai-codex/gpt-5.3-codex` - synthesizer (`synthModel`): `gemini-3.1-pro-preview` - researcher (`researcherModel`): `grok-4-1-fast-reasoning` + + +Note: plugin id is `braintrust-plugin`; command remains `/braintrust`. diff --git a/extensions/braintrust/index.ts b/extensions/braintrust/index.ts index 6645b7e..165163b 100644 --- a/extensions/braintrust/index.ts +++ b/extensions/braintrust/index.ts @@ -123,7 +123,7 @@ function buildFallbackPrepend(settings: ReturnType): string } export default { - id: "braintrust", + id: "braintrust-plugin", name: "Braintrust", description: "Multi-agent orchestration control plane", configSchema: emptyPluginConfigSchema(), diff --git a/extensions/braintrust/openclaw.plugin.json b/extensions/braintrust/openclaw.plugin.json index 0fb0685..742fe69 100644 --- a/extensions/braintrust/openclaw.plugin.json +++ b/extensions/braintrust/openclaw.plugin.json @@ -1,5 +1,5 @@ { - "id": "braintrust", + "id": "braintrust-plugin", "configSchema": { "type": "object", "additionalProperties": false, From ffcd8a686f7cbe19cff55fb795d4fb224d98c2d0 Mon Sep 17 00:00:00 2001 From: Ra's al Ghul Date: Fri, 27 Feb 2026 22:15:47 -0500 Subject: [PATCH 04/14] fix(plugin): make hook payload handling robust across runtime shapes --- extensions/braintrust/index.ts | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/extensions/braintrust/index.ts b/extensions/braintrust/index.ts index 165163b..81b029a 100644 --- a/extensions/braintrust/index.ts +++ b/extensions/braintrust/index.ts @@ -229,19 +229,22 @@ export default { }; }); - api.on("llm_input", async ({ event, context }) => { + api.on("llm_input", async (payload) => { if (!enabled) return; - const e = event as PluginHookLlmInputEvent; + const event = ((payload as any)?.event ?? payload) as PluginHookLlmInputEvent; + const context = ((payload as any)?.context ?? {}) as any; + const e = event; api.logger.info( - `[braintrust] llm_input run=${e.runId} session=${context.sessionKey ?? "unknown"} model=${e.model}`, + `[braintrust] llm_input run=${(e as any)?.runId ?? "unknown"} session=${context?.sessionKey ?? "unknown"} model=${(e as any)?.model ?? "unknown"}`, ); }); - api.on("llm_output", async ({ event }) => { + api.on("llm_output", async (payload) => { if (!enabled) return; - const e = event as PluginHookLlmOutputEvent; + const event = ((payload as any)?.event ?? payload) as PluginHookLlmOutputEvent; + const e = event; api.logger.info( - `[braintrust] llm_output run=${e.runId} model=${e.model} responses=${e.assistantTexts.length}`, + `[braintrust] llm_output run=${(e as any)?.runId ?? "unknown"} model=${(e as any)?.model ?? "unknown"} responses=${Array.isArray((e as any)?.assistantTexts) ? (e as any).assistantTexts.length : 0}`, ); }); }, From 64141022ecc289e3638051ff86befa9afdf2577b Mon Sep 17 00:00:00 2001 From: Ra's al Ghul Date: Fri, 27 Feb 2026 22:21:33 -0500 Subject: [PATCH 05/14] Harden braintrust hook payload handling --- extensions/braintrust/index.ts | 7 +++-- extensions/braintrust/src/index.test.ts | 41 ++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/extensions/braintrust/index.ts b/extensions/braintrust/index.ts index 81b029a..3ba0165 100644 --- a/extensions/braintrust/index.ts +++ b/extensions/braintrust/index.ts @@ -179,10 +179,11 @@ export default { }, }); - api.on("before_prompt_build", async ({ event, context }) => { + api.on("before_prompt_build", async (payload) => { if (!enabled) return; - const e = event as PluginHookBeforePromptBuildEvent; - const prompt = extractPromptFromMessages(e.messages); + const event = ((payload as any)?.event ?? payload ?? {}) as PluginHookBeforePromptBuildEvent; + const context = ((payload as any)?.context ?? {}) as any; + const prompt = extractPromptFromMessages((event as any)?.messages); const execute = resolveRuntimeExecutor(api, context); if (!execute || !prompt) { diff --git a/extensions/braintrust/src/index.test.ts b/extensions/braintrust/src/index.test.ts index 8238c1b..779e342 100644 --- a/extensions/braintrust/src/index.test.ts +++ b/extensions/braintrust/src/index.test.ts @@ -4,7 +4,7 @@ vi.mock("openclaw/plugin-sdk", () => ({ emptyPluginConfigSchema: () => ({}), })); -type HookHandler = (args: { event: unknown; context?: unknown }) => Promise<{ prependContext?: string } | void>; +type HookHandler = (args: unknown) => Promise<{ prependContext?: string } | void>; type FakeApi = { pluginConfig: Record; @@ -76,4 +76,43 @@ describe("braintrust plugin runtime integration", () => { expect(out?.prependContext).toContain("BRAINTRUST MODE ACTIVE."); expect(out?.prependContext).toContain("Simulate roles:"); }); + + it("tolerates missing or malformed before_prompt_build payloads", async () => { + const { default: plugin } = await import("../index.js"); + const { api, hooks } = setupApi({ enabled: true, teamSize: 3 }); + + plugin.register(api as never); + + const beforePromptBuild = hooks.get("before_prompt_build"); + expect(beforePromptBuild).toBeTypeOf("function"); + + const malformedPayloads: unknown[] = [undefined, null, "oops", 42, {}, { event: null }, { event: { messages: null } }]; + + for (const payload of malformedPayloads) { + await expect(beforePromptBuild!(payload)).resolves.not.toThrow(); + } + }); + + it("tolerates missing or malformed llm_input/llm_output payloads", async () => { + const { default: plugin } = await import("../index.js"); + const { api, hooks, logger } = setupApi({ enabled: true, teamSize: 3 }); + + plugin.register(api as never); + + const llmInput = hooks.get("llm_input"); + const llmOutput = hooks.get("llm_output"); + + expect(llmInput).toBeTypeOf("function"); + expect(llmOutput).toBeTypeOf("function"); + + const malformedPayloads: unknown[] = [undefined, null, "oops", 42, {}, { event: null }, { event: { assistantTexts: "bad" } }]; + + for (const payload of malformedPayloads) { + await expect(llmInput!(payload)).resolves.not.toThrow(); + await expect(llmOutput!(payload)).resolves.not.toThrow(); + } + + expect(logger.info).toHaveBeenCalled(); + }); + }); From 5443a04ce3c90772fa4a9d27a191aba53d274daa Mon Sep 17 00:00:00 2001 From: Ra's al Ghul Date: Fri, 27 Feb 2026 22:23:49 -0500 Subject: [PATCH 06/14] fix(plugin): expose real Braintrust config schema to runtime --- extensions/braintrust/index.ts | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/extensions/braintrust/index.ts b/extensions/braintrust/index.ts index 3ba0165..6c440c0 100644 --- a/extensions/braintrust/index.ts +++ b/extensions/braintrust/index.ts @@ -5,11 +5,28 @@ import type { PluginHookLlmOutputEvent, } from "openclaw/plugin-sdk"; -import { emptyPluginConfigSchema } from "openclaw/plugin-sdk"; import { buildUnavailableNotice, DEFAULT_QUORUM, evaluateQuorum, type QuorumEvaluation } from "./src/policy.js"; import { runRuntimeBridge, type CandidateRunnerInput, type CandidateRunnerOutput } from "./src/runtime-bridge.js"; import { readSettings } from "./src/settings.js"; + +const braintrustConfigSchema = { + type: "object", + additionalProperties: false, + properties: { + enabled: { type: "boolean", default: false }, + teamSize: { type: "integer", minimum: 1, maximum: 4, default: 3 }, + strategy: { type: "string", enum: ["independent", "debate", "panel"], default: "panel" }, + model: { type: "string", default: "gemini-3-flash-preview" }, + criticModel: { type: "string", default: "openai-codex/gpt-5.3-codex" }, + researcherModel: { type: "string", default: "grok-4-1-fast-reasoning" }, + synthModel: { type: "string", default: "gemini-3.1-pro-preview" }, + timeoutSeconds: { type: "integer", minimum: 10, maximum: 300, default: 90 }, + minParticipatingAgents: { type: "integer", minimum: 1, maximum: 4, default: 2 }, + minAnsweringAgents: { type: "integer", minimum: 1, maximum: 4, default: 2 }, + }, +} as const; + type RuntimeExecuteInput = { model: string; prompt: string; @@ -126,7 +143,7 @@ export default { id: "braintrust-plugin", name: "Braintrust", description: "Multi-agent orchestration control plane", - configSchema: emptyPluginConfigSchema(), + configSchema: braintrustConfigSchema, register(api: OpenClawPluginApi) { const settings = readSettings(api.pluginConfig); From 9b24b668e994003e0c96a6ab6409ca8afac33b23 Mon Sep 17 00:00:00 2001 From: Ra's al Ghul Date: Fri, 27 Feb 2026 22:24:53 -0500 Subject: [PATCH 07/14] Add reproducible BrainTrust live validation script --- README.md | 13 +++ .../braintrust/src/live-validation.test.ts | 105 ++++++++++++++++++ scripts/validate-braintrust.sh | 29 +++++ 3 files changed, 147 insertions(+) create mode 100644 extensions/braintrust/src/live-validation.test.ts create mode 100755 scripts/validate-braintrust.sh diff --git a/README.md b/README.md index 9ec88df..98ad794 100644 --- a/README.md +++ b/README.md @@ -4,3 +4,16 @@ Plugin-only repo for OpenClaw Braintrust experimentation. Contents: - `extensions/braintrust/` โ€” Braintrust plugin scaffold (config, command, quorum policy tests) + +## Live Feature Validation + +Run the reproducible validation script: + +```bash +./scripts/validate-braintrust.sh +``` + +What it verifies: +- `/braintrust on` and `/braintrust status` command flow +- sample prompt path returns exactly one synthesized final output +- if quorum cannot be met, plugin emits explicit `Braintrust temporarily unavailable (...)` notice diff --git a/extensions/braintrust/src/live-validation.test.ts b/extensions/braintrust/src/live-validation.test.ts new file mode 100644 index 0000000..6b27b06 --- /dev/null +++ b/extensions/braintrust/src/live-validation.test.ts @@ -0,0 +1,105 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; + +vi.mock("openclaw/plugin-sdk", () => ({ + emptyPluginConfigSchema: () => ({}), +})); + +type HookHandler = (args: { event: unknown; context?: unknown }) => Promise<{ prependContext?: string } | void>; + +type FakeApi = { + pluginConfig: Record; + logger: { info: (message: string) => void }; + registerCommand: (command: unknown) => void; + on: (name: string, handler: HookHandler) => void; +}; + +function setupApi(pluginConfig: Record = {}) { + const hooks = new Map(); + const commands: unknown[] = []; + const logger = { info: vi.fn() }; + const api: FakeApi = { + pluginConfig, + logger, + registerCommand: (command) => { + commands.push(command); + }, + on: (name, handler) => { + hooks.set(name, handler); + }, + }; + return { api, hooks, commands, logger }; +} + +type Command = { handler: (ctx: { args?: string }) => Promise<{ text: string }> }; + +describe("live braintrust validation flow", () => { + beforeEach(() => { + vi.resetModules(); + }); + + it("validates /braintrust on + status + synthesized output path", async () => { + const { default: plugin } = await import("../index.js"); + const { api, hooks, commands } = setupApi({ enabled: false, teamSize: 3 }); + + plugin.register(api as never); + + const cmd = commands[0] as Command; + const on = await cmd.handler({ args: "on" }); + const status = await cmd.handler({ args: "status" }); + + expect(on.text).toContain("Braintrust: ON"); + expect(status.text).toContain("Braintrust: ON"); + + const beforePromptBuild = hooks.get("before_prompt_build"); + expect(beforePromptBuild).toBeTypeOf("function"); + + const out = await beforePromptBuild!({ + event: { messages: [{ role: "user", content: "Give me one rollout recommendation." }] }, + context: { + runModel: async ({ role }: { role: string }) => { + if (role === "critic") return { text: "Keep it small and reversible." }; + if (role === "researcher") return { text: "Ship behind a feature flag." }; + return { text: "Start with a 5% canary and monitor error budgets." }; + }, + }, + }); + + const prepend = out?.prependContext ?? ""; + expect(prepend).toContain("Use this runtime-bridge synthesis as your final answer."); + + const lines = prepend + .split("\n") + .map((line) => line.trim()) + .filter(Boolean) + .filter((line) => !line.startsWith("BRAINTRUST MODE ACTIVE")) + .filter((line) => !line.startsWith("Use this runtime-bridge synthesis as your final answer.")); + + expect(lines).toHaveLength(1); + }); + + it("returns explicit unavailable notice when quorum is not met", async () => { + const { default: plugin } = await import("../index.js"); + const { api, hooks, commands } = setupApi({ enabled: true, teamSize: 4, minAnsweringAgents: 3, minParticipatingAgents: 3 }); + + plugin.register(api as never); + + const cmd = commands[0] as Command; + const status = await cmd.handler({ args: "status" }); + expect(status.text).toContain("Braintrust: ON"); + + const beforePromptBuild = hooks.get("before_prompt_build"); + const out = await beforePromptBuild!({ + event: { messages: [{ role: "user", content: "Give me one rollout recommendation." }] }, + context: { + runModel: async ({ role }: { role: string }) => { + if (role === "critic") return { text: "I cannot comply with that request." }; + if (role === "researcher") throw new Error("simulated timeout"); + return { text: "candidate" }; + }, + }, + }); + + expect(out?.prependContext).toContain("Quorum could not be satisfied"); + expect(out?.prependContext).toContain("Braintrust temporarily unavailable"); + }); +}); diff --git a/scripts/validate-braintrust.sh b/scripts/validate-braintrust.sh new file mode 100755 index 0000000..b5fca6a --- /dev/null +++ b/scripts/validate-braintrust.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +set -euo pipefail + +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +PLUGIN_DIR="$REPO_ROOT/extensions/braintrust" + +if [[ ! -f "$PLUGIN_DIR/package.json" ]]; then + echo "[error] braintrust plugin package.json not found at: $PLUGIN_DIR" >&2 + exit 1 +fi + +echo "== BrainTrust live validation ==" +echo "repo: $REPO_ROOT" +echo "plugin: $PLUGIN_DIR" + +echo "[1/2] Running unit/runtime tests for command + synthesis contract..." +( + cd "$PLUGIN_DIR" + if command -v pnpm >/dev/null 2>&1; then + pnpm vitest run src/live-validation.test.ts src/runtime-bridge.integration.test.ts + else + npm exec --yes vitest run src/live-validation.test.ts src/runtime-bridge.integration.test.ts + fi +) + +echo "[2/2] Validation passed" +echo "- /braintrust on + status command path: OK" +echo "- sample prompt synthesis path: OK" +echo "- quorum-unavailable explicit notice path: OK" From 50ae00ac330f5debad748124bb1e86be7297a648 Mon Sep 17 00:00:00 2001 From: Ra's al Ghul Date: Fri, 27 Feb 2026 22:38:12 -0500 Subject: [PATCH 08/14] fix(plugin): ship braintrust runtime entry as javascript --- extensions/braintrust/index.js | 221 +++++++++++++++++++++ extensions/braintrust/openclaw.plugin.json | 66 +++++- extensions/braintrust/package.json | 2 +- 3 files changed, 277 insertions(+), 12 deletions(-) create mode 100644 extensions/braintrust/index.js diff --git a/extensions/braintrust/index.js b/extensions/braintrust/index.js new file mode 100644 index 0000000..d33ab0e --- /dev/null +++ b/extensions/braintrust/index.js @@ -0,0 +1,221 @@ +import { buildUnavailableNotice, DEFAULT_QUORUM, evaluateQuorum } from "./src/policy.js"; +import { runRuntimeBridge } from "./src/runtime-bridge.js"; +import { readSettings } from "./src/settings.js"; +const braintrustConfigSchema = { + type: "object", + additionalProperties: false, + properties: { + enabled: { type: "boolean", default: false }, + teamSize: { type: "integer", minimum: 1, maximum: 4, default: 3 }, + strategy: { type: "string", enum: ["independent", "debate", "panel"], default: "panel" }, + model: { type: "string", default: "gemini-3-flash-preview" }, + criticModel: { type: "string", default: "openai-codex/gpt-5.3-codex" }, + researcherModel: { type: "string", default: "grok-4-1-fast-reasoning" }, + synthModel: { type: "string", default: "gemini-3.1-pro-preview" }, + timeoutSeconds: { type: "integer", minimum: 10, maximum: 300, default: 90 }, + minParticipatingAgents: { type: "integer", minimum: 1, maximum: 4, default: 2 }, + minAnsweringAgents: { type: "integer", minimum: 1, maximum: 4, default: 2 } + } +}; +function formatQuorumStatus(e) { + if (!e) return `quorum: ${DEFAULT_QUORUM.minParticipatingAgents}/${DEFAULT_QUORUM.minAnsweringAgents}`; + return `participating=${e.participating} answering=${e.answering} refused=${e.refused} failed=${e.failed}`; +} +function extractPromptFromMessages(messages) { + if (!Array.isArray(messages)) return ""; + for (let i = messages.length - 1; i >= 0; i -= 1) { + const msg = messages[i]; + if (msg?.role !== "user") continue; + const c = msg.content; + if (typeof c === "string") return c; + if (Array.isArray(c)) { + const text = c.map((part) => typeof part === "string" ? part : part?.text ?? "").join("\n").trim(); + if (text) return text; + } + } + return ""; +} +function readTextOutput(out) { + if (typeof out === "string") return out; + if (!out || typeof out !== "object") return ""; + const candidate = out; + if (typeof candidate.text === "string") return candidate.text; + if (typeof candidate.outputText === "string") return candidate.outputText; + if (typeof candidate.assistantText === "string") return candidate.assistantText; + if (Array.isArray(candidate.assistantTexts) && typeof candidate.assistantTexts[0] === "string") { + return candidate.assistantTexts[0]; + } + if (typeof candidate.content === "string") return candidate.content; + if (Array.isArray(candidate.choices)) { + const first = candidate.choices[0]; + if (typeof first?.text === "string") return first.text; + if (typeof first?.message?.content === "string") return first.message.content; + } + return ""; +} +function resolveRuntimeExecutor(api, runtimeContext) { + const source = [ + runtimeContext, + api + ]; + const names = ["runModel", "invokeModel", "runLlm", "invokeLlm", "complete"]; + for (const obj of source) { + if (!obj) continue; + for (const name of names) { + const fn = obj[name]; + if (typeof fn !== "function") continue; + return async (input) => { + const out = await fn({ + model: input.model, + role: input.role, + timeoutSeconds: input.timeoutSeconds, + messages: [ + { + role: "system", + content: `You are Braintrust ${input.role}. Respond with one concise candidate answer for the user prompt.` + }, + { role: "user", content: input.prompt } + ] + }); + const text = readTextOutput(out).trim(); + if (!text) { + throw new Error("empty output"); + } + return { + text, + refusal: /\b(cannot comply|i can't|i cannot|refuse|not able)\b/i.test(text) + }; + }; + } + } + return void 0; +} +function buildFallbackPrepend(settings) { + return [ + "BRAINTRUST MODE ACTIVE.", + `Use a ${settings.teamSize}-agent internal panel with strategy=${settings.strategy}.`, + `Simulate roles: solver(model=${settings.model}), critic(model=${settings.criticModel}), synthesizer(model=${settings.synthModel}).`, + `Quorum contract: require >=${settings.minParticipatingAgents} participating and >=${settings.minAnsweringAgents} answering agents.`, + "If quorum cannot be satisfied, return exactly: Braintrust temporarily unavailable (...).", + "Return only one final answer to the user." + ].join("\n"); +} +var index_default = { + id: "braintrust-plugin", + name: "Braintrust", + description: "Multi-agent orchestration control plane", + configSchema: braintrustConfigSchema, + register(api) { + const settings = readSettings(api.pluginConfig); + let enabled = settings.enabled; + let lastQuorumEvaluation; + const statusLine = () => [ + `Braintrust: ${enabled ? "ON" : "OFF"}`, + `strategy=${settings.strategy}`, + `teamSize=${settings.teamSize}`, + `solver=${settings.model}`, + `critic=${settings.criticModel}`, + `researcher=${settings.researcherModel}`, + `synth=${settings.synthModel}`, + `timeout=${settings.timeoutSeconds}s`, + `minParticipating=${settings.minParticipatingAgents}`, + `minAnswering=${settings.minAnsweringAgents}`, + formatQuorumStatus(lastQuorumEvaluation) + ].join(" \xB7 "); + api.registerCommand({ + name: "braintrust", + description: "Control Braintrust mode: /braintrust on|off|status|unavailable", + acceptsArgs: true, + handler: async (ctx) => { + const arg = (ctx.args ?? "status").trim().toLowerCase(); + if (arg === "on") { + enabled = true; + return { text: `\u2705 ${statusLine()}` }; + } + if (arg === "off") { + enabled = false; + return { text: `\u{1F6D1} ${statusLine()}` }; + } + if (arg === "unavailable") { + const fallback = buildUnavailableNotice( + lastQuorumEvaluation ?? { + participating: 0, + answering: 0, + refused: 0, + failed: settings.teamSize, + meetsQuorum: false, + reason: `only 0/${settings.teamSize} agents participated` + } + ); + return { text: fallback }; + } + return { text: statusLine() }; + } + }); + api.on("before_prompt_build", async (payload) => { + if (!enabled) return; + const event = payload?.event ?? payload ?? {}; + const context = payload?.context ?? {}; + const prompt = extractPromptFromMessages(event?.messages); + const execute = resolveRuntimeExecutor(api, context); + if (!execute || !prompt) { + api.logger.info("[braintrust] runtime bridge unavailable in hook context, using policy-only prompt injection"); + lastQuorumEvaluation = { + participating: settings.teamSize, + answering: settings.teamSize, + refused: 0, + failed: 0, + meetsQuorum: true + }; + return { prependContext: buildFallbackPrepend(settings) }; + } + const bridge = await runRuntimeBridge( + { prompt, settings }, + ({ role, model, prompt: p, timeoutSeconds }) => execute({ role, model, prompt: p, timeoutSeconds }) + ); + lastQuorumEvaluation = evaluateQuorum(bridge.candidates, { + minParticipatingAgents: settings.minParticipatingAgents, + minAnsweringAgents: settings.minAnsweringAgents + }); + api.logger.info( + `[braintrust] runtime bridge complete unavailable=${bridge.unavailable} candidates=${bridge.candidates.length}` + ); + if (bridge.unavailable) { + return { + prependContext: [ + "BRAINTRUST MODE ACTIVE.", + "Quorum could not be satisfied by runtime-bridge execution.", + `Return exactly this notice: ${bridge.final}` + ].join("\n") + }; + } + return { + prependContext: [ + "BRAINTRUST MODE ACTIVE.", + "Use this runtime-bridge synthesis as your final answer.", + bridge.final + ].join("\n\n") + }; + }); + api.on("llm_input", async (payload) => { + if (!enabled) return; + const event = payload?.event ?? payload; + const context = payload?.context ?? {}; + const e = event; + api.logger.info( + `[braintrust] llm_input run=${e?.runId ?? "unknown"} session=${context?.sessionKey ?? "unknown"} model=${e?.model ?? "unknown"}` + ); + }); + api.on("llm_output", async (payload) => { + if (!enabled) return; + const event = payload?.event ?? payload; + const e = event; + api.logger.info( + `[braintrust] llm_output run=${e?.runId ?? "unknown"} model=${e?.model ?? "unknown"} responses=${Array.isArray(e?.assistantTexts) ? e.assistantTexts.length : 0}` + ); + }); + } +}; +export { + index_default as default +}; diff --git a/extensions/braintrust/openclaw.plugin.json b/extensions/braintrust/openclaw.plugin.json index 742fe69..61900b1 100644 --- a/extensions/braintrust/openclaw.plugin.json +++ b/extensions/braintrust/openclaw.plugin.json @@ -4,16 +4,60 @@ "type": "object", "additionalProperties": false, "properties": { - "enabled": { "type": "boolean", "default": false }, - "teamSize": { "type": "integer", "minimum": 1, "maximum": 4, "default": 3 }, - "strategy": { "type": "string", "enum": ["independent", "debate", "panel"], "default": "panel" }, - "model": { "type": "string", "default": "gemini-3-flash-preview" }, - "criticModel": { "type": "string", "default": "openai-codex/gpt-5.3-codex" }, - "researcherModel": { "type": "string", "default": "grok-4-1-fast-reasoning" }, - "synthModel": { "type": "string", "default": "gemini-3.1-pro-preview" }, - "timeoutSeconds": { "type": "integer", "minimum": 10, "maximum": 300, "default": 90 }, - "minParticipatingAgents": { "type": "integer", "minimum": 1, "maximum": 4, "default": 2 }, - "minAnsweringAgents": { "type": "integer", "minimum": 1, "maximum": 4, "default": 2 } + "enabled": { + "type": "boolean", + "default": false + }, + "teamSize": { + "type": "integer", + "minimum": 1, + "maximum": 4, + "default": 3 + }, + "strategy": { + "type": "string", + "enum": [ + "independent", + "debate", + "panel" + ], + "default": "panel" + }, + "model": { + "type": "string", + "default": "gemini-3-flash-preview" + }, + "criticModel": { + "type": "string", + "default": "openai-codex/gpt-5.3-codex" + }, + "researcherModel": { + "type": "string", + "default": "grok-4-1-fast-reasoning" + }, + "synthModel": { + "type": "string", + "default": "gemini-3.1-pro-preview" + }, + "timeoutSeconds": { + "type": "integer", + "minimum": 10, + "maximum": 300, + "default": 90 + }, + "minParticipatingAgents": { + "type": "integer", + "minimum": 1, + "maximum": 4, + "default": 2 + }, + "minAnsweringAgents": { + "type": "integer", + "minimum": 1, + "maximum": 4, + "default": 2 + } } - } + }, + "entry": "./index.js" } diff --git a/extensions/braintrust/package.json b/extensions/braintrust/package.json index 69c92ea..9a25f21 100644 --- a/extensions/braintrust/package.json +++ b/extensions/braintrust/package.json @@ -9,7 +9,7 @@ }, "openclaw": { "extensions": [ - "./index.ts" + "./index.js" ] }, "scripts": { From e45d89522ddbd1559bc3e8cc84b4e72635fe0651 Mon Sep 17 00:00:00 2001 From: Agent User Date: Sat, 28 Feb 2026 03:39:48 +0000 Subject: [PATCH 09/14] fix(braintrust): add local CLI command routing for control actions --- extensions/braintrust/README.md | 5 +- extensions/braintrust/index.js | 67 +++++++++++++++---------- extensions/braintrust/index.ts | 88 +++++++++++++++++++-------------- 3 files changed, 96 insertions(+), 64 deletions(-) diff --git a/extensions/braintrust/README.md b/extensions/braintrust/README.md index df2ef06..60bb3bb 100644 --- a/extensions/braintrust/README.md +++ b/extensions/braintrust/README.md @@ -3,7 +3,8 @@ Feature-flagged plugin path for multi-agent orchestration in OpenClaw. ## Current behavior (implemented) -- `/braintrust on|off|status|unavailable` +- Chat/auto-reply surfaces: `/braintrust on|off|status|unavailable` (via `registerCommand`) +- Local CLI surface: `openclaw braintrust [on|off|status|unavailable]` (via `registerCli`) - Configurable team size / strategy / model roles - Quorum contract configuration (`minParticipatingAgents`, `minAnsweringAgents`) - Prompt injection with explicit quorum policy + unavailable contract @@ -14,6 +15,8 @@ Feature-flagged plugin path for multi-agent orchestration in OpenClaw. This plugin **does not yet perform true parallel fan-out/fan-in orchestration** by itself. It sets control policy and prompt/lifecycle behavior; runtime fan-out wiring remains a core integration task. +Also note: `openclaw agent --local --message '/braintrust ...'` does not route through plugin slash-command dispatch. Use `openclaw braintrust ...` for local CLI control. + ## Quorum contract - Minimum participating agents: default `2` - Minimum answering agents: default `2` diff --git a/extensions/braintrust/index.js b/extensions/braintrust/index.js index d33ab0e..5db11ec 100644 --- a/extensions/braintrust/index.js +++ b/extensions/braintrust/index.js @@ -21,6 +21,11 @@ function formatQuorumStatus(e) { if (!e) return `quorum: ${DEFAULT_QUORUM.minParticipatingAgents}/${DEFAULT_QUORUM.minAnsweringAgents}`; return `participating=${e.participating} answering=${e.answering} refused=${e.refused} failed=${e.failed}`; } +function parseBraintrustAction(raw) { + const arg = (raw ?? "status").trim().toLowerCase(); + if (arg === "on" || arg === "off" || arg === "unavailable") return arg; + return "status"; +} function extractPromptFromMessages(messages) { if (!Array.isArray(messages)) return ""; for (let i = messages.length - 1; i >= 0; i -= 1) { @@ -122,36 +127,48 @@ var index_default = { `minAnswering=${settings.minAnsweringAgents}`, formatQuorumStatus(lastQuorumEvaluation) ].join(" \xB7 "); + const renderUnavailable = () => buildUnavailableNotice( + lastQuorumEvaluation ?? { + participating: 0, + answering: 0, + refused: 0, + failed: settings.teamSize, + meetsQuorum: false, + reason: `only 0/${settings.teamSize} agents participated` + } + ); + const executeBraintrustAction = (action) => { + if (action === "on") { + enabled = true; + return `\u2705 ${statusLine()}`; + } + if (action === "off") { + enabled = false; + return `\u{1F6D1} ${statusLine()}`; + } + if (action === "unavailable") { + return renderUnavailable(); + } + return statusLine(); + }; api.registerCommand({ name: "braintrust", description: "Control Braintrust mode: /braintrust on|off|status|unavailable", acceptsArgs: true, - handler: async (ctx) => { - const arg = (ctx.args ?? "status").trim().toLowerCase(); - if (arg === "on") { - enabled = true; - return { text: `\u2705 ${statusLine()}` }; - } - if (arg === "off") { - enabled = false; - return { text: `\u{1F6D1} ${statusLine()}` }; - } - if (arg === "unavailable") { - const fallback = buildUnavailableNotice( - lastQuorumEvaluation ?? { - participating: 0, - answering: 0, - refused: 0, - failed: settings.teamSize, - meetsQuorum: false, - reason: `only 0/${settings.teamSize} agents participated` - } - ); - return { text: fallback }; - } - return { text: statusLine() }; - } + handler: async (ctx) => ({ text: executeBraintrustAction(parseBraintrustAction(ctx.args)) }) }); + api.registerCli( + ({ program }) => { + program + .command("braintrust") + .description("Braintrust controls for local CLI surfaces") + .argument("[action]", "on|off|status|unavailable", "status") + .action((action) => { + console.log(executeBraintrustAction(parseBraintrustAction(action))); + }); + }, + { commands: ["braintrust"] } + ); api.on("before_prompt_build", async (payload) => { if (!enabled) return; const event = payload?.event ?? payload ?? {}; diff --git a/extensions/braintrust/index.ts b/extensions/braintrust/index.ts index 6c440c0..4ff5472 100644 --- a/extensions/braintrust/index.ts +++ b/extensions/braintrust/index.ts @@ -9,7 +9,6 @@ import { buildUnavailableNotice, DEFAULT_QUORUM, evaluateQuorum, type QuorumEval import { runRuntimeBridge, type CandidateRunnerInput, type CandidateRunnerOutput } from "./src/runtime-bridge.js"; import { readSettings } from "./src/settings.js"; - const braintrustConfigSchema = { type: "object", additionalProperties: false, @@ -35,12 +34,19 @@ type RuntimeExecuteInput = { }; type RuntimeExecuteFn = (input: RuntimeExecuteInput) => Promise; +type BraintrustAction = "on" | "off" | "status" | "unavailable"; function formatQuorumStatus(e?: QuorumEvaluation): string { if (!e) return `quorum: ${DEFAULT_QUORUM.minParticipatingAgents}/${DEFAULT_QUORUM.minAnsweringAgents}`; return `participating=${e.participating} answering=${e.answering} refused=${e.refused} failed=${e.failed}`; } +function parseBraintrustAction(raw?: string): BraintrustAction { + const arg = (raw ?? "status").trim().toLowerCase(); + if (arg === "on" || arg === "off" || arg === "unavailable") return arg; + return "status"; +} + function extractPromptFromMessages(messages: unknown): string { if (!Array.isArray(messages)) return ""; for (let i = messages.length - 1; i >= 0; i -= 1) { @@ -88,11 +94,7 @@ function readTextOutput(out: unknown): string { } function resolveRuntimeExecutor(api: OpenClawPluginApi, runtimeContext: unknown): RuntimeExecuteFn | undefined { - const source = [ - runtimeContext as Record | undefined, - api as unknown as Record, - ]; - + const source = [runtimeContext as Record | undefined, api as unknown as Record]; const names = ["runModel", "invokeModel", "runLlm", "invokeLlm", "complete"]; for (const obj of source) { @@ -114,9 +116,7 @@ function resolveRuntimeExecutor(api: OpenClawPluginApi, runtimeContext: unknown) ], }); const text = readTextOutput(out).trim(); - if (!text) { - throw new Error("empty output"); - } + if (!text) throw new Error("empty output"); return { text, refusal: /\b(cannot comply|i can't|i cannot|refuse|not able)\b/i.test(text), @@ -165,37 +165,51 @@ export default { formatQuorumStatus(lastQuorumEvaluation), ].join(" ยท "); + const renderUnavailable = () => + buildUnavailableNotice( + lastQuorumEvaluation ?? { + participating: 0, + answering: 0, + refused: 0, + failed: settings.teamSize, + meetsQuorum: false, + reason: `only 0/${settings.teamSize} agents participated`, + }, + ); + + const executeBraintrustAction = (action: BraintrustAction): string => { + if (action === "on") { + enabled = true; + return `โœ… ${statusLine()}`; + } + if (action === "off") { + enabled = false; + return `๐Ÿ›‘ ${statusLine()}`; + } + if (action === "unavailable") return renderUnavailable(); + return statusLine(); + }; + api.registerCommand({ name: "braintrust", description: "Control Braintrust mode: /braintrust on|off|status|unavailable", acceptsArgs: true, - handler: async (ctx) => { - const arg = (ctx.args ?? "status").trim().toLowerCase(); - if (arg === "on") { - enabled = true; - return { text: `โœ… ${statusLine()}` }; - } - if (arg === "off") { - enabled = false; - return { text: `๐Ÿ›‘ ${statusLine()}` }; - } - if (arg === "unavailable") { - const fallback = buildUnavailableNotice( - lastQuorumEvaluation ?? { - participating: 0, - answering: 0, - refused: 0, - failed: settings.teamSize, - meetsQuorum: false, - reason: `only 0/${settings.teamSize} agents participated`, - }, - ); - return { text: fallback }; - } - return { text: statusLine() }; - }, + handler: async (ctx) => ({ text: executeBraintrustAction(parseBraintrustAction(ctx.args)) }), }); + api.registerCli( + ({ program }) => { + program + .command("braintrust") + .description("Braintrust controls for local CLI surfaces") + .argument("[action]", "on|off|status|unavailable", "status") + .action((action: string) => { + console.log(executeBraintrustAction(parseBraintrustAction(action))); + }); + }, + { commands: ["braintrust"] }, + ); + api.on("before_prompt_build", async (payload) => { if (!enabled) return; const event = ((payload as any)?.event ?? payload ?? {}) as PluginHookBeforePromptBuildEvent; @@ -251,18 +265,16 @@ export default { if (!enabled) return; const event = ((payload as any)?.event ?? payload) as PluginHookLlmInputEvent; const context = ((payload as any)?.context ?? {}) as any; - const e = event; api.logger.info( - `[braintrust] llm_input run=${(e as any)?.runId ?? "unknown"} session=${context?.sessionKey ?? "unknown"} model=${(e as any)?.model ?? "unknown"}`, + `[braintrust] llm_input run=${(event as any)?.runId ?? "unknown"} session=${context?.sessionKey ?? "unknown"} model=${(event as any)?.model ?? "unknown"}`, ); }); api.on("llm_output", async (payload) => { if (!enabled) return; const event = ((payload as any)?.event ?? payload) as PluginHookLlmOutputEvent; - const e = event; api.logger.info( - `[braintrust] llm_output run=${(e as any)?.runId ?? "unknown"} model=${(e as any)?.model ?? "unknown"} responses=${Array.isArray((e as any)?.assistantTexts) ? (e as any).assistantTexts.length : 0}`, + `[braintrust] llm_output run=${(event as any)?.runId ?? "unknown"} model=${(event as any)?.model ?? "unknown"} responses=${Array.isArray((event as any)?.assistantTexts) ? (event as any).assistantTexts.length : 0}`, ); }); }, From bf7d2990927345307e6cabe52e270d263a1fe19e Mon Sep 17 00:00:00 2001 From: Agent User Date: Fri, 6 Mar 2026 17:00:18 +0000 Subject: [PATCH 10/14] Implement 4-agent Braintrust system with structured JSON for three parallel agents + synthesizer - Set default teamSize=4 - Add synthesizer role (agent-4) that outputs structured JSON {final, reasoning, sources} - Update roleFor/modelFor/synth detection to use agent-4 for synthesis - Enhance synthesizeDeterministic to parse JSON from synthesizer when present - Update docs and plugin config - Use subagent runtime parallelism via existing Promise.all fan-out Closes braintrust-json-agents task. --- extensions/braintrust/README.md | 2 +- extensions/braintrust/openclaw.plugin.json | 2 +- extensions/braintrust/src/runtime-bridge.ts | 9 ++++++--- extensions/braintrust/src/settings.ts | 2 +- extensions/braintrust/src/synth.ts | 18 ++++++++++++++++++ 5 files changed, 27 insertions(+), 6 deletions(-) diff --git a/extensions/braintrust/README.md b/extensions/braintrust/README.md index 60bb3bb..f798156 100644 --- a/extensions/braintrust/README.md +++ b/extensions/braintrust/README.md @@ -33,8 +33,8 @@ pnpm vitest run extensions/braintrust/src/policy.test.ts extensions/braintrust/s ## Default model routing - solver (`model`): `gemini-3-flash-preview` - critic (`criticModel`): `openai-codex/gpt-5.3-codex` -- synthesizer (`synthModel`): `gemini-3.1-pro-preview` - researcher (`researcherModel`): `grok-4-1-fast-reasoning` +- synthesizer (`synthModel`): `gemini-3.1-pro-preview` (now agent-4, outputs structured JSON with `{"final": "...", "reasoning": "...", "sources": [...]}`) Note: plugin id is `braintrust-plugin`; command remains `/braintrust`. diff --git a/extensions/braintrust/openclaw.plugin.json b/extensions/braintrust/openclaw.plugin.json index 61900b1..7116130 100644 --- a/extensions/braintrust/openclaw.plugin.json +++ b/extensions/braintrust/openclaw.plugin.json @@ -12,7 +12,7 @@ "type": "integer", "minimum": 1, "maximum": 4, - "default": 3 + "default": 4 }, "strategy": { "type": "string", diff --git a/extensions/braintrust/src/runtime-bridge.ts b/extensions/braintrust/src/runtime-bridge.ts index 20a6c1d..0d7be15 100644 --- a/extensions/braintrust/src/runtime-bridge.ts +++ b/extensions/braintrust/src/runtime-bridge.ts @@ -3,7 +3,7 @@ import type { BraintrustSettings } from "./settings.js"; import { synthesizeDeterministic, type SynthesisOutput } from "./synth.js"; export type CandidateRunnerInput = { - role: "solver" | "critic" | "researcher"; + role: "solver" | "critic" | "researcher" | "synthesizer"; model: string; prompt: string; timeoutSeconds: number; @@ -33,11 +33,12 @@ export type RuntimeBridgeResult = { function roleFor(index: number): CandidateRunnerInput["role"] { if (index === 1) return "critic"; if (index === 2) return "researcher"; + if (index === 3) return "synthesizer"; return "solver"; } function modelFor(index: number, settings: BraintrustSettings): string { - if (index === settings.teamSize - 1) return settings.synthModel; + if (index === 3) return settings.synthModel; if (index === 1) return settings.criticModel; if (index === 2) return settings.researcherModel; return settings.model; @@ -92,7 +93,9 @@ export async function runRuntimeBridge( runCandidate({ role: roleFor(i), model, - prompt: input.prompt, + prompt: i === 3 + ? `${input.prompt}\n\nRespond ONLY with valid JSON in this exact format (no extra text, no markdown):\n{\n "final": "the single best synthesized answer",\n "reasoning": "brief explanation of how you combined the inputs",\n "sources": ["list of agent ids used"]\n}` + : input.prompt, timeoutSeconds: input.settings.timeoutSeconds, }), input.settings.timeoutSeconds, diff --git a/extensions/braintrust/src/settings.ts b/extensions/braintrust/src/settings.ts index 197b256..84ddee9 100644 --- a/extensions/braintrust/src/settings.ts +++ b/extensions/braintrust/src/settings.ts @@ -17,7 +17,7 @@ export type BraintrustSettings = { export const DEFAULT_SETTINGS: BraintrustSettings = { enabled: false, - teamSize: 3, + teamSize: 4, strategy: "panel", model: "gemini-3-flash-preview", criticModel: "openai-codex/gpt-5.3-codex", diff --git a/extensions/braintrust/src/synth.ts b/extensions/braintrust/src/synth.ts index 4c46264..4e25b0e 100644 --- a/extensions/braintrust/src/synth.ts +++ b/extensions/braintrust/src/synth.ts @@ -22,6 +22,24 @@ export function synthesizeDeterministic(input: SynthesisInput): SynthesisOutput final: "Braintrust temporarily unavailable (no usable candidate output).", }; } + + // New structured JSON mode for 4-agent system + const synthAgent = input.candidates.find((c) => c.id === "agent-4"); + if (synthAgent?.status === "ok" && synthAgent.text) { + try { + const parsed = JSON.parse(synthAgent.text.trim()); + if (parsed.final && typeof parsed.final === "string") { + return { + final: parsed.final, + winnerId: "agent-4", + }; + } + } catch (e) { + // fallback if not valid JSON + } + } + + // fallback to shortest successful ok.sort((a, b) => { const la = (a.text ?? "").length; const lb = (b.text ?? "").length; From 8ac13eddc6f68205c071fecd370b62793e31c06c Mon Sep 17 00:00:00 2001 From: Victor al Ghul Date: Sat, 7 Mar 2026 22:44:12 +0000 Subject: [PATCH 11/14] feat(braintrust): harden runtime bridge foundation --- extensions/braintrust/README.md | 15 +- extensions/braintrust/index.js | 113 ++++++++++----- extensions/braintrust/index.ts | 130 +++++++++++++----- extensions/braintrust/src/index.test.ts | 39 +++++- .../src/runtime-bridge.integration.test.ts | 6 +- .../braintrust/src/runtime-bridge.test.ts | 56 ++++---- extensions/braintrust/src/runtime-bridge.ts | 7 +- extensions/braintrust/src/settings.ts | 4 + 8 files changed, 263 insertions(+), 107 deletions(-) diff --git a/extensions/braintrust/README.md b/extensions/braintrust/README.md index f798156..4f8704b 100644 --- a/extensions/braintrust/README.md +++ b/extensions/braintrust/README.md @@ -3,17 +3,18 @@ Feature-flagged plugin path for multi-agent orchestration in OpenClaw. ## Current behavior (implemented) -- Chat/auto-reply surfaces: `/braintrust on|off|status|unavailable` (via `registerCommand`) -- Local CLI surface: `openclaw braintrust [on|off|status|unavailable]` (via `registerCli`) +- Chat/auto-reply surfaces: `/braintrust on|off|status|unavailable|solver |critic |researcher ` (via `registerCommand`) +- Local CLI surface: `openclaw braintrust [on|off|status|unavailable|solver |critic |researcher ]` (via `registerCli` when available) - Configurable team size / strategy / model roles +- Runtime bridge calls real model executions when a runtime executor is available in hook context - Quorum contract configuration (`minParticipatingAgents`, `minAnsweringAgents`) -- Prompt injection with explicit quorum policy + unavailable contract +- Explicit unavailable behavior when runtime fan-out is unavailable; no fake panel simulation fallback - Logging hooks for `llm_input` and `llm_output` - Deterministic quorum policy helpers + unit tests ## Important limitation (still pending) -This plugin **does not yet perform true parallel fan-out/fan-in orchestration** by itself. -It sets control policy and prompt/lifecycle behavior; runtime fan-out wiring remains a core integration task. +This plugin now routes Braintrust candidates through the runtime executor when one is exposed in hook context, but it **still does not own true orchestrator-managed subagent lifecycle** by itself. +It relies on the hosting runtime for actual worker/model execution; deeper worker spawning, cross-agent state, and richer fan-in remain core integration tasks. Also note: `openclaw agent --local --message '/braintrust ...'` does not route through plugin slash-command dispatch. Use `openclaw braintrust ...` for local CLI control. @@ -34,7 +35,9 @@ pnpm vitest run extensions/braintrust/src/policy.test.ts extensions/braintrust/s - solver (`model`): `gemini-3-flash-preview` - critic (`criticModel`): `openai-codex/gpt-5.3-codex` - researcher (`researcherModel`): `grok-4-1-fast-reasoning` -- synthesizer (`synthModel`): `gemini-3.1-pro-preview` (now agent-4, outputs structured JSON with `{"final": "...", "reasoning": "...", "sources": [...]}`) +- synthesizer: **active chat model** (agent-4, expected to output structured JSON like `{"final": "...", "reasoning": "...", "sources": [...]}`) + +`syntModel` / `synthModel` config is now effectively legacy compatibility state; synthesis should follow the active chat model instead of a dedicated synth override. Note: plugin id is `braintrust-plugin`; command remains `/braintrust`. diff --git a/extensions/braintrust/index.js b/extensions/braintrust/index.js index 5db11ec..9680ae0 100644 --- a/extensions/braintrust/index.js +++ b/extensions/braintrust/index.js @@ -22,8 +22,14 @@ function formatQuorumStatus(e) { return `participating=${e.participating} answering=${e.answering} refused=${e.refused} failed=${e.failed}`; } function parseBraintrustAction(raw) { - const arg = (raw ?? "status").trim().toLowerCase(); - if (arg === "on" || arg === "off" || arg === "unavailable") return arg; + const arg = (raw ?? "status").trim(); + const lowered = arg.toLowerCase(); + if (lowered === "on" || lowered === "off" || lowered === "status" || lowered === "unavailable") return lowered; + for (const role of ["solver", "critic", "researcher"]) { + if (!lowered.startsWith(`${role} `)) continue; + const model = arg.slice(role.length).trim(); + if (model) return `${role} ${model}`; + } return "status"; } function extractPromptFromMessages(messages) { @@ -95,14 +101,27 @@ function resolveRuntimeExecutor(api, runtimeContext) { } return void 0; } -function buildFallbackPrepend(settings) { +function resolveActiveChatModel(event, context) { + const sources = [event, context]; + for (const source of sources) { + const model = source?.model; + if (typeof model === "string" && model.trim()) return model.trim(); + } + return void 0; +} +function buildUnavailablePrepend(settings, reason) { + const notice = buildUnavailableNotice({ + participating: 0, + answering: 0, + refused: 0, + failed: settings.teamSize, + meetsQuorum: false, + reason + }); return [ "BRAINTRUST MODE ACTIVE.", - `Use a ${settings.teamSize}-agent internal panel with strategy=${settings.strategy}.`, - `Simulate roles: solver(model=${settings.model}), critic(model=${settings.criticModel}), synthesizer(model=${settings.synthModel}).`, - `Quorum contract: require >=${settings.minParticipatingAgents} participating and >=${settings.minAnsweringAgents} answering agents.`, - "If quorum cannot be satisfied, return exactly: Braintrust temporarily unavailable (...).", - "Return only one final answer to the user." + "Runtime bridge unavailable; do not simulate panelists or fabricate quorum.", + `Return exactly this notice: ${notice}` ].join("\n"); } var index_default = { @@ -113,15 +132,18 @@ var index_default = { register(api) { const settings = readSettings(api.pluginConfig); let enabled = settings.enabled; + let solverModel = settings.model; + let criticModel = settings.criticModel; + let researcherModel = settings.researcherModel; let lastQuorumEvaluation; const statusLine = () => [ `Braintrust: ${enabled ? "ON" : "OFF"}`, `strategy=${settings.strategy}`, `teamSize=${settings.teamSize}`, - `solver=${settings.model}`, - `critic=${settings.criticModel}`, - `researcher=${settings.researcherModel}`, - `synth=${settings.synthModel}`, + `solver=${solverModel}`, + `critic=${criticModel}`, + `researcher=${researcherModel}`, + "synth=active-chat-model", `timeout=${settings.timeoutSeconds}s`, `minParticipating=${settings.minParticipatingAgents}`, `minAnswering=${settings.minAnsweringAgents}`, @@ -149,45 +171,72 @@ var index_default = { if (action === "unavailable") { return renderUnavailable(); } + if (action.startsWith("solver ")) { + solverModel = action.slice("solver ".length).trim(); + return `\u2705 solver=${solverModel} \u00b7 ${statusLine()}`; + } + if (action.startsWith("critic ")) { + criticModel = action.slice("critic ".length).trim(); + return `\u2705 critic=${criticModel} \u00b7 ${statusLine()}`; + } + if (action.startsWith("researcher ")) { + researcherModel = action.slice("researcher ".length).trim(); + return `\u2705 researcher=${researcherModel} \u00b7 ${statusLine()}`; + } return statusLine(); }; api.registerCommand({ name: "braintrust", - description: "Control Braintrust mode: /braintrust on|off|status|unavailable", + description: "Control Braintrust mode: /braintrust on|off|status|unavailable|solver |critic |researcher ", acceptsArgs: true, handler: async (ctx) => ({ text: executeBraintrustAction(parseBraintrustAction(ctx.args)) }) }); - api.registerCli( - ({ program }) => { - program - .command("braintrust") - .description("Braintrust controls for local CLI surfaces") - .argument("[action]", "on|off|status|unavailable", "status") - .action((action) => { - console.log(executeBraintrustAction(parseBraintrustAction(action))); - }); - }, - { commands: ["braintrust"] } - ); + if (typeof api.registerCli === "function") { + api.registerCli( + ({ program }) => { + program + .command("braintrust") + .description("Braintrust controls for local CLI surfaces") + .argument("[action...]", "on|off|status|unavailable|solver |critic |researcher ") + .action((action) => { + console.log(executeBraintrustAction(parseBraintrustAction(action.join(" ")))); + }); + }, + { commands: ["braintrust"] } + ); + } api.on("before_prompt_build", async (payload) => { if (!enabled) return; const event = payload?.event ?? payload ?? {}; const context = payload?.context ?? {}; const prompt = extractPromptFromMessages(event?.messages); const execute = resolveRuntimeExecutor(api, context); + const activeChatModel = resolveActiveChatModel(event, context); + const runtimeSettings = { + ...settings, + model: solverModel, + criticModel, + researcherModel + }; if (!execute || !prompt) { - api.logger.info("[braintrust] runtime bridge unavailable in hook context, using policy-only prompt injection"); + api.logger.info("[braintrust] runtime bridge unavailable in hook context; refusing fake panel simulation"); lastQuorumEvaluation = { - participating: settings.teamSize, - answering: settings.teamSize, + participating: 0, + answering: 0, refused: 0, - failed: 0, - meetsQuorum: true + failed: runtimeSettings.teamSize, + meetsQuorum: false, + reason: !execute ? "runtime executor unavailable" : "missing user prompt" + }; + return { + prependContext: buildUnavailablePrepend( + runtimeSettings, + !execute ? "runtime executor unavailable" : "missing user prompt" + ) }; - return { prependContext: buildFallbackPrepend(settings) }; } const bridge = await runRuntimeBridge( - { prompt, settings }, + { prompt, settings: runtimeSettings, activeChatModel }, ({ role, model, prompt: p, timeoutSeconds }) => execute({ role, model, prompt: p, timeoutSeconds }) ); lastQuorumEvaluation = evaluateQuorum(bridge.candidates, { diff --git a/extensions/braintrust/index.ts b/extensions/braintrust/index.ts index 4ff5472..d3b4b28 100644 --- a/extensions/braintrust/index.ts +++ b/extensions/braintrust/index.ts @@ -34,7 +34,14 @@ type RuntimeExecuteInput = { }; type RuntimeExecuteFn = (input: RuntimeExecuteInput) => Promise; -type BraintrustAction = "on" | "off" | "status" | "unavailable"; +type BraintrustAction = + | "on" + | "off" + | "status" + | "unavailable" + | `solver ${string}` + | `critic ${string}` + | `researcher ${string}`; function formatQuorumStatus(e?: QuorumEvaluation): string { if (!e) return `quorum: ${DEFAULT_QUORUM.minParticipatingAgents}/${DEFAULT_QUORUM.minAnsweringAgents}`; @@ -42,8 +49,16 @@ function formatQuorumStatus(e?: QuorumEvaluation): string { } function parseBraintrustAction(raw?: string): BraintrustAction { - const arg = (raw ?? "status").trim().toLowerCase(); - if (arg === "on" || arg === "off" || arg === "unavailable") return arg; + const arg = (raw ?? "status").trim(); + const lowered = arg.toLowerCase(); + if (lowered === "on" || lowered === "off" || lowered === "status" || lowered === "unavailable") return lowered; + + for (const role of ["solver", "critic", "researcher"] as const) { + if (!lowered.startsWith(`${role} `)) continue; + const model = arg.slice(role.length).trim(); + if (model) return `${role} ${model}`; + } + return "status"; } @@ -128,14 +143,29 @@ function resolveRuntimeExecutor(api: OpenClawPluginApi, runtimeContext: unknown) return undefined; } -function buildFallbackPrepend(settings: ReturnType): string { +function resolveActiveChatModel(event: unknown, context: unknown): string | undefined { + const sources = [event as Record | undefined, context as Record | undefined]; + for (const source of sources) { + const model = source?.model; + if (typeof model === "string" && model.trim()) return model.trim(); + } + return undefined; +} + +function buildUnavailablePrepend(settings: ReturnType, reason: string): string { + const notice = buildUnavailableNotice({ + participating: 0, + answering: 0, + refused: 0, + failed: settings.teamSize, + meetsQuorum: false, + reason, + }); + return [ "BRAINTRUST MODE ACTIVE.", - `Use a ${settings.teamSize}-agent internal panel with strategy=${settings.strategy}.`, - `Simulate roles: solver(model=${settings.model}), critic(model=${settings.criticModel}), synthesizer(model=${settings.synthModel}).`, - `Quorum contract: require >=${settings.minParticipatingAgents} participating and >=${settings.minAnsweringAgents} answering agents.`, - "If quorum cannot be satisfied, return exactly: Braintrust temporarily unavailable (...).", - "Return only one final answer to the user.", + "Runtime bridge unavailable; do not simulate panelists or fabricate quorum.", + `Return exactly this notice: ${notice}`, ].join("\n"); } @@ -148,6 +178,9 @@ export default { register(api: OpenClawPluginApi) { const settings = readSettings(api.pluginConfig); let enabled = settings.enabled; + let solverModel = settings.model; + let criticModel = settings.criticModel; + let researcherModel = settings.researcherModel; let lastQuorumEvaluation: QuorumEvaluation | undefined; const statusLine = () => @@ -155,10 +188,10 @@ export default { `Braintrust: ${enabled ? "ON" : "OFF"}`, `strategy=${settings.strategy}`, `teamSize=${settings.teamSize}`, - `solver=${settings.model}`, - `critic=${settings.criticModel}`, - `researcher=${settings.researcherModel}`, - `synth=${settings.synthModel}`, + `solver=${solverModel}`, + `critic=${criticModel}`, + `researcher=${researcherModel}`, + "synth=active-chat-model", `timeout=${settings.timeoutSeconds}s`, `minParticipating=${settings.minParticipatingAgents}`, `minAnswering=${settings.minAnsweringAgents}`, @@ -187,28 +220,42 @@ export default { return `๐Ÿ›‘ ${statusLine()}`; } if (action === "unavailable") return renderUnavailable(); + if (action.startsWith("solver ")) { + solverModel = action.slice("solver ".length).trim(); + return `โœ… solver=${solverModel} ยท ${statusLine()}`; + } + if (action.startsWith("critic ")) { + criticModel = action.slice("critic ".length).trim(); + return `โœ… critic=${criticModel} ยท ${statusLine()}`; + } + if (action.startsWith("researcher ")) { + researcherModel = action.slice("researcher ".length).trim(); + return `โœ… researcher=${researcherModel} ยท ${statusLine()}`; + } return statusLine(); }; api.registerCommand({ name: "braintrust", - description: "Control Braintrust mode: /braintrust on|off|status|unavailable", + description: "Control Braintrust mode: /braintrust on|off|status|unavailable|solver |critic |researcher ", acceptsArgs: true, handler: async (ctx) => ({ text: executeBraintrustAction(parseBraintrustAction(ctx.args)) }), }); - api.registerCli( - ({ program }) => { - program - .command("braintrust") - .description("Braintrust controls for local CLI surfaces") - .argument("[action]", "on|off|status|unavailable", "status") - .action((action: string) => { - console.log(executeBraintrustAction(parseBraintrustAction(action))); - }); - }, - { commands: ["braintrust"] }, - ); + if (typeof api.registerCli === "function") { + api.registerCli( + ({ program }) => { + program + .command("braintrust") + .description("Braintrust controls for local CLI surfaces") + .argument("[action...]", "on|off|status|unavailable|solver |critic |researcher ") + .action((action: string[]) => { + console.log(executeBraintrustAction(parseBraintrustAction(action.join(" ")))); + }); + }, + { commands: ["braintrust"] }, + ); + } api.on("before_prompt_build", async (payload) => { if (!enabled) return; @@ -217,20 +264,35 @@ export default { const prompt = extractPromptFromMessages((event as any)?.messages); const execute = resolveRuntimeExecutor(api, context); + const activeChatModel = resolveActiveChatModel(event, context); + const runtimeSettings = { + ...settings, + model: solverModel, + criticModel, + researcherModel, + }; + if (!execute || !prompt) { - api.logger.info("[braintrust] runtime bridge unavailable in hook context, using policy-only prompt injection"); + api.logger.info("[braintrust] runtime bridge unavailable in hook context; refusing fake panel simulation"); lastQuorumEvaluation = { - participating: settings.teamSize, - answering: settings.teamSize, + participating: 0, + answering: 0, refused: 0, - failed: 0, - meetsQuorum: true, + failed: runtimeSettings.teamSize, + meetsQuorum: false, + reason: !execute ? "runtime executor unavailable" : "missing user prompt", + }; + return { + prependContext: buildUnavailablePrepend( + runtimeSettings, + !execute ? "runtime executor unavailable" : "missing user prompt", + ), }; - return { prependContext: buildFallbackPrepend(settings) }; } - const bridge = await runRuntimeBridge({ prompt, settings }, ({ role, model, prompt: p, timeoutSeconds }) => - execute({ role, model, prompt: p, timeoutSeconds }), + const bridge = await runRuntimeBridge( + { prompt, settings: runtimeSettings, activeChatModel }, + ({ role, model, prompt: p, timeoutSeconds }) => execute({ role, model, prompt: p, timeoutSeconds }), ); lastQuorumEvaluation = evaluateQuorum(bridge.candidates, { diff --git a/extensions/braintrust/src/index.test.ts b/extensions/braintrust/src/index.test.ts index 779e342..8867892 100644 --- a/extensions/braintrust/src/index.test.ts +++ b/extensions/braintrust/src/index.test.ts @@ -10,6 +10,7 @@ type FakeApi = { pluginConfig: Record; logger: { info: (message: string) => void }; registerCommand: (command: unknown) => void; + registerCli?: (register: (ctx: { program: { command: (name: string) => { description: (text: string) => { argument: (...args: unknown[]) => { action: (handler: (action: string[]) => void) => void } } } } }) => void, meta?: unknown) => void; on: (name: string, handler: HookHandler) => void; }; @@ -23,6 +24,7 @@ function setupApi(pluginConfig: Record = {}) { registerCommand: (command) => { commands.push(command); }, + registerCli: vi.fn(), on: (name, handler) => { hooks.set(name, handler); }, @@ -74,7 +76,42 @@ describe("braintrust plugin runtime integration", () => { }); expect(out?.prependContext).toContain("BRAINTRUST MODE ACTIVE."); - expect(out?.prependContext).toContain("Simulate roles:"); + expect(out?.prependContext).toContain("do not simulate panelists"); + expect(out?.prependContext).toContain("temporarily unavailable"); + }); + + it("supports runtime model override commands for solver/critic/researcher and uses active chat model for synth", async () => { + const { default: plugin } = await import("../index.js"); + const calls: Array<{ model: string; role: string }> = []; + const { api, hooks, commands } = setupApi({ enabled: true, teamSize: 4 }); + + plugin.register(api as never); + + const command = commands[0] as { handler: (ctx: { args?: string }) => Promise<{ text: string }> }; + await command.handler({ args: "solver openai/solver-x" }); + await command.handler({ args: "critic anthropic/critic-y" }); + await command.handler({ args: "researcher xai/researcher-z" }); + + const beforePromptBuild = hooks.get("before_prompt_build"); + const out = await beforePromptBuild!({ + event: { model: "openai/chat-main", messages: [{ role: "user", content: "What is 2+2?" }] }, + context: { + runModel: async ({ model, role }: { model: string; role: string }) => { + calls.push({ model, role }); + return role === "synthesizer" + ? { text: JSON.stringify({ final: "4", reasoning: "matched consensus", sources: ["agent-1"] }) } + : { text: "4" }; + }, + }, + }); + + expect(calls).toEqual([ + { model: "openai/solver-x", role: "solver" }, + { model: "anthropic/critic-y", role: "critic" }, + { model: "xai/researcher-z", role: "researcher" }, + { model: "openai/chat-main", role: "synthesizer" }, + ]); + expect(out?.prependContext).toContain("4"); }); it("tolerates missing or malformed before_prompt_build payloads", async () => { diff --git a/extensions/braintrust/src/runtime-bridge.integration.test.ts b/extensions/braintrust/src/runtime-bridge.integration.test.ts index e193ff9..5188872 100644 --- a/extensions/braintrust/src/runtime-bridge.integration.test.ts +++ b/extensions/braintrust/src/runtime-bridge.integration.test.ts @@ -15,7 +15,7 @@ describe("runtime bridge integration flow", () => { const started = Date.now(); const result = await runRuntimeBridge( - { prompt: "Build a rollout plan", settings }, + { prompt: "Build a rollout plan", settings, activeChatModel: "openai/gpt-active-chat" }, async ({ role, model }) => { if (role === "critic") { // Should be auto-classified as refusal by text even without refusal flag. @@ -26,8 +26,8 @@ describe("runtime bridge integration flow", () => { await new Promise((resolve) => setTimeout(resolve, 1500)); return { text: "late answer" }; } - // 4th worker uses synthModel but still acts as candidate in bridge fan-out. - if (model === settings.synthModel) return { text: "brief" }; + // 4th worker uses the active chat model for synthesis. + if (model === "openai/gpt-active-chat") return { text: "brief" }; return { text: "solid plan" }; }, ); diff --git a/extensions/braintrust/src/runtime-bridge.test.ts b/extensions/braintrust/src/runtime-bridge.test.ts index 36bce91..9ef2469 100644 --- a/extensions/braintrust/src/runtime-bridge.test.ts +++ b/extensions/braintrust/src/runtime-bridge.test.ts @@ -80,31 +80,31 @@ describe("runRuntimeBridge", () => { }); - it("uses four distinct models when teamSize=4", async () => { - const settings = { - ...DEFAULT_SETTINGS, - enabled: true, - teamSize: 4, - model: "gemini-3-flash-preview", - criticModel: "openai-codex/gpt-5.3-codex", - researcherModel: "grok-4-1-fast-reasoning", - synthModel: "gemini-3.1-pro-preview", - minParticipatingAgents: 2, - minAnsweringAgents: 2, - }; - const seen = []; - const result = await runRuntimeBridge( - { prompt: "hello", settings }, - async ({ model }) => { - seen.push(model); - return { text: `ok-${model}`, refusal: false }; - }, - ); - expect(result.unavailable).toBe(false); - expect(new Set(seen)).toEqual(new Set([ - "gemini-3-flash-preview", - "openai-codex/gpt-5.3-codex", - "grok-4-1-fast-reasoning", - "gemini-3.1-pro-preview", - ])); - }); +it("uses active chat model for the synthesizer slot", async () => { + const settings = { + ...DEFAULT_SETTINGS, + enabled: true, + teamSize: 4, + model: "gemini-3-flash-preview", + criticModel: "openai-codex/gpt-5.3-codex", + researcherModel: "grok-4-1-fast-reasoning", + synthModel: "gemini-3.1-pro-preview", + minParticipatingAgents: 2, + minAnsweringAgents: 2, + }; + const seen: string[] = []; + const result = await runRuntimeBridge( + { prompt: "hello", settings, activeChatModel: "openai/gpt-active-chat" }, + async ({ model }) => { + seen.push(model); + return { text: `ok-${model}`, refusal: false }; + }, + ); + expect(result.unavailable).toBe(false); + expect(seen).toEqual([ + "gemini-3-flash-preview", + "openai-codex/gpt-5.3-codex", + "grok-4-1-fast-reasoning", + "openai/gpt-active-chat", + ]); +}); diff --git a/extensions/braintrust/src/runtime-bridge.ts b/extensions/braintrust/src/runtime-bridge.ts index 0d7be15..0ee73bb 100644 --- a/extensions/braintrust/src/runtime-bridge.ts +++ b/extensions/braintrust/src/runtime-bridge.ts @@ -20,6 +20,7 @@ export type CandidateRunner = (input: CandidateRunnerInput) => Promise { const tasks = Array.from({ length: input.settings.teamSize }, async (_, i) => { const id = `agent-${i + 1}`; - const model = modelFor(i, input.settings); + const model = modelFor(i, input.settings, input.activeChatModel); const started = Date.now(); try { const out = await withTimeout( diff --git a/extensions/braintrust/src/settings.ts b/extensions/braintrust/src/settings.ts index 84ddee9..201e350 100644 --- a/extensions/braintrust/src/settings.ts +++ b/extensions/braintrust/src/settings.ts @@ -9,6 +9,10 @@ export type BraintrustSettings = { model: string; criticModel: string; researcherModel: string; + /** + * Deprecated: synthesis now runs on the active chat model instead of a dedicated synth model. + * Kept for backward-compatible config parsing/status output only. + */ synthModel: string; timeoutSeconds: number; minParticipatingAgents: number; From c0bd9f71677501e6ffc0f4037fc3e86837b8dd9e Mon Sep 17 00:00:00 2001 From: Victor al Ghul Date: Sat, 7 Mar 2026 22:55:44 +0000 Subject: [PATCH 12/14] refactor(braintrust): remove provider-specific model fixtures --- extensions/braintrust/README.md | 12 +- extensions/braintrust/index.js | 10 +- extensions/braintrust/index.ts | 10 +- extensions/braintrust/openclaw.plugin.json | 8 +- extensions/braintrust/pnpm-lock.yaml | 7769 +++++++++++++++++ extensions/braintrust/src/index.test.ts | 16 +- extensions/braintrust/src/policy.test.ts | 20 +- extensions/braintrust/src/policy.ts | 1 + .../src/runtime-bridge.integration.test.ts | 25 +- .../braintrust/src/runtime-bridge.test.ts | 75 +- extensions/braintrust/src/runtime-bridge.ts | 141 +- extensions/braintrust/src/settings.test.ts | 2 +- extensions/braintrust/src/settings.ts | 8 +- extensions/braintrust/src/synth.ts | 51 +- extensions/braintrust/src/worker-plan.test.ts | 41 + extensions/braintrust/src/worker-plan.ts | 90 + 16 files changed, 8133 insertions(+), 146 deletions(-) create mode 100644 extensions/braintrust/pnpm-lock.yaml create mode 100644 extensions/braintrust/src/worker-plan.test.ts create mode 100644 extensions/braintrust/src/worker-plan.ts diff --git a/extensions/braintrust/README.md b/extensions/braintrust/README.md index 4f8704b..e1f2581 100644 --- a/extensions/braintrust/README.md +++ b/extensions/braintrust/README.md @@ -7,14 +7,16 @@ Feature-flagged plugin path for multi-agent orchestration in OpenClaw. - Local CLI surface: `openclaw braintrust [on|off|status|unavailable|solver |critic |researcher ]` (via `registerCli` when available) - Configurable team size / strategy / model roles - Runtime bridge calls real model executions when a runtime executor is available in hook context +- Worker-plan execution now uses explicit solver / critic / researcher / synthesizer roles with strategy-shaped prompts instead of index-only slot simulation +- Synthesizer receives prior worker outputs and runs on the active chat model, returning structured JSON when the runtime supports it - Quorum contract configuration (`minParticipatingAgents`, `minAnsweringAgents`) - Explicit unavailable behavior when runtime fan-out is unavailable; no fake panel simulation fallback - Logging hooks for `llm_input` and `llm_output` - Deterministic quorum policy helpers + unit tests ## Important limitation (still pending) -This plugin now routes Braintrust candidates through the runtime executor when one is exposed in hook context, but it **still does not own true orchestrator-managed subagent lifecycle** by itself. -It relies on the hosting runtime for actual worker/model execution; deeper worker spawning, cross-agent state, and richer fan-in remain core integration tasks. +This plugin now routes Braintrust workers through the runtime executor when one is exposed in hook context, but it **still does not own true orchestrator-managed subagent lifecycle** by itself. +It relies on the hosting runtime for actual worker/model execution; deeper worker spawning, persistent cross-agent state, and first-class host-managed worker/session APIs remain core integration tasks. Also note: `openclaw agent --local --message '/braintrust ...'` does not route through plugin slash-command dispatch. Use `openclaw braintrust ...` for local CLI control. @@ -32,9 +34,9 @@ pnpm vitest run extensions/braintrust/src/policy.test.ts extensions/braintrust/s ## Default model routing -- solver (`model`): `gemini-3-flash-preview` -- critic (`criticModel`): `openai-codex/gpt-5.3-codex` -- researcher (`researcherModel`): `grok-4-1-fast-reasoning` +- solver (`model`): `model-a` +- critic (`criticModel`): `model-b` +- researcher (`researcherModel`): `model-c` - synthesizer: **active chat model** (agent-4, expected to output structured JSON like `{"final": "...", "reasoning": "...", "sources": [...]}`) `syntModel` / `synthModel` config is now effectively legacy compatibility state; synthesis should follow the active chat model instead of a dedicated synth override. diff --git a/extensions/braintrust/index.js b/extensions/braintrust/index.js index 9680ae0..574c260 100644 --- a/extensions/braintrust/index.js +++ b/extensions/braintrust/index.js @@ -6,12 +6,12 @@ const braintrustConfigSchema = { additionalProperties: false, properties: { enabled: { type: "boolean", default: false }, - teamSize: { type: "integer", minimum: 1, maximum: 4, default: 3 }, + teamSize: { type: "integer", minimum: 1, maximum: 4, default: 4 }, strategy: { type: "string", enum: ["independent", "debate", "panel"], default: "panel" }, - model: { type: "string", default: "gemini-3-flash-preview" }, - criticModel: { type: "string", default: "openai-codex/gpt-5.3-codex" }, - researcherModel: { type: "string", default: "grok-4-1-fast-reasoning" }, - synthModel: { type: "string", default: "gemini-3.1-pro-preview" }, + model: { type: "string", default: "model-a" }, + criticModel: { type: "string", default: "model-b" }, + researcherModel: { type: "string", default: "model-c" }, + synthModel: { type: "string", default: "legacy-synth-model" }, timeoutSeconds: { type: "integer", minimum: 10, maximum: 300, default: 90 }, minParticipatingAgents: { type: "integer", minimum: 1, maximum: 4, default: 2 }, minAnsweringAgents: { type: "integer", minimum: 1, maximum: 4, default: 2 } diff --git a/extensions/braintrust/index.ts b/extensions/braintrust/index.ts index d3b4b28..2f2046a 100644 --- a/extensions/braintrust/index.ts +++ b/extensions/braintrust/index.ts @@ -14,12 +14,12 @@ const braintrustConfigSchema = { additionalProperties: false, properties: { enabled: { type: "boolean", default: false }, - teamSize: { type: "integer", minimum: 1, maximum: 4, default: 3 }, + teamSize: { type: "integer", minimum: 1, maximum: 4, default: 4 }, strategy: { type: "string", enum: ["independent", "debate", "panel"], default: "panel" }, - model: { type: "string", default: "gemini-3-flash-preview" }, - criticModel: { type: "string", default: "openai-codex/gpt-5.3-codex" }, - researcherModel: { type: "string", default: "grok-4-1-fast-reasoning" }, - synthModel: { type: "string", default: "gemini-3.1-pro-preview" }, + model: { type: "string", default: "model-a" }, + criticModel: { type: "string", default: "model-b" }, + researcherModel: { type: "string", default: "model-c" }, + synthModel: { type: "string", default: "legacy-synth-model" }, timeoutSeconds: { type: "integer", minimum: 10, maximum: 300, default: 90 }, minParticipatingAgents: { type: "integer", minimum: 1, maximum: 4, default: 2 }, minAnsweringAgents: { type: "integer", minimum: 1, maximum: 4, default: 2 }, diff --git a/extensions/braintrust/openclaw.plugin.json b/extensions/braintrust/openclaw.plugin.json index 7116130..c591bb2 100644 --- a/extensions/braintrust/openclaw.plugin.json +++ b/extensions/braintrust/openclaw.plugin.json @@ -25,19 +25,19 @@ }, "model": { "type": "string", - "default": "gemini-3-flash-preview" + "default": "model-a" }, "criticModel": { "type": "string", - "default": "openai-codex/gpt-5.3-codex" + "default": "model-b" }, "researcherModel": { "type": "string", - "default": "grok-4-1-fast-reasoning" + "default": "model-c" }, "synthModel": { "type": "string", - "default": "gemini-3.1-pro-preview" + "default": "legacy-synth-model" }, "timeoutSeconds": { "type": "integer", diff --git a/extensions/braintrust/pnpm-lock.yaml b/extensions/braintrust/pnpm-lock.yaml new file mode 100644 index 0000000..934c933 --- /dev/null +++ b/extensions/braintrust/pnpm-lock.yaml @@ -0,0 +1,7769 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + openclaw: + specifier: '>=2026.1.26' + version: 2026.3.2(@napi-rs/canvas@0.1.96)(@types/express@5.0.6)(hono@4.12.5)(node-llama-cpp@3.16.2) + devDependencies: + vitest: + specifier: ^3.2.4 + version: 3.2.4(@types/node@25.3.5)(jiti@2.6.1)(yaml@2.8.2) + +packages: + + '@agentclientprotocol/sdk@0.14.1': + resolution: {integrity: sha512-b6r3PS3Nly+Wyw9U+0nOr47bV8tfS476EgyEMhoKvJCZLbgqoDFN7DJwkxL88RR0aiOqOYV1ZnESHqb+RmdH8w==} + peerDependencies: + zod: ^3.25.0 || ^4.0.0 + + '@anthropic-ai/sdk@0.73.0': + resolution: {integrity: sha512-URURVzhxXGJDGUGFunIOtBlSl7KWvZiAAKY/ttTkZAkXT9bTPqdk2eK0b8qqSxXpikh3QKPnPYpiyX98zf5ebw==} + hasBin: true + peerDependencies: + zod: ^3.25.0 || ^4.0.0 + peerDependenciesMeta: + zod: + optional: true + + '@aws-crypto/crc32@5.2.0': + resolution: {integrity: sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==} + engines: {node: '>=16.0.0'} + + '@aws-crypto/sha256-browser@5.2.0': + resolution: {integrity: sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==} + + '@aws-crypto/sha256-js@5.2.0': + resolution: {integrity: sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==} + engines: {node: '>=16.0.0'} + + '@aws-crypto/supports-web-crypto@5.2.0': + resolution: {integrity: sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==} + + '@aws-crypto/util@5.2.0': + resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} + + '@aws-sdk/client-bedrock-runtime@3.1004.0': + resolution: {integrity: sha512-t8cl+bPLlHZQD2Sw1a4hSLUybqJZU71+m8znkyeU8CHntFqEp2mMbuLKdHKaAYQ1fAApXMsvzenCAkDzNeeJlw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/client-bedrock@3.1004.0': + resolution: {integrity: sha512-JbfZSV85IL+43S7rPBmeMbvoOYXs1wmrfbEpHkDBjkvbukRQWtoetiPAXNSKDfFq1qVsoq8sWPdoerDQwlUO8w==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/core@3.973.18': + resolution: {integrity: sha512-GUIlegfcK2LO1J2Y98sCJy63rQSiLiDOgVw7HiHPRqfI2vb3XozTVqemwO0VSGXp54ngCnAQz0Lf0YPCBINNxA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-env@3.972.16': + resolution: {integrity: sha512-HrdtnadvTGAQUr18sPzGlE5El3ICphnH6SU7UQOMOWFgRKbTRNN8msTxM4emzguUso9CzaHU2xy5ctSrmK5YNA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-http@3.972.18': + resolution: {integrity: sha512-NyB6smuZAixND5jZumkpkunQ0voc4Mwgkd+SZ6cvAzIB7gK8HV8Zd4rS8Kn5MmoGgusyNfVGG+RLoYc4yFiw+A==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-ini@3.972.17': + resolution: {integrity: sha512-dFqh7nfX43B8dO1aPQHOcjC0SnCJ83H3F+1LoCh3X1P7E7N09I+0/taID0asU6GCddfDExqnEvQtDdkuMe5tKQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-login@3.972.17': + resolution: {integrity: sha512-gf2E5b7LpKb+JX2oQsRIDxdRZjBFZt2olCGlWCdb3vBERbXIPgm2t1R5mEnwd4j0UEO/Tbg5zN2KJbHXttJqwA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-node@3.972.18': + resolution: {integrity: sha512-ZDJa2gd1xiPg/nBDGhUlat02O8obaDEnICBAVS8qieZ0+nDfaB0Z3ec6gjZj27OqFTjnB/Q5a0GwQwb7rMVViw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-process@3.972.16': + resolution: {integrity: sha512-n89ibATwnLEg0ZdZmUds5bq8AfBAdoYEDpqP3uzPLaRuGelsKlIvCYSNNvfgGLi8NaHPNNhs1HjJZYbqkW9b+g==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-sso@3.972.17': + resolution: {integrity: sha512-wGtte+48xnhnhHMl/MsxzacBPs5A+7JJedjiP452IkHY7vsbYKcvQBqFye8LwdTJVeHtBHv+JFeTscnwepoWGg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-web-identity@3.972.17': + resolution: {integrity: sha512-8aiVJh6fTdl8gcyL+sVNcNwTtWpmoFa1Sh7xlj6Z7L/cZ/tYMEBHq44wTYG8Kt0z/PpGNopD89nbj3FHl9QmTA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/eventstream-handler-node@3.972.10': + resolution: {integrity: sha512-g2Z9s6Y4iNh0wICaEqutgYgt/Pmhv5Ev9G3eKGFe2w9VuZDhc76vYdop6I5OocmpHV79d4TuLG+JWg5rQIVDVA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-eventstream@3.972.7': + resolution: {integrity: sha512-VWndapHYCfwLgPpCb/xwlMKG4imhFzKJzZcKOEioGn7OHY+6gdr0K7oqy1HZgbLa3ACznZ9fku+DzmAi8fUC0g==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-host-header@3.972.7': + resolution: {integrity: sha512-aHQZgztBFEpDU1BB00VWCIIm85JjGjQW1OG9+98BdmaOpguJvzmXBGbnAiYcciCd+IS4e9BEq664lhzGnWJHgQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-logger@3.972.7': + resolution: {integrity: sha512-LXhiWlWb26txCU1vcI9PneESSeRp/RYY/McuM4SpdrimQR5NgwaPb4VJCadVeuGWgh6QmqZ6rAKSoL1ob16W6w==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-recursion-detection@3.972.7': + resolution: {integrity: sha512-l2VQdcBcYLzIzykCHtXlbpiVCZ94/xniLIkAj0jpnpjY4xlgZx7f56Ypn+uV1y3gG0tNVytJqo3K9bfMFee7SQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-user-agent@3.972.19': + resolution: {integrity: sha512-Km90fcXt3W/iqujHzuM6IaDkYCj73gsYufcuWXApWdzoTy6KGk8fnchAjePMARU0xegIR3K4N3yIo1vy7OVe8A==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-websocket@3.972.12': + resolution: {integrity: sha512-iyPP6FVDKe/5wy5ojC0akpDFG1vX3FeCUU47JuwN8xfvT66xlEI8qUJZPtN55TJVFzzWZJpWL78eqUE31md08Q==} + engines: {node: '>= 14.0.0'} + + '@aws-sdk/nested-clients@3.996.7': + resolution: {integrity: sha512-MlGWA8uPaOs5AiTZ5JLM4uuWDm9EEAnm9cqwvqQIc6kEgel/8s1BaOWm9QgUcfc9K8qd7KkC3n43yDbeXOA2tg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/region-config-resolver@3.972.7': + resolution: {integrity: sha512-/Ev/6AI8bvt4HAAptzSjThGUMjcWaX3GX8oERkB0F0F9x2dLSBdgFDiyrRz3i0u0ZFZFQ1b28is4QhyqXTUsVA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/token-providers@3.1004.0': + resolution: {integrity: sha512-j9BwZZId9sFp+4GPhf6KrwO8Tben2sXibZA8D1vv2I1zBdvkUHcBA2g4pkqIpTRalMTLC0NPkBPX0gERxfy/iA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/types@3.973.5': + resolution: {integrity: sha512-hl7BGwDCWsjH8NkZfx+HgS7H2LyM2lTMAI7ba9c8O0KqdBLTdNJivsHpqjg9rNlAlPyREb6DeDRXUl0s8uFdmQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/util-endpoints@3.996.4': + resolution: {integrity: sha512-Hek90FBmd4joCFj+Vc98KLJh73Zqj3s2W56gjAcTkrNLMDI5nIFkG9YpfcJiVI1YlE2Ne1uOQNe+IgQ/Vz2XRA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/util-format-url@3.972.7': + resolution: {integrity: sha512-V+PbnWfUl93GuFwsOHsAq7hY/fnm9kElRqR8IexIJr5Rvif9e614X5sGSyz3mVSf1YAZ+VTy63W1/pGdA55zyA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/util-locate-window@3.965.5': + resolution: {integrity: sha512-WhlJNNINQB+9qtLtZJcpQdgZw3SCDCpXdUJP7cToGwHbCWCnRckGlc6Bx/OhWwIYFNAn+FIydY8SZ0QmVu3xTQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/util-user-agent-browser@3.972.7': + resolution: {integrity: sha512-7SJVuvhKhMF/BkNS1n0QAJYgvEwYbK2QLKBrzDiwQGiTRU6Yf1f3nehTzm/l21xdAOtWSfp2uWSddPnP2ZtsVw==} + + '@aws-sdk/util-user-agent-node@3.973.4': + resolution: {integrity: sha512-uqKeLqZ9D3nQjH7HGIERNXK9qnSpUK08l4MlJ5/NZqSSdeJsVANYp437EM9sEzwU28c2xfj2V6qlkqzsgtKs6Q==} + engines: {node: '>=20.0.0'} + peerDependencies: + aws-crt: '>=1.0.0' + peerDependenciesMeta: + aws-crt: + optional: true + + '@aws-sdk/xml-builder@3.972.10': + resolution: {integrity: sha512-OnejAIVD+CxzyAUrVic7lG+3QRltyja9LoNqCE/1YVs8ichoTbJlVSaZ9iSMcnHLyzrSNtvaOGjSDRP+d/ouFA==} + engines: {node: '>=20.0.0'} + + '@aws/lambda-invoke-store@0.2.3': + resolution: {integrity: sha512-oLvsaPMTBejkkmHhjf09xTgk71mOqyr/409NKhRIL08If7AhVfUsJhVsx386uJaqNd42v9kWamQ9lFbkoC2dYw==} + engines: {node: '>=18.0.0'} + + '@babel/runtime@7.28.6': + resolution: {integrity: sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==} + engines: {node: '>=6.9.0'} + + '@borewit/text-codec@0.2.1': + resolution: {integrity: sha512-k7vvKPbf7J2fZ5klGRD9AeKfUvojuZIQ3BT5u7Jfv+puwXkUBUT5PVyMDfJZpy30CBDXGMgw7fguK/lpOMBvgw==} + + '@buape/carbon@0.0.0-beta-20260216184201': + resolution: {integrity: sha512-u5mgYcigfPVqT7D9gVTGd+3YSflTreQmrWog7ORbb0z5w9eT8ft4rJOdw9fGwr75zMu9kXpSBaAcY2eZoJFSdA==} + + '@cacheable/memory@2.0.8': + resolution: {integrity: sha512-FvEb29x5wVwu/Kf93IWwsOOEuhHh6dYCJF3vcKLzXc0KXIW181AOzv6ceT4ZpBHDvAfG60eqb+ekmrnLHIy+jw==} + + '@cacheable/node-cache@1.7.6': + resolution: {integrity: sha512-6Omk2SgNnjtxB5f/E6bTIWIt5xhdpx39fGNRQgU9lojvRxU68v+qY+SXXLsp3ZGukqoPjsK21wZ6XABFr/Ge3A==} + engines: {node: '>=18'} + + '@cacheable/utils@2.4.0': + resolution: {integrity: sha512-PeMMsqjVq+bF0WBsxFBxr/WozBJiZKY0rUojuaCoIaKnEl3Ju1wfEwS+SV1DU/cSe8fqHIPiYJFif8T3MVt4cQ==} + + '@clack/core@1.1.0': + resolution: {integrity: sha512-SVcm4Dqm2ukn64/8Gub2wnlA5nS2iWJyCkdNHcvNHPIeBTGojpdJ+9cZKwLfmqy7irD4N5qLteSilJlE0WLAtA==} + + '@clack/prompts@1.1.0': + resolution: {integrity: sha512-pkqbPGtohJAvm4Dphs2M8xE29ggupihHdy1x84HNojZuMtFsHiUlRvqD24tM2+XmI+61LlfNceM3Wr7U5QES5g==} + + '@cloudflare/workers-types@4.20260120.0': + resolution: {integrity: sha512-B8pueG+a5S+mdK3z8oKu1ShcxloZ7qWb68IEyLLaepvdryIbNC7JVPcY0bWsjS56UQVKc5fnyRge3yZIwc9bxw==} + + '@discordjs/node-pre-gyp@0.4.5': + resolution: {integrity: sha512-YJOVVZ545x24mHzANfYoy0BJX5PDyeZlpiJjDkUBM/V/Ao7TFX9lcUvCN4nr0tbr5ubeaXxtEBILUrHtTphVeQ==} + hasBin: true + + '@discordjs/opus@0.10.0': + resolution: {integrity: sha512-HHEnSNrSPmFEyndRdQBJN2YE6egyXS9JUnJWyP6jficK0Y+qKMEZXyYTgmzpjrxXP1exM/hKaNP7BRBUEWkU5w==} + engines: {node: '>=12.0.0'} + + '@discordjs/voice@0.19.0': + resolution: {integrity: sha512-UyX6rGEXzVyPzb1yvjHtPfTlnLvB5jX/stAMdiytHhfoydX+98hfympdOwsnTktzr+IRvphxTbdErgYDJkEsvw==} + engines: {node: '>=22.12.0'} + + '@emnapi/core@1.8.1': + resolution: {integrity: sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==} + + '@emnapi/runtime@1.8.1': + resolution: {integrity: sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==} + + '@emnapi/wasi-threads@1.1.0': + resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} + + '@esbuild/aix-ppc64@0.27.3': + resolution: {integrity: sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.27.3': + resolution: {integrity: sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.27.3': + resolution: {integrity: sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.27.3': + resolution: {integrity: sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.27.3': + resolution: {integrity: sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.27.3': + resolution: {integrity: sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.27.3': + resolution: {integrity: sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.27.3': + resolution: {integrity: sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.27.3': + resolution: {integrity: sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.27.3': + resolution: {integrity: sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.27.3': + resolution: {integrity: sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.27.3': + resolution: {integrity: sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.27.3': + resolution: {integrity: sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.27.3': + resolution: {integrity: sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.27.3': + resolution: {integrity: sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.27.3': + resolution: {integrity: sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.27.3': + resolution: {integrity: sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.27.3': + resolution: {integrity: sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.27.3': + resolution: {integrity: sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.27.3': + resolution: {integrity: sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.27.3': + resolution: {integrity: sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.27.3': + resolution: {integrity: sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.27.3': + resolution: {integrity: sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.27.3': + resolution: {integrity: sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.27.3': + resolution: {integrity: sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.27.3': + resolution: {integrity: sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@google/genai@1.44.0': + resolution: {integrity: sha512-kRt9ZtuXmz+tLlcNntN/VV4LRdpl6ZOu5B1KbfNgfR65db15O6sUQcwnwLka8sT/V6qysD93fWrgJHF2L7dA9A==} + engines: {node: '>=20.0.0'} + peerDependencies: + '@modelcontextprotocol/sdk': ^1.25.2 + peerDependenciesMeta: + '@modelcontextprotocol/sdk': + optional: true + + '@grammyjs/runner@2.0.3': + resolution: {integrity: sha512-nckmTs1dPWfVQteK9cxqxzE+0m1VRvluLWB8UgFzsjg62w3qthPJt0TYtJBEdG7OedvfQq4vnFAyE6iaMkR42A==} + engines: {node: '>=12.20.0 || >=14.13.1'} + peerDependencies: + grammy: ^1.13.1 + + '@grammyjs/transformer-throttler@1.2.1': + resolution: {integrity: sha512-CpWB0F3rJdUiKsq7826QhQsxbZi4wqfz1ccKX+fr+AOC+o8K7ZvS+wqX0suSu1QCsyUq2MDpNiKhyL2ZOJUS4w==} + engines: {node: ^12.20.0 || >=14.13.1} + peerDependencies: + grammy: ^1.0.0 + + '@grammyjs/types@3.25.0': + resolution: {integrity: sha512-iN9i5p+8ZOu9OMxWNcguojQfz4K/PDyMPOnL7PPCON+SoA/F8OKMH3uR7CVUkYfdNe0GCz8QOzAWrnqusQYFOg==} + + '@hapi/boom@9.1.4': + resolution: {integrity: sha512-Ls1oH8jaN1vNsqcaHVYJrKmgMcKsC1wcp8bujvXrHaAqD2iDYq3HoOwsxwo09Cuda5R5nC0o0IxlrlTuvPuzSw==} + + '@hapi/hoek@9.3.0': + resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==} + + '@homebridge/ciao@1.3.5': + resolution: {integrity: sha512-f7MAw7YuoEYgJEQ1VyRcLHGuVmCpmXi65GVR8CAtPWPqIZf/HFr4vHzVpOfQMpEQw9Pt5uh07guuLt5HE8ruog==} + hasBin: true + + '@hono/node-server@1.19.9': + resolution: {integrity: sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw==} + engines: {node: '>=18.14.1'} + peerDependencies: + hono: ^4 + + '@huggingface/jinja@0.5.5': + resolution: {integrity: sha512-xRlzazC+QZwr6z4ixEqYHo9fgwhTZ3xNSdljlKfUFGZSdlvt166DljRELFUfFytlYOYvo3vTisA/AFOuOAzFQQ==} + engines: {node: '>=18'} + + '@img/colour@1.1.0': + resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} + engines: {node: '>=18'} + + '@img/sharp-darwin-arm64@0.34.5': + resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-x64@0.34.5': + resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-darwin-arm64@1.2.4': + resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.2.4': + resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-linux-arm64@1.2.4': + resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-arm@1.2.4': + resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-ppc64@1.2.4': + resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-riscv64@1.2.4': + resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-s390x@1.2.4': + resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-x64@1.2.4': + resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@img/sharp-libvips-linuxmusl-x64@1.2.4': + resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@img/sharp-linux-arm64@0.34.5': + resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-arm@0.34.5': + resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-ppc64@0.34.5': + resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-riscv64@0.34.5': + resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-s390x@0.34.5': + resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-x64@0.34.5': + resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@img/sharp-linuxmusl-arm64@0.34.5': + resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@img/sharp-linuxmusl-x64@0.34.5': + resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@img/sharp-wasm32@0.34.5': + resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [wasm32] + + '@img/sharp-win32-arm64@0.34.5': + resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [win32] + + '@img/sharp-win32-ia32@0.34.5': + resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-x64@0.34.5': + resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [win32] + + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + + '@isaacs/fs-minipass@4.0.1': + resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} + engines: {node: '>=18.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@keyv/bigmap@1.3.1': + resolution: {integrity: sha512-WbzE9sdmQtKy8vrNPa9BRnwZh5UF4s1KTmSK0KUVLo3eff5BlQNNWDnFOouNpKfPKDnms9xynJjsMYjMaT/aFQ==} + engines: {node: '>= 18'} + peerDependencies: + keyv: ^5.6.0 + + '@keyv/serialize@1.1.1': + resolution: {integrity: sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==} + + '@kwsites/file-exists@1.1.1': + resolution: {integrity: sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==} + + '@kwsites/promise-deferred@1.1.1': + resolution: {integrity: sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==} + + '@larksuiteoapi/node-sdk@1.59.0': + resolution: {integrity: sha512-sBpkruTvZDOxnVtoTbepWKRX0j1Y1ZElQYu0x7+v088sI9pcpbVp6ZzCGn62dhrKPatzNyCJyzYCPXPYQWccrA==} + + '@line/bot-sdk@10.6.0': + resolution: {integrity: sha512-4hSpglL/G/cW2JCcohaYz/BS0uOSJNV9IEYdMm0EiPEvDLayoI2hGq2D86uYPQFD2gvgkyhmzdShpWLG3P5r3w==} + engines: {node: '>=20'} + + '@lydell/node-pty-darwin-arm64@1.2.0-beta.3': + resolution: {integrity: sha512-owcv+e1/OSu3bf9ZBdUQqJsQF888KyuSIiPYFNn0fLhgkhm9F3Pvha76Kj5mCPnodf7hh3suDe7upw7GPRXftQ==} + cpu: [arm64] + os: [darwin] + + '@lydell/node-pty-darwin-x64@1.2.0-beta.3': + resolution: {integrity: sha512-k38O+UviWrWdxtqZBBc/D8NJU11Rey8Y2YMwSWNxLv3eXZZdF5IVpbBkI/2RmLsV5nCcciqLPbukxeZnEfPlwA==} + cpu: [x64] + os: [darwin] + + '@lydell/node-pty-linux-arm64@1.2.0-beta.3': + resolution: {integrity: sha512-HUwRpGu3O+4sv9DAQFKnyW5LYhyYu2SDUa/bdFO/t4dIFCM4uDJEq47wfRM7+aYtJTi1b3lakN8SlWeuFQqJQQ==} + cpu: [arm64] + os: [linux] + + '@lydell/node-pty-linux-x64@1.2.0-beta.3': + resolution: {integrity: sha512-+RRY0PoCUeQaCvPR7/UnkGbxulwbFtoTWJfe+o4T1RcNtngrgaI55I9nl8CD8uqhGrB3smKuyvPM5UtwGhASUw==} + cpu: [x64] + os: [linux] + + '@lydell/node-pty-win32-arm64@1.2.0-beta.3': + resolution: {integrity: sha512-UEDd9ASp2M3iIYpIzfmfBlpyn4+K1G4CAjYcHWStptCkefoSVXWTiUBIa1KjBjZi3/xmsHIDpBEYTkGWuvLt2Q==} + cpu: [arm64] + os: [win32] + + '@lydell/node-pty-win32-x64@1.2.0-beta.3': + resolution: {integrity: sha512-TpdqSFYx7/Rj+68tuP6F/lkRYrHCYAIJgaS1bx3SctTkb5QAQCFwOKHd4xlsivmEOMT2LdhkJggPxwX9PAO5pQ==} + cpu: [x64] + os: [win32] + + '@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==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@mariozechner/clipboard-darwin-universal@0.3.2': + resolution: {integrity: sha512-mxSheKTW2U9LsBdXy0SdmdCAE5HqNS9QUmpNHLnfJ+SsbFKALjEZc5oRrVMXxGQSirDvYf5bjmRyT0QYYonnlg==} + engines: {node: '>= 10'} + os: [darwin] + + '@mariozechner/clipboard-darwin-x64@0.3.2': + resolution: {integrity: sha512-U1BcVEoidvwIp95+HJswSW+xr28EQiHR7rZjH6pn8Sja5yO4Yoe3yCN0Zm8Lo72BbSOK/fTSq0je7CJpaPCspg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@mariozechner/clipboard-linux-arm64-gnu@0.3.2': + resolution: {integrity: sha512-BsinwG3yWTIjdgNCxsFlip7LkfwPk+ruw/aFCXHUg/fb5XC/Ksp+YMQ7u0LUtiKzIv/7LMXgZInJQH6gxbAaqQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@mariozechner/clipboard-linux-arm64-musl@0.3.2': + resolution: {integrity: sha512-0/Gi5Xq2V6goXBop19ePoHvXsmJD9SzFlO3S+d6+T2b+BlPcpOu3Oa0wTjl+cZrLAAEzA86aPNBI+VVAFDFPKw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@mariozechner/clipboard-linux-riscv64-gnu@0.3.2': + resolution: {integrity: sha512-2AFFiXB24qf0zOZsxI1GJGb9wQGlOJyN6UwoXqmKS3dpQi/l6ix30IzDDA4c4ZcCcx4D+9HLYXhC1w7Sov8pXA==} + engines: {node: '>= 10'} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@mariozechner/clipboard-linux-x64-gnu@0.3.2': + resolution: {integrity: sha512-v6fVnsn7WMGg73Dab8QMwyFce7tzGfgEixKgzLP8f1GJqkJZi5zO4k4FOHzSgUufgLil63gnxvMpjWkgfeQN7A==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@mariozechner/clipboard-linux-x64-musl@0.3.2': + resolution: {integrity: sha512-xVUtnoMQ8v2JVyfJLKKXACA6avdnchdbBkTsZs8BgJQo29qwCp5NIHAUO8gbJ40iaEGToW5RlmVk2M9V0HsHEw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@mariozechner/clipboard-win32-arm64-msvc@0.3.2': + resolution: {integrity: sha512-AEgg95TNi8TGgak2wSXZkXKCvAUTjWoU1Pqb0ON7JHrX78p616XUFNTJohtIon3e0w6k0pYPZeCuqRCza/Tqeg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@mariozechner/clipboard-win32-x64-msvc@0.3.2': + resolution: {integrity: sha512-tGRuYpZwDOD7HBrCpyRuhGnHHSCknELvqwKKUG4JSfSB7JIU7LKRh6zx6fMUOQd8uISK35TjFg5UcNih+vJhFA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@mariozechner/clipboard@0.3.2': + resolution: {integrity: sha512-IHQpksNjo7EAtGuHFU+tbWDp5LarH3HU/8WiB9O70ZEoBPHOg0/6afwSLK0QyNMMmx4Bpi/zl6+DcBXe95nWYA==} + engines: {node: '>= 10'} + + '@mariozechner/jiti@2.6.5': + resolution: {integrity: sha512-faGUlTcXka5l7rv0lP3K3vGW/ejRuOS24RR2aSFWREUQqzjgdsuWNo/IiPqL3kWRGt6Ahl2+qcDAwtdeWeuGUw==} + hasBin: true + + '@mariozechner/pi-agent-core@0.55.3': + resolution: {integrity: sha512-rqbfpQ9BrP6BDiW+Ps3A8Z/p9+Md/pAfc/ECq8JP6cwnZL/jQgU355KWZKtF8zM9az1p0Q9hIWi9cQygVo6Auw==} + engines: {node: '>=20.0.0'} + + '@mariozechner/pi-ai@0.55.3': + resolution: {integrity: sha512-f9jWoDzJR9Wy/H8JPMbjoM4WvVUeFZ65QdYA9UHIfoOopDfwWE8F8JHQOj5mmmILMacXuzsqA3J7MYqNWZRvvQ==} + engines: {node: '>=20.0.0'} + hasBin: true + + '@mariozechner/pi-coding-agent@0.55.3': + resolution: {integrity: sha512-5SFbB7/BIp/Crjre7UNjUeNfpoU1KSW/i6LXa+ikJTBqI5LukWq2avE5l0v0M8Pg/dt1go2XCLrNFlQJiQDSPQ==} + engines: {node: '>=20.0.0'} + hasBin: true + + '@mariozechner/pi-tui@0.55.3': + resolution: {integrity: sha512-Gh4wkYgiSPCJJaB/4wEWSL7Ga8bxSq1Crp1RPRT4vKybE/DG0W/MQr5VJDvktarxtJrD16ixScwE4dzdox/PIA==} + engines: {node: '>=20.0.0'} + + '@mistralai/mistralai@1.10.0': + resolution: {integrity: sha512-tdIgWs4Le8vpvPiUEWne6tK0qbVc+jMenujnvTqOjogrJUsCSQhus0tHTU1avDDh5//Rq2dFgP9mWRAdIEoBqg==} + + '@mozilla/readability@0.6.0': + resolution: {integrity: sha512-juG5VWh4qAivzTAeMzvY9xs9HY5rAcr2E4I7tiSSCokRFi7XIZCAu92ZkSTsIj1OPceCifL3cpfteP3pDT9/QQ==} + engines: {node: '>=14.0.0'} + + '@napi-rs/canvas-android-arm64@0.1.96': + resolution: {integrity: sha512-ew1sPrN3dGdZ3L4FoohPfnjq0f9/Jk7o+wP7HkQZokcXgIUD6FIyICEWGhMYzv53j63wUcPvZeAwgewX58/egg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + + '@napi-rs/canvas-darwin-arm64@0.1.96': + resolution: {integrity: sha512-Q/wOXZ5PzTqpdmA5eUOcegCf4Go/zz3aZ5DlzSeDpOjFmfwMKh8EzLAoweQ+mJVagcHQyzoJhaTEnrO68TNyNg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@napi-rs/canvas-darwin-x64@0.1.96': + resolution: {integrity: sha512-UrXiQz28tQEvGM1qvyptewOAfmUrrd5+wvi6Rzjj2VprZI8iZ2KIvBD2lTTG1bVF95AbeDeG7PJA0D9sLKaOFA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@napi-rs/canvas-linux-arm-gnueabihf@0.1.96': + resolution: {integrity: sha512-I90ODxweD8aEP6XKU/NU+biso95MwCtQ2F46dUvhec1HesFi0tq/tAJkYic/1aBSiO/1kGKmSeD1B0duOHhEHQ==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@napi-rs/canvas-linux-arm64-gnu@0.1.96': + resolution: {integrity: sha512-Dx/0+RFV++w3PcRy+4xNXkghhXjA5d0Mw1bs95emn5Llinp1vihMaA6WJt3oYv2LAHc36+gnrhIBsPhUyI2SGw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@napi-rs/canvas-linux-arm64-musl@0.1.96': + resolution: {integrity: sha512-UvOi7fii3IE2KDfEfhh8m+LpzSRvhGK7o1eho99M2M0HTik11k3GX+2qgVx9EtujN3/bhFFS1kSO3+vPMaJ0Mg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@napi-rs/canvas-linux-riscv64-gnu@0.1.96': + resolution: {integrity: sha512-MBSukhGCQ5nRtf9NbFYWOU080yqkZU1PbuH4o1ROvB4CbPl12fchDR35tU83Wz8gWIM9JTn99lBn9DenPIv7Ig==} + engines: {node: '>= 10'} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@napi-rs/canvas-linux-x64-gnu@0.1.96': + resolution: {integrity: sha512-I/ccu2SstyKiV3HIeVzyBIWfrJo8cN7+MSQZPnabewWV6hfJ2nY7Df2WqOHmobBRUw84uGR6zfQHsUEio/m5Vg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@napi-rs/canvas-linux-x64-musl@0.1.96': + resolution: {integrity: sha512-H3uov7qnTl73GDT4h52lAqpJPsl1tIUyNPWJyhQ6gHakohNqqRq3uf80+NEpzcytKGEOENP1wX3yGwZxhjiWEQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@napi-rs/canvas-win32-arm64-msvc@0.1.96': + resolution: {integrity: sha512-ATp6Y+djOjYtkfV/VRH7CZ8I1MEtkUQBmKUbuWw5zWEHHqfL0cEcInE4Cxgx7zkNAhEdBbnH8HMVrqNp+/gwxA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@napi-rs/canvas-win32-x64-msvc@0.1.96': + resolution: {integrity: sha512-UYGdTltVd+Z8mcIuoqGmAXXUvwH5CLf2M6mIB5B0/JmX5J041jETjqtSYl7gN+aj3k1by/SG6sS0hAwCqyK7zw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@napi-rs/canvas@0.1.96': + resolution: {integrity: sha512-6NNmNxvoJKeucVjxaaRUt3La2i5jShgiAbaY3G/72s1Vp3U06XPrAIxkAjBxpDcamEn/t+WJ4OOlGmvILo4/Ew==} + engines: {node: '>= 10'} + + '@napi-rs/wasm-runtime@1.1.1': + resolution: {integrity: sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==} + + '@node-llama-cpp/linux-arm64@3.16.2': + resolution: {integrity: sha512-CxzgPsS84wL3W5sZRgxP3c9iJKEW+USrak1SmX6EAJxW/v9QGzehvT6W/aR1FyfidiIyQtOp3ga0Gg/9xfJPGw==} + engines: {node: '>=20.0.0'} + cpu: [arm64, x64] + os: [linux] + libc: [glibc] + + '@node-llama-cpp/linux-armv7l@3.16.2': + resolution: {integrity: sha512-9G6W/MkQ/DLwGmpcj143NQ50QJg5gQZfzVf5RYx77VczBqhgwkgYHILekYrOs4xanOeqeJ8jnOnQQSp1YaJZUg==} + engines: {node: '>=20.0.0'} + cpu: [arm, x64] + os: [linux] + libc: [glibc] + + '@node-llama-cpp/linux-x64-cuda-ext@3.16.2': + resolution: {integrity: sha512-47d9myCJauZyzAlN7IK1eIt/4CcBMslF+yHy4q+yJotD/RV/S6qRpK2kGn+ybtdVjkPGNCoPkHKcyla9iIVjbw==} + engines: {node: '>=20.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@node-llama-cpp/linux-x64-cuda@3.16.2': + resolution: {integrity: sha512-LTBQFqjin7tyrLNJz0XWTB5QAHDsZV71/qiiRRjXdBKSZHVVaPLfdgxypGu7ggPeBNsv+MckRXdlH5C7yMtE4A==} + engines: {node: '>=20.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@node-llama-cpp/linux-x64-vulkan@3.16.2': + resolution: {integrity: sha512-HDLAw4ZhwJuhKuF6n4x520yZXAQZahUOXtCGvPubjfpmIOElKrfDvCVlRsthAP0JwcwINzIQlVys3boMIXfBgw==} + engines: {node: '>=20.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@node-llama-cpp/linux-x64@3.16.2': + resolution: {integrity: sha512-OXYf8rVfoDyvN+YrfKk8F9An9a5GOxVIM8OcR1U911tc0oRNf8yfJrQ8KrM75R26gwq0Y6YZwVTP0vRCInwWOw==} + engines: {node: '>=20.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@node-llama-cpp/mac-arm64-metal@3.16.2': + resolution: {integrity: sha512-nEZ74qB0lUohF88yR741YUrUqz/qD+FJFzUTHj0FwxAynSZCjvwtzEDtavRlh3qd3yLD/0ChNn00/RQ54ISImw==} + engines: {node: '>=20.0.0'} + cpu: [arm64, x64] + os: [darwin] + + '@node-llama-cpp/mac-x64@3.16.2': + resolution: {integrity: sha512-BjA+DgeDt+kRxVMV6kChb9XVXm7U5b90jUif7Z/s6ZXtOOnV6exrTM2W09kbSqAiNhZmctcVY83h2dwNTZ/yIw==} + engines: {node: '>=20.0.0'} + cpu: [x64] + os: [darwin] + + '@node-llama-cpp/win-arm64@3.16.2': + resolution: {integrity: sha512-XHNFQzUjYODtkZjIn4NbQVrBtGB9RI9TpisiALryqfrIqagQmjBh6dmxZWlt5uduKAfT7M2/2vrABGR490FACA==} + engines: {node: '>=20.0.0'} + cpu: [arm64, x64] + os: [win32] + + '@node-llama-cpp/win-x64-cuda-ext@3.16.2': + resolution: {integrity: sha512-sdv4Kzn9bOQWNBRvw6B/zcn8dQRfZhjIHv5AfDBIOfRlSCgjebFpBeYUoU4wZPpjr3ISwcqO5MEWsw+AbUdV3Q==} + engines: {node: '>=20.0.0'} + cpu: [x64] + os: [win32] + + '@node-llama-cpp/win-x64-cuda@3.16.2': + resolution: {integrity: sha512-jStDELHrU3rKQMOk5Hs5bWEazyjE2hzHwpNf6SblOpaGkajM/HJtxEZoL0mLHJx5qeXs4oOVkr7AzuLy0WPpNA==} + engines: {node: '>=20.0.0'} + cpu: [x64] + os: [win32] + + '@node-llama-cpp/win-x64-vulkan@3.16.2': + resolution: {integrity: sha512-9xuHFCOhCQjZgQSFrk79EuSKn9nGWt/SAq/3wujQSQLtgp8jGdtZgwcmuDUoemInf10en2dcOmEt7t8dQdC3XA==} + engines: {node: '>=20.0.0'} + cpu: [x64] + os: [win32] + + '@node-llama-cpp/win-x64@3.16.2': + resolution: {integrity: sha512-etrivzbyLNVhZlUosFW8JSL0OSiuKQf9qcI3dNdehD907sHquQbBJrG7lXcdL6IecvXySp3oAwCkM87VJ0b3Fg==} + engines: {node: '>=20.0.0'} + cpu: [x64] + os: [win32] + + '@nolyfill/domexception@1.0.28': + resolution: {integrity: sha512-tlc/FcYIv5i8RYsl2iDil4A0gOihaas1R5jPcIC4Zw3GhjKsVilw90aHcVlhZPTBLGBzd379S+VcnsDjd9ChiA==} + engines: {node: '>=12.4.0'} + + '@octokit/app@16.1.2': + resolution: {integrity: sha512-8j7sEpUYVj18dxvh0KWj6W/l6uAiVRBl1JBDVRqH1VHKAO/G5eRVl4yEoYACjakWers1DjUkcCHyJNQK47JqyQ==} + engines: {node: '>= 20'} + + '@octokit/auth-app@8.2.0': + resolution: {integrity: sha512-vVjdtQQwomrZ4V46B9LaCsxsySxGoHsyw6IYBov/TqJVROrlYdyNgw5q6tQbB7KZt53v1l1W53RiqTvpzL907g==} + engines: {node: '>= 20'} + + '@octokit/auth-oauth-app@9.0.3': + resolution: {integrity: sha512-+yoFQquaF8OxJSxTb7rnytBIC2ZLbLqA/yb71I4ZXT9+Slw4TziV9j/kyGhUFRRTF2+7WlnIWsePZCWHs+OGjg==} + engines: {node: '>= 20'} + + '@octokit/auth-oauth-device@8.0.3': + resolution: {integrity: sha512-zh2W0mKKMh/VWZhSqlaCzY7qFyrgd9oTWmTmHaXnHNeQRCZr/CXy2jCgHo4e4dJVTiuxP5dLa0YM5p5QVhJHbw==} + engines: {node: '>= 20'} + + '@octokit/auth-oauth-user@6.0.2': + resolution: {integrity: sha512-qLoPPc6E6GJoz3XeDG/pnDhJpTkODTGG4kY0/Py154i/I003O9NazkrwJwRuzgCalhzyIeWQ+6MDvkUmKXjg/A==} + engines: {node: '>= 20'} + + '@octokit/auth-token@6.0.0': + resolution: {integrity: sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==} + engines: {node: '>= 20'} + + '@octokit/auth-unauthenticated@7.0.3': + resolution: {integrity: sha512-8Jb1mtUdmBHL7lGmop9mU9ArMRUTRhg8vp0T1VtZ4yd9vEm3zcLwmjQkhNEduKawOOORie61xhtYIhTDN+ZQ3g==} + engines: {node: '>= 20'} + + '@octokit/core@7.0.6': + resolution: {integrity: sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q==} + engines: {node: '>= 20'} + + '@octokit/endpoint@11.0.3': + resolution: {integrity: sha512-FWFlNxghg4HrXkD3ifYbS/IdL/mDHjh9QcsNyhQjN8dplUoZbejsdpmuqdA76nxj2xoWPs7p8uX2SNr9rYu0Ag==} + engines: {node: '>= 20'} + + '@octokit/graphql@9.0.3': + resolution: {integrity: sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA==} + engines: {node: '>= 20'} + + '@octokit/oauth-app@8.0.3': + resolution: {integrity: sha512-jnAjvTsPepyUaMu9e69hYBuozEPgYqP4Z3UnpmvoIzHDpf8EXDGvTY1l1jK0RsZ194oRd+k6Hm13oRU8EoDFwg==} + engines: {node: '>= 20'} + + '@octokit/oauth-authorization-url@8.0.0': + resolution: {integrity: sha512-7QoLPRh/ssEA/HuHBHdVdSgF8xNLz/Bc5m9fZkArJE5bb6NmVkDm3anKxXPmN1zh6b5WKZPRr3697xKT/yM3qQ==} + engines: {node: '>= 20'} + + '@octokit/oauth-methods@6.0.2': + resolution: {integrity: sha512-HiNOO3MqLxlt5Da5bZbLV8Zarnphi4y9XehrbaFMkcoJ+FL7sMxH/UlUsCVxpddVu4qvNDrBdaTVE2o4ITK8ng==} + engines: {node: '>= 20'} + + '@octokit/openapi-types@27.0.0': + resolution: {integrity: sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA==} + + '@octokit/openapi-webhooks-types@12.1.0': + resolution: {integrity: sha512-WiuzhOsiOvb7W3Pvmhf8d2C6qaLHXrWiLBP4nJ/4kydu+wpagV5Fkz9RfQwV2afYzv3PB+3xYgp4mAdNGjDprA==} + + '@octokit/plugin-paginate-graphql@6.0.0': + resolution: {integrity: sha512-crfpnIoFiBtRkvPqOyLOsw12XsveYuY2ieP6uYDosoUegBJpSVxGwut9sxUgFFcll3VTOTqpUf8yGd8x1OmAkQ==} + engines: {node: '>= 20'} + peerDependencies: + '@octokit/core': '>=6' + + '@octokit/plugin-paginate-rest@14.0.0': + resolution: {integrity: sha512-fNVRE7ufJiAA3XUrha2omTA39M6IXIc6GIZLvlbsm8QOQCYvpq/LkMNGyFlB1d8hTDzsAXa3OKtybdMAYsV/fw==} + engines: {node: '>= 20'} + peerDependencies: + '@octokit/core': '>=6' + + '@octokit/plugin-rest-endpoint-methods@17.0.0': + resolution: {integrity: sha512-B5yCyIlOJFPqUUeiD0cnBJwWJO8lkJs5d8+ze9QDP6SvfiXSz1BF+91+0MeI1d2yxgOhU/O+CvtiZ9jSkHhFAw==} + engines: {node: '>= 20'} + peerDependencies: + '@octokit/core': '>=6' + + '@octokit/plugin-retry@8.1.0': + resolution: {integrity: sha512-O1FZgXeiGb2sowEr/hYTr6YunGdSAFWnr2fyW39Ah85H8O33ELASQxcvOFF5LE6Tjekcyu2ms4qAzJVhSaJxTw==} + engines: {node: '>= 20'} + peerDependencies: + '@octokit/core': '>=7' + + '@octokit/plugin-throttling@11.0.3': + resolution: {integrity: sha512-34eE0RkFCKycLl2D2kq7W+LovheM/ex3AwZCYN8udpi6bxsyjZidb2McXs69hZhLmJlDqTSP8cH+jSRpiaijBg==} + engines: {node: '>= 20'} + peerDependencies: + '@octokit/core': ^7.0.0 + + '@octokit/request-error@7.1.0': + resolution: {integrity: sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw==} + engines: {node: '>= 20'} + + '@octokit/request@10.0.8': + resolution: {integrity: sha512-SJZNwY9pur9Agf7l87ywFi14W+Hd9Jg6Ifivsd33+/bGUQIjNujdFiXII2/qSlN2ybqUHfp5xpekMEjIBTjlSw==} + engines: {node: '>= 20'} + + '@octokit/types@16.0.0': + resolution: {integrity: sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg==} + + '@octokit/webhooks-methods@6.0.0': + resolution: {integrity: sha512-MFlzzoDJVw/GcbfzVC1RLR36QqkTLUf79vLVO3D+xn7r0QgxnFoLZgtrzxiQErAjFUOdH6fas2KeQJ1yr/qaXQ==} + engines: {node: '>= 20'} + + '@octokit/webhooks@14.2.0': + resolution: {integrity: sha512-da6KbdNCV5sr1/txD896V+6W0iamFWrvVl8cHkBSPT+YlvmT3DwXa4jxZnQc+gnuTEqSWbBeoSZYTayXH9wXcw==} + engines: {node: '>= 20'} + + '@pinojs/redact@0.4.0': + resolution: {integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==} + + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + + '@protobufjs/aspromise@1.1.2': + resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} + + '@protobufjs/base64@1.1.2': + resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} + + '@protobufjs/codegen@2.0.4': + resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==} + + '@protobufjs/eventemitter@1.1.0': + resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==} + + '@protobufjs/fetch@1.1.0': + resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==} + + '@protobufjs/float@1.0.2': + resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} + + '@protobufjs/inquire@1.1.0': + resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==} + + '@protobufjs/path@1.1.2': + resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} + + '@protobufjs/pool@1.1.0': + resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} + + '@protobufjs/utf8@1.1.0': + resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} + + '@reflink/reflink-darwin-arm64@0.1.19': + resolution: {integrity: sha512-ruy44Lpepdk1FqDz38vExBY/PVUsjxZA+chd9wozjUH9JjuDT/HEaQYA6wYN9mf041l0yLVar6BCZuWABJvHSA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@reflink/reflink-darwin-x64@0.1.19': + resolution: {integrity: sha512-By85MSWrMZa+c26TcnAy8SDk0sTUkYlNnwknSchkhHpGXOtjNDUOxJE9oByBnGbeuIE1PiQsxDG3Ud+IVV9yuA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@reflink/reflink-linux-arm64-gnu@0.1.19': + resolution: {integrity: sha512-7P+er8+rP9iNeN+bfmccM4hTAaLP6PQJPKWSA4iSk2bNvo6KU6RyPgYeHxXmzNKzPVRcypZQTpFgstHam6maVg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@reflink/reflink-linux-arm64-musl@0.1.19': + resolution: {integrity: sha512-37iO/Dp6m5DDaC2sf3zPtx/hl9FV3Xze4xoYidrxxS9bgP3S8ALroxRK6xBG/1TtfXKTvolvp+IjrUU6ujIGmA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@reflink/reflink-linux-x64-gnu@0.1.19': + resolution: {integrity: sha512-jbI8jvuYCaA3MVUdu8vLoLAFqC+iNMpiSuLbxlAgg7x3K5bsS8nOpTRnkLF7vISJ+rVR8W+7ThXlXlUQ93ulkw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@reflink/reflink-linux-x64-musl@0.1.19': + resolution: {integrity: sha512-e9FBWDe+lv7QKAwtKOt6A2W/fyy/aEEfr0g6j/hWzvQcrzHCsz07BNQYlNOjTfeytrtLU7k449H1PI95jA4OjQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@reflink/reflink-win32-arm64-msvc@0.1.19': + resolution: {integrity: sha512-09PxnVIQcd+UOn4WAW73WU6PXL7DwGS6wPlkMhMg2zlHHG65F3vHepOw06HFCq+N42qkaNAc8AKIabWvtk6cIQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@reflink/reflink-win32-x64-msvc@0.1.19': + resolution: {integrity: sha512-E//yT4ni2SyhwP8JRjVGWr3cbnhWDiPLgnQ66qqaanjjnMiu3O/2tjCPQXlcGc/DEYofpDc9fvhv6tALQsMV9w==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@reflink/reflink@0.1.19': + resolution: {integrity: sha512-DmCG8GzysnCZ15bres3N5AHCmwBwYgp0As6xjhQ47rAUTUXxJiK+lLUxaGsX3hd/30qUpVElh05PbGuxRPgJwA==} + engines: {node: '>= 10'} + + '@rollup/rollup-android-arm-eabi@4.59.0': + resolution: {integrity: sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.59.0': + resolution: {integrity: sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.59.0': + resolution: {integrity: sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.59.0': + resolution: {integrity: sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.59.0': + resolution: {integrity: sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.59.0': + resolution: {integrity: sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.59.0': + resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm-musleabihf@4.59.0': + resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==} + cpu: [arm] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-arm64-gnu@4.59.0': + resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm64-musl@4.59.0': + resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-loong64-gnu@4.59.0': + resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==} + cpu: [loong64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-loong64-musl@4.59.0': + resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==} + cpu: [loong64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-ppc64-gnu@4.59.0': + resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-ppc64-musl@4.59.0': + resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==} + cpu: [ppc64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-riscv64-gnu@4.59.0': + resolution: {integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-riscv64-musl@4.59.0': + resolution: {integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-s390x-gnu@4.59.0': + resolution: {integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-gnu@4.59.0': + resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-musl@4.59.0': + resolution: {integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rollup/rollup-openbsd-x64@4.59.0': + resolution: {integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.59.0': + resolution: {integrity: sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.59.0': + resolution: {integrity: sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.59.0': + resolution: {integrity: sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.59.0': + resolution: {integrity: sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.59.0': + resolution: {integrity: sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==} + cpu: [x64] + os: [win32] + + '@silvia-odwyer/photon-node@0.3.4': + resolution: {integrity: sha512-bnly4BKB3KDTFxrUIcgCLbaeVVS8lrAkri1pEzskpmxu9MdfGQTy8b8EgcD83ywD3RPMsIulY8xJH5Awa+t9fA==} + + '@sinclair/typebox@0.34.48': + resolution: {integrity: sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==} + + '@slack/bolt@4.6.0': + resolution: {integrity: sha512-xPgfUs2+OXSugz54Ky07pA890+Qydk22SYToi8uGpXeHSt1JWwFJkRyd/9Vlg5I1AdfdpGXExDpwnbuN9Q/2dQ==} + 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==} + engines: {node: '>= 18', npm: '>= 8.6.0'} + + '@slack/oauth@3.0.4': + resolution: {integrity: sha512-+8H0g7mbrHndEUbYCP7uYyBCbwqmm3E6Mo3nfsDvZZW74zKk1ochfH/fWSvGInYNCVvaBUbg3RZBbTp0j8yJCg==} + engines: {node: '>=18', npm: '>=8.6.0'} + + '@slack/socket-mode@2.0.5': + resolution: {integrity: sha512-VaapvmrAifeFLAFaDPfGhEwwunTKsI6bQhYzxRXw7BSujZUae5sANO76WqlVsLXuhVtCVrBWPiS2snAQR2RHJQ==} + engines: {node: '>= 18', npm: '>= 8.6.0'} + + '@slack/types@2.20.0': + resolution: {integrity: sha512-PVF6P6nxzDMrzPC8fSCsnwaI+kF8YfEpxf3MqXmdyjyWTYsZQURpkK7WWUWvP5QpH55pB7zyYL9Qem/xSgc5VA==} + engines: {node: '>= 12.13.0', npm: '>= 6.12.0'} + + '@slack/web-api@7.14.1': + resolution: {integrity: sha512-RoygyteJeFswxDPJjUMESn9dldWVMD2xUcHHd9DenVavSfVC6FeVnSdDerOO7m8LLvw4Q132nQM4hX8JiF7dng==} + engines: {node: '>= 18', npm: '>= 8.6.0'} + + '@smithy/abort-controller@4.2.11': + resolution: {integrity: sha512-Hj4WoYWMJnSpM6/kchsm4bUNTL9XiSyhvoMb2KIq4VJzyDt7JpGHUZHkVNPZVC7YE1tf8tPeVauxpFBKGW4/KQ==} + engines: {node: '>=18.0.0'} + + '@smithy/config-resolver@4.4.10': + resolution: {integrity: sha512-IRTkd6ps0ru+lTWnfnsbXzW80A8Od8p3pYiZnW98K2Hb20rqfsX7VTlfUwhrcOeSSy68Gn9WBofwPuw3e5CCsg==} + engines: {node: '>=18.0.0'} + + '@smithy/core@3.23.9': + resolution: {integrity: sha512-1Vcut4LEL9HZsdpI0vFiRYIsaoPwZLjAxnVQDUMQK8beMS+EYPLDQCXtbzfxmM5GzSgjfe2Q9M7WaXwIMQllyQ==} + engines: {node: '>=18.0.0'} + + '@smithy/credential-provider-imds@4.2.11': + resolution: {integrity: sha512-lBXrS6ku0kTj3xLmsJW0WwqWbGQ6ueooYyp/1L9lkyT0M02C+DWwYwc5aTyXFbRaK38ojALxNixg+LxKSHZc0g==} + engines: {node: '>=18.0.0'} + + '@smithy/eventstream-codec@4.2.11': + resolution: {integrity: sha512-Sf39Ml0iVX+ba/bgMPxaXWAAFmHqYLTmbjAPfLPLY8CrYkRDEqZdUsKC1OwVMCdJXfAt0v4j49GIJ8DoSYAe6w==} + engines: {node: '>=18.0.0'} + + '@smithy/eventstream-serde-browser@4.2.11': + resolution: {integrity: sha512-3rEpo3G6f/nRS7fQDsZmxw/ius6rnlIpz4UX6FlALEzz8JoSxFmdBt0SZnthis+km7sQo6q5/3e+UJcuQivoXA==} + engines: {node: '>=18.0.0'} + + '@smithy/eventstream-serde-config-resolver@4.3.11': + resolution: {integrity: sha512-XeNIA8tcP/GDWnnKkO7qEm/bg0B/bP9lvIXZBXcGZwZ+VYM8h8k9wuDvUODtdQ2Wcp2RcBkPTCSMmaniVHrMlA==} + engines: {node: '>=18.0.0'} + + '@smithy/eventstream-serde-node@4.2.11': + resolution: {integrity: sha512-fzbCh18rscBDTQSCrsp1fGcclLNF//nJyhjldsEl/5wCYmgpHblv5JSppQAyQI24lClsFT0wV06N1Porn0IsEw==} + engines: {node: '>=18.0.0'} + + '@smithy/eventstream-serde-universal@4.2.11': + resolution: {integrity: sha512-MJ7HcI+jEkqoWT5vp+uoVaAjBrmxBtKhZTeynDRG/seEjJfqyg3SiqMMqyPnAMzmIfLaeJ/uiuSDP/l9AnMy/Q==} + engines: {node: '>=18.0.0'} + + '@smithy/fetch-http-handler@5.3.13': + resolution: {integrity: sha512-U2Hcfl2s3XaYjikN9cT4mPu8ybDbImV3baXR0PkVlC0TTx808bRP3FaPGAzPtB8OByI+JqJ1kyS+7GEgae7+qQ==} + engines: {node: '>=18.0.0'} + + '@smithy/hash-node@4.2.11': + resolution: {integrity: sha512-T+p1pNynRkydpdL015ruIoyPSRw9e/SQOWmSAMmmprfswMrd5Ow5igOWNVlvyVFZlxXqGmyH3NQwfwy8r5Jx0A==} + engines: {node: '>=18.0.0'} + + '@smithy/invalid-dependency@4.2.11': + resolution: {integrity: sha512-cGNMrgykRmddrNhYy1yBdrp5GwIgEkniS7k9O1VLB38yxQtlvrxpZtUVvo6T4cKpeZsriukBuuxfJcdZQc/f/g==} + engines: {node: '>=18.0.0'} + + '@smithy/is-array-buffer@2.2.0': + resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==} + engines: {node: '>=14.0.0'} + + '@smithy/is-array-buffer@4.2.2': + resolution: {integrity: sha512-n6rQ4N8Jj4YTQO3YFrlgZuwKodf4zUFs7EJIWH86pSCWBaAtAGBFfCM7Wx6D2bBJ2xqFNxGBSrUWswT3M0VJow==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-content-length@4.2.11': + resolution: {integrity: sha512-UvIfKYAKhCzr4p6jFevPlKhQwyQwlJ6IeKLDhmV1PlYfcW3RL4ROjNEDtSik4NYMi9kDkH7eSwyTP3vNJ/u/Dw==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-endpoint@4.4.23': + resolution: {integrity: sha512-UEFIejZy54T1EJn2aWJ45voB7RP2T+IRzUqocIdM6GFFa5ClZncakYJfcYnoXt3UsQrZZ9ZRauGm77l9UCbBLw==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-retry@4.4.40': + resolution: {integrity: sha512-YhEMakG1Ae57FajERdHNZ4ShOPIY7DsgV+ZoAxo/5BT0KIe+f6DDU2rtIymNNFIj22NJfeeI6LWIifrwM0f+rA==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-serde@4.2.12': + resolution: {integrity: sha512-W9g1bOLui7Xn5FABRVS0o3rXL0gfN37d/8I/W7i0N7oxjx9QecUmXEMSUMADTODwdtka9cN43t5BI2CodLJpng==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-stack@4.2.11': + resolution: {integrity: sha512-s+eenEPW6RgliDk2IhjD2hWOxIx1NKrOHxEwNUaUXxYBxIyCcDfNULZ2Mu15E3kwcJWBedTET/kEASPV1A1Akg==} + engines: {node: '>=18.0.0'} + + '@smithy/node-config-provider@4.3.11': + resolution: {integrity: sha512-xD17eE7kaLgBBGf5CZQ58hh2YmwK1Z0O8YhffwB/De2jsL0U3JklmhVYJ9Uf37OtUDLF2gsW40Xwwag9U869Gg==} + engines: {node: '>=18.0.0'} + + '@smithy/node-http-handler@4.4.14': + resolution: {integrity: sha512-DamSqaU8nuk0xTJDrYnRzZndHwwRnyj/n/+RqGGCcBKB4qrQem0mSDiWdupaNWdwxzyMU91qxDmHOCazfhtO3A==} + engines: {node: '>=18.0.0'} + + '@smithy/property-provider@4.2.11': + resolution: {integrity: sha512-14T1V64o6/ndyrnl1ze1ZhyLzIeYNN47oF/QU6P5m82AEtyOkMJTb0gO1dPubYjyyKuPD6OSVMPDKe+zioOnCg==} + engines: {node: '>=18.0.0'} + + '@smithy/protocol-http@5.3.11': + resolution: {integrity: sha512-hI+barOVDJBkNt4y0L2mu3Ugc0w7+BpJ2CZuLwXtSltGAAwCb3IvnalGlbDV/UCS6a9ZuT3+exd1WxNdLb5IlQ==} + engines: {node: '>=18.0.0'} + + '@smithy/querystring-builder@4.2.11': + resolution: {integrity: sha512-7spdikrYiljpket6u0up2Ck2mxhy7dZ0+TDd+S53Dg2DHd6wg+YNJrTCHiLdgZmEXZKI7LJZcwL3721ZRDFiqA==} + engines: {node: '>=18.0.0'} + + '@smithy/querystring-parser@4.2.11': + resolution: {integrity: sha512-nE3IRNjDltvGcoThD2abTozI1dkSy8aX+a2N1Rs55en5UsdyyIXgGEmevUL3okZFoJC77JgRGe99xYohhsjivQ==} + engines: {node: '>=18.0.0'} + + '@smithy/service-error-classification@4.2.11': + resolution: {integrity: sha512-HkMFJZJUhzU3HvND1+Yw/kYWXp4RPDLBWLcK1n+Vqw8xn4y2YiBhdww8IxhkQjP/QlZun5bwm3vcHc8AqIU3zw==} + engines: {node: '>=18.0.0'} + + '@smithy/shared-ini-file-loader@4.4.6': + resolution: {integrity: sha512-IB/M5I8G0EeXZTHsAxpx51tMQ5R719F3aq+fjEB6VtNcCHDc0ajFDIGDZw+FW9GxtEkgTduiPpjveJdA/CX7sw==} + engines: {node: '>=18.0.0'} + + '@smithy/signature-v4@5.3.11': + resolution: {integrity: sha512-V1L6N9aKOBAN4wEHLyqjLBnAz13mtILU0SeDrjOaIZEeN6IFa6DxwRt1NNpOdmSpQUfkBj0qeD3m6P77uzMhgQ==} + engines: {node: '>=18.0.0'} + + '@smithy/smithy-client@4.12.3': + resolution: {integrity: sha512-7k4UxjSpHmPN2AxVhvIazRSzFQjWnud3sOsXcFStzagww17j1cFQYqTSiQ8xuYK3vKLR1Ni8FzuT3VlKr3xCNw==} + engines: {node: '>=18.0.0'} + + '@smithy/types@4.13.0': + resolution: {integrity: sha512-COuLsZILbbQsdrwKQpkkpyep7lCsByxwj7m0Mg5v66/ZTyenlfBc40/QFQ5chO0YN/PNEH1Bi3fGtfXPnYNeDw==} + engines: {node: '>=18.0.0'} + + '@smithy/url-parser@4.2.11': + resolution: {integrity: sha512-oTAGGHo8ZYc5VZsBREzuf5lf2pAurJQsccMusVZ85wDkX66ojEc/XauiGjzCj50A61ObFTPe6d7Pyt6UBYaing==} + 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==} + 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.39': + resolution: {integrity: sha512-ui7/Ho/+VHqS7Km2wBw4/Ab4RktoiSshgcgpJzC4keFPs6tLJS4IQwbeahxQS3E/w98uq6E1mirCH/id9xIXeQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-defaults-mode-node@4.2.42': + resolution: {integrity: sha512-QDA84CWNe8Akpj15ofLO+1N3Rfg8qa2K5uX0y6HnOp4AnRYRgWrKx/xzbYNbVF9ZsyJUYOfcoaN3y93wA/QJ2A==} + engines: {node: '>=18.0.0'} + + '@smithy/util-endpoints@3.3.2': + resolution: {integrity: sha512-+4HFLpE5u29AbFlTdlKIT7jfOzZ8PDYZKTb3e+AgLz986OYwqTourQ5H+jg79/66DB69Un1+qKecLnkZdAsYcA==} + 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.11': + resolution: {integrity: sha512-r3dtF9F+TpSZUxpOVVtPfk09Rlo4lT6ORBqEvX3IBT6SkQAdDSVKR5GcfmZbtl7WKhKnmb3wbDTQ6ibR2XHClw==} + engines: {node: '>=18.0.0'} + + '@smithy/util-retry@4.2.11': + resolution: {integrity: sha512-XSZULmL5x6aCTTii59wJqKsY1l3eMIAomRAccW7Tzh9r8s7T/7rdo03oektuH5jeYRlJMPcNP92EuRDvk9aXbw==} + engines: {node: '>=18.0.0'} + + '@smithy/util-stream@4.5.17': + resolution: {integrity: sha512-793BYZ4h2JAQkNHcEnyFxDTcZbm9bVybD0UV/LEWmZ5bkTms7JqjfrLMi2Qy0E5WFcCzLwCAPgcvcvxoeALbAQ==} + 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'} + + '@snazzah/davey-android-arm-eabi@0.1.10': + resolution: {integrity: sha512-7bwHxSNEI2wVXOT6xnmpnO9SHb2xwAnf9oEdL45dlfVHTgU1Okg5rwGwRvZ2aLVFFbTyecfC8EVZyhpyTkjLSw==} + engines: {node: '>= 10'} + cpu: [arm] + os: [android] + + '@snazzah/davey-android-arm64@0.1.10': + resolution: {integrity: sha512-68WUf2LQwQTP9MgPcCqTWwJztJSIk0keGfF2Y/b+MihSDh29fYJl7C0rbz69aUrVCvCC2lYkB/46P8X1kBz7yg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + + '@snazzah/davey-darwin-arm64@0.1.10': + resolution: {integrity: sha512-nYC+DWCGUC1jUGEenCNQE/jJpL/02m0ebY/NvTCQbul5ktI/ShVzgA3kzssEhZvhf6jbH048Rs39wDhp/b24Jg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@snazzah/davey-darwin-x64@0.1.10': + resolution: {integrity: sha512-0q5Rrcs+O9sSSnPX+A3R3djEQs2nTAtMe5N3lApO6lZas/QNMl6wkEWCvTbDc2cfAYBMSk2jgc1awlRXi4LX3Q==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@snazzah/davey-freebsd-x64@0.1.10': + resolution: {integrity: sha512-/Gq5YDD6Oz8iBqVJLswUnetCv9JCRo1quYX5ujzpAG8zPCNItZo4g4h5p9C+h4Yoay2quWBYhoaVqQKT96bm8g==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + + '@snazzah/davey-linux-arm-gnueabihf@0.1.10': + resolution: {integrity: sha512-0Z7Vrt0WIbgxws9CeHB9qlueYJlvltI44rUuZmysdi70UcHGxlr7nE3MnzYCr9nRWRegohn8EQPWHMKMDJH2GA==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@snazzah/davey-linux-arm64-gnu@0.1.10': + resolution: {integrity: sha512-xhZQycn4QB+qXhqm/QmZ+kb9MHMXcbjjoPfvcIL4WMQXFG/zUWHW8EiBk7ZTEGMOpeab3F9D1+MlgumglYByUQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@snazzah/davey-linux-arm64-musl@0.1.10': + resolution: {integrity: sha512-pudzQCP9rZItwW4qHHvciMwtNd9kWH4l73g6Id1LRpe6sc8jiFBV7W+YXITj2PZbI0by6XPfkRP6Dk5IkGOuAw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@snazzah/davey-linux-x64-gnu@0.1.10': + resolution: {integrity: sha512-DC8qRmk+xJEFNqjxKB46cETKeDQqgUqE5p39KXS2k6Vl/XTi8pw8pXOxrPfYte5neoqlWAVQzbxuLnwpyRJVEQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@snazzah/davey-linux-x64-musl@0.1.10': + resolution: {integrity: sha512-wPR5/2QmsF7sR0WUaCwbk4XI3TLcxK9PVK8mhgcAYyuRpbhcVgNGWXs8ulcyMSXve5pFRJAFAuMTGCEb014peg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@snazzah/davey-wasm32-wasi@0.1.10': + resolution: {integrity: sha512-SfQavU+eKTDbRmPeLRodrVSfsWq25PYTmH1nIZW3B27L6IkijzjXZZuxiU1ZG1gdI5fB7mwXrOTtx34t+vAG7Q==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@snazzah/davey-win32-arm64-msvc@0.1.10': + resolution: {integrity: sha512-Raafk53smYs67wZCY9bQXHXzbaiRMS5QCdjTdin3D9fF5A06T/0Zv1z7/YnaN+O3GSL/Ou3RvynF7SziToYiFQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@snazzah/davey-win32-ia32-msvc@0.1.10': + resolution: {integrity: sha512-pAs43l/DiZ+icqBwxIwNePzuYxFM1ZblVuf7t6vwwSLxvova7vnREnU7qDVjbc5/YTUHOsqYy3S6TpZMzDo2lw==} + engines: {node: '>= 10'} + cpu: [ia32] + os: [win32] + + '@snazzah/davey-win32-x64-msvc@0.1.10': + resolution: {integrity: sha512-kr6148VVBoUT4CtD+5hYshTFRny7R/xQZxXFhFc0fYjtmdMVM8Px9M91olg1JFNxuNzdfMfTufR58Q3wfBocug==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@snazzah/davey@0.1.10': + resolution: {integrity: sha512-J5f7vV5/tnj0xGnqufFRd6qiWn3FcR3iXjpjpEmO2Ok+Io0AASkMaZ3I39TsL45as0Qo5bq9wWuamFQ77PjJ+g==} + engines: {node: '>= 10'} + + '@tinyhttp/content-disposition@2.2.4': + resolution: {integrity: sha512-5Kc5CM2Ysn3vTTArBs2vESUt0AQiWZA86yc1TI3B+lxXmtEq133C1nxXNOgnzhrivdPZIh3zLj5gDnZjoLL5GA==} + engines: {node: '>=12.17.0'} + + '@tokenizer/inflate@0.4.1': + resolution: {integrity: sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==} + engines: {node: '>=18'} + + '@tokenizer/token@0.3.0': + resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} + + '@tootallnate/quickjs-emscripten@0.23.0': + resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==} + + '@tybys/wasm-util@0.10.1': + resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} + + '@types/aws-lambda@8.10.161': + resolution: {integrity: sha512-rUYdp+MQwSFocxIOcSsYSF3YYYC/uUpMbCY/mbO21vGqfrEYvNSoPyKYDj6RhXXpPfS0KstW9RwG3qXh9sL7FQ==} + + '@types/body-parser@1.19.6': + resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} + + '@types/bun@1.3.9': + resolution: {integrity: sha512-KQ571yULOdWJiMH+RIWIOZ7B2RXQGpL1YQrBtLIV3FqDcCu6FsbFUBwhdKUlCKUpS3PJDsHlJ1QKlpxoVR+xtw==} + + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + + '@types/connect@3.4.38': + resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/express-serve-static-core@5.1.1': + resolution: {integrity: sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==} + + '@types/express@5.0.6': + resolution: {integrity: sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==} + + '@types/http-errors@2.0.5': + resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==} + + '@types/jsonwebtoken@9.0.10': + resolution: {integrity: sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==} + + '@types/long@4.0.2': + resolution: {integrity: sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==} + + '@types/mime-types@2.1.4': + resolution: {integrity: sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w==} + + '@types/ms@2.1.0': + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + + '@types/node@10.17.60': + resolution: {integrity: sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==} + + '@types/node@24.12.0': + resolution: {integrity: sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ==} + + '@types/node@25.3.5': + resolution: {integrity: sha512-oX8xrhvpiyRCQkG1MFchB09f+cXftgIXb3a7UUa4Y3wpmZPw5tyZGTLWhlESOLq1Rq6oDlc8npVU2/9xiCuXMA==} + + '@types/qs@6.15.0': + resolution: {integrity: sha512-JawvT8iBVWpzTrz3EGw9BTQFg3BQNmwERdKE22vlTxawwtbyUSlMppvZYKLZzB5zgACXdXxbD3m1bXaMqP/9ow==} + + '@types/range-parser@1.2.7': + resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} + + '@types/retry@0.12.0': + resolution: {integrity: sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==} + + '@types/send@1.2.1': + resolution: {integrity: sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==} + + '@types/serve-static@2.2.0': + resolution: {integrity: sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==} + + '@types/ws@8.18.1': + resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} + + '@types/yauzl@2.10.3': + resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} + + '@vitest/expect@3.2.4': + resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} + + '@vitest/mocker@3.2.4': + resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==} + peerDependencies: + msw: ^2.4.9 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@3.2.4': + resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} + + '@vitest/runner@3.2.4': + resolution: {integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==} + + '@vitest/snapshot@3.2.4': + resolution: {integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==} + + '@vitest/spy@3.2.4': + resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} + + '@vitest/utils@3.2.4': + resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} + + '@whiskeysockets/baileys@7.0.0-rc.9': + resolution: {integrity: sha512-YFm5gKXfDP9byCXCW3OPHKXLzrAKzolzgVUlRosHHgwbnf2YOO3XknkMm6J7+F0ns8OA0uuSBhgkRHTDtqkacw==} + engines: {node: '>=20.0.0'} + peerDependencies: + audio-decode: ^2.1.3 + jimp: ^1.6.0 + link-preview-js: ^3.0.0 + sharp: '*' + peerDependenciesMeta: + audio-decode: + optional: true + jimp: + optional: true + 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 + + abbrev@1.1.1: + resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} + + abort-controller@3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} + + accepts@2.0.0: + resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} + engines: {node: '>= 0.6'} + + agent-base@6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} + + ajv-formats@3.0.1: + resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv@8.18.0: + resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} + + ansi-escapes@6.2.1: + resolution: {integrity: sha512-4nJ3yixlEthEJ9Rk4vPcdBRkZvQZlYyu8j4/Mqz5sgIkddmEnH2Yj2ZrnP9S3tQOvSNRUIgVNF/1yPpRAGNRig==} + engines: {node: '>=14.16'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} + engines: {node: '>=12'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} + engines: {node: '>=12'} + + any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + + aproba@2.1.0: + resolution: {integrity: sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew==} + + are-we-there-yet@2.0.0: + resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==} + engines: {node: '>=10'} + deprecated: This package is no longer supported. + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + + ast-types@0.13.4: + resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==} + engines: {node: '>=4'} + + async-mutex@0.5.0: + resolution: {integrity: sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==} + + async-retry@1.3.3: + resolution: {integrity: sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + atomic-sleep@1.0.0: + resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} + engines: {node: '>=8.0.0'} + + axios@1.13.6: + resolution: {integrity: sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + basic-ftp@5.2.0: + resolution: {integrity: sha512-VoMINM2rqJwJgfdHq6RiUudKt2BV+FY5ZFezP/ypmwayk68+NzzAQy4XXLlqsGD4MCzq3DrmNFD/uUmBJuGoXw==} + engines: {node: '>=10.0.0'} + + before-after-hook@4.0.0: + resolution: {integrity: sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==} + + bignumber.js@9.3.1: + resolution: {integrity: sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==} + + body-parser@2.2.2: + resolution: {integrity: sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==} + engines: {node: '>=18'} + + boolbase@1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + + bottleneck@2.19.5: + resolution: {integrity: sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==} + + bowser@2.14.1: + resolution: {integrity: sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==} + + brace-expansion@1.1.12: + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + + brace-expansion@5.0.4: + resolution: {integrity: sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==} + engines: {node: 18 || 20 || >=22} + + buffer-crc32@0.2.13: + resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} + + buffer-equal-constant-time@1.0.1: + resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} + + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + + bun-types@1.3.9: + resolution: {integrity: sha512-+UBWWOakIP4Tswh0Bt0QD0alpTY8cb5hvgiYeWCMet9YukHbzuruIEeXC2D7nMJPB12kbh8C7XJykSexEqGKJg==} + + bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + + cacheable@2.3.3: + resolution: {integrity: sha512-iffYMX4zxKp54evOH27fm92hs+DeC1DhXmNVN8Tr94M/iZIV42dqTHSR2Ik4TOSPyOAwKr7Yu3rN9ALoLkbWyQ==} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + chai@5.3.3: + resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} + engines: {node: '>=18'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chalk@5.6.2: + resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + check-error@2.1.3: + resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==} + engines: {node: '>= 16'} + + chmodrp@1.0.2: + resolution: {integrity: sha512-TdngOlFV1FLTzU0o1w8MB6/BFywhtLC0SzRTGJU7T9lmdjlCWeMRt1iVo0Ki+ldwNk0BqNiKoc8xpLZEQ8mY1w==} + + chokidar@5.0.0: + resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} + engines: {node: '>= 20.19.0'} + + chownr@2.0.0: + resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} + engines: {node: '>=10'} + + chownr@3.0.0: + resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} + engines: {node: '>=18'} + + ci-info@4.4.0: + resolution: {integrity: sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==} + engines: {node: '>=8'} + + cli-cursor@5.0.0: + resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} + engines: {node: '>=18'} + + cli-highlight@2.1.11: + resolution: {integrity: sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==} + engines: {node: '>=8.0.0', npm: '>=5.0.0'} + hasBin: true + + cli-spinners@2.9.2: + resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} + engines: {node: '>=6'} + + cli-spinners@3.4.0: + resolution: {integrity: sha512-bXfOC4QcT1tKXGorxL3wbJm6XJPDqEnij2gQ2m7ESQuE+/z9YFIWnl/5RpTiKWbMq3EVKR4fRLJGn6DVfu0mpw==} + engines: {node: '>=18.20'} + + cliui@7.0.4: + resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + cmake-js@8.0.0: + resolution: {integrity: sha512-YbUP88RDwCvoQkZhRtGURYm9RIpWdtvZuhT87fKNoLjk8kIFIFeARpKfuZQGdwfH99GZpUmqSfcDrK62X7lTgg==} + engines: {node: ^20.17.0 || >=22.9.0} + hasBin: true + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + color-support@1.1.3: + resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} + hasBin: true + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + commander@10.0.1: + resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} + engines: {node: '>=14'} + + commander@14.0.3: + resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==} + engines: {node: '>=20'} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + console-control-strings@1.1.0: + resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} + + content-disposition@1.0.1: + resolution: {integrity: sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==} + engines: {node: '>=18'} + + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + + cookie-signature@1.2.2: + resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} + engines: {node: '>=6.6.0'} + + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} + + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + + croner@10.0.1: + resolution: {integrity: sha512-ixNtAJndqh173VQ4KodSdJEI6nuioBWI0V1ITNKhZZsO0pEMoDxz539T4FTTbSZ/xIOSuDnzxLVRqBVSvPNE2g==} + engines: {node: '>=18.0'} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + css-select@5.2.2: + resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==} + + css-what@6.2.2: + resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} + engines: {node: '>= 6'} + + cssom@0.5.0: + resolution: {integrity: sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==} + + curve25519-js@0.0.4: + resolution: {integrity: sha512-axn2UMEnkhyDUPWOwVKBMVIzSQy2ejH2xRGy1wq81dqRwApXfIzfbE3hIX0ZRFBIihf/KDqK158DLwESu4AK1w==} + + data-uri-to-buffer@4.0.1: + resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} + engines: {node: '>= 12'} + + data-uri-to-buffer@6.0.2: + resolution: {integrity: sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==} + engines: {node: '>= 14'} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-eql@5.0.2: + resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} + engines: {node: '>=6'} + + deep-extend@0.6.0: + resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} + engines: {node: '>=4.0.0'} + + 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'} + + delegates@1.0.0: + resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} + + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + diff@8.0.3: + resolution: {integrity: sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==} + engines: {node: '>=0.3.1'} + + discord-api-types@0.38.37: + resolution: {integrity: sha512-Cv47jzY1jkGkh5sv0bfHYqGgKOWO1peOrGMkDFM4UmaGMOTgOW8QSexhvixa9sVOiz8MnVOBryWYyw/CEVhj7w==} + + discord-api-types@0.38.41: + resolution: {integrity: sha512-yMECyR8j9c2fVTvCQ+Qc24pweYFIZk/XoxDOmt1UvPeSw5tK6gXBd/2hhP+FEAe9Y6ny8pRMaf618XDK4U53OQ==} + + dom-serializer@2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + + domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + + domhandler@5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} + + domutils@3.2.2: + resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} + + dotenv@17.3.1: + resolution: {integrity: sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==} + engines: {node: '>=12'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + ecdsa-sig-formatter@1.0.11: + resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} + + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + + emoji-regex@10.6.0: + resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + + end-of-stream@1.4.5: + resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + entities@7.0.1: + resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} + engines: {node: '>=0.12'} + + env-var@7.5.0: + resolution: {integrity: sha512-mKZOzLRN0ETzau2W2QXefbFjo5EF4yWq28OyKb9ICdeNhHJlOE/pHHnz4hdYJ9cNZXcJHo5xN4OT4pzuSHSNvA==} + engines: {node: '>=10'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + esbuild@0.27.3: + resolution: {integrity: sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + + 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'} + + event-target-shim@5.0.1: + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} + engines: {node: '>=6'} + + eventemitter3@4.0.7: + resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} + + eventemitter3@5.0.4: + resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} + + expect-type@1.3.0: + resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} + engines: {node: '>=12.0.0'} + + express@5.2.1: + resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==} + engines: {node: '>= 18'} + + extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + + extract-zip@2.0.1: + resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} + engines: {node: '>= 10.17.0'} + hasBin: true + + fast-content-type-parse@3.0.0: + resolution: {integrity: sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg==} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-uri@3.1.0: + resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} + + fast-xml-builder@1.0.0: + resolution: {integrity: sha512-fpZuDogrAgnyt9oDDz+5DBz0zgPdPZz6D4IR7iESxRXElrlGTRkHJ9eEt+SACRJwT0FNFrt71DFQIUFBJfX/uQ==} + + fast-xml-parser@5.4.1: + resolution: {integrity: sha512-BQ30U1mKkvXQXXkAGcuyUA/GA26oEB7NzOtsxCDtyu62sjGw5QraKFhx2Em3WQNjPw9PG6MQ9yuIIgkSDfGu5A==} + hasBin: true + + fd-slicer@1.1.0: + resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fetch-blob@3.2.0: + resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} + engines: {node: ^12.20 || >= 14.13} + + file-type@21.3.0: + resolution: {integrity: sha512-8kPJMIGz1Yt/aPEwOsrR97ZyZaD1Iqm8PClb1nYFclUCkBi0Ma5IsYNQzvSFS9ib51lWyIw5mIT9rWzI/xjpzA==} + engines: {node: '>=20'} + + filename-reserved-regex@3.0.0: + resolution: {integrity: sha512-hn4cQfU6GOT/7cFHXBqeBg2TbrMBgdD0kcjLhvSQYYwm3s4B6cjvBfb7nBALJLAXqmU5xajSa7X2NnUud/VCdw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + filenamify@6.0.0: + resolution: {integrity: sha512-vqIlNogKeyD3yzrm0yhRMQg8hOVwYcYRfjEoODd49iCprMn4HL85gK3HcykQE53EPIpX3HcAbGA5ELQv216dAQ==} + engines: {node: '>=16'} + + finalhandler@2.1.1: + resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==} + engines: {node: '>= 18.0.0'} + + follow-redirects@1.15.11: + resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} + engines: {node: '>=14'} + + form-data@4.0.5: + resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} + engines: {node: '>= 6'} + + formdata-polyfill@4.0.10: + resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} + engines: {node: '>=12.20.0'} + + forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + + fresh@2.0.0: + resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} + engines: {node: '>= 0.8'} + + fs-extra@11.3.4: + resolution: {integrity: sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA==} + engines: {node: '>=14.14'} + + fs-minipass@2.1.0: + resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} + engines: {node: '>= 8'} + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + gauge@3.0.2: + resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==} + engines: {node: '>=10'} + deprecated: This package is no longer supported. + + gaxios@7.1.3: + resolution: {integrity: sha512-YGGyuEdVIjqxkxVH1pUTMY/XtmmsApXrCVv5EU25iX6inEPbV+VakJfLealkBtJN69AQmh1eGOdCl9Sm1UP6XQ==} + engines: {node: '>=18'} + + gcp-metadata@8.1.2: + resolution: {integrity: sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==} + engines: {node: '>=18'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-east-asian-width@1.5.0: + resolution: {integrity: sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==} + engines: {node: '>=18'} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-stream@5.2.0: + resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} + engines: {node: '>=8'} + + get-uri@6.0.5: + resolution: {integrity: sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==} + engines: {node: '>= 14'} + + glob@10.5.0: + resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + hasBin: true + + glob@13.0.6: + resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==} + engines: {node: 18 || 20 || >=22} + + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + + google-auth-library@10.6.1: + resolution: {integrity: sha512-5awwuLrzNol+pFDmKJd0dKtZ0fPLAtoA5p7YO4ODsDu6ONJUVqbYwvv8y2ZBO5MBNp9TJXigB19710kYpBPdtA==} + engines: {node: '>=18'} + + google-logging-utils@1.1.3: + resolution: {integrity: sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==} + engines: {node: '>=14'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + grammy@1.41.1: + resolution: {integrity: sha512-wcHAQ1e7svL3fJMpDchcQVcWUmywhuepOOjHUHmMmWAwUJEIyK5ea5sbSjZd+Gy1aMpZeP8VYJa+4tP+j1YptQ==} + engines: {node: ^12.20.0 || >=14.13.1} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + has-unicode@2.0.1: + resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} + + hashery@1.5.0: + resolution: {integrity: sha512-nhQ6ExaOIqti2FDWoEMWARUqIKyjr2VcZzXShrI+A3zpeiuPWzx6iPftt44LhP74E5sW36B75N6VHbvRtpvO6Q==} + engines: {node: '>=20'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + highlight.js@10.7.3: + resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} + + hono@4.12.5: + resolution: {integrity: sha512-3qq+FUBtlTHhtYxbxheZgY8NIFnkkC/MR8u5TTsr7YZ3wixryQ3cCwn3iZbg8p8B88iDBBAYSfZDS75t8MN7Vg==} + engines: {node: '>=16.9.0'} + + 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==} + engines: {node: ^20.17.0 || >=22.9.0} + + html-escaper@3.0.3: + resolution: {integrity: sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==} + + htmlparser2@10.1.0: + resolution: {integrity: sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==} + + http-errors@2.0.1: + resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} + engines: {node: '>= 0.8'} + + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + + 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'} + + iconv-lite@0.7.2: + resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} + engines: {node: '>=0.10.0'} + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + + immediate@3.0.6: + resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} + + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + 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'} + + ipaddr.js@2.3.0: + resolution: {integrity: sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg==} + engines: {node: '>= 10'} + + ipull@3.9.5: + resolution: {integrity: sha512-5w/yZB5lXmTfsvNawmvkCjYo4SJNuKQz/av8TC1UiOyfOHyaM+DReqbpU2XpWYfmY+NIUbRRH8PUAWsxaS+IfA==} + engines: {node: '>=18.0.0'} + hasBin: true + + is-electron@2.2.2: + resolution: {integrity: sha512-FO/Rhvz5tuw4MCWkpMzHFKWD2LsfHzIb7i6MdPYZ/KW7AlxawyLkqdy+jPZP1WubqEADE3O4FUENlJHDfQASRg==} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-fullwidth-code-point@5.1.0: + resolution: {integrity: sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==} + engines: {node: '>=18'} + + is-interactive@2.0.0: + resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} + engines: {node: '>=12'} + + is-promise@4.0.0: + resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + + is-unicode-supported@2.1.0: + resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} + engines: {node: '>=18'} + + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + isexe@4.0.0: + resolution: {integrity: sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==} + engines: {node: '>=20'} + + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + + jiti@2.6.1: + resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} + hasBin: true + + js-tokens@9.0.1: + resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} + + json-bigint@1.0.0: + resolution: {integrity: sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==} + + json-schema-to-ts@3.1.1: + resolution: {integrity: sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==} + engines: {node: '>=16'} + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + json-with-bigint@3.5.7: + resolution: {integrity: sha512-7ei3MdAI5+fJPVnKlW77TKNKwQ5ppSzWvhPuSuINT/GYW9ZOC1eRKOuhV9yHG5aEsUPj9BBx5JIekkmoLHxZOw==} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsonfile@6.2.0: + resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} + + jsonwebtoken@9.0.3: + resolution: {integrity: sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==} + engines: {node: '>=12', npm: '>=6'} + + jszip@3.10.1: + resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==} + + jwa@2.0.1: + resolution: {integrity: sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==} + + jws@4.0.1: + resolution: {integrity: sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==} + + keyv@5.6.0: + resolution: {integrity: sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==} + + koffi@2.15.1: + resolution: {integrity: sha512-mnc0C0crx/xMSljb5s9QbnLrlFHprioFO1hkXyuSuO/QtbpLDa0l/uM21944UfQunMKmp3/r789DTDxVyyH6aA==} + + lie@3.3.0: + resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} + + lifecycle-utils@2.1.0: + resolution: {integrity: sha512-AnrXnE2/OF9PHCyFg0RSqsnQTzV991XaZA/buhFDoc58xU7rhSCDgCz/09Lqpsn4MpoPHt7TRAXV1kWZypFVsA==} + + lifecycle-utils@3.1.1: + resolution: {integrity: sha512-gNd3OvhFNjHykJE3uGntz7UuPzWlK9phrIdXxU9Adis0+ExkwnZibfxCJWiWWZ+a6VbKiZrb+9D9hCQWd4vjTg==} + + linkedom@0.18.12: + resolution: {integrity: sha512-jalJsOwIKuQJSeTvsgzPe9iJzyfVaEJiEXl+25EkKevsULHvMJzpNqwvj1jOESWdmgKDiXObyjOYwlUqG7wo1Q==} + engines: {node: '>=16'} + peerDependencies: + canvas: '>= 2' + peerDependenciesMeta: + canvas: + optional: true + + linkify-it@5.0.0: + resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} + + lodash.debounce@4.0.8: + resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + + lodash.identity@3.0.0: + resolution: {integrity: sha512-AupTIzdLQxJS5wIYUQlgGyk2XRTfGXA+MCghDHqZk0pzUNYvd3EESS6dkChNauNYVIutcb0dfHw1ri9Q1yPV8Q==} + + lodash.includes@4.3.0: + resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} + + lodash.isboolean@3.0.3: + resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} + + lodash.isinteger@4.0.4: + resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==} + + lodash.isnumber@3.0.3: + resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==} + + lodash.isplainobject@4.0.6: + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + + lodash.isstring@4.0.1: + resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + lodash.once@4.1.1: + resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} + + lodash.pickby@4.6.0: + resolution: {integrity: sha512-AZV+GsS/6ckvPOVQPXSiFFacKvKB4kOQu6ynt9wz0F3LO4R9Ij4K1ddYsIytDpSgLz88JHd9P+oaLeej5/Sl7Q==} + + log-symbols@7.0.1: + 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==} + + loupe@3.2.1: + resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} + + lowdb@7.0.1: + resolution: {integrity: sha512-neJAj8GwF0e8EpycYIDFqEPcx9Qz4GUho20jWFR7YiFeXzF1YMLdxB36PypcTSPMA+4+LvgyMacYhlr18Zlymw==} + engines: {node: '>=18'} + + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + + lru-cache@11.2.6: + resolution: {integrity: sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==} + engines: {node: 20 || >=22} + + lru-cache@7.18.3: + resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} + engines: {node: '>=12'} + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + make-dir@3.1.0: + resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} + engines: {node: '>=8'} + + markdown-it@14.1.1: + resolution: {integrity: sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==} + hasBin: true + + marked@15.0.12: + resolution: {integrity: sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==} + engines: {node: '>= 18'} + hasBin: true + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + mdurl@2.0.0: + resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} + + media-typer@1.1.0: + resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} + engines: {node: '>= 0.8'} + + merge-descriptors@2.0.0: + resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} + engines: {node: '>=18'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-db@1.54.0: + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mime-types@3.0.2: + resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} + engines: {node: '>=18'} + + mimic-function@5.0.1: + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} + engines: {node: '>=18'} + + minimatch@10.2.4: + resolution: {integrity: sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==} + engines: {node: 18 || 20 || >=22} + + minimatch@3.1.5: + resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} + + minimatch@9.0.9: + resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==} + engines: {node: '>=16 || 14 >=14.17'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + minipass@3.3.6: + resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} + engines: {node: '>=8'} + + minipass@5.0.0: + resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} + engines: {node: '>=8'} + + minipass@7.1.3: + resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} + engines: {node: '>=16 || 14 >=14.17'} + + minizlib@2.1.2: + resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} + engines: {node: '>= 8'} + + minizlib@3.1.0: + resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==} + engines: {node: '>= 18'} + + mkdirp@1.0.4: + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} + hasBin: true + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + music-metadata@11.12.1: + resolution: {integrity: sha512-j++ltLxHDb5VCXET9FzQ8bnueiLHwQKgCO7vcbkRH/3F7fRjPkv6qncGEJ47yFhmemcYtgvsOAlcQ1dRBTkDjg==} + engines: {node: '>=18'} + + mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + nanoid@5.1.6: + resolution: {integrity: sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg==} + engines: {node: ^18 || >=20} + hasBin: true + + negotiator@1.0.0: + 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.6.0: + resolution: {integrity: sha512-gBVjCaqDlRUk0EwoPNKzIr9KkS9041G/q31IBShPs1Xz6UTA+EXdZADbzqAJQrpDRq71CIMnOP5VMut3SL0z5Q==} + engines: {node: ^18 || ^20 || >= 21} + + node-api-headers@1.8.0: + resolution: {integrity: sha512-jfnmiKWjRAGbdD1yQS28bknFM1tbHC1oucyuMPjmkEs+kpiu76aRs40WlTmBmyEgzDM76ge1DQ7XJ3R5deiVjQ==} + + node-domexception@1.0.0: + resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} + engines: {node: '>=10.5.0'} + deprecated: Use your platform's native DOMException instead + + node-edge-tts@1.2.10: + resolution: {integrity: sha512-bV2i4XU54D45+US0Zm1HcJRkifuB3W438dWyuJEHLQdKxnuqlI1kim2MOvR6Q3XUQZvfF9PoDyR1Rt7aeXhPdQ==} + hasBin: true + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + node-fetch@3.3.2: + resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + node-llama-cpp@3.16.2: + resolution: {integrity: sha512-ovhuTaXSWfcoyfI8ljWxO2Rg63mNxqQQAbDGkXRhlgsL7UjPqm2Nsy1bTNa0ZaQRg3vezG4agnCJTImrICY/0A==} + engines: {node: '>=20.0.0'} + hasBin: true + peerDependencies: + typescript: '>=5.0.0' + peerDependenciesMeta: + typescript: + optional: true + + node-readable-to-web-readable-stream@0.4.2: + resolution: {integrity: sha512-/cMZNI34v//jUTrI+UIo4ieHAB5EZRY/+7OmXZgBxaWBMcW2tGdceIw06RFxWxrKZ5Jp3sI2i5TsRo+CBhtVLQ==} + + nopt@5.0.0: + resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==} + engines: {node: '>=6'} + hasBin: true + + npmlog@5.0.1: + resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==} + deprecated: This package is no longer supported. + + nth-check@2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + octokit@5.0.5: + resolution: {integrity: sha512-4+/OFSqOjoyULo7eN7EA97DE0Xydj/PW5aIckxqQIoFjFwqXKuFCvXUJObyJfBF9Khu4RL/jlDRI9FPaMGfPnw==} + engines: {node: '>= 20'} + + on-exit-leak-free@2.1.2: + resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} + engines: {node: '>=14.0.0'} + + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + onetime@7.0.0: + resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} + engines: {node: '>=18'} + + openai@6.10.0: + resolution: {integrity: sha512-ITxOGo7rO3XRMiKA5l7tQ43iNNu+iXGFAcf2t+aWVzzqRaS0i7m1K2BhxNdaveB+5eENhO0VY1FkiZzhBk4v3A==} + hasBin: true + peerDependencies: + ws: ^8.18.0 + zod: ^3.25 || ^4.0 + peerDependenciesMeta: + ws: + optional: true + zod: + optional: true + + openclaw@2026.3.2: + resolution: {integrity: sha512-Gkqx24m7PF1DUXPI968DuC9n52lTZ5hI3X5PIi0HosC7J7d6RLkgVppj1mxvgiQAWMp41E41elvoi/h4KBjFcQ==} + engines: {node: '>=22.12.0'} + hasBin: true + peerDependencies: + '@napi-rs/canvas': ^0.1.89 + node-llama-cpp: 3.16.2 + + opusscript@0.1.1: + resolution: {integrity: sha512-mL0fZZOUnXdZ78woRXp18lApwpp0lF5tozJOD1Wut0dgrA9WuQTgSels/CSmFleaAZrJi/nci5KOVtbuxeWoQA==} + + ora@9.3.0: + resolution: {integrity: sha512-lBX72MWFduWEf7v7uWf5DHp9Jn5BI8bNPGuFgtXMmr2uDz2Gz2749y3am3agSDdkhHPHYmmxEGSKH85ZLGzgXw==} + engines: {node: '>=20'} + + osc-progress@0.3.0: + resolution: {integrity: sha512-4/8JfsetakdeEa4vAYV45FW20aY+B/+K8NEXp5Eiar3wR8726whgHrbSg5Ar/ZY1FLJ/AGtUqV7W2IVF+Gvp9A==} + engines: {node: '>=20'} + + p-finally@1.0.0: + resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==} + engines: {node: '>=4'} + + p-queue@6.6.2: + resolution: {integrity: sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==} + engines: {node: '>=8'} + + p-queue@9.1.0: + resolution: {integrity: sha512-O/ZPaXuQV29uSLbxWBGGZO1mCQXV2BLIwUr59JUU9SoH76mnYvtms7aafH/isNSNGwuEfP6W/4xD0/TJXxrizw==} + engines: {node: '>=20'} + + p-retry@4.6.2: + resolution: {integrity: sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==} + engines: {node: '>=8'} + + p-timeout@3.2.0: + resolution: {integrity: sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==} + engines: {node: '>=8'} + + p-timeout@7.0.1: + 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==} + + pako@1.0.11: + resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + + parse-ms@3.0.0: + resolution: {integrity: sha512-Tpb8Z7r7XbbtBTrM9UhpkzzaMrqA2VXMT3YChzYltwV3P3pM6t8wl7TvpMnSTosz1aQAdVib7kdoys7vYOPerw==} + engines: {node: '>=12'} + + parse-ms@4.0.0: + resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==} + engines: {node: '>=18'} + + parse5-htmlparser2-tree-adapter@6.0.1: + resolution: {integrity: sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==} + + parse5@5.1.1: + resolution: {integrity: sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==} + + parse5@6.0.1: + resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==} + + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + + partial-json@0.1.7: + resolution: {integrity: sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA==} + + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + + path-scurry@2.0.2: + resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} + engines: {node: 18 || 20 || >=22} + + path-to-regexp@8.3.0: + resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + pathval@2.0.1: + resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} + engines: {node: '>= 14.16'} + + pdfjs-dist@5.5.207: + resolution: {integrity: sha512-WMqqw06w1vUt9ZfT0gOFhMf3wHsWhaCrxGrckGs5Cci6ybDW87IvPaOd2pnBwT6BJuP/CzXDZxjFgmSULLdsdw==} + engines: {node: '>=20.19.0 || >=22.13.0 || >=24'} + + pend@1.2.0: + resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + + pino-abstract-transport@2.0.0: + resolution: {integrity: sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==} + + pino-std-serializers@7.1.0: + resolution: {integrity: sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==} + + pino@9.14.0: + resolution: {integrity: sha512-8OEwKp5juEvb/MjpIc4hjqfgCNysrS94RIOMXYvpYCdm/jglrKEiAYmiumbmGhCvs+IcInsphYDFwqrjr7398w==} + hasBin: true + + playwright-core@1.58.2: + resolution: {integrity: sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==} + engines: {node: '>=18'} + hasBin: true + + postcss@8.5.8: + resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==} + engines: {node: ^10 || ^12 || >=14} + + pretty-bytes@6.1.1: + resolution: {integrity: sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==} + engines: {node: ^14.13.1 || >=16.0.0} + + pretty-ms@8.0.0: + resolution: {integrity: sha512-ASJqOugUF1bbzI35STMBUpZqdfYKlJugy6JBziGi2EE+AL5JPJGSzvpeVXojxrr0ViUYoToUjb5kjSEGf7Y83Q==} + engines: {node: '>=14.16'} + + pretty-ms@9.3.0: + resolution: {integrity: sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==} + engines: {node: '>=18'} + + prism-media@1.3.5: + resolution: {integrity: sha512-IQdl0Q01m4LrkN1EGIE9lphov5Hy7WWlH6ulf5QdGePLlPas9p2mhgddTEHrlaXYjjFToM1/rWuwF37VF4taaA==} + peerDependencies: + '@discordjs/opus': '>=0.8.0 <1.0.0' + ffmpeg-static: ^5.0.2 || ^4.2.7 || ^3.0.0 || ^2.4.0 + node-opus: ^0.3.3 + opusscript: ^0.0.8 + peerDependenciesMeta: + '@discordjs/opus': + optional: true + ffmpeg-static: + optional: true + node-opus: + optional: true + opusscript: + optional: true + + process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + + process-warning@5.0.0: + resolution: {integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==} + + 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'} + + proxy-addr@2.0.7: + 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==} + + pump@3.0.4: + resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==} + + punycode.js@2.3.1: + resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} + engines: {node: '>=6'} + + qified@0.6.0: + resolution: {integrity: sha512-tsSGN1x3h569ZSU1u6diwhltLyfUWDp3YbFHedapTmpBl0B3P6U3+Qptg7xu+v+1io1EwhdPyyRHYbEw0KN2FA==} + engines: {node: '>=20'} + + qrcode-terminal@0.12.0: + resolution: {integrity: sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ==} + hasBin: true + + qs@6.15.0: + resolution: {integrity: sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==} + engines: {node: '>=0.6'} + + quick-format-unescaped@4.0.4: + resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} + + range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + + raw-body@3.0.2: + resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==} + engines: {node: '>= 0.10'} + + rc@1.2.8: + resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} + hasBin: true + + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + readdirp@5.0.0: + resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==} + engines: {node: '>= 20.19.0'} + + real-require@0.2.0: + resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} + engines: {node: '>= 12.13.0'} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + restore-cursor@5.1.0: + resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} + engines: {node: '>=18'} + + retry@0.12.0: + resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} + engines: {node: '>= 4'} + + retry@0.13.1: + resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} + engines: {node: '>= 4'} + + rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + + rimraf@5.0.10: + resolution: {integrity: sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==} + hasBin: true + + rollup@4.59.0: + resolution: {integrity: sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + router@2.2.0: + resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} + engines: {node: '>= 18'} + + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safe-stable-stringify@2.5.0: + resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} + engines: {node: '>=10'} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.7.4: + resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} + engines: {node: '>=10'} + hasBin: true + + send@1.2.1: + resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} + engines: {node: '>= 18'} + + serve-static@2.2.1: + resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} + engines: {node: '>= 18'} + + set-blocking@2.0.0: + resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + + setimmediate@1.0.5: + resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} + + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + + sharp@0.34.5: + resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + side-channel-list@1.0.0: + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + simple-git@3.32.3: + resolution: {integrity: sha512-56a5oxFdWlsGygOXHWrG+xjj5w9ZIt2uQbzqiIGdR/6i5iococ7WQ/bNPzWxCJdEUGUCmyMH0t9zMpRJTaKxmw==} + + sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + + sleep-promise@9.1.0: + resolution: {integrity: sha512-UHYzVpz9Xn8b+jikYSD6bqvf754xL2uBUzDFwiU6NcdZeifPr6UfgU43xpkPu67VMS88+TI2PSI7Eohgqf2fKA==} + + slice-ansi@7.1.2: + resolution: {integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==} + engines: {node: '>=18'} + + slice-ansi@8.0.0: + 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==} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + + sqlite-vec-darwin-arm64@0.1.7-alpha.2: + resolution: {integrity: sha512-raIATOqFYkeCHhb/t3r7W7Cf2lVYdf4J3ogJ6GFc8PQEgHCPEsi+bYnm2JT84MzLfTlSTIdxr4/NKv+zF7oLPw==} + cpu: [arm64] + os: [darwin] + + sqlite-vec-darwin-x64@0.1.7-alpha.2: + resolution: {integrity: sha512-jeZEELsQjjRsVojsvU5iKxOvkaVuE+JYC8Y4Ma8U45aAERrDYmqZoHvgSG7cg1PXL3bMlumFTAmHynf1y4pOzA==} + cpu: [x64] + os: [darwin] + + sqlite-vec-linux-arm64@0.1.7-alpha.2: + resolution: {integrity: sha512-6Spj4Nfi7tG13jsUG+W7jnT0bCTWbyPImu2M8nWp20fNrd1SZ4g3CSlDAK8GBdavX7wRlbBHCZ+BDa++rbDewA==} + cpu: [arm64] + os: [linux] + + sqlite-vec-linux-x64@0.1.7-alpha.2: + resolution: {integrity: sha512-IcgrbHaDccTVhXDf8Orwdc2+hgDLAFORl6OBUhcvlmwswwBP1hqBTSEhovClG4NItwTOBNgpwOoQ7Qp3VDPWLg==} + cpu: [x64] + os: [linux] + + sqlite-vec-windows-x64@0.1.7-alpha.2: + resolution: {integrity: sha512-TRP6hTjAcwvQ6xpCZvjP00pdlda8J38ArFy1lMYhtQWXiIBmWnhMaMbq4kaeCYwvTTddfidatRS+TJrwIKB/oQ==} + cpu: [x64] + os: [win32] + + sqlite-vec@0.1.7-alpha.2: + resolution: {integrity: sha512-rNgRCv+4V4Ed3yc33Qr+nNmjhtrMnnHzXfLVPeGb28Dx5mmDL3Ngw/Wk8vhCGjj76+oC6gnkmMG8y73BZWGBwQ==} + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + statuses@2.0.2: + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} + + std-env@3.10.0: + resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + + stdin-discarder@0.3.1: + resolution: {integrity: sha512-reExS1kSGoElkextOcPkel4NE99S0BWxjUHQeDFnR8S993JxpPX7KU4MNmO19NXhlJp+8dmdCbKQVNgLJh2teA==} + engines: {node: '>=18'} + + stdout-update@4.0.1: + resolution: {integrity: sha512-wiS21Jthlvl1to+oorePvcyrIkiG/6M3D3VTmDUlJm7Cy6SbFhKkAvX+YBuHLxck/tO3mrdpC/cNesigQc3+UQ==} + engines: {node: '>=16.0.0'} + + steno@4.0.2: + resolution: {integrity: sha512-yhPIQXjrlt1xv7dyPQg2P17URmXbuM5pdGkpiMB3RenprfiBlvK415Lctfe0eshk90oA7/tNq7WEiMK8RSP39A==} + engines: {node: '>=18'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + string-width@7.2.0: + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} + engines: {node: '>=18'} + + string-width@8.2.0: + resolution: {integrity: sha512-6hJPQ8N0V0P3SNmP6h2J99RLuzrWz2gvT7VnK5tKvrNqJoyS9W4/Fb8mo31UiPvy00z7DQXkP2hnKBVav76thw==} + engines: {node: '>=20'} + + string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.2.0: + resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} + engines: {node: '>=12'} + + strip-json-comments@2.0.1: + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + engines: {node: '>=0.10.0'} + + strip-literal@3.1.0: + resolution: {integrity: sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==} + + strnum@2.2.0: + resolution: {integrity: sha512-Y7Bj8XyJxnPAORMZj/xltsfo55uOiyHcU2tnAVzHUnSJR/KsEX+9RoDeXEnsXtl/CX4fAcrt64gZ13aGaWPeBg==} + + strtok3@10.3.4: + resolution: {integrity: sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg==} + engines: {node: '>=18'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + tar@6.2.1: + resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} + engines: {node: '>=10'} + deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + + tar@7.5.10: + resolution: {integrity: sha512-8mOPs1//5q/rlkNSPcCegA6hiHJYDmSLEI8aMH/CdSQJNWztHC9WHNam5zdQlfpTwB9Xp7IBEsHfV5LKMJGVAw==} + engines: {node: '>=18'} + + tar@7.5.9: + resolution: {integrity: sha512-BTLcK0xsDh2+PUe9F6c2TlRp4zOOBMTkoQHQIWSIzI0R7KG46uEwq4OPk2W7bZcprBMsuaeFsqwYr7pjh6CuHg==} + engines: {node: '>=18'} + + thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + + thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + + thread-stream@3.1.0: + resolution: {integrity: sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==} + + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + + tinypool@1.1.1: + resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} + engines: {node: ^18.0.0 || >=20.0.0} + + tinyrainbow@2.0.0: + resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} + engines: {node: '>=14.0.0'} + + tinyspy@4.0.4: + resolution: {integrity: sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==} + engines: {node: '>=14.0.0'} + + toad-cache@3.7.0: + resolution: {integrity: sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==} + engines: {node: '>=12'} + + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + + token-types@6.1.2: + resolution: {integrity: sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==} + engines: {node: '>=14.16'} + + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + + ts-algebra@2.0.0: + resolution: {integrity: sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + tslog@4.10.2: + resolution: {integrity: sha512-XuELoRpMR+sq8fuWwX7P0bcj+PRNiicOKDEb3fGNURhxWVyykCi9BNq7c4uVz7h7P0sj8qgBsr5SWS6yBClq3g==} + engines: {node: '>=16'} + + tsscmp@1.0.6: + resolution: {integrity: sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==} + engines: {node: '>=0.6.x'} + + type-is@2.0.1: + resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} + engines: {node: '>= 0.6'} + + uc.micro@2.1.0: + resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} + + uhyphen@0.2.0: + resolution: {integrity: sha512-qz3o9CHXmJJPGBdqzab7qAYuW8kQGKNEuoHFYrBwV6hWIMcpAmxDLXojcHfFr9US1Pe6zUswEIJIbLI610fuqA==} + + uint8array-extras@1.5.0: + resolution: {integrity: sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==} + engines: {node: '>=18'} + + undici-types@7.16.0: + resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} + + undici-types@7.18.2: + resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} + + undici@7.22.0: + resolution: {integrity: sha512-RqslV2Us5BrllB+JeiZnK4peryVTndy9Dnqq62S3yYRRTj0tFQCwEniUy2167skdGOy3vqRzEvl1Dm4sV2ReDg==} + engines: {node: '>=20.18.1'} + + universal-github-app-jwt@2.2.2: + resolution: {integrity: sha512-dcmbeSrOdTnsjGjUfAlqNDJrhxXizjAz94ija9Qw8YkZ1uu0d+GoZzyH+Jb9tIIqvGsadUfwg+22k5aDqqwzbw==} + + universal-user-agent@7.0.3: + resolution: {integrity: sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==} + + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + + url-join@4.0.1: + resolution: {integrity: sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + validate-npm-package-name@7.0.2: + resolution: {integrity: sha512-hVDIBwsRruT73PbK7uP5ebUt+ezEtCmzZz3F59BSr2F6OVFnJ/6h8liuvdLrQ88Xmnk6/+xGGuq+pG9WwTuy3A==} + engines: {node: ^20.17.0 || >=22.9.0} + + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + + vite-node@3.2.4: + resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + + vite@7.3.1: + resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + jiti: '>=1.21.0' + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitest@3.2.4: + resolution: {integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/debug': ^4.1.12 + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + '@vitest/browser': 3.2.4 + '@vitest/ui': 3.2.4 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/debug': + optional: true + '@types/node': + optional: true + '@vitest/browser': + 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'} + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + which@6.0.1: + resolution: {integrity: sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==} + engines: {node: ^20.17.0 || >=22.9.0} + hasBin: true + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + + wide-align@1.1.5: + resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} + + win-guid@0.2.1: + resolution: {integrity: sha512-gEIQU4mkgl2OPeoNrWflcJFJ3Ae2BPd4eCsHHA/XikslkIVms/nHhvnvzIZV7VLmBvtFlDOzLt9rrZT+n6D67A==} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + ws@8.19.0: + resolution: {integrity: sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==} + 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'} + + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + + yallist@5.0.0: + resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} + engines: {node: '>=18'} + + yaml@2.8.2: + resolution: {integrity: sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==} + engines: {node: '>= 14.6'} + hasBin: true + + yargs-parser@20.2.9: + resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} + engines: {node: '>=10'} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@16.2.0: + resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} + engines: {node: '>=10'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + yauzl@2.10.0: + resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} + + yoctocolors@2.1.2: + resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==} + engines: {node: '>=18'} + + zod-to-json-schema@3.25.1: + resolution: {integrity: sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==} + peerDependencies: + zod: ^3.25 || ^4 + + zod@3.25.76: + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + + zod@4.3.6: + resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} + +snapshots: + + '@agentclientprotocol/sdk@0.14.1(zod@4.3.6)': + dependencies: + zod: 4.3.6 + + '@anthropic-ai/sdk@0.73.0(zod@4.3.6)': + dependencies: + json-schema-to-ts: 3.1.1 + optionalDependencies: + zod: 4.3.6 + + '@aws-crypto/crc32@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.973.5 + tslib: 2.8.1 + + '@aws-crypto/sha256-browser@5.2.0': + dependencies: + '@aws-crypto/sha256-js': 5.2.0 + '@aws-crypto/supports-web-crypto': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.973.5 + '@aws-sdk/util-locate-window': 3.965.5 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + + '@aws-crypto/sha256-js@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.973.5 + tslib: 2.8.1 + + '@aws-crypto/supports-web-crypto@5.2.0': + dependencies: + tslib: 2.8.1 + + '@aws-crypto/util@5.2.0': + dependencies: + '@aws-sdk/types': 3.973.5 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + + '@aws-sdk/client-bedrock-runtime@3.1004.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.973.18 + '@aws-sdk/credential-provider-node': 3.972.18 + '@aws-sdk/eventstream-handler-node': 3.972.10 + '@aws-sdk/middleware-eventstream': 3.972.7 + '@aws-sdk/middleware-host-header': 3.972.7 + '@aws-sdk/middleware-logger': 3.972.7 + '@aws-sdk/middleware-recursion-detection': 3.972.7 + '@aws-sdk/middleware-user-agent': 3.972.19 + '@aws-sdk/middleware-websocket': 3.972.12 + '@aws-sdk/region-config-resolver': 3.972.7 + '@aws-sdk/token-providers': 3.1004.0 + '@aws-sdk/types': 3.973.5 + '@aws-sdk/util-endpoints': 3.996.4 + '@aws-sdk/util-user-agent-browser': 3.972.7 + '@aws-sdk/util-user-agent-node': 3.973.4 + '@smithy/config-resolver': 4.4.10 + '@smithy/core': 3.23.9 + '@smithy/eventstream-serde-browser': 4.2.11 + '@smithy/eventstream-serde-config-resolver': 4.3.11 + '@smithy/eventstream-serde-node': 4.2.11 + '@smithy/fetch-http-handler': 5.3.13 + '@smithy/hash-node': 4.2.11 + '@smithy/invalid-dependency': 4.2.11 + '@smithy/middleware-content-length': 4.2.11 + '@smithy/middleware-endpoint': 4.4.23 + '@smithy/middleware-retry': 4.4.40 + '@smithy/middleware-serde': 4.2.12 + '@smithy/middleware-stack': 4.2.11 + '@smithy/node-config-provider': 4.3.11 + '@smithy/node-http-handler': 4.4.14 + '@smithy/protocol-http': 5.3.11 + '@smithy/smithy-client': 4.12.3 + '@smithy/types': 4.13.0 + '@smithy/url-parser': 4.2.11 + '@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.39 + '@smithy/util-defaults-mode-node': 4.2.42 + '@smithy/util-endpoints': 3.3.2 + '@smithy/util-middleware': 4.2.11 + '@smithy/util-retry': 4.2.11 + '@smithy/util-stream': 4.5.17 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/client-bedrock@3.1004.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.973.18 + '@aws-sdk/credential-provider-node': 3.972.18 + '@aws-sdk/middleware-host-header': 3.972.7 + '@aws-sdk/middleware-logger': 3.972.7 + '@aws-sdk/middleware-recursion-detection': 3.972.7 + '@aws-sdk/middleware-user-agent': 3.972.19 + '@aws-sdk/region-config-resolver': 3.972.7 + '@aws-sdk/token-providers': 3.1004.0 + '@aws-sdk/types': 3.973.5 + '@aws-sdk/util-endpoints': 3.996.4 + '@aws-sdk/util-user-agent-browser': 3.972.7 + '@aws-sdk/util-user-agent-node': 3.973.4 + '@smithy/config-resolver': 4.4.10 + '@smithy/core': 3.23.9 + '@smithy/fetch-http-handler': 5.3.13 + '@smithy/hash-node': 4.2.11 + '@smithy/invalid-dependency': 4.2.11 + '@smithy/middleware-content-length': 4.2.11 + '@smithy/middleware-endpoint': 4.4.23 + '@smithy/middleware-retry': 4.4.40 + '@smithy/middleware-serde': 4.2.12 + '@smithy/middleware-stack': 4.2.11 + '@smithy/node-config-provider': 4.3.11 + '@smithy/node-http-handler': 4.4.14 + '@smithy/protocol-http': 5.3.11 + '@smithy/smithy-client': 4.12.3 + '@smithy/types': 4.13.0 + '@smithy/url-parser': 4.2.11 + '@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.39 + '@smithy/util-defaults-mode-node': 4.2.42 + '@smithy/util-endpoints': 3.3.2 + '@smithy/util-middleware': 4.2.11 + '@smithy/util-retry': 4.2.11 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/core@3.973.18': + dependencies: + '@aws-sdk/types': 3.973.5 + '@aws-sdk/xml-builder': 3.972.10 + '@smithy/core': 3.23.9 + '@smithy/node-config-provider': 4.3.11 + '@smithy/property-provider': 4.2.11 + '@smithy/protocol-http': 5.3.11 + '@smithy/signature-v4': 5.3.11 + '@smithy/smithy-client': 4.12.3 + '@smithy/types': 4.13.0 + '@smithy/util-base64': 4.3.2 + '@smithy/util-middleware': 4.2.11 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-env@3.972.16': + dependencies: + '@aws-sdk/core': 3.973.18 + '@aws-sdk/types': 3.973.5 + '@smithy/property-provider': 4.2.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-http@3.972.18': + dependencies: + '@aws-sdk/core': 3.973.18 + '@aws-sdk/types': 3.973.5 + '@smithy/fetch-http-handler': 5.3.13 + '@smithy/node-http-handler': 4.4.14 + '@smithy/property-provider': 4.2.11 + '@smithy/protocol-http': 5.3.11 + '@smithy/smithy-client': 4.12.3 + '@smithy/types': 4.13.0 + '@smithy/util-stream': 4.5.17 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-ini@3.972.17': + dependencies: + '@aws-sdk/core': 3.973.18 + '@aws-sdk/credential-provider-env': 3.972.16 + '@aws-sdk/credential-provider-http': 3.972.18 + '@aws-sdk/credential-provider-login': 3.972.17 + '@aws-sdk/credential-provider-process': 3.972.16 + '@aws-sdk/credential-provider-sso': 3.972.17 + '@aws-sdk/credential-provider-web-identity': 3.972.17 + '@aws-sdk/nested-clients': 3.996.7 + '@aws-sdk/types': 3.973.5 + '@smithy/credential-provider-imds': 4.2.11 + '@smithy/property-provider': 4.2.11 + '@smithy/shared-ini-file-loader': 4.4.6 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-login@3.972.17': + dependencies: + '@aws-sdk/core': 3.973.18 + '@aws-sdk/nested-clients': 3.996.7 + '@aws-sdk/types': 3.973.5 + '@smithy/property-provider': 4.2.11 + '@smithy/protocol-http': 5.3.11 + '@smithy/shared-ini-file-loader': 4.4.6 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-node@3.972.18': + dependencies: + '@aws-sdk/credential-provider-env': 3.972.16 + '@aws-sdk/credential-provider-http': 3.972.18 + '@aws-sdk/credential-provider-ini': 3.972.17 + '@aws-sdk/credential-provider-process': 3.972.16 + '@aws-sdk/credential-provider-sso': 3.972.17 + '@aws-sdk/credential-provider-web-identity': 3.972.17 + '@aws-sdk/types': 3.973.5 + '@smithy/credential-provider-imds': 4.2.11 + '@smithy/property-provider': 4.2.11 + '@smithy/shared-ini-file-loader': 4.4.6 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-process@3.972.16': + dependencies: + '@aws-sdk/core': 3.973.18 + '@aws-sdk/types': 3.973.5 + '@smithy/property-provider': 4.2.11 + '@smithy/shared-ini-file-loader': 4.4.6 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-sso@3.972.17': + dependencies: + '@aws-sdk/core': 3.973.18 + '@aws-sdk/nested-clients': 3.996.7 + '@aws-sdk/token-providers': 3.1004.0 + '@aws-sdk/types': 3.973.5 + '@smithy/property-provider': 4.2.11 + '@smithy/shared-ini-file-loader': 4.4.6 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-web-identity@3.972.17': + dependencies: + '@aws-sdk/core': 3.973.18 + '@aws-sdk/nested-clients': 3.996.7 + '@aws-sdk/types': 3.973.5 + '@smithy/property-provider': 4.2.11 + '@smithy/shared-ini-file-loader': 4.4.6 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/eventstream-handler-node@3.972.10': + dependencies: + '@aws-sdk/types': 3.973.5 + '@smithy/eventstream-codec': 4.2.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-eventstream@3.972.7': + dependencies: + '@aws-sdk/types': 3.973.5 + '@smithy/protocol-http': 5.3.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-host-header@3.972.7': + dependencies: + '@aws-sdk/types': 3.973.5 + '@smithy/protocol-http': 5.3.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-logger@3.972.7': + dependencies: + '@aws-sdk/types': 3.973.5 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-recursion-detection@3.972.7': + dependencies: + '@aws-sdk/types': 3.973.5 + '@aws/lambda-invoke-store': 0.2.3 + '@smithy/protocol-http': 5.3.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-user-agent@3.972.19': + dependencies: + '@aws-sdk/core': 3.973.18 + '@aws-sdk/types': 3.973.5 + '@aws-sdk/util-endpoints': 3.996.4 + '@smithy/core': 3.23.9 + '@smithy/protocol-http': 5.3.11 + '@smithy/types': 4.13.0 + '@smithy/util-retry': 4.2.11 + tslib: 2.8.1 + + '@aws-sdk/middleware-websocket@3.972.12': + dependencies: + '@aws-sdk/types': 3.973.5 + '@aws-sdk/util-format-url': 3.972.7 + '@smithy/eventstream-codec': 4.2.11 + '@smithy/eventstream-serde-browser': 4.2.11 + '@smithy/fetch-http-handler': 5.3.13 + '@smithy/protocol-http': 5.3.11 + '@smithy/signature-v4': 5.3.11 + '@smithy/types': 4.13.0 + '@smithy/util-base64': 4.3.2 + '@smithy/util-hex-encoding': 4.2.2 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + + '@aws-sdk/nested-clients@3.996.7': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.973.18 + '@aws-sdk/middleware-host-header': 3.972.7 + '@aws-sdk/middleware-logger': 3.972.7 + '@aws-sdk/middleware-recursion-detection': 3.972.7 + '@aws-sdk/middleware-user-agent': 3.972.19 + '@aws-sdk/region-config-resolver': 3.972.7 + '@aws-sdk/types': 3.973.5 + '@aws-sdk/util-endpoints': 3.996.4 + '@aws-sdk/util-user-agent-browser': 3.972.7 + '@aws-sdk/util-user-agent-node': 3.973.4 + '@smithy/config-resolver': 4.4.10 + '@smithy/core': 3.23.9 + '@smithy/fetch-http-handler': 5.3.13 + '@smithy/hash-node': 4.2.11 + '@smithy/invalid-dependency': 4.2.11 + '@smithy/middleware-content-length': 4.2.11 + '@smithy/middleware-endpoint': 4.4.23 + '@smithy/middleware-retry': 4.4.40 + '@smithy/middleware-serde': 4.2.12 + '@smithy/middleware-stack': 4.2.11 + '@smithy/node-config-provider': 4.3.11 + '@smithy/node-http-handler': 4.4.14 + '@smithy/protocol-http': 5.3.11 + '@smithy/smithy-client': 4.12.3 + '@smithy/types': 4.13.0 + '@smithy/url-parser': 4.2.11 + '@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.39 + '@smithy/util-defaults-mode-node': 4.2.42 + '@smithy/util-endpoints': 3.3.2 + '@smithy/util-middleware': 4.2.11 + '@smithy/util-retry': 4.2.11 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/region-config-resolver@3.972.7': + dependencies: + '@aws-sdk/types': 3.973.5 + '@smithy/config-resolver': 4.4.10 + '@smithy/node-config-provider': 4.3.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/token-providers@3.1004.0': + dependencies: + '@aws-sdk/core': 3.973.18 + '@aws-sdk/nested-clients': 3.996.7 + '@aws-sdk/types': 3.973.5 + '@smithy/property-provider': 4.2.11 + '@smithy/shared-ini-file-loader': 4.4.6 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/types@3.973.5': + dependencies: + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/util-endpoints@3.996.4': + dependencies: + '@aws-sdk/types': 3.973.5 + '@smithy/types': 4.13.0 + '@smithy/url-parser': 4.2.11 + '@smithy/util-endpoints': 3.3.2 + tslib: 2.8.1 + + '@aws-sdk/util-format-url@3.972.7': + dependencies: + '@aws-sdk/types': 3.973.5 + '@smithy/querystring-builder': 4.2.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/util-locate-window@3.965.5': + dependencies: + tslib: 2.8.1 + + '@aws-sdk/util-user-agent-browser@3.972.7': + dependencies: + '@aws-sdk/types': 3.973.5 + '@smithy/types': 4.13.0 + bowser: 2.14.1 + tslib: 2.8.1 + + '@aws-sdk/util-user-agent-node@3.973.4': + dependencies: + '@aws-sdk/middleware-user-agent': 3.972.19 + '@aws-sdk/types': 3.973.5 + '@smithy/node-config-provider': 4.3.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/xml-builder@3.972.10': + dependencies: + '@smithy/types': 4.13.0 + fast-xml-parser: 5.4.1 + tslib: 2.8.1 + + '@aws/lambda-invoke-store@0.2.3': {} + + '@babel/runtime@7.28.6': {} + + '@borewit/text-codec@0.2.1': {} + + '@buape/carbon@0.0.0-beta-20260216184201(@discordjs/opus@0.10.0)(hono@4.12.5)(opusscript@0.1.1)': + dependencies: + '@types/node': 25.3.5 + discord-api-types: 0.38.37 + optionalDependencies: + '@cloudflare/workers-types': 4.20260120.0 + '@discordjs/voice': 0.19.0(@discordjs/opus@0.10.0)(opusscript@0.1.1) + '@hono/node-server': 1.19.9(hono@4.12.5) + '@types/bun': 1.3.9 + '@types/ws': 8.18.1 + ws: 8.19.0 + transitivePeerDependencies: + - '@discordjs/opus' + - bufferutil + - ffmpeg-static + - hono + - node-opus + - opusscript + - utf-8-validate + + '@cacheable/memory@2.0.8': + dependencies: + '@cacheable/utils': 2.4.0 + '@keyv/bigmap': 1.3.1(keyv@5.6.0) + hookified: 1.15.1 + keyv: 5.6.0 + + '@cacheable/node-cache@1.7.6': + dependencies: + cacheable: 2.3.3 + hookified: 1.15.1 + keyv: 5.6.0 + + '@cacheable/utils@2.4.0': + dependencies: + hashery: 1.5.0 + keyv: 5.6.0 + + '@clack/core@1.1.0': + dependencies: + sisteransi: 1.0.5 + + '@clack/prompts@1.1.0': + dependencies: + '@clack/core': 1.1.0 + sisteransi: 1.0.5 + + '@cloudflare/workers-types@4.20260120.0': + optional: true + + '@discordjs/node-pre-gyp@0.4.5': + dependencies: + detect-libc: 2.1.2 + https-proxy-agent: 5.0.1 + make-dir: 3.1.0 + node-fetch: 2.7.0 + nopt: 5.0.0 + npmlog: 5.0.1 + rimraf: 3.0.2 + semver: 7.7.4 + tar: 6.2.1 + transitivePeerDependencies: + - encoding + - supports-color + optional: true + + '@discordjs/opus@0.10.0': + dependencies: + '@discordjs/node-pre-gyp': 0.4.5 + node-addon-api: 8.6.0 + transitivePeerDependencies: + - encoding + - supports-color + optional: true + + '@discordjs/voice@0.19.0(@discordjs/opus@0.10.0)(opusscript@0.1.1)': + dependencies: + '@types/ws': 8.18.1 + discord-api-types: 0.38.41 + prism-media: 1.3.5(@discordjs/opus@0.10.0)(opusscript@0.1.1) + tslib: 2.8.1 + ws: 8.19.0 + transitivePeerDependencies: + - '@discordjs/opus' + - bufferutil + - ffmpeg-static + - node-opus + - opusscript + - utf-8-validate + + '@emnapi/core@1.8.1': + dependencies: + '@emnapi/wasi-threads': 1.1.0 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.8.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.1.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@esbuild/aix-ppc64@0.27.3': + optional: true + + '@esbuild/android-arm64@0.27.3': + optional: true + + '@esbuild/android-arm@0.27.3': + optional: true + + '@esbuild/android-x64@0.27.3': + optional: true + + '@esbuild/darwin-arm64@0.27.3': + optional: true + + '@esbuild/darwin-x64@0.27.3': + optional: true + + '@esbuild/freebsd-arm64@0.27.3': + optional: true + + '@esbuild/freebsd-x64@0.27.3': + optional: true + + '@esbuild/linux-arm64@0.27.3': + optional: true + + '@esbuild/linux-arm@0.27.3': + optional: true + + '@esbuild/linux-ia32@0.27.3': + optional: true + + '@esbuild/linux-loong64@0.27.3': + optional: true + + '@esbuild/linux-mips64el@0.27.3': + optional: true + + '@esbuild/linux-ppc64@0.27.3': + optional: true + + '@esbuild/linux-riscv64@0.27.3': + optional: true + + '@esbuild/linux-s390x@0.27.3': + optional: true + + '@esbuild/linux-x64@0.27.3': + optional: true + + '@esbuild/netbsd-arm64@0.27.3': + optional: true + + '@esbuild/netbsd-x64@0.27.3': + optional: true + + '@esbuild/openbsd-arm64@0.27.3': + optional: true + + '@esbuild/openbsd-x64@0.27.3': + optional: true + + '@esbuild/openharmony-arm64@0.27.3': + optional: true + + '@esbuild/sunos-x64@0.27.3': + optional: true + + '@esbuild/win32-arm64@0.27.3': + optional: true + + '@esbuild/win32-ia32@0.27.3': + optional: true + + '@esbuild/win32-x64@0.27.3': + optional: true + + '@google/genai@1.44.0': + dependencies: + google-auth-library: 10.6.1 + p-retry: 4.6.2 + protobufjs: 7.5.4 + ws: 8.19.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + '@grammyjs/runner@2.0.3(grammy@1.41.1)': + dependencies: + abort-controller: 3.0.0 + grammy: 1.41.1 + + '@grammyjs/transformer-throttler@1.2.1(grammy@1.41.1)': + dependencies: + bottleneck: 2.19.5 + grammy: 1.41.1 + + '@grammyjs/types@3.25.0': {} + + '@hapi/boom@9.1.4': + dependencies: + '@hapi/hoek': 9.3.0 + + '@hapi/hoek@9.3.0': {} + + '@homebridge/ciao@1.3.5': + dependencies: + debug: 4.4.3 + fast-deep-equal: 3.1.3 + source-map-support: 0.5.21 + tslib: 2.8.1 + transitivePeerDependencies: + - supports-color + + '@hono/node-server@1.19.9(hono@4.12.5)': + dependencies: + hono: 4.12.5 + optional: true + + '@huggingface/jinja@0.5.5': {} + + '@img/colour@1.1.0': {} + + '@img/sharp-darwin-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.2.4 + optional: true + + '@img/sharp-darwin-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.2.4 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-darwin-x64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-arm@1.2.4': + optional: true + + '@img/sharp-libvips-linux-ppc64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-riscv64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-s390x@1.2.4': + optional: true + + '@img/sharp-libvips-linux-x64@1.2.4': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.2.4': + optional: true + + '@img/sharp-linux-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.2.4 + optional: true + + '@img/sharp-linux-arm@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.2.4 + optional: true + + '@img/sharp-linux-ppc64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-ppc64': 1.2.4 + optional: true + + '@img/sharp-linux-riscv64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-riscv64': 1.2.4 + optional: true + + '@img/sharp-linux-s390x@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.2.4 + optional: true + + '@img/sharp-linux-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.2.4 + optional: true + + '@img/sharp-linuxmusl-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 + optional: true + + '@img/sharp-linuxmusl-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + optional: true + + '@img/sharp-wasm32@0.34.5': + dependencies: + '@emnapi/runtime': 1.8.1 + optional: true + + '@img/sharp-win32-arm64@0.34.5': + optional: true + + '@img/sharp-win32-ia32@0.34.5': + optional: true + + '@img/sharp-win32-x64@0.34.5': + optional: true + + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.2.0 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + + '@isaacs/fs-minipass@4.0.1': + dependencies: + minipass: 7.1.3 + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@keyv/bigmap@1.3.1(keyv@5.6.0)': + dependencies: + hashery: 1.5.0 + hookified: 1.15.1 + keyv: 5.6.0 + + '@keyv/serialize@1.1.1': {} + + '@kwsites/file-exists@1.1.1': + dependencies: + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@kwsites/promise-deferred@1.1.1': {} + + '@larksuiteoapi/node-sdk@1.59.0': + dependencies: + axios: 1.13.6 + lodash.identity: 3.0.0 + lodash.merge: 4.6.2 + lodash.pickby: 4.6.0 + protobufjs: 7.5.4 + qs: 6.15.0 + ws: 8.19.0 + transitivePeerDependencies: + - bufferutil + - debug + - utf-8-validate + + '@line/bot-sdk@10.6.0': + dependencies: + '@types/node': 24.12.0 + optionalDependencies: + axios: 1.13.6 + transitivePeerDependencies: + - debug + + '@lydell/node-pty-darwin-arm64@1.2.0-beta.3': + optional: true + + '@lydell/node-pty-darwin-x64@1.2.0-beta.3': + optional: true + + '@lydell/node-pty-linux-arm64@1.2.0-beta.3': + optional: true + + '@lydell/node-pty-linux-x64@1.2.0-beta.3': + optional: true + + '@lydell/node-pty-win32-arm64@1.2.0-beta.3': + optional: true + + '@lydell/node-pty-win32-x64@1.2.0-beta.3': + optional: true + + '@lydell/node-pty@1.2.0-beta.3': + optionalDependencies: + '@lydell/node-pty-darwin-arm64': 1.2.0-beta.3 + '@lydell/node-pty-darwin-x64': 1.2.0-beta.3 + '@lydell/node-pty-linux-arm64': 1.2.0-beta.3 + '@lydell/node-pty-linux-x64': 1.2.0-beta.3 + '@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': + optional: true + + '@mariozechner/clipboard-darwin-universal@0.3.2': + optional: true + + '@mariozechner/clipboard-darwin-x64@0.3.2': + optional: true + + '@mariozechner/clipboard-linux-arm64-gnu@0.3.2': + optional: true + + '@mariozechner/clipboard-linux-arm64-musl@0.3.2': + optional: true + + '@mariozechner/clipboard-linux-riscv64-gnu@0.3.2': + optional: true + + '@mariozechner/clipboard-linux-x64-gnu@0.3.2': + optional: true + + '@mariozechner/clipboard-linux-x64-musl@0.3.2': + optional: true + + '@mariozechner/clipboard-win32-arm64-msvc@0.3.2': + optional: true + + '@mariozechner/clipboard-win32-x64-msvc@0.3.2': + optional: true + + '@mariozechner/clipboard@0.3.2': + optionalDependencies: + '@mariozechner/clipboard-darwin-arm64': 0.3.2 + '@mariozechner/clipboard-darwin-universal': 0.3.2 + '@mariozechner/clipboard-darwin-x64': 0.3.2 + '@mariozechner/clipboard-linux-arm64-gnu': 0.3.2 + '@mariozechner/clipboard-linux-arm64-musl': 0.3.2 + '@mariozechner/clipboard-linux-riscv64-gnu': 0.3.2 + '@mariozechner/clipboard-linux-x64-gnu': 0.3.2 + '@mariozechner/clipboard-linux-x64-musl': 0.3.2 + '@mariozechner/clipboard-win32-arm64-msvc': 0.3.2 + '@mariozechner/clipboard-win32-x64-msvc': 0.3.2 + optional: true + + '@mariozechner/jiti@2.6.5': + dependencies: + std-env: 3.10.0 + yoctocolors: 2.1.2 + + '@mariozechner/pi-agent-core@0.55.3(ws@8.19.0)(zod@4.3.6)': + dependencies: + '@mariozechner/pi-ai': 0.55.3(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.55.3(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.1004.0 + '@google/genai': 1.44.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.22.0 + 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.55.3(ws@8.19.0)(zod@4.3.6)': + dependencies: + '@mariozechner/jiti': 2.6.5 + '@mariozechner/pi-agent-core': 0.55.3(ws@8.19.0)(zod@4.3.6) + '@mariozechner/pi-ai': 0.55.3(ws@8.19.0)(zod@4.3.6) + '@mariozechner/pi-tui': 0.55.3 + '@silvia-odwyer/photon-node': 0.3.4 + chalk: 5.6.2 + cli-highlight: 2.1.11 + diff: 8.0.3 + extract-zip: 2.0.1 + file-type: 21.3.0 + glob: 13.0.6 + hosted-git-info: 9.0.2 + ignore: 7.0.5 + marked: 15.0.12 + minimatch: 10.2.4 + proper-lockfile: 4.1.2 + yaml: 2.8.2 + optionalDependencies: + '@mariozechner/clipboard': 0.3.2 + transitivePeerDependencies: + - '@modelcontextprotocol/sdk' + - aws-crt + - bufferutil + - supports-color + - utf-8-validate + - ws + - zod + + '@mariozechner/pi-tui@0.55.3': + 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 + + '@mistralai/mistralai@1.10.0': + dependencies: + zod: 3.25.76 + zod-to-json-schema: 3.25.1(zod@3.25.76) + + '@mozilla/readability@0.6.0': {} + + '@napi-rs/canvas-android-arm64@0.1.96': + optional: true + + '@napi-rs/canvas-darwin-arm64@0.1.96': + optional: true + + '@napi-rs/canvas-darwin-x64@0.1.96': + optional: true + + '@napi-rs/canvas-linux-arm-gnueabihf@0.1.96': + optional: true + + '@napi-rs/canvas-linux-arm64-gnu@0.1.96': + optional: true + + '@napi-rs/canvas-linux-arm64-musl@0.1.96': + optional: true + + '@napi-rs/canvas-linux-riscv64-gnu@0.1.96': + optional: true + + '@napi-rs/canvas-linux-x64-gnu@0.1.96': + optional: true + + '@napi-rs/canvas-linux-x64-musl@0.1.96': + optional: true + + '@napi-rs/canvas-win32-arm64-msvc@0.1.96': + optional: true + + '@napi-rs/canvas-win32-x64-msvc@0.1.96': + optional: true + + '@napi-rs/canvas@0.1.96': + optionalDependencies: + '@napi-rs/canvas-android-arm64': 0.1.96 + '@napi-rs/canvas-darwin-arm64': 0.1.96 + '@napi-rs/canvas-darwin-x64': 0.1.96 + '@napi-rs/canvas-linux-arm-gnueabihf': 0.1.96 + '@napi-rs/canvas-linux-arm64-gnu': 0.1.96 + '@napi-rs/canvas-linux-arm64-musl': 0.1.96 + '@napi-rs/canvas-linux-riscv64-gnu': 0.1.96 + '@napi-rs/canvas-linux-x64-gnu': 0.1.96 + '@napi-rs/canvas-linux-x64-musl': 0.1.96 + '@napi-rs/canvas-win32-arm64-msvc': 0.1.96 + '@napi-rs/canvas-win32-x64-msvc': 0.1.96 + + '@napi-rs/wasm-runtime@1.1.1': + dependencies: + '@emnapi/core': 1.8.1 + '@emnapi/runtime': 1.8.1 + '@tybys/wasm-util': 0.10.1 + optional: true + + '@node-llama-cpp/linux-arm64@3.16.2': + optional: true + + '@node-llama-cpp/linux-armv7l@3.16.2': + optional: true + + '@node-llama-cpp/linux-x64-cuda-ext@3.16.2': + optional: true + + '@node-llama-cpp/linux-x64-cuda@3.16.2': + optional: true + + '@node-llama-cpp/linux-x64-vulkan@3.16.2': + optional: true + + '@node-llama-cpp/linux-x64@3.16.2': + optional: true + + '@node-llama-cpp/mac-arm64-metal@3.16.2': + optional: true + + '@node-llama-cpp/mac-x64@3.16.2': + optional: true + + '@node-llama-cpp/win-arm64@3.16.2': + optional: true + + '@node-llama-cpp/win-x64-cuda-ext@3.16.2': + optional: true + + '@node-llama-cpp/win-x64-cuda@3.16.2': + optional: true + + '@node-llama-cpp/win-x64-vulkan@3.16.2': + optional: true + + '@node-llama-cpp/win-x64@3.16.2': + optional: true + + '@nolyfill/domexception@1.0.28': {} + + '@octokit/app@16.1.2': + dependencies: + '@octokit/auth-app': 8.2.0 + '@octokit/auth-unauthenticated': 7.0.3 + '@octokit/core': 7.0.6 + '@octokit/oauth-app': 8.0.3 + '@octokit/plugin-paginate-rest': 14.0.0(@octokit/core@7.0.6) + '@octokit/types': 16.0.0 + '@octokit/webhooks': 14.2.0 + + '@octokit/auth-app@8.2.0': + dependencies: + '@octokit/auth-oauth-app': 9.0.3 + '@octokit/auth-oauth-user': 6.0.2 + '@octokit/request': 10.0.8 + '@octokit/request-error': 7.1.0 + '@octokit/types': 16.0.0 + toad-cache: 3.7.0 + universal-github-app-jwt: 2.2.2 + universal-user-agent: 7.0.3 + + '@octokit/auth-oauth-app@9.0.3': + dependencies: + '@octokit/auth-oauth-device': 8.0.3 + '@octokit/auth-oauth-user': 6.0.2 + '@octokit/request': 10.0.8 + '@octokit/types': 16.0.0 + universal-user-agent: 7.0.3 + + '@octokit/auth-oauth-device@8.0.3': + dependencies: + '@octokit/oauth-methods': 6.0.2 + '@octokit/request': 10.0.8 + '@octokit/types': 16.0.0 + universal-user-agent: 7.0.3 + + '@octokit/auth-oauth-user@6.0.2': + dependencies: + '@octokit/auth-oauth-device': 8.0.3 + '@octokit/oauth-methods': 6.0.2 + '@octokit/request': 10.0.8 + '@octokit/types': 16.0.0 + universal-user-agent: 7.0.3 + + '@octokit/auth-token@6.0.0': {} + + '@octokit/auth-unauthenticated@7.0.3': + dependencies: + '@octokit/request-error': 7.1.0 + '@octokit/types': 16.0.0 + + '@octokit/core@7.0.6': + dependencies: + '@octokit/auth-token': 6.0.0 + '@octokit/graphql': 9.0.3 + '@octokit/request': 10.0.8 + '@octokit/request-error': 7.1.0 + '@octokit/types': 16.0.0 + before-after-hook: 4.0.0 + universal-user-agent: 7.0.3 + + '@octokit/endpoint@11.0.3': + dependencies: + '@octokit/types': 16.0.0 + universal-user-agent: 7.0.3 + + '@octokit/graphql@9.0.3': + dependencies: + '@octokit/request': 10.0.8 + '@octokit/types': 16.0.0 + universal-user-agent: 7.0.3 + + '@octokit/oauth-app@8.0.3': + dependencies: + '@octokit/auth-oauth-app': 9.0.3 + '@octokit/auth-oauth-user': 6.0.2 + '@octokit/auth-unauthenticated': 7.0.3 + '@octokit/core': 7.0.6 + '@octokit/oauth-authorization-url': 8.0.0 + '@octokit/oauth-methods': 6.0.2 + '@types/aws-lambda': 8.10.161 + universal-user-agent: 7.0.3 + + '@octokit/oauth-authorization-url@8.0.0': {} + + '@octokit/oauth-methods@6.0.2': + dependencies: + '@octokit/oauth-authorization-url': 8.0.0 + '@octokit/request': 10.0.8 + '@octokit/request-error': 7.1.0 + '@octokit/types': 16.0.0 + + '@octokit/openapi-types@27.0.0': {} + + '@octokit/openapi-webhooks-types@12.1.0': {} + + '@octokit/plugin-paginate-graphql@6.0.0(@octokit/core@7.0.6)': + dependencies: + '@octokit/core': 7.0.6 + + '@octokit/plugin-paginate-rest@14.0.0(@octokit/core@7.0.6)': + dependencies: + '@octokit/core': 7.0.6 + '@octokit/types': 16.0.0 + + '@octokit/plugin-rest-endpoint-methods@17.0.0(@octokit/core@7.0.6)': + dependencies: + '@octokit/core': 7.0.6 + '@octokit/types': 16.0.0 + + '@octokit/plugin-retry@8.1.0(@octokit/core@7.0.6)': + dependencies: + '@octokit/core': 7.0.6 + '@octokit/request-error': 7.1.0 + '@octokit/types': 16.0.0 + bottleneck: 2.19.5 + + '@octokit/plugin-throttling@11.0.3(@octokit/core@7.0.6)': + dependencies: + '@octokit/core': 7.0.6 + '@octokit/types': 16.0.0 + bottleneck: 2.19.5 + + '@octokit/request-error@7.1.0': + dependencies: + '@octokit/types': 16.0.0 + + '@octokit/request@10.0.8': + dependencies: + '@octokit/endpoint': 11.0.3 + '@octokit/request-error': 7.1.0 + '@octokit/types': 16.0.0 + fast-content-type-parse: 3.0.0 + json-with-bigint: 3.5.7 + universal-user-agent: 7.0.3 + + '@octokit/types@16.0.0': + dependencies: + '@octokit/openapi-types': 27.0.0 + + '@octokit/webhooks-methods@6.0.0': {} + + '@octokit/webhooks@14.2.0': + dependencies: + '@octokit/openapi-webhooks-types': 12.1.0 + '@octokit/request-error': 7.1.0 + '@octokit/webhooks-methods': 6.0.0 + + '@pinojs/redact@0.4.0': {} + + '@pkgjs/parseargs@0.11.0': + optional: true + + '@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': + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/inquire': 1.1.0 + + '@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': {} + + '@reflink/reflink-darwin-arm64@0.1.19': + optional: true + + '@reflink/reflink-darwin-x64@0.1.19': + optional: true + + '@reflink/reflink-linux-arm64-gnu@0.1.19': + optional: true + + '@reflink/reflink-linux-arm64-musl@0.1.19': + optional: true + + '@reflink/reflink-linux-x64-gnu@0.1.19': + optional: true + + '@reflink/reflink-linux-x64-musl@0.1.19': + optional: true + + '@reflink/reflink-win32-arm64-msvc@0.1.19': + optional: true + + '@reflink/reflink-win32-x64-msvc@0.1.19': + optional: true + + '@reflink/reflink@0.1.19': + optionalDependencies: + '@reflink/reflink-darwin-arm64': 0.1.19 + '@reflink/reflink-darwin-x64': 0.1.19 + '@reflink/reflink-linux-arm64-gnu': 0.1.19 + '@reflink/reflink-linux-arm64-musl': 0.1.19 + '@reflink/reflink-linux-x64-gnu': 0.1.19 + '@reflink/reflink-linux-x64-musl': 0.1.19 + '@reflink/reflink-win32-arm64-msvc': 0.1.19 + '@reflink/reflink-win32-x64-msvc': 0.1.19 + optional: true + + '@rollup/rollup-android-arm-eabi@4.59.0': + optional: true + + '@rollup/rollup-android-arm64@4.59.0': + optional: true + + '@rollup/rollup-darwin-arm64@4.59.0': + optional: true + + '@rollup/rollup-darwin-x64@4.59.0': + optional: true + + '@rollup/rollup-freebsd-arm64@4.59.0': + optional: true + + '@rollup/rollup-freebsd-x64@4.59.0': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.59.0': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.59.0': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.59.0': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.59.0': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.59.0': + optional: true + + '@rollup/rollup-linux-loong64-musl@4.59.0': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.59.0': + optional: true + + '@rollup/rollup-linux-ppc64-musl@4.59.0': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.59.0': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.59.0': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.59.0': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.59.0': + optional: true + + '@rollup/rollup-linux-x64-musl@4.59.0': + optional: true + + '@rollup/rollup-openbsd-x64@4.59.0': + optional: true + + '@rollup/rollup-openharmony-arm64@4.59.0': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.59.0': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.59.0': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.59.0': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.59.0': + optional: true + + '@silvia-odwyer/photon-node@0.3.4': {} + + '@sinclair/typebox@0.34.48': {} + + '@slack/bolt@4.6.0(@types/express@5.0.6)': + dependencies: + '@slack/logger': 4.0.0 + '@slack/oauth': 3.0.4 + '@slack/socket-mode': 2.0.5 + '@slack/types': 2.20.0 + '@slack/web-api': 7.14.1 + '@types/express': 5.0.6 + axios: 1.13.6 + express: 5.2.1 + path-to-regexp: 8.3.0 + raw-body: 3.0.2 + tsscmp: 1.0.6 + transitivePeerDependencies: + - bufferutil + - debug + - supports-color + - utf-8-validate + + '@slack/logger@4.0.0': + dependencies: + '@types/node': 25.3.5 + + '@slack/oauth@3.0.4': + dependencies: + '@slack/logger': 4.0.0 + '@slack/web-api': 7.14.1 + '@types/jsonwebtoken': 9.0.10 + '@types/node': 25.3.5 + jsonwebtoken: 9.0.3 + transitivePeerDependencies: + - debug + + '@slack/socket-mode@2.0.5': + dependencies: + '@slack/logger': 4.0.0 + '@slack/web-api': 7.14.1 + '@types/node': 25.3.5 + '@types/ws': 8.18.1 + eventemitter3: 5.0.4 + ws: 8.19.0 + transitivePeerDependencies: + - bufferutil + - debug + - utf-8-validate + + '@slack/types@2.20.0': {} + + '@slack/web-api@7.14.1': + dependencies: + '@slack/logger': 4.0.0 + '@slack/types': 2.20.0 + '@types/node': 25.3.5 + '@types/retry': 0.12.0 + axios: 1.13.6 + eventemitter3: 5.0.4 + form-data: 4.0.5 + is-electron: 2.2.2 + is-stream: 2.0.1 + p-queue: 6.6.2 + p-retry: 4.6.2 + retry: 0.13.1 + transitivePeerDependencies: + - debug + + '@smithy/abort-controller@4.2.11': + dependencies: + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/config-resolver@4.4.10': + dependencies: + '@smithy/node-config-provider': 4.3.11 + '@smithy/types': 4.13.0 + '@smithy/util-config-provider': 4.2.2 + '@smithy/util-endpoints': 3.3.2 + '@smithy/util-middleware': 4.2.11 + tslib: 2.8.1 + + '@smithy/core@3.23.9': + dependencies: + '@smithy/middleware-serde': 4.2.12 + '@smithy/protocol-http': 5.3.11 + '@smithy/types': 4.13.0 + '@smithy/util-base64': 4.3.2 + '@smithy/util-body-length-browser': 4.2.2 + '@smithy/util-middleware': 4.2.11 + '@smithy/util-stream': 4.5.17 + '@smithy/util-utf8': 4.2.2 + '@smithy/uuid': 1.1.2 + tslib: 2.8.1 + + '@smithy/credential-provider-imds@4.2.11': + dependencies: + '@smithy/node-config-provider': 4.3.11 + '@smithy/property-provider': 4.2.11 + '@smithy/types': 4.13.0 + '@smithy/url-parser': 4.2.11 + tslib: 2.8.1 + + '@smithy/eventstream-codec@4.2.11': + dependencies: + '@aws-crypto/crc32': 5.2.0 + '@smithy/types': 4.13.0 + '@smithy/util-hex-encoding': 4.2.2 + tslib: 2.8.1 + + '@smithy/eventstream-serde-browser@4.2.11': + dependencies: + '@smithy/eventstream-serde-universal': 4.2.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/eventstream-serde-config-resolver@4.3.11': + dependencies: + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/eventstream-serde-node@4.2.11': + dependencies: + '@smithy/eventstream-serde-universal': 4.2.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/eventstream-serde-universal@4.2.11': + dependencies: + '@smithy/eventstream-codec': 4.2.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/fetch-http-handler@5.3.13': + dependencies: + '@smithy/protocol-http': 5.3.11 + '@smithy/querystring-builder': 4.2.11 + '@smithy/types': 4.13.0 + '@smithy/util-base64': 4.3.2 + tslib: 2.8.1 + + '@smithy/hash-node@4.2.11': + dependencies: + '@smithy/types': 4.13.0 + '@smithy/util-buffer-from': 4.2.2 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + + '@smithy/invalid-dependency@4.2.11': + dependencies: + '@smithy/types': 4.13.0 + 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.11': + dependencies: + '@smithy/protocol-http': 5.3.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/middleware-endpoint@4.4.23': + dependencies: + '@smithy/core': 3.23.9 + '@smithy/middleware-serde': 4.2.12 + '@smithy/node-config-provider': 4.3.11 + '@smithy/shared-ini-file-loader': 4.4.6 + '@smithy/types': 4.13.0 + '@smithy/url-parser': 4.2.11 + '@smithy/util-middleware': 4.2.11 + tslib: 2.8.1 + + '@smithy/middleware-retry@4.4.40': + dependencies: + '@smithy/node-config-provider': 4.3.11 + '@smithy/protocol-http': 5.3.11 + '@smithy/service-error-classification': 4.2.11 + '@smithy/smithy-client': 4.12.3 + '@smithy/types': 4.13.0 + '@smithy/util-middleware': 4.2.11 + '@smithy/util-retry': 4.2.11 + '@smithy/uuid': 1.1.2 + tslib: 2.8.1 + + '@smithy/middleware-serde@4.2.12': + dependencies: + '@smithy/protocol-http': 5.3.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/middleware-stack@4.2.11': + dependencies: + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/node-config-provider@4.3.11': + dependencies: + '@smithy/property-provider': 4.2.11 + '@smithy/shared-ini-file-loader': 4.4.6 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/node-http-handler@4.4.14': + dependencies: + '@smithy/abort-controller': 4.2.11 + '@smithy/protocol-http': 5.3.11 + '@smithy/querystring-builder': 4.2.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/property-provider@4.2.11': + dependencies: + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/protocol-http@5.3.11': + dependencies: + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/querystring-builder@4.2.11': + dependencies: + '@smithy/types': 4.13.0 + '@smithy/util-uri-escape': 4.2.2 + tslib: 2.8.1 + + '@smithy/querystring-parser@4.2.11': + dependencies: + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/service-error-classification@4.2.11': + dependencies: + '@smithy/types': 4.13.0 + + '@smithy/shared-ini-file-loader@4.4.6': + dependencies: + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/signature-v4@5.3.11': + dependencies: + '@smithy/is-array-buffer': 4.2.2 + '@smithy/protocol-http': 5.3.11 + '@smithy/types': 4.13.0 + '@smithy/util-hex-encoding': 4.2.2 + '@smithy/util-middleware': 4.2.11 + '@smithy/util-uri-escape': 4.2.2 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + + '@smithy/smithy-client@4.12.3': + dependencies: + '@smithy/core': 3.23.9 + '@smithy/middleware-endpoint': 4.4.23 + '@smithy/middleware-stack': 4.2.11 + '@smithy/protocol-http': 5.3.11 + '@smithy/types': 4.13.0 + '@smithy/util-stream': 4.5.17 + tslib: 2.8.1 + + '@smithy/types@4.13.0': + dependencies: + tslib: 2.8.1 + + '@smithy/url-parser@4.2.11': + dependencies: + '@smithy/querystring-parser': 4.2.11 + '@smithy/types': 4.13.0 + 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': + dependencies: + tslib: 2.8.1 + + '@smithy/util-buffer-from@2.2.0': + dependencies: + '@smithy/is-array-buffer': 2.2.0 + tslib: 2.8.1 + + '@smithy/util-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.39': + dependencies: + '@smithy/property-provider': 4.2.11 + '@smithy/smithy-client': 4.12.3 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/util-defaults-mode-node@4.2.42': + dependencies: + '@smithy/config-resolver': 4.4.10 + '@smithy/credential-provider-imds': 4.2.11 + '@smithy/node-config-provider': 4.3.11 + '@smithy/property-provider': 4.2.11 + '@smithy/smithy-client': 4.12.3 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/util-endpoints@3.3.2': + dependencies: + '@smithy/node-config-provider': 4.3.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/util-hex-encoding@4.2.2': + dependencies: + tslib: 2.8.1 + + '@smithy/util-middleware@4.2.11': + dependencies: + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/util-retry@4.2.11': + dependencies: + '@smithy/service-error-classification': 4.2.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/util-stream@4.5.17': + dependencies: + '@smithy/fetch-http-handler': 5.3.13 + '@smithy/node-http-handler': 4.4.14 + '@smithy/types': 4.13.0 + '@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 + + '@snazzah/davey-android-arm-eabi@0.1.10': + optional: true + + '@snazzah/davey-android-arm64@0.1.10': + optional: true + + '@snazzah/davey-darwin-arm64@0.1.10': + optional: true + + '@snazzah/davey-darwin-x64@0.1.10': + optional: true + + '@snazzah/davey-freebsd-x64@0.1.10': + optional: true + + '@snazzah/davey-linux-arm-gnueabihf@0.1.10': + optional: true + + '@snazzah/davey-linux-arm64-gnu@0.1.10': + optional: true + + '@snazzah/davey-linux-arm64-musl@0.1.10': + optional: true + + '@snazzah/davey-linux-x64-gnu@0.1.10': + optional: true + + '@snazzah/davey-linux-x64-musl@0.1.10': + optional: true + + '@snazzah/davey-wasm32-wasi@0.1.10': + dependencies: + '@napi-rs/wasm-runtime': 1.1.1 + optional: true + + '@snazzah/davey-win32-arm64-msvc@0.1.10': + optional: true + + '@snazzah/davey-win32-ia32-msvc@0.1.10': + optional: true + + '@snazzah/davey-win32-x64-msvc@0.1.10': + optional: true + + '@snazzah/davey@0.1.10': + optionalDependencies: + '@snazzah/davey-android-arm-eabi': 0.1.10 + '@snazzah/davey-android-arm64': 0.1.10 + '@snazzah/davey-darwin-arm64': 0.1.10 + '@snazzah/davey-darwin-x64': 0.1.10 + '@snazzah/davey-freebsd-x64': 0.1.10 + '@snazzah/davey-linux-arm-gnueabihf': 0.1.10 + '@snazzah/davey-linux-arm64-gnu': 0.1.10 + '@snazzah/davey-linux-arm64-musl': 0.1.10 + '@snazzah/davey-linux-x64-gnu': 0.1.10 + '@snazzah/davey-linux-x64-musl': 0.1.10 + '@snazzah/davey-wasm32-wasi': 0.1.10 + '@snazzah/davey-win32-arm64-msvc': 0.1.10 + '@snazzah/davey-win32-ia32-msvc': 0.1.10 + '@snazzah/davey-win32-x64-msvc': 0.1.10 + + '@tinyhttp/content-disposition@2.2.4': {} + + '@tokenizer/inflate@0.4.1': + dependencies: + debug: 4.4.3 + token-types: 6.1.2 + transitivePeerDependencies: + - supports-color + + '@tokenizer/token@0.3.0': {} + + '@tootallnate/quickjs-emscripten@0.23.0': {} + + '@tybys/wasm-util@0.10.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@types/aws-lambda@8.10.161': {} + + '@types/body-parser@1.19.6': + dependencies: + '@types/connect': 3.4.38 + '@types/node': 25.3.5 + + '@types/bun@1.3.9': + dependencies: + bun-types: 1.3.9 + optional: true + + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + + '@types/connect@3.4.38': + dependencies: + '@types/node': 25.3.5 + + '@types/deep-eql@4.0.2': {} + + '@types/estree@1.0.8': {} + + '@types/express-serve-static-core@5.1.1': + dependencies: + '@types/node': 25.3.5 + '@types/qs': 6.15.0 + '@types/range-parser': 1.2.7 + '@types/send': 1.2.1 + + '@types/express@5.0.6': + dependencies: + '@types/body-parser': 1.19.6 + '@types/express-serve-static-core': 5.1.1 + '@types/serve-static': 2.2.0 + + '@types/http-errors@2.0.5': {} + + '@types/jsonwebtoken@9.0.10': + dependencies: + '@types/ms': 2.1.0 + '@types/node': 25.3.5 + + '@types/long@4.0.2': {} + + '@types/mime-types@2.1.4': {} + + '@types/ms@2.1.0': {} + + '@types/node@10.17.60': {} + + '@types/node@24.12.0': + dependencies: + undici-types: 7.16.0 + + '@types/node@25.3.5': + dependencies: + undici-types: 7.18.2 + + '@types/qs@6.15.0': {} + + '@types/range-parser@1.2.7': {} + + '@types/retry@0.12.0': {} + + '@types/send@1.2.1': + dependencies: + '@types/node': 25.3.5 + + '@types/serve-static@2.2.0': + dependencies: + '@types/http-errors': 2.0.5 + '@types/node': 25.3.5 + + '@types/ws@8.18.1': + dependencies: + '@types/node': 25.3.5 + + '@types/yauzl@2.10.3': + dependencies: + '@types/node': 25.3.5 + optional: true + + '@vitest/expect@3.2.4': + dependencies: + '@types/chai': 5.2.3 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.3.3 + tinyrainbow: 2.0.0 + + '@vitest/mocker@3.2.4(vite@7.3.1(@types/node@25.3.5)(jiti@2.6.1)(yaml@2.8.2))': + 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.5)(jiti@2.6.1)(yaml@2.8.2) + + '@vitest/pretty-format@3.2.4': + dependencies: + tinyrainbow: 2.0.0 + + '@vitest/runner@3.2.4': + dependencies: + '@vitest/utils': 3.2.4 + pathe: 2.0.3 + strip-literal: 3.1.0 + + '@vitest/snapshot@3.2.4': + dependencies: + '@vitest/pretty-format': 3.2.4 + magic-string: 0.30.21 + pathe: 2.0.3 + + '@vitest/spy@3.2.4': + dependencies: + tinyspy: 4.0.4 + + '@vitest/utils@3.2.4': + dependencies: + '@vitest/pretty-format': 3.2.4 + loupe: 3.2.1 + tinyrainbow: 2.0.0 + + '@whiskeysockets/baileys@7.0.0-rc.9(sharp@0.34.5)': + dependencies: + '@cacheable/node-cache': 1.7.6 + '@hapi/boom': 9.1.4 + async-mutex: 0.5.0 + libsignal: '@whiskeysockets/libsignal-node@https://codeload.github.com/whiskeysockets/libsignal-node/tar.gz/1c30d7d7e76a3b0aa120b04dc6a26f5a12dccf67' + lru-cache: 11.2.6 + music-metadata: 11.12.1 + p-queue: 9.1.0 + pino: 9.14.0 + protobufjs: 7.5.4 + sharp: 0.34.5 + ws: 8.19.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + '@whiskeysockets/libsignal-node@https://codeload.github.com/whiskeysockets/libsignal-node/tar.gz/1c30d7d7e76a3b0aa120b04dc6a26f5a12dccf67': + dependencies: + curve25519-js: 0.0.4 + protobufjs: 6.8.8 + + abbrev@1.1.1: + optional: true + + abort-controller@3.0.0: + dependencies: + event-target-shim: 5.0.1 + + accepts@2.0.0: + dependencies: + mime-types: 3.0.2 + negotiator: 1.0.0 + + agent-base@6.0.2: + dependencies: + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + optional: true + + 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 + fast-uri: 3.1.0 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + ansi-escapes@6.2.1: {} + + ansi-regex@5.0.1: {} + + ansi-regex@6.2.2: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@6.2.3: {} + + any-promise@1.3.0: {} + + aproba@2.1.0: + optional: true + + are-we-there-yet@2.0.0: + dependencies: + delegates: 1.0.0 + readable-stream: 3.6.2 + optional: true + + argparse@2.0.1: {} + + assertion-error@2.0.1: {} + + ast-types@0.13.4: + dependencies: + tslib: 2.8.1 + + async-mutex@0.5.0: + dependencies: + tslib: 2.8.1 + + async-retry@1.3.3: + dependencies: + retry: 0.13.1 + + asynckit@0.4.0: {} + + atomic-sleep@1.0.0: {} + + axios@1.13.6: + dependencies: + follow-redirects: 1.15.11 + form-data: 4.0.5 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + + balanced-match@1.0.2: {} + + balanced-match@4.0.4: {} + + base64-js@1.5.1: {} + + basic-ftp@5.2.0: {} + + before-after-hook@4.0.0: {} + + bignumber.js@9.3.1: {} + + body-parser@2.2.2: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 4.4.3 + http-errors: 2.0.1 + iconv-lite: 0.7.2 + on-finished: 2.4.1 + qs: 6.15.0 + raw-body: 3.0.2 + type-is: 2.0.1 + transitivePeerDependencies: + - supports-color + + boolbase@1.0.0: {} + + bottleneck@2.19.5: {} + + bowser@2.14.1: {} + + brace-expansion@1.1.12: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + optional: true + + brace-expansion@2.0.2: + dependencies: + balanced-match: 1.0.2 + + brace-expansion@5.0.4: + dependencies: + balanced-match: 4.0.4 + + buffer-crc32@0.2.13: {} + + buffer-equal-constant-time@1.0.1: {} + + buffer-from@1.1.2: {} + + bun-types@1.3.9: + dependencies: + '@types/node': 25.3.5 + optional: true + + bytes@3.1.2: {} + + cac@6.7.14: {} + + cacheable@2.3.3: + dependencies: + '@cacheable/memory': 2.0.8 + '@cacheable/utils': 2.4.0 + hookified: 1.15.1 + keyv: 5.6.0 + qified: 0.6.0 + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + chai@5.3.3: + dependencies: + assertion-error: 2.0.1 + check-error: 2.1.3 + deep-eql: 5.0.2 + loupe: 3.2.1 + pathval: 2.0.1 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chalk@5.6.2: {} + + check-error@2.1.3: {} + + chmodrp@1.0.2: {} + + chokidar@5.0.0: + dependencies: + readdirp: 5.0.0 + + chownr@2.0.0: + optional: true + + chownr@3.0.0: {} + + ci-info@4.4.0: {} + + cli-cursor@5.0.0: + dependencies: + restore-cursor: 5.1.0 + + cli-highlight@2.1.11: + dependencies: + chalk: 4.1.2 + highlight.js: 10.7.3 + mz: 2.7.0 + parse5: 5.1.1 + parse5-htmlparser2-tree-adapter: 6.0.1 + yargs: 16.2.0 + + cli-spinners@2.9.2: {} + + cli-spinners@3.4.0: {} + + cliui@7.0.4: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + cmake-js@8.0.0: + dependencies: + debug: 4.4.3 + fs-extra: 11.3.4 + node-api-headers: 1.8.0 + rc: 1.2.8 + semver: 7.7.4 + tar: 7.5.10 + url-join: 4.0.1 + which: 6.0.1 + yargs: 17.7.2 + transitivePeerDependencies: + - supports-color + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + color-support@1.1.3: + optional: true + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + commander@10.0.1: {} + + commander@14.0.3: {} + + concat-map@0.0.1: + optional: true + + console-control-strings@1.1.0: + optional: true + + content-disposition@1.0.1: {} + + content-type@1.0.5: {} + + cookie-signature@1.2.2: {} + + cookie@0.7.2: {} + + core-util-is@1.0.3: {} + + croner@10.0.1: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + css-select@5.2.2: + dependencies: + boolbase: 1.0.0 + css-what: 6.2.2 + domhandler: 5.0.3 + domutils: 3.2.2 + nth-check: 2.1.1 + + css-what@6.2.2: {} + + cssom@0.5.0: {} + + curve25519-js@0.0.4: {} + + data-uri-to-buffer@4.0.1: {} + + data-uri-to-buffer@6.0.2: {} + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + deep-eql@5.0.2: {} + + deep-extend@0.6.0: {} + + degenerator@5.0.1: + dependencies: + ast-types: 0.13.4 + escodegen: 2.1.0 + esprima: 4.0.1 + + delayed-stream@1.0.0: {} + + delegates@1.0.0: + optional: true + + depd@2.0.0: {} + + detect-libc@2.1.2: {} + + diff@8.0.3: {} + + discord-api-types@0.38.37: {} + + discord-api-types@0.38.41: {} + + dom-serializer@2.0.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.5.0 + + domelementtype@2.3.0: {} + + domhandler@5.0.3: + dependencies: + domelementtype: 2.3.0 + + domutils@3.2.2: + dependencies: + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + + dotenv@17.3.1: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + eastasianwidth@0.2.0: {} + + ecdsa-sig-formatter@1.0.11: + dependencies: + safe-buffer: 5.2.1 + + ee-first@1.1.1: {} + + emoji-regex@10.6.0: {} + + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + + encodeurl@2.0.0: {} + + end-of-stream@1.4.5: + dependencies: + once: 1.4.0 + + entities@4.5.0: {} + + entities@7.0.1: {} + + env-var@7.5.0: {} + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-module-lexer@1.7.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + esbuild@0.27.3: + optionalDependencies: + '@esbuild/aix-ppc64': 0.27.3 + '@esbuild/android-arm': 0.27.3 + '@esbuild/android-arm64': 0.27.3 + '@esbuild/android-x64': 0.27.3 + '@esbuild/darwin-arm64': 0.27.3 + '@esbuild/darwin-x64': 0.27.3 + '@esbuild/freebsd-arm64': 0.27.3 + '@esbuild/freebsd-x64': 0.27.3 + '@esbuild/linux-arm': 0.27.3 + '@esbuild/linux-arm64': 0.27.3 + '@esbuild/linux-ia32': 0.27.3 + '@esbuild/linux-loong64': 0.27.3 + '@esbuild/linux-mips64el': 0.27.3 + '@esbuild/linux-ppc64': 0.27.3 + '@esbuild/linux-riscv64': 0.27.3 + '@esbuild/linux-s390x': 0.27.3 + '@esbuild/linux-x64': 0.27.3 + '@esbuild/netbsd-arm64': 0.27.3 + '@esbuild/netbsd-x64': 0.27.3 + '@esbuild/openbsd-arm64': 0.27.3 + '@esbuild/openbsd-x64': 0.27.3 + '@esbuild/openharmony-arm64': 0.27.3 + '@esbuild/sunos-x64': 0.27.3 + '@esbuild/win32-arm64': 0.27.3 + '@esbuild/win32-ia32': 0.27.3 + '@esbuild/win32-x64': 0.27.3 + + escalade@3.2.0: {} + + escape-html@1.0.3: {} + + 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: {} + + eventemitter3@4.0.7: {} + + eventemitter3@5.0.4: {} + + expect-type@1.3.0: {} + + express@5.2.1: + dependencies: + accepts: 2.0.0 + body-parser: 2.2.2 + content-disposition: 1.0.1 + content-type: 1.0.5 + cookie: 0.7.2 + cookie-signature: 1.2.2 + debug: 4.4.3 + depd: 2.0.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 2.1.1 + fresh: 2.0.0 + http-errors: 2.0.1 + merge-descriptors: 2.0.0 + mime-types: 3.0.2 + on-finished: 2.4.1 + once: 1.4.0 + parseurl: 1.3.3 + proxy-addr: 2.0.7 + qs: 6.15.0 + range-parser: 1.2.1 + router: 2.2.0 + send: 1.2.1 + serve-static: 2.2.1 + statuses: 2.0.2 + type-is: 2.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + extend@3.0.2: {} + + extract-zip@2.0.1: + dependencies: + debug: 4.4.3 + get-stream: 5.2.0 + yauzl: 2.10.0 + optionalDependencies: + '@types/yauzl': 2.10.3 + transitivePeerDependencies: + - supports-color + + fast-content-type-parse@3.0.0: {} + + fast-deep-equal@3.1.3: {} + + fast-uri@3.1.0: {} + + fast-xml-builder@1.0.0: {} + + fast-xml-parser@5.4.1: + dependencies: + fast-xml-builder: 1.0.0 + strnum: 2.2.0 + + fd-slicer@1.1.0: + dependencies: + pend: 1.2.0 + + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + + fetch-blob@3.2.0: + dependencies: + node-domexception: 1.0.0 + web-streams-polyfill: 3.3.3 + + file-type@21.3.0: + dependencies: + '@tokenizer/inflate': 0.4.1 + strtok3: 10.3.4 + token-types: 6.1.2 + uint8array-extras: 1.5.0 + transitivePeerDependencies: + - supports-color + + filename-reserved-regex@3.0.0: {} + + filenamify@6.0.0: + dependencies: + filename-reserved-regex: 3.0.0 + + finalhandler@2.1.1: + dependencies: + debug: 4.4.3 + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + follow-redirects@1.15.11: {} + + foreground-child@3.3.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + + form-data@4.0.5: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.2 + mime-types: 2.1.35 + + formdata-polyfill@4.0.10: + dependencies: + fetch-blob: 3.2.0 + + forwarded@0.2.0: {} + + fresh@2.0.0: {} + + fs-extra@11.3.4: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.2.0 + universalify: 2.0.1 + + fs-minipass@2.1.0: + dependencies: + minipass: 3.3.6 + optional: true + + fs.realpath@1.0.0: + optional: true + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + gauge@3.0.2: + dependencies: + aproba: 2.1.0 + color-support: 1.1.3 + console-control-strings: 1.1.0 + has-unicode: 2.0.1 + object-assign: 4.1.1 + signal-exit: 3.0.7 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wide-align: 1.1.5 + optional: true + + gaxios@7.1.3: + dependencies: + extend: 3.0.2 + https-proxy-agent: 7.0.6 + node-fetch: 3.3.2 + rimraf: 5.0.10 + transitivePeerDependencies: + - supports-color + + gcp-metadata@8.1.2: + dependencies: + gaxios: 7.1.3 + google-logging-utils: 1.1.3 + json-bigint: 1.0.0 + transitivePeerDependencies: + - supports-color + + get-caller-file@2.0.5: {} + + get-east-asian-width@1.5.0: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + get-stream@5.2.0: + dependencies: + pump: 3.0.4 + + 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 + + glob@10.5.0: + dependencies: + foreground-child: 3.3.1 + jackspeak: 3.4.3 + minimatch: 9.0.9 + minipass: 7.1.3 + package-json-from-dist: 1.0.1 + path-scurry: 1.11.1 + + glob@13.0.6: + dependencies: + minimatch: 10.2.4 + minipass: 7.1.3 + path-scurry: 2.0.2 + + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.5 + once: 1.4.0 + path-is-absolute: 1.0.1 + optional: true + + google-auth-library@10.6.1: + dependencies: + base64-js: 1.5.1 + ecdsa-sig-formatter: 1.0.11 + gaxios: 7.1.3 + gcp-metadata: 8.1.2 + google-logging-utils: 1.1.3 + jws: 4.0.1 + transitivePeerDependencies: + - supports-color + + google-logging-utils@1.1.3: {} + + gopd@1.2.0: {} + + graceful-fs@4.2.11: {} + + grammy@1.41.1: + dependencies: + '@grammyjs/types': 3.25.0 + abort-controller: 3.0.0 + debug: 4.4.3 + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + - supports-color + + has-flag@4.0.0: {} + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + has-unicode@2.0.1: + optional: true + + hashery@1.5.0: + dependencies: + hookified: 1.15.1 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + highlight.js@10.7.3: {} + + hono@4.12.5: + optional: true + + hookified@1.15.1: {} + + hosted-git-info@9.0.2: + dependencies: + lru-cache: 11.2.6 + + html-escaper@3.0.3: {} + + htmlparser2@10.1.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.2.2 + entities: 7.0.1 + + http-errors@2.0.1: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.2 + toidentifier: 1.0.1 + + http-proxy-agent@7.0.2: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + https-proxy-agent@5.0.1: + dependencies: + agent-base: 6.0.2 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + optional: true + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + iconv-lite@0.7.2: + dependencies: + safer-buffer: 2.1.2 + + ieee754@1.2.1: {} + + ignore@7.0.5: {} + + immediate@3.0.6: {} + + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + optional: true + + inherits@2.0.4: {} + + ini@1.3.8: {} + + ip-address@10.1.0: {} + + ipaddr.js@1.9.1: {} + + ipaddr.js@2.3.0: {} + + ipull@3.9.5: + dependencies: + '@tinyhttp/content-disposition': 2.2.4 + async-retry: 1.3.3 + chalk: 5.6.2 + ci-info: 4.4.0 + cli-spinners: 2.9.2 + commander: 10.0.1 + eventemitter3: 5.0.4 + filenamify: 6.0.0 + fs-extra: 11.3.4 + is-unicode-supported: 2.1.0 + lifecycle-utils: 2.1.0 + lodash.debounce: 4.0.8 + lowdb: 7.0.1 + pretty-bytes: 6.1.1 + pretty-ms: 8.0.0 + sleep-promise: 9.1.0 + slice-ansi: 7.1.2 + stdout-update: 4.0.1 + strip-ansi: 7.2.0 + optionalDependencies: + '@reflink/reflink': 0.1.19 + + is-electron@2.2.2: {} + + is-fullwidth-code-point@3.0.0: {} + + is-fullwidth-code-point@5.1.0: + dependencies: + get-east-asian-width: 1.5.0 + + is-interactive@2.0.0: {} + + is-promise@4.0.0: {} + + is-stream@2.0.1: {} + + is-unicode-supported@2.1.0: {} + + isarray@1.0.0: {} + + isexe@2.0.0: {} + + isexe@4.0.0: {} + + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + + jiti@2.6.1: {} + + js-tokens@9.0.1: {} + + json-bigint@1.0.0: + dependencies: + bignumber.js: 9.3.1 + + json-schema-to-ts@3.1.1: + dependencies: + '@babel/runtime': 7.28.6 + ts-algebra: 2.0.0 + + json-schema-traverse@1.0.0: {} + + json-with-bigint@3.5.7: {} + + json5@2.2.3: {} + + jsonfile@6.2.0: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + + jsonwebtoken@9.0.3: + dependencies: + jws: 4.0.1 + lodash.includes: 4.3.0 + lodash.isboolean: 3.0.3 + lodash.isinteger: 4.0.4 + lodash.isnumber: 3.0.3 + lodash.isplainobject: 4.0.6 + lodash.isstring: 4.0.1 + lodash.once: 4.1.1 + ms: 2.1.3 + semver: 7.7.4 + + jszip@3.10.1: + dependencies: + lie: 3.3.0 + pako: 1.0.11 + readable-stream: 2.3.8 + setimmediate: 1.0.5 + + jwa@2.0.1: + dependencies: + buffer-equal-constant-time: 1.0.1 + ecdsa-sig-formatter: 1.0.11 + safe-buffer: 5.2.1 + + jws@4.0.1: + dependencies: + jwa: 2.0.1 + safe-buffer: 5.2.1 + + keyv@5.6.0: + dependencies: + '@keyv/serialize': 1.1.1 + + koffi@2.15.1: {} + + lie@3.3.0: + dependencies: + immediate: 3.0.6 + + lifecycle-utils@2.1.0: {} + + lifecycle-utils@3.1.1: {} + + linkedom@0.18.12: + dependencies: + css-select: 5.2.2 + cssom: 0.5.0 + html-escaper: 3.0.3 + htmlparser2: 10.1.0 + uhyphen: 0.2.0 + + linkify-it@5.0.0: + dependencies: + uc.micro: 2.1.0 + + lodash.debounce@4.0.8: {} + + lodash.identity@3.0.0: {} + + lodash.includes@4.3.0: {} + + lodash.isboolean@3.0.3: {} + + lodash.isinteger@4.0.4: {} + + lodash.isnumber@3.0.3: {} + + lodash.isplainobject@4.0.6: {} + + lodash.isstring@4.0.1: {} + + lodash.merge@4.6.2: {} + + lodash.once@4.1.1: {} + + lodash.pickby@4.6.0: {} + + log-symbols@7.0.1: + dependencies: + is-unicode-supported: 2.1.0 + yoctocolors: 2.1.2 + + long@4.0.0: {} + + long@5.3.2: {} + + loupe@3.2.1: {} + + lowdb@7.0.1: + dependencies: + steno: 4.0.2 + + lru-cache@10.4.3: {} + + lru-cache@11.2.6: {} + + lru-cache@7.18.3: {} + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + make-dir@3.1.0: + dependencies: + semver: 6.3.1 + optional: true + + markdown-it@14.1.1: + dependencies: + argparse: 2.0.1 + entities: 4.5.0 + linkify-it: 5.0.0 + mdurl: 2.0.0 + punycode.js: 2.3.1 + uc.micro: 2.1.0 + + marked@15.0.12: {} + + math-intrinsics@1.1.0: {} + + mdurl@2.0.0: {} + + media-typer@1.1.0: {} + + merge-descriptors@2.0.0: {} + + mime-db@1.52.0: {} + + mime-db@1.54.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mime-types@3.0.2: + dependencies: + mime-db: 1.54.0 + + mimic-function@5.0.1: {} + + minimatch@10.2.4: + dependencies: + brace-expansion: 5.0.4 + + minimatch@3.1.5: + dependencies: + brace-expansion: 1.1.12 + optional: true + + minimatch@9.0.9: + dependencies: + brace-expansion: 2.0.2 + + minimist@1.2.8: {} + + minipass@3.3.6: + dependencies: + yallist: 4.0.0 + optional: true + + minipass@5.0.0: + optional: true + + minipass@7.1.3: {} + + minizlib@2.1.2: + dependencies: + minipass: 3.3.6 + yallist: 4.0.0 + optional: true + + minizlib@3.1.0: + dependencies: + minipass: 7.1.3 + + mkdirp@1.0.4: + optional: true + + ms@2.1.3: {} + + music-metadata@11.12.1: + dependencies: + '@borewit/text-codec': 0.2.1 + '@tokenizer/token': 0.3.0 + content-type: 1.0.5 + debug: 4.4.3 + file-type: 21.3.0 + media-typer: 1.1.0 + strtok3: 10.3.4 + token-types: 6.1.2 + uint8array-extras: 1.5.0 + win-guid: 0.2.1 + transitivePeerDependencies: + - supports-color + + mz@2.7.0: + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + + nanoid@3.3.11: {} + + nanoid@5.1.6: {} + + negotiator@1.0.0: {} + + netmask@2.0.2: {} + + node-addon-api@8.6.0: {} + + node-api-headers@1.8.0: {} + + node-domexception@1.0.0: {} + + node-edge-tts@1.2.10: + dependencies: + https-proxy-agent: 7.0.6 + ws: 8.19.0 + yargs: 17.7.2 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + + node-fetch@3.3.2: + dependencies: + data-uri-to-buffer: 4.0.1 + fetch-blob: 3.2.0 + formdata-polyfill: 4.0.10 + + node-llama-cpp@3.16.2: + dependencies: + '@huggingface/jinja': 0.5.5 + async-retry: 1.3.3 + bytes: 3.1.2 + chalk: 5.6.2 + chmodrp: 1.0.2 + cmake-js: 8.0.0 + cross-spawn: 7.0.6 + env-var: 7.5.0 + filenamify: 6.0.0 + fs-extra: 11.3.4 + ignore: 7.0.5 + ipull: 3.9.5 + is-unicode-supported: 2.1.0 + lifecycle-utils: 3.1.1 + log-symbols: 7.0.1 + nanoid: 5.1.6 + node-addon-api: 8.6.0 + octokit: 5.0.5 + ora: 9.3.0 + pretty-ms: 9.3.0 + proper-lockfile: 4.1.2 + semver: 7.7.4 + simple-git: 3.32.3 + slice-ansi: 8.0.0 + stdout-update: 4.0.1 + strip-ansi: 7.2.0 + validate-npm-package-name: 7.0.2 + which: 6.0.1 + yargs: 17.7.2 + optionalDependencies: + '@node-llama-cpp/linux-arm64': 3.16.2 + '@node-llama-cpp/linux-armv7l': 3.16.2 + '@node-llama-cpp/linux-x64': 3.16.2 + '@node-llama-cpp/linux-x64-cuda': 3.16.2 + '@node-llama-cpp/linux-x64-cuda-ext': 3.16.2 + '@node-llama-cpp/linux-x64-vulkan': 3.16.2 + '@node-llama-cpp/mac-arm64-metal': 3.16.2 + '@node-llama-cpp/mac-x64': 3.16.2 + '@node-llama-cpp/win-arm64': 3.16.2 + '@node-llama-cpp/win-x64': 3.16.2 + '@node-llama-cpp/win-x64-cuda': 3.16.2 + '@node-llama-cpp/win-x64-cuda-ext': 3.16.2 + '@node-llama-cpp/win-x64-vulkan': 3.16.2 + transitivePeerDependencies: + - supports-color + + node-readable-to-web-readable-stream@0.4.2: + optional: true + + nopt@5.0.0: + dependencies: + abbrev: 1.1.1 + optional: true + + npmlog@5.0.1: + dependencies: + are-we-there-yet: 2.0.0 + console-control-strings: 1.1.0 + gauge: 3.0.2 + set-blocking: 2.0.0 + optional: true + + nth-check@2.1.1: + dependencies: + boolbase: 1.0.0 + + object-assign@4.1.1: {} + + object-inspect@1.13.4: {} + + octokit@5.0.5: + dependencies: + '@octokit/app': 16.1.2 + '@octokit/core': 7.0.6 + '@octokit/oauth-app': 8.0.3 + '@octokit/plugin-paginate-graphql': 6.0.0(@octokit/core@7.0.6) + '@octokit/plugin-paginate-rest': 14.0.0(@octokit/core@7.0.6) + '@octokit/plugin-rest-endpoint-methods': 17.0.0(@octokit/core@7.0.6) + '@octokit/plugin-retry': 8.1.0(@octokit/core@7.0.6) + '@octokit/plugin-throttling': 11.0.3(@octokit/core@7.0.6) + '@octokit/request-error': 7.1.0 + '@octokit/types': 16.0.0 + '@octokit/webhooks': 14.2.0 + + on-exit-leak-free@2.1.2: {} + + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + onetime@7.0.0: + dependencies: + mimic-function: 5.0.1 + + openai@6.10.0(ws@8.19.0)(zod@4.3.6): + optionalDependencies: + ws: 8.19.0 + zod: 4.3.6 + + openclaw@2026.3.2(@napi-rs/canvas@0.1.96)(@types/express@5.0.6)(hono@4.12.5)(node-llama-cpp@3.16.2): + dependencies: + '@agentclientprotocol/sdk': 0.14.1(zod@4.3.6) + '@aws-sdk/client-bedrock': 3.1004.0 + '@buape/carbon': 0.0.0-beta-20260216184201(@discordjs/opus@0.10.0)(hono@4.12.5)(opusscript@0.1.1) + '@clack/prompts': 1.1.0 + '@discordjs/voice': 0.19.0(@discordjs/opus@0.10.0)(opusscript@0.1.1) + '@grammyjs/runner': 2.0.3(grammy@1.41.1) + '@grammyjs/transformer-throttler': 1.2.1(grammy@1.41.1) + '@homebridge/ciao': 1.3.5 + '@larksuiteoapi/node-sdk': 1.59.0 + '@line/bot-sdk': 10.6.0 + '@lydell/node-pty': 1.2.0-beta.3 + '@mariozechner/pi-agent-core': 0.55.3(ws@8.19.0)(zod@4.3.6) + '@mariozechner/pi-ai': 0.55.3(ws@8.19.0)(zod@4.3.6) + '@mariozechner/pi-coding-agent': 0.55.3(ws@8.19.0)(zod@4.3.6) + '@mariozechner/pi-tui': 0.55.3 + '@mozilla/readability': 0.6.0 + '@napi-rs/canvas': 0.1.96 + '@sinclair/typebox': 0.34.48 + '@slack/bolt': 4.6.0(@types/express@5.0.6) + '@slack/web-api': 7.14.1 + '@snazzah/davey': 0.1.10 + '@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.41 + dotenv: 17.3.1 + express: 5.2.1 + file-type: 21.3.0 + gaxios: 7.1.3 + google-auth-library: 10.6.1 + grammy: 1.41.1 + https-proxy-agent: 7.0.6 + ipaddr.js: 2.3.0 + 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-domexception: '@nolyfill/domexception@1.0.28' + node-edge-tts: 1.2.10 + node-llama-cpp: 3.16.2 + opusscript: 0.1.1 + osc-progress: 0.3.0 + pdfjs-dist: 5.5.207 + playwright-core: 1.58.2 + qrcode-terminal: 0.12.0 + sharp: 0.34.5 + sqlite-vec: 0.1.7-alpha.2 + strip-ansi: 7.2.0 + tar: 7.5.9 + tslog: 4.10.2 + undici: 7.22.0 + ws: 8.19.0 + yaml: 2.8.2 + zod: 4.3.6 + optionalDependencies: + '@discordjs/opus': 0.10.0 + 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 + + opusscript@0.1.1: {} + + ora@9.3.0: + dependencies: + chalk: 5.6.2 + cli-cursor: 5.0.0 + cli-spinners: 3.4.0 + is-interactive: 2.0.0 + is-unicode-supported: 2.1.0 + log-symbols: 7.0.1 + stdin-discarder: 0.3.1 + string-width: 8.2.0 + + osc-progress@0.3.0: {} + + p-finally@1.0.0: {} + + p-queue@6.6.2: + dependencies: + eventemitter3: 4.0.7 + p-timeout: 3.2.0 + + p-queue@9.1.0: + dependencies: + eventemitter3: 5.0.4 + p-timeout: 7.0.1 + + p-retry@4.6.2: + dependencies: + '@types/retry': 0.12.0 + retry: 0.13.1 + + p-timeout@3.2.0: + dependencies: + p-finally: 1.0.0 + + 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: {} + + parse-ms@3.0.0: {} + + parse-ms@4.0.0: {} + + parse5-htmlparser2-tree-adapter@6.0.1: + dependencies: + parse5: 6.0.1 + + parse5@5.1.1: {} + + parse5@6.0.1: {} + + parseurl@1.3.3: {} + + partial-json@0.1.7: {} + + path-is-absolute@1.0.1: + optional: true + + path-key@3.1.1: {} + + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.3 + + path-scurry@2.0.2: + dependencies: + lru-cache: 11.2.6 + minipass: 7.1.3 + + path-to-regexp@8.3.0: {} + + pathe@2.0.3: {} + + pathval@2.0.1: {} + + pdfjs-dist@5.5.207: + optionalDependencies: + '@napi-rs/canvas': 0.1.96 + node-readable-to-web-readable-stream: 0.4.2 + + pend@1.2.0: {} + + picocolors@1.1.1: {} + + picomatch@4.0.3: {} + + pino-abstract-transport@2.0.0: + dependencies: + split2: 4.2.0 + + pino-std-serializers@7.1.0: {} + + pino@9.14.0: + dependencies: + '@pinojs/redact': 0.4.0 + atomic-sleep: 1.0.0 + on-exit-leak-free: 2.1.2 + pino-abstract-transport: 2.0.0 + pino-std-serializers: 7.1.0 + process-warning: 5.0.0 + quick-format-unescaped: 4.0.4 + real-require: 0.2.0 + safe-stable-stringify: 2.5.0 + sonic-boom: 4.2.1 + thread-stream: 3.1.0 + + playwright-core@1.58.2: {} + + postcss@8.5.8: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + pretty-bytes@6.1.1: {} + + pretty-ms@8.0.0: + dependencies: + parse-ms: 3.0.0 + + pretty-ms@9.3.0: + dependencies: + parse-ms: 4.0.0 + + prism-media@1.3.5(@discordjs/opus@0.10.0)(opusscript@0.1.1): + optionalDependencies: + '@discordjs/opus': 0.10.0 + opusscript: 0.1.1 + + process-nextick-args@2.0.1: {} + + process-warning@5.0.0: {} + + proper-lockfile@4.1.2: + dependencies: + graceful-fs: 4.2.11 + retry: 0.12.0 + signal-exit: 3.0.7 + + protobufjs@6.8.8: + 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/float': 1.0.2 + '@protobufjs/inquire': 1.1.0 + '@protobufjs/path': 1.1.2 + '@protobufjs/pool': 1.1.0 + '@protobufjs/utf8': 1.1.0 + '@types/long': 4.0.2 + '@types/node': 10.17.60 + long: 4.0.0 + + protobufjs@7.5.4: + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/base64': 1.1.2 + '@protobufjs/codegen': 2.0.4 + '@protobufjs/eventemitter': 1.1.0 + '@protobufjs/fetch': 1.1.0 + '@protobufjs/float': 1.0.2 + '@protobufjs/inquire': 1.1.0 + '@protobufjs/path': 1.1.2 + '@protobufjs/pool': 1.1.0 + '@protobufjs/utf8': 1.1.0 + '@types/node': 25.3.5 + long: 5.3.2 + + proxy-addr@2.0.7: + dependencies: + 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: {} + + pump@3.0.4: + dependencies: + end-of-stream: 1.4.5 + once: 1.4.0 + + punycode.js@2.3.1: {} + + qified@0.6.0: + dependencies: + hookified: 1.15.1 + + qrcode-terminal@0.12.0: {} + + qs@6.15.0: + dependencies: + side-channel: 1.1.0 + + quick-format-unescaped@4.0.4: {} + + range-parser@1.2.1: {} + + raw-body@3.0.2: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.1 + iconv-lite: 0.7.2 + unpipe: 1.0.0 + + rc@1.2.8: + dependencies: + deep-extend: 0.6.0 + ini: 1.3.8 + minimist: 1.2.8 + strip-json-comments: 2.0.1 + + readable-stream@2.3.8: + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + optional: true + + readdirp@5.0.0: {} + + real-require@0.2.0: {} + + require-directory@2.1.1: {} + + require-from-string@2.0.2: {} + + restore-cursor@5.1.0: + dependencies: + onetime: 7.0.0 + signal-exit: 4.1.0 + + retry@0.12.0: {} + + retry@0.13.1: {} + + rimraf@3.0.2: + dependencies: + glob: 7.2.3 + optional: true + + rimraf@5.0.10: + dependencies: + glob: 10.5.0 + + rollup@4.59.0: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.59.0 + '@rollup/rollup-android-arm64': 4.59.0 + '@rollup/rollup-darwin-arm64': 4.59.0 + '@rollup/rollup-darwin-x64': 4.59.0 + '@rollup/rollup-freebsd-arm64': 4.59.0 + '@rollup/rollup-freebsd-x64': 4.59.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.59.0 + '@rollup/rollup-linux-arm-musleabihf': 4.59.0 + '@rollup/rollup-linux-arm64-gnu': 4.59.0 + '@rollup/rollup-linux-arm64-musl': 4.59.0 + '@rollup/rollup-linux-loong64-gnu': 4.59.0 + '@rollup/rollup-linux-loong64-musl': 4.59.0 + '@rollup/rollup-linux-ppc64-gnu': 4.59.0 + '@rollup/rollup-linux-ppc64-musl': 4.59.0 + '@rollup/rollup-linux-riscv64-gnu': 4.59.0 + '@rollup/rollup-linux-riscv64-musl': 4.59.0 + '@rollup/rollup-linux-s390x-gnu': 4.59.0 + '@rollup/rollup-linux-x64-gnu': 4.59.0 + '@rollup/rollup-linux-x64-musl': 4.59.0 + '@rollup/rollup-openbsd-x64': 4.59.0 + '@rollup/rollup-openharmony-arm64': 4.59.0 + '@rollup/rollup-win32-arm64-msvc': 4.59.0 + '@rollup/rollup-win32-ia32-msvc': 4.59.0 + '@rollup/rollup-win32-x64-gnu': 4.59.0 + '@rollup/rollup-win32-x64-msvc': 4.59.0 + fsevents: 2.3.3 + + router@2.2.0: + dependencies: + debug: 4.4.3 + depd: 2.0.0 + is-promise: 4.0.0 + parseurl: 1.3.3 + path-to-regexp: 8.3.0 + transitivePeerDependencies: + - supports-color + + safe-buffer@5.1.2: {} + + safe-buffer@5.2.1: {} + + safe-stable-stringify@2.5.0: {} + + safer-buffer@2.1.2: {} + + semver@6.3.1: + optional: true + + semver@7.7.4: {} + + send@1.2.1: + dependencies: + debug: 4.4.3 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 2.0.0 + http-errors: 2.0.1 + mime-types: 3.0.2 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + serve-static@2.2.1: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 1.2.1 + transitivePeerDependencies: + - supports-color + + set-blocking@2.0.0: + optional: true + + setimmediate@1.0.5: {} + + setprototypeof@1.2.0: {} + + sharp@0.34.5: + dependencies: + '@img/colour': 1.1.0 + detect-libc: 2.1.2 + semver: 7.7.4 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.34.5 + '@img/sharp-darwin-x64': 0.34.5 + '@img/sharp-libvips-darwin-arm64': 1.2.4 + '@img/sharp-libvips-darwin-x64': 1.2.4 + '@img/sharp-libvips-linux-arm': 1.2.4 + '@img/sharp-libvips-linux-arm64': 1.2.4 + '@img/sharp-libvips-linux-ppc64': 1.2.4 + '@img/sharp-libvips-linux-riscv64': 1.2.4 + '@img/sharp-libvips-linux-s390x': 1.2.4 + '@img/sharp-libvips-linux-x64': 1.2.4 + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 + '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + '@img/sharp-linux-arm': 0.34.5 + '@img/sharp-linux-arm64': 0.34.5 + '@img/sharp-linux-ppc64': 0.34.5 + '@img/sharp-linux-riscv64': 0.34.5 + '@img/sharp-linux-s390x': 0.34.5 + '@img/sharp-linux-x64': 0.34.5 + '@img/sharp-linuxmusl-arm64': 0.34.5 + '@img/sharp-linuxmusl-x64': 0.34.5 + '@img/sharp-wasm32': 0.34.5 + '@img/sharp-win32-arm64': 0.34.5 + '@img/sharp-win32-ia32': 0.34.5 + '@img/sharp-win32-x64': 0.34.5 + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + side-channel-list@1.0.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.0 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + siginfo@2.0.0: {} + + signal-exit@3.0.7: {} + + signal-exit@4.1.0: {} + + simple-git@3.32.3: + dependencies: + '@kwsites/file-exists': 1.1.1 + '@kwsites/promise-deferred': 1.1.1 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + sisteransi@1.0.5: {} + + sleep-promise@9.1.0: {} + + slice-ansi@7.1.2: + dependencies: + ansi-styles: 6.2.3 + is-fullwidth-code-point: 5.1.0 + + slice-ansi@8.0.0: + dependencies: + 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 + + source-map-js@1.2.1: {} + + source-map-support@0.5.21: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map@0.6.1: {} + + split2@4.2.0: {} + + sqlite-vec-darwin-arm64@0.1.7-alpha.2: + optional: true + + sqlite-vec-darwin-x64@0.1.7-alpha.2: + optional: true + + sqlite-vec-linux-arm64@0.1.7-alpha.2: + optional: true + + sqlite-vec-linux-x64@0.1.7-alpha.2: + optional: true + + sqlite-vec-windows-x64@0.1.7-alpha.2: + optional: true + + sqlite-vec@0.1.7-alpha.2: + optionalDependencies: + sqlite-vec-darwin-arm64: 0.1.7-alpha.2 + sqlite-vec-darwin-x64: 0.1.7-alpha.2 + sqlite-vec-linux-arm64: 0.1.7-alpha.2 + sqlite-vec-linux-x64: 0.1.7-alpha.2 + sqlite-vec-windows-x64: 0.1.7-alpha.2 + + stackback@0.0.2: {} + + statuses@2.0.2: {} + + std-env@3.10.0: {} + + stdin-discarder@0.3.1: {} + + stdout-update@4.0.1: + dependencies: + ansi-escapes: 6.2.1 + ansi-styles: 6.2.3 + string-width: 7.2.0 + strip-ansi: 7.2.0 + + steno@4.0.2: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.2.0 + + string-width@7.2.0: + dependencies: + emoji-regex: 10.6.0 + get-east-asian-width: 1.5.0 + strip-ansi: 7.2.0 + + string-width@8.2.0: + dependencies: + get-east-asian-width: 1.5.0 + strip-ansi: 7.2.0 + + string_decoder@1.1.1: + dependencies: + safe-buffer: 5.1.2 + + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + optional: true + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.2.0: + dependencies: + ansi-regex: 6.2.2 + + strip-json-comments@2.0.1: {} + + strip-literal@3.1.0: + dependencies: + js-tokens: 9.0.1 + + strnum@2.2.0: {} + + strtok3@10.3.4: + dependencies: + '@tokenizer/token': 0.3.0 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + tar@6.2.1: + dependencies: + chownr: 2.0.0 + fs-minipass: 2.1.0 + minipass: 5.0.0 + minizlib: 2.1.2 + mkdirp: 1.0.4 + yallist: 4.0.0 + optional: true + + tar@7.5.10: + dependencies: + '@isaacs/fs-minipass': 4.0.1 + chownr: 3.0.0 + minipass: 7.1.3 + minizlib: 3.1.0 + yallist: 5.0.0 + + tar@7.5.9: + dependencies: + '@isaacs/fs-minipass': 4.0.1 + chownr: 3.0.0 + minipass: 7.1.3 + minizlib: 3.1.0 + yallist: 5.0.0 + + thenify-all@1.6.0: + dependencies: + thenify: 3.3.1 + + thenify@3.3.1: + dependencies: + any-promise: 1.3.0 + + thread-stream@3.1.0: + dependencies: + real-require: 0.2.0 + + tinybench@2.9.0: {} + + tinyexec@0.3.2: {} + + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + + tinypool@1.1.1: {} + + tinyrainbow@2.0.0: {} + + tinyspy@4.0.4: {} + + toad-cache@3.7.0: {} + + toidentifier@1.0.1: {} + + token-types@6.1.2: + dependencies: + '@borewit/text-codec': 0.2.1 + '@tokenizer/token': 0.3.0 + ieee754: 1.2.1 + + tr46@0.0.3: {} + + ts-algebra@2.0.0: {} + + tslib@2.8.1: {} + + tslog@4.10.2: {} + + tsscmp@1.0.6: {} + + type-is@2.0.1: + dependencies: + content-type: 1.0.5 + media-typer: 1.1.0 + mime-types: 3.0.2 + + uc.micro@2.1.0: {} + + uhyphen@0.2.0: {} + + uint8array-extras@1.5.0: {} + + undici-types@7.16.0: {} + + undici-types@7.18.2: {} + + undici@7.22.0: {} + + universal-github-app-jwt@2.2.2: {} + + universal-user-agent@7.0.3: {} + + universalify@2.0.1: {} + + unpipe@1.0.0: {} + + url-join@4.0.1: {} + + util-deprecate@1.0.2: {} + + validate-npm-package-name@7.0.2: {} + + vary@1.1.2: {} + + vite-node@3.2.4(@types/node@25.3.5)(jiti@2.6.1)(yaml@2.8.2): + 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.5)(jiti@2.6.1)(yaml@2.8.2) + transitivePeerDependencies: + - '@types/node' + - jiti + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + vite@7.3.1(@types/node@25.3.5)(jiti@2.6.1)(yaml@2.8.2): + dependencies: + esbuild: 0.27.3 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.8 + rollup: 4.59.0 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 25.3.5 + fsevents: 2.3.3 + jiti: 2.6.1 + yaml: 2.8.2 + + vitest@3.2.4(@types/node@25.3.5)(jiti@2.6.1)(yaml@2.8.2): + dependencies: + '@types/chai': 5.2.3 + '@vitest/expect': 3.2.4 + '@vitest/mocker': 3.2.4(vite@7.3.1(@types/node@25.3.5)(jiti@2.6.1)(yaml@2.8.2)) + '@vitest/pretty-format': 3.2.4 + '@vitest/runner': 3.2.4 + '@vitest/snapshot': 3.2.4 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.3.3 + debug: 4.4.3 + expect-type: 1.3.0 + magic-string: 0.30.21 + pathe: 2.0.3 + picomatch: 4.0.3 + std-env: 3.10.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.15 + tinypool: 1.1.1 + tinyrainbow: 2.0.0 + vite: 7.3.1(@types/node@25.3.5)(jiti@2.6.1)(yaml@2.8.2) + vite-node: 3.2.4(@types/node@25.3.5)(jiti@2.6.1)(yaml@2.8.2) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 25.3.5 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + web-streams-polyfill@3.3.3: {} + + webidl-conversions@3.0.1: {} + + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + which@6.0.1: + dependencies: + isexe: 4.0.0 + + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + + wide-align@1.1.5: + dependencies: + string-width: 4.2.3 + optional: true + + win-guid@0.2.1: {} + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.3 + string-width: 5.1.2 + strip-ansi: 7.2.0 + + wrappy@1.0.2: {} + + ws@8.19.0: {} + + y18n@5.0.8: {} + + yallist@4.0.0: + optional: true + + yallist@5.0.0: {} + + yaml@2.8.2: {} + + yargs-parser@20.2.9: {} + + yargs-parser@21.1.1: {} + + yargs@16.2.0: + dependencies: + cliui: 7.0.4 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 20.2.9 + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + yauzl@2.10.0: + dependencies: + buffer-crc32: 0.2.13 + fd-slicer: 1.1.0 + + 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.76: {} + + zod@4.3.6: {} diff --git a/extensions/braintrust/src/index.test.ts b/extensions/braintrust/src/index.test.ts index 8867892..0179eb8 100644 --- a/extensions/braintrust/src/index.test.ts +++ b/extensions/braintrust/src/index.test.ts @@ -88,13 +88,13 @@ describe("braintrust plugin runtime integration", () => { plugin.register(api as never); const command = commands[0] as { handler: (ctx: { args?: string }) => Promise<{ text: string }> }; - await command.handler({ args: "solver openai/solver-x" }); - await command.handler({ args: "critic anthropic/critic-y" }); - await command.handler({ args: "researcher xai/researcher-z" }); + await command.handler({ args: "solver solver-model-override" }); + await command.handler({ args: "critic critic-model-override" }); + await command.handler({ args: "researcher researcher-model-override" }); const beforePromptBuild = hooks.get("before_prompt_build"); const out = await beforePromptBuild!({ - event: { model: "openai/chat-main", messages: [{ role: "user", content: "What is 2+2?" }] }, + event: { model: "runtime-chat-model", messages: [{ role: "user", content: "What is 2+2?" }] }, context: { runModel: async ({ model, role }: { model: string; role: string }) => { calls.push({ model, role }); @@ -106,10 +106,10 @@ describe("braintrust plugin runtime integration", () => { }); expect(calls).toEqual([ - { model: "openai/solver-x", role: "solver" }, - { model: "anthropic/critic-y", role: "critic" }, - { model: "xai/researcher-z", role: "researcher" }, - { model: "openai/chat-main", role: "synthesizer" }, + { model: "solver-model-override", role: "solver" }, + { model: "critic-model-override", role: "critic" }, + { model: "researcher-model-override", role: "researcher" }, + { model: "runtime-chat-model", role: "synthesizer" }, ]); expect(out?.prependContext).toContain("4"); }); diff --git a/extensions/braintrust/src/policy.test.ts b/extensions/braintrust/src/policy.test.ts index a219cdf..a1c228b 100644 --- a/extensions/braintrust/src/policy.test.ts +++ b/extensions/braintrust/src/policy.test.ts @@ -4,10 +4,10 @@ import { buildUnavailableNotice, evaluateQuorum } from "./policy.js"; describe("evaluateQuorum", () => { it("passes when at least 2 agents answer", () => { const result = evaluateQuorum([ - { id: "a", model: "sonnet", status: "ok" }, - { id: "b", model: "gpt", status: "ok" }, - { id: "c", model: "grok", status: "timeout" }, - { id: "d", model: "gemini", status: "refusal" }, + { id: "a", model: "model-a", status: "ok" }, + { id: "b", model: "model-b", status: "ok" }, + { id: "c", model: "model-c", status: "timeout" }, + { id: "d", model: "model-d", status: "refusal" }, ]); expect(result.meetsQuorum).toBe(true); expect(result.participating).toBe(3); @@ -16,9 +16,9 @@ describe("evaluateQuorum", () => { it("fails when only 1 agent participated", () => { const result = evaluateQuorum([ - { id: "a", model: "sonnet", status: "ok" }, - { id: "b", model: "gpt", status: "timeout" }, - { id: "c", model: "grok", status: "error" }, + { id: "a", model: "model-a", status: "ok" }, + { id: "b", model: "model-b", status: "timeout" }, + { id: "c", model: "model-c", status: "error" }, ]); expect(result.meetsQuorum).toBe(false); expect(result.reason).toContain("participated"); @@ -27,9 +27,9 @@ describe("evaluateQuorum", () => { it("fails when participation is enough but answering is too low", () => { const result = evaluateQuorum([ - { id: "a", model: "sonnet", status: "refusal" }, - { id: "b", model: "gpt", status: "ok" }, - { id: "c", model: "grok", status: "refusal" }, + { id: "a", model: "model-a", status: "refusal" }, + { id: "b", model: "model-b", status: "ok" }, + { id: "c", model: "model-c", status: "refusal" }, ]); expect(result.meetsQuorum).toBe(false); expect(result.reason).toContain("usable answers"); diff --git a/extensions/braintrust/src/policy.ts b/extensions/braintrust/src/policy.ts index 0e711ed..dd35018 100644 --- a/extensions/braintrust/src/policy.ts +++ b/extensions/braintrust/src/policy.ts @@ -2,6 +2,7 @@ export type CandidateStatus = "ok" | "refusal" | "timeout" | "error"; export type Candidate = { id: string; + role?: "solver" | "critic" | "researcher" | "synthesizer"; model: string; status: CandidateStatus; text?: string; diff --git a/extensions/braintrust/src/runtime-bridge.integration.test.ts b/extensions/braintrust/src/runtime-bridge.integration.test.ts index 5188872..e907bc9 100644 --- a/extensions/braintrust/src/runtime-bridge.integration.test.ts +++ b/extensions/braintrust/src/runtime-bridge.integration.test.ts @@ -3,7 +3,7 @@ import { runRuntimeBridge } from "./runtime-bridge.js"; import { DEFAULT_SETTINGS } from "./settings.js"; describe("runtime bridge integration flow", () => { - it("handles timeout + refusal plumbing and still synthesizes deterministic winner", async () => { + it("handles timeout + refusal plumbing and still uses structured synthesis when available", async () => { const settings = { ...DEFAULT_SETTINGS, enabled: true, @@ -15,29 +15,38 @@ describe("runtime bridge integration flow", () => { const started = Date.now(); const result = await runRuntimeBridge( - { prompt: "Build a rollout plan", settings, activeChatModel: "openai/gpt-active-chat" }, - async ({ role, model }) => { + { prompt: "Build a rollout plan", settings, activeChatModel: "runtime-chat-model" }, + async ({ role, model, prompt }) => { if (role === "critic") { - // Should be auto-classified as refusal by text even without refusal flag. return { text: "As an AI, I cannot provide this." }; } if (role === "researcher") { - // Should be converted into timeout by runtime bridge guardrail. await new Promise((resolve) => setTimeout(resolve, 1500)); return { text: "late answer" }; } - // 4th worker uses the active chat model for synthesis. - if (model === "openai/gpt-active-chat") return { text: "brief" }; + if (model === "runtime-chat-model") { + expect(prompt).toContain("agent-1 (solver, ok)"); + expect(prompt).toContain("agent-2 (critic, refusal)"); + expect(prompt).toContain("agent-3 (researcher, timeout)"); + return { + text: JSON.stringify({ + final: "brief", + reasoning: "trusted the surviving usable answer", + sources: ["agent-1"], + }), + }; + } return { text: "solid plan" }; }, ); const tookMs = Date.now() - started; - expect(tookMs).toBeLessThan(1400); + expect(tookMs).toBeLessThan(2600); expect(result.unavailable).toBe(false); expect(result.final).toBe("brief"); expect(result.synthesis?.winnerId).toBe("agent-4"); + expect(result.synthesis?.sources).toEqual(["agent-1"]); const byId = Object.fromEntries(result.candidates.map((c) => [c.id, c])); expect(byId["agent-2"].status).toBe("refusal"); diff --git a/extensions/braintrust/src/runtime-bridge.test.ts b/extensions/braintrust/src/runtime-bridge.test.ts index 9ef2469..c9404e3 100644 --- a/extensions/braintrust/src/runtime-bridge.test.ts +++ b/extensions/braintrust/src/runtime-bridge.test.ts @@ -30,7 +30,7 @@ describe("runRuntimeBridge", () => { async ({ role }) => { if (role === "solver") return { text: "ok", refusal: false }; if (role === "critic") throw new Error("timeout after 90s"); - throw new Error("provider failed"); + throw new Error("runtime execution failed"); }, ); expect(result.unavailable).toBe(true); @@ -77,34 +77,49 @@ describe("runRuntimeBridge", () => { expect(result.candidates.filter((c) => c.status === "refusal")).toHaveLength(2); expect(result.unavailable).toBe(true); }); -}); - -it("uses active chat model for the synthesizer slot", async () => { - const settings = { - ...DEFAULT_SETTINGS, - enabled: true, - teamSize: 4, - model: "gemini-3-flash-preview", - criticModel: "openai-codex/gpt-5.3-codex", - researcherModel: "grok-4-1-fast-reasoning", - synthModel: "gemini-3.1-pro-preview", - minParticipatingAgents: 2, - minAnsweringAgents: 2, - }; - const seen: string[] = []; - const result = await runRuntimeBridge( - { prompt: "hello", settings, activeChatModel: "openai/gpt-active-chat" }, - async ({ model }) => { - seen.push(model); - return { text: `ok-${model}`, refusal: false }; - }, - ); - expect(result.unavailable).toBe(false); - expect(seen).toEqual([ - "gemini-3-flash-preview", - "openai-codex/gpt-5.3-codex", - "grok-4-1-fast-reasoning", - "openai/gpt-active-chat", - ]); + it("uses active chat model for the synthesizer slot and passes worker outputs into synthesis", async () => { + const settings = { + ...DEFAULT_SETTINGS, + enabled: true, + teamSize: 4, + model: "model-a", + criticModel: "model-b", + researcherModel: "model-c", + synthModel: "legacy-synth-model", + minParticipatingAgents: 2, + minAnsweringAgents: 2, + }; + const seen: Array<{ model: string; role: string; prompt: string }> = []; + const result = await runRuntimeBridge( + { prompt: "hello", settings, activeChatModel: "runtime-chat-model" }, + async ({ model, role, prompt }) => { + seen.push({ model, role, prompt }); + if (role === "synthesizer") { + expect(prompt).toContain("agent-1 (solver, ok)"); + expect(prompt).toContain("agent-2 (critic, ok)"); + expect(prompt).toContain("agent-3 (researcher, ok)"); + return { + text: JSON.stringify({ + final: "synthesized answer", + reasoning: "combined the worker outputs", + sources: ["agent-1", "agent-3"], + }), + refusal: false, + }; + } + return { text: `ok-${role}`, refusal: false }; + }, + ); + expect(result.unavailable).toBe(false); + expect(result.final).toBe("synthesized answer"); + expect(result.synthesis?.winnerId).toBe("agent-4"); + expect(result.synthesis?.sources).toEqual(["agent-1", "agent-3"]); + expect(seen.map(({ model, role }) => ({ model, role }))).toEqual([ + { model: "model-a", role: "solver" }, + { model: "model-b", role: "critic" }, + { model: "model-c", role: "researcher" }, + { model: "runtime-chat-model", role: "synthesizer" }, + ]); + }); }); diff --git a/extensions/braintrust/src/runtime-bridge.ts b/extensions/braintrust/src/runtime-bridge.ts index 0ee73bb..af64d60 100644 --- a/extensions/braintrust/src/runtime-bridge.ts +++ b/extensions/braintrust/src/runtime-bridge.ts @@ -1,9 +1,10 @@ import { buildUnavailableNotice, evaluateQuorum, type Candidate, type CandidateStatus } from "./policy.js"; import type { BraintrustSettings } from "./settings.js"; import { synthesizeDeterministic, type SynthesisOutput } from "./synth.js"; +import { buildWorkerPlan, type WorkerRole } from "./worker-plan.js"; export type CandidateRunnerInput = { - role: "solver" | "critic" | "researcher" | "synthesizer"; + role: WorkerRole; model: string; prompt: string; timeoutSeconds: number; @@ -31,20 +32,6 @@ export type RuntimeBridgeResult = { synthesis?: SynthesisOutput; }; -function roleFor(index: number): CandidateRunnerInput["role"] { - if (index === 1) return "critic"; - if (index === 2) return "researcher"; - if (index === 3) return "synthesizer"; - return "solver"; -} - -function modelFor(index: number, settings: BraintrustSettings, activeChatModel?: string): string { - if (index === 3) return activeChatModel?.trim() || settings.model; - if (index === 1) return settings.criticModel; - if (index === 2) return settings.researcherModel; - return settings.model; -} - function classifyError(error: unknown): CandidateStatus { const msg = String(error ?? "").toLowerCase(); if (msg.includes("timeout")) return "timeout"; @@ -85,42 +72,98 @@ export async function runRuntimeBridge( input: RuntimeBridgeInput, runCandidate: CandidateRunner, ): Promise { - const tasks = Array.from({ length: input.settings.teamSize }, async (_, i) => { - const id = `agent-${i + 1}`; - const model = modelFor(i, input.settings, input.activeChatModel); - const started = Date.now(); - try { - const out = await withTimeout( - runCandidate({ - role: roleFor(i), - model, - prompt: i === 3 - ? `${input.prompt}\n\nRespond ONLY with valid JSON in this exact format (no extra text, no markdown):\n{\n "final": "the single best synthesized answer",\n "reasoning": "brief explanation of how you combined the inputs",\n "sources": ["list of agent ids used"]\n}` - : input.prompt, - timeoutSeconds: input.settings.timeoutSeconds, - }), - input.settings.timeoutSeconds, - ); - const latencyMs = out.latencyMs ?? Date.now() - started; - const refusal = out.refusal ?? isLikelyRefusal(out.text); - return { - id, - model, - status: refusal ? "refusal" : "ok", - text: out.text, - latencyMs, - } as Candidate; - } catch (err) { - return { - id, - model, - status: classifyError(err), - latencyMs: Date.now() - started, - } as Candidate; - } + const initialWorkers = buildWorkerPlan({ + prompt: input.prompt, + settings: input.settings, + activeChatModel: input.activeChatModel, }); - const candidates = await Promise.all(tasks); + const nonSynthWorkers = initialWorkers.filter((worker) => worker.role !== "synthesizer"); + const initialCandidates = await Promise.all( + nonSynthWorkers.map(async (worker) => { + const started = Date.now(); + try { + const out = await withTimeout( + runCandidate({ + role: worker.role, + model: worker.model, + prompt: worker.prompt, + timeoutSeconds: input.settings.timeoutSeconds, + }), + input.settings.timeoutSeconds, + ); + const latencyMs = out.latencyMs ?? Date.now() - started; + const refusal = out.refusal ?? isLikelyRefusal(out.text); + return { + id: worker.id, + role: worker.role, + model: worker.model, + status: refusal ? "refusal" : "ok", + text: out.text, + latencyMs, + } as Candidate; + } catch (err) { + return { + id: worker.id, + role: worker.role, + model: worker.model, + status: classifyError(err), + latencyMs: Date.now() - started, + } as Candidate; + } + }), + ); + + const synthWorker = initialWorkers.find((worker) => worker.role === "synthesizer"); + const candidates = [...initialCandidates]; + + if (synthWorker) { + const synthPlan = buildWorkerPlan({ + prompt: input.prompt, + settings: input.settings, + activeChatModel: input.activeChatModel, + completedWorkers: candidates.map((candidate) => ({ + id: candidate.id, + role: (candidate.role ?? "solver") as WorkerRole, + text: candidate.text, + status: candidate.status, + })), + }).find((worker) => worker.role === "synthesizer"); + + if (synthPlan) { + const started = Date.now(); + try { + const out = await withTimeout( + runCandidate({ + role: synthPlan.role, + model: synthPlan.model, + prompt: synthPlan.prompt, + timeoutSeconds: input.settings.timeoutSeconds, + }), + input.settings.timeoutSeconds, + ); + const latencyMs = out.latencyMs ?? Date.now() - started; + const refusal = out.refusal ?? isLikelyRefusal(out.text); + candidates.push({ + id: synthPlan.id, + role: synthPlan.role, + model: synthPlan.model, + status: refusal ? "refusal" : "ok", + text: out.text, + latencyMs, + }); + } catch (err) { + candidates.push({ + id: synthPlan.id, + role: synthPlan.role, + model: synthPlan.model, + status: classifyError(err), + latencyMs: Date.now() - started, + }); + } + } + } + const quorum = evaluateQuorum(candidates, { minParticipatingAgents: input.settings.minParticipatingAgents, minAnsweringAgents: input.settings.minAnsweringAgents, diff --git a/extensions/braintrust/src/settings.test.ts b/extensions/braintrust/src/settings.test.ts index 9a4c437..655e6e2 100644 --- a/extensions/braintrust/src/settings.test.ts +++ b/extensions/braintrust/src/settings.test.ts @@ -18,6 +18,6 @@ describe("readSettings", () => { const got = readSettings({ strategy: "debate", enabled: true }); expect(got.strategy).toBe("debate"); expect(got.enabled).toBe(true); - expect(got.researcherModel).toBe("grok-4-1-fast-reasoning"); + expect(got.researcherModel).toBe("model-c"); }); }); diff --git a/extensions/braintrust/src/settings.ts b/extensions/braintrust/src/settings.ts index 201e350..8de9a96 100644 --- a/extensions/braintrust/src/settings.ts +++ b/extensions/braintrust/src/settings.ts @@ -23,10 +23,10 @@ export const DEFAULT_SETTINGS: BraintrustSettings = { enabled: false, teamSize: 4, strategy: "panel", - model: "gemini-3-flash-preview", - criticModel: "openai-codex/gpt-5.3-codex", - researcherModel: "grok-4-1-fast-reasoning", - synthModel: "gemini-3.1-pro-preview", + model: "model-a", + criticModel: "model-b", + researcherModel: "model-c", + synthModel: "legacy-synth-model", timeoutSeconds: 90, minParticipatingAgents: DEFAULT_QUORUM.minParticipatingAgents, minAnsweringAgents: DEFAULT_QUORUM.minAnsweringAgents, diff --git a/extensions/braintrust/src/synth.ts b/extensions/braintrust/src/synth.ts index 4e25b0e..e9d1993 100644 --- a/extensions/braintrust/src/synth.ts +++ b/extensions/braintrust/src/synth.ts @@ -8,11 +8,39 @@ export type SynthesisInput = { export type SynthesisOutput = { final: string; winnerId?: string; + reasoning?: string; + sources?: string[]; }; +function parseStructuredSynthesis(candidate: Candidate): SynthesisOutput | undefined { + if (candidate.status !== "ok" || !candidate.text?.trim()) return undefined; + + try { + const parsed = JSON.parse(candidate.text.trim()) as { + final?: unknown; + reasoning?: unknown; + sources?: unknown; + }; + + if (typeof parsed.final !== "string" || !parsed.final.trim()) return undefined; + + return { + final: parsed.final.trim(), + winnerId: candidate.id, + reasoning: typeof parsed.reasoning === "string" ? parsed.reasoning.trim() : undefined, + sources: Array.isArray(parsed.sources) + ? parsed.sources.filter((value): value is string => typeof value === "string" && value.trim().length > 0) + : undefined, + }; + } catch { + return undefined; + } +} + /** - * Deterministic synthesis placeholder. - * - prefers shortest successful candidate (concise bias) + * Deterministic fallback synthesis. + * - prefers structured synthesizer output when present + * - otherwise prefers shortest successful candidate (concise bias) * - stable tie-break by id */ export function synthesizeDeterministic(input: SynthesisInput): SynthesisOutput { @@ -23,23 +51,12 @@ export function synthesizeDeterministic(input: SynthesisInput): SynthesisOutput }; } - // New structured JSON mode for 4-agent system - const synthAgent = input.candidates.find((c) => c.id === "agent-4"); - if (synthAgent?.status === "ok" && synthAgent.text) { - try { - const parsed = JSON.parse(synthAgent.text.trim()); - if (parsed.final && typeof parsed.final === "string") { - return { - final: parsed.final, - winnerId: "agent-4", - }; - } - } catch (e) { - // fallback if not valid JSON - } + const synthAgent = input.candidates.find((c) => c.role === "synthesizer") ?? input.candidates.find((c) => c.id === "agent-4"); + if (synthAgent) { + const parsed = parseStructuredSynthesis(synthAgent); + if (parsed) return parsed; } - // fallback to shortest successful ok.sort((a, b) => { const la = (a.text ?? "").length; const lb = (b.text ?? "").length; diff --git a/extensions/braintrust/src/worker-plan.test.ts b/extensions/braintrust/src/worker-plan.test.ts new file mode 100644 index 0000000..ab8ffb3 --- /dev/null +++ b/extensions/braintrust/src/worker-plan.test.ts @@ -0,0 +1,41 @@ +import { describe, expect, it } from "vitest"; +import { DEFAULT_SETTINGS } from "./settings.js"; +import { buildWorkerPlan } from "./worker-plan.js"; + +describe("buildWorkerPlan", () => { + it("builds role-specific workers and uses active chat model for synthesis", () => { + const plan = buildWorkerPlan({ + prompt: "Design a rollout plan.", + settings: { ...DEFAULT_SETTINGS, teamSize: 4, strategy: "debate" }, + activeChatModel: "runtime-chat-model", + }); + + expect(plan.map((worker) => ({ id: worker.id, role: worker.role, model: worker.model }))).toEqual([ + { id: "agent-1", role: "solver", model: DEFAULT_SETTINGS.model }, + { id: "agent-2", role: "critic", model: DEFAULT_SETTINGS.criticModel }, + { id: "agent-3", role: "researcher", model: DEFAULT_SETTINGS.researcherModel }, + { id: "agent-4", role: "synthesizer", model: "runtime-chat-model" }, + ]); + + expect(plan[0].prompt).toContain("Braintrust solver"); + expect(plan[1].prompt).toContain("Braintrust critic"); + expect(plan[2].prompt).toContain("Braintrust researcher"); + expect(plan[3].prompt).toContain("Respond ONLY with valid JSON"); + }); + + it("injects completed worker outputs into the synthesizer prompt", () => { + const plan = buildWorkerPlan({ + prompt: "What should we ship next?", + settings: { ...DEFAULT_SETTINGS, teamSize: 4 }, + completedWorkers: [ + { id: "agent-1", role: "solver", status: "ok", text: "Ship the settings page." }, + { id: "agent-2", role: "critic", status: "ok", text: "Watch migration risk." }, + ], + }); + + const synth = plan.find((worker) => worker.role === "synthesizer"); + expect(synth?.prompt).toContain("Worker outputs to synthesize"); + expect(synth?.prompt).toContain("agent-1 (solver, ok)"); + expect(synth?.prompt).toContain("agent-2 (critic, ok)"); + }); +}); diff --git a/extensions/braintrust/src/worker-plan.ts b/extensions/braintrust/src/worker-plan.ts new file mode 100644 index 0000000..275cc87 --- /dev/null +++ b/extensions/braintrust/src/worker-plan.ts @@ -0,0 +1,90 @@ +import type { BraintrustSettings, BraintrustStrategy } from "./settings.js"; + +export type WorkerRole = "solver" | "critic" | "researcher" | "synthesizer"; + +export type BraintrustWorker = { + id: string; + role: WorkerRole; + model: string; + prompt: string; +}; + +function roleInstruction(role: WorkerRole, strategy: BraintrustStrategy): string { + if (role === "solver") { + if (strategy === "debate") { + return "You are the Braintrust solver. Produce the strongest direct answer you can, but note one assumption that could be challenged by another worker."; + } + if (strategy === "independent") { + return "You are the Braintrust solver. Solve the user request independently and concisely, without referencing other workers."; + } + return "You are the Braintrust solver. Produce the strongest direct answer for the user request, optimized for usefulness and clarity."; + } + + if (role === "critic") { + if (strategy === "debate") { + return "You are the Braintrust critic. Stress-test likely solver claims, identify risks or weak assumptions, and propose corrections."; + } + if (strategy === "independent") { + return "You are the Braintrust critic. Independently answer the user request while emphasizing failure modes, risks, and caveats."; + } + return "You are the Braintrust critic. Review the problem from a skeptical angle, focusing on risks, edge cases, and missing constraints."; + } + + if (role === "researcher") { + if (strategy === "debate") { + return "You are the Braintrust researcher. Add factual context, tradeoffs, and external-style evidence that would help settle a disagreement."; + } + if (strategy === "independent") { + return "You are the Braintrust researcher. Independently answer the user request with concrete facts, references, or implementation details."; + } + return "You are the Braintrust researcher. Contribute supporting facts, implementation details, or concrete options relevant to the request."; + } + + return "You are the Braintrust synthesizer. Combine the worker outputs into the single best final answer. Respond ONLY with valid JSON: {\"final\": string, \"reasoning\": string, \"sources\": string[] }."; +} + +function modelFor(role: WorkerRole, settings: BraintrustSettings, activeChatModel?: string): string { + if (role === "critic") return settings.criticModel; + if (role === "researcher") return settings.researcherModel; + if (role === "synthesizer") return activeChatModel?.trim() || settings.model; + return settings.model; +} + +const ROLE_ORDER: WorkerRole[] = ["solver", "critic", "researcher", "synthesizer"]; + +export function buildWorkerPlan(input: { + prompt: string; + settings: BraintrustSettings; + activeChatModel?: string; + completedWorkers?: Array<{ id: string; role: WorkerRole; text?: string; status?: string }>; +}): BraintrustWorker[] { + const { prompt, settings, activeChatModel } = input; + const roles = ROLE_ORDER.slice(0, Math.max(1, Math.min(settings.teamSize, ROLE_ORDER.length))); + + return roles.map((role, index) => { + const id = `agent-${index + 1}`; + const instruction = roleInstruction(role, settings.strategy); + + let workerPrompt = prompt; + if (role === "synthesizer") { + const prior = (input.completedWorkers ?? []) + .filter((worker) => worker.role !== "synthesizer") + .map((worker) => `${worker.id} (${worker.role}, ${worker.status ?? "unknown"}):\n${worker.text?.trim() || "[no text]"}`) + .join("\n\n"); + + workerPrompt = [ + prompt, + "", + "Worker outputs to synthesize:", + prior || "[no completed worker outputs available]", + ].join("\n"); + } + + return { + id, + role, + model: modelFor(role, settings, activeChatModel), + prompt: `${instruction}\n\nUser request:\n${workerPrompt}`, + }; + }); +} From f606e2dc254f0970973138f1514049075ca392de Mon Sep 17 00:00:00 2001 From: Victor al Ghul Date: Sat, 7 Mar 2026 23:30:21 +0000 Subject: [PATCH 13/14] Implement persistent braintrust orchestration --- README.md | 5 +- extensions/braintrust/README.md | 76 +++---- extensions/braintrust/index.js | 188 ++++------------ extensions/braintrust/index.ts | 201 ++++-------------- extensions/braintrust/src/index.test.ts | 156 +++++++------- .../braintrust/src/live-validation.test.ts | 134 +++++++----- extensions/braintrust/src/orchestrator.js | 138 ++++++++++++ .../braintrust/src/orchestrator.test.ts | 39 ++++ extensions/braintrust/src/orchestrator.ts | 190 +++++++++++++++++ extensions/braintrust/src/policy.js | 40 ++++ extensions/braintrust/src/runtime-bridge.js | 115 ++++++++++ .../braintrust/src/runtime-bridge.test.ts | 70 +++--- extensions/braintrust/src/runtime-bridge.ts | 157 ++++++-------- extensions/braintrust/src/settings.js | 37 ++++ extensions/braintrust/src/state.js | 47 ++++ extensions/braintrust/src/state.ts | 86 ++++++++ extensions/braintrust/src/synth.js | 31 +++ scripts/validate-braintrust.sh | 4 +- 18 files changed, 1105 insertions(+), 609 deletions(-) create mode 100644 extensions/braintrust/src/orchestrator.js create mode 100644 extensions/braintrust/src/orchestrator.test.ts create mode 100644 extensions/braintrust/src/orchestrator.ts create mode 100644 extensions/braintrust/src/policy.js create mode 100644 extensions/braintrust/src/runtime-bridge.js create mode 100644 extensions/braintrust/src/settings.js create mode 100644 extensions/braintrust/src/state.js create mode 100644 extensions/braintrust/src/state.ts create mode 100644 extensions/braintrust/src/synth.js diff --git a/README.md b/README.md index 98ad794..7e3dd29 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Plugin-only repo for OpenClaw Braintrust experimentation. Contents: -- `extensions/braintrust/` โ€” Braintrust plugin scaffold (config, command, quorum policy tests) +- `extensions/braintrust/` โ€” Braintrust plugin with persistent state, quorum policy, and runtime worker orchestration ## Live Feature Validation @@ -15,5 +15,6 @@ Run the reproducible validation script: What it verifies: - `/braintrust on` and `/braintrust status` command flow -- sample prompt path returns exactly one synthesized final output +- runtime bridge creates one durable Braintrust session and returns exactly one synthesized final output - if quorum cannot be met, plugin emits explicit `Braintrust temporarily unavailable (...)` notice +- worker/session state is persisted to `.braintrust/state.json` diff --git a/extensions/braintrust/README.md b/extensions/braintrust/README.md index e1f2581..0e81b65 100644 --- a/extensions/braintrust/README.md +++ b/extensions/braintrust/README.md @@ -1,45 +1,49 @@ -# Braintrust Plugin (MVP Control Plane) - -Feature-flagged plugin path for multi-agent orchestration in OpenClaw. - -## Current behavior (implemented) -- Chat/auto-reply surfaces: `/braintrust on|off|status|unavailable|solver |critic |researcher ` (via `registerCommand`) -- Local CLI surface: `openclaw braintrust [on|off|status|unavailable|solver |critic |researcher ]` (via `registerCli` when available) -- Configurable team size / strategy / model roles -- Runtime bridge calls real model executions when a runtime executor is available in hook context -- Worker-plan execution now uses explicit solver / critic / researcher / synthesizer roles with strategy-shaped prompts instead of index-only slot simulation -- Synthesizer receives prior worker outputs and runs on the active chat model, returning structured JSON when the runtime supports it -- Quorum contract configuration (`minParticipatingAgents`, `minAnsweringAgents`) -- Explicit unavailable behavior when runtime fan-out is unavailable; no fake panel simulation fallback -- Logging hooks for `llm_input` and `llm_output` -- Deterministic quorum policy helpers + unit tests - -## Important limitation (still pending) -This plugin now routes Braintrust workers through the runtime executor when one is exposed in hook context, but it **still does not own true orchestrator-managed subagent lifecycle** by itself. -It relies on the hosting runtime for actual worker/model execution; deeper worker spawning, persistent cross-agent state, and first-class host-managed worker/session APIs remain core integration tasks. - -Also note: `openclaw agent --local --message '/braintrust ...'` does not route through plugin slash-command dispatch. Use `openclaw braintrust ...` for local CLI control. - -## Quorum contract -- Minimum participating agents: default `2` -- Minimum answering agents: default `2` -- If quorum fails, return explicit unavailable notice instead of pretending panel output. +# Braintrust Plugin + +Feature-flagged Braintrust plugin for OpenClaw with persistent control state and orchestrator-managed worker lifecycle. + +## What it now does +- Chat/auto-reply surfaces: `/braintrust on|off|status|unavailable|sessions` +- Local CLI surface: `openclaw braintrust [on|off|status|unavailable|sessions]` +- Persists enabled/disabled state plus recent session history to `extensions/braintrust/.braintrust/state.json` +- Runs true plugin-owned fan-out/fan-in orchestration when a runtime executor is available +- Tracks per-worker lifecycle (`pending -> running -> completed/refused/timeout/error`) for each Braintrust session +- Enforces quorum before allowing a synthesized final answer through +- Falls back to policy-only prompt injection when the runtime hook cannot execute workers directly +- Logs `llm_input` / `llm_output` activity for observability + +## Runtime behavior +When enabled and a runtime executor is present, Braintrust now: +1. Creates a durable session record +2. Starts up to 4 workers with distinct roles/models +3. Updates worker state as each candidate finishes +4. Evaluates quorum +5. Persists the final synthesized answer or explicit unavailable notice + +Recent sessions can be inspected with `/braintrust sessions`. + +## Durable state +Braintrust stores durable local state in: + +```text +extensions/braintrust/.braintrust/state.json +``` -See `src/policy.ts` and `src/policy.test.ts`. +That file keeps: +- whether Braintrust is currently enabled +- recent sessions and worker statuses +- final result/unavailable reason for each session ## Test ```bash -pnpm vitest run extensions/braintrust/src/policy.test.ts extensions/braintrust/src/settings.test.ts +pnpm vitest run +./scripts/validate-braintrust.sh ``` - ## Default model routing -- solver (`model`): `model-a` -- critic (`criticModel`): `model-b` -- researcher (`researcherModel`): `model-c` -- synthesizer: **active chat model** (agent-4, expected to output structured JSON like `{"final": "...", "reasoning": "...", "sources": [...]}`) - -`syntModel` / `synthModel` config is now effectively legacy compatibility state; synthesis should follow the active chat model instead of a dedicated synth override. - +- solver (`model`): `gemini-3-flash-preview` +- critic (`criticModel`): `openai-codex/gpt-5.3-codex` +- researcher (`researcherModel`): `grok-4-1-fast-reasoning` +- synthesizer (`synthModel`): `gemini-3.1-pro-preview` Note: plugin id is `braintrust-plugin`; command remains `/braintrust`. diff --git a/extensions/braintrust/index.js b/extensions/braintrust/index.js index 574c260..1822d85 100644 --- a/extensions/braintrust/index.js +++ b/extensions/braintrust/index.js @@ -1,5 +1,5 @@ -import { buildUnavailableNotice, DEFAULT_QUORUM, evaluateQuorum } from "./src/policy.js"; -import { runRuntimeBridge } from "./src/runtime-bridge.js"; +import { buildUnavailableNotice } from "./src/policy.js"; +import { BraintrustOrchestrator } from "./src/orchestrator.js"; import { readSettings } from "./src/settings.js"; const braintrustConfigSchema = { type: "object", @@ -8,28 +8,18 @@ const braintrustConfigSchema = { enabled: { type: "boolean", default: false }, teamSize: { type: "integer", minimum: 1, maximum: 4, default: 4 }, strategy: { type: "string", enum: ["independent", "debate", "panel"], default: "panel" }, - model: { type: "string", default: "model-a" }, - criticModel: { type: "string", default: "model-b" }, - researcherModel: { type: "string", default: "model-c" }, - synthModel: { type: "string", default: "legacy-synth-model" }, + model: { type: "string", default: "gemini-3-flash-preview" }, + criticModel: { type: "string", default: "openai-codex/gpt-5.3-codex" }, + researcherModel: { type: "string", default: "grok-4-1-fast-reasoning" }, + synthModel: { type: "string", default: "gemini-3.1-pro-preview" }, timeoutSeconds: { type: "integer", minimum: 10, maximum: 300, default: 90 }, minParticipatingAgents: { type: "integer", minimum: 1, maximum: 4, default: 2 }, minAnsweringAgents: { type: "integer", minimum: 1, maximum: 4, default: 2 } } }; -function formatQuorumStatus(e) { - if (!e) return `quorum: ${DEFAULT_QUORUM.minParticipatingAgents}/${DEFAULT_QUORUM.minAnsweringAgents}`; - return `participating=${e.participating} answering=${e.answering} refused=${e.refused} failed=${e.failed}`; -} function parseBraintrustAction(raw) { - const arg = (raw ?? "status").trim(); - const lowered = arg.toLowerCase(); - if (lowered === "on" || lowered === "off" || lowered === "status" || lowered === "unavailable") return lowered; - for (const role of ["solver", "critic", "researcher"]) { - if (!lowered.startsWith(`${role} `)) continue; - const model = arg.slice(role.length).trim(); - if (model) return `${role} ${model}`; - } + const arg = (raw ?? "status").trim().toLowerCase(); + if (arg === "on" || arg === "off" || arg === "unavailable" || arg === "sessions") return arg; return "status"; } function extractPromptFromMessages(messages) { @@ -65,10 +55,7 @@ function readTextOutput(out) { return ""; } function resolveRuntimeExecutor(api, runtimeContext) { - const source = [ - runtimeContext, - api - ]; + const source = [runtimeContext, api]; const names = ["runModel", "invokeModel", "runLlm", "invokeLlm", "complete"]; for (const obj of source) { if (!obj) continue; @@ -80,18 +67,17 @@ function resolveRuntimeExecutor(api, runtimeContext) { model: input.model, role: input.role, timeoutSeconds: input.timeoutSeconds, + metadata: { candidateId: input.candidateId, role: input.role, pluginId: "braintrust-plugin" }, messages: [ { role: "system", - content: `You are Braintrust ${input.role}. Respond with one concise candidate answer for the user prompt.` + content: `You are Braintrust ${input.role}. Candidate id=${input.candidateId}. Respond with one concise candidate answer for the user prompt.` }, { role: "user", content: input.prompt } ] }); const text = readTextOutput(out).trim(); - if (!text) { - throw new Error("empty output"); - } + if (!text) throw new Error("empty output"); return { text, refusal: /\b(cannot comply|i can't|i cannot|refuse|not able)\b/i.test(text) @@ -101,27 +87,14 @@ function resolveRuntimeExecutor(api, runtimeContext) { } return void 0; } -function resolveActiveChatModel(event, context) { - const sources = [event, context]; - for (const source of sources) { - const model = source?.model; - if (typeof model === "string" && model.trim()) return model.trim(); - } - return void 0; -} -function buildUnavailablePrepend(settings, reason) { - const notice = buildUnavailableNotice({ - participating: 0, - answering: 0, - refused: 0, - failed: settings.teamSize, - meetsQuorum: false, - reason - }); +function buildFallbackPrepend(settings) { return [ "BRAINTRUST MODE ACTIVE.", - "Runtime bridge unavailable; do not simulate panelists or fabricate quorum.", - `Return exactly this notice: ${notice}` + `Use a ${settings.teamSize}-agent internal panel with strategy=${settings.strategy}.`, + `Simulate roles: solver(model=${settings.model}), critic(model=${settings.criticModel}), researcher(model=${settings.researcherModel}), synthesizer(model=${settings.synthModel}).`, + `Quorum contract: require >=${settings.minParticipatingAgents} participating and >=${settings.minAnsweringAgents} answering agents.`, + "If quorum cannot be satisfied, return exactly: Braintrust temporarily unavailable (...).", + "Return only one final answer to the user." ].join("\n"); } var index_default = { @@ -130,119 +103,42 @@ var index_default = { description: "Multi-agent orchestration control plane", configSchema: braintrustConfigSchema, register(api) { - const settings = readSettings(api.pluginConfig); - let enabled = settings.enabled; - let solverModel = settings.model; - let criticModel = settings.criticModel; - let researcherModel = settings.researcherModel; - let lastQuorumEvaluation; - const statusLine = () => [ - `Braintrust: ${enabled ? "ON" : "OFF"}`, - `strategy=${settings.strategy}`, - `teamSize=${settings.teamSize}`, - `solver=${solverModel}`, - `critic=${criticModel}`, - `researcher=${researcherModel}`, - "synth=active-chat-model", - `timeout=${settings.timeoutSeconds}s`, - `minParticipating=${settings.minParticipatingAgents}`, - `minAnswering=${settings.minAnsweringAgents}`, - formatQuorumStatus(lastQuorumEvaluation) - ].join(" \xB7 "); + const orchestrator = new BraintrustOrchestrator(api, readSettings(api.pluginConfig)); const renderUnavailable = () => buildUnavailableNotice( - lastQuorumEvaluation ?? { + orchestrator.getLastQuorumEvaluation() ?? { participating: 0, answering: 0, refused: 0, - failed: settings.teamSize, + failed: orchestrator.getSettings().teamSize, meetsQuorum: false, - reason: `only 0/${settings.teamSize} agents participated` + reason: `only 0/${orchestrator.getSettings().teamSize} agents participated` } ); - const executeBraintrustAction = (action) => { - if (action === "on") { - enabled = true; - return `\u2705 ${statusLine()}`; - } - if (action === "off") { - enabled = false; - return `\u{1F6D1} ${statusLine()}`; - } - if (action === "unavailable") { - return renderUnavailable(); - } - if (action.startsWith("solver ")) { - solverModel = action.slice("solver ".length).trim(); - return `\u2705 solver=${solverModel} \u00b7 ${statusLine()}`; - } - if (action.startsWith("critic ")) { - criticModel = action.slice("critic ".length).trim(); - return `\u2705 critic=${criticModel} \u00b7 ${statusLine()}`; - } - if (action.startsWith("researcher ")) { - researcherModel = action.slice("researcher ".length).trim(); - return `\u2705 researcher=${researcherModel} \u00b7 ${statusLine()}`; - } - return statusLine(); - }; api.registerCommand({ name: "braintrust", - description: "Control Braintrust mode: /braintrust on|off|status|unavailable|solver |critic |researcher ", + description: "Control Braintrust mode: /braintrust on|off|status|unavailable|sessions", acceptsArgs: true, - handler: async (ctx) => ({ text: executeBraintrustAction(parseBraintrustAction(ctx.args)) }) + handler: async (ctx) => ({ text: orchestrator.executeAction(parseBraintrustAction(ctx.args), renderUnavailable) }) }); - if (typeof api.registerCli === "function") { - api.registerCli( - ({ program }) => { - program - .command("braintrust") - .description("Braintrust controls for local CLI surfaces") - .argument("[action...]", "on|off|status|unavailable|solver |critic |researcher ") - .action((action) => { - console.log(executeBraintrustAction(parseBraintrustAction(action.join(" ")))); - }); - }, - { commands: ["braintrust"] } - ); - } + api.registerCli?.( + ({ program }) => { + program.command("braintrust").description("Braintrust controls for local CLI surfaces").argument("[action]", "on|off|status|unavailable|sessions", "status").action((action) => { + console.log(orchestrator.executeAction(parseBraintrustAction(action), renderUnavailable)); + }); + }, + { commands: ["braintrust"] } + ); api.on("before_prompt_build", async (payload) => { - if (!enabled) return; + if (!orchestrator.isEnabled()) return; const event = payload?.event ?? payload ?? {}; const context = payload?.context ?? {}; - const prompt = extractPromptFromMessages(event?.messages); + const prompt = extractPromptFromMessages(event?.messages) || event.prompt || ""; const execute = resolveRuntimeExecutor(api, context); - const activeChatModel = resolveActiveChatModel(event, context); - const runtimeSettings = { - ...settings, - model: solverModel, - criticModel, - researcherModel - }; if (!execute || !prompt) { - api.logger.info("[braintrust] runtime bridge unavailable in hook context; refusing fake panel simulation"); - lastQuorumEvaluation = { - participating: 0, - answering: 0, - refused: 0, - failed: runtimeSettings.teamSize, - meetsQuorum: false, - reason: !execute ? "runtime executor unavailable" : "missing user prompt" - }; - return { - prependContext: buildUnavailablePrepend( - runtimeSettings, - !execute ? "runtime executor unavailable" : "missing user prompt" - ) - }; + api.logger.info("[braintrust] runtime bridge unavailable in hook context, using policy-only prompt injection"); + return { prependContext: buildFallbackPrepend(orchestrator.getSettings()) }; } - const bridge = await runRuntimeBridge( - { prompt, settings: runtimeSettings, activeChatModel }, - ({ role, model, prompt: p, timeoutSeconds }) => execute({ role, model, prompt: p, timeoutSeconds }) - ); - lastQuorumEvaluation = evaluateQuorum(bridge.candidates, { - minParticipatingAgents: settings.minParticipatingAgents, - minAnsweringAgents: settings.minAnsweringAgents - }); + const bridge = await orchestrator.runPrompt(prompt, ({ role, model, prompt: p, timeoutSeconds, candidateId }) => execute({ role, model, prompt: p, timeoutSeconds, candidateId })); api.logger.info( `[braintrust] runtime bridge complete unavailable=${bridge.unavailable} candidates=${bridge.candidates.length}` ); @@ -264,20 +160,18 @@ var index_default = { }; }); api.on("llm_input", async (payload) => { - if (!enabled) return; + if (!orchestrator.isEnabled()) return; const event = payload?.event ?? payload; const context = payload?.context ?? {}; - const e = event; api.logger.info( - `[braintrust] llm_input run=${e?.runId ?? "unknown"} session=${context?.sessionKey ?? "unknown"} model=${e?.model ?? "unknown"}` + `[braintrust] llm_input run=${event?.runId ?? "unknown"} session=${context?.sessionKey ?? "unknown"} model=${event?.model ?? "unknown"}` ); }); api.on("llm_output", async (payload) => { - if (!enabled) return; + if (!orchestrator.isEnabled()) return; const event = payload?.event ?? payload; - const e = event; api.logger.info( - `[braintrust] llm_output run=${e?.runId ?? "unknown"} model=${e?.model ?? "unknown"} responses=${Array.isArray(e?.assistantTexts) ? e.assistantTexts.length : 0}` + `[braintrust] llm_output run=${event?.runId ?? "unknown"} model=${event?.model ?? "unknown"} responses=${Array.isArray(event?.assistantTexts) ? event.assistantTexts.length : 0}` ); }); } diff --git a/extensions/braintrust/index.ts b/extensions/braintrust/index.ts index 2f2046a..e50e889 100644 --- a/extensions/braintrust/index.ts +++ b/extensions/braintrust/index.ts @@ -5,8 +5,9 @@ import type { PluginHookLlmOutputEvent, } from "openclaw/plugin-sdk"; -import { buildUnavailableNotice, DEFAULT_QUORUM, evaluateQuorum, type QuorumEvaluation } from "./src/policy.js"; -import { runRuntimeBridge, type CandidateRunnerInput, type CandidateRunnerOutput } from "./src/runtime-bridge.js"; +import { buildUnavailableNotice } from "./src/policy.js"; +import { BraintrustOrchestrator, type OrchestratorAction } from "./src/orchestrator.js"; +import type { BraintrustSettings } from "./src/settings.js"; import { readSettings } from "./src/settings.js"; const braintrustConfigSchema = { @@ -16,10 +17,10 @@ const braintrustConfigSchema = { enabled: { type: "boolean", default: false }, teamSize: { type: "integer", minimum: 1, maximum: 4, default: 4 }, strategy: { type: "string", enum: ["independent", "debate", "panel"], default: "panel" }, - model: { type: "string", default: "model-a" }, - criticModel: { type: "string", default: "model-b" }, - researcherModel: { type: "string", default: "model-c" }, - synthModel: { type: "string", default: "legacy-synth-model" }, + model: { type: "string", default: "gemini-3-flash-preview" }, + criticModel: { type: "string", default: "openai-codex/gpt-5.3-codex" }, + researcherModel: { type: "string", default: "grok-4-1-fast-reasoning" }, + synthModel: { type: "string", default: "gemini-3.1-pro-preview" }, timeoutSeconds: { type: "integer", minimum: 10, maximum: 300, default: 90 }, minParticipatingAgents: { type: "integer", minimum: 1, maximum: 4, default: 2 }, minAnsweringAgents: { type: "integer", minimum: 1, maximum: 4, default: 2 }, @@ -29,36 +30,16 @@ const braintrustConfigSchema = { type RuntimeExecuteInput = { model: string; prompt: string; - role: CandidateRunnerInput["role"]; + role: "solver" | "critic" | "researcher" | "synthesizer"; timeoutSeconds: number; + candidateId: string; }; -type RuntimeExecuteFn = (input: RuntimeExecuteInput) => Promise; -type BraintrustAction = - | "on" - | "off" - | "status" - | "unavailable" - | `solver ${string}` - | `critic ${string}` - | `researcher ${string}`; - -function formatQuorumStatus(e?: QuorumEvaluation): string { - if (!e) return `quorum: ${DEFAULT_QUORUM.minParticipatingAgents}/${DEFAULT_QUORUM.minAnsweringAgents}`; - return `participating=${e.participating} answering=${e.answering} refused=${e.refused} failed=${e.failed}`; -} - -function parseBraintrustAction(raw?: string): BraintrustAction { - const arg = (raw ?? "status").trim(); - const lowered = arg.toLowerCase(); - if (lowered === "on" || lowered === "off" || lowered === "status" || lowered === "unavailable") return lowered; - - for (const role of ["solver", "critic", "researcher"] as const) { - if (!lowered.startsWith(`${role} `)) continue; - const model = arg.slice(role.length).trim(); - if (model) return `${role} ${model}`; - } +type RuntimeExecuteFn = (input: RuntimeExecuteInput) => Promise<{ text: string; refusal?: boolean; latencyMs?: number }>; +function parseBraintrustAction(raw?: string): OrchestratorAction { + const arg = (raw ?? "status").trim().toLowerCase(); + if (arg === "on" || arg === "off" || arg === "unavailable" || arg === "sessions") return arg; return "status"; } @@ -122,10 +103,11 @@ function resolveRuntimeExecutor(api: OpenClawPluginApi, runtimeContext: unknown) model: input.model, role: input.role, timeoutSeconds: input.timeoutSeconds, + metadata: { candidateId: input.candidateId, role: input.role, pluginId: "braintrust-plugin" }, messages: [ { role: "system", - content: `You are Braintrust ${input.role}. Respond with one concise candidate answer for the user prompt.`, + content: `You are Braintrust ${input.role}. Candidate id=${input.candidateId}. Respond with one concise candidate answer for the user prompt.`, }, { role: "user", content: input.prompt }, ], @@ -143,29 +125,14 @@ function resolveRuntimeExecutor(api: OpenClawPluginApi, runtimeContext: unknown) return undefined; } -function resolveActiveChatModel(event: unknown, context: unknown): string | undefined { - const sources = [event as Record | undefined, context as Record | undefined]; - for (const source of sources) { - const model = source?.model; - if (typeof model === "string" && model.trim()) return model.trim(); - } - return undefined; -} - -function buildUnavailablePrepend(settings: ReturnType, reason: string): string { - const notice = buildUnavailableNotice({ - participating: 0, - answering: 0, - refused: 0, - failed: settings.teamSize, - meetsQuorum: false, - reason, - }); - +function buildFallbackPrepend(settings: BraintrustSettings): string { return [ "BRAINTRUST MODE ACTIVE.", - "Runtime bridge unavailable; do not simulate panelists or fabricate quorum.", - `Return exactly this notice: ${notice}`, + `Use a ${settings.teamSize}-agent internal panel with strategy=${settings.strategy}.`, + `Simulate roles: solver(model=${settings.model}), critic(model=${settings.criticModel}), researcher(model=${settings.researcherModel}), synthesizer(model=${settings.synthModel}).`, + `Quorum contract: require >=${settings.minParticipatingAgents} participating and >=${settings.minAnsweringAgents} answering agents.`, + "If quorum cannot be satisfied, return exactly: Braintrust temporarily unavailable (...).", + "Return only one final answer to the user.", ].join("\n"); } @@ -176,130 +143,56 @@ export default { configSchema: braintrustConfigSchema, register(api: OpenClawPluginApi) { - const settings = readSettings(api.pluginConfig); - let enabled = settings.enabled; - let solverModel = settings.model; - let criticModel = settings.criticModel; - let researcherModel = settings.researcherModel; - let lastQuorumEvaluation: QuorumEvaluation | undefined; - - const statusLine = () => - [ - `Braintrust: ${enabled ? "ON" : "OFF"}`, - `strategy=${settings.strategy}`, - `teamSize=${settings.teamSize}`, - `solver=${solverModel}`, - `critic=${criticModel}`, - `researcher=${researcherModel}`, - "synth=active-chat-model", - `timeout=${settings.timeoutSeconds}s`, - `minParticipating=${settings.minParticipatingAgents}`, - `minAnswering=${settings.minAnsweringAgents}`, - formatQuorumStatus(lastQuorumEvaluation), - ].join(" ยท "); + const orchestrator = new BraintrustOrchestrator(api, readSettings(api.pluginConfig)); const renderUnavailable = () => buildUnavailableNotice( - lastQuorumEvaluation ?? { + orchestrator.getLastQuorumEvaluation() ?? { participating: 0, answering: 0, refused: 0, - failed: settings.teamSize, + failed: orchestrator.getSettings().teamSize, meetsQuorum: false, - reason: `only 0/${settings.teamSize} agents participated`, + reason: `only 0/${orchestrator.getSettings().teamSize} agents participated`, }, ); - const executeBraintrustAction = (action: BraintrustAction): string => { - if (action === "on") { - enabled = true; - return `โœ… ${statusLine()}`; - } - if (action === "off") { - enabled = false; - return `๐Ÿ›‘ ${statusLine()}`; - } - if (action === "unavailable") return renderUnavailable(); - if (action.startsWith("solver ")) { - solverModel = action.slice("solver ".length).trim(); - return `โœ… solver=${solverModel} ยท ${statusLine()}`; - } - if (action.startsWith("critic ")) { - criticModel = action.slice("critic ".length).trim(); - return `โœ… critic=${criticModel} ยท ${statusLine()}`; - } - if (action.startsWith("researcher ")) { - researcherModel = action.slice("researcher ".length).trim(); - return `โœ… researcher=${researcherModel} ยท ${statusLine()}`; - } - return statusLine(); - }; - api.registerCommand({ name: "braintrust", - description: "Control Braintrust mode: /braintrust on|off|status|unavailable|solver |critic |researcher ", + description: "Control Braintrust mode: /braintrust on|off|status|unavailable|sessions", acceptsArgs: true, - handler: async (ctx) => ({ text: executeBraintrustAction(parseBraintrustAction(ctx.args)) }), + handler: async (ctx) => ({ text: orchestrator.executeAction(parseBraintrustAction(ctx.args), renderUnavailable) }), }); - if (typeof api.registerCli === "function") { - api.registerCli( - ({ program }) => { - program - .command("braintrust") - .description("Braintrust controls for local CLI surfaces") - .argument("[action...]", "on|off|status|unavailable|solver |critic |researcher ") - .action((action: string[]) => { - console.log(executeBraintrustAction(parseBraintrustAction(action.join(" ")))); - }); - }, - { commands: ["braintrust"] }, - ); - } + api.registerCli?.( + ({ program }) => { + program + .command("braintrust") + .description("Braintrust controls for local CLI surfaces") + .argument("[action]", "on|off|status|unavailable|sessions", "status") + .action((action: string) => { + console.log(orchestrator.executeAction(parseBraintrustAction(action), renderUnavailable)); + }); + }, + { commands: ["braintrust"] }, + ); api.on("before_prompt_build", async (payload) => { - if (!enabled) return; + if (!orchestrator.isEnabled()) return; const event = ((payload as any)?.event ?? payload ?? {}) as PluginHookBeforePromptBuildEvent; const context = ((payload as any)?.context ?? {}) as any; - const prompt = extractPromptFromMessages((event as any)?.messages); + const prompt = extractPromptFromMessages((event as any)?.messages) || event.prompt || ""; const execute = resolveRuntimeExecutor(api, context); - const activeChatModel = resolveActiveChatModel(event, context); - const runtimeSettings = { - ...settings, - model: solverModel, - criticModel, - researcherModel, - }; - if (!execute || !prompt) { - api.logger.info("[braintrust] runtime bridge unavailable in hook context; refusing fake panel simulation"); - lastQuorumEvaluation = { - participating: 0, - answering: 0, - refused: 0, - failed: runtimeSettings.teamSize, - meetsQuorum: false, - reason: !execute ? "runtime executor unavailable" : "missing user prompt", - }; - return { - prependContext: buildUnavailablePrepend( - runtimeSettings, - !execute ? "runtime executor unavailable" : "missing user prompt", - ), - }; + api.logger.info("[braintrust] runtime bridge unavailable in hook context, using policy-only prompt injection"); + return { prependContext: buildFallbackPrepend(orchestrator.getSettings()) }; } - const bridge = await runRuntimeBridge( - { prompt, settings: runtimeSettings, activeChatModel }, - ({ role, model, prompt: p, timeoutSeconds }) => execute({ role, model, prompt: p, timeoutSeconds }), + const bridge = await orchestrator.runPrompt(prompt, ({ role, model, prompt: p, timeoutSeconds, candidateId }) => + execute({ role, model, prompt: p, timeoutSeconds, candidateId }), ); - lastQuorumEvaluation = evaluateQuorum(bridge.candidates, { - minParticipatingAgents: settings.minParticipatingAgents, - minAnsweringAgents: settings.minAnsweringAgents, - }); - api.logger.info( `[braintrust] runtime bridge complete unavailable=${bridge.unavailable} candidates=${bridge.candidates.length}`, ); @@ -324,7 +217,7 @@ export default { }); api.on("llm_input", async (payload) => { - if (!enabled) return; + if (!orchestrator.isEnabled()) return; const event = ((payload as any)?.event ?? payload) as PluginHookLlmInputEvent; const context = ((payload as any)?.context ?? {}) as any; api.logger.info( @@ -333,7 +226,7 @@ export default { }); api.on("llm_output", async (payload) => { - if (!enabled) return; + if (!orchestrator.isEnabled()) return; const event = ((payload as any)?.event ?? payload) as PluginHookLlmOutputEvent; api.logger.info( `[braintrust] llm_output run=${(event as any)?.runId ?? "unknown"} model=${(event as any)?.model ?? "unknown"} responses=${Array.isArray((event as any)?.assistantTexts) ? (event as any).assistantTexts.length : 0}`, diff --git a/extensions/braintrust/src/index.test.ts b/extensions/braintrust/src/index.test.ts index 0179eb8..a7614f1 100644 --- a/extensions/braintrust/src/index.test.ts +++ b/extensions/braintrust/src/index.test.ts @@ -1,3 +1,6 @@ +import { mkdtempSync, rmSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; import { beforeEach, describe, expect, it, vi } from "vitest"; vi.mock("openclaw/plugin-sdk", () => ({ @@ -10,14 +13,16 @@ type FakeApi = { pluginConfig: Record; logger: { info: (message: string) => void }; registerCommand: (command: unknown) => void; - registerCli?: (register: (ctx: { program: { command: (name: string) => { description: (text: string) => { argument: (...args: unknown[]) => { action: (handler: (action: string[]) => void) => void } } } } }) => void, meta?: unknown) => void; + registerCli: (registrar: unknown, opts?: unknown) => void; on: (name: string, handler: HookHandler) => void; + resolvePath: (input: string) => string; }; function setupApi(pluginConfig: Record = {}) { const hooks = new Map(); const commands: unknown[] = []; const logger = { info: vi.fn() }; + const root = mkdtempSync(join(tmpdir(), "braintrust-index-test-")); const api: FakeApi = { pluginConfig, logger, @@ -28,8 +33,9 @@ function setupApi(pluginConfig: Record = {}) { on: (name, handler) => { hooks.set(name, handler); }, + resolvePath: (input) => join(root, input.replace(/^\.\//, "")), }; - return { api, hooks, commands, logger }; + return { api, hooks, commands, logger, cleanup: () => rmSync(root, { recursive: true, force: true }) }; } describe("braintrust plugin runtime integration", () => { @@ -40,116 +46,96 @@ describe("braintrust plugin runtime integration", () => { it("executes runtime bridge through before_prompt_build when runtime executor exists", async () => { const { default: plugin } = await import("../index.js"); const calls: Array<{ model: string; role: string }> = []; - const { api, hooks } = setupApi({ enabled: true, teamSize: 3 }); - - plugin.register(api as never); - - const beforePromptBuild = hooks.get("before_prompt_build"); - expect(beforePromptBuild).toBeTypeOf("function"); - - const out = await beforePromptBuild!({ - event: { messages: [{ role: "user", content: "What is 2+2?" }] }, - context: { - runModel: async ({ model, role }: { model: string; role: string }) => { - calls.push({ model, role }); - if (role === "critic") return { text: "The answer is likely four." }; - return { text: "4" }; + const { api, hooks, cleanup } = setupApi({ enabled: true, teamSize: 3 }); + + try { + plugin.register(api as never); + + const beforePromptBuild = hooks.get("before_prompt_build"); + expect(beforePromptBuild).toBeTypeOf("function"); + + const out = await beforePromptBuild!({ + event: { messages: [{ role: "user", content: "What is 2+2?" }] }, + context: { + runModel: async ({ model, role }: { model: string; role: string }) => { + calls.push({ model, role }); + if (role === "critic") return { text: "The answer is likely four." }; + return { text: "4" }; + }, }, - }, - }); + }); - expect(calls).toHaveLength(3); - expect(out?.prependContext).toContain("Use this runtime-bridge synthesis as your final answer."); - expect(out?.prependContext).toContain("4"); + expect(calls).toHaveLength(3); + expect(out?.prependContext).toContain("Use this runtime-bridge synthesis as your final answer."); + expect(out?.prependContext).toContain("4"); + } finally { + cleanup(); + } }); it("falls back to policy-only injection when runtime executor is missing", async () => { const { default: plugin } = await import("../index.js"); - const { api, hooks } = setupApi({ enabled: true, teamSize: 3 }); + const { api, hooks, cleanup } = setupApi({ enabled: true, teamSize: 3 }); - plugin.register(api as never); + try { + plugin.register(api as never); - const beforePromptBuild = hooks.get("before_prompt_build"); - const out = await beforePromptBuild!({ - event: { messages: [{ role: "user", content: "Hello" }] }, - context: {}, - }); + const beforePromptBuild = hooks.get("before_prompt_build"); + const out = await beforePromptBuild!({ + event: { messages: [{ role: "user", content: "Hello" }] }, + context: {}, + }); - expect(out?.prependContext).toContain("BRAINTRUST MODE ACTIVE."); - expect(out?.prependContext).toContain("do not simulate panelists"); - expect(out?.prependContext).toContain("temporarily unavailable"); - }); - - it("supports runtime model override commands for solver/critic/researcher and uses active chat model for synth", async () => { - const { default: plugin } = await import("../index.js"); - const calls: Array<{ model: string; role: string }> = []; - const { api, hooks, commands } = setupApi({ enabled: true, teamSize: 4 }); - - plugin.register(api as never); - - const command = commands[0] as { handler: (ctx: { args?: string }) => Promise<{ text: string }> }; - await command.handler({ args: "solver solver-model-override" }); - await command.handler({ args: "critic critic-model-override" }); - await command.handler({ args: "researcher researcher-model-override" }); - - const beforePromptBuild = hooks.get("before_prompt_build"); - const out = await beforePromptBuild!({ - event: { model: "runtime-chat-model", messages: [{ role: "user", content: "What is 2+2?" }] }, - context: { - runModel: async ({ model, role }: { model: string; role: string }) => { - calls.push({ model, role }); - return role === "synthesizer" - ? { text: JSON.stringify({ final: "4", reasoning: "matched consensus", sources: ["agent-1"] }) } - : { text: "4" }; - }, - }, - }); - - expect(calls).toEqual([ - { model: "solver-model-override", role: "solver" }, - { model: "critic-model-override", role: "critic" }, - { model: "researcher-model-override", role: "researcher" }, - { model: "runtime-chat-model", role: "synthesizer" }, - ]); - expect(out?.prependContext).toContain("4"); + expect(out?.prependContext).toContain("BRAINTRUST MODE ACTIVE."); + expect(out?.prependContext).toContain("Simulate roles:"); + } finally { + cleanup(); + } }); it("tolerates missing or malformed before_prompt_build payloads", async () => { const { default: plugin } = await import("../index.js"); - const { api, hooks } = setupApi({ enabled: true, teamSize: 3 }); + const { api, hooks, cleanup } = setupApi({ enabled: true, teamSize: 3 }); - plugin.register(api as never); + try { + plugin.register(api as never); - const beforePromptBuild = hooks.get("before_prompt_build"); - expect(beforePromptBuild).toBeTypeOf("function"); + const beforePromptBuild = hooks.get("before_prompt_build"); + expect(beforePromptBuild).toBeTypeOf("function"); - const malformedPayloads: unknown[] = [undefined, null, "oops", 42, {}, { event: null }, { event: { messages: null } }]; + const malformedPayloads: unknown[] = [undefined, null, "oops", 42, {}, { event: null }, { event: { messages: null } }]; - for (const payload of malformedPayloads) { - await expect(beforePromptBuild!(payload)).resolves.not.toThrow(); + for (const payload of malformedPayloads) { + await expect(beforePromptBuild!(payload)).resolves.not.toThrow(); + } + } finally { + cleanup(); } }); it("tolerates missing or malformed llm_input/llm_output payloads", async () => { const { default: plugin } = await import("../index.js"); - const { api, hooks, logger } = setupApi({ enabled: true, teamSize: 3 }); + const { api, hooks, logger, cleanup } = setupApi({ enabled: true, teamSize: 3 }); - plugin.register(api as never); + try { + plugin.register(api as never); - const llmInput = hooks.get("llm_input"); - const llmOutput = hooks.get("llm_output"); + const llmInput = hooks.get("llm_input"); + const llmOutput = hooks.get("llm_output"); - expect(llmInput).toBeTypeOf("function"); - expect(llmOutput).toBeTypeOf("function"); + expect(llmInput).toBeTypeOf("function"); + expect(llmOutput).toBeTypeOf("function"); - const malformedPayloads: unknown[] = [undefined, null, "oops", 42, {}, { event: null }, { event: { assistantTexts: "bad" } }]; + const malformedPayloads: unknown[] = [undefined, null, "oops", 42, {}, { event: null }, { event: { assistantTexts: "bad" } }]; - for (const payload of malformedPayloads) { - await expect(llmInput!(payload)).resolves.not.toThrow(); - await expect(llmOutput!(payload)).resolves.not.toThrow(); - } + for (const payload of malformedPayloads) { + await expect(llmInput!(payload)).resolves.not.toThrow(); + await expect(llmOutput!(payload)).resolves.not.toThrow(); + } - expect(logger.info).toHaveBeenCalled(); + expect(logger.info).toHaveBeenCalled(); + } finally { + cleanup(); + } }); - }); diff --git a/extensions/braintrust/src/live-validation.test.ts b/extensions/braintrust/src/live-validation.test.ts index 6b27b06..42f00f3 100644 --- a/extensions/braintrust/src/live-validation.test.ts +++ b/extensions/braintrust/src/live-validation.test.ts @@ -1,3 +1,6 @@ +import { mkdtempSync, readFileSync, rmSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; import { beforeEach, describe, expect, it, vi } from "vitest"; vi.mock("openclaw/plugin-sdk", () => ({ @@ -10,24 +13,29 @@ type FakeApi = { pluginConfig: Record; logger: { info: (message: string) => void }; registerCommand: (command: unknown) => void; + registerCli: (registrar: unknown, opts?: unknown) => void; on: (name: string, handler: HookHandler) => void; + resolvePath: (input: string) => string; }; function setupApi(pluginConfig: Record = {}) { const hooks = new Map(); const commands: unknown[] = []; const logger = { info: vi.fn() }; + const root = mkdtempSync(join(tmpdir(), "braintrust-live-test-")); const api: FakeApi = { pluginConfig, logger, registerCommand: (command) => { commands.push(command); }, + registerCli: vi.fn(), on: (name, handler) => { hooks.set(name, handler); }, + resolvePath: (input) => join(root, input.replace(/^\.\//, "")), }; - return { api, hooks, commands, logger }; + return { api, hooks, commands, cleanup: () => rmSync(root, { recursive: true, force: true }), statePath: join(root, ".braintrust", "state.json") }; } type Command = { handler: (ctx: { args?: string }) => Promise<{ text: string }> }; @@ -39,67 +47,79 @@ describe("live braintrust validation flow", () => { it("validates /braintrust on + status + synthesized output path", async () => { const { default: plugin } = await import("../index.js"); - const { api, hooks, commands } = setupApi({ enabled: false, teamSize: 3 }); - - plugin.register(api as never); - - const cmd = commands[0] as Command; - const on = await cmd.handler({ args: "on" }); - const status = await cmd.handler({ args: "status" }); - - expect(on.text).toContain("Braintrust: ON"); - expect(status.text).toContain("Braintrust: ON"); - - const beforePromptBuild = hooks.get("before_prompt_build"); - expect(beforePromptBuild).toBeTypeOf("function"); - - const out = await beforePromptBuild!({ - event: { messages: [{ role: "user", content: "Give me one rollout recommendation." }] }, - context: { - runModel: async ({ role }: { role: string }) => { - if (role === "critic") return { text: "Keep it small and reversible." }; - if (role === "researcher") return { text: "Ship behind a feature flag." }; - return { text: "Start with a 5% canary and monitor error budgets." }; + const { api, hooks, commands, cleanup, statePath } = setupApi({ enabled: false, teamSize: 3 }); + + try { + plugin.register(api as never); + + const cmd = commands[0] as Command; + const on = await cmd.handler({ args: "on" }); + const status = await cmd.handler({ args: "status" }); + const sessions = await cmd.handler({ args: "sessions" }); + + expect(on.text).toContain("Braintrust: ON"); + expect(status.text).toContain("Braintrust: ON"); + expect(sessions.text).toContain("none recorded yet"); + + const beforePromptBuild = hooks.get("before_prompt_build"); + expect(beforePromptBuild).toBeTypeOf("function"); + + const out = await beforePromptBuild!({ + event: { messages: [{ role: "user", content: "Give me one rollout recommendation." }] }, + context: { + runModel: async ({ role }: { role: string }) => { + if (role === "critic") return { text: "Keep it small and reversible." }; + if (role === "researcher") return { text: "Ship behind a feature flag." }; + return { text: "Start with a 5% canary and monitor error budgets." }; + }, }, - }, - }); - - const prepend = out?.prependContext ?? ""; - expect(prepend).toContain("Use this runtime-bridge synthesis as your final answer."); - - const lines = prepend - .split("\n") - .map((line) => line.trim()) - .filter(Boolean) - .filter((line) => !line.startsWith("BRAINTRUST MODE ACTIVE")) - .filter((line) => !line.startsWith("Use this runtime-bridge synthesis as your final answer.")); - - expect(lines).toHaveLength(1); + }); + + const prepend = out?.prependContext ?? ""; + expect(prepend).toContain("Use this runtime-bridge synthesis as your final answer."); + + const lines = prepend + .split("\n") + .map((line) => line.trim()) + .filter(Boolean) + .filter((line) => !line.startsWith("BRAINTRUST MODE ACTIVE")) + .filter((line) => !line.startsWith("Use this runtime-bridge synthesis as your final answer.")); + + expect(lines).toHaveLength(1); + expect(readFileSync(statePath, "utf8")).toContain('"status": "completed"'); + } finally { + cleanup(); + } }); it("returns explicit unavailable notice when quorum is not met", async () => { const { default: plugin } = await import("../index.js"); - const { api, hooks, commands } = setupApi({ enabled: true, teamSize: 4, minAnsweringAgents: 3, minParticipatingAgents: 3 }); - - plugin.register(api as never); - - const cmd = commands[0] as Command; - const status = await cmd.handler({ args: "status" }); - expect(status.text).toContain("Braintrust: ON"); - - const beforePromptBuild = hooks.get("before_prompt_build"); - const out = await beforePromptBuild!({ - event: { messages: [{ role: "user", content: "Give me one rollout recommendation." }] }, - context: { - runModel: async ({ role }: { role: string }) => { - if (role === "critic") return { text: "I cannot comply with that request." }; - if (role === "researcher") throw new Error("simulated timeout"); - return { text: "candidate" }; + const { api, hooks, commands, cleanup, statePath } = setupApi({ enabled: true, teamSize: 4, minAnsweringAgents: 3, minParticipatingAgents: 3 }); + + try { + plugin.register(api as never); + + const cmd = commands[0] as Command; + const status = await cmd.handler({ args: "status" }); + expect(status.text).toContain("Braintrust: ON"); + + const beforePromptBuild = hooks.get("before_prompt_build"); + const out = await beforePromptBuild!({ + event: { messages: [{ role: "user", content: "Give me one rollout recommendation." }] }, + context: { + runModel: async ({ role }: { role: string }) => { + if (role === "critic") return { text: "I cannot comply with that request." }; + if (role === "researcher") throw new Error("simulated timeout"); + return { text: "candidate" }; + }, }, - }, - }); - - expect(out?.prependContext).toContain("Quorum could not be satisfied"); - expect(out?.prependContext).toContain("Braintrust temporarily unavailable"); + }); + + expect(out?.prependContext).toContain("Quorum could not be satisfied"); + expect(out?.prependContext).toContain("Braintrust temporarily unavailable"); + expect(readFileSync(statePath, "utf8")).toContain('"status": "unavailable"'); + } finally { + cleanup(); + } }); }); diff --git a/extensions/braintrust/src/orchestrator.js b/extensions/braintrust/src/orchestrator.js new file mode 100644 index 0000000..f7134d6 --- /dev/null +++ b/extensions/braintrust/src/orchestrator.js @@ -0,0 +1,138 @@ +import { existsSync } from "node:fs"; +import { evaluateQuorum } from "./policy.js"; +import { runRuntimeBridge } from "./runtime-bridge.js"; +import { loadState, mapCandidateStatus, mergeSettings, resolveStatePath, saveState, trimPreview } from "./state.js"; +function nowIso() { + return new Date().toISOString(); +} +function buildSessionId() { + return `bt-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`; +} +export class BraintrustOrchestrator { + constructor(api, initialSettings) { + this.api = api; + this.statePath = resolveStatePath((input) => api.resolvePath(input)); + const hadPersistedState = existsSync(this.statePath); + this.state = loadState(this.statePath); + if (!hadPersistedState) { + this.state.enabled = initialSettings.enabled; + this.state.settingsOverrides = { + teamSize: initialSettings.teamSize, + strategy: initialSettings.strategy, + model: initialSettings.model, + criticModel: initialSettings.criticModel, + researcherModel: initialSettings.researcherModel, + synthModel: initialSettings.synthModel, + timeoutSeconds: initialSettings.timeoutSeconds, + minParticipatingAgents: initialSettings.minParticipatingAgents, + minAnsweringAgents: initialSettings.minAnsweringAgents + }; + } + this.settings = mergeSettings(initialSettings, this.state); + this.persist(); + } + getSettings() { + return this.settings; + } + isEnabled() { + return this.state.enabled; + } + getLastQuorumEvaluation() { + return this.lastQuorumEvaluation; + } + statusLine() { + const recent = this.state.sessions[0]; + return [ + `Braintrust: ${this.state.enabled ? "ON" : "OFF"}`, + `strategy=${this.settings.strategy}`, + `teamSize=${this.settings.teamSize}`, + `solver=${this.settings.model}`, + `critic=${this.settings.criticModel}`, + `researcher=${this.settings.researcherModel}`, + `synth=${this.settings.synthModel}`, + `timeout=${this.settings.timeoutSeconds}s`, + `minParticipating=${this.settings.minParticipatingAgents}`, + `minAnswering=${this.settings.minAnsweringAgents}`, + recent ? `lastSession=${recent.id}:${recent.status}` : "lastSession=none" + ].join(" ยท "); + } + sessionsSummary() { + if (this.state.sessions.length === 0) return "Braintrust sessions: none recorded yet."; + const lines = this.state.sessions.slice(0, 5).map((session) => { + const workers = session.workers.map((worker) => `${worker.id}:${worker.status}`).join(", "); + return `${session.id} ยท ${session.status} ยท ${session.startedAt} ยท ${workers}`; + }); + return ["Braintrust sessions:", ...lines].join("\n"); + } + executeAction(action, renderUnavailable) { + if (action === "on") { + this.state.enabled = true; + this.settings = { ...this.settings, enabled: true }; + this.persist(); + return `โœ… ${this.statusLine()}`; + } + if (action === "off") { + this.state.enabled = false; + this.settings = { ...this.settings, enabled: false }; + this.persist(); + return `๐Ÿ›‘ ${this.statusLine()}`; + } + if (action === "sessions") return this.sessionsSummary(); + if (action === "unavailable") return renderUnavailable(); + return this.statusLine(); + } + async runPrompt(prompt, runCandidate) { + const session = { + id: buildSessionId(), + prompt, + strategy: this.settings.strategy, + status: "running", + startedAt: nowIso(), + workers: Array.from({ length: this.settings.teamSize }, (_, index) => ({ + id: `agent-${index + 1}`, + role: index === 0 ? "solver" : index === 1 ? "critic" : index === 2 ? "researcher" : "synthesizer", + model: index === 0 ? this.settings.model : index === 1 ? this.settings.criticModel : index === 2 ? this.settings.researcherModel : this.settings.synthModel, + status: "pending", + updatedAt: nowIso() + })) + }; + this.state.sessions.unshift(session); + this.state.sessions = this.state.sessions.slice(0, 20); + this.persist(); + const bridge = await runRuntimeBridge({ prompt, settings: this.settings }, runCandidate, { + onCandidateStart: ({ id }) => { + const worker = session.workers.find((entry) => entry.id === id); + if (!worker) return; + worker.status = "running"; + worker.updatedAt = nowIso(); + this.persist(); + }, + onCandidateComplete: ({ id, status, text, latencyMs }) => { + this.updateWorker(session, id, status, text, latencyMs); + } + }); + this.lastQuorumEvaluation = evaluateQuorum(bridge.candidates, { + minParticipatingAgents: this.settings.minParticipatingAgents, + minAnsweringAgents: this.settings.minAnsweringAgents + }); + session.status = bridge.unavailable ? "unavailable" : "completed"; + session.unavailableReason = bridge.reason; + session.final = bridge.final; + session.endedAt = nowIso(); + this.persist(); + return bridge; + } + updateWorker(session, id, status, text, latencyMs) { + const worker = session.workers.find((entry) => entry.id === id); + if (!worker) return; + worker.status = mapCandidateStatus(status); + worker.preview = trimPreview(text); + worker.latencyMs = latencyMs; + worker.updatedAt = nowIso(); + this.persist(); + } + persist() { + this.state.updatedAt = nowIso(); + saveState(this.statePath, this.state); + } +} diff --git a/extensions/braintrust/src/orchestrator.test.ts b/extensions/braintrust/src/orchestrator.test.ts new file mode 100644 index 0000000..df74bc7 --- /dev/null +++ b/extensions/braintrust/src/orchestrator.test.ts @@ -0,0 +1,39 @@ +import { mkdtempSync, readFileSync, rmSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { afterEach, describe, expect, it, vi } from "vitest"; +import { BraintrustOrchestrator } from "./orchestrator.js"; +import { DEFAULT_SETTINGS } from "./settings.js"; + +const roots: string[] = []; + +afterEach(() => { + while (roots.length > 0) { + rmSync(roots.pop()!, { recursive: true, force: true }); + } +}); + +describe("BraintrustOrchestrator", () => { + it("persists enabled state and session lifecycle to disk", async () => { + const root = mkdtempSync(join(tmpdir(), "braintrust-orchestrator-test-")); + roots.push(root); + const api = { + resolvePath: (input: string) => join(root, input.replace(/^\.\//, "")), + logger: { info: vi.fn() }, + } as any; + + const orchestrator = new BraintrustOrchestrator(api, { ...DEFAULT_SETTINGS, enabled: false, teamSize: 2 }); + const unavailable = () => "Braintrust temporarily unavailable (test)."; + + expect(orchestrator.executeAction("on", unavailable)).toContain("Braintrust: ON"); + + const result = await orchestrator.runPrompt("hello", async ({ candidateId }) => ({ text: `done-${candidateId}` })); + + expect(result.unavailable).toBe(false); + + const state = JSON.parse(readFileSync(join(root, ".braintrust", "state.json"), "utf8")); + expect(state.enabled).toBe(true); + expect(state.sessions[0].status).toBe("completed"); + expect(state.sessions[0].workers[0].status).toBe("completed"); + }); +}); diff --git a/extensions/braintrust/src/orchestrator.ts b/extensions/braintrust/src/orchestrator.ts new file mode 100644 index 0000000..bd053a9 --- /dev/null +++ b/extensions/braintrust/src/orchestrator.ts @@ -0,0 +1,190 @@ +import { existsSync } from "node:fs"; +import type { OpenClawPluginApi } from "openclaw/plugin-sdk"; +import { evaluateQuorum, type CandidateStatus } from "./policy.js"; +import { runRuntimeBridge, type CandidateRunner } from "./runtime-bridge.js"; +import type { BraintrustSettings } from "./settings.js"; +import { + loadState, + mapCandidateStatus, + mergeSettings, + resolveStatePath, + saveState, + trimPreview, + type BraintrustPersistentState, + type BraintrustSessionState, +} from "./state.js"; + +export type OrchestratorAction = "on" | "off" | "status" | "unavailable" | "sessions"; + +function nowIso(): string { + return new Date().toISOString(); +} + +function buildSessionId(): string { + return `bt-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`; +} + +export class BraintrustOrchestrator { + private readonly statePath: string; + private state: BraintrustPersistentState; + private settings: BraintrustSettings; + private lastQuorumEvaluation: + | ReturnType + | undefined; + + constructor(private readonly api: OpenClawPluginApi, initialSettings: BraintrustSettings) { + this.statePath = resolveStatePath((input) => api.resolvePath(input)); + const hadPersistedState = existsSync(this.statePath); + this.state = loadState(this.statePath); + if (!hadPersistedState) { + this.state.enabled = initialSettings.enabled; + this.state.settingsOverrides = { + teamSize: initialSettings.teamSize, + strategy: initialSettings.strategy, + model: initialSettings.model, + criticModel: initialSettings.criticModel, + researcherModel: initialSettings.researcherModel, + synthModel: initialSettings.synthModel, + timeoutSeconds: initialSettings.timeoutSeconds, + minParticipatingAgents: initialSettings.minParticipatingAgents, + minAnsweringAgents: initialSettings.minAnsweringAgents, + }; + } + this.settings = mergeSettings(initialSettings, this.state); + this.persist(); + } + + getSettings(): BraintrustSettings { + return this.settings; + } + + isEnabled(): boolean { + return this.state.enabled; + } + + getLastQuorumEvaluation() { + return this.lastQuorumEvaluation; + } + + statusLine(): string { + const recent = this.state.sessions[0]; + return [ + `Braintrust: ${this.state.enabled ? "ON" : "OFF"}`, + `strategy=${this.settings.strategy}`, + `teamSize=${this.settings.teamSize}`, + `solver=${this.settings.model}`, + `critic=${this.settings.criticModel}`, + `researcher=${this.settings.researcherModel}`, + `synth=${this.settings.synthModel}`, + `timeout=${this.settings.timeoutSeconds}s`, + `minParticipating=${this.settings.minParticipatingAgents}`, + `minAnswering=${this.settings.minAnsweringAgents}`, + recent ? `lastSession=${recent.id}:${recent.status}` : "lastSession=none", + ].join(" ยท "); + } + + sessionsSummary(): string { + if (this.state.sessions.length === 0) return "Braintrust sessions: none recorded yet."; + const lines = this.state.sessions.slice(0, 5).map((session) => { + const workers = session.workers.map((worker) => `${worker.id}:${worker.status}`).join(", "); + return `${session.id} ยท ${session.status} ยท ${session.startedAt} ยท ${workers}`; + }); + return ["Braintrust sessions:", ...lines].join("\n"); + } + + executeAction(action: OrchestratorAction, renderUnavailable: () => string): string { + if (action === "on") { + this.state.enabled = true; + this.settings = { ...this.settings, enabled: true }; + this.persist(); + return `โœ… ${this.statusLine()}`; + } + if (action === "off") { + this.state.enabled = false; + this.settings = { ...this.settings, enabled: false }; + this.persist(); + return `๐Ÿ›‘ ${this.statusLine()}`; + } + if (action === "sessions") return this.sessionsSummary(); + if (action === "unavailable") return renderUnavailable(); + return this.statusLine(); + } + + async runPrompt(prompt: string, runCandidate: CandidateRunner) { + const session: BraintrustSessionState = { + id: buildSessionId(), + prompt, + strategy: this.settings.strategy, + status: "running", + startedAt: nowIso(), + workers: Array.from({ length: this.settings.teamSize }, (_, index) => ({ + id: `agent-${index + 1}`, + role: index === 0 ? "solver" : index === 1 ? "critic" : index === 2 ? "researcher" : "synthesizer", + model: + index === 0 + ? this.settings.model + : index === 1 + ? this.settings.criticModel + : index === 2 + ? this.settings.researcherModel + : this.settings.synthModel, + status: "pending", + updatedAt: nowIso(), + })), + }; + + this.state.sessions.unshift(session); + this.state.sessions = this.state.sessions.slice(0, 20); + this.persist(); + + const bridge = await runRuntimeBridge( + { prompt, settings: this.settings }, + runCandidate, + { + onCandidateStart: ({ id }) => { + const worker = session.workers.find((entry) => entry.id === id); + if (!worker) return; + worker.status = "running"; + worker.updatedAt = nowIso(); + this.persist(); + }, + onCandidateComplete: ({ id, status, text, latencyMs }) => { + this.updateWorker(session, id, status, text, latencyMs); + }, + }, + ); + + this.lastQuorumEvaluation = evaluateQuorum(bridge.candidates, { + minParticipatingAgents: this.settings.minParticipatingAgents, + minAnsweringAgents: this.settings.minAnsweringAgents, + }); + + session.status = bridge.unavailable ? "unavailable" : "completed"; + session.unavailableReason = bridge.reason; + session.final = bridge.final; + session.endedAt = nowIso(); + this.persist(); + return bridge; + } + + private updateWorker( + session: BraintrustSessionState, + id: string, + status: CandidateStatus, + text?: string, + latencyMs?: number, + ): void { + const worker = session.workers.find((entry) => entry.id === id); + if (!worker) return; + worker.status = mapCandidateStatus(status); + worker.preview = trimPreview(text); + worker.latencyMs = latencyMs; + worker.updatedAt = nowIso(); + this.persist(); + } + + private persist(): void { + this.state.updatedAt = nowIso(); + saveState(this.statePath, this.state); + } +} diff --git a/extensions/braintrust/src/policy.js b/extensions/braintrust/src/policy.js new file mode 100644 index 0000000..77cdf91 --- /dev/null +++ b/extensions/braintrust/src/policy.js @@ -0,0 +1,40 @@ +export const DEFAULT_QUORUM = { + minParticipatingAgents: 2, + minAnsweringAgents: 2 +}; +export function evaluateQuorum(candidates, cfg = DEFAULT_QUORUM) { + const participating = candidates.filter((c) => c.status !== "timeout" && c.status !== "error").length; + const answering = candidates.filter((c) => c.status === "ok").length; + const refused = candidates.filter((c) => c.status === "refusal").length; + const failed = candidates.length - participating; + if (participating < cfg.minParticipatingAgents) { + return { + participating, + answering, + refused, + failed, + meetsQuorum: false, + reason: `only ${participating}/${candidates.length} agents participated` + }; + } + if (answering < cfg.minAnsweringAgents) { + return { + participating, + answering, + refused, + failed, + meetsQuorum: false, + reason: `only ${answering}/${candidates.length} agents produced usable answers` + }; + } + return { + participating, + answering, + refused, + failed, + meetsQuorum: true + }; +} +export function buildUnavailableNotice(evalResult) { + return `Braintrust temporarily unavailable (${evalResult.reason ?? "insufficient quorum"}).`; +} diff --git a/extensions/braintrust/src/runtime-bridge.js b/extensions/braintrust/src/runtime-bridge.js new file mode 100644 index 0000000..703b823 --- /dev/null +++ b/extensions/braintrust/src/runtime-bridge.js @@ -0,0 +1,115 @@ +import { buildUnavailableNotice, evaluateQuorum } from "./policy.js"; +import { synthesizeDeterministic } from "./synth.js"; +function roleFor(index) { + if (index === 1) return "critic"; + if (index === 2) return "researcher"; + if (index === 3) return "synthesizer"; + return "solver"; +} +function modelFor(index, settings) { + if (index === 3) return settings.synthModel; + if (index === 1) return settings.criticModel; + if (index === 2) return settings.researcherModel; + return settings.model; +} +function classifyError(error) { + const msg = String(error ?? "").toLowerCase(); + if (msg.includes("timeout")) return "timeout"; + return "error"; +} +function isLikelyRefusal(text) { + const t = text.trim().toLowerCase(); + if (!t) return false; + return [ + "i can't", + "i cannot", + "can't help with", + "cannot help with", + "cannot comply", + "i'm sorry", + "i am sorry", + "as an ai", + "unable to assist", + "i must refuse" + ].some((needle) => t.includes(needle)); +} +async function withTimeout(promise, timeoutSeconds) { + let timeoutHandle; + const timeoutPromise = new Promise((_, reject) => { + timeoutHandle = setTimeout(() => reject(new Error(`timeout after ${timeoutSeconds}s`)), timeoutSeconds * 1000); + }); + try { + return await Promise.race([promise, timeoutPromise]); + } finally { + if (timeoutHandle) clearTimeout(timeoutHandle); + } +} +export async function runRuntimeBridge(input, runCandidate, hooks = {}) { + const tasks = Array.from({ length: input.settings.teamSize }, async (_, i) => { + const id = `agent-${i + 1}`; + const role = roleFor(i); + const model = modelFor(i, input.settings); + const started = Date.now(); + hooks.onCandidateStart?.({ id, role, model }); + try { + const out = await withTimeout(runCandidate({ + candidateId: id, + role, + model, + prompt: i === 3 ? `${input.prompt}\n\nRespond ONLY with valid JSON in this exact format (no extra text, no markdown):\n{\n "final": "the single best synthesized answer",\n "reasoning": "brief explanation of how you combined the inputs",\n "sources": ["list of agent ids used"]\n}` : input.prompt, + timeoutSeconds: input.settings.timeoutSeconds + }), input.settings.timeoutSeconds); + const latencyMs = out.latencyMs ?? Date.now() - started; + const refusal = out.refusal ?? isLikelyRefusal(out.text); + const candidate = { + id, + model, + status: refusal ? "refusal" : "ok", + text: out.text, + latencyMs + }; + hooks.onCandidateComplete?.(candidate); + return candidate; + } catch (err) { + const candidate = { + id, + model, + status: classifyError(err), + latencyMs: Date.now() - started + }; + hooks.onCandidateComplete?.(candidate); + return candidate; + } + }); + const candidates = await Promise.all(tasks); + const quorum = evaluateQuorum(candidates, { + minParticipatingAgents: input.settings.minParticipatingAgents, + minAnsweringAgents: input.settings.minAnsweringAgents + }); + if (!quorum.meetsQuorum) { + return { + final: buildUnavailableNotice(quorum), + candidates, + unavailable: true, + reason: quorum.reason + }; + } + const synthesis = synthesizeDeterministic({ + prompt: input.prompt, + candidates + }); + const final = synthesis.final ?? buildUnavailableNotice({ + participating: 0, + answering: 0, + refused: 0, + failed: candidates.length, + meetsQuorum: false, + reason: "no usable candidate output" + }); + return { + final, + candidates, + unavailable: false, + synthesis + }; +} diff --git a/extensions/braintrust/src/runtime-bridge.test.ts b/extensions/braintrust/src/runtime-bridge.test.ts index c9404e3..3d58ac9 100644 --- a/extensions/braintrust/src/runtime-bridge.test.ts +++ b/extensions/braintrust/src/runtime-bridge.test.ts @@ -30,7 +30,7 @@ describe("runRuntimeBridge", () => { async ({ role }) => { if (role === "solver") return { text: "ok", refusal: false }; if (role === "critic") throw new Error("timeout after 90s"); - throw new Error("runtime execution failed"); + throw new Error("provider failed"); }, ); expect(result.unavailable).toBe(true); @@ -78,48 +78,52 @@ describe("runRuntimeBridge", () => { expect(result.unavailable).toBe(true); }); - it("uses active chat model for the synthesizer slot and passes worker outputs into synthesis", async () => { + it("uses four distinct models when teamSize=4", async () => { const settings = { ...DEFAULT_SETTINGS, enabled: true, teamSize: 4, - model: "model-a", - criticModel: "model-b", - researcherModel: "model-c", - synthModel: "legacy-synth-model", + model: "gemini-3-flash-preview", + criticModel: "openai-codex/gpt-5.3-codex", + researcherModel: "grok-4-1-fast-reasoning", + synthModel: "gemini-3.1-pro-preview", minParticipatingAgents: 2, minAnsweringAgents: 2, }; - const seen: Array<{ model: string; role: string; prompt: string }> = []; + const seen: string[] = []; const result = await runRuntimeBridge( - { prompt: "hello", settings, activeChatModel: "runtime-chat-model" }, - async ({ model, role, prompt }) => { - seen.push({ model, role, prompt }); - if (role === "synthesizer") { - expect(prompt).toContain("agent-1 (solver, ok)"); - expect(prompt).toContain("agent-2 (critic, ok)"); - expect(prompt).toContain("agent-3 (researcher, ok)"); - return { - text: JSON.stringify({ - final: "synthesized answer", - reasoning: "combined the worker outputs", - sources: ["agent-1", "agent-3"], - }), - refusal: false, - }; - } - return { text: `ok-${role}`, refusal: false }; + { prompt: "hello", settings }, + async ({ model }) => { + seen.push(model); + return { text: `ok-${model}`, refusal: false }; }, ); expect(result.unavailable).toBe(false); - expect(result.final).toBe("synthesized answer"); - expect(result.synthesis?.winnerId).toBe("agent-4"); - expect(result.synthesis?.sources).toEqual(["agent-1", "agent-3"]); - expect(seen.map(({ model, role }) => ({ model, role }))).toEqual([ - { model: "model-a", role: "solver" }, - { model: "model-b", role: "critic" }, - { model: "model-c", role: "researcher" }, - { model: "runtime-chat-model", role: "synthesizer" }, - ]); + expect(new Set(seen)).toEqual( + new Set([ + "gemini-3-flash-preview", + "openai-codex/gpt-5.3-codex", + "grok-4-1-fast-reasoning", + "gemini-3.1-pro-preview", + ]), + ); + }); + + it("reports candidate lifecycle hooks", async () => { + const settings = { ...DEFAULT_SETTINGS, enabled: true, teamSize: 2 }; + const started: string[] = []; + const completed: string[] = []; + + await runRuntimeBridge( + { prompt: "hello", settings }, + async ({ candidateId }) => ({ text: `answer-${candidateId}` }), + { + onCandidateStart: ({ id }) => started.push(id), + onCandidateComplete: ({ id }) => completed.push(id), + }, + ); + + expect(started).toEqual(["agent-1", "agent-2"]); + expect(completed).toEqual(["agent-1", "agent-2"]); }); }); diff --git a/extensions/braintrust/src/runtime-bridge.ts b/extensions/braintrust/src/runtime-bridge.ts index af64d60..a4b2657 100644 --- a/extensions/braintrust/src/runtime-bridge.ts +++ b/extensions/braintrust/src/runtime-bridge.ts @@ -1,13 +1,13 @@ import { buildUnavailableNotice, evaluateQuorum, type Candidate, type CandidateStatus } from "./policy.js"; import type { BraintrustSettings } from "./settings.js"; import { synthesizeDeterministic, type SynthesisOutput } from "./synth.js"; -import { buildWorkerPlan, type WorkerRole } from "./worker-plan.js"; export type CandidateRunnerInput = { - role: WorkerRole; + role: "solver" | "critic" | "researcher" | "synthesizer"; model: string; prompt: string; timeoutSeconds: number; + candidateId: string; }; export type CandidateRunnerOutput = { @@ -21,7 +21,6 @@ export type CandidateRunner = (input: CandidateRunnerInput) => Promise void; + onCandidateComplete?: (candidate: Candidate) => void; +}; + +function roleFor(index: number): CandidateRunnerInput["role"] { + if (index === 1) return "critic"; + if (index === 2) return "researcher"; + if (index === 3) return "synthesizer"; + return "solver"; +} + +function modelFor(index: number, settings: BraintrustSettings): string { + if (index === 3) return settings.synthModel; + if (index === 1) return settings.criticModel; + if (index === 2) return settings.researcherModel; + return settings.model; +} + function classifyError(error: unknown): CandidateStatus { const msg = String(error ?? "").toLowerCase(); if (msg.includes("timeout")) return "timeout"; @@ -71,99 +89,52 @@ async function withTimeout(promise: Promise, timeoutSeconds: number): Prom export async function runRuntimeBridge( input: RuntimeBridgeInput, runCandidate: CandidateRunner, + hooks: RuntimeBridgeHooks = {}, ): Promise { - const initialWorkers = buildWorkerPlan({ - prompt: input.prompt, - settings: input.settings, - activeChatModel: input.activeChatModel, - }); - - const nonSynthWorkers = initialWorkers.filter((worker) => worker.role !== "synthesizer"); - const initialCandidates = await Promise.all( - nonSynthWorkers.map(async (worker) => { - const started = Date.now(); - try { - const out = await withTimeout( - runCandidate({ - role: worker.role, - model: worker.model, - prompt: worker.prompt, - timeoutSeconds: input.settings.timeoutSeconds, - }), - input.settings.timeoutSeconds, - ); - const latencyMs = out.latencyMs ?? Date.now() - started; - const refusal = out.refusal ?? isLikelyRefusal(out.text); - return { - id: worker.id, - role: worker.role, - model: worker.model, - status: refusal ? "refusal" : "ok", - text: out.text, - latencyMs, - } as Candidate; - } catch (err) { - return { - id: worker.id, - role: worker.role, - model: worker.model, - status: classifyError(err), - latencyMs: Date.now() - started, - } as Candidate; - } - }), - ); - - const synthWorker = initialWorkers.find((worker) => worker.role === "synthesizer"); - const candidates = [...initialCandidates]; - - if (synthWorker) { - const synthPlan = buildWorkerPlan({ - prompt: input.prompt, - settings: input.settings, - activeChatModel: input.activeChatModel, - completedWorkers: candidates.map((candidate) => ({ - id: candidate.id, - role: (candidate.role ?? "solver") as WorkerRole, - text: candidate.text, - status: candidate.status, - })), - }).find((worker) => worker.role === "synthesizer"); - - if (synthPlan) { - const started = Date.now(); - try { - const out = await withTimeout( - runCandidate({ - role: synthPlan.role, - model: synthPlan.model, - prompt: synthPlan.prompt, - timeoutSeconds: input.settings.timeoutSeconds, - }), - input.settings.timeoutSeconds, - ); - const latencyMs = out.latencyMs ?? Date.now() - started; - const refusal = out.refusal ?? isLikelyRefusal(out.text); - candidates.push({ - id: synthPlan.id, - role: synthPlan.role, - model: synthPlan.model, - status: refusal ? "refusal" : "ok", - text: out.text, - latencyMs, - }); - } catch (err) { - candidates.push({ - id: synthPlan.id, - role: synthPlan.role, - model: synthPlan.model, - status: classifyError(err), - latencyMs: Date.now() - started, - }); - } + const tasks = Array.from({ length: input.settings.teamSize }, async (_, i) => { + const id = `agent-${i + 1}`; + const role = roleFor(i); + const model = modelFor(i, input.settings); + const started = Date.now(); + hooks.onCandidateStart?.({ id, role, model }); + try { + const out = await withTimeout( + runCandidate({ + candidateId: id, + role, + model, + prompt: + i === 3 + ? `${input.prompt}\n\nRespond ONLY with valid JSON in this exact format (no extra text, no markdown):\n{\n "final": "the single best synthesized answer",\n "reasoning": "brief explanation of how you combined the inputs",\n "sources": ["list of agent ids used"]\n}` + : input.prompt, + timeoutSeconds: input.settings.timeoutSeconds, + }), + input.settings.timeoutSeconds, + ); + const latencyMs = out.latencyMs ?? Date.now() - started; + const refusal = out.refusal ?? isLikelyRefusal(out.text); + const candidate = { + id, + model, + status: refusal ? "refusal" : "ok", + text: out.text, + latencyMs, + } as Candidate; + hooks.onCandidateComplete?.(candidate); + return candidate; + } catch (err) { + const candidate = { + id, + model, + status: classifyError(err), + latencyMs: Date.now() - started, + } as Candidate; + hooks.onCandidateComplete?.(candidate); + return candidate; } - } + }); + const candidates = await Promise.all(tasks); const quorum = evaluateQuorum(candidates, { minParticipatingAgents: input.settings.minParticipatingAgents, minAnsweringAgents: input.settings.minAnsweringAgents, diff --git a/extensions/braintrust/src/settings.js b/extensions/braintrust/src/settings.js new file mode 100644 index 0000000..5c513ae --- /dev/null +++ b/extensions/braintrust/src/settings.js @@ -0,0 +1,37 @@ +import { DEFAULT_QUORUM } from "./policy.js"; +export const DEFAULT_SETTINGS = { + enabled: false, + teamSize: 4, + strategy: "panel", + model: "gemini-3-flash-preview", + criticModel: "openai-codex/gpt-5.3-codex", + researcherModel: "grok-4-1-fast-reasoning", + synthModel: "gemini-3.1-pro-preview", + timeoutSeconds: 90, + minParticipatingAgents: DEFAULT_QUORUM.minParticipatingAgents, + minAnsweringAgents: DEFAULT_QUORUM.minAnsweringAgents +}; +function clampInt(value, min, max, fallback) { + const n = Number(value); + if (!Number.isFinite(n)) return fallback; + return Math.max(min, Math.min(max, Math.trunc(n))); +} +export function readSettings(raw) { + const p = raw ?? {}; + const strategy = p.strategy === "independent" || p.strategy === "debate" || p.strategy === "panel" ? p.strategy : DEFAULT_SETTINGS.strategy; + const teamSize = clampInt(p.teamSize, 1, 4, DEFAULT_SETTINGS.teamSize); + const minParticipatingAgents = clampInt(p.minParticipatingAgents, 1, teamSize, DEFAULT_SETTINGS.minParticipatingAgents); + const minAnsweringAgents = clampInt(p.minAnsweringAgents, 1, minParticipatingAgents, DEFAULT_SETTINGS.minAnsweringAgents); + return { + enabled: Boolean(p.enabled ?? DEFAULT_SETTINGS.enabled), + teamSize, + strategy, + model: String(p.model ?? DEFAULT_SETTINGS.model), + criticModel: String(p.criticModel ?? DEFAULT_SETTINGS.criticModel), + researcherModel: String(p.researcherModel ?? DEFAULT_SETTINGS.researcherModel), + synthModel: String(p.synthModel ?? DEFAULT_SETTINGS.synthModel), + timeoutSeconds: clampInt(p.timeoutSeconds, 10, 300, DEFAULT_SETTINGS.timeoutSeconds), + minParticipatingAgents, + minAnsweringAgents + }; +} diff --git a/extensions/braintrust/src/state.js b/extensions/braintrust/src/state.js new file mode 100644 index 0000000..5ee0798 --- /dev/null +++ b/extensions/braintrust/src/state.js @@ -0,0 +1,47 @@ +import { mkdirSync, readFileSync, writeFileSync } from "node:fs"; +import { dirname } from "node:path"; +export const DEFAULT_PERSISTED_STATE = { + version: 1, + enabled: false, + settingsOverrides: {}, + sessions: [], + updatedAt: new Date(0).toISOString() +}; +export function resolveStatePath(resolver) { + return resolver("./.braintrust/state.json"); +} +export function loadState(path) { + try { + const raw = JSON.parse(readFileSync(path, "utf8")); + return { + version: 1, + enabled: typeof raw.enabled === "boolean" ? raw.enabled : DEFAULT_PERSISTED_STATE.enabled, + settingsOverrides: raw.settingsOverrides && typeof raw.settingsOverrides === "object" ? raw.settingsOverrides : {}, + sessions: Array.isArray(raw.sessions) ? raw.sessions.slice(0, 20) : [], + updatedAt: typeof raw.updatedAt === "string" ? raw.updatedAt : new Date().toISOString() + }; + } catch { + return { ...DEFAULT_PERSISTED_STATE, updatedAt: new Date().toISOString() }; + } +} +export function saveState(path, state) { + mkdirSync(dirname(path), { recursive: true }); + writeFileSync(path, JSON.stringify({ ...state, updatedAt: new Date().toISOString() }, null, 2)); +} +export function mergeSettings(base, state) { + return { + ...base, + ...state.settingsOverrides, + enabled: state.enabled + }; +} +export function trimPreview(text) { + const value = text?.replace(/\s+/g, " ").trim(); + if (!value) return void 0; + return value.length > 160 ? `${value.slice(0, 157)}...` : value; +} +export function mapCandidateStatus(status) { + if (status === "ok") return "completed"; + if (status === "refusal") return "refused"; + return status; +} diff --git a/extensions/braintrust/src/state.ts b/extensions/braintrust/src/state.ts new file mode 100644 index 0000000..b82852b --- /dev/null +++ b/extensions/braintrust/src/state.ts @@ -0,0 +1,86 @@ +import { mkdirSync, readFileSync, writeFileSync } from "node:fs"; +import { dirname } from "node:path"; +import type { BraintrustSettings } from "./settings.js"; +import type { CandidateStatus } from "./policy.js"; + +export type BraintrustWorkerState = { + id: string; + role: "solver" | "critic" | "researcher" | "synthesizer"; + model: string; + status: "pending" | "running" | "completed" | "refused" | "timeout" | "error"; + latencyMs?: number; + preview?: string; + updatedAt: string; +}; + +export type BraintrustSessionState = { + id: string; + prompt: string; + strategy: BraintrustSettings["strategy"]; + status: "running" | "completed" | "unavailable" | "error"; + startedAt: string; + endedAt?: string; + unavailableReason?: string; + final?: string; + workers: BraintrustWorkerState[]; +}; + +export type BraintrustPersistentState = { + version: 1; + enabled: boolean; + settingsOverrides: Partial; + sessions: BraintrustSessionState[]; + updatedAt: string; +}; + +export const DEFAULT_PERSISTED_STATE: BraintrustPersistentState = { + version: 1, + enabled: false, + settingsOverrides: {}, + sessions: [], + updatedAt: new Date(0).toISOString(), +}; + +export function resolveStatePath(resolver: (input: string) => string): string { + return resolver("./.braintrust/state.json"); +} + +export function loadState(path: string): BraintrustPersistentState { + try { + const raw = JSON.parse(readFileSync(path, "utf8")) as Partial; + return { + version: 1, + enabled: typeof raw.enabled === "boolean" ? raw.enabled : DEFAULT_PERSISTED_STATE.enabled, + settingsOverrides: raw.settingsOverrides && typeof raw.settingsOverrides === "object" ? raw.settingsOverrides : {}, + sessions: Array.isArray(raw.sessions) ? raw.sessions.slice(0, 20) as BraintrustSessionState[] : [], + updatedAt: typeof raw.updatedAt === "string" ? raw.updatedAt : new Date().toISOString(), + }; + } catch { + return { ...DEFAULT_PERSISTED_STATE, updatedAt: new Date().toISOString() }; + } +} + +export function saveState(path: string, state: BraintrustPersistentState): void { + mkdirSync(dirname(path), { recursive: true }); + writeFileSync(path, JSON.stringify({ ...state, updatedAt: new Date().toISOString() }, null, 2)); +} + +export function mergeSettings(base: BraintrustSettings, state: BraintrustPersistentState): BraintrustSettings { + return { + ...base, + ...state.settingsOverrides, + enabled: state.enabled, + }; +} + +export function trimPreview(text: string | undefined): string | undefined { + const value = text?.replace(/\s+/g, " ").trim(); + if (!value) return undefined; + return value.length > 160 ? `${value.slice(0, 157)}...` : value; +} + +export function mapCandidateStatus(status: CandidateStatus): BraintrustWorkerState["status"] { + if (status === "ok") return "completed"; + if (status === "refusal") return "refused"; + return status; +} diff --git a/extensions/braintrust/src/synth.js b/extensions/braintrust/src/synth.js new file mode 100644 index 0000000..29cde35 --- /dev/null +++ b/extensions/braintrust/src/synth.js @@ -0,0 +1,31 @@ +export function synthesizeDeterministic(input) { + const ok = input.candidates.filter((c) => c.status === "ok" && c.text?.trim()); + if (ok.length === 0) { + return { + final: "Braintrust temporarily unavailable (no usable candidate output)." + }; + } + const synthAgent = input.candidates.find((c) => c.id === "agent-4"); + if (synthAgent?.status === "ok" && synthAgent.text) { + try { + const parsed = JSON.parse(synthAgent.text.trim()); + if (parsed.final && typeof parsed.final === "string") { + return { + final: parsed.final, + winnerId: "agent-4" + }; + } + } catch { + } + } + ok.sort((a, b) => { + const la = (a.text ?? "").length; + const lb = (b.text ?? "").length; + if (la !== lb) return la - lb; + return a.id.localeCompare(b.id); + }); + return { + final: ok[0].text ?? "", + winnerId: ok[0].id + }; +} diff --git a/scripts/validate-braintrust.sh b/scripts/validate-braintrust.sh index b5fca6a..dd87c4a 100755 --- a/scripts/validate-braintrust.sh +++ b/scripts/validate-braintrust.sh @@ -16,8 +16,8 @@ echo "plugin: $PLUGIN_DIR" echo "[1/2] Running unit/runtime tests for command + synthesis contract..." ( cd "$PLUGIN_DIR" - if command -v pnpm >/dev/null 2>&1; then - pnpm vitest run src/live-validation.test.ts src/runtime-bridge.integration.test.ts + if command -v pnpm >/dev/null 2>&1 && pnpm exec vitest --version >/dev/null 2>&1; then + pnpm exec vitest run src/live-validation.test.ts src/runtime-bridge.integration.test.ts else npm exec --yes vitest run src/live-validation.test.ts src/runtime-bridge.integration.test.ts fi From f04449a1245db5000ba11ca45d3aeb3ce4c4cc1b Mon Sep 17 00:00:00 2001 From: Victor al Ghul Date: Sat, 7 Mar 2026 23:58:19 +0000 Subject: [PATCH 14/14] Remove Braintrust model defaults --- extensions/braintrust/README.md | 21 +-- extensions/braintrust/index.js | 100 ++++++------- extensions/braintrust/index.ts | 52 +++++-- extensions/braintrust/openclaw.plugin.json | 8 +- extensions/braintrust/src/index.test.ts | 8 +- extensions/braintrust/src/orchestrator.js | 27 ++-- extensions/braintrust/src/orchestrator.ts | 35 +++-- extensions/braintrust/src/policy.test.ts | 20 +-- .../src/runtime-bridge.integration.test.ts | 4 +- extensions/braintrust/src/runtime-bridge.js | 138 +++++++++--------- .../braintrust/src/runtime-bridge.test.ts | 19 +-- extensions/braintrust/src/runtime-bridge.ts | 117 ++++++++++----- extensions/braintrust/src/settings.js | 19 ++- extensions/braintrust/src/settings.test.ts | 10 +- extensions/braintrust/src/settings.ts | 24 +-- extensions/braintrust/src/synth.js | 33 +++-- extensions/braintrust/src/synth.test.ts | 6 +- extensions/braintrust/src/worker-plan.js | 62 ++++++++ extensions/braintrust/src/worker-plan.test.ts | 30 +++- extensions/braintrust/src/worker-plan.ts | 25 +++- 20 files changed, 463 insertions(+), 295 deletions(-) create mode 100644 extensions/braintrust/src/worker-plan.js diff --git a/extensions/braintrust/README.md b/extensions/braintrust/README.md index 0e81b65..7f6aee3 100644 --- a/extensions/braintrust/README.md +++ b/extensions/braintrust/README.md @@ -15,13 +15,20 @@ Feature-flagged Braintrust plugin for OpenClaw with persistent control state and ## Runtime behavior When enabled and a runtime executor is present, Braintrust now: 1. Creates a durable session record -2. Starts up to 4 workers with distinct roles/models -3. Updates worker state as each candidate finishes -4. Evaluates quorum -5. Persists the final synthesized answer or explicit unavailable notice +2. Starts up to 4 workers with distinct roles +3. Resolves each worker model at runtime +4. Updates worker state as each candidate finishes +5. Evaluates quorum +6. Persists the final synthesized answer or explicit unavailable notice Recent sessions can be inspected with `/braintrust sessions`. +## Model routing +- No model/provider defaults are baked in. +- Empty role settings mean "inherit the active chat model at runtime." +- `synthModel` is retained only for backward-compatible parsing; synthesis inherits the active chat model at runtime. +- If a role-specific model is set explicitly, Braintrust uses that configured value for that role. + ## Durable state Braintrust stores durable local state in: @@ -40,10 +47,4 @@ pnpm vitest run ./scripts/validate-braintrust.sh ``` -## Default model routing -- solver (`model`): `gemini-3-flash-preview` -- critic (`criticModel`): `openai-codex/gpt-5.3-codex` -- researcher (`researcherModel`): `grok-4-1-fast-reasoning` -- synthesizer (`synthModel`): `gemini-3.1-pro-preview` - Note: plugin id is `braintrust-plugin`; command remains `/braintrust`. diff --git a/extensions/braintrust/index.js b/extensions/braintrust/index.js index 1822d85..4034d9a 100644 --- a/extensions/braintrust/index.js +++ b/extensions/braintrust/index.js @@ -8,10 +8,10 @@ const braintrustConfigSchema = { enabled: { type: "boolean", default: false }, teamSize: { type: "integer", minimum: 1, maximum: 4, default: 4 }, strategy: { type: "string", enum: ["independent", "debate", "panel"], default: "panel" }, - model: { type: "string", default: "gemini-3-flash-preview" }, - criticModel: { type: "string", default: "openai-codex/gpt-5.3-codex" }, - researcherModel: { type: "string", default: "grok-4-1-fast-reasoning" }, - synthModel: { type: "string", default: "gemini-3.1-pro-preview" }, + model: { type: "string", default: "" }, + criticModel: { type: "string", default: "" }, + researcherModel: { type: "string", default: "" }, + synthModel: { type: "string", default: "" }, timeoutSeconds: { type: "integer", minimum: 10, maximum: 300, default: 90 }, minParticipatingAgents: { type: "integer", minimum: 1, maximum: 4, default: 2 }, minAnsweringAgents: { type: "integer", minimum: 1, maximum: 4, default: 2 } @@ -43,9 +43,7 @@ function readTextOutput(out) { if (typeof candidate.text === "string") return candidate.text; if (typeof candidate.outputText === "string") return candidate.outputText; if (typeof candidate.assistantText === "string") return candidate.assistantText; - if (Array.isArray(candidate.assistantTexts) && typeof candidate.assistantTexts[0] === "string") { - return candidate.assistantTexts[0]; - } + if (Array.isArray(candidate.assistantTexts) && typeof candidate.assistantTexts[0] === "string") return candidate.assistantTexts[0]; if (typeof candidate.content === "string") return candidate.content; if (Array.isArray(candidate.choices)) { const first = candidate.choices[0]; @@ -63,8 +61,7 @@ function resolveRuntimeExecutor(api, runtimeContext) { const fn = obj[name]; if (typeof fn !== "function") continue; return async (input) => { - const out = await fn({ - model: input.model, + const payload = { role: input.role, timeoutSeconds: input.timeoutSeconds, metadata: { candidateId: input.candidateId, role: input.role, pluginId: "braintrust-plugin" }, @@ -75,23 +72,32 @@ function resolveRuntimeExecutor(api, runtimeContext) { }, { role: "user", content: input.prompt } ] - }); + }; + if (input.model.trim()) payload.model = input.model; + const out = await fn(payload); const text = readTextOutput(out).trim(); if (!text) throw new Error("empty output"); - return { - text, - refusal: /\b(cannot comply|i can't|i cannot|refuse|not able)\b/i.test(text) - }; + return { text, refusal: /\b(cannot comply|i can't|i cannot|refuse|not able)\b/i.test(text) }; }; } } return void 0; } +function describeConfiguredModel(value, synth = false) { + if (synth) return "inherit active chat model at runtime"; + return value.trim() || "inherit active chat model at runtime"; +} function buildFallbackPrepend(settings) { return [ "BRAINTRUST MODE ACTIVE.", `Use a ${settings.teamSize}-agent internal panel with strategy=${settings.strategy}.`, - `Simulate roles: solver(model=${settings.model}), critic(model=${settings.criticModel}), researcher(model=${settings.researcherModel}), synthesizer(model=${settings.synthModel}).`, + [ + "Role model routing:", + `- solver: ${describeConfiguredModel(settings.model)}`, + `- critic: ${describeConfiguredModel(settings.criticModel)}`, + `- researcher: ${describeConfiguredModel(settings.researcherModel)}`, + `- synthesizer: ${describeConfiguredModel(settings.synthModel, true)}` + ].join("\n"), `Quorum contract: require >=${settings.minParticipatingAgents} participating and >=${settings.minAnsweringAgents} answering agents.`, "If quorum cannot be satisfied, return exactly: Braintrust temporarily unavailable (...).", "Return only one final answer to the user." @@ -104,78 +110,62 @@ var index_default = { configSchema: braintrustConfigSchema, register(api) { const orchestrator = new BraintrustOrchestrator(api, readSettings(api.pluginConfig)); - const renderUnavailable = () => buildUnavailableNotice( - orchestrator.getLastQuorumEvaluation() ?? { - participating: 0, - answering: 0, - refused: 0, - failed: orchestrator.getSettings().teamSize, - meetsQuorum: false, - reason: `only 0/${orchestrator.getSettings().teamSize} agents participated` - } - ); + const renderUnavailable = () => buildUnavailableNotice(orchestrator.getLastQuorumEvaluation() ?? { + participating: 0, + answering: 0, + refused: 0, + failed: orchestrator.getSettings().teamSize, + meetsQuorum: false, + reason: `only 0/${orchestrator.getSettings().teamSize} agents participated` + }); api.registerCommand({ name: "braintrust", description: "Control Braintrust mode: /braintrust on|off|status|unavailable|sessions", acceptsArgs: true, handler: async (ctx) => ({ text: orchestrator.executeAction(parseBraintrustAction(ctx.args), renderUnavailable) }) }); - api.registerCli?.( - ({ program }) => { - program.command("braintrust").description("Braintrust controls for local CLI surfaces").argument("[action]", "on|off|status|unavailable|sessions", "status").action((action) => { - console.log(orchestrator.executeAction(parseBraintrustAction(action), renderUnavailable)); - }); - }, - { commands: ["braintrust"] } - ); + api.registerCli?.(({ program }) => { + program.command("braintrust").description("Braintrust controls for local CLI surfaces").argument("[action]", "on|off|status|unavailable|sessions", "status").action((action) => { + console.log(orchestrator.executeAction(parseBraintrustAction(action), renderUnavailable)); + }); + }, { commands: ["braintrust"] }); api.on("before_prompt_build", async (payload) => { if (!orchestrator.isEnabled()) return; const event = payload?.event ?? payload ?? {}; const context = payload?.context ?? {}; const prompt = extractPromptFromMessages(event?.messages) || event.prompt || ""; const execute = resolveRuntimeExecutor(api, context); + const activeChatModel = typeof event?.model === "string" ? event.model : void 0; if (!execute || !prompt) { api.logger.info("[braintrust] runtime bridge unavailable in hook context, using policy-only prompt injection"); return { prependContext: buildFallbackPrepend(orchestrator.getSettings()) }; } - const bridge = await orchestrator.runPrompt(prompt, ({ role, model, prompt: p, timeoutSeconds, candidateId }) => execute({ role, model, prompt: p, timeoutSeconds, candidateId })); - api.logger.info( - `[braintrust] runtime bridge complete unavailable=${bridge.unavailable} candidates=${bridge.candidates.length}` + const bridge = await orchestrator.runPrompt( + prompt, + ({ role, model, prompt: p, timeoutSeconds, candidateId }) => execute({ role, model, prompt: p, timeoutSeconds, candidateId }), + activeChatModel ); + api.logger.info(`[braintrust] runtime bridge complete unavailable=${bridge.unavailable} candidates=${bridge.candidates.length}`); if (bridge.unavailable) { return { - prependContext: [ - "BRAINTRUST MODE ACTIVE.", - "Quorum could not be satisfied by runtime-bridge execution.", - `Return exactly this notice: ${bridge.final}` - ].join("\n") + prependContext: ["BRAINTRUST MODE ACTIVE.", "Quorum could not be satisfied by runtime-bridge execution.", `Return exactly this notice: ${bridge.final}`].join("\n") }; } return { - prependContext: [ - "BRAINTRUST MODE ACTIVE.", - "Use this runtime-bridge synthesis as your final answer.", - bridge.final - ].join("\n\n") + prependContext: ["BRAINTRUST MODE ACTIVE.", "Use this runtime-bridge synthesis as your final answer.", bridge.final].join("\n\n") }; }); api.on("llm_input", async (payload) => { if (!orchestrator.isEnabled()) return; const event = payload?.event ?? payload; const context = payload?.context ?? {}; - api.logger.info( - `[braintrust] llm_input run=${event?.runId ?? "unknown"} session=${context?.sessionKey ?? "unknown"} model=${event?.model ?? "unknown"}` - ); + api.logger.info(`[braintrust] llm_input run=${event?.runId ?? "unknown"} session=${context?.sessionKey ?? "unknown"} model=${event?.model ?? "unknown"}`); }); api.on("llm_output", async (payload) => { if (!orchestrator.isEnabled()) return; const event = payload?.event ?? payload; - api.logger.info( - `[braintrust] llm_output run=${event?.runId ?? "unknown"} model=${event?.model ?? "unknown"} responses=${Array.isArray(event?.assistantTexts) ? event.assistantTexts.length : 0}` - ); + api.logger.info(`[braintrust] llm_output run=${event?.runId ?? "unknown"} model=${event?.model ?? "unknown"} responses=${Array.isArray(event?.assistantTexts) ? event.assistantTexts.length : 0}`); }); } }; -export { - index_default as default -}; +export { index_default as default }; diff --git a/extensions/braintrust/index.ts b/extensions/braintrust/index.ts index e50e889..e481977 100644 --- a/extensions/braintrust/index.ts +++ b/extensions/braintrust/index.ts @@ -17,10 +17,10 @@ const braintrustConfigSchema = { enabled: { type: "boolean", default: false }, teamSize: { type: "integer", minimum: 1, maximum: 4, default: 4 }, strategy: { type: "string", enum: ["independent", "debate", "panel"], default: "panel" }, - model: { type: "string", default: "gemini-3-flash-preview" }, - criticModel: { type: "string", default: "openai-codex/gpt-5.3-codex" }, - researcherModel: { type: "string", default: "grok-4-1-fast-reasoning" }, - synthModel: { type: "string", default: "gemini-3.1-pro-preview" }, + model: { type: "string", default: "" }, + criticModel: { type: "string", default: "" }, + researcherModel: { type: "string", default: "" }, + synthModel: { type: "string", default: "" }, timeoutSeconds: { type: "integer", minimum: 10, maximum: 300, default: 90 }, minParticipatingAgents: { type: "integer", minimum: 1, maximum: 4, default: 2 }, minAnsweringAgents: { type: "integer", minimum: 1, maximum: 4, default: 2 }, @@ -53,7 +53,8 @@ function extractPromptFromMessages(messages: unknown): string { if (Array.isArray(c)) { const text = c .map((part) => (typeof part === "string" ? part : (part as { text?: string })?.text ?? "")) - .join("\n") + .join(" +") .trim(); if (text) return text; } @@ -99,8 +100,7 @@ function resolveRuntimeExecutor(api: OpenClawPluginApi, runtimeContext: unknown) const fn = obj[name]; if (typeof fn !== "function") continue; return async (input) => { - const out = await (fn as (payload: unknown) => Promise)({ - model: input.model, + const payload: Record = { role: input.role, timeoutSeconds: input.timeoutSeconds, metadata: { candidateId: input.candidateId, role: input.role, pluginId: "braintrust-plugin" }, @@ -111,12 +111,14 @@ function resolveRuntimeExecutor(api: OpenClawPluginApi, runtimeContext: unknown) }, { role: "user", content: input.prompt }, ], - }); + }; + if (input.model.trim()) payload.model = input.model; + const out = await (fn as (payload: unknown) => Promise)(payload); const text = readTextOutput(out).trim(); if (!text) throw new Error("empty output"); return { text, - refusal: /\b(cannot comply|i can't|i cannot|refuse|not able)\b/i.test(text), + refusal: /(cannot comply|i can't|i cannot|refuse|not able)/i.test(text), }; }; } @@ -125,15 +127,28 @@ function resolveRuntimeExecutor(api: OpenClawPluginApi, runtimeContext: unknown) return undefined; } +function describeConfiguredModel(value: string, synth = false): string { + if (synth) return "inherit active chat model at runtime"; + return value.trim() || "inherit active chat model at runtime"; +} + function buildFallbackPrepend(settings: BraintrustSettings): string { return [ "BRAINTRUST MODE ACTIVE.", `Use a ${settings.teamSize}-agent internal panel with strategy=${settings.strategy}.`, - `Simulate roles: solver(model=${settings.model}), critic(model=${settings.criticModel}), researcher(model=${settings.researcherModel}), synthesizer(model=${settings.synthModel}).`, + [ + "Role model routing:", + `- solver: ${describeConfiguredModel(settings.model)}`, + `- critic: ${describeConfiguredModel(settings.criticModel)}`, + `- researcher: ${describeConfiguredModel(settings.researcherModel)}`, + `- synthesizer: ${describeConfiguredModel(settings.synthModel, true)}`, + ].join(" +"), `Quorum contract: require >=${settings.minParticipatingAgents} participating and >=${settings.minAnsweringAgents} answering agents.`, "If quorum cannot be satisfied, return exactly: Braintrust temporarily unavailable (...).", "Return only one final answer to the user.", - ].join("\n"); + ].join(" +"); } export default { @@ -183,14 +198,18 @@ export default { const context = ((payload as any)?.context ?? {}) as any; const prompt = extractPromptFromMessages((event as any)?.messages) || event.prompt || ""; const execute = resolveRuntimeExecutor(api, context); + const activeChatModel = typeof (event as any)?.model === "string" ? (event as any).model : undefined; if (!execute || !prompt) { api.logger.info("[braintrust] runtime bridge unavailable in hook context, using policy-only prompt injection"); return { prependContext: buildFallbackPrepend(orchestrator.getSettings()) }; } - const bridge = await orchestrator.runPrompt(prompt, ({ role, model, prompt: p, timeoutSeconds, candidateId }) => - execute({ role, model, prompt: p, timeoutSeconds, candidateId }), + const bridge = await orchestrator.runPrompt( + prompt, + ({ role, model, prompt: p, timeoutSeconds, candidateId }) => + execute({ role, model, prompt: p, timeoutSeconds, candidateId }), + activeChatModel, ); api.logger.info( @@ -203,7 +222,8 @@ export default { "BRAINTRUST MODE ACTIVE.", "Quorum could not be satisfied by runtime-bridge execution.", `Return exactly this notice: ${bridge.final}`, - ].join("\n"), + ].join(" +"), }; } @@ -212,7 +232,9 @@ export default { "BRAINTRUST MODE ACTIVE.", "Use this runtime-bridge synthesis as your final answer.", bridge.final, - ].join("\n\n"), + ].join(" + +"), }; }); diff --git a/extensions/braintrust/openclaw.plugin.json b/extensions/braintrust/openclaw.plugin.json index c591bb2..1ff5e32 100644 --- a/extensions/braintrust/openclaw.plugin.json +++ b/extensions/braintrust/openclaw.plugin.json @@ -25,19 +25,19 @@ }, "model": { "type": "string", - "default": "model-a" + "default": "" }, "criticModel": { "type": "string", - "default": "model-b" + "default": "" }, "researcherModel": { "type": "string", - "default": "model-c" + "default": "" }, "synthModel": { "type": "string", - "default": "legacy-synth-model" + "default": "" }, "timeoutSeconds": { "type": "integer", diff --git a/extensions/braintrust/src/index.test.ts b/extensions/braintrust/src/index.test.ts index a7614f1..2f4060d 100644 --- a/extensions/braintrust/src/index.test.ts +++ b/extensions/braintrust/src/index.test.ts @@ -45,7 +45,7 @@ describe("braintrust plugin runtime integration", () => { it("executes runtime bridge through before_prompt_build when runtime executor exists", async () => { const { default: plugin } = await import("../index.js"); - const calls: Array<{ model: string; role: string }> = []; + const calls: Array<{ model?: string; role: string }> = []; const { api, hooks, cleanup } = setupApi({ enabled: true, teamSize: 3 }); try { @@ -57,7 +57,7 @@ describe("braintrust plugin runtime integration", () => { const out = await beforePromptBuild!({ event: { messages: [{ role: "user", content: "What is 2+2?" }] }, context: { - runModel: async ({ model, role }: { model: string; role: string }) => { + runModel: async ({ model, role }: { model?: string; role: string }) => { calls.push({ model, role }); if (role === "critic") return { text: "The answer is likely four." }; return { text: "4" }; @@ -66,6 +66,7 @@ describe("braintrust plugin runtime integration", () => { }); expect(calls).toHaveLength(3); + expect(calls.every((call) => call.model === undefined)).toBe(true); expect(out?.prependContext).toContain("Use this runtime-bridge synthesis as your final answer."); expect(out?.prependContext).toContain("4"); } finally { @@ -87,7 +88,8 @@ describe("braintrust plugin runtime integration", () => { }); expect(out?.prependContext).toContain("BRAINTRUST MODE ACTIVE."); - expect(out?.prependContext).toContain("Simulate roles:"); + expect(out?.prependContext).toContain("Role model routing:"); + expect(out?.prependContext).toContain("inherit active chat model at runtime"); } finally { cleanup(); } diff --git a/extensions/braintrust/src/orchestrator.js b/extensions/braintrust/src/orchestrator.js index f7134d6..3a79783 100644 --- a/extensions/braintrust/src/orchestrator.js +++ b/extensions/braintrust/src/orchestrator.js @@ -2,13 +2,17 @@ import { existsSync } from "node:fs"; import { evaluateQuorum } from "./policy.js"; import { runRuntimeBridge } from "./runtime-bridge.js"; import { loadState, mapCandidateStatus, mergeSettings, resolveStatePath, saveState, trimPreview } from "./state.js"; +import { buildWorkerPlan } from "./worker-plan.js"; function nowIso() { return new Date().toISOString(); } function buildSessionId() { return `bt-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`; } -export class BraintrustOrchestrator { +function describeModel(model) { + return model.trim() || "inherit-active-chat-model"; +} +class BraintrustOrchestrator { constructor(api, initialSettings) { this.api = api; this.statePath = resolveStatePath((input) => api.resolvePath(input)); @@ -46,10 +50,10 @@ export class BraintrustOrchestrator { `Braintrust: ${this.state.enabled ? "ON" : "OFF"}`, `strategy=${this.settings.strategy}`, `teamSize=${this.settings.teamSize}`, - `solver=${this.settings.model}`, - `critic=${this.settings.criticModel}`, - `researcher=${this.settings.researcherModel}`, - `synth=${this.settings.synthModel}`, + `solver=${describeModel(this.settings.model)}`, + `critic=${describeModel(this.settings.criticModel)}`, + `researcher=${describeModel(this.settings.researcherModel)}`, + "synth=inherit-active-chat-model", `timeout=${this.settings.timeoutSeconds}s`, `minParticipating=${this.settings.minParticipatingAgents}`, `minAnswering=${this.settings.minAnsweringAgents}`, @@ -81,17 +85,17 @@ export class BraintrustOrchestrator { if (action === "unavailable") return renderUnavailable(); return this.statusLine(); } - async runPrompt(prompt, runCandidate) { + async runPrompt(prompt, runCandidate, activeChatModel) { const session = { id: buildSessionId(), prompt, strategy: this.settings.strategy, status: "running", startedAt: nowIso(), - workers: Array.from({ length: this.settings.teamSize }, (_, index) => ({ - id: `agent-${index + 1}`, - role: index === 0 ? "solver" : index === 1 ? "critic" : index === 2 ? "researcher" : "synthesizer", - model: index === 0 ? this.settings.model : index === 1 ? this.settings.criticModel : index === 2 ? this.settings.researcherModel : this.settings.synthModel, + workers: buildWorkerPlan({ prompt, settings: this.settings, activeChatModel }).map((worker) => ({ + id: worker.id, + role: worker.role, + model: worker.model, status: "pending", updatedAt: nowIso() })) @@ -99,7 +103,7 @@ export class BraintrustOrchestrator { this.state.sessions.unshift(session); this.state.sessions = this.state.sessions.slice(0, 20); this.persist(); - const bridge = await runRuntimeBridge({ prompt, settings: this.settings }, runCandidate, { + const bridge = await runRuntimeBridge({ prompt, settings: this.settings, activeChatModel }, runCandidate, { onCandidateStart: ({ id }) => { const worker = session.workers.find((entry) => entry.id === id); if (!worker) return; @@ -136,3 +140,4 @@ export class BraintrustOrchestrator { saveState(this.statePath, this.state); } } +export { BraintrustOrchestrator }; diff --git a/extensions/braintrust/src/orchestrator.ts b/extensions/braintrust/src/orchestrator.ts index bd053a9..074ab9c 100644 --- a/extensions/braintrust/src/orchestrator.ts +++ b/extensions/braintrust/src/orchestrator.ts @@ -13,6 +13,7 @@ import { type BraintrustPersistentState, type BraintrustSessionState, } from "./state.js"; +import { buildWorkerPlan } from "./worker-plan.js"; export type OrchestratorAction = "on" | "off" | "status" | "unavailable" | "sessions"; @@ -24,6 +25,10 @@ function buildSessionId(): string { return `bt-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`; } +function describeModel(model: string): string { + return model.trim() || "inherit-active-chat-model"; +} + export class BraintrustOrchestrator { private readonly statePath: string; private state: BraintrustPersistentState; @@ -72,10 +77,10 @@ export class BraintrustOrchestrator { `Braintrust: ${this.state.enabled ? "ON" : "OFF"}`, `strategy=${this.settings.strategy}`, `teamSize=${this.settings.teamSize}`, - `solver=${this.settings.model}`, - `critic=${this.settings.criticModel}`, - `researcher=${this.settings.researcherModel}`, - `synth=${this.settings.synthModel}`, + `solver=${describeModel(this.settings.model)}`, + `critic=${describeModel(this.settings.criticModel)}`, + `researcher=${describeModel(this.settings.researcherModel)}`, + "synth=inherit-active-chat-model", `timeout=${this.settings.timeoutSeconds}s`, `minParticipating=${this.settings.minParticipatingAgents}`, `minAnswering=${this.settings.minAnsweringAgents}`, @@ -89,7 +94,8 @@ export class BraintrustOrchestrator { const workers = session.workers.map((worker) => `${worker.id}:${worker.status}`).join(", "); return `${session.id} ยท ${session.status} ยท ${session.startedAt} ยท ${workers}`; }); - return ["Braintrust sessions:", ...lines].join("\n"); + return ["Braintrust sessions:", ...lines].join(" +"); } executeAction(action: OrchestratorAction, renderUnavailable: () => string): string { @@ -110,24 +116,17 @@ export class BraintrustOrchestrator { return this.statusLine(); } - async runPrompt(prompt: string, runCandidate: CandidateRunner) { + async runPrompt(prompt: string, runCandidate: CandidateRunner, activeChatModel?: string) { const session: BraintrustSessionState = { id: buildSessionId(), prompt, strategy: this.settings.strategy, status: "running", startedAt: nowIso(), - workers: Array.from({ length: this.settings.teamSize }, (_, index) => ({ - id: `agent-${index + 1}`, - role: index === 0 ? "solver" : index === 1 ? "critic" : index === 2 ? "researcher" : "synthesizer", - model: - index === 0 - ? this.settings.model - : index === 1 - ? this.settings.criticModel - : index === 2 - ? this.settings.researcherModel - : this.settings.synthModel, + workers: buildWorkerPlan({ prompt, settings: this.settings, activeChatModel }).map((worker) => ({ + id: worker.id, + role: worker.role, + model: worker.model, status: "pending", updatedAt: nowIso(), })), @@ -138,7 +137,7 @@ export class BraintrustOrchestrator { this.persist(); const bridge = await runRuntimeBridge( - { prompt, settings: this.settings }, + { prompt, settings: this.settings, activeChatModel }, runCandidate, { onCandidateStart: ({ id }) => { diff --git a/extensions/braintrust/src/policy.test.ts b/extensions/braintrust/src/policy.test.ts index a1c228b..ba09f84 100644 --- a/extensions/braintrust/src/policy.test.ts +++ b/extensions/braintrust/src/policy.test.ts @@ -4,10 +4,10 @@ import { buildUnavailableNotice, evaluateQuorum } from "./policy.js"; describe("evaluateQuorum", () => { it("passes when at least 2 agents answer", () => { const result = evaluateQuorum([ - { id: "a", model: "model-a", status: "ok" }, - { id: "b", model: "model-b", status: "ok" }, - { id: "c", model: "model-c", status: "timeout" }, - { id: "d", model: "model-d", status: "refusal" }, + { id: "a", model: "", status: "ok" }, + { id: "b", model: "", status: "ok" }, + { id: "c", model: "", status: "timeout" }, + { id: "d", model: "", status: "refusal" }, ]); expect(result.meetsQuorum).toBe(true); expect(result.participating).toBe(3); @@ -16,9 +16,9 @@ describe("evaluateQuorum", () => { it("fails when only 1 agent participated", () => { const result = evaluateQuorum([ - { id: "a", model: "model-a", status: "ok" }, - { id: "b", model: "model-b", status: "timeout" }, - { id: "c", model: "model-c", status: "error" }, + { id: "a", model: "", status: "ok" }, + { id: "b", model: "", status: "timeout" }, + { id: "c", model: "", status: "error" }, ]); expect(result.meetsQuorum).toBe(false); expect(result.reason).toContain("participated"); @@ -27,9 +27,9 @@ describe("evaluateQuorum", () => { it("fails when participation is enough but answering is too low", () => { const result = evaluateQuorum([ - { id: "a", model: "model-a", status: "refusal" }, - { id: "b", model: "model-b", status: "ok" }, - { id: "c", model: "model-c", status: "refusal" }, + { id: "a", model: "", status: "refusal" }, + { id: "b", model: "", status: "ok" }, + { id: "c", model: "", status: "refusal" }, ]); expect(result.meetsQuorum).toBe(false); expect(result.reason).toContain("usable answers"); diff --git a/extensions/braintrust/src/runtime-bridge.integration.test.ts b/extensions/braintrust/src/runtime-bridge.integration.test.ts index e907bc9..0ff2b5c 100644 --- a/extensions/braintrust/src/runtime-bridge.integration.test.ts +++ b/extensions/braintrust/src/runtime-bridge.integration.test.ts @@ -24,7 +24,8 @@ describe("runtime bridge integration flow", () => { await new Promise((resolve) => setTimeout(resolve, 1500)); return { text: "late answer" }; } - if (model === "runtime-chat-model") { + if (role === "synthesizer") { + expect(model).toBe("runtime-chat-model"); expect(prompt).toContain("agent-1 (solver, ok)"); expect(prompt).toContain("agent-2 (critic, refusal)"); expect(prompt).toContain("agent-3 (researcher, timeout)"); @@ -36,6 +37,7 @@ describe("runtime bridge integration flow", () => { }), }; } + expect(model).toBe("runtime-chat-model"); return { text: "solid plan" }; }, ); diff --git a/extensions/braintrust/src/runtime-bridge.js b/extensions/braintrust/src/runtime-bridge.js index 703b823..6dcd9ad 100644 --- a/extensions/braintrust/src/runtime-bridge.js +++ b/extensions/braintrust/src/runtime-bridge.js @@ -1,17 +1,6 @@ import { buildUnavailableNotice, evaluateQuorum } from "./policy.js"; import { synthesizeDeterministic } from "./synth.js"; -function roleFor(index) { - if (index === 1) return "critic"; - if (index === 2) return "researcher"; - if (index === 3) return "synthesizer"; - return "solver"; -} -function modelFor(index, settings) { - if (index === 3) return settings.synthModel; - if (index === 1) return settings.criticModel; - if (index === 2) return settings.researcherModel; - return settings.model; -} +import { buildWorkerPlan } from "./worker-plan.js"; function classifyError(error) { const msg = String(error ?? "").toLowerCase(); if (msg.includes("timeout")) return "timeout"; @@ -20,23 +9,12 @@ function classifyError(error) { function isLikelyRefusal(text) { const t = text.trim().toLowerCase(); if (!t) return false; - return [ - "i can't", - "i cannot", - "can't help with", - "cannot help with", - "cannot comply", - "i'm sorry", - "i am sorry", - "as an ai", - "unable to assist", - "i must refuse" - ].some((needle) => t.includes(needle)); + return ["i can't", "i cannot", "can't help with", "cannot help with", "cannot comply", "i'm sorry", "i am sorry", "as an ai", "unable to assist", "i must refuse"].some((needle) => t.includes(needle)); } async function withTimeout(promise, timeoutSeconds) { let timeoutHandle; const timeoutPromise = new Promise((_, reject) => { - timeoutHandle = setTimeout(() => reject(new Error(`timeout after ${timeoutSeconds}s`)), timeoutSeconds * 1000); + timeoutHandle = setTimeout(() => reject(new Error(`timeout after ${timeoutSeconds}s`)), timeoutSeconds * 1e3); }); try { return await Promise.race([promise, timeoutPromise]); @@ -45,71 +23,85 @@ async function withTimeout(promise, timeoutSeconds) { } } export async function runRuntimeBridge(input, runCandidate, hooks = {}) { - const tasks = Array.from({ length: input.settings.teamSize }, async (_, i) => { - const id = `agent-${i + 1}`; - const role = roleFor(i); - const model = modelFor(i, input.settings); + const initialPlan = buildWorkerPlan({ + prompt: input.prompt, + settings: input.settings, + activeChatModel: input.activeChatModel + }); + const synthesisWorker = initialPlan.find((worker) => worker.role === "synthesizer"); + const primaryWorkers = initialPlan.filter((worker) => worker.role !== "synthesizer"); + const primaryCandidates = await Promise.all(primaryWorkers.map(async (worker) => { const started = Date.now(); - hooks.onCandidateStart?.({ id, role, model }); + hooks.onCandidateStart?.({ id: worker.id, role: worker.role, model: worker.model }); try { const out = await withTimeout(runCandidate({ - candidateId: id, - role, - model, - prompt: i === 3 ? `${input.prompt}\n\nRespond ONLY with valid JSON in this exact format (no extra text, no markdown):\n{\n "final": "the single best synthesized answer",\n "reasoning": "brief explanation of how you combined the inputs",\n "sources": ["list of agent ids used"]\n}` : input.prompt, + candidateId: worker.id, + role: worker.role, + model: worker.model, + prompt: worker.prompt, timeoutSeconds: input.settings.timeoutSeconds }), input.settings.timeoutSeconds); const latencyMs = out.latencyMs ?? Date.now() - started; const refusal = out.refusal ?? isLikelyRefusal(out.text); - const candidate = { - id, - model, - status: refusal ? "refusal" : "ok", - text: out.text, - latencyMs - }; + const candidate = { id: worker.id, model: worker.model, status: refusal ? "refusal" : "ok", text: out.text, latencyMs }; hooks.onCandidateComplete?.(candidate); return candidate; } catch (err) { - const candidate = { - id, - model, - status: classifyError(err), - latencyMs: Date.now() - started - }; + const candidate = { id: worker.id, model: worker.model, status: classifyError(err), latencyMs: Date.now() - started }; hooks.onCandidateComplete?.(candidate); return candidate; } - }); - const candidates = await Promise.all(tasks); + })); + const candidates = [...primaryCandidates]; + if (synthesisWorker) { + const synthPlan = buildWorkerPlan({ + prompt: input.prompt, + settings: input.settings, + activeChatModel: input.activeChatModel, + completedWorkers: primaryCandidates.map((candidate) => ({ + id: candidate.id, + role: primaryWorkers.find((worker) => worker.id === candidate.id)?.role ?? "solver", + status: candidate.status, + text: candidate.text + })) + }); + const synthWorker = synthPlan.find((worker) => worker.role === "synthesizer"); + const started = Date.now(); + hooks.onCandidateStart?.({ id: synthWorker.id, role: synthWorker.role, model: synthWorker.model }); + try { + const out = await withTimeout(runCandidate({ + candidateId: synthWorker.id, + role: synthWorker.role, + model: synthWorker.model, + prompt: `${synthWorker.prompt} + +Respond ONLY with valid JSON in this exact format (no extra text, no markdown): +{ + "final": "the single best synthesized answer", + "reasoning": "brief explanation of how you combined the inputs", + "sources": ["list of agent ids used"] +}`, + timeoutSeconds: input.settings.timeoutSeconds + }), input.settings.timeoutSeconds); + const latencyMs = out.latencyMs ?? Date.now() - started; + const refusal = out.refusal ?? isLikelyRefusal(out.text); + const candidate = { id: synthWorker.id, model: synthWorker.model, status: refusal ? "refusal" : "ok", text: out.text, latencyMs }; + hooks.onCandidateComplete?.(candidate); + candidates.push(candidate); + } catch (err) { + const candidate = { id: synthWorker.id, model: synthWorker.model, status: classifyError(err), latencyMs: Date.now() - started }; + hooks.onCandidateComplete?.(candidate); + candidates.push(candidate); + } + } const quorum = evaluateQuorum(candidates, { minParticipatingAgents: input.settings.minParticipatingAgents, minAnsweringAgents: input.settings.minAnsweringAgents }); if (!quorum.meetsQuorum) { - return { - final: buildUnavailableNotice(quorum), - candidates, - unavailable: true, - reason: quorum.reason - }; + return { final: buildUnavailableNotice(quorum), candidates, unavailable: true, reason: quorum.reason }; } - const synthesis = synthesizeDeterministic({ - prompt: input.prompt, - candidates - }); - const final = synthesis.final ?? buildUnavailableNotice({ - participating: 0, - answering: 0, - refused: 0, - failed: candidates.length, - meetsQuorum: false, - reason: "no usable candidate output" - }); - return { - final, - candidates, - unavailable: false, - synthesis - }; + const synthesis = synthesizeDeterministic({ prompt: input.prompt, candidates }); + const final = synthesis.final ?? buildUnavailableNotice({ participating: 0, answering: 0, refused: 0, failed: candidates.length, meetsQuorum: false, reason: "no usable candidate output" }); + return { final, candidates, unavailable: false, synthesis }; } diff --git a/extensions/braintrust/src/runtime-bridge.test.ts b/extensions/braintrust/src/runtime-bridge.test.ts index 3d58ac9..99f7830 100644 --- a/extensions/braintrust/src/runtime-bridge.test.ts +++ b/extensions/braintrust/src/runtime-bridge.test.ts @@ -78,35 +78,24 @@ describe("runRuntimeBridge", () => { expect(result.unavailable).toBe(true); }); - it("uses four distinct models when teamSize=4", async () => { + it("inherits the active chat model for every unset role", async () => { const settings = { ...DEFAULT_SETTINGS, enabled: true, teamSize: 4, - model: "gemini-3-flash-preview", - criticModel: "openai-codex/gpt-5.3-codex", - researcherModel: "grok-4-1-fast-reasoning", - synthModel: "gemini-3.1-pro-preview", minParticipatingAgents: 2, minAnsweringAgents: 2, }; const seen: string[] = []; const result = await runRuntimeBridge( - { prompt: "hello", settings }, + { prompt: "hello", settings, activeChatModel: "runtime-chat-model" }, async ({ model }) => { seen.push(model); - return { text: `ok-${model}`, refusal: false }; + return { text: `ok-${model || "empty"}`, refusal: false }; }, ); expect(result.unavailable).toBe(false); - expect(new Set(seen)).toEqual( - new Set([ - "gemini-3-flash-preview", - "openai-codex/gpt-5.3-codex", - "grok-4-1-fast-reasoning", - "gemini-3.1-pro-preview", - ]), - ); + expect(new Set(seen)).toEqual(new Set(["runtime-chat-model"])); }); it("reports candidate lifecycle hooks", async () => { diff --git a/extensions/braintrust/src/runtime-bridge.ts b/extensions/braintrust/src/runtime-bridge.ts index a4b2657..025995b 100644 --- a/extensions/braintrust/src/runtime-bridge.ts +++ b/extensions/braintrust/src/runtime-bridge.ts @@ -1,6 +1,7 @@ import { buildUnavailableNotice, evaluateQuorum, type Candidate, type CandidateStatus } from "./policy.js"; import type { BraintrustSettings } from "./settings.js"; import { synthesizeDeterministic, type SynthesisOutput } from "./synth.js"; +import { buildWorkerPlan } from "./worker-plan.js"; export type CandidateRunnerInput = { role: "solver" | "critic" | "researcher" | "synthesizer"; @@ -21,6 +22,7 @@ export type CandidateRunner = (input: CandidateRunnerInput) => Promise void; }; -function roleFor(index: number): CandidateRunnerInput["role"] { - if (index === 1) return "critic"; - if (index === 2) return "researcher"; - if (index === 3) return "synthesizer"; - return "solver"; -} - -function modelFor(index: number, settings: BraintrustSettings): string { - if (index === 3) return settings.synthModel; - if (index === 1) return settings.criticModel; - if (index === 2) return settings.researcherModel; - return settings.model; -} - function classifyError(error: unknown): CandidateStatus { const msg = String(error ?? "").toLowerCase(); if (msg.includes("timeout")) return "timeout"; @@ -91,22 +79,84 @@ export async function runRuntimeBridge( runCandidate: CandidateRunner, hooks: RuntimeBridgeHooks = {}, ): Promise { - const tasks = Array.from({ length: input.settings.teamSize }, async (_, i) => { - const id = `agent-${i + 1}`; - const role = roleFor(i); - const model = modelFor(i, input.settings); + const initialPlan = buildWorkerPlan({ + prompt: input.prompt, + settings: input.settings, + activeChatModel: input.activeChatModel, + }); + const synthesisWorker = initialPlan.find((worker) => worker.role === "synthesizer"); + const primaryWorkers = initialPlan.filter((worker) => worker.role !== "synthesizer"); + + const primaryCandidates = await Promise.all( + primaryWorkers.map(async (worker) => { + const started = Date.now(); + hooks.onCandidateStart?.({ id: worker.id, role: worker.role, model: worker.model }); + try { + const out = await withTimeout( + runCandidate({ + candidateId: worker.id, + role: worker.role, + model: worker.model, + prompt: worker.prompt, + timeoutSeconds: input.settings.timeoutSeconds, + }), + input.settings.timeoutSeconds, + ); + const latencyMs = out.latencyMs ?? Date.now() - started; + const refusal = out.refusal ?? isLikelyRefusal(out.text); + const candidate = { + id: worker.id, + model: worker.model, + status: refusal ? "refusal" : "ok", + text: out.text, + latencyMs, + } as Candidate; + hooks.onCandidateComplete?.(candidate); + return candidate; + } catch (err) { + const candidate = { + id: worker.id, + model: worker.model, + status: classifyError(err), + latencyMs: Date.now() - started, + } as Candidate; + hooks.onCandidateComplete?.(candidate); + return candidate; + } + }), + ); + + const candidates = [...primaryCandidates]; + + if (synthesisWorker) { + const synthPlan = buildWorkerPlan({ + prompt: input.prompt, + settings: input.settings, + activeChatModel: input.activeChatModel, + completedWorkers: primaryCandidates.map((candidate) => ({ + id: candidate.id, + role: primaryWorkers.find((worker) => worker.id === candidate.id)?.role ?? "solver", + status: candidate.status, + text: candidate.text, + })), + }); + const synthWorker = synthPlan.find((worker) => worker.role === "synthesizer")!; const started = Date.now(); - hooks.onCandidateStart?.({ id, role, model }); + hooks.onCandidateStart?.({ id: synthWorker.id, role: synthWorker.role, model: synthWorker.model }); try { const out = await withTimeout( runCandidate({ - candidateId: id, - role, - model, - prompt: - i === 3 - ? `${input.prompt}\n\nRespond ONLY with valid JSON in this exact format (no extra text, no markdown):\n{\n "final": "the single best synthesized answer",\n "reasoning": "brief explanation of how you combined the inputs",\n "sources": ["list of agent ids used"]\n}` - : input.prompt, + candidateId: synthWorker.id, + role: synthWorker.role, + model: synthWorker.model, + prompt: `${synthWorker.prompt} + +Respond ONLY with valid JSON in this exact format (no extra text, no markdown): +{ + "final": "the single best synthesized answer", + "reasoning": "brief explanation of how you combined the inputs", + "sources": ["list of agent ids used"] +}`, timeoutSeconds: input.settings.timeoutSeconds, }), input.settings.timeoutSeconds, @@ -114,27 +164,26 @@ export async function runRuntimeBridge( const latencyMs = out.latencyMs ?? Date.now() - started; const refusal = out.refusal ?? isLikelyRefusal(out.text); const candidate = { - id, - model, + id: synthWorker.id, + model: synthWorker.model, status: refusal ? "refusal" : "ok", text: out.text, latencyMs, } as Candidate; hooks.onCandidateComplete?.(candidate); - return candidate; + candidates.push(candidate); } catch (err) { const candidate = { - id, - model, + id: synthWorker.id, + model: synthWorker.model, status: classifyError(err), latencyMs: Date.now() - started, } as Candidate; hooks.onCandidateComplete?.(candidate); - return candidate; + candidates.push(candidate); } - }); + } - const candidates = await Promise.all(tasks); const quorum = evaluateQuorum(candidates, { minParticipatingAgents: input.settings.minParticipatingAgents, minAnsweringAgents: input.settings.minAnsweringAgents, diff --git a/extensions/braintrust/src/settings.js b/extensions/braintrust/src/settings.js index 5c513ae..d3aae40 100644 --- a/extensions/braintrust/src/settings.js +++ b/extensions/braintrust/src/settings.js @@ -3,10 +3,10 @@ export const DEFAULT_SETTINGS = { enabled: false, teamSize: 4, strategy: "panel", - model: "gemini-3-flash-preview", - criticModel: "openai-codex/gpt-5.3-codex", - researcherModel: "grok-4-1-fast-reasoning", - synthModel: "gemini-3.1-pro-preview", + model: "", + criticModel: "", + researcherModel: "", + synthModel: "", timeoutSeconds: 90, minParticipatingAgents: DEFAULT_QUORUM.minParticipatingAgents, minAnsweringAgents: DEFAULT_QUORUM.minAnsweringAgents @@ -16,6 +16,9 @@ function clampInt(value, min, max, fallback) { if (!Number.isFinite(n)) return fallback; return Math.max(min, Math.min(max, Math.trunc(n))); } +function readModelSetting(value) { + return typeof value === "string" ? value.trim() : ""; +} export function readSettings(raw) { const p = raw ?? {}; const strategy = p.strategy === "independent" || p.strategy === "debate" || p.strategy === "panel" ? p.strategy : DEFAULT_SETTINGS.strategy; @@ -26,10 +29,10 @@ export function readSettings(raw) { enabled: Boolean(p.enabled ?? DEFAULT_SETTINGS.enabled), teamSize, strategy, - model: String(p.model ?? DEFAULT_SETTINGS.model), - criticModel: String(p.criticModel ?? DEFAULT_SETTINGS.criticModel), - researcherModel: String(p.researcherModel ?? DEFAULT_SETTINGS.researcherModel), - synthModel: String(p.synthModel ?? DEFAULT_SETTINGS.synthModel), + model: readModelSetting(p.model), + criticModel: readModelSetting(p.criticModel), + researcherModel: readModelSetting(p.researcherModel), + synthModel: readModelSetting(p.synthModel), timeoutSeconds: clampInt(p.timeoutSeconds, 10, 300, DEFAULT_SETTINGS.timeoutSeconds), minParticipatingAgents, minAnsweringAgents diff --git a/extensions/braintrust/src/settings.test.ts b/extensions/braintrust/src/settings.test.ts index 655e6e2..25ec35c 100644 --- a/extensions/braintrust/src/settings.test.ts +++ b/extensions/braintrust/src/settings.test.ts @@ -14,10 +14,16 @@ describe("readSettings", () => { expect(got.minAnsweringAgents).toBe(2); }); - it("accepts strategy overrides", () => { + it("accepts strategy overrides and keeps models empty by default", () => { const got = readSettings({ strategy: "debate", enabled: true }); expect(got.strategy).toBe("debate"); expect(got.enabled).toBe(true); - expect(got.researcherModel).toBe("model-c"); + expect(got.researcherModel).toBe(""); + }); + + it("trims configured model overrides", () => { + const got = readSettings({ model: " solver-override ", criticModel: " critic-override " }); + expect(got.model).toBe("solver-override"); + expect(got.criticModel).toBe("critic-override"); }); }); diff --git a/extensions/braintrust/src/settings.ts b/extensions/braintrust/src/settings.ts index 8de9a96..c8f5036 100644 --- a/extensions/braintrust/src/settings.ts +++ b/extensions/braintrust/src/settings.ts @@ -10,8 +10,8 @@ export type BraintrustSettings = { criticModel: string; researcherModel: string; /** - * Deprecated: synthesis now runs on the active chat model instead of a dedicated synth model. - * Kept for backward-compatible config parsing/status output only. + * Deprecated: synthesis now runs on the active chat model. Kept only for + * backward-compatible config parsing and persisted-state hydration. */ synthModel: string; timeoutSeconds: number; @@ -23,10 +23,10 @@ export const DEFAULT_SETTINGS: BraintrustSettings = { enabled: false, teamSize: 4, strategy: "panel", - model: "model-a", - criticModel: "model-b", - researcherModel: "model-c", - synthModel: "legacy-synth-model", + model: "", + criticModel: "", + researcherModel: "", + synthModel: "", timeoutSeconds: 90, minParticipatingAgents: DEFAULT_QUORUM.minParticipatingAgents, minAnsweringAgents: DEFAULT_QUORUM.minAnsweringAgents, @@ -38,6 +38,10 @@ function clampInt(value: unknown, min: number, max: number, fallback: number): n return Math.max(min, Math.min(max, Math.trunc(n))); } +function readModelSetting(value: unknown): string { + return typeof value === "string" ? value.trim() : ""; +} + export function readSettings(raw: Record | undefined): BraintrustSettings { const p = raw ?? {}; const strategy: BraintrustStrategy = @@ -63,10 +67,10 @@ export function readSettings(raw: Record | undefined): Braintru enabled: Boolean(p.enabled ?? DEFAULT_SETTINGS.enabled), teamSize, strategy, - model: String(p.model ?? DEFAULT_SETTINGS.model), - criticModel: String(p.criticModel ?? DEFAULT_SETTINGS.criticModel), - researcherModel: String(p.researcherModel ?? DEFAULT_SETTINGS.researcherModel), - synthModel: String(p.synthModel ?? DEFAULT_SETTINGS.synthModel), + model: readModelSetting(p.model), + criticModel: readModelSetting(p.criticModel), + researcherModel: readModelSetting(p.researcherModel), + synthModel: readModelSetting(p.synthModel), timeoutSeconds: clampInt(p.timeoutSeconds, 10, 300, DEFAULT_SETTINGS.timeoutSeconds), minParticipatingAgents, minAnsweringAgents, diff --git a/extensions/braintrust/src/synth.js b/extensions/braintrust/src/synth.js index 29cde35..a4670c8 100644 --- a/extensions/braintrust/src/synth.js +++ b/extensions/braintrust/src/synth.js @@ -1,3 +1,20 @@ +function parseStructuredSynthesis(candidate) { + if (candidate.status !== "ok" || !candidate.text?.trim()) return void 0; + try { + const parsed = JSON.parse(candidate.text.trim()); + if (typeof parsed.final !== "string" || !parsed.final.trim()) return void 0; + return { + final: parsed.final.trim(), + winnerId: candidate.id, + reasoning: typeof parsed.reasoning === "string" ? parsed.reasoning.trim() : void 0, + sources: Array.isArray(parsed.sources) + ? parsed.sources.filter((value) => typeof value === "string" && value.trim().length > 0) + : void 0 + }; + } catch { + return void 0; + } +} export function synthesizeDeterministic(input) { const ok = input.candidates.filter((c) => c.status === "ok" && c.text?.trim()); if (ok.length === 0) { @@ -5,18 +22,10 @@ export function synthesizeDeterministic(input) { final: "Braintrust temporarily unavailable (no usable candidate output)." }; } - const synthAgent = input.candidates.find((c) => c.id === "agent-4"); - if (synthAgent?.status === "ok" && synthAgent.text) { - try { - const parsed = JSON.parse(synthAgent.text.trim()); - if (parsed.final && typeof parsed.final === "string") { - return { - final: parsed.final, - winnerId: "agent-4" - }; - } - } catch { - } + const synthAgent = input.candidates.find((c) => c.role === "synthesizer") ?? input.candidates.find((c) => c.id === "agent-4"); + if (synthAgent) { + const parsed = parseStructuredSynthesis(synthAgent); + if (parsed) return parsed; } ok.sort((a, b) => { const la = (a.text ?? "").length; diff --git a/extensions/braintrust/src/synth.test.ts b/extensions/braintrust/src/synth.test.ts index c421eda..28fcd86 100644 --- a/extensions/braintrust/src/synth.test.ts +++ b/extensions/braintrust/src/synth.test.ts @@ -6,8 +6,8 @@ describe("synthesizeDeterministic", () => { const out = synthesizeDeterministic({ prompt: "hello", candidates: [ - { id: "a", model: "x", status: "ok", text: "this is longer" }, - { id: "b", model: "y", status: "ok", text: "short" }, + { id: "a", model: "", status: "ok", text: "this is longer" }, + { id: "b", model: "", status: "ok", text: "short" }, ], }); expect(out.final).toBe("short"); @@ -17,7 +17,7 @@ describe("synthesizeDeterministic", () => { it("returns unavailable when no usable outputs", () => { const out = synthesizeDeterministic({ prompt: "hello", - candidates: [{ id: "a", model: "x", status: "refusal", text: "cannot" }], + candidates: [{ id: "a", model: "", status: "refusal", text: "cannot" }], }); expect(out.final.toLowerCase()).toContain("temporarily unavailable"); }); diff --git a/extensions/braintrust/src/worker-plan.js b/extensions/braintrust/src/worker-plan.js new file mode 100644 index 0000000..73aebb1 --- /dev/null +++ b/extensions/braintrust/src/worker-plan.js @@ -0,0 +1,62 @@ +function roleInstruction(role, strategy) { + if (role === "solver") { + if (strategy === "debate") { + return "You are the Braintrust solver. Produce the strongest direct answer you can, but note one assumption that could be challenged by another worker."; + } + if (strategy === "independent") { + return "You are the Braintrust solver. Solve the user request independently and concisely, without referencing other workers."; + } + return "You are the Braintrust solver. Produce the strongest direct answer for the user request, optimized for usefulness and clarity."; + } + if (role === "critic") { + if (strategy === "debate") { + return "You are the Braintrust critic. Stress-test likely solver claims, identify risks or weak assumptions, and propose corrections."; + } + if (strategy === "independent") { + return "You are the Braintrust critic. Independently answer the user request while emphasizing failure modes, risks, and caveats."; + } + return "You are the Braintrust critic. Review the problem from a skeptical angle, focusing on risks, edge cases, and missing constraints."; + } + if (role === "researcher") { + if (strategy === "debate") { + return "You are the Braintrust researcher. Add factual context, tradeoffs, and external-style evidence that would help settle a disagreement."; + } + if (strategy === "independent") { + return "You are the Braintrust researcher. Independently answer the user request with concrete facts, references, or implementation details."; + } + return "You are the Braintrust researcher. Contribute supporting facts, implementation details, or concrete options relevant to the request."; + } + return "You are the Braintrust synthesizer. Combine the worker outputs into the single best final answer. Respond ONLY with valid JSON: {\"final\": string, \"reasoning\": string, \"sources\": string[] }."; +} +function configuredModelFor(role, settings) { + if (role === "critic") return settings.criticModel; + if (role === "researcher") return settings.researcherModel; + if (role === "synthesizer") return settings.synthModel; + return settings.model; +} +function modelFor(role, settings, activeChatModel) { + return configuredModelFor(role, settings).trim() || activeChatModel?.trim() || ""; +} +const ROLE_ORDER = ["solver", "critic", "researcher", "synthesizer"]; +export function buildWorkerPlan(input) { + const { prompt, settings, activeChatModel } = input; + const roles = ROLE_ORDER.slice(0, Math.max(1, Math.min(settings.teamSize, ROLE_ORDER.length))); + return roles.map((role, index) => { + const id = `agent-${index + 1}`; + const instruction = roleInstruction(role, settings.strategy); + let workerPrompt = prompt; + if (role === "synthesizer") { + const prior = (input.completedWorkers ?? []) + .filter((worker) => worker.role !== "synthesizer") + .map((worker) => `${worker.id} (${worker.role}, ${worker.status ?? "unknown"}):\n${worker.text?.trim() || "[no text]"}`) + .join("\n\n"); + workerPrompt = [prompt, "", "Worker outputs to synthesize:", prior || "[no completed worker outputs available]"].join("\n"); + } + return { + id, + role, + model: modelFor(role, settings, activeChatModel), + prompt: `${instruction}\n\nUser request:\n${workerPrompt}` + }; + }); +} diff --git a/extensions/braintrust/src/worker-plan.test.ts b/extensions/braintrust/src/worker-plan.test.ts index ab8ffb3..0d4947e 100644 --- a/extensions/braintrust/src/worker-plan.test.ts +++ b/extensions/braintrust/src/worker-plan.test.ts @@ -3,7 +3,7 @@ import { DEFAULT_SETTINGS } from "./settings.js"; import { buildWorkerPlan } from "./worker-plan.js"; describe("buildWorkerPlan", () => { - it("builds role-specific workers and uses active chat model for synthesis", () => { + it("builds role-specific workers and uses active chat model when role settings are unset", () => { const plan = buildWorkerPlan({ prompt: "Design a rollout plan.", settings: { ...DEFAULT_SETTINGS, teamSize: 4, strategy: "debate" }, @@ -11,9 +11,9 @@ describe("buildWorkerPlan", () => { }); expect(plan.map((worker) => ({ id: worker.id, role: worker.role, model: worker.model }))).toEqual([ - { id: "agent-1", role: "solver", model: DEFAULT_SETTINGS.model }, - { id: "agent-2", role: "critic", model: DEFAULT_SETTINGS.criticModel }, - { id: "agent-3", role: "researcher", model: DEFAULT_SETTINGS.researcherModel }, + { id: "agent-1", role: "solver", model: "runtime-chat-model" }, + { id: "agent-2", role: "critic", model: "runtime-chat-model" }, + { id: "agent-3", role: "researcher", model: "runtime-chat-model" }, { id: "agent-4", role: "synthesizer", model: "runtime-chat-model" }, ]); @@ -23,6 +23,28 @@ describe("buildWorkerPlan", () => { expect(plan[3].prompt).toContain("Respond ONLY with valid JSON"); }); + it("preserves explicit per-role overrides when configured", () => { + const plan = buildWorkerPlan({ + prompt: "Design a rollout plan.", + settings: { + ...DEFAULT_SETTINGS, + teamSize: 4, + model: "solver-override", + criticModel: "critic-override", + researcherModel: "researcher-override", + synthModel: "", + }, + activeChatModel: "runtime-chat-model", + }); + + expect(plan.map((worker) => worker.model)).toEqual([ + "solver-override", + "critic-override", + "researcher-override", + "runtime-chat-model", + ]); + }); + it("injects completed worker outputs into the synthesizer prompt", () => { const plan = buildWorkerPlan({ prompt: "What should we ship next?", diff --git a/extensions/braintrust/src/worker-plan.ts b/extensions/braintrust/src/worker-plan.ts index 275cc87..27e30cc 100644 --- a/extensions/braintrust/src/worker-plan.ts +++ b/extensions/braintrust/src/worker-plan.ts @@ -40,16 +40,20 @@ function roleInstruction(role: WorkerRole, strategy: BraintrustStrategy): string return "You are the Braintrust researcher. Contribute supporting facts, implementation details, or concrete options relevant to the request."; } - return "You are the Braintrust synthesizer. Combine the worker outputs into the single best final answer. Respond ONLY with valid JSON: {\"final\": string, \"reasoning\": string, \"sources\": string[] }."; + return "You are the Braintrust synthesizer. Combine the worker outputs into the single best final answer. Respond ONLY with valid JSON: {"final": string, "reasoning": string, "sources": string[] }."; } -function modelFor(role: WorkerRole, settings: BraintrustSettings, activeChatModel?: string): string { +function configuredModelFor(role: WorkerRole, settings: BraintrustSettings): string { if (role === "critic") return settings.criticModel; if (role === "researcher") return settings.researcherModel; - if (role === "synthesizer") return activeChatModel?.trim() || settings.model; + if (role === "synthesizer") return settings.synthModel; return settings.model; } +function modelFor(role: WorkerRole, settings: BraintrustSettings, activeChatModel?: string): string { + return configuredModelFor(role, settings).trim() || activeChatModel?.trim() || ""; +} + const ROLE_ORDER: WorkerRole[] = ["solver", "critic", "researcher", "synthesizer"]; export function buildWorkerPlan(input: { @@ -69,22 +73,29 @@ export function buildWorkerPlan(input: { if (role === "synthesizer") { const prior = (input.completedWorkers ?? []) .filter((worker) => worker.role !== "synthesizer") - .map((worker) => `${worker.id} (${worker.role}, ${worker.status ?? "unknown"}):\n${worker.text?.trim() || "[no text]"}`) - .join("\n\n"); + .map((worker) => `${worker.id} (${worker.role}, ${worker.status ?? "unknown"}): +${worker.text?.trim() || "[no text]"}`) + .join(" + +"); workerPrompt = [ prompt, "", "Worker outputs to synthesize:", prior || "[no completed worker outputs available]", - ].join("\n"); + ].join(" +"); } return { id, role, model: modelFor(role, settings, activeChatModel), - prompt: `${instruction}\n\nUser request:\n${workerPrompt}`, + prompt: `${instruction} + +User request: +${workerPrompt}`, }; }); }