feat(prompts): native Muse tools (cron.create, subagent spawn) primary in envelope; harvester bridges native names to box ops
This commit is contained in:
@@ -125,6 +125,48 @@ def detect_explicit_refusal(text):
|
||||
return any(re.search(pat, t) for pat in REFUSAL_PATTERNS)
|
||||
|
||||
|
||||
# Muse-native tool names (docs.muse-dev.online/capabilities.html) -> box exec ops.
|
||||
NATIVE_ALIASES = {
|
||||
"cron.create": "followup.create",
|
||||
"cron.runonce": "followup.create",
|
||||
"cron.schedule": "followup.create",
|
||||
"subagent.spawn": "swarm.spawn",
|
||||
"subagents.spawn": "swarm.spawn",
|
||||
"subagent.list": "swarm.list",
|
||||
"subagent.status": "swarm.status",
|
||||
}
|
||||
|
||||
|
||||
def normalize_native_call(op, args):
|
||||
"""Bridge native Muse tool names/args to the box ops the exec daemon allows."""
|
||||
op = NATIVE_ALIASES.get(op, op)
|
||||
if not isinstance(args, dict):
|
||||
return op, args
|
||||
args = dict(args)
|
||||
if op == "followup.create":
|
||||
for k in ("kind", "mode", "schedule"): # native cron fields the box op does not take
|
||||
args.pop(k, None)
|
||||
if "in_m" not in args:
|
||||
for k in ("minutes", "in_minutes", "delay_m", "runonce_m"):
|
||||
if k in args:
|
||||
args["in_m"] = args.pop(k)
|
||||
break
|
||||
if "prompt" not in args:
|
||||
for k in ("body", "task", "message", "text"):
|
||||
if k in args:
|
||||
args["prompt"] = args.pop(k)
|
||||
break
|
||||
elif op == "swarm.spawn":
|
||||
if "task" not in args:
|
||||
for k in ("prompt", "body", "instructions"):
|
||||
if k in args:
|
||||
args["task"] = args.pop(k)
|
||||
break
|
||||
if "count" not in args and "n" in args:
|
||||
args["count"] = args.pop("n")
|
||||
return op, args
|
||||
|
||||
|
||||
def parse_tool_calls(text):
|
||||
"""
|
||||
Extract structured tool/exec calls from assistant messages.
|
||||
@@ -166,7 +208,7 @@ def parse_tool_calls(text):
|
||||
# De-duplicate identical calls within one message (envelope repeats the spawn
|
||||
# call at top and bottom; an agent echoing both must not run it twice).
|
||||
seen, unique = set(), []
|
||||
for op, args in calls:
|
||||
for op, args in [normalize_native_call(o, a) for o, a in calls]:
|
||||
key = (op, json.dumps(args, sort_keys=True, default=str))
|
||||
if key in seen:
|
||||
continue
|
||||
|
||||
Reference in New Issue
Block a user