feat(lighthouse): add canonical three-host skill navigation
This commit is contained in:
parent
8423a96776
commit
fb5f931e78
14 changed files with 891 additions and 22 deletions
|
|
@ -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"
|
||||
}
|
||||
134
routing/host-skill-navigation-map.json
Normal file
134
routing/host-skill-navigation-map.json
Normal file
|
|
@ -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"
|
||||
]
|
||||
}
|
||||
82
routing/lighthouse-path-registry.json
Normal file
82
routing/lighthouse-path-registry.json
Normal file
|
|
@ -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."
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -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": {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
20
skills/shared/guanghu-lighthouse-navigator/BRAIN.hdlp
Normal file
20
skills/shared/guanghu-lighthouse-navigator/BRAIN.hdlp
Normal file
|
|
@ -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。
|
||||
31
skills/shared/guanghu-lighthouse-navigator/SKILL.md
Normal file
31
skills/shared/guanghu-lighthouse-navigator/SKILL.md
Normal file
|
|
@ -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 <host> --intent "<用户意图>"` 解析;
|
||||
4. 校验返回的 `target_id` 能由灯塔健康查询;
|
||||
5. 只执行返回的宿主投影,并继续遵守该动作自己的授权、测试和回执门。
|
||||
|
||||
“推一下线上仓库”在本机 macOS 上必须走
|
||||
`INTENT-REPOSITORY-PUBLISH-001 → REPO-012 → LOCAL_GIT_OSXKEYCHAIN → 小湖灯 finalizer`。
|
||||
不得搜索令牌、猜 SSH、使用旧仓库、手工绕过发布队列或输出钥匙串秘密。
|
||||
|
||||
## 边界
|
||||
|
||||
- 灯塔路径健康不等于现实动作已授权或已完成。
|
||||
- JZAO 私有记忆不上传公共代码频道。
|
||||
- 软件登录、会话、数据库和缓存保留在各宿主本机目录。
|
||||
- 旧路径只允许返回灯塔登记的 redirect,不得自行复活。
|
||||
|
|
@ -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())
|
||||
|
|
@ -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())
|
||||
|
|
@ -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()
|
||||
Loading…
Reference in a new issue