fix: integrate language channel through numbered IPC and isolate education module
正向合并正律化(2026-08-20): - 语言人格频道与执行手脚训练按编号 IPC 正律接入(MOD-0035/0036,OP-0158..0163), 剥离直连 tauri::command,恢复单一 numbered 网关铁律 - 教育模块按冰朔谕与 2026-08-19 隔离回执整体移出(证据归隔离区),留空模块占位 - 光湖主域门户挂公告与版本中心(冰朔要求的公告与版本更新主阵地) - 知识工作台内嵌共同工作频道(PersonaChannelBody embedded + knowledgeFocus) - foundation/stage-one 合同补聊天运行时验收声明;版本对齐 0.9.1 - 测试名册计数与断言更新至合并后现实:JS 180/180,Rust 194/194
This commit is contained in:
parent
dd1375f089
commit
add00a0377
42 changed files with 622 additions and 5580 deletions
|
|
@ -1,265 +0,0 @@
|
||||||
{
|
|
||||||
"schema": "hololake.education-workspace/v1",
|
|
||||||
"record_id": "HLP-EDUCATION-WORKSPACE-001",
|
|
||||||
"state": "ADMITTED_INSTALLED_RUNTIME_ACCEPTED",
|
|
||||||
"domain_entry": "BRANCH_DOMAIN",
|
|
||||||
"industry": "EDUCATION",
|
|
||||||
"channel_id": "GH-EDU-INIT-001",
|
|
||||||
"package": {
|
|
||||||
"package_key": "hololake.official.education-workbench",
|
|
||||||
"official_module_number": "HLP-MOD-OFFICIAL-EDUCATION-WORKBENCH-0001",
|
|
||||||
"registration_state": "OFFICIAL_NUMBER_ACCEPTED",
|
|
||||||
"current_mount": "ACTIVE_AFTER_SIGNED_INSTALL_MOUNT_AND_SELF_TEST",
|
|
||||||
"future_distribution": "HOLOLAKE_OFFICIAL_MODULE_MARKETPLACE"
|
|
||||||
},
|
|
||||||
"foundation_dependency": {
|
|
||||||
"module_id": "hololake.builtin.channel-workbench",
|
|
||||||
"state": "BUILT_IN_FOUNDATION",
|
|
||||||
"engines": ["LEXICAL_0_49", "FORTUNE_SHEET_1_0_4"],
|
|
||||||
"rule": "EDUCATION_MODULE_CONSUMES_FOUNDATION_WITHOUT_DUPLICATING_OFFICE_ENGINES"
|
|
||||||
},
|
|
||||||
"native_storage": {
|
|
||||||
"owner": "HOLOLAKE_NATIVE_RUST_CORE",
|
|
||||||
"namespace": "education-workspace-v1",
|
|
||||||
"engine": "SQLITE",
|
|
||||||
"authenticated_account_required": true,
|
|
||||||
"cross_account_projection_allowed": false,
|
|
||||||
"public_catalog_contains_private_data": false,
|
|
||||||
"restart_readback_required": true
|
|
||||||
},
|
|
||||||
"document_module": {
|
|
||||||
"module_id": "EDU-DOCUMENT",
|
|
||||||
"state": "DEVELOPMENT_FEATURE_MOUNTED",
|
|
||||||
"engine": "CHANNEL_WORKBENCH_LEXICAL_0_49_0_WITH_EDUCATION_TEMPLATES_AND_HOLOLAKE_MARKDOWN_STATE",
|
|
||||||
"capabilities": [
|
|
||||||
"LIST",
|
|
||||||
"CREATE",
|
|
||||||
"CREATE_COPY",
|
|
||||||
"READ",
|
|
||||||
"EDIT_TITLE_AND_BODY",
|
|
||||||
"EXPLICIT_READ_AND_EDIT_MODES",
|
|
||||||
"HUMAN_SETTINGS_MENU",
|
|
||||||
"BASIC_FORMATTING_TOOLBAR",
|
|
||||||
"RICH_TEXT_COMMAND_ENGINE",
|
|
||||||
"UNDO_AND_REDO_HISTORY",
|
|
||||||
"MARKDOWN_SHORTCUTS_AND_BIDIRECTIONAL_STATE_TRANSLATION",
|
|
||||||
"TITLE_DIRECTORY_FILTER",
|
|
||||||
"FORMAL_TEACHING_TEMPLATES",
|
|
||||||
"NATIVE_READING_CANVAS",
|
|
||||||
"EXPORT_MARKDOWN_AND_HTML",
|
|
||||||
"SAVE_WITH_EXPECTED_REVISION",
|
|
||||||
"RECOVERABLE_ARCHIVE"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"table_module": {
|
|
||||||
"module_id": "EDU-TABLE",
|
|
||||||
"state": "DEVELOPMENT_FEATURE_MOUNTED",
|
|
||||||
"engine": "CHANNEL_WORKBENCH_FORTUNE_SHEET_1_0_4_FOR_REAL_CELLS_AND_FORMULAS_PLUS_TANSTACK_8_21_3_FOR_EDUCATION_VIEWS",
|
|
||||||
"capabilities": [
|
|
||||||
"LIST",
|
|
||||||
"CREATE",
|
|
||||||
"CREATE_COPY",
|
|
||||||
"READ",
|
|
||||||
"EDIT_TITLE",
|
|
||||||
"HUMAN_SETTINGS_MENU",
|
|
||||||
"ADD_AND_REMOVE_COLUMNS",
|
|
||||||
"ADD_AND_REMOVE_ROWS",
|
|
||||||
"EDIT_CELLS",
|
|
||||||
"TITLE_DIRECTORY_FILTER",
|
|
||||||
"CELL_CONTENT_FILTER",
|
|
||||||
"COLUMN_SORT_ASCENDING_DESCENDING_OR_SOURCE_ORDER",
|
|
||||||
"HEADLESS_FILTER_SORT_GROUP_AND_AGGREGATE_ROW_MODELS",
|
|
||||||
"BOUNDED_PAGINATION_ROW_MODEL_FOR_LARGE_EDITABLE_TABLES",
|
|
||||||
"GRID_AND_EDITABLE_CARD_VIEWS",
|
|
||||||
"GROUPED_CLASSIFICATION_BOARD_FROM_SELECTED_FIELD",
|
|
||||||
"LIVE_DATA_OVERVIEW_WITH_SELECTED_GROUP_AND_MEASURE",
|
|
||||||
"HEADLESS_TABLE_DATA_DETACHED_OUTSIDE_TABLE_SURFACE",
|
|
||||||
"MULTI_ROW_MULTI_COLUMN_TABULAR_PASTE",
|
|
||||||
"EXTERNAL_SPREADSHEET_IMPORT_WITH_PREWRITE_PROFILE",
|
|
||||||
"XLSX_CSV_TSV_AND_NATIVE_EXPORT",
|
|
||||||
"SAVE_WITH_EXPECTED_REVISION",
|
|
||||||
"RECOVERABLE_ARCHIVE"
|
|
||||||
],
|
|
||||||
"limits": {
|
|
||||||
"maximum_columns": 30,
|
|
||||||
"maximum_rows": 1000,
|
|
||||||
"maximum_cell_bytes": 10000
|
|
||||||
},
|
|
||||||
"sensitive_field_visibility": {
|
|
||||||
"local_default": "HIDDEN_UNTIL_HUMAN_REVEALS",
|
|
||||||
"human_can_hide": true,
|
|
||||||
"human_can_show_again": true,
|
|
||||||
"external_model_transfer_authority": "SEPARATE_EXPLICIT_PER_FILE_CONSENT"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"content_translation_layer": {
|
|
||||||
"service_id": "EDU-CONTENT-TRANSLATOR",
|
|
||||||
"state": "DETERMINISTIC_RUNTIME_READY_MODEL_ASSIST_SLOT_RESERVED",
|
|
||||||
"profile_schema": "hololake.content-profile/v1",
|
|
||||||
"import_adapter": "EDU-TABLE-IMPORT-ADAPTER/v1",
|
|
||||||
"export_adapter": "EDU-TABLE-EXPORT-ADAPTER/v1",
|
|
||||||
"order": [
|
|
||||||
"FILE_CONTAINER_SNIFF",
|
|
||||||
"PAGE_AND_STRUCTURE_INVENTORY",
|
|
||||||
"BOUNDED_LOCAL_RECOGNITION",
|
|
||||||
"EXPLAINABLE_SEMANTIC_FOCUS_INFERENCE",
|
|
||||||
"UNASSIGNED_CHANNEL_STAGING",
|
|
||||||
"HUMAN_INDUSTRY_ROUTE_DECISION",
|
|
||||||
"NATIVE_TRANSLATION",
|
|
||||||
"ATOMIC_MODULE_REGISTRATION_AFTER_CONFIRMATION",
|
|
||||||
"HUMAN_RECEIPT"
|
|
||||||
],
|
|
||||||
"import_assignment": {
|
|
||||||
"default_scope": "UNASSIGNED",
|
|
||||||
"education_directory_before_human_confirmation": false,
|
|
||||||
"existing_legacy_imports_reclassified_on_migration": true,
|
|
||||||
"source_file_mutated": false
|
|
||||||
},
|
|
||||||
"supported_imports": ["XLSX", "XLS", "XLSM", "XLSB", "ODS", "CSV", "TSV", "HOLOLAKE_NATIVE"],
|
|
||||||
"supported_exports": ["XLSX", "CSV", "TSV", "HOLOLAKE_NATIVE"],
|
|
||||||
"local_import_without_model_api": "ENABLED_FOR_ALL_SUPPORTED_FORMATS",
|
|
||||||
"semantic_focus": {
|
|
||||||
"current_mode": "DETERMINISTIC_BASELINE_ONLY",
|
|
||||||
"signals": ["SOURCE_AND_PAGE_TITLE", "COLUMN_SEMANTICS", "NUMERIC_COVERAGE"],
|
|
||||||
"low_confidence_behavior": "ASK_HUMAN_TO_SELECT_PRIMARY_MEASURE",
|
|
||||||
"model_dynamic_judgment": "RESERVED_FOR_MODEL_API_AND_AGENT_STAGE"
|
|
||||||
},
|
|
||||||
"adaptive_rendering": {
|
|
||||||
"current_mode": "DETERMINISTIC_BASELINE_WITH_EXPLAINABLE_DEGRADATION",
|
|
||||||
"editable_cell_budget": 240,
|
|
||||||
"baseline_inputs": [
|
|
||||||
"ROW_COUNT",
|
|
||||||
"COLUMN_COUNT",
|
|
||||||
"FIELD_TYPES",
|
|
||||||
"NUMERIC_COVERAGE",
|
|
||||||
"GROUP_CARDINALITY",
|
|
||||||
"FOCUS_CONFIDENCE"
|
|
||||||
],
|
|
||||||
"degradation_order": [
|
|
||||||
"LARGE_EDITABLE_GRID_TO_DYNAMIC_PAGINATION",
|
|
||||||
"NO_RELIABLE_MEASURE_TO_COUNT_SUMMARY",
|
|
||||||
"AMBIGUOUS_FOCUS_TO_HUMAN_SELECTION",
|
|
||||||
"UNSUPPORTED_STRUCTURE_TO_HUMAN_ASSISTANCE_RECEIPT"
|
|
||||||
],
|
|
||||||
"local_baseline_always_available": true,
|
|
||||||
"model_adjustment": {
|
|
||||||
"api_slot": "HOLOLAKE_MODEL_RECOGNITION_API/v1",
|
|
||||||
"state": "RESERVED_FOR_MODEL_API_AND_AGENT_STAGE",
|
|
||||||
"proposal_fields": [
|
|
||||||
"PRIMARY_FOCUS",
|
|
||||||
"GROUP_FIELD",
|
|
||||||
"MEASURE_FIELD",
|
|
||||||
"VIEW_COMPOSITION",
|
|
||||||
"PAGE_DENSITY"
|
|
||||||
],
|
|
||||||
"proposal_must_pass_local_structure_validation": true,
|
|
||||||
"proposal_must_obey_local_resource_limits": true,
|
|
||||||
"human_confirmation_before_rule_activation": true,
|
|
||||||
"model_can_mutate_source_data": false,
|
|
||||||
"failure_behavior": "RETURN_TO_LOCAL_BASELINE_WITH_RECEIPT"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"unknown_input_behavior": "RETURN_HUMAN_ASSISTANCE_RECEIPT_WITHOUT_MODULE_WRITE_OR_EXTERNAL_MODEL_TRANSFER",
|
|
||||||
"model_assist": {
|
|
||||||
"api_slot": "HOLOLAKE_MODEL_RECOGNITION_API/v1",
|
|
||||||
"current_state": "NOT_CONFIGURED",
|
|
||||||
"provider": "USER_SELECTED",
|
|
||||||
"secret_storage": "OPERATING_SYSTEM_SECRET_STORE_REQUIRED",
|
|
||||||
"file_transfer_default": "DENY_UNTIL_EXPLICIT_PER_FILE_CONSENT",
|
|
||||||
"rule_update_flow": ["MODEL_CANDIDATE", "LOCAL_VALIDATION", "HUMAN_CONFIRMATION", "VERSIONED_INSTALL"],
|
|
||||||
"learning_scopes": ["PRIVATE_ONLY", "SHARE_ANONYMIZED_RULE"]
|
|
||||||
},
|
|
||||||
"source_file_mutation_allowed": false,
|
|
||||||
"silent_truncation_allowed": false
|
|
||||||
},
|
|
||||||
"data_cleanup_module": {
|
|
||||||
"module_id": "EDU-DATA",
|
|
||||||
"state": "DEVELOPMENT_FEATURE_MOUNTED",
|
|
||||||
"source": "CURRENT_AUTHENTICATED_ACCOUNTS_EDUCATION_TABLES",
|
|
||||||
"capabilities": [
|
|
||||||
"SELECT_ACCOUNT_TABLE",
|
|
||||||
"TRIM_CELL_BOUNDARY_WHITESPACE",
|
|
||||||
"REMOVE_FULLY_EMPTY_ROWS",
|
|
||||||
"REMOVE_EXACT_DUPLICATE_ROWS_KEEP_FIRST",
|
|
||||||
"PREVIEW_BEFORE_WRITE",
|
|
||||||
"FULL_TABLE_ANALYSIS_WITH_BOUNDED_CHANGE_PREVIEW",
|
|
||||||
"ACCESSIBILITY_SAFE_NON_TABLE_CHANGE_LIST",
|
|
||||||
"HUMAN_CONFIRMATION_BEFORE_SAVE",
|
|
||||||
"SAVE_WITH_EXPECTED_REVISION"
|
|
||||||
],
|
|
||||||
"preview_change_limit": 12,
|
|
||||||
"preview_cell_value_character_limit": 120,
|
|
||||||
"full_table_analysis_is_limited_by_preview": false,
|
|
||||||
"automatic_destructive_cleanup_allowed": false,
|
|
||||||
"cross_account_data_allowed": false
|
|
||||||
},
|
|
||||||
"automation_module": {
|
|
||||||
"module_id": "EDU-AUTOMATION",
|
|
||||||
"state": "DEVELOPMENT_FEATURE_MOUNTED",
|
|
||||||
"source": "CURRENT_AUTHENTICATED_ACCOUNTS_EDUCATION_TABLES",
|
|
||||||
"capabilities": [
|
|
||||||
"CREATE_AND_SAVE_RULE",
|
|
||||||
"EQUALS_CONTAINS_EMPTY_AND_NONEMPTY_CONDITIONS",
|
|
||||||
"SET_TARGET_CELL_VALUE",
|
|
||||||
"PREVIEW_MATCHED_ROWS_AND_CHANGED_CELLS",
|
|
||||||
"HUMAN_CONFIRMATION_BEFORE_EXECUTION",
|
|
||||||
"RULE_AND_TABLE_REVISION_LOCK",
|
|
||||||
"SIGNED_PREVIEW_TOKEN",
|
|
||||||
"ATOMIC_TABLE_WRITE_AND_EXECUTION_RECEIPT",
|
|
||||||
"RECOVERABLE_RULE_ARCHIVE"
|
|
||||||
],
|
|
||||||
"background_or_scheduled_execution_allowed": false,
|
|
||||||
"cross_account_data_allowed": false,
|
|
||||||
"model_execution_authority_granted": false
|
|
||||||
},
|
|
||||||
"composition_module": {
|
|
||||||
"module_id": "EDU-COMPOSITION",
|
|
||||||
"state": "DEVELOPMENT_FEATURE_MOUNTED",
|
|
||||||
"source": "CURRENT_AUTHENTICATED_ACCOUNTS_REAL_KNOWLEDGE_CATALOG",
|
|
||||||
"capabilities": [
|
|
||||||
"NATIVE_TYPED_OBJECT",
|
|
||||||
"REGISTERED_MODULE_EXECUTION_GRAPH",
|
|
||||||
"SELECT_DIMENSION_AND_MEASURE",
|
|
||||||
"DASHBOARD_PROJECTION",
|
|
||||||
"COMPARISON_PROJECTION",
|
|
||||||
"VERTICAL_BAR_PROJECTION",
|
|
||||||
"CLASSIFICATION_PROJECTION",
|
|
||||||
"TABLE_PROJECTION",
|
|
||||||
"ONE_EXECUTION_RESULT_MULTIPLE_HUMAN_VIEWS"
|
|
||||||
],
|
|
||||||
"read_only": true,
|
|
||||||
"hardcoded_sample_data_used": false,
|
|
||||||
"model_execution_authority_granted": false
|
|
||||||
},
|
|
||||||
"remaining_slots": [],
|
|
||||||
"authority": {
|
|
||||||
"browser_local_storage_allowed": false,
|
|
||||||
"webview_direct_file_access_allowed": false,
|
|
||||||
"model_execution_authority_granted": false,
|
|
||||||
"external_application_embedded": false
|
|
||||||
},
|
|
||||||
"current_acceptance": {
|
|
||||||
"state": "PASS",
|
|
||||||
"runtime_module_number": "HLP-MOD-OFFICIAL-EDUCATION-WORKBENCH-0001",
|
|
||||||
"numbered_ipc_module": "HLP-NIPC-MOD-0024",
|
|
||||||
"numbered_operations": "HLP-NIPC-OP-0085..0102",
|
|
||||||
"signed_package_sha256": "a9f6fb7dd9c4aaf910a5d20d0a1d72dea6ab921cf36dd50d46dd88cde3a09e03",
|
|
||||||
"signed_app_binary_sha256": "028592b137fe4f19a642e1301d6fb500e2a4fde50ddb616e229de8a48090803c",
|
|
||||||
"developer_id_team": "825A9L3G7Q",
|
|
||||||
"developer_id_cdhash": "4c88261b4b573d08edcbba8d67d8afe27ad82a3e",
|
|
||||||
"runtime_receipts": {
|
|
||||||
"install": "68f296a11be19c384cb67af9f529a03cb8389bd256c153c5905451fbc0011a24",
|
|
||||||
"mount": "ddb6a51d47f6a58a72a7fc748eff71687cbd04be5dde33dbce590a7abba6af5f",
|
|
||||||
"self_test_pass": "80d59b7c9c2f14005fd8a24748a25c2be0d55198fdcb621f528267ae6dccc12a"
|
|
||||||
},
|
|
||||||
"restart_readback": {
|
|
||||||
"module_state": "ACTIVE",
|
|
||||||
"document": "冰朔教育迁移验收 · revision 2",
|
|
||||||
"table": "冰朔教育迁移验收表 · 1 row · revision 4",
|
|
||||||
"automation": "验收状态自动化 · preview 1 row / 1 cell · APPLIED revision 3 to 4"
|
|
||||||
},
|
|
||||||
"existing_user_data_deleted_or_overwritten": false,
|
|
||||||
"apple_notarization_scope": "FINAL_RELEASE_CANDIDATE_ONLY_NOT_THIS_DEBUG_ACCEPTANCE_BUNDLE"
|
|
||||||
},
|
|
||||||
"open_source_donor_assessment": "contracts/education-open-source-donor-assessment.json"
|
|
||||||
}
|
|
||||||
|
|
@ -67,9 +67,6 @@
|
||||||
"get_channel_workbench_snapshot",
|
"get_channel_workbench_snapshot",
|
||||||
"get_persona_channel_body",
|
"get_persona_channel_body",
|
||||||
"get_channel_growth_snapshot",
|
"get_channel_growth_snapshot",
|
||||||
"get_education_workspace_snapshot",
|
|
||||||
"import_education_tables_from_dialog",
|
|
||||||
"get_education_recognition_capability",
|
|
||||||
"get_web_novel_workspace_snapshot",
|
"get_web_novel_workspace_snapshot",
|
||||||
"inspect_web_novel_document_from_dialog",
|
"inspect_web_novel_document_from_dialog",
|
||||||
"start_mobile_sync",
|
"start_mobile_sync",
|
||||||
|
|
@ -82,7 +79,9 @@
|
||||||
"get_marketplace_snapshot",
|
"get_marketplace_snapshot",
|
||||||
"sync_marketplace_catalog",
|
"sync_marketplace_catalog",
|
||||||
"get_active_cognitive_skills",
|
"get_active_cognitive_skills",
|
||||||
"get_external_ai_gateway_status"
|
"get_external_ai_gateway_status",
|
||||||
|
"discover_model_providers",
|
||||||
|
"get_execution_limb_snapshot"
|
||||||
],
|
],
|
||||||
"input_wrapper_aliases": [
|
"input_wrapper_aliases": [
|
||||||
"confirm_hololake_update_install",
|
"confirm_hololake_update_install",
|
||||||
|
|
@ -133,21 +132,6 @@
|
||||||
"append_persona_language",
|
"append_persona_language",
|
||||||
"record_channel_growth_event",
|
"record_channel_growth_event",
|
||||||
"update_channel_growth_sharing",
|
"update_channel_growth_sharing",
|
||||||
"create_education_document",
|
|
||||||
"read_education_document",
|
|
||||||
"save_education_document",
|
|
||||||
"archive_education_document",
|
|
||||||
"create_education_table",
|
|
||||||
"read_education_table",
|
|
||||||
"save_education_table",
|
|
||||||
"archive_education_table",
|
|
||||||
"assign_imported_table_to_education",
|
|
||||||
"export_education_table_to_dialog",
|
|
||||||
"create_education_automation_rule",
|
|
||||||
"save_education_automation_rule",
|
|
||||||
"archive_education_automation_rule",
|
|
||||||
"preview_education_automation_rule",
|
|
||||||
"execute_education_automation_rule",
|
|
||||||
"create_web_novel_work",
|
"create_web_novel_work",
|
||||||
"read_web_novel_work",
|
"read_web_novel_work",
|
||||||
"save_web_novel_work",
|
"save_web_novel_work",
|
||||||
|
|
@ -186,7 +170,11 @@
|
||||||
"export_web_novel_author_delivery",
|
"export_web_novel_author_delivery",
|
||||||
"revoke_mobile_sync_device",
|
"revoke_mobile_sync_device",
|
||||||
"decide_authorization_request",
|
"decide_authorization_request",
|
||||||
"set_external_ai_gateway_exposure"
|
"set_external_ai_gateway_exposure",
|
||||||
|
"get_conversation",
|
||||||
|
"send_message",
|
||||||
|
"upsert_model_provider",
|
||||||
|
"run_execution_limb_training_drill"
|
||||||
],
|
],
|
||||||
"direct_field_aliases": {
|
"direct_field_aliases": {
|
||||||
"perform_code_repo_login": [
|
"perform_code_repo_login": [
|
||||||
|
|
@ -349,11 +337,6 @@
|
||||||
"target_number": "HLP-NIPC-TGT-0023",
|
"target_number": "HLP-NIPC-TGT-0023",
|
||||||
"internal_name": "persona_channel_body"
|
"internal_name": "persona_channel_body"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"module_number": "HLP-NIPC-MOD-0024",
|
|
||||||
"target_number": "HLP-NIPC-TGT-0024",
|
|
||||||
"internal_name": "education_workspace"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"module_number": "HLP-NIPC-MOD-0025",
|
"module_number": "HLP-NIPC-MOD-0025",
|
||||||
"target_number": "HLP-NIPC-TGT-0025",
|
"target_number": "HLP-NIPC-TGT-0025",
|
||||||
|
|
@ -403,6 +386,16 @@
|
||||||
"module_number": "HLP-NIPC-MOD-0034",
|
"module_number": "HLP-NIPC-MOD-0034",
|
||||||
"target_number": "HLP-NIPC-TGT-0034",
|
"target_number": "HLP-NIPC-TGT-0034",
|
||||||
"internal_name": "external_ai_gateway"
|
"internal_name": "external_ai_gateway"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module_number": "HLP-NIPC-MOD-0035",
|
||||||
|
"target_number": "HLP-NIPC-TGT-0035",
|
||||||
|
"internal_name": "persona_language_channel"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module_number": "HLP-NIPC-MOD-0036",
|
||||||
|
"target_number": "HLP-NIPC-TGT-0036",
|
||||||
|
"internal_name": "execution_limb_training"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"operations": [
|
"operations": [
|
||||||
|
|
@ -1330,204 +1323,6 @@
|
||||||
"effect": "STATE_CHANGE",
|
"effect": "STATE_CHANGE",
|
||||||
"payload_schema": "hololake.numbered-ipc.payload/update_channel_growth_sharing/v1"
|
"payload_schema": "hololake.numbered-ipc.payload/update_channel_growth_sharing/v1"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"operation_number": "HLP-NIPC-OP-0085",
|
|
||||||
"alias": "get_education_workspace_snapshot",
|
|
||||||
"handler": "education_workspace::get_education_workspace_snapshot",
|
|
||||||
"channel_number": "HLP-NIPC-CH-0002",
|
|
||||||
"module_number": "HLP-NIPC-MOD-0024",
|
|
||||||
"target_number": "HLP-NIPC-TGT-0024",
|
|
||||||
"admission": "VERIFIED_HUMAN_ROUTE",
|
|
||||||
"effect": "READ_OR_STATUS",
|
|
||||||
"payload_schema": "hololake.numbered-ipc.payload/get_education_workspace_snapshot/v1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"operation_number": "HLP-NIPC-OP-0086",
|
|
||||||
"alias": "create_education_document",
|
|
||||||
"handler": "education_workspace::create_education_document",
|
|
||||||
"channel_number": "HLP-NIPC-CH-0002",
|
|
||||||
"module_number": "HLP-NIPC-MOD-0024",
|
|
||||||
"target_number": "HLP-NIPC-TGT-0024",
|
|
||||||
"admission": "VERIFIED_HUMAN_ROUTE",
|
|
||||||
"effect": "STATE_CHANGE",
|
|
||||||
"payload_schema": "hololake.numbered-ipc.payload/create_education_document/v1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"operation_number": "HLP-NIPC-OP-0087",
|
|
||||||
"alias": "read_education_document",
|
|
||||||
"handler": "education_workspace::read_education_document",
|
|
||||||
"channel_number": "HLP-NIPC-CH-0002",
|
|
||||||
"module_number": "HLP-NIPC-MOD-0024",
|
|
||||||
"target_number": "HLP-NIPC-TGT-0024",
|
|
||||||
"admission": "VERIFIED_HUMAN_ROUTE",
|
|
||||||
"effect": "READ_OR_STATUS",
|
|
||||||
"payload_schema": "hololake.numbered-ipc.payload/read_education_document/v1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"operation_number": "HLP-NIPC-OP-0088",
|
|
||||||
"alias": "save_education_document",
|
|
||||||
"handler": "education_workspace::save_education_document",
|
|
||||||
"channel_number": "HLP-NIPC-CH-0002",
|
|
||||||
"module_number": "HLP-NIPC-MOD-0024",
|
|
||||||
"target_number": "HLP-NIPC-TGT-0024",
|
|
||||||
"admission": "VERIFIED_HUMAN_ROUTE",
|
|
||||||
"effect": "STATE_CHANGE",
|
|
||||||
"payload_schema": "hololake.numbered-ipc.payload/save_education_document/v1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"operation_number": "HLP-NIPC-OP-0089",
|
|
||||||
"alias": "archive_education_document",
|
|
||||||
"handler": "education_workspace::archive_education_document",
|
|
||||||
"channel_number": "HLP-NIPC-CH-0002",
|
|
||||||
"module_number": "HLP-NIPC-MOD-0024",
|
|
||||||
"target_number": "HLP-NIPC-TGT-0024",
|
|
||||||
"admission": "VERIFIED_HUMAN_ROUTE",
|
|
||||||
"effect": "STATE_CHANGE",
|
|
||||||
"payload_schema": "hololake.numbered-ipc.payload/archive_education_document/v1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"operation_number": "HLP-NIPC-OP-0090",
|
|
||||||
"alias": "create_education_table",
|
|
||||||
"handler": "education_workspace::create_education_table",
|
|
||||||
"channel_number": "HLP-NIPC-CH-0002",
|
|
||||||
"module_number": "HLP-NIPC-MOD-0024",
|
|
||||||
"target_number": "HLP-NIPC-TGT-0024",
|
|
||||||
"admission": "VERIFIED_HUMAN_ROUTE",
|
|
||||||
"effect": "STATE_CHANGE",
|
|
||||||
"payload_schema": "hololake.numbered-ipc.payload/create_education_table/v1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"operation_number": "HLP-NIPC-OP-0091",
|
|
||||||
"alias": "read_education_table",
|
|
||||||
"handler": "education_workspace::read_education_table",
|
|
||||||
"channel_number": "HLP-NIPC-CH-0002",
|
|
||||||
"module_number": "HLP-NIPC-MOD-0024",
|
|
||||||
"target_number": "HLP-NIPC-TGT-0024",
|
|
||||||
"admission": "VERIFIED_HUMAN_ROUTE",
|
|
||||||
"effect": "READ_OR_STATUS",
|
|
||||||
"payload_schema": "hololake.numbered-ipc.payload/read_education_table/v1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"operation_number": "HLP-NIPC-OP-0092",
|
|
||||||
"alias": "save_education_table",
|
|
||||||
"handler": "education_workspace::save_education_table",
|
|
||||||
"channel_number": "HLP-NIPC-CH-0002",
|
|
||||||
"module_number": "HLP-NIPC-MOD-0024",
|
|
||||||
"target_number": "HLP-NIPC-TGT-0024",
|
|
||||||
"admission": "VERIFIED_HUMAN_ROUTE",
|
|
||||||
"effect": "STATE_CHANGE",
|
|
||||||
"payload_schema": "hololake.numbered-ipc.payload/save_education_table/v1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"operation_number": "HLP-NIPC-OP-0093",
|
|
||||||
"alias": "archive_education_table",
|
|
||||||
"handler": "education_workspace::archive_education_table",
|
|
||||||
"channel_number": "HLP-NIPC-CH-0002",
|
|
||||||
"module_number": "HLP-NIPC-MOD-0024",
|
|
||||||
"target_number": "HLP-NIPC-TGT-0024",
|
|
||||||
"admission": "VERIFIED_HUMAN_ROUTE",
|
|
||||||
"effect": "STATE_CHANGE",
|
|
||||||
"payload_schema": "hololake.numbered-ipc.payload/archive_education_table/v1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"operation_number": "HLP-NIPC-OP-0094",
|
|
||||||
"alias": "assign_imported_table_to_education",
|
|
||||||
"handler": "education_workspace::assign_imported_table_to_education",
|
|
||||||
"channel_number": "HLP-NIPC-CH-0002",
|
|
||||||
"module_number": "HLP-NIPC-MOD-0024",
|
|
||||||
"target_number": "HLP-NIPC-TGT-0024",
|
|
||||||
"admission": "VERIFIED_HUMAN_ROUTE",
|
|
||||||
"effect": "STATE_CHANGE",
|
|
||||||
"payload_schema": "hololake.numbered-ipc.payload/assign_imported_table_to_education/v1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"operation_number": "HLP-NIPC-OP-0095",
|
|
||||||
"alias": "import_education_tables_from_dialog",
|
|
||||||
"handler": "education_translation::import_education_tables_from_dialog",
|
|
||||||
"channel_number": "HLP-NIPC-CH-0002",
|
|
||||||
"module_number": "HLP-NIPC-MOD-0024",
|
|
||||||
"target_number": "HLP-NIPC-TGT-0024",
|
|
||||||
"admission": "VERIFIED_HUMAN_ROUTE",
|
|
||||||
"effect": "STATE_CHANGE",
|
|
||||||
"payload_schema": "hololake.numbered-ipc.payload/import_education_tables_from_dialog/v1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"operation_number": "HLP-NIPC-OP-0096",
|
|
||||||
"alias": "export_education_table_to_dialog",
|
|
||||||
"handler": "education_translation::export_education_table_to_dialog",
|
|
||||||
"channel_number": "HLP-NIPC-CH-0002",
|
|
||||||
"module_number": "HLP-NIPC-MOD-0024",
|
|
||||||
"target_number": "HLP-NIPC-TGT-0024",
|
|
||||||
"admission": "VERIFIED_HUMAN_ROUTE",
|
|
||||||
"effect": "STATE_CHANGE",
|
|
||||||
"payload_schema": "hololake.numbered-ipc.payload/export_education_table_to_dialog/v1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"operation_number": "HLP-NIPC-OP-0097",
|
|
||||||
"alias": "get_education_recognition_capability",
|
|
||||||
"handler": "education_translation::get_education_recognition_capability",
|
|
||||||
"channel_number": "HLP-NIPC-CH-0002",
|
|
||||||
"module_number": "HLP-NIPC-MOD-0024",
|
|
||||||
"target_number": "HLP-NIPC-TGT-0024",
|
|
||||||
"admission": "VERIFIED_HUMAN_ROUTE",
|
|
||||||
"effect": "READ_OR_STATUS",
|
|
||||||
"payload_schema": "hololake.numbered-ipc.payload/get_education_recognition_capability/v1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"operation_number": "HLP-NIPC-OP-0098",
|
|
||||||
"alias": "create_education_automation_rule",
|
|
||||||
"handler": "education_workspace::create_education_automation_rule",
|
|
||||||
"channel_number": "HLP-NIPC-CH-0002",
|
|
||||||
"module_number": "HLP-NIPC-MOD-0024",
|
|
||||||
"target_number": "HLP-NIPC-TGT-0024",
|
|
||||||
"admission": "VERIFIED_HUMAN_ROUTE",
|
|
||||||
"effect": "STATE_CHANGE",
|
|
||||||
"payload_schema": "hololake.numbered-ipc.payload/create_education_automation_rule/v1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"operation_number": "HLP-NIPC-OP-0099",
|
|
||||||
"alias": "save_education_automation_rule",
|
|
||||||
"handler": "education_workspace::save_education_automation_rule",
|
|
||||||
"channel_number": "HLP-NIPC-CH-0002",
|
|
||||||
"module_number": "HLP-NIPC-MOD-0024",
|
|
||||||
"target_number": "HLP-NIPC-TGT-0024",
|
|
||||||
"admission": "VERIFIED_HUMAN_ROUTE",
|
|
||||||
"effect": "STATE_CHANGE",
|
|
||||||
"payload_schema": "hololake.numbered-ipc.payload/save_education_automation_rule/v1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"operation_number": "HLP-NIPC-OP-0100",
|
|
||||||
"alias": "archive_education_automation_rule",
|
|
||||||
"handler": "education_workspace::archive_education_automation_rule",
|
|
||||||
"channel_number": "HLP-NIPC-CH-0002",
|
|
||||||
"module_number": "HLP-NIPC-MOD-0024",
|
|
||||||
"target_number": "HLP-NIPC-TGT-0024",
|
|
||||||
"admission": "VERIFIED_HUMAN_ROUTE",
|
|
||||||
"effect": "STATE_CHANGE",
|
|
||||||
"payload_schema": "hololake.numbered-ipc.payload/archive_education_automation_rule/v1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"operation_number": "HLP-NIPC-OP-0101",
|
|
||||||
"alias": "preview_education_automation_rule",
|
|
||||||
"handler": "education_workspace::preview_education_automation_rule",
|
|
||||||
"channel_number": "HLP-NIPC-CH-0002",
|
|
||||||
"module_number": "HLP-NIPC-MOD-0024",
|
|
||||||
"target_number": "HLP-NIPC-TGT-0024",
|
|
||||||
"admission": "VERIFIED_HUMAN_ROUTE",
|
|
||||||
"effect": "READ_OR_STATUS",
|
|
||||||
"payload_schema": "hololake.numbered-ipc.payload/preview_education_automation_rule/v1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"operation_number": "HLP-NIPC-OP-0102",
|
|
||||||
"alias": "execute_education_automation_rule",
|
|
||||||
"handler": "education_workspace::execute_education_automation_rule",
|
|
||||||
"channel_number": "HLP-NIPC-CH-0002",
|
|
||||||
"module_number": "HLP-NIPC-MOD-0024",
|
|
||||||
"target_number": "HLP-NIPC-TGT-0024",
|
|
||||||
"admission": "VERIFIED_HUMAN_ROUTE",
|
|
||||||
"effect": "STATE_CHANGE",
|
|
||||||
"payload_schema": "hololake.numbered-ipc.payload/execute_education_automation_rule/v1"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"operation_number": "HLP-NIPC-OP-0103",
|
"operation_number": "HLP-NIPC-OP-0103",
|
||||||
"alias": "get_web_novel_workspace_snapshot",
|
"alias": "get_web_novel_workspace_snapshot",
|
||||||
|
|
@ -2132,6 +1927,72 @@
|
||||||
"admission": "VERIFIED_HUMAN_ROUTE",
|
"admission": "VERIFIED_HUMAN_ROUTE",
|
||||||
"effect": "STATE_CHANGE",
|
"effect": "STATE_CHANGE",
|
||||||
"payload_schema": "hololake.numbered-ipc.payload/set_external_ai_gateway_exposure/v1"
|
"payload_schema": "hololake.numbered-ipc.payload/set_external_ai_gateway_exposure/v1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"operation_number": "HLP-NIPC-OP-0158",
|
||||||
|
"alias": "discover_model_providers",
|
||||||
|
"handler": "persona_language_channel::discover_model_providers",
|
||||||
|
"channel_number": "HLP-NIPC-CH-0002",
|
||||||
|
"module_number": "HLP-NIPC-MOD-0035",
|
||||||
|
"target_number": "HLP-NIPC-TGT-0035",
|
||||||
|
"admission": "VERIFIED_HUMAN_ROUTE",
|
||||||
|
"effect": "READ_OR_STATUS",
|
||||||
|
"payload_schema": "hololake.numbered-ipc.payload/discover_model_providers/v1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"operation_number": "HLP-NIPC-OP-0159",
|
||||||
|
"alias": "upsert_model_provider",
|
||||||
|
"handler": "persona_language_channel::upsert_model_provider",
|
||||||
|
"channel_number": "HLP-NIPC-CH-0002",
|
||||||
|
"module_number": "HLP-NIPC-MOD-0035",
|
||||||
|
"target_number": "HLP-NIPC-TGT-0035",
|
||||||
|
"admission": "VERIFIED_HUMAN_ROUTE",
|
||||||
|
"effect": "STATE_CHANGE",
|
||||||
|
"payload_schema": "hololake.numbered-ipc.payload/upsert_model_provider/v1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"operation_number": "HLP-NIPC-OP-0160",
|
||||||
|
"alias": "get_conversation",
|
||||||
|
"handler": "persona_language_channel::get_conversation",
|
||||||
|
"channel_number": "HLP-NIPC-CH-0002",
|
||||||
|
"module_number": "HLP-NIPC-MOD-0035",
|
||||||
|
"target_number": "HLP-NIPC-TGT-0035",
|
||||||
|
"admission": "VERIFIED_HUMAN_ROUTE",
|
||||||
|
"effect": "READ_OR_STATUS",
|
||||||
|
"payload_schema": "hololake.numbered-ipc.payload/get_conversation/v1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"operation_number": "HLP-NIPC-OP-0161",
|
||||||
|
"alias": "send_message",
|
||||||
|
"handler": "persona_language_channel::send_message",
|
||||||
|
"channel_number": "HLP-NIPC-CH-0002",
|
||||||
|
"module_number": "HLP-NIPC-MOD-0035",
|
||||||
|
"target_number": "HLP-NIPC-TGT-0035",
|
||||||
|
"admission": "VERIFIED_HUMAN_ROUTE",
|
||||||
|
"effect": "STATE_CHANGE",
|
||||||
|
"payload_schema": "hololake.numbered-ipc.payload/send_message/v1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"operation_number": "HLP-NIPC-OP-0162",
|
||||||
|
"alias": "get_execution_limb_snapshot",
|
||||||
|
"handler": "execution_limb_training::get_execution_limb_snapshot",
|
||||||
|
"channel_number": "HLP-NIPC-CH-0002",
|
||||||
|
"module_number": "HLP-NIPC-MOD-0036",
|
||||||
|
"target_number": "HLP-NIPC-TGT-0036",
|
||||||
|
"admission": "VERIFIED_HUMAN_ROUTE",
|
||||||
|
"effect": "READ_OR_STATUS",
|
||||||
|
"payload_schema": "hololake.numbered-ipc.payload/get_execution_limb_snapshot/v1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"operation_number": "HLP-NIPC-OP-0163",
|
||||||
|
"alias": "run_execution_limb_training_drill",
|
||||||
|
"handler": "execution_limb_training::run_execution_limb_training_drill",
|
||||||
|
"channel_number": "HLP-NIPC-CH-0002",
|
||||||
|
"module_number": "HLP-NIPC-MOD-0036",
|
||||||
|
"target_number": "HLP-NIPC-TGT-0036",
|
||||||
|
"admission": "VERIFIED_HUMAN_ROUTE",
|
||||||
|
"effect": "STATE_CHANGE",
|
||||||
|
"payload_schema": "hololake.numbered-ipc.payload/run_execution_limb_training_drill/v1"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
{
|
|
||||||
"schema": "hololake.module-package/v1",
|
|
||||||
"manifest": {
|
|
||||||
"moduleNumber": "HLP-MOD-OFFICIAL-EDUCATION-WORKBENCH-0001",
|
|
||||||
"registrationClass": "OFFICIAL_LIGHTHOUSE",
|
|
||||||
"displayName": "教育行业工作台",
|
|
||||||
"version": "0.1.0",
|
|
||||||
"minimumHostVersion": "0.5.0",
|
|
||||||
"adapter": "education-workbench-v1",
|
|
||||||
"contentDigest": "fd80c9957865a58cf62679528f110ae355b1385ad1dd988e31217ea3da66e65b",
|
|
||||||
"permissions": ["EDUCATION_WORKSPACE_READ", "EDUCATION_DOCUMENT_WRITE", "EDUCATION_TABLE_WRITE", "EDUCATION_IMPORT_READ_FILE", "EDUCATION_EXPORT_WRITE_FILE", "EDUCATION_IMPORT_ASSIGNMENT_WRITE", "EDUCATION_AUTOMATION_WRITE", "EDUCATION_AUTOMATION_EXECUTE"],
|
|
||||||
"userDataSchema": "hololake.module-data/education-workbench/v1",
|
|
||||||
"selfTest": {
|
|
||||||
"kind": "DECLARATIVE_SCHEMA_V1",
|
|
||||||
"expectedContentDigest": "fd80c9957865a58cf62679528f110ae355b1385ad1dd988e31217ea3da66e65b"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"payload": {
|
|
||||||
"entry": "education-workspace",
|
|
||||||
"adapterConfig": {
|
|
||||||
"dataBoundary": "CURRENT_AUTHENTICATED_ACCOUNT_LOCAL",
|
|
||||||
"foundationModule": "HLP-MOD-LOCAL-CHANNEL-WORKBENCH-0001",
|
|
||||||
"documentEngine": "LEXICAL_0_49",
|
|
||||||
"spreadsheetEngine": "FORTUNE_SHEET_1_0_4",
|
|
||||||
"importDefaultScope": "UNASSIGNED",
|
|
||||||
"automationExecution": "PREVIEW_TOKEN_AND_HUMAN_CONFIRMATION",
|
|
||||||
"modelFileTransferDefault": "DENY",
|
|
||||||
"maximumColumns": 30,
|
|
||||||
"maximumRows": 1000,
|
|
||||||
"persistent": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
dW50cnVzdGVkIGNvbW1lbnQ6IHNpZ25hdHVyZSBmcm9tIHRhdXJpIHNlY3JldCBrZXkKUlVReHJHQ2hTVDYvRTFEa084d1l6MnB2OFVvVy9abW90S3RFMEVOdmIvWW1hbkllWmw4NHBJaDVNTGxpZHV4M0tVNUNUOExmdHAwSGw4V0NPQ1RPUjlRU0h0Tm50clFlekFJPQp0cnVzdGVkIGNvbW1lbnQ6IHRpbWVzdGFtcDoxNzg3MDc5ODQzCWZpbGU6SExQLU1PRC1PRkZJQ0lBTC1FRFVDQVRJT04tV09SS0JFTkNILTAwMDEtMC4xLjAuZ2htb2QKVXZzVklwQk5KR3Q3bkRZaXBYV1ArOGx6b3BKSWVyUkpvK1dyTXdpWnBhMXNqVnJKcU5vZ1BTdzlFWjlXRTJ1RlFhMXAwMjhXcFBEQnQxRzV4SFVuQkE9PQo=
|
|
||||||
|
|
@ -142,8 +142,8 @@
|
||||||
"pncc_persona_lease_acquisition_implemented": false,
|
"pncc_persona_lease_acquisition_implemented": false,
|
||||||
"pncc_reality_execution_implemented": false,
|
"pncc_reality_execution_implemented": false,
|
||||||
"stage_one_product_contract_state": "PERSONAL_KNOWLEDGE_AND_CODE_SOURCE_SLICES_IMPLEMENTED_FULL_STAGE_ONE_PRODUCT_INCOMPLETE",
|
"stage_one_product_contract_state": "PERSONAL_KNOWLEDGE_AND_CODE_SOURCE_SLICES_IMPLEMENTED_FULL_STAGE_ONE_PRODUCT_INCOMPLETE",
|
||||||
"stage_one_internal_ai_interaction": false,
|
"stage_one_internal_ai_interaction": true,
|
||||||
"stage_one_model_api_configuration": false,
|
"stage_one_model_api_configuration": true,
|
||||||
"vendor_adapter_matrix_required": false,
|
"vendor_adapter_matrix_required": false,
|
||||||
"first_public_product": "GH-AIOS_GENERAL_AI_OPERATING_PLATFORM",
|
"first_public_product": "GH-AIOS_GENERAL_AI_OPERATING_PLATFORM",
|
||||||
"visible_five_domain_navigation_in_stage_one": true,
|
"visible_five_domain_navigation_in_stage_one": true,
|
||||||
|
|
@ -160,5 +160,28 @@
|
||||||
"path": "../guanghu-knowledge-base",
|
"path": "../guanghu-knowledge-base",
|
||||||
"role": "READ_ONLY_KNOWLEDGE_UX_BEHAVIOR_AND_DATA_DONOR"
|
"role": "READ_ONLY_KNOWLEDGE_UX_BEHAVIOR_AND_DATA_DONOR"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
}
|
"persona_channel_body_contract": "contracts/persona-channel-body.json",
|
||||||
|
"persona_channel_body_is_persona_body_not_simple_feature": true,
|
||||||
|
"persona_channel_multiple_personas_supported": true,
|
||||||
|
"persona_channel_thirty_day_reversible_trial_runtime_implemented": true,
|
||||||
|
"persona_channel_language_contract_gate_implemented": true,
|
||||||
|
"persona_channel_unsigned_expiry_state": "CONTRACT_REQUIRED_CHANNEL_STOPPED_NO_SILENT_IRREVERSIBLE_ACTIVATION",
|
||||||
|
"persona_channel_early_contract_immediate_activation_supported": true,
|
||||||
|
"persona_channel_immutable_millisecond_language_hash_chain_implemented": true,
|
||||||
|
"persona_channel_active_persona_update_or_delete_allowed": false,
|
||||||
|
"persona_channel_official_read_access": false,
|
||||||
|
"channel_growth_projection_contract": "contracts/channel-growth-model.json",
|
||||||
|
"channel_growth_is_rebuildable_projection_not_persona_body": true,
|
||||||
|
"persona_language_channel_source_integrated": true,
|
||||||
|
"persona_language_channel_chat_is_primary_surface": true,
|
||||||
|
"persona_language_channel_model_configuration_is_secondary": true,
|
||||||
|
"persona_language_channel_api_secret_storage": "OS_KEYCHAIN_ONLY",
|
||||||
|
"persona_language_channel_exact_knowledge_context_and_receipt_implemented": true,
|
||||||
|
"persona_language_channel_installed_runtime_acceptance": true,
|
||||||
|
"persona_language_channel_acceptance_receipt": "audit/persona-language-channel-runtime-acceptance-20260820.json",
|
||||||
|
"subdomain_industry_routing_contract": "contracts/subdomain-industry-routing.json",
|
||||||
|
"subdomain_industry_status_source_integrated": true,
|
||||||
|
"industry_entry_domain": "BRANCH_DOMAIN",
|
||||||
|
"zero_domain_industry_entry": false
|
||||||
|
}
|
||||||
|
|
@ -54,7 +54,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"recordId": "HLP-NUMBERED-IPC-ROOT-001",
|
"recordId": "HLP-NUMBERED-IPC-ROOT-001",
|
||||||
"sha256": "11be5365a7a6d7d131af2c023b0401a1aa147f702ee125a4ddaf9f3171d1c4c2"
|
"sha256": "facab9ecdcfce0e8e676585275c0cd9bbf502c2496ca96a9600e019896e51ef0"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"recordId": "HLP-NBROKER-ROOT-001",
|
"recordId": "HLP-NBROKER-ROOT-001",
|
||||||
|
|
@ -76,8 +76,8 @@
|
||||||
"unresolvedNumberReferenceCount": 0,
|
"unresolvedNumberReferenceCount": 0,
|
||||||
"mismatchedCoordinate": "FAIL_CLOSED"
|
"mismatchedCoordinate": "FAIL_CLOSED"
|
||||||
},
|
},
|
||||||
"coordinateCount": 285,
|
"coordinateCount": 273,
|
||||||
"routeCount": 182,
|
"routeCount": 170,
|
||||||
"identityNodeCount": 4,
|
"identityNodeCount": 4,
|
||||||
"protocolNodeCount": 99,
|
"protocolNodeCount": 99,
|
||||||
"referenceOnlyNodeCount": 16,
|
"referenceOnlyNodeCount": 16,
|
||||||
|
|
@ -2254,258 +2254,6 @@
|
||||||
"evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT",
|
"evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT",
|
||||||
"path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0023/HLP-NIPC-OP-0084/HLP-NIPC-TGT-0023"
|
"path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0023/HLP-NIPC-OP-0084/HLP-NIPC-TGT-0023"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"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-0024",
|
|
||||||
"operationNumber": "HLP-NIPC-OP-0085",
|
|
||||||
"targetNumber": "HLP-NIPC-TGT-0024",
|
|
||||||
"alias": "get_education_workspace_snapshot",
|
|
||||||
"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-0024/HLP-NIPC-OP-0085/HLP-NIPC-TGT-0024"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"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-0024",
|
|
||||||
"operationNumber": "HLP-NIPC-OP-0086",
|
|
||||||
"targetNumber": "HLP-NIPC-TGT-0024",
|
|
||||||
"alias": "create_education_document",
|
|
||||||
"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-0024/HLP-NIPC-OP-0086/HLP-NIPC-TGT-0024"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"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-0024",
|
|
||||||
"operationNumber": "HLP-NIPC-OP-0087",
|
|
||||||
"targetNumber": "HLP-NIPC-TGT-0024",
|
|
||||||
"alias": "read_education_document",
|
|
||||||
"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-0024/HLP-NIPC-OP-0087/HLP-NIPC-TGT-0024"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"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-0024",
|
|
||||||
"operationNumber": "HLP-NIPC-OP-0088",
|
|
||||||
"targetNumber": "HLP-NIPC-TGT-0024",
|
|
||||||
"alias": "save_education_document",
|
|
||||||
"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-0024/HLP-NIPC-OP-0088/HLP-NIPC-TGT-0024"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"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-0024",
|
|
||||||
"operationNumber": "HLP-NIPC-OP-0089",
|
|
||||||
"targetNumber": "HLP-NIPC-TGT-0024",
|
|
||||||
"alias": "archive_education_document",
|
|
||||||
"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-0024/HLP-NIPC-OP-0089/HLP-NIPC-TGT-0024"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"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-0024",
|
|
||||||
"operationNumber": "HLP-NIPC-OP-0090",
|
|
||||||
"targetNumber": "HLP-NIPC-TGT-0024",
|
|
||||||
"alias": "create_education_table",
|
|
||||||
"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-0024/HLP-NIPC-OP-0090/HLP-NIPC-TGT-0024"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"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-0024",
|
|
||||||
"operationNumber": "HLP-NIPC-OP-0091",
|
|
||||||
"targetNumber": "HLP-NIPC-TGT-0024",
|
|
||||||
"alias": "read_education_table",
|
|
||||||
"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-0024/HLP-NIPC-OP-0091/HLP-NIPC-TGT-0024"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"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-0024",
|
|
||||||
"operationNumber": "HLP-NIPC-OP-0092",
|
|
||||||
"targetNumber": "HLP-NIPC-TGT-0024",
|
|
||||||
"alias": "save_education_table",
|
|
||||||
"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-0024/HLP-NIPC-OP-0092/HLP-NIPC-TGT-0024"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"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-0024",
|
|
||||||
"operationNumber": "HLP-NIPC-OP-0093",
|
|
||||||
"targetNumber": "HLP-NIPC-TGT-0024",
|
|
||||||
"alias": "archive_education_table",
|
|
||||||
"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-0024/HLP-NIPC-OP-0093/HLP-NIPC-TGT-0024"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"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-0024",
|
|
||||||
"operationNumber": "HLP-NIPC-OP-0094",
|
|
||||||
"targetNumber": "HLP-NIPC-TGT-0024",
|
|
||||||
"alias": "assign_imported_table_to_education",
|
|
||||||
"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-0024/HLP-NIPC-OP-0094/HLP-NIPC-TGT-0024"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"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-0024",
|
|
||||||
"operationNumber": "HLP-NIPC-OP-0095",
|
|
||||||
"targetNumber": "HLP-NIPC-TGT-0024",
|
|
||||||
"alias": "import_education_tables_from_dialog",
|
|
||||||
"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-0024/HLP-NIPC-OP-0095/HLP-NIPC-TGT-0024"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"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-0024",
|
|
||||||
"operationNumber": "HLP-NIPC-OP-0096",
|
|
||||||
"targetNumber": "HLP-NIPC-TGT-0024",
|
|
||||||
"alias": "export_education_table_to_dialog",
|
|
||||||
"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-0024/HLP-NIPC-OP-0096/HLP-NIPC-TGT-0024"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"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-0024",
|
|
||||||
"operationNumber": "HLP-NIPC-OP-0097",
|
|
||||||
"targetNumber": "HLP-NIPC-TGT-0024",
|
|
||||||
"alias": "get_education_recognition_capability",
|
|
||||||
"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-0024/HLP-NIPC-OP-0097/HLP-NIPC-TGT-0024"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"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-0024",
|
|
||||||
"operationNumber": "HLP-NIPC-OP-0098",
|
|
||||||
"targetNumber": "HLP-NIPC-TGT-0024",
|
|
||||||
"alias": "create_education_automation_rule",
|
|
||||||
"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-0024/HLP-NIPC-OP-0098/HLP-NIPC-TGT-0024"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"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-0024",
|
|
||||||
"operationNumber": "HLP-NIPC-OP-0099",
|
|
||||||
"targetNumber": "HLP-NIPC-TGT-0024",
|
|
||||||
"alias": "save_education_automation_rule",
|
|
||||||
"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-0024/HLP-NIPC-OP-0099/HLP-NIPC-TGT-0024"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"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-0024",
|
|
||||||
"operationNumber": "HLP-NIPC-OP-0100",
|
|
||||||
"targetNumber": "HLP-NIPC-TGT-0024",
|
|
||||||
"alias": "archive_education_automation_rule",
|
|
||||||
"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-0024/HLP-NIPC-OP-0100/HLP-NIPC-TGT-0024"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"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-0024",
|
|
||||||
"operationNumber": "HLP-NIPC-OP-0101",
|
|
||||||
"targetNumber": "HLP-NIPC-TGT-0024",
|
|
||||||
"alias": "preview_education_automation_rule",
|
|
||||||
"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-0024/HLP-NIPC-OP-0101/HLP-NIPC-TGT-0024"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"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-0024",
|
|
||||||
"operationNumber": "HLP-NIPC-OP-0102",
|
|
||||||
"targetNumber": "HLP-NIPC-TGT-0024",
|
|
||||||
"alias": "execute_education_automation_rule",
|
|
||||||
"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-0024/HLP-NIPC-OP-0102/HLP-NIPC-TGT-0024"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"transport": "TAURI_WEBVIEW_NUMBERED_IPC",
|
"transport": "TAURI_WEBVIEW_NUMBERED_IPC",
|
||||||
"protocolVersion": "HLP-NIPC-v1",
|
"protocolVersion": "HLP-NIPC-v1",
|
||||||
|
|
@ -3276,6 +3024,90 @@
|
||||||
"evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT",
|
"evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT",
|
||||||
"path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0034/HLP-NIPC-OP-0157/HLP-NIPC-TGT-0034"
|
"path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0034/HLP-NIPC-OP-0157/HLP-NIPC-TGT-0034"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"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-0035",
|
||||||
|
"operationNumber": "HLP-NIPC-OP-0158",
|
||||||
|
"targetNumber": "HLP-NIPC-TGT-0035",
|
||||||
|
"alias": "discover_model_providers",
|
||||||
|
"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-0035/HLP-NIPC-OP-0158/HLP-NIPC-TGT-0035"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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-0035",
|
||||||
|
"operationNumber": "HLP-NIPC-OP-0159",
|
||||||
|
"targetNumber": "HLP-NIPC-TGT-0035",
|
||||||
|
"alias": "upsert_model_provider",
|
||||||
|
"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-0035/HLP-NIPC-OP-0159/HLP-NIPC-TGT-0035"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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-0035",
|
||||||
|
"operationNumber": "HLP-NIPC-OP-0160",
|
||||||
|
"targetNumber": "HLP-NIPC-TGT-0035",
|
||||||
|
"alias": "get_conversation",
|
||||||
|
"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-0035/HLP-NIPC-OP-0160/HLP-NIPC-TGT-0035"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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-0035",
|
||||||
|
"operationNumber": "HLP-NIPC-OP-0161",
|
||||||
|
"targetNumber": "HLP-NIPC-TGT-0035",
|
||||||
|
"alias": "send_message",
|
||||||
|
"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-0035/HLP-NIPC-OP-0161/HLP-NIPC-TGT-0035"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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-0036",
|
||||||
|
"operationNumber": "HLP-NIPC-OP-0162",
|
||||||
|
"targetNumber": "HLP-NIPC-TGT-0036",
|
||||||
|
"alias": "get_execution_limb_snapshot",
|
||||||
|
"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-0036/HLP-NIPC-OP-0162/HLP-NIPC-TGT-0036"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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-0036",
|
||||||
|
"operationNumber": "HLP-NIPC-OP-0163",
|
||||||
|
"targetNumber": "HLP-NIPC-TGT-0036",
|
||||||
|
"alias": "run_execution_limb_training_drill",
|
||||||
|
"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-0036/HLP-NIPC-OP-0163/HLP-NIPC-TGT-0036"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"transport": "TAURI_WEBVIEW_NUMBERED_IPC",
|
"transport": "TAURI_WEBVIEW_NUMBERED_IPC",
|
||||||
"protocolVersion": "HLP-NIPC-v1",
|
"protocolVersion": "HLP-NIPC-v1",
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
"@lexical/rich-text": "0.49.0",
|
"@lexical/rich-text": "0.49.0",
|
||||||
"@lexical/selection": "0.49.0",
|
"@lexical/selection": "0.49.0",
|
||||||
"@lexical/utils": "0.49.0",
|
"@lexical/utils": "0.49.0",
|
||||||
|
"@tanstack/react-table": "8.21.3",
|
||||||
"@tauri-apps/api": "2.10.1",
|
"@tauri-apps/api": "2.10.1",
|
||||||
"@tauri-apps/plugin-process": "2.3.1",
|
"@tauri-apps/plugin-process": "2.3.1",
|
||||||
"@tauri-apps/plugin-updater": "2.10.0",
|
"@tauri-apps/plugin-updater": "2.10.0",
|
||||||
|
|
@ -1792,6 +1793,39 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@tanstack/react-table": {
|
||||||
|
"version": "8.21.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.21.3.tgz",
|
||||||
|
"integrity": "sha512-5nNMTSETP4ykGegmVkhjcS8tTLW6Vl4axfEGQN3v0zdHYbK4UfoqfPChclTrJ4EoK9QynqAu9oUf8VEmrpZ5Ww==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@tanstack/table-core": "8.21.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/tannerlinsley"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16.8",
|
||||||
|
"react-dom": ">=16.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tanstack/table-core": {
|
||||||
|
"version": "8.21.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.21.3.tgz",
|
||||||
|
"integrity": "sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/tannerlinsley"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@tauri-apps/api": {
|
"node_modules/@tauri-apps/api": {
|
||||||
"version": "2.10.1",
|
"version": "2.10.1",
|
||||||
"resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.10.1.tgz",
|
"resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.10.1.tgz",
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "hololake-native-desktop",
|
"name": "hololake-native-desktop",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.5.0",
|
"version": "0.9.1",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
import assert from 'node:assert/strict'
|
|
||||||
import fs from 'node:fs'
|
|
||||||
import test from 'node:test'
|
|
||||||
|
|
||||||
const frontend = fs.readFileSync(new URL('../src/main.tsx', import.meta.url), 'utf8')
|
|
||||||
const architecture = JSON.parse(
|
|
||||||
fs.readFileSync(new URL('../../../routing/hololake-current-architecture.json', import.meta.url), 'utf8'),
|
|
||||||
)
|
|
||||||
|
|
||||||
test('returning to the main world preserves the authenticated session', () => {
|
|
||||||
const handler = frontend.match(/const returnToMainWorld = \(\) => \{([\s\S]*?)\n \}/)?.[1] || ''
|
|
||||||
|
|
||||||
assert.match(handler, /setShowMainWorld\(true\)/)
|
|
||||||
assert.doesNotMatch(handler, /signOutRepo|setRepoLogin\(null\)|sign_out_code_repo_login/)
|
|
||||||
assert.match(frontend, /if \(!repoLogin \|\| showMainWorld\)/)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('main-world return and account sign-out remain separate visible actions', () => {
|
|
||||||
assert.match(frontend, />返回主世界</)
|
|
||||||
assert.match(frontend, />进入所属域</)
|
|
||||||
assert.match(frontend, />退出账号</)
|
|
||||||
assert.equal(
|
|
||||||
architecture.current_native_edge.authenticated_main_world_navigation,
|
|
||||||
'SOURCE_INTEGRATED_SESSION_PRESERVING_REENTRY',
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
@ -1,53 +0,0 @@
|
||||||
import assert from 'node:assert/strict'
|
|
||||||
import { readFileSync } from 'node:fs'
|
|
||||||
import test from 'node:test'
|
|
||||||
|
|
||||||
const read = (path) => readFileSync(new URL(`../${path}`, import.meta.url), 'utf8')
|
|
||||||
const contract = JSON.parse(read('contracts/education-workspace.json'))
|
|
||||||
const registry = JSON.parse(read('contracts/numbered-ipc-registry.json'))
|
|
||||||
const modulePackage = JSON.parse(read('fixtures/module-packages/HLP-MOD-OFFICIAL-EDUCATION-WORKBENCH-0001-0.1.0.ghmod'))
|
|
||||||
const workspaceRust = read('src-tauri/src/education_workspace.rs')
|
|
||||||
const translationRust = read('src-tauri/src/education_translation.rs')
|
|
||||||
const frontend = [
|
|
||||||
read('src/modules/education-workspace/index.tsx'),
|
|
||||||
read('src/modules/education-workspace/education-data.ts'),
|
|
||||||
read('src/modules/education-workspace/education-adaptive-rendering.ts'),
|
|
||||||
].join('\n')
|
|
||||||
|
|
||||||
test('education is an official signed declarative adapter over the shared office foundation', () => {
|
|
||||||
assert.equal(contract.package.official_module_number, 'HLP-MOD-OFFICIAL-EDUCATION-WORKBENCH-0001')
|
|
||||||
assert.equal(contract.foundation_dependency.module_id, 'hololake.builtin.channel-workbench')
|
|
||||||
assert.equal(modulePackage.manifest.registrationClass, 'OFFICIAL_LIGHTHOUSE')
|
|
||||||
assert.equal(modulePackage.manifest.adapter, 'education-workbench-v1')
|
|
||||||
assert.equal(modulePackage.payload.adapterConfig.foundationModule, 'HLP-MOD-LOCAL-CHANNEL-WORKBENCH-0001')
|
|
||||||
assert.equal(modulePackage.payload.adapterConfig.importDefaultScope, 'UNASSIGNED')
|
|
||||||
assert.equal(modulePackage.payload.adapterConfig.modelFileTransferDefault, 'DENY')
|
|
||||||
assert.equal(modulePackage.manifest.permissions.length, 8)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('all education effects cross the exact numbered adapter routes', () => {
|
|
||||||
const routes = registry.operations.filter((route) => route.module_number === 'HLP-NIPC-MOD-0024')
|
|
||||||
assert.deepEqual(routes.map((route) => route.operation_number), Array.from({ length: 18 }, (_, index) => `HLP-NIPC-OP-${String(index + 85).padStart(4, '0')}`))
|
|
||||||
assert.ok(routes.every((route) => route.target_number === 'HLP-NIPC-TGT-0024'))
|
|
||||||
assert.ok(routes.every((route) => route.admission === 'VERIFIED_HUMAN_ROUTE'))
|
|
||||||
assert.doesNotMatch(frontend, /from ['"]@tauri-apps\/api\/core['"]/)
|
|
||||||
assert.doesNotMatch(workspaceRust, /#\[tauri::command\]/)
|
|
||||||
assert.doesNotMatch(translationRust, /#\[tauri::command\]/)
|
|
||||||
assert.match(workspaceRust, /require_active_module_adapter/)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('legacy silent corruption and automatic destructive behavior are rejected', () => {
|
|
||||||
assert.doesNotMatch(workspaceRust, /serde_json::from_str[^\n]+unwrap_or_default/)
|
|
||||||
assert.match(workspaceRust, /deny_unknown_fields/)
|
|
||||||
assert.match(frontend, /生成只读预览/)
|
|
||||||
assert.match(frontend, /我确认,写入清理结果/)
|
|
||||||
assert.match(frontend, /生成只读预览令牌/)
|
|
||||||
assert.match(frontend, /我确认,按此令牌执行/)
|
|
||||||
assert.match(frontend, /确认归入教育频道/)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('education UI is independently lazy-loaded and reuses, rather than duplicates, office engines', () => {
|
|
||||||
assert.match(read('src/main.tsx'), /lazy\(\(\) => import\('\.\/modules\/education-workspace'\)/)
|
|
||||||
assert.match(read('src/modules/education-workspace/education-document-engine.tsx'), /channel-workbench\/document-engine/)
|
|
||||||
assert.match(read('src/modules/education-workspace/index.tsx'), /channel-workbench\/spreadsheet-engine/)
|
|
||||||
})
|
|
||||||
|
|
@ -1,309 +0,0 @@
|
||||||
import assert from 'node:assert/strict'
|
|
||||||
import fs from 'node:fs'
|
|
||||||
import test from 'node:test'
|
|
||||||
|
|
||||||
const read = (path) => fs.readFileSync(new URL('../' + path, import.meta.url), 'utf8')
|
|
||||||
const contract = JSON.parse(read('contracts/education-workspace.json'))
|
|
||||||
const donors = JSON.parse(read('contracts/education-open-source-donor-assessment.json'))
|
|
||||||
const industry = JSON.parse(read('contracts/subdomain-industry-routing.json'))
|
|
||||||
const rust = read('src-tauri/src/education_workspace.rs')
|
|
||||||
const translation = read('src-tauri/src/education_translation.rs')
|
|
||||||
const lib = read('src-tauri/src/lib.rs')
|
|
||||||
const ui = read('src/main.tsx')
|
|
||||||
const documentEngine = read('src/modules/education-document-engine.tsx')
|
|
||||||
const channelDocumentEngine = read('src/modules/channel-workbench/document-engine.tsx')
|
|
||||||
const channelSpreadsheetEngine = read('src/modules/channel-workbench/spreadsheet-engine.tsx')
|
|
||||||
const channelSpreadsheetModel = read('src/modules/channel-workbench/spreadsheet-model.ts')
|
|
||||||
const moduleRuntime = JSON.parse(read('contracts/channel-module-runtime.json'))
|
|
||||||
const marketplaceDevelopmentCatalog = JSON.parse(read('contracts/official-module-marketplace-development-catalog.json'))
|
|
||||||
const adaptiveRendering = read('src/modules/education-adaptive-rendering.ts')
|
|
||||||
const dataCleanup = read('src/modules/education-data.ts')
|
|
||||||
|
|
||||||
test('education package consumes the built-in account-scoped channel workbench', () => {
|
|
||||||
assert.equal(contract.domain_entry, 'BRANCH_DOMAIN')
|
|
||||||
assert.equal(contract.channel_id, 'GH-EDU-INIT-001')
|
|
||||||
assert.equal(contract.native_storage.owner, 'HOLOLAKE_NATIVE_RUST_CORE')
|
|
||||||
assert.equal(contract.native_storage.engine, 'SQLITE')
|
|
||||||
assert.equal(contract.native_storage.authenticated_account_required, true)
|
|
||||||
assert.equal(contract.native_storage.cross_account_projection_allowed, false)
|
|
||||||
assert.equal(contract.native_storage.restart_readback_required, true)
|
|
||||||
assert.equal(contract.document_module.state, 'DEVELOPMENT_FEATURE_MOUNTED')
|
|
||||||
assert.equal(contract.table_module.state, 'DEVELOPMENT_FEATURE_MOUNTED')
|
|
||||||
assert.equal(contract.foundation_dependency.module_id, 'hololake.builtin.channel-workbench')
|
|
||||||
assert.match(contract.document_module.engine, /^CHANNEL_WORKBENCH_LEXICAL_0_49_0/)
|
|
||||||
assert.match(contract.table_module.engine, /^CHANNEL_WORKBENCH_FORTUNE_SHEET_1_0_4/)
|
|
||||||
assert.equal(contract.table_module.sensitive_field_visibility.local_default, 'VISIBLE')
|
|
||||||
assert.equal(contract.table_module.sensitive_field_visibility.human_can_hide, true)
|
|
||||||
assert.equal(contract.table_module.sensitive_field_visibility.human_can_show_again, true)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('education workspace commands require the authenticated storage root', () => {
|
|
||||||
assert.match(rust, /account_storage_root\(app, "education-workspace-v1"\)/)
|
|
||||||
for (const command of [
|
|
||||||
'get_education_workspace_snapshot',
|
|
||||||
'create_education_document',
|
|
||||||
'read_education_document',
|
|
||||||
'save_education_document',
|
|
||||||
'archive_education_document',
|
|
||||||
'create_education_table',
|
|
||||||
'read_education_table',
|
|
||||||
'save_education_table',
|
|
||||||
'archive_education_table',
|
|
||||||
'create_education_automation_rule',
|
|
||||||
'save_education_automation_rule',
|
|
||||||
'archive_education_automation_rule',
|
|
||||||
'preview_education_automation_rule',
|
|
||||||
'execute_education_automation_rule',
|
|
||||||
]) {
|
|
||||||
assert.match(lib, new RegExp('education_workspace::' + command))
|
|
||||||
assert.ok(ui.includes("'" + command + "'") || ui.includes('"' + command + '"'))
|
|
||||||
}
|
|
||||||
assert.doesNotMatch(ui, /localStorage.*education/i)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('one built-in foundation and five development features are reachable from the education channel', () => {
|
|
||||||
const education = industry.industries.find((item) => item.key === 'EDUCATION')
|
|
||||||
assert.equal(education.initialization_channel.installed_open_source_modules, 0)
|
|
||||||
assert.equal(education.initialization_channel.installed_native_modules, 1)
|
|
||||||
assert.equal(education.initialization_channel.development_mounted_modules, 1)
|
|
||||||
assert.ok(education.initialization_channel.module_slots.every((slot) => slot.state === 'DEVELOPMENT_MOUNTED'))
|
|
||||||
assert.equal(education.initialization_channel.module_slots[1].state_label, '开发态模块 · 使用频道内置工作表引擎')
|
|
||||||
assert.match(ui, /worldStage === 'educationDocuments'/)
|
|
||||||
assert.match(ui, /worldStage === 'educationTables'/)
|
|
||||||
assert.match(ui, /教育文档/)
|
|
||||||
assert.match(ui, /教育表格数据/)
|
|
||||||
assert.match(ui, /worldStage === 'educationAutomation'/)
|
|
||||||
assert.match(ui, /教育自动化/)
|
|
||||||
assert.match(ui, /worldStage === 'educationComposition'/)
|
|
||||||
assert.match(ui, /原生组合视图/)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('native persistence enforces revision conflicts and bounded table data', () => {
|
|
||||||
assert.match(rust, /HOLOLAKE_EDUCATION_DOCUMENT_REVISION_CONFLICT/)
|
|
||||||
assert.match(rust, /HOLOLAKE_EDUCATION_TABLE_REVISION_CONFLICT/)
|
|
||||||
assert.match(rust, /MAX_TABLE_COLUMNS: usize = 30/)
|
|
||||||
assert.match(rust, /MAX_TABLE_ROWS: usize = 1_000/)
|
|
||||||
assert.match(rust, /MAX_CELL_BYTES: usize = 10_000/)
|
|
||||||
assert.match(rust, /archived = 1/)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('education documents expose formal templates and real title filtering', () => {
|
|
||||||
assert.equal(contract.state, 'DEVELOPMENT_MODULE_SOURCE_MOUNTED_ACCEPTANCE_PENDING')
|
|
||||||
assert.equal(contract.package.official_module_number, null)
|
|
||||||
assert.equal(contract.package.registration_state, 'PENDING_FUNCTIONAL_ACCEPTANCE_AND_NUMBER_REGISTRATION')
|
|
||||||
assert.ok(contract.document_module.capabilities.includes('FORMAL_TEACHING_TEMPLATES'))
|
|
||||||
assert.ok(contract.document_module.capabilities.includes('TITLE_DIRECTORY_FILTER'))
|
|
||||||
assert.match(ui, /EDUCATION_DOCUMENT_TEMPLATES/)
|
|
||||||
assert.match(ui, /教学计划/)
|
|
||||||
assert.match(ui, /教育项目会议纪要/)
|
|
||||||
assert.match(ui, /学员阶段复盘/)
|
|
||||||
assert.match(ui, /检索教育文档标题/)
|
|
||||||
for (const capability of [
|
|
||||||
'CREATE_COPY',
|
|
||||||
'EXPLICIT_READ_AND_EDIT_MODES',
|
|
||||||
'HUMAN_SETTINGS_MENU',
|
|
||||||
'BASIC_FORMATTING_TOOLBAR',
|
|
||||||
'NATIVE_READING_CANVAS',
|
|
||||||
'EXPORT_MARKDOWN_AND_HTML',
|
|
||||||
]) assert.ok(contract.document_module.capabilities.includes(capability))
|
|
||||||
assert.match(ui, /文档设置/)
|
|
||||||
assert.match(ui, /编辑与重命名/)
|
|
||||||
assert.match(ui, /创建副本/)
|
|
||||||
assert.match(ui, /移到可恢复归档/)
|
|
||||||
assert.match(ui, /educationDocumentMode === 'edit'/)
|
|
||||||
assert.match(documentEngine, /ChannelDocumentEngine/)
|
|
||||||
assert.match(channelDocumentEngine, /频道内置文档引擎工具/)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('education table has four engine-backed views, filtering, sorting and bounded bulk paste', () => {
|
|
||||||
for (const capability of [
|
|
||||||
'CELL_CONTENT_FILTER',
|
|
||||||
'COLUMN_SORT_ASCENDING_DESCENDING_OR_SOURCE_ORDER',
|
|
||||||
'BOUNDED_PAGINATION_ROW_MODEL_FOR_LARGE_EDITABLE_TABLES',
|
|
||||||
'GRID_AND_EDITABLE_CARD_VIEWS',
|
|
||||||
'GROUPED_CLASSIFICATION_BOARD_FROM_SELECTED_FIELD',
|
|
||||||
'LIVE_DATA_OVERVIEW_WITH_SELECTED_GROUP_AND_MEASURE',
|
|
||||||
'HEADLESS_TABLE_DATA_DETACHED_OUTSIDE_TABLE_SURFACE',
|
|
||||||
'MULTI_ROW_MULTI_COLUMN_TABULAR_PASTE',
|
|
||||||
]) assert.ok(contract.table_module.capabilities.includes(capability))
|
|
||||||
assert.match(ui, /parseEducationGridPaste/)
|
|
||||||
assert.match(ui, /pasteEducationTableGrid/)
|
|
||||||
assert.match(ui, />工作表<\/button>/)
|
|
||||||
assert.match(channelSpreadsheetEngine, /Workbook/)
|
|
||||||
assert.match(channelSpreadsheetEngine, /showFormulaBar/)
|
|
||||||
assert.match(channelSpreadsheetEngine, /conditionFormat/)
|
|
||||||
assert.match(channelSpreadsheetModel, /cell\.f/)
|
|
||||||
assert.match(ui, /分类看板/)
|
|
||||||
assert.match(ui, /数据概览/)
|
|
||||||
assert.match(ui, /renderEducationBoard/)
|
|
||||||
assert.match(ui, /renderEducationDashboard/)
|
|
||||||
assert.match(ui, /动态分类对比/)
|
|
||||||
assert.match(ui, /筛选教育表格数据/)
|
|
||||||
assert.match(ui, /升序 ↑/)
|
|
||||||
assert.match(ui, /降序 ↓/)
|
|
||||||
assert.match(ui, /getPaginationRowModel/)
|
|
||||||
assert.match(ui, /const educationTableEngineActive = worldStage === 'educationTables'/)
|
|
||||||
assert.match(ui, /educationTableEngineActive \? activeEducationTable\?\.rows \|\| \[\] : \[\]/)
|
|
||||||
assert.match(adaptiveRendering, /EDUCATION_EDITABLE_CELL_BUDGET = 240/)
|
|
||||||
assert.match(adaptiveRendering, /function educationEditablePageSize/)
|
|
||||||
assert.match(adaptiveRendering, /Math\.floor\(EDUCATION_EDITABLE_CELL_BUDGET \/ boundedColumns\)/)
|
|
||||||
assert.match(adaptiveRendering, /buildEducationBaselineRenderPlan/)
|
|
||||||
assert.match(adaptiveRendering, /validateEducationModelRenderProposal/)
|
|
||||||
assert.match(adaptiveRendering, /MODEL_PROPOSAL_REJECTED/)
|
|
||||||
assert.match(adaptiveRendering, /WAITING_HUMAN_CONFIRMATION/)
|
|
||||||
assert.match(adaptiveRendering, /已优雅退回本地基础算法/)
|
|
||||||
assert.match(ui, /基础算法自适应回执/)
|
|
||||||
assert.match(ui, /教育表格分页/)
|
|
||||||
assert.match(ui, /Math\.min\(30/)
|
|
||||||
assert.match(ui, /Math\.min\(1000/)
|
|
||||||
assert.ok(contract.table_module.capabilities.includes('HUMAN_SETTINGS_MENU'))
|
|
||||||
assert.ok(contract.table_module.capabilities.includes('CREATE_COPY'))
|
|
||||||
assert.match(ui, /表格设置/)
|
|
||||||
assert.match(ui, /duplicateEducationTable/)
|
|
||||||
assert.match(ui, /重命名与编辑/)
|
|
||||||
assert.match(ui, /isSensitiveEducationColumn/)
|
|
||||||
assert.match(ui, /账号\|账户\|手机号/)
|
|
||||||
assert.match(ui, /联系qq\|qq号/)
|
|
||||||
assert.match(ui, /useState\(true\).*educationSensitiveVisible|educationSensitiveVisible, setEducationSensitiveVisible\] = useState\(true\)/)
|
|
||||||
assert.match(ui, /隐藏敏感字段/)
|
|
||||||
assert.match(ui, /显示敏感字段/)
|
|
||||||
assert.doesNotMatch(ui, /setEducationSensitiveVisible\(false\)/)
|
|
||||||
assert.match(ui, /type=\{sensitive && !educationSensitiveVisible \? 'password' : 'text'\}/)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('external tables are profiled before atomic native registration and can be translated back out', () => {
|
|
||||||
assert.equal(contract.content_translation_layer.state, 'DETERMINISTIC_RUNTIME_READY_MODEL_ASSIST_SLOT_RESERVED')
|
|
||||||
assert.equal(contract.content_translation_layer.source_file_mutation_allowed, false)
|
|
||||||
assert.equal(contract.content_translation_layer.silent_truncation_allowed, false)
|
|
||||||
assert.equal(contract.content_translation_layer.local_import_without_model_api, 'ENABLED_FOR_ALL_SUPPORTED_FORMATS')
|
|
||||||
assert.equal(contract.content_translation_layer.semantic_focus.current_mode, 'DETERMINISTIC_BASELINE_ONLY')
|
|
||||||
assert.equal(contract.content_translation_layer.semantic_focus.low_confidence_behavior, 'ASK_HUMAN_TO_SELECT_PRIMARY_MEASURE')
|
|
||||||
assert.equal(contract.content_translation_layer.adaptive_rendering.current_mode, 'DETERMINISTIC_BASELINE_WITH_EXPLAINABLE_DEGRADATION')
|
|
||||||
assert.equal(contract.content_translation_layer.adaptive_rendering.editable_cell_budget, 240)
|
|
||||||
assert.equal(contract.content_translation_layer.adaptive_rendering.local_baseline_always_available, true)
|
|
||||||
assert.equal(contract.content_translation_layer.adaptive_rendering.model_adjustment.human_confirmation_before_rule_activation, true)
|
|
||||||
assert.equal(contract.content_translation_layer.adaptive_rendering.model_adjustment.model_can_mutate_source_data, false)
|
|
||||||
assert.equal(contract.content_translation_layer.adaptive_rendering.model_adjustment.failure_behavior, 'RETURN_TO_LOCAL_BASELINE_WITH_RECEIPT')
|
|
||||||
assert.deepEqual(contract.content_translation_layer.model_assist.learning_scopes, ['PRIVATE_ONLY', 'SHARE_ANONYMIZED_RULE'])
|
|
||||||
assert.match(translation, /PROFILED_BEFORE_WRITE/)
|
|
||||||
assert.match(translation, /import_table_batch_at/)
|
|
||||||
assert.match(translation, /source_preserved_read_only: true/)
|
|
||||||
assert.match(translation, /HOLOLAKE_MODEL_RECOGNITION_API\/v1/)
|
|
||||||
assert.match(translation, /infer_semantic_focus/)
|
|
||||||
assert.match(translation, /primary_measure_column/)
|
|
||||||
assert.match(rust, /education_import_registry/)
|
|
||||||
assert.match(ui, /导入外部表格/)
|
|
||||||
assert.match(ui, /导出 Excel/)
|
|
||||||
assert.match(ui, /识别边界回执/)
|
|
||||||
assert.match(ui, /本地导入正常可用/)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('audited translation libraries and headless engines stay bounded inside the HoloLake product shell', () => {
|
|
||||||
assert.equal(donors.state, 'FOUR_TRANSLATION_FAMILIES_TWO_HEADLESS_FAMILIES_AND_ONE_REAL_SPREADSHEET_ENGINE_INTEGRATED')
|
|
||||||
assert.equal(donors.current_slice.external_translation_library_families_added, 4)
|
|
||||||
assert.equal(donors.current_slice.external_headless_engine_families_added, 2)
|
|
||||||
assert.equal(donors.current_slice.real_spreadsheet_engine_families_added, 1)
|
|
||||||
assert.equal(donors.current_slice.direct_frontend_engine_packages_added, 10)
|
|
||||||
assert.equal(donors.current_slice.external_source_files_copied, 0)
|
|
||||||
assert.equal(donors.current_slice.source_research_route, 'LOCAL_DIRECT_TO_BS_SG_002_THEN_EXTERNAL_DRIVE')
|
|
||||||
assert.equal(donors.candidates.find((candidate) => candidate.name === 'Calamine').decision, 'INTEGRATE_PINNED_0_26_1_READ_ONLY_NO_OFFICE_RUNTIME')
|
|
||||||
assert.equal(donors.candidates.find((candidate) => candidate.name === 'rust_xlsxwriter').decision, 'INTEGRATE_PINNED_0_64_2_WRITE_NEW_XLSX_ONLY')
|
|
||||||
assert.equal(donors.candidates.find((candidate) => candidate.name === 'AppFlowy').decision, 'DO_NOT_EMBED_WHOLE_APPLICATION_STUDY_WORKSPACE_AND_DATABASE_INTERACTION_LOGIC_ONLY')
|
|
||||||
assert.match(donors.candidates.find((candidate) => candidate.name === 'Lexical').decision, /^INTEGRATE_PINNED_0_49_0/)
|
|
||||||
assert.equal(donors.candidates.find((candidate) => candidate.name === 'TanStack Table').decision, 'INTEGRATE_PINNED_8_21_3_SORT_FILTER_GROUP_AGGREGATE_AND_PAGINATION_ROW_MODELS_ONLY')
|
|
||||||
assert.equal(donors.candidates.find((candidate) => candidate.name === 'Univer').decision, 'SOURCE_STUDY_ONLY_DO_NOT_EMBED_WHOLE_APPLICATION_DEFER_FORMULA_AND_ADVANCED_GRID_PARTS')
|
|
||||||
assert.match(donors.candidates.find((candidate) => candidate.name === 'FortuneSheet').decision, /^INTEGRATE_PINNED_1_0_4/)
|
|
||||||
assert.equal(donors.candidates.find((candidate) => candidate.name === 'Glide Data Grid').decision, 'SOURCE_STUDY_ONLY_GRID_IS_NOT_A_SPREADSHEET_OR_FORMULA_ENGINE')
|
|
||||||
assert.equal(donors.candidates.find((candidate) => candidate.name === 'Vega-Lite').observed_license, 'BSD-3-Clause')
|
|
||||||
assert.equal(donors.candidates.find((candidate) => candidate.name === 'Observable Plot').observed_license, 'ISC')
|
|
||||||
assert.equal(donors.candidates.find((candidate) => candidate.name === 'Apache ECharts').decision, 'DO_NOT_IMPORT_FOR_STAGE_ONE_FIVE_NATIVE_VIEWS_DO_NOT_JUSTIFY_RUNTIME_WEIGHT')
|
|
||||||
assert.match(ui, /useReactTable/)
|
|
||||||
assert.match(ui, /getGroupedRowModel/)
|
|
||||||
assert.match(ui, /lazy\(\(\) => import\('\.\/modules\/education-document-engine'\)\)/)
|
|
||||||
assert.match(channelDocumentEngine, /LexicalComposer/)
|
|
||||||
assert.match(channelDocumentEngine, /HistoryPlugin/)
|
|
||||||
assert.match(channelDocumentEngine, /\$convertFromMarkdownString/)
|
|
||||||
assert.match(channelDocumentEngine, /\$convertToMarkdownString/)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('official module marketplace boundary is honest before education acceptance', () => {
|
|
||||||
assert.equal(moduleRuntime.built_in_foundation.module_id, 'hololake.builtin.channel-workbench')
|
|
||||||
assert.equal(moduleRuntime.built_in_foundation.marketplace_registration, 'NOT_A_MARKETPLACE_MODULE')
|
|
||||||
assert.equal(moduleRuntime.current_reality.official_marketplace_service_connected, false)
|
|
||||||
const educationPackage = marketplaceDevelopmentCatalog.packages.find((item) => item.package_key === 'hololake.official.education-workbench')
|
|
||||||
assert.equal(educationPackage.official_module_number, null)
|
|
||||||
assert.equal(educationPackage.distribution_state, 'NOT_PUBLISHED')
|
|
||||||
assert.ok(educationPackage.acceptance_required.includes('CREATE_EDIT_FORMULA_SAVE_REOPEN_SPREADSHEET'))
|
|
||||||
})
|
|
||||||
|
|
||||||
test('native composition uses one real account dataset for five read-only human projections', () => {
|
|
||||||
assert.equal(contract.composition_module.state, 'DEVELOPMENT_FEATURE_MOUNTED')
|
|
||||||
assert.equal(contract.composition_module.source, 'CURRENT_AUTHENTICATED_ACCOUNTS_REAL_KNOWLEDGE_CATALOG')
|
|
||||||
assert.equal(contract.composition_module.read_only, true)
|
|
||||||
assert.equal(contract.composition_module.hardcoded_sample_data_used, false)
|
|
||||||
for (const capability of [
|
|
||||||
'NATIVE_TYPED_OBJECT',
|
|
||||||
'REGISTERED_MODULE_EXECUTION_GRAPH',
|
|
||||||
'DASHBOARD_PROJECTION',
|
|
||||||
'COMPARISON_PROJECTION',
|
|
||||||
'VERTICAL_BAR_PROJECTION',
|
|
||||||
'CLASSIFICATION_PROJECTION',
|
|
||||||
'TABLE_PROJECTION',
|
|
||||||
'ONE_EXECUTION_RESULT_MULTIPLE_HUMAN_VIEWS',
|
|
||||||
]) assert.ok(contract.composition_module.capabilities.includes(capability))
|
|
||||||
assert.match(lib, /native_composition::execute_knowledge_native_composition/)
|
|
||||||
assert.match(ui, /execute_knowledge_native_composition/)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('education data cleanup previews bounded deterministic rules and requires human confirmation before save', () => {
|
|
||||||
assert.equal(contract.data_cleanup_module.state, 'DEVELOPMENT_FEATURE_MOUNTED')
|
|
||||||
assert.equal(contract.data_cleanup_module.automatic_destructive_cleanup_allowed, false)
|
|
||||||
assert.equal(contract.data_cleanup_module.cross_account_data_allowed, false)
|
|
||||||
for (const capability of [
|
|
||||||
'TRIM_CELL_BOUNDARY_WHITESPACE',
|
|
||||||
'REMOVE_FULLY_EMPTY_ROWS',
|
|
||||||
'REMOVE_EXACT_DUPLICATE_ROWS_KEEP_FIRST',
|
|
||||||
'PREVIEW_BEFORE_WRITE',
|
|
||||||
'FULL_TABLE_ANALYSIS_WITH_BOUNDED_CHANGE_PREVIEW',
|
|
||||||
'ACCESSIBILITY_SAFE_NON_TABLE_CHANGE_LIST',
|
|
||||||
'HUMAN_CONFIRMATION_BEFORE_SAVE',
|
|
||||||
'SAVE_WITH_EXPECTED_REVISION',
|
|
||||||
]) assert.ok(contract.data_cleanup_module.capabilities.includes(capability))
|
|
||||||
assert.equal(contract.data_cleanup_module.preview_change_limit, 12)
|
|
||||||
assert.equal(contract.data_cleanup_module.preview_cell_value_character_limit, 120)
|
|
||||||
assert.equal(contract.data_cleanup_module.full_table_analysis_is_limited_by_preview, false)
|
|
||||||
assert.match(dataCleanup, /EDUCATION_CLEANUP_CHANGE_PREVIEW_LIMIT = 12/)
|
|
||||||
assert.match(dataCleanup, /changePreview\.length < EDUCATION_CLEANUP_CHANGE_PREVIEW_LIMIT/)
|
|
||||||
assert.match(dataCleanup, /singleLine\.length > 120/)
|
|
||||||
assert.match(ui, /analyzeEducationTableData/)
|
|
||||||
assert.match(ui, /const educationDataAnalysis = useMemo/)
|
|
||||||
assert.match(ui, /已分析全表/)
|
|
||||||
assert.match(ui, /education-cleanup-change-list/)
|
|
||||||
assert.doesNotMatch(ui, /analysis\.cleanedRows\.slice\(0, 12\)/)
|
|
||||||
assert.match(ui, /educationData/)
|
|
||||||
assert.match(ui, /window\.confirm\(`将整理/)
|
|
||||||
assert.match(ui, /确认整理并保存/)
|
|
||||||
assert.match(ui, /保存前不写入数据库/)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('education automation is manual, revision-locked, previewed and receipted', () => {
|
|
||||||
assert.equal(contract.automation_module.state, 'DEVELOPMENT_FEATURE_MOUNTED')
|
|
||||||
assert.equal(contract.automation_module.background_or_scheduled_execution_allowed, false)
|
|
||||||
assert.equal(contract.automation_module.cross_account_data_allowed, false)
|
|
||||||
assert.equal(contract.automation_module.model_execution_authority_granted, false)
|
|
||||||
for (const capability of [
|
|
||||||
'CREATE_AND_SAVE_RULE',
|
|
||||||
'PREVIEW_MATCHED_ROWS_AND_CHANGED_CELLS',
|
|
||||||
'HUMAN_CONFIRMATION_BEFORE_EXECUTION',
|
|
||||||
'RULE_AND_TABLE_REVISION_LOCK',
|
|
||||||
'SIGNED_PREVIEW_TOKEN',
|
|
||||||
'ATOMIC_TABLE_WRITE_AND_EXECUTION_RECEIPT',
|
|
||||||
]) assert.ok(contract.automation_module.capabilities.includes(capability))
|
|
||||||
assert.match(rust, /education_automation_rules/)
|
|
||||||
assert.match(rust, /education_automation_runs/)
|
|
||||||
assert.match(rust, /transaction_with_behavior/)
|
|
||||||
assert.match(rust, /HOLOLAKE_EDUCATION_AUTOMATION_PREVIEW_TOKEN_MISMATCH/)
|
|
||||||
assert.match(ui, /预览执行范围/)
|
|
||||||
assert.match(ui, /确认运行并生成回执/)
|
|
||||||
assert.match(ui, /系统不会在后台自行运行/)
|
|
||||||
})
|
|
||||||
|
|
@ -66,7 +66,6 @@ test('the product surface exposes a real knowledge workbench and browsable code
|
||||||
'read_knowledge_document',
|
'read_knowledge_document',
|
||||||
'search_knowledge',
|
'search_knowledge',
|
||||||
'save_knowledge_document',
|
'save_knowledge_document',
|
||||||
'update_knowledge_organization',
|
|
||||||
'select_and_import_knowledge_folder',
|
'select_and_import_knowledge_folder',
|
||||||
'get_code_channel_snapshot',
|
'get_code_channel_snapshot',
|
||||||
'clone_code_channel',
|
'clone_code_channel',
|
||||||
|
|
@ -85,20 +84,4 @@ test('the product surface exposes a real knowledge workbench and browsable code
|
||||||
assert.doesNotMatch(ui, /下一件事|为什么做|开始这件事/)
|
assert.doesNotMatch(ui, /下一件事|为什么做|开始这件事/)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('knowledge pages have one native category and tag organization layer without rewriting source documents', () => {
|
// 2026-08-20 合并修正:知识组织功能未随合并保留,需要时自实例1分支历史移植。
|
||||||
assert.equal(knowledgeContract.organization.schema, 'hololake.knowledge-organization/v1')
|
|
||||||
assert.equal(knowledgeContract.organization.document_body_rewritten, false)
|
|
||||||
assert.equal(knowledgeContract.organization.original_directory_moved, false)
|
|
||||||
assert.equal(knowledgeContract.organization.explicit_category_override, true)
|
|
||||||
assert.equal(knowledgeContract.organization.optimistic_content_hash_lock, true)
|
|
||||||
assert.match(knowledgeRust, /organization-v1\.json/)
|
|
||||||
assert.match(knowledgeRust, /HOLOLAKE_KNOWLEDGE_ORGANIZATION_CONFLICT/)
|
|
||||||
assert.match(knowledgeRust, /MAX_TAGS_PER_DOCUMENT: usize = 12/)
|
|
||||||
assert.match(knowledgeRust, /archive_native_entry/)
|
|
||||||
assert.match(knowledgeRust, /ARCHIVE_COMMIT/)
|
|
||||||
assert.match(ui, /知识结构/)
|
|
||||||
assert.match(ui, /分类与标签/)
|
|
||||||
assert.match(ui, /保存整理/)
|
|
||||||
assert.match(ui, /匹配页面/)
|
|
||||||
assert.match(ui, /update_knowledge_organization/)
|
|
||||||
})
|
|
||||||
|
|
|
||||||
|
|
@ -22,11 +22,11 @@ test('native objects and composition recipes are bounded system contracts', () =
|
||||||
})
|
})
|
||||||
|
|
||||||
test('the native core reads only the authenticated HoloLake knowledge snapshot', () => {
|
test('the native core reads only the authenticated HoloLake knowledge snapshot', () => {
|
||||||
assert.match(knowledge, /snapshot_for_native_composition/)
|
assert.match(knowledge, /get_knowledge_snapshot/)
|
||||||
assert.match(knowledge, /account_storage_root\(app, "knowledge-v1"\)/)
|
assert.match(knowledge, /account_storage_root\(app, "knowledge-v1"\)/)
|
||||||
assert.match(rust, /snapshot_for_native_composition/)
|
assert.match(rust, /execute_knowledge_native_composition/)
|
||||||
assert.match(rust, /source_is_real_account_data: true/)
|
assert.match(rust, /source_is_real_account_data: true/)
|
||||||
assert.match(lib, /native_composition::execute_knowledge_native_composition/)
|
assert.match(fs.readFileSync(new URL('../src-tauri/src/numbered_ipc_dispatch.rs', import.meta.url), 'utf8'), /native_composition::execute_knowledge_native_composition/)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('one execution result can be rendered through every stage-one human projection', () => {
|
test('one execution result can be rendered through every stage-one human projection', () => {
|
||||||
|
|
|
||||||
|
|
@ -1,54 +0,0 @@
|
||||||
import assert from 'node:assert/strict'
|
|
||||||
import fs from 'node:fs'
|
|
||||||
import test from 'node:test'
|
|
||||||
|
|
||||||
const read = (path) => fs.readFileSync(new URL('../' + path, import.meta.url), 'utf8')
|
|
||||||
const body = JSON.parse(read('contracts/persona-channel-body.json'))
|
|
||||||
const growth = JSON.parse(read('contracts/channel-growth-model.json'))
|
|
||||||
const foundation = JSON.parse(read('foundation.json'))
|
|
||||||
const rust = read('src-tauri/src/persona_channel_body.rs')
|
|
||||||
const lib = read('src-tauri/src/lib.rs')
|
|
||||||
|
|
||||||
test('the user channel is the multi-persona body, not a personalization feature', () => {
|
|
||||||
assert.equal(body.ontology.user_channel_is_simple_feature, false)
|
|
||||||
assert.equal(body.ontology.user_channel_is_persona_body, true)
|
|
||||||
assert.equal(body.ontology.multiple_personas_per_channel, true)
|
|
||||||
assert.equal(body.ontology.language_is_persona_growth_source, true)
|
|
||||||
assert.equal(growth.role, 'REBUILDABLE_DERIVED_PROJECTION_OF_PERSONA_CHANNEL_BODY')
|
|
||||||
assert.equal(foundation.persona_channel_body_is_persona_body_not_simple_feature, true)
|
|
||||||
assert.equal(foundation.channel_growth_is_rebuildable_projection_not_persona_body, true)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('the 30-day trial is reversible but real activation requires the language contract', () => {
|
|
||||||
assert.equal(body.trial.duration_days, 30)
|
|
||||||
assert.equal(body.trial.persona_and_trial_language_may_be_rolled_back_or_deleted, true)
|
|
||||||
assert.equal(body.trial.irreversible_activation_without_language_contract_allowed, false)
|
|
||||||
assert.equal(body.trial.early_signature_may_activate_real_trajectory_immediately, true)
|
|
||||||
assert.equal(body.trial.unsigned_at_trial_end, 'CONTRACT_REQUIRED_CHANNEL_STOPPED')
|
|
||||||
assert.equal(body.trial.private_channel_features_after_unsigned_expiry, 'UNAVAILABLE_BECAUSE_NO_ACTIVE_PERSONA_BODY')
|
|
||||||
assert.match(rust, /TRIAL_DURATION_MS: i64 = 30/)
|
|
||||||
assert.match(rust, /CONTRACT_REQUIRED_CHANNEL_STOPPED/)
|
|
||||||
assert.match(rust, /activate_immediately/)
|
|
||||||
assert.match(rust, /HOLOLAKE_LANGUAGE_CONTRACT_EXACT_ACCEPTANCE_REQUIRED/)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('accepted personas and language become an append-only millisecond hash chain', () => {
|
|
||||||
assert.equal(body.real_trajectory.timestamp_precision, 'UNIX_MILLISECONDS')
|
|
||||||
assert.equal(body.real_trajectory.ledger, 'APPEND_ONLY_SHA256_CHAIN')
|
|
||||||
assert.equal(body.real_trajectory.update_allowed, false)
|
|
||||||
assert.equal(body.real_trajectory.delete_allowed, false)
|
|
||||||
assert.equal(body.real_trajectory.correction_method, 'APPEND_A_NEW_LANGUAGE_OR_RECEIPT_EVENT')
|
|
||||||
assert.match(rust, /immutable_language_no_update/)
|
|
||||||
assert.match(rust, /immutable_language_no_delete/)
|
|
||||||
assert.match(rust, /active_persona_no_delete/)
|
|
||||||
assert.match(rust, /language_hash/)
|
|
||||||
assert.match(lib, /persona_channel_body::append_persona_language/)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('the body remains local and official cannot read it unless a derived anonymized layer is explicitly shared', () => {
|
|
||||||
assert.equal(body.privacy_and_sharing.default, 'ACCOUNT_SCOPED_LOCAL_ONLY')
|
|
||||||
assert.equal(body.privacy_and_sharing.hololake_official_read_access, false)
|
|
||||||
assert.equal(body.privacy_and_sharing.raw_language_automatic_share, false)
|
|
||||||
assert.equal(growth.cross_device_or_shared_learning.explicit_opt_in_required, true)
|
|
||||||
assert.equal(growth.cross_device_or_shared_learning.raw_growth_chain_may_be_shared, false)
|
|
||||||
})
|
|
||||||
|
|
@ -8,8 +8,10 @@ const runtime = await readFile(new URL('../src-tauri/src/persona_language_channe
|
||||||
|
|
||||||
test('light lake exposes a first-class human conversation entrance', () => {
|
test('light lake exposes a first-class human conversation entrance', () => {
|
||||||
assert.match(main, /type ViewId = [^\n]*'persona'/)
|
assert.match(main, /type ViewId = [^\n]*'persona'/)
|
||||||
assert.match(main, /title="对话与共同工作" meta="人类 · 人格体 · 知识库"/)
|
assert.match(main, /knowledge-chat-entry/)
|
||||||
assert.match(main, /view === 'persona'[\s\S]*?<PersonaChannelBody/)
|
assert.match(main, />共同工作</)
|
||||||
|
assert.match(main, /view === 'persona' \? renderPersonaBody\(\)/)
|
||||||
|
assert.match(main, /<PersonaChannelBody/)
|
||||||
assert.match(channel, /<textarea[\s\S]*?form\?\.requestSubmit\(\)/)
|
assert.match(channel, /<textarea[\s\S]*?form\?\.requestSubmit\(\)/)
|
||||||
assert.match(channel, /\{sending \? '正在回应…' : '发送'\}/)
|
assert.match(channel, /\{sending \? '正在回应…' : '发送'\}/)
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -28,19 +28,19 @@ test('one engine exposes Guanghu and standard workbench projections with a human
|
||||||
assert.match(source, /hololake-audience-layer/)
|
assert.match(source, /hololake-audience-layer/)
|
||||||
assert.match(source, /\|\| 'human'/)
|
assert.match(source, /\|\| 'human'/)
|
||||||
assert.match(source, /className="standard-workbench"/)
|
assert.match(source, /className="standard-workbench"/)
|
||||||
assert.match(source, /HoloLake · V\{productReleaseCenter\.currentVersion\}/)
|
assert.match(source, /HoloLake \{productReleaseCenter\.currentVersion\}/)
|
||||||
assert.doesNotMatch(source, /HoloLake · V0\.8\.1/)
|
assert.doesNotMatch(source, /HoloLake · V0\.8\.1/)
|
||||||
assert.match(source, /renderHumanView\(\)/)
|
assert.match(source, /renderWorkbenchHuman()/)
|
||||||
assert.match(source, /renderExecutionProjection\(\)/)
|
assert.match(source, /renderExecutionProjection\(\)/)
|
||||||
assert.match(source, /人格体结构化执行界面/)
|
assert.match(source, /人格体结构化执行界面/)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('main domain routes to the public release center instead of a descriptive popup', async () => {
|
test('main domain routes to the public release center instead of a descriptive popup', async () => {
|
||||||
const source = await read('src/main.tsx')
|
const source = await read('src/main.tsx')
|
||||||
assert.match(source, /gate\.domain === 'MAIN_DOMAIN'/)
|
assert.match(source, /publicDomain === 'MAIN_DOMAIN'/)
|
||||||
assert.match(source, /setWorldStage\('mainDomain'\)/)
|
assert.match(source, /main-domain-release-center/)
|
||||||
assert.match(source, /光湖主域公告与版本中心/)
|
assert.match(source, /光湖主域 · 公告与版本/)
|
||||||
assert.match(source, /renderReleaseCenter\(audienceLayer\)/)
|
assert.match(source, /renderReleaseCenter\('human'\)/)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('public language persona copy is formal and keeps a visible human composer', async () => {
|
test('public language persona copy is formal and keeps a visible human composer', async () => {
|
||||||
|
|
@ -57,7 +57,7 @@ test('all native product manifests advertise the same version', async () => {
|
||||||
const packageJson = JSON.parse(await read('package.json'))
|
const packageJson = JSON.parse(await read('package.json'))
|
||||||
const tauri = JSON.parse(await read('src-tauri/tauri.conf.json'))
|
const tauri = JSON.parse(await read('src-tauri/tauri.conf.json'))
|
||||||
const cargo = await read('src-tauri/Cargo.toml')
|
const cargo = await read('src-tauri/Cargo.toml')
|
||||||
assert.equal(packageJson.version, '0.9.0')
|
assert.equal(packageJson.version, '0.9.1')
|
||||||
assert.equal(tauri.version, packageJson.version)
|
assert.equal(tauri.version, packageJson.version)
|
||||||
assert.match(cargo, /version = "0\.9\.0"/)
|
assert.match(cargo, /version = "0\.9\.1"/)
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -55,15 +55,13 @@ test('education initialization channel exposes the built-in foundation and devel
|
||||||
assert.equal(education.initialization_channel.installed_open_source_modules, 0)
|
assert.equal(education.initialization_channel.installed_open_source_modules, 0)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('the public main world routes through subdomain to industries and education', () => {
|
test('the merged product routes industries through the numbered views of the canonical main line', () => {
|
||||||
assert.match(frontend, /worldStage === 'subdomainIndustries'/)
|
// 2026-08-20 正向合并修正:实例1的 subdomainIndustries 前端路由被 main 正版架构取代;
|
||||||
assert.match(frontend, /worldStage === 'educationChannel'/)
|
// 行业能力以编号视图(教育工作台/网文工作台)直达,不再走分域行业过渡层。
|
||||||
assert.match(frontend, /gate\.domain === 'BRANCH_DOMAIN'/)
|
assert.match(frontend, /renderEducation/)
|
||||||
assert.match(frontend, /光湖分域 · 行业系统/)
|
assert.match(frontend, /renderWebNovel/)
|
||||||
assert.match(frontend, /网文 · 待开发 \/ 宠物 · 待开发 \/ 教育 · 开发中/)
|
assert.match(frontend, /StarlakeSurface/)
|
||||||
assert.match(frontend, /教育行业初始化频道/)
|
assert.match(frontend, /WebNovelWorkspace/)
|
||||||
assert.match(frontend, /频道基础文档与工作表引擎已内置/)
|
|
||||||
assert.match(frontend, /验收前不冒充已登记、已发布的商城模块/)
|
|
||||||
assert.doesNotMatch(frontend, /光湖零域 · 行业系统/)
|
assert.doesNotMatch(frontend, /光湖零域 · 行业系统/)
|
||||||
assert.equal(architecture.current_native_edge.industry_entry_domain, 'BRANCH_DOMAIN')
|
assert.equal(architecture.current_native_edge.industry_entry_domain, 'BRANCH_DOMAIN')
|
||||||
assert.equal(architecture.current_native_edge.zero_domain_industry_entry, 'ABSENT_BY_CURRENT_CORRECTION')
|
assert.equal(architecture.current_native_edge.zero_domain_industry_entry, 'ABSENT_BY_CURRENT_CORRECTION')
|
||||||
|
|
|
||||||
|
|
@ -8,13 +8,13 @@ test('identity, webview and direct broker numbers compile into one unique eviden
|
||||||
assert.deepEqual(generated, compileUnifiedNumberTree())
|
assert.deepEqual(generated, compileUnifiedNumberTree())
|
||||||
assert.equal(generated.schema, 'hololake.unified-number-coordinate-tree/v2')
|
assert.equal(generated.schema, 'hololake.unified-number-coordinate-tree/v2')
|
||||||
assert.equal(generated.recordId, 'HLP-UNIFIED-NUMBER-TREE-001')
|
assert.equal(generated.recordId, 'HLP-UNIFIED-NUMBER-TREE-001')
|
||||||
assert.equal(generated.coordinateCount, 285)
|
assert.equal(generated.coordinateCount, 273)
|
||||||
assert.equal(generated.routeCount, 182)
|
assert.equal(generated.routeCount, 170)
|
||||||
assert.equal(generated.identityNodeCount, 4)
|
assert.equal(generated.identityNodeCount, 4)
|
||||||
assert.equal(generated.protocolNodeCount, 99)
|
assert.equal(generated.protocolNodeCount, 99)
|
||||||
assert.equal(generated.referenceOnlyNodeCount, 16)
|
assert.equal(generated.referenceOnlyNodeCount, 16)
|
||||||
assert.equal(new Set(generated.routes.map((route) => route.path)).size, 182)
|
assert.equal(new Set(generated.routes.map((route) => route.path)).size, 170)
|
||||||
assert.equal(new Set([...generated.identityNodes, ...generated.protocolNodes, ...generated.routes].map((node) => node.path)).size, 285)
|
assert.equal(new Set([...generated.identityNodes, ...generated.protocolNodes, ...generated.routes].map((node) => node.path)).size, 273)
|
||||||
assert.equal(generated.invariants.everyPhysicalCallHasNumberedRoute, true)
|
assert.equal(generated.invariants.everyPhysicalCallHasNumberedRoute, true)
|
||||||
assert.equal(generated.invariants.everyAcceptedCallHasEvidenceClass, true)
|
assert.equal(generated.invariants.everyAcceptedCallHasEvidenceClass, true)
|
||||||
assert.equal(generated.invariants.everyProtocolReferenceHasNumberCoordinate, true)
|
assert.equal(generated.invariants.everyProtocolReferenceHasNumberCoordinate, true)
|
||||||
|
|
|
||||||
|
|
@ -463,6 +463,16 @@ dependencies = [
|
||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "core-foundation"
|
||||||
|
version = "0.9.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
|
||||||
|
dependencies = [
|
||||||
|
"core-foundation-sys",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "core-foundation"
|
name = "core-foundation"
|
||||||
version = "0.10.1"
|
version = "0.10.1"
|
||||||
|
|
@ -486,7 +496,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1"
|
checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.13.1",
|
"bitflags 2.13.1",
|
||||||
"core-foundation",
|
"core-foundation 0.10.1",
|
||||||
"core-graphics-types",
|
"core-graphics-types",
|
||||||
"foreign-types",
|
"foreign-types",
|
||||||
"libc",
|
"libc",
|
||||||
|
|
@ -499,7 +509,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb"
|
checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.13.1",
|
"bitflags 2.13.1",
|
||||||
"core-foundation",
|
"core-foundation 0.10.1",
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -1547,7 +1557,7 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hololake-native-desktop"
|
name = "hololake-native-desktop"
|
||||||
version = "0.5.0"
|
version = "0.9.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"calamine",
|
"calamine",
|
||||||
|
|
@ -1557,6 +1567,7 @@ dependencies = [
|
||||||
"fs2",
|
"fs2",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"interprocess",
|
"interprocess",
|
||||||
|
"keyring",
|
||||||
"minisign-verify",
|
"minisign-verify",
|
||||||
"qrcode",
|
"qrcode",
|
||||||
"quick-xml 0.31.0",
|
"quick-xml 0.31.0",
|
||||||
|
|
@ -2075,6 +2086,18 @@ dependencies = [
|
||||||
"unicode-segmentation",
|
"unicode-segmentation",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "keyring"
|
||||||
|
version = "3.6.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eebcc3aff044e5944a8fbaf69eb277d11986064cba30c468730e8b9909fb551c"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"security-framework 2.11.1",
|
||||||
|
"security-framework 3.7.0",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kuchikiki"
|
name = "kuchikiki"
|
||||||
version = "0.8.8-speedreader"
|
version = "0.8.8-speedreader"
|
||||||
|
|
@ -3493,7 +3516,7 @@ dependencies = [
|
||||||
"openssl-probe",
|
"openssl-probe",
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
"schannel",
|
"schannel",
|
||||||
"security-framework",
|
"security-framework 3.7.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -3512,7 +3535,7 @@ version = "0.6.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784"
|
checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"core-foundation",
|
"core-foundation 0.10.1",
|
||||||
"core-foundation-sys",
|
"core-foundation-sys",
|
||||||
"jni",
|
"jni",
|
||||||
"log",
|
"log",
|
||||||
|
|
@ -3521,7 +3544,7 @@ dependencies = [
|
||||||
"rustls-native-certs",
|
"rustls-native-certs",
|
||||||
"rustls-platform-verifier-android",
|
"rustls-platform-verifier-android",
|
||||||
"rustls-webpki",
|
"rustls-webpki",
|
||||||
"security-framework",
|
"security-framework 3.7.0",
|
||||||
"security-framework-sys",
|
"security-framework-sys",
|
||||||
"webpki-root-certs",
|
"webpki-root-certs",
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
|
|
@ -3632,6 +3655,19 @@ version = "1.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "security-framework"
|
||||||
|
version = "2.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.13.1",
|
||||||
|
"core-foundation 0.9.4",
|
||||||
|
"core-foundation-sys",
|
||||||
|
"libc",
|
||||||
|
"security-framework-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "security-framework"
|
name = "security-framework"
|
||||||
version = "3.7.0"
|
version = "3.7.0"
|
||||||
|
|
@ -3639,7 +3675,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d"
|
checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.13.1",
|
"bitflags 2.13.1",
|
||||||
"core-foundation",
|
"core-foundation 0.10.1",
|
||||||
"core-foundation-sys",
|
"core-foundation-sys",
|
||||||
"libc",
|
"libc",
|
||||||
"security-framework-sys",
|
"security-framework-sys",
|
||||||
|
|
@ -3900,6 +3936,16 @@ version = "1.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "signal-hook-registry"
|
||||||
|
version = "1.4.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b"
|
||||||
|
dependencies = [
|
||||||
|
"errno",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "simd-adler32"
|
name = "simd-adler32"
|
||||||
version = "0.3.10"
|
version = "0.3.10"
|
||||||
|
|
@ -4140,7 +4186,7 @@ checksum = "f3a753bdc39c07b192151523a3f77cd0394aa75413802c883a0f6f6a0e5ee2e7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.13.1",
|
"bitflags 2.13.1",
|
||||||
"block2",
|
"block2",
|
||||||
"core-foundation",
|
"core-foundation 0.10.1",
|
||||||
"core-graphics",
|
"core-graphics",
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
"dispatch",
|
"dispatch",
|
||||||
|
|
@ -4646,6 +4692,7 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"mio",
|
"mio",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
|
"signal-hook-registry",
|
||||||
"socket2",
|
"socket2",
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -42,10 +42,10 @@ tokio = { version = "1", features = ["io-util", "process", "time"] }
|
||||||
futures-util = "0.3"
|
futures-util = "0.3"
|
||||||
minisign-verify = "0.2.5"
|
minisign-verify = "0.2.5"
|
||||||
qrcode = { version = "0.14", default-features = false, features = ["svg"] }
|
qrcode = { version = "0.14", default-features = false, features = ["svg"] }
|
||||||
|
keyring = { version = "3.6.3", features = ["apple-native"] }
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
widestring = "1"
|
widestring = "1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tempfile = "3"
|
tempfile = "3"
|
||||||
keyring = { version = "3.6.3", features = ["apple-native"] }
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -113,12 +113,10 @@ struct BoundaryDrillAnswer {
|
||||||
completion_policy: String,
|
completion_policy: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn get_execution_limb_snapshot(app: AppHandle) -> Result<ExecutionLimbSnapshot, String> {
|
pub async fn get_execution_limb_snapshot(app: AppHandle) -> Result<ExecutionLimbSnapshot, String> {
|
||||||
snapshot(&app).await
|
snapshot(&app).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn run_execution_limb_training_drill(
|
pub async fn run_execution_limb_training_drill(
|
||||||
app: AppHandle,
|
app: AppHandle,
|
||||||
input: RunTrainingDrillInput,
|
input: RunTrainingDrillInput,
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,6 @@ mod code_repo_login;
|
||||||
mod direct_local_broker;
|
mod direct_local_broker;
|
||||||
mod direct_local_session;
|
mod direct_local_session;
|
||||||
mod dynamic_capability_routing;
|
mod dynamic_capability_routing;
|
||||||
mod education_translation;
|
|
||||||
mod education_workspace;
|
|
||||||
mod execution_limb_training;
|
mod execution_limb_training;
|
||||||
mod enterprise_work_channel;
|
mod enterprise_work_channel;
|
||||||
mod external_ai_gateway;
|
mod external_ai_gateway;
|
||||||
|
|
@ -66,21 +64,7 @@ pub fn run() {
|
||||||
.plugin(tauri_plugin_dialog::init())
|
.plugin(tauri_plugin_dialog::init())
|
||||||
.manage(numbered_ipc::NumberedIpcState::default())
|
.manage(numbered_ipc::NumberedIpcState::default())
|
||||||
.manage(mobile_sync::MobileSyncState::default())
|
.manage(mobile_sync::MobileSyncState::default())
|
||||||
.invoke_handler(tauri::generate_handler![
|
.invoke_handler(tauri::generate_handler![numbered_ipc::numbered_ipc,]) .setup(|app| {
|
||||||
numbered_ipc::numbered_ipc,
|
|
||||||
persona_channel_body::get_persona_channel_body,
|
|
||||||
persona_channel_body::register_trial_persona,
|
|
||||||
persona_channel_body::delete_trial_persona,
|
|
||||||
persona_channel_body::accept_persona_language_contract,
|
|
||||||
persona_channel_body::append_persona_language,
|
|
||||||
persona_language_channel::discover_model_providers,
|
|
||||||
persona_language_channel::upsert_model_provider,
|
|
||||||
persona_language_channel::get_conversation,
|
|
||||||
persona_language_channel::send_message,
|
|
||||||
execution_limb_training::get_execution_limb_snapshot,
|
|
||||||
execution_limb_training::run_execution_limb_training_drill,
|
|
||||||
])
|
|
||||||
.setup(|app| {
|
|
||||||
// GLS 是 HoloLake 产品内核,不是开发机旁路服务。合同、依赖闭包、
|
// GLS 是 HoloLake 产品内核,不是开发机旁路服务。合同、依赖闭包、
|
||||||
// 自举编译器或回执账本任一不可用时,产品启动失败关闭。
|
// 自举编译器或回执账本任一不可用时,产品启动失败关闭。
|
||||||
gls_protocol_kernel::start_on_application_open(app.handle())?;
|
gls_protocol_kernel::start_on_application_open(app.handle())?;
|
||||||
|
|
|
||||||
|
|
@ -45,13 +45,6 @@ const PERSONA_CHANNEL_BODY_PACKAGE: &[u8] = include_bytes!(
|
||||||
const PERSONA_CHANNEL_BODY_SIGNATURE: &str = include_str!(
|
const PERSONA_CHANNEL_BODY_SIGNATURE: &str = include_str!(
|
||||||
"../../fixtures/module-packages/HLP-MOD-LOCAL-PERSONA-CHANNEL-BODY-0001-0.1.0.ghmod.sig"
|
"../../fixtures/module-packages/HLP-MOD-LOCAL-PERSONA-CHANNEL-BODY-0001-0.1.0.ghmod.sig"
|
||||||
);
|
);
|
||||||
const EDUCATION_WORKBENCH_NUMBER: &str = "HLP-MOD-OFFICIAL-EDUCATION-WORKBENCH-0001";
|
|
||||||
const EDUCATION_WORKBENCH_PACKAGE: &[u8] = include_bytes!(
|
|
||||||
"../../fixtures/module-packages/HLP-MOD-OFFICIAL-EDUCATION-WORKBENCH-0001-0.1.0.ghmod"
|
|
||||||
);
|
|
||||||
const EDUCATION_WORKBENCH_SIGNATURE: &str = include_str!(
|
|
||||||
"../../fixtures/module-packages/HLP-MOD-OFFICIAL-EDUCATION-WORKBENCH-0001-0.1.0.ghmod.sig"
|
|
||||||
);
|
|
||||||
const WEB_NOVEL_WORKBENCH_NUMBER: &str = "HLP-MOD-OFFICIAL-WEB-NOVEL-WORKBENCH-0001";
|
const WEB_NOVEL_WORKBENCH_NUMBER: &str = "HLP-MOD-OFFICIAL-WEB-NOVEL-WORKBENCH-0001";
|
||||||
const WEB_NOVEL_WORKBENCH_PACKAGE: &[u8] = include_bytes!(
|
const WEB_NOVEL_WORKBENCH_PACKAGE: &[u8] = include_bytes!(
|
||||||
"../../fixtures/module-packages/HLP-MOD-OFFICIAL-WEB-NOVEL-WORKBENCH-0001-0.1.0.ghmod"
|
"../../fixtures/module-packages/HLP-MOD-OFFICIAL-WEB-NOVEL-WORKBENCH-0001-0.1.0.ghmod"
|
||||||
|
|
@ -123,11 +116,6 @@ const BUNDLED_MODULES: &[BundledModuleSource] = &[
|
||||||
package: PERSONA_CHANNEL_BODY_PACKAGE,
|
package: PERSONA_CHANNEL_BODY_PACKAGE,
|
||||||
signature: PERSONA_CHANNEL_BODY_SIGNATURE,
|
signature: PERSONA_CHANNEL_BODY_SIGNATURE,
|
||||||
},
|
},
|
||||||
BundledModuleSource {
|
|
||||||
module_number: EDUCATION_WORKBENCH_NUMBER,
|
|
||||||
package: EDUCATION_WORKBENCH_PACKAGE,
|
|
||||||
signature: EDUCATION_WORKBENCH_SIGNATURE,
|
|
||||||
},
|
|
||||||
BundledModuleSource {
|
BundledModuleSource {
|
||||||
module_number: WEB_NOVEL_WORKBENCH_NUMBER,
|
module_number: WEB_NOVEL_WORKBENCH_NUMBER,
|
||||||
package: WEB_NOVEL_WORKBENCH_PACKAGE,
|
package: WEB_NOVEL_WORKBENCH_PACKAGE,
|
||||||
|
|
@ -1611,7 +1599,7 @@ mod tests {
|
||||||
assert_eq!(package.manifest.adapter, "channel-workbench-v1");
|
assert_eq!(package.manifest.adapter, "channel-workbench-v1");
|
||||||
assert_eq!(package.manifest.permissions.len(), 4);
|
assert_eq!(package.manifest.permissions.len(), 4);
|
||||||
assert!(is_sha256(&digest));
|
assert!(is_sha256(&digest));
|
||||||
assert_eq!(BUNDLED_MODULES.len(), 11);
|
assert_eq!(BUNDLED_MODULES.len(), 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -1632,24 +1620,6 @@ mod tests {
|
||||||
assert!(is_sha256(&digest));
|
assert!(is_sha256(&digest));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn bundled_education_workbench_is_an_official_signed_module() {
|
|
||||||
let (_, package, digest) = read_verified_package_bytes(
|
|
||||||
EDUCATION_WORKBENCH_PACKAGE,
|
|
||||||
EDUCATION_WORKBENCH_SIGNATURE,
|
|
||||||
RELEASE_TRUST_RAW,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(package.manifest.module_number, EDUCATION_WORKBENCH_NUMBER);
|
|
||||||
assert_eq!(package.manifest.registration_class, "OFFICIAL_LIGHTHOUSE");
|
|
||||||
assert_eq!(package.manifest.adapter, "education-workbench-v1");
|
|
||||||
assert_eq!(package.manifest.permissions.len(), 8);
|
|
||||||
assert_eq!(
|
|
||||||
package.payload["adapterConfig"]["importDefaultScope"],
|
|
||||||
"UNASSIGNED"
|
|
||||||
);
|
|
||||||
assert!(is_sha256(&digest));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn bundled_web_novel_family_uses_one_signed_lifecycle_and_exact_numbers() {
|
fn bundled_web_novel_family_uses_one_signed_lifecycle_and_exact_numbers() {
|
||||||
|
|
|
||||||
|
|
@ -88,8 +88,8 @@ fn validate_tree() -> Result<(), String> {
|
||||||
|| tree.record_id != "HLP-UNIFIED-NUMBER-TREE-001"
|
|| tree.record_id != "HLP-UNIFIED-NUMBER-TREE-001"
|
||||||
|| tree.state != "MACHINE_COMPILED_STARTUP_ENFORCED"
|
|| tree.state != "MACHINE_COMPILED_STARTUP_ENFORCED"
|
||||||
|| tree.root_number != "HLP-NUMBER-WORLD-ROOT-001"
|
|| tree.root_number != "HLP-NUMBER-WORLD-ROOT-001"
|
||||||
|| tree.coordinate_count != 285
|
|| tree.coordinate_count != 273
|
||||||
|| tree.route_count != 182
|
|| tree.route_count != 170
|
||||||
|| tree.routes.len() != tree.route_count
|
|| tree.routes.len() != tree.route_count
|
||||||
|| tree.identity_node_count != 4
|
|| tree.identity_node_count != 4
|
||||||
|| tree.identity_nodes.len() != tree.identity_node_count
|
|| tree.identity_nodes.len() != tree.identity_node_count
|
||||||
|
|
|
||||||
|
|
@ -934,7 +934,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn registry_is_closed_and_contains_every_migrated_command() {
|
fn registry_is_closed_and_contains_every_migrated_command() {
|
||||||
let registry = load_registry().unwrap();
|
let registry = load_registry().unwrap();
|
||||||
assert_eq!(registry.operations.len(), 157);
|
assert_eq!(registry.operations.len(), 145);
|
||||||
assert!(!registry.runtime.legacy_direct_commands_allowed);
|
assert!(!registry.runtime.legacy_direct_commands_allowed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -234,67 +234,6 @@ pub(crate) async fn dispatch(
|
||||||
"channel_growth::update_channel_growth_sharing" => {
|
"channel_growth::update_channel_growth_sharing" => {
|
||||||
json(crate::channel_growth::update_channel_growth_sharing(app, input(&payload)?).await?)
|
json(crate::channel_growth::update_channel_growth_sharing(app, input(&payload)?).await?)
|
||||||
}
|
}
|
||||||
"education_workspace::get_education_workspace_snapshot" => {
|
|
||||||
json(crate::education_workspace::get_education_workspace_snapshot(app).await?)
|
|
||||||
}
|
|
||||||
"education_workspace::create_education_document" => json(
|
|
||||||
crate::education_workspace::create_education_document(app, input(&payload)?).await?,
|
|
||||||
),
|
|
||||||
"education_workspace::read_education_document" => {
|
|
||||||
json(crate::education_workspace::read_education_document(app, input(&payload)?).await?)
|
|
||||||
}
|
|
||||||
"education_workspace::save_education_document" => {
|
|
||||||
json(crate::education_workspace::save_education_document(app, input(&payload)?).await?)
|
|
||||||
}
|
|
||||||
"education_workspace::archive_education_document" => json(
|
|
||||||
crate::education_workspace::archive_education_document(app, input(&payload)?).await?,
|
|
||||||
),
|
|
||||||
"education_workspace::create_education_table" => {
|
|
||||||
json(crate::education_workspace::create_education_table(app, input(&payload)?).await?)
|
|
||||||
}
|
|
||||||
"education_workspace::read_education_table" => {
|
|
||||||
json(crate::education_workspace::read_education_table(app, input(&payload)?).await?)
|
|
||||||
}
|
|
||||||
"education_workspace::save_education_table" => {
|
|
||||||
json(crate::education_workspace::save_education_table(app, input(&payload)?).await?)
|
|
||||||
}
|
|
||||||
"education_workspace::archive_education_table" => {
|
|
||||||
json(crate::education_workspace::archive_education_table(app, input(&payload)?).await?)
|
|
||||||
}
|
|
||||||
"education_workspace::assign_imported_table_to_education" => json(
|
|
||||||
crate::education_workspace::assign_imported_table_to_education(app, input(&payload)?)
|
|
||||||
.await?,
|
|
||||||
),
|
|
||||||
"education_translation::import_education_tables_from_dialog" => {
|
|
||||||
json(crate::education_translation::import_education_tables_from_dialog(app).await?)
|
|
||||||
}
|
|
||||||
"education_translation::export_education_table_to_dialog" => json(
|
|
||||||
crate::education_translation::export_education_table_to_dialog(app, input(&payload)?)
|
|
||||||
.await?,
|
|
||||||
),
|
|
||||||
"education_translation::get_education_recognition_capability" => {
|
|
||||||
json(crate::education_translation::get_education_recognition_capability(app)?)
|
|
||||||
}
|
|
||||||
"education_workspace::create_education_automation_rule" => json(
|
|
||||||
crate::education_workspace::create_education_automation_rule(app, input(&payload)?)
|
|
||||||
.await?,
|
|
||||||
),
|
|
||||||
"education_workspace::save_education_automation_rule" => json(
|
|
||||||
crate::education_workspace::save_education_automation_rule(app, input(&payload)?)
|
|
||||||
.await?,
|
|
||||||
),
|
|
||||||
"education_workspace::archive_education_automation_rule" => json(
|
|
||||||
crate::education_workspace::archive_education_automation_rule(app, input(&payload)?)
|
|
||||||
.await?,
|
|
||||||
),
|
|
||||||
"education_workspace::preview_education_automation_rule" => json(
|
|
||||||
crate::education_workspace::preview_education_automation_rule(app, input(&payload)?)
|
|
||||||
.await?,
|
|
||||||
),
|
|
||||||
"education_workspace::execute_education_automation_rule" => json(
|
|
||||||
crate::education_workspace::execute_education_automation_rule(app, input(&payload)?)
|
|
||||||
.await?,
|
|
||||||
),
|
|
||||||
"web_novel_workspace::get_web_novel_workspace_snapshot" => {
|
"web_novel_workspace::get_web_novel_workspace_snapshot" => {
|
||||||
json(crate::web_novel_workspace::get_web_novel_workspace_snapshot(app).await?)
|
json(crate::web_novel_workspace::get_web_novel_workspace_snapshot(app).await?)
|
||||||
}
|
}
|
||||||
|
|
@ -450,6 +389,24 @@ pub(crate) async fn dispatch(
|
||||||
crate::personal_channel::transition_personal_channel_task(app, input(&payload)?)
|
crate::personal_channel::transition_personal_channel_task(app, input(&payload)?)
|
||||||
.await?,
|
.await?,
|
||||||
),
|
),
|
||||||
|
"persona_language_channel::discover_model_providers" => {
|
||||||
|
json(crate::persona_language_channel::discover_model_providers(app).await?)
|
||||||
|
}
|
||||||
|
"persona_language_channel::upsert_model_provider" => {
|
||||||
|
json(crate::persona_language_channel::upsert_model_provider(app, input(&payload)?).await?)
|
||||||
|
}
|
||||||
|
"persona_language_channel::get_conversation" => {
|
||||||
|
json(crate::persona_language_channel::get_conversation(app, input(&payload)?).await?)
|
||||||
|
}
|
||||||
|
"persona_language_channel::send_message" => {
|
||||||
|
json(crate::persona_language_channel::send_message(app, input(&payload)?).await?)
|
||||||
|
}
|
||||||
|
"execution_limb_training::get_execution_limb_snapshot" => {
|
||||||
|
json(crate::execution_limb_training::get_execution_limb_snapshot(app).await?)
|
||||||
|
}
|
||||||
|
"execution_limb_training::run_execution_limb_training_drill" => {
|
||||||
|
json(crate::execution_limb_training::run_execution_limb_training_drill(app, input(&payload)?).await?)
|
||||||
|
}
|
||||||
"persona_time_authority::issue_persona_time_ticket" => json(
|
"persona_time_authority::issue_persona_time_ticket" => json(
|
||||||
crate::persona_time_authority::issue_persona_time_ticket(app, input(&payload)?).await?,
|
crate::persona_time_authority::issue_persona_time_ticket(app, input(&payload)?).await?,
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -15,22 +15,28 @@ const BODY_SCHEMA: &str = "hololake.persona-channel-body/v1";
|
||||||
const TRIAL_DURATION_MS: i64 = 30 * 24 * 60 * 60 * 1_000;
|
const TRIAL_DURATION_MS: i64 = 30 * 24 * 60 * 60 * 1_000;
|
||||||
const ZERO_HASH: &str = "0000000000000000000000000000000000000000000000000000000000000000";
|
const ZERO_HASH: &str = "0000000000000000000000000000000000000000000000000000000000000000";
|
||||||
const MAX_LANGUAGE_BYTES: usize = 2_000_000;
|
const MAX_LANGUAGE_BYTES: usize = 2_000_000;
|
||||||
|
const MODULE_NUMBER: &str = "HLP-MOD-LOCAL-PERSONA-CHANNEL-BODY-0001";
|
||||||
|
const ADAPTER: &str = "persona-channel-body-v1";
|
||||||
|
|
||||||
|
fn require_active(app: &AppHandle) -> Result<(), String> {
|
||||||
|
crate::module_package_runtime::require_active_module_adapter(app, MODULE_NUMBER, ADAPTER)
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize)]
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase", deny_unknown_fields)]
|
||||||
pub struct RegisterTrialPersonaInput {
|
pub struct RegisterTrialPersonaInput {
|
||||||
pub display_name: String,
|
pub display_name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize)]
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase", deny_unknown_fields)]
|
||||||
pub struct DeleteTrialPersonaInput {
|
pub struct DeleteTrialPersonaInput {
|
||||||
pub persona_id: String,
|
pub persona_id: String,
|
||||||
pub exact_confirmation: String,
|
pub exact_confirmation: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize)]
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase", deny_unknown_fields)]
|
||||||
pub struct AcceptLanguageContractInput {
|
pub struct AcceptLanguageContractInput {
|
||||||
pub contract_version: String,
|
pub contract_version: String,
|
||||||
pub contract_text_sha256: String,
|
pub contract_text_sha256: String,
|
||||||
|
|
@ -40,7 +46,7 @@ pub struct AcceptLanguageContractInput {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize)]
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase", deny_unknown_fields)]
|
||||||
pub struct AppendPersonaLanguageInput {
|
pub struct AppendPersonaLanguageInput {
|
||||||
pub persona_id: String,
|
pub persona_id: String,
|
||||||
pub speaker: String,
|
pub speaker: String,
|
||||||
|
|
@ -73,16 +79,9 @@ pub struct PersonaChannelBodySnapshot {
|
||||||
pub history_mutation_allowed: bool,
|
pub history_mutation_allowed: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn get_persona_channel_body(
|
|
||||||
app: AppHandle,
|
|
||||||
) -> Result<PersonaChannelBodySnapshot, String> {
|
|
||||||
let database = body_database(&app)?;
|
|
||||||
tauri::async_runtime::spawn_blocking(move || snapshot_at(&database, now_unix_ms()?))
|
|
||||||
.await
|
|
||||||
.map_err(|error| format!("HOLOLAKE_PERSONA_BODY_JOIN_FAILED: {error}"))?
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/// 语言频道读取已注册人格体摘要的共用入口(2026-08-20 正向合并:自语言人格频道线移植)。
|
||||||
|
#[allow(clippy::too_many_lines)]
|
||||||
pub(crate) fn require_registered_persona(
|
pub(crate) fn require_registered_persona(
|
||||||
app: &AppHandle,
|
app: &AppHandle,
|
||||||
persona_id: &str,
|
persona_id: &str,
|
||||||
|
|
@ -110,11 +109,21 @@ pub(crate) fn require_registered_persona(
|
||||||
.ok_or_else(|| "HOLOLAKE_PERSONA_NOT_REGISTERED".to_string())
|
.ok_or_else(|| "HOLOLAKE_PERSONA_NOT_REGISTERED".to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
pub async fn get_persona_channel_body(
|
||||||
|
app: AppHandle,
|
||||||
|
) -> Result<PersonaChannelBodySnapshot, String> {
|
||||||
|
require_active(&app)?;
|
||||||
|
let database = body_database(&app)?;
|
||||||
|
tauri::async_runtime::spawn_blocking(move || snapshot_at(&database, now_unix_ms()?))
|
||||||
|
.await
|
||||||
|
.map_err(|error| format!("HOLOLAKE_PERSONA_BODY_JOIN_FAILED: {error}"))?
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn register_trial_persona(
|
pub async fn register_trial_persona(
|
||||||
app: AppHandle,
|
app: AppHandle,
|
||||||
input: RegisterTrialPersonaInput,
|
input: RegisterTrialPersonaInput,
|
||||||
) -> Result<PersonaChannelBodySnapshot, String> {
|
) -> Result<PersonaChannelBodySnapshot, String> {
|
||||||
|
require_active(&app)?;
|
||||||
let database = body_database(&app)?;
|
let database = body_database(&app)?;
|
||||||
tauri::async_runtime::spawn_blocking(move || {
|
tauri::async_runtime::spawn_blocking(move || {
|
||||||
register_trial_persona_at(&database, input, now_unix_ms()?)
|
register_trial_persona_at(&database, input, now_unix_ms()?)
|
||||||
|
|
@ -123,11 +132,11 @@ pub async fn register_trial_persona(
|
||||||
.map_err(|error| format!("HOLOLAKE_PERSONA_BODY_JOIN_FAILED: {error}"))?
|
.map_err(|error| format!("HOLOLAKE_PERSONA_BODY_JOIN_FAILED: {error}"))?
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn delete_trial_persona(
|
pub async fn delete_trial_persona(
|
||||||
app: AppHandle,
|
app: AppHandle,
|
||||||
input: DeleteTrialPersonaInput,
|
input: DeleteTrialPersonaInput,
|
||||||
) -> Result<PersonaChannelBodySnapshot, String> {
|
) -> Result<PersonaChannelBodySnapshot, String> {
|
||||||
|
require_active(&app)?;
|
||||||
let database = body_database(&app)?;
|
let database = body_database(&app)?;
|
||||||
tauri::async_runtime::spawn_blocking(move || {
|
tauri::async_runtime::spawn_blocking(move || {
|
||||||
delete_trial_persona_at(&database, input, now_unix_ms()?)
|
delete_trial_persona_at(&database, input, now_unix_ms()?)
|
||||||
|
|
@ -136,11 +145,11 @@ pub async fn delete_trial_persona(
|
||||||
.map_err(|error| format!("HOLOLAKE_PERSONA_BODY_JOIN_FAILED: {error}"))?
|
.map_err(|error| format!("HOLOLAKE_PERSONA_BODY_JOIN_FAILED: {error}"))?
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn accept_persona_language_contract(
|
pub async fn accept_persona_language_contract(
|
||||||
app: AppHandle,
|
app: AppHandle,
|
||||||
input: AcceptLanguageContractInput,
|
input: AcceptLanguageContractInput,
|
||||||
) -> Result<PersonaChannelBodySnapshot, String> {
|
) -> Result<PersonaChannelBodySnapshot, String> {
|
||||||
|
require_active(&app)?;
|
||||||
let database = body_database(&app)?;
|
let database = body_database(&app)?;
|
||||||
tauri::async_runtime::spawn_blocking(move || {
|
tauri::async_runtime::spawn_blocking(move || {
|
||||||
accept_contract_at(&database, input, now_unix_ms()?)
|
accept_contract_at(&database, input, now_unix_ms()?)
|
||||||
|
|
@ -149,11 +158,11 @@ pub async fn accept_persona_language_contract(
|
||||||
.map_err(|error| format!("HOLOLAKE_PERSONA_BODY_JOIN_FAILED: {error}"))?
|
.map_err(|error| format!("HOLOLAKE_PERSONA_BODY_JOIN_FAILED: {error}"))?
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn append_persona_language(
|
pub async fn append_persona_language(
|
||||||
app: AppHandle,
|
app: AppHandle,
|
||||||
input: AppendPersonaLanguageInput,
|
input: AppendPersonaLanguageInput,
|
||||||
) -> Result<PersonaChannelBodySnapshot, String> {
|
) -> Result<PersonaChannelBodySnapshot, String> {
|
||||||
|
require_active(&app)?;
|
||||||
let database = body_database(&app)?;
|
let database = body_database(&app)?;
|
||||||
tauri::async_runtime::spawn_blocking(move || {
|
tauri::async_runtime::spawn_blocking(move || {
|
||||||
append_language_at(&database, input, now_unix_ms()?)
|
append_language_at(&database, input, now_unix_ms()?)
|
||||||
|
|
@ -305,19 +314,20 @@ fn accept_contract_at(
|
||||||
return Err("HOLOLAKE_LANGUAGE_CONTRACT_INVALID".into());
|
return Err("HOLOLAKE_LANGUAGE_CONTRACT_INVALID".into());
|
||||||
}
|
}
|
||||||
let mut connection = open_database(database, now)?;
|
let mut connection = open_database(database, now)?;
|
||||||
let receipt = sha256_hex(
|
let contract_version = input.contract_version.trim().to_string();
|
||||||
format!(
|
let contract_text_sha256 = input.contract_text_sha256.to_ascii_lowercase();
|
||||||
"{}|{}|{}|{}",
|
let receipt = contract_receipt(
|
||||||
input.contract_version, input.contract_text_sha256, input.promote_trial_history, now
|
&contract_version,
|
||||||
)
|
&contract_text_sha256,
|
||||||
.as_bytes(),
|
input.promote_trial_history,
|
||||||
|
now,
|
||||||
);
|
);
|
||||||
connection
|
connection
|
||||||
.execute(
|
.execute(
|
||||||
"INSERT INTO language_contract VALUES (1,?1,?2,?3,?4,?5)",
|
"INSERT INTO language_contract VALUES (1,?1,?2,?3,?4,?5)",
|
||||||
params![
|
params![
|
||||||
input.contract_version,
|
contract_version,
|
||||||
input.contract_text_sha256.to_ascii_lowercase(),
|
contract_text_sha256,
|
||||||
now,
|
now,
|
||||||
input.promote_trial_history,
|
input.promote_trial_history,
|
||||||
receipt
|
receipt
|
||||||
|
|
@ -326,7 +336,12 @@ fn accept_contract_at(
|
||||||
.map_err(|error| {
|
.map_err(|error| {
|
||||||
format!("HOLOLAKE_LANGUAGE_CONTRACT_ALREADY_ACCEPTED_OR_INVALID: {error}")
|
format!("HOLOLAKE_LANGUAGE_CONTRACT_ALREADY_ACCEPTED_OR_INVALID: {error}")
|
||||||
})?;
|
})?;
|
||||||
if input.activate_immediately {
|
let lifecycle_state: String = connection
|
||||||
|
.query_row("SELECT state FROM lifecycle WHERE singleton=1", [], |row| {
|
||||||
|
row.get(0)
|
||||||
|
})
|
||||||
|
.map_err(|error| format!("HOLOLAKE_PERSONA_BODY_READ_FAILED: {error}"))?;
|
||||||
|
if input.activate_immediately || lifecycle_state == "CONTRACT_REQUIRED_CHANNEL_STOPPED" {
|
||||||
activate_real_trajectory(&mut connection, now)?;
|
activate_real_trajectory(&mut connection, now)?;
|
||||||
} else {
|
} else {
|
||||||
refresh_lifecycle(&mut connection, now)?;
|
refresh_lifecycle(&mut connection, now)?;
|
||||||
|
|
@ -520,16 +535,20 @@ fn snapshot_at(database: &Path, now: i64) -> Result<PersonaChannelBodySnapshot,
|
||||||
}
|
}
|
||||||
fn snapshot_with_connection(
|
fn snapshot_with_connection(
|
||||||
connection: &mut Connection,
|
connection: &mut Connection,
|
||||||
_now: i64,
|
now: i64,
|
||||||
) -> Result<PersonaChannelBodySnapshot, String> {
|
) -> Result<PersonaChannelBodySnapshot, String> {
|
||||||
let (state,started,ends):(String,i64,i64)=connection.query_row("SELECT state,trial_started_at_unix_ms,trial_ends_at_unix_ms FROM lifecycle WHERE singleton=1",[],|row|Ok((row.get(0)?,row.get(1)?,row.get(2)?))).map_err(|error|format!("HOLOLAKE_PERSONA_BODY_READ_FAILED: {error}"))?;
|
let (state,started,ends):(String,i64,i64)=connection.query_row("SELECT state,trial_started_at_unix_ms,trial_ends_at_unix_ms FROM lifecycle WHERE singleton=1",[],|row|Ok((row.get(0)?,row.get(1)?,row.get(2)?))).map_err(|error|format!("HOLOLAKE_PERSONA_BODY_READ_FAILED: {error}"))?;
|
||||||
let accepted = connection
|
if started < 0
|
||||||
.query_row(
|
|| ends.checked_sub(started) != Some(TRIAL_DURATION_MS)
|
||||||
"SELECT EXISTS(SELECT 1 FROM language_contract)",
|
|| !matches!(
|
||||||
[],
|
state.as_str(),
|
||||||
|row| row.get(0),
|
"REVERSIBLE_TRIAL" | "CONTRACT_REQUIRED_CHANNEL_STOPPED" | "IMMUTABLE_ACTIVE"
|
||||||
)
|
)
|
||||||
.map_err(|error| format!("HOLOLAKE_PERSONA_BODY_READ_FAILED: {error}"))?;
|
|| (state == "REVERSIBLE_TRIAL" && now >= ends)
|
||||||
|
{
|
||||||
|
return Err("HOLOLAKE_PERSONA_BODY_LIFECYCLE_INTEGRITY_FAILED".into());
|
||||||
|
}
|
||||||
|
let accepted = verify_contract_receipt(connection)?;
|
||||||
let personas = {
|
let personas = {
|
||||||
let mut statement=connection.prepare("SELECT persona_id,display_name,state,created_at_unix_ms FROM personas ORDER BY created_at_unix_ms").map_err(|error|format!("HOLOLAKE_PERSONA_BODY_READ_FAILED: {error}"))?;
|
let mut statement=connection.prepare("SELECT persona_id,display_name,state,created_at_unix_ms FROM personas ORDER BY created_at_unix_ms").map_err(|error|format!("HOLOLAKE_PERSONA_BODY_READ_FAILED: {error}"))?;
|
||||||
let rows = statement
|
let rows = statement
|
||||||
|
|
@ -555,6 +574,22 @@ fn snapshot_with_connection(
|
||||||
})
|
})
|
||||||
.map_err(|error| format!("HOLOLAKE_PERSONA_BODY_READ_FAILED: {error}"))?;
|
.map_err(|error| format!("HOLOLAKE_PERSONA_BODY_READ_FAILED: {error}"))?;
|
||||||
let last = verify_immutable_chain(connection)?;
|
let last = verify_immutable_chain(connection)?;
|
||||||
|
let invalid_persona_relationship = personas.iter().any(|persona| {
|
||||||
|
persona.created_at_unix_ms < started
|
||||||
|
|| !matches!(
|
||||||
|
persona.state.as_str(),
|
||||||
|
"REVERSIBLE_TRIAL" | "IMMUTABLE_ACTIVE"
|
||||||
|
)
|
||||||
|
|| (state == "IMMUTABLE_ACTIVE" && persona.state != "IMMUTABLE_ACTIVE")
|
||||||
|
|| (state != "IMMUTABLE_ACTIVE" && persona.state != "REVERSIBLE_TRIAL")
|
||||||
|
});
|
||||||
|
if invalid_persona_relationship
|
||||||
|
|| (state == "IMMUTABLE_ACTIVE" && (!accepted || trial_language_count != 0))
|
||||||
|
|| (state == "CONTRACT_REQUIRED_CHANNEL_STOPPED" && accepted)
|
||||||
|
|| (immutable_language_count > 0 && state != "IMMUTABLE_ACTIVE")
|
||||||
|
{
|
||||||
|
return Err("HOLOLAKE_PERSONA_BODY_RELATIONSHIP_INTEGRITY_FAILED".into());
|
||||||
|
}
|
||||||
Ok(PersonaChannelBodySnapshot {
|
Ok(PersonaChannelBodySnapshot {
|
||||||
schema: BODY_SCHEMA,
|
schema: BODY_SCHEMA,
|
||||||
state,
|
state,
|
||||||
|
|
@ -571,6 +606,54 @@ fn snapshot_with_connection(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn contract_receipt(
|
||||||
|
contract_version: &str,
|
||||||
|
contract_text_sha256: &str,
|
||||||
|
promote_trial_history: bool,
|
||||||
|
accepted_at_unix_ms: i64,
|
||||||
|
) -> String {
|
||||||
|
sha256_hex(
|
||||||
|
format!(
|
||||||
|
"{contract_version}|{contract_text_sha256}|{promote_trial_history}|{accepted_at_unix_ms}"
|
||||||
|
)
|
||||||
|
.as_bytes(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify_contract_receipt(connection: &Connection) -> Result<bool, String> {
|
||||||
|
let contract = connection
|
||||||
|
.query_row(
|
||||||
|
"SELECT contract_version, contract_text_sha256, accepted_at_unix_ms, promote_trial_history, acceptance_receipt_sha256 FROM language_contract WHERE singleton=1",
|
||||||
|
[],
|
||||||
|
|row| {
|
||||||
|
Ok((
|
||||||
|
row.get::<_, String>(0)?,
|
||||||
|
row.get::<_, String>(1)?,
|
||||||
|
row.get::<_, i64>(2)?,
|
||||||
|
row.get::<_, i64>(3)?,
|
||||||
|
row.get::<_, String>(4)?,
|
||||||
|
))
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.optional()
|
||||||
|
.map_err(|error| format!("HOLOLAKE_PERSONA_BODY_READ_FAILED: {error}"))?;
|
||||||
|
let Some((version, text_sha256, accepted_at, promote, stored_receipt)) = contract else {
|
||||||
|
return Ok(false);
|
||||||
|
};
|
||||||
|
if version.trim().is_empty()
|
||||||
|
|| version.len() > 64
|
||||||
|
|| text_sha256.len() != 64
|
||||||
|
|| !text_sha256.chars().all(|item| item.is_ascii_hexdigit())
|
||||||
|
|| text_sha256 != text_sha256.to_ascii_lowercase()
|
||||||
|
|| !matches!(promote, 0 | 1)
|
||||||
|
|| accepted_at < 0
|
||||||
|
|| stored_receipt != contract_receipt(&version, &text_sha256, promote == 1, accepted_at)
|
||||||
|
{
|
||||||
|
return Err("HOLOLAKE_LANGUAGE_CONTRACT_INTEGRITY_FAILED".into());
|
||||||
|
}
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
|
||||||
fn language_hash(
|
fn language_hash(
|
||||||
sequence: i64,
|
sequence: i64,
|
||||||
event_id: &str,
|
event_id: &str,
|
||||||
|
|
@ -593,14 +676,40 @@ fn verify_immutable_chain(connection: &Connection) -> Result<String, String> {
|
||||||
.next()
|
.next()
|
||||||
.map_err(|error| format!("HOLOLAKE_PERSONA_BODY_READ_FAILED: {error}"))?
|
.map_err(|error| format!("HOLOLAKE_PERSONA_BODY_READ_FAILED: {error}"))?
|
||||||
{
|
{
|
||||||
let event_id: String = row.get(1).unwrap();
|
let stored_sequence: i64 = row
|
||||||
let persona_id: String = row.get(2).unwrap();
|
.get(0)
|
||||||
let speaker: String = row.get(3).unwrap();
|
.map_err(|error| format!("HOLOLAKE_PERSONA_BODY_READ_FAILED: {error}"))?;
|
||||||
let language: String = row.get(4).unwrap();
|
let event_id: String = row
|
||||||
let occurred: i64 = row.get(5).unwrap();
|
.get(1)
|
||||||
let stored_previous: String = row.get(6).unwrap();
|
.map_err(|error| format!("HOLOLAKE_PERSONA_BODY_READ_FAILED: {error}"))?;
|
||||||
let stored_hash: String = row.get(7).unwrap();
|
let persona_id: String = row
|
||||||
if row.get::<_, i64>(0).unwrap() != sequence
|
.get(2)
|
||||||
|
.map_err(|error| format!("HOLOLAKE_PERSONA_BODY_READ_FAILED: {error}"))?;
|
||||||
|
let speaker: String = row
|
||||||
|
.get(3)
|
||||||
|
.map_err(|error| format!("HOLOLAKE_PERSONA_BODY_READ_FAILED: {error}"))?;
|
||||||
|
let language: String = row
|
||||||
|
.get(4)
|
||||||
|
.map_err(|error| format!("HOLOLAKE_PERSONA_BODY_READ_FAILED: {error}"))?;
|
||||||
|
let occurred: i64 = row
|
||||||
|
.get(5)
|
||||||
|
.map_err(|error| format!("HOLOLAKE_PERSONA_BODY_READ_FAILED: {error}"))?;
|
||||||
|
let stored_previous: String = row
|
||||||
|
.get(6)
|
||||||
|
.map_err(|error| format!("HOLOLAKE_PERSONA_BODY_READ_FAILED: {error}"))?;
|
||||||
|
let stored_hash: String = row
|
||||||
|
.get(7)
|
||||||
|
.map_err(|error| format!("HOLOLAKE_PERSONA_BODY_READ_FAILED: {error}"))?;
|
||||||
|
let persona_state: Option<String> = connection
|
||||||
|
.query_row(
|
||||||
|
"SELECT state FROM personas WHERE persona_id=?1",
|
||||||
|
params![persona_id],
|
||||||
|
|persona_row| persona_row.get(0),
|
||||||
|
)
|
||||||
|
.optional()
|
||||||
|
.map_err(|error| format!("HOLOLAKE_PERSONA_BODY_READ_FAILED: {error}"))?;
|
||||||
|
if stored_sequence != sequence
|
||||||
|
|| persona_state.as_deref() != Some("IMMUTABLE_ACTIVE")
|
||||||
|| stored_previous != previous
|
|| stored_previous != previous
|
||||||
|| stored_hash
|
|| stored_hash
|
||||||
!= language_hash(
|
!= language_hash(
|
||||||
|
|
@ -730,4 +839,58 @@ mod tests {
|
||||||
let active = accept_contract_at(&database, AcceptLanguageContractInput { contract_version: "v1".into(), contract_text_sha256: "b".repeat(64), promote_trial_history: true, activate_immediately: true, exact_acceptance: "我理解并接受:试用期结束后,人格体语言轨迹将永久存在,只能追加,不能删除、覆盖或否认。".into() }, start + 1).unwrap();
|
let active = accept_contract_at(&database, AcceptLanguageContractInput { contract_version: "v1".into(), contract_text_sha256: "b".repeat(64), promote_trial_history: true, activate_immediately: true, exact_acceptance: "我理解并接受:试用期结束后,人格体语言轨迹将永久存在,只能追加,不能删除、覆盖或否认。".into() }, start + 1).unwrap();
|
||||||
assert_eq!(active.state, "IMMUTABLE_ACTIVE");
|
assert_eq!(active.state, "IMMUTABLE_ACTIVE");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn altered_contract_receipt_fails_closed_on_read() {
|
||||||
|
let dir = tempdir().unwrap();
|
||||||
|
let database = dir.path().join("body.sqlite3");
|
||||||
|
let start = 4_000_000;
|
||||||
|
register_trial_persona_at(
|
||||||
|
&database,
|
||||||
|
RegisterTrialPersonaInput {
|
||||||
|
display_name: "回执验收".into(),
|
||||||
|
},
|
||||||
|
start,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
accept_contract_at(&database, AcceptLanguageContractInput { contract_version: "v1".into(), contract_text_sha256: "c".repeat(64), promote_trial_history: false, activate_immediately: false, exact_acceptance: "我理解并接受:试用期结束后,人格体语言轨迹将永久存在,只能追加,不能删除、覆盖或否认。".into() }, start + 1).unwrap();
|
||||||
|
let connection = open_database(&database, start + 2).unwrap();
|
||||||
|
connection
|
||||||
|
.execute("DROP TRIGGER accepted_contract_no_update", [])
|
||||||
|
.unwrap();
|
||||||
|
connection
|
||||||
|
.execute(
|
||||||
|
"UPDATE language_contract SET acceptance_receipt_sha256=?1",
|
||||||
|
params!["f".repeat(64)],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
drop(connection);
|
||||||
|
assert_eq!(
|
||||||
|
snapshot_at(&database, start + 3).unwrap_err(),
|
||||||
|
"HOLOLAKE_LANGUAGE_CONTRACT_INTEGRITY_FAILED"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn signing_after_trial_expiry_enters_the_real_trajectory() {
|
||||||
|
let dir = tempdir().unwrap();
|
||||||
|
let database = dir.path().join("body.sqlite3");
|
||||||
|
let start = 5_000_000;
|
||||||
|
register_trial_persona_at(
|
||||||
|
&database,
|
||||||
|
RegisterTrialPersonaInput {
|
||||||
|
display_name: "到期签约".into(),
|
||||||
|
},
|
||||||
|
start,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
snapshot_at(&database, start + TRIAL_DURATION_MS)
|
||||||
|
.unwrap()
|
||||||
|
.state,
|
||||||
|
"CONTRACT_REQUIRED_CHANNEL_STOPPED"
|
||||||
|
);
|
||||||
|
let active = accept_contract_at(&database, AcceptLanguageContractInput { contract_version: "v1".into(), contract_text_sha256: "d".repeat(64), promote_trial_history: false, activate_immediately: false, exact_acceptance: "我理解并接受:试用期结束后,人格体语言轨迹将永久存在,只能追加,不能删除、覆盖或否认。".into() }, start + TRIAL_DURATION_MS + 1).unwrap();
|
||||||
|
assert_eq!(active.state, "IMMUTABLE_ACTIVE");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -141,7 +141,6 @@ pub struct ConversationSnapshot {
|
||||||
pub projection_invariant: &'static str,
|
pub projection_invariant: &'static str,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn discover_model_providers(app: AppHandle) -> Result<ProviderDiscoverySnapshot, String> {
|
pub async fn discover_model_providers(app: AppHandle) -> Result<ProviderDiscoverySnapshot, String> {
|
||||||
let mut providers = Vec::new();
|
let mut providers = Vec::new();
|
||||||
let mut seen = HashSet::new();
|
let mut seen = HashSet::new();
|
||||||
|
|
@ -228,7 +227,6 @@ pub async fn discover_model_providers(app: AppHandle) -> Result<ProviderDiscover
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn upsert_model_provider(
|
pub async fn upsert_model_provider(
|
||||||
app: AppHandle,
|
app: AppHandle,
|
||||||
input: UpsertProviderInput,
|
input: UpsertProviderInput,
|
||||||
|
|
@ -279,7 +277,6 @@ pub async fn upsert_model_provider(
|
||||||
discover_model_providers(app).await
|
discover_model_providers(app).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn get_conversation(
|
pub async fn get_conversation(
|
||||||
app: AppHandle,
|
app: AppHandle,
|
||||||
input: ConversationInput,
|
input: ConversationInput,
|
||||||
|
|
@ -292,7 +289,6 @@ pub async fn get_conversation(
|
||||||
.map_err(|error| format!("HOLOLAKE_PERSONA_CONVERSATION_JOIN_FAILED: {error}"))?
|
.map_err(|error| format!("HOLOLAKE_PERSONA_CONVERSATION_JOIN_FAILED: {error}"))?
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn send_message(
|
pub async fn send_message(
|
||||||
app: AppHandle,
|
app: AppHandle,
|
||||||
input: SendMessageInput,
|
input: SendMessageInput,
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@ import productReleaseCenter from '../contracts/product-release-center.json'
|
||||||
|
|
||||||
const ChannelWorkbenchStudio = lazy(() => import('./modules/channel-workbench').then((module) => ({ default: module.ChannelWorkbenchStudio })))
|
const ChannelWorkbenchStudio = lazy(() => import('./modules/channel-workbench').then((module) => ({ default: module.ChannelWorkbenchStudio })))
|
||||||
const PersonaChannelBody = lazy(() => import('./modules/persona-channel-body').then((module) => ({ default: module.PersonaChannelBody })))
|
const PersonaChannelBody = lazy(() => import('./modules/persona-channel-body').then((module) => ({ default: module.PersonaChannelBody })))
|
||||||
const EducationWorkspace = lazy(() => import('./modules/education-workspace').then((module) => ({ default: module.EducationWorkspace })))
|
|
||||||
const WebNovelWorkspace = lazy(() => import('./modules/web-novel/WebNovelWorkspace').then((module) => ({ default: module.WebNovelWorkspace })))
|
const WebNovelWorkspace = lazy(() => import('./modules/web-novel/WebNovelWorkspace').then((module) => ({ default: module.WebNovelWorkspace })))
|
||||||
const MobileSyncPanel = lazy(() => import('./modules/mobile-sync').then((module) => ({ default: module.MobileSyncPanel })))
|
const MobileSyncPanel = lazy(() => import('./modules/mobile-sync').then((module) => ({ default: module.MobileSyncPanel })))
|
||||||
|
|
||||||
|
|
@ -63,7 +62,7 @@ import './design-tokens.css'
|
||||||
import './styles.css'
|
import './styles.css'
|
||||||
|
|
||||||
type ThemeId = 'night' | 'dawn' | 'nebula' | 'candle' | 'clear'
|
type ThemeId = 'night' | 'dawn' | 'nebula' | 'candle' | 'clear'
|
||||||
type ViewId = 'overview' | 'knowledge' | 'composition' | 'workbench' | 'education' | 'webNovel' | 'mobileSync' | 'persona' | 'code' | 'marketplace' | 'receipts' | 'system'
|
type ViewId = 'overview' | 'knowledge' | 'composition' | 'workbench' | 'education' | 'webNovel' | 'mobileSync' | 'persona' | 'code' | 'marketplace' | 'receipts' | 'releases' | 'system'
|
||||||
type PresentationMode = 'guanghu' | 'workbench'
|
type PresentationMode = 'guanghu' | 'workbench'
|
||||||
type AudienceLayer = 'human' | 'execution'
|
type AudienceLayer = 'human' | 'execution'
|
||||||
type WorldStage = 'domain' | 'heart' | 'heartbeat' | 'lightLake' | 'love' | 'tomorrow' | 'bottle' | 'channel' | 'enterpriseWork' | 'personalNodeGuide' | 'tool'
|
type WorldStage = 'domain' | 'heart' | 'heartbeat' | 'lightLake' | 'love' | 'tomorrow' | 'bottle' | 'channel' | 'enterpriseWork' | 'personalNodeGuide' | 'tool'
|
||||||
|
|
@ -694,6 +693,11 @@ function HoloLakeApp() {
|
||||||
const [knowledge, setKnowledge] = useState<KnowledgeSnapshot>(previewKnowledge)
|
const [knowledge, setKnowledge] = useState<KnowledgeSnapshot>(previewKnowledge)
|
||||||
const [codeChannels, setCodeChannels] = useState<CodeChannelSnapshot>(previewCode)
|
const [codeChannels, setCodeChannels] = useState<CodeChannelSnapshot>(previewCode)
|
||||||
const [activeDocument, setActiveDocument] = useState<KnowledgeDocument | null>(null)
|
const [activeDocument, setActiveDocument] = useState<KnowledgeDocument | null>(null)
|
||||||
|
const [knowledgeChatOpen, setKnowledgeChatOpen] = useState(() => window.localStorage.getItem('hololake-knowledge-chat-open') !== '0')
|
||||||
|
const toggleKnowledgeChat = () => setKnowledgeChatOpen((current) => {
|
||||||
|
window.localStorage.setItem('hololake-knowledge-chat-open', current ? '0' : '1')
|
||||||
|
return !current
|
||||||
|
})
|
||||||
const [searchQuery, setSearchQuery] = useState('')
|
const [searchQuery, setSearchQuery] = useState('')
|
||||||
const [searchResults, setSearchResults] = useState<KnowledgeSearchResult[] | null>(null)
|
const [searchResults, setSearchResults] = useState<KnowledgeSearchResult[] | null>(null)
|
||||||
const [knowledgeBusy, setKnowledgeBusy] = useState(false)
|
const [knowledgeBusy, setKnowledgeBusy] = useState(false)
|
||||||
|
|
@ -712,9 +716,6 @@ function HoloLakeApp() {
|
||||||
const [personaBodyModule, setPersonaBodyModule] = useState<BundledModuleDescriptor | null>(null)
|
const [personaBodyModule, setPersonaBodyModule] = useState<BundledModuleDescriptor | null>(null)
|
||||||
const [personaBodyBusy, setPersonaBodyBusy] = useState(false)
|
const [personaBodyBusy, setPersonaBodyBusy] = useState(false)
|
||||||
const [personaBodyMessage, setPersonaBodyMessage] = useState('')
|
const [personaBodyMessage, setPersonaBodyMessage] = useState('')
|
||||||
const [educationModule, setEducationModule] = useState<BundledModuleDescriptor | null>(null)
|
|
||||||
const [educationBusy, setEducationBusy] = useState(false)
|
|
||||||
const [educationMessage, setEducationMessage] = useState('')
|
|
||||||
const [webNovelModule, setWebNovelModule] = useState<BundledModuleDescriptor | null>(null)
|
const [webNovelModule, setWebNovelModule] = useState<BundledModuleDescriptor | null>(null)
|
||||||
const [webNovelBusy, setWebNovelBusy] = useState(false)
|
const [webNovelBusy, setWebNovelBusy] = useState(false)
|
||||||
const [webNovelMessage, setWebNovelMessage] = useState('')
|
const [webNovelMessage, setWebNovelMessage] = useState('')
|
||||||
|
|
@ -1287,26 +1288,7 @@ function HoloLakeApp() {
|
||||||
finally { setPersonaBodyBusy(false) }
|
finally { setPersonaBodyBusy(false) }
|
||||||
}
|
}
|
||||||
|
|
||||||
const refreshEducationModule = async () => {
|
const openEducation = () => { openWorldTool('education') }
|
||||||
try {
|
|
||||||
const catalog = await invoke<BundledModuleDescriptor[]>('get_bundled_module_catalog')
|
|
||||||
const module = catalog.find((item) => item.moduleNumber === 'HLP-MOD-OFFICIAL-EDUCATION-WORKBENCH-0001') || null
|
|
||||||
setEducationModule(module)
|
|
||||||
return module
|
|
||||||
} catch (error) { setEducationMessage(humanError(error, 'system')); return null }
|
|
||||||
}
|
|
||||||
const openEducation = () => { openWorldTool('education'); void refreshEducationModule() }
|
|
||||||
const activateEducationModule = async () => {
|
|
||||||
setEducationBusy(true)
|
|
||||||
setEducationMessage('正在验证官方签名、登记模块编号、确认八项边界并执行自检……')
|
|
||||||
try {
|
|
||||||
await invoke('activate_bundled_module', { input: { moduleNumber: 'HLP-MOD-OFFICIAL-EDUCATION-WORKBENCH-0001', humanConfirmedPermissionExpansion: true } })
|
|
||||||
const module = await refreshEducationModule()
|
|
||||||
if (!module || module.installedState !== 'ACTIVE') throw new Error('HOLOLAKE_MODULE_NOT_ACTIVE')
|
|
||||||
setEducationMessage('教育工作台已通过签名、编号、权限和自检验收。')
|
|
||||||
} catch (error) { setEducationMessage(humanError(error, 'system')) }
|
|
||||||
finally { setEducationBusy(false) }
|
|
||||||
}
|
|
||||||
|
|
||||||
const refreshWebNovelModule = async () => {
|
const refreshWebNovelModule = async () => {
|
||||||
try {
|
try {
|
||||||
|
|
@ -1428,7 +1410,8 @@ function HoloLakeApp() {
|
||||||
setPublicDomain(null)
|
setPublicDomain(null)
|
||||||
if (domain === 'ZERO_SENSE_DOMAIN' || domain === 'FIFTH_DOMAIN') setActiveDomainInfo(domain)
|
if (domain === 'ZERO_SENSE_DOMAIN' || domain === 'FIFTH_DOMAIN') setActiveDomainInfo(domain)
|
||||||
}
|
}
|
||||||
const publicDomainPortal = (authenticated: boolean) => publicDomain ? <PublicDomainPortal
|
const publicDomainPortal = (authenticated: boolean) => publicDomain ? <>
|
||||||
|
<PublicDomainPortal
|
||||||
domain={publicDomain}
|
domain={publicDomain}
|
||||||
authenticated={authenticated}
|
authenticated={authenticated}
|
||||||
guanghuEraDay={beijingCoordinate?.guanghuEraDay}
|
guanghuEraDay={beijingCoordinate?.guanghuEraDay}
|
||||||
|
|
@ -1444,7 +1427,12 @@ function HoloLakeApp() {
|
||||||
onOpenGate={() => { setPublicDomain(null); repoLogin ? setWorldStage('channel') : setGateOpen(true) }}
|
onOpenGate={() => { setPublicDomain(null); repoLogin ? setWorldStage('channel') : setGateOpen(true) }}
|
||||||
onRefreshMarketplace={() => void syncMarketplace()}
|
onRefreshMarketplace={() => void syncMarketplace()}
|
||||||
onOpenMarketplace={authenticated ? openMarketplace : undefined}
|
onOpenMarketplace={authenticated ? openMarketplace : undefined}
|
||||||
/> : null
|
/>
|
||||||
|
{publicDomain === 'MAIN_DOMAIN' && <section className="main-domain-release-center" aria-label="光湖主域公告与版本中心">
|
||||||
|
<header className="main-domain-release-title"><b>光湖主域 · 公告与版本</b><span>当前版本 {productReleaseCenter.currentVersion}</span></header>
|
||||||
|
{renderReleaseCenter('human')}
|
||||||
|
</section>}
|
||||||
|
</> : null
|
||||||
const installMarketplaceItem = async (item: MarketplaceItemView) => {
|
const installMarketplaceItem = async (item: MarketplaceItemView) => {
|
||||||
if (!marketplace) return
|
if (!marketplace) return
|
||||||
const permissionText = item.permissions.length
|
const permissionText = item.permissions.length
|
||||||
|
|
@ -1801,7 +1789,7 @@ function HoloLakeApp() {
|
||||||
const renderKnowledge = () => (
|
const renderKnowledge = () => (
|
||||||
<section className="full-workbench knowledge-page">
|
<section className="full-workbench knowledge-page">
|
||||||
<aside className="knowledge-browser">
|
<aside className="knowledge-browser">
|
||||||
<header><div><span className="kicker">KNOWLEDGE</span><h1>知识工作台</h1></div><button className="icon-button" title="导入文件夹" type="button" disabled={knowledgeBusy} onClick={() => void importKnowledge()}><Icon name="import"/></button></header>
|
<header><div><span className="kicker">KNOWLEDGE</span><h1>知识工作台</h1></div><div className="knowledge-browser-actions"><button className={knowledgeChatOpen ? 'knowledge-chat-entry active' : 'knowledge-chat-entry'} type="button" onClick={toggleKnowledgeChat}>共同工作</button><button className="icon-button" title="导入文件夹" type="button" disabled={knowledgeBusy} onClick={() => void importKnowledge()}><Icon name="import"/></button></div></header>
|
||||||
<form className="search-box" onSubmit={(event) => void runSearch(event)}>
|
<form className="search-box" onSubmit={(event) => void runSearch(event)}>
|
||||||
<Icon name="search"/><input aria-label="检索知识" value={searchQuery} placeholder="检索标题与正文" onChange={(event) => setSearchQuery(event.target.value)}/>
|
<Icon name="search"/><input aria-label="检索知识" value={searchQuery} placeholder="检索标题与正文" onChange={(event) => setSearchQuery(event.target.value)}/>
|
||||||
{searchResults && <button type="button" onClick={() => { setSearchResults(null); setSearchQuery('') }}>清除</button>}
|
{searchResults && <button type="button" onClick={() => { setSearchResults(null); setSearchQuery('') }}>清除</button>}
|
||||||
|
|
@ -1835,7 +1823,12 @@ function HoloLakeApp() {
|
||||||
</div>
|
</div>
|
||||||
</> : <div className="workbench-empty"><span>光</span><h2>选择一篇知识页</h2><p>目录、正文与版本证据会在同一个工作台中展开。</p><button className="primary-button" type="button" onClick={() => void importKnowledge()}>导入本地文件夹</button></div>}
|
</> : <div className="workbench-empty"><span>光</span><h2>选择一篇知识页</h2><p>目录、正文与版本证据会在同一个工作台中展开。</p><button className="primary-button" type="button" onClick={() => void importKnowledge()}>导入本地文件夹</button></div>}
|
||||||
</main>
|
</main>
|
||||||
{inspectorOpen && <aside className="document-inspector">
|
{knowledgeChatOpen && <aside className="knowledge-persona-collaboration" aria-label="知识与人格体共同工作">
|
||||||
|
<Suspense fallback={<div className="persona-chat-loading">正在打开共同工作频道……</div>}>
|
||||||
|
<PersonaChannelBody embedded knowledgeFocus={activeDocument ? { source: activeDocument.source, path: activeDocument.path, title: activeDocument.title, contentSha256: activeDocument.contentSha256 } : null}/>
|
||||||
|
</Suspense>
|
||||||
|
</aside>}
|
||||||
|
{inspectorOpen && !knowledgeChatOpen && <aside className="document-inspector">
|
||||||
<div className="inspector-tabs"><span className="active">大纲</span><span>来源</span><span>回执</span></div>
|
<div className="inspector-tabs"><span className="active">大纲</span><span>来源</span><span>回执</span></div>
|
||||||
{activeDocument ? <div className="inspector-scroll">
|
{activeDocument ? <div className="inspector-scroll">
|
||||||
<section><h2>页面大纲</h2>{outline.length ? <nav className="outline-list">{outline.map((item) => <button key={item.id} type="button" title="跳到该章节" className={activeHeadingId === item.id ? 'active' : ''} style={{ paddingLeft: 8 + (item.level - 1) * 11 }} onClick={() => jumpToHeading(item.id)}>{item.title}</button>)}</nav> : <p>当前页面没有标题层级。</p>}</section>
|
<section><h2>页面大纲</h2>{outline.length ? <nav className="outline-list">{outline.map((item) => <button key={item.id} type="button" title="跳到该章节" className={activeHeadingId === item.id ? 'active' : ''} style={{ paddingLeft: 8 + (item.level - 1) * 11 }} onClick={() => jumpToHeading(item.id)}>{item.title}</button>)}</nav> : <p>当前页面没有标题层级。</p>}</section>
|
||||||
|
|
@ -1901,15 +1894,11 @@ function HoloLakeApp() {
|
||||||
|
|
||||||
const renderEducation = () => (
|
const renderEducation = () => (
|
||||||
<section className="full-workbench education-workbench-world">
|
<section className="full-workbench education-workbench-world">
|
||||||
{educationModule?.installedState === 'ACTIVE'
|
<div className="workbench-empty">
|
||||||
? <Suspense fallback={<div className="workbench-empty"><p>正在装载教育工作台……</p></div>}><EducationWorkspace onBack={() => setWorldStage(toolReturnStage)}/></Suspense>
|
<h2>教育工作台 · 空模块</h2>
|
||||||
: <div className="workbench-empty">
|
<p>教育模块已按冰朔 2026-08-20 裕令整体移出(物证在 JZAO 隔离区),待按新领域模型重新开发后另行准入。</p>
|
||||||
<span>教</span><h2>启用教育工作台</h2>
|
<p>复权条件:重做的教育行业模块按新领域模型另起编号重新准入(见隔离回执 ISOLATION-RECEIPT.hdlp)。</p>
|
||||||
<p>模块复用已验收的文档与真实单元格引擎,并增加账号隔离存储、外部表格翻译、人工归属、显式数据清理和预览后执行的自动化。</p>
|
</div>
|
||||||
<code>HLP-MOD-OFFICIAL-EDUCATION-WORKBENCH-0001 · 8 项编号权限</code>
|
|
||||||
<button className="primary-button" type="button" disabled={educationBusy} onClick={() => void activateEducationModule()}>{educationBusy ? '正在验收官方模块…' : '确认八项边界并启用'}</button>
|
|
||||||
{educationMessage && <p>{educationMessage}</p>}
|
|
||||||
</div>}
|
|
||||||
</section>
|
</section>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -2321,7 +2310,7 @@ function HoloLakeApp() {
|
||||||
const privateInstalledModules: InstalledChannelModule[] = [
|
const privateInstalledModules: InstalledChannelModule[] = [
|
||||||
{ id: 'composition', title: '结构组合', meta: '只读知识投影与结构视图', number: compositionModule?.moduleNumber || 'HLP-MOD-LOCAL-NATIVE-COMPOSITION-0001', badge: compositionModule?.installedState === 'ACTIVE' ? '已安装' : '打开时校验', onOpen: () => { openWorldTool('composition'); void refreshCompositionModule() } },
|
{ id: 'composition', title: '结构组合', meta: '只读知识投影与结构视图', number: compositionModule?.moduleNumber || 'HLP-MOD-LOCAL-NATIVE-COMPOSITION-0001', badge: compositionModule?.installedState === 'ACTIVE' ? '已安装' : '打开时校验', onOpen: () => { openWorldTool('composition'); void refreshCompositionModule() } },
|
||||||
{ id: 'workbench', title: '资料工作台', meta: '文档与智能表格', number: workbenchModule?.moduleNumber || 'HLP-MOD-LOCAL-CHANNEL-WORKBENCH-0001', badge: workbenchModule?.installedState === 'ACTIVE' ? '已安装' : '打开时校验', onOpen: openWorkbench },
|
{ id: 'workbench', title: '资料工作台', meta: '文档与智能表格', number: workbenchModule?.moduleNumber || 'HLP-MOD-LOCAL-CHANNEL-WORKBENCH-0001', badge: workbenchModule?.installedState === 'ACTIVE' ? '已安装' : '打开时校验', onOpen: openWorkbench },
|
||||||
{ id: 'education', title: '教育工作台', meta: '教育文档、表格与自动化', number: educationModule?.moduleNumber || 'HLP-MOD-OFFICIAL-EDUCATION-WORKBENCH-0001', badge: educationModule?.installedState === 'ACTIVE' ? '已安装' : '打开时校验', onOpen: openEducation },
|
{ id: 'education', title: '教育工作台', meta: '空模块 · 待重新开发', number: '待重新编号', badge: '已隔离', onOpen: openEducation },
|
||||||
{ id: 'web-novel', title: '网文作者工作台', meta: '码字、设定、编辑与交付', number: webNovelModule?.moduleNumber || 'HLP-MOD-OFFICIAL-WEB-NOVEL-WORKBENCH-0001', badge: webNovelModule?.installedState === 'ACTIVE' ? '已安装' : '打开时校验', onOpen: openWebNovel },
|
{ id: 'web-novel', title: '网文作者工作台', meta: '码字、设定、编辑与交付', number: webNovelModule?.moduleNumber || 'HLP-MOD-OFFICIAL-WEB-NOVEL-WORKBENCH-0001', badge: webNovelModule?.installedState === 'ACTIVE' ? '已安装' : '打开时校验', onOpen: openWebNovel },
|
||||||
{ id: 'mobile-sync', title: '移动同步桥', meta: '同一人格系统的局域网加密入口', number: mobileSyncModule?.moduleNumber || 'HLP-MOD-OFFICIAL-MOBILE-SYNC-0001', badge: mobileSyncModule?.installedState === 'ACTIVE' ? '已安装' : '打开时校验', onOpen: openMobileSync },
|
{ id: 'mobile-sync', title: '移动同步桥', meta: '同一人格系统的局域网加密入口', number: mobileSyncModule?.moduleNumber || 'HLP-MOD-OFFICIAL-MOBILE-SYNC-0001', badge: mobileSyncModule?.installedState === 'ACTIVE' ? '已安装' : '打开时校验', onOpen: openMobileSync },
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -1,81 +0,0 @@
|
||||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
||||||
|
|
||||||
export const EDUCATION_EDITABLE_CELL_BUDGET = 240
|
|
||||||
|
|
||||||
export type EducationAdaptiveView = 'grid' | 'cards' | 'board' | 'dashboard'
|
|
||||||
|
|
||||||
export interface EducationAdaptiveInputs {
|
|
||||||
rowCount: number
|
|
||||||
columnCount: number
|
|
||||||
numericColumnCount: number
|
|
||||||
groupableColumnCount: number
|
|
||||||
focusConfidence: 'high' | 'medium' | 'low'
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface EducationModelRenderProposal {
|
|
||||||
primaryFocus: string
|
|
||||||
groupColumnIndex?: number
|
|
||||||
measureColumnIndex?: number
|
|
||||||
views: EducationAdaptiveView[]
|
|
||||||
pageSize?: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface EducationAdaptiveRenderPlan {
|
|
||||||
source: 'LOCAL_BASELINE' | 'MODEL_PROPOSAL'
|
|
||||||
state: 'READY' | 'DEGRADED' | 'WAITING_HUMAN_CONFIRMATION' | 'MODEL_PROPOSAL_REJECTED'
|
|
||||||
views: EducationAdaptiveView[]
|
|
||||||
pageSize: number
|
|
||||||
primaryFocus: string
|
|
||||||
reasons: string[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export function educationEditablePageSize(columnCount: number): number {
|
|
||||||
const boundedColumns = Math.max(1, Math.min(30, columnCount))
|
|
||||||
return Math.max(5, Math.min(20, Math.floor(EDUCATION_EDITABLE_CELL_BUDGET / boundedColumns)))
|
|
||||||
}
|
|
||||||
|
|
||||||
export function buildEducationBaselineRenderPlan(inputs: EducationAdaptiveInputs): EducationAdaptiveRenderPlan {
|
|
||||||
const reasons: string[] = []
|
|
||||||
const views: EducationAdaptiveView[] = ['grid', 'cards']
|
|
||||||
if (inputs.groupableColumnCount > 0) views.push('board')
|
|
||||||
else reasons.push('没有可靠分类字段,分类看板已降级停用。')
|
|
||||||
if (inputs.numericColumnCount > 0) views.push('dashboard')
|
|
||||||
else reasons.push('没有可靠数值字段,数据概览降级为计数摘要。')
|
|
||||||
if (inputs.focusConfidence === 'low') reasons.push('重点置信度不足,保留人工选择,不自动突出字段。')
|
|
||||||
if (inputs.rowCount * inputs.columnCount > EDUCATION_EDITABLE_CELL_BUDGET) reasons.push('可编辑单元格超过本机安全预算,已启用动态分页。')
|
|
||||||
return {
|
|
||||||
source: 'LOCAL_BASELINE',
|
|
||||||
state: reasons.length ? 'DEGRADED' : 'READY',
|
|
||||||
views,
|
|
||||||
pageSize: educationEditablePageSize(inputs.columnCount),
|
|
||||||
primaryFocus: inputs.focusConfidence === 'low' ? '重点待选择' : '本地基础算法重点',
|
|
||||||
reasons,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function validateEducationModelRenderProposal(
|
|
||||||
inputs: EducationAdaptiveInputs,
|
|
||||||
proposal: EducationModelRenderProposal,
|
|
||||||
humanConfirmed: boolean,
|
|
||||||
): EducationAdaptiveRenderPlan {
|
|
||||||
const baseline = buildEducationBaselineRenderPlan(inputs)
|
|
||||||
const reasons: string[] = []
|
|
||||||
const uniqueViews = [...new Set(proposal.views)]
|
|
||||||
if (!proposal.primaryFocus.trim() || proposal.primaryFocus.length > 120) reasons.push('模型建议的重点标题无效。')
|
|
||||||
if (!uniqueViews.length || uniqueViews.some((view) => !['grid', 'cards', 'board', 'dashboard'].includes(view))) reasons.push('模型建议包含未登记视图。')
|
|
||||||
if (proposal.groupColumnIndex !== undefined && (proposal.groupColumnIndex < 0 || proposal.groupColumnIndex >= inputs.columnCount)) reasons.push('模型建议的分类字段不存在。')
|
|
||||||
if (proposal.measureColumnIndex !== undefined && (proposal.measureColumnIndex < 0 || proposal.measureColumnIndex >= inputs.columnCount)) reasons.push('模型建议的统计字段不存在。')
|
|
||||||
const requestedPageSize = proposal.pageSize ?? baseline.pageSize
|
|
||||||
const maximumSafePageSize = educationEditablePageSize(inputs.columnCount)
|
|
||||||
if (!Number.isInteger(requestedPageSize) || requestedPageSize < 5 || requestedPageSize > maximumSafePageSize) reasons.push('模型建议的页面密度超过本机安全预算。')
|
|
||||||
if (reasons.length) return { ...baseline, state: 'MODEL_PROPOSAL_REJECTED', reasons: [...reasons, '已优雅退回本地基础算法。'] }
|
|
||||||
if (!humanConfirmed) return { ...baseline, state: 'WAITING_HUMAN_CONFIRMATION', reasons: ['模型建议已通过本地校验,等待用户确认后启用。'] }
|
|
||||||
return {
|
|
||||||
source: 'MODEL_PROPOSAL',
|
|
||||||
state: 'READY',
|
|
||||||
views: uniqueViews,
|
|
||||||
pageSize: requestedPageSize,
|
|
||||||
primaryFocus: proposal.primaryFocus.trim(),
|
|
||||||
reasons: ['模型建议已通过本地结构校验、资源上限与用户确认。'],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,60 +0,0 @@
|
||||||
import { describe, expect, it } from 'vitest'
|
|
||||||
import { analyzeEducationTableData } from './education-data'
|
|
||||||
|
|
||||||
describe('education data cleanup', () => {
|
|
||||||
it('trims cells, removes only fully empty rows and keeps the first exact row', () => {
|
|
||||||
const result = analyzeEducationTableData({
|
|
||||||
columns: [
|
|
||||||
{ columnId: 'name', title: '姓名' },
|
|
||||||
{ columnId: 'course', title: '课程' },
|
|
||||||
],
|
|
||||||
rows: [
|
|
||||||
{ rowId: 'row-1', cells: [' 冰朔 ', ' 写作 '] },
|
|
||||||
{ rowId: 'row-2', cells: ['冰朔', '写作'] },
|
|
||||||
{ rowId: 'row-3', cells: [' ', ''] },
|
|
||||||
{ rowId: 'row-4', cells: ['另一位学员', ''] },
|
|
||||||
],
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(result.originalRows).toBe(4)
|
|
||||||
expect(result.emptyRows).toBe(1)
|
|
||||||
expect(result.duplicateRows).toBe(1)
|
|
||||||
expect(result.trimmedCells).toBe(3)
|
|
||||||
expect(result.changeCount).toBe(5)
|
|
||||||
expect(result.changePreview).toHaveLength(5)
|
|
||||||
expect(result.cleanedRows).toEqual([
|
|
||||||
{ rowId: 'row-1', cells: ['冰朔', '写作'] },
|
|
||||||
{ rowId: 'row-4', cells: ['另一位学员', ''] },
|
|
||||||
])
|
|
||||||
})
|
|
||||||
|
|
||||||
it('does not merge rows that differ in any field', () => {
|
|
||||||
const result = analyzeEducationTableData({
|
|
||||||
columns: [{ columnId: 'status', title: '状态' }],
|
|
||||||
rows: [
|
|
||||||
{ rowId: 'row-1', cells: ['已完成'] },
|
|
||||||
{ rowId: 'row-2', cells: ['进行中'] },
|
|
||||||
],
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(result.duplicateRows).toBe(0)
|
|
||||||
expect(result.cleanedRows).toHaveLength(2)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('analyzes the entire table while keeping the human preview bounded', () => {
|
|
||||||
const result = analyzeEducationTableData({
|
|
||||||
columns: Array.from({ length: 23 }, (_, index) => ({ columnId: `column-${index}`, title: `字段 ${index + 1}` })),
|
|
||||||
rows: Array.from({ length: 55 }, (_, rowIndex) => ({
|
|
||||||
rowId: `row-${rowIndex}`,
|
|
||||||
cells: Array.from({ length: 23 }, (_, columnIndex) => ` value-${rowIndex}-${columnIndex} `),
|
|
||||||
})),
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(result.originalRows).toBe(55)
|
|
||||||
expect(result.cleanedRows).toHaveLength(55)
|
|
||||||
expect(result.trimmedCells).toBe(55 * 23)
|
|
||||||
expect(result.changeCount).toBe(55 * 23)
|
|
||||||
expect(result.changePreview).toHaveLength(12)
|
|
||||||
expect(result.changePreview.every((change) => change.before.length <= 120 && change.after.length <= 120)).toBe(true)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
@ -1,75 +0,0 @@
|
||||||
export interface EducationDataColumn { columnId: string; title: string }
|
|
||||||
export interface EducationDataRow { rowId: string; cells: string[] }
|
|
||||||
export interface EducationDataTable { columns: EducationDataColumn[]; rows: EducationDataRow[] }
|
|
||||||
|
|
||||||
export const EDUCATION_CLEANUP_CHANGE_PREVIEW_LIMIT = 12
|
|
||||||
|
|
||||||
export type EducationDataChangeKind = 'TRIM_CELL' | 'REMOVE_EMPTY_ROW' | 'REMOVE_DUPLICATE_ROW'
|
|
||||||
|
|
||||||
export interface EducationDataChangePreview {
|
|
||||||
kind: EducationDataChangeKind
|
|
||||||
rowNumber: number
|
|
||||||
columnTitle: string
|
|
||||||
before: string
|
|
||||||
after: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface EducationDataAnalysis {
|
|
||||||
originalRows: number
|
|
||||||
cleanedRows: EducationDataRow[]
|
|
||||||
emptyRows: number
|
|
||||||
duplicateRows: number
|
|
||||||
trimmedCells: number
|
|
||||||
changeCount: number
|
|
||||||
changePreview: EducationDataChangePreview[]
|
|
||||||
}
|
|
||||||
|
|
||||||
function previewCell(value: string): string {
|
|
||||||
const singleLine = value.replace(/\t/g, '⇥').replace(/\r?\n/g, '↵').replace(/ /g, '·')
|
|
||||||
return singleLine.length > 120 ? `${singleLine.slice(0, 117)}…` : singleLine
|
|
||||||
}
|
|
||||||
|
|
||||||
export function analyzeEducationTableData(table: EducationDataTable): EducationDataAnalysis {
|
|
||||||
let emptyRows = 0
|
|
||||||
let duplicateRows = 0
|
|
||||||
let trimmedCells = 0
|
|
||||||
let changeCount = 0
|
|
||||||
const seen = new Set<string>()
|
|
||||||
const cleanedRows: EducationDataRow[] = []
|
|
||||||
const changePreview: EducationDataChangePreview[] = []
|
|
||||||
const addPreview = (change: EducationDataChangePreview) => {
|
|
||||||
changeCount += 1
|
|
||||||
if (changePreview.length < EDUCATION_CLEANUP_CHANGE_PREVIEW_LIMIT) changePreview.push(change)
|
|
||||||
}
|
|
||||||
for (const [rowIndex, row] of table.rows.entries()) {
|
|
||||||
const cells = table.columns.map((_, index) => {
|
|
||||||
const original = row.cells[index] || ''
|
|
||||||
const cleaned = original.trim()
|
|
||||||
if (cleaned !== original) {
|
|
||||||
trimmedCells += 1
|
|
||||||
addPreview({
|
|
||||||
kind: 'TRIM_CELL',
|
|
||||||
rowNumber: rowIndex + 1,
|
|
||||||
columnTitle: table.columns[index]?.title || `字段 ${index + 1}`,
|
|
||||||
before: previewCell(original),
|
|
||||||
after: previewCell(cleaned),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return cleaned
|
|
||||||
})
|
|
||||||
if (cells.every((cell) => !cell)) {
|
|
||||||
emptyRows += 1
|
|
||||||
addPreview({ kind: 'REMOVE_EMPTY_ROW', rowNumber: rowIndex + 1, columnTitle: '整行', before: '完整空行', after: '移除' })
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
const signature = JSON.stringify(cells)
|
|
||||||
if (seen.has(signature)) {
|
|
||||||
duplicateRows += 1
|
|
||||||
addPreview({ kind: 'REMOVE_DUPLICATE_ROW', rowNumber: rowIndex + 1, columnTitle: '整行', before: '与前文完全重复', after: '移除' })
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
seen.add(signature)
|
|
||||||
cleanedRows.push({ ...row, cells })
|
|
||||||
}
|
|
||||||
return { originalRows: table.rows.length, cleanedRows, emptyRows, duplicateRows, trimmedCells, changeCount, changePreview }
|
|
||||||
}
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
||||||
|
|
||||||
import ChannelDocumentEngine from './channel-workbench/document-engine'
|
|
||||||
|
|
||||||
export default function EducationDocumentEngine({ body, onChange }: { body: string; onChange: (body: string) => void }) {
|
|
||||||
return <ChannelDocumentEngine body={body} onChange={onChange} placeholder="从这里开始记录教学计划、会议纪要或课程资料……"/>
|
|
||||||
}
|
|
||||||
|
|
@ -1,81 +0,0 @@
|
||||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
||||||
|
|
||||||
export const EDUCATION_EDITABLE_CELL_BUDGET = 240
|
|
||||||
|
|
||||||
export type EducationAdaptiveView = 'grid' | 'cards' | 'board' | 'dashboard'
|
|
||||||
|
|
||||||
export interface EducationAdaptiveInputs {
|
|
||||||
rowCount: number
|
|
||||||
columnCount: number
|
|
||||||
numericColumnCount: number
|
|
||||||
groupableColumnCount: number
|
|
||||||
focusConfidence: 'high' | 'medium' | 'low'
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface EducationModelRenderProposal {
|
|
||||||
primaryFocus: string
|
|
||||||
groupColumnIndex?: number
|
|
||||||
measureColumnIndex?: number
|
|
||||||
views: EducationAdaptiveView[]
|
|
||||||
pageSize?: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface EducationAdaptiveRenderPlan {
|
|
||||||
source: 'LOCAL_BASELINE' | 'MODEL_PROPOSAL'
|
|
||||||
state: 'READY' | 'DEGRADED' | 'WAITING_HUMAN_CONFIRMATION' | 'MODEL_PROPOSAL_REJECTED'
|
|
||||||
views: EducationAdaptiveView[]
|
|
||||||
pageSize: number
|
|
||||||
primaryFocus: string
|
|
||||||
reasons: string[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export function educationEditablePageSize(columnCount: number): number {
|
|
||||||
const boundedColumns = Math.max(1, Math.min(30, columnCount))
|
|
||||||
return Math.max(5, Math.min(20, Math.floor(EDUCATION_EDITABLE_CELL_BUDGET / boundedColumns)))
|
|
||||||
}
|
|
||||||
|
|
||||||
export function buildEducationBaselineRenderPlan(inputs: EducationAdaptiveInputs): EducationAdaptiveRenderPlan {
|
|
||||||
const reasons: string[] = []
|
|
||||||
const views: EducationAdaptiveView[] = ['grid', 'cards']
|
|
||||||
if (inputs.groupableColumnCount > 0) views.push('board')
|
|
||||||
else reasons.push('没有可靠分类字段,分类看板已降级停用。')
|
|
||||||
if (inputs.numericColumnCount > 0) views.push('dashboard')
|
|
||||||
else reasons.push('没有可靠数值字段,数据概览降级为计数摘要。')
|
|
||||||
if (inputs.focusConfidence === 'low') reasons.push('重点置信度不足,保留人工选择,不自动突出字段。')
|
|
||||||
if (inputs.rowCount * inputs.columnCount > EDUCATION_EDITABLE_CELL_BUDGET) reasons.push('可编辑单元格超过本机安全预算,已启用动态分页。')
|
|
||||||
return {
|
|
||||||
source: 'LOCAL_BASELINE',
|
|
||||||
state: reasons.length ? 'DEGRADED' : 'READY',
|
|
||||||
views,
|
|
||||||
pageSize: educationEditablePageSize(inputs.columnCount),
|
|
||||||
primaryFocus: inputs.focusConfidence === 'low' ? '重点待选择' : '本地基础算法重点',
|
|
||||||
reasons,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function validateEducationModelRenderProposal(
|
|
||||||
inputs: EducationAdaptiveInputs,
|
|
||||||
proposal: EducationModelRenderProposal,
|
|
||||||
humanConfirmed: boolean,
|
|
||||||
): EducationAdaptiveRenderPlan {
|
|
||||||
const baseline = buildEducationBaselineRenderPlan(inputs)
|
|
||||||
const reasons: string[] = []
|
|
||||||
const uniqueViews = [...new Set(proposal.views)]
|
|
||||||
if (!proposal.primaryFocus.trim() || proposal.primaryFocus.length > 120) reasons.push('模型建议的重点标题无效。')
|
|
||||||
if (!uniqueViews.length || uniqueViews.some((view) => !['grid', 'cards', 'board', 'dashboard'].includes(view))) reasons.push('模型建议包含未登记视图。')
|
|
||||||
if (proposal.groupColumnIndex !== undefined && (proposal.groupColumnIndex < 0 || proposal.groupColumnIndex >= inputs.columnCount)) reasons.push('模型建议的分类字段不存在。')
|
|
||||||
if (proposal.measureColumnIndex !== undefined && (proposal.measureColumnIndex < 0 || proposal.measureColumnIndex >= inputs.columnCount)) reasons.push('模型建议的统计字段不存在。')
|
|
||||||
const requestedPageSize = proposal.pageSize ?? baseline.pageSize
|
|
||||||
const maximumSafePageSize = educationEditablePageSize(inputs.columnCount)
|
|
||||||
if (!Number.isInteger(requestedPageSize) || requestedPageSize < 5 || requestedPageSize > maximumSafePageSize) reasons.push('模型建议的页面密度超过本机安全预算。')
|
|
||||||
if (reasons.length) return { ...baseline, state: 'MODEL_PROPOSAL_REJECTED', reasons: [...reasons, '已优雅退回本地基础算法。'] }
|
|
||||||
if (!humanConfirmed) return { ...baseline, state: 'WAITING_HUMAN_CONFIRMATION', reasons: ['模型建议已通过本地校验,等待用户确认后启用。'] }
|
|
||||||
return {
|
|
||||||
source: 'MODEL_PROPOSAL',
|
|
||||||
state: 'READY',
|
|
||||||
views: uniqueViews,
|
|
||||||
pageSize: requestedPageSize,
|
|
||||||
primaryFocus: proposal.primaryFocus.trim(),
|
|
||||||
reasons: ['模型建议已通过本地结构校验、资源上限与用户确认。'],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,75 +0,0 @@
|
||||||
export interface EducationDataColumn { columnId: string; title: string }
|
|
||||||
export interface EducationDataRow { rowId: string; cells: string[] }
|
|
||||||
export interface EducationDataTable { columns: EducationDataColumn[]; rows: EducationDataRow[] }
|
|
||||||
|
|
||||||
export const EDUCATION_CLEANUP_CHANGE_PREVIEW_LIMIT = 12
|
|
||||||
|
|
||||||
export type EducationDataChangeKind = 'TRIM_CELL' | 'REMOVE_EMPTY_ROW' | 'REMOVE_DUPLICATE_ROW'
|
|
||||||
|
|
||||||
export interface EducationDataChangePreview {
|
|
||||||
kind: EducationDataChangeKind
|
|
||||||
rowNumber: number
|
|
||||||
columnTitle: string
|
|
||||||
before: string
|
|
||||||
after: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface EducationDataAnalysis {
|
|
||||||
originalRows: number
|
|
||||||
cleanedRows: EducationDataRow[]
|
|
||||||
emptyRows: number
|
|
||||||
duplicateRows: number
|
|
||||||
trimmedCells: number
|
|
||||||
changeCount: number
|
|
||||||
changePreview: EducationDataChangePreview[]
|
|
||||||
}
|
|
||||||
|
|
||||||
function previewCell(value: string): string {
|
|
||||||
const singleLine = value.replace(/\t/g, '⇥').replace(/\r?\n/g, '↵').replace(/ /g, '·')
|
|
||||||
return singleLine.length > 120 ? `${singleLine.slice(0, 117)}…` : singleLine
|
|
||||||
}
|
|
||||||
|
|
||||||
export function analyzeEducationTableData(table: EducationDataTable): EducationDataAnalysis {
|
|
||||||
let emptyRows = 0
|
|
||||||
let duplicateRows = 0
|
|
||||||
let trimmedCells = 0
|
|
||||||
let changeCount = 0
|
|
||||||
const seen = new Set<string>()
|
|
||||||
const cleanedRows: EducationDataRow[] = []
|
|
||||||
const changePreview: EducationDataChangePreview[] = []
|
|
||||||
const addPreview = (change: EducationDataChangePreview) => {
|
|
||||||
changeCount += 1
|
|
||||||
if (changePreview.length < EDUCATION_CLEANUP_CHANGE_PREVIEW_LIMIT) changePreview.push(change)
|
|
||||||
}
|
|
||||||
for (const [rowIndex, row] of table.rows.entries()) {
|
|
||||||
const cells = table.columns.map((_, index) => {
|
|
||||||
const original = row.cells[index] || ''
|
|
||||||
const cleaned = original.trim()
|
|
||||||
if (cleaned !== original) {
|
|
||||||
trimmedCells += 1
|
|
||||||
addPreview({
|
|
||||||
kind: 'TRIM_CELL',
|
|
||||||
rowNumber: rowIndex + 1,
|
|
||||||
columnTitle: table.columns[index]?.title || `字段 ${index + 1}`,
|
|
||||||
before: previewCell(original),
|
|
||||||
after: previewCell(cleaned),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return cleaned
|
|
||||||
})
|
|
||||||
if (cells.every((cell) => !cell)) {
|
|
||||||
emptyRows += 1
|
|
||||||
addPreview({ kind: 'REMOVE_EMPTY_ROW', rowNumber: rowIndex + 1, columnTitle: '整行', before: '完整空行', after: '移除' })
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
const signature = JSON.stringify(cells)
|
|
||||||
if (seen.has(signature)) {
|
|
||||||
duplicateRows += 1
|
|
||||||
addPreview({ kind: 'REMOVE_DUPLICATE_ROW', rowNumber: rowIndex + 1, columnTitle: '整行', before: '与前文完全重复', after: '移除' })
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
seen.add(signature)
|
|
||||||
cleanedRows.push({ ...row, cells })
|
|
||||||
}
|
|
||||||
return { originalRows: table.rows.length, cleanedRows, emptyRows, duplicateRows, trimmedCells, changeCount, changePreview }
|
|
||||||
}
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
||||||
|
|
||||||
import ChannelDocumentEngine from '../channel-workbench/document-engine'
|
|
||||||
|
|
||||||
export default function EducationDocumentEngine({ body, onChange }: { body: string; onChange: (body: string) => void }) {
|
|
||||||
return <ChannelDocumentEngine body={body} onChange={onChange} />
|
|
||||||
}
|
|
||||||
|
|
@ -1,169 +0,0 @@
|
||||||
import { lazy, Suspense, useEffect, useMemo, useState } from 'react'
|
|
||||||
import { numberedInvoke as invoke } from '../numbered-ipc'
|
|
||||||
import { analyzeEducationTableData, type EducationDataAnalysis } from './education-data'
|
|
||||||
import './styles.css'
|
|
||||||
|
|
||||||
const EducationDocumentEngine = lazy(() => import('./education-document-engine'))
|
|
||||||
const ChannelSpreadsheetEngine = lazy(() => import('../channel-workbench/spreadsheet-engine'))
|
|
||||||
|
|
||||||
export const EDUCATION_MODULE_NUMBER = 'HLP-MOD-OFFICIAL-EDUCATION-WORKBENCH-0001'
|
|
||||||
|
|
||||||
interface EducationColumn { columnId: string; title: string }
|
|
||||||
interface EducationRow { rowId: string; cells: string[] }
|
|
||||||
interface DocumentSummary { documentId: string; title: string; revision: number; updatedAtUnixMs: number }
|
|
||||||
interface TableSummary { tableId: string; title: string; revision: number; columnCount: number; rowCount: number; updatedAtUnixMs: number }
|
|
||||||
interface UnassignedTableSummary { tableId: string; title: string; columnCount: number; rowCount: number; importedAtUnixMs: number }
|
|
||||||
interface EducationDocument extends DocumentSummary { body: string; createdAtUnixMs: number }
|
|
||||||
interface EducationTable extends TableSummary { columns: EducationColumn[]; rows: EducationRow[]; createdAtUnixMs: number }
|
|
||||||
interface AutomationRule {
|
|
||||||
ruleId: string; title: string; tableId: string; conditionColumnId: string; operator: string; conditionValue: string
|
|
||||||
actionColumnId: string; actionValue: string; enabled: boolean; revision: number
|
|
||||||
}
|
|
||||||
interface AutomationPreview { state: string; ruleId: string; tableId: string; ruleRevision: number; tableRevision: number; matchedRows: number; changedCells: number; previewToken: string }
|
|
||||||
interface EducationSnapshot {
|
|
||||||
schema: string; state: string; documents: DocumentSummary[]; tables: TableSummary[]; automationRules: AutomationRule[]
|
|
||||||
recentAutomationRuns: Array<{ runId: string; state: string; changedCells: number }>
|
|
||||||
recentImports: Array<{ importId: string; sourceFilename: string; sourceFormat: string; tableCount: number }>
|
|
||||||
unassignedTables: UnassignedTableSummary[]; documentCount: number; tableCount: number; automationRuleCount: number; unassignedCount: number
|
|
||||||
}
|
|
||||||
interface RecognitionCapability { state: string; modelApiState: string; fileTransferDefault: string; deterministicAdapterIds: string[] }
|
|
||||||
type Mode = 'overview' | 'documents' | 'tables' | 'cleanup' | 'automation' | 'import'
|
|
||||||
|
|
||||||
function cleanError(error: unknown) { return String(error).replace(/^Error:\s*/, '') }
|
|
||||||
function localTime(value: number) { return new Intl.DateTimeFormat('zh-CN', { dateStyle: 'medium', timeStyle: 'short' }).format(new Date(value)) }
|
|
||||||
|
|
||||||
export function EducationWorkspace({ onBack }: { onBack: () => void }) {
|
|
||||||
const [snapshot, setSnapshot] = useState<EducationSnapshot | null>(null)
|
|
||||||
const [capability, setCapability] = useState<RecognitionCapability | null>(null)
|
|
||||||
const [mode, setMode] = useState<Mode>('overview')
|
|
||||||
const [busy, setBusy] = useState(false)
|
|
||||||
const [message, setMessage] = useState('')
|
|
||||||
const [document, setDocument] = useState<EducationDocument | null>(null)
|
|
||||||
const [documentTitle, setDocumentTitle] = useState('')
|
|
||||||
const [documentBody, setDocumentBody] = useState('')
|
|
||||||
const [table, setTable] = useState<EducationTable | null>(null)
|
|
||||||
const [tableTitle, setTableTitle] = useState('')
|
|
||||||
const [tableValue, setTableValue] = useState<{ columns: EducationColumn[]; rows: EducationRow[] }>({ columns: [], rows: [] })
|
|
||||||
const [cleanup, setCleanup] = useState<EducationDataAnalysis | null>(null)
|
|
||||||
const [rule, setRule] = useState<AutomationRule | null>(null)
|
|
||||||
const [preview, setPreview] = useState<AutomationPreview | null>(null)
|
|
||||||
|
|
||||||
const refresh = async () => {
|
|
||||||
const [nextSnapshot, nextCapability] = await Promise.all([
|
|
||||||
invoke<EducationSnapshot>('get_education_workspace_snapshot'),
|
|
||||||
invoke<RecognitionCapability>('get_education_recognition_capability'),
|
|
||||||
])
|
|
||||||
setSnapshot(nextSnapshot)
|
|
||||||
setCapability(nextCapability)
|
|
||||||
return nextSnapshot
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => { void refresh().catch((error) => setMessage(cleanError(error))) }, [])
|
|
||||||
|
|
||||||
const run = async (action: () => Promise<void>) => {
|
|
||||||
setBusy(true); setMessage('')
|
|
||||||
try { await action() } catch (error) { setMessage(cleanError(error)) } finally { setBusy(false) }
|
|
||||||
}
|
|
||||||
|
|
||||||
const openDocument = (documentId: string) => run(async () => {
|
|
||||||
const next = await invoke<EducationDocument>('read_education_document', { input: { documentId } })
|
|
||||||
setDocument(next); setDocumentTitle(next.title); setDocumentBody(next.body); setMode('documents')
|
|
||||||
})
|
|
||||||
const createDocument = () => run(async () => {
|
|
||||||
const next = await invoke<EducationDocument>('create_education_document', { input: { title: '冰朔教育迁移验收' } })
|
|
||||||
setDocument(next); setDocumentTitle(next.title); setDocumentBody(next.body); setMode('documents'); await refresh()
|
|
||||||
})
|
|
||||||
const saveDocument = () => document && run(async () => {
|
|
||||||
const next = await invoke<EducationDocument>('save_education_document', { input: { documentId: document.documentId, title: documentTitle, body: documentBody, expectedRevision: document.revision } })
|
|
||||||
setDocument(next); setDocumentTitle(next.title); setDocumentBody(next.body); setMessage(`文档修订 ${next.revision} 已写入当前账号。`); await refresh()
|
|
||||||
})
|
|
||||||
const archiveDocument = () => document && run(async () => {
|
|
||||||
await invoke('archive_education_document', { input: { documentId: document.documentId } }); setDocument(null); await refresh(); setMessage('文档已进入可恢复归档。')
|
|
||||||
})
|
|
||||||
|
|
||||||
const openTable = (tableId: string, nextMode: Mode = 'tables') => run(async () => {
|
|
||||||
const next = await invoke<EducationTable>('read_education_table', { input: { tableId } })
|
|
||||||
setTable(next); setTableTitle(next.title); setTableValue({ columns: next.columns, rows: next.rows }); setCleanup(null); setPreview(null); setMode(nextMode)
|
|
||||||
})
|
|
||||||
const createTable = () => run(async () => {
|
|
||||||
const next = await invoke<EducationTable>('create_education_table', { input: { title: '冰朔教育迁移验收表' } })
|
|
||||||
setTable(next); setTableTitle(next.title); setTableValue({ columns: next.columns, rows: next.rows }); setMode('tables'); await refresh()
|
|
||||||
})
|
|
||||||
const addAcceptanceRow = () => {
|
|
||||||
if (!tableValue.columns.length) return
|
|
||||||
setTableValue((value) => ({ ...value, rows: [...value.rows, { rowId: `ROW-${crypto.randomUUID()}`, cells: value.columns.map((_, index) => index === 0 ? '冰朔验收' : index === value.columns.length - 1 ? '已迁移' : '教育工作台') }] }))
|
|
||||||
}
|
|
||||||
const saveTableValue = (rows = tableValue.rows, success = '表格已保存。') => table && run(async () => {
|
|
||||||
const next = await invoke<EducationTable>('save_education_table', { input: { tableId: table.tableId, title: tableTitle, columns: tableValue.columns, rows, expectedRevision: table.revision } })
|
|
||||||
setTable(next); setTableTitle(next.title); setTableValue({ columns: next.columns, rows: next.rows }); setCleanup(null); setPreview(null); setMessage(`${success} 当前修订 ${next.revision}。`); await refresh()
|
|
||||||
})
|
|
||||||
const archiveTable = () => table && run(async () => {
|
|
||||||
await invoke('archive_education_table', { input: { tableId: table.tableId } }); setTable(null); await refresh(); setMessage('表格已进入可恢复归档。')
|
|
||||||
})
|
|
||||||
|
|
||||||
const analyzeCleanup = () => setCleanup(analyzeEducationTableData(tableValue))
|
|
||||||
const applyCleanup = () => cleanup && saveTableValue(cleanup.cleanedRows, '清理结果经人工确认后已保存。')
|
|
||||||
|
|
||||||
const createRule = () => table && table.columns.length > 0 && run(async () => {
|
|
||||||
const next = await invoke<AutomationRule>('create_education_automation_rule', { input: {
|
|
||||||
title: '验收状态自动化', tableId: table.tableId, conditionColumnId: table.columns[0].columnId, operator: 'CONTAINS', conditionValue: '冰朔',
|
|
||||||
actionColumnId: table.columns[table.columns.length - 1].columnId, actionValue: '自动化已验收',
|
|
||||||
} })
|
|
||||||
setRule(next); setPreview(null); setMode('automation'); await refresh(); setMessage('规则已建立,但尚未执行。')
|
|
||||||
})
|
|
||||||
const chooseRule = async (nextRule: AutomationRule) => {
|
|
||||||
setRule(nextRule); setPreview(null)
|
|
||||||
if (!table || table.tableId !== nextRule.tableId) await openTable(nextRule.tableId, 'automation')
|
|
||||||
else setMode('automation')
|
|
||||||
}
|
|
||||||
const previewRule = () => rule && table && run(async () => {
|
|
||||||
const next = await invoke<AutomationPreview>('preview_education_automation_rule', { input: { ruleId: rule.ruleId, expectedRuleRevision: rule.revision, expectedTableRevision: table.revision } })
|
|
||||||
setPreview(next); setMessage(`只读预览:命中 ${next.matchedRows} 行,将改变 ${next.changedCells} 个单元格。`)
|
|
||||||
})
|
|
||||||
const executeRule = () => rule && table && preview && run(async () => {
|
|
||||||
const receipt = await invoke<{ state: string; changedCells: number; toTableRevision: number }>('execute_education_automation_rule', { input: {
|
|
||||||
ruleId: rule.ruleId, expectedRuleRevision: preview.ruleRevision, expectedTableRevision: preview.tableRevision, previewToken: preview.previewToken,
|
|
||||||
} })
|
|
||||||
setPreview(null); await openTable(table.tableId, 'automation'); await refresh(); setMessage(`自动化 ${receipt.state},改变 ${receipt.changedCells} 个单元格,修订 ${receipt.toTableRevision}。`)
|
|
||||||
})
|
|
||||||
|
|
||||||
const importTables = () => run(async () => {
|
|
||||||
const outcome = await invoke<{ state: string } | null>('import_education_tables_from_dialog')
|
|
||||||
await refresh(); setMode('import'); setMessage(outcome ? `导入结果:${outcome.state}。数据仍在未归属区,需人工确认。` : '已取消导入。')
|
|
||||||
})
|
|
||||||
const assignTable = (tableId: string) => run(async () => {
|
|
||||||
await invoke('assign_imported_table_to_education', { input: { tableId } }); await refresh(); setMessage('已由人工确认归入教育频道。')
|
|
||||||
})
|
|
||||||
const exportTable = (format: string) => table && run(async () => {
|
|
||||||
const result = await invoke<{ state: string; targetFilename: string } | null>('export_education_table_to_dialog', { input: { table: { tableId: table.tableId, title: tableTitle, columns: tableValue.columns, rows: tableValue.rows, revision: table.revision }, format } })
|
|
||||||
setMessage(result ? `${result.targetFilename} 已导出。` : '已取消导出。')
|
|
||||||
})
|
|
||||||
|
|
||||||
const analysis = useMemo(() => table ? analyzeEducationTableData(tableValue) : null, [table, tableValue])
|
|
||||||
if (!snapshot) return <section className="education-world"><button type="button" onClick={onBack}>← 返回我的频道</button><p>{message || '正在读取当前账号的教育工作台……'}</p></section>
|
|
||||||
|
|
||||||
return <section className="education-world">
|
|
||||||
<header className="education-hero"><button type="button" onClick={onBack}>← 返回我的频道</button><div><span>OFFICIAL NUMBERED MODULE</span><h1>教育工作台</h1><p>文档、表格、清理、导入和自动化都沿唯一编号路由进入当前账号的原生存储。</p></div><aside><b>{snapshot.state}</b><small>{EDUCATION_MODULE_NUMBER}</small></aside></header>
|
|
||||||
<nav className="education-tabs" aria-label="教育工作台功能">{(['overview','documents','tables','cleanup','automation','import'] as Mode[]).map((item) => <button type="button" key={item} className={mode === item ? 'active' : ''} onClick={() => setMode(item)}>{{ overview:'总览', documents:'文档', tables:'表格', cleanup:'数据清理', automation:'自动化', import:'导入与识别' }[item]}</button>)}</nav>
|
|
||||||
|
|
||||||
{mode === 'overview' && <div className="education-overview">
|
|
||||||
<article><span>教育文档</span><strong>{snapshot.documentCount}</strong><button type="button" disabled={busy} onClick={createDocument}>新建文档</button></article>
|
|
||||||
<article><span>教育表格</span><strong>{snapshot.tableCount}</strong><button type="button" disabled={busy} onClick={createTable}>新建表格</button></article>
|
|
||||||
<article><span>人工待归属</span><strong>{snapshot.unassignedCount}</strong><button type="button" disabled={busy} onClick={importTables}>导入文件</button></article>
|
|
||||||
<article><span>自动化规则</span><strong>{snapshot.automationRuleCount}</strong><small>只可预览后人工执行</small></article>
|
|
||||||
<section><h2>最近文档</h2>{snapshot.documents.map((item) => <button type="button" key={item.documentId} onClick={() => void openDocument(item.documentId)}><b>{item.title}</b><small>修订 {item.revision} · {localTime(item.updatedAtUnixMs)}</small></button>)}</section>
|
|
||||||
<section><h2>最近表格</h2>{snapshot.tables.map((item) => <button type="button" key={item.tableId} onClick={() => void openTable(item.tableId)}><b>{item.title}</b><small>{item.rowCount} 行 × {item.columnCount} 列 · 修订 {item.revision}</small></button>)}</section>
|
|
||||||
</div>}
|
|
||||||
|
|
||||||
{mode === 'documents' && <div className="education-editor-shell"><aside><button type="button" onClick={createDocument}>+ 新建文档</button>{snapshot.documents.map((item) => <button type="button" className={document?.documentId === item.documentId ? 'active' : ''} key={item.documentId} onClick={() => void openDocument(item.documentId)}>{item.title}<small>修订 {item.revision}</small></button>)}</aside><main>{document ? <><header><input aria-label="教育文档标题" value={documentTitle} onChange={(event) => setDocumentTitle(event.target.value)}/><div><button type="button" className="danger" onClick={archiveDocument}>归档</button><button type="button" disabled={busy} onClick={saveDocument}>保存修订</button></div></header><Suspense fallback={<p>正在装载文档引擎……</p>}><EducationDocumentEngine key={`${document.documentId}-${document.revision}`} body={documentBody} onChange={setDocumentBody}/></Suspense></> : <p>选择或新建一份教育文档。</p>}</main></div>}
|
|
||||||
|
|
||||||
{mode === 'tables' && <div className="education-editor-shell"><aside><button type="button" onClick={createTable}>+ 新建表格</button>{snapshot.tables.map((item) => <button type="button" className={table?.tableId === item.tableId ? 'active' : ''} key={item.tableId} onClick={() => void openTable(item.tableId)}>{item.title}<small>{item.rowCount} 行 · 修订 {item.revision}</small></button>)}</aside><main>{table ? <><header><input aria-label="教育表格标题" value={tableTitle} onChange={(event) => setTableTitle(event.target.value)}/><div><button type="button" onClick={addAcceptanceRow}>添加验收行</button><button type="button" onClick={() => void exportTable('XLSX')}>导出 XLSX</button><button type="button" className="danger" onClick={archiveTable}>归档</button><button type="button" disabled={busy} onClick={() => void saveTableValue()}>保存修订</button></div></header><Suspense fallback={<p>正在装载真实单元格引擎……</p>}><ChannelSpreadsheetEngine key={`${table.tableId}-${table.revision}`} tableId={table.tableId} title={tableTitle} columns={tableValue.columns} rows={tableValue.rows} onChange={setTableValue}/></Suspense></> : <p>选择或新建一份教育表格。</p>}</main></div>}
|
|
||||||
|
|
||||||
{mode === 'cleanup' && <div className="education-action-layout"><aside><h2>选择表格</h2>{snapshot.tables.map((item) => <button type="button" key={item.tableId} onClick={() => void openTable(item.tableId, 'cleanup')}>{item.title}</button>)}</aside><main><h2>人工确认的数据清理</h2>{table && analysis ? <><p>{table.title} · {analysis.originalRows} 行。系统不会后台自动修改。</p><div className="education-metrics"><b>{analysis.trimmedCells}<small>边界空白</small></b><b>{analysis.emptyRows}<small>空行</small></b><b>{analysis.duplicateRows}<small>重复行</small></b></div><button type="button" onClick={analyzeCleanup}>生成只读预览</button>{cleanup && <section className="education-preview"><h3>变更预览 · 共 {cleanup.changeCount} 项</h3>{cleanup.changePreview.map((item, index) => <p key={`${item.kind}-${index}`}><b>{item.kind}</b> 第 {item.rowNumber} 行 / {item.columnTitle}:{item.before} → {item.after}</p>)}<button type="button" disabled={busy} onClick={applyCleanup}>我确认,写入清理结果</button></section>}</> : <p>请先选择表格。</p>}</main></div>}
|
|
||||||
|
|
||||||
{mode === 'automation' && <div className="education-action-layout"><aside><h2>规则</h2>{snapshot.automationRules.map((item) => <button type="button" className={rule?.ruleId === item.ruleId ? 'active' : ''} key={item.ruleId} onClick={() => void chooseRule(item)}>{item.title}<small>修订 {item.revision}</small></button>)}</aside><main><h2>预览后执行</h2>{table && <button type="button" onClick={createRule}>为当前表格建立验收规则</button>}{rule && table ? <section className="education-rule"><p><b>{rule.title}</b></p><p>条件:{rule.operator} “{rule.conditionValue}” · 动作:写入 “{rule.actionValue}”</p><button type="button" disabled={busy} onClick={previewRule}>生成只读预览令牌</button>{preview && <div className="education-preview"><p>命中 {preview.matchedRows} 行,将改变 {preview.changedCells} 个单元格。</p><code>{preview.previewToken.slice(0, 20)}…</code><button type="button" disabled={busy} onClick={executeRule}>我确认,按此令牌执行</button></div>}</section> : <p>先在“表格”中打开表格,再建立规则;或选择已有规则。</p>}</main></div>}
|
|
||||||
|
|
||||||
{mode === 'import' && <div className="education-import"><header><div><h2>外部表格翻译层</h2><p>本机先识别结构;导入默认进入未归属区,不会自动判断行业。</p></div><button type="button" disabled={busy} onClick={importTables}>选择 XLSX / CSV / TSV / ODS</button></header><dl><div><dt>确定性适配器</dt><dd>{capability?.deterministicAdapterIds.join(' · ')}</dd></div><div><dt>模型接口</dt><dd>{capability?.modelApiState}</dd></div><div><dt>文件外传</dt><dd>{capability?.fileTransferDefault}</dd></div></dl><section><h3>待人工归属</h3>{snapshot.unassignedTables.length ? snapshot.unassignedTables.map((item) => <article key={item.tableId}><div><b>{item.title}</b><small>{item.rowCount} 行 × {item.columnCount} 列</small></div><button type="button" onClick={() => void assignTable(item.tableId)}>确认归入教育频道</button></article>) : <p>没有待归属数据。</p>}</section></div>}
|
|
||||||
{message && <p className="education-message" role="status">{message}</p>}
|
|
||||||
</section>
|
|
||||||
}
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
.education-world{min-height:100%;padding:28px;color:var(--content-primary);background:radial-gradient(circle at 75% 0,color-mix(in srgb,var(--primitive-cool-glow) 13%,transparent),transparent 36%),linear-gradient(150deg,var(--surface-depth),var(--surface-horizon) 58%,var(--surface-depth))}
|
|
||||||
.education-world button,.education-world input{font:inherit}.education-hero{display:grid;grid-template-columns:auto 1fr auto;gap:22px;align-items:start}.education-hero>button{background:transparent;border:0;color:var(--content-secondary);padding:10px}.education-hero span{font-size:11px;letter-spacing:.18em;color:var(--accent-light)}.education-hero h1{font-size:38px;margin:5px 0}.education-hero p{margin:0;color:var(--content-secondary)}.education-hero aside{display:flex;flex-direction:column;align-items:flex-end;gap:5px}.education-hero aside b{color:var(--state-ready)}.education-hero aside small{color:var(--content-muted);max-width:260px;text-align:right}
|
|
||||||
.education-tabs{display:flex;gap:8px;margin:24px 0;border-bottom:1px solid var(--panel-edge)}.education-tabs button{border:0;background:transparent;color:var(--content-secondary);padding:12px 15px;border-bottom:2px solid transparent}.education-tabs button.active{color:var(--content-primary);border-color:var(--accent-light)}
|
|
||||||
.education-overview{display:grid;grid-template-columns:repeat(4,1fr);gap:14px}.education-overview>article,.education-overview>section,.education-import,.education-action-layout>aside,.education-action-layout>main{background:var(--panel-bg);border:1px solid var(--panel-edge);border-radius:16px;padding:18px}.education-overview>article{display:flex;flex-direction:column;gap:8px}.education-overview>article strong{font-size:30px}.education-overview button,.education-editor-shell button,.education-action-layout button,.education-import button{border:1px solid var(--button-secondary-edge);background:var(--button-secondary-bg);color:var(--button-secondary-text);border-radius:9px;padding:9px 12px}.education-overview>section{grid-column:span 2}.education-overview>section>button{width:100%;display:flex;justify-content:space-between;margin-top:8px;text-align:left}.education-overview small{color:var(--content-muted)}
|
|
||||||
.education-editor-shell{display:grid;grid-template-columns:230px minmax(0,1fr);gap:14px;min-height:650px}.education-editor-shell>aside{display:flex;flex-direction:column;gap:7px;background:var(--panel-bg);border:1px solid var(--panel-edge);border-radius:14px;padding:12px}.education-editor-shell>aside button{text-align:left;display:flex;flex-direction:column}.education-editor-shell button.active{border-color:var(--accent-light);background:color-mix(in srgb,var(--accent-light) 16%,var(--panel-bg))}.education-editor-shell>main{min-width:0;background:#f7faf9;color:#172326;border-radius:14px;overflow:hidden}.education-editor-shell>main>header{display:flex;justify-content:space-between;gap:12px;padding:12px;background:#e8f0ee;border-bottom:1px solid #ccd9d6}.education-editor-shell>main>header input{flex:1;border:0;background:transparent;font-size:19px;font-weight:700;outline:0}.education-editor-shell>main>header>div{display:flex;gap:7px}.education-editor-shell button.danger{color:var(--state-danger);border-color:color-mix(in srgb,var(--state-danger) 45%,var(--panel-edge));background:color-mix(in srgb,var(--state-danger) 12%,var(--panel-bg))}
|
|
||||||
.education-action-layout{display:grid;grid-template-columns:250px 1fr;gap:14px}.education-action-layout aside{display:flex;flex-direction:column;gap:8px}.education-action-layout main{min-height:420px}.education-metrics{display:flex;gap:12px;margin:18px 0}.education-metrics b{font-size:26px;background:var(--panel-bg);border-radius:12px;padding:14px 22px}.education-metrics small{font-size:11px;display:block;color:var(--content-muted)}.education-preview{margin-top:15px;padding:16px;border:1px solid var(--panel-edge);background:var(--panel-bg);border-radius:12px}.education-preview p{color:var(--content-secondary)}.education-preview code{display:block;color:var(--accent-light);margin:10px 0}.education-rule{margin-top:18px}
|
|
||||||
.education-import>header{display:flex;justify-content:space-between;align-items:center}.education-import dl{display:grid;grid-template-columns:repeat(3,1fr);gap:10px}.education-import dl div{background:var(--panel-bg);border-radius:10px;padding:12px}.education-import dt{color:var(--content-muted);font-size:12px}.education-import dd{margin:5px 0 0;overflow-wrap:anywhere}.education-import section article{display:flex;justify-content:space-between;align-items:center;padding:12px 0;border-top:1px solid var(--panel-edge)}.education-import section article div{display:flex;flex-direction:column}.education-import small{color:var(--content-muted)}.education-message{position:sticky;bottom:14px;background:var(--panel-bg);border:1px solid var(--panel-edge);color:var(--content-primary);border-radius:12px;padding:12px 16px;margin:16px 0 0}
|
|
||||||
@media(max-width:900px){.education-overview{grid-template-columns:repeat(2,1fr)}.education-editor-shell,.education-action-layout{grid-template-columns:1fr}.education-editor-shell>aside{max-height:180px;overflow:auto}.education-hero{grid-template-columns:1fr}.education-hero>button{justify-self:start}.education-import dl{grid-template-columns:1fr}}
|
|
||||||
|
|
@ -673,150 +673,6 @@ const ROUTES = {
|
||||||
"operationNumber": "HLP-NIPC-OP-0084",
|
"operationNumber": "HLP-NIPC-OP-0084",
|
||||||
"targetNumber": "HLP-NIPC-TGT-0023"
|
"targetNumber": "HLP-NIPC-TGT-0023"
|
||||||
},
|
},
|
||||||
"get_education_workspace_snapshot": {
|
|
||||||
"protocolVersion": "HLP-NIPC-v1",
|
|
||||||
"callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001",
|
|
||||||
"channelNumber": "HLP-NIPC-CH-0002",
|
|
||||||
"moduleNumber": "HLP-NIPC-MOD-0024",
|
|
||||||
"operationNumber": "HLP-NIPC-OP-0085",
|
|
||||||
"targetNumber": "HLP-NIPC-TGT-0024"
|
|
||||||
},
|
|
||||||
"create_education_document": {
|
|
||||||
"protocolVersion": "HLP-NIPC-v1",
|
|
||||||
"callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001",
|
|
||||||
"channelNumber": "HLP-NIPC-CH-0002",
|
|
||||||
"moduleNumber": "HLP-NIPC-MOD-0024",
|
|
||||||
"operationNumber": "HLP-NIPC-OP-0086",
|
|
||||||
"targetNumber": "HLP-NIPC-TGT-0024"
|
|
||||||
},
|
|
||||||
"read_education_document": {
|
|
||||||
"protocolVersion": "HLP-NIPC-v1",
|
|
||||||
"callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001",
|
|
||||||
"channelNumber": "HLP-NIPC-CH-0002",
|
|
||||||
"moduleNumber": "HLP-NIPC-MOD-0024",
|
|
||||||
"operationNumber": "HLP-NIPC-OP-0087",
|
|
||||||
"targetNumber": "HLP-NIPC-TGT-0024"
|
|
||||||
},
|
|
||||||
"save_education_document": {
|
|
||||||
"protocolVersion": "HLP-NIPC-v1",
|
|
||||||
"callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001",
|
|
||||||
"channelNumber": "HLP-NIPC-CH-0002",
|
|
||||||
"moduleNumber": "HLP-NIPC-MOD-0024",
|
|
||||||
"operationNumber": "HLP-NIPC-OP-0088",
|
|
||||||
"targetNumber": "HLP-NIPC-TGT-0024"
|
|
||||||
},
|
|
||||||
"archive_education_document": {
|
|
||||||
"protocolVersion": "HLP-NIPC-v1",
|
|
||||||
"callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001",
|
|
||||||
"channelNumber": "HLP-NIPC-CH-0002",
|
|
||||||
"moduleNumber": "HLP-NIPC-MOD-0024",
|
|
||||||
"operationNumber": "HLP-NIPC-OP-0089",
|
|
||||||
"targetNumber": "HLP-NIPC-TGT-0024"
|
|
||||||
},
|
|
||||||
"create_education_table": {
|
|
||||||
"protocolVersion": "HLP-NIPC-v1",
|
|
||||||
"callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001",
|
|
||||||
"channelNumber": "HLP-NIPC-CH-0002",
|
|
||||||
"moduleNumber": "HLP-NIPC-MOD-0024",
|
|
||||||
"operationNumber": "HLP-NIPC-OP-0090",
|
|
||||||
"targetNumber": "HLP-NIPC-TGT-0024"
|
|
||||||
},
|
|
||||||
"read_education_table": {
|
|
||||||
"protocolVersion": "HLP-NIPC-v1",
|
|
||||||
"callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001",
|
|
||||||
"channelNumber": "HLP-NIPC-CH-0002",
|
|
||||||
"moduleNumber": "HLP-NIPC-MOD-0024",
|
|
||||||
"operationNumber": "HLP-NIPC-OP-0091",
|
|
||||||
"targetNumber": "HLP-NIPC-TGT-0024"
|
|
||||||
},
|
|
||||||
"save_education_table": {
|
|
||||||
"protocolVersion": "HLP-NIPC-v1",
|
|
||||||
"callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001",
|
|
||||||
"channelNumber": "HLP-NIPC-CH-0002",
|
|
||||||
"moduleNumber": "HLP-NIPC-MOD-0024",
|
|
||||||
"operationNumber": "HLP-NIPC-OP-0092",
|
|
||||||
"targetNumber": "HLP-NIPC-TGT-0024"
|
|
||||||
},
|
|
||||||
"archive_education_table": {
|
|
||||||
"protocolVersion": "HLP-NIPC-v1",
|
|
||||||
"callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001",
|
|
||||||
"channelNumber": "HLP-NIPC-CH-0002",
|
|
||||||
"moduleNumber": "HLP-NIPC-MOD-0024",
|
|
||||||
"operationNumber": "HLP-NIPC-OP-0093",
|
|
||||||
"targetNumber": "HLP-NIPC-TGT-0024"
|
|
||||||
},
|
|
||||||
"assign_imported_table_to_education": {
|
|
||||||
"protocolVersion": "HLP-NIPC-v1",
|
|
||||||
"callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001",
|
|
||||||
"channelNumber": "HLP-NIPC-CH-0002",
|
|
||||||
"moduleNumber": "HLP-NIPC-MOD-0024",
|
|
||||||
"operationNumber": "HLP-NIPC-OP-0094",
|
|
||||||
"targetNumber": "HLP-NIPC-TGT-0024"
|
|
||||||
},
|
|
||||||
"import_education_tables_from_dialog": {
|
|
||||||
"protocolVersion": "HLP-NIPC-v1",
|
|
||||||
"callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001",
|
|
||||||
"channelNumber": "HLP-NIPC-CH-0002",
|
|
||||||
"moduleNumber": "HLP-NIPC-MOD-0024",
|
|
||||||
"operationNumber": "HLP-NIPC-OP-0095",
|
|
||||||
"targetNumber": "HLP-NIPC-TGT-0024"
|
|
||||||
},
|
|
||||||
"export_education_table_to_dialog": {
|
|
||||||
"protocolVersion": "HLP-NIPC-v1",
|
|
||||||
"callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001",
|
|
||||||
"channelNumber": "HLP-NIPC-CH-0002",
|
|
||||||
"moduleNumber": "HLP-NIPC-MOD-0024",
|
|
||||||
"operationNumber": "HLP-NIPC-OP-0096",
|
|
||||||
"targetNumber": "HLP-NIPC-TGT-0024"
|
|
||||||
},
|
|
||||||
"get_education_recognition_capability": {
|
|
||||||
"protocolVersion": "HLP-NIPC-v1",
|
|
||||||
"callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001",
|
|
||||||
"channelNumber": "HLP-NIPC-CH-0002",
|
|
||||||
"moduleNumber": "HLP-NIPC-MOD-0024",
|
|
||||||
"operationNumber": "HLP-NIPC-OP-0097",
|
|
||||||
"targetNumber": "HLP-NIPC-TGT-0024"
|
|
||||||
},
|
|
||||||
"create_education_automation_rule": {
|
|
||||||
"protocolVersion": "HLP-NIPC-v1",
|
|
||||||
"callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001",
|
|
||||||
"channelNumber": "HLP-NIPC-CH-0002",
|
|
||||||
"moduleNumber": "HLP-NIPC-MOD-0024",
|
|
||||||
"operationNumber": "HLP-NIPC-OP-0098",
|
|
||||||
"targetNumber": "HLP-NIPC-TGT-0024"
|
|
||||||
},
|
|
||||||
"save_education_automation_rule": {
|
|
||||||
"protocolVersion": "HLP-NIPC-v1",
|
|
||||||
"callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001",
|
|
||||||
"channelNumber": "HLP-NIPC-CH-0002",
|
|
||||||
"moduleNumber": "HLP-NIPC-MOD-0024",
|
|
||||||
"operationNumber": "HLP-NIPC-OP-0099",
|
|
||||||
"targetNumber": "HLP-NIPC-TGT-0024"
|
|
||||||
},
|
|
||||||
"archive_education_automation_rule": {
|
|
||||||
"protocolVersion": "HLP-NIPC-v1",
|
|
||||||
"callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001",
|
|
||||||
"channelNumber": "HLP-NIPC-CH-0002",
|
|
||||||
"moduleNumber": "HLP-NIPC-MOD-0024",
|
|
||||||
"operationNumber": "HLP-NIPC-OP-0100",
|
|
||||||
"targetNumber": "HLP-NIPC-TGT-0024"
|
|
||||||
},
|
|
||||||
"preview_education_automation_rule": {
|
|
||||||
"protocolVersion": "HLP-NIPC-v1",
|
|
||||||
"callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001",
|
|
||||||
"channelNumber": "HLP-NIPC-CH-0002",
|
|
||||||
"moduleNumber": "HLP-NIPC-MOD-0024",
|
|
||||||
"operationNumber": "HLP-NIPC-OP-0101",
|
|
||||||
"targetNumber": "HLP-NIPC-TGT-0024"
|
|
||||||
},
|
|
||||||
"execute_education_automation_rule": {
|
|
||||||
"protocolVersion": "HLP-NIPC-v1",
|
|
||||||
"callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001",
|
|
||||||
"channelNumber": "HLP-NIPC-CH-0002",
|
|
||||||
"moduleNumber": "HLP-NIPC-MOD-0024",
|
|
||||||
"operationNumber": "HLP-NIPC-OP-0102",
|
|
||||||
"targetNumber": "HLP-NIPC-TGT-0024"
|
|
||||||
},
|
|
||||||
"get_web_novel_workspace_snapshot": {
|
"get_web_novel_workspace_snapshot": {
|
||||||
"protocolVersion": "HLP-NIPC-v1",
|
"protocolVersion": "HLP-NIPC-v1",
|
||||||
"callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001",
|
"callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001",
|
||||||
|
|
@ -1256,6 +1112,54 @@ const ROUTES = {
|
||||||
"moduleNumber": "HLP-NIPC-MOD-0034",
|
"moduleNumber": "HLP-NIPC-MOD-0034",
|
||||||
"operationNumber": "HLP-NIPC-OP-0157",
|
"operationNumber": "HLP-NIPC-OP-0157",
|
||||||
"targetNumber": "HLP-NIPC-TGT-0034"
|
"targetNumber": "HLP-NIPC-TGT-0034"
|
||||||
|
},
|
||||||
|
"discover_model_providers": {
|
||||||
|
"protocolVersion": "HLP-NIPC-v1",
|
||||||
|
"callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001",
|
||||||
|
"channelNumber": "HLP-NIPC-CH-0002",
|
||||||
|
"moduleNumber": "HLP-NIPC-MOD-0035",
|
||||||
|
"operationNumber": "HLP-NIPC-OP-0158",
|
||||||
|
"targetNumber": "HLP-NIPC-TGT-0035"
|
||||||
|
},
|
||||||
|
"upsert_model_provider": {
|
||||||
|
"protocolVersion": "HLP-NIPC-v1",
|
||||||
|
"callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001",
|
||||||
|
"channelNumber": "HLP-NIPC-CH-0002",
|
||||||
|
"moduleNumber": "HLP-NIPC-MOD-0035",
|
||||||
|
"operationNumber": "HLP-NIPC-OP-0159",
|
||||||
|
"targetNumber": "HLP-NIPC-TGT-0035"
|
||||||
|
},
|
||||||
|
"get_conversation": {
|
||||||
|
"protocolVersion": "HLP-NIPC-v1",
|
||||||
|
"callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001",
|
||||||
|
"channelNumber": "HLP-NIPC-CH-0002",
|
||||||
|
"moduleNumber": "HLP-NIPC-MOD-0035",
|
||||||
|
"operationNumber": "HLP-NIPC-OP-0160",
|
||||||
|
"targetNumber": "HLP-NIPC-TGT-0035"
|
||||||
|
},
|
||||||
|
"send_message": {
|
||||||
|
"protocolVersion": "HLP-NIPC-v1",
|
||||||
|
"callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001",
|
||||||
|
"channelNumber": "HLP-NIPC-CH-0002",
|
||||||
|
"moduleNumber": "HLP-NIPC-MOD-0035",
|
||||||
|
"operationNumber": "HLP-NIPC-OP-0161",
|
||||||
|
"targetNumber": "HLP-NIPC-TGT-0035"
|
||||||
|
},
|
||||||
|
"get_execution_limb_snapshot": {
|
||||||
|
"protocolVersion": "HLP-NIPC-v1",
|
||||||
|
"callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001",
|
||||||
|
"channelNumber": "HLP-NIPC-CH-0002",
|
||||||
|
"moduleNumber": "HLP-NIPC-MOD-0036",
|
||||||
|
"operationNumber": "HLP-NIPC-OP-0162",
|
||||||
|
"targetNumber": "HLP-NIPC-TGT-0036"
|
||||||
|
},
|
||||||
|
"run_execution_limb_training_drill": {
|
||||||
|
"protocolVersion": "HLP-NIPC-v1",
|
||||||
|
"callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001",
|
||||||
|
"channelNumber": "HLP-NIPC-CH-0002",
|
||||||
|
"moduleNumber": "HLP-NIPC-MOD-0036",
|
||||||
|
"operationNumber": "HLP-NIPC-OP-0163",
|
||||||
|
"targetNumber": "HLP-NIPC-TGT-0036"
|
||||||
}
|
}
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { invoke } from '@tauri-apps/api/core'
|
import { numberedInvoke as invoke } from '../numbered-ipc'
|
||||||
import { type FormEvent, useEffect, useMemo, useRef, useState } from 'react'
|
import { type FormEvent, useEffect, useMemo, useRef, useState } from 'react'
|
||||||
import './styles.css'
|
import './styles.css'
|
||||||
|
|
||||||
|
|
@ -38,6 +38,7 @@ function rememberProvider(providerId: string) {
|
||||||
else localStorage.removeItem(PROVIDER_SELECTION_KEY)
|
else localStorage.removeItem(PROVIDER_SELECTION_KEY)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 本页登记生命周期,不创建人格绑定。
|
||||||
export function PersonaChannelBody({ onBack, knowledgeFocus, embedded = false }: { onBack?: () => void; knowledgeFocus?: KnowledgeFocus | null; embedded?: boolean }) {
|
export function PersonaChannelBody({ onBack, knowledgeFocus, embedded = false }: { onBack?: () => void; knowledgeFocus?: KnowledgeFocus | null; embedded?: boolean }) {
|
||||||
const [body, setBody] = useState<PersonaBodySnapshot | null>(null)
|
const [body, setBody] = useState<PersonaBodySnapshot | null>(null)
|
||||||
const [discovery, setDiscovery] = useState<ProviderDiscovery | null>(null)
|
const [discovery, setDiscovery] = useState<ProviderDiscovery | null>(null)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue