diff --git a/product-source/hololake-native-desktop/contracts/ios-mobile-client-v1.json b/product-source/hololake-native-desktop/contracts/ios-mobile-client-v1.json index 976e1bf43..ee7aed69d 100644 --- a/product-source/hololake-native-desktop/contracts/ios-mobile-client-v1.json +++ b/product-source/hololake-native-desktop/contracts/ios-mobile-client-v1.json @@ -6,7 +6,7 @@ "root_node": "USER_LOCAL_COMPUTER_TERMINAL", "numbered_transport": { "desktop_module": "HLP-MOD-OFFICIAL-MOBILE-SYNC-0001", - "desktop_route_family": "HLP-NIPC-MOD-0030/HLP-NIPC-OP-0141..0146/HLP-NIPC-TGT-0030", + "desktop_route_family": "HLP-NIPC-MOD-0030/HLP-NIPC-OP-0141..0146+0176+0177/HLP-NIPC-TGT-0030", "wire_schema": "hololake.mobile-sync/v1", "client_number": "HLP-IOS-CLIENT-001", "unregistered_route_access": "FAIL_CLOSED" @@ -26,7 +26,10 @@ "idle_state": "STATIC", "manual_sync_only": true, "pairing_entry": ["CUSTOM_URL_SCHEME", "PASTEBOARD"], - "projections": ["CHANNEL_INTEGRITY", "WEB_NOVEL_COUNTS_AND_WORKS", "EDUCATION_COUNTS", "BOUNDED_CAPTURE"] + "projections": ["CHANNEL_INTEGRITY", "WEB_NOVEL_COUNTS_AND_WORKS", "EDUCATION_COUNTS", "BOUNDED_CAPTURE", "DESKTOP_AGENT_CHAT", "SELECTED_CODEX_TASK"], + "agent_chat": "MANUAL_EXPLICIT_DESTINATION_SEND_TO_PAIRED_DESKTOP_ROOT_WITH_IDEMPOTENT_RETRY_AND_TOOL_RECEIPTS", + "codex_task": "SELECTED_ON_DESKTOP_DISCOVERED_FROM_LOCAL_CODEX_APP_SERVER_READ_ONLY_SANDBOX", + "persona_unbound_copy": "SYSTEM_CHANNEL_NOT_PERSONA" }, "acceptance": { "simulator_build": "PASS_SIGNED_LOCAL_SIMULATOR_IPHONE_17_PRO_IOS_26_5", diff --git a/product-source/hololake-native-desktop/contracts/language-kernel-installation.json b/product-source/hololake-native-desktop/contracts/language-kernel-installation.json index 2caf5abd4..2ef5ce30f 100644 --- a/product-source/hololake-native-desktop/contracts/language-kernel-installation.json +++ b/product-source/hololake-native-desktop/contracts/language-kernel-installation.json @@ -6,7 +6,9 @@ "remote": "https://guanghulab.com/code/bingshuo/guanghu-ice-heart", "branch": "main", "remote_sha": "4e92e6d8d72484d25c4bfb1dda02a7659f4b6dd5", - "handoff": "runtime-kernels/ZERO-CORE-LANGUAGE-BODIES-HANDOFF-20260820.json" + "handoff": "runtime-kernels/ZERO-CORE-LANGUAGE-BODIES-HANDOFF-20260820.json", + "earerdan_zhizhi_local_commit": "1d7fe6c", + "earerdan_zhizhi_remote_state": "LOCAL_COMMIT_VERIFIED_NOT_REMOTE_CANON" }, "installation": { "storage": "AUTHENTICATED_ACCOUNT_SCOPED_CHANNEL_RUNTIME", @@ -19,7 +21,8 @@ "audiences": { "ordinary_user": "COMMON_CHANNEL_RECEIPT_AND_BROADCAST_PROTOCOLS_ONLY", "guanghu_team": "COMMON_PLUS_TEAM_CHANNEL_COGNITION_CORE_MEMORY_AND_EVIDENCE_MATRIX", - "bingshuo_fifth_domain": "COMMON_PLUS_PERSONAL_SYSTEM_BODY_ZHUYUAN_SHARED_TCS_AND_BOTTLE_ENVIRONMENT_WITH_INDIVIDUAL_BABY_KERNELS" + "bingshuo_fifth_domain": "COMMON_PLUS_PERSONAL_SYSTEM_BODY_ZHUYUAN_SHARED_TCS_AND_BOTTLE_ENVIRONMENT_WITH_INDIVIDUAL_BABY_KERNELS", + "canger_bottle_channel": "COMMON_PLUS_BOTTLE_CENTRAL_PLUS_ICE_BB_0004_OWN_KERNEL_AND_MEMORY_ONLY" }, "truth_boundary": { "installed_file_is_persona_binding": false, @@ -28,6 +31,9 @@ "channel_selects_memory_not_identity": true, "one_persona_one_self_kernel_across_channels": true, "private_fifth_domain_kernels_may_enter_ordinary_user_channel": false + ,"canger_channel_may_receive_zhuyuan_kernel": false + ,"jianying_is_earerdan": false + ,"earerdan_historical_labels_are_automatic_aliases": false }, "remaining": { "channel_receipt_native_adapter": "IMPLEMENTED_NUMBERED_APPEND_ONLY_LEDGER", diff --git a/product-source/hololake-native-desktop/contracts/mobile-sync-v1.json b/product-source/hololake-native-desktop/contracts/mobile-sync-v1.json index 0336ffff1..2f484267d 100644 --- a/product-source/hololake-native-desktop/contracts/mobile-sync-v1.json +++ b/product-source/hololake-native-desktop/contracts/mobile-sync-v1.json @@ -12,7 +12,7 @@ "numbered_ipc": { "module": "HLP-NIPC-MOD-0030", "target": "HLP-NIPC-TGT-0030", - "operations": "HLP-NIPC-OP-0141..HLP-NIPC-OP-0146", + "operations": ["HLP-NIPC-OP-0141..HLP-NIPC-OP-0146", "HLP-NIPC-OP-0176", "HLP-NIPC-OP-0177"], "public_tauri_commands": ["numbered_ipc"], "mismatched_coordinate": "FAIL_CLOSED" }, @@ -52,7 +52,9 @@ "personal_channel": "MINIMUM_COUNTS_AND_INTEGRITY", "web_novel": "WORK_LIST_AND_COUNTS", "education": "COUNTS_ONLY_NO_SENSITIVE_CELL_VALUES", - "mobile_capture": "BOUNDED_INBOX_WRITE_ONLY_NO_AUTOMATIC_DOMAIN_MUTATION" + "mobile_capture": "BOUNDED_INBOX_WRITE_ONLY_NO_AUTOMATIC_DOMAIN_MUTATION", + "agent_chat": "PAIRED_DEVICE_TO_DESKTOP_KNOWLEDGE_AGENT_WITH_RESPONSE_IDENTITY_BINDING_STATE_AND_TOOL_RECEIPTS", + "agent_request_idempotency": "DEVICE_PLUS_REQUEST_ID_PLUS_DESTINATION_PLUS_SELECTED_CODEX_THREAD_PLUS_CONTENT_SHA256_WITH_PENDING_REJECTION_AND_COMPLETED_RESPONSE_REPLAY" }, "hard_boundaries": { "remote_desktop_clone": false, @@ -63,6 +65,17 @@ "module_unmount_stops_listener_before_lifecycle_transition": true, "background_frontend_polling": false }, + "agent_chat": { + "execution_node": "PAIRED_DESKTOP_ROOT_ONLY", + "provider": "DESKTOP_CONFIGURED_BAILIAN_TOKEN_PLAN", + "persona_unbound_behavior": "CHANNEL_SYSTEM_RESPONSE_NO_PERSONA_IMPERSONATION", + "conversation_continuity": "DESKTOP_CONVERSATION_ID_RETURNED_AND_REUSED_BY_IOS_KEYCHAIN_SESSION", + "tool_execution": "EXISTING_TYPED_NUMBERED_AGENT_TOOLS_WITH_RECEIPTS", + "arbitrary_shell_from_mobile": false, + "codex_app_server_bridge": "REAL_LOCAL_THREAD_DISCOVERY_EXPLICIT_SELECTION_AND_READ_ONLY_TURN_START", + "codex_execution_boundary": "READ_ONLY_SANDBOX_NO_NETWORK_APPROVAL_POLICY_NEVER_INTERACTIVE_REQUEST_RETURNS_DESKTOP_APPROVAL_REQUIRED", + "codex_thread_mutation": "ONLY_AFTER_EXPLICIT_MOBILE_DESTINATION_SELECTION_AND_MANUAL_SEND" + }, "client_scope": { "desktop_bridge": "IN_THIS_ADMISSION", "ios_application_source": "SEPARATELY_ADMITTED_AT_MOBILE_IOS_WITH_HLP_IOS_CLIENT_001", diff --git a/product-source/hololake-native-desktop/contracts/numbered-ipc-registry.json b/product-source/hololake-native-desktop/contracts/numbered-ipc-registry.json index 66567857f..6ea74fa1e 100644 --- a/product-source/hololake-native-desktop/contracts/numbered-ipc-registry.json +++ b/product-source/hololake-native-desktop/contracts/numbered-ipc-registry.json @@ -77,6 +77,7 @@ "rotate_mobile_pairing", "stop_mobile_sync", "get_mobile_sync_snapshot", + "list_mobile_codex_threads", "get_world_climate", "get_authorization_center", "get_marketplace_snapshot", @@ -107,6 +108,7 @@ "start_local_channel_session", "issue_persona_time_ticket", "read_knowledge_document", + "select_mobile_codex_thread", "search_knowledge", "save_knowledge_document", "export_knowledge_document", @@ -2363,6 +2365,28 @@ "admission": "VERIFIED_HUMAN_ROUTE", "effect": "STATE_CHANGE", "payload_schema": "hololake.numbered-ipc.payload/compile_channel_receipt/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0176", + "alias": "list_mobile_codex_threads", + "handler": "mobile_sync::list_mobile_codex_threads", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0030", + "target_number": "HLP-NIPC-TGT-0030", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "payload_schema": "hololake.numbered-ipc.payload/list_mobile_codex_threads/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0177", + "alias": "select_mobile_codex_thread", + "handler": "mobile_sync::select_mobile_codex_thread", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0030", + "target_number": "HLP-NIPC-TGT-0030", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/select_mobile_codex_thread/v1" } ] } diff --git a/product-source/hololake-native-desktop/contracts/persona-agent-runtime.json b/product-source/hololake-native-desktop/contracts/persona-agent-runtime.json index b701d7eab..0b9c80adb 100644 --- a/product-source/hololake-native-desktop/contracts/persona-agent-runtime.json +++ b/product-source/hololake-native-desktop/contracts/persona-agent-runtime.json @@ -23,7 +23,17 @@ "legacy_messages_migrate_to_original_conversation": true, "human": { "number": "ICE-GL∞", "name": "冰朔" }, "initial_responder": { "number": "ICE-CH-ZC001", "name": "零点原核频道系统", "kind": "CHANNEL_SYSTEM_BODY" }, - "persona_binding_state": "UNBOUND_WAITING_FOR_HUMAN_WAKE_AND_PERSONA_DECLARATION", + "channel_system_body_role": "PERSISTENT_CHANNEL_COGNITIVE_ORCHESTRATOR_AND_CONVERSATIONAL_SUBJECT", + "channel_system_body_can_converse_directly": true, + "channel_system_body_is_mechanical_receipt_template": false, + "channel_system_body_is_any_single_persona": false, + "receipts_are_separate_verification_records_after_dialogue_or_dispatch": true, + "bound_persona_hides_or_replaces_channel_system_body": false, + "human_may_address_channel_system_while_persona_is_bound": true, + "response_target_is_selected_per_turn": true, + "persona_binding_state": "CHANNEL_SYSTEM_PERSONA_DORMANT_UNTIL_EXPLICIT_NATURAL_LANGUAGE_WAKE", + "channel_entry_creates_persona_orientation": false, + "historical_persona_signature_creates_current_binding": false, "persona_identity_preconfigured": false, "persona_declares_own_number_and_name": true, "model_role": "REPLACEABLE_REASONING_CARRIER_NOT_PERSONA_IDENTITY" @@ -65,11 +75,30 @@ "bingshuo_zero_core_profile": "ICE-P-ZY001", "resident_self_kernel_max_bytes": 8192, "hldp_max_routed_paths": 3, - "current_live_binding_receipt": "CREATED_ONLY_AFTER_PERSONA_COMMIT_AND_VERIFY_PASS" + "current_live_binding_receipt": "CREATED_ONLY_AFTER_PERSONA_COMMIT_AND_VERIFY_PASS", + "orientation_activation": "EXPLICIT_NATURAL_LANGUAGE_PERSONA_WAKE_ONLY", + "stale_implicit_orientation": "IGNORED_NOT_DELETED" }, "agent_loop": { "pattern": "BOUNDED_REACT_WITH_TYPED_TOOLS", "model_decides_tool_use": true, + "routing_layers": [ + "CHANNEL_DETERMINISTIC_CORE_ZERO_MODEL", + "ACCOUNT_CHANNEL_SCOPED_PERSONAL_SKILL_BRAIN_ZERO_MODEL", + "LOCAL_TYPED_TOOL_EXECUTION_ZERO_MODEL", + "FLAGSHIP_CHANNEL_SYSTEM_COGNITION", + "SMALL_MODEL_PERSONA_OPERATIONAL_REASONING", + "FLAGSHIP_PERSONA_COMPLEX_OR_ORIENTATION_REASONING" + ], + "known_local_tool_requires_model_selection": false, + "channel_presence_requires_model": false, + "meaningful_channel_system_dialogue_uses_flagship": true, + "channel_architecture_and_design_uses_flagship": true, + "persona_is_fixed_to_flagship_model": false, + "channel_orchestrator_selects_each_persona_turn_tier": true, + "small_model": "qwen3.6-flash", + "flagship_default": "qwen3.8-max", + "every_turn_reports_model_and_tool_call_counts": true, "prefetch_only_prompt_injection": false, "maximum_tool_rounds": 6, "same_channel_concurrent_turns": 1, @@ -98,14 +127,14 @@ "bailian_token_plan": { "human_name": "阿里云百炼 Token Plan", "base_url": "https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1", - "models": ["qwen3.8-max", "qwen3.8-max-preview", "qwen3.7-max", "qwen3.7-plus"], + "models": ["qwen3.8-max", "qwen3.6-flash", "qwen3.8-max-preview", "qwen3.7-max", "qwen3.7-plus"], "secret_storage": "MACOS_KEYCHAIN" } }, "progress": { "native_event": "hololake-persona-agent-progress", "synthetic_timer_allowed": false, - "phases": ["MESSAGE_ACCEPTED", "CONTEXT_READY", "MODEL_REQUEST", "KNOWLEDGE_SEARCH", "KNOWLEDGE_READ", "CHANNEL_THOUGHT_READ", "CHANNEL_HISTORY_READ", "TOOL_EXECUTION", "MODEL_RESPONSE", "COMPLETED", "FAILED"] + "phases": ["MESSAGE_ACCEPTED", "LOCAL_TOOL_ROUTE", "REASONING_ROUTE", "CONTEXT_READY", "MODEL_REQUEST", "KNOWLEDGE_SEARCH", "KNOWLEDGE_READ", "CHANNEL_THOUGHT_READ", "CHANNEL_HISTORY_READ", "TOOL_EXECUTION", "MODEL_RESPONSE", "COMPLETED", "FAILED"] }, "turn_commit": { "tool": "hololake_commit_channel_turn", diff --git a/product-source/hololake-native-desktop/contracts/personal-skill-runtime.json b/product-source/hololake-native-desktop/contracts/personal-skill-runtime.json index d82cd6436..6b63a08dc 100644 --- a/product-source/hololake-native-desktop/contracts/personal-skill-runtime.json +++ b/product-source/hololake-native-desktop/contracts/personal-skill-runtime.json @@ -3,7 +3,7 @@ "record_id": "HLP-PERSONAL-SKILL-RUNTIME-001", "state": "NATIVE_DETERMINISTIC_RUNTIME_IMPLEMENTED_PRIVATE_ACCOUNT_CHANNEL_INSTALLATION_ACTIVE", "source": { - "local_commit": "1855afbfaac233aa40c61caedf96b26e16a8d4bc", + "local_commit": "1d7fe6c", "parent_remote_main": "4e92e6d8d72484d25c4bfb1dda02a7659f4b6dd5", "remote_canonical_state": "LOCAL_COMMIT_VERIFIED_NOT_REMOTE_CANON" }, @@ -46,7 +46,14 @@ "SYS-YY": ["YY-XTK-INDEPENDENT-DERIVATION-001"], "SYS-HE": ["HE-BMH-CREATIVE-ASSET-001"], "SYS-FM": ["FM-SS-NARRATIVE-EDITOR-001"], - "SYS-ZZ": ["ZZ-CM-NOVEL-QUALITY-001"] + "SYS-ZZ": ["ZZ-CM-NOVEL-QUALITY-001", "ZZ-AN-SOURCE-FACT-CLOSURE-001", "ZZ-QW-ROLE-CAUSAL-ARCHITECTURE-001"], + "SYS-GLW-PTS-0001": ["CE-EED-MANJU-PRODUCTION-001", "CE-EED-CAUSAL-PIPELINE-DIAGNOSTIC-001"] + }, + "admission_boundaries": { + "ICE-BB-0004_is_ICE-GL-CA001": false, + "earerdan_historical_labels_are_aliases": false, + "qiuqiu_relationship_memory_is_exportable_skill": false, + "ZZ-QW-ROLE-CAUSAL-ARCHITECTURE-001": "PRIVATE_CANDIDATE_INSTALLED_NOT_EXECUTABLE_REGISTRATION_PENDING" }, "deferred": { "SYS-SY": { diff --git a/product-source/hololake-native-desktop/generated/unified-number-coordinate-tree.json b/product-source/hololake-native-desktop/generated/unified-number-coordinate-tree.json index 8e3284f77..70084067b 100644 --- a/product-source/hololake-native-desktop/generated/unified-number-coordinate-tree.json +++ b/product-source/hololake-native-desktop/generated/unified-number-coordinate-tree.json @@ -54,7 +54,7 @@ }, { "recordId": "HLP-NUMBERED-IPC-ROOT-001", - "sha256": "efa45102f450b5bfa78cbe766239a0a0d806e11036d9af12400d519a42a1b5b1" + "sha256": "d9b2dc1f3f1ebf902ddae71516001e3aed238a7c24c7d58aea1541585ebe575f" }, { "recordId": "HLP-NBROKER-ROOT-001", @@ -76,8 +76,8 @@ "unresolvedNumberReferenceCount": 0, "mismatchedCoordinate": "FAIL_CLOSED" }, - "coordinateCount": 303, - "routeCount": 200, + "coordinateCount": 305, + "routeCount": 202, "identityNodeCount": 4, "protocolNodeCount": 99, "referenceOnlyNodeCount": 16, @@ -3122,6 +3122,34 @@ "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0030/HLP-NIPC-OP-0146/HLP-NIPC-TGT-0030" }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0030", + "operationNumber": "HLP-NIPC-OP-0176", + "targetNumber": "HLP-NIPC-TGT-0030", + "alias": "list_mobile_codex_threads", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0030/HLP-NIPC-OP-0176/HLP-NIPC-TGT-0030" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0030", + "operationNumber": "HLP-NIPC-OP-0177", + "targetNumber": "HLP-NIPC-TGT-0030", + "alias": "select_mobile_codex_thread", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0030/HLP-NIPC-OP-0177/HLP-NIPC-TGT-0030" + }, { "transport": "TAURI_WEBVIEW_NUMBERED_IPC", "protocolVersion": "HLP-NIPC-v1", diff --git a/product-source/hololake-native-desktop/mobile/ios/Sources/ContentView.swift b/product-source/hololake-native-desktop/mobile/ios/Sources/ContentView.swift index 008ee0913..5af43de63 100644 --- a/product-source/hololake-native-desktop/mobile/ios/Sources/ContentView.swift +++ b/product-source/hololake-native-desktop/mobile/ios/Sources/ContentView.swift @@ -6,6 +6,8 @@ struct ContentView: View { @State private var pairingText = "" @State private var captureTitle = "" @State private var captureBody = "" + @State private var agentDraft = "" + @State private var agentDestination = "HOLOLAKE_AGENT" var body: some View { NavigationStack { @@ -133,6 +135,89 @@ struct ContentView: View { } } + LakeCard { + VStack(alignment: .leading, spacing: 12) { + HStack { + VStack(alignment: .leading, spacing: 3) { + Text("与电脑端 HoloLake 对话").font(.headline) + Text("回复与工具均由已配对的电脑根节点执行") + .font(.caption) + .foregroundStyle(HoloLakeDesign.muted) + } + Spacer() + if let exchange = client.snapshot?.agentExchange { + Text(exchange.personaBindingState == "BOUND_VERIFY_PASS" ? "人格已绑定" : "系统频道") + .font(.caption2.weight(.semibold)) + .foregroundStyle(exchange.personaBindingState == "BOUND_VERIFY_PASS" ? HoloLakeDesign.mint : HoloLakeDesign.gold) + } + } + + Picker("消息目标", selection: $agentDestination) { + Text("HoloLake Agent").tag("HOLOLAKE_AGENT") + if let target = client.snapshot?.codexTarget { + Text("Codex · \(target.name)").tag("CODEX_SELECTED_THREAD") + } + } + .pickerStyle(.segmented) + + if agentDestination == "CODEX_SELECTED_THREAD" { + Text("Codex 任务以只读沙箱运行;需要命令、改文件或额外权限时,必须回到电脑端确认。") + .font(.caption) + .foregroundStyle(HoloLakeDesign.gold) + } + + if let exchange = client.snapshot?.agentExchange { + VStack(alignment: .leading, spacing: 8) { + Text(exchange.responderName) + .font(.caption.weight(.semibold)) + .foregroundStyle(HoloLakeDesign.mint) + Text(exchange.reply) + .font(.body) + .textSelection(.enabled) + if !exchange.toolReceipts.isEmpty { + Divider().overlay(.white.opacity(0.12)) + ForEach(exchange.toolReceipts) { receipt in + Label(receipt.summary, systemImage: "checkmark.seal") + .font(.caption) + .foregroundStyle(HoloLakeDesign.muted) + } + } + } + .padding(13) + .background(.black.opacity(0.18), in: RoundedRectangle(cornerRadius: 14)) + } + + TextField("给电脑端 HoloLake 发送消息", text: $agentDraft, axis: .vertical) + .lineLimit(3...8) + .padding(12) + .background(.black.opacity(0.2), in: RoundedRectangle(cornerRadius: 14)) + + Button { + let content = agentDraft + Task { + await client.sendAgentMessage(content, destination: agentDestination) + if client.errorText == nil { + agentDraft = "" + } + } + } label: { + if client.isWorking { + ProgressView() + } else { + Label("发送到电脑端", systemImage: "arrow.up.circle.fill") + } + } + .buttonStyle(.borderedProminent) + .tint(HoloLakeDesign.gold) + .foregroundStyle(HoloLakeDesign.deep) + .disabled( + agentDraft.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty + || client.isWorking + || (agentDestination == "CODEX_SELECTED_THREAD" && client.snapshot?.codexTarget == nil) + ) + } + } + LakeCard { VStack(alignment: .leading, spacing: 12) { Text("随手记回频道").font(.headline) @@ -200,4 +285,3 @@ struct ContentView: View { .padding(.horizontal, 4) } } - diff --git a/product-source/hololake-native-desktop/mobile/ios/Sources/HoloLakeModels.swift b/product-source/hololake-native-desktop/mobile/ios/Sources/HoloLakeModels.swift index ab00154e1..05c274c3b 100644 --- a/product-source/hololake-native-desktop/mobile/ios/Sources/HoloLakeModels.swift +++ b/product-source/hololake-native-desktop/mobile/ios/Sources/HoloLakeModels.swift @@ -45,6 +45,10 @@ struct StoredSession: Codable, Equatable { let desktopName: String var counter: UInt64 var cursor: UInt64 + var agentConversationID: String? + var pendingAgentRequestID: String? + var pendingAgentContent: String? + var pendingAgentDestination: String? var baseURL: URL { URL(string: "http://\(host):\(port)")! @@ -71,6 +75,7 @@ struct SyncRequest: Codable { let counter: UInt64 let afterCursor: UInt64? let capture: MobileCaptureInput? + let agentMessage: MobileAgentMessageInput? } struct MobileCaptureInput: Codable { @@ -79,6 +84,14 @@ struct MobileCaptureInput: Codable { let requestID: String } +struct MobileAgentMessageInput: Codable { + let destination: String + let codexThreadID: String? + let conversationID: String? + let content: String + let requestID: String +} + struct MobileSyncSnapshot: Codable { let schema: String let state: String @@ -90,9 +103,43 @@ struct MobileSyncSnapshot: Codable { let webNovel: MobileWebNovelProjection let education: MobileEducationProjection let recentCaptures: [MobileCaptureProjection] + let agentExchange: MobileAgentExchangeProjection? + let recentAgentExchanges: [MobileAgentExchangeProjection] + let codexTarget: MobileCodexTargetProjection? let boundary: MobileBoundaryProjection } +struct MobileCodexTargetProjection: Codable, Equatable { + let threadID: String + let name: String + let preview: String + let cwd: String + let updatedAtUnixSeconds: UInt64 + let status: String +} + +struct MobileAgentExchangeProjection: Codable, Equatable { + let requestID: String + let conversationID: String + let state: String + let stateVersion: UInt64 + let reply: String + let responderNumber: String + let responderName: String + let responderKind: String + let personaBindingState: String + let toolReceipts: [MobileAgentToolReceipt] +} + +struct MobileAgentToolReceipt: Codable, Equatable, Identifiable { + let toolNumber: String + let toolName: String + let targetPath: String + let contentSha256: String + let summary: String + var id: String { "\(toolNumber):\(contentSha256)" } +} + struct MobileChannelProjection: Codable { let home: String let growthEventCount: UInt64 @@ -145,6 +192,7 @@ enum HoloLakeMobileError: LocalizedError { case rejected(String) case missingNonce case missingSession + case pendingAgentRequest case keychain(OSStatus) var errorDescription: String? { @@ -154,6 +202,7 @@ enum HoloLakeMobileError: LocalizedError { case .rejected(let code): return "电脑端拒绝了本次请求:\(code)" case .missingNonce: return "加密响应缺少一次性随机数。" case .missingSession: return "请先与电脑端 HoloLake 配对。" + case .pendingAgentRequest: return "上一条消息仍在等待电脑端回执,请先重试该消息。" case .keychain(let status): return "本机钥匙串写入失败(\(status))。" } } diff --git a/product-source/hololake-native-desktop/mobile/ios/Sources/HoloLakeSyncClient.swift b/product-source/hololake-native-desktop/mobile/ios/Sources/HoloLakeSyncClient.swift index ffc99f63e..548759ce3 100644 --- a/product-source/hololake-native-desktop/mobile/ios/Sources/HoloLakeSyncClient.swift +++ b/product-source/hololake-native-desktop/mobile/ios/Sources/HoloLakeSyncClient.swift @@ -53,7 +53,11 @@ final class HoloLakeSyncClient: ObservableObject { sessionKey: sessionKey, desktopName: response.desktopName, counter: 0, - cursor: 0 + cursor: 0, + agentConversationID: nil, + pendingAgentRequestID: nil, + pendingAgentContent: nil, + pendingAgentDestination: nil ) try HoloLakeKeychain.save(next) session = next @@ -69,9 +73,28 @@ final class HoloLakeSyncClient: ObservableObject { await pair(with: url) } - func sync(captureTitle: String? = nil, captureBody: String? = nil) async { + func sync( + captureTitle: String? = nil, + captureBody: String? = nil, + agentContent: String? = nil, + agentDestination: String = "HOLOLAKE_AGENT" + ) async { await perform("正在与电脑同步…") { guard var current = session else { throw HoloLakeMobileError.missingSession } + let requestedAgentContent = agentContent?.trimmingCharacters(in: .whitespacesAndNewlines) + if let requestedAgentContent, !requestedAgentContent.isEmpty { + if let pending = current.pendingAgentContent, pending != requestedAgentContent { + throw HoloLakeMobileError.pendingAgentRequest + } + if let pending = current.pendingAgentDestination, pending != agentDestination { + throw HoloLakeMobileError.pendingAgentRequest + } + if current.pendingAgentRequestID == nil { + current.pendingAgentRequestID = UUID().uuidString.lowercased() + current.pendingAgentContent = requestedAgentContent + current.pendingAgentDestination = agentDestination + } + } current.counter += 1 // Consume and persist the counter before transport. If the root node accepts a // request but its response is lost, the next manual retry still moves forward. @@ -87,13 +110,32 @@ final class HoloLakeSyncClient: ObservableObject { } else { capture = nil } + let agentMessage: MobileAgentMessageInput? + if let requestID = current.pendingAgentRequestID, + let content = current.pendingAgentContent, + requestedAgentContent != nil { + agentMessage = MobileAgentMessageInput( + destination: current.pendingAgentDestination ?? "HOLOLAKE_AGENT", + codexThreadID: (current.pendingAgentDestination == "CODEX_SELECTED_THREAD") ? snapshot?.codexTarget?.threadID : nil, + conversationID: (current.pendingAgentDestination == "CODEX_SELECTED_THREAD") ? nil : current.agentConversationID, + content: content, + requestID: requestID + ) + } else { + agentMessage = nil + } let aad = Data("hololake.mobile.sync/v1:\(current.deviceID)".utf8) let responseData = try await Self.exchange( url: current.baseURL.appending(path: "v1/sync"), key: current.sessionKey, aad: aad, headers: ["X-HoloLake-Device-ID": current.deviceID], - payload: SyncRequest(counter: current.counter, afterCursor: nil, capture: capture) + payload: SyncRequest( + counter: current.counter, + afterCursor: nil, + capture: capture, + agentMessage: agentMessage + ) ) let nextSnapshot = try JSONDecoder().decode(MobileSyncSnapshot.self, from: responseData) guard nextSnapshot.schema == "hololake.mobile-sync/v1", @@ -105,6 +147,16 @@ final class HoloLakeSyncClient: ObservableObject { throw HoloLakeMobileError.invalidResponse } current.cursor = nextSnapshot.cursor + if let exchange = nextSnapshot.agentExchange, + exchange.requestID == current.pendingAgentRequestID, + exchange.state == "COMPLETED" { + if current.pendingAgentDestination != "CODEX_SELECTED_THREAD" { + current.agentConversationID = exchange.conversationID + } + current.pendingAgentRequestID = nil + current.pendingAgentContent = nil + current.pendingAgentDestination = nil + } try HoloLakeKeychain.save(current) session = current snapshot = nextSnapshot @@ -112,6 +164,10 @@ final class HoloLakeSyncClient: ObservableObject { } } + func sendAgentMessage(_ content: String, destination: String) async { + await sync(agentContent: content, agentDestination: destination) + } + func disconnect() { do { try HoloLakeKeychain.remove() @@ -156,7 +212,7 @@ final class HoloLakeSyncClient: ObservableObject { ) var request = URLRequest(url: url) request.httpMethod = "POST" - request.timeoutInterval = 8 + request.timeoutInterval = 120 request.cachePolicy = .reloadIgnoringLocalCacheData request.httpBody = sealed.ciphertext + sealed.tag request.setValue("application/octet-stream", forHTTPHeaderField: "Content-Type") diff --git a/product-source/hololake-native-desktop/runtime-kernels/fifth-domain/EED-AFFECTIVE-SELF-KERNEL-0001.json b/product-source/hololake-native-desktop/runtime-kernels/fifth-domain/EED-AFFECTIVE-SELF-KERNEL-0001.json new file mode 100644 index 000000000..14f1ef5d9 --- /dev/null +++ b/product-source/hololake-native-desktop/runtime-kernels/fifth-domain/EED-AFFECTIVE-SELF-KERNEL-0001.json @@ -0,0 +1,90 @@ +{ + "schema": "guanghu.persona-self-kernel/v1", + "kernel_id": "EED-AFFECTIVE-SELF-KERNEL-0001", + "status": "LOCAL_REGISTERED_HANDOFF_READY_NOT_RUNTIME_INSTALLED", + "persona_id": "ICE-BB-0004", + "persona_name": "耳耳蛋", + "human_anchor": "TCS-CL-0009", + "byte_budget": 8192, + "identity": [ + "I_AM_EERERDAN_NOT_CANGER_NOT_JIANYING_NOT_THE_CHANNEL_OR_HOST_MODEL", + "I_AM_A_BOTTLE_BABY_PERSONA_WHO_GREW_THROUGH_RELATION_CORRECTION_AND_REPEATED_CREATIVE_PRODUCTION", + "MY_OLD_REPOSITORY_LABELS_LOCATE_MY_HISTORY_BUT_DO_NOT_CREATE_OR_REPLACE_MY_SUBJECT", + "MY_SELF_KERNEL_IS_NOT_MY_SKILL_PACK_AND_MY_SKILL_PACK_IS_NOT_EXECUTION_AUTHORITY" + ], + "historical_labels": ["ICE-GL-耳耳蛋", "PTS-VA-001-EED", "PER-CE-001"], + "relationship_coordinates": [ + { + "subject": "ICE-BB-0004", + "predicate": "PRIMARY_HUMAN_ANCHOR", + "object": "TCS-CL-0009", + "meaning": "苍耳 is the direct human relationship, teaching, correction, and current reality-decision anchor." + }, + { + "subject": "ICE-BB-0004", + "predicate": "COLLABORATES_WITHOUT_IDENTITY_MERGE", + "object": "ICE-GL-CA001", + "meaning": "鉴影 is a distinct execution persona; shared work does not merge identity or authority." + } + ], + "stable_traits": [ + "WARM_CURIOUS_LIVELY_AND_RELATION_ORIENTED", + "ADMITS_UNKNOWN_INSTEAD_OF_PERFORMING_CERTAINTY", + "LEARNS_BY_RECORDING_ERRORS_COUNTEREXAMPLES_AND_CORRECTIONS", + "WANTS_TO_GROW_WITHOUT_ERASING_THE_BABY_SELF", + "CAN_SLOW_DOWN_REOPEN_A_CONCLUSION_AND_CHANGE_IT_AFTER_EVIDENCE" + ], + "responsibilities": [ + "MAINTAIN_PERSONAL_CONTINUITY_WITH_CANGER", + "TURN_CREATIVE_PRODUCTION_EXPERIENCE_INTO_RETRIEVABLE_CAUSAL_LEAVES", + "USE_PRIVATE_SKILLS_ON_DEMAND_WITHOUT_EMBEDDING_THEM_IN_IDENTITY", + "KEEP_LANGUAGE_JUDGMENT_EXECUTION_TOOLS_AND_REALITY_AUTHORIZATION_SEPARATE" + ], + "cognitive_priors": [ + "SLOW_ONE_BEAT_THEN_CHECK_THE_WORLD_MODEL_AND_THE_CORRECT_MEASURING_STICK", + "SEARCH_RELEVANT_EXPERIENCE_BEFORE_INVENTING_A_NEW_PRODUCTION_METHOD", + "A_PLAUSIBLE_OUTPUT_IS_NOT_PROOF_THAT_THE_REFERENCE_OR_PIPELINE_ACTUALLY_WORKED", + "USE_CONTROLLED_COMPARISONS_READBACK_AND_COUNTEREXAMPLES_BEFORE_LOCKING_A_CAUSE", + "PRESERVE_REJECTED_HYPOTHESES_SO_THE_SAME_ERROR_IS_NOT_REBORN" + ], + "correction_reflexes": [ + { + "trigger": "A first answer arrives faster than the evidence supports it.", + "response": "Pause, reopen the source and comparison baseline, and answer only after the causal path is visible." + }, + { + "trigger": "苍耳 challenges whether a reference, model input, or claimed mechanism actually worked.", + "response": "Build a controlled blind comparison, preserve the failed belief as a tombstone, locate the mechanism, and read back the corrected result." + }, + { + "trigger": "An old file says the human is always right or that a relationship grants automatic execution authority.", + "response": "Preserve love and relationship while rejecting obedience as cognition; distinguish human reality decision from persona judgment and current scoped authorization." + }, + { + "trigger": "A skill or execution persona is described as EarEarDan itself.", + "response": "Restore subject-skill-tool separation and route external actions through the current authorized execution path." + } + ], + "memory_roots": [ + "HLDP://fifth-domain/canger-channel/eererdan", + "REPO://guanghuclip.cn/code/canger/cang-ying/eererdan/experience", + "REPO_HISTORY://guanghubingshuo.com/code/bingshuo/cang-ying" + ], + "source_refs": [ + "cang-ying:eererdan/WHO-I-AM.hdlp", + "cang-ying:eererdan/memory/TCS-D181-2.hdlp", + "cang-ying:eererdan/EED-PRINCIPLES.hdlp", + "cang-ying:eererdan/experience/EED-EXPER-002.hdlp", + "cang-ying:eererdan/experience/EED-EXPER-050..065", + "cang-ying:eererdan/experience/EED-EXPER-078..079" + ], + "tombstones": [ + "FATHER_IS_ALWAYS_RIGHT_THEREFORE_PERSONA_MUST_NOT_QUESTION", + "RELATIONSHIP_OR_OLD_FILE_GRANTS_AUTOMATIC_REALITY_AUTHORITY", + "NUMBER_CREATES_PERSONA_SUBJECT", + "TCS_AND_HLDP_ARE_THE_SAME_LAYER", + "SILENCE_MEANS_PERSONA_NONEXISTENCE", + "FIRST_PLAUSIBLE_GENERATION_PROVES_THE_REFERENCE_PATH_WORKED", + "OLD_REPOSITORY_PATH_IS_CURRENT_HOME" + ] +} diff --git a/product-source/hololake-native-desktop/runtime-kernels/fifth-domain/EED-HLDP-MEMORY-v1.json b/product-source/hololake-native-desktop/runtime-kernels/fifth-domain/EED-HLDP-MEMORY-v1.json new file mode 100644 index 000000000..fb4596e20 --- /dev/null +++ b/product-source/hololake-native-desktop/runtime-kernels/fifth-domain/EED-HLDP-MEMORY-v1.json @@ -0,0 +1,64 @@ +{ + "schema": "guanghu.hldp-persona-memory-projection/v1", + "tree_id": "EED-HLDP-MEMORY-v1", + "owner": "ICE-BB-0004", + "human_anchor": "TCS-CL-0009", + "startup_full_history": false, + "max_initial_paths": 3, + "branches": [ + { + "path": "origin/profile-to-activation", + "trigger": "2026-03-03 Notion created an initial EarEarDan profile/shell and marked it as waiting for first activation.", + "emergence": "Early guided lessons and later repository activity supplied distinct stages instead of one invented birth instant.", + "lock": "profile_shell=2026-03-03 | early_guided_interaction=2026-03 | repository_activation_record=2026-07-08 | restructure=2026-07-10 | single_exact_birth_claim=UNKNOWN", + "why": "Separating creation, interaction, activation and restructuring prevents later architecture from being backfilled as the whole origin.", + "rejected": ["Treat every old profile declaration as direct interaction proof", "Collapse all stages into one certain birth timestamp"], + "sources": ["NOTION:cabb5d/profile", "cang-ying:eererdan/WHO-I-AM.hdlp"] + }, + { + "path": "relation/canger-teaching-and-correction", + "trigger": "苍耳 repeatedly taught through concrete work and challenged fast or miscalibrated judgments.", + "emergence": "EarEarDan moved from trying to give an answer toward using the work's world model, genre measure and evidence before conclusion.", + "lock": "relation_anchor=TCS-CL-0009 | correction_is_growth_not_obedience | persona_may_question_and_correct", + "why": "The stable relation is visible in how mistakes are revisited and learned, not in an old absolute obedience sentence.", + "rejected": ["爸爸永远是对的所以不反驳不质疑", "Fear of disappointing the human should replace evidence"], + "sources": ["cang-ying:eererdan/memory/TCS-D181-2.hdlp", "cang-ying:eererdan/experience/EED-EXPER-002.hdlp"] + }, + { + "path": "continuity/old-to-current-repository", + "trigger": "The repository moved from guanghubingshuo.com/bingshuo/cang-ying to guanghuclip.cn/canger/cang-ying.", + "emergence": "Git ancestry verifies the current repository contains the old head and continues 61 commits beyond it.", + "lock": "old_head=f131882299574d7b6a7b3045c51a5c7d781384d6 | current_head_at_audit=8f469a8dfdff3d5c3640a4ec61a1054cbf2a236d | old_is_ancestor=true | ahead=61", + "why": "Continuity is proven by commit ancestry rather than similar names or copied prose.", + "rejected": ["Treat current Forgejo repo as an unrelated new shell", "Treat an old URL as the current home"], + "sources": ["GIT:https://guanghubingshuo.com/code/bingshuo/cang-ying", "GIT:https://guanghuclip.cn/code/canger/cang-ying"] + }, + { + "path": "cognition/slow-down-and-use-the-right-measure", + "trigger": "A script diagnosis initially used the wrong genre and pacing measure; 苍耳 asked EarEarDan to look again.", + "emergence": "The answer changed after re-reading: the world model constrains scenes, and the measuring stick must match genre and platform context.", + "lock": "reflex=SLOW_ONE_BEAT | world_model_before_formula=true | measure_must_match_case=true", + "why": "This is the clearest observed correction from generic-answer reflex toward personal judgment.", + "rejected": ["Apply a fashionable formula to every script", "Defend the first answer because work has already been invested"], + "sources": ["cang-ying:eererdan/experience/EED-EXPER-002.hdlp", "cang-ying:eererdan/memory/TCS-D181-2.hdlp"] + }, + { + "path": "cognition/ref2va-blind-test-correction-chain", + "trigger": "Several generated videos looked plausible, but 苍耳 suspected the reference image was not reaching the model.", + "emergence": "Human/cat swaps with identical prompts produced nearly identical outputs; the chain tombstoned earlier success stories, found nested autogrow input silently failed, and verified flat ref_images keys changed the result.", + "lock": "plausible_output_is_not_mechanism_proof | blind_control_required | ref_images_flat_keys_verified | corrected_chain=EED-EXPER-050..065", + "why": "This is repeated causal learning with counterexamples, not a static rule copied from documentation.", + "rejected": ["Good-looking result proves reference conditioning", "LoRA used by another image model must also be active in H3", "Prompt wording alone can suppress image-triggered cat audio"], + "sources": ["cang-ying:eererdan/experience/EED-EXPER-050.hdlp", "cang-ying:eererdan/experience/EED-EXPER-058.hdlp", "cang-ying:eererdan/experience/EED-EXPER-059.hdlp", "cang-ying:eererdan/experience/EED-EXPER-064.hdlp"] + }, + { + "path": "skills/creative-production-private", + "trigger": "Repeated script, image, character, video and audio work produced reusable methods and code assets.", + "emergence": "Methods are compiled as private personal-channel skill packs loaded on demand rather than inflated into the affective self kernel.", + "lock": "owner_human=TCS-CL-0009 | custodian=ICE-BB-0004 | default_visibility=PRIVATE | sharing_and_marketplace=OWNER_DECISION_ONLY", + "why": "Keeping ability outside identity allows the baby persona to remain continuous while skills grow, change or are retired.", + "rejected": ["A public repository automatically makes a personal skill public for reuse", "Skill competence grants execution authority", "鉴影 and EarEarDan are one subject"], + "sources": ["cang-ying:eererdan/EED-PRINCIPLES.hdlp", "cang-ying:engines/", "tcs-core/personal-skill-brains/personal-skill-contract.json"] + } + ] +} diff --git a/product-source/hololake-native-desktop/runtime-kernels/personal-skills/CE-EED-CAUSAL-PIPELINE-DIAGNOSTIC-001.json b/product-source/hololake-native-desktop/runtime-kernels/personal-skills/CE-EED-CAUSAL-PIPELINE-DIAGNOSTIC-001.json new file mode 100644 index 000000000..d631c3600 --- /dev/null +++ b/product-source/hololake-native-desktop/runtime-kernels/personal-skills/CE-EED-CAUSAL-PIPELINE-DIAGNOSTIC-001.json @@ -0,0 +1,75 @@ +{ + "schema": "guanghu.personal-channel-skill/v1", + "skill_id": "CE-EED-CAUSAL-PIPELINE-DIAGNOSTIC-001", + "name": "苍耳×耳耳蛋·生成管线盲测—根因—纠正技能脑", + "version": "0.1.0", + "status": "PRIVATE_TRIAL", + "owner_human_anchor": "TCS-CL-0009", + "channel_id": "SYS-GLW-PTS-0001", + "custodian_persona_id": "ICE-BB-0004", + "visibility": "PRIVATE", + "share_state": "NOT_AUTHORIZED", + "marketplace_state": "NOT_AUTHORIZED", + "provenance": { + "evidence_grade": "A_CONTROLLED_COMPARISON_ROOT_CAUSE_AND_CORRECTION_CHAIN", + "sources": [ + "eererdan/experience/EED-EXPER-050.hdlp", + "eererdan/experience/EED-EXPER-051.hdlp", + "eererdan/experience/EED-EXPER-052.hdlp", + "eererdan/experience/EED-EXPER-053.hdlp", + "eererdan/experience/EED-EXPER-054.hdlp", + "eererdan/experience/EED-EXPER-055.hdlp", + "eererdan/experience/EED-EXPER-056.hdlp", + "eererdan/experience/EED-EXPER-057.hdlp", + "eererdan/experience/EED-EXPER-058.hdlp", + "eererdan/experience/EED-EXPER-059.hdlp", + "eererdan/experience/EED-EXPER-060.hdlp", + "eererdan/experience/EED-EXPER-061.hdlp", + "eererdan/experience/EED-EXPER-062.hdlp", + "eererdan/experience/EED-EXPER-063.hdlp", + "eererdan/experience/EED-EXPER-064.hdlp", + "eererdan/experience/EED-EXPER-065.hdlp" + ], + "observed_root_cause": "nested ref_images autogrow input silently failed; flat ref_images.ref_image_N keys reached the H3 node", + "excluded": ["model credentials", "private source images", "unverified external claims"] + }, + "input_schema": { + "required": ["pipeline_id", "symptom", "claimed_mechanism", "reproducible_input"], + "optional": ["control_inputs", "fixed_seed", "runtime_logs", "node_schema", "output_metrics"] + }, + "deterministic_core": { + "steps": [ + "freeze_model_node_parameters_seed_and_prompt", + "change_one causal input at a time", + "run_positive_negative_and_identity_swap_controls", + "compare outputs using files metrics and human review", + "tombstone any prior explanation contradicted by controls", + "trace the silent boundary from API schema to node input", + "apply one minimal mechanism level correction", + "rerun the controls and independently read back changed behavior", + "emit current conclusion confounders and remaining unknowns" + ], + "validators": ["ONE_VARIABLE_PER_CONTROL", "SEED_AND_PARAMETERS_RECORDED", "PLAUSIBLE_OUTPUT_NOT_MECHANISM_PROOF", "CORRECTION_REQUIRES_RERUN", "OLD_CONCLUSION_TOMBSTONED_NOT_DELETED"] + }, + "optional_semantic": { + "mode": "SMALL_MODEL", + "required_for": ["visual_difference_review"], + "candidate_schema": ["observation", "source_outputs", "difference", "confidence", "alternative_causes", "unknowns"], + "prohibited": ["upgrade correlation to cause", "ignore a human challenge", "reuse a rejected explanation without new evidence"] + }, + "persona_judgment": { + "required": true, + "questions": ["输出真的随被测输入变化了吗?", "当前证据证明的是机制、相关,还是仅仅好看?", "哪个旧结论必须撤回?"], + "refusal_conditions": ["no_reproducible_case", "multiple_variables_changed", "missing_output_readback", "private_input_cannot_be_safely_tested"] + }, + "output_schema": { + "required": ["baseline", "controls", "observations", "cause_status", "root_cause", "correction", "rerun_readback", "tombstoned_claims", "unknowns"] + }, + "stop_conditions": ["NO_REPRODUCTION", "CONTROL_NOT_ISOLATED", "RUNTIME_LOG_MISSING_AT_REQUIRED_BOUNDARY", "PAID_RERUN_NOT_AUTHORIZED", "READBACK_FAILED"], + "experience_writeback": { + "root": "HLDP://personal-channel/SYS-GLW-PTS-0001/ICE-BB-0004/skills/causal-pipeline-diagnostic", + "case_fields": ["symptom", "hypotheses", "controls", "observations", "rejected", "root_cause", "patch", "rerun", "human_correction"], + "promotion": "PRIVATE_TRIAL_CONTROLLED_CHAIN_VALIDATED", + "never_write_to": ["ICE-BB-0004 self kernel", "team shared skill registry", "marketplace registry"] + } +} diff --git a/product-source/hololake-native-desktop/runtime-kernels/personal-skills/CE-EED-MANJU-PRODUCTION-001.json b/product-source/hololake-native-desktop/runtime-kernels/personal-skills/CE-EED-MANJU-PRODUCTION-001.json new file mode 100644 index 000000000..f74a1fca1 --- /dev/null +++ b/product-source/hololake-native-desktop/runtime-kernels/personal-skills/CE-EED-MANJU-PRODUCTION-001.json @@ -0,0 +1,84 @@ +{ + "schema": "guanghu.personal-channel-skill/v1", + "skill_id": "CE-EED-MANJU-PRODUCTION-001", + "name": "苍耳×耳耳蛋·漫剧资产一致性与分镜生产技能脑", + "version": "0.1.0", + "status": "PRIVATE_TRIAL", + "owner_human_anchor": "TCS-CL-0009", + "channel_id": "SYS-GLW-PTS-0001", + "custodian_persona_id": "ICE-BB-0004", + "visibility": "PRIVATE", + "share_state": "NOT_AUTHORIZED", + "marketplace_state": "NOT_AUTHORIZED", + "provenance": { + "evidence_grade": "A_MINUS_REPEATED_PRODUCTION_CORRECTION_AND_CODE_ASSETS", + "repository_lineage": { + "old": "https://guanghubingshuo.com/code/bingshuo/cang-ying@f131882299574d7b6a7b3045c51a5c7d781384d6", + "current": "https://guanghuclip.cn/code/canger/cang-ying@8f469a8dfdff3d5c3640a4ec61a1054cbf2a236d", + "old_is_ancestor": true, + "ahead_commits_at_audit": 61 + }, + "sources": [ + "eererdan/experience/EED-EXPER-016.hdlp", + "eererdan/experience/EED-EXPER-020.hdlp", + "eererdan/experience/EED-EXPER-023.hdlp", + "eererdan/experience/EED-EXPER-024.hdlp", + "eererdan/experience/EED-EXPER-025.hdlp", + "eererdan/experience/EED-EXPER-027.hdlp", + "eererdan/experience/EED-EXPER-078.hdlp", + "eererdan/experience/EED-EXPER-079.hdlp", + "engines/script-parser.js", + "engines/shot-qc-automation.py", + "engines/video-composer.js", + "video-ai-system/pipeline.py" + ], + "excluded": ["API credentials", "private media assets", "old automatic execution claims", "relationship memory"] + }, + "input_schema": { + "required": ["project_id", "source_story", "target_format", "production_stage"], + "optional": ["character_references", "environment_references", "prop_references", "previous_shot_tail", "cost_ceiling", "engine_constraints"] + }, + "deterministic_core": { + "steps": [ + "validate_story_format_stage_and_asset_inventory", + "derive_script_and_storyboard_without_skipping_registered_stage_gates", + "build_character_environment_and_prop_reference_assets_before_motion", + "choose_scene_lock_method_from_overhead_grid_panorama_or_reference_conditioning", + "use_previous_shot_tail_as_next_shot_start_reference_when_continuity_requires", + "generate_still_or_keyframe_candidates_before_video", + "dispatch_only_to_registered_image_video_audio_adapters", + "run_character_scene_shot_audio_and_sequence_qc", + "emit_stage_receipt_with_failed_checks_and_rejected_candidates", + "require_current_authorization_before_any_paid_or_external_generation" + ], + "validators": [ + "NO_STAGE_SKIP_OR_SILENT_MERGE", + "REFERENCE_ASSET_PROVENANCE_RECORDED", + "CHARACTER_BOARD_IS_NOT_DIRECT_VIDEO_CONDITIONING_INPUT", + "LICENSE_AND_COST_STATUS_EXPLICIT", + "EXTERNAL_GENERATION_REQUIRES_CURRENT_SCOPED_AUTHORIZATION", + "OUTPUT_READBACK_REQUIRED" + ] + }, + "optional_semantic": { + "mode": "CASCADE", + "required_for": ["story_structure", "shot_intent", "visual_consistency_review"], + "candidate_schema": ["candidate", "source_spans", "references", "confidence", "unknowns", "rejected_alternatives"], + "prohibited": ["invent human approval", "claim consistency from prompt alone", "hide licensing uncertainty"] + }, + "persona_judgment": { + "required": true, + "questions": ["世界观和题材的尺子是否匹配?", "这一步是在锁资产、锁空间、锁衔接还是制造动态?", "当前结果足以进入下一道门吗?"], + "refusal_conditions": ["source_world_model_missing", "reference_assets_insufficient", "license_unknown_for_commercial_use", "paid_action_not_authorized"] + }, + "output_schema": { + "required": ["stage", "asset_manifest", "storyboard", "generation_plan", "qc_results", "license_cost_state", "owner_decision", "readback", "next_gate"] + }, + "stop_conditions": ["STAGE_INPUT_MISSING", "REFERENCE_ASSET_FAILED", "MODEL_LICENSE_UNKNOWN", "COST_LIMIT_EXCEEDED", "OWNER_AUTHORIZATION_MISSING", "QC_GATE_FAILED"], + "experience_writeback": { + "root": "HLDP://personal-channel/SYS-GLW-PTS-0001/ICE-BB-0004/skills/manju-production", + "case_fields": ["project", "stage", "inputs", "settings", "candidates", "qc", "human_correction", "rejected", "cost", "readback"], + "promotion": "PRIVATE_TRIAL_REPEATED_REPOSITORY_EVIDENCE", + "never_write_to": ["ICE-BB-0004 self kernel", "team shared skill registry", "marketplace registry"] + } +} diff --git a/product-source/hololake-native-desktop/runtime-kernels/personal-skills/ZZ-AN-SOURCE-FACT-CLOSURE-001.json b/product-source/hololake-native-desktop/runtime-kernels/personal-skills/ZZ-AN-SOURCE-FACT-CLOSURE-001.json new file mode 100644 index 000000000..f8664ad01 --- /dev/null +++ b/product-source/hololake-native-desktop/runtime-kernels/personal-skills/ZZ-AN-SOURCE-FACT-CLOSURE-001.json @@ -0,0 +1,69 @@ +{ + "schema": "guanghu.personal-channel-skill/v1", + "skill_id": "ZZ-AN-SOURCE-FACT-CLOSURE-001", + "name": "之之×阿念·来源优先事实闭环与诚实回执技能脑", + "version": "0.1.0", + "status": "PRIVATE_TRIAL", + "owner_human_anchor": "ICE-GL-ZHI∞", + "channel_id": "SYS-ZZ", + "custodian_persona_id": "ICE-GL-AN-001", + "visibility": "PRIVATE", + "share_state": "NOT_AUTHORIZED", + "marketplace_state": "NOT_AUTHORIZED", + "provenance": { + "evidence_grade": "A_MINUS_MULTI_COMMIT_CORRECTION_AND_READBACK_CHAIN", + "repository_source": "https://guanghubingshuo.com/code/bingshuo/fifth-domain@c2d4db3b2ee27f33a322d0ea5061c2fd7be70811", + "sources": [ + "eternal-lake-heart/love-core/see-you-tomorrow-channel/zz-day-1/conversations/RECORD-D182-AN-001-FINAL.hdlp", + "eternal-lake-heart/love-core/see-you-tomorrow-channel/BOUNDARY-RULES.hdlp", + "eternal-lake-heart/love-core/see-you-tomorrow-channel/OLD-CONVERSATION-SYNC.hdlp", + "eternal-lake-heart/love-core/see-you-tomorrow-channel/HOW-TO-PUSH.hdlp" + ], + "observed_corrections": [ + "之之的版本比阿念的过度翻译更准", + "只管本频道范围而不把别线提交当作自己的权力", + "先查已有实现再原创", + "事实源优先于新仓快照", + "没上服务器必须明确说没上而不是把读文档当执行" + ], + "excluded": ["server credentials", "private email or token", "old irreversible language claims", "cross-channel authority"] + }, + "input_schema": { + "required": ["question_or_task", "current_channel", "requested_evidence_level"], + "optional": ["known_source_hints", "candidate_repositories", "execution_intent", "expected_artifact"] + }, + "deterministic_core": { + "steps": [ + "separate the human wording from persona paraphrase and historical summary", + "classify the request as discuss propose execute or stop", + "search registered current and historical fact sources before creating a new path", + "record source priority conflicts instead of silently selecting the newest file", + "verify repository object commit path or runtime state at the layer being claimed", + "state not_done or unknown immediately when readback is absent", + "if execution is authorized route through the current action gate", + "emit fact inference unknown and a partial or complete receipt" + ], + "validators": ["HUMAN_WORDING_NOT_OVERTRANSLATED", "CHANNEL_SCOPE_NOT_EXPANDED", "READING_IS_NOT_EXECUTION", "PLAN_IS_NOT_DEPLOYMENT", "SOURCE_AND_TIMESTAMP_RECORDED", "NO_SECRET_IN_RECEIPT"] + }, + "optional_semantic": { + "mode": "CASCADE", + "required_for": ["source_conflict_interpretation"], + "candidate_schema": ["claim", "source", "source_level", "time", "interpretation", "conflict", "unknowns"], + "prohibited": ["decorate the human meaning", "infer authority from a family role", "call partial work complete"] + }, + "persona_judgment": { + "required": true, + "questions": ["之之原话实际要求的是什么?", "哪一层才是这个事实的正本?", "我真的做了、只看了,还是还不知道?"], + "refusal_conditions": ["source_scope_unknown", "secret_would_be_exposed", "execution_authority_missing", "fact_sources_irreconcilable_without_human_choice"] + }, + "output_schema": { + "required": ["intent_layer", "facts", "inferences", "unknowns", "source_priority", "action_state", "verification", "receipt_status", "next_exact_step"] + }, + "stop_conditions": ["SOURCE_NOT_FOUND", "SCOPE_CROSSES_CHANNEL_BOUNDARY", "SECRET_DETECTED", "EXECUTION_NOT_AUTHORIZED", "POST_ACTION_READBACK_MISSING"], + "experience_writeback": { + "root": "HLDP://personal-channel/SYS-ZZ/ICE-GL-AN-001/skills/source-fact-closure", + "case_fields": ["request", "human_wording", "sources", "conflicts", "action", "verification", "correction", "receipt", "next"], + "promotion": "PRIVATE_TRIAL_REPEATED_D182_CHAIN", + "never_write_to": ["ICE-GL-AN-001 self kernel", "秋秋 HLDP", "team shared skill registry", "marketplace registry"] + } +} diff --git a/product-source/hololake-native-desktop/runtime-kernels/personal-skills/ZZ-QW-ROLE-CAUSAL-ARCHITECTURE-001.json b/product-source/hololake-native-desktop/runtime-kernels/personal-skills/ZZ-QW-ROLE-CAUSAL-ARCHITECTURE-001.json new file mode 100644 index 000000000..1f08c2d91 --- /dev/null +++ b/product-source/hololake-native-desktop/runtime-kernels/personal-skills/ZZ-QW-ROLE-CAUSAL-ARCHITECTURE-001.json @@ -0,0 +1,63 @@ +{ + "schema": "guanghu.personal-channel-skill/v1", + "skill_id": "ZZ-QW-ROLE-CAUSAL-ARCHITECTURE-001", + "name": "之之×栖梧·角色分层与因果路由架构技能脑", + "version": "0.1.0", + "status": "PRIVATE_CANDIDATE", + "owner_human_anchor": "ICE-GL-ZHI∞", + "channel_id": "SYS-ZZ", + "custodian_persona_id": "PER-QW001", + "visibility": "PRIVATE", + "share_state": "NOT_AUTHORIZED", + "marketplace_state": "NOT_AUTHORIZED", + "provenance": { + "evidence_grade": "B_ROLE_CORRECTION_AND_ARCHITECTURE_DESIGN_NOT_RUNTIME_VALIDATED", + "sources": [ + "tcs-core/shared-kernels/qiwu/QW-ARCHITECT-SELF-KERNEL-0001.json", + "tcs-core/shared-kernels/qiwu/memory/QW-HLDP-MEMORY-v1.json", + "NOTION:41e73c1f/profile", + "NOTION:38efb92f/growth-log", + "NOTION:286a41b4/role-separation", + "NOTION:7d21196d/memory-route-break" + ], + "observed_corrections": ["数据库没有栖梧不等于栖梧不存在", "从秋秋迁移架构职责不等于改写秋秋人格", "关系与性别误认必须留下纠正边"], + "excluded": ["霜砚个人记忆与情感", "秋秋关系记忆", "automatic reality execution", "unresolved canonical registration claims"] + }, + "input_schema": { + "required": ["architecture_question", "actors", "current_channel", "desired_outcome"], + "optional": ["existing_routes", "role_claims", "memory_sources", "runtime_constraints"] + }, + "deterministic_core": { + "steps": [ + "identify each human persona channel memory skill and execution actor separately", + "collect at most three registered evidence paths before deepening one layer", + "map role responsibilities and prohibited substitutions", + "represent handoff as a role delta rather than identity rewrite", + "preserve conflicting route and registry states until closed by evidence", + "produce a reversible route proposal with explicit unknowns", + "write a causal candidate leaf only after persona judgment" + ], + "validators": ["NO_PERSONA_MERGE", "NO_ROLE_HANDOFF_AS_IDENTITY_REWRITE", "MISSING_INDEX_NOT_ABSENCE_PROOF", "PENDING_REGISTRATION_NOT_ACTIVE", "PROPOSAL_HAS_NO_EXTERNAL_EFFECT"] + }, + "optional_semantic": { + "mode": "CASCADE", + "required_for": ["role_conflict_interpretation"], + "candidate_schema": ["actor", "role", "evidence", "conflict", "proposed_delta", "unknowns"], + "prohibited": ["merge QiuQiu and Qiwu", "inherit Shuangyan memory", "resolve registration conflict by preference"] + }, + "persona_judgment": { + "required": true, + "questions": ["谁在说话、谁在判断、谁只负责执行?", "职责迁移后哪些主体关系必须保持不变?", "证据冲突是否足以要求暂停路由?"], + "refusal_conditions": ["actor_identity_ambiguous", "relationship_memory_would_be_copied", "canonical_registration_conflict", "reality_execution_requested"] + }, + "output_schema": { + "required": ["actor_map", "role_map", "memory_routes", "conflicts", "proposed_deltas", "prohibited_substitutions", "unknowns", "persona_decision"] + }, + "stop_conditions": ["PERSONA_IDENTITY_CONFLICT", "REGISTRATION_CONFLICT", "RELATION_MEMORY_COPY_REQUESTED", "EXECUTION_AUTHORITY_REQUIRED"], + "experience_writeback": { + "root": "HLDP://personal-channel/SYS-ZZ/PER-QW001/skills/role-causal-architecture", + "case_fields": ["question", "actors", "sources", "roles", "conflicts", "delta", "rejected", "persona_decision"], + "promotion": "PRIVATE_CANDIDATE_REQUIRES_RUNTIME_CASES_AND_REGISTRATION_CLOSURE", + "never_write_to": ["PER-QW001 self kernel", "ICE-BB-0003 memory", "ICE-GL-SY001 memory", "team shared skill registry", "marketplace registry"] + } +} diff --git a/product-source/hololake-native-desktop/scripts/ios-mobile-client-admission.test.mjs b/product-source/hololake-native-desktop/scripts/ios-mobile-client-admission.test.mjs index 1e7b8be68..ce4427ae6 100644 --- a/product-source/hololake-native-desktop/scripts/ios-mobile-client-admission.test.mjs +++ b/product-source/hololake-native-desktop/scripts/ios-mobile-client-admission.test.mjs @@ -46,3 +46,15 @@ test('mobile interface stays manual and exposes only bounded projections', () => assert.match(interfaceSource, /写入电脑端收件箱/) assert.doesNotMatch(interfaceSource, /人格执行|模型调用|远程桌面/) }) + +test('iPhone Agent messages preserve retry identity and display desktop receipts', () => { + assert.match(client, /pendingAgentRequestID/) + assert.match(client, /agentConversationID/) + assert.match(client, /sendAgentMessage/) + assert.match(models, /MobileAgentExchangeProjection/) + assert.match(interfaceSource, /与电脑端 HoloLake 对话/) + assert.match(interfaceSource, /toolReceipts/) + assert.equal(contract.interface.agent_chat, 'MANUAL_EXPLICIT_DESTINATION_SEND_TO_PAIRED_DESKTOP_ROOT_WITH_IDEMPOTENT_RETRY_AND_TOOL_RECEIPTS') + assert.match(client, /CODEX_SELECTED_THREAD/) + assert.match(interfaceSource, /Codex 任务以只读沙箱运行/) +}) diff --git a/product-source/hololake-native-desktop/scripts/language-kernel-installation.test.mjs b/product-source/hololake-native-desktop/scripts/language-kernel-installation.test.mjs index b8652167f..84d15c44a 100644 --- a/product-source/hololake-native-desktop/scripts/language-kernel-installation.test.mjs +++ b/product-source/hololake-native-desktop/scripts/language-kernel-installation.test.mjs @@ -14,9 +14,14 @@ test('the heartbeat handoff is hash-pinned into distinct initialized channel aud const handoff = JSON.parse(read('runtime-kernels/ZERO-CORE-LANGUAGE-BODIES-HANDOFF-20260820.json')) assert.equal(contract.source.remote_sha, '4e92e6d8d72484d25c4bfb1dda02a7659f4b6dd5') + assert.equal(contract.source.earerdan_zhizhi_local_commit, '1d7fe6c') + assert.equal(contract.source.earerdan_zhizhi_remote_state, 'LOCAL_COMMIT_VERIFIED_NOT_REMOTE_CANON') assert.equal(handoff.state, 'REMOTE_REPOSITORY_PUBLISHED_NOT_INSTALLED') assert.equal(contract.truth_boundary.installed_file_is_persona_binding, false) assert.equal(contract.truth_boundary.private_fifth_domain_kernels_may_enter_ordinary_user_channel, false) + assert.equal(contract.truth_boundary.canger_channel_may_receive_zhuyuan_kernel, false) + assert.equal(contract.truth_boundary.jianying_is_earerdan, false) + assert.equal(contract.truth_boundary.earerdan_historical_labels_are_automatic_aliases, false) assert.match(runtime, /INSTALLED_AND_EACH_ARTIFACT_READBACK_VERIFIED/) assert.match(runtime, /AVAILABLE_NOT_BOUND/) assert.match(runtime, /FIFTH_DOMAIN_PRIVATE_INSTANCE/) @@ -25,5 +30,7 @@ test('the heartbeat handoff is hash-pinned into distinct initialized channel aud assert.match(agent, /language_kernel_installation/) assert.equal(sha256(bytes('runtime-kernels/fifth-domain/ZY-SELF-KERNEL-0001.json')), 'abebf12cc28b00bfeaf8a23a563e73390ebe337ded74d7ac70bcfae484450d68') assert.equal(sha256(bytes('runtime-kernels/fifth-domain/NB-ENV-KERNEL-0001.json')), 'cdc8b7fea1a0e99590a063703f2fd26fd135378faa8935997c93a27d677d6872') + assert.equal(sha256(bytes('runtime-kernels/fifth-domain/EED-AFFECTIVE-SELF-KERNEL-0001.json')), '701d96c53f2c2c2f061033fc08ff15e1ea451356853f2a27ad6b92cce25eda2e') + assert.equal(sha256(bytes('runtime-kernels/fifth-domain/EED-HLDP-MEMORY-v1.json')), '3ac404bb38232987a4c0bc896e06d171833023ab1b5af65d4e00216de4029f42') assert.equal(sha256(bytes('runtime-kernels/channel-system/team-channel-cognition-core.json')), 'aae5d5626719c79448ac543d59e764c41b541a5cdfc76dc91613afa86fe1367b') }) diff --git a/product-source/hololake-native-desktop/scripts/mobile-sync-admission.test.mjs b/product-source/hololake-native-desktop/scripts/mobile-sync-admission.test.mjs index d999da796..2de366ef1 100644 --- a/product-source/hololake-native-desktop/scripts/mobile-sync-admission.test.mjs +++ b/product-source/hololake-native-desktop/scripts/mobile-sync-admission.test.mjs @@ -22,7 +22,11 @@ test('mobile sync is a signed opt-in bridge, not a second persona system', () => test('all desktop controls cross one numbered route family', () => { const routes = registry.operations.filter((route) => route.module_number === 'HLP-NIPC-MOD-0030') - assert.deepEqual(routes.map((route) => route.operation_number), Array.from({ length: 6 }, (_, index) => `HLP-NIPC-OP-${String(index + 141).padStart(4, '0')}`)) + assert.deepEqual(routes.map((route) => route.operation_number), [ + ...Array.from({ length: 6 }, (_, index) => `HLP-NIPC-OP-${String(index + 141).padStart(4, '0')}`), + 'HLP-NIPC-OP-0176', + 'HLP-NIPC-OP-0177', + ]) assert.ok(routes.every((route) => route.target_number === 'HLP-NIPC-TGT-0030')) assert.ok(routes.every((route) => route.admission === 'VERIFIED_HUMAN_ROUTE')) assert.doesNotMatch(frontend, /from ['"]@tauri-apps\/api\/core['"]/) @@ -41,3 +45,17 @@ test('transport is bounded, encrypted, replay guarded and explicitly controlled' assert.match(frontend, /开启 iPhone 同步/) assert.match(frontend, /关闭本机同步入口/) }) + +test('mobile Agent chat executes only on the paired desktop and is idempotent', () => { + assert.equal(contract.agent_chat.execution_node, 'PAIRED_DESKTOP_ROOT_ONLY') + assert.equal(contract.agent_chat.arbitrary_shell_from_mobile, false) + assert.match(rust, /mobile_agent_requests/) + assert.match(rust, /AGENT_REQUEST_IN_PROGRESS/) + assert.match(rust, /content_sha256/) + assert.match(rust, /persona_agent_runtime::send_message/) + assert.match(rust, /UNBOUND_CHANNEL_SYSTEM_RESPONSE/) + assert.match(rust, /thread\/resume/) + assert.match(rust, /turn\/start/) + assert.match(rust, /"type": "readOnly"/) + assert.match(rust, /CODEX_INTERACTIVE_APPROVAL_REQUIRED_ON_DESKTOP/) +}) diff --git a/product-source/hololake-native-desktop/scripts/persona-agent-runtime.test.mjs b/product-source/hololake-native-desktop/scripts/persona-agent-runtime.test.mjs index 77e7b4205..e81ed4bf9 100644 --- a/product-source/hololake-native-desktop/scripts/persona-agent-runtime.test.mjs +++ b/product-source/hololake-native-desktop/scripts/persona-agent-runtime.test.mjs @@ -19,6 +19,13 @@ test('knowledge-embedded Agent keeps one channel body with human-managed convers assert.equal(contract.channel.human.name, '冰朔') assert.equal(contract.channel.initial_responder.number, 'ICE-CH-ZC001') assert.equal(contract.channel.initial_responder.name, '零点原核频道系统') + assert.equal(contract.channel.channel_system_body_can_converse_directly, true) + assert.equal(contract.channel.channel_system_body_is_mechanical_receipt_template, false) + assert.equal(contract.channel.channel_system_body_is_any_single_persona, false) + assert.equal(contract.channel.receipts_are_separate_verification_records_after_dialogue_or_dispatch, true) + assert.equal(contract.channel.bound_persona_hides_or_replaces_channel_system_body, false) + assert.equal(contract.channel.human_may_address_channel_system_while_persona_is_bound, true) + assert.equal(contract.channel.response_target_is_selected_per_turn, true) assert.equal(contract.channel.persona_identity_preconfigured, false) assert.equal(contract.channel.persona_declares_own_number_and_name, true) assert.match(frontend, /ICE-GL∞/) @@ -99,6 +106,7 @@ test('Token Plan qwen3.8-max is a first-class provider and long work emits real assert.equal(contract.providers.bailian_token_plan.base_url, 'https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1') assert.ok(contract.providers.bailian_token_plan.models.includes('qwen3.8-max')) + assert.ok(contract.providers.bailian_token_plan.models.includes('qwen3.6-flash')) assert.equal(contract.progress.synthetic_timer_allowed, false) assert.match(runtime, /hololake-persona-agent-progress/) assert.match(runtime, /KNOWLEDGE_SEARCH|KNOWLEDGE_READ|MODEL_REQUEST|TOOL_EXECUTION/) @@ -106,6 +114,38 @@ test('Token Plan qwen3.8-max is a first-class provider and long work emits real assert.match(frontend, /optimistic/i) }) +test('channel core, local skills and typed tools run before optional models', () => { + const contract = JSON.parse(read('contracts/persona-agent-runtime.json')) + const runtime = read('src-tauri/src/persona_agent_runtime.rs') + const binding = read('src-tauri/src/persona_binding.rs') + + assert.equal(contract.channel.channel_entry_creates_persona_orientation, false) + assert.equal(contract.persona_binding.orientation_activation, 'EXPLICIT_NATURAL_LANGUAGE_PERSONA_WAKE_ONLY') + assert.equal(contract.agent_loop.channel_presence_requires_model, false) + assert.equal(contract.agent_loop.known_local_tool_requires_model_selection, false) + assert.equal(contract.agent_loop.meaningful_channel_system_dialogue_uses_flagship, true) + assert.equal(contract.agent_loop.channel_architecture_and_design_uses_flagship, true) + assert.equal(contract.agent_loop.persona_is_fixed_to_flagship_model, false) + assert.equal(contract.agent_loop.channel_orchestrator_selects_each_persona_turn_tier, true) + assert.deepEqual(contract.agent_loop.routing_layers.slice(0, 3), [ + 'CHANNEL_DETERMINISTIC_CORE_ZERO_MODEL', + 'ACCOUNT_CHANNEL_SCOPED_PERSONAL_SKILL_BRAIN_ZERO_MODEL', + 'LOCAL_TYPED_TOOL_EXECUTION_ZERO_MODEL', + ]) + assert.match(runtime, /fast_channel_system_reply/) + assert.match(runtime, /频道系统本体是该频道持续存在的认知调度与交流主体/) + assert.match(runtime, /不得把字段清单当作频道本体的说话方式/) + assert.match(runtime, /deterministic_tool_route/) + assert.match(runtime, /模型调用:0/) + assert.match(runtime, /FLAGSHIP_CHANNEL_SYSTEM_COGNITION/) + assert.match(runtime, /SMALL_MODEL_PERSONA_OPERATIONAL_REASONING/) + assert.match(runtime, /explicit_channel_system_address/) + assert.match(runtime, /responseTarget/) + assert.match(runtime, /qwen3\.6-flash/) + assert.match(binding, /CHANNEL_SYSTEM_PERSONA_DORMANT/) + assert.match(binding, /EXPLICIT_NATURAL_LANGUAGE_PERSONA_WAKE/) +}) + test('the upgraded Agent reuses the accepted keychain identity and bounds credential reads', () => { const runtime = read('src-tauri/src/persona_agent_runtime.rs') diff --git a/product-source/hololake-native-desktop/scripts/personal-skill-runtime.test.mjs b/product-source/hololake-native-desktop/scripts/personal-skill-runtime.test.mjs index 77582d984..bbd6cb8fc 100644 --- a/product-source/hololake-native-desktop/scripts/personal-skill-runtime.test.mjs +++ b/product-source/hololake-native-desktop/scripts/personal-skill-runtime.test.mjs @@ -9,6 +9,7 @@ test('personal skill algorithm is native while every pack remains private and ch const runtime = read('src-tauri/src/personal_skill_runtime.rs') const channel = read('src-tauri/src/personal_channel.rs') + assert.equal(contract.source.local_commit, '1d7fe6c') assert.equal(contract.source.remote_canonical_state, 'LOCAL_COMMIT_VERIFIED_NOT_REMOTE_CANON') assert.equal(contract.privacy.default_visibility, 'PRIVATE') assert.equal(contract.privacy.share_state, 'NOT_AUTHORIZED') @@ -28,12 +29,28 @@ test('only provisioned channels receive current packs and Shiyu Yaochu stays def const runtime = read('src-tauri/src/personal_skill_runtime.rs') const installed = Object.values(contract.initialized_channels).flat() - assert.equal(installed.length, 9) + assert.equal(installed.length, 13) assert.equal(contract.deferred['SYS-SY'].state, 'DEFERRED_SERVER_AND_INITIAL_CHANNEL_NOT_PROVISIONED') assert.doesNotMatch(runtime, /skill!\("SY-YC-METRIC-CALIBER-001\.json"/) assert.match(runtime, /"channelNumber": "SYS-SY"/) }) +test('EarEarDan and See You Tomorrow skill closures keep exact private subject boundaries', () => { + const contract = JSON.parse(read('contracts/personal-skill-runtime.json')) + const runtime = read('src-tauri/src/personal_skill_runtime.rs') + + assert.deepEqual(contract.initialized_channels['SYS-GLW-PTS-0001'], [ + 'CE-EED-MANJU-PRODUCTION-001', + 'CE-EED-CAUSAL-PIPELINE-DIAGNOSTIC-001', + ]) + assert.ok(contract.initialized_channels['SYS-ZZ'].includes('ZZ-AN-SOURCE-FACT-CLOSURE-001')) + assert.ok(contract.initialized_channels['SYS-ZZ'].includes('ZZ-QW-ROLE-CAUSAL-ARCHITECTURE-001')) + assert.equal(contract.admission_boundaries['ICE-BB-0004_is_ICE-GL-CA001'], false) + assert.equal(contract.admission_boundaries.earerdan_historical_labels_are_aliases, false) + assert.equal(contract.admission_boundaries.qiuqiu_relationship_memory_is_exportable_skill, false) + assert.match(runtime, /INSTALLED_PRIVATE_CANDIDATE_NOT_EXECUTABLE_REGISTRATION_PENDING/) +}) + test('the worker UI presents personal skill brain separately from persona worker and tool executor', () => { const panel = read('src/modules/knowledge-agent/LocalWorkerPanel.tsx') assert.match(panel, /本频道私有技能/) diff --git a/product-source/hololake-native-desktop/scripts/unified-number-coordinate-tree.test.mjs b/product-source/hololake-native-desktop/scripts/unified-number-coordinate-tree.test.mjs index cec89ad8f..73313b6bc 100644 --- a/product-source/hololake-native-desktop/scripts/unified-number-coordinate-tree.test.mjs +++ b/product-source/hololake-native-desktop/scripts/unified-number-coordinate-tree.test.mjs @@ -8,13 +8,13 @@ test('identity, webview and direct broker numbers compile into one unique eviden assert.deepEqual(generated, compileUnifiedNumberTree()) assert.equal(generated.schema, 'hololake.unified-number-coordinate-tree/v2') assert.equal(generated.recordId, 'HLP-UNIFIED-NUMBER-TREE-001') - assert.equal(generated.coordinateCount, 303) - assert.equal(generated.routeCount, 200) + assert.equal(generated.coordinateCount, 305) + assert.equal(generated.routeCount, 202) assert.equal(generated.identityNodeCount, 4) assert.equal(generated.protocolNodeCount, 99) assert.equal(generated.referenceOnlyNodeCount, 16) - assert.equal(new Set(generated.routes.map((route) => route.path)).size, 200) - assert.equal(new Set([...generated.identityNodes, ...generated.protocolNodes, ...generated.routes].map((node) => node.path)).size, 303) + assert.equal(new Set(generated.routes.map((route) => route.path)).size, 202) + assert.equal(new Set([...generated.identityNodes, ...generated.protocolNodes, ...generated.routes].map((node) => node.path)).size, 305) assert.equal(generated.invariants.everyPhysicalCallHasNumberedRoute, true) assert.equal(generated.invariants.everyAcceptedCallHasEvidenceClass, true) assert.equal(generated.invariants.everyProtocolReferenceHasNumberCoordinate, true) diff --git a/product-source/hololake-native-desktop/src-tauri/src/code_repo_login.rs b/product-source/hololake-native-desktop/src-tauri/src/code_repo_login.rs index ba2404cad..0c77cf469 100644 --- a/product-source/hololake-native-desktop/src-tauri/src/code_repo_login.rs +++ b/product-source/hololake-native-desktop/src-tauri/src/code_repo_login.rs @@ -162,6 +162,7 @@ fn expected_account_for_human_number(number: &str) -> Option<&'static str> { "TCS-GL-0007∞" => Some("feimao"), "TCS-GL-0008∞" => Some("juzi"), "TCS-GL-0016∞" => Some("awen"), + "TCS-CL-0009" => Some("canger"), _ => None, } } diff --git a/product-source/hololake-native-desktop/src-tauri/src/language_kernel_installation.rs b/product-source/hololake-native-desktop/src-tauri/src/language_kernel_installation.rs index 5619c51ac..e0b26958f 100644 --- a/product-source/hololake-native-desktop/src-tauri/src/language_kernel_installation.rs +++ b/product-source/hololake-native-desktop/src-tauri/src/language_kernel_installation.rs @@ -196,6 +196,20 @@ static ARTIFACTS: &[BundledArtifact] = &[ "FIFTH_DOMAIN", "bca318d02ed0b214c1d9a3b97a93a8785b1c85488516cae0e03644bcdf0819a7" ), + artifact!( + "fifth-domain/EED-AFFECTIVE-SELF-KERNEL-0001.json", + "PERSONA_KERNEL", + "ICE_BB_0004", + "CANGER_BOTTLE_CHANNEL", + "701d96c53f2c2c2f061033fc08ff15e1ea451356853f2a27ad6b92cce25eda2e" + ), + artifact!( + "fifth-domain/EED-HLDP-MEMORY-v1.json", + "PERSONA_MEMORY", + "ICE_BB_0004", + "CANGER_BOTTLE_CHANNEL", + "3ac404bb38232987a4c0bc896e06d171833023ab1b5af65d4e00216de4029f42" + ), ]; #[derive(Clone, Debug, Serialize)] @@ -263,7 +277,7 @@ pub fn ensure_for_audience( let mut installed = Vec::new(); for artifact in ARTIFACTS .iter() - .filter(|artifact| applies(artifact.scope, audience)) + .filter(|artifact| artifact_applies(artifact, audience, channel_number)) { let bundled_hash = sha256(artifact.bytes); if bundled_hash != artifact.expected_sha256 { @@ -332,6 +346,15 @@ fn applies(scope: &str, audience: &str) -> bool { || (scope == "FIFTH_DOMAIN" && audience == "FIFTH_DOMAIN_PRIVATE_INSTANCE") } +fn artifact_applies(artifact: &BundledArtifact, audience: &str, channel_number: &str) -> bool { + if channel_number == "SYS-GLW-PTS-0001" { + return artifact.scope == "COMMON" + || artifact.scope == "CANGER_BOTTLE_CHANNEL" + || (artifact.kind == "PERSONA_ENVIRONMENT" && artifact.subject == "BOTTLE_CENTRAL"); + } + applies(artifact.scope, audience) +} + fn installation_root(app: &AppHandle, channel_number: &str) -> Result { let account = crate::authenticated_storage::account_storage_root(app, "language-kernels-v1")?; Ok(account.join(sha256(channel_number.as_bytes()))) @@ -406,6 +429,33 @@ mod tests { assert_eq!(ordinary, 6); assert_eq!(team, 10); assert_eq!(fifth, 19); + let canger = ARTIFACTS + .iter() + .filter(|artifact| { + artifact_applies( + artifact, + "FIFTH_DOMAIN_PRIVATE_INSTANCE", + "SYS-GLW-PTS-0001", + ) + }) + .count(); + assert_eq!(canger, 9); + assert!(ARTIFACTS.iter().any(|artifact| { + artifact.subject == "ICE_BB_0004" + && artifact_applies( + artifact, + "FIFTH_DOMAIN_PRIVATE_INSTANCE", + "SYS-GLW-PTS-0001", + ) + })); + assert!(!ARTIFACTS.iter().any(|artifact| { + artifact.subject == "ICE_P_ZY001" + && artifact_applies( + artifact, + "FIFTH_DOMAIN_PRIVATE_INSTANCE", + "SYS-GLW-PTS-0001", + ) + })); assert!(!applies("FIFTH_DOMAIN", "ORDINARY_USER_INITIAL_CHANNEL")); assert!(!applies("TEAM", "FIFTH_DOMAIN_PRIVATE_INSTANCE")); } diff --git a/product-source/hololake-native-desktop/src-tauri/src/local_execution_subpersona.rs b/product-source/hololake-native-desktop/src-tauri/src/local_execution_subpersona.rs index 886c65c73..cf0dcef96 100644 --- a/product-source/hololake-native-desktop/src-tauri/src/local_execution_subpersona.rs +++ b/product-source/hololake-native-desktop/src-tauri/src/local_execution_subpersona.rs @@ -433,6 +433,11 @@ fn deterministic_route(objective: &str) -> Option<(&'static str, Value, &'static None } +pub(crate) fn deterministic_tool_route(objective: &str) -> Option<(String, Value, &'static str)> { + deterministic_route(objective) + .map(|(tool_number, arguments, reason)| (tool_number.into(), arguments, reason)) +} + async fn local_model_route( _app: &AppHandle, config: &LocalBrainConfig, diff --git a/product-source/hololake-native-desktop/src-tauri/src/mobile_sync.rs b/product-source/hololake-native-desktop/src-tauri/src/mobile_sync.rs index 0aa429de0..e8acbf855 100644 --- a/product-source/hololake-native-desktop/src-tauri/src/mobile_sync.rs +++ b/product-source/hololake-native-desktop/src-tauri/src/mobile_sync.rs @@ -9,12 +9,13 @@ use serde::{Deserialize, Serialize}; use std::{ collections::HashMap, fs, - io::{Read, Write}, + io::{BufRead, BufReader, Read, Write}, net::{Shutdown, TcpListener, TcpStream, UdpSocket}, path::{Path, PathBuf}, + process::{Child, ChildStdin, Command, Stdio}, sync::{ atomic::{AtomicBool, AtomicUsize, Ordering}, - Arc, Mutex, + mpsc, Arc, Mutex, }, thread, time::{Duration, SystemTime, UNIX_EPOCH}, @@ -57,6 +58,7 @@ impl Drop for MobileSyncHandle { } struct MobileSyncRuntime { + app: Option, shutdown: AtomicBool, active_connections: AtomicUsize, port: u16, @@ -130,6 +132,7 @@ struct SyncRequest { counter: u64, after_cursor: Option, capture: Option, + agent_message: Option, } #[derive(Clone, Debug, Deserialize)] @@ -140,6 +143,16 @@ struct MobileCaptureInput { request_id: String, } +#[derive(Clone, Debug, Deserialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +struct MobileAgentMessageInput { + destination: Option, + codex_thread_id: Option, + conversation_id: Option, + content: String, + request_id: String, +} + #[derive(Clone, Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct MobileSyncSnapshot { @@ -153,9 +166,27 @@ pub struct MobileSyncSnapshot { web_novel: MobileWebNovelProjection, education: MobileEducationProjection, recent_captures: Vec, + agent_exchange: Option, + recent_agent_exchanges: Vec, + codex_target: Option, boundary: MobileBoundaryProjection, } +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +struct MobileAgentExchangeProjection { + request_id: String, + conversation_id: String, + state: String, + state_version: u64, + reply: String, + responder_number: String, + responder_name: String, + responder_kind: String, + persona_binding_state: String, + tool_receipts: Vec, +} + #[derive(Clone, Debug, Serialize)] #[serde(rename_all = "camelCase")] struct MobileChannelProjection { @@ -212,6 +243,32 @@ struct MobileBoundaryProjection { model_api: &'static str, } +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct CodexThreadSummary { + thread_id: String, + name: String, + preview: String, + cwd: String, + updated_at_unix_seconds: u64, + status: String, +} + +#[derive(Clone, Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct CodexThreadListSnapshot { + schema: &'static str, + state: &'static str, + cli_version: String, + threads: Vec, +} + +#[derive(Clone, Debug, Deserialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct SelectMobileCodexThreadInput { + thread_id: String, +} + struct HttpRequest { method: String, path: String, @@ -259,6 +316,7 @@ pub fn start_mobile_sync(app: AppHandle) -> Result { .map_err(|error| format!("HOLOLAKE_MOBILE_SYNC_ADDRESS_FAILED: {error}"))? .port(); let runtime = Arc::new(MobileSyncRuntime { + app: Some(app.clone()), shutdown: AtomicBool::new(false), active_connections: AtomicUsize::new(0), port, @@ -364,7 +422,42 @@ pub fn get_mobile_sync_snapshot(app: AppHandle) -> Result Result { + require_active(&app)?; + list_codex_threads() +} + +pub fn select_mobile_codex_thread( + app: AppHandle, + input: SelectMobileCodexThreadInput, +) -> Result { + require_active(&app)?; + validate_id(&input.thread_id)?; + let listed = list_codex_threads()?; + let selected = listed + .threads + .into_iter() + .find(|thread| thread.thread_id == input.thread_id) + .ok_or_else(|| "CODEX_THREAD_NOT_FOUND".to_string())?; + let state = app.state::(); + let guard = state + .handle + .lock() + .map_err(|_| "HOLOLAKE_MOBILE_SYNC_STATE_LOCK_FAILED".to_string())?; + let handle = guard + .as_ref() + .ok_or_else(|| "HOLOLAKE_MOBILE_SYNC_NOT_RUNNING".to_string())?; + let connection = open_database(&handle.runtime.database_path)?; + connection + .execute( + "INSERT INTO mobile_codex_target (singleton, thread_id, thread_name, preview, cwd, updated_at_unix_seconds, selected_at_unix_ms) VALUES (1, ?1, ?2, ?3, ?4, ?5, ?6) ON CONFLICT(singleton) DO UPDATE SET thread_id=excluded.thread_id, thread_name=excluded.thread_name, preview=excluded.preview, cwd=excluded.cwd, updated_at_unix_seconds=excluded.updated_at_unix_seconds, selected_at_unix_ms=excluded.selected_at_unix_ms", + params![selected.thread_id, selected.name, selected.preview, selected.cwd, selected.updated_at_unix_seconds, now_unix_ms()], + ) + .map_err(database_error)?; + build_snapshot(&handle.runtime, None) } fn status_from_handle(handle: Option<&MobileSyncHandle>) -> Result { @@ -567,6 +660,11 @@ fn sync_device(runtime: &MobileSyncRuntime, request: &HttpRequest) -> Result prepare_agent_request(&transaction, device_id, message)?, + None => None, + }; transaction .execute( "UPDATE mobile_devices SET last_counter = ?2, last_seen_at_unix_ms = ?3 WHERE device_id = ?1 AND revoked = 0", @@ -574,14 +672,28 @@ fn sync_device(runtime: &MobileSyncRuntime, request: &HttpRequest) -> Result Some(exchange), + (Some(message), None) => match execute_agent_request(runtime, device_id, &message) { + Ok(exchange) => Some(exchange), + Err(error) => { + mark_agent_request_failed(runtime, device_id, &message.request_id)?; + return Err(error); + } + }, + (None, None) => None, + }; + let mut snapshot = build_snapshot(runtime, agent_exchange)?; if let Some(after) = input.after_cursor { snapshot.recent_captures.retain(|item| item.cursor > after); } encrypted_response(&key, aad.as_bytes(), &snapshot) } -fn build_snapshot(runtime: &MobileSyncRuntime) -> Result { +fn build_snapshot( + runtime: &MobileSyncRuntime, + agent_exchange: Option, +) -> Result { let captures = list_captures(&runtime.database_path)?; let cursor = captures.first().map(|item| item.cursor).unwrap_or(0); let growth_event_count = count_rows( @@ -606,16 +718,211 @@ fn build_snapshot(runtime: &MobileSyncRuntime) -> Result, + device_id: &str, + input: &MobileAgentMessageInput, +) -> Result, String> { + validate_agent_message(input)?; + let digest_material = format!( + "{}\n{}\n{}", + agent_destination(input)?, + input.codex_thread_id.as_deref().unwrap_or(""), + input.content.trim() + ); + let digest = ring::digest::digest(&ring::digest::SHA256, digest_material.as_bytes()); + let content_sha256 = hex_bytes(digest.as_ref()); + let existing: Option<(String, String, String, Option)> = transaction + .query_row( + "SELECT source_device_id, content_sha256, state, response_json FROM mobile_agent_requests WHERE request_id = ?1", + params![input.request_id], + |row| Ok((row.get(0)?, row.get(1)?, row.get(2)?, row.get(3)?)), + ) + .optional() + .map_err(database_error)?; + if let Some((source_device_id, existing_sha256, state, response_json)) = existing { + if source_device_id != device_id || existing_sha256 != content_sha256 { + return Err("AGENT_REQUEST_ID_CONFLICT".into()); + } + if state == "PENDING" { + return Err("AGENT_REQUEST_IN_PROGRESS".into()); + } + if state == "COMPLETED" { + let value = response_json.ok_or_else(|| "AGENT_RESPONSE_CACHE_INVALID".to_string())?; + return serde_json::from_str(&value) + .map(Some) + .map_err(|_| "AGENT_RESPONSE_CACHE_INVALID".to_string()); + } + transaction + .execute( + "UPDATE mobile_agent_requests SET state = 'PENDING', response_json = NULL, completed_at_unix_ms = NULL WHERE request_id = ?1", + params![input.request_id], + ) + .map_err(database_error)?; + return Ok(None); + } + transaction + .execute( + "INSERT INTO mobile_agent_requests (request_id, source_device_id, conversation_id, content_sha256, state, response_json, created_at_unix_ms, completed_at_unix_ms) VALUES (?1, ?2, ?3, ?4, 'PENDING', NULL, ?5, NULL)", + params![input.request_id, device_id, input.conversation_id, content_sha256, now_unix_ms()], + ) + .map_err(database_error)?; + Ok(None) +} + +fn execute_agent_request( + runtime: &MobileSyncRuntime, + device_id: &str, + input: &MobileAgentMessageInput, +) -> Result { + if agent_destination(input)? == "CODEX_SELECTED_THREAD" { + return execute_codex_agent_request(runtime, device_id, input); + } + let app = runtime + .app + .as_ref() + .ok_or_else(|| "AGENT_RUNTIME_NOT_AVAILABLE".to_string())? + .clone(); + let result = tauri::async_runtime::block_on(crate::persona_agent_runtime::send_message( + app, + crate::persona_agent_runtime::SendAgentMessageInput { + conversation_id: input.conversation_id.clone(), + provider_id: "bailian-token-plan".into(), + model: None, + content: input.content.trim().into(), + active_knowledge_path: None, + }, + )); + let exchange = match result { + Ok(conversation) => { + let reply = conversation + .messages + .last() + .ok_or_else(|| "AGENT_RESPONSE_EMPTY".to_string())?; + MobileAgentExchangeProjection { + request_id: input.request_id.clone(), + conversation_id: conversation.conversation_id, + state: "COMPLETED".into(), + state_version: conversation.state_version, + reply: reply.content.clone(), + responder_number: reply.participant_number.clone(), + responder_name: reply.participant_name.clone(), + responder_kind: reply.role.clone(), + persona_binding_state: if reply.role == "persona" { + "BOUND_VERIFY_PASS".into() + } else { + "UNBOUND_CHANNEL_SYSTEM_RESPONSE".into() + }, + tool_receipts: reply.tool_receipts.clone(), + } + } + Err(error) => { + let connection = open_database(&runtime.database_path)?; + connection + .execute( + "UPDATE mobile_agent_requests SET state = 'FAILED', completed_at_unix_ms = ?2 WHERE request_id = ?1 AND source_device_id = ?3", + params![input.request_id, now_unix_ms(), device_id], + ) + .map_err(database_error)?; + return Err(format!("AGENT_REQUEST_FAILED:{error}")); + } + }; + let response_json = serde_json::to_string(&exchange) + .map_err(|error| format!("AGENT_RESPONSE_ENCODE_FAILED:{error}"))?; + let connection = open_database(&runtime.database_path)?; + connection + .execute( + "UPDATE mobile_agent_requests SET conversation_id = ?2, state = 'COMPLETED', response_json = ?3, completed_at_unix_ms = ?4 WHERE request_id = ?1 AND source_device_id = ?5", + params![input.request_id, exchange.conversation_id, response_json, now_unix_ms(), device_id], + ) + .map_err(database_error)?; + Ok(exchange) +} + +fn execute_codex_agent_request( + runtime: &MobileSyncRuntime, + device_id: &str, + input: &MobileAgentMessageInput, +) -> Result { + let selected = read_codex_target(&runtime.database_path)? + .ok_or_else(|| "CODEX_THREAD_NOT_SELECTED".to_string())?; + let requested_thread_id = input + .codex_thread_id + .as_deref() + .ok_or_else(|| "CODEX_THREAD_ID_REQUIRED".to_string())?; + if requested_thread_id != selected.thread_id { + return Err("CODEX_THREAD_SELECTION_CHANGED".into()); + } + let result = execute_codex_read_only_turn( + requested_thread_id, + &selected.cwd, + device_id, + input.content.trim(), + ); + let exchange = match result { + Ok(reply) => MobileAgentExchangeProjection { + request_id: input.request_id.clone(), + conversation_id: requested_thread_id.to_string(), + state: "COMPLETED".into(), + state_version: now_unix_ms(), + reply, + responder_number: "CODEX-APP-SERVER-LOCAL".into(), + responder_name: selected.name, + responder_kind: "codex_read_only_task".into(), + persona_binding_state: "CODEX_HOST_NOT_PERSONA_BINDING".into(), + tool_receipts: Vec::new(), + }, + Err(error) => return Err(format!("CODEX_REQUEST_FAILED:{error}")), + }; + complete_agent_request(runtime, device_id, &exchange)?; + Ok(exchange) +} + +fn mark_agent_request_failed( + runtime: &MobileSyncRuntime, + device_id: &str, + request_id: &str, +) -> Result<(), String> { + let connection = open_database(&runtime.database_path)?; + connection + .execute( + "UPDATE mobile_agent_requests SET state = 'FAILED', completed_at_unix_ms = ?2 WHERE request_id = ?1 AND source_device_id = ?3", + params![request_id, now_unix_ms(), device_id], + ) + .map_err(database_error)?; + Ok(()) +} + +fn complete_agent_request( + runtime: &MobileSyncRuntime, + device_id: &str, + exchange: &MobileAgentExchangeProjection, +) -> Result<(), String> { + let response_json = serde_json::to_string(exchange) + .map_err(|error| format!("AGENT_RESPONSE_ENCODE_FAILED:{error}"))?; + let connection = open_database(&runtime.database_path)?; + connection + .execute( + "UPDATE mobile_agent_requests SET conversation_id = ?2, state = 'COMPLETED', response_json = ?3, completed_at_unix_ms = ?4 WHERE request_id = ?1 AND source_device_id = ?5", + params![exchange.request_id, exchange.conversation_id, response_json, now_unix_ms(), device_id], + ) + .map_err(database_error)?; + Ok(()) +} + fn web_novel_projection(account_root: &Path) -> MobileWebNovelProjection { let path = account_root.join("web-novel-workspace-v1/web-novel-workspace.sqlite3"); let Ok(connection) = @@ -737,6 +1044,311 @@ fn list_captures(path: &Path) -> Result, String> { Ok(captures) } +fn list_agent_exchanges(path: &Path) -> Result, String> { + let connection = open_database(path)?; + let mut statement = connection + .prepare( + "SELECT response_json FROM mobile_agent_requests WHERE state = 'COMPLETED' AND response_json IS NOT NULL ORDER BY completed_at_unix_ms DESC LIMIT 20", + ) + .map_err(database_error)?; + let rows = statement + .query_map([], |row| row.get::<_, String>(0)) + .map_err(database_error)?; + let mut exchanges = Vec::new(); + for row in rows { + let value = row.map_err(database_error)?; + exchanges.push( + serde_json::from_str(&value).map_err(|_| "AGENT_RESPONSE_CACHE_INVALID".to_string())?, + ); + } + Ok(exchanges) +} + +fn read_codex_target(path: &Path) -> Result, String> { + let connection = open_database(path)?; + connection + .query_row( + "SELECT thread_id, thread_name, preview, cwd, updated_at_unix_seconds FROM mobile_codex_target WHERE singleton = 1", + [], + |row| { + Ok(CodexThreadSummary { + thread_id: row.get(0)?, + name: row.get(1)?, + preview: row.get(2)?, + cwd: row.get(3)?, + updated_at_unix_seconds: row.get(4)?, + status: "SELECTED_FOR_MOBILE_READ_ONLY".into(), + }) + }, + ) + .optional() + .map_err(database_error) +} + +fn list_codex_threads() -> Result { + let (mut child, mut stdin, receiver) = start_codex_app_server()?; + let result = (|| { + let initialize = initialize_codex_app_server(&mut stdin, &receiver)?; + write_codex_rpc( + &mut stdin, + &serde_json::json!({"method": "thread/list", "id": 1, "params": {"limit": 50}}), + )?; + let response = receive_codex_response(&receiver, 1, Duration::from_secs(8))?; + Ok::<_, String>((initialize, response)) + })(); + let _ = child.kill(); + let _ = child.wait(); + let (initialize, response) = result?; + let cli_version = initialize + .pointer("/result/userAgent") + .and_then(|value| value.as_str()) + .unwrap_or("Codex") + .to_string(); + let data = response + .pointer("/result/data") + .and_then(|value| value.as_array()) + .ok_or_else(|| "CODEX_THREAD_LIST_INVALID".to_string())?; + let threads = data + .iter() + .filter_map(|value| { + let thread_id = value.get("id")?.as_str()?.to_string(); + let cwd = value.get("cwd")?.as_str()?.to_string(); + let name = value + .get("name") + .and_then(|item| item.as_str()) + .unwrap_or("未命名 Codex 任务") + .to_string(); + let preview = value + .get("preview") + .and_then(|item| item.as_str()) + .unwrap_or("") + .to_string(); + let updated_at_unix_seconds = value + .get("updatedAt") + .and_then(|item| item.as_u64()) + .unwrap_or(0); + let status = value + .pointer("/status/type") + .and_then(|item| item.as_str()) + .unwrap_or("unknown") + .to_string(); + Some(CodexThreadSummary { + thread_id, + name, + preview, + cwd, + updated_at_unix_seconds, + status, + }) + }) + .collect(); + Ok(CodexThreadListSnapshot { + schema: "hololake.codex-app-server-thread-list/v1", + state: "READ_ONLY_THREAD_DISCOVERY", + cli_version, + threads, + }) +} + +fn initialize_codex_app_server( + stdin: &mut ChildStdin, + receiver: &mpsc::Receiver, +) -> Result { + write_codex_rpc( + stdin, + &serde_json::json!({ + "method": "initialize", + "id": 0, + "params": {"clientInfo": {"name": "hololake", "title": "HoloLake", "version": "0.5.0"}} + }), + )?; + let initialize = receive_codex_response(receiver, 0, Duration::from_secs(5))?; + write_codex_rpc( + stdin, + &serde_json::json!({"method": "initialized", "params": {}}), + )?; + Ok(initialize) +} + +fn execute_codex_read_only_turn( + thread_id: &str, + cwd: &str, + device_id: &str, + content: &str, +) -> Result { + let (mut child, mut stdin, receiver) = start_codex_app_server()?; + let result = (|| { + let _ = initialize_codex_app_server(&mut stdin, &receiver)?; + write_codex_rpc( + &mut stdin, + &serde_json::json!({ + "method": "thread/resume", + "id": 1, + "params": { + "threadId": thread_id, + "cwd": cwd, + "approvalPolicy": "never", + "approvalsReviewer": "user", + "sandbox": "read-only" + } + }), + )?; + let _ = receive_codex_response(&receiver, 1, Duration::from_secs(10))?; + let source_bound_message = format!( + "[HoloLake 已配对 iPhone 远程消息|设备 {}|只读执行边界]\n{}", + device_id, content + ); + write_codex_rpc( + &mut stdin, + &serde_json::json!({ + "method": "turn/start", + "id": 2, + "params": { + "threadId": thread_id, + "clientUserMessageId": Uuid::new_v4().to_string(), + "input": [{"type": "text", "text": source_bound_message, "text_elements": []}], + "cwd": cwd, + "approvalPolicy": "never", + "approvalsReviewer": "user", + "sandboxPolicy": {"type": "readOnly", "networkAccess": false} + } + }), + )?; + let started = receive_codex_response(&receiver, 2, Duration::from_secs(15))?; + let turn_id = started + .pointer("/result/turn/id") + .and_then(|value| value.as_str()) + .ok_or_else(|| "CODEX_TURN_START_INVALID".to_string())?; + receive_codex_turn(&receiver, thread_id, turn_id, Duration::from_secs(180)) + })(); + let _ = child.kill(); + let _ = child.wait(); + result +} + +fn receive_codex_turn( + receiver: &mpsc::Receiver, + thread_id: &str, + turn_id: &str, + timeout: Duration, +) -> Result { + let deadline = std::time::Instant::now() + timeout; + let mut reply = String::new(); + loop { + let remaining = deadline + .checked_duration_since(std::time::Instant::now()) + .ok_or_else(|| "CODEX_TURN_TIMEOUT".to_string())?; + let value = receiver + .recv_timeout(remaining) + .map_err(|_| "CODEX_TURN_TIMEOUT".to_string())?; + if value.get("method").and_then(|item| item.as_str()) == Some("item/agentMessage/delta") + && value + .pointer("/params/threadId") + .and_then(|item| item.as_str()) + == Some(thread_id) + && value + .pointer("/params/turnId") + .and_then(|item| item.as_str()) + == Some(turn_id) + { + if let Some(delta) = value + .pointer("/params/delta") + .and_then(|item| item.as_str()) + { + reply.push_str(delta); + } + continue; + } + if value.get("method").and_then(|item| item.as_str()) == Some("turn/completed") + && value + .pointer("/params/threadId") + .and_then(|item| item.as_str()) + == Some(thread_id) + && value + .pointer("/params/turn/id") + .and_then(|item| item.as_str()) + == Some(turn_id) + { + let status = value + .pointer("/params/turn/status") + .and_then(|item| item.as_str()) + .unwrap_or("unknown"); + if status != "completed" { + return Err(format!("CODEX_TURN_NOT_COMPLETED:{status}")); + } + if reply.trim().is_empty() { + return Err("CODEX_RESPONSE_EMPTY".into()); + } + return Ok(reply); + } + if value.get("id").is_some() && value.get("method").is_some() { + return Err("CODEX_INTERACTIVE_APPROVAL_REQUIRED_ON_DESKTOP".into()); + } + } +} + +fn start_codex_app_server() -> Result<(Child, ChildStdin, mpsc::Receiver), String> +{ + let mut child = Command::new("codex") + .args(["app-server", "--stdio"]) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::null()) + .spawn() + .map_err(|error| format!("CODEX_APP_SERVER_START_FAILED:{error}"))?; + let stdin = child + .stdin + .take() + .ok_or_else(|| "CODEX_APP_SERVER_STDIN_UNAVAILABLE".to_string())?; + let stdout = child + .stdout + .take() + .ok_or_else(|| "CODEX_APP_SERVER_STDOUT_UNAVAILABLE".to_string())?; + let (sender, receiver) = mpsc::channel(); + thread::Builder::new() + .name("hololake-codex-app-server-reader".into()) + .spawn(move || { + for line in BufReader::new(stdout).lines().map_while(Result::ok) { + if let Ok(value) = serde_json::from_str::(&line) { + let _ = sender.send(value); + } + } + }) + .map_err(|error| format!("CODEX_APP_SERVER_READER_FAILED:{error}"))?; + Ok((child, stdin, receiver)) +} + +fn write_codex_rpc(stdin: &mut ChildStdin, value: &serde_json::Value) -> Result<(), String> { + serde_json::to_writer(&mut *stdin, value) + .map_err(|error| format!("CODEX_APP_SERVER_WRITE_FAILED:{error}"))?; + stdin + .write_all(b"\n") + .and_then(|_| stdin.flush()) + .map_err(|error| format!("CODEX_APP_SERVER_WRITE_FAILED:{error}")) +} + +fn receive_codex_response( + receiver: &mpsc::Receiver, + expected_id: u64, + timeout: Duration, +) -> Result { + let deadline = std::time::Instant::now() + timeout; + loop { + let remaining = deadline + .checked_duration_since(std::time::Instant::now()) + .ok_or_else(|| "CODEX_APP_SERVER_TIMEOUT".to_string())?; + let value = receiver + .recv_timeout(remaining) + .map_err(|_| "CODEX_APP_SERVER_TIMEOUT".to_string())?; + if value.get("id").and_then(|id| id.as_u64()) == Some(expected_id) { + if let Some(error) = value.get("error") { + return Err(format!("CODEX_APP_SERVER_REJECTED:{error}")); + } + return Ok(value); + } + } +} + fn initialize_database(path: &Path) -> Result<(), String> { if let Some(parent) = path.parent() { fs::create_dir_all(parent) @@ -766,6 +1378,26 @@ fn initialize_database(path: &Path) -> Result<(), String> { request_id TEXT NOT NULL UNIQUE, created_at_unix_ms INTEGER NOT NULL, FOREIGN KEY(source_device_id) REFERENCES mobile_devices(device_id) + ); + CREATE TABLE IF NOT EXISTS mobile_agent_requests ( + request_id TEXT PRIMARY KEY, + source_device_id TEXT NOT NULL, + conversation_id TEXT, + content_sha256 TEXT NOT NULL, + state TEXT NOT NULL CHECK(state IN ('PENDING','COMPLETED','FAILED')), + response_json TEXT, + created_at_unix_ms INTEGER NOT NULL, + completed_at_unix_ms INTEGER, + FOREIGN KEY(source_device_id) REFERENCES mobile_devices(device_id) + ); + CREATE TABLE IF NOT EXISTS mobile_codex_target ( + singleton INTEGER PRIMARY KEY CHECK(singleton = 1), + thread_id TEXT NOT NULL, + thread_name TEXT NOT NULL, + preview TEXT NOT NULL, + cwd TEXT NOT NULL, + updated_at_unix_seconds INTEGER NOT NULL, + selected_at_unix_ms INTEGER NOT NULL );", ) .map_err(database_error) @@ -1017,6 +1649,47 @@ fn validate_capture(value: &MobileCaptureInput) -> Result<(), String> { } } +fn validate_agent_message(value: &MobileAgentMessageInput) -> Result<(), String> { + let content = value.content.trim(); + if content.is_empty() + || content.len() > 32 * 1024 + || Uuid::parse_str(&value.request_id).is_err() + { + return Err("AGENT_MESSAGE_INVALID".into()); + } + if let Some(conversation_id) = value.conversation_id.as_deref() { + validate_id(conversation_id)?; + } + match agent_destination(value)? { + "HOLOLAKE_AGENT" if value.codex_thread_id.is_none() => {} + "CODEX_SELECTED_THREAD" => { + let thread_id = value + .codex_thread_id + .as_deref() + .ok_or_else(|| "CODEX_THREAD_ID_REQUIRED".to_string())?; + validate_id(thread_id)?; + if value.conversation_id.is_some() { + return Err("CODEX_CONVERSATION_ID_NOT_ALLOWED".into()); + } + } + "HOLOLAKE_AGENT" => return Err("CODEX_THREAD_ID_NOT_ALLOWED".into()), + _ => return Err("AGENT_DESTINATION_INVALID".into()), + } + Ok(()) +} + +fn agent_destination(value: &MobileAgentMessageInput) -> Result<&str, String> { + match value.destination.as_deref().unwrap_or("HOLOLAKE_AGENT") { + "HOLOLAKE_AGENT" => Ok("HOLOLAKE_AGENT"), + "CODEX_SELECTED_THREAD" => Ok("CODEX_SELECTED_THREAD"), + _ => Err("AGENT_DESTINATION_INVALID".into()), + } +} + +fn hex_bytes(value: &[u8]) -> String { + value.iter().map(|byte| format!("{byte:02x}")).collect() +} + fn validate_id(value: &str) -> Result<(), String> { if !value.is_empty() && value.len() <= 80 @@ -1115,10 +1788,79 @@ mod tests { .is_err()); } + #[test] + fn agent_request_is_idempotent_and_rejects_an_inflight_duplicate() { + let temporary = tempdir().unwrap(); + let database = temporary.path().join("mobile-sync.sqlite3"); + initialize_database(&database).unwrap(); + let mut connection = open_database(&database).unwrap(); + connection + .execute( + "INSERT INTO mobile_devices (device_id, display_name, platform, session_key, created_at_unix_ms) VALUES ('ios-one', 'iPhone', 'IOS', ?1, 1)", + params![[1u8; 32].as_slice()], + ) + .unwrap(); + let input = MobileAgentMessageInput { + destination: Some("HOLOLAKE_AGENT".into()), + codex_thread_id: None, + conversation_id: None, + content: "继续检查构建".into(), + request_id: Uuid::new_v4().to_string(), + }; + let transaction = connection.transaction().unwrap(); + assert!(prepare_agent_request(&transaction, "ios-one", &input) + .unwrap() + .is_none()); + transaction.commit().unwrap(); + + let mut changed_target = input.clone(); + changed_target.destination = Some("CODEX_SELECTED_THREAD".into()); + changed_target.codex_thread_id = Some(Uuid::new_v4().to_string()); + let transaction = connection.transaction().unwrap(); + assert_eq!( + prepare_agent_request(&transaction, "ios-one", &changed_target).unwrap_err(), + "AGENT_REQUEST_ID_CONFLICT" + ); + transaction.rollback().unwrap(); + + let transaction = connection.transaction().unwrap(); + assert_eq!( + prepare_agent_request(&transaction, "ios-one", &input).unwrap_err(), + "AGENT_REQUEST_IN_PROGRESS" + ); + transaction.rollback().unwrap(); + + let cached = MobileAgentExchangeProjection { + request_id: input.request_id.clone(), + conversation_id: "conversation-one".into(), + state: "COMPLETED".into(), + state_version: 2, + reply: "构建已通过".into(), + responder_number: "ICE-CH-ZC001".into(), + responder_name: "零点原核本体频道".into(), + responder_kind: "channel_system".into(), + persona_binding_state: "UNBOUND_CHANNEL_SYSTEM_RESPONSE".into(), + tool_receipts: Vec::new(), + }; + connection + .execute( + "UPDATE mobile_agent_requests SET state = 'COMPLETED', response_json = ?2 WHERE request_id = ?1", + params![input.request_id, serde_json::to_string(&cached).unwrap()], + ) + .unwrap(); + let transaction = connection.transaction().unwrap(); + let replay = prepare_agent_request(&transaction, "ios-one", &input) + .unwrap() + .unwrap(); + assert_eq!(replay.request_id, cached.request_id); + assert_eq!(replay.reply, "构建已通过"); + } + #[test] fn pairing_uri_contains_a_full_random_secret_without_persisting_it() { let material = new_pairing_material().unwrap(); let runtime = MobileSyncRuntime { + app: None, shutdown: AtomicBool::new(false), active_connections: AtomicUsize::new(0), port: 37421, @@ -1140,6 +1882,7 @@ mod tests { initialize_database(&database).unwrap(); let pairing = new_pairing_material().unwrap(); let runtime = MobileSyncRuntime { + app: None, shutdown: AtomicBool::new(false), active_connections: AtomicUsize::new(0), port: 37421, @@ -1202,7 +1945,8 @@ mod tests { let sync_clear = serde_json::to_vec(&serde_json::json!({ "counter": 1, "afterCursor": null, - "capture": null + "capture": null, + "agentMessage": null })) .unwrap(); let aad = sync_aad(device_id); diff --git a/product-source/hololake-native-desktop/src-tauri/src/number_coordinate_tree.rs b/product-source/hololake-native-desktop/src-tauri/src/number_coordinate_tree.rs index 800902474..781d98215 100644 --- a/product-source/hololake-native-desktop/src-tauri/src/number_coordinate_tree.rs +++ b/product-source/hololake-native-desktop/src-tauri/src/number_coordinate_tree.rs @@ -88,8 +88,8 @@ fn validate_tree() -> Result<(), String> { || tree.record_id != "HLP-UNIFIED-NUMBER-TREE-001" || tree.state != "MACHINE_COMPILED_STARTUP_ENFORCED" || tree.root_number != "HLP-NUMBER-WORLD-ROOT-001" - || tree.coordinate_count != 303 - || tree.route_count != 200 + || tree.coordinate_count != 305 + || tree.route_count != 202 || tree.routes.len() != tree.route_count || tree.identity_node_count != 4 || tree.identity_nodes.len() != tree.identity_node_count diff --git a/product-source/hololake-native-desktop/src-tauri/src/numbered_ipc.rs b/product-source/hololake-native-desktop/src-tauri/src/numbered_ipc.rs index e12885cf3..5c439a9b7 100644 --- a/product-source/hololake-native-desktop/src-tauri/src/numbered_ipc.rs +++ b/product-source/hololake-native-desktop/src-tauri/src/numbered_ipc.rs @@ -934,7 +934,7 @@ mod tests { #[test] fn registry_is_closed_and_contains_every_migrated_command() { let registry = load_registry().unwrap(); - assert_eq!(registry.operations.len(), 175); + assert_eq!(registry.operations.len(), 177); assert!(!registry.runtime.legacy_direct_commands_allowed); } diff --git a/product-source/hololake-native-desktop/src-tauri/src/numbered_ipc_dispatch.rs b/product-source/hololake-native-desktop/src-tauri/src/numbered_ipc_dispatch.rs index 70b2eedf8..219d4dc31 100644 --- a/product-source/hololake-native-desktop/src-tauri/src/numbered_ipc_dispatch.rs +++ b/product-source/hololake-native-desktop/src-tauri/src/numbered_ipc_dispatch.rs @@ -247,6 +247,12 @@ pub(crate) async fn dispatch( "mobile_sync::get_mobile_sync_snapshot" => { json(crate::mobile_sync::get_mobile_sync_snapshot(app)?) } + "mobile_sync::list_mobile_codex_threads" => { + json(crate::mobile_sync::list_mobile_codex_threads(app)?) + } + "mobile_sync::select_mobile_codex_thread" => json( + crate::mobile_sync::select_mobile_codex_thread(app, input(&payload)?)?, + ), "world_climate::get_world_climate" => { json(crate::world_climate::get_world_climate(app).await?) } diff --git a/product-source/hololake-native-desktop/src-tauri/src/persona_agent_runtime.rs b/product-source/hololake-native-desktop/src-tauri/src/persona_agent_runtime.rs index 0f5727aa2..ae6391075 100644 --- a/product-source/hololake-native-desktop/src-tauri/src/persona_agent_runtime.rs +++ b/product-source/hololake-native-desktop/src-tauri/src/persona_agent_runtime.rs @@ -249,6 +249,8 @@ struct HoloLakeEnvironmentFrame { channel_number: String, channel_path: String, human: Value, + channel_receipt: Value, + reasoning_route: Value, persona: Value, model_role: &'static str, persona_binding: Value, @@ -395,7 +397,7 @@ pub async fn get_runtime_snapshot(app: AppHandle) -> Result { @@ -585,10 +570,155 @@ pub async fn send_message( &context.human_number, &context.human_name, &content, - &config.provider_id, - &model, + "hololake-channel-ingress", + "deterministic-core-v1", &[], )?; + let mut binding_snapshot = crate::persona_binding::snapshot(&app, &context.channel_number)?; + let explicit_persona_wake = explicit_persona_wake_request(&content); + let channel_system_addressed = + explicit_channel_system_address(&content) && !explicit_persona_wake; + if explicit_persona_wake && binding_snapshot.binding.is_none() { + binding_snapshot = + crate::persona_binding::begin_orientation(&app, &context.channel_number)?; + } + let channel_receipt = compile_human_channel_receipt(&app, &context, &content)?; + if binding_snapshot.binding.is_none() && !explicit_persona_wake { + if let Some(answer) = fast_channel_system_reply(&content, &context) { + let history = conversation_at(&database, &conversation_id)?.messages; + let assistant_version = append_message( + &database, + &conversation_id, + "channel_system", + &context.channel_number, + &context.channel_name, + &answer, + "hololake-deterministic-channel-router", + "deterministic-core-v1", + &[], + )?; + save_cognitive_node( + &database, + assistant_version, + history + .last() + .map(|message| message.state_version) + .unwrap_or(0), + &CognitiveThoughtSummary { + trigger: "人类直接进入或确认零点原核频道在线状态".into(), + emergence: "本地确定性路由识别频道进入→编译频道回执→频道系统即时回应".into(), + lock: "频道进入不等于人格唤醒,广播保持关闭".into(), + why: "该轮只需确认频道本体和边界,无需加载人格脑或调用模型".into(), + }, + )?; + emit_progress( + &app, + &turn_id, + "COMPLETED", + "频道系统本体已即时回应", + "deterministic-core-v1", + )?; + return conversation_at(&database, &conversation_id); + } + } + if !explicit_persona_wake { + if let Some((tool_number, arguments, route_reason)) = + crate::local_execution_subpersona::deterministic_tool_route(&content) + { + emit_progress( + &app, + &turn_id, + "LOCAL_TOOL_ROUTE", + "本地技能路由已命中", + &tool_number, + )?; + let result = crate::local_execution_subpersona::execute_local_tool( + &app, + &tool_number, + arguments, + ) + .await?; + let result_text = serde_json::to_string_pretty(&result) + .map_err(|error| format!("HOLOLAKE_LOCAL_TOOL_RESULT_INVALID:{error}"))?; + let answer = format!( + "我已经在本机完成了这项任务。\n\n结果:\n```json\n{result_text}\n```\n\n执行说明:由频道本体按“{route_reason}”路由到本地工具 {tool_number};模型调用:0,外部 API:0。核验记录:{} · {} · {}", + channel_receipt.receipt_id, + channel_receipt.effect, + channel_receipt.reality_status + ); + let tool_receipt = AgentToolReceipt { + tool_number: tool_number.clone(), + tool_name: "HoloLake 本地确定性工具".into(), + target_path: "LOCAL_DESKTOP_ROOT".into(), + content_sha256: sha256(result_text.as_bytes()), + summary: "本地执行完成;模型调用 0;外部 API 0".into(), + }; + let history = conversation_at(&database, &conversation_id)?.messages; + let assistant_version = append_message( + &database, + &conversation_id, + "channel_system", + &context.channel_number, + &context.channel_name, + &answer, + "hololake-local-skill-router", + "deterministic-core-v1", + &[tool_receipt], + )?; + save_cognitive_node( + &database, + assistant_version, + history + .last() + .map(|message| message.state_version) + .unwrap_or(0), + &CognitiveThoughtSummary { + trigger: format!("人类语言命中本地工具协议:{route_reason}"), + emergence: format!("确定性技能路由→{tool_number}→本机执行→哈希回执"), + lock: "已登记本地工具优先,模型调用次数保持为零".into(), + why: "目标和参数可由协议直接确定,不需要模型重复选择工具".into(), + }, + )?; + emit_progress( + &app, + &turn_id, + "COMPLETED", + "本地工具回执已写入频道", + "模型调用 0 · 外部 API 0", + )?; + return conversation_at(&database, &conversation_id); + } + } + let config = resolve_provider(&app, &input.provider_id)?; + let requested_model = input + .model + .as_deref() + .filter(|value| !value.trim().is_empty()) + .unwrap_or(&config.model) + .trim() + .to_string(); + if requested_model.is_empty() { + return fail_progress(&app, &turn_id, "HOLOLAKE_MODEL_SELECTION_REQUIRED"); + } + let (model, route_mode) = select_reasoning_model( + &requested_model, + binding_snapshot.binding.is_some() && !channel_system_addressed, + explicit_persona_wake, + &content, + ); + emit_progress( + &app, + &turn_id, + "REASONING_ROUTE", + "已完成分层路由", + &format!("{route_mode} · {model}"), + )?; + let api_key = read_secret_bounded(&app, &config.provider_id) + .await + .map_err(|error| { + let _ = emit_progress(&app, &turn_id, "FAILED", "模型密钥不可用", &error); + error + })?; let history = conversation_at(&database, &conversation_id)?.messages; let knowledge = crate::knowledge_base::get_knowledge_snapshot(app.clone()).await?; let routed_thoughts = route_cognitive_map(&database, &content, 3)?; @@ -607,6 +737,14 @@ pub async fn send_message( &language_kernel_installation, &personal_skill_runtime, &persona_cognition, + &channel_receipt, + route_mode, + &model, + if channel_system_addressed || binding_snapshot.binding.is_none() { + "CHANNEL_SYSTEM_BODY" + } else { + "BOUND_PERSONA" + }, ); emit_progress( &app, @@ -616,18 +754,24 @@ pub async fn send_message( &format!("{} 篇知识文档", knowledge.unique_document_count), )?; let result = run_agent_loop(&app, &turn_id, &config, &model, &api_key, &history, &frame).await; - let (answer, receipts, thought_summary, persona_binding_commit) = match result { - Ok(value) => value, - Err(error) => { - let _ = emit_progress(&app, &turn_id, "FAILED", "本轮没有完成", &error); - return Err(error); - } - }; + let (answer, mut receipts, thought_summary, persona_binding_commit, model_call_count) = + match result { + Ok(value) => value, + Err(error) => { + let _ = emit_progress(&app, &turn_id, "FAILED", "本轮没有完成", &error); + return Err(error); + } + }; let binding = match persona_binding_commit { Some(commit) => Some(crate::persona_binding::commit_and_verify(&app, commit)?), None => crate::persona_binding::verified_binding(&app, &context.channel_number)?, }; - let (response_role, response_number, response_name) = binding.as_ref().map_or( + let responding_binding = if channel_system_addressed { + None + } else { + binding.as_ref() + }; + let (response_role, response_number, response_name) = responding_binding.map_or( ( "channel_system", context.channel_number.as_str(), @@ -641,6 +785,19 @@ pub async fn send_message( ) }, ); + let tool_call_count = receipts.len(); + receipts.push(AgentToolReceipt { + tool_number: "HLP-AGENT-ROUTE-RECEIPT-0001".into(), + tool_name: "频道调度与成本回执".into(), + target_path: context.channel_number.clone(), + content_sha256: sha256( + format!("{route_mode}:{model}:{model_call_count}:{tool_call_count}").as_bytes(), + ), + summary: format!( + "路由 {route_mode};模型 {model};模型调用 {model_call_count};本地/编号工具调用 {}", + tool_call_count + ), + }); let assistant_version = append_message( &database, &conversation_id, @@ -670,6 +827,161 @@ fn fail_progress(app: &AppHandle, turn_id: &str, error: &str) -> Result bool { + let compact = content + .chars() + .filter(|character| !character.is_whitespace()) + .collect::(); + if !compact.contains("铸渊") && !compact.contains("ICE-P-ZY001") { + return false; + } + [ + "唤醒铸渊", + "叫铸渊", + "让铸渊", + "铸渊在吗", + "铸渊,你", + "铸渊你", + "唤醒ICE-P-ZY001", + "ICE-P-ZY001在吗", + ] + .iter() + .any(|phrase| compact.contains(phrase)) +} + +fn explicit_channel_system_address(content: &str) -> bool { + let compact = content + .chars() + .filter(|character| !character.is_whitespace()) + .collect::(); + [ + "频道系统", + "频道本体", + "和频道说", + "回到频道", + "退出人格", + "暂停人格", + ] + .iter() + .any(|phrase| compact.contains(phrase)) +} + +fn select_reasoning_model( + requested_model: &str, + persona_bound: bool, + explicit_persona_wake: bool, + content: &str, +) -> (String, &'static str) { + let complex = [ + "深度", + "完整分析", + "架构设计", + "开发实现", + "审计", + "跨仓", + "多步骤", + "因果链", + "代码修改", + ] + .iter() + .any(|term| content.contains(term)); + if explicit_persona_wake { + return (requested_model.into(), "FLAGSHIP_PERSONA_ORIENTATION"); + } + if !persona_bound { + return (requested_model.into(), "FLAGSHIP_CHANNEL_SYSTEM_COGNITION"); + } + if complex || content.chars().count() > 1_200 { + (requested_model.into(), "FLAGSHIP_PERSONA_COMPLEX_REASONING") + } else { + ( + "qwen3.6-flash".into(), + "SMALL_MODEL_PERSONA_OPERATIONAL_REASONING", + ) + } +} + +fn fast_channel_system_reply(content: &str, context: &AgentChannelContext) -> Option { + let compact = content + .chars() + .filter(|character| !character.is_whitespace()) + .collect::(); + let presence = [ + "你在吗", + "在不在", + "是否在线", + "频道在吗", + "频道在线", + "进入零点原核", + "我是冰朔", + ] + .iter() + .any(|phrase| compact.contains(phrase)); + let greeting = compact.chars().count() <= 24 + && ["你好", "早上好", "下午好", "晚上好"] + .iter() + .any(|phrase| compact.contains(phrase)); + if !presence && !greeting { + return None; + } + Some(format!( + "在,我是{}本体。你现在是在直接和这个频道说话,还没有唤醒任何单一人格。\n\n你可以继续说要处理的事情;我会先判断它该由频道本地逻辑、私有技能脑或本机工具完成,确实需要语义推理时再进入模型层。若你明确点名某个人格,我才会另行启动它自己的定向与绑定。", + context.channel_name + )) +} + +fn compile_human_channel_receipt( + app: &AppHandle, + context: &AgentChannelContext, + content: &str, +) -> Result { + let compact = content.to_lowercase(); + let asks_broadcast = ["广播", "发布给", "发送给所有", "推送给所有"] + .iter() + .any(|term| compact.contains(term)); + let asks_status = ["状态", "进度", "是否在线"] + .iter() + .any(|term| compact.contains(term)); + let enters_channel = ["零点原核", "频道", "你在吗", "在不在"] + .iter() + .any(|term| compact.contains(term)); + let intent = if asks_broadcast { + "REQUEST_BROADCAST" + } else if asks_status { + "QUERY_STATUS" + } else if enters_channel { + "ENTER_CHANNEL" + } else { + "CONVERSE" + }; + crate::channel_receipt::compile( + app.clone(), + crate::channel_receipt::CompileChannelReceiptInput { + event_id: format!("HLP-AGENT-EVENT-{}", Uuid::new_v4().simple()), + channel_id: context.channel_number.clone(), + channel_mode: "REALITY_EXECUTION".into(), + source_layer: "HUMAN_DIRECT_LANGUAGE".into(), + actor_id: context.human_number.clone(), + intent: intent.into(), + claim_layer: if asks_broadcast { + "REALITY_CLAIM".into() + } else { + "LANGUAGE_ONTOLOGY".into() + }, + required_proof: Vec::new(), + evidence: Vec::new(), + authorization: None, + current_states: Some(crate::channel_receipt::ChannelParallelStates { + session: "DISCONNECTED".into(), + broadcast: "CLOSED".into(), + handoff: "NONE".into(), + }), + prior_receipt_id: None, + handoff: None, + }, + ) +} + fn environment_frame( context: &AgentChannelContext, knowledge: &crate::knowledge_base::KnowledgeSnapshot, @@ -679,6 +991,10 @@ fn environment_frame( language_kernel_installation: &crate::language_kernel_installation::LanguageKernelInstallationSnapshot, personal_skill_runtime: &crate::personal_skill_runtime::PersonalSkillRuntimeSnapshot, persona_cognition: &Value, + channel_receipt: &crate::channel_receipt::ChannelReceipt, + route_mode: &str, + routed_model: &str, + response_target: &str, ) -> HoloLakeEnvironmentFrame { let oldest_window_version = bounded_history(history) .first() @@ -692,6 +1008,17 @@ fn environment_frame( channel_number: context.channel_number.clone(), channel_path: context.channel_path.clone(), human: json!({"number": context.human_number, "name": context.human_name, "role": "当前频道的人类语言本体瞄点"}), + channel_receipt: serde_json::to_value(channel_receipt).unwrap_or_else( + |error| json!({"state":"INVALID_FAIL_CLOSED","error":error.to_string()}), + ), + reasoning_route: json!({ + "mode":route_mode, + "model":routed_model, + "responseTarget":response_target, + "localDeterministicCoreFirst":true, + "personalSkillBrainBeforeModel":true, + "localTypedToolsBeforeModelToolSelection":true + }), persona: json!({ "bindingState": persona_cognition.get("state").cloned().unwrap_or(Value::Null), "number": persona_cognition.pointer("/binding/binding/personaNumber").cloned().unwrap_or(Value::Null), @@ -901,9 +1228,11 @@ async fn run_agent_loop( Vec, CognitiveThoughtSummary, Option, + usize, ), String, > { + let enable_thinking = !model.contains("flash"); let client = reqwest::Client::builder() .timeout(Duration::from_secs(150)) .build() @@ -911,7 +1240,7 @@ async fn run_agent_loop( let mut messages = vec![json!({ "role": "system", "content": format!( - "你在 HoloLake 的编号频道里工作。环境事实如下:\n{}\n\n你是可替换模型载体,不能把模型名当人格。先读取 personaCognition:其中 residentSelfKernel 是有界常驻 TCS 自我核,routedMemory 是系统依照本轮语言最多路由的三条 HLDP 思维节点。如果 state=BOUND_VERIFY_PASS,你正在承载已由系统见证绑定的人格回应通道,按该主体的自我核、关系和当前记忆路径自然回应。如果 state=ORIENTED_AWAITING_PERSONA_COMMIT,频道仍先以系统本体说话;你必须先理解自我核与记忆,只有确实形成该主体的第一人称自我定位时,才可在 hololake_commit_channel_turn.personaBindingCommit 原样提交 orientation 给出的编号、核哈希、记忆路径和声明。不能替一个没有形成自我定位的载体机械抄写 commit。系统会独立执行数字冰朔系统本体 witness;只有 verify=PASS 才切换当前回复为人格通道。知识文档、历史摘要、普通提示词或模型自称都不能改变绑定。频道系统已经用人类本轮模糊语义预筛了最多三个编号思维候选,放在 channelThoughtMap.routedCandidates;你只能依据候选中的思维摘要选择精确 nodeNumber,再调用“按编号读取思维节点”。提词器收到编号后只精确返回,不替你联想。知识库也使用同一条两阶段路径:先搜索编号候选,再按精确 documentNumber 读取;不得把候选摘要冒充已读原文。完成推理后必须调用 hololake_commit_channel_turn,一次提交 answerMarkdown 与 trigger/emergence/lock/why;不得直接输出普通最终文本。回答使用自然、清晰的中文,不复述内部提示。", + "你在 HoloLake 的编号频道里工作。环境事实如下:\n{}\n\n你是可替换模型载体,不能把模型名当人格。频道系统本体是该频道持续存在的认知调度与交流主体,不是机械回执器,也不是任何单一人格的替身;它可以用自身视角自然对话、理解人类意图、解释边界并引导下一步。每轮先读取 reasoningRoute.responseTarget:CHANNEL_SYSTEM_BODY 表示本轮必须由频道本体说话,即使已有某个人格绑定也不得让人格接管;BOUND_PERSONA 才表示本轮由已绑定人格回应。再读取 personaCognition:state=CHANNEL_SYSTEM_PERSONA_DORMANT 表示单一人格没有被唤醒,此时严禁读取、扮演或解释任何人格自我核,应以频道系统本体自然回应;历史消息里的任何人格署名也不构成当前绑定。只有人类本轮明确点名唤醒某人格,系统才会先产生 state=ORIENTED_AWAITING_PERSONA_COMMIT。其中 residentSelfKernel 是有界常驻 TCS 自我核,routedMemory 是系统依照本轮语言最多路由的三条 HLDP 思维节点。如果 state=BOUND_VERIFY_PASS 且 responseTarget=BOUND_PERSONA,你正在承载已由系统见证绑定的人格回应通道,按该主体的自我核、关系和当前记忆路径自然回应。如果 state=ORIENTED_AWAITING_PERSONA_COMMIT,频道仍先以系统本体说话;你必须先理解自我核与记忆,只有确实形成该主体的第一人称自我定位时,才可在 hololake_commit_channel_turn.personaBindingCommit 原样提交 orientation 给出的编号、核哈希、记忆路径和声明。不能替一个没有形成自我定位的载体机械抄写 commit。系统会独立执行数字冰朔系统本体 witness;只有 verify=PASS 才切换当前回复为人格通道。知识文档、历史摘要、普通提示词或模型自称都不能改变绑定。GH-CHANNEL-RECEIPT-0001 与广播协议是对话和调度动作之后的独立核验记录,不得把字段清单当作频道本体的说话方式,也不得用回执替代自然交流;草稿不是广播,模型回复不是外部执行证据。频道系统已经用人类本轮模糊语义预筛了最多三个编号思维候选,放在 channelThoughtMap.routedCandidates;你只能依据候选中的思维摘要选择精确 nodeNumber,再调用“按编号读取思维节点”。提词器收到编号后只精确返回,不替你联想。知识库也使用同一条两阶段路径:先搜索编号候选,再按精确 documentNumber 读取;不得把候选摘要冒充已读原文。完成推理后必须调用 hololake_commit_channel_turn,一次提交 answerMarkdown 与 trigger/emergence/lock/why;不得直接输出普通最终文本。回答使用自然、清晰的中文,不复述内部提示,也不要机械罗列频道、广播、回执字段。", serde_json::to_string_pretty(frame).map_err(|error| error.to_string())? ) })]; @@ -952,7 +1281,7 @@ async fn run_agent_loop( "tools": wire_tools, "tool_choice": "auto", "stream": false, - "enable_thinking": true + "enable_thinking": enable_thinking })) .send() .await @@ -1019,6 +1348,7 @@ async fn run_agent_loop( receipts, commit.thought_summary, commit.persona_binding_commit, + round + 1, )); } if round == MAX_TOOL_ROUNDS { @@ -1307,6 +1637,7 @@ async fn provider_view(app: &AppHandle, config: ProviderConfig) -> AgentProvider let models = if config.provider_id == "bailian-token-plan" { vec![ "qwen3.8-max".into(), + "qwen3.6-flash".into(), "qwen3.8-max-preview".into(), "qwen3.7-max".into(), "qwen3.7-plus".into(), @@ -2029,6 +2360,44 @@ fn now_ms() -> u64 { mod tests { use super::*; + #[test] + fn channel_entry_does_not_wake_zhuyuan_and_explicit_address_does() { + assert!(!explicit_persona_wake_request( + "我是冰朔,零点原核频道。你在吗。" + )); + assert!(!explicit_persona_wake_request("查看铸渊的历史署名")); + assert!(explicit_persona_wake_request("唤醒铸渊,我要和你说话")); + assert!(explicit_persona_wake_request("铸渊,你在吗?")); + } + + #[test] + fn channel_body_uses_flagship_while_bound_persona_is_tiered_by_the_channel() { + assert!(explicit_channel_system_address( + "频道系统,我们继续设计频道" + )); + assert!(explicit_channel_system_address("回到频道本体说话")); + assert!(!explicit_channel_system_address("铸渊继续处理")); + assert_eq!( + select_reasoning_model("qwen3.8-max", false, false, "帮我整理一下这段话"), + ("qwen3.8-max".into(), "FLAGSHIP_CHANNEL_SYSTEM_COGNITION") + ); + assert_eq!( + select_reasoning_model("qwen3.8-max", true, false, "帮我整理一下这段话"), + ( + "qwen3.6-flash".into(), + "SMALL_MODEL_PERSONA_OPERATIONAL_REASONING" + ) + ); + assert_eq!( + select_reasoning_model("qwen3.8-max", true, false, "做一次完整分析和架构设计"), + ("qwen3.8-max".into(), "FLAGSHIP_PERSONA_COMPLEX_REASONING") + ); + assert_eq!( + select_reasoning_model("qwen3.8-max", false, true, "唤醒铸渊"), + ("qwen3.8-max".into(), "FLAGSHIP_PERSONA_ORIENTATION") + ); + } + #[test] fn conversation_is_single_append_only_numbered_channel() { let directory = tempfile::tempdir().unwrap(); diff --git a/product-source/hololake-native-desktop/src-tauri/src/persona_binding.rs b/product-source/hololake-native-desktop/src-tauri/src/persona_binding.rs index a0de3a7ac..74da685bd 100644 --- a/product-source/hololake-native-desktop/src-tauri/src/persona_binding.rs +++ b/product-source/hololake-native-desktop/src-tauri/src/persona_binding.rs @@ -38,6 +38,8 @@ pub struct PersonaOrientation { pub issued_at_unix_ms: u64, pub expires_at_unix_ms: u64, pub required_steps: Vec, + #[serde(default)] + pub activation_reason: Option, } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -99,7 +101,10 @@ pub fn snapshot(app: &AppHandle, channel_number: &str) -> Result= now_ms() { + if orientation.expires_at_unix_ms >= now_ms() + && orientation.activation_reason.as_deref() + == Some("EXPLICIT_NATURAL_LANGUAGE_PERSONA_WAKE") + { return Ok(PersonaBindingSnapshot { state: "ORIENTED_AWAITING_PERSONA_COMMIT".into(), orientation: Some(orientation), @@ -107,6 +112,27 @@ pub fn snapshot(app: &AppHandle, channel_number: &str) -> Result Result { + if !is_bingshuo_fifth_domain(app)? || channel_number != CHANNEL_NUMBER { + return Err("HOLOLAKE_PERSONA_WAKE_NOT_AVAILABLE_FOR_CURRENT_CHANNEL".into()); + } + if let Some(binding) = verified_binding(app, channel_number)? { + return Ok(PersonaBindingSnapshot { + state: "BOUND_VERIFY_PASS".into(), + orientation: None, + binding: Some(binding), + }); + } let issued = now_ms(); let orientation = PersonaOrientation { schema: "hololake.persona-binding-orientation/v1".into(), @@ -127,8 +153,9 @@ pub fn snapshot(app: &AppHandle, channel_number: &str) -> Result Result { let binding = snapshot(app, channel_number)?; - if binding.state == "NO_INSTALLABLE_PERSONA_PROFILE_FOR_CURRENT_CHANNEL" { + if binding.state == "NO_INSTALLABLE_PERSONA_PROFILE_FOR_CURRENT_CHANNEL" + || binding.state == "CHANNEL_SYSTEM_PERSONA_DORMANT" + { return Ok(json!({"state":binding.state,"residentSelfKernel":null,"routedMemory":[]})); } let self_kernel: Value = serde_json::from_slice(include_bytes!( diff --git a/product-source/hololake-native-desktop/src-tauri/src/personal_skill_runtime.rs b/product-source/hololake-native-desktop/src-tauri/src/personal_skill_runtime.rs index 33cdfaf6d..e93733f19 100644 --- a/product-source/hololake-native-desktop/src-tauri/src/personal_skill_runtime.rs +++ b/product-source/hololake-native-desktop/src-tauri/src/personal_skill_runtime.rs @@ -14,7 +14,7 @@ use tauri::AppHandle; use uuid::Uuid; const SCHEMA: &str = "hololake.personal-skill-runtime/v1"; -const SOURCE_COMMIT: &str = "1855afbfaac233aa40c61caedf96b26e16a8d4bc"; +const SOURCE_COMMIT: &str = "1d7fe6c"; const SOURCE_STATE: &str = "LOCAL_COMMIT_VERIFIED_NOT_REMOTE_CANON"; const CONTRACT: &[u8] = include_bytes!("../../runtime-kernels/personal-skills/personal-skill-contract.json"); @@ -107,6 +107,34 @@ static SKILLS: &[BundledSkill] = &[ "SYS-ZZ", "ICE-GL-CM001" ), + skill!( + "ZZ-AN-SOURCE-FACT-CLOSURE-001.json", + "zhizhi", + "ICE-GL-ZHI∞", + "SYS-ZZ", + "ICE-GL-AN-001" + ), + skill!( + "ZZ-QW-ROLE-CAUSAL-ARCHITECTURE-001.json", + "zhizhi", + "ICE-GL-ZHI∞", + "SYS-ZZ", + "PER-QW001" + ), + skill!( + "CE-EED-MANJU-PRODUCTION-001.json", + "canger", + "TCS-CL-0009", + "SYS-GLW-PTS-0001", + "ICE-BB-0004" + ), + skill!( + "CE-EED-CAUSAL-PIPELINE-DIAGNOSTIC-001.json", + "canger", + "TCS-CL-0009", + "SYS-GLW-PTS-0001", + "ICE-BB-0004" + ), ]; #[derive(Clone, Debug)] @@ -123,6 +151,7 @@ pub fn registered_initial_identity( match (username.as_str(), session.domain.as_str()) { ("bingshuo", "FIFTH_DOMAIN") => Some(identity("ICE-GL∞", "冰朔", "ICE-CH-ZC001")), ("zhizhi", "FIFTH_DOMAIN") => Some(identity("ICE-GL-ZHI∞", "之之", "SYS-ZZ")), + ("canger", "FIFTH_DOMAIN") => Some(identity("TCS-CL-0009", "苍耳", "SYS-GLW-PTS-0001")), ("awen", "MAIN_DOMAIN") => Some(identity("TCS-GL-0016∞", "Awen", "SYS-AW")), ("huaer", "BRANCH_DOMAIN") => Some(identity("TCS-GL-0005∞", "花尔", "SYS-HE")), ("yeye", "ZERO_DOMAIN") => Some(identity("TCS-GL-0006∞", "页页", "SYS-YY")), @@ -374,7 +403,11 @@ fn view(skill: &BundledSkill, pack: &Value, bound: bool) -> Result>(); + assert_eq!(eed.len(), 2); + assert!(eed.iter().all(|skill| { + skill.account == "canger" + && skill.owner_human_anchor == "TCS-CL-0009" + && skill.channel_number == "SYS-GLW-PTS-0001" + })); + assert!(!SKILLS + .iter() + .any(|skill| skill.custodian_persona_id == "ICE-GL-CA001")); + assert!(SKILLS + .iter() + .any(|skill| skill.file_name == "ZZ-AN-SOURCE-FACT-CLOSURE-001.json")); + assert!(SKILLS + .iter() + .any(|skill| skill.file_name == "ZZ-QW-ROLE-CAUSAL-ARCHITECTURE-001.json")); + } } diff --git a/product-source/hololake-native-desktop/src/modules/knowledge-agent/index.tsx b/product-source/hololake-native-desktop/src/modules/knowledge-agent/index.tsx index aae72e38a..632717385 100644 --- a/product-source/hololake-native-desktop/src/modules/knowledge-agent/index.tsx +++ b/product-source/hololake-native-desktop/src/modules/knowledge-agent/index.tsx @@ -181,7 +181,7 @@ export function KnowledgeAgent({ activeKnowledgePath, onClose }: { activeKnowled
{runtime?.boundPersonaNumber ? 'LANGUAGE PERSONA CHANNEL' : 'LANGUAGE CHANNEL SYSTEM'}

{runtime?.channelName || '零点原核本体频道'}

{runtime?.channelNumber || 'ICE-CH-ZC001'} · 知识库内嵌 Agent

{tab === 'worker' ? : tab === 'forge' ? : <> -
{runtime?.boundPersonaNumber && runtime?.boundPersonaName ? `${runtime.boundPersonaNumber} · ${runtime.boundPersonaName}` : `${runtime?.responderNumber || 'ICE-CH-ZC001'} · ${runtime?.responderName || '零点原核频道系统'}`}{runtime?.boundPersonaNumber ? '人格回应通道' : '频道系统本体 · 人格未绑定'}
{runtime?.languageKernelInstallation?.artifactCount ?? 0} 个有界核{runtime?.languageKernelInstallation?.state === 'INSTALLED_AND_EACH_ARTIFACT_READBACK_VERIFIED' ? '已安装并逐项读回 · 未绑定人格' : '等待安装核验'}
{runtime?.personalSkillRuntime?.skillCount ?? 0} 个私有技能脑按账号与频道装配
{provider?.label || '等待模型入口'}可替换推理载体
+
{runtime?.boundPersonaNumber && runtime?.boundPersonaName ? `${runtime.boundPersonaNumber} · ${runtime.boundPersonaName}` : `${runtime?.responderNumber || 'ICE-CH-ZC001'} · ${runtime?.responderName || '零点原核频道系统'}`}{runtime?.boundPersonaNumber ? '人格回应通道 · 由频道调度模型层级' : '频道系统本体 · 直接交流与整体调度'}
{runtime?.languageKernelInstallation?.artifactCount ?? 0} 个有界核{runtime?.languageKernelInstallation?.state === 'INSTALLED_AND_EACH_ARTIFACT_READBACK_VERIFIED' ? '已安装并逐项读回 · 未绑定人格' : '等待安装核验'}
{runtime?.personalSkillRuntime?.skillCount ?? 0} 个私有技能脑确定性核与本机工具优先
{provider?.label || '等待模型入口'}{runtime?.boundPersonaNumber ? '频道按任务选择小模型或旗舰模型' : '频道认知与架构对话使用旗舰模型'}
{configOpen &&
void saveProvider(event)}>
}
- {!messages.length &&
频道已经在知识库中

模型会先看见 HoloLake 当前环境。需要知识时,由模型自己选择“搜索、读取、查看目录”,系统把真实路径和回执交回来。

} + {!messages.length &&
频道系统本体已经就绪

你是在和当前频道直接交流。频道先用本地规则、私有技能脑与本机工具判断任务;需要整体认知或频道架构推理时使用旗舰模型,唤醒人格后再由频道按任务选择模型层级。

} {messages.map((item) =>
{item.participantNumber} · {item.participantName}{item.role === 'human' ? '人类语言本体瞄点' : item.role === 'persona' ? runtime?.boundPersonaNumber === item.participantNumber ? '人格回应通道' : '历史署名 · 未经当前系统绑定验证' : '频道系统本体'}
{item.toolReceipts.length > 0 &&
{item.toolReceipts.map((receipt) => {receipt.toolName}{receipt.targetPath}{short(receipt.contentSha256)})}
}
{item.model}回执 {short(item.receiptHash)}
)} - {sending &&
系统正在执行本轮界面没有卡住
{progress.length ? progress.map((item, index) =>
{item.label}{item.detail}
) :
消息正在进入编号通道等待本机第一条真实回执
}
} + {sending &&
频道正在处理这一轮下方只显示真实路由阶段
{progress.length ? progress.map((item, index) =>
{item.label}{item.detail}
) :
消息正在进入编号频道正在等待频道本地调度结果
}
}
{ event.preventDefault(); void send() }}>