claude-smart 0.2.42 → 0.2.44

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.
Files changed (402) hide show
  1. package/.claude-plugin/marketplace.json +3 -3
  2. package/README.md +1 -1
  3. package/bin/claude-smart.js +2 -2
  4. package/package.json +9 -3
  5. package/plugin/.claude-plugin/plugin.json +9 -3
  6. package/plugin/.codex-plugin/plugin.json +1 -1
  7. package/plugin/README.md +23 -3
  8. package/plugin/pyproject.toml +3 -3
  9. package/plugin/scripts/_lib.sh +91 -0
  10. package/plugin/scripts/backend-service.sh +51 -4
  11. package/plugin/scripts/cli.sh +3 -1
  12. package/plugin/scripts/codex-hook.js +72 -4
  13. package/plugin/scripts/dashboard-build.sh +1 -0
  14. package/plugin/scripts/dashboard-service.sh +1 -0
  15. package/plugin/scripts/ensure-plugin-root.sh +1 -0
  16. package/plugin/scripts/hook_entry.sh +6 -3
  17. package/plugin/scripts/smart-install.sh +3 -2
  18. package/plugin/src/README.md +57 -0
  19. package/plugin/src/claude_smart/context_format.py +11 -12
  20. package/plugin/src/claude_smart/cs_cite.py +26 -12
  21. package/plugin/src/claude_smart/ids.py +13 -5
  22. package/plugin/uv.lock +126 -5
  23. package/plugin/vendor/reflexio/.env.example +62 -0
  24. package/plugin/vendor/reflexio/LICENSE +201 -0
  25. package/plugin/vendor/reflexio/README.md +338 -0
  26. package/plugin/vendor/reflexio/pyproject.toml +274 -0
  27. package/plugin/vendor/reflexio/reflexio/README.md +184 -0
  28. package/plugin/vendor/reflexio/reflexio/__init__.py +166 -0
  29. package/plugin/vendor/reflexio/reflexio/benchmarks/__init__.py +1 -0
  30. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/README.md +109 -0
  31. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/__init__.py +1 -0
  32. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/backends.py +175 -0
  33. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/bench.py +642 -0
  34. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/embed_cache.py +330 -0
  35. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/report.py +317 -0
  36. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/results/report.md +43 -0
  37. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/results/results.json +4478 -0
  38. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/scenarios.py +134 -0
  39. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/seed.py +255 -0
  40. package/plugin/vendor/reflexio/reflexio/cli/README.md +287 -0
  41. package/plugin/vendor/reflexio/reflexio/cli/__init__.py +0 -0
  42. package/plugin/vendor/reflexio/reflexio/cli/__main__.py +56 -0
  43. package/plugin/vendor/reflexio/reflexio/cli/_client.py +86 -0
  44. package/plugin/vendor/reflexio/reflexio/cli/app.py +127 -0
  45. package/plugin/vendor/reflexio/reflexio/cli/bootstrap_config.py +265 -0
  46. package/plugin/vendor/reflexio/reflexio/cli/codex_auth.py +503 -0
  47. package/plugin/vendor/reflexio/reflexio/cli/commands/__init__.py +0 -0
  48. package/plugin/vendor/reflexio/reflexio/cli/commands/admin_cmd.py +65 -0
  49. package/plugin/vendor/reflexio/reflexio/cli/commands/agent_playbooks.py +503 -0
  50. package/plugin/vendor/reflexio/reflexio/cli/commands/api.py +114 -0
  51. package/plugin/vendor/reflexio/reflexio/cli/commands/auth.py +109 -0
  52. package/plugin/vendor/reflexio/reflexio/cli/commands/config_cmd.py +511 -0
  53. package/plugin/vendor/reflexio/reflexio/cli/commands/doctor.py +127 -0
  54. package/plugin/vendor/reflexio/reflexio/cli/commands/embeddings.py +53 -0
  55. package/plugin/vendor/reflexio/reflexio/cli/commands/interactions.py +478 -0
  56. package/plugin/vendor/reflexio/reflexio/cli/commands/profiles.py +303 -0
  57. package/plugin/vendor/reflexio/reflexio/cli/commands/services.py +289 -0
  58. package/plugin/vendor/reflexio/reflexio/cli/commands/setup_cmd.py +964 -0
  59. package/plugin/vendor/reflexio/reflexio/cli/commands/shortcuts.py +285 -0
  60. package/plugin/vendor/reflexio/reflexio/cli/commands/status_cmd.py +143 -0
  61. package/plugin/vendor/reflexio/reflexio/cli/commands/user_playbooks.py +373 -0
  62. package/plugin/vendor/reflexio/reflexio/cli/env_loader.py +284 -0
  63. package/plugin/vendor/reflexio/reflexio/cli/errors.py +217 -0
  64. package/plugin/vendor/reflexio/reflexio/cli/log_format.py +247 -0
  65. package/plugin/vendor/reflexio/reflexio/cli/output.py +867 -0
  66. package/plugin/vendor/reflexio/reflexio/cli/paths.py +41 -0
  67. package/plugin/vendor/reflexio/reflexio/cli/run_services.py +391 -0
  68. package/plugin/vendor/reflexio/reflexio/cli/state.py +204 -0
  69. package/plugin/vendor/reflexio/reflexio/cli/stop_services.py +96 -0
  70. package/plugin/vendor/reflexio/reflexio/cli/utils.py +329 -0
  71. package/plugin/vendor/reflexio/reflexio/client/__init__.py +3 -0
  72. package/plugin/vendor/reflexio/reflexio/client/cache.py +150 -0
  73. package/plugin/vendor/reflexio/reflexio/client/client.py +2613 -0
  74. package/plugin/vendor/reflexio/reflexio/defaults.py +23 -0
  75. package/plugin/vendor/reflexio/reflexio/integrations/__init__.py +0 -0
  76. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/.clawhubignore +7 -0
  77. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/README.md +274 -0
  78. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/TESTING.md +517 -0
  79. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/hook/handler.js +473 -0
  80. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/package-lock.json +2156 -0
  81. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/package.json +18 -0
  82. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/hook/handler.ts +241 -0
  83. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/hook/setup.ts +140 -0
  84. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/index.ts +130 -0
  85. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/publish.ts +113 -0
  86. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/search.ts +52 -0
  87. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/server.ts +103 -0
  88. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/sqlite-buffer.ts +156 -0
  89. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/user-id.ts +134 -0
  90. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/openclaw.plugin.json +41 -0
  91. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/package.json +17 -0
  92. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/rules/reflexio.md +24 -0
  93. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/reflexio/SKILL.md +48 -0
  94. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/publish_clawhub.sh +278 -0
  95. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/references/HOOK.md +164 -0
  96. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/scripts/install.sh +36 -0
  97. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/scripts/uninstall.sh +35 -0
  98. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/publish.test.ts +27 -0
  99. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/search.test.ts +31 -0
  100. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/server.test.ts +42 -0
  101. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/setup.test.ts +49 -0
  102. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/sqlite-buffer.test.ts +91 -0
  103. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/user-id.test.ts +50 -0
  104. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tsconfig.json +16 -0
  105. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/types/openclaw.d.ts +230 -0
  106. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/vitest.config.ts +13 -0
  107. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/README.md +120 -0
  108. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/TESTING.md +168 -0
  109. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/package-lock.json +1657 -0
  110. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/package.json +16 -0
  111. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/HEARTBEAT.md +6 -0
  112. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/README.md +84 -0
  113. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/SKILL.md +194 -0
  114. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/_meta.json +6 -0
  115. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/agents/reflexio-extractor.md +45 -0
  116. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/hook/handler.ts +214 -0
  117. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/hook/setup.ts +55 -0
  118. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/index.ts +327 -0
  119. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/consolidate.ts +233 -0
  120. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/dedup.ts +80 -0
  121. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/io.ts +155 -0
  122. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/openclaw-cli.ts +67 -0
  123. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/search.ts +33 -0
  124. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/write-playbook.ts +76 -0
  125. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/write-profile.ts +79 -0
  126. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/openclaw.plugin.json +46 -0
  127. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/package.json +18 -0
  128. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/prompts/README.md +36 -0
  129. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/prompts/full_consolidation.md +56 -0
  130. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/prompts/playbook_extraction.md +217 -0
  131. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/prompts/profile_extraction.md +132 -0
  132. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/skills/reflexio-consolidate/SKILL.md +33 -0
  133. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/skills/reflexio-embedded/SKILL.md +194 -0
  134. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/HOOK.md +18 -0
  135. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/architecture.md +49 -0
  136. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/comparison.md +31 -0
  137. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/future-work.md +47 -0
  138. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/porting-notes.md +52 -0
  139. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/scripts/install.sh +52 -0
  140. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/scripts/uninstall.sh +36 -0
  141. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/consolidate.test.ts +135 -0
  142. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/dedup.test.ts +104 -0
  143. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/io.test.ts +175 -0
  144. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/search.test.ts +66 -0
  145. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/smoke-test.ts +140 -0
  146. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/write-playbook.test.ts +93 -0
  147. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/write-profile.test.ts +174 -0
  148. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tsconfig.json +16 -0
  149. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/types/openclaw.d.ts +230 -0
  150. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/vitest.config.ts +7 -0
  151. package/plugin/vendor/reflexio/reflexio/lib/__init__.py +23 -0
  152. package/plugin/vendor/reflexio/reflexio/lib/_agent_playbook.py +310 -0
  153. package/plugin/vendor/reflexio/reflexio/lib/_base.py +225 -0
  154. package/plugin/vendor/reflexio/reflexio/lib/_config.py +83 -0
  155. package/plugin/vendor/reflexio/reflexio/lib/_dashboard.py +266 -0
  156. package/plugin/vendor/reflexio/reflexio/lib/_generation.py +176 -0
  157. package/plugin/vendor/reflexio/reflexio/lib/_interactions.py +334 -0
  158. package/plugin/vendor/reflexio/reflexio/lib/_operations.py +153 -0
  159. package/plugin/vendor/reflexio/reflexio/lib/_profiles.py +545 -0
  160. package/plugin/vendor/reflexio/reflexio/lib/_reflection.py +52 -0
  161. package/plugin/vendor/reflexio/reflexio/lib/_search.py +167 -0
  162. package/plugin/vendor/reflexio/reflexio/lib/_storage_labels.py +103 -0
  163. package/plugin/vendor/reflexio/reflexio/lib/_user_playbook.py +288 -0
  164. package/plugin/vendor/reflexio/reflexio/lib/reflexio_lib.py +27 -0
  165. package/plugin/vendor/reflexio/reflexio/models/__init__.py +0 -0
  166. package/plugin/vendor/reflexio/reflexio/models/api_schema/__init__.py +0 -0
  167. package/plugin/vendor/reflexio/reflexio/models/api_schema/braintrust_schema.py +141 -0
  168. package/plugin/vendor/reflexio/reflexio/models/api_schema/common.py +41 -0
  169. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/__init__.py +3 -0
  170. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/entities.py +1112 -0
  171. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/enums.py +63 -0
  172. package/plugin/vendor/reflexio/reflexio/models/api_schema/eval_overview_schema.py +487 -0
  173. package/plugin/vendor/reflexio/reflexio/models/api_schema/internal_schema.py +28 -0
  174. package/plugin/vendor/reflexio/reflexio/models/api_schema/pending_tool_call_schema.py +83 -0
  175. package/plugin/vendor/reflexio/reflexio/models/api_schema/retriever_schema.py +768 -0
  176. package/plugin/vendor/reflexio/reflexio/models/api_schema/service_schemas.py +9 -0
  177. package/plugin/vendor/reflexio/reflexio/models/api_schema/stall_state_schema.py +32 -0
  178. package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/__init__.py +3 -0
  179. package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/converters.py +177 -0
  180. package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/entities.py +129 -0
  181. package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/enums.py +25 -0
  182. package/plugin/vendor/reflexio/reflexio/models/api_schema/validators.py +333 -0
  183. package/plugin/vendor/reflexio/reflexio/models/config_schema.py +908 -0
  184. package/plugin/vendor/reflexio/reflexio/models/py.typed +0 -0
  185. package/plugin/vendor/reflexio/reflexio/server/OVERVIEW.md +90 -0
  186. package/plugin/vendor/reflexio/reflexio/server/README.md +622 -0
  187. package/plugin/vendor/reflexio/reflexio/server/__init__.py +210 -0
  188. package/plugin/vendor/reflexio/reflexio/server/__main__.py +132 -0
  189. package/plugin/vendor/reflexio/reflexio/server/_auth.py +25 -0
  190. package/plugin/vendor/reflexio/reflexio/server/api.py +2868 -0
  191. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/README.md +34 -0
  192. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/account_api.py +143 -0
  193. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/health_api.py +91 -0
  194. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/pending_tool_call_api.py +572 -0
  195. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/precondition_checks.py +66 -0
  196. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/publisher_api.py +562 -0
  197. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/request_context.py +50 -0
  198. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/stall_state_api.py +100 -0
  199. package/plugin/vendor/reflexio/reflexio/server/cache/__init__.py +15 -0
  200. package/plugin/vendor/reflexio/reflexio/server/cache/reflexio_cache.py +208 -0
  201. package/plugin/vendor/reflexio/reflexio/server/correlation.py +46 -0
  202. package/plugin/vendor/reflexio/reflexio/server/llm/__init__.py +30 -0
  203. package/plugin/vendor/reflexio/reflexio/server/llm/embedding_service.py +359 -0
  204. package/plugin/vendor/reflexio/reflexio/server/llm/image_utils.py +55 -0
  205. package/plugin/vendor/reflexio/reflexio/server/llm/litellm_client.py +1871 -0
  206. package/plugin/vendor/reflexio/reflexio/server/llm/llm_utils.py +140 -0
  207. package/plugin/vendor/reflexio/reflexio/server/llm/model_defaults.py +479 -0
  208. package/plugin/vendor/reflexio/reflexio/server/llm/providers/__init__.py +1 -0
  209. package/plugin/vendor/reflexio/reflexio/server/llm/providers/claude_code_provider.py +1122 -0
  210. package/plugin/vendor/reflexio/reflexio/server/llm/providers/claude_code_stream_parser.py +197 -0
  211. package/plugin/vendor/reflexio/reflexio/server/llm/providers/embedding_service_provider.py +338 -0
  212. package/plugin/vendor/reflexio/reflexio/server/llm/providers/local_embedding_provider.py +213 -0
  213. package/plugin/vendor/reflexio/reflexio/server/llm/providers/nomic_embedding_provider.py +288 -0
  214. package/plugin/vendor/reflexio/reflexio/server/llm/rerank/__init__.py +6 -0
  215. package/plugin/vendor/reflexio/reflexio/server/llm/rerank/cross_encoder_reranker.py +187 -0
  216. package/plugin/vendor/reflexio/reflexio/server/llm/rerank/llm_reranker.py +148 -0
  217. package/plugin/vendor/reflexio/reflexio/server/llm/tools.py +716 -0
  218. package/plugin/vendor/reflexio/reflexio/server/operation_limiter.py +179 -0
  219. package/plugin/vendor/reflexio/reflexio/server/prompt/__init__.py +0 -0
  220. package/plugin/vendor/reflexio/reflexio/server/prompt/_dispatchers.py +54 -0
  221. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/README.md +121 -0
  222. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/agent_success_evaluation/v1.0.0.prompt.md +58 -0
  223. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/agent_success_evaluation_with_comparison/v1.0.0.prompt.md +76 -0
  224. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/answer_synthesis/v1.5.2.prompt.md +88 -0
  225. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/compress_session_for_query/v1.3.0.prompt.md +31 -0
  226. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/document_expansion/v1.0.0.prompt.md +20 -0
  227. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.0.0.prompt.md +53 -0
  228. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.1.0.prompt.md +57 -0
  229. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.2.0.prompt.md +68 -0
  230. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.3.0.prompt.md +70 -0
  231. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.4.0.prompt.md +77 -0
  232. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.5.0.prompt.md +82 -0
  233. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.6.0.prompt.md +83 -0
  234. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_aggregation/v2.1.0.prompt.md +193 -0
  235. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_aggregation/v2.2.0.prompt.md +206 -0
  236. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v1.0.0-deprecated.prompt.md +66 -0
  237. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v1.0.0.prompt.md +43 -0
  238. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v1.1.0.prompt.md +46 -0
  239. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.0.0-deprecated.prompt.md +64 -0
  240. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.0.0.prompt.md +39 -0
  241. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.1.0.prompt.md +39 -0
  242. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.2.0.prompt.md +47 -0
  243. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.3.0.prompt.md +58 -0
  244. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.3.1.prompt.md +69 -0
  245. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.3.2.prompt.md +71 -0
  246. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.0.2.prompt.md +254 -0
  247. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.1.0.prompt.md +274 -0
  248. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.2.0.prompt.md +283 -0
  249. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.2.2.prompt.md +234 -0
  250. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.2.3.prompt.md +244 -0
  251. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v1.0.0.prompt.md +73 -0
  252. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v2.0.0.prompt.md +86 -0
  253. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.0.0.prompt.md +97 -0
  254. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.1.0.prompt.md +119 -0
  255. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.2.0.prompt.md +123 -0
  256. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.3.0.prompt.md +137 -0
  257. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.0.0.prompt.md +14 -0
  258. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.1.0.prompt.md +24 -0
  259. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.2.0.prompt.md +29 -0
  260. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main_expert/v1.0.0.prompt.md +11 -0
  261. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main_expert/v1.1.0.prompt.md +21 -0
  262. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main_expert/v1.2.0.prompt.md +25 -0
  263. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_optimizer_judge/v1.0.0.prompt.md +37 -0
  264. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_optimizer_judge/v1.1.0.prompt.md +40 -0
  265. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_optimizer_judge/v1.2.0.prompt.md +36 -0
  266. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_should_generate/v1.0.0.prompt.md +45 -0
  267. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_should_generate/v2.0.0.prompt.md +81 -0
  268. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_should_generate/v3.0.0.prompt.md +80 -0
  269. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_should_generate_expert/v1.0.0.prompt.md +34 -0
  270. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_deduplication/v1.0.0.prompt.md +116 -0
  271. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_should_generate/v1.0.0.prompt.md +33 -0
  272. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_should_generate_override/v1.0.0.prompt.md +16 -0
  273. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.0.0.prompt.md +140 -0
  274. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.1.0.prompt.md +160 -0
  275. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_main/v1.0.0.prompt.md +14 -0
  276. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/query_reformulation/v1.0.0.prompt.md +19 -0
  277. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/rerank_relevance/v1.1.0.prompt.md +44 -0
  278. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/shadow_comparison/v1.0.0.prompt.md +43 -0
  279. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/shadow_content_evaluation/v1.0.0.prompt.md +33 -0
  280. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_evaluation/prompt_evaluation_dataset/feedback_extraction_main_v1.jsonl +10 -0
  281. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_evaluation/prompt_evaluation_dataset/profile_update_main_v1.jsonl +10 -0
  282. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_manager.py +280 -0
  283. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_schema.py +11 -0
  284. package/plugin/vendor/reflexio/reflexio/server/services/README.md +58 -0
  285. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/_eval_health.py +131 -0
  286. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_constants.py +60 -0
  287. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_service.py +228 -0
  288. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_utils.py +87 -0
  289. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluator.py +372 -0
  290. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/delayed_group_evaluator.py +156 -0
  291. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/group_evaluation_runner.py +336 -0
  292. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/regen_jobs.py +471 -0
  293. package/plugin/vendor/reflexio/reflexio/server/services/base_generation_service.py +1668 -0
  294. package/plugin/vendor/reflexio/reflexio/server/services/braintrust/__init__.py +0 -0
  295. package/plugin/vendor/reflexio/reflexio/server/services/braintrust/_cron.py +196 -0
  296. package/plugin/vendor/reflexio/reflexio/server/services/braintrust/_encryption.py +101 -0
  297. package/plugin/vendor/reflexio/reflexio/server/services/braintrust/client.py +167 -0
  298. package/plugin/vendor/reflexio/reflexio/server/services/braintrust/service.py +281 -0
  299. package/plugin/vendor/reflexio/reflexio/server/services/configurator/base_configurator.py +179 -0
  300. package/plugin/vendor/reflexio/reflexio/server/services/configurator/config_storage.py +62 -0
  301. package/plugin/vendor/reflexio/reflexio/server/services/configurator/configurator.py +87 -0
  302. package/plugin/vendor/reflexio/reflexio/server/services/configurator/local_file_config_storage.py +187 -0
  303. package/plugin/vendor/reflexio/reflexio/server/services/configurator/test_config_storage.py +162 -0
  304. package/plugin/vendor/reflexio/reflexio/server/services/deduplication_utils.py +112 -0
  305. package/plugin/vendor/reflexio/reflexio/server/services/embedding_text.py +62 -0
  306. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/__init__.py +0 -0
  307. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/distribution.py +33 -0
  308. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/eval_sampler.py +126 -0
  309. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/group_aggregation.py +192 -0
  310. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/hero_state.py +75 -0
  311. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/rule_attribution.py +97 -0
  312. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/service.py +515 -0
  313. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/shadow_aggregation.py +90 -0
  314. package/plugin/vendor/reflexio/reflexio/server/services/extraction/__init__.py +0 -0
  315. package/plugin/vendor/reflexio/reflexio/server/services/extraction/agent_run_records.py +91 -0
  316. package/plugin/vendor/reflexio/reflexio/server/services/extraction/invariants.py +303 -0
  317. package/plugin/vendor/reflexio/reflexio/server/services/extraction/outcome.py +25 -0
  318. package/plugin/vendor/reflexio/reflexio/server/services/extraction/pending_tool_call_dispatch.py +358 -0
  319. package/plugin/vendor/reflexio/reflexio/server/services/extraction/plan.py +138 -0
  320. package/plugin/vendor/reflexio/reflexio/server/services/extraction/prior_answer_search.py +217 -0
  321. package/plugin/vendor/reflexio/reflexio/server/services/extraction/resumable_agent.py +535 -0
  322. package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_scheduler.py +171 -0
  323. package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_worker.py +779 -0
  324. package/plugin/vendor/reflexio/reflexio/server/services/extraction/tools.py +1125 -0
  325. package/plugin/vendor/reflexio/reflexio/server/services/extractor_config_utils.py +94 -0
  326. package/plugin/vendor/reflexio/reflexio/server/services/extractor_interaction_utils.py +251 -0
  327. package/plugin/vendor/reflexio/reflexio/server/services/generation_service.py +702 -0
  328. package/plugin/vendor/reflexio/reflexio/server/services/operation_state_utils.py +835 -0
  329. package/plugin/vendor/reflexio/reflexio/server/services/playbook/README.md +89 -0
  330. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_aggregator.py +1388 -0
  331. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_consolidator.py +1045 -0
  332. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_extractor.py +436 -0
  333. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_generation_service.py +808 -0
  334. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_service_constants.py +28 -0
  335. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_service_utils.py +362 -0
  336. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/__init__.py +24 -0
  337. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/assistant_webhook.py +246 -0
  338. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/gepa_adapter.py +291 -0
  339. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/judge.py +97 -0
  340. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/models.py +96 -0
  341. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/optimizer.py +645 -0
  342. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/rollout.py +35 -0
  343. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/scenario_resolver.py +93 -0
  344. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/scheduler.py +174 -0
  345. package/plugin/vendor/reflexio/reflexio/server/services/pre_retrieval/__init__.py +26 -0
  346. package/plugin/vendor/reflexio/reflexio/server/services/pre_retrieval/_document_expander.py +179 -0
  347. package/plugin/vendor/reflexio/reflexio/server/services/pre_retrieval/_query_reformulator.py +297 -0
  348. package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_deduplicator.py +772 -0
  349. package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_extractor.py +462 -0
  350. package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_generation_service.py +737 -0
  351. package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_generation_service_utils.py +290 -0
  352. package/plugin/vendor/reflexio/reflexio/server/services/reflection/__init__.py +17 -0
  353. package/plugin/vendor/reflexio/reflexio/server/services/reflection/reflection_extractor.py +247 -0
  354. package/plugin/vendor/reflexio/reflexio/server/services/reflection/reflection_service.py +803 -0
  355. package/plugin/vendor/reflexio/reflexio/server/services/reflection/reflection_service_utils.py +146 -0
  356. package/plugin/vendor/reflexio/reflexio/server/services/retrieval/__init__.py +0 -0
  357. package/plugin/vendor/reflexio/reflexio/server/services/retrieval/relevance_floor.py +80 -0
  358. package/plugin/vendor/reflexio/reflexio/server/services/search/__init__.py +0 -0
  359. package/plugin/vendor/reflexio/reflexio/server/services/service_utils.py +756 -0
  360. package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/__init__.py +1 -0
  361. package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/judge.py +184 -0
  362. package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/outcome.py +81 -0
  363. package/plugin/vendor/reflexio/reflexio/server/services/storage/constants.py +2 -0
  364. package/plugin/vendor/reflexio/reflexio/server/services/storage/error.py +11 -0
  365. package/plugin/vendor/reflexio/reflexio/server/services/storage/retention.py +154 -0
  366. package/plugin/vendor/reflexio/reflexio/server/services/storage/retention_mixin.py +155 -0
  367. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/__init__.py +59 -0
  368. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_agent_run.py +1298 -0
  369. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_base.py +1945 -0
  370. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_extras.py +600 -0
  371. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_operations.py +346 -0
  372. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_playbook.py +1378 -0
  373. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_profiles.py +747 -0
  374. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_requests.py +263 -0
  375. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_shadow_verdicts.py +193 -0
  376. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_share_links.py +166 -0
  377. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_stall_state.py +217 -0
  378. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/__init__.py +153 -0
  379. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_agent_run.py +384 -0
  380. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_base.py +71 -0
  381. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_extras.py +235 -0
  382. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_operations.py +170 -0
  383. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_playbook.py +677 -0
  384. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_profiles.py +250 -0
  385. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_requests.py +154 -0
  386. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_shadow_verdicts.py +130 -0
  387. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_share_links.py +93 -0
  388. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_stall_state.py +76 -0
  389. package/plugin/vendor/reflexio/reflexio/server/services/unified_search_service.py +572 -0
  390. package/plugin/vendor/reflexio/reflexio/server/site_var/README.md +77 -0
  391. package/plugin/vendor/reflexio/reflexio/server/site_var/feature_flags.py +116 -0
  392. package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_manager.py +263 -0
  393. package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_sources/feature_flags.json +13 -0
  394. package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_sources/llm_model_setting.json +7 -0
  395. package/plugin/vendor/reflexio/reflexio/server/tracing.py +158 -0
  396. package/plugin/vendor/reflexio/reflexio/server/usage_metrics.py +113 -0
  397. package/plugin/vendor/reflexio/reflexio/server/uvicorn_logging.py +76 -0
  398. package/plugin/vendor/reflexio/reflexio/test_support/__init__.py +1 -0
  399. package/plugin/vendor/reflexio/reflexio/test_support/llm_fixtures.py +62 -0
  400. package/plugin/vendor/reflexio/reflexio/test_support/llm_mock.py +242 -0
  401. package/plugin/vendor/reflexio/reflexio/test_support/llm_model_registry.py +129 -0
  402. package/plugin/vendor/reflexio/reflexio/test_support/skip_decorators.py +43 -0
@@ -0,0 +1,303 @@
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]
@@ -0,0 +1,25 @@
1
+ """Shared extraction outcome types for resumable extraction paths."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass, field
6
+ from typing import Literal
7
+
8
+
9
+ @dataclass(frozen=True)
10
+ class ExtractionOutcome[T]:
11
+ """Result wrapper returned by extractors that need explicit empty results."""
12
+
13
+ status: Literal["completed", "empty"]
14
+ items: list[T] = field(default_factory=list)
15
+ run_id: str | None = None
16
+
17
+ @classmethod
18
+ def completed(
19
+ cls, items: list[T], *, run_id: str | None = None
20
+ ) -> ExtractionOutcome[T]:
21
+ return cls(status="completed", items=items, run_id=run_id)
22
+
23
+ @classmethod
24
+ def empty(cls, *, run_id: str | None = None) -> ExtractionOutcome[T]:
25
+ return cls(status="empty", items=[], run_id=run_id)
@@ -0,0 +1,358 @@
1
+ """Async information tools for resumable extraction runs."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ import uuid
7
+ from dataclasses import dataclass, field
8
+ from datetime import UTC, datetime, timedelta
9
+ from typing import Annotated, Any
10
+
11
+ from pydantic import BaseModel, Field, field_validator
12
+
13
+ from reflexio.models.config_schema import PendingToolCallConfig
14
+ from reflexio.server.llm.tools import (
15
+ AsyncAccepted,
16
+ AsyncInfoTool,
17
+ AsyncRequestSpec,
18
+ Tool,
19
+ )
20
+ from reflexio.server.services.storage.storage_base import (
21
+ BaseStorage,
22
+ PendingToolCallRecord,
23
+ PendingToolCallStatus,
24
+ RunToolDependencyRecord,
25
+ build_pending_tool_call_dedup_key,
26
+ build_scope_hash,
27
+ human_feedback_scope,
28
+ )
29
+ from reflexio.server.usage_metrics import record_usage_event
30
+
31
+ logger = logging.getLogger(__name__)
32
+
33
+
34
+ class AskHumanArgs(BaseModel):
35
+ """Ask Agent Builder for missing extraction information and continue now."""
36
+
37
+ question: Annotated[str, Field(min_length=1)]
38
+ answer_format: str | None = None
39
+ tags: list[str] = Field(default_factory=list)
40
+
41
+ @field_validator("tags", mode="before")
42
+ @classmethod
43
+ def _coerce_tags(cls, value: Any) -> Any:
44
+ if isinstance(value, str):
45
+ return [tag.strip() for tag in value.split(",") if tag.strip()]
46
+ return value
47
+
48
+
49
+ class AttachPendingInfoRequestArgs(BaseModel):
50
+ """Attach this run to a relevant pending Prior Knowledge request."""
51
+
52
+ pending_tool_call_id: Annotated[str, Field(min_length=1)]
53
+ why_relevant: str | None = None
54
+
55
+
56
+ class PendingToolCallDispatcher:
57
+ """Dispatch newly-created pending tool calls to an external channel."""
58
+
59
+ def dispatch(self, record: PendingToolCallRecord) -> None:
60
+ raise NotImplementedError
61
+
62
+
63
+ class NoopPendingToolCallDispatcher(PendingToolCallDispatcher):
64
+ """Local/test dispatcher that intentionally does nothing."""
65
+
66
+ def dispatch(self, _record: PendingToolCallRecord) -> None:
67
+ return None
68
+
69
+
70
+ @dataclass(slots=True)
71
+ class PendingToolCallToolContext:
72
+ storage: BaseStorage
73
+ run_id: str
74
+ org_id: str
75
+ extractor_kind: str
76
+ user_id: str | None = None
77
+ config: PendingToolCallConfig = field(default_factory=PendingToolCallConfig)
78
+ dispatcher: PendingToolCallDispatcher = field(
79
+ default_factory=NoopPendingToolCallDispatcher
80
+ )
81
+
82
+
83
+ def _build_async_request_spec(
84
+ *,
85
+ args: AskHumanArgs,
86
+ ctx: PendingToolCallToolContext,
87
+ ) -> AsyncRequestSpec:
88
+ tool_config = ctx.config.for_tool("ask_human")
89
+ scope = human_feedback_scope(ctx.org_id)
90
+ return AsyncRequestSpec(
91
+ tool_name="ask_human",
92
+ dedup_key=build_pending_tool_call_dedup_key(
93
+ tool_name="ask_human",
94
+ question_text=args.question,
95
+ answer_format=args.answer_format,
96
+ ),
97
+ scope=scope,
98
+ question_text=args.question,
99
+ answer_format=args.answer_format,
100
+ args={
101
+ "question": args.question,
102
+ "answer_format": args.answer_format,
103
+ },
104
+ tags=args.tags,
105
+ cache_until_seconds=tool_config.dedup_cache_seconds,
106
+ valid_until_seconds=tool_config.prior_answer_valid_seconds,
107
+ )
108
+
109
+
110
+ def _question_embedding(
111
+ storage: BaseStorage,
112
+ question_text: str,
113
+ ) -> list[float] | None:
114
+ get_embedding = getattr(storage, "_get_embedding", None)
115
+ if not callable(get_embedding):
116
+ return None
117
+ try:
118
+ embedding = get_embedding(question_text, purpose="document")
119
+ except TypeError:
120
+ embedding = get_embedding(question_text)
121
+ except Exception as exc: # pragma: no cover - backend logs details elsewhere
122
+ logger.warning(
123
+ "event=pending_tool_call_embedding_failed error_type=%s error=%s",
124
+ type(exc).__name__,
125
+ exc,
126
+ )
127
+ return None
128
+ return embedding if isinstance(embedding, list) else None
129
+
130
+
131
+ def handle_ask_human(
132
+ args: AskHumanArgs,
133
+ ctx: PendingToolCallToolContext,
134
+ ) -> AsyncAccepted:
135
+ if not ctx.run_id:
136
+ raise ValueError("ask_human requires a durable agent run_id")
137
+
138
+ current = datetime.now(UTC)
139
+ spec = _build_async_request_spec(args=args, ctx=ctx)
140
+ tool_config = ctx.config.for_tool(spec.tool_name)
141
+ # Observational soft cap only: intentionally read OUTSIDE the create/attach
142
+ # transaction. It never blocks or fails extraction (a small count race here
143
+ # would at most over/under-log the warning by one), so it does not need the
144
+ # per-scope transaction boundary that the dedup insert/attach uses.
145
+ unresolved_count = ctx.storage.count_unresolved_followup_dependencies(
146
+ org_id=ctx.org_id,
147
+ extractor_kind=ctx.extractor_kind,
148
+ tool_name=spec.tool_name,
149
+ )
150
+ if unresolved_count >= tool_config.max_pending_followups_per_scope:
151
+ logger.warning(
152
+ "event=pending_followup_soft_cap_exceeded org_id=%s extractor_kind=%s "
153
+ "tool_name=%s current_count=%d soft_cap=%d",
154
+ ctx.org_id,
155
+ ctx.extractor_kind,
156
+ spec.tool_name,
157
+ unresolved_count,
158
+ tool_config.max_pending_followups_per_scope,
159
+ )
160
+
161
+ record = PendingToolCallRecord(
162
+ id=f"ptc_{uuid.uuid4().hex}",
163
+ org_id=ctx.org_id,
164
+ user_id=ctx.user_id,
165
+ scope=spec.scope,
166
+ scope_hash=build_scope_hash(spec.scope),
167
+ tool_name=spec.tool_name,
168
+ dedup_key=spec.dedup_key,
169
+ status=PendingToolCallStatus.PENDING,
170
+ question_text=spec.question_text,
171
+ args=spec.args,
172
+ tags=spec.tags,
173
+ answer_format=spec.answer_format,
174
+ embedding=_question_embedding(ctx.storage, spec.question_text),
175
+ expires_at=current + timedelta(seconds=tool_config.pending_ttl_seconds),
176
+ cache_until=current + timedelta(seconds=spec.cache_until_seconds),
177
+ )
178
+ upsert = ctx.storage.create_or_attach_pending_tool_call(
179
+ record=record,
180
+ dependency=RunToolDependencyRecord(
181
+ run_id=ctx.run_id,
182
+ pending_tool_call_id=record.id,
183
+ ),
184
+ now=current,
185
+ )
186
+ record_usage_event(
187
+ org_id=ctx.org_id,
188
+ event_name="extraction_agent_async_info_requested",
189
+ event_category="extraction_agent",
190
+ user_id=ctx.user_id,
191
+ pipeline=ctx.extractor_kind,
192
+ outcome="created" if upsert.created else "deduped",
193
+ metadata={
194
+ "run_id": ctx.run_id,
195
+ "pending_tool_call_id": upsert.pending_tool_call.id,
196
+ "tool_name": upsert.pending_tool_call.tool_name,
197
+ },
198
+ )
199
+ if upsert.created:
200
+ ctx.dispatcher.dispatch(upsert.pending_tool_call)
201
+ logger.info(
202
+ "event=pending_tool_call_created org_id=%s user_id=%s "
203
+ "extractor_kind=%s run_id=%s pending_tool_call_id=%s "
204
+ "tool_name=%s",
205
+ ctx.org_id,
206
+ ctx.user_id,
207
+ ctx.extractor_kind,
208
+ ctx.run_id,
209
+ upsert.pending_tool_call.id,
210
+ upsert.pending_tool_call.tool_name,
211
+ )
212
+ else:
213
+ logger.info(
214
+ "event=extraction_agent_followup_attached org_id=%s user_id=%s "
215
+ "extractor_kind=%s run_id=%s pending_tool_call_id=%s "
216
+ "tool_name=%s deduped=true",
217
+ ctx.org_id,
218
+ ctx.user_id,
219
+ ctx.extractor_kind,
220
+ ctx.run_id,
221
+ upsert.pending_tool_call.id,
222
+ upsert.pending_tool_call.tool_name,
223
+ )
224
+
225
+ return AsyncAccepted(
226
+ pending_tool_call_id=upsert.pending_tool_call.id,
227
+ result={
228
+ "status": "request_pending",
229
+ "pending_tool_call_id": upsert.pending_tool_call.id,
230
+ "instruction": (
231
+ "Continue extraction using currently available evidence. "
232
+ "This run will be resumed when the answer arrives."
233
+ ),
234
+ },
235
+ )
236
+
237
+
238
+ def handle_attach_pending_info_request(
239
+ args: AttachPendingInfoRequestArgs,
240
+ ctx: PendingToolCallToolContext,
241
+ ) -> dict[str, Any]:
242
+ if not ctx.run_id:
243
+ raise ValueError("attach_pending_info_request requires a durable agent run_id")
244
+
245
+ record = ctx.storage.get_pending_tool_call(args.pending_tool_call_id)
246
+ expected_scope = human_feedback_scope(ctx.org_id)
247
+ current = datetime.now(UTC)
248
+ if (
249
+ record is None
250
+ or record.org_id != ctx.org_id
251
+ or record.tool_name != "ask_human"
252
+ or record.scope_hash != build_scope_hash(expected_scope)
253
+ ):
254
+ return {
255
+ "status": "not_found",
256
+ "instruction": (
257
+ "The referenced Prior Knowledge request is unavailable. "
258
+ "Continue extraction, or call ask_human if the information is still needed."
259
+ ),
260
+ }
261
+
262
+ if record.status == PendingToolCallStatus.RESOLVED:
263
+ if record.valid_until is not None and record.valid_until <= current:
264
+ return {
265
+ "status": "expired",
266
+ "instruction": (
267
+ "The referenced answer is no longer valid. Continue extraction, "
268
+ "or call ask_human if the information is still needed."
269
+ ),
270
+ }
271
+ return {
272
+ "status": "already_resolved",
273
+ "pending_tool_call_id": record.id,
274
+ "question": record.question_text,
275
+ "result": record.result or {},
276
+ "instruction": (
277
+ "Use this result only if it is relevant to the current extraction."
278
+ ),
279
+ }
280
+
281
+ if record.status != PendingToolCallStatus.PENDING or (
282
+ record.expires_at is not None and record.expires_at <= current
283
+ ):
284
+ return {
285
+ "status": "unavailable",
286
+ "instruction": (
287
+ "The referenced request is no longer pending. Continue extraction, "
288
+ "or call ask_human if the information is still needed."
289
+ ),
290
+ }
291
+
292
+ ctx.storage.attach_run_tool_dependency(
293
+ RunToolDependencyRecord(
294
+ run_id=ctx.run_id,
295
+ pending_tool_call_id=record.id,
296
+ )
297
+ )
298
+ logger.info(
299
+ "event=extraction_agent_followup_attached org_id=%s user_id=%s "
300
+ "extractor_kind=%s run_id=%s pending_tool_call_id=%s "
301
+ "tool_name=%s",
302
+ ctx.org_id,
303
+ ctx.user_id,
304
+ ctx.extractor_kind,
305
+ ctx.run_id,
306
+ record.id,
307
+ record.tool_name,
308
+ )
309
+ return {
310
+ "status": "attached_for_followup",
311
+ "pending_tool_call_id": record.id,
312
+ "instruction": (
313
+ "Continue extraction using currently available evidence. "
314
+ "This run will be resumed when the pending answer arrives."
315
+ ),
316
+ }
317
+
318
+
319
+ def _handle_ask_human_tool(args: BaseModel, ctx: Any) -> AsyncAccepted:
320
+ if not isinstance(args, AskHumanArgs):
321
+ raise TypeError(f"Expected AskHumanArgs, got {type(args).__name__}")
322
+ ctx = getattr(ctx, "extra_tool_context", ctx)
323
+ if not isinstance(ctx, PendingToolCallToolContext):
324
+ raise TypeError(
325
+ f"Expected PendingToolCallToolContext, got {type(ctx).__name__}"
326
+ )
327
+ return handle_ask_human(args, ctx)
328
+
329
+
330
+ def _handle_attach_pending_info_request_tool(
331
+ args: BaseModel, ctx: Any
332
+ ) -> dict[str, Any]:
333
+ if not isinstance(args, AttachPendingInfoRequestArgs):
334
+ raise TypeError(
335
+ f"Expected AttachPendingInfoRequestArgs, got {type(args).__name__}"
336
+ )
337
+ ctx = getattr(ctx, "extra_tool_context", ctx)
338
+ if not isinstance(ctx, PendingToolCallToolContext):
339
+ raise TypeError(
340
+ f"Expected PendingToolCallToolContext, got {type(ctx).__name__}"
341
+ )
342
+ return handle_attach_pending_info_request(args, ctx)
343
+
344
+
345
+ def create_ask_human_tool() -> AsyncInfoTool:
346
+ return AsyncInfoTool(
347
+ name="ask_human",
348
+ args_model=AskHumanArgs,
349
+ handler=_handle_ask_human_tool,
350
+ )
351
+
352
+
353
+ def create_attach_pending_info_request_tool() -> Tool:
354
+ return Tool(
355
+ name="attach_pending_info_request",
356
+ args_model=AttachPendingInfoRequestArgs,
357
+ handler=_handle_attach_pending_info_request_tool,
358
+ )