claude-smart 0.2.44 → 0.2.46
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 +34 -5
- package/bin/claude-smart.js +295 -5
- 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 +22 -2
- 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/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 +158 -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 +174 -0
- package/plugin/opencode/tsconfig.json +13 -0
- package/plugin/pyproject.toml +20 -3
- package/plugin/scripts/_lib.sh +77 -29
- package/plugin/scripts/backend-service.sh +27 -5
- package/plugin/scripts/dashboard-service.sh +7 -5
- package/plugin/scripts/ensure-plugin-root.sh +4 -4
- package/plugin/scripts/hook_entry.sh +3 -3
- 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 +10 -8
- package/plugin/src/README.md +1 -1
- package/plugin/src/claude_smart/cli.py +304 -6
- 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 +1 -1
- package/plugin/vendor/reflexio/.env.example +54 -30
- package/plugin/vendor/reflexio/README.md +14 -8
- package/plugin/vendor/reflexio/pyproject.toml +13 -1
- package/plugin/vendor/reflexio/reflexio/README.md +1 -0
- 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 +210 -50
- 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/client/client.py +57 -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 +238 -10
- package/plugin/vendor/reflexio/reflexio/lib/_config.py +11 -4
- package/plugin/vendor/reflexio/reflexio/lib/_generation.py +16 -5
- package/plugin/vendor/reflexio/reflexio/lib/_lineage_parity_readers.py +187 -0
- package/plugin/vendor/reflexio/reflexio/lib/_profiles.py +198 -6
- package/plugin/vendor/reflexio/reflexio/lib/_reflection.py +1 -1
- package/plugin/vendor/reflexio/reflexio/lib/_search.py +29 -20
- package/plugin/vendor/reflexio/reflexio/lib/_user_playbook.py +2 -1
- 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/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 +52 -0
- 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 +88 -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 +45 -32
- 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/_auth.py +65 -2
- package/plugin/vendor/reflexio/reflexio/server/api.py +734 -85
- package/plugin/vendor/reflexio/reflexio/server/api_endpoints/precondition_checks.py +7 -0
- package/plugin/vendor/reflexio/reflexio/server/api_endpoints/request_context.py +3 -1
- 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/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/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/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 +266 -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 +26 -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 +39 -16
- package/plugin/vendor/reflexio/reflexio/server/services/generation_service.py +105 -23
- package/plugin/vendor/reflexio/reflexio/server/services/lineage/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/server/services/lineage/gc_scheduler.py +193 -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 +11 -12
- 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} +316 -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/user_detail_stripping.py +84 -0
- 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} +30 -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/relevance_floor.py +83 -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 +27 -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 +2 -0
- 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 +642 -197
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_extras.py +61 -104
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_lineage.py +628 -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 +978 -184
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_profiles.py +519 -128
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_requests.py +77 -3
- 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/storage_base/__init__.py +131 -20
- 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 +36 -53
- 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 +243 -7
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_profiles.py +121 -1
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_requests.py +60 -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/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 +229 -32
- 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/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,69 @@
|
|
|
1
|
+
"""before_prompt_build hook — buffer the user turn and inject matching context.
|
|
2
|
+
|
|
3
|
+
Two responsibilities, in order:
|
|
4
|
+
|
|
5
|
+
1. Buffer the prompt into the session JSONL (this is the sole source of
|
|
6
|
+
``"User"`` role turns downstream — openClaw replays the rest of the
|
|
7
|
+
transcript via tool events, not before_prompt_build).
|
|
8
|
+
2. Use the prompt text as a search query against reflexio's preferences +
|
|
9
|
+
skills and emit the top hits as ``prependContext`` so the model sees
|
|
10
|
+
relevant rules before planning the response.
|
|
11
|
+
|
|
12
|
+
The shared pipeline lives in ``context_inject.emit_context``. Retrieval is
|
|
13
|
+
best-effort: any failure from search (reflexio unreachable, HTTP timeout,
|
|
14
|
+
unexpected shape) is caught so the buffered-prompt behaviour is always
|
|
15
|
+
preserved.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import logging
|
|
21
|
+
import time
|
|
22
|
+
from typing import Any
|
|
23
|
+
|
|
24
|
+
from openclaw_smart import context_inject, ids, state
|
|
25
|
+
|
|
26
|
+
_LOGGER = logging.getLogger(__name__)
|
|
27
|
+
_TOP_K = 3
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def handle(payload: dict[str, Any]) -> None:
|
|
31
|
+
"""before_prompt_build dispatcher — buffers the prompt, then injects context.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
payload (dict[str, Any]): The openClaw event/ctx blob (merged). Expected
|
|
35
|
+
keys ``sessionKey`` (or ``sessionId``), ``prompt``, optionally
|
|
36
|
+
``agentId`` and ``workspaceDir``.
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
None: Side effects only — appends to the session buffer and may
|
|
40
|
+
write a ``{"prependContext": ...}`` JSON document to stdout.
|
|
41
|
+
"""
|
|
42
|
+
session_id = payload.get("sessionKey") or payload.get("sessionId")
|
|
43
|
+
prompt = payload.get("prompt") or ""
|
|
44
|
+
if not session_id or not prompt:
|
|
45
|
+
return
|
|
46
|
+
|
|
47
|
+
project_id = ids.resolve_project_id_with_fallback(
|
|
48
|
+
cwd=payload.get("workspaceDir"),
|
|
49
|
+
agent_id=payload.get("agentId"),
|
|
50
|
+
)
|
|
51
|
+
state.append(
|
|
52
|
+
session_id,
|
|
53
|
+
{
|
|
54
|
+
"ts": int(time.time()),
|
|
55
|
+
"role": "User",
|
|
56
|
+
"content": prompt,
|
|
57
|
+
"user_id": project_id,
|
|
58
|
+
},
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
try:
|
|
62
|
+
context_inject.emit_context(
|
|
63
|
+
session_id=session_id,
|
|
64
|
+
project_id=project_id,
|
|
65
|
+
query=prompt,
|
|
66
|
+
top_k=_TOP_K,
|
|
67
|
+
)
|
|
68
|
+
except Exception as exc: # noqa: BLE001 — never break the user's turn
|
|
69
|
+
_LOGGER.debug("before_prompt_build context inject failed: %s", exc)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""before_tool_call hook — observe-only stub.
|
|
2
|
+
|
|
3
|
+
Phase 0 probe finding Q1: openClaw's ``before_tool_call`` hook does NOT
|
|
4
|
+
honor ``prependContext`` returned by plugins, so injecting context here
|
|
5
|
+
would have no effect on the upcoming tool call. (In contrast, Claude
|
|
6
|
+
Code's PreToolUse hook is the primary just-in-time injection point.)
|
|
7
|
+
|
|
8
|
+
This handler is therefore a silent no-op in v1. The TS shim may choose
|
|
9
|
+
not to register it at all — having a stub here keeps the dispatcher
|
|
10
|
+
symmetric and leaves room for future observe-only telemetry.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import logging
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
_LOGGER = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def handle(payload: dict[str, Any]) -> None:
|
|
22
|
+
"""before_tool_call dispatcher — silent no-op.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
payload (dict[str, Any]): Unused. openClaw delivers
|
|
26
|
+
``{toolName, params, sessionKey, agentId}`` here but we have no
|
|
27
|
+
actionable side effect to apply per Phase 0 finding Q1.
|
|
28
|
+
"""
|
|
29
|
+
del payload
|
|
30
|
+
_LOGGER.debug("before_tool_call invoked (observe-only stub)")
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""session_end hook — flush any remaining interactions with forced extraction.
|
|
2
|
+
|
|
3
|
+
Fires when an openClaw session terminates. We force extraction here (unlike
|
|
4
|
+
``agent_end``, which lets reflexio queue extraction for its next sweep) so
|
|
5
|
+
the user's distilled lessons from this session are usable in the very next
|
|
6
|
+
session, not after the next scheduled sweep.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
from openclaw_smart import ids, publish
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def handle(payload: dict[str, Any]) -> None:
|
|
17
|
+
"""Drain the buffer and trigger synchronous extraction.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
payload (dict[str, Any]): openClaw event/ctx blob, expected to
|
|
21
|
+
contain ``sessionKey`` (or ``sessionId``) and optionally
|
|
22
|
+
``agentId`` / ``workspaceDir``.
|
|
23
|
+
"""
|
|
24
|
+
session_id = payload.get("sessionKey") or payload.get("sessionId")
|
|
25
|
+
if not session_id:
|
|
26
|
+
return
|
|
27
|
+
project_id = ids.resolve_project_id_with_fallback(
|
|
28
|
+
cwd=payload.get("workspaceDir"),
|
|
29
|
+
agent_id=payload.get("agentId"),
|
|
30
|
+
)
|
|
31
|
+
publish.publish_unpublished(
|
|
32
|
+
session_id=session_id,
|
|
33
|
+
project_id=project_id,
|
|
34
|
+
force_extraction=True,
|
|
35
|
+
skip_aggregation=False,
|
|
36
|
+
)
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"""session_start hook — apply startup defaults without broad memory retrieval.
|
|
2
|
+
|
|
3
|
+
For openClaw, the ``session_start`` event delivers ``{sessionId, sessionKey,
|
|
4
|
+
agentId, workspaceDir}``. The handler:
|
|
5
|
+
|
|
6
|
+
1. Fetches the current stall state from reflexio and renders a 1-line banner
|
|
7
|
+
to ``prependContext`` if learning is paused.
|
|
8
|
+
2. Pushes openclaw-smart's preferred extraction defaults (smaller window /
|
|
9
|
+
stride than reflexio's out-of-box 10/5).
|
|
10
|
+
3. Pushes optimizer defaults so reflexio can evaluate candidate playbooks
|
|
11
|
+
via the local ``openclaw infer model run`` CLI.
|
|
12
|
+
|
|
13
|
+
Output: when there is a banner, emits ``{"prependContext": "<banner>"}`` on
|
|
14
|
+
stdout for the TS shim to relay back to openClaw. Otherwise emits nothing.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import contextlib
|
|
20
|
+
import json
|
|
21
|
+
import os
|
|
22
|
+
import sys
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
from typing import Any
|
|
25
|
+
|
|
26
|
+
from openclaw_smart.reflexio_adapter import Adapter
|
|
27
|
+
from openclaw_smart.stall_banner import render_banner
|
|
28
|
+
|
|
29
|
+
# openclaw-smart's preferred extraction cadence — more frequent, smaller
|
|
30
|
+
# windows than reflexio's out-of-box 10/5. Applied idempotently to the
|
|
31
|
+
# reflexio server on every session_start via
|
|
32
|
+
# Adapter.apply_extraction_defaults.
|
|
33
|
+
_WINDOW_SIZE = 5
|
|
34
|
+
_STRIDE_SIZE = 3
|
|
35
|
+
# Optimizer is on by default. Set this env var to "0" to skip pushing the
|
|
36
|
+
# openclaw-smart optimizer defaults on session_start (kill switch).
|
|
37
|
+
_DISABLE_OPTIMIZER_ENV = "OPENCLAW_SMART_ENABLE_OPTIMIZER"
|
|
38
|
+
_OPTIMIZER_TIMEOUT_SECONDS = 300
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _adapter() -> Adapter:
|
|
42
|
+
"""Construct the reflexio adapter for this hook invocation.
|
|
43
|
+
|
|
44
|
+
Indirected through a factory so tests can monkeypatch the adapter
|
|
45
|
+
construction without touching the ``Adapter`` class itself.
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
Adapter: A fresh adapter bound to the current process env.
|
|
49
|
+
"""
|
|
50
|
+
return Adapter()
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _stall_banner(adapter: Any) -> str:
|
|
54
|
+
"""Return the prepend-able stall banner, or "" if no banner should fire.
|
|
55
|
+
|
|
56
|
+
Reads ``adapter.fetch_stall_state()``; if it reports an active,
|
|
57
|
+
not-yet-notified stall, renders a one-line banner via
|
|
58
|
+
``stall_banner.render_banner``. All exceptions are absorbed: this is
|
|
59
|
+
defense-in-depth — even though the hook dispatcher already wraps
|
|
60
|
+
``handle`` in try/except, a stall-path bug must never block the
|
|
61
|
+
existing playbook/profile rendering.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
adapter (Any): The adapter to query. Duck-typed so tests can stub.
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
str: The banner text, or ``""`` when there is nothing to show.
|
|
68
|
+
"""
|
|
69
|
+
try:
|
|
70
|
+
state_obj = adapter.fetch_stall_state()
|
|
71
|
+
except Exception: # noqa: BLE001 — stall path must never crash the hook.
|
|
72
|
+
return ""
|
|
73
|
+
if state_obj is None:
|
|
74
|
+
return ""
|
|
75
|
+
if not getattr(state_obj, "stalled", False):
|
|
76
|
+
return ""
|
|
77
|
+
if getattr(state_obj, "notified_in_cc", False):
|
|
78
|
+
return ""
|
|
79
|
+
try:
|
|
80
|
+
return render_banner(
|
|
81
|
+
reason=getattr(state_obj, "reason", None),
|
|
82
|
+
reset_estimate=getattr(state_obj, "reset_estimate", None),
|
|
83
|
+
)
|
|
84
|
+
except Exception: # noqa: BLE001 — render_banner bug must not block playbook injection.
|
|
85
|
+
return ""
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def handle(payload: dict[str, Any]) -> None:
|
|
89
|
+
"""Handle a session_start hook payload.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
payload (dict[str, Any]): openClaw event/ctx blob, expected to
|
|
93
|
+
contain ``sessionKey`` (or ``sessionId``).
|
|
94
|
+
"""
|
|
95
|
+
session_id = payload.get("sessionKey") or payload.get("sessionId")
|
|
96
|
+
if not session_id:
|
|
97
|
+
return
|
|
98
|
+
|
|
99
|
+
adapter = _adapter()
|
|
100
|
+
|
|
101
|
+
# Stall banner — emitted via prependContext, fires at most once per stall
|
|
102
|
+
# event (controlled server-side via mark_stall_notified).
|
|
103
|
+
banner = _stall_banner(adapter)
|
|
104
|
+
|
|
105
|
+
adapter.apply_extraction_defaults(
|
|
106
|
+
window_size=_WINDOW_SIZE,
|
|
107
|
+
stride_size=_STRIDE_SIZE,
|
|
108
|
+
)
|
|
109
|
+
if os.environ.get(_DISABLE_OPTIMIZER_ENV) != "0":
|
|
110
|
+
adapter.apply_optimizer_defaults(
|
|
111
|
+
script_path=_optimizer_assistant_path(),
|
|
112
|
+
timeout_seconds=_OPTIMIZER_TIMEOUT_SECONDS,
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
if not banner:
|
|
116
|
+
return
|
|
117
|
+
|
|
118
|
+
sys.stdout.write(json.dumps({"prependContext": banner}))
|
|
119
|
+
sys.stdout.write("\n")
|
|
120
|
+
|
|
121
|
+
# Telemetry must not break the session.
|
|
122
|
+
with contextlib.suppress(Exception):
|
|
123
|
+
adapter.mark_stall_notified()
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def _optimizer_assistant_path() -> str:
|
|
127
|
+
"""Return the absolute path to the openclaw-smart-optimizer-assistant binary."""
|
|
128
|
+
executable = Path(sys.executable)
|
|
129
|
+
suffix = ".exe" if os.name == "nt" else ""
|
|
130
|
+
return str(executable.with_name(f"openclaw-smart-optimizer-assistant{suffix}"))
|
package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/hook.py
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"""Dispatch table for openclaw-smart hook events.
|
|
2
|
+
|
|
3
|
+
The TS shim spawns ``python -m openclaw_smart.hook <event>`` (or the
|
|
4
|
+
``openclaw-smart-hook`` console script) once per hook invocation, piping
|
|
5
|
+
the openClaw event payload on stdin. This module reads the JSON, routes
|
|
6
|
+
to the matching handler, and makes sure no unhandled exception ever
|
|
7
|
+
propagates back to openClaw.
|
|
8
|
+
|
|
9
|
+
Handlers emit their own ``{"prependContext": …}`` JSON to stdout when
|
|
10
|
+
they have context to inject; otherwise they emit nothing. The dispatcher
|
|
11
|
+
emits nothing on no-op paths (unknown event, recursion guard, handler
|
|
12
|
+
exception) so the TS shim can treat empty stdout as "no return value".
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import json
|
|
18
|
+
import logging
|
|
19
|
+
import sys
|
|
20
|
+
from collections.abc import Callable
|
|
21
|
+
from typing import Any
|
|
22
|
+
|
|
23
|
+
from openclaw_smart import runtime
|
|
24
|
+
from openclaw_smart.internal_call import is_internal_invocation
|
|
25
|
+
|
|
26
|
+
_LOGGER = logging.getLogger(__name__)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _load_handlers() -> dict[str, Callable[[dict[str, Any]], None]]:
|
|
30
|
+
"""Import handler modules lazily to keep cold-start cost low."""
|
|
31
|
+
from openclaw_smart.events import (
|
|
32
|
+
after_tool_call,
|
|
33
|
+
agent_end,
|
|
34
|
+
before_prompt_build,
|
|
35
|
+
before_tool_call,
|
|
36
|
+
session_end,
|
|
37
|
+
session_start,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
"session-start": session_start.handle,
|
|
42
|
+
"before-prompt-build": before_prompt_build.handle,
|
|
43
|
+
"before-tool-call": before_tool_call.handle,
|
|
44
|
+
"after-tool-call": after_tool_call.handle,
|
|
45
|
+
"agent-end": agent_end.handle,
|
|
46
|
+
"session-end": session_end.handle,
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
_HANDLERS: dict[str, Callable[[dict[str, Any]], None]] | None = None
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _handlers() -> dict[str, Callable[[dict[str, Any]], None]]:
|
|
54
|
+
global _HANDLERS
|
|
55
|
+
if _HANDLERS is None:
|
|
56
|
+
_HANDLERS = _load_handlers()
|
|
57
|
+
return _HANDLERS
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _read_stdin_json() -> dict[str, Any]:
|
|
61
|
+
"""Parse stdin as JSON. Returns ``{}`` on empty or malformed input."""
|
|
62
|
+
try:
|
|
63
|
+
raw = sys.stdin.read()
|
|
64
|
+
except (OSError, ValueError) as exc:
|
|
65
|
+
_LOGGER.debug("stdin read failed: %s", exc)
|
|
66
|
+
return {}
|
|
67
|
+
if not raw.strip():
|
|
68
|
+
return {}
|
|
69
|
+
try:
|
|
70
|
+
parsed = json.loads(raw)
|
|
71
|
+
except json.JSONDecodeError as exc:
|
|
72
|
+
_LOGGER.debug("stdin JSON decode failed: %s", exc)
|
|
73
|
+
return {}
|
|
74
|
+
return parsed if isinstance(parsed, dict) else {}
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _parse_args(argv: list[str]) -> str:
|
|
78
|
+
"""Return the event name from ``argv``.
|
|
79
|
+
|
|
80
|
+
Accepts either ``[event]`` or ``[host, event]`` for symmetry with
|
|
81
|
+
claude-smart, but the host arg (if present) must be ``"openclaw"`` and
|
|
82
|
+
is otherwise ignored — runtime.set_host is called either way.
|
|
83
|
+
"""
|
|
84
|
+
if not argv:
|
|
85
|
+
return ""
|
|
86
|
+
if len(argv) >= 2 and argv[0] in runtime.VALID_HOSTS:
|
|
87
|
+
return argv[1]
|
|
88
|
+
return argv[0]
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def main(argv: list[str] | None = None) -> int:
|
|
92
|
+
"""Entry point used by ``python -m openclaw_smart.hook`` and the console script.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
argv (list[str] | None): Command-line args (sans program name).
|
|
96
|
+
Defaults to ``sys.argv[1:]``.
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
int: Always 0 — failures are absorbed so the host never sees a
|
|
100
|
+
non-zero exit and decides to surface a plugin error.
|
|
101
|
+
"""
|
|
102
|
+
argv = argv if argv is not None else sys.argv[1:]
|
|
103
|
+
event = _parse_args(argv)
|
|
104
|
+
runtime.set_host(runtime.HOST_OPENCLAW)
|
|
105
|
+
if not event:
|
|
106
|
+
_LOGGER.warning("hook dispatcher called with no event name")
|
|
107
|
+
return 0
|
|
108
|
+
|
|
109
|
+
payload = _read_stdin_json()
|
|
110
|
+
|
|
111
|
+
# Self-feedback guard: when this hook fires inside reflexio's own
|
|
112
|
+
# openclaw subprocess (the openclaw LLM provider), skip all handlers
|
|
113
|
+
# so we don't publish the extractor's system prompt back into reflexio.
|
|
114
|
+
# See openclaw_smart.internal_call for detection logic.
|
|
115
|
+
if is_internal_invocation(payload):
|
|
116
|
+
return 0
|
|
117
|
+
|
|
118
|
+
handler = _handlers().get(event)
|
|
119
|
+
if handler is None:
|
|
120
|
+
_LOGGER.warning("unknown hook event: %s", event)
|
|
121
|
+
return 0
|
|
122
|
+
|
|
123
|
+
try:
|
|
124
|
+
handler(payload)
|
|
125
|
+
except Exception as exc: # noqa: BLE001 — hooks must never crash the session.
|
|
126
|
+
_LOGGER.exception("hook handler %s raised: %s", event, exc)
|
|
127
|
+
return 0
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
if __name__ == "__main__":
|
|
131
|
+
raise SystemExit(main())
|
package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/ids.py
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"""Resolve stable identifiers for openClaw sessions.
|
|
2
|
+
|
|
3
|
+
Two identifiers matter to reflexio:
|
|
4
|
+
|
|
5
|
+
- ``session_id``: openClaw's per-session id (``sessionKey``), passed via the
|
|
6
|
+
TS shim. We forward it to reflexio's interaction ``session_id`` field so
|
|
7
|
+
individual turns remain attributable to their conversation, but it is no
|
|
8
|
+
longer the scope key for extracted preferences.
|
|
9
|
+
- ``project_id``: a stable, cross-session name for the project. We use this
|
|
10
|
+
as reflexio's ``user_id`` for preferences, so user preferences extracted
|
|
11
|
+
in one session are visible to every later session in the same repo.
|
|
12
|
+
``agent_version`` is hardcoded to ``"openclaw"`` in the adapter so shared
|
|
13
|
+
skills roll up globally across all openclaw projects.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import logging
|
|
19
|
+
import os
|
|
20
|
+
import shutil
|
|
21
|
+
import subprocess # noqa: S404 — git invocation with a fixed flag set.
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
|
|
24
|
+
_LOGGER = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _resolve_from_git(cwd: Path) -> str | None:
|
|
28
|
+
"""Return the git toplevel basename for *cwd*, or ``None`` if not a repo.
|
|
29
|
+
|
|
30
|
+
Must never raise — callers depend on this returning ``None`` cleanly so
|
|
31
|
+
hook handlers can fall back to other identifiers. Resolves ``git`` to an
|
|
32
|
+
absolute path first to avoid PATH-dependent process spawning, and catches
|
|
33
|
+
any ``OSError`` (e.g., ``cwd`` is unreadable or has been deleted) along
|
|
34
|
+
with the previously-handled ``FileNotFoundError`` / ``TimeoutExpired``.
|
|
35
|
+
"""
|
|
36
|
+
git_bin = shutil.which("git")
|
|
37
|
+
if not git_bin:
|
|
38
|
+
return None
|
|
39
|
+
try:
|
|
40
|
+
result = subprocess.run( # noqa: S603 — fixed argv, cwd is a Path.
|
|
41
|
+
[git_bin, "rev-parse", "--show-toplevel"],
|
|
42
|
+
cwd=cwd,
|
|
43
|
+
capture_output=True,
|
|
44
|
+
text=True,
|
|
45
|
+
check=False,
|
|
46
|
+
timeout=5,
|
|
47
|
+
)
|
|
48
|
+
if result.returncode == 0:
|
|
49
|
+
toplevel = result.stdout.strip()
|
|
50
|
+
if toplevel:
|
|
51
|
+
return Path(toplevel).name
|
|
52
|
+
except (OSError, subprocess.TimeoutExpired) as exc:
|
|
53
|
+
# OSError covers FileNotFoundError + PermissionError + missing-cwd.
|
|
54
|
+
_LOGGER.debug("git toplevel resolution failed: %s", exc)
|
|
55
|
+
return None
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def resolve_project_id(cwd: str | os.PathLike[str] | None = None) -> str:
|
|
59
|
+
"""Return a stable project identifier for the given working directory.
|
|
60
|
+
|
|
61
|
+
Prefers the basename of the git toplevel (so worktrees, submodules, and
|
|
62
|
+
``cd src/`` all still map to the same project). Falls back to the cwd
|
|
63
|
+
basename when the directory is not inside a git repo.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
cwd: Working directory to resolve. Defaults to ``os.getcwd()``.
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
str: A non-empty identifier. Never raises.
|
|
70
|
+
"""
|
|
71
|
+
base = Path(cwd) if cwd is not None else Path.cwd()
|
|
72
|
+
return _resolve_from_git(base) or base.name or "unknown-project"
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def resolve_project_id_with_fallback(
|
|
76
|
+
cwd: str | os.PathLike[str] | None,
|
|
77
|
+
agent_id: str | None,
|
|
78
|
+
) -> str:
|
|
79
|
+
"""Resolve project ID, falling back to ``agent_id`` when not in a git repo.
|
|
80
|
+
|
|
81
|
+
openClaw can run from any directory (or none at all), so when there is no
|
|
82
|
+
git context we prefer the openClaw agent id over a fabricated cwd
|
|
83
|
+
basename, which would be brittle across sessions.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
cwd: Working directory of the openClaw session (``ctx.workspaceDir``).
|
|
87
|
+
agent_id: openClaw agent id from ``ctx.agentId``.
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
str: git repo basename if cwd is in a repo; else ``agent_id``; else
|
|
91
|
+
the literal ``"openclaw"``.
|
|
92
|
+
"""
|
|
93
|
+
base = Path(cwd) if cwd is not None else Path.cwd()
|
|
94
|
+
return _resolve_from_git(base) or agent_id or "openclaw"
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""Detect hook invocations that should not be published to reflexio.
|
|
2
|
+
|
|
3
|
+
The single concern for openclaw-smart is reflexio's own LLM provider. The
|
|
4
|
+
``openclaw`` LiteLLM provider (see
|
|
5
|
+
``reflexio.server.llm.providers.openclaw_provider``) shells out to the
|
|
6
|
+
``openclaw`` CLI to answer extractor prompts. That subprocess is a full
|
|
7
|
+
openClaw invocation, so it fires *our* hooks too — and without a guard,
|
|
8
|
+
the ``agent_end`` hook would publish the extractor's own system prompt
|
|
9
|
+
back into reflexio as a user interaction. Reflexio would then train on
|
|
10
|
+
its own internals.
|
|
11
|
+
|
|
12
|
+
Detection signals, OR'd:
|
|
13
|
+
- Env var ``OPENCLAW_SMART_INTERNAL=1``, set by reflexio's provider
|
|
14
|
+
before spawning ``openclaw``.
|
|
15
|
+
- ``payload.cwd`` resolves inside the reflexio repository. Catches
|
|
16
|
+
direct interactive ``openclaw`` runs from inside the reflexio
|
|
17
|
+
checkout (manual debugging) that would otherwise pollute the corpus.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import os
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
from typing import Any
|
|
25
|
+
|
|
26
|
+
INTERNAL_ENV = "OPENCLAW_SMART_INTERNAL"
|
|
27
|
+
|
|
28
|
+
# Plugin layout (in-repo / editable):
|
|
29
|
+
# <reflexio_repo>/reflexio/integrations/openclaw/plugin/src/openclaw_smart/internal_call.py
|
|
30
|
+
# parents[5] = <reflexio_repo>, the directory we want to fence off.
|
|
31
|
+
#
|
|
32
|
+
# In install-mode layouts (PyPI wheel, ~/.openclaw/plugins/cache/...), this
|
|
33
|
+
# fixed depth doesn't hold. Guarding the index lookup keeps module import
|
|
34
|
+
# from crashing on unfamiliar layouts — the env marker is the primary
|
|
35
|
+
# signal there. ``OPENCLAW_SMART_REFLEXIO_DIR`` lets callers (and tests)
|
|
36
|
+
# override the path without touching the module.
|
|
37
|
+
_THIS_DIR = Path(__file__).resolve().parent
|
|
38
|
+
_override_reflexio_dir = os.environ.get("OPENCLAW_SMART_REFLEXIO_DIR")
|
|
39
|
+
if _override_reflexio_dir:
|
|
40
|
+
_REFLEXIO_DIR: Path = Path(_override_reflexio_dir).resolve()
|
|
41
|
+
else:
|
|
42
|
+
_parents = _THIS_DIR.parents
|
|
43
|
+
# In an unexpected layout we fall back to _THIS_DIR itself, which makes
|
|
44
|
+
# the relative_to() check below match (correctly) only when cwd is
|
|
45
|
+
# literally inside this module's own directory — effectively a no-op
|
|
46
|
+
# fence, ceding all detection responsibility to the env marker.
|
|
47
|
+
_REFLEXIO_DIR = _parents[5] if len(_parents) > 5 else _THIS_DIR
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def is_internal_invocation(payload: dict[str, Any]) -> bool:
|
|
51
|
+
"""True if this hook fire originated from reflexio's own LLM provider.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
payload (dict[str, Any]): Parsed openClaw hook payload. Only ``cwd``
|
|
55
|
+
(or ``workspaceDir``) is inspected.
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
bool: True when the env marker is set or ``cwd`` points inside the
|
|
59
|
+
reflexio repository. False otherwise, including when ``cwd`` is
|
|
60
|
+
missing or unresolvable.
|
|
61
|
+
"""
|
|
62
|
+
if os.environ.get(INTERNAL_ENV) == "1":
|
|
63
|
+
return True
|
|
64
|
+
cwd = payload.get("cwd") or payload.get("workspaceDir")
|
|
65
|
+
if not isinstance(cwd, str) or not cwd:
|
|
66
|
+
return False
|
|
67
|
+
try:
|
|
68
|
+
resolved = Path(cwd).resolve()
|
|
69
|
+
except OSError:
|
|
70
|
+
return False
|
|
71
|
+
try:
|
|
72
|
+
resolved.relative_to(_REFLEXIO_DIR)
|
|
73
|
+
except ValueError:
|
|
74
|
+
return False
|
|
75
|
+
return True
|