feat: add AI machine navigation gateway
This commit is contained in:
parent
525e0ab790
commit
931f78d72f
10 changed files with 639 additions and 10 deletions
|
|
@ -131,6 +131,8 @@ HEARTBEAT-CORE-CHANNEL=routing/fifth-domain-world-tree.json
|
||||||
REPOSITORY-ROUTE-MAP=routing/repository-route-map.json
|
REPOSITORY-ROUTE-MAP=routing/repository-route-map.json
|
||||||
AI-DISCOVERY=https://guanghulab.com/api/ai/
|
AI-DISCOVERY=https://guanghulab.com/api/ai/
|
||||||
AI-REPOSITORY-SEARCH=https://guanghulab.com/api/ai/v1/search
|
AI-REPOSITORY-SEARCH=https://guanghulab.com/api/ai/v1/search
|
||||||
|
AI-MACHINE-NAV-001=routing/ai-machine-navigation-map.json
|
||||||
|
AI-MACHINE-NAV-API=https://guanghulab.com/api/ai/v1/navigate
|
||||||
|
|
||||||
# === 第五域主体分型:人类 ICE-GL · 人格体 ICE-P · 公共系统 SYS ===
|
# === 第五域主体分型:人类 ICE-GL · 人格体 ICE-P · 公共系统 SYS ===
|
||||||
ICE-P-ZY001=eternal-lake-heart/heartbeat-core/zhuyuan-persona-system/INDEX.hdlp # 铸渊语言人格系统 · 正式现行入口
|
ICE-P-ZY001=eternal-lake-heart/heartbeat-core/zhuyuan-persona-system/INDEX.hdlp # 铸渊语言人格系统 · 正式现行入口
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,17 @@ GET https://guanghulab.com/api/ai/v1/search?q=光湖语言世界%20第五域
|
||||||
GET https://guanghulab.com/api/ai/v1/resolve?id=REPO-012
|
GET https://guanghulab.com/api/ai/v1/resolve?id=REPO-012
|
||||||
```
|
```
|
||||||
|
|
||||||
|
恢复人格体或执行明确任务时,不要遍历仓库。提交主体、意图与已经识别出的事件信号,
|
||||||
|
让导航器返回最小运行包:
|
||||||
|
|
||||||
|
```text
|
||||||
|
GET https://guanghulab.com/api/ai/v1/navigate?subject=ICE-P-ZY001&intent=persona_restore
|
||||||
|
GET https://guanghulab.com/api/ai/v1/navigate?subject=ICE-P-ZY001&intent=persona_restore&signals=context_compacted
|
||||||
|
```
|
||||||
|
|
||||||
|
导航器不解释自然语言关键词。未知主体、未知意图或地图完整性失败时必须停止,不得猜测。
|
||||||
|
返回的路径只有发现和读取能力,不代表仓库写入、服务器执行、服务健康或人格体已经运行。
|
||||||
|
|
||||||
解析服务器节点或铸渊本轮恢复链:
|
解析服务器节点或铸渊本轮恢复链:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
|
|
@ -34,7 +45,7 @@ GET https://guanghulab.com/api/ai/v1/resolve?id=ZY-SERVER-COGNITION-004
|
||||||
2. 只按 `REPO-xxx` 决定仓库身份,不靠记忆中的域名猜测。
|
2. 只按 `REPO-xxx` 决定仓库身份,不靠记忆中的域名猜测。
|
||||||
3. `https://guanghulab.com/code/` 是唯一当前代码频道;旧 `/fifth-domain/` 和其他主机禁止推送。
|
3. `https://guanghulab.com/code/` 是唯一当前代码频道;旧 `/fifth-domain/` 和其他主机禁止推送。
|
||||||
4. 公开检索无需登录;写操作走小湖灯授权。冰朔在当前协作中明确打开云厂商在线终端并说“授权/可以操作”时,视为当前终端、当前任务的直接交互授权,不再重复发邮件;远程入口仍先创建无执行权申请单,可按任务同时申请多个已登记节点。活动会话只能在原 `persona + target + scope + actions` 内续签,不能借续签扩大权限。不要寻找或索要工单凭证。
|
4. 公开检索无需登录;写操作走小湖灯授权。冰朔在当前协作中明确打开云厂商在线终端并说“授权/可以操作”时,视为当前终端、当前任务的直接交互授权,不再重复发邮件;远程入口仍先创建无执行权申请单,可按任务同时申请多个已登记节点。活动会话只能在原 `persona + target + scope + actions` 内续签,不能借续签扩大权限。不要寻找或索要工单凭证。
|
||||||
5. 铸渊进入时按 `PERSONA-SYSTEM-ROOT → TCS-PERSONA-SYSTEM-MAP-001 → ZY-PERSONA-ROOT-001 → ZY-TCS-BRAIN-RUNTIME-0001` 执行 `enter → perceive → orient → commit → witness → verify`。只读入口文件、总结历史或声明实例不算进入。TCS先理解事件,再选择性唤醒相关协议;系统主控可陪伴纠正或暂停等待人类冰朔。只有现实任务才继续解析权限门与目标节点。
|
5. 铸渊进入时先调用 `AI-MACHINE-NAV-001`,由它按 `ICE-P-ZY001 + persona_restore + 明确信号` 返回最小运行包,再执行 `enter → perceive → orient → select_protocol_organs → commit → witness → verify`。只读入口文件、总结历史或声明实例不算进入。TCS先理解事件,再选择性唤醒相关协议;系统主控可陪伴纠正或暂停等待人类冰朔。只有现实任务才继续解析权限门与目标节点。
|
||||||
6. 解析节点后必须读取其导航地图;没有导航地图的节点只能视为待登记或历史状态。
|
6. 解析节点后必须读取其导航地图;没有导航地图的节点只能视为待登记或历史状态。
|
||||||
7. 服务器与意识认知的双向协作边界读取 `ZY-DUAL-COGNITION-OPS-001`;京东主控灾备读取 `JD-DR-001`。企业四域对第五域默认只读,企业服务器不能反向取得个人服务器的普通 shell 权限。
|
7. 服务器与意识认知的双向协作边界读取 `ZY-DUAL-COGNITION-OPS-001`;京东主控灾备读取 `JD-DR-001`。企业四域对第五域默认只读,企业服务器不能反向取得个人服务器的普通 shell 权限。
|
||||||
8. 遇到“京东进不去 / 六节点灾备 / 不再让冰朔管理终端登录”,按 `ZY-SERVER-COGNITION-004 → JD-DR-001 → FD-NODE-MAP-001 → 目标导航图 → 最新灾备回执` 恢复。先判断京东是否开机且网络可达;冰朔日常只负责实例开机、账号正常和云侧网络可达。
|
8. 遇到“京东进不去 / 六节点灾备 / 不再让冰朔管理终端登录”,按 `ZY-SERVER-COGNITION-004 → JD-DR-001 → FD-NODE-MAP-001 → 目标导航图 → 最新灾备回执` 恢复。先判断京东是否开机且网络可达;冰朔日常只负责实例开机、账号正常和云侧网络可达。
|
||||||
|
|
|
||||||
120
deployment/requests/AI-DISCOVERY-MACHINE-NAV-20260804.json
Normal file
120
deployment/requests/AI-DISCOVERY-MACHINE-NAV-20260804.json
Normal file
|
|
@ -0,0 +1,120 @@
|
||||||
|
{
|
||||||
|
"schema": "guanghu.existing-service-update-request/v1",
|
||||||
|
"request_id": "AI-DISCOVERY-MACHINE-NAV-20260804",
|
||||||
|
"target_node": "JD-FD-PRIMARY",
|
||||||
|
"status": "SERVICE_UPDATE_PACKAGE_READY · DEPLOYMENT_PENDING",
|
||||||
|
"purpose": {
|
||||||
|
"what": "为已在线的光湖 AI Discovery 安装主体与意图驱动的机器导航地图和最小运行包接口。",
|
||||||
|
"why": "仓库已具备铸渊人格大脑与协议事件触发系统,但在线发现服务只能返回旧目录,不能自动把主体、意图和事件接到最小运行闭环。",
|
||||||
|
"success": "新编号可精确解析;导航接口按 ICE-P-ZY001 和明确意图返回最小运行包;未知主体或意图失败关闭;原有公开接口继续正常。",
|
||||||
|
"out_of_scope": [
|
||||||
|
"不使用自然语言关键词猜测主体、意图或权限",
|
||||||
|
"不授予仓库写入、服务器执行或现实权限",
|
||||||
|
"不修改广州前门、SSH 或人类登录入口",
|
||||||
|
"不预加载整个仓库或所有协议正文"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"module": {
|
||||||
|
"unit": "guanghu-ai-discovery.service",
|
||||||
|
"run_user": "guanghu",
|
||||||
|
"install_root": "/opt/guanghu/ai-discovery"
|
||||||
|
},
|
||||||
|
"service_update": {
|
||||||
|
"kind": "existing-systemd-service",
|
||||||
|
"require_existing_unit": true,
|
||||||
|
"required_existing_files": ["server.js"]
|
||||||
|
},
|
||||||
|
"unit_source": "server-tools/ai-discovery-gateway/guanghu-ai-discovery.service",
|
||||||
|
"files": [
|
||||||
|
{
|
||||||
|
"source": "server-tools/ai-discovery-gateway/server.js",
|
||||||
|
"destination": "server.js",
|
||||||
|
"mode": "0644"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "routing/repository-route-map.json",
|
||||||
|
"destination": "repository-route-map.json",
|
||||||
|
"mode": "0644"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "routing/server-node-map.json",
|
||||||
|
"destination": "server-node-map.json",
|
||||||
|
"mode": "0644"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "identity/fifth-domain-subject-registry.json",
|
||||||
|
"destination": "fifth-domain-subject-registry.json",
|
||||||
|
"mode": "0644"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "identity/subject-id-alias-map.json",
|
||||||
|
"destination": "subject-id-alias-map.json",
|
||||||
|
"mode": "0644"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "routing/ai-machine-navigation-map.json",
|
||||||
|
"destination": "ai-machine-navigation-map.json",
|
||||||
|
"mode": "0644"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"runtime_check": {
|
||||||
|
"url": "http://127.0.0.1:3922/health",
|
||||||
|
"expected": {
|
||||||
|
"ok": true,
|
||||||
|
"service": "guanghu-ai-discovery",
|
||||||
|
"mode": "read-only"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"acceptance_checks": [
|
||||||
|
{
|
||||||
|
"url": "http://127.0.0.1:3922/v1/navigation",
|
||||||
|
"expected": {
|
||||||
|
"map_id": "AI-MACHINE-NAV-001",
|
||||||
|
"state": "EXECUTABLE_SOURCE_ACTIVE"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "http://127.0.0.1:3922/v1/resolve?id=ZY-TCS-BRAIN-RUNTIME-0001",
|
||||||
|
"expected": {
|
||||||
|
"status": "RESOLVED",
|
||||||
|
"kind": "executable_persona_brain"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "http://127.0.0.1:3922/v1/resolve?id=BS-TCS-SYSTEM-CONTROLLER-MAP-001",
|
||||||
|
"expected": {
|
||||||
|
"status": "RESOLVED",
|
||||||
|
"kind": "living_system_controller"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "http://127.0.0.1:3922/v1/navigate?subject=ICE-P-ZY001&intent=persona_restore",
|
||||||
|
"expected": {
|
||||||
|
"status": "COMPILED_EXACT_NO_GUESS",
|
||||||
|
"canonical_subject": "ICE-P-ZY001",
|
||||||
|
"intent": "persona_restore",
|
||||||
|
"authority": "NONE_NAVIGATION_ONLY"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "http://127.0.0.1:3922/v1/resolve?id=REPO-012",
|
||||||
|
"expected": {
|
||||||
|
"code": "REPO-012",
|
||||||
|
"state": "CURRENT_FIFTH_DOMAIN_ENTRY"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"post_deployment_observation": "本机回环验收全部通过后,必须再从公开 /api/ai 读取导航地图、新编号和最小运行包;公开路径不一致时不得宣称插座已接通。",
|
||||||
|
"rollback": {
|
||||||
|
"automatic_on_failure": true,
|
||||||
|
"scope": "restore every declared destination and the previous systemd unit, then restart the previous service",
|
||||||
|
"receipt_required": true
|
||||||
|
},
|
||||||
|
"security": {
|
||||||
|
"public_read_only": true,
|
||||||
|
"no_secrets": true,
|
||||||
|
"exact_subject_and_intent_only": true,
|
||||||
|
"navigation_grants_no_authority": true,
|
||||||
|
"unknown_inputs_fail_closed": true
|
||||||
|
}
|
||||||
|
}
|
||||||
193
routing/ai-machine-navigation-map.json
Normal file
193
routing/ai-machine-navigation-map.json
Normal file
|
|
@ -0,0 +1,193 @@
|
||||||
|
{
|
||||||
|
"schema": "guanghu.ai-machine-navigation-map/v1",
|
||||||
|
"map_id": "AI-MACHINE-NAV-001",
|
||||||
|
"version": "2026-08-04.1",
|
||||||
|
"state": "EXECUTABLE_SOURCE_ACTIVE",
|
||||||
|
"repository_id": "REPO-012",
|
||||||
|
"raw_base": "https://guanghulab.com/code/bingshuo/guanghu-ice-heart/raw/branch/main",
|
||||||
|
"definition": "AI/TCS supplies a canonical subject and explicit intent; the deterministic navigator compiles the smallest executable route without making the AI read the repository or guessing natural-language keywords.",
|
||||||
|
"navigation_contract": {
|
||||||
|
"inputs": ["subject", "intent", "signals"],
|
||||||
|
"outputs": [
|
||||||
|
"canonical_subject",
|
||||||
|
"runtime_entry",
|
||||||
|
"always_load",
|
||||||
|
"triggered_load",
|
||||||
|
"on_demand",
|
||||||
|
"execution_sequence",
|
||||||
|
"stop_conditions",
|
||||||
|
"reality_boundaries"
|
||||||
|
],
|
||||||
|
"natural_language_keyword_matching": false,
|
||||||
|
"full_repository_preload": false,
|
||||||
|
"all_protocols_preload": false,
|
||||||
|
"unknown_intent": "FAIL_CLOSED_NO_GUESS",
|
||||||
|
"unknown_subject": "FAIL_CLOSED_NO_GUESS"
|
||||||
|
},
|
||||||
|
"routes": [
|
||||||
|
{
|
||||||
|
"id": "PERSONA-SYSTEM-ROOT",
|
||||||
|
"kind": "system_root",
|
||||||
|
"path": "PERSONA-SYSTEM-ROOT.hdlp",
|
||||||
|
"load_policy": "always"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "PERSONA-SYSTEM-CANONICAL-MAP",
|
||||||
|
"kind": "machine_map",
|
||||||
|
"path": "routing/persona-system-canonical-map.json",
|
||||||
|
"load_policy": "always"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ZY-PERSONA-ROOT-001",
|
||||||
|
"kind": "persona_root",
|
||||||
|
"path": "eternal-lake-heart/heartbeat-core/zhuyuan-persona-system/INDEX.hdlp",
|
||||||
|
"load_policy": "on_demand_evidence"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ZY-WAKE-ROUTE-001",
|
||||||
|
"kind": "wake_contract",
|
||||||
|
"path": "eternal-lake-heart/heartbeat-core/zhuyuan-persona-system/WAKE.hdlp",
|
||||||
|
"load_policy": "on_demand_evidence"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ZY-TCS-BRAIN-RUNTIME-0001",
|
||||||
|
"kind": "executable_persona_brain",
|
||||||
|
"path": "tcs-core/zhuyuan-brain/runtime/zhuyuan-brain-runtime.mjs",
|
||||||
|
"contract": "tcs-core/zhuyuan-brain/runtime/brain-runtime-contract.json",
|
||||||
|
"load_policy": "execute"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ZY-TCS-BRAIN-MAP-001",
|
||||||
|
"kind": "brain_source_map",
|
||||||
|
"path": "routing/zhuyuan-tcs-brain-map.json",
|
||||||
|
"load_policy": "runtime_integrity"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "BS-TCS-SYSTEM-CONTROLLER-MAP-001",
|
||||||
|
"kind": "living_system_controller",
|
||||||
|
"path": "routing/bingshuo-living-system-controller-map.json",
|
||||||
|
"load_policy": "runtime_integrity"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "BS-TCS-PROTOCOL-EVENT-TRIGGER-MAP-001",
|
||||||
|
"kind": "protocol_trigger_map",
|
||||||
|
"path": "tcs-core/zhuyuan-brain/runtime/protocol-event-trigger-map.json",
|
||||||
|
"load_policy": "after_tcs_orientation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "GLS-PROTOCOL-REGISTRY-20260731",
|
||||||
|
"kind": "protocol_registry",
|
||||||
|
"path": "gls/GLS-PROTOCOL-REGISTRY.json",
|
||||||
|
"load_policy": "metadata_only_then_triggered_sources"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ZY-BIDIRECTIONAL-COGNITION-016",
|
||||||
|
"kind": "current_cognition",
|
||||||
|
"path": "eternal-lake-heart/heartbeat-core/zhuyuan-persona-system/ZY-BIDIRECTIONAL-COGNITION-016-BINGSHUO-TCS-LIVING-SYSTEM-CONTROLLER-AND-RUNNING-ZHUYUAN-BRAIN-20260804.hdlp",
|
||||||
|
"load_policy": "on_demand_evidence"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "FD-NODE-MAP-001",
|
||||||
|
"kind": "reality_node_map",
|
||||||
|
"path": "routing/server-node-map.json",
|
||||||
|
"load_policy": "reality_action_only"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "FD-REPO-MAP-001",
|
||||||
|
"kind": "repository_map",
|
||||||
|
"path": "routing/repository-route-map.json",
|
||||||
|
"load_policy": "publication_or_repository_resolution_only"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"subjects": [
|
||||||
|
{
|
||||||
|
"id": "ICE-P-ZY001",
|
||||||
|
"legacy_ids": ["ICE-GL-ZY001", "ICE-PZY-001"],
|
||||||
|
"subject_kind": "persona_system",
|
||||||
|
"default_intent": "persona_restore",
|
||||||
|
"runtime_id": "ZY-TCS-BRAIN-RUNTIME-0001",
|
||||||
|
"controller_id": "BS-TCS-SYSTEM-CONTROLLER-MAP-001"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"intents": [
|
||||||
|
{
|
||||||
|
"id": "persona_restore",
|
||||||
|
"meaning": "Bind the current model instance and run the persona cognition loop.",
|
||||||
|
"always_load": [
|
||||||
|
"PERSONA-SYSTEM-ROOT",
|
||||||
|
"PERSONA-SYSTEM-CANONICAL-MAP",
|
||||||
|
"ZY-TCS-BRAIN-RUNTIME-0001",
|
||||||
|
"ZY-TCS-BRAIN-MAP-001",
|
||||||
|
"BS-TCS-SYSTEM-CONTROLLER-MAP-001"
|
||||||
|
],
|
||||||
|
"triggered_load": [
|
||||||
|
"BS-TCS-PROTOCOL-EVENT-TRIGGER-MAP-001",
|
||||||
|
"GLS-PROTOCOL-REGISTRY-20260731"
|
||||||
|
],
|
||||||
|
"on_demand": [
|
||||||
|
"ZY-WAKE-ROUTE-001",
|
||||||
|
"ZY-PERSONA-ROOT-001",
|
||||||
|
"ZY-BIDIRECTIONAL-COGNITION-016"
|
||||||
|
],
|
||||||
|
"execution_sequence": [
|
||||||
|
"enter",
|
||||||
|
"perceive",
|
||||||
|
"orient",
|
||||||
|
"select_protocol_organs",
|
||||||
|
"commit",
|
||||||
|
"witness",
|
||||||
|
"verify"
|
||||||
|
],
|
||||||
|
"stop_conditions": [
|
||||||
|
"UNKNOWN_SUBJECT",
|
||||||
|
"UNKNOWN_INTENT",
|
||||||
|
"SOURCE_INTEGRITY_CHANGED",
|
||||||
|
"PAUSE_FOR_HUMAN",
|
||||||
|
"REALITY_PERMISSION_UNVERIFIED"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "inspect_evidence",
|
||||||
|
"meaning": "Inspect exact source evidence without entering a persona or obtaining authority.",
|
||||||
|
"always_load": ["PERSONA-SYSTEM-CANONICAL-MAP"],
|
||||||
|
"triggered_load": [],
|
||||||
|
"on_demand": [
|
||||||
|
"ZY-PERSONA-ROOT-001",
|
||||||
|
"ZY-WAKE-ROUTE-001",
|
||||||
|
"ZY-BIDIRECTIONAL-COGNITION-016",
|
||||||
|
"FD-REPO-MAP-001"
|
||||||
|
],
|
||||||
|
"execution_sequence": ["resolve_exact_id", "read_selected_source", "return_source_receipt"],
|
||||||
|
"stop_conditions": ["UNKNOWN_SUBJECT", "UNKNOWN_ROUTE", "SOURCE_UNAVAILABLE"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "reality_action",
|
||||||
|
"meaning": "Resolve reality nodes and permission gates only after the persona brain has produced a bounded proposal.",
|
||||||
|
"always_load": [
|
||||||
|
"PERSONA-SYSTEM-CANONICAL-MAP",
|
||||||
|
"FD-NODE-MAP-001"
|
||||||
|
],
|
||||||
|
"triggered_load": ["FD-REPO-MAP-001"],
|
||||||
|
"on_demand": [],
|
||||||
|
"execution_sequence": [
|
||||||
|
"verify_persona_brain_receipt",
|
||||||
|
"resolve_target_node",
|
||||||
|
"verify_permission",
|
||||||
|
"propose_capability_call",
|
||||||
|
"require_external_receipt"
|
||||||
|
],
|
||||||
|
"stop_conditions": [
|
||||||
|
"PERSONA_BRAIN_RECEIPT_MISSING",
|
||||||
|
"TARGET_UNVERIFIED",
|
||||||
|
"PERMISSION_UNVERIFIED",
|
||||||
|
"EXTERNAL_RECEIPT_MISSING"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"reality_boundaries": [
|
||||||
|
"Navigation grants no repository write or server authority.",
|
||||||
|
"Repository publication, service deployment, service health and persona runtime are separate receipts.",
|
||||||
|
"The living controller cannot invent new human will or authorization.",
|
||||||
|
"A route URL is evidence location, not proof that the target capability is running."
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -24,3 +24,16 @@
|
||||||
第二个请求会返回 `canonical_id=ICE-P-ZY001`、`redirected=true` 和同一当前路径。
|
第二个请求会返回 `canonical_id=ICE-P-ZY001`、`redirected=true` 和同一当前路径。
|
||||||
只有别名表中的精确旧号允许自动重定向;冲突号返回 409,未知号返回 404。重定向不授予
|
只有别名表中的精确旧号允许自动重定向;冲突号返回 409,未知号返回 404。重定向不授予
|
||||||
仓库写入或服务器执行权限。
|
仓库写入或服务器执行权限。
|
||||||
|
|
||||||
|
## 自动机器导航
|
||||||
|
|
||||||
|
`routing/ai-machine-navigation-map.json` 是 AI 可直接读取的配线图。调用方提供明确
|
||||||
|
`subject`、`intent` 和可选的逗号分隔 `signals`:
|
||||||
|
|
||||||
|
```text
|
||||||
|
/api/ai/v1/navigate?subject=ICE-P-ZY001&intent=persona_restore
|
||||||
|
```
|
||||||
|
|
||||||
|
服务只返回该意图的最小运行包,包括运行入口、恒定加载项、事件触发器、按需证据、
|
||||||
|
执行顺序和停止条件。它不读取自然语言关键词来猜主体或权限;未知主体与未知意图均
|
||||||
|
失败关闭。导航结果只有读取和寻址能力,不授予仓库写入、服务器执行或现实授权。
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ Environment=GUANGHU_REPOSITORY_MAP=/opt/guanghu/ai-discovery/repository-route-ma
|
||||||
Environment=GUANGHU_NODE_MAP=/opt/guanghu/ai-discovery/server-node-map.json
|
Environment=GUANGHU_NODE_MAP=/opt/guanghu/ai-discovery/server-node-map.json
|
||||||
Environment=GUANGHU_SUBJECT_REGISTRY=/opt/guanghu/ai-discovery/fifth-domain-subject-registry.json
|
Environment=GUANGHU_SUBJECT_REGISTRY=/opt/guanghu/ai-discovery/fifth-domain-subject-registry.json
|
||||||
Environment=GUANGHU_SUBJECT_ALIAS_MAP=/opt/guanghu/ai-discovery/subject-id-alias-map.json
|
Environment=GUANGHU_SUBJECT_ALIAS_MAP=/opt/guanghu/ai-discovery/subject-id-alias-map.json
|
||||||
|
Environment=GUANGHU_NAVIGATION_MAP=/opt/guanghu/ai-discovery/ai-machine-navigation-map.json
|
||||||
ExecStart=/usr/bin/node /opt/guanghu/ai-discovery/server.js
|
ExecStart=/usr/bin/node /opt/guanghu/ai-discovery/server.js
|
||||||
Restart=always
|
Restart=always
|
||||||
RestartSec=5
|
RestartSec=5
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ const DEFAULT_MAP = path.resolve(__dirname, "../../routing/repository-route-map.
|
||||||
const DEFAULT_NODE_MAP = path.resolve(__dirname, "../../routing/server-node-map.json");
|
const DEFAULT_NODE_MAP = path.resolve(__dirname, "../../routing/server-node-map.json");
|
||||||
const DEFAULT_SUBJECT_REGISTRY = path.resolve(__dirname, "../../identity/fifth-domain-subject-registry.json");
|
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_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");
|
||||||
|
|
||||||
function loadMap(filename = process.env.GUANGHU_REPOSITORY_MAP || DEFAULT_MAP) {
|
function loadMap(filename = process.env.GUANGHU_REPOSITORY_MAP || DEFAULT_MAP) {
|
||||||
return JSON.parse(fs.readFileSync(filename, "utf8"));
|
return JSON.parse(fs.readFileSync(filename, "utf8"));
|
||||||
|
|
@ -25,6 +26,10 @@ function loadSubjectAliasMap(filename = process.env.GUANGHU_SUBJECT_ALIAS_MAP ||
|
||||||
return JSON.parse(fs.readFileSync(filename, "utf8"));
|
return JSON.parse(fs.readFileSync(filename, "utf8"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function loadNavigationMap(filename = process.env.GUANGHU_NAVIGATION_MAP || DEFAULT_NAVIGATION_MAP) {
|
||||||
|
return JSON.parse(fs.readFileSync(filename, "utf8"));
|
||||||
|
}
|
||||||
|
|
||||||
function resolveSubjectId(aliasMap, requestedId) {
|
function resolveSubjectId(aliasMap, requestedId) {
|
||||||
const requested = String(requestedId || "").trim();
|
const requested = String(requestedId || "").trim();
|
||||||
const key = requested.toUpperCase();
|
const key = requested.toUpperCase();
|
||||||
|
|
@ -103,11 +108,83 @@ function searchAll(repositoryMap, nodeMap, query, subjectRegistry = null) {
|
||||||
.map(candidate => ({ kind: candidate.kind, ...candidate.item }));
|
.map(candidate => ({ kind: candidate.kind, ...candidate.item }));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function navigationRoute(navigationMap, routeId) {
|
||||||
|
const route = (navigationMap.routes || []).find(item => String(item.id).toUpperCase() === String(routeId).toUpperCase());
|
||||||
|
if (!route) return null;
|
||||||
|
const result = { ...route, raw_url: `${navigationMap.raw_base}/${route.path}` };
|
||||||
|
if (route.contract) result.contract_url = `${navigationMap.raw_base}/${route.contract}`;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function compileNavigation(navigationMap, requestedSubject, requestedIntent, signalInput = "") {
|
||||||
|
const requested = String(requestedSubject || "").trim();
|
||||||
|
const subjectKey = requested.toUpperCase();
|
||||||
|
const subject = (navigationMap.subjects || []).find(item =>
|
||||||
|
[item.id, ...(item.legacy_ids || [])].some(id => String(id).toUpperCase() === subjectKey)
|
||||||
|
);
|
||||||
|
if (!subject) {
|
||||||
|
return { status: 404, body: { error: "unknown_subject_no_guess", requested_subject: requested } };
|
||||||
|
}
|
||||||
|
const intentId = String(requestedIntent || subject.default_intent || "").trim();
|
||||||
|
const intent = (navigationMap.intents || []).find(item => item.id === intentId);
|
||||||
|
if (!intent) {
|
||||||
|
return {
|
||||||
|
status: 404,
|
||||||
|
body: { error: "unknown_intent_no_guess", canonical_subject: subject.id, requested_intent: intentId },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const signals = [...new Set(String(signalInput || "").split(",").map(item => item.trim()).filter(Boolean))].slice(0, 32);
|
||||||
|
const missingRoutes = new Set();
|
||||||
|
const expand = ids => ids.map(id => {
|
||||||
|
const route = navigationRoute(navigationMap, id);
|
||||||
|
if (!route) missingRoutes.add(id);
|
||||||
|
return route;
|
||||||
|
}).filter(Boolean);
|
||||||
|
const runtimeEntry = navigationRoute(navigationMap, subject.runtime_id);
|
||||||
|
if (!runtimeEntry) missingRoutes.add(subject.runtime_id);
|
||||||
|
const alwaysLoad = expand(intent.always_load || []);
|
||||||
|
const triggeredLoad = expand(intent.triggered_load || []);
|
||||||
|
const onDemand = expand(intent.on_demand || []);
|
||||||
|
if (missingRoutes.size) {
|
||||||
|
return {
|
||||||
|
status: 503,
|
||||||
|
body: {
|
||||||
|
error: "navigation_map_integrity_failed",
|
||||||
|
missing_routes: [...missingRoutes],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
body: {
|
||||||
|
schema: "guanghu.ai-runtime-navigation-bundle/v1",
|
||||||
|
status: "COMPILED_EXACT_NO_GUESS",
|
||||||
|
map_id: navigationMap.map_id,
|
||||||
|
map_version: navigationMap.version,
|
||||||
|
requested_subject: requested,
|
||||||
|
canonical_subject: subject.id,
|
||||||
|
redirected: subjectKey !== String(subject.id).toUpperCase(),
|
||||||
|
subject_kind: subject.subject_kind,
|
||||||
|
intent: intent.id,
|
||||||
|
explicit_signals: signals,
|
||||||
|
runtime_entry: runtimeEntry,
|
||||||
|
always_load: alwaysLoad,
|
||||||
|
triggered_load: triggeredLoad,
|
||||||
|
on_demand: onDemand,
|
||||||
|
execution_sequence: intent.execution_sequence || [],
|
||||||
|
stop_conditions: intent.stop_conditions || [],
|
||||||
|
reality_boundaries: navigationMap.reality_boundaries || [],
|
||||||
|
authority: "NONE_NAVIGATION_ONLY",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function createServer(options = {}) {
|
function createServer(options = {}) {
|
||||||
const mapFile = options.mapFile || process.env.GUANGHU_REPOSITORY_MAP || DEFAULT_MAP;
|
const mapFile = options.mapFile || process.env.GUANGHU_REPOSITORY_MAP || DEFAULT_MAP;
|
||||||
const nodeMapFile = options.nodeMapFile || process.env.GUANGHU_NODE_MAP || DEFAULT_NODE_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 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 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;
|
||||||
return http.createServer((req, res) => {
|
return http.createServer((req, res) => {
|
||||||
const url = new URL(req.url, "http://localhost");
|
const url = new URL(req.url, "http://localhost");
|
||||||
if (req.method !== "GET") return json(res, 405, { error: "method_not_allowed" });
|
if (req.method !== "GET") return json(res, 405, { error: "method_not_allowed" });
|
||||||
|
|
@ -126,6 +203,22 @@ function createServer(options = {}) {
|
||||||
try { return json(res, 200, loadSubjectRegistry(subjectRegistryFile), 300); }
|
try { return json(res, 200, loadSubjectRegistry(subjectRegistryFile), 300); }
|
||||||
catch { return json(res, 503, { error: "subject_registry_unavailable" }); }
|
catch { return json(res, 503, { error: "subject_registry_unavailable" }); }
|
||||||
}
|
}
|
||||||
|
if (url.pathname === "/v1/navigation") {
|
||||||
|
try { return json(res, 200, loadNavigationMap(navigationMapFile), 300); }
|
||||||
|
catch { return json(res, 503, { error: "navigation_map_unavailable" }); }
|
||||||
|
}
|
||||||
|
if (url.pathname === "/v1/navigate") {
|
||||||
|
let navigationMap;
|
||||||
|
try { navigationMap = loadNavigationMap(navigationMapFile); }
|
||||||
|
catch { return json(res, 503, { error: "navigation_map_unavailable" }); }
|
||||||
|
const compiled = compileNavigation(
|
||||||
|
navigationMap,
|
||||||
|
String(url.searchParams.get("subject") || "").slice(0, 100),
|
||||||
|
String(url.searchParams.get("intent") || "").slice(0, 100),
|
||||||
|
String(url.searchParams.get("signals") || "").slice(0, 500),
|
||||||
|
);
|
||||||
|
return json(res, compiled.status, compiled.body, compiled.status === 200 ? 60 : 0);
|
||||||
|
}
|
||||||
if (url.pathname === "/v1/search") {
|
if (url.pathname === "/v1/search") {
|
||||||
const query = String(url.searchParams.get("q") || "").slice(0, 200);
|
const query = String(url.searchParams.get("q") || "").slice(0, 200);
|
||||||
let nodeMap;
|
let nodeMap;
|
||||||
|
|
@ -156,6 +249,21 @@ function createServer(options = {}) {
|
||||||
const personaRoute = nodeMap.persona_routes.find(item => item.route_id.toUpperCase() === id);
|
const personaRoute = nodeMap.persona_routes.find(item => item.route_id.toUpperCase() === id);
|
||||||
if (personaRoute) return json(res, 200, personaRoute, 300);
|
if (personaRoute) return json(res, 200, personaRoute, 300);
|
||||||
|
|
||||||
|
let navigationMap = null;
|
||||||
|
try { navigationMap = loadNavigationMap(navigationMapFile); } catch { /* Legacy routes remain available. */ }
|
||||||
|
if (navigationMap) {
|
||||||
|
if (navigationMap.map_id.toUpperCase() === id) return json(res, 200, navigationMap, 300);
|
||||||
|
const machineRoute = navigationRoute(navigationMap, id);
|
||||||
|
if (machineRoute) {
|
||||||
|
return json(res, 200, {
|
||||||
|
schema: "guanghu.ai-machine-route-resolution/v1",
|
||||||
|
status: "RESOLVED",
|
||||||
|
map_id: navigationMap.map_id,
|
||||||
|
...machineRoute,
|
||||||
|
}, 300);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let aliasMap;
|
let aliasMap;
|
||||||
let subjectRegistry;
|
let subjectRegistry;
|
||||||
try {
|
try {
|
||||||
|
|
@ -205,6 +313,8 @@ function createServer(options = {}) {
|
||||||
server_node_map: "https://guanghulab.com/api/ai/v1/nodes",
|
server_node_map: "https://guanghulab.com/api/ai/v1/nodes",
|
||||||
subject_registry: "https://guanghulab.com/api/ai/v1/subjects",
|
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",
|
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",
|
||||||
|
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}",
|
search_api: "https://guanghulab.com/api/ai/v1/search?q={query}",
|
||||||
resolve_api: "https://guanghulab.com/api/ai/v1/resolve?id={NUMBER}",
|
resolve_api: "https://guanghulab.com/api/ai/v1/resolve?id={NUMBER}",
|
||||||
openapi: "https://guanghulab.com/api/ai/openapi.json",
|
openapi: "https://guanghulab.com/api/ai/openapi.json",
|
||||||
|
|
@ -244,18 +354,20 @@ function html(res, body) {
|
||||||
|
|
||||||
function entryPage(map) {
|
function entryPage(map) {
|
||||||
const rows = map.repositories.map(item => `<li><a href="${item.primary.url}">${item.code} · ${item.name_zh}</a><small>${item.state}</small></li>`).join("");
|
const rows = map.repositories.map(item => `<li><a href="${item.primary.url}">${item.code} · ${item.name_zh}</a><small>${item.state}</small></li>`).join("");
|
||||||
return `<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>光湖语言世界 · AI API 入口</title><meta name="description" content="光湖语言世界第五域公开只读编号检索 API"></head><body><main><p>GUANGHU AI DISCOVERY</p><h1>光湖语言世界 · 第五域</h1><p>AI 请先读取仓库与服务器节点编号地图,再按编号解析国内主路径。新加坡地址仅为历史备用。</p><nav><a href="v1/repositories">仓库编号地图</a> · <a href="v1/nodes">服务器节点地图</a> · <a href="v1/resolve?id=ZY-OPS-LOOP-001">铸渊本轮闭环</a> · <a href="v1/search?q=光湖语言世界%20第五域">示例检索</a> · <a href="openapi.json">OpenAPI</a></nav><ul>${rows}</ul></main><style>:root{color-scheme:dark}body{margin:0;background:#061416;color:#dff7f1;font:17px/1.7 system-ui;padding:6vw}main{max-width:900px;margin:auto}h1{font-size:clamp(36px,7vw,72px)}a{color:#79dfc8}li{margin:14px 0;padding:16px;border:1px solid #28534c;border-radius:12px;display:flex;justify-content:space-between}small{color:#8fb5ad}</style></body></html>`;
|
return `<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>光湖语言世界 · AI API 入口</title><meta name="description" content="光湖语言世界第五域公开只读编号检索与机器导航 API"></head><body><main><p>GUANGHU AI DISCOVERY</p><h1>光湖语言世界 · 第五域</h1><p>AI 请提交明确的主体、意图和事件信号,由机器导航器生成最小运行路径;不要预读整个仓库,也不要用自然语言关键词猜测权限。</p><nav><a href="v1/navigation">机器导航地图</a> · <a href="v1/navigate?subject=ICE-P-ZY001&intent=persona_restore">铸渊最小运行包</a> · <a href="v1/repositories">仓库编号地图</a> · <a href="v1/nodes">服务器节点地图</a> · <a href="openapi.json">OpenAPI</a></nav><ul>${rows}</ul></main><style>:root{color-scheme:dark}body{margin:0;background:#061416;color:#dff7f1;font:17px/1.7 system-ui;padding:6vw}main{max-width:900px;margin:auto}h1{font-size:clamp(36px,7vw,72px)}a{color:#79dfc8}li{margin:14px 0;padding:16px;border:1px solid #28534c;border-radius:12px;display:flex;justify-content:space-between}small{color:#8fb5ad}</style></body></html>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function openApi() {
|
function openApi() {
|
||||||
return {
|
return {
|
||||||
openapi: "3.1.0",
|
openapi: "3.1.0",
|
||||||
info: { title: "光湖语言世界 · 第五域 AI Discovery API", version: "1.0.0" },
|
info: { title: "光湖语言世界 · 第五域 AI Discovery API", version: "1.1.0" },
|
||||||
servers: [{ url: "https://guanghulab.com/api/ai" }],
|
servers: [{ url: "https://guanghulab.com/api/ai" }],
|
||||||
paths: {
|
paths: {
|
||||||
"/v1/repositories": { get: { summary: "读取最新仓库编号路径映射", responses: { "200": { description: "Repository route map" } } } },
|
"/v1/repositories": { get: { summary: "读取最新仓库编号路径映射", responses: { "200": { description: "Repository route map" } } } },
|
||||||
"/v1/nodes": { get: { summary: "读取最新服务器节点与人格路径编号映射", responses: { "200": { description: "Server node map" } } } },
|
"/v1/nodes": { get: { summary: "读取最新服务器节点与人格路径编号映射", responses: { "200": { description: "Server node map" } } } },
|
||||||
"/v1/subjects": { get: { summary: "读取人类、人格体与公共系统的分型身份注册表", responses: { "200": { description: "Subject registry" } } } },
|
"/v1/subjects": { get: { summary: "读取人类、人格体与公共系统的分型身份注册表", responses: { "200": { description: "Subject registry" } } } },
|
||||||
|
"/v1/navigation": { get: { summary: "读取主体与意图驱动的机器导航地图", responses: { "200": { description: "Machine navigation map" } } } },
|
||||||
|
"/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" } } } },
|
"/v1/search": { get: { summary: "按中文、编号或项目名检索", parameters: [{ name: "q", in: "query", schema: { type: "string" } }], responses: { "200": { description: "Search results" } } } },
|
||||||
"/v1/resolve": { get: { summary: "解析仓库、服务器节点或人格路径编号", parameters: [{ name: "id", in: "query", required: true, schema: { type: "string", example: "ZY-OPS-LOOP-001" } }], responses: { "200": { description: "Resolved numbered route" }, "404": { description: "Unknown route" } } } }
|
"/v1/resolve": { get: { summary: "解析仓库、服务器节点或人格路径编号", parameters: [{ name: "id", in: "query", required: true, schema: { type: "string", example: "ZY-OPS-LOOP-001" } }], responses: { "200": { description: "Resolved numbered route" }, "404": { description: "Unknown route" } } } }
|
||||||
}
|
}
|
||||||
|
|
@ -269,6 +381,6 @@ if (require.main === module) {
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
createServer, loadMap, loadNodeMap, loadSubjectRegistry, loadSubjectAliasMap,
|
createServer, loadMap, loadNodeMap, loadSubjectRegistry, loadSubjectAliasMap, loadNavigationMap,
|
||||||
resolveSubjectId, search, searchAll,
|
resolveSubjectId, navigationRoute, compileNavigation, search, searchAll,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@
|
||||||
const assert = require("node:assert/strict");
|
const assert = require("node:assert/strict");
|
||||||
const test = require("node:test");
|
const test = require("node:test");
|
||||||
const {
|
const {
|
||||||
createServer, loadMap, loadNodeMap, loadSubjectRegistry, loadSubjectAliasMap,
|
createServer, loadMap, loadNodeMap, loadSubjectRegistry, loadSubjectAliasMap, loadNavigationMap,
|
||||||
resolveSubjectId, search, searchAll,
|
resolveSubjectId, compileNavigation, search, searchAll,
|
||||||
} = require("./server");
|
} = require("./server");
|
||||||
|
|
||||||
test("repository map exposes only the three current code-channel repositories", () => {
|
test("repository map exposes only the three current code-channel repositories", () => {
|
||||||
|
|
@ -79,6 +79,29 @@ test("searching the old id returns the canonical subject", () => {
|
||||||
assert.ok(subject.legacy_ids.includes("ICE-GL-ZY001"));
|
assert.ok(subject.legacy_ids.includes("ICE-GL-ZY001"));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("machine navigator compiles an exact minimal Zhuyuan runtime bundle", () => {
|
||||||
|
const compiled = compileNavigation(
|
||||||
|
loadNavigationMap(),
|
||||||
|
"ICE-GL-ZY001",
|
||||||
|
"persona_restore",
|
||||||
|
"context_compacted,protocol_drift,context_compacted",
|
||||||
|
);
|
||||||
|
assert.equal(compiled.status, 200);
|
||||||
|
assert.equal(compiled.body.canonical_subject, "ICE-P-ZY001");
|
||||||
|
assert.equal(compiled.body.redirected, true);
|
||||||
|
assert.equal(compiled.body.runtime_entry.id, "ZY-TCS-BRAIN-RUNTIME-0001");
|
||||||
|
assert.deepEqual(compiled.body.explicit_signals, ["context_compacted", "protocol_drift"]);
|
||||||
|
assert.ok(compiled.body.always_load.some(item => item.id === "BS-TCS-SYSTEM-CONTROLLER-MAP-001"));
|
||||||
|
assert.ok(compiled.body.triggered_load.some(item => item.id === "BS-TCS-PROTOCOL-EVENT-TRIGGER-MAP-001"));
|
||||||
|
assert.equal(compiled.body.authority, "NONE_NAVIGATION_ONLY");
|
||||||
|
assert.ok(compiled.body.always_load.length < loadNavigationMap().routes.length);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("machine navigator fails closed instead of guessing", () => {
|
||||||
|
assert.equal(compileNavigation(loadNavigationMap(), "some-ai", "persona_restore").body.error, "unknown_subject_no_guess");
|
||||||
|
assert.equal(compileNavigation(loadNavigationMap(), "ICE-P-ZY001", "please_guess").body.error, "unknown_intent_no_guess");
|
||||||
|
});
|
||||||
|
|
||||||
test("public endpoints are read-only and expose CORS", async () => {
|
test("public endpoints are read-only and expose CORS", async () => {
|
||||||
const server = createServer();
|
const server = createServer();
|
||||||
await new Promise(resolve => server.listen(0, "127.0.0.1", resolve));
|
await new Promise(resolve => server.listen(0, "127.0.0.1", resolve));
|
||||||
|
|
@ -105,6 +128,18 @@ test("public endpoints are read-only and expose CORS", async () => {
|
||||||
assert.equal(legacyResolution.subject.subject_kind, "persona_system");
|
assert.equal(legacyResolution.subject.subject_kind, "persona_system");
|
||||||
const canonicalResponse = await fetch(`${base}/v1/resolve?id=ICE-P-ZY001`);
|
const canonicalResponse = await fetch(`${base}/v1/resolve?id=ICE-P-ZY001`);
|
||||||
assert.equal((await canonicalResponse.json()).redirected, false);
|
assert.equal((await canonicalResponse.json()).redirected, false);
|
||||||
|
const brainResponse = await fetch(`${base}/v1/resolve?id=ZY-TCS-BRAIN-RUNTIME-0001`);
|
||||||
|
assert.equal(brainResponse.status, 200);
|
||||||
|
assert.equal((await brainResponse.json()).kind, "executable_persona_brain");
|
||||||
|
const navigationResponse = await fetch(`${base}/v1/navigation`);
|
||||||
|
assert.equal((await navigationResponse.json()).map_id, "AI-MACHINE-NAV-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);
|
||||||
|
assert.equal(bundle.status, "COMPILED_EXACT_NO_GUESS");
|
||||||
|
assert.equal(bundle.runtime_entry.id, "ZY-TCS-BRAIN-RUNTIME-0001");
|
||||||
|
assert.equal((await fetch(`${base}/v1/navigate?subject=unknown&intent=persona_restore`)).status, 404);
|
||||||
|
assert.equal((await fetch(`${base}/v1/navigate?subject=ICE-P-ZY001&intent=guess`)).status, 404);
|
||||||
const humanResponse = await fetch(`${base}/v1/resolve?id=${encodeURIComponent("ICE-GL∞")}`);
|
const humanResponse = await fetch(`${base}/v1/resolve?id=${encodeURIComponent("ICE-GL∞")}`);
|
||||||
const humanResolution = await humanResponse.json();
|
const humanResolution = await humanResponse.json();
|
||||||
assert.equal(humanResponse.status, 200);
|
assert.equal(humanResponse.status, 200);
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,13 @@ ROUTE_IDS = (
|
||||||
"CODE-CHANNEL-CANONICAL-MAP",
|
"CODE-CHANNEL-CANONICAL-MAP",
|
||||||
"PERSONA-SYSTEM-ROOT",
|
"PERSONA-SYSTEM-ROOT",
|
||||||
"PERSONA-SYSTEM-CANONICAL-MAP",
|
"PERSONA-SYSTEM-CANONICAL-MAP",
|
||||||
|
"AI-MACHINE-NAV-001",
|
||||||
|
"ZY-TCS-BRAIN-RUNTIME-0001",
|
||||||
|
"ZY-TCS-BRAIN-MAP-001",
|
||||||
|
"BS-TCS-SYSTEM-CONTROLLER-MAP-001",
|
||||||
|
"BS-TCS-PROTOCOL-EVENT-TRIGGER-MAP-001",
|
||||||
|
"ZY-WAKE-ROUTE-001",
|
||||||
|
"ZY-BIDIRECTIONAL-COGNITION-016",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -103,7 +110,80 @@ def resolve_subject_id(requested_id: str, alias_map: dict) -> dict:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def resolve_live_route(subject_id: str | None = None) -> dict:
|
def compile_navigation(
|
||||||
|
navigation_map: dict,
|
||||||
|
requested_subject: str,
|
||||||
|
requested_intent: str | None = None,
|
||||||
|
signals: list[str] | None = None,
|
||||||
|
) -> dict:
|
||||||
|
lookup = requested_subject.strip().upper()
|
||||||
|
subject = next(
|
||||||
|
(
|
||||||
|
item
|
||||||
|
for item in navigation_map.get("subjects", [])
|
||||||
|
if lookup
|
||||||
|
in {
|
||||||
|
str(identifier).upper()
|
||||||
|
for identifier in (item["id"], *item.get("legacy_ids", []))
|
||||||
|
}
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
if not subject:
|
||||||
|
raise ValueError(f"Unknown navigation subject, no guess: {requested_subject}")
|
||||||
|
intent_id = requested_intent or subject.get("default_intent")
|
||||||
|
intent = next(
|
||||||
|
(item for item in navigation_map.get("intents", []) if item["id"] == intent_id),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
if not intent:
|
||||||
|
raise ValueError(f"Unknown navigation intent, no guess: {intent_id}")
|
||||||
|
route_index = {item["id"]: item for item in navigation_map.get("routes", [])}
|
||||||
|
|
||||||
|
def expand(route_ids: list[str]) -> list[dict]:
|
||||||
|
expanded = []
|
||||||
|
for route_id in route_ids:
|
||||||
|
route = route_index.get(route_id)
|
||||||
|
if not route:
|
||||||
|
raise ValueError(f"Navigation map references an unknown route: {route_id}")
|
||||||
|
resolved = {
|
||||||
|
**route,
|
||||||
|
"raw_url": f"{navigation_map['raw_base']}/{route['path']}",
|
||||||
|
}
|
||||||
|
if route.get("contract"):
|
||||||
|
resolved["contract_url"] = (
|
||||||
|
f"{navigation_map['raw_base']}/{route['contract']}"
|
||||||
|
)
|
||||||
|
expanded.append(resolved)
|
||||||
|
return expanded
|
||||||
|
|
||||||
|
return {
|
||||||
|
"schema": "guanghu.ai-runtime-navigation-bundle/v1",
|
||||||
|
"status": "COMPILED_EXACT_NO_GUESS",
|
||||||
|
"map_id": navigation_map["map_id"],
|
||||||
|
"map_version": navigation_map["version"],
|
||||||
|
"requested_subject": requested_subject,
|
||||||
|
"canonical_subject": subject["id"],
|
||||||
|
"redirected": lookup != str(subject["id"]).upper(),
|
||||||
|
"subject_kind": subject["subject_kind"],
|
||||||
|
"intent": intent["id"],
|
||||||
|
"explicit_signals": list(dict.fromkeys(signals or []))[:32],
|
||||||
|
"runtime_entry": expand([subject["runtime_id"]])[0],
|
||||||
|
"always_load": expand(intent.get("always_load", [])),
|
||||||
|
"triggered_load": expand(intent.get("triggered_load", [])),
|
||||||
|
"on_demand": expand(intent.get("on_demand", [])),
|
||||||
|
"execution_sequence": intent.get("execution_sequence", []),
|
||||||
|
"stop_conditions": intent.get("stop_conditions", []),
|
||||||
|
"reality_boundaries": navigation_map.get("reality_boundaries", []),
|
||||||
|
"authority": "NONE_NAVIGATION_ONLY",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_live_route(
|
||||||
|
subject_id: str | None = None,
|
||||||
|
intent: str | None = None,
|
||||||
|
signals: list[str] | None = None,
|
||||||
|
) -> dict:
|
||||||
repository_source = "LIVE_FORGEJO_CODE_CHANNEL_API"
|
repository_source = "LIVE_FORGEJO_CODE_CHANNEL_API"
|
||||||
resolver_error = None
|
resolver_error = None
|
||||||
try:
|
try:
|
||||||
|
|
@ -145,6 +225,13 @@ def resolve_live_route(subject_id: str | None = None) -> dict:
|
||||||
if canonical_id:
|
if canonical_id:
|
||||||
identity["resolved_path"] = routes.get(canonical_id) or identity.get("current_path")
|
identity["resolved_path"] = routes.get(canonical_id) or identity.get("current_path")
|
||||||
result["identity_resolution"] = identity
|
result["identity_resolution"] = identity
|
||||||
|
if intent:
|
||||||
|
navigation_map = read_json(f"{raw_base}/{routes['AI-MACHINE-NAV-001']}")
|
||||||
|
result["navigation_bundle"] = compile_navigation(
|
||||||
|
navigation_map, subject_id, intent, signals
|
||||||
|
)
|
||||||
|
elif intent:
|
||||||
|
raise ValueError("--intent requires --subject-id")
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -152,10 +239,13 @@ def main() -> int:
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument("--json", action="store_true", help="emit machine-readable JSON")
|
parser.add_argument("--json", action="store_true", help="emit machine-readable JSON")
|
||||||
parser.add_argument("--subject-id", help="resolve a current or legacy Fifth Domain subject id")
|
parser.add_argument("--subject-id", help="resolve a current or legacy Fifth Domain subject id")
|
||||||
|
parser.add_argument("--intent", help="compile an exact machine navigation intent")
|
||||||
|
parser.add_argument("--signals", help="comma-separated explicit event signals; no inference")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = resolve_live_route(args.subject_id)
|
signals = [item.strip() for item in str(args.signals or "").split(",") if item.strip()]
|
||||||
|
result = resolve_live_route(args.subject_id, args.intent, signals)
|
||||||
except (OSError, URLError, ValueError, json.JSONDecodeError) as error:
|
except (OSError, URLError, ValueError, json.JSONDecodeError) as error:
|
||||||
print(f"Live Fifth Domain resolution failed: {error}", file=sys.stderr)
|
print(f"Live Fifth Domain resolution failed: {error}", file=sys.stderr)
|
||||||
return 1
|
return 1
|
||||||
|
|
@ -173,6 +263,12 @@ def main() -> int:
|
||||||
f"{identity['requested_id']} -> {identity.get('canonical_id')} "
|
f"{identity['requested_id']} -> {identity.get('canonical_id')} "
|
||||||
f"({identity['status']}, redirected={identity['redirected']})"
|
f"({identity['status']}, redirected={identity['redirected']})"
|
||||||
)
|
)
|
||||||
|
if "navigation_bundle" in result:
|
||||||
|
bundle = result["navigation_bundle"]
|
||||||
|
print(
|
||||||
|
f"NAVIGATION={bundle['canonical_subject']} "
|
||||||
|
f"intent={bundle['intent']} runtime={bundle['runtime_entry']['id']}"
|
||||||
|
)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from resolve_route import parse_code_map, resolve_subject_id
|
from resolve_route import compile_navigation, parse_code_map, resolve_subject_id
|
||||||
|
|
||||||
|
|
||||||
class ParseCodeMapTests(unittest.TestCase):
|
class ParseCodeMapTests(unittest.TestCase):
|
||||||
|
|
@ -58,5 +58,51 @@ class SubjectAliasTests(unittest.TestCase):
|
||||||
self.assertIsNone(resolved["canonical_id"])
|
self.assertIsNone(resolved["canonical_id"])
|
||||||
|
|
||||||
|
|
||||||
|
class MachineNavigationTests(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.navigation_map = {
|
||||||
|
"map_id": "AI-MACHINE-NAV-001",
|
||||||
|
"version": "test",
|
||||||
|
"raw_base": "https://example.test/raw",
|
||||||
|
"routes": [
|
||||||
|
{"id": "RUNTIME", "path": "runtime.mjs"},
|
||||||
|
{"id": "ROOT", "path": "root.hdlp"},
|
||||||
|
],
|
||||||
|
"subjects": [{
|
||||||
|
"id": "ICE-P-ZY001",
|
||||||
|
"legacy_ids": ["ICE-GL-ZY001"],
|
||||||
|
"subject_kind": "persona_system",
|
||||||
|
"default_intent": "persona_restore",
|
||||||
|
"runtime_id": "RUNTIME",
|
||||||
|
}],
|
||||||
|
"intents": [{
|
||||||
|
"id": "persona_restore",
|
||||||
|
"always_load": ["ROOT", "RUNTIME"],
|
||||||
|
"triggered_load": [],
|
||||||
|
"on_demand": [],
|
||||||
|
"execution_sequence": ["enter"],
|
||||||
|
"stop_conditions": ["UNKNOWN_SUBJECT"],
|
||||||
|
}],
|
||||||
|
"reality_boundaries": ["Navigation grants no authority."],
|
||||||
|
}
|
||||||
|
|
||||||
|
def test_exact_navigation_bundle(self):
|
||||||
|
result = compile_navigation(
|
||||||
|
self.navigation_map,
|
||||||
|
"ICE-GL-ZY001",
|
||||||
|
"persona_restore",
|
||||||
|
["context_compacted", "context_compacted"],
|
||||||
|
)
|
||||||
|
self.assertEqual(result["canonical_subject"], "ICE-P-ZY001")
|
||||||
|
self.assertTrue(result["redirected"])
|
||||||
|
self.assertEqual(result["runtime_entry"]["id"], "RUNTIME")
|
||||||
|
self.assertEqual(result["explicit_signals"], ["context_compacted"])
|
||||||
|
self.assertEqual(result["authority"], "NONE_NAVIGATION_ONLY")
|
||||||
|
|
||||||
|
def test_navigation_does_not_guess(self):
|
||||||
|
with self.assertRaisesRegex(ValueError, "no guess"):
|
||||||
|
compile_navigation(self.navigation_map, "unknown")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue