open-agents-ai 0.46.0 → 0.47.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/README.md +23 -0
- package/dist/index.js +67 -0
- package/dist/scripts/autoresearch-prepare.py +71 -1
- package/dist/scripts/autoresearch-train.py +31 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -333,6 +333,12 @@ Memory flows bidirectionally: the swarm reads all 5 keys at startup (Phase 0) an
|
|
|
333
333
|
|
|
334
334
|
The Monitor agent can be "detached" between experiment rounds by the Flow Maintainer. When detached, the monitor receives a sub-task (e.g., "analyze GPU memory patterns from last 3 runs") instead of its standard watch prompt. This lets the swarm use idle monitoring capacity for useful analysis work.
|
|
335
335
|
|
|
336
|
+
#### Dependency Management
|
|
337
|
+
|
|
338
|
+
The autoresearch tool uses [`uv`](https://docs.astral.sh/uv/) for zero-setup Python environment management. Running `autoresearch(action="setup")` creates a `pyproject.toml` with all dependencies (torch, kernels, pyarrow, rustbpe, tiktoken, etc.) and runs `uv sync` to create a `.venv` automatically.
|
|
339
|
+
|
|
340
|
+
If the Python scripts are invoked directly (without `uv run`), they self-bootstrap: detect missing packages, create a local `.venv`, install dependencies (including CUDA 12.8 torch), and re-exec with the venv's Python. This handles cases where the agent calls `python3 prepare.py` instead of `uv run prepare.py`.
|
|
341
|
+
|
|
336
342
|
If no GPU is detected, the REM stage falls back to the standard multi-agent creative exploration (Visionary + Pragmatist + Cross-Pollinator + Synthesizer).
|
|
337
343
|
|
|
338
344
|
## Blessed Mode — Infinite Warm Loop
|
|
@@ -386,6 +392,23 @@ Connect the agent to a Telegram bot. Each incoming message spawns a dedicated su
|
|
|
386
392
|
|
|
387
393
|
The bot token and admin ID are persisted to project settings, so you only need to set them once. After that, bare `/telegram` toggles the bridge on and off like a service watchdog.
|
|
388
394
|
|
|
395
|
+
### Admin Slash Command Passthrough
|
|
396
|
+
|
|
397
|
+
When the admin sends a `/command` in a private DM, it's routed directly through the terminal's command handler — the same code path as typing the command in the TUI. This means you can control the agent from your phone:
|
|
398
|
+
|
|
399
|
+
```
|
|
400
|
+
/model qwen3.5:122b → switch model
|
|
401
|
+
/voice → toggle TTS
|
|
402
|
+
/dream → enter dream mode
|
|
403
|
+
/listen → toggle voice input
|
|
404
|
+
/stats → show session metrics
|
|
405
|
+
/config → show current config
|
|
406
|
+
/bless → toggle blessed mode
|
|
407
|
+
/telegram status → check bridge status
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
The command output is captured, ANSI-stripped, and sent back as a Telegram message. Skill invocations (e.g., `/ralph`, `/eval-agent`) are queued as tasks.
|
|
411
|
+
|
|
389
412
|
### Sub-Agent Architecture
|
|
390
413
|
|
|
391
414
|
Each Telegram message spawns an independent `AgenticRunner` sub-agent. Sub-agent tool calls, status updates, and streaming tokens appear in the terminal waterfall view with `✈ @username` prefixes — so you can watch all Telegram conversations happening alongside your main work.
|
package/dist/index.js
CHANGED
|
@@ -9158,6 +9158,12 @@ Next steps:
|
|
|
9158
9158
|
if (!existsSync19(join22(workspace, "train.py"))) {
|
|
9159
9159
|
return { success: false, output: "", error: `No train.py found in ${workspace}. Run autoresearch(action="setup") first.`, durationMs: Date.now() - start };
|
|
9160
9160
|
}
|
|
9161
|
+
if (!existsSync19(join22(workspace, ".venv")) && existsSync19(join22(workspace, "pyproject.toml"))) {
|
|
9162
|
+
try {
|
|
9163
|
+
execSync16("uv sync 2>&1", { cwd: workspace, encoding: "utf-8", timeout: 3e5 });
|
|
9164
|
+
} catch {
|
|
9165
|
+
}
|
|
9166
|
+
}
|
|
9161
9167
|
const timeoutMin = Number(args["timeout_minutes"] ?? 10);
|
|
9162
9168
|
const timeoutMs = timeoutMin * 60 * 1e3;
|
|
9163
9169
|
const logPath = join22(workspace, "run.log");
|
|
@@ -26629,6 +26635,8 @@ with summary "no_reply" to silently skip without responding.
|
|
|
26629
26635
|
onSubAgentEvent = null;
|
|
26630
26636
|
/** Tool policy config — user overrides from config */
|
|
26631
26637
|
toolPolicyConfig;
|
|
26638
|
+
/** Command handler for admin DM slash commands (wired from interactive.ts) */
|
|
26639
|
+
commandHandler = null;
|
|
26632
26640
|
/** Media cache — fileUniqueId → cache entry */
|
|
26633
26641
|
mediaCache = /* @__PURE__ */ new Map();
|
|
26634
26642
|
/** Media cache directory */
|
|
@@ -26653,6 +26661,15 @@ with summary "no_reply" to silently skip without responding.
|
|
|
26653
26661
|
setToolPolicyConfig(config) {
|
|
26654
26662
|
this.toolPolicyConfig = config;
|
|
26655
26663
|
}
|
|
26664
|
+
/**
|
|
26665
|
+
* Set a command handler for admin DM slash commands.
|
|
26666
|
+
* When an admin sends a /command in DM, it gets routed through the TUI's
|
|
26667
|
+
* handleSlashCommand instead of spawning a sub-agent.
|
|
26668
|
+
* The handler receives the raw command string and returns a text result.
|
|
26669
|
+
*/
|
|
26670
|
+
setCommandHandler(handler) {
|
|
26671
|
+
this.commandHandler = handler;
|
|
26672
|
+
}
|
|
26656
26673
|
/** Register event handler for sub-agent activity (waterfall view) */
|
|
26657
26674
|
setOnSubAgentEvent(handler) {
|
|
26658
26675
|
this.onSubAgentEvent = handler;
|
|
@@ -26791,6 +26808,26 @@ with summary "no_reply" to silently skip without responding.
|
|
|
26791
26808
|
const isAdmin = this.isAdminUser(msg);
|
|
26792
26809
|
const toolContext = this.resolveToolContext(msg, isAdmin);
|
|
26793
26810
|
const isAdminDM = toolContext === "telegram-admin-dm";
|
|
26811
|
+
if (isAdminDM && msg.text.startsWith("/") && this.commandHandler) {
|
|
26812
|
+
const cmdName = msg.text.split(/\s+/)[0].slice(1).toLowerCase();
|
|
26813
|
+
if (cmdName !== "start") {
|
|
26814
|
+
renderTelegramSubAgentEvent(msg.username, `command: ${msg.text}`);
|
|
26815
|
+
try {
|
|
26816
|
+
const result = await this.commandHandler(msg.text);
|
|
26817
|
+
if (result) {
|
|
26818
|
+
const html = convertMarkdownToTelegramHTML(result);
|
|
26819
|
+
await this.sendMessageHTML(msg.chatId, html);
|
|
26820
|
+
} else {
|
|
26821
|
+
await this.sendMessage(msg.chatId, `Command executed: ${msg.text}`);
|
|
26822
|
+
}
|
|
26823
|
+
} catch (err) {
|
|
26824
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
26825
|
+
await this.sendMessage(msg.chatId, `Command error: ${errMsg}`).catch(() => {
|
|
26826
|
+
});
|
|
26827
|
+
}
|
|
26828
|
+
return;
|
|
26829
|
+
}
|
|
26830
|
+
}
|
|
26794
26831
|
const existing = this.subAgents.get(msg.chatId);
|
|
26795
26832
|
if (existing && !existing.aborted) {
|
|
26796
26833
|
existing.runner.injectUserMessage(msg.text);
|
|
@@ -29723,6 +29760,36 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
|
|
|
29723
29760
|
});
|
|
29724
29761
|
}
|
|
29725
29762
|
}
|
|
29763
|
+
telegramBridge.setCommandHandler(async (input) => {
|
|
29764
|
+
const captured = [];
|
|
29765
|
+
const origWrite = process.stdout.write;
|
|
29766
|
+
process.stdout.write = function(chunk, ...args) {
|
|
29767
|
+
if (typeof chunk === "string") {
|
|
29768
|
+
const clean = chunk.replace(/\x1B\[[0-9;]*[a-zA-Z]/g, "").trim();
|
|
29769
|
+
if (clean)
|
|
29770
|
+
captured.push(clean);
|
|
29771
|
+
}
|
|
29772
|
+
return origWrite.call(process.stdout, chunk, ...args);
|
|
29773
|
+
};
|
|
29774
|
+
try {
|
|
29775
|
+
const result = await handleSlashCommand(input, commandCtx);
|
|
29776
|
+
process.stdout.write = origWrite;
|
|
29777
|
+
if (result === "exit") {
|
|
29778
|
+
return "Exit command received (ignored via Telegram).";
|
|
29779
|
+
}
|
|
29780
|
+
if (result === "not_a_command") {
|
|
29781
|
+
return null;
|
|
29782
|
+
}
|
|
29783
|
+
if (typeof result === "object" && result.type === "skill") {
|
|
29784
|
+
rl.emit("line", input);
|
|
29785
|
+
return `Skill invoked: ${result.name}`;
|
|
29786
|
+
}
|
|
29787
|
+
return captured.length > 0 ? captured.join("\n") : `Done: ${input}`;
|
|
29788
|
+
} catch (err) {
|
|
29789
|
+
process.stdout.write = origWrite;
|
|
29790
|
+
throw err;
|
|
29791
|
+
}
|
|
29792
|
+
});
|
|
29726
29793
|
await telegramBridge.start();
|
|
29727
29794
|
writeContent(() => renderTelegramStart(telegramBridge.botUsername, adminId));
|
|
29728
29795
|
showPrompt();
|
|
@@ -3,7 +3,8 @@ One-time data preparation for autoresearch experiments.
|
|
|
3
3
|
Downloads data shards and trains a BPE tokenizer.
|
|
4
4
|
|
|
5
5
|
Usage:
|
|
6
|
-
|
|
6
|
+
uv run prepare.py # recommended (uses pyproject.toml deps)
|
|
7
|
+
python prepare.py # auto-installs missing deps into venv
|
|
7
8
|
python prepare.py --num-shards 8 # download only 8 shards (for testing)
|
|
8
9
|
|
|
9
10
|
Data and tokenizer are stored in ~/.cache/autoresearch/.
|
|
@@ -15,8 +16,77 @@ import time
|
|
|
15
16
|
import math
|
|
16
17
|
import argparse
|
|
17
18
|
import pickle
|
|
19
|
+
import subprocess
|
|
18
20
|
from multiprocessing import Pool
|
|
19
21
|
|
|
22
|
+
# ---------------------------------------------------------------------------
|
|
23
|
+
# Auto-bootstrap: if running outside uv, ensure deps are installed
|
|
24
|
+
# ---------------------------------------------------------------------------
|
|
25
|
+
|
|
26
|
+
_REQUIRED_PACKAGES = {
|
|
27
|
+
"requests": "requests",
|
|
28
|
+
"pyarrow": "pyarrow",
|
|
29
|
+
"rustbpe": "rustbpe",
|
|
30
|
+
"tiktoken": "tiktoken",
|
|
31
|
+
"torch": "torch",
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
def _bootstrap_deps():
|
|
35
|
+
"""Auto-install missing dependencies into a local venv if not using uv."""
|
|
36
|
+
missing = []
|
|
37
|
+
for module_name, pip_name in _REQUIRED_PACKAGES.items():
|
|
38
|
+
try:
|
|
39
|
+
__import__(module_name)
|
|
40
|
+
except ImportError:
|
|
41
|
+
missing.append(pip_name)
|
|
42
|
+
|
|
43
|
+
if not missing:
|
|
44
|
+
return # All deps available
|
|
45
|
+
|
|
46
|
+
print(f"Missing packages: {', '.join(missing)}")
|
|
47
|
+
print("Auto-installing into local venv...")
|
|
48
|
+
|
|
49
|
+
script_dir = os.path.dirname(os.path.abspath(__file__))
|
|
50
|
+
venv_dir = os.path.join(script_dir, ".venv")
|
|
51
|
+
|
|
52
|
+
# Create venv if needed
|
|
53
|
+
if not os.path.exists(venv_dir):
|
|
54
|
+
print(f"Creating venv at {venv_dir}...")
|
|
55
|
+
subprocess.check_call([sys.executable, "-m", "venv", venv_dir])
|
|
56
|
+
|
|
57
|
+
# Determine pip path
|
|
58
|
+
pip_path = os.path.join(venv_dir, "bin", "pip")
|
|
59
|
+
if not os.path.exists(pip_path):
|
|
60
|
+
pip_path = os.path.join(venv_dir, "Scripts", "pip.exe") # Windows
|
|
61
|
+
|
|
62
|
+
# Install missing packages
|
|
63
|
+
# For torch, use the CUDA 12.8 index if available
|
|
64
|
+
torch_pkgs = [p for p in missing if p == "torch"]
|
|
65
|
+
other_pkgs = [p for p in missing if p != "torch"]
|
|
66
|
+
|
|
67
|
+
if other_pkgs:
|
|
68
|
+
print(f"Installing: {', '.join(other_pkgs)}")
|
|
69
|
+
subprocess.check_call([pip_path, "install", "--quiet"] + other_pkgs)
|
|
70
|
+
|
|
71
|
+
if torch_pkgs:
|
|
72
|
+
print("Installing torch (CUDA 12.8)...")
|
|
73
|
+
subprocess.check_call([
|
|
74
|
+
pip_path, "install", "--quiet", "torch",
|
|
75
|
+
"--index-url", "https://download.pytorch.org/whl/cu128",
|
|
76
|
+
])
|
|
77
|
+
|
|
78
|
+
# Re-exec with the venv's Python
|
|
79
|
+
venv_python = os.path.join(venv_dir, "bin", "python")
|
|
80
|
+
if not os.path.exists(venv_python):
|
|
81
|
+
venv_python = os.path.join(venv_dir, "Scripts", "python.exe")
|
|
82
|
+
|
|
83
|
+
print(f"Re-launching with venv Python: {venv_python}")
|
|
84
|
+
os.execv(venv_python, [venv_python] + sys.argv)
|
|
85
|
+
|
|
86
|
+
# Only bootstrap if not already in a venv/uv-managed environment
|
|
87
|
+
if not (hasattr(sys, "real_prefix") or (hasattr(sys, "base_prefix") and sys.base_prefix != sys.prefix)):
|
|
88
|
+
_bootstrap_deps()
|
|
89
|
+
|
|
20
90
|
import requests
|
|
21
91
|
import pyarrow.parquet as pq
|
|
22
92
|
import rustbpe
|
|
@@ -5,9 +5,40 @@ Usage: uv run train.py
|
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
import os
|
|
8
|
+
import sys
|
|
9
|
+
import subprocess
|
|
8
10
|
os.environ["PYTORCH_ALLOC_CONF"] = "expandable_segments:True"
|
|
9
11
|
os.environ["HF_HUB_DISABLE_PROGRESS_BARS"] = "1"
|
|
10
12
|
|
|
13
|
+
# ---------------------------------------------------------------------------
|
|
14
|
+
# Auto-bootstrap: ensure we're running in a venv with deps installed
|
|
15
|
+
# ---------------------------------------------------------------------------
|
|
16
|
+
|
|
17
|
+
def _ensure_venv():
|
|
18
|
+
"""If not in a venv, check for .venv in script dir and re-exec."""
|
|
19
|
+
if hasattr(sys, "real_prefix") or (hasattr(sys, "base_prefix") and sys.base_prefix != sys.prefix):
|
|
20
|
+
return # Already in a venv
|
|
21
|
+
|
|
22
|
+
script_dir = os.path.dirname(os.path.abspath(__file__))
|
|
23
|
+
venv_python = os.path.join(script_dir, ".venv", "bin", "python")
|
|
24
|
+
|
|
25
|
+
if os.path.exists(venv_python):
|
|
26
|
+
print(f"Re-launching with venv Python: {venv_python}")
|
|
27
|
+
os.execv(venv_python, [venv_python] + sys.argv)
|
|
28
|
+
else:
|
|
29
|
+
# Try to run prepare.py bootstrap first (it creates the venv)
|
|
30
|
+
prepare_py = os.path.join(script_dir, "prepare.py")
|
|
31
|
+
if os.path.exists(prepare_py):
|
|
32
|
+
print("No venv found. Running prepare.py to bootstrap dependencies...")
|
|
33
|
+
subprocess.check_call([sys.executable, prepare_py, "--num-shards", "0"])
|
|
34
|
+
# After prepare.py creates the venv, re-exec
|
|
35
|
+
if os.path.exists(venv_python):
|
|
36
|
+
os.execv(venv_python, [venv_python] + sys.argv)
|
|
37
|
+
print("ERROR: No venv found. Run 'uv sync' or 'python prepare.py' first.", file=sys.stderr)
|
|
38
|
+
sys.exit(1)
|
|
39
|
+
|
|
40
|
+
_ensure_venv()
|
|
41
|
+
|
|
11
42
|
import gc
|
|
12
43
|
import math
|
|
13
44
|
import time
|
package/package.json
CHANGED