open-agents-ai 0.103.99 → 0.104.2

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 +233 -76
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -30893,6 +30893,7 @@ ${activitySummary}
30893
30893
  });
30894
30894
 
30895
30895
  // packages/cli/dist/tui/model-picker.js
30896
+ import { totalmem as totalmem2 } from "node:os";
30896
30897
  async function fetchOllamaModels(baseUrl) {
30897
30898
  const url = `${normalizeBaseUrl(baseUrl)}/api/tags`;
30898
30899
  const resp = await fetch(url, {
@@ -30932,10 +30933,22 @@ async function fetchOllamaModels(baseUrl) {
30932
30933
  }
30933
30934
  }
30934
30935
  if (show.model_info) {
30935
- for (const [key, value] of Object.entries(show.model_info)) {
30936
- if (key.endsWith(".context_length") && typeof value === "number") {
30937
- result[i].contextLength = value;
30938
- break;
30936
+ const info = show.model_info;
30937
+ const arch = info["general.architecture"];
30938
+ const paramCount = info["general.parameter_count"];
30939
+ const fileSizeGB = result[i].sizeBytes > 0 ? result[i].sizeBytes / 1024 ** 3 : paramCount ? paramCount * 0.6 / 1024 ** 3 : 4;
30940
+ if (arch) {
30941
+ const archMax = info[`${arch}.context_length`];
30942
+ const nLayers = info[`${arch}.block_count`];
30943
+ const nKVHeads = info[`${arch}.attention.head_count_kv`] ?? info[`${arch}.attention.head_count`];
30944
+ const keyDim = info[`${arch}.attention.key_length`];
30945
+ const valDim = info[`${arch}.attention.value_length`] ?? keyDim;
30946
+ if (archMax && nLayers && nKVHeads && keyDim && valDim) {
30947
+ const kvBytesPerToken = nLayers * nKVHeads * (keyDim + valDim) * 2;
30948
+ result[i].contextLength = estimateRealisticContext(kvBytesPerToken, archMax, fileSizeGB);
30949
+ } else if (archMax) {
30950
+ const kvEstimate = fileSizeGB <= 5 ? 524288 : fileSizeGB <= 20 ? 1048576 : 1572864;
30951
+ result[i].contextLength = estimateRealisticContext(kvEstimate, archMax, fileSizeGB);
30939
30952
  }
30940
30953
  }
30941
30954
  }
@@ -31231,9 +31244,23 @@ async function queryModelContextSize(baseUrl, modelName) {
31231
31244
  return parseInt(match[1], 10);
31232
31245
  }
31233
31246
  if (data.model_info) {
31234
- for (const [key, value] of Object.entries(data.model_info)) {
31235
- if (key.endsWith(".context_length") && typeof value === "number") {
31236
- return value;
31247
+ const info = data.model_info;
31248
+ const arch = info["general.architecture"];
31249
+ const paramCount = info["general.parameter_count"];
31250
+ const modelSizeGB2 = paramCount ? paramCount * 0.6 / 1024 ** 3 : 4;
31251
+ if (arch) {
31252
+ const archMax = info[`${arch}.context_length`];
31253
+ const nLayers = info[`${arch}.block_count`];
31254
+ const nKVHeads = info[`${arch}.attention.head_count_kv`] ?? info[`${arch}.attention.head_count`];
31255
+ const keyDim = info[`${arch}.attention.key_length`];
31256
+ const valDim = info[`${arch}.attention.value_length`] ?? keyDim;
31257
+ if (archMax && nLayers && nKVHeads && keyDim && valDim) {
31258
+ const kvBytesPerToken = nLayers * nKVHeads * (keyDim + valDim) * 2;
31259
+ return estimateRealisticContext(kvBytesPerToken, archMax, modelSizeGB2);
31260
+ }
31261
+ if (archMax) {
31262
+ const kvEstimate = modelSizeGB2 <= 5 ? 524288 : modelSizeGB2 <= 20 ? 1048576 : 1572864;
31263
+ return estimateRealisticContext(kvEstimate, archMax, modelSizeGB2);
31237
31264
  }
31238
31265
  }
31239
31266
  }
@@ -31242,10 +31269,25 @@ async function queryModelContextSize(baseUrl, modelName) {
31242
31269
  return null;
31243
31270
  }
31244
31271
  }
31272
+ function estimateRealisticContext(kvBytesPerToken, archMax, modelSizeGB2) {
31273
+ const totalMemGB = totalmem2() / 1024 ** 3;
31274
+ const usableBytes = totalMemGB * 0.7 * 1024 ** 3;
31275
+ const maxTokens = Math.floor(usableBytes / kvBytesPerToken);
31276
+ let numCtx = Math.max(2048, Math.floor(maxTokens / 1024) * 1024);
31277
+ numCtx = Math.min(numCtx, 131072, archMax);
31278
+ if (modelSizeGB2 && modelSizeGB2 > 0) {
31279
+ const maxKVBytes = modelSizeGB2 * 4 * 1024 ** 3;
31280
+ const budgetCap = Math.max(2048, Math.floor(maxKVBytes / kvBytesPerToken / 1024) * 1024);
31281
+ numCtx = Math.min(numCtx, budgetCap);
31282
+ }
31283
+ return numCtx;
31284
+ }
31245
31285
  async function queryOpenAIContextSize(baseUrl, modelName, apiKey) {
31246
31286
  try {
31247
31287
  const models = await fetchOpenAIModels(baseUrl, apiKey);
31248
31288
  const model = models.find((m) => m.name === modelName);
31289
+ if (model?.contextLength)
31290
+ return model.contextLength;
31249
31291
  if (model?.size) {
31250
31292
  const match = model.size.match(/(\d+)K ctx/);
31251
31293
  if (match)
@@ -32155,20 +32197,30 @@ function recommendModel(specs) {
32155
32197
  }
32156
32198
  return QWEN_VARIANTS.find((v) => v.tag === "qwen3.5:cloud");
32157
32199
  }
32158
- function calculateContextWindow(specs, modelSizeGB2) {
32200
+ function calculateContextWindow(specs, modelSizeGB2, kvBytesPerToken, archMax) {
32159
32201
  const totalAvail = Math.max(specs.gpuVramGB, specs.totalRamGB);
32160
- const remaining = totalAvail - modelSizeGB2;
32161
- if (remaining >= 200)
32162
- return { numCtx: 131072, label: "128K" };
32163
- if (remaining >= 100)
32164
- return { numCtx: 65536, label: "64K" };
32165
- if (remaining >= 50)
32166
- return { numCtx: 32768, label: "32K" };
32167
- if (remaining >= 20)
32168
- return { numCtx: 16384, label: "16K" };
32169
- if (remaining >= 8)
32170
- return { numCtx: 8192, label: "8K" };
32171
- return { numCtx: 4096, label: "4K" };
32202
+ const remaining = Math.max(0, totalAvail - modelSizeGB2);
32203
+ const usableGB = remaining * 0.85;
32204
+ let numCtx;
32205
+ if (kvBytesPerToken && kvBytesPerToken > 0) {
32206
+ const maxTokens = Math.floor(usableGB * 1024 ** 3 / kvBytesPerToken);
32207
+ numCtx = Math.max(2048, Math.floor(maxTokens / 1024) * 1024);
32208
+ } else {
32209
+ const kvEstimate = modelSizeGB2 <= 5 ? 524288 : modelSizeGB2 <= 20 ? 1048576 : 1572864;
32210
+ const maxTokens = Math.floor(usableGB * 1024 ** 3 / kvEstimate);
32211
+ numCtx = Math.max(2048, Math.floor(maxTokens / 1024) * 1024);
32212
+ }
32213
+ numCtx = Math.min(numCtx, 131072);
32214
+ if (archMax && archMax > 0)
32215
+ numCtx = Math.min(numCtx, archMax);
32216
+ if (kvBytesPerToken && kvBytesPerToken > 0 && modelSizeGB2 > 0) {
32217
+ const maxKVBytes = modelSizeGB2 * 4 * 1024 ** 3;
32218
+ const maxTokensFromBudget = Math.floor(maxKVBytes / kvBytesPerToken);
32219
+ const budgetCap = Math.max(2048, Math.floor(maxTokensFromBudget / 1024) * 1024);
32220
+ numCtx = Math.min(numCtx, budgetCap);
32221
+ }
32222
+ const label = numCtx >= 1024 ? `${Math.floor(numCtx / 1024)}K` : String(numCtx);
32223
+ return { numCtx, label };
32172
32224
  }
32173
32225
  function modelSupportsToolCalling(modelName) {
32174
32226
  const lower = modelName.toLowerCase();
@@ -33512,9 +33564,40 @@ function modelSizeGB(models, modelName) {
33512
33564
  const known = QWEN_VARIANTS.find((v) => modelName.includes(v.tag.split(":")[1] ?? ""));
33513
33565
  return known?.sizeGB ?? 4;
33514
33566
  }
33515
- async function createExpandedVariantAsync(baseModel, specs, sizeGB) {
33567
+ async function queryModelKVInfo(backendUrl, modelName) {
33568
+ try {
33569
+ const normalized = backendUrl.replace(/\/+$/, "");
33570
+ const res = await fetch(`${normalized}/api/show`, {
33571
+ method: "POST",
33572
+ headers: { "Content-Type": "application/json" },
33573
+ body: JSON.stringify({ name: modelName }),
33574
+ signal: AbortSignal.timeout(5e3)
33575
+ });
33576
+ if (!res.ok)
33577
+ return null;
33578
+ const data = await res.json();
33579
+ if (!data.model_info)
33580
+ return null;
33581
+ const info = data.model_info;
33582
+ const arch = info["general.architecture"];
33583
+ if (!arch)
33584
+ return null;
33585
+ const nLayers = info[`${arch}.block_count`];
33586
+ const nKVHeads = info[`${arch}.attention.head_count_kv`] ?? info[`${arch}.attention.head_count`];
33587
+ const keyDim = info[`${arch}.attention.key_length`];
33588
+ const valDim = info[`${arch}.attention.value_length`] ?? keyDim;
33589
+ const archMax = info[`${arch}.context_length`];
33590
+ if (!nLayers || !nKVHeads || !keyDim || !valDim || !archMax)
33591
+ return null;
33592
+ const kvBytesPerToken = nLayers * nKVHeads * (keyDim + valDim) * 2;
33593
+ return { kvBytesPerToken, archMax };
33594
+ } catch {
33595
+ return null;
33596
+ }
33597
+ }
33598
+ async function createExpandedVariantAsync(baseModel, specs, sizeGB, kvBytesPerToken, archMax) {
33516
33599
  const customName = expandedModelName(baseModel);
33517
- const ctx = calculateContextWindow(specs, sizeGB);
33600
+ const ctx = calculateContextWindow(specs, sizeGB, kvBytesPerToken, archMax);
33518
33601
  try {
33519
33602
  const numPredict = Math.min(16384, Math.max(2048, Math.floor(ctx.numCtx * 0.25)));
33520
33603
  const modelfileContent = [
@@ -33537,37 +33620,51 @@ async function createExpandedVariantAsync(baseModel, specs, sizeGB) {
33537
33620
  }
33538
33621
  }
33539
33622
  async function ensureExpandedContext(modelName, backendUrl) {
33540
- if (modelName.startsWith("open-agents-")) {
33541
- const specs2 = await detectSystemSpecsAsync();
33542
- const ctx2 = calculateContextWindow(specs2, 4);
33543
- return { model: modelName, created: false, contextLabel: ctx2.label, numCtx: ctx2.numCtx };
33544
- }
33545
33623
  if (modelName.includes("cloud") || modelName.includes(":cloud")) {
33546
33624
  return { model: modelName, created: false, contextLabel: "remote", numCtx: 0 };
33547
33625
  }
33548
- const existing = await checkExpandedVariant(modelName, backendUrl);
33549
- if (existing === null) {
33550
- return { model: modelName, created: false, contextLabel: "", numCtx: 0 };
33551
- }
33552
33626
  const specs = await detectSystemSpecsAsync();
33553
- if (typeof existing === "string") {
33554
- let sizeGB2 = 4;
33555
- try {
33556
- const models = await fetchOllamaModels(backendUrl);
33557
- sizeGB2 = modelSizeGB(models, modelName);
33558
- } catch {
33559
- }
33560
- const ctx2 = calculateContextWindow(specs, sizeGB2);
33561
- return { model: existing, created: false, contextLabel: ctx2.label, numCtx: ctx2.numCtx };
33562
- }
33627
+ const kvInfo = await queryModelKVInfo(backendUrl, modelName);
33563
33628
  let sizeGB = 4;
33564
33629
  try {
33565
33630
  const models = await fetchOllamaModels(backendUrl);
33566
33631
  sizeGB = modelSizeGB(models, modelName);
33567
33632
  } catch {
33568
33633
  }
33569
- const ctx = calculateContextWindow(specs, sizeGB);
33570
- const created = await createExpandedVariantAsync(modelName, specs, sizeGB);
33634
+ const ctx = calculateContextWindow(specs, sizeGB, kvInfo?.kvBytesPerToken, kvInfo?.archMax);
33635
+ if (modelName.startsWith("open-agents-")) {
33636
+ try {
33637
+ const normalized = backendUrl.replace(/\/+$/, "");
33638
+ const showRes = await fetch(`${normalized}/api/show`, {
33639
+ method: "POST",
33640
+ headers: { "Content-Type": "application/json" },
33641
+ body: JSON.stringify({ name: modelName }),
33642
+ signal: AbortSignal.timeout(5e3)
33643
+ });
33644
+ if (showRes.ok) {
33645
+ const showData = await showRes.json();
33646
+ const numCtxMatch = showData.parameters?.match(/num_ctx\s+(\d+)/);
33647
+ const currentNumCtx = numCtxMatch ? parseInt(numCtxMatch[1], 10) : 0;
33648
+ if (currentNumCtx !== ctx.numCtx) {
33649
+ const fromMatch = showData.modelfile?.match(/^FROM\s+(.+)$/m);
33650
+ const baseModel = fromMatch?.[1]?.trim();
33651
+ if (baseModel && !baseModel.startsWith("open-agents-")) {
33652
+ await createExpandedVariantAsync(baseModel, specs, sizeGB, kvInfo?.kvBytesPerToken, kvInfo?.archMax);
33653
+ }
33654
+ }
33655
+ }
33656
+ } catch {
33657
+ }
33658
+ return { model: modelName, created: false, contextLabel: ctx.label, numCtx: ctx.numCtx };
33659
+ }
33660
+ const existing = await checkExpandedVariant(modelName, backendUrl);
33661
+ if (existing === null) {
33662
+ return { model: modelName, created: false, contextLabel: "", numCtx: 0 };
33663
+ }
33664
+ if (typeof existing === "string") {
33665
+ return { model: existing, created: false, contextLabel: ctx.label, numCtx: ctx.numCtx };
33666
+ }
33667
+ const created = await createExpandedVariantAsync(modelName, specs, sizeGB, kvInfo?.kvBytesPerToken, kvInfo?.archMax);
33571
33668
  if (created) {
33572
33669
  return { model: created, created: true, contextLabel: ctx.label, numCtx: ctx.numCtx };
33573
33670
  }
@@ -33610,6 +33707,45 @@ var init_setup = __esm({
33610
33707
  }
33611
33708
  });
33612
33709
 
33710
+ // packages/cli/dist/tui/overlay-lock.js
33711
+ function isOverlayActive() {
33712
+ return _overlayActive;
33713
+ }
33714
+ function bufferIfOverlay(stream, data) {
33715
+ if (!_overlayActive)
33716
+ return false;
33717
+ _buffer.push({ stream, data });
33718
+ return true;
33719
+ }
33720
+ function enterOverlay() {
33721
+ _depth++;
33722
+ _overlayActive = true;
33723
+ }
33724
+ function leaveOverlay() {
33725
+ _depth = Math.max(0, _depth - 1);
33726
+ if (_depth === 0) {
33727
+ _overlayActive = false;
33728
+ const pending = _buffer;
33729
+ _buffer = [];
33730
+ for (const { stream, data } of pending) {
33731
+ if (stream === "stdout") {
33732
+ process.stdout.write(data);
33733
+ } else {
33734
+ process.stderr.write(data);
33735
+ }
33736
+ }
33737
+ }
33738
+ }
33739
+ var _overlayActive, _depth, _buffer;
33740
+ var init_overlay_lock = __esm({
33741
+ "packages/cli/dist/tui/overlay-lock.js"() {
33742
+ "use strict";
33743
+ _overlayActive = false;
33744
+ _depth = 0;
33745
+ _buffer = [];
33746
+ }
33747
+ });
33748
+
33613
33749
  // packages/cli/dist/tui/tui-select.js
33614
33750
  function ansi3(code, text) {
33615
33751
  return isTTY3 ? `\x1B[${code}m${text}\x1B[0m` : text;
@@ -33681,7 +33817,7 @@ function tuiSelect(opts) {
33681
33817
  cursor = first >= 0 ? first : 0;
33682
33818
  }
33683
33819
  const selectChrome = 3;
33684
- const contentArea = process.stdout.rows ?? 24;
33820
+ const contentArea = opts.availableRows ?? process.stdout.rows ?? 24;
33685
33821
  const maxVisible = opts.maxVisible ?? Math.max(3, contentArea - selectChrome);
33686
33822
  let scrollOffset = 0;
33687
33823
  let lastRenderedLines = 0;
@@ -33704,6 +33840,7 @@ function tuiSelect(opts) {
33704
33840
  }
33705
33841
  stdin.resume();
33706
33842
  process.stdout.write("\x1B[?1049h\x1B[2J\x1B[H\x1B[?25l");
33843
+ enterOverlay();
33707
33844
  function clampScroll(displayList) {
33708
33845
  const cursorPos = displayList.indexOf(cursor);
33709
33846
  if (cursorPos < 0)
@@ -33797,6 +33934,7 @@ function tuiSelect(opts) {
33797
33934
  function cleanup() {
33798
33935
  stdin.removeListener("data", onData);
33799
33936
  process.stdout.removeListener("resize", onResize);
33937
+ leaveOverlay();
33800
33938
  process.stdout.write("\x1B[?1049l\x1B[?25h");
33801
33939
  if (typeof stdin.setRawMode === "function") {
33802
33940
  stdin.setRawMode(hadRawMode ?? false);
@@ -34052,6 +34190,7 @@ var isTTY3, selectColors;
34052
34190
  var init_tui_select = __esm({
34053
34191
  "packages/cli/dist/tui/tui-select.js"() {
34054
34192
  "use strict";
34193
+ init_overlay_lock();
34055
34194
  isTTY3 = process.stdout.isTTY ?? false;
34056
34195
  selectColors = {
34057
34196
  orange: (t) => fg2562(208, t),
@@ -34094,6 +34233,7 @@ function showDropPanel(opts) {
34094
34233
  }
34095
34234
  stdin.resume();
34096
34235
  process.stdout.write("\x1B[?1049h\x1B[2J\x1B[H\x1B[?25h");
34236
+ enterOverlay();
34097
34237
  let inputBuf = "";
34098
34238
  let errorMsg = "";
34099
34239
  function render() {
@@ -34137,6 +34277,7 @@ function showDropPanel(opts) {
34137
34277
  }
34138
34278
  function cleanup() {
34139
34279
  stdin.removeListener("data", onData);
34280
+ leaveOverlay();
34140
34281
  process.stdout.write("\x1B[?1049l\x1B[?25h");
34141
34282
  if (typeof stdin.setRawMode === "function") {
34142
34283
  stdin.setRawMode(hadRawMode ?? false);
@@ -34212,6 +34353,7 @@ var isTTY4, dc;
34212
34353
  var init_drop_panel = __esm({
34213
34354
  "packages/cli/dist/tui/drop-panel.js"() {
34214
34355
  "use strict";
34356
+ init_overlay_lock();
34215
34357
  isTTY4 = process.stdout.isTTY ?? false;
34216
34358
  dc = {
34217
34359
  bold: (t) => ansi4("1", t),
@@ -37180,9 +37322,7 @@ async function handleSlashCommand(input, ctx) {
37180
37322
  return "handled";
37181
37323
  }
37182
37324
  if (arg === "list" || arg === "ls" || arg === "voices") {
37183
- ctx.setOverlayActive?.(true);
37184
37325
  await handleVoiceList(ctx);
37185
- ctx.setOverlayActive?.(false);
37186
37326
  return "handled";
37187
37327
  }
37188
37328
  const msg = await ctx.voiceSetModel(arg);
@@ -38268,7 +38408,6 @@ async function handleVoiceMenu(ctx, save, hasLocal) {
38268
38408
  { key: "clone", label: "Clone Voice", detail: "drag & drop audio file" },
38269
38409
  { key: "list", label: "Manage Clone Voices", detail: "rename, play, delete" }
38270
38410
  ];
38271
- ctx.setOverlayActive?.(true);
38272
38411
  const result = await tuiSelect({
38273
38412
  items,
38274
38413
  title: "Voice Configuration",
@@ -38276,7 +38415,6 @@ async function handleVoiceMenu(ctx, save, hasLocal) {
38276
38415
  availableRows: ctx.availableContentRows?.(),
38277
38416
  skipKeys: ["header-status", "header-config"]
38278
38417
  });
38279
- ctx.setOverlayActive?.(false);
38280
38418
  if (!result.confirmed || !result.key)
38281
38419
  return;
38282
38420
  switch (result.key) {
@@ -38287,7 +38425,6 @@ async function handleVoiceMenu(ctx, save, hasLocal) {
38287
38425
  continue;
38288
38426
  }
38289
38427
  case "mode": {
38290
- ctx.setOverlayActive?.(true);
38291
38428
  const modeItems = [
38292
38429
  { key: "chat", label: "Chat", detail: "speaks streamed model text at normal pitch" },
38293
38430
  { key: "action", label: "Action", detail: "speaks tool call narrations with emotion pitch" },
@@ -38300,7 +38437,6 @@ async function handleVoiceMenu(ctx, save, hasLocal) {
38300
38437
  rl: ctx.rl,
38301
38438
  availableRows: ctx.availableContentRows?.()
38302
38439
  });
38303
- ctx.setOverlayActive?.(false);
38304
38440
  if (modeResult.confirmed && modeResult.key) {
38305
38441
  const mode = modeResult.key;
38306
38442
  ctx.voiceSetMode?.(mode);
@@ -38309,7 +38445,6 @@ async function handleVoiceMenu(ctx, save, hasLocal) {
38309
38445
  continue;
38310
38446
  }
38311
38447
  case "voice": {
38312
- ctx.setOverlayActive?.(true);
38313
38448
  const voiceItems = [
38314
38449
  { key: "header-onnx", label: selectColors.dim("\u2500\u2500\u2500 ONNX Voices \u2500\u2500\u2500") },
38315
38450
  { key: "glados", label: "GLaDOS", detail: "Portal AI voice (ONNX)" },
@@ -38330,15 +38465,13 @@ async function handleVoiceMenu(ctx, save, hasLocal) {
38330
38465
  availableRows: ctx.availableContentRows?.(),
38331
38466
  skipKeys: ["header-onnx", "header-mlx", "header-clone"]
38332
38467
  });
38333
- ctx.setOverlayActive?.(false);
38334
38468
  if (voiceResult.confirmed && voiceResult.key) {
38335
- const msg = await ctx.voiceSetModel(voiceResult.key);
38469
+ await ctx.voiceSetModel(voiceResult.key);
38336
38470
  save({ voice: true, voiceModel: voiceResult.key });
38337
38471
  }
38338
38472
  continue;
38339
38473
  }
38340
38474
  case "system": {
38341
- ctx.setOverlayActive?.(true);
38342
38475
  const sysItems = [
38343
38476
  { key: "onnx", label: "Piper ONNX", detail: "cross-platform, fast, offline" },
38344
38477
  { key: "luxtts", label: "LuxTTS", detail: "voice cloning, Python venv" },
@@ -38355,10 +38488,8 @@ async function handleVoiceMenu(ctx, save, hasLocal) {
38355
38488
  availableRows: ctx.availableContentRows?.(),
38356
38489
  skipKeys: ["header-import"]
38357
38490
  });
38358
- ctx.setOverlayActive?.(false);
38359
38491
  if (sysResult.confirmed && sysResult.key) {
38360
38492
  if (sysResult.key === "import-onnx") {
38361
- ctx.setOverlayActive?.(true);
38362
38493
  const onnxDrop = await showDropPanel({
38363
38494
  title: "Import Piper Voice \u2014 Drop .onnx File",
38364
38495
  instruction: "Drag and drop the .onnx model file",
@@ -38366,11 +38497,9 @@ async function handleVoiceMenu(ctx, save, hasLocal) {
38366
38497
  typeLabel: "ONNX model",
38367
38498
  rl: ctx.rl
38368
38499
  });
38369
- ctx.setOverlayActive?.(false);
38370
38500
  if (!onnxDrop.confirmed || !onnxDrop.path) {
38371
38501
  continue;
38372
38502
  }
38373
- ctx.setOverlayActive?.(true);
38374
38503
  const jsonDrop = await showDropPanel({
38375
38504
  title: "Import Piper Voice \u2014 Drop .onnx.json Config",
38376
38505
  instruction: "Drag and drop the .onnx.json config file for the model",
@@ -38378,7 +38507,6 @@ async function handleVoiceMenu(ctx, save, hasLocal) {
38378
38507
  typeLabel: "JSON config",
38379
38508
  rl: ctx.rl
38380
38509
  });
38381
- ctx.setOverlayActive?.(false);
38382
38510
  if (!jsonDrop.confirmed || !jsonDrop.path) {
38383
38511
  continue;
38384
38512
  }
@@ -38409,7 +38537,6 @@ async function handleVoiceMenu(ctx, save, hasLocal) {
38409
38537
  continue;
38410
38538
  }
38411
38539
  case "clone": {
38412
- ctx.setOverlayActive?.(true);
38413
38540
  const dropResult = await showDropPanel({
38414
38541
  title: "Voice Clone \u2014 Drop Audio File",
38415
38542
  instruction: "Drag and drop an audio file to use as voice clone reference",
@@ -38417,20 +38544,15 @@ async function handleVoiceMenu(ctx, save, hasLocal) {
38417
38544
  typeLabel: "Audio files",
38418
38545
  rl: ctx.rl
38419
38546
  });
38420
- ctx.setOverlayActive?.(false);
38421
38547
  if (dropResult.confirmed && dropResult.path && ctx.voiceClone) {
38422
38548
  await ctx.voiceClone(dropResult.path);
38423
38549
  const newFilename = ctx.voiceGetLastClonedFilename?.();
38424
- ctx.setOverlayActive?.(true);
38425
38550
  await handleVoiceList(ctx, newFilename ?? void 0);
38426
- ctx.setOverlayActive?.(false);
38427
38551
  }
38428
38552
  continue;
38429
38553
  }
38430
38554
  case "list": {
38431
- ctx.setOverlayActive?.(true);
38432
38555
  await handleVoiceList(ctx);
38433
- ctx.setOverlayActive?.(false);
38434
38556
  continue;
38435
38557
  }
38436
38558
  }
@@ -46312,7 +46434,7 @@ var init_braille_spinner = __esm({
46312
46434
  });
46313
46435
 
46314
46436
  // packages/cli/dist/tui/system-metrics.js
46315
- import { loadavg as loadavg2, cpus as cpus2, totalmem as totalmem2, freemem as freemem2, platform as platform4 } from "node:os";
46437
+ import { loadavg as loadavg2, cpus as cpus2, totalmem as totalmem3, freemem as freemem2, platform as platform4 } from "node:os";
46316
46438
  import { exec as exec3 } from "node:child_process";
46317
46439
  import { readFile as readFile16 } from "node:fs/promises";
46318
46440
  function formatRate(bytesPerSec) {
@@ -46446,7 +46568,7 @@ function collectCpuRam() {
46446
46568
  const [l1] = loadavg2();
46447
46569
  const cores = cpus2().length;
46448
46570
  const cpuModel = cpus2()[0]?.model ?? "";
46449
- const totalMem = totalmem2();
46571
+ const totalMem = totalmem3();
46450
46572
  const usedMem = totalMem - freemem2();
46451
46573
  return {
46452
46574
  cpuUtil: Math.min(100, Math.round(l1 / cores * 100)),
@@ -48410,14 +48532,18 @@ function startTask(task, config, repoRoot, voice, stream, taskStores, bruteForce
48410
48532
  }
48411
48533
  backend = new CascadeBackend(config.model, [primaryEndpoint, ...fallbackEndpoints], {
48412
48534
  onSwitch: (_from, to, reason) => {
48413
- process.stderr.write(`
48535
+ const msg = `
48414
48536
  [cascade] Failover \u2192 ${to.label ?? to.url}: ${reason}
48415
- `);
48537
+ `;
48538
+ if (!bufferIfOverlay("stderr", msg))
48539
+ process.stderr.write(msg);
48416
48540
  },
48417
48541
  onProbeResult: (ep, success) => {
48418
48542
  if (success) {
48419
- process.stderr.write(`[cascade] Primary recovered: ${ep.label ?? ep.url}
48420
- `);
48543
+ const msg = `[cascade] Primary recovered: ${ep.label ?? ep.url}
48544
+ `;
48545
+ if (!bufferIfOverlay("stderr", msg))
48546
+ process.stderr.write(msg);
48421
48547
  }
48422
48548
  }
48423
48549
  });
@@ -48657,8 +48783,22 @@ ${entry.fullContent}`
48657
48783
  let streamStartMs = 0;
48658
48784
  let streamTextBuffer = "";
48659
48785
  const contentWrite = (fn) => {
48660
- if (overlayActive)
48786
+ if (isOverlayActive()) {
48787
+ const origWrite = process.stdout.write;
48788
+ const chunks = [];
48789
+ process.stdout.write = ((chunk) => {
48790
+ chunks.push(chunk);
48791
+ return true;
48792
+ });
48793
+ try {
48794
+ fn();
48795
+ } finally {
48796
+ process.stdout.write = origWrite;
48797
+ }
48798
+ for (const chunk of chunks)
48799
+ bufferIfOverlay("stdout", chunk);
48661
48800
  return;
48801
+ }
48662
48802
  if (statusBar?.isActive) {
48663
48803
  statusBar.beginContentWrite();
48664
48804
  fn();
@@ -49410,8 +49550,22 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
49410
49550
  }
49411
49551
  }
49412
49552
  function writeContent(fn) {
49413
- if (overlayActive)
49553
+ if (isOverlayActive()) {
49554
+ const origWrite = process.stdout.write;
49555
+ const chunks = [];
49556
+ process.stdout.write = ((chunk) => {
49557
+ chunks.push(chunk);
49558
+ return true;
49559
+ });
49560
+ try {
49561
+ fn();
49562
+ } finally {
49563
+ process.stdout.write = origWrite;
49564
+ }
49565
+ for (const chunk of chunks)
49566
+ bufferIfOverlay("stdout", chunk);
49414
49567
  return;
49568
+ }
49415
49569
  if (statusBar.isActive) {
49416
49570
  statusBar.beginContentWrite();
49417
49571
  try {
@@ -49749,7 +49903,10 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
49749
49903
  return voiceEngine.lastClonedFilename;
49750
49904
  },
49751
49905
  setOverlayActive(active) {
49752
- overlayActive = active;
49906
+ if (active)
49907
+ enterOverlay();
49908
+ else
49909
+ leaveOverlay();
49753
49910
  },
49754
49911
  streamToggle() {
49755
49912
  streamEnabled = !streamEnabled;
@@ -51439,7 +51596,7 @@ async function runWithTUI(task, config, repoPath) {
51439
51596
  process.exit(1);
51440
51597
  }
51441
51598
  }
51442
- var overlayActive, taskManager;
51599
+ var taskManager;
51443
51600
  var init_interactive = __esm({
51444
51601
  "packages/cli/dist/tui/interactive.js"() {
51445
51602
  "use strict";
@@ -51475,7 +51632,7 @@ var init_interactive = __esm({
51475
51632
  init_telegram_bridge();
51476
51633
  init_status_bar();
51477
51634
  init_dist6();
51478
- overlayActive = false;
51635
+ init_overlay_lock();
51479
51636
  taskManager = new BackgroundTaskManager();
51480
51637
  }
51481
51638
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.103.99",
3
+ "version": "0.104.2",
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",