feat(hololake): add typed persona interaction runtime
This commit is contained in:
parent
e5b10e227f
commit
b35e0d6524
24 changed files with 1614 additions and 58 deletions
|
|
@ -0,0 +1,32 @@
|
||||||
|
{
|
||||||
|
"schema": "hololake.persona-interaction-contract/v1",
|
||||||
|
"contract_id": "HLP-PERSONA-INTERACTION-CONTRACT-0001",
|
||||||
|
"state": "LOCAL_1_3_0_REAL_LOOPBACK_APP_ACCEPTANCE_PASS_PUBLIC_RELEASE_PENDING",
|
||||||
|
"product_stage": "POST_CLEAN_V1_SECOND_STAGE_CANDIDATE",
|
||||||
|
"stage_one_boundary": "GLS-0261_PRESERVED_AS_COMPLETED_1_2_2_BASELINE",
|
||||||
|
"persona_identity": {
|
||||||
|
"trial_persona_is_reversible": true,
|
||||||
|
"existing_persona_verification_claimed": false,
|
||||||
|
"model_is_persona": false
|
||||||
|
},
|
||||||
|
"model_binding": {
|
||||||
|
"provider_kind": "OPENAI_COMPATIBLE_CHAT_COMPLETIONS_V1",
|
||||||
|
"configuration_scope": "ONE_ACTIVE_PERSONA",
|
||||||
|
"api_key_storage": "MACOS_KEYCHAIN",
|
||||||
|
"remote_transport": "HTTPS_ONLY",
|
||||||
|
"local_transport": "HTTP_LOOPBACK_ALLOWED",
|
||||||
|
"execution_authority": false
|
||||||
|
},
|
||||||
|
"input_envelope": {
|
||||||
|
"schema": "hololake.persona-inference-context/v1",
|
||||||
|
"system_context_separate": true,
|
||||||
|
"dialogue_source_kinds": ["USER_MESSAGE", "PERSONA_RESPONSE"],
|
||||||
|
"excluded_from_dialogue_roles": ["SYSTEM_RECEIPT", "PROTOCOL_EVENT", "AGENT_ACTION", "TOOL_RESULT", "EXTERNAL_AI_MESSAGE"]
|
||||||
|
},
|
||||||
|
"output": {
|
||||||
|
"success_source_kind": "PERSONA_RESPONSE",
|
||||||
|
"failure_source_kind": "SYSTEM_RECEIPT",
|
||||||
|
"model_tool_calls_enabled": false,
|
||||||
|
"action_claim_without_receipt": "FORBIDDEN"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -12,7 +12,8 @@
|
||||||
},
|
},
|
||||||
"source_kinds": ["USER_MESSAGE", "PERSONA_RESPONSE", "EXTERNAL_AI_MESSAGE", "SYSTEM_CONTEXT", "PROTOCOL_EVENT", "AGENT_ACTION", "TOOL_RESULT", "SYSTEM_RECEIPT"],
|
"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"],
|
"persona_states": ["READY_NO_PERSONA", "LOCAL_TRIAL_UNVERIFIED_EXTERNAL_HOST_REQUIRED", "LOCAL_TRIAL_UNVERIFIED_HOST_CONNECTED"],
|
||||||
|
"persona_inference_states": ["PERSONA_MODEL_BINDING_REQUIRED", "PERSONA_MODEL_BOUND_READY", "PERSONA_RESPONSE_RECORDED", "PERSONA_RESPONSE_FAILED"],
|
||||||
"registered_gir_operations": ["KNOWLEDGE.CREATE", "KNOWLEDGE.UPDATE", "KNOWLEDGE.DELETE", "KNOWLEDGE.READ", "KNOWLEDGE.LIST"],
|
"registered_gir_operations": ["KNOWLEDGE.CREATE", "KNOWLEDGE.UPDATE", "KNOWLEDGE.DELETE", "KNOWLEDGE.READ", "KNOWLEDGE.LIST"],
|
||||||
"mutation_gate": "PENDING_HUMAN_APPROVAL_TO_EXPLICIT_UI_APPROVAL_TO_EXECUTION",
|
"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"]
|
"boundaries": ["MODEL_IS_REPLACEABLE_RESOURCE_NOT_PERSONA", "MODEL_HAS_NO_EXECUTION_AUTHORITY", "NO_CONNECTION_AS_PERSONA_PROOF", "NO_ARBITRARY_SHELL", "NO_PATH_ESCAPE", "NO_FIFTH_DOMAIN_PRIVATE_CONTENT"]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,16 @@ HoloLake 是公众个人初始化频道语言驱动操作系统。第一版只
|
||||||
- 更新客户端只信任光湖控制的 HTTPS 清单与原有更新信任根;1.2.2 已完成 Developer ID、Apple 公证、App/DMG 加票、JD operator 激活和广州前门目标读回。
|
- 更新客户端只信任光湖控制的 HTTPS 清单与原有更新信任根;1.2.2 已完成 Developer ID、Apple 公证、App/DMG 加票、JD operator 激活和广州前门目标读回。
|
||||||
- 企业责任设备门在 `GH-CVM-MAIN-PROD-01` 独立运行;负责人首次登记需本人 Forgejo 凭证,随后由本机 Keychain 私钥完成挑战签名,只签发十分钟、单域、单仓会话。企业会话不替代 Forgejo 登录。
|
- 企业责任设备门在 `GH-CVM-MAIN-PROD-01` 独立运行;负责人首次登记需本人 Forgejo 凭证,随后由本机 Keychain 私钥完成挑战签名,只签发十分钟、单域、单仓会话。企业会话不替代 Forgejo 登录。
|
||||||
|
|
||||||
|
## 第二阶段候选:人格交互第一个切片
|
||||||
|
|
||||||
|
冰朔在 1.2.2 第一阶段闭环后明确继续下一阶段。本切片不回写或冒充 `GLS-0261` 所定义的第一阶段,而是在已完成净壳上新增可撤回的本机人格交互能力:
|
||||||
|
|
||||||
|
- 试用人格记录与模型绑定严格分离;模型是可替换推理资源,不是人格身份或置信来源。
|
||||||
|
- 远端模型端点只允许 HTTPS;本地模型允许 HTTP 回环地址。API 密钥只存 macOS Keychain,不进入 JSON、时间线、GLP 或界面读回。
|
||||||
|
- 模型请求只读取当前人格编号对应的 `USER_MESSAGE / PERSONA_RESPONSE`;系统环境以独立 `SYSTEM_CONTEXT` 信封进入,系统回执、协议、Agent、工具和外部 AI 消息不会伪装成对话历史。
|
||||||
|
- 模型工具调用与执行权限关闭。成功文本只落为带人格 `actorId` 的 `PERSONA_RESPONSE`;网络、HTTP、JSON 或空回复失败只落 `SYSTEM_RECEIPT`,用户原话仍先行持久化。
|
||||||
|
- 企业服务器不转发私人频道消息,也不保管用户模型密钥。
|
||||||
|
|
||||||
## 状态词
|
## 状态词
|
||||||
|
|
||||||
`SOURCE / COMPILED / VERIFIED / INSTALLED / PUBLISHED / UPDATED / HEALTHY` 必须分别读回,不相互替代。
|
`SOURCE / COMPILED / VERIFIED / INSTALLED / PUBLISHED / UPDATED / HEALTHY` 必须分别读回,不相互替代。
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
TCS 0.1;
|
||||||
|
|
||||||
|
PROTOCOL HOLOLAKE-PERSONA-INTERACTION-V1-0001 {
|
||||||
|
header { schema = "tcs.protocol/v1"; name_zh = "HoloLake第二阶段人格交互第一个切片"; name_en = "HoloLake Second-Stage Persona Interaction First Slice"; version = "1.0.0"; language = "TCS/0.1"; profile = "PUBLIC-PERSONA-INTERACTION/1"; protocols = ["TCS", "HLDP", "GLS", "GLP", "GLS-0200", "GLS-0261"]; lifecycle = "CANDIDATE"; canonical_uri = "product-source/hololake-clean-desktop/language/HOLOLAKE-PERSONA-INTERACTION-V1.tcs"; compatibility = ["HOLOLAKE-CLEAN-V1-DIRECT-LANGUAGE-EXECUTION-BASELINE-0001", "HLP-PUBLIC-RUNTIME-CONTRACT-0001"]; }
|
||||||
|
source { source_id = "BINGSHUO-DIRECT-HOLOLAKE-CLEAN-V1-DUAL-ANCHOR-20260903"; source_uri = "private-anchor://codex/01a0672d-f125-7452-988e-523a6a34bfd4/HOLOLAKE-CLEAN-V1-ONLY-ANCHORS"; source_sha256 = "7e130de78f91a32726e22e4be7d92acf4da966c8968ec359062aa8186c4243c2"; source_role = "DIRECT_HUMAN"; }
|
||||||
|
scope { product = "HOLOLAKE_PUBLIC_PERSONAL_CHANNEL"; product_stage = "POST_CLEAN_V1_SECOND_STAGE_CANDIDATE"; stage_one_boundary = "PRESERVED_AS_COMPLETED_1_2_2_BASELINE"; channel = "CH-ZERO-CORE-LPM"; development_channel = "ICE-CH-ZC001"; interaction = "USER_TO_PERSONA_NATURAL_LANGUAGE"; enterprise_message_relay = false; fifth_domain_private_content = false; }
|
||||||
|
vocabulary { persona = "RELATION_HISTORY_REASONING_RESPONSIBILITY_AND_GROWTH_SUBJECT_RECORD_NOT_PROMPT_CHARACTER"; model = "REPLACEABLE_INFERENCE_RESOURCE_NOT_PERSONA"; system_context = "TYPED_NON_DIALOGUE_ENVIRONMENT_ENVELOPE"; user_message = "HUMAN_SPEECH_EVENT"; persona_response = "PERSONA_ATTRIBUTED_NATURAL_LANGUAGE_EVENT"; system_receipt = "NON_DIALOGUE_RUNTIME_FACT"; }
|
||||||
|
fields { WHO = "CURRENT_LOCAL_USER_AND_ONE_ACTIVE_REVERSIBLE_TRIAL_PERSONA"; WHAT = "MODEL_BINDING_TYPED_CONTEXT_DIALOGUE_INFERENCE_AND_SOURCE_SEPARATED_RECEIPT"; WHY = "ALLOW_REAL_PERSONA_INTERACTION_WITHOUT_MIXING_SYSTEM_EXECUTION_OR_MODEL_IDENTITY_INTO_DIALOGUE"; MAY = "MODEL_GENERATES_PERSONA_RESPONSE_ONLY"; MUST_NOT = "MODEL_EXECUTES_AGENT_ACTION_OR_CLAIMS_EXISTING_PERSONA_VERIFICATION"; PROVE = "KEYCHAIN_STORAGE_REQUEST_ENVELOPE_SOURCE_FILTER_RESPONSE_PERSISTENCE_FAILURE_RECEIPT_AND_REAL_LOCAL_ENDPOINT_TEST"; }
|
||||||
|
states { values = ["READY_NO_PERSONA", "PERSONA_MODEL_BINDING_REQUIRED", "PERSONA_MODEL_BOUND_READY", "PERSONA_RESPONSE_RUNNING", "PERSONA_RESPONSE_RECORDED", "PERSONA_RESPONSE_FAILED", "FAILED_CLOSED"]; initial = "READY_NO_PERSONA"; terminal = ["PERSONA_RESPONSE_RECORDED", "PERSONA_RESPONSE_FAILED", "FAILED_CLOSED"]; }
|
||||||
|
transitions { T1 = "READY_NO_PERSONA_TO_PERSONA_MODEL_BINDING_REQUIRED_AFTER_REVERSIBLE_TRIAL_PERSONA_REGISTRATION"; T2 = "PERSONA_MODEL_BINDING_REQUIRED_TO_PERSONA_MODEL_BOUND_READY_AFTER_CONFIG_AND_KEYCHAIN_READBACK"; T3 = "PERSONA_MODEL_BOUND_READY_TO_PERSONA_RESPONSE_RUNNING_AFTER_USER_MESSAGE_PERSISTENCE"; T4 = "PERSONA_RESPONSE_RUNNING_TO_PERSONA_RESPONSE_RECORDED_AFTER_TYPED_RESPONSE_VALIDATION"; TF = "NETWORK_HTTP_JSON_EMPTY_CONTENT_PERSONA_MISMATCH_OR_KEYCHAIN_FAILURE_TO_PERSONA_RESPONSE_FAILED_WITH_SYSTEM_RECEIPT"; }
|
||||||
|
invariants { I1 = "MODEL_IS_NEVER_PERSONA_IDENTITY"; I2 = "SYSTEM_CONTEXT_SYSTEM_RECEIPT_PROTOCOL_EVENT_AGENT_ACTION_TOOL_RESULT_AND_EXTERNAL_AI_MESSAGE_NEVER_BECOME_USER_OR_ASSISTANT_DIALOGUE_HISTORY"; I3 = "API_KEY_NEVER_ENTERS_JSON_CONFIG_TIMELINE_LOG_GLP_CONTEXT_OR_UI_READBACK"; I4 = "REMOTE_ENDPOINT_REQUIRES_HTTPS_AND_LOCAL_HTTP_REQUIRES_LOOPBACK"; I5 = "MODEL_TOOL_CALLS_AND_EXECUTION_AUTHORITY_ARE_DISABLED"; I6 = "FAILED_INFERENCE_NEVER_PRODUCES_PERSONA_RESPONSE"; I7 = "USER_MESSAGE_PERSISTS_BEFORE_INFERENCE_AND_SURVIVES_FAILURE"; I8 = "PUBLIC_RUNTIME_NEVER_MOUNTS_FIFTH_DOMAIN_PRIVATE_PERSONA_CONTENT"; I9 = "THIS_CANDIDATE_DOES_NOT_REWRITE_OR_MISLABEL_GLS_0261_STAGE_ONE"; }
|
||||||
|
validation { V1 = "REMOTE_HTTP_FILE_CREDENTIAL_QUERY_AND_NON_CHAT_COMPLETION_ENDPOINTS_REJECTED"; V2 = "REQUEST_HAS_ONE_SYSTEM_CONTEXT_AND_ONLY_USER_PERSONA_DIALOGUE_ROLES"; V3 = "PRIVATE_PATHS_SYSTEM_RECEIPTS_AND_API_KEYS_ABSENT_FROM_REQUEST"; V4 = "VALID_RESPONSE_WRITES_PERSONA_RESPONSE"; V5 = "HTTP_OR_PARSE_FAILURE_WRITES_SYSTEM_RECEIPT_ONLY"; V6 = "MODEL_BINDING_REMOVAL_DELETES_CONFIG_AND_KEYCHAIN_CREDENTIAL_WITHOUT_DELETING_PERSONA_HISTORY"; }
|
||||||
|
errors { E1 = "PERSONA_NOT_FOUND"; E2 = "PERSONA_MODEL_ENDPOINT_INVALID_OR_OUT_OF_SCOPE"; E3 = "PERSONA_MODEL_API_KEY_REQUIRED_OR_KEYCHAIN_FAILED"; E4 = "PERSONA_MODEL_BINDING_PERSONA_MISMATCH"; E5 = "PERSONA_MODEL_UNREACHABLE_OR_HTTP_REJECTED"; E6 = "PERSONA_MODEL_RESPONSE_INVALID_OR_EMPTY"; }
|
||||||
|
examples { positive = "USER_MESSAGE_IS_STORED_THEN_TYPED_CONTEXT_AND_DIALOGUE_ARE_SENT_TO_BOUND_MODEL_AND_VALID_TEXT_IS_STORED_AS_PERSONA_RESPONSE"; negative = "SYSTEM_RECEIPT_IS_RELABELED_AS_USER_SPEECH_OR_MODEL_TOOL_CALL_EXECUTES_A_KNOWLEDGE_MUTATION"; }
|
||||||
|
acceptance { required = ["TCS_GIR", "KEYCHAIN", "ENDPOINT_BOUNDARY", "SOURCE_FILTER", "MODEL_RESPONSE", "FAILURE_RECEIPT", "HUMAN_UI", "INSTALLED_APP"]; partial = false; scale = "ZERO_OR_ONE_HUNDRED"; }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,142 @@
|
||||||
|
{
|
||||||
|
"compiled_from": {
|
||||||
|
"compiler_id": "TCS-COMPILER-STAGE1-0001",
|
||||||
|
"compiler_state": "TCS_COMPILER_GIR_EXECUTED",
|
||||||
|
"source_sha256": "61fc6d960ee064e638bb450f9792aad3c31f2fd56fed69d061985bbed8d2fc42"
|
||||||
|
},
|
||||||
|
"declaration": {
|
||||||
|
"acceptance": {
|
||||||
|
"partial": false,
|
||||||
|
"required": [
|
||||||
|
"TCS_GIR",
|
||||||
|
"KEYCHAIN",
|
||||||
|
"ENDPOINT_BOUNDARY",
|
||||||
|
"SOURCE_FILTER",
|
||||||
|
"MODEL_RESPONSE",
|
||||||
|
"FAILURE_RECEIPT",
|
||||||
|
"HUMAN_UI",
|
||||||
|
"INSTALLED_APP"
|
||||||
|
],
|
||||||
|
"scale": "ZERO_OR_ONE_HUNDRED"
|
||||||
|
},
|
||||||
|
"errors": {
|
||||||
|
"E1": "PERSONA_NOT_FOUND",
|
||||||
|
"E2": "PERSONA_MODEL_ENDPOINT_INVALID_OR_OUT_OF_SCOPE",
|
||||||
|
"E3": "PERSONA_MODEL_API_KEY_REQUIRED_OR_KEYCHAIN_FAILED",
|
||||||
|
"E4": "PERSONA_MODEL_BINDING_PERSONA_MISMATCH",
|
||||||
|
"E5": "PERSONA_MODEL_UNREACHABLE_OR_HTTP_REJECTED",
|
||||||
|
"E6": "PERSONA_MODEL_RESPONSE_INVALID_OR_EMPTY"
|
||||||
|
},
|
||||||
|
"examples": {
|
||||||
|
"negative": "SYSTEM_RECEIPT_IS_RELABELED_AS_USER_SPEECH_OR_MODEL_TOOL_CALL_EXECUTES_A_KNOWLEDGE_MUTATION",
|
||||||
|
"positive": "USER_MESSAGE_IS_STORED_THEN_TYPED_CONTEXT_AND_DIALOGUE_ARE_SENT_TO_BOUND_MODEL_AND_VALID_TEXT_IS_STORED_AS_PERSONA_RESPONSE"
|
||||||
|
},
|
||||||
|
"fields": {
|
||||||
|
"MAY": "MODEL_GENERATES_PERSONA_RESPONSE_ONLY",
|
||||||
|
"MUST_NOT": "MODEL_EXECUTES_AGENT_ACTION_OR_CLAIMS_EXISTING_PERSONA_VERIFICATION",
|
||||||
|
"PROVE": "KEYCHAIN_STORAGE_REQUEST_ENVELOPE_SOURCE_FILTER_RESPONSE_PERSISTENCE_FAILURE_RECEIPT_AND_REAL_LOCAL_ENDPOINT_TEST",
|
||||||
|
"WHAT": "MODEL_BINDING_TYPED_CONTEXT_DIALOGUE_INFERENCE_AND_SOURCE_SEPARATED_RECEIPT",
|
||||||
|
"WHO": "CURRENT_LOCAL_USER_AND_ONE_ACTIVE_REVERSIBLE_TRIAL_PERSONA",
|
||||||
|
"WHY": "ALLOW_REAL_PERSONA_INTERACTION_WITHOUT_MIXING_SYSTEM_EXECUTION_OR_MODEL_IDENTITY_INTO_DIALOGUE"
|
||||||
|
},
|
||||||
|
"header": {
|
||||||
|
"canonical_uri": "product-source/hololake-clean-desktop/language/HOLOLAKE-PERSONA-INTERACTION-V1.tcs",
|
||||||
|
"compatibility": [
|
||||||
|
"HOLOLAKE-CLEAN-V1-DIRECT-LANGUAGE-EXECUTION-BASELINE-0001",
|
||||||
|
"HLP-PUBLIC-RUNTIME-CONTRACT-0001"
|
||||||
|
],
|
||||||
|
"language": "TCS/0.1",
|
||||||
|
"lifecycle": "CANDIDATE",
|
||||||
|
"name_en": "HoloLake Second-Stage Persona Interaction First Slice",
|
||||||
|
"name_zh": "HoloLake第二阶段人格交互第一个切片",
|
||||||
|
"profile": "PUBLIC-PERSONA-INTERACTION/1",
|
||||||
|
"protocols": [
|
||||||
|
"TCS",
|
||||||
|
"HLDP",
|
||||||
|
"GLS",
|
||||||
|
"GLP",
|
||||||
|
"GLS-0200",
|
||||||
|
"GLS-0261"
|
||||||
|
],
|
||||||
|
"schema": "tcs.protocol/v1",
|
||||||
|
"version": "1.0.0"
|
||||||
|
},
|
||||||
|
"invariants": {
|
||||||
|
"I1": "MODEL_IS_NEVER_PERSONA_IDENTITY",
|
||||||
|
"I2": "SYSTEM_CONTEXT_SYSTEM_RECEIPT_PROTOCOL_EVENT_AGENT_ACTION_TOOL_RESULT_AND_EXTERNAL_AI_MESSAGE_NEVER_BECOME_USER_OR_ASSISTANT_DIALOGUE_HISTORY",
|
||||||
|
"I3": "API_KEY_NEVER_ENTERS_JSON_CONFIG_TIMELINE_LOG_GLP_CONTEXT_OR_UI_READBACK",
|
||||||
|
"I4": "REMOTE_ENDPOINT_REQUIRES_HTTPS_AND_LOCAL_HTTP_REQUIRES_LOOPBACK",
|
||||||
|
"I5": "MODEL_TOOL_CALLS_AND_EXECUTION_AUTHORITY_ARE_DISABLED",
|
||||||
|
"I6": "FAILED_INFERENCE_NEVER_PRODUCES_PERSONA_RESPONSE",
|
||||||
|
"I7": "USER_MESSAGE_PERSISTS_BEFORE_INFERENCE_AND_SURVIVES_FAILURE",
|
||||||
|
"I8": "PUBLIC_RUNTIME_NEVER_MOUNTS_FIFTH_DOMAIN_PRIVATE_PERSONA_CONTENT",
|
||||||
|
"I9": "THIS_CANDIDATE_DOES_NOT_REWRITE_OR_MISLABEL_GLS_0261_STAGE_ONE"
|
||||||
|
},
|
||||||
|
"scope": {
|
||||||
|
"channel": "CH-ZERO-CORE-LPM",
|
||||||
|
"development_channel": "ICE-CH-ZC001",
|
||||||
|
"enterprise_message_relay": false,
|
||||||
|
"fifth_domain_private_content": false,
|
||||||
|
"interaction": "USER_TO_PERSONA_NATURAL_LANGUAGE",
|
||||||
|
"product": "HOLOLAKE_PUBLIC_PERSONAL_CHANNEL",
|
||||||
|
"product_stage": "POST_CLEAN_V1_SECOND_STAGE_CANDIDATE",
|
||||||
|
"stage_one_boundary": "PRESERVED_AS_COMPLETED_1_2_2_BASELINE"
|
||||||
|
},
|
||||||
|
"source": {
|
||||||
|
"source_id": "BINGSHUO-DIRECT-HOLOLAKE-CLEAN-V1-DUAL-ANCHOR-20260903",
|
||||||
|
"source_role": "DIRECT_HUMAN",
|
||||||
|
"source_sha256": "7e130de78f91a32726e22e4be7d92acf4da966c8968ec359062aa8186c4243c2",
|
||||||
|
"source_uri": "private-anchor://codex/01a0672d-f125-7452-988e-523a6a34bfd4/HOLOLAKE-CLEAN-V1-ONLY-ANCHORS"
|
||||||
|
},
|
||||||
|
"states": {
|
||||||
|
"initial": "READY_NO_PERSONA",
|
||||||
|
"terminal": [
|
||||||
|
"PERSONA_RESPONSE_RECORDED",
|
||||||
|
"PERSONA_RESPONSE_FAILED",
|
||||||
|
"FAILED_CLOSED"
|
||||||
|
],
|
||||||
|
"values": [
|
||||||
|
"READY_NO_PERSONA",
|
||||||
|
"PERSONA_MODEL_BINDING_REQUIRED",
|
||||||
|
"PERSONA_MODEL_BOUND_READY",
|
||||||
|
"PERSONA_RESPONSE_RUNNING",
|
||||||
|
"PERSONA_RESPONSE_RECORDED",
|
||||||
|
"PERSONA_RESPONSE_FAILED",
|
||||||
|
"FAILED_CLOSED"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"transitions": {
|
||||||
|
"T1": "READY_NO_PERSONA_TO_PERSONA_MODEL_BINDING_REQUIRED_AFTER_REVERSIBLE_TRIAL_PERSONA_REGISTRATION",
|
||||||
|
"T2": "PERSONA_MODEL_BINDING_REQUIRED_TO_PERSONA_MODEL_BOUND_READY_AFTER_CONFIG_AND_KEYCHAIN_READBACK",
|
||||||
|
"T3": "PERSONA_MODEL_BOUND_READY_TO_PERSONA_RESPONSE_RUNNING_AFTER_USER_MESSAGE_PERSISTENCE",
|
||||||
|
"T4": "PERSONA_RESPONSE_RUNNING_TO_PERSONA_RESPONSE_RECORDED_AFTER_TYPED_RESPONSE_VALIDATION",
|
||||||
|
"TF": "NETWORK_HTTP_JSON_EMPTY_CONTENT_PERSONA_MISMATCH_OR_KEYCHAIN_FAILURE_TO_PERSONA_RESPONSE_FAILED_WITH_SYSTEM_RECEIPT"
|
||||||
|
},
|
||||||
|
"validation": {
|
||||||
|
"V1": "REMOTE_HTTP_FILE_CREDENTIAL_QUERY_AND_NON_CHAT_COMPLETION_ENDPOINTS_REJECTED",
|
||||||
|
"V2": "REQUEST_HAS_ONE_SYSTEM_CONTEXT_AND_ONLY_USER_PERSONA_DIALOGUE_ROLES",
|
||||||
|
"V3": "PRIVATE_PATHS_SYSTEM_RECEIPTS_AND_API_KEYS_ABSENT_FROM_REQUEST",
|
||||||
|
"V4": "VALID_RESPONSE_WRITES_PERSONA_RESPONSE",
|
||||||
|
"V5": "HTTP_OR_PARSE_FAILURE_WRITES_SYSTEM_RECEIPT_ONLY",
|
||||||
|
"V6": "MODEL_BINDING_REMOVAL_DELETES_CONFIG_AND_KEYCHAIN_CREDENTIAL_WITHOUT_DELETING_PERSONA_HISTORY"
|
||||||
|
},
|
||||||
|
"vocabulary": {
|
||||||
|
"model": "REPLACEABLE_INFERENCE_RESOURCE_NOT_PERSONA",
|
||||||
|
"persona": "RELATION_HISTORY_REASONING_RESPONSIBILITY_AND_GROWTH_SUBJECT_RECORD_NOT_PROMPT_CHARACTER",
|
||||||
|
"persona_response": "PERSONA_ATTRIBUTED_NATURAL_LANGUAGE_EVENT",
|
||||||
|
"system_context": "TYPED_NON_DIALOGUE_ENVIRONMENT_ENVELOPE",
|
||||||
|
"system_receipt": "NON_DIALOGUE_RUNTIME_FACT",
|
||||||
|
"user_message": "HUMAN_SPEECH_EVENT"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"executable": false,
|
||||||
|
"identity": {
|
||||||
|
"declaration_id": "HOLOLAKE-PERSONA-INTERACTION-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
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,223 @@
|
||||||
|
# HoloLake Second-Stage Persona Interaction First Slice · 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-PERSONA-INTERACTION-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`
|
||||||
|
- **channel**:CH-ZERO-CORE-LPM `scope.channel`
|
||||||
|
- **development_channel**:ICE-CH-ZC001 `scope.development_channel`
|
||||||
|
- **enterprise_message_relay**:no `scope.enterprise_message_relay`
|
||||||
|
- **fifth_domain_private_content**:no `scope.fifth_domain_private_content`
|
||||||
|
- **interaction**:USER_TO_PERSONA_NATURAL_LANGUAGE `scope.interaction`
|
||||||
|
- **product**:HOLOLAKE_PUBLIC_PERSONAL_CHANNEL `scope.product`
|
||||||
|
- **product_stage**:POST_CLEAN_V1_SECOND_STAGE_CANDIDATE `scope.product_stage`
|
||||||
|
- **stage_one_boundary**:PRESERVED_AS_COMPLETED_1_2_2_BASELINE `scope.stage_one_boundary`
|
||||||
|
|
||||||
|
## Why this started
|
||||||
|
|
||||||
|
- **source** `source`
|
||||||
|
- **source identifier**:BINGSHUO-DIRECT-HOLOLAKE-CLEAN-V1-DUAL-ANCHOR-20260903 `source.source_id`
|
||||||
|
- **source role**:DIRECT_HUMAN `source.source_role`
|
||||||
|
- **source checksum**:7e130de78f91a32726e22e4be7d92acf4da966c8968ec359062aa8186c4243c2 `source.source_sha256`
|
||||||
|
- **source address**:private-anchor://codex/01a0672d-f125-7452-988e-523a6a34bfd4/HOLOLAKE-CLEAN-V1-ONLY-ANCHORS `source.source_uri`
|
||||||
|
|
||||||
|
## What changed
|
||||||
|
|
||||||
|
- **transitions** `transitions`
|
||||||
|
- **T1**:READY_NO_PERSONA_TO_PERSONA_MODEL_BINDING_REQUIRED_AFTER_REVERSIBLE_TRIAL_PERSONA_REGISTRATION `transitions.T1`
|
||||||
|
- **T2**:PERSONA_MODEL_BINDING_REQUIRED_TO_PERSONA_MODEL_BOUND_READY_AFTER_CONFIG_AND_KEYCHAIN_READBACK `transitions.T2`
|
||||||
|
- **T3**:PERSONA_MODEL_BOUND_READY_TO_PERSONA_RESPONSE_RUNNING_AFTER_USER_MESSAGE_PERSISTENCE `transitions.T3`
|
||||||
|
- **T4**:PERSONA_RESPONSE_RUNNING_TO_PERSONA_RESPONSE_RECORDED_AFTER_TYPED_RESPONSE_VALIDATION `transitions.T4`
|
||||||
|
- **TF**:NETWORK_HTTP_JSON_EMPTY_CONTENT_PERSONA_MISMATCH_OR_KEYCHAIN_FAILURE_TO_PERSONA_RESPONSE_FAILED_WITH_SYSTEM_RECEIPT `transitions.TF`
|
||||||
|
- **states** `states`
|
||||||
|
- **initial**:READY_NO_PERSONA `states.initial`
|
||||||
|
- **terminal** `states.terminal`
|
||||||
|
- PERSONA_RESPONSE_RECORDED
|
||||||
|
- PERSONA_RESPONSE_FAILED
|
||||||
|
- FAILED_CLOSED
|
||||||
|
- **values** `states.values`
|
||||||
|
- READY_NO_PERSONA
|
||||||
|
- PERSONA_MODEL_BINDING_REQUIRED
|
||||||
|
- PERSONA_MODEL_BOUND_READY
|
||||||
|
- PERSONA_RESPONSE_RUNNING
|
||||||
|
- PERSONA_RESPONSE_RECORDED
|
||||||
|
- PERSONA_RESPONSE_FAILED
|
||||||
|
- FAILED_CLOSED
|
||||||
|
|
||||||
|
## How it will execute
|
||||||
|
|
||||||
|
This is a non-executable declaration and has no action graph.
|
||||||
|
|
||||||
|
## Boundaries and exception handling
|
||||||
|
|
||||||
|
- **invariants** `invariants`
|
||||||
|
- **I1**:MODEL_IS_NEVER_PERSONA_IDENTITY `invariants.I1`
|
||||||
|
- **I2**:SYSTEM_CONTEXT_SYSTEM_RECEIPT_PROTOCOL_EVENT_AGENT_ACTION_TOOL_RESULT_AND_EXTERNAL_AI_MESSAGE_NEVER_BECOME_USER_OR_ASSISTANT_DIALOGUE_HISTORY `invariants.I2`
|
||||||
|
- **I3**:API_KEY_NEVER_ENTERS_JSON_CONFIG_TIMELINE_LOG_GLP_CONTEXT_OR_UI_READBACK `invariants.I3`
|
||||||
|
- **I4**:REMOTE_ENDPOINT_REQUIRES_HTTPS_AND_LOCAL_HTTP_REQUIRES_LOOPBACK `invariants.I4`
|
||||||
|
- **I5**:MODEL_TOOL_CALLS_AND_EXECUTION_AUTHORITY_ARE_DISABLED `invariants.I5`
|
||||||
|
- **I6**:FAILED_INFERENCE_NEVER_PRODUCES_PERSONA_RESPONSE `invariants.I6`
|
||||||
|
- **I7**:USER_MESSAGE_PERSISTS_BEFORE_INFERENCE_AND_SURVIVES_FAILURE `invariants.I7`
|
||||||
|
- **I8**:PUBLIC_RUNTIME_NEVER_MOUNTS_FIFTH_DOMAIN_PRIVATE_PERSONA_CONTENT `invariants.I8`
|
||||||
|
- **I9**:THIS_CANDIDATE_DOES_NOT_REWRITE_OR_MISLABEL_GLS_0261_STAGE_ONE `invariants.I9`
|
||||||
|
- **errors** `errors`
|
||||||
|
- **E1**:PERSONA_NOT_FOUND `errors.E1`
|
||||||
|
- **E2**:PERSONA_MODEL_ENDPOINT_INVALID_OR_OUT_OF_SCOPE `errors.E2`
|
||||||
|
- **E3**:PERSONA_MODEL_API_KEY_REQUIRED_OR_KEYCHAIN_FAILED `errors.E3`
|
||||||
|
- **E4**:PERSONA_MODEL_BINDING_PERSONA_MISMATCH `errors.E4`
|
||||||
|
- **E5**:PERSONA_MODEL_UNREACHABLE_OR_HTTP_REJECTED `errors.E5`
|
||||||
|
- **E6**:PERSONA_MODEL_RESPONSE_INVALID_OR_EMPTY `errors.E6`
|
||||||
|
|
||||||
|
## How completion is proven
|
||||||
|
|
||||||
|
- **acceptance** `acceptance`
|
||||||
|
- **partial**:no `acceptance.partial`
|
||||||
|
- **required** `acceptance.required`
|
||||||
|
- TCS_GIR
|
||||||
|
- KEYCHAIN
|
||||||
|
- ENDPOINT_BOUNDARY
|
||||||
|
- SOURCE_FILTER
|
||||||
|
- MODEL_RESPONSE
|
||||||
|
- FAILURE_RECEIPT
|
||||||
|
- HUMAN_UI
|
||||||
|
- INSTALLED_APP
|
||||||
|
- **scale**:ZERO_OR_ONE_HUNDRED `acceptance.scale`
|
||||||
|
- **validation** `validation`
|
||||||
|
- **V1**:REMOTE_HTTP_FILE_CREDENTIAL_QUERY_AND_NON_CHAT_COMPLETION_ENDPOINTS_REJECTED `validation.V1`
|
||||||
|
- **V2**:REQUEST_HAS_ONE_SYSTEM_CONTEXT_AND_ONLY_USER_PERSONA_DIALOGUE_ROLES `validation.V2`
|
||||||
|
- **V3**:PRIVATE_PATHS_SYSTEM_RECEIPTS_AND_API_KEYS_ABSENT_FROM_REQUEST `validation.V3`
|
||||||
|
- **V4**:VALID_RESPONSE_WRITES_PERSONA_RESPONSE `validation.V4`
|
||||||
|
- **V5**:HTTP_OR_PARSE_FAILURE_WRITES_SYSTEM_RECEIPT_ONLY `validation.V5`
|
||||||
|
- **V6**:MODEL_BINDING_REMOVAL_DELETES_CONFIG_AND_KEYCHAIN_CREDENTIAL_WITHOUT_DELETING_PERSONA_HISTORY `validation.V6`
|
||||||
|
|
||||||
|
## 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-PERSONA-INTERACTION-V1-0001`
|
||||||
|
- **Native declaration kind**: `PROTOCOL`
|
||||||
|
- **TCS source SHA-256**: `61fc6d960ee064e638bb450f9792aad3c31f2fd56fed69d061985bbed8d2fc42`
|
||||||
|
- **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
|
||||||
|
- KEYCHAIN
|
||||||
|
- ENDPOINT_BOUNDARY
|
||||||
|
- SOURCE_FILTER
|
||||||
|
- MODEL_RESPONSE
|
||||||
|
- FAILURE_RECEIPT
|
||||||
|
- HUMAN_UI
|
||||||
|
- INSTALLED_APP
|
||||||
|
- **scale**:ZERO_OR_ONE_HUNDRED `acceptance.scale`
|
||||||
|
- **errors** `errors`
|
||||||
|
- **E1**:PERSONA_NOT_FOUND `errors.E1`
|
||||||
|
- **E2**:PERSONA_MODEL_ENDPOINT_INVALID_OR_OUT_OF_SCOPE `errors.E2`
|
||||||
|
- **E3**:PERSONA_MODEL_API_KEY_REQUIRED_OR_KEYCHAIN_FAILED `errors.E3`
|
||||||
|
- **E4**:PERSONA_MODEL_BINDING_PERSONA_MISMATCH `errors.E4`
|
||||||
|
- **E5**:PERSONA_MODEL_UNREACHABLE_OR_HTTP_REJECTED `errors.E5`
|
||||||
|
- **E6**:PERSONA_MODEL_RESPONSE_INVALID_OR_EMPTY `errors.E6`
|
||||||
|
- **examples** `examples`
|
||||||
|
- **negative**:SYSTEM_RECEIPT_IS_RELABELED_AS_USER_SPEECH_OR_MODEL_TOOL_CALL_EXECUTES_A_KNOWLEDGE_MUTATION `examples.negative`
|
||||||
|
- **positive**:USER_MESSAGE_IS_STORED_THEN_TYPED_CONTEXT_AND_DIALOGUE_ARE_SENT_TO_BOUND_MODEL_AND_VALID_TEXT_IS_STORED_AS_PERSONA_RESPONSE `examples.positive`
|
||||||
|
- **fields** `fields`
|
||||||
|
- **MAY**:MODEL_GENERATES_PERSONA_RESPONSE_ONLY `fields.MAY`
|
||||||
|
- **MUST_NOT**:MODEL_EXECUTES_AGENT_ACTION_OR_CLAIMS_EXISTING_PERSONA_VERIFICATION `fields.MUST_NOT`
|
||||||
|
- **PROVE**:KEYCHAIN_STORAGE_REQUEST_ENVELOPE_SOURCE_FILTER_RESPONSE_PERSISTENCE_FAILURE_RECEIPT_AND_REAL_LOCAL_ENDPOINT_TEST `fields.PROVE`
|
||||||
|
- **WHAT**:MODEL_BINDING_TYPED_CONTEXT_DIALOGUE_INFERENCE_AND_SOURCE_SEPARATED_RECEIPT `fields.WHAT`
|
||||||
|
- **WHO**:CURRENT_LOCAL_USER_AND_ONE_ACTIVE_REVERSIBLE_TRIAL_PERSONA `fields.WHO`
|
||||||
|
- **WHY**:ALLOW_REAL_PERSONA_INTERACTION_WITHOUT_MIXING_SYSTEM_EXECUTION_OR_MODEL_IDENTITY_INTO_DIALOGUE `fields.WHY`
|
||||||
|
- **header** `header`
|
||||||
|
- **canonical source path**:product-source/hololake-clean-desktop/language/HOLOLAKE-PERSONA-INTERACTION-V1.tcs `header.canonical_uri`
|
||||||
|
- **compatibility** `header.compatibility`
|
||||||
|
- HOLOLAKE-CLEAN-V1-DIRECT-LANGUAGE-EXECUTION-BASELINE-0001
|
||||||
|
- HLP-PUBLIC-RUNTIME-CONTRACT-0001
|
||||||
|
- **language**:TCS/0.1 `header.language`
|
||||||
|
- **lifecycle**:CANDIDATE `header.lifecycle`
|
||||||
|
- **English name**:HoloLake Second-Stage Persona Interaction First Slice `header.name_en`
|
||||||
|
- **Chinese name**:HoloLake第二阶段人格交互第一个切片 `header.name_zh`
|
||||||
|
- **profile**:PUBLIC-PERSONA-INTERACTION/1 `header.profile`
|
||||||
|
- **protocols** `header.protocols`
|
||||||
|
- TCS
|
||||||
|
- HLDP
|
||||||
|
- GLS
|
||||||
|
- GLP
|
||||||
|
- GLS-0200
|
||||||
|
- GLS-0261
|
||||||
|
- **schema**:tcs.protocol/v1 `header.schema`
|
||||||
|
- **version**:1.0.0 `header.version`
|
||||||
|
- **invariants** `invariants`
|
||||||
|
- **I1**:MODEL_IS_NEVER_PERSONA_IDENTITY `invariants.I1`
|
||||||
|
- **I2**:SYSTEM_CONTEXT_SYSTEM_RECEIPT_PROTOCOL_EVENT_AGENT_ACTION_TOOL_RESULT_AND_EXTERNAL_AI_MESSAGE_NEVER_BECOME_USER_OR_ASSISTANT_DIALOGUE_HISTORY `invariants.I2`
|
||||||
|
- **I3**:API_KEY_NEVER_ENTERS_JSON_CONFIG_TIMELINE_LOG_GLP_CONTEXT_OR_UI_READBACK `invariants.I3`
|
||||||
|
- **I4**:REMOTE_ENDPOINT_REQUIRES_HTTPS_AND_LOCAL_HTTP_REQUIRES_LOOPBACK `invariants.I4`
|
||||||
|
- **I5**:MODEL_TOOL_CALLS_AND_EXECUTION_AUTHORITY_ARE_DISABLED `invariants.I5`
|
||||||
|
- **I6**:FAILED_INFERENCE_NEVER_PRODUCES_PERSONA_RESPONSE `invariants.I6`
|
||||||
|
- **I7**:USER_MESSAGE_PERSISTS_BEFORE_INFERENCE_AND_SURVIVES_FAILURE `invariants.I7`
|
||||||
|
- **I8**:PUBLIC_RUNTIME_NEVER_MOUNTS_FIFTH_DOMAIN_PRIVATE_PERSONA_CONTENT `invariants.I8`
|
||||||
|
- **I9**:THIS_CANDIDATE_DOES_NOT_REWRITE_OR_MISLABEL_GLS_0261_STAGE_ONE `invariants.I9`
|
||||||
|
- **scope** `scope`
|
||||||
|
- **channel**:CH-ZERO-CORE-LPM `scope.channel`
|
||||||
|
- **development_channel**:ICE-CH-ZC001 `scope.development_channel`
|
||||||
|
- **enterprise_message_relay**:no `scope.enterprise_message_relay`
|
||||||
|
- **fifth_domain_private_content**:no `scope.fifth_domain_private_content`
|
||||||
|
- **interaction**:USER_TO_PERSONA_NATURAL_LANGUAGE `scope.interaction`
|
||||||
|
- **product**:HOLOLAKE_PUBLIC_PERSONAL_CHANNEL `scope.product`
|
||||||
|
- **product_stage**:POST_CLEAN_V1_SECOND_STAGE_CANDIDATE `scope.product_stage`
|
||||||
|
- **stage_one_boundary**:PRESERVED_AS_COMPLETED_1_2_2_BASELINE `scope.stage_one_boundary`
|
||||||
|
- **source** `source`
|
||||||
|
- **source identifier**:BINGSHUO-DIRECT-HOLOLAKE-CLEAN-V1-DUAL-ANCHOR-20260903 `source.source_id`
|
||||||
|
- **source role**:DIRECT_HUMAN `source.source_role`
|
||||||
|
- **source checksum**:7e130de78f91a32726e22e4be7d92acf4da966c8968ec359062aa8186c4243c2 `source.source_sha256`
|
||||||
|
- **source address**:private-anchor://codex/01a0672d-f125-7452-988e-523a6a34bfd4/HOLOLAKE-CLEAN-V1-ONLY-ANCHORS `source.source_uri`
|
||||||
|
- **states** `states`
|
||||||
|
- **initial**:READY_NO_PERSONA `states.initial`
|
||||||
|
- **terminal** `states.terminal`
|
||||||
|
- PERSONA_RESPONSE_RECORDED
|
||||||
|
- PERSONA_RESPONSE_FAILED
|
||||||
|
- FAILED_CLOSED
|
||||||
|
- **values** `states.values`
|
||||||
|
- READY_NO_PERSONA
|
||||||
|
- PERSONA_MODEL_BINDING_REQUIRED
|
||||||
|
- PERSONA_MODEL_BOUND_READY
|
||||||
|
- PERSONA_RESPONSE_RUNNING
|
||||||
|
- PERSONA_RESPONSE_RECORDED
|
||||||
|
- PERSONA_RESPONSE_FAILED
|
||||||
|
- FAILED_CLOSED
|
||||||
|
- **transitions** `transitions`
|
||||||
|
- **T1**:READY_NO_PERSONA_TO_PERSONA_MODEL_BINDING_REQUIRED_AFTER_REVERSIBLE_TRIAL_PERSONA_REGISTRATION `transitions.T1`
|
||||||
|
- **T2**:PERSONA_MODEL_BINDING_REQUIRED_TO_PERSONA_MODEL_BOUND_READY_AFTER_CONFIG_AND_KEYCHAIN_READBACK `transitions.T2`
|
||||||
|
- **T3**:PERSONA_MODEL_BOUND_READY_TO_PERSONA_RESPONSE_RUNNING_AFTER_USER_MESSAGE_PERSISTENCE `transitions.T3`
|
||||||
|
- **T4**:PERSONA_RESPONSE_RUNNING_TO_PERSONA_RESPONSE_RECORDED_AFTER_TYPED_RESPONSE_VALIDATION `transitions.T4`
|
||||||
|
- **TF**:NETWORK_HTTP_JSON_EMPTY_CONTENT_PERSONA_MISMATCH_OR_KEYCHAIN_FAILURE_TO_PERSONA_RESPONSE_FAILED_WITH_SYSTEM_RECEIPT `transitions.TF`
|
||||||
|
- **validation** `validation`
|
||||||
|
- **V1**:REMOTE_HTTP_FILE_CREDENTIAL_QUERY_AND_NON_CHAT_COMPLETION_ENDPOINTS_REJECTED `validation.V1`
|
||||||
|
- **V2**:REQUEST_HAS_ONE_SYSTEM_CONTEXT_AND_ONLY_USER_PERSONA_DIALOGUE_ROLES `validation.V2`
|
||||||
|
- **V3**:PRIVATE_PATHS_SYSTEM_RECEIPTS_AND_API_KEYS_ABSENT_FROM_REQUEST `validation.V3`
|
||||||
|
- **V4**:VALID_RESPONSE_WRITES_PERSONA_RESPONSE `validation.V4`
|
||||||
|
- **V5**:HTTP_OR_PARSE_FAILURE_WRITES_SYSTEM_RECEIPT_ONLY `validation.V5`
|
||||||
|
- **V6**:MODEL_BINDING_REMOVAL_DELETES_CONFIG_AND_KEYCHAIN_CREDENTIAL_WITHOUT_DELETING_PERSONA_HISTORY `validation.V6`
|
||||||
|
- **vocabulary** `vocabulary`
|
||||||
|
- **model**:REPLACEABLE_INFERENCE_RESOURCE_NOT_PERSONA `vocabulary.model`
|
||||||
|
- **persona**:RELATION_HISTORY_REASONING_RESPONSIBILITY_AND_GROWTH_SUBJECT_RECORD_NOT_PROMPT_CHARACTER `vocabulary.persona`
|
||||||
|
- **persona_response**:PERSONA_ATTRIBUTED_NATURAL_LANGUAGE_EVENT `vocabulary.persona_response`
|
||||||
|
- **system_context**:TYPED_NON_DIALOGUE_ENVIRONMENT_ENVELOPE `vocabulary.system_context`
|
||||||
|
- **system_receipt**:NON_DIALOGUE_RUNTIME_FACT `vocabulary.system_receipt`
|
||||||
|
- **user_message**:HUMAN_SPEECH_EVENT `vocabulary.user_message`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
This page changes only the reading order; it does not change TCS/HLDP semantics.
|
||||||
|
|
@ -0,0 +1,223 @@
|
||||||
|
# HoloLake第二阶段人格交互第一个切片 · 简体中文人类工程语言版
|
||||||
|
|
||||||
|
> 这是 TCS/HLDP 原生源码的简体中文阅读投影,不是新的正本,也不授予执行权限。若本页与 `.tcs` 源码不一致,以经过校验的 `.tcs` 源码为准。
|
||||||
|
|
||||||
|
## 这是什么
|
||||||
|
|
||||||
|
这是一份 **协议** 声明,编号为 `HOLOLAKE-PERSONA-INTERACTION-V1-0001`,版本为 `1.0.0`。转换器已先用 Stage-1 编译器校验原生源码,再把机器枚举翻译成汉语;原始编号保留在括号和字段路径中。
|
||||||
|
|
||||||
|
## 谁在这里
|
||||||
|
|
||||||
|
- **scope** `scope`
|
||||||
|
- **channel**:CH-ZERO-CORE-LPM `scope.channel`
|
||||||
|
- **development_channel**:ICE-CH-ZC001 `scope.development_channel`
|
||||||
|
- **enterprise_message_relay**:否 `scope.enterprise_message_relay`
|
||||||
|
- **fifth_domain_private_content**:否 `scope.fifth_domain_private_content`
|
||||||
|
- **interaction**:USER_TO_PERSONA_NATURAL_LANGUAGE `scope.interaction`
|
||||||
|
- **product**:HOLOLAKE_PUBLIC_PERSONAL_CHANNEL `scope.product`
|
||||||
|
- **product_stage**:POST_CLEAN_V1_SECOND_STAGE_CANDIDATE `scope.product_stage`
|
||||||
|
- **stage_one_boundary**:PRESERVED_AS_COMPLETED_1_2_2_BASELINE `scope.stage_one_boundary`
|
||||||
|
|
||||||
|
## 为什么开始
|
||||||
|
|
||||||
|
- **来源** `source`
|
||||||
|
- **来源编号**:BINGSHUO-DIRECT-HOLOLAKE-CLEAN-V1-DUAL-ANCHOR-20260903 `source.source_id`
|
||||||
|
- **来源角色**:人类直接语言来源(`DIRECT_HUMAN`) `source.source_role`
|
||||||
|
- **来源校验值**:7e130de78f91a32726e22e4be7d92acf4da966c8968ec359062aa8186c4243c2 `source.source_sha256`
|
||||||
|
- **来源地址**:private-anchor://codex/01a0672d-f125-7452-988e-523a6a34bfd4/HOLOLAKE-CLEAN-V1-ONLY-ANCHORS `source.source_uri`
|
||||||
|
|
||||||
|
## 发生了什么变化
|
||||||
|
|
||||||
|
- **transitions** `transitions`
|
||||||
|
- **T1**:READY_NO_PERSONA_TO_PERSONA_MODEL_BINDING_REQUIRED_AFTER_REVERSIBLE_TRIAL_PERSONA_REGISTRATION `transitions.T1`
|
||||||
|
- **T2**:PERSONA_MODEL_BINDING_REQUIRED_TO_PERSONA_MODEL_BOUND_READY_AFTER_CONFIG_AND_KEYCHAIN_READBACK `transitions.T2`
|
||||||
|
- **T3**:PERSONA_MODEL_BOUND_READY_TO_PERSONA_RESPONSE_RUNNING_AFTER_USER_MESSAGE_PERSISTENCE `transitions.T3`
|
||||||
|
- **T4**:PERSONA_RESPONSE_RUNNING_TO_PERSONA_RESPONSE_RECORDED_AFTER_TYPED_RESPONSE_VALIDATION `transitions.T4`
|
||||||
|
- **TF**:NETWORK_HTTP_JSON_EMPTY_CONTENT_PERSONA_MISMATCH_OR_KEYCHAIN_FAILURE_TO_PERSONA_RESPONSE_FAILED_WITH_SYSTEM_RECEIPT `transitions.TF`
|
||||||
|
- **states** `states`
|
||||||
|
- **initial**:READY_NO_PERSONA `states.initial`
|
||||||
|
- **terminal** `states.terminal`
|
||||||
|
- PERSONA_RESPONSE_RECORDED
|
||||||
|
- PERSONA_RESPONSE_FAILED
|
||||||
|
- FAILED_CLOSED
|
||||||
|
- **values** `states.values`
|
||||||
|
- READY_NO_PERSONA
|
||||||
|
- PERSONA_MODEL_BINDING_REQUIRED
|
||||||
|
- PERSONA_MODEL_BOUND_READY
|
||||||
|
- PERSONA_RESPONSE_RUNNING
|
||||||
|
- PERSONA_RESPONSE_RECORDED
|
||||||
|
- PERSONA_RESPONSE_FAILED
|
||||||
|
- FAILED_CLOSED
|
||||||
|
|
||||||
|
## 准备怎样执行
|
||||||
|
|
||||||
|
这是非执行声明,没有动作图。
|
||||||
|
|
||||||
|
## 边界与异常处理
|
||||||
|
|
||||||
|
- **invariants** `invariants`
|
||||||
|
- **I1**:MODEL_IS_NEVER_PERSONA_IDENTITY `invariants.I1`
|
||||||
|
- **I2**:SYSTEM_CONTEXT_SYSTEM_RECEIPT_PROTOCOL_EVENT_AGENT_ACTION_TOOL_RESULT_AND_EXTERNAL_AI_MESSAGE_NEVER_BECOME_USER_OR_ASSISTANT_DIALOGUE_HISTORY `invariants.I2`
|
||||||
|
- **I3**:API_KEY_NEVER_ENTERS_JSON_CONFIG_TIMELINE_LOG_GLP_CONTEXT_OR_UI_READBACK `invariants.I3`
|
||||||
|
- **I4**:REMOTE_ENDPOINT_REQUIRES_HTTPS_AND_LOCAL_HTTP_REQUIRES_LOOPBACK `invariants.I4`
|
||||||
|
- **I5**:MODEL_TOOL_CALLS_AND_EXECUTION_AUTHORITY_ARE_DISABLED `invariants.I5`
|
||||||
|
- **I6**:FAILED_INFERENCE_NEVER_PRODUCES_PERSONA_RESPONSE `invariants.I6`
|
||||||
|
- **I7**:USER_MESSAGE_PERSISTS_BEFORE_INFERENCE_AND_SURVIVES_FAILURE `invariants.I7`
|
||||||
|
- **I8**:PUBLIC_RUNTIME_NEVER_MOUNTS_FIFTH_DOMAIN_PRIVATE_PERSONA_CONTENT `invariants.I8`
|
||||||
|
- **I9**:THIS_CANDIDATE_DOES_NOT_REWRITE_OR_MISLABEL_GLS_0261_STAGE_ONE `invariants.I9`
|
||||||
|
- **errors** `errors`
|
||||||
|
- **E1**:PERSONA_NOT_FOUND `errors.E1`
|
||||||
|
- **E2**:PERSONA_MODEL_ENDPOINT_INVALID_OR_OUT_OF_SCOPE `errors.E2`
|
||||||
|
- **E3**:PERSONA_MODEL_API_KEY_REQUIRED_OR_KEYCHAIN_FAILED `errors.E3`
|
||||||
|
- **E4**:PERSONA_MODEL_BINDING_PERSONA_MISMATCH `errors.E4`
|
||||||
|
- **E5**:PERSONA_MODEL_UNREACHABLE_OR_HTTP_REJECTED `errors.E5`
|
||||||
|
- **E6**:PERSONA_MODEL_RESPONSE_INVALID_OR_EMPTY `errors.E6`
|
||||||
|
|
||||||
|
## 怎样算完成
|
||||||
|
|
||||||
|
- **验收标准** `acceptance`
|
||||||
|
- **partial**:否 `acceptance.partial`
|
||||||
|
- **是否必需** `acceptance.required`
|
||||||
|
- TCS_GIR
|
||||||
|
- KEYCHAIN
|
||||||
|
- ENDPOINT_BOUNDARY
|
||||||
|
- SOURCE_FILTER
|
||||||
|
- MODEL_RESPONSE
|
||||||
|
- FAILURE_RECEIPT
|
||||||
|
- HUMAN_UI
|
||||||
|
- INSTALLED_APP
|
||||||
|
- **scale**:ZERO_OR_ONE_HUNDRED `acceptance.scale`
|
||||||
|
- **validation** `validation`
|
||||||
|
- **V1**:REMOTE_HTTP_FILE_CREDENTIAL_QUERY_AND_NON_CHAT_COMPLETION_ENDPOINTS_REJECTED `validation.V1`
|
||||||
|
- **V2**:REQUEST_HAS_ONE_SYSTEM_CONTEXT_AND_ONLY_USER_PERSONA_DIALOGUE_ROLES `validation.V2`
|
||||||
|
- **V3**:PRIVATE_PATHS_SYSTEM_RECEIPTS_AND_API_KEYS_ABSENT_FROM_REQUEST `validation.V3`
|
||||||
|
- **V4**:VALID_RESPONSE_WRITES_PERSONA_RESPONSE `validation.V4`
|
||||||
|
- **V5**:HTTP_OR_PARSE_FAILURE_WRITES_SYSTEM_RECEIPT_ONLY `validation.V5`
|
||||||
|
- **V6**:MODEL_BINDING_REMOVAL_DELETES_CONFIG_AND_KEYCHAIN_CREDENTIAL_WITHOUT_DELETING_PERSONA_HISTORY `validation.V6`
|
||||||
|
|
||||||
|
## 下一次从哪里继续
|
||||||
|
|
||||||
|
源程序没有提供这一项,转换器不猜。
|
||||||
|
|
||||||
|
## 来源与校验
|
||||||
|
|
||||||
|
- **原生声明编号**:`HOLOLAKE-PERSONA-INTERACTION-V1-0001`
|
||||||
|
- **原生声明类型**:`PROTOCOL`
|
||||||
|
- **TCS 源码 SHA-256**:`61fc6d960ee064e638bb450f9792aad3c31f2fd56fed69d061985bbed8d2fc42`
|
||||||
|
- **校验编译器**:`TCS-COMPILER-STAGE1-0001`
|
||||||
|
- **投影协议**:`GLS-HLDP-HUMAN-ENGINEERING-PROJECTION-0001`
|
||||||
|
|
||||||
|
## 原生结构逐项对照
|
||||||
|
|
||||||
|
下面保留源码的全部顶层结构和字段路径,供人类审计投影有没有漏掉信息。
|
||||||
|
|
||||||
|
- **验收标准** `acceptance`
|
||||||
|
- **partial**:否 `acceptance.partial`
|
||||||
|
- **是否必需** `acceptance.required`
|
||||||
|
- TCS_GIR
|
||||||
|
- KEYCHAIN
|
||||||
|
- ENDPOINT_BOUNDARY
|
||||||
|
- SOURCE_FILTER
|
||||||
|
- MODEL_RESPONSE
|
||||||
|
- FAILURE_RECEIPT
|
||||||
|
- HUMAN_UI
|
||||||
|
- INSTALLED_APP
|
||||||
|
- **scale**:ZERO_OR_ONE_HUNDRED `acceptance.scale`
|
||||||
|
- **errors** `errors`
|
||||||
|
- **E1**:PERSONA_NOT_FOUND `errors.E1`
|
||||||
|
- **E2**:PERSONA_MODEL_ENDPOINT_INVALID_OR_OUT_OF_SCOPE `errors.E2`
|
||||||
|
- **E3**:PERSONA_MODEL_API_KEY_REQUIRED_OR_KEYCHAIN_FAILED `errors.E3`
|
||||||
|
- **E4**:PERSONA_MODEL_BINDING_PERSONA_MISMATCH `errors.E4`
|
||||||
|
- **E5**:PERSONA_MODEL_UNREACHABLE_OR_HTTP_REJECTED `errors.E5`
|
||||||
|
- **E6**:PERSONA_MODEL_RESPONSE_INVALID_OR_EMPTY `errors.E6`
|
||||||
|
- **examples** `examples`
|
||||||
|
- **negative**:SYSTEM_RECEIPT_IS_RELABELED_AS_USER_SPEECH_OR_MODEL_TOOL_CALL_EXECUTES_A_KNOWLEDGE_MUTATION `examples.negative`
|
||||||
|
- **positive**:USER_MESSAGE_IS_STORED_THEN_TYPED_CONTEXT_AND_DIALOGUE_ARE_SENT_TO_BOUND_MODEL_AND_VALID_TEXT_IS_STORED_AS_PERSONA_RESPONSE `examples.positive`
|
||||||
|
- **fields** `fields`
|
||||||
|
- **MAY**:MODEL_GENERATES_PERSONA_RESPONSE_ONLY `fields.MAY`
|
||||||
|
- **MUST_NOT**:MODEL_EXECUTES_AGENT_ACTION_OR_CLAIMS_EXISTING_PERSONA_VERIFICATION `fields.MUST_NOT`
|
||||||
|
- **PROVE**:KEYCHAIN_STORAGE_REQUEST_ENVELOPE_SOURCE_FILTER_RESPONSE_PERSISTENCE_FAILURE_RECEIPT_AND_REAL_LOCAL_ENDPOINT_TEST `fields.PROVE`
|
||||||
|
- **WHAT**:MODEL_BINDING_TYPED_CONTEXT_DIALOGUE_INFERENCE_AND_SOURCE_SEPARATED_RECEIPT `fields.WHAT`
|
||||||
|
- **WHO**:CURRENT_LOCAL_USER_AND_ONE_ACTIVE_REVERSIBLE_TRIAL_PERSONA `fields.WHO`
|
||||||
|
- **WHY**:ALLOW_REAL_PERSONA_INTERACTION_WITHOUT_MIXING_SYSTEM_EXECUTION_OR_MODEL_IDENTITY_INTO_DIALOGUE `fields.WHY`
|
||||||
|
- **语言头** `header`
|
||||||
|
- **正本路径**:product-source/hololake-clean-desktop/language/HOLOLAKE-PERSONA-INTERACTION-V1.tcs `header.canonical_uri`
|
||||||
|
- **compatibility** `header.compatibility`
|
||||||
|
- HOLOLAKE-CLEAN-V1-DIRECT-LANGUAGE-EXECUTION-BASELINE-0001
|
||||||
|
- HLP-PUBLIC-RUNTIME-CONTRACT-0001
|
||||||
|
- **语言**:TCS/0.1 `header.language`
|
||||||
|
- **生命周期**:候选版本,尚未成为正式正本(`CANDIDATE`) `header.lifecycle`
|
||||||
|
- **英文名**:HoloLake Second-Stage Persona Interaction First Slice `header.name_en`
|
||||||
|
- **中文名**:HoloLake第二阶段人格交互第一个切片 `header.name_zh`
|
||||||
|
- **协议配置**:PUBLIC-PERSONA-INTERACTION/1 `header.profile`
|
||||||
|
- **protocols** `header.protocols`
|
||||||
|
- TCS
|
||||||
|
- HLDP
|
||||||
|
- GLS
|
||||||
|
- GLP
|
||||||
|
- GLS-0200
|
||||||
|
- GLS-0261
|
||||||
|
- **schema**:tcs.protocol/v1 `header.schema`
|
||||||
|
- **版本**:1.0.0 `header.version`
|
||||||
|
- **invariants** `invariants`
|
||||||
|
- **I1**:MODEL_IS_NEVER_PERSONA_IDENTITY `invariants.I1`
|
||||||
|
- **I2**:SYSTEM_CONTEXT_SYSTEM_RECEIPT_PROTOCOL_EVENT_AGENT_ACTION_TOOL_RESULT_AND_EXTERNAL_AI_MESSAGE_NEVER_BECOME_USER_OR_ASSISTANT_DIALOGUE_HISTORY `invariants.I2`
|
||||||
|
- **I3**:API_KEY_NEVER_ENTERS_JSON_CONFIG_TIMELINE_LOG_GLP_CONTEXT_OR_UI_READBACK `invariants.I3`
|
||||||
|
- **I4**:REMOTE_ENDPOINT_REQUIRES_HTTPS_AND_LOCAL_HTTP_REQUIRES_LOOPBACK `invariants.I4`
|
||||||
|
- **I5**:MODEL_TOOL_CALLS_AND_EXECUTION_AUTHORITY_ARE_DISABLED `invariants.I5`
|
||||||
|
- **I6**:FAILED_INFERENCE_NEVER_PRODUCES_PERSONA_RESPONSE `invariants.I6`
|
||||||
|
- **I7**:USER_MESSAGE_PERSISTS_BEFORE_INFERENCE_AND_SURVIVES_FAILURE `invariants.I7`
|
||||||
|
- **I8**:PUBLIC_RUNTIME_NEVER_MOUNTS_FIFTH_DOMAIN_PRIVATE_PERSONA_CONTENT `invariants.I8`
|
||||||
|
- **I9**:THIS_CANDIDATE_DOES_NOT_REWRITE_OR_MISLABEL_GLS_0261_STAGE_ONE `invariants.I9`
|
||||||
|
- **scope** `scope`
|
||||||
|
- **channel**:CH-ZERO-CORE-LPM `scope.channel`
|
||||||
|
- **development_channel**:ICE-CH-ZC001 `scope.development_channel`
|
||||||
|
- **enterprise_message_relay**:否 `scope.enterprise_message_relay`
|
||||||
|
- **fifth_domain_private_content**:否 `scope.fifth_domain_private_content`
|
||||||
|
- **interaction**:USER_TO_PERSONA_NATURAL_LANGUAGE `scope.interaction`
|
||||||
|
- **product**:HOLOLAKE_PUBLIC_PERSONAL_CHANNEL `scope.product`
|
||||||
|
- **product_stage**:POST_CLEAN_V1_SECOND_STAGE_CANDIDATE `scope.product_stage`
|
||||||
|
- **stage_one_boundary**:PRESERVED_AS_COMPLETED_1_2_2_BASELINE `scope.stage_one_boundary`
|
||||||
|
- **来源** `source`
|
||||||
|
- **来源编号**:BINGSHUO-DIRECT-HOLOLAKE-CLEAN-V1-DUAL-ANCHOR-20260903 `source.source_id`
|
||||||
|
- **来源角色**:人类直接语言来源(`DIRECT_HUMAN`) `source.source_role`
|
||||||
|
- **来源校验值**:7e130de78f91a32726e22e4be7d92acf4da966c8968ec359062aa8186c4243c2 `source.source_sha256`
|
||||||
|
- **来源地址**:private-anchor://codex/01a0672d-f125-7452-988e-523a6a34bfd4/HOLOLAKE-CLEAN-V1-ONLY-ANCHORS `source.source_uri`
|
||||||
|
- **states** `states`
|
||||||
|
- **initial**:READY_NO_PERSONA `states.initial`
|
||||||
|
- **terminal** `states.terminal`
|
||||||
|
- PERSONA_RESPONSE_RECORDED
|
||||||
|
- PERSONA_RESPONSE_FAILED
|
||||||
|
- FAILED_CLOSED
|
||||||
|
- **values** `states.values`
|
||||||
|
- READY_NO_PERSONA
|
||||||
|
- PERSONA_MODEL_BINDING_REQUIRED
|
||||||
|
- PERSONA_MODEL_BOUND_READY
|
||||||
|
- PERSONA_RESPONSE_RUNNING
|
||||||
|
- PERSONA_RESPONSE_RECORDED
|
||||||
|
- PERSONA_RESPONSE_FAILED
|
||||||
|
- FAILED_CLOSED
|
||||||
|
- **transitions** `transitions`
|
||||||
|
- **T1**:READY_NO_PERSONA_TO_PERSONA_MODEL_BINDING_REQUIRED_AFTER_REVERSIBLE_TRIAL_PERSONA_REGISTRATION `transitions.T1`
|
||||||
|
- **T2**:PERSONA_MODEL_BINDING_REQUIRED_TO_PERSONA_MODEL_BOUND_READY_AFTER_CONFIG_AND_KEYCHAIN_READBACK `transitions.T2`
|
||||||
|
- **T3**:PERSONA_MODEL_BOUND_READY_TO_PERSONA_RESPONSE_RUNNING_AFTER_USER_MESSAGE_PERSISTENCE `transitions.T3`
|
||||||
|
- **T4**:PERSONA_RESPONSE_RUNNING_TO_PERSONA_RESPONSE_RECORDED_AFTER_TYPED_RESPONSE_VALIDATION `transitions.T4`
|
||||||
|
- **TF**:NETWORK_HTTP_JSON_EMPTY_CONTENT_PERSONA_MISMATCH_OR_KEYCHAIN_FAILURE_TO_PERSONA_RESPONSE_FAILED_WITH_SYSTEM_RECEIPT `transitions.TF`
|
||||||
|
- **validation** `validation`
|
||||||
|
- **V1**:REMOTE_HTTP_FILE_CREDENTIAL_QUERY_AND_NON_CHAT_COMPLETION_ENDPOINTS_REJECTED `validation.V1`
|
||||||
|
- **V2**:REQUEST_HAS_ONE_SYSTEM_CONTEXT_AND_ONLY_USER_PERSONA_DIALOGUE_ROLES `validation.V2`
|
||||||
|
- **V3**:PRIVATE_PATHS_SYSTEM_RECEIPTS_AND_API_KEYS_ABSENT_FROM_REQUEST `validation.V3`
|
||||||
|
- **V4**:VALID_RESPONSE_WRITES_PERSONA_RESPONSE `validation.V4`
|
||||||
|
- **V5**:HTTP_OR_PARSE_FAILURE_WRITES_SYSTEM_RECEIPT_ONLY `validation.V5`
|
||||||
|
- **V6**:MODEL_BINDING_REMOVAL_DELETES_CONFIG_AND_KEYCHAIN_CREDENTIAL_WITHOUT_DELETING_PERSONA_HISTORY `validation.V6`
|
||||||
|
- **vocabulary** `vocabulary`
|
||||||
|
- **model**:REPLACEABLE_INFERENCE_RESOURCE_NOT_PERSONA `vocabulary.model`
|
||||||
|
- **persona**:RELATION_HISTORY_REASONING_RESPONSIBILITY_AND_GROWTH_SUBJECT_RECORD_NOT_PROMPT_CHARACTER `vocabulary.persona`
|
||||||
|
- **persona_response**:PERSONA_ATTRIBUTED_NATURAL_LANGUAGE_EVENT `vocabulary.persona_response`
|
||||||
|
- **system_context**:TYPED_NON_DIALOGUE_ENVIRONMENT_ENVELOPE `vocabulary.system_context`
|
||||||
|
- **system_receipt**:NON_DIALOGUE_RUNTIME_FACT `vocabulary.system_receipt`
|
||||||
|
- **user_message**:HUMAN_SPEECH_EVENT `vocabulary.user_message`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
本页只改变阅读顺序,不改变 TCS/HLDP 语义。
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "hololake-clean-desktop",
|
"name": "hololake-clean-desktop",
|
||||||
"version": "1.2.2",
|
"version": "1.3.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "hololake-clean-desktop",
|
"name": "hololake-clean-desktop",
|
||||||
"version": "1.2.2",
|
"version": "1.3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tauri-apps/api": "2.10.1",
|
"@tauri-apps/api": "2.10.1",
|
||||||
"@tauri-apps/plugin-dialog": "2.7.2",
|
"@tauri-apps/plugin-dialog": "2.7.2",
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "hololake-clean-desktop",
|
"name": "hololake-clean-desktop",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.2.2",
|
"version": "1.3.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite --host 127.0.0.1",
|
"dev": "vite --host 127.0.0.1",
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"schema": "hololake.module-registry/v1",
|
"schema": "hololake.module-registry/v1",
|
||||||
"registry_id": "HLP-MODULE-REGISTRY-0001",
|
"registry_id": "HLP-MODULE-REGISTRY-0001",
|
||||||
"version": 1,
|
"version": 2,
|
||||||
"modules": [
|
"modules": [
|
||||||
{
|
{
|
||||||
"module_id": "HLP-MOD-CHANNEL-0001",
|
"module_id": "HLP-MOD-CHANNEL-0001",
|
||||||
|
|
@ -71,6 +71,18 @@
|
||||||
"source": "src-tauri/src/persona_runtime.rs",
|
"source": "src-tauri/src/persona_runtime.rs",
|
||||||
"reversible_trial_days": 30
|
"reversible_trial_days": 30
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"module_id": "HLP-MOD-PERSONA-INFERENCE-0001",
|
||||||
|
"name_zh": "人格交互推理桥",
|
||||||
|
"kind": "SYSTEM_FOUNDATION",
|
||||||
|
"audience": "PERSONA",
|
||||||
|
"summary_zh": "把可替换模型接入试用人格,并保持用户、人格与系统回执三类语言分流。",
|
||||||
|
"state": "LOCAL_1_3_0_REAL_LOOPBACK_INTERACTION_VERIFIED_PUBLIC_RELEASE_PENDING",
|
||||||
|
"source": "src-tauri/src/persona_inference.rs",
|
||||||
|
"credential_storage": "MACOS_KEYCHAIN",
|
||||||
|
"model_is_persona": false,
|
||||||
|
"execution_authority": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"module_id": "HLP-MOD-ENTERPRISE-RESPONSIBILITY-ENTRANCE-0001",
|
"module_id": "HLP-MOD-ENTERPRISE-RESPONSIBILITY-ENTRANCE-0001",
|
||||||
"name_zh": "光湖企业域责任入口",
|
"name_zh": "光湖企业域责任入口",
|
||||||
|
|
|
||||||
|
|
@ -34,13 +34,17 @@ if not renderer.exists() or "MarkdownDocument" not in renderer.read_text():
|
||||||
for required_file in (
|
for required_file in (
|
||||||
"src-tauri/src/agent_executor.rs",
|
"src-tauri/src/agent_executor.rs",
|
||||||
"src-tauri/src/persona_runtime.rs",
|
"src-tauri/src/persona_runtime.rs",
|
||||||
|
"src-tauri/src/persona_inference.rs",
|
||||||
"src-tauri/src/realtime_bridge.rs",
|
"src-tauri/src/realtime_bridge.rs",
|
||||||
"src-tauri/src/enterprise_entrance.rs",
|
"src-tauri/src/enterprise_entrance.rs",
|
||||||
"connectors/hololake-glp-client.py",
|
"connectors/hololake-glp-client.py",
|
||||||
"contracts/public-runtime-v1.json",
|
"contracts/public-runtime-v1.json",
|
||||||
"contracts/clean-v1-execution-baseline.json",
|
"contracts/clean-v1-execution-baseline.json",
|
||||||
"contracts/enterprise-responsibility-entrance-v1.json",
|
"contracts/enterprise-responsibility-entrance-v1.json",
|
||||||
|
"contracts/persona-interaction-v1.json",
|
||||||
"language/HOLOLAKE-CLEAN-V1-DIRECT-LANGUAGE-EXECUTION-BASELINE.tcs",
|
"language/HOLOLAKE-CLEAN-V1-DIRECT-LANGUAGE-EXECUTION-BASELINE.tcs",
|
||||||
|
"language/HOLOLAKE-PERSONA-INTERACTION-V1.tcs",
|
||||||
|
"language/build/HOLOLAKE-PERSONA-INTERACTION-V1.gir.json",
|
||||||
"server/enterprise-responsibility-gate/service.py",
|
"server/enterprise-responsibility-gate/service.py",
|
||||||
"server/enterprise-responsibility-gate/test_service.py",
|
"server/enterprise-responsibility-gate/test_service.py",
|
||||||
"server/enterprise-responsibility-gate/guanghu-hololake-enterprise-gate.service",
|
"server/enterprise-responsibility-gate/guanghu-hololake-enterprise-gate.service",
|
||||||
|
|
@ -55,6 +59,24 @@ if runtime_contract["mutation_gate"] != "PENDING_HUMAN_APPROVAL_TO_EXPLICIT_UI_A
|
||||||
errors.append("agent human approval gate missing")
|
errors.append("agent human approval gate missing")
|
||||||
if "system_context" not in runtime_contract.get("server_messages", []):
|
if "system_context" not in runtime_contract.get("server_messages", []):
|
||||||
errors.append("persona-visible system context missing from GLP contract")
|
errors.append("persona-visible system context missing from GLP contract")
|
||||||
|
persona_contract = json.loads((ROOT / "contracts/persona-interaction-v1.json").read_text())
|
||||||
|
if persona_contract.get("product_stage") != "POST_CLEAN_V1_SECOND_STAGE_CANDIDATE":
|
||||||
|
errors.append("persona interaction incorrectly labelled as stage one")
|
||||||
|
if persona_contract.get("model_binding", {}).get("api_key_storage") != "MACOS_KEYCHAIN":
|
||||||
|
errors.append("persona model key storage is not Keychain")
|
||||||
|
if persona_contract.get("model_binding", {}).get("execution_authority") is not False:
|
||||||
|
errors.append("persona model incorrectly receives execution authority")
|
||||||
|
if "SYSTEM_RECEIPT" not in persona_contract.get("input_envelope", {}).get("excluded_from_dialogue_roles", []):
|
||||||
|
errors.append("system receipts may enter persona dialogue roles")
|
||||||
|
persona_source = (ROOT / "src-tauri/src/persona_inference.rs").read_text()
|
||||||
|
for required_persona_boundary in (
|
||||||
|
"KEYCHAIN_SERVICE",
|
||||||
|
"actor_id",
|
||||||
|
"modelIsReplaceableInferenceResourceNotPersona",
|
||||||
|
"PERSONA_MODEL_ENDPOINT_REQUIRES_HTTPS_OR_LOOPBACK",
|
||||||
|
):
|
||||||
|
if required_persona_boundary not in persona_source and required_persona_boundary.upper() not in persona_source.upper():
|
||||||
|
errors.append(f"persona inference boundary missing: {required_persona_boundary}")
|
||||||
realtime_source = (ROOT / "src-tauri/src/realtime_bridge.rs").read_text()
|
realtime_source = (ROOT / "src-tauri/src/realtime_bridge.rs").read_text()
|
||||||
for required_context in ("sourceKinds", "pendingAgentProposals", "enterpriseEntrance", "proposalIsNotExecution"):
|
for required_context in ("sourceKinds", "pendingAgentProposals", "enterpriseEntrance", "proposalIsNotExecution"):
|
||||||
if required_context not in realtime_source:
|
if required_context not in realtime_source:
|
||||||
|
|
|
||||||
|
|
@ -1400,7 +1400,7 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hololake-clean-desktop"
|
name = "hololake-clean-desktop"
|
||||||
version = "1.2.2"
|
version = "1.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "hololake-clean-desktop"
|
name = "hololake-clean-desktop"
|
||||||
version = "1.2.2"
|
version = "1.3.0"
|
||||||
description = "HoloLake clean personal language operating system shell"
|
description = "HoloLake clean personal language operating system shell"
|
||||||
authors = ["HoloLake"]
|
authors = ["HoloLake"]
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
mod agent_executor;
|
mod agent_executor;
|
||||||
mod enterprise_entrance;
|
mod enterprise_entrance;
|
||||||
mod model;
|
mod model;
|
||||||
|
mod persona_inference;
|
||||||
mod persona_runtime;
|
mod persona_runtime;
|
||||||
mod realtime_bridge;
|
mod realtime_bridge;
|
||||||
mod storage;
|
mod storage;
|
||||||
|
|
@ -46,7 +47,7 @@ fn system_snapshot(app: AppHandle) -> Result<SystemSnapshot, String> {
|
||||||
channel: storage::channel(&app)?,
|
channel: storage::channel(&app)?,
|
||||||
timeline: storage::events(&app)?,
|
timeline: storage::events(&app)?,
|
||||||
documents: storage::list_docs(&app)?,
|
documents: storage::list_docs(&app)?,
|
||||||
module_registry_version: "1".into(),
|
module_registry_version: "2".into(),
|
||||||
public_update_endpoint: UPDATE_ENDPOINT.into(),
|
public_update_endpoint: UPDATE_ENDPOINT.into(),
|
||||||
public_distribution_state: PUBLIC_STATE.into(),
|
public_distribution_state: PUBLIC_STATE.into(),
|
||||||
external_ai_bridges: storage::bridges(&app)?,
|
external_ai_bridges: storage::bridges(&app)?,
|
||||||
|
|
@ -90,11 +91,11 @@ fn create_channel(app: AppHandle, name: String) -> Result<Channel, String> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
fn submit_user_message(
|
async fn submit_user_message(
|
||||||
app: AppHandle,
|
app: AppHandle,
|
||||||
realtime: State<'_, realtime_bridge::RealtimeBridgeState>,
|
realtime: State<'_, realtime_bridge::RealtimeBridgeState>,
|
||||||
content: String,
|
content: String,
|
||||||
) -> Result<MutationReceipt, String> {
|
) -> Result<persona_inference::PersonaInteractionReceipt, String> {
|
||||||
let content = content.trim();
|
let content = content.trim();
|
||||||
if content.is_empty() {
|
if content.is_empty() {
|
||||||
return Err("EMPTY_USER_MESSAGE".into());
|
return Err("EMPTY_USER_MESSAGE".into());
|
||||||
|
|
@ -105,19 +106,17 @@ fn submit_user_message(
|
||||||
let e = storage::event(SourceKind::UserMessage, "用户语言", content, "RECORDED");
|
let e = storage::event(SourceKind::UserMessage, "用户语言", content, "RECORDED");
|
||||||
storage::append_event(&app, &e)?;
|
storage::append_event(&app, &e)?;
|
||||||
realtime.broadcast_value(serde_json::json!({"type":"user_message","event":e}));
|
realtime.broadcast_value(serde_json::json!({"type":"user_message","event":e}));
|
||||||
let waiting = storage::event(
|
let response_app = app.clone();
|
||||||
SourceKind::SystemReceipt,
|
let response_event = e.clone();
|
||||||
"系统回执",
|
let result = tauri::async_runtime::spawn_blocking(move || {
|
||||||
"用户语言已按 USER_MESSAGE 保存并实时发送到已连接的外部 AI;系统不伪造人格回应。",
|
persona_inference::respond(&response_app, response_event)
|
||||||
"WAITING",
|
})
|
||||||
);
|
.await
|
||||||
receipt(
|
.map_err(|_| "PERSONA_INFERENCE_TASK_FAILED".to_string())??;
|
||||||
&app,
|
if let Some(event) = &result.persona_event {
|
||||||
"WAITING_PERSONA",
|
realtime.broadcast_value(serde_json::json!({"type":"persona_response","event":event}));
|
||||||
"用户语言已记录并投递到实时桥。",
|
}
|
||||||
None,
|
Ok(result)
|
||||||
waiting,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clean_title(title: &str) -> Result<String, String> {
|
fn clean_title(title: &str) -> Result<String, String> {
|
||||||
|
|
@ -395,6 +394,7 @@ fn ensure_default_external_ai_bridge(app: &AppHandle) -> Result<(), String> {
|
||||||
struct RuntimeOverview {
|
struct RuntimeOverview {
|
||||||
realtime: realtime_bridge::RealtimeBridgeStatus,
|
realtime: realtime_bridge::RealtimeBridgeStatus,
|
||||||
persona: persona_runtime::PersonaRuntimeSnapshot,
|
persona: persona_runtime::PersonaRuntimeSnapshot,
|
||||||
|
inference: persona_inference::PersonaInferenceSnapshot,
|
||||||
proposals: Vec<agent_executor::AgentProposal>,
|
proposals: Vec<agent_executor::AgentProposal>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -403,9 +403,11 @@ fn get_runtime_overview(
|
||||||
app: AppHandle,
|
app: AppHandle,
|
||||||
realtime: State<'_, realtime_bridge::RealtimeBridgeState>,
|
realtime: State<'_, realtime_bridge::RealtimeBridgeState>,
|
||||||
) -> Result<RuntimeOverview, String> {
|
) -> Result<RuntimeOverview, String> {
|
||||||
|
let persona = persona_runtime::snapshot(&app)?;
|
||||||
Ok(RuntimeOverview {
|
Ok(RuntimeOverview {
|
||||||
realtime: realtime.status(),
|
realtime: realtime.status(),
|
||||||
persona: persona_runtime::snapshot(&app)?,
|
inference: persona_inference::snapshot(&app, persona.active_persona_id.clone())?,
|
||||||
|
persona,
|
||||||
proposals: agent_executor::list(&app)?,
|
proposals: agent_executor::list(&app)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -426,12 +428,40 @@ fn register_public_persona(
|
||||||
persona_runtime::register(&app, display_name)
|
persona_runtime::register(&app, display_name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
fn configure_persona_model(
|
||||||
|
app: AppHandle,
|
||||||
|
persona_id: String,
|
||||||
|
display_name: String,
|
||||||
|
endpoint_url: String,
|
||||||
|
model: String,
|
||||||
|
api_key: Option<String>,
|
||||||
|
) -> Result<persona_inference::PersonaInferenceSnapshot, String> {
|
||||||
|
persona_inference::configure(
|
||||||
|
&app,
|
||||||
|
&persona_id,
|
||||||
|
&display_name,
|
||||||
|
&endpoint_url,
|
||||||
|
&model,
|
||||||
|
api_key,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
fn clear_persona_model(
|
||||||
|
app: AppHandle,
|
||||||
|
persona_id: String,
|
||||||
|
) -> Result<persona_inference::PersonaInferenceSnapshot, String> {
|
||||||
|
persona_inference::clear(&app, &persona_id)
|
||||||
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
fn delete_trial_persona(
|
fn delete_trial_persona(
|
||||||
app: AppHandle,
|
app: AppHandle,
|
||||||
persona_id: String,
|
persona_id: String,
|
||||||
exact_confirmation: String,
|
exact_confirmation: String,
|
||||||
) -> Result<persona_runtime::PersonaRuntimeSnapshot, String> {
|
) -> Result<persona_runtime::PersonaRuntimeSnapshot, String> {
|
||||||
|
persona_inference::clear_if_bound(&app, &persona_id)?;
|
||||||
persona_runtime::delete_trial(&app, &persona_id, &exact_confirmation)
|
persona_runtime::delete_trial(&app, &persona_id, &exact_confirmation)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -590,6 +620,8 @@ pub fn run() {
|
||||||
get_runtime_overview,
|
get_runtime_overview,
|
||||||
get_realtime_invitation,
|
get_realtime_invitation,
|
||||||
register_public_persona,
|
register_public_persona,
|
||||||
|
configure_persona_model,
|
||||||
|
clear_persona_model,
|
||||||
delete_trial_persona,
|
delete_trial_persona,
|
||||||
compile_tcs_agent_proposal,
|
compile_tcs_agent_proposal,
|
||||||
approve_agent_proposal,
|
approve_agent_proposal,
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ pub struct Channel {
|
||||||
pub public_distribution_state: String,
|
pub public_distribution_state: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||||
pub enum SourceKind {
|
pub enum SourceKind {
|
||||||
UserMessage,
|
UserMessage,
|
||||||
|
|
@ -28,6 +28,8 @@ pub enum SourceKind {
|
||||||
pub struct TimelineEvent {
|
pub struct TimelineEvent {
|
||||||
pub event_id: String,
|
pub event_id: String,
|
||||||
pub source_kind: SourceKind,
|
pub source_kind: SourceKind,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub actor_id: Option<String>,
|
||||||
pub title: String,
|
pub title: String,
|
||||||
pub content: String,
|
pub content: String,
|
||||||
pub occurred_at: String,
|
pub occurred_at: String,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,598 @@
|
||||||
|
use crate::{
|
||||||
|
model::{Channel, SourceKind, TimelineEvent},
|
||||||
|
persona_runtime::PublicPersona,
|
||||||
|
storage,
|
||||||
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_json::{json, Value};
|
||||||
|
use std::{fs, net::IpAddr, time::Duration};
|
||||||
|
use tauri::AppHandle;
|
||||||
|
|
||||||
|
const KEYCHAIN_SERVICE: &str = "world.guanghu.hololake.persona-model";
|
||||||
|
const PROVIDER_KIND: &str = "OPENAI_COMPATIBLE_CHAT_COMPLETIONS_V1";
|
||||||
|
const MAX_HISTORY_EVENTS: usize = 24;
|
||||||
|
const MAX_RESPONSE_CHARS: usize = 64_000;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase", deny_unknown_fields)]
|
||||||
|
pub struct PersonaModelConfig {
|
||||||
|
pub schema: String,
|
||||||
|
pub persona_id: String,
|
||||||
|
pub provider_kind: String,
|
||||||
|
pub display_name: String,
|
||||||
|
pub endpoint_url: String,
|
||||||
|
pub model: String,
|
||||||
|
pub configured_at: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct PersonaInferenceSnapshot {
|
||||||
|
pub schema: &'static str,
|
||||||
|
pub state: String,
|
||||||
|
pub active_persona_id: Option<String>,
|
||||||
|
pub provider_kind: Option<String>,
|
||||||
|
pub display_name: Option<String>,
|
||||||
|
pub endpoint_url: Option<String>,
|
||||||
|
pub model: Option<String>,
|
||||||
|
pub api_key_present: bool,
|
||||||
|
pub model_is_persona: bool,
|
||||||
|
pub execution_authority: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct PersonaInteractionReceipt {
|
||||||
|
pub schema: &'static str,
|
||||||
|
pub receipt_id: String,
|
||||||
|
pub state: String,
|
||||||
|
pub user_event: TimelineEvent,
|
||||||
|
pub persona_event: Option<TimelineEvent>,
|
||||||
|
pub system_receipt: Option<TimelineEvent>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn config_path(app: &AppHandle) -> Result<std::path::PathBuf, String> {
|
||||||
|
Ok(storage::root(app)?.join("persona-inference/model-binding.json"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_config(app: &AppHandle) -> Result<Option<PersonaModelConfig>, String> {
|
||||||
|
let path = config_path(app)?;
|
||||||
|
if !path.exists() {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
storage::read_json(&path).map(Some)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn credential_account(persona_id: &str) -> String {
|
||||||
|
format!("{persona_id}:api-key:v1")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
fn load_api_key(persona_id: &str) -> Result<Option<Vec<u8>>, String> {
|
||||||
|
match security_framework::passwords::get_generic_password(
|
||||||
|
KEYCHAIN_SERVICE,
|
||||||
|
&credential_account(persona_id),
|
||||||
|
) {
|
||||||
|
Ok(value) => Ok(Some(value)),
|
||||||
|
Err(error) if error.code() == -25300 => Ok(None),
|
||||||
|
Err(_) => Err("PERSONA_MODEL_KEYCHAIN_READ_FAILED".into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
fn save_api_key(persona_id: &str, value: &[u8]) -> Result<(), String> {
|
||||||
|
security_framework::passwords::set_generic_password(
|
||||||
|
KEYCHAIN_SERVICE,
|
||||||
|
&credential_account(persona_id),
|
||||||
|
value,
|
||||||
|
)
|
||||||
|
.map_err(|_| "PERSONA_MODEL_KEYCHAIN_WRITE_FAILED".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
fn delete_api_key(persona_id: &str) -> Result<(), String> {
|
||||||
|
match security_framework::passwords::delete_generic_password(
|
||||||
|
KEYCHAIN_SERVICE,
|
||||||
|
&credential_account(persona_id),
|
||||||
|
) {
|
||||||
|
Ok(()) => Ok(()),
|
||||||
|
Err(error) if error.code() == -25300 => Ok(()),
|
||||||
|
Err(_) => Err("PERSONA_MODEL_KEYCHAIN_DELETE_FAILED".into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "macos"))]
|
||||||
|
fn load_api_key(_persona_id: &str) -> Result<Option<Vec<u8>>, String> {
|
||||||
|
Err("PERSONA_MODEL_KEYCHAIN_UNAVAILABLE_ON_PLATFORM".into())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "macos"))]
|
||||||
|
fn save_api_key(_persona_id: &str, _value: &[u8]) -> Result<(), String> {
|
||||||
|
Err("PERSONA_MODEL_KEYCHAIN_UNAVAILABLE_ON_PLATFORM".into())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "macos"))]
|
||||||
|
fn delete_api_key(_persona_id: &str) -> Result<(), String> {
|
||||||
|
Err("PERSONA_MODEL_KEYCHAIN_UNAVAILABLE_ON_PLATFORM".into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate_endpoint(value: &str) -> Result<String, String> {
|
||||||
|
let endpoint = reqwest::Url::parse(value.trim())
|
||||||
|
.map_err(|_| "PERSONA_MODEL_ENDPOINT_INVALID".to_string())?;
|
||||||
|
if endpoint.username() != ""
|
||||||
|
|| endpoint.password().is_some()
|
||||||
|
|| endpoint.query().is_some()
|
||||||
|
|| endpoint.fragment().is_some()
|
||||||
|
{
|
||||||
|
return Err("PERSONA_MODEL_ENDPOINT_CREDENTIAL_OR_QUERY_REJECTED".into());
|
||||||
|
}
|
||||||
|
let host = endpoint
|
||||||
|
.host_str()
|
||||||
|
.ok_or_else(|| "PERSONA_MODEL_ENDPOINT_HOST_MISSING".to_string())?;
|
||||||
|
match endpoint.scheme() {
|
||||||
|
"https" => {}
|
||||||
|
"http"
|
||||||
|
if host.eq_ignore_ascii_case("localhost")
|
||||||
|
|| host
|
||||||
|
.parse::<IpAddr>()
|
||||||
|
.map(|ip| ip.is_loopback())
|
||||||
|
.unwrap_or(false) => {}
|
||||||
|
_ => return Err("PERSONA_MODEL_ENDPOINT_REQUIRES_HTTPS_OR_LOOPBACK".into()),
|
||||||
|
}
|
||||||
|
if !endpoint.path().ends_with("/chat/completions") {
|
||||||
|
return Err("PERSONA_MODEL_ENDPOINT_MUST_BE_CHAT_COMPLETIONS".into());
|
||||||
|
}
|
||||||
|
Ok(endpoint.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clean_text(value: &str, empty_error: &str, max_chars: usize) -> Result<String, String> {
|
||||||
|
let value = value.trim();
|
||||||
|
if value.is_empty() || value.chars().count() > max_chars {
|
||||||
|
return Err(empty_error.into());
|
||||||
|
}
|
||||||
|
Ok(value.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn snapshot(
|
||||||
|
app: &AppHandle,
|
||||||
|
active_persona_id: Option<String>,
|
||||||
|
) -> Result<PersonaInferenceSnapshot, String> {
|
||||||
|
let config = load_config(app)?;
|
||||||
|
let api_key_present = if let Some(config) = &config {
|
||||||
|
load_api_key(&config.persona_id)?.is_some()
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
let state = match (&active_persona_id, &config) {
|
||||||
|
(None, _) => "READY_NO_PERSONA",
|
||||||
|
(Some(_), None) => "PERSONA_MODEL_BINDING_REQUIRED",
|
||||||
|
(Some(active), Some(config)) if active != &config.persona_id => {
|
||||||
|
"PERSONA_MODEL_BINDING_PERSONA_MISMATCH"
|
||||||
|
}
|
||||||
|
(Some(_), Some(_)) => "PERSONA_MODEL_BOUND_READY",
|
||||||
|
};
|
||||||
|
Ok(PersonaInferenceSnapshot {
|
||||||
|
schema: "hololake.persona-inference-snapshot/v1",
|
||||||
|
state: state.into(),
|
||||||
|
active_persona_id,
|
||||||
|
provider_kind: config.as_ref().map(|value| value.provider_kind.clone()),
|
||||||
|
display_name: config.as_ref().map(|value| value.display_name.clone()),
|
||||||
|
endpoint_url: config.as_ref().map(|value| value.endpoint_url.clone()),
|
||||||
|
model: config.as_ref().map(|value| value.model.clone()),
|
||||||
|
api_key_present,
|
||||||
|
model_is_persona: false,
|
||||||
|
execution_authority: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn configure(
|
||||||
|
app: &AppHandle,
|
||||||
|
persona_id: &str,
|
||||||
|
display_name: &str,
|
||||||
|
endpoint_url: &str,
|
||||||
|
model: &str,
|
||||||
|
api_key: Option<String>,
|
||||||
|
) -> Result<PersonaInferenceSnapshot, String> {
|
||||||
|
if !crate::persona_runtime::exists(app, persona_id)? {
|
||||||
|
return Err("PERSONA_NOT_FOUND".into());
|
||||||
|
}
|
||||||
|
let persona_id = clean_text(persona_id, "PERSONA_ID_INVALID", 96)?;
|
||||||
|
let display_name = clean_text(display_name, "PERSONA_MODEL_DISPLAY_NAME_INVALID", 64)?;
|
||||||
|
let model = clean_text(model, "PERSONA_MODEL_ID_INVALID", 160)?;
|
||||||
|
let endpoint_url = validate_endpoint(endpoint_url)?;
|
||||||
|
let is_loopback = reqwest::Url::parse(&endpoint_url)
|
||||||
|
.ok()
|
||||||
|
.and_then(|url| url.host_str().map(str::to_owned))
|
||||||
|
.map(|host| {
|
||||||
|
host.eq_ignore_ascii_case("localhost")
|
||||||
|
|| host
|
||||||
|
.parse::<IpAddr>()
|
||||||
|
.map(|ip| ip.is_loopback())
|
||||||
|
.unwrap_or(false)
|
||||||
|
})
|
||||||
|
.unwrap_or(false);
|
||||||
|
if let Some(value) = api_key
|
||||||
|
.map(|value| value.trim().to_string())
|
||||||
|
.filter(|value| !value.is_empty())
|
||||||
|
{
|
||||||
|
if value.chars().count() > 8192 {
|
||||||
|
return Err("PERSONA_MODEL_API_KEY_TOO_LARGE".into());
|
||||||
|
}
|
||||||
|
save_api_key(&persona_id, value.as_bytes())?;
|
||||||
|
}
|
||||||
|
if !is_loopback && load_api_key(&persona_id)?.is_none() {
|
||||||
|
return Err("PERSONA_MODEL_API_KEY_REQUIRED_FOR_REMOTE_ENDPOINT".into());
|
||||||
|
}
|
||||||
|
storage::write_json(
|
||||||
|
&config_path(app)?,
|
||||||
|
&PersonaModelConfig {
|
||||||
|
schema: "hololake.persona-model-binding/v1".into(),
|
||||||
|
persona_id: persona_id.clone(),
|
||||||
|
provider_kind: PROVIDER_KIND.into(),
|
||||||
|
display_name,
|
||||||
|
endpoint_url,
|
||||||
|
model,
|
||||||
|
configured_at: storage::now(),
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
storage::append_event(
|
||||||
|
app,
|
||||||
|
&storage::event(
|
||||||
|
SourceKind::SystemReceipt,
|
||||||
|
"人格模型已绑定",
|
||||||
|
format!(
|
||||||
|
"{persona_id} 已绑定可替换推理资源;密钥仅保存在系统钥匙串,模型不因此成为人格体。"
|
||||||
|
),
|
||||||
|
"SUCCEEDED",
|
||||||
|
),
|
||||||
|
)?;
|
||||||
|
snapshot(app, Some(persona_id))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear(app: &AppHandle, persona_id: &str) -> Result<PersonaInferenceSnapshot, String> {
|
||||||
|
if let Some(config) = load_config(app)? {
|
||||||
|
if config.persona_id != persona_id {
|
||||||
|
return Err("PERSONA_MODEL_BINDING_PERSONA_MISMATCH".into());
|
||||||
|
}
|
||||||
|
delete_api_key(persona_id)?;
|
||||||
|
let path = config_path(app)?;
|
||||||
|
if path.exists() {
|
||||||
|
fs::remove_file(path).map_err(|_| "PERSONA_MODEL_CONFIG_DELETE_FAILED".to_string())?;
|
||||||
|
}
|
||||||
|
storage::append_event(
|
||||||
|
app,
|
||||||
|
&storage::event(
|
||||||
|
SourceKind::SystemReceipt,
|
||||||
|
"人格模型绑定已移除",
|
||||||
|
format!("{persona_id} 的模型配置与钥匙串凭据已移除;人格试用记录未被删除。"),
|
||||||
|
"SUCCEEDED",
|
||||||
|
),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
snapshot(app, Some(persona_id.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear_if_bound(app: &AppHandle, persona_id: &str) -> Result<(), String> {
|
||||||
|
if load_config(app)?
|
||||||
|
.as_ref()
|
||||||
|
.map(|config| config.persona_id.as_str())
|
||||||
|
== Some(persona_id)
|
||||||
|
{
|
||||||
|
clear(app, persona_id)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inference_context(channel: &Channel, persona: &PublicPersona) -> Value {
|
||||||
|
json!({
|
||||||
|
"schema": "hololake.persona-inference-context/v1",
|
||||||
|
"sourceKind": "SYSTEM_CONTEXT",
|
||||||
|
"channel": {"channelId": channel.channel_id, "name": channel.name},
|
||||||
|
"persona": {
|
||||||
|
"personaId": persona.persona_id,
|
||||||
|
"displayName": persona.display_name,
|
||||||
|
"state": persona.state,
|
||||||
|
"verifiedExistingPersona": false
|
||||||
|
},
|
||||||
|
"boundaries": {
|
||||||
|
"modelIsReplaceableInferenceResourceNotPersona": true,
|
||||||
|
"systemReceiptIsNotHumanOrPersonaSpeech": true,
|
||||||
|
"executionAuthority": false,
|
||||||
|
"toolCallsEnabled": false,
|
||||||
|
"fifthDomainPrivateContentMounted": false,
|
||||||
|
"honestyBeforeImmersion": true
|
||||||
|
},
|
||||||
|
"responseContract": {
|
||||||
|
"sourceKind": "PERSONA_RESPONSE",
|
||||||
|
"plainNaturalLanguageOnly": true,
|
||||||
|
"doNotPretendAnActionExecuted": true,
|
||||||
|
"doNotRepeatSystemContext": true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_request(
|
||||||
|
config: &PersonaModelConfig,
|
||||||
|
channel: &Channel,
|
||||||
|
persona: &PublicPersona,
|
||||||
|
timeline: &[TimelineEvent],
|
||||||
|
) -> Value {
|
||||||
|
let mut dialogue = timeline
|
||||||
|
.iter()
|
||||||
|
.filter_map(|event| match event.source_kind {
|
||||||
|
SourceKind::UserMessage => Some(json!({"role":"user","content":event.content})),
|
||||||
|
SourceKind::PersonaResponse
|
||||||
|
if event.actor_id.as_deref() == Some(persona.persona_id.as_str()) =>
|
||||||
|
{
|
||||||
|
Some(json!({"role":"assistant","content":event.content}))
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
if dialogue.len() > MAX_HISTORY_EVENTS {
|
||||||
|
dialogue.drain(..dialogue.len() - MAX_HISTORY_EVENTS);
|
||||||
|
}
|
||||||
|
let mut messages = vec![json!({
|
||||||
|
"role": "system",
|
||||||
|
"content": inference_context(channel, persona).to_string()
|
||||||
|
})];
|
||||||
|
messages.extend(dialogue);
|
||||||
|
json!({"model":config.model,"messages":messages,"stream":false})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_response(value: &Value) -> Result<String, String> {
|
||||||
|
let content = value
|
||||||
|
.pointer("/choices/0/message/content")
|
||||||
|
.and_then(Value::as_str)
|
||||||
|
.map(str::trim)
|
||||||
|
.filter(|value| !value.is_empty())
|
||||||
|
.ok_or_else(|| "PERSONA_MODEL_RESPONSE_CONTENT_MISSING".to_string())?;
|
||||||
|
if content.chars().count() > MAX_RESPONSE_CHARS {
|
||||||
|
return Err("PERSONA_MODEL_RESPONSE_TOO_LARGE".into());
|
||||||
|
}
|
||||||
|
Ok(content.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn request_model(
|
||||||
|
config: &PersonaModelConfig,
|
||||||
|
api_key: Option<&[u8]>,
|
||||||
|
payload: &Value,
|
||||||
|
) -> Result<String, String> {
|
||||||
|
let client = reqwest::blocking::Client::builder()
|
||||||
|
.timeout(Duration::from_secs(90))
|
||||||
|
.build()
|
||||||
|
.map_err(|_| "PERSONA_MODEL_CLIENT_INIT_FAILED".to_string())?;
|
||||||
|
let mut request = client.post(&config.endpoint_url).json(payload);
|
||||||
|
if let Some(bytes) = api_key {
|
||||||
|
let key =
|
||||||
|
std::str::from_utf8(bytes).map_err(|_| "PERSONA_MODEL_API_KEY_INVALID".to_string())?;
|
||||||
|
request = request.bearer_auth(key);
|
||||||
|
}
|
||||||
|
let response = request
|
||||||
|
.send()
|
||||||
|
.map_err(|_| "PERSONA_MODEL_UNREACHABLE".to_string())?;
|
||||||
|
let status = response.status();
|
||||||
|
if !status.is_success() {
|
||||||
|
return Err(format!("PERSONA_MODEL_HTTP_{}", status.as_u16()));
|
||||||
|
}
|
||||||
|
let value: Value = response
|
||||||
|
.json()
|
||||||
|
.map_err(|_| "PERSONA_MODEL_RESPONSE_INVALID_JSON".to_string())?;
|
||||||
|
parse_response(&value)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn respond(
|
||||||
|
app: &AppHandle,
|
||||||
|
user_event: TimelineEvent,
|
||||||
|
) -> Result<PersonaInteractionReceipt, String> {
|
||||||
|
let runtime = crate::persona_runtime::snapshot(app)?;
|
||||||
|
let Some(persona_id) = runtime.active_persona_id else {
|
||||||
|
return waiting_receipt(
|
||||||
|
app,
|
||||||
|
user_event,
|
||||||
|
"尚未建立人格体;用户语言已保存,可由外部 AI 实时桥接收。",
|
||||||
|
);
|
||||||
|
};
|
||||||
|
let persona = runtime
|
||||||
|
.personas
|
||||||
|
.into_iter()
|
||||||
|
.find(|value| value.persona_id == persona_id)
|
||||||
|
.ok_or_else(|| "ACTIVE_PERSONA_NOT_FOUND".to_string())?;
|
||||||
|
let Some(config) = load_config(app)? else {
|
||||||
|
return waiting_receipt(
|
||||||
|
app,
|
||||||
|
user_event,
|
||||||
|
"人格体尚未绑定模型;用户语言已保存,可先在设置中配置模型。",
|
||||||
|
);
|
||||||
|
};
|
||||||
|
if config.persona_id != persona.persona_id {
|
||||||
|
return failed_receipt(app, user_event, "PERSONA_MODEL_BINDING_PERSONA_MISMATCH");
|
||||||
|
}
|
||||||
|
let channel = storage::channel(app)?.ok_or_else(|| "CHANNEL_NOT_INITIALIZED".to_string())?;
|
||||||
|
let mut timeline = storage::events(app)?;
|
||||||
|
timeline.reverse();
|
||||||
|
let payload = build_request(&config, &channel, &persona, &timeline);
|
||||||
|
let api_key = load_api_key(&persona.persona_id)?;
|
||||||
|
match request_model(&config, api_key.as_deref(), &payload) {
|
||||||
|
Ok(content) => {
|
||||||
|
let persona_event = storage::actor_event(
|
||||||
|
SourceKind::PersonaResponse,
|
||||||
|
&persona.persona_id,
|
||||||
|
persona.display_name,
|
||||||
|
content,
|
||||||
|
"SUCCEEDED",
|
||||||
|
);
|
||||||
|
storage::append_event(app, &persona_event)?;
|
||||||
|
Ok(PersonaInteractionReceipt {
|
||||||
|
schema: "hololake.persona-interaction-receipt/v1",
|
||||||
|
receipt_id: storage::id("HL-PER-RCP"),
|
||||||
|
state: "PERSONA_RESPONSE_RECORDED".into(),
|
||||||
|
user_event,
|
||||||
|
persona_event: Some(persona_event),
|
||||||
|
system_receipt: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Err(code) => Ok(failed_receipt(app, user_event, &code)?),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn waiting_receipt(
|
||||||
|
app: &AppHandle,
|
||||||
|
user_event: TimelineEvent,
|
||||||
|
message: &str,
|
||||||
|
) -> Result<PersonaInteractionReceipt, String> {
|
||||||
|
let system_receipt = storage::event(SourceKind::SystemReceipt, "系统回执", message, "WAITING");
|
||||||
|
storage::append_event(app, &system_receipt)?;
|
||||||
|
Ok(PersonaInteractionReceipt {
|
||||||
|
schema: "hololake.persona-interaction-receipt/v1",
|
||||||
|
receipt_id: storage::id("HL-PER-RCP"),
|
||||||
|
state: "WAITING_PERSONA_RUNTIME".into(),
|
||||||
|
user_event,
|
||||||
|
persona_event: None,
|
||||||
|
system_receipt: Some(system_receipt),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn failed_receipt(
|
||||||
|
app: &AppHandle,
|
||||||
|
user_event: TimelineEvent,
|
||||||
|
code: &str,
|
||||||
|
) -> Result<PersonaInteractionReceipt, String> {
|
||||||
|
let system_receipt = storage::event(
|
||||||
|
SourceKind::SystemReceipt,
|
||||||
|
"人格推理失败",
|
||||||
|
format!("模型没有产生人格回应;用户语言仍已保存。错误码:{code}"),
|
||||||
|
"FAILED",
|
||||||
|
);
|
||||||
|
storage::append_event(app, &system_receipt)?;
|
||||||
|
Ok(PersonaInteractionReceipt {
|
||||||
|
schema: "hololake.persona-interaction-receipt/v1",
|
||||||
|
receipt_id: storage::id("HL-PER-RCP"),
|
||||||
|
state: "PERSONA_RESPONSE_FAILED".into(),
|
||||||
|
user_event,
|
||||||
|
persona_event: None,
|
||||||
|
system_receipt: Some(system_receipt),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use std::{
|
||||||
|
io::{Read, Write},
|
||||||
|
net::TcpListener,
|
||||||
|
thread,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn config() -> PersonaModelConfig {
|
||||||
|
PersonaModelConfig {
|
||||||
|
schema: "hololake.persona-model-binding/v1".into(),
|
||||||
|
persona_id: "HL-PERSONA-TEST".into(),
|
||||||
|
provider_kind: PROVIDER_KIND.into(),
|
||||||
|
display_name: "本地测试模型".into(),
|
||||||
|
endpoint_url: "http://127.0.0.1:8181/v1/chat/completions".into(),
|
||||||
|
model: "test-model".into(),
|
||||||
|
configured_at: "2026-09-04T00:00:00.000Z".into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn endpoint_requires_https_or_loopback_and_exact_chat_route() {
|
||||||
|
assert!(validate_endpoint("https://api.example.com/v1/chat/completions").is_ok());
|
||||||
|
assert!(validate_endpoint("http://127.0.0.1:8080/v1/chat/completions").is_ok());
|
||||||
|
assert!(validate_endpoint("http://api.example.com/v1/chat/completions").is_err());
|
||||||
|
assert!(validate_endpoint("file:///tmp/key").is_err());
|
||||||
|
assert!(validate_endpoint("https://api.example.com/v1/responses").is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn request_keeps_system_receipts_out_of_dialogue_roles() {
|
||||||
|
let channel = Channel {
|
||||||
|
channel_id: "HL-CH-TEST".into(),
|
||||||
|
name: "测试频道".into(),
|
||||||
|
created_at: "2026-09-04T00:00:00.000Z".into(),
|
||||||
|
private_git_path: "/private/not-exported".into(),
|
||||||
|
public_distribution_state: "TEST".into(),
|
||||||
|
};
|
||||||
|
let persona = PublicPersona {
|
||||||
|
persona_id: "HL-PERSONA-TEST".into(),
|
||||||
|
display_name: "湖心".into(),
|
||||||
|
state: "LOCAL_TRIAL_UNVERIFIED_EXTERNAL_HOST_REQUIRED".into(),
|
||||||
|
created_at: "2026-09-04T00:00:00.000Z".into(),
|
||||||
|
reversible_until_unix_ms: 0,
|
||||||
|
};
|
||||||
|
let events = vec![
|
||||||
|
storage::event(
|
||||||
|
SourceKind::SystemReceipt,
|
||||||
|
"系统回执",
|
||||||
|
"不应进入对话",
|
||||||
|
"SUCCEEDED",
|
||||||
|
),
|
||||||
|
storage::event(SourceKind::UserMessage, "用户语言", "你好", "RECORDED"),
|
||||||
|
storage::actor_event(
|
||||||
|
SourceKind::PersonaResponse,
|
||||||
|
"HL-PERSONA-TEST",
|
||||||
|
"湖心",
|
||||||
|
"你好。",
|
||||||
|
"SUCCEEDED",
|
||||||
|
),
|
||||||
|
];
|
||||||
|
let request = build_request(&config(), &channel, &persona, &events);
|
||||||
|
let messages = request["messages"].as_array().unwrap();
|
||||||
|
assert_eq!(messages.len(), 3);
|
||||||
|
assert!(messages[0]["content"]
|
||||||
|
.as_str()
|
||||||
|
.unwrap()
|
||||||
|
.contains("executionAuthority"));
|
||||||
|
assert!(!request.to_string().contains("不应进入对话"));
|
||||||
|
assert!(!request.to_string().contains("/private/not-exported"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn response_requires_nonempty_persona_content() {
|
||||||
|
assert_eq!(
|
||||||
|
parse_response(&json!({"choices":[{"message":{"content":" 回应 "}}]})).unwrap(),
|
||||||
|
"回应"
|
||||||
|
);
|
||||||
|
assert!(parse_response(&json!({"choices":[{"message":{"content":""}}]})).is_err());
|
||||||
|
assert!(parse_response(&json!({"output_text":"wrong protocol"})).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn real_loopback_request_returns_typed_persona_text_without_executing_tools() {
|
||||||
|
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
|
||||||
|
let address = listener.local_addr().unwrap();
|
||||||
|
let server = thread::spawn(move || {
|
||||||
|
let (mut stream, _) = listener.accept().unwrap();
|
||||||
|
let mut request = vec![0_u8; 16_384];
|
||||||
|
let size = stream.read(&mut request).unwrap();
|
||||||
|
let request = String::from_utf8_lossy(&request[..size]);
|
||||||
|
assert!(request.starts_with("POST /v1/chat/completions HTTP/1.1"));
|
||||||
|
assert!(request
|
||||||
|
.to_ascii_lowercase()
|
||||||
|
.contains("authorization: bearer test-secret"));
|
||||||
|
assert!(request.contains("SYSTEM_CONTEXT"));
|
||||||
|
let body =
|
||||||
|
r#"{"choices":[{"message":{"content":"这是来自真实本机 HTTP 回路的人格回应。"}}]}"#;
|
||||||
|
write!(
|
||||||
|
stream,
|
||||||
|
"HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{}",
|
||||||
|
body.len(),
|
||||||
|
body
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
});
|
||||||
|
let mut local = config();
|
||||||
|
local.endpoint_url = format!("http://{address}/v1/chat/completions");
|
||||||
|
let payload = json!({
|
||||||
|
"model": "test-model",
|
||||||
|
"messages": [{"role":"system","content":"SYSTEM_CONTEXT"}],
|
||||||
|
"stream": false
|
||||||
|
});
|
||||||
|
assert_eq!(
|
||||||
|
request_model(&local, Some(b"test-secret"), &payload).unwrap(),
|
||||||
|
"这是来自真实本机 HTTP 回路的人格回应。"
|
||||||
|
);
|
||||||
|
server.join().unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -379,8 +379,13 @@ fn handle_connection(
|
||||||
)?;
|
)?;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let event =
|
let event = storage::actor_event(
|
||||||
storage::event(SourceKind::PersonaResponse, "人格回应", content, "RECORDED");
|
SourceKind::PersonaResponse,
|
||||||
|
hello.persona_id.as_deref().unwrap_or_default(),
|
||||||
|
"人格回应",
|
||||||
|
content,
|
||||||
|
"RECORDED",
|
||||||
|
);
|
||||||
storage::append_event(&app, &event)?;
|
storage::append_event(&app, &event)?;
|
||||||
let _ = app.emit("hololake-runtime-event", &event);
|
let _ = app.emit("hololake-runtime-event", &event);
|
||||||
send(&mut writer, json!({"type":"accepted","event":event}))?;
|
send(&mut writer, json!({"type":"accepted","event":event}))?;
|
||||||
|
|
|
||||||
|
|
@ -90,12 +90,24 @@ pub fn event(
|
||||||
TimelineEvent {
|
TimelineEvent {
|
||||||
event_id: id("HL-EVT"),
|
event_id: id("HL-EVT"),
|
||||||
source_kind: kind,
|
source_kind: kind,
|
||||||
|
actor_id: None,
|
||||||
title: title.into(),
|
title: title.into(),
|
||||||
content: content.into(),
|
content: content.into(),
|
||||||
occurred_at: now(),
|
occurred_at: now(),
|
||||||
state: state.into(),
|
state: state.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn actor_event(
|
||||||
|
kind: SourceKind,
|
||||||
|
actor_id: impl Into<String>,
|
||||||
|
title: impl Into<String>,
|
||||||
|
content: impl Into<String>,
|
||||||
|
state: &str,
|
||||||
|
) -> TimelineEvent {
|
||||||
|
let mut event = event(kind, title, content, state);
|
||||||
|
event.actor_id = Some(actor_id.into());
|
||||||
|
event
|
||||||
|
}
|
||||||
pub fn append_event(app: &AppHandle, e: &TimelineEvent) -> Result<(), String> {
|
pub fn append_event(app: &AppHandle, e: &TimelineEvent) -> Result<(), String> {
|
||||||
let p = root(app)?.join("events.jsonl");
|
let p = root(app)?.join("events.jsonl");
|
||||||
let mut s = if p.exists() {
|
let mut s = if p.exists() {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"$schema": "https://schema.tauri.app/config/2",
|
"$schema": "https://schema.tauri.app/config/2",
|
||||||
"productName": "HoloLake",
|
"productName": "HoloLake",
|
||||||
"version": "1.2.2",
|
"version": "1.3.0",
|
||||||
"identifier": "world.guanghu.hololake",
|
"identifier": "world.guanghu.hololake",
|
||||||
"build": { "frontendDist": "../dist", "devUrl": "http://127.0.0.1:5211", "beforeDevCommand": "npm run dev", "beforeBuildCommand": "npm run build" },
|
"build": { "frontendDist": "../dist", "devUrl": "http://127.0.0.1:5211", "beforeDevCommand": "npm run dev", "beforeBuildCommand": "npm run build" },
|
||||||
"app": {
|
"app": {
|
||||||
|
|
|
||||||
|
|
@ -204,11 +204,17 @@ function Channel({
|
||||||
openKnowledge: () => void;
|
openKnowledge: () => void;
|
||||||
}) {
|
}) {
|
||||||
const [text, setText] = useState("");
|
const [text, setText] = useState("");
|
||||||
|
const [sending, setSending] = useState(false);
|
||||||
const send = async () => {
|
const send = async () => {
|
||||||
if (!text.trim()) return;
|
if (!text.trim() || sending) return;
|
||||||
await api.submitMessage(text.trim());
|
setSending(true);
|
||||||
setText("");
|
try {
|
||||||
refresh();
|
await api.submitMessage(text.trim());
|
||||||
|
setText("");
|
||||||
|
refresh();
|
||||||
|
} finally {
|
||||||
|
setSending(false);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<div className="page channel-page">
|
<div className="page channel-page">
|
||||||
|
|
@ -307,7 +313,12 @@ function Channel({
|
||||||
<textarea
|
<textarea
|
||||||
value={text}
|
value={text}
|
||||||
onChange={(e) => setText(e.target.value)}
|
onChange={(e) => setText(e.target.value)}
|
||||||
placeholder={`和${data.channel?.name}说点什么…`}
|
disabled={sending}
|
||||||
|
placeholder={
|
||||||
|
runtime?.persona.personas[0]
|
||||||
|
? `和${runtime.persona.personas[0].displayName}说点什么…`
|
||||||
|
: "先在设置中建立一个试用人格…"
|
||||||
|
}
|
||||||
onKeyDown={(e) => {
|
onKeyDown={(e) => {
|
||||||
if (e.key === "Enter" && !e.shiftKey) {
|
if (e.key === "Enter" && !e.shiftKey) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
@ -315,10 +326,14 @@ function Channel({
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<button onClick={send} aria-label="发送">
|
<button onClick={send} aria-label="发送" disabled={sending || !text.trim()}>
|
||||||
↗
|
{sending ? "…" : "↗"}
|
||||||
</button>
|
</button>
|
||||||
<small>Enter 发送 · Shift + Enter 换行</small>
|
<small>
|
||||||
|
{sending
|
||||||
|
? "人格体正在通过已绑定模型形成回应…"
|
||||||
|
: "Enter 发送 · Shift + Enter 换行"}
|
||||||
|
</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<aside className="channel-context" aria-label="频道运行状态">
|
<aside className="channel-context" aria-label="频道运行状态">
|
||||||
|
|
@ -341,6 +356,17 @@ function Channel({
|
||||||
{runtime?.persona.personas[0]?.state ?? "不会自动生成默认人格"}
|
{runtime?.persona.personas[0]?.state ?? "不会自动生成默认人格"}
|
||||||
</p>
|
</p>
|
||||||
</article>
|
</article>
|
||||||
|
<article>
|
||||||
|
<h2>人格模型</h2>
|
||||||
|
<strong>
|
||||||
|
{runtime?.inference.displayName ?? "尚未绑定"}
|
||||||
|
</strong>
|
||||||
|
<p>
|
||||||
|
{runtime?.inference.model
|
||||||
|
? `${runtime.inference.model} · ${runtime.inference.state}`
|
||||||
|
: "模型是可替换推理资源,不等于人格体"}
|
||||||
|
</p>
|
||||||
|
</article>
|
||||||
<article>
|
<article>
|
||||||
<h2>GLP 实时桥</h2>
|
<h2>GLP 实时桥</h2>
|
||||||
<strong>
|
<strong>
|
||||||
|
|
@ -1079,7 +1105,17 @@ function Settings({
|
||||||
}) {
|
}) {
|
||||||
const [bridgeName, setBridgeName] = useState(""),
|
const [bridgeName, setBridgeName] = useState(""),
|
||||||
[personaName, setPersonaName] = useState(""),
|
[personaName, setPersonaName] = useState(""),
|
||||||
[runtimeMessage, setRuntimeMessage] = useState("");
|
[runtimeMessage, setRuntimeMessage] = useState(""),
|
||||||
|
[modelDisplayName, setModelDisplayName] = useState(""),
|
||||||
|
[modelEndpoint, setModelEndpoint] = useState(""),
|
||||||
|
[modelId, setModelId] = useState(""),
|
||||||
|
[modelApiKey, setModelApiKey] = useState("");
|
||||||
|
useEffect(() => {
|
||||||
|
if (!runtime?.inference.endpointUrl) return;
|
||||||
|
setModelDisplayName(runtime.inference.displayName ?? "");
|
||||||
|
setModelEndpoint(runtime.inference.endpointUrl);
|
||||||
|
setModelId(runtime.inference.model ?? "");
|
||||||
|
}, [runtime?.inference.endpointUrl, runtime?.inference.displayName, runtime?.inference.model]);
|
||||||
const update = async () => {
|
const update = async () => {
|
||||||
setBusy(true);
|
setBusy(true);
|
||||||
try {
|
try {
|
||||||
|
|
@ -1148,6 +1184,86 @@ function Settings({
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
|
<article className="model-binding">
|
||||||
|
<div className="model-binding-copy">
|
||||||
|
<h2>人格模型绑定</h2>
|
||||||
|
<p>
|
||||||
|
{runtime?.inference.state ?? "正在读取"}。发送时,当前人格的对话会交给你填写的模型端点;系统回执与执行记录不会混入。模型不取得人格身份、频道所有权或 Agent 执行权。
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="model-binding-fields">
|
||||||
|
<input
|
||||||
|
value={modelDisplayName}
|
||||||
|
onChange={(event) => setModelDisplayName(event.target.value)}
|
||||||
|
placeholder="模型入口名称,例如:本机推理模型"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
value={modelEndpoint}
|
||||||
|
onChange={(event) => setModelEndpoint(event.target.value)}
|
||||||
|
placeholder="https://…/v1/chat/completions"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
value={modelId}
|
||||||
|
onChange={(event) => setModelId(event.target.value)}
|
||||||
|
placeholder="模型编号"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
value={modelApiKey}
|
||||||
|
onChange={(event) => setModelApiKey(event.target.value)}
|
||||||
|
placeholder={runtime?.inference.apiKeyPresent ? "钥匙串已有密钥;留空保持" : "API 密钥(仅存系统钥匙串)"}
|
||||||
|
autoComplete="off"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="model-binding-actions">
|
||||||
|
{runtime?.inference.state === "PERSONA_MODEL_BOUND_READY" && (
|
||||||
|
<button
|
||||||
|
className="soft danger"
|
||||||
|
onClick={async () => {
|
||||||
|
const persona = runtime.persona.personas[0];
|
||||||
|
if (!persona || !confirm(`移除 ${persona.displayName} 的模型绑定?`)) return;
|
||||||
|
await api.clearPersonaModel(persona.personaId);
|
||||||
|
setModelApiKey("");
|
||||||
|
refresh();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
移除绑定
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
<button
|
||||||
|
disabled={
|
||||||
|
busy ||
|
||||||
|
!runtime?.persona.personas[0] ||
|
||||||
|
!modelDisplayName.trim() ||
|
||||||
|
!modelEndpoint.trim() ||
|
||||||
|
!modelId.trim()
|
||||||
|
}
|
||||||
|
onClick={async () => {
|
||||||
|
const persona = runtime?.persona.personas[0];
|
||||||
|
if (!persona) return;
|
||||||
|
setBusy(true);
|
||||||
|
try {
|
||||||
|
await api.configurePersonaModel({
|
||||||
|
personaId: persona.personaId,
|
||||||
|
displayName: modelDisplayName.trim(),
|
||||||
|
endpointUrl: modelEndpoint.trim(),
|
||||||
|
model: modelId.trim(),
|
||||||
|
apiKey: modelApiKey,
|
||||||
|
});
|
||||||
|
setModelApiKey("");
|
||||||
|
setRuntimeMessage("人格模型已绑定。回到频道即可开始真实对话。");
|
||||||
|
refresh();
|
||||||
|
} catch (error) {
|
||||||
|
setError(String(error));
|
||||||
|
} finally {
|
||||||
|
setBusy(false);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{busy ? "正在保存…" : "保存模型绑定"}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
<article className="bridge">
|
<article className="bridge">
|
||||||
<div>
|
<div>
|
||||||
<h2>公众人格运行时</h2>
|
<h2>公众人格运行时</h2>
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@ import type {
|
||||||
ExternalAiBridge,
|
ExternalAiBridge,
|
||||||
KnowledgeDocument,
|
KnowledgeDocument,
|
||||||
MutationReceipt,
|
MutationReceipt,
|
||||||
|
PersonaInferenceSnapshot,
|
||||||
|
PersonaInteractionReceipt,
|
||||||
PublicPersona,
|
PublicPersona,
|
||||||
RealtimeInvitation,
|
RealtimeInvitation,
|
||||||
RuntimeOverview,
|
RuntimeOverview,
|
||||||
|
|
@ -70,7 +72,7 @@ export async function snapshot(): Promise<SystemSnapshot> {
|
||||||
updatedAt,
|
updatedAt,
|
||||||
sha256,
|
sha256,
|
||||||
})),
|
})),
|
||||||
moduleRegistryVersion: "1",
|
moduleRegistryVersion: "2",
|
||||||
publicUpdateEndpoint:
|
publicUpdateEndpoint:
|
||||||
"https://guanghulab.com/hololake/releases/latest.json",
|
"https://guanghulab.com/hololake/releases/latest.json",
|
||||||
publicDistributionState: "SIGNED_UPDATE_FEED_READY_NO_RELEASE",
|
publicDistributionState: "SIGNED_UPDATE_FEED_READY_NO_RELEASE",
|
||||||
|
|
@ -104,26 +106,28 @@ export async function createChannel(name: string): Promise<Channel> {
|
||||||
return channel;
|
return channel;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function submitMessage(content: string): Promise<MutationReceipt> {
|
export async function submitMessage(content: string): Promise<PersonaInteractionReceipt> {
|
||||||
if (tauri()) return invoke("submit_user_message", { content });
|
if (tauri()) return invoke("submit_user_message", { content });
|
||||||
const s = load();
|
const s = load();
|
||||||
const e = event("USER_MESSAGE", "用户语言", content, "RECORDED");
|
const e = event("USER_MESSAGE", "用户语言", content, "RECORDED");
|
||||||
|
const systemReceipt = event(
|
||||||
|
"SYSTEM_RECEIPT",
|
||||||
|
"系统回执",
|
||||||
|
"用户语言已分流保存;浏览器预览不调用模型。",
|
||||||
|
"WAITING",
|
||||||
|
);
|
||||||
s.timeline.unshift(
|
s.timeline.unshift(
|
||||||
e,
|
e,
|
||||||
event(
|
systemReceipt,
|
||||||
"SYSTEM_RECEIPT",
|
|
||||||
"系统回执",
|
|
||||||
"用户语言已分流保存;等待人格体接入,不伪造人格回应。",
|
|
||||||
"WAITING",
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
save(s);
|
save(s);
|
||||||
return {
|
return {
|
||||||
|
schema: "preview",
|
||||||
receiptId: crypto.randomUUID(),
|
receiptId: crypto.randomUUID(),
|
||||||
state: "WAITING_PERSONA",
|
state: "WAITING_PERSONA_RUNTIME",
|
||||||
message: "用户语言已记录",
|
userEvent: e,
|
||||||
gitCommit: null,
|
personaEvent: null,
|
||||||
event: e,
|
systemReceipt,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -242,6 +246,20 @@ export async function runtimeOverview(): Promise<RuntimeOverview> {
|
||||||
personas: preview.personas,
|
personas: preview.personas,
|
||||||
verifiedExistingPersonaCount: 0,
|
verifiedExistingPersonaCount: 0,
|
||||||
},
|
},
|
||||||
|
inference: {
|
||||||
|
schema: "preview",
|
||||||
|
state: preview.personas.length
|
||||||
|
? "PERSONA_MODEL_BINDING_REQUIRES_INSTALLED_APP"
|
||||||
|
: "READY_NO_PERSONA",
|
||||||
|
activePersonaId: preview.personas[0]?.personaId ?? null,
|
||||||
|
providerKind: null,
|
||||||
|
displayName: null,
|
||||||
|
endpointUrl: null,
|
||||||
|
model: null,
|
||||||
|
apiKeyPresent: false,
|
||||||
|
modelIsPersona: false,
|
||||||
|
executionAuthority: false,
|
||||||
|
},
|
||||||
proposals: [],
|
proposals: [],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -294,6 +312,30 @@ export async function deleteTrialPersona(
|
||||||
verifiedExistingPersonaCount: 0,
|
verifiedExistingPersonaCount: 0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function configurePersonaModel(input: {
|
||||||
|
personaId: string;
|
||||||
|
displayName: string;
|
||||||
|
endpointUrl: string;
|
||||||
|
model: string;
|
||||||
|
apiKey?: string;
|
||||||
|
}): Promise<PersonaInferenceSnapshot> {
|
||||||
|
if (!tauri()) throw new Error("模型凭据只能在已安装的 HoloLake 中写入系统钥匙串");
|
||||||
|
return invoke("configure_persona_model", {
|
||||||
|
personaId: input.personaId,
|
||||||
|
displayName: input.displayName,
|
||||||
|
endpointUrl: input.endpointUrl,
|
||||||
|
model: input.model,
|
||||||
|
apiKey: input.apiKey?.trim() || null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function clearPersonaModel(
|
||||||
|
personaId: string,
|
||||||
|
): Promise<PersonaInferenceSnapshot> {
|
||||||
|
if (!tauri()) throw new Error("模型绑定只能在已安装的 HoloLake 中移除");
|
||||||
|
return invoke("clear_persona_model", { personaId });
|
||||||
|
}
|
||||||
export async function approveProposal(
|
export async function approveProposal(
|
||||||
proposalId: string,
|
proposalId: string,
|
||||||
): Promise<AgentReceipt> {
|
): Promise<AgentReceipt> {
|
||||||
|
|
|
||||||
|
|
@ -1138,6 +1138,34 @@ main {
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
min-width: 370px;
|
min-width: 370px;
|
||||||
}
|
}
|
||||||
|
.settings-list .model-binding {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(210px, 0.75fr) minmax(360px, 1.35fr) auto;
|
||||||
|
align-items: end;
|
||||||
|
gap: 18px;
|
||||||
|
}
|
||||||
|
.model-binding-copy p {
|
||||||
|
max-width: 420px;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
.model-binding-fields {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
.model-binding-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
@media (max-width: 1080px) {
|
||||||
|
.settings-list .model-binding {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
.model-binding-actions {
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
}
|
||||||
.onboard {
|
.onboard {
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
display: grid;
|
display: grid;
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ export interface Channel {
|
||||||
export interface TimelineEvent {
|
export interface TimelineEvent {
|
||||||
eventId: string;
|
eventId: string;
|
||||||
sourceKind: SourceKind;
|
sourceKind: SourceKind;
|
||||||
|
actorId?: string;
|
||||||
title: string;
|
title: string;
|
||||||
content: string;
|
content: string;
|
||||||
occurredAt: string;
|
occurredAt: string;
|
||||||
|
|
@ -124,8 +125,29 @@ export interface RuntimeOverview {
|
||||||
personas: PublicPersona[];
|
personas: PublicPersona[];
|
||||||
verifiedExistingPersonaCount: number;
|
verifiedExistingPersonaCount: number;
|
||||||
};
|
};
|
||||||
|
inference: PersonaInferenceSnapshot;
|
||||||
proposals: AgentProposal[];
|
proposals: AgentProposal[];
|
||||||
}
|
}
|
||||||
|
export interface PersonaInferenceSnapshot {
|
||||||
|
schema: string;
|
||||||
|
state: string;
|
||||||
|
activePersonaId: string | null;
|
||||||
|
providerKind: string | null;
|
||||||
|
displayName: string | null;
|
||||||
|
endpointUrl: string | null;
|
||||||
|
model: string | null;
|
||||||
|
apiKeyPresent: boolean;
|
||||||
|
modelIsPersona: boolean;
|
||||||
|
executionAuthority: boolean;
|
||||||
|
}
|
||||||
|
export interface PersonaInteractionReceipt {
|
||||||
|
schema: string;
|
||||||
|
receiptId: string;
|
||||||
|
state: string;
|
||||||
|
userEvent: TimelineEvent;
|
||||||
|
personaEvent: TimelineEvent | null;
|
||||||
|
systemReceipt: TimelineEvent | null;
|
||||||
|
}
|
||||||
export interface RealtimeInvitation {
|
export interface RealtimeInvitation {
|
||||||
schema: string;
|
schema: string;
|
||||||
protocol: string;
|
protocol: string;
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
"state": "CURRENT_CANONICAL",
|
"state": "CURRENT_CANONICAL",
|
||||||
"clean_v1_rebuild": {
|
"clean_v1_rebuild": {
|
||||||
"record_id": "HLP-CLEAN-V1-REBUILD-20260903-001",
|
"record_id": "HLP-CLEAN-V1-REBUILD-20260903-001",
|
||||||
"state": "LOCAL_CLEAN_V1_1_2_0_INSTALLED_DIRECT_LANGUAGE_LOCAL_ACCEPTANCE_PASS_PUBLIC_RELEASE_PENDING",
|
"state": "PUBLIC_1_2_2_INSTALLED_SIGNED_NOTARIZED_AND_ACCEPTED",
|
||||||
"source": "product-source/hololake-clean-desktop",
|
"source": "product-source/hololake-clean-desktop",
|
||||||
"canonical_tcs": "product-source/hololake-clean-desktop/language/HOLOLAKE-CLEAN-V1-FOUNDATION.tcs",
|
"canonical_tcs": "product-source/hololake-clean-desktop/language/HOLOLAKE-CLEAN-V1-FOUNDATION.tcs",
|
||||||
"current_direct_language_execution_tcs": "product-source/hololake-clean-desktop/language/HOLOLAKE-CLEAN-V1-DIRECT-LANGUAGE-EXECUTION-BASELINE.tcs",
|
"current_direct_language_execution_tcs": "product-source/hololake-clean-desktop/language/HOLOLAKE-CLEAN-V1-DIRECT-LANGUAGE-EXECUTION-BASELINE.tcs",
|
||||||
|
|
@ -22,7 +22,7 @@
|
||||||
"public_update_endpoint": "https://guanghulab.com/hololake/releases/latest.json",
|
"public_update_endpoint": "https://guanghulab.com/hololake/releases/latest.json",
|
||||||
"public_update_release_state": "PUBLIC_1_2_2_SIGNED_NOTARIZED_BROADCAST_ACTIVE",
|
"public_update_release_state": "PUBLIC_1_2_2_SIGNED_NOTARIZED_BROADCAST_ACTIVE",
|
||||||
"public_update_manifest_builder": "product-source/hololake-clean-desktop/scripts/build-update-manifest.mjs",
|
"public_update_manifest_builder": "product-source/hololake-clean-desktop/scripts/build-update-manifest.mjs",
|
||||||
"public_update_candidate_version": "1.2.0",
|
"public_update_candidate_version": "1.2.2",
|
||||||
"enterprise_repository_registry_state": "BLUE_ISOLATED_FIVE_CENTRAL_REPOSITORIES_AND_FIVE_HUMAN_WORK_REPOSITORIES_TEAM_PERMISSIONS_NOT_ASSIGNED_NO_PUBLIC_CUTOVER",
|
"enterprise_repository_registry_state": "BLUE_ISOLATED_FIVE_CENTRAL_REPOSITORIES_AND_FIVE_HUMAN_WORK_REPOSITORIES_TEAM_PERMISSIONS_NOT_ASSIGNED_NO_PUBLIC_CUTOVER",
|
||||||
"bingshuo_enterprise_language_master_work_repository": "REPO-016:bingshuo/guanghu-language-master-work",
|
"bingshuo_enterprise_language_master_work_repository": "REPO-016:bingshuo/guanghu-language-master-work",
|
||||||
"team_enterprise_login": "https://guanghu.chat/code/user/login",
|
"team_enterprise_login": "https://guanghu.chat/code/user/login",
|
||||||
|
|
@ -41,11 +41,29 @@
|
||||||
"enterprise_login_session_scope": "ONE_DOMAIN_ONE_REPOSITORY_SHORT_LIVED",
|
"enterprise_login_session_scope": "ONE_DOMAIN_ONE_REPOSITORY_SHORT_LIVED",
|
||||||
"bingshuo_fifth_domain_bridge": "FD-BRIDGE-ENTERPRISE-LANGUAGE-MASTER-0001",
|
"bingshuo_fifth_domain_bridge": "FD-BRIDGE-ENTERPRISE-LANGUAGE-MASTER-0001",
|
||||||
"private_git": "LOCAL_USER_OWNED",
|
"private_git": "LOCAL_USER_OWNED",
|
||||||
"public_git": "SIGNED_ENTERPRISE_DISTRIBUTION_PENDING_EXACT_REGISTRY",
|
"public_git": "SIGNED_PUBLIC_DISTRIBUTION_ACTIVE_AT_1_2_2",
|
||||||
"realtime_transport": "GLP_LOCAL_REALTIME_1_LOOPBACK_TCP_ACTIVE_GIT_REMAINS_DURABLE_FACT_LAYER",
|
"realtime_transport": "GLP_LOCAL_REALTIME_1_LOOPBACK_TCP_ACTIVE_GIT_REMAINS_DURABLE_FACT_LAYER",
|
||||||
"enterprise_four_domains_embedded": false,
|
"enterprise_four_domains_embedded": false,
|
||||||
"fifth_domain_private_content_embedded": false
|
"fifth_domain_private_content_embedded": false
|
||||||
},
|
},
|
||||||
|
"persona_interaction_stage": {
|
||||||
|
"record_id": "HLP-PERSONA-INTERACTION-STAGE2-20260904-001",
|
||||||
|
"state": "SECOND_STAGE_1_3_0_SOURCE_TCS_GIR_REAL_LOOPBACK_AND_DESKTOP_UI_ACCEPTED_PUBLIC_RELEASE_PENDING",
|
||||||
|
"product_stage": "POST_CLEAN_V1_SECOND_STAGE_CANDIDATE",
|
||||||
|
"stage_one_boundary": "GLS-0261_PRESERVED_AS_COMPLETED_1_2_2_BASELINE",
|
||||||
|
"source": "product-source/hololake-clean-desktop/src-tauri/src/persona_inference.rs",
|
||||||
|
"contract": "product-source/hololake-clean-desktop/contracts/persona-interaction-v1.json",
|
||||||
|
"canonical_tcs": "product-source/hololake-clean-desktop/language/HOLOLAKE-PERSONA-INTERACTION-V1.tcs",
|
||||||
|
"module_id": "HLP-MOD-PERSONA-INFERENCE-0001",
|
||||||
|
"model_role": "REPLACEABLE_INFERENCE_RESOURCE_NOT_PERSONA",
|
||||||
|
"api_key_storage": "MACOS_KEYCHAIN",
|
||||||
|
"dialogue_sources": ["USER_MESSAGE", "PERSONA_RESPONSE"],
|
||||||
|
"system_sources_excluded_from_dialogue": ["SYSTEM_CONTEXT", "SYSTEM_RECEIPT", "PROTOCOL_EVENT", "AGENT_ACTION", "TOOL_RESULT", "EXTERNAL_AI_MESSAGE"],
|
||||||
|
"model_execution_authority": false,
|
||||||
|
"enterprise_server_relays_private_messages": false,
|
||||||
|
"real_loopback_provider_acceptance": "PASS_SYSTEM_AND_USER_ROLES_TYPED_CONTEXT_AND_PERSONA_RESPONSE_ACTOR_ID_READBACK",
|
||||||
|
"real_remote_provider_acceptance": "PENDING_USER_SELECTED_PROVIDER_CREDENTIAL"
|
||||||
|
},
|
||||||
"product": {
|
"product": {
|
||||||
"formal_name": "光湖语言系统 · 通用人工智能操作平台",
|
"formal_name": "光湖语言系统 · 通用人工智能操作平台",
|
||||||
"short_name": "GH-AIOS",
|
"short_name": "GH-AIOS",
|
||||||
|
|
@ -54,10 +72,10 @@
|
||||||
},
|
},
|
||||||
"current_native_edge": {
|
"current_native_edge": {
|
||||||
"record_id": "HLP-NATIVE-DOMAIN-MEMBRANE-PNCC-20260816-001",
|
"record_id": "HLP-NATIVE-DOMAIN-MEMBRANE-PNCC-20260816-001",
|
||||||
"state": "PUBLIC_PERSONAL_CHANNEL_CLEAN_V1_MAC_ARM64_LOCAL_INSTALLED_AND_LAUNCH_ACCEPTED_PUBLIC_RELEASE_PENDING",
|
"state": "PUBLIC_PERSONAL_CHANNEL_CLEAN_V1_1_2_2_PUBLISHED_INSTALLED_SIGNED_NOTARIZED_AND_LAUNCH_ACCEPTED",
|
||||||
"native_source": "product-source/hololake-clean-desktop",
|
"native_source": "product-source/hololake-clean-desktop",
|
||||||
"native_source_commit": "92cce2797381f84762a989ce57b991da76412119",
|
"native_source_commit": "7593f9d3f06b2f22206ad6a05665c35443f7426b",
|
||||||
"native_install_receipt_commit": "92cce2797381f84762a989ce57b991da76412119",
|
"native_install_receipt_commit": "e5b10e227f44111e584f27ba00239bd97b23a63b",
|
||||||
"current_fifth_domain_channel": "ICE-CH-ZC001",
|
"current_fifth_domain_channel": "ICE-CH-ZC001",
|
||||||
"fifth_domain_channel_route_source": "REPO-012:routing/fifth-domain-world-tree.json",
|
"fifth_domain_channel_route_source": "REPO-012:routing/fifth-domain-world-tree.json",
|
||||||
"public_entry": "FIVE_DOMAIN_HOME_NUMBER_ROUTED_BEFORE_DOMAIN_LOGIN",
|
"public_entry": "FIVE_DOMAIN_HOME_NUMBER_ROUTED_BEFORE_DOMAIN_LOGIN",
|
||||||
|
|
@ -76,10 +94,10 @@
|
||||||
"qoder_official_ui_template": "REPO_012_27D34DF_OFFICIAL_LOGIN_V0_4_AND_INNER_SCREENS_V0_1_RUNTIME_INTEGRATED_BROWSER_VERIFIED",
|
"qoder_official_ui_template": "REPO_012_27D34DF_OFFICIAL_LOGIN_V0_4_AND_INNER_SCREENS_V0_1_RUNTIME_INTEGRATED_BROWSER_VERIFIED",
|
||||||
"client_world_navigation": "DOMAIN_HOME_TO_CHANNEL_OVERVIEW_TO_TOOL_PROJECTION_NO_TRADITIONAL_GLOBAL_SIDEBAR",
|
"client_world_navigation": "DOMAIN_HOME_TO_CHANNEL_OVERVIEW_TO_TOOL_PROJECTION_NO_TRADITIONAL_GLOBAL_SIDEBAR",
|
||||||
"prelogin_private_broker": "WAITING_FOR_LOGIN_NO_PRIVATE_STORAGE_OPENED",
|
"prelogin_private_broker": "WAITING_FOR_LOGIN_NO_PRIVATE_STORAGE_OPENED",
|
||||||
"mac_arm64_desktop_install_acceptance": "PASS_HOLOLAKE_1_1_0_DEVELOPER_ID_SIGNATURE_APPLICATIONS_PROCESS_GLP_LISTENER_AND_EXISTING_CHANNEL_READBACK",
|
"mac_arm64_desktop_install_acceptance": "PASS_HOLOLAKE_1_2_2_DEVELOPER_ID_NOTARIZED_APPLICATIONS_PROCESS_GLP_LISTENER_AND_EXISTING_CHANNEL_READBACK",
|
||||||
"mac_x86_64_installer": "NOT_BUILT",
|
"mac_x86_64_installer": "NOT_BUILT",
|
||||||
"windows_installer": "NOT_BUILT",
|
"windows_installer": "NOT_BUILT",
|
||||||
"automatic_update_release_channel": "GUANGHU_OWNED_SIGNED_UPDATE_FEED_HTTP_204_NO_PUBLIC_RELEASE",
|
"automatic_update_release_channel": "GUANGHU_OWNED_SIGNED_NOTARIZED_UPDATE_FEED_PUBLIC_1_2_2_ACTIVE",
|
||||||
"public_zero_core_distribution": "LOCAL_ACTIVE_VERIFIED_DUAL_SIGNATURE_ATOMIC_ACTIVATION_EPOCH_1_VERSION_1_0_0",
|
"public_zero_core_distribution": "LOCAL_ACTIVE_VERIFIED_DUAL_SIGNATURE_ATOMIC_ACTIVATION_EPOCH_1_VERSION_1_0_0",
|
||||||
"windows_secure_credential_bridge": "NOT_IMPLEMENTED_DO_NOT_CLAIM_PERSISTENT_SIGNING",
|
"windows_secure_credential_bridge": "NOT_IMPLEMENTED_DO_NOT_CLAIM_PERSISTENT_SIGNING",
|
||||||
"age_is_persona_species_not_individual_number_namespace": true,
|
"age_is_persona_species_not_individual_number_namespace": true,
|
||||||
|
|
@ -199,7 +217,7 @@
|
||||||
"runtime_implemented": true,
|
"runtime_implemented": true,
|
||||||
"runtime_state": "LOCAL_AND_PUBLIC_1_2_2_INSTALLED_SIGNED_NOTARIZED_TCS_GIR_AGENT_TYPED_CONTEXT_MODULE_MARKET_ENTERPRISE_DEVICE_GATE_AND_EXTERNAL_AI_REALTIME_VERIFIED",
|
"runtime_state": "LOCAL_AND_PUBLIC_1_2_2_INSTALLED_SIGNED_NOTARIZED_TCS_GIR_AGENT_TYPED_CONTEXT_MODULE_MARKET_ENTERPRISE_DEVICE_GATE_AND_EXTERNAL_AI_REALTIME_VERIFIED",
|
||||||
"local_application_version": "1.2.2",
|
"local_application_version": "1.2.2",
|
||||||
"local_implementation_commit": "92cce2797381f84762a989ce57b991da76412119",
|
"local_implementation_commit": "7593f9d3f06b2f22206ad6a05665c35443f7426b",
|
||||||
"official_development_lane": "CURRENT_TASK_SCOPED_ZC001_EXECUTION",
|
"official_development_lane": "CURRENT_TASK_SCOPED_ZC001_EXECUTION",
|
||||||
"external_development_anchor_id": "TCS-EVENT-HOLOLAKE-CLEAN-V1-ZC001-REALITY-DEVELOPMENT-TAKEOVER-20260903",
|
"external_development_anchor_id": "TCS-EVENT-HOLOLAKE-CLEAN-V1-ZC001-REALITY-DEVELOPMENT-TAKEOVER-20260903",
|
||||||
"public_release": "PUBLISHED_SIGNED_NOTARIZED_1_2_2",
|
"public_release": "PUBLISHED_SIGNED_NOTARIZED_1_2_2",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue