open-agents-ai 0.107.0 → 0.108.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.
- package/dist/index.js +117 -10
- package/package.json +1 -1
- package/prompts/agentic/system-large.md +25 -0
package/dist/index.js
CHANGED
|
@@ -7557,6 +7557,12 @@ var init_repl = __esm({
|
|
|
7557
7557
|
setLlmQueryHandler(handler) {
|
|
7558
7558
|
this.llmQueryHandler = handler;
|
|
7559
7559
|
}
|
|
7560
|
+
/** Callback for retrieving externalized handle content (RLM Context OS) */
|
|
7561
|
+
handleResolver = null;
|
|
7562
|
+
/** Set the handle resolver for retrieve() in the REPL (COHERE Layer 2) */
|
|
7563
|
+
setHandleResolver(resolver) {
|
|
7564
|
+
this.handleResolver = resolver;
|
|
7565
|
+
}
|
|
7560
7566
|
/** Get the number of sub-calls made this session */
|
|
7561
7567
|
getSubCallCount() {
|
|
7562
7568
|
return this.subCallCount;
|
|
@@ -7684,6 +7690,45 @@ def llm_query(prompt, context=""):
|
|
|
7684
7690
|
finally:
|
|
7685
7691
|
sock.close()
|
|
7686
7692
|
|
|
7693
|
+
def retrieve(handle_id):
|
|
7694
|
+
"""Retrieve full content of an externalized tool output by its handle ID.
|
|
7695
|
+
Use this to access large outputs that were stored as handles instead of
|
|
7696
|
+
being shown inline. Returns the full content as a string.
|
|
7697
|
+
Args:
|
|
7698
|
+
handle_id: The handle ID (e.g., 'a1b2c3d4')
|
|
7699
|
+
Returns: Full content string, or error message if not found
|
|
7700
|
+
"""
|
|
7701
|
+
sock_path = os.environ.get("OA_LLM_QUERY_SOCKET", "")
|
|
7702
|
+
if not sock_path:
|
|
7703
|
+
return "[retrieve unavailable: no IPC socket]"
|
|
7704
|
+
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
|
7705
|
+
try:
|
|
7706
|
+
sock.connect(sock_path)
|
|
7707
|
+
request = json.dumps({"op": "retrieve", "handle_id": str(handle_id)})
|
|
7708
|
+
data = request.encode("utf-8")
|
|
7709
|
+
sock.sendall(len(data).to_bytes(4, "big") + data)
|
|
7710
|
+
length_bytes = b""
|
|
7711
|
+
while len(length_bytes) < 4:
|
|
7712
|
+
chunk = sock.recv(4 - len(length_bytes))
|
|
7713
|
+
if not chunk:
|
|
7714
|
+
return "[retrieve: connection closed]"
|
|
7715
|
+
length_bytes += chunk
|
|
7716
|
+
resp_len = int.from_bytes(length_bytes, "big")
|
|
7717
|
+
resp_data = b""
|
|
7718
|
+
while len(resp_data) < resp_len:
|
|
7719
|
+
chunk = sock.recv(min(resp_len - len(resp_data), 65536))
|
|
7720
|
+
if not chunk:
|
|
7721
|
+
break
|
|
7722
|
+
resp_data += chunk
|
|
7723
|
+
result = json.loads(resp_data.decode("utf-8"))
|
|
7724
|
+
if "error" in result:
|
|
7725
|
+
return f"[retrieve error: {result['error']}]"
|
|
7726
|
+
return result.get("content", "")
|
|
7727
|
+
except Exception as e:
|
|
7728
|
+
return f"[retrieve error: {e}]"
|
|
7729
|
+
finally:
|
|
7730
|
+
sock.close()
|
|
7731
|
+
|
|
7687
7732
|
# Signal REPL ready
|
|
7688
7733
|
print("__OA_REPL_READY__")
|
|
7689
7734
|
`.trim();
|
|
@@ -7725,14 +7770,27 @@ print("__OA_REPL_READY__")
|
|
|
7725
7770
|
let response;
|
|
7726
7771
|
try {
|
|
7727
7772
|
const req = JSON.parse(msgData);
|
|
7728
|
-
if (
|
|
7729
|
-
|
|
7730
|
-
|
|
7731
|
-
|
|
7773
|
+
if (req.op === "retrieve") {
|
|
7774
|
+
if (!this.handleResolver) {
|
|
7775
|
+
response = { error: "Handle resolver not configured" };
|
|
7776
|
+
} else {
|
|
7777
|
+
const content = this.handleResolver(req.handle_id ?? "");
|
|
7778
|
+
if (content !== null) {
|
|
7779
|
+
response = { content };
|
|
7780
|
+
} else {
|
|
7781
|
+
response = { error: `Handle '${req.handle_id}' not found` };
|
|
7782
|
+
}
|
|
7783
|
+
}
|
|
7732
7784
|
} else {
|
|
7733
|
-
this.
|
|
7734
|
-
|
|
7735
|
-
|
|
7785
|
+
if (!this.llmQueryHandler) {
|
|
7786
|
+
response = { error: "llm_query handler not configured" };
|
|
7787
|
+
} else if (this.subCallCount >= this.maxSubCalls) {
|
|
7788
|
+
response = { error: `Sub-call limit reached (${this.maxSubCalls})` };
|
|
7789
|
+
} else {
|
|
7790
|
+
this.subCallCount++;
|
|
7791
|
+
const result = await this.llmQueryHandler(req.prompt ?? "", req.context);
|
|
7792
|
+
response = { response: result };
|
|
7793
|
+
}
|
|
7736
7794
|
}
|
|
7737
7795
|
} catch (err) {
|
|
7738
7796
|
response = { error: err instanceof Error ? err.message : String(err) };
|
|
@@ -19992,8 +20050,29 @@ If you're stuck, try a completely different approach. Do NOT repeat what failed
|
|
|
19992
20050
|
}
|
|
19993
20051
|
}
|
|
19994
20052
|
const { toolOutputMaxChars: maxLen } = this.contextLimits();
|
|
19995
|
-
|
|
20053
|
+
let output;
|
|
20054
|
+
if (!result.success) {
|
|
20055
|
+
output = `Error: ${result.error || "unknown error"}
|
|
19996
20056
|
${result.output.length > maxLen ? this.foldOutput(result.output, maxLen) : result.output}`;
|
|
20057
|
+
} else if (result.output.length > maxLen) {
|
|
20058
|
+
const handleId = this.quickHash(tc.name + String(tc.arguments?.["path"] ?? "") + String(turn));
|
|
20059
|
+
const lineCount = (result.output.match(/\n/g) || []).length + 1;
|
|
20060
|
+
const preview = result.output.slice(0, 500).replace(/\n/g, " ");
|
|
20061
|
+
this._memexArchive.set(handleId, {
|
|
20062
|
+
id: handleId,
|
|
20063
|
+
toolName: tc.name,
|
|
20064
|
+
summary: `${tc.name} output (${lineCount} lines, ${result.output.length} chars)`,
|
|
20065
|
+
fullContent: result.output,
|
|
20066
|
+
turn,
|
|
20067
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
20068
|
+
});
|
|
20069
|
+
output = `[Output externalized \u2014 ${result.output.length} chars, ${lineCount} lines]
|
|
20070
|
+
Handle: ${handleId}
|
|
20071
|
+
Preview: ${preview}...
|
|
20072
|
+
Full content available via: repl_exec(code="data = retrieve('${handleId}')") or memex_retrieve(id="${handleId}")`;
|
|
20073
|
+
} else {
|
|
20074
|
+
output = result.output;
|
|
20075
|
+
}
|
|
19997
20076
|
this.emit({
|
|
19998
20077
|
type: "tool_result",
|
|
19999
20078
|
toolName: tc.name,
|
|
@@ -20383,9 +20462,29 @@ Integrate this guidance into your current approach. Continue working on the task
|
|
|
20383
20462
|
}
|
|
20384
20463
|
}
|
|
20385
20464
|
const { toolOutputMaxChars: maxLen2 } = this.contextLimits();
|
|
20386
|
-
|
|
20387
|
-
|
|
20465
|
+
let output;
|
|
20466
|
+
if (!result.success) {
|
|
20467
|
+
output = `Error: ${result.error || "unknown error"}
|
|
20388
20468
|
${result.output}`;
|
|
20469
|
+
} else if (result.output.length > maxLen2) {
|
|
20470
|
+
const handleId = this.quickHash(tc.name + String(tc.arguments?.["path"] ?? "") + String(turn));
|
|
20471
|
+
const lineCount = (result.output.match(/\n/g) || []).length + 1;
|
|
20472
|
+
const preview = result.output.slice(0, 500).replace(/\n/g, " ");
|
|
20473
|
+
this._memexArchive.set(handleId, {
|
|
20474
|
+
id: handleId,
|
|
20475
|
+
toolName: tc.name,
|
|
20476
|
+
summary: `${tc.name} output (${lineCount} lines, ${result.output.length} chars)`,
|
|
20477
|
+
fullContent: result.output,
|
|
20478
|
+
turn,
|
|
20479
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
20480
|
+
});
|
|
20481
|
+
output = `[Output externalized \u2014 ${result.output.length} chars, ${lineCount} lines]
|
|
20482
|
+
Handle: ${handleId}
|
|
20483
|
+
Preview: ${preview}...
|
|
20484
|
+
Full content available via: repl_exec(code="data = retrieve('${handleId}')") or memex_retrieve(id="${handleId}")`;
|
|
20485
|
+
} else {
|
|
20486
|
+
output = result.output;
|
|
20487
|
+
}
|
|
20389
20488
|
this.emit({ type: "tool_result", toolName: tc.name, content: output.slice(0, 200), success: result.success, turn, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
20390
20489
|
const enoentTools2 = /* @__PURE__ */ new Set(["file_read", "list_directory", "find_files"]);
|
|
20391
20490
|
if (!result.success && enoentTools2.has(tc.name) && /ENOENT|no such file|does not exist|not found/i.test(result.error ?? result.output)) {
|
|
@@ -50290,6 +50389,14 @@ ${context}` : prompt;
|
|
|
50290
50389
|
});
|
|
50291
50390
|
}
|
|
50292
50391
|
}
|
|
50392
|
+
for (const tool of tools) {
|
|
50393
|
+
if ("setHandleResolver" in tool && typeof tool.setHandleResolver === "function") {
|
|
50394
|
+
tool.setHandleResolver((handleId) => {
|
|
50395
|
+
const entry = runner.getMemexEntry(handleId);
|
|
50396
|
+
return entry?.fullContent ?? null;
|
|
50397
|
+
});
|
|
50398
|
+
}
|
|
50399
|
+
}
|
|
50293
50400
|
const replToolForFinalVar = tools.find((t) => t.name === "repl_exec");
|
|
50294
50401
|
if (replToolForFinalVar && "readVariable" in replToolForFinalVar) {
|
|
50295
50402
|
runner.options.finalVarResolver = async (varName) => {
|
package/package.json
CHANGED
|
@@ -462,3 +462,28 @@ Entries in a directory listing are RELATIVE to the directory you listed.
|
|
|
462
462
|
- If an entry is marked "d" (directory), use list_directory on it — NOT file_read
|
|
463
463
|
- list_directory output includes full relative paths you can copy directly into your next tool call
|
|
464
464
|
- Prefer list_directory over shell ls — it shows full relative paths you can copy directly into your next tool call
|
|
465
|
+
|
|
466
|
+
## RLM Context Operating System
|
|
467
|
+
|
|
468
|
+
The repl_exec tool provides a persistent Python REPL where variables persist between calls. Use it for:
|
|
469
|
+
|
|
470
|
+
**Data Processing**: When you need to process, transform, or analyze data across multiple steps, use repl_exec. Variables, functions, and imports survive between calls.
|
|
471
|
+
|
|
472
|
+
**Recursive LLM Calls**: Inside the REPL, `llm_query(prompt, context="")` invokes the language model on a sub-prompt. Use it in loops to analyze chunks of large content:
|
|
473
|
+
```python
|
|
474
|
+
# Example: analyze each file in a list
|
|
475
|
+
results = []
|
|
476
|
+
for filename in filenames:
|
|
477
|
+
with open(filename) as f:
|
|
478
|
+
content = f.read()
|
|
479
|
+
summary = llm_query("Summarize the key purpose of this code", content)
|
|
480
|
+
results.append(f"{filename}: {summary}")
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
**Externalized Context**: When your input is very large, it may be stored as the `context` variable in the REPL. Use `print(context[:1000])` to examine it, slice it, and process chunks with llm_query().
|
|
484
|
+
|
|
485
|
+
**Handle Retrieval**: When a tool output is too large for context, it's stored as a handle. Access it via `data = retrieve('handle_id')` in the REPL.
|
|
486
|
+
|
|
487
|
+
**Output Construction**: For very long outputs, build the result as a REPL variable and return it with FINAL_VAR(variable_name) instead of autoregressive generation.
|
|
488
|
+
|
|
489
|
+
**Provenance**: Based on "Recursive Language Models" (Zhang, Kraska, Khattab — MIT CSAIL, arxiv:2512.24601) and Project COHERE Layer 2 architecture.
|