claude-smart 0.2.45 → 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 +5 -1
- package/plugin/scripts/backend-service.sh +22 -2
- package/plugin/scripts/ensure-plugin-root.sh +4 -4
- package/plugin/scripts/hook_entry.sh +1 -1
- package/plugin/scripts/opencode-claude-compat +4 -0
- package/plugin/scripts/opencode-claude-compat.cmd +3 -0
- package/plugin/scripts/opencode-claude-compat.js +225 -0
- package/plugin/scripts/smart-install.sh +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,215 @@
|
|
|
1
|
+
from abc import abstractmethod
|
|
2
|
+
from typing import Literal
|
|
3
|
+
|
|
4
|
+
from reflexio.models.api_schema.domain.entities import LineageContext, LineageEvent
|
|
5
|
+
|
|
6
|
+
EntityType = Literal["user_playbook", "agent_playbook", "profile"]
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class LineageEventMixin:
|
|
10
|
+
"""Abstract storage interface for the append-only, content-free lineage log."""
|
|
11
|
+
|
|
12
|
+
@abstractmethod
|
|
13
|
+
def append_lineage_event(self, event: LineageEvent) -> int:
|
|
14
|
+
"""Append an event; idempotent on (org_id, entity_type, entity_id, op, request_id).
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
event (LineageEvent): The fully-formed event to persist. ``event_id``
|
|
18
|
+
may be 0; the storage layer assigns a real id on insert. On a
|
|
19
|
+
duplicate ``(org_id, entity_type, entity_id, op, request_id)`` the existing row
|
|
20
|
+
is returned unchanged.
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
int: The assigned or existing ``event_id``.
|
|
24
|
+
|
|
25
|
+
Note:
|
|
26
|
+
This method deliberately does NOT enforce a non-empty ``request_id``.
|
|
27
|
+
System and GC events (e.g. ``hard_delete`` from TTL GC, ``status_change``
|
|
28
|
+
from internal transitions) legitimately use an auto-generated UUID that
|
|
29
|
+
need not be tied to a user-facing request id. Callers that require
|
|
30
|
+
request-scoped lineage (``merge_records``, ``supersede_record``) enforce
|
|
31
|
+
non-empty ``request_id`` themselves.
|
|
32
|
+
"""
|
|
33
|
+
raise NotImplementedError
|
|
34
|
+
|
|
35
|
+
@abstractmethod
|
|
36
|
+
def get_lineage_events(
|
|
37
|
+
self,
|
|
38
|
+
*,
|
|
39
|
+
entity_type: str | None = None,
|
|
40
|
+
entity_id: str | None = None,
|
|
41
|
+
org_id: str | None = None,
|
|
42
|
+
request_id: str | None = None,
|
|
43
|
+
) -> list[LineageEvent]:
|
|
44
|
+
"""Retrieve lineage events, optionally filtered.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
entity_type (str | None): Filter to events for this entity type. If
|
|
48
|
+
None, no entity_type filter is applied.
|
|
49
|
+
entity_id (str | None): Filter to events for this entity id. If None,
|
|
50
|
+
no entity_id filter is applied.
|
|
51
|
+
org_id (str | None): Filter to events for this org. If None, no
|
|
52
|
+
org_id filter is applied.
|
|
53
|
+
request_id (str | None): Filter to events for this request id. If
|
|
54
|
+
None, no request_id filter is applied.
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
list[LineageEvent]: Matching events ordered by ``event_id`` ascending.
|
|
58
|
+
|
|
59
|
+
Note:
|
|
60
|
+
Enterprise/Supabase overrides must also apply the ``request_id`` filter
|
|
61
|
+
to maintain contract parity with the SQLite implementation (B3b T3).
|
|
62
|
+
"""
|
|
63
|
+
raise NotImplementedError
|
|
64
|
+
|
|
65
|
+
@abstractmethod
|
|
66
|
+
def merge_records(
|
|
67
|
+
self,
|
|
68
|
+
*,
|
|
69
|
+
entity_type: EntityType,
|
|
70
|
+
survivor_id: str,
|
|
71
|
+
source_ids: list[str],
|
|
72
|
+
context: LineageContext,
|
|
73
|
+
) -> None:
|
|
74
|
+
"""Soft-delete each source into the survivor in one atomic transaction.
|
|
75
|
+
|
|
76
|
+
Sets ``status=MERGED`` and ``merged_into=survivor_id`` on each source
|
|
77
|
+
whose status is not already a tombstone (MERGED or SUPERSEDED). Appends
|
|
78
|
+
a single ``merge`` lineage event keyed on ``survivor_id``. Idempotent —
|
|
79
|
+
re-running on already-tombstoned sources is a no-op.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
entity_type (str): One of ``"user_playbook"``, ``"agent_playbook"``,
|
|
83
|
+
or ``"profile"``.
|
|
84
|
+
survivor_id (str): The id of the record that survives the merge.
|
|
85
|
+
source_ids (list[str]): Ids of records to tombstone as merged.
|
|
86
|
+
context (LineageContext): Caller-supplied intent (actor, reason, etc.).
|
|
87
|
+
|
|
88
|
+
Raises:
|
|
89
|
+
ValueError: If ``context.request_id`` is empty or whitespace-only.
|
|
90
|
+
"""
|
|
91
|
+
raise NotImplementedError
|
|
92
|
+
|
|
93
|
+
@abstractmethod
|
|
94
|
+
def supersede_record(
|
|
95
|
+
self,
|
|
96
|
+
*,
|
|
97
|
+
entity_type: EntityType,
|
|
98
|
+
incumbent_id: str,
|
|
99
|
+
successor_id: str,
|
|
100
|
+
context: LineageContext,
|
|
101
|
+
) -> bool:
|
|
102
|
+
"""Atomically replace the incumbent with the successor if incumbent is CURRENT.
|
|
103
|
+
|
|
104
|
+
Sets ``status=SUPERSEDED`` and ``superseded_by=successor_id`` on the
|
|
105
|
+
incumbent **only** when its ``status IS NULL`` (CURRENT). Appends a
|
|
106
|
+
``revise`` lineage event keyed on ``successor_id`` when the guard
|
|
107
|
+
succeeds. Returns ``False`` without mutating anything when the incumbent
|
|
108
|
+
is not CURRENT (its status is already set).
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
entity_type (str): One of ``"user_playbook"``, ``"agent_playbook"``,
|
|
112
|
+
or ``"profile"``.
|
|
113
|
+
incumbent_id (str): The id of the record to supersede.
|
|
114
|
+
successor_id (str): The id of the record that replaces the incumbent.
|
|
115
|
+
context (LineageContext): Caller-supplied intent (actor, reason, etc.).
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
bool: ``True`` if the incumbent was CURRENT and was superseded;
|
|
119
|
+
``False`` if the incumbent was not CURRENT and no mutation occurred.
|
|
120
|
+
|
|
121
|
+
Raises:
|
|
122
|
+
ValueError: If ``context.request_id`` is empty or whitespace-only.
|
|
123
|
+
"""
|
|
124
|
+
raise NotImplementedError
|
|
125
|
+
|
|
126
|
+
@abstractmethod
|
|
127
|
+
def has_inbound_lineage_refs(
|
|
128
|
+
self, *, entity_type: EntityType, entity_id: str
|
|
129
|
+
) -> bool:
|
|
130
|
+
"""Return True if any row points at ``entity_id`` via merged_into/superseded_by.
|
|
131
|
+
|
|
132
|
+
Org-scoped but deliberately NOT user_id-scoped: a cross-user chain
|
|
133
|
+
(one user's tombstone pointing at another user's survivor) must be
|
|
134
|
+
detected so the survivor is purged, not hard-deleted, on erasure.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
entity_type (EntityType): One of ``"user_playbook"``, ``"agent_playbook"``,
|
|
138
|
+
or ``"profile"``.
|
|
139
|
+
entity_id (str): The entity's primary key to check for inbound refs.
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
bool: True if any row in the entity's table has ``merged_into == entity_id``
|
|
143
|
+
OR ``superseded_by == entity_id``; False otherwise.
|
|
144
|
+
"""
|
|
145
|
+
raise NotImplementedError
|
|
146
|
+
|
|
147
|
+
@abstractmethod
|
|
148
|
+
def purge_content(self, *, entity_type: EntityType, entity_id: str) -> bool:
|
|
149
|
+
"""Blank a record's PII body, keep its lineage skeleton, emit op=purge.
|
|
150
|
+
|
|
151
|
+
Keeps only ``{pk, status, merged_into, superseded_by, retired_at}`` and
|
|
152
|
+
non-PII bookkeeping columns (timestamps, TTL); blanks every other column
|
|
153
|
+
(text → ``''``, nullable → ``NULL``; ``user_id`` → ``''``). Irreversible.
|
|
154
|
+
Emits a single ``op=purge`` lineage event with a deterministic
|
|
155
|
+
``request_id="purge_{entity_id}"`` so repeated calls are idempotent —
|
|
156
|
+
an already-blank row re-runs without recording a duplicate event.
|
|
157
|
+
Returns ``True`` if a row existed (whether or not new blanking was done).
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
entity_type (EntityType): One of ``"user_playbook"`` or ``"profile"``.
|
|
161
|
+
``"agent_playbook"`` is **not** supported (agent playbooks have no
|
|
162
|
+
``user_id`` and are out of scope for content purge) — implementations
|
|
163
|
+
must raise ``ValueError`` for that value.
|
|
164
|
+
entity_id (str): The entity's primary key as a string.
|
|
165
|
+
|
|
166
|
+
Returns:
|
|
167
|
+
bool: ``True`` if the row exists; ``False`` if the id had no matching row.
|
|
168
|
+
|
|
169
|
+
Raises:
|
|
170
|
+
ValueError: If ``entity_type`` is not a recognized entity type or is
|
|
171
|
+
``"agent_playbook"``.
|
|
172
|
+
"""
|
|
173
|
+
raise NotImplementedError
|
|
174
|
+
|
|
175
|
+
@abstractmethod
|
|
176
|
+
def gc_expired_tombstones(
|
|
177
|
+
self, *, entity_type: str, older_than_epoch: int, limit: int = 1000
|
|
178
|
+
) -> int:
|
|
179
|
+
"""Hard-delete tombstone rows that are older than the given epoch cutoff.
|
|
180
|
+
|
|
181
|
+
Emits one ``hard_delete`` lineage event per deleted row before deleting it,
|
|
182
|
+
all within a single atomic transaction. Rows on legal hold are skipped.
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
entity_type (str): One of ``"user_playbook"``, ``"agent_playbook"``,
|
|
186
|
+
or ``"profile"``.
|
|
187
|
+
older_than_epoch (int): Unix timestamp. Rows whose age column value
|
|
188
|
+
is strictly less than this cutoff are eligible.
|
|
189
|
+
limit (int): Maximum number of rows to delete in one call. Defaults
|
|
190
|
+
to 1000.
|
|
191
|
+
|
|
192
|
+
Returns:
|
|
193
|
+
int: The number of rows physically deleted.
|
|
194
|
+
|
|
195
|
+
Raises:
|
|
196
|
+
ValueError: If ``entity_type`` is not a recognized entity type.
|
|
197
|
+
"""
|
|
198
|
+
raise NotImplementedError
|
|
199
|
+
|
|
200
|
+
def list_org_ids(self) -> list[str]:
|
|
201
|
+
"""Return every distinct org_id known to this storage instance.
|
|
202
|
+
|
|
203
|
+
Used by :class:`LineageGCScheduler` to enumerate all tenants so GC
|
|
204
|
+
runs for every org, not just the bootstrap org.
|
|
205
|
+
|
|
206
|
+
Returns:
|
|
207
|
+
list[str]: Distinct org ids, order unspecified.
|
|
208
|
+
|
|
209
|
+
Raises:
|
|
210
|
+
NotImplementedError: If the backend has not yet implemented this
|
|
211
|
+
method (enterprise backends owe this in B2 Task 6).
|
|
212
|
+
"""
|
|
213
|
+
raise NotImplementedError(
|
|
214
|
+
f"{type(self).__name__} does not implement list_org_ids"
|
|
215
|
+
)
|
|
@@ -168,3 +168,23 @@ class OperationMixin:
|
|
|
168
168
|
- 'state' (dict): The current operation state after the operation
|
|
169
169
|
"""
|
|
170
170
|
raise NotImplementedError
|
|
171
|
+
|
|
172
|
+
@abstractmethod
|
|
173
|
+
def clear_in_progress_lock_if_owner(
|
|
174
|
+
self,
|
|
175
|
+
state_key: str,
|
|
176
|
+
request_id: str,
|
|
177
|
+
cleared_state: dict,
|
|
178
|
+
) -> bool:
|
|
179
|
+
"""Atomically clear an in-progress lock only if ``request_id`` still owns it.
|
|
180
|
+
|
|
181
|
+
Args:
|
|
182
|
+
state_key: Operation-state row key for the lock.
|
|
183
|
+
request_id: Request attempting to release the lock.
|
|
184
|
+
cleared_state: Replacement operation-state payload to write on success.
|
|
185
|
+
|
|
186
|
+
Returns:
|
|
187
|
+
bool: ``True`` when the caller still owned the lock and the clear
|
|
188
|
+
was applied, else ``False``.
|
|
189
|
+
"""
|
|
190
|
+
raise NotImplementedError
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import logging
|
|
1
2
|
from abc import abstractmethod
|
|
3
|
+
from collections.abc import Sequence
|
|
2
4
|
|
|
3
5
|
from reflexio.models.api_schema.common import BlockingIssue
|
|
4
6
|
from reflexio.models.api_schema.domain import (
|
|
@@ -13,11 +15,22 @@ from reflexio.models.api_schema.domain import (
|
|
|
13
15
|
Status,
|
|
14
16
|
UserPlaybook,
|
|
15
17
|
)
|
|
18
|
+
from reflexio.models.api_schema.domain.entities import LineageEvent
|
|
16
19
|
from reflexio.models.api_schema.retriever_schema import (
|
|
17
20
|
SearchAgentPlaybookRequest,
|
|
18
21
|
SearchUserPlaybookRequest,
|
|
19
22
|
)
|
|
20
23
|
from reflexio.models.config_schema import SearchOptions
|
|
24
|
+
from reflexio.server.tracing import capture_anomaly
|
|
25
|
+
|
|
26
|
+
logger = logging.getLogger(__name__)
|
|
27
|
+
|
|
28
|
+
_AGGREGATE_EVENT_EMIT_ATTEMPTS = 3
|
|
29
|
+
|
|
30
|
+
# Shared prefix for aggregate lineage event reasons.
|
|
31
|
+
# Consumers: storage_base (here), sqlite_storage/_playbook.py, and lib/_agent_playbook.py
|
|
32
|
+
# (which imports this constant to keep the parser and producers in sync).
|
|
33
|
+
AGGREGATE_REASON_PREFIX = "aggregate:"
|
|
21
34
|
|
|
22
35
|
|
|
23
36
|
class PlaybookMixin:
|
|
@@ -42,6 +55,7 @@ class PlaybookMixin:
|
|
|
42
55
|
start_time: int | None = None,
|
|
43
56
|
end_time: int | None = None,
|
|
44
57
|
include_embedding: bool = False,
|
|
58
|
+
tags: list[str] | None = None,
|
|
45
59
|
) -> list[UserPlaybook]:
|
|
46
60
|
"""Get user playbooks from storage.
|
|
47
61
|
|
|
@@ -56,6 +70,7 @@ class PlaybookMixin:
|
|
|
56
70
|
start_time (int, optional): Unix timestamp. Only return playbooks created at or after this time.
|
|
57
71
|
end_time (int, optional): Unix timestamp. Only return playbooks created at or before this time.
|
|
58
72
|
include_embedding (bool): If True, fetch and parse embedding vectors. Defaults to False.
|
|
73
|
+
tags (list[str], optional): Match playbooks having any of these tags.
|
|
59
74
|
|
|
60
75
|
Returns:
|
|
61
76
|
list[UserPlaybook]: List of user playbook objects
|
|
@@ -166,11 +181,17 @@ class PlaybookMixin:
|
|
|
166
181
|
raise NotImplementedError
|
|
167
182
|
|
|
168
183
|
@abstractmethod
|
|
169
|
-
def delete_user_playbooks_by_ids(
|
|
184
|
+
def delete_user_playbooks_by_ids(
|
|
185
|
+
self, user_playbook_ids: list[int], *, emit_hard_delete: bool = True
|
|
186
|
+
) -> int:
|
|
170
187
|
"""Delete user playbooks by their IDs.
|
|
171
188
|
|
|
172
189
|
Args:
|
|
173
190
|
user_playbook_ids: List of user_playbook_id values to delete
|
|
191
|
+
emit_hard_delete: When True (default), append a ``hard_delete``
|
|
192
|
+
lineage event per id (genuine erasure). Set False for rollback
|
|
193
|
+
cleanup of a never-live row (e.g. a lost supersede CAS), so no
|
|
194
|
+
spurious audit event is recorded.
|
|
174
195
|
|
|
175
196
|
Returns:
|
|
176
197
|
int: Number of user playbooks deleted
|
|
@@ -206,8 +227,19 @@ class PlaybookMixin:
|
|
|
206
227
|
raise NotImplementedError
|
|
207
228
|
|
|
208
229
|
@abstractmethod
|
|
209
|
-
def get_user_playbook_by_id(
|
|
210
|
-
|
|
230
|
+
def get_user_playbook_by_id(
|
|
231
|
+
self, user_playbook_id: int, *, include_tombstones: bool = False
|
|
232
|
+
) -> UserPlaybook | None:
|
|
233
|
+
"""Fetch one user playbook by primary key.
|
|
234
|
+
|
|
235
|
+
Args:
|
|
236
|
+
user_playbook_id: The user_playbook_id to look up.
|
|
237
|
+
include_tombstones: When False (default), MERGED/SUPERSEDED rows
|
|
238
|
+
return None. Set to True for lineage resolution (resolve_current).
|
|
239
|
+
|
|
240
|
+
Returns:
|
|
241
|
+
The UserPlaybook if found and not filtered, otherwise None.
|
|
242
|
+
"""
|
|
211
243
|
raise NotImplementedError
|
|
212
244
|
|
|
213
245
|
@abstractmethod
|
|
@@ -274,6 +306,77 @@ class PlaybookMixin:
|
|
|
274
306
|
"""
|
|
275
307
|
raise NotImplementedError
|
|
276
308
|
|
|
309
|
+
def save_agent_playbook_with_aggregate_event(
|
|
310
|
+
self,
|
|
311
|
+
agent_playbook: AgentPlaybook,
|
|
312
|
+
*,
|
|
313
|
+
source_ids: list[str],
|
|
314
|
+
request_id: str,
|
|
315
|
+
run_mode: str,
|
|
316
|
+
) -> AgentPlaybook:
|
|
317
|
+
"""Persist an agent playbook AND its ``op=aggregate`` lineage event.
|
|
318
|
+
|
|
319
|
+
Backends SHOULD override this so the row insert and the event commit in ONE
|
|
320
|
+
transaction (the event is the sole record of the run->playbook membership for
|
|
321
|
+
reconstruction). This base default is a non-atomic save-then-emit fallback
|
|
322
|
+
with bounded retry + loud (level=error) on final failure.
|
|
323
|
+
|
|
324
|
+
Args:
|
|
325
|
+
agent_playbook (AgentPlaybook): The playbook to persist.
|
|
326
|
+
source_ids (list[str]): IDs of the source entities that produced this playbook.
|
|
327
|
+
request_id (str): The aggregation run ID (used as the lineage event request_id).
|
|
328
|
+
run_mode (str): The aggregation run mode (e.g. ``full_archive`` or ``incremental``).
|
|
329
|
+
|
|
330
|
+
Returns:
|
|
331
|
+
AgentPlaybook: The saved playbook with ``agent_playbook_id`` populated.
|
|
332
|
+
|
|
333
|
+
Raises:
|
|
334
|
+
ValueError: If ``request_id`` is empty (would produce an unreconstructable event).
|
|
335
|
+
"""
|
|
336
|
+
if not request_id or not request_id.strip():
|
|
337
|
+
raise ValueError(
|
|
338
|
+
"save_agent_playbook_with_aggregate_event requires a non-empty request_id"
|
|
339
|
+
)
|
|
340
|
+
saved = self.save_agent_playbooks([agent_playbook])[0]
|
|
341
|
+
event = LineageEvent(
|
|
342
|
+
org_id=self.org_id, # type: ignore[attr-defined]
|
|
343
|
+
entity_type="agent_playbook",
|
|
344
|
+
entity_id=str(saved.agent_playbook_id),
|
|
345
|
+
op="aggregate",
|
|
346
|
+
prov_relation="wasDerivedFrom",
|
|
347
|
+
source_ids=source_ids,
|
|
348
|
+
actor="aggregator",
|
|
349
|
+
request_id=request_id,
|
|
350
|
+
reason=f"{AGGREGATE_REASON_PREFIX}{run_mode}",
|
|
351
|
+
)
|
|
352
|
+
# The row is already committed; this default is non-atomic (SQLite overrides it to
|
|
353
|
+
# make the INSERT + event one transaction). The event is the sole reconstruction signal
|
|
354
|
+
# for the run, so make the emit durable: bounded retry (idempotent on retrying the
|
|
355
|
+
# same row's emit — entity_id is a fresh autoincrement per run, so this is NOT
|
|
356
|
+
# cross-run idempotency), and on final failure fail LOUD at level=error so the gap
|
|
357
|
+
# is paged + backfillable rather than silently lost. Never raise — the playbook
|
|
358
|
+
# itself is saved and must not be lost.
|
|
359
|
+
for attempt in range(_AGGREGATE_EVENT_EMIT_ATTEMPTS):
|
|
360
|
+
try:
|
|
361
|
+
self.append_lineage_event(event) # type: ignore[attr-defined]
|
|
362
|
+
return saved
|
|
363
|
+
except Exception: # noqa: BLE001
|
|
364
|
+
logger.warning(
|
|
365
|
+
"aggregate lineage event append failed (attempt %d/%d) for agent_playbook %s",
|
|
366
|
+
attempt + 1,
|
|
367
|
+
_AGGREGATE_EVENT_EMIT_ATTEMPTS,
|
|
368
|
+
saved.agent_playbook_id,
|
|
369
|
+
exc_info=True,
|
|
370
|
+
)
|
|
371
|
+
capture_anomaly(
|
|
372
|
+
"lineage.aggregate.append_failed",
|
|
373
|
+
level="error",
|
|
374
|
+
entity_id=str(saved.agent_playbook_id),
|
|
375
|
+
org_id=self.org_id, # type: ignore[attr-defined]
|
|
376
|
+
request_id=request_id,
|
|
377
|
+
)
|
|
378
|
+
return saved
|
|
379
|
+
|
|
277
380
|
@abstractmethod
|
|
278
381
|
def get_agent_playbooks(
|
|
279
382
|
self,
|
|
@@ -282,6 +385,7 @@ class PlaybookMixin:
|
|
|
282
385
|
agent_version: str | None = None,
|
|
283
386
|
status_filter: list[Status | None] | None = None,
|
|
284
387
|
playbook_status_filter: list[PlaybookStatus] | None = None,
|
|
388
|
+
tags: list[str] | None = None,
|
|
285
389
|
) -> list[AgentPlaybook]:
|
|
286
390
|
"""Get agent playbooks from storage.
|
|
287
391
|
|
|
@@ -292,6 +396,7 @@ class PlaybookMixin:
|
|
|
292
396
|
status_filter (list[Optional[Status]], optional): List of Status values to filter by. None in the list means CURRENT status.
|
|
293
397
|
playbook_status_filter (Optional[list[PlaybookStatus]]): List of PlaybookStatus values to filter by.
|
|
294
398
|
If None, returns all playbook statuses.
|
|
399
|
+
tags (list[str], optional): Match playbooks having any of these tags.
|
|
295
400
|
|
|
296
401
|
Returns:
|
|
297
402
|
list[AgentPlaybook]: List of agent playbook objects
|
|
@@ -299,8 +404,19 @@ class PlaybookMixin:
|
|
|
299
404
|
raise NotImplementedError
|
|
300
405
|
|
|
301
406
|
@abstractmethod
|
|
302
|
-
def get_agent_playbook_by_id(
|
|
303
|
-
|
|
407
|
+
def get_agent_playbook_by_id(
|
|
408
|
+
self, agent_playbook_id: int, *, include_tombstones: bool = False
|
|
409
|
+
) -> AgentPlaybook | None:
|
|
410
|
+
"""Fetch one agent playbook by primary key.
|
|
411
|
+
|
|
412
|
+
Args:
|
|
413
|
+
agent_playbook_id: The agent_playbook_id to look up.
|
|
414
|
+
include_tombstones: When False (default), MERGED/SUPERSEDED rows
|
|
415
|
+
return None. Set to True for lineage resolution (resolve_current).
|
|
416
|
+
|
|
417
|
+
Returns:
|
|
418
|
+
The AgentPlaybook if found and not filtered, otherwise None.
|
|
419
|
+
"""
|
|
304
420
|
raise NotImplementedError
|
|
305
421
|
|
|
306
422
|
@abstractmethod
|
|
@@ -354,6 +470,7 @@ class PlaybookMixin:
|
|
|
354
470
|
rationale: str | None = None,
|
|
355
471
|
blocking_issue: BlockingIssue | None = None,
|
|
356
472
|
playbook_status: PlaybookStatus | None = None,
|
|
473
|
+
tags: list[str] | None = None,
|
|
357
474
|
) -> None:
|
|
358
475
|
"""Update editable fields of an agent playbook. Only non-None fields are updated.
|
|
359
476
|
|
|
@@ -365,6 +482,7 @@ class PlaybookMixin:
|
|
|
365
482
|
rationale (str, optional): New rationale text
|
|
366
483
|
blocking_issue (BlockingIssue, optional): New blocking issue
|
|
367
484
|
playbook_status (PlaybookStatus, optional): New playbook status
|
|
485
|
+
tags (list[str], optional): Replacement tags
|
|
368
486
|
|
|
369
487
|
Raises:
|
|
370
488
|
ValueError: If agent playbook with the given ID is not found
|
|
@@ -380,6 +498,7 @@ class PlaybookMixin:
|
|
|
380
498
|
trigger: str | None = None,
|
|
381
499
|
rationale: str | None = None,
|
|
382
500
|
blocking_issue: BlockingIssue | None = None,
|
|
501
|
+
tags: list[str] | None = None,
|
|
383
502
|
) -> None:
|
|
384
503
|
"""Update editable fields of a user playbook. Only non-None fields are updated.
|
|
385
504
|
|
|
@@ -390,12 +509,34 @@ class PlaybookMixin:
|
|
|
390
509
|
trigger (str, optional): New trigger text
|
|
391
510
|
rationale (str, optional): New rationale text
|
|
392
511
|
blocking_issue (BlockingIssue, optional): New blocking issue
|
|
512
|
+
tags (list[str], optional): Replacement tags
|
|
393
513
|
|
|
394
514
|
Raises:
|
|
395
515
|
ValueError: If user playbook with the given ID is not found
|
|
396
516
|
"""
|
|
397
517
|
raise NotImplementedError
|
|
398
518
|
|
|
519
|
+
@abstractmethod
|
|
520
|
+
def supersede_user_playbooks_by_ids(
|
|
521
|
+
self, user_playbook_ids: list[int], request_id: str
|
|
522
|
+
) -> int:
|
|
523
|
+
"""Soft-delete user playbooks by setting status to SUPERSEDED.
|
|
524
|
+
|
|
525
|
+
Eligible rows (CURRENT, PENDING, or ARCHIVED; not already MERGED /
|
|
526
|
+
SUPERSEDED) are transitioned to SUPERSEDED and emit one status_change
|
|
527
|
+
lineage event under the shared request id. This is the user-playbook
|
|
528
|
+
analogue of the existing agent/profile soft-supersede helpers and
|
|
529
|
+
preserves dead-source content for point-in-time attribution reads.
|
|
530
|
+
|
|
531
|
+
Args:
|
|
532
|
+
user_playbook_ids (list[int]): User playbook ids to supersede.
|
|
533
|
+
request_id (str): Shared request id for all emitted lineage events.
|
|
534
|
+
|
|
535
|
+
Returns:
|
|
536
|
+
int: Number of user playbooks actually updated.
|
|
537
|
+
"""
|
|
538
|
+
raise NotImplementedError
|
|
539
|
+
|
|
399
540
|
@abstractmethod
|
|
400
541
|
def archive_agent_playbooks_by_playbook_name(
|
|
401
542
|
self, playbook_name: str, agent_version: str | None = None
|
|
@@ -419,6 +560,47 @@ class PlaybookMixin:
|
|
|
419
560
|
"""
|
|
420
561
|
raise NotImplementedError
|
|
421
562
|
|
|
563
|
+
@abstractmethod
|
|
564
|
+
def supersede_agent_playbooks_by_ids(
|
|
565
|
+
self, agent_playbook_ids: list[int], request_id: str
|
|
566
|
+
) -> int:
|
|
567
|
+
"""Soft-delete agent playbooks by setting status to SUPERSEDED, emitting set-based lineage.
|
|
568
|
+
|
|
569
|
+
For each eligible id (not APPROVED, not already tombstoned), updates status to
|
|
570
|
+
SUPERSEDED and emits one status_change event under the shared request_id.
|
|
571
|
+
Atomic: mutation and event in one commit, guarded on rowcount.
|
|
572
|
+
FTS/vec rows are NOT removed.
|
|
573
|
+
|
|
574
|
+
Args:
|
|
575
|
+
agent_playbook_ids (list[int]): Agent playbook ids to supersede.
|
|
576
|
+
request_id (str): Shared request id for all emitted lineage events.
|
|
577
|
+
|
|
578
|
+
Returns:
|
|
579
|
+
int: Number of agent playbooks actually updated.
|
|
580
|
+
"""
|
|
581
|
+
raise NotImplementedError
|
|
582
|
+
|
|
583
|
+
@abstractmethod
|
|
584
|
+
def supersede_agent_playbooks_by_playbook_name(
|
|
585
|
+
self, playbook_name: str, agent_version: str | None, request_id: str
|
|
586
|
+
) -> int:
|
|
587
|
+
"""Soft-delete archived agent playbooks by name/version via SUPERSEDED status.
|
|
588
|
+
|
|
589
|
+
Selects rows with playbook_name matching and status='archived', then
|
|
590
|
+
soft-supersedes each one with a status_change lineage event under request_id.
|
|
591
|
+
Atomic: one commit at the end.
|
|
592
|
+
FTS/vec rows are NOT removed.
|
|
593
|
+
|
|
594
|
+
Args:
|
|
595
|
+
playbook_name (str): Playbook name to supersede.
|
|
596
|
+
agent_version (str | None): Agent version filter. None matches all versions.
|
|
597
|
+
request_id (str): Shared request id for all emitted lineage events.
|
|
598
|
+
|
|
599
|
+
Returns:
|
|
600
|
+
int: Number of agent playbooks actually updated.
|
|
601
|
+
"""
|
|
602
|
+
raise NotImplementedError
|
|
603
|
+
|
|
422
604
|
@abstractmethod
|
|
423
605
|
def restore_archived_agent_playbooks_by_playbook_name(
|
|
424
606
|
self, playbook_name: str, agent_version: str | None = None
|
|
@@ -461,6 +643,13 @@ class PlaybookMixin:
|
|
|
461
643
|
"""Return source user playbook ids for an agent playbook."""
|
|
462
644
|
raise NotImplementedError
|
|
463
645
|
|
|
646
|
+
@abstractmethod
|
|
647
|
+
def get_source_user_playbook_ids_for_agent_playbooks(
|
|
648
|
+
self, agent_playbook_ids: Sequence[int]
|
|
649
|
+
) -> dict[int, list[int]]:
|
|
650
|
+
"""Return source user playbook ids keyed by agent playbook id."""
|
|
651
|
+
raise NotImplementedError
|
|
652
|
+
|
|
464
653
|
@abstractmethod
|
|
465
654
|
def set_source_windows_for_agent_playbook(
|
|
466
655
|
self,
|
|
@@ -557,12 +746,18 @@ class PlaybookMixin:
|
|
|
557
746
|
raise NotImplementedError
|
|
558
747
|
|
|
559
748
|
@abstractmethod
|
|
560
|
-
def delete_agent_playbooks_by_ids(
|
|
749
|
+
def delete_agent_playbooks_by_ids(
|
|
750
|
+
self, agent_playbook_ids: list[int], *, emit_hard_delete: bool = True
|
|
751
|
+
) -> None:
|
|
561
752
|
"""Permanently delete agent playbooks by their IDs.
|
|
562
753
|
No-op if agent_playbook_ids is empty.
|
|
563
754
|
|
|
564
755
|
Args:
|
|
565
756
|
agent_playbook_ids (list[int]): List of agent playbook IDs to delete
|
|
757
|
+
emit_hard_delete: When True (default), append a ``hard_delete``
|
|
758
|
+
lineage event per id (genuine erasure). Set False for rollback
|
|
759
|
+
cleanup of a never-live row (e.g. a lost supersede CAS), so no
|
|
760
|
+
spurious audit event is recorded.
|
|
566
761
|
"""
|
|
567
762
|
raise NotImplementedError
|
|
568
763
|
|
|
@@ -634,6 +829,45 @@ class PlaybookMixin:
|
|
|
634
829
|
"""
|
|
635
830
|
raise NotImplementedError
|
|
636
831
|
|
|
832
|
+
def get_agent_success_evaluation_results_in_window(
|
|
833
|
+
self,
|
|
834
|
+
from_ts: int,
|
|
835
|
+
to_ts: int,
|
|
836
|
+
agent_version: str | None = None,
|
|
837
|
+
limit: int | None = None,
|
|
838
|
+
) -> list[AgentSuccessEvaluationResult]:
|
|
839
|
+
"""Return eval results in ``[from_ts, to_ts]``.
|
|
840
|
+
|
|
841
|
+
Default implementation filters the existing latest-results method.
|
|
842
|
+
SQL backends should override so callers do not depend on an arbitrary
|
|
843
|
+
latest-row cap.
|
|
844
|
+
"""
|
|
845
|
+
rows = self.get_agent_success_evaluation_results(
|
|
846
|
+
limit=limit or 10_000,
|
|
847
|
+
agent_version=agent_version,
|
|
848
|
+
)
|
|
849
|
+
return [r for r in rows if from_ts <= r.created_at <= to_ts]
|
|
850
|
+
|
|
851
|
+
def get_agent_success_evaluation_result_ids(
|
|
852
|
+
self,
|
|
853
|
+
user_id: str,
|
|
854
|
+
session_id: str,
|
|
855
|
+
evaluation_name: str,
|
|
856
|
+
agent_version: str,
|
|
857
|
+
) -> list[int]:
|
|
858
|
+
"""Return result ids for one eval identity tuple."""
|
|
859
|
+
rows = self.get_agent_success_evaluation_results(
|
|
860
|
+
limit=10_000,
|
|
861
|
+
agent_version=agent_version,
|
|
862
|
+
)
|
|
863
|
+
return [
|
|
864
|
+
r.result_id
|
|
865
|
+
for r in rows
|
|
866
|
+
if r.user_id == user_id
|
|
867
|
+
and r.session_id == session_id
|
|
868
|
+
and r.evaluation_name == evaluation_name
|
|
869
|
+
]
|
|
870
|
+
|
|
637
871
|
@abstractmethod
|
|
638
872
|
def delete_all_agent_success_evaluation_results(self) -> None:
|
|
639
873
|
"""Delete all agent success evaluation results from storage."""
|
|
@@ -642,13 +876,15 @@ class PlaybookMixin:
|
|
|
642
876
|
@abstractmethod
|
|
643
877
|
def delete_agent_success_evaluation_results_for_session(
|
|
644
878
|
self,
|
|
879
|
+
user_id: str,
|
|
645
880
|
session_id: str,
|
|
646
881
|
evaluation_name: str,
|
|
647
882
|
agent_version: str,
|
|
648
883
|
) -> int:
|
|
649
|
-
"""Delete stored results for (session_id, evaluation_name, agent_version).
|
|
884
|
+
"""Delete stored results for (user_id, session_id, evaluation_name, agent_version).
|
|
650
885
|
|
|
651
886
|
Args:
|
|
887
|
+
user_id (str): User whose session results to clear.
|
|
652
888
|
session_id (str): Session whose results to clear.
|
|
653
889
|
evaluation_name (str): Which evaluator's results to clear.
|
|
654
890
|
agent_version (str): Agent version scope.
|