feat: unified fleet CLI, Main Chat preservation policy, sidechat routing, and file transfers
- Added CHAT_POLICY.md and README.md banner enforcing sidechat-first and file-transfer-first rules. - Added strict Main Chat block to super dm send and super dm wo with --allow-main-chat override. - Implemented file transfer staging and metadata registry in super dm send-file and super dm files (with clean subcommand). - Added full job lifecycle management (show, create, enable, disable, delete, run --follow) to super-cli.py and box-ctl.py. - Audited all jobs in jobs/*.json and redirected automated dispatches away from Main Chat. - Hardened chromebox-watchdog.sh with systemd user session environment exports and stale singleton cleanup. - Added compose_check command choice to muse-chat-api.py.
This commit is contained in:
@@ -62,6 +62,12 @@ try:
|
||||
except ImportError:
|
||||
HAS_REGISTRY = False
|
||||
|
||||
try:
|
||||
import pipeline_engine
|
||||
HAS_PIPELINE = True
|
||||
except ImportError:
|
||||
HAS_PIPELINE = False
|
||||
|
||||
VALID_AGENTS = ["muse", "pip", "646", "opm"]
|
||||
DEFAULT_PORTS = {"muse": 9410, "pip": 9420, "646": 9430, "opm": 9440}
|
||||
|
||||
@@ -384,8 +390,8 @@ def harvest_agent_thread(cdp, agent, thread_info, watermarks, followups, dry_run
|
||||
}
|
||||
if not dry_run:
|
||||
append_jsonl(JOB_LOG, job_record)
|
||||
# Trigger chain_next if configured
|
||||
trigger_chain_next(job_id, result_text)
|
||||
# Trigger pipeline chaining or next job if configured
|
||||
trigger_chain_next(job_id, result_text, success=not is_fail)
|
||||
|
||||
# Check and clear pending follow-ups
|
||||
clear_matching_followups(followups, agent, thread_id, mid, text, dry_run)
|
||||
@@ -393,9 +399,8 @@ def harvest_agent_thread(cdp, agent, thread_info, watermarks, followups, dry_run
|
||||
return new_messages, new_wm, job_results
|
||||
|
||||
|
||||
def trigger_chain_next(job_id, result_text):
|
||||
"""If the completed job has a chain_next property, dispatch it with context."""
|
||||
# Job ID format: <name>-<timestamp>-<uuid>
|
||||
def trigger_chain_next(job_id, result_text, success=True):
|
||||
"""If the completed job has on_success, on_failure, or chain_next, dispatch downstream."""
|
||||
parts = job_id.split("-")
|
||||
if len(parts) < 3:
|
||||
return
|
||||
@@ -404,20 +409,55 @@ def trigger_chain_next(job_id, result_text):
|
||||
if not job_file.exists():
|
||||
return
|
||||
|
||||
# Update pipeline ledger if this job belongs to an active pipeline
|
||||
pipeline_run_id = None
|
||||
step_n = 1
|
||||
if HAS_PIPELINE:
|
||||
run_entry, step_entry = pipeline_engine.record_step_result(job_id, success, result_text)
|
||||
if run_entry:
|
||||
pipeline_run_id = run_entry.get("run_id")
|
||||
step_n = step_entry.get("step_n", 1) + 1
|
||||
|
||||
try:
|
||||
with open(job_file, "r", encoding="utf-8") as f:
|
||||
cfg = json.load(f)
|
||||
chain_next = cfg.get("chain_next")
|
||||
if chain_next and (JOBS_DIR / f"{chain_next}.json").exists():
|
||||
|
||||
next_job = None
|
||||
if success:
|
||||
next_job = cfg.get("on_success") or cfg.get("chain_next")
|
||||
else:
|
||||
next_job = cfg.get("on_failure")
|
||||
|
||||
if next_job and (JOBS_DIR / f"{next_job}.json").exists():
|
||||
# Inter-step settle delay to prevent browser race conditions
|
||||
step_delay = int(cfg.get("step_delay", 5))
|
||||
if step_delay > 0:
|
||||
time.sleep(step_delay)
|
||||
|
||||
env = os.environ.copy()
|
||||
env["CHAIN_PREV_JOB_ID"] = job_id
|
||||
env["CHAIN_PREV_RESULT"] = result_text[:1000]
|
||||
if pipeline_run_id:
|
||||
env["CHAIN_PIPELINE_RUN_ID"] = pipeline_run_id
|
||||
env["CHAIN_STEP_N"] = str(step_n)
|
||||
|
||||
cmd = [sys.executable, str(DISPATCH_PY), next_job]
|
||||
if pipeline_run_id:
|
||||
cmd.extend(["--pipeline-run", pipeline_run_id, "--step-n", str(step_n)])
|
||||
|
||||
subprocess.Popen(
|
||||
[sys.executable, str(DISPATCH_PY), chain_next],
|
||||
cmd,
|
||||
env=env,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL,
|
||||
)
|
||||
else:
|
||||
# End of chain for this pipeline run
|
||||
if pipeline_run_id and HAS_PIPELINE:
|
||||
if success:
|
||||
pipeline_engine.complete_pipeline(pipeline_run_id)
|
||||
else:
|
||||
pipeline_engine.fail_pipeline(pipeline_run_id, "step_failed_without_fallback")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
Reference in New Issue
Block a user