diff --git a/extensions/memory-core/package.json b/extensions/memory-core/package.json index 48a089aaa..2dd09751b 100644 --- a/extensions/memory-core/package.json +++ b/extensions/memory-core/package.json @@ -8,7 +8,7 @@ "./index.ts" ] }, - "dependencies": { - "clawdbot": "workspace:*" + "peerDependencies": { + "clawdbot": ">=2026.1.23-1" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5c186498f..b034da7a2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -335,8 +335,8 @@ importers: extensions/memory-core: dependencies: clawdbot: - specifier: workspace:* - version: link:../.. + 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) extensions/memory-lancedb: dependencies: @@ -2987,6 +2987,11 @@ packages: class-variance-authority@0.7.1: resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} + clawdbot@2026.1.23: + resolution: {integrity: sha512-w8RjScbxj3YbJYtcB0GBITqmyUYegVbBXDgu/zRxB4AB/SEErR6BNWGeZDWQNFaMftIyJhjttACxSd8G20aREA==} + engines: {node: '>=22.12.0'} + hasBin: true + cli-cursor@5.0.0: resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} engines: {node: '>=18'} @@ -8368,6 +8373,82 @@ 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): + dependencies: + '@agentclientprotocol/sdk': 0.13.1(zod@4.3.6) + '@aws-sdk/client-bedrock': 3.975.0 + '@buape/carbon': 0.14.0(hono@4.11.4) + '@clack/prompts': 0.11.0 + '@grammyjs/runner': 2.0.3(grammy@1.39.3) + '@grammyjs/transformer-throttler': 1.2.1(grammy@1.39.3) + '@homebridge/ciao': 1.3.4 + '@lydell/node-pty': 1.2.0-beta.3 + '@mariozechner/pi-agent-core': 0.49.3(ws@8.19.0)(zod@4.3.6) + '@mariozechner/pi-ai': 0.49.3(ws@8.19.0)(zod@4.3.6) + '@mariozechner/pi-coding-agent': 0.49.3(ws@8.19.0)(zod@4.3.6) + '@mariozechner/pi-tui': 0.49.3 + '@mozilla/readability': 0.6.0 + '@sinclair/typebox': 0.34.47 + '@slack/bolt': 4.6.0(@types/express@5.0.6) + '@slack/web-api': 7.13.0 + '@whiskeysockets/baileys': 7.0.0-rc.9(audio-decode@2.2.3)(sharp@0.34.5) + ajv: 8.17.1 + body-parser: 2.2.2 + chalk: 5.6.2 + chokidar: 5.0.0 + chromium-bidi: 13.0.1(devtools-protocol@0.0.1561482) + cli-highlight: 2.1.11 + commander: 14.0.2 + croner: 9.1.0 + detect-libc: 2.1.2 + discord-api-types: 0.38.37 + dotenv: 17.2.3 + express: 5.2.1 + file-type: 21.3.0 + grammy: 1.39.3 + hono: 4.11.4 + jiti: 2.6.1 + json5: 2.2.3 + jszip: 3.10.1 + linkedom: 0.18.12 + long: 5.3.2 + markdown-it: 14.1.0 + osc-progress: 0.3.0 + pdfjs-dist: 5.4.530 + playwright-core: 1.58.0 + proper-lockfile: 4.1.2 + qrcode-terminal: 0.12.0 + sharp: 0.34.5 + sqlite-vec: 0.1.7-alpha.2 + tar: 7.5.4 + tslog: 4.10.2 + undici: 7.19.0 + ws: 8.19.0 + yaml: 2.8.2 + zod: 4.3.6 + optionalDependencies: + '@napi-rs/canvas': 0.1.88 + node-llama-cpp: 3.15.0(typescript@5.9.3) + transitivePeerDependencies: + - '@discordjs/opus' + - '@modelcontextprotocol/sdk' + - '@types/express' + - audio-decode + - aws-crt + - bufferutil + - canvas + - debug + - devtools-protocol + - encoding + - ffmpeg-static + - jimp + - link-preview-js + - node-opus + - opusscript + - supports-color + - typescript + - utf-8-validate + cli-cursor@5.0.0: dependencies: restore-cursor: 5.1.0 diff --git a/scripts/e2e/Dockerfile b/scripts/e2e/Dockerfile index f7cde334f..0ae4a5063 100644 --- a/scripts/e2e/Dockerfile +++ b/scripts/e2e/Dockerfile @@ -13,9 +13,11 @@ COPY scripts ./scripts COPY docs ./docs COPY skills ./skills COPY patches ./patches +COPY ui ./ui COPY extensions/memory-core ./extensions/memory-core RUN pnpm install --frozen-lockfile RUN pnpm build +RUN pnpm ui:build CMD ["bash"] diff --git a/src/cli/cron-cli/register.cron-add.ts b/src/cli/cron-cli/register.cron-add.ts index 74e23a1e7..ee311ac43 100644 --- a/src/cli/cron-cli/register.cron-add.ts +++ b/src/cli/cron-cli/register.cron-add.ts @@ -2,7 +2,7 @@ import type { Command } from "commander"; import type { CronJob } from "../../cron/types.js"; import { danger } from "../../globals.js"; import { defaultRuntime } from "../../runtime.js"; -import { normalizeAgentId } from "../../routing/session-key.js"; +import { sanitizeAgentId } from "../../routing/session-key.js"; import type { GatewayRpcOpts } from "../gateway-rpc.js"; import { addGatewayClientOptions, callGatewayFromCli } from "../gateway-rpc.js"; import { parsePositiveIntOrUndefined } from "../program/helpers.js"; @@ -140,7 +140,7 @@ export function registerCronAddCommand(cron: Command) { const agentId = typeof opts.agent === "string" && opts.agent.trim() - ? normalizeAgentId(opts.agent.trim()) + ? sanitizeAgentId(opts.agent.trim()) : undefined; const payload = (() => { diff --git a/src/cli/cron-cli/register.cron-edit.ts b/src/cli/cron-cli/register.cron-edit.ts index efb1edead..1f600c5ab 100644 --- a/src/cli/cron-cli/register.cron-edit.ts +++ b/src/cli/cron-cli/register.cron-edit.ts @@ -1,7 +1,7 @@ import type { Command } from "commander"; import { danger } from "../../globals.js"; import { defaultRuntime } from "../../runtime.js"; -import { normalizeAgentId } from "../../routing/session-key.js"; +import { sanitizeAgentId } from "../../routing/session-key.js"; import { addGatewayClientOptions, callGatewayFromCli } from "../gateway-rpc.js"; import { getCronChannelOptions, @@ -91,7 +91,7 @@ export function registerCronEditCommand(cron: Command) { throw new Error("Use --agent or --clear-agent, not both"); } if (typeof opts.agent === "string" && opts.agent.trim()) { - patch.agentId = normalizeAgentId(opts.agent.trim()); + patch.agentId = sanitizeAgentId(opts.agent.trim()); } if (opts.clearAgent) { patch.agentId = null; diff --git a/src/cron/normalize.ts b/src/cron/normalize.ts index 25304edb4..55e21684f 100644 --- a/src/cron/normalize.ts +++ b/src/cron/normalize.ts @@ -1,4 +1,4 @@ -import { normalizeAgentId } from "../routing/session-key.js"; +import { sanitizeAgentId } from "../routing/session-key.js"; import { parseAbsoluteTimeMs } from "./parse.js"; import { migrateLegacyCronPayload } from "./payload-migration.js"; import type { CronJobCreate, CronJobPatch } from "./types.js"; @@ -76,7 +76,7 @@ export function normalizeCronJobInput( next.agentId = null; } else if (typeof agentId === "string") { const trimmed = agentId.trim(); - if (trimmed) next.agentId = normalizeAgentId(trimmed); + if (trimmed) next.agentId = sanitizeAgentId(trimmed); else delete next.agentId; } } diff --git a/src/routing/resolve-route.ts b/src/routing/resolve-route.ts index cc22ce51a..7ca3bf59f 100644 --- a/src/routing/resolve-route.ts +++ b/src/routing/resolve-route.ts @@ -7,6 +7,7 @@ import { DEFAULT_ACCOUNT_ID, DEFAULT_MAIN_KEY, normalizeAgentId, + sanitizeAgentId, } from "./session-key.js"; export type RoutePeerKind = "dm" | "group" | "channel"; @@ -93,13 +94,13 @@ function listAgents(cfg: ClawdbotConfig) { function pickFirstExistingAgentId(cfg: ClawdbotConfig, agentId: string): string { const trimmed = (agentId ?? "").trim(); - if (!trimmed) return normalizeAgentId(resolveDefaultAgentId(cfg)); + if (!trimmed) return sanitizeAgentId(resolveDefaultAgentId(cfg)); const normalized = normalizeAgentId(trimmed); const agents = listAgents(cfg); - if (agents.length === 0) return normalized; + if (agents.length === 0) return sanitizeAgentId(trimmed); const match = agents.find((agent) => normalizeAgentId(agent.id) === normalized); - if (match?.id?.trim()) return normalizeAgentId(match.id.trim()); - return normalizeAgentId(resolveDefaultAgentId(cfg)); + if (match?.id?.trim()) return sanitizeAgentId(match.id.trim()); + return sanitizeAgentId(resolveDefaultAgentId(cfg)); } function matchesChannel( diff --git a/src/routing/session-key.ts b/src/routing/session-key.ts index 58aff29ee..37de9adab 100644 --- a/src/routing/session-key.ts +++ b/src/routing/session-key.ts @@ -64,6 +64,19 @@ export function normalizeAgentId(value: string | undefined | null): string { ); } +export function sanitizeAgentId(value: string | undefined | null): string { + const trimmed = (value ?? "").trim(); + if (!trimmed) return DEFAULT_AGENT_ID; + if (/^[a-z0-9][a-z0-9_-]{0,63}$/i.test(trimmed)) return trimmed; + return ( + trimmed + .replace(/[^a-z0-9_-]+/gi, "-") + .replace(/^-+/, "") + .replace(/-+$/, "") + .slice(0, 64) || DEFAULT_AGENT_ID + ); +} + export function normalizeAccountId(value: string | undefined | null): string { const trimmed = (value ?? "").trim(); if (!trimmed) return DEFAULT_ACCOUNT_ID;