Files
box/bin/prompt_envelope.py
T

132 lines
5.6 KiB
Python

#!/usr/bin/env python3
"""prompt_envelope.py — wraps every dispatched job prompt so agents DO work.
Why: agents treated job DMs as informational and replied with prose; swarms and
timers never got created. The envelope puts an executable subagent-spawn tool
call at the TOP (before the task) and repeats it at the BOTTOM (after the task),
with a work-first mandate and explicit tool/curl surface in between.
Profiles tune the spawn recipe per job family; add new ones to PROFILES.
"""
import json
import re
import hashlib
BOX_API = "https://box.muse-dev.online/api/box"
def _response_rule():
return (
"\nRESPONSE RULE: Execute your steps using [TOOL ...] directives or background tmux commands."
" When complete, conclude your output with the [RESULT ...] line so the harvester records it.\n"
)
def _box_surface_block(agent, job_name, job_id, target):
"""Clean runtime context block for the agent."""
header = (
"\n---- BOX RUNTIME ----\n"
"THREAD: " + str(target) + "\n"
"RUNTIME: https://box.muse-dev.online/\n"
"JOB: " + str(job_id) + "\n"
"AGENT: " + str(agent) + "\n"
)
return header + _response_rule()
UUID_RE = re.compile(r"[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}")
# profile -> (spawn count, followup minutes, spawn task hint)
PROFILES = {
"pulse": (2, 30, "execute your scope's standing work: set timers, verify them, report per-slot verdicts"),
"health": (2, 60, "run the prescribed health gates read-only and report per-gate verdicts"),
"swarm": (2, 20, "sweep swarms and sidechats read-only and report stale or unarchived items"),
"work": (2, 15, "split the task above into two independent halves and complete each"),
}
def pick_profile(job_name):
n = job_name or ""
if n.startswith("autonomy-pulse"):
return "pulse"
if "health" in n:
return "health"
if "swarm" in n:
return "swarm"
return "work"
def _tool(op, args):
# no ']' inside the JSON: the harvester's [TOOL ...] regex stops at the first one
return "[TOOL %s %s]" % (op, json.dumps(args, separators=(", ", ": ")))
def spawn_call(job_id, job_name, profile):
count, _, hint = PROFILES[profile]
# no brackets in the task text: the harvester's [TOOL ...] regex stops at the first ']'
task = "Subagent for job %s (%s): %s." % (job_id, job_name, hint)
return _tool("swarm.spawn", {"count": count, "task": task[:900], "label": (job_name or "job")[:60]})
def native_followup_call(job_id, job_name, profile, agent):
"""Muse-native one-shot cron (cron.create runonce) - bridged to followup.create."""
_, mins, _ = PROFILES[profile]
return _tool("cron.create", {
"kind": "runonce", "agent": agent, "in_m": mins,
"prompt": "Continue job %s (%s): verify with cron.runs and swarm.list then act or close." % (job_id, job_name)})
def followup_call(job_id, job_name, profile, agent):
_, mins, _ = PROFILES[profile]
return _tool("followup.create", {
"agent": agent, "in_m": mins,
"prompt": "Continue job %s (%s): check swarm results with swarm.list then act or close." % (job_id, job_name)})
def thread_url(target):
t = str(target)
return "https://box.muse-dev.online/%s/%s" % ("thread" if UUID_RE.fullmatch(t.lower()) else "sidechat", t)
def wrap(job_name, job_id, agent, target, rendered):
"""Return the full DM body (without the [JOB id] prefix)."""
profile = pick_profile(job_name)
spawn = spawn_call(job_id, job_name, profile)
follow = native_followup_call(job_id, job_name, profile, agent)
parts = job_id.split("-")
wo_id = parts[-1] if len(parts) > 1 and len(parts[-1]) >= 8 else hashlib.sha256(job_id.encode()).hexdigest()[:8]
session_name = f"work-{agent}-{wo_id}"
tmux_new = _tool("tmux.new", {"session": session_name, "command": "bash"})
tmux_send = _tool("tmux.send", {"session": session_name, "keys": "echo 'Starting task execution...'"})
top = (
f"[WO:{wo_id}] WORK ORDER - ACTION REQUIRED, NOT INFORMATIONAL.\n"
"Your FIRST output must be tool calls, not prose. Run background commands via shared tmux:\n"
" 1) %s\n"
" 2) %s\n"
" 3) %s\n"
" 4) %s\n"
"Each tool call returns a result message; keep going until the task is done.\n"
"---- TASK ----\n" % (tmux_new, tmux_send, spawn, follow)
)
has_result = "[RESULT" in rendered
bottom = (
"\n---- END TASK ----\n"
"REMINDER (importance: critical): work is only real if a tool call ran. "
"Execute and inspect commands in your background tmux session or subagents:\n"
" %s\n"
" %s\n"
" Capture output: [TOOL tmux.capture {\"session\": \"%s\", \"lines\": 30}]\n"
"Native tools (preferred): cron.create (runonce/interval), cron.status, cron.view, cron.runs "
"(truth about what ran), cron.run, subagent spawn; hooks.dry_run before hooks.enable. "
"Native cron form: %s\n"
"Box fallback tools: [TOOL health.check {}] [TOOL swarm.list {}] [TOOL swarm.status {\"swarm_id\": \"<id>\"}] "
"[TOOL swarm.results {\"swarm_id\": \"<id>\"}] [TOOL quality.check {}]\n"
% (spawn, follow, session_name, native_followup_call(job_id, job_name, profile, agent))
)
bottom += _box_surface_block(agent, job_name, job_id, target)
if not has_result:
bottom += "Close with: [RESULT %s] <OK|FAILED>: <summary of tool outputs>\n" % job_id
else:
bottom += "Close using the [RESULT ...] line specified in the task, summarising tool outputs.\n"
return top + rendered.strip() + "\n" + bottom