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,18 @@
|
|
|
1
|
+
import * as path from "node:path";
|
|
2
|
+
import { defineConfig } from "vitest/config";
|
|
3
|
+
|
|
4
|
+
export default defineConfig({
|
|
5
|
+
test: {
|
|
6
|
+
include: ["tests-ts/**/*.test.ts"],
|
|
7
|
+
},
|
|
8
|
+
resolve: {
|
|
9
|
+
alias: {
|
|
10
|
+
// The real openclaw plugin SDK ships with the host. Under vitest we
|
|
11
|
+
// shim only the surface the plugin imports.
|
|
12
|
+
"openclaw/plugin-sdk/plugin-entry": path.resolve(
|
|
13
|
+
__dirname,
|
|
14
|
+
"tests-ts/__mocks__/plugin-entry-stub.ts",
|
|
15
|
+
),
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
});
|
|
@@ -1,8 +1,19 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from collections import defaultdict
|
|
5
|
+
from typing import Literal, cast
|
|
6
|
+
|
|
1
7
|
from reflexio.lib._base import (
|
|
2
8
|
STORAGE_NOT_CONFIGURED_MSG,
|
|
3
9
|
ReflexioBase,
|
|
4
10
|
_require_storage,
|
|
5
11
|
)
|
|
12
|
+
from reflexio.models.api_schema.domain.entities import (
|
|
13
|
+
PlaybookAggregationChangeLog,
|
|
14
|
+
agent_playbook_to_snapshot,
|
|
15
|
+
)
|
|
16
|
+
from reflexio.models.api_schema.domain.enums import Status
|
|
6
17
|
from reflexio.models.api_schema.retriever_schema import (
|
|
7
18
|
GetAgentPlaybooksRequest,
|
|
8
19
|
GetAgentPlaybooksResponse,
|
|
@@ -24,29 +35,44 @@ from reflexio.models.api_schema.service_schemas import (
|
|
|
24
35
|
PlaybookAggregationChangeLogResponse,
|
|
25
36
|
)
|
|
26
37
|
from reflexio.models.config_schema import SearchOptions
|
|
38
|
+
from reflexio.server.services.storage.storage_base import BaseStorage
|
|
39
|
+
from reflexio.server.services.storage.storage_base._playbook import (
|
|
40
|
+
AGGREGATE_REASON_PREFIX,
|
|
41
|
+
)
|
|
27
42
|
from reflexio.server.tracing import profile_step
|
|
28
43
|
|
|
29
44
|
|
|
30
45
|
class AgentPlaybookMixin(ReflexioBase):
|
|
31
46
|
def get_playbook_aggregation_change_logs(
|
|
32
|
-
self,
|
|
47
|
+
self,
|
|
48
|
+
playbook_name: str,
|
|
49
|
+
agent_version: str,
|
|
33
50
|
) -> PlaybookAggregationChangeLogResponse:
|
|
34
|
-
"""Get playbook aggregation change logs.
|
|
51
|
+
"""Get playbook aggregation change logs, served from the lineage reconstruction.
|
|
52
|
+
|
|
53
|
+
The change-log view is rebuilt on demand from ``lineage_event`` rows via
|
|
54
|
+
:func:`reconstruct_playbook_aggregation_change_log`. The legacy
|
|
55
|
+
``playbook_aggregation_change_logs`` table is no longer read. Results are
|
|
56
|
+
filtered to entries matching ``playbook_name`` and ``agent_version``.
|
|
57
|
+
``updated_agent_playbooks`` is always ``[]`` (tolerated parity delta — Decision 3).
|
|
35
58
|
|
|
36
59
|
Args:
|
|
37
|
-
playbook_name (str):
|
|
38
|
-
agent_version (str):
|
|
60
|
+
playbook_name (str): Filter — only logs for this playbook name are returned.
|
|
61
|
+
agent_version (str): Filter — only logs for this agent version are returned.
|
|
39
62
|
|
|
40
63
|
Returns:
|
|
41
|
-
PlaybookAggregationChangeLogResponse: Response containing
|
|
64
|
+
PlaybookAggregationChangeLogResponse: Response containing the reconstructed
|
|
65
|
+
change logs filtered by playbook_name and agent_version.
|
|
42
66
|
"""
|
|
43
67
|
if not self._is_storage_configured():
|
|
44
68
|
return PlaybookAggregationChangeLogResponse(success=True, change_logs=[])
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
)
|
|
48
|
-
return
|
|
49
|
-
|
|
69
|
+
# Legacy table no longer read; served by reconstruction filtered by
|
|
70
|
+
# playbook_name + agent_version. updated_agent_playbooks is always []
|
|
71
|
+
# (tolerated parity delta).
|
|
72
|
+
return reconstruct_playbook_aggregation_change_log(
|
|
73
|
+
self._get_storage(),
|
|
74
|
+
playbook_name=playbook_name,
|
|
75
|
+
agent_version=agent_version,
|
|
50
76
|
)
|
|
51
77
|
|
|
52
78
|
@_require_storage(DeleteAgentPlaybookResponse)
|
|
@@ -193,6 +219,7 @@ class AgentPlaybookMixin(ReflexioBase):
|
|
|
193
219
|
playbook_status_filter=[request.playbook_status_filter]
|
|
194
220
|
if request.playbook_status_filter
|
|
195
221
|
else None,
|
|
222
|
+
tags=request.tags,
|
|
196
223
|
)
|
|
197
224
|
return GetAgentPlaybooksResponse(
|
|
198
225
|
success=True,
|
|
@@ -308,3 +335,204 @@ class AgentPlaybookMixin(ReflexioBase):
|
|
|
308
335
|
return UpdateAgentPlaybookResponse(
|
|
309
336
|
success=True, msg="Agent playbook updated successfully"
|
|
310
337
|
)
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
# ---------------------------------------------------------------------------
|
|
341
|
+
# Standalone read-side reconstruction (Phase B3b Task 2)
|
|
342
|
+
# ---------------------------------------------------------------------------
|
|
343
|
+
|
|
344
|
+
logger = logging.getLogger(__name__)
|
|
345
|
+
|
|
346
|
+
# Prefix for aggregate lineage event reasons — single source of truth in
|
|
347
|
+
# AGGREGATE_REASON_PREFIX (storage_base/_playbook.py); imported here for the parser.
|
|
348
|
+
_PREFIX = AGGREGATE_REASON_PREFIX
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
def reconstruct_playbook_aggregation_change_log(
|
|
352
|
+
storage: BaseStorage,
|
|
353
|
+
*,
|
|
354
|
+
limit: int = 100,
|
|
355
|
+
playbook_name: str | None = None,
|
|
356
|
+
agent_version: str | None = None,
|
|
357
|
+
) -> PlaybookAggregationChangeLogResponse:
|
|
358
|
+
"""Rebuild the PlaybookAggregationChangeLog view from lineage events.
|
|
359
|
+
|
|
360
|
+
Uses two immutable / stable signals to classify every aggregation run:
|
|
361
|
+
|
|
362
|
+
* **added(R)** — entity_ids of ``aggregate`` lineage events with
|
|
363
|
+
``request_id == R``. Each ``aggregate`` event records one playbook
|
|
364
|
+
saved in the run. The ``reason`` field encodes the run mode:
|
|
365
|
+
``"aggregate:full_archive"`` or ``"aggregate:incremental"``; any other
|
|
366
|
+
value defaults to ``"incremental"``.
|
|
367
|
+
|
|
368
|
+
* **removed(R)** — entity_ids of ``status_change`` lineage events with
|
|
369
|
+
``to_status == "superseded"`` and ``request_id == R``. This is the
|
|
370
|
+
exact signature emitted by ``supersede_agent_playbooks_by_ids`` and
|
|
371
|
+
``supersede_agent_playbooks_by_playbook_name`` (the soft-delete paths).
|
|
372
|
+
APPROVED playbooks are skipped by those helpers, so they have no removal
|
|
373
|
+
signal and are correctly absent from reconstruction.removed.
|
|
374
|
+
|
|
375
|
+
Groups are formed over the union of request_ids from both signals.
|
|
376
|
+
Request_id ``""`` is skipped — it would merge unrelated runs.
|
|
377
|
+
A row is emitted only when ``added or removed`` is non-empty.
|
|
378
|
+
|
|
379
|
+
When a removed playbook's tombstone has been physically purged (GC),
|
|
380
|
+
it is silently omitted from ``removed_agent_playbooks`` rather than crashing.
|
|
381
|
+
|
|
382
|
+
``updated_agent_playbooks = []`` (Decision 3 — tolerated delta; updates
|
|
383
|
+
are folded into added/removed in the B3b reconstruction model).
|
|
384
|
+
|
|
385
|
+
An agent_playbook *added* in run R is included in R's
|
|
386
|
+
``added_agent_playbooks`` even if a later run superseded (tombstoned) it
|
|
387
|
+
(resolved with ``include_tombstones=True``) — so R is not dropped from the
|
|
388
|
+
change log, matching the legacy table and ``reconstruct_profile_change_log``.
|
|
389
|
+
As with the removed side, once a tombstone is physically purged (GC) the
|
|
390
|
+
snapshot resolves to ``None`` and is silently omitted.
|
|
391
|
+
|
|
392
|
+
Version-semantics note (E1): for remove-only runs with mixed agent_version
|
|
393
|
+
across the superseded set, ``agent_version`` is that of the first resolved
|
|
394
|
+
removed snapshot. This is a query-VISIBILITY loss — version-filtered
|
|
395
|
+
queries will omit those removals — not just a label ambiguity.
|
|
396
|
+
|
|
397
|
+
Run-scalars (``playbook_name``, ``agent_version``) are read from the
|
|
398
|
+
reconstructed content: ``added[0]`` is preferred, else ``removed[0]``.
|
|
399
|
+
For a remove-only run whose superseded playbooks span multiple versions,
|
|
400
|
+
``agent_version`` is that of the first resolved removed snapshot — a
|
|
401
|
+
tolerated, documented ambiguity (the aggregate event carries no version
|
|
402
|
+
since remove-only runs emit no aggregate event).
|
|
403
|
+
|
|
404
|
+
Args:
|
|
405
|
+
storage (BaseStorage): Storage instance to query.
|
|
406
|
+
limit (int): Maximum number of reconstructed entries to return.
|
|
407
|
+
Defaults to 100.
|
|
408
|
+
playbook_name (str | None): When provided, only logs whose
|
|
409
|
+
``playbook_name`` matches are returned. Defaults to ``None``
|
|
410
|
+
(no filter).
|
|
411
|
+
agent_version (str | None): When provided, only logs whose
|
|
412
|
+
``agent_version`` matches are returned. Defaults to ``None``
|
|
413
|
+
(no filter).
|
|
414
|
+
|
|
415
|
+
Returns:
|
|
416
|
+
PlaybookAggregationChangeLogResponse: ``success=True`` with
|
|
417
|
+
reconstructed rows ordered most-recent-first (by max event
|
|
418
|
+
``created_at`` in each request_id group), filtered by
|
|
419
|
+
``playbook_name``/``agent_version`` when supplied, and capped
|
|
420
|
+
at ``limit``.
|
|
421
|
+
"""
|
|
422
|
+
if limit <= 0:
|
|
423
|
+
return PlaybookAggregationChangeLogResponse(success=True, change_logs=[])
|
|
424
|
+
|
|
425
|
+
all_events = storage.get_lineage_events(
|
|
426
|
+
entity_type="agent_playbook", org_id=storage.org_id
|
|
427
|
+
)
|
|
428
|
+
|
|
429
|
+
added_by_req: dict[str, list[str]] = defaultdict(list)
|
|
430
|
+
removed_by_req: dict[str, list[str]] = defaultdict(list)
|
|
431
|
+
run_mode_by_req: dict[str, Literal["full_archive", "incremental"]] = {}
|
|
432
|
+
sort_key: dict[str, tuple[int, int]] = {}
|
|
433
|
+
|
|
434
|
+
for evt in all_events:
|
|
435
|
+
req = evt.request_id
|
|
436
|
+
if not req:
|
|
437
|
+
continue # skip empty — never merge unrelated runs
|
|
438
|
+
cur = sort_key.get(req, (0, 0))
|
|
439
|
+
if (evt.created_at, evt.event_id) > cur:
|
|
440
|
+
sort_key[req] = (evt.created_at, evt.event_id)
|
|
441
|
+
if evt.op == "aggregate":
|
|
442
|
+
added_by_req[req].append(evt.entity_id)
|
|
443
|
+
if req not in run_mode_by_req:
|
|
444
|
+
reason = evt.reason or ""
|
|
445
|
+
if reason.startswith(_PREFIX):
|
|
446
|
+
suffix = reason[len(_PREFIX) :]
|
|
447
|
+
run_mode_by_req[req] = cast(
|
|
448
|
+
Literal["full_archive", "incremental"],
|
|
449
|
+
suffix
|
|
450
|
+
if suffix in ("full_archive", "incremental")
|
|
451
|
+
else "incremental",
|
|
452
|
+
)
|
|
453
|
+
else:
|
|
454
|
+
run_mode_by_req[req] = "incremental"
|
|
455
|
+
elif evt.op == "status_change" and evt.to_status == Status.SUPERSEDED.value:
|
|
456
|
+
removed_by_req[req].append(evt.entity_id)
|
|
457
|
+
|
|
458
|
+
candidate_reqs = set(added_by_req) | set(removed_by_req)
|
|
459
|
+
sorted_reqs = sorted(
|
|
460
|
+
candidate_reqs,
|
|
461
|
+
key=lambda r: sort_key.get(r, (0, 0)),
|
|
462
|
+
reverse=True,
|
|
463
|
+
)
|
|
464
|
+
|
|
465
|
+
logs: list[PlaybookAggregationChangeLog] = []
|
|
466
|
+
# PERFORMANCE NOTE (M3): as of Track B retirement this serves the live
|
|
467
|
+
# ``/api/playbook_aggregation_change_logs`` endpoint (the legacy table is no
|
|
468
|
+
# longer read), so it IS on the request path. Two bounds keep it from scaling
|
|
469
|
+
# with full org history: (1) the lineage-event read is paginated in the
|
|
470
|
+
# Supabase backend (``get_lineage_events`` loops ``.range()`` windows), so
|
|
471
|
+
# PostgREST ``max_rows`` can no longer silently truncate to the oldest rows;
|
|
472
|
+
# (2) name/version filtering AND the ``limit`` are applied INSIDE this loop and
|
|
473
|
+
# we ``break`` once ``limit`` matches are collected, so the per-entity
|
|
474
|
+
# ``get_agent_playbook_by_id`` (N+1) resolution stops at the requested page
|
|
475
|
+
# instead of resolving every run in the org. ``sorted_reqs`` is most-recent
|
|
476
|
+
# first, so breaking after ``limit`` matches yields the ``limit`` most-recent
|
|
477
|
+
# matches. Remaining follow-up (tracked, NOT done here): a batch ``*_by_ids``
|
|
478
|
+
# fetch shared with ``reconstruct_profile_change_log``.
|
|
479
|
+
for req in sorted_reqs:
|
|
480
|
+
added = []
|
|
481
|
+
for eid in added_by_req[req]:
|
|
482
|
+
try:
|
|
483
|
+
# include_tombstones: a playbook added in this run but later
|
|
484
|
+
# superseded must still appear in this run's added side (so the
|
|
485
|
+
# run is not dropped) — mirrors the removed-side resolve below.
|
|
486
|
+
pb = storage.get_agent_playbook_by_id(int(eid), include_tombstones=True)
|
|
487
|
+
except (ValueError, TypeError):
|
|
488
|
+
logger.warning(
|
|
489
|
+
"reconstruct: malformed entity_id %r in added_by_req, skipping", eid
|
|
490
|
+
)
|
|
491
|
+
continue
|
|
492
|
+
if pb is not None:
|
|
493
|
+
added.append(agent_playbook_to_snapshot(pb))
|
|
494
|
+
|
|
495
|
+
removed = []
|
|
496
|
+
for eid in removed_by_req[req]:
|
|
497
|
+
try:
|
|
498
|
+
pb = storage.get_agent_playbook_by_id(int(eid), include_tombstones=True)
|
|
499
|
+
except (ValueError, TypeError):
|
|
500
|
+
logger.warning(
|
|
501
|
+
"reconstruct: malformed entity_id %r in removed_by_req, skipping",
|
|
502
|
+
eid,
|
|
503
|
+
)
|
|
504
|
+
continue
|
|
505
|
+
if pb is not None:
|
|
506
|
+
removed.append(agent_playbook_to_snapshot(pb))
|
|
507
|
+
|
|
508
|
+
if not added and not removed:
|
|
509
|
+
continue
|
|
510
|
+
|
|
511
|
+
# agent_version is the run's REPRESENTATIVE snapshot version (added[0], else
|
|
512
|
+
# removed[0]). For a remove-only run whose superseded playbooks span multiple
|
|
513
|
+
# versions, the label is that of the first resolved removed snapshot — a
|
|
514
|
+
# tolerated, documented ambiguity (the aggregate event carries no version).
|
|
515
|
+
first = added[0] if added else removed[0]
|
|
516
|
+
# Filter INSIDE the loop so the ``limit`` is applied to the post-filter
|
|
517
|
+
# set (not a pre-filter slice) AND we can stop once the page is full.
|
|
518
|
+
if playbook_name is not None and first.playbook_name != playbook_name:
|
|
519
|
+
continue
|
|
520
|
+
if agent_version is not None and first.agent_version != agent_version:
|
|
521
|
+
continue
|
|
522
|
+
|
|
523
|
+
ts, _ = sort_key.get(req, (0, 0))
|
|
524
|
+
logs.append(
|
|
525
|
+
PlaybookAggregationChangeLog(
|
|
526
|
+
playbook_name=first.playbook_name,
|
|
527
|
+
agent_version=first.agent_version,
|
|
528
|
+
run_mode=run_mode_by_req.get(req, "incremental"),
|
|
529
|
+
added_agent_playbooks=added,
|
|
530
|
+
removed_agent_playbooks=removed,
|
|
531
|
+
updated_agent_playbooks=[],
|
|
532
|
+
created_at=ts,
|
|
533
|
+
)
|
|
534
|
+
)
|
|
535
|
+
if len(logs) >= limit: # limit > 0 guaranteed above
|
|
536
|
+
break
|
|
537
|
+
|
|
538
|
+
return PlaybookAggregationChangeLogResponse(success=True, change_logs=logs)
|
|
@@ -2,7 +2,7 @@ from typing import Any
|
|
|
2
2
|
|
|
3
3
|
from reflexio.lib._base import ReflexioBase
|
|
4
4
|
from reflexio.models.api_schema.retriever_schema import SetConfigResponse
|
|
5
|
-
from reflexio.models.config_schema import Config
|
|
5
|
+
from reflexio.models.config_schema import Config, StorageConfigManagedSupabase
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class ConfigMixin(ReflexioBase):
|
|
@@ -22,10 +22,17 @@ class ConfigMixin(ReflexioBase):
|
|
|
22
22
|
config = Config(**config)
|
|
23
23
|
|
|
24
24
|
# Validate storage connection before setting config.
|
|
25
|
-
# If no storage_config provided,
|
|
26
|
-
#
|
|
25
|
+
# If no storage_config provided, or the caller round-tripped the
|
|
26
|
+
# redacted platform-managed marker that get_config() emits in place
|
|
27
|
+
# of real credentials, preserve the existing storage configuration
|
|
28
|
+
# (callers like get_config() don't expose storage_config for
|
|
29
|
+
# security). Without this, the marker reaches the readiness check as
|
|
30
|
+
# an unfillable StorageConfigManagedSupabase and fails with
|
|
31
|
+
# "Storage configuration is incomplete".
|
|
27
32
|
storage_config = config.storage_config
|
|
28
|
-
if storage_config is None
|
|
33
|
+
if storage_config is None or isinstance(
|
|
34
|
+
storage_config, StorageConfigManagedSupabase
|
|
35
|
+
):
|
|
29
36
|
storage_config = configurator.get_current_storage_configuration()
|
|
30
37
|
config.storage_config = storage_config
|
|
31
38
|
|
|
@@ -38,17 +38,28 @@ class GenerationMixin(ReflexioBase):
|
|
|
38
38
|
"""
|
|
39
39
|
if not self._is_storage_configured():
|
|
40
40
|
raise ValueError(STORAGE_NOT_CONFIGURED_MSG)
|
|
41
|
-
from reflexio.server.services.playbook.
|
|
41
|
+
from reflexio.server.services.playbook.components.aggregator import (
|
|
42
42
|
PlaybookAggregator,
|
|
43
43
|
)
|
|
44
44
|
from reflexio.server.services.playbook.playbook_service_utils import (
|
|
45
45
|
PlaybookAggregatorRequest,
|
|
46
46
|
)
|
|
47
|
+
from reflexio.server.services.playbook.user_detail_stripping import (
|
|
48
|
+
create_aggregation_user_detail_stripper,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
user_detail_stripper = create_aggregation_user_detail_stripper(
|
|
52
|
+
self.request_context.configurator
|
|
53
|
+
)
|
|
54
|
+
aggregator_kwargs = {}
|
|
55
|
+
if user_detail_stripper is not None:
|
|
56
|
+
aggregator_kwargs["user_detail_stripper"] = user_detail_stripper
|
|
47
57
|
|
|
48
58
|
playbook_aggregator = PlaybookAggregator(
|
|
49
59
|
llm_client=self.llm_client,
|
|
50
60
|
request_context=self.request_context,
|
|
51
61
|
agent_version=agent_version,
|
|
62
|
+
**aggregator_kwargs,
|
|
52
63
|
)
|
|
53
64
|
aggregator_request = PlaybookAggregatorRequest(
|
|
54
65
|
agent_version=agent_version,
|
|
@@ -88,7 +99,7 @@ class GenerationMixin(ReflexioBase):
|
|
|
88
99
|
Returns:
|
|
89
100
|
RerunProfileGenerationResponse: Response containing success status, message, and count of profiles generated
|
|
90
101
|
"""
|
|
91
|
-
from reflexio.server.services.profile.
|
|
102
|
+
from reflexio.server.services.profile.service import (
|
|
92
103
|
ProfileGenerationService,
|
|
93
104
|
)
|
|
94
105
|
|
|
@@ -113,7 +124,7 @@ class GenerationMixin(ReflexioBase):
|
|
|
113
124
|
Returns:
|
|
114
125
|
ManualProfileGenerationResponse: Response containing success status, message, and count of profiles generated
|
|
115
126
|
"""
|
|
116
|
-
from reflexio.server.services.profile.
|
|
127
|
+
from reflexio.server.services.profile.service import (
|
|
117
128
|
ProfileGenerationService,
|
|
118
129
|
)
|
|
119
130
|
|
|
@@ -138,7 +149,7 @@ class GenerationMixin(ReflexioBase):
|
|
|
138
149
|
Returns:
|
|
139
150
|
RerunPlaybookGenerationResponse: Response containing success status, message, and count of playbooks generated
|
|
140
151
|
"""
|
|
141
|
-
from reflexio.server.services.playbook.
|
|
152
|
+
from reflexio.server.services.playbook.service import (
|
|
142
153
|
PlaybookGenerationService,
|
|
143
154
|
)
|
|
144
155
|
|
|
@@ -163,7 +174,7 @@ class GenerationMixin(ReflexioBase):
|
|
|
163
174
|
Returns:
|
|
164
175
|
ManualPlaybookGenerationResponse: Response containing success status, message, and count of playbooks generated
|
|
165
176
|
"""
|
|
166
|
-
from reflexio.server.services.playbook.
|
|
177
|
+
from reflexio.server.services.playbook.service import (
|
|
167
178
|
PlaybookGenerationService,
|
|
168
179
|
)
|
|
169
180
|
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
"""Read-only PostgREST storage reader for change-log reconstruction.
|
|
2
|
+
|
|
3
|
+
``RestStorageReader`` exposes only the read methods that
|
|
4
|
+
``reconstruct_profile_change_log`` calls (the ``ChangeLogReadStorage``
|
|
5
|
+
protocol), sourced from Supabase **PostgREST GETs**. It is deliberately NOT a
|
|
6
|
+
``BaseStorage`` subclass and performs **no writes** — so it can be pointed at a
|
|
7
|
+
production data ref to run the reconstruction without the side effects of
|
|
8
|
+
constructing the real ``SupabaseStorage`` (whose ``__init__`` can mutate
|
|
9
|
+
PostgREST schema config).
|
|
10
|
+
|
|
11
|
+
The HTTP layer is injectable (``fetch``) so the reconstruction pipeline can be
|
|
12
|
+
unit-tested with canned rows and no network.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from collections.abc import Callable
|
|
18
|
+
from urllib.parse import urlparse
|
|
19
|
+
|
|
20
|
+
from reflexio.models.api_schema.domain.entities import (
|
|
21
|
+
LineageEvent,
|
|
22
|
+
UserProfile,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
# Page size for unbounded reads. A read returning a full page may be truncated,
|
|
26
|
+
# which would silently skew the reconstruction — so reaching it flips
|
|
27
|
+
# ``truncated`` and callers can surface the run as untrustworthy.
|
|
28
|
+
_FETCH_CAP = 10_000
|
|
29
|
+
|
|
30
|
+
_LINEAGE_EVENT_FIELDS = set(LineageEvent.model_fields)
|
|
31
|
+
_USER_PROFILE_FIELDS = set(UserProfile.model_fields)
|
|
32
|
+
|
|
33
|
+
# Explicit column list for profiles — avoids pulling the embedding/FTS columns.
|
|
34
|
+
_PROFILE_COLUMNS = (
|
|
35
|
+
"profile_id,user_id,content,last_modified_timestamp,"
|
|
36
|
+
"generated_from_request_id,status,superseded_by,merged_into,source"
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
Fetch = Callable[[str, dict], list[dict]]
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _model[M](cls: type[M], fields: set[str], row: dict) -> M:
|
|
43
|
+
"""Build a Pydantic model from a row, ignoring columns it does not declare."""
|
|
44
|
+
return cls(**{k: v for k, v in row.items() if k in fields})
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class RestStorageReader:
|
|
48
|
+
"""Read-only PostgREST reader implementing the parity-check read surface."""
|
|
49
|
+
|
|
50
|
+
def __init__(
|
|
51
|
+
self,
|
|
52
|
+
base_url: str,
|
|
53
|
+
api_key: str,
|
|
54
|
+
*,
|
|
55
|
+
org_id: str,
|
|
56
|
+
schema: str = "public",
|
|
57
|
+
fetch: Fetch | None = None,
|
|
58
|
+
) -> None:
|
|
59
|
+
# The api_key is a service_role credential (RLS-bypassing). Refuse to
|
|
60
|
+
# attach it to anything but an https Supabase host, so a typo'd or
|
|
61
|
+
# hostile --supabase-url cannot exfiltrate it.
|
|
62
|
+
host = urlparse(base_url).hostname or ""
|
|
63
|
+
if urlparse(base_url).scheme != "https" or not host.endswith(".supabase.co"):
|
|
64
|
+
raise ValueError(
|
|
65
|
+
f"base_url must be an https://<ref>.supabase.co URL; got {base_url!r}"
|
|
66
|
+
)
|
|
67
|
+
self.org_id = org_id
|
|
68
|
+
self._base = base_url.rstrip("/")
|
|
69
|
+
self._key = api_key
|
|
70
|
+
self._schema = schema
|
|
71
|
+
self._fetch: Fetch = fetch or self._http_fetch
|
|
72
|
+
# Set when any unbounded read returns a full _FETCH_CAP page: the
|
|
73
|
+
# reconstruction inputs may be truncated, so the verdict is untrustworthy.
|
|
74
|
+
self.truncated = False
|
|
75
|
+
|
|
76
|
+
def _http_fetch(self, table: str, params: dict) -> list[dict]:
|
|
77
|
+
import requests # noqa: PLC0415 — only needed on the live path
|
|
78
|
+
|
|
79
|
+
headers = {"apikey": self._key, "Authorization": f"Bearer {self._key}"}
|
|
80
|
+
# Non-public schemas (the shared org_<id> cohort) are selected per-request
|
|
81
|
+
# via Accept-Profile; dedicated refs keep data in public.
|
|
82
|
+
if self._schema and self._schema != "public":
|
|
83
|
+
headers["Accept-Profile"] = self._schema
|
|
84
|
+
resp = requests.get(
|
|
85
|
+
f"{self._base}/rest/v1/{table}",
|
|
86
|
+
headers=headers,
|
|
87
|
+
params=params,
|
|
88
|
+
timeout=30,
|
|
89
|
+
allow_redirects=False, # never forward the service_role key on a 3xx
|
|
90
|
+
)
|
|
91
|
+
if resp.is_redirect:
|
|
92
|
+
raise RuntimeError(
|
|
93
|
+
f"PostgREST returned a redirect for {table!r} — refusing to follow it "
|
|
94
|
+
"and forward credentials. Check --supabase-url."
|
|
95
|
+
)
|
|
96
|
+
if resp.status_code in (401, 403):
|
|
97
|
+
raise RuntimeError(
|
|
98
|
+
f"PostgREST {resp.status_code} for {table!r}: the service_role key is "
|
|
99
|
+
"not valid for this data ref (keys are per-ref / per-server)."
|
|
100
|
+
)
|
|
101
|
+
if resp.status_code in (404, 406):
|
|
102
|
+
raise RuntimeError(
|
|
103
|
+
f"PostgREST {resp.status_code} for {table!r}: table/schema not exposed "
|
|
104
|
+
f"— check --schema (got {self._schema!r}; use org_<id> for the shared cohort)."
|
|
105
|
+
)
|
|
106
|
+
resp.raise_for_status()
|
|
107
|
+
return resp.json()
|
|
108
|
+
|
|
109
|
+
def _note_page(self, rows: list[dict]) -> list[dict]:
|
|
110
|
+
"""Flag a possibly-truncated read (a full page may have more behind it)."""
|
|
111
|
+
if len(rows) >= _FETCH_CAP:
|
|
112
|
+
self.truncated = True
|
|
113
|
+
return rows
|
|
114
|
+
|
|
115
|
+
def get_lineage_events(
|
|
116
|
+
self,
|
|
117
|
+
*,
|
|
118
|
+
entity_type: str | None = None,
|
|
119
|
+
entity_id: str | None = None,
|
|
120
|
+
org_id: str | None = None,
|
|
121
|
+
request_id: str | None = None,
|
|
122
|
+
) -> list[LineageEvent]:
|
|
123
|
+
params: dict = {"select": "*", "order": "event_id.asc", "limit": _FETCH_CAP}
|
|
124
|
+
if entity_type:
|
|
125
|
+
params["entity_type"] = f"eq.{entity_type}"
|
|
126
|
+
if entity_id:
|
|
127
|
+
params["entity_id"] = f"eq.{entity_id}"
|
|
128
|
+
if request_id:
|
|
129
|
+
params["request_id"] = f"eq.{request_id}"
|
|
130
|
+
if org_id:
|
|
131
|
+
params["org_id"] = f"eq.{org_id}"
|
|
132
|
+
rows = self._note_page(self._fetch("lineage_event", params))
|
|
133
|
+
return [_model(LineageEvent, _LINEAGE_EVENT_FIELDS, r) for r in rows]
|
|
134
|
+
|
|
135
|
+
def get_distinct_generated_from_request_ids(self) -> list[str]:
|
|
136
|
+
rows = self._note_page(
|
|
137
|
+
self._fetch(
|
|
138
|
+
"profiles",
|
|
139
|
+
{"select": "generated_from_request_id", "limit": _FETCH_CAP},
|
|
140
|
+
)
|
|
141
|
+
)
|
|
142
|
+
return sorted(
|
|
143
|
+
{
|
|
144
|
+
r["generated_from_request_id"]
|
|
145
|
+
for r in rows
|
|
146
|
+
if r.get("generated_from_request_id")
|
|
147
|
+
}
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
def get_profiles_by_generated_from_request_id(
|
|
151
|
+
self, request_id: str
|
|
152
|
+
) -> list[UserProfile]:
|
|
153
|
+
rows = self._note_page(
|
|
154
|
+
self._fetch(
|
|
155
|
+
"profiles",
|
|
156
|
+
{
|
|
157
|
+
"select": _PROFILE_COLUMNS,
|
|
158
|
+
"generated_from_request_id": f"eq.{request_id}",
|
|
159
|
+
"limit": _FETCH_CAP,
|
|
160
|
+
},
|
|
161
|
+
)
|
|
162
|
+
)
|
|
163
|
+
return [_model(UserProfile, _USER_PROFILE_FIELDS, r) for r in rows]
|
|
164
|
+
|
|
165
|
+
def get_all_generated_profiles(self) -> list[UserProfile]:
|
|
166
|
+
# Fetch all profiles in one page and filter non-empty gfr client-side
|
|
167
|
+
# (mirrors get_distinct; avoids PostgREST empty-string operator ambiguity).
|
|
168
|
+
rows = self._note_page(
|
|
169
|
+
self._fetch("profiles", {"select": _PROFILE_COLUMNS, "limit": _FETCH_CAP})
|
|
170
|
+
)
|
|
171
|
+
return [
|
|
172
|
+
_model(UserProfile, _USER_PROFILE_FIELDS, r)
|
|
173
|
+
for r in rows
|
|
174
|
+
if r.get("generated_from_request_id")
|
|
175
|
+
]
|
|
176
|
+
|
|
177
|
+
def get_profile_by_id(
|
|
178
|
+
self,
|
|
179
|
+
profile_id: str,
|
|
180
|
+
*,
|
|
181
|
+
include_tombstones: bool = False, # noqa: ARG002 — id lookup always returns tombstones
|
|
182
|
+
) -> UserProfile | None:
|
|
183
|
+
rows = self._fetch(
|
|
184
|
+
"profiles",
|
|
185
|
+
{"select": _PROFILE_COLUMNS, "profile_id": f"eq.{profile_id}", "limit": 1},
|
|
186
|
+
)
|
|
187
|
+
return _model(UserProfile, _USER_PROFILE_FIELDS, rows[0]) if rows else None
|