2026-01-23 07:34:50 +00:00
|
|
|
import { spawn } from "node:child_process";
|
2026-02-07 08:27:50 +00:00
|
|
|
import fs from "node:fs";
|
2026-01-23 11:36:28 +00:00
|
|
|
import os from "node:os";
|
2026-02-07 08:27:50 +00:00
|
|
|
import path from "node:path";
|
2026-01-23 07:34:50 +00:00
|
|
|
|
2026-02-15 03:35:02 +00:00
|
|
|
// On Windows, `.cmd` launchers can fail with `spawn EINVAL` when invoked without a shell
|
|
|
|
|
// (especially under GitHub Actions + Git Bash). Use `shell: true` and let the shell resolve pnpm.
|
|
|
|
|
const pnpm = "pnpm";
|
2026-01-23 07:34:50 +00:00
|
|
|
|
2026-02-14 12:42:11 +00:00
|
|
|
const unitIsolatedFilesRaw = [
|
2026-02-13 04:30:39 +00:00
|
|
|
"src/plugins/loader.test.ts",
|
|
|
|
|
"src/plugins/tools.optional.test.ts",
|
|
|
|
|
"src/agents/session-tool-result-guard.tool-result-persist-hook.test.ts",
|
|
|
|
|
"src/security/fix.test.ts",
|
2026-02-22 21:59:13 +00:00
|
|
|
// Runtime source guard scans are sensitive to filesystem contention.
|
|
|
|
|
"src/security/temp-path-guard.test.ts",
|
2026-02-13 04:45:04 +00:00
|
|
|
"src/security/audit.test.ts",
|
2026-02-13 04:30:39 +00:00
|
|
|
"src/utils.test.ts",
|
|
|
|
|
"src/auto-reply/tool-meta.test.ts",
|
2026-02-13 04:45:04 +00:00
|
|
|
"src/auto-reply/envelope.test.ts",
|
2026-02-13 04:30:39 +00:00
|
|
|
"src/commands/auth-choice.test.ts",
|
2026-02-24 04:39:29 +00:00
|
|
|
// Process supervision + docker setup suites are stable but setup-heavy.
|
|
|
|
|
"src/process/supervisor/supervisor.test.ts",
|
|
|
|
|
"src/docker-setup.test.ts",
|
|
|
|
|
// Filesystem-heavy skills sync suite.
|
|
|
|
|
"src/agents/skills.build-workspace-skills-prompt.syncs-merged-skills-into-target-workspace.test.ts",
|
|
|
|
|
// Real git hook integration test; keep signal, move off unit-fast critical path.
|
|
|
|
|
"test/git-hooks-pre-commit.test.ts",
|
|
|
|
|
// Setup-heavy doctor command suites; keep them off the unit-fast critical path.
|
|
|
|
|
"src/commands/doctor.warns-state-directory-is-missing.test.ts",
|
|
|
|
|
"src/commands/doctor.warns-per-agent-sandbox-docker-browser-prune.test.ts",
|
|
|
|
|
"src/commands/doctor.runs-legacy-state-migrations-yes-mode-without.test.ts",
|
|
|
|
|
// Setup-heavy CLI update flow suite; move off unit-fast critical path.
|
|
|
|
|
"src/cli/update-cli.test.ts",
|
|
|
|
|
// Expensive schema build/bootstrap checks; keep coverage but run in isolated lane.
|
|
|
|
|
"src/config/schema.test.ts",
|
|
|
|
|
"src/config/schema.tags.test.ts",
|
|
|
|
|
// CLI smoke/agent flows are stable but setup-heavy.
|
|
|
|
|
"src/cli/program.smoke.test.ts",
|
|
|
|
|
"src/commands/agent.test.ts",
|
2026-02-14 03:29:55 +00:00
|
|
|
"src/media/store.test.ts",
|
2026-02-13 04:30:39 +00:00
|
|
|
"src/media/store.header-ext.test.ts",
|
2026-02-14 03:29:55 +00:00
|
|
|
"src/web/media.test.ts",
|
|
|
|
|
"src/web/auto-reply.web-auto-reply.falls-back-text-media-send-fails.test.ts",
|
2026-02-13 04:30:39 +00:00
|
|
|
"src/browser/server.covers-additional-endpoint-branches.test.ts",
|
|
|
|
|
"src/browser/server.post-tabs-open-profile-unknown-returns-404.test.ts",
|
|
|
|
|
"src/browser/server.agent-contract-snapshot-endpoints.test.ts",
|
|
|
|
|
"src/browser/server.agent-contract-form-layout-act-commands.test.ts",
|
|
|
|
|
"src/browser/server.skips-default-maxchars-explicitly-set-zero.test.ts",
|
|
|
|
|
"src/browser/server.auth-token-gates-http.test.ts",
|
2026-02-16 04:20:21 +00:00
|
|
|
// Keep this high-variance heavy file off the unit-fast critical path.
|
|
|
|
|
"src/auto-reply/reply.block-streaming.test.ts",
|
2026-02-16 04:31:26 +00:00
|
|
|
// Archive extraction/fixture-heavy suite; keep off unit-fast critical path.
|
|
|
|
|
"src/hooks/install.test.ts",
|
2026-02-22 21:59:13 +00:00
|
|
|
// Download/extraction safety cases can spike under unit-fast contention.
|
|
|
|
|
"src/agents/skills-install.download.test.ts",
|
|
|
|
|
// Heavy runner/exec/archive suites are stable but contend on shared resources under vmForks.
|
|
|
|
|
"src/agents/pi-embedded-runner.test.ts",
|
|
|
|
|
"src/agents/bash-tools.test.ts",
|
|
|
|
|
"src/agents/openclaw-tools.subagents.sessions-spawn.lifecycle.test.ts",
|
|
|
|
|
"src/agents/bash-tools.exec.background-abort.test.ts",
|
|
|
|
|
"src/agents/subagent-announce.format.test.ts",
|
|
|
|
|
"src/infra/archive.test.ts",
|
|
|
|
|
"src/cli/daemon-cli.coverage.test.ts",
|
|
|
|
|
// Model normalization test imports config/model discovery stack; keep off unit-fast critical path.
|
|
|
|
|
"src/agents/models-config.normalizes-gemini-3-ids-preview-google-providers.test.ts",
|
|
|
|
|
// Auth profile rotation suite is retry-heavy and high-variance under vmForks contention.
|
|
|
|
|
"src/agents/pi-embedded-runner.run-embedded-pi-agent.auth-profile-rotation.test.ts",
|
|
|
|
|
// Heavy trigger command scenarios; keep off unit-fast critical path to reduce contention noise.
|
|
|
|
|
"src/auto-reply/reply.triggers.trigger-handling.filters-usage-summary-current-model-provider.test.ts",
|
2026-02-23 13:30:47 +00:00
|
|
|
"src/auto-reply/reply.triggers.trigger-handling.targets-active-session-native-stop.test.ts",
|
2026-02-22 21:59:13 +00:00
|
|
|
"src/auto-reply/reply.triggers.group-intro-prompts.test.ts",
|
|
|
|
|
"src/auto-reply/reply.triggers.trigger-handling.handles-inline-commands-strips-it-before-agent.test.ts",
|
|
|
|
|
"src/web/auto-reply.web-auto-reply.compresses-common-formats-jpeg-cap.test.ts",
|
2026-02-16 04:34:48 +00:00
|
|
|
// Setup-heavy bot bootstrap suite.
|
|
|
|
|
"src/telegram/bot.create-telegram-bot.test.ts",
|
2026-02-16 04:50:19 +00:00
|
|
|
// Medium-heavy bot behavior suite; move off unit-fast critical path.
|
|
|
|
|
"src/telegram/bot.test.ts",
|
2026-02-16 04:34:48 +00:00
|
|
|
// Slack slash registration tests are setup-heavy and can bottleneck unit-fast.
|
|
|
|
|
"src/slack/monitor/slash.test.ts",
|
2026-02-15 04:49:48 +00:00
|
|
|
// Uses process-level unhandledRejection listeners; keep it off vmForks to avoid cross-file leakage.
|
2026-02-15 19:18:49 +00:00
|
|
|
"src/imessage/monitor.shutdown.unhandled-rejection.test.ts",
|
2026-02-13 04:30:39 +00:00
|
|
|
];
|
2026-02-14 12:42:11 +00:00
|
|
|
const unitIsolatedFiles = unitIsolatedFilesRaw.filter((file) => fs.existsSync(file));
|
2026-02-13 04:30:39 +00:00
|
|
|
|
|
|
|
|
const children = new Set();
|
|
|
|
|
const isCI = process.env.CI === "true" || process.env.GITHUB_ACTIONS === "true";
|
|
|
|
|
const isMacOS = process.platform === "darwin" || process.env.RUNNER_OS === "macOS";
|
|
|
|
|
const isWindows = process.platform === "win32" || process.env.RUNNER_OS === "Windows";
|
|
|
|
|
const isWindowsCi = isCI && isWindows;
|
2026-02-25 12:16:17 +02:00
|
|
|
const hostCpuCount = os.cpus().length;
|
|
|
|
|
const hostMemoryGiB = Math.floor(os.totalmem() / 1024 ** 3);
|
|
|
|
|
// Keep aggressive local defaults for high-memory workstations (Mac Studio class).
|
|
|
|
|
const highMemLocalHost = !isCI && hostMemoryGiB >= 96;
|
|
|
|
|
const lowMemLocalHost = !isCI && hostMemoryGiB < 64;
|
2026-02-13 08:15:25 -05:00
|
|
|
const nodeMajor = Number.parseInt(process.versions.node.split(".")[0] ?? "", 10);
|
2026-02-14 03:38:49 +00:00
|
|
|
// vmForks is a big win for transform/import heavy suites, but Node 24 had
|
2026-02-25 12:16:17 +02:00
|
|
|
// regressions with Vitest's vm runtime in this repo, and low-memory local hosts
|
|
|
|
|
// are more likely to hit per-worker V8 heap ceilings. Keep it opt-out via
|
2026-02-14 03:38:49 +00:00
|
|
|
// OPENCLAW_TEST_VM_FORKS=0, and let users force-enable with =1.
|
|
|
|
|
const supportsVmForks = Number.isFinite(nodeMajor) ? nodeMajor !== 24 : true;
|
2026-02-13 04:30:39 +00:00
|
|
|
const useVmForks =
|
|
|
|
|
process.env.OPENCLAW_TEST_VM_FORKS === "1" ||
|
2026-02-25 12:16:17 +02:00
|
|
|
(process.env.OPENCLAW_TEST_VM_FORKS !== "0" && !isWindows && supportsVmForks && !lowMemLocalHost);
|
2026-02-13 13:28:23 +00:00
|
|
|
const disableIsolation = process.env.OPENCLAW_TEST_NO_ISOLATE === "1";
|
2026-01-23 07:34:50 +00:00
|
|
|
const runs = [
|
2026-02-13 04:30:39 +00:00
|
|
|
...(useVmForks
|
|
|
|
|
? [
|
|
|
|
|
{
|
|
|
|
|
name: "unit-fast",
|
|
|
|
|
args: [
|
|
|
|
|
"vitest",
|
|
|
|
|
"run",
|
|
|
|
|
"--config",
|
|
|
|
|
"vitest.unit.config.ts",
|
|
|
|
|
"--pool=vmForks",
|
2026-02-13 13:28:23 +00:00
|
|
|
...(disableIsolation ? ["--isolate=false"] : []),
|
2026-02-13 04:30:39 +00:00
|
|
|
...unitIsolatedFiles.flatMap((file) => ["--exclude", file]),
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "unit-isolated",
|
|
|
|
|
args: [
|
|
|
|
|
"vitest",
|
|
|
|
|
"run",
|
|
|
|
|
"--config",
|
|
|
|
|
"vitest.unit.config.ts",
|
|
|
|
|
"--pool=forks",
|
|
|
|
|
...unitIsolatedFiles,
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
]
|
|
|
|
|
: [
|
|
|
|
|
{
|
|
|
|
|
name: "unit",
|
|
|
|
|
args: ["vitest", "run", "--config", "vitest.unit.config.ts"],
|
|
|
|
|
},
|
|
|
|
|
]),
|
2026-01-23 11:36:28 +00:00
|
|
|
{
|
|
|
|
|
name: "extensions",
|
2026-02-13 04:30:39 +00:00
|
|
|
args: [
|
|
|
|
|
"vitest",
|
|
|
|
|
"run",
|
|
|
|
|
"--config",
|
|
|
|
|
"vitest.extensions.config.ts",
|
|
|
|
|
...(useVmForks ? ["--pool=vmForks"] : []),
|
|
|
|
|
],
|
2026-01-23 11:36:28 +00:00
|
|
|
},
|
2026-01-23 07:34:50 +00:00
|
|
|
{
|
|
|
|
|
name: "gateway",
|
2026-02-13 04:30:39 +00:00
|
|
|
args: [
|
|
|
|
|
"vitest",
|
|
|
|
|
"run",
|
|
|
|
|
"--config",
|
|
|
|
|
"vitest.gateway.config.ts",
|
2026-02-14 20:32:54 +01:00
|
|
|
// Gateway tests are sensitive to vmForks behavior (global state + env stubs).
|
|
|
|
|
// Keep them on process forks for determinism even when other suites use vmForks.
|
|
|
|
|
"--pool=forks",
|
2026-02-13 04:30:39 +00:00
|
|
|
],
|
2026-01-23 07:34:50 +00:00
|
|
|
},
|
|
|
|
|
];
|
2026-01-30 03:15:10 +01:00
|
|
|
const shardOverride = Number.parseInt(process.env.OPENCLAW_TEST_SHARDS ?? "", 10);
|
2026-02-26 00:33:36 -06:00
|
|
|
const configuredShardCount =
|
|
|
|
|
Number.isFinite(shardOverride) && shardOverride > 1 ? shardOverride : null;
|
|
|
|
|
const shardCount = configuredShardCount ?? (isWindowsCi ? 2 : 1);
|
|
|
|
|
const shardIndexOverride = (() => {
|
|
|
|
|
const parsed = Number.parseInt(process.env.OPENCLAW_TEST_SHARD_INDEX ?? "", 10);
|
|
|
|
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
|
|
|
|
|
})();
|
|
|
|
|
|
|
|
|
|
if (shardIndexOverride !== null && shardCount <= 1) {
|
|
|
|
|
console.error(
|
|
|
|
|
`[test-parallel] OPENCLAW_TEST_SHARD_INDEX=${String(
|
|
|
|
|
shardIndexOverride,
|
|
|
|
|
)} requires OPENCLAW_TEST_SHARDS>1.`,
|
|
|
|
|
);
|
|
|
|
|
process.exit(2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (shardIndexOverride !== null && shardIndexOverride > shardCount) {
|
|
|
|
|
console.error(
|
|
|
|
|
`[test-parallel] OPENCLAW_TEST_SHARD_INDEX=${String(
|
|
|
|
|
shardIndexOverride,
|
|
|
|
|
)} exceeds OPENCLAW_TEST_SHARDS=${String(shardCount)}.`,
|
|
|
|
|
);
|
|
|
|
|
process.exit(2);
|
|
|
|
|
}
|
2026-02-06 23:18:19 -03:00
|
|
|
const windowsCiArgs = isWindowsCi ? ["--dangerouslyIgnoreUnhandledErrors"] : [];
|
2026-02-12 17:59:44 +00:00
|
|
|
const silentArgs =
|
|
|
|
|
process.env.OPENCLAW_TEST_SHOW_PASSED_LOGS === "1" ? [] : ["--silent=passed-only"];
|
2026-02-07 20:02:32 -08:00
|
|
|
const rawPassthroughArgs = process.argv.slice(2);
|
|
|
|
|
const passthroughArgs =
|
|
|
|
|
rawPassthroughArgs[0] === "--" ? rawPassthroughArgs.slice(1) : rawPassthroughArgs;
|
2026-02-15 07:40:13 -06:00
|
|
|
const rawTestProfile = process.env.OPENCLAW_TEST_PROFILE?.trim().toLowerCase();
|
|
|
|
|
const testProfile =
|
|
|
|
|
rawTestProfile === "low" ||
|
|
|
|
|
rawTestProfile === "max" ||
|
|
|
|
|
rawTestProfile === "normal" ||
|
|
|
|
|
rawTestProfile === "serial"
|
|
|
|
|
? rawTestProfile
|
|
|
|
|
: "normal";
|
2026-01-30 03:15:10 +01:00
|
|
|
const overrideWorkers = Number.parseInt(process.env.OPENCLAW_TEST_WORKERS ?? "", 10);
|
2026-01-31 21:21:09 +09:00
|
|
|
const resolvedOverride =
|
|
|
|
|
Number.isFinite(overrideWorkers) && overrideWorkers > 0 ? overrideWorkers : null;
|
2026-02-23 20:48:05 +02:00
|
|
|
const parallelGatewayEnabled =
|
|
|
|
|
process.env.OPENCLAW_TEST_PARALLEL_GATEWAY === "1" || (!isCI && highMemLocalHost);
|
|
|
|
|
// Keep gateway serial by default except when explicitly requested or on high-memory local hosts.
|
2026-02-12 17:59:44 +00:00
|
|
|
const keepGatewaySerial =
|
|
|
|
|
isWindowsCi ||
|
|
|
|
|
process.env.OPENCLAW_TEST_SERIAL_GATEWAY === "1" ||
|
2026-02-15 07:40:13 -06:00
|
|
|
testProfile === "serial" ||
|
2026-02-23 20:48:05 +02:00
|
|
|
!parallelGatewayEnabled;
|
2026-02-12 17:59:44 +00:00
|
|
|
const parallelRuns = keepGatewaySerial ? runs.filter((entry) => entry.name !== "gateway") : runs;
|
|
|
|
|
const serialRuns = keepGatewaySerial ? runs.filter((entry) => entry.name === "gateway") : [];
|
2026-02-22 21:59:13 +00:00
|
|
|
const baseLocalWorkers = Math.max(4, Math.min(16, hostCpuCount));
|
|
|
|
|
const loadAwareDisabledRaw = process.env.OPENCLAW_TEST_LOAD_AWARE?.trim().toLowerCase();
|
|
|
|
|
const loadAwareDisabled = loadAwareDisabledRaw === "0" || loadAwareDisabledRaw === "false";
|
|
|
|
|
const loadRatio =
|
|
|
|
|
!isCI && !loadAwareDisabled && process.platform !== "win32" && hostCpuCount > 0
|
|
|
|
|
? os.loadavg()[0] / hostCpuCount
|
|
|
|
|
: 0;
|
|
|
|
|
// Keep the fast-path unchanged on normal load; only throttle under extreme host pressure.
|
|
|
|
|
const extremeLoadScale = loadRatio >= 1.1 ? 0.75 : loadRatio >= 1 ? 0.85 : 1;
|
|
|
|
|
const localWorkers = Math.max(4, Math.min(16, Math.floor(baseLocalWorkers * extremeLoadScale)));
|
2026-02-15 07:40:13 -06:00
|
|
|
const defaultWorkerBudget =
|
|
|
|
|
testProfile === "low"
|
|
|
|
|
? {
|
|
|
|
|
unit: 2,
|
|
|
|
|
unitIsolated: 1,
|
2026-02-25 12:16:17 +02:00
|
|
|
extensions: 4,
|
2026-02-15 07:40:13 -06:00
|
|
|
gateway: 1,
|
|
|
|
|
}
|
|
|
|
|
: testProfile === "serial"
|
|
|
|
|
? {
|
|
|
|
|
unit: 1,
|
|
|
|
|
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))),
|
|
|
|
|
}
|
2026-02-23 20:48:05 +02:00
|
|
|
: highMemLocalHost
|
|
|
|
|
? {
|
|
|
|
|
// High-memory local hosts can prioritize wall-clock speed.
|
|
|
|
|
unit: Math.max(4, Math.min(14, Math.floor((localWorkers * 7) / 8))),
|
|
|
|
|
unitIsolated: Math.max(1, Math.min(2, Math.floor(localWorkers / 6) || 1)),
|
|
|
|
|
extensions: Math.max(1, Math.min(4, Math.floor(localWorkers / 4))),
|
2026-02-24 04:39:29 +00:00
|
|
|
gateway: Math.max(2, Math.min(6, Math.floor(localWorkers / 2))),
|
2026-02-23 20:48:05 +02:00
|
|
|
}
|
|
|
|
|
: lowMemLocalHost
|
|
|
|
|
? {
|
|
|
|
|
// Sub-64 GiB local hosts are prone to OOM with large vmFork runs.
|
|
|
|
|
unit: 2,
|
|
|
|
|
unitIsolated: 1,
|
2026-02-25 12:16:17 +02:00
|
|
|
extensions: 4,
|
2026-02-23 20:48:05 +02:00
|
|
|
gateway: 1,
|
|
|
|
|
}
|
|
|
|
|
: {
|
|
|
|
|
// 64-95 GiB local hosts: conservative split with some parallel headroom.
|
|
|
|
|
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,
|
|
|
|
|
};
|
2026-02-07 07:57:50 +00:00
|
|
|
|
2026-01-25 07:22:36 -05:00
|
|
|
// Keep worker counts predictable for local runs; trim macOS CI workers to avoid worker crashes/OOM.
|
2026-01-25 10:18:51 +05:30
|
|
|
// In CI on linux/windows, prefer Vitest defaults to avoid cross-test interference from lower worker counts.
|
2026-02-07 07:57:50 +00:00
|
|
|
const maxWorkersForRun = (name) => {
|
|
|
|
|
if (resolvedOverride) {
|
|
|
|
|
return resolvedOverride;
|
|
|
|
|
}
|
|
|
|
|
if (isCI && !isMacOS) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
if (isCI && isMacOS) {
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2026-02-13 04:30:39 +00:00
|
|
|
if (name === "unit-isolated") {
|
2026-02-15 07:40:13 -06:00
|
|
|
return defaultWorkerBudget.unitIsolated;
|
2026-02-13 04:30:39 +00:00
|
|
|
}
|
2026-02-07 07:57:50 +00:00
|
|
|
if (name === "extensions") {
|
2026-02-15 07:40:13 -06:00
|
|
|
return defaultWorkerBudget.extensions;
|
2026-02-07 07:57:50 +00:00
|
|
|
}
|
|
|
|
|
if (name === "gateway") {
|
2026-02-15 07:40:13 -06:00
|
|
|
return defaultWorkerBudget.gateway;
|
2026-02-07 07:57:50 +00:00
|
|
|
}
|
2026-02-15 07:40:13 -06:00
|
|
|
return defaultWorkerBudget.unit;
|
2026-02-07 07:57:50 +00:00
|
|
|
};
|
2026-01-23 07:34:50 +00:00
|
|
|
|
2026-01-24 11:16:41 +00:00
|
|
|
const WARNING_SUPPRESSION_FLAGS = [
|
|
|
|
|
"--disable-warning=ExperimentalWarning",
|
|
|
|
|
"--disable-warning=DEP0040",
|
|
|
|
|
"--disable-warning=DEP0060",
|
2026-02-13 13:28:23 +00:00
|
|
|
"--disable-warning=MaxListenersExceededWarning",
|
2026-01-24 11:16:41 +00:00
|
|
|
];
|
|
|
|
|
|
2026-02-15 05:06:58 +00:00
|
|
|
const DEFAULT_CI_MAX_OLD_SPACE_SIZE_MB = 4096;
|
|
|
|
|
const maxOldSpaceSizeMb = (() => {
|
|
|
|
|
// CI can hit Node heap limits (especially on large suites). Allow override, default to 4GB.
|
|
|
|
|
const raw = process.env.OPENCLAW_TEST_MAX_OLD_SPACE_SIZE_MB ?? "";
|
|
|
|
|
const parsed = Number.parseInt(raw, 10);
|
|
|
|
|
if (Number.isFinite(parsed) && parsed > 0) {
|
|
|
|
|
return parsed;
|
|
|
|
|
}
|
|
|
|
|
if (isCI && !isWindows) {
|
|
|
|
|
return DEFAULT_CI_MAX_OLD_SPACE_SIZE_MB;
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
})();
|
|
|
|
|
|
2026-02-07 08:27:50 +00:00
|
|
|
function resolveReportDir() {
|
|
|
|
|
const raw = process.env.OPENCLAW_VITEST_REPORT_DIR?.trim();
|
|
|
|
|
if (!raw) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
try {
|
|
|
|
|
fs.mkdirSync(raw, { recursive: true });
|
|
|
|
|
} catch {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
return raw;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function buildReporterArgs(entry, extraArgs) {
|
|
|
|
|
const reportDir = resolveReportDir();
|
|
|
|
|
if (!reportDir) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Vitest supports both `--shard 1/2` and `--shard=1/2`. We use it in the
|
|
|
|
|
// split-arg form, so we need to read the next arg to avoid overwriting reports.
|
|
|
|
|
const shardIndex = extraArgs.findIndex((arg) => arg === "--shard");
|
|
|
|
|
const inlineShardArg = extraArgs.find(
|
|
|
|
|
(arg) => typeof arg === "string" && arg.startsWith("--shard="),
|
|
|
|
|
);
|
|
|
|
|
const shardValue =
|
|
|
|
|
shardIndex >= 0 && typeof extraArgs[shardIndex + 1] === "string"
|
|
|
|
|
? extraArgs[shardIndex + 1]
|
|
|
|
|
: typeof inlineShardArg === "string"
|
|
|
|
|
? inlineShardArg.slice("--shard=".length)
|
|
|
|
|
: "";
|
|
|
|
|
const shardSuffix = shardValue
|
|
|
|
|
? `-shard${String(shardValue).replaceAll("/", "of").replaceAll(" ", "")}`
|
|
|
|
|
: "";
|
|
|
|
|
|
|
|
|
|
const outputFile = path.join(reportDir, `vitest-${entry.name}${shardSuffix}.json`);
|
|
|
|
|
return ["--reporter=default", "--reporter=json", "--outputFile", outputFile];
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-27 16:39:28 +00:00
|
|
|
const runOnce = (entry, extraArgs = []) =>
|
2026-01-23 07:34:50 +00:00
|
|
|
new Promise((resolve) => {
|
2026-02-07 07:57:50 +00:00
|
|
|
const maxWorkers = maxWorkersForRun(entry.name);
|
2026-02-07 08:27:50 +00:00
|
|
|
const reporterArgs = buildReporterArgs(entry, extraArgs);
|
2026-02-25 12:16:17 +02:00
|
|
|
// vmForks with a single worker has shown cross-file leakage in extension suites.
|
|
|
|
|
// Fall back to process forks when we intentionally clamp that lane to one worker.
|
|
|
|
|
const entryArgs =
|
|
|
|
|
entry.name === "extensions" && maxWorkers === 1 && entry.args.includes("--pool=vmForks")
|
|
|
|
|
? entry.args.map((arg) => (arg === "--pool=vmForks" ? "--pool=forks" : arg))
|
|
|
|
|
: entry.args;
|
2026-01-27 16:39:28 +00:00
|
|
|
const args = maxWorkers
|
2026-02-07 08:27:50 +00:00
|
|
|
? [
|
2026-02-25 12:16:17 +02:00
|
|
|
...entryArgs,
|
2026-02-07 08:27:50 +00:00
|
|
|
"--maxWorkers",
|
|
|
|
|
String(maxWorkers),
|
2026-02-12 17:59:44 +00:00
|
|
|
...silentArgs,
|
2026-02-07 08:27:50 +00:00
|
|
|
...reporterArgs,
|
|
|
|
|
...windowsCiArgs,
|
|
|
|
|
...extraArgs,
|
|
|
|
|
]
|
2026-02-25 12:16:17 +02:00
|
|
|
: [...entryArgs, ...silentArgs, ...reporterArgs, ...windowsCiArgs, ...extraArgs];
|
2026-01-24 11:16:41 +00:00
|
|
|
const nodeOptions = process.env.NODE_OPTIONS ?? "";
|
|
|
|
|
const nextNodeOptions = WARNING_SUPPRESSION_FLAGS.reduce(
|
|
|
|
|
(acc, flag) => (acc.includes(flag) ? acc : `${acc} ${flag}`.trim()),
|
|
|
|
|
nodeOptions,
|
|
|
|
|
);
|
2026-02-15 05:06:58 +00:00
|
|
|
const heapFlag =
|
|
|
|
|
maxOldSpaceSizeMb && !nextNodeOptions.includes("--max-old-space-size=")
|
|
|
|
|
? `--max-old-space-size=${maxOldSpaceSizeMb}`
|
|
|
|
|
: null;
|
|
|
|
|
const resolvedNodeOptions = heapFlag
|
|
|
|
|
? `${nextNodeOptions} ${heapFlag}`.trim()
|
|
|
|
|
: nextNodeOptions;
|
2026-02-15 03:35:02 +00:00
|
|
|
let child;
|
|
|
|
|
try {
|
|
|
|
|
child = spawn(pnpm, args, {
|
|
|
|
|
stdio: "inherit",
|
2026-02-15 05:06:58 +00:00
|
|
|
env: { ...process.env, VITEST_GROUP: entry.name, NODE_OPTIONS: resolvedNodeOptions },
|
2026-02-15 03:35:02 +00:00
|
|
|
shell: isWindows,
|
|
|
|
|
});
|
|
|
|
|
} catch (err) {
|
|
|
|
|
console.error(`[test-parallel] spawn failed: ${String(err)}`);
|
|
|
|
|
resolve(1);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2026-01-23 07:34:50 +00:00
|
|
|
children.add(child);
|
2026-02-15 03:35:02 +00:00
|
|
|
child.on("error", (err) => {
|
|
|
|
|
console.error(`[test-parallel] child error: ${String(err)}`);
|
|
|
|
|
});
|
2026-01-23 07:34:50 +00:00
|
|
|
child.on("exit", (code, signal) => {
|
|
|
|
|
children.delete(child);
|
|
|
|
|
resolve(code ?? (signal ? 1 : 0));
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2026-01-27 16:39:28 +00:00
|
|
|
const run = async (entry) => {
|
2026-01-31 21:29:14 +09:00
|
|
|
if (shardCount <= 1) {
|
|
|
|
|
return runOnce(entry);
|
|
|
|
|
}
|
2026-02-26 00:33:36 -06:00
|
|
|
if (shardIndexOverride !== null) {
|
|
|
|
|
return runOnce(entry, ["--shard", `${shardIndexOverride}/${shardCount}`]);
|
|
|
|
|
}
|
2026-01-27 16:39:28 +00:00
|
|
|
for (let shardIndex = 1; shardIndex <= shardCount; shardIndex += 1) {
|
|
|
|
|
// eslint-disable-next-line no-await-in-loop
|
|
|
|
|
const code = await runOnce(entry, ["--shard", `${shardIndex}/${shardCount}`]);
|
2026-01-31 21:29:14 +09:00
|
|
|
if (code !== 0) {
|
|
|
|
|
return code;
|
|
|
|
|
}
|
2026-01-27 16:39:28 +00:00
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
};
|
|
|
|
|
|
2026-01-23 07:34:50 +00:00
|
|
|
const shutdown = (signal) => {
|
|
|
|
|
for (const child of children) {
|
|
|
|
|
child.kill(signal);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
process.on("SIGINT", () => shutdown("SIGINT"));
|
|
|
|
|
process.on("SIGTERM", () => shutdown("SIGTERM"));
|
|
|
|
|
|
fix: cron scheduler reliability, store hardening, and UX improvements (#10776)
* refactor: update cron job wake mode and run mode handling
- Changed default wake mode from 'next-heartbeat' to 'now' in CronJobEditor and related CLI commands.
- Updated cron-tool tests to reflect changes in run mode, introducing 'due' and 'force' options.
- Enhanced cron-tool logic to handle new run modes and ensure compatibility with existing job structures.
- Added new tests for delivery plan consistency and job execution behavior under various conditions.
- Improved normalization functions to handle wake mode and session target casing.
This refactor aims to streamline cron job configurations and enhance the overall user experience with clearer defaults and improved functionality.
* test: enhance cron job functionality and UI
- Added tests to ensure the isolated agent correctly announces the final payload text when delivering messages via Telegram.
- Implemented a new function to pick the last deliverable payload from a list of delivery payloads.
- Enhanced the cron service to maintain legacy "every" jobs while minute cron jobs recompute schedules.
- Updated the cron store migration tests to verify the addition of anchorMs to legacy every schedules.
- Improved the UI for displaying cron job details, including job state and delivery information, with new styles and layout adjustments.
These changes aim to improve the reliability and user experience of the cron job system.
* test: enhance sessions thinking level handling
- Added tests to verify that the correct thinking levels are applied during session spawning.
- Updated the sessions-spawn-tool to include a new parameter for overriding thinking levels.
- Enhanced the UI to support additional thinking levels, including "xhigh" and "full", and improved the handling of current options in dropdowns.
These changes aim to improve the flexibility and accuracy of thinking level configurations in session management.
* feat: enhance session management and cron job functionality
- Introduced passthrough arguments in the test-parallel script to allow for flexible command-line options.
- Updated session handling to hide cron run alias session keys from the sessions list, improving clarity.
- Enhanced the cron service to accurately record job start times and durations, ensuring better tracking of job execution.
- Added tests to verify the correct behavior of the cron service under various conditions, including zero-delay timers.
These changes aim to improve the usability and reliability of session and cron job management.
* feat: implement job running state checks in cron service
- Added functionality to prevent manual job runs if a job is already in progress, enhancing job management.
- Updated the `isJobDue` function to include checks for running jobs, ensuring accurate scheduling.
- Enhanced the `run` function to return a specific reason when a job is already running.
- Introduced a new test case to verify the behavior of forced manual runs during active job execution.
These changes aim to improve the reliability and clarity of cron job execution and management.
* feat: add session ID and key to CronRunLogEntry model
- Introduced `sessionid` and `sessionkey` properties to the `CronRunLogEntry` struct for enhanced tracking of session-related information.
- Updated the initializer and Codable conformance to accommodate the new properties, ensuring proper serialization and deserialization.
These changes aim to improve the granularity of logging and session management within the cron job system.
* fix: improve session display name resolution
- Updated the `resolveSessionDisplayName` function to ensure that both label and displayName are trimmed and default to an empty string if not present.
- Enhanced the logic to prevent returning the key if it matches the label or displayName, improving clarity in session naming.
These changes aim to enhance the accuracy and usability of session display names in the UI.
* perf: skip cron store persist when idle timer tick produces no changes
recomputeNextRuns now returns a boolean indicating whether any job
state was mutated. The idle path in onTimer only persists when the
return value is true, eliminating unnecessary file writes every 60s
for far-future or idle schedules.
* fix: prep for merge - explicit delivery mode migration, docs + changelog (#10776) (thanks @tyler6204)
2026-02-06 18:03:03 -08:00
|
|
|
if (passthroughArgs.length > 0) {
|
2026-02-07 08:27:50 +00:00
|
|
|
const maxWorkers = maxWorkersForRun("unit");
|
fix: cron scheduler reliability, store hardening, and UX improvements (#10776)
* refactor: update cron job wake mode and run mode handling
- Changed default wake mode from 'next-heartbeat' to 'now' in CronJobEditor and related CLI commands.
- Updated cron-tool tests to reflect changes in run mode, introducing 'due' and 'force' options.
- Enhanced cron-tool logic to handle new run modes and ensure compatibility with existing job structures.
- Added new tests for delivery plan consistency and job execution behavior under various conditions.
- Improved normalization functions to handle wake mode and session target casing.
This refactor aims to streamline cron job configurations and enhance the overall user experience with clearer defaults and improved functionality.
* test: enhance cron job functionality and UI
- Added tests to ensure the isolated agent correctly announces the final payload text when delivering messages via Telegram.
- Implemented a new function to pick the last deliverable payload from a list of delivery payloads.
- Enhanced the cron service to maintain legacy "every" jobs while minute cron jobs recompute schedules.
- Updated the cron store migration tests to verify the addition of anchorMs to legacy every schedules.
- Improved the UI for displaying cron job details, including job state and delivery information, with new styles and layout adjustments.
These changes aim to improve the reliability and user experience of the cron job system.
* test: enhance sessions thinking level handling
- Added tests to verify that the correct thinking levels are applied during session spawning.
- Updated the sessions-spawn-tool to include a new parameter for overriding thinking levels.
- Enhanced the UI to support additional thinking levels, including "xhigh" and "full", and improved the handling of current options in dropdowns.
These changes aim to improve the flexibility and accuracy of thinking level configurations in session management.
* feat: enhance session management and cron job functionality
- Introduced passthrough arguments in the test-parallel script to allow for flexible command-line options.
- Updated session handling to hide cron run alias session keys from the sessions list, improving clarity.
- Enhanced the cron service to accurately record job start times and durations, ensuring better tracking of job execution.
- Added tests to verify the correct behavior of the cron service under various conditions, including zero-delay timers.
These changes aim to improve the usability and reliability of session and cron job management.
* feat: implement job running state checks in cron service
- Added functionality to prevent manual job runs if a job is already in progress, enhancing job management.
- Updated the `isJobDue` function to include checks for running jobs, ensuring accurate scheduling.
- Enhanced the `run` function to return a specific reason when a job is already running.
- Introduced a new test case to verify the behavior of forced manual runs during active job execution.
These changes aim to improve the reliability and clarity of cron job execution and management.
* feat: add session ID and key to CronRunLogEntry model
- Introduced `sessionid` and `sessionkey` properties to the `CronRunLogEntry` struct for enhanced tracking of session-related information.
- Updated the initializer and Codable conformance to accommodate the new properties, ensuring proper serialization and deserialization.
These changes aim to improve the granularity of logging and session management within the cron job system.
* fix: improve session display name resolution
- Updated the `resolveSessionDisplayName` function to ensure that both label and displayName are trimmed and default to an empty string if not present.
- Enhanced the logic to prevent returning the key if it matches the label or displayName, improving clarity in session naming.
These changes aim to enhance the accuracy and usability of session display names in the UI.
* perf: skip cron store persist when idle timer tick produces no changes
recomputeNextRuns now returns a boolean indicating whether any job
state was mutated. The idle path in onTimer only persists when the
return value is true, eliminating unnecessary file writes every 60s
for far-future or idle schedules.
* fix: prep for merge - explicit delivery mode migration, docs + changelog (#10776) (thanks @tyler6204)
2026-02-06 18:03:03 -08:00
|
|
|
const args = maxWorkers
|
2026-02-12 17:59:44 +00:00
|
|
|
? [
|
|
|
|
|
"vitest",
|
|
|
|
|
"run",
|
|
|
|
|
"--maxWorkers",
|
|
|
|
|
String(maxWorkers),
|
|
|
|
|
...silentArgs,
|
|
|
|
|
...windowsCiArgs,
|
|
|
|
|
...passthroughArgs,
|
|
|
|
|
]
|
|
|
|
|
: ["vitest", "run", ...silentArgs, ...windowsCiArgs, ...passthroughArgs];
|
fix: cron scheduler reliability, store hardening, and UX improvements (#10776)
* refactor: update cron job wake mode and run mode handling
- Changed default wake mode from 'next-heartbeat' to 'now' in CronJobEditor and related CLI commands.
- Updated cron-tool tests to reflect changes in run mode, introducing 'due' and 'force' options.
- Enhanced cron-tool logic to handle new run modes and ensure compatibility with existing job structures.
- Added new tests for delivery plan consistency and job execution behavior under various conditions.
- Improved normalization functions to handle wake mode and session target casing.
This refactor aims to streamline cron job configurations and enhance the overall user experience with clearer defaults and improved functionality.
* test: enhance cron job functionality and UI
- Added tests to ensure the isolated agent correctly announces the final payload text when delivering messages via Telegram.
- Implemented a new function to pick the last deliverable payload from a list of delivery payloads.
- Enhanced the cron service to maintain legacy "every" jobs while minute cron jobs recompute schedules.
- Updated the cron store migration tests to verify the addition of anchorMs to legacy every schedules.
- Improved the UI for displaying cron job details, including job state and delivery information, with new styles and layout adjustments.
These changes aim to improve the reliability and user experience of the cron job system.
* test: enhance sessions thinking level handling
- Added tests to verify that the correct thinking levels are applied during session spawning.
- Updated the sessions-spawn-tool to include a new parameter for overriding thinking levels.
- Enhanced the UI to support additional thinking levels, including "xhigh" and "full", and improved the handling of current options in dropdowns.
These changes aim to improve the flexibility and accuracy of thinking level configurations in session management.
* feat: enhance session management and cron job functionality
- Introduced passthrough arguments in the test-parallel script to allow for flexible command-line options.
- Updated session handling to hide cron run alias session keys from the sessions list, improving clarity.
- Enhanced the cron service to accurately record job start times and durations, ensuring better tracking of job execution.
- Added tests to verify the correct behavior of the cron service under various conditions, including zero-delay timers.
These changes aim to improve the usability and reliability of session and cron job management.
* feat: implement job running state checks in cron service
- Added functionality to prevent manual job runs if a job is already in progress, enhancing job management.
- Updated the `isJobDue` function to include checks for running jobs, ensuring accurate scheduling.
- Enhanced the `run` function to return a specific reason when a job is already running.
- Introduced a new test case to verify the behavior of forced manual runs during active job execution.
These changes aim to improve the reliability and clarity of cron job execution and management.
* feat: add session ID and key to CronRunLogEntry model
- Introduced `sessionid` and `sessionkey` properties to the `CronRunLogEntry` struct for enhanced tracking of session-related information.
- Updated the initializer and Codable conformance to accommodate the new properties, ensuring proper serialization and deserialization.
These changes aim to improve the granularity of logging and session management within the cron job system.
* fix: improve session display name resolution
- Updated the `resolveSessionDisplayName` function to ensure that both label and displayName are trimmed and default to an empty string if not present.
- Enhanced the logic to prevent returning the key if it matches the label or displayName, improving clarity in session naming.
These changes aim to enhance the accuracy and usability of session display names in the UI.
* perf: skip cron store persist when idle timer tick produces no changes
recomputeNextRuns now returns a boolean indicating whether any job
state was mutated. The idle path in onTimer only persists when the
return value is true, eliminating unnecessary file writes every 60s
for far-future or idle schedules.
* fix: prep for merge - explicit delivery mode migration, docs + changelog (#10776) (thanks @tyler6204)
2026-02-06 18:03:03 -08:00
|
|
|
const nodeOptions = process.env.NODE_OPTIONS ?? "";
|
|
|
|
|
const nextNodeOptions = WARNING_SUPPRESSION_FLAGS.reduce(
|
|
|
|
|
(acc, flag) => (acc.includes(flag) ? acc : `${acc} ${flag}`.trim()),
|
|
|
|
|
nodeOptions,
|
|
|
|
|
);
|
|
|
|
|
const code = await new Promise((resolve) => {
|
2026-02-15 03:35:02 +00:00
|
|
|
let child;
|
|
|
|
|
try {
|
|
|
|
|
child = spawn(pnpm, args, {
|
|
|
|
|
stdio: "inherit",
|
|
|
|
|
env: { ...process.env, NODE_OPTIONS: nextNodeOptions },
|
|
|
|
|
shell: isWindows,
|
|
|
|
|
});
|
|
|
|
|
} catch (err) {
|
|
|
|
|
console.error(`[test-parallel] spawn failed: ${String(err)}`);
|
|
|
|
|
resolve(1);
|
|
|
|
|
return;
|
|
|
|
|
}
|
fix: cron scheduler reliability, store hardening, and UX improvements (#10776)
* refactor: update cron job wake mode and run mode handling
- Changed default wake mode from 'next-heartbeat' to 'now' in CronJobEditor and related CLI commands.
- Updated cron-tool tests to reflect changes in run mode, introducing 'due' and 'force' options.
- Enhanced cron-tool logic to handle new run modes and ensure compatibility with existing job structures.
- Added new tests for delivery plan consistency and job execution behavior under various conditions.
- Improved normalization functions to handle wake mode and session target casing.
This refactor aims to streamline cron job configurations and enhance the overall user experience with clearer defaults and improved functionality.
* test: enhance cron job functionality and UI
- Added tests to ensure the isolated agent correctly announces the final payload text when delivering messages via Telegram.
- Implemented a new function to pick the last deliverable payload from a list of delivery payloads.
- Enhanced the cron service to maintain legacy "every" jobs while minute cron jobs recompute schedules.
- Updated the cron store migration tests to verify the addition of anchorMs to legacy every schedules.
- Improved the UI for displaying cron job details, including job state and delivery information, with new styles and layout adjustments.
These changes aim to improve the reliability and user experience of the cron job system.
* test: enhance sessions thinking level handling
- Added tests to verify that the correct thinking levels are applied during session spawning.
- Updated the sessions-spawn-tool to include a new parameter for overriding thinking levels.
- Enhanced the UI to support additional thinking levels, including "xhigh" and "full", and improved the handling of current options in dropdowns.
These changes aim to improve the flexibility and accuracy of thinking level configurations in session management.
* feat: enhance session management and cron job functionality
- Introduced passthrough arguments in the test-parallel script to allow for flexible command-line options.
- Updated session handling to hide cron run alias session keys from the sessions list, improving clarity.
- Enhanced the cron service to accurately record job start times and durations, ensuring better tracking of job execution.
- Added tests to verify the correct behavior of the cron service under various conditions, including zero-delay timers.
These changes aim to improve the usability and reliability of session and cron job management.
* feat: implement job running state checks in cron service
- Added functionality to prevent manual job runs if a job is already in progress, enhancing job management.
- Updated the `isJobDue` function to include checks for running jobs, ensuring accurate scheduling.
- Enhanced the `run` function to return a specific reason when a job is already running.
- Introduced a new test case to verify the behavior of forced manual runs during active job execution.
These changes aim to improve the reliability and clarity of cron job execution and management.
* feat: add session ID and key to CronRunLogEntry model
- Introduced `sessionid` and `sessionkey` properties to the `CronRunLogEntry` struct for enhanced tracking of session-related information.
- Updated the initializer and Codable conformance to accommodate the new properties, ensuring proper serialization and deserialization.
These changes aim to improve the granularity of logging and session management within the cron job system.
* fix: improve session display name resolution
- Updated the `resolveSessionDisplayName` function to ensure that both label and displayName are trimmed and default to an empty string if not present.
- Enhanced the logic to prevent returning the key if it matches the label or displayName, improving clarity in session naming.
These changes aim to enhance the accuracy and usability of session display names in the UI.
* perf: skip cron store persist when idle timer tick produces no changes
recomputeNextRuns now returns a boolean indicating whether any job
state was mutated. The idle path in onTimer only persists when the
return value is true, eliminating unnecessary file writes every 60s
for far-future or idle schedules.
* fix: prep for merge - explicit delivery mode migration, docs + changelog (#10776) (thanks @tyler6204)
2026-02-06 18:03:03 -08:00
|
|
|
children.add(child);
|
2026-02-15 03:35:02 +00:00
|
|
|
child.on("error", (err) => {
|
|
|
|
|
console.error(`[test-parallel] child error: ${String(err)}`);
|
|
|
|
|
});
|
fix: cron scheduler reliability, store hardening, and UX improvements (#10776)
* refactor: update cron job wake mode and run mode handling
- Changed default wake mode from 'next-heartbeat' to 'now' in CronJobEditor and related CLI commands.
- Updated cron-tool tests to reflect changes in run mode, introducing 'due' and 'force' options.
- Enhanced cron-tool logic to handle new run modes and ensure compatibility with existing job structures.
- Added new tests for delivery plan consistency and job execution behavior under various conditions.
- Improved normalization functions to handle wake mode and session target casing.
This refactor aims to streamline cron job configurations and enhance the overall user experience with clearer defaults and improved functionality.
* test: enhance cron job functionality and UI
- Added tests to ensure the isolated agent correctly announces the final payload text when delivering messages via Telegram.
- Implemented a new function to pick the last deliverable payload from a list of delivery payloads.
- Enhanced the cron service to maintain legacy "every" jobs while minute cron jobs recompute schedules.
- Updated the cron store migration tests to verify the addition of anchorMs to legacy every schedules.
- Improved the UI for displaying cron job details, including job state and delivery information, with new styles and layout adjustments.
These changes aim to improve the reliability and user experience of the cron job system.
* test: enhance sessions thinking level handling
- Added tests to verify that the correct thinking levels are applied during session spawning.
- Updated the sessions-spawn-tool to include a new parameter for overriding thinking levels.
- Enhanced the UI to support additional thinking levels, including "xhigh" and "full", and improved the handling of current options in dropdowns.
These changes aim to improve the flexibility and accuracy of thinking level configurations in session management.
* feat: enhance session management and cron job functionality
- Introduced passthrough arguments in the test-parallel script to allow for flexible command-line options.
- Updated session handling to hide cron run alias session keys from the sessions list, improving clarity.
- Enhanced the cron service to accurately record job start times and durations, ensuring better tracking of job execution.
- Added tests to verify the correct behavior of the cron service under various conditions, including zero-delay timers.
These changes aim to improve the usability and reliability of session and cron job management.
* feat: implement job running state checks in cron service
- Added functionality to prevent manual job runs if a job is already in progress, enhancing job management.
- Updated the `isJobDue` function to include checks for running jobs, ensuring accurate scheduling.
- Enhanced the `run` function to return a specific reason when a job is already running.
- Introduced a new test case to verify the behavior of forced manual runs during active job execution.
These changes aim to improve the reliability and clarity of cron job execution and management.
* feat: add session ID and key to CronRunLogEntry model
- Introduced `sessionid` and `sessionkey` properties to the `CronRunLogEntry` struct for enhanced tracking of session-related information.
- Updated the initializer and Codable conformance to accommodate the new properties, ensuring proper serialization and deserialization.
These changes aim to improve the granularity of logging and session management within the cron job system.
* fix: improve session display name resolution
- Updated the `resolveSessionDisplayName` function to ensure that both label and displayName are trimmed and default to an empty string if not present.
- Enhanced the logic to prevent returning the key if it matches the label or displayName, improving clarity in session naming.
These changes aim to enhance the accuracy and usability of session display names in the UI.
* perf: skip cron store persist when idle timer tick produces no changes
recomputeNextRuns now returns a boolean indicating whether any job
state was mutated. The idle path in onTimer only persists when the
return value is true, eliminating unnecessary file writes every 60s
for far-future or idle schedules.
* fix: prep for merge - explicit delivery mode migration, docs + changelog (#10776) (thanks @tyler6204)
2026-02-06 18:03:03 -08:00
|
|
|
child.on("exit", (exitCode, signal) => {
|
|
|
|
|
children.delete(child);
|
|
|
|
|
resolve(exitCode ?? (signal ? 1 : 0));
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
process.exit(Number(code) || 0);
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-23 11:36:28 +00:00
|
|
|
const parallelCodes = await Promise.all(parallelRuns.map(run));
|
|
|
|
|
const failedParallel = parallelCodes.find((code) => code !== 0);
|
|
|
|
|
if (failedParallel !== undefined) {
|
|
|
|
|
process.exit(failedParallel);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const entry of serialRuns) {
|
|
|
|
|
// eslint-disable-next-line no-await-in-loop
|
|
|
|
|
const code = await run(entry);
|
|
|
|
|
if (code !== 0) {
|
|
|
|
|
process.exit(code);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
process.exit(0);
|