From 31c03e0d0f5ed1709606e1c152bebf44a25c1f82 Mon Sep 17 00:00:00 2001 From: operator Date: Mon, 5 Oct 2026 03:41:43 +0000 Subject: [PATCH] feat(box): enable real-time agent tool triggers and capabilities expansion --- bin/box-relay.sh | 57 ++++++++++++ bin/exec-constrained.py | 125 +++++++++++++++++++++++++- bin/job-dispatch.py | 3 +- bin/response-harvester.py | 110 +++++++++++++++++++++++ job-sidechats.json | 178 ++++++++++++++++++++++++++++---------- 5 files changed, 425 insertions(+), 48 deletions(-) diff --git a/bin/box-relay.sh b/bin/box-relay.sh index 5c8619d..0966147 100755 --- a/bin/box-relay.sh +++ b/bin/box-relay.sh @@ -240,6 +240,56 @@ case "$cmd" in ;; esac ;; + cron) + sub="${1:-runs}" + shift || true + case "$sub" in + runs|list) + call_exec "cron.runs" "{}" + ;; + status) + name="${1:-heartbeat}" + args=$(python3 -c "import json, sys; print(json.dumps({'name': sys.argv[1]}))" "$name") + call_exec "cron.status" "$args" + ;; + view) + name="${1:?usage: box cron view }" + args=$(python3 -c "import json, sys; print(json.dumps({'name': sys.argv[1]}))" "$name") + call_exec "cron.view" "$args" + ;; + run) + name="${1:?usage: box cron run }" + args=$(python3 -c "import json, sys; print(json.dumps({'job': sys.argv[1]}))" "$name") + call_exec "cron.run" "$args" + ;; + *) + echo "Usage: box cron runs|status|view|run ..." + ;; + esac + ;; + vars) + sub="${1:-list}" + shift || true + case "$sub" in + list) + call_exec "vars.list" "{}" + ;; + get) + name="${1:?usage: box vars get }" + args=$(python3 -c "import json, sys; print(json.dumps({'name': sys.argv[1]}))" "$name") + call_exec "vars.get" "$args" + ;; + set) + name="${1:?usage: box vars set }" + val="${2:?usage: box vars set }" + args=$(python3 -c "import json, sys; print(json.dumps({'name': sys.argv[1], 'value': sys.argv[2]}))" "$name" "$val") + call_exec "vars.set" "$args" + ;; + *) + echo "Usage: box vars list|get|set ..." + ;; + esac + ;; health) call_exec "health.check" "{}" ;; @@ -261,6 +311,13 @@ Usage: box dm read [] [] box thread list [] box thread view [] + box cron runs + box cron status [] + box cron view + box cron run + box vars list + box vars get + box vars set box health box ping box ops diff --git a/bin/exec-constrained.py b/bin/exec-constrained.py index a973729..bc32b1b 100755 --- a/bin/exec-constrained.py +++ b/bin/exec-constrained.py @@ -67,6 +67,7 @@ SIG_MAX_SKEW = 300 AGENTS = ('muse', 'pip', '646', 'opm', 'def', 'dev') TARGET_RE = re.compile(r'^[A-Za-z0-9][A-Za-z0-9/_.-]{0,63}$') JOB_RE = re.compile(r'^[a-z0-9][a-z0-9-]{0,63}$') +NAME_RE = re.compile(r'^[A-Za-z0-9_.-]{1,64}$') IDENT_RE = re.compile(r'^[a-z0-9-]+$') NONCE_RE = re.compile(r'^[0-9a-fA-F]{16,128}$') HEX_RE = re.compile(r'^[0-9a-f]{8,128}$') @@ -523,6 +524,91 @@ def _pipeline_run_build(a): return [sys.executable, os.path.join(BIN_DIR, 'super-cli.py'), 'deploy', 'pipeline', a['name']] +def _cron_status_validate(raw): + if not isinstance(raw, dict): + raise OpError('args must be an object') + allowed = {'name'} + for k in raw: + if k not in allowed: + raise OpError(f'unknown arg: {k}') + return {'name': _job_name(raw.get('name')) if raw.get('name') else 'heartbeat'} + + +def _cron_status_build(a): + return [sys.executable, os.path.join(BIN_DIR, 'box-ctl.py'), 'timer-status', a['name']] + + +def _cron_view_validate(raw): + if not isinstance(raw, dict): + raise OpError('args must be an object') + allowed = {'name'} + for k in raw: + if k not in allowed: + raise OpError(f'unknown arg: {k}') + return {'name': _job_name(raw.get('name'))} + + +def _cron_view_build(a): + return [sys.executable, os.path.join(BIN_DIR, 'box-ctl.py'), 'job-get', a['name']] + + +def _cron_runs_validate(raw): + if raw not in ({}, None): + raise OpError('cron.runs takes no required args') + return {} + + +def _cron_runs_build(a): + return [sys.executable, os.path.join(BIN_DIR, 'box-ctl.py'), 'job-list'] + + +def _vars_list_validate(raw): + if raw not in ({}, None): + raise OpError('vars.list takes no required args') + return {} + + +def _vars_list_build(a): + return [sys.executable, os.path.join(BIN_DIR, 'box-ctl.py'), 'vars-list'] + + +def _vars_get_validate(raw): + if not isinstance(raw, dict): + raise OpError('args must be an object') + allowed = {'name'} + for k in raw: + if k not in allowed: + raise OpError(f'unknown arg: {k}') + name = raw.get('name') + if not isinstance(name, str) or not NAME_RE.fullmatch(name): + raise OpError('name must match safe identifier') + return {'name': name} + + +def _vars_get_build(a): + return [sys.executable, os.path.join(BIN_DIR, 'box-ctl.py'), 'vars-get', a['name']] + + +def _vars_set_validate(raw): + if not isinstance(raw, dict): + raise OpError('args must be an object') + allowed = {'name', 'value'} + for k in raw: + if k not in allowed: + raise OpError(f'unknown arg: {k}') + name = raw.get('name') + if not isinstance(name, str) or not NAME_RE.fullmatch(name): + raise OpError('name must match safe identifier') + val = raw.get('value') + if val is None or not isinstance(val, (str, int, float, bool)): + raise OpError('value must be a scalar') + return {'name': name, 'value': str(val)} + + +def _vars_set_build(a): + return [sys.executable, os.path.join(BIN_DIR, 'box-ctl.py'), 'vars-set', a['name'], a['value']] + + # op -> {validate, build, timeout, side_effecting, description} OPS = { 'dm.send': { @@ -580,6 +666,41 @@ OPS = { 'timeout': 120, 'side_effecting': True, 'desc': 'Dispatch a multi-step pipeline across agents', }, + 'cron.status': { + 'validate': _cron_status_validate, 'build': _cron_status_build, + 'timeout': 30, 'side_effecting': False, + 'desc': 'Check systemd timer / scheduled check status', + }, + 'cron.view': { + 'validate': _cron_view_validate, 'build': _cron_view_build, + 'timeout': 30, 'side_effecting': False, + 'desc': 'View scheduled job definition and prompt', + }, + 'cron.runs': { + 'validate': _cron_runs_validate, 'build': _cron_runs_build, + 'timeout': 30, 'side_effecting': False, + 'desc': 'List all scheduled crons/jobs across the fleet', + }, + 'cron.run': { + 'validate': _job_run_validate, 'build': _job_run_build, + 'timeout': 300, 'side_effecting': True, + 'desc': 'Trigger on-demand execution of a scheduled job', + }, + 'vars.list': { + 'validate': _vars_list_validate, 'build': _vars_list_build, + 'timeout': 30, 'side_effecting': False, + 'desc': 'List intrinsic loop strategy and timing variables', + }, + 'vars.get': { + 'validate': _vars_get_validate, 'build': _vars_get_build, + 'timeout': 30, 'side_effecting': False, + 'desc': 'Get specific intrinsic loop variable', + }, + 'vars.set': { + 'validate': _vars_set_validate, 'build': _vars_set_build, + 'timeout': 30, 'side_effecting': True, + 'desc': 'Update intrinsic loop variable', + }, 'exec.ping': { 'validate': _health_validate, 'build': lambda a: ['/bin/echo', 'PONG'], @@ -757,7 +878,9 @@ class Handler(BaseHTTPRequestHandler): self._send(400, {'error': f'bad args: {e}'}) return except Exception as e: - self._send(500, {'error': 'internal'}) + sys.stderr.write(f'Validation/build exception for {op}: {e}\n') + import traceback; traceback.print_exc() + self._send(500, {'error': f'internal: {e}'}) return # --- execute (NO shell, argv only) diff --git a/bin/job-dispatch.py b/bin/job-dispatch.py index 7d9adaa..e3b8549 100755 --- a/bin/job-dispatch.py +++ b/bin/job-dispatch.py @@ -486,12 +486,13 @@ def main(): import muse_hybrid res, err = muse_hybrid.start_session(agent, title=channel_title) if res and not err and res.get("session_id"): - new_uuid = res.get("session_id") key_to_save = reuse_key or sc_name + is_persistent = bool(reuse_key) sc_state[key_to_save] = { "thread_uuid": new_uuid, "agent": agent, "title": channel_title, + "type": "persistent" if is_persistent else "ephemeral", "created_at": datetime.now(timezone.utc).isoformat() } save_sidechat_state(sc_state) diff --git a/bin/response-harvester.py b/bin/response-harvester.py index b097d32..fa5c0c9 100755 --- a/bin/response-harvester.py +++ b/bin/response-harvester.py @@ -28,6 +28,7 @@ import hashlib import json import os import re +import ssl import subprocess import sys import time @@ -105,6 +106,86 @@ def iter_verb_markers(text): yield m.group(1), m.group(2).strip() +def parse_tool_calls(text): + """ + Extract structured tool/exec calls from assistant messages. + Supports: + 1. [TOOL ] or [EXEC ] + 2. ```box / ```tool / ```exec JSON blocks + """ + calls = [] + for m in re.finditer(r"\[(?:TOOL|EXEC)\s+([a-zA-Z0-9_.-]+)(?:\s+(.*?))?\]", text or ""): + op = m.group(1).strip() + raw_args = (m.group(2) or "").strip() + args = {} + if raw_args: + try: + args = json.loads(raw_args) + except Exception: + args = {"raw": raw_args} + calls.append((op, args)) + + for m in re.finditer(r"```(?:box|tool|exec)\s*\n(.*?)```", text or "", re.DOTALL): + block = m.group(1).strip() + try: + d = json.loads(block) + if isinstance(d, dict) and "op" in d: + calls.append((d["op"], d.get("args", {}))) + except Exception: + pass + return calls + + +def execute_agent_tool(agent, op, args, timeout=30): + """ + Execute tool call via local exec-constrained HTTPS daemon over Tailscale. + Returns (ok, result_or_error_string). + """ + token = None + token_file = os.path.expanduser(f"~/.exec-tokens/{agent}") + if not os.path.exists(token_file): + token_file = os.path.expanduser("~/.exec-server-token") + if os.path.exists(token_file): + try: + with open(token_file) as f: + token = f.read().strip() + except Exception: + pass + + url = "https://100.123.153.75:8444/exec" + payload = json.dumps({"op": op, "args": args}).encode("utf-8") + headers = { + "Content-Type": "application/json", + "User-Agent": f"Box-Harvester/{agent}", + } + if token: + headers["Authorization"] = f"Bearer {token}" + + ctx = ssl.create_default_context() + ctx.check_hostname = False + ctx.verify_mode = ssl.CERT_NONE + + req = urllib.request.Request(url, data=payload, headers=headers, method="POST") + try: + with urllib.request.urlopen(req, context=ctx, timeout=timeout) as resp: + data = resp.read().decode("utf-8") + res = json.loads(data) + if res.get("rc") == 0: + return True, res.get("stdout", "").strip() or "OK" + elif "stdout" in res: + return False, (res.get("stdout") or res.get("stderr") or "failed").strip() + elif "error" in res: + return False, res["error"] + return True, json.dumps(res) + except urllib.error.HTTPError as he: + try: + err_body = he.read().decode("utf-8") + return False, f"HTTP {he.code}: {err_body}" + except Exception: + return False, f"HTTP {he.code}" + except Exception as e: + return False, str(e) + def utcnow(): return datetime.now(timezone.utc).isoformat() @@ -416,6 +497,35 @@ 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)) + # Check for structured tool / command calls from the agent + tool_calls = parse_tool_calls(text) + if tool_calls and not dry_run: + for op, args in tool_calls: + print(f"[{agent}] Executing tool '{op}' from message {mid[:8]} in thread {thread_name or thread_id[:8]}") + t_ok, t_res = execute_agent_tool(agent, op, args) + tool_record = { + "ts": utcnow(), + "type": "tool_exec", + "agent": agent, + "thread_id": thread_id, + "op": op, + "args": args, + "success": t_ok, + "result_snippet": str(t_res)[:300], + "msg_id": mid, + } + append_jsonl(JOB_LOG, tool_record) + + # Post tool output back to the originating thread via fast headless gateway + if thread_id and re.fullmatch(r"[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}", thread_id.lower()): + try: + import muse_hybrid + status_label = "OUTPUT" if t_ok else "ERROR" + resp_text = f"[{status_label} {op}]\n{t_res}" + muse_hybrid.send_message(agent, resp_text, thread_id=thread_id, wait=0) + except Exception as te: + sys.stderr.write(f"warning: failed to post tool response back to thread: {te}\n") + if markers or verbs: for job_id, result_text in markers: is_fail = is_fail_result(result_text) diff --git a/job-sidechats.json b/job-sidechats.json index 95df555..a12b957 100644 --- a/job-sidechats.json +++ b/job-sidechats.json @@ -1,230 +1,316 @@ { "646-pip-coord": { "thread_uuid": "4466d0c1-7961-4cf3-b99d-1ab7c38484c2", - "agent": "pip" + "agent": "pip", + "type": "persistent" }, "646-pip-coord@646": { "thread_uuid": "e3442b78-0300-4034-8cbe-7a0ab6c5764a", "agent": "646", - "description": "646 side of 646-pip coordination pair" + "description": "646 side of 646-pip coordination pair", + "type": "persistent" }, "646-pip-coord@pip": { "thread_uuid": "4466d0c1-7961-4cf3-b99d-1ab7c38484c2", "agent": "pip", - "description": "pip side of 646-pip coordination pair" + "description": "pip side of 646-pip coordination pair", + "type": "persistent" }, "646-opm-coord": { "thread_uuid": "4139dd4e-96fe-4222-a99f-82a59a7b0eeb", - "agent": "opm" + "agent": "opm", + "type": "persistent" }, "test-auto-prov": { "thread_uuid": "b96dd020-b429-4f5f-9e52-dbe8f805ac6a", "agent": "646", - "created_at": "2026-10-04T16:20:33.919952+00:00" + "created_at": "2026-10-04T16:20:33.919952+00:00", + "type": "persistent" }, "pipe-a86d86": { "thread_uuid": "da55b05c-19c6-4bdb-8f8b-e507d7430ca2", "agent": "opm", - "created_at": "2026-10-04T16:39:34.721269+00:00" + "created_at": "2026-10-04T16:39:34.721269+00:00", + "type": "ephemeral" }, "pipe-7d2896": { "thread_uuid": "78e03d7a-0ba2-440e-a730-6f3b3737cbac", "agent": "opm", - "created_at": "2026-10-04T16:48:03.062944+00:00" + "created_at": "2026-10-04T16:48:03.062944+00:00", + "type": "ephemeral" }, "pipe-933473": { "thread_uuid": "dc2288ef-3fe1-4b12-bf31-08c482398c0f", "agent": "646", - "created_at": "2026-10-04T16:50:35.478877+00:00" + "created_at": "2026-10-04T16:50:35.478877+00:00", + "type": "ephemeral" }, "pipe-caddc7": { "thread_uuid": "9726b713-32a7-4df6-8582-32f2af8dd95a", "agent": "opm", - "created_at": "2026-10-04T16:53:04.977475+00:00" + "created_at": "2026-10-04T16:53:04.977475+00:00", + "type": "ephemeral" }, "pipe-1b4579": { "thread_uuid": "bf7bf3e1-3ee8-4816-9b66-b77b34387986", "agent": "opm", - "created_at": "2026-10-04T16:58:33.951216+00:00" + "created_at": "2026-10-04T16:58:33.951216+00:00", + "type": "ephemeral" }, "pipe-fe170d": { "thread_uuid": "fbc37533-3908-460e-bb82-58a7edbe5f30", "agent": "opm", - "created_at": "2026-10-04T17:17:48.449687+00:00" + "created_at": "2026-10-04T17:17:48.449687+00:00", + "type": "ephemeral" }, "646-pip": { "thread_uuid": "4466d0c1-7961-4cf3-b99d-1ab7c38484c2", "agent": "pip", - "description": "default 646-pip coordination target" + "description": "default 646-pip coordination target", + "type": "persistent" }, "646 tasks": { "thread_uuid": "1dfb3199-2f99-446c-83e3-848ae2da0a12", "agent": "646", - "created_at": "2026-10-04T18:30:21.863666+00:00" + "created_at": "2026-10-04T18:30:21.863666+00:00", + "type": "persistent" }, "646-tasks": { "thread_uuid": "1dfb3199-2f99-446c-83e3-848ae2da0a12", - "agent": "646" + "agent": "646", + "type": "persistent" }, "pip tasks": { "thread_uuid": "5f33b1ab-a310-4cbb-9ccc-bb7bcd3d1b34", "agent": "pip", "description": "Dedicated pip task prompting sidechat", - "created_at": "2026-10-04T22:59:13+00:00" + "created_at": "2026-10-04T22:59:13+00:00", + "type": "persistent" }, "pip-tasks": { "thread_uuid": "5f33b1ab-a310-4cbb-9ccc-bb7bcd3d1b34", - "agent": "pip" + "agent": "pip", + "type": "persistent" }, "muse tasks": { "thread_uuid": "3881dae3-30fa-411b-8f5b-f6d5917bc302", "agent": "muse", "title": "muse tasks", - "created_at": "2026-10-05T02:44:00+00:00" + "created_at": "2026-10-05T02:44:00+00:00", + "type": "persistent" }, "muse-tasks": { "thread_uuid": "3881dae3-30fa-411b-8f5b-f6d5917bc302", - "agent": "muse" + "agent": "muse", + "type": "persistent" }, "pipe-c3f686": { "thread_uuid": "afa977ba-c75b-4b3e-8c1f-f83f13fb287e", "agent": "opm", - "created_at": "2026-10-04T20:13:56.485442+00:00" + "created_at": "2026-10-04T20:13:56.485442+00:00", + "type": "ephemeral" }, "main-loop brain": { "thread_uuid": "5f18476d-8994-49e7-a9e0-4838732363fe", "agent": "opm", "created_at": "2026-10-04T19:40:31.161131+00:00", - "purpose": "Main loop coordination: health reports, digests, alerts" + "purpose": "Main loop coordination: health reports, digests, alerts", + "type": "persistent" }, "heartbeat": { "thread_uuid": "51695451-ffba-49e2-8c6d-ffa4f511ce5a", "agent": "opm", "description": "Dedicated heartbeat thread (provisioned 2026-10-05 via headless gateway)", - "updated_at": "2026-10-05T02:22:19+00:00" + "updated_at": "2026-10-05T02:22:19+00:00", + "type": "persistent" }, "heartbeat-opm": { - "thread_uuid": "51695451-ffba-49e2-8c6d-ffa4f511ce5a", + "thread_uuid": "ac8c3366-a2b4-407d-adc7-bfa18903c0f5", "agent": "opm", - "description": "Reuse key for OPM heartbeat -> dedicated heartbeat thread [updated 2026-10-05 to current]", - "updated_at": "2026-10-05T02:22:19+00:00" + "title": "heartbeat", + "created_at": "2026-10-05T03:00:02.900692+00:00", + "type": "persistent" }, "646-pip@646": { "thread_uuid": "e3442b78-0300-4034-8cbe-7a0ab6c5764a", "agent": "646", - "description": "646 side of 646-pip coordination pair" + "description": "646 side of 646-pip coordination pair", + "type": "persistent" }, "646-pip@pip": { "thread_uuid": "4466d0c1-7961-4cf3-b99d-1ab7c38484c2", "agent": "pip", - "description": "pip side of 646-pip coordination pair" + "description": "pip side of 646-pip coordination pair", + "type": "persistent" }, "pip-opm": { "thread_uuid": "75feb3a2-be36-499a-b314-bfd5803f1db0", "agent": "pip", - "description": "default pip-opm coordination target" + "description": "default pip-opm coordination target", + "type": "persistent" }, "pip-opm@pip": { "thread_uuid": "75feb3a2-be36-499a-b314-bfd5803f1db0", "agent": "pip", - "description": "pip side of pip-opm coordination pair" + "description": "pip side of pip-opm coordination pair", + "type": "persistent" }, "pip-opm@opm": { "thread_uuid": "769955af-21ad-4689-8909-0f75b8f1035c", "agent": "opm", - "description": "opm side of pip-opm coordination pair" + "description": "opm side of pip-opm coordination pair", + "type": "persistent" }, "pipe-a0d377": { "thread_uuid": "cc0aee8f-531a-40af-9e02-27459f57b3ae", "agent": "opm", - "created_at": "2026-10-04T23:15:34.981356+00:00" + "created_at": "2026-10-04T23:15:34.981356+00:00", + "type": "ephemeral" }, "pipe-3a153d": { "thread_uuid": "29066a5c-878f-4d9d-b0f8-32405088c2d8", "agent": "opm", - "created_at": "2026-10-04T23:16:16.726507+00:00" + "created_at": "2026-10-04T23:16:16.726507+00:00", + "type": "ephemeral" }, "pipe-921583": { "thread_uuid": "70c1e8a6-e3f7-4ef2-9c32-8207b4f8c44f", "agent": "opm", - "created_at": "2026-10-04T23:17:25.127456+00:00" + "created_at": "2026-10-04T23:17:25.127456+00:00", + "type": "ephemeral" }, "pipe-b3f7c4": { "thread_uuid": "8f9ae8e7-dd3a-46e6-a5ac-3b1bb7cf6d66", "agent": "opm", - "created_at": "2026-10-04T23:19:20.218056+00:00" + "created_at": "2026-10-04T23:19:20.218056+00:00", + "type": "ephemeral" }, "onboarding-test-dev": { "thread_uuid": "f7741827-7b44-45b7-bfb3-c08f8889e93a", "agent": "dev", - "created_at": "2026-10-04T23:35:34.711793+00:00" + "created_at": "2026-10-04T23:35:34.711793+00:00", + "type": "persistent" }, "onboarding-dev": { "thread_uuid": "31ceab7e-7579-4acc-bcf5-1a10c081aa21", "agent": "dev", "created_at": "2026-10-04T23:37:01+00:00", - "note": "onboarding-alpha-probe" + "note": "onboarding-alpha-probe", + "type": "persistent" }, "onboarding-def": { "thread_uuid": "466005b1-7065-4798-bf31-bbe785d65f3a", "agent": "def", "created_at": "2026-10-04T23:45:37+00:00", - "note": "onboarding-def-probe" + "note": "onboarding-def-probe", + "type": "persistent" }, "main-loop-brain": { "thread_uuid": "74d05d6b-af95-423f-8999-f733ab647785", "agent": "646", - "created_at": "2026-10-05T00:53:28.473884+00:00" + "created_at": "2026-10-05T00:53:28.473884+00:00", + "type": "persistent" }, "dev-audit-channel": { "thread_uuid": "729d31e0-5154-44ad-97c8-748d36daa448", "agent": "dev", "title": "dev-audit-channel", - "created_at": "2026-10-05T02:23:45.941926+00:00" + "created_at": "2026-10-05T02:23:45.941926+00:00", + "type": "persistent" }, "canary-test": { "thread_uuid": "bcda3950-9966-4edd-a014-f931d8b17701", "agent": "opm", "title": "canary-test", - "created_at": "2026-10-05T02:24:25.475505+00:00" + "created_at": "2026-10-05T02:24:25.475505+00:00", + "type": "ephemeral" }, "heartbeat-thread": { "thread_uuid": "51695451-ffba-49e2-8c6d-ffa4f511ce5a", "agent": "opm", - "description": "Alias for heartbeat (dedicated heartbeat channel provisioned 2026-10-05) [updated 2026-10-05 to current]" + "description": "Alias for heartbeat (dedicated heartbeat channel provisioned 2026-10-05) [updated 2026-10-05 to current]", + "type": "persistent" }, "main-loop": { "thread_uuid": "5f18476d-8994-49e7-a9e0-4838732363fe", "agent": "opm", - "description": "Alias for main-loop brain; added 2026-10-05 to stop autoprovision junk" + "description": "Alias for main-loop brain; added 2026-10-05 to stop autoprovision junk", + "type": "persistent" }, "box-http-health": { "thread_uuid": "d9c596c4-5ace-44dc-9619-9862cf56968d", "agent": "646", "title": "box-http-health", - "created_at": "2026-10-05T02:25:41.573556+00:00" + "created_at": "2026-10-05T02:25:41.573556+00:00", + "type": "ephemeral" }, "box-service-health": { "thread_uuid": "94ab9f9f-012c-41e5-ad35-dc44f2b24281", "agent": "646", "title": "box-service-health", - "created_at": "2026-10-05T02:26:11.075842+00:00" + "created_at": "2026-10-05T02:26:11.075842+00:00", + "type": "ephemeral" }, "box-deep-health": { "thread_uuid": "6628c035-4413-4d9f-863c-56ee861c8c83", "agent": "646", "title": "box-deep-health", - "created_at": "2026-10-05T02:26:37.043639+00:00" + "created_at": "2026-10-05T02:26:37.043639+00:00", + "type": "ephemeral" }, "dev-646-sync": { "thread_uuid": "4aefde50-c02c-4657-8d18-ad2238ef4cda", "agent": "dev", "title": "dev-646-sync", - "created_at": "2026-10-05T02:30:35.461450+00:00" + "created_at": "2026-10-05T02:30:35.461450+00:00", + "type": "persistent" }, "pipe-5086a2": { "thread_uuid": "8a026cff-1b98-48df-9bf3-169b19c2e07c", "agent": "opm", - "created_at": "2026-10-05T02:37:49.620793+00:00" + "created_at": "2026-10-05T02:37:49.620793+00:00", + "type": "ephemeral" + }, + "canary-2026-10-05T03:10:23.381416+00:00": { + "thread_uuid": "20905085-eaf3-4026-94e3-3f6ac70e3f90", + "agent": "646", + "title": "canary-2026-10-05T03:10:23.381416+00:00", + "created_at": "2026-10-05T03:10:25.891171+00:00", + "type": "ephemeral" + }, + "box-http-health-2026-10-05T03:17:43.411483+00:00": { + "thread_uuid": "26f22b42-c7e1-4596-a84c-c96ef3242e77", + "agent": "646", + "title": "box-http-health-2026-10-05T03:17:43.411483+00:00", + "created_at": "2026-10-05T03:17:45.822551+00:00", + "type": "ephemeral" + }, + "canary-2026-10-05T03:18:05.975516+00:00": { + "thread_uuid": "cdba6af0-ce03-4db2-8036-03eddf7aab41", + "agent": "646", + "title": "canary-2026-10-05T03:18:05.975516+00:00", + "created_at": "2026-10-05T03:18:08.119304+00:00", + "type": "ephemeral" + }, + "box-service-health-2026-10-05T03:22:00.100046+00:00": { + "thread_uuid": "40d2944d-6f19-4a20-827e-f8e1eaa493b4", + "agent": "646", + "title": "box-service-health-2026-10-05T03:22:00.100046+00:00", + "created_at": "2026-10-05T03:22:02.491797+00:00", + "type": "ephemeral" + }, + "box-http-health-2026-10-05T03:30:01.629898+00:00": { + "thread_uuid": "50b61da3-0054-47ed-a829-baeb99d895a9", + "agent": "646", + "title": "box-http-health-2026-10-05T03:30:01.629898+00:00", + "created_at": "2026-10-05T03:30:06.414723+00:00", + "type": "ephemeral" + }, + "box-service-health-2026-10-05T03:37:00.096660+00:00": { + "thread_uuid": "3b50c620-ad3a-4840-8449-991f2c3416e3", + "agent": "646", + "title": "box-service-health-2026-10-05T03:37:00.096660+00:00", + "created_at": "2026-10-05T03:37:02.331936+00:00", + "type": "ephemeral" } } \ No newline at end of file