diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ea5607fbf5..c31a0dd466c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ Docs: https://docs.openclaw.ai - Security/Gateway: block `system.execApprovals.*` via `node.invoke` (use `exec.approvals.node.*` instead). Thanks @christos-eth. - Security/Exec: harden PATH handling by disabling project-local `node_modules/.bin` bootstrapping by default, disallowing node-host `PATH` overrides, and spawning ACP servers via the current executable by default. Thanks @akhmittra. - CLI: fix lazy core command registration so top-level maintenance commands (`doctor`, `dashboard`, `reset`, `uninstall`) resolve correctly instead of exposing a non-functional `maintenance` placeholder command. +- Telegram: when `channels.telegram.commands.native` is `false`, exclude plugin commands from `setMyCommands` menu registration while keeping plugin slash handlers callable. (#15132) Thanks @Glucksberg. - Security/Agents: scope CLI process cleanup to owned child PIDs to avoid killing unrelated processes on shared hosts. Thanks @aether-ai-agent. - Security/Agents (macOS): prevent shell injection when writing Claude CLI keychain credentials. (#15924) Thanks @aether-ai-agent. - Security: fix Chutes manual OAuth login state validation (thanks @aether-ai-agent). (#16058) diff --git a/package.json b/package.json index 9efd75f972c..0fa53043822 100644 --- a/package.json +++ b/package.json @@ -103,8 +103,6 @@ "test:install:e2e:openai": "OPENCLAW_E2E_MODELS=openai CLAWDBOT_E2E_MODELS=openai bash scripts/test-install-sh-e2e-docker.sh", "test:install:smoke": "bash scripts/test-install-sh-docker.sh", "test:live": "OPENCLAW_LIVE_TEST=1 CLAWDBOT_LIVE_TEST=1 vitest run --config vitest.live.config.ts", - "test:lowcpu": "OPENCLAW_TEST_PROFILE=low NODE_OPTIONS=--max-old-space-size=8192 node scripts/test-parallel.mjs", - "test:macmini": "OPENCLAW_TEST_PROFILE=low NODE_OPTIONS=--max-old-space-size=8192 node scripts/test-parallel.mjs", "test:ui": "pnpm --dir ui test", "test:watch": "vitest", "tsgo:test": "tsgo -p tsconfig.test.json", diff --git a/scripts/test-parallel.mjs b/scripts/test-parallel.mjs index 4cd1a4c0e97..ccfaa111080 100644 --- a/scripts/test-parallel.mjs +++ b/scripts/test-parallel.mjs @@ -110,11 +110,6 @@ const silentArgs = const rawPassthroughArgs = process.argv.slice(2); const passthroughArgs = rawPassthroughArgs[0] === "--" ? rawPassthroughArgs.slice(1) : rawPassthroughArgs; -const rawTestProfile = process.env.OPENCLAW_TEST_PROFILE?.trim().toLowerCase(); -const testProfile = - rawTestProfile === "low" || rawTestProfile === "max" || rawTestProfile === "normal" - ? rawTestProfile - : "normal"; const overrideWorkers = Number.parseInt(process.env.OPENCLAW_TEST_WORKERS ?? "", 10); const resolvedOverride = Number.isFinite(overrideWorkers) && overrideWorkers > 0 ? overrideWorkers : null; @@ -127,29 +122,10 @@ const keepGatewaySerial = const parallelRuns = keepGatewaySerial ? runs.filter((entry) => entry.name !== "gateway") : runs; const serialRuns = keepGatewaySerial ? runs.filter((entry) => entry.name === "gateway") : []; const localWorkers = Math.max(4, Math.min(16, os.cpus().length)); -const defaultWorkerBudget = - testProfile === "low" - ? { - unit: 2, - unitIsolated: 1, - extensions: 1, - gateway: 1, - } - : testProfile === "max" - ? { - unit: localWorkers, - unitIsolated: Math.min(4, localWorkers), - extensions: Math.max(1, Math.min(6, Math.floor(localWorkers / 2))), - gateway: Math.max(1, Math.min(2, Math.floor(localWorkers / 4))), - } - : { - // Local `pnpm test` runs multiple vitest groups concurrently; - // keep per-group workers conservative to avoid pegging all cores. - unit: Math.max(2, Math.min(8, Math.floor(localWorkers / 2))), - unitIsolated: 1, - extensions: Math.max(1, Math.min(4, Math.floor(localWorkers / 4))), - gateway: 1, - }; +const defaultUnitWorkers = localWorkers; +// Local perf: extensions tend to be the critical path under parallel vitest runs; give them more headroom. +const defaultExtensionsWorkers = Math.max(1, Math.min(6, Math.floor(localWorkers / 2))); +const defaultGatewayWorkers = Math.max(1, Math.min(2, Math.floor(localWorkers / 4))); // Keep worker counts predictable for local runs; trim macOS CI workers to avoid worker crashes/OOM. // In CI on linux/windows, prefer Vitest defaults to avoid cross-test interference from lower worker counts. @@ -164,15 +140,16 @@ const maxWorkersForRun = (name) => { return 1; } if (name === "unit-isolated") { - return defaultWorkerBudget.unitIsolated; + // Local: allow a bit of parallelism while keeping this run stable. + return Math.min(4, localWorkers); } if (name === "extensions") { - return defaultWorkerBudget.extensions; + return defaultExtensionsWorkers; } if (name === "gateway") { - return defaultWorkerBudget.gateway; + return defaultGatewayWorkers; } - return defaultWorkerBudget.unit; + return defaultUnitWorkers; }; const WARNING_SUPPRESSION_FLAGS = [ diff --git a/src/telegram/bot-native-commands.plugin-auth.test.ts b/src/telegram/bot-native-commands.plugin-auth.test.ts index cabd3338019..8904fdb5401 100644 --- a/src/telegram/bot-native-commands.plugin-auth.test.ts +++ b/src/telegram/bot-native-commands.plugin-auth.test.ts @@ -23,7 +23,7 @@ vi.mock("../pairing/pairing-store.js", () => ({ })); describe("registerTelegramNativeCommands (plugin auth)", () => { - it("caps menu registration at 100 while leaving hidden plugin handlers available", () => { + it("does not register plugin commands in menu when native=false but keeps handlers available", () => { const specs = Array.from({ length: 101 }, (_, i) => ({ name: `cmd_${i}`, description: `Command ${i}`, @@ -73,14 +73,8 @@ describe("registerTelegramNativeCommands (plugin auth)", () => { opts: { token: "token" }, }); - const registered = setMyCommands.mock.calls[0]?.[0] as Array<{ - command: string; - description: string; - }>; - expect(registered).toHaveLength(100); - expect(registered[0]).toEqual({ command: "cmd_0", description: "Command 0" }); - expect(registered[99]).toEqual({ command: "cmd_99", description: "Command 99" }); - expect(log).toHaveBeenCalledWith(expect.stringContaining("registering first 100")); + expect(setMyCommands).not.toHaveBeenCalled(); + expect(log).not.toHaveBeenCalledWith(expect.stringContaining("registering first 100")); expect(Object.keys(handlers)).toHaveLength(101); }); diff --git a/src/telegram/bot-native-commands.ts b/src/telegram/bot-native-commands.ts index b15a761ffd9..61fd189423c 100644 --- a/src/telegram/bot-native-commands.ts +++ b/src/telegram/bot-native-commands.ts @@ -339,7 +339,7 @@ export const registerTelegramNativeCommands = ({ command: command.name, description: command.description, })), - ...pluginCatalog.commands, + ...(nativeEnabled ? pluginCatalog.commands : []), ...customCommands, ]; const { commandsToRegister, totalCommands, maxCommands, overflowCount } = @@ -357,7 +357,7 @@ export const registerTelegramNativeCommands = ({ // Keep hidden commands callable by registering handlers for the full catalog. syncTelegramMenuCommands({ bot, runtime, commandsToRegister }); - if (commandsToRegister.length > 0) { + if (commandsToRegister.length > 0 || pluginCatalog.commands.length > 0) { if (typeof (bot as unknown as { command?: unknown }).command !== "function") { logVerbose("telegram: bot.command unavailable; skipping native handlers"); } else {