claude-smart 0.2.41 → 0.2.43

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 (396) hide show
  1. package/.claude-plugin/marketplace.json +17 -0
  2. package/README.md +1 -1
  3. package/bin/claude-smart.js +86 -48
  4. package/package.json +10 -3
  5. package/plugin/.claude-plugin/plugin.json +9 -3
  6. package/plugin/.codex-plugin/plugin.json +1 -1
  7. package/plugin/README.md +2 -2
  8. package/plugin/dashboard/next.config.ts +9 -1
  9. package/plugin/pyproject.toml +2 -2
  10. package/plugin/scripts/_lib.sh +91 -0
  11. package/plugin/scripts/backend-service.sh +46 -15
  12. package/plugin/scripts/cli.sh +29 -1
  13. package/plugin/scripts/codex-hook.js +72 -4
  14. package/plugin/scripts/dashboard-build.sh +1 -0
  15. package/plugin/scripts/dashboard-service.sh +1 -0
  16. package/plugin/scripts/ensure-plugin-root.sh +7 -14
  17. package/plugin/scripts/hook_entry.sh +1 -0
  18. package/plugin/scripts/smart-install.sh +18 -2
  19. package/plugin/src/claude_smart/cli.py +72 -38
  20. package/plugin/src/claude_smart/context_format.py +11 -12
  21. package/plugin/src/claude_smart/cs_cite.py +26 -12
  22. package/plugin/src/claude_smart/ids.py +13 -5
  23. package/plugin/uv.lock +1 -1
  24. package/plugin/vendor/reflexio/.env.example +53 -0
  25. package/plugin/vendor/reflexio/LICENSE +201 -0
  26. package/plugin/vendor/reflexio/README.md +338 -0
  27. package/plugin/vendor/reflexio/pyproject.toml +271 -0
  28. package/plugin/vendor/reflexio/reflexio/README.md +184 -0
  29. package/plugin/vendor/reflexio/reflexio/__init__.py +166 -0
  30. package/plugin/vendor/reflexio/reflexio/benchmarks/__init__.py +1 -0
  31. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/README.md +109 -0
  32. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/__init__.py +1 -0
  33. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/backends.py +175 -0
  34. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/bench.py +642 -0
  35. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/embed_cache.py +330 -0
  36. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/report.py +317 -0
  37. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/results/report.md +43 -0
  38. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/results/results.json +4478 -0
  39. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/scenarios.py +134 -0
  40. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/seed.py +255 -0
  41. package/plugin/vendor/reflexio/reflexio/cli/README.md +287 -0
  42. package/plugin/vendor/reflexio/reflexio/cli/__init__.py +0 -0
  43. package/plugin/vendor/reflexio/reflexio/cli/__main__.py +56 -0
  44. package/plugin/vendor/reflexio/reflexio/cli/_client.py +86 -0
  45. package/plugin/vendor/reflexio/reflexio/cli/app.py +127 -0
  46. package/plugin/vendor/reflexio/reflexio/cli/bootstrap_config.py +266 -0
  47. package/plugin/vendor/reflexio/reflexio/cli/codex_auth.py +503 -0
  48. package/plugin/vendor/reflexio/reflexio/cli/commands/__init__.py +0 -0
  49. package/plugin/vendor/reflexio/reflexio/cli/commands/admin_cmd.py +65 -0
  50. package/plugin/vendor/reflexio/reflexio/cli/commands/agent_playbooks.py +503 -0
  51. package/plugin/vendor/reflexio/reflexio/cli/commands/api.py +114 -0
  52. package/plugin/vendor/reflexio/reflexio/cli/commands/auth.py +109 -0
  53. package/plugin/vendor/reflexio/reflexio/cli/commands/config_cmd.py +511 -0
  54. package/plugin/vendor/reflexio/reflexio/cli/commands/doctor.py +127 -0
  55. package/plugin/vendor/reflexio/reflexio/cli/commands/embeddings.py +53 -0
  56. package/plugin/vendor/reflexio/reflexio/cli/commands/interactions.py +478 -0
  57. package/plugin/vendor/reflexio/reflexio/cli/commands/profiles.py +303 -0
  58. package/plugin/vendor/reflexio/reflexio/cli/commands/services.py +289 -0
  59. package/plugin/vendor/reflexio/reflexio/cli/commands/setup_cmd.py +961 -0
  60. package/plugin/vendor/reflexio/reflexio/cli/commands/shortcuts.py +285 -0
  61. package/plugin/vendor/reflexio/reflexio/cli/commands/status_cmd.py +143 -0
  62. package/plugin/vendor/reflexio/reflexio/cli/commands/user_playbooks.py +373 -0
  63. package/plugin/vendor/reflexio/reflexio/cli/env_loader.py +284 -0
  64. package/plugin/vendor/reflexio/reflexio/cli/errors.py +217 -0
  65. package/plugin/vendor/reflexio/reflexio/cli/log_format.py +247 -0
  66. package/plugin/vendor/reflexio/reflexio/cli/output.py +867 -0
  67. package/plugin/vendor/reflexio/reflexio/cli/paths.py +41 -0
  68. package/plugin/vendor/reflexio/reflexio/cli/run_services.py +391 -0
  69. package/plugin/vendor/reflexio/reflexio/cli/state.py +204 -0
  70. package/plugin/vendor/reflexio/reflexio/cli/stop_services.py +96 -0
  71. package/plugin/vendor/reflexio/reflexio/cli/utils.py +329 -0
  72. package/plugin/vendor/reflexio/reflexio/client/__init__.py +3 -0
  73. package/plugin/vendor/reflexio/reflexio/client/cache.py +150 -0
  74. package/plugin/vendor/reflexio/reflexio/client/client.py +2613 -0
  75. package/plugin/vendor/reflexio/reflexio/defaults.py +23 -0
  76. package/plugin/vendor/reflexio/reflexio/integrations/__init__.py +0 -0
  77. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/.clawhubignore +7 -0
  78. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/README.md +274 -0
  79. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/TESTING.md +517 -0
  80. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/hook/handler.js +473 -0
  81. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/package-lock.json +2156 -0
  82. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/package.json +18 -0
  83. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/hook/handler.ts +241 -0
  84. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/hook/setup.ts +140 -0
  85. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/index.ts +130 -0
  86. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/publish.ts +113 -0
  87. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/search.ts +52 -0
  88. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/server.ts +103 -0
  89. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/sqlite-buffer.ts +156 -0
  90. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/user-id.ts +134 -0
  91. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/openclaw.plugin.json +41 -0
  92. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/package.json +17 -0
  93. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/rules/reflexio.md +24 -0
  94. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/reflexio/SKILL.md +48 -0
  95. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/publish_clawhub.sh +278 -0
  96. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/references/HOOK.md +164 -0
  97. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/scripts/install.sh +36 -0
  98. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/scripts/uninstall.sh +35 -0
  99. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/publish.test.ts +27 -0
  100. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/search.test.ts +31 -0
  101. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/server.test.ts +42 -0
  102. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/setup.test.ts +49 -0
  103. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/sqlite-buffer.test.ts +91 -0
  104. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/user-id.test.ts +50 -0
  105. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tsconfig.json +16 -0
  106. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/types/openclaw.d.ts +230 -0
  107. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/vitest.config.ts +13 -0
  108. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/README.md +120 -0
  109. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/TESTING.md +168 -0
  110. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/package-lock.json +1657 -0
  111. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/package.json +16 -0
  112. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/HEARTBEAT.md +6 -0
  113. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/README.md +84 -0
  114. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/SKILL.md +194 -0
  115. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/_meta.json +6 -0
  116. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/agents/reflexio-extractor.md +45 -0
  117. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/hook/handler.ts +214 -0
  118. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/hook/setup.ts +55 -0
  119. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/index.ts +327 -0
  120. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/consolidate.ts +233 -0
  121. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/dedup.ts +80 -0
  122. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/io.ts +155 -0
  123. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/openclaw-cli.ts +67 -0
  124. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/search.ts +33 -0
  125. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/write-playbook.ts +76 -0
  126. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/write-profile.ts +79 -0
  127. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/openclaw.plugin.json +46 -0
  128. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/package.json +18 -0
  129. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/prompts/README.md +36 -0
  130. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/prompts/full_consolidation.md +56 -0
  131. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/prompts/playbook_extraction.md +217 -0
  132. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/prompts/profile_extraction.md +132 -0
  133. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/skills/reflexio-consolidate/SKILL.md +33 -0
  134. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/skills/reflexio-embedded/SKILL.md +194 -0
  135. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/HOOK.md +18 -0
  136. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/architecture.md +49 -0
  137. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/comparison.md +31 -0
  138. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/future-work.md +47 -0
  139. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/porting-notes.md +52 -0
  140. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/scripts/install.sh +52 -0
  141. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/scripts/uninstall.sh +36 -0
  142. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/consolidate.test.ts +135 -0
  143. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/dedup.test.ts +104 -0
  144. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/io.test.ts +175 -0
  145. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/search.test.ts +66 -0
  146. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/smoke-test.ts +140 -0
  147. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/write-playbook.test.ts +93 -0
  148. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/write-profile.test.ts +174 -0
  149. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tsconfig.json +16 -0
  150. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/types/openclaw.d.ts +230 -0
  151. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/vitest.config.ts +7 -0
  152. package/plugin/vendor/reflexio/reflexio/lib/__init__.py +23 -0
  153. package/plugin/vendor/reflexio/reflexio/lib/_agent_playbook.py +310 -0
  154. package/plugin/vendor/reflexio/reflexio/lib/_base.py +225 -0
  155. package/plugin/vendor/reflexio/reflexio/lib/_config.py +83 -0
  156. package/plugin/vendor/reflexio/reflexio/lib/_dashboard.py +266 -0
  157. package/plugin/vendor/reflexio/reflexio/lib/_generation.py +176 -0
  158. package/plugin/vendor/reflexio/reflexio/lib/_interactions.py +334 -0
  159. package/plugin/vendor/reflexio/reflexio/lib/_operations.py +153 -0
  160. package/plugin/vendor/reflexio/reflexio/lib/_profiles.py +545 -0
  161. package/plugin/vendor/reflexio/reflexio/lib/_reflection.py +52 -0
  162. package/plugin/vendor/reflexio/reflexio/lib/_search.py +167 -0
  163. package/plugin/vendor/reflexio/reflexio/lib/_storage_labels.py +103 -0
  164. package/plugin/vendor/reflexio/reflexio/lib/_user_playbook.py +288 -0
  165. package/plugin/vendor/reflexio/reflexio/lib/reflexio_lib.py +27 -0
  166. package/plugin/vendor/reflexio/reflexio/models/__init__.py +0 -0
  167. package/plugin/vendor/reflexio/reflexio/models/api_schema/__init__.py +0 -0
  168. package/plugin/vendor/reflexio/reflexio/models/api_schema/braintrust_schema.py +141 -0
  169. package/plugin/vendor/reflexio/reflexio/models/api_schema/common.py +41 -0
  170. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/__init__.py +3 -0
  171. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/entities.py +1103 -0
  172. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/enums.py +63 -0
  173. package/plugin/vendor/reflexio/reflexio/models/api_schema/eval_overview_schema.py +487 -0
  174. package/plugin/vendor/reflexio/reflexio/models/api_schema/internal_schema.py +28 -0
  175. package/plugin/vendor/reflexio/reflexio/models/api_schema/pending_tool_call_schema.py +83 -0
  176. package/plugin/vendor/reflexio/reflexio/models/api_schema/retriever_schema.py +766 -0
  177. package/plugin/vendor/reflexio/reflexio/models/api_schema/service_schemas.py +9 -0
  178. package/plugin/vendor/reflexio/reflexio/models/api_schema/stall_state_schema.py +32 -0
  179. package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/__init__.py +3 -0
  180. package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/converters.py +177 -0
  181. package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/entities.py +129 -0
  182. package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/enums.py +25 -0
  183. package/plugin/vendor/reflexio/reflexio/models/api_schema/validators.py +280 -0
  184. package/plugin/vendor/reflexio/reflexio/models/config_schema.py +908 -0
  185. package/plugin/vendor/reflexio/reflexio/models/py.typed +0 -0
  186. package/plugin/vendor/reflexio/reflexio/server/OVERVIEW.md +90 -0
  187. package/plugin/vendor/reflexio/reflexio/server/README.md +616 -0
  188. package/plugin/vendor/reflexio/reflexio/server/__init__.py +210 -0
  189. package/plugin/vendor/reflexio/reflexio/server/__main__.py +132 -0
  190. package/plugin/vendor/reflexio/reflexio/server/_auth.py +25 -0
  191. package/plugin/vendor/reflexio/reflexio/server/api.py +2714 -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 +540 -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 +110 -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 +1595 -0
  206. package/plugin/vendor/reflexio/reflexio/server/llm/llm_utils.py +112 -0
  207. package/plugin/vendor/reflexio/reflexio/server/llm/model_defaults.py +469 -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 +210 -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 +255 -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 +177 -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 +699 -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_extraction_context/v4.0.2.prompt.md +254 -0
  245. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.1.0.prompt.md +274 -0
  246. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.2.0.prompt.md +279 -0
  247. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v1.0.0.prompt.md +73 -0
  248. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v2.0.0.prompt.md +86 -0
  249. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.0.0.prompt.md +97 -0
  250. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.1.0.prompt.md +119 -0
  251. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.2.0.prompt.md +123 -0
  252. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.3.0.prompt.md +127 -0
  253. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.0.0.prompt.md +14 -0
  254. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.1.0.prompt.md +24 -0
  255. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.2.0.prompt.md +29 -0
  256. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main_expert/v1.0.0.prompt.md +11 -0
  257. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main_expert/v1.1.0.prompt.md +21 -0
  258. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main_expert/v1.2.0.prompt.md +25 -0
  259. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_optimizer_judge/v1.0.0.prompt.md +37 -0
  260. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_optimizer_judge/v1.1.0.prompt.md +40 -0
  261. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_optimizer_judge/v1.2.0.prompt.md +36 -0
  262. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_should_generate/v1.0.0.prompt.md +45 -0
  263. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_should_generate/v2.0.0.prompt.md +81 -0
  264. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_should_generate/v3.0.0.prompt.md +80 -0
  265. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_should_generate_expert/v1.0.0.prompt.md +34 -0
  266. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_deduplication/v1.0.0.prompt.md +116 -0
  267. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_should_generate/v1.0.0.prompt.md +33 -0
  268. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_should_generate_override/v1.0.0.prompt.md +16 -0
  269. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.0.0.prompt.md +140 -0
  270. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.1.0.prompt.md +160 -0
  271. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_main/v1.0.0.prompt.md +14 -0
  272. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/query_reformulation/v1.0.0.prompt.md +19 -0
  273. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/rerank_relevance/v1.1.0.prompt.md +44 -0
  274. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/shadow_comparison/v1.0.0.prompt.md +43 -0
  275. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/shadow_content_evaluation/v1.0.0.prompt.md +33 -0
  276. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_evaluation/prompt_evaluation_dataset/feedback_extraction_main_v1.jsonl +10 -0
  277. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_evaluation/prompt_evaluation_dataset/profile_update_main_v1.jsonl +10 -0
  278. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_manager.py +280 -0
  279. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_schema.py +11 -0
  280. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/_eval_health.py +131 -0
  281. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_constants.py +60 -0
  282. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_service.py +228 -0
  283. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_utils.py +87 -0
  284. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluator.py +372 -0
  285. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/delayed_group_evaluator.py +156 -0
  286. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/group_evaluation_runner.py +340 -0
  287. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/regen_jobs.py +471 -0
  288. package/plugin/vendor/reflexio/reflexio/server/services/base_generation_service.py +1626 -0
  289. package/plugin/vendor/reflexio/reflexio/server/services/braintrust/__init__.py +0 -0
  290. package/plugin/vendor/reflexio/reflexio/server/services/braintrust/_cron.py +196 -0
  291. package/plugin/vendor/reflexio/reflexio/server/services/braintrust/_encryption.py +101 -0
  292. package/plugin/vendor/reflexio/reflexio/server/services/braintrust/client.py +167 -0
  293. package/plugin/vendor/reflexio/reflexio/server/services/braintrust/service.py +281 -0
  294. package/plugin/vendor/reflexio/reflexio/server/services/configurator/base_configurator.py +179 -0
  295. package/plugin/vendor/reflexio/reflexio/server/services/configurator/config_storage.py +62 -0
  296. package/plugin/vendor/reflexio/reflexio/server/services/configurator/configurator.py +87 -0
  297. package/plugin/vendor/reflexio/reflexio/server/services/configurator/local_file_config_storage.py +187 -0
  298. package/plugin/vendor/reflexio/reflexio/server/services/configurator/test_config_storage.py +162 -0
  299. package/plugin/vendor/reflexio/reflexio/server/services/deduplication_utils.py +112 -0
  300. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/__init__.py +0 -0
  301. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/distribution.py +33 -0
  302. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/eval_sampler.py +126 -0
  303. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/group_aggregation.py +192 -0
  304. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/hero_state.py +75 -0
  305. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/rule_attribution.py +97 -0
  306. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/service.py +515 -0
  307. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/shadow_aggregation.py +90 -0
  308. package/plugin/vendor/reflexio/reflexio/server/services/extraction/__init__.py +0 -0
  309. package/plugin/vendor/reflexio/reflexio/server/services/extraction/agent_run_records.py +91 -0
  310. package/plugin/vendor/reflexio/reflexio/server/services/extraction/invariants.py +303 -0
  311. package/plugin/vendor/reflexio/reflexio/server/services/extraction/outcome.py +25 -0
  312. package/plugin/vendor/reflexio/reflexio/server/services/extraction/pending_tool_call_dispatch.py +351 -0
  313. package/plugin/vendor/reflexio/reflexio/server/services/extraction/plan.py +138 -0
  314. package/plugin/vendor/reflexio/reflexio/server/services/extraction/prior_answer_search.py +217 -0
  315. package/plugin/vendor/reflexio/reflexio/server/services/extraction/resumable_agent.py +468 -0
  316. package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_scheduler.py +171 -0
  317. package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_worker.py +777 -0
  318. package/plugin/vendor/reflexio/reflexio/server/services/extraction/tools.py +1125 -0
  319. package/plugin/vendor/reflexio/reflexio/server/services/extractor_config_utils.py +91 -0
  320. package/plugin/vendor/reflexio/reflexio/server/services/extractor_interaction_utils.py +251 -0
  321. package/plugin/vendor/reflexio/reflexio/server/services/generation_service.py +689 -0
  322. package/plugin/vendor/reflexio/reflexio/server/services/operation_state_utils.py +835 -0
  323. package/plugin/vendor/reflexio/reflexio/server/services/playbook/README.md +89 -0
  324. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_aggregator.py +1388 -0
  325. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_consolidator.py +960 -0
  326. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_extractor.py +436 -0
  327. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_generation_service.py +808 -0
  328. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_service_constants.py +28 -0
  329. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_service_utils.py +362 -0
  330. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/__init__.py +24 -0
  331. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/assistant_webhook.py +246 -0
  332. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/gepa_adapter.py +291 -0
  333. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/judge.py +97 -0
  334. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/models.py +96 -0
  335. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/optimizer.py +645 -0
  336. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/rollout.py +35 -0
  337. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/scenario_resolver.py +93 -0
  338. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/scheduler.py +174 -0
  339. package/plugin/vendor/reflexio/reflexio/server/services/pre_retrieval/__init__.py +26 -0
  340. package/plugin/vendor/reflexio/reflexio/server/services/pre_retrieval/_document_expander.py +179 -0
  341. package/plugin/vendor/reflexio/reflexio/server/services/pre_retrieval/_query_reformulator.py +297 -0
  342. package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_deduplicator.py +741 -0
  343. package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_extractor.py +462 -0
  344. package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_generation_service.py +734 -0
  345. package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_generation_service_utils.py +290 -0
  346. package/plugin/vendor/reflexio/reflexio/server/services/reflection/__init__.py +17 -0
  347. package/plugin/vendor/reflexio/reflexio/server/services/reflection/reflection_extractor.py +247 -0
  348. package/plugin/vendor/reflexio/reflexio/server/services/reflection/reflection_service.py +800 -0
  349. package/plugin/vendor/reflexio/reflexio/server/services/reflection/reflection_service_utils.py +146 -0
  350. package/plugin/vendor/reflexio/reflexio/server/services/retrieval/__init__.py +0 -0
  351. package/plugin/vendor/reflexio/reflexio/server/services/retrieval/relevance_floor.py +70 -0
  352. package/plugin/vendor/reflexio/reflexio/server/services/search/__init__.py +0 -0
  353. package/plugin/vendor/reflexio/reflexio/server/services/service_utils.py +671 -0
  354. package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/__init__.py +1 -0
  355. package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/judge.py +184 -0
  356. package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/outcome.py +81 -0
  357. package/plugin/vendor/reflexio/reflexio/server/services/storage/constants.py +2 -0
  358. package/plugin/vendor/reflexio/reflexio/server/services/storage/error.py +11 -0
  359. package/plugin/vendor/reflexio/reflexio/server/services/storage/retention.py +154 -0
  360. package/plugin/vendor/reflexio/reflexio/server/services/storage/retention_mixin.py +155 -0
  361. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/__init__.py +59 -0
  362. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_agent_run.py +1253 -0
  363. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_base.py +1945 -0
  364. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_extras.py +600 -0
  365. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_operations.py +346 -0
  366. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_playbook.py +1378 -0
  367. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_profiles.py +747 -0
  368. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_requests.py +263 -0
  369. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_shadow_verdicts.py +193 -0
  370. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_share_links.py +166 -0
  371. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_stall_state.py +217 -0
  372. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/__init__.py +153 -0
  373. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_agent_run.py +372 -0
  374. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_base.py +71 -0
  375. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_extras.py +235 -0
  376. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_operations.py +170 -0
  377. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_playbook.py +677 -0
  378. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_profiles.py +250 -0
  379. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_requests.py +154 -0
  380. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_shadow_verdicts.py +130 -0
  381. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_share_links.py +93 -0
  382. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_stall_state.py +76 -0
  383. package/plugin/vendor/reflexio/reflexio/server/services/unified_search_service.py +568 -0
  384. package/plugin/vendor/reflexio/reflexio/server/site_var/README.md +77 -0
  385. package/plugin/vendor/reflexio/reflexio/server/site_var/feature_flags.py +116 -0
  386. package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_manager.py +263 -0
  387. package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_sources/feature_flags.json +13 -0
  388. package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_sources/llm_model_setting.json +7 -0
  389. package/plugin/vendor/reflexio/reflexio/server/tracing.py +158 -0
  390. package/plugin/vendor/reflexio/reflexio/server/usage_metrics.py +113 -0
  391. package/plugin/vendor/reflexio/reflexio/server/uvicorn_logging.py +76 -0
  392. package/plugin/vendor/reflexio/reflexio/test_support/__init__.py +1 -0
  393. package/plugin/vendor/reflexio/reflexio/test_support/llm_fixtures.py +62 -0
  394. package/plugin/vendor/reflexio/reflexio/test_support/llm_mock.py +242 -0
  395. package/plugin/vendor/reflexio/reflexio/test_support/llm_model_registry.py +129 -0
  396. package/plugin/vendor/reflexio/reflexio/test_support/skip_decorators.py +43 -0
@@ -0,0 +1,197 @@
1
+ """Parse the NDJSON stream from ``claude -p --output-format stream-json``
2
+ and classify whether the call ended in a credit/auth stall.
3
+
4
+ Public surface:
5
+ - parse_stream_json(stdout, exit_code, stderr_text) -> ParseResult
6
+ - classify_stall(result) -> "billing_error" | "auth_error" | None
7
+ - parse_reset_estimate(text) -> datetime | None
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import json
13
+ import logging
14
+ import re
15
+ from dataclasses import dataclass, field
16
+ from datetime import UTC, datetime, time, timedelta
17
+
18
+ from reflexio.models.api_schema.stall_state_schema import StallReason
19
+
20
+ _LOGGER = logging.getLogger(__name__)
21
+
22
+ _BILLING_CATEGORIES = {"billing_error"}
23
+ _AUTH_CATEGORIES = {"authentication_failed", "oauth_org_not_allowed"}
24
+
25
+ _TEXT_PATTERNS_BILLING = (
26
+ "hit your weekly limit",
27
+ "hit your session limit",
28
+ "credit balance is too low",
29
+ "billing_error",
30
+ )
31
+ _TEXT_PATTERNS_AUTH = (
32
+ "please run /login",
33
+ "oauth token revoked",
34
+ "oauth token has expired",
35
+ "not logged in",
36
+ )
37
+
38
+ _RESET_RE = re.compile(
39
+ r"resets\s+(?:(?P<weekday>mon|tue|wed|thu|fri|sat|sun)\w*\s+)?"
40
+ r"(?P<hour>\d{1,2}):(?P<minute>\d{2})\s*(?P<ampm>am|pm)",
41
+ re.IGNORECASE,
42
+ )
43
+
44
+
45
+ @dataclass
46
+ class ParseResult:
47
+ """Aggregated state of one ``claude -p`` invocation.
48
+
49
+ Attributes:
50
+ success (bool): True iff exit_code==0 AND a terminal result event
51
+ with non-empty text appeared.
52
+ terminal_text (str): The ``result`` field from the terminal event.
53
+ retry_errors (list[str]): All ``error`` strings observed in
54
+ ``api_retry`` events, in order.
55
+ stderr_text (str): Raw stderr text from the subprocess.
56
+ raw_lines_parsed (int): NDJSON lines parsed successfully.
57
+ raw_lines_failed (int): NDJSON lines that failed to parse.
58
+ """
59
+
60
+ success: bool
61
+ terminal_text: str
62
+ retry_errors: list[str] = field(default_factory=list)
63
+ stderr_text: str = ""
64
+ raw_lines_parsed: int = 0
65
+ raw_lines_failed: int = 0
66
+
67
+ @property
68
+ def stall_candidate(self) -> str | None:
69
+ """The last retry error string in a stall category, or None."""
70
+ for err in reversed(self.retry_errors):
71
+ if err in _BILLING_CATEGORIES or err in _AUTH_CATEGORIES:
72
+ return err
73
+ return None
74
+
75
+
76
+ def parse_stream_json(
77
+ stdout: str,
78
+ *,
79
+ exit_code: int,
80
+ stderr_text: str = "",
81
+ ) -> ParseResult:
82
+ """Parse the full NDJSON stream from claude -p.
83
+
84
+ Args:
85
+ stdout (str): Raw subprocess stdout — newline-delimited JSON events.
86
+ exit_code (int): Subprocess exit code.
87
+ stderr_text (str): Raw stderr — used by the text-fallback path.
88
+
89
+ Returns:
90
+ ParseResult: Aggregated state. ``success`` requires both a clean
91
+ exit and a parseable terminal ``result`` event.
92
+ """
93
+ retry_errors: list[str] = []
94
+ terminal_text = ""
95
+ parsed = 0
96
+ failed = 0
97
+ saw_terminal = False
98
+ for line in stdout.splitlines():
99
+ if not line.strip():
100
+ continue
101
+ try:
102
+ event = json.loads(line)
103
+ except json.JSONDecodeError:
104
+ failed += 1
105
+ continue
106
+ parsed += 1
107
+ if not isinstance(event, dict):
108
+ continue
109
+ match event.get("type"), event.get("subtype"):
110
+ case ("system", "api_retry"):
111
+ err = event.get("error")
112
+ if isinstance(err, str):
113
+ retry_errors.append(err)
114
+ case ("result", _) | (_, "result"):
115
+ text = event.get("result")
116
+ if isinstance(text, str):
117
+ terminal_text = text
118
+ saw_terminal = True
119
+ return ParseResult(
120
+ success=(exit_code == 0 and saw_terminal and bool(terminal_text)),
121
+ terminal_text=terminal_text,
122
+ retry_errors=retry_errors,
123
+ stderr_text=stderr_text,
124
+ raw_lines_parsed=parsed,
125
+ raw_lines_failed=failed,
126
+ )
127
+
128
+
129
+ def classify_stall(result: ParseResult) -> StallReason | None:
130
+ """Decide whether a finished ParseResult represents a stall.
131
+
132
+ Args:
133
+ result (ParseResult): Output of :func:`parse_stream_json`.
134
+
135
+ Returns:
136
+ StallReason | None: ``"billing_error"`` or ``"auth_error"`` only
137
+ when the call terminated unsuccessfully AND a stall-class
138
+ signal was observed (event or text fallback).
139
+ """
140
+ if result.success:
141
+ return None
142
+ candidate = result.stall_candidate
143
+ if candidate in _BILLING_CATEGORIES:
144
+ return "billing_error"
145
+ if candidate in _AUTH_CATEGORIES:
146
+ return "auth_error"
147
+ return _classify_from_text(result.stderr_text + " " + result.terminal_text)
148
+
149
+
150
+ def _classify_from_text(text: str) -> StallReason | None:
151
+ """Belt-and-suspenders: regex the raw error text after NDJSON has been parsed."""
152
+ lower = text.lower()
153
+ if any(p in lower for p in _TEXT_PATTERNS_BILLING):
154
+ _LOGGER.warning("Stall classified via text fallback (billing): %r", text[:200])
155
+ return "billing_error"
156
+ if any(p in lower for p in _TEXT_PATTERNS_AUTH):
157
+ _LOGGER.warning("Stall classified via text fallback (auth): %r", text[:200])
158
+ return "auth_error"
159
+ return None
160
+
161
+
162
+ def parse_reset_estimate(text: str) -> datetime | None:
163
+ """Best-effort parse of a reset time from Claude Code error text.
164
+
165
+ The hour/minute extracted from the message text is treated as UTC, then
166
+ advanced one day if it has already passed. Claude Code's error messages
167
+ typically express reset times in the user's local timezone, so the
168
+ returned datetime may be off by the user's UTC offset (up to ±24h).
169
+ Callers should treat this as an approximation, not an authoritative
170
+ reset moment.
171
+
172
+ Args:
173
+ text (str): Error text — typically the terminal event message or stderr.
174
+
175
+ Returns:
176
+ datetime | None: A UTC-naive-in-spirit but tz-aware datetime that
177
+ best approximates the next reset, or None when no recognizable
178
+ pattern is found.
179
+ """
180
+ match = _RESET_RE.search(text or "")
181
+ if not match:
182
+ return None
183
+ hour_raw = int(match.group("hour"))
184
+ minute = int(match.group("minute"))
185
+ # The regex permits 1–2 digit hours and minutes; reject out-of-range
186
+ # values so "13:00pm" or "10:75am" parse as None instead of producing
187
+ # silently-wrong times via modulo arithmetic.
188
+ if not 1 <= hour_raw <= 12 or not 0 <= minute <= 59:
189
+ return None
190
+ hour = hour_raw % 12
191
+ if match.group("ampm").lower() == "pm":
192
+ hour += 12
193
+ today = datetime.now(UTC).date()
194
+ candidate = datetime.combine(today, time(hour, minute), tzinfo=UTC)
195
+ if candidate <= datetime.now(UTC):
196
+ candidate += timedelta(days=1)
197
+ return candidate
@@ -0,0 +1,210 @@
1
+ """HTTP embedding service provider.
2
+
3
+ This module is the routing boundary between Reflexio's LLM client and an
4
+ OpenAI-compatible embedding service. It lets local Claude Smart installs keep
5
+ one model process alive across backend workers, Claude Code, and Codex, while
6
+ also supporting a horizontally-scaled internal service in cloud deployments.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import logging
12
+ import os
13
+ import time
14
+ from typing import Any, Literal
15
+
16
+ import httpx
17
+
18
+ _LOGGER = logging.getLogger(__name__)
19
+
20
+ EmbeddingProviderMode = Literal[
21
+ "cloud", "local_service", "internal_service", "inprocess", "off"
22
+ ]
23
+
24
+ _ENV_PROVIDER = "REFLEXIO_EMBEDDING_PROVIDER"
25
+ _ENV_SERVICE_URL = "REFLEXIO_EMBEDDING_SERVICE_URL"
26
+ _ENV_TIMEOUT_MS = "REFLEXIO_EMBEDDING_SERVICE_TIMEOUT_MS"
27
+ _ENV_EMBEDDING_PORT = "EMBEDDING_PORT"
28
+ _ENV_CLAUDE_SMART_LOCAL = "CLAUDE_SMART_USE_LOCAL_EMBEDDING"
29
+ _DEFAULT_LOCAL_PORT = 8072
30
+ _DEFAULT_INTERNAL_SERVICE_TIMEOUT_MS = 2_000
31
+ _DEFAULT_LOCAL_SERVICE_TIMEOUT_MS = 30_000
32
+ _SERVICE_MODES = {"local_service", "internal_service"}
33
+ _VALID_MODES = {"cloud", *_SERVICE_MODES, "inprocess", "off"}
34
+
35
+
36
+ class EmbeddingUnavailableError(RuntimeError):
37
+ """Raised when the configured embedding provider is unavailable."""
38
+
39
+
40
+ def _local_service_url() -> str:
41
+ port = os.environ.get(_ENV_EMBEDDING_PORT, str(_DEFAULT_LOCAL_PORT))
42
+ return f"http://127.0.0.1:{port}"
43
+
44
+
45
+ def embedding_service_url(mode: EmbeddingProviderMode | None = None) -> str:
46
+ """Return the configured embedding service URL.
47
+
48
+ ``local_service`` defaults to ``127.0.0.1:$EMBEDDING_PORT`` so local
49
+ Claude Smart hosts can share a single machine daemon. ``internal_service``
50
+ requires an explicit URL because it is deployment-specific.
51
+ """
52
+ resolved = mode or embedding_provider_mode()
53
+ configured = os.environ.get(_ENV_SERVICE_URL)
54
+ if configured:
55
+ return configured.rstrip("/")
56
+ if resolved == "local_service":
57
+ return _local_service_url()
58
+ raise EmbeddingUnavailableError(
59
+ f"{_ENV_SERVICE_URL} is required when {_ENV_PROVIDER}={resolved}"
60
+ )
61
+
62
+
63
+ def embedding_service_timeout_seconds(
64
+ mode: EmbeddingProviderMode | None = None,
65
+ ) -> float:
66
+ raw = os.environ.get(_ENV_TIMEOUT_MS)
67
+ if raw is None:
68
+ resolved = mode or embedding_provider_mode()
69
+ if resolved == "local_service":
70
+ return _DEFAULT_LOCAL_SERVICE_TIMEOUT_MS / 1000
71
+ return _DEFAULT_INTERNAL_SERVICE_TIMEOUT_MS / 1000
72
+ try:
73
+ timeout_ms = int(raw)
74
+ except ValueError as exc:
75
+ raise EmbeddingUnavailableError(
76
+ f"{_ENV_TIMEOUT_MS} must be an integer number of milliseconds"
77
+ ) from exc
78
+ return max(timeout_ms, 1) / 1000
79
+
80
+
81
+ def _ordered_embeddings_from_response(
82
+ data: Any, expected_count: int
83
+ ) -> list[list[float]]:
84
+ if not isinstance(data, list):
85
+ raise ValueError("embedding service response is missing data[]")
86
+ if len(data) != expected_count:
87
+ raise ValueError(
88
+ "embedding service response cardinality mismatch: "
89
+ f"expected {expected_count}, got {len(data)}"
90
+ )
91
+
92
+ seen: set[int] = set()
93
+ indexed_embeddings: list[tuple[int, list[Any]]] = []
94
+ for item in data:
95
+ if not isinstance(item, dict):
96
+ raise ValueError("embedding service response data[] has invalid item")
97
+
98
+ index = item.get("index")
99
+ if type(index) is not int:
100
+ raise ValueError("embedding service response has invalid index")
101
+ if index in seen:
102
+ raise ValueError(f"embedding service response has duplicate index {index}")
103
+ seen.add(index)
104
+
105
+ embedding = item.get("embedding")
106
+ if not isinstance(embedding, list):
107
+ raise ValueError("embedding service response has invalid embeddings")
108
+ indexed_embeddings.append((index, embedding))
109
+
110
+ expected_indices = set(range(expected_count))
111
+ if seen != expected_indices:
112
+ raise ValueError(
113
+ "embedding service response indices mismatch: "
114
+ f"expected {sorted(expected_indices)}, got {sorted(seen)}"
115
+ )
116
+
117
+ return [
118
+ [float(value) for value in embedding]
119
+ for _, embedding in sorted(indexed_embeddings)
120
+ ]
121
+
122
+
123
+ def embedding_provider_mode(model: str | None = None) -> EmbeddingProviderMode:
124
+ """Resolve the embedding provider mode for a model.
125
+
126
+ Backwards compatibility: a ``local/*`` model still uses the in-process
127
+ embedder unless the user explicitly chooses a mode or Claude Smart's legacy
128
+ ``CLAUDE_SMART_USE_LOCAL_EMBEDDING=1`` flag is present. That legacy flag
129
+ now means "use the shared local service" by default.
130
+ """
131
+ configured = os.environ.get(_ENV_PROVIDER)
132
+ if configured:
133
+ mode = configured.strip().lower()
134
+ if mode not in _VALID_MODES:
135
+ raise EmbeddingUnavailableError(
136
+ f"Invalid {_ENV_PROVIDER}={configured!r}; expected one of "
137
+ f"{', '.join(sorted(_VALID_MODES))}"
138
+ )
139
+ return mode # type: ignore[return-value]
140
+
141
+ if os.environ.get(_ENV_CLAUDE_SMART_LOCAL) == "1":
142
+ return "local_service"
143
+
144
+ if os.environ.get(_ENV_SERVICE_URL):
145
+ return "internal_service"
146
+
147
+ if model and model.startswith("local/"):
148
+ return "inprocess"
149
+ return "cloud"
150
+
151
+
152
+ def should_use_embedding_service(model: str) -> bool:
153
+ """Return True when embedding requests should call the HTTP service."""
154
+ return embedding_provider_mode(model) in _SERVICE_MODES
155
+
156
+
157
+ def get_service_embeddings(
158
+ texts: list[str],
159
+ *,
160
+ model: str,
161
+ dimensions: int | None = None,
162
+ ) -> list[list[float]]:
163
+ """Call the OpenAI-compatible embedding service.
164
+
165
+ Args:
166
+ texts: Inputs to embed.
167
+ model: Embedding model name.
168
+ dimensions: Optional embedding dimensions.
169
+
170
+ Returns:
171
+ Embeddings in input order.
172
+
173
+ Raises:
174
+ EmbeddingUnavailableError: If the service cannot be reached or returns
175
+ an invalid response.
176
+ """
177
+ if not texts:
178
+ return []
179
+
180
+ mode = embedding_provider_mode(model)
181
+ if mode == "off":
182
+ raise EmbeddingUnavailableError("Embedding provider is disabled")
183
+ if mode not in _SERVICE_MODES:
184
+ raise EmbeddingUnavailableError(
185
+ f"Embedding service requested while {_ENV_PROVIDER}={mode}"
186
+ )
187
+
188
+ url = f"{embedding_service_url(mode)}/v1/embeddings"
189
+ payload: dict[str, Any] = {"model": model, "input": texts}
190
+ if dimensions:
191
+ payload["dimensions"] = dimensions
192
+
193
+ timeout = embedding_service_timeout_seconds(mode)
194
+ last_error: Exception | None = None
195
+ for attempt in range(2):
196
+ try:
197
+ with httpx.Client(timeout=timeout) as client:
198
+ response = client.post(url, json=payload)
199
+ response.raise_for_status()
200
+ body = response.json()
201
+ return _ordered_embeddings_from_response(body.get("data"), len(texts))
202
+ except Exception as exc: # noqa: BLE001 - normalized to typed degradation signal
203
+ last_error = exc
204
+ if attempt == 0:
205
+ time.sleep(0.1)
206
+
207
+ _LOGGER.warning("Embedding service unavailable at %s: %s", url, last_error)
208
+ raise EmbeddingUnavailableError(
209
+ f"Embedding service unavailable at {url}: {last_error}"
210
+ ) from last_error
@@ -0,0 +1,213 @@
1
+ """Local in-process embedder using Chroma's ONNX all-MiniLM-L6-v2.
2
+
3
+ Lets reflexio run without any external embedding API key. Activation is
4
+ opt-in via ``CLAUDE_SMART_USE_LOCAL_EMBEDDING=1`` and requires the
5
+ ``chromadb`` pip package to be installed (we re-use its packaged ONNX
6
+ model + tokenizer rather than re-bundling them).
7
+
8
+ The model natively produces 384-dim vectors; reflexio's storage schema
9
+ expects 512 dims (``EMBEDDING_DIMENSIONS`` in the vec0 virtual tables).
10
+ We zero-pad each vector to 512 inside this module so the rest of
11
+ reflexio is unchanged. Cosine similarity is preserved on the 384-dim
12
+ subspace — safe as long as *all* embeddings in a given DB come from
13
+ this provider (mixing providers has always required a DB wipe).
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import importlib.util
19
+ import logging
20
+ import os
21
+ import threading
22
+ from typing import Any
23
+
24
+ _LOGGER = logging.getLogger(__name__)
25
+
26
+ _ENV_ENABLE = "CLAUDE_SMART_USE_LOCAL_EMBEDDING"
27
+ _MODEL_KEY = "local/minilm-l6-v2"
28
+
29
+ # Reflexio's storage schema (vec0 virtual tables) expects this dimension.
30
+ # MiniLM-L6-v2 natively produces 384; we pad with zeros to _TARGET_DIM.
31
+ _NATIVE_DIM = 384
32
+ _TARGET_DIM = 512
33
+
34
+ # Conservative character budget to stay under MiniLM's 256-token hard cap.
35
+ # ~4 chars/token in English prose; leave headroom so we never raise the
36
+ # ValueError that ONNXMiniLM_L6_V2 throws on over-length input.
37
+ _MAX_CHARS = 800
38
+
39
+
40
+ class LocalEmbedderError(RuntimeError):
41
+ """Raised when the local embedder is called without chromadb installed."""
42
+
43
+
44
+ class LocalEmbedder:
45
+ """Lazily-loaded singleton wrapping Chroma's ONNXMiniLM_L6_V2."""
46
+
47
+ _instance: LocalEmbedder | None = None
48
+ _lock = threading.Lock()
49
+
50
+ def __init__(self) -> None:
51
+ self._ef: Any | None = None
52
+ self._ef_lock = threading.Lock()
53
+
54
+ @classmethod
55
+ def get(cls) -> LocalEmbedder:
56
+ """Return the process-wide singleton, constructing it on first use."""
57
+ if cls._instance is None:
58
+ with cls._lock:
59
+ if cls._instance is None:
60
+ cls._instance = cls()
61
+ return cls._instance
62
+
63
+ def _load(self) -> Any:
64
+ """Lazy-import and instantiate the ONNX embedding function."""
65
+ if self._ef is not None:
66
+ return self._ef
67
+ with self._ef_lock:
68
+ if self._ef is not None:
69
+ return self._ef
70
+ try:
71
+ from chromadb.utils.embedding_functions import ( # type: ignore[import-not-found]
72
+ ONNXMiniLM_L6_V2,
73
+ )
74
+ except ImportError as exc:
75
+ raise LocalEmbedderError(
76
+ f"{_ENV_ENABLE}=1 but `chromadb` is not installed. "
77
+ "Install with `uv add chromadb` or `pip install chromadb`."
78
+ ) from exc
79
+ self._ef = ONNXMiniLM_L6_V2()
80
+ _LOGGER.info(
81
+ "Initialized local ONNX embedder (model=%s, cache=%s)",
82
+ ONNXMiniLM_L6_V2.MODEL_NAME,
83
+ ONNXMiniLM_L6_V2.DOWNLOAD_PATH,
84
+ )
85
+ return self._ef
86
+
87
+ def embed(self, texts: list[str]) -> list[list[float]]:
88
+ """Embed a batch of documents, returning 512-dim padded vectors.
89
+
90
+ Args:
91
+ texts: Documents to embed. Each is truncated to ``_MAX_CHARS``
92
+ characters to stay under the 256-token cap of MiniLM-L6-v2.
93
+
94
+ Returns:
95
+ list[list[float]]: One vector per input, each exactly
96
+ ``_TARGET_DIM`` (512) floats with the last 128 positions
97
+ zero-padded.
98
+ """
99
+ ef = self._load()
100
+ safe_inputs = [(text or "")[:_MAX_CHARS] for text in texts]
101
+ raw = ef(safe_inputs)
102
+ return [_pad(vec) for vec in raw]
103
+
104
+
105
+ def _pad(vec: Any) -> list[float]:
106
+ """Zero-pad a 384-dim vector to ``_TARGET_DIM`` as a plain list[float]."""
107
+ as_list = list(vec) if not isinstance(vec, list) else vec
108
+ floats = [float(x) for x in as_list]
109
+ if len(floats) == _TARGET_DIM:
110
+ return floats
111
+ if len(floats) > _TARGET_DIM:
112
+ return floats[:_TARGET_DIM]
113
+ return floats + [0.0] * (_TARGET_DIM - len(floats))
114
+
115
+
116
+ _REGISTERED = False
117
+
118
+
119
+ def is_chromadb_importable() -> bool:
120
+ """Return True when the ``chromadb`` package is importable.
121
+
122
+ Independent of :data:`_ENV_ENABLE` (``CLAUDE_SMART_USE_LOCAL_EMBEDDING``).
123
+ Used by callers that want to know whether the local fallback is
124
+ *possible* regardless of whether claude-smart has explicitly opted in.
125
+
126
+ Returns:
127
+ bool: True if ``importlib.util.find_spec("chromadb")`` finds the
128
+ package, False otherwise.
129
+ """
130
+ return importlib.util.find_spec("chromadb") is not None
131
+
132
+
133
+ def register_if_chromadb_available() -> bool:
134
+ """Make the local embedder available whenever ``chromadb`` imports.
135
+
136
+ This is the env-var-independent companion to
137
+ :func:`register_if_enabled`. Called once from ``litellm_client`` at
138
+ module import so the local-embedder dispatch is wired up regardless
139
+ of whether claude-smart's opt-in env var is set. Idempotent — safe
140
+ to call more than once per process.
141
+
142
+ The actual routing is done by a prefix check in
143
+ ``LiteLLMClient.get_embedding(s)``; this function's job is to
144
+ eagerly probe for ``chromadb`` and log a clear message.
145
+
146
+ Returns:
147
+ bool: True if the embedder is usable after this call, False
148
+ when ``chromadb`` is not importable.
149
+ """
150
+ global _REGISTERED
151
+ if _REGISTERED:
152
+ return True
153
+ if not is_chromadb_importable():
154
+ _LOGGER.debug("Local embedder not registered: `chromadb` is not installed.")
155
+ return False
156
+ _REGISTERED = True
157
+ _LOGGER.info("Local embedding provider enabled (model=%s)", _MODEL_KEY)
158
+ return True
159
+
160
+
161
+ def register_if_enabled() -> bool:
162
+ """Backwards-compatible alias for :func:`register_if_chromadb_available`.
163
+
164
+ Historically gated on ``CLAUDE_SMART_USE_LOCAL_EMBEDDING=1``; now
165
+ delegates to the chromadb-only check so the local embedder is also
166
+ available as a silent fallback when no cloud embedder is configured.
167
+ The claude-smart opt-in env var continues to drive
168
+ :func:`is_local_embedder_available` (provider-priority ordering),
169
+ which is the public contract the env var actually owns.
170
+
171
+ Returns:
172
+ bool: True if the embedder is usable after this call, False
173
+ otherwise.
174
+ """
175
+ return register_if_chromadb_available()
176
+
177
+
178
+ def is_enabled() -> bool:
179
+ """Return True when a previous registration call has succeeded.
180
+
181
+ Returns:
182
+ bool: True if the provider is currently registered and usable in
183
+ this process, False otherwise.
184
+ """
185
+ return _REGISTERED
186
+
187
+
188
+ def is_local_embedder_available() -> bool:
189
+ """Return True iff both the env flag is set and ``chromadb`` imports.
190
+
191
+ Unlike :func:`is_enabled`, this does not require
192
+ :func:`register_if_enabled` to have run. It is the predicate
193
+ ``model_defaults.detect_available_providers`` uses to decide whether
194
+ to surface ``"local"`` as an option.
195
+
196
+ Returns:
197
+ bool: True when ``CLAUDE_SMART_USE_LOCAL_EMBEDDING=1``
198
+ AND ``chromadb`` is importable.
199
+ """
200
+ if os.environ.get(_ENV_ENABLE) != "1":
201
+ return False
202
+ return is_chromadb_importable()
203
+
204
+
205
+ __all__ = [
206
+ "LocalEmbedder",
207
+ "LocalEmbedderError",
208
+ "is_chromadb_importable",
209
+ "is_enabled",
210
+ "is_local_embedder_available",
211
+ "register_if_chromadb_available",
212
+ "register_if_enabled",
213
+ ]