feat(relay): add subagent spawn, thread tools, and container box client
- Upgrade exec-constrained.py with subagent.spawn, thread.list, thread.view, pipeline.run ops - Grant full ops permissions to all fleet agent identities (646, pip, muse, opm) - Implement bin/box-relay.sh zero-dependency client supporting bearer and SSH signature auth - Add fast hybrid gateway path to dm.py for sub-2s verified deliveries - Fix wait=0 handling in super-cli.py subagent deployments - Add hourly check-in jobs and scheduler for 646, pip, muse - Document agent tooling and relay APIs in docs/AGENT-TOOLING.md
This commit is contained in:
+74
-1
@@ -1246,8 +1246,8 @@ def cmd_thread_view(args):
|
||||
# Fallback to box-chat.py / CDP if gateway returned no messages
|
||||
if not messages:
|
||||
cmd = ["python3", str(BIN_DIR / "box-chat.py"), "thread-messages", agent, thread_id, "--limit", str(limit)]
|
||||
res = subprocess.run(cmd, capture_output=True, text=True)
|
||||
try:
|
||||
res = subprocess.run(cmd, capture_output=True, text=True, timeout=15)
|
||||
data = json.loads(res.stdout)
|
||||
messages = data.get("messages", []) if data.get("ok") else []
|
||||
except Exception:
|
||||
@@ -2502,6 +2502,63 @@ def cmd_pipeline_prune(args):
|
||||
print(json.dumps({"pruned": pruned, "count": len(pruned)}, indent=2))
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Domain: DEPLOY (Multi-Agent Pipelines, Subagent Spawning, and Oversight)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def cmd_deploy(args):
|
||||
action = getattr(args, "action", None)
|
||||
|
||||
if action == "subagent":
|
||||
agent = args.agent
|
||||
title = args.title or "subagent-task"
|
||||
prompt = args.prompt
|
||||
wait_val = getattr(args, "wait", 30)
|
||||
wait = 30 if wait_val is None else int(wait_val)
|
||||
|
||||
print(c_bold(f"\n=== DEPLOYING SUBAGENT ON [{agent}] ==="))
|
||||
print(f" Agent: {c_cyan(agent)}")
|
||||
print(f" Title: {c_yellow(title)}")
|
||||
print(f" Prompt: {c_dim(prompt[:90])}...\n")
|
||||
|
||||
import muse_hybrid
|
||||
res, err = muse_hybrid.start_session(agent, title=title)
|
||||
if err or not res:
|
||||
print(c_err(f"✖ Failed to spawn subagent session: {err}"), file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
session_id = res.get("session_id")
|
||||
print(c_green(f"✔ Subagent session spawned: {session_id}"))
|
||||
|
||||
if prompt:
|
||||
print(f" Dispatching task prompt to subagent (waiting up to {wait}s)...")
|
||||
send_res, send_err = muse_hybrid.send_message(agent, prompt, thread_id=session_id, wait=wait)
|
||||
if send_err:
|
||||
print(c_warn(f"Notice: {send_err}"))
|
||||
else:
|
||||
print(c_green("✔ Task prompt delivered."))
|
||||
|
||||
# Fetch fresh history to show assistant's initial work
|
||||
msgs, _ = muse_hybrid.get_history(agent, thread_id=session_id, limit=5)
|
||||
if msgs:
|
||||
for m in msgs:
|
||||
role = m.get("role")
|
||||
if role == "assistant":
|
||||
print(f"\n{c_bold('--- Subagent Response ---')}\n{m.get('text')}\n")
|
||||
|
||||
print(f" Oversee anytime with: {c_cyan(f'box thread view {agent} {session_id}')}\n")
|
||||
return
|
||||
|
||||
# Pipeline deployment
|
||||
pipe_name = getattr(args, "name", None)
|
||||
if not pipe_name:
|
||||
print(c_err("Error: Specify pipeline name (e.g. box deploy pipeline pipe-demo-step1)"), file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
args.name = pipe_name
|
||||
cmd_pipeline_run(args)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Domain: LOOP (Intrinsic Loop Strategy, Health, Break Taxonomy, and Control)
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -3333,6 +3390,20 @@ def build_parser():
|
||||
p_va_rb.add_argument("name", help="Variable name")
|
||||
p_va_rb.add_argument("--revision", default=None, help="Revision step (int) or timestamp")
|
||||
|
||||
# Domain: deploy (Deploy multi-agent pipelines or subagent tasks with oversight)
|
||||
p_deploy = subparsers.add_parser("deploy", parents=[common], help="Deploy multi-agent task or pipeline across nodes with live oversight")
|
||||
deploy_sub = p_deploy.add_subparsers(dest="action")
|
||||
|
||||
p_dep_pipe = deploy_sub.add_parser("pipeline", parents=[common], help="Deploy multi-agent pipeline")
|
||||
p_dep_pipe.add_argument("name", help="Pipeline root job name (e.g. pipe-demo-step1)")
|
||||
p_dep_pipe.add_argument("--dry-run", action="store_true", help="Simulate pipeline without sending DMs")
|
||||
|
||||
p_dep_sub = deploy_sub.add_parser("subagent", parents=[common], help="Spawn sub-agent session and dispatch task")
|
||||
p_dep_sub.add_argument("--agent", required=True, choices=VALID_NODES, help="Agent node to spawn subagent on")
|
||||
p_dep_sub.add_argument("--title", default="subagent-task", help="Title for the subagent session")
|
||||
p_dep_sub.add_argument("prompt", help="Task prompt for the subagent")
|
||||
p_dep_sub.add_argument("--wait", type=int, default=30, help="Seconds to wait for subagent response")
|
||||
|
||||
# Domain: muse (fast headless gateway via muse-cli-node with isolated per-node Cloudflare WARP egress)
|
||||
p_muse = subparsers.add_parser("muse", parents=[common], help="Direct headless gateway client (muse-cli-node)")
|
||||
p_muse.add_argument("node", choices=VALID_NODES, help="Target agent node")
|
||||
@@ -3506,6 +3577,8 @@ def main():
|
||||
cmd_loop_strat(args)
|
||||
elif args.domain == "vars":
|
||||
cmd_loop_vars(args)
|
||||
elif args.domain == "deploy":
|
||||
cmd_deploy(args)
|
||||
elif args.domain == "muse":
|
||||
cmd = [str(BIN_DIR / "muse-cli-node"), args.node] + (args.muse_args or [])
|
||||
res = subprocess.run(cmd)
|
||||
|
||||
Reference in New Issue
Block a user