open-agents-ai 0.103.78 → 0.103.80

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 +102 -12
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -19134,7 +19134,8 @@ If you're stuck, try a completely different approach. Do NOT repeat what failed
19134
19134
  if (!choice)
19135
19135
  break;
19136
19136
  const msg = choice.message;
19137
- const isEmptyResponse = (!msg.content || !msg.content.trim()) && (!msg.toolCalls || msg.toolCalls.length === 0);
19137
+ const isThinkOnly = response._thinkOnly === true;
19138
+ const isEmptyResponse = !isThinkOnly && (!msg.content || !msg.content.trim()) && (!msg.toolCalls || msg.toolCalls.length === 0);
19138
19139
  if (isEmptyResponse) {
19139
19140
  consecutiveEmptyResponses++;
19140
19141
  if (consecutiveEmptyResponses >= 2) {
@@ -20970,6 +20971,8 @@ ${transcript}`
20970
20971
  }
20971
20972
  this.emit({ type: "stream_end", content, turn, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
20972
20973
  const cleanContent = content.replace(/<think>[\s\S]*?<\/think>/g, "").trim();
20974
+ const hadThinking = content.includes("<think>") && content.includes("</think>");
20975
+ const thinkOnlyResponse = hadThinking && !cleanContent;
20973
20976
  const toolCalls = toolCallAccumulators.size > 0 ? Array.from(toolCallAccumulators.values()).map((tc) => {
20974
20977
  let args;
20975
20978
  try {
@@ -20980,10 +20983,20 @@ ${transcript}`
20980
20983
  }
20981
20984
  return { id: tc.id, name: tc.name, arguments: args };
20982
20985
  }) : void 0;
20983
- return {
20984
- choices: [{ message: { content: cleanContent || null, toolCalls } }],
20986
+ const resp = {
20987
+ choices: [{
20988
+ message: {
20989
+ // For think-only responses, include a marker so the model knows it already
20990
+ // thought about this (prevents infinite think loops without visible output)
20991
+ content: thinkOnlyResponse ? "[Your previous response was internal reasoning only. Now respond with visible text or a tool call.]" : cleanContent || null,
20992
+ toolCalls
20993
+ }
20994
+ }],
20985
20995
  usage: streamUsage
20986
20996
  };
20997
+ if (thinkOnlyResponse)
20998
+ resp._thinkOnly = true;
20999
+ return resp;
20987
21000
  }
20988
21001
  };
20989
21002
  OllamaAgenticBackend = class {
@@ -30706,7 +30719,8 @@ async function fetchOllamaModels(baseUrl) {
30706
30719
  sizeBytes: m.size,
30707
30720
  modified: formatRelativeTime(m.modified_at),
30708
30721
  parameterSize: m.details?.parameter_size,
30709
- contextLength: void 0
30722
+ contextLength: void 0,
30723
+ caps: void 0
30710
30724
  })).sort((a, b) => b.sizeBytes - a.sizeBytes);
30711
30725
  const normalized = normalizeBaseUrl(baseUrl);
30712
30726
  const showResults = await Promise.allSettled(result.map((m) => fetch(`${normalized}/api/show`, {
@@ -30735,6 +30749,34 @@ async function fetchOllamaModels(baseUrl) {
30735
30749
  }
30736
30750
  }
30737
30751
  }
30752
+ const modelCaps = { vision: false, toolUse: false, thinking: false };
30753
+ const nameLower = result[i].name.toLowerCase();
30754
+ if (Array.isArray(show.capabilities)) {
30755
+ if (show.capabilities.includes("vision"))
30756
+ modelCaps.vision = true;
30757
+ if (show.capabilities.includes("tools"))
30758
+ modelCaps.toolUse = true;
30759
+ if (show.capabilities.includes("thinking"))
30760
+ modelCaps.thinking = true;
30761
+ }
30762
+ if (show.model_info) {
30763
+ for (const key of Object.keys(show.model_info)) {
30764
+ const k = key.toLowerCase();
30765
+ if (k.includes("vision.block_count") || k.includes("clip.") || k.includes("image_token_id") || k.includes("projector")) {
30766
+ const val = show.model_info[key];
30767
+ if (val !== null && val !== void 0 && val !== 0 && val !== "") {
30768
+ modelCaps.vision = true;
30769
+ }
30770
+ }
30771
+ }
30772
+ }
30773
+ if (/qwen3|qwen2\.5|llama3\.[13]|mistral|mixtral|command-r|gemma3|devstral|deepseek/.test(nameLower)) {
30774
+ modelCaps.toolUse = true;
30775
+ }
30776
+ if (show.template && (show.template.includes("<think>") || show.template.includes("thinking"))) {
30777
+ modelCaps.thinking = true;
30778
+ }
30779
+ result[i].caps = modelCaps;
30738
30780
  }
30739
30781
  return result;
30740
30782
  }
@@ -31107,6 +31149,16 @@ function formatContextLength(tokens) {
31107
31149
  return `${Math.round(tokens / 1024)}K ctx`;
31108
31150
  return `${tokens} ctx`;
31109
31151
  }
31152
+ function formatCaps(caps) {
31153
+ const tags = [];
31154
+ if (caps.vision)
31155
+ tags.push("vision");
31156
+ if (caps.toolUse)
31157
+ tags.push("tools");
31158
+ if (caps.thinking)
31159
+ tags.push("think");
31160
+ return tags.join("+");
31161
+ }
31110
31162
  function formatRelativeTime(iso) {
31111
31163
  const now = Date.now();
31112
31164
  const then = new Date(iso).getTime();
@@ -35240,10 +35292,11 @@ async function showModelPicker(ctx, local = false) {
35240
35292
  const available = liveModelNames.has(h.value) ? "" : c2.yellow(" [offline]");
35241
35293
  const meta = modelMap.get(h.value);
35242
35294
  const ctx2 = meta?.contextLength ? ` ${formatContextLength(meta.contextLength)}` : "";
35295
+ const capStr = meta?.caps ? ` ${formatCaps(meta.caps)}` : "";
35243
35296
  items.push({
35244
35297
  key: h.value,
35245
35298
  label: h.value,
35246
- detail: `${uses}${ctx2}${available}`
35299
+ detail: `${uses}${ctx2}${capStr}${available}`
35247
35300
  });
35248
35301
  }
35249
35302
  items.push({ key: "__header_available__", label: c2.dim("\u2500\u2500 Available \u2500\u2500"), detail: "" });
@@ -35253,10 +35306,11 @@ async function showModelPicker(ctx, local = false) {
35253
35306
  if (history.length > 0 && historyKeys.has(m.name))
35254
35307
  continue;
35255
35308
  const ctx2 = m.contextLength ? formatContextLength(m.contextLength) : "";
35309
+ const capStr = m.caps ? formatCaps(m.caps) : "";
35256
35310
  items.push({
35257
35311
  key: m.name,
35258
35312
  label: m.name,
35259
- detail: [m.parameterSize, ctx2, m.size, m.modified].filter(Boolean).join(" ")
35313
+ detail: [m.parameterSize, ctx2, capStr, m.modified].filter(Boolean).join(" ")
35260
35314
  });
35261
35315
  }
35262
35316
  const result = await tuiSelect({
@@ -39430,6 +39484,23 @@ Error: ${err2 instanceof Error ? err2.message : String(err2)}`);
39430
39484
  if (existsSync34(venvPy)) {
39431
39485
  try {
39432
39486
  execSync27(`${venvPy} -c "import sys; sys.path.insert(0, '${luxttsRepoDir()}'); from zipvoice.luxvoice import LuxTTS; print('ok')"`, { stdio: "pipe", timeout: 3e4 });
39487
+ let hasCudaSys = false;
39488
+ try {
39489
+ execSync27("nvidia-smi", { stdio: "pipe", timeout: 5e3 });
39490
+ hasCudaSys = true;
39491
+ } catch {
39492
+ }
39493
+ if (hasCudaSys) {
39494
+ try {
39495
+ const torchCheck = execSync27(`${venvPy} -c "import torch; print('cuda' if torch.cuda.is_available() else 'cpu')"`, { stdio: "pipe", timeout: 15e3 }).toString().trim();
39496
+ if (torchCheck === "cpu") {
39497
+ renderWarning("GPU detected but PyTorch is CPU-only. Reinstalling with CUDA support...");
39498
+ execSync27(`${JSON.stringify(venvPy)} -m pip install --quiet --force-reinstall torch torchaudio --index-url https://download.pytorch.org/whl/cu124`, { stdio: "pipe", timeout: 6e5 });
39499
+ renderInfo("PyTorch reinstalled with CUDA GPU support.");
39500
+ }
39501
+ } catch {
39502
+ }
39503
+ }
39433
39504
  this.writeLuxttsInferScript();
39434
39505
  this.autoDetectCloneRef();
39435
39506
  return;
@@ -39449,14 +39520,33 @@ Error: ${err2 instanceof Error ? err2.message : String(err2)}`);
39449
39520
  throw new Error(`Failed to create venv: ${err instanceof Error ? err.message : String(err)}`);
39450
39521
  }
39451
39522
  }
39452
- renderInfo(" Installing PyTorch (CPU)...");
39523
+ let hasCuda = false;
39453
39524
  try {
39454
- execSync27(`${JSON.stringify(venvPy)} -m pip install --quiet torch torchaudio --index-url https://download.pytorch.org/whl/cpu`, { stdio: "pipe", timeout: 6e5 });
39455
- } catch (err) {
39525
+ execSync27("nvidia-smi", { stdio: "pipe", timeout: 5e3 });
39526
+ hasCuda = true;
39527
+ } catch {
39528
+ }
39529
+ if (hasCuda) {
39530
+ renderInfo(" Installing PyTorch (CUDA GPU)...");
39456
39531
  try {
39457
- execSync27(`${JSON.stringify(venvPy)} -m pip install --quiet torch torchaudio`, { stdio: "pipe", timeout: 6e5 });
39458
- } catch (err2) {
39459
- throw new Error(`Failed to install PyTorch: ${err2 instanceof Error ? err2.message : String(err2)}`);
39532
+ execSync27(`${JSON.stringify(venvPy)} -m pip install --quiet torch torchaudio --index-url https://download.pytorch.org/whl/cu124`, { stdio: "pipe", timeout: 6e5 });
39533
+ } catch {
39534
+ try {
39535
+ execSync27(`${JSON.stringify(venvPy)} -m pip install --quiet torch torchaudio`, { stdio: "pipe", timeout: 6e5 });
39536
+ } catch (err2) {
39537
+ throw new Error(`Failed to install PyTorch (CUDA): ${err2 instanceof Error ? err2.message : String(err2)}`);
39538
+ }
39539
+ }
39540
+ } else {
39541
+ renderInfo(" Installing PyTorch (CPU \u2014 no NVIDIA GPU detected)...");
39542
+ try {
39543
+ execSync27(`${JSON.stringify(venvPy)} -m pip install --quiet torch torchaudio --index-url https://download.pytorch.org/whl/cpu`, { stdio: "pipe", timeout: 6e5 });
39544
+ } catch {
39545
+ try {
39546
+ execSync27(`${JSON.stringify(venvPy)} -m pip install --quiet torch torchaudio`, { stdio: "pipe", timeout: 6e5 });
39547
+ } catch (err2) {
39548
+ throw new Error(`Failed to install PyTorch: ${err2 instanceof Error ? err2.message : String(err2)}`);
39549
+ }
39460
39550
  }
39461
39551
  }
39462
39552
  const repoDir = luxttsRepoDir();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.103.78",
3
+ "version": "0.103.80",
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",