open-agents-ai 0.31.2 → 0.31.3

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 +64 -26
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -11012,6 +11012,39 @@ Rules:
11012
11012
  setContextWindowSize(size) {
11013
11013
  this.options.contextWindowSize = size;
11014
11014
  }
11015
+ // -------------------------------------------------------------------------
11016
+ // Context-aware limits — all dynamic values derived from contextWindowSize
11017
+ // and modelTier. Call this instead of using hardcoded magic numbers.
11018
+ // -------------------------------------------------------------------------
11019
+ /**
11020
+ * Compute all context-dependent limits from the current contextWindowSize
11021
+ * and modelTier. Returns sensible defaults when contextWindowSize is 0.
11022
+ */
11023
+ contextLimits() {
11024
+ const ctx = this.options.contextWindowSize;
11025
+ const tier = this.options.modelTier ?? "large";
11026
+ const compactionThreshold = ctx > 0 ? Math.min(this.options.compactionThreshold, Math.floor(ctx * 0.75)) : this.options.compactionThreshold;
11027
+ const keepRecentDivisor = tier === "small" ? 2e3 : tier === "medium" ? 3e3 : 4e3;
11028
+ const keepRecent = ctx > 0 ? Math.max(4, Math.min(12, Math.floor(ctx / keepRecentDivisor))) : 12;
11029
+ const maxOutputTokens = ctx > 0 ? Math.min(this.options.maxTokens, Math.max(2048, Math.floor(ctx * 0.25))) : this.options.maxTokens;
11030
+ const toolOutputMaxChars = ctx > 0 ? Math.max(2e3, Math.min(8e3, Math.floor(ctx * 0.5))) : 8e3;
11031
+ const foldLineThreshold = tier === "small" ? 30 : tier === "medium" ? 35 : 40;
11032
+ const foldHeadLines = tier === "small" ? 12 : tier === "medium" ? 16 : 20;
11033
+ const foldTailLines = tier === "small" ? 5 : tier === "medium" ? 8 : 10;
11034
+ const maxSummaryChars = ctx > 0 ? Math.max(2e3, Math.min(8e3, Math.floor(ctx * 0.2))) : 4e3;
11035
+ const repetitionWindow = tier === "small" ? 6 : tier === "medium" ? 8 : 10;
11036
+ return {
11037
+ compactionThreshold,
11038
+ keepRecent,
11039
+ maxOutputTokens,
11040
+ toolOutputMaxChars,
11041
+ foldLineThreshold,
11042
+ foldHeadLines,
11043
+ foldTailLines,
11044
+ maxSummaryChars,
11045
+ repetitionWindow
11046
+ };
11047
+ }
11015
11048
  /** Register a tool for the agent to use */
11016
11049
  registerTool(tool) {
11017
11050
  this.tools.set(tool.name, tool);
@@ -11093,7 +11126,8 @@ Rules:
11093
11126
  detectRepetition(recentToolCalls) {
11094
11127
  if (recentToolCalls.length < 4)
11095
11128
  return 0;
11096
- const window = recentToolCalls.slice(-8);
11129
+ const { repetitionWindow } = this.contextLimits();
11130
+ const window = recentToolCalls.slice(-repetitionWindow);
11097
11131
  const uniqueKeys = new Set(window.map((tc) => `${tc.name}:${tc.argsKey}`));
11098
11132
  const ratio = 1 - uniqueKeys.size / window.length;
11099
11133
  return ratio;
@@ -11213,8 +11247,7 @@ Integrate this guidance into your current approach. Continue working on the task
11213
11247
  });
11214
11248
  }
11215
11249
  const compacted = this.compactMessages(messages);
11216
- const ctxWindow = this.options.contextWindowSize;
11217
- const effectiveMaxTokens = ctxWindow > 0 ? Math.min(this.options.maxTokens, Math.max(2048, Math.floor(ctxWindow * 0.25))) : this.options.maxTokens;
11250
+ const { maxOutputTokens: effectiveMaxTokens } = this.contextLimits();
11218
11251
  const chatRequest = {
11219
11252
  messages: compacted,
11220
11253
  tools: toolDefs,
@@ -11313,8 +11346,7 @@ Integrate this guidance into your current approach. Continue working on the task
11313
11346
  }
11314
11347
  }
11315
11348
  }
11316
- const ctxW = this.options.contextWindowSize;
11317
- const maxLen = ctxW > 0 ? Math.max(2e3, Math.min(8e3, Math.floor(ctxW * 0.5))) : 8e3;
11349
+ const { toolOutputMaxChars: maxLen } = this.contextLimits();
11318
11350
  const output = result.success ? result.output.length > maxLen ? this.foldOutput(result.output, maxLen) : result.output : `Error: ${result.error || "unknown error"}
11319
11351
  ${result.output.length > maxLen ? this.foldOutput(result.output, maxLen) : result.output}`;
11320
11352
  this.emit({
@@ -11527,8 +11559,8 @@ Integrate this guidance into your current approach. Continue working on the task
11527
11559
  }
11528
11560
  }
11529
11561
  }
11530
- const maxLen = 8e3;
11531
- const output = result.success ? result.output.length > maxLen ? result.output.slice(0, maxLen) + `
11562
+ const { toolOutputMaxChars: maxLen2 } = this.contextLimits();
11563
+ const output = result.success ? result.output.length > maxLen2 ? result.output.slice(0, maxLen2) + `
11532
11564
  ...(truncated)` : result.output : `Error: ${result.error || "unknown error"}
11533
11565
  ${result.output}`;
11534
11566
  this.emit({ type: "tool_result", toolName: tc.name, content: output.slice(0, 200), success: result.success, turn, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
@@ -11629,11 +11661,12 @@ ${marker}` : marker);
11629
11661
  // -------------------------------------------------------------------------
11630
11662
  foldOutput(output, maxChars) {
11631
11663
  const lines = output.split("\n");
11632
- if (lines.length <= 40) {
11664
+ const { foldLineThreshold, foldHeadLines, foldTailLines } = this.contextLimits();
11665
+ if (lines.length <= foldLineThreshold) {
11633
11666
  return output.slice(0, maxChars) + "\n...(truncated)";
11634
11667
  }
11635
- const headLines = 20;
11636
- const tailLines = 10;
11668
+ const headLines = foldHeadLines;
11669
+ const tailLines = foldTailLines;
11637
11670
  const head = lines.slice(0, headLines).join("\n");
11638
11671
  const tail = lines.slice(-tailLines).join("\n");
11639
11672
  const omitted = lines.length - headLines - tailLines;
@@ -11662,13 +11695,11 @@ ${tail}`;
11662
11695
  return sum;
11663
11696
  }, 0);
11664
11697
  const estimatedTokens = totalChars / 4;
11665
- const ctxWinThreshold = this.options.contextWindowSize > 0 ? Math.floor(this.options.contextWindowSize * 0.75) : Infinity;
11666
- const effectiveThreshold = Math.min(this.options.compactionThreshold, ctxWinThreshold);
11667
- if (estimatedTokens < effectiveThreshold) {
11698
+ const limits = this.contextLimits();
11699
+ if (estimatedTokens < limits.compactionThreshold) {
11668
11700
  return messages;
11669
11701
  }
11670
- const ctxWin = this.options.contextWindowSize;
11671
- const keepRecent = ctxWin > 0 ? Math.max(4, Math.min(12, Math.floor(ctxWin / 4e3))) : 12;
11702
+ const keepRecent = limits.keepRecent;
11672
11703
  const head = messages.slice(0, 2);
11673
11704
  if (messages.length <= 2 + keepRecent)
11674
11705
  return messages;
@@ -11722,13 +11753,13 @@ ${combinedSummary}
11722
11753
  * When the combined text exceeds the budget, condense the older summary.
11723
11754
  */
11724
11755
  progressiveSummarize(olderSummary, newerSummary) {
11725
- const MAX_SUMMARY_CHARS = 4e3;
11756
+ const { maxSummaryChars } = this.contextLimits();
11726
11757
  const combined = `${olderSummary}
11727
11758
 
11728
11759
  ---
11729
11760
 
11730
11761
  ${newerSummary}`;
11731
- if (combined.length <= MAX_SUMMARY_CHARS) {
11762
+ if (combined.length <= maxSummaryChars) {
11732
11763
  return combined;
11733
11764
  }
11734
11765
  const condensed = this.condenseSummary(olderSummary);
@@ -11737,8 +11768,8 @@ ${newerSummary}`;
11737
11768
  ---
11738
11769
 
11739
11770
  ${newerSummary}`;
11740
- if (result.length > MAX_SUMMARY_CHARS) {
11741
- const budget = MAX_SUMMARY_CHARS - newerSummary.length - 60;
11771
+ if (result.length > maxSummaryChars) {
11772
+ const budget = maxSummaryChars - newerSummary.length - 60;
11742
11773
  return budget > 200 ? `[Earlier work, condensed]
11743
11774
  ${olderSummary.slice(0, budget)}...
11744
11775
 
@@ -15599,11 +15630,12 @@ async function doSetup(config, rl) {
15599
15630
  const createModelfile = await ask(rl, ` Create optimized model "${c2.bold(customName)}" with ${ctx.label} context? (Y/n) `);
15600
15631
  if (createModelfile.toLowerCase() !== "n") {
15601
15632
  try {
15633
+ const numPredict = Math.min(16384, Math.max(2048, Math.floor(ctx.numCtx * 0.25)));
15602
15634
  const modelfileContent = [
15603
15635
  `FROM ${selectedVariant.tag}`,
15604
15636
  `PARAMETER num_ctx ${ctx.numCtx}`,
15605
15637
  `PARAMETER temperature 0`,
15606
- `PARAMETER num_predict 16384`,
15638
+ `PARAMETER num_predict ${numPredict}`,
15607
15639
  `PARAMETER stop "<|endoftext|>"`
15608
15640
  ].join("\n");
15609
15641
  const modelDir2 = join24(homedir9(), ".open-agents", "models");
@@ -16005,11 +16037,12 @@ function createExpandedVariant(baseModel, specs, sizeGB) {
16005
16037
  const customName = expandedModelName(baseModel);
16006
16038
  const ctx = calculateContextWindow(specs, sizeGB);
16007
16039
  try {
16040
+ const numPredict = Math.min(16384, Math.max(2048, Math.floor(ctx.numCtx * 0.25)));
16008
16041
  const modelfileContent = [
16009
16042
  `FROM ${baseModel}`,
16010
16043
  `PARAMETER num_ctx ${ctx.numCtx}`,
16011
16044
  `PARAMETER temperature 0`,
16012
- `PARAMETER num_predict 16384`,
16045
+ `PARAMETER num_predict ${numPredict}`,
16013
16046
  `PARAMETER stop "<|endoftext|>"`
16014
16047
  ].join("\n");
16015
16048
  const modelDir2 = join24(homedir9(), ".open-agents", "models");
@@ -20983,7 +21016,7 @@ function createTaskCompleteTool() {
20983
21016
  }
20984
21017
  };
20985
21018
  }
20986
- function buildTools(repoRoot, config) {
21019
+ function buildTools(repoRoot, config, contextWindowSize) {
20987
21020
  const executionTools = [
20988
21021
  new FileReadTool(repoRoot),
20989
21022
  new FileWriteTool(repoRoot),
@@ -21044,11 +21077,11 @@ function buildTools(repoRoot, config) {
21044
21077
  ];
21045
21078
  return [
21046
21079
  ...executionTools.map(adaptTool2),
21047
- createSubAgentTool(config, repoRoot),
21080
+ createSubAgentTool(config, repoRoot, contextWindowSize),
21048
21081
  createTaskCompleteTool()
21049
21082
  ];
21050
21083
  }
21051
- function createSubAgentTool(config, repoRoot) {
21084
+ function createSubAgentTool(config, repoRoot, ctxWindowSize) {
21052
21085
  return {
21053
21086
  name: "sub_agent",
21054
21087
  description: "Delegate a sub-task to an independent agent with its own context window. Each sub-agent creates an independent backend connection, enabling TRUE PARALLEL inference when the backend supports concurrent requests (Ollama with OLLAMA_NUM_PARALLEL > 1). BEST PRACTICE: Launch multiple sub_agent calls with background=true in ONE response to maximize parallelism. Check results via task_status/task_output.",
@@ -21069,13 +21102,18 @@ function createSubAgentTool(config, repoRoot) {
21069
21102
  return { success: false, output: "", error: "task is required" };
21070
21103
  }
21071
21104
  const backend = new OllamaAgenticBackend(config.backendUrl, config.model, config.apiKey);
21105
+ const subCtxWindow = ctxWindowSize ?? 0;
21106
+ const subTier = getModelTier(config.model);
21107
+ const subCompaction = subTier === "small" ? 12e3 : subTier === "medium" ? 24e3 : 4e4;
21072
21108
  const subRunner = new AgenticRunner(backend, {
21073
21109
  maxTurns,
21074
21110
  maxTokens: 16384,
21075
21111
  temperature: 0,
21076
21112
  requestTimeoutMs: config.timeoutMs,
21077
21113
  taskTimeoutMs: config.timeoutMs * 2,
21078
- compactionThreshold: 4e4
21114
+ compactionThreshold: subCompaction,
21115
+ contextWindowSize: subCtxWindow,
21116
+ modelTier: subTier
21079
21117
  });
21080
21118
  const subTools = [
21081
21119
  new FileReadTool(repoRoot),
@@ -21145,7 +21183,7 @@ function startTask(task, config, repoRoot, voice, stream, taskStores, bruteForce
21145
21183
  // effectively unlimited — no hard timeout, agent runs until complete or aborted
21146
21184
  contextWindowSize: contextWindowSize ?? 0
21147
21185
  });
21148
- const tools = buildTools(repoRoot, config);
21186
+ const tools = buildTools(repoRoot, config, contextWindowSize);
21149
21187
  if (contextWindowSize && contextWindowSize > 0) {
21150
21188
  for (const tool of tools) {
21151
21189
  if ("setContextWindowSize" in tool && typeof tool.setContextWindowSize === "function") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.31.2",
3
+ "version": "0.31.3",
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",