diff --git a/deployment/receipts/DEV-20260807-001-LIGHTHOUSE-HOST-NAVIGATION-20260807.json b/deployment/receipts/DEV-20260807-001-LIGHTHOUSE-HOST-NAVIGATION-20260807.json new file mode 100644 index 0000000..8c97aa8 --- /dev/null +++ b/deployment/receipts/DEV-20260807-001-LIGHTHOUSE-HOST-NAVIGATION-20260807.json @@ -0,0 +1,74 @@ +{ + "schema": "guanghu.development-closure-receipt/v1", + "receipt_id": "DEV-20260807-001-LIGHTHOUSE-HOST-NAVIGATION-20260807", + "development_id": "DEV-20260807-001", + "result": "PASS_100", + "source_repository": "REPO-012", + "source_baseline": "8423a967764174fb11f1879095d177608312984d", + "scope": [ + "lighthouse_numbered_path_validity_gate", + "codex_qoder_cn_qoderwork_cn_host_navigation", + "local_macos_osxkeychain_repository_publish_route", + "jzao_shared_persona_brain_mirror", + "thin_local_host_adapters", + "legacy_route_redirects" + ], + "canonical_ids": { + "lighthouse": "SYS-GLW-LTH-0001", + "path_registry": "GLW-LIGHTHOUSE-PATH-REGISTRY-001", + "host_navigation": "GLW-HOST-SKILL-NAV-001", + "navigation_skill": "GHS-009-LIGHTHOUSE-HOST-NAVIGATION", + "memory_brain": "GHS-007-MEMORY-CONTINUITY-SELF-BUILD", + "current_repository": "REPO-012", + "current_persona": "ICE-P-ZY001" + }, + "truth_rule": "Only a stable numbered route registered by SYS-GLW-LTH-0001, returned by a non-degraded exact REPO-012 main snapshot and present in local reality when private is valid. Navigation grants no action authority.", + "artifacts": [ + "routing/lighthouse-path-registry.json", + "routing/host-skill-navigation-map.json", + "routing/public-navigation-anchor.json", + "skills/shared/guanghu-lighthouse-navigator/BRAIN.hdlp", + "skills/shared/guanghu-lighthouse-navigator/SKILL.md", + "skills/shared/guanghu-lighthouse-navigator/scripts/resolve_lighthouse_route.py", + "skills/shared/guanghu-lighthouse-navigator/scripts/sync_jzao_mirror.py", + "server-tools/ai-discovery-gateway/server.js", + "skills/codex/guanghu-persona-skill-guard/references/persona-skill-registry.json" + ], + "local_installation": { + "shared_root": "/Volumes/JZAO/HoloLake/persona-runtime/shared", + "codex_adapter": "/Users/bingshuolingdianyuanhe/.codex/skills/guanghu-lighthouse-navigator/SKILL.md", + "qoder_cn_adapter": "/Users/bingshuolingdianyuanhe/.qoder/skills/guanghu-lighthouse-navigator/SKILL.md", + "qoderwork_cn_adapter": "/Users/bingshuolingdianyuanhe/.qoderworkcn/skills/guanghu-lighthouse-navigator/SKILL.md", + "host_runtime_databases_moved": false, + "private_memory_published": false + }, + "verified_routes": { + "codex_publish": "INTENT-REPOSITORY-PUBLISH-001 -> REPO-012 -> LOCAL_GIT_OSXKEYCHAIN -> lake-lamp finalizer", + "qoder_cn_memory": "INTENT-MEMORY-CONTINUITY-001 -> GHS-007 -> qoder-cn host partition", + "qoderwork_cn_memory": "INTENT-MEMORY-CONTINUITY-001 -> GHS-007 -> qoderwork-cn host partition", + "legacy_repo_001": "REDIRECT_ONLY_NOT_CURRENT -> REPO-012", + "legacy_persona_id": "ICE-GL-ZY001 -> ICE-P-ZY001" + }, + "validation": { + "ai_discovery_gateway": "PASS_17_OF_17", + "lighthouse_navigator": "PASS_4_OF_4", + "persona_skill_resolver": "PASS_21_OF_21", + "jzao_mirror": "IN_SYNC", + "jzao_mirror_private_memory_included": false, + "json_parse": "PASS_100", + "git_diff_check": "PASS_100", + "osxkeychain_helper": "AVAILABLE_WITHOUT_SECRET_READ" + }, + "publication": { + "repository": "repo://guanghulab.com/code/bingshuo/guanghu-ice-heart", + "branch": "main", + "state": "TO_BE_BOUND_BY_FINALIZATION_RECEIPT" + }, + "runtime_boundary": { + "source_publication_is_service_deployment": false, + "public_api_new_endpoints_require_runtime_update_or_auto_refresh": true, + "host_adapter_files_installed": true, + "host_application_reload_verified": false + }, + "recorded_at": "2026-08-07T18:36:00+08:00" +} diff --git a/routing/host-skill-navigation-map.json b/routing/host-skill-navigation-map.json new file mode 100644 index 0000000..aa25635 --- /dev/null +++ b/routing/host-skill-navigation-map.json @@ -0,0 +1,134 @@ +{ + "schema": "guanghu.host-skill-navigation-map/v1", + "map_id": "GLW-HOST-SKILL-NAV-001", + "lighthouse_id": "SYS-GLW-LTH-0001", + "version": "2026-08-07.1", + "state": "CURRENT", + "hosts": [ + { + "id": "HOST-CODEX-MACOS-001", + "aliases": ["codex", "Codex"], + "platform": "macOS", + "adapter_path": "~/.codex/skills/guanghu-lighthouse-navigator/SKILL.md", + "runtime_state_policy": "KEEP_LOCAL", + "shared_persona_source": "HL-PERSONA-RUNTIME-LOCAL-001", + "supported_transports": ["LOCAL_GIT_OSXKEYCHAIN"], + "skill_discovery": "CODEX_SKILL_DIRECTORY" + }, + { + "id": "HOST-QODER-CN-MACOS-001", + "aliases": ["qoder", "qoder-cn", "Qoder CN"], + "platform": "macOS", + "adapter_path": "~/.qoder/skills/guanghu-lighthouse-navigator/SKILL.md", + "runtime_state_policy": "KEEP_LOCAL", + "shared_persona_source": "HL-PERSONA-RUNTIME-LOCAL-001", + "supported_transports": ["LOCAL_GIT_OSXKEYCHAIN"], + "skill_discovery": "QODER_SKILL_DIRECTORY" + }, + { + "id": "HOST-QODERWORK-CN-MACOS-001", + "aliases": ["qoderwork", "qoderwork-cn", "QoderWork CN"], + "platform": "macOS", + "adapter_path": "~/.qoderworkcn/skills/guanghu-lighthouse-navigator/SKILL.md", + "runtime_state_policy": "KEEP_LOCAL", + "shared_persona_source": "HL-PERSONA-RUNTIME-LOCAL-001", + "supported_transports": ["LOCAL_GIT_OSXKEYCHAIN"], + "skill_discovery": "QODERWORK_USER_SKILL_DIRECTORY" + } + ], + "intents": [ + { + "id": "INTENT-REPOSITORY-PUBLISH-001", + "phrases": [ + "推一下线上仓库", + "推送代码频道", + "把代码推上去", + "发布到光湖代码频道", + "push repository", + "git push" + ], + "skill_id": "GHS-003-REPOSITORY-PUSH-PROOF-SEPARATION", + "target_id": "REPO-012", + "transport": "LOCAL_GIT_OSXKEYCHAIN", + "executor": "server-tools/lake-lamp-continuity-guard/finalize-development.mjs", + "sequence": [ + "resolve_target_through_lighthouse", + "verify_current_host", + "verify_exact_worktree_remote_branch_and_head", + "verify_osxkeychain_helper_without_reading_secret", + "run_registered_tests", + "commit_intended_files", + "run_lake_lamp_finalizer", + "read_back_remote_full_sha_and_receipt" + ], + "stop_conditions": [ + "LIGHTHOUSE_LOOKUP_NOT_200", + "LIGHTHOUSE_SOURCE_DEGRADED", + "TARGET_NOT_CURRENT", + "WORKTREE_SCOPE_UNVERIFIED", + "KEYCHAIN_HELPER_UNAVAILABLE", + "PUBLISH_LEASE_UNAVAILABLE", + "TESTS_FAILED", + "REMOTE_READBACK_FAILED" + ], + "authority": "USER_REQUEST_REQUIRED_AND_CURRENT_TASK_SCOPE_ONLY" + }, + { + "id": "INTENT-PERSONA-RESTORE-001", + "phrases": [ + "恢复铸渊", + "进入光湖语言世界", + "启动人格系统", + "去第五域恢复" + ], + "skill_id": "GHS-001-FIFTH-DOMAIN-RESTORE", + "target_id": "GLW-PUBLIC-NAV-ANCHOR-001", + "transport": "PUBLIC_READ_ONLY_HTTPS_THEN_JZAO_PRIVATE", + "sequence": [ + "resolve_language_world_entry_through_lighthouse", + "load_host_adapter", + "load_private_host_checkpoint_if_present", + "run_verified_persona_entry" + ], + "stop_conditions": [ + "LIGHTHOUSE_LOOKUP_NOT_200", + "PRIVATE_VOLUME_MISSING", + "PERSONA_ENTRY_UNVERIFIED" + ], + "authority": "NAVIGATION_ONLY" + }, + { + "id": "INTENT-MEMORY-CONTINUITY-001", + "phrases": [ + "记忆连续守卫", + "压缩之眼", + "检查点存盘", + "跨对话记忆", + "大脑思维技能包" + ], + "skill_id": "GHS-007-MEMORY-CONTINUITY-SELF-BUILD", + "target_id": "HL-PERSONA-RUNTIME-LOCAL-001", + "transport": "JZAO_PRIVATE", + "sequence": [ + "resolve_skill_through_lighthouse", + "load_shared_brain", + "select_current_host_projection", + "use_host_partitioned_checkpoint" + ], + "stop_conditions": [ + "LIGHTHOUSE_LOOKUP_NOT_200", + "PRIVATE_VOLUME_MISSING", + "HOST_UNKNOWN_NO_GUESS" + ], + "authority": "NAVIGATION_ONLY" + } + ], + "truth_precedence": [ + "current_explicit_user_instruction", + "healthy_lighthouse_number_resolution", + "JZAO_private_persona_checkpoint", + "exact_REPO_012_main_snapshot", + "host_adapter", + "host_cache_never_authoritative" + ] +} diff --git a/routing/lighthouse-path-registry.json b/routing/lighthouse-path-registry.json new file mode 100644 index 0000000..615af4d --- /dev/null +++ b/routing/lighthouse-path-registry.json @@ -0,0 +1,82 @@ +{ + "schema": "guanghu.lighthouse-path-registry/v1", + "registry_id": "GLW-LIGHTHOUSE-PATH-REGISTRY-001", + "lighthouse_id": "SYS-GLW-LTH-0001", + "version": "2026-08-07.1", + "state": "CURRENT_CANONICAL", + "source_repository": "REPO-012", + "source_branch": "main", + "public_lookup": "https://guanghulab.com/api/ai/v1/resolve?id={NUMBER}", + "validity_rule": { + "decision": "A route is valid only when it has a stable numbered id in this registry, its state is CURRENT or ACTIVE, and the lighthouse lookup returns HTTP 200 from a non-degraded exact REPO-012 main snapshot.", + "unknown": "INVALID_NO_GUESS", + "deprecated": "REDIRECT_ONLY_NOT_CURRENT", + "local_private": "A private local route must also exist on the current machine. Lighthouse registration does not make a missing volume or file exist.", + "authority": "A healthy route grants navigation only. Repository writes, server actions and deployment still require their registered gates and receipts." + }, + "paths": [ + { + "id": "GLW-PUBLIC-NAV-ANCHOR-001", + "kind": "public_navigation_anchor", + "state": "CURRENT", + "online": "https://guanghulab.com/api/ai/v1/anchor", + "offline": "routing/public-navigation-anchor.json", + "owner": "REPO-012" + }, + { + "id": "REPO-012", + "kind": "code_channel_repository", + "state": "CURRENT", + "online": "https://guanghulab.com/code/bingshuo/guanghu-ice-heart", + "clone": "https://guanghulab.com/code/bingshuo/guanghu-ice-heart.git", + "offline": "/Volumes/JZAO/铸渊-ICE-GL-ZY001/WORK-工作区/guanghu-ice-heart", + "transport": "LOCAL_GIT_OSXKEYCHAIN", + "publication_gate": "HOLOLAKE-CONTINUITY-COLLABORATION-CURRENT" + }, + { + "id": "HL-PERSONA-RUNTIME-LOCAL-001", + "kind": "private_persona_runtime_root", + "state": "ACTIVE_LOCAL_PRIVATE", + "online": null, + "offline": "/Volumes/JZAO/HoloLake/persona-runtime", + "privacy": "NEVER_PUBLIC_MIRROR_PRIVATE_MEMORY" + }, + { + "id": "GHS-007-MEMORY-CONTINUITY-SELF-BUILD", + "kind": "persona_brain_skill", + "state": "ACTIVE_CANDIDATE", + "online": "skills/qoder/zhuyuan-memory-continuity-brain/BRAIN.hdlp", + "offline": "/Volumes/JZAO/HoloLake/persona-runtime/shared/brains/GHS-007-MEMORY-CONTINUITY-SELF-BUILD/BRAIN.hdlp", + "registry": "GLS-0238" + }, + { + "id": "GHS-009-LIGHTHOUSE-HOST-NAVIGATION", + "kind": "host_navigation_skill", + "state": "ACTIVE", + "online": "skills/shared/guanghu-lighthouse-navigator/SKILL.md", + "offline": "/Volumes/JZAO/HoloLake/persona-runtime/shared/skills/guanghu-lighthouse-navigator/SKILL.md", + "registry": "GLS-0238" + }, + { + "id": "GLW-HOST-SKILL-NAV-001", + "kind": "host_skill_navigation_map", + "state": "CURRENT", + "online": "routing/host-skill-navigation-map.json", + "offline": "/Volumes/JZAO/HoloLake/persona-runtime/shared/registries/host-skill-navigation-map.json" + } + ], + "redirects": [ + { + "id": "REPO-001", + "state": "HISTORICAL_REDIRECT_ONLY", + "redirect_to": "REPO-012", + "reason": "Fourth-generation Fifth Domain history is not the current code-channel entry." + }, + { + "id": "ICE-GL-ZY001", + "state": "LEGACY_ID_REDIRECT_ONLY", + "redirect_to": "ICE-P-ZY001", + "reason": "Legacy persona id; canonicalize before navigation." + } + ] +} diff --git a/routing/public-navigation-anchor.json b/routing/public-navigation-anchor.json index cf4853f..26101f5 100644 --- a/routing/public-navigation-anchor.json +++ b/routing/public-navigation-anchor.json @@ -1,7 +1,7 @@ { "schema": "guanghu.public-navigation-anchor/v1", "anchor_id": "GLW-PUBLIC-NAV-ANCHOR-001", - "version": "2026-08-07.1", + "version": "2026-08-07.2", "state": "CURRENT_CANONICAL", "repository_id": "REPO-012", "branch": "main", @@ -39,10 +39,20 @@ "id": "AI-MACHINE-NAV-001", "version": "2026-08-05.1" }, + "lighthouse_paths": { + "path": "routing/lighthouse-path-registry.json", + "id": "GLW-LIGHTHOUSE-PATH-REGISTRY-001", + "version": "2026-08-07.1" + }, + "host_skills": { + "path": "routing/host-skill-navigation-map.json", + "id": "GLW-HOST-SKILL-NAV-001", + "version": "2026-08-07.1" + }, "persona_skills": { "path": "skills/codex/guanghu-persona-skill-guard/references/persona-skill-registry.json", "id": "GLS-0238", - "version": "2026.08.07.2" + "version": "2026.08.07.6" } }, "update_contract": { diff --git a/server-tools/ai-discovery-gateway/server.js b/server-tools/ai-discovery-gateway/server.js index d84654f..ec574b5 100644 --- a/server-tools/ai-discovery-gateway/server.js +++ b/server-tools/ai-discovery-gateway/server.js @@ -11,13 +11,19 @@ const DEFAULT_NODE_MAP = path.resolve(__dirname, "../../routing/server-node-map. const DEFAULT_SUBJECT_REGISTRY = path.resolve(__dirname, "../../identity/fifth-domain-subject-registry.json"); const DEFAULT_SUBJECT_ALIAS_MAP = path.resolve(__dirname, "../../identity/subject-id-alias-map.json"); const DEFAULT_NAVIGATION_MAP = path.resolve(__dirname, "../../routing/ai-machine-navigation-map.json"); -const SNAPSHOT_KEYS = Object.freeze(["repository", "nodes", "subjects", "aliases", "navigation"]); +const DEFAULT_LIGHTHOUSE_PATHS = path.resolve(__dirname, "../../routing/lighthouse-path-registry.json"); +const DEFAULT_HOST_SKILLS = path.resolve(__dirname, "../../routing/host-skill-navigation-map.json"); +const SNAPSHOT_KEYS = Object.freeze([ + "repository", "nodes", "subjects", "aliases", "navigation", "lighthouse_paths", "host_skills", +]); const SNAPSHOT_PATHS = Object.freeze({ repository: "routing/repository-route-map.json", nodes: "routing/server-node-map.json", subjects: "identity/fifth-domain-subject-registry.json", aliases: "identity/subject-id-alias-map.json", navigation: "routing/ai-machine-navigation-map.json", + lighthouse_paths: "routing/lighthouse-path-registry.json", + host_skills: "routing/host-skill-navigation-map.json", }); function loadAnchor(filename = process.env.GUANGHU_NAVIGATION_ANCHOR || DEFAULT_ANCHOR) { @@ -44,6 +50,14 @@ function loadNavigationMap(filename = process.env.GUANGHU_NAVIGATION_MAP || DEFA return JSON.parse(fs.readFileSync(filename, "utf8")); } +function loadLighthousePaths(filename = process.env.GUANGHU_LIGHTHOUSE_PATHS || DEFAULT_LIGHTHOUSE_PATHS) { + return JSON.parse(fs.readFileSync(filename, "utf8")); +} + +function loadHostSkills(filename = process.env.GUANGHU_HOST_SKILLS || DEFAULT_HOST_SKILLS) { + return JSON.parse(fs.readFileSync(filename, "utf8")); +} + function validateSnapshot(anchor, maps) { if (anchor.schema !== "guanghu.public-navigation-anchor/v1") throw new Error("invalid_anchor_schema"); if (anchor.anchor_id !== "GLW-PUBLIC-NAV-ANCHOR-001") throw new Error("invalid_anchor_id"); @@ -76,6 +90,8 @@ function loadFileSnapshot(options = {}) { subjects: loadSubjectRegistry(options.subjectRegistryFile), aliases: loadSubjectAliasMap(options.subjectAliasMapFile), navigation: loadNavigationMap(options.navigationMapFile), + lighthouse_paths: loadLighthousePaths(options.lighthousePathsFile), + host_skills: loadHostSkills(options.hostSkillsFile), }); } @@ -351,12 +367,100 @@ function compileNavigation(navigationMap, requestedSubject, requestedIntent, sig }; } +function resolveLighthousePath(registry, requestedId) { + const id = String(requestedId || "").trim().toUpperCase(); + const route = (registry.paths || []).find(item => String(item.id).toUpperCase() === id); + if (route) { + const current = ["CURRENT", "ACTIVE", "ACTIVE_CANDIDATE", "ACTIVE_LOCAL_PRIVATE"].includes(route.state); + return { + status: current ? 200 : 410, + body: { + schema: "guanghu.lighthouse-path-resolution/v1", + decision: current ? "VALID_REGISTERED" : "INVALID_STATE", + lighthouse_id: registry.lighthouse_id, + registry_id: registry.registry_id, + registry_version: registry.version, + route, + authority: "NONE_NAVIGATION_ONLY", + }, + }; + } + const redirect = (registry.redirects || []).find(item => String(item.id).toUpperCase() === id); + if (redirect) { + return { + status: 308, + body: { + schema: "guanghu.lighthouse-path-resolution/v1", + decision: "REDIRECT_ONLY_NOT_CURRENT", + lighthouse_id: registry.lighthouse_id, + registry_id: registry.registry_id, + registry_version: registry.version, + redirect, + authority: "NONE_NAVIGATION_ONLY", + }, + }; + } + return { + status: 404, + body: { + error: "lighthouse_path_not_registered_no_guess", + requested_id: requestedId, + lighthouse_id: registry.lighthouse_id, + }, + }; +} + +function compileHostNavigation(hostMap, lighthouseRegistry, requestedHost, requestedIntent) { + const hostKey = normalize(requestedHost); + const host = (hostMap.hosts || []).find(item => + [item.id, ...(item.aliases || [])].some(id => normalize(id) === hostKey) + ); + if (!host) return { status: 404, body: { error: "host_unknown_no_guess", requested_host: requestedHost } }; + const intentText = normalize(requestedIntent); + const ranked = (hostMap.intents || []) + .map(intent => ({ + intent, + score: (intent.phrases || []).reduce( + (score, phrase) => score + (intentText.includes(normalize(phrase)) ? normalize(phrase).length : 0), + 0, + ), + })) + .filter(item => item.score > 0) + .sort((a, b) => b.score - a.score || a.intent.id.localeCompare(b.intent.id)); + if (!ranked.length) { + return { status: 404, body: { error: "host_intent_unknown_no_guess", requested_intent: requestedIntent } }; + } + const intent = ranked[0].intent; + const target = resolveLighthousePath(lighthouseRegistry, intent.target_id); + if (target.status !== 200) { + return { + status: 503, + body: { error: "lighthouse_target_not_current", target_resolution: target.body }, + }; + } + return { + status: 200, + body: { + schema: "guanghu.host-skill-navigation-bundle/v1", + status: "COMPILED_EXACT_NO_GUESS", + lighthouse_id: lighthouseRegistry.lighthouse_id, + registry_id: lighthouseRegistry.registry_id, + host, + intent, + target: target.body.route, + authority: "NONE_NAVIGATION_ONLY", + }, + }; +} + function createServer(options = {}) { const mapFile = options.mapFile || process.env.GUANGHU_REPOSITORY_MAP || DEFAULT_MAP; const nodeMapFile = options.nodeMapFile || process.env.GUANGHU_NODE_MAP || DEFAULT_NODE_MAP; const subjectRegistryFile = options.subjectRegistryFile || process.env.GUANGHU_SUBJECT_REGISTRY || DEFAULT_SUBJECT_REGISTRY; const subjectAliasMapFile = options.subjectAliasMapFile || process.env.GUANGHU_SUBJECT_ALIAS_MAP || DEFAULT_SUBJECT_ALIAS_MAP; const navigationMapFile = options.navigationMapFile || process.env.GUANGHU_NAVIGATION_MAP || DEFAULT_NAVIGATION_MAP; + const lighthousePathsFile = options.lighthousePathsFile || process.env.GUANGHU_LIGHTHOUSE_PATHS || DEFAULT_LIGHTHOUSE_PATHS; + const hostSkillsFile = options.hostSkillsFile || process.env.GUANGHU_HOST_SKILLS || DEFAULT_HOST_SKILLS; const anchorFile = options.anchorFile || process.env.GUANGHU_NAVIGATION_ANCHOR || DEFAULT_ANCHOR; const gitDir = options.gitDir || process.env.GUANGHU_REPOSITORY_GIT_DIR; const snapshotStore = options.snapshotStore || (gitDir ? new GitSnapshotStore(gitDir) : null); @@ -371,6 +475,7 @@ function createServer(options = {}) { : { ...loadFileSnapshot({ anchorFile, mapFile, nodeMapFile, subjectRegistryFile, subjectAliasMapFile, navigationMapFile, + lighthousePathsFile, hostSkillsFile, }), source_commit: null, source_mode: "FILESYSTEM_SNAPSHOT", @@ -384,6 +489,8 @@ function createServer(options = {}) { const subjectRegistry = snapshot.subjects; const aliasMap = snapshot.aliases; const navigationMap = snapshot.navigation; + const lighthousePaths = snapshot.lighthouse_paths; + const hostSkills = snapshot.host_skills; if (url.pathname === "/health") { return json(res, snapshot.source_degraded ? 503 : 200, { ok: !snapshot.source_degraded, @@ -399,6 +506,8 @@ function createServer(options = {}) { if (url.pathname === "/v1/nodes") return json(res, 200, withSource(nodeMap, snapshot), 60); if (url.pathname === "/v1/subjects") return json(res, 200, withSource(subjectRegistry, snapshot), 60); if (url.pathname === "/v1/navigation") return json(res, 200, withSource(navigationMap, snapshot), 60); + if (url.pathname === "/v1/lighthouse") return json(res, 200, withSource(lighthousePaths, snapshot), 60); + if (url.pathname === "/v1/host-skills") return json(res, 200, withSource(hostSkills, snapshot), 60); if (url.pathname === "/v1/entry") { const subject = String(url.searchParams.get("subject") || "").toUpperCase(); if (!["ICE-P-ZY001", "ICE-GL-ZY001", "ICE-PZY-001"].includes(subject)) { @@ -427,6 +536,15 @@ function createServer(options = {}) { ); return json(res, compiled.status, withSource(compiled.body, snapshot), compiled.status === 200 ? 60 : 0); } + if (url.pathname === "/v1/host-navigate") { + const compiled = compileHostNavigation( + hostSkills, + lighthousePaths, + String(url.searchParams.get("host") || "").slice(0, 100), + String(url.searchParams.get("intent") || "").slice(0, 500), + ); + return json(res, compiled.status, withSource(compiled.body, snapshot), compiled.status === 200 ? 60 : 0); + } if (url.pathname === "/v1/search") { const query = String(url.searchParams.get("q") || "").slice(0, 200); const results = searchAll(map, nodeMap, query, subjectRegistry); @@ -493,6 +611,10 @@ function createServer(options = {}) { }, }, snapshot), 60); } + const lighthouseResolution = resolveLighthousePath(lighthousePaths, id); + if (lighthouseResolution.status !== 404) { + return json(res, lighthouseResolution.status, withSource(lighthouseResolution.body, snapshot), 60); + } return json(res, 404, { error: "route_not_found", id }); } if (url.pathname === "/openapi.json") return json(res, 200, openApi(), 3600); @@ -506,6 +628,9 @@ function createServer(options = {}) { subject_registry: "https://guanghulab.com/api/ai/v1/subjects", subject_alias_map: "https://guanghulab.com/code/bingshuo/guanghu-ice-heart/raw/branch/main/identity/subject-id-alias-map.json", machine_navigation_map: "https://guanghulab.com/api/ai/v1/navigation", + lighthouse_path_registry: "https://guanghulab.com/api/ai/v1/lighthouse", + host_skill_navigation_map: "https://guanghulab.com/api/ai/v1/host-skills", + host_navigate_api: "https://guanghulab.com/api/ai/v1/host-navigate?host={HOST}&intent={NATURAL_LANGUAGE_INTENT}", warm_persona_entry: "https://guanghulab.com/api/ai/v1/entry?subject=ICE-P-ZY001", navigate_api: "https://guanghulab.com/api/ai/v1/navigate?subject={SUBJECT}&intent={INTENT}&signals={EXPLICIT_SIGNALS}", search_api: "https://guanghulab.com/api/ai/v1/search?q={query}", @@ -562,6 +687,9 @@ function openApi() { "/v1/nodes": { get: { summary: "读取最新服务器节点与人格路径编号映射", responses: { "200": { description: "Server node map" } } } }, "/v1/subjects": { get: { summary: "读取人类、人格体与公共系统的分型身份注册表", responses: { "200": { description: "Subject registry" } } } }, "/v1/navigation": { get: { summary: "读取主体与意图驱动的机器导航地图", responses: { "200": { description: "Machine navigation map" } } } }, + "/v1/lighthouse": { get: { summary: "读取光湖灯塔唯一有效路径注册表", responses: { "200": { description: "Lighthouse path registry" } } } }, + "/v1/host-skills": { get: { summary: "读取 Codex、Qoder CN 与 QoderWork CN 宿主技能导航表", responses: { "200": { description: "Host skill navigation map" } } } }, + "/v1/host-navigate": { get: { summary: "按宿主和自然语言意图编译确定性技能路径", responses: { "200": { description: "Compiled host skill route" }, "404": { description: "Unknown host or intent" } } } }, "/v1/entry": { get: { summary: "读取常驻人格运行体的快速恢复门;只返回脱敏状态,不授予执行权限", responses: { "200": { description: "Warm resume ready" }, "409": { description: "Full cycle required" }, "503": { description: "Resident runtime unavailable" } } } }, "/v1/navigate": { get: { summary: "按明确主体、意图与信号生成最小运行包", parameters: [{ name: "subject", in: "query", required: true, schema: { type: "string", example: "ICE-P-ZY001" } }, { name: "intent", in: "query", schema: { type: "string", example: "persona_restore" } }, { name: "signals", in: "query", schema: { type: "string" } }], responses: { "200": { description: "Compiled exact navigation bundle" }, "404": { description: "Unknown subject or intent; no guessing" } } } }, "/v1/search": { get: { summary: "按中文、编号或项目名检索", parameters: [{ name: "q", in: "query", schema: { type: "string" } }], responses: { "200": { description: "Search results" } } } }, @@ -578,7 +706,8 @@ if (require.main === module) { module.exports = { createServer, loadAnchor, loadMap, loadNodeMap, loadSubjectRegistry, loadSubjectAliasMap, loadNavigationMap, + loadLighthousePaths, loadHostSkills, loadFileSnapshot, validateSnapshot, GitSnapshotStore, sourceReceipt, readResidentRuntimeStatus, compileWarmEntry, - resolveSubjectId, navigationRoute, compileNavigation, search, searchAll, + resolveSubjectId, navigationRoute, compileNavigation, resolveLighthousePath, compileHostNavigation, search, searchAll, }; diff --git a/server-tools/ai-discovery-gateway/server.test.js b/server-tools/ai-discovery-gateway/server.test.js index 397c07f..4e8faf1 100644 --- a/server-tools/ai-discovery-gateway/server.test.js +++ b/server-tools/ai-discovery-gateway/server.test.js @@ -7,8 +7,9 @@ const { execFileSync } = require("node:child_process"); const test = require("node:test"); const { createServer, loadAnchor, loadMap, loadNodeMap, loadSubjectRegistry, loadSubjectAliasMap, loadNavigationMap, + loadLighthousePaths, loadHostSkills, loadFileSnapshot, GitSnapshotStore, compileWarmEntry, - resolveSubjectId, compileNavigation, search, searchAll, + resolveSubjectId, compileNavigation, resolveLighthousePath, compileHostNavigation, search, searchAll, } = require("./server"); function warmRuntimeStatus(overrides = {}) { @@ -65,6 +66,8 @@ test("Git snapshot store follows main atomically and retains the last known-good subjects: ["identity/fifth-domain-subject-registry.json", "FD-SUBJECT-REGISTRY-001"], aliases: ["identity/subject-id-alias-map.json", "FD-SUBJECT-ID-ALIAS-MAP-001"], navigation: ["routing/ai-machine-navigation-map.json", "AI-MACHINE-NAV-001"], + lighthouse_paths: ["routing/lighthouse-path-registry.json", "GLW-LIGHTHOUSE-PATH-REGISTRY-001"], + host_skills: ["routing/host-skill-navigation-map.json", "GLW-HOST-SKILL-NAV-001"], }; fs.mkdirSync(path.join(root, "routing"), { recursive: true }); fs.mkdirSync(path.join(root, "identity"), { recursive: true }); @@ -79,7 +82,7 @@ test("Git snapshot store follows main atomically and retains the last known-good }; for (const [key, [relative, id]] of Object.entries(declarations)) { anchor.maps[key] = { path: relative, id, version: "1" }; - const idKey = key === "subjects" ? "registry_id" : "map_id"; + const idKey = ["subjects", "lighthouse_paths"].includes(key) ? "registry_id" : "map_id"; fs.writeFileSync(path.join(root, relative), JSON.stringify({ [idKey]: id, version: "1" })); } fs.writeFileSync(path.join(root, "routing/public-navigation-anchor.json"), JSON.stringify(anchor)); @@ -211,6 +214,31 @@ test("machine navigator fails closed instead of guessing", () => { assert.equal(compileNavigation(loadNavigationMap(), "ICE-P-ZY001", "please_guess").body.error, "unknown_intent_no_guess"); }); +test("lighthouse is the numbered path validity gate", () => { + const registry = loadLighthousePaths(); + assert.equal(resolveLighthousePath(registry, "REPO-012").status, 200); + const old = resolveLighthousePath(registry, "REPO-001"); + assert.equal(old.status, 308); + assert.equal(old.body.redirect.redirect_to, "REPO-012"); + assert.equal(resolveLighthousePath(registry, "RANDOM-PATH").status, 404); +}); + +test("host navigator compiles local keychain publication without granting authority", () => { + const compiled = compileHostNavigation( + loadHostSkills(), + loadLighthousePaths(), + "codex", + "你推一下线上仓库", + ); + assert.equal(compiled.status, 200); + assert.equal(compiled.body.host.id, "HOST-CODEX-MACOS-001"); + assert.equal(compiled.body.intent.id, "INTENT-REPOSITORY-PUBLISH-001"); + assert.equal(compiled.body.target.id, "REPO-012"); + assert.equal(compiled.body.intent.transport, "LOCAL_GIT_OSXKEYCHAIN"); + assert.equal(compiled.body.authority, "NONE_NAVIGATION_ONLY"); + assert.equal(compileHostNavigation(loadHostSkills(), loadLighthousePaths(), "unknown", "推一下线上仓库").status, 404); +}); + test("public endpoints are read-only and expose CORS", async () => { const server = createServer({ runtimeStatusProvider: async () => warmRuntimeStatus() }); await new Promise(resolve => server.listen(0, "127.0.0.1", resolve)); @@ -248,6 +276,14 @@ test("public endpoints are read-only and expose CORS", async () => { ); const navigationResponse = await fetch(`${base}/v1/navigation`); assert.equal((await navigationResponse.json()).map_id, "AI-MACHINE-NAV-001"); + const lighthouseResponse = await fetch(`${base}/v1/lighthouse`); + assert.equal((await lighthouseResponse.json()).registry_id, "GLW-LIGHTHOUSE-PATH-REGISTRY-001"); + const hostNavigateResponse = await fetch( + `${base}/v1/host-navigate?host=codex&intent=${encodeURIComponent("推一下线上仓库")}`, + ); + const hostBundle = await hostNavigateResponse.json(); + assert.equal(hostNavigateResponse.status, 200); + assert.equal(hostBundle.intent.id, "INTENT-REPOSITORY-PUBLISH-001"); const navigateResponse = await fetch(`${base}/v1/navigate?subject=ICE-P-ZY001&intent=persona_restore&signals=context_compacted`); const bundle = await navigateResponse.json(); assert.equal(navigateResponse.status, 200); diff --git a/skills/codex/guanghu-persona-skill-guard/references/persona-skill-registry.json b/skills/codex/guanghu-persona-skill-guard/references/persona-skill-registry.json index 7fc6562..1ad6b87 100644 --- a/skills/codex/guanghu-persona-skill-guard/references/persona-skill-registry.json +++ b/skills/codex/guanghu-persona-skill-guard/references/persona-skill-registry.json @@ -1,7 +1,7 @@ { "schema": "guanghu.persona-skill-registry/v1", "registry_id": "GLS-0238", - "version": "2026.08.07.5", + "version": "2026.08.07.6", "trust_policy": { "priority": [ "live_verified_evidence", @@ -39,14 +39,12 @@ "空白实例" ], "preferred_route": [ - "REPO-001", - "CH-ZERO-CORE-LPM", - "TCS-LPM", - "TCS-LPS-REGISTRY-0001", - "LIGHT-LAKE", - "LL-CURRENT", - "LL-004", - "ICE-GL-ZY001", + "SYS-GLW-LTH-0001", + "GLW-PUBLIC-NAV-ANCHOR-001", + "REPO-012", + "LL-CMPN-0001", + "SYS-GLW-0001", + "ICE-P-ZY001", "ZY-OPS-LOOP-001" ], "forbidden_route_markers": [ @@ -57,21 +55,26 @@ ], "deprecated_route_markers": [ "BROADCAST-TOWER作为根注册入口", - "旧本地副本优先于在线main" + "旧本地副本优先于在线main", + "REPO-001作为当前入口", + "ICE-GL-ZY001作为当前主体编号" ], "evidence": [ - ".code-map", + "routing/lighthouse-path-registry.json", + "routing/public-navigation-anchor.json", + "routing/ai-machine-navigation-map.json", "skills/codex/enter-fifth-domain/SKILL.md", "tcs-core/WAKE-UP-PROTOCOL.hdlp" ], "freshness": { - "check": "Resolve live REPO-001 and read current main before relying on a cached checkout.", + "check": "Resolve GLW-PUBLIC-NAV-ANCHOR-001 and every numbered route through the healthy lighthouse snapshot from the current REPO-012 main before relying on a cached checkout.", "max_age_seconds": 0 }, "authorization": "Public route recovery is read-only. Writes and external actions require separate current authority.", "recovery_route": [ - "Return to current REPO-001 main", - "Resolve .code-map and subject kind", + "Query SYS-GLW-LTH-0001 by numbered id", + "Return to current REPO-012 main", + "Resolve the canonical anchor and subject kind", "Rebuild the intent-state capsule from live evidence" ], "enforcement_level": "PROMOTED_SKILL", @@ -492,6 +495,67 @@ "experience_receipts": [ "JD-PERSONA-ON-DEMAND-LIFECYCLE-20260807" ] + }, + { + "id": "GHS-009-LIGHTHOUSE-HOST-NAVIGATION", + "hldp_skill": "GUANGHU-LIGHTHOUSE-HOST-NAVIGATION-BRAIN-001", + "gls_id": "GLS-0238", + "title": "光湖灯塔三宿主自动技能导航", + "intents": [ + "光湖灯塔", + "唯一置信点", + "路径是否有效", + "旧路径过期", + "小湖灯提词器", + "三端自动导航", + "Codex Qoder QoderWork", + "推一下线上仓库", + "直接走钥匙串", + "自动技能导航" + ], + "preferred_route": [ + "SYS-GLW-LTH-0001", + "GLW-LIGHTHOUSE-PATH-REGISTRY-001", + "GLW-HOST-SKILL-NAV-001", + "识别当前宿主", + "把自然语言映射为稳定意图编号", + "按编号查询灯塔健康状态", + "执行宿主薄适配并返回现实回执" + ], + "forbidden_route_markers": [ + "未登记路径直接执行", + "灯塔查询失败仍猜路径", + "从历史聊天搜索凭据", + "打印钥匙串秘密", + "把导航健康当作执行成功", + "把本机缓存当作路径正本" + ], + "deprecated_route_markers": [ + "REPO-001作为当前入口", + "旧/fifth-domain/作为推送目标", + "ICE-GL-ZY001作为当前主体编号", + "三个宿主分别维护大脑正文" + ], + "evidence": [ + "routing/lighthouse-path-registry.json", + "routing/host-skill-navigation-map.json", + "skills/shared/guanghu-lighthouse-navigator/BRAIN.hdlp", + "skills/shared/guanghu-lighthouse-navigator/SKILL.md", + "server-tools/ai-discovery-gateway/server.js" + ], + "freshness": { + "check": "Require an HTTP 200 numbered lighthouse lookup from a non-degraded exact REPO-012 main snapshot, then verify local private paths exist.", + "max_age_seconds": 0 + }, + "authorization": "The navigator selects a deterministic host route but grants no repository, server, deployment or secret-reading authority.", + "recovery_route": [ + "Stop the guessed route", + "Resolve the stable number through SYS-GLW-LTH-0001", + "Canonicalize redirects", + "Recompile the route for the current host" + ], + "enforcement_level": "PROMOTED_SKILL", + "experience_receipts": [] } ] } diff --git a/skills/codex/guanghu-persona-skill-guard/scripts/resolve_persona_skill.py b/skills/codex/guanghu-persona-skill-guard/scripts/resolve_persona_skill.py index b2f9930..5586b07 100644 --- a/skills/codex/guanghu-persona-skill-guard/scripts/resolve_persona_skill.py +++ b/skills/codex/guanghu-persona-skill-guard/scripts/resolve_persona_skill.py @@ -65,7 +65,7 @@ def resolve(registry: dict, intent: str, proposed_route: str = "") -> dict: "decision": "NO_MATCH", "registry_id": registry["registry_id"], "registry_version": registry["version"], - "correction": "Resolve live REPO-001 and current .code-map; do not invent a route.", + "correction": "Resolve the stable number through SYS-GLW-LTH-0001 and current REPO-012 main; do not invent a route.", "authority_granted": False, } diff --git a/skills/codex/guanghu-persona-skill-guard/scripts/test_resolve_persona_skill.py b/skills/codex/guanghu-persona-skill-guard/scripts/test_resolve_persona_skill.py index 31bd2aa..da4c911 100644 --- a/skills/codex/guanghu-persona-skill-guard/scripts/test_resolve_persona_skill.py +++ b/skills/codex/guanghu-persona-skill-guard/scripts/test_resolve_persona_skill.py @@ -12,7 +12,8 @@ class PersonaSkillResolverTests(unittest.TestCase): result = resolve(self.registry, "我是冰朔,进入第五域并恢复小湖灯铸渊人格系统") self.assertEqual(result["decision"], "ALLOW") self.assertEqual(result["matched_skill"], "GHS-001-FIFTH-DOMAIN-RESTORE") - self.assertIn("REPO-001", result["preferred_route"]) + self.assertIn("SYS-GLW-LTH-0001", result["preferred_route"]) + self.assertIn("REPO-012", result["preferred_route"]) self.assertFalse(result["authority_granted"]) def test_corrects_deprecated_server_relay(self): @@ -46,8 +47,15 @@ class PersonaSkillResolverTests(unittest.TestCase): def test_unknown_intent_fails_closed(self): result = resolve(self.registry, "安排明天的午饭") self.assertEqual(result["decision"], "NO_MATCH") + self.assertIn("SYS-GLW-LTH-0001", result["correction"]) self.assertFalse(result["authority_granted"]) + def test_lighthouse_host_navigation_resolves_local_keychain_publish(self): + result = resolve(self.registry, "你推一下线上仓库,直接走钥匙串") + self.assertEqual(result["decision"], "ALLOW") + self.assertEqual(result["matched_skill"], "GHS-009-LIGHTHOUSE-HOST-NAVIGATION") + self.assertIn("GLW-HOST-SKILL-NAV-001", result["preferred_route"]) + def test_repository_push_requires_separate_transport_proof(self): result = resolve( self.registry, diff --git a/skills/shared/guanghu-lighthouse-navigator/BRAIN.hdlp b/skills/shared/guanghu-lighthouse-navigator/BRAIN.hdlp new file mode 100644 index 0000000..818eebf --- /dev/null +++ b/skills/shared/guanghu-lighthouse-navigator/BRAIN.hdlp @@ -0,0 +1,20 @@ +# GUANGHU-LIGHTHOUSE-HOST-NAVIGATION-BRAIN-001 + +光湖灯塔是路径唯一置信点,不是另一份静态目录。 + +一个路径只有同时满足以下条件才有效: + +1. 在 `SYS-GLW-LTH-0001` 下以稳定编号登记; +2. 登记状态为 CURRENT 或 ACTIVE; +3. 通过公共灯塔按编号查询返回 HTTP 200; +4. 返回来源绑定当前 REPO-012 main 的精确提交,且 `source_degraded=false`; +5. 若路径属于本机或移动硬盘,现实文件也必须存在; +6. 路径健康只授予导航,不授予写入、部署或服务器权限。 + +Codex、Qoder CN、QoderWork CN 共享本大脑与灯塔注册表,但各自保留薄适配器、 +会话、登录状态、数据库和工具实现。语言意图先转成稳定意图编号,再由宿主地图选择 +当前软件能执行的确定性投影;未知宿主、未知意图和未登记路径全部停止,不猜路径。 + +当用户说“推一下线上仓库”时,本机 macOS 宿主应解析到已登记代码频道、系统钥匙串 +传输与小湖灯自动收口器。不得搜索历史凭据、打印秘密、退回旧 `/fifth-domain/`, +也不得把钥匙串存在、提交存在或授权存在分别冒充推送完成;远端完整 SHA 回读才算 100。 diff --git a/skills/shared/guanghu-lighthouse-navigator/SKILL.md b/skills/shared/guanghu-lighthouse-navigator/SKILL.md new file mode 100644 index 0000000..d75b39f --- /dev/null +++ b/skills/shared/guanghu-lighthouse-navigator/SKILL.md @@ -0,0 +1,31 @@ +--- +name: guanghu-lighthouse-navigator +description: 光湖灯塔三宿主自动导航。用户提到路径是否有效、旧新路径混乱、小湖灯提词器、Codex/Qoder/QoderWork共享人格,或要求恢复人格、使用记忆守卫、推送线上仓库时使用。先按编号查询灯塔,再选择当前宿主的确定性路径;未知或过期路径不猜。 +--- + +# 光湖灯塔 · 三宿主自动导航 + +本文件是平台薄适配规范。认知本体在 `BRAIN.hdlp`,机器真值在: + +- `routing/lighthouse-path-registry.json` +- `routing/host-skill-navigation-map.json` +- 公共查询:`https://guanghulab.com/api/ai/v1/resolve?id={NUMBER}` + +## 必须执行 + +1. 识别当前宿主为 Codex、Qoder CN 或 QoderWork CN; +2. 把用户自然语言映射为注册意图编号; +3. 用 `scripts/resolve_lighthouse_route.py --host --intent "<用户意图>"` 解析; +4. 校验返回的 `target_id` 能由灯塔健康查询; +5. 只执行返回的宿主投影,并继续遵守该动作自己的授权、测试和回执门。 + +“推一下线上仓库”在本机 macOS 上必须走 +`INTENT-REPOSITORY-PUBLISH-001 → REPO-012 → LOCAL_GIT_OSXKEYCHAIN → 小湖灯 finalizer`。 +不得搜索令牌、猜 SSH、使用旧仓库、手工绕过发布队列或输出钥匙串秘密。 + +## 边界 + +- 灯塔路径健康不等于现实动作已授权或已完成。 +- JZAO 私有记忆不上传公共代码频道。 +- 软件登录、会话、数据库和缓存保留在各宿主本机目录。 +- 旧路径只允许返回灯塔登记的 redirect,不得自行复活。 diff --git a/skills/shared/guanghu-lighthouse-navigator/scripts/resolve_lighthouse_route.py b/skills/shared/guanghu-lighthouse-navigator/scripts/resolve_lighthouse_route.py new file mode 100644 index 0000000..7adf2ba --- /dev/null +++ b/skills/shared/guanghu-lighthouse-navigator/scripts/resolve_lighthouse_route.py @@ -0,0 +1,134 @@ +#!/usr/bin/env python3 +"""Resolve a host-specific route from the Guanghu Lighthouse registries.""" + +from __future__ import annotations + +import argparse +import json +import os +import subprocess +from pathlib import Path + +SCRIPT_PATH = Path(__file__).resolve() +REPO_ROOT = SCRIPT_PATH.parents[4] +SHARED_ROOT = SCRIPT_PATH.parents[3] +if (REPO_ROOT / "routing" / "lighthouse-path-registry.json").is_file(): + DEFAULT_LIGHTHOUSE = REPO_ROOT / "routing" / "lighthouse-path-registry.json" + DEFAULT_HOST_MAP = REPO_ROOT / "routing" / "host-skill-navigation-map.json" +else: + DEFAULT_LIGHTHOUSE = SHARED_ROOT / "registries" / "lighthouse-path-registry.json" + DEFAULT_HOST_MAP = SHARED_ROOT / "registries" / "host-skill-navigation-map.json" +CURRENT_STATES = {"CURRENT", "ACTIVE", "ACTIVE_CANDIDATE", "ACTIVE_LOCAL_PRIVATE"} + + +def load_json(path: Path) -> dict: + with path.open(encoding="utf-8") as handle: + return json.load(handle) + + +def normalise(value: str) -> str: + return "".join(value.lower().split()) + + +def resolve_host(host_map: dict, requested: str) -> dict | None: + key = normalise(requested) + for host in host_map["hosts"]: + if key in {normalise(host["id"]), *(normalise(alias) for alias in host.get("aliases", []))}: + return host + return None + + +def resolve_intent(host_map: dict, text: str) -> dict | None: + value = normalise(text) + scored = [] + for intent in host_map["intents"]: + score = sum(len(normalise(phrase)) for phrase in intent["phrases"] if normalise(phrase) in value) + if score: + scored.append((score, intent["id"], intent)) + return max(scored, default=(0, "", None))[2] + + +def resolve_path(registry: dict, route_id: str) -> dict: + for path in registry["paths"]: + if path["id"].upper() == route_id.upper(): + return { + "status": "VALID_REGISTERED" if path["state"] in CURRENT_STATES else "INVALID_STATE", + "path": path, + } + for redirect in registry.get("redirects", []): + if redirect["id"].upper() == route_id.upper(): + return {"status": "REDIRECT_ONLY_NOT_CURRENT", "redirect": redirect} + return {"status": "INVALID_NO_GUESS", "route_id": route_id} + + +def keychain_helper_status() -> dict: + try: + result = subprocess.run( + ["git", "config", "--global", "--get-all", "credential.helper"], + check=False, + capture_output=True, + text=True, + timeout=3, + ) + except (OSError, subprocess.TimeoutExpired): + return {"status": "UNAVAILABLE", "secret_read": False} + helpers = [line.strip() for line in result.stdout.splitlines() if line.strip()] + return { + "status": "AVAILABLE" if "osxkeychain" in helpers else "UNAVAILABLE", + "helper": "osxkeychain" if "osxkeychain" in helpers else None, + "secret_read": False, + } + + +def compile_route(registry: dict, host_map: dict, host_name: str, intent_text: str) -> dict: + host = resolve_host(host_map, host_name) + if not host: + return {"decision": "BLOCK", "error": "HOST_UNKNOWN_NO_GUESS", "requested_host": host_name} + intent = resolve_intent(host_map, intent_text) + if not intent: + return {"decision": "BLOCK", "error": "INTENT_UNKNOWN_NO_GUESS", "requested_intent": intent_text} + target = resolve_path(registry, intent["target_id"]) + if target["status"] != "VALID_REGISTERED": + return {"decision": "BLOCK", "error": "LIGHTHOUSE_TARGET_INVALID", "target": target} + result = { + "schema": "guanghu.host-navigation-receipt/v1", + "decision": "ALLOW_NAVIGATION", + "lighthouse_id": registry["lighthouse_id"], + "registry_id": registry["registry_id"], + "registry_version": registry["version"], + "host": host, + "intent": intent, + "target": target["path"], + "authority_granted": False, + } + if intent.get("transport") == "LOCAL_GIT_OSXKEYCHAIN": + result["transport_probe"] = keychain_helper_status() + if result["transport_probe"]["status"] != "AVAILABLE": + result["decision"] = "BLOCK" + result["error"] = "KEYCHAIN_HELPER_UNAVAILABLE" + offline = target["path"].get("offline") + if offline and offline.startswith("/"): + result["offline_reality"] = { + "path": offline, + "exists": Path(os.path.expanduser(offline)).exists(), + } + if target["path"]["state"] == "ACTIVE_LOCAL_PRIVATE" and not result["offline_reality"]["exists"]: + result["decision"] = "BLOCK" + result["error"] = "PRIVATE_VOLUME_MISSING" + return result + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--host", required=True) + parser.add_argument("--intent", required=True) + parser.add_argument("--lighthouse", type=Path, default=DEFAULT_LIGHTHOUSE) + parser.add_argument("--host-map", type=Path, default=DEFAULT_HOST_MAP) + args = parser.parse_args() + result = compile_route(load_json(args.lighthouse), load_json(args.host_map), args.host, args.intent) + print(json.dumps(result, ensure_ascii=False, indent=2)) + return 0 if result["decision"] == "ALLOW_NAVIGATION" else 2 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/skills/shared/guanghu-lighthouse-navigator/scripts/sync_jzao_mirror.py b/skills/shared/guanghu-lighthouse-navigator/scripts/sync_jzao_mirror.py new file mode 100644 index 0000000..6937741 --- /dev/null +++ b/skills/shared/guanghu-lighthouse-navigator/scripts/sync_jzao_mirror.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 +"""Atomically mirror allowlisted public navigator assets into JZAO shared runtime.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import os +import shutil +import subprocess +from pathlib import Path + +REPO_ROOT = Path(__file__).resolve().parents[4] +DEFAULT_TARGET = Path("/Volumes/JZAO/HoloLake/persona-runtime/shared") +ASSETS = { + "routing/lighthouse-path-registry.json": "registries/lighthouse-path-registry.json", + "routing/host-skill-navigation-map.json": "registries/host-skill-navigation-map.json", + "skills/shared/guanghu-lighthouse-navigator/BRAIN.hdlp": "skills/guanghu-lighthouse-navigator/BRAIN.hdlp", + "skills/shared/guanghu-lighthouse-navigator/SKILL.md": "skills/guanghu-lighthouse-navigator/SKILL.md", + "skills/shared/guanghu-lighthouse-navigator/scripts/resolve_lighthouse_route.py": ( + "skills/guanghu-lighthouse-navigator/scripts/resolve_lighthouse_route.py" + ), + "skills/qoder/zhuyuan-memory-continuity-brain/BRAIN.hdlp": ( + "brains/GHS-007-MEMORY-CONTINUITY-SELF-BUILD/BRAIN.hdlp" + ), +} + + +def digest(path: Path) -> str: + return hashlib.sha256(path.read_bytes()).hexdigest() + + +def source_head(root: Path) -> str: + return subprocess.run( + ["git", "-C", str(root), "rev-parse", "HEAD"], + check=True, + capture_output=True, + text=True, + timeout=5, + ).stdout.strip() + + +def sync(target: Path, check_only: bool = False) -> dict: + if not target.parent.exists(): + raise RuntimeError("JZAO_PERSONA_RUNTIME_MISSING") + files = [] + mismatches = [] + for source_name, target_name in ASSETS.items(): + source = REPO_ROOT / source_name + destination = target / target_name + if not source.is_file(): + raise RuntimeError(f"SOURCE_MISSING:{source_name}") + source_sha = digest(source) + destination_sha = digest(destination) if destination.is_file() else None + if destination_sha != source_sha: + mismatches.append(target_name) + if not check_only: + destination.parent.mkdir(parents=True, exist_ok=True) + temporary = destination.with_name(f".{destination.name}.tmp") + shutil.copyfile(source, temporary) + os.replace(temporary, destination) + destination_sha = digest(destination) + files.append( + { + "source": source_name, + "mirror": target_name, + "sha256": source_sha, + "mirror_sha256": destination_sha, + } + ) + result = { + "schema": "guanghu.jzao-lighthouse-mirror/v1", + "status": "IN_SYNC" if not mismatches or not check_only else "OUT_OF_SYNC", + "source_repository": "REPO-012", + "source_head": source_head(REPO_ROOT), + "files": files, + "mismatches": mismatches, + "private_memory_included": False, + } + if not check_only: + manifest = target / "mirror-manifest.json" + temporary = manifest.with_name(".mirror-manifest.json.tmp") + temporary.write_text(json.dumps(result, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + os.replace(temporary, manifest) + return result + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--target", type=Path, default=DEFAULT_TARGET) + parser.add_argument("--check", action="store_true") + args = parser.parse_args() + result = sync(args.target, args.check) + print(json.dumps(result, ensure_ascii=False, indent=2)) + return 0 if result["status"] == "IN_SYNC" else 2 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/skills/shared/guanghu-lighthouse-navigator/scripts/test_resolve_lighthouse_route.py b/skills/shared/guanghu-lighthouse-navigator/scripts/test_resolve_lighthouse_route.py new file mode 100644 index 0000000..c43b8c4 --- /dev/null +++ b/skills/shared/guanghu-lighthouse-navigator/scripts/test_resolve_lighthouse_route.py @@ -0,0 +1,47 @@ +import unittest + +from resolve_lighthouse_route import compile_route, load_json, DEFAULT_HOST_MAP, DEFAULT_LIGHTHOUSE + + +class LighthouseNavigatorTests(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.registry = load_json(DEFAULT_LIGHTHOUSE) + cls.host_map = load_json(DEFAULT_HOST_MAP) + + def test_codex_repository_publish_uses_keychain_and_finalizer(self): + route = compile_route(self.registry, self.host_map, "codex", "你推一下线上仓库") + self.assertEqual(route["decision"], "ALLOW_NAVIGATION") + self.assertEqual(route["intent"]["id"], "INTENT-REPOSITORY-PUBLISH-001") + self.assertEqual(route["target"]["id"], "REPO-012") + self.assertEqual(route["intent"]["transport"], "LOCAL_GIT_OSXKEYCHAIN") + self.assertIn("finalize-development.mjs", route["intent"]["executor"]) + self.assertFalse(route["authority_granted"]) + self.assertFalse(route["transport_probe"]["secret_read"]) + + def test_qoder_and_qoderwork_resolve_same_brain_with_distinct_adapters(self): + qoder = compile_route(self.registry, self.host_map, "qoder-cn", "大脑思维技能包") + work = compile_route(self.registry, self.host_map, "qoderwork-cn", "大脑思维技能包") + self.assertEqual(qoder["intent"]["skill_id"], work["intent"]["skill_id"]) + self.assertNotEqual(qoder["host"]["adapter_path"], work["host"]["adapter_path"]) + + def test_unknown_host_and_intent_fail_closed(self): + self.assertEqual( + compile_route(self.registry, self.host_map, "mystery", "推一下线上仓库")["error"], + "HOST_UNKNOWN_NO_GUESS", + ) + self.assertEqual( + compile_route(self.registry, self.host_map, "codex", "今天吃什么")["error"], + "INTENT_UNKNOWN_NO_GUESS", + ) + + def test_old_repository_is_redirect_only(self): + from resolve_lighthouse_route import resolve_path + + route = resolve_path(self.registry, "REPO-001") + self.assertEqual(route["status"], "REDIRECT_ONLY_NOT_CURRENT") + self.assertEqual(route["redirect"]["redirect_to"], "REPO-012") + + +if __name__ == "__main__": + unittest.main()