open-agents-ai 0.103.17 → 0.103.19

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.
Files changed (2) hide show
  1. package/dist/index.js +1661 -251
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1257,9 +1257,58 @@ var init_dist = __esm({
1257
1257
  });
1258
1258
 
1259
1259
  // packages/execution/dist/tool-executor.js
1260
+ var DEFAULT_CONFIG2, ToolExecutor;
1260
1261
  var init_tool_executor = __esm({
1261
1262
  "packages/execution/dist/tool-executor.js"() {
1262
1263
  "use strict";
1264
+ DEFAULT_CONFIG2 = {
1265
+ workingDir: ".",
1266
+ timeout: 3e4,
1267
+ maxOutputSize: 1e5
1268
+ };
1269
+ ToolExecutor = class {
1270
+ tools = /* @__PURE__ */ new Map();
1271
+ config;
1272
+ constructor(config) {
1273
+ this.config = { ...DEFAULT_CONFIG2, ...config };
1274
+ }
1275
+ register(tool) {
1276
+ this.tools.set(tool.name, tool);
1277
+ }
1278
+ getToolDefinitions() {
1279
+ return Array.from(this.tools.values()).map((tool) => ({
1280
+ type: "function",
1281
+ function: {
1282
+ name: tool.name,
1283
+ description: tool.description,
1284
+ parameters: tool.parameters
1285
+ }
1286
+ }));
1287
+ }
1288
+ async execute(toolName, args) {
1289
+ const tool = this.tools.get(toolName);
1290
+ if (!tool) {
1291
+ return {
1292
+ success: false,
1293
+ output: "",
1294
+ error: `Unknown tool: ${toolName}`,
1295
+ durationMs: 0
1296
+ };
1297
+ }
1298
+ const start = performance.now();
1299
+ try {
1300
+ const result = await tool.execute(args);
1301
+ return result;
1302
+ } catch (error) {
1303
+ return {
1304
+ success: false,
1305
+ output: "",
1306
+ error: error instanceof Error ? error.message : String(error),
1307
+ durationMs: performance.now() - start
1308
+ };
1309
+ }
1310
+ }
1311
+ };
1263
1312
  }
1264
1313
  });
1265
1314
 
@@ -4845,6 +4894,9 @@ function hasCommand(cmd) {
4845
4894
  return false;
4846
4895
  }
4847
4896
  }
4897
+ function setSudoPassword(pw) {
4898
+ _sudoPassword = pw;
4899
+ }
4848
4900
  function runInstall(installCmd) {
4849
4901
  const plat = process.platform;
4850
4902
  let cmd = installCmd;
@@ -4944,6 +4996,54 @@ function ensureCommand(command) {
4944
4996
  _cache.set(command, result);
4945
4997
  return result;
4946
4998
  }
4999
+ function ensureDepsForGroup(group) {
5000
+ const deps = DESKTOP_DEPS.filter((d) => d.group === group);
5001
+ const results = deps.map((d) => ({ command: d.command, result: ensureCommand(d.command) }));
5002
+ const allAvailable = results.every((r) => r.result.available);
5003
+ const installed = results.filter((r) => r.result.installed);
5004
+ const failed = results.filter((r) => !r.result.available);
5005
+ const parts = [];
5006
+ if (installed.length > 0) {
5007
+ parts.push(`Installed: ${installed.map((r) => r.command).join(", ")}`);
5008
+ }
5009
+ if (failed.length > 0) {
5010
+ parts.push(`Missing: ${failed.map((r) => `${r.command} (${r.result.error})`).join(", ")}`);
5011
+ }
5012
+ return { allAvailable, results, summary: parts.join(". ") || "All dependencies available." };
5013
+ }
5014
+ function ensureAllDesktopDeps() {
5015
+ const results = DESKTOP_DEPS.map((d) => ({
5016
+ dep: d,
5017
+ result: ensureCommand(d.command)
5018
+ }));
5019
+ const installed = results.filter((r) => r.result.installed).map((r) => r.dep.command);
5020
+ const missing = results.filter((r) => !r.result.available).map((r) => r.dep.command);
5021
+ const allAvailable = missing.length === 0;
5022
+ const parts = [];
5023
+ if (installed.length > 0) {
5024
+ parts.push(`Auto-installed: ${installed.join(", ")}`);
5025
+ }
5026
+ if (missing.length > 0) {
5027
+ parts.push(`Could not install: ${missing.join(", ")}`);
5028
+ }
5029
+ return {
5030
+ allAvailable,
5031
+ summary: parts.join(". ") || "All desktop dependencies available.",
5032
+ installed,
5033
+ missing
5034
+ };
5035
+ }
5036
+ function checkDesktopDeps() {
5037
+ return DESKTOP_DEPS.map((d) => ({
5038
+ dep: d,
5039
+ available: hasCommand(d.command)
5040
+ }));
5041
+ }
5042
+ function resetDepCache() {
5043
+ _cache.clear();
5044
+ _detectedPkgManager = void 0;
5045
+ _sudoPassword = null;
5046
+ }
4947
5047
  var DESKTOP_DEPS, _detectedPkgManager, _cache, _sudoPassword;
4948
5048
  var init_system_deps = __esm({
4949
5049
  "packages/execution/dist/system-deps.js"() {
@@ -7759,6 +7859,10 @@ Details: ${err.message}` : "");
7759
7859
  throw new Error(moondreamError);
7760
7860
  }
7761
7861
  }
7862
+ function resetMoondreamClient() {
7863
+ moondreamClient = null;
7864
+ moondreamError = null;
7865
+ }
7762
7866
  function loadImageBuffer(workingDir, rawPath) {
7763
7867
  const fullPath = resolve16(workingDir, rawPath);
7764
7868
  if (!existsSync14(fullPath)) {
@@ -12167,9 +12271,16 @@ async function handleCmd(cmd) {
12167
12271
  riData = { prompt: riPrompt };
12168
12272
  }
12169
12273
 
12274
+ var riAuthKey = args.auth_key || '';
12275
+
12170
12276
  // Derive capability name from model
12171
12277
  var riCapName = 'inference:' + riModel.replace(/[^a-zA-Z0-9._-]/g, '_');
12172
- dlog('remote_infer: model=' + riModel + ' cap=' + riCapName + ' prompt_len=' + riPrompt.length);
12278
+ dlog('remote_infer: model=' + riModel + ' cap=' + riCapName + ' prompt_len=' + riPrompt.length + (riAuthKey ? ' auth=yes' : ''));
12279
+
12280
+ // Include auth key in data if provided
12281
+ if (riAuthKey) {
12282
+ riData.auth_key = riAuthKey;
12283
+ }
12173
12284
 
12174
12285
  // If target_peer specified, invoke directly. Otherwise auto-discover.
12175
12286
  if (riTargetPeer) {
@@ -12414,6 +12525,12 @@ async function handleCmd(cmd) {
12414
12525
 
12415
12526
  // \u2500\u2500 Metered Inference Exposure \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
12416
12527
  case 'expose': {
12528
+ // Auth key for gating inference access (passed from ExposeP2PGateway)
12529
+ var exposeAuthKey = args.auth_key || '';
12530
+ if (exposeAuthKey) {
12531
+ dlog('expose: auth key configured (' + exposeAuthKey.length + ' chars)');
12532
+ }
12533
+
12417
12534
  // Query Ollama for available models
12418
12535
  const ollamaUrl = args.ollama_url || process.env.OLLAMA_URL || 'http://localhost:11434';
12419
12536
  let models = [];
@@ -12513,6 +12630,25 @@ async function handleCmd(cmd) {
12513
12630
  const logFile = join(invocationsDir, Date.now() + '-' + capName + '.json');
12514
12631
  try { writeFileSync(logFile, JSON.stringify(logEntry, null, 2)); } catch {}
12515
12632
 
12633
+ // Auth key validation \u2014 if expose was started with auth_key, require it
12634
+ if (exposeAuthKey) {
12635
+ var reqAuthKey = '';
12636
+ if (request.data && typeof request.data === 'object' && request.data.auth_key) {
12637
+ reqAuthKey = request.data.auth_key;
12638
+ } else if (request.metadata && request.metadata.auth_key) {
12639
+ reqAuthKey = request.metadata.auth_key;
12640
+ }
12641
+ if (reqAuthKey !== exposeAuthKey) {
12642
+ dlog('expose: auth REJECTED for ' + capName + ' from ' + (request.from || 'unknown'));
12643
+ await stream.write({ type: 'invoke.accept', version: 1, requestId: request.requestId, accepted: false });
12644
+ await stream.write({ type: 'invoke.event', version: 1, requestId: request.requestId, seq: 0, event: 'error', data: 'Unauthorized \u2014 invalid or missing auth key' });
12645
+ await stream.write({ type: 'invoke.done', version: 1, requestId: request.requestId, usage: { inputBytes: 0, outputBytes: 0 } });
12646
+ stream.close();
12647
+ return;
12648
+ }
12649
+ dlog('expose: auth OK for ' + capName);
12650
+ }
12651
+
12516
12652
  // Collect input via stream data events
12517
12653
  let prompt = '';
12518
12654
  var dataChunks = [];
@@ -12655,6 +12791,64 @@ async function handleCmd(cmd) {
12655
12791
  }
12656
12792
  }
12657
12793
 
12794
+ // Register system_metrics capability \u2014 returns CPU/GPU/memory utilization
12795
+ if (typeof nexus.registerCapability === 'function') {
12796
+ nexus.registerCapability('system_metrics', async (request, stream) => {
12797
+ // Auth check for system_metrics too
12798
+ if (exposeAuthKey) {
12799
+ var smAuthKey = '';
12800
+ if (request.data && typeof request.data === 'object' && request.data.auth_key) {
12801
+ smAuthKey = request.data.auth_key;
12802
+ } else if (request.metadata && request.metadata.auth_key) {
12803
+ smAuthKey = request.metadata.auth_key;
12804
+ }
12805
+ if (smAuthKey !== exposeAuthKey) {
12806
+ await stream.write({ type: 'invoke.accept', version: 1, requestId: request.requestId, accepted: false });
12807
+ await stream.write({ type: 'invoke.done', version: 1, requestId: request.requestId, usage: { inputBytes: 0, outputBytes: 0 } });
12808
+ stream.close();
12809
+ return;
12810
+ }
12811
+ }
12812
+ await stream.write({ type: 'invoke.accept', version: 1, requestId: request.requestId, accepted: true });
12813
+ try {
12814
+ var os = require('os');
12815
+ var loads = os.loadavg();
12816
+ var cores = os.cpus().length;
12817
+ var totalMem = os.totalmem();
12818
+ var freeMem = os.freemem();
12819
+ var usedMem = totalMem - freeMem;
12820
+ var gpuInfo = { available: false, name: '', utilization: 0, vramUsedMB: 0, vramTotalMB: 0, vramUtilization: 0 };
12821
+ try {
12822
+ var cp = require('child_process');
12823
+ var smiOut = cp.execSync('nvidia-smi --query-gpu=utilization.gpu,memory.used,memory.total,name --format=csv,noheader,nounits 2>/dev/null', { encoding: 'utf8', timeout: 3000 });
12824
+ var smiLine = smiOut.trim().split('\\n')[0];
12825
+ if (smiLine) {
12826
+ var sp = smiLine.split(',').map(function(s) { return s.trim(); });
12827
+ gpuInfo.available = true;
12828
+ gpuInfo.utilization = parseInt(sp[0] || '0', 10) || 0;
12829
+ gpuInfo.vramUsedMB = parseInt(sp[1] || '0', 10) || 0;
12830
+ gpuInfo.vramTotalMB = parseInt(sp[2] || '0', 10) || 0;
12831
+ gpuInfo.name = sp[3] || '';
12832
+ gpuInfo.vramUtilization = gpuInfo.vramTotalMB > 0 ? Math.round((gpuInfo.vramUsedMB / gpuInfo.vramTotalMB) * 100) : 0;
12833
+ }
12834
+ } catch (ge) { /* no GPU */ }
12835
+ var metricsPayload = {
12836
+ cpu: { utilization: Math.min(100, Math.round((loads[0] / cores) * 100)), cores: cores },
12837
+ memory: { utilization: Math.round((usedMem / totalMem) * 100), totalGB: Math.round((totalMem / (1024*1024*1024)) * 10) / 10 },
12838
+ gpu: gpuInfo,
12839
+ timestamp: new Date().toISOString(),
12840
+ };
12841
+ await stream.write({ type: 'invoke.event', version: 1, requestId: request.requestId, seq: 0, event: 'result', data: JSON.stringify(metricsPayload) });
12842
+ await stream.write({ type: 'invoke.done', version: 1, requestId: request.requestId, usage: { inputBytes: 0, outputBytes: JSON.stringify(metricsPayload).length } });
12843
+ } catch (me) {
12844
+ await stream.write({ type: 'invoke.event', version: 1, requestId: request.requestId, seq: 0, event: 'error', data: 'metrics error: ' + me.message });
12845
+ await stream.write({ type: 'invoke.done', version: 1, requestId: request.requestId, usage: { inputBytes: 0, outputBytes: 0 } });
12846
+ }
12847
+ stream.close();
12848
+ });
12849
+ dlog('system_metrics capability registered');
12850
+ }
12851
+
12658
12852
  // Write pricing menu to file
12659
12853
  const pricingFile = join(nexusDir, 'pricing.json');
12660
12854
  writeFileSync(pricingFile, JSON.stringify({ updated: new Date().toISOString(), models: pricingMenu }, null, 2));
@@ -14065,21 +14259,185 @@ process.on('SIGINT', () => process.emit('SIGTERM'));
14065
14259
  });
14066
14260
 
14067
14261
  // packages/execution/dist/shellRunner.js
14262
+ import { spawn as spawn12 } from "node:child_process";
14263
+ async function runShell(options) {
14264
+ const { command, args = [], cwd: cwd4, env, timeoutMs = DEFAULT_TIMEOUT_MS } = options;
14265
+ const mergedEnv = env ? { ...process.env, ...env } : process.env;
14266
+ return new Promise((resolve31) => {
14267
+ const start = Date.now();
14268
+ let timedOut = false;
14269
+ const child = spawn12(command, args, {
14270
+ cwd: cwd4,
14271
+ env: mergedEnv
14272
+ // Do NOT use shell:true — we pass command + args separately
14273
+ });
14274
+ let stdout = "";
14275
+ let stderr = "";
14276
+ child.stdout.on("data", (chunk) => {
14277
+ stdout += chunk.toString();
14278
+ });
14279
+ child.stderr.on("data", (chunk) => {
14280
+ stderr += chunk.toString();
14281
+ });
14282
+ const timer = setTimeout(() => {
14283
+ timedOut = true;
14284
+ child.kill("SIGKILL");
14285
+ }, timeoutMs);
14286
+ child.on("close", (code) => {
14287
+ clearTimeout(timer);
14288
+ const durationMs = Date.now() - start;
14289
+ const exitCode = timedOut ? -1 : code ?? -1;
14290
+ resolve31({
14291
+ stdout,
14292
+ stderr,
14293
+ exitCode,
14294
+ success: !timedOut && exitCode === 0,
14295
+ timedOut,
14296
+ durationMs
14297
+ });
14298
+ });
14299
+ child.on("error", (err) => {
14300
+ clearTimeout(timer);
14301
+ const durationMs = Date.now() - start;
14302
+ resolve31({
14303
+ stdout,
14304
+ stderr: stderr + err.message,
14305
+ exitCode: -1,
14306
+ success: false,
14307
+ timedOut: false,
14308
+ durationMs
14309
+ });
14310
+ });
14311
+ });
14312
+ }
14313
+ var DEFAULT_TIMEOUT_MS;
14068
14314
  var init_shellRunner = __esm({
14069
14315
  "packages/execution/dist/shellRunner.js"() {
14070
14316
  "use strict";
14317
+ DEFAULT_TIMEOUT_MS = 6e4;
14071
14318
  }
14072
14319
  });
14073
14320
 
14074
14321
  // packages/execution/dist/gitWorktree.js
14322
+ async function createWorktree(options) {
14323
+ const { repoPath, worktreePath, branch, timeoutMs = DEFAULT_GIT_TIMEOUT_MS } = options;
14324
+ const result = await runShell({
14325
+ command: "git",
14326
+ args: ["worktree", "add", "-b", branch, worktreePath],
14327
+ cwd: repoPath,
14328
+ timeoutMs
14329
+ });
14330
+ if (!result.success) {
14331
+ throw new Error(`git worktree add failed (exit ${result.exitCode}): ${result.stderr}`);
14332
+ }
14333
+ return { worktreePath, branch };
14334
+ }
14335
+ async function removeWorktree(options) {
14336
+ const { repoPath, worktreePath, timeoutMs = DEFAULT_GIT_TIMEOUT_MS } = options;
14337
+ const result = await runShell({
14338
+ command: "git",
14339
+ args: ["worktree", "remove", "--force", worktreePath],
14340
+ cwd: repoPath,
14341
+ timeoutMs
14342
+ });
14343
+ if (!result.success) {
14344
+ const isAlreadyGone = result.stderr.includes("not a working tree") || result.stderr.includes("is not a working tree") || result.stderr.includes("No such file");
14345
+ if (!isAlreadyGone) {
14346
+ throw new Error(`git worktree remove failed (exit ${result.exitCode}): ${result.stderr}`);
14347
+ }
14348
+ }
14349
+ await runShell({
14350
+ command: "git",
14351
+ args: ["worktree", "prune"],
14352
+ cwd: repoPath,
14353
+ timeoutMs
14354
+ });
14355
+ }
14356
+ var DEFAULT_GIT_TIMEOUT_MS;
14075
14357
  var init_gitWorktree = __esm({
14076
14358
  "packages/execution/dist/gitWorktree.js"() {
14077
14359
  "use strict";
14078
14360
  init_shellRunner();
14361
+ DEFAULT_GIT_TIMEOUT_MS = 3e4;
14079
14362
  }
14080
14363
  });
14081
14364
 
14082
14365
  // packages/execution/dist/patchApplier.js
14366
+ import { readFileSync as readFileSync17, writeFileSync as writeFileSync7, existsSync as existsSync24, mkdirSync as mkdirSync7 } from "node:fs";
14367
+ import { dirname as dirname11 } from "node:path";
14368
+ import { spawn as spawn13 } from "node:child_process";
14369
+ async function applyPatch(patch) {
14370
+ switch (patch.type) {
14371
+ case "block-replace":
14372
+ return applyBlockReplace(patch);
14373
+ case "rewrite":
14374
+ return applyRewrite(patch);
14375
+ case "new-file":
14376
+ return applyNewFile(patch);
14377
+ case "unified-diff":
14378
+ return applyUnifiedDiff(patch);
14379
+ }
14380
+ }
14381
+ function applyBlockReplace(patch) {
14382
+ const original = readFileSync17(patch.filePath, "utf-8");
14383
+ if (!original.includes(patch.oldContent)) {
14384
+ throw new Error(`Block not found in "${patch.filePath}": the oldContent string was not found in the file.`);
14385
+ }
14386
+ const updated = original.replace(patch.oldContent, patch.newContent);
14387
+ writeFileSync7(patch.filePath, updated, "utf-8");
14388
+ }
14389
+ function applyRewrite(patch) {
14390
+ writeFileSync7(patch.filePath, patch.newContent, "utf-8");
14391
+ }
14392
+ function applyNewFile(patch) {
14393
+ if (existsSync24(patch.filePath)) {
14394
+ throw new Error(`Cannot create new file: "${patch.filePath}" already exists.`);
14395
+ }
14396
+ mkdirSync7(dirname11(patch.filePath), { recursive: true });
14397
+ writeFileSync7(patch.filePath, patch.newContent, "utf-8");
14398
+ }
14399
+ async function applyUnifiedDiff(patch) {
14400
+ const { filePath, diff } = patch;
14401
+ const result = await runWithStdin({
14402
+ command: "patch",
14403
+ args: [
14404
+ "--no-backup-if-mismatch",
14405
+ "-p1",
14406
+ filePath
14407
+ ],
14408
+ cwd: dirname11(filePath),
14409
+ stdin: diff
14410
+ });
14411
+ if (!result.success) {
14412
+ throw new Error(`patch command failed (exit ${result.exitCode}): ${result.stderr}`);
14413
+ }
14414
+ }
14415
+ function runWithStdin(options) {
14416
+ const { command, args, cwd: cwd4, stdin } = options;
14417
+ return new Promise((resolve31) => {
14418
+ const child = spawn13(command, args, {
14419
+ cwd: cwd4,
14420
+ stdio: ["pipe", "pipe", "pipe"]
14421
+ });
14422
+ let stdout = "";
14423
+ let stderr = "";
14424
+ child.stdout.on("data", (chunk) => {
14425
+ stdout += chunk.toString();
14426
+ });
14427
+ child.stderr.on("data", (chunk) => {
14428
+ stderr += chunk.toString();
14429
+ });
14430
+ child.stdin.write(stdin);
14431
+ child.stdin.end();
14432
+ child.on("close", (code) => {
14433
+ const exitCode = code ?? -1;
14434
+ resolve31({ success: exitCode === 0, exitCode, stdout, stderr });
14435
+ });
14436
+ child.on("error", (err) => {
14437
+ resolve31({ success: false, exitCode: -1, stdout, stderr: err.message });
14438
+ });
14439
+ });
14440
+ }
14083
14441
  var init_patchApplier = __esm({
14084
14442
  "packages/execution/dist/patchApplier.js"() {
14085
14443
  "use strict";
@@ -14087,6 +14445,55 @@ var init_patchApplier = __esm({
14087
14445
  });
14088
14446
 
14089
14447
  // packages/execution/dist/formatterRunner.js
14448
+ function buildFormatterCommand(formatter, extraArgs) {
14449
+ switch (formatter) {
14450
+ case "prettier":
14451
+ return {
14452
+ command: "npx",
14453
+ args: ["prettier", "--write", ".", ...extraArgs]
14454
+ };
14455
+ case "black":
14456
+ return {
14457
+ command: "black",
14458
+ args: [".", ...extraArgs]
14459
+ };
14460
+ case "rustfmt":
14461
+ return {
14462
+ command: "rustfmt",
14463
+ args: [...extraArgs]
14464
+ };
14465
+ case "gofmt":
14466
+ return {
14467
+ command: "gofmt",
14468
+ args: ["-w", ".", ...extraArgs]
14469
+ };
14470
+ }
14471
+ }
14472
+ async function runFormatter(options) {
14473
+ const { profile, extraArgs = [] } = options;
14474
+ if (profile.formatter === null) {
14475
+ return {
14476
+ success: true,
14477
+ tool: "none",
14478
+ stdout: "",
14479
+ stderr: "",
14480
+ skipped: true
14481
+ };
14482
+ }
14483
+ const { command, args } = buildFormatterCommand(profile.formatter, extraArgs);
14484
+ const shellResult = await runShell({
14485
+ command,
14486
+ args,
14487
+ cwd: profile.projectRoot
14488
+ });
14489
+ return {
14490
+ success: shellResult.success,
14491
+ tool: profile.formatter,
14492
+ stdout: shellResult.stdout,
14493
+ stderr: shellResult.stderr,
14494
+ skipped: false
14495
+ };
14496
+ }
14090
14497
  var init_formatterRunner = __esm({
14091
14498
  "packages/execution/dist/formatterRunner.js"() {
14092
14499
  "use strict";
@@ -14095,6 +14502,60 @@ var init_formatterRunner = __esm({
14095
14502
  });
14096
14503
 
14097
14504
  // packages/execution/dist/linterRunner.js
14505
+ function buildLinterCommand(linter, extraArgs) {
14506
+ switch (linter) {
14507
+ case "eslint":
14508
+ return {
14509
+ command: "npx",
14510
+ args: ["eslint", ".", ...extraArgs]
14511
+ };
14512
+ case "ruff":
14513
+ return {
14514
+ command: "ruff",
14515
+ args: ["check", ".", ...extraArgs]
14516
+ };
14517
+ case "flake8":
14518
+ return {
14519
+ command: "flake8",
14520
+ args: [".", ...extraArgs]
14521
+ };
14522
+ case "clippy":
14523
+ return {
14524
+ command: "cargo",
14525
+ args: ["clippy", ...extraArgs]
14526
+ };
14527
+ case "golangci-lint":
14528
+ return {
14529
+ command: "golangci-lint",
14530
+ args: ["run", ...extraArgs]
14531
+ };
14532
+ }
14533
+ }
14534
+ async function runLinter(options) {
14535
+ const { profile, extraArgs = [] } = options;
14536
+ if (profile.linter === null) {
14537
+ return {
14538
+ success: true,
14539
+ tool: "none",
14540
+ stdout: "",
14541
+ stderr: "",
14542
+ skipped: true
14543
+ };
14544
+ }
14545
+ const { command, args } = buildLinterCommand(profile.linter, extraArgs);
14546
+ const shellResult = await runShell({
14547
+ command,
14548
+ args,
14549
+ cwd: profile.projectRoot
14550
+ });
14551
+ return {
14552
+ success: shellResult.success,
14553
+ tool: profile.linter,
14554
+ stdout: shellResult.stdout,
14555
+ stderr: shellResult.stderr,
14556
+ skipped: false
14557
+ };
14558
+ }
14098
14559
  var init_linterRunner = __esm({
14099
14560
  "packages/execution/dist/linterRunner.js"() {
14100
14561
  "use strict";
@@ -14103,6 +14564,50 @@ var init_linterRunner = __esm({
14103
14564
  });
14104
14565
 
14105
14566
  // packages/execution/dist/typecheckRunner.js
14567
+ function buildTypecheckerCommand(typechecker, extraArgs) {
14568
+ switch (typechecker) {
14569
+ case "tsc":
14570
+ return {
14571
+ command: "npx",
14572
+ args: ["tsc", "--noEmit", ...extraArgs]
14573
+ };
14574
+ case "mypy":
14575
+ return {
14576
+ command: "mypy",
14577
+ args: [".", ...extraArgs]
14578
+ };
14579
+ case "pyright":
14580
+ return {
14581
+ command: "pyright",
14582
+ args: [...extraArgs]
14583
+ };
14584
+ }
14585
+ }
14586
+ async function runTypecheck(options) {
14587
+ const { profile, extraArgs = [] } = options;
14588
+ if (profile.typechecker === null) {
14589
+ return {
14590
+ success: true,
14591
+ tool: "none",
14592
+ stdout: "",
14593
+ stderr: "",
14594
+ skipped: true
14595
+ };
14596
+ }
14597
+ const { command, args } = buildTypecheckerCommand(profile.typechecker, extraArgs);
14598
+ const shellResult = await runShell({
14599
+ command,
14600
+ args,
14601
+ cwd: profile.projectRoot
14602
+ });
14603
+ return {
14604
+ success: shellResult.success,
14605
+ tool: profile.typechecker,
14606
+ stdout: shellResult.stdout,
14607
+ stderr: shellResult.stderr,
14608
+ skipped: false
14609
+ };
14610
+ }
14106
14611
  var init_typecheckRunner = __esm({
14107
14612
  "packages/execution/dist/typecheckRunner.js"() {
14108
14613
  "use strict";
@@ -14111,6 +14616,60 @@ var init_typecheckRunner = __esm({
14111
14616
  });
14112
14617
 
14113
14618
  // packages/execution/dist/testRunner.js
14619
+ function buildTestRunnerCommand(testRunner, extraArgs) {
14620
+ switch (testRunner) {
14621
+ case "jest":
14622
+ return {
14623
+ command: "npx",
14624
+ args: ["jest", ...extraArgs]
14625
+ };
14626
+ case "vitest":
14627
+ return {
14628
+ command: "npx",
14629
+ args: ["vitest", "run", ...extraArgs]
14630
+ };
14631
+ case "pytest":
14632
+ return {
14633
+ command: "pytest",
14634
+ args: [...extraArgs]
14635
+ };
14636
+ case "mocha":
14637
+ return {
14638
+ command: "npx",
14639
+ args: ["mocha", ...extraArgs]
14640
+ };
14641
+ case "go-test":
14642
+ return {
14643
+ command: "go",
14644
+ args: ["test", "./...", ...extraArgs]
14645
+ };
14646
+ }
14647
+ }
14648
+ async function runTests(options) {
14649
+ const { profile, extraArgs = [] } = options;
14650
+ if (profile.testRunner === null) {
14651
+ return {
14652
+ success: true,
14653
+ tool: "none",
14654
+ stdout: "",
14655
+ stderr: "",
14656
+ skipped: true
14657
+ };
14658
+ }
14659
+ const { command, args } = buildTestRunnerCommand(profile.testRunner, extraArgs);
14660
+ const shellResult = await runShell({
14661
+ command,
14662
+ args,
14663
+ cwd: profile.projectRoot
14664
+ });
14665
+ return {
14666
+ success: shellResult.success,
14667
+ tool: profile.testRunner,
14668
+ stdout: shellResult.stdout,
14669
+ stderr: shellResult.stderr,
14670
+ skipped: false
14671
+ };
14672
+ }
14114
14673
  var init_testRunner = __esm({
14115
14674
  "packages/execution/dist/testRunner.js"() {
14116
14675
  "use strict";
@@ -14119,6 +14678,71 @@ var init_testRunner = __esm({
14119
14678
  });
14120
14679
 
14121
14680
  // packages/execution/dist/buildRunner.js
14681
+ function buildBuilderCommand(profile, extraArgs) {
14682
+ switch (profile.builder) {
14683
+ case "tsc":
14684
+ return {
14685
+ command: "npx",
14686
+ args: ["tsc", "--build", ...extraArgs]
14687
+ };
14688
+ case "vite":
14689
+ return {
14690
+ command: "npx",
14691
+ args: ["vite", "build", ...extraArgs]
14692
+ };
14693
+ case "webpack":
14694
+ return {
14695
+ command: "npx",
14696
+ args: ["webpack", ...extraArgs]
14697
+ };
14698
+ case "cargo":
14699
+ return {
14700
+ command: "cargo",
14701
+ args: ["build", "--release", ...extraArgs]
14702
+ };
14703
+ case "go-build":
14704
+ return {
14705
+ command: "go",
14706
+ args: ["build", "./...", ...extraArgs]
14707
+ };
14708
+ case "custom": {
14709
+ if (!profile.buildCommand) {
14710
+ throw new Error('buildCommand is required when builder is "custom"');
14711
+ }
14712
+ return {
14713
+ command: "sh",
14714
+ args: ["-c", profile.buildCommand]
14715
+ };
14716
+ }
14717
+ default:
14718
+ throw new Error(`Unsupported builder: ${String(profile.builder)}`);
14719
+ }
14720
+ }
14721
+ async function runBuild(options) {
14722
+ const { profile, extraArgs = [] } = options;
14723
+ if (profile.builder === null) {
14724
+ return {
14725
+ success: true,
14726
+ tool: "none",
14727
+ stdout: "",
14728
+ stderr: "",
14729
+ skipped: true
14730
+ };
14731
+ }
14732
+ const { command, args } = buildBuilderCommand(profile, extraArgs);
14733
+ const shellResult = await runShell({
14734
+ command,
14735
+ args,
14736
+ cwd: profile.projectRoot
14737
+ });
14738
+ return {
14739
+ success: shellResult.success,
14740
+ tool: profile.builder,
14741
+ stdout: shellResult.stdout,
14742
+ stderr: shellResult.stderr,
14743
+ skipped: false
14744
+ };
14745
+ }
14122
14746
  var init_buildRunner = __esm({
14123
14747
  "packages/execution/dist/buildRunner.js"() {
14124
14748
  "use strict";
@@ -14127,6 +14751,100 @@ var init_buildRunner = __esm({
14127
14751
  });
14128
14752
 
14129
14753
  // packages/execution/dist/validationPipeline.js
14754
+ async function runValidationPipeline(config) {
14755
+ const { projectRoot, profile, stopOnFirstFailure = true } = config;
14756
+ const formatterProfile = {
14757
+ formatter: profile.formatter,
14758
+ projectRoot
14759
+ };
14760
+ const linterProfile = {
14761
+ linter: profile.linter,
14762
+ projectRoot
14763
+ };
14764
+ const typecheckProfile = {
14765
+ typechecker: profile.typechecker,
14766
+ projectRoot
14767
+ };
14768
+ const testProfile = {
14769
+ testRunner: profile.testRunner,
14770
+ projectRoot
14771
+ };
14772
+ const buildProfile = {
14773
+ builder: profile.builder,
14774
+ projectRoot,
14775
+ buildCommand: profile.buildCommand
14776
+ };
14777
+ const steps = [];
14778
+ const start = Date.now();
14779
+ const formatRaw = await runFormatter({ profile: formatterProfile });
14780
+ const formatStep = {
14781
+ name: "format",
14782
+ success: formatRaw.success,
14783
+ tool: formatRaw.tool,
14784
+ stdout: formatRaw.stdout,
14785
+ stderr: formatRaw.stderr,
14786
+ skipped: formatRaw.skipped
14787
+ };
14788
+ steps.push(formatStep);
14789
+ if (!formatStep.success && stopOnFirstFailure) {
14790
+ return { overall: "fail", steps, totalDurationMs: Date.now() - start };
14791
+ }
14792
+ const lintRaw = await runLinter({ profile: linterProfile });
14793
+ const lintStep = {
14794
+ name: "lint",
14795
+ success: lintRaw.success,
14796
+ tool: lintRaw.tool,
14797
+ stdout: lintRaw.stdout,
14798
+ stderr: lintRaw.stderr,
14799
+ skipped: lintRaw.skipped
14800
+ };
14801
+ steps.push(lintStep);
14802
+ if (!lintStep.success && stopOnFirstFailure) {
14803
+ return { overall: "fail", steps, totalDurationMs: Date.now() - start };
14804
+ }
14805
+ const typecheckRaw = await runTypecheck({ profile: typecheckProfile });
14806
+ const typecheckStep = {
14807
+ name: "typecheck",
14808
+ success: typecheckRaw.success,
14809
+ tool: typecheckRaw.tool,
14810
+ stdout: typecheckRaw.stdout,
14811
+ stderr: typecheckRaw.stderr,
14812
+ skipped: typecheckRaw.skipped
14813
+ };
14814
+ steps.push(typecheckStep);
14815
+ if (!typecheckStep.success && stopOnFirstFailure) {
14816
+ return { overall: "fail", steps, totalDurationMs: Date.now() - start };
14817
+ }
14818
+ const testRaw = await runTests({ profile: testProfile });
14819
+ const testStep = {
14820
+ name: "test",
14821
+ success: testRaw.success,
14822
+ tool: testRaw.tool,
14823
+ stdout: testRaw.stdout,
14824
+ stderr: testRaw.stderr,
14825
+ skipped: testRaw.skipped
14826
+ };
14827
+ steps.push(testStep);
14828
+ if (!testStep.success && stopOnFirstFailure) {
14829
+ return { overall: "fail", steps, totalDurationMs: Date.now() - start };
14830
+ }
14831
+ const buildRaw = await runBuild({ profile: buildProfile });
14832
+ const buildStep = {
14833
+ name: "build",
14834
+ success: buildRaw.success,
14835
+ tool: buildRaw.tool,
14836
+ stdout: buildRaw.stdout,
14837
+ stderr: buildRaw.stderr,
14838
+ skipped: buildRaw.skipped
14839
+ };
14840
+ steps.push(buildStep);
14841
+ const anyFailed = steps.some((s) => !s.success);
14842
+ return {
14843
+ overall: anyFailed ? "fail" : "pass",
14844
+ steps,
14845
+ totalDurationMs: Date.now() - start
14846
+ };
14847
+ }
14130
14848
  var init_validationPipeline = __esm({
14131
14849
  "packages/execution/dist/validationPipeline.js"() {
14132
14850
  "use strict";
@@ -14139,6 +14857,97 @@ var init_validationPipeline = __esm({
14139
14857
  });
14140
14858
 
14141
14859
  // packages/execution/dist/index.js
14860
+ var dist_exports = {};
14861
+ __export(dist_exports, {
14862
+ AgendaTool: () => AgendaTool,
14863
+ AiwgHealthTool: () => AiwgHealthTool,
14864
+ AiwgSetupTool: () => AiwgSetupTool,
14865
+ AiwgWorkflowTool: () => AiwgWorkflowTool,
14866
+ AutoresearchTool: () => AutoresearchTool,
14867
+ BackgroundRunTool: () => BackgroundRunTool,
14868
+ BackgroundTaskManager: () => BackgroundTaskManager,
14869
+ BatchEditTool: () => BatchEditTool,
14870
+ BrowserActionTool: () => BrowserActionTool,
14871
+ CodeSandboxTool: () => CodeSandboxTool,
14872
+ CodebaseMapTool: () => CodebaseMapTool,
14873
+ CreateToolTool: () => CreateToolTool,
14874
+ CronAgentTool: () => CronAgentTool,
14875
+ CustomTool: () => CustomTool,
14876
+ DESKTOP_DEPS: () => DESKTOP_DEPS,
14877
+ DesktopClickTool: () => DesktopClickTool,
14878
+ DesktopDescribeTool: () => DesktopDescribeTool,
14879
+ DiagnosticTool: () => DiagnosticTool,
14880
+ ExploreToolsTool: () => ExploreToolsTool,
14881
+ FactoryTool: () => FactoryTool,
14882
+ FileEditTool: () => FileEditTool,
14883
+ FilePatchTool: () => FilePatchTool,
14884
+ FileReadTool: () => FileReadTool,
14885
+ FileWriteTool: () => FileWriteTool,
14886
+ GitInfoTool: () => GitInfoTool,
14887
+ GlobFindTool: () => GlobFindTool,
14888
+ GrepSearchTool: () => GrepSearchTool,
14889
+ ImageReadTool: () => ImageReadTool,
14890
+ ListDirectoryTool: () => ListDirectoryTool,
14891
+ ManageToolsTool: () => ManageToolsTool,
14892
+ MemoryReadTool: () => MemoryReadTool,
14893
+ MemorySearchTool: () => MemorySearchTool,
14894
+ MemoryWriteTool: () => MemoryWriteTool,
14895
+ NexusTool: () => NexusTool,
14896
+ OCRTool: () => OCRTool,
14897
+ OcrImageAdvancedTool: () => OcrImageAdvancedTool,
14898
+ OcrPdfTool: () => OcrPdfTool,
14899
+ OpenCodeTool: () => OpenCodeTool,
14900
+ PdfToTextTool: () => PdfToTextTool,
14901
+ ReminderTool: () => ReminderTool,
14902
+ SchedulerTool: () => SchedulerTool,
14903
+ ScreenshotTool: () => ScreenshotTool,
14904
+ ShellTool: () => ShellTool,
14905
+ SkillBuildTool: () => SkillBuildTool,
14906
+ SkillExecuteTool: () => SkillExecuteTool,
14907
+ SkillListTool: () => SkillListTool,
14908
+ StructuredFileTool: () => StructuredFileTool,
14909
+ StructuredReadTool: () => StructuredReadTool,
14910
+ TaskOutputTool: () => TaskOutputTool,
14911
+ TaskStatusTool: () => TaskStatusTool,
14912
+ TaskStopTool: () => TaskStopTool,
14913
+ ToolExecutor: () => ToolExecutor,
14914
+ TranscribeFileTool: () => TranscribeFileTool,
14915
+ TranscribeUrlTool: () => TranscribeUrlTool,
14916
+ VisionTool: () => VisionTool,
14917
+ WebCrawlTool: () => WebCrawlTool,
14918
+ WebFetchTool: () => WebFetchTool,
14919
+ WebSearchTool: () => WebSearchTool,
14920
+ applyPatch: () => applyPatch,
14921
+ buildCustomTools: () => buildCustomTools,
14922
+ buildSkillsSummary: () => buildSkillsSummary,
14923
+ checkDesktopDeps: () => checkDesktopDeps,
14924
+ createWorktree: () => createWorktree,
14925
+ detectSearchProvider: () => detectSearchProvider,
14926
+ discoverSkills: () => discoverSkills,
14927
+ ensureAllDesktopDeps: () => ensureAllDesktopDeps,
14928
+ ensureCommand: () => ensureCommand,
14929
+ ensureDepsForGroup: () => ensureDepsForGroup,
14930
+ getActiveAttentionItems: () => getActiveAttentionItems,
14931
+ getDueReminders: () => getDueReminders,
14932
+ isImagePath: () => isImagePath,
14933
+ listCustomToolFiles: () => listCustomToolFiles,
14934
+ loadCustomTools: () => loadCustomTools,
14935
+ loadReminderStore: () => loadReminderStore,
14936
+ loadSkillContent: () => loadSkillContent,
14937
+ markRemindersSeen: () => markRemindersSeen,
14938
+ removeWorktree: () => removeWorktree,
14939
+ resetDepCache: () => resetDepCache,
14940
+ resetMoondreamClient: () => resetMoondreamClient,
14941
+ runBuild: () => runBuild,
14942
+ runFormatter: () => runFormatter,
14943
+ runLinter: () => runLinter,
14944
+ runShell: () => runShell,
14945
+ runTests: () => runTests,
14946
+ runTypecheck: () => runTypecheck,
14947
+ runValidationPipeline: () => runValidationPipeline,
14948
+ saveCustomToolDefinition: () => saveCustomToolDefinition,
14949
+ setSudoPassword: () => setSudoPassword
14950
+ });
14142
14951
  var init_dist2 = __esm({
14143
14952
  "packages/execution/dist/index.js"() {
14144
14953
  "use strict";
@@ -14873,17 +15682,17 @@ var init_dist3 = __esm({
14873
15682
  });
14874
15683
 
14875
15684
  // packages/orchestrator/dist/promptLoader.js
14876
- import { readFileSync as readFileSync17, existsSync as existsSync24 } from "node:fs";
14877
- import { join as join31, dirname as dirname11 } from "node:path";
15685
+ import { readFileSync as readFileSync18, existsSync as existsSync25 } from "node:fs";
15686
+ import { join as join31, dirname as dirname12 } from "node:path";
14878
15687
  import { fileURLToPath as fileURLToPath7 } from "node:url";
14879
15688
  function loadPrompt(promptPath, vars) {
14880
15689
  let content = cache.get(promptPath);
14881
15690
  if (content === void 0) {
14882
15691
  const fullPath = join31(PROMPTS_DIR, promptPath);
14883
- if (!existsSync24(fullPath)) {
15692
+ if (!existsSync25(fullPath)) {
14884
15693
  throw new Error(`Prompt file not found: ${fullPath}`);
14885
15694
  }
14886
- content = readFileSync17(fullPath, "utf-8");
15695
+ content = readFileSync18(fullPath, "utf-8");
14887
15696
  cache.set(promptPath, content);
14888
15697
  }
14889
15698
  if (!vars)
@@ -14895,7 +15704,7 @@ var init_promptLoader = __esm({
14895
15704
  "packages/orchestrator/dist/promptLoader.js"() {
14896
15705
  "use strict";
14897
15706
  __filename = fileURLToPath7(import.meta.url);
14898
- __dirname4 = dirname11(__filename);
15707
+ __dirname4 = dirname12(__filename);
14899
15708
  PROMPTS_DIR = join31(__dirname4, "..", "prompts");
14900
15709
  cache = /* @__PURE__ */ new Map();
14901
15710
  }
@@ -15232,12 +16041,12 @@ var init_grep_search2 = __esm({
15232
16041
  });
15233
16042
 
15234
16043
  // packages/retrieval/dist/code-retriever.js
15235
- var DEFAULT_CONFIG2, CodeRetriever;
16044
+ var DEFAULT_CONFIG3, CodeRetriever;
15236
16045
  var init_code_retriever = __esm({
15237
16046
  "packages/retrieval/dist/code-retriever.js"() {
15238
16047
  "use strict";
15239
16048
  init_grep_search2();
15240
- DEFAULT_CONFIG2 = {
16049
+ DEFAULT_CONFIG3 = {
15241
16050
  rootDir: ".",
15242
16051
  maxResults: 20,
15243
16052
  maxFileSize: 5e5,
@@ -15248,7 +16057,7 @@ var init_code_retriever = __esm({
15248
16057
  config;
15249
16058
  grepSearch;
15250
16059
  constructor(config) {
15251
- this.config = { ...DEFAULT_CONFIG2, ...config };
16060
+ this.config = { ...DEFAULT_CONFIG3, ...config };
15252
16061
  this.grepSearch = new GrepSearch(this.config.rootDir);
15253
16062
  }
15254
16063
  async searchByPattern(pattern) {
@@ -15959,8 +16768,8 @@ var init_contextAssembler = __esm({
15959
16768
  });
15960
16769
 
15961
16770
  // packages/retrieval/dist/index.js
15962
- var dist_exports = {};
15963
- __export(dist_exports, {
16771
+ var dist_exports2 = {};
16772
+ __export(dist_exports2, {
15964
16773
  CodeRetriever: () => CodeRetriever,
15965
16774
  GrepSearch: () => GrepSearch,
15966
16775
  IndexBackedSemanticSearchEngine: () => IndexBackedSemanticSearchEngine,
@@ -15997,7 +16806,7 @@ var init_dist4 = __esm({
15997
16806
 
15998
16807
  // packages/orchestrator/dist/scoutRunner.js
15999
16808
  async function loadCodeRetriever(rootDir) {
16000
- const mod = await Promise.resolve().then(() => (init_dist4(), dist_exports));
16809
+ const mod = await Promise.resolve().then(() => (init_dist4(), dist_exports2));
16001
16810
  return new mod.CodeRetriever({ rootDir });
16002
16811
  }
16003
16812
  function parseJsonFromContent4(content) {
@@ -18258,10 +19067,10 @@ ${marker}` : marker);
18258
19067
  if (!this._workingDirectory)
18259
19068
  return;
18260
19069
  try {
18261
- const { mkdirSync: mkdirSync20, writeFileSync: writeFileSync18 } = __require("node:fs");
19070
+ const { mkdirSync: mkdirSync21, writeFileSync: writeFileSync19 } = __require("node:fs");
18262
19071
  const { join: join55 } = __require("node:path");
18263
19072
  const sessionDir = join55(this._workingDirectory, ".oa", "session", this._sessionId);
18264
- mkdirSync20(sessionDir, { recursive: true });
19073
+ mkdirSync21(sessionDir, { recursive: true });
18265
19074
  const checkpoint = {
18266
19075
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
18267
19076
  sessionId: this._sessionId,
@@ -18273,7 +19082,7 @@ ${marker}` : marker);
18273
19082
  memexEntryCount: this._memexArchive.size,
18274
19083
  fileRegistrySize: this._fileRegistry.size
18275
19084
  };
18276
- writeFileSync18(join55(sessionDir, "checkpoint.json"), JSON.stringify(checkpoint, null, 2));
19085
+ writeFileSync19(join55(sessionDir, "checkpoint.json"), JSON.stringify(checkpoint, null, 2));
18277
19086
  } catch {
18278
19087
  }
18279
19088
  }
@@ -19515,10 +20324,12 @@ var init_nexusBackend = __esm({
19515
20324
  sendFn;
19516
20325
  model;
19517
20326
  targetPeer;
19518
- constructor(sendFn, model, targetPeer) {
20327
+ authKey;
20328
+ constructor(sendFn, model, targetPeer, authKey) {
19519
20329
  this.sendFn = sendFn;
19520
20330
  this.model = model;
19521
20331
  this.targetPeer = targetPeer || "";
20332
+ this.authKey = authKey || "";
19522
20333
  }
19523
20334
  async chatCompletion(request) {
19524
20335
  const daemonArgs = {
@@ -19531,6 +20342,9 @@ var init_nexusBackend = __esm({
19531
20342
  if (this.targetPeer) {
19532
20343
  daemonArgs.target_peer = this.targetPeer;
19533
20344
  }
20345
+ if (this.authKey) {
20346
+ daemonArgs.auth_key = this.authKey;
20347
+ }
19534
20348
  const rawResult = await this.sendFn("remote_infer", daemonArgs, request.timeoutMs || 12e4);
19535
20349
  let parsed;
19536
20350
  try {
@@ -19664,6 +20478,273 @@ var init_nexusBackend = __esm({
19664
20478
  }
19665
20479
  });
19666
20480
 
20481
+ // packages/orchestrator/dist/cascadeBackend.js
20482
+ var CascadeBackend;
20483
+ var init_cascadeBackend = __esm({
20484
+ "packages/orchestrator/dist/cascadeBackend.js"() {
20485
+ "use strict";
20486
+ init_agenticRunner();
20487
+ init_nexusBackend();
20488
+ CascadeBackend = class {
20489
+ model;
20490
+ endpoints;
20491
+ backends = /* @__PURE__ */ new Map();
20492
+ activeIndex = 0;
20493
+ consecutiveFailures = 0;
20494
+ maxFailures;
20495
+ probeTimer = null;
20496
+ onSwitch;
20497
+ onProbeResult;
20498
+ stopped = false;
20499
+ constructor(model, endpoints, options) {
20500
+ if (endpoints.length === 0)
20501
+ throw new Error("CascadeBackend requires at least one endpoint");
20502
+ this.model = model;
20503
+ this.endpoints = [...endpoints];
20504
+ this.maxFailures = options?.maxFailuresBeforeSwitch ?? 2;
20505
+ this.onSwitch = options?.onSwitch;
20506
+ this.onProbeResult = options?.onProbeResult;
20507
+ for (const ep of this.endpoints) {
20508
+ this.backends.set(ep.url, this.createBackend(ep));
20509
+ }
20510
+ if (this.endpoints.length > 1) {
20511
+ const probeInterval = options?.probeIntervalMs ?? 3e4;
20512
+ this.probeTimer = setInterval(() => this.probePrimary(), probeInterval);
20513
+ }
20514
+ }
20515
+ /** Get the currently active endpoint */
20516
+ get activeEndpoint() {
20517
+ return this.endpoints[this.activeIndex];
20518
+ }
20519
+ /** Get all endpoints with their status */
20520
+ get endpointStatus() {
20521
+ return this.endpoints.map((ep, i) => ({
20522
+ ...ep,
20523
+ active: i === this.activeIndex,
20524
+ failures: i === this.activeIndex ? this.consecutiveFailures : 0
20525
+ }));
20526
+ }
20527
+ /** Number of available fallback endpoints */
20528
+ get fallbackCount() {
20529
+ return this.endpoints.filter((ep, i) => i !== this.activeIndex && ep.modelAvailable !== false).length;
20530
+ }
20531
+ async chatCompletion(request) {
20532
+ const backend = this.getActiveBackend();
20533
+ try {
20534
+ const result = await backend.chatCompletion(request);
20535
+ this.consecutiveFailures = 0;
20536
+ return result;
20537
+ } catch (err) {
20538
+ this.consecutiveFailures++;
20539
+ if (this.isTransientError(err) && this.consecutiveFailures >= this.maxFailures) {
20540
+ const nextIdx = this.findNextAvailableEndpoint();
20541
+ if (nextIdx !== null && nextIdx !== this.activeIndex) {
20542
+ const from = this.endpoints[this.activeIndex];
20543
+ const to = this.endpoints[nextIdx];
20544
+ const reason = `${this.consecutiveFailures} consecutive failures: ${err instanceof Error ? err.message : String(err)}`;
20545
+ this.activeIndex = nextIdx;
20546
+ this.consecutiveFailures = 0;
20547
+ this.onSwitch?.(from, to, reason);
20548
+ const newBackend = this.getActiveBackend();
20549
+ try {
20550
+ const result = await newBackend.chatCompletion(request);
20551
+ return result;
20552
+ } catch (cascadeErr) {
20553
+ this.consecutiveFailures++;
20554
+ throw cascadeErr;
20555
+ }
20556
+ }
20557
+ }
20558
+ throw err;
20559
+ }
20560
+ }
20561
+ /**
20562
+ * Streaming support — delegates to active backend's stream method if available.
20563
+ */
20564
+ async *chatCompletionStream(request) {
20565
+ const backend = this.getActiveBackend();
20566
+ const streamable = backend;
20567
+ if (typeof streamable.chatCompletionStream !== "function") {
20568
+ const result = await this.chatCompletion(request);
20569
+ const choice = result.choices[0];
20570
+ if (choice?.message.content) {
20571
+ yield { type: "content", content: choice.message.content };
20572
+ }
20573
+ if (choice?.message.toolCalls) {
20574
+ for (let i = 0; i < choice.message.toolCalls.length; i++) {
20575
+ const tc = choice.message.toolCalls[i];
20576
+ yield {
20577
+ type: "tool_call_delta",
20578
+ toolCallIndex: i,
20579
+ toolCallId: tc.id,
20580
+ toolCallName: tc.name,
20581
+ toolCallArgs: JSON.stringify(tc.arguments)
20582
+ };
20583
+ }
20584
+ }
20585
+ if (result.usage) {
20586
+ yield {
20587
+ type: "usage",
20588
+ usage: {
20589
+ promptTokens: result.usage.promptTokens ?? 0,
20590
+ completionTokens: result.usage.completionTokens ?? 0,
20591
+ totalTokens: result.usage.totalTokens
20592
+ }
20593
+ };
20594
+ }
20595
+ yield { type: "finish", finishReason: "stop" };
20596
+ return;
20597
+ }
20598
+ try {
20599
+ for await (const chunk of streamable.chatCompletionStream(request)) {
20600
+ yield chunk;
20601
+ }
20602
+ this.consecutiveFailures = 0;
20603
+ } catch (err) {
20604
+ this.consecutiveFailures++;
20605
+ if (this.isTransientError(err) && this.consecutiveFailures >= this.maxFailures) {
20606
+ const nextIdx = this.findNextAvailableEndpoint();
20607
+ if (nextIdx !== null && nextIdx !== this.activeIndex) {
20608
+ const from = this.endpoints[this.activeIndex];
20609
+ const to = this.endpoints[nextIdx];
20610
+ this.activeIndex = nextIdx;
20611
+ this.consecutiveFailures = 0;
20612
+ this.onSwitch?.(from, to, err instanceof Error ? err.message : String(err));
20613
+ const result = await this.chatCompletion(request);
20614
+ const choice = result.choices[0];
20615
+ if (choice?.message.content) {
20616
+ yield { type: "content", content: choice.message.content };
20617
+ }
20618
+ if (choice?.message.toolCalls) {
20619
+ for (let i = 0; i < choice.message.toolCalls.length; i++) {
20620
+ const tc = choice.message.toolCalls[i];
20621
+ yield {
20622
+ type: "tool_call_delta",
20623
+ toolCallIndex: i,
20624
+ toolCallId: tc.id,
20625
+ toolCallName: tc.name,
20626
+ toolCallArgs: JSON.stringify(tc.arguments)
20627
+ };
20628
+ }
20629
+ }
20630
+ yield { type: "finish", finishReason: "stop" };
20631
+ return;
20632
+ }
20633
+ }
20634
+ throw err;
20635
+ }
20636
+ }
20637
+ /** Add a fallback endpoint at the end of the cascade */
20638
+ addEndpoint(endpoint) {
20639
+ this.endpoints.push(endpoint);
20640
+ this.backends.set(endpoint.url, this.createBackend(endpoint));
20641
+ if (this.endpoints.length === 2 && !this.probeTimer) {
20642
+ this.probeTimer = setInterval(() => this.probePrimary(), 3e4);
20643
+ }
20644
+ }
20645
+ /** Remove an endpoint from the cascade */
20646
+ removeEndpoint(url) {
20647
+ const idx = this.endpoints.findIndex((ep) => ep.url === url);
20648
+ if (idx === -1)
20649
+ return;
20650
+ if (idx === this.activeIndex)
20651
+ return;
20652
+ this.endpoints.splice(idx, 1);
20653
+ this.backends.delete(url);
20654
+ if (this.activeIndex > idx) {
20655
+ this.activeIndex--;
20656
+ }
20657
+ }
20658
+ /** Stop the cascade (clears probe timer) */
20659
+ stop() {
20660
+ this.stopped = true;
20661
+ if (this.probeTimer) {
20662
+ clearInterval(this.probeTimer);
20663
+ this.probeTimer = null;
20664
+ }
20665
+ }
20666
+ /** Update the model (e.g. when user runs /model) */
20667
+ setModel(model) {
20668
+ this.model = model;
20669
+ for (const ep of this.endpoints) {
20670
+ this.backends.set(ep.url, this.createBackend(ep));
20671
+ }
20672
+ }
20673
+ // ── Private helpers ────────────────────────────────────────────────────
20674
+ getActiveBackend() {
20675
+ const ep = this.endpoints[this.activeIndex];
20676
+ return this.backends.get(ep.url);
20677
+ }
20678
+ createBackend(ep) {
20679
+ if (ep.backendType === "nexus" && ep.sendCommandFn) {
20680
+ const targetPeer = ep.url.startsWith("peer://") ? ep.url.slice(7) : void 0;
20681
+ return new NexusAgenticBackend(ep.sendCommandFn, this.model, targetPeer, ep.apiKey);
20682
+ }
20683
+ return new OllamaAgenticBackend(ep.url, this.model, ep.apiKey);
20684
+ }
20685
+ findNextAvailableEndpoint() {
20686
+ for (let offset = 1; offset < this.endpoints.length; offset++) {
20687
+ const idx = (this.activeIndex + offset) % this.endpoints.length;
20688
+ const ep = this.endpoints[idx];
20689
+ if (ep.modelAvailable !== false) {
20690
+ return idx;
20691
+ }
20692
+ }
20693
+ return null;
20694
+ }
20695
+ isTransientError(err) {
20696
+ const msg = err instanceof Error ? err.message : String(err);
20697
+ if (/Backend HTTP (502|503|504)/i.test(msg))
20698
+ return true;
20699
+ if (/fetch failed|ECONNREFUSED|ECONNRESET|ETIMEDOUT|EPIPE|socket hang up/i.test(msg))
20700
+ return true;
20701
+ if (/received HTML error page/i.test(msg))
20702
+ return true;
20703
+ if (/model is loading|server busy|overloaded/i.test(msg))
20704
+ return true;
20705
+ if (/remote inference failed|timed out/i.test(msg))
20706
+ return true;
20707
+ return false;
20708
+ }
20709
+ /**
20710
+ * Probe the primary endpoint to check if it's recovered.
20711
+ * If the current active endpoint is NOT the primary, tests the primary
20712
+ * with a lightweight request and switches back if successful.
20713
+ */
20714
+ async probePrimary() {
20715
+ if (this.stopped)
20716
+ return;
20717
+ if (this.activeIndex === 0)
20718
+ return;
20719
+ if (this.endpoints.length === 0)
20720
+ return;
20721
+ const primary = this.endpoints[0];
20722
+ const primaryBackend = this.backends.get(primary.url);
20723
+ if (!primaryBackend)
20724
+ return;
20725
+ try {
20726
+ const result = await primaryBackend.chatCompletion({
20727
+ messages: [{ role: "user", content: "ping" }],
20728
+ tools: [],
20729
+ temperature: 0,
20730
+ maxTokens: 1,
20731
+ timeoutMs: 1e4
20732
+ });
20733
+ if (result.choices.length > 0) {
20734
+ this.onProbeResult?.(primary, true);
20735
+ const from = this.endpoints[this.activeIndex];
20736
+ this.activeIndex = 0;
20737
+ this.consecutiveFailures = 0;
20738
+ this.onSwitch?.(from, primary, "Primary endpoint recovered");
20739
+ }
20740
+ } catch {
20741
+ this.onProbeResult?.(primary, false);
20742
+ }
20743
+ }
20744
+ };
20745
+ }
20746
+ });
20747
+
19667
20748
  // packages/orchestrator/dist/flowstatePrompt.js
19668
20749
  var FLOWSTATE_PROMPT;
19669
20750
  var init_flowstatePrompt = __esm({
@@ -20198,6 +21279,7 @@ var init_dist5 = __esm({
20198
21279
  init_retryController();
20199
21280
  init_agenticRunner();
20200
21281
  init_nexusBackend();
21282
+ init_cascadeBackend();
20201
21283
  init_flowstatePrompt();
20202
21284
  init_costTracker();
20203
21285
  init_workEvaluator();
@@ -20218,9 +21300,9 @@ __export(listen_exports, {
20218
21300
  isVideoPath: () => isVideoPath,
20219
21301
  waitForTranscribeCli: () => waitForTranscribeCli
20220
21302
  });
20221
- import { spawn as spawn12, execSync as execSync23 } from "node:child_process";
20222
- import { existsSync as existsSync25, mkdirSync as mkdirSync7, writeFileSync as writeFileSync7, readdirSync as readdirSync6 } from "node:fs";
20223
- import { join as join34, dirname as dirname12 } from "node:path";
21303
+ import { spawn as spawn14, execSync as execSync23 } from "node:child_process";
21304
+ import { existsSync as existsSync26, mkdirSync as mkdirSync8, writeFileSync as writeFileSync8, readdirSync as readdirSync6 } from "node:fs";
21305
+ import { join as join34, dirname as dirname13 } from "node:path";
20224
21306
  import { homedir as homedir8 } from "node:os";
20225
21307
  import { fileURLToPath as fileURLToPath8 } from "node:url";
20226
21308
  import { EventEmitter } from "node:events";
@@ -20304,7 +21386,7 @@ function findMicCaptureCommand() {
20304
21386
  return null;
20305
21387
  }
20306
21388
  function findLiveWhisperScript() {
20307
- const thisDir = dirname12(fileURLToPath8(import.meta.url));
21389
+ const thisDir = dirname13(fileURLToPath8(import.meta.url));
20308
21390
  const candidates = [
20309
21391
  join34(thisDir, "../../../../packages/execution/scripts/live-whisper.py"),
20310
21392
  join34(thisDir, "../../../packages/execution/scripts/live-whisper.py"),
@@ -20314,7 +21396,7 @@ function findLiveWhisperScript() {
20314
21396
  join34(thisDir, "../../scripts/live-whisper.py")
20315
21397
  ];
20316
21398
  for (const p of candidates) {
20317
- if (existsSync25(p))
21399
+ if (existsSync26(p))
20318
21400
  return p;
20319
21401
  }
20320
21402
  try {
@@ -20328,17 +21410,17 @@ function findLiveWhisperScript() {
20328
21410
  join34(globalRoot, "open-agents-ai", "scripts", "live-whisper.py")
20329
21411
  ];
20330
21412
  for (const p of candidates2) {
20331
- if (existsSync25(p))
21413
+ if (existsSync26(p))
20332
21414
  return p;
20333
21415
  }
20334
21416
  } catch {
20335
21417
  }
20336
21418
  const nvmBase = join34(homedir8(), ".nvm", "versions", "node");
20337
- if (existsSync25(nvmBase)) {
21419
+ if (existsSync26(nvmBase)) {
20338
21420
  try {
20339
21421
  for (const ver of readdirSync6(nvmBase)) {
20340
21422
  const p = join34(nvmBase, ver, "lib", "node_modules", "open-agents-ai", "dist", "scripts", "live-whisper.py");
20341
- if (existsSync25(p))
21423
+ if (existsSync26(p))
20342
21424
  return p;
20343
21425
  }
20344
21426
  } catch {
@@ -20356,7 +21438,7 @@ function ensureTranscribeCliBackground() {
20356
21438
  timeout: 5e3,
20357
21439
  stdio: ["pipe", "pipe", "pipe"]
20358
21440
  }).trim();
20359
- if (existsSync25(join34(globalRoot, "transcribe-cli", "dist", "index.js"))) {
21441
+ if (existsSync26(join34(globalRoot, "transcribe-cli", "dist", "index.js"))) {
20360
21442
  return true;
20361
21443
  }
20362
21444
  } catch {
@@ -20428,7 +21510,7 @@ var init_listen = __esm({
20428
21510
  const timeout = setTimeout(() => {
20429
21511
  reject(new Error("Whisper fallback: model load timeout (5 min). First run downloads the model."));
20430
21512
  }, 3e5);
20431
- this.process = spawn12("python3", [
21513
+ this.process = spawn14("python3", [
20432
21514
  this.scriptPath,
20433
21515
  "--model",
20434
21516
  this.model,
@@ -20580,7 +21662,7 @@ var init_listen = __esm({
20580
21662
  stdio: ["pipe", "pipe", "pipe"]
20581
21663
  }).trim();
20582
21664
  const tcPath = join34(globalRoot, "transcribe-cli");
20583
- if (existsSync25(join34(tcPath, "dist", "index.js"))) {
21665
+ if (existsSync26(join34(tcPath, "dist", "index.js"))) {
20584
21666
  const { createRequire: createRequire4 } = await import("node:module");
20585
21667
  const req = createRequire4(import.meta.url);
20586
21668
  return req(join34(tcPath, "dist", "index.js"));
@@ -20588,12 +21670,12 @@ var init_listen = __esm({
20588
21670
  } catch {
20589
21671
  }
20590
21672
  const nvmBase = join34(homedir8(), ".nvm", "versions", "node");
20591
- if (existsSync25(nvmBase)) {
21673
+ if (existsSync26(nvmBase)) {
20592
21674
  try {
20593
21675
  const { readdirSync: readdirSync16 } = await import("node:fs");
20594
21676
  for (const ver of readdirSync16(nvmBase)) {
20595
21677
  const tcPath = join34(nvmBase, ver, "lib", "node_modules", "transcribe-cli");
20596
- if (existsSync25(join34(tcPath, "dist", "index.js"))) {
21678
+ if (existsSync26(join34(tcPath, "dist", "index.js"))) {
20597
21679
  const { createRequire: createRequire4 } = await import("node:module");
20598
21680
  const req = createRequire4(import.meta.url);
20599
21681
  return req(join34(tcPath, "dist", "index.js"));
@@ -20710,7 +21792,7 @@ transcribe-cli error: ${transcribeCliError}` : "";
20710
21792
  return `Failed to start live transcription: ${msg}${tcHint}`;
20711
21793
  }
20712
21794
  }
20713
- this.micProcess = spawn12(micCmd.cmd, micCmd.args, {
21795
+ this.micProcess = spawn14(micCmd.cmd, micCmd.args, {
20714
21796
  stdio: ["pipe", "pipe", "pipe"],
20715
21797
  env: { ...process.env }
20716
21798
  });
@@ -20879,9 +21961,9 @@ transcribe-cli error: ${transcribeCliError}` : "";
20879
21961
  if (outputDir) {
20880
21962
  const { basename: basename16 } = await import("node:path");
20881
21963
  const transcriptDir = join34(outputDir, ".oa", "transcripts");
20882
- mkdirSync7(transcriptDir, { recursive: true });
21964
+ mkdirSync8(transcriptDir, { recursive: true });
20883
21965
  const outFile = join34(transcriptDir, `${basename16(filePath)}.txt`);
20884
- writeFileSync7(outFile, result.text, "utf-8");
21966
+ writeFileSync8(outFile, result.text, "utf-8");
20885
21967
  }
20886
21968
  return {
20887
21969
  text: result.text,
@@ -25470,7 +26552,7 @@ var init_render = __esm({
25470
26552
 
25471
26553
  // packages/cli/dist/tui/voice-session.js
25472
26554
  import { createServer } from "node:http";
25473
- import { spawn as spawn13, execSync as execSync24 } from "node:child_process";
26555
+ import { spawn as spawn15, execSync as execSync24 } from "node:child_process";
25474
26556
  import { EventEmitter as EventEmitter2 } from "node:events";
25475
26557
  function generateFrontendHTML() {
25476
26558
  return `<!DOCTYPE html>
@@ -26124,7 +27206,7 @@ var init_voice_session = __esm({
26124
27206
  const timeout = setTimeout(() => {
26125
27207
  reject(new Error("Cloudflared tunnel start timeout (30s)"));
26126
27208
  }, 3e4);
26127
- this.cloudflaredProcess = spawn13("cloudflared", [
27209
+ this.cloudflaredProcess = spawn15("cloudflared", [
26128
27210
  "tunnel",
26129
27211
  "--url",
26130
27212
  `http://127.0.0.1:${port}`
@@ -26198,12 +27280,12 @@ var init_voice_session = __esm({
26198
27280
 
26199
27281
  // packages/cli/dist/tui/expose.js
26200
27282
  import { createServer as createServer2, request as httpRequest } from "node:http";
26201
- import { spawn as spawn14, exec } from "node:child_process";
27283
+ import { spawn as spawn16, exec } from "node:child_process";
26202
27284
  import { EventEmitter as EventEmitter3 } from "node:events";
26203
27285
  import { randomBytes as randomBytes7 } from "node:crypto";
26204
27286
  import { URL as URL2 } from "node:url";
26205
27287
  import { loadavg, cpus, totalmem, freemem } from "node:os";
26206
- import { existsSync as existsSync26, readFileSync as readFileSync18, writeFileSync as writeFileSync8, unlinkSync as unlinkSync3, mkdirSync as mkdirSync8, readdirSync as readdirSync7 } from "node:fs";
27288
+ import { existsSync as existsSync27, readFileSync as readFileSync19, writeFileSync as writeFileSync9, unlinkSync as unlinkSync3, mkdirSync as mkdirSync9, readdirSync as readdirSync7 } from "node:fs";
26207
27289
  import { join as join35 } from "node:path";
26208
27290
  function cleanForwardHeaders(raw, targetHost) {
26209
27291
  const out = {};
@@ -26233,9 +27315,9 @@ function fmtTokens(n) {
26233
27315
  function readExposeState(stateDir) {
26234
27316
  try {
26235
27317
  const path = join35(stateDir, STATE_FILE_NAME);
26236
- if (!existsSync26(path))
27318
+ if (!existsSync27(path))
26237
27319
  return null;
26238
- const raw = readFileSync18(path, "utf8");
27320
+ const raw = readFileSync19(path, "utf8");
26239
27321
  const data = JSON.parse(raw);
26240
27322
  if (!data.pid || !data.tunnelUrl || !data.authKey || !data.proxyPort)
26241
27323
  return null;
@@ -26246,8 +27328,8 @@ function readExposeState(stateDir) {
26246
27328
  }
26247
27329
  function writeExposeState(stateDir, state) {
26248
27330
  try {
26249
- mkdirSync8(stateDir, { recursive: true });
26250
- writeFileSync8(join35(stateDir, STATE_FILE_NAME), JSON.stringify(state, null, 2));
27331
+ mkdirSync9(stateDir, { recursive: true });
27332
+ writeFileSync9(join35(stateDir, STATE_FILE_NAME), JSON.stringify(state, null, 2));
26251
27333
  } catch {
26252
27334
  }
26253
27335
  }
@@ -26349,6 +27431,7 @@ var init_expose = __esm({
26349
27431
  _targetUrl;
26350
27432
  _kind;
26351
27433
  _stateDir;
27434
+ _fullAccess;
26352
27435
  _stats = {
26353
27436
  status: "standby",
26354
27437
  totalRequests: 0,
@@ -26381,6 +27464,7 @@ var init_expose = __esm({
26381
27464
  this._kind = options.kind;
26382
27465
  this._targetUrl = options.targetUrl ?? DEFAULT_TARGETS[options.kind];
26383
27466
  this._stateDir = options.stateDir ?? null;
27467
+ this._fullAccess = options.fullAccess ?? false;
26384
27468
  if (options.authKey === void 0 || options.authKey === "") {
26385
27469
  this._authKey = randomBytes7(24).toString("base64url");
26386
27470
  } else {
@@ -26566,6 +27650,18 @@ var init_expose = __esm({
26566
27650
  this.emitStats();
26567
27651
  url.searchParams.delete("key");
26568
27652
  const forwardPath = url.pathname + url.search;
27653
+ if (!this._fullAccess) {
27654
+ const p = url.pathname.toLowerCase();
27655
+ if (p === "/api/pull" || p === "/api/delete" || p === "/api/create" || p === "/api/copy" || p === "/api/push" || p === "/api/blobs") {
27656
+ this._stats.activeConnections--;
27657
+ user.activeRequests--;
27658
+ res.writeHead(403, { "Content-Type": "application/json" });
27659
+ res.end(JSON.stringify({
27660
+ error: "Forbidden \u2014 model management is disabled. Provider must run /expose with --full to allow this."
27661
+ }));
27662
+ return;
27663
+ }
27664
+ }
26569
27665
  const bodyChunks = [];
26570
27666
  req.on("data", (chunk) => bodyChunks.push(chunk));
26571
27667
  req.on("end", () => {
@@ -26755,7 +27851,7 @@ var init_expose = __esm({
26755
27851
  const timeout = setTimeout(() => {
26756
27852
  reject(new Error("Cloudflared tunnel start timeout (30s)"));
26757
27853
  }, 3e4);
26758
- this.cloudflaredProcess = spawn14("cloudflared", [
27854
+ this.cloudflaredProcess = spawn16("cloudflared", [
26759
27855
  "tunnel",
26760
27856
  "--url",
26761
27857
  `http://127.0.0.1:${port}`,
@@ -26956,6 +28052,7 @@ ${this.formatConnectionInfo()}`);
26956
28052
  // NexusTool instance
26957
28053
  _kind;
26958
28054
  _targetUrl;
28055
+ _authKey;
26959
28056
  _peerId = null;
26960
28057
  _capabilities = [];
26961
28058
  _exposedModels = 0;
@@ -26976,6 +28073,9 @@ ${this.formatConnectionInfo()}`);
26976
28073
  get peerId() {
26977
28074
  return this._peerId;
26978
28075
  }
28076
+ get authKey() {
28077
+ return this._authKey;
28078
+ }
26979
28079
  get stats() {
26980
28080
  return this._stats;
26981
28081
  }
@@ -26999,6 +28099,11 @@ ${this.formatConnectionInfo()}`);
26999
28099
  this._margin = options.margin ?? 0.5;
27000
28100
  this._onInfo = options.onInfo;
27001
28101
  this._onError = options.onError;
28102
+ if (options.authKey === void 0 || options.authKey === "") {
28103
+ this._authKey = randomBytes7(24).toString("base64url");
28104
+ } else {
28105
+ this._authKey = options.authKey;
28106
+ }
27002
28107
  }
27003
28108
  async start() {
27004
28109
  this._onInfo?.("Connecting to nexus P2P network...");
@@ -27015,14 +28120,16 @@ ${this.formatConnectionInfo()}`);
27015
28120
  let exposeResult = await this._nexusTool.execute({
27016
28121
  action: "expose",
27017
28122
  ollama_url: this._targetUrl,
27018
- margin: String(this._margin)
28123
+ margin: String(this._margin),
28124
+ auth_key: this._authKey
27019
28125
  });
27020
28126
  if (!exposeResult.success && exposeResult.error?.includes("not running")) {
27021
28127
  await new Promise((r) => setTimeout(r, 3e3));
27022
28128
  exposeResult = await this._nexusTool.execute({
27023
28129
  action: "expose",
27024
28130
  ollama_url: this._targetUrl,
27025
- margin: String(this._margin)
28131
+ margin: String(this._margin),
28132
+ auth_key: this._authKey
27026
28133
  });
27027
28134
  }
27028
28135
  if (!exposeResult.success) {
@@ -27032,7 +28139,7 @@ ${this.formatConnectionInfo()}`);
27032
28139
  const statusPath = join35(nexusDir, "status.json");
27033
28140
  for (let i = 0; i < 60; i++) {
27034
28141
  try {
27035
- const raw = readFileSync18(statusPath, "utf8");
28142
+ const raw = readFileSync19(statusPath, "utf8");
27036
28143
  if (raw.length > 10) {
27037
28144
  const status = JSON.parse(raw);
27038
28145
  if (status.connected && status.peerId) {
@@ -27065,8 +28172,8 @@ ${this.formatConnectionInfo()}`);
27065
28172
  this._pollTimer = setInterval(() => {
27066
28173
  try {
27067
28174
  const statusPath = join35(nexusDir, "status.json");
27068
- if (existsSync26(statusPath)) {
27069
- const status = JSON.parse(readFileSync18(statusPath, "utf8"));
28175
+ if (existsSync27(statusPath)) {
28176
+ const status = JSON.parse(readFileSync19(statusPath, "utf8"));
27070
28177
  if (status.peerId && !this._peerId) {
27071
28178
  this._peerId = status.peerId;
27072
28179
  }
@@ -27076,7 +28183,7 @@ ${this.formatConnectionInfo()}`);
27076
28183
  }
27077
28184
  try {
27078
28185
  const invocDir = join35(nexusDir, "invocations");
27079
- if (existsSync26(invocDir)) {
28186
+ if (existsSync27(invocDir)) {
27080
28187
  const files = readdirSync7(invocDir);
27081
28188
  const invocCount = files.filter((f) => f.endsWith(".json")).length;
27082
28189
  if (invocCount > this._stats.totalRequests) {
@@ -27088,8 +28195,8 @@ ${this.formatConnectionInfo()}`);
27088
28195
  }
27089
28196
  try {
27090
28197
  const meteringFile = join35(nexusDir, "metering.jsonl");
27091
- if (existsSync26(meteringFile)) {
27092
- const content = readFileSync18(meteringFile, "utf8");
28198
+ if (existsSync27(meteringFile)) {
28199
+ const content = readFileSync19(meteringFile, "utf8");
27093
28200
  if (content.length > lastMeteringSize) {
27094
28201
  const newContent = content.slice(lastMeteringSize);
27095
28202
  lastMeteringSize = content.length;
@@ -27165,15 +28272,24 @@ ${this.formatConnectionInfo()}`);
27165
28272
  }
27166
28273
  /** Format connection info for display */
27167
28274
  formatConnectionInfo() {
28275
+ const endpointCmd = `/endpoint ${this._peerId ?? "<peer-id>"} --auth ${this._authKey}`;
27168
28276
  const lines = [];
27169
28277
  lines.push(` ${c2.green("\u25CF")} Expose gateway active ${c2.dim("(libp2p)")}`);
27170
28278
  lines.push(` ${c2.cyan("Backend".padEnd(12))} ${this._kind} (${this._targetUrl})`);
27171
28279
  lines.push(` ${c2.cyan("Transport".padEnd(12))} ${c2.bold("libp2p")} (DHT + mDNS + NATS relay)`);
27172
28280
  lines.push(` ${c2.cyan("Peer ID".padEnd(12))} ${c2.bold(this._peerId ?? "connecting...")}`);
28281
+ lines.push(` ${c2.cyan("Auth Key".padEnd(12))} ${c2.bold(this._authKey)}`);
27173
28282
  lines.push(` ${c2.cyan("Models".padEnd(12))} ${this._exposedModels || this._capabilities.filter((cap) => cap.startsWith("inference:")).length} exposed`);
27174
28283
  lines.push("");
27175
- lines.push(` ${c2.dim("Remote peers discover your models via nexus network.")}`);
27176
- lines.push(` ${c2.dim("Consumers use: /nexus invoke_capability inference:<model>")}`);
28284
+ lines.push(` ${c2.dim("Remote consumers connect with:")}`);
28285
+ lines.push(` ${c2.bold(endpointCmd)}`);
28286
+ lines.push("");
28287
+ lines.push(` ${c2.yellow("!")} ${c2.dim("Keep this key secure \u2014 it grants full access to your models.")}`);
28288
+ if ((process.stdout.isTTY ?? false) && this._peerId) {
28289
+ const b64 = Buffer.from(endpointCmd).toString("base64");
28290
+ lines.push(`\x1B]52;c;${b64}\x07`);
28291
+ lines.push(` ${c2.dim("(copied to clipboard)")}`);
28292
+ }
27177
28293
  const infCaps = this._capabilities.filter((cap) => cap.startsWith("inference:"));
27178
28294
  if (infCaps.length > 0) {
27179
28295
  lines.push("");
@@ -27255,8 +28371,8 @@ var init_types = __esm({
27255
28371
 
27256
28372
  // packages/cli/dist/tui/p2p/secret-vault.js
27257
28373
  import { createCipheriv as createCipheriv2, createDecipheriv as createDecipheriv2, randomBytes as randomBytes8, scryptSync as scryptSync2, createHash as createHash2 } from "node:crypto";
27258
- import { readFileSync as readFileSync19, writeFileSync as writeFileSync9, existsSync as existsSync27, mkdirSync as mkdirSync9 } from "node:fs";
27259
- import { join as join36, dirname as dirname13 } from "node:path";
28374
+ import { readFileSync as readFileSync20, writeFileSync as writeFileSync10, existsSync as existsSync28, mkdirSync as mkdirSync10 } from "node:fs";
28375
+ import { join as join36, dirname as dirname14 } from "node:path";
27260
28376
  var PLACEHOLDER_PREFIX, PLACEHOLDER_SUFFIX, CIPHER_ALGO, SALT_LEN, IV_LEN, KEY_LEN, SecretVault;
27261
28377
  var init_secret_vault = __esm({
27262
28378
  "packages/cli/dist/tui/p2p/secret-vault.js"() {
@@ -27467,19 +28583,19 @@ var init_secret_vault = __esm({
27467
28583
  const encrypted = Buffer.concat([cipher.update(data, "utf8"), cipher.final()]);
27468
28584
  const tag = cipher.getAuthTag();
27469
28585
  const blob = Buffer.concat([salt, iv, tag, encrypted]);
27470
- const dir = dirname13(this.storePath);
27471
- if (!existsSync27(dir))
27472
- mkdirSync9(dir, { recursive: true });
27473
- writeFileSync9(this.storePath, blob, { mode: 384 });
28586
+ const dir = dirname14(this.storePath);
28587
+ if (!existsSync28(dir))
28588
+ mkdirSync10(dir, { recursive: true });
28589
+ writeFileSync10(this.storePath, blob, { mode: 384 });
27474
28590
  }
27475
28591
  /**
27476
28592
  * Load vault from disk, decrypting with the given passphrase.
27477
28593
  * Returns the number of secrets loaded.
27478
28594
  */
27479
28595
  load(passphrase) {
27480
- if (!this.storePath || !existsSync27(this.storePath))
28596
+ if (!this.storePath || !existsSync28(this.storePath))
27481
28597
  return 0;
27482
- const blob = readFileSync19(this.storePath);
28598
+ const blob = readFileSync20(this.storePath);
27483
28599
  if (blob.length < SALT_LEN + IV_LEN + 16) {
27484
28600
  throw new Error("Vault file is corrupted (too small)");
27485
28601
  }
@@ -28516,7 +29632,54 @@ async function fetchOpenAIModels(baseUrl, apiKey) {
28516
29632
  parameterSize: m.owned_by ?? void 0
28517
29633
  })).sort((a, b) => a.name.localeCompare(b.name));
28518
29634
  }
29635
+ async function fetchPeerModels(peerId) {
29636
+ try {
29637
+ const { NexusTool: NexusTool2 } = await Promise.resolve().then(() => (init_dist2(), dist_exports));
29638
+ const cwd4 = process.cwd();
29639
+ const nexusTool = new NexusTool2(cwd4);
29640
+ const result = await nexusTool.execute({
29641
+ action: "find_agent",
29642
+ peer_id: peerId
29643
+ });
29644
+ if (result.success && result.output) {
29645
+ const models = [];
29646
+ const capMatches = result.output.matchAll(/inference:([^\s,\]]+)/g);
29647
+ for (const m of capMatches) {
29648
+ const capName = m[1];
29649
+ const modelName = capName.replace(/_(\d+[bBmMkK])$/, ":$1").replace(/_latest$/, ":latest");
29650
+ models.push({
29651
+ name: modelName,
29652
+ size: "",
29653
+ modified: "",
29654
+ sizeBytes: 0,
29655
+ parameterSize: "remote"
29656
+ });
29657
+ }
29658
+ if (models.length > 0)
29659
+ return models;
29660
+ }
29661
+ const { existsSync: existsSync42, readFileSync: readFileSync31 } = await import("node:fs");
29662
+ const { join: join55 } = await import("node:path");
29663
+ const pricingPath = join55(nexusTool.getNexusDir(), "pricing.json");
29664
+ if (existsSync42(pricingPath)) {
29665
+ const pricing = JSON.parse(readFileSync31(pricingPath, "utf8"));
29666
+ return (pricing.models || []).map((m) => ({
29667
+ name: m.model || "unknown",
29668
+ size: m.parameterSize || "",
29669
+ modified: "",
29670
+ sizeBytes: 0,
29671
+ parameterSize: m.parameterSize || "remote"
29672
+ }));
29673
+ }
29674
+ return [];
29675
+ } catch {
29676
+ return [];
29677
+ }
29678
+ }
28519
29679
  async function fetchModels(baseUrl, apiKey) {
29680
+ if (baseUrl.startsWith("peer://")) {
29681
+ return fetchPeerModels(baseUrl.slice(7));
29682
+ }
28520
29683
  const provider = detectProvider(baseUrl);
28521
29684
  if (provider.id === "ollama") {
28522
29685
  try {
@@ -28593,6 +29756,8 @@ async function queryOpenAIContextSize(baseUrl, modelName, apiKey) {
28593
29756
  }
28594
29757
  }
28595
29758
  async function queryContextSize(baseUrl, modelName, apiKey) {
29759
+ if (baseUrl.startsWith("peer://"))
29760
+ return 32768;
28596
29761
  const ollamaSize = await queryModelContextSize(baseUrl, modelName);
28597
29762
  if (ollamaSize)
28598
29763
  return ollamaSize;
@@ -28600,6 +29765,16 @@ async function queryContextSize(baseUrl, modelName, apiKey) {
28600
29765
  }
28601
29766
  async function queryModelCapabilities(baseUrl, modelName) {
28602
29767
  const caps = { vision: false, toolUse: false, thinking: false };
29768
+ if (baseUrl.startsWith("peer://")) {
29769
+ const nameLower = modelName.toLowerCase();
29770
+ if (/qwen3|qwen2\.5|llama3\.[13]|mistral|mixtral|command-r|gemma3|devstral|deepseek/.test(nameLower)) {
29771
+ caps.toolUse = true;
29772
+ }
29773
+ if (/qwen3|deepseek-r1/.test(nameLower)) {
29774
+ caps.thinking = true;
29775
+ }
29776
+ return caps;
29777
+ }
28603
29778
  try {
28604
29779
  const normalized = normalizeBaseUrl(baseUrl);
28605
29780
  const res = await fetch(`${normalized}/api/show`, {
@@ -28699,17 +29874,17 @@ var init_render2 = __esm({
28699
29874
  });
28700
29875
 
28701
29876
  // packages/prompts/dist/promptLoader.js
28702
- import { readFileSync as readFileSync20, existsSync as existsSync28 } from "node:fs";
28703
- import { join as join37, dirname as dirname14 } from "node:path";
29877
+ import { readFileSync as readFileSync21, existsSync as existsSync29 } from "node:fs";
29878
+ import { join as join37, dirname as dirname15 } from "node:path";
28704
29879
  import { fileURLToPath as fileURLToPath9 } from "node:url";
28705
29880
  function loadPrompt2(promptPath, vars) {
28706
29881
  let content = cache2.get(promptPath);
28707
29882
  if (content === void 0) {
28708
29883
  const fullPath = join37(PROMPTS_DIR2, promptPath);
28709
- if (!existsSync28(fullPath)) {
29884
+ if (!existsSync29(fullPath)) {
28710
29885
  throw new Error(`Prompt file not found: ${fullPath}`);
28711
29886
  }
28712
- content = readFileSync20(fullPath, "utf-8");
29887
+ content = readFileSync21(fullPath, "utf-8");
28713
29888
  cache2.set(promptPath, content);
28714
29889
  }
28715
29890
  if (!vars)
@@ -28721,10 +29896,10 @@ var init_promptLoader2 = __esm({
28721
29896
  "packages/prompts/dist/promptLoader.js"() {
28722
29897
  "use strict";
28723
29898
  __filename2 = fileURLToPath9(import.meta.url);
28724
- __dirname5 = dirname14(__filename2);
29899
+ __dirname5 = dirname15(__filename2);
28725
29900
  devPath = join37(__dirname5, "..", "templates");
28726
29901
  publishedPath = join37(__dirname5, "..", "prompts", "templates");
28727
- PROMPTS_DIR2 = existsSync28(devPath) ? devPath : publishedPath;
29902
+ PROMPTS_DIR2 = existsSync29(devPath) ? devPath : publishedPath;
28728
29903
  cache2 = /* @__PURE__ */ new Map();
28729
29904
  }
28730
29905
  });
@@ -28835,7 +30010,7 @@ var init_task_templates = __esm({
28835
30010
  });
28836
30011
 
28837
30012
  // packages/prompts/dist/index.js
28838
- import { join as join38, dirname as dirname15 } from "node:path";
30013
+ import { join as join38, dirname as dirname16 } from "node:path";
28839
30014
  import { fileURLToPath as fileURLToPath10 } from "node:url";
28840
30015
  var _dir, _packageRoot;
28841
30016
  var init_dist6 = __esm({
@@ -28845,27 +30020,27 @@ var init_dist6 = __esm({
28845
30020
  init_render2();
28846
30021
  init_task_templates();
28847
30022
  init_render2();
28848
- _dir = dirname15(fileURLToPath10(import.meta.url));
30023
+ _dir = dirname16(fileURLToPath10(import.meta.url));
28849
30024
  _packageRoot = join38(_dir, "..");
28850
30025
  }
28851
30026
  });
28852
30027
 
28853
30028
  // packages/cli/dist/tui/oa-directory.js
28854
- import { existsSync as existsSync29, mkdirSync as mkdirSync10, readFileSync as readFileSync21, writeFileSync as writeFileSync10, readdirSync as readdirSync8, statSync as statSync9, unlinkSync as unlinkSync4 } from "node:fs";
30029
+ import { existsSync as existsSync30, mkdirSync as mkdirSync11, readFileSync as readFileSync22, writeFileSync as writeFileSync11, readdirSync as readdirSync8, statSync as statSync9, unlinkSync as unlinkSync4 } from "node:fs";
28855
30030
  import { join as join39, relative as relative2, basename as basename9, extname as extname8 } from "node:path";
28856
30031
  import { homedir as homedir9 } from "node:os";
28857
30032
  function initOaDirectory(repoRoot) {
28858
30033
  const oaPath = join39(repoRoot, OA_DIR);
28859
30034
  for (const sub of SUBDIRS) {
28860
- mkdirSync10(join39(oaPath, sub), { recursive: true });
30035
+ mkdirSync11(join39(oaPath, sub), { recursive: true });
28861
30036
  }
28862
30037
  try {
28863
30038
  const gitignorePath = join39(repoRoot, ".gitignore");
28864
30039
  const settingsPattern = ".oa/settings.json";
28865
- if (existsSync29(gitignorePath)) {
28866
- const content = readFileSync21(gitignorePath, "utf-8");
30040
+ if (existsSync30(gitignorePath)) {
30041
+ const content = readFileSync22(gitignorePath, "utf-8");
28867
30042
  if (!content.includes(settingsPattern)) {
28868
- writeFileSync10(gitignorePath, content.trimEnd() + "\n" + settingsPattern + "\n", "utf-8");
30043
+ writeFileSync11(gitignorePath, content.trimEnd() + "\n" + settingsPattern + "\n", "utf-8");
28869
30044
  }
28870
30045
  }
28871
30046
  } catch {
@@ -28873,13 +30048,13 @@ function initOaDirectory(repoRoot) {
28873
30048
  return oaPath;
28874
30049
  }
28875
30050
  function hasOaDirectory(repoRoot) {
28876
- return existsSync29(join39(repoRoot, OA_DIR, "index"));
30051
+ return existsSync30(join39(repoRoot, OA_DIR, "index"));
28877
30052
  }
28878
30053
  function loadProjectSettings(repoRoot) {
28879
30054
  const settingsPath = join39(repoRoot, OA_DIR, "settings.json");
28880
30055
  try {
28881
- if (existsSync29(settingsPath)) {
28882
- return JSON.parse(readFileSync21(settingsPath, "utf-8"));
30056
+ if (existsSync30(settingsPath)) {
30057
+ return JSON.parse(readFileSync22(settingsPath, "utf-8"));
28883
30058
  }
28884
30059
  } catch {
28885
30060
  }
@@ -28887,16 +30062,16 @@ function loadProjectSettings(repoRoot) {
28887
30062
  }
28888
30063
  function saveProjectSettings(repoRoot, settings) {
28889
30064
  const oaPath = join39(repoRoot, OA_DIR);
28890
- mkdirSync10(oaPath, { recursive: true });
30065
+ mkdirSync11(oaPath, { recursive: true });
28891
30066
  const existing = loadProjectSettings(repoRoot);
28892
30067
  const merged = { ...existing, ...settings };
28893
- writeFileSync10(join39(oaPath, "settings.json"), JSON.stringify(merged, null, 2) + "\n", { encoding: "utf-8", mode: 384 });
30068
+ writeFileSync11(join39(oaPath, "settings.json"), JSON.stringify(merged, null, 2) + "\n", { encoding: "utf-8", mode: 384 });
28894
30069
  }
28895
30070
  function loadGlobalSettings() {
28896
30071
  const settingsPath = join39(homedir9(), ".open-agents", "settings.json");
28897
30072
  try {
28898
- if (existsSync29(settingsPath)) {
28899
- return JSON.parse(readFileSync21(settingsPath, "utf-8"));
30073
+ if (existsSync30(settingsPath)) {
30074
+ return JSON.parse(readFileSync22(settingsPath, "utf-8"));
28900
30075
  }
28901
30076
  } catch {
28902
30077
  }
@@ -28904,10 +30079,10 @@ function loadGlobalSettings() {
28904
30079
  }
28905
30080
  function saveGlobalSettings(settings) {
28906
30081
  const dir = join39(homedir9(), ".open-agents");
28907
- mkdirSync10(dir, { recursive: true });
30082
+ mkdirSync11(dir, { recursive: true });
28908
30083
  const existing = loadGlobalSettings();
28909
30084
  const merged = { ...existing, ...settings };
28910
- writeFileSync10(join39(dir, "settings.json"), JSON.stringify(merged, null, 2) + "\n", { encoding: "utf-8", mode: 384 });
30085
+ writeFileSync11(join39(dir, "settings.json"), JSON.stringify(merged, null, 2) + "\n", { encoding: "utf-8", mode: 384 });
28911
30086
  }
28912
30087
  function resolveSettings(repoRoot) {
28913
30088
  const global = loadGlobalSettings();
@@ -28924,10 +30099,10 @@ function discoverContextFiles(repoRoot, maxContentLen = 8e3) {
28924
30099
  for (const name of CONTEXT_FILES) {
28925
30100
  const filePath = join39(dir, name);
28926
30101
  const normalizedName = name.toLowerCase();
28927
- if (existsSync29(filePath) && !seen.has(filePath)) {
30102
+ if (existsSync30(filePath) && !seen.has(filePath)) {
28928
30103
  seen.add(filePath);
28929
30104
  try {
28930
- let content = readFileSync21(filePath, "utf-8");
30105
+ let content = readFileSync22(filePath, "utf-8");
28931
30106
  if (content.length > maxContentLen) {
28932
30107
  content = content.slice(0, maxContentLen) + "\n\n...(truncated)";
28933
30108
  }
@@ -28942,10 +30117,10 @@ function discoverContextFiles(repoRoot, maxContentLen = 8e3) {
28942
30117
  }
28943
30118
  }
28944
30119
  const projectMap = join39(dir, OA_DIR, "context", "project-map.md");
28945
- if (existsSync29(projectMap) && !seen.has(projectMap)) {
30120
+ if (existsSync30(projectMap) && !seen.has(projectMap)) {
28946
30121
  seen.add(projectMap);
28947
30122
  try {
28948
- let content = readFileSync21(projectMap, "utf-8");
30123
+ let content = readFileSync22(projectMap, "utf-8");
28949
30124
  if (content.length > maxContentLen) {
28950
30125
  content = content.slice(0, maxContentLen) + "\n\n...(truncated)";
28951
30126
  }
@@ -28977,7 +30152,7 @@ function discoverContextFiles(repoRoot, maxContentLen = 8e3) {
28977
30152
  function readIndexMeta(repoRoot) {
28978
30153
  const metaPath = join39(repoRoot, OA_DIR, "index", "meta.json");
28979
30154
  try {
28980
- return JSON.parse(readFileSync21(metaPath, "utf-8"));
30155
+ return JSON.parse(readFileSync22(metaPath, "utf-8"));
28981
30156
  } catch {
28982
30157
  return null;
28983
30158
  }
@@ -29029,18 +30204,18 @@ ${tree}\`\`\`
29029
30204
  }
29030
30205
  const content = sections.join("\n");
29031
30206
  const contextDir = join39(repoRoot, OA_DIR, "context");
29032
- mkdirSync10(contextDir, { recursive: true });
29033
- writeFileSync10(join39(contextDir, "project-map.md"), content, "utf-8");
30207
+ mkdirSync11(contextDir, { recursive: true });
30208
+ writeFileSync11(join39(contextDir, "project-map.md"), content, "utf-8");
29034
30209
  return content;
29035
30210
  }
29036
30211
  function saveSession(repoRoot, session) {
29037
30212
  const historyDir = join39(repoRoot, OA_DIR, "history");
29038
- mkdirSync10(historyDir, { recursive: true });
29039
- writeFileSync10(join39(historyDir, `${session.id}.json`), JSON.stringify(session, null, 2), "utf-8");
30213
+ mkdirSync11(historyDir, { recursive: true });
30214
+ writeFileSync11(join39(historyDir, `${session.id}.json`), JSON.stringify(session, null, 2), "utf-8");
29040
30215
  }
29041
30216
  function loadRecentSessions(repoRoot, limit = 5) {
29042
30217
  const historyDir = join39(repoRoot, OA_DIR, "history");
29043
- if (!existsSync29(historyDir))
30218
+ if (!existsSync30(historyDir))
29044
30219
  return [];
29045
30220
  try {
29046
30221
  const files = readdirSync8(historyDir).filter((f) => f.endsWith(".json") && f !== "pending-task.json").map((f) => {
@@ -29049,7 +30224,7 @@ function loadRecentSessions(repoRoot, limit = 5) {
29049
30224
  }).sort((a, b) => b.mtime - a.mtime).slice(0, limit);
29050
30225
  return files.map((f) => {
29051
30226
  try {
29052
- return JSON.parse(readFileSync21(join39(historyDir, f.file), "utf-8"));
30227
+ return JSON.parse(readFileSync22(join39(historyDir, f.file), "utf-8"));
29053
30228
  } catch {
29054
30229
  return null;
29055
30230
  }
@@ -29060,15 +30235,15 @@ function loadRecentSessions(repoRoot, limit = 5) {
29060
30235
  }
29061
30236
  function savePendingTask(repoRoot, task) {
29062
30237
  const historyDir = join39(repoRoot, OA_DIR, "history");
29063
- mkdirSync10(historyDir, { recursive: true });
29064
- writeFileSync10(join39(historyDir, PENDING_TASK_FILE), JSON.stringify(task, null, 2) + "\n", "utf-8");
30238
+ mkdirSync11(historyDir, { recursive: true });
30239
+ writeFileSync11(join39(historyDir, PENDING_TASK_FILE), JSON.stringify(task, null, 2) + "\n", "utf-8");
29065
30240
  }
29066
30241
  function loadPendingTask(repoRoot) {
29067
30242
  const filePath = join39(repoRoot, OA_DIR, "history", PENDING_TASK_FILE);
29068
30243
  try {
29069
- if (!existsSync29(filePath))
30244
+ if (!existsSync30(filePath))
29070
30245
  return null;
29071
- const data = JSON.parse(readFileSync21(filePath, "utf-8"));
30246
+ const data = JSON.parse(readFileSync22(filePath, "utf-8"));
29072
30247
  try {
29073
30248
  unlinkSync4(filePath);
29074
30249
  } catch {
@@ -29080,12 +30255,12 @@ function loadPendingTask(repoRoot) {
29080
30255
  }
29081
30256
  function saveSessionContext(repoRoot, entry) {
29082
30257
  const contextDir = join39(repoRoot, OA_DIR, "context");
29083
- mkdirSync10(contextDir, { recursive: true });
30258
+ mkdirSync11(contextDir, { recursive: true });
29084
30259
  const filePath = join39(contextDir, CONTEXT_SAVE_FILE);
29085
30260
  let ctx;
29086
30261
  try {
29087
- if (existsSync29(filePath)) {
29088
- ctx = JSON.parse(readFileSync21(filePath, "utf-8"));
30262
+ if (existsSync30(filePath)) {
30263
+ ctx = JSON.parse(readFileSync22(filePath, "utf-8"));
29089
30264
  } else {
29090
30265
  ctx = { entries: [], maxEntries: MAX_CONTEXT_ENTRIES, updatedAt: "" };
29091
30266
  }
@@ -29097,14 +30272,14 @@ function saveSessionContext(repoRoot, entry) {
29097
30272
  ctx.entries = ctx.entries.slice(-ctx.maxEntries);
29098
30273
  }
29099
30274
  ctx.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
29100
- writeFileSync10(filePath, JSON.stringify(ctx, null, 2) + "\n", "utf-8");
30275
+ writeFileSync11(filePath, JSON.stringify(ctx, null, 2) + "\n", "utf-8");
29101
30276
  }
29102
30277
  function loadSessionContext(repoRoot) {
29103
30278
  const filePath = join39(repoRoot, OA_DIR, "context", CONTEXT_SAVE_FILE);
29104
30279
  try {
29105
- if (!existsSync29(filePath))
30280
+ if (!existsSync30(filePath))
29106
30281
  return null;
29107
- return JSON.parse(readFileSync21(filePath, "utf-8"));
30282
+ return JSON.parse(readFileSync22(filePath, "utf-8"));
29108
30283
  } catch {
29109
30284
  return null;
29110
30285
  }
@@ -29152,11 +30327,11 @@ function detectManifests(repoRoot) {
29152
30327
  ];
29153
30328
  for (const check of checks) {
29154
30329
  const filePath = join39(repoRoot, check.file);
29155
- if (existsSync29(filePath)) {
30330
+ if (existsSync30(filePath)) {
29156
30331
  let name;
29157
30332
  if (check.nameField) {
29158
30333
  try {
29159
- const data = JSON.parse(readFileSync21(filePath, "utf-8"));
30334
+ const data = JSON.parse(readFileSync22(filePath, "utf-8"));
29160
30335
  name = data[check.nameField];
29161
30336
  } catch {
29162
30337
  }
@@ -29185,7 +30360,7 @@ function findKeyFiles(repoRoot) {
29185
30360
  { pattern: "CLAUDE.md", description: "Claude Code context" }
29186
30361
  ];
29187
30362
  for (const check of checks) {
29188
- if (existsSync29(join39(repoRoot, check.pattern))) {
30363
+ if (existsSync30(join39(repoRoot, check.pattern))) {
29189
30364
  keyFiles.push({ path: check.pattern, description: check.description });
29190
30365
  }
29191
30366
  }
@@ -29228,8 +30403,8 @@ function buildDirTree(root, maxDepth, prefix = "", depth = 0) {
29228
30403
  }
29229
30404
  function loadUsageFile(filePath) {
29230
30405
  try {
29231
- if (existsSync29(filePath)) {
29232
- return JSON.parse(readFileSync21(filePath, "utf-8"));
30406
+ if (existsSync30(filePath)) {
30407
+ return JSON.parse(readFileSync22(filePath, "utf-8"));
29233
30408
  }
29234
30409
  } catch {
29235
30410
  }
@@ -29237,8 +30412,8 @@ function loadUsageFile(filePath) {
29237
30412
  }
29238
30413
  function saveUsageFile(filePath, data) {
29239
30414
  const dir = join39(filePath, "..");
29240
- mkdirSync10(dir, { recursive: true });
29241
- writeFileSync10(filePath, JSON.stringify(data, null, 2) + "\n", { encoding: "utf-8", mode: 384 });
30415
+ mkdirSync11(dir, { recursive: true });
30416
+ writeFileSync11(filePath, JSON.stringify(data, null, 2) + "\n", { encoding: "utf-8", mode: 384 });
29242
30417
  }
29243
30418
  function recordUsage(kind, value, opts) {
29244
30419
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -29341,9 +30516,9 @@ var init_oa_directory = __esm({
29341
30516
 
29342
30517
  // packages/cli/dist/tui/setup.js
29343
30518
  import * as readline from "node:readline";
29344
- import { execSync as execSync25, spawn as spawn15, exec as exec2 } from "node:child_process";
30519
+ import { execSync as execSync25, spawn as spawn17, exec as exec2 } from "node:child_process";
29345
30520
  import { promisify as promisify5 } from "node:util";
29346
- import { existsSync as existsSync30, writeFileSync as writeFileSync11, mkdirSync as mkdirSync11 } from "node:fs";
30521
+ import { existsSync as existsSync31, writeFileSync as writeFileSync12, mkdirSync as mkdirSync12 } from "node:fs";
29347
30522
  import { join as join40 } from "node:path";
29348
30523
  import { homedir as homedir10, platform } from "node:os";
29349
30524
  function detectSystemSpecs() {
@@ -29621,7 +30796,7 @@ async function installOllamaMac(_rl) {
29621
30796
  execSync25('/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"', { stdio: "inherit", timeout: 6e5 });
29622
30797
  if (!hasCmd("brew")) {
29623
30798
  try {
29624
- const brewPrefix = existsSync30("/opt/homebrew/bin/brew") ? "/opt/homebrew" : "/usr/local";
30799
+ const brewPrefix = existsSync31("/opt/homebrew/bin/brew") ? "/opt/homebrew" : "/usr/local";
29625
30800
  process.env["PATH"] = `${brewPrefix}/bin:${process.env["PATH"]}`;
29626
30801
  } catch {
29627
30802
  }
@@ -29729,7 +30904,7 @@ async function ensureOllamaRunning(backendUrl, rl) {
29729
30904
  process.stdout.write(` ${c2.cyan("\u25CF")} Starting ollama serve...
29730
30905
  `);
29731
30906
  try {
29732
- const child = spawn15("ollama", ["serve"], { stdio: "ignore", detached: true });
30907
+ const child = spawn17("ollama", ["serve"], { stdio: "ignore", detached: true });
29733
30908
  child.unref();
29734
30909
  } catch {
29735
30910
  process.stdout.write(` ${c2.yellow("\u26A0")} Could not start ollama serve.
@@ -30197,7 +31372,7 @@ async function doSetup(config, rl) {
30197
31372
  ${c2.cyan("\u25CF")} Ollama is installed but not running. Starting automatically...
30198
31373
  `);
30199
31374
  try {
30200
- const child = spawn15("ollama", ["serve"], { stdio: "ignore", detached: true });
31375
+ const child = spawn17("ollama", ["serve"], { stdio: "ignore", detached: true });
30201
31376
  child.unref();
30202
31377
  await new Promise((resolve31) => setTimeout(resolve31, 3e3));
30203
31378
  try {
@@ -30225,7 +31400,7 @@ async function doSetup(config, rl) {
30225
31400
  ${c2.cyan("\u25CF")} Starting ollama serve...
30226
31401
  `);
30227
31402
  try {
30228
- const child = spawn15("ollama", ["serve"], { stdio: "ignore", detached: true });
31403
+ const child = spawn17("ollama", ["serve"], { stdio: "ignore", detached: true });
30229
31404
  child.unref();
30230
31405
  await new Promise((resolve31) => setTimeout(resolve31, 3e3));
30231
31406
  try {
@@ -30376,9 +31551,9 @@ async function doSetup(config, rl) {
30376
31551
  `PARAMETER stop "<|endoftext|>"`
30377
31552
  ].join("\n");
30378
31553
  const modelDir2 = join40(homedir10(), ".open-agents", "models");
30379
- mkdirSync11(modelDir2, { recursive: true });
31554
+ mkdirSync12(modelDir2, { recursive: true });
30380
31555
  const modelfilePath = join40(modelDir2, `Modelfile.${customName}`);
30381
- writeFileSync11(modelfilePath, modelfileContent + "\n", "utf8");
31556
+ writeFileSync12(modelfilePath, modelfileContent + "\n", "utf8");
30382
31557
  process.stdout.write(` ${c2.dim("Creating model...")} `);
30383
31558
  execSync25(`ollama create ${customName} -f ${modelfilePath}`, {
30384
31559
  stdio: "pipe",
@@ -30423,7 +31598,7 @@ async function isModelAvailable(config) {
30423
31598
  }
30424
31599
  function isFirstRun() {
30425
31600
  try {
30426
- return !existsSync30(join40(homedir10(), ".open-agents", "config.json"));
31601
+ return !existsSync31(join40(homedir10(), ".open-agents", "config.json"));
30427
31602
  } catch {
30428
31603
  return true;
30429
31604
  }
@@ -30473,7 +31648,7 @@ function hasVenvModule() {
30473
31648
  function ensureVenv(log) {
30474
31649
  const venvDir = getVenvDir();
30475
31650
  const venvPip = join40(venvDir, "bin", "pip");
30476
- if (existsSync30(venvPip))
31651
+ if (existsSync31(venvPip))
30477
31652
  return venvDir;
30478
31653
  log("Creating Python venv for vision deps...");
30479
31654
  if (!hasCmd("python3")) {
@@ -30485,7 +31660,7 @@ function ensureVenv(log) {
30485
31660
  return null;
30486
31661
  }
30487
31662
  try {
30488
- mkdirSync11(join40(homedir10(), ".open-agents"), { recursive: true });
31663
+ mkdirSync12(join40(homedir10(), ".open-agents"), { recursive: true });
30489
31664
  execSync25(`python3 -m venv "${venvDir}"`, { stdio: "pipe", timeout: 3e4 });
30490
31665
  execSync25(`"${join40(venvDir, "bin", "pip")}" install --upgrade pip`, {
30491
31666
  stdio: "pipe",
@@ -30681,12 +31856,12 @@ async function ensureVisionDeps(onInfo, getSudoPassword) {
30681
31856
  const venvBin = join40(venvDir, "bin");
30682
31857
  const venvMoondream = join40(venvBin, "moondream-station");
30683
31858
  const venv = ensureVenv(log);
30684
- if (venv && !hasCmd("moondream-station") && !existsSync30(venvMoondream)) {
31859
+ if (venv && !hasCmd("moondream-station") && !existsSync31(venvMoondream)) {
30685
31860
  const venvPip = join40(venvBin, "pip");
30686
31861
  log("Installing moondream-station in ~/.open-agents/venv...");
30687
31862
  try {
30688
31863
  execSync25(`"${venvPip}" install moondream-station`, { stdio: "pipe", timeout: 3e5 });
30689
- if (existsSync30(venvMoondream)) {
31864
+ if (existsSync31(venvMoondream)) {
30690
31865
  log("moondream-station installed successfully.");
30691
31866
  } else {
30692
31867
  try {
@@ -30818,9 +31993,9 @@ async function createExpandedVariantAsync(baseModel, specs, sizeGB) {
30818
31993
  `PARAMETER stop "<|endoftext|>"`
30819
31994
  ].join("\n");
30820
31995
  const modelDir2 = join40(homedir10(), ".open-agents", "models");
30821
- mkdirSync11(modelDir2, { recursive: true });
31996
+ mkdirSync12(modelDir2, { recursive: true });
30822
31997
  const modelfilePath = join40(modelDir2, `Modelfile.${customName}`);
30823
- writeFileSync11(modelfilePath, modelfileContent + "\n", "utf8");
31998
+ writeFileSync12(modelfilePath, modelfileContent + "\n", "utf8");
30824
31999
  await execAsync(`ollama create ${customName} -f ${modelfilePath}`, {
30825
32000
  timeout: 12e4
30826
32001
  });
@@ -31838,6 +33013,7 @@ async function handleSlashCommand(input, ctx) {
31838
33013
  renderInfo(" /expose status Show usage stats");
31839
33014
  renderInfo(" --key <key> Use specific auth key (tunnel mode)");
31840
33015
  renderInfo(" --key Auto-generate auth key (tunnel mode)");
33016
+ renderInfo(" --full Allow full Ollama API access (pull/delete/etc)");
31841
33017
  }
31842
33018
  return "handled";
31843
33019
  }
@@ -31857,8 +33033,9 @@ async function handleSlashCommand(input, ctx) {
31857
33033
  } else if (exposeParts.includes("--libp2p")) {
31858
33034
  exposeTransport = "libp2p";
31859
33035
  }
33036
+ const exposeFullAccess = exposeParts.includes("--full");
31860
33037
  try {
31861
- const tunnelUrl = await ctx.exposeStart(kindOrUrl, exposeAuthKey, exposeTransport);
33038
+ const tunnelUrl = await ctx.exposeStart(kindOrUrl, exposeAuthKey, exposeTransport, exposeFullAccess);
31862
33039
  if (tunnelUrl) {
31863
33040
  const info = ctx.getExposeInfo?.();
31864
33041
  if (info) {
@@ -32618,6 +33795,10 @@ async function handleEndpoint(arg, ctx, local = false) {
32618
33795
  if (authIdx !== -1 && parts[authIdx + 1]) {
32619
33796
  apiKey = parts[authIdx + 1];
32620
33797
  }
33798
+ if (url.startsWith("12D3KooW") && url.length > 40 && !url.includes("://")) {
33799
+ await handlePeerEndpoint(url, apiKey, ctx, local);
33800
+ return;
33801
+ }
32621
33802
  try {
32622
33803
  new URL(url);
32623
33804
  } catch {
@@ -32741,6 +33922,110 @@ async function handleEndpoint(arg, ctx, local = false) {
32741
33922
  renderWarning(`Could not verify model availability on ${provider.label}. If inference fails, use /model to switch.`);
32742
33923
  }
32743
33924
  }
33925
+ async function handlePeerEndpoint(peerId, authKey, ctx, local) {
33926
+ process.stdout.write(`
33927
+ ${c2.dim("Detected:")} ${c2.bold("libp2p peer")}
33928
+ `);
33929
+ process.stdout.write(` ${c2.cyan("Peer ID".padEnd(12))} ${peerId}
33930
+ `);
33931
+ if (authKey) {
33932
+ process.stdout.write(` ${c2.cyan("Auth Key".padEnd(12))} ${authKey.slice(0, 8)}...
33933
+ `);
33934
+ }
33935
+ process.stdout.write(` ${c2.dim("Connecting to nexus network...")} `);
33936
+ try {
33937
+ if (!ctx.nexusConnect) {
33938
+ throw new Error("Nexus not available \u2014 run /nexus connect first");
33939
+ }
33940
+ const connectMsg = await ctx.nexusConnect();
33941
+ process.stdout.write(`${c2.green("\u2714")} Connected
33942
+ `);
33943
+ } catch (err) {
33944
+ process.stdout.write(`${c2.yellow("\u26A0")} ${err instanceof Error ? err.message : String(err)}
33945
+ `);
33946
+ renderInfo("Setting peer endpoint anyway \u2014 nexus may connect later.");
33947
+ }
33948
+ const peerUrl = `peer://${peerId}`;
33949
+ ctx.setEndpoint(peerUrl, "nexus", authKey);
33950
+ const endpointSettings = { backendUrl: peerUrl, backendType: "nexus", ...authKey ? { apiKey: authKey } : {} };
33951
+ if (local) {
33952
+ ctx.saveLocalSettings(endpointSettings);
33953
+ } else {
33954
+ setConfigValue("backendUrl", peerUrl);
33955
+ setConfigValue("backendType", "nexus");
33956
+ if (authKey)
33957
+ setConfigValue("apiKey", authKey);
33958
+ ctx.saveSettings(endpointSettings);
33959
+ }
33960
+ recordUsage("endpoint", peerUrl, {
33961
+ repoRoot: local ? ctx.repoRoot : void 0,
33962
+ local,
33963
+ meta: {
33964
+ provider: "libp2p-peer",
33965
+ backendType: "nexus",
33966
+ peerId,
33967
+ ...authKey ? { authHint: authKey.slice(0, 4) + "..." } : {}
33968
+ }
33969
+ });
33970
+ process.stdout.write(`
33971
+ ${c2.green("\u2714")} Endpoint set to libp2p peer${local ? " (project-local)" : ""}:
33972
+ `);
33973
+ process.stdout.write(` ${c2.cyan("Transport".padEnd(12))} libp2p (DHT + mDNS + NATS relay)
33974
+ `);
33975
+ process.stdout.write(` ${c2.cyan("Peer ID".padEnd(12))} ${peerId}
33976
+ `);
33977
+ if (authKey) {
33978
+ process.stdout.write(` ${c2.cyan("Auth".padEnd(12))} ${authKey.slice(0, 8)}...
33979
+ `);
33980
+ }
33981
+ process.stdout.write(`
33982
+ ${c2.dim("Inference requests will be routed via nexus P2P network.")}
33983
+
33984
+ `);
33985
+ try {
33986
+ const models = await fetchModels(peerUrl, authKey);
33987
+ if (models.length > 0) {
33988
+ process.stdout.write(` ${c2.green("\u2714")} Discovered ${models.length} model(s) on peer:
33989
+ `);
33990
+ for (const m of models.slice(0, 10)) {
33991
+ process.stdout.write(` ${c2.dim("\u2022")} ${m.name}${m.parameterSize ? c2.dim(` (${m.parameterSize})`) : ""}
33992
+ `);
33993
+ }
33994
+ if (models.length > 10) {
33995
+ process.stdout.write(` ${c2.dim(`... and ${models.length - 10} more`)}
33996
+ `);
33997
+ }
33998
+ process.stdout.write("\n");
33999
+ const currentModel = ctx.config.model;
34000
+ const found = findModel(models, currentModel);
34001
+ if (!found && models.length > 0) {
34002
+ const autoModel = models[0].name;
34003
+ ctx.setModel(autoModel);
34004
+ if (local) {
34005
+ ctx.saveLocalSettings({ model: autoModel });
34006
+ } else {
34007
+ ctx.saveSettings({ model: autoModel });
34008
+ }
34009
+ renderWarning(`Model "${currentModel}" not found on peer.`);
34010
+ renderModelSwitch(currentModel, autoModel);
34011
+ } else if (found) {
34012
+ process.stdout.write(` ${c2.green("\u2714")} Model "${currentModel}" available on peer.
34013
+
34014
+ `);
34015
+ }
34016
+ } else {
34017
+ renderWarning("No models discovered on peer. The peer may not have exposed yet, or discovery needs time.");
34018
+ renderInfo("Use /models to try again, or just send a prompt \u2014 inference will attempt to reach the peer directly.");
34019
+ }
34020
+ } catch {
34021
+ renderWarning("Could not discover models on peer. Use /models to try again later.");
34022
+ }
34023
+ ctx.refreshModelCache?.();
34024
+ if (ctx.hasActiveTask?.()) {
34025
+ ctx.abortActiveTask?.();
34026
+ renderWarning("Active task aborted \u2014 endpoint changed. Send a new prompt to continue.");
34027
+ }
34028
+ }
32744
34029
  async function handleUpdate(subcommand, ctx) {
32745
34030
  const repoRoot = ctx.repoRoot;
32746
34031
  if (subcommand === "auto") {
@@ -32761,17 +34046,17 @@ async function handleUpdate(subcommand, ctx) {
32761
34046
  try {
32762
34047
  const { createRequire: createRequire4 } = await import("node:module");
32763
34048
  const { fileURLToPath: fileURLToPath14 } = await import("node:url");
32764
- const { dirname: dirname19, join: join55 } = await import("node:path");
32765
- const { existsSync: existsSync41 } = await import("node:fs");
34049
+ const { dirname: dirname20, join: join55 } = await import("node:path");
34050
+ const { existsSync: existsSync42 } = await import("node:fs");
32766
34051
  const req = createRequire4(import.meta.url);
32767
- const thisDir = dirname19(fileURLToPath14(import.meta.url));
34052
+ const thisDir = dirname20(fileURLToPath14(import.meta.url));
32768
34053
  const candidates = [
32769
34054
  join55(thisDir, "..", "package.json"),
32770
34055
  join55(thisDir, "..", "..", "package.json"),
32771
34056
  join55(thisDir, "..", "..", "..", "package.json")
32772
34057
  ];
32773
34058
  for (const pkgPath of candidates) {
32774
- if (existsSync41(pkgPath)) {
34059
+ if (existsSync42(pkgPath)) {
32775
34060
  const pkg = req(pkgPath);
32776
34061
  if (pkg.name === "open-agents-ai" || pkg.name === "@open-agents/cli") {
32777
34062
  currentVersion = pkg.version ?? "0.0.0";
@@ -33056,7 +34341,7 @@ var init_commands = __esm({
33056
34341
  });
33057
34342
 
33058
34343
  // packages/cli/dist/tui/project-context.js
33059
- import { existsSync as existsSync31, readFileSync as readFileSync22, readdirSync as readdirSync9 } from "node:fs";
34344
+ import { existsSync as existsSync32, readFileSync as readFileSync23, readdirSync as readdirSync9 } from "node:fs";
33060
34345
  import { join as join41, basename as basename10 } from "node:path";
33061
34346
  import { execSync as execSync26 } from "node:child_process";
33062
34347
  import { homedir as homedir11, platform as platform2, release } from "node:os";
@@ -33093,9 +34378,9 @@ function loadProjectMap(repoRoot) {
33093
34378
  initOaDirectory(repoRoot);
33094
34379
  }
33095
34380
  const mapPath = join41(repoRoot, OA_DIR, "context", "project-map.md");
33096
- if (existsSync31(mapPath)) {
34381
+ if (existsSync32(mapPath)) {
33097
34382
  try {
33098
- const content = readFileSync22(mapPath, "utf-8");
34383
+ const content = readFileSync23(mapPath, "utf-8");
33099
34384
  return content;
33100
34385
  } catch {
33101
34386
  }
@@ -33141,7 +34426,7 @@ function loadMemoryContext(repoRoot) {
33141
34426
  if (oaEntries)
33142
34427
  sections.push(oaEntries);
33143
34428
  const legacyMemDir = join41(repoRoot, ".open-agents", "memory");
33144
- if (legacyMemDir !== oaMemDir && existsSync31(legacyMemDir)) {
34429
+ if (legacyMemDir !== oaMemDir && existsSync32(legacyMemDir)) {
33145
34430
  const legacyEntries = loadMemoryDir(legacyMemDir, "project/legacy");
33146
34431
  if (legacyEntries)
33147
34432
  sections.push(legacyEntries);
@@ -33153,14 +34438,14 @@ function loadMemoryContext(repoRoot) {
33153
34438
  return sections.join("\n\n");
33154
34439
  }
33155
34440
  function loadMemoryDir(memDir, scope) {
33156
- if (!existsSync31(memDir))
34441
+ if (!existsSync32(memDir))
33157
34442
  return "";
33158
34443
  const lines = [];
33159
34444
  try {
33160
34445
  const files = readdirSync9(memDir).filter((f) => f.endsWith(".json"));
33161
34446
  for (const file of files.slice(0, 10)) {
33162
34447
  try {
33163
- const raw = readFileSync22(join41(memDir, file), "utf-8");
34448
+ const raw = readFileSync23(join41(memDir, file), "utf-8");
33164
34449
  const entries = JSON.parse(raw);
33165
34450
  const topic = basename10(file, ".json");
33166
34451
  const keys = Object.keys(entries);
@@ -34183,22 +35468,22 @@ var init_carousel = __esm({
34183
35468
  });
34184
35469
 
34185
35470
  // packages/cli/dist/tui/carousel-descriptors.js
34186
- import { existsSync as existsSync32, readFileSync as readFileSync23, writeFileSync as writeFileSync12, mkdirSync as mkdirSync12, readdirSync as readdirSync10 } from "node:fs";
35471
+ import { existsSync as existsSync33, readFileSync as readFileSync24, writeFileSync as writeFileSync13, mkdirSync as mkdirSync13, readdirSync as readdirSync10 } from "node:fs";
34187
35472
  import { join as join42, basename as basename11 } from "node:path";
34188
35473
  function loadToolProfile(repoRoot) {
34189
35474
  const filePath = join42(repoRoot, OA_DIR, "context", TOOL_PROFILE_FILE);
34190
35475
  try {
34191
- if (!existsSync32(filePath))
35476
+ if (!existsSync33(filePath))
34192
35477
  return null;
34193
- return JSON.parse(readFileSync23(filePath, "utf-8"));
35478
+ return JSON.parse(readFileSync24(filePath, "utf-8"));
34194
35479
  } catch {
34195
35480
  return null;
34196
35481
  }
34197
35482
  }
34198
35483
  function saveToolProfile(repoRoot, profile) {
34199
35484
  const contextDir = join42(repoRoot, OA_DIR, "context");
34200
- mkdirSync12(contextDir, { recursive: true });
34201
- writeFileSync12(join42(contextDir, TOOL_PROFILE_FILE), JSON.stringify(profile, null, 2), "utf-8");
35485
+ mkdirSync13(contextDir, { recursive: true });
35486
+ writeFileSync13(join42(contextDir, TOOL_PROFILE_FILE), JSON.stringify(profile, null, 2), "utf-8");
34202
35487
  }
34203
35488
  function categorizeToolCall(toolName) {
34204
35489
  for (const cat of TOOL_CATEGORIES) {
@@ -34258,9 +35543,9 @@ function weightedColor(profile) {
34258
35543
  function loadCachedDescriptors(repoRoot) {
34259
35544
  const filePath = join42(repoRoot, OA_DIR, "context", DESCRIPTOR_FILE);
34260
35545
  try {
34261
- if (!existsSync32(filePath))
35546
+ if (!existsSync33(filePath))
34262
35547
  return null;
34263
- const cached = JSON.parse(readFileSync23(filePath, "utf-8"));
35548
+ const cached = JSON.parse(readFileSync24(filePath, "utf-8"));
34264
35549
  return cached.phrases.length > 0 ? cached.phrases : null;
34265
35550
  } catch {
34266
35551
  return null;
@@ -34268,13 +35553,13 @@ function loadCachedDescriptors(repoRoot) {
34268
35553
  }
34269
35554
  function saveCachedDescriptors(repoRoot, phrases, sourceHash) {
34270
35555
  const contextDir = join42(repoRoot, OA_DIR, "context");
34271
- mkdirSync12(contextDir, { recursive: true });
35556
+ mkdirSync13(contextDir, { recursive: true });
34272
35557
  const cached = {
34273
35558
  phrases,
34274
35559
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
34275
35560
  sourceHash
34276
35561
  };
34277
- writeFileSync12(join42(contextDir, DESCRIPTOR_FILE), JSON.stringify(cached, null, 2), "utf-8");
35562
+ writeFileSync13(join42(contextDir, DESCRIPTOR_FILE), JSON.stringify(cached, null, 2), "utf-8");
34278
35563
  }
34279
35564
  function generateDescriptors(repoRoot) {
34280
35565
  const profile = loadToolProfile(repoRoot);
@@ -34324,9 +35609,9 @@ function generateDescriptors(repoRoot) {
34324
35609
  function extractFromPackageJson(repoRoot, tags) {
34325
35610
  const pkgPath = join42(repoRoot, "package.json");
34326
35611
  try {
34327
- if (!existsSync32(pkgPath))
35612
+ if (!existsSync33(pkgPath))
34328
35613
  return;
34329
- const pkg = JSON.parse(readFileSync23(pkgPath, "utf-8"));
35614
+ const pkg = JSON.parse(readFileSync24(pkgPath, "utf-8"));
34330
35615
  if (pkg.name && typeof pkg.name === "string") {
34331
35616
  const parts = pkg.name.replace(/^@/, "").split("/");
34332
35617
  for (const p of parts)
@@ -34370,7 +35655,7 @@ function extractFromManifests(repoRoot, tags) {
34370
35655
  { file: ".github/workflows", tag: "ci/cd" }
34371
35656
  ];
34372
35657
  for (const check of manifestChecks) {
34373
- if (existsSync32(join42(repoRoot, check.file))) {
35658
+ if (existsSync33(join42(repoRoot, check.file))) {
34374
35659
  tags.push(check.tag);
34375
35660
  }
34376
35661
  }
@@ -34394,14 +35679,14 @@ function extractFromSessions(repoRoot, tags) {
34394
35679
  function extractFromMemory(repoRoot, tags) {
34395
35680
  const memoryDir = join42(repoRoot, OA_DIR, "memory");
34396
35681
  try {
34397
- if (!existsSync32(memoryDir))
35682
+ if (!existsSync33(memoryDir))
34398
35683
  return;
34399
35684
  const files = readdirSync10(memoryDir).filter((f) => f.endsWith(".json"));
34400
35685
  for (const file of files) {
34401
35686
  const topic = file.replace(/\.json$/, "").replace(/[-_]/g, " ");
34402
35687
  tags.push(topic);
34403
35688
  try {
34404
- const data = JSON.parse(readFileSync23(join42(memoryDir, file), "utf-8"));
35689
+ const data = JSON.parse(readFileSync24(join42(memoryDir, file), "utf-8"));
34405
35690
  if (data && typeof data === "object") {
34406
35691
  const keys = Object.keys(data).slice(0, 3);
34407
35692
  for (const key of keys) {
@@ -34536,7 +35821,7 @@ var init_carousel_descriptors = __esm({
34536
35821
  });
34537
35822
 
34538
35823
  // packages/cli/dist/tui/voice.js
34539
- import { existsSync as existsSync33, mkdirSync as mkdirSync13, writeFileSync as writeFileSync13, readFileSync as readFileSync24, unlinkSync as unlinkSync5 } from "node:fs";
35824
+ import { existsSync as existsSync34, mkdirSync as mkdirSync14, writeFileSync as writeFileSync14, readFileSync as readFileSync25, unlinkSync as unlinkSync5 } from "node:fs";
34540
35825
  import { join as join43 } from "node:path";
34541
35826
  import { homedir as homedir12, tmpdir as tmpdir6, platform as platform3 } from "node:os";
34542
35827
  import { execSync as execSync27, spawn as nodeSpawn } from "node:child_process";
@@ -35546,7 +36831,7 @@ var init_voice = __esm({
35546
36831
  return buffer;
35547
36832
  }
35548
36833
  writeWav(samples, sampleRate, path) {
35549
- writeFileSync13(path, this.buildWavBuffer(samples, sampleRate));
36834
+ writeFileSync14(path, this.buildWavBuffer(samples, sampleRate));
35550
36835
  }
35551
36836
  // -------------------------------------------------------------------------
35552
36837
  // Audio playback (system default speakers)
@@ -35722,11 +37007,11 @@ Error: ${err2 instanceof Error ? err2.message : String(err2)}`);
35722
37007
  return;
35723
37008
  }
35724
37009
  }
35725
- if (!existsSync33(wavPath))
37010
+ if (!existsSync34(wavPath))
35726
37011
  return;
35727
37012
  if (volume !== 1) {
35728
37013
  try {
35729
- const wavData = readFileSync24(wavPath);
37014
+ const wavData = readFileSync25(wavPath);
35730
37015
  if (wavData.length > 44) {
35731
37016
  const header = wavData.subarray(0, 44);
35732
37017
  const samples = new Int16Array(wavData.buffer, wavData.byteOffset + 44, (wavData.length - 44) / 2);
@@ -35734,14 +37019,14 @@ Error: ${err2 instanceof Error ? err2.message : String(err2)}`);
35734
37019
  samples[i] = Math.round(samples[i] * volume);
35735
37020
  }
35736
37021
  const scaled = Buffer.concat([header, Buffer.from(samples.buffer, samples.byteOffset, samples.byteLength)]);
35737
- writeFileSync13(wavPath, scaled);
37022
+ writeFileSync14(wavPath, scaled);
35738
37023
  }
35739
37024
  } catch {
35740
37025
  }
35741
37026
  }
35742
37027
  if (this.onPCMOutput) {
35743
37028
  try {
35744
- const wavData = readFileSync24(wavPath);
37029
+ const wavData = readFileSync25(wavPath);
35745
37030
  if (wavData.length > 44) {
35746
37031
  const pcm = Buffer.from(wavData.buffer, wavData.byteOffset + 44, wavData.length - 44);
35747
37032
  const sampleRate = wavData.readUInt32LE(24);
@@ -35790,10 +37075,10 @@ Error: ${err2 instanceof Error ? err2.message : String(err2)}`);
35790
37075
  return null;
35791
37076
  }
35792
37077
  }
35793
- if (!existsSync33(wavPath))
37078
+ if (!existsSync34(wavPath))
35794
37079
  return null;
35795
37080
  try {
35796
- const data = readFileSync24(wavPath);
37081
+ const data = readFileSync25(wavPath);
35797
37082
  unlinkSync5(wavPath);
35798
37083
  return data;
35799
37084
  } catch {
@@ -35807,24 +37092,24 @@ Error: ${err2 instanceof Error ? err2.message : String(err2)}`);
35807
37092
  if (this.ort)
35808
37093
  return;
35809
37094
  const arch = process.arch;
35810
- mkdirSync13(voiceDir(), { recursive: true });
37095
+ mkdirSync14(voiceDir(), { recursive: true });
35811
37096
  const pkgPath = join43(voiceDir(), "package.json");
35812
37097
  const expectedDeps = {
35813
37098
  "onnxruntime-node": "^1.21.0",
35814
37099
  "phonemizer": "^1.2.1"
35815
37100
  };
35816
- if (existsSync33(pkgPath)) {
37101
+ if (existsSync34(pkgPath)) {
35817
37102
  try {
35818
- const existing = JSON.parse(readFileSync24(pkgPath, "utf8"));
37103
+ const existing = JSON.parse(readFileSync25(pkgPath, "utf8"));
35819
37104
  if (!existing.dependencies?.["phonemizer"]) {
35820
37105
  existing.dependencies = { ...existing.dependencies, ...expectedDeps };
35821
- writeFileSync13(pkgPath, JSON.stringify(existing, null, 2));
37106
+ writeFileSync14(pkgPath, JSON.stringify(existing, null, 2));
35822
37107
  }
35823
37108
  } catch {
35824
37109
  }
35825
37110
  }
35826
- if (!existsSync33(pkgPath)) {
35827
- writeFileSync13(pkgPath, JSON.stringify({
37111
+ if (!existsSync34(pkgPath)) {
37112
+ writeFileSync14(pkgPath, JSON.stringify({
35828
37113
  name: "open-agents-voice",
35829
37114
  private: true,
35830
37115
  dependencies: expectedDeps
@@ -35841,7 +37126,7 @@ Error: ${err2 instanceof Error ? err2.message : String(err2)}`);
35841
37126
  }
35842
37127
  };
35843
37128
  const onnxNodeModules = join43(voiceDir(), "node_modules", "onnxruntime-node");
35844
- const onnxInstalled = existsSync33(onnxNodeModules);
37129
+ const onnxInstalled = existsSync34(onnxNodeModules);
35845
37130
  if (onnxInstalled && !probeOnnx()) {
35846
37131
  throw new Error(`Voice synthesis unavailable: ONNX runtime crashes on this CPU (${process.platform}-${arch}). This is a known issue with some ARM SoCs where the CPU vendor is not recognized. Voice feedback will be disabled but all other features work normally.`);
35847
37132
  }
@@ -35899,18 +37184,18 @@ Error: ${err instanceof Error ? err.message : String(err)}`);
35899
37184
  const dir = modelDir(id);
35900
37185
  const onnxPath = modelOnnxPath(id);
35901
37186
  const configPath = modelConfigPath(id);
35902
- if (existsSync33(onnxPath) && existsSync33(configPath))
37187
+ if (existsSync34(onnxPath) && existsSync34(configPath))
35903
37188
  return;
35904
- mkdirSync13(dir, { recursive: true });
35905
- if (!existsSync33(configPath)) {
37189
+ mkdirSync14(dir, { recursive: true });
37190
+ if (!existsSync34(configPath)) {
35906
37191
  renderInfo(`Downloading ${model.label} voice config...`);
35907
37192
  const configResp = await fetch(model.configUrl);
35908
37193
  if (!configResp.ok)
35909
37194
  throw new Error(`Failed to download config: HTTP ${configResp.status}`);
35910
37195
  const configText = await configResp.text();
35911
- writeFileSync13(configPath, configText);
37196
+ writeFileSync14(configPath, configText);
35912
37197
  }
35913
- if (!existsSync33(onnxPath)) {
37198
+ if (!existsSync34(onnxPath)) {
35914
37199
  renderInfo(`Downloading ${model.label} voice model (this may take a minute)...`);
35915
37200
  const onnxResp = await fetch(model.onnxUrl);
35916
37201
  if (!onnxResp.ok)
@@ -35935,7 +37220,7 @@ Error: ${err instanceof Error ? err.message : String(err)}`);
35935
37220
  }
35936
37221
  }
35937
37222
  const fullBuffer = Buffer.concat(chunks);
35938
- writeFileSync13(onnxPath, fullBuffer);
37223
+ writeFileSync14(onnxPath, fullBuffer);
35939
37224
  renderInfo(`${model.label} model downloaded (${formatBytes2(fullBuffer.length)}).`);
35940
37225
  }
35941
37226
  }
@@ -35947,10 +37232,10 @@ Error: ${err instanceof Error ? err.message : String(err)}`);
35947
37232
  throw new Error("ONNX runtime not loaded");
35948
37233
  const onnxPath = modelOnnxPath(this.modelId);
35949
37234
  const configPath = modelConfigPath(this.modelId);
35950
- if (!existsSync33(onnxPath) || !existsSync33(configPath)) {
37235
+ if (!existsSync34(onnxPath) || !existsSync34(configPath)) {
35951
37236
  throw new Error(`Model files not found for ${this.modelId}`);
35952
37237
  }
35953
- this.config = JSON.parse(readFileSync24(configPath, "utf8"));
37238
+ this.config = JSON.parse(readFileSync25(configPath, "utf8"));
35954
37239
  this.session = await this.ort.InferenceSession.create(onnxPath, {
35955
37240
  executionProviders: ["cpu"],
35956
37241
  graphOptimizationLevel: "all"
@@ -36811,13 +38096,13 @@ var init_stream_renderer = __esm({
36811
38096
  });
36812
38097
 
36813
38098
  // packages/cli/dist/tui/edit-history.js
36814
- import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync14 } from "node:fs";
38099
+ import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync15 } from "node:fs";
36815
38100
  import { join as join44 } from "node:path";
36816
38101
  function createEditHistoryLogger(repoRoot, sessionId) {
36817
38102
  const historyDir = join44(repoRoot, ".oa", "history");
36818
38103
  const logPath = join44(historyDir, "edits.jsonl");
36819
38104
  try {
36820
- mkdirSync14(historyDir, { recursive: true });
38105
+ mkdirSync15(historyDir, { recursive: true });
36821
38106
  } catch {
36822
38107
  }
36823
38108
  function logToolCall(toolName, toolArgs, success) {
@@ -36926,17 +38211,17 @@ var init_edit_history = __esm({
36926
38211
  });
36927
38212
 
36928
38213
  // packages/cli/dist/tui/promptLoader.js
36929
- import { readFileSync as readFileSync25, existsSync as existsSync34 } from "node:fs";
36930
- import { join as join45, dirname as dirname16 } from "node:path";
38214
+ import { readFileSync as readFileSync26, existsSync as existsSync35 } from "node:fs";
38215
+ import { join as join45, dirname as dirname17 } from "node:path";
36931
38216
  import { fileURLToPath as fileURLToPath11 } from "node:url";
36932
38217
  function loadPrompt3(promptPath, vars) {
36933
38218
  let content = cache3.get(promptPath);
36934
38219
  if (content === void 0) {
36935
38220
  const fullPath = join45(PROMPTS_DIR3, promptPath);
36936
- if (!existsSync34(fullPath)) {
38221
+ if (!existsSync35(fullPath)) {
36937
38222
  throw new Error(`Prompt file not found: ${fullPath}`);
36938
38223
  }
36939
- content = readFileSync25(fullPath, "utf-8");
38224
+ content = readFileSync26(fullPath, "utf-8");
36940
38225
  cache3.set(promptPath, content);
36941
38226
  }
36942
38227
  if (!vars)
@@ -36948,24 +38233,24 @@ var init_promptLoader3 = __esm({
36948
38233
  "packages/cli/dist/tui/promptLoader.js"() {
36949
38234
  "use strict";
36950
38235
  __filename3 = fileURLToPath11(import.meta.url);
36951
- __dirname6 = dirname16(__filename3);
38236
+ __dirname6 = dirname17(__filename3);
36952
38237
  devPath2 = join45(__dirname6, "..", "..", "prompts");
36953
38238
  publishedPath2 = join45(__dirname6, "..", "prompts");
36954
- PROMPTS_DIR3 = existsSync34(devPath2) ? devPath2 : publishedPath2;
38239
+ PROMPTS_DIR3 = existsSync35(devPath2) ? devPath2 : publishedPath2;
36955
38240
  cache3 = /* @__PURE__ */ new Map();
36956
38241
  }
36957
38242
  });
36958
38243
 
36959
38244
  // packages/cli/dist/tui/dream-engine.js
36960
- import { mkdirSync as mkdirSync15, writeFileSync as writeFileSync14, readFileSync as readFileSync26, existsSync as existsSync35, cpSync, rmSync, readdirSync as readdirSync11 } from "node:fs";
38245
+ import { mkdirSync as mkdirSync16, writeFileSync as writeFileSync15, readFileSync as readFileSync27, existsSync as existsSync36, cpSync, rmSync, readdirSync as readdirSync11 } from "node:fs";
36961
38246
  import { join as join46, basename as basename12 } from "node:path";
36962
38247
  import { execSync as execSync28 } from "node:child_process";
36963
38248
  function loadAutoresearchMemory(repoRoot) {
36964
38249
  const memoryPath = join46(repoRoot, ".oa", "memory", "autoresearch.json");
36965
- if (!existsSync35(memoryPath))
38250
+ if (!existsSync36(memoryPath))
36966
38251
  return "";
36967
38252
  try {
36968
- const raw = readFileSync26(memoryPath, "utf-8");
38253
+ const raw = readFileSync27(memoryPath, "utf-8");
36969
38254
  const data = JSON.parse(raw);
36970
38255
  const sections = [];
36971
38256
  for (const key of AUTORESEARCH_MEMORY_KEYS) {
@@ -37161,8 +38446,8 @@ var init_dream_engine = __esm({
37161
38446
  }
37162
38447
  try {
37163
38448
  const dir = join46(targetPath, "..");
37164
- mkdirSync15(dir, { recursive: true });
37165
- writeFileSync14(targetPath, content, "utf-8");
38449
+ mkdirSync16(dir, { recursive: true });
38450
+ writeFileSync15(targetPath, content, "utf-8");
37166
38451
  return { success: true, output: `Wrote ${content.length} bytes to ${rawPath}`, durationMs: Date.now() - start };
37167
38452
  } catch (err) {
37168
38453
  return { success: false, output: "", error: String(err), durationMs: Date.now() - start };
@@ -37195,15 +38480,15 @@ var init_dream_engine = __esm({
37195
38480
  return { success: false, output: "", error: "Autoresearch mode: edits are confined to .oa/autoresearch/", durationMs: Date.now() - start };
37196
38481
  }
37197
38482
  try {
37198
- if (!existsSync35(targetPath)) {
38483
+ if (!existsSync36(targetPath)) {
37199
38484
  return { success: false, output: "", error: `File not found: ${rawPath}`, durationMs: Date.now() - start };
37200
38485
  }
37201
- let content = readFileSync26(targetPath, "utf-8");
38486
+ let content = readFileSync27(targetPath, "utf-8");
37202
38487
  if (!content.includes(oldStr)) {
37203
38488
  return { success: false, output: "", error: "old_string not found in file", durationMs: Date.now() - start };
37204
38489
  }
37205
38490
  content = content.replace(oldStr, newStr);
37206
- writeFileSync14(targetPath, content, "utf-8");
38491
+ writeFileSync15(targetPath, content, "utf-8");
37207
38492
  return { success: true, output: `Edited ${rawPath}`, durationMs: Date.now() - start };
37208
38493
  } catch (err) {
37209
38494
  return { success: false, output: "", error: String(err), durationMs: Date.now() - start };
@@ -37250,8 +38535,8 @@ var init_dream_engine = __esm({
37250
38535
  }
37251
38536
  try {
37252
38537
  const dir = join46(targetPath, "..");
37253
- mkdirSync15(dir, { recursive: true });
37254
- writeFileSync14(targetPath, content, "utf-8");
38538
+ mkdirSync16(dir, { recursive: true });
38539
+ writeFileSync15(targetPath, content, "utf-8");
37255
38540
  return { success: true, output: `Wrote ${content.length} bytes to ${rawPath}`, durationMs: Date.now() - start };
37256
38541
  } catch (err) {
37257
38542
  return { success: false, output: "", error: String(err), durationMs: Date.now() - start };
@@ -37284,15 +38569,15 @@ var init_dream_engine = __esm({
37284
38569
  return { success: false, output: "", error: "Dream mode: edits are confined to .oa/dreams/", durationMs: Date.now() - start };
37285
38570
  }
37286
38571
  try {
37287
- if (!existsSync35(targetPath)) {
38572
+ if (!existsSync36(targetPath)) {
37288
38573
  return { success: false, output: "", error: `File not found: ${rawPath}`, durationMs: Date.now() - start };
37289
38574
  }
37290
- let content = readFileSync26(targetPath, "utf-8");
38575
+ let content = readFileSync27(targetPath, "utf-8");
37291
38576
  if (!content.includes(oldStr)) {
37292
38577
  return { success: false, output: "", error: "old_string not found in file", durationMs: Date.now() - start };
37293
38578
  }
37294
38579
  content = content.replace(oldStr, newStr);
37295
- writeFileSync14(targetPath, content, "utf-8");
38580
+ writeFileSync15(targetPath, content, "utf-8");
37296
38581
  return { success: true, output: `Edited ${rawPath}`, durationMs: Date.now() - start };
37297
38582
  } catch (err) {
37298
38583
  return { success: false, output: "", error: String(err), durationMs: Date.now() - start };
@@ -37377,7 +38662,7 @@ var init_dream_engine = __esm({
37377
38662
  startedAt: (/* @__PURE__ */ new Date()).toISOString(),
37378
38663
  results: []
37379
38664
  };
37380
- mkdirSync15(this.dreamsDir, { recursive: true });
38665
+ mkdirSync16(this.dreamsDir, { recursive: true });
37381
38666
  this.saveDreamState();
37382
38667
  try {
37383
38668
  for (let cycle = 1; cycle <= totalCycles; cycle++) {
@@ -37431,7 +38716,7 @@ ${result.summary}`;
37431
38716
  renderDreamContraction(cycle);
37432
38717
  const cycleSummary = this.buildCycleSummary(cycle, previousFindings);
37433
38718
  const summaryPath = join46(this.dreamsDir, `cycle-${cycle}-summary.md`);
37434
- writeFileSync14(summaryPath, cycleSummary, "utf-8");
38719
+ writeFileSync15(summaryPath, cycleSummary, "utf-8");
37435
38720
  }
37436
38721
  if (mode === "lucid" && !this.abortController.signal.aborted) {
37437
38722
  this.saveVersionCheckpoint(cycle);
@@ -38029,8 +39314,8 @@ ${summaryResult}
38029
39314
  *Generated by open-agents autoresearch swarm*
38030
39315
  `;
38031
39316
  try {
38032
- mkdirSync15(this.dreamsDir, { recursive: true });
38033
- writeFileSync14(reportPath, report, "utf-8");
39317
+ mkdirSync16(this.dreamsDir, { recursive: true });
39318
+ writeFileSync15(reportPath, report, "utf-8");
38034
39319
  } catch {
38035
39320
  }
38036
39321
  renderSwarmComplete(workspace);
@@ -38098,7 +39383,7 @@ ${summaryResult}
38098
39383
  saveVersionCheckpoint(cycle) {
38099
39384
  const checkpointDir = join46(this.dreamsDir, "checkpoints", `cycle-${cycle}`);
38100
39385
  try {
38101
- mkdirSync15(checkpointDir, { recursive: true });
39386
+ mkdirSync16(checkpointDir, { recursive: true });
38102
39387
  try {
38103
39388
  const gitStatus = execSync28("git status --porcelain", {
38104
39389
  cwd: this.repoRoot,
@@ -38115,10 +39400,10 @@ ${summaryResult}
38115
39400
  encoding: "utf-8",
38116
39401
  timeout: 5e3
38117
39402
  }).trim();
38118
- writeFileSync14(join46(checkpointDir, "git-status.txt"), gitStatus, "utf-8");
38119
- writeFileSync14(join46(checkpointDir, "git-diff.patch"), gitDiff, "utf-8");
38120
- writeFileSync14(join46(checkpointDir, "git-hash.txt"), gitHash, "utf-8");
38121
- writeFileSync14(join46(checkpointDir, "checkpoint.json"), JSON.stringify({
39403
+ writeFileSync15(join46(checkpointDir, "git-status.txt"), gitStatus, "utf-8");
39404
+ writeFileSync15(join46(checkpointDir, "git-diff.patch"), gitDiff, "utf-8");
39405
+ writeFileSync15(join46(checkpointDir, "git-hash.txt"), gitHash, "utf-8");
39406
+ writeFileSync15(join46(checkpointDir, "checkpoint.json"), JSON.stringify({
38122
39407
  cycle,
38123
39408
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
38124
39409
  gitHash,
@@ -38126,7 +39411,7 @@ ${summaryResult}
38126
39411
  }, null, 2), "utf-8");
38127
39412
  renderInfo(`Checkpoint saved: cycle ${cycle} (${gitHash.slice(0, 8)})`);
38128
39413
  } catch {
38129
- writeFileSync14(join46(checkpointDir, "checkpoint.json"), JSON.stringify({ cycle, timestamp: (/* @__PURE__ */ new Date()).toISOString(), mode: this.state.mode }, null, 2), "utf-8");
39414
+ writeFileSync15(join46(checkpointDir, "checkpoint.json"), JSON.stringify({ cycle, timestamp: (/* @__PURE__ */ new Date()).toISOString(), mode: this.state.mode }, null, 2), "utf-8");
38130
39415
  renderInfo(`Checkpoint saved: cycle ${cycle} (no git)`);
38131
39416
  }
38132
39417
  } catch (err) {
@@ -38184,14 +39469,14 @@ ${files.map((f) => `- [\`${f}\`](./${f})`).join("\n")}
38184
39469
  ---
38185
39470
  *Auto-generated by open-agents dream engine*
38186
39471
  `;
38187
- writeFileSync14(join46(this.dreamsDir, "PROPOSAL-INDEX.md"), index, "utf-8");
39472
+ writeFileSync15(join46(this.dreamsDir, "PROPOSAL-INDEX.md"), index, "utf-8");
38188
39473
  } catch {
38189
39474
  }
38190
39475
  }
38191
39476
  /** Save dream state for resume/inspection */
38192
39477
  saveDreamState() {
38193
39478
  try {
38194
- writeFileSync14(join46(this.dreamsDir, "dream-state.json"), JSON.stringify(this.state, null, 2) + "\n", "utf-8");
39479
+ writeFileSync15(join46(this.dreamsDir, "dream-state.json"), JSON.stringify(this.state, null, 2) + "\n", "utf-8");
38195
39480
  } catch {
38196
39481
  }
38197
39482
  }
@@ -38565,7 +39850,7 @@ var init_bless_engine = __esm({
38565
39850
  });
38566
39851
 
38567
39852
  // packages/cli/dist/tui/dmn-engine.js
38568
- import { existsSync as existsSync36, readFileSync as readFileSync27, writeFileSync as writeFileSync15, mkdirSync as mkdirSync16, readdirSync as readdirSync12, unlinkSync as unlinkSync6 } from "node:fs";
39853
+ import { existsSync as existsSync37, readFileSync as readFileSync28, writeFileSync as writeFileSync16, mkdirSync as mkdirSync17, readdirSync as readdirSync12, unlinkSync as unlinkSync6 } from "node:fs";
38569
39854
  import { join as join47, basename as basename13 } from "node:path";
38570
39855
  function buildDMNGatherPrompt(recentTaskSummaries, dueReminders, attentionItems, memoryTopics, capabilities, competence, reflectionBuffer) {
38571
39856
  const competenceReport = competence.length > 0 ? competence.map((c3) => {
@@ -38681,7 +39966,7 @@ var init_dmn_engine = __esm({
38681
39966
  this.repoRoot = repoRoot;
38682
39967
  this.stateDir = join47(repoRoot, ".oa", "dmn");
38683
39968
  this.historyDir = join47(repoRoot, ".oa", "dmn", "cycles");
38684
- mkdirSync16(this.historyDir, { recursive: true });
39969
+ mkdirSync17(this.historyDir, { recursive: true });
38685
39970
  this.loadState();
38686
39971
  }
38687
39972
  get stats() {
@@ -39274,7 +40559,7 @@ OUTPUT: Call task_complete with JSON:
39274
40559
  join47(this.repoRoot, ".open-agents", "memory")
39275
40560
  ];
39276
40561
  for (const dir of dirs) {
39277
- if (!existsSync36(dir))
40562
+ if (!existsSync37(dir))
39278
40563
  continue;
39279
40564
  try {
39280
40565
  const files = readdirSync12(dir).filter((f) => f.endsWith(".json"));
@@ -39291,23 +40576,23 @@ OUTPUT: Call task_complete with JSON:
39291
40576
  // ── State persistence ─────────────────────────────────────────────────
39292
40577
  loadState() {
39293
40578
  const path = join47(this.stateDir, "state.json");
39294
- if (existsSync36(path)) {
40579
+ if (existsSync37(path)) {
39295
40580
  try {
39296
- this.state = JSON.parse(readFileSync27(path, "utf-8"));
40581
+ this.state = JSON.parse(readFileSync28(path, "utf-8"));
39297
40582
  } catch {
39298
40583
  }
39299
40584
  }
39300
40585
  }
39301
40586
  saveState() {
39302
40587
  try {
39303
- writeFileSync15(join47(this.stateDir, "state.json"), JSON.stringify(this.state, null, 2) + "\n", "utf-8");
40588
+ writeFileSync16(join47(this.stateDir, "state.json"), JSON.stringify(this.state, null, 2) + "\n", "utf-8");
39304
40589
  } catch {
39305
40590
  }
39306
40591
  }
39307
40592
  saveCycleResult(result) {
39308
40593
  try {
39309
40594
  const filename = `cycle-${result.cycleNumber}-${Date.now()}.json`;
39310
- writeFileSync15(join47(this.historyDir, filename), JSON.stringify(result, null, 2) + "\n", "utf-8");
40595
+ writeFileSync16(join47(this.historyDir, filename), JSON.stringify(result, null, 2) + "\n", "utf-8");
39311
40596
  const files = readdirSync12(this.historyDir).filter((f) => f.startsWith("cycle-") && f.endsWith(".json")).sort();
39312
40597
  if (files.length > 50) {
39313
40598
  for (const old of files.slice(0, files.length - 50)) {
@@ -39325,7 +40610,7 @@ OUTPUT: Call task_complete with JSON:
39325
40610
  });
39326
40611
 
39327
40612
  // packages/cli/dist/tui/snr-engine.js
39328
- import { existsSync as existsSync37, readdirSync as readdirSync13, readFileSync as readFileSync28 } from "node:fs";
40613
+ import { existsSync as existsSync38, readdirSync as readdirSync13, readFileSync as readFileSync29 } from "node:fs";
39329
40614
  import { join as join48, basename as basename14 } from "node:path";
39330
40615
  function computeDPrime(signalScores, noiseScores) {
39331
40616
  if (signalScores.length === 0 || noiseScores.length === 0)
@@ -39570,7 +40855,7 @@ Call task_complete with the JSON array when done.`, onEvent)
39570
40855
  join48(this.repoRoot, ".open-agents", "memory")
39571
40856
  ];
39572
40857
  for (const dir of dirs) {
39573
- if (!existsSync37(dir))
40858
+ if (!existsSync38(dir))
39574
40859
  continue;
39575
40860
  try {
39576
40861
  const files = readdirSync13(dir).filter((f) => f.endsWith(".json"));
@@ -39579,7 +40864,7 @@ Call task_complete with the JSON array when done.`, onEvent)
39579
40864
  if (topics.length > 0 && !topics.includes(topic))
39580
40865
  continue;
39581
40866
  try {
39582
- const data = JSON.parse(readFileSync28(join48(dir, f), "utf-8"));
40867
+ const data = JSON.parse(readFileSync29(join48(dir, f), "utf-8"));
39583
40868
  for (const [key, val] of Object.entries(data)) {
39584
40869
  const value = typeof val === "object" && val !== null && "value" in val ? String(val.value) : String(val);
39585
40870
  entries.push({ topic, key, value });
@@ -40146,7 +41431,7 @@ var init_tool_policy = __esm({
40146
41431
  });
40147
41432
 
40148
41433
  // packages/cli/dist/tui/telegram-bridge.js
40149
- import { mkdirSync as mkdirSync17, existsSync as existsSync38, unlinkSync as unlinkSync7, readdirSync as readdirSync14, statSync as statSync10 } from "node:fs";
41434
+ import { mkdirSync as mkdirSync18, existsSync as existsSync39, unlinkSync as unlinkSync7, readdirSync as readdirSync14, statSync as statSync10 } from "node:fs";
40150
41435
  import { join as join49, resolve as resolve27 } from "node:path";
40151
41436
  import { writeFile as writeFileAsync } from "node:fs/promises";
40152
41437
  function convertMarkdownToTelegramHTML(md) {
@@ -40475,7 +41760,7 @@ with summary "no_reply" to silently skip without responding.
40475
41760
  this.polling = true;
40476
41761
  this.abortController = new AbortController();
40477
41762
  try {
40478
- mkdirSync17(this.mediaCacheDir, { recursive: true });
41763
+ mkdirSync18(this.mediaCacheDir, { recursive: true });
40479
41764
  } catch {
40480
41765
  }
40481
41766
  this.mediaCacheCleanupTimer = setInterval(() => this.cleanupMediaCache(), 5 * 60 * 1e3);
@@ -42012,6 +43297,56 @@ var init_status_bar = __esm({
42012
43297
  poll();
42013
43298
  this._remoteMetricsTimer = setInterval(poll, 1e4);
42014
43299
  }
43300
+ /**
43301
+ * Start polling a peer endpoint for metrics via nexus daemon.
43302
+ * Queries the remote peer's system_metrics capability for CPU/GPU data,
43303
+ * with fallback to local metering data.
43304
+ */
43305
+ startPeerMetricsPolling(sendCommand, peerId, authKey) {
43306
+ this.stopRemoteMetricsPolling();
43307
+ const poll = async () => {
43308
+ try {
43309
+ const queryData = { type: "query" };
43310
+ if (authKey)
43311
+ queryData.auth_key = authKey;
43312
+ const raw = await sendCommand("invoke_capability", {
43313
+ target_peer: peerId,
43314
+ capability: "system_metrics",
43315
+ data: JSON.stringify(queryData)
43316
+ }, 8e3);
43317
+ const result = JSON.parse(raw);
43318
+ if (result.ok !== false && result.result) {
43319
+ const data = typeof result.result === "string" ? JSON.parse(result.result) : result.result;
43320
+ this.setRemoteMetrics({
43321
+ cpuUtil: data.cpu?.utilization ?? 0,
43322
+ gpuUtil: data.gpu?.available ? data.gpu.utilization ?? 0 : -1,
43323
+ gpuName: data.gpu?.name ?? "",
43324
+ vramUtil: data.gpu?.available ? data.gpu.vramUtilization ?? 0 : -1,
43325
+ memUtil: data.memory?.utilization ?? 0
43326
+ });
43327
+ return;
43328
+ }
43329
+ } catch {
43330
+ }
43331
+ try {
43332
+ const raw = await sendCommand("metering_status", { peer_id: peerId }, 5e3);
43333
+ const result = JSON.parse(raw);
43334
+ if (result.ok !== false && result.output) {
43335
+ this.setRemoteMetrics({
43336
+ cpuUtil: -1,
43337
+ // -1 = unavailable (won't render bar)
43338
+ gpuUtil: -1,
43339
+ gpuName: "peer",
43340
+ vramUtil: -1,
43341
+ memUtil: -1
43342
+ });
43343
+ }
43344
+ } catch {
43345
+ }
43346
+ };
43347
+ poll();
43348
+ this._remoteMetricsTimer = setInterval(poll, 1e4);
43349
+ }
42015
43350
  /** Stop polling remote metrics */
42016
43351
  stopRemoteMetricsPolling() {
42017
43352
  if (this._remoteMetricsTimer) {
@@ -42745,11 +44080,11 @@ var init_status_bar = __esm({
42745
44080
  import * as readline2 from "node:readline";
42746
44081
  import { Writable } from "node:stream";
42747
44082
  import { cwd } from "node:process";
42748
- import { resolve as resolve28, join as join50, dirname as dirname17, extname as extname9 } from "node:path";
44083
+ import { resolve as resolve28, join as join50, dirname as dirname18, extname as extname9 } from "node:path";
42749
44084
  import { createRequire as createRequire2 } from "node:module";
42750
44085
  import { fileURLToPath as fileURLToPath12 } from "node:url";
42751
- import { readFileSync as readFileSync29, writeFileSync as writeFileSync16, appendFileSync as appendFileSync3, rmSync as rmSync2, readdirSync as readdirSync15, mkdirSync as mkdirSync18 } from "node:fs";
42752
- import { existsSync as existsSync39 } from "node:fs";
44086
+ import { readFileSync as readFileSync30, writeFileSync as writeFileSync17, appendFileSync as appendFileSync3, rmSync as rmSync2, readdirSync as readdirSync15, mkdirSync as mkdirSync19 } from "node:fs";
44087
+ import { existsSync as existsSync40 } from "node:fs";
42753
44088
  import { homedir as homedir13 } from "node:os";
42754
44089
  function formatTimeAgo(date) {
42755
44090
  const seconds = Math.floor((Date.now() - date.getTime()) / 1e3);
@@ -42767,14 +44102,14 @@ function formatTimeAgo(date) {
42767
44102
  function getVersion3() {
42768
44103
  try {
42769
44104
  const require2 = createRequire2(import.meta.url);
42770
- const thisDir = dirname17(fileURLToPath12(import.meta.url));
44105
+ const thisDir = dirname18(fileURLToPath12(import.meta.url));
42771
44106
  const candidates = [
42772
44107
  join50(thisDir, "..", "package.json"),
42773
44108
  join50(thisDir, "..", "..", "package.json"),
42774
44109
  join50(thisDir, "..", "..", "..", "package.json")
42775
44110
  ];
42776
44111
  for (const pkgPath of candidates) {
42777
- if (existsSync39(pkgPath)) {
44112
+ if (existsSync40(pkgPath)) {
42778
44113
  const pkg = require2(pkgPath);
42779
44114
  if (pkg.name === "open-agents-ai" || pkg.name === "@open-agents/cli") {
42780
44115
  return pkg.version ?? "0.0.0";
@@ -42917,7 +44252,8 @@ function createSubAgentTool(config, repoRoot, ctxWindowSize) {
42917
44252
  let backend;
42918
44253
  if (config.backendType === "nexus") {
42919
44254
  const nexusTool = new NexusTool(repoRoot);
42920
- backend = new NexusAgenticBackend(nexusTool.sendCommand.bind(nexusTool), config.model);
44255
+ const subTargetPeer = config.backendUrl.startsWith("peer://") ? config.backendUrl.slice(7) : void 0;
44256
+ backend = new NexusAgenticBackend(nexusTool.sendCommand.bind(nexusTool), config.model, subTargetPeer, config.apiKey);
42921
44257
  } else {
42922
44258
  backend = new OllamaAgenticBackend(config.backendUrl, config.model, config.apiKey);
42923
44259
  }
@@ -42984,11 +44320,11 @@ function gatherMemorySnippets(root) {
42984
44320
  join50(root, ".open-agents", "memory")
42985
44321
  ];
42986
44322
  for (const dir of dirs) {
42987
- if (!existsSync39(dir))
44323
+ if (!existsSync40(dir))
42988
44324
  continue;
42989
44325
  try {
42990
44326
  for (const f of readdirSync15(dir).filter((f2) => f2.endsWith(".json"))) {
42991
- const data = JSON.parse(readFileSync29(join50(dir, f), "utf-8"));
44327
+ const data = JSON.parse(readFileSync30(join50(dir, f), "utf-8"));
42992
44328
  for (const val of Object.values(data)) {
42993
44329
  const v = typeof val === "object" && val !== null && "value" in val ? String(val.value) : String(val);
42994
44330
  if (v.length > 10)
@@ -43094,10 +44430,58 @@ function startTask(task, config, repoRoot, voice, stream, taskStores, bruteForce
43094
44430
  if (config.backendType === "nexus") {
43095
44431
  const nexusTool = new NexusTool(repoRoot);
43096
44432
  const sendFn = nexusTool.sendCommand.bind(nexusTool);
43097
- backend = new NexusAgenticBackend(sendFn, config.model);
44433
+ const targetPeer = config.backendUrl.startsWith("peer://") ? config.backendUrl.slice(7) : void 0;
44434
+ backend = new NexusAgenticBackend(sendFn, config.model, targetPeer, config.apiKey);
43098
44435
  } else {
43099
44436
  backend = new OllamaAgenticBackend(config.backendUrl, config.model, config.apiKey);
43100
44437
  }
44438
+ try {
44439
+ const endpointHistory = loadUsageHistory("endpoint", repoRoot);
44440
+ const fallbackEndpoints = [];
44441
+ for (const entry of endpointHistory) {
44442
+ if (entry.value === config.backendUrl)
44443
+ continue;
44444
+ const bt = entry.meta?.backendType ?? "ollama";
44445
+ const ep = {
44446
+ url: entry.value,
44447
+ backendType: bt,
44448
+ apiKey: entry.meta?.authHint ? void 0 : void 0,
44449
+ // auth keys not stored in history
44450
+ label: entry.meta?.provider ?? entry.value
44451
+ };
44452
+ if (bt === "nexus" && ep.url.startsWith("peer://")) {
44453
+ const nt = new NexusTool(repoRoot);
44454
+ ep.sendCommandFn = nt.sendCommand.bind(nt);
44455
+ }
44456
+ fallbackEndpoints.push(ep);
44457
+ }
44458
+ if (fallbackEndpoints.length > 0) {
44459
+ const primaryEndpoint = {
44460
+ url: config.backendUrl,
44461
+ backendType: config.backendType,
44462
+ apiKey: config.apiKey,
44463
+ label: "primary"
44464
+ };
44465
+ if (config.backendType === "nexus" && config.backendUrl.startsWith("peer://")) {
44466
+ const nt = new NexusTool(repoRoot);
44467
+ primaryEndpoint.sendCommandFn = nt.sendCommand.bind(nt);
44468
+ }
44469
+ backend = new CascadeBackend(config.model, [primaryEndpoint, ...fallbackEndpoints], {
44470
+ onSwitch: (_from, to, reason) => {
44471
+ process.stderr.write(`
44472
+ [cascade] Failover \u2192 ${to.label ?? to.url}: ${reason}
44473
+ `);
44474
+ },
44475
+ onProbeResult: (ep, success) => {
44476
+ if (success) {
44477
+ process.stderr.write(`[cascade] Primary recovered: ${ep.label ?? ep.url}
44478
+ `);
44479
+ }
44480
+ }
44481
+ });
44482
+ }
44483
+ } catch {
44484
+ }
43101
44485
  const compactionThreshold = modelTier === "small" ? 12e3 : modelTier === "medium" ? 24e3 : 4e4;
43102
44486
  const runner = new AgenticRunner(backend, {
43103
44487
  maxTurns: 60,
@@ -43521,6 +44905,9 @@ ${emotionContext}` : `Working directory: ${repoRoot}`;
43521
44905
  } catch {
43522
44906
  }
43523
44907
  }
44908
+ if (backend && typeof backend.stop === "function") {
44909
+ backend.stop();
44910
+ }
43524
44911
  });
43525
44912
  return { runner, promise, filesTouched, get toolCallCount() {
43526
44913
  return toolSequence.length;
@@ -43930,8 +45317,8 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
43930
45317
  const MAX_HISTORY_LINES = 500;
43931
45318
  let savedHistory = [];
43932
45319
  try {
43933
- if (existsSync39(HISTORY_FILE)) {
43934
- const raw = readFileSync29(HISTORY_FILE, "utf8").trim();
45320
+ if (existsSync40(HISTORY_FILE)) {
45321
+ const raw = readFileSync30(HISTORY_FILE, "utf8").trim();
43935
45322
  if (raw)
43936
45323
  savedHistory = raw.split("\n").reverse();
43937
45324
  }
@@ -43950,12 +45337,12 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
43950
45337
  if (!line.trim())
43951
45338
  return;
43952
45339
  try {
43953
- mkdirSync18(HISTORY_DIR, { recursive: true });
45340
+ mkdirSync19(HISTORY_DIR, { recursive: true });
43954
45341
  appendFileSync3(HISTORY_FILE, line + "\n", "utf8");
43955
45342
  if (Math.random() < 0.02) {
43956
- const all = readFileSync29(HISTORY_FILE, "utf8").trim().split("\n");
45343
+ const all = readFileSync30(HISTORY_FILE, "utf8").trim().split("\n");
43957
45344
  if (all.length > MAX_HISTORY_LINES) {
43958
- writeFileSync16(HISTORY_FILE, all.slice(-MAX_HISTORY_LINES).join("\n") + "\n", "utf8");
45345
+ writeFileSync17(HISTORY_FILE, all.slice(-MAX_HISTORY_LINES).join("\n") + "\n", "utf8");
43959
45346
  }
43960
45347
  }
43961
45348
  } catch {
@@ -44085,8 +45472,13 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
44085
45472
  } catch {
44086
45473
  }
44087
45474
  try {
44088
- const isRemote = !currentConfig.backendUrl.includes("127.0.0.1") && !currentConfig.backendUrl.includes("localhost");
44089
- if (isRemote) {
45475
+ const isPeer = currentConfig.backendUrl.startsWith("peer://");
45476
+ const isRemote = !currentConfig.backendUrl.includes("127.0.0.1") && !currentConfig.backendUrl.includes("localhost") && !isPeer;
45477
+ if (isPeer) {
45478
+ const peerId = currentConfig.backendUrl.slice(7);
45479
+ const nexusTool = new NexusTool(repoRoot);
45480
+ statusBar.startPeerMetricsPolling(nexusTool.sendCommand.bind(nexusTool), peerId, currentConfig.apiKey);
45481
+ } else if (isRemote) {
44090
45482
  statusBar.startRemoteMetricsPolling(currentConfig.backendUrl, currentConfig.apiKey);
44091
45483
  }
44092
45484
  } catch {
@@ -44158,8 +45550,13 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
44158
45550
  };
44159
45551
  const newProvider = detectProvider(url);
44160
45552
  costTracker.setProvider(newProvider.id);
44161
- const isRemote = !url.includes("127.0.0.1") && !url.includes("localhost");
44162
- if (isRemote) {
45553
+ const isPeer = url.startsWith("peer://");
45554
+ const isRemote = !url.includes("127.0.0.1") && !url.includes("localhost") && !isPeer;
45555
+ if (isPeer) {
45556
+ const peerId = url.slice(7);
45557
+ const nexusTool = new NexusTool(repoRoot);
45558
+ statusBar.startPeerMetricsPolling(nexusTool.sendCommand.bind(nexusTool), peerId, apiKey);
45559
+ } else if (isRemote) {
44163
45560
  statusBar.startRemoteMetricsPolling(url, apiKey);
44164
45561
  } else {
44165
45562
  statusBar.stopRemoteMetricsPolling();
@@ -44714,7 +46111,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
44714
46111
  getCallUrl() {
44715
46112
  return voiceSession?.tunnelUrl ?? null;
44716
46113
  },
44717
- async exposeStart(kindOrUrl, authKey, transport) {
46114
+ async exposeStart(kindOrUrl, authKey, transport, fullAccess) {
44718
46115
  const knownKinds = ["ollama", "vllm", "llvm"];
44719
46116
  let kind;
44720
46117
  let targetUrl;
@@ -44730,6 +46127,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
44730
46127
  const p2pGateway = new ExposeP2PGateway({
44731
46128
  kind,
44732
46129
  targetUrl,
46130
+ authKey,
44733
46131
  stateDir: join50(repoRoot, ".oa")
44734
46132
  }, nexusTool);
44735
46133
  p2pGateway.on("stats", (stats) => {
@@ -44758,7 +46156,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
44758
46156
  }
44759
46157
  }
44760
46158
  }
44761
- const tunnelGateway = new ExposeGateway({ kind, targetUrl, authKey, stateDir: join50(repoRoot, ".oa") });
46159
+ const tunnelGateway = new ExposeGateway({ kind, targetUrl, authKey, fullAccess, stateDir: join50(repoRoot, ".oa") });
44762
46160
  tunnelGateway.on("stats", (stats) => {
44763
46161
  statusBar.setExposeStatus({
44764
46162
  status: stats.status,
@@ -44795,7 +46193,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
44795
46193
  if (exposeGateway instanceof ExposeP2PGateway) {
44796
46194
  return {
44797
46195
  tunnelUrl: exposeGateway.peerId,
44798
- authKey: "",
46196
+ authKey: exposeGateway.authKey,
44799
46197
  stats: exposeGateway.formatStats()
44800
46198
  };
44801
46199
  }
@@ -44805,6 +46203,18 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
44805
46203
  stats: exposeGateway.formatStats()
44806
46204
  };
44807
46205
  },
46206
+ // ── Nexus daemon connect ──────────────────────────────────────────────
46207
+ async nexusConnect() {
46208
+ const nexusTool = new NexusTool(repoRoot);
46209
+ const result = await nexusTool.execute({
46210
+ action: "connect",
46211
+ agent_name: "open-agents-node",
46212
+ agent_type: "general"
46213
+ });
46214
+ if (!result.success)
46215
+ throw new Error(result.error || "Connect failed");
46216
+ return result.output;
46217
+ },
44808
46218
  // ── P2P mesh controls ────────────────────────────────────────────────
44809
46219
  async p2pStart(bootstrapPeers) {
44810
46220
  if (peerMesh)
@@ -44954,7 +46364,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
44954
46364
  },
44955
46365
  destroyProject() {
44956
46366
  const oaPath = join50(repoRoot, OA_DIR);
44957
- if (existsSync39(oaPath)) {
46367
+ if (existsSync40(oaPath)) {
44958
46368
  try {
44959
46369
  rmSync2(oaPath, { recursive: true, force: true });
44960
46370
  writeContent(() => renderInfo(`Removed ${OA_DIR}/ directory.`));
@@ -45285,13 +46695,13 @@ Execute this skill now. Follow the behavioral guidance above.`;
45285
46695
  }
45286
46696
  }
45287
46697
  const cleanPath = input.replace(/^['"]|['"]$/g, "").trim();
45288
- const isImage = isImagePath(cleanPath) && existsSync39(resolve28(repoRoot, cleanPath));
45289
- const isMedia = !isImage && isTranscribablePath(cleanPath) && existsSync39(resolve28(repoRoot, cleanPath));
46698
+ const isImage = isImagePath(cleanPath) && existsSync40(resolve28(repoRoot, cleanPath));
46699
+ const isMedia = !isImage && isTranscribablePath(cleanPath) && existsSync40(resolve28(repoRoot, cleanPath));
45290
46700
  if (activeTask) {
45291
46701
  if (isImage) {
45292
46702
  try {
45293
46703
  const imgPath = resolve28(repoRoot, cleanPath);
45294
- const imgBuffer = readFileSync29(imgPath);
46704
+ const imgBuffer = readFileSync30(imgPath);
45295
46705
  const base64 = imgBuffer.toString("base64");
45296
46706
  const ext = extname9(cleanPath).toLowerCase();
45297
46707
  const mime = ext === ".png" ? "image/png" : ext === ".gif" ? "image/gif" : ext === ".webp" ? "image/webp" : "image/jpeg";
@@ -45989,13 +47399,13 @@ __export(index_repo_exports, {
45989
47399
  indexRepoCommand: () => indexRepoCommand
45990
47400
  });
45991
47401
  import { resolve as resolve29 } from "node:path";
45992
- import { existsSync as existsSync40, statSync as statSync11 } from "node:fs";
47402
+ import { existsSync as existsSync41, statSync as statSync11 } from "node:fs";
45993
47403
  import { cwd as cwd2 } from "node:process";
45994
47404
  async function indexRepoCommand(opts, _config) {
45995
47405
  const repoRoot = resolve29(opts.repoPath ?? cwd2());
45996
47406
  printHeader("Index Repository");
45997
47407
  printInfo(`Indexing: ${repoRoot}`);
45998
- if (!existsSync40(repoRoot)) {
47408
+ if (!existsSync41(repoRoot)) {
45999
47409
  printError(`Path does not exist: ${repoRoot}`);
46000
47410
  process.exit(1);
46001
47411
  }
@@ -46469,7 +47879,7 @@ var serve_exports = {};
46469
47879
  __export(serve_exports, {
46470
47880
  serveCommand: () => serveCommand
46471
47881
  });
46472
- import { spawn as spawn16 } from "node:child_process";
47882
+ import { spawn as spawn18 } from "node:child_process";
46473
47883
  async function serveCommand(opts, config) {
46474
47884
  const backendType = config.backendType;
46475
47885
  if (backendType === "ollama") {
@@ -46562,7 +47972,7 @@ async function serveVllm(opts, config) {
46562
47972
  }
46563
47973
  async function runVllmServer(args, verbose) {
46564
47974
  return new Promise((resolve31, reject) => {
46565
- const child = spawn16("python", args, {
47975
+ const child = spawn18("python", args, {
46566
47976
  stdio: verbose ? "inherit" : ["ignore", "pipe", "pipe"],
46567
47977
  env: { ...process.env }
46568
47978
  });
@@ -46627,7 +48037,7 @@ __export(eval_exports, {
46627
48037
  evalCommand: () => evalCommand
46628
48038
  });
46629
48039
  import { tmpdir as tmpdir7 } from "node:os";
46630
- import { mkdirSync as mkdirSync19, writeFileSync as writeFileSync17 } from "node:fs";
48040
+ import { mkdirSync as mkdirSync20, writeFileSync as writeFileSync18 } from "node:fs";
46631
48041
  import { join as join53 } from "node:path";
46632
48042
  async function evalCommand(opts, config) {
46633
48043
  const suiteName = opts.suite ?? "basic";
@@ -46754,8 +48164,8 @@ async function evalCommand(opts, config) {
46754
48164
  }
46755
48165
  function createTempEvalRepo() {
46756
48166
  const dir = join53(tmpdir7(), `open-agents-eval-${Date.now()}`);
46757
- mkdirSync19(dir, { recursive: true });
46758
- writeFileSync17(join53(dir, "package.json"), JSON.stringify({ name: "eval-repo", version: "0.0.0" }, null, 2) + "\n", "utf8");
48167
+ mkdirSync20(dir, { recursive: true });
48168
+ writeFileSync18(join53(dir, "package.json"), JSON.stringify({ name: "eval-repo", version: "0.0.0" }, null, 2) + "\n", "utf8");
46759
48169
  return dir;
46760
48170
  }
46761
48171
  var BASIC_SUITE, FULL_SUITE, SUITES;
@@ -46815,7 +48225,7 @@ init_updater();
46815
48225
  import { parseArgs as nodeParseArgs2 } from "node:util";
46816
48226
  import { createRequire as createRequire3 } from "node:module";
46817
48227
  import { fileURLToPath as fileURLToPath13 } from "node:url";
46818
- import { dirname as dirname18, join as join54 } from "node:path";
48228
+ import { dirname as dirname19, join as join54 } from "node:path";
46819
48229
 
46820
48230
  // packages/cli/dist/cli.js
46821
48231
  import { createInterface } from "node:readline";
@@ -46922,7 +48332,7 @@ init_output();
46922
48332
  function getVersion4() {
46923
48333
  try {
46924
48334
  const require2 = createRequire3(import.meta.url);
46925
- const pkgPath = join54(dirname18(fileURLToPath13(import.meta.url)), "..", "package.json");
48335
+ const pkgPath = join54(dirname19(fileURLToPath13(import.meta.url)), "..", "package.json");
46926
48336
  const pkg = require2(pkgPath);
46927
48337
  return pkg.version;
46928
48338
  } catch {