claude-smart 0.2.45 → 0.2.47
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +3 -3
- package/README.md +47 -10
- package/bin/claude-smart.js +535 -23
- package/package.json +22 -3
- package/plugin/.claude-plugin/plugin.json +4 -2
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/README.md +27 -6
- package/plugin/commands/clear-all.md +1 -0
- package/plugin/commands/dashboard.md +1 -0
- package/plugin/commands/learn.md +1 -0
- package/plugin/commands/restart.md +1 -0
- package/plugin/commands/show.md +1 -0
- package/plugin/dashboard/app/configure/env/page.tsx +40 -14
- package/plugin/dashboard/app/configure/server/page.tsx +51 -1
- package/plugin/dashboard/app/layout.tsx +20 -0
- package/plugin/dashboard/app/preferences/[id]/page.tsx +8 -8
- package/plugin/dashboard/app/skills/project/[id]/page.tsx +7 -3
- package/plugin/dashboard/app/skills/shared/[id]/page.tsx +10 -3
- package/plugin/dashboard/lib/claude-settings-file.ts +20 -10
- package/plugin/dashboard/lib/reflexio-client.ts +16 -0
- package/plugin/dashboard/lib/status.ts +10 -3
- package/plugin/dashboard/lib/types.ts +18 -6
- package/plugin/hooks/codex-hooks.json +0 -1
- package/plugin/opencode/assistant-buffer.ts +108 -0
- package/plugin/opencode/dist/assistant-buffer.js +96 -0
- package/plugin/opencode/dist/internal.js +12 -0
- package/plugin/opencode/dist/payload.js +85 -0
- package/plugin/opencode/dist/server.mjs +232 -0
- package/plugin/opencode/internal.ts +18 -0
- package/plugin/opencode/package.json +3 -0
- package/plugin/opencode/payload.ts +90 -0
- package/plugin/opencode/server.mts +251 -0
- package/plugin/opencode/tsconfig.json +13 -0
- package/plugin/pyproject.toml +25 -4
- package/plugin/scripts/_lib.sh +5 -1
- package/plugin/scripts/backend-service.sh +22 -2
- package/plugin/scripts/ensure-plugin-root.sh +4 -4
- package/plugin/scripts/hook_entry.sh +1 -1
- package/plugin/scripts/opencode-claude-compat +4 -0
- package/plugin/scripts/opencode-claude-compat.cmd +3 -0
- package/plugin/scripts/opencode-claude-compat.js +225 -0
- package/plugin/scripts/smart-install.sh +17 -9
- package/plugin/src/README.md +1 -1
- package/plugin/src/claude_smart/cli.py +498 -12
- package/plugin/src/claude_smart/context_format.py +9 -9
- package/plugin/src/claude_smart/cs_cite.py +66 -19
- package/plugin/src/claude_smart/env_config.py +12 -4
- package/plugin/src/claude_smart/events/session_start.py +26 -7
- package/plugin/src/claude_smart/events/stop.py +1 -1
- package/plugin/src/claude_smart/ids.py +1 -1
- package/plugin/src/claude_smart/reflexio_adapter.py +5 -5
- package/plugin/src/claude_smart/runtime.py +7 -1
- package/plugin/uv.lock +5 -5
- package/plugin/vendor/reflexio/.env.example +54 -30
- package/plugin/vendor/reflexio/README.md +17 -11
- package/plugin/vendor/reflexio/pyproject.toml +13 -1
- package/plugin/vendor/reflexio/reflexio/README.md +4 -1
- package/plugin/vendor/reflexio/reflexio/cli/README.md +9 -7
- package/plugin/vendor/reflexio/reflexio/cli/__main__.py +10 -1
- package/plugin/vendor/reflexio/reflexio/cli/bootstrap_config.py +22 -5
- package/plugin/vendor/reflexio/reflexio/cli/commands/interactions.py +34 -4
- package/plugin/vendor/reflexio/reflexio/cli/commands/profiles.py +12 -7
- package/plugin/vendor/reflexio/reflexio/cli/commands/services.py +13 -6
- package/plugin/vendor/reflexio/reflexio/cli/commands/setup_cmd.py +212 -52
- package/plugin/vendor/reflexio/reflexio/cli/commands/shortcuts.py +15 -2
- package/plugin/vendor/reflexio/reflexio/cli/env_loader.py +292 -22
- package/plugin/vendor/reflexio/reflexio/cli/log_format.py +29 -4
- package/plugin/vendor/reflexio/reflexio/cli/run_services.py +17 -8
- package/plugin/vendor/reflexio/reflexio/cli/stop_services.py +13 -10
- package/plugin/vendor/reflexio/reflexio/cli/utils.py +44 -1
- package/plugin/vendor/reflexio/reflexio/client/client.py +154 -13
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/README.md +135 -257
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/npm/openclaw-smart/bin/openclaw-smart.js +13 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/npm/openclaw-smart/package.json +15 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/README.md +38 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/index.ts +151 -110
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/openclaw.plugin.json +20 -10
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/package.json +28 -6
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/pyproject.toml +42 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/_lib.sh +371 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/backend-log-runner.sh +33 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/backend-service.sh +271 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/cli.sh +68 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/dashboard-open.sh +15 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/ensure-plugin-root.sh +84 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/hook_entry.sh +106 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/npm-cli.js +219 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/smart-install.sh +269 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/clear-all/SKILL.md +8 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/dashboard/SKILL.md +8 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/learn/SKILL.md +10 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/reflexio/SKILL.md +15 -44
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/restart/SKILL.md +6 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/show/SKILL.md +8 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/cli.py +634 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/context_format.py +224 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/context_inject.py +79 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/after_tool_call.py +160 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/agent_end.py +187 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/before_prompt_build.py +69 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/before_tool_call.py +30 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/session_end.py +36 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/session_start.py +130 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/hook.py +131 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/ids.py +94 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/internal_call.py +75 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/oc_cite.py +196 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/optimizer_assistant.py +272 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/publish.py +96 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/query_compose.py +66 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/reflexio_adapter.py +336 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/runtime.py +47 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/stall_banner.py +61 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/state.py +323 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/integration/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/integration/test_e2e_session_loop.py +190 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/integration/test_publish_to_local_reflexio_integration.py +112 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/integration/test_recursion_guard_integration.py +86 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/integration/test_search_inject_integration.py +144 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_cli.py +184 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_after_tool_call.py +142 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_agent_end.py +233 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_before_prompt_build.py +116 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_before_tool_call.py +35 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_session_end.py +47 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_session_start.py +109 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_hook_dispatch.py +117 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_ids.py +41 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_internal_call.py +50 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_oc_cite.py +88 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_publish.py +48 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_query_compose.py +48 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_reflexio_adapter.py +188 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_runtime.py +41 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_state.py +235 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests-ts/__mocks__/plugin-entry-stub.ts +6 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests-ts/test_npm_cli.test.ts +56 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests-ts/test_shim_dispatch.test.ts +171 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tsconfig.build.json +18 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/{tsconfig.json → plugin/tsconfig.json} +2 -2
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/uv.lock +3835 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/vitest.config.ts +18 -0
- package/plugin/vendor/reflexio/reflexio/lib/_agent_playbook.py +246 -10
- package/plugin/vendor/reflexio/reflexio/lib/_base.py +15 -0
- package/plugin/vendor/reflexio/reflexio/lib/_config.py +11 -4
- package/plugin/vendor/reflexio/reflexio/lib/_generation.py +17 -5
- package/plugin/vendor/reflexio/reflexio/lib/_lineage_parity_readers.py +187 -0
- package/plugin/vendor/reflexio/reflexio/lib/_profiles.py +224 -21
- package/plugin/vendor/reflexio/reflexio/lib/_reflection.py +1 -1
- package/plugin/vendor/reflexio/reflexio/lib/_search.py +52 -25
- package/plugin/vendor/reflexio/reflexio/lib/_user_playbook.py +11 -1
- package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/__init__.py +1 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/entities.py +127 -38
- package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/enums.py +4 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/governance.py +117 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/eval_overview_schema.py +64 -41
- package/plugin/vendor/reflexio/reflexio/models/api_schema/internal_schema.py +19 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/pending_tool_call_schema.py +12 -5
- package/plugin/vendor/reflexio/reflexio/models/api_schema/retriever_schema.py +97 -3
- package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/converters.py +5 -1
- package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/entities.py +5 -1
- package/plugin/vendor/reflexio/reflexio/models/config_schema.py +104 -7
- package/plugin/vendor/reflexio/reflexio/models/structured_output.py +148 -0
- package/plugin/vendor/reflexio/reflexio/server/OVERVIEW.md +3 -3
- package/plugin/vendor/reflexio/reflexio/server/README.md +58 -33
- package/plugin/vendor/reflexio/reflexio/server/__init__.py +29 -5
- package/plugin/vendor/reflexio/reflexio/server/__main__.py +2 -2
- package/plugin/vendor/reflexio/reflexio/server/api.py +899 -103
- package/plugin/vendor/reflexio/reflexio/server/api_endpoints/precondition_checks.py +7 -0
- package/plugin/vendor/reflexio/reflexio/server/api_endpoints/request_context.py +4 -2
- package/plugin/vendor/reflexio/reflexio/server/auth.py +90 -0
- package/plugin/vendor/reflexio/reflexio/server/billing_meter.py +173 -0
- package/plugin/vendor/reflexio/reflexio/server/billing_signals.py +64 -0
- package/plugin/vendor/reflexio/reflexio/server/cache/reflexio_cache.py +46 -16
- package/plugin/vendor/reflexio/reflexio/server/env_utils.py +65 -0
- package/plugin/vendor/reflexio/reflexio/server/extensions.py +213 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/embedding_service.py +19 -4
- package/plugin/vendor/reflexio/reflexio/server/llm/litellm_client.py +264 -77
- package/plugin/vendor/reflexio/reflexio/server/llm/llm_utils.py +69 -7
- package/plugin/vendor/reflexio/reflexio/server/llm/model_defaults.py +4 -4
- package/plugin/vendor/reflexio/reflexio/server/llm/providers/claude_code_provider.py +1 -1
- package/plugin/vendor/reflexio/reflexio/server/llm/providers/embedding_service_provider.py +75 -16
- package/plugin/vendor/reflexio/reflexio/server/llm/providers/openclaw_provider.py +280 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/rerank/cross_encoder_reranker.py +12 -1
- package/plugin/vendor/reflexio/reflexio/server/llm/token_accounting.py +48 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/tools.py +107 -17
- package/plugin/vendor/reflexio/reflexio/server/operation_limiter.py +360 -4
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.6.0.prompt.md +1 -1
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.7.0.prompt.md +85 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_aggregation/v2.2.0.prompt.md +1 -1
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_aggregation/v2.3.0.prompt.md +253 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.3.2.prompt.md +1 -1
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.3.3.prompt.md +74 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.2.3.prompt.md +1 -1
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.3.0.prompt.md +266 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.4.0.prompt.md +278 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.3.0.prompt.md +1 -1
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.4.0.prompt.md +137 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.2.0.prompt.md +1 -1
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.3.0.prompt.md +34 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.0.0.prompt.md +5 -12
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.1.0.prompt.md +5 -12
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.2.0.prompt.md +155 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/tagging/v1.0.0.prompt.md +23 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_manager.py +89 -32
- package/plugin/vendor/reflexio/reflexio/server/services/README.md +28 -11
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/README.md +22 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/__init__.py +1 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_constants.py +4 -2
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_utils.py +1 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/components/__init__.py +7 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/{agent_success_evaluator.py → components/evaluator.py} +3 -16
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/regen_jobs.py +28 -72
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/{group_evaluation_runner.py → runner.py} +12 -10
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/{delayed_group_evaluator.py → scheduler.py} +10 -2
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/{agent_success_evaluation_service.py → service.py} +36 -1
- package/plugin/vendor/reflexio/reflexio/server/services/base_generation_service.py +160 -0
- package/plugin/vendor/reflexio/reflexio/server/services/configurator/base_configurator.py +5 -0
- package/plugin/vendor/reflexio/reflexio/server/services/deduplication_utils.py +1 -1
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/README.md +9 -0
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/components/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/{rule_attribution.py → components/rule_attribution.py} +10 -9
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/eval_sampler.py +15 -24
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/service.py +238 -164
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/README.md +31 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/outcome.py +13 -3
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/pending_tool_call_dispatch.py +3 -2
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/resumable_agent.py +32 -118
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_worker.py +47 -16
- package/plugin/vendor/reflexio/reflexio/server/services/generation_service.py +105 -23
- package/plugin/vendor/reflexio/reflexio/server/services/governance/config.py +52 -0
- package/plugin/vendor/reflexio/reflexio/server/services/governance/service.py +378 -0
- package/plugin/vendor/reflexio/reflexio/server/services/governance/subject_refs.py +34 -0
- package/plugin/vendor/reflexio/reflexio/server/services/lineage/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/server/services/lineage/gc_scheduler.py +219 -0
- package/plugin/vendor/reflexio/reflexio/server/services/lineage/resolve.py +98 -0
- package/plugin/vendor/reflexio/reflexio/server/services/operation_state_utils.py +26 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/README.md +18 -11
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/aggregation_prompt_processing.py +100 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/__init__.py +5 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/{playbook_aggregator.py → components/aggregator.py} +334 -178
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/{playbook_consolidator.py → components/consolidator.py} +168 -62
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/{playbook_extractor.py → components/extractor.py} +5 -1
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_edit_apply.py +74 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_service_utils.py +33 -3
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/{playbook_generation_service.py → service.py} +121 -19
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/README.md +10 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/models.py +2 -1
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/optimizer.py +178 -30
- package/plugin/vendor/reflexio/reflexio/server/services/pre_retrieval/README.md +9 -0
- package/plugin/vendor/reflexio/reflexio/server/services/profile/components/__init__.py +17 -0
- package/plugin/vendor/reflexio/reflexio/server/services/profile/{profile_deduplicator.py → components/consolidator.py} +7 -6
- package/plugin/vendor/reflexio/reflexio/server/services/profile/{profile_extractor.py → components/extractor.py} +31 -22
- package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_generation_service_utils.py +6 -13
- package/plugin/vendor/reflexio/reflexio/server/services/profile/{profile_generation_service.py → service.py} +32 -50
- package/plugin/vendor/reflexio/reflexio/server/services/reflection/__init__.py +4 -8
- package/plugin/vendor/reflexio/reflexio/server/services/reflection/components/__init__.py +7 -0
- package/plugin/vendor/reflexio/reflexio/server/services/reflection/reflection_service_utils.py +12 -1
- package/plugin/vendor/reflexio/reflexio/server/services/reflection/{reflection_service.py → service.py} +36 -16
- package/plugin/vendor/reflexio/reflexio/server/services/retrieval/recency.py +211 -0
- package/plugin/vendor/reflexio/reflexio/server/services/retrieval/relevance_floor.py +99 -6
- package/plugin/vendor/reflexio/reflexio/server/services/service_utils.py +36 -1
- package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/README.md +8 -0
- package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/outcome.py +1 -2
- package/plugin/vendor/reflexio/reflexio/server/services/storage/error.py +31 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/governance_validation.py +681 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/retention.py +9 -9
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/__init__.py +16 -2
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_agent_run.py +4 -1
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_base.py +644 -197
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_extras.py +106 -119
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_governance.py +1965 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_lineage.py +630 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_operations.py +38 -6
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_playbook.py +31 -1369
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_profiles.py +756 -210
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_requests.py +148 -34
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_shadow_verdicts.py +23 -9
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_stall_state.py +5 -2
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/__init__.py +13 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_agent.py +952 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_eval_results.py +189 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_optimization.py +247 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_source_linkage.py +145 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_user.py +838 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/__init__.py +150 -23
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_agent_run.py +2 -8
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_base.py +5 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_extras.py +40 -57
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_governance.py +148 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_lineage.py +215 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_operations.py +20 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_playbook.py +4 -677
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_profiles.py +134 -1
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_requests.py +62 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_retrieval_log.py +51 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_shadow_verdicts.py +17 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/__init__.py +13 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_agent.py +365 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_eval_results.py +124 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_optimization.py +85 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_source_linkage.py +47 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_user.py +333 -0
- package/plugin/vendor/reflexio/reflexio/server/services/tagging/README.md +8 -0
- package/plugin/vendor/reflexio/reflexio/server/services/tagging/__init__.py +1 -0
- package/plugin/vendor/reflexio/reflexio/server/services/tagging/service.py +200 -0
- package/plugin/vendor/reflexio/reflexio/server/services/tagging/tagging_scheduler.py +149 -0
- package/plugin/vendor/reflexio/reflexio/server/services/unified_search_service.py +372 -34
- package/plugin/vendor/reflexio/reflexio/server/site_var/README.md +2 -1
- package/plugin/vendor/reflexio/reflexio/server/site_var/feature_flags.py +120 -1
- package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_sources/feature_flags.json +4 -0
- package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_sources/search_settings.json +5 -0
- package/plugin/vendor/reflexio/reflexio/server/tracing.py +30 -0
- package/plugin/vendor/reflexio/reflexio/server/usage_metrics.py +18 -0
- package/plugin/vendor/reflexio/reflexio/test_support/llm_mock.py +61 -26
- package/plugin/vendor/reflexio/reflexio/test_support/llm_model_registry.py +41 -4
- package/scripts/setup-claude-smart.sh +8 -3
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/TESTING.md +0 -517
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/hook/handler.js +0 -473
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/package-lock.json +0 -2156
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/package.json +0 -18
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/hook/handler.ts +0 -241
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/hook/setup.ts +0 -140
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/publish.ts +0 -113
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/search.ts +0 -52
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/server.ts +0 -103
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/sqlite-buffer.ts +0 -156
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/user-id.ts +0 -134
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/rules/reflexio.md +0 -24
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/publish_clawhub.sh +0 -278
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/references/HOOK.md +0 -164
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/scripts/install.sh +0 -36
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/scripts/uninstall.sh +0 -35
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/publish.test.ts +0 -27
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/search.test.ts +0 -31
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/server.test.ts +0 -42
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/setup.test.ts +0 -49
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/sqlite-buffer.test.ts +0 -91
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/user-id.test.ts +0 -50
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/vitest.config.ts +0 -13
- package/plugin/vendor/reflexio/reflexio/server/_auth.py +0 -25
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/group_aggregation.py +0 -192
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/invariants.py +0 -303
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/plan.py +0 -138
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/tools.py +0 -1125
- /package/plugin/vendor/reflexio/reflexio/integrations/{__init__.py → openclaw/plugin/src/openclaw_smart/__init__.py} +0 -0
- /package/plugin/vendor/reflexio/reflexio/integrations/openclaw/{types → plugin/types}/openclaw.d.ts +0 -0
- /package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/{distribution.py → components/distribution.py} +0 -0
- /package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/{hero_state.py → components/hero_state.py} +0 -0
- /package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/{shadow_aggregation.py → components/shadow_aggregation.py} +0 -0
- /package/plugin/vendor/reflexio/reflexio/server/services/reflection/{reflection_extractor.py → components/extractor.py} +0 -0
package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/group_aggregation.py
DELETED
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
"""Pure aggregation helpers for F2's session-level A/B groups.
|
|
2
|
-
|
|
3
|
-
These functions are deliberately DB-free and LLM-free so they can be unit
|
|
4
|
-
tested in milliseconds and exercised by mutmut. The storage layer hands
|
|
5
|
-
in raw tuples; we hand back SuccessRateTrendByGroup.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
from __future__ import annotations
|
|
9
|
-
|
|
10
|
-
import math
|
|
11
|
-
from collections import defaultdict
|
|
12
|
-
from collections.abc import Iterable
|
|
13
|
-
from enum import StrEnum
|
|
14
|
-
from typing import Any
|
|
15
|
-
|
|
16
|
-
from reflexio.models.api_schema.eval_overview_schema import (
|
|
17
|
-
SuccessRateTrendByGroup,
|
|
18
|
-
TrendPoint,
|
|
19
|
-
)
|
|
20
|
-
|
|
21
|
-
# Z-value for a 95% Wald CI on a binomial proportion (two-sided).
|
|
22
|
-
_Z_95 = 1.96
|
|
23
|
-
|
|
24
|
-
# Cap on the lift CI half-width. With very small sample sizes the Wald CI
|
|
25
|
-
# computes to absurd widths (e.g., ±70pp), and rendering that on a
|
|
26
|
-
# dashboard implies false precision. 0.5 (= ±50pp) is the product-meaningful
|
|
27
|
-
# threshold: any wider than that, the user should think "we don't know"
|
|
28
|
-
# and the frontend should consider rendering a low-confidence indicator.
|
|
29
|
-
_MAX_CI_HALF_WIDTH = 0.5
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
class GroupAssignment(StrEnum):
|
|
33
|
-
"""Group label derived from `Request.metadata.reflexio_retrieval_enabled`."""
|
|
34
|
-
|
|
35
|
-
TREATMENT = "treatment"
|
|
36
|
-
CONTROL = "control"
|
|
37
|
-
UNTAGGED = "untagged"
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def assign_group_from_metadata(metadata: dict[str, Any]) -> GroupAssignment:
|
|
41
|
-
"""
|
|
42
|
-
Bucket a session by its first request's metadata.
|
|
43
|
-
|
|
44
|
-
Reads ``metadata.reflexio_retrieval_enabled`` as a true bool. Anything
|
|
45
|
-
else (missing, string, int, None) maps to UNTAGGED — we never silently
|
|
46
|
-
coerce non-bool values, so customers see how many of their sessions
|
|
47
|
-
are tagged inconsistently.
|
|
48
|
-
|
|
49
|
-
Args:
|
|
50
|
-
metadata (dict[str, Any]): The first request's metadata dict. Non-dict
|
|
51
|
-
input is tolerated and routed to UNTAGGED (defensive — the caller
|
|
52
|
-
may pass a value read from a JSON column with no type guarantee).
|
|
53
|
-
|
|
54
|
-
Returns:
|
|
55
|
-
GroupAssignment: TREATMENT, CONTROL, or UNTAGGED.
|
|
56
|
-
"""
|
|
57
|
-
if not isinstance(metadata, dict):
|
|
58
|
-
return GroupAssignment.UNTAGGED
|
|
59
|
-
value = metadata.get("reflexio_retrieval_enabled")
|
|
60
|
-
if value is True:
|
|
61
|
-
return GroupAssignment.TREATMENT
|
|
62
|
-
if value is False:
|
|
63
|
-
return GroupAssignment.CONTROL
|
|
64
|
-
return GroupAssignment.UNTAGGED
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
def group_session_outcomes_by_metadata(
|
|
68
|
-
outcomes: Iterable[tuple[str, int, bool, dict[str, Any]]],
|
|
69
|
-
) -> dict[GroupAssignment, list[tuple[str, int, bool]]]:
|
|
70
|
-
"""
|
|
71
|
-
Split (session_id, ts, is_success, metadata) tuples into three groups.
|
|
72
|
-
|
|
73
|
-
Args:
|
|
74
|
-
outcomes (Iterable[tuple[str, int, bool, dict[str, Any]]]): Tuples of
|
|
75
|
-
(session_id, ts, is_success, first_request_metadata).
|
|
76
|
-
|
|
77
|
-
Returns:
|
|
78
|
-
dict[GroupAssignment, list[tuple[str, int, bool]]]: Dict keyed by every
|
|
79
|
-
GroupAssignment member (TREATMENT, CONTROL, UNTAGGED) — always present,
|
|
80
|
-
possibly empty list — with values being (session_id, ts, is_success)
|
|
81
|
-
tuples (metadata dropped after binning).
|
|
82
|
-
"""
|
|
83
|
-
out: dict[GroupAssignment, list[tuple[str, int, bool]]] = {
|
|
84
|
-
GroupAssignment.TREATMENT: [],
|
|
85
|
-
GroupAssignment.CONTROL: [],
|
|
86
|
-
GroupAssignment.UNTAGGED: [],
|
|
87
|
-
}
|
|
88
|
-
for session_id, ts, is_success, metadata in outcomes:
|
|
89
|
-
group = assign_group_from_metadata(metadata)
|
|
90
|
-
out[group].append((session_id, ts, is_success))
|
|
91
|
-
return out
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
def bucket_outcomes(
|
|
95
|
-
outcomes: Iterable[tuple[str, int, bool]],
|
|
96
|
-
bucket_seconds: int,
|
|
97
|
-
) -> list[dict[str, int]]:
|
|
98
|
-
"""
|
|
99
|
-
Bucket outcomes by time, returning one dict per bucket.
|
|
100
|
-
|
|
101
|
-
Args:
|
|
102
|
-
outcomes (Iterable[tuple[str, int, bool]]): Tuples of (session_id, ts,
|
|
103
|
-
is_success). ``session_id`` is unused at this stage; kept for future
|
|
104
|
-
per-session breakdowns.
|
|
105
|
-
bucket_seconds (int): Bucket width in seconds (e.g., 86400 for daily,
|
|
106
|
-
604800 for weekly). Must be positive.
|
|
107
|
-
|
|
108
|
-
Returns:
|
|
109
|
-
list[dict[str, int]]: List of ``{"ts": bucket_start, "n": int,
|
|
110
|
-
"successes": int}``, sorted by ``ts`` ascending. Empty buckets are
|
|
111
|
-
omitted.
|
|
112
|
-
|
|
113
|
-
Raises:
|
|
114
|
-
ValueError: When ``bucket_seconds`` is not positive.
|
|
115
|
-
"""
|
|
116
|
-
if bucket_seconds <= 0:
|
|
117
|
-
raise ValueError(f"bucket_seconds must be positive, got {bucket_seconds}")
|
|
118
|
-
counts: dict[int, dict[str, int]] = defaultdict(lambda: {"n": 0, "successes": 0})
|
|
119
|
-
for _, ts, is_success in outcomes:
|
|
120
|
-
bucket_start = (ts // bucket_seconds) * bucket_seconds
|
|
121
|
-
counts[bucket_start]["n"] += 1
|
|
122
|
-
if is_success:
|
|
123
|
-
counts[bucket_start]["successes"] += 1
|
|
124
|
-
return [
|
|
125
|
-
{"ts": bucket_start, "n": d["n"], "successes": d["successes"]}
|
|
126
|
-
for bucket_start, d in sorted(counts.items())
|
|
127
|
-
]
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
def compute_trend_by_group(
|
|
131
|
-
outcomes: Iterable[tuple[str, int, bool, dict[str, Any]]],
|
|
132
|
-
bucket_seconds: int,
|
|
133
|
-
) -> SuccessRateTrendByGroup:
|
|
134
|
-
"""
|
|
135
|
-
Build the dual+untagged-curve trend payload from raw session outcomes.
|
|
136
|
-
|
|
137
|
-
Args:
|
|
138
|
-
outcomes (Iterable[tuple[str, int, bool, dict[str, Any]]]): Tuples of
|
|
139
|
-
(session_id, ts, is_success, first_request_metadata).
|
|
140
|
-
bucket_seconds (int): Bucket width passed to :func:`bucket_outcomes`.
|
|
141
|
-
|
|
142
|
-
Returns:
|
|
143
|
-
SuccessRateTrendByGroup: Three curves (any of which may be empty).
|
|
144
|
-
"""
|
|
145
|
-
grouped = group_session_outcomes_by_metadata(outcomes)
|
|
146
|
-
return SuccessRateTrendByGroup(
|
|
147
|
-
treatment=_to_trend_points(grouped[GroupAssignment.TREATMENT], bucket_seconds),
|
|
148
|
-
control=_to_trend_points(grouped[GroupAssignment.CONTROL], bucket_seconds),
|
|
149
|
-
untagged=_to_trend_points(grouped[GroupAssignment.UNTAGGED], bucket_seconds),
|
|
150
|
-
)
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
def _to_trend_points(
|
|
154
|
-
outcomes: list[tuple[str, int, bool]],
|
|
155
|
-
bucket_seconds: int,
|
|
156
|
-
) -> list[TrendPoint]:
|
|
157
|
-
"""Convert raw outcomes into bucketed TrendPoints; omit empty buckets."""
|
|
158
|
-
return [
|
|
159
|
-
TrendPoint(ts=b["ts"], rate=b["successes"] / b["n"], n=b["n"])
|
|
160
|
-
for b in bucket_outcomes(outcomes, bucket_seconds)
|
|
161
|
-
if b["n"] > 0
|
|
162
|
-
]
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
def compute_lift_with_ci(
|
|
166
|
-
n_t: int, p_t: float, n_c: int, p_c: float
|
|
167
|
-
) -> tuple[float | None, float | None]:
|
|
168
|
-
"""
|
|
169
|
-
Compute success-rate lift (treatment − control) and a 95% CI half-width.
|
|
170
|
-
|
|
171
|
-
Uses the Wald approximation for the difference of two binomial
|
|
172
|
-
proportions. Returns ``(None, None)`` if either group has zero samples —
|
|
173
|
-
surfaces honest "no estimate" rather than fake zeros.
|
|
174
|
-
|
|
175
|
-
Args:
|
|
176
|
-
n_t (int): Treatment group size.
|
|
177
|
-
p_t (float): Treatment success rate, in ``[0.0, 1.0]``.
|
|
178
|
-
n_c (int): Control group size.
|
|
179
|
-
p_c (float): Control success rate, in ``[0.0, 1.0]``.
|
|
180
|
-
|
|
181
|
-
Returns:
|
|
182
|
-
tuple[float | None, float | None]: ``(lift, ci_half_width)`` — both in
|
|
183
|
-
proportion units (e.g., 0.14 for 14pp), or ``(None, None)`` when there
|
|
184
|
-
isn't enough data.
|
|
185
|
-
"""
|
|
186
|
-
if n_t <= 0 or n_c <= 0:
|
|
187
|
-
return None, None
|
|
188
|
-
lift = p_t - p_c
|
|
189
|
-
var = (p_t * (1 - p_t)) / n_t + (p_c * (1 - p_c)) / n_c
|
|
190
|
-
ci_half = _Z_95 * math.sqrt(var)
|
|
191
|
-
ci_half = min(ci_half, _MAX_CI_HALF_WIDTH)
|
|
192
|
-
return lift, ci_half
|
|
@@ -1,303 +0,0 @@
|
|
|
1
|
-
"""Plan-level invariants for the agentic-v2 extraction pipeline.
|
|
2
|
-
|
|
3
|
-
Invariants are pure functions over ``ExtractionCtx``. Hard violations drop
|
|
4
|
-
offending ops from the commit; soft violations are logged and applied.
|
|
5
|
-
See spec §6 for the full catalog and severity policy.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
from __future__ import annotations
|
|
9
|
-
|
|
10
|
-
import logging
|
|
11
|
-
|
|
12
|
-
from reflexio.server.services.extraction.plan import (
|
|
13
|
-
CommitResult,
|
|
14
|
-
CreateUserPlaybookOp,
|
|
15
|
-
CreateUserProfileOp,
|
|
16
|
-
DeleteUserPlaybookOp,
|
|
17
|
-
DeleteUserProfileOp,
|
|
18
|
-
ExtractionCtx,
|
|
19
|
-
Violation,
|
|
20
|
-
)
|
|
21
|
-
|
|
22
|
-
logger = logging.getLogger(__name__)
|
|
23
|
-
|
|
24
|
-
PLAN_SIZE_CAP = 30
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
# --- Hard invariants ---
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
def inv_A_search_before_create(ctx: ExtractionCtx) -> list[Violation]: # noqa: N802
|
|
31
|
-
"""Every CreateOp must be preceded by ≥1 search_* call this run."""
|
|
32
|
-
create_indices = [
|
|
33
|
-
i
|
|
34
|
-
for i, op in enumerate(ctx.plan)
|
|
35
|
-
if isinstance(op, (CreateUserProfileOp, CreateUserPlaybookOp))
|
|
36
|
-
]
|
|
37
|
-
if create_indices and ctx.search_count == 0:
|
|
38
|
-
return [
|
|
39
|
-
Violation(
|
|
40
|
-
code="A",
|
|
41
|
-
severity="hard",
|
|
42
|
-
affected_op_indices=create_indices,
|
|
43
|
-
msg="Plan has create ops but no search was performed this run",
|
|
44
|
-
)
|
|
45
|
-
]
|
|
46
|
-
return []
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
def inv_B_delete_known_id(ctx: ExtractionCtx) -> list[Violation]: # noqa: N802
|
|
50
|
-
"""Every DeleteOp(id) must reference an id in ctx.known_ids.
|
|
51
|
-
|
|
52
|
-
known_ids is populated by search/get/create tool handlers — so deletes
|
|
53
|
-
targeting hallucinated ids (agent never saw them) are rejected.
|
|
54
|
-
"""
|
|
55
|
-
violations: list[Violation] = []
|
|
56
|
-
for i, op in enumerate(ctx.plan):
|
|
57
|
-
if (
|
|
58
|
-
isinstance(op, (DeleteUserProfileOp, DeleteUserPlaybookOp))
|
|
59
|
-
and op.id not in ctx.known_ids
|
|
60
|
-
):
|
|
61
|
-
violations.append(
|
|
62
|
-
Violation(
|
|
63
|
-
code="B",
|
|
64
|
-
severity="hard",
|
|
65
|
-
affected_op_indices=[i],
|
|
66
|
-
msg=f"Delete of unknown id {op.id!r}",
|
|
67
|
-
)
|
|
68
|
-
)
|
|
69
|
-
return violations
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
def inv_D_plan_size_cap(ctx: ExtractionCtx) -> list[Violation]: # noqa: N802
|
|
73
|
-
"""Plan cannot exceed PLAN_SIZE_CAP ops — guards runaway loops."""
|
|
74
|
-
if len(ctx.plan) > PLAN_SIZE_CAP:
|
|
75
|
-
overflow = list(range(PLAN_SIZE_CAP, len(ctx.plan)))
|
|
76
|
-
return [
|
|
77
|
-
Violation(
|
|
78
|
-
code="D",
|
|
79
|
-
severity="hard",
|
|
80
|
-
affected_op_indices=overflow,
|
|
81
|
-
msg=f"Plan size {len(ctx.plan)} exceeds cap {PLAN_SIZE_CAP}",
|
|
82
|
-
)
|
|
83
|
-
]
|
|
84
|
-
return []
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
def inv_F_no_duplicate_deletes(ctx: ExtractionCtx) -> list[Violation]: # noqa: N802
|
|
88
|
-
"""Same id cannot be deleted twice in one plan."""
|
|
89
|
-
seen: set[str] = set()
|
|
90
|
-
violations: list[Violation] = []
|
|
91
|
-
for i, op in enumerate(ctx.plan):
|
|
92
|
-
if isinstance(op, (DeleteUserProfileOp, DeleteUserPlaybookOp)):
|
|
93
|
-
if op.id in seen:
|
|
94
|
-
violations.append(
|
|
95
|
-
Violation(
|
|
96
|
-
code="F",
|
|
97
|
-
severity="hard",
|
|
98
|
-
affected_op_indices=[i],
|
|
99
|
-
msg=f"Duplicate delete of id {op.id!r}",
|
|
100
|
-
)
|
|
101
|
-
)
|
|
102
|
-
else:
|
|
103
|
-
seen.add(op.id)
|
|
104
|
-
return violations
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
def inv_J_scope_match(_ctx: ExtractionCtx) -> list[Violation]: # noqa: N802
|
|
108
|
-
"""User_id scope is primarily enforced at the storage layer (handlers inject
|
|
109
|
-
ctx.user_id). This invariant is a placeholder for future cross-user checks;
|
|
110
|
-
for v1 it is a no-op."""
|
|
111
|
-
return []
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
HARD_INVARIANTS = (
|
|
115
|
-
inv_A_search_before_create,
|
|
116
|
-
inv_B_delete_known_id,
|
|
117
|
-
inv_D_plan_size_cap,
|
|
118
|
-
inv_F_no_duplicate_deletes,
|
|
119
|
-
inv_J_scope_match,
|
|
120
|
-
)
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
# --- Soft invariants ---
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
def inv_E_no_duplicate_creates(ctx: ExtractionCtx) -> list[Violation]: # noqa: N802
|
|
127
|
-
"""Two CreateOps with identical content in one plan = oscillation smell."""
|
|
128
|
-
seen: dict[str, int] = {}
|
|
129
|
-
violations: list[Violation] = []
|
|
130
|
-
for i, op in enumerate(ctx.plan):
|
|
131
|
-
key = None
|
|
132
|
-
if isinstance(op, CreateUserProfileOp):
|
|
133
|
-
key = f"profile::{op.content}"
|
|
134
|
-
elif isinstance(op, CreateUserPlaybookOp):
|
|
135
|
-
key = f"playbook::{op.trigger}::{op.content}"
|
|
136
|
-
if key is None:
|
|
137
|
-
continue
|
|
138
|
-
if key in seen:
|
|
139
|
-
violations.append(
|
|
140
|
-
Violation(
|
|
141
|
-
code="E",
|
|
142
|
-
severity="soft",
|
|
143
|
-
affected_op_indices=[i],
|
|
144
|
-
msg=f"Duplicate create content at op {i}",
|
|
145
|
-
)
|
|
146
|
-
)
|
|
147
|
-
else:
|
|
148
|
-
seen[key] = i
|
|
149
|
-
return violations
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
def inv_H_source_span_present(ctx: ExtractionCtx) -> list[Violation]: # noqa: N802
|
|
153
|
-
"""CreateOps must have non-whitespace source_span.
|
|
154
|
-
|
|
155
|
-
Schema enforces min_length=1, but whitespace-only slips through —
|
|
156
|
-
this is the secondary guard.
|
|
157
|
-
"""
|
|
158
|
-
violations: list[Violation] = []
|
|
159
|
-
for i, op in enumerate(ctx.plan):
|
|
160
|
-
if (
|
|
161
|
-
isinstance(op, (CreateUserProfileOp, CreateUserPlaybookOp))
|
|
162
|
-
and not op.source_span.strip()
|
|
163
|
-
):
|
|
164
|
-
violations.append(
|
|
165
|
-
Violation(
|
|
166
|
-
code="H",
|
|
167
|
-
severity="soft",
|
|
168
|
-
affected_op_indices=[i],
|
|
169
|
-
msg=f"Empty/whitespace source_span on create op {i}",
|
|
170
|
-
)
|
|
171
|
-
)
|
|
172
|
-
return violations
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
def inv_K_deletes_without_creates(ctx: ExtractionCtx) -> list[Violation]: # noqa: N802
|
|
176
|
-
"""Plan with deletes but no creates is unusual — worth logging."""
|
|
177
|
-
has_delete = any(
|
|
178
|
-
isinstance(op, (DeleteUserProfileOp, DeleteUserPlaybookOp)) for op in ctx.plan
|
|
179
|
-
)
|
|
180
|
-
has_create = any(
|
|
181
|
-
isinstance(op, (CreateUserProfileOp, CreateUserPlaybookOp)) for op in ctx.plan
|
|
182
|
-
)
|
|
183
|
-
if has_delete and not has_create:
|
|
184
|
-
indices = [
|
|
185
|
-
i
|
|
186
|
-
for i, op in enumerate(ctx.plan)
|
|
187
|
-
if isinstance(op, (DeleteUserProfileOp, DeleteUserPlaybookOp))
|
|
188
|
-
]
|
|
189
|
-
return [
|
|
190
|
-
Violation(
|
|
191
|
-
code="K",
|
|
192
|
-
severity="soft",
|
|
193
|
-
affected_op_indices=indices,
|
|
194
|
-
msg="Plan contains deletes without any matching creates",
|
|
195
|
-
)
|
|
196
|
-
]
|
|
197
|
-
return []
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
SOFT_INVARIANTS = (
|
|
201
|
-
inv_E_no_duplicate_creates,
|
|
202
|
-
inv_H_source_span_present,
|
|
203
|
-
inv_K_deletes_without_creates,
|
|
204
|
-
)
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
# --- Oscillation resolver ---
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
def resolve_tentative_oscillations(plan: list) -> set[int]:
|
|
211
|
-
"""Return plan indices to drop: create+delete-tentative pairs cancel.
|
|
212
|
-
|
|
213
|
-
When the agent creates an entity (issuing a tentative_id) and later
|
|
214
|
-
deletes that same tentative_id within the same plan, both ops are
|
|
215
|
-
dropped before invariants fire. This is the "oscillated self-correction"
|
|
216
|
-
pattern — the agent changed its mind mid-run.
|
|
217
|
-
|
|
218
|
-
The tentative_id format is ``tentative::<kind>::<plan_index_at_issue_time>``,
|
|
219
|
-
matching ``_next_tentative_id`` in tools.py which uses ``len(ctx.plan)``
|
|
220
|
-
(the plan length BEFORE the op is appended, i.e. the future index of the op).
|
|
221
|
-
|
|
222
|
-
Args:
|
|
223
|
-
plan: The accumulated list of PlanOp instances from ctx.plan.
|
|
224
|
-
|
|
225
|
-
Returns:
|
|
226
|
-
Set of plan indices to exclude from apply. Both the create and the
|
|
227
|
-
delete are dropped when a matching pair is found.
|
|
228
|
-
"""
|
|
229
|
-
drop: set[int] = set()
|
|
230
|
-
pending_creates: dict[str, int] = {}
|
|
231
|
-
for i, op in enumerate(plan):
|
|
232
|
-
if isinstance(op, CreateUserProfileOp):
|
|
233
|
-
tentative_id = f"tentative::profile::{i}"
|
|
234
|
-
pending_creates[tentative_id] = i
|
|
235
|
-
elif isinstance(op, CreateUserPlaybookOp):
|
|
236
|
-
tentative_id = f"tentative::user_playbook::{i}"
|
|
237
|
-
pending_creates[tentative_id] = i
|
|
238
|
-
elif isinstance(op, (DeleteUserProfileOp, DeleteUserPlaybookOp)):
|
|
239
|
-
if op.id.startswith("tentative::") and op.id in pending_creates:
|
|
240
|
-
drop.add(pending_creates.pop(op.id))
|
|
241
|
-
drop.add(i)
|
|
242
|
-
return drop
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
# --- commit_plan ---
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
def commit_plan(
|
|
249
|
-
ctx: ExtractionCtx,
|
|
250
|
-
storage: object,
|
|
251
|
-
*,
|
|
252
|
-
outcome: str, # Literal["finish_tool","max_steps","error"]
|
|
253
|
-
) -> CommitResult:
|
|
254
|
-
"""Run all invariants, then apply surviving ops atomically.
|
|
255
|
-
|
|
256
|
-
Args:
|
|
257
|
-
ctx: Populated ExtractionCtx from the agent loop.
|
|
258
|
-
storage: BaseStorage handle for apply.
|
|
259
|
-
outcome: How the loop terminated.
|
|
260
|
-
|
|
261
|
-
Returns:
|
|
262
|
-
CommitResult containing applied ops + all violations (hard + soft).
|
|
263
|
-
"""
|
|
264
|
-
# Error outcome — discard everything, do not apply
|
|
265
|
-
if outcome == "error":
|
|
266
|
-
return CommitResult(applied=[], violations=[], outcome="error")
|
|
267
|
-
|
|
268
|
-
violations: list[Violation] = []
|
|
269
|
-
for check in HARD_INVARIANTS:
|
|
270
|
-
violations.extend(check(ctx))
|
|
271
|
-
for check in SOFT_INVARIANTS:
|
|
272
|
-
violations.extend(check(ctx))
|
|
273
|
-
|
|
274
|
-
dropped: set[int] = set()
|
|
275
|
-
# Oscillation resolver runs first: matching create+delete-tentative pairs
|
|
276
|
-
# cancel before invariants decide what to keep.
|
|
277
|
-
dropped.update(resolve_tentative_oscillations(ctx.plan))
|
|
278
|
-
for v in violations:
|
|
279
|
-
if v.severity == "hard":
|
|
280
|
-
dropped.update(v.affected_op_indices)
|
|
281
|
-
|
|
282
|
-
ops_to_apply = [op for i, op in enumerate(ctx.plan) if i not in dropped]
|
|
283
|
-
|
|
284
|
-
for v in violations:
|
|
285
|
-
logger.info(
|
|
286
|
-
"invariant_violation user_id=%s code=%s severity=%s op_indices=%s msg=%s",
|
|
287
|
-
ctx.user_id,
|
|
288
|
-
v.code,
|
|
289
|
-
v.severity,
|
|
290
|
-
v.affected_op_indices,
|
|
291
|
-
v.msg,
|
|
292
|
-
)
|
|
293
|
-
|
|
294
|
-
# Delegate actual storage writes to the tool-handler module (Task 5 wires this in).
|
|
295
|
-
# Lazy import so Task 3 can land before tools.py exists.
|
|
296
|
-
from reflexio.server.services.extraction.tools import (
|
|
297
|
-
apply_plan_op, # noqa: PLC0415 # type: ignore[import-not-found]
|
|
298
|
-
)
|
|
299
|
-
|
|
300
|
-
for op in ops_to_apply:
|
|
301
|
-
apply_plan_op(op, storage, ctx)
|
|
302
|
-
|
|
303
|
-
return CommitResult(applied=ops_to_apply, violations=violations, outcome=outcome) # type: ignore[arg-type]
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
"""Plan-op types, ExtractionCtx, HandlerBundle, and commit-result types for the agentic-v2 pipeline.
|
|
2
|
-
|
|
3
|
-
Tool handlers append PlanOp instances to ``ctx.plan`` rather than hitting
|
|
4
|
-
storage directly. A deterministic commit stage at ``finish`` (or on
|
|
5
|
-
``max_steps``) runs invariants and applies the valid ops atomically.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
from __future__ import annotations
|
|
9
|
-
|
|
10
|
-
from dataclasses import dataclass, field
|
|
11
|
-
from typing import Annotated, Literal
|
|
12
|
-
|
|
13
|
-
from pydantic import BaseModel, ConfigDict, Field
|
|
14
|
-
|
|
15
|
-
# Mirrors ProfileTimeToLive — kept as Literal to avoid circular import on enum.
|
|
16
|
-
ProfileTTL = Literal[
|
|
17
|
-
"one_day", "one_week", "one_month", "one_quarter", "one_year", "infinity"
|
|
18
|
-
]
|
|
19
|
-
|
|
20
|
-
PlaybookStrength = Literal["hard", "soft"]
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
class _BasePlanOp(BaseModel):
|
|
24
|
-
"""Base class for all PlanOp variants. Discriminated union via ``op``."""
|
|
25
|
-
|
|
26
|
-
model_config = ConfigDict(frozen=True)
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
class CreateUserProfileOp(_BasePlanOp):
|
|
30
|
-
op: Literal["create_user_profile"] = "create_user_profile"
|
|
31
|
-
content: Annotated[str, Field(min_length=1)]
|
|
32
|
-
ttl: ProfileTTL
|
|
33
|
-
source_span: Annotated[str, Field(min_length=1)]
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
class DeleteUserProfileOp(_BasePlanOp):
|
|
37
|
-
op: Literal["delete_user_profile"] = "delete_user_profile"
|
|
38
|
-
id: Annotated[str, Field(min_length=1)]
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
class CreateUserPlaybookOp(_BasePlanOp):
|
|
42
|
-
op: Literal["create_user_playbook"] = "create_user_playbook"
|
|
43
|
-
trigger: Annotated[str, Field(min_length=1)]
|
|
44
|
-
content: Annotated[str, Field(min_length=1)]
|
|
45
|
-
rationale: str = ""
|
|
46
|
-
strength: PlaybookStrength = "soft"
|
|
47
|
-
source_span: Annotated[str, Field(min_length=1)]
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
class DeleteUserPlaybookOp(_BasePlanOp):
|
|
51
|
-
op: Literal["delete_user_playbook"] = "delete_user_playbook"
|
|
52
|
-
id: Annotated[str, Field(min_length=1)]
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
PlanOp = Annotated[
|
|
56
|
-
CreateUserProfileOp
|
|
57
|
-
| DeleteUserProfileOp
|
|
58
|
-
| CreateUserPlaybookOp
|
|
59
|
-
| DeleteUserPlaybookOp,
|
|
60
|
-
Field(discriminator="op"),
|
|
61
|
-
]
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
@dataclass
|
|
65
|
-
class ExtractionCtx:
|
|
66
|
-
"""Per-run state for the extraction agent.
|
|
67
|
-
|
|
68
|
-
Attributes:
|
|
69
|
-
user_id: Authenticated user the run is scoped to.
|
|
70
|
-
agent_version: Agent version from the active config.
|
|
71
|
-
request_id: Source publish_interaction request UUID — embedded into
|
|
72
|
-
every profile/playbook this run creates so retrieval can trace
|
|
73
|
-
back to the originating session. Empty string when called from
|
|
74
|
-
test contexts that don't have a publish request.
|
|
75
|
-
plan: Accumulated PlanOps awaiting commit.
|
|
76
|
-
known_ids: Ids the agent has legitimately seen (from search/get/create
|
|
77
|
-
handlers). Invariant B checks delete ids against this set.
|
|
78
|
-
search_count: Number of search_* tool calls. Invariant A gates on this.
|
|
79
|
-
finished: True once the agent calls the ``finish`` tool.
|
|
80
|
-
"""
|
|
81
|
-
|
|
82
|
-
user_id: str
|
|
83
|
-
agent_version: str
|
|
84
|
-
request_id: str = ""
|
|
85
|
-
plan: list = field(
|
|
86
|
-
default_factory=list
|
|
87
|
-
) # list[PlanOp] — type-erased to avoid forward-ref issues
|
|
88
|
-
known_ids: set[str] = field(default_factory=set)
|
|
89
|
-
search_count: int = 0
|
|
90
|
-
finished: bool = False
|
|
91
|
-
search_answer: str | None = None
|
|
92
|
-
agent_playbook_status_filter: list[str] | None = None
|
|
93
|
-
# Compressed rehydration excerpts captured by `read_session_text` calls
|
|
94
|
-
# during the agent loop. Surfaced verbatim on the response so callers can
|
|
95
|
-
# include them in downstream context without going through the search
|
|
96
|
-
# agent's natural-language `finish(answer=…)` synthesis (which paraphrases
|
|
97
|
-
# operands and loses fidelity).
|
|
98
|
-
rehydrated_excerpts: list[str] = field(default_factory=list)
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
@dataclass(slots=True)
|
|
102
|
-
class HandlerBundle:
|
|
103
|
-
"""Glue so tool handlers can access shared services through one param.
|
|
104
|
-
|
|
105
|
-
The run_tool_loop primitive passes a single ``ctx`` param to tool handlers;
|
|
106
|
-
handlers in tools.py need access to BaseStorage and an ExtractionCtx. A few
|
|
107
|
-
handlers (e.g., the rehydration tool) additionally call back into the LLM
|
|
108
|
-
layer for in-tool denoising — those receive ``llm_client`` and
|
|
109
|
-
``prompt_manager`` here. The tool-loop caller builds one of these before
|
|
110
|
-
driving the loop.
|
|
111
|
-
|
|
112
|
-
Args:
|
|
113
|
-
storage: BaseStorage handle.
|
|
114
|
-
ctx: ExtractionCtx with per-run state.
|
|
115
|
-
llm_client: Optional LiteLLMClient for in-tool LLM calls (e.g.
|
|
116
|
-
compression). ``None`` in test paths that don't exercise tools
|
|
117
|
-
requiring LLM completions.
|
|
118
|
-
prompt_manager: Optional PromptManager for rendering in-tool prompts.
|
|
119
|
-
Same ``None`` semantics as ``llm_client``.
|
|
120
|
-
"""
|
|
121
|
-
|
|
122
|
-
storage: object
|
|
123
|
-
ctx: ExtractionCtx
|
|
124
|
-
llm_client: object | None = None
|
|
125
|
-
prompt_manager: object | None = None
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
class Violation(BaseModel):
|
|
129
|
-
code: Literal["A", "B", "D", "E", "F", "H", "J", "K"]
|
|
130
|
-
severity: Literal["hard", "soft"]
|
|
131
|
-
affected_op_indices: list[int]
|
|
132
|
-
msg: str
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
class CommitResult(BaseModel):
|
|
136
|
-
applied: list[PlanOp]
|
|
137
|
-
violations: list[Violation]
|
|
138
|
-
outcome: Literal["finish_tool", "max_steps", "error"]
|