Add main-loop enable/disable per-agent toggle
- self_main_loop.py: 'enabled' map in config (default all True); do_check skips disabled agents (marks disabled:true in results); new enable/disable actions with optional --agent. - box-ctl.py: main-loop enable|disable [--agent <name>] actions, USAGE updated. Check skips disabled agents so the loop can be toggled per Chromebox without stopping the systemd timer. Session: sidechat/chromebox-ops
This commit is contained in:
+28
-9
@@ -650,18 +650,25 @@ def act_notify(agent, message, sidechat=None, allow_main_chat=False):
|
|||||||
out(True, agent=agent, target=target, sent=True)
|
out(True, agent=agent, target=target, sent=True)
|
||||||
|
|
||||||
|
|
||||||
def act_main_loop(sub):
|
def act_main_loop(sub, agent=None):
|
||||||
"""Run the main-chat self-monitor loop (self_main_loop.py).
|
"""Run the main-chat self-monitor loop (self_main_loop.py).
|
||||||
|
|
||||||
check: one loop iteration -> JSON {ok, prompted, errors, new_total, agents}
|
check: one loop iteration -> JSON {ok, prompted, errors, new_total, agents}
|
||||||
status: watermark / last run / prompt-sidechat config, no chat reads.
|
status: watermark / last run / prompt-sidechat config, no chat reads.
|
||||||
|
enable: enable the loop (per-agent or all); check skips disabled agents.
|
||||||
|
disable: disable the loop (per-agent or all).
|
||||||
"""
|
"""
|
||||||
if sub not in ("check", "status"):
|
if sub not in ("check", "status", "enable", "disable"):
|
||||||
fail("BAD_NAME", "usage: main-loop check|status")
|
fail("BAD_NAME", "usage: main-loop check|status|enable|disable [--agent <name>]")
|
||||||
audit("main-loop-" + sub)
|
if agent is not None and agent not in VALID_AGENTS:
|
||||||
|
fail("BAD_NAME", f"agent must be one of {sorted(VALID_AGENTS)}")
|
||||||
|
audit("main-loop-" + sub + (f"-{agent}" if agent else ""))
|
||||||
script = BIN / "self_main_loop.py"
|
script = BIN / "self_main_loop.py"
|
||||||
|
cmd = [sys.executable, str(script), sub]
|
||||||
|
if agent:
|
||||||
|
cmd += ["--agent", agent]
|
||||||
try:
|
try:
|
||||||
r = subprocess.run([sys.executable, str(script), sub],
|
r = subprocess.run(cmd,
|
||||||
capture_output=True, text=True, timeout=600)
|
capture_output=True, text=True, timeout=600)
|
||||||
except subprocess.TimeoutExpired:
|
except subprocess.TimeoutExpired:
|
||||||
fail("LOOP_TIMEOUT", "self_main_loop.py timed out")
|
fail("LOOP_TIMEOUT", "self_main_loop.py timed out")
|
||||||
@@ -1171,7 +1178,8 @@ fleet:
|
|||||||
relay-health
|
relay-health
|
||||||
cdp-latency
|
cdp-latency
|
||||||
identity-audit VM identity audit drift check
|
identity-audit VM identity audit drift check
|
||||||
main-loop check|status main-chat self-monitor: prompt sidechat on new main msgs
|
main-loop check|status|enable|disable [--agent <name>]
|
||||||
|
main-chat self-monitor; enable/disable per agent or all
|
||||||
|
|
||||||
timer actions:
|
timer actions:
|
||||||
timer-list
|
timer-list
|
||||||
@@ -1415,9 +1423,20 @@ def main(argv):
|
|||||||
fail("BAD_ARGS", "usage: chrome-errors")
|
fail("BAD_ARGS", "usage: chrome-errors")
|
||||||
act_chrome_errors()
|
act_chrome_errors()
|
||||||
elif action == "main-loop":
|
elif action == "main-loop":
|
||||||
if len(rest) != 1 or rest[0] not in ("check", "status"):
|
if not rest or rest[0] not in ("check", "status", "enable", "disable"):
|
||||||
fail("BAD_NAME", "usage: main-loop check|status")
|
fail("BAD_NAME", "usage: main-loop check|status|enable|disable [--agent <name>]")
|
||||||
act_main_loop(rest[0])
|
sub = rest[0]
|
||||||
|
agent = None
|
||||||
|
args = rest[1:]
|
||||||
|
if "--agent" in args:
|
||||||
|
i = args.index("--agent")
|
||||||
|
if i + 1 >= len(args):
|
||||||
|
fail("BAD_ARGS", "usage: main-loop %s [--agent <name>]" % sub)
|
||||||
|
agent = args[i + 1]
|
||||||
|
args = args[:i] + args[i + 2:]
|
||||||
|
if args:
|
||||||
|
fail("BAD_ARGS", "usage: main-loop %s [--agent <name>]" % sub)
|
||||||
|
act_main_loop(sub, agent)
|
||||||
elif action == "dm-log":
|
elif action == "dm-log":
|
||||||
limit = 50
|
limit = 50
|
||||||
if rest:
|
if rest:
|
||||||
|
|||||||
+40
-2
@@ -93,11 +93,16 @@ def get_config(st):
|
|||||||
agents = [a for a in agents if a in DEFAULT_AGENTS]
|
agents = [a for a in agents if a in DEFAULT_AGENTS]
|
||||||
prompt = dict(DEFAULT_PROMPT_SIDECHAT)
|
prompt = dict(DEFAULT_PROMPT_SIDECHAT)
|
||||||
prompt.update(cfg.get("prompt_sidechat") or {})
|
prompt.update(cfg.get("prompt_sidechat") or {})
|
||||||
|
enabled = dict(cfg.get("enabled") or {})
|
||||||
|
# Default any unlisted agent to enabled.
|
||||||
|
for a in agents:
|
||||||
|
enabled.setdefault(a, True)
|
||||||
return {
|
return {
|
||||||
"agents": agents or list(DEFAULT_AGENTS),
|
"agents": agents or list(DEFAULT_AGENTS),
|
||||||
"prompt_sidechat": prompt,
|
"prompt_sidechat": prompt,
|
||||||
"sender": cfg.get("sender") or DEFAULT_SENDER,
|
"sender": cfg.get("sender") or DEFAULT_SENDER,
|
||||||
"read_limit": int(cfg.get("read_limit") or READ_LIMIT),
|
"read_limit": int(cfg.get("read_limit") or READ_LIMIT),
|
||||||
|
"enabled": enabled,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -180,11 +185,15 @@ def do_check(only_agent=None):
|
|||||||
return {"ok": False, "error": "unknown agent: %s" % only_agent}
|
return {"ok": False, "error": "unknown agent: %s" % only_agent}
|
||||||
|
|
||||||
agents_state = st.get("agents") or {}
|
agents_state = st.get("agents") or {}
|
||||||
|
enabled = cfg.get("enabled") or {}
|
||||||
results = {}
|
results = {}
|
||||||
prompted = 0
|
prompted = 0
|
||||||
errors = 0
|
errors = 0
|
||||||
|
|
||||||
for agent in agents:
|
for agent in agents:
|
||||||
|
if not enabled.get(agent, True):
|
||||||
|
results[agent] = {"ok": True, "new": 0, "disabled": True}
|
||||||
|
continue
|
||||||
wm = agents_state.get(agent) or {}
|
wm = agents_state.get(agent) or {}
|
||||||
ok, payload = read_main_chat(agent, cfg["read_limit"])
|
ok, payload = read_main_chat(agent, cfg["read_limit"])
|
||||||
if not ok:
|
if not ok:
|
||||||
@@ -231,19 +240,40 @@ def do_check(only_agent=None):
|
|||||||
st["config"] = {"agents": cfg["agents"],
|
st["config"] = {"agents": cfg["agents"],
|
||||||
"prompt_sidechat": cfg["prompt_sidechat"],
|
"prompt_sidechat": cfg["prompt_sidechat"],
|
||||||
"sender": cfg["sender"],
|
"sender": cfg["sender"],
|
||||||
"read_limit": cfg["read_limit"]}
|
"read_limit": cfg["read_limit"],
|
||||||
|
"enabled": dict(cfg.get("enabled") or
|
||||||
|
{a: True for a in cfg["agents"]})}
|
||||||
save_state(st)
|
save_state(st)
|
||||||
return {"ok": errors == 0, "prompted": prompted, "errors": errors,
|
return {"ok": errors == 0, "prompted": prompted, "errors": errors,
|
||||||
"new_total": sum(r.get("new", 0) for r in results.values()),
|
"new_total": sum(r.get("new", 0) for r in results.values()),
|
||||||
"agents": results}
|
"agents": results}
|
||||||
|
|
||||||
|
|
||||||
|
def _set_enabled(agent, value):
|
||||||
|
"""Enable/disable the loop for one agent (or all if agent is None)."""
|
||||||
|
st = load_state()
|
||||||
|
cfg = st.get("config") or {}
|
||||||
|
agents = cfg.get("agents") or list(DEFAULT_AGENTS)
|
||||||
|
agents = [a for a in agents if a in DEFAULT_AGENTS] or list(DEFAULT_AGENTS)
|
||||||
|
if agent and agent not in DEFAULT_AGENTS:
|
||||||
|
return {"ok": False, "error": "unknown agent: %s" % agent}
|
||||||
|
enabled = dict(cfg.get("enabled") or {})
|
||||||
|
targets = [agent] if agent else agents
|
||||||
|
for a in targets:
|
||||||
|
enabled[a] = value
|
||||||
|
cfg["enabled"] = enabled
|
||||||
|
st["config"] = cfg
|
||||||
|
save_state(st)
|
||||||
|
return {"ok": True, "enabled": {a: enabled.get(a, True) for a in targets}}
|
||||||
|
|
||||||
|
|
||||||
def do_status():
|
def do_status():
|
||||||
st = load_state()
|
st = load_state()
|
||||||
cfg = get_config(st)
|
cfg = get_config(st)
|
||||||
agents_state = st.get("agents") or {}
|
agents_state = st.get("agents") or {}
|
||||||
return {"ok": True,
|
return {"ok": True,
|
||||||
"config": cfg,
|
"config": cfg,
|
||||||
|
"enabled": cfg.get("enabled") or {},
|
||||||
"watermark": agents_state,
|
"watermark": agents_state,
|
||||||
"last_run": st.get("last_run"),
|
"last_run": st.get("last_run"),
|
||||||
"last_result": st.get("last_result")}
|
"last_result": st.get("last_result")}
|
||||||
@@ -261,8 +291,16 @@ def main(argv):
|
|||||||
print(json.dumps(do_status()))
|
print(json.dumps(do_status()))
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
if action == "enable":
|
||||||
|
print(json.dumps(_set_enabled(only_agent, True)))
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if action == "disable":
|
||||||
|
print(json.dumps(_set_enabled(only_agent, False)))
|
||||||
|
return 0
|
||||||
|
|
||||||
if action != "check":
|
if action != "check":
|
||||||
print(json.dumps({"ok": False, "error": "usage: check|status [--agent X]"}))
|
print(json.dumps({"ok": False, "error": "usage: check|status|enable|disable [--agent X]"}))
|
||||||
return 2
|
return 2
|
||||||
|
|
||||||
# Overlap guard: timer may fire while a slow run is still going.
|
# Overlap guard: timer may fire while a slow run is still going.
|
||||||
|
|||||||
Reference in New Issue
Block a user