@cydm/pie 2.7.4 → 2.7.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -48,6 +48,31 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
48
48
  mod
49
49
  ));
50
50
 
51
+ // vendor/computer-use-axi/src/test-backend-guard.ts
52
+ function requireFakeBackend(env = process.env) {
53
+ return env.PIE_COMPUTER_USE_REQUIRE_FAKE_BACKEND === "1";
54
+ }
55
+ function assertNativeBackendAllowed() {
56
+ if (requireFakeBackend()) throw new Error("Required fake backend: native desktop access is forbidden (backend not installed in this module instance).");
57
+ }
58
+ function resolveTestOnlyBackend(env = process.env) {
59
+ if (requireFakeBackend(env) && (env.PIE_COMPUTER_USE_FAKE_BACKEND !== "1" || env.PIE_COMPUTER_USE_ENABLE_TEST_COMMANDS !== "1")) {
60
+ throw new Error("Required fake backend needs PIE_COMPUTER_USE_FAKE_BACKEND=1 and PIE_COMPUTER_USE_ENABLE_TEST_COMMANDS=1.");
61
+ }
62
+ if (env.PIE_COMPUTER_USE_FAKE_BACKEND !== "1") return { enabled: false };
63
+ if (env.PIE_COMPUTER_USE_ENABLE_TEST_COMMANDS !== "1") {
64
+ return {
65
+ enabled: false,
66
+ warning: "PIE_COMPUTER_USE_FAKE_BACKEND is test-only and was ignored: it requires PIE_COMPUTER_USE_ENABLE_TEST_COMMANDS=1 as well."
67
+ };
68
+ }
69
+ return { enabled: true };
70
+ }
71
+ var init_test_backend_guard = __esm({
72
+ "vendor/computer-use-axi/src/test-backend-guard.ts"() {
73
+ }
74
+ });
75
+
51
76
  // vendor/computer-use-axi/src/platform/coerce.ts
52
77
  function toBoolean(value) {
53
78
  return value === true || value === "true" || value === 1;
@@ -2307,7 +2332,7 @@ var require_websocket = __commonJS({
2307
2332
  var http = __require("http");
2308
2333
  var net4 = __require("net");
2309
2334
  var tls = __require("tls");
2310
- var { randomBytes, createHash: createHash4 } = __require("crypto");
2335
+ var { randomBytes, createHash: createHash5 } = __require("crypto");
2311
2336
  var { Duplex, Readable } = __require("stream");
2312
2337
  var { URL: URL2 } = __require("url");
2313
2338
  var PerMessageDeflate2 = require_permessage_deflate();
@@ -2988,7 +3013,7 @@ var require_websocket = __commonJS({
2988
3013
  abortHandshake(websocket, socket, "Invalid Upgrade header");
2989
3014
  return;
2990
3015
  }
2991
- const digest = createHash4("sha1").update(key + GUID).digest("base64");
3016
+ const digest = createHash5("sha1").update(key + GUID).digest("base64");
2992
3017
  if (res.headers["sec-websocket-accept"] !== digest) {
2993
3018
  abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
2994
3019
  return;
@@ -3357,7 +3382,7 @@ var require_websocket_server = __commonJS({
3357
3382
  var EventEmitter = __require("events");
3358
3383
  var http = __require("http");
3359
3384
  var { Duplex } = __require("stream");
3360
- var { createHash: createHash4 } = __require("crypto");
3385
+ var { createHash: createHash5 } = __require("crypto");
3361
3386
  var extension2 = require_extension();
3362
3387
  var PerMessageDeflate2 = require_permessage_deflate();
3363
3388
  var subprotocol2 = require_subprotocol();
@@ -3664,7 +3689,7 @@ var require_websocket_server = __commonJS({
3664
3689
  );
3665
3690
  }
3666
3691
  if (this._state > RUNNING) return abortHandshake(socket, 503);
3667
- const digest = createHash4("sha1").update(key + GUID).digest("base64");
3692
+ const digest = createHash5("sha1").update(key + GUID).digest("base64");
3668
3693
  const headers = [
3669
3694
  "HTTP/1.1 101 Switching Protocols",
3670
3695
  "Upgrade: websocket",
@@ -3852,6 +3877,7 @@ async function isResolvedHelperExecutable(filePath) {
3852
3877
  return actualPath === expectedPath;
3853
3878
  }
3854
3879
  async function runProcess(command, args, timeoutMs, signal, env) {
3880
+ assertNativeBackendAllowed();
3855
3881
  throwIfAborted(signal);
3856
3882
  await new Promise((resolve2, reject) => {
3857
3883
  const child = spawn(command, args, {
@@ -3900,6 +3926,7 @@ ${output}`.trim()));
3900
3926
  var COMMAND_TIMEOUT_MS2, HELPER_PROTOCOL_VERSION, HELPER_SETUP_TIMEOUT_MS, HELPER_APP_PATH, HELPER_APP_EXECUTABLE_PATH, DEFAULT_HELPER_SOCKET_PATH, HELPER_SOCKET_PATH, usingExternalHelperSocket, PACKAGE_ROOT, SETUP_HELPER_SCRIPT, HelperTransportError, HelperCommandError, MacosHelperClient, macosHelper;
3901
3927
  var init_helper = __esm({
3902
3928
  "vendor/computer-use-axi/src/platform/macos/helper.ts"() {
3929
+ init_test_backend_guard();
3903
3930
  init_coerce();
3904
3931
  init_helper_path();
3905
3932
  init_package_root();
@@ -3935,6 +3962,7 @@ var init_helper = __esm({
3935
3962
  return this.diagnosticsCache;
3936
3963
  }
3937
3964
  async ensureInstalled(signal) {
3965
+ assertNativeBackendAllowed();
3938
3966
  if (usingExternalHelperSocket) return;
3939
3967
  if (await isExecutable(HELPER_APP_EXECUTABLE_PATH)) {
3940
3968
  return;
@@ -3949,11 +3977,13 @@ var init_helper = __esm({
3949
3977
  }
3950
3978
  }
3951
3979
  async launchDaemon(signal) {
3980
+ assertNativeBackendAllowed();
3952
3981
  if (usingExternalHelperSocket) throw new HelperTransportError(`External helper socket is unavailable at ${HELPER_SOCKET_PATH}.`);
3953
3982
  await mkdir(path9.dirname(HELPER_SOCKET_PATH), { recursive: true });
3954
3983
  await runProcess("open", ["-n", "-g", HELPER_APP_PATH, "--args", "serve", "--socket", HELPER_SOCKET_PATH], COMMAND_TIMEOUT_MS2, signal);
3955
3984
  }
3956
3985
  async daemonCommand(cmd, args, timeoutMs, signal) {
3986
+ assertNativeBackendAllowed();
3957
3987
  return await new Promise((resolve2, reject) => {
3958
3988
  const id = `req_${++this.requestSequence}`;
3959
3989
  const socket = net.createConnection(HELPER_SOCKET_PATH);
@@ -3996,6 +4026,7 @@ var init_helper = __esm({
3996
4026
  });
3997
4027
  }
3998
4028
  async ensureDaemon(signal) {
4029
+ assertNativeBackendAllowed();
3999
4030
  if (this.daemonAvailable) return true;
4000
4031
  try {
4001
4032
  await this.daemonCommand("diagnostics", {}, 1e3, signal);
@@ -4028,6 +4059,7 @@ var init_helper = __esm({
4028
4059
  }
4029
4060
  }
4030
4061
  async restart(signal) {
4062
+ assertNativeBackendAllowed();
4031
4063
  await this.command("shutdown", {}, { signal, timeoutMs: 2e3 }).catch(() => void 0);
4032
4064
  this.daemonAvailable = false;
4033
4065
  await sleep(400, signal);
@@ -4310,23 +4342,25 @@ var init_permissions2 = __esm({
4310
4342
  });
4311
4343
 
4312
4344
  // vendor/computer-use-axi/src/broker-server.ts
4313
- import { createHash as createHash3, randomUUID as randomUUID8 } from "node:crypto";
4345
+ init_test_backend_guard();
4346
+ import { createHash as createHash4, randomUUID as randomUUID8 } from "node:crypto";
4314
4347
  import { chmodSync as chmodSync2, lstatSync as lstatSync4, readFileSync as readFileSync4, unlinkSync as unlinkSync4, writeFileSync as writeFileSync3 } from "node:fs";
4315
4348
  import net3 from "node:net";
4316
4349
  import path14 from "node:path";
4317
4350
 
4318
4351
  // vendor/computer-use-axi/src/artifact-store.ts
4319
4352
  import { createHash as createHash2, randomUUID as randomUUID2 } from "node:crypto";
4320
- import { closeSync, existsSync as existsSync2, lstatSync as lstatSync3, mkdirSync as mkdirSync4, openSync, readdirSync as readdirSync2, rmdirSync, unlinkSync as unlinkSync2, writeSync } from "node:fs";
4321
- import path3 from "node:path";
4353
+ import { closeSync, existsSync as existsSync3, lstatSync as lstatSync3, mkdirSync as mkdirSync4, openSync, readdirSync as readdirSync2, rmdirSync as rmdirSync2, unlinkSync as unlinkSync2, writeSync } from "node:fs";
4354
+ import path4 from "node:path";
4322
4355
 
4323
4356
  // vendor/computer-use-axi/src/broker-protocol.ts
4324
4357
  import { createHash, randomUUID } from "node:crypto";
4325
4358
  import { chmodSync, lstatSync as lstatSync2, mkdirSync as mkdirSync3 } from "node:fs";
4326
- import path2 from "node:path";
4359
+ import path3 from "node:path";
4327
4360
 
4328
- // vendor/computer-use-axi/src/persist.ts
4329
- import { lstatSync, mkdirSync as mkdirSync2, readdirSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "node:fs";
4361
+ // vendor/computer-use-axi/src/config.ts
4362
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
4363
+ import { AsyncLocalStorage } from "node:async_hooks";
4330
4364
  import path from "node:path";
4331
4365
 
4332
4366
  // src/config.ts
@@ -4370,26 +4404,151 @@ function getConfigDir() {
4370
4404
  return join(homedir(), CONFIG_DIR_NAME);
4371
4405
  }
4372
4406
 
4407
+ // vendor/computer-use-axi/host.ts
4408
+ function getAgentDir() {
4409
+ return getConfigDir();
4410
+ }
4411
+
4412
+ // vendor/computer-use-axi/src/config.ts
4413
+ var DEFAULT_CONFIG = {
4414
+ browser_use: true,
4415
+ headless: false,
4416
+ cursor_overlay: true,
4417
+ managed_browser: "chrome"
4418
+ };
4419
+ var REQUEST_ENV_KEYS = ["PIE_COMPUTER_USE_BROWSER_USE", "PIE_COMPUTER_USE_HEADLESS", "PIE_COMPUTER_USE_CURSOR_OVERLAY", "PIE_COMPUTER_USE_MANAGED_BROWSER", "PIE_COMPUTER_USE_HELIUM_EXECUTABLE", "PIE_COMPUTER_USE_CHROME_EXECUTABLE", "PIE_COMPUTER_USE_CDP_PORT", "PIE_COMPUTER_USE_DELIVERY_POLICY", "PIE_COMPUTER_USE_EVENT_DELIVERY"];
4420
+ function captureComputerUseEnvironment(env = process.env) {
4421
+ const out = {};
4422
+ for (const key of REQUEST_ENV_KEYS) {
4423
+ const value = env[key]?.trim();
4424
+ if (!value) continue;
4425
+ if (Buffer.byteLength(value) > 4096 || value.includes("\0")) throw new Error(`Invalid ${key}`);
4426
+ if (["PIE_COMPUTER_USE_BROWSER_USE", "PIE_COMPUTER_USE_HEADLESS", "PIE_COMPUTER_USE_CURSOR_OVERLAY"].includes(key)) {
4427
+ const parsed = parseBoolean(value);
4428
+ if (parsed === void 0) throw new Error(`Invalid ${key}`);
4429
+ out[key] = String(parsed);
4430
+ } else if (key === "PIE_COMPUTER_USE_MANAGED_BROWSER") {
4431
+ if (value !== "helium" && value !== "chrome") throw new Error(`Invalid ${key}`);
4432
+ out[key] = value;
4433
+ } else if (key === "PIE_COMPUTER_USE_DELIVERY_POLICY" || key === "PIE_COMPUTER_USE_EVENT_DELIVERY") {
4434
+ const policy = value.toLowerCase();
4435
+ if (!["default", "background", "pid", "foreground", "hid", "ax_only", "ax-only"].includes(policy)) throw new Error(`Invalid ${key}`);
4436
+ out[key] = policy;
4437
+ } else if (key === "PIE_COMPUTER_USE_CDP_PORT") {
4438
+ if (!/^\d+$/.test(value) || Number(value) < 1 || Number(value) > 65535) throw new Error(`Invalid ${key}`);
4439
+ out[key] = String(Number(value));
4440
+ } else {
4441
+ if (!path.isAbsolute(value)) throw new Error(`Invalid ${key}: expected absolute path`);
4442
+ out[key] = value;
4443
+ }
4444
+ }
4445
+ return Object.freeze(out);
4446
+ }
4447
+ var managedCdpPort;
4448
+ function setManagedCdpPort(port) {
4449
+ managedCdpPort = port;
4450
+ }
4451
+ function getComputerUseEnvironmentValue(key) {
4452
+ return operationConfig.getStore()?.environment[key] ?? (key === "PIE_COMPUTER_USE_CDP_PORT" ? managedCdpPort : void 0);
4453
+ }
4454
+ var operationConfig = new AsyncLocalStorage();
4455
+ var defaultLoaded = Object.freeze({ config: Object.freeze({ ...DEFAULT_CONFIG }), sources: [], env: {}, environment: {} });
4456
+ function withComputerUseConfig(cwd, run, environment) {
4457
+ return operationConfig.run(loadComputerUseConfig(cwd, environment), run);
4458
+ }
4459
+ function parseBoolean(value) {
4460
+ if (typeof value === "boolean") return value;
4461
+ if (typeof value === "number") return value === 1 ? true : value === 0 ? false : void 0;
4462
+ if (typeof value !== "string") return void 0;
4463
+ const normalized = value.trim().toLowerCase();
4464
+ if (["1", "true", "yes", "on", "enabled"].includes(normalized)) return true;
4465
+ if (["0", "false", "no", "off", "disabled"].includes(normalized)) return false;
4466
+ return void 0;
4467
+ }
4468
+ function normalizePartial(raw) {
4469
+ if (!raw || typeof raw !== "object") return {};
4470
+ const source = raw.computer_use && typeof raw.computer_use === "object" ? raw.computer_use : raw;
4471
+ const out = {};
4472
+ const browserUse = parseBoolean(source.browser_use);
4473
+ const headless = parseBoolean(source.headless);
4474
+ const cursorOverlay = parseBoolean(source.cursor_overlay);
4475
+ if (browserUse !== void 0) out.browser_use = browserUse;
4476
+ if (headless !== void 0) out.headless = headless;
4477
+ if (cursorOverlay !== void 0) out.cursor_overlay = cursorOverlay;
4478
+ const managedBrowser = source.managed_browser;
4479
+ if (managedBrowser === "helium" || managedBrowser === "chrome") out.managed_browser = managedBrowser;
4480
+ return out;
4481
+ }
4482
+ function readConfigFile(filePath) {
4483
+ if (!existsSync2(filePath)) return { path: filePath, exists: false };
4484
+ try {
4485
+ const parsed = JSON.parse(readFileSync2(filePath, "utf-8"));
4486
+ return { path: filePath, exists: true, values: normalizePartial(parsed) };
4487
+ } catch (error) {
4488
+ return { path: filePath, exists: true, error: error instanceof Error ? error.message : String(error) };
4489
+ }
4490
+ }
4491
+ function readEnv(environment) {
4492
+ const out = {};
4493
+ const browserUse = parseBoolean(environment.PIE_COMPUTER_USE_BROWSER_USE);
4494
+ const headless = parseBoolean(environment.PIE_COMPUTER_USE_HEADLESS);
4495
+ const cursorOverlay = parseBoolean(environment.PIE_COMPUTER_USE_CURSOR_OVERLAY);
4496
+ if (browserUse !== void 0) out.browser_use = browserUse;
4497
+ if (headless !== void 0) out.headless = headless;
4498
+ if (cursorOverlay !== void 0) out.cursor_overlay = cursorOverlay;
4499
+ const managedBrowser = environment.PIE_COMPUTER_USE_MANAGED_BROWSER;
4500
+ if (managedBrowser === "helium" || managedBrowser === "chrome") out.managed_browser = managedBrowser;
4501
+ return out;
4502
+ }
4503
+ function loadComputerUseConfig(cwd, environment = captureComputerUseEnvironment()) {
4504
+ const sources = [
4505
+ readConfigFile(path.join(getAgentDir(), "extensions", "computer-use.json")),
4506
+ readConfigFile(path.join(cwd, ".pie", "computer-use.json"))
4507
+ ];
4508
+ environment = captureComputerUseEnvironment(environment);
4509
+ const env = readEnv(environment);
4510
+ const config = { ...DEFAULT_CONFIG };
4511
+ for (const source of sources) {
4512
+ if (source.values) Object.assign(config, source.values);
4513
+ }
4514
+ Object.assign(config, env);
4515
+ return Object.freeze({ config: Object.freeze(config), sources, env, environment });
4516
+ }
4517
+ function getComputerUseConfig() {
4518
+ return getLoadedComputerUseConfig().config;
4519
+ }
4520
+ function getLoadedComputerUseConfig() {
4521
+ return operationConfig.getStore() ?? defaultLoaded;
4522
+ }
4523
+ function isHeadlessMode() {
4524
+ return getComputerUseConfig().headless;
4525
+ }
4526
+ function isBrowserUseEnabled() {
4527
+ return getComputerUseConfig().browser_use;
4528
+ }
4529
+
4373
4530
  // vendor/computer-use-axi/src/persist.ts
4531
+ import { lstatSync, mkdirSync as mkdirSync2, opendirSync, readdirSync, readFileSync as readFileSync3, renameSync as renameSync2, rmdirSync, unlinkSync, writeFileSync } from "node:fs";
4532
+ import path2 from "node:path";
4374
4533
  function stateRootDir(env = process.env) {
4375
4534
  const override = env.PIE_COMPUTER_USE_STATE_DIR?.trim();
4376
4535
  if (override) return override;
4377
- return path.join(getConfigDir(), "computer-use");
4536
+ return path2.join(getConfigDir(), "computer-use");
4378
4537
  }
4379
4538
  function statesDir(env = process.env) {
4380
- return path.join(stateRootDir(env), "states");
4539
+ return path2.join(stateRootDir(env), "states");
4381
4540
  }
4382
4541
  function outputsDir(env = process.env) {
4383
- return path.join(stateRootDir(env), "outputs");
4542
+ return path2.join(stateRootDir(env), "outputs");
4384
4543
  }
4385
4544
  function runtimeStatePath(env = process.env) {
4386
- return path.join(stateRootDir(env), "runtime.json");
4545
+ return path2.join(stateRootDir(env), "runtime.json");
4387
4546
  }
4388
4547
  function brokerStateDir(env = process.env) {
4389
- return path.join(stateRootDir(env), "broker");
4548
+ return path2.join(stateRootDir(env), "broker");
4390
4549
  }
4391
4550
  function brokerManifestPath(env = process.env) {
4392
- return path.join(brokerStateDir(env), "manifest.json");
4551
+ return path2.join(brokerStateDir(env), "manifest.json");
4393
4552
  }
4394
4553
  function loadBrokerManifest(env = process.env) {
4395
4554
  const manifest = readJsonFile(brokerManifestPath(env));
@@ -4406,7 +4565,7 @@ function saveBrokerManifest(manifest, env = process.env) {
4406
4565
  writeJsonAtomic(brokerManifestPath(env), { ...manifest, stateIds: manifest.stateIds.slice(0, 256) });
4407
4566
  }
4408
4567
  function brokerDrainMarkerPath(env = process.env) {
4409
- return path.join(brokerStateDir(env), "draining.json");
4568
+ return path2.join(brokerStateDir(env), "draining.json");
4410
4569
  }
4411
4570
  function loadDrainMarker(env = process.env) {
4412
4571
  const marker = readJsonFile(brokerDrainMarkerPath(env));
@@ -4445,7 +4604,7 @@ function cleanupLegacyBrokerCache(env = process.env) {
4445
4604
  try {
4446
4605
  for (const name of readdirSync(statesDirPath).slice(0, 512)) {
4447
4606
  if (!/^[A-Za-z0-9-]+\.json$/.test(name)) continue;
4448
- const target = path.join(statesDirPath, name);
4607
+ const target = path2.join(statesDirPath, name);
4449
4608
  try {
4450
4609
  const stats = lstatSync(target);
4451
4610
  if (!stats.isFile() || stats.isSymbolicLink()) continue;
@@ -4460,7 +4619,7 @@ function cleanupLegacyBrokerCache(env = process.env) {
4460
4619
  try {
4461
4620
  for (const name of readdirSync(oldOutputs).slice(0, 512)) {
4462
4621
  if (!/^o\d+\.(txt|meta\.json)$/.test(name)) continue;
4463
- const target = path.join(oldOutputs, name);
4622
+ const target = path2.join(oldOutputs, name);
4464
4623
  try {
4465
4624
  const stats = lstatSync(target);
4466
4625
  if (!stats.isFile() || stats.isSymbolicLink()) continue;
@@ -4471,17 +4630,54 @@ function cleanupLegacyBrokerCache(env = process.env) {
4471
4630
  }
4472
4631
  } catch {
4473
4632
  }
4633
+ outputs2 += cleanupOrphanOwnerOutputs(path2.join(oldOutputs, "owners"));
4474
4634
  return { runtimeFile, states, outputs: outputs2 };
4475
4635
  }
4636
+ function cleanupOrphanOwnerOutputs(root) {
4637
+ try {
4638
+ const stat = lstatSync(root);
4639
+ if (!stat.isDirectory() || stat.isSymbolicLink()) throw new Error("Unsafe computer-use outputs directory");
4640
+ } catch (error) {
4641
+ if (error.code === "ENOENT") return 0;
4642
+ throw error;
4643
+ }
4644
+ let remaining = 65536;
4645
+ let removed = 0;
4646
+ const visit = (directory, depth) => {
4647
+ const handle = opendirSync(directory);
4648
+ try {
4649
+ for (let entry = handle.readSync(); entry; entry = handle.readSync()) {
4650
+ if (--remaining < 0) throw new Error("Computer-use stale output cleanup budget exceeded; refusing unaccounted output storage");
4651
+ const target = path2.join(directory, entry.name);
4652
+ const stat = lstatSync(target);
4653
+ if (stat.isSymbolicLink()) {
4654
+ unlinkSync(target);
4655
+ continue;
4656
+ }
4657
+ if (stat.isDirectory() && depth === 0) {
4658
+ visit(target, 1);
4659
+ rmdirSync(target);
4660
+ } else if (stat.isFile() && depth === 1 && /^\d+\.txt$/.test(entry.name)) {
4661
+ unlinkSync(target);
4662
+ removed++;
4663
+ } else throw new Error(`Unexpected computer-use output entry: ${target}`);
4664
+ }
4665
+ } finally {
4666
+ handle.closeSync();
4667
+ }
4668
+ };
4669
+ visit(root, 0);
4670
+ return removed;
4671
+ }
4476
4672
  function writeJsonAtomic(filePath, value) {
4477
- mkdirSync2(path.dirname(filePath), { recursive: true });
4673
+ mkdirSync2(path2.dirname(filePath), { recursive: true });
4478
4674
  const temp = `${filePath}.${process.pid}.tmp`;
4479
4675
  writeFileSync(temp, JSON.stringify(value), "utf8");
4480
4676
  renameSync2(temp, filePath);
4481
4677
  }
4482
4678
  function readJsonFile(filePath) {
4483
4679
  try {
4484
- return JSON.parse(readFileSync2(filePath, "utf8"));
4680
+ return JSON.parse(readFileSync3(filePath, "utf8"));
4485
4681
  } catch {
4486
4682
  return void 0;
4487
4683
  }
@@ -4498,8 +4694,8 @@ function normalizeComputerUseOwner(value) {
4498
4694
  }
4499
4695
 
4500
4696
  // vendor/computer-use-axi/src/broker-protocol.ts
4501
- var BROKER_PROTOCOL_VERSION = 1;
4502
- var BROKER_ENGINE_VERSION = 2;
4697
+ var BROKER_PROTOCOL_VERSION = 3;
4698
+ var BROKER_ENGINE_VERSION = 4;
4503
4699
  var BROKER_PACKAGE_VERSION = "0.5.1-pie.1";
4504
4700
  var BROKER_RESULT_CACHE_MAX_ENTRIES = 256;
4505
4701
  var BROKER_REPLAY_LEDGER_MAX_IDS = 65536;
@@ -4825,7 +5021,19 @@ function validateRequest(value) {
4825
5021
  }
4826
5022
  const params = validateCommandParams(command, value.params);
4827
5023
  const cwd = value.cwd === void 0 ? process.cwd() : boundedString(value.cwd, "cwd", 4096);
4828
- if (!path2.isAbsolute(cwd)) throw new BrokerProtocolError("invalid_request", "cwd must be absolute.");
5024
+ if (!path3.isAbsolute(cwd)) throw new BrokerProtocolError("invalid_request", "cwd must be absolute.");
5025
+ const rawEnvironment = value.environment ?? {};
5026
+ assertPlainRecord(rawEnvironment, "environment");
5027
+ assertKeys(rawEnvironment, "environment", REQUEST_ENV_KEYS);
5028
+ for (const entry of Object.values(rawEnvironment)) {
5029
+ if (typeof entry !== "string") throw new BrokerProtocolError("invalid_request", "environment values must be strings.");
5030
+ }
5031
+ let environment;
5032
+ try {
5033
+ environment = captureComputerUseEnvironment(rawEnvironment);
5034
+ } catch (error) {
5035
+ throw new BrokerProtocolError("invalid_request", String(error));
5036
+ }
4829
5037
  const budgetMs = value.budgetMs === void 0 ? BROKER_DEFAULT_REQUEST_BUDGET_MS : Math.trunc(boundedNumber(value.budgetMs, "budgetMs", BROKER_MIN_REQUEST_BUDGET_MS, BROKER_MAX_REQUEST_BUDGET_MS));
4830
5038
  return {
4831
5039
  requestId: value.requestId,
@@ -4835,6 +5043,7 @@ function validateRequest(value) {
4835
5043
  command,
4836
5044
  params,
4837
5045
  cwd,
5046
+ environment,
4838
5047
  budgetMs
4839
5048
  };
4840
5049
  }
@@ -4844,8 +5053,10 @@ function validateHello(value) {
4844
5053
  if (value.protocolVersion !== BROKER_PROTOCOL_VERSION) {
4845
5054
  throw new BrokerProtocolError("protocol_mismatch", `Protocol mismatch: broker ${BROKER_PROTOCOL_VERSION}, client ${String(value.protocolVersion)}.`);
4846
5055
  }
5056
+ if (value.buildIdentity !== BROKER_BUILD.identity) throw new BrokerProtocolError("build_mismatch", "Broker engine build identity differs.");
4847
5057
  return {
4848
5058
  type: "hello",
5059
+ buildIdentity: value.buildIdentity,
4849
5060
  protocolVersion: value.protocolVersion,
4850
5061
  clientVersion: value.clientVersion === void 0 ? void 0 : boundedString(value.clientVersion, "clientVersion", 128)
4851
5062
  };
@@ -4874,6 +5085,7 @@ var FrameDecoder = class {
4874
5085
  for (; ; ) {
4875
5086
  const newline = this.buffer.indexOf(10);
4876
5087
  if (newline < 0) break;
5088
+ if (newline > this.maxBytes) throw new BrokerProtocolError("frame_too_large", `Frame exceeds the ${this.maxBytes}-byte limit.`);
4877
5089
  const line = this.buffer.subarray(0, newline).toString("utf8").trim();
4878
5090
  this.buffer = this.buffer.subarray(newline + 1);
4879
5091
  if (!line) continue;
@@ -4898,11 +5110,11 @@ function resolveBrokerEndpoint(env = process.env) {
4898
5110
  const key = createHash("sha256").update(seed).digest("hex").slice(0, 16);
4899
5111
  return { kind: "named-pipe", path: `\\\\.\\pipe\\pie-computer-use-axi-${key}` };
4900
5112
  }
4901
- const socketPath = socketOverride || path2.join(stateRootDir(env), "run", "broker.sock");
5113
+ const socketPath = socketOverride || path3.join(stateRootDir(env), "run", "broker.sock");
4902
5114
  if (Buffer.byteLength(socketPath, "utf8") > 100) {
4903
5115
  throw new BrokerProtocolError("endpoint_too_long", `Unix socket path is ${Buffer.byteLength(socketPath)} bytes (limit ~104): ${socketPath}. Set PIE_COMPUTER_USE_BROKER_SOCKET to a shorter path.`);
4904
5116
  }
4905
- return { kind: "unix-socket", path: socketPath, directory: path2.dirname(socketPath) };
5117
+ return { kind: "unix-socket", path: socketPath, directory: path3.dirname(socketPath) };
4906
5118
  }
4907
5119
  function ensurePrivateDirectory(directory) {
4908
5120
  mkdirSync3(directory, { recursive: true, mode: 448 });
@@ -4921,8 +5133,8 @@ function canonicalize(value) {
4921
5133
  }
4922
5134
  return value;
4923
5135
  }
4924
- function requestFingerprint(command, params, cwd) {
4925
- const seed = JSON.stringify({ command, params: canonicalize(params), cwd });
5136
+ function requestFingerprint(command, params, cwd, environment = {}) {
5137
+ const seed = JSON.stringify({ command, params: canonicalize(params), cwd, environment: canonicalize(environment) });
4926
5138
  return createHash("sha256").update(seed).digest("hex").slice(0, 32);
4927
5139
  }
4928
5140
 
@@ -4953,9 +5165,9 @@ var ArtifactStore = class {
4953
5165
  totalBytes = 0;
4954
5166
  /** Absolute path for one owner/generation, created 0700 and symlink-checked. */
4955
5167
  directoryFor(owner, generation = this.options.generation) {
4956
- const root = path3.join(this.options.root, safeComponent(generation, "generation"));
5168
+ const root = path4.join(this.options.root, safeComponent(generation, "generation"));
4957
5169
  const ownerKey = `owner-${createHash2("sha256").update(normalizeComputerUseOwner(owner)).digest("hex")}`;
4958
- const ownerDir = path3.join(root, ownerKey);
5170
+ const ownerDir = path4.join(root, ownerKey);
4959
5171
  ensurePrivateDirectory(this.options.root);
4960
5172
  ensurePrivateDirectory(root);
4961
5173
  ensurePrivateDirectory(ownerDir);
@@ -4965,7 +5177,7 @@ var ArtifactStore = class {
4965
5177
  const generation = input.generation ?? this.options.generation;
4966
5178
  const directory = this.directoryFor(input.owner, generation);
4967
5179
  const artifactId = randomUUID2();
4968
- const absolutePath = path3.join(directory, `${artifactId}.${extensionFor(input.mimeType)}`);
5180
+ const absolutePath = path4.join(directory, `${artifactId}.${extensionFor(input.mimeType)}`);
4969
5181
  const fd = openSync(absolutePath, "wx", 384);
4970
5182
  try {
4971
5183
  let offset = 0;
@@ -4994,6 +5206,16 @@ var ArtifactStore = class {
4994
5206
  this.sweep();
4995
5207
  return record;
4996
5208
  }
5209
+ /** Hold existing evidence while a hydrated observation is used. */
5210
+ pinForState(stateId2) {
5211
+ const ids = [];
5212
+ for (const record of this.records.values()) {
5213
+ if (record.stateId !== stateId2) continue;
5214
+ this.pin(record.artifactId);
5215
+ ids.push(record.artifactId);
5216
+ }
5217
+ return ids;
5218
+ }
4997
5219
  pin(artifactId) {
4998
5220
  this.pins.set(artifactId, (this.pins.get(artifactId) ?? 0) + 1);
4999
5221
  }
@@ -5045,13 +5267,13 @@ var ArtifactStore = class {
5045
5267
  /** Remove files for a generation that no longer owns the broker. */
5046
5268
  cleanupGeneration(generation) {
5047
5269
  if (!SAFE_COMPONENT.test(generation)) return 0;
5048
- const directory = path3.join(this.options.root, generation);
5049
- if (!existsSync2(directory)) return 0;
5270
+ const directory = path4.join(this.options.root, generation);
5271
+ if (!existsSync3(directory)) return 0;
5050
5272
  const stats = lstatSync3(directory);
5051
5273
  if (stats.isSymbolicLink() || !stats.isDirectory()) return 0;
5052
5274
  let removed = 0;
5053
5275
  for (const name of readdirSync2(directory)) {
5054
- const ownerDir = path3.join(directory, name);
5276
+ const ownerDir = path4.join(directory, name);
5055
5277
  let ownerStats;
5056
5278
  try {
5057
5279
  ownerStats = lstatSync3(ownerDir);
@@ -5060,7 +5282,7 @@ var ArtifactStore = class {
5060
5282
  }
5061
5283
  if (ownerStats.isSymbolicLink() || !ownerStats.isDirectory()) continue;
5062
5284
  for (const file of readdirSync2(ownerDir)) {
5063
- const target = path3.join(ownerDir, file);
5285
+ const target = path4.join(ownerDir, file);
5064
5286
  let fileStats;
5065
5287
  try {
5066
5288
  fileStats = lstatSync3(target);
@@ -5072,12 +5294,12 @@ var ArtifactStore = class {
5072
5294
  removed += 1;
5073
5295
  }
5074
5296
  try {
5075
- rmdirSync(ownerDir);
5297
+ rmdirSync2(ownerDir);
5076
5298
  } catch {
5077
5299
  }
5078
5300
  }
5079
5301
  try {
5080
- rmdirSync(directory);
5302
+ rmdirSync2(directory);
5081
5303
  } catch {
5082
5304
  }
5083
5305
  return removed;
@@ -5101,7 +5323,7 @@ var ArtifactStore = class {
5101
5323
  for (const name of names) {
5102
5324
  if (scanned >= maxGenerations) break;
5103
5325
  if (name === current || !SAFE_COMPONENT.test(name)) continue;
5104
- const directory = path3.join(this.options.root, name);
5326
+ const directory = path4.join(this.options.root, name);
5105
5327
  let stats;
5106
5328
  try {
5107
5329
  stats = lstatSync3(directory);
@@ -5133,11 +5355,12 @@ var ArtifactStore = class {
5133
5355
  };
5134
5356
  function artifactsRoot(env = process.env) {
5135
5357
  const base = env.PIE_COMPUTER_USE_ARTIFACTS_DIR?.trim();
5136
- if (base) return path3.resolve(base);
5137
- return path3.join(stateRootDir(env), "artifacts");
5358
+ if (base) return path4.resolve(base);
5359
+ return path4.join(stateRootDir(env), "artifacts");
5138
5360
  }
5139
5361
 
5140
5362
  // vendor/computer-use-axi/src/bridge.ts
5363
+ init_test_backend_guard();
5141
5364
  import { spawn as spawn4 } from "node:child_process";
5142
5365
  import { randomUUID as randomUUID7 } from "node:crypto";
5143
5366
  import { constants as fsConstants5, mkdtempSync } from "node:fs";
@@ -5161,7 +5384,7 @@ function keys(value) {
5161
5384
  if (!Array.isArray(value) || value.length === 0) throw new Error("keypress.keys must contain at least one key.");
5162
5385
  return value.map((key) => String(key));
5163
5386
  }
5164
- function path4(value, env) {
5387
+ function path5(value, env) {
5165
5388
  if (!Array.isArray(value) || value.length < 2) throw new Error("drag.path must contain at least two points.");
5166
5389
  return value.map((point, index) => {
5167
5390
  const x = Array.isArray(point) ? toFiniteNumber(point[0], NaN) : toFiniteNumber(point?.x, NaN);
@@ -5193,7 +5416,7 @@ function nativeTarget(action, operation, env) {
5193
5416
  env.validatePoint(x, y);
5194
5417
  return { x, y };
5195
5418
  }
5196
- if (operation === "drag" && action.path?.length) return path4(action.path, env)[0];
5419
+ if (operation === "drag" && action.path?.length) return path5(action.path, env)[0];
5197
5420
  throw new Error(`${operation} requires either ref or both x and y.`);
5198
5421
  }
5199
5422
  function focusedTarget(env) {
@@ -5263,7 +5486,7 @@ function prepareAction(action, state, env) {
5263
5486
  case "scroll":
5264
5487
  return { action: operation, target, params: { scrollX: scrollDelta(action.scrollX), scrollY: scrollDelta(action.scrollY) }, usesCurrentFocus: false, needsForeground: false };
5265
5488
  case "drag":
5266
- return { action: operation, target, params: { path: path4(action.path, env) }, usesCurrentFocus: false, needsForeground: false };
5489
+ return { action: operation, target, params: { path: path5(action.path, env) }, usesCurrentFocus: false, needsForeground: false };
5267
5490
  case "moveMouse":
5268
5491
  return { action: operation, target, params: {}, usesCurrentFocus: false, needsForeground: false };
5269
5492
  }
@@ -5289,6 +5512,7 @@ function outcomeAfterObservedValues(current, actions, valueForRef) {
5289
5512
  }
5290
5513
 
5291
5514
  // vendor/computer-use-axi/src/cdp.ts
5515
+ init_test_backend_guard();
5292
5516
  import { randomUUID as randomUUID3 } from "node:crypto";
5293
5517
 
5294
5518
  // ../../node_modules/ws/wrapper.mjs
@@ -5792,6 +6016,7 @@ var CdpTab = class _CdpTab {
5792
6016
  loadFired;
5793
6017
  static async connect(wsUrl, targetId, title, epoch = connectionEpoch) {
5794
6018
  assertConnectionEpoch(epoch);
6019
+ assertNativeBackendAllowed();
5795
6020
  const ws = new wrapper_default(wsUrl);
5796
6021
  connectingSockets.add(ws);
5797
6022
  try {
@@ -5951,6 +6176,7 @@ var CdpTab = class _CdpTab {
5951
6176
  return entries;
5952
6177
  }
5953
6178
  send(method, params = {}) {
6179
+ assertNativeBackendAllowed();
5954
6180
  const id = this.nextId++;
5955
6181
  return new Promise((resolve2, reject) => {
5956
6182
  const timer = setTimeout(() => {
@@ -6028,7 +6254,7 @@ var CdpTab = class _CdpTab {
6028
6254
  };
6029
6255
  var connectedTabs = /* @__PURE__ */ new Map();
6030
6256
  var connectingTabs = /* @__PURE__ */ new Map();
6031
- var lastConnectFailureAt = 0;
6257
+ var lastConnectFailureAt = /* @__PURE__ */ new Map();
6032
6258
  function disconnectCdp() {
6033
6259
  if (disconnecting) return disconnecting;
6034
6260
  connectionEpoch++;
@@ -6037,62 +6263,64 @@ function disconnectCdp() {
6037
6263
  connectingSockets.clear();
6038
6264
  connectedTabs.clear();
6039
6265
  connectingTabs.clear();
6040
- lastConnectFailureAt = 0;
6266
+ lastConnectFailureAt.clear();
6041
6267
  disconnecting = Promise.all(tabs.map((tab) => tab.close())).then(() => void 0).finally(() => {
6042
6268
  disconnecting = void 0;
6043
6269
  });
6044
6270
  return disconnecting;
6045
6271
  }
6046
- function cdpEnabled() {
6047
- const rawPort = process.env.PIE_COMPUTER_USE_CDP_PORT ?? "";
6272
+ function cdpEnabled(rawPort = getComputerUseEnvironmentValue("PIE_COMPUTER_USE_CDP_PORT") ?? "") {
6048
6273
  if (!/^\d+$/.test(rawPort)) return false;
6049
6274
  const port = Number(rawPort);
6050
6275
  return Number.isInteger(port) && port > 0 && port <= 65535 && typeof wrapper_default !== "undefined";
6051
6276
  }
6052
6277
  async function cdpTabForWindow(windowTitle, frame) {
6053
6278
  const epoch = connectionEpoch;
6054
- if (!cdpEnabled()) return void 0;
6055
- if (Date.now() - lastConnectFailureAt < CONNECT_FAILURE_RETRY_MS) return void 0;
6279
+ const port = getComputerUseEnvironmentValue("PIE_COMPUTER_USE_CDP_PORT") ?? "";
6280
+ if (!cdpEnabled(port)) return void 0;
6281
+ if (Date.now() - (lastConnectFailureAt.get(port) ?? 0) < CONNECT_FAILURE_RETRY_MS) return void 0;
6056
6282
  try {
6057
6283
  assertConnectionEpoch(epoch);
6058
- for (const tab of connectedTabs.values()) {
6284
+ for (const [key2, tab] of connectedTabs) {
6285
+ if (!key2.startsWith(`${port}:`)) continue;
6059
6286
  if (tab.isOpen && titlesMatch(tab.title, windowTitle) && await tabMatchesFrame(tab, frame)) {
6060
6287
  assertConnectionEpoch(epoch);
6061
6288
  if (tab.isOpen) return tab;
6062
6289
  }
6063
6290
  }
6064
- const pages = await cdpPages(epoch);
6291
+ const pages = await cdpPages(epoch, port);
6065
6292
  const match = await pickTab(pages, windowTitle, frame, epoch);
6066
6293
  assertConnectionEpoch(epoch);
6067
6294
  if (!match) return void 0;
6068
- const existing = connectedTabs.get(match.id);
6295
+ const key = `${port}:${match.id}`;
6296
+ const existing = connectedTabs.get(key);
6069
6297
  if (existing?.isOpen) {
6070
6298
  existing.title = match.title;
6071
6299
  return existing;
6072
6300
  }
6073
- let connecting = connectingTabs.get(match.id);
6301
+ let connecting = connectingTabs.get(key);
6074
6302
  if (!connecting) {
6075
6303
  connecting = CdpTab.connect(match.webSocketDebuggerUrl, match.id, match.title, epoch);
6076
- connectingTabs.set(match.id, connecting);
6304
+ connectingTabs.set(key, connecting);
6077
6305
  }
6078
6306
  let connected;
6079
6307
  try {
6080
6308
  connected = await connecting;
6081
6309
  } finally {
6082
- if (connectingTabs.get(match.id) === connecting) connectingTabs.delete(match.id);
6310
+ if (connectingTabs.get(key) === connecting) connectingTabs.delete(key);
6083
6311
  }
6084
6312
  assertConnectionEpoch(epoch);
6085
- connectedTabs.set(match.id, connected);
6313
+ connectedTabs.set(key, connected);
6086
6314
  return connected;
6087
6315
  } catch {
6088
- if (epoch === connectionEpoch && !disconnecting) lastConnectFailureAt = Date.now();
6316
+ if (epoch === connectionEpoch && !disconnecting) lastConnectFailureAt.set(port, Date.now());
6089
6317
  return void 0;
6090
6318
  }
6091
6319
  }
6092
- async function listCdpPageContexts() {
6093
- const pages = await cdpPages();
6320
+ async function listCdpPageContexts(port = getComputerUseEnvironmentValue("PIE_COMPUTER_USE_CDP_PORT")) {
6321
+ const pages = await cdpPages(connectionEpoch, port);
6094
6322
  return pages.map((page) => ({
6095
- contextId: cdpContextId(page.id),
6323
+ contextId: cdpContextId(page.id, port),
6096
6324
  targetId: page.id,
6097
6325
  title: page.title,
6098
6326
  url: page.url ?? ""
@@ -6209,14 +6437,18 @@ async function withCdpContextTab(contextId, run) {
6209
6437
  }
6210
6438
  async function cdpPageForContext(contextId, epoch) {
6211
6439
  if (!contextId.startsWith(CDP_CONTEXT_PREFIX)) return void 0;
6212
- const targetId = contextId.slice(CDP_CONTEXT_PREFIX.length);
6213
- const pages = await cdpPages(epoch);
6440
+ const identity = contextId.slice(CDP_CONTEXT_PREFIX.length);
6441
+ const separator = identity.indexOf(":");
6442
+ if (separator < 1) return void 0;
6443
+ const port = identity.slice(0, separator);
6444
+ const targetId = identity.slice(separator + 1);
6445
+ const pages = await cdpPages(epoch, port);
6214
6446
  return pages.find((candidate) => candidate.id === targetId);
6215
6447
  }
6216
- async function cdpPages(epoch = connectionEpoch) {
6448
+ async function cdpPages(epoch = connectionEpoch, port = getComputerUseEnvironmentValue("PIE_COMPUTER_USE_CDP_PORT")) {
6217
6449
  assertConnectionEpoch(epoch);
6218
- if (!cdpEnabled()) return [];
6219
- const port = process.env.PIE_COMPUTER_USE_CDP_PORT;
6450
+ if (!port || !cdpEnabled(port)) return [];
6451
+ assertNativeBackendAllowed();
6220
6452
  const response = await fetch(`http://127.0.0.1:${port}/json/list`, { signal: AbortSignal.timeout(2e3) });
6221
6453
  const targets = await response.json();
6222
6454
  assertConnectionEpoch(epoch);
@@ -6224,8 +6456,8 @@ async function cdpPages(epoch = connectionEpoch) {
6224
6456
  (target) => target.type === "page" && target.webSocketDebuggerUrl && isLocalDebuggerWebSocket(target.webSocketDebuggerUrl, port)
6225
6457
  );
6226
6458
  }
6227
- function cdpContextId(targetId) {
6228
- return `${CDP_CONTEXT_PREFIX}${targetId}`;
6459
+ function cdpContextId(targetId, port) {
6460
+ return `${CDP_CONTEXT_PREFIX}${port}:${targetId}`;
6229
6461
  }
6230
6462
  function axString(raw) {
6231
6463
  const value = raw?.value ?? raw;
@@ -6372,98 +6604,6 @@ function titlesMatch(tabTitle, windowTitle) {
6372
6604
  return tab === win || win.startsWith(tab) || tab.startsWith(win);
6373
6605
  }
6374
6606
 
6375
- // vendor/computer-use-axi/src/config.ts
6376
- import { existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
6377
- import { AsyncLocalStorage } from "node:async_hooks";
6378
- import path5 from "node:path";
6379
-
6380
- // vendor/computer-use-axi/host.ts
6381
- function getAgentDir() {
6382
- return getConfigDir();
6383
- }
6384
-
6385
- // vendor/computer-use-axi/src/config.ts
6386
- var DEFAULT_CONFIG = {
6387
- browser_use: true,
6388
- headless: false,
6389
- cursor_overlay: true,
6390
- managed_browser: "chrome"
6391
- };
6392
- var operationConfig = new AsyncLocalStorage();
6393
- var defaultLoaded = Object.freeze({ config: Object.freeze({ ...DEFAULT_CONFIG }), sources: [], env: {} });
6394
- function withComputerUseConfig(cwd, run) {
6395
- return operationConfig.run(loadComputerUseConfig(cwd), run);
6396
- }
6397
- function parseBoolean(value) {
6398
- if (typeof value === "boolean") return value;
6399
- if (typeof value === "number") return value === 1 ? true : value === 0 ? false : void 0;
6400
- if (typeof value !== "string") return void 0;
6401
- const normalized = value.trim().toLowerCase();
6402
- if (["1", "true", "yes", "on", "enabled"].includes(normalized)) return true;
6403
- if (["0", "false", "no", "off", "disabled"].includes(normalized)) return false;
6404
- return void 0;
6405
- }
6406
- function normalizePartial(raw) {
6407
- if (!raw || typeof raw !== "object") return {};
6408
- const source = raw.computer_use && typeof raw.computer_use === "object" ? raw.computer_use : raw;
6409
- const out = {};
6410
- const browserUse = parseBoolean(source.browser_use);
6411
- const headless = parseBoolean(source.headless);
6412
- const cursorOverlay = parseBoolean(source.cursor_overlay);
6413
- if (browserUse !== void 0) out.browser_use = browserUse;
6414
- if (headless !== void 0) out.headless = headless;
6415
- if (cursorOverlay !== void 0) out.cursor_overlay = cursorOverlay;
6416
- const managedBrowser = source.managed_browser;
6417
- if (managedBrowser === "helium" || managedBrowser === "chrome") out.managed_browser = managedBrowser;
6418
- return out;
6419
- }
6420
- function readConfigFile(filePath) {
6421
- if (!existsSync3(filePath)) return { path: filePath, exists: false };
6422
- try {
6423
- const parsed = JSON.parse(readFileSync3(filePath, "utf-8"));
6424
- return { path: filePath, exists: true, values: normalizePartial(parsed) };
6425
- } catch (error) {
6426
- return { path: filePath, exists: true, error: error instanceof Error ? error.message : String(error) };
6427
- }
6428
- }
6429
- function readEnv() {
6430
- const out = {};
6431
- const browserUse = parseBoolean(process.env.PIE_COMPUTER_USE_BROWSER_USE);
6432
- const headless = parseBoolean(process.env.PIE_COMPUTER_USE_HEADLESS);
6433
- const cursorOverlay = parseBoolean(process.env.PIE_COMPUTER_USE_CURSOR_OVERLAY);
6434
- if (browserUse !== void 0) out.browser_use = browserUse;
6435
- if (headless !== void 0) out.headless = headless;
6436
- if (cursorOverlay !== void 0) out.cursor_overlay = cursorOverlay;
6437
- const managedBrowser = process.env.PIE_COMPUTER_USE_MANAGED_BROWSER;
6438
- if (managedBrowser === "helium" || managedBrowser === "chrome") out.managed_browser = managedBrowser;
6439
- return out;
6440
- }
6441
- function loadComputerUseConfig(cwd) {
6442
- const sources = [
6443
- readConfigFile(path5.join(getAgentDir(), "extensions", "computer-use.json")),
6444
- readConfigFile(path5.join(cwd, ".pie", "computer-use.json"))
6445
- ];
6446
- const env = readEnv();
6447
- const config = { ...DEFAULT_CONFIG };
6448
- for (const source of sources) {
6449
- if (source.values) Object.assign(config, source.values);
6450
- }
6451
- Object.assign(config, env);
6452
- return Object.freeze({ config: Object.freeze(config), sources, env });
6453
- }
6454
- function getComputerUseConfig() {
6455
- return getLoadedComputerUseConfig().config;
6456
- }
6457
- function getLoadedComputerUseConfig() {
6458
- return operationConfig.getStore() ?? defaultLoaded;
6459
- }
6460
- function isHeadlessMode() {
6461
- return getComputerUseConfig().headless;
6462
- }
6463
- function isBrowserUseEnabled() {
6464
- return getComputerUseConfig().browser_use;
6465
- }
6466
-
6467
6607
  // vendor/computer-use-axi/src/note.ts
6468
6608
  function normalizedLabel(value) {
6469
6609
  return value.toLowerCase().replace(/\s+/g, " ").trim();
@@ -6617,8 +6757,9 @@ function renderNote(note) {
6617
6757
  }
6618
6758
 
6619
6759
  // vendor/computer-use-axi/src/output.ts
6620
- import { closeSync as closeSync2, mkdirSync as mkdirSync5, openSync as openSync2, readSync, readdirSync as readdirSync3, statSync, unlinkSync as unlinkSync3, writeFileSync as writeFileSync2 } from "node:fs";
6760
+ import { closeSync as closeSync2, openSync as openSync2, readSync, readdirSync as readdirSync3, statSync, unlinkSync as unlinkSync3, writeFileSync as writeFileSync2 } from "node:fs";
6621
6761
  import path6 from "node:path";
6762
+ import { createHash as createHash3 } from "node:crypto";
6622
6763
  var MODEL_TEXT_MAX_BYTES = 48 * 1024;
6623
6764
  var MODEL_TEXT_MAX_LINES = 2e3;
6624
6765
  var OUTPUT_PAGE_BYTES = 16 * 1024;
@@ -6630,8 +6771,13 @@ var outputs = /* @__PURE__ */ new Map();
6630
6771
  var nextOutputId = 1;
6631
6772
  var outputBytes = 0;
6632
6773
  function outputDirectoryFor(owner) {
6633
- const dir = path6.join(outputsDir(), "owners", normalizeComputerUseOwner(owner));
6634
- mkdirSync5(dir, { recursive: true, mode: 448 });
6774
+ const root = outputsDir();
6775
+ const owners = path6.join(root, "owners");
6776
+ const ownerKey = `owner-${createHash3("sha256").update(normalizeComputerUseOwner(owner)).digest("hex")}`;
6777
+ const dir = path6.join(owners, ownerKey);
6778
+ ensurePrivateDirectory(root);
6779
+ ensurePrivateDirectory(owners);
6780
+ ensurePrivateDirectory(dir);
6635
6781
  return dir;
6636
6782
  }
6637
6783
  function utf8Prefix(bytes, maxBytes) {
@@ -6763,6 +6909,9 @@ function clearStoredOutputs(owner) {
6763
6909
  // vendor/computer-use-axi/src/bridge.ts
6764
6910
  init_coerce();
6765
6911
 
6912
+ // vendor/computer-use-axi/src/platform/index.ts
6913
+ init_test_backend_guard();
6914
+
6766
6915
  // vendor/computer-use-axi/src/platform/macos/backend.ts
6767
6916
  init_coerce();
6768
6917
  init_helper();
@@ -6983,6 +7132,7 @@ init_architecture();
6983
7132
  init_coerce();
6984
7133
 
6985
7134
  // vendor/computer-use-axi/src/platform/linux/helper.ts
7135
+ init_test_backend_guard();
6986
7136
  init_package_root();
6987
7137
  import { spawn as spawn2 } from "node:child_process";
6988
7138
  import { randomUUID as randomUUID4 } from "node:crypto";
@@ -7006,6 +7156,7 @@ async function isExecutable2(filePath) {
7006
7156
  async function runProcess2(command, args, timeoutMs, signal, env) {
7007
7157
  if (signal?.aborted) throw new Error("Operation aborted.");
7008
7158
  await new Promise((resolve2, reject) => {
7159
+ assertNativeBackendAllowed();
7009
7160
  const child = spawn2(command, args, { stdio: ["ignore", "pipe", "pipe"], env });
7010
7161
  let stdout = "";
7011
7162
  let stderr = "";
@@ -7093,6 +7244,7 @@ var LinuxHelperClient = class {
7093
7244
  this.buffer = "";
7094
7245
  }
7095
7246
  async ensureInstalled(signal) {
7247
+ assertNativeBackendAllowed();
7096
7248
  if (await isExecutable2(this.helperPath) && this.installChecked) return;
7097
7249
  if (!this.installPromise) {
7098
7250
  const installPromise = (async () => {
@@ -7136,6 +7288,7 @@ var LinuxHelperClient = class {
7136
7288
  return await waitForShared(this.processPromise, signal);
7137
7289
  }
7138
7290
  async startProcess() {
7291
+ assertNativeBackendAllowed();
7139
7292
  const child = spawn2(this.helperPath, [], { stdio: ["pipe", "pipe", "pipe"] });
7140
7293
  child.stdout.setEncoding("utf8");
7141
7294
  child.stderr.setEncoding("utf8");
@@ -7188,6 +7341,7 @@ var LinuxHelperClient = class {
7188
7341
  }
7189
7342
  }
7190
7343
  async command(cmd, args = {}, options) {
7344
+ assertNativeBackendAllowed();
7191
7345
  const child = await this.process(options?.signal);
7192
7346
  const id = randomUUID4();
7193
7347
  const timeoutMs = options?.timeoutMs ?? COMMAND_TIMEOUT_MS3;
@@ -7387,6 +7541,7 @@ var linuxBackend = {
7387
7541
  init_coerce();
7388
7542
 
7389
7543
  // vendor/computer-use-axi/src/platform/windows/helper.ts
7544
+ init_test_backend_guard();
7390
7545
  init_package_root();
7391
7546
  import { spawn as spawn3 } from "node:child_process";
7392
7547
  import { randomUUID as randomUUID5 } from "node:crypto";
@@ -7410,6 +7565,7 @@ async function isExecutable3(filePath) {
7410
7565
  async function runProcess3(command, args, timeoutMs, signal, env) {
7411
7566
  if (signal?.aborted) throw new Error("Operation aborted.");
7412
7567
  await new Promise((resolve2, reject) => {
7568
+ assertNativeBackendAllowed();
7413
7569
  const child = spawn3(command, args, { stdio: ["ignore", "pipe", "pipe"], env });
7414
7570
  let stdout = "";
7415
7571
  let stderr = "";
@@ -7470,6 +7626,7 @@ var WindowsHelperClient = class {
7470
7626
  child.unref();
7471
7627
  }
7472
7628
  async ensureInstalled(signal) {
7629
+ assertNativeBackendAllowed();
7473
7630
  if (await isExecutable3(WINDOWS_HELPER_PATH) && this.installChecked) return;
7474
7631
  await runProcess3(process.execPath, [SETUP_HELPER_SCRIPT3, "--platform", "windows", "--runtime"], HELPER_SETUP_TIMEOUT_MS3, signal, { ...process.env, ELECTRON_RUN_AS_NODE: "1", BUN_BE_BUN: "1" });
7475
7632
  this.installChecked = true;
@@ -7478,6 +7635,7 @@ var WindowsHelperClient = class {
7478
7635
  async process(signal) {
7479
7636
  await this.ensureInstalled(signal);
7480
7637
  if (this.child && this.child.exitCode === null && !this.child.killed) return this.child;
7638
+ assertNativeBackendAllowed();
7481
7639
  const child = spawn3(WINDOWS_HELPER_PATH, [], { stdio: ["pipe", "pipe", "pipe"] });
7482
7640
  child.stdout.setEncoding("utf8");
7483
7641
  child.stderr.setEncoding("utf8");
@@ -7523,6 +7681,7 @@ var WindowsHelperClient = class {
7523
7681
  }
7524
7682
  }
7525
7683
  async command(cmd, args = {}, options) {
7684
+ assertNativeBackendAllowed();
7526
7685
  const child = await this.process(options?.signal);
7527
7686
  const id = randomUUID5();
7528
7687
  const timeoutMs = options?.timeoutMs ?? COMMAND_TIMEOUT_MS4;
@@ -7746,10 +7905,17 @@ var UnsupportedPlatformBackend = class {
7746
7905
  }
7747
7906
  };
7748
7907
  function platformBackendForRuntime(platform = process.platform) {
7749
- if (platform === "darwin") return macosPlatformBackend;
7750
- if (platform === "win32") return windowsBackend;
7751
- if (platform === "linux") return linuxBackend;
7752
- return new UnsupportedPlatformBackend(platform);
7908
+ const backend = platform === "darwin" ? macosPlatformBackend : platform === "win32" ? windowsBackend : platform === "linux" ? linuxBackend : new UnsupportedPlatformBackend(platform);
7909
+ return new Proxy(backend, {
7910
+ get(target, property, receiver) {
7911
+ const value = Reflect.get(target, property, receiver);
7912
+ if (typeof value !== "function") return value;
7913
+ return (...args) => {
7914
+ assertNativeBackendAllowed();
7915
+ return Reflect.apply(value, target, args);
7916
+ };
7917
+ }
7918
+ });
7753
7919
  }
7754
7920
  var currentPlatformBackend = platformBackendForRuntime();
7755
7921
  var backendOverrideInstalled = false;
@@ -8378,6 +8544,7 @@ function desktopResourceKey(target) {
8378
8544
  }
8379
8545
  function persistOperation(state) {
8380
8546
  if (!state.currentTarget || !state.currentCapture || !state.currentLook || !state.currentOutline) return;
8547
+ if (expiredStateReasons.has(state.currentCapture.stateId) || state.helperGeneration !== helperGeneration) return;
8381
8548
  const resourceKey = state.resourceKey ?? desktopResourceKey(state.currentTarget);
8382
8549
  const epoch = state.epoch ?? resourceScheduler.epoch(resourceKey);
8383
8550
  savedStates.saveDesktop(state, resourceKey, epoch);
@@ -8401,10 +8568,7 @@ async function releaseComputerUseSession() {
8401
8568
  });
8402
8569
  await attempt("drain scheduler", () => drain);
8403
8570
  await attempt("release owned browser", () => releaseOwnedBrowserState(runtimeState));
8404
- if (runtimeState.managedBrowserCdpPort && process.env.PIE_COMPUTER_USE_CDP_PORT === runtimeState.managedBrowserCdpPort) {
8405
- if (runtimeState.previousCdpPort === void 0) delete process.env.PIE_COMPUTER_USE_CDP_PORT;
8406
- else process.env.PIE_COMPUTER_USE_CDP_PORT = runtimeState.previousCdpPort;
8407
- }
8571
+ setManagedCdpPort(void 0);
8408
8572
  runtimeState.previousCdpPort = void 0;
8409
8573
  if (runtimeState.managedBrowser === void 0) {
8410
8574
  runtimeState.managedBrowserCdpPort = void 0;
@@ -8428,7 +8592,7 @@ function currentRuntimeMode() {
8428
8592
  }
8429
8593
  function currentDeliveryPolicy() {
8430
8594
  if (isHeadlessMode()) return "background";
8431
- const value = (process.env.PIE_COMPUTER_USE_DELIVERY_POLICY ?? process.env.PIE_COMPUTER_USE_EVENT_DELIVERY ?? "default").toLowerCase();
8595
+ const value = (getComputerUseEnvironmentValue("PIE_COMPUTER_USE_DELIVERY_POLICY") ?? getComputerUseEnvironmentValue("PIE_COMPUTER_USE_EVENT_DELIVERY") ?? "default").toLowerCase();
8432
8596
  return value === "background" || value === "pid" ? "background" : value === "foreground" || value === "hid" ? "foreground" : value === "ax_only" || value === "ax-only" ? "ax_only" : "default";
8433
8597
  }
8434
8598
  function nativeInputDelivery(policy = currentDeliveryPolicy()) {
@@ -9359,7 +9523,7 @@ async function refreshBrowserSnapshot(contextId, tool, base) {
9359
9523
  const browser = await cdpSnapshotForContext(contextId);
9360
9524
  if (!browser) throw new Error(`Browser root '${contextId}' is no longer available. Call find_roots and observe_ui again.`);
9361
9525
  const state = operationState();
9362
- const resourceKey = state.resourceKey ?? `cdp:${browser.targetId}`;
9526
+ const resourceKey = state.resourceKey ?? `cdp:${browser.contextId.slice(BROWSER_CONTEXT_PREFIX.length)}`;
9363
9527
  return browserObservationResult(browser, resourceKey, state.epoch ?? resourceScheduler.epoch(resourceKey), tool, base);
9364
9528
  }
9365
9529
  function sliceText(value, offsetValue, _limitValue) {
@@ -9998,7 +10162,7 @@ async function performAct(params, signal) {
9998
10162
  }
9999
10163
  function managedBrowserExecutableCandidates(browser) {
10000
10164
  const overrideName = browser === "helium" ? "PIE_COMPUTER_USE_HELIUM_EXECUTABLE" : "PIE_COMPUTER_USE_CHROME_EXECUTABLE";
10001
- const override = trimOrUndefined(process.env[overrideName]);
10165
+ const override = trimOrUndefined(getComputerUseEnvironmentValue(overrideName));
10002
10166
  if (override) return [path13.resolve(override)];
10003
10167
  const platformCandidates = process.platform === "darwin" ? browser === "helium" ? [
10004
10168
  "/Applications/Helium.app/Contents/MacOS/Helium",
@@ -10039,7 +10203,7 @@ async function managedBrowserExecutable(browser) {
10039
10203
  }
10040
10204
  }
10041
10205
  const overrideName = browser === "helium" ? "PIE_COMPUTER_USE_HELIUM_EXECUTABLE" : "PIE_COMPUTER_USE_CHROME_EXECUTABLE";
10042
- if (trimOrUndefined(process.env[overrideName])) {
10206
+ if (trimOrUndefined(getComputerUseEnvironmentValue(overrideName))) {
10043
10207
  throw new Error(`${browser} executable from ${overrideName} was not found or is not executable: ${candidates[0]}.`);
10044
10208
  }
10045
10209
  throw new Error(`${browser} executable was not found. Set ${overrideName} to its absolute path.`);
@@ -10069,6 +10233,7 @@ async function waitForCdpPort(port, signal) {
10069
10233
  throw new Error(`Managed browser did not expose CDP on port ${port} within ${MANAGED_BROWSER_READY_TIMEOUT_MS}ms.`);
10070
10234
  }
10071
10235
  async function performLaunchBrowser(params, signal) {
10236
+ assertNativeBackendAllowed();
10072
10237
  const browser = getComputerUseConfig().managed_browser;
10073
10238
  const executable = await managedBrowserExecutable(browser);
10074
10239
  const port = await freeTcpPort();
@@ -10085,9 +10250,6 @@ async function performLaunchBrowser(params, signal) {
10085
10250
  "--no-default-browser-check",
10086
10251
  url
10087
10252
  ];
10088
- if (runtimeState.previousCdpPort === void 0 && runtimeState.managedBrowserCdpPort === void 0) {
10089
- runtimeState.previousCdpPort = process.env.PIE_COMPUTER_USE_CDP_PORT;
10090
- }
10091
10253
  const managedBrowser = spawn4(executable, args, { stdio: "ignore", detached: true });
10092
10254
  managedBrowser.unref();
10093
10255
  const stalePid = runtimeState.managedBrowserPid;
@@ -10102,22 +10264,21 @@ async function performLaunchBrowser(params, signal) {
10102
10264
  runtimeState.managedBrowserPid = managedBrowser.pid;
10103
10265
  runtimeState.managedBrowserProfileDir = profileDir;
10104
10266
  persistRuntimeBridgeState();
10105
- process.env.PIE_COMPUTER_USE_CDP_PORT = String(port);
10267
+ setManagedCdpPort(String(port));
10106
10268
  try {
10107
10269
  await waitForCdpPort(port, signal);
10108
10270
  } catch (error) {
10109
10271
  if (runtimeState.managedBrowser === managedBrowser) {
10110
10272
  await releaseOwnedBrowserState(runtimeState);
10111
- if (runtimeState.previousCdpPort === void 0) delete process.env.PIE_COMPUTER_USE_CDP_PORT;
10112
- else process.env.PIE_COMPUTER_USE_CDP_PORT = runtimeState.previousCdpPort;
10273
+ setManagedCdpPort(void 0);
10113
10274
  runtimeState.managedBrowserCdpPort = void 0;
10114
10275
  runtimeState.previousCdpPort = void 0;
10115
10276
  }
10116
10277
  throw error;
10117
10278
  }
10118
- const page = (await listCdpPageContexts())[0];
10279
+ const page = (await listCdpPageContexts(String(port)))[0];
10119
10280
  if (!page) throw new Error("Managed browser launched without a CDP page context.");
10120
- const resourceKey = `cdp:${page.targetId}`;
10281
+ const resourceKey = `cdp:${page.contextId.slice(BROWSER_CONTEXT_PREFIX.length)}`;
10121
10282
  const scheduled = await resourceScheduler.read(resourceKey, async () => await cdpSnapshotForContext(page.contextId));
10122
10283
  if (!scheduled.value) throw new Error("Managed browser page could not be observed after launch.");
10123
10284
  return browserObservationResult(scheduled.value, resourceKey, scheduled.epoch, "launch_browser");
@@ -10178,6 +10339,9 @@ async function executeTool(tool, ctx, params, signal, run, runtime) {
10178
10339
  const outputRef = tool === "read_text" && trimOrUndefined(params?.ref)?.startsWith("@o") === true;
10179
10340
  const requestedStateId = outputRef ? void 0 : trimOrUndefined(params?.stateId);
10180
10341
  const stateRecord = requestedStateId ? savedStates.get(requestedStateId) : void 0;
10342
+ if (requestedStateId && expiredStateReasons.has(requestedStateId)) {
10343
+ throw new Error(`State '${requestedStateId}' expired because ${expiredStateReasons.get(requestedStateId)}. Observe the root again.`);
10344
+ }
10181
10345
  if (requestedStateId && !stateRecord) {
10182
10346
  const reason = expiredStateReasons.get(requestedStateId);
10183
10347
  if (reason) {
@@ -10197,6 +10361,7 @@ async function executeTool(tool, ctx, params, signal, run, runtime) {
10197
10361
  operation.onDispatch = runtime?.onDispatch;
10198
10362
  if (requestedStateId && stateRecord) assertStateAccess(requestedStateId, stateRecord, operation);
10199
10363
  return await withComputerUseConfig(ctx.cwd, () => savedStates.operations.run(operation, async () => {
10364
+ if (requestedStateId) operation.artifactIds = engineArtifacts?.pinForState(requestedStateId) ?? [];
10200
10365
  try {
10201
10366
  const browserOnly = operation.contextId || ["launch_browser", "navigate_browser", "evaluate_browser"].includes(tool) || tool === "find_roots" && params.kind === "browser_page" || tool === "observe_ui" && runtimeState.browserContextByRoot.has(params.root ?? "");
10202
10367
  await resourceScheduler.read("session-lifecycle", async () => {
@@ -10213,7 +10378,7 @@ async function executeTool(tool, ctx, params, signal, run, runtime) {
10213
10378
  } finally {
10214
10379
  for (const artifactId of operation.artifactIds ?? []) engineArtifacts?.unpin(artifactId);
10215
10380
  }
10216
- }));
10381
+ }), runtime?.environment);
10217
10382
  }
10218
10383
  var BROKER_PERFORMERS = {
10219
10384
  find_roots: performListWindows,
@@ -10318,7 +10483,7 @@ async function probeBroker(endpoint, timeoutMs = 800) {
10318
10483
  };
10319
10484
  socket.setTimeout(timeoutMs, () => finish({ error: "timeout" }));
10320
10485
  socket.on("connect", () => {
10321
- socket.write(encodeFrame({ type: "hello", protocolVersion: BROKER_PROTOCOL_VERSION, clientVersion: "probe" }));
10486
+ socket.write(encodeFrame({ type: "hello", protocolVersion: BROKER_PROTOCOL_VERSION, clientVersion: "probe", buildIdentity: BROKER_BUILD.identity }));
10322
10487
  });
10323
10488
  socket.on("data", (chunk) => {
10324
10489
  try {
@@ -10494,6 +10659,7 @@ async function startBroker(options = {}) {
10494
10659
  helperGeneration: currentHelperGenerationSafe(),
10495
10660
  requestId: request.requestId,
10496
10661
  cwd: request.cwd,
10662
+ environment: request.environment,
10497
10663
  signal: entry.controller.signal,
10498
10664
  onDispatch: () => {
10499
10665
  entry.dispatched = true;
@@ -10517,7 +10683,7 @@ async function startBroker(options = {}) {
10517
10683
  return;
10518
10684
  }
10519
10685
  const key = request.requestId;
10520
- const fingerprint = requestFingerprint(request.command, request.params, request.cwd);
10686
+ const fingerprint = requestFingerprint(request.command, request.params, request.cwd, request.environment);
10521
10687
  const existing = entries.get(key);
10522
10688
  if (existing) {
10523
10689
  if (existing.ownerId !== request.ownerId || existing.command !== request.command || existing.fingerprint !== fingerprint) {
@@ -10729,7 +10895,8 @@ async function startBroker(options = {}) {
10729
10895
  pid: process.pid,
10730
10896
  startedAt,
10731
10897
  platform: process.platform,
10732
- transport: endpoint.kind
10898
+ transport: endpoint.kind,
10899
+ backend: { fakeInstalled: options.fakeBackendInstalled === true, requireFake: requireFakeBackend() }
10733
10900
  });
10734
10901
  } catch (error) {
10735
10902
  helloRejected = true;
@@ -10886,13 +11053,6 @@ async function startBroker(options = {}) {
10886
11053
  clearDrainMarker();
10887
11054
  } catch {
10888
11055
  }
10889
- if (endpoint.kind === "unix-socket") {
10890
- try {
10891
- const stats = lstatSync4(endpoint.path);
10892
- if (stats.isSocket()) unlinkSync4(endpoint.path);
10893
- } catch {
10894
- }
10895
- }
10896
11056
  options.onShutdown?.();
10897
11057
  void reason;
10898
11058
  };
@@ -10918,7 +11078,7 @@ async function startBroker(options = {}) {
10918
11078
  uncertain,
10919
11079
  requests: entries.size,
10920
11080
  ownerCount: owners.size,
10921
- ownerFingerprints: [...owners].map((owner) => createHash3("sha256").update(owner).digest("hex").slice(0, 12)),
11081
+ ownerFingerprints: [...owners].map((owner) => createHash4("sha256").update(owner).digest("hex").slice(0, 12)),
10922
11082
  artifacts: artifacts.stats(),
10923
11083
  legacyCache,
10924
11084
  idleExitMs
@@ -10939,19 +11099,8 @@ async function startBroker(options = {}) {
10939
11099
  return statusHandle;
10940
11100
  }
10941
11101
 
10942
- // vendor/computer-use-axi/src/test-backend-guard.ts
10943
- function resolveTestOnlyBackend(env = process.env) {
10944
- if (env.PIE_COMPUTER_USE_FAKE_BACKEND !== "1") return { enabled: false };
10945
- if (env.PIE_COMPUTER_USE_ENABLE_TEST_COMMANDS !== "1") {
10946
- return {
10947
- enabled: false,
10948
- warning: "PIE_COMPUTER_USE_FAKE_BACKEND is test-only and was ignored: it requires PIE_COMPUTER_USE_ENABLE_TEST_COMMANDS=1 as well."
10949
- };
10950
- }
10951
- return { enabled: true };
10952
- }
10953
-
10954
11102
  // vendor/computer-use-axi/src/broker-main.ts
11103
+ init_test_backend_guard();
10955
11104
  async function loadTestOnlyBackend() {
10956
11105
  const decision = resolveTestOnlyBackend();
10957
11106
  if (decision.warning) process.stderr.write(`computer-use-axi broker: ${decision.warning}
@@ -10964,6 +11113,7 @@ async function loadTestOnlyBackend() {
10964
11113
  setPlatformBackend(fake);
10965
11114
  return fake;
10966
11115
  } catch (error) {
11116
+ if (requireFakeBackend()) throw new Error("Required fake backend failed to load; refusing native fallback.", { cause: error });
10967
11117
  process.stderr.write(
10968
11118
  `computer-use-axi broker: the test-only fake backend is not available in this build (${error instanceof Error ? error.message : String(error)}); continuing with the real platform backend.
10969
11119
  `
@@ -10976,6 +11126,7 @@ async function main() {
10976
11126
  const testCommands = process.env.PIE_COMPUTER_USE_ENABLE_TEST_COMMANDS === "1";
10977
11127
  const handle = await startBroker({
10978
11128
  testCommands,
11129
+ fakeBackendInstalled: Boolean(fake),
10979
11130
  // Fail-closed capacity contract hooks (tests only; both require the
10980
11131
  // explicit test-command switch and are ignored in every other build).
10981
11132
  resultCacheLimit: testCommands && Number.isFinite(Number(process.env.PIE_COMPUTER_USE_TEST_RESULT_CACHE_LIMIT)) ? Math.trunc(Number(process.env.PIE_COMPUTER_USE_TEST_RESULT_CACHE_LIMIT)) : void 0,