feat(hololake): complete public runtime loop

This commit is contained in:
冰朔 2026-09-03 19:44:19 +08:00
commit 92cce27973
26 changed files with 2626 additions and 79 deletions

View file

@ -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()

View file

@ -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"]
}

View file

@ -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 清单与签名;服务器发行清单尚未发布。
## 状态词

View file

@ -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";
}
}

View file

@ -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
}

View file

@ -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.

View file

@ -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 语义。

View file

@ -0,0 +1,5 @@
来源:冰朔 ICE-GL∞ 在当前 Codex 任务中的直接自然语言2026-09-03。
继续开发通用 TCS/GIR Agent 执行器、公众人格运行时、外部 AI 实时通信,把这些做完,让 HoloLake 软件在冰朔本地电脑上能够正常使用。
沿用此前已经确认的边界人格体是推理大脑Agent 是执行手脚;公众 HoloLake 是每个用户自己的初始化频道,不内置冰朔第五域私人核,不把外部 AI 连接冒充人格绑定。人类语言、人格回应、系统环境、协议运行、Agent 动作、工具结果和系统回执必须分流。执行必须经过 TCS/GIR、能力白名单、人类审批、目标读回和回执。

View file

@ -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",

View file

@ -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",

View file

@ -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
}
]
}

View file

@ -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"]

View file

@ -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]]

View file

@ -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"

View file

@ -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<String, Value>,
}
#[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<String, Value>,
}
#[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<GirAction>,
}
#[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<String>,
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<Value>,
pub git_commit: Option<String>,
pub target_readback_sha256: String,
}
fn assignments(block: &str) -> Result<Vec<String>, String> {
let string_re = Regex::new(r#"\"((?:\\.|[^\"])*)\""#).unwrap();
string_re
.captures_iter(block)
.map(|capture| {
serde_json::from_str::<String>(&format!("\"{}\"", &capture[1]))
.map_err(|_| "TCS-E1001 INVALID_STRING".to_string())
})
.collect()
}
pub fn compile(
source: &str,
inputs: &BTreeMap<String, Value>,
channel_id: &str,
) -> Result<GirProgram, String> {
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<std::path::PathBuf, String> {
Ok(storage::root(app)?.join("agent-proposals.json"))
}
fn load(app: &AppHandle) -> Result<Vec<AgentProposal>, 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<Vec<AgentProposal>, String> {
load(app)
}
pub fn queue(
app: &AppHandle,
request: TcsCompileRequest,
client_id: String,
persona_id: Option<String>,
) -> Result<AgentProposal, String> {
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<String, Value>, 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<String, Value>, name: &str) -> Result<String, String> {
value(input, name)?
.as_str()
.map(str::to_owned)
.ok_or_else(|| format!("TCS-E2001 TEXT_REQUIRED:{name}"))
}
fn safe_title(title: &str) -> Result<String, String> {
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<AgentReceipt, String> {
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<AgentProposal, String> {
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"));
}
}

View file

@ -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<Channel, String> {
}
#[tauri::command]
fn submit_user_message(app: AppHandle, content: String) -> Result<MutationReceipt, String> {
fn submit_user_message(
app: AppHandle,
realtime: State<'_, realtime_bridge::RealtimeBridgeState>,
content: String,
) -> Result<MutationReceipt, String> {
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<MutationReceip
}
let e = storage::event(SourceKind::UserMessage, "用户语言", content, "RECORDED");
storage::append_event(&app, &e)?;
realtime.broadcast_value(serde_json::json!({"type":"user_message","event":e}));
let waiting = storage::event(
SourceKind::SystemReceipt,
"系统回执",
"用户语言已按 USER_MESSAGE 保存;当前未绑定人格模型,不伪造人格回应。",
"用户语言已按 USER_MESSAGE 保存并实时发送到已连接的外部 AI系统不伪造人格回应。",
"WAITING",
);
receipt(
&app,
"WAITING_PERSONA",
"用户语言已记录,等待人格体接入",
"用户语言已记录并投递到实时桥",
None,
waiting,
)
@ -328,7 +337,7 @@ fn register_external_ai_bridge(
display_name: name.into(),
inbox_path: inbox.to_string_lossy().into_owned(),
outbox_path: outbox.to_string_lossy().into_owned(),
state: "LOCAL_EXPRESSION_ONLY_NO_EXECUTION_AUTHORITY".into(),
state: "GLP_REALTIME_REGISTERED_NO_EXECUTION_AUTHORITY".into(),
};
let mut all = storage::bridges(&app)?;
all.push(bridge.clone());
@ -346,12 +355,166 @@ fn register_external_ai_bridge(
Ok(bridge)
}
fn ensure_default_external_ai_bridge(app: &AppHandle) -> 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<agent_executor::AgentProposal>,
}
#[tauri::command]
fn get_runtime_overview(
app: AppHandle,
realtime: State<'_, realtime_bridge::RealtimeBridgeState>,
) -> Result<RuntimeOverview, String> {
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::RealtimeInvitation, String> {
realtime_bridge::invitation(&app, &realtime)
}
#[tauri::command]
fn register_public_persona(
app: AppHandle,
display_name: String,
) -> Result<persona_runtime::PublicPersona, String> {
persona_runtime::register(&app, display_name)
}
#[tauri::command]
fn delete_trial_persona(
app: AppHandle,
persona_id: String,
exact_confirmation: String,
) -> Result<persona_runtime::PersonaRuntimeSnapshot, String> {
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::AgentProposal, String> {
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<agent_executor::AgentReceipt, String> {
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<agent_executor::AgentProposal, String> {
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")

View file

@ -15,6 +15,7 @@ pub struct Channel {
pub enum SourceKind {
UserMessage,
PersonaResponse,
ExternalAiMessage,
SystemContext,
ProtocolEvent,
AgentAction,

View file

@ -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<String>,
pub personas: Vec<PublicPersona>,
pub verified_existing_persona_count: usize,
}
fn path(app: &AppHandle) -> Result<std::path::PathBuf, String> {
Ok(storage::root(app)?.join("public-persona-runtime.json"))
}
fn load(app: &AppHandle) -> Result<Vec<PublicPersona>, 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<bool, String> {
Ok(load(app)?
.iter()
.any(|persona| persona.persona_id == persona_id))
}
pub fn snapshot(app: &AppHandle) -> Result<PersonaRuntimeSnapshot, String> {
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<PublicPersona, String> {
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<PersonaRuntimeSnapshot, String> {
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"));
}
}

View file

@ -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<String>,
_listener: Arc<TcpListener>,
clients: Arc<Mutex<Vec<mpsc::Sender<String>>>>,
connected: Arc<AtomicUsize>,
}
#[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<String>,
}
fn token_path(app: &AppHandle) -> Result<std::path::PathBuf, String> {
Ok(storage::root(app)?.join("realtime-bridge-token"))
}
fn load_or_create_token(app: &AppHandle) -> Result<String, String> {
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<RealtimeBridgeState, String> {
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<RealtimeInvitation, String> {
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<std::path::PathBuf, String> {
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::<String>();
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<AtomicUsize>);
impl Drop for ConnectionGuard {
fn drop(&mut self) {
self.0.fetch_sub(1, Ordering::SeqCst);
}
}

View file

@ -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<PathBuf, String> {
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<PathBuf, String> {
fs::create_dir_all(&p).map_err(|e| e.to_string())?;
Ok(p)
}
fn read_json<T: serde::de::DeserializeOwned>(p: &Path) -> Result<T, String> {
pub fn read_json<T: serde::de::DeserializeOwned>(p: &Path) -> Result<T, String> {
serde_json::from_slice(&fs::read(p).map_err(|e| e.to_string())?).map_err(|e| e.to_string())
}
fn write_json<T: serde::Serialize>(p: &Path, v: &T) -> Result<(), String> {
pub fn write_json<T: serde::Serialize + ?Sized>(p: &Path, v: &T) -> Result<(), String> {
if let Some(x) = p.parent() {
fs::create_dir_all(x).map_err(|e| e.to_string())?
}

View file

@ -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)" } }
}

View file

@ -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<SourceKind, string> = {
USER_MESSAGE: "用户语言",
PERSONA_RESPONSE: "人格回应",
EXTERNAL_AI_MESSAGE: "外部 AI",
SYSTEM_CONTEXT: "系统环境",
PROTOCOL_EVENT: "协议运行",
AGENT_ACTION: "Agent 执行",
@ -43,6 +46,7 @@ const labels: Record<SourceKind, string> = {
export default function App() {
const [data, setData] = useState<SystemSnapshot | null>(null),
[runtime, setRuntime] = useState<RuntimeOverview | null>(null),
[view, setView] = useState<View>("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
</div>
);
if (!data.channel) return <Onboarding onDone={refresh} error={error} />;
if (!data.channel) return <Onboarding onDone={refreshAll} error={error} />;
return (
<div className="app-shell">
<div className="titlebar">
@ -104,7 +123,8 @@ export default function App() {
{view === "channel" && (
<Channel
data={data}
refresh={refresh}
runtime={runtime}
refresh={refreshAll}
openKnowledge={() => setView("knowledge")}
/>
)}{" "}
@ -115,7 +135,8 @@ export default function App() {
{view === "settings" && (
<Settings
data={data}
refresh={refresh}
runtime={runtime}
refresh={refreshAll}
busy={busy}
setBusy={setBusy}
setError={setError}
@ -171,10 +192,12 @@ function Onboarding({ onDone, error }: { onDone: () => 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) => (
<article
className={
e.sourceKind === "USER_MESSAGE" ? "human" : "persona"
e.sourceKind === "USER_MESSAGE"
? "human"
: e.sourceKind === "PERSONA_RESPONSE"
? "persona"
: "external-ai"
}
key={e.eventId}
>
@ -226,6 +254,52 @@ function Channel({
</article>
))}
</section>
{runtime &&
runtime.proposals.some(
(proposal) => proposal.state === "PENDING_HUMAN_APPROVAL",
) && (
<section className="approval-queue">
<h2> Agent </h2>
{runtime.proposals
.filter(
(proposal) => proposal.state === "PENDING_HUMAN_APPROVAL",
)
.map((proposal) => (
<article key={proposal.proposalId}>
<div>
<strong>{proposal.gir.programId}</strong>
<p>
{proposal.gir.actions
.map((action) => action.operation)
.join(" → ")}
</p>
<small>
{proposal.clientId} · {proposal.proposalId}
</small>
</div>
<div>
<button
className="soft"
onClick={async () => {
await api.rejectProposal(proposal.proposalId);
refresh();
}}
>
</button>
<button
onClick={async () => {
await api.approveProposal(proposal.proposalId);
refresh();
}}
>
</button>
</div>
</article>
))}
</section>
)}
<ReceiptStream data={data} />
<div className="composer">
<textarea
@ -256,6 +330,23 @@ function Channel({
<strong></strong>
<p className="path">{data.channel?.privateGitPath}</p>
</article>
<article>
<h2></h2>
<strong>
{runtime?.persona.personas[0]?.displayName ?? "尚未建立"}
</strong>
<p>
{runtime?.persona.personas[0]?.state ?? "不会自动生成默认人格"}
</p>
</article>
<article>
<h2>GLP </h2>
<strong>
{runtime?.realtime.state ?? "正在启动"} ·{" "}
{runtime?.realtime.connectedClients ?? 0}
</strong>
<p>{runtime?.realtime.endpoint ?? "仅监听本机回环地址"}</p>
</article>
<article>
<h2></h2>
<strong>
@ -276,7 +367,9 @@ function ReceiptStream({ data }: { data: SystemSnapshot }) {
const rows = data.timeline
.filter(
(e) =>
e.sourceKind !== "USER_MESSAGE" && e.sourceKind !== "PERSONA_RESPONSE",
e.sourceKind !== "USER_MESSAGE" &&
e.sourceKind !== "PERSONA_RESPONSE" &&
e.sourceKind !== "EXTERNAL_AI_MESSAGE",
)
.slice(0, 5);
return (
@ -839,18 +932,22 @@ function Portal() {
}
function Settings({
data,
runtime,
refresh,
busy,
setBusy,
setError,
}: {
data: SystemSnapshot;
runtime: RuntimeOverview | null;
refresh: () => void;
busy: boolean;
setBusy: (v: boolean) => void;
setError: (v: string) => void;
}) {
const [bridgeName, setBridgeName] = useState("");
const [bridgeName, setBridgeName] = useState(""),
[personaName, setPersonaName] = useState(""),
[runtimeMessage, setRuntimeMessage] = useState("");
const update = async () => {
setBusy(true);
try {
@ -894,7 +991,11 @@ function Settings({
<article className="bridge">
<div>
<h2> AI</h2>
<p></p>
<p>
{runtime?.realtime.endpoint ?? "本机实时桥正在启动"} ·{" "}
{runtime?.realtime.connectedClients ?? 0}{" "}
</p>
</div>
<div>
<input
@ -915,17 +1016,95 @@ function Settings({
</button>
</div>
</article>
<article className="bridge">
<div>
<h2></h2>
<p>
{runtime?.persona.personas.length
? `${runtime.persona.personas.length} 个本频道试用人格;既有人格验证数 ${runtime.persona.verifiedExistingPersonaCount}`
: "建立一个本频道可逆试用人格,再由外部 AI 宿主实时承载。"}
</p>
</div>
<div>
<input
value={personaName}
onChange={(e) => setPersonaName(e.target.value)}
placeholder="给试用人格起名"
/>
<button
onClick={async () => {
if (personaName.trim()) {
const persona = await api.registerPersona(personaName.trim());
setPersonaName("");
setRuntimeMessage(
`${persona.displayName} 已建立;尚未验证为既有历史人格。`,
);
refresh();
}
}}
>
</button>
</div>
</article>
<article>
<div>
<h2></h2>
<p>
{runtimeMessage ||
"先建立一个外部 AI 入口,然后复制本机 GLP 接入命令给外部编程 AI。"}
</p>
</div>
<button
className="soft"
disabled={!data.externalAiBridges.length}
onClick={async () => {
const invitation = await api.realtimeInvitation();
const bridge = data.externalAiBridges[0];
const persona = runtime?.persona.personas[0];
const command = `${invitation.connectorCommand} --bridge-id ${bridge.bridgeId}${persona ? ` --persona-id ${persona.personaId}` : ""}`;
await navigator.clipboard.writeText(command);
setRuntimeMessage(
"实时接入命令已复制。令牌只在本机 descriptor 文件中。",
);
}}
>
</button>
</article>
{data.externalAiBridges.map((b) => (
<article key={b.bridgeId}>
<div>
<h2>{b.displayName}</h2>
<p>
{b.bridgeId} · {b.inboxPath}
{b.bridgeId} ·{" "}
{runtime?.realtime.protocol ?? "GLP_LOCAL_REALTIME/1"}
</p>
</div>
<span className="status">{b.state}</span>
</article>
))}
{runtime?.persona.personas.map((persona) => (
<article key={persona.personaId}>
<div>
<h2>{persona.displayName}</h2>
<p>
{persona.personaId} · {persona.state}
</p>
</div>
<button
className="soft danger"
onClick={async () => {
if (confirm(`删除试用人格 ${persona.personaId}`)) {
await api.deleteTrialPersona(persona.personaId);
refresh();
}
}}
>
</button>
</article>
))}
</section>
</div>
);

View file

@ -1,53 +1,293 @@
import { invoke } from "@tauri-apps/api/core";
import type { Channel, ExternalAiBridge, KnowledgeDocument, MutationReceipt, SystemSnapshot, TimelineEvent } from "./types";
import type {
AgentProposal,
AgentReceipt,
Channel,
ExternalAiBridge,
KnowledgeDocument,
MutationReceipt,
PublicPersona,
RealtimeInvitation,
RuntimeOverview,
SystemSnapshot,
TimelineEvent,
} from "./types";
const tauri = () => "__TAURI_INTERNALS__" in window;
const now = () => new Date().toISOString();
const key = "hololake.clean.browser-state.v1";
const runtimeKey = "hololake.clean.browser-runtime.v1";
type BrowserState = { channel: Channel | null; timeline: TimelineEvent[]; documents: KnowledgeDocument[]; bridges: ExternalAiBridge[] };
const empty: BrowserState = { channel: null, timeline: [], documents: [], bridges: [] };
const load = (): BrowserState => JSON.parse(localStorage.getItem(key) ?? JSON.stringify(empty));
const save = (state: BrowserState) => localStorage.setItem(key, JSON.stringify(state));
const event = (sourceKind: TimelineEvent["sourceKind"], title: string, content: string, state: TimelineEvent["state"] = "SUCCEEDED"): TimelineEvent => ({ eventId: crypto.randomUUID(), sourceKind, title, content, occurredAt: now(), state });
type BrowserState = {
channel: Channel | null;
timeline: TimelineEvent[];
documents: KnowledgeDocument[];
bridges: ExternalAiBridge[];
};
const empty: BrowserState = {
channel: null,
timeline: [],
documents: [],
bridges: [],
};
const load = (): BrowserState =>
JSON.parse(localStorage.getItem(key) ?? JSON.stringify(empty));
const save = (state: BrowserState) =>
localStorage.setItem(key, JSON.stringify(state));
const loadRuntime = (): { personas: PublicPersona[] } =>
JSON.parse(localStorage.getItem(runtimeKey) ?? '{"personas":[]}');
const saveRuntime = (state: { personas: PublicPersona[] }) =>
localStorage.setItem(runtimeKey, JSON.stringify(state));
const event = (
sourceKind: TimelineEvent["sourceKind"],
title: string,
content: string,
state: TimelineEvent["state"] = "SUCCEEDED",
): TimelineEvent => ({
eventId: crypto.randomUUID(),
sourceKind,
title,
content,
occurredAt: now(),
state,
});
export async function snapshot(): Promise<SystemSnapshot> {
if (tauri()) return invoke("system_snapshot");
const s = load();
return { appVersion: "1.0.0-browser", channel: s.channel, timeline: s.timeline, documents: s.documents.map(({ path, title, updatedAt, sha256 }) => ({ path, title, updatedAt, sha256 })), moduleRegistryVersion: "1", publicUpdateEndpoint: "https://guanghulab.com/hololake/releases/latest.json", publicDistributionState: "SIGNED_UPDATE_FEED_READY_NO_RELEASE", externalAiBridges: s.bridges };
return {
appVersion: "1.1.0-browser",
channel: s.channel,
timeline: s.timeline,
documents: s.documents.map(({ path, title, updatedAt, sha256 }) => ({
path,
title,
updatedAt,
sha256,
})),
moduleRegistryVersion: "1",
publicUpdateEndpoint:
"https://guanghulab.com/hololake/releases/latest.json",
publicDistributionState: "SIGNED_UPDATE_FEED_READY_NO_RELEASE",
externalAiBridges: s.bridges,
};
}
export async function createChannel(name: string): Promise<Channel> {
if (tauri()) return invoke("create_channel", { name });
const s = load(); const channel: Channel = { channelId: `HL-CH-${crypto.randomUUID().slice(0,8).toUpperCase()}`, name, createdAt: now(), privateGitPath: "浏览器预览不写入磁盘", publicDistributionState: "SIGNED_UPDATE_FEED_READY_NO_RELEASE" };
s.channel = channel; s.timeline.unshift(event("SYSTEM_RECEIPT", "频道已建立", `${channel.channelId} 已登记。`)); save(s); return channel;
const s = load();
const channel: Channel = {
channelId: `HL-CH-${crypto.randomUUID().slice(0, 8).toUpperCase()}`,
name,
createdAt: now(),
privateGitPath: "浏览器预览不写入磁盘",
publicDistributionState: "SIGNED_UPDATE_FEED_READY_NO_RELEASE",
};
s.channel = channel;
s.timeline.unshift(
event("SYSTEM_RECEIPT", "频道已建立", `${channel.channelId} 已登记。`),
);
save(s);
return channel;
}
export async function submitMessage(content: string): Promise<MutationReceipt> {
if (tauri()) return invoke("submit_user_message", { content });
const s = load(); const e = event("USER_MESSAGE", "用户语言", content, "RECORDED"); s.timeline.unshift(e, event("SYSTEM_RECEIPT", "系统回执", "用户语言已分流保存;等待人格体接入,不伪造人格回应。", "WAITING")); save(s);
return { receiptId: crypto.randomUUID(), state: "WAITING_PERSONA", message: "用户语言已记录", gitCommit: null, event: e };
const s = load();
const e = event("USER_MESSAGE", "用户语言", content, "RECORDED");
s.timeline.unshift(
e,
event(
"SYSTEM_RECEIPT",
"系统回执",
"用户语言已分流保存;等待人格体接入,不伪造人格回应。",
"WAITING",
),
);
save(s);
return {
receiptId: crypto.randomUUID(),
state: "WAITING_PERSONA",
message: "用户语言已记录",
gitCommit: null,
event: e,
};
}
export async function readDocument(path: string): Promise<KnowledgeDocument> {
if (tauri()) return invoke("read_knowledge_document", { path });
const doc = load().documents.find(d => d.path === path); if (!doc) throw new Error("文档不存在"); return doc;
const doc = load().documents.find((d) => d.path === path);
if (!doc) throw new Error("文档不存在");
return doc;
}
export async function createDocument(title: string): Promise<MutationReceipt> {
if (tauri()) return invoke("create_knowledge_document", { title });
const s = load(); const path = `${title.trim().replace(/[\\/:*?\"<>|]/g,"-")}.md`; const d = { path, title: title.trim(), body: `# ${title.trim()}\n\n`, updatedAt: now(), sha256: "browser-preview" }; s.documents.unshift(d); const e=event("AGENT_ACTION","Agent 执行",`已创建 ${path}`); s.timeline.unshift(e); save(s); return {receiptId:crypto.randomUUID(),state:"SUCCEEDED",message:"文档已创建",gitCommit:"browser-preview",event:e};
const s = load();
const path = `${title.trim().replace(/[\\/:*?\"<>|]/g, "-")}.md`;
const d = {
path,
title: title.trim(),
body: `# ${title.trim()}\n\n`,
updatedAt: now(),
sha256: "browser-preview",
};
s.documents.unshift(d);
const e = event("AGENT_ACTION", "Agent 执行", `已创建 ${path}`);
s.timeline.unshift(e);
save(s);
return {
receiptId: crypto.randomUUID(),
state: "SUCCEEDED",
message: "文档已创建",
gitCommit: "browser-preview",
event: e,
};
}
export async function saveDocument(path: string, body: string): Promise<MutationReceipt> {
export async function saveDocument(
path: string,
body: string,
): Promise<MutationReceipt> {
if (tauri()) return invoke("save_knowledge_document", { path, body });
const s=load(); const d=s.documents.find(x=>x.path===path); if(!d) throw new Error("文档不存在"); d.body=body; d.updatedAt=now(); const e=event("SYSTEM_RECEIPT","系统回执",`${path} 已保存到私人 Git 预览层。`); s.timeline.unshift(e); save(s); return {receiptId:crypto.randomUUID(),state:"SUCCEEDED",message:"保存完成",gitCommit:"browser-preview",event:e};
const s = load();
const d = s.documents.find((x) => x.path === path);
if (!d) throw new Error("文档不存在");
d.body = body;
d.updatedAt = now();
const e = event(
"SYSTEM_RECEIPT",
"系统回执",
`${path} 已保存到私人 Git 预览层。`,
);
s.timeline.unshift(e);
save(s);
return {
receiptId: crypto.randomUUID(),
state: "SUCCEEDED",
message: "保存完成",
gitCommit: "browser-preview",
event: e,
};
}
export async function deleteDocument(path: string): Promise<MutationReceipt> {
if (tauri()) return invoke("delete_knowledge_document", { path });
const s=load(); s.documents=s.documents.filter(x=>x.path!==path); const e=event("SYSTEM_RECEIPT","系统回执",`${path} 已移入回收站。`); s.timeline.unshift(e); save(s); return {receiptId:crypto.randomUUID(),state:"SUCCEEDED",message:"已移入回收站",gitCommit:"browser-preview",event:e};
const s = load();
s.documents = s.documents.filter((x) => x.path !== path);
const e = event("SYSTEM_RECEIPT", "系统回执", `${path} 已移入回收站。`);
s.timeline.unshift(e);
save(s);
return {
receiptId: crypto.randomUUID(),
state: "SUCCEEDED",
message: "已移入回收站",
gitCommit: "browser-preview",
event: e,
};
}
export async function importFolder(): Promise<MutationReceipt | null> { if (!tauri()) throw new Error("文件夹导入请在已安装的 HoloLake 中使用"); return invoke("import_knowledge_folder"); }
export async function exportDocument(path: string): Promise<string | null> { if (!tauri()) throw new Error("下载请在已安装的 HoloLake 中使用"); return invoke("export_knowledge_document", { path }); }
export async function registerBridge(displayName: string): Promise<ExternalAiBridge> {
export async function importFolder(): Promise<MutationReceipt | null> {
if (!tauri()) throw new Error("文件夹导入请在已安装的 HoloLake 中使用");
return invoke("import_knowledge_folder");
}
export async function exportDocument(path: string): Promise<string | null> {
if (!tauri()) throw new Error("下载请在已安装的 HoloLake 中使用");
return invoke("export_knowledge_document", { path });
}
export async function registerBridge(
displayName: string,
): Promise<ExternalAiBridge> {
if (tauri()) return invoke("register_external_ai_bridge", { displayName });
const s=load(); const b={bridgeId:`HL-AI-${crypto.randomUUID().slice(0,8).toUpperCase()}`,displayName,inboxPath:"浏览器预览",outboxPath:"浏览器预览",state:"LOCAL_EXPRESSION_ONLY"}; s.bridges.push(b); save(s); return b;
const s = load();
const b = {
bridgeId: `HL-AI-${crypto.randomUUID().slice(0, 8).toUpperCase()}`,
displayName,
inboxPath: "浏览器预览",
outboxPath: "浏览器预览",
state: "LOCAL_EXPRESSION_ONLY",
};
s.bridges.push(b);
save(s);
return b;
}
export async function runtimeOverview(): Promise<RuntimeOverview> {
if (tauri()) return invoke("get_runtime_overview");
const preview = loadRuntime();
return {
realtime: {
schema: "preview",
state: "LISTENING",
protocol: "GLP_LOCAL_REALTIME/1",
endpoint: "tcp://127.0.0.1:39281",
connectedClients: 0,
loopbackOnly: true,
transportGrantsAuthority: false,
},
persona: {
schema: "preview",
state: preview.personas.length
? "TRIAL_PERSONA_READY"
: "READY_NO_PERSONA",
activePersonaId: preview.personas[0]?.personaId ?? null,
personas: preview.personas,
verifiedExistingPersonaCount: 0,
},
proposals: [],
};
}
export async function realtimeInvitation(): Promise<RealtimeInvitation> {
if (tauri()) return invoke("get_realtime_invitation");
return {
schema: "preview",
protocol: "GLP_LOCAL_REALTIME/1",
endpoint: "tcp://127.0.0.1:39281",
token: "browser-preview",
descriptorPath: "browser-preview",
connectorCommand: "仅已安装 App 可生成接入命令",
warning: "令牌不证明人格身份。",
};
}
export async function registerPersona(
displayName: string,
): Promise<PublicPersona> {
if (tauri()) return invoke("register_public_persona", { displayName });
const persona = {
personaId: `HL-PERSONA-${crypto.randomUUID().slice(0, 10).toUpperCase()}`,
displayName,
state: "LOCAL_TRIAL_UNVERIFIED_EXTERNAL_HOST_REQUIRED",
createdAt: now(),
reversibleUntilUnixMs: Date.now() + 30 * 86400000,
};
const preview = loadRuntime();
preview.personas.push(persona);
saveRuntime(preview);
return persona;
}
export async function deleteTrialPersona(
personaId: string,
): Promise<RuntimeOverview["persona"]> {
if (tauri())
return invoke("delete_trial_persona", {
personaId,
exactConfirmation: `删除试用人格 ${personaId}`,
});
const preview = loadRuntime();
preview.personas = preview.personas.filter(
(persona) => persona.personaId !== personaId,
);
saveRuntime(preview);
return {
schema: "preview",
state: preview.personas.length ? "TRIAL_PERSONA_READY" : "READY_NO_PERSONA",
activePersonaId: preview.personas[0]?.personaId ?? null,
personas: preview.personas,
verifiedExistingPersonaCount: 0,
};
}
export async function approveProposal(
proposalId: string,
): Promise<AgentReceipt> {
return invoke("approve_agent_proposal", { proposalId });
}
export async function rejectProposal(
proposalId: string,
): Promise<AgentProposal> {
return invoke("reject_agent_proposal", { proposalId });
}

View file

@ -353,6 +353,9 @@ main {
.conversation article.persona span {
color: #c5a6ff;
}
.conversation article.external-ai span {
color: #72d6ca;
}
.conversation article p {
margin: 0;
line-height: 1.7;
@ -365,6 +368,47 @@ main {
border-radius: 18px;
background: rgba(4, 16, 30, 0.74);
}
.approval-queue {
margin-top: 18px;
padding: 18px 20px;
border: 1px solid rgba(240, 197, 107, 0.38);
border-radius: 18px;
background: rgba(40, 29, 12, 0.36);
}
.approval-queue > h2 {
margin: 0 0 12px;
color: #f4d895;
font-size: 13px;
}
.approval-queue article {
display: flex;
align-items: center;
justify-content: space-between;
gap: 18px;
padding: 12px 13px;
border-radius: 11px;
background: rgba(6, 18, 32, 0.7);
}
.approval-queue article + article {
margin-top: 8px;
}
.approval-queue strong {
font-size: 13px;
}
.approval-queue p {
margin: 5px 0;
color: #b7c8d8;
font-size: 11px;
}
.approval-queue small {
color: #6f879e;
font-size: 9px;
}
.approval-queue article > div:last-child {
display: flex;
flex: none;
gap: 7px;
}
.receipt-stream h2 {
font-size: 13px;
font-weight: 570;

View file

@ -1,9 +1,128 @@
export type SourceKind = "USER_MESSAGE" | "PERSONA_RESPONSE" | "SYSTEM_CONTEXT" | "PROTOCOL_EVENT" | "AGENT_ACTION" | "TOOL_RESULT" | "SYSTEM_RECEIPT";
export type SourceKind =
| "USER_MESSAGE"
| "PERSONA_RESPONSE"
| "EXTERNAL_AI_MESSAGE"
| "SYSTEM_CONTEXT"
| "PROTOCOL_EVENT"
| "AGENT_ACTION"
| "TOOL_RESULT"
| "SYSTEM_RECEIPT";
export interface Channel { channelId: string; name: string; createdAt: string; privateGitPath: string; publicDistributionState: string; }
export interface TimelineEvent { eventId: string; sourceKind: SourceKind; title: string; content: string; occurredAt: string; state: "RECORDED" | "WAITING" | "RUNNING" | "SUCCEEDED" | "FAILED"; }
export interface KnowledgeDocument { path: string; title: string; body: string; updatedAt: string; sha256: string; }
export interface KnowledgeSummary { path: string; title: string; updatedAt: string; sha256: string; }
export interface SystemSnapshot { appVersion: string; channel: Channel | null; timeline: TimelineEvent[]; documents: KnowledgeSummary[]; moduleRegistryVersion: string; publicUpdateEndpoint: string; publicDistributionState: string; externalAiBridges: ExternalAiBridge[]; }
export interface ExternalAiBridge { bridgeId: string; displayName: string; inboxPath: string; outboxPath: string; state: string; }
export interface MutationReceipt { receiptId: string; state: string; message: string; gitCommit: string | null; event: TimelineEvent; }
export interface Channel {
channelId: string;
name: string;
createdAt: string;
privateGitPath: string;
publicDistributionState: string;
}
export interface TimelineEvent {
eventId: string;
sourceKind: SourceKind;
title: string;
content: string;
occurredAt: string;
state: "RECORDED" | "WAITING" | "RUNNING" | "SUCCEEDED" | "FAILED";
}
export interface KnowledgeDocument {
path: string;
title: string;
body: string;
updatedAt: string;
sha256: string;
}
export interface KnowledgeSummary {
path: string;
title: string;
updatedAt: string;
sha256: string;
}
export interface SystemSnapshot {
appVersion: string;
channel: Channel | null;
timeline: TimelineEvent[];
documents: KnowledgeSummary[];
moduleRegistryVersion: string;
publicUpdateEndpoint: string;
publicDistributionState: string;
externalAiBridges: ExternalAiBridge[];
}
export interface ExternalAiBridge {
bridgeId: string;
displayName: string;
inboxPath: string;
outboxPath: string;
state: string;
}
export interface MutationReceipt {
receiptId: string;
state: string;
message: string;
gitCommit: string | null;
event: TimelineEvent;
}
export interface PublicPersona {
personaId: string;
displayName: string;
state: string;
createdAt: string;
reversibleUntilUnixMs: number;
}
export interface GirAction {
actionId: string;
operation: string;
input: Record<string, unknown>;
}
export interface GirProgram {
schema: string;
programId: string;
channelId: string;
sourceSha256: string;
compilerId: string;
unresolvedNaturalLanguage: boolean;
actions: GirAction[];
}
export interface AgentProposal {
proposalId: string;
clientId: string;
personaId: string | null;
state: string;
createdAt: string;
gir: GirProgram;
}
export interface AgentReceipt {
schema: string;
receiptId: string;
proposalId: string;
state: string;
actionResults: unknown[];
gitCommit: string | null;
targetReadbackSha256: string;
}
export interface RuntimeOverview {
realtime: {
schema: string;
state: string;
protocol: string;
endpoint: string;
connectedClients: number;
loopbackOnly: boolean;
transportGrantsAuthority: boolean;
};
persona: {
schema: string;
state: string;
activePersonaId: string | null;
personas: PublicPersona[];
verifiedExistingPersonaCount: number;
};
proposals: AgentProposal[];
}
export interface RealtimeInvitation {
schema: string;
protocol: string;
endpoint: string;
token: string;
descriptorPath: string;
connectorCommand: string;
warning: string;
}