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,193 @@
|
|
|
1
|
+
"""Process-local scheduler for lineage tombstone garbage collection.
|
|
2
|
+
|
|
3
|
+
The scheduler is per-org and config-gated: each tick it discovers every org,
|
|
4
|
+
reads that org's ``lineage_gc`` config, and — if enabled — hard-deletes
|
|
5
|
+
tombstone rows older than the configured grace window. One org's failure
|
|
6
|
+
never stalls the loop; errors are captured as Sentry anomalies and the
|
|
7
|
+
scheduler continues to the next org.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import logging
|
|
13
|
+
import threading
|
|
14
|
+
import time
|
|
15
|
+
from collections.abc import Callable
|
|
16
|
+
|
|
17
|
+
from reflexio.server.api_endpoints.request_context import RequestContext
|
|
18
|
+
from reflexio.server.tracing import capture_anomaly
|
|
19
|
+
|
|
20
|
+
logger = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
_DEFAULT_POLL_INTERVAL_SECONDS = 86400
|
|
23
|
+
_MIN_POLL_SECONDS = 1
|
|
24
|
+
|
|
25
|
+
# Window-misconfiguration tripwire: if a single tick deletes more than this
|
|
26
|
+
# many tombstones for one org, something is likely wrong with the grace window.
|
|
27
|
+
_HIGH_VOLUME_THRESHOLD = 1000
|
|
28
|
+
|
|
29
|
+
_ENTITY_TYPES = ("user_playbook", "agent_playbook", "profile")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class LineageGCScheduler:
|
|
33
|
+
"""Polling daemon that garbage-collects expired lineage tombstones per org."""
|
|
34
|
+
|
|
35
|
+
def __init__(
|
|
36
|
+
self,
|
|
37
|
+
*,
|
|
38
|
+
request_context_factory: Callable[[str], RequestContext],
|
|
39
|
+
bootstrap_org_id: str,
|
|
40
|
+
) -> None:
|
|
41
|
+
self.request_context_factory = request_context_factory
|
|
42
|
+
self.bootstrap_org_id = bootstrap_org_id
|
|
43
|
+
self._stop_event = threading.Event()
|
|
44
|
+
self._thread: threading.Thread | None = None
|
|
45
|
+
|
|
46
|
+
def start(self) -> None:
|
|
47
|
+
"""Start the daemon thread (idempotent if already running)."""
|
|
48
|
+
if self._thread is not None and self._thread.is_alive():
|
|
49
|
+
return
|
|
50
|
+
self._stop_event.clear()
|
|
51
|
+
self._thread = threading.Thread(
|
|
52
|
+
target=self._run_loop,
|
|
53
|
+
name="reflexio-lineage-gc-scheduler",
|
|
54
|
+
daemon=True,
|
|
55
|
+
)
|
|
56
|
+
self._thread.start()
|
|
57
|
+
logger.info("event=lineage_gc_scheduler_started")
|
|
58
|
+
|
|
59
|
+
def stop(self, *, timeout_seconds: float = 5.0) -> None:
|
|
60
|
+
"""Signal the daemon to stop and wait for it to finish."""
|
|
61
|
+
self._stop_event.set()
|
|
62
|
+
if self._thread is not None:
|
|
63
|
+
self._thread.join(timeout=timeout_seconds)
|
|
64
|
+
self._thread = None
|
|
65
|
+
logger.info("event=lineage_gc_scheduler_stopped")
|
|
66
|
+
|
|
67
|
+
def _discover_org_ids(self, bootstrap_ctx: RequestContext) -> list[str]:
|
|
68
|
+
"""Return every known org, always including the bootstrap org."""
|
|
69
|
+
storage = getattr(bootstrap_ctx, "storage", None)
|
|
70
|
+
org_ids: list[str] = []
|
|
71
|
+
if storage is not None:
|
|
72
|
+
try:
|
|
73
|
+
org_ids = storage.list_org_ids()
|
|
74
|
+
except NotImplementedError:
|
|
75
|
+
logger.warning(
|
|
76
|
+
"event=lineage_gc_list_org_ids_not_implemented "
|
|
77
|
+
"backend=%s bootstrap_org_id=%s — GC will only process bootstrap org",
|
|
78
|
+
type(storage).__name__,
|
|
79
|
+
bootstrap_ctx.org_id,
|
|
80
|
+
)
|
|
81
|
+
org_ids = []
|
|
82
|
+
if bootstrap_ctx.org_id not in org_ids:
|
|
83
|
+
org_ids = [bootstrap_ctx.org_id, *org_ids]
|
|
84
|
+
return org_ids
|
|
85
|
+
|
|
86
|
+
def _gc_tick(self, org_ids: list[str]) -> None:
|
|
87
|
+
"""Run one GC pass across the given org IDs.
|
|
88
|
+
|
|
89
|
+
Factored out of ``_run_loop`` so tests can exercise it without threads.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
org_ids (list[str]): Org IDs to process in this tick.
|
|
93
|
+
"""
|
|
94
|
+
for org_id in org_ids:
|
|
95
|
+
if self._stop_event.is_set():
|
|
96
|
+
break
|
|
97
|
+
try:
|
|
98
|
+
ctx = self.request_context_factory(org_id)
|
|
99
|
+
cfg = ctx.configurator.get_config()
|
|
100
|
+
if not cfg.lineage_gc.enabled:
|
|
101
|
+
continue
|
|
102
|
+
if ctx.storage is None:
|
|
103
|
+
continue
|
|
104
|
+
older_than_epoch = (
|
|
105
|
+
int(time.time())
|
|
106
|
+
- cfg.lineage_gc.tombstone_grace_window_days * 86400
|
|
107
|
+
)
|
|
108
|
+
total_deleted = 0
|
|
109
|
+
for entity_type in _ENTITY_TYPES:
|
|
110
|
+
count = ctx.storage.gc_expired_tombstones(
|
|
111
|
+
entity_type=entity_type,
|
|
112
|
+
older_than_epoch=older_than_epoch,
|
|
113
|
+
)
|
|
114
|
+
total_deleted += count
|
|
115
|
+
if total_deleted:
|
|
116
|
+
logger.info(
|
|
117
|
+
"event=lineage_gc_tick org_id=%s deleted=%d",
|
|
118
|
+
org_id,
|
|
119
|
+
total_deleted,
|
|
120
|
+
)
|
|
121
|
+
if total_deleted > _HIGH_VOLUME_THRESHOLD:
|
|
122
|
+
capture_anomaly(
|
|
123
|
+
"lineage.gc.high_volume",
|
|
124
|
+
org_id=org_id,
|
|
125
|
+
count=total_deleted,
|
|
126
|
+
)
|
|
127
|
+
except Exception:
|
|
128
|
+
capture_anomaly("lineage.gc.run_failed", org_id=org_id)
|
|
129
|
+
logger.exception("event=lineage_gc_org_failed org_id=%s", org_id)
|
|
130
|
+
|
|
131
|
+
def _run_loop(self) -> None:
|
|
132
|
+
while not self._stop_event.is_set():
|
|
133
|
+
poll_interval = _DEFAULT_POLL_INTERVAL_SECONDS
|
|
134
|
+
try:
|
|
135
|
+
bootstrap_ctx = self.request_context_factory(self.bootstrap_org_id)
|
|
136
|
+
cfg = bootstrap_ctx.configurator.get_config()
|
|
137
|
+
poll_interval = cfg.lineage_gc.poll_interval_seconds
|
|
138
|
+
org_ids = self._discover_org_ids(bootstrap_ctx)
|
|
139
|
+
self._gc_tick(org_ids)
|
|
140
|
+
except Exception:
|
|
141
|
+
logger.exception("event=lineage_gc_scheduler_tick_failed")
|
|
142
|
+
self._stop_event.wait(max(poll_interval, _MIN_POLL_SECONDS))
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def maybe_start_lineage_gc(
|
|
146
|
+
request_context_factory: Callable[[str], RequestContext],
|
|
147
|
+
*,
|
|
148
|
+
bootstrap_org_id: str,
|
|
149
|
+
) -> LineageGCScheduler | None:
|
|
150
|
+
"""Start the GC scheduler only when the bootstrap-org config enables it.
|
|
151
|
+
|
|
152
|
+
Off by default. Enablement criteria (must ALL hold before enabling for a
|
|
153
|
+
production org):
|
|
154
|
+
|
|
155
|
+
1. **Mechanism**: GC ages tombstones by ``retired_at`` (the INTEGER epoch
|
|
156
|
+
written at every tombstone write-path). Rows with ``retired_at = NULL``
|
|
157
|
+
(created before the column was added) are never eligible and are retained.
|
|
158
|
+
2. **Grace window**: 90 days is the vetted default (``tombstone_grace_window_days``).
|
|
159
|
+
This is a per-deployment policy knob — do not shorten without reviewing
|
|
160
|
+
PII-lifetime obligations (GDPR Art. 5(1)(e)) and audit-depth requirements.
|
|
161
|
+
3. **B2↔B3 timing gate**: enable per-org only once the grace window is ≥ the
|
|
162
|
+
reconstruction read-back horizon used by B3 changelog replay, OR once B3 is
|
|
163
|
+
fully shipped and the horizon is confirmed. Enabling before this point risks
|
|
164
|
+
GC'ing tombstones the B3 replay still needs.
|
|
165
|
+
4. **DPO sign-off**: obtain sign-off on the PII-lifetime and audit-depth
|
|
166
|
+
implications before enabling in any deployment that processes personal data.
|
|
167
|
+
|
|
168
|
+
Args:
|
|
169
|
+
request_context_factory: Builds an org-scoped :class:`RequestContext`.
|
|
170
|
+
bootstrap_org_id: Org used to read config and seed cross-org discovery.
|
|
171
|
+
|
|
172
|
+
Returns:
|
|
173
|
+
LineageGCScheduler: The started scheduler, or ``None`` if not enabled.
|
|
174
|
+
"""
|
|
175
|
+
try:
|
|
176
|
+
ctx = request_context_factory(bootstrap_org_id)
|
|
177
|
+
cfg = ctx.configurator.get_config()
|
|
178
|
+
if not cfg.lineage_gc.enabled:
|
|
179
|
+
return None
|
|
180
|
+
except Exception as exc:
|
|
181
|
+
logger.warning(
|
|
182
|
+
"event=lineage_gc_scheduler_start_skipped error_type=%s error=%s",
|
|
183
|
+
type(exc).__name__,
|
|
184
|
+
exc,
|
|
185
|
+
)
|
|
186
|
+
return None
|
|
187
|
+
|
|
188
|
+
scheduler = LineageGCScheduler(
|
|
189
|
+
request_context_factory=request_context_factory,
|
|
190
|
+
bootstrap_org_id=bootstrap_org_id,
|
|
191
|
+
)
|
|
192
|
+
scheduler.start()
|
|
193
|
+
return scheduler
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Literal
|
|
4
|
+
|
|
5
|
+
from reflexio.models.api_schema.domain.entities import RecordRef
|
|
6
|
+
from reflexio.server.tracing import capture_anomaly
|
|
7
|
+
|
|
8
|
+
EntityType = Literal["user_playbook", "agent_playbook", "profile"]
|
|
9
|
+
|
|
10
|
+
_GETTER = {
|
|
11
|
+
"user_playbook": lambda s, i: s.get_user_playbook_by_id(
|
|
12
|
+
int(i), include_tombstones=True
|
|
13
|
+
),
|
|
14
|
+
"agent_playbook": lambda s, i: s.get_agent_playbook_by_id(
|
|
15
|
+
int(i), include_tombstones=True
|
|
16
|
+
),
|
|
17
|
+
"profile": lambda s, i: s.get_profile_by_id(str(i), include_tombstones=True),
|
|
18
|
+
}
|
|
19
|
+
_MAX_HOPS = 8 # Phase A: chains are 1-hop; cap guards malformed pointers
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def resolve_current(
|
|
23
|
+
storage: Any, entity_type: EntityType, record_id: Any
|
|
24
|
+
) -> RecordRef | None:
|
|
25
|
+
"""Follow merged_into/superseded_by pointers to the live survivor.
|
|
26
|
+
|
|
27
|
+
**Consumer contract:** if the returned ``RecordRef.is_purged`` is ``True``,
|
|
28
|
+
the survivor's content body has been blanked by ``purge_content`` (GDPR/erasure).
|
|
29
|
+
Any consumer that dereferences the resolved record's content MUST skip or treat
|
|
30
|
+
the record as absent when ``is_purged=True`` — reading blank content as if it
|
|
31
|
+
were valid data is a silent bug.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
storage: Any storage backend exposing get_*_by_id with include_tombstones.
|
|
35
|
+
entity_type: One of "user_playbook", "agent_playbook", "profile".
|
|
36
|
+
record_id: The primary key of the record to resolve (int for playbooks, str for profiles).
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
RecordRef pointing to the live survivor (is_purged=True if its body is blank),
|
|
40
|
+
or None if the record doesn't exist, there's a cycle, or the chain exceeds _MAX_HOPS.
|
|
41
|
+
|
|
42
|
+
Raises:
|
|
43
|
+
ValueError: If ``entity_type`` is not a recognized entity type.
|
|
44
|
+
"""
|
|
45
|
+
get = _GETTER.get(entity_type)
|
|
46
|
+
if get is None:
|
|
47
|
+
raise ValueError(f"unknown entity_type: {entity_type!r}")
|
|
48
|
+
visited: set[str] = set()
|
|
49
|
+
cur = get(storage, record_id)
|
|
50
|
+
if cur is None:
|
|
51
|
+
return None
|
|
52
|
+
while True:
|
|
53
|
+
cur_id = str(_pk(cur, entity_type))
|
|
54
|
+
if cur_id in visited or len(visited) >= _MAX_HOPS:
|
|
55
|
+
# A cycle or an over-long chain means a malformed pointer graph;
|
|
56
|
+
# we return None (callers treat as unresolvable) but surface it to
|
|
57
|
+
# Sentry so the otherwise-silent breakage is observable.
|
|
58
|
+
reason = "cycle" if cur_id in visited else "max_hops_exceeded"
|
|
59
|
+
capture_anomaly(
|
|
60
|
+
f"lineage.resolve_current.{reason}",
|
|
61
|
+
entity_type=entity_type,
|
|
62
|
+
entity_id=str(record_id),
|
|
63
|
+
hops=len(visited),
|
|
64
|
+
)
|
|
65
|
+
return None # cycle or runaway chain
|
|
66
|
+
visited.add(cur_id)
|
|
67
|
+
nxt = cur.merged_into if cur.merged_into is not None else cur.superseded_by
|
|
68
|
+
if nxt is None:
|
|
69
|
+
return RecordRef(id=cur_id, is_purged=(not cur.content))
|
|
70
|
+
nxt_row = get(storage, nxt)
|
|
71
|
+
if nxt_row is None:
|
|
72
|
+
return RecordRef(
|
|
73
|
+
id=cur_id, is_purged=(not cur.content)
|
|
74
|
+
) # dangling pointer — stop here
|
|
75
|
+
cur = nxt_row
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _pk(row: Any, entity_type: EntityType) -> Any:
|
|
79
|
+
"""Extract the primary key from a row based on its entity type.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
row: The entity row object.
|
|
83
|
+
entity_type: One of "user_playbook", "agent_playbook", "profile".
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
The primary key value.
|
|
87
|
+
|
|
88
|
+
Raises:
|
|
89
|
+
ValueError: If ``entity_type`` is not a recognized entity type.
|
|
90
|
+
"""
|
|
91
|
+
pk = {
|
|
92
|
+
"user_playbook": "user_playbook_id",
|
|
93
|
+
"agent_playbook": "agent_playbook_id",
|
|
94
|
+
"profile": "profile_id",
|
|
95
|
+
}.get(entity_type)
|
|
96
|
+
if pk is None:
|
|
97
|
+
raise ValueError(f"unknown entity_type: {entity_type!r}")
|
|
98
|
+
return getattr(row, pk)
|
|
@@ -535,6 +535,32 @@ class OperationStateManager:
|
|
|
535
535
|
state_key,
|
|
536
536
|
)
|
|
537
537
|
|
|
538
|
+
def clear_lock_if_owner(
|
|
539
|
+
self,
|
|
540
|
+
request_id: str,
|
|
541
|
+
scope_id: str | None = None,
|
|
542
|
+
) -> bool:
|
|
543
|
+
"""Atomically clear a lock only if ``request_id`` still owns it."""
|
|
544
|
+
state_key = self._lock_key(scope_id)
|
|
545
|
+
cleared = self.storage.clear_in_progress_lock_if_owner(
|
|
546
|
+
state_key,
|
|
547
|
+
request_id,
|
|
548
|
+
{
|
|
549
|
+
"in_progress": False,
|
|
550
|
+
"current_request_id": None,
|
|
551
|
+
"pending_request_id": None,
|
|
552
|
+
"pending_request_queue": [],
|
|
553
|
+
},
|
|
554
|
+
)
|
|
555
|
+
if cleared:
|
|
556
|
+
logger.debug(
|
|
557
|
+
"Cleared in-progress lock for %s: state_key=%s, request_id=%s",
|
|
558
|
+
self.service_name,
|
|
559
|
+
state_key,
|
|
560
|
+
request_id,
|
|
561
|
+
)
|
|
562
|
+
return cleared
|
|
563
|
+
|
|
538
564
|
def release_lock_pop_queue(
|
|
539
565
|
self,
|
|
540
566
|
request_id: str,
|
|
@@ -5,10 +5,10 @@ Description: Playbook extraction, aggregation, and consolidation pipeline
|
|
|
5
5
|
|
|
6
6
|
## Main Entry Points
|
|
7
7
|
|
|
8
|
-
- **Service Orchestrator**: `
|
|
9
|
-
- **Playbook Extractor**: `
|
|
10
|
-
- **Playbook Aggregator**: `
|
|
11
|
-
- **Playbook Consolidator**: `
|
|
8
|
+
- **Service Orchestrator**: `service.py` - Manages playbook extraction lifecycle (regular, rerun, manual modes)
|
|
9
|
+
- **Playbook Extractor**: `components/extractor.py` - Extracts user playbooks from interactions via LLM
|
|
10
|
+
- **Playbook Aggregator**: `components/aggregator.py` - Clusters similar user playbooks and generates aggregated insights
|
|
11
|
+
- **Playbook Consolidator**: `components/consolidator.py` - Reconciles newly extracted playbooks against existing storage via LLM. Decides per pair to merge as duplicates, prefer the new entry, prefer the existing entry, differentiate (split with refined triggers), or keep both as independent
|
|
12
12
|
|
|
13
13
|
## Supporting Files
|
|
14
14
|
|
|
@@ -16,6 +16,7 @@ Description: Playbook extraction, aggregation, and consolidation pipeline
|
|
|
16
16
|
|------|---------|
|
|
17
17
|
| `playbook_service_constants.py` | Prompt IDs for all playbook operations |
|
|
18
18
|
| `playbook_service_utils.py` | Request dataclasses, Pydantic output schemas, message construction utilities |
|
|
19
|
+
| `user_detail_stripping.py` | Optional aggregation-boundary interfaces and helpers for prompt-only user-detail normalization |
|
|
19
20
|
|
|
20
21
|
## Architecture
|
|
21
22
|
|
|
@@ -30,7 +31,7 @@ Interactions
|
|
|
30
31
|
-> AgentPlaybook (aggregated insights) -> Storage
|
|
31
32
|
```
|
|
32
33
|
|
|
33
|
-
### Playbook Extraction (`
|
|
34
|
+
### Playbook Extraction (`components/extractor.py`)
|
|
34
35
|
|
|
35
36
|
Extends `BaseGenerationService` extractor pattern. Each extractor:
|
|
36
37
|
1. Checks stride_size threshold before running
|
|
@@ -41,20 +42,18 @@ Extends `BaseGenerationService` extractor pattern. Each extractor:
|
|
|
41
42
|
|
|
42
43
|
**Tool Analysis**: Reads `tool_can_use` from root `Config` for tool usage analysis and blocking issue detection.
|
|
43
44
|
|
|
44
|
-
### Playbook Aggregation (`
|
|
45
|
+
### Playbook Aggregation (`components/aggregator.py`)
|
|
45
46
|
|
|
46
47
|
Triggered manually via `/api/run_playbook_aggregation`. Clusters user playbooks and generates consolidated insights.
|
|
47
48
|
|
|
48
49
|
**Key Methods**:
|
|
49
50
|
- `get_clusters(user_playbooks, config)` - HDBSCAN/Agglomerative clustering on embeddings
|
|
50
51
|
- `aggregate()` - Full aggregation pipeline with LLM-based consolidation
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
**Change Log**: After each aggregation, saves a `PlaybookAggregationChangeLog` to storage. In full_archive mode, all old playbooks are "removed" and new ones "added". In incremental mode, maps old->new via fingerprints to detect updates. Saving is best-effort (failures logged, don't block aggregation).
|
|
52
|
+
**Change Log**: The legacy `playbook_aggregation_change_logs` table is retired (Track B, 2026-06-24) — the aggregator no longer writes it. The change-log view is reconstructed on demand from `lineage_event` via `reconstruct_playbook_aggregation_change_log` (`lib/_agent_playbook.py`): each run emits `op=aggregate` events (the "added" side) and `status_change→superseded` events from the supersede calls (the "removed" side), grouped by the run's `request_id`. Per-row `updated` pairing is not reconstructed (`updated_agent_playbooks=[]`, a tolerated parity delta).
|
|
54
53
|
|
|
55
54
|
**Clustering**: Embeds user playbooks -> HDBSCAN clustering -> falls back to Agglomerative if too few clusters
|
|
56
55
|
|
|
57
|
-
### Playbook Consolidation (`
|
|
56
|
+
### Playbook Consolidation (`components/consolidator.py`)
|
|
58
57
|
|
|
59
58
|
Consolidates newly extracted playbooks against existing playbooks in the database via LLM semantic matching. For each NEW vs EXISTING pair the LLM returns one of five decision kinds, and the consolidator applies the chosen kind:
|
|
60
59
|
|
|
@@ -70,10 +69,10 @@ A safety fallback inserts any new candidate that no decision consumed, so extrac
|
|
|
70
69
|
|
|
71
70
|
| Constant | Prompt ID | Used By |
|
|
72
71
|
|----------|-----------|---------|
|
|
73
|
-
| `
|
|
72
|
+
| `PLAYBOOK_SHOULD_GENERATE_PROMPT_ID` | `playbook_should_generate` | PlaybookExtractor |
|
|
74
73
|
| `PLAYBOOK_EXTRACTION_CONTEXT_PROMPT_ID` | `playbook_extraction_context` | PlaybookExtractor |
|
|
75
74
|
| `PLAYBOOK_EXTRACTION_PROMPT_ID` | `playbook_extraction_main` | PlaybookExtractor |
|
|
76
|
-
| `
|
|
75
|
+
| `PLAYBOOK_AGGREGATION_PROMPT_ID` | `playbook_aggregation` | PlaybookAggregator |
|
|
77
76
|
|
|
78
77
|
## Key Output Schemas (in `playbook_service_utils.py`)
|
|
79
78
|
|