open-agents-ai 0.106.0 → 0.107.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 +81 -2
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -7561,6 +7561,44 @@ var init_repl = __esm({
7561
7561
  getSubCallCount() {
7562
7562
  return this.subCallCount;
7563
7563
  }
7564
+ /**
7565
+ * Pre-load a large context string into the REPL as the `context` variable.
7566
+ * Used by prompt externalization (RLM paper §2, Principle 1: symbolic handle).
7567
+ * The context is stored as a Python string variable — the model can then
7568
+ * slice, search, and process it via repl_exec without it entering the LLM context.
7569
+ */
7570
+ async loadContext(content) {
7571
+ if (!this.proc || this.proc.killed || this.proc.exitCode !== null) {
7572
+ await this.startProcess();
7573
+ }
7574
+ const tempFile = join18(tmpdir3(), `oa-repl-ctx-${randomBytes2(6).toString("hex")}.txt`);
7575
+ const { writeFileSync: writeFs, unlinkSync: unlinkFs } = await import("node:fs");
7576
+ writeFs(tempFile, content, "utf8");
7577
+ const result = await this.executeCode(`with open(${JSON.stringify(tempFile)}, "r") as _f:
7578
+ context = _f.read()
7579
+ import os; os.unlink(${JSON.stringify(tempFile)})
7580
+ print(f"Context loaded: {len(context)} chars")`);
7581
+ try {
7582
+ unlinkFs(tempFile);
7583
+ } catch {
7584
+ }
7585
+ return result.success;
7586
+ }
7587
+ /**
7588
+ * Read a variable's value from the REPL environment.
7589
+ * Used by FINAL_VAR to extract the task output (RLM paper §2, Principle 2).
7590
+ */
7591
+ async readVariable(varName) {
7592
+ if (!this.proc || this.proc.killed || this.proc.exitCode !== null)
7593
+ return null;
7594
+ const result = await this.executeCode(`try:
7595
+ print(str(${varName}))
7596
+ except NameError:
7597
+ print("__OA_VAR_NOT_FOUND__")`);
7598
+ if (!result.success || result.output.includes("__OA_VAR_NOT_FOUND__"))
7599
+ return null;
7600
+ return result.output.trim();
7601
+ }
7564
7602
  async execute(args) {
7565
7603
  const code = String(args.code ?? "");
7566
7604
  const reset = Boolean(args.reset);
@@ -19313,7 +19351,8 @@ var init_agenticRunner = __esm({
19313
19351
  modelTier: options?.modelTier ?? "large",
19314
19352
  contextWindowSize: options?.contextWindowSize ?? 0,
19315
19353
  personality: options?.personality ?? PERSONALITY_PRESETS.balanced,
19316
- personalityName: options?.personalityName ?? ""
19354
+ personalityName: options?.personalityName ?? "",
19355
+ finalVarResolver: options?.finalVarResolver ?? void 0
19317
19356
  };
19318
19357
  }
19319
19358
  /** Update context window size (e.g. after querying Ollama /api/show) */
@@ -20382,6 +20421,17 @@ ${result.output}`;
20382
20421
  summary = content;
20383
20422
  break;
20384
20423
  }
20424
+ const finalVarMatch = content.match(/FINAL_VAR\s*\(\s*["']?(\w+)["']?\s*\)/);
20425
+ if (finalVarMatch && this.options.finalVarResolver) {
20426
+ const varName = finalVarMatch[1];
20427
+ const varValue = await this.options.finalVarResolver(varName);
20428
+ if (varValue !== null) {
20429
+ completed = true;
20430
+ summary = varValue;
20431
+ this.emit({ type: "status", content: `FINAL_VAR(${varName}): resolved ${varValue.length} chars from REPL`, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
20432
+ break;
20433
+ }
20434
+ }
20385
20435
  if (consecutiveTextOnly >= MAX_CONSECUTIVE_TEXT_ONLY) {
20386
20436
  this.emit({
20387
20437
  type: "status",
@@ -50240,6 +50290,12 @@ ${context}` : prompt;
50240
50290
  });
50241
50291
  }
50242
50292
  }
50293
+ const replToolForFinalVar = tools.find((t) => t.name === "repl_exec");
50294
+ if (replToolForFinalVar && "readVariable" in replToolForFinalVar) {
50295
+ runner.options.finalVarResolver = async (varName) => {
50296
+ return replToolForFinalVar.readVariable(varName);
50297
+ };
50298
+ }
50243
50299
  runner.registerTools(tools);
50244
50300
  runner.registerTool({
50245
50301
  name: "memex_retrieve",
@@ -50698,7 +50754,30 @@ ${entry.fullContent}`
50698
50754
 
50699
50755
  ${emotionContext}` : `Working directory: ${repoRoot}`;
50700
50756
  resetNarrationContext();
50701
- const promise = runner.run(task, systemContext).then((result) => {
50757
+ let effectiveTask = task;
50758
+ const estimatedTaskTokens = Math.ceil(task.length / 4);
50759
+ const externalizeThreshold = contextWindowSize ? Math.floor(contextWindowSize * 0.4) : 3e4;
50760
+ if (estimatedTaskTokens > externalizeThreshold) {
50761
+ const replTool = tools.find((t) => t.name === "repl_exec");
50762
+ if (replTool && "loadContext" in replTool) {
50763
+ replTool.loadContext(task);
50764
+ const prefix = task.slice(0, 500).replace(/\n/g, " ");
50765
+ effectiveTask = `[RLM MODE \u2014 Large input externalized to REPL]
50766
+ Your input has been stored as the variable \`context\` in the persistent Python REPL.
50767
+ Context length: ${task.length} characters (~${estimatedTaskTokens} tokens)
50768
+ Prefix: "${prefix}..."
50769
+
50770
+ Use repl_exec to examine and process the context. For example:
50771
+ repl_exec(code="print(context[:1000])") # see the beginning
50772
+ repl_exec(code="print(len(context))") # check length
50773
+ repl_exec(code="chunks = context.split('\\n\\n')") # split into chunks
50774
+
50775
+ Use llm_query(prompt, chunk) inside repl_exec to analyze chunks.
50776
+ When done, either call task_complete with your answer, or use FINAL_VAR(variable_name) to return a REPL variable as the output.`;
50777
+ contentWrite(() => renderInfo(`RLM: Externalized ${task.length} chars (~${estimatedTaskTokens} tokens) to REPL context variable`));
50778
+ }
50779
+ }
50780
+ const promise = runner.run(effectiveTask, systemContext).then((result) => {
50702
50781
  const tokens = { total: result.totalTokens, estimated: result.estimatedTokens };
50703
50782
  contentWrite(() => {
50704
50783
  if (result.completed) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.106.0",
3
+ "version": "0.107.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",