open-agents-ai 0.103.23 → 0.103.25

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 +184 -37
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -12046,7 +12046,7 @@ ${truncated}`, durationMs: performance.now() - start };
12046
12046
 
12047
12047
  // packages/execution/dist/tools/nexus.js
12048
12048
  import { readFile as readFile13, writeFile as writeFile12, mkdir as mkdir8, chmod, unlink, readdir as readdir3, open as fsOpen } from "node:fs/promises";
12049
- import { existsSync as existsSync23, readFileSync as readFileSync16 } from "node:fs";
12049
+ import { existsSync as existsSync23, readFileSync as readFileSync16, watch as fsWatchLocal } from "node:fs";
12050
12050
  import { resolve as resolve26, join as join30 } from "node:path";
12051
12051
  import { randomBytes as randomBytes6, createCipheriv, createDecipheriv, scryptSync, createHash } from "node:crypto";
12052
12052
  import { execSync as execSync22, spawn as spawn11 } from "node:child_process";
@@ -12072,7 +12072,7 @@ var init_nexus = __esm({
12072
12072
  */
12073
12073
 
12074
12074
  import { NexusClient, createFileAuditHook } from 'open-agents-nexus';
12075
- import { readFileSync, writeFileSync, existsSync, mkdirSync, unlinkSync, readdirSync, appendFileSync } from 'node:fs';
12075
+ import { readFileSync, writeFileSync, existsSync, mkdirSync, unlinkSync, readdirSync, appendFileSync, watch as fsWatch } from 'node:fs';
12076
12076
  import { join } from 'node:path';
12077
12077
 
12078
12078
  const nexusDir = process.argv[2];
@@ -12672,8 +12672,8 @@ async function handleCmd(cmd) {
12672
12672
  // Wait for input data to arrive (invoke protocol sends data after accept)
12673
12673
  var waitMs = 0;
12674
12674
  while (!inputDone && dataChunks.length === 0 && waitMs < 5000) {
12675
- await new Promise(function(r) { setTimeout(r, 100); });
12676
- waitMs += 100;
12675
+ await new Promise(function(r) { setTimeout(r, 10); });
12676
+ waitMs += 10;
12677
12677
  }
12678
12678
  prompt = dataChunks.join('');
12679
12679
  dlog('expose: received ' + dataChunks.length + ' chunks, prompt_len=' + prompt.length + ' inputDone=' + inputDone);
@@ -12904,9 +12904,10 @@ async function handleCmd(cmd) {
12904
12904
  }
12905
12905
  }
12906
12906
 
12907
- // Command polling loop \u2014 check for cmd.json every 500ms
12907
+ // Command polling loop \u2014 check for cmd.json every 50ms (fast IPC)
12908
+ // fs.existsSync + readFileSync costs ~0.01ms per call, negligible CPU at 50ms interval.
12908
12909
  let lastCmdId = '';
12909
- setInterval(() => {
12910
+ function checkCmd() {
12910
12911
  try {
12911
12912
  if (!existsSync(cmdFile)) return;
12912
12913
  const raw = readFileSync(cmdFile, 'utf8');
@@ -12917,7 +12918,14 @@ setInterval(() => {
12917
12918
  writeResp(cmd.id, { ok: false, output: 'Error: ' + (err.message || String(err)) });
12918
12919
  });
12919
12920
  } catch {}
12920
- }, 500);
12921
+ }
12922
+ setInterval(checkCmd, 50);
12923
+ // Also watch for cmd.json changes for instant notification (best-effort)
12924
+ try {
12925
+ fsWatch(nexusDir, { persistent: false }, function(evType, filename) {
12926
+ if (filename === 'cmd.json') checkCmd();
12927
+ });
12928
+ } catch {}
12921
12929
 
12922
12930
  // Crash protection \u2014 prevent unhandled errors from killing the daemon
12923
12931
  process.on('uncaughtException', (err) => {
@@ -13310,11 +13318,59 @@ process.on('SIGINT', () => process.emit('SIGTERM'));
13310
13318
  await unlink(respFile).catch(() => {
13311
13319
  });
13312
13320
  await writeFile12(cmdFile, JSON.stringify({ id: cmdId, action, args }, null, 2));
13313
- const polls = Math.ceil(timeoutMs / 500);
13314
- for (let i = 0; i < polls; i++) {
13315
- await new Promise((r) => setTimeout(r, 500));
13316
- if (!existsSync23(respFile))
13317
- continue;
13321
+ const pollMs = 50;
13322
+ const polls = Math.ceil(timeoutMs / pollMs);
13323
+ let resolved = false;
13324
+ let watcher = null;
13325
+ const watchResolve = new Promise((watchDone) => {
13326
+ try {
13327
+ watcher = fsWatchLocal(this.nexusDir, { persistent: false }, (_evType, filename) => {
13328
+ if (resolved || filename !== "resp.json")
13329
+ return;
13330
+ try {
13331
+ if (!existsSync23(respFile))
13332
+ return;
13333
+ const resp = JSON.parse(readFileSync16(respFile, "utf8"));
13334
+ if (resp.id === cmdId) {
13335
+ resolved = true;
13336
+ watchDone(resp.output || (resp.ok ? "OK" : "Failed"));
13337
+ }
13338
+ } catch {
13339
+ }
13340
+ });
13341
+ } catch {
13342
+ }
13343
+ });
13344
+ const pollResolve = (async () => {
13345
+ for (let i = 0; i < polls && !resolved; i++) {
13346
+ await new Promise((r) => setTimeout(r, pollMs));
13347
+ if (resolved)
13348
+ return null;
13349
+ if (!existsSync23(respFile))
13350
+ continue;
13351
+ try {
13352
+ const resp = JSON.parse(await readFile13(respFile, "utf8"));
13353
+ if (resp.id === cmdId) {
13354
+ resolved = true;
13355
+ return resp.output || (resp.ok ? "OK" : "Failed");
13356
+ }
13357
+ } catch {
13358
+ }
13359
+ }
13360
+ return null;
13361
+ })();
13362
+ try {
13363
+ const result = await Promise.race([watchResolve, pollResolve]);
13364
+ if (result)
13365
+ return result;
13366
+ } finally {
13367
+ resolved = true;
13368
+ try {
13369
+ watcher?.close();
13370
+ } catch {
13371
+ }
13372
+ }
13373
+ if (existsSync23(respFile)) {
13318
13374
  try {
13319
13375
  const resp = JSON.parse(await readFile13(respFile, "utf8"));
13320
13376
  if (resp.id === cmdId) {
@@ -28178,7 +28234,7 @@ ${this.formatConnectionInfo()}`);
28178
28234
  if (!connectResult.success) {
28179
28235
  throw new Error(`Nexus daemon connect failed: ${connectResult.error}`);
28180
28236
  }
28181
- await new Promise((r) => setTimeout(r, 2e3));
28237
+ await new Promise((r) => setTimeout(r, 500));
28182
28238
  this._onInfo?.("Registering inference capabilities...");
28183
28239
  let exposeResult = await this._nexusTool.execute({
28184
28240
  action: "expose",
@@ -28187,7 +28243,7 @@ ${this.formatConnectionInfo()}`);
28187
28243
  auth_key: this._authKey
28188
28244
  });
28189
28245
  if (!exposeResult.success && exposeResult.error?.includes("not running")) {
28190
- await new Promise((r) => setTimeout(r, 3e3));
28246
+ await new Promise((r) => setTimeout(r, 1500));
28191
28247
  exposeResult = await this._nexusTool.execute({
28192
28248
  action: "expose",
28193
28249
  ollama_url: this._targetUrl,
@@ -28200,7 +28256,7 @@ ${this.formatConnectionInfo()}`);
28200
28256
  }
28201
28257
  const nexusDir = this._nexusTool.getNexusDir();
28202
28258
  const statusPath = join35(nexusDir, "status.json");
28203
- for (let i = 0; i < 60; i++) {
28259
+ for (let i = 0; i < 80; i++) {
28204
28260
  try {
28205
28261
  const raw = readFileSync19(statusPath, "utf8");
28206
28262
  if (raw.length > 10) {
@@ -28214,7 +28270,7 @@ ${this.formatConnectionInfo()}`);
28214
28270
  }
28215
28271
  } catch {
28216
28272
  }
28217
- await new Promise((r) => setTimeout(r, 500));
28273
+ await new Promise((r) => setTimeout(r, i < 20 ? 100 : 250));
28218
28274
  }
28219
28275
  this._stats.status = "active";
28220
28276
  this._stats.startedAt = Date.now();
@@ -29756,8 +29812,26 @@ async function fetchOpenAIModels(baseUrl, apiKey) {
29756
29812
  async function fetchPeerModels(peerId) {
29757
29813
  try {
29758
29814
  const { NexusTool: NexusTool2 } = await Promise.resolve().then(() => (init_dist2(), dist_exports));
29815
+ const { existsSync: existsSync42, readFileSync: readFileSync31 } = await import("node:fs");
29816
+ const { join: join55 } = await import("node:path");
29759
29817
  const cwd4 = process.cwd();
29760
29818
  const nexusTool = new NexusTool2(cwd4);
29819
+ const pricingPath = join55(nexusTool.getNexusDir(), "pricing.json");
29820
+ if (existsSync42(pricingPath)) {
29821
+ try {
29822
+ const pricing = JSON.parse(readFileSync31(pricingPath, "utf8"));
29823
+ const localModels = (pricing.models || []).map((m) => ({
29824
+ name: m.model || "unknown",
29825
+ size: m.parameterSize || "",
29826
+ modified: "",
29827
+ sizeBytes: 0,
29828
+ parameterSize: m.parameterSize || "remote"
29829
+ }));
29830
+ if (localModels.length > 0)
29831
+ return localModels;
29832
+ } catch {
29833
+ }
29834
+ }
29761
29835
  const result = await nexusTool.execute({
29762
29836
  action: "find_agent",
29763
29837
  peer_id: peerId
@@ -29779,19 +29853,6 @@ async function fetchPeerModels(peerId) {
29779
29853
  if (models.length > 0)
29780
29854
  return models;
29781
29855
  }
29782
- const { existsSync: existsSync42, readFileSync: readFileSync31 } = await import("node:fs");
29783
- const { join: join55 } = await import("node:path");
29784
- const pricingPath = join55(nexusTool.getNexusDir(), "pricing.json");
29785
- if (existsSync42(pricingPath)) {
29786
- const pricing = JSON.parse(readFileSync31(pricingPath, "utf8"));
29787
- return (pricing.models || []).map((m) => ({
29788
- name: m.model || "unknown",
29789
- size: m.parameterSize || "",
29790
- modified: "",
29791
- sizeBytes: 0,
29792
- parameterSize: m.parameterSize || "remote"
29793
- }));
29794
- }
29795
29856
  return [];
29796
29857
  } catch {
29797
29858
  return [];
@@ -30600,6 +30661,20 @@ function loadUsageHistory(kind, repoRoot) {
30600
30661
  }
30601
30662
  return Array.from(map.values()).sort((a, b) => new Date(b.lastUsed).getTime() - new Date(a.lastUsed).getTime());
30602
30663
  }
30664
+ function deleteUsageRecord(kind, value, repoRoot) {
30665
+ const remove = (filePath) => {
30666
+ const data = loadUsageFile(filePath);
30667
+ const before = data.records.length;
30668
+ data.records = data.records.filter((r) => !(r.kind === kind && r.value === value));
30669
+ if (data.records.length < before) {
30670
+ saveUsageFile(filePath, data);
30671
+ }
30672
+ };
30673
+ remove(join39(homedir9(), ".open-agents", USAGE_HISTORY_FILE));
30674
+ if (repoRoot) {
30675
+ remove(join39(repoRoot, OA_DIR, USAGE_HISTORY_FILE));
30676
+ }
30677
+ }
30603
30678
  var OA_DIR, SUBDIRS, CONTEXT_FILES, PENDING_TASK_FILE, CONTEXT_SAVE_FILE, MAX_CONTEXT_ENTRIES, SKIP_DIRS, USAGE_HISTORY_FILE, MAX_HISTORY_RECORDS;
30604
30679
  var init_oa_directory = __esm({
30605
30680
  "packages/cli/dist/tui/oa-directory.js"() {
@@ -32243,6 +32318,8 @@ function tuiSelect(opts) {
32243
32318
  const isSkippable = (idx) => skipSet.has(items[idx].key);
32244
32319
  let filter = "";
32245
32320
  let matchSet = /* @__PURE__ */ new Set();
32321
+ let deleteConfirmIdx = -1;
32322
+ let deleteConfirmSel = false;
32246
32323
  function updateFilter() {
32247
32324
  if (!filter) {
32248
32325
  matchSet = new Set(items.map((_, i) => i));
@@ -32367,7 +32444,11 @@ function tuiSelect(opts) {
32367
32444
  }
32368
32445
  const focused = idx === cursor;
32369
32446
  const isActive = item.key === activeKey;
32370
- if (filter) {
32447
+ if (deleteConfirmIdx === idx) {
32448
+ const yesLabel = deleteConfirmSel ? selectColors.bold(selectColors.green("[Yes]")) : selectColors.dim("[Yes]");
32449
+ const noLabel = !deleteConfirmSel ? selectColors.bold(selectColors.orange("[No]")) : selectColors.dim("[No]");
32450
+ lines.push(` ${ansi3("31", "\u2715")} ${ansi3("31", stripAnsi(item.label))} Delete? ${yesLabel} ${noLabel}`);
32451
+ } else if (filter) {
32371
32452
  lines.push(matchRow(item, focused, isActive));
32372
32453
  } else {
32373
32454
  lines.push(renderRow(item, focused, isActive));
@@ -32378,8 +32459,13 @@ function tuiSelect(opts) {
32378
32459
  lines.push(` ${selectColors.dim(` \u25BC ${remaining} more`)}`);
32379
32460
  }
32380
32461
  lines.push("");
32381
- const actionHint = opts.onAction ? " \u2190/\u2192/Space toggle" : "";
32382
- lines.push(` ${selectColors.dim("\u2191/\u2193 navigate Enter select" + actionHint + " Esc " + (filter ? "clear filter" : "cancel") + " Type to filter")}`);
32462
+ if (deleteConfirmIdx >= 0) {
32463
+ lines.push(` ${selectColors.dim("\u2190/\u2192 select Enter confirm Esc cancel")}`);
32464
+ } else {
32465
+ const actionHint = opts.onAction ? " \u2190/\u2192/Space toggle" : "";
32466
+ const deleteHint = opts.onDelete ? " Del remove" : "";
32467
+ lines.push(` ${selectColors.dim("\u2191/\u2193 navigate Enter select" + actionHint + deleteHint + " Esc " + (filter ? "clear filter" : "cancel") + " Type to filter")}`);
32468
+ }
32383
32469
  lines.push("");
32384
32470
  const output = lines.join("\n");
32385
32471
  process.stdout.write(output);
@@ -32409,6 +32495,48 @@ function tuiSelect(opts) {
32409
32495
  }
32410
32496
  function onData(chunk) {
32411
32497
  const seq = chunk.toString("utf8");
32498
+ if (deleteConfirmIdx >= 0) {
32499
+ if (seq === "\x1B[D") {
32500
+ deleteConfirmSel = true;
32501
+ render();
32502
+ } else if (seq === "\x1B[C") {
32503
+ deleteConfirmSel = false;
32504
+ render();
32505
+ } else if (seq === "\r" || seq === "\n") {
32506
+ if (deleteConfirmSel && opts.onDelete) {
32507
+ const deletedIdx = deleteConfirmIdx;
32508
+ deleteConfirmIdx = -1;
32509
+ deleteConfirmSel = false;
32510
+ opts.onDelete(items[deletedIdx], (removed) => {
32511
+ if (removed) {
32512
+ items.splice(deletedIdx, 1);
32513
+ if (items.length === 0) {
32514
+ cleanup();
32515
+ resolve31({ confirmed: false, key: null, index: -1 });
32516
+ return;
32517
+ }
32518
+ updateFilter();
32519
+ if (cursor >= items.length)
32520
+ cursor = items.length - 1;
32521
+ const valid = findSelectable(cursor, 1) ?? findSelectable(cursor, -1);
32522
+ if (valid >= 0)
32523
+ cursor = valid;
32524
+ scrollOffset = 0;
32525
+ }
32526
+ render();
32527
+ });
32528
+ } else {
32529
+ deleteConfirmIdx = -1;
32530
+ deleteConfirmSel = false;
32531
+ render();
32532
+ }
32533
+ } else if (seq === "\x1B" || seq === "\x1B\x1B" || seq === "") {
32534
+ deleteConfirmIdx = -1;
32535
+ deleteConfirmSel = false;
32536
+ render();
32537
+ }
32538
+ return;
32539
+ }
32412
32540
  if (seq === "\x1B[A") {
32413
32541
  const next = findSelectable(cursor - 1, -1);
32414
32542
  if (next >= 0 && next !== cursor) {
@@ -32449,6 +32577,12 @@ function tuiSelect(opts) {
32449
32577
  cursor = last;
32450
32578
  render();
32451
32579
  }
32580
+ } else if (seq === "\x1B[3~" && opts.onDelete) {
32581
+ if (!isSkippable(cursor) && matchSet.has(cursor) && items[cursor].key !== activeKey) {
32582
+ deleteConfirmIdx = cursor;
32583
+ deleteConfirmSel = false;
32584
+ render();
32585
+ }
32452
32586
  } else if (seq === "\x1B[D" && opts.onAction) {
32453
32587
  if (!isSkippable(cursor) && matchSet.has(cursor)) {
32454
32588
  if (opts.onAction(items[cursor], "left"))
@@ -33850,20 +33984,28 @@ async function handleEndpoint(arg, ctx, local = false) {
33850
33984
  const items = history.map((h) => {
33851
33985
  const provider2 = h.meta?.provider ?? detectProvider(h.value).label;
33852
33986
  const uses = h.localUses > 0 ? `${h.useCount} uses (${h.localUses} local)` : `${h.useCount} uses`;
33987
+ const auth = h.meta?.authHint ? ` Auth: ${h.meta.authHint}` : "";
33853
33988
  return {
33854
33989
  key: h.value,
33855
33990
  label: h.value,
33856
- detail: `${provider2} ${uses}`
33991
+ detail: `${provider2} ${uses}${auth}`
33857
33992
  };
33858
33993
  });
33859
33994
  const result = await tuiSelect({
33860
33995
  items,
33861
33996
  activeKey: ctx.config.backendUrl,
33862
33997
  title: "Select Endpoint",
33863
- rl: ctx.rl
33998
+ rl: ctx.rl,
33999
+ onDelete: (item, done) => {
34000
+ deleteUsageRecord("endpoint", item.key, ctx.repoRoot);
34001
+ done(true);
34002
+ }
33864
34003
  });
33865
34004
  if (result.confirmed && result.key) {
33866
- await handleEndpoint(result.key, ctx, local);
34005
+ const selectedRecord = history.find((h) => h.value === result.key);
34006
+ const savedAuth = selectedRecord?.meta?.authKey;
34007
+ const endpointArg = savedAuth ? `${result.key} --auth ${savedAuth}` : result.key;
34008
+ await handleEndpoint(endpointArg, ctx, local);
33867
34009
  return;
33868
34010
  }
33869
34011
  renderInfo("Endpoint selection cancelled.");
@@ -33937,6 +34079,11 @@ async function handleEndpoint(arg, ctx, local = false) {
33937
34079
  await handlePeerEndpoint(url, apiKey, ctx, local);
33938
34080
  return;
33939
34081
  }
34082
+ if (url.startsWith("peer://")) {
34083
+ const extractedPeerId = url.slice(7);
34084
+ await handlePeerEndpoint(extractedPeerId, apiKey, ctx, local);
34085
+ return;
34086
+ }
33940
34087
  try {
33941
34088
  new URL(url);
33942
34089
  } catch {
@@ -34010,7 +34157,7 @@ async function handleEndpoint(arg, ctx, local = false) {
34010
34157
  meta: {
34011
34158
  provider: provider.label,
34012
34159
  backendType,
34013
- ...apiKey ? { authHint: apiKey.slice(0, 4) + "..." } : {}
34160
+ ...apiKey ? { authHint: apiKey.slice(0, 4) + "...", authKey: apiKey } : {}
34014
34161
  }
34015
34162
  });
34016
34163
  process.stdout.write(`
@@ -34102,7 +34249,7 @@ async function handlePeerEndpoint(peerId, authKey, ctx, local) {
34102
34249
  provider: "libp2p-peer",
34103
34250
  backendType: "nexus",
34104
34251
  peerId,
34105
- ...authKey ? { authHint: authKey.slice(0, 4) + "..." } : {}
34252
+ ...authKey ? { authHint: authKey.slice(0, 4) + "...", authKey } : {}
34106
34253
  }
34107
34254
  });
34108
34255
  process.stdout.write(`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.103.23",
3
+ "version": "0.103.25",
4
4
  "description": "AI coding agent powered by open-source models (Ollama/vLLM) — interactive TUI with agentic tool-calling loop",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",