foxclaw 0.1.0 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (514) hide show
  1. package/README.md +54 -29
  2. package/bin/foxclaw.cjs +51 -0
  3. package/index.js +4 -10
  4. package/package.json +46 -38
  5. package/python/.dockerignore +35 -0
  6. package/python/.env.example +173 -0
  7. package/python/.gitattributes +46 -0
  8. package/python/.github/workflows/ci.yml +167 -0
  9. package/python/.github/workflows/release.yml +183 -0
  10. package/python/CHANGELOG.md +57 -0
  11. package/python/CHANGELOG_CN.md +57 -0
  12. package/python/Dockerfile +64 -0
  13. package/python/LICENSE +21 -0
  14. package/python/MANIFEST.in +14 -0
  15. package/python/README.md +201 -0
  16. package/python/README_CN.md +336 -0
  17. package/python/bench/bm25_audit.py +186 -0
  18. package/python/bench/bm25_regression.py +177 -0
  19. package/python/bench/bm25_stress.py +155 -0
  20. package/python/bench/cd_audit.py +215 -0
  21. package/python/bench/chaos.py +140 -0
  22. package/python/bench/concurrency.py +103 -0
  23. package/python/bench/e2e_real_models.py +94 -0
  24. package/python/bench/llm_retry_test.py +241 -0
  25. package/python/bench/load_test.py +193 -0
  26. package/python/bench/vector_ivf_upgrade.py +128 -0
  27. package/python/docs/CHANNELS.md +195 -0
  28. package/python/docs/GOVERNANCE.md +164 -0
  29. package/python/docs/RELEASE.md +110 -0
  30. package/python/docs/RUN.md +75 -0
  31. package/python/docs/RUN_CN.md +121 -0
  32. package/python/docs//351/205/215/347/275/256/346/214/207/345/215/227.md +152 -0
  33. package/python/evals/__init__.py +0 -0
  34. package/python/evals/governance/README.md +61 -0
  35. package/python/evals/governance/__init__.py +0 -0
  36. package/python/evals/governance/cases.py +115 -0
  37. package/python/evals/governance/compare_embeddings.py +94 -0
  38. package/python/evals/governance/convergence.svg +11 -0
  39. package/python/evals/governance/report.json +371 -0
  40. package/python/evals/governance/run_evals.py +368 -0
  41. package/python/examples/governance_middleware_demo.py +72 -0
  42. package/python/examples/langgraph_governance_demo.py +104 -0
  43. package/python/examples/sample_itinerary.csv +6 -0
  44. package/python/examples/sample_knowledge.csv +9 -0
  45. package/python/examples/sample_transactions.csv +14 -0
  46. package/python/foxclaw/__init__.py +408 -0
  47. package/python/foxclaw/agents/__init__.py +12 -0
  48. package/python/foxclaw/agents/agents.py +35 -0
  49. package/python/foxclaw/agents/base.py +794 -0
  50. package/python/foxclaw/agents/impl/__init__.py +1 -0
  51. package/python/foxclaw/agents/impl/_helpers.py +70 -0
  52. package/python/foxclaw/agents/impl/finance.py +231 -0
  53. package/python/foxclaw/agents/impl/office.py +350 -0
  54. package/python/foxclaw/agents/impl/supervisor.py +592 -0
  55. package/python/foxclaw/agents/mixins/_context.py +128 -0
  56. package/python/foxclaw/agents/mixins/augment.py +143 -0
  57. package/python/foxclaw/agents/mixins/llm.py +67 -0
  58. package/python/foxclaw/agents/mixins/memory.py +41 -0
  59. package/python/foxclaw/agents/mixins/preference.py +131 -0
  60. package/python/foxclaw/agents/mixins/prompt.py +71 -0
  61. package/python/foxclaw/agents/mixins/skillbox.py +58 -0
  62. package/python/foxclaw/agents/mixins/tool_exec.py +417 -0
  63. package/python/foxclaw/api/__init__.py +224 -0
  64. package/python/foxclaw/api/_state.py +43 -0
  65. package/python/foxclaw/api/bootstrap.py +96 -0
  66. package/python/foxclaw/api/middleware.py +294 -0
  67. package/python/foxclaw/api/routes/ahs.py +404 -0
  68. package/python/foxclaw/api/routes/attachments.py +116 -0
  69. package/python/foxclaw/api/routes/browser.py +35 -0
  70. package/python/foxclaw/api/routes/domain.py +306 -0
  71. package/python/foxclaw/api/routes/knowledge.py +145 -0
  72. package/python/foxclaw/api/routes/llm_admin.py +449 -0
  73. package/python/foxclaw/api/routes/memory.py +105 -0
  74. package/python/foxclaw/api/routes/orchestration.py +39 -0
  75. package/python/foxclaw/api/routes/recovery.py +44 -0
  76. package/python/foxclaw/api/routes/review.py +266 -0
  77. package/python/foxclaw/api/routes/search.py +33 -0
  78. package/python/foxclaw/api/routes/system.py +311 -0
  79. package/python/foxclaw/api/routes/task.py +263 -0
  80. package/python/foxclaw/api/routes/ws.py +64 -0
  81. package/python/foxclaw/api/spine.py +52 -0
  82. package/python/foxclaw/browser/__init__.py +13 -0
  83. package/python/foxclaw/browser/action/__init__.py +1 -0
  84. package/python/foxclaw/browser/action/action_executor.py +50 -0
  85. package/python/foxclaw/browser/action/action_types.py +44 -0
  86. package/python/foxclaw/browser/action/action_verifier.py +68 -0
  87. package/python/foxclaw/browser/action/url_policy.py +143 -0
  88. package/python/foxclaw/browser/adapter/__init__.py +1 -0
  89. package/python/foxclaw/browser/adapter/base_adapter.py +23 -0
  90. package/python/foxclaw/browser/adapter/web_adapter.py +46 -0
  91. package/python/foxclaw/browser/driver/__init__.py +1 -0
  92. package/python/foxclaw/browser/driver/base_driver.py +61 -0
  93. package/python/foxclaw/browser/driver/playwright_driver.py +170 -0
  94. package/python/foxclaw/browser/perception/__init__.py +1 -0
  95. package/python/foxclaw/browser/perception/dom_extractor.py +42 -0
  96. package/python/foxclaw/browser/perception/element_indexer.py +56 -0
  97. package/python/foxclaw/browser/perception/semantic_reducer.py +33 -0
  98. package/python/foxclaw/browser/perception/snapshot.py +29 -0
  99. package/python/foxclaw/browser/service.py +144 -0
  100. package/python/foxclaw/browser/session/__init__.py +1 -0
  101. package/python/foxclaw/browser/session/session_manager.py +57 -0
  102. package/python/foxclaw/capability/__init__.py +12 -0
  103. package/python/foxclaw/capability/embedding_service.py +65 -0
  104. package/python/foxclaw/channels/__init__.py +138 -0
  105. package/python/foxclaw/channels/discord.py +175 -0
  106. package/python/foxclaw/channels/dispatcher.py +179 -0
  107. package/python/foxclaw/channels/feishu.py +215 -0
  108. package/python/foxclaw/channels/openclaw_bridge.py +151 -0
  109. package/python/foxclaw/channels/signing.py +187 -0
  110. package/python/foxclaw/channels/telegram.py +120 -0
  111. package/python/foxclaw/channels/weixin.py +215 -0
  112. package/python/foxclaw/cli.py +183 -0
  113. package/python/foxclaw/cognitive/__init__.py +4 -0
  114. package/python/foxclaw/cognitive/reasoner.py +81 -0
  115. package/python/foxclaw/cognitive/state.py +141 -0
  116. package/python/foxclaw/core/__init__.py +9 -0
  117. package/python/foxclaw/core/audit_chain.py +131 -0
  118. package/python/foxclaw/core/audit_sink.py +100 -0
  119. package/python/foxclaw/core/compaction.py +265 -0
  120. package/python/foxclaw/core/config.py +65 -0
  121. package/python/foxclaw/core/confirm_nonce.py +151 -0
  122. package/python/foxclaw/core/dispatch.py +27 -0
  123. package/python/foxclaw/core/envfile.py +48 -0
  124. package/python/foxclaw/core/failure_policy.py +142 -0
  125. package/python/foxclaw/core/gateway.py +81 -0
  126. package/python/foxclaw/core/governance_audit.py +171 -0
  127. package/python/foxclaw/core/idempotency.py +282 -0
  128. package/python/foxclaw/core/injection_guard.py +202 -0
  129. package/python/foxclaw/core/memory_adapter.py +489 -0
  130. package/python/foxclaw/core/metrics.py +88 -0
  131. package/python/foxclaw/core/model_router.py +985 -0
  132. package/python/foxclaw/core/paths.py +103 -0
  133. package/python/foxclaw/core/recovery.py +349 -0
  134. package/python/foxclaw/core/scheduler.py +141 -0
  135. package/python/foxclaw/core/secrets.py +131 -0
  136. package/python/foxclaw/core/sessions.py +155 -0
  137. package/python/foxclaw/core/settings.py +414 -0
  138. package/python/foxclaw/core/shared_write.py +186 -0
  139. package/python/foxclaw/core/steering_queue.py +154 -0
  140. package/python/foxclaw/core/system_bus.py +84 -0
  141. package/python/foxclaw/core/thought_chain.py +316 -0
  142. package/python/foxclaw/core/token_meter.py +231 -0
  143. package/python/foxclaw/core/tools.py +164 -0
  144. package/python/foxclaw/core/tracing.py +375 -0
  145. package/python/foxclaw/core/truth_pipeline.py +110 -0
  146. package/python/foxclaw/core/update_check.py +149 -0
  147. package/python/foxclaw/core/vec_recall_adapter.py +477 -0
  148. package/python/foxclaw/core/verify.py +173 -0
  149. package/python/foxclaw/core/web_search.py +218 -0
  150. package/python/foxclaw/finance/__init__.py +15 -0
  151. package/python/foxclaw/finance/evidence.py +149 -0
  152. package/python/foxclaw/finance/fx.py +98 -0
  153. package/python/foxclaw/finance/graph.py +179 -0
  154. package/python/foxclaw/finance/models.py +146 -0
  155. package/python/foxclaw/finance/nlparse.py +515 -0
  156. package/python/foxclaw/finance/policy.py +301 -0
  157. package/python/foxclaw/finance/reasoner.py +121 -0
  158. package/python/foxclaw/finance/reasoning.py +319 -0
  159. package/python/foxclaw/finance/reconcile.py +175 -0
  160. package/python/foxclaw/finance/runtime.py +500 -0
  161. package/python/foxclaw/governance/__init__.py +24 -0
  162. package/python/foxclaw/governance/middleware.py +176 -0
  163. package/python/foxclaw/harness/__init__.py +17 -0
  164. package/python/foxclaw/harness/_base.py +66 -0
  165. package/python/foxclaw/harness/_policy.py +340 -0
  166. package/python/foxclaw/harness/drift.py +77 -0
  167. package/python/foxclaw/harness/event_bus.py +28 -0
  168. package/python/foxclaw/harness/harness.py +358 -0
  169. package/python/foxclaw/harness/loop_detection.py +145 -0
  170. package/python/foxclaw/itinerary/__init__.py +10 -0
  171. package/python/foxclaw/itinerary/amap_client.py +114 -0
  172. package/python/foxclaw/itinerary/models.py +80 -0
  173. package/python/foxclaw/itinerary/reasoner.py +96 -0
  174. package/python/foxclaw/itinerary/reasoning.py +168 -0
  175. package/python/foxclaw/itinerary/runtime.py +144 -0
  176. package/python/foxclaw/kernel/__init__.py +13 -0
  177. package/python/foxclaw/kernel/capabilities.py +72 -0
  178. package/python/foxclaw/kernel/policy_gate.py +108 -0
  179. package/python/foxclaw/kernel/process_manager.py +186 -0
  180. package/python/foxclaw/kernel/scheduler.py +125 -0
  181. package/python/foxclaw/kernel/sentinel.py +443 -0
  182. package/python/foxclaw/kernel/sentinel_client.py +243 -0
  183. package/python/foxclaw/kernel/spine.py +97 -0
  184. package/python/foxclaw/knowledge/__init__.py +360 -0
  185. package/python/foxclaw/knowledge/truth_crystallizer.py +89 -0
  186. package/python/foxclaw/learning/__init__.py +12 -0
  187. package/python/foxclaw/learning/knowledge_graph.py +144 -0
  188. package/python/foxclaw/learning/models.py +93 -0
  189. package/python/foxclaw/learning/reasoner.py +67 -0
  190. package/python/foxclaw/learning/runtime.py +351 -0
  191. package/python/foxclaw/learning/spaced_repetition.py +91 -0
  192. package/python/foxclaw/mcp_server.py +188 -0
  193. package/python/foxclaw/orchestration/__init__.py +167 -0
  194. package/python/foxclaw/pipeline/__init__.py +23 -0
  195. package/python/foxclaw/pipeline/cognitive_pipeline.py +429 -0
  196. package/python/foxclaw/pipeline/stage_meta.py +21 -0
  197. package/python/foxclaw/pipeline/supervisor_stages.py +201 -0
  198. package/python/foxclaw/planning/__init__.py +17 -0
  199. package/python/foxclaw/planning/llm_planner.py +232 -0
  200. package/python/foxclaw/planning/planner.py +94 -0
  201. package/python/foxclaw/search/__init__.py +24 -0
  202. package/python/foxclaw/search/agent_port.py +190 -0
  203. package/python/foxclaw/search/evidence.py +109 -0
  204. package/python/foxclaw/search/external_search.py +50 -0
  205. package/python/foxclaw/search/hkc_read_port.py +60 -0
  206. package/python/foxclaw/search/internal_search.py +71 -0
  207. package/python/foxclaw/search/models.py +127 -0
  208. package/python/foxclaw/search/planner.py +44 -0
  209. package/python/foxclaw/search/retriever.py +39 -0
  210. package/python/foxclaw/search/runtime.py +127 -0
  211. package/python/foxclaw/search/strategy.py +44 -0
  212. package/python/foxclaw/skills/__init__.py +559 -0
  213. package/python/foxclaw/skills/archive.py +111 -0
  214. package/python/foxclaw/skills/fetch.py +226 -0
  215. package/python/foxclaw/skills/hooks_spec.py +69 -0
  216. package/python/foxclaw/skills/install_cmd.py +216 -0
  217. package/python/foxclaw/skills/lifecycle.py +252 -0
  218. package/python/foxclaw/skills/markdown_skill.py +175 -0
  219. package/python/foxclaw/skills/markdown_store.py +39 -0
  220. package/python/foxclaw/skills/mcp_client.py +184 -0
  221. package/python/foxclaw/skills/mcp_store.py +40 -0
  222. package/python/foxclaw/skills/permission.py +145 -0
  223. package/python/foxclaw/skills/pipeline.py +354 -0
  224. package/python/foxclaw/skills/policy.py +74 -0
  225. package/python/foxclaw/skills/review.py +72 -0
  226. package/python/foxclaw/skills/store.py +57 -0
  227. package/python/frontend/assets/core_bg.jpg +0 -0
  228. package/python/frontend/deck/auth.js +25 -0
  229. package/python/frontend/deck/chat_model_picker.js +483 -0
  230. package/python/frontend/deck/domain.js +276 -0
  231. package/python/frontend/deck/main.js +1806 -0
  232. package/python/frontend/deck/markdown.js +44 -0
  233. package/python/frontend/deck/settings.js +307 -0
  234. package/python/frontend/deck/state.js +5 -0
  235. package/python/frontend/deck/util.js +4 -0
  236. package/python/frontend/deck/visuals.js +190 -0
  237. package/python/frontend/deck.css +855 -0
  238. package/python/frontend/deck.html +686 -0
  239. package/python/frontend/deck_v2.html +341 -0
  240. package/python/frontend/planet/index.html +266 -0
  241. package/python/frontend/planet/planet.css +579 -0
  242. package/python/frontend/planet/planet.js +1436 -0
  243. package/python/frontend/planet/vendor/three.core.min.js +6 -0
  244. package/python/frontend/planet/vendor/three.module.min.js +6 -0
  245. package/python/frontend/v3/assets/index-CQSBdxk4.js +34 -0
  246. package/python/frontend/v3/assets/index-D5nIsjms.css +1 -0
  247. package/python/frontend/v3/index.html +15 -0
  248. package/python/frontend/widget_runtime.js +252 -0
  249. package/python/frontend/widgets.js +639 -0
  250. package/python/frontend_src/index.html +14 -0
  251. package/python/frontend_src/package-lock.json +1376 -0
  252. package/python/frontend_src/package.json +19 -0
  253. package/python/frontend_src/src/App.vue +58 -0
  254. package/python/frontend_src/src/api.js +82 -0
  255. package/python/frontend_src/src/components/ChatDock.vue +365 -0
  256. package/python/frontend_src/src/components/ComposerMenu.vue +75 -0
  257. package/python/frontend_src/src/components/DocViewer.vue +162 -0
  258. package/python/frontend_src/src/components/Inspector.vue +235 -0
  259. package/python/frontend_src/src/components/LeftRail.vue +415 -0
  260. package/python/frontend_src/src/components/MenuPopover.vue +128 -0
  261. package/python/frontend_src/src/components/MessageStream.vue +389 -0
  262. package/python/frontend_src/src/components/ModelPicker.vue +350 -0
  263. package/python/frontend_src/src/components/QuickIngest.vue +112 -0
  264. package/python/frontend_src/src/components/Stage.vue +272 -0
  265. package/python/frontend_src/src/components/ToolMenu.vue +141 -0
  266. package/python/frontend_src/src/components/TopBar.vue +118 -0
  267. package/python/frontend_src/src/components/panels/ActivityPanel.vue +82 -0
  268. package/python/frontend_src/src/components/panels/AgentsPanel.vue +83 -0
  269. package/python/frontend_src/src/components/panels/KnowledgePanel.vue +425 -0
  270. package/python/frontend_src/src/components/panels/MemoryPanel.vue +117 -0
  271. package/python/frontend_src/src/components/panels/PanelBox.vue +50 -0
  272. package/python/frontend_src/src/components/panels/ReviewPanel.vue +292 -0
  273. package/python/frontend_src/src/components/panels/SettingsPanel.vue +358 -0
  274. package/python/frontend_src/src/components/panels/TasksPanel.vue +73 -0
  275. package/python/frontend_src/src/components/panels/ToolsPanel.vue +746 -0
  276. package/python/frontend_src/src/components/panels/UsagePanel.vue +105 -0
  277. package/python/frontend_src/src/lib/fmt.js +44 -0
  278. package/python/frontend_src/src/lib/i18n.js +53 -0
  279. package/python/frontend_src/src/lib/locales/en.js +173 -0
  280. package/python/frontend_src/src/lib/locales/zh.js +174 -0
  281. package/python/frontend_src/src/lib/md.js +190 -0
  282. package/python/frontend_src/src/main.js +9 -0
  283. package/python/frontend_src/src/stores/app.js +91 -0
  284. package/python/frontend_src/src/stores/chat.js +450 -0
  285. package/python/frontend_src/src/stores/model.js +96 -0
  286. package/python/frontend_src/src/stores/panels.js +289 -0
  287. package/python/frontend_src/src/stores/profile.js +31 -0
  288. package/python/frontend_src/src/stores/system.js +25 -0
  289. package/python/frontend_src/src/styles/base.css +138 -0
  290. package/python/frontend_src/src/styles/panel.css +117 -0
  291. package/python/frontend_src/src/styles/tokens.css +78 -0
  292. package/python/frontend_src/vite.config.js +39 -0
  293. package/python/pyproject.toml +268 -0
  294. package/python/requirements.txt +311 -0
  295. package/python/skills/README.md +123 -0
  296. package/python/skills/execution-contract/SKILL.md +30 -0
  297. package/python/skills/general-assistant/SKILL.md +34 -0
  298. package/python/skills/planner/SKILL.md +33 -0
  299. package/python/skills/schedule-itinerary/SKILL.md +35 -0
  300. package/python/skills/skill-composition/SKILL.md +31 -0
  301. package/python/skills/skill-guard/SKILL.md +67 -0
  302. package/python/skills/skill-memory/SKILL.md +50 -0
  303. package/python/skills/skill-router/SKILL.md +35 -0
  304. package/python/tests/conftest.py +54 -0
  305. package/python/tests/run_all.py +200 -0
  306. package/python/tests/test_agent_lifecycle.py +326 -0
  307. package/python/tests/test_ahs_p2.py +393 -0
  308. package/python/tests/test_anthropic_tools.py +192 -0
  309. package/python/tests/test_api.py +97 -0
  310. package/python/tests/test_api_auth.py +79 -0
  311. package/python/tests/test_api_host_guard.py +69 -0
  312. package/python/tests/test_attachments.py +151 -0
  313. package/python/tests/test_audit_chain.py +116 -0
  314. package/python/tests/test_audit_sink.py +130 -0
  315. package/python/tests/test_browser.py +149 -0
  316. package/python/tests/test_browser_request_guard.py +180 -0
  317. package/python/tests/test_capabilities.py +152 -0
  318. package/python/tests/test_channel_signing.py +185 -0
  319. package/python/tests/test_channel_trust.py +109 -0
  320. package/python/tests/test_channels.py +174 -0
  321. package/python/tests/test_cli.py +46 -0
  322. package/python/tests/test_compaction_toolpair.py +119 -0
  323. package/python/tests/test_config_layering.py +61 -0
  324. package/python/tests/test_confirm_nonce.py +186 -0
  325. package/python/tests/test_context_compaction_wiring.py +124 -0
  326. package/python/tests/test_dispatch.py +89 -0
  327. package/python/tests/test_domain_evals.py +334 -0
  328. package/python/tests/test_domain_import.py +72 -0
  329. package/python/tests/test_domain_reasoners.py +134 -0
  330. package/python/tests/test_e2e.py +161 -0
  331. package/python/tests/test_emb_cache.py +97 -0
  332. package/python/tests/test_embedding_singleton.py +87 -0
  333. package/python/tests/test_envfile.py +83 -0
  334. package/python/tests/test_evals_smoke.py +90 -0
  335. package/python/tests/test_event_spine.py +174 -0
  336. package/python/tests/test_evidence_semantic.py +88 -0
  337. package/python/tests/test_evidence_sse.py +94 -0
  338. package/python/tests/test_file_ingest.py +135 -0
  339. package/python/tests/test_finance.py +144 -0
  340. package/python/tests/test_finance_ledger.py +245 -0
  341. package/python/tests/test_finance_persist.py +174 -0
  342. package/python/tests/test_gateway.py +125 -0
  343. package/python/tests/test_governance.py +421 -0
  344. package/python/tests/test_governance_audit.py +324 -0
  345. package/python/tests/test_governance_middleware.py +137 -0
  346. package/python/tests/test_hkc.py +268 -0
  347. package/python/tests/test_hkc_index_build_manager.py +306 -0
  348. package/python/tests/test_hkc_manifest_reuse.py +294 -0
  349. package/python/tests/test_hkc_search_event_sync.py +482 -0
  350. package/python/tests/test_import_path_guard.py +155 -0
  351. package/python/tests/test_injection_guard.py +193 -0
  352. package/python/tests/test_integration.py +117 -0
  353. package/python/tests/test_itinerary.py +100 -0
  354. package/python/tests/test_kee_semantic.py +84 -0
  355. package/python/tests/test_kernel.py +212 -0
  356. package/python/tests/test_learning.py +101 -0
  357. package/python/tests/test_llm_admin.py +145 -0
  358. package/python/tests/test_llm_planner.py +344 -0
  359. package/python/tests/test_markdown_skills.py +62 -0
  360. package/python/tests/test_markdown_skills_openclaw.py +66 -0
  361. package/python/tests/test_mcp_connectors.py +97 -0
  362. package/python/tests/test_mcp_guardian_discovery.py +87 -0
  363. package/python/tests/test_mcp_no_memory.py +43 -0
  364. package/python/tests/test_mcp_policy_gate.py +76 -0
  365. package/python/tests/test_mcp_server.py +92 -0
  366. package/python/tests/test_memory_recall.py +92 -0
  367. package/python/tests/test_metrics.py +58 -0
  368. package/python/tests/test_multi_chain.py +90 -0
  369. package/python/tests/test_multimodal.py +122 -0
  370. package/python/tests/test_negotiation.py +136 -0
  371. package/python/tests/test_orchestration.py +109 -0
  372. package/python/tests/test_orchestration_decoupled.py +103 -0
  373. package/python/tests/test_p3_p5_hardening.py +324 -0
  374. package/python/tests/test_parallel_dispatch.py +207 -0
  375. package/python/tests/test_pipeline.py +492 -0
  376. package/python/tests/test_plan_visible.py +86 -0
  377. package/python/tests/test_planner.py +88 -0
  378. package/python/tests/test_policy_gate.py +108 -0
  379. package/python/tests/test_profile.py +67 -0
  380. package/python/tests/test_readme_claims.py +163 -0
  381. package/python/tests/test_reasoner.py +170 -0
  382. package/python/tests/test_release_check.py +252 -0
  383. package/python/tests/test_resume_checkpoint.py +148 -0
  384. package/python/tests/test_routing.py +85 -0
  385. package/python/tests/test_scheduler.py +138 -0
  386. package/python/tests/test_search.py +194 -0
  387. package/python/tests/test_secrets.py +153 -0
  388. package/python/tests/test_semantic.py +69 -0
  389. package/python/tests/test_sessions.py +90 -0
  390. package/python/tests/test_settings.py +265 -0
  391. package/python/tests/test_shadow_learning.py +290 -0
  392. package/python/tests/test_shared_write.py +200 -0
  393. package/python/tests/test_skill_taint_gate.py +145 -0
  394. package/python/tests/test_skills.py +117 -0
  395. package/python/tests/test_skills_fetch.py +282 -0
  396. package/python/tests/test_stats_and_events.py +162 -0
  397. package/python/tests/test_streaming.py +55 -0
  398. package/python/tests/test_supervisor_pipeline.py +176 -0
  399. package/python/tests/test_supervisor_triage.py +102 -0
  400. package/python/tests/test_taint.py +112 -0
  401. package/python/tests/test_task_stream.py +74 -0
  402. package/python/tests/test_thinking_sse.py +66 -0
  403. package/python/tests/test_thinking_stream.py +76 -0
  404. package/python/tests/test_tool_center.py +113 -0
  405. package/python/tests/test_tool_parallel.py +113 -0
  406. package/python/tests/test_tool_params.py +99 -0
  407. package/python/tests/test_tool_use.py +229 -0
  408. package/python/tests/test_toplevel_concurrency.py +116 -0
  409. package/python/tests/test_tracing.py +256 -0
  410. package/python/tests/test_truth_crystallizer.py +125 -0
  411. package/python/tests/test_truth_flow.py +88 -0
  412. package/python/tests/test_update_check.py +95 -0
  413. package/python/tests/test_url_policy.py +53 -0
  414. package/python/tests/test_verify_evidence.py +212 -0
  415. package/python/tests/test_version_sync.py +100 -0
  416. package/python/tests/test_web_trigger.py +82 -0
  417. package/python/tools/check_faiss_type.py +30 -0
  418. package/python/tools/check_log.ps1 +38 -0
  419. package/python/tools/ivf_status.py +72 -0
  420. package/python/tools/mypy_baseline.txt +13 -0
  421. package/python/tools/mypy_ratchet.py +143 -0
  422. package/python/tools/pack.py +173 -0
  423. package/python/tools/release_check.py +349 -0
  424. package/python/tools/restart_foxclaw.ps1 +71 -0
  425. package/python/tools/shared_write_audit.py +190 -0
  426. package/python/tools/size_baseline.txt +28 -0
  427. package/python/tools/size_check.py +102 -0
  428. package/python/tools/strip_comments.py +228 -0
  429. package/python/tools/verify_env.bat +2 -0
  430. package/python/tools/verify_env.py +25 -0
  431. package/python/vendor/HMR_LICENSE +37 -0
  432. package/python/vendor/HMR_README.md +65 -0
  433. package/python/vendor/hkc/LICENSE +37 -0
  434. package/python/vendor/hkc/hkc_ace/__init__.py +0 -0
  435. package/python/vendor/hkc/hkc_ace/ace.py +457 -0
  436. package/python/vendor/hkc/hkc_ace/skill_taxonomy.json +465 -0
  437. package/python/vendor/hkc/hkc_api/__init__.py +0 -0
  438. package/python/vendor/hkc/hkc_api/container.py +416 -0
  439. package/python/vendor/hkc/hkc_api/ingest_jobs.py +103 -0
  440. package/python/vendor/hkc/hkc_api/main.py +157 -0
  441. package/python/vendor/hkc/hkc_api/routes/__init__.py +0 -0
  442. package/python/vendor/hkc/hkc_api/routes/ability.py +151 -0
  443. package/python/vendor/hkc/hkc_api/routes/config.py +51 -0
  444. package/python/vendor/hkc/hkc_api/routes/knowledge.py +630 -0
  445. package/python/vendor/hkc/hkc_api/routes/search.py +88 -0
  446. package/python/vendor/hkc/hkc_api/schemas.py +153 -0
  447. package/python/vendor/hkc/hkc_api/synthesis.py +179 -0
  448. package/python/vendor/hkc/hkc_core/__init__.py +0 -0
  449. package/python/vendor/hkc/hkc_core/graph/__init__.py +0 -0
  450. package/python/vendor/hkc/hkc_core/graph/base.py +188 -0
  451. package/python/vendor/hkc/hkc_core/graph/sqlite_store.py +789 -0
  452. package/python/vendor/hkc/hkc_core/models/__init__.py +0 -0
  453. package/python/vendor/hkc/hkc_core/models/enums.py +65 -0
  454. package/python/vendor/hkc/hkc_core/models/ku.py +307 -0
  455. package/python/vendor/hkc/hkc_core/utils/__init__.py +0 -0
  456. package/python/vendor/hkc/hkc_core/utils/id_gen.py +113 -0
  457. package/python/vendor/hkc/hkc_crystallizer/__init__.py +44 -0
  458. package/python/vendor/hkc/hkc_crystallizer/adapters.py +94 -0
  459. package/python/vendor/hkc/hkc_crystallizer/candidate.py +145 -0
  460. package/python/vendor/hkc/hkc_crystallizer/crystallizer.py +179 -0
  461. package/python/vendor/hkc/hkc_crystallizer/ingress.py +90 -0
  462. package/python/vendor/hkc/hkc_kde/__init__.py +0 -0
  463. package/python/vendor/hkc/hkc_kde/chunker.py +197 -0
  464. package/python/vendor/hkc/hkc_kde/extractor.py +623 -0
  465. package/python/vendor/hkc/hkc_kde/kde.py +298 -0
  466. package/python/vendor/hkc/hkc_kde/loaders/__init__.py +0 -0
  467. package/python/vendor/hkc/hkc_kde/loaders/ebook_loader.py +153 -0
  468. package/python/vendor/hkc/hkc_kde/loaders/office_loader.py +194 -0
  469. package/python/vendor/hkc/hkc_kde/loaders/pdf_loader.py +162 -0
  470. package/python/vendor/hkc/hkc_kde/loaders/text_loader.py +157 -0
  471. package/python/vendor/hkc/hkc_kde/loaders/web_loader.py +91 -0
  472. package/python/vendor/hkc/hkc_kde/models.py +74 -0
  473. package/python/vendor/hkc/hkc_kde/packager.py +329 -0
  474. package/python/vendor/hkc/hkc_kde/relation_disc.py +151 -0
  475. package/python/vendor/hkc/hkc_kee/__init__.py +0 -0
  476. package/python/vendor/hkc/hkc_kee/dedup.py +227 -0
  477. package/python/vendor/hkc/hkc_kee/fingerprint.py +106 -0
  478. package/python/vendor/hkc/hkc_kee/kee.py +541 -0
  479. package/python/vendor/hkc/hkc_kep/__init__.py +0 -0
  480. package/python/vendor/hkc/hkc_kep/event_bus.py +205 -0
  481. package/python/vendor/hkc/hkc_sdk/__init__.py +63 -0
  482. package/python/vendor/hkc/hkc_sdk/base.py +151 -0
  483. package/python/vendor/hkc/hkc_sdk/direct_client.py +153 -0
  484. package/python/vendor/hkc/hkc_sdk/exceptions.py +39 -0
  485. package/python/vendor/hkc/hkc_sdk/http_client.py +166 -0
  486. package/python/vendor/hkc/hkc_sdk/models.py +171 -0
  487. package/python/vendor/hkc/hkc_search/__init__.py +0 -0
  488. package/python/vendor/hkc/hkc_search/bm25.py +325 -0
  489. package/python/vendor/hkc/hkc_search/embedding_backends.py +374 -0
  490. package/python/vendor/hkc/hkc_search/graph_search.py +132 -0
  491. package/python/vendor/hkc/hkc_search/hybrid.py +331 -0
  492. package/python/vendor/hkc/hkc_search/index_build_manager.py +378 -0
  493. package/python/vendor/hkc/hkc_search/vector_search.py +484 -0
  494. package/python/vendor/hmr/__init__.py +0 -0
  495. package/python/vendor/hmr/core/__init__.py +0 -0
  496. package/python/vendor/hmr/core/hmr.py +684 -0
  497. package/python/vendor/hmr/core/models.py +310 -0
  498. package/python/vendor/hmr/engines/__init__.py +0 -0
  499. package/python/vendor/hmr/engines/evolution.py +710 -0
  500. package/python/vendor/hmr/engines/jit_compiler.py +472 -0
  501. package/python/vendor/hmr/engines/lifecycle.py +264 -0
  502. package/python/vendor/hmr/engines/policy.py +625 -0
  503. package/python/vendor/hmr/engines/recall.py +260 -0
  504. package/python/vendor/hmr/engines/runtime_state.py +158 -0
  505. package/python/vendor/hmr/engines/scheduler.py +323 -0
  506. package/python/vendor/hmr/engines/semantic.py +150 -0
  507. package/python/vendor/hmr/engines/temporal.py +261 -0
  508. package/python/vendor/hmr/engines/thought_chain.py +635 -0
  509. package/python/vendor/hmr/graph/__init__.py +0 -0
  510. package/python/vendor/hmr/graph/cwg.py +338 -0
  511. package/python/vendor/hmr/graph/memory_graph.py +530 -0
  512. package/python/vendor/hmr/storage/__init__.py +0 -0
  513. package/python/vendor/hmr/storage/memory_fs.py +300 -0
  514. package/python/vendor/hmr/storage/vector_store.py +520 -0
@@ -0,0 +1,686 @@
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>FOXCLAW DECK · AI 指挥中枢 v2</title>
7
+ <link rel="preconnect" href="https://fonts.googleapis.com">
8
+ <link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;500;600;700;900&family=Share+Tech+Mono&family=Rajdhani:wght@300;400;500;600;700&family=Noto+Sans+SC:wght@300;400;500;700&display=swap" rel="stylesheet">
9
+ <style>
10
+ :root{
11
+ --void:#03060e;--deep:#060b18;--panel:rgba(10,20,40,0.55);--panel-solid:#0a1428;--glass:rgba(14,28,54,0.4);
12
+ --hud:#1de9ff;--hud-dim:rgba(29,233,255,0.5);--hud-faint:rgba(29,233,255,0.12);--hud-glow:rgba(29,233,255,0.6);
13
+ --plasma:#b066ff;--plasma-dim:rgba(176,102,255,0.5);--plasma-faint:rgba(176,102,255,0.12);
14
+ --amber:#ffb347;--amber-faint:rgba(255,179,71,0.12);--jade:#1dffa6;--jade-faint:rgba(29,255,166,0.12);
15
+ --alert:#ff4060;--alert-faint:rgba(255,64,96,0.12);--rose:#ff5e9c;--azure:#4d9fff;
16
+ --txt:#cfe8ff;--txt-dim:#6b88aa;--txt-faint:#3a4d68;
17
+ --line:rgba(29,233,255,0.18);--line-soft:rgba(120,160,220,0.1);
18
+ --mono:'Share Tech Mono',monospace;--display:'Orbitron',sans-serif;--tech:'Rajdhani',sans-serif;--sc:'Noto Sans SC',sans-serif;
19
+ }
20
+ *,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
21
+ html,body{height:100%;width:100%;background:var(--void);color:var(--txt);font-family:var(--tech);overflow:hidden;-webkit-font-smoothing:antialiased;user-select:none}
22
+ ::-webkit-scrollbar{width:5px;height:5px}::-webkit-scrollbar-track{background:transparent}
23
+ ::-webkit-scrollbar-thumb{background:var(--hud-faint);border-radius:3px}::-webkit-scrollbar-thumb:hover{background:var(--hud-dim)}
24
+
25
+ .deck-bg{position:fixed;inset:0;z-index:0;background:radial-gradient(ellipse 80% 50% at 50% 0%,rgba(29,233,255,0.06),transparent 60%),radial-gradient(ellipse 60% 40% at 80% 100%,rgba(176,102,255,0.05),transparent 55%),radial-gradient(ellipse 50% 60% at 15% 80%,rgba(29,255,166,0.03),transparent 50%),var(--void)}
26
+ .grid-overlay{position:fixed;inset:0;z-index:1;pointer-events:none;background-image:linear-gradient(var(--line-soft) 1px,transparent 1px),linear-gradient(90deg,var(--line-soft) 1px,transparent 1px);background-size:48px 48px;mask-image:radial-gradient(ellipse 90% 80% at 50% 45%,#000 30%,transparent 90%);opacity:0.5}
27
+ .scanline{position:fixed;left:0;right:0;height:120px;z-index:2;pointer-events:none;background:linear-gradient(to bottom,transparent,rgba(29,233,255,0.025),transparent);animation:scan 8s linear infinite}
28
+ @keyframes scan{0%{top:-120px}100%{top:100%}}
29
+ .starfield{position:fixed;inset:0;z-index:1;pointer-events:none}
30
+ .star{position:absolute;background:#fff;border-radius:50%;animation:twinkle var(--d) ease-in-out infinite}
31
+ @keyframes twinkle{0%,100%{opacity:var(--o)}50%{opacity:0.1}}
32
+
33
+ .deck{position:relative;z-index:5;height:100vh;width:100vw;display:grid;grid-template-rows:54px 1fr 88px;grid-template-areas:"command" "bridge" "console"}
34
+
35
+ .command-bar{grid-area:command;display:flex;align-items:center;padding:0 20px;gap:18px;background:linear-gradient(to bottom,rgba(8,16,32,0.9),rgba(6,12,24,0.5));border-bottom:1px solid var(--line);position:relative}
36
+ .command-bar::after{content:'';position:absolute;bottom:-1px;left:0;right:0;height:1px;background:linear-gradient(90deg,transparent,var(--hud-dim),transparent)}
37
+ .deck-brand{display:flex;align-items:center;gap:10px}
38
+ .brand-mark{width:34px;height:34px;position:relative;display:flex;align-items:center;justify-content:center}
39
+ .brand-mark::before{content:'';position:absolute;inset:0;border:1.5px solid var(--hud);border-radius:50%;border-top-color:transparent;border-bottom-color:transparent;animation:spin 4s linear infinite}
40
+ .brand-mark::after{content:'';position:absolute;inset:5px;border:1px solid var(--plasma-dim);border-radius:50%;border-left-color:transparent;border-right-color:transparent;animation:spin 3s linear infinite reverse}
41
+ .brand-core{width:8px;height:8px;background:var(--hud);border-radius:50%;box-shadow:0 0 10px var(--hud-glow)}
42
+ @keyframes spin{to{transform:rotate(360deg)}}
43
+ .brand-text{font-family:var(--display);font-weight:700;font-size:16px;letter-spacing:0.18em;color:var(--txt)}
44
+ .brand-text b{color:var(--hud);text-shadow:0 0 12px var(--hud-glow)}
45
+ .brand-ver{font-family:var(--mono);font-size:9px;color:var(--txt-faint);letter-spacing:0.1em;margin-top:-2px}
46
+ .cmd-divider{width:1px;height:26px;background:var(--line)}
47
+ .workspace-tabs{display:flex;gap:4px}
48
+ .ws-tab{font-family:var(--tech);font-size:12px;font-weight:600;letter-spacing:0.05em;padding:6px 14px;border-radius:4px;cursor:pointer;color:var(--txt-dim);border:1px solid transparent;transition:all .15s;text-transform:uppercase;display:flex;align-items:center;gap:6px}
49
+ .ws-tab:hover{color:var(--txt);background:var(--hud-faint)}
50
+ .ws-tab.active{color:var(--hud);border-color:var(--hud-dim);background:var(--hud-faint);text-shadow:0 0 8px var(--hud-glow)}
51
+ .ws-tab .dot{width:5px;height:5px;border-radius:50%;background:currentColor}
52
+ .cmd-spacer{flex:1}
53
+ .sys-telemetry{display:flex;align-items:center;gap:14px;font-family:var(--mono);font-size:10px}
54
+ .tele-item{display:flex;flex-direction:column;align-items:flex-end;line-height:1.2}
55
+ .tele-label{color:var(--txt-faint);font-size:8px;letter-spacing:0.15em}
56
+ .tele-val{color:var(--hud);font-size:12px}.tele-val.warn{color:var(--amber)}.tele-val.ok{color:var(--jade)}
57
+ .add-widget-btn{display:none;align-items:center;gap:6px;border:1px solid var(--plasma-dim);border-radius:4px;padding:6px 12px;cursor:pointer;color:var(--plasma);font-family:var(--tech);font-size:12px;font-weight:600;letter-spacing:0.05em;background:var(--plasma-faint);transition:all .15s}
58
+ .add-widget-btn:hover{background:rgba(176,102,255,0.2)}
59
+ .mode-toggle.edit ~ .add-widget-btn,body.edit .add-widget-btn{display:flex}
60
+ .mode-toggle{display:flex;align-items:center;gap:8px;border:1px solid var(--line);border-radius:20px;padding:5px 6px 5px 12px;cursor:pointer;transition:all .2s}
61
+ .mode-toggle:hover{border-color:var(--hud-dim)}
62
+ .mode-toggle .mt-label{font-size:11px;font-weight:600;letter-spacing:0.06em;color:var(--txt-dim)}
63
+ .mode-switch{width:36px;height:18px;border-radius:10px;background:var(--panel-solid);border:1px solid var(--line);position:relative;transition:all .2s}
64
+ .mode-switch::after{content:'';position:absolute;top:1px;left:1px;width:14px;height:14px;border-radius:50%;background:var(--txt-dim);transition:all .25s}
65
+ .mode-toggle.edit .mode-switch{background:var(--plasma-faint);border-color:var(--plasma-dim)}
66
+ .mode-toggle.edit .mode-switch::after{left:19px;background:var(--plasma);box-shadow:0 0 8px var(--plasma)}
67
+ .mode-toggle.edit .mt-label{color:var(--plasma)}
68
+
69
+ .bridge{grid-area:bridge;display:grid;grid-template-columns:300px 1fr 320px;gap:14px;padding:14px 18px;overflow:hidden;min-height:0;position:relative}
70
+ .bridge.edit-mode{background:linear-gradient(var(--plasma-faint) 1px,transparent 1px),linear-gradient(90deg,var(--plasma-faint) 1px,transparent 1px);background-size:24px 24px}
71
+ .bridge-col{display:flex;flex-direction:column;gap:14px;overflow:hidden;min-height:0}
72
+
73
+ .panel{background:var(--panel);border:1px solid var(--line);border-radius:4px;position:relative;backdrop-filter:blur(8px);display:flex;flex-direction:column;overflow:hidden}
74
+ .panel::before,.panel::after{content:'';position:absolute;width:12px;height:12px;pointer-events:none;border-color:var(--hud-dim);border-style:solid}
75
+ .panel::before{top:-1px;left:-1px;border-width:1px 0 0 1px}
76
+ .panel::after{bottom:-1px;right:-1px;border-width:0 1px 1px 0}
77
+ .panel-corner-tr,.panel-corner-bl{position:absolute;width:12px;height:12px;pointer-events:none;border-color:var(--hud-dim);border-style:solid}
78
+ .panel-corner-tr{top:-1px;right:-1px;border-width:1px 1px 0 0}
79
+ .panel-corner-bl{bottom:-1px;left:-1px;border-width:0 0 1px 1px}
80
+ .bridge.edit-mode .panel{border-color:var(--plasma-dim);cursor:move}
81
+ .bridge.edit-mode .panel::before,.bridge.edit-mode .panel::after,.bridge.edit-mode .panel-corner-tr,.bridge.edit-mode .panel-corner-bl{border-color:var(--plasma)}
82
+ .panel.dragging{opacity:0.7;z-index:100;box-shadow:0 0 30px var(--plasma-dim)}
83
+ .panel-head{display:flex;align-items:center;gap:8px;padding:9px 12px;border-bottom:1px solid var(--line);flex-shrink:0;background:linear-gradient(to right,var(--hud-faint),transparent)}
84
+ .panel-head .ph-icon{color:var(--hud);font-size:13px;width:16px;text-align:center}
85
+ .panel-head .ph-title{font-family:var(--display);font-size:10px;font-weight:600;letter-spacing:0.12em;color:var(--txt);text-transform:uppercase;flex:1}
86
+ .panel-head .ph-status{font-family:var(--mono);font-size:9px;color:var(--txt-dim)}
87
+ .panel-head .ph-drag{color:var(--plasma);font-size:12px;display:none;cursor:grab}
88
+ .panel-head .ph-remove{color:var(--alert);font-size:11px;cursor:pointer}
89
+ .bridge.edit-mode .panel-head .ph-drag{display:block}
90
+ .panel-body{flex:1;overflow-y:auto;min-height:0;position:relative}
91
+
92
+ .roster{padding:8px}
93
+ .agent-pod{display:flex;align-items:center;gap:10px;padding:9px 10px;margin-bottom:6px;border:1px solid var(--line-soft);border-radius:4px;cursor:pointer;transition:all .15s;position:relative;overflow:hidden;background:var(--glass)}
94
+ .agent-pod::before{content:'';position:absolute;left:0;top:0;bottom:0;width:2px;background:var(--apc,var(--hud));opacity:0;transition:opacity .15s}
95
+ .agent-pod:hover{border-color:var(--apc,var(--hud-dim));background:var(--hud-faint)}.agent-pod:hover::before{opacity:1}
96
+ .agent-pod.active{border-color:var(--apc);background:rgba(29,233,255,0.06);box-shadow:inset 0 0 20px rgba(29,233,255,0.05)}
97
+ .agent-pod.active::before{opacity:1;box-shadow:0 0 10px var(--apc)}
98
+ .pod-avatar{width:38px;height:38px;border-radius:4px;flex-shrink:0;position:relative;display:flex;align-items:center;justify-content:center;font-size:18px;background:var(--glass);border:1px solid var(--apc,var(--hud-dim))}
99
+ .pod-avatar .ring-pulse{position:absolute;inset:-3px;border:1px solid var(--apc,var(--hud));border-radius:5px;opacity:0;animation:podpulse 2s ease-out infinite}
100
+ .agent-pod.busy .pod-avatar .ring-pulse{opacity:1}
101
+ @keyframes podpulse{0%{transform:scale(1);opacity:0.6}100%{transform:scale(1.25);opacity:0}}
102
+ .pod-info{flex:1;min-width:0}
103
+ .pod-name{font-family:var(--display);font-size:11px;font-weight:600;letter-spacing:0.04em;color:var(--txt)}
104
+ .pod-role{font-size:10px;color:var(--txt-dim);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
105
+ .pod-state{display:flex;align-items:center;gap:4px;margin-top:3px;font-family:var(--mono);font-size:8px;letter-spacing:0.08em;color:var(--txt-faint)}
106
+ .pod-state .s-dot{width:5px;height:5px;border-radius:50%;background:var(--jade);box-shadow:0 0 5px var(--jade)}
107
+ .agent-pod.busy .pod-state .s-dot{background:var(--amber);box-shadow:0 0 5px var(--amber);animation:blink 1s infinite}
108
+ @keyframes blink{50%{opacity:0.3}}
109
+ .pod-metric{text-align:right;flex-shrink:0}
110
+ .pod-metric .pm-val{font-family:var(--mono);font-size:16px;color:var(--apc,var(--hud))}
111
+ .pod-metric .pm-lbl{font-size:8px;color:var(--txt-faint);letter-spacing:0.06em}
112
+
113
+ .core-panel{flex:1;display:flex;flex-direction:column;min-height:0}
114
+ .core-stage{flex:1;position:relative;display:flex;flex-direction:column;align-items:center;justify-content:center;overflow:hidden;min-height:0}
115
+ .core-orbit{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:340px;height:340px}
116
+ .orbit-ring{position:absolute;border-radius:50%;border:1px solid var(--hud-faint);top:50%;left:50%;transform:translate(-50%,-50%)}
117
+ .orbit-ring.r1{width:340px;height:340px;border-style:dashed;animation:spin 60s linear infinite}
118
+ .orbit-ring.r2{width:260px;height:260px;border-color:var(--plasma-faint);animation:spin 40s linear infinite reverse}
119
+ .orbit-ring.r3{width:190px;height:190px;border-color:var(--hud-faint)}
120
+ .orbit-tick{position:absolute;top:50%;left:50%;width:340px;height:340px;transform:translate(-50%,-50%);animation:spin 30s linear infinite}
121
+ .orbit-tick span{position:absolute;width:3px;height:8px;background:var(--hud-dim);top:0;left:50%;transform-origin:center 170px}
122
+ .radar-sweep{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:340px;height:340px;border-radius:50%;background:conic-gradient(from 0deg,transparent 0deg,transparent 320deg,rgba(29,233,255,0.15) 358deg,rgba(29,233,255,0.3) 360deg);animation:spin 4s linear infinite}
123
+ .core-avatar{position:relative;z-index:3;width:130px;height:130px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:54px;background:radial-gradient(circle at 50% 40%,rgba(29,233,255,0.18),rgba(10,20,40,0.8));border:2px solid var(--cac,var(--hud));box-shadow:0 0 40px var(--cac,var(--hud-glow)),inset 0 0 30px rgba(29,233,255,0.15);animation:coreFloat 4s ease-in-out infinite}
124
+ @keyframes coreFloat{0%,100%{transform:translateY(0)}50%{transform:translateY(-8px)}}
125
+ .core-name{position:relative;z-index:3;margin-top:22px;font-family:var(--display);font-size:20px;font-weight:700;letter-spacing:0.2em;color:var(--txt);text-shadow:0 0 16px var(--cac,var(--hud-glow));text-transform:uppercase}
126
+ .core-role{position:relative;z-index:3;font-family:var(--tech);font-size:12px;color:var(--txt-dim);letter-spacing:0.15em;margin-top:2px}
127
+ .core-statusline{position:relative;z-index:3;margin-top:18px;font-family:var(--mono);font-size:12px;color:var(--cac,var(--hud));letter-spacing:0.1em;min-height:18px;display:flex;align-items:center;gap:8px}
128
+ .core-statusline .cs-cursor{width:8px;height:14px;background:var(--cac,var(--hud));animation:blink 1s step-end infinite}
129
+ .core-telemetry{position:relative;z-index:3;display:flex;gap:18px;margin-top:18px}
130
+ .ct-stat{text-align:center}.ct-stat .cts-val{font-family:var(--mono);font-size:20px;color:var(--txt)}.ct-stat .cts-lbl{font-size:9px;color:var(--txt-faint);letter-spacing:0.1em;text-transform:uppercase}
131
+ .core-hud-tl,.core-hud-tr,.core-hud-bl,.core-hud-br{position:absolute;font-family:var(--mono);font-size:9px;color:var(--txt-faint);line-height:1.5;z-index:4;letter-spacing:0.05em}
132
+ .core-hud-tl{top:12px;left:14px}.core-hud-tr{top:12px;right:14px;text-align:right}.core-hud-bl{bottom:12px;left:14px}.core-hud-br{bottom:12px;right:14px;text-align:right}
133
+ .core-hud-tl b,.core-hud-tr b,.core-hud-bl b,.core-hud-br b{color:var(--hud);font-weight:400}
134
+
135
+ .harness-widget{padding:12px;display:flex;align-items:center;gap:14px}
136
+ .h-ring-svg{flex-shrink:0}
137
+ .h-ring-bg{fill:none;stroke:var(--line);stroke-width:5}
138
+ .h-ring-fg{fill:none;stroke-width:5;stroke-linecap:round;transform:rotate(-90deg);transform-origin:50% 50%;transition:stroke-dashoffset 1s ease;filter:drop-shadow(0 0 4px currentColor)}
139
+ .h-ring-label{font-family:var(--mono);text-anchor:middle;dominant-baseline:central}
140
+ .h-info{flex:1}
141
+ .h-mode{display:inline-flex;align-items:center;gap:5px;font-family:var(--mono);font-size:9px;padding:3px 8px;border-radius:3px;letter-spacing:0.08em}
142
+ .h-mode.shadow{background:var(--amber-faint);color:var(--amber)}.h-mode.active{background:var(--jade-faint);color:var(--jade)}
143
+ .h-detail{font-family:var(--mono);font-size:10px;color:var(--txt-dim);margin-top:8px;line-height:1.6}.h-detail b{color:var(--hud)}
144
+ .weight-section{padding:0 12px 12px}
145
+ .wt-row{display:flex;align-items:center;gap:8px;margin-bottom:7px}
146
+ .wt-lbl{font-size:10px;color:var(--txt-dim);width:56px;flex-shrink:0;letter-spacing:0.03em}
147
+ .wt-track{flex:1;height:3px;background:var(--panel-solid);border-radius:2px;overflow:hidden;border:1px solid var(--line-soft)}
148
+ .wt-fill{height:100%;border-radius:2px;transition:width .8s ease;box-shadow:0 0 6px currentColor}
149
+ .wt-pct{font-family:var(--mono);font-size:9px;color:var(--txt-faint);width:26px;text-align:right}
150
+
151
+ .tc-stream{padding:10px 12px;display:flex;flex-direction:column;gap:5px}
152
+ .tc-node{display:flex;gap:8px;align-items:flex-start}
153
+ .tc-rail{display:flex;flex-direction:column;align-items:center;flex-shrink:0;padding-top:3px}
154
+ .tc-dot{width:7px;height:7px;border-radius:50%;border:1.5px solid var(--tcc,var(--hud));background:var(--void);flex-shrink:0;box-shadow:0 0 6px var(--tcc,var(--hud))}
155
+ .tc-seg{width:1px;flex:1;min-height:14px;background:linear-gradient(var(--tcc,var(--line)),transparent);margin-top:2px;opacity:0.5}
156
+ .tc-card{flex:1;background:var(--glass);border:1px solid var(--line-soft);border-radius:3px;padding:6px 9px;margin-bottom:3px}
157
+ .tc-top{display:flex;justify-content:space-between;align-items:center;margin-bottom:3px}
158
+ .tc-type{font-family:var(--mono);font-size:8px;letter-spacing:0.1em;color:var(--txt-faint);text-transform:uppercase}
159
+ .tc-stat{font-family:var(--mono);font-size:8px;padding:1px 5px;border-radius:3px;letter-spacing:0.05em}
160
+ .st-PASS{background:var(--jade-faint);color:var(--jade)}.st-WARN{background:var(--amber-faint);color:var(--amber)}.st-BLOCK{background:var(--alert-faint);color:var(--alert)}.st-BASELINE{background:var(--plasma-faint);color:var(--plasma)}.st-PENDING{background:var(--hud-faint);color:var(--txt-dim)}
161
+ .tc-text{font-size:10px;color:var(--txt-dim);line-height:1.5;font-family:var(--sc)}
162
+ .tc-meta{display:flex;gap:6px;margin-top:4px}
163
+ .tc-pill{font-family:var(--mono);font-size:8px;color:var(--txt-faint);background:var(--panel-solid);padding:1px 5px;border-radius:3px;border:1px solid var(--line-soft)}
164
+
165
+ .evt-feed{padding:8px 10px;display:flex;flex-direction:column;gap:5px}
166
+ .evt{background:var(--glass);border:1px solid var(--line-soft);border-radius:3px;padding:6px 9px;border-left:2px solid var(--ec,var(--hud));animation:evtIn .3s ease}
167
+ @keyframes evtIn{from{opacity:0;transform:translateX(-6px)}to{opacity:1;transform:translateX(0)}}
168
+ .evt-top{display:flex;justify-content:space-between;align-items:center}
169
+ .evt-type{font-family:var(--mono);font-size:8px;letter-spacing:0.1em;text-transform:uppercase;color:var(--ec,var(--hud))}
170
+ .evt-time{font-family:var(--mono);font-size:8px;color:var(--txt-faint)}
171
+ .evt-body{font-size:9px;color:var(--txt-dim);margin-top:2px;line-height:1.4;font-family:var(--mono)}
172
+
173
+ /* DAG widget */
174
+ .dag-wrap{padding:8px 10px;height:100%;display:flex;align-items:center;justify-content:center}
175
+ .dag-svg{width:100%;height:100%;max-height:230px}
176
+ .dag-edge{transition:stroke .3s}
177
+ .dag-edge-flow{stroke:var(--hud)!important;stroke-width:2.5!important;stroke-dasharray:6 6;animation:dagDash .7s linear;filter:drop-shadow(0 0 4px var(--hud-glow))}
178
+ @keyframes dagDash{from{stroke-dashoffset:24}to{stroke-dashoffset:0}}
179
+ .dag-node{transition:opacity .3s}
180
+ .dag-node-shape{transition:all .3s}
181
+ .dag-node.dag-dim{opacity:0.35}
182
+ .dag-node.dag-active .dag-node-shape{filter:drop-shadow(0 0 6px var(--dnc))}
183
+ .dag-node.dag-active .dag-label{fill:var(--dnc)!important}
184
+ .dag-node.dag-pulse .dag-node-shape{stroke-width:3;filter:drop-shadow(0 0 12px var(--dnc));transform-box:fill-box;transform-origin:center;animation:dagNodePulse .7s ease}
185
+ @keyframes dagNodePulse{0%,100%{transform:scale(1)}40%{transform:scale(1.25)}}
186
+ .dag-icon{pointer-events:none}
187
+
188
+ /* Terminal widget */
189
+ .term{height:100%;display:flex;flex-direction:column;font-family:var(--mono);font-size:11px;padding:8px 10px}
190
+ .term-out{flex:1;overflow-y:auto;min-height:0;line-height:1.55}
191
+ .term-line{display:flex;gap:6px;white-space:pre-wrap;word-break:break-word;animation:termIn .15s ease}
192
+ @keyframes termIn{from{opacity:0}to{opacity:1}}
193
+ .tl-ts{color:var(--txt-faint);flex-shrink:0;font-size:9px;padding-top:1px}
194
+ .tl-pfx{flex-shrink:0;font-weight:bold}
195
+ .tl-txt{flex:1}
196
+ .tl-cmd .tl-pfx,.tl-cmd .tl-txt{color:var(--hud)}
197
+ .tl-info .tl-pfx,.tl-info .tl-txt{color:var(--azure)}
198
+ .tl-ok .tl-pfx,.tl-ok .tl-txt{color:var(--jade)}
199
+ .tl-warn .tl-pfx,.tl-warn .tl-txt{color:var(--amber)}
200
+ .tl-err .tl-pfx,.tl-err .tl-txt{color:var(--alert)}
201
+ .tl-tool .tl-pfx,.tl-tool .tl-txt{color:var(--plasma)}
202
+ .tl-out .tl-txt{color:var(--txt-dim)}
203
+ .term-prompt-line{display:flex;align-items:center;gap:3px;flex-shrink:0;padding-top:6px;border-top:1px solid var(--line-soft);margin-top:4px;font-size:10px}
204
+ .term-ps{color:var(--jade)}.term-ps-sep{color:var(--txt-dim)}.term-ps-path{color:var(--azure)}.term-ps-dollar{color:var(--txt-dim);margin:0 4px}
205
+ .term-caret{width:7px;height:13px;background:var(--hud);animation:blink 1s step-end infinite}
206
+
207
+ /* AI Browser widget */
208
+ .brz{height:100%;display:flex;flex-direction:column;padding:6px}
209
+ .brz-bar{display:flex;align-items:center;gap:8px;padding:6px 8px;background:var(--panel-solid);border:1px solid var(--line);border-radius:4px 4px 0 0;flex-shrink:0}
210
+ .brz-dots{display:flex;gap:4px}
211
+ .brz-dots span{width:7px;height:7px;border-radius:50%;background:var(--txt-faint)}
212
+ .brz-dots span:nth-child(1){background:#ff4060}.brz-dots span:nth-child(2){background:#ffb347}.brz-dots span:nth-child(3){background:#1dffa6}
213
+ .brz-url{flex:1;font-family:var(--mono);font-size:10px;color:var(--txt-dim);background:var(--void);padding:4px 10px;border-radius:3px;border:1px solid var(--line-soft);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
214
+ .brz-status{font-family:var(--mono);font-size:8px;padding:2px 6px;border-radius:3px;letter-spacing:0.05em;background:var(--hud-faint);color:var(--txt-dim)}
215
+ .brz-status.loading{background:var(--amber-faint);color:var(--amber)}
216
+ .brz-status.loaded{background:var(--jade-faint);color:var(--jade)}
217
+ .brz-progress{height:2px;width:0;background:var(--hud);box-shadow:0 0 6px var(--hud-glow);transition:width .6s ease,opacity .3s;opacity:0}
218
+ .brz-viewport{flex:1;background:rgba(8,14,28,0.6);border:1px solid var(--line);border-top:none;border-radius:0 0 4px 4px;overflow:hidden;position:relative;min-height:0}
219
+ .brz-empty{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;color:var(--txt-faint);font-family:var(--mono);font-size:10px}
220
+ .brz-page{padding:12px;position:relative;height:100%}
221
+ .brz-skel{background:linear-gradient(90deg,var(--line-soft),rgba(29,233,255,0.06),var(--line-soft));background-size:200% 100%;animation:skelShine 1.5s infinite;border-radius:3px}
222
+ @keyframes skelShine{0%{background-position:200% 0}100%{background-position:-200% 0}}
223
+ .brz-skel-title{height:14px;width:55%;margin-bottom:10px}
224
+ .brz-skel-nav{display:flex;gap:8px;margin-bottom:12px}
225
+ .brz-skel-nav span{height:8px;width:36px;background:var(--line-soft);border-radius:3px}
226
+ .brz-hero{height:50px;background:linear-gradient(135deg,var(--hud-faint),var(--plasma-faint));border-radius:4px;margin-bottom:12px;border:1px solid var(--line-soft)}
227
+ .brz-skel-line{height:7px;margin-bottom:7px}.w90{width:90%}.w75{width:75%}.w85{width:85%}
228
+ .brz-cards{display:flex;gap:8px;margin-top:12px}
229
+ .brz-card{flex:1;height:40px;background:var(--glass);border:1px solid var(--line-soft);border-radius:4px}
230
+ .brz-cursor{position:absolute;font-size:14px;color:var(--hud);transition:left .5s ease,top .5s ease,opacity .3s;opacity:0;z-index:5;filter:drop-shadow(0 0 6px var(--hud-glow));pointer-events:none;transform:translate(-50%,-50%)}
231
+ .brz-cursor::after{content:attr(data-label);position:absolute;left:16px;top:-2px;font-family:var(--sc);font-size:9px;color:var(--hud);background:var(--void);padding:2px 6px;border-radius:3px;border:1px solid var(--hud-dim);white-space:nowrap}
232
+ .brz-cursor.act-click{color:var(--amber)}.brz-cursor.act-extract{color:var(--jade)}
233
+ .brz-ripple{position:absolute;width:20px;height:20px;border:2px solid var(--hud);border-radius:50%;transform:translate(-50%,-50%);animation:brzRipple .6s ease-out;pointer-events:none;z-index:4}
234
+ @keyframes brzRipple{0%{width:8px;height:8px;opacity:1}100%{width:40px;height:40px;opacity:0}}
235
+
236
+ /* Voice widget */
237
+ .voice{height:100%;display:flex;flex-direction:column;padding:10px;gap:8px}
238
+ .voice-wave{width:100%;height:44px;background:var(--void);border:1px solid var(--line-soft);border-radius:4px;flex-shrink:0}
239
+ .voice-transcript{flex:1;font-family:var(--sc);font-size:12px;color:var(--txt-faint);line-height:1.5;overflow-y:auto;min-height:24px;display:flex;align-items:center;padding:0 4px}
240
+ .voice-transcript.has-text{color:var(--txt);align-items:flex-start}
241
+ .voice-controls{display:flex;align-items:center;gap:10px;flex-shrink:0}
242
+ .voice-mic{width:40px;height:40px;border-radius:50%;border:1.5px solid var(--hud);background:var(--hud-faint);color:var(--hud);font-size:16px;cursor:pointer;transition:all .2s;flex-shrink:0}
243
+ .voice-mic:hover{background:rgba(29,233,255,0.2)}
244
+ .voice-mic.recording{border-color:var(--alert);color:var(--alert);background:var(--alert-faint);animation:micPulse 1.2s infinite}
245
+ @keyframes micPulse{0%,100%{box-shadow:0 0 0 0 rgba(255,64,96,0.4)}50%{box-shadow:0 0 0 8px rgba(255,64,96,0)}}
246
+ .voice-meta{flex:1;min-width:0}
247
+ .voice-state{font-family:var(--mono);font-size:11px;color:var(--txt-dim)}
248
+ .voice-hint{font-size:9px;color:var(--txt-faint);margin-top:1px}
249
+ .voice-send{background:var(--jade-faint);border:1px solid var(--jade);color:var(--jade);font-family:var(--tech);font-size:11px;font-weight:600;padding:8px 12px;border-radius:4px;cursor:pointer;transition:all .15s;flex-shrink:0}
250
+ .voice-send:hover:not(:disabled){background:rgba(29,255,166,0.2)}
251
+ .voice-send:disabled{opacity:0.35;cursor:not-allowed}
252
+
253
+ .console{grid-area:console;display:flex;align-items:center;gap:14px;padding:0 18px;background:linear-gradient(to top,rgba(8,16,32,0.9),rgba(6,12,24,0.4));border-top:1px solid var(--line);position:relative}
254
+ .console::before{content:'';position:absolute;top:0;left:0;right:0;height:1px;background:linear-gradient(90deg,transparent,var(--hud-dim),transparent)}
255
+ .flow-viz{display:flex;align-items:center;gap:6px;flex-shrink:0}
256
+ .flow-node{width:34px;height:34px;border-radius:4px;display:flex;align-items:center;justify-content:center;font-size:15px;border:1px solid var(--line);background:var(--glass);position:relative;transition:all .2s}
257
+ .flow-node.lit{border-color:var(--hud);box-shadow:0 0 12px var(--hud-glow);background:var(--hud-faint)}
258
+ .flow-arrow{color:var(--txt-faint);font-size:12px;font-family:var(--mono)}
259
+ .flow-arrow.lit{color:var(--hud);text-shadow:0 0 8px var(--hud-glow)}
260
+ .console-input-wrap{flex:1;display:flex;align-items:center;gap:10px;position:relative}
261
+ .ci-prompt{font-family:var(--mono);font-size:14px;color:var(--hud);text-shadow:0 0 8px var(--hud-glow)}
262
+ .console-input{flex:1;background:var(--panel);border:1px solid var(--line);border-radius:4px;color:var(--txt);font-family:var(--sc);font-size:14px;padding:11px 16px;outline:none;transition:all .2s;letter-spacing:0.02em}
263
+ .console-input:focus{border-color:var(--hud);box-shadow:0 0 0 1px var(--hud-dim),inset 0 0 20px rgba(29,233,255,0.05)}
264
+ .console-input::placeholder{color:var(--txt-faint)}.console-input:disabled{opacity:0.5}
265
+ .quick-cmds{display:flex;gap:6px;position:absolute;top:-34px;left:30px}
266
+ .qc{background:var(--panel);border:1px solid var(--line-soft);border-radius:14px;color:var(--txt-dim);font-family:var(--tech);font-size:11px;font-weight:500;padding:4px 11px;cursor:pointer;transition:all .15s;backdrop-filter:blur(8px)}
267
+ .qc:hover{border-color:var(--hud-dim);color:var(--hud)}
268
+ .agent-target{background:var(--panel);border:1px solid var(--line);border-radius:4px;color:var(--hud);font-family:var(--tech);font-size:13px;font-weight:600;padding:11px 12px;outline:none;cursor:pointer;letter-spacing:0.04em}
269
+ .transmit-btn{background:var(--hud-faint);border:1px solid var(--hud);border-radius:4px;color:var(--hud);font-family:var(--display);font-size:12px;font-weight:600;letter-spacing:0.1em;padding:11px 22px;cursor:pointer;transition:all .15s;text-transform:uppercase;display:flex;align-items:center;gap:8px}
270
+ .transmit-btn:hover{background:rgba(29,233,255,0.2);box-shadow:0 0 16px var(--hud-glow)}
271
+ .transmit-btn:active{transform:scale(0.97)}.transmit-btn:disabled{opacity:0.4;cursor:not-allowed;box-shadow:none}
272
+
273
+ .chat-log{position:absolute;bottom:12px;left:14px;right:14px;max-height:42%;overflow-y:auto;display:flex;flex-direction:column;gap:8px;z-index:5;pointer-events:auto}
274
+ .msg{max-width:80%;padding:8px 12px;border-radius:4px;font-size:12px;line-height:1.6;font-family:var(--sc);backdrop-filter:blur(6px)}
275
+ .msg.user{align-self:flex-end;background:var(--plasma-faint);border:1px solid var(--plasma-dim);color:var(--txt)}
276
+ .msg.agent{align-self:flex-start;background:var(--glass);border:1px solid var(--line)}
277
+ .msg.blocked{background:var(--alert-faint);border-color:var(--alert)}
278
+ .msg .m-tag{font-family:var(--mono);font-size:9px;color:var(--txt-faint);margin-bottom:3px;letter-spacing:0.06em}
279
+ .empty{padding:24px;text-align:center;color:var(--txt-faint);font-family:var(--mono);font-size:10px;letter-spacing:0.05em}
280
+ .offline-banner{position:fixed;bottom:96px;left:50%;transform:translateX(-50%);z-index:200;background:var(--amber-faint);border:1px solid var(--amber);border-radius:4px;padding:6px 16px;font-family:var(--mono);font-size:10px;color:var(--amber);letter-spacing:0.05em;display:none}
281
+
282
+ /* Widget Library modal */
283
+ .lib-overlay{position:fixed;inset:0;z-index:300;background:rgba(3,6,14,0.8);backdrop-filter:blur(4px);display:none;align-items:center;justify-content:center}
284
+ .lib-overlay.show{display:flex}
285
+ .lib-modal{background:var(--panel-solid);border:1px solid var(--plasma-dim);border-radius:6px;padding:20px;width:480px;max-width:90vw;position:relative}
286
+ .lib-modal::before,.lib-modal::after{content:'';position:absolute;width:14px;height:14px;border-color:var(--plasma);border-style:solid}
287
+ .lib-modal::before{top:-1px;left:-1px;border-width:1px 0 0 1px}.lib-modal::after{bottom:-1px;right:-1px;border-width:0 1px 1px 0}
288
+ .lib-title{font-family:var(--display);font-size:13px;font-weight:600;letter-spacing:0.1em;color:var(--plasma);margin-bottom:14px;text-transform:uppercase}
289
+ .lib-grid{display:grid;grid-template-columns:1fr 1fr;gap:8px}
290
+ .lib-card{background:var(--glass);border:1px solid var(--line-soft);border-radius:4px;padding:12px;cursor:pointer;transition:all .15s;display:flex;align-items:center;gap:10px}
291
+ .lib-card:hover{border-color:var(--plasma-dim);background:var(--plasma-faint)}
292
+ .lib-icon{font-size:18px;color:var(--hud);width:24px;text-align:center}
293
+ .lib-info{flex:1}.lib-name{font-family:var(--tech);font-size:12px;font-weight:600;color:var(--txt)}.lib-col{font-size:9px;color:var(--txt-faint);font-family:var(--mono)}
294
+ .lib-close{position:absolute;top:14px;right:16px;color:var(--txt-dim);cursor:pointer;font-size:16px}.lib-close:hover{color:var(--alert)}
295
+ </style>
296
+ </head>
297
+ <body>
298
+
299
+ <div class="deck-bg"></div>
300
+ <div class="starfield" id="starfield"></div>
301
+ <div class="grid-overlay"></div>
302
+ <div class="scanline"></div>
303
+
304
+ <div class="deck">
305
+ <div class="command-bar">
306
+ <div class="deck-brand">
307
+ <div class="brand-mark"><div class="brand-core"></div></div>
308
+ <div><div class="brand-text">FOXCLAW <b>DECK</b></div><div class="brand-ver">AI COMMAND BRIDGE · v2.0 · WIDGET RUNTIME</div></div>
309
+ </div>
310
+ <div class="cmd-divider"></div>
311
+ <div class="workspace-tabs" id="wsTabs">
312
+ <div class="ws-tab active" data-ws="command"><span class="dot"></span>指挥</div>
313
+ <div class="ws-tab" data-ws="research"><span class="dot"></span>研究</div>
314
+ <div class="ws-tab" data-ws="finance"><span class="dot"></span>财务</div>
315
+ <div class="ws-tab" data-ws="warroom"><span class="dot"></span>作战室</div>
316
+ </div>
317
+ <div class="cmd-spacer"></div>
318
+ <div class="sys-telemetry">
319
+ <div class="tele-item"><span class="tele-label">AGENTS</span><span class="tele-val ok" id="teleAgents">5/5</span></div>
320
+ <div class="tele-item"><span class="tele-label">MEMORY</span><span class="tele-val" id="teleMemory">0</span></div>
321
+ <div class="tele-item"><span class="tele-label">HARNESS</span><span class="tele-val warn" id="teleHarness">SHADOW</span></div>
322
+ <div class="tele-item"><span class="tele-label">UPLINK</span><span class="tele-val" id="teleUplink">···</span></div>
323
+ </div>
324
+ <div class="cmd-divider"></div>
325
+ <div class="mode-toggle" id="modeToggle" onclick="toggleMode()">
326
+ <span class="mt-label">显示</span><div class="mode-switch"></div>
327
+ </div>
328
+ <div class="add-widget-btn" id="addWidgetBtn" onclick="openLibrary()">+ 添加面板</div>
329
+ </div>
330
+
331
+ <div class="bridge" id="bridge">
332
+ <div class="bridge-col" id="colLeft"></div>
333
+ <div class="bridge-col" id="colCenter"></div>
334
+ <div class="bridge-col" id="colRight"></div>
335
+ </div>
336
+
337
+ <div class="console">
338
+ <div class="flow-viz" id="flowViz">
339
+ <div class="flow-node" data-flow="supervisor" title="主管">🏢</div><span class="flow-arrow">›</span>
340
+ <div class="flow-node" data-flow="finance" title="财务">💰</div><span class="flow-arrow">›</span>
341
+ <div class="flow-node" data-flow="office" title="办公学习 · 日程">📚</div>
342
+ </div>
343
+ <div class="cmd-divider"></div>
344
+ <div class="console-input-wrap">
345
+ <div class="quick-cmds">
346
+ <div class="qc" onclick="quickCmd('分析本月收支情况')">分析收支</div>
347
+ <div class="qc" onclick="quickCmd('制定下周工作计划')">工作计划</div>
348
+ <div class="qc" onclick="quickCmd('学习AI最新论文摘要')">学习总结</div>
349
+ <div class="qc" onclick="quickCmd('生成月度报告')">生成报告</div>
350
+ </div>
351
+ <span class="ci-prompt">❯</span>
352
+ <input class="console-input" id="consoleInput" placeholder="下达指令... 主管 Agent 将自动调度" onkeydown="handleKey(event)">
353
+ </div>
354
+ <select class="agent-target" id="agentTarget">
355
+ <option value="supervisor">◈ 主管</option>
356
+ <option value="finance">◈ 财务</option><option value="office">◈ 办公学习</option>
357
+ </select>
358
+ <button class="transmit-btn" id="transmitBtn" onclick="transmit()"><span>传输</span> ❯❯</button>
359
+ </div>
360
+ </div>
361
+
362
+ <div class="offline-banner" id="offlineBanner">⚠ 后端离线 · 显示演示数据</div>
363
+
364
+ <div class="lib-overlay" id="libOverlay" onclick="if(event.target===this)closeLibrary()">
365
+ <div class="lib-modal">
366
+ <div class="lib-close" onclick="closeLibrary()">✕</div>
367
+ <div class="lib-title">面板库 · 点击添加到指挥舱</div>
368
+ <div class="lib-grid" id="libGrid"></div>
369
+ </div>
370
+ </div>
371
+
372
+ <script src="widget_runtime.js"></script>
373
+ <script src="widgets.js"></script>
374
+ <script>
375
+ /* ════════════════════════════════════════════════════════════
376
+ FOXCLAW DECK v2 — Main wiring
377
+ ════════════════════════════════════════════════════════════ */
378
+ // 从后端服务时(http://host:port)用同源地址;直接双击打开 html(file://)时回退到 localhost:8000
379
+ const _origin = (location.protocol === 'http:' || location.protocol === 'https:')
380
+ ? location.origin : 'http://localhost:8000';
381
+ const API = _origin + '/api';
382
+ const WS = (_origin.replace(/^http/, 'ws')) + '/ws/harness';
383
+ const AGENTS=[
384
+ {id:'supervisor',name:'主管',code:'SUPERVISOR',role:'全局统筹',emoji:'🏢',color:'#1de9ff',tasks:0},
385
+ {id:'finance', name:'财务',code:'FINANCE', role:'财务专业',emoji:'💰',color:'#ffb347',tasks:0},
386
+ {id:'office', name:'办公学习',code:'OFFICE', role:'办公·学习·日程',emoji:'📚',color:'#1dffa6',tasks:0},
387
+ ];
388
+ const AG=id=>AGENTS.find(a=>a.id===id);
389
+ let activeAgent='supervisor',editMode=false,online=false,ws=null,taskSeq=0;
390
+ const tasks=[];
391
+
392
+ const esc=s=>String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/\n/g,'<br>');
393
+ const statusColor=s=>({PASS:'#1dffa6',WARN:'#ffb347',BLOCK:'#ff4060',BASELINE:'#b066ff',PENDING:'#3a4d68'}[s]||'#1de9ff');
394
+
395
+ /* ── 共享上下文:widget 通过 ctx 调用这些 ── */
396
+ const ctx={
397
+ AGENTS, AG, activeAgent,
398
+ setActiveCore,
399
+ loadChainInto, loadHarnessInto,
400
+ };
401
+
402
+ window.addEventListener('DOMContentLoaded',()=>{
403
+ buildStars();
404
+ WidgetRuntime.init(
405
+ {left:document.getElementById('colLeft'),center:document.getElementById('colCenter'),right:document.getElementById('colRight')},
406
+ ctx
407
+ );
408
+ // 恢复布局,失败则用默认
409
+ if(!WidgetRuntime.loadLayout()){
410
+ WidgetRuntime.setLayout(WidgetRuntime.defaultLayout());
411
+ }
412
+ WidgetRuntime.notifyAgentChange('supervisor');
413
+ bindWorkspaces();
414
+ connectWS();
415
+ pollAll();
416
+ setInterval(pollAll,10000);
417
+ });
418
+
419
+ function buildStars(){
420
+ const f=document.getElementById('starfield');let h='';
421
+ for(let i=0;i<90;i++){const s=Math.random()*1.8+0.3,o=Math.random()*0.6+0.2,d=Math.random()*4+2;
422
+ h+=`<div class="star" style="left:${Math.random()*100}%;top:${Math.random()*100}%;width:${s}px;height:${s}px;--o:${o};--d:${d}s"></div>`;}
423
+ f.innerHTML=h;
424
+ }
425
+
426
+ /* ── ACTIVE CORE ── */
427
+ function setActiveCore(id){
428
+ activeAgent=id; ctx.activeAgent=id;
429
+ document.getElementById('agentTarget').value=id;
430
+ document.documentElement.style.setProperty('--cac',AG(id).color);
431
+ WidgetRuntime.notifyAgentChange(id);
432
+ WidgetRuntime.refreshAll(id);
433
+ setFlowActive(id);
434
+ }
435
+ function setCoreStatus(text,busy){
436
+ const coreBody=ctx._coreEl;
437
+ if(coreBody){
438
+ const t=coreBody.querySelector('[data-role="core-status-text"]'); if(t)t.textContent=text;
439
+ }
440
+ // roster busy state
441
+ if(ctx._rosterRefresh)ctx._rosterRefresh();
442
+ }
443
+
444
+ /* ── FLOW ── */
445
+ function setFlowActive(id){document.querySelectorAll('.flow-node').forEach(n=>n.classList.toggle('lit',n.dataset.flow===id));}
446
+ function dagPulse(id){if(ctx._dagEl&&typeof _dagPulse==='function')_dagPulse(ctx._dagEl,id);}
447
+ function browseIfRelevant(task,agentId){
448
+ if(!ctx.browseTo)return;
449
+ // 涉及搜索/学习/查询的任务触发浏览器可视化
450
+ const webKeywords=['学习','搜索','查','论文','新闻','网','资料','了解','调研'];
451
+ if(webKeywords.some(k=>task.includes(k))){
452
+ const q=encodeURIComponent(task.slice(0,20));
453
+ ctx.browseTo(`https://www.bing.com/search?q=${q}`);
454
+ }
455
+ }
456
+ function termExec(task,agentId){
457
+ if(!ctx.termLog)return;
458
+ const a=AG(agentId);
459
+ const steps=[
460
+ [`task dispatch --agent ${agentId} "${task.slice(0,24)}"`,'cmd',0],
461
+ [`[${a.code}] 接收指令 · 注册目标基线`,'info',300],
462
+ [`recall --query "${task.slice(0,16)}" --top-k 5`,'tool',650],
463
+ [`检索到 3 条相关记忆 · 注入上下文`,'ok',950],
464
+ [`reasoning · 分析任务类型与执行路径`,'out',1300],
465
+ [`harness check · Δ drift 评估中...`,'tool',1700],
466
+ [`harness PASS · 推理链已记录`,'ok',2050],
467
+ ];
468
+ steps.forEach(([line,type,delay])=>setTimeout(()=>ctx.termLog(line,type),delay));
469
+ }
470
+ function animateFlow(){
471
+ const nodes=document.querySelectorAll('.flow-node'),arrows=document.querySelectorAll('.flow-arrow');let i=0;
472
+ const iv=setInterval(()=>{nodes.forEach(n=>n.classList.remove('lit'));arrows.forEach(a=>a.classList.remove('lit'));
473
+ if(i<nodes.length){nodes[i].classList.add('lit');if(i>0)arrows[i-1].classList.add('lit');}i++;
474
+ if(i>nodes.length){clearInterval(iv);setFlowActive(activeAgent);}},180);
475
+ }
476
+
477
+ /* ── MODE / LIBRARY ── */
478
+ function toggleMode(){
479
+ editMode=!editMode;
480
+ const t=document.getElementById('modeToggle'),b=document.getElementById('bridge');
481
+ t.classList.toggle('edit',editMode);
482
+ document.body.classList.toggle('edit',editMode);
483
+ t.querySelector('.mt-label').textContent=editMode?'编辑':'显示';
484
+ b.classList.toggle('edit-mode',editMode);
485
+ WidgetRuntime.setEditMode(editMode);
486
+ document.querySelectorAll('.panel').forEach(p=>editMode?enableDrag(p):disableDrag(p));
487
+ }
488
+ function openLibrary(){
489
+ const grid=document.getElementById('libGrid');
490
+ grid.innerHTML=WidgetRuntime.getLibrary().map(w=>`
491
+ <div class="lib-card" onclick="addPanel('${w.id}')">
492
+ <div class="lib-icon">${w.icon}</div>
493
+ <div class="lib-info"><div class="lib-name">${w.title}</div><div class="lib-col">→ ${w.defaultColumn}</div></div>
494
+ </div>`).join('');
495
+ document.getElementById('libOverlay').classList.add('show');
496
+ }
497
+ function closeLibrary(){document.getElementById('libOverlay').classList.remove('show');}
498
+ function addPanel(widgetId){
499
+ WidgetRuntime.addInstance(widgetId);
500
+ closeLibrary();
501
+ if(editMode){document.querySelectorAll('.panel').forEach(p=>enableDrag(p));WidgetRuntime.setEditMode(true);}
502
+ }
503
+
504
+ /* ── DRAG ── */
505
+ let dragEl=null,dragOX=0,dragOY=0;
506
+ function enableDrag(panel){
507
+ const handle=panel.querySelector('.ph-drag');if(!handle)return;
508
+ handle.onmousedown=e=>{
509
+ e.preventDefault();dragEl=panel;
510
+ const r=panel.getBoundingClientRect();dragOX=e.clientX-r.left;dragOY=e.clientY-r.top;
511
+ panel.classList.add('dragging');panel.style.position='fixed';panel.style.width=r.width+'px';
512
+ panel.style.left=r.left+'px';panel.style.top=r.top+'px';panel.style.zIndex=200;
513
+ document.onmousemove=onDragMove;document.onmouseup=onDragEnd;
514
+ };
515
+ }
516
+ function disableDrag(panel){const h=panel.querySelector('.ph-drag');if(h)h.onmousedown=null;
517
+ panel.style.position='';panel.style.left='';panel.style.top='';panel.style.width='';panel.style.zIndex='';}
518
+ function onDragMove(e){if(!dragEl)return;dragEl.style.left=(e.clientX-dragOX)+'px';dragEl.style.top=(e.clientY-dragOY)+'px';
519
+ ['colLeft','colCenter','colRight'].forEach(id=>{const c=document.getElementById(id),r=c.getBoundingClientRect();
520
+ c.style.outline=(e.clientX>r.left&&e.clientX<r.right)?'1px dashed var(--plasma)':'';});}
521
+ function onDragEnd(e){
522
+ if(!dragEl)return;let target=null;
523
+ ['colLeft','colCenter','colRight'].forEach(id=>{const c=document.getElementById(id),r=c.getBoundingClientRect();
524
+ c.style.outline='';if(e.clientX>r.left&&e.clientX<r.right)target=c;});
525
+ const p=dragEl;p.classList.remove('dragging');
526
+ p.style.position='';p.style.left='';p.style.top='';p.style.width='';p.style.zIndex='';
527
+ if(target){const sibs=[...target.children];let inserted=false;
528
+ for(const s of sibs){if(s!==p&&e.clientY<s.getBoundingClientRect().top+s.offsetHeight/2){target.insertBefore(p,s);inserted=true;break;}}
529
+ if(!inserted&&p.parentElement!==target)target.appendChild(p);}
530
+ dragEl=null;document.onmousemove=null;document.onmouseup=null;
531
+ WidgetRuntime.captureLayoutFromDOM();
532
+ }
533
+
534
+ /* ── WORKSPACES ── */
535
+ function bindWorkspaces(){
536
+ document.querySelectorAll('.ws-tab').forEach(t=>{t.onclick=()=>{
537
+ document.querySelectorAll('.ws-tab').forEach(x=>x.classList.remove('active'));t.classList.add('active');
538
+ const map={research:'office',finance:'finance',warroom:'supervisor',command:'supervisor'};
539
+ if(map[t.dataset.ws])setActiveCore(map[t.dataset.ws]);};});
540
+ }
541
+
542
+ /* ── CONSOLE ── */
543
+ function handleKey(e){if(e.key==='Enter'){e.preventDefault();transmit();}}
544
+ function quickCmd(t){document.getElementById('consoleInput').value=t;transmit();}
545
+ async function transmit(){
546
+ const input=document.getElementById('consoleInput'),task=input.value.trim();if(!task)return;
547
+ const agentId=document.getElementById('agentTarget').value;
548
+ if(agentId!==activeAgent)setActiveCore(agentId);
549
+ const btn=document.getElementById('transmitBtn');btn.disabled=true;input.value='';
550
+ addMsg('user',task);addTask(task,agentId);animateFlow();dagPulse(agentId);
551
+ termExec(task,agentId);browseIfRelevant(task,agentId);
552
+ setCoreStatus('解析指令...',true);typeStatus(['解析指令...','检索记忆...','规划方案...','执行中...']);
553
+ const a=AG(agentId);a.tasks++;updatePodTask(agentId,a.tasks);
554
+ try{
555
+ const r=await fetch(`${API}/task`,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({task,agent_id:agentId})});
556
+ const data=await r.json();
557
+ if(data.error==='harness_blocked'){addMsg('agent',`⛔ HARNESS 截断 · ${data.reason}`,true);setCoreStatus('指令被拦截',false);updateTask(task,'BLOCK');if(ctx.termLog)ctx.termLog(`harness BLOCK · ${data.reason}`,'err');}
558
+ else{const res=typeof data.result==='string'?data.result:JSON.stringify(data.result,null,2);
559
+ addMsg('agent',res);setCoreStatus('任务完成 · 待命中',false);updateTask(task,'PASS');
560
+ if(ctx.termLog)ctx.termLog('task complete · 记忆已巩固写回','ok');
561
+ // 主管真实调度:若结果含 dispatched_to,DAG 沿真实派发路径重新点亮
562
+ const dt=data.result&&data.result.dispatched_to;
563
+ if(dt&&dt!=='supervisor'&&agentId==='supervisor'){
564
+ if(ctx.termLog)ctx.termLog(`dispatch → ${dt} · 子 Agent 执行并汇聚`,'tool');
565
+ dagPulse(dt);
566
+ }
567
+ WidgetRuntime.refreshAll(agentId);}
568
+ }catch(e){
569
+ addMsg('agent',`◌ 离线模式 · 演示响应:已记录任务「${task.slice(0,20)}」并写入 ${a.name} 记忆库`);
570
+ setCoreStatus('离线演示 · 待命中',false);updateTask(task,'PASS');demoChain(agentId,task);
571
+ if(ctx.termLog)ctx.termLog('offline · 演示执行完成 · 记忆已写入','ok');
572
+ }finally{btn.disabled=false;}
573
+ }
574
+ let typeIv=null;
575
+ function typeStatus(steps){clearInterval(typeIv);let i=0;
576
+ typeIv=setInterval(()=>{const coreBody=ctx._coreEl;
577
+ if(i<steps.length&&coreBody){const t=coreBody.querySelector('[data-role="core-status-text"]');if(t)t.textContent=steps[i];i++;}
578
+ else clearInterval(typeIv);},600);}
579
+
580
+ function addMsg(role,content,blocked){
581
+ const coreBody=ctx._coreEl;if(!coreBody)return;
582
+ const log=coreBody.querySelector('[data-role="chat-log"]');if(!log)return;
583
+ const a=AG(activeAgent);const el=document.createElement('div');
584
+ el.className=`msg ${role}${blocked?' blocked':''}`;
585
+ el.innerHTML=role==='agent'?`<div class="m-tag">${a.name} AGENT</div>${esc(content)}`:esc(content);
586
+ log.appendChild(el);log.scrollTop=log.scrollHeight;
587
+ }
588
+ function updatePodTask(agentId,n){
589
+ document.querySelectorAll(`[data-podtask="${agentId}"]`).forEach(e=>e.textContent=n);
590
+ const coreBody=ctx._coreEl;if(coreBody){const t=coreBody.querySelector('[data-core="tasks"]');if(t&&agentId===activeAgent)t.textContent=n;}
591
+ }
592
+
593
+ /* ── TASKS ── */
594
+ function addTask(name,agentId){
595
+ const id=`tsk${taskSeq++}`,a=AG(agentId);
596
+ const s=ctx._taskStreamEl;if(!s)return;
597
+ s.querySelector('.empty')?.remove();
598
+ const el=document.createElement('div');el.className='tc-node';el.id=id;
599
+ el.innerHTML=`<div class="tc-rail"><div class="tc-dot" style="--tcc:${a.color}"></div></div>
600
+ <div class="tc-card"><div class="tc-top"><span class="tc-type">${a.code}</span><span class="tc-stat st-PENDING" id="${id}-st">RUNNING</span></div>
601
+ <div class="tc-text">${esc(name.slice(0,50))}</div></div>`;
602
+ s.insertBefore(el,s.firstChild);tasks.unshift({id,name,agentId});
603
+ }
604
+ function updateTask(name,status){
605
+ const t=tasks.find(x=>name.startsWith(x.name.slice(0,30))||x.name.startsWith(name.slice(0,30)));if(!t)return;
606
+ const el=document.getElementById(`${t.id}-st`);if(!el)return;
607
+ el.className=`tc-stat st-${status}`;el.textContent=status==='PASS'?'DONE':status==='BLOCK'?'BLOCKED':'RUNNING';
608
+ }
609
+
610
+ /* ── DATA LOADERS (called by widgets via ctx) ── */
611
+ async function loadChainInto(el,agentId){
612
+ const stream=el.querySelector('[data-role="tc-stream"]');if(!stream)return;
613
+ try{const r=await fetch(`${API}/thoughtchain/${agentId}/chain`);const d=await r.json();
614
+ renderChainEl(stream,(d.nodes||[]).slice(-15).reverse());}
615
+ catch{stream.innerHTML='<div class="empty">离线 · 无推理数据</div>';}
616
+ }
617
+ function renderChainEl(stream,nodes){
618
+ if(!nodes.length){stream.innerHTML='<div class="empty">执行任务后显示推理轨迹</div>';return;}
619
+ stream.innerHTML=nodes.map((n,i)=>{const c=statusColor(n.harness_status);
620
+ return`<div class="tc-node"><div class="tc-rail"><div class="tc-dot" style="--tcc:${c}"></div>${i<nodes.length-1?`<div class="tc-seg" style="--tcc:${c}"></div>`:''}</div>
621
+ <div class="tc-card"><div class="tc-top"><span class="tc-type">${n.type||'reasoning'}</span><span class="tc-stat st-${n.harness_status}">${n.harness_status}</span></div>
622
+ <div class="tc-text">${esc((n.content||'').slice(0,80))}${(n.content||'').length>80?'…':''}</div>
623
+ <div class="tc-meta"><span class="tc-pill">conf ${(n.conf||0).toFixed(2)}</span>${n.harness_delta!=null?`<span class="tc-pill">Δ ${n.harness_delta.toFixed(3)}</span>`:''}${n.dominant_dim?`<span class="tc-pill">${n.dominant_dim}</span>`:''}</div></div></div>`;}).join('');
624
+ // 更新核心 HUD
625
+ const coreBody=ctx._coreEl;if(coreBody){const nn=coreBody.querySelector('[data-hud="nodes"]');if(nn)nn.textContent=nodes.length;
626
+ const cn=coreBody.querySelector('[data-core="nodes"]');if(cn)cn.textContent=nodes.length;}
627
+ }
628
+ function demoChain(agentId,task){
629
+ const stream=document.querySelector('[data-widget="thoughtchain"] [data-role="tc-stream"]');if(!stream)return;
630
+ const demo=[
631
+ {type:'goal',content:`目标基线:${task}`,conf:1.0,harness_status:'BASELINE'},
632
+ {type:'memory_retrieval',content:'检索相关历史记忆 3 条',conf:0.95,harness_status:'PASS',harness_delta:0.04,dominant_dim:'semantic'},
633
+ {type:'reasoning',content:'分析任务类型与执行路径',conf:0.91,harness_status:'PASS',harness_delta:0.08,dominant_dim:'semantic'},
634
+ {type:'decision',content:'生成执行方案并记录决策',conf:0.88,harness_status:'PASS',harness_delta:0.11,dominant_dim:'scope'},
635
+ ];
636
+ renderChainEl(stream,demo.reverse());
637
+ }
638
+ async function loadHarnessInto(el,agentId){
639
+ try{const[hR,sR]=await Promise.all([fetch(`${API}/harness/${agentId}/status`),fetch(`${API}/thoughtchain/${agentId}/summary`)]);
640
+ applyHarnessEl(el,await hR.json(),await sR.json());}
641
+ catch{applyHarnessEl(el,{thresholds:{run_count:0,shadow_rounds:20,block:0.25,warn:0.15,shadow_mode:true},weights:{semantic:0.5,tool:0.3,scope:0.2},policy:{version:1,learned_rules:0}},{avg_delta:0,total_nodes:0,avg_confidence:0});}
642
+ }
643
+ function applyHarnessEl(el,h,sum){
644
+ const th=h.thresholds||{},wt=h.weights||{semantic:0.5,tool:0.3,scope:0.2},pol=h.policy||{};
645
+ const prog=Math.min((th.run_count||0)/(th.shadow_rounds||20),1),circ=2*Math.PI*30;
646
+ const ring=el.querySelector('[data-role="h-ring"]');
647
+ if(ring){ring.style.strokeDashoffset=circ*(1-prog);ring.style.stroke=th.shadow_mode?'#ffb347':'#1dffa6';}
648
+ const setT=(r,v)=>{const e=el.querySelector(`[data-role="${r}"]`);if(e)e.textContent=v;};
649
+ setT('h-ring-val',th.run_count||0);setT('h-block',(th.block||0.25).toFixed(3));
650
+ setT('h-warn',(th.warn||0.15).toFixed(3));setT('h-avg',(sum.avg_delta||0).toFixed(3));
651
+ const chip=el.querySelector('[data-role="h-mode"]');
652
+ if(chip){if(th.shadow_mode===false){chip.className='h-mode active';chip.textContent='● ACTIVE 活跃';
653
+ document.getElementById('teleHarness').textContent='ACTIVE';document.getElementById('teleHarness').className='tele-val ok';}
654
+ else{chip.className='h-mode shadow';chip.textContent='◔ SHADOW 校准';}}
655
+ const setW=(r,v)=>{const f=el.querySelector(`[data-role="wt-${r}"]`),p=el.querySelector(`[data-role="wt-${r}-pct"]`);
656
+ if(f)f.style.width=`${(v*100).toFixed(0)}%`;if(p)p.textContent=`${(v*100).toFixed(0)}%`;};
657
+ setW('sem',wt.semantic||0.5);setW('tool',wt.tool||0.3);setW('scope',wt.scope||0.2);
658
+ // core HUD
659
+ const coreBody=ctx._coreEl;if(coreBody){
660
+ const u=(k,v)=>{const e=coreBody.querySelector(`[data-hud="${k}"]`);if(e)e.textContent=v;};
661
+ u('conf',(sum.avg_confidence||0).toFixed(2));u('drift',(sum.avg_delta||0).toFixed(3));
662
+ u('policy','v'+(pol.version||1));u('rules',pol.learned_rules||0);u('block',(th.block||0.25).toFixed(2));
663
+ u('mode',th.shadow_mode===false?'ACTIVE':'SHADOW');
664
+ const m=coreBody.querySelector('[data-core="mem"]');if(m)m.textContent=sum.total_nodes||0;
665
+ }
666
+ }
667
+
668
+ /* ── POLL + WS ── */
669
+ async function pollAll(){
670
+ try{const r=await fetch(`${API}/status`);if(!r.ok)throw 0;const data=await r.json();setOnline(true);
671
+ let mem=0;for(const[id,s]of Object.entries(data))mem+=s?.thought_chain?.total_nodes||0;
672
+ document.getElementById('teleMemory').textContent=mem;WidgetRuntime.refreshAll(activeAgent);
673
+ }catch{setOnline(false);}
674
+ }
675
+ function setOnline(v){online=v;
676
+ document.getElementById('teleUplink').textContent=v?'LINKED':'OFFLINE';
677
+ document.getElementById('teleUplink').className=v?'tele-val ok':'tele-val warn';
678
+ document.getElementById('offlineBanner').style.display=v?'none':'block';}
679
+ function connectWS(){
680
+ try{ws=new WebSocket(WS);
681
+ ws.onmessage=e=>{try{const m=JSON.parse(e.data);WidgetRuntime.dispatch(m.event,{data:m.data});}catch{}};
682
+ ws.onopen=()=>setOnline(true);ws.onclose=()=>setTimeout(connectWS,4000);}catch{}
683
+ }
684
+ </script>
685
+ </body>
686
+ </html>