open-agents-ai 0.64.0 → 0.66.0

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 +109 -14
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -13801,7 +13801,7 @@ Rules:
13801
13801
  const keepRecentDivisor = tier === "small" ? 2e3 : tier === "medium" ? 3e3 : 4e3;
13802
13802
  keepRecent = Math.max(4, Math.min(keepRecentMax, Math.floor(ctx / keepRecentDivisor)));
13803
13803
  } else {
13804
- keepRecent = deep ? 20 : 12;
13804
+ keepRecent = deep ? tier === "small" ? 8 : tier === "medium" ? 14 : 20 : tier === "small" ? 4 : tier === "medium" ? 6 : 12;
13805
13805
  }
13806
13806
  const maxOutputTokens = ctx > 0 ? Math.min(this.options.maxTokens, Math.max(2048, Math.floor(ctx * 0.25))) : this.options.maxTokens;
13807
13807
  const toolOutputCeiling = deep ? 16e3 : 8e3;
@@ -14088,7 +14088,14 @@ Integrate this guidance into your current approach. Continue working on the task
14088
14088
  const choiceContent = response.choices[0]?.message?.content ?? "";
14089
14089
  const choiceArgs = response.choices[0]?.message?.toolCalls?.map((tc) => JSON.stringify(tc.arguments)).join("") ?? "";
14090
14090
  estimatedTokens += Math.ceil((choiceContent.length + choiceArgs.length) / 4);
14091
- const estimatedContextTokens = Math.ceil(compacted.reduce((sum, m) => sum + (typeof m.content === "string" ? m.content.length : 100), 0) / 4);
14091
+ const estimatedContextTokens = Math.ceil(compacted.reduce((sum, m) => {
14092
+ let chars = typeof m.content === "string" ? m.content.length : 100;
14093
+ if (m.tool_calls) {
14094
+ for (const tc of m.tool_calls)
14095
+ chars += tc.function.arguments?.length ?? 0;
14096
+ }
14097
+ return sum + chars;
14098
+ }, 0) / 4);
14092
14099
  this.emit({
14093
14100
  type: "token_usage",
14094
14101
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
@@ -14386,7 +14393,14 @@ Integrate this guidance into your current approach. Continue working on the task
14386
14393
  const choiceContent2 = response.choices[0]?.message?.content ?? "";
14387
14394
  const choiceArgs2 = response.choices[0]?.message?.toolCalls?.map((tc) => JSON.stringify(tc.arguments)).join("") ?? "";
14388
14395
  estimatedTokens += Math.ceil((choiceContent2.length + choiceArgs2.length) / 4);
14389
- const bfEstCtx = Math.ceil(compactedMsgs.reduce((sum, m) => sum + (typeof m.content === "string" ? m.content.length : 100), 0) / 4);
14396
+ const bfEstCtx = Math.ceil(compactedMsgs.reduce((sum, m) => {
14397
+ let chars = typeof m.content === "string" ? m.content.length : 100;
14398
+ if (m.tool_calls) {
14399
+ for (const tc of m.tool_calls)
14400
+ chars += tc.function.arguments?.length ?? 0;
14401
+ }
14402
+ return sum + chars;
14403
+ }, 0) / 4);
14390
14404
  this.emit({
14391
14405
  type: "token_usage",
14392
14406
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
@@ -14659,12 +14673,19 @@ ${tail}`;
14659
14673
  if (messages.length < 3)
14660
14674
  return messages;
14661
14675
  const totalChars = messages.reduce((sum, m) => {
14676
+ let chars = 0;
14662
14677
  if (typeof m.content === "string")
14663
- return sum + m.content.length;
14664
- if (Array.isArray(m.content)) {
14665
- return sum + m.content.reduce((s, p) => s + (p.text?.length || 0) + (p.image_url ? 1e3 : 0), 0);
14678
+ chars += m.content.length;
14679
+ else if (Array.isArray(m.content)) {
14680
+ chars += m.content.reduce((s, p) => s + (p.text?.length || 0) + (p.image_url ? 1e3 : 0), 0);
14681
+ }
14682
+ if (m.tool_calls) {
14683
+ for (const tc of m.tool_calls) {
14684
+ chars += tc.function.arguments?.length ?? 0;
14685
+ chars += tc.function.name?.length ?? 0;
14686
+ }
14666
14687
  }
14667
- return sum;
14688
+ return sum + chars;
14668
14689
  }, 0);
14669
14690
  const estimatedTokens = totalChars / 4;
14670
14691
  const limits = this.contextLimits();
@@ -14758,7 +14779,34 @@ ${fullSummary}
14758
14779
  [Continue from the recent context below. Do not repeat work already completed above.]`
14759
14780
  };
14760
14781
  this.persistCheckpoint(fullSummary);
14761
- const result = [...head, compactionMsg, ...recent];
14782
+ let result = [...head, compactionMsg, ...recent];
14783
+ const ctxWindow = this.options.contextWindowSize;
14784
+ if (ctxWindow > 0) {
14785
+ const estimateResult = (msgs) => msgs.reduce((sum, m) => {
14786
+ let chars = typeof m.content === "string" ? m.content.length : 100;
14787
+ if (m.tool_calls) {
14788
+ for (const tc of m.tool_calls)
14789
+ chars += tc.function.arguments?.length ?? 0;
14790
+ }
14791
+ return sum + chars;
14792
+ }, 0) / 4;
14793
+ const safetyTarget = Math.floor(ctxWindow * 0.65);
14794
+ let trimmedRecent = [...recent];
14795
+ while (estimateResult(result) > safetyTarget && trimmedRecent.length > 2) {
14796
+ trimmedRecent = trimmedRecent.slice(1);
14797
+ while (trimmedRecent.length > 1 && trimmedRecent[0]?.role === "tool") {
14798
+ trimmedRecent = trimmedRecent.slice(1);
14799
+ }
14800
+ result = [...head, compactionMsg, ...trimmedRecent];
14801
+ }
14802
+ if (trimmedRecent.length < recent.length) {
14803
+ this.emit({
14804
+ type: "status",
14805
+ content: `Post-compaction trim: reduced recent from ${recent.length} to ${trimmedRecent.length} messages to fit ${ctxWindow.toLocaleString()}-token context window`,
14806
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
14807
+ });
14808
+ }
14809
+ }
14762
14810
  if (result.length < 2)
14763
14811
  return messages;
14764
14812
  return result;
@@ -25423,14 +25471,39 @@ async function handleUpdate(subcommand, ctx) {
25423
25471
  async function switchModel(query, ctx, local = false) {
25424
25472
  try {
25425
25473
  const models = await fetchModels(ctx.config.backendUrl, ctx.config.apiKey);
25426
- const match = findModel(models, query);
25474
+ let match = findModel(models, query);
25427
25475
  if (!match) {
25428
- renderError(`Model not found: "${query}"`);
25429
- renderInfo("Available models:");
25430
- for (const m of models.slice(0, 10)) {
25431
- renderInfo(` ${m.name}`);
25476
+ if (ctx.config.backendType === "ollama") {
25477
+ if (!/^[a-zA-Z0-9][a-zA-Z0-9._:/-]*$/.test(query)) {
25478
+ renderError(`Invalid model name: "${query}"`);
25479
+ return;
25480
+ }
25481
+ renderInfo(`Model "${query}" not found locally. Pulling from Ollama registry...`);
25482
+ try {
25483
+ pullModelWithAutoUpdate(query);
25484
+ const refreshedModels = await fetchModels(ctx.config.backendUrl, ctx.config.apiKey);
25485
+ match = findModel(refreshedModels, query);
25486
+ if (!match) {
25487
+ renderError(`Model "${query}" was pulled but could not be found.`);
25488
+ return;
25489
+ }
25490
+ } catch {
25491
+ renderError(`Model "${query}" could not be pulled from Ollama registry.`);
25492
+ renderInfo("Check that the model name is correct: https://ollama.com/library");
25493
+ renderInfo("Available local models:");
25494
+ for (const m of models.slice(0, 10)) {
25495
+ renderInfo(` ${m.name}`);
25496
+ }
25497
+ return;
25498
+ }
25499
+ } else {
25500
+ renderError(`Model not found: "${query}"`);
25501
+ renderInfo("Available models:");
25502
+ for (const m of models.slice(0, 10)) {
25503
+ renderInfo(` ${m.name}`);
25504
+ }
25505
+ return;
25432
25506
  }
25433
- return;
25434
25507
  }
25435
25508
  let finalModel = match.name;
25436
25509
  if (ctx.config.backendType === "ollama") {
@@ -25464,6 +25537,7 @@ async function switchModel(query, ctx, local = false) {
25464
25537
  const caps = await queryModelCapabilities(ctx.config.backendUrl, finalModel);
25465
25538
  ctx.setCapabilities(caps);
25466
25539
  }
25540
+ ctx.refreshModelCache?.();
25467
25541
  } catch (err) {
25468
25542
  renderError(`Failed to switch model: ${err instanceof Error ? err.message : String(err)}`);
25469
25543
  }
@@ -35012,6 +35086,9 @@ async function startInteractive(config, repoPath) {
35012
35086
  if (ctxSize) {
35013
35087
  resolvedContextWindowSize = ctxSize;
35014
35088
  statusBar.setContextWindowSize(ctxSize);
35089
+ if (activeTask) {
35090
+ activeTask.runner.setContextWindowSize(ctxSize);
35091
+ }
35015
35092
  }
35016
35093
  }).catch(() => {
35017
35094
  });
@@ -35239,9 +35316,26 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
35239
35316
  ];
35240
35317
  const discoveredSkillNames = discoverSkills(repoRoot).map((s) => `/${s.name}`);
35241
35318
  const allCompletions = [.../* @__PURE__ */ new Set([...BUILTIN_COMMANDS, ...discoveredSkillNames])].sort();
35319
+ let cachedModelNames = [];
35320
+ function refreshModelCache() {
35321
+ fetchModels(config.backendUrl, config.apiKey).then((models) => {
35322
+ cachedModelNames = models.map((m) => m.name);
35323
+ }).catch(() => {
35324
+ });
35325
+ }
35326
+ refreshModelCache();
35242
35327
  function completer(line) {
35243
35328
  if (!line.startsWith("/"))
35244
35329
  return [[], line];
35330
+ const modelMatch = line.match(/^\/model\s+(.*)$/i);
35331
+ if (modelMatch) {
35332
+ const partial = modelMatch[1].toLowerCase();
35333
+ const hits2 = cachedModelNames.filter((n) => n.toLowerCase().startsWith(partial));
35334
+ if (hits2.length === 0 && partial.length > 0) {
35335
+ return [[modelMatch[1]], modelMatch[1]];
35336
+ }
35337
+ return [hits2, modelMatch[1]];
35338
+ }
35245
35339
  const lower = line.toLowerCase();
35246
35340
  const hits = allCompletions.filter((c3) => c3.toLowerCase().startsWith(lower));
35247
35341
  return [hits, line];
@@ -35914,6 +36008,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
35914
36008
  resolvedCaps = caps;
35915
36009
  statusBar.setCapabilities(caps);
35916
36010
  },
36011
+ refreshModelCache,
35917
36012
  hasActiveTask: () => activeTask !== null,
35918
36013
  requestCompaction(strategy) {
35919
36014
  if (!activeTask)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.64.0",
3
+ "version": "0.66.0",
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",