feat(harvester): synthesize [RESULT <job-id>] DECLINE on explicit assistant natural language refusal

This commit is contained in:
operator
2026-10-05 04:44:38 +00:00
parent ac8fd9a06e
commit 7581e983d5
+39
View File
@@ -108,6 +108,23 @@ def iter_verb_markers(text):
yield m.group(1), m.group(2).strip()
REFUSAL_PATTERNS = [
r"\bnot doing that\b",
r"\brefuse to\b",
r"\bdecline to\b",
r"\bdeclining\b",
r"\bwill not perform\b",
r"\bnot emitting the \[?result\]?\b",
r"\bnot sending result\b",
r"\bproduction change with recursion risk\b",
]
def detect_explicit_refusal(text):
"""Detect plain-language refusal/decline from an assistant in a task thread."""
t = (text or "").lower()
return any(re.search(pat, t) for pat in REFUSAL_PATTERNS)
def parse_tool_calls(text):
"""
Extract structured tool/exec calls from assistant messages.
@@ -581,6 +598,28 @@ def process_messages(raw_messages, agent, thread_id, thread_name, last_wm, follo
if author == "assistant":
markers = list(iter_result_markers(text))
verbs = list(iter_verb_markers(text))
# Synthesize [RESULT <job-id>] DECLINE if assistant explicitly refuses the task in plain text
if not markers and not verbs and detect_explicit_refusal(text):
matching_jid = None
for f_id, f_rec in followups.items():
if f_rec.get("status") in ("pending", "acknowledged") and (f_rec.get("thread_uuid") == thread_id or f_rec.get("recipient") == agent):
matching_jid = f_rec.get("job_id") or f_id
break
if not matching_jid and JOB_SIDECHATS_FILE.exists():
try:
sc_state = json.loads(JOB_SIDECHATS_FILE.read_text(encoding="utf-8"))
for k, v in sc_state.items():
if isinstance(v, dict) and v.get("thread_uuid") == thread_id:
matching_jid = k
break
except Exception:
pass
if matching_jid:
refusal_snippet = f"DECLINE: {text.strip()[:180]}"
print(f"[{agent}] Synthesizing [RESULT {matching_jid}] DECLINE from explicit assistant refusal")
markers.append((matching_jid, refusal_snippet))
# Check for structured tool / command calls from the agent
tool_calls = parse_tool_calls(text)
if tool_calls and not dry_run: