Files
buzz/desktop/src/shared/api/tauri.test.mjs
Will Pfleger 6c40ce394f feat(desktop): cap OpenClaw agent parallelism at 5 (#4019)
OpenClaw connects to a single shared Gateway daemon. Spawning the
default 10 ACP workers per agent is both resource-expensive and
architecturally wrong — each worker opens a separate gateway connection.
Tyler's ruling: cap at 5, lower if needed.

## Contract

Store the requested value (1–32) verbatim at every persistence and wire
boundary. Apply `effective = min(requested, harness_cap)` only at the
four enforcement points:

| Boundary | Implementation |
|---|---|
| Local spawn | `BUZZ_ACP_AGENTS` env var in child `Command` |
| Remote deploy | `launch.policy_env["BUZZ_ACP_AGENTS"]` + legacy
`parallelism` field |
| Restart badge | `SpawnConfigSnapshot.parallelism` stores effective
value; the diff surface displays what actually runs |
| UI copy | Amber hint when requested > cap; no `max` attribute, no
save-path clamp |

`BUZZ_ACP_AGENTS` is added to `RESERVED_ENV_KEYS` — the Desktop resolves
the effective value into `policy_env`; a user-supplied override in `env`
would bypass the cap and is silently stripped.

## Changes

**`managed_agents/parallelism.rs`** (new) — policy core:
- `OPENCLAW_MAX_PARALLELISM = 5`
- `harness_max_parallelism(command)` — keyed on
`normalize_command_identity` so path prefixes, `.exe` suffixes, and
other cosmetic differences are ignored
- `effective_parallelism(command, value)` — identity for uncapped
harnesses
- `acp_agents_value(command, parallelism)` — `env("BUZZ_ACP_AGENTS", …)`
helper

**`runtime.rs`** — spawn clamp: `BUZZ_ACP_AGENTS =
acp_agents_value(effective_command, record.parallelism)`

**`agents_deploy.rs`** — deploy egress clamp: `build_deploy_payload`
resolves `effective_parallelism` once from `descriptor.command`; both
`launch.policy_env["BUZZ_ACP_AGENTS"]` and the legacy top-level
`parallelism` field use that value — the two are always consistent
regardless of stale `record.agent_command` pins

**`spawn_snapshot.rs`** — `from_inputs` stores
`effective_parallelism(&descriptor.command, record.parallelism)` in the
`parallelism` field. Over-cap edits that don't change the pool (e.g. 10
→ 8, both clamp to 5 on OpenClaw) produce equal snapshots; cap crossings
(8 → 3) produce different snapshots.

**`AcpRuntimeCatalogEntry.max_parallelism: Option<u32>`** — derived from
the static definition command, not the probed `entry.command` (which may
be `null` for unavailable entries), so unavailable OpenClaw entries
still carry the cap. Propagated through all four catalog constructors
(builtin discovery, preset catalog construction, custom discovery,
custom-save response), IPC types
(`RawAcpRuntimeCatalogEntry.max_parallelism`), and the frontend catalog
type.

**UI** — `EditAgentAdvancedFields` and `PersonaAdvancedFields` show an
amber hint when `selectedRuntime.maxParallelism` is set and the current
value exceeds it. Cap and label come from the catalog entry — no
hardcoded 5 in TS. No `max` attribute on inputs; the input stays
`type="text"` with 1–32 copy.

**Docs** — `docs/remote-agents.md`: `BUZZ_ACP_AGENTS` moved from the
deliberately-non-reserved section to reserved; new contract documented.
`desktop/src/features/agents/AGENTS.md`: command-keyed execution policy
documented as the sanctioned second metadata source feeding the catalog
projection.

## Tests

**Rust** (`parallelism.rs`):
- `policy_table` — `harness_max_parallelism` and `effective_parallelism`
across all openclaw variants and uncapped harnesses
- `acp_agents_value_openclaw_above_cap_is_capped` — spawn-env seam
- `override_direction_*` — both override directions (openclaw runtime +
goose override; goose runtime + openclaw override)
- `summary_persona_inherited_*` — live persona wins over stale
`agent_command`
- `snapshot_export_carries_requested_definition_parallelism` — requested
value travels wire/sync unchanged

**Rust** (`spawn_snapshot/tests.rs`):
- `openclaw_above_cap_parallelism_snapshots_equal` — stored 10 vs 8,
both clamp to 5 → snapshots equal
- `openclaw_cap_crossing_parallelism_snapshots_differ` — 8 (clamps to 5)
vs 3 → snapshots differ

**Rust** (`discovery/presets.rs`):
- `openclaw_preset_unavailable_carries_max_parallelism` /
`openclaw_preset_available_carries_max_parallelism` — catalog metadata
present with `command: null` and with a resolved path

**Rust** (`agents_deploy.rs`):
- `launch_block_openclaw_over_cap_policy_env_is_capped` — direct
`launch.policy_env` seam
-
`deploy_payload_json_stale_goose_record_live_openclaw_descriptor_both_capped`
— stale `record.agent_command=goose`, live descriptor=openclaw: both
fields cap to 5
-
`deploy_payload_json_stale_openclaw_record_live_goose_descriptor_both_uncapped`
— stale `record.agent_command=openclaw`, live descriptor=goose: both
fields pass through requested
- `deploy_payload_json_explicit_openclaw_override_both_capped` —
explicit `agent_command_override=openclaw`: both fields cap to 5

**Rust** (`persona_events/stale_pin_tests.rs`):
- `apply_persona_snapshot_goose_to_custom_harness_drops_stale_goose_pin`
— custom-direction stale-pin drop (builtin pin → loaded custom harness
via `update_loaded_harness_registry`)

**TypeScript** (`agentParallelism.test.mjs`):
- `parallelismCapHint` — at/below cap (null), above cap (hint includes
label and cap value), singular form for cap=1, uncapped harness (null)

**TypeScript** (`tauri.test.mjs`):
- `fromRawAcpRuntimeCatalogEntry` round-trips `max_parallelism` →
`maxParallelism`; absent when `undefined`

---------

Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
2026-08-05 16:09:14 -04:00

266 lines
8.3 KiB
JavaScript

/**
* Unit tests for tauri.ts — focused on `applyTauriRateLimitIfNeeded`, the
* extracted `relay rate-limited:` classifier that activates the shared
* rate-limit gate when Rust emits an HTTP 429 error prefix.
*
* Testing the exported production function (not a local copy) ensures any
* change to the classifier logic is immediately covered here.
*/
import assert from "node:assert/strict";
import test from "node:test";
// ── Fake-timer + gate setup ───────────────────────────────────────────────────
let fakeNow = 0;
const pendingTimers = new Map();
let nextTimerId = 1;
function fakeSetTimeout(fn, ms) {
const id = nextTimerId++;
pendingTimers.set(id, { fn, fireAt: fakeNow + ms });
return id;
}
function fakeClearTimeout(id) {
pendingTimers.delete(id);
}
function tickTo(ms) {
fakeNow = ms;
for (const [id, { fn, fireAt }] of Array.from(pendingTimers.entries())) {
if (fireAt <= fakeNow) {
pendingTimers.delete(id);
fn();
}
}
}
const origDateNow = Date.now;
function setFakeNow(ms) {
fakeNow = ms;
Date.now = () => fakeNow;
}
globalThis.window = {
setTimeout: fakeSetTimeout,
clearTimeout: fakeClearTimeout,
};
setFakeNow(0);
const { isRateLimited, resetRateLimitGate } = await import(
"./relayRateLimitGate.ts"
);
// Import the production classifier from tauri.ts — tests must exercise the
// real function, not a local copy, so a logic change is always caught here.
const { applyTauriRateLimitIfNeeded } = await import("./tauri.ts");
function resetGate(startMs = 0) {
pendingTimers.clear();
nextTimerId = 1;
setFakeNow(startMs);
resetRateLimitGate();
}
// ── applyTauriRateLimitIfNeeded: relay rate-limited: prefix ───────────────────
test("relay rate-limited: prefix activates the rate-limit gate", () => {
resetGate(0);
applyTauriRateLimitIfNeeded("relay rate-limited: retry in 10s");
assert.equal(isRateLimited(), true, "gate must be active after 429 error");
});
test("relay rate-limited: prefix parses the retry hint and arms the gate duration", () => {
resetGate(0);
applyTauriRateLimitIfNeeded("relay rate-limited: retry in 7s");
// Gate should be active at 6s.
setFakeNow(6_000);
assert.equal(isRateLimited(), true);
// Gate should expire after 7s.
tickTo(7_001);
assert.equal(isRateLimited(), false);
});
test("relay rate-limited: with no hint uses the 10s default", () => {
resetGate(0);
applyTauriRateLimitIfNeeded("relay rate-limited: quota exceeded");
tickTo(9_999);
assert.equal(isRateLimited(), true);
tickTo(10_001);
assert.equal(isRateLimited(), false);
});
test("non-rate-limited error does not activate the gate", () => {
resetGate(0);
applyTauriRateLimitIfNeeded("relay returned 404 Not Found");
assert.equal(
isRateLimited(),
false,
"gate must remain inactive for unrelated errors",
);
});
test("relay rate-limited: prefix check is case-sensitive (Rust always emits lowercase)", () => {
resetGate(0);
// The prefix from Rust is always lowercase; mixed-case must not trigger it.
applyTauriRateLimitIfNeeded("Relay rate-limited: retry in 5s");
assert.equal(
isRateLimited(),
false,
"uppercase prefix must not activate gate (relay emits lowercase only)",
);
});
// ── fromRawAcpRuntimeCatalogEntry: custom row API-boundary (B-2) ─────────────
//
// These tests feed real raw custom catalog rows through fromRawAcpRuntimeCatalogEntry
// and verify the Rust→TypeScript mapping boundary: definition_env (snake_case)
// arrives as definitionEnv (camelCase), source "custom" is preserved, and the
// env round-trips end-to-end so a save-then-edit cycle cannot erase env.
const { fromRawAcpRuntimeCatalogEntry } = await import("./tauri.ts");
test("fromRawAcpRuntimeCatalogEntry maps definition_env to definitionEnv", () => {
const raw = {
id: "my-harness",
label: "My Harness",
availability: "available",
command: "my-bin",
source: "custom",
definition_env: { ANTHROPIC_API_KEY: "sk-test", MODEL: "claude-3" },
default_args: [],
can_auto_install: false,
requires_external_cli: false,
install_hint: "",
install_instructions_url: "",
};
const entry = fromRawAcpRuntimeCatalogEntry(raw);
assert.deepStrictEqual(entry.definitionEnv, {
ANTHROPIC_API_KEY: "sk-test",
MODEL: "claude-3",
});
assert.equal(entry.source, "custom");
});
test("fromRawAcpRuntimeCatalogEntry defaults definitionEnv to {} when absent", () => {
// Rust serialization skips empty BTreeMap, so definition_env will be absent
// for harnesses with no env defined — the mapper must default to {}.
const raw = {
id: "no-env-harness",
label: "No Env",
availability: "available",
command: "no-env-bin",
source: "custom",
default_args: [],
can_auto_install: false,
requires_external_cli: false,
install_hint: "",
install_instructions_url: "",
};
const entry = fromRawAcpRuntimeCatalogEntry(raw);
assert.deepStrictEqual(
entry.definitionEnv,
{},
"absent definition_env must map to empty object, not undefined",
);
});
test("fromRawAcpRuntimeCatalogEntry preserves source preset", () => {
const raw = {
id: "cursor",
label: "Cursor",
availability: "available",
command: "cursor",
source: "preset",
default_args: [],
can_auto_install: false,
requires_external_cli: false,
install_hint: "",
install_instructions_url: "",
};
const entry = fromRawAcpRuntimeCatalogEntry(raw);
assert.equal(entry.source, "preset");
assert.deepStrictEqual(entry.definitionEnv, {});
});
test("fromRawAcpRuntimeCatalogEntry env round-trips through edit payload shape", () => {
// Simulate the full save → re-open cycle: raw entry comes back from Rust
// with definition_env populated; the edit form reads entry.definitionEnv.
// Verify the env values are identical before and after the mapper.
const envValues = { OPENAI_API_KEY: "sk-live-abc", REGION: "us-east-1" };
const raw = {
id: "openai-harness",
label: "OpenAI",
availability: "not_installed",
command: "openai-agent",
source: "custom",
definition_env: envValues,
default_args: ["--acp"],
can_auto_install: false,
requires_external_cli: true,
install_hint: "Install the OpenAI CLI",
install_instructions_url: "https://platform.openai.com/docs",
};
const entry = fromRawAcpRuntimeCatalogEntry(raw);
// The edit form reads entry.definitionEnv; it must equal the original env.
assert.deepStrictEqual(
entry.definitionEnv,
envValues,
"env must round-trip: edit form must see the same values that Rust serialized",
);
});
// ── max_parallelism → maxParallelism mapping ──────────────────────────────────
test("fromRawAcpRuntimeCatalogEntry maps max_parallelism to maxParallelism when present", () => {
const raw = {
id: "openclaw",
label: "OpenClaw",
availability: "not_installed",
command: null,
source: "preset",
default_args: [],
can_auto_install: false,
requires_external_cli: false,
install_hint: "",
install_instructions_url: "",
max_parallelism: 5,
};
const entry = fromRawAcpRuntimeCatalogEntry(raw);
assert.equal(
entry.maxParallelism,
5,
"max_parallelism: 5 must map to maxParallelism: 5",
);
});
test("fromRawAcpRuntimeCatalogEntry omits maxParallelism when max_parallelism is absent", () => {
const raw = {
id: "goose",
label: "Goose",
availability: "available",
command: "goose",
source: "builtin",
default_args: [],
can_auto_install: false,
requires_external_cli: false,
install_hint: "",
install_instructions_url: "",
// No max_parallelism field — uncapped harness.
};
const entry = fromRawAcpRuntimeCatalogEntry(raw);
assert.equal(
entry.maxParallelism,
undefined,
"uncapped harness must have maxParallelism: undefined",
);
});
// ── Teardown ──────────────────────────────────────────────────────────────────
test("teardown — restore Date.now", () => {
Date.now = origDateNow;
assert.ok(true);
});