feat(fleet): expand VALID_AGENTS to include dev and def
This commit is contained in:
+175
-2
@@ -41,7 +41,9 @@ DM_LOG = NETVM_ROOT / "dm-log.jsonl"
|
||||
WATCHDOG_STATE = NETVM_ROOT / "main-chat-watchdog.state"
|
||||
|
||||
NAME_RE = re.compile(r"^[a-z0-9-]{1,64}$")
|
||||
VALID_AGENTS = {"muse", "pip", "646", "opm"}
|
||||
VALID_AGENTS = {"muse", "pip", "646", "opm", "dev", "def"}
|
||||
THREAD_RE = re.compile(r"^[A-Za-z0-9-]{1,64}$")
|
||||
THREAD_OPS = ("list", "pin", "unpin", "archive", "unarchive")
|
||||
|
||||
# Default sidechat per agent for `box notify`. Mirrors super-cli.py
|
||||
# DEFAULT_AGENT_SIDECHATS (kept in sync manually; super-cli is the
|
||||
@@ -1216,6 +1218,14 @@ loop actions:
|
||||
loop-resolve <dm_id> [note]
|
||||
loop-remediate [--dry-run]
|
||||
|
||||
thread actions (muse-cli gateway; hyphenated aliases for `thread <sub>`):
|
||||
thread-list --agent <agent> [--limit N]
|
||||
thread-pin --agent <agent> --thread <id>
|
||||
thread-unpin --agent <agent> --thread <id>
|
||||
thread-archive --agent <agent> --thread <id> --confirm
|
||||
thread-unarchive --agent <agent> --thread <id>
|
||||
thread-rename --agent <agent> --thread <id> --title <title>
|
||||
|
||||
notify:
|
||||
notify <agent> <message> [--sidechat <name>] [--allow-main-chat]
|
||||
Send a DM to an agent's default sidechat (never main chat unless
|
||||
@@ -1224,7 +1234,74 @@ notify:
|
||||
policy:
|
||||
policy chat policy version, rule, and compliance summary
|
||||
policy check <agent> per-agent compliance detail
|
||||
policy show print the full CHAT_POLICY.md"""
|
||||
policy show print the full CHAT_POLICY.md
|
||||
|
||||
thread actions (muse-cli gateway):
|
||||
thread list <agent> [--limit N]
|
||||
thread pin <agent> <thread-id>
|
||||
thread unpin <agent> <thread-id>
|
||||
thread archive <agent> <thread-id> --confirm
|
||||
thread unarchive <agent> <thread-id>"""
|
||||
|
||||
|
||||
def act_thread(op, agent, thread=None, title=None, limit=None, confirm=False):
|
||||
"""Thread bookkeeping via bin/muse-threads.py (hybrid gateway).
|
||||
|
||||
ops: list | pin | unpin | archive | unarchive | rename
|
||||
archive requires confirm=True (the --confirm flag; BatchMode SSH
|
||||
cannot prompt, so the flag is the explicit confirmation).
|
||||
"""
|
||||
if op not in ("list", "pin", "unpin", "archive", "unarchive", "rename"):
|
||||
fail("BAD_NAME", "usage: thread list|pin|unpin|archive|unarchive|rename <agent> [args]")
|
||||
if agent not in VALID_AGENTS:
|
||||
fail("BAD_NAME", f"agent must be one of {sorted(VALID_AGENTS)}")
|
||||
if op == "list":
|
||||
audit("thread-list-" + agent)
|
||||
else:
|
||||
if not thread or not THREAD_RE.match(thread):
|
||||
fail("BAD_NAME", "thread id must match ^[A-Za-z0-9-]{1,64}$",
|
||||
{"field": "thread", "value": thread})
|
||||
if op == "archive" and not confirm:
|
||||
fail("CONFIRM_REQUIRED",
|
||||
"archive is a visible side effect -- pass --confirm to proceed",
|
||||
{"would_archive": {"agent": agent, "thread": thread}})
|
||||
if op == "rename" and not title:
|
||||
fail("BAD_NAME", "rename requires a title")
|
||||
audit("thread-" + op, f"{agent}/{thread}")
|
||||
helper = os.path.join(os.path.dirname(os.path.abspath(__file__)), "muse-threads.py")
|
||||
cmd = [sys.executable, helper, op, "--agent", agent]
|
||||
if thread:
|
||||
cmd += ["--thread", thread]
|
||||
if title:
|
||||
cmd += ["--title", title]
|
||||
try:
|
||||
p = subprocess.run(cmd, capture_output=True, text=True, timeout=120)
|
||||
except subprocess.TimeoutExpired:
|
||||
fail("GATEWAY_UNAVAILABLE", f"muse-threads.py timed out (op={op})")
|
||||
except OSError as e:
|
||||
fail("GATEWAY_UNAVAILABLE", f"cannot run muse-threads.py: {e}")
|
||||
# Helper prints one JSON object; relay it, applying --limit for list
|
||||
if p.returncode == 0:
|
||||
raw = p.stdout.strip()
|
||||
if limit is not None and op == "list" and raw:
|
||||
try:
|
||||
data = json.loads(raw)
|
||||
if isinstance(data.get("threads"), list):
|
||||
data["threads"] = data["threads"][:limit]
|
||||
print(json.dumps(data))
|
||||
return
|
||||
except ValueError:
|
||||
pass
|
||||
print(raw if raw else '{"ok": true}')
|
||||
return
|
||||
# Failure: try to extract the helper's JSON error, else wrap
|
||||
try:
|
||||
err = json.loads(p.stdout.strip() or "{}")
|
||||
fail(err.get("code", "GATEWAY_ERROR"), err.get("error", "thread op failed"),
|
||||
err.get("detail"))
|
||||
except (ValueError, AttributeError):
|
||||
fail("GATEWAY_ERROR", f"thread {op} failed",
|
||||
(p.stderr.strip() or p.stdout.strip())[:300])
|
||||
|
||||
|
||||
def main(argv):
|
||||
@@ -1374,6 +1451,30 @@ def main(argv):
|
||||
elif action == "loop-remediate":
|
||||
dry = "--dry-run" in rest
|
||||
act_loop_remediate(dry_run=dry)
|
||||
elif action in ("thread-list", "thread-pin", "thread-unpin",
|
||||
"thread-archive", "thread-unarchive", "thread-rename"):
|
||||
op = action[len("thread-"):]
|
||||
agent = None
|
||||
thread = None
|
||||
title = None
|
||||
idx = 0
|
||||
while idx < len(rest):
|
||||
if rest[idx] == "--agent" and idx + 1 < len(rest):
|
||||
agent = rest[idx + 1]
|
||||
idx += 2
|
||||
elif rest[idx] == "--thread" and idx + 1 < len(rest):
|
||||
thread = rest[idx + 1]
|
||||
idx += 2
|
||||
elif rest[idx] == "--title" and idx + 1 < len(rest):
|
||||
title = rest[idx + 1]
|
||||
idx += 2
|
||||
else:
|
||||
idx += 1
|
||||
if not agent:
|
||||
fail("BAD_NAME", f"usage: {action} --agent <agent> [--thread <id>] [--title <title>]")
|
||||
if op in ("pin", "unpin", "archive", "unarchive", "rename") and not thread:
|
||||
fail("BAD_NAME", f"usage: {action} --agent <agent> --thread <id> [--title <title>]")
|
||||
act_thread(op, agent, thread, title)
|
||||
elif action == "notify":
|
||||
# notify <agent> <message> [--sidechat <name>] [--allow-main-chat]
|
||||
args = list(rest)
|
||||
@@ -1437,6 +1538,78 @@ def main(argv):
|
||||
if args:
|
||||
fail("BAD_ARGS", "usage: main-loop %s [--agent <name>]" % sub)
|
||||
act_main_loop(sub, agent)
|
||||
elif action == "thread":
|
||||
if not rest or rest[0] not in THREAD_OPS:
|
||||
fail("BAD_NAME", "usage: thread list|pin|unpin|archive|unarchive <agent> [args]")
|
||||
sub = rest[0]
|
||||
args = rest[1:]
|
||||
if not args:
|
||||
fail("BAD_NAME", f"usage: thread {sub} <agent> [...]")
|
||||
agent = args[0]
|
||||
args = args[1:]
|
||||
thread_id = None
|
||||
limit = None
|
||||
confirm = "--confirm" in args
|
||||
args = [a for a in args if a != "--confirm"]
|
||||
if confirm and sub != "archive":
|
||||
fail("BAD_ARGS", "--confirm only applies to thread archive")
|
||||
idx = 0
|
||||
while idx < len(args):
|
||||
if args[idx] == "--limit" and idx + 1 < len(args):
|
||||
try:
|
||||
limit = int(args[idx + 1])
|
||||
except ValueError:
|
||||
fail("BAD_LIMIT", "usage: thread list <agent> [--limit N]")
|
||||
idx += 2
|
||||
elif thread_id is None and not args[idx].startswith("--"):
|
||||
thread_id = args[idx]
|
||||
idx += 1
|
||||
else:
|
||||
fail("BAD_ARGS", f"usage: thread {sub} <agent> [...]")
|
||||
if limit is not None and sub != "list":
|
||||
fail("BAD_ARGS", "--limit only applies to thread list")
|
||||
if sub in ("pin", "unpin", "archive", "unarchive") and not thread_id:
|
||||
fail("BAD_NAME", f"usage: thread {sub} <agent> <thread-id> [--confirm]")
|
||||
if sub == "list" and thread_id:
|
||||
fail("BAD_ARGS", "usage: thread list <agent> [--limit N]")
|
||||
act_thread(sub, agent, thread=thread_id, limit=limit, confirm=confirm)
|
||||
elif action in ("thread-list", "thread-pin", "thread-unpin",
|
||||
"thread-archive", "thread-unarchive", "thread-rename"):
|
||||
op = action[len("thread-"):]
|
||||
agent = None
|
||||
thread_id = None
|
||||
title = None
|
||||
limit = None
|
||||
confirm = "--confirm" in rest
|
||||
args = [a for a in rest if a != "--confirm"]
|
||||
if confirm and op != "archive":
|
||||
fail("BAD_ARGS", "--confirm only applies to thread-archive")
|
||||
idx = 0
|
||||
while idx < len(args):
|
||||
if args[idx] == "--agent" and idx + 1 < len(args):
|
||||
agent = args[idx + 1]
|
||||
idx += 2
|
||||
elif args[idx] == "--thread" and idx + 1 < len(args):
|
||||
thread_id = args[idx + 1]
|
||||
idx += 2
|
||||
elif args[idx] == "--title" and idx + 1 < len(args):
|
||||
title = args[idx + 1]
|
||||
idx += 2
|
||||
elif args[idx] == "--limit" and idx + 1 < len(args):
|
||||
try:
|
||||
limit = int(args[idx + 1])
|
||||
except ValueError:
|
||||
fail("BAD_LIMIT", "usage: thread-list --agent <agent> [--limit N]")
|
||||
idx += 2
|
||||
else:
|
||||
fail("BAD_ARGS", f"usage: {action} --agent <agent> [--thread <id>] [--title <t>] [--confirm] [--limit N]")
|
||||
if not agent:
|
||||
fail("BAD_NAME", f"usage: {action} --agent <agent> [...]")
|
||||
if limit is not None and op != "list":
|
||||
fail("BAD_ARGS", "--limit only applies to thread-list")
|
||||
if op in ("pin", "unpin", "archive", "unarchive", "rename") and not thread_id:
|
||||
fail("BAD_NAME", f"usage: {action} --agent <agent> --thread <id> [...]")
|
||||
act_thread(op, agent, thread=thread_id, title=title, limit=limit, confirm=confirm)
|
||||
elif action == "dm-log":
|
||||
limit = 50
|
||||
if rest:
|
||||
|
||||
Reference in New Issue
Block a user