claude-smart 0.2.45 → 0.2.47
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/.claude-plugin/marketplace.json +3 -3
- package/README.md +47 -10
- package/bin/claude-smart.js +535 -23
- package/package.json +22 -3
- package/plugin/.claude-plugin/plugin.json +4 -2
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/README.md +27 -6
- package/plugin/commands/clear-all.md +1 -0
- package/plugin/commands/dashboard.md +1 -0
- package/plugin/commands/learn.md +1 -0
- package/plugin/commands/restart.md +1 -0
- package/plugin/commands/show.md +1 -0
- package/plugin/dashboard/app/configure/env/page.tsx +40 -14
- package/plugin/dashboard/app/configure/server/page.tsx +51 -1
- package/plugin/dashboard/app/layout.tsx +20 -0
- package/plugin/dashboard/app/preferences/[id]/page.tsx +8 -8
- package/plugin/dashboard/app/skills/project/[id]/page.tsx +7 -3
- package/plugin/dashboard/app/skills/shared/[id]/page.tsx +10 -3
- package/plugin/dashboard/lib/claude-settings-file.ts +20 -10
- package/plugin/dashboard/lib/reflexio-client.ts +16 -0
- package/plugin/dashboard/lib/status.ts +10 -3
- package/plugin/dashboard/lib/types.ts +18 -6
- package/plugin/hooks/codex-hooks.json +0 -1
- package/plugin/opencode/assistant-buffer.ts +108 -0
- package/plugin/opencode/dist/assistant-buffer.js +96 -0
- package/plugin/opencode/dist/internal.js +12 -0
- package/plugin/opencode/dist/payload.js +85 -0
- package/plugin/opencode/dist/server.mjs +232 -0
- package/plugin/opencode/internal.ts +18 -0
- package/plugin/opencode/package.json +3 -0
- package/plugin/opencode/payload.ts +90 -0
- package/plugin/opencode/server.mts +251 -0
- package/plugin/opencode/tsconfig.json +13 -0
- package/plugin/pyproject.toml +25 -4
- package/plugin/scripts/_lib.sh +5 -1
- package/plugin/scripts/backend-service.sh +22 -2
- package/plugin/scripts/ensure-plugin-root.sh +4 -4
- package/plugin/scripts/hook_entry.sh +1 -1
- package/plugin/scripts/opencode-claude-compat +4 -0
- package/plugin/scripts/opencode-claude-compat.cmd +3 -0
- package/plugin/scripts/opencode-claude-compat.js +225 -0
- package/plugin/scripts/smart-install.sh +17 -9
- package/plugin/src/README.md +1 -1
- package/plugin/src/claude_smart/cli.py +498 -12
- package/plugin/src/claude_smart/context_format.py +9 -9
- package/plugin/src/claude_smart/cs_cite.py +66 -19
- package/plugin/src/claude_smart/env_config.py +12 -4
- package/plugin/src/claude_smart/events/session_start.py +26 -7
- package/plugin/src/claude_smart/events/stop.py +1 -1
- package/plugin/src/claude_smart/ids.py +1 -1
- package/plugin/src/claude_smart/reflexio_adapter.py +5 -5
- package/plugin/src/claude_smart/runtime.py +7 -1
- package/plugin/uv.lock +5 -5
- package/plugin/vendor/reflexio/.env.example +54 -30
- package/plugin/vendor/reflexio/README.md +17 -11
- package/plugin/vendor/reflexio/pyproject.toml +13 -1
- package/plugin/vendor/reflexio/reflexio/README.md +4 -1
- package/plugin/vendor/reflexio/reflexio/cli/README.md +9 -7
- package/plugin/vendor/reflexio/reflexio/cli/__main__.py +10 -1
- package/plugin/vendor/reflexio/reflexio/cli/bootstrap_config.py +22 -5
- package/plugin/vendor/reflexio/reflexio/cli/commands/interactions.py +34 -4
- package/plugin/vendor/reflexio/reflexio/cli/commands/profiles.py +12 -7
- package/plugin/vendor/reflexio/reflexio/cli/commands/services.py +13 -6
- package/plugin/vendor/reflexio/reflexio/cli/commands/setup_cmd.py +212 -52
- package/plugin/vendor/reflexio/reflexio/cli/commands/shortcuts.py +15 -2
- package/plugin/vendor/reflexio/reflexio/cli/env_loader.py +292 -22
- package/plugin/vendor/reflexio/reflexio/cli/log_format.py +29 -4
- package/plugin/vendor/reflexio/reflexio/cli/run_services.py +17 -8
- package/plugin/vendor/reflexio/reflexio/cli/stop_services.py +13 -10
- package/plugin/vendor/reflexio/reflexio/cli/utils.py +44 -1
- package/plugin/vendor/reflexio/reflexio/client/client.py +154 -13
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/README.md +135 -257
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/npm/openclaw-smart/bin/openclaw-smart.js +13 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/npm/openclaw-smart/package.json +15 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/README.md +38 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/index.ts +151 -110
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/openclaw.plugin.json +20 -10
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/package.json +28 -6
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/pyproject.toml +42 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/_lib.sh +371 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/backend-log-runner.sh +33 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/backend-service.sh +271 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/cli.sh +68 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/dashboard-open.sh +15 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/ensure-plugin-root.sh +84 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/hook_entry.sh +106 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/npm-cli.js +219 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/smart-install.sh +269 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/clear-all/SKILL.md +8 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/dashboard/SKILL.md +8 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/learn/SKILL.md +10 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/reflexio/SKILL.md +15 -44
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/restart/SKILL.md +6 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/show/SKILL.md +8 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/cli.py +634 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/context_format.py +224 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/context_inject.py +79 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/after_tool_call.py +160 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/agent_end.py +187 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/before_prompt_build.py +69 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/before_tool_call.py +30 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/session_end.py +36 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/session_start.py +130 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/hook.py +131 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/ids.py +94 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/internal_call.py +75 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/oc_cite.py +196 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/optimizer_assistant.py +272 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/publish.py +96 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/query_compose.py +66 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/reflexio_adapter.py +336 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/runtime.py +47 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/stall_banner.py +61 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/state.py +323 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/integration/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/integration/test_e2e_session_loop.py +190 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/integration/test_publish_to_local_reflexio_integration.py +112 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/integration/test_recursion_guard_integration.py +86 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/integration/test_search_inject_integration.py +144 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_cli.py +184 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_after_tool_call.py +142 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_agent_end.py +233 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_before_prompt_build.py +116 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_before_tool_call.py +35 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_session_end.py +47 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_session_start.py +109 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_hook_dispatch.py +117 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_ids.py +41 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_internal_call.py +50 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_oc_cite.py +88 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_publish.py +48 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_query_compose.py +48 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_reflexio_adapter.py +188 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_runtime.py +41 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_state.py +235 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests-ts/__mocks__/plugin-entry-stub.ts +6 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests-ts/test_npm_cli.test.ts +56 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests-ts/test_shim_dispatch.test.ts +171 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tsconfig.build.json +18 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/{tsconfig.json → plugin/tsconfig.json} +2 -2
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/uv.lock +3835 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/vitest.config.ts +18 -0
- package/plugin/vendor/reflexio/reflexio/lib/_agent_playbook.py +246 -10
- package/plugin/vendor/reflexio/reflexio/lib/_base.py +15 -0
- package/plugin/vendor/reflexio/reflexio/lib/_config.py +11 -4
- package/plugin/vendor/reflexio/reflexio/lib/_generation.py +17 -5
- package/plugin/vendor/reflexio/reflexio/lib/_lineage_parity_readers.py +187 -0
- package/plugin/vendor/reflexio/reflexio/lib/_profiles.py +224 -21
- package/plugin/vendor/reflexio/reflexio/lib/_reflection.py +1 -1
- package/plugin/vendor/reflexio/reflexio/lib/_search.py +52 -25
- package/plugin/vendor/reflexio/reflexio/lib/_user_playbook.py +11 -1
- package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/__init__.py +1 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/entities.py +127 -38
- package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/enums.py +4 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/governance.py +117 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/eval_overview_schema.py +64 -41
- package/plugin/vendor/reflexio/reflexio/models/api_schema/internal_schema.py +19 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/pending_tool_call_schema.py +12 -5
- package/plugin/vendor/reflexio/reflexio/models/api_schema/retriever_schema.py +97 -3
- package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/converters.py +5 -1
- package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/entities.py +5 -1
- package/plugin/vendor/reflexio/reflexio/models/config_schema.py +104 -7
- package/plugin/vendor/reflexio/reflexio/models/structured_output.py +148 -0
- package/plugin/vendor/reflexio/reflexio/server/OVERVIEW.md +3 -3
- package/plugin/vendor/reflexio/reflexio/server/README.md +58 -33
- package/plugin/vendor/reflexio/reflexio/server/__init__.py +29 -5
- package/plugin/vendor/reflexio/reflexio/server/__main__.py +2 -2
- package/plugin/vendor/reflexio/reflexio/server/api.py +899 -103
- package/plugin/vendor/reflexio/reflexio/server/api_endpoints/precondition_checks.py +7 -0
- package/plugin/vendor/reflexio/reflexio/server/api_endpoints/request_context.py +4 -2
- package/plugin/vendor/reflexio/reflexio/server/auth.py +90 -0
- package/plugin/vendor/reflexio/reflexio/server/billing_meter.py +173 -0
- package/plugin/vendor/reflexio/reflexio/server/billing_signals.py +64 -0
- package/plugin/vendor/reflexio/reflexio/server/cache/reflexio_cache.py +46 -16
- package/plugin/vendor/reflexio/reflexio/server/env_utils.py +65 -0
- package/plugin/vendor/reflexio/reflexio/server/extensions.py +213 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/embedding_service.py +19 -4
- package/plugin/vendor/reflexio/reflexio/server/llm/litellm_client.py +264 -77
- package/plugin/vendor/reflexio/reflexio/server/llm/llm_utils.py +69 -7
- package/plugin/vendor/reflexio/reflexio/server/llm/model_defaults.py +4 -4
- package/plugin/vendor/reflexio/reflexio/server/llm/providers/claude_code_provider.py +1 -1
- package/plugin/vendor/reflexio/reflexio/server/llm/providers/embedding_service_provider.py +75 -16
- package/plugin/vendor/reflexio/reflexio/server/llm/providers/openclaw_provider.py +280 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/rerank/cross_encoder_reranker.py +12 -1
- package/plugin/vendor/reflexio/reflexio/server/llm/token_accounting.py +48 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/tools.py +107 -17
- package/plugin/vendor/reflexio/reflexio/server/operation_limiter.py +360 -4
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.6.0.prompt.md +1 -1
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.7.0.prompt.md +85 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_aggregation/v2.2.0.prompt.md +1 -1
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_aggregation/v2.3.0.prompt.md +253 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.3.2.prompt.md +1 -1
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.3.3.prompt.md +74 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.2.3.prompt.md +1 -1
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.3.0.prompt.md +266 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.4.0.prompt.md +278 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.3.0.prompt.md +1 -1
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.4.0.prompt.md +137 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.2.0.prompt.md +1 -1
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.3.0.prompt.md +34 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.0.0.prompt.md +5 -12
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.1.0.prompt.md +5 -12
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.2.0.prompt.md +155 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/tagging/v1.0.0.prompt.md +23 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_manager.py +89 -32
- package/plugin/vendor/reflexio/reflexio/server/services/README.md +28 -11
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/README.md +22 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/__init__.py +1 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_constants.py +4 -2
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_utils.py +1 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/components/__init__.py +7 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/{agent_success_evaluator.py → components/evaluator.py} +3 -16
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/regen_jobs.py +28 -72
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/{group_evaluation_runner.py → runner.py} +12 -10
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/{delayed_group_evaluator.py → scheduler.py} +10 -2
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/{agent_success_evaluation_service.py → service.py} +36 -1
- package/plugin/vendor/reflexio/reflexio/server/services/base_generation_service.py +160 -0
- package/plugin/vendor/reflexio/reflexio/server/services/configurator/base_configurator.py +5 -0
- package/plugin/vendor/reflexio/reflexio/server/services/deduplication_utils.py +1 -1
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/README.md +9 -0
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/components/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/{rule_attribution.py → components/rule_attribution.py} +10 -9
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/eval_sampler.py +15 -24
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/service.py +238 -164
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/README.md +31 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/outcome.py +13 -3
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/pending_tool_call_dispatch.py +3 -2
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/resumable_agent.py +32 -118
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_worker.py +47 -16
- package/plugin/vendor/reflexio/reflexio/server/services/generation_service.py +105 -23
- package/plugin/vendor/reflexio/reflexio/server/services/governance/config.py +52 -0
- package/plugin/vendor/reflexio/reflexio/server/services/governance/service.py +378 -0
- package/plugin/vendor/reflexio/reflexio/server/services/governance/subject_refs.py +34 -0
- package/plugin/vendor/reflexio/reflexio/server/services/lineage/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/server/services/lineage/gc_scheduler.py +219 -0
- package/plugin/vendor/reflexio/reflexio/server/services/lineage/resolve.py +98 -0
- package/plugin/vendor/reflexio/reflexio/server/services/operation_state_utils.py +26 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/README.md +18 -11
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/aggregation_prompt_processing.py +100 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/__init__.py +5 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/{playbook_aggregator.py → components/aggregator.py} +334 -178
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/{playbook_consolidator.py → components/consolidator.py} +168 -62
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/{playbook_extractor.py → components/extractor.py} +5 -1
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_edit_apply.py +74 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_service_utils.py +33 -3
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/{playbook_generation_service.py → service.py} +121 -19
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/README.md +10 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/models.py +2 -1
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/optimizer.py +178 -30
- package/plugin/vendor/reflexio/reflexio/server/services/pre_retrieval/README.md +9 -0
- package/plugin/vendor/reflexio/reflexio/server/services/profile/components/__init__.py +17 -0
- package/plugin/vendor/reflexio/reflexio/server/services/profile/{profile_deduplicator.py → components/consolidator.py} +7 -6
- package/plugin/vendor/reflexio/reflexio/server/services/profile/{profile_extractor.py → components/extractor.py} +31 -22
- package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_generation_service_utils.py +6 -13
- package/plugin/vendor/reflexio/reflexio/server/services/profile/{profile_generation_service.py → service.py} +32 -50
- package/plugin/vendor/reflexio/reflexio/server/services/reflection/__init__.py +4 -8
- package/plugin/vendor/reflexio/reflexio/server/services/reflection/components/__init__.py +7 -0
- package/plugin/vendor/reflexio/reflexio/server/services/reflection/reflection_service_utils.py +12 -1
- package/plugin/vendor/reflexio/reflexio/server/services/reflection/{reflection_service.py → service.py} +36 -16
- package/plugin/vendor/reflexio/reflexio/server/services/retrieval/recency.py +211 -0
- package/plugin/vendor/reflexio/reflexio/server/services/retrieval/relevance_floor.py +99 -6
- package/plugin/vendor/reflexio/reflexio/server/services/service_utils.py +36 -1
- package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/README.md +8 -0
- package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/outcome.py +1 -2
- package/plugin/vendor/reflexio/reflexio/server/services/storage/error.py +31 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/governance_validation.py +681 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/retention.py +9 -9
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/__init__.py +16 -2
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_agent_run.py +4 -1
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_base.py +644 -197
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_extras.py +106 -119
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_governance.py +1965 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_lineage.py +630 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_operations.py +38 -6
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_playbook.py +31 -1369
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_profiles.py +756 -210
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_requests.py +148 -34
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_shadow_verdicts.py +23 -9
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_stall_state.py +5 -2
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/__init__.py +13 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_agent.py +952 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_eval_results.py +189 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_optimization.py +247 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_source_linkage.py +145 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_user.py +838 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/__init__.py +150 -23
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_agent_run.py +2 -8
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_base.py +5 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_extras.py +40 -57
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_governance.py +148 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_lineage.py +215 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_operations.py +20 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_playbook.py +4 -677
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_profiles.py +134 -1
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_requests.py +62 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_retrieval_log.py +51 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_shadow_verdicts.py +17 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/__init__.py +13 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_agent.py +365 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_eval_results.py +124 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_optimization.py +85 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_source_linkage.py +47 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_user.py +333 -0
- package/plugin/vendor/reflexio/reflexio/server/services/tagging/README.md +8 -0
- package/plugin/vendor/reflexio/reflexio/server/services/tagging/__init__.py +1 -0
- package/plugin/vendor/reflexio/reflexio/server/services/tagging/service.py +200 -0
- package/plugin/vendor/reflexio/reflexio/server/services/tagging/tagging_scheduler.py +149 -0
- package/plugin/vendor/reflexio/reflexio/server/services/unified_search_service.py +372 -34
- package/plugin/vendor/reflexio/reflexio/server/site_var/README.md +2 -1
- package/plugin/vendor/reflexio/reflexio/server/site_var/feature_flags.py +120 -1
- package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_sources/feature_flags.json +4 -0
- package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_sources/search_settings.json +5 -0
- package/plugin/vendor/reflexio/reflexio/server/tracing.py +30 -0
- package/plugin/vendor/reflexio/reflexio/server/usage_metrics.py +18 -0
- package/plugin/vendor/reflexio/reflexio/test_support/llm_mock.py +61 -26
- package/plugin/vendor/reflexio/reflexio/test_support/llm_model_registry.py +41 -4
- package/scripts/setup-claude-smart.sh +8 -3
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/TESTING.md +0 -517
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/hook/handler.js +0 -473
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/package-lock.json +0 -2156
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/package.json +0 -18
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/hook/handler.ts +0 -241
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/hook/setup.ts +0 -140
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/publish.ts +0 -113
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/search.ts +0 -52
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/server.ts +0 -103
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/sqlite-buffer.ts +0 -156
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/user-id.ts +0 -134
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/rules/reflexio.md +0 -24
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/publish_clawhub.sh +0 -278
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/references/HOOK.md +0 -164
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/scripts/install.sh +0 -36
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/scripts/uninstall.sh +0 -35
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/publish.test.ts +0 -27
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/search.test.ts +0 -31
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/server.test.ts +0 -42
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/setup.test.ts +0 -49
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/sqlite-buffer.test.ts +0 -91
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/user-id.test.ts +0 -50
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/vitest.config.ts +0 -13
- package/plugin/vendor/reflexio/reflexio/server/_auth.py +0 -25
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/group_aggregation.py +0 -192
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/invariants.py +0 -303
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/plan.py +0 -138
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/tools.py +0 -1125
- /package/plugin/vendor/reflexio/reflexio/integrations/{__init__.py → openclaw/plugin/src/openclaw_smart/__init__.py} +0 -0
- /package/plugin/vendor/reflexio/reflexio/integrations/openclaw/{types → plugin/types}/openclaw.d.ts +0 -0
- /package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/{distribution.py → components/distribution.py} +0 -0
- /package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/{hero_state.py → components/hero_state.py} +0 -0
- /package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/{shadow_aggregation.py → components/shadow_aggregation.py} +0 -0
- /package/plugin/vendor/reflexio/reflexio/server/services/reflection/{reflection_extractor.py → components/extractor.py} +0 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"""Integration: hook silently no-ops when OPENCLAW_SMART_INTERNAL=1.
|
|
2
|
+
|
|
3
|
+
This guard prevents an infinite feedback loop where the reflexio backend's
|
|
4
|
+
own openclaw subprocess (the openclaw LLM provider) re-enters openclaw-smart
|
|
5
|
+
and republishes the extractor's system prompt back into reflexio.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import io
|
|
11
|
+
import json
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
import pytest
|
|
15
|
+
|
|
16
|
+
pytestmark = pytest.mark.integration
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@pytest.fixture
|
|
20
|
+
def isolated_state(monkeypatch, tmp_path: Path) -> Path:
|
|
21
|
+
"""Force state.py to write under tmp_path so the test never touches ~/.openclaw-smart/."""
|
|
22
|
+
state_dir = tmp_path / "sessions"
|
|
23
|
+
monkeypatch.setenv("OPENCLAW_SMART_STATE_DIR", str(state_dir))
|
|
24
|
+
return state_dir
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_hook_short_circuits_when_internal_env_set(
|
|
28
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
29
|
+
capsys: pytest.CaptureFixture[str],
|
|
30
|
+
isolated_state: Path,
|
|
31
|
+
) -> None:
|
|
32
|
+
"""With OPENCLAW_SMART_INTERNAL=1, every handler is bypassed and stdout is empty."""
|
|
33
|
+
monkeypatch.setenv("OPENCLAW_SMART_INTERNAL", "1")
|
|
34
|
+
|
|
35
|
+
from openclaw_smart import hook
|
|
36
|
+
|
|
37
|
+
payload = {
|
|
38
|
+
"event": {"prompt": "anything"},
|
|
39
|
+
"ctx": {"sessionKey": "internal-1", "agentId": "a"},
|
|
40
|
+
}
|
|
41
|
+
monkeypatch.setattr("sys.stdin", io.StringIO(json.dumps(payload)))
|
|
42
|
+
|
|
43
|
+
rc = hook.main(["openclaw", "before-prompt-build"])
|
|
44
|
+
assert rc == 0
|
|
45
|
+
|
|
46
|
+
# No session JSONL should have been created — the handler bailed before
|
|
47
|
+
# ever touching state.append().
|
|
48
|
+
assert not isolated_state.exists() or not list(isolated_state.glob("*.jsonl"))
|
|
49
|
+
|
|
50
|
+
out = capsys.readouterr().out
|
|
51
|
+
# The TS shim treats empty stdout as "no return value" per the design.
|
|
52
|
+
assert out.strip() == ""
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def test_hook_short_circuits_when_workspace_inside_reflexio(
|
|
56
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
57
|
+
capsys: pytest.CaptureFixture[str],
|
|
58
|
+
isolated_state: Path,
|
|
59
|
+
tmp_path: Path,
|
|
60
|
+
) -> None:
|
|
61
|
+
"""A workspaceDir inside the reflexio install dir is also treated as internal.
|
|
62
|
+
|
|
63
|
+
The reflexio backend cwd's into its own checkout when it spawns the openclaw
|
|
64
|
+
subprocess for the LLM provider. internal_call.is_internal_invocation honours
|
|
65
|
+
this so the recursion guard fires even if the env var got stripped.
|
|
66
|
+
"""
|
|
67
|
+
import reflexio
|
|
68
|
+
|
|
69
|
+
reflexio_pkg_dir = Path(reflexio.__file__).resolve().parent
|
|
70
|
+
payload = {
|
|
71
|
+
"event": {"prompt": "anything"},
|
|
72
|
+
"ctx": {
|
|
73
|
+
"sessionKey": "internal-2",
|
|
74
|
+
"agentId": "a",
|
|
75
|
+
"workspaceDir": str(reflexio_pkg_dir / "sub"),
|
|
76
|
+
},
|
|
77
|
+
}
|
|
78
|
+
monkeypatch.setattr("sys.stdin", io.StringIO(json.dumps(payload)))
|
|
79
|
+
|
|
80
|
+
from openclaw_smart import hook
|
|
81
|
+
|
|
82
|
+
rc = hook.main(["openclaw", "before-prompt-build"])
|
|
83
|
+
assert rc == 0
|
|
84
|
+
out = capsys.readouterr().out
|
|
85
|
+
assert out.strip() == ""
|
|
86
|
+
assert not isolated_state.exists() or not list(isolated_state.glob("*.jsonl"))
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"""Integration: search reflexio, render the hits, assert the prependContext envelope.
|
|
2
|
+
|
|
3
|
+
Two flavours of coverage:
|
|
4
|
+
|
|
5
|
+
* **live-backend path** — when reflexio is reachable at ``REFLEXIO_URL``, run
|
|
6
|
+
``context_inject.emit_context`` end-to-end and assert the expected envelope
|
|
7
|
+
shape. Skipped automatically when the backend isn't running.
|
|
8
|
+
* **stub path** — replace ``Adapter.search_all`` with a fixed return value so
|
|
9
|
+
the rendering + envelope-emit logic is exercised regardless of environment.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import json
|
|
15
|
+
import os
|
|
16
|
+
import sys
|
|
17
|
+
from io import StringIO
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
from unittest.mock import MagicMock
|
|
20
|
+
|
|
21
|
+
import pytest
|
|
22
|
+
|
|
23
|
+
pytestmark = pytest.mark.integration
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _reflexio_url() -> str:
|
|
27
|
+
return os.environ.get("REFLEXIO_URL", "http://localhost:8061/")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _backend_alive(url: str) -> bool:
|
|
31
|
+
import urllib.error
|
|
32
|
+
import urllib.request
|
|
33
|
+
|
|
34
|
+
health = url.rstrip("/") + "/health"
|
|
35
|
+
try:
|
|
36
|
+
with urllib.request.urlopen(health, timeout=1.5) as resp: # nosec — local only
|
|
37
|
+
return resp.status == 200
|
|
38
|
+
except (urllib.error.URLError, TimeoutError, OSError):
|
|
39
|
+
return False
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@pytest.fixture
|
|
43
|
+
def isolated_state(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> Path:
|
|
44
|
+
state_dir = tmp_path / "sessions"
|
|
45
|
+
monkeypatch.setenv("OPENCLAW_SMART_STATE_DIR", str(state_dir))
|
|
46
|
+
return state_dir
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _parse_emitted(captured: StringIO) -> dict | None:
|
|
50
|
+
raw = captured.getvalue().strip()
|
|
51
|
+
if not raw:
|
|
52
|
+
return None
|
|
53
|
+
return json.loads(raw)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def test_emit_context_returns_false_when_search_empty(
|
|
57
|
+
monkeypatch: pytest.MonkeyPatch, isolated_state: Path
|
|
58
|
+
) -> None:
|
|
59
|
+
"""No hits → no envelope, no JSONL side-effect."""
|
|
60
|
+
from openclaw_smart import context_inject
|
|
61
|
+
|
|
62
|
+
captured = StringIO()
|
|
63
|
+
monkeypatch.setattr(sys, "stdout", captured)
|
|
64
|
+
|
|
65
|
+
fake_adapter = MagicMock()
|
|
66
|
+
fake_adapter.search_all.return_value = ([], [], [])
|
|
67
|
+
|
|
68
|
+
emitted = context_inject.emit_context(
|
|
69
|
+
session_id="sess-empty",
|
|
70
|
+
project_id="proj-1",
|
|
71
|
+
query="anything",
|
|
72
|
+
top_k=3,
|
|
73
|
+
adapter=fake_adapter,
|
|
74
|
+
)
|
|
75
|
+
assert emitted is False
|
|
76
|
+
assert _parse_emitted(captured) is None
|
|
77
|
+
assert not (isolated_state / "sess-empty.injected.jsonl").exists()
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def test_emit_context_wraps_hits_in_prependContext_envelope(
|
|
81
|
+
monkeypatch: pytest.MonkeyPatch, isolated_state: Path
|
|
82
|
+
) -> None:
|
|
83
|
+
"""A stubbed user playbook is rendered as markdown under ``prependContext``."""
|
|
84
|
+
from openclaw_smart import context_inject
|
|
85
|
+
|
|
86
|
+
captured = StringIO()
|
|
87
|
+
monkeypatch.setattr(sys, "stdout", captured)
|
|
88
|
+
|
|
89
|
+
user_playbook = {
|
|
90
|
+
"content": "Always validate user input before persistence.",
|
|
91
|
+
"trigger": "any external string lands in the persistence layer",
|
|
92
|
+
"rationale": "blocks a whole class of injection bugs",
|
|
93
|
+
"user_playbook_id": "pb-1",
|
|
94
|
+
}
|
|
95
|
+
fake_adapter = MagicMock()
|
|
96
|
+
fake_adapter.search_all.return_value = ([user_playbook], [], [])
|
|
97
|
+
|
|
98
|
+
emitted = context_inject.emit_context(
|
|
99
|
+
session_id="sess-hit",
|
|
100
|
+
project_id="proj-1",
|
|
101
|
+
query="how do I validate user input",
|
|
102
|
+
top_k=3,
|
|
103
|
+
adapter=fake_adapter,
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
assert emitted is True
|
|
107
|
+
payload = _parse_emitted(captured)
|
|
108
|
+
assert payload is not None
|
|
109
|
+
md = payload.get("prependContext")
|
|
110
|
+
assert md, f"expected prependContext markdown, got {payload!r}"
|
|
111
|
+
# Title text shows up inside the rendered markdown.
|
|
112
|
+
assert "validate user input" in md.lower()
|
|
113
|
+
# Citation registry was persisted for the agent_end hook to consume.
|
|
114
|
+
assert (isolated_state / "sess-hit.injected.jsonl").exists()
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def test_emit_context_against_live_backend(
|
|
118
|
+
monkeypatch: pytest.MonkeyPatch, isolated_state: Path
|
|
119
|
+
) -> None:
|
|
120
|
+
"""When a real backend is running, the full pipeline executes without error.
|
|
121
|
+
|
|
122
|
+
No fixture data is seeded — the backend may legitimately return zero hits;
|
|
123
|
+
the assertion is that the call completes, not that there's data to inject.
|
|
124
|
+
"""
|
|
125
|
+
if not _backend_alive(_reflexio_url()):
|
|
126
|
+
pytest.skip(f"reflexio backend not reachable at {_reflexio_url()}")
|
|
127
|
+
|
|
128
|
+
from openclaw_smart import context_inject
|
|
129
|
+
|
|
130
|
+
captured = StringIO()
|
|
131
|
+
monkeypatch.setattr(sys, "stdout", captured)
|
|
132
|
+
|
|
133
|
+
# No assertion on emitted bool — backend state is environment-dependent.
|
|
134
|
+
context_inject.emit_context(
|
|
135
|
+
session_id="sess-live",
|
|
136
|
+
project_id="proj-live",
|
|
137
|
+
query="ping",
|
|
138
|
+
top_k=3,
|
|
139
|
+
)
|
|
140
|
+
# If markdown was emitted it should at least be valid JSON with the right key.
|
|
141
|
+
raw = captured.getvalue().strip()
|
|
142
|
+
if raw:
|
|
143
|
+
payload = json.loads(raw)
|
|
144
|
+
assert "prependContext" in payload
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
"""Tests for openclaw_smart.cli."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from argparse import Namespace
|
|
6
|
+
from unittest.mock import patch
|
|
7
|
+
|
|
8
|
+
import pytest
|
|
9
|
+
from openclaw_smart import cli
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@pytest.fixture(autouse=True)
|
|
13
|
+
def isolate_state_dir(monkeypatch, tmp_path):
|
|
14
|
+
sessions = tmp_path / "sessions"
|
|
15
|
+
monkeypatch.setenv("OPENCLAW_SMART_STATE_DIR", str(sessions))
|
|
16
|
+
return sessions
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_cmd_show_fetches_all_entities(capsys):
|
|
20
|
+
with (
|
|
21
|
+
patch("openclaw_smart.cli.Adapter") as Ad,
|
|
22
|
+
patch("openclaw_smart.cli.ids.resolve_project_id", return_value="proj-x"),
|
|
23
|
+
):
|
|
24
|
+
Ad.return_value.fetch_all.return_value = ([], [], [])
|
|
25
|
+
rc = cli.cmd_show(Namespace(project=None))
|
|
26
|
+
assert rc == 0
|
|
27
|
+
out = capsys.readouterr().out
|
|
28
|
+
assert "proj-x" in out
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def test_cmd_show_renders_markdown(capsys):
|
|
32
|
+
with (
|
|
33
|
+
patch("openclaw_smart.cli.Adapter") as Ad,
|
|
34
|
+
patch("openclaw_smart.cli.ids.resolve_project_id", return_value="proj-x"),
|
|
35
|
+
):
|
|
36
|
+
Ad.return_value.fetch_all.return_value = (
|
|
37
|
+
[{"user_playbook_id": "abc", "content": "Test rule"}],
|
|
38
|
+
[],
|
|
39
|
+
[],
|
|
40
|
+
)
|
|
41
|
+
cli.cmd_show(Namespace(project=None))
|
|
42
|
+
out = capsys.readouterr().out
|
|
43
|
+
assert "Test rule" in out
|
|
44
|
+
assert "[oc:" in out
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def test_cmd_show_honors_project_override(capsys):
|
|
48
|
+
with (
|
|
49
|
+
patch("openclaw_smart.cli.Adapter") as Ad,
|
|
50
|
+
patch("openclaw_smart.cli.ids.resolve_project_id", return_value="ignored"),
|
|
51
|
+
):
|
|
52
|
+
Ad.return_value.fetch_all.return_value = ([], [], [])
|
|
53
|
+
cli.cmd_show(Namespace(project="explicit-proj"))
|
|
54
|
+
Ad.return_value.fetch_all.assert_called_once()
|
|
55
|
+
kwargs = Ad.return_value.fetch_all.call_args[1]
|
|
56
|
+
assert kwargs["project_id"] == "explicit-proj"
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def test_cmd_learn_no_session_returns_zero(isolate_state_dir):
|
|
60
|
+
rc = cli.cmd_learn(Namespace(session=None, project=None, note=None))
|
|
61
|
+
assert rc == 0
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def test_cmd_learn_force_extracts(isolate_state_dir):
|
|
65
|
+
# Seed a session JSONL so _latest_session_id picks it up.
|
|
66
|
+
from openclaw_smart import state
|
|
67
|
+
|
|
68
|
+
state.append("sess-1", {"role": "User", "content": "x"})
|
|
69
|
+
with (
|
|
70
|
+
patch("openclaw_smart.cli.publish") as pub,
|
|
71
|
+
patch("openclaw_smart.cli.ids.resolve_project_id", return_value="proj-x"),
|
|
72
|
+
):
|
|
73
|
+
pub.publish_unpublished.return_value = ("ok", 1)
|
|
74
|
+
rc = cli.cmd_learn(Namespace(session=None, project=None, note=None))
|
|
75
|
+
assert rc == 0
|
|
76
|
+
kwargs = pub.publish_unpublished.call_args[1]
|
|
77
|
+
assert kwargs["force_extraction"] is True
|
|
78
|
+
assert kwargs["session_id"] == "sess-1"
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def test_cmd_learn_appends_note(isolate_state_dir):
|
|
82
|
+
from openclaw_smart import state
|
|
83
|
+
|
|
84
|
+
state.append("sess-2", {"role": "User", "content": "earlier"})
|
|
85
|
+
with (
|
|
86
|
+
patch("openclaw_smart.cli.publish") as pub,
|
|
87
|
+
patch("openclaw_smart.cli.ids.resolve_project_id", return_value="proj-x"),
|
|
88
|
+
):
|
|
89
|
+
pub.publish_unpublished.return_value = ("ok", 2)
|
|
90
|
+
cli.cmd_learn(Namespace(session="sess-2", project=None, note="key insight"))
|
|
91
|
+
records = state.read_all("sess-2")
|
|
92
|
+
contents = [r.get("content") for r in records]
|
|
93
|
+
assert "key insight" in contents
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def test_cmd_learn_handles_unreachable_backend(isolate_state_dir, capsys):
|
|
97
|
+
from openclaw_smart import state
|
|
98
|
+
|
|
99
|
+
state.append("sess-3", {"role": "User", "content": "x"})
|
|
100
|
+
with (
|
|
101
|
+
patch("openclaw_smart.cli.publish") as pub,
|
|
102
|
+
patch("openclaw_smart.cli.ids.resolve_project_id", return_value="proj-x"),
|
|
103
|
+
):
|
|
104
|
+
pub.publish_unpublished.return_value = ("failed", 0)
|
|
105
|
+
rc = cli.cmd_learn(Namespace(session=None, project=None, note=None))
|
|
106
|
+
assert rc == 1
|
|
107
|
+
assert "Failed to reach reflexio" in capsys.readouterr().out
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def test_cmd_clear_all_requires_yes(capsys):
|
|
111
|
+
with patch("openclaw_smart.cli._resolve_clear_all_targets", return_value=[]):
|
|
112
|
+
rc = cli.cmd_clear_all(Namespace(yes=False))
|
|
113
|
+
assert rc != 0
|
|
114
|
+
assert "--yes" in capsys.readouterr().out
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def test_cmd_clear_all_with_yes_proceeds(monkeypatch, tmp_path):
|
|
118
|
+
sessions = tmp_path / "sessions"
|
|
119
|
+
sessions.mkdir()
|
|
120
|
+
(sessions / "old.jsonl").write_text('{"role":"User"}\n')
|
|
121
|
+
monkeypatch.setenv("OPENCLAW_SMART_STATE_DIR", str(sessions))
|
|
122
|
+
|
|
123
|
+
with (
|
|
124
|
+
patch("openclaw_smart.cli._resolve_clear_all_targets", return_value=[]),
|
|
125
|
+
patch("openclaw_smart.cli._service_status", return_value="not running"),
|
|
126
|
+
patch("openclaw_smart.cli._run_service", return_value=0),
|
|
127
|
+
):
|
|
128
|
+
rc = cli.cmd_clear_all(Namespace(yes=True))
|
|
129
|
+
assert rc == 0
|
|
130
|
+
assert not (sessions / "old.jsonl").exists()
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def test_cmd_clear_all_restarts_backend_after_delete_failure(tmp_path):
|
|
134
|
+
target = cli._ClearAllTarget(
|
|
135
|
+
path=tmp_path / "reflexio-openclaw-test",
|
|
136
|
+
kind="dir",
|
|
137
|
+
label="test target",
|
|
138
|
+
)
|
|
139
|
+
service_calls: list[str] = []
|
|
140
|
+
|
|
141
|
+
def fake_run_service(_script, command) -> int: # noqa: ANN001
|
|
142
|
+
service_calls.append(command)
|
|
143
|
+
return 0
|
|
144
|
+
|
|
145
|
+
with (
|
|
146
|
+
patch("openclaw_smart.cli._resolve_clear_all_targets", return_value=[target]),
|
|
147
|
+
patch("openclaw_smart.cli._service_status", return_value="running on 8071"),
|
|
148
|
+
patch(
|
|
149
|
+
"openclaw_smart.cli._remove_clear_all_target",
|
|
150
|
+
side_effect=cli._ClearAllError("boom"),
|
|
151
|
+
),
|
|
152
|
+
patch("openclaw_smart.cli._run_service", side_effect=fake_run_service),
|
|
153
|
+
):
|
|
154
|
+
rc = cli.cmd_clear_all(Namespace(yes=True))
|
|
155
|
+
|
|
156
|
+
assert rc == 1
|
|
157
|
+
assert service_calls == ["stop", "start"]
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def test_build_parser_accepts_show():
|
|
161
|
+
parser = cli._build_parser()
|
|
162
|
+
args = parser.parse_args(["show"])
|
|
163
|
+
assert args.command == "show"
|
|
164
|
+
assert args.project is None
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def test_build_parser_accepts_learn_with_note():
|
|
168
|
+
parser = cli._build_parser()
|
|
169
|
+
args = parser.parse_args(["learn", "--note", "hello"])
|
|
170
|
+
assert args.command == "learn"
|
|
171
|
+
assert args.note == "hello"
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def test_build_parser_accepts_clear_all_yes():
|
|
175
|
+
parser = cli._build_parser()
|
|
176
|
+
args = parser.parse_args(["clear-all", "--yes"])
|
|
177
|
+
assert args.yes is True
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def test_build_parser_accepts_restart_flags():
|
|
181
|
+
parser = cli._build_parser()
|
|
182
|
+
args = parser.parse_args(["restart", "--skip-backend", "--no-rebuild"])
|
|
183
|
+
assert args.skip_backend is True
|
|
184
|
+
assert args.no_rebuild is True
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"""Tests for openclaw_smart.events.after_tool_call."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
|
|
7
|
+
import pytest
|
|
8
|
+
from openclaw_smart.events import after_tool_call as atc
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@pytest.fixture(autouse=True)
|
|
12
|
+
def isolate_state_dir(monkeypatch, tmp_path):
|
|
13
|
+
sessions = tmp_path / "sessions"
|
|
14
|
+
monkeypatch.setenv("OPENCLAW_SMART_STATE_DIR", str(sessions))
|
|
15
|
+
return sessions
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _read_records(sessions_dir, session_id="s1"):
|
|
19
|
+
path = sessions_dir / f"{session_id}.jsonl"
|
|
20
|
+
if not path.exists():
|
|
21
|
+
return []
|
|
22
|
+
return [json.loads(line) for line in path.read_text().splitlines() if line]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def test_handle_records_tool_invocation_camelcase(isolate_state_dir):
|
|
26
|
+
"""openClaw delivers camelCase fields; handler translates to snake_case."""
|
|
27
|
+
atc.handle(
|
|
28
|
+
{
|
|
29
|
+
"sessionKey": "s1",
|
|
30
|
+
"toolName": "Bash",
|
|
31
|
+
"params": {"command": "ls"},
|
|
32
|
+
"result": "file1.txt",
|
|
33
|
+
"agentId": "a",
|
|
34
|
+
}
|
|
35
|
+
)
|
|
36
|
+
records = _read_records(isolate_state_dir)
|
|
37
|
+
assert len(records) == 1
|
|
38
|
+
rec = records[0]
|
|
39
|
+
assert rec["role"] == "Assistant_tool"
|
|
40
|
+
assert rec["tool_name"] == "Bash"
|
|
41
|
+
assert rec["tool_input"] == {"command": "ls"}
|
|
42
|
+
assert rec["tool_output"] == "file1.txt"
|
|
43
|
+
assert rec["status"] == "success"
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def test_handle_accepts_snake_case_fallback(isolate_state_dir):
|
|
47
|
+
"""Already-translated payloads work too."""
|
|
48
|
+
atc.handle(
|
|
49
|
+
{
|
|
50
|
+
"sessionKey": "s2",
|
|
51
|
+
"tool_name": "Edit",
|
|
52
|
+
"tool_input": {"file_path": "x.py", "new_string": "hi"},
|
|
53
|
+
"tool_response": "ok",
|
|
54
|
+
"status": "success",
|
|
55
|
+
}
|
|
56
|
+
)
|
|
57
|
+
records = _read_records(isolate_state_dir, "s2")
|
|
58
|
+
assert records[0]["tool_name"] == "Edit"
|
|
59
|
+
assert records[0]["tool_input"]["file_path"] == "x.py"
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def test_handle_redacts_high_entropy_secrets(isolate_state_dir):
|
|
63
|
+
secret_command = "API_KEY=AbCdEfGhIj1234567890QwErTyUiOp curl ..."
|
|
64
|
+
atc.handle(
|
|
65
|
+
{
|
|
66
|
+
"sessionKey": "s3",
|
|
67
|
+
"toolName": "Bash",
|
|
68
|
+
"params": {"command": secret_command},
|
|
69
|
+
"result": "",
|
|
70
|
+
}
|
|
71
|
+
)
|
|
72
|
+
records = _read_records(isolate_state_dir, "s3")
|
|
73
|
+
command = records[0]["tool_input"]["command"]
|
|
74
|
+
assert "AbCdEfGhIj1234567890QwErTyUiOp" not in command
|
|
75
|
+
assert "<redacted:" in command
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def test_handle_does_not_redact_simple_assignments(isolate_state_dir):
|
|
79
|
+
"""LOG_LEVEL=INFO should not be redacted — too short and not entropy-y."""
|
|
80
|
+
atc.handle(
|
|
81
|
+
{
|
|
82
|
+
"sessionKey": "s4",
|
|
83
|
+
"toolName": "Bash",
|
|
84
|
+
"params": {"command": "LOG_LEVEL=INFO some_cmd"},
|
|
85
|
+
"result": "",
|
|
86
|
+
}
|
|
87
|
+
)
|
|
88
|
+
records = _read_records(isolate_state_dir, "s4")
|
|
89
|
+
assert "LOG_LEVEL=INFO" in records[0]["tool_input"]["command"]
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def test_handle_classifies_error_status(isolate_state_dir):
|
|
93
|
+
atc.handle(
|
|
94
|
+
{
|
|
95
|
+
"sessionKey": "s5",
|
|
96
|
+
"toolName": "Bash",
|
|
97
|
+
"params": {"command": "false"},
|
|
98
|
+
"result": {"is_error": True, "error": "exit 1"},
|
|
99
|
+
}
|
|
100
|
+
)
|
|
101
|
+
records = _read_records(isolate_state_dir, "s5")
|
|
102
|
+
assert records[0]["status"] == "error"
|
|
103
|
+
assert "exit 1" in records[0]["tool_output"]
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def test_handle_flattens_bash_dict_output(isolate_state_dir):
|
|
107
|
+
atc.handle(
|
|
108
|
+
{
|
|
109
|
+
"sessionKey": "s6",
|
|
110
|
+
"toolName": "Bash",
|
|
111
|
+
"params": {"command": "echo hi"},
|
|
112
|
+
"result": {"stdout": "hi", "stderr": ""},
|
|
113
|
+
}
|
|
114
|
+
)
|
|
115
|
+
records = _read_records(isolate_state_dir, "s6")
|
|
116
|
+
assert records[0]["tool_output"] == "hi"
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def test_handle_skips_when_no_session_id(isolate_state_dir):
|
|
120
|
+
atc.handle({"toolName": "Bash", "params": {"command": "ls"}})
|
|
121
|
+
assert _read_records(isolate_state_dir) == []
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def test_handle_skips_when_no_tool_name(isolate_state_dir):
|
|
125
|
+
atc.handle({"sessionKey": "s7", "params": {"command": "ls"}})
|
|
126
|
+
assert _read_records(isolate_state_dir, "s7") == []
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def test_handle_truncates_long_output(isolate_state_dir):
|
|
130
|
+
long_output = "x" * 5000
|
|
131
|
+
atc.handle(
|
|
132
|
+
{
|
|
133
|
+
"sessionKey": "s8",
|
|
134
|
+
"toolName": "Bash",
|
|
135
|
+
"params": {"command": "cat large_file"},
|
|
136
|
+
"result": long_output,
|
|
137
|
+
}
|
|
138
|
+
)
|
|
139
|
+
records = _read_records(isolate_state_dir, "s8")
|
|
140
|
+
output = records[0]["tool_output"]
|
|
141
|
+
assert "truncated" in output
|
|
142
|
+
assert len(output) < 5000
|