foxclaw 0.1.2 → 0.1.4
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.
- package/index.js +1 -8
- package/package.json +1 -1
- package/python/.env.example +8 -0
- package/python/.github/workflows/ci.yml +47 -2
- package/python/.github/workflows/release.yml +5 -0
- package/python/CHANGELOG.md +100 -0
- package/python/CHANGELOG_CN.md +100 -0
- package/python/README.md +17 -2
- package/python/README_CN.md +38 -20
- package/python/bench/bm25_audit.py +1 -14
- package/python/bench/bm25_regression.py +0 -1
- package/python/bench/bm25_stress.py +0 -4
- package/python/bench/cd_audit.py +0 -23
- package/python/bench/chaos.py +6 -8
- package/python/bench/e2e_real_models.py +0 -1
- package/python/bench/llm_retry_test.py +2 -9
- package/python/bench/load_test.py +5 -8
- package/python/bench/vector_ivf_upgrade.py +1 -7
- package/python/docs/CONTENT_GUARD.md +189 -0
- package/python/docs/CONTENT_GUARD_CN.md +189 -0
- package/python/docs/RELEASE.md +78 -37
- package/python/docs/RUN_CN.md +2 -2
- package/python/evals/governance/cases.py +0 -37
- package/python/evals/governance/run_evals.py +5 -37
- package/python/examples/governance_middleware_demo.py +1 -6
- package/python/examples/langgraph_governance_demo.py +7 -11
- package/python/foxclaw/__init__.py +74 -152
- package/python/foxclaw/agents/base.py +82 -136
- package/python/foxclaw/agents/impl/_helpers.py +2 -10
- package/python/foxclaw/agents/impl/finance.py +4 -21
- package/python/foxclaw/agents/impl/office.py +7 -24
- package/python/foxclaw/agents/impl/supervisor.py +12 -79
- package/python/foxclaw/agents/mixins/_context.py +0 -23
- package/python/foxclaw/agents/mixins/augment.py +10 -16
- package/python/foxclaw/agents/mixins/llm.py +7 -6
- package/python/foxclaw/agents/mixins/memory.py +23 -3
- package/python/foxclaw/agents/mixins/preference.py +58 -8
- package/python/foxclaw/agents/mixins/prompt.py +59 -12
- package/python/foxclaw/agents/mixins/skillbox.py +6 -6
- package/python/foxclaw/agents/mixins/tool_exec.py +16 -42
- package/python/foxclaw/agents/rules/finance/RULES.md +131 -0
- package/python/foxclaw/api/__init__.py +18 -77
- package/python/foxclaw/api/_state.py +43 -0
- package/python/foxclaw/api/bootstrap.py +114 -0
- package/python/foxclaw/api/middleware.py +5 -28
- package/python/foxclaw/api/routes/ahs.py +60 -73
- package/python/foxclaw/api/routes/attachments.py +5 -6
- package/python/foxclaw/api/routes/browser.py +7 -11
- package/python/foxclaw/api/routes/domain.py +18 -26
- package/python/foxclaw/api/routes/knowledge.py +3 -21
- package/python/foxclaw/api/routes/llm_admin.py +21 -33
- package/python/foxclaw/api/routes/memory.py +50 -12
- package/python/foxclaw/api/routes/orchestration.py +3 -5
- package/python/foxclaw/api/routes/recovery.py +4 -3
- package/python/foxclaw/api/routes/review.py +2 -11
- package/python/foxclaw/api/routes/search.py +3 -7
- package/python/foxclaw/api/routes/system.py +33 -49
- package/python/foxclaw/api/routes/task.py +31 -49
- package/python/foxclaw/api/routes/ws.py +2 -6
- package/python/foxclaw/api/spine.py +1 -4
- package/python/foxclaw/behavior/__init__.py +20 -0
- package/python/foxclaw/behavior/behavior.yaml +62 -0
- package/python/foxclaw/behavior/spec.py +287 -0
- package/python/foxclaw/browser/action/action_executor.py +1 -5
- package/python/foxclaw/browser/action/action_types.py +0 -1
- package/python/foxclaw/browser/action/url_policy.py +1 -5
- package/python/foxclaw/browser/adapter/web_adapter.py +1 -1
- package/python/foxclaw/browser/driver/playwright_driver.py +20 -16
- package/python/foxclaw/browser/perception/dom_extractor.py +0 -1
- package/python/foxclaw/browser/perception/element_indexer.py +1 -2
- package/python/foxclaw/browser/service.py +7 -11
- package/python/foxclaw/capability/embedding_service.py +4 -6
- package/python/foxclaw/channels/__init__.py +9 -11
- package/python/foxclaw/channels/discord.py +4 -13
- package/python/foxclaw/channels/dispatcher.py +2 -27
- package/python/foxclaw/channels/feishu.py +10 -20
- package/python/foxclaw/channels/openclaw_bridge.py +4 -9
- package/python/foxclaw/channels/signing.py +9 -9
- package/python/foxclaw/channels/telegram.py +3 -4
- package/python/foxclaw/channels/weixin.py +7 -17
- package/python/foxclaw/cli.py +42 -13
- package/python/foxclaw/cognitive/reasoner.py +9 -10
- package/python/foxclaw/cognitive/state.py +18 -24
- package/python/foxclaw/core/audit_chain.py +1 -3
- package/python/foxclaw/core/audit_sink.py +3 -3
- package/python/foxclaw/core/compaction.py +8 -26
- package/python/foxclaw/core/config.py +2 -2
- package/python/foxclaw/core/confirm_nonce.py +6 -12
- package/python/foxclaw/core/content_guard.py +341 -0
- package/python/foxclaw/core/envfile.py +2 -2
- package/python/foxclaw/core/failure_policy.py +17 -26
- package/python/foxclaw/core/gateway.py +6 -10
- package/python/foxclaw/core/governance_audit.py +3 -7
- package/python/foxclaw/core/idempotency.py +15 -22
- package/python/foxclaw/core/injection_guard.py +10 -29
- package/python/foxclaw/core/memory_adapter.py +53 -60
- package/python/foxclaw/core/metrics.py +4 -5
- package/python/foxclaw/core/model_router.py +74 -150
- package/python/foxclaw/core/paths.py +1 -1
- package/python/foxclaw/core/recovery.py +21 -34
- package/python/foxclaw/core/scheduler.py +23 -26
- package/python/foxclaw/core/secrets.py +0 -3
- package/python/foxclaw/core/sensitive_write.py +283 -0
- package/python/foxclaw/core/sessions.py +4 -6
- package/python/foxclaw/core/settings.py +16 -32
- package/python/foxclaw/core/shared_write.py +11 -23
- package/python/foxclaw/core/steering_queue.py +12 -11
- package/python/foxclaw/core/system_bus.py +1 -1
- package/python/foxclaw/core/thought_chain.py +30 -32
- package/python/foxclaw/core/token_meter.py +9 -13
- package/python/foxclaw/core/tools.py +4 -12
- package/python/foxclaw/core/tracing.py +25 -40
- package/python/foxclaw/core/truth_pipeline.py +5 -9
- package/python/foxclaw/core/update_check.py +149 -0
- package/python/foxclaw/core/vec_recall_adapter.py +11 -37
- package/python/foxclaw/core/verify.py +6 -14
- package/python/foxclaw/core/web_search.py +4 -28
- package/python/foxclaw/finance/evidence.py +14 -18
- package/python/foxclaw/finance/fx.py +1 -3
- package/python/foxclaw/finance/graph.py +9 -18
- package/python/foxclaw/finance/models.py +20 -24
- package/python/foxclaw/finance/nlparse.py +12 -72
- package/python/foxclaw/finance/policy.py +8 -36
- package/python/foxclaw/finance/reasoner.py +3 -11
- package/python/foxclaw/finance/reasoning.py +13 -34
- package/python/foxclaw/finance/reconcile.py +5 -10
- package/python/foxclaw/finance/runtime.py +19 -46
- package/python/foxclaw/governance/middleware.py +13 -21
- package/python/foxclaw/harness/_base.py +2 -8
- package/python/foxclaw/harness/_policy.py +19 -38
- package/python/foxclaw/harness/drift.py +1 -13
- package/python/foxclaw/harness/event_bus.py +2 -5
- package/python/foxclaw/harness/harness.py +9 -97
- package/python/foxclaw/harness/loop_detection.py +11 -18
- package/python/foxclaw/itinerary/amap_client.py +2 -5
- package/python/foxclaw/itinerary/models.py +7 -7
- package/python/foxclaw/itinerary/reasoner.py +1 -6
- package/python/foxclaw/itinerary/reasoning.py +15 -21
- package/python/foxclaw/itinerary/runtime.py +8 -12
- package/python/foxclaw/kernel/capabilities.py +5 -6
- package/python/foxclaw/kernel/policy_gate.py +4 -10
- package/python/foxclaw/kernel/process_manager.py +17 -36
- package/python/foxclaw/kernel/scheduler.py +3 -9
- package/python/foxclaw/kernel/sentinel.py +13 -23
- package/python/foxclaw/kernel/sentinel_client.py +12 -15
- package/python/foxclaw/kernel/spine.py +9 -9
- package/python/foxclaw/knowledge/__init__.py +4 -20
- package/python/foxclaw/knowledge/truth_crystallizer.py +5 -8
- package/python/foxclaw/learning/knowledge_graph.py +4 -9
- package/python/foxclaw/learning/models.py +13 -13
- package/python/foxclaw/learning/reasoner.py +1 -4
- package/python/foxclaw/learning/runtime.py +19 -31
- package/python/foxclaw/learning/spaced_repetition.py +2 -7
- package/python/foxclaw/mcp_server.py +2 -3
- package/python/foxclaw/orchestration/__init__.py +16 -33
- package/python/foxclaw/pipeline/cognitive_pipeline.py +45 -97
- package/python/foxclaw/pipeline/stage_meta.py +21 -0
- package/python/foxclaw/pipeline/supervisor_stages.py +13 -42
- package/python/foxclaw/planning/llm_planner.py +3 -3
- package/python/foxclaw/planning/planner.py +6 -11
- package/python/foxclaw/search/agent_port.py +7 -11
- package/python/foxclaw/search/evidence.py +0 -4
- package/python/foxclaw/search/internal_search.py +3 -5
- package/python/foxclaw/search/models.py +11 -13
- package/python/foxclaw/search/planner.py +3 -3
- package/python/foxclaw/search/retriever.py +1 -1
- package/python/foxclaw/search/runtime.py +1 -8
- package/python/foxclaw/skills/__init__.py +46 -100
- package/python/foxclaw/skills/archive.py +111 -0
- package/python/foxclaw/skills/fetch.py +226 -0
- package/python/foxclaw/skills/hooks_spec.py +21 -25
- package/python/foxclaw/skills/install_cmd.py +216 -0
- package/python/foxclaw/skills/lifecycle.py +16 -42
- package/python/foxclaw/skills/markdown_skill.py +10 -9
- package/python/foxclaw/skills/mcp_client.py +5 -11
- package/python/foxclaw/skills/mcp_store.py +0 -3
- package/python/foxclaw/skills/permission.py +13 -48
- package/python/foxclaw/skills/pipeline.py +21 -53
- package/python/foxclaw/skills/policy.py +4 -10
- package/python/foxclaw/skills/review.py +3 -3
- package/python/frontend/deck/auth.js +0 -7
- package/python/frontend/deck/chat_model_picker.js +0 -54
- package/python/frontend/deck/domain.js +0 -20
- package/python/frontend/deck/main.js +0 -144
- package/python/frontend/deck/markdown.js +0 -4
- package/python/frontend/deck/settings.js +1 -18
- package/python/frontend/deck/state.js +0 -1
- package/python/frontend/deck/util.js +0 -2
- package/python/frontend/deck/visuals.js +0 -22
- package/python/frontend/planet/planet.js +18 -201
- package/python/frontend/v3/assets/index-JKfHlb-a.css +1 -0
- package/python/frontend/v3/assets/index-tN90dpUH.js +34 -0
- package/python/frontend/v3/index.html +3 -3
- package/python/frontend/widget_runtime.js +0 -43
- package/python/frontend/widgets.js +0 -57
- package/python/frontend_src/index.html +1 -1
- package/python/frontend_src/package-lock.json +2 -2
- package/python/frontend_src/package.json +1 -1
- package/python/frontend_src/src/App.vue +3 -7
- package/python/frontend_src/src/api.js +0 -7
- package/python/frontend_src/src/components/ChatDock.vue +27 -39
- package/python/frontend_src/src/components/DocViewer.vue +0 -9
- package/python/frontend_src/src/components/Inspector.vue +0 -6
- package/python/frontend_src/src/components/LeftRail.vue +60 -39
- package/python/frontend_src/src/components/MenuPopover.vue +9 -11
- package/python/frontend_src/src/components/MessageStream.vue +18 -30
- package/python/frontend_src/src/components/ModelPicker.vue +0 -5
- package/python/frontend_src/src/components/QuickIngest.vue +0 -6
- package/python/frontend_src/src/components/Stage.vue +71 -25
- package/python/frontend_src/src/components/ToolMenu.vue +0 -3
- package/python/frontend_src/src/components/TopBar.vue +38 -10
- package/python/frontend_src/src/components/panels/ActivityPanel.vue +0 -2
- package/python/frontend_src/src/components/panels/AgentsPanel.vue +2 -5
- package/python/frontend_src/src/components/panels/KnowledgePanel.vue +0 -16
- package/python/frontend_src/src/components/panels/MemoryPanel.vue +42 -3
- package/python/frontend_src/src/components/panels/PanelBox.vue +6 -7
- package/python/frontend_src/src/components/panels/ReviewPanel.vue +0 -16
- package/python/frontend_src/src/components/panels/SettingsPanel.vue +142 -38
- package/python/frontend_src/src/components/panels/TasksPanel.vue +0 -1
- package/python/frontend_src/src/components/panels/ToolsPanel.vue +125 -19
- package/python/frontend_src/src/lib/fmt.js +0 -4
- package/python/frontend_src/src/lib/i18n.js +53 -0
- package/python/frontend_src/src/lib/locales/en.js +173 -0
- package/python/frontend_src/src/lib/locales/zh.js +174 -0
- package/python/frontend_src/src/lib/md.js +0 -14
- package/python/frontend_src/src/main.js +2 -0
- package/python/frontend_src/src/stores/app.js +17 -32
- package/python/frontend_src/src/stores/chat.js +0 -34
- package/python/frontend_src/src/stores/model.js +0 -8
- package/python/frontend_src/src/stores/panels.js +16 -44
- package/python/frontend_src/src/stores/profile.js +31 -0
- package/python/frontend_src/src/stores/system.js +25 -0
- package/python/frontend_src/vite.config.js +0 -7
- package/python/pyproject.toml +17 -3
- package/python/requirements.txt +311 -0
- package/python/tests/conftest.py +0 -22
- package/python/tests/run_all.py +4 -66
- package/python/tests/test_agent_lifecycle.py +4 -38
- package/python/tests/test_agent_rules.py +81 -0
- package/python/tests/test_ahs_p2.py +4 -25
- package/python/tests/test_anthropic_tools.py +1 -15
- package/python/tests/test_api.py +19 -9
- package/python/tests/test_api_auth.py +1 -5
- package/python/tests/test_attachments.py +9 -25
- package/python/tests/test_audit_chain.py +2 -7
- package/python/tests/test_audit_sink.py +0 -6
- package/python/tests/test_behavior_spec.py +198 -0
- package/python/tests/test_browser.py +2 -6
- package/python/tests/test_browser_request_guard.py +0 -4
- package/python/tests/test_capabilities.py +0 -6
- package/python/tests/test_channel_signing.py +2 -9
- package/python/tests/test_channels.py +1 -11
- package/python/tests/test_config_layering.py +0 -4
- package/python/tests/test_confirm_nonce.py +1 -12
- package/python/tests/test_conflict_dismiss.py +143 -0
- package/python/tests/test_content_guard.py +177 -0
- package/python/tests/test_context_compaction_wiring.py +1 -7
- package/python/tests/test_dispatch.py +0 -6
- package/python/tests/test_domain_evals.py +11 -80
- package/python/tests/test_domain_import.py +0 -6
- package/python/tests/test_domain_reasoners.py +0 -14
- package/python/tests/test_e2e.py +0 -17
- package/python/tests/test_emb_cache.py +7 -17
- package/python/tests/test_embedding_singleton.py +1 -3
- package/python/tests/test_evals_smoke.py +0 -24
- package/python/tests/test_event_spine.py +10 -18
- package/python/tests/test_evidence_semantic.py +1 -14
- package/python/tests/test_evidence_sse.py +3 -6
- package/python/tests/test_file_ingest.py +0 -11
- package/python/tests/test_finance.py +0 -9
- package/python/tests/test_finance_ledger.py +5 -12
- package/python/tests/test_finance_persist.py +8 -12
- package/python/tests/test_gateway.py +3 -22
- package/python/tests/test_governance.py +5 -52
- package/python/tests/test_governance_audit.py +4 -39
- package/python/tests/test_governance_middleware.py +1 -7
- package/python/tests/test_hkc.py +6 -24
- package/python/tests/test_hkc_index_build_manager.py +0 -33
- package/python/tests/test_hkc_manifest_reuse.py +4 -27
- package/python/tests/test_hkc_search_event_sync.py +3 -54
- package/python/tests/test_import_path_guard.py +0 -6
- package/python/tests/test_injection_guard.py +18 -36
- package/python/tests/test_integration.py +5 -29
- package/python/tests/test_itinerary.py +0 -3
- package/python/tests/test_kee_semantic.py +0 -5
- package/python/tests/test_kernel.py +1 -11
- package/python/tests/test_learning.py +3 -12
- package/python/tests/test_llm_admin.py +4 -19
- package/python/tests/test_llm_planner.py +1 -23
- package/python/tests/test_markdown_skills.py +3 -14
- package/python/tests/test_markdown_skills_openclaw.py +4 -9
- package/python/tests/test_mcp_connectors.py +3 -15
- package/python/tests/test_mcp_guardian_discovery.py +2 -6
- package/python/tests/test_mcp_no_memory.py +3 -3
- package/python/tests/test_mcp_policy_gate.py +0 -1
- package/python/tests/test_mcp_server.py +6 -9
- package/python/tests/test_memory_recall.py +1 -4
- package/python/tests/test_metrics.py +0 -8
- package/python/tests/test_multi_chain.py +1 -13
- package/python/tests/test_multimodal.py +5 -18
- package/python/tests/test_negotiation.py +3 -13
- package/python/tests/test_orchestration.py +1 -11
- package/python/tests/test_orchestration_decoupled.py +0 -6
- package/python/tests/test_p3_p5_hardening.py +3 -17
- package/python/tests/test_parallel_dispatch.py +4 -12
- package/python/tests/test_pipeline.py +17 -58
- package/python/tests/test_plan_visible.py +4 -4
- package/python/tests/test_planner.py +0 -7
- package/python/tests/test_policy_gate.py +2 -3
- package/python/tests/test_profile.py +67 -0
- package/python/tests/test_readme_claims.py +163 -0
- package/python/tests/test_reasoner.py +2 -17
- package/python/tests/test_release_check.py +2 -13
- package/python/tests/test_resume_checkpoint.py +7 -15
- package/python/tests/test_routing.py +2 -12
- package/python/tests/test_scheduler.py +7 -10
- package/python/tests/test_search.py +1 -11
- package/python/tests/test_secrets.py +4 -10
- package/python/tests/test_semantic.py +1 -10
- package/python/tests/test_sensitive_write.py +181 -0
- package/python/tests/test_sessions.py +3 -4
- package/python/tests/test_settings.py +2 -18
- package/python/tests/test_shadow_learning.py +3 -43
- package/python/tests/test_shared_write.py +0 -7
- package/python/tests/test_skill_taint_gate.py +1 -10
- package/python/tests/test_skills.py +2 -18
- package/python/tests/test_skills_fetch.py +282 -0
- package/python/tests/test_stats_and_events.py +2 -5
- package/python/tests/test_streaming.py +2 -7
- package/python/tests/test_supervisor_pipeline.py +4 -5
- package/python/tests/test_supervisor_triage.py +4 -10
- package/python/tests/test_taint.py +2 -7
- package/python/tests/test_task_stream.py +1 -12
- package/python/tests/test_thinking_sse.py +0 -1
- package/python/tests/test_tool_center.py +1 -17
- package/python/tests/test_tool_parallel.py +2 -2
- package/python/tests/test_tool_params.py +1 -5
- package/python/tests/test_tool_use.py +0 -13
- package/python/tests/test_toplevel_concurrency.py +3 -4
- package/python/tests/test_tracing.py +7 -41
- package/python/tests/test_truth_crystallizer.py +4 -6
- package/python/tests/test_truth_flow.py +2 -7
- package/python/tests/test_update_check.py +95 -0
- package/python/tests/test_verify_evidence.py +8 -21
- package/python/tests/test_version_sync.py +35 -6
- package/python/tests/test_web_trigger.py +0 -17
- package/python/tools/build_content_lexicon.py +218 -0
- package/python/tools/bundle_web.py +98 -0
- package/python/tools/check_faiss_type.py +0 -1
- package/python/tools/check_log.ps1 +0 -8
- package/python/tools/mypy_baseline.txt +12 -4
- package/python/tools/mypy_ratchet.py +52 -6
- package/python/tools/pack.py +0 -5
- package/python/tools/recheck_conflicts.py +156 -0
- package/python/tools/release_check.py +24 -15
- package/python/tools/restart_foxclaw.ps1 +5 -8
- package/python/tools/shared_write_audit.py +1 -15
- package/python/tools/size_check.py +1 -1
- package/python/tools/strip_comments.py +228 -0
- package/python/vendor/hkc/hkc_api/routes/ability.py +31 -0
- package/python/vendor/hkc/hkc_api/schemas.py +11 -0
- package/python/vendor/hkc/hkc_kee/kee.py +124 -3
- package/python/vendor/hkc/hkc_sdk/base.py +22 -0
- package/python/vendor/hkc/hkc_sdk/direct_client.py +10 -0
- package/python/vendor/hkc/hkc_sdk/http_client.py +19 -0
- package/python/foxclaw/api/app.py +0 -123
- package/python/frontend/v3/assets/index-BD7VPbMV.js +0 -33
- package/python/frontend/v3/assets/index-CAvnYWGH.css +0 -1
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
/* 极简 Markdown → HTML。对话气泡与文档阅读共用这一份渲染器。
|
|
2
|
-
*
|
|
3
|
-
* 之前是两套:DocViewer 自带 readHTML/inl(支持标题、列表、行内代码、粗体、链接,
|
|
4
|
-
* 但**不支持 ``` 代码块**),而 MessageStream 直接 {{ m.content }} 原样输出 ——
|
|
5
|
-
* 于是模型的 `## 标题`、`**粗体**`、`- 列表`、反引号代码块在对话里全是原文。
|
|
6
|
-
*
|
|
7
|
-
* 安全边界:先全量转义 & < > ",之后只把识别到的标记换成标签;
|
|
8
|
-
* 所有标签都由本文件产出,链接只放行 http(s)。
|
|
9
|
-
*/
|
|
10
1
|
|
|
11
2
|
const esc = (s) =>
|
|
12
3
|
String(s ?? '')
|
|
@@ -71,8 +62,6 @@ function renderTable(rows) {
|
|
|
71
62
|
return `<table><thead><tr>${th}</tr></thead>${body.length ? `<tbody>${trs}</tbody>` : ''}</table>`
|
|
72
63
|
}
|
|
73
64
|
|
|
74
|
-
/* src 为空 → 空串。流式时最后一段代码块可能还没闭合,
|
|
75
|
-
* 这里把它当"开着"的代码块渲染,而不是把三个反引号当正文甩出来。 */
|
|
76
65
|
export function mdToHTML(src) {
|
|
77
66
|
const text = String(src ?? '').replace(/\r\n?/g, '\n').replace(/\n$/, '')
|
|
78
67
|
if (!text.trim()) return ''
|
|
@@ -114,7 +103,6 @@ export function mdToHTML(src) {
|
|
|
114
103
|
continue
|
|
115
104
|
}
|
|
116
105
|
|
|
117
|
-
/* 围栏代码块 —— 对话里最常出现,优先级最高 */
|
|
118
106
|
const fence = RE_FENCE.exec(line)
|
|
119
107
|
if (fence) {
|
|
120
108
|
flush()
|
|
@@ -137,7 +125,6 @@ export function mdToHTML(src) {
|
|
|
137
125
|
continue
|
|
138
126
|
}
|
|
139
127
|
|
|
140
|
-
/* 表格:当前行是 | ... | 且下一行是分隔行 */
|
|
141
128
|
if (RE_TABLE_ROW.test(line) && i + 1 < lines.length && RE_TABLE_SEP.test(lines[i + 1]) && lines[i + 1].includes('-')) {
|
|
142
129
|
flush()
|
|
143
130
|
const rows = [splitRow(line)]
|
|
@@ -154,7 +141,6 @@ export function mdToHTML(src) {
|
|
|
154
141
|
const h = RE_HEADING.exec(line)
|
|
155
142
|
if (h) {
|
|
156
143
|
flush()
|
|
157
|
-
/* # → h2, #### 及以下 → h5,不再往下掉(对话里 h6 太小) */
|
|
158
144
|
const lv = Math.min(h[1].length + 1, 5)
|
|
159
145
|
out.push(`<h${lv}>${inline(h[2])}</h${lv}>`)
|
|
160
146
|
i += 1
|
|
@@ -1,52 +1,44 @@
|
|
|
1
1
|
import { reactive } from 'vue'
|
|
2
2
|
import { getJSON } from '../api.js'
|
|
3
|
+
import { t } from '../lib/i18n.js'
|
|
3
4
|
|
|
4
|
-
/* 单一状态源:所有栏只读这一份,不再有 _syncNav 那类「三个入口各改一遍」的补丁。 */
|
|
5
5
|
export const state = reactive({
|
|
6
|
-
/* 左栏 */
|
|
7
6
|
sessions: [],
|
|
8
7
|
sessionsState: 'idle', // idle | loading | error
|
|
9
8
|
sessionId: null,
|
|
10
9
|
|
|
11
|
-
/* 中间舞台 */
|
|
12
10
|
module: 'overview',
|
|
13
11
|
doc: null, // { id, title, kind:'text'|'code', lang, path, content, meta }
|
|
14
12
|
|
|
15
|
-
/* 右栏 Inspector:双内容态,同一栏 */
|
|
16
13
|
railOpen: true,
|
|
17
14
|
railMode: 'progress', // 'progress' | 'context'
|
|
18
15
|
|
|
19
|
-
/* 办公进度(OfficeAgent 的态势与进程);学习账本在 panels.review 里 */
|
|
20
16
|
officeStats: { waypoints: 0, conflicts: 0, alerts: 0, active: 0, total: 0 },
|
|
21
17
|
})
|
|
22
18
|
|
|
23
19
|
export const MODULES = [
|
|
24
|
-
{ id: 'overview', label: '概览', tag: 'Overview', group: '' },
|
|
25
|
-
{ id: 'activity', label: '活动', tag: 'Activity', group: '工作台' },
|
|
26
|
-
{ id: 'tasks', label: '定时任务', tag: 'Tasks', group: '工作台', icon: 'clock' },
|
|
27
|
-
{ id: 'agents', label: '代理', tag: 'Agents', group: '工作台', icon: 'bot', beta: true },
|
|
28
|
-
{ id: 'tools', label: '技能·连接器', tag: 'Tools', group: '能力', icon: 'plug' },
|
|
29
|
-
{ id: 'usage', label: '使用情况', tag: 'Usage', group: '能力' },
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
{ id: '
|
|
33
|
-
{ id: '
|
|
34
|
-
{ id: 'memory', label: '记忆中心', tag: 'Memory', group: '知识', icon: 'memory' },
|
|
35
|
-
/* 设置不是"干活的地方",所以不进常驻栏;顶栏 ⚙ 与「更多」都能到,
|
|
36
|
-
顶栏那个 ⚙ 之前是死按钮(title 写着 P3 接入)。 */
|
|
37
|
-
/* 设置落在「更多」里,那里不画图标;之前挂的 icon:'gear' 在 LeftRail 的 ICONS 里
|
|
38
|
-
并不存在(只有 book/clock/plug/bot/more/review/memory/dot),是个死字段。 */
|
|
39
|
-
{ id: 'settings', label: '设置', tag: 'Settings', group: '系统' },
|
|
20
|
+
{ id: 'overview', label: '概览', tag: 'Overview', group: '', groupKey: '' },
|
|
21
|
+
{ id: 'activity', label: '活动', tag: 'Activity', group: '工作台', groupKey: 'group.workspace' },
|
|
22
|
+
{ id: 'tasks', label: '定时任务', tag: 'Tasks', group: '工作台', groupKey: 'group.workspace', icon: 'clock' },
|
|
23
|
+
{ id: 'agents', label: '代理', tag: 'Agents', group: '工作台', groupKey: 'group.workspace', icon: 'bot', beta: true },
|
|
24
|
+
{ id: 'tools', label: '技能·连接器', tag: 'Tools', group: '能力', groupKey: 'group.capability', icon: 'plug' },
|
|
25
|
+
{ id: 'usage', label: '使用情况', tag: 'Usage', group: '能力', groupKey: 'group.capability' },
|
|
26
|
+
{ id: 'knowledge', label: '学习库', tag: 'Knowledge', group: '知识', groupKey: 'group.knowledge', icon: 'book', quick: ['review', 'memory'] },
|
|
27
|
+
{ id: 'review', label: '复习', tag: 'Review', group: '知识', groupKey: 'group.knowledge', icon: 'review' },
|
|
28
|
+
{ id: 'memory', label: '记忆中心', tag: 'Memory', group: '知识', groupKey: 'group.knowledge', icon: 'memory' },
|
|
29
|
+
{ id: 'settings', label: '设置', tag: 'Settings', group: '系统', groupKey: 'group.system', icon: 'gear' },
|
|
40
30
|
]
|
|
41
31
|
|
|
42
|
-
/* 左栏常驻的功能入口(顺序即渲染顺序)。不进这个数组的模块全部落进底部「更多」浮层。
|
|
43
|
-
常驻项恒可见 = 不用先点开菜单才知道有什么功能;代价是列高固定,所以只放四个。 */
|
|
44
32
|
export const RAIL = ['knowledge', 'tasks', 'tools', 'agents']
|
|
45
33
|
|
|
34
|
+
// 设置是侧栏底部的固定入口,不参与主导航与「更多」弹层
|
|
35
|
+
export const PINNED = ['settings']
|
|
36
|
+
|
|
46
37
|
export const railItems = () => RAIL.map((id) => MODULES.find((m) => m.id === id)).filter(Boolean)
|
|
47
|
-
export const moreItems = () => MODULES.filter((m) => !RAIL.includes(m.id))
|
|
38
|
+
export const moreItems = () => MODULES.filter((m) => !RAIL.includes(m.id) && !PINNED.includes(m.id))
|
|
48
39
|
|
|
49
|
-
export const moduleLabel = (id) => MODULES.
|
|
40
|
+
export const moduleLabel = (id) => (MODULES.some((m) => m.id === id) ? t(`nav.${id}`) : id)
|
|
41
|
+
export const groupLabel = (key) => (key ? t(key) : '')
|
|
50
42
|
export const moduleIcon = (id) => MODULES.find((m) => m.id === id)?.icon ?? 'dot'
|
|
51
43
|
|
|
52
44
|
export async function loadSessions() {
|
|
@@ -68,13 +60,7 @@ export function openSession(s) {
|
|
|
68
60
|
}
|
|
69
61
|
|
|
70
62
|
export async function loadProgress() {
|
|
71
|
-
/* 学习账本已改由 panels.review(/api/review/*)提供 —— SM-2 状态写在 KU.extra 里,
|
|
72
|
-
和知识库是同一份数据;/learning/analysis 只统计 learning_note 一类条目,
|
|
73
|
-
两套口径并存会让右栏数字和复习面板对不上。 */
|
|
74
63
|
|
|
75
|
-
/* 办公 = OfficeAgent(行政职责已并入它)的全局态势 + 进程。
|
|
76
|
-
「已摄入资料」已挪到右栏的「最近知识」(读 /api/knowledge/recent),
|
|
77
|
-
书籍级列表留在知识库面板,这里不再重复拉 /knowledge/sources。 */
|
|
78
64
|
const [sit, proc] = await Promise.all([
|
|
79
65
|
getJSON('/orchestration/situation').catch(() => null),
|
|
80
66
|
getJSON('/processes?limit=5').catch(() => null),
|
|
@@ -93,7 +79,6 @@ export function setModule(id) {
|
|
|
93
79
|
state.doc = null
|
|
94
80
|
}
|
|
95
81
|
|
|
96
|
-
/* 打开一份文件 → 正文进中间舞台(宽度够读代码),右栏同步切到检视态并自动展开 */
|
|
97
82
|
export function openDoc(doc) {
|
|
98
83
|
state.doc = doc
|
|
99
84
|
state.railMode = 'context'
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/* 对话与输入区状态:模式 / 附件 / 图片 / 摄入任务 / 消息流 / SSE */
|
|
2
1
|
import { reactive } from 'vue'
|
|
3
2
|
import { API, delJSON, getJSON, getRaw, postForm, postFormRaw, postJSON } from '../api.js'
|
|
4
3
|
import { loadSessions, state as app } from './app.js'
|
|
@@ -14,12 +13,9 @@ export const chat = reactive({
|
|
|
14
13
|
ingest: null, // {name,pct,status,ku,msg}
|
|
15
14
|
toast: '',
|
|
16
15
|
|
|
17
|
-
/* 对话 */
|
|
18
16
|
messages: [], // [{role,content,thinking,evidence,error,ts,streaming,stage,plan}]
|
|
19
17
|
sessionId: null,
|
|
20
18
|
streamState: 'idle', // idle | streaming | done | error
|
|
21
|
-
/* 错误落在具体那条消息的 m.error 上(界面上能看到 + 能点重试),
|
|
22
|
-
这里不再留一个没人读的 lastError */
|
|
23
19
|
})
|
|
24
20
|
|
|
25
21
|
let toastTimer = 0
|
|
@@ -35,7 +31,6 @@ export function setMode(m) {
|
|
|
35
31
|
localStorage.setItem(MODE_KEY, m)
|
|
36
32
|
}
|
|
37
33
|
|
|
38
|
-
/* ── 附件:POST /api/upload → {id,name,size,mime,url,text} ── */
|
|
39
34
|
export async function attachFile(file) {
|
|
40
35
|
if (!file) return
|
|
41
36
|
if (file.size > MAX_MB * 1048576) return say(`${file.name} 超过 ${MAX_MB}MB`)
|
|
@@ -50,7 +45,6 @@ export async function attachFile(file) {
|
|
|
50
45
|
}
|
|
51
46
|
}
|
|
52
47
|
|
|
53
|
-
/* ── 图片:本地转 dataURL,不落盘 ── */
|
|
54
48
|
export function attachImage(file) {
|
|
55
49
|
if (!file) return
|
|
56
50
|
if (!file.type.startsWith('image/')) return say('不是图片文件')
|
|
@@ -67,8 +61,6 @@ export function dropImage(i) {
|
|
|
67
61
|
chat.images.splice(i, 1)
|
|
68
62
|
}
|
|
69
63
|
|
|
70
|
-
/* ── 摄入知识库:POST /knowledge/ingest/file/async → 轮询 /knowledge/ingest/jobs/{id}
|
|
71
|
-
注意路径无 /api 前缀:vendor/hkc 的 knowledge router 是根路径挂载的。 ── */
|
|
72
64
|
export async function ingestFile(file, collection = '') {
|
|
73
65
|
if (!file) return
|
|
74
66
|
if (chat.ingest) return say('等当前文件摄入完成再传下一个')
|
|
@@ -90,7 +82,6 @@ export async function ingestFolder(files) {
|
|
|
90
82
|
const list = [...files].filter(Boolean)
|
|
91
83
|
for (const f of list) {
|
|
92
84
|
await ingestFile(f)
|
|
93
|
-
/* ingestFile 内部已串行守卫:等当前 job 结束再投下一个 */
|
|
94
85
|
while (chat.ingest && chat.ingest.status !== 'error') {
|
|
95
86
|
if (chat.ingest.status === 'done') break
|
|
96
87
|
await new Promise((r) => setTimeout(r, 600))
|
|
@@ -123,8 +114,6 @@ async function pollIngest(jobId) {
|
|
|
123
114
|
say('摄入超时,后台可能仍在跑')
|
|
124
115
|
}
|
|
125
116
|
|
|
126
|
-
/* ══════════ 会话 ══════════ */
|
|
127
|
-
|
|
128
117
|
const STAGE_CN = {
|
|
129
118
|
perceive: '感知', recall: '召回', meta_check: '自检', schedule: '排程',
|
|
130
119
|
route: '路由', plan: '规划', dispatch: '分发', act: '执行',
|
|
@@ -150,7 +139,6 @@ function adoptSession(id) {
|
|
|
150
139
|
localStorage.setItem(SID_KEY, id)
|
|
151
140
|
}
|
|
152
141
|
|
|
153
|
-
/* 启动时恢复上次会话(后端 sessions 落盘,thinking/evidence 不落盘) */
|
|
154
142
|
export async function bootChat() {
|
|
155
143
|
const saved = localStorage.getItem(SID_KEY)
|
|
156
144
|
if (saved) await loadSession(saved, { quiet: true })
|
|
@@ -173,14 +161,11 @@ export async function loadSession(id, { quiet = false } = {}) {
|
|
|
173
161
|
}
|
|
174
162
|
}
|
|
175
163
|
|
|
176
|
-
/* 组件间的一次性信号:斜杠命令要调 ToolMenu 里的文件选择,又不能硬耦合。
|
|
177
|
-
(/设置 不再走这里 —— 它现在直接切到设置面板,见 ChatDock 的命令表) */
|
|
178
164
|
export const ui = reactive({ ingestTick: 0 })
|
|
179
165
|
export function requestIngest() {
|
|
180
166
|
ui.ingestTick += 1
|
|
181
167
|
}
|
|
182
168
|
|
|
183
|
-
/* 纯本地消息(/帮助 之类),不经过后端 */
|
|
184
169
|
export function pushLocal(content) {
|
|
185
170
|
chat.messages.push({
|
|
186
171
|
role: 'assistant', content, ts: Date.now(),
|
|
@@ -188,7 +173,6 @@ export function pushLocal(content) {
|
|
|
188
173
|
})
|
|
189
174
|
}
|
|
190
175
|
|
|
191
|
-
/* 认知搜索(HSR):POST /api/search/query,结果作为一张证据卡挂在消息流里 */
|
|
192
176
|
export async function runSearch(rawQuery) {
|
|
193
177
|
const query = String(rawQuery || '').trim()
|
|
194
178
|
if (!query) return say('用法:/搜索 关键词')
|
|
@@ -205,7 +189,6 @@ export async function runSearch(rawQuery) {
|
|
|
205
189
|
const msg = chat.messages[chat.messages.length - 1]
|
|
206
190
|
|
|
207
191
|
try {
|
|
208
|
-
/* internal_only=true:只查 HMR 记忆 + HKC 知识 + Agent 态势,不联网 —— 联网分支实测返回空图 */
|
|
209
192
|
const d = await postJSON('/search/query', { query, agent_id: 'supervisor', internal_only: true })
|
|
210
193
|
const nodes = (d.graph && d.graph.nodes) || []
|
|
211
194
|
msg.search = {
|
|
@@ -242,7 +225,6 @@ export async function newSession() {
|
|
|
242
225
|
}
|
|
243
226
|
}
|
|
244
227
|
|
|
245
|
-
/* 改标题:后端 rename 会把空串兜回「新会话」,这里先挡掉空提交,避免误清标题 */
|
|
246
228
|
export async function renameSession(id, title) {
|
|
247
229
|
const t = String(title || '').trim().slice(0, 60)
|
|
248
230
|
if (!id || !t) return
|
|
@@ -255,8 +237,6 @@ export async function renameSession(id, title) {
|
|
|
255
237
|
}
|
|
256
238
|
}
|
|
257
239
|
|
|
258
|
-
/* 删会话。删的是当前会话时落回列表里的下一条;一条不剩就新开一个 ——
|
|
259
|
-
否则界面会停在一个已经不存在的 sessionId 上,下一条消息直接 404。 */
|
|
260
240
|
export async function deleteSession(id) {
|
|
261
241
|
if (!id) return
|
|
262
242
|
try {
|
|
@@ -271,7 +251,6 @@ export async function deleteSession(id) {
|
|
|
271
251
|
else await newSession()
|
|
272
252
|
}
|
|
273
253
|
|
|
274
|
-
/* 重发:把这条回复对应的提问找回来,截断到它之前再投一次(附件/图片一起还原) */
|
|
275
254
|
export async function retry(msg) {
|
|
276
255
|
if (chat.streamState === 'streaming') return say('正在生成,先点停止')
|
|
277
256
|
const i = chat.messages.indexOf(msg)
|
|
@@ -301,28 +280,20 @@ async function ensureSession() {
|
|
|
301
280
|
adoptSession(saved)
|
|
302
281
|
return saved
|
|
303
282
|
} catch {
|
|
304
|
-
/* 后端已无此会话 → 落到下面新建 */
|
|
305
283
|
}
|
|
306
284
|
}
|
|
307
285
|
return newSession()
|
|
308
286
|
}
|
|
309
287
|
|
|
310
|
-
/* ══════════ 发送 / 流式 ══════════ */
|
|
311
|
-
|
|
312
288
|
let ctrl = null
|
|
313
|
-
/* 每次 send 领一个代号。切会话 / 新建会话时把代号推进,在跑的那一路回来发现
|
|
314
|
-
代号变了就不再碰全局状态 —— 否则它会把新会话的 streamState 覆盖掉、还把 ctrl
|
|
315
|
-
清空,导致新流停不下来,两路 token 同时往不同消息里写。 */
|
|
316
289
|
let gen = 0
|
|
317
290
|
|
|
318
|
-
/* 掐断在跑的流:切会话、新建会话、重试前都先调一次 */
|
|
319
291
|
function cutStream() {
|
|
320
292
|
gen += 1
|
|
321
293
|
if (ctrl) {
|
|
322
294
|
try {
|
|
323
295
|
ctrl.abort()
|
|
324
296
|
} catch {
|
|
325
|
-
/* 已经收尾了 */
|
|
326
297
|
}
|
|
327
298
|
ctrl = null
|
|
328
299
|
}
|
|
@@ -351,7 +322,6 @@ export async function send(rawText) {
|
|
|
351
322
|
const attachments = chat.attachments.map((a) => ({
|
|
352
323
|
name: a.name, size: a.size, mime: a.mime, url: a.url, text: a.text || '',
|
|
353
324
|
}))
|
|
354
|
-
/* 后端只用最近 12 条拼上下文,与旧前端保持一致 */
|
|
355
325
|
const history = chat.messages
|
|
356
326
|
.filter((m) => m.content && m.role && !m.error)
|
|
357
327
|
.slice(-12)
|
|
@@ -359,15 +329,12 @@ export async function send(rawText) {
|
|
|
359
329
|
|
|
360
330
|
chat.messages.push({
|
|
361
331
|
role: 'user', content: text, ts: Date.now(), attachments, streaming: false, thinking: '',
|
|
362
|
-
/* 存一份图片快照:重试时 send() 从 chat.images 取图,不带上就丢图了 */
|
|
363
332
|
images: chat.images.map((im) => ({ ...im })),
|
|
364
333
|
})
|
|
365
334
|
chat.messages.push({
|
|
366
335
|
role: 'assistant', content: '', thinking: '', evidence: [], error: '', notice: '',
|
|
367
336
|
ts: Date.now(), streaming: true, stage: '', plan: null,
|
|
368
337
|
})
|
|
369
|
-
/* 必须拿回数组里的响应式代理:直接改 push 进去的原始对象不会触发重渲染,
|
|
370
|
-
表现就是气泡永远停在「···」而内容其实早到了 */
|
|
371
338
|
const reply = chat.messages[chat.messages.length - 1]
|
|
372
339
|
chat.attachments = []
|
|
373
340
|
chat.images = []
|
|
@@ -412,7 +379,6 @@ export async function send(rawText) {
|
|
|
412
379
|
}
|
|
413
380
|
|
|
414
381
|
function applyEvent(msg, name, ev) {
|
|
415
|
-
/* 命名事件与 JSON 键双判据:后端明确要求两者都认 */
|
|
416
382
|
if (name === 'thinking' || ev.thinking) {
|
|
417
383
|
msg.thinking += ev.thinking || ''
|
|
418
384
|
return
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/* 模型选择:全部走 /api/llm/*(llm_admin.py),切换后服务端立即生效,无需重启。 */
|
|
2
1
|
import { reactive } from 'vue'
|
|
3
2
|
import { delJSON, getJSON, postJSON, putJSON } from '../api.js'
|
|
4
3
|
|
|
@@ -28,7 +27,6 @@ export async function loadModels(force = false) {
|
|
|
28
27
|
}
|
|
29
28
|
}
|
|
30
29
|
|
|
31
|
-
/* 当前 pill 上显示的名字与色点 */
|
|
32
30
|
export function currentLabel() {
|
|
33
31
|
const c = model.current
|
|
34
32
|
if (!c) return { name: 'Auto', color: '#7e93b8', sub: '跟随 .env 默认' }
|
|
@@ -49,9 +47,6 @@ export async function selectModel({ source, provider = null, name = null }) {
|
|
|
49
47
|
return r
|
|
50
48
|
}
|
|
51
49
|
|
|
52
|
-
/* 设置面板里的模型清单:auto / 预设 / 自定义摊平成同一形状。
|
|
53
|
-
active 的判定必须和上面的 currentLabel 一致 —— 两处各写一套,切换后 pill 和
|
|
54
|
-
列表就会各说各的(这类"同一状态两套判定"是回归温床)。 */
|
|
55
50
|
export function modelOptions() {
|
|
56
51
|
const c = model.current
|
|
57
52
|
const out = [{
|
|
@@ -81,13 +76,10 @@ export async function addCustom(payload) {
|
|
|
81
76
|
await loadModels(true)
|
|
82
77
|
}
|
|
83
78
|
|
|
84
|
-
/* 连通性测试:后端发一个 "hi" 走真实客户端,失败时 **不抛** 而是返回 {ok:false,error}
|
|
85
|
-
—— 那正是要显示给用户看的东西,别当异常吞掉。 */
|
|
86
79
|
export async function testModel(payload) {
|
|
87
80
|
return postJSON('/llm/test', payload)
|
|
88
81
|
}
|
|
89
82
|
|
|
90
|
-
/* 单个自定义模型详情:api_key 永不出网,只回 has_key(编辑表单里留空即保留原值) */
|
|
91
83
|
export async function getCustom(name) {
|
|
92
84
|
return getJSON(`/llm/custom/${encodeURIComponent(name)}`)
|
|
93
85
|
}
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
/* 左栏菜单 8 项的数据层。
|
|
2
|
-
一项一个 loader,内部每个端点单独 .catch —— 某个子系统挂了只让对应区块空着,
|
|
3
|
-
不让整个面板变「后端未连接」。组件只负责渲染,不各自写 fetch。 */
|
|
4
1
|
import { reactive } from 'vue'
|
|
5
2
|
import { getJSON, getRaw, delJSON, patchJSON, postJSON } from '../api.js'
|
|
6
3
|
import { openDoc } from './app.js'
|
|
@@ -15,15 +12,10 @@ export const panels = reactive({
|
|
|
15
12
|
usage: { state: 'idle', tokens: null, gateway: {}, metrics: null, slow: [] },
|
|
16
13
|
knowledge: {
|
|
17
14
|
state: 'idle', stats: {}, index: {}, sources: [], abilities: [],
|
|
18
|
-
/* 最近录入的知识单元:右栏只读前 10 条,面板看全量 */
|
|
19
15
|
recent: [], recentTotal: 0,
|
|
20
|
-
/* 录入是同步 LLM 抽取,可能几十秒 —— 状态提到 store 里,
|
|
21
|
-
切到别的面板再回来时不会以为「没提交过」 */
|
|
22
16
|
ingest: 'idle', ingestMsg: '', ingestErr: '',
|
|
23
17
|
},
|
|
24
|
-
memory: { state: 'idle', stats: {}, recent: [], prefs: [] },
|
|
25
|
-
/* SM-2 复习:stats 是全局账本,queue 是「今天这一轮要过的卡」。
|
|
26
|
-
session 放 store 是因为复习到一半点开文档再回来,不能让进度归零。 */
|
|
18
|
+
memory: { state: 'idle', stats: {}, recent: [], prefs: [], pending: [] },
|
|
27
19
|
review: {
|
|
28
20
|
state: 'idle',
|
|
29
21
|
stats: { cards: 0, learned: 0, fresh: 0, due: 0, avg_mastery: 0, load7: {}, risk: [] },
|
|
@@ -36,7 +28,6 @@ export const panels = reactive({
|
|
|
36
28
|
})
|
|
37
29
|
|
|
38
30
|
const LOADERS = {
|
|
39
|
-
/* 活动 = 事件总线(系统发生了什么) + 请求链路(哪次调用慢) + 思考链健康度 */
|
|
40
31
|
async activity(p) {
|
|
41
32
|
const [bus, traces, thought] = await Promise.all([
|
|
42
33
|
safe(getJSON('/bus/history?limit=40')),
|
|
@@ -49,7 +40,6 @@ const LOADERS = {
|
|
|
49
40
|
return !!(bus || traces || thought)
|
|
50
41
|
},
|
|
51
42
|
|
|
52
|
-
/* 任务 = 调度器记账 + 进程生命周期(后端把每个任务建模成进程) */
|
|
53
43
|
async tasks(p) {
|
|
54
44
|
const [sched, proc] = await Promise.all([
|
|
55
45
|
safe(getJSON('/scheduler/stats')),
|
|
@@ -61,7 +51,6 @@ const LOADERS = {
|
|
|
61
51
|
return !!(sched || proc)
|
|
62
52
|
},
|
|
63
53
|
|
|
64
|
-
/* 代理 = /api/status(全部 Agent 的 harness + 思考链) + 系统信息 */
|
|
65
54
|
async agents(p) {
|
|
66
55
|
const [all, info] = await Promise.all([
|
|
67
56
|
safe(getJSON('/status')),
|
|
@@ -72,7 +61,6 @@ const LOADERS = {
|
|
|
72
61
|
return !!(all || info)
|
|
73
62
|
},
|
|
74
63
|
|
|
75
|
-
/* 技能 = 内置/HTTP/MCP 技能表 + 指令技能(SKILL.md) + 熔断器 */
|
|
76
64
|
async tools(p) {
|
|
77
65
|
const [sk, md, br] = await Promise.all([
|
|
78
66
|
safe(getJSON('/ahs/skills')),
|
|
@@ -85,7 +73,6 @@ const LOADERS = {
|
|
|
85
73
|
return !!(sk || md || br)
|
|
86
74
|
},
|
|
87
75
|
|
|
88
|
-
/* 使用情况 = token 计量 + 网关配额 + 端点延迟(p95 最慢的几条) */
|
|
89
76
|
async usage(p) {
|
|
90
77
|
const [tk, gw, mt] = await Promise.all([
|
|
91
78
|
safe(getJSON('/tokens')),
|
|
@@ -102,8 +89,6 @@ const LOADERS = {
|
|
|
102
89
|
return !!(tk || gw || mt)
|
|
103
90
|
},
|
|
104
91
|
|
|
105
|
-
/* 知识库 = HKC 根路径 /stats 与 /knowledge/sources,加 Foxclaw 侧能力清单
|
|
106
|
-
与最近录入的 KU(右栏和面板共用这一份) */
|
|
107
92
|
async knowledge(p) {
|
|
108
93
|
const [st, src, ab, rc] = await Promise.all([
|
|
109
94
|
safe(getRaw('/stats')),
|
|
@@ -120,8 +105,6 @@ const LOADERS = {
|
|
|
120
105
|
return !!(st || src || ab || rc)
|
|
121
106
|
},
|
|
122
107
|
|
|
123
|
-
/* 复习 = SM-2 账本 + 今日队列。会话进行中只更新账本,不动队列 ——
|
|
124
|
-
否则右栏刷新一次就把当前卡片换掉了。 */
|
|
125
108
|
async review(p) {
|
|
126
109
|
const [st, q] = await Promise.all([
|
|
127
110
|
safe(getJSON('/review/stats')),
|
|
@@ -137,24 +120,24 @@ const LOADERS = {
|
|
|
137
120
|
return !!(st || q)
|
|
138
121
|
},
|
|
139
122
|
|
|
140
|
-
/* 记忆中心 = HMR 统计 + 最近记忆 + 用户偏好 */
|
|
141
123
|
async memory(p) {
|
|
142
|
-
const [st, rc, pf] = await Promise.all([
|
|
124
|
+
const [st, rc, pf, pd] = await Promise.all([
|
|
143
125
|
safe(getJSON('/memory/stats')),
|
|
144
126
|
safe(getJSON('/memory/recent?limit=12')),
|
|
145
127
|
safe(getJSON('/preferences')),
|
|
128
|
+
safe(getJSON('/memory/pending')),
|
|
146
129
|
])
|
|
147
130
|
p.stats = st || {}
|
|
148
131
|
p.recent = rc?.memories || []
|
|
149
132
|
p.prefs = pf?.preferences || []
|
|
150
|
-
|
|
133
|
+
p.pending = pd?.pending || []
|
|
134
|
+
return !!(st || rc || pf || pd)
|
|
151
135
|
},
|
|
152
136
|
}
|
|
153
137
|
|
|
154
138
|
export async function loadPanel(id, force = false) {
|
|
155
139
|
const p = panels[id]
|
|
156
140
|
if (!p || !LOADERS[id]) return
|
|
157
|
-
/* 已有数据就不重拉:切换菜单来回点不重复打后端;要新数据显式刷新 */
|
|
158
141
|
if (!force && p.state !== 'idle' && p.state !== 'error') return
|
|
159
142
|
p.state = 'loading'
|
|
160
143
|
let ok = false
|
|
@@ -164,18 +147,11 @@ export async function loadPanel(id, force = false) {
|
|
|
164
147
|
|
|
165
148
|
export const refreshPanel = (id) => loadPanel(id, true)
|
|
166
149
|
|
|
167
|
-
/* ── 面板内的写操作(都是真实端点,不是本地假删除) ── */
|
|
168
|
-
|
|
169
|
-
/* 指令技能启停:PATCH /ahs/md-skills/{id} → 全量重拉一次保证和服务端一致 */
|
|
170
150
|
export async function toggleMdSkill(id, enabled) {
|
|
171
151
|
await patchJSON(`/ahs/md-skills/${encodeURIComponent(id)}`, { enabled })
|
|
172
152
|
await refreshPanel('tools')
|
|
173
153
|
}
|
|
174
154
|
|
|
175
|
-
/* 录入一段短知识:POST /knowledge/ingest → KDE 抽取成 KU → KEE 演化。
|
|
176
|
-
同步 LLM 抽取,可能几十秒,所以状态挂在 store 上而不是组件里。
|
|
177
|
-
成功后**静默重拉**:走 refreshPanel 会把 state 置回 loading,整面板闪骨架,
|
|
178
|
-
而这里只想要新数据。 */
|
|
179
155
|
export async function ingestKnowledge({ text, title = '', domain = '' }) {
|
|
180
156
|
const p = panels.knowledge
|
|
181
157
|
const body = (text || '').trim()
|
|
@@ -197,8 +173,6 @@ export async function ingestKnowledge({ text, title = '', domain = '' }) {
|
|
|
197
173
|
const n = d?.ku_count ?? 0
|
|
198
174
|
p.ingestMsg = n
|
|
199
175
|
? `已抽取 ${n} 条知识单元${counts ? ` · ${counts}` : ''}`
|
|
200
|
-
/* 走到这里说明 LLM 调用是成功的(全失败会 502 进 catch),只是这段文字里
|
|
201
|
-
没抽出 Concept/Fact/Claim/Entity。别提示"没配模型"误导用户去改 key。 */
|
|
202
176
|
: '模型没从这段文字里抽出知识条目 —— 试试写得更具体(谁 / 是什么 / 为什么)'
|
|
203
177
|
try { await LOADERS.knowledge(p); p.state = 'ok' } catch {}
|
|
204
178
|
return d
|
|
@@ -209,9 +183,6 @@ export async function ingestKnowledge({ text, title = '', domain = '' }) {
|
|
|
209
183
|
}
|
|
210
184
|
}
|
|
211
185
|
|
|
212
|
-
/* ── SM-2 复习会话 ── */
|
|
213
|
-
|
|
214
|
-
/* 开一轮:队列空了就先拉一次。会话状态在 store 里,点开文档再回来不丢进度。 */
|
|
215
186
|
export async function startReview() {
|
|
216
187
|
const p = panels.review
|
|
217
188
|
if (!p.queue.length) await loadPanel('review', true)
|
|
@@ -226,9 +197,6 @@ export function revealCard() {
|
|
|
226
197
|
panels.review.revealed = true
|
|
227
198
|
}
|
|
228
199
|
|
|
229
|
-
/* 评分 → 服务端按 SM-2 算下一次间隔 → 翻到下一张。
|
|
230
|
-
四档对应 SM-2 的 0/3/4/5:0 完全忘记(重置)、3 勉强、4 记住、5 轻松。
|
|
231
|
-
只更新账本不动队列 —— 队列是这一轮的现场。 */
|
|
232
200
|
export async function gradeCard(grade) {
|
|
233
201
|
const p = panels.review
|
|
234
202
|
const card = p.queue[p.index]
|
|
@@ -244,13 +212,22 @@ export async function gradeCard(grade) {
|
|
|
244
212
|
return d
|
|
245
213
|
}
|
|
246
214
|
|
|
247
|
-
/* 删除一条误存的偏好:DELETE /preferences/{id} */
|
|
248
215
|
export async function dropPreference(id) {
|
|
249
216
|
await delJSON(`/preferences/${encodeURIComponent(id)}`)
|
|
250
217
|
await refreshPanel('memory')
|
|
251
218
|
}
|
|
252
219
|
|
|
253
|
-
|
|
220
|
+
// 高敏感内容被闸门拦下后停在这里,用户点头才落库(见 foxclaw/core/sensitive_write.py)
|
|
221
|
+
export async function approveSensitiveWrite(id) {
|
|
222
|
+
await postJSON(`/memory/pending/${encodeURIComponent(id)}/approve`, {})
|
|
223
|
+
await refreshPanel('memory')
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export async function rejectSensitiveWrite(id) {
|
|
227
|
+
await postJSON(`/memory/pending/${encodeURIComponent(id)}/reject`, {})
|
|
228
|
+
await refreshPanel('memory')
|
|
229
|
+
}
|
|
230
|
+
|
|
254
231
|
export async function openMdSkill(id) {
|
|
255
232
|
const d = await getJSON(`/ahs/md-skills/${encodeURIComponent(id)}`)
|
|
256
233
|
openDoc({
|
|
@@ -264,8 +241,6 @@ export async function openMdSkill(id) {
|
|
|
264
241
|
})
|
|
265
242
|
}
|
|
266
243
|
|
|
267
|
-
/* extra 里的业务字段逐类型不同(Fact 有 statement、Evidence 有 source、Entity 有 aliases),
|
|
268
|
-
按类型硬编码会漏;这里统一按「有值就列出来」处理,任何 KU 打开都有内容可读。 */
|
|
269
244
|
const KU_FIELDS = [
|
|
270
245
|
['statement', '陈述'],
|
|
271
246
|
['definition', '定义'],
|
|
@@ -289,8 +264,6 @@ const kuVal = (v) => {
|
|
|
289
264
|
return v === 0 || v === '' || v == null ? '' : String(v)
|
|
290
265
|
}
|
|
291
266
|
|
|
292
|
-
/* 单个 KU 的正文 → 中间舞台。走 HKC 根路径 /knowledge/ku/{id}
|
|
293
|
-
(那条路径带 source_text,列表/图谱的响应为了瘦身把它剔掉了) */
|
|
294
267
|
export async function openKu(id) {
|
|
295
268
|
const d = await getRaw(`/knowledge/ku/${encodeURIComponent(id)}`)
|
|
296
269
|
const extra = d.extra || {}
|
|
@@ -298,7 +271,6 @@ export async function openKu(id) {
|
|
|
298
271
|
const rows = KU_FIELDS
|
|
299
272
|
.map(([k, label]) => [label, kuVal(extra[k])])
|
|
300
273
|
.filter(([, v]) => v)
|
|
301
|
-
/* statement 已经在标题下一行重复过一次,正文里不再复读 */
|
|
302
274
|
const body = rows.filter(([, v]) => v !== d.summary && v !== d.name)
|
|
303
275
|
|
|
304
276
|
const parts = []
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { reactive } from 'vue'
|
|
2
|
+
import { getJSON, putJSON } from '../api.js'
|
|
3
|
+
|
|
4
|
+
export const profile = reactive({
|
|
5
|
+
nickname: '',
|
|
6
|
+
loaded: false,
|
|
7
|
+
saving: false,
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
export async function loadProfile(force = false) {
|
|
11
|
+
if (profile.loaded && !force) return profile
|
|
12
|
+
try {
|
|
13
|
+
const d = await getJSON('/profile')
|
|
14
|
+
profile.nickname = d.nickname || ''
|
|
15
|
+
profile.loaded = true
|
|
16
|
+
} catch {
|
|
17
|
+
}
|
|
18
|
+
return profile
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function saveNickname(nick) {
|
|
22
|
+
profile.saving = true
|
|
23
|
+
try {
|
|
24
|
+
const d = await putJSON('/profile', { nickname: nick || '' })
|
|
25
|
+
profile.nickname = d.nickname || ''
|
|
26
|
+
profile.loaded = true
|
|
27
|
+
return profile.nickname
|
|
28
|
+
} finally {
|
|
29
|
+
profile.saving = false
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { reactive } from 'vue'
|
|
2
|
+
import { getJSON } from '../api.js'
|
|
3
|
+
|
|
4
|
+
export const system = reactive({
|
|
5
|
+
version: '',
|
|
6
|
+
update: null, // { enabled, current, latest, update_available, checked_at, error }
|
|
7
|
+
loaded: false,
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
let inflight = null
|
|
11
|
+
|
|
12
|
+
export function loadSystemInfo(force = false) {
|
|
13
|
+
if (system.loaded && !force) return Promise.resolve(system)
|
|
14
|
+
if (inflight) return inflight
|
|
15
|
+
inflight = getJSON('/system/info')
|
|
16
|
+
.then((d) => {
|
|
17
|
+
system.version = d.version || ''
|
|
18
|
+
system.update = d.update || null
|
|
19
|
+
system.loaded = true
|
|
20
|
+
return system
|
|
21
|
+
})
|
|
22
|
+
.catch(() => system) // 拿不到就维持旧值:顶栏不显示版本,不显示更新
|
|
23
|
+
.finally(() => { inflight = null })
|
|
24
|
+
return inflight
|
|
25
|
+
}
|
|
@@ -3,8 +3,6 @@ import { defineConfig } from 'vite'
|
|
|
3
3
|
import vue from '@vitejs/plugin-vue'
|
|
4
4
|
import { fileURLToPath, URL } from 'node:url'
|
|
5
5
|
|
|
6
|
-
/* 设置面板底部的版本行照 OpenClaw 写成 git@<sha> · <N 天前>。
|
|
7
|
-
构建时抓一次 git 信息;没有 git(打包机 / 压缩包)就退回 dev,不让构建失败。 */
|
|
8
6
|
function gitInfo() {
|
|
9
7
|
try {
|
|
10
8
|
const sha = execSync('git rev-parse --short HEAD', { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim()
|
|
@@ -15,9 +13,6 @@ function gitInfo() {
|
|
|
15
13
|
}
|
|
16
14
|
}
|
|
17
15
|
|
|
18
|
-
/* 产物直接写进 frontend/v3/,后端 (StaticFiles(directory=frontend, html=True)) 零改动:
|
|
19
|
-
/v3/ -> frontend/v3/index.html
|
|
20
|
-
base 必须是 '/v3/',否则 assets 会打到站点根。 */
|
|
21
16
|
export default defineConfig({
|
|
22
17
|
base: '/v3/',
|
|
23
18
|
plugins: [vue()],
|
|
@@ -34,12 +29,10 @@ export default defineConfig({
|
|
|
34
29
|
},
|
|
35
30
|
server: {
|
|
36
31
|
port: 5173,
|
|
37
|
-
/* dev 时把 API 与 WS 透到本地后端,前端代码里一律用相对路径 /api */
|
|
38
32
|
proxy: {
|
|
39
33
|
'/api': { target: 'http://127.0.0.1:8000', changeOrigin: true },
|
|
40
34
|
'/ws': { target: 'ws://127.0.0.1:8000', ws: true },
|
|
41
35
|
'/planet': { target: 'http://127.0.0.1:8000', changeOrigin: true },
|
|
42
|
-
/* /knowledge/* 无 /api 前缀:HKC 自带 router 由后端根路径挂载 */
|
|
43
36
|
'/knowledge': { target: 'http://127.0.0.1:8000', changeOrigin: true },
|
|
44
37
|
},
|
|
45
38
|
},
|