foxclaw 0.1.2 → 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.
- 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/CHANGELOG.md +57 -0
- package/python/CHANGELOG_CN.md +57 -0
- package/python/README.md +11 -2
- package/python/README_CN.md +32 -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/RELEASE.md +4 -3
- 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 +34 -152
- package/python/foxclaw/agents/base.py +65 -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 +7 -1
- package/python/foxclaw/agents/mixins/preference.py +58 -8
- package/python/foxclaw/agents/mixins/prompt.py +0 -6
- package/python/foxclaw/agents/mixins/skillbox.py +6 -6
- package/python/foxclaw/agents/mixins/tool_exec.py +16 -42
- package/python/foxclaw/api/__init__.py +18 -77
- package/python/foxclaw/api/_state.py +43 -0
- package/python/foxclaw/api/{app.py → bootstrap.py} +5 -32
- 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 +28 -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/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/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 +30 -57
- package/python/foxclaw/core/metrics.py +4 -5
- package/python/foxclaw/core/model_router.py +36 -148
- 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/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 +0 -201
- package/python/frontend/v3/assets/index-CQSBdxk4.js +34 -0
- package/python/frontend/v3/assets/index-D5nIsjms.css +1 -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 +0 -1
- 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 +0 -41
- 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 +6 -3
- package/python/requirements.txt +311 -0
- package/python/tests/conftest.py +0 -22
- package/python/tests/run_all.py +3 -66
- package/python/tests/test_agent_lifecycle.py +4 -38
- 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_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_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_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/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/release_check.py +7 -7
- 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/frontend/v3/assets/index-BD7VPbMV.js +0 -33
- package/python/frontend/v3/assets/index-CAvnYWGH.css +0 -1
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
// 缓存刷新:改动子模块时,连同顶栏版本号一起把 ?v= 递增(零构建下的显式 cache-bust)
|
|
2
1
|
import './auth.js?v=5.37';
|
|
3
2
|
import { escapeHtml } from './util.js?v=2.9';
|
|
4
3
|
import { renderMarkdown, renderReply } from './markdown.js?v=2.9';
|
|
@@ -8,22 +7,14 @@ import { fetchAMapConfig, kpSet, renderFinance, renderLearning, renderItinerary,
|
|
|
8
7
|
import { openSetMenu, switchProvider, uiPref, openAuthSetting } from './settings.js?v=5.36'; // uiPref.ballAnim 默认 false
|
|
9
8
|
import './chat_model_picker.js?v=1.4'; // chat 输入框旁的下拉式模型选择器;v1.4 保存即切换 + BASE URL 自动补 /v1 + 模型 ID 软提示
|
|
10
9
|
|
|
11
|
-
/* ════════════ 后端数据层:接真实 Foxclaw ════════════ */
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
10
|
let activeAgent='supervisor';
|
|
16
|
-
// 三个 Agent(supervisor/finance/office) + 一个**视图** admin(行程地图,已不是 Agent)
|
|
17
11
|
const AGENT_CN={supervisor:'主管',finance:'财务',office:'办公学习',admin:'行程'};
|
|
18
12
|
|
|
19
|
-
/* 在线状态 */
|
|
20
13
|
function setUplink(on){
|
|
21
14
|
const el=document.getElementById('upStat');
|
|
22
15
|
if(el){el.textContent=on?'100%':'离线'; el.style.color=on?'var(--green)':'var(--amber)';}
|
|
23
|
-
// 副标题固定为品牌语,不再被状态文字覆盖(状态已在右上角显示)
|
|
24
16
|
}
|
|
25
17
|
|
|
26
|
-
/* 鉴权提示条:后端 401 → 顶部弹一条,引导去「访问密钥」配置(只弹一次) */
|
|
27
18
|
function showAuthHint(){
|
|
28
19
|
if(document.getElementById('authHint')) return;
|
|
29
20
|
const bar=document.createElement('div');
|
|
@@ -34,10 +25,8 @@ function showAuthHint(){
|
|
|
34
25
|
document.getElementById('ah-go').onclick=()=>{ bar.remove(); openAuthSetting(); };
|
|
35
26
|
document.getElementById('ah-x').onclick=()=>bar.remove();
|
|
36
27
|
}
|
|
37
|
-
// 鉴权注入模块(auth.js)检测到 401 时回调此函数弹提示条
|
|
38
28
|
window.__foxclawAuthHint=showAuthHint;
|
|
39
29
|
|
|
40
|
-
/* 1. 加载五 Agent 状态 */
|
|
41
30
|
async function loadStatus(){
|
|
42
31
|
try{
|
|
43
32
|
const r=await fetch(`${API}/status`); if(!r.ok)throw 0;
|
|
@@ -50,10 +39,6 @@ async function loadStatus(){
|
|
|
50
39
|
}catch(e){ setUplink(false); }
|
|
51
40
|
}
|
|
52
41
|
|
|
53
|
-
/* 2. 记忆中心真实统计 */
|
|
54
|
-
/* 右栏「记忆」面板:四联统计 + 最近记忆信息条。
|
|
55
|
-
沿用 loadMemStats 这个名字 —— 全文已有 8 处调用点(状态刷新/录入后/定时),
|
|
56
|
-
改这一个函数即可让新面板处处自动刷新,无需再散布新调用。 */
|
|
57
42
|
async function loadMemStats(){
|
|
58
43
|
const set=(id,v)=>{const el=document.getElementById(id); if(el)el.textContent=v;};
|
|
59
44
|
try{
|
|
@@ -66,10 +51,8 @@ async function loadMemStats(){
|
|
|
66
51
|
set('memVec',(d.total_memories||0).toLocaleString()); // 兼容旧元素(若仍存在)
|
|
67
52
|
set('memRel',(d.relations||0).toLocaleString());
|
|
68
53
|
}catch(e){}
|
|
69
|
-
// 会话列表现由 loadSessions() 渲染进同一右栏(记忆面板下),这里不再管"最近记忆列表"。
|
|
70
54
|
}
|
|
71
55
|
|
|
72
|
-
/* 右栏「知识库」面板:知识信息条 */
|
|
73
56
|
async function loadKnowList(){
|
|
74
57
|
const list=document.getElementById('knowList'); if(!list)return;
|
|
75
58
|
try{
|
|
@@ -89,7 +72,6 @@ async function loadKnowList(){
|
|
|
89
72
|
}catch(e){ list.innerHTML='<div class="kp-empty">后端未连接</div>'; }
|
|
90
73
|
}
|
|
91
74
|
|
|
92
|
-
/* 3. Agent 卡片点击切换 */
|
|
93
75
|
document.querySelectorAll('.acard').forEach(card=>{
|
|
94
76
|
card.addEventListener('click',()=>{
|
|
95
77
|
document.querySelectorAll('.acard').forEach(c=>c.classList.remove('on'));
|
|
@@ -102,8 +84,6 @@ document.querySelectorAll('.acard').forEach(card=>{
|
|
|
102
84
|
refreshAgentStatus();
|
|
103
85
|
});
|
|
104
86
|
});
|
|
105
|
-
/* 顶栏右侧「财务/地图」面板入口:只开中央仪表盘,不改 activeAgent —— 对话统一走
|
|
106
|
-
输入框(主管自动分诊)。再点一次收起面板。 */
|
|
107
87
|
document.querySelectorAll('.abtn').forEach(btn=>{
|
|
108
88
|
btn.addEventListener('click',()=>{
|
|
109
89
|
const ag=btn.dataset.agent, wasOn=btn.classList.contains('on');
|
|
@@ -113,7 +93,6 @@ document.querySelectorAll('.abtn').forEach(btn=>{
|
|
|
113
93
|
loadKernel(ag); showInCore(ag);
|
|
114
94
|
});
|
|
115
95
|
});
|
|
116
|
-
/* 统一刷新各卡状态文字:工作中 / 在线(选中) / 离线(灰) */
|
|
117
96
|
function refreshAgentStatus(){
|
|
118
97
|
document.querySelectorAll('.acard').forEach(c=>{
|
|
119
98
|
const st=c.querySelector('.st-tx'); if(!st)return;
|
|
@@ -124,13 +103,6 @@ function refreshAgentStatus(){
|
|
|
124
103
|
}
|
|
125
104
|
refreshAgentStatus();
|
|
126
105
|
|
|
127
|
-
|
|
128
|
-
/* ===== 中央焦点空间:激活 Agent 显示其当前内容,空闲回到全息球 ===== */
|
|
129
|
-
/* ═══ 中间栏输出中枢(Center)═══════════════════════════════════════════════
|
|
130
|
-
中间栏有两个叠放的输出层 —— 对话浮层 coreChat(z5)/ 面板层 coreStage(z3),外加背后的 3D 核心。
|
|
131
|
-
过去每个入口自己开关这两层,常忘关另一层 → 页面盖着不退、白白渲染(用户实测踩到)。
|
|
132
|
-
现在**统一由 Center 管**:凡要在中间栏显示东西,一律走 Center.panel()/chat()/home(),
|
|
133
|
-
同一时刻只有一个视图活着,天然互斥,不必再各处补"记得关另一层"。 */
|
|
134
106
|
const Center = {
|
|
135
107
|
mode:'home',
|
|
136
108
|
panel(){ // 打开面板层(知识/模块/领域视图);返回 #coreStage 供调用方写 innerHTML
|
|
@@ -157,10 +129,8 @@ const Center = {
|
|
|
157
129
|
function clearCore(){ Center.home(); _syncNav('overview'); } // 兼容旧调用名(9 处)→ 统一走中枢
|
|
158
130
|
async function showInCore(agent){
|
|
159
131
|
const wrap=document.querySelector('.corewrap'), stage=document.getElementById('coreStage');
|
|
160
|
-
// 打开中间栏面板前先关掉对话浮层(core-chat z5 会盖住 core-stage z3,否则点侧栏"像没反应")
|
|
161
132
|
document.getElementById('coreChat')?.classList.remove('show');
|
|
162
133
|
const m=KP_META[agent]||KP_META.office;
|
|
163
|
-
// 主管空闲态:回到全息球(它没有"专属任务画面")
|
|
164
134
|
if(agent==='supervisor'){ clearCore(); return; }
|
|
165
135
|
Center.panel();
|
|
166
136
|
stage.innerHTML=`<div class="cs-head"><div class="t"><span class="dot"></span>${m.title} <em>${m.tag}</em></div>
|
|
@@ -178,7 +148,6 @@ async function showInCore(agent){
|
|
|
178
148
|
setTimeout(()=>{drawAMapInto('coreMap',d.map_data||[]); initMapSearch();},60);
|
|
179
149
|
}else if(agent==='office'){
|
|
180
150
|
const items=(d.items||[]);
|
|
181
|
-
// 摄入入口必须常在(此前只挂在右栏内核面板,右栏改版后就消失了 —— 回归过一次,别再删)
|
|
182
151
|
const ingest='<div class="ingest-row" style="max-width:520px;margin:0 auto 14px">'
|
|
183
152
|
+'<button class="ing-btn" onclick="document.getElementById(\'cmdAttach\')?.click()">📄 导入文件</button>'
|
|
184
153
|
+'<button class="ing-btn" onclick="document.getElementById(\'cmdFolder\')?.click()">📁 导入文件夹</button>'
|
|
@@ -195,7 +164,6 @@ async function showInCore(agent){
|
|
|
195
164
|
}catch(e){ body.innerHTML='<div class="kp-empty">后端未连接</div>'; }
|
|
196
165
|
}
|
|
197
166
|
|
|
198
|
-
/* 3b. 内核详情面板:随 Agent 切换展示对应内核数据 */
|
|
199
167
|
const KP_META={
|
|
200
168
|
supervisor:{title:'全局态势',tag:'Situation'},
|
|
201
169
|
finance:{title:'财务驾驶舱',tag:'Finance'},
|
|
@@ -206,8 +174,6 @@ const KP_META={
|
|
|
206
174
|
async function loadKernel(agent){
|
|
207
175
|
const m=KP_META[agent]||KP_META.office;
|
|
208
176
|
try{
|
|
209
|
-
// 每个分支独立容错:数据请求失败(如首次 HKC 装配慢/500)→ 用空数据 {} 渲染面板,
|
|
210
|
-
// **保留录入/导入入口**(它们不依赖数据),而不是抛到外层 catch 把整个面板变成"后端未连接"。
|
|
211
177
|
if(agent==='finance'){
|
|
212
178
|
const d=await fetch(`${API}/finance/analysis?period=2026-06¤t_balance=11000`).then(r=>r.json()).catch(()=>({}));
|
|
213
179
|
_lastKernelData.finance=d; kpSet(m.title,m.tag,renderFinance(d||{}));
|
|
@@ -227,7 +193,6 @@ async function loadKernel(agent){
|
|
|
227
193
|
}catch(e){ kpSet(m.title,m.tag,'<div class="kp-empty">后端未连接 · 该面板需真实数据</div>'); }
|
|
228
194
|
}
|
|
229
195
|
|
|
230
|
-
/* 领域文件导入:上传 csv/xlsx/json → /api/{domain}/import/file(超越 csv_path)*/
|
|
231
196
|
function importDomainFile(agent){
|
|
232
197
|
const map={finance:{d:'finance',cn:'交易'}, office:{d:'learning',cn:'知识点'}, admin:{d:'itinerary',cn:'行程点'}};
|
|
233
198
|
const m=map[agent]||map.finance;
|
|
@@ -249,8 +214,6 @@ function importDomainFile(agent){
|
|
|
249
214
|
inp.click();
|
|
250
215
|
}
|
|
251
216
|
|
|
252
|
-
/* 4. 发任务到后端 */
|
|
253
|
-
/* Agent 忙碌态:有任务时卡片亮主题色 + 显示任务标题 */
|
|
254
217
|
const agentTasks={};
|
|
255
218
|
function setAgentBusy(agent, task){
|
|
256
219
|
agentTasks[agent]=task;
|
|
@@ -275,20 +238,15 @@ async function transmit(){
|
|
|
275
238
|
const inp=document.getElementById('cmd'); let task=(inp.value||'').trim();
|
|
276
239
|
const hasImgs=_pendingImages.length>0, hasFiles=_pendingFiles.length>0;
|
|
277
240
|
if(!task && !hasImgs && !hasFiles)return;
|
|
278
|
-
// 斜杠指令:以 / 开头交给命令处理器,不发给后端(如 /搜索 记忆架构、/工具、/清库 确认)
|
|
279
241
|
if(task[0]==='/'){ inp.value=''; hideSlashMenu(); runSlash(task); return; }
|
|
280
242
|
inp.value='';
|
|
281
243
|
if((hasImgs||hasFiles) && !task) task=hasImgs?'请描述并分析这张图片':'请阅读并总结这个文件';
|
|
282
244
|
prependTask(task,activeAgent,'pending');
|
|
283
|
-
// 标记当前 Agent 忙碌 + 显示任务标题
|
|
284
245
|
setAgentBusy(activeAgent, task);
|
|
285
|
-
// 数据流:核心连发数束光点射向当前 Agent(看得见它在工作)
|
|
286
246
|
if(window._emitStream){ for(let i=0;i<3;i++) setTimeout(()=>window._emitStream(activeAgent), i*180); }
|
|
287
|
-
// 弹出回复浮窗(加载态);带图则把用户图片缩略图渲染进提问区
|
|
288
247
|
const qThumbs=hasImgs?_pendingImages.map(x=>x.url):null;
|
|
289
248
|
const qFiles=hasFiles?_pendingFiles.map(x=>({name:x.name,size:x.size,url:x.url})):null;
|
|
290
249
|
showReplyPop(task, activeAgent, false, qThumbs, qFiles);
|
|
291
|
-
// 多模态/附件:带图或带文件必走流式(/chat/stream 消费 images/attachments);发送后清空附件条
|
|
292
250
|
if(hasImgs || hasFiles){
|
|
293
251
|
const imgs=hasImgs?_pendingImages.map(x=>({media_type:x.media_type, data:x.data})):null;
|
|
294
252
|
const atts=hasFiles?_pendingFiles.map(x=>({name:x.name,size:x.size,mime:x.mime,url:x.url,text:x.text})):null;
|
|
@@ -297,10 +255,7 @@ async function transmit(){
|
|
|
297
255
|
return transmitStream(task, activeAgent, imgs, atts);
|
|
298
256
|
}
|
|
299
257
|
if(chatModeStream){ _refTriggers={}; return transmitStream(task, activeAgent); }
|
|
300
|
-
// 任务模式:@引用把技能触发词带入后端 task(用户可见输入保留 @名称)
|
|
301
258
|
const outTask=applyRefs(task);
|
|
302
|
-
// 领域 Agent 任务 → 流式认知管线(逐 stage 进度 + act 逐字);主管编排任务不支持流式
|
|
303
|
-
// (/api/task/stream 只跑单 Agent 管线、不做多 Agent 分派),仍走阻塞 /api/task 保住编排能力。
|
|
304
259
|
if(activeAgent!=='supervisor'){ _refTriggers={}; return transmitTaskStream(outTask, activeAgent); }
|
|
305
260
|
try{
|
|
306
261
|
const r=await fetch(`${API}/task`,{method:'POST',headers:{'Content-Type':'application/json'},
|
|
@@ -312,7 +267,6 @@ async function transmit(){
|
|
|
312
267
|
}else{
|
|
313
268
|
markTask(task,'ok');
|
|
314
269
|
const res=data.result||{};
|
|
315
|
-
// 多 Agent 顺序执行:展示每一步的子任务 + 处理 Agent + 回复
|
|
316
270
|
if(res.dispatched_to==='multi' && Array.isArray(res.multi_steps)){
|
|
317
271
|
let html=`<div style="margin-bottom:10px;color:var(--cyan);font-weight:600">${escapeHtml(res.summary||'多步任务')}</div>`;
|
|
318
272
|
res.multi_steps.forEach(s=>{
|
|
@@ -332,7 +286,6 @@ async function transmit(){
|
|
|
332
286
|
let via='';
|
|
333
287
|
if(res.dispatched_to&&res.dispatched_to!=='supervisor'&&res.dispatched_to!==activeAgent){
|
|
334
288
|
via=`⤷ 主管已分派给 ${AGENT_CN[res.dispatched_to]||res.dispatched_to} Agent 处理`;
|
|
335
|
-
// 实际处理的 Agent 也标忙碌
|
|
336
289
|
setAgentBusy(res.dispatched_to, task);
|
|
337
290
|
}else if(res.self_answered){
|
|
338
291
|
via='⤷ 主管直接答复';
|
|
@@ -345,13 +298,9 @@ async function transmit(){
|
|
|
345
298
|
fillReplyPop('', '请求失败:后端未连接,或模型未配置 Key', 'err');
|
|
346
299
|
}
|
|
347
300
|
}
|
|
348
|
-
/* 回复显示在中央对话层(最上层)*/
|
|
349
301
|
function showReplyPop(task, agent, force, qImages, qFiles){
|
|
350
|
-
// 偏好关闭则不弹被动回复;但 force=true(如用户主动发起的文件摄入)必须可见,
|
|
351
|
-
// 否则点了 📎 却因偏好静默 → 屏幕零反应,以为坏了。
|
|
352
302
|
if(!force && typeof uiPref!=='undefined' && !uiPref.autoChat) return;
|
|
353
303
|
document.getElementById('ccAgent').textContent=`${AGENT_CN[agent]||agent} Agent`;
|
|
354
|
-
// 重置为单轮结构(对话线程 renderChatThread 可能已替换过 cc-body,这里恢复 ccQ/ccReply/ccStatus)
|
|
355
304
|
const _b=document.querySelector('#coreChat .cc-body');
|
|
356
305
|
if(_b && !document.getElementById('ccQ'))
|
|
357
306
|
_b.innerHTML='<div class="cc-q" id="ccQ"></div><div id="ccReply"></div><div class="cc-status" id="ccStatus"></div>';
|
|
@@ -380,10 +329,6 @@ document.getElementById('ccClose')?.addEventListener('click',()=>document.getEle
|
|
|
380
329
|
document.querySelector('.cmdbar .send')?.addEventListener('click',transmit);
|
|
381
330
|
document.getElementById('cmd')?.addEventListener('keydown',e=>{if(e.key==='Enter')transmit();});
|
|
382
331
|
|
|
383
|
-
|
|
384
|
-
/* ── 附件摄入:chat 栏 📎 → 文件进 HKC 知识库(异步 + 进度轮询)──────────────
|
|
385
|
-
交互层与知识层打通:对话框既能聊天,也能直接喂文件蒸馏成长期知识。
|
|
386
|
-
走 /knowledge/ingest/file/async(秒返 job_id,后台线程池抽取),前端轮询进度。*/
|
|
387
332
|
const _INGEST_MAX_BYTES=20*1024*1024;
|
|
388
333
|
let _ingestBusy=false; // 串行守卫:一次只摄入一个文件,避免并发轮询串写同一进度弹层
|
|
389
334
|
(function(){
|
|
@@ -400,21 +345,16 @@ let _ingestBusy=false; // 串行守卫:一次只摄入一个文件,避免并
|
|
|
400
345
|
try{ await ingestChatFile(f); } finally{ _ingestBusy=false; }
|
|
401
346
|
});
|
|
402
347
|
})();
|
|
403
|
-
/* 若知识星球 iframe 已打开,刷新它拉最新图(同源 /planet/);未打开无需动作(下次懒加载自然是新数据)*/
|
|
404
348
|
function _refreshPlanet(){
|
|
405
349
|
try{ const fr=document.getElementById('planetFrame'); const w=fr&&fr.contentWindow;
|
|
406
350
|
if(w && fr.src && fr.src!=='about:blank' && typeof w.loadData==='function') w.loadData(); }catch(e){}
|
|
407
351
|
}
|
|
408
|
-
/* 摄入单个文件:上传 + 轮询,返回 {ok,ku_count,error,skipped};不弹窗(由调用方展示),不抛异常。
|
|
409
|
-
onStatus(msg) 可选进度回调。供单文件(📎)与文件夹(📁)逐个摄入共用。 */
|
|
410
352
|
async function _ingestFileOnce(file, onStatus, collection){
|
|
411
353
|
if(file.size>_INGEST_MAX_BYTES) return {ok:false, skipped:true, error:`过大(${(file.size/1048576).toFixed(1)}MB>20MB)`};
|
|
412
354
|
let job;
|
|
413
355
|
try{
|
|
414
356
|
const fd=new FormData(); fd.append('file', file);
|
|
415
|
-
// 文件名(去扩展名)当来源标题 → 文件 Evidence 以「文件名/章节名」命名(二级)
|
|
416
357
|
fd.append('source_title', (file.name||'').replace(/\.[^.]+$/,''));
|
|
417
|
-
// 文件夹名 → 合集(一级);后端据此建/挂合集节点、并让 KEE 按合集判同源
|
|
418
358
|
if(collection) fd.append('collection', collection);
|
|
419
359
|
const r=await fetch(`${_origin}/knowledge/ingest/file/async`,{method:'POST',body:fd});
|
|
420
360
|
if(!r.ok) return {ok:false, error: r.status===415?'不支持的格式': r.status===503?'知识核心未启用':`上传失败(${r.status})`};
|
|
@@ -448,7 +388,6 @@ async function ingestChatFile(file){
|
|
|
448
388
|
}
|
|
449
389
|
}
|
|
450
390
|
|
|
451
|
-
/* ── 文件夹摄入:📁 一次选一个文件夹 → 过滤支持格式 → 顺序逐个摄入(不做多文件夹/并发批量)── */
|
|
452
391
|
const _INGEST_EXTS=['.pdf','.md','.markdown','.txt','.epub','.mobi','.azw3','.azw','.docx','.xlsx'];
|
|
453
392
|
const _FOLDER_MAX_FILES=100; // 单文件夹一次最多摄入的文件数(防选到超大目录顺序摄入几小时+烧 LLM)
|
|
454
393
|
function _supportedFile(name){ const n=(name||'').toLowerCase(); return _INGEST_EXTS.some(e=>n.endsWith(e)); }
|
|
@@ -472,8 +411,6 @@ async function ingestFolder(fileList){
|
|
|
472
411
|
files=files.slice(0, _FOLDER_MAX_FILES);
|
|
473
412
|
}
|
|
474
413
|
const N=files.length; let doneCount=0, failCount=0, totalKu=0, done=0; const log=[];
|
|
475
|
-
// 并发摄入:后端线程池 max_workers=2,前端开 3 个 worker 正好把池占满(匹配星球吞吐)。
|
|
476
|
-
// 之前严格串行(一次 1 个文件)比星球慢一倍。合集 find-or-create 已加锁,并发安全。
|
|
477
414
|
const CONC=Math.min(3, N); let idx=0;
|
|
478
415
|
async function _worker(){
|
|
479
416
|
while(idx<N){
|
|
@@ -495,8 +432,6 @@ async function ingestFolder(fileList){
|
|
|
495
432
|
if(!btn||!inp) return;
|
|
496
433
|
btn.addEventListener('click',()=>inp.click());
|
|
497
434
|
inp.addEventListener('change',async ()=>{
|
|
498
|
-
// 先把 FileList 拷成数组再清空输入 —— inp.value='' 会清空 inp.files,
|
|
499
|
-
// 而 fs 若是它的活引用就会一起变空(fs.length→0)导致下面直接 return、什么都不做。
|
|
500
435
|
const fs=[...inp.files]; inp.value=''; if(!fs.length) return;
|
|
501
436
|
if(_ingestBusy){ showReplyPop('📁 文件夹', activeAgent, true); fillReplyPop('', '请等当前摄入完成再选文件夹', 'err'); return; }
|
|
502
437
|
_ingestBusy=true;
|
|
@@ -504,7 +439,6 @@ async function ingestFolder(fileList){
|
|
|
504
439
|
});
|
|
505
440
|
})();
|
|
506
441
|
|
|
507
|
-
/* 流式/任务 模式开关:默认流式(逐字直答);任务模式走编排(/api/task,不流式) */
|
|
508
442
|
let chatModeStream=true;
|
|
509
443
|
(function(){
|
|
510
444
|
const el=document.getElementById('chatMode');
|
|
@@ -515,9 +449,6 @@ let chatModeStream=true;
|
|
|
515
449
|
sync();
|
|
516
450
|
})();
|
|
517
451
|
|
|
518
|
-
/* ===== 工具菜单:输入框左侧「+」→ 上方竖排弹窗(模式/上传/文件夹/图片/连接器)=====
|
|
519
|
-
原先这些是 chat 栏一排横向图标,太挤。现收进弹窗;菜单项点击的是**隐藏保留的原按钮**,
|
|
520
|
-
因此上传/文件夹/图片/连接器/模式切换全部复用既有逻辑,零重复实现。 */
|
|
521
452
|
function toolMenuOpen(){ const m=document.getElementById('toolMenu'); return !!(m&&m.classList.contains('show')); }
|
|
522
453
|
function hideToolMenu(){ const m=document.getElementById('toolMenu'); if(m)m.classList.remove('show'); }
|
|
523
454
|
function _toolRows(){
|
|
@@ -554,16 +485,11 @@ function renderToolMenu(){
|
|
|
554
485
|
const m=document.getElementById('toolMenu'), tools=document.getElementById('cmdTools');
|
|
555
486
|
if(!toolMenuOpen() || !m) return;
|
|
556
487
|
if(m.contains(e.target) || e.target.id==='cmdMore') return;
|
|
557
|
-
// 菜单项内部是用 .click() 触发隐藏原按钮的,那个合成事件会冒泡到 document——
|
|
558
|
-
// 不能把它当"点外部"而关掉菜单(否则切模式时菜单会意外收起)
|
|
559
488
|
if(tools && tools.contains(e.target)) return;
|
|
560
489
|
hideToolMenu();
|
|
561
490
|
});
|
|
562
491
|
})();
|
|
563
492
|
|
|
564
|
-
/* ===== 斜杠指令:输入框以 / 开头弹命令菜单,复用现有操作(纯前端,零新后端)=====
|
|
565
|
-
输入 / → 弹菜单;↑↓ 选择、Enter/Tab 执行或补全、Esc 关闭。
|
|
566
|
-
带 <参数> 的命令(如 /搜索)Enter 先补全命令名,再输参数回车执行。 */
|
|
567
493
|
const SLASH_COMMANDS=[
|
|
568
494
|
{cmd:'搜索', icon:'🔍', arg:'关键词', desc:'检索知识库', aliases:['search'], run:(q)=>slashSearch(q)},
|
|
569
495
|
{cmd:'摄入', icon:'📎', desc:'上传文件进知识库', aliases:['上传','upload'], run:()=>{ const f=document.getElementById('cmdFile'); if(f)f.click(); }},
|
|
@@ -631,7 +557,6 @@ async function slashReset(arg){
|
|
|
631
557
|
if(w&&fr.src&&fr.src!=='about:blank'&&typeof w.loadData==='function')w.loadData(); }catch(e){}
|
|
632
558
|
}catch(e){ fillReplyPop('', '清库失败:后端未连接', 'err'); }
|
|
633
559
|
}
|
|
634
|
-
/* 菜单 UI + 键盘导航 */
|
|
635
560
|
let _slashSel=0, _slashList=[];
|
|
636
561
|
function slashFilter(text){
|
|
637
562
|
const q=text.slice(1).toLowerCase();
|
|
@@ -669,7 +594,6 @@ function onSlashInput(){
|
|
|
669
594
|
const inp=document.getElementById('cmd'); if(!inp)return;
|
|
670
595
|
inp.addEventListener('input', onSlashInput);
|
|
671
596
|
inp.addEventListener('blur', ()=>setTimeout(hideSlashMenu,150)); // 失焦关(留时间给 mousedown)
|
|
672
|
-
// 捕获阶段拦截:菜单开时,Enter/方向键先于 transmit 的 bubble 监听被处理
|
|
673
597
|
document.addEventListener('keydown', function(e){
|
|
674
598
|
if(e.target!==inp || !slashMenuOpen()) return;
|
|
675
599
|
if(e.key==='ArrowDown'){ e.preventDefault(); e.stopPropagation(); _slashSel=(_slashSel+1)%_slashList.length; updateSlashSel(); }
|
|
@@ -679,9 +603,7 @@ function onSlashInput(){
|
|
|
679
603
|
}, true);
|
|
680
604
|
})();
|
|
681
605
|
|
|
682
|
-
/* ===== 连接器/插件入口:🔌 面板 + @引用(纯前端,复用现有 AHS API)===== */
|
|
683
606
|
let _atList=[], _atSel=0, _atCache=null, _atCtx=null, _refTriggers={};
|
|
684
|
-
/* --- 🔌 面板:列出 MCP 连接器 / 技能 / SKILL.md,徽章 + 启停 --- */
|
|
685
607
|
async function openConnPanel(){
|
|
686
608
|
const p=document.getElementById('connPanel'); if(!p)return;
|
|
687
609
|
if(p.classList.contains('show')){ p.classList.remove('show'); return; }
|
|
@@ -736,7 +658,6 @@ function renderConnPanel(p, skills, servers, md){
|
|
|
736
658
|
if(p&&p.classList.contains('show')&&!p.contains(e.target)&&e.target.id!=='cmdConn') p.classList.remove('show'); });
|
|
737
659
|
})();
|
|
738
660
|
|
|
739
|
-
/* --- @引用:输入 @ 弹技能/连接器 → 插入 @名称;发送(任务模式)时把其触发词带入 task,让技能匹配可靠命中 --- */
|
|
740
661
|
async function ensureAtCache(){
|
|
741
662
|
if(_atCache) return _atCache;
|
|
742
663
|
try{
|
|
@@ -795,7 +716,6 @@ async function onAtInput(){
|
|
|
795
716
|
_atList=all.filter(c=> !q || c.name.toLowerCase().includes(q) || (c.trigger||'').toLowerCase().includes(q)).slice(0,8);
|
|
796
717
|
_atSel=0; renderAtMenu();
|
|
797
718
|
}
|
|
798
|
-
/* 发送任务模式时,把已引用技能的触发词带入 task(不改用户可见输入),让后端 match 命中 */
|
|
799
719
|
function applyRefs(task){
|
|
800
720
|
let out=task;
|
|
801
721
|
for(const nm in _refTriggers){
|
|
@@ -817,7 +737,6 @@ function applyRefs(task){
|
|
|
817
737
|
}, true);
|
|
818
738
|
})();
|
|
819
739
|
|
|
820
|
-
/* ===== 图片附件 → 多模态对话:附图 + 缩放压缩 + 随问题发给视觉模型 ===== */
|
|
821
740
|
let _pendingImages=[]; // [{media_type,data(base64),url(预览),name}]
|
|
822
741
|
const _IMG_MAX=6, _IMG_MAXDIM=1280, _IMG_Q=0.85;
|
|
823
742
|
function _downscaleImage(file){
|
|
@@ -875,7 +794,6 @@ async function visionAdvisory(){
|
|
|
875
794
|
}
|
|
876
795
|
})();
|
|
877
796
|
|
|
878
|
-
/* ===== 文件附件 → 聊天附件:上传落盘 + 随问题发给 LLM(文本类提取正文,其它降级元数据) ===== */
|
|
879
797
|
let _pendingFiles=[]; // [{id,name,size,mime,url,text}]
|
|
880
798
|
const _FILE_MAX=6, _FILE_MAXB=20*1024*1024;
|
|
881
799
|
function _fileSize(sz){
|
|
@@ -924,11 +842,9 @@ function clearPendingFiles(){ _pendingFiles=[]; renderFileStrip(); }
|
|
|
924
842
|
}
|
|
925
843
|
})();
|
|
926
844
|
|
|
927
|
-
/* 流式直答:SSE 逐字追加进回复浮窗 */
|
|
928
845
|
let _chatHistory=[]; // 多轮对话历史 [{role,content}];随请求带给后端 + 界面渲染成对话线程
|
|
929
846
|
function clearChatHistory(){ _chatHistory=[]; } // 新话题/清屏可调
|
|
930
847
|
|
|
931
|
-
/* ═══ 会话历史:对话不再刷新即丢,落盘到后端、左栏可回看 ═══ */
|
|
932
848
|
let _sessionId = localStorage.getItem('foxclawSessionId') || '';
|
|
933
849
|
async function ensureSession(){
|
|
934
850
|
if(_sessionId){
|
|
@@ -957,7 +873,6 @@ async function openSession(id){
|
|
|
957
873
|
await loadSessions();
|
|
958
874
|
}catch(e){}
|
|
959
875
|
}
|
|
960
|
-
/* 渲染已存在会话(只有历史,没有"当前问"和空答槽)*/
|
|
961
876
|
function renderSessionThread(agent){
|
|
962
877
|
const body=document.querySelector('#coreChat .cc-body'); if(!body)return;
|
|
963
878
|
const a=document.getElementById('ccAgent'); if(a)a.textContent=`${AGENT_CN[agent]||agent} Agent`;
|
|
@@ -970,8 +885,6 @@ function renderSessionThread(agent){
|
|
|
970
885
|
Center.chat();
|
|
971
886
|
body.scrollTop=body.scrollHeight;
|
|
972
887
|
}
|
|
973
|
-
/* 会话列表:渲染进左栏 #nsList(会话历史是这个界面的主体内容)。
|
|
974
|
-
每条 → openSession(id) 真实打开对应会话;✕ → 删除。 */
|
|
975
888
|
async function loadSessions(){
|
|
976
889
|
const list=document.getElementById('nsList'); if(!list)return;
|
|
977
890
|
const cnt=document.getElementById('nsCount');
|
|
@@ -1006,8 +919,6 @@ async function delSession(id){
|
|
|
1006
919
|
}
|
|
1007
920
|
await loadSessions();
|
|
1008
921
|
}
|
|
1009
|
-
// 把 _chatHistory + 当前问 重建成双方对称的气泡对话线程(助手左/用户右,各自独立圆角框),
|
|
1010
|
-
// cc-body 可滚动 = 下拉浏览历史。保留上下文,不再只显示最新一条。
|
|
1011
922
|
function _fmtTime(ts){ const d=new Date(ts||Date.now()); return ('0'+d.getHours()).slice(-2)+':'+('0'+d.getMinutes()).slice(-2); }
|
|
1012
923
|
function _chatRow(role, inner, agent, ts, isSlot){
|
|
1013
924
|
const t=_fmtTime(ts);
|
|
@@ -1040,9 +951,6 @@ function renderChatThread(currentQ, agent, attachments){
|
|
|
1040
951
|
Center.chat();
|
|
1041
952
|
body.scrollTop=body.scrollHeight;
|
|
1042
953
|
}
|
|
1043
|
-
/* 执行证据渲染:把后端每步的核验结论摊开给人看。
|
|
1044
|
-
三态有意不做成"全部打勾"——unverified 显示为「未核验」,failed 显示为「未完成」,
|
|
1045
|
-
因为把没凭据的步骤画成绿色,正是这套机制要消灭的东西。 */
|
|
1046
954
|
function renderEvidence(items){
|
|
1047
955
|
const wrap=document.getElementById('ccEvidence'); if(!wrap)return;
|
|
1048
956
|
if(!items||!items.length){ wrap.hidden=true; wrap.innerHTML=''; return; }
|
|
@@ -1070,8 +978,6 @@ async function transmitStream(task, agent, images, attachments){
|
|
|
1070
978
|
const rp=document.getElementById('ccReply'), st=document.getElementById('ccStatus');
|
|
1071
979
|
rp.textContent=''; rp.style.color='var(--ink)'; st.style.color='var(--ink-dim)'; st.textContent='◌ 流式生成中…';
|
|
1072
980
|
let acc='';
|
|
1073
|
-
// 推理过程折叠块:每轮重置。后端以 SSE `event: thinking` 单独送出思考增量,
|
|
1074
|
-
// 正文(delta)不含思考,这里只做展示,不参与落库/复制。
|
|
1075
981
|
let _thinkWrap=document.getElementById('ccThink');
|
|
1076
982
|
if(!_thinkWrap && rp.parentNode){
|
|
1077
983
|
_thinkWrap=document.createElement('details');
|
|
@@ -1082,8 +988,6 @@ async function transmitStream(task, agent, images, attachments){
|
|
|
1082
988
|
const _thinkBody=document.getElementById('ccThinkBody');
|
|
1083
989
|
if(_thinkBody) _thinkBody.textContent='';
|
|
1084
990
|
if(_thinkWrap){ _thinkWrap.hidden=true; _thinkWrap.open=false; }
|
|
1085
|
-
// 执行证据:工具轮每步「核验到没到位」+ 可点开的凭据(来源链接 / 真实文件)。
|
|
1086
|
-
// 挂在答案**下方**——正文是给用户读的结论,这里是给人查的凭据,两回事。
|
|
1087
991
|
let _evWrap=document.getElementById('ccEvidence');
|
|
1088
992
|
if(!_evWrap && rp.parentNode){
|
|
1089
993
|
_evWrap=document.createElement('div');
|
|
@@ -1106,7 +1010,6 @@ async function transmitStream(task, agent, images, attachments){
|
|
|
1106
1010
|
let idx;
|
|
1107
1011
|
while((idx=buf.indexOf('\n\n'))>=0){
|
|
1108
1012
|
const block=buf.slice(0,idx); buf=buf.slice(idx+2);
|
|
1109
|
-
// 兼容命名事件(SSE 规范):`event: thinking` + `data: {...}`;无 event 行按默认事件处理。
|
|
1110
1013
|
const evName=((block.match(/^event:\s*(\S+)/m)||[])[1])||'message';
|
|
1111
1014
|
const dataLine=((block.match(/^data:\s*([\s\S]*)$/m)||[])[1]||'').trim();
|
|
1112
1015
|
if(!dataLine)continue;
|
|
@@ -1138,15 +1041,11 @@ async function transmitStream(task, agent, images, attachments){
|
|
|
1138
1041
|
if(typeof loadMemStats==='function') loadMemStats();
|
|
1139
1042
|
}
|
|
1140
1043
|
|
|
1141
|
-
/* 任务模式流式(/api/task/stream):SSE 逐 stage 进度 + act 逐字 + 最终结果。仅领域 Agent 用——
|
|
1142
|
-
该端点跑单 Agent 认知管线,不做主管的多 Agent 分派(主管编排任务仍走阻塞 /api/task)。 */
|
|
1143
1044
|
async function transmitTaskStream(task, agent){
|
|
1144
1045
|
const rp=document.getElementById('ccReply'), st=document.getElementById('ccStatus');
|
|
1145
1046
|
rp.textContent=''; rp.style.color='var(--ink)'; st.style.color='var(--ink-dim)'; st.textContent='◌ 认知管线启动…';
|
|
1146
1047
|
let acc='', stages=[];
|
|
1147
1048
|
const STAGE_CN={perceive:'感知',recall:'召回',plan:'规划',route:'路由',act:'执行',verify:'校验',reflect:'反思'};
|
|
1148
|
-
// 显式计划块:后端在 plan stage 之后补发 {"plan":{steps,flow,multi_step}} → 列出计划步骤,
|
|
1149
|
-
// 让"它打算怎么做"在执行前就可见(此前只在跑完后才能从 thought chain 里翻)。
|
|
1150
1049
|
let _planWrap=document.getElementById('ccPlan');
|
|
1151
1050
|
if(!_planWrap && rp.parentNode){
|
|
1152
1051
|
_planWrap=document.createElement('details');
|
|
@@ -1198,7 +1097,6 @@ async function transmitTaskStream(task, agent){
|
|
|
1198
1097
|
if(typeof loadMemStats==='function') loadMemStats();
|
|
1199
1098
|
}
|
|
1200
1099
|
|
|
1201
|
-
/* 任务流操作 */
|
|
1202
1100
|
function prependTask(name,agent,status){
|
|
1203
1101
|
const list=document.getElementById('taskList'); if(!list)return;
|
|
1204
1102
|
const now=new Date(); const hh=String(now.getHours()).padStart(2,'0')+':'+String(now.getMinutes()).padStart(2,'0');
|
|
@@ -1216,11 +1114,9 @@ function markTask(name,status){
|
|
|
1216
1114
|
row.querySelector('.ti').textContent=status==='ok'?'✓':status==='blocked'?'✕':'◌';}
|
|
1217
1115
|
}
|
|
1218
1116
|
|
|
1219
|
-
/* 5. WebSocket 实时事件流 */
|
|
1220
1117
|
window.actPulse=0;
|
|
1221
1118
|
function connectWS(){
|
|
1222
1119
|
try{
|
|
1223
|
-
// 后端若开了鉴权,WS 用 ?key= 传(浏览器无法给 WS 设请求头)
|
|
1224
1120
|
const _k=localStorage.getItem('foxclawApiKey');
|
|
1225
1121
|
const ws=new WebSocket(WS + (_k?('?key='+encodeURIComponent(_k)):''));
|
|
1226
1122
|
ws.onmessage=ev=>{
|
|
@@ -1240,7 +1136,6 @@ function flashCmd(c){
|
|
|
1240
1136
|
setTimeout(()=>{bar.style.boxShadow=''; bar.style.borderColor='';},700);
|
|
1241
1137
|
}
|
|
1242
1138
|
|
|
1243
|
-
/* ===== 全屏 overlay 浮层 ===== */
|
|
1244
1139
|
function openOverlay(agent){
|
|
1245
1140
|
const m=KP_META[agent]||KP_META.office;
|
|
1246
1141
|
document.getElementById('ovTitle').textContent=m.title;
|
|
@@ -1248,7 +1143,6 @@ function openOverlay(agent){
|
|
|
1248
1143
|
const body=document.getElementById('ovBody');
|
|
1249
1144
|
const d=_lastKernelData[agent]||{};
|
|
1250
1145
|
if(agent==='admin'){
|
|
1251
|
-
// 行程:左留白 + 地图 + 右栏(录入行程),左右等宽对称
|
|
1252
1146
|
body.innerHTML=`<div class="ov-grid">
|
|
1253
1147
|
<div class="ov-spacer"></div>
|
|
1254
1148
|
<div style="position:relative;height:100%">
|
|
@@ -1279,11 +1173,9 @@ document.getElementById('ovClose')?.addEventListener('click',closeOverlay);
|
|
|
1279
1173
|
document.getElementById('ovMask')?.addEventListener('click',e=>{if(e.target.id==='ovMask')closeOverlay();});
|
|
1280
1174
|
document.addEventListener('keydown',e=>{if(e.key==='Escape')closeOverlay();});
|
|
1281
1175
|
|
|
1282
|
-
/* 地图自动全屏开关(持久化到内存) */
|
|
1283
1176
|
let autoMap=false;
|
|
1284
1177
|
document.getElementById('ovAutoMap')?.addEventListener('change',e=>{autoMap=e.target.checked;});
|
|
1285
1178
|
|
|
1286
|
-
/* 全屏知识图谱(Canvas 节点连线) */
|
|
1287
1179
|
function drawKnowledgeGraph(canvasId,d){
|
|
1288
1180
|
const c=document.getElementById(canvasId); if(!c)return;
|
|
1289
1181
|
const x=c.getContext('2d');
|
|
@@ -1301,7 +1193,6 @@ function drawKnowledgeGraph(canvasId,d){
|
|
|
1301
1193
|
x.clearRect(0,0,c.width,c.height);rot+=0.002;
|
|
1302
1194
|
const cx=c.width/2,cy=c.height/2,R=Math.min(cx,cy)*0.8;
|
|
1303
1195
|
nodes.forEach(n=>{n._x=cx+Math.cos(n.a+rot)*n.r*R;n._y=cy+Math.sin(n.a+rot)*n.r*R;});
|
|
1304
|
-
// 连线(同主题相连)
|
|
1305
1196
|
x.lineWidth=1;
|
|
1306
1197
|
for(let i=0;i<nodes.length;i++)for(let j=i+1;j<nodes.length;j++){
|
|
1307
1198
|
if(nodes[i].topic&&nodes[i].topic===nodes[j].topic){
|
|
@@ -1316,8 +1207,6 @@ function drawKnowledgeGraph(canvasId,d){
|
|
|
1316
1207
|
loop();
|
|
1317
1208
|
}
|
|
1318
1209
|
|
|
1319
|
-
|
|
1320
|
-
/* ===== 左侧导航 hover 弹出精简条目 + 点击进中央大屏 ===== */
|
|
1321
1210
|
const MODULE_META={
|
|
1322
1211
|
tasks:{title:'任务中心',tag:'Tasks'},
|
|
1323
1212
|
memory:{title:'记忆中心',tag:'Memory'},
|
|
@@ -1327,7 +1216,6 @@ const MODULE_META={
|
|
|
1327
1216
|
agents:{title:'代理',tag:'Agents'},
|
|
1328
1217
|
usage:{title:'使用情况',tag:'Usage'},
|
|
1329
1218
|
};
|
|
1330
|
-
/* 取模块精简条目(供轮播框 + 中央大屏用)*/
|
|
1331
1219
|
async function fetchModuleBrief(mod){
|
|
1332
1220
|
try{
|
|
1333
1221
|
if(mod==='tasks'){
|
|
@@ -1357,19 +1245,16 @@ async function fetchModuleBrief(mod){
|
|
|
1357
1245
|
return [{title:'加载失败',sub:'后端未连接'}];
|
|
1358
1246
|
}
|
|
1359
1247
|
|
|
1360
|
-
/* 填充三个轮播框 + 自动滚动 */
|
|
1361
1248
|
function fillFeed(mod, trackId){
|
|
1362
1249
|
const track=document.getElementById(trackId); if(!track)return;
|
|
1363
1250
|
fetchModuleBrief(mod).then(items=>{
|
|
1364
1251
|
if(!items||!items.length){ track.innerHTML='<div class="feed-empty">暂无数据</div>'; return; }
|
|
1365
1252
|
const render=()=>items.map((it,idx)=>`<div class="feed-item" data-mod="${mod}" data-idx="${idx}"><b>${escapeHtml(it.title)}</b>${it.sub?`<span>${escapeHtml(it.sub)}</span>`:''}</div>`).join('');
|
|
1366
|
-
// 内容较多才滚动:复制一份首尾相接,无缝循环
|
|
1367
1253
|
const enough=items.length>=4;
|
|
1368
1254
|
track.innerHTML=render()+(enough?render():'');
|
|
1369
1255
|
track.classList.toggle('rolling', enough);
|
|
1370
1256
|
if(enough){ track.style.animationDuration=(items.length*2.2)+'s'; }
|
|
1371
1257
|
else{ track.style.animationDuration=''; }
|
|
1372
|
-
// 点条目 → 中央大屏
|
|
1373
1258
|
track.querySelectorAll('.feed-item').forEach(el=>el.onclick=()=>{
|
|
1374
1259
|
if(mod==='knowledge'){ const it=items[+el.dataset.idx]; if(it) showKnowledgeArticle(it); }
|
|
1375
1260
|
else showModuleInCore(mod);
|
|
@@ -1381,7 +1266,6 @@ function refreshFeeds(){
|
|
|
1381
1266
|
fillFeed('memory','feedMemory');
|
|
1382
1267
|
fillFeed('knowledge','feedKnowledge');
|
|
1383
1268
|
}
|
|
1384
|
-
/* 单篇全文卡片(含删除 + 我的笔记)*/
|
|
1385
1269
|
function _knowCardHtml(k){
|
|
1386
1270
|
const notes=(k.notes||[]);
|
|
1387
1271
|
return `<div class="know-card">
|
|
@@ -1399,7 +1283,6 @@ function _knowCardHtml(k){
|
|
|
1399
1283
|
</div>
|
|
1400
1284
|
</div>`;
|
|
1401
1285
|
}
|
|
1402
|
-
/* 列表视图:每篇一行(标题+摘要+删除),点击进详情。reload=删除/刷新后重新加载 */
|
|
1403
1286
|
function _renderKnowList(body, items, reload){
|
|
1404
1287
|
body.innerHTML='<div class="know-index">'+items.map((k,i)=>`
|
|
1405
1288
|
<div class="know-row" data-i="${i}">
|
|
@@ -1409,8 +1292,6 @@ function _renderKnowList(body, items, reload){
|
|
|
1409
1292
|
body.querySelectorAll('.know-row').forEach(el=>el.onclick=(e)=>{ if(e.target.closest('.know-del'))return; _renderKnowDetail(body, items, +el.dataset.i, reload); });
|
|
1410
1293
|
_wireKnowDelete(body, reload);
|
|
1411
1294
|
}
|
|
1412
|
-
/* 详情视图:只显示这一篇的全文 + 返回列表 */
|
|
1413
|
-
/* 笔记保存(有内容才提交;保存后回调 onSaved 重渲染),多处复用 */
|
|
1414
1295
|
function _wireKnowNote(body, item, onSaved){
|
|
1415
1296
|
const saveBtn=body.querySelector('.know-note-save');
|
|
1416
1297
|
if(!saveBtn)return;
|
|
@@ -1432,7 +1313,6 @@ function _renderKnowDetail(body, items, i, reload){
|
|
|
1432
1313
|
_wireKnowDelete(body, reload);
|
|
1433
1314
|
_wireKnowNote(body, k, ()=>_renderKnowDetail(body, items, i, reload));
|
|
1434
1315
|
}
|
|
1435
|
-
/* 左栏点单条文章 → 中央「独立阅读器」只显示这一篇(不显示全部列表) */
|
|
1436
1316
|
function showKnowledgeArticle(item){
|
|
1437
1317
|
const wrap=document.querySelector('.corewrap'), stage=document.getElementById('coreStage');
|
|
1438
1318
|
if(!wrap||!stage||!item)return;
|
|
@@ -1455,7 +1335,6 @@ function _wireKnowDelete(container, onDone){
|
|
|
1455
1335
|
}catch(e){ alert('删除失败:'+e); }
|
|
1456
1336
|
});
|
|
1457
1337
|
}
|
|
1458
|
-
/* 知识库 → 中央展开全文卡片(读完整录入内容,保留换行) */
|
|
1459
1338
|
async function showKnowledgeFull(){
|
|
1460
1339
|
const wrap=document.querySelector('.corewrap'), stage=document.getElementById('coreStage');
|
|
1461
1340
|
if(!wrap||!stage)return;
|
|
@@ -1472,8 +1351,6 @@ async function showKnowledgeFull(){
|
|
|
1472
1351
|
_renderKnowList(body, items, showKnowledgeFull);
|
|
1473
1352
|
}catch(e){ body.innerHTML='<div class="kp-empty" style="padding:30px 0">后端未连接</div>'; }
|
|
1474
1353
|
}
|
|
1475
|
-
/* 左栏「菜单」按钮:把原来平铺的八项功能收进一个分类菜单(浮层贴着按钮右侧展开)。
|
|
1476
|
-
靠按钮的实时位置定位,不写死坐标 —— 窗口缩放 / 侧栏宽度变化都不会错位。 */
|
|
1477
1354
|
const _navMenu=document.getElementById('navMenu');
|
|
1478
1355
|
const _navMenuBtn=document.getElementById('navMenuBtn');
|
|
1479
1356
|
function setNavMenu(open){
|
|
@@ -1500,8 +1377,6 @@ document.addEventListener('click',e=>{
|
|
|
1500
1377
|
document.addEventListener('keydown',e=>{ if(e.key==='Escape') setNavMenu(false); });
|
|
1501
1378
|
window.addEventListener('resize',()=>setNavMenu(false));
|
|
1502
1379
|
|
|
1503
|
-
/* 当前视图 → 菜单按钮文案 + 菜单内高亮(视图可能从菜单、右栏 ›、slash 命令等多处打开,
|
|
1504
|
-
统一在这里同步,免得各处各写一份 active 状态) */
|
|
1505
1380
|
function _syncNav(mod){
|
|
1506
1381
|
let name='';
|
|
1507
1382
|
document.querySelectorAll('#navList .nav-link').forEach(x=>{
|
|
@@ -1510,11 +1385,9 @@ function _syncNav(mod){
|
|
|
1510
1385
|
if(hit) name=x.querySelector('b')?.textContent||'';
|
|
1511
1386
|
});
|
|
1512
1387
|
const lbl=document.getElementById('navMenuLabel');
|
|
1513
|
-
// 回显成「菜单 · 任务」:既保留"这是个菜单"的语义,又能一眼看出在哪一屏
|
|
1514
1388
|
if(lbl) lbl.textContent=(name&&mod!=='overview')?`菜单 · ${name}`:'菜单';
|
|
1515
1389
|
}
|
|
1516
1390
|
|
|
1517
|
-
// 左栏导航:点击 → 中央大屏显示对应视图(overview 回核心;knowledge 走全屏知识视图)
|
|
1518
1391
|
document.querySelectorAll('#navList .nav-link').forEach(el=>{
|
|
1519
1392
|
el.addEventListener('click',()=>{
|
|
1520
1393
|
const mod=el.dataset.module;
|
|
@@ -1526,7 +1399,6 @@ document.querySelectorAll('#navList .nav-link').forEach(el=>{
|
|
|
1526
1399
|
});
|
|
1527
1400
|
});
|
|
1528
1401
|
|
|
1529
|
-
/* 中央大屏显示模块详情 */
|
|
1530
1402
|
async function showModuleInCore(mod){
|
|
1531
1403
|
const meta=MODULE_META[mod]||{title:mod,tag:''};
|
|
1532
1404
|
const wrap=document.querySelector('.corewrap'), stage=document.getElementById('coreStage');
|
|
@@ -1544,7 +1416,6 @@ async function showModuleInCore(mod){
|
|
|
1544
1416
|
if(mod==='memory') wirePrefDelete(body);
|
|
1545
1417
|
}catch(e){ body.innerHTML='<div class="kp-empty">后端未连接</div>'; }
|
|
1546
1418
|
}
|
|
1547
|
-
/* 偏好删除按钮:删一条误记的偏好 → 刷新记忆中心大屏(F3 前端闭环) */
|
|
1548
1419
|
function wirePrefDelete(body){
|
|
1549
1420
|
body.querySelectorAll('.pref-del').forEach(btn=>{
|
|
1550
1421
|
btn.onclick=async()=>{
|
|
@@ -1573,7 +1444,6 @@ async function renderModuleBig(mod){
|
|
|
1573
1444
|
const d=await fetch(`${API}/bus/history?limit=40`).then(x=>x.json()).catch(()=>({}));
|
|
1574
1445
|
const evs=(d.events||[]);
|
|
1575
1446
|
if(!evs.length) return '<div class="kp-empty" style="padding:40px">暂无活动记录 · 系统事件(执行→审计→记忆)会实时汇入这里</div>';
|
|
1576
|
-
// 事件真实字段:{channel, topic, payload, ts(epoch 秒·浮点), source, correlation_id}
|
|
1577
1447
|
return '<div class="big-list">'+evs.slice().reverse().map(e=>{
|
|
1578
1448
|
const topic=e.topic||e.channel||'事件';
|
|
1579
1449
|
const ts=Number(e.ts);
|
|
@@ -1849,9 +1719,6 @@ async function wireToolCenter(root){
|
|
|
1849
1719
|
};
|
|
1850
1720
|
}
|
|
1851
1721
|
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
/* ===== 数据录入表单 ===== */
|
|
1855
1722
|
const ENTRY_FORMS={
|
|
1856
1723
|
finance:{title:'录入交易',sub:'添加一笔收支记录',endpoint:'/finance/import',
|
|
1857
1724
|
fields:[
|
|
@@ -1891,7 +1758,6 @@ function openEntry(kind){
|
|
|
1891
1758
|
document.getElementById('entrySub').textContent=f.sub;
|
|
1892
1759
|
document.getElementById('entryMsg').textContent='';
|
|
1893
1760
|
let h='';
|
|
1894
|
-
// 行程点的经纬度并排
|
|
1895
1761
|
f.fields.forEach(fd=>{
|
|
1896
1762
|
const ctl = fd.type==='textarea'
|
|
1897
1763
|
? `<textarea data-k="${fd.k}" placeholder="${fd.ph||''}" rows="8" style="width:100%;resize:vertical;font-family:inherit;line-height:1.6"></textarea>`
|
|
@@ -1917,9 +1783,6 @@ document.getElementById('entrySubmit')?.addEventListener('click',async()=>{
|
|
|
1917
1783
|
}catch(e){ msg.style.color='var(--amber)'; msg.textContent='录入失败:后端未连接'; }
|
|
1918
1784
|
});
|
|
1919
1785
|
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
/* 启动 —— 全部包 try-catch,任一失败不影响其他;还失败的也在 upStat 显示具体错 */
|
|
1923
1786
|
const _bootTry=(fn,name)=>{ try{ const r=fn(); if(r&&r.catch) r.catch(e=>{ try{document.getElementById('upStat').textContent='启动失败:'+name}catch(_){ } }); }
|
|
1924
1787
|
catch(e){ try{document.getElementById('upStat').textContent='启动失败:'+name}catch(_){ } } };
|
|
1925
1788
|
_bootTry(fetchAMapConfig, 'fetchAMapConfig');
|
|
@@ -1931,20 +1794,13 @@ _bootTry(()=>loadKernel('supervisor'), 'loadKernel');
|
|
|
1931
1794
|
try{ setInterval(loadStatus,15000); }catch(e){}
|
|
1932
1795
|
try{ setInterval(loadMemStats,20000); }catch(e){}
|
|
1933
1796
|
try{ setInterval(loadKnowList,30000); }catch(e){}
|
|
1934
|
-
// 右栏面板标题的 "›" → 打开对应中央大屏
|
|
1935
1797
|
document.getElementById('memMore')?.addEventListener('click',()=>showModuleInCore('memory'));
|
|
1936
1798
|
document.getElementById('knowMore')?.addEventListener('click',()=>showKnowledgeFull());
|
|
1937
|
-
// 知识摄入入口:复用底部指令栏里那三个隐藏的原有控件(逻辑不重复实现)
|
|
1938
1799
|
document.getElementById('ingFile')?.addEventListener('click',()=>document.getElementById('cmdAttach')?.click());
|
|
1939
1800
|
document.getElementById('ingFolder')?.addEventListener('click',()=>document.getElementById('cmdFolder')?.click());
|
|
1940
1801
|
document.getElementById('ingNote')?.addEventListener('click',()=>{ try{ openEntry('office'); }catch(e){} });
|
|
1941
|
-
// 会话历史:只渲染列表,**不预建空会话**(否则每次刷新都堆一条 0 条的空壳);
|
|
1942
|
-
// 真正建会话推迟到用户发第一条消息时(transmitStream 里 ensureSession)。
|
|
1943
1802
|
_bootTry(loadSessions, 'loadSessions');
|
|
1944
1803
|
document.getElementById('sessNew')?.addEventListener('click',()=>newSession(true));
|
|
1945
1804
|
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
/* 内联 onclick 桥接:模块作用域下,HTML 里 onclick="fn()" 需从 window 解析 */
|
|
1949
1805
|
Object.assign(window, { openEntry, importDomainFile, switchProvider });
|
|
1950
1806
|
|