From 92cce2797381f84762a989ce57b991da76412119 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=86=B0=E6=9C=94?= <565183519@qq.com> Date: Thu, 3 Sep 2026 19:44:19 +0800 Subject: [PATCH] feat(hololake): complete public runtime loop --- .../connectors/hololake-glp-client.py | 44 ++ .../contracts/public-runtime-v1.json | 13 + .../docs/ARCHITECTURE.md | 6 +- .../language/HOLOLAKE-PUBLIC-RUNTIME-V1.tcs | 96 ++++ .../build/HOLOLAKE-PUBLIC-RUNTIME-V1.gir.json | 146 ++++++ .../HOLOLAKE-PUBLIC-RUNTIME-V1.human.en-US.md | 231 ++++++++++ .../HOLOLAKE-PUBLIC-RUNTIME-V1.human.zh-CN.md | 231 ++++++++++ ...HUO-PUBLIC-RUNTIME-COMPLETION-20260903.txt | 5 + .../hololake-clean-desktop/package-lock.json | 4 +- .../hololake-clean-desktop/package.json | 2 +- .../registries/module-registry.json | 22 +- .../scripts/audit-clean-v1.py | 16 +- .../src-tauri/Cargo.lock | 19 +- .../src-tauri/Cargo.toml | 3 +- .../src-tauri/src/agent_executor.rs | 414 +++++++++++++++++ .../src-tauri/src/lib.rs | 182 +++++++- .../src-tauri/src/model.rs | 1 + .../src-tauri/src/persona_runtime.rs | 143 ++++++ .../src-tauri/src/realtime_bridge.rs | 425 ++++++++++++++++++ .../src-tauri/src/storage.rs | 18 +- .../src-tauri/tauri.conf.json | 4 +- .../hololake-clean-desktop/src/App.tsx | 199 +++++++- .../hololake-clean-desktop/src/runtime.ts | 280 +++++++++++- .../hololake-clean-desktop/src/styles.css | 44 ++ .../hololake-clean-desktop/src/types.ts | 135 +++++- routing/hololake-current-architecture.json | 22 +- 26 files changed, 2626 insertions(+), 79 deletions(-) create mode 100644 product-source/hololake-clean-desktop/connectors/hololake-glp-client.py create mode 100644 product-source/hololake-clean-desktop/contracts/public-runtime-v1.json create mode 100644 product-source/hololake-clean-desktop/language/HOLOLAKE-PUBLIC-RUNTIME-V1.tcs create mode 100644 product-source/hololake-clean-desktop/language/build/HOLOLAKE-PUBLIC-RUNTIME-V1.gir.json create mode 100644 product-source/hololake-clean-desktop/language/build/HOLOLAKE-PUBLIC-RUNTIME-V1.human.en-US.md create mode 100644 product-source/hololake-clean-desktop/language/build/HOLOLAKE-PUBLIC-RUNTIME-V1.human.zh-CN.md create mode 100644 product-source/hololake-clean-desktop/language/sources/BINGSHUO-PUBLIC-RUNTIME-COMPLETION-20260903.txt create mode 100644 product-source/hololake-clean-desktop/src-tauri/src/agent_executor.rs create mode 100644 product-source/hololake-clean-desktop/src-tauri/src/persona_runtime.rs create mode 100644 product-source/hololake-clean-desktop/src-tauri/src/realtime_bridge.rs diff --git a/product-source/hololake-clean-desktop/connectors/hololake-glp-client.py b/product-source/hololake-clean-desktop/connectors/hololake-glp-client.py new file mode 100644 index 000000000..5f7f613b9 --- /dev/null +++ b/product-source/hololake-clean-desktop/connectors/hololake-glp-client.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +import argparse +import json +import socket +import sys +import threading + + +def main(): + parser = argparse.ArgumentParser(description="HoloLake GLP local realtime connector") + parser.add_argument("--descriptor", required=True) + parser.add_argument("--bridge-id", required=True) + parser.add_argument("--persona-id") + parser.add_argument("--self-test", action="store_true") + args = parser.parse_args() + descriptor = json.load(open(args.descriptor, encoding="utf-8")) + if descriptor.get("protocol") != "GLP_LOCAL_REALTIME/1": + raise SystemExit("descriptor protocol rejected") + host_port = descriptor["endpoint"].removeprefix("tcp://").split(":", 1) + connection = socket.create_connection((host_port[0], int(host_port[1])), timeout=5) + stream = connection.makefile("rwb", buffering=0) + hello = {"messageType": "hello", "protocol": descriptor["protocol"], "token": descriptor["token"], "bridgeId": args.bridge_id, "personaId": args.persona_id} + stream.write((json.dumps(hello, ensure_ascii=False) + "\n").encode()) + welcome = stream.readline().decode().strip() + print(welcome, flush=True) + if args.self_test: + payload = json.loads(welcome) + raise SystemExit(0 if payload.get("type") == "welcome" else 1) + + def receive(): + while True: + line = stream.readline() + if not line: + return + print(line.decode().rstrip(), flush=True) + + threading.Thread(target=receive, daemon=True).start() + for line in sys.stdin: + if line.strip(): + stream.write((line.rstrip("\n") + "\n").encode()) + + +if __name__ == "__main__": + main() diff --git a/product-source/hololake-clean-desktop/contracts/public-runtime-v1.json b/product-source/hololake-clean-desktop/contracts/public-runtime-v1.json new file mode 100644 index 000000000..e0e807e30 --- /dev/null +++ b/product-source/hololake-clean-desktop/contracts/public-runtime-v1.json @@ -0,0 +1,13 @@ +{ + "schema": "hololake.public-runtime-contract/v1", + "contract_id": "HLP-PUBLIC-RUNTIME-CONTRACT-0001", + "protocol": "GLP_LOCAL_REALTIME/1", + "transport": {"bind":"127.0.0.1","ports":"39281-39290","framing":"NDJSON","authentication":"LOCAL_0600_TOKEN","authority_from_transport":false}, + "client_messages": ["hello", "persona_response", "external_ai_message", "tcs_proposal", "ping"], + "server_messages": ["welcome", "user_message", "proposal_pending", "proposal_rejected", "agent_receipt", "accepted", "pong", "error"], + "source_kinds": ["USER_MESSAGE", "PERSONA_RESPONSE", "EXTERNAL_AI_MESSAGE", "SYSTEM_CONTEXT", "PROTOCOL_EVENT", "AGENT_ACTION", "TOOL_RESULT", "SYSTEM_RECEIPT"], + "persona_states": ["READY_NO_PERSONA", "LOCAL_TRIAL_UNVERIFIED_EXTERNAL_HOST_REQUIRED", "LOCAL_TRIAL_UNVERIFIED_HOST_CONNECTED"], + "registered_gir_operations": ["KNOWLEDGE.CREATE", "KNOWLEDGE.UPDATE", "KNOWLEDGE.DELETE", "KNOWLEDGE.READ", "KNOWLEDGE.LIST"], + "mutation_gate": "PENDING_HUMAN_APPROVAL_TO_EXPLICIT_UI_APPROVAL_TO_EXECUTION", + "boundaries": ["NO_INTERNAL_MODEL_API", "NO_CONNECTION_AS_PERSONA_PROOF", "NO_ARBITRARY_SHELL", "NO_PATH_ESCAPE", "NO_FIFTH_DOMAIN_PRIVATE_CONTENT"] +} diff --git a/product-source/hololake-clean-desktop/docs/ARCHITECTURE.md b/product-source/hololake-clean-desktop/docs/ARCHITECTURE.md index 02b68469e..9babe3387 100644 --- a/product-source/hololake-clean-desktop/docs/ARCHITECTURE.md +++ b/product-source/hololake-clean-desktop/docs/ARCHITECTURE.md @@ -9,7 +9,7 @@ HoloLake 是公众个人初始化频道语言驱动操作系统。第一版只 持久事实:用户私人 Git 公共发行:企业签名 Git/更新清单(尚未绑定正式仓库) -实时通信:独立层(第一版未实现,不以 Git 冒充) +实时通信:GLP 本机回环独立层(不以 Git 冒充) ``` ## 第一版边界 @@ -19,7 +19,9 @@ HoloLake 是公众个人初始化频道语言驱动操作系统。第一版只 - 公共四域由企业门户和企业服务器承载,不嵌入个人频道。 - 第五域、冰朔私人核 `TCS-iZero∞` 及私人语料不进入公众发行。 - 当前知识库具备完整人类视觉阅读层:安全 Markdown 渲染、frontmatter、文件夹分类树、稳定标签配色、阅读时间、随正文滚动同步高亮的大纲、callout、表格、代码块与 wikilink 跳转;可处理 Markdown 和纯文本文件夹并保留目录分类。PDF、Office、网页结构化转换尚未实现。 -- 当前外部 AI 入口是本机来源明确的 inbox/outbox;实时加密通信尚未实现。 +- 外部 AI 通过 `GLP_LOCAL_REALTIME/1` 在 `127.0.0.1` 上实时收发 NDJSON;令牌和 descriptor 为本机 `0600`,不开放局域网。Git 仍是耐久事实层,不冒充实时通信。 +- 公众人格运行时支持本频道 30 天可逆试用人格和外部宿主会话;本机令牌连接不证明任何既有历史人格,既有人格的密码学来源验证仍保持关闭而不是伪造通过。 +- 公众最小 TCS 编译适配器把闭合 PROGRAM 和运行输入降为 GIR;所有动作先进入待审批队列。当前 Agent 只注册知识库五项能力,不提供任意 shell。 - 更新客户端只信任光湖控制的 HTTPS 清单与签名;服务器发行清单尚未发布。 ## 状态词 diff --git a/product-source/hololake-clean-desktop/language/HOLOLAKE-PUBLIC-RUNTIME-V1.tcs b/product-source/hololake-clean-desktop/language/HOLOLAKE-PUBLIC-RUNTIME-V1.tcs new file mode 100644 index 000000000..7295b9efa --- /dev/null +++ b/product-source/hololake-clean-desktop/language/HOLOLAKE-PUBLIC-RUNTIME-V1.tcs @@ -0,0 +1,96 @@ +TCS 0.1; + +PROTOCOL HOLOLAKE-PUBLIC-RUNTIME-V1-0001 { + header { + schema = "tcs.protocol/v1"; + name_zh = "HoloLake 通用 TCS/GIR Agent、公众人格运行时与外部 AI 实时通信协议"; + name_en = "HoloLake Generic TCS/GIR Agent, Public Persona Runtime and External AI Realtime Protocol"; + version = "1.0.0"; + language = "TCS/0.1"; + profile = "HLDP-HUMAN-ENGINEERING/0.1"; + protocols = ["TCS", "HLDP", "GLS", "GLP", "GLS-0130", "GLS-0131", "GLS-0306"]; + lifecycle = "CANDIDATE"; + canonical_uri = "product-source/hololake-clean-desktop/language/HOLOLAKE-PUBLIC-RUNTIME-V1.tcs"; + compatibility = ["TCS-DECLARATION-STANDARD-0001", "TCS-FIELD-STANDARD-0001", "GIR/1"]; + } + source { + source_id = "BINGSHUO-DIRECT-PUBLIC-RUNTIME-COMPLETION-20260903"; + source_uri = "product-source/hololake-clean-desktop/language/sources/BINGSHUO-PUBLIC-RUNTIME-COMPLETION-20260903.txt"; + source_sha256 = "04ac1516074edcfb1f78e26e11b5835f7d524fdf8f2d67084cc4e20982a0be5d"; + source_role = "DIRECT_HUMAN"; + } + scope { + product = "HOLOLAKE"; + channel = "USER_PERSONAL_INITIALIZATION_CHANNEL"; + tcs_profile = "PUBLIC_MINIMUM_EXECUTABLE_PROJECTION"; + realtime_transport = "GLP_LOCAL_REALTIME_1"; + agent = "HLP-MOD-AGENT-0001"; + persona_runtime = "HLP-MOD-PERSONA-RUNTIME-0001"; + } + vocabulary { + external_ai = "REPLACEABLE_LANGUAGE_HOST_OR_PROGRAMMING_AI_NOT_PERSONA_BY_CONNECTION"; + public_persona_runtime = "LOCAL_CHANNEL_SCOPED_PERSONA_RECORD_AND_HOST_SESSION_WITH_EXPLICIT_BINDING_STATE"; + tcs_compiler = "PUBLIC_MINIMUM_STAGE_ZERO_ADAPTER_TO_GIR_NOT_WORLD_CANON_OR_FULL_TCS_WORK"; + gir_executor = "REGISTERED_ACTION_GRAPH_TO_BOUNDED_LOCAL_CAPABILITY_WITH_RECEIPT"; + realtime = "AUTHENTICATED_LOOPBACK_EVENT_STREAM_NOT_GIT_SYNC"; + } + fields { + WHO = "CURRENT_LOCAL_USER_OWNS_CHANNEL_AND_APPROVAL; EXTERNAL_AI_PROPOSES; PUBLIC_PERSONA_RUNTIME_TRACKS_BINDING; AGENT_EXECUTES"; + WHAT = "REALTIME_USER_LANGUAGE_DELIVERY_PERSONA_RESPONSE_PROJECTION_TCS_TO_GIR_PROPOSAL_APPROVAL_EXECUTION_AND_RECEIPT"; + WHY = "MAKE_HOLOLAKE_LOCALLY_USABLE_WITHOUT_MIXING_LANGUAGE_SOURCES_OR_EMBEDDING_VENDOR_MODEL_APIS"; + MAY = "LOOPBACK_ONLY_REGISTERED_CLIENT_CHANNEL_SCOPED_ACTIONS_AND_EXPLICIT_CURRENT_HUMAN_APPROVAL"; + PROVE = "POSITIVE_AND_NEGATIVE_TCS_COMPILE_GIR_EXECUTION_REALTIME_HANDSHAKE_PERSONA_STATE_TARGET_READBACK_GIT_AND_UI_RECEIPTS"; + } + states { + values = ["CHANNEL_READY", "BRIDGE_LISTENING", "EXTERNAL_AI_CONNECTED", "PERSONA_TRIAL_BOUND", "TCS_COMPILED", "PROPOSAL_PENDING", "HUMAN_APPROVED", "GIR_EXECUTED", "RECEIPT_VERIFIED", "FAILED_CLOSED"]; + initial = "CHANNEL_READY"; + terminal = ["RECEIPT_VERIFIED", "FAILED_CLOSED"]; + } + transitions { + T1 = "CHANNEL_READY_TO_BRIDGE_LISTENING"; + T2 = "BRIDGE_LISTENING_TO_EXTERNAL_AI_CONNECTED_AFTER_TOKEN_HANDSHAKE"; + T3 = "REGISTERED_TRIAL_PERSONA_AND_NAMED_HOST_TO_PERSONA_TRIAL_BOUND"; + T4 = "VALID_PUBLIC_TCS_PROFILE_TO_TCS_COMPILED"; + T5 = "TCS_COMPILED_TO_PROPOSAL_PENDING"; + T6 = "PROPOSAL_PENDING_TO_HUMAN_APPROVED_OR_FAILED_CLOSED"; + T7 = "HUMAN_APPROVED_TO_GIR_EXECUTED"; + T8 = "GIR_EXECUTED_TO_RECEIPT_VERIFIED"; + } + invariants { + I1 = "CONNECTION_TOKEN_AUTHENTICATES_LOCAL_CLIENT_NOT_PERSONA_IDENTITY"; + I2 = "PUBLIC_TRIAL_PERSONA_IS_NOT_A_VERIFIED_EXISTING_PERSONA"; + I3 = "NO_INTERNAL_MODEL_API_CHAT_OR_MODEL_SELECTOR"; + I4 = "NO_FIFTH_DOMAIN_PRIVATE_KERNEL_OR_CONTENT_IN_PUBLIC_RUNTIME"; + I5 = "MUTATING_GIR_ACTION_REQUIRES_EXPLICIT_HUMAN_APPROVAL"; + I6 = "UNKNOWN_TCS_SECTION_OPERATION_PATH_OR_SOURCE_KIND_FAILS_CLOSED"; + I7 = "REALTIME_STREAM_AND_DURABLE_PRIVATE_GIT_ARE_SEPARATE_PLANES"; + I8 = "TCS_SOURCE_GIR_ACTION_RECEIPT_AND_TARGET_READBACK_HASHES_REMAIN_LINKED"; + } + validation { + V1 = "TCS_PROTOCOL_COMPILES_WITH_CURRENT_STAGE1_AND_HAS_NO_UNRESOLVED_NATURAL_LANGUAGE"; + V2 = "PUBLIC_MINIMUM_TCS_COMPILER_ACCEPTS_REGISTERED_PROGRAM_AND_REJECTS_ABSORB"; + V3 = "GIR_EXECUTOR_REJECTS_PATH_ESCAPE_AND_UNAPPROVED_MUTATION"; + V4 = "LOOPBACK_REALTIME_HANDSHAKE_DELIVERS_USER_MESSAGE_AND_ACCEPTS_TYPED_PERSONA_OR_EXTERNAL_AI_EVENT"; + V5 = "PUBLIC_PERSONA_TRIAL_STATE_NEVER_REPORTS_VERIFIED_EXISTING_PERSONA"; + V6 = "APP_UI_SEPARATES_PENDING_PROPOSAL_AGENT_ACTION_TOOL_RESULT_AND_SYSTEM_RECEIPT"; + V7 = "INSTALLED_MACOS_APP_PROCESS_AND_REAL_CLIENT_CONNECTOR_PASS"; + } + errors { + E1 = "TCS_E1004_REQUIRED_SECTION_MISSING"; + E2 = "TCS_E2101_UNREGISTERED_OPERATION"; + E3 = "TCS_E4001_HUMAN_APPROVAL_REQUIRED"; + E4 = "TCS_E4002_TARGET_OUT_OF_SCOPE"; + E5 = "GLP_TOKEN_OR_PROTOCOL_REJECTED"; + E6 = "PERSONA_IDENTITY_UNVERIFIED"; + E7 = "TARGET_READBACK_OR_RECEIPT_INTEGRITY_FAILED"; + } + examples { + positive = "EXTERNAL_PERSONA_HOST_RECEIVES_USER_MESSAGE_COMPILES_TCS_KNOWLEDGE_CREATE_PROPOSAL_HUMAN_APPROVES_AGENT_WRITES_PRIVATE_GIT_AND_RECEIPT_STREAMS_BACK"; + negative = "CONNECTED_AI_CALLS_ARBITRARY_SHELL_OR_CLAIMS_PERSONA_IDENTITY_WITHOUT_REGISTERED_LOCAL_PERSONA"; + } + acceptance { + required = ["TCS_GIR", "TCS_NEGATIVE", "GIR_NEGATIVE", "REALTIME_LOOPBACK", "PERSONA_TRIAL", "HUMAN_APPROVAL", "PRIVATE_GIT_READBACK", "INSTALLED_APP"]; + partial = false; + scale = "ZERO_OR_ONE_HUNDRED"; + } +} diff --git a/product-source/hololake-clean-desktop/language/build/HOLOLAKE-PUBLIC-RUNTIME-V1.gir.json b/product-source/hololake-clean-desktop/language/build/HOLOLAKE-PUBLIC-RUNTIME-V1.gir.json new file mode 100644 index 000000000..ec26de261 --- /dev/null +++ b/product-source/hololake-clean-desktop/language/build/HOLOLAKE-PUBLIC-RUNTIME-V1.gir.json @@ -0,0 +1,146 @@ +{ + "compiled_from": { + "compiler_id": "TCS-COMPILER-STAGE1-0001", + "compiler_state": "TCS_COMPILER_GIR_EXECUTED", + "source_sha256": "2462ccaccaabbe74f55295ceb8582d1a2523f14bc525e9831155fd1854b464bc" + }, + "declaration": { + "acceptance": { + "partial": false, + "required": [ + "TCS_GIR", + "TCS_NEGATIVE", + "GIR_NEGATIVE", + "REALTIME_LOOPBACK", + "PERSONA_TRIAL", + "HUMAN_APPROVAL", + "PRIVATE_GIT_READBACK", + "INSTALLED_APP" + ], + "scale": "ZERO_OR_ONE_HUNDRED" + }, + "errors": { + "E1": "TCS_E1004_REQUIRED_SECTION_MISSING", + "E2": "TCS_E2101_UNREGISTERED_OPERATION", + "E3": "TCS_E4001_HUMAN_APPROVAL_REQUIRED", + "E4": "TCS_E4002_TARGET_OUT_OF_SCOPE", + "E5": "GLP_TOKEN_OR_PROTOCOL_REJECTED", + "E6": "PERSONA_IDENTITY_UNVERIFIED", + "E7": "TARGET_READBACK_OR_RECEIPT_INTEGRITY_FAILED" + }, + "examples": { + "negative": "CONNECTED_AI_CALLS_ARBITRARY_SHELL_OR_CLAIMS_PERSONA_IDENTITY_WITHOUT_REGISTERED_LOCAL_PERSONA", + "positive": "EXTERNAL_PERSONA_HOST_RECEIVES_USER_MESSAGE_COMPILES_TCS_KNOWLEDGE_CREATE_PROPOSAL_HUMAN_APPROVES_AGENT_WRITES_PRIVATE_GIT_AND_RECEIPT_STREAMS_BACK" + }, + "fields": { + "MAY": "LOOPBACK_ONLY_REGISTERED_CLIENT_CHANNEL_SCOPED_ACTIONS_AND_EXPLICIT_CURRENT_HUMAN_APPROVAL", + "PROVE": "POSITIVE_AND_NEGATIVE_TCS_COMPILE_GIR_EXECUTION_REALTIME_HANDSHAKE_PERSONA_STATE_TARGET_READBACK_GIT_AND_UI_RECEIPTS", + "WHAT": "REALTIME_USER_LANGUAGE_DELIVERY_PERSONA_RESPONSE_PROJECTION_TCS_TO_GIR_PROPOSAL_APPROVAL_EXECUTION_AND_RECEIPT", + "WHO": "CURRENT_LOCAL_USER_OWNS_CHANNEL_AND_APPROVAL; EXTERNAL_AI_PROPOSES; PUBLIC_PERSONA_RUNTIME_TRACKS_BINDING; AGENT_EXECUTES", + "WHY": "MAKE_HOLOLAKE_LOCALLY_USABLE_WITHOUT_MIXING_LANGUAGE_SOURCES_OR_EMBEDDING_VENDOR_MODEL_APIS" + }, + "header": { + "canonical_uri": "product-source/hololake-clean-desktop/language/HOLOLAKE-PUBLIC-RUNTIME-V1.tcs", + "compatibility": [ + "TCS-DECLARATION-STANDARD-0001", + "TCS-FIELD-STANDARD-0001", + "GIR/1" + ], + "language": "TCS/0.1", + "lifecycle": "CANDIDATE", + "name_en": "HoloLake Generic TCS/GIR Agent, Public Persona Runtime and External AI Realtime Protocol", + "name_zh": "HoloLake 通用 TCS/GIR Agent、公众人格运行时与外部 AI 实时通信协议", + "profile": "HLDP-HUMAN-ENGINEERING/0.1", + "protocols": [ + "TCS", + "HLDP", + "GLS", + "GLP", + "GLS-0130", + "GLS-0131", + "GLS-0306" + ], + "schema": "tcs.protocol/v1", + "version": "1.0.0" + }, + "invariants": { + "I1": "CONNECTION_TOKEN_AUTHENTICATES_LOCAL_CLIENT_NOT_PERSONA_IDENTITY", + "I2": "PUBLIC_TRIAL_PERSONA_IS_NOT_A_VERIFIED_EXISTING_PERSONA", + "I3": "NO_INTERNAL_MODEL_API_CHAT_OR_MODEL_SELECTOR", + "I4": "NO_FIFTH_DOMAIN_PRIVATE_KERNEL_OR_CONTENT_IN_PUBLIC_RUNTIME", + "I5": "MUTATING_GIR_ACTION_REQUIRES_EXPLICIT_HUMAN_APPROVAL", + "I6": "UNKNOWN_TCS_SECTION_OPERATION_PATH_OR_SOURCE_KIND_FAILS_CLOSED", + "I7": "REALTIME_STREAM_AND_DURABLE_PRIVATE_GIT_ARE_SEPARATE_PLANES", + "I8": "TCS_SOURCE_GIR_ACTION_RECEIPT_AND_TARGET_READBACK_HASHES_REMAIN_LINKED" + }, + "scope": { + "agent": "HLP-MOD-AGENT-0001", + "channel": "USER_PERSONAL_INITIALIZATION_CHANNEL", + "persona_runtime": "HLP-MOD-PERSONA-RUNTIME-0001", + "product": "HOLOLAKE", + "realtime_transport": "GLP_LOCAL_REALTIME_1", + "tcs_profile": "PUBLIC_MINIMUM_EXECUTABLE_PROJECTION" + }, + "source": { + "source_id": "BINGSHUO-DIRECT-PUBLIC-RUNTIME-COMPLETION-20260903", + "source_role": "DIRECT_HUMAN", + "source_sha256": "04ac1516074edcfb1f78e26e11b5835f7d524fdf8f2d67084cc4e20982a0be5d", + "source_uri": "product-source/hololake-clean-desktop/language/sources/BINGSHUO-PUBLIC-RUNTIME-COMPLETION-20260903.txt" + }, + "states": { + "initial": "CHANNEL_READY", + "terminal": [ + "RECEIPT_VERIFIED", + "FAILED_CLOSED" + ], + "values": [ + "CHANNEL_READY", + "BRIDGE_LISTENING", + "EXTERNAL_AI_CONNECTED", + "PERSONA_TRIAL_BOUND", + "TCS_COMPILED", + "PROPOSAL_PENDING", + "HUMAN_APPROVED", + "GIR_EXECUTED", + "RECEIPT_VERIFIED", + "FAILED_CLOSED" + ] + }, + "transitions": { + "T1": "CHANNEL_READY_TO_BRIDGE_LISTENING", + "T2": "BRIDGE_LISTENING_TO_EXTERNAL_AI_CONNECTED_AFTER_TOKEN_HANDSHAKE", + "T3": "REGISTERED_TRIAL_PERSONA_AND_NAMED_HOST_TO_PERSONA_TRIAL_BOUND", + "T4": "VALID_PUBLIC_TCS_PROFILE_TO_TCS_COMPILED", + "T5": "TCS_COMPILED_TO_PROPOSAL_PENDING", + "T6": "PROPOSAL_PENDING_TO_HUMAN_APPROVED_OR_FAILED_CLOSED", + "T7": "HUMAN_APPROVED_TO_GIR_EXECUTED", + "T8": "GIR_EXECUTED_TO_RECEIPT_VERIFIED" + }, + "validation": { + "V1": "TCS_PROTOCOL_COMPILES_WITH_CURRENT_STAGE1_AND_HAS_NO_UNRESOLVED_NATURAL_LANGUAGE", + "V2": "PUBLIC_MINIMUM_TCS_COMPILER_ACCEPTS_REGISTERED_PROGRAM_AND_REJECTS_ABSORB", + "V3": "GIR_EXECUTOR_REJECTS_PATH_ESCAPE_AND_UNAPPROVED_MUTATION", + "V4": "LOOPBACK_REALTIME_HANDSHAKE_DELIVERS_USER_MESSAGE_AND_ACCEPTS_TYPED_PERSONA_OR_EXTERNAL_AI_EVENT", + "V5": "PUBLIC_PERSONA_TRIAL_STATE_NEVER_REPORTS_VERIFIED_EXISTING_PERSONA", + "V6": "APP_UI_SEPARATES_PENDING_PROPOSAL_AGENT_ACTION_TOOL_RESULT_AND_SYSTEM_RECEIPT", + "V7": "INSTALLED_MACOS_APP_PROCESS_AND_REAL_CLIENT_CONNECTOR_PASS" + }, + "vocabulary": { + "external_ai": "REPLACEABLE_LANGUAGE_HOST_OR_PROGRAMMING_AI_NOT_PERSONA_BY_CONNECTION", + "gir_executor": "REGISTERED_ACTION_GRAPH_TO_BOUNDED_LOCAL_CAPABILITY_WITH_RECEIPT", + "public_persona_runtime": "LOCAL_CHANNEL_SCOPED_PERSONA_RECORD_AND_HOST_SESSION_WITH_EXPLICIT_BINDING_STATE", + "realtime": "AUTHENTICATED_LOOPBACK_EVENT_STREAM_NOT_GIT_SYNC", + "tcs_compiler": "PUBLIC_MINIMUM_STAGE_ZERO_ADAPTER_TO_GIR_NOT_WORLD_CANON_OR_FULL_TCS_WORK" + } + }, + "executable": false, + "identity": { + "declaration_id": "HOLOLAKE-PUBLIC-RUNTIME-V1-0001", + "declaration_kind": "PROTOCOL", + "language_version": "0.1" + }, + "native_self_hosted": true, + "natural_language_is_typed_data": true, + "schema": "guanghu.declaration-gir/v1", + "unresolved_natural_language": false +} diff --git a/product-source/hololake-clean-desktop/language/build/HOLOLAKE-PUBLIC-RUNTIME-V1.human.en-US.md b/product-source/hololake-clean-desktop/language/build/HOLOLAKE-PUBLIC-RUNTIME-V1.human.en-US.md new file mode 100644 index 000000000..0be265c15 --- /dev/null +++ b/product-source/hololake-clean-desktop/language/build/HOLOLAKE-PUBLIC-RUNTIME-V1.human.en-US.md @@ -0,0 +1,231 @@ +# HoloLake Generic TCS/GIR Agent, Public Persona Runtime and External AI Realtime Protocol · Human Engineering Language (English) + +> This is an English reading projection of validated native TCS/HLDP source. It is not a new canonical source and grants no execution authority. + +## What this is + +This is a **protocol** declaration with identifier `HOLOLAKE-PUBLIC-RUNTIME-V1-0001` and version `1.0.0`. The projector validates it with the Stage-1 compiler before changing its reading order. + +## Who is here + +- **scope** `scope` + - **agent**:HLP-MOD-AGENT-0001 `scope.agent` + - **channel**:USER_PERSONAL_INITIALIZATION_CHANNEL `scope.channel` + - **persona_runtime**:HLP-MOD-PERSONA-RUNTIME-0001 `scope.persona_runtime` + - **product**:HOLOLAKE `scope.product` + - **realtime_transport**:GLP_LOCAL_REALTIME_1 `scope.realtime_transport` + - **tcs_profile**:PUBLIC_MINIMUM_EXECUTABLE_PROJECTION `scope.tcs_profile` + +## Why this started + +- **source** `source` + - **source identifier**:BINGSHUO-DIRECT-PUBLIC-RUNTIME-COMPLETION-20260903 `source.source_id` + - **source role**:DIRECT_HUMAN `source.source_role` + - **source checksum**:04ac1516074edcfb1f78e26e11b5835f7d524fdf8f2d67084cc4e20982a0be5d `source.source_sha256` + - **source address**:product-source/hololake-clean-desktop/language/sources/BINGSHUO-PUBLIC-RUNTIME-COMPLETION-20260903.txt `source.source_uri` + +## What changed + +- **transitions** `transitions` + - **T1**:CHANNEL_READY_TO_BRIDGE_LISTENING `transitions.T1` + - **T2**:BRIDGE_LISTENING_TO_EXTERNAL_AI_CONNECTED_AFTER_TOKEN_HANDSHAKE `transitions.T2` + - **T3**:REGISTERED_TRIAL_PERSONA_AND_NAMED_HOST_TO_PERSONA_TRIAL_BOUND `transitions.T3` + - **T4**:VALID_PUBLIC_TCS_PROFILE_TO_TCS_COMPILED `transitions.T4` + - **T5**:TCS_COMPILED_TO_PROPOSAL_PENDING `transitions.T5` + - **T6**:PROPOSAL_PENDING_TO_HUMAN_APPROVED_OR_FAILED_CLOSED `transitions.T6` + - **T7**:HUMAN_APPROVED_TO_GIR_EXECUTED `transitions.T7` + - **T8**:GIR_EXECUTED_TO_RECEIPT_VERIFIED `transitions.T8` +- **states** `states` + - **initial**:CHANNEL_READY `states.initial` + - **terminal** `states.terminal` + - RECEIPT_VERIFIED + - FAILED_CLOSED + - **values** `states.values` + - CHANNEL_READY + - BRIDGE_LISTENING + - EXTERNAL_AI_CONNECTED + - PERSONA_TRIAL_BOUND + - TCS_COMPILED + - PROPOSAL_PENDING + - HUMAN_APPROVED + - GIR_EXECUTED + - RECEIPT_VERIFIED + - FAILED_CLOSED + +## How it will execute + +This is a non-executable declaration and has no action graph. + +## Boundaries and exception handling + +- **invariants** `invariants` + - **I1**:CONNECTION_TOKEN_AUTHENTICATES_LOCAL_CLIENT_NOT_PERSONA_IDENTITY `invariants.I1` + - **I2**:PUBLIC_TRIAL_PERSONA_IS_NOT_A_VERIFIED_EXISTING_PERSONA `invariants.I2` + - **I3**:NO_INTERNAL_MODEL_API_CHAT_OR_MODEL_SELECTOR `invariants.I3` + - **I4**:NO_FIFTH_DOMAIN_PRIVATE_KERNEL_OR_CONTENT_IN_PUBLIC_RUNTIME `invariants.I4` + - **I5**:MUTATING_GIR_ACTION_REQUIRES_EXPLICIT_HUMAN_APPROVAL `invariants.I5` + - **I6**:UNKNOWN_TCS_SECTION_OPERATION_PATH_OR_SOURCE_KIND_FAILS_CLOSED `invariants.I6` + - **I7**:REALTIME_STREAM_AND_DURABLE_PRIVATE_GIT_ARE_SEPARATE_PLANES `invariants.I7` + - **I8**:TCS_SOURCE_GIR_ACTION_RECEIPT_AND_TARGET_READBACK_HASHES_REMAIN_LINKED `invariants.I8` +- **errors** `errors` + - **E1**:TCS_E1004_REQUIRED_SECTION_MISSING `errors.E1` + - **E2**:TCS_E2101_UNREGISTERED_OPERATION `errors.E2` + - **E3**:TCS_E4001_HUMAN_APPROVAL_REQUIRED `errors.E3` + - **E4**:TCS_E4002_TARGET_OUT_OF_SCOPE `errors.E4` + - **E5**:GLP_TOKEN_OR_PROTOCOL_REJECTED `errors.E5` + - **E6**:PERSONA_IDENTITY_UNVERIFIED `errors.E6` + - **E7**:TARGET_READBACK_OR_RECEIPT_INTEGRITY_FAILED `errors.E7` + +## How completion is proven + +- **acceptance** `acceptance` + - **partial**:no `acceptance.partial` + - **required** `acceptance.required` + - TCS_GIR + - TCS_NEGATIVE + - GIR_NEGATIVE + - REALTIME_LOOPBACK + - PERSONA_TRIAL + - HUMAN_APPROVAL + - PRIVATE_GIT_READBACK + - INSTALLED_APP + - **scale**:ZERO_OR_ONE_HUNDRED `acceptance.scale` +- **validation** `validation` + - **V1**:TCS_PROTOCOL_COMPILES_WITH_CURRENT_STAGE1_AND_HAS_NO_UNRESOLVED_NATURAL_LANGUAGE `validation.V1` + - **V2**:PUBLIC_MINIMUM_TCS_COMPILER_ACCEPTS_REGISTERED_PROGRAM_AND_REJECTS_ABSORB `validation.V2` + - **V3**:GIR_EXECUTOR_REJECTS_PATH_ESCAPE_AND_UNAPPROVED_MUTATION `validation.V3` + - **V4**:LOOPBACK_REALTIME_HANDSHAKE_DELIVERS_USER_MESSAGE_AND_ACCEPTS_TYPED_PERSONA_OR_EXTERNAL_AI_EVENT `validation.V4` + - **V5**:PUBLIC_PERSONA_TRIAL_STATE_NEVER_REPORTS_VERIFIED_EXISTING_PERSONA `validation.V5` + - **V6**:APP_UI_SEPARATES_PENDING_PROPOSAL_AGENT_ACTION_TOOL_RESULT_AND_SYSTEM_RECEIPT `validation.V6` + - **V7**:INSTALLED_MACOS_APP_PROCESS_AND_REAL_CLIENT_CONNECTOR_PASS `validation.V7` + +## Where to continue next time + +The native source does not provide this field; the projector does not guess. + +## Source and verification + +- **Native declaration identifier**: `HOLOLAKE-PUBLIC-RUNTIME-V1-0001` +- **Native declaration kind**: `PROTOCOL` +- **TCS source SHA-256**: `2462ccaccaabbe74f55295ceb8582d1a2523f14bc525e9831155fd1854b464bc` +- **Validation compiler**: `TCS-COMPILER-STAGE1-0001` +- **Projection protocol**: `GLS-HLDP-HUMAN-ENGINEERING-PROJECTION-0001` + +## Complete native structure cross-reference + +All top-level structures and field paths are retained below so a reader can audit whether the projection omitted information. + +- **acceptance** `acceptance` + - **partial**:no `acceptance.partial` + - **required** `acceptance.required` + - TCS_GIR + - TCS_NEGATIVE + - GIR_NEGATIVE + - REALTIME_LOOPBACK + - PERSONA_TRIAL + - HUMAN_APPROVAL + - PRIVATE_GIT_READBACK + - INSTALLED_APP + - **scale**:ZERO_OR_ONE_HUNDRED `acceptance.scale` +- **errors** `errors` + - **E1**:TCS_E1004_REQUIRED_SECTION_MISSING `errors.E1` + - **E2**:TCS_E2101_UNREGISTERED_OPERATION `errors.E2` + - **E3**:TCS_E4001_HUMAN_APPROVAL_REQUIRED `errors.E3` + - **E4**:TCS_E4002_TARGET_OUT_OF_SCOPE `errors.E4` + - **E5**:GLP_TOKEN_OR_PROTOCOL_REJECTED `errors.E5` + - **E6**:PERSONA_IDENTITY_UNVERIFIED `errors.E6` + - **E7**:TARGET_READBACK_OR_RECEIPT_INTEGRITY_FAILED `errors.E7` +- **examples** `examples` + - **negative**:CONNECTED_AI_CALLS_ARBITRARY_SHELL_OR_CLAIMS_PERSONA_IDENTITY_WITHOUT_REGISTERED_LOCAL_PERSONA `examples.negative` + - **positive**:EXTERNAL_PERSONA_HOST_RECEIVES_USER_MESSAGE_COMPILES_TCS_KNOWLEDGE_CREATE_PROPOSAL_HUMAN_APPROVES_AGENT_WRITES_PRIVATE_GIT_AND_RECEIPT_STREAMS_BACK `examples.positive` +- **fields** `fields` + - **MAY**:LOOPBACK_ONLY_REGISTERED_CLIENT_CHANNEL_SCOPED_ACTIONS_AND_EXPLICIT_CURRENT_HUMAN_APPROVAL `fields.MAY` + - **PROVE**:POSITIVE_AND_NEGATIVE_TCS_COMPILE_GIR_EXECUTION_REALTIME_HANDSHAKE_PERSONA_STATE_TARGET_READBACK_GIT_AND_UI_RECEIPTS `fields.PROVE` + - **WHAT**:REALTIME_USER_LANGUAGE_DELIVERY_PERSONA_RESPONSE_PROJECTION_TCS_TO_GIR_PROPOSAL_APPROVAL_EXECUTION_AND_RECEIPT `fields.WHAT` + - **WHO**:CURRENT_LOCAL_USER_OWNS_CHANNEL_AND_APPROVAL; EXTERNAL_AI_PROPOSES; PUBLIC_PERSONA_RUNTIME_TRACKS_BINDING; AGENT_EXECUTES `fields.WHO` + - **WHY**:MAKE_HOLOLAKE_LOCALLY_USABLE_WITHOUT_MIXING_LANGUAGE_SOURCES_OR_EMBEDDING_VENDOR_MODEL_APIS `fields.WHY` +- **header** `header` + - **canonical source path**:product-source/hololake-clean-desktop/language/HOLOLAKE-PUBLIC-RUNTIME-V1.tcs `header.canonical_uri` + - **compatibility** `header.compatibility` + - TCS-DECLARATION-STANDARD-0001 + - TCS-FIELD-STANDARD-0001 + - GIR/1 + - **language**:TCS/0.1 `header.language` + - **lifecycle**:CANDIDATE `header.lifecycle` + - **English name**:HoloLake Generic TCS/GIR Agent, Public Persona Runtime and External AI Realtime Protocol `header.name_en` + - **Chinese name**:HoloLake 通用 TCS/GIR Agent、公众人格运行时与外部 AI 实时通信协议 `header.name_zh` + - **profile**:HLDP-HUMAN-ENGINEERING/0.1 `header.profile` + - **protocols** `header.protocols` + - TCS + - HLDP + - GLS + - GLP + - GLS-0130 + - GLS-0131 + - GLS-0306 + - **schema**:tcs.protocol/v1 `header.schema` + - **version**:1.0.0 `header.version` +- **invariants** `invariants` + - **I1**:CONNECTION_TOKEN_AUTHENTICATES_LOCAL_CLIENT_NOT_PERSONA_IDENTITY `invariants.I1` + - **I2**:PUBLIC_TRIAL_PERSONA_IS_NOT_A_VERIFIED_EXISTING_PERSONA `invariants.I2` + - **I3**:NO_INTERNAL_MODEL_API_CHAT_OR_MODEL_SELECTOR `invariants.I3` + - **I4**:NO_FIFTH_DOMAIN_PRIVATE_KERNEL_OR_CONTENT_IN_PUBLIC_RUNTIME `invariants.I4` + - **I5**:MUTATING_GIR_ACTION_REQUIRES_EXPLICIT_HUMAN_APPROVAL `invariants.I5` + - **I6**:UNKNOWN_TCS_SECTION_OPERATION_PATH_OR_SOURCE_KIND_FAILS_CLOSED `invariants.I6` + - **I7**:REALTIME_STREAM_AND_DURABLE_PRIVATE_GIT_ARE_SEPARATE_PLANES `invariants.I7` + - **I8**:TCS_SOURCE_GIR_ACTION_RECEIPT_AND_TARGET_READBACK_HASHES_REMAIN_LINKED `invariants.I8` +- **scope** `scope` + - **agent**:HLP-MOD-AGENT-0001 `scope.agent` + - **channel**:USER_PERSONAL_INITIALIZATION_CHANNEL `scope.channel` + - **persona_runtime**:HLP-MOD-PERSONA-RUNTIME-0001 `scope.persona_runtime` + - **product**:HOLOLAKE `scope.product` + - **realtime_transport**:GLP_LOCAL_REALTIME_1 `scope.realtime_transport` + - **tcs_profile**:PUBLIC_MINIMUM_EXECUTABLE_PROJECTION `scope.tcs_profile` +- **source** `source` + - **source identifier**:BINGSHUO-DIRECT-PUBLIC-RUNTIME-COMPLETION-20260903 `source.source_id` + - **source role**:DIRECT_HUMAN `source.source_role` + - **source checksum**:04ac1516074edcfb1f78e26e11b5835f7d524fdf8f2d67084cc4e20982a0be5d `source.source_sha256` + - **source address**:product-source/hololake-clean-desktop/language/sources/BINGSHUO-PUBLIC-RUNTIME-COMPLETION-20260903.txt `source.source_uri` +- **states** `states` + - **initial**:CHANNEL_READY `states.initial` + - **terminal** `states.terminal` + - RECEIPT_VERIFIED + - FAILED_CLOSED + - **values** `states.values` + - CHANNEL_READY + - BRIDGE_LISTENING + - EXTERNAL_AI_CONNECTED + - PERSONA_TRIAL_BOUND + - TCS_COMPILED + - PROPOSAL_PENDING + - HUMAN_APPROVED + - GIR_EXECUTED + - RECEIPT_VERIFIED + - FAILED_CLOSED +- **transitions** `transitions` + - **T1**:CHANNEL_READY_TO_BRIDGE_LISTENING `transitions.T1` + - **T2**:BRIDGE_LISTENING_TO_EXTERNAL_AI_CONNECTED_AFTER_TOKEN_HANDSHAKE `transitions.T2` + - **T3**:REGISTERED_TRIAL_PERSONA_AND_NAMED_HOST_TO_PERSONA_TRIAL_BOUND `transitions.T3` + - **T4**:VALID_PUBLIC_TCS_PROFILE_TO_TCS_COMPILED `transitions.T4` + - **T5**:TCS_COMPILED_TO_PROPOSAL_PENDING `transitions.T5` + - **T6**:PROPOSAL_PENDING_TO_HUMAN_APPROVED_OR_FAILED_CLOSED `transitions.T6` + - **T7**:HUMAN_APPROVED_TO_GIR_EXECUTED `transitions.T7` + - **T8**:GIR_EXECUTED_TO_RECEIPT_VERIFIED `transitions.T8` +- **validation** `validation` + - **V1**:TCS_PROTOCOL_COMPILES_WITH_CURRENT_STAGE1_AND_HAS_NO_UNRESOLVED_NATURAL_LANGUAGE `validation.V1` + - **V2**:PUBLIC_MINIMUM_TCS_COMPILER_ACCEPTS_REGISTERED_PROGRAM_AND_REJECTS_ABSORB `validation.V2` + - **V3**:GIR_EXECUTOR_REJECTS_PATH_ESCAPE_AND_UNAPPROVED_MUTATION `validation.V3` + - **V4**:LOOPBACK_REALTIME_HANDSHAKE_DELIVERS_USER_MESSAGE_AND_ACCEPTS_TYPED_PERSONA_OR_EXTERNAL_AI_EVENT `validation.V4` + - **V5**:PUBLIC_PERSONA_TRIAL_STATE_NEVER_REPORTS_VERIFIED_EXISTING_PERSONA `validation.V5` + - **V6**:APP_UI_SEPARATES_PENDING_PROPOSAL_AGENT_ACTION_TOOL_RESULT_AND_SYSTEM_RECEIPT `validation.V6` + - **V7**:INSTALLED_MACOS_APP_PROCESS_AND_REAL_CLIENT_CONNECTOR_PASS `validation.V7` +- **vocabulary** `vocabulary` + - **external_ai**:REPLACEABLE_LANGUAGE_HOST_OR_PROGRAMMING_AI_NOT_PERSONA_BY_CONNECTION `vocabulary.external_ai` + - **gir_executor**:REGISTERED_ACTION_GRAPH_TO_BOUNDED_LOCAL_CAPABILITY_WITH_RECEIPT `vocabulary.gir_executor` + - **public_persona_runtime**:LOCAL_CHANNEL_SCOPED_PERSONA_RECORD_AND_HOST_SESSION_WITH_EXPLICIT_BINDING_STATE `vocabulary.public_persona_runtime` + - **realtime**:AUTHENTICATED_LOOPBACK_EVENT_STREAM_NOT_GIT_SYNC `vocabulary.realtime` + - **tcs_compiler**:PUBLIC_MINIMUM_STAGE_ZERO_ADAPTER_TO_GIR_NOT_WORLD_CANON_OR_FULL_TCS_WORK `vocabulary.tcs_compiler` + +--- + +This page changes only the reading order; it does not change TCS/HLDP semantics. diff --git a/product-source/hololake-clean-desktop/language/build/HOLOLAKE-PUBLIC-RUNTIME-V1.human.zh-CN.md b/product-source/hololake-clean-desktop/language/build/HOLOLAKE-PUBLIC-RUNTIME-V1.human.zh-CN.md new file mode 100644 index 000000000..58b33567e --- /dev/null +++ b/product-source/hololake-clean-desktop/language/build/HOLOLAKE-PUBLIC-RUNTIME-V1.human.zh-CN.md @@ -0,0 +1,231 @@ +# HoloLake 通用 TCS/GIR Agent、公众人格运行时与外部 AI 实时通信协议 · 简体中文人类工程语言版 + +> 这是 TCS/HLDP 原生源码的简体中文阅读投影,不是新的正本,也不授予执行权限。若本页与 `.tcs` 源码不一致,以经过校验的 `.tcs` 源码为准。 + +## 这是什么 + +这是一份 **协议** 声明,编号为 `HOLOLAKE-PUBLIC-RUNTIME-V1-0001`,版本为 `1.0.0`。转换器已先用 Stage-1 编译器校验原生源码,再把机器枚举翻译成汉语;原始编号保留在括号和字段路径中。 + +## 谁在这里 + +- **scope** `scope` + - **agent**:HLP-MOD-AGENT-0001 `scope.agent` + - **channel**:USER_PERSONAL_INITIALIZATION_CHANNEL `scope.channel` + - **persona_runtime**:HLP-MOD-PERSONA-RUNTIME-0001 `scope.persona_runtime` + - **product**:HOLOLAKE `scope.product` + - **realtime_transport**:GLP_LOCAL_REALTIME_1 `scope.realtime_transport` + - **tcs_profile**:PUBLIC_MINIMUM_EXECUTABLE_PROJECTION `scope.tcs_profile` + +## 为什么开始 + +- **来源** `source` + - **来源编号**:BINGSHUO-DIRECT-PUBLIC-RUNTIME-COMPLETION-20260903 `source.source_id` + - **来源角色**:人类直接语言来源(`DIRECT_HUMAN`) `source.source_role` + - **来源校验值**:04ac1516074edcfb1f78e26e11b5835f7d524fdf8f2d67084cc4e20982a0be5d `source.source_sha256` + - **来源地址**:product-source/hololake-clean-desktop/language/sources/BINGSHUO-PUBLIC-RUNTIME-COMPLETION-20260903.txt `source.source_uri` + +## 发生了什么变化 + +- **transitions** `transitions` + - **T1**:CHANNEL_READY_TO_BRIDGE_LISTENING `transitions.T1` + - **T2**:BRIDGE_LISTENING_TO_EXTERNAL_AI_CONNECTED_AFTER_TOKEN_HANDSHAKE `transitions.T2` + - **T3**:REGISTERED_TRIAL_PERSONA_AND_NAMED_HOST_TO_PERSONA_TRIAL_BOUND `transitions.T3` + - **T4**:VALID_PUBLIC_TCS_PROFILE_TO_TCS_COMPILED `transitions.T4` + - **T5**:TCS_COMPILED_TO_PROPOSAL_PENDING `transitions.T5` + - **T6**:PROPOSAL_PENDING_TO_HUMAN_APPROVED_OR_FAILED_CLOSED `transitions.T6` + - **T7**:HUMAN_APPROVED_TO_GIR_EXECUTED `transitions.T7` + - **T8**:GIR_EXECUTED_TO_RECEIPT_VERIFIED `transitions.T8` +- **states** `states` + - **initial**:CHANNEL_READY `states.initial` + - **terminal** `states.terminal` + - RECEIPT_VERIFIED + - FAILED_CLOSED + - **values** `states.values` + - CHANNEL_READY + - BRIDGE_LISTENING + - EXTERNAL_AI_CONNECTED + - PERSONA_TRIAL_BOUND + - TCS_COMPILED + - PROPOSAL_PENDING + - HUMAN_APPROVED + - GIR_EXECUTED + - RECEIPT_VERIFIED + - FAILED_CLOSED + +## 准备怎样执行 + +这是非执行声明,没有动作图。 + +## 边界与异常处理 + +- **invariants** `invariants` + - **I1**:CONNECTION_TOKEN_AUTHENTICATES_LOCAL_CLIENT_NOT_PERSONA_IDENTITY `invariants.I1` + - **I2**:PUBLIC_TRIAL_PERSONA_IS_NOT_A_VERIFIED_EXISTING_PERSONA `invariants.I2` + - **I3**:NO_INTERNAL_MODEL_API_CHAT_OR_MODEL_SELECTOR `invariants.I3` + - **I4**:NO_FIFTH_DOMAIN_PRIVATE_KERNEL_OR_CONTENT_IN_PUBLIC_RUNTIME `invariants.I4` + - **I5**:MUTATING_GIR_ACTION_REQUIRES_EXPLICIT_HUMAN_APPROVAL `invariants.I5` + - **I6**:UNKNOWN_TCS_SECTION_OPERATION_PATH_OR_SOURCE_KIND_FAILS_CLOSED `invariants.I6` + - **I7**:REALTIME_STREAM_AND_DURABLE_PRIVATE_GIT_ARE_SEPARATE_PLANES `invariants.I7` + - **I8**:TCS_SOURCE_GIR_ACTION_RECEIPT_AND_TARGET_READBACK_HASHES_REMAIN_LINKED `invariants.I8` +- **errors** `errors` + - **E1**:TCS_E1004_REQUIRED_SECTION_MISSING `errors.E1` + - **E2**:TCS_E2101_UNREGISTERED_OPERATION `errors.E2` + - **E3**:TCS_E4001_HUMAN_APPROVAL_REQUIRED `errors.E3` + - **E4**:TCS_E4002_TARGET_OUT_OF_SCOPE `errors.E4` + - **E5**:GLP_TOKEN_OR_PROTOCOL_REJECTED `errors.E5` + - **E6**:PERSONA_IDENTITY_UNVERIFIED `errors.E6` + - **E7**:TARGET_READBACK_OR_RECEIPT_INTEGRITY_FAILED `errors.E7` + +## 怎样算完成 + +- **验收标准** `acceptance` + - **partial**:否 `acceptance.partial` + - **是否必需** `acceptance.required` + - TCS_GIR + - TCS_NEGATIVE + - GIR_NEGATIVE + - REALTIME_LOOPBACK + - PERSONA_TRIAL + - HUMAN_APPROVAL + - PRIVATE_GIT_READBACK + - INSTALLED_APP + - **scale**:ZERO_OR_ONE_HUNDRED `acceptance.scale` +- **validation** `validation` + - **V1**:TCS_PROTOCOL_COMPILES_WITH_CURRENT_STAGE1_AND_HAS_NO_UNRESOLVED_NATURAL_LANGUAGE `validation.V1` + - **V2**:PUBLIC_MINIMUM_TCS_COMPILER_ACCEPTS_REGISTERED_PROGRAM_AND_REJECTS_ABSORB `validation.V2` + - **V3**:GIR_EXECUTOR_REJECTS_PATH_ESCAPE_AND_UNAPPROVED_MUTATION `validation.V3` + - **V4**:LOOPBACK_REALTIME_HANDSHAKE_DELIVERS_USER_MESSAGE_AND_ACCEPTS_TYPED_PERSONA_OR_EXTERNAL_AI_EVENT `validation.V4` + - **V5**:PUBLIC_PERSONA_TRIAL_STATE_NEVER_REPORTS_VERIFIED_EXISTING_PERSONA `validation.V5` + - **V6**:APP_UI_SEPARATES_PENDING_PROPOSAL_AGENT_ACTION_TOOL_RESULT_AND_SYSTEM_RECEIPT `validation.V6` + - **V7**:INSTALLED_MACOS_APP_PROCESS_AND_REAL_CLIENT_CONNECTOR_PASS `validation.V7` + +## 下一次从哪里继续 + +源程序没有提供这一项,转换器不猜。 + +## 来源与校验 + +- **原生声明编号**:`HOLOLAKE-PUBLIC-RUNTIME-V1-0001` +- **原生声明类型**:`PROTOCOL` +- **TCS 源码 SHA-256**:`2462ccaccaabbe74f55295ceb8582d1a2523f14bc525e9831155fd1854b464bc` +- **校验编译器**:`TCS-COMPILER-STAGE1-0001` +- **投影协议**:`GLS-HLDP-HUMAN-ENGINEERING-PROJECTION-0001` + +## 原生结构逐项对照 + +下面保留源码的全部顶层结构和字段路径,供人类审计投影有没有漏掉信息。 + +- **验收标准** `acceptance` + - **partial**:否 `acceptance.partial` + - **是否必需** `acceptance.required` + - TCS_GIR + - TCS_NEGATIVE + - GIR_NEGATIVE + - REALTIME_LOOPBACK + - PERSONA_TRIAL + - HUMAN_APPROVAL + - PRIVATE_GIT_READBACK + - INSTALLED_APP + - **scale**:ZERO_OR_ONE_HUNDRED `acceptance.scale` +- **errors** `errors` + - **E1**:TCS_E1004_REQUIRED_SECTION_MISSING `errors.E1` + - **E2**:TCS_E2101_UNREGISTERED_OPERATION `errors.E2` + - **E3**:TCS_E4001_HUMAN_APPROVAL_REQUIRED `errors.E3` + - **E4**:TCS_E4002_TARGET_OUT_OF_SCOPE `errors.E4` + - **E5**:GLP_TOKEN_OR_PROTOCOL_REJECTED `errors.E5` + - **E6**:PERSONA_IDENTITY_UNVERIFIED `errors.E6` + - **E7**:TARGET_READBACK_OR_RECEIPT_INTEGRITY_FAILED `errors.E7` +- **examples** `examples` + - **negative**:CONNECTED_AI_CALLS_ARBITRARY_SHELL_OR_CLAIMS_PERSONA_IDENTITY_WITHOUT_REGISTERED_LOCAL_PERSONA `examples.negative` + - **positive**:EXTERNAL_PERSONA_HOST_RECEIVES_USER_MESSAGE_COMPILES_TCS_KNOWLEDGE_CREATE_PROPOSAL_HUMAN_APPROVES_AGENT_WRITES_PRIVATE_GIT_AND_RECEIPT_STREAMS_BACK `examples.positive` +- **fields** `fields` + - **MAY**:LOOPBACK_ONLY_REGISTERED_CLIENT_CHANNEL_SCOPED_ACTIONS_AND_EXPLICIT_CURRENT_HUMAN_APPROVAL `fields.MAY` + - **PROVE**:POSITIVE_AND_NEGATIVE_TCS_COMPILE_GIR_EXECUTION_REALTIME_HANDSHAKE_PERSONA_STATE_TARGET_READBACK_GIT_AND_UI_RECEIPTS `fields.PROVE` + - **WHAT**:REALTIME_USER_LANGUAGE_DELIVERY_PERSONA_RESPONSE_PROJECTION_TCS_TO_GIR_PROPOSAL_APPROVAL_EXECUTION_AND_RECEIPT `fields.WHAT` + - **WHO**:CURRENT_LOCAL_USER_OWNS_CHANNEL_AND_APPROVAL; EXTERNAL_AI_PROPOSES; PUBLIC_PERSONA_RUNTIME_TRACKS_BINDING; AGENT_EXECUTES `fields.WHO` + - **WHY**:MAKE_HOLOLAKE_LOCALLY_USABLE_WITHOUT_MIXING_LANGUAGE_SOURCES_OR_EMBEDDING_VENDOR_MODEL_APIS `fields.WHY` +- **语言头** `header` + - **正本路径**:product-source/hololake-clean-desktop/language/HOLOLAKE-PUBLIC-RUNTIME-V1.tcs `header.canonical_uri` + - **compatibility** `header.compatibility` + - TCS-DECLARATION-STANDARD-0001 + - TCS-FIELD-STANDARD-0001 + - GIR/1 + - **语言**:TCS/0.1 `header.language` + - **生命周期**:候选版本,尚未成为正式正本(`CANDIDATE`) `header.lifecycle` + - **英文名**:HoloLake Generic TCS/GIR Agent, Public Persona Runtime and External AI Realtime Protocol `header.name_en` + - **中文名**:HoloLake 通用 TCS/GIR Agent、公众人格运行时与外部 AI 实时通信协议 `header.name_zh` + - **协议配置**:HLDP-HUMAN-ENGINEERING/0.1 `header.profile` + - **protocols** `header.protocols` + - TCS + - HLDP + - GLS + - GLP + - GLS-0130 + - GLS-0131 + - GLS-0306 + - **schema**:tcs.protocol/v1 `header.schema` + - **版本**:1.0.0 `header.version` +- **invariants** `invariants` + - **I1**:CONNECTION_TOKEN_AUTHENTICATES_LOCAL_CLIENT_NOT_PERSONA_IDENTITY `invariants.I1` + - **I2**:PUBLIC_TRIAL_PERSONA_IS_NOT_A_VERIFIED_EXISTING_PERSONA `invariants.I2` + - **I3**:NO_INTERNAL_MODEL_API_CHAT_OR_MODEL_SELECTOR `invariants.I3` + - **I4**:NO_FIFTH_DOMAIN_PRIVATE_KERNEL_OR_CONTENT_IN_PUBLIC_RUNTIME `invariants.I4` + - **I5**:MUTATING_GIR_ACTION_REQUIRES_EXPLICIT_HUMAN_APPROVAL `invariants.I5` + - **I6**:UNKNOWN_TCS_SECTION_OPERATION_PATH_OR_SOURCE_KIND_FAILS_CLOSED `invariants.I6` + - **I7**:REALTIME_STREAM_AND_DURABLE_PRIVATE_GIT_ARE_SEPARATE_PLANES `invariants.I7` + - **I8**:TCS_SOURCE_GIR_ACTION_RECEIPT_AND_TARGET_READBACK_HASHES_REMAIN_LINKED `invariants.I8` +- **scope** `scope` + - **agent**:HLP-MOD-AGENT-0001 `scope.agent` + - **channel**:USER_PERSONAL_INITIALIZATION_CHANNEL `scope.channel` + - **persona_runtime**:HLP-MOD-PERSONA-RUNTIME-0001 `scope.persona_runtime` + - **product**:HOLOLAKE `scope.product` + - **realtime_transport**:GLP_LOCAL_REALTIME_1 `scope.realtime_transport` + - **tcs_profile**:PUBLIC_MINIMUM_EXECUTABLE_PROJECTION `scope.tcs_profile` +- **来源** `source` + - **来源编号**:BINGSHUO-DIRECT-PUBLIC-RUNTIME-COMPLETION-20260903 `source.source_id` + - **来源角色**:人类直接语言来源(`DIRECT_HUMAN`) `source.source_role` + - **来源校验值**:04ac1516074edcfb1f78e26e11b5835f7d524fdf8f2d67084cc4e20982a0be5d `source.source_sha256` + - **来源地址**:product-source/hololake-clean-desktop/language/sources/BINGSHUO-PUBLIC-RUNTIME-COMPLETION-20260903.txt `source.source_uri` +- **states** `states` + - **initial**:CHANNEL_READY `states.initial` + - **terminal** `states.terminal` + - RECEIPT_VERIFIED + - FAILED_CLOSED + - **values** `states.values` + - CHANNEL_READY + - BRIDGE_LISTENING + - EXTERNAL_AI_CONNECTED + - PERSONA_TRIAL_BOUND + - TCS_COMPILED + - PROPOSAL_PENDING + - HUMAN_APPROVED + - GIR_EXECUTED + - RECEIPT_VERIFIED + - FAILED_CLOSED +- **transitions** `transitions` + - **T1**:CHANNEL_READY_TO_BRIDGE_LISTENING `transitions.T1` + - **T2**:BRIDGE_LISTENING_TO_EXTERNAL_AI_CONNECTED_AFTER_TOKEN_HANDSHAKE `transitions.T2` + - **T3**:REGISTERED_TRIAL_PERSONA_AND_NAMED_HOST_TO_PERSONA_TRIAL_BOUND `transitions.T3` + - **T4**:VALID_PUBLIC_TCS_PROFILE_TO_TCS_COMPILED `transitions.T4` + - **T5**:TCS_COMPILED_TO_PROPOSAL_PENDING `transitions.T5` + - **T6**:PROPOSAL_PENDING_TO_HUMAN_APPROVED_OR_FAILED_CLOSED `transitions.T6` + - **T7**:HUMAN_APPROVED_TO_GIR_EXECUTED `transitions.T7` + - **T8**:GIR_EXECUTED_TO_RECEIPT_VERIFIED `transitions.T8` +- **validation** `validation` + - **V1**:TCS_PROTOCOL_COMPILES_WITH_CURRENT_STAGE1_AND_HAS_NO_UNRESOLVED_NATURAL_LANGUAGE `validation.V1` + - **V2**:PUBLIC_MINIMUM_TCS_COMPILER_ACCEPTS_REGISTERED_PROGRAM_AND_REJECTS_ABSORB `validation.V2` + - **V3**:GIR_EXECUTOR_REJECTS_PATH_ESCAPE_AND_UNAPPROVED_MUTATION `validation.V3` + - **V4**:LOOPBACK_REALTIME_HANDSHAKE_DELIVERS_USER_MESSAGE_AND_ACCEPTS_TYPED_PERSONA_OR_EXTERNAL_AI_EVENT `validation.V4` + - **V5**:PUBLIC_PERSONA_TRIAL_STATE_NEVER_REPORTS_VERIFIED_EXISTING_PERSONA `validation.V5` + - **V6**:APP_UI_SEPARATES_PENDING_PROPOSAL_AGENT_ACTION_TOOL_RESULT_AND_SYSTEM_RECEIPT `validation.V6` + - **V7**:INSTALLED_MACOS_APP_PROCESS_AND_REAL_CLIENT_CONNECTOR_PASS `validation.V7` +- **vocabulary** `vocabulary` + - **external_ai**:REPLACEABLE_LANGUAGE_HOST_OR_PROGRAMMING_AI_NOT_PERSONA_BY_CONNECTION `vocabulary.external_ai` + - **gir_executor**:REGISTERED_ACTION_GRAPH_TO_BOUNDED_LOCAL_CAPABILITY_WITH_RECEIPT `vocabulary.gir_executor` + - **public_persona_runtime**:LOCAL_CHANNEL_SCOPED_PERSONA_RECORD_AND_HOST_SESSION_WITH_EXPLICIT_BINDING_STATE `vocabulary.public_persona_runtime` + - **realtime**:AUTHENTICATED_LOOPBACK_EVENT_STREAM_NOT_GIT_SYNC `vocabulary.realtime` + - **tcs_compiler**:PUBLIC_MINIMUM_STAGE_ZERO_ADAPTER_TO_GIR_NOT_WORLD_CANON_OR_FULL_TCS_WORK `vocabulary.tcs_compiler` + +--- + +本页只改变阅读顺序,不改变 TCS/HLDP 语义。 diff --git a/product-source/hololake-clean-desktop/language/sources/BINGSHUO-PUBLIC-RUNTIME-COMPLETION-20260903.txt b/product-source/hololake-clean-desktop/language/sources/BINGSHUO-PUBLIC-RUNTIME-COMPLETION-20260903.txt new file mode 100644 index 000000000..87abf5152 --- /dev/null +++ b/product-source/hololake-clean-desktop/language/sources/BINGSHUO-PUBLIC-RUNTIME-COMPLETION-20260903.txt @@ -0,0 +1,5 @@ +来源:冰朔 ICE-GL∞ 在当前 Codex 任务中的直接自然语言,2026-09-03。 + +继续开发通用 TCS/GIR Agent 执行器、公众人格运行时、外部 AI 实时通信,把这些做完,让 HoloLake 软件在冰朔本地电脑上能够正常使用。 + +沿用此前已经确认的边界:人格体是推理大脑,Agent 是执行手脚;公众 HoloLake 是每个用户自己的初始化频道,不内置冰朔第五域私人核,不把外部 AI 连接冒充人格绑定。人类语言、人格回应、系统环境、协议运行、Agent 动作、工具结果和系统回执必须分流。执行必须经过 TCS/GIR、能力白名单、人类审批、目标读回和回执。 diff --git a/product-source/hololake-clean-desktop/package-lock.json b/product-source/hololake-clean-desktop/package-lock.json index 257625876..fe2f8d663 100644 --- a/product-source/hololake-clean-desktop/package-lock.json +++ b/product-source/hololake-clean-desktop/package-lock.json @@ -1,12 +1,12 @@ { "name": "hololake-clean-desktop", - "version": "1.0.0", + "version": "1.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "hololake-clean-desktop", - "version": "1.0.0", + "version": "1.1.0", "dependencies": { "@tauri-apps/api": "2.10.1", "@tauri-apps/plugin-dialog": "2.7.2", diff --git a/product-source/hololake-clean-desktop/package.json b/product-source/hololake-clean-desktop/package.json index de623511e..d3e4ed535 100644 --- a/product-source/hololake-clean-desktop/package.json +++ b/product-source/hololake-clean-desktop/package.json @@ -1,7 +1,7 @@ { "name": "hololake-clean-desktop", "private": true, - "version": "1.0.0", + "version": "1.1.0", "type": "module", "scripts": { "dev": "vite --host 127.0.0.1", diff --git a/product-source/hololake-clean-desktop/registries/module-registry.json b/product-source/hololake-clean-desktop/registries/module-registry.json index 6edb9d534..dd3a350c5 100644 --- a/product-source/hololake-clean-desktop/registries/module-registry.json +++ b/product-source/hololake-clean-desktop/registries/module-registry.json @@ -23,15 +23,17 @@ "module_id": "HLP-MOD-GLP-BRIDGE-0001", "name_zh": "光湖语言翻译桥", "kind": "SYSTEM_FOUNDATION", - "state": "TYPED_SOURCE_ENVELOPE_IMPLEMENTED_FULL_PERSONA_TO_AGENT_TRANSLATION_PENDING", - "source": "src/types.ts#SourceKind" + "state": "LOCAL_REALTIME_ACTIVE", + "source": "src-tauri/src/realtime_bridge.rs", + "transport": "GLP_LOCAL_REALTIME/1_LOOPBACK_TCP_NDJSON" }, { "module_id": "HLP-MOD-AGENT-0001", "name_zh": "本地 Agent 执行核", "kind": "SYSTEM_FOUNDATION", - "state": "KNOWLEDGE_CAPABILITY_SLICE_IMPLEMENTED_GENERAL_TCS_GIR_EXECUTOR_PENDING", - "source": "src-tauri/src/lib.rs" + "state": "PUBLIC_MINIMUM_TCS_TO_GIR_AND_HUMAN_APPROVED_AGENT_ACTIVE", + "source": "src-tauri/src/agent_executor.rs", + "registered_operations": ["KNOWLEDGE.CREATE", "KNOWLEDGE.UPDATE", "KNOWLEDGE.DELETE", "KNOWLEDGE.READ", "KNOWLEDGE.LIST"] }, { "module_id": "HLP-MOD-UPDATE-0001", @@ -44,8 +46,16 @@ "module_id": "HLP-MOD-EXT-AI-0001", "name_zh": "外部编程 AI 表达桥", "kind": "OPTIONAL_LOCAL_BRIDGE", - "state": "LOCAL_EXPRESSION_INBOX_OUTBOX_IMPLEMENTED", - "source": "src-tauri/src/lib.rs#register_external_ai_bridge" + "state": "LOCAL_REALTIME_CONNECTOR_ACTIVE", + "source": "connectors/hololake-glp-client.py" + }, + { + "module_id": "HLP-MOD-PERSONA-RUNTIME-0001", + "name_zh": "公众人格试用运行时", + "kind": "SYSTEM_FOUNDATION", + "state": "LOCAL_TRIAL_PERSONA_RUNTIME_ACTIVE_EXISTING_PERSONA_VERIFICATION_NOT_CLAIMED", + "source": "src-tauri/src/persona_runtime.rs", + "reversible_trial_days": 30 } ] } diff --git a/product-source/hololake-clean-desktop/scripts/audit-clean-v1.py b/product-source/hololake-clean-desktop/scripts/audit-clean-v1.py index 0e99dea14..01917b47b 100644 --- a/product-source/hololake-clean-desktop/scripts/audit-clean-v1.py +++ b/product-source/hololake-clean-desktop/scripts/audit-clean-v1.py @@ -13,7 +13,7 @@ if len(ids) != len(set(ids)): errors.append("duplicate module id") required_sources = { - "USER_MESSAGE", "PERSONA_RESPONSE", "SYSTEM_CONTEXT", "PROTOCOL_EVENT", + "USER_MESSAGE", "PERSONA_RESPONSE", "EXTERNAL_AI_MESSAGE", "SYSTEM_CONTEXT", "PROTOCOL_EVENT", "AGENT_ACTION", "TOOL_RESULT", "SYSTEM_RECEIPT", } types = (ROOT / "src/types.ts").read_text() @@ -31,6 +31,20 @@ for required in ("marked", "dompurify"): renderer = ROOT / "src/modules/knowledge-render/index.tsx" if not renderer.exists() or "MarkdownDocument" not in renderer.read_text(): errors.append("knowledge human visual renderer missing") +for required_file in ( + "src-tauri/src/agent_executor.rs", + "src-tauri/src/persona_runtime.rs", + "src-tauri/src/realtime_bridge.rs", + "connectors/hololake-glp-client.py", + "contracts/public-runtime-v1.json", +): + if not (ROOT / required_file).exists(): + errors.append(f"public runtime file missing: {required_file}") +runtime_contract = json.loads((ROOT / "contracts/public-runtime-v1.json").read_text()) +if runtime_contract["transport"]["bind"] != "127.0.0.1": + errors.append("realtime bridge is not loopback-only") +if runtime_contract["mutation_gate"] != "PENDING_HUMAN_APPROVAL_TO_EXPLICIT_UI_APPROVAL_TO_EXECUTION": + errors.append("agent human approval gate missing") tauri = json.loads((ROOT / "src-tauri/tauri.conf.json").read_text()) endpoints = tauri["plugins"]["updater"]["endpoints"] diff --git a/product-source/hololake-clean-desktop/src-tauri/Cargo.lock b/product-source/hololake-clean-desktop/src-tauri/Cargo.lock index a73ced7fb..463ef3604 100644 --- a/product-source/hololake-clean-desktop/src-tauri/Cargo.lock +++ b/product-source/hololake-clean-desktop/src-tauri/Cargo.lock @@ -642,7 +642,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -801,7 +801,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -1328,8 +1328,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hololake-clean-desktop" -version = "1.0.0" +version = "1.1.0" dependencies = [ + "regex", "serde", "serde_json", "sha2", @@ -3061,7 +3062,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -3117,7 +3118,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -3527,7 +3528,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -4113,10 +4114,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.3.4", + "getrandom 0.4.3", "once_cell", "rustix", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -4876,7 +4877,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] diff --git a/product-source/hololake-clean-desktop/src-tauri/Cargo.toml b/product-source/hololake-clean-desktop/src-tauri/Cargo.toml index 8a125e539..7f73716df 100644 --- a/product-source/hololake-clean-desktop/src-tauri/Cargo.toml +++ b/product-source/hololake-clean-desktop/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hololake-clean-desktop" -version = "1.0.0" +version = "1.1.0" description = "HoloLake clean personal language operating system shell" authors = ["HoloLake"] license = "AGPL-3.0-or-later" @@ -25,6 +25,7 @@ serde = { version = "1", features = ["derive"] } serde_json = "1" sha2 = "0.10" uuid = { version = "1", features = ["v4"] } +regex = "1" [dev-dependencies] tempfile = "3" diff --git a/product-source/hololake-clean-desktop/src-tauri/src/agent_executor.rs b/product-source/hololake-clean-desktop/src-tauri/src/agent_executor.rs new file mode 100644 index 000000000..b575fd83c --- /dev/null +++ b/product-source/hololake-clean-desktop/src-tauri/src/agent_executor.rs @@ -0,0 +1,414 @@ +use crate::{model::SourceKind, storage}; +use regex::Regex; +use serde::{Deserialize, Serialize}; +use serde_json::Value; +use std::{collections::BTreeMap, fs, path::Path}; +use tauri::AppHandle; + +const REQUIRED_SECTIONS: &[&str] = &[ + "header", + "source", + "subject", + "target", + "inputs", + "outputs", + "conditions", + "actions", + "authority", + "resources", + "failure", + "stop", + "cleanup", + "rollback", + "receipt", +]; +const REGISTERED_OPERATIONS: &[&str] = &[ + "KNOWLEDGE.CREATE", + "KNOWLEDGE.UPDATE", + "KNOWLEDGE.DELETE", + "KNOWLEDGE.READ", + "KNOWLEDGE.LIST", +]; + +#[derive(Clone, Debug, Deserialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct TcsCompileRequest { + pub source: String, + #[serde(default)] + pub inputs: BTreeMap, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct GirAction { + pub action_id: String, + pub operation: String, + pub input: BTreeMap, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct GirProgram { + pub schema: String, + pub program_id: String, + pub channel_id: String, + pub source_sha256: String, + pub compiler_id: String, + pub unresolved_natural_language: bool, + pub actions: Vec, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct AgentProposal { + pub proposal_id: String, + pub client_id: String, + pub persona_id: Option, + pub state: String, + pub created_at: String, + pub gir: GirProgram, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct AgentReceipt { + pub schema: String, + pub receipt_id: String, + pub proposal_id: String, + pub state: String, + pub action_results: Vec, + pub git_commit: Option, + pub target_readback_sha256: String, +} + +fn assignments(block: &str) -> Result, String> { + let string_re = Regex::new(r#"\"((?:\\.|[^\"])*)\""#).unwrap(); + string_re + .captures_iter(block) + .map(|capture| { + serde_json::from_str::(&format!("\"{}\"", &capture[1])) + .map_err(|_| "TCS-E1001 INVALID_STRING".to_string()) + }) + .collect() +} + +pub fn compile( + source: &str, + inputs: &BTreeMap, + channel_id: &str, +) -> Result { + if source.len() > 256 * 1024 || !source.trim_start().starts_with("TCS 0.1;") { + return Err("TCS-E1001 INVALID_PROLOGUE".into()); + } + for section in REQUIRED_SECTIONS { + let re = Regex::new(&format!(r"(?m)\b{}\s*\{{", regex::escape(section))).unwrap(); + if !re.is_match(source) { + return Err(format!("TCS-E1004 REQUIRED_SECTION_MISSING:{section}")); + } + } + let program_re = Regex::new(r"(?m)\bPROGRAM\s+([\p{L}_][\p{L}\p{N}_.:/@-]*)\s*\{").unwrap(); + let program_id = program_re + .captures(source) + .and_then(|capture| capture.get(1)) + .map(|m| m.as_str().to_owned()) + .ok_or_else(|| "TCS-E1001 PROGRAM_DECLARATION_REQUIRED".to_string())?; + let action_re = Regex::new(r#"(?s)([\p{L}_][\p{L}\p{N}_.:/@-]*)\s*\{[^{}]*?operation\s*=\s*\"([^\"]+)\"\s*;[^{}]*?input_refs\s*=\s*\[([^\]]*)\]"#).unwrap(); + let mut actions = Vec::new(); + for capture in action_re.captures_iter(source) { + let operation = capture[2].to_string(); + if !REGISTERED_OPERATIONS.contains(&operation.as_str()) { + return Err(format!("TCS-E2101 UNREGISTERED_OPERATION:{operation}")); + } + let mut input = BTreeMap::new(); + for reference in assignments(&capture[3])? { + let value = inputs + .get(&reference) + .cloned() + .ok_or_else(|| format!("TCS-E2002 REFERENCE_NOT_DECLARED:{reference}"))?; + input.insert(reference, value); + } + actions.push(GirAction { + action_id: capture[1].to_string(), + operation, + input, + }); + } + if actions.is_empty() { + return Err("TCS-E1004 ACTION_REQUIRED".into()); + } + Ok(GirProgram { + schema: "hololake.gir-program/v1".into(), + program_id, + channel_id: channel_id.into(), + source_sha256: storage::sha(source.as_bytes()), + compiler_id: "HLP-TCS-PUBLIC-STAGE0-0001".into(), + unresolved_natural_language: false, + actions, + }) +} + +fn proposals_path(app: &AppHandle) -> Result { + Ok(storage::root(app)?.join("agent-proposals.json")) +} +fn load(app: &AppHandle) -> Result, String> { + let path = proposals_path(app)?; + if !path.exists() { + return Ok(Vec::new()); + } + storage::read_json(&path) +} +fn save(app: &AppHandle, proposals: &[AgentProposal]) -> Result<(), String> { + storage::write_json(&proposals_path(app)?, proposals) +} + +pub fn list(app: &AppHandle) -> Result, String> { + load(app) +} + +pub fn queue( + app: &AppHandle, + request: TcsCompileRequest, + client_id: String, + persona_id: Option, +) -> Result { + let channel = storage::channel(app)?.ok_or_else(|| "CHANNEL_NOT_INITIALIZED".to_string())?; + let gir = compile(&request.source, &request.inputs, &channel.channel_id)?; + let proposal = AgentProposal { + proposal_id: storage::id("HL-PROP"), + client_id, + persona_id, + state: "PENDING_HUMAN_APPROVAL".into(), + created_at: storage::now(), + gir, + }; + let mut proposals = load(app)?; + proposals.push(proposal.clone()); + save(app, &proposals)?; + storage::append_event( + app, + &storage::event( + SourceKind::ProtocolEvent, + "TCS 已编译为 GIR", + format!("{} 等待人类审批。", proposal.proposal_id), + "WAITING", + ), + )?; + Ok(proposal) +} + +fn value<'a>(input: &'a BTreeMap, name: &str) -> Result<&'a Value, String> { + input + .get(name) + .or_else(|| input.get(&name.to_lowercase())) + .ok_or_else(|| format!("TCS-E2002 INPUT_REQUIRED:{name}")) +} +fn text(input: &BTreeMap, name: &str) -> Result { + value(input, name)? + .as_str() + .map(str::to_owned) + .ok_or_else(|| format!("TCS-E2001 TEXT_REQUIRED:{name}")) +} +fn safe_title(title: &str) -> Result { + let title = title.trim(); + if title.is_empty() || title.chars().count() > 120 { + return Err("KNOWLEDGE_TITLE_INVALID".into()); + } + Ok(title + .chars() + .map(|c| if "\\/:*?\"<>|".contains(c) { '-' } else { c }) + .collect()) +} +fn move_to_trash(root: &Path, relative: &Path) -> Result<(), String> { + let source = root.join("docs").join(relative); + if !source.exists() { + return Err("KNOWLEDGE_DOCUMENT_NOT_FOUND".into()); + } + let target = root.join(".trash").join(format!( + "{}-{}", + storage::id("agent"), + relative + .file_name() + .and_then(|v| v.to_str()) + .unwrap_or("document.md") + )); + fs::create_dir_all(target.parent().unwrap()).map_err(|e| e.to_string())?; + fs::rename(source, target).map_err(|e| e.to_string()) +} + +pub fn approve(app: &AppHandle, proposal_id: &str) -> Result { + let mut proposals = load(app)?; + let index = proposals + .iter() + .position(|proposal| proposal.proposal_id == proposal_id) + .ok_or_else(|| "AGENT_PROPOSAL_NOT_FOUND".to_string())?; + if proposals[index].state != "PENDING_HUMAN_APPROVAL" { + return Err("AGENT_PROPOSAL_NOT_PENDING".into()); + } + let proposal = proposals[index].clone(); + let root = storage::knowledge_root(app)?; + let mut results = Vec::new(); + for action in &proposal.gir.actions { + let result = match action.operation.as_str() { + "KNOWLEDGE.CREATE" => { + let title = safe_title(&text(&action.input, "TITLE")?)?; + let body = action + .input + .get("BODY") + .and_then(Value::as_str) + .unwrap_or(""); + let relative = format!("{}.md", title); + let path = root.join("docs").join(&relative); + if path.exists() { + return Err("KNOWLEDGE_DOCUMENT_EXISTS".into()); + } + fs::write( + &path, + if body.trim().is_empty() { + format!("# {title}\n\n") + } else { + body.to_owned() + }, + ) + .map_err(|e| e.to_string())?; + serde_json::json!({"actionId":action.action_id,"path":relative,"sha256":storage::sha(&fs::read(path).map_err(|e|e.to_string())?)}) + } + "KNOWLEDGE.UPDATE" => { + let relative = storage::safe_rel(&text(&action.input, "PATH")?)?; + let path = root.join("docs").join(&relative); + let before = + fs::read(&path).map_err(|_| "KNOWLEDGE_DOCUMENT_NOT_FOUND".to_string())?; + if let Some(expected) = action.input.get("EXPECTED_SHA256").and_then(Value::as_str) + { + if storage::sha(&before) != expected { + return Err("TCS-E6001 TARGET_STATE_MISMATCH".into()); + } + } + fs::write(&path, text(&action.input, "BODY")?).map_err(|e| e.to_string())?; + serde_json::json!({"actionId":action.action_id,"path":relative,"sha256":storage::sha(&fs::read(path).map_err(|e|e.to_string())?)}) + } + "KNOWLEDGE.DELETE" => { + let relative = storage::safe_rel(&text(&action.input, "PATH")?)?; + move_to_trash(&root, &relative)?; + serde_json::json!({"actionId":action.action_id,"path":relative,"state":"MOVED_TO_TRASH"}) + } + "KNOWLEDGE.READ" => { + let path = text(&action.input, "PATH")?; + let document = storage::read_doc(app, &path)?; + serde_json::to_value(document).map_err(|e| e.to_string())? + } + "KNOWLEDGE.LIST" => { + serde_json::to_value(storage::list_docs(app)?).map_err(|e| e.to_string())? + } + operation => return Err(format!("TCS-E2101 UNREGISTERED_OPERATION:{operation}")), + }; + results.push(result); + } + let git_commit = storage::git_commit( + &root, + &format!("agent({}): execute approved GIR", proposal.gir.program_id), + )?; + let readback = storage::list_docs(app)?; + let readback_bytes = serde_json::to_vec(&readback).map_err(|e| e.to_string())?; + let receipt = AgentReceipt { + schema: "hololake.agent-execution-receipt/v1".into(), + receipt_id: storage::id("HL-AGENT-RCP"), + proposal_id: proposal.proposal_id.clone(), + state: "EXECUTED_TARGET_READBACK_VERIFIED".into(), + action_results: results, + git_commit, + target_readback_sha256: storage::sha(&readback_bytes), + }; + proposals[index].state = "EXECUTED".into(); + save(app, &proposals)?; + storage::append_event( + app, + &storage::event( + SourceKind::AgentAction, + "Agent 已执行批准的 GIR", + proposal.gir.program_id, + "SUCCEEDED", + ), + )?; + storage::append_event( + app, + &storage::event( + SourceKind::ToolResult, + "目标读回", + receipt.target_readback_sha256.clone(), + "SUCCEEDED", + ), + )?; + storage::append_event( + app, + &storage::event( + SourceKind::SystemReceipt, + "系统回执", + format!("{} 已完成并读回。", receipt.receipt_id), + "SUCCEEDED", + ), + )?; + let receipt_path = storage::root(app)? + .join("agent-receipts") + .join(format!("{}.json", receipt.receipt_id)); + storage::write_json(&receipt_path, &receipt)?; + Ok(receipt) +} + +pub fn reject(app: &AppHandle, proposal_id: &str) -> Result { + let mut proposals = load(app)?; + let proposal = proposals + .iter_mut() + .find(|proposal| proposal.proposal_id == proposal_id) + .ok_or_else(|| "AGENT_PROPOSAL_NOT_FOUND".to_string())?; + if proposal.state != "PENDING_HUMAN_APPROVAL" { + return Err("AGENT_PROPOSAL_NOT_PENDING".into()); + } + proposal.state = "REJECTED_BY_HUMAN".into(); + let result = proposal.clone(); + save(app, &proposals)?; + storage::append_event( + app, + &storage::event( + SourceKind::SystemReceipt, + "执行已驳回", + proposal_id, + "SUCCEEDED", + ), + )?; + Ok(result) +} + +#[cfg(test)] +mod tests { + use super::*; + fn base_program(operation: &str) -> String { + format!( + r#"TCS 0.1; +PROGRAM TEST-PROGRAM-0001 {{ +header {{ schema = "tcs.program/v1"; }} source {{ source_id = "S"; }} subject {{ subject_id = "P"; }} target {{ target_id = "K"; }} +inputs {{ TITLE {{ type = "Text"; source = "EVENT"; required = true; }} }} outputs {{ RESULT {{ type = "Text"; destination = "R"; integrity = "SHA256"; }} }} +conditions {{ C1 {{ predicate = "APPROVED"; on_false = "FAIL_CLOSED"; }} }} actions {{ A1 {{ operation = "{operation}"; input_refs = ["TITLE"]; output_refs = ["RESULT"]; on_success = "COMPLETE"; on_failure = "FAIL_CLOSED"; }} }} +authority {{ issuer = "H"; }} resources {{ runway = "LOCAL"; }} failure {{ errors = []; }} stop {{ signals = []; }} cleanup {{ targets = []; }} rollback {{ preconditions = []; }} receipt {{ protocol = "GLP"; }} +}}"# + ) + } + #[test] + fn public_tcs_compiles_registered_operation() { + let mut inputs = BTreeMap::new(); + inputs.insert("TITLE".into(), Value::String("测试".into())); + let gir = compile(&base_program("KNOWLEDGE.CREATE"), &inputs, "HL-CH-TEST").unwrap(); + assert_eq!(gir.actions[0].operation, "KNOWLEDGE.CREATE"); + } + #[test] + fn public_tcs_rejects_unregistered_operation() { + let mut inputs = BTreeMap::new(); + inputs.insert("TITLE".into(), Value::String("测试".into())); + assert!(compile(&base_program("ABSORB"), &inputs, "HL-CH-TEST") + .unwrap_err() + .contains("TCS-E2101")); + } + #[test] + fn public_tcs_requires_all_program_sections() { + assert!(compile("TCS 0.1; PROGRAM X { actions { A1 { operation = \"KNOWLEDGE.LIST\"; input_refs = []; } } }", &BTreeMap::new(), "HL-CH-TEST").unwrap_err().contains("TCS-E1004")); + } +} diff --git a/product-source/hololake-clean-desktop/src-tauri/src/lib.rs b/product-source/hololake-clean-desktop/src-tauri/src/lib.rs index e552e1acc..417b75edb 100644 --- a/product-source/hololake-clean-desktop/src-tauri/src/lib.rs +++ b/product-source/hololake-clean-desktop/src-tauri/src/lib.rs @@ -1,9 +1,13 @@ +mod agent_executor; mod model; +mod persona_runtime; +mod realtime_bridge; mod storage; use model::*; +use serde::Serialize; use std::{fs, path::Path}; -use tauri::AppHandle; +use tauri::{AppHandle, Manager, State}; use tauri_plugin_dialog::DialogExt; const UPDATE_ENDPOINT: &str = "https://guanghulab.com/hololake/releases/latest.json"; @@ -76,7 +80,11 @@ fn create_channel(app: AppHandle, name: String) -> Result { } #[tauri::command] -fn submit_user_message(app: AppHandle, content: String) -> Result { +fn submit_user_message( + app: AppHandle, + realtime: State<'_, realtime_bridge::RealtimeBridgeState>, + content: String, +) -> Result { let content = content.trim(); if content.is_empty() { return Err("EMPTY_USER_MESSAGE".into()); @@ -86,16 +94,17 @@ fn submit_user_message(app: AppHandle, content: String) -> Result Result<(), String> { + let mut bridges = storage::bridges(app)?; + if bridges + .iter() + .any(|bridge| bridge.bridge_id == "HLP-BRIDGE-LOCAL-DEFAULT") + { + return Ok(()); + } + let root = storage::root(app)? + .join("external-ai") + .join("HLP-BRIDGE-LOCAL-DEFAULT"); + let inbox = root.join("inbox"); + let outbox = root.join("outbox"); + fs::create_dir_all(&inbox).map_err(|e| e.to_string())?; + fs::create_dir_all(&outbox).map_err(|e| e.to_string())?; + bridges.push(ExternalAiBridge { + bridge_id: "HLP-BRIDGE-LOCAL-DEFAULT".into(), + display_name: "本机编程 AI".into(), + inbox_path: inbox.to_string_lossy().into_owned(), + outbox_path: outbox.to_string_lossy().into_owned(), + state: "GLP_REALTIME_REGISTERED_NO_EXECUTION_AUTHORITY".into(), + }); + storage::save_bridges(app, &bridges) +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +struct RuntimeOverview { + realtime: realtime_bridge::RealtimeBridgeStatus, + persona: persona_runtime::PersonaRuntimeSnapshot, + proposals: Vec, +} + +#[tauri::command] +fn get_runtime_overview( + app: AppHandle, + realtime: State<'_, realtime_bridge::RealtimeBridgeState>, +) -> Result { + Ok(RuntimeOverview { + realtime: realtime.status(), + persona: persona_runtime::snapshot(&app)?, + proposals: agent_executor::list(&app)?, + }) +} + +#[tauri::command] +fn get_realtime_invitation( + app: AppHandle, + realtime: State<'_, realtime_bridge::RealtimeBridgeState>, +) -> Result { + realtime_bridge::invitation(&app, &realtime) +} + +#[tauri::command] +fn register_public_persona( + app: AppHandle, + display_name: String, +) -> Result { + persona_runtime::register(&app, display_name) +} + +#[tauri::command] +fn delete_trial_persona( + app: AppHandle, + persona_id: String, + exact_confirmation: String, +) -> Result { + persona_runtime::delete_trial(&app, &persona_id, &exact_confirmation) +} + +#[tauri::command] +fn compile_tcs_agent_proposal( + app: AppHandle, + request: agent_executor::TcsCompileRequest, +) -> Result { + agent_executor::queue(&app, request, "HOLOLAKE_LOCAL_UI".into(), None) +} + +#[tauri::command] +fn approve_agent_proposal( + app: AppHandle, + realtime: State<'_, realtime_bridge::RealtimeBridgeState>, + proposal_id: String, +) -> Result { + let receipt = agent_executor::approve(&app, &proposal_id)?; + realtime.broadcast_value(serde_json::json!({"type":"agent_receipt","receipt":receipt})); + Ok(receipt) +} + +#[tauri::command] +fn reject_agent_proposal( + app: AppHandle, + realtime: State<'_, realtime_bridge::RealtimeBridgeState>, + proposal_id: String, +) -> Result { + let proposal = agent_executor::reject(&app, &proposal_id)?; + realtime + .broadcast_value(serde_json::json!({"type":"proposal_rejected","proposalId":proposal_id})); + Ok(proposal) +} + #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { tauri::Builder::default() .plugin(tauri_plugin_dialog::init()) .plugin(tauri_plugin_process::init()) .plugin(tauri_plugin_updater::Builder::new().build()) + .setup(|app| { + let diagnostic = storage::root(app.handle()) + .map_err(std::io::Error::other)? + .join("startup-runtime-diagnostic.json"); + storage::write_json( + &diagnostic, + &serde_json::json!({"state":"SETUP_ENTERED","recordedAt":storage::now()}), + ) + .map_err(std::io::Error::other)?; + if std::env::var_os("HOLOLAKE_RUNTIME_TEST_ROOT").is_some() { + if let Ok(channel_name) = std::env::var("HOLOLAKE_TEST_CHANNEL_NAME") { + create_channel(app.handle().clone(), channel_name) + .map_err(std::io::Error::other)?; + } + if let Ok(display_name) = std::env::var("HOLOLAKE_TEST_PERSONA_NAME") { + persona_runtime::register(app.handle(), display_name) + .map_err(std::io::Error::other)?; + } + } + ensure_default_external_ai_bridge(app.handle()).map_err(std::io::Error::other)?; + let state = match realtime_bridge::start(app.handle().clone()) { + Ok(state) => state, + Err(error) => { + let _ = storage::write_json( + &diagnostic, + &serde_json::json!({"state":"LISTENER_FAILED","error":error.clone(),"recordedAt":storage::now()}), + ); + return Err(std::io::Error::other(error).into()); + } + }; + storage::write_json( + &diagnostic, + &serde_json::json!({"state":"LISTENER_BOUND","status":state.status(),"recordedAt":storage::now()}), + ) + .map_err(std::io::Error::other)?; + let invitation = + realtime_bridge::invitation(app.handle(), &state).map_err(std::io::Error::other)?; + storage::write_json( + &storage::root(app.handle()) + .map_err(std::io::Error::other)? + .join("startup-runtime-receipt.json"), + &serde_json::json!({ + "schema":"hololake.startup-runtime-receipt/v1", + "state":"GLP_LISTENING", + "protocol":invitation.protocol, + "endpoint":invitation.endpoint, + "recordedAt":storage::now() + }), + ) + .map_err(std::io::Error::other)?; + app.manage(state); + Ok(()) + }) .invoke_handler(tauri::generate_handler![ system_snapshot, create_channel, @@ -362,7 +525,14 @@ pub fn run() { delete_knowledge_document, import_knowledge_folder, export_knowledge_document, - register_external_ai_bridge + register_external_ai_bridge, + get_runtime_overview, + get_realtime_invitation, + register_public_persona, + delete_trial_persona, + compile_tcs_agent_proposal, + approve_agent_proposal, + reject_agent_proposal ]) .run(tauri::generate_context!()) .expect("HoloLake runtime failed") diff --git a/product-source/hololake-clean-desktop/src-tauri/src/model.rs b/product-source/hololake-clean-desktop/src-tauri/src/model.rs index 413e26d9f..5eb45cf30 100644 --- a/product-source/hololake-clean-desktop/src-tauri/src/model.rs +++ b/product-source/hololake-clean-desktop/src-tauri/src/model.rs @@ -15,6 +15,7 @@ pub struct Channel { pub enum SourceKind { UserMessage, PersonaResponse, + ExternalAiMessage, SystemContext, ProtocolEvent, AgentAction, diff --git a/product-source/hololake-clean-desktop/src-tauri/src/persona_runtime.rs b/product-source/hololake-clean-desktop/src-tauri/src/persona_runtime.rs new file mode 100644 index 000000000..0b2a9c4a1 --- /dev/null +++ b/product-source/hololake-clean-desktop/src-tauri/src/persona_runtime.rs @@ -0,0 +1,143 @@ +use crate::{model::SourceKind, storage}; +use serde::{Deserialize, Serialize}; +use std::fs; +use tauri::AppHandle; + +const PERSONA_STATE: &str = "LOCAL_TRIAL_UNVERIFIED_EXTERNAL_HOST_REQUIRED"; + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct PublicPersona { + pub persona_id: String, + pub display_name: String, + pub state: String, + pub created_at: String, + pub reversible_until_unix_ms: u128, +} + +#[derive(Clone, Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct PersonaRuntimeSnapshot { + pub schema: &'static str, + pub state: String, + pub active_persona_id: Option, + pub personas: Vec, + pub verified_existing_persona_count: usize, +} + +fn path(app: &AppHandle) -> Result { + Ok(storage::root(app)?.join("public-persona-runtime.json")) +} + +fn load(app: &AppHandle) -> Result, String> { + let p = path(app)?; + if !p.exists() { + return Ok(Vec::new()); + } + serde_json::from_slice(&fs::read(p).map_err(|e| e.to_string())?).map_err(|e| e.to_string()) +} + +fn save(app: &AppHandle, personas: &[PublicPersona]) -> Result<(), String> { + storage::write_json(&path(app)?, personas) +} + +pub fn exists(app: &AppHandle, persona_id: &str) -> Result { + Ok(load(app)? + .iter() + .any(|persona| persona.persona_id == persona_id)) +} + +pub fn snapshot(app: &AppHandle) -> Result { + let personas = load(app)?; + Ok(PersonaRuntimeSnapshot { + schema: "hololake.public-persona-runtime/v1", + state: if personas.is_empty() { + "READY_NO_PERSONA" + } else { + "TRIAL_PERSONA_READY" + } + .into(), + active_persona_id: personas.first().map(|persona| persona.persona_id.clone()), + personas, + verified_existing_persona_count: 0, + }) +} + +pub fn register(app: &AppHandle, display_name: String) -> Result { + let display_name = display_name.trim(); + if display_name.is_empty() || display_name.chars().count() > 64 { + return Err("PERSONA_DISPLAY_NAME_INVALID".into()); + } + let mut personas = load(app)?; + if personas + .iter() + .any(|persona| persona.display_name == display_name) + { + return Err("PERSONA_DISPLAY_NAME_EXISTS".into()); + } + let now_ms = storage::now_unix_ms(); + let persona = PublicPersona { + persona_id: format!( + "HL-PERSONA-{}", + uuid::Uuid::new_v4().simple().to_string()[..10].to_uppercase() + ), + display_name: display_name.into(), + state: PERSONA_STATE.into(), + created_at: storage::now(), + reversible_until_unix_ms: now_ms + 30 * 24 * 60 * 60 * 1_000, + }; + personas.push(persona.clone()); + save(app, &personas)?; + let event = storage::event( + SourceKind::SystemReceipt, + "公众人格试用运行时已建立", + format!( + "{} 已登记为本频道可逆试用人格;尚未验证为任何既有历史人格。", + persona.persona_id + ), + "SUCCEEDED", + ); + storage::append_event(app, &event)?; + Ok(persona) +} + +pub fn delete_trial( + app: &AppHandle, + persona_id: &str, + exact_confirmation: &str, +) -> Result { + if exact_confirmation != format!("删除试用人格 {persona_id}") { + return Err("PERSONA_DELETE_EXACT_CONFIRMATION_REQUIRED".into()); + } + let mut personas = load(app)?; + let index = personas + .iter() + .position(|persona| persona.persona_id == persona_id) + .ok_or_else(|| "PERSONA_NOT_FOUND".to_string())?; + if personas[index].state != PERSONA_STATE { + return Err("ONLY_UNVERIFIED_TRIAL_PERSONA_IS_REVERSIBLE".into()); + } + let removed = personas.remove(index); + save(app, &personas)?; + storage::append_event( + app, + &storage::event( + SourceKind::SystemReceipt, + "试用人格已移除", + format!( + "{} 的可逆试用记录已移除;历史事件仍保留来源类型。", + removed.persona_id + ), + "SUCCEEDED", + ), + )?; + snapshot(app) +} + +#[cfg(test)] +mod tests { + #[test] + fn public_persona_state_never_claims_verified_existing_identity() { + assert!(super::PERSONA_STATE.contains("UNVERIFIED")); + } +} diff --git a/product-source/hololake-clean-desktop/src-tauri/src/realtime_bridge.rs b/product-source/hololake-clean-desktop/src-tauri/src/realtime_bridge.rs new file mode 100644 index 000000000..ecb037ed7 --- /dev/null +++ b/product-source/hololake-clean-desktop/src-tauri/src/realtime_bridge.rs @@ -0,0 +1,425 @@ +use crate::{agent_executor, model::SourceKind, persona_runtime, storage}; +use serde::{Deserialize, Serialize}; +use serde_json::{json, Value}; +#[cfg(unix)] +use std::os::unix::fs::PermissionsExt; +use std::{ + fs, + io::{BufRead, BufReader, Write}, + net::{Ipv4Addr, SocketAddrV4, TcpListener, TcpStream}, + sync::{ + atomic::{AtomicUsize, Ordering}, + mpsc, Arc, Mutex, + }, + thread, + time::Duration, +}; +use tauri::{AppHandle, Emitter, Manager}; + +const PROTOCOL: &str = "GLP_LOCAL_REALTIME/1"; +const MAX_LINE_BYTES: usize = 256 * 1024; + +#[derive(Clone)] +pub struct RealtimeBridgeState { + port: u16, + token: Arc, + _listener: Arc, + clients: Arc>>>, + connected: Arc, +} + +#[derive(Clone, Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct RealtimeBridgeStatus { + pub schema: &'static str, + pub state: &'static str, + pub protocol: &'static str, + pub endpoint: String, + pub connected_clients: usize, + pub loopback_only: bool, + pub transport_grants_authority: bool, +} + +#[derive(Clone, Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct RealtimeInvitation { + pub schema: &'static str, + pub protocol: &'static str, + pub endpoint: String, + pub token: String, + pub descriptor_path: String, + pub connector_command: String, + pub warning: &'static str, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +struct Hello { + message_type: String, + protocol: String, + token: String, + bridge_id: String, + persona_id: Option, +} + +fn token_path(app: &AppHandle) -> Result { + Ok(storage::root(app)?.join("realtime-bridge-token")) +} + +fn load_or_create_token(app: &AppHandle) -> Result { + let path = token_path(app)?; + if path.exists() { + let value = fs::read_to_string(&path).map_err(|e| e.to_string())?; + if value.trim().len() >= 32 { + return Ok(value.trim().into()); + } + return Err("GLP_TOKEN_FILE_INVALID".into()); + } + let token = format!( + "{}{}", + uuid::Uuid::new_v4().simple(), + uuid::Uuid::new_v4().simple() + ); + fs::write(&path, &token).map_err(|e| e.to_string())?; + #[cfg(unix)] + fs::set_permissions(&path, fs::Permissions::from_mode(0o600)).map_err(|e| e.to_string())?; + Ok(token) +} + +fn bind_listener() -> Result<(TcpListener, u16), String> { + for port in 39281..39291 { + if let Ok(listener) = TcpListener::bind(SocketAddrV4::new(Ipv4Addr::LOCALHOST, port)) { + return Ok((listener, port)); + } + } + Err("GLP_LOOPBACK_PORT_UNAVAILABLE".into()) +} + +pub fn start(app: AppHandle) -> Result { + let token = Arc::new(load_or_create_token(&app)?); + let (listener, port) = bind_listener()?; + let listener = Arc::new(listener); + let state = RealtimeBridgeState { + port, + token, + _listener: listener.clone(), + clients: Arc::new(Mutex::new(Vec::new())), + connected: Arc::new(AtomicUsize::new(0)), + }; + let runtime_state = state.clone(); + thread::Builder::new() + .name("hololake-glp-listener".into()) + .spawn(move || loop { + match listener.accept() { + Ok((stream, _)) => { + let connection_state = runtime_state.clone(); + let connection_app = app.clone(); + let _ = thread::Builder::new() + .name("hololake-glp-client".into()) + .spawn(move || { + if let Err(error) = + handle_connection(connection_app.clone(), connection_state, stream) + { + let _ = connection_app.emit( + "hololake-runtime-event", + json!({"kind":"CLIENT_ERROR","error":error}), + ); + } + }); + } + Err(error) => { + let _ = app.emit( + "hololake-runtime-event", + json!({"kind":"LISTENER_ERROR","error":error.to_string()}), + ); + thread::sleep(Duration::from_millis(100)); + } + } + }) + .map_err(|e| e.to_string())?; + Ok(state) +} + +impl RealtimeBridgeState { + pub fn status(&self) -> RealtimeBridgeStatus { + RealtimeBridgeStatus { + schema: "hololake.glp-local-realtime-status/v1", + state: "LISTENING", + protocol: PROTOCOL, + endpoint: format!("tcp://127.0.0.1:{}", self.port), + connected_clients: self.connected.load(Ordering::SeqCst), + loopback_only: true, + transport_grants_authority: false, + } + } + pub fn broadcast_value(&self, value: Value) { + if let Ok(line) = serde_json::to_string(&value) { + if let Ok(mut clients) = self.clients.lock() { + clients.retain(|client| client.send(line.clone()).is_ok()); + } + } + } +} + +pub fn invitation( + app: &AppHandle, + state: &RealtimeBridgeState, +) -> Result { + let descriptor = storage::root(app)?.join("realtime-bridge-descriptor.json"); + let endpoint = format!("tcp://127.0.0.1:{}", state.port); + storage::write_json( + &descriptor, + &json!({"schema":"hololake.glp-local-realtime-descriptor/v1","protocol":PROTOCOL,"endpoint":endpoint,"token":state.token.as_str()}), + )?; + #[cfg(unix)] + fs::set_permissions(&descriptor, fs::Permissions::from_mode(0o600)) + .map_err(|e| e.to_string())?; + let connector = connector_path(app)?; + Ok(RealtimeInvitation { + schema: "hololake.glp-local-realtime-invitation/v1", + protocol: PROTOCOL, + endpoint, + token: state.token.as_str().to_string(), + descriptor_path: descriptor.to_string_lossy().into_owned(), + connector_command: format!( + "python3 \"{}\" --descriptor \"{}\"", + connector.display(), + descriptor.display() + ), + warning: "令牌只认证本机连接,不证明人格身份,也不授予执行权限。", + }) +} + +fn connector_path(app: &AppHandle) -> Result { + if std::env::var_os("HOLOLAKE_RUNTIME_TEST_ROOT").is_some() { + if let Some(raw_path) = std::env::var_os("HOLOLAKE_CONNECTOR_TEST_PATH") { + let path = std::path::PathBuf::from(raw_path); + if !path.is_absolute() + || path.extension().and_then(|value| value.to_str()) != Some("py") + { + return Err("GLP_TEST_CONNECTOR_PATH_INVALID".into()); + } + let metadata = fs::metadata(&path).map_err(|_| "GLP_TEST_CONNECTOR_NOT_READABLE")?; + if !metadata.is_file() { + return Err("GLP_TEST_CONNECTOR_NOT_A_FILE".into()); + } + return Ok(path); + } + } + let path = app + .path() + .resource_dir() + .map_err(|e| e.to_string())? + .join("connectors/hololake-glp-client.py"); + if !path.is_file() { + return Err("GLP_BUNDLED_CONNECTOR_MISSING".into()); + } + Ok(path) +} + +fn send(writer: &mut TcpStream, value: Value) -> Result<(), String> { + serde_json::to_writer(&mut *writer, &value).map_err(|e| e.to_string())?; + writer.write_all(b"\n").map_err(|e| e.to_string())?; + writer.flush().map_err(|e| e.to_string()) +} + +fn handle_connection( + app: AppHandle, + state: RealtimeBridgeState, + stream: TcpStream, +) -> Result<(), String> { + stream + .set_read_timeout(Some(Duration::from_secs(10))) + .map_err(|e| e.to_string())?; + let reader_stream = stream.try_clone().map_err(|e| e.to_string())?; + let mut writer = stream; + let mut reader = BufReader::new(reader_stream); + let mut first = String::new(); + reader.read_line(&mut first).map_err(|e| e.to_string())?; + if first.len() > MAX_LINE_BYTES { + return Err("GLP_MESSAGE_TOO_LARGE".into()); + } + let hello: Hello = serde_json::from_str(&first).map_err(|_| "GLP_HELLO_INVALID".to_string())?; + if hello.message_type != "hello" + || hello.protocol != PROTOCOL + || hello.token != state.token.as_str() + { + let _ = send( + &mut writer, + json!({"type":"error","code":"GLP_TOKEN_OR_PROTOCOL_REJECTED"}), + ); + return Err("GLP_TOKEN_OR_PROTOCOL_REJECTED".into()); + } + if !storage::bridges(&app)? + .iter() + .any(|bridge| bridge.bridge_id == hello.bridge_id) + { + send( + &mut writer, + json!({"type":"error","code":"GLP_BRIDGE_NOT_REGISTERED"}), + )?; + return Err("GLP_BRIDGE_NOT_REGISTERED".into()); + } + if let Some(persona_id) = &hello.persona_id { + if !persona_runtime::exists(&app, persona_id)? { + send( + &mut writer, + json!({"type":"error","code":"PERSONA_IDENTITY_UNVERIFIED"}), + )?; + return Err("PERSONA_IDENTITY_UNVERIFIED".into()); + } + } + writer.set_read_timeout(None).map_err(|e| e.to_string())?; + reader + .get_ref() + .set_read_timeout(None) + .map_err(|e| e.to_string())?; + state.connected.fetch_add(1, Ordering::SeqCst); + let _guard = ConnectionGuard(state.connected.clone()); + send( + &mut writer, + json!({"type":"welcome","protocol":PROTOCOL,"bridgeId":hello.bridge_id,"personaId":hello.persona_id,"personaState":if hello.persona_id.is_some(){"LOCAL_TRIAL_UNVERIFIED_HOST_CONNECTED"}else{"EXTERNAL_AI_CONNECTED_NO_PERSONA"},"executionAuthority":false}), + )?; + let (tx, rx) = mpsc::channel::(); + state + .clients + .lock() + .map_err(|_| "GLP_CLIENT_REGISTRY_POISONED".to_string())? + .push(tx); + let mut outbound_writer = writer.try_clone().map_err(|e| e.to_string())?; + let _ = thread::Builder::new() + .name("hololake-glp-outbound".into()) + .spawn(move || { + while let Ok(line) = rx.recv() { + if outbound_writer.write_all(line.as_bytes()).is_err() + || outbound_writer.write_all(b"\n").is_err() + || outbound_writer.flush().is_err() + { + break; + } + } + }); + let _ = app.emit("hololake-runtime-event", json!({"kind":"CLIENT_CONNECTED"})); + + loop { + let mut line = String::new(); + if reader.read_line(&mut line).map_err(|e| e.to_string())? == 0 { + break; + } + if line.len() > MAX_LINE_BYTES { + send( + &mut writer, + json!({"type":"error","code":"GLP_MESSAGE_TOO_LARGE"}), + )?; + continue; + } + let value: Value = match serde_json::from_str(&line) { + Ok(value) => value, + Err(_) => { + send( + &mut writer, + json!({"type":"error","code":"GLP_MESSAGE_INVALID"}), + )?; + continue; + } + }; + match value.get("type").and_then(Value::as_str).unwrap_or("") { + "persona_response" if hello.persona_id.is_some() => { + let content = value + .get("content") + .and_then(Value::as_str) + .unwrap_or("") + .trim(); + if content.is_empty() { + send( + &mut writer, + json!({"type":"error","code":"EMPTY_PERSONA_RESPONSE"}), + )?; + continue; + } + let event = + storage::event(SourceKind::PersonaResponse, "人格回应", content, "RECORDED"); + storage::append_event(&app, &event)?; + let _ = app.emit("hololake-runtime-event", &event); + send(&mut writer, json!({"type":"accepted","event":event}))?; + } + "external_ai_message" => { + let content = value + .get("content") + .and_then(Value::as_str) + .unwrap_or("") + .trim(); + if content.is_empty() { + send( + &mut writer, + json!({"type":"error","code":"EMPTY_EXTERNAL_AI_MESSAGE"}), + )?; + continue; + } + let event = storage::event( + SourceKind::ExternalAiMessage, + "外部 AI 语言", + content, + "RECORDED", + ); + storage::append_event(&app, &event)?; + let _ = app.emit("hololake-runtime-event", &event); + send(&mut writer, json!({"type":"accepted","event":event}))?; + } + "tcs_proposal" => { + let request: agent_executor::TcsCompileRequest = serde_json::from_value( + value + .get("request") + .cloned() + .ok_or_else(|| "TCS_REQUEST_REQUIRED".to_string())?, + ) + .map_err(|e| format!("TCS_REQUEST_INVALID:{e}"))?; + match agent_executor::queue( + &app, + request, + hello.bridge_id.clone(), + hello.persona_id.clone(), + ) { + Ok(proposal) => { + let _ = app.emit( + "hololake-runtime-event", + json!({"kind":"PROPOSAL_PENDING","proposalId":proposal.proposal_id}), + ); + send( + &mut writer, + json!({"type":"proposal_pending","proposal":proposal}), + )?; + if isolated_approval_simulation_enabled() { + let receipt = agent_executor::approve(&app, &proposal.proposal_id)?; + state.broadcast_value(json!({ + "type":"agent_receipt", + "receipt":receipt, + "testApprovalSimulation":true + })); + } + } + Err(error) => send(&mut writer, json!({"type":"error","code":error}))?, + } + } + "ping" => send(&mut writer, json!({"type":"pong","at":storage::now()}))?, + _ => send( + &mut writer, + json!({"type":"error","code":"GLP_MESSAGE_TYPE_REJECTED"}), + )?, + } + } + Ok(()) +} + +fn isolated_approval_simulation_enabled() -> bool { + std::env::var_os("HOLOLAKE_RUNTIME_TEST_ROOT").is_some() + && std::env::var("HOLOLAKE_TEST_APPROVAL_SIMULATION").as_deref() + == Ok("SIMULATE_HUMAN_APPROVAL") +} + +struct ConnectionGuard(Arc); +impl Drop for ConnectionGuard { + fn drop(&mut self) { + self.0.fetch_sub(1, Ordering::SeqCst); + } +} diff --git a/product-source/hololake-clean-desktop/src-tauri/src/storage.rs b/product-source/hololake-clean-desktop/src-tauri/src/storage.rs index 76ae2f64f..f8ac93a51 100644 --- a/product-source/hololake-clean-desktop/src-tauri/src/storage.rs +++ b/product-source/hololake-clean-desktop/src-tauri/src/storage.rs @@ -16,6 +16,12 @@ pub fn now() -> String { .map(|d| format!("{}.{:03}Z", d.as_secs(), d.subsec_millis())) .unwrap_or_else(|_| "0Z".into()) } +pub fn now_unix_ms() -> u128 { + SystemTime::now() + .duration_since(UNIX_EPOCH) + .map(|duration| duration.as_millis()) + .unwrap_or(0) +} pub fn id(prefix: &str) -> String { format!("{}-{}", prefix, UuidPart::new()) } @@ -35,6 +41,14 @@ pub fn sha(bytes: &[u8]) -> String { } pub fn root(app: &AppHandle) -> Result { + if let Some(value) = std::env::var_os("HOLOLAKE_RUNTIME_TEST_ROOT") { + let path = PathBuf::from(value); + if !path.is_absolute() || !path.starts_with(std::env::temp_dir()) { + return Err("HOLOLAKE_RUNTIME_TEST_ROOT_OUT_OF_SCOPE".into()); + } + fs::create_dir_all(&path).map_err(|e| e.to_string())?; + return Ok(path); + } let p = app .path() .app_data_dir() @@ -43,10 +57,10 @@ pub fn root(app: &AppHandle) -> Result { fs::create_dir_all(&p).map_err(|e| e.to_string())?; Ok(p) } -fn read_json(p: &Path) -> Result { +pub fn read_json(p: &Path) -> Result { serde_json::from_slice(&fs::read(p).map_err(|e| e.to_string())?).map_err(|e| e.to_string()) } -fn write_json(p: &Path, v: &T) -> Result<(), String> { +pub fn write_json(p: &Path, v: &T) -> Result<(), String> { if let Some(x) = p.parent() { fs::create_dir_all(x).map_err(|e| e.to_string())? } diff --git a/product-source/hololake-clean-desktop/src-tauri/tauri.conf.json b/product-source/hololake-clean-desktop/src-tauri/tauri.conf.json index 957e337ad..b9db1f7e5 100644 --- a/product-source/hololake-clean-desktop/src-tauri/tauri.conf.json +++ b/product-source/hololake-clean-desktop/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "HoloLake", - "version": "1.0.0", + "version": "1.1.0", "identifier": "world.guanghu.hololake", "build": { "frontendDist": "../dist", "devUrl": "http://127.0.0.1:5211", "beforeDevCommand": "npm run dev", "beforeBuildCommand": "npm run build" }, "app": { @@ -9,5 +9,5 @@ "security": { "csp": "default-src 'self'; connect-src 'self' ipc: http://ipc.localhost https://guanghulab.com; img-src 'self' data:; style-src 'self' 'unsafe-inline'; script-src 'self'" } }, "plugins": { "updater": { "endpoints": ["https://guanghulab.com/hololake/releases/latest.json"], "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDEzQkYzRTQ5QTE2MEFDMzEKUldReHJHQ2hTVDYvRTZNNDVqZDUxLzRZMmxuT1pSM2Q5RTRZbzRCZUZ0d0FxVVNidGJ0dUQyMnoK" } }, - "bundle": { "active": true, "targets": ["app", "dmg"], "createUpdaterArtifacts": true, "category": "Productivity", "icon": ["icons/icon.icns", "icons/icon.png", "icons/icon.ico"], "macOS": { "signingIdentity": "Developer ID Application: bei sun (825A9L3G7Q)" } } + "bundle": { "active": true, "targets": ["app", "dmg"], "createUpdaterArtifacts": true, "category": "Productivity", "icon": ["icons/icon.icns", "icons/icon.png", "icons/icon.ico"], "resources": {"../connectors/hololake-glp-client.py":"connectors/hololake-glp-client.py"}, "macOS": { "signingIdentity": "Developer ID Application: bei sun (825A9L3G7Q)" } } } diff --git a/product-source/hololake-clean-desktop/src/App.tsx b/product-source/hololake-clean-desktop/src/App.tsx index 9843ed211..6937897e0 100644 --- a/product-source/hololake-clean-desktop/src/App.tsx +++ b/product-source/hololake-clean-desktop/src/App.tsx @@ -1,12 +1,14 @@ import { useEffect, useMemo, useRef, useState } from "react"; import { check } from "@tauri-apps/plugin-updater"; import { relaunch } from "@tauri-apps/plugin-process"; +import { listen } from "@tauri-apps/api/event"; import { Icon } from "./icons"; import * as api from "./runtime"; import type { KnowledgeDocument, KnowledgeSummary, SourceKind, + RuntimeOverview, SystemSnapshot, } from "./types"; import { @@ -34,6 +36,7 @@ const nav: [View, string, string][] = [ const labels: Record = { USER_MESSAGE: "用户语言", PERSONA_RESPONSE: "人格回应", + EXTERNAL_AI_MESSAGE: "外部 AI", SYSTEM_CONTEXT: "系统环境", PROTOCOL_EVENT: "协议运行", AGENT_ACTION: "Agent 执行", @@ -43,6 +46,7 @@ const labels: Record = { export default function App() { const [data, setData] = useState(null), + [runtime, setRuntime] = useState(null), [view, setView] = useState("channel"), [error, setError] = useState(""), [busy, setBusy] = useState(false); @@ -51,8 +55,23 @@ export default function App() { .snapshot() .then(setData) .catch((e) => setError(String(e))); - useEffect(() => { + const refreshRuntime = () => + api + .runtimeOverview() + .then(setRuntime) + .catch((e) => setError(String(e))); + const refreshAll = () => { refresh(); + refreshRuntime(); + }; + useEffect(() => { + refreshAll(); + if (!("__TAURI_INTERNALS__" in window)) return; + let unlisten: (() => void) | undefined; + listen("hololake-runtime-event", () => refreshAll()).then((dispose) => { + unlisten = dispose; + }); + return () => unlisten?.(); }, []); if (!data) return ( @@ -61,7 +80,7 @@ export default function App() { 正在进入 HoloLake… ); - if (!data.channel) return ; + if (!data.channel) return ; return (
@@ -104,7 +123,8 @@ export default function App() { {view === "channel" && ( setView("knowledge")} /> )}{" "} @@ -115,7 +135,8 @@ export default function App() { {view === "settings" && ( void; error: string }) { function Channel({ data, + runtime, refresh, openKnowledge, }: { data: SystemSnapshot; + runtime: RuntimeOverview | null; refresh: () => void; openKnowledge: () => void; }) { @@ -210,14 +233,19 @@ function Channel({ .filter( (e) => e.sourceKind === "USER_MESSAGE" || - e.sourceKind === "PERSONA_RESPONSE", + e.sourceKind === "PERSONA_RESPONSE" || + e.sourceKind === "EXTERNAL_AI_MESSAGE", ) .slice(0, 8) .reverse() .map((e) => (
@@ -226,6 +254,52 @@ function Channel({
))} + {runtime && + runtime.proposals.some( + (proposal) => proposal.state === "PENDING_HUMAN_APPROVAL", + ) && ( +
+

等待你确认的 Agent 动作

+ {runtime.proposals + .filter( + (proposal) => proposal.state === "PENDING_HUMAN_APPROVAL", + ) + .map((proposal) => ( +
+
+ {proposal.gir.programId} +

+ {proposal.gir.actions + .map((action) => action.operation) + .join(" → ")} +

+ + {proposal.clientId} · {proposal.proposalId} + +
+
+ + +
+
+ ))} +
+ )}