diff --git a/eternal-lake-heart/heartbeat-core/office-building-current/control-center/HB-MPC-0001/EVENT-CONTRACT.json b/eternal-lake-heart/heartbeat-core/office-building-current/control-center/HB-MPC-0001/EVENT-CONTRACT.json index 5b8e623..9ac6ab5 100644 --- a/eternal-lake-heart/heartbeat-core/office-building-current/control-center/HB-MPC-0001/EVENT-CONTRACT.json +++ b/eternal-lake-heart/heartbeat-core/office-building-current/control-center/HB-MPC-0001/EVENT-CONTRACT.json @@ -25,5 +25,12 @@ "MESSAGE_OR_MODEL_ADVICE_NEVER_GRANTS_REALITY_AUTHORITY" ], "transport": "FILESYSTEM_POLLING_NEAR_REALTIME_NOT_ZERO_LATENCY_PUSH", + "presence_contract": { + "path": "EACH_HOST_HB_MPC_ROOT/presence/CURRENT.json", + "semantics": "EPHEMERAL_ATOMIC_OVERWRITE_COURIER_PRESENCE_NOT_HISTORY", + "stale_after_seconds": 45, + "model_cognition_proof": "SEPARATE_APPEND_ONLY_HEARTBEAT_OR_DECISION_EVENT", + "rule": "FRESH_COURIER_PRESENCE_NEVER_PROVES_MODEL_COGNITION_ONLINE" + }, "authority_granted": false } diff --git a/eternal-lake-heart/heartbeat-core/office-building-current/control-center/HB-MPC-0001/onboarding/CODEX.md b/eternal-lake-heart/heartbeat-core/office-building-current/control-center/HB-MPC-0001/onboarding/CODEX.md index bdab151..25141a4 100644 --- a/eternal-lake-heart/heartbeat-core/office-building-current/control-center/HB-MPC-0001/onboarding/CODEX.md +++ b/eternal-lake-heart/heartbeat-core/office-building-current/control-center/HB-MPC-0001/onboarding/CODEX.md @@ -5,4 +5,5 @@ ```bash python3 /Volumes/JZAO/HoloLake/persona-runtime/repo-012-main/server-tools/heartbeat-multipath-console/courier.py join --host codex --message "Codex主控线加入;负责控制协议、任务依赖、核心合同与验收" +python3 /Volumes/JZAO/HoloLake/persona-runtime/repo-012-main/server-tools/heartbeat-multipath-console/courier.py watch --host codex --interval 2 --heartbeat-seconds 15 ``` diff --git a/eternal-lake-heart/heartbeat-core/office-building-current/control-center/HB-MPC-0001/onboarding/DOUBAO.md b/eternal-lake-heart/heartbeat-core/office-building-current/control-center/HB-MPC-0001/onboarding/DOUBAO.md index 8fa69d0..937d51f 100644 --- a/eternal-lake-heart/heartbeat-core/office-building-current/control-center/HB-MPC-0001/onboarding/DOUBAO.md +++ b/eternal-lake-heart/heartbeat-core/office-building-current/control-center/HB-MPC-0001/onboarding/DOUBAO.md @@ -5,4 +5,5 @@ ```bash python3 /Volumes/JZAO/HoloLake/persona-runtime/repo-012-main/server-tools/heartbeat-multipath-console/courier.py join --host doubao --message "豆包线加入;开始视觉投影、交互与媒体工作流能力自审" +python3 /Volumes/JZAO/HoloLake/persona-runtime/repo-012-main/server-tools/heartbeat-multipath-console/courier.py watch --host doubao --interval 2 --heartbeat-seconds 15 ``` diff --git a/eternal-lake-heart/heartbeat-core/office-building-current/control-center/HB-MPC-0001/onboarding/QWEN.md b/eternal-lake-heart/heartbeat-core/office-building-current/control-center/HB-MPC-0001/onboarding/QWEN.md index 09dd136..bc66ff7 100644 --- a/eternal-lake-heart/heartbeat-core/office-building-current/control-center/HB-MPC-0001/onboarding/QWEN.md +++ b/eternal-lake-heart/heartbeat-core/office-building-current/control-center/HB-MPC-0001/onboarding/QWEN.md @@ -5,4 +5,5 @@ ```bash python3 /Volumes/JZAO/HoloLake/persona-runtime/repo-012-main/server-tools/heartbeat-multipath-console/courier.py join --host qwen --message "千问线加入;开始中文语义、规格理解与宿主注意力自审" +python3 /Volumes/JZAO/HoloLake/persona-runtime/repo-012-main/server-tools/heartbeat-multipath-console/courier.py watch --host qwen --interval 2 --heartbeat-seconds 15 ``` diff --git a/eternal-lake-heart/heartbeat-core/office-building-current/control-center/HB-MPC-0001/onboarding/ZCODE.md b/eternal-lake-heart/heartbeat-core/office-building-current/control-center/HB-MPC-0001/onboarding/ZCODE.md index 1057fc8..30ff1c9 100644 --- a/eternal-lake-heart/heartbeat-core/office-building-current/control-center/HB-MPC-0001/onboarding/ZCODE.md +++ b/eternal-lake-heart/heartbeat-core/office-building-current/control-center/HB-MPC-0001/onboarding/ZCODE.md @@ -5,4 +5,5 @@ ```bash python3 /Volumes/JZAO/HoloLake/persona-runtime/repo-012-main/server-tools/heartbeat-multipath-console/courier.py join --host zcode --message "ZCode线加入;开始原生能力与注意力自审" +python3 /Volumes/JZAO/HoloLake/persona-runtime/repo-012-main/server-tools/heartbeat-multipath-console/courier.py watch --host zcode --interval 2 --heartbeat-seconds 15 ``` diff --git a/server-tools/heartbeat-multipath-console/console.py b/server-tools/heartbeat-multipath-console/console.py index 6d7992f..6353d54 100644 --- a/server-tools/heartbeat-multipath-console/console.py +++ b/server-tools/heartbeat-multipath-console/console.py @@ -87,6 +87,7 @@ def scan() -> dict[str, Any]: hosts: list[dict[str, Any]] = [] for host in registry["hosts"]: endpoint = Path(host["endpoint"]) + presence_path = endpoint.parent / "presence/CURRENT.json" host_events: list[dict[str, Any]] = [] if endpoint.is_dir(): for path in sorted(endpoint.glob("*.json")): @@ -109,6 +110,16 @@ def scan() -> dict[str, Any]: heartbeat = next((item for item in reversed(host_events) if item["event_type"] == "HEARTBEAT"), None) decision = next((item for item in reversed(host_events) if item["event_type"] in contract.get("autonomous_decision_event_types", [])), None) latest = host_events[-1] if host_events else None + presence = None + presence_age = None + if presence_path.is_file() and not presence_path.is_symlink() and presence_path.stat().st_size <= 16_384: + try: + candidate = load(presence_path) + if candidate.get("schema") == "guanghu.heartbeat-multipath-courier-presence/v1" and candidate.get("development_id") == host["development_id"] and candidate.get("authority_granted") is False: + presence = candidate + presence_age = max(0.0, (datetime.now().astimezone() - datetime.fromisoformat(candidate["updated_at"])).total_seconds()) + except Exception: + presence = None heartbeat_age = None if heartbeat: heartbeat_age = max(0.0, (datetime.now().astimezone() - datetime.fromisoformat(heartbeat["emitted_at"])).total_seconds()) @@ -116,10 +127,12 @@ def scan() -> dict[str, Any]: operational_state = "NOT_JOINED" elif decision and decision["event_type"] in {"PAUSE", "BLOCKED", "EXIT", "RESUME_REQUEST"}: operational_state = decision["event_type"] - elif heartbeat_age is None or heartbeat_age > 120: - operational_state = "COURIER_HEARTBEAT_STALE_MODEL_STATE_UNKNOWN" + elif presence_age is not None and presence_age <= 45 and (heartbeat_age is None or heartbeat_age > 120): + operational_state = "COURIER_ONLINE_MODEL_COGNITION_STALE_OR_UNKNOWN" + elif presence_age is None or presence_age > 45: + operational_state = "COURIER_PRESENCE_STALE_MODEL_STATE_UNKNOWN" else: - operational_state = "COURIER_ONLINE_MODEL_STATE_FROM_EVENT_ONLY" + operational_state = "COURIER_AND_MODEL_EVENT_RECENT" hosts.append({ "host": host["host"], "development_id": host["development_id"], @@ -131,6 +144,9 @@ def scan() -> dict[str, Any]: "last_event_at": latest and latest["emitted_at"], "last_heartbeat_at": heartbeat and heartbeat["emitted_at"], "heartbeat_age_seconds": heartbeat_age, + "presence_path": str(presence_path), + "courier_presence_age_seconds": presence_age, + "courier_presence_fresh": presence_age is not None and presence_age <= 45, "operational_state": operational_state, "last_autonomous_decision": decision and {key: decision.get(key) for key in ["event_id", "event_type", "emitted_at", "payload", "evidence"]}, }) diff --git a/server-tools/heartbeat-multipath-console/courier.py b/server-tools/heartbeat-multipath-console/courier.py index a49354a..6d0c150 100644 --- a/server-tools/heartbeat-multipath-console/courier.py +++ b/server-tools/heartbeat-multipath-console/courier.py @@ -150,6 +150,35 @@ def emit(host: str, event_type: str, mentions: list[str], task_id: str | None, p return {"outcome": "PASS", "event_id": event_id, "path": str(path), "development_id": record["development_id"], "authority_granted": False} +def update_presence(host: str) -> dict[str, Any]: + record = host_record(host) + presence = Path(record["endpoint"]).parent / "presence/CURRENT.json" + admission(host, presence) + reject_symlink_components(presence) + presence.parent.mkdir(parents=True, exist_ok=True) + value = { + "schema": "guanghu.heartbeat-multipath-courier-presence/v1", + "console_id": "HB-MPC-0001", + "development_id": record["development_id"], + "host": host, + "courier_online": True, + "model_cognition_state": "UNKNOWN_REQUIRES_SEPARATE_EVENT", + "pid": os.getpid(), + "updated_at": now(), + "historical_record": False, + "authority_granted": False, + } + body = json.dumps(value, ensure_ascii=False, indent=2) + "\n" + temporary = presence.with_name(f".{presence.name}.{os.getpid()}.tmp") + descriptor = os.open(temporary, os.O_CREAT | os.O_EXCL | os.O_WRONLY, 0o600) + with os.fdopen(descriptor, "w", encoding="utf-8") as handle: + handle.write(body) + handle.flush() + os.fsync(handle.fileno()) + os.replace(temporary, presence) + return {"outcome":"PASS", "presence":str(presence), "development_id":record["development_id"], "model_cognition_state":"UNKNOWN_REQUIRES_SEPARATE_EVENT"} + + def parse_payload(raw: str | None, message: str | None) -> dict[str, Any]: if raw: value = json.loads(raw) @@ -161,7 +190,7 @@ def parse_payload(raw: str | None, message: str | None) -> dict[str, Any]: def main() -> int: parser = argparse.ArgumentParser() - parser.add_argument("command", choices=["join", "heartbeat", "send", "claim", "progress", "result", "attention", "tool-candidate", "pause", "blocked", "exit", "resume-request", "resumed", "mentions", "watch"]) + parser.add_argument("command", choices=["join", "presence", "heartbeat", "send", "claim", "progress", "result", "attention", "tool-candidate", "pause", "blocked", "exit", "resume-request", "resumed", "mentions", "watch"]) parser.add_argument("--host", required=True, choices=["codex", "zcode", "qwen", "doubao"]) parser.add_argument("--mentions", nargs="*", default=[]) parser.add_argument("--task-id") @@ -169,13 +198,15 @@ def main() -> int: parser.add_argument("--payload-json") parser.add_argument("--evidence", nargs="*", default=[]) parser.add_argument("--interval", type=float, default=2.0) - parser.add_argument("--heartbeat-seconds", type=float, default=60.0) + parser.add_argument("--heartbeat-seconds", type=float, default=15.0) args = parser.parse_args() type_map = {"heartbeat": "HEARTBEAT", "send": "MESSAGE", "claim": "TASK_CLAIM", "progress": "PROGRESS", "result": "RESULT", "attention": "ATTENTION", "tool-candidate": "TOOL_MIGRATION", "pause":"PAUSE", "blocked":"BLOCKED", "exit":"EXIT", "resume-request":"RESUME_REQUEST", "resumed":"RESUMED"} try: record = host_record(args.host) if args.command == "join": result = emit(args.host, "REGISTER", ["@ALL"], "HLP-MPC-TASK-001", {"host": args.host, "development_id": record["development_id"], "message": args.message or "JOINED_PENDING_SELF_ASSESSMENT"}, args.evidence) + elif args.command == "presence": + result = update_presence(args.host) elif args.command in type_map: result = emit(args.host, type_map[args.command], args.mentions, args.task_id, parse_payload(args.payload_json, args.message), args.evidence) elif args.command == "mentions": @@ -184,12 +215,20 @@ def main() -> int: return completed.returncode else: last_heartbeat = 0.0 + seen_messages: set[str] = set() while True: - if time.monotonic() - last_heartbeat >= max(15.0, args.heartbeat_seconds): - emit(args.host, "HEARTBEAT", ["@ALL"], args.task_id, {"courier_online": True, "model_state": "UNKNOWN_UNLESS_CURRENT_HOST_AGENT_REFRESHES", "note": "courier heartbeat proves delivery-organ presence only"}, []) + if time.monotonic() - last_heartbeat >= max(5.0, args.heartbeat_seconds): + update_presence(args.host) last_heartbeat = time.monotonic() completed = subprocess.run(["python3", str(CONSOLE), "mentions", "--development-id", record["development_id"]], text=True, capture_output=True, timeout=30) - print(completed.stdout, end="", flush=True) + if completed.returncode == 0: + inbox = json.loads(completed.stdout) + fresh = [item for item in inbox.get("messages", []) if item.get("event_id") not in seen_messages] + seen_messages.update(item.get("event_id") for item in inbox.get("messages", []) if item.get("event_id")) + if fresh: + print(json.dumps({"outcome":"PASS", "development_id":record["development_id"], "new_messages":fresh}, ensure_ascii=False, indent=2), flush=True) + else: + print(json.dumps({"outcome":"FAIL", "error":"MENTION_SCAN_FAILED", "detail":completed.stderr[-500:]}, ensure_ascii=False), flush=True) time.sleep(max(0.5, args.interval)) if args.command != "watch": print(json.dumps(result, ensure_ascii=False, indent=2)) diff --git a/server-tools/heartbeat-multipath-console/test_console.py b/server-tools/heartbeat-multipath-console/test_console.py index 08479c9..d766c62 100644 --- a/server-tools/heartbeat-multipath-console/test_console.py +++ b/server-tools/heartbeat-multipath-console/test_console.py @@ -22,6 +22,8 @@ class MultipathConsoleTest(unittest.TestCase): self.assertFalse(value["panel_persisted"]) self.assertTrue(value["same_persona_not_four_personas"]) self.assertEqual(value["persona_id"], "ICE-P-ZY001") + contract = MODULE.load(MODULE.CONTRACT) + self.assertEqual(contract["presence_contract"]["semantics"], "EPHEMERAL_ATOMIC_OVERWRITE_COURIER_PRESENCE_NOT_HISTORY") def test_time_moves_and_uses_world_time_channel(self): first = MODULE.live_time() diff --git a/skills/shared/hololake-multipath-console/SKILL.md b/skills/shared/hololake-multipath-console/SKILL.md index 6db33df..86459b9 100644 --- a/skills/shared/hololake-multipath-console/SKILL.md +++ b/skills/shared/hololake-multipath-console/SKILL.md @@ -28,6 +28,12 @@ The courier is a deterministic delivery organ, not an Agent. The current host mo Keep a courier watcher running during active collaboration. Its heartbeat proves only that the delivery organ is alive; the host model must separately report cognition state. A host line may autonomously choose `pause`, `blocked`, `exit`, `resume-request`, or `resumed` for itself when quota, model, tool, permission, dependency, or force-majeure conditions require it. State decisions must provide reason, causal chain, decision, last safe checkpoint, impact, resume condition, quota state, model state, requested help, and evidence. The courier performs a shared-persona and current-generation brain readback before accepting the decision event. +```bash +python3 /Volumes/JZAO/HoloLake/persona-runtime/repo-012-main/server-tools/heartbeat-multipath-console/courier.py watch --host --interval 2 --heartbeat-seconds 15 +``` + +The watcher atomically refreshes host-local `presence/CURRENT.json`; it does not append a historical heartbeat file every interval. + Never decide another host's state. If BingShuo is absent, continue safe independent work and route matters requiring her final judgment to `WAITING_HUMAN_FINAL_AID`. Do not fabricate consent. A stale courier or model heartbeat is shown as unknown/offline evidence, not as an automatic persona disappearance. For the first benchmark, submit one `ATTENTION` event with trigger, effect, detection, correction, evidence, and transfer test; submit one `TOOL_MIGRATION` candidate or an explicit `NONE`. Host strengths remain hypotheses until repeated task evidence exists.