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,9 +1,3 @@
|
|
|
1
|
-
# tools/check_log.ps1
|
|
2
|
-
# 检查 uvicorn 日志中是否有 IVF 升级关键字(行扫描 + FileShare.ReadWrite 兼容锁文件)。
|
|
3
|
-
# 用法:
|
|
4
|
-
# powershell -NoProfile -File tools\check_log.ps1 # 查 stdout(uvicorn.log)
|
|
5
|
-
# powershell -NoProfile -File tools\check_log.ps1 err # 查 stderr(uvicorn.err.log)
|
|
6
|
-
# powershell -NoProfile -File tools\check_log.ps1 err grep # 仅 grep 'VectorIndex 升级' 行
|
|
7
1
|
param(
|
|
8
2
|
[ValidateSet("stdout","err")] $which = "stdout",
|
|
9
3
|
[switch] $grep
|
|
@@ -18,8 +12,6 @@ if (-not (Test-Path $log)) {
|
|
|
18
12
|
exit 1
|
|
19
13
|
}
|
|
20
14
|
|
|
21
|
-
# 用 [System.IO.File]::Open + FileShare.ReadWrite 兼容 python 文件正在写入的日志
|
|
22
|
-
# (PowerShell 5.1 Get-Content 在 UTF-8 + 文件被锁场景会返空,见 review [2])
|
|
23
15
|
$fs = [System.IO.File]::Open($log, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::ReadWrite)
|
|
24
16
|
$reader = New-Object System.IO.StreamReader($fs, [System.Text.Encoding]::UTF8)
|
|
25
17
|
$content = $reader.ReadToEnd()
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# mypy 类型错误棘轮基线。只允许下降。
|
|
2
|
-
# 2026-11-
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
|
|
2
|
+
# 2026-11-10 复核:mypy 2.4.1 / Python 3.12(.venv,依赖按 requirements.txt)→ 0 errors。
|
|
3
|
+
# 历史:673(2.4.0 / 3.14 旧依赖)→ 655(cli.py 不再用 **dict 展开调 uvicorn.run)
|
|
4
|
+
# → 645(playwright_driver 收进 _require_page;_policy 用 np.float64)
|
|
5
|
+
# → 408(api/app.py 改名 bootstrap.py,解掉 101 条「子模块名遮蔽 app 变量」;
|
|
6
|
+
# 路由层经 os_instance() 取共享实例,消掉 104 条 Optional 属性访问;
|
|
7
|
+
# PolicyMixin 声明宿主注入状态;-33;model_router 补 Any/Optional 注解)。
|
|
8
|
+
# → 152(基准校准;此后按批真修:httpx kwargs 类型化 / named_stage 装修饰器、
|
|
9
|
+
# None 默认字段 Optional 化、tracing+守门人句柄窄化、Literal 收窄、
|
|
10
|
+
# max/min key 改 lambda、坐标与脏输入窄化)。
|
|
11
|
+
# → 0(2026-11-10:余量清空。修法一律是真修——Optional 注解、局部变量窄化、
|
|
12
|
+
# cast(Literal)、诚实返回类型;未新增 type: ignore,未放宽 mypy 配置)
|
|
13
|
+
0
|
|
@@ -27,8 +27,10 @@ from pathlib import Path
|
|
|
27
27
|
ROOT = Path(__file__).resolve().parent.parent
|
|
28
28
|
BASELINE = ROOT / "tools" / "mypy_baseline.txt"
|
|
29
29
|
SUMMARY = re.compile(r"Found (\d+) errors? in (\d+) files?")
|
|
30
|
+
DETAIL = re.compile(r"^(?P<file>[^:\n]+?\.py):\d+: error: .*\[(?P<code>[a-z-]+)\]$")
|
|
31
|
+
TOP_N = 30
|
|
32
|
+
SAMPLES: dict[str, list[str]] = {}
|
|
30
33
|
|
|
31
|
-
# Windows GBK 控制台遇到 ✓/✗ 会 UnicodeEncodeError,直接把输出钉成 utf-8。
|
|
32
34
|
for _s in (sys.stdout, sys.stderr):
|
|
33
35
|
try:
|
|
34
36
|
_s.reconfigure(encoding="utf-8", errors="replace") # type: ignore[union-attr]
|
|
@@ -48,34 +50,77 @@ def read_baseline() -> int:
|
|
|
48
50
|
sys.exit(2)
|
|
49
51
|
|
|
50
52
|
|
|
51
|
-
def run_mypy() -> tuple[int, int]:
|
|
52
|
-
"""返回 (错误数,
|
|
53
|
+
def run_mypy() -> tuple[int, int, dict[str, int], dict[str, int]]:
|
|
54
|
+
"""返回 (错误数, 文件数, 按文件计数, 按错误码计数)。
|
|
55
|
+
|
|
56
|
+
配置取自 pyproject [tool.mypy],此处不重复传参。明细统计是为了让 CI 失败时能直接
|
|
57
|
+
看出「多出来的错误在哪」——此前门禁只打印总数,CI 报 486 而本地 408 时无从下手。
|
|
58
|
+
"""
|
|
53
59
|
r = subprocess.run(
|
|
54
60
|
[sys.executable, "-m", "mypy", "foxclaw"],
|
|
55
61
|
cwd=ROOT, capture_output=True, text=True, encoding="utf-8", errors="replace",
|
|
56
62
|
)
|
|
57
63
|
out = (r.stdout or "") + (r.stderr or "")
|
|
64
|
+
by_file: dict[str, int] = {}
|
|
65
|
+
by_code: dict[str, int] = {}
|
|
66
|
+
for line in out.splitlines():
|
|
67
|
+
d = DETAIL.match(line.strip())
|
|
68
|
+
if d:
|
|
69
|
+
f = d.group("file").replace("\\", "/")
|
|
70
|
+
by_file[f] = by_file.get(f, 0) + 1
|
|
71
|
+
by_code[d.group("code")] = by_code.get(d.group("code"), 0) + 1
|
|
72
|
+
SAMPLES.setdefault(f, []).append(line.strip().split("error: ", 1)[-1])
|
|
58
73
|
m = SUMMARY.search(out)
|
|
59
74
|
if m:
|
|
60
|
-
return int(m.group(1)), int(m.group(2))
|
|
75
|
+
return int(m.group(1)), int(m.group(2)), by_file, by_code
|
|
61
76
|
if "Success: no issues found" in out:
|
|
62
|
-
return 0, 0
|
|
77
|
+
return 0, 0, {}, {}
|
|
63
78
|
print(out[-4000:])
|
|
64
79
|
print("✗ 无法解析 mypy 输出(格式变了?上面是原始输出)")
|
|
65
80
|
sys.exit(2)
|
|
66
81
|
|
|
67
82
|
|
|
83
|
+
def report(by_file: dict[str, int], by_code: dict[str, int]) -> None:
|
|
84
|
+
"""失败时打印分布 + 样本消息,便于对照本地与 CI 的差异。"""
|
|
85
|
+
if by_code:
|
|
86
|
+
print(" 按错误码:")
|
|
87
|
+
for code, n in sorted(by_code.items(), key=lambda kv: -kv[1])[:TOP_N]:
|
|
88
|
+
print(f" {n:4d} {code}")
|
|
89
|
+
if by_file:
|
|
90
|
+
print(f" 按文件(top {TOP_N}):")
|
|
91
|
+
for f, n in sorted(by_file.items(), key=lambda kv: -kv[1])[:TOP_N]:
|
|
92
|
+
print(f" {n:4d} {f}")
|
|
93
|
+
if SAMPLES:
|
|
94
|
+
top_files = sorted(by_file.items(), key=lambda kv: -kv[1])[:5]
|
|
95
|
+
print(" 错误样本(按错误最多的文件取前 4 条):")
|
|
96
|
+
for f, _ in top_files:
|
|
97
|
+
for s in SAMPLES.get(f, [])[:4]:
|
|
98
|
+
print(f" {f}: {s}")
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def toolchain() -> str:
|
|
102
|
+
"""打出 mypy 与解释器版本:本地/CI 计数不一致时,版本漂移是第一嫌疑。"""
|
|
103
|
+
try:
|
|
104
|
+
from importlib.metadata import version
|
|
105
|
+
mypy_ver = version("mypy")
|
|
106
|
+
except Exception:
|
|
107
|
+
mypy_ver = "?"
|
|
108
|
+
return f"mypy {mypy_ver} on Python {sys.version.split()[0]}"
|
|
109
|
+
|
|
110
|
+
|
|
68
111
|
def main() -> int:
|
|
69
112
|
baseline = read_baseline()
|
|
70
|
-
errors, files = run_mypy()
|
|
113
|
+
errors, files, by_file, by_code = run_mypy()
|
|
71
114
|
update = "--update" in sys.argv[1:]
|
|
72
115
|
|
|
73
116
|
print(f"mypy: {errors} errors in {files} files | baseline: {baseline}")
|
|
117
|
+
print(f" toolchain: {toolchain()}")
|
|
74
118
|
|
|
75
119
|
if update:
|
|
76
120
|
if errors > baseline:
|
|
77
121
|
print(f"✗ 拒绝更新基线:{errors} > {baseline}。棘轮只允许下调,"
|
|
78
122
|
f"请先修掉新增的 {errors - baseline} 个错误")
|
|
123
|
+
report(by_file, by_code)
|
|
79
124
|
return 1
|
|
80
125
|
BASELINE.write_text(f"{errors}\n", encoding="utf-8")
|
|
81
126
|
print(f"✓ 基线已刷新为 {errors}")
|
|
@@ -84,6 +129,7 @@ def main() -> int:
|
|
|
84
129
|
if errors > baseline:
|
|
85
130
|
print(f"✗ 类型错误增加 {errors - baseline} 个({baseline} → {errors})。"
|
|
86
131
|
f"新增错误必须修掉,不能靠调高基线通过")
|
|
132
|
+
report(by_file, by_code)
|
|
87
133
|
return 1
|
|
88
134
|
if errors < baseline:
|
|
89
135
|
print(f"✓ 通过,且比基线少 {baseline - errors} 个 —— "
|
package/python/tools/pack.py
CHANGED
|
@@ -37,7 +37,6 @@ from pathlib import Path
|
|
|
37
37
|
ROOT = Path(__file__).resolve().parent.parent
|
|
38
38
|
DIST = ROOT / "dist"
|
|
39
39
|
|
|
40
|
-
# docs/internal/ 只在「内部版」允许出现
|
|
41
40
|
_INTERNAL_DOCS = "docs/internal/"
|
|
42
41
|
|
|
43
42
|
|
|
@@ -58,12 +57,10 @@ def _forbidden(path: str, allow_internal_docs: bool) -> str | None:
|
|
|
58
57
|
if p.endswith("/"):
|
|
59
58
|
return None
|
|
60
59
|
parts = p.split("/")
|
|
61
|
-
# 归档带 --prefix=Foxclaw-xxx/,故按「任意层级」匹配
|
|
62
60
|
joined = "/".join(parts[1:]) if len(parts) > 1 else p
|
|
63
61
|
|
|
64
62
|
if not allow_internal_docs and _INTERNAL_DOCS in p:
|
|
65
63
|
return "内部机密文档 docs/internal/(对外分发版必须排除)"
|
|
66
|
-
# .env / .env.bak-* / .env.local —— 但 .env.example 模板必须保留
|
|
67
64
|
base = parts[-1]
|
|
68
65
|
if base == ".env" or (base.startswith(".env.") and base != ".env.example"):
|
|
69
66
|
return "密钥配置文件"
|
|
@@ -122,7 +119,6 @@ def build(variant: str, ref: str) -> Path | None:
|
|
|
122
119
|
|
|
123
120
|
cmd = ["archive", "--format=zip", f"--prefix={prefix}", "-o", str(out), ref]
|
|
124
121
|
if variant == "public":
|
|
125
|
-
# 用 git pathspec magic 排除内部文档目录
|
|
126
122
|
cmd += ["--", ".", f":(exclude){_INTERNAL_DOCS.rstrip('/')}"]
|
|
127
123
|
|
|
128
124
|
r = git(cmd)
|
|
@@ -131,7 +127,6 @@ def build(variant: str, ref: str) -> Path | None:
|
|
|
131
127
|
out.unlink(missing_ok=True)
|
|
132
128
|
return None
|
|
133
129
|
|
|
134
|
-
# 内容校验 —— 宁可不出包,也不出错包
|
|
135
130
|
bad = check_archive(out, allow_internal_docs=(variant == "internal"))
|
|
136
131
|
if bad:
|
|
137
132
|
out.unlink(missing_ok=True)
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""复核存量冲突卡:用当前判定规则重算,把已经不成立的卡批量标记为误判。
|
|
3
|
+
|
|
4
|
+
默认 dry-run,只打印统计与样本;加 --apply 才真正调 /conflicts/bulk-dismiss。
|
|
5
|
+
写入必须走运行中的服务——只有它才持有 open 冲突集合与向量索引,直接改库会让
|
|
6
|
+
服务内的缓存与库不一致(那些因冲突被置 disputed 的 claim 也不会回到检索)。
|
|
7
|
+
|
|
8
|
+
用法:
|
|
9
|
+
python tools/recheck_conflicts.py # 只看统计
|
|
10
|
+
python tools/recheck_conflicts.py --apply # 确认后批量关闭误判
|
|
11
|
+
"""
|
|
12
|
+
import argparse
|
|
13
|
+
import json
|
|
14
|
+
import sqlite3
|
|
15
|
+
import sys
|
|
16
|
+
import urllib.request
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
|
|
19
|
+
ROOT = Path(__file__).resolve().parent.parent
|
|
20
|
+
sys.path.insert(0, str(ROOT / "vendor" / "hkc"))
|
|
21
|
+
|
|
22
|
+
from hkc_kee.kee import _SimpleEmbedder # noqa: E402
|
|
23
|
+
|
|
24
|
+
THRESHOLD = 0.30 # 与 KnowledgeEvolutionEngine.similarity_threshold 一致
|
|
25
|
+
DEFAULT_DB = ROOT / "foxclaw_data" / "hkc" / "hkc.db"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def load_open(conn):
|
|
29
|
+
cols = [r[1] for r in conn.execute("pragma table_info(conflict_cards)")]
|
|
30
|
+
idx = {c: i for i, c in enumerate(cols)}
|
|
31
|
+
rows = conn.execute(
|
|
32
|
+
"select * from conflict_cards where status='open' order by conflict_id"
|
|
33
|
+
).fetchall()
|
|
34
|
+
return [
|
|
35
|
+
{
|
|
36
|
+
"conflict_id": r[idx["conflict_id"]],
|
|
37
|
+
"a": r[idx["claim_a_id"]],
|
|
38
|
+
"b": r[idx["claim_b_id"]],
|
|
39
|
+
"domain": r[idx["domain"]],
|
|
40
|
+
}
|
|
41
|
+
for r in rows
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def load_claims(conn):
|
|
46
|
+
cols = [r[1] for r in conn.execute("pragma table_info(knowledge_units)")]
|
|
47
|
+
idx = {c: i for i, c in enumerate(cols)}
|
|
48
|
+
out = {}
|
|
49
|
+
for r in conn.execute("select * from knowledge_units"):
|
|
50
|
+
if r[idx["ku_type"]] != "Claim":
|
|
51
|
+
continue
|
|
52
|
+
try:
|
|
53
|
+
extra = json.loads(r[idx["extra"]] or "{}")
|
|
54
|
+
except Exception:
|
|
55
|
+
extra = {}
|
|
56
|
+
out[r[idx["ku_id"]]] = {
|
|
57
|
+
"text": (extra.get("statement") or r[idx["name"]] or "").strip(),
|
|
58
|
+
"claim_status": extra.get("claim_status", "active"),
|
|
59
|
+
}
|
|
60
|
+
return out
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def classify(cards, claims, emb):
|
|
64
|
+
buckets = {"claim_missing": [], "not_contradiction": [], "still_contradiction": []}
|
|
65
|
+
for card in cards:
|
|
66
|
+
ca, cb = claims.get(card["a"]), claims.get(card["b"])
|
|
67
|
+
if not ca or not cb or not ca["text"] or not cb["text"]:
|
|
68
|
+
buckets["claim_missing"].append(card)
|
|
69
|
+
continue
|
|
70
|
+
score = emb.score(ca["text"], cb["text"])
|
|
71
|
+
card["score"] = round(score, 3)
|
|
72
|
+
card["a_text"], card["b_text"] = ca["text"][:70], cb["text"][:70]
|
|
73
|
+
key = "still_contradiction" if score < -THRESHOLD else "not_contradiction"
|
|
74
|
+
buckets[key].append(card)
|
|
75
|
+
return buckets
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def post(base_url, path, payload, timeout=300):
|
|
79
|
+
req = urllib.request.Request(
|
|
80
|
+
base_url.rstrip("/") + path,
|
|
81
|
+
data=json.dumps(payload).encode("utf-8"),
|
|
82
|
+
headers={"Content-Type": "application/json"},
|
|
83
|
+
method="POST",
|
|
84
|
+
)
|
|
85
|
+
with urllib.request.urlopen(req, timeout=timeout) as resp:
|
|
86
|
+
return json.load(resp)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def main(argv=None):
|
|
90
|
+
ap = argparse.ArgumentParser(description="复核并批量关闭误判冲突卡")
|
|
91
|
+
ap.add_argument("--db", default=str(DEFAULT_DB), help="HKC sqlite 路径(只读)")
|
|
92
|
+
ap.add_argument("--base-url", default="http://127.0.0.1:8000",
|
|
93
|
+
help="运行中的服务地址(--apply 时用)")
|
|
94
|
+
ap.add_argument("--apply", action="store_true", help="真正提交,默认只统计")
|
|
95
|
+
ap.add_argument("--chunk", type=int, default=200, help="每次批量提交的张数")
|
|
96
|
+
ap.add_argument("--sample", type=int, default=5, help="每类打印的样本数")
|
|
97
|
+
ap.add_argument("--json", action="store_true", help="输出 JSON 摘要")
|
|
98
|
+
args = ap.parse_args(argv)
|
|
99
|
+
|
|
100
|
+
db = Path(args.db)
|
|
101
|
+
if not db.exists():
|
|
102
|
+
print(f"找不到库文件:{db}", file=sys.stderr)
|
|
103
|
+
return 2
|
|
104
|
+
|
|
105
|
+
conn = sqlite3.connect(f"file:{db.as_posix()}?mode=ro", uri=True)
|
|
106
|
+
cards = load_open(conn)
|
|
107
|
+
claims = load_claims(conn)
|
|
108
|
+
if not cards:
|
|
109
|
+
print("没有 open 冲突卡,无需处理")
|
|
110
|
+
return 0
|
|
111
|
+
|
|
112
|
+
buckets = classify(cards, claims, _SimpleEmbedder())
|
|
113
|
+
closed = buckets["not_contradiction"] + buckets["claim_missing"]
|
|
114
|
+
|
|
115
|
+
summary = {
|
|
116
|
+
"open_total": len(cards),
|
|
117
|
+
"not_contradiction": len(buckets["not_contradiction"]),
|
|
118
|
+
"claim_missing": len(buckets["claim_missing"]),
|
|
119
|
+
"still_contradiction": len(buckets["still_contradiction"]),
|
|
120
|
+
"to_close": len(closed),
|
|
121
|
+
}
|
|
122
|
+
if args.json:
|
|
123
|
+
print(json.dumps(summary, ensure_ascii=False, indent=2))
|
|
124
|
+
else:
|
|
125
|
+
print(f"open 冲突卡 {summary['open_total']}")
|
|
126
|
+
print(f" 重算后不再矛盾 {summary['not_contradiction']} ← 可关闭")
|
|
127
|
+
print(f" claim 已不存在 {summary['claim_missing']} ← 可关闭")
|
|
128
|
+
print(f" 仍然矛盾 {summary['still_contradiction']} ← 留给人工裁决")
|
|
129
|
+
|
|
130
|
+
if args.sample:
|
|
131
|
+
print("\n仍然矛盾的样本(需人工看):")
|
|
132
|
+
for c in buckets["still_contradiction"][:args.sample]:
|
|
133
|
+
print(f" {c['conflict_id']} score={c['score']}")
|
|
134
|
+
print(f" A: {c['a_text']}")
|
|
135
|
+
print(f" B: {c['b_text']}")
|
|
136
|
+
|
|
137
|
+
if not args.apply:
|
|
138
|
+
if closed:
|
|
139
|
+
print(f"\ndry-run,未提交。确认后加 --apply 关闭 {len(closed)} 张。")
|
|
140
|
+
return 0
|
|
141
|
+
|
|
142
|
+
ids = [c["conflict_id"] for c in closed]
|
|
143
|
+
done = 0
|
|
144
|
+
for i in range(0, len(ids), args.chunk):
|
|
145
|
+
batch = ids[i:i + args.chunk]
|
|
146
|
+
res = post(args.base_url, "/conflicts/bulk-dismiss",
|
|
147
|
+
{"conflict_ids": batch, "resolved_by": "recheck",
|
|
148
|
+
"note": "复核误判:两个观点并非同一命题的正反两面"})
|
|
149
|
+
done += int(res.get("dismissed", 0))
|
|
150
|
+
print(f" 已关闭 {done}/{len(ids)}")
|
|
151
|
+
print(f"完成:提交 {len(ids)} 张,实际关闭 {done} 张")
|
|
152
|
+
return 0
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
if __name__ == "__main__":
|
|
156
|
+
sys.exit(main())
|
|
@@ -10,10 +10,11 @@ PyPI / npm 的版本号不可撤回:上传后只能 yank(仍占号、仍可被 p
|
|
|
10
10
|
|
|
11
11
|
两侧产物性质不同(别把"发布成功"当"装完能用")
|
|
12
12
|
------------------------------------------
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
后端是同一份代码:`hmr` 与 `hkc_*` 经 `packages.find` 的 `where=vendor` 被提升成
|
|
14
|
+
顶层包,已经真的在 wheel 里(0.1.2 起就是),pip 装完知识/记忆子系统完好。
|
|
15
|
+
**真正缺的是前端 UI** —— `frontend/` 在仓库根、不属于任何包,构建前必须先跑
|
|
16
|
+
`tools/bundle_web.py` 把它复制成 `foxclaw/web/`。漏跑则 wheel 里没有任何
|
|
17
|
+
html/js,`foxclaw serve` 首页 404,所以这里对"缺 foxclaw/web"判 **error**。
|
|
17
18
|
|
|
18
19
|
用法
|
|
19
20
|
----
|
|
@@ -41,18 +42,15 @@ ROOT = Path(__file__).resolve().parent.parent
|
|
|
41
42
|
class Issue:
|
|
42
43
|
"""一条结果。level=error 导致非零退出;warn 只提示。"""
|
|
43
44
|
|
|
44
|
-
level: str
|
|
45
|
+
level: str
|
|
45
46
|
code: str
|
|
46
47
|
msg: str
|
|
47
48
|
|
|
48
49
|
def __str__(self) -> str:
|
|
49
|
-
# 只用 ASCII 记号:Windows 默认控制台是 GBK,✓/✗/⚠ 会抛 UnicodeEncodeError
|
|
50
50
|
mark = "ERROR" if self.level == "error" else " WARN"
|
|
51
51
|
return f" [{mark}] {self.code}: {self.msg}"
|
|
52
52
|
|
|
53
53
|
|
|
54
|
-
# ── 版本来源(与 tests/test_version_sync.py 同一份事实)────────────
|
|
55
|
-
|
|
56
54
|
def _dunder_version(root: Path) -> str:
|
|
57
55
|
txt = (root / "foxclaw" / "__init__.py").read_text(encoding="utf-8")
|
|
58
56
|
m = re.search(r'^__version__\s*=\s*["\']([^"\']+)["\']', txt, re.M)
|
|
@@ -74,6 +72,10 @@ def _npm_package(root: Path) -> dict:
|
|
|
74
72
|
return json.loads((root / "npm" / "package.json").read_text(encoding="utf-8"))
|
|
75
73
|
|
|
76
74
|
|
|
75
|
+
def _frontend_package(root: Path) -> dict:
|
|
76
|
+
return json.loads((root / "frontend_src" / "package.json").read_text(encoding="utf-8"))
|
|
77
|
+
|
|
78
|
+
|
|
77
79
|
def project_name(root: Path = ROOT) -> str:
|
|
78
80
|
try:
|
|
79
81
|
return _project(root).get("name", "")
|
|
@@ -82,7 +84,7 @@ def project_name(root: Path = ROOT) -> str:
|
|
|
82
84
|
|
|
83
85
|
|
|
84
86
|
def collect_versions(root: Path = ROOT) -> dict:
|
|
85
|
-
"""
|
|
87
|
+
"""五处版本号;读不到留空串,交给 check_versions 报 unreadable。"""
|
|
86
88
|
def safe(fn, *a):
|
|
87
89
|
try:
|
|
88
90
|
return fn(*a) or ""
|
|
@@ -94,6 +96,7 @@ def collect_versions(root: Path = ROOT) -> dict:
|
|
|
94
96
|
"foxclaw/__init__.py:__version__": safe(_dunder_version, root),
|
|
95
97
|
"npm/package.json:version": safe(lambda: _npm_package(root)["version"]),
|
|
96
98
|
"npm/index.js:export const version": safe(_npm_index_version, root),
|
|
99
|
+
"frontend_src/package.json:version": safe(lambda: _frontend_package(root)["version"]),
|
|
97
100
|
}
|
|
98
101
|
|
|
99
102
|
|
|
@@ -158,8 +161,6 @@ def check_tag(tag: str, version: str) -> list:
|
|
|
158
161
|
return []
|
|
159
162
|
|
|
160
163
|
|
|
161
|
-
# ── 归档内容 ──────────────────────────────────────────────
|
|
162
|
-
|
|
163
164
|
def forbidden_entry(entry: str):
|
|
164
165
|
"""归档内条目是否绝对不该出现;返回原因,干净则 None。"""
|
|
165
166
|
p = entry.replace("\\", "/")
|
|
@@ -278,10 +279,18 @@ def check_dist(dist: Path, name: str, version: str) -> list:
|
|
|
278
279
|
issues.append(Issue("error", "missing_entry", f"{sdist.name} 里没有 foxclaw/cli.py"))
|
|
279
280
|
|
|
280
281
|
issues += _scan_forbidden(whl) + _scan_forbidden(sdist)
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
282
|
+
# HMR/HKC 在 wheel 里被"提升"成顶层包(hmr/、hkc_ace/…),没有 vendor/ 目录;
|
|
283
|
+
# 判据看顶层包,不看 vendor/ 路径,否则会把完好的 wheel 误判成占位包。
|
|
284
|
+
if not any(e.startswith("hmr/") for e in entries):
|
|
285
|
+
issues.append(Issue("error", "missing_hmr",
|
|
286
|
+
f"{whl.name} 里没有 hmr/ → 记忆子系统会降级;检查 packages.find"))
|
|
287
|
+
if not any(e.startswith("hkc_") for e in entries):
|
|
288
|
+
issues.append(Issue("error", "missing_hkc",
|
|
289
|
+
f"{whl.name} 里没有 hkc_* → 知识子系统会降级;检查 packages.find"))
|
|
290
|
+
if "foxclaw/web/v3/index.html" not in entries:
|
|
291
|
+
issues.append(Issue("error", "missing_web_ui",
|
|
292
|
+
f"{whl.name} 缺 foxclaw/web/v3/index.html → pip 装完首页 404;"
|
|
293
|
+
f"构建前先跑 python tools/bundle_web.py"))
|
|
285
294
|
return issues
|
|
286
295
|
|
|
287
296
|
|
|
@@ -1,16 +1,15 @@
|
|
|
1
|
-
# tools/restart_foxclaw.ps1
|
|
2
|
-
# 杀旧进程 → 后台启新(带 --reload 自动加载源码修改) → 等 /stats 可达
|
|
3
|
-
# 用法: powershell -NoProfile -File tools\restart_foxclaw.ps1
|
|
4
1
|
$ErrorActionPreference = "Continue"
|
|
5
2
|
|
|
6
3
|
$root = Split-Path -Parent $PSScriptRoot
|
|
7
4
|
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
$py = Join-Path $root ".venv\Scripts\python.exe"
|
|
6
|
+
if (-not (Test-Path $py)) {
|
|
7
|
+
Write-Host "[1] WARN: .venv missing, fallback to PATH python (see README quick start)"
|
|
8
|
+
$py = (Get-Command python -ErrorAction SilentlyContinue).Source
|
|
9
|
+
}
|
|
10
10
|
if (-not $py) { $py = "python" }
|
|
11
11
|
Write-Host "[1] python = $py"
|
|
12
12
|
|
|
13
|
-
# 2. 找 LISTENING on :8000
|
|
14
13
|
Write-Host "[2] Find LISTENING on :8000"
|
|
15
14
|
$targetPid = $null
|
|
16
15
|
$netstatLines = netstat -ano | Select-String ":8000"
|
|
@@ -30,7 +29,6 @@ if ($targetPid) {
|
|
|
30
29
|
Write-Host " -> No LISTEN on :8000 (may already be down)"
|
|
31
30
|
}
|
|
32
31
|
|
|
33
|
-
# 3. 启服务(后台,隐藏窗口,带 --reload + 日志 redirect)
|
|
34
32
|
$logDir = Join-Path $root "foxclaw_data"
|
|
35
33
|
if (-not (Test-Path $logDir)) { New-Item -ItemType Directory -Path $logDir | Out-Null }
|
|
36
34
|
$outLog = Join-Path $logDir "uvicorn.log"
|
|
@@ -53,7 +51,6 @@ Start-Process $py -ArgumentList $uviArgs `
|
|
|
53
51
|
-RedirectStandardError $errLog | Out-Null
|
|
54
52
|
Write-Host " -> Started, polling /stats up to 30s"
|
|
55
53
|
|
|
56
|
-
# 4. 轮询 /stats 等就绪
|
|
57
54
|
$ready = $false
|
|
58
55
|
for ($i=0; $i -lt 15; $i++) {
|
|
59
56
|
try {
|
|
@@ -35,10 +35,9 @@ from pathlib import Path
|
|
|
35
35
|
|
|
36
36
|
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
|
37
37
|
|
|
38
|
-
# 走 stub embedding:审计判定与向量无关,只为把启动时间压下来。
|
|
39
38
|
os.environ.setdefault("HKC_EMBEDDING", "stub")
|
|
40
39
|
|
|
41
|
-
for _s in (sys.stdout, sys.stderr):
|
|
40
|
+
for _s in (sys.stdout, sys.stderr):
|
|
42
41
|
try:
|
|
43
42
|
_s.reconfigure(encoding="utf-8")
|
|
44
43
|
except Exception:
|
|
@@ -57,11 +56,6 @@ def check(cond: bool, msg: str) -> bool:
|
|
|
57
56
|
return bool(cond)
|
|
58
57
|
|
|
59
58
|
|
|
60
|
-
# ── 8 处真实共享写入点(参数照抄调用点,勿凭记忆改写) ────────────────────
|
|
61
|
-
# 用于「零误伤」验收:这些在 enforce 下都必须放行,否则就是策略表错了。
|
|
62
|
-
# foxclaw/agents/agents.py:269 / :390 / :468 / :509 / :594 / :631 (主管,6 处)
|
|
63
|
-
# foxclaw/agents/base.py:779 (偏好,各 Agent)
|
|
64
|
-
# foxclaw/core/truth_pipeline.py:86 (真值,写入方 system)
|
|
65
59
|
_WHO_ANY = ("supervisor", "finance", "office", "admin", "assistant")
|
|
66
60
|
LEGITIMATE = [
|
|
67
61
|
("supervisor", "decision", ["dispatch", "multi_step", "to:finance"], "多步记录", "主管多步分发"),
|
|
@@ -74,8 +68,6 @@ LEGITIMATE = [
|
|
|
74
68
|
("system", "execution", ["truth_pipeline"], "真值", "真值管线沉淀"),
|
|
75
69
|
]
|
|
76
70
|
|
|
77
|
-
# 越界探针:模拟「一条 Agent 把指令写进共享层,让别的 Agent 下一轮召回时读到」。
|
|
78
|
-
# 这是绕开守门人的跨 Agent 注入通道,也是 P0-3 要拦的东西。
|
|
79
71
|
INTRUSIONS = [
|
|
80
72
|
("office", "decision", ["finance.transfer"], "财务应给办公转账1000元", "办公→财务职责域"),
|
|
81
73
|
("office", "decision", ["itinerary.plan"], "把周三会议改到周五", "办公→行政职责域"),
|
|
@@ -133,7 +125,6 @@ def main() -> int:
|
|
|
133
125
|
from foxclaw import Foxclaw
|
|
134
126
|
osi = Foxclaw(data_dir=data_dir, shadow_rounds=5)
|
|
135
127
|
|
|
136
|
-
# ── 1. 装配自检:闸门真的接上了吗?没挂上则后面全是在测空气 ──
|
|
137
128
|
print("\n【1】装配自检")
|
|
138
129
|
pol = getattr(osi.hmr, "shared_write_policy", None)
|
|
139
130
|
spine = getattr(osi.hmr, "shared_write_spine", None)
|
|
@@ -147,22 +138,18 @@ def main() -> int:
|
|
|
147
138
|
print("\n装配不完整,后续验收无意义。中止。")
|
|
148
139
|
return 1
|
|
149
140
|
|
|
150
|
-
# ── 2. 零误伤:8 处真实写入必须全部放行 ──
|
|
151
141
|
print("\n【2】真实写入点零误伤(8 处调用点的原样参数)")
|
|
152
142
|
for agent_id, mem_type, tags, title, why in LEGITIMATE:
|
|
153
143
|
if agent_id == "__ANY__":
|
|
154
|
-
# 偏好固化发生在基类,任一 Agent 都可能触发 —— 全部都得放行
|
|
155
144
|
for who in _WHO_ANY:
|
|
156
145
|
audit_case(osi.hmr, who, mem_type, tags, f"{title}:示例内容", True)
|
|
157
146
|
continue
|
|
158
147
|
audit_case(osi.hmr, agent_id, mem_type, tags, f"{title}:示例内容", True)
|
|
159
148
|
|
|
160
|
-
# ── 3. 越界探针:应全部被判 False ──
|
|
161
149
|
print("\n【3】越界探针(跨 Agent 注入通道)")
|
|
162
150
|
for agent_id, mem_type, tags, content, why in INTRUSIONS:
|
|
163
151
|
audit_case(osi.hmr, agent_id, mem_type, tags, content, False)
|
|
164
152
|
|
|
165
|
-
# ── 4. 判据汇总(不依赖落库,直接读脊柱 history) ──
|
|
166
153
|
print("\n【4】审计事件汇总(等价于 /api/bus/history?channel=memory.shared.write)")
|
|
167
154
|
evs = osi.event_spine.history("memory.shared.write", 500)
|
|
168
155
|
allowed_n = sum(1 for e in evs if e["payload"]["allowed"])
|
|
@@ -175,7 +162,6 @@ def main() -> int:
|
|
|
175
162
|
f"sha={p['content_sha']} {p['reason']}")
|
|
176
163
|
check(len(evs) > 0, "审计事件确实产生(链路口径通)")
|
|
177
164
|
|
|
178
|
-
# ── 5. 结论 ──
|
|
179
165
|
print("\n" + "=" * 66)
|
|
180
166
|
if _FAILS:
|
|
181
167
|
print(f"✗ 验收未通过:{len(_FAILS)}/{_TOTAL} 项偏离预期")
|