From c1479624341c80cd756edaf26704e0128456cb09 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 25 Jan 2026 00:04:37 +0000 Subject: [PATCH 001/214] fix: normalize googlechat targets --- CHANGELOG.md | 1 + extensions/googlechat/src/targets.ts | 4 ++-- src/channels/plugins/group-mentions.ts | 11 +++++++++++ src/plugin-sdk/index.ts | 1 + 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 559afe74b..2444e1447 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ Docs: https://docs.clawd.bot - macOS: default direct-transport `ws://` URLs to port 18789; document `gateway.remote.transport`. (#1603) Thanks @ngutman. - Voice Call: return stream TwiML for outbound conversation calls on initial Twilio webhook. (#1634) - Google Chat: tighten email allowlist matching, typing cleanup, media caps, and onboarding/docs/tests. (#1635) Thanks @iHildy. +- Google Chat: normalize space targets without double `spaces/` prefix. ## 2026.1.23-1 diff --git a/extensions/googlechat/src/targets.ts b/extensions/googlechat/src/targets.ts index 58df49484..a294bf128 100644 --- a/extensions/googlechat/src/targets.ts +++ b/extensions/googlechat/src/targets.ts @@ -6,8 +6,8 @@ export function normalizeGoogleChatTarget(raw?: string | null): string | undefin if (!trimmed) return undefined; const withoutPrefix = trimmed.replace(/^(googlechat|google-chat|gchat):/i, ""); const normalized = withoutPrefix - .replace(/^user:/i, "users/") - .replace(/^space:/i, "spaces/"); + .replace(/^user:(users\/)?/i, "users/") + .replace(/^space:(spaces\/)?/i, "spaces/"); if (isGoogleChatUserTarget(normalized)) { const suffix = normalized.slice("users/".length); return suffix.includes("@") ? `users/${suffix.toLowerCase()}` : normalized; diff --git a/src/channels/plugins/group-mentions.ts b/src/channels/plugins/group-mentions.ts index b15ce1b07..9d254e57a 100644 --- a/src/channels/plugins/group-mentions.ts +++ b/src/channels/plugins/group-mentions.ts @@ -164,6 +164,17 @@ export function resolveGoogleChatGroupRequireMention(params: GroupMentionParams) }); } +export function resolveGoogleChatGroupToolPolicy( + params: GroupMentionParams, +): GroupToolPolicyConfig | undefined { + return resolveChannelGroupToolsPolicy({ + cfg: params.cfg, + channel: "googlechat", + groupId: params.groupId, + accountId: params.accountId, + }); +} + export function resolveSlackGroupRequireMention(params: GroupMentionParams): boolean { const account = resolveSlackAccount({ cfg: params.cfg, diff --git a/src/plugin-sdk/index.ts b/src/plugin-sdk/index.ts index 8e08dad25..f40d99d82 100644 --- a/src/plugin-sdk/index.ts +++ b/src/plugin-sdk/index.ts @@ -154,6 +154,7 @@ export { resolveWhatsAppGroupRequireMention, resolveBlueBubblesGroupToolPolicy, resolveDiscordGroupToolPolicy, + resolveGoogleChatGroupToolPolicy, resolveIMessageGroupToolPolicy, resolveSlackGroupToolPolicy, resolveTelegramGroupToolPolicy, From 85b27fe5fe58704cc3c1f28e33fea42043862034 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 25 Jan 2026 00:05:33 +0000 Subject: [PATCH 002/214] docs: fix ollama links --- docs/providers/ollama.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/providers/ollama.md b/docs/providers/ollama.md index 3548e02f3..3d17425d0 100644 --- a/docs/providers/ollama.md +++ b/docs/providers/ollama.md @@ -215,5 +215,5 @@ ollama serve ## See Also - [Model Providers](/concepts/model-providers) - Overview of all providers -- [Model Selection](/agents/model-selection) - How to choose models -- [Configuration](/configuration) - Full config reference +- [Model Selection](/concepts/models) - How to choose models +- [Configuration](/gateway/configuration) - Full config reference From ce89bc2b40bbf4b174485fc9d5ace96d81152c42 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 25 Jan 2026 00:07:13 +0000 Subject: [PATCH 003/214] docs: add anthropic auth error troubleshooting --- docs/gateway/troubleshooting.md | 18 ++++++++++++++++++ docs/providers/anthropic.md | 5 +++++ 2 files changed, 23 insertions(+) diff --git a/docs/gateway/troubleshooting.md b/docs/gateway/troubleshooting.md index c3e245cb2..24815e258 100644 --- a/docs/gateway/troubleshooting.md +++ b/docs/gateway/troubleshooting.md @@ -31,6 +31,24 @@ See also: [Health checks](/gateway/health) and [Logging](/logging). ## Common Issues +### No API key found for provider "anthropic" + +This means the **agent’s auth store is empty** or missing Anthropic credentials. +Auth is **per agent**, so a new agent won’t inherit the main agent’s keys. + +Fix options: +- Re-run onboarding and choose **Anthropic** for that agent. +- Or paste a setup-token on the **gateway host**: + ```bash + clawdbot models auth setup-token --provider anthropic + ``` +- Or copy `auth-profiles.json` from the main agent dir to the new agent dir. + +Verify: +```bash +clawdbot models status +``` + ### OAuth token refresh failed (Anthropic Claude subscription) This means the stored Anthropic OAuth token expired and the refresh failed. diff --git a/docs/providers/anthropic.md b/docs/providers/anthropic.md index b5f723d92..7876c4ae9 100644 --- a/docs/providers/anthropic.md +++ b/docs/providers/anthropic.md @@ -114,6 +114,11 @@ clawdbot onboard --auth-choice claude-cli - If the Claude CLI login lives on a different machine, use `clawdbot models auth paste-token --provider anthropic` on the gateway host. +**No API key found for provider "anthropic"** +- Auth is **per agent**. New agents don’t inherit the main agent’s keys. +- Re-run onboarding for that agent, or paste a setup-token / API key on the + gateway host, then verify with `clawdbot models status`. + **No credentials found for profile `anthropic:default` or `anthropic:claude-cli`** - Run `clawdbot models status` to see which auth profile is active. - Re-run onboarding, or paste a setup-token / API key for that profile. From d57b88c7afb230ed48007beed03ff5f612c145d8 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 25 Jan 2026 00:10:25 +0000 Subject: [PATCH 004/214] docs: add railway quick checklist --- docs/railway.mdx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/railway.mdx b/docs/railway.mdx index a4794eb20..21e985f2c 100644 --- a/docs/railway.mdx +++ b/docs/railway.mdx @@ -3,6 +3,16 @@ title: Deploy on Railway --- Deploy Clawdbot on Railway with a one-click template and finish setup in your browser. +This is the easiest “no terminal on the server” path: Railway runs the Gateway for you, +and you configure everything via the `/setup` web wizard. + +## Quick checklist (new users) + +1) Click **Deploy on Railway** (below). +2) Add a **Volume** mounted at `/data`. +3) Set the required **Variables** (at least `SETUP_PASSWORD`). +4) Enable **HTTP Proxy** on port `8080`. +5) Open `https:///setup` and finish the wizard. ## One-click deploy From cbe19ad2f2601dc868fa49c9bf893faca7110841 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 25 Jan 2026 00:12:31 +0000 Subject: [PATCH 005/214] docs: add hosting hub links --- docs/help/faq.md | 16 ++++++++++++++++ docs/platforms/index.md | 1 + 2 files changed, 17 insertions(+) diff --git a/docs/help/faq.md b/docs/help/faq.md index 65593ff05..cae91ec15 100644 --- a/docs/help/faq.md +++ b/docs/help/faq.md @@ -26,6 +26,7 @@ Quick answers plus deeper troubleshooting for real-world setups (local dev, VPS, - [The docs didn’t answer my question — how do I get a better answer?](#the-docs-didnt-answer-my-question--how-do-i-get-a-better-answer) - [How do I install Clawdbot on Linux?](#how-do-i-install-clawdbot-on-linux) - [How do I install Clawdbot on a VPS?](#how-do-i-install-clawdbot-on-a-vps) + - [Where are the cloud/VPS install guides?](#where-are-the-cloudvps-install-guides) - [Can I ask Clawd to update itself?](#can-i-ask-clawd-to-update-itself) - [What does the onboarding wizard actually do?](#what-does-the-onboarding-wizard-actually-do) - [Do I need a Claude or OpenAI subscription to run this?](#do-i-need-a-claude-or-openai-subscription-to-run-this) @@ -437,6 +438,21 @@ Any Linux VPS works. Install on the server, then use SSH/Tailscale to reach the Guides: [exe.dev](/platforms/exe-dev), [Hetzner](/platforms/hetzner), [Fly.io](/platforms/fly). Remote access: [Gateway remote](/gateway/remote). +### Where are the cloud/VPS install guides? + +We keep a **hosting hub** with the common providers. Pick one and follow the guide: + +- [Railway](/railway) (one‑click, browser‑based setup) +- [Fly.io](/platforms/fly) +- [Hetzner](/platforms/hetzner) +- [exe.dev](/platforms/exe-dev) + +How it works in the cloud: the **Gateway runs on the server**, and you access it +from your laptop/phone via the Control UI (or Tailscale/SSH). Your state + workspace +live on the server, so treat the host as the source of truth and back it up. + +Hub: [Platforms](/platforms). Remote access: [Gateway remote](/gateway/remote). + ### Can I ask Clawd to update itself? Short answer: **possible, not recommended**. The update flow can restart the diff --git a/docs/platforms/index.md b/docs/platforms/index.md index bc721db8e..8184ea00c 100644 --- a/docs/platforms/index.md +++ b/docs/platforms/index.md @@ -23,6 +23,7 @@ Native companion apps for Windows are also planned; the Gateway is recommended v ## VPS & hosting +- Railway (one-click): [Railway](/railway) - Fly.io: [Fly.io](/platforms/fly) - Hetzner (Docker): [Hetzner](/platforms/hetzner) - exe.dev (VM + HTTPS proxy): [exe.dev](/platforms/exe-dev) From 2f58d59f22e3eb6d29d8dde55ebf560ad3a1f769 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 25 Jan 2026 00:13:44 +0000 Subject: [PATCH 006/214] docs: add nodes note to cloud guides --- docs/help/faq.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/help/faq.md b/docs/help/faq.md index cae91ec15..f32ecb299 100644 --- a/docs/help/faq.md +++ b/docs/help/faq.md @@ -451,7 +451,12 @@ How it works in the cloud: the **Gateway runs on the server**, and you access it from your laptop/phone via the Control UI (or Tailscale/SSH). Your state + workspace live on the server, so treat the host as the source of truth and back it up. +You can pair **nodes** (Mac/iOS/Android/headless) to that cloud Gateway to access +local screen/camera/canvas or run commands on your laptop while keeping the +Gateway in the cloud. + Hub: [Platforms](/platforms). Remote access: [Gateway remote](/gateway/remote). +Nodes: [Nodes](/nodes), [Nodes CLI](/cli/nodes). ### Can I ask Clawd to update itself? From 426168a338c37383b66d9ac2b063990fd5124811 Mon Sep 17 00:00:00 2001 From: Richard Pinedo Date: Sat, 24 Jan 2026 19:15:54 -0500 Subject: [PATCH 007/214] Add link understanding tool support (#1637) * Add * Fix --------- Co-authored-by: Richard --- src/auto-reply/reply/get-reply.ts | 5 + src/auto-reply/templating.ts | 1 + src/config/schema.ts | 5 + src/config/types.tools.ts | 25 +++++ src/config/zod-schema.agent-runtime.ts | 2 + src/config/zod-schema.core.ts | 20 ++++ src/link-understanding/apply.ts | 37 +++++++ src/link-understanding/defaults.ts | 2 + src/link-understanding/detect.test.ts | 27 +++++ src/link-understanding/detect.ts | 49 +++++++++ src/link-understanding/format.ts | 10 ++ src/link-understanding/index.ts | 4 + src/link-understanding/runner.ts | 136 +++++++++++++++++++++++++ 13 files changed, 323 insertions(+) create mode 100644 src/link-understanding/apply.ts create mode 100644 src/link-understanding/defaults.ts create mode 100644 src/link-understanding/detect.test.ts create mode 100644 src/link-understanding/detect.ts create mode 100644 src/link-understanding/format.ts create mode 100644 src/link-understanding/index.ts create mode 100644 src/link-understanding/runner.ts diff --git a/src/auto-reply/reply/get-reply.ts b/src/auto-reply/reply/get-reply.ts index 20887c340..f6259d738 100644 --- a/src/auto-reply/reply/get-reply.ts +++ b/src/auto-reply/reply/get-reply.ts @@ -12,6 +12,7 @@ import { resolveCommandAuthorization } from "../command-auth.js"; import type { MsgContext } from "../templating.js"; import { SILENT_REPLY_TOKEN } from "../tokens.js"; import { applyMediaUnderstanding } from "../../media-understanding/apply.js"; +import { applyLinkUnderstanding } from "../../link-understanding/apply.js"; import type { GetReplyOptions, ReplyPayload } from "../types.js"; import { resolveDefaultModel } from "./directive-handling.js"; import { resolveReplyDirectives } from "./get-reply-directives.js"; @@ -89,6 +90,10 @@ export async function getReplyFromConfig( agentDir, activeModel: { provider, model }, }); + await applyLinkUnderstanding({ + ctx: finalized, + cfg, + }); } const commandAuthorized = finalized.CommandAuthorized; diff --git a/src/auto-reply/templating.ts b/src/auto-reply/templating.ts index e9cd6d229..dd424ee71 100644 --- a/src/auto-reply/templating.ts +++ b/src/auto-reply/templating.ts @@ -71,6 +71,7 @@ export type MsgContext = { Transcript?: string; MediaUnderstanding?: MediaUnderstandingOutput[]; MediaUnderstandingDecisions?: MediaUnderstandingDecision[]; + LinkUnderstanding?: string[]; Prompt?: string; MaxChars?: number; ChatType?: string; diff --git a/src/config/schema.ts b/src/config/schema.ts index d7ad28b5c..d61b5964e 100644 --- a/src/config/schema.ts +++ b/src/config/schema.ts @@ -158,6 +158,11 @@ const FIELD_LABELS: Record = { "tools.media.video.attachments": "Video Understanding Attachment Policy", "tools.media.video.models": "Video Understanding Models", "tools.media.video.scope": "Video Understanding Scope", + "tools.links.enabled": "Enable Link Understanding", + "tools.links.maxLinks": "Link Understanding Max Links", + "tools.links.timeoutSeconds": "Link Understanding Timeout (sec)", + "tools.links.models": "Link Understanding Models", + "tools.links.scope": "Link Understanding Scope", "tools.profile": "Tool Profile", "agents.list[].tools.profile": "Agent Tool Profile", "tools.byProvider": "Tool Policy by Provider", diff --git a/src/config/types.tools.ts b/src/config/types.tools.ts index fab0cca47..ad7f69d85 100644 --- a/src/config/types.tools.ts +++ b/src/config/types.tools.ts @@ -102,6 +102,30 @@ export type MediaUnderstandingConfig = { models?: MediaUnderstandingModelConfig[]; }; +export type LinkModelConfig = { + /** Use a CLI command for link processing. */ + type?: "cli"; + /** CLI binary (required when type=cli). */ + command: string; + /** CLI args (template-enabled). */ + args?: string[]; + /** Optional timeout override (seconds) for this model entry. */ + timeoutSeconds?: number; +}; + +export type LinkToolsConfig = { + /** Enable link understanding when models are configured. */ + enabled?: boolean; + /** Optional scope gating for understanding. */ + scope?: MediaUnderstandingScopeConfig; + /** Max number of links to process per message. */ + maxLinks?: number; + /** Default timeout (seconds). */ + timeoutSeconds?: number; + /** Ordered model list (fallbacks in order). */ + models?: LinkModelConfig[]; +}; + export type MediaToolsConfig = { /** Shared model list applied across image/audio/video. */ models?: MediaUnderstandingModelConfig[]; @@ -347,6 +371,7 @@ export type ToolsConfig = { }; }; media?: MediaToolsConfig; + links?: LinkToolsConfig; /** Message tool configuration. */ message?: { /** diff --git a/src/config/zod-schema.agent-runtime.ts b/src/config/zod-schema.agent-runtime.ts index 5f82cff77..c733dcfa9 100644 --- a/src/config/zod-schema.agent-runtime.ts +++ b/src/config/zod-schema.agent-runtime.ts @@ -5,6 +5,7 @@ import { GroupChatSchema, HumanDelaySchema, IdentitySchema, + ToolsLinksSchema, ToolsMediaSchema, } from "./zod-schema.core.js"; @@ -428,6 +429,7 @@ export const ToolsSchema = z byProvider: z.record(z.string(), ToolPolicyWithProfileSchema).optional(), web: ToolsWebSchema, media: ToolsMediaSchema, + links: ToolsLinksSchema, message: z .object({ allowCrossContextSend: z.boolean().optional(), diff --git a/src/config/zod-schema.core.ts b/src/config/zod-schema.core.ts index 4087b8c7a..0301a52fe 100644 --- a/src/config/zod-schema.core.ts +++ b/src/config/zod-schema.core.ts @@ -454,6 +454,26 @@ export const ToolsMediaSchema = z .strict() .optional(); +export const LinkModelSchema = z + .object({ + type: z.literal("cli").optional(), + command: z.string().min(1), + args: z.array(z.string()).optional(), + timeoutSeconds: z.number().int().positive().optional(), + }) + .strict(); + +export const ToolsLinksSchema = z + .object({ + enabled: z.boolean().optional(), + scope: MediaUnderstandingScopeSchema, + maxLinks: z.number().int().positive().optional(), + timeoutSeconds: z.number().int().positive().optional(), + models: z.array(LinkModelSchema).optional(), + }) + .strict() + .optional(); + export const NativeCommandsSettingSchema = z.union([z.boolean(), z.literal("auto")]); export const ProviderCommandsSchema = z diff --git a/src/link-understanding/apply.ts b/src/link-understanding/apply.ts new file mode 100644 index 000000000..82cd1e9f4 --- /dev/null +++ b/src/link-understanding/apply.ts @@ -0,0 +1,37 @@ +import type { ClawdbotConfig } from "../config/config.js"; +import type { MsgContext } from "../auto-reply/templating.js"; +import { finalizeInboundContext } from "../auto-reply/reply/inbound-context.js"; +import { formatLinkUnderstandingBody } from "./format.js"; +import { runLinkUnderstanding } from "./runner.js"; + +export type ApplyLinkUnderstandingResult = { + outputs: string[]; + urls: string[]; +}; + +export async function applyLinkUnderstanding(params: { + ctx: MsgContext; + cfg: ClawdbotConfig; +}): Promise { + const result = await runLinkUnderstanding({ + cfg: params.cfg, + ctx: params.ctx, + }); + + if (result.outputs.length === 0) { + return result; + } + + params.ctx.LinkUnderstanding = [...(params.ctx.LinkUnderstanding ?? []), ...result.outputs]; + params.ctx.Body = formatLinkUnderstandingBody({ + body: params.ctx.Body, + outputs: result.outputs, + }); + + finalizeInboundContext(params.ctx, { + forceBodyForAgent: true, + forceBodyForCommands: true, + }); + + return result; +} diff --git a/src/link-understanding/defaults.ts b/src/link-understanding/defaults.ts new file mode 100644 index 000000000..1b35621ef --- /dev/null +++ b/src/link-understanding/defaults.ts @@ -0,0 +1,2 @@ +export const DEFAULT_LINK_TIMEOUT_SECONDS = 30; +export const DEFAULT_MAX_LINKS = 3; diff --git a/src/link-understanding/detect.test.ts b/src/link-understanding/detect.test.ts new file mode 100644 index 000000000..07545f403 --- /dev/null +++ b/src/link-understanding/detect.test.ts @@ -0,0 +1,27 @@ +import { describe, expect, it } from "vitest"; + +import { extractLinksFromMessage } from "./detect.js"; + +describe("extractLinksFromMessage", () => { + it("extracts bare http/https URLs in order", () => { + const links = extractLinksFromMessage("see https://a.example and http://b.test"); + expect(links).toEqual(["https://a.example", "http://b.test"]); + }); + + it("dedupes links and enforces maxLinks", () => { + const links = extractLinksFromMessage("https://a.example https://a.example https://b.test", { + maxLinks: 1, + }); + expect(links).toEqual(["https://a.example"]); + }); + + it("ignores markdown links", () => { + const links = extractLinksFromMessage("[doc](https://docs.example) https://bare.example"); + expect(links).toEqual(["https://bare.example"]); + }); + + it("blocks 127.0.0.1", () => { + const links = extractLinksFromMessage("http://127.0.0.1/test https://ok.test"); + expect(links).toEqual(["https://ok.test"]); + }); +}); diff --git a/src/link-understanding/detect.ts b/src/link-understanding/detect.ts new file mode 100644 index 000000000..9edecde63 --- /dev/null +++ b/src/link-understanding/detect.ts @@ -0,0 +1,49 @@ +import { DEFAULT_MAX_LINKS } from "./defaults.js"; + +// Remove markdown link syntax so only bare URLs are considered. +const MARKDOWN_LINK_RE = /\[[^\]]*]\((https?:\/\/\S+?)\)/gi; +const BARE_LINK_RE = /https?:\/\/\S+/gi; + +function stripMarkdownLinks(message: string): string { + return message.replace(MARKDOWN_LINK_RE, " "); +} + +function resolveMaxLinks(value?: number): number { + if (typeof value === "number" && Number.isFinite(value) && value > 0) { + return Math.floor(value); + } + return DEFAULT_MAX_LINKS; +} + +function isAllowedUrl(raw: string): boolean { + try { + const parsed = new URL(raw); + if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return false; + if (parsed.hostname === "127.0.0.1") return false; + return true; + } catch { + return false; + } +} + +export function extractLinksFromMessage(message: string, opts?: { maxLinks?: number }): string[] { + const source = message?.trim(); + if (!source) return []; + + const maxLinks = resolveMaxLinks(opts?.maxLinks); + const sanitized = stripMarkdownLinks(source); + const seen = new Set(); + const results: string[] = []; + + for (const match of sanitized.matchAll(BARE_LINK_RE)) { + const raw = match[0]?.trim(); + if (!raw) continue; + if (!isAllowedUrl(raw)) continue; + if (seen.has(raw)) continue; + seen.add(raw); + results.push(raw); + if (results.length >= maxLinks) break; + } + + return results; +} diff --git a/src/link-understanding/format.ts b/src/link-understanding/format.ts new file mode 100644 index 000000000..b28d16a1a --- /dev/null +++ b/src/link-understanding/format.ts @@ -0,0 +1,10 @@ +export function formatLinkUnderstandingBody(params: { body?: string; outputs: string[] }): string { + const outputs = params.outputs.map((output) => output.trim()).filter(Boolean); + if (outputs.length === 0) { + return params.body ?? ""; + } + + const base = (params.body ?? "").trim(); + if (!base) return outputs.join("\n"); + return `${base}\n\n${outputs.join("\n")}`; +} diff --git a/src/link-understanding/index.ts b/src/link-understanding/index.ts new file mode 100644 index 000000000..d772f9655 --- /dev/null +++ b/src/link-understanding/index.ts @@ -0,0 +1,4 @@ +export { applyLinkUnderstanding } from "./apply.js"; +export { extractLinksFromMessage } from "./detect.js"; +export { formatLinkUnderstandingBody } from "./format.js"; +export { runLinkUnderstanding } from "./runner.js"; diff --git a/src/link-understanding/runner.ts b/src/link-understanding/runner.ts new file mode 100644 index 000000000..d5976a7a4 --- /dev/null +++ b/src/link-understanding/runner.ts @@ -0,0 +1,136 @@ +import type { ClawdbotConfig } from "../config/config.js"; +import type { MsgContext } from "../auto-reply/templating.js"; +import { applyTemplate } from "../auto-reply/templating.js"; +import type { LinkModelConfig, LinkToolsConfig } from "../config/types.tools.js"; +import { logVerbose, shouldLogVerbose } from "../globals.js"; +import { runExec } from "../process/exec.js"; +import { CLI_OUTPUT_MAX_BUFFER } from "../media-understanding/defaults.js"; +import { resolveTimeoutMs } from "../media-understanding/resolve.js"; +import { + normalizeMediaUnderstandingChatType, + resolveMediaUnderstandingScope, +} from "../media-understanding/scope.js"; +import { DEFAULT_LINK_TIMEOUT_SECONDS } from "./defaults.js"; +import { extractLinksFromMessage } from "./detect.js"; + +export type LinkUnderstandingResult = { + urls: string[]; + outputs: string[]; +}; + +function resolveScopeDecision(params: { + config?: LinkToolsConfig; + ctx: MsgContext; +}): "allow" | "deny" { + return resolveMediaUnderstandingScope({ + scope: params.config?.scope, + sessionKey: params.ctx.SessionKey, + channel: params.ctx.Surface ?? params.ctx.Provider, + chatType: normalizeMediaUnderstandingChatType(params.ctx.ChatType), + }); +} + +function resolveTimeoutMsFromConfig(params: { + config?: LinkToolsConfig; + entry: LinkModelConfig; +}): number { + const configured = params.entry.timeoutSeconds ?? params.config?.timeoutSeconds; + return resolveTimeoutMs(configured, DEFAULT_LINK_TIMEOUT_SECONDS); +} + +async function runCliEntry(params: { + entry: LinkModelConfig; + ctx: MsgContext; + url: string; + config?: LinkToolsConfig; +}): Promise { + if ((params.entry.type ?? "cli") !== "cli") return null; + const command = params.entry.command.trim(); + if (!command) return null; + const args = params.entry.args ?? []; + const timeoutMs = resolveTimeoutMsFromConfig({ config: params.config, entry: params.entry }); + const templCtx = { + ...params.ctx, + LinkUrl: params.url, + }; + const argv = [command, ...args].map((part, index) => + index === 0 ? part : applyTemplate(part, templCtx), + ); + + if (shouldLogVerbose()) { + logVerbose(`Link understanding via CLI: ${argv.join(" ")}`); + } + + const { stdout } = await runExec(argv[0], argv.slice(1), { + timeoutMs, + maxBuffer: CLI_OUTPUT_MAX_BUFFER, + }); + const trimmed = stdout.trim(); + return trimmed || null; +} + +async function runLinkEntries(params: { + entries: LinkModelConfig[]; + ctx: MsgContext; + url: string; + config?: LinkToolsConfig; +}): Promise { + let lastError: unknown; + for (const entry of params.entries) { + try { + const output = await runCliEntry({ + entry, + ctx: params.ctx, + url: params.url, + config: params.config, + }); + if (output) return output; + } catch (err) { + lastError = err; + if (shouldLogVerbose()) { + logVerbose(`Link understanding failed for ${params.url}: ${String(err)}`); + } + } + } + if (lastError && shouldLogVerbose()) { + logVerbose(`Link understanding exhausted for ${params.url}`); + } + return null; +} + +export async function runLinkUnderstanding(params: { + cfg: ClawdbotConfig; + ctx: MsgContext; + message?: string; +}): Promise { + const config = params.cfg.tools?.links; + if (!config || config.enabled === false) return { urls: [], outputs: [] }; + + const scopeDecision = resolveScopeDecision({ config, ctx: params.ctx }); + if (scopeDecision === "deny") { + if (shouldLogVerbose()) { + logVerbose("Link understanding disabled by scope policy."); + } + return { urls: [], outputs: [] }; + } + + const message = params.message ?? params.ctx.CommandBody ?? params.ctx.RawBody ?? params.ctx.Body; + const links = extractLinksFromMessage(message ?? "", { maxLinks: config?.maxLinks }); + if (links.length === 0) return { urls: [], outputs: [] }; + + const entries = config?.models ?? []; + if (entries.length === 0) return { urls: links, outputs: [] }; + + const outputs: string[] = []; + for (const url of links) { + const output = await runLinkEntries({ + entries, + ctx: params.ctx, + url, + config, + }); + if (output) outputs.push(output); + } + + return { urls: links, outputs }; +} From 3696aade0910ed51a718d2607ecbfa547470d7fe Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 25 Jan 2026 00:19:02 +0000 Subject: [PATCH 008/214] chore: refresh pnpm lock --- pnpm-lock.yaml | 805 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 625 insertions(+), 180 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4ee87da5d..bbb6961a2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -346,7 +346,7 @@ importers: dependencies: clawdbot: specifier: '>=2026.1.23-1' - version: 2026.1.23(@types/express@5.0.6)(audio-decode@2.2.3)(devtools-protocol@0.0.1561482)(typescript@5.9.3) + version: 2026.1.23-1(@types/express@5.0.6)(audio-decode@2.2.3)(devtools-protocol@0.0.1561482)(typescript@5.9.3) extensions/memory-lancedb: dependencies: @@ -397,10 +397,6 @@ importers: extensions/open-prose: {} - extensions/open-prose: {} - - extensions/open-prose: {} - extensions/signal: {} extensions/slack: {} @@ -512,82 +508,142 @@ packages: '@aws-crypto/util@5.2.0': resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} - '@aws-sdk/client-bedrock-runtime@3.975.0': - resolution: {integrity: sha512-ZptHL8Z8y2m6sq1ksl+MIGoXxzRkWuOzqbGOd+P5htwIX0kEvzmxPwAqyCoiULn1OjS+kB+TCxfvBUVyglq3MQ==} + '@aws-sdk/client-bedrock-runtime@3.972.0': + resolution: {integrity: sha512-rzSuqgMkL488bR9TnZEALBa+SV1FfR3B7CkYvs6R5uZm2AqBMfq7xNZR/pgMiAH/YLlI9FWAh1aPmdnG7iXxnA==} engines: {node: '>=20.0.0'} '@aws-sdk/client-bedrock@3.975.0': resolution: {integrity: sha512-rA30CX0zcTGKx0S8JSyASVKFYTdQmkDkpkE5o1Mv4j3RmLcp7J2/WeYGVLjWprkNjlAlfpxG3V9VqPsayQ3LzA==} engines: {node: '>=20.0.0'} + '@aws-sdk/client-sso@3.972.0': + resolution: {integrity: sha512-5qw6qLiRE4SUiz0hWy878dSR13tSVhbTWhsvFT8mGHe37NRRiaobm5MA2sWD0deRAuO98djSiV+dhWXa1xIFNw==} + engines: {node: '>=20.0.0'} + '@aws-sdk/client-sso@3.974.0': resolution: {integrity: sha512-ci+GiM0c4ULo4D79UMcY06LcOLcfvUfiyt8PzNY0vbt5O8BfCPYf4QomwVgkNcLLCYmroO4ge2Yy1EsLUlcD6g==} engines: {node: '>=20.0.0'} + '@aws-sdk/core@3.972.0': + resolution: {integrity: sha512-nEeUW2M9F+xdIaD98F5MBcQ4ITtykj3yKbgFZ6J0JtL3bq+Z90szQ6Yy8H/BLPYXTs3V4n9ifnBo8cprRDiE6A==} + engines: {node: '>=20.0.0'} + '@aws-sdk/core@3.973.1': resolution: {integrity: sha512-Ocubx42QsMyVs9ANSmFpRm0S+hubWljpPLjOi9UFrtcnVJjrVJTzQ51sN0e5g4e8i8QZ7uY73zosLmgYL7kZTQ==} engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-env@3.972.0': + resolution: {integrity: sha512-kKHoNv+maHlPQOAhYamhap0PObd16SAb3jwaY0KYgNTiSbeXlbGUZPLioo9oA3wU10zItJzx83ClU7d7h40luA==} + engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-env@3.972.1': resolution: {integrity: sha512-/etNHqnx96phy/SjI0HRC588o4vKH5F0xfkZ13yAATV7aNrb+5gYGNE6ePWafP+FuZ3HkULSSlJFj0AxgrAqYw==} engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-http@3.972.0': + resolution: {integrity: sha512-xzEi81L7I5jGUbpmqEHCe7zZr54hCABdj4H+3LzktHYuovV/oqnvoDdvZpGFR0e/KAw1+PL38NbGrpG30j6qlA==} + engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-http@3.972.2': resolution: {integrity: sha512-mXgdaUfe5oM+tWKyeZ7Vh/iQ94FrkMky1uuzwTOmFADiRcSk5uHy/e3boEFedXiT/PRGzgBmqvJVK4F6lUISCg==} engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-ini@3.972.0': + resolution: {integrity: sha512-ruhAMceUIq2aknFd3jhWxmO0P0Efab5efjyIXOkI9i80g+zDY5VekeSxfqRKStEEJSKSCHDLQuOu0BnAn4Rzew==} + engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-ini@3.972.1': resolution: {integrity: sha512-OdbJA3v+XlNDsrYzNPRUwr8l7gw1r/nR8l4r96MDzSBDU8WEo8T6C06SvwaXR8SpzsjO3sq5KMP86wXWg7Rj4g==} engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-login@3.972.0': + resolution: {integrity: sha512-SsrsFJsEYAJHO4N/r2P0aK6o8si6f1lprR+Ej8J731XJqTckSGs/HFHcbxOyW/iKt+LNUvZa59/VlJmjhF4bEQ==} + engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-login@3.972.1': resolution: {integrity: sha512-CccqDGL6ZrF3/EFWZefvKW7QwwRdxlHUO8NVBKNVcNq6womrPDvqB6xc9icACtE0XB0a7PLoSTkAg8bQVkTO2w==} engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-node@3.972.0': + resolution: {integrity: sha512-wwJDpEGl6+sOygic8QKu0OHVB8SiodqF1fr5jvUlSFfS6tJss/E9vBc2aFjl7zI6KpAIYfIzIgM006lRrZtWCQ==} + engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-node@3.972.1': resolution: {integrity: sha512-DwXPk9GfuU/xG9tmCyXFVkCr6X3W8ZCoL5Ptb0pbltEx1/LCcg7T+PBqDlPiiinNCD6ilIoMJDWsnJ8ikzZA7Q==} engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-process@3.972.0': + resolution: {integrity: sha512-nmzYhamLDJ8K+v3zWck79IaKMc350xZnWsf/GeaXO6E3MewSzd3lYkTiMi7lEp3/UwDm9NHfPguoPm+mhlSWQQ==} + engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-process@3.972.1': resolution: {integrity: sha512-bi47Zigu3692SJwdBvo8y1dEwE6B61stCwCFnuRWJVTfiM84B+VTSCV661CSWJmIZzmcy7J5J3kWyxL02iHj0w==} engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-sso@3.972.0': + resolution: {integrity: sha512-6mYyfk1SrMZ15cH9T53yAF4YSnvq4yU1Xlgm3nqV1gZVQzmF5kr4t/F3BU3ygbvzi4uSwWxG3I3TYYS5eMlAyg==} + engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-sso@3.972.1': resolution: {integrity: sha512-dLZVNhM7wSgVUFsgVYgI5hb5Z/9PUkT46pk/SHrSmUqfx6YDvoV4YcPtaiRqviPpEGGiRtdQMEadyOKIRqulUQ==} engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-web-identity@3.972.0': + resolution: {integrity: sha512-vsJXBGL8H54kz4T6do3p5elATj5d1izVGUXMluRJntm9/I0be/zUYtdd4oDTM2kSUmd4Zhyw3fMQ9lw7CVhd4A==} + engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-web-identity@3.972.1': resolution: {integrity: sha512-YMDeYgi0u687Ay0dAq/pFPKuijrlKTgsaB/UATbxCs/FzZfMiG4If5ksywHmmW7MiYUF8VVv+uou3TczvLrN4w==} engines: {node: '>=20.0.0'} - '@aws-sdk/eventstream-handler-node@3.972.1': - resolution: {integrity: sha512-sbPqSY+BjhHDTRUhCEvCY3lNL76FcPxiTuYesbSV0ZBfPT1JONjkAT8U6DIAy9C0ynlEuPfdVngMAOFDxP0kcQ==} + '@aws-sdk/eventstream-handler-node@3.972.0': + resolution: {integrity: sha512-B1AEv+TQOVxg2t60GMfrcagJvQjpx1p6UASUoFMLevV9K3WNI5qYTjtutMiifKY0HwK6g86zXgN/dpeaSi3q5Q==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-eventstream@3.972.1': - resolution: {integrity: sha512-40iO9eYwycHmyZ5MnBRlQy35P7Aug3FRVAfrU8Lp88Ti66amJynvzgAuM+JaE/4LUTfFWigLfmdIp/d8CX625g==} + '@aws-sdk/middleware-eventstream@3.972.0': + resolution: {integrity: sha512-DAxRFg8txGGQUOCR3lPK15tjULafmoHR6Vmoi4WAm+GAnR+pHxJQfc2yN1+mfd0q6HqWfTCDJvJg8qZ4I8/I9g==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-host-header@3.972.0': + resolution: {integrity: sha512-3eztFI6F9/eHtkIaWKN3nT+PM+eQ6p1MALDuNshFk323ixuCZzOOVT8oUqtZa30Z6dycNXJwhlIq7NhUVFfimw==} engines: {node: '>=20.0.0'} '@aws-sdk/middleware-host-header@3.972.1': resolution: {integrity: sha512-/R82lXLPmZ9JaUGSUdKtBp2k/5xQxvBT3zZWyKiBOhyulFotlfvdlrO8TnqstBimsl4lYEYySDL+W6ldFh6ALg==} engines: {node: '>=20.0.0'} + '@aws-sdk/middleware-logger@3.972.0': + resolution: {integrity: sha512-ZvdyVRwzK+ra31v1pQrgbqR/KsLD+wwJjHgko6JfoKUBIcEfAwJzQKO6HspHxdHWTVUz6MgvwskheR/TTYZl2g==} + engines: {node: '>=20.0.0'} + '@aws-sdk/middleware-logger@3.972.1': resolution: {integrity: sha512-JGgFl6cHg9G2FHu4lyFIzmFN8KESBiRr84gLC3Aeni0Gt1nKm+KxWLBuha/RPcXxJygGXCcMM4AykkIwxor8RA==} engines: {node: '>=20.0.0'} + '@aws-sdk/middleware-recursion-detection@3.972.0': + resolution: {integrity: sha512-F2SmUeO+S6l1h6dydNet3BQIk173uAkcfU1HDkw/bUdRLAnh15D3HP9vCZ7oCPBNcdEICbXYDmx0BR9rRUHGlQ==} + engines: {node: '>=20.0.0'} + '@aws-sdk/middleware-recursion-detection@3.972.1': resolution: {integrity: sha512-taGzNRe8vPHjnliqXIHp9kBgIemLE/xCaRTMH1NH0cncHeaPcjxtnCroAAM9aOlPuKvBe2CpZESyvM1+D8oI7Q==} engines: {node: '>=20.0.0'} + '@aws-sdk/middleware-user-agent@3.972.0': + resolution: {integrity: sha512-kFHQm2OCBJCzGWRafgdWHGFjitUXY/OxXngymcX4l8CiyiNDZB27HDDBg2yLj3OUJc4z4fexLMmP8r9vgag19g==} + engines: {node: '>=20.0.0'} + '@aws-sdk/middleware-user-agent@3.972.2': resolution: {integrity: sha512-d+Exq074wy0X6wvShg/kmZVtkah+28vMuqCtuY3cydg8LUZOJBtbAolCpEJizSyb8mJJZF9BjWaTANXL4OYnkg==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-websocket@3.972.1': - resolution: {integrity: sha512-zej/2+u6KCEHUipHW/4KXwj+4PTJkrORwR4KHNHE8ATdzLf7hwu7HsPK+TyQXZSftH+VqYkFmTbyF9OPxOpwmw==} + '@aws-sdk/middleware-websocket@3.972.0': + resolution: {integrity: sha512-3pvbb/HtE7A8U38jk24RQ9T92d40NNSzjDEVEkBYZYhxExVcJ/Lk5Z+NM283FEtoi1T++oYrLuYDr1CIQxnaXQ==} engines: {node: '>= 14.0.0'} + '@aws-sdk/nested-clients@3.972.0': + resolution: {integrity: sha512-QGlbnuGzSQJVG6bR9Qw6G0Blh6abFR4VxNa61ttMbzy9jt28xmk2iGtrYLrQPlCCPhY6enHqjTWm3n3LOb0wAw==} + engines: {node: '>=20.0.0'} + '@aws-sdk/nested-clients@3.974.0': resolution: {integrity: sha512-k3dwdo/vOiHMJc9gMnkPl1BA5aQfTrZbz+8fiDkWrPagqAioZgmo5oiaOaeX0grObfJQKDtcpPFR4iWf8cgl8Q==} engines: {node: '>=20.0.0'} @@ -596,10 +652,18 @@ packages: resolution: {integrity: sha512-OkeFHPlQj2c/Y5bQGkX14pxhDWUGUFt3LRHhjcDKsSCw6lrxKcxN3WFZN0qbJwKNydP+knL5nxvfgKiCLpTLRA==} engines: {node: '>=20.0.0'} + '@aws-sdk/region-config-resolver@3.972.0': + resolution: {integrity: sha512-JyOf+R/6vJW8OEVFCAyzEOn2reri/Q+L0z9zx4JQSKWvTmJ1qeFO25sOm8VIfB8URKhfGRTQF30pfYaH2zxt/A==} + engines: {node: '>=20.0.0'} + '@aws-sdk/region-config-resolver@3.972.1': resolution: {integrity: sha512-voIY8RORpxLAEgEkYaTFnkaIuRwVBEc+RjVZYcSSllPV+ZEKAacai6kNhJeE3D70Le+JCfvRb52tng/AVHY+jQ==} engines: {node: '>=20.0.0'} + '@aws-sdk/token-providers@3.972.0': + resolution: {integrity: sha512-kWlXG+y5nZhgXGEtb72Je+EvqepBPs8E3vZse//1PYLWs2speFqbGE/ywCXmzEJgHgVqSB/u/lqBvs5WlYmSqQ==} + engines: {node: '>=20.0.0'} + '@aws-sdk/token-providers@3.974.0': resolution: {integrity: sha512-cBykL0LiccKIgNhGWvQRTPvsBLPZxnmJU3pYxG538jpFX8lQtrCy1L7mmIHNEdxIdIGEPgAEHF8/JQxgBToqUQ==} engines: {node: '>=20.0.0'} @@ -620,17 +684,29 @@ packages: resolution: {integrity: sha512-6JHsl1V/a1ZW8D8AFfd4R52fwZPnZ5H4U6DS8m/bWT8qad72NvbOFAC7U2cDtFs2TShqUO3TEiX/EJibtY3ijg==} engines: {node: '>=20.0.0'} - '@aws-sdk/util-format-url@3.972.1': - resolution: {integrity: sha512-8wJ4/XOLU/RIYBHsXsIOTR04bNmalC8F2YPMyf3oL8YC750M3Rv5WGywW0Fo07HCv770KXJOzVq03Gyl68moFg==} + '@aws-sdk/util-format-url@3.972.0': + resolution: {integrity: sha512-o4zqsW/PxrcsTla/Yh2dkRS26kP76QQWZq/i/JgVNFBAr9x0E2oJcCeh8Daj2AA+8AZ8VWln9x706FFzWWQwvQ==} engines: {node: '>=20.0.0'} '@aws-sdk/util-locate-window@3.965.3': resolution: {integrity: sha512-FNUqAjlKAGA7GM05kywE99q8wiPHPZqrzhq3wXRga6PRD6A0kzT85Pb0AzYBVTBRpSrKyyr6M92Y6bnSBVp2BA==} engines: {node: '>=20.0.0'} + '@aws-sdk/util-user-agent-browser@3.972.0': + resolution: {integrity: sha512-eOLdkQyoRbDgioTS3Orr7iVsVEutJyMZxvyZ6WAF95IrF0kfWx5Rd/KXnfbnG/VKa2CvjZiitWfouLzfVEyvJA==} + '@aws-sdk/util-user-agent-browser@3.972.1': resolution: {integrity: sha512-IgF55NFmJX8d9Wql9M0nEpk2eYbuD8G4781FN4/fFgwTXBn86DvlZJuRWDCMcMqZymnBVX7HW9r+3r9ylqfW0w==} + '@aws-sdk/util-user-agent-node@3.972.0': + resolution: {integrity: sha512-GOy+AiSrE9kGiojiwlZvVVSXwylu4+fmP0MJfvras/MwP09RB/YtQuOVR1E0fKQc6OMwaTNBjgAbOEhxuWFbAw==} + engines: {node: '>=20.0.0'} + peerDependencies: + aws-crt: '>=1.0.0' + peerDependenciesMeta: + aws-crt: + optional: true + '@aws-sdk/util-user-agent-node@3.972.1': resolution: {integrity: sha512-oIs4JFcADzoZ0c915R83XvK2HltWupxNsXUIuZse2rgk7b97zTpkxaqXiH0h9ylh31qtgo/t8hp4tIqcsMrEbQ==} engines: {node: '>=20.0.0'} @@ -640,6 +716,10 @@ packages: aws-crt: optional: true + '@aws-sdk/xml-builder@3.972.0': + resolution: {integrity: sha512-POaGMcXnozzqBUyJM3HLUZ9GR6OKJWPGJEmhtTnxZXt8B6JcJ/6K3xRJ5H/j8oovVLz8Wg6vFxAHv8lvuASxMg==} + engines: {node: '>=20.0.0'} + '@aws-sdk/xml-builder@3.972.1': resolution: {integrity: sha512-6zZGlPOqn7Xb+25MAXGb1JhgvaC5HjZj6GzszuVrnEgbhvzBRFGKYemuHBV4bho+dtqeYKPgaZUv7/e80hIGNg==} engines: {node: '>=20.0.0'} @@ -2079,128 +2159,128 @@ packages: '@rolldown/pluginutils@1.0.0-rc.1': resolution: {integrity: sha512-UTBjtTxVOhodhzFVp/ayITaTETRHPUPYZPXQe0WU0wOgxghMojXxYjOiPOauKIYNWJAWS2fd7gJgGQK8GU8vDA==} - '@rollup/rollup-android-arm-eabi@4.56.0': - resolution: {integrity: sha512-LNKIPA5k8PF1+jAFomGe3qN3bbIgJe/IlpDBwuVjrDKrJhVWywgnJvflMt/zkbVNLFtF1+94SljYQS6e99klnw==} + '@rollup/rollup-android-arm-eabi@4.55.3': + resolution: {integrity: sha512-qyX8+93kK/7R5BEXPC2PjUt0+fS/VO2BVHjEHyIEWiYn88rcRBHmdLgoJjktBltgAf+NY7RfCGB1SoyKS/p9kg==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.56.0': - resolution: {integrity: sha512-lfbVUbelYqXlYiU/HApNMJzT1E87UPGvzveGg2h0ktUNlOCxKlWuJ9jtfvs1sKHdwU4fzY7Pl8sAl49/XaEk6Q==} + '@rollup/rollup-android-arm64@4.55.3': + resolution: {integrity: sha512-6sHrL42bjt5dHQzJ12Q4vMKfN+kUnZ0atHHnv4V0Wd9JMTk7FDzSY35+7qbz3ypQYMBPANbpGK7JpnWNnhGt8g==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.56.0': - resolution: {integrity: sha512-EgxD1ocWfhoD6xSOeEEwyE7tDvwTgZc8Bss7wCWe+uc7wO8G34HHCUH+Q6cHqJubxIAnQzAsyUsClt0yFLu06w==} + '@rollup/rollup-darwin-arm64@4.55.3': + resolution: {integrity: sha512-1ht2SpGIjEl2igJ9AbNpPIKzb1B5goXOcmtD0RFxnwNuMxqkR6AUaaErZz+4o+FKmzxcSNBOLrzsICZVNYa1Rw==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.56.0': - resolution: {integrity: sha512-1vXe1vcMOssb/hOF8iv52A7feWW2xnu+c8BV4t1F//m9QVLTfNVpEdja5ia762j/UEJe2Z1jAmEqZAK42tVW3g==} + '@rollup/rollup-darwin-x64@4.55.3': + resolution: {integrity: sha512-FYZ4iVunXxtT+CZqQoPVwPhH7549e/Gy7PIRRtq4t5f/vt54pX6eG9ebttRH6QSH7r/zxAFA4EZGlQ0h0FvXiA==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.56.0': - resolution: {integrity: sha512-bof7fbIlvqsyv/DtaXSck4VYQ9lPtoWNFCB/JY4snlFuJREXfZnm+Ej6yaCHfQvofJDXLDMTVxWscVSuQvVWUQ==} + '@rollup/rollup-freebsd-arm64@4.55.3': + resolution: {integrity: sha512-M/mwDCJ4wLsIgyxv2Lj7Len+UMHd4zAXu4GQ2UaCdksStglWhP61U3uowkaYBQBhVoNpwx5Hputo8eSqM7K82Q==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.56.0': - resolution: {integrity: sha512-KNa6lYHloW+7lTEkYGa37fpvPq+NKG/EHKM8+G/g9WDU7ls4sMqbVRV78J6LdNuVaeeK5WB9/9VAFbKxcbXKYg==} + '@rollup/rollup-freebsd-x64@4.55.3': + resolution: {integrity: sha512-5jZT2c7jBCrMegKYTYTpni8mg8y3uY8gzeq2ndFOANwNuC/xJbVAoGKR9LhMDA0H3nIhvaqUoBEuJoICBudFrA==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.56.0': - resolution: {integrity: sha512-E8jKK87uOvLrrLN28jnAAAChNq5LeCd2mGgZF+fGF5D507WlG/Noct3lP/QzQ6MrqJ5BCKNwI9ipADB6jyiq2A==} + '@rollup/rollup-linux-arm-gnueabihf@4.55.3': + resolution: {integrity: sha512-YeGUhkN1oA+iSPzzhEjVPS29YbViOr8s4lSsFaZKLHswgqP911xx25fPOyE9+khmN6W4VeM0aevbDp4kkEoHiA==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.56.0': - resolution: {integrity: sha512-jQosa5FMYF5Z6prEpTCCmzCXz6eKr/tCBssSmQGEeozA9tkRUty/5Vx06ibaOP9RCrW1Pvb8yp3gvZhHwTDsJw==} + '@rollup/rollup-linux-arm-musleabihf@4.55.3': + resolution: {integrity: sha512-eo0iOIOvcAlWB3Z3eh8pVM8hZ0oVkK3AjEM9nSrkSug2l15qHzF3TOwT0747omI6+CJJvl7drwZepT+re6Fy/w==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.56.0': - resolution: {integrity: sha512-uQVoKkrC1KGEV6udrdVahASIsaF8h7iLG0U0W+Xn14ucFwi6uS539PsAr24IEF9/FoDtzMeeJXJIBo5RkbNWvQ==} + '@rollup/rollup-linux-arm64-gnu@4.55.3': + resolution: {integrity: sha512-DJay3ep76bKUDImmn//W5SvpjRN5LmK/ntWyeJs/dcnwiiHESd3N4uteK9FDLf0S0W8E6Y0sVRXpOCoQclQqNg==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.56.0': - resolution: {integrity: sha512-vLZ1yJKLxhQLFKTs42RwTwa6zkGln+bnXc8ueFGMYmBTLfNu58sl5/eXyxRa2RarTkJbXl8TKPgfS6V5ijNqEA==} + '@rollup/rollup-linux-arm64-musl@4.55.3': + resolution: {integrity: sha512-BKKWQkY2WgJ5MC/ayvIJTHjy0JUGb5efaHCUiG/39sSUvAYRBaO3+/EK0AZT1RF3pSj86O24GLLik9mAYu0IJg==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loong64-gnu@4.56.0': - resolution: {integrity: sha512-FWfHOCub564kSE3xJQLLIC/hbKqHSVxy8vY75/YHHzWvbJL7aYJkdgwD/xGfUlL5UV2SB7otapLrcCj2xnF1dg==} + '@rollup/rollup-linux-loong64-gnu@4.55.3': + resolution: {integrity: sha512-Q9nVlWtKAG7ISW80OiZGxTr6rYtyDSkauHUtvkQI6TNOJjFvpj4gcH+KaJihqYInnAzEEUetPQubRwHef4exVg==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-loong64-musl@4.56.0': - resolution: {integrity: sha512-z1EkujxIh7nbrKL1lmIpqFTc/sr0u8Uk0zK/qIEFldbt6EDKWFk/pxFq3gYj4Bjn3aa9eEhYRlL3H8ZbPT1xvA==} + '@rollup/rollup-linux-loong64-musl@4.55.3': + resolution: {integrity: sha512-2H5LmhzrpC4fFRNwknzmmTvvyJPHwESoJgyReXeFoYYuIDfBhP29TEXOkCJE/KxHi27mj7wDUClNq78ue3QEBQ==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-ppc64-gnu@4.56.0': - resolution: {integrity: sha512-iNFTluqgdoQC7AIE8Q34R3AuPrJGJirj5wMUErxj22deOcY7XwZRaqYmB6ZKFHoVGqRcRd0mqO+845jAibKCkw==} + '@rollup/rollup-linux-ppc64-gnu@4.55.3': + resolution: {integrity: sha512-9S542V0ie9LCTznPYlvaeySwBeIEa7rDBgLHKZ5S9DBgcqdJYburabm8TqiqG6mrdTzfV5uttQRHcbKff9lWtA==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-ppc64-musl@4.56.0': - resolution: {integrity: sha512-MtMeFVlD2LIKjp2sE2xM2slq3Zxf9zwVuw0jemsxvh1QOpHSsSzfNOTH9uYW9i1MXFxUSMmLpeVeUzoNOKBaWg==} + '@rollup/rollup-linux-ppc64-musl@4.55.3': + resolution: {integrity: sha512-ukxw+YH3XXpcezLgbJeasgxyTbdpnNAkrIlFGDl7t+pgCxZ89/6n1a+MxlY7CegU+nDgrgdqDelPRNQ/47zs0g==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.56.0': - resolution: {integrity: sha512-in+v6wiHdzzVhYKXIk5U74dEZHdKN9KH0Q4ANHOTvyXPG41bajYRsy7a8TPKbYPl34hU7PP7hMVHRvv/5aCSew==} + '@rollup/rollup-linux-riscv64-gnu@4.55.3': + resolution: {integrity: sha512-Iauw9UsTTvlF++FhghFJjqYxyXdggXsOqGpFBylaRopVpcbfyIIsNvkf9oGwfgIcf57z3m8+/oSYTo6HutBFNw==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-musl@4.56.0': - resolution: {integrity: sha512-yni2raKHB8m9NQpI9fPVwN754mn6dHQSbDTwxdr9SE0ks38DTjLMMBjrwvB5+mXrX+C0npX0CVeCUcvvvD8CNQ==} + '@rollup/rollup-linux-riscv64-musl@4.55.3': + resolution: {integrity: sha512-3OqKAHSEQXKdq9mQ4eajqUgNIK27VZPW3I26EP8miIzuKzCJ3aW3oEn2pzF+4/Hj/Moc0YDsOtBgT5bZ56/vcA==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.56.0': - resolution: {integrity: sha512-zhLLJx9nQPu7wezbxt2ut+CI4YlXi68ndEve16tPc/iwoylWS9B3FxpLS2PkmfYgDQtosah07Mj9E0khc3Y+vQ==} + '@rollup/rollup-linux-s390x-gnu@4.55.3': + resolution: {integrity: sha512-0CM8dSVzVIaqMcXIFej8zZrSFLnGrAE8qlNbbHfTw1EEPnFTg1U1ekI0JdzjPyzSfUsHWtodilQQG/RA55berA==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.56.0': - resolution: {integrity: sha512-MVC6UDp16ZSH7x4rtuJPAEoE1RwS8N4oK9DLHy3FTEdFoUTCFVzMfJl/BVJ330C+hx8FfprA5Wqx4FhZXkj2Kw==} + '@rollup/rollup-linux-x64-gnu@4.55.3': + resolution: {integrity: sha512-+fgJE12FZMIgBaKIAGd45rxf+5ftcycANJRWk8Vz0NnMTM5rADPGuRFTYar+Mqs560xuART7XsX2lSACa1iOmQ==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.56.0': - resolution: {integrity: sha512-ZhGH1eA4Qv0lxaV00azCIS1ChedK0V32952Md3FtnxSqZTBTd6tgil4nZT5cU8B+SIw3PFYkvyR4FKo2oyZIHA==} + '@rollup/rollup-linux-x64-musl@4.55.3': + resolution: {integrity: sha512-tMD7NnbAolWPzQlJQJjVFh/fNH3K/KnA7K8gv2dJWCwwnaK6DFCYST1QXYWfu5V0cDwarWC8Sf/cfMHniNq21A==} cpu: [x64] os: [linux] - '@rollup/rollup-openbsd-x64@4.56.0': - resolution: {integrity: sha512-O16XcmyDeFI9879pEcmtWvD/2nyxR9mF7Gs44lf1vGGx8Vg2DRNx11aVXBEqOQhWb92WN4z7fW/q4+2NYzCbBA==} + '@rollup/rollup-openbsd-x64@4.55.3': + resolution: {integrity: sha512-u5KsqxOxjEeIbn7bUK1MPM34jrnPwjeqgyin4/N6e/KzXKfpE9Mi0nCxcQjaM9lLmPcHmn/xx1yOjgTMtu1jWQ==} cpu: [x64] os: [openbsd] - '@rollup/rollup-openharmony-arm64@4.56.0': - resolution: {integrity: sha512-LhN/Reh+7F3RCgQIRbgw8ZMwUwyqJM+8pXNT6IIJAqm2IdKkzpCh/V9EdgOMBKuebIrzswqy4ATlrDgiOwbRcQ==} + '@rollup/rollup-openharmony-arm64@4.55.3': + resolution: {integrity: sha512-vo54aXwjpTtsAnb3ca7Yxs9t2INZg7QdXN/7yaoG7nPGbOBXYXQY41Km+S1Ov26vzOAzLcAjmMdjyEqS1JkVhw==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.56.0': - resolution: {integrity: sha512-kbFsOObXp3LBULg1d3JIUQMa9Kv4UitDmpS+k0tinPBz3watcUiV2/LUDMMucA6pZO3WGE27P7DsfaN54l9ing==} + '@rollup/rollup-win32-arm64-msvc@4.55.3': + resolution: {integrity: sha512-HI+PIVZ+m+9AgpnY3pt6rinUdRYrGHvmVdsNQ4odNqQ/eRF78DVpMR7mOq7nW06QxpczibwBmeQzB68wJ+4W4A==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.56.0': - resolution: {integrity: sha512-vSSgny54D6P4vf2izbtFm/TcWYedw7f8eBrOiGGecyHyQB9q4Kqentjaj8hToe+995nob/Wv48pDqL5a62EWtg==} + '@rollup/rollup-win32-ia32-msvc@4.55.3': + resolution: {integrity: sha512-vRByotbdMo3Wdi+8oC2nVxtc3RkkFKrGaok+a62AT8lz/YBuQjaVYAS5Zcs3tPzW43Vsf9J0wehJbUY5xRSekA==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.56.0': - resolution: {integrity: sha512-FeCnkPCTHQJFbiGG49KjV5YGW/8b9rrXAM2Mz2kiIoktq2qsJxRD5giEMEOD2lPdgs72upzefaUvS+nc8E3UzQ==} + '@rollup/rollup-win32-x64-gnu@4.55.3': + resolution: {integrity: sha512-POZHq7UeuzMJljC5NjKi8vKMFN6/5EOqcX1yGntNLp7rUTpBAXQ1hW8kWPFxYLv07QMcNM75xqVLGPWQq6TKFA==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.56.0': - resolution: {integrity: sha512-H8AE9Ur/t0+1VXujj90w0HrSOuv0Nq9r1vSZF2t5km20NTfosQsGGUXDaKdQZzwuLts7IyL1fYT4hM95TI9c4g==} + '@rollup/rollup-win32-x64-msvc@4.55.3': + resolution: {integrity: sha512-aPFONczE4fUFKNXszdvnd2GqKEYQdV5oEsIbKPujJmWlCI9zEsv1Otig8RKK+X9bed9gFUN6LAeN4ZcNuu4zjg==} cpu: [x64] os: [win32] @@ -2256,6 +2336,10 @@ packages: resolution: {integrity: sha512-qJpzYC64kaj3S0fueiu3kXm8xPrR3PcXDPEgnaNMRn0EjNSZFoFjvbUp0YUDsRhN1CB90EnHJtbxWKevnH99UQ==} engines: {node: '>=18.0.0'} + '@smithy/core@3.21.0': + resolution: {integrity: sha512-bg2TfzgsERyETAxc/Ims/eJX8eAnIeTi4r4LHpMpfF/2NyO6RsWis0rjKcCPaGksljmOb23BZRiCeT/3NvwkXw==} + engines: {node: '>=18.0.0'} + '@smithy/core@3.21.1': resolution: {integrity: sha512-NUH8R4O6FkN8HKMojzbGg/5pNjsfTjlMmeFclyPfPaXXUrbr5TzhWgbf7t92wfrpCHRgpjyz7ffASIS3wX28aA==} engines: {node: '>=18.0.0'} @@ -2308,10 +2392,18 @@ packages: resolution: {integrity: sha512-RO0jeoaYAB1qBRhfVyq0pMgBoUK34YEJxVxyjOWYZiOKOq2yMZ4MnVXMZCUDenpozHue207+9P5ilTV1zeda0A==} engines: {node: '>=18.0.0'} + '@smithy/middleware-endpoint@4.4.10': + resolution: {integrity: sha512-kwWpNltpxrvPabnjEFvwSmA+66l6s2ReCvgVSzW/z92LU4T28fTdgZ18IdYRYOrisu2NMQ0jUndRScbO65A/zg==} + engines: {node: '>=18.0.0'} + '@smithy/middleware-endpoint@4.4.11': resolution: {integrity: sha512-/WqsrycweGGfb9sSzME4CrsuayjJF6BueBmkKlcbeU5q18OhxRrvvKlmfw3tpDsK5ilx2XUJvoukwxHB0nHs/Q==} engines: {node: '>=18.0.0'} + '@smithy/middleware-retry@4.4.26': + resolution: {integrity: sha512-ozZMoTAr+B2aVYfLYfkssFvc8ZV3p/vLpVQ7/k277xxUOA9ykSPe5obL2j6yHfbdrM/SZV7qj0uk/hSqavHrLw==} + engines: {node: '>=18.0.0'} + '@smithy/middleware-retry@4.4.27': resolution: {integrity: sha512-xFUYCGRVsfgiN5EjsJJSzih9+yjStgMTCLANPlf0LVQkPDYCe0hz97qbdTZosFOiYlGBlHYityGRxrQ/hxhfVQ==} engines: {node: '>=18.0.0'} @@ -2360,6 +2452,10 @@ packages: resolution: {integrity: sha512-6A4vdGj7qKNRF16UIcO8HhHjKW27thsxYci+5r/uVRkdcBEkOEiY8OMPuydLX4QHSrJqGHPJzPRwwVTqbLZJhg==} engines: {node: '>=18.0.0'} + '@smithy/smithy-client@4.10.11': + resolution: {integrity: sha512-6o804SCyHGMXAb5mFJ+iTy9kVKv7F91a9szN0J+9X6p8A0NrdpUxdaC57aye2ipQkP2C4IAqETEpGZ0Zj77Haw==} + engines: {node: '>=18.0.0'} + '@smithy/smithy-client@4.10.12': resolution: {integrity: sha512-VKO/HKoQ5OrSHW6AJUmEnUKeXI1/5LfCwO9cwyao7CmLvGnZeM1i36Lyful3LK1XU7HwTVieTqO1y2C/6t3qtA==} engines: {node: '>=18.0.0'} @@ -2396,10 +2492,18 @@ packages: resolution: {integrity: sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q==} engines: {node: '>=18.0.0'} + '@smithy/util-defaults-mode-browser@4.3.25': + resolution: {integrity: sha512-8ugoNMtss2dJHsXnqsibGPqoaafvWJPACmYKxJ4E6QWaDrixsAemmiMMAVbvwYadjR0H9G2+AlzsInSzRi8PSw==} + engines: {node: '>=18.0.0'} + '@smithy/util-defaults-mode-browser@4.3.26': resolution: {integrity: sha512-vva0dzYUTgn7DdE0uaha10uEdAgmdLnNFowKFjpMm6p2R0XDk5FHPX3CBJLzWQkQXuEprsb0hGz9YwbicNWhjw==} engines: {node: '>=18.0.0'} + '@smithy/util-defaults-mode-node@4.2.28': + resolution: {integrity: sha512-mjUdcP8h3E0K/XvNMi9oBXRV3DMCzeRiYIieZ1LQ7jq5tu6GH/GTWym7a1xIIE0pKSoLcpGsaImuQhGPSIJzAA==} + engines: {node: '>=18.0.0'} + '@smithy/util-defaults-mode-node@4.2.29': resolution: {integrity: sha512-c6D7IUBsZt/aNnTBHMTf+OVh+h/JcxUUgfTcIJaWRe6zhOum1X+pNKSZtZ+7fbOn5I99XVFtmrnXKv8yHHErTQ==} engines: {node: '>=18.0.0'} @@ -2446,17 +2550,17 @@ packages: '@swc/helpers@0.5.18': resolution: {integrity: sha512-TXTnIcNJQEKwThMMqBXsZ4VGAza6bvN4pa41Rkqoio6QBKMvo+5lexeTMScGCIxtzgQJzElcvIltani+adC5PQ==} - '@thi.ng/bitstream@2.4.39': - resolution: {integrity: sha512-VhdYiBqoSpCXil4BGMgHr6fS0i1uGWTqE2oszd453bDmAdthc24VUvzZoKu7GorLopOJwrnkhpcAl5004hpYaw==} + '@thi.ng/bitstream@2.4.38': + resolution: {integrity: sha512-Y5vpB2w9zS8a6FE1G3H8QhQYYvT3qmOpXOYmKMCZKwyQXY3XCuZDFeIIm1b23CyjtED5vmdr6+E6HZaAW1GNsA==} engines: {node: '>=18'} - '@thi.ng/errors@2.6.2': - resolution: {integrity: sha512-YN89WmgOhAnK5/2gI9LckplmQCYld6adPUgjTo8DozgutAqF7zzYfuzFrCGztbT6zBwaCWUpPyQboiu+OtZIvA==} + '@thi.ng/errors@2.6.1': + resolution: {integrity: sha512-5kkJ1+JK6OInYMnRXtiJ6qZMt2zNqEuw0ZNwU8bFPfxF3yiWD5tcDNVLwE4EsMm8cGwH1K0h0TI5HIPfHSUWow==} engines: {node: '>=18'} - '@tinyhttp/content-disposition@2.2.3': - resolution: {integrity: sha512-0nSvOgFHvq0a15+pZAdbAyHUk0+AGLX6oyo45b7fPdgWdPfHA19IfgUKRECYT0aw86ZP6ZDDLxGQ7FEA1fAVOg==} - engines: {node: '>=12.17.0'} + '@tinyhttp/content-disposition@2.2.2': + resolution: {integrity: sha512-crXw1txzrS36huQOyQGYFvhTeLeG0Si1xu+/l6kXUVYpE0TjFjEZRqTbuadQLfKGZ0jaI+jJoRyqaWwxOSHW2g==} + engines: {node: '>=12.20.0'} '@tokenizer/inflate@0.4.1': resolution: {integrity: sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==} @@ -3001,8 +3105,8 @@ packages: class-variance-authority@0.7.1: resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} - clawdbot@2026.1.23: - resolution: {integrity: sha512-w8RjScbxj3YbJYtcB0GBITqmyUYegVbBXDgu/zRxB4AB/SEErR6BNWGeZDWQNFaMftIyJhjttACxSd8G20aREA==} + clawdbot@2026.1.23-1: + resolution: {integrity: sha512-t51ks5bnTRQNCzoTunUJaoeMjamvP3zP5EyyadmI34kXYGIbWcCx242w5XMr5h4sLSw59nBw3lJ74vErWDsz9w==} engines: {node: '>=22.12.0'} hasBin: true @@ -3812,8 +3916,8 @@ packages: jwa@2.0.1: resolution: {integrity: sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==} - jwks-rsa@3.2.2: - resolution: {integrity: sha512-BqTyEDV+lS8F2trk3A+qJnxV5Q9EqKCBJOPti3W97r7qTympCZjb7h2X6f2kc+0K3rsSTY1/6YG2eaXKoj497w==} + jwks-rsa@3.2.1: + resolution: {integrity: sha512-r7QdN9TdqI6aFDFZt+GpAqj5yRtMUv23rL2I01i7B8P2/g8F0ioEN6VeSObKgTLs4GmmNJwP9J7Fyp/AYDBGRg==} engines: {node: '>=14'} jws@4.0.1: @@ -4181,8 +4285,8 @@ packages: resolution: {integrity: sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==} engines: {node: ^18 || ^20 || >= 21} - node-api-headers@1.8.0: - resolution: {integrity: sha512-jfnmiKWjRAGbdD1yQS28bknFM1tbHC1oucyuMPjmkEs+kpiu76aRs40WlTmBmyEgzDM76ge1DQ7XJ3R5deiVjQ==} + node-api-headers@1.7.0: + resolution: {integrity: sha512-uJMGdkhVwu9+I3UsVvI3KW6ICAy/yDfsu5Br9rSnTtY3WpoaComXvKloiV5wtx0Md2rn0B9n29Ys2WMNwWxj9A==} node-domexception@1.0.0: resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} @@ -4698,8 +4802,8 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} hasBin: true - rollup@4.56.0: - resolution: {integrity: sha512-9FwVqlgUHzbXtDg9RCMgodF3Ua4Na6Gau+Sdt9vyCN4RhHfVKX2DCHy3BjMLTDd47ITDhYAnTwGulWTblJSDLg==} + rollup@4.55.3: + resolution: {integrity: sha512-y9yUpfQvetAjiDLtNMf1hL9NXchIJgWt6zIKeoB+tCd3npX08Eqfzg60V9DhIGVMtQ0AlMkFw5xa+AQ37zxnAA==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -5367,7 +5471,7 @@ snapshots: '@aws-crypto/crc32@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.973.0 + '@aws-sdk/types': 3.972.0 tslib: 2.8.1 '@aws-crypto/sha256-browser@5.2.0': @@ -5375,7 +5479,7 @@ snapshots: '@aws-crypto/sha256-js': 5.2.0 '@aws-crypto/supports-web-crypto': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.973.0 + '@aws-sdk/types': 3.972.0 '@aws-sdk/util-locate-window': 3.965.3 '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 @@ -5383,7 +5487,7 @@ snapshots: '@aws-crypto/sha256-js@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.973.0 + '@aws-sdk/types': 3.972.0 tslib: 2.8.1 '@aws-crypto/supports-web-crypto@5.2.0': @@ -5392,31 +5496,31 @@ snapshots: '@aws-crypto/util@5.2.0': dependencies: - '@aws-sdk/types': 3.973.0 + '@aws-sdk/types': 3.972.0 '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 - '@aws-sdk/client-bedrock-runtime@3.975.0': + '@aws-sdk/client-bedrock-runtime@3.972.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.973.1 - '@aws-sdk/credential-provider-node': 3.972.1 - '@aws-sdk/eventstream-handler-node': 3.972.1 - '@aws-sdk/middleware-eventstream': 3.972.1 - '@aws-sdk/middleware-host-header': 3.972.1 - '@aws-sdk/middleware-logger': 3.972.1 - '@aws-sdk/middleware-recursion-detection': 3.972.1 - '@aws-sdk/middleware-user-agent': 3.972.2 - '@aws-sdk/middleware-websocket': 3.972.1 - '@aws-sdk/region-config-resolver': 3.972.1 - '@aws-sdk/token-providers': 3.975.0 - '@aws-sdk/types': 3.973.0 + '@aws-sdk/core': 3.972.0 + '@aws-sdk/credential-provider-node': 3.972.0 + '@aws-sdk/eventstream-handler-node': 3.972.0 + '@aws-sdk/middleware-eventstream': 3.972.0 + '@aws-sdk/middleware-host-header': 3.972.0 + '@aws-sdk/middleware-logger': 3.972.0 + '@aws-sdk/middleware-recursion-detection': 3.972.0 + '@aws-sdk/middleware-user-agent': 3.972.0 + '@aws-sdk/middleware-websocket': 3.972.0 + '@aws-sdk/region-config-resolver': 3.972.0 + '@aws-sdk/token-providers': 3.972.0 + '@aws-sdk/types': 3.972.0 '@aws-sdk/util-endpoints': 3.972.0 - '@aws-sdk/util-user-agent-browser': 3.972.1 - '@aws-sdk/util-user-agent-node': 3.972.1 + '@aws-sdk/util-user-agent-browser': 3.972.0 + '@aws-sdk/util-user-agent-node': 3.972.0 '@smithy/config-resolver': 4.4.6 - '@smithy/core': 3.21.1 + '@smithy/core': 3.21.0 '@smithy/eventstream-serde-browser': 4.2.8 '@smithy/eventstream-serde-config-resolver': 4.3.8 '@smithy/eventstream-serde-node': 4.2.8 @@ -5424,21 +5528,21 @@ snapshots: '@smithy/hash-node': 4.2.8 '@smithy/invalid-dependency': 4.2.8 '@smithy/middleware-content-length': 4.2.8 - '@smithy/middleware-endpoint': 4.4.11 - '@smithy/middleware-retry': 4.4.27 + '@smithy/middleware-endpoint': 4.4.10 + '@smithy/middleware-retry': 4.4.26 '@smithy/middleware-serde': 4.2.9 '@smithy/middleware-stack': 4.2.8 '@smithy/node-config-provider': 4.3.8 '@smithy/node-http-handler': 4.4.8 '@smithy/protocol-http': 5.3.8 - '@smithy/smithy-client': 4.10.12 + '@smithy/smithy-client': 4.10.11 '@smithy/types': 4.12.0 '@smithy/url-parser': 4.2.8 '@smithy/util-base64': 4.3.0 '@smithy/util-body-length-browser': 4.2.0 '@smithy/util-body-length-node': 4.2.1 - '@smithy/util-defaults-mode-browser': 4.3.26 - '@smithy/util-defaults-mode-node': 4.2.29 + '@smithy/util-defaults-mode-browser': 4.3.25 + '@smithy/util-defaults-mode-node': 4.2.28 '@smithy/util-endpoints': 3.2.8 '@smithy/util-middleware': 4.2.8 '@smithy/util-retry': 4.2.8 @@ -5493,6 +5597,49 @@ snapshots: transitivePeerDependencies: - aws-crt + '@aws-sdk/client-sso@3.972.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.972.0 + '@aws-sdk/middleware-host-header': 3.972.0 + '@aws-sdk/middleware-logger': 3.972.0 + '@aws-sdk/middleware-recursion-detection': 3.972.0 + '@aws-sdk/middleware-user-agent': 3.972.0 + '@aws-sdk/region-config-resolver': 3.972.0 + '@aws-sdk/types': 3.972.0 + '@aws-sdk/util-endpoints': 3.972.0 + '@aws-sdk/util-user-agent-browser': 3.972.0 + '@aws-sdk/util-user-agent-node': 3.972.0 + '@smithy/config-resolver': 4.4.6 + '@smithy/core': 3.21.0 + '@smithy/fetch-http-handler': 5.3.9 + '@smithy/hash-node': 4.2.8 + '@smithy/invalid-dependency': 4.2.8 + '@smithy/middleware-content-length': 4.2.8 + '@smithy/middleware-endpoint': 4.4.10 + '@smithy/middleware-retry': 4.4.26 + '@smithy/middleware-serde': 4.2.9 + '@smithy/middleware-stack': 4.2.8 + '@smithy/node-config-provider': 4.3.8 + '@smithy/node-http-handler': 4.4.8 + '@smithy/protocol-http': 5.3.8 + '@smithy/smithy-client': 4.10.11 + '@smithy/types': 4.12.0 + '@smithy/url-parser': 4.2.8 + '@smithy/util-base64': 4.3.0 + '@smithy/util-body-length-browser': 4.2.0 + '@smithy/util-body-length-node': 4.2.1 + '@smithy/util-defaults-mode-browser': 4.3.25 + '@smithy/util-defaults-mode-node': 4.2.28 + '@smithy/util-endpoints': 3.2.8 + '@smithy/util-middleware': 4.2.8 + '@smithy/util-retry': 4.2.8 + '@smithy/util-utf8': 4.2.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + '@aws-sdk/client-sso@3.974.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 @@ -5536,6 +5683,22 @@ snapshots: transitivePeerDependencies: - aws-crt + '@aws-sdk/core@3.972.0': + dependencies: + '@aws-sdk/types': 3.972.0 + '@aws-sdk/xml-builder': 3.972.0 + '@smithy/core': 3.21.0 + '@smithy/node-config-provider': 4.3.8 + '@smithy/property-provider': 4.2.8 + '@smithy/protocol-http': 5.3.8 + '@smithy/signature-v4': 5.3.8 + '@smithy/smithy-client': 4.10.11 + '@smithy/types': 4.12.0 + '@smithy/util-base64': 4.3.0 + '@smithy/util-middleware': 4.2.8 + '@smithy/util-utf8': 4.2.0 + tslib: 2.8.1 + '@aws-sdk/core@3.973.1': dependencies: '@aws-sdk/types': 3.973.0 @@ -5552,6 +5715,14 @@ snapshots: '@smithy/util-utf8': 4.2.0 tslib: 2.8.1 + '@aws-sdk/credential-provider-env@3.972.0': + dependencies: + '@aws-sdk/core': 3.972.0 + '@aws-sdk/types': 3.972.0 + '@smithy/property-provider': 4.2.8 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + '@aws-sdk/credential-provider-env@3.972.1': dependencies: '@aws-sdk/core': 3.973.1 @@ -5560,6 +5731,19 @@ snapshots: '@smithy/types': 4.12.0 tslib: 2.8.1 + '@aws-sdk/credential-provider-http@3.972.0': + dependencies: + '@aws-sdk/core': 3.972.0 + '@aws-sdk/types': 3.972.0 + '@smithy/fetch-http-handler': 5.3.9 + '@smithy/node-http-handler': 4.4.8 + '@smithy/property-provider': 4.2.8 + '@smithy/protocol-http': 5.3.8 + '@smithy/smithy-client': 4.10.11 + '@smithy/types': 4.12.0 + '@smithy/util-stream': 4.5.10 + tslib: 2.8.1 + '@aws-sdk/credential-provider-http@3.972.2': dependencies: '@aws-sdk/core': 3.973.1 @@ -5573,6 +5757,25 @@ snapshots: '@smithy/util-stream': 4.5.10 tslib: 2.8.1 + '@aws-sdk/credential-provider-ini@3.972.0': + dependencies: + '@aws-sdk/core': 3.972.0 + '@aws-sdk/credential-provider-env': 3.972.0 + '@aws-sdk/credential-provider-http': 3.972.0 + '@aws-sdk/credential-provider-login': 3.972.0 + '@aws-sdk/credential-provider-process': 3.972.0 + '@aws-sdk/credential-provider-sso': 3.972.0 + '@aws-sdk/credential-provider-web-identity': 3.972.0 + '@aws-sdk/nested-clients': 3.972.0 + '@aws-sdk/types': 3.972.0 + '@smithy/credential-provider-imds': 4.2.8 + '@smithy/property-provider': 4.2.8 + '@smithy/shared-ini-file-loader': 4.4.3 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + '@aws-sdk/credential-provider-ini@3.972.1': dependencies: '@aws-sdk/core': 3.973.1 @@ -5592,6 +5795,19 @@ snapshots: transitivePeerDependencies: - aws-crt + '@aws-sdk/credential-provider-login@3.972.0': + dependencies: + '@aws-sdk/core': 3.972.0 + '@aws-sdk/nested-clients': 3.972.0 + '@aws-sdk/types': 3.972.0 + '@smithy/property-provider': 4.2.8 + '@smithy/protocol-http': 5.3.8 + '@smithy/shared-ini-file-loader': 4.4.3 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + '@aws-sdk/credential-provider-login@3.972.1': dependencies: '@aws-sdk/core': 3.973.1 @@ -5605,6 +5821,23 @@ snapshots: transitivePeerDependencies: - aws-crt + '@aws-sdk/credential-provider-node@3.972.0': + dependencies: + '@aws-sdk/credential-provider-env': 3.972.0 + '@aws-sdk/credential-provider-http': 3.972.0 + '@aws-sdk/credential-provider-ini': 3.972.0 + '@aws-sdk/credential-provider-process': 3.972.0 + '@aws-sdk/credential-provider-sso': 3.972.0 + '@aws-sdk/credential-provider-web-identity': 3.972.0 + '@aws-sdk/types': 3.972.0 + '@smithy/credential-provider-imds': 4.2.8 + '@smithy/property-provider': 4.2.8 + '@smithy/shared-ini-file-loader': 4.4.3 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + '@aws-sdk/credential-provider-node@3.972.1': dependencies: '@aws-sdk/credential-provider-env': 3.972.1 @@ -5622,6 +5855,15 @@ snapshots: transitivePeerDependencies: - aws-crt + '@aws-sdk/credential-provider-process@3.972.0': + dependencies: + '@aws-sdk/core': 3.972.0 + '@aws-sdk/types': 3.972.0 + '@smithy/property-provider': 4.2.8 + '@smithy/shared-ini-file-loader': 4.4.3 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + '@aws-sdk/credential-provider-process@3.972.1': dependencies: '@aws-sdk/core': 3.973.1 @@ -5631,6 +5873,19 @@ snapshots: '@smithy/types': 4.12.0 tslib: 2.8.1 + '@aws-sdk/credential-provider-sso@3.972.0': + dependencies: + '@aws-sdk/client-sso': 3.972.0 + '@aws-sdk/core': 3.972.0 + '@aws-sdk/token-providers': 3.972.0 + '@aws-sdk/types': 3.972.0 + '@smithy/property-provider': 4.2.8 + '@smithy/shared-ini-file-loader': 4.4.3 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + '@aws-sdk/credential-provider-sso@3.972.1': dependencies: '@aws-sdk/client-sso': 3.974.0 @@ -5644,6 +5899,18 @@ snapshots: transitivePeerDependencies: - aws-crt + '@aws-sdk/credential-provider-web-identity@3.972.0': + dependencies: + '@aws-sdk/core': 3.972.0 + '@aws-sdk/nested-clients': 3.972.0 + '@aws-sdk/types': 3.972.0 + '@smithy/property-provider': 4.2.8 + '@smithy/shared-ini-file-loader': 4.4.3 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + '@aws-sdk/credential-provider-web-identity@3.972.1': dependencies: '@aws-sdk/core': 3.973.1 @@ -5656,16 +5923,23 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/eventstream-handler-node@3.972.1': + '@aws-sdk/eventstream-handler-node@3.972.0': dependencies: - '@aws-sdk/types': 3.973.0 + '@aws-sdk/types': 3.972.0 '@smithy/eventstream-codec': 4.2.8 '@smithy/types': 4.12.0 tslib: 2.8.1 - '@aws-sdk/middleware-eventstream@3.972.1': + '@aws-sdk/middleware-eventstream@3.972.0': dependencies: - '@aws-sdk/types': 3.973.0 + '@aws-sdk/types': 3.972.0 + '@smithy/protocol-http': 5.3.8 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-host-header@3.972.0': + dependencies: + '@aws-sdk/types': 3.972.0 '@smithy/protocol-http': 5.3.8 '@smithy/types': 4.12.0 tslib: 2.8.1 @@ -5677,12 +5951,26 @@ snapshots: '@smithy/types': 4.12.0 tslib: 2.8.1 + '@aws-sdk/middleware-logger@3.972.0': + dependencies: + '@aws-sdk/types': 3.972.0 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + '@aws-sdk/middleware-logger@3.972.1': dependencies: '@aws-sdk/types': 3.973.0 '@smithy/types': 4.12.0 tslib: 2.8.1 + '@aws-sdk/middleware-recursion-detection@3.972.0': + dependencies: + '@aws-sdk/types': 3.972.0 + '@aws/lambda-invoke-store': 0.2.3 + '@smithy/protocol-http': 5.3.8 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + '@aws-sdk/middleware-recursion-detection@3.972.1': dependencies: '@aws-sdk/types': 3.973.0 @@ -5691,6 +5979,16 @@ snapshots: '@smithy/types': 4.12.0 tslib: 2.8.1 + '@aws-sdk/middleware-user-agent@3.972.0': + dependencies: + '@aws-sdk/core': 3.972.0 + '@aws-sdk/types': 3.972.0 + '@aws-sdk/util-endpoints': 3.972.0 + '@smithy/core': 3.21.0 + '@smithy/protocol-http': 5.3.8 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + '@aws-sdk/middleware-user-agent@3.972.2': dependencies: '@aws-sdk/core': 3.973.1 @@ -5701,10 +5999,10 @@ snapshots: '@smithy/types': 4.12.0 tslib: 2.8.1 - '@aws-sdk/middleware-websocket@3.972.1': + '@aws-sdk/middleware-websocket@3.972.0': dependencies: - '@aws-sdk/types': 3.973.0 - '@aws-sdk/util-format-url': 3.972.1 + '@aws-sdk/types': 3.972.0 + '@aws-sdk/util-format-url': 3.972.0 '@smithy/eventstream-codec': 4.2.8 '@smithy/eventstream-serde-browser': 4.2.8 '@smithy/fetch-http-handler': 5.3.9 @@ -5714,6 +6012,49 @@ snapshots: '@smithy/util-hex-encoding': 4.2.0 tslib: 2.8.1 + '@aws-sdk/nested-clients@3.972.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.972.0 + '@aws-sdk/middleware-host-header': 3.972.0 + '@aws-sdk/middleware-logger': 3.972.0 + '@aws-sdk/middleware-recursion-detection': 3.972.0 + '@aws-sdk/middleware-user-agent': 3.972.0 + '@aws-sdk/region-config-resolver': 3.972.0 + '@aws-sdk/types': 3.972.0 + '@aws-sdk/util-endpoints': 3.972.0 + '@aws-sdk/util-user-agent-browser': 3.972.0 + '@aws-sdk/util-user-agent-node': 3.972.0 + '@smithy/config-resolver': 4.4.6 + '@smithy/core': 3.21.0 + '@smithy/fetch-http-handler': 5.3.9 + '@smithy/hash-node': 4.2.8 + '@smithy/invalid-dependency': 4.2.8 + '@smithy/middleware-content-length': 4.2.8 + '@smithy/middleware-endpoint': 4.4.10 + '@smithy/middleware-retry': 4.4.26 + '@smithy/middleware-serde': 4.2.9 + '@smithy/middleware-stack': 4.2.8 + '@smithy/node-config-provider': 4.3.8 + '@smithy/node-http-handler': 4.4.8 + '@smithy/protocol-http': 5.3.8 + '@smithy/smithy-client': 4.10.11 + '@smithy/types': 4.12.0 + '@smithy/url-parser': 4.2.8 + '@smithy/util-base64': 4.3.0 + '@smithy/util-body-length-browser': 4.2.0 + '@smithy/util-body-length-node': 4.2.1 + '@smithy/util-defaults-mode-browser': 4.3.25 + '@smithy/util-defaults-mode-node': 4.2.28 + '@smithy/util-endpoints': 3.2.8 + '@smithy/util-middleware': 4.2.8 + '@smithy/util-retry': 4.2.8 + '@smithy/util-utf8': 4.2.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + '@aws-sdk/nested-clients@3.974.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 @@ -5800,6 +6141,14 @@ snapshots: transitivePeerDependencies: - aws-crt + '@aws-sdk/region-config-resolver@3.972.0': + dependencies: + '@aws-sdk/types': 3.972.0 + '@smithy/config-resolver': 4.4.6 + '@smithy/node-config-provider': 4.3.8 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + '@aws-sdk/region-config-resolver@3.972.1': dependencies: '@aws-sdk/types': 3.973.0 @@ -5808,6 +6157,18 @@ snapshots: '@smithy/types': 4.12.0 tslib: 2.8.1 + '@aws-sdk/token-providers@3.972.0': + dependencies: + '@aws-sdk/core': 3.972.0 + '@aws-sdk/nested-clients': 3.972.0 + '@aws-sdk/types': 3.972.0 + '@smithy/property-provider': 4.2.8 + '@smithy/shared-ini-file-loader': 4.4.3 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + '@aws-sdk/token-providers@3.974.0': dependencies: '@aws-sdk/core': 3.973.1 @@ -5850,9 +6211,9 @@ snapshots: '@smithy/util-endpoints': 3.2.8 tslib: 2.8.1 - '@aws-sdk/util-format-url@3.972.1': + '@aws-sdk/util-format-url@3.972.0': dependencies: - '@aws-sdk/types': 3.973.0 + '@aws-sdk/types': 3.972.0 '@smithy/querystring-builder': 4.2.8 '@smithy/types': 4.12.0 tslib: 2.8.1 @@ -5861,6 +6222,13 @@ snapshots: dependencies: tslib: 2.8.1 + '@aws-sdk/util-user-agent-browser@3.972.0': + dependencies: + '@aws-sdk/types': 3.972.0 + '@smithy/types': 4.12.0 + bowser: 2.13.1 + tslib: 2.8.1 + '@aws-sdk/util-user-agent-browser@3.972.1': dependencies: '@aws-sdk/types': 3.973.0 @@ -5868,6 +6236,14 @@ snapshots: bowser: 2.13.1 tslib: 2.8.1 + '@aws-sdk/util-user-agent-node@3.972.0': + dependencies: + '@aws-sdk/middleware-user-agent': 3.972.0 + '@aws-sdk/types': 3.972.0 + '@smithy/node-config-provider': 4.3.8 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + '@aws-sdk/util-user-agent-node@3.972.1': dependencies: '@aws-sdk/middleware-user-agent': 3.972.2 @@ -5876,6 +6252,12 @@ snapshots: '@smithy/types': 4.12.0 tslib: 2.8.1 + '@aws-sdk/xml-builder@3.972.0': + dependencies: + '@smithy/types': 4.12.0 + fast-xml-parser: 5.2.5 + tslib: 2.8.1 + '@aws-sdk/xml-builder@3.972.1': dependencies: '@smithy/types': 4.12.0 @@ -6451,7 +6833,7 @@ snapshots: '@mariozechner/pi-ai@0.49.3(ws@8.19.0)(zod@4.3.6)': dependencies: '@anthropic-ai/sdk': 0.71.2(zod@4.3.6) - '@aws-sdk/client-bedrock-runtime': 3.975.0 + '@aws-sdk/client-bedrock-runtime': 3.972.0 '@google/genai': 1.34.0 '@mistralai/mistralai': 1.10.0 '@sinclair/typebox': 0.34.47 @@ -6541,7 +6923,7 @@ snapshots: '@microsoft/agents-activity': 1.2.2 axios: 1.13.2(debug@4.4.3) jsonwebtoken: 9.0.3 - jwks-rsa: 3.2.2 + jwks-rsa: 3.2.1 object-path: 0.11.8 transitivePeerDependencies: - debug @@ -7262,79 +7644,79 @@ snapshots: '@rolldown/pluginutils@1.0.0-rc.1': {} - '@rollup/rollup-android-arm-eabi@4.56.0': + '@rollup/rollup-android-arm-eabi@4.55.3': optional: true - '@rollup/rollup-android-arm64@4.56.0': + '@rollup/rollup-android-arm64@4.55.3': optional: true - '@rollup/rollup-darwin-arm64@4.56.0': + '@rollup/rollup-darwin-arm64@4.55.3': optional: true - '@rollup/rollup-darwin-x64@4.56.0': + '@rollup/rollup-darwin-x64@4.55.3': optional: true - '@rollup/rollup-freebsd-arm64@4.56.0': + '@rollup/rollup-freebsd-arm64@4.55.3': optional: true - '@rollup/rollup-freebsd-x64@4.56.0': + '@rollup/rollup-freebsd-x64@4.55.3': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.56.0': + '@rollup/rollup-linux-arm-gnueabihf@4.55.3': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.56.0': + '@rollup/rollup-linux-arm-musleabihf@4.55.3': optional: true - '@rollup/rollup-linux-arm64-gnu@4.56.0': + '@rollup/rollup-linux-arm64-gnu@4.55.3': optional: true - '@rollup/rollup-linux-arm64-musl@4.56.0': + '@rollup/rollup-linux-arm64-musl@4.55.3': optional: true - '@rollup/rollup-linux-loong64-gnu@4.56.0': + '@rollup/rollup-linux-loong64-gnu@4.55.3': optional: true - '@rollup/rollup-linux-loong64-musl@4.56.0': + '@rollup/rollup-linux-loong64-musl@4.55.3': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.56.0': + '@rollup/rollup-linux-ppc64-gnu@4.55.3': optional: true - '@rollup/rollup-linux-ppc64-musl@4.56.0': + '@rollup/rollup-linux-ppc64-musl@4.55.3': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.56.0': + '@rollup/rollup-linux-riscv64-gnu@4.55.3': optional: true - '@rollup/rollup-linux-riscv64-musl@4.56.0': + '@rollup/rollup-linux-riscv64-musl@4.55.3': optional: true - '@rollup/rollup-linux-s390x-gnu@4.56.0': + '@rollup/rollup-linux-s390x-gnu@4.55.3': optional: true - '@rollup/rollup-linux-x64-gnu@4.56.0': + '@rollup/rollup-linux-x64-gnu@4.55.3': optional: true - '@rollup/rollup-linux-x64-musl@4.56.0': + '@rollup/rollup-linux-x64-musl@4.55.3': optional: true - '@rollup/rollup-openbsd-x64@4.56.0': + '@rollup/rollup-openbsd-x64@4.55.3': optional: true - '@rollup/rollup-openharmony-arm64@4.56.0': + '@rollup/rollup-openharmony-arm64@4.55.3': optional: true - '@rollup/rollup-win32-arm64-msvc@4.56.0': + '@rollup/rollup-win32-arm64-msvc@4.55.3': optional: true - '@rollup/rollup-win32-ia32-msvc@4.56.0': + '@rollup/rollup-win32-ia32-msvc@4.55.3': optional: true - '@rollup/rollup-win32-x64-gnu@4.56.0': + '@rollup/rollup-win32-x64-gnu@4.55.3': optional: true - '@rollup/rollup-win32-x64-msvc@4.56.0': + '@rollup/rollup-win32-x64-msvc@4.55.3': optional: true '@scure/base@1.1.1': {} @@ -7342,12 +7724,12 @@ snapshots: '@scure/bip32@1.3.1': dependencies: '@noble/curves': 1.1.0 - '@noble/hashes': 1.3.1 + '@noble/hashes': 1.3.2 '@scure/base': 1.1.1 '@scure/bip39@1.2.1': dependencies: - '@noble/hashes': 1.3.1 + '@noble/hashes': 1.3.2 '@scure/base': 1.1.1 '@selderee/plugin-htmlparser2@0.11.0': @@ -7438,6 +7820,19 @@ snapshots: '@smithy/util-middleware': 4.2.8 tslib: 2.8.1 + '@smithy/core@3.21.0': + dependencies: + '@smithy/middleware-serde': 4.2.9 + '@smithy/protocol-http': 5.3.8 + '@smithy/types': 4.12.0 + '@smithy/util-base64': 4.3.0 + '@smithy/util-body-length-browser': 4.2.0 + '@smithy/util-middleware': 4.2.8 + '@smithy/util-stream': 4.5.10 + '@smithy/util-utf8': 4.2.0 + '@smithy/uuid': 1.1.0 + tslib: 2.8.1 + '@smithy/core@3.21.1': dependencies: '@smithy/middleware-serde': 4.2.9 @@ -7523,6 +7918,17 @@ snapshots: '@smithy/types': 4.12.0 tslib: 2.8.1 + '@smithy/middleware-endpoint@4.4.10': + dependencies: + '@smithy/core': 3.21.0 + '@smithy/middleware-serde': 4.2.9 + '@smithy/node-config-provider': 4.3.8 + '@smithy/shared-ini-file-loader': 4.4.3 + '@smithy/types': 4.12.0 + '@smithy/url-parser': 4.2.8 + '@smithy/util-middleware': 4.2.8 + tslib: 2.8.1 + '@smithy/middleware-endpoint@4.4.11': dependencies: '@smithy/core': 3.21.1 @@ -7534,6 +7940,18 @@ snapshots: '@smithy/util-middleware': 4.2.8 tslib: 2.8.1 + '@smithy/middleware-retry@4.4.26': + dependencies: + '@smithy/node-config-provider': 4.3.8 + '@smithy/protocol-http': 5.3.8 + '@smithy/service-error-classification': 4.2.8 + '@smithy/smithy-client': 4.10.11 + '@smithy/types': 4.12.0 + '@smithy/util-middleware': 4.2.8 + '@smithy/util-retry': 4.2.8 + '@smithy/uuid': 1.1.0 + tslib: 2.8.1 + '@smithy/middleware-retry@4.4.27': dependencies: '@smithy/node-config-provider': 4.3.8 @@ -7613,6 +8031,16 @@ snapshots: '@smithy/util-utf8': 4.2.0 tslib: 2.8.1 + '@smithy/smithy-client@4.10.11': + dependencies: + '@smithy/core': 3.21.0 + '@smithy/middleware-endpoint': 4.4.10 + '@smithy/middleware-stack': 4.2.8 + '@smithy/protocol-http': 5.3.8 + '@smithy/types': 4.12.0 + '@smithy/util-stream': 4.5.10 + tslib: 2.8.1 + '@smithy/smithy-client@4.10.12': dependencies: '@smithy/core': 3.21.1 @@ -7661,6 +8089,13 @@ snapshots: dependencies: tslib: 2.8.1 + '@smithy/util-defaults-mode-browser@4.3.25': + dependencies: + '@smithy/property-provider': 4.2.8 + '@smithy/smithy-client': 4.10.11 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + '@smithy/util-defaults-mode-browser@4.3.26': dependencies: '@smithy/property-provider': 4.2.8 @@ -7668,6 +8103,16 @@ snapshots: '@smithy/types': 4.12.0 tslib: 2.8.1 + '@smithy/util-defaults-mode-node@4.2.28': + dependencies: + '@smithy/config-resolver': 4.4.6 + '@smithy/credential-provider-imds': 4.2.8 + '@smithy/node-config-provider': 4.3.8 + '@smithy/property-provider': 4.2.8 + '@smithy/smithy-client': 4.10.11 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + '@smithy/util-defaults-mode-node@4.2.29': dependencies: '@smithy/config-resolver': 4.4.6 @@ -7734,15 +8179,15 @@ snapshots: dependencies: tslib: 2.8.1 - '@thi.ng/bitstream@2.4.39': + '@thi.ng/bitstream@2.4.38': dependencies: - '@thi.ng/errors': 2.6.2 + '@thi.ng/errors': 2.6.1 optional: true - '@thi.ng/errors@2.6.2': + '@thi.ng/errors@2.6.1': optional: true - '@tinyhttp/content-disposition@2.2.3': + '@tinyhttp/content-disposition@2.2.2': optional: true '@tokenizer/inflate@0.4.1': @@ -8387,7 +8832,7 @@ snapshots: dependencies: clsx: 2.1.1 - clawdbot@2026.1.23(@types/express@5.0.6)(audio-decode@2.2.3)(devtools-protocol@0.0.1561482)(typescript@5.9.3): + clawdbot@2026.1.23-1(@types/express@5.0.6)(audio-decode@2.2.3)(devtools-protocol@0.0.1561482)(typescript@5.9.3): dependencies: '@agentclientprotocol/sdk': 0.13.1(zod@4.3.6) '@aws-sdk/client-bedrock': 3.975.0 @@ -8500,7 +8945,7 @@ snapshots: debug: 4.4.3 fs-extra: 11.3.3 memory-stream: 1.0.0 - node-api-headers: 1.8.0 + node-api-headers: 1.7.0 npmlog: 6.0.2 rc: 1.2.8 semver: 7.7.3 @@ -9222,7 +9667,7 @@ snapshots: ipull@3.9.3: dependencies: - '@tinyhttp/content-disposition': 2.2.3 + '@tinyhttp/content-disposition': 2.2.2 async-retry: 1.3.3 chalk: 5.6.2 ci-info: 4.3.1 @@ -9392,7 +9837,7 @@ snapshots: ecdsa-sig-formatter: 1.0.11 safe-buffer: 5.2.1 - jwks-rsa@3.2.2: + jwks-rsa@3.2.1: dependencies: '@types/jsonwebtoken': 9.0.10 debug: 4.4.3 @@ -9750,7 +10195,7 @@ snapshots: node-addon-api@8.5.0: optional: true - node-api-headers@1.8.0: + node-api-headers@1.7.0: optional: true node-domexception@1.0.0: {} @@ -10197,7 +10642,7 @@ snapshots: qoa-format@1.0.1: dependencies: - '@thi.ng/bitstream': 2.4.39 + '@thi.ng/bitstream': 2.4.38 optional: true qrcode-terminal@0.12.0: {} @@ -10374,35 +10819,35 @@ snapshots: '@rolldown/binding-win32-arm64-msvc': 1.0.0-rc.1 '@rolldown/binding-win32-x64-msvc': 1.0.0-rc.1 - rollup@4.56.0: + rollup@4.55.3: dependencies: '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.56.0 - '@rollup/rollup-android-arm64': 4.56.0 - '@rollup/rollup-darwin-arm64': 4.56.0 - '@rollup/rollup-darwin-x64': 4.56.0 - '@rollup/rollup-freebsd-arm64': 4.56.0 - '@rollup/rollup-freebsd-x64': 4.56.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.56.0 - '@rollup/rollup-linux-arm-musleabihf': 4.56.0 - '@rollup/rollup-linux-arm64-gnu': 4.56.0 - '@rollup/rollup-linux-arm64-musl': 4.56.0 - '@rollup/rollup-linux-loong64-gnu': 4.56.0 - '@rollup/rollup-linux-loong64-musl': 4.56.0 - '@rollup/rollup-linux-ppc64-gnu': 4.56.0 - '@rollup/rollup-linux-ppc64-musl': 4.56.0 - '@rollup/rollup-linux-riscv64-gnu': 4.56.0 - '@rollup/rollup-linux-riscv64-musl': 4.56.0 - '@rollup/rollup-linux-s390x-gnu': 4.56.0 - '@rollup/rollup-linux-x64-gnu': 4.56.0 - '@rollup/rollup-linux-x64-musl': 4.56.0 - '@rollup/rollup-openbsd-x64': 4.56.0 - '@rollup/rollup-openharmony-arm64': 4.56.0 - '@rollup/rollup-win32-arm64-msvc': 4.56.0 - '@rollup/rollup-win32-ia32-msvc': 4.56.0 - '@rollup/rollup-win32-x64-gnu': 4.56.0 - '@rollup/rollup-win32-x64-msvc': 4.56.0 + '@rollup/rollup-android-arm-eabi': 4.55.3 + '@rollup/rollup-android-arm64': 4.55.3 + '@rollup/rollup-darwin-arm64': 4.55.3 + '@rollup/rollup-darwin-x64': 4.55.3 + '@rollup/rollup-freebsd-arm64': 4.55.3 + '@rollup/rollup-freebsd-x64': 4.55.3 + '@rollup/rollup-linux-arm-gnueabihf': 4.55.3 + '@rollup/rollup-linux-arm-musleabihf': 4.55.3 + '@rollup/rollup-linux-arm64-gnu': 4.55.3 + '@rollup/rollup-linux-arm64-musl': 4.55.3 + '@rollup/rollup-linux-loong64-gnu': 4.55.3 + '@rollup/rollup-linux-loong64-musl': 4.55.3 + '@rollup/rollup-linux-ppc64-gnu': 4.55.3 + '@rollup/rollup-linux-ppc64-musl': 4.55.3 + '@rollup/rollup-linux-riscv64-gnu': 4.55.3 + '@rollup/rollup-linux-riscv64-musl': 4.55.3 + '@rollup/rollup-linux-s390x-gnu': 4.55.3 + '@rollup/rollup-linux-x64-gnu': 4.55.3 + '@rollup/rollup-linux-x64-musl': 4.55.3 + '@rollup/rollup-openbsd-x64': 4.55.3 + '@rollup/rollup-openharmony-arm64': 4.55.3 + '@rollup/rollup-win32-arm64-msvc': 4.55.3 + '@rollup/rollup-win32-ia32-msvc': 4.55.3 + '@rollup/rollup-win32-x64-gnu': 4.55.3 + '@rollup/rollup-win32-x64-msvc': 4.55.3 fsevents: 2.3.3 router@2.2.0: @@ -10916,7 +11361,7 @@ snapshots: fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 - rollup: 4.56.0 + rollup: 4.55.3 tinyglobby: 0.2.15 optionalDependencies: '@types/node': 25.0.10 From dd57483e5ef9b9b458a06363ea4261236bb24d4f Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 25 Jan 2026 00:20:01 +0000 Subject: [PATCH 009/214] docs: add vps hosting hub --- docs/help/faq.md | 1 + docs/platforms/index.md | 1 + docs/vps.md | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 36 insertions(+) create mode 100644 docs/vps.md diff --git a/docs/help/faq.md b/docs/help/faq.md index f32ecb299..45bde9cde 100644 --- a/docs/help/faq.md +++ b/docs/help/faq.md @@ -442,6 +442,7 @@ Remote access: [Gateway remote](/gateway/remote). We keep a **hosting hub** with the common providers. Pick one and follow the guide: +- [VPS hosting](/vps) (all providers in one place) - [Railway](/railway) (one‑click, browser‑based setup) - [Fly.io](/platforms/fly) - [Hetzner](/platforms/hetzner) diff --git a/docs/platforms/index.md b/docs/platforms/index.md index 8184ea00c..1b5c85129 100644 --- a/docs/platforms/index.md +++ b/docs/platforms/index.md @@ -23,6 +23,7 @@ Native companion apps for Windows are also planned; the Gateway is recommended v ## VPS & hosting +- VPS hub: [VPS hosting](/vps) - Railway (one-click): [Railway](/railway) - Fly.io: [Fly.io](/platforms/fly) - Hetzner (Docker): [Hetzner](/platforms/hetzner) diff --git a/docs/vps.md b/docs/vps.md new file mode 100644 index 000000000..f32402d66 --- /dev/null +++ b/docs/vps.md @@ -0,0 +1,34 @@ +--- +summary: "VPS hosting hub for Clawdbot (Railway/Fly/Hetzner/exe.dev)" +read_when: + - You want to run the Gateway in the cloud + - You need a quick map of VPS/hosting guides +--- +# VPS hosting + +This hub links to the supported VPS/hosting guides and explains how cloud +deployments work at a high level. + +## Pick a provider + +- **Railway** (one‑click + browser setup): [Railway](/railway) +- **Fly.io**: [Fly.io](/platforms/fly) +- **Hetzner (Docker)**: [Hetzner](/platforms/hetzner) +- **exe.dev** (VM + HTTPS proxy): [exe.dev](/platforms/exe-dev) + +## How cloud setups work + +- The **Gateway runs on the VPS** and owns state + workspace. +- You connect from your laptop/phone via the **Control UI** or **Tailscale/SSH**. +- Treat the VPS as the source of truth and **back up** the state + workspace. + +Remote access: [Gateway remote](/gateway/remote) +Platforms hub: [Platforms](/platforms) + +## Using nodes with a VPS + +You can keep the Gateway in the cloud and pair **nodes** on your local devices +(Mac/iOS/Android/headless). Nodes provide local screen/camera/canvas and `system.run` +capabilities while the Gateway stays in the cloud. + +Docs: [Nodes](/nodes), [Nodes CLI](/cli/nodes) From 5ea15ff7fedb9e3338b4dc8fe8031b3707435ae3 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 25 Jan 2026 00:23:24 +0000 Subject: [PATCH 010/214] docs: add aws mention to vps hub --- docs/vps.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/vps.md b/docs/vps.md index f32402d66..a6d267513 100644 --- a/docs/vps.md +++ b/docs/vps.md @@ -15,6 +15,8 @@ deployments work at a high level. - **Fly.io**: [Fly.io](/platforms/fly) - **Hetzner (Docker)**: [Hetzner](/platforms/hetzner) - **exe.dev** (VM + HTTPS proxy): [exe.dev](/platforms/exe-dev) +- **AWS (EC2/Lightsail/free tier)**: works well too. Video guide: + https://x.com/techfrenAJ/status/2014934471095812547 ## How cloud setups work From a6c97b5a48793cc9070746460b55782aa2b2724a Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 25 Jan 2026 00:36:36 +0000 Subject: [PATCH 011/214] fix: reload TUI history after reconnect --- CHANGELOG.md | 1 + src/tui/tui.ts | 21 +++++++++++---------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2444e1447..8809332bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ Docs: https://docs.clawd.bot - BlueBubbles: keep part-index GUIDs in reply tags when short IDs are missing. - Web UI: hide internal `message_id` hints in chat bubbles. - Heartbeat: normalize target identifiers for consistent routing. +- TUI: reload history after gateway reconnect to restore session state. (#1663) - Telegram: use wrapped fetch for long-polling on Node to normalize AbortSignal handling. (#1639) - Exec: keep approvals for elevated ask unless full mode. (#1616) Thanks @ivancasco. - Agents: auto-compact on context overflow prompt errors before failing. (#1627) Thanks @rodrigouroz. diff --git a/src/tui/tui.ts b/src/tui/tui.ts index cf8341d59..31be58fd5 100644 --- a/src/tui/tui.ts +++ b/src/tui/tui.ts @@ -90,6 +90,7 @@ export async function runTui(opts: TuiOptions) { let activeChatRunId: string | null = null; let historyLoaded = false; let isConnected = false; + let wasDisconnected = false; let toolsExpanded = false; let showThinking = false; @@ -584,20 +585,18 @@ export async function runTui(opts: TuiOptions) { client.onConnected = () => { isConnected = true; + const reconnected = wasDisconnected; + wasDisconnected = false; setConnectionStatus("connected"); void (async () => { await refreshAgents(); updateHeader(); - if (!historyLoaded) { - await loadHistory(); - setConnectionStatus("gateway connected", 4000); - tui.requestRender(); - if (!autoMessageSent && autoMessage) { - autoMessageSent = true; - await sendMessage(autoMessage); - } - } else { - setConnectionStatus("gateway reconnected", 4000); + await loadHistory(); + setConnectionStatus(reconnected ? "gateway reconnected" : "gateway connected", 4000); + tui.requestRender(); + if (!autoMessageSent && autoMessage) { + autoMessageSent = true; + await sendMessage(autoMessage); } updateFooter(); tui.requestRender(); @@ -606,6 +605,8 @@ export async function runTui(opts: TuiOptions) { client.onDisconnected = (reason) => { isConnected = false; + wasDisconnected = true; + historyLoaded = false; const reasonLabel = reason?.trim() ? reason.trim() : "closed"; setConnectionStatus(`gateway disconnected: ${reasonLabel}`, 5000); setActivityStatus("idle"); From 6375ee836fefb0ec3d70c85fc83bda2ad00c54d4 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 25 Jan 2026 00:39:50 +0000 Subject: [PATCH 012/214] docs: clarify remote transport IP reporting --- docs/platforms/mac/remote.md | 16 +++++++++++++--- docs/platforms/macos.md | 3 +++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/docs/platforms/mac/remote.md b/docs/platforms/mac/remote.md index 7ff86d0bb..6d36700f9 100644 --- a/docs/platforms/mac/remote.md +++ b/docs/platforms/mac/remote.md @@ -10,7 +10,13 @@ This flow lets the macOS app act as a full remote control for a Clawdbot gateway ## Modes - **Local (this Mac)**: Everything runs on the laptop. No SSH involved. -- **Remote over SSH**: Clawdbot commands are executed on the remote host. The mac app opens an SSH connection with `-o BatchMode` plus your chosen identity/key. +- **Remote over SSH (default)**: Clawdbot commands are executed on the remote host. The mac app opens an SSH connection with `-o BatchMode` plus your chosen identity/key and a local port-forward. +- **Remote direct (ws/wss)**: No SSH tunnel. The mac app connects to the gateway URL directly (for example, via Tailscale Serve or a public HTTPS reverse proxy). + +## Remote transports +Remote mode supports two transports: +- **SSH tunnel** (default): Uses `ssh -N -L ...` to forward the gateway port to localhost. The gateway will see the node’s IP as `127.0.0.1` because the tunnel is loopback. +- **Direct (ws/wss)**: Connects straight to the gateway URL. The gateway sees the real client IP. ## Prereqs on the remote host 1) Install Node + pnpm and build/install the Clawdbot CLI (`pnpm install && pnpm build && pnpm link --global`). @@ -20,16 +26,19 @@ This flow lets the macOS app act as a full remote control for a Clawdbot gateway ## macOS app setup 1) Open *Settings → General*. 2) Under **Clawdbot runs**, pick **Remote over SSH** and set: + - **Transport**: **SSH tunnel** or **Direct (ws/wss)**. - **SSH target**: `user@host` (optional `:port`). - If the gateway is on the same LAN and advertises Bonjour, pick it from the discovered list to auto-fill this field. + - **Gateway URL** (Direct only): `wss://gateway.example.ts.net` (or `ws://...` for local/LAN). - **Identity file** (advanced): path to your key. - **Project root** (advanced): remote checkout path used for commands. - **CLI path** (advanced): optional path to a runnable `clawdbot` entrypoint/binary (auto-filled when advertised). 3) Hit **Test remote**. Success indicates the remote `clawdbot status --json` runs correctly. Failures usually mean PATH/CLI issues; exit 127 means the CLI isn’t found remotely. 4) Health checks and Web Chat will now run through this SSH tunnel automatically. -## Web Chat over SSH -- Web Chat connects to the gateway over the forwarded WebSocket control port (default 18789). +## Web Chat +- **SSH tunnel**: Web Chat connects to the gateway over the forwarded WebSocket control port (default 18789). +- **Direct (ws/wss)**: Web Chat connects straight to the configured gateway URL. - There is no separate WebChat HTTP server anymore. ## Permissions @@ -49,6 +58,7 @@ This flow lets the macOS app act as a full remote control for a Clawdbot gateway - **exit 127 / not found**: `clawdbot` isn’t on PATH for non-login shells. Add it to `/etc/paths`, your shell rc, or symlink into `/usr/local/bin`/`/opt/homebrew/bin`. - **Health probe failed**: check SSH reachability, PATH, and that Baileys is logged in (`clawdbot status --json`). - **Web Chat stuck**: confirm the gateway is running on the remote host and the forwarded port matches the gateway WS port; the UI requires a healthy WS connection. +- **Node IP shows 127.0.0.1**: expected with the SSH tunnel. Switch **Transport** to **Direct (ws/wss)** if you want the gateway to see the real client IP. - **Voice Wake**: trigger phrases are forwarded automatically in remote mode; no separate forwarder is needed. ## Notification sounds diff --git a/docs/platforms/macos.md b/docs/platforms/macos.md index e27e10e56..36e7f2182 100644 --- a/docs/platforms/macos.md +++ b/docs/platforms/macos.md @@ -180,6 +180,9 @@ components can talk to a remote Gateway as if it were on localhost. or restarts it if needed. - **SSH shape:** `ssh -N -L :127.0.0.1:` with BatchMode + ExitOnForwardFailure + keepalive options. +- **IP reporting:** the SSH tunnel uses loopback, so the gateway will see the node + IP as `127.0.0.1`. Use **Direct (ws/wss)** transport if you want the real client + IP to appear (see [macOS remote access](/platforms/mac/remote)). For setup steps, see [macOS remote access](/platforms/mac/remote). For protocol details, see [Gateway protocol](/gateway/protocol). From 92e794dc18127fc1afbbb3e8eff716d4906d3712 Mon Sep 17 00:00:00 2001 From: Tyler Yust <64381258+tyler6204@users.noreply.github.com> Date: Sat, 24 Jan 2026 16:47:10 -0800 Subject: [PATCH 013/214] feat: add chunking mode option for BlueBubbles (#1645) * feat: add chunking mode for outbound messages - Introduced `chunkMode` option in various account configurations to allow splitting messages by "length" or "newline". - Updated message processing to handle chunking based on the selected mode. - Added tests for new chunking functionality, ensuring correct behavior for both modes. * feat: enhance chunking mode documentation and configuration - Added `chunkMode` option to the BlueBubbles account configuration, allowing users to choose between "length" and "newline" for message chunking. - Updated documentation to clarify the behavior of the `chunkMode` setting. - Adjusted account merging logic to incorporate the new `chunkMode` configuration. * refactor: simplify chunk mode handling for BlueBubbles - Removed `chunkMode` configuration from various account schemas and types, centralizing chunk mode logic to BlueBubbles only. - Updated `processMessage` to default to "newline" for BlueBubbles chunking. - Adjusted tests to reflect changes in chunk mode handling for BlueBubbles, ensuring proper functionality. * fix: update default chunk mode to 'length' for BlueBubbles - Changed the default value of `chunkMode` from 'newline' to 'length' in the BlueBubbles configuration and related processing functions. - Updated documentation to reflect the new default behavior for chunking messages. - Adjusted tests to ensure the correct default value is returned for BlueBubbles chunk mode. --- docs/channels/bluebubbles.md | 1 + extensions/bluebubbles/src/accounts.ts | 3 +- extensions/bluebubbles/src/config-schema.ts | 1 + extensions/bluebubbles/src/monitor.ts | 20 +++- extensions/bluebubbles/src/types.ts | 2 + src/auto-reply/chunk.test.ts | 101 +++++++++++++++++++- src/auto-reply/chunk.ts | 84 +++++++++++++++- src/auto-reply/reply/block-streaming.ts | 20 +++- src/config/zod-schema.providers-core.ts | 1 + src/infra/outbound/deliver.ts | 3 +- src/plugin-sdk/index.ts | 1 + src/plugins/runtime/index.ts | 12 ++- src/plugins/runtime/types.ts | 6 ++ 13 files changed, 247 insertions(+), 8 deletions(-) diff --git a/docs/channels/bluebubbles.md b/docs/channels/bluebubbles.md index cf5faee1d..eed40b681 100644 --- a/docs/channels/bluebubbles.md +++ b/docs/channels/bluebubbles.md @@ -196,6 +196,7 @@ Provider options: - `channels.bluebubbles.sendReadReceipts`: Send read receipts (default: `true`). - `channels.bluebubbles.blockStreaming`: Enable block streaming (default: `true`). - `channels.bluebubbles.textChunkLimit`: Outbound chunk size in chars (default: 4000). +- `channels.bluebubbles.chunkMode`: `length` (default) splits only when exceeding `textChunkLimit`; `newline` splits on every newline and sends each line immediately during streaming. - `channels.bluebubbles.mediaMaxMb`: Inbound media cap in MB (default: 8). - `channels.bluebubbles.historyLimit`: Max group messages for context (0 disables). - `channels.bluebubbles.dmHistoryLimit`: DM history limit. diff --git a/extensions/bluebubbles/src/accounts.ts b/extensions/bluebubbles/src/accounts.ts index 5a4fee8ba..9fc94356d 100644 --- a/extensions/bluebubbles/src/accounts.ts +++ b/extensions/bluebubbles/src/accounts.ts @@ -47,7 +47,8 @@ function mergeBlueBubblesAccountConfig( }; const { accounts: _ignored, ...rest } = base; const account = resolveAccountConfig(cfg, accountId) ?? {}; - return { ...rest, ...account }; + const chunkMode = account.chunkMode ?? rest.chunkMode ?? "length"; + return { ...rest, ...account, chunkMode }; } export function resolveBlueBubblesAccount(params: { diff --git a/extensions/bluebubbles/src/config-schema.ts b/extensions/bluebubbles/src/config-schema.ts index 844641b94..dc532e979 100644 --- a/extensions/bluebubbles/src/config-schema.ts +++ b/extensions/bluebubbles/src/config-schema.ts @@ -38,6 +38,7 @@ const bluebubblesAccountSchema = z.object({ historyLimit: z.number().int().min(0).optional(), dmHistoryLimit: z.number().int().min(0).optional(), textChunkLimit: z.number().int().positive().optional(), + chunkMode: z.enum(["length", "newline"]).optional(), mediaMaxMb: z.number().int().positive().optional(), sendReadReceipts: z.boolean().optional(), blockStreaming: z.boolean().optional(), diff --git a/extensions/bluebubbles/src/monitor.ts b/extensions/bluebubbles/src/monitor.ts index 570ca42e0..8635b183e 100644 --- a/extensions/bluebubbles/src/monitor.ts +++ b/extensions/bluebubbles/src/monitor.ts @@ -1851,16 +1851,21 @@ async function processMessage( account.config.textChunkLimit && account.config.textChunkLimit > 0 ? account.config.textChunkLimit : DEFAULT_TEXT_LIMIT; + const chunkMode = account.config.chunkMode ?? "length"; const tableMode = core.channel.text.resolveMarkdownTableMode({ cfg: config, channel: "bluebubbles", accountId: account.accountId, }); const text = core.channel.text.convertMarkdownTables(payload.text ?? "", tableMode); - const chunks = core.channel.text.chunkMarkdownText(text, textLimit); + const chunks = + chunkMode === "newline" + ? core.channel.text.chunkTextWithMode(text, textLimit, chunkMode) + : core.channel.text.chunkMarkdownText(text, textLimit); if (!chunks.length && text) chunks.push(text); if (!chunks.length) return; - for (const chunk of chunks) { + for (let i = 0; i < chunks.length; i++) { + const chunk = chunks[i]; const result = await sendMessageBlueBubbles(outboundTarget, chunk, { cfg: config, accountId: account.accountId, @@ -1869,6 +1874,17 @@ async function processMessage( maybeEnqueueOutboundMessageId(result.messageId, chunk); sentMessage = true; statusSink?.({ lastOutboundAt: Date.now() }); + // In newline mode, restart typing after each chunk if more chunks remain + // Small delay allows the Apple API to finish clearing the typing state from message send + if (chunkMode === "newline" && i < chunks.length - 1 && chatGuidForActions) { + await new Promise((r) => setTimeout(r, 150)); + sendBlueBubblesTyping(chatGuidForActions, true, { + cfg: config, + accountId: account.accountId, + }).catch(() => { + // Ignore typing errors + }); + } } }, onReplyStart: async () => { diff --git a/extensions/bluebubbles/src/types.ts b/extensions/bluebubbles/src/types.ts index 6b1da775b..d2aeb4022 100644 --- a/extensions/bluebubbles/src/types.ts +++ b/extensions/bluebubbles/src/types.ts @@ -38,6 +38,8 @@ export type BlueBubblesAccountConfig = { dms?: Record; /** Outbound text chunk size (chars). Default: 4000. */ textChunkLimit?: number; + /** Chunking mode: "newline" (default) splits on every newline; "length" splits by size. */ + chunkMode?: "length" | "newline"; blockStreaming?: boolean; /** Merge streamed block replies before sending. */ blockStreamingCoalesce?: Record; diff --git a/src/auto-reply/chunk.test.ts b/src/auto-reply/chunk.test.ts index 17e98739c..7a4b41d0e 100644 --- a/src/auto-reply/chunk.test.ts +++ b/src/auto-reply/chunk.test.ts @@ -1,6 +1,13 @@ import { describe, expect, it } from "vitest"; -import { chunkMarkdownText, chunkText, resolveTextChunkLimit } from "./chunk.js"; +import { + chunkByNewline, + chunkMarkdownText, + chunkText, + chunkTextWithMode, + resolveChunkMode, + resolveTextChunkLimit, +} from "./chunk.js"; function expectFencesBalanced(chunks: string[]) { for (const chunk of chunks) { @@ -231,3 +238,95 @@ describe("chunkMarkdownText", () => { expect(chunks.join("")).toBe(text); }); }); + +describe("chunkByNewline", () => { + it("splits text on newlines", () => { + const text = "Line one\nLine two\nLine three"; + const chunks = chunkByNewline(text, 1000); + expect(chunks).toEqual(["Line one", "Line two", "Line three"]); + }); + + it("filters empty lines", () => { + const text = "Line one\n\n\nLine two\n\nLine three"; + const chunks = chunkByNewline(text, 1000); + expect(chunks).toEqual(["Line one", "Line two", "Line three"]); + }); + + it("trims whitespace from lines", () => { + const text = " Line one \n Line two "; + const chunks = chunkByNewline(text, 1000); + expect(chunks).toEqual(["Line one", "Line two"]); + }); + + it("falls back to length-based for long lines", () => { + const text = "Short line\n" + "a".repeat(50) + "\nAnother short"; + const chunks = chunkByNewline(text, 20); + expect(chunks[0]).toBe("Short line"); + // Long line gets split into multiple chunks + expect(chunks[1].length).toBe(20); + expect(chunks[2].length).toBe(20); + expect(chunks[3].length).toBe(10); + expect(chunks[4]).toBe("Another short"); + }); + + it("returns empty array for empty input", () => { + expect(chunkByNewline("", 100)).toEqual([]); + }); + + it("returns empty array for whitespace-only input", () => { + expect(chunkByNewline(" \n\n ", 100)).toEqual([]); + }); +}); + +describe("chunkTextWithMode", () => { + it("uses length-based chunking for length mode", () => { + const text = "Line one\nLine two"; + const chunks = chunkTextWithMode(text, 1000, "length"); + expect(chunks).toEqual(["Line one\nLine two"]); + }); + + it("uses newline-based chunking for newline mode", () => { + const text = "Line one\nLine two"; + const chunks = chunkTextWithMode(text, 1000, "newline"); + expect(chunks).toEqual(["Line one", "Line two"]); + }); +}); + +describe("resolveChunkMode", () => { + it("returns length as default", () => { + expect(resolveChunkMode(undefined, "telegram")).toBe("length"); + expect(resolveChunkMode({}, "discord")).toBe("length"); + expect(resolveChunkMode(undefined, "bluebubbles")).toBe("length"); + }); + + it("returns length for internal channel", () => { + const cfg = { channels: { bluebubbles: { chunkMode: "newline" as const } } }; + expect(resolveChunkMode(cfg, "__internal__")).toBe("length"); + }); + + it("supports provider-level overrides for bluebubbles", () => { + const cfg = { channels: { bluebubbles: { chunkMode: "newline" as const } } }; + expect(resolveChunkMode(cfg, "bluebubbles")).toBe("newline"); + expect(resolveChunkMode(cfg, "discord")).toBe("length"); + }); + + it("supports account-level overrides for bluebubbles", () => { + const cfg = { + channels: { + bluebubbles: { + chunkMode: "length" as const, + accounts: { + primary: { chunkMode: "newline" as const }, + }, + }, + }, + }; + expect(resolveChunkMode(cfg, "bluebubbles", "primary")).toBe("newline"); + expect(resolveChunkMode(cfg, "bluebubbles", "other")).toBe("length"); + }); + + it("ignores chunkMode for non-bluebubbles providers", () => { + const cfg = { channels: { ["telegram" as string]: { chunkMode: "newline" as const } } }; + expect(resolveChunkMode(cfg, "telegram")).toBe("length"); + }); +}); diff --git a/src/auto-reply/chunk.ts b/src/auto-reply/chunk.ts index abbd830a2..281612e37 100644 --- a/src/auto-reply/chunk.ts +++ b/src/auto-reply/chunk.ts @@ -10,11 +10,20 @@ import { INTERNAL_MESSAGE_CHANNEL } from "../utils/message-channel.js"; export type TextChunkProvider = ChannelId | typeof INTERNAL_MESSAGE_CHANNEL; +/** + * Chunking mode for outbound messages: + * - "length": Split only when exceeding textChunkLimit (default) + * - "newline": Split on every newline, with fallback to length-based for long lines + */ +export type ChunkMode = "length" | "newline"; + const DEFAULT_CHUNK_LIMIT = 4000; +const DEFAULT_CHUNK_MODE: ChunkMode = "length"; type ProviderChunkConfig = { textChunkLimit?: number; - accounts?: Record; + chunkMode?: ChunkMode; + accounts?: Record; }; function resolveChunkLimitForProvider( @@ -63,6 +72,79 @@ export function resolveTextChunkLimit( return fallback; } +function resolveChunkModeForProvider( + cfgSection: ProviderChunkConfig | undefined, + accountId?: string | null, +): ChunkMode | undefined { + if (!cfgSection) return undefined; + const normalizedAccountId = normalizeAccountId(accountId); + const accounts = cfgSection.accounts; + if (accounts && typeof accounts === "object") { + const direct = accounts[normalizedAccountId]; + if (direct?.chunkMode) { + return direct.chunkMode; + } + const matchKey = Object.keys(accounts).find( + (key) => key.toLowerCase() === normalizedAccountId.toLowerCase(), + ); + const match = matchKey ? accounts[matchKey] : undefined; + if (match?.chunkMode) { + return match.chunkMode; + } + } + return cfgSection.chunkMode; +} + +export function resolveChunkMode( + cfg: ClawdbotConfig | undefined, + provider?: TextChunkProvider, + accountId?: string | null, +): ChunkMode { + if (!provider || provider === INTERNAL_MESSAGE_CHANNEL) return DEFAULT_CHUNK_MODE; + // Chunk mode is only supported for BlueBubbles. + if (provider !== "bluebubbles") return DEFAULT_CHUNK_MODE; + const channelsConfig = cfg?.channels as Record | undefined; + const providerConfig = (channelsConfig?.[provider] ?? + (cfg as Record | undefined)?.[provider]) as ProviderChunkConfig | undefined; + const mode = resolveChunkModeForProvider(providerConfig, accountId); + return mode ?? DEFAULT_CHUNK_MODE; +} + +/** + * Split text on newlines, filtering empty lines. + * Lines exceeding maxLineLength are further split using length-based chunking. + */ +export function chunkByNewline(text: string, maxLineLength: number): string[] { + if (!text) return []; + const lines = text.split("\n"); + const chunks: string[] = []; + + for (const line of lines) { + const trimmed = line.trim(); + if (!trimmed) continue; // skip empty lines + + if (trimmed.length <= maxLineLength) { + chunks.push(trimmed); + } else { + // Long line: fall back to length-based chunking + const subChunks = chunkText(trimmed, maxLineLength); + chunks.push(...subChunks); + } + } + + return chunks; +} + +/** + * Unified chunking function that dispatches based on mode. + */ +export function chunkTextWithMode(text: string, limit: number, mode: ChunkMode): string[] { + if (mode === "newline") { + return chunkByNewline(text, limit); + } + return chunkText(text, limit); +} + export function chunkText(text: string, limit: number): string[] { if (!text) return []; if (limit <= 0) return [text]; diff --git a/src/auto-reply/reply/block-streaming.ts b/src/auto-reply/reply/block-streaming.ts index 52d114500..fb462b107 100644 --- a/src/auto-reply/reply/block-streaming.ts +++ b/src/auto-reply/reply/block-streaming.ts @@ -7,7 +7,7 @@ import { INTERNAL_MESSAGE_CHANNEL, listDeliverableMessageChannels, } from "../../utils/message-channel.js"; -import { resolveTextChunkLimit, type TextChunkProvider } from "../chunk.js"; +import { resolveChunkMode, resolveTextChunkLimit, type TextChunkProvider } from "../chunk.js"; const DEFAULT_BLOCK_STREAM_MIN = 800; const DEFAULT_BLOCK_STREAM_MAX = 1200; @@ -68,6 +68,17 @@ export function resolveBlockStreamingChunking( fallbackLimit: providerChunkLimit, }); const chunkCfg = cfg?.agents?.defaults?.blockStreamingChunk; + + // BlueBubbles-only: if chunkMode is "newline", use newline-based streaming + const channelChunkMode = resolveChunkMode(cfg, providerKey, accountId); + if (channelChunkMode === "newline") { + // For newline mode: use very low minChars to flush quickly on newlines + const minChars = Math.max(1, Math.floor(chunkCfg?.minChars ?? 1)); + const maxRequested = Math.max(1, Math.floor(chunkCfg?.maxChars ?? textLimit)); + const maxChars = Math.max(1, Math.min(maxRequested, textLimit)); + return { minChars, maxChars, breakPreference: "newline" }; + } + const maxRequested = Math.max(1, Math.floor(chunkCfg?.maxChars ?? DEFAULT_BLOCK_STREAM_MAX)); const maxChars = Math.max(1, Math.min(maxRequested, textLimit)); const minFallback = DEFAULT_BLOCK_STREAM_MIN; @@ -91,6 +102,13 @@ export function resolveBlockStreamingCoalescing( }, ): BlockStreamingCoalescing | undefined { const providerKey = normalizeChunkProvider(provider); + + // BlueBubbles-only: when chunkMode is "newline", disable coalescing to send each line immediately + const channelChunkMode = resolveChunkMode(cfg, providerKey, accountId); + if (channelChunkMode === "newline") { + return undefined; + } + const providerId = providerKey ? normalizeChannelId(providerKey) : null; const providerChunkLimit = providerId ? getChannelDock(providerId)?.outbound?.textChunkLimit diff --git a/src/config/zod-schema.providers-core.ts b/src/config/zod-schema.providers-core.ts index 2aee48711..9d1eaa285 100644 --- a/src/config/zod-schema.providers-core.ts +++ b/src/config/zod-schema.providers-core.ts @@ -633,6 +633,7 @@ export const BlueBubblesAccountSchemaBase = z dmHistoryLimit: z.number().int().min(0).optional(), dms: z.record(z.string(), DmConfigSchema.optional()).optional(), textChunkLimit: z.number().int().positive().optional(), + chunkMode: z.enum(["length", "newline"]).optional(), mediaMaxMb: z.number().int().positive().optional(), sendReadReceipts: z.boolean().optional(), blockStreaming: z.boolean().optional(), diff --git a/src/infra/outbound/deliver.ts b/src/infra/outbound/deliver.ts index 73f5550e0..2665e9957 100644 --- a/src/infra/outbound/deliver.ts +++ b/src/infra/outbound/deliver.ts @@ -212,7 +212,8 @@ export async function deliverOutboundPayloads(params: { results.push(await handler.sendText(text)); return; } - for (const chunk of handler.chunker(text, textLimit)) { + const chunks = handler.chunker(text, textLimit); + for (const chunk of chunks) { throwIfAborted(abortSignal); results.push(await handler.sendText(chunk)); } diff --git a/src/plugin-sdk/index.ts b/src/plugin-sdk/index.ts index f40d99d82..cb4e95a82 100644 --- a/src/plugin-sdk/index.ts +++ b/src/plugin-sdk/index.ts @@ -112,6 +112,7 @@ export type { WizardPrompter } from "../wizard/prompts.js"; export { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../routing/session-key.js"; export { resolveAckReaction } from "../agents/identity.js"; export type { ReplyPayload } from "../auto-reply/types.js"; +export type { ChunkMode } from "../auto-reply/chunk.js"; export { SILENT_REPLY_TOKEN, isSilentReplyText } from "../auto-reply/tokens.js"; export { buildPendingHistoryContextFromMap, diff --git a/src/plugins/runtime/index.ts b/src/plugins/runtime/index.ts index 5783711b1..50e0a2d03 100644 --- a/src/plugins/runtime/index.ts +++ b/src/plugins/runtime/index.ts @@ -1,6 +1,13 @@ import { createRequire } from "node:module"; -import { chunkMarkdownText, chunkText, resolveTextChunkLimit } from "../../auto-reply/chunk.js"; +import { + chunkByNewline, + chunkMarkdownText, + chunkText, + chunkTextWithMode, + resolveChunkMode, + resolveTextChunkLimit, +} from "../../auto-reply/chunk.js"; import { hasControlCommand, isControlCommandMessage, @@ -160,8 +167,11 @@ export function createPluginRuntime(): PluginRuntime { }, channel: { text: { + chunkByNewline, chunkMarkdownText, chunkText, + chunkTextWithMode, + resolveChunkMode, resolveTextChunkLimit, hasControlCommand, resolveMarkdownTableMode, diff --git a/src/plugins/runtime/types.ts b/src/plugins/runtime/types.ts index 115cb447e..40d936762 100644 --- a/src/plugins/runtime/types.ts +++ b/src/plugins/runtime/types.ts @@ -35,8 +35,11 @@ type ResolveInboundDebounceMs = type ResolveCommandAuthorizedFromAuthorizers = typeof import("../../channels/command-gating.js").resolveCommandAuthorizedFromAuthorizers; type ResolveTextChunkLimit = typeof import("../../auto-reply/chunk.js").resolveTextChunkLimit; +type ResolveChunkMode = typeof import("../../auto-reply/chunk.js").resolveChunkMode; type ChunkMarkdownText = typeof import("../../auto-reply/chunk.js").chunkMarkdownText; type ChunkText = typeof import("../../auto-reply/chunk.js").chunkText; +type ChunkTextWithMode = typeof import("../../auto-reply/chunk.js").chunkTextWithMode; +type ChunkByNewline = typeof import("../../auto-reply/chunk.js").chunkByNewline; type ResolveMarkdownTableMode = typeof import("../../config/markdown-tables.js").resolveMarkdownTableMode; type ConvertMarkdownTables = typeof import("../../markdown/tables.js").convertMarkdownTables; @@ -173,8 +176,11 @@ export type PluginRuntime = { }; channel: { text: { + chunkByNewline: ChunkByNewline; chunkMarkdownText: ChunkMarkdownText; chunkText: ChunkText; + chunkTextWithMode: ChunkTextWithMode; + resolveChunkMode: ResolveChunkMode; resolveTextChunkLimit: ResolveTextChunkLimit; hasControlCommand: HasControlCommand; resolveMarkdownTableMode: ResolveMarkdownTableMode; From 6a7a1d708525b8f734ee6925d3fc68d988d19c87 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 25 Jan 2026 01:00:15 +0000 Subject: [PATCH 014/214] fix: add chat stop button Co-authored-by: Nathan Broadbent --- CHANGELOG.md | 1 + ui/src/ui/views/chat.test.ts | 96 ++++++++++++++++++++++++++++++++++++ ui/src/ui/views/chat.ts | 7 +-- 3 files changed, 101 insertions(+), 3 deletions(-) create mode 100644 ui/src/ui/views/chat.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 8809332bb..e4d31e635 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ Docs: https://docs.clawd.bot ### Fixes - BlueBubbles: keep part-index GUIDs in reply tags when short IDs are missing. - Web UI: hide internal `message_id` hints in chat bubbles. +- Web UI: show Stop button during active runs, swap back to New session when idle. (#1664) Thanks @ndbroadbent. - Heartbeat: normalize target identifiers for consistent routing. - TUI: reload history after gateway reconnect to restore session state. (#1663) - Telegram: use wrapped fetch for long-polling on Node to normalize AbortSignal handling. (#1639) diff --git a/ui/src/ui/views/chat.test.ts b/ui/src/ui/views/chat.test.ts new file mode 100644 index 000000000..6cd469558 --- /dev/null +++ b/ui/src/ui/views/chat.test.ts @@ -0,0 +1,96 @@ +import { render } from "lit"; +import { describe, expect, it, vi } from "vitest"; + +import type { SessionsListResult } from "../types"; +import { renderChat, type ChatProps } from "./chat"; + +function createSessions(): SessionsListResult { + return { + ts: 0, + path: "", + count: 0, + defaults: { model: null, contextTokens: null }, + sessions: [], + }; +} + +function createProps(overrides: Partial = {}): ChatProps { + return { + sessionKey: "main", + onSessionKeyChange: () => undefined, + thinkingLevel: null, + showThinking: false, + loading: false, + sending: false, + canAbort: false, + compactionStatus: null, + messages: [], + toolMessages: [], + stream: null, + streamStartedAt: null, + assistantAvatarUrl: null, + draft: "", + queue: [], + connected: true, + canSend: true, + disabledReason: null, + error: null, + sessions: createSessions(), + focusMode: false, + assistantName: "Clawdbot", + assistantAvatar: null, + onRefresh: () => undefined, + onToggleFocusMode: () => undefined, + onDraftChange: () => undefined, + onSend: () => undefined, + onQueueRemove: () => undefined, + onNewSession: () => undefined, + ...overrides, + }; +} + +describe("chat view", () => { + it("shows a stop button when aborting is available", () => { + const container = document.createElement("div"); + const onAbort = vi.fn(); + render( + renderChat( + createProps({ + canAbort: true, + onAbort, + }), + ), + container, + ); + + const stopButton = Array.from(container.querySelectorAll("button")).find( + (btn) => btn.textContent?.trim() === "Stop", + ); + expect(stopButton).not.toBeUndefined(); + stopButton?.dispatchEvent(new MouseEvent("click", { bubbles: true })); + expect(onAbort).toHaveBeenCalledTimes(1); + expect(container.textContent).not.toContain("New session"); + }); + + it("shows a new session button when aborting is unavailable", () => { + const container = document.createElement("div"); + const onNewSession = vi.fn(); + render( + renderChat( + createProps({ + canAbort: false, + onNewSession, + }), + ), + container, + ); + + const newSessionButton = Array.from(container.querySelectorAll("button")).find( + (btn) => btn.textContent?.trim() === "New session", + ); + expect(newSessionButton).not.toBeUndefined(); + newSessionButton?.dispatchEvent(new MouseEvent("click", { bubbles: true })); + expect(onNewSession).toHaveBeenCalledTimes(1); + expect(container.textContent).not.toContain("Stop"); + }); +}); diff --git a/ui/src/ui/views/chat.ts b/ui/src/ui/views/chat.ts index 9bae523ed..677c2a183 100644 --- a/ui/src/ui/views/chat.ts +++ b/ui/src/ui/views/chat.ts @@ -97,6 +97,7 @@ function renderCompactionIndicator(status: CompactionIndicatorStatus | null | un export function renderChat(props: ChatProps) { const canCompose = props.connected; const isBusy = props.sending || props.stream !== null; + const canAbort = Boolean(props.canAbort && props.onAbort); const activeSession = props.sessions?.sessions?.find( (row) => row.key === props.sessionKey, ); @@ -254,10 +255,10 @@ export function renderChat(props: ChatProps) {