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,1298 @@
1
+ """SQLite storage for resumable extraction agent runs."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import sqlite3
6
+ from datetime import UTC, datetime, timedelta
7
+ from typing import Any
8
+
9
+ from reflexio.server.services.storage.storage_base import (
10
+ AgentBinding,
11
+ AgentRunRecord,
12
+ AgentRunStatus,
13
+ PendingToolCallRecord,
14
+ PendingToolCallStatus,
15
+ PendingToolCallUpsertResult,
16
+ PriorAnswerMatch,
17
+ RunToolDependencyKind,
18
+ RunToolDependencyRecord,
19
+ embedding_similarity,
20
+ not_applicable_tool_result,
21
+ )
22
+
23
+ from ._base import SQLiteStorageBase, _json_dumps, _json_loads
24
+
25
+
26
+ def _dt(value: str | None) -> datetime | None:
27
+ if value is None:
28
+ return None
29
+ return datetime.fromisoformat(value)
30
+
31
+
32
+ def _dt_str(value: datetime | None) -> str | None:
33
+ if value is None:
34
+ return None
35
+ if value.tzinfo is None:
36
+ value = value.replace(tzinfo=UTC)
37
+ return value.astimezone(UTC).isoformat()
38
+
39
+
40
+ def _row_to_agent_run(row: sqlite3.Row) -> AgentRunRecord:
41
+ data = dict(row)
42
+ binding = AgentBinding(
43
+ org_id=data["org_id"],
44
+ extractor_kind=data["extractor_kind"],
45
+ user_id=data.get("user_id"),
46
+ request_id=data["request_id"],
47
+ agent_version=data.get("agent_version"),
48
+ source=data.get("source"),
49
+ source_interaction_ids=_json_loads(data.get("source_interaction_ids")) or [],
50
+ window_start_interaction_id=data.get("window_start_interaction_id"),
51
+ window_end_interaction_id=data.get("window_end_interaction_id"),
52
+ extractor_config_hash=data.get("extractor_config_hash"),
53
+ )
54
+ return AgentRunRecord(
55
+ id=data["id"],
56
+ binding=binding,
57
+ status=AgentRunStatus(data["status"]),
58
+ generation_request_snapshot=_json_loads(data.get("generation_request_snapshot"))
59
+ or {},
60
+ service_config_snapshot=_json_loads(data.get("service_config_snapshot")),
61
+ agent_context_snapshot=data.get("agent_context_snapshot"),
62
+ committed_output=_json_loads(data.get("committed_output")),
63
+ pending_tool_call_ids=_json_loads(data.get("pending_tool_call_ids")) or [],
64
+ max_steps_remaining=(
65
+ int(data["max_steps_remaining"])
66
+ if data.get("max_steps_remaining") is not None
67
+ else None
68
+ ),
69
+ resume_attempts=int(data.get("resume_attempts") or 0),
70
+ finalization_attempts=int(data.get("finalization_attempts") or 0),
71
+ next_resume_at=_dt(data.get("next_resume_at")),
72
+ claimed_by=data.get("claimed_by"),
73
+ claimed_at=_dt(data.get("claimed_at")),
74
+ agent_completed_at=_dt(data.get("agent_completed_at")),
75
+ finalized_at=_dt(data.get("finalized_at")),
76
+ created_at=_dt(data.get("created_at")),
77
+ updated_at=_dt(data.get("updated_at")),
78
+ expires_at=_dt(data.get("expires_at")),
79
+ last_error=data.get("last_error"),
80
+ )
81
+
82
+
83
+ def _row_to_pending_tool_call(row: sqlite3.Row) -> PendingToolCallRecord:
84
+ data = dict(row)
85
+ return PendingToolCallRecord(
86
+ id=data["id"],
87
+ org_id=data["org_id"],
88
+ scope=_json_loads(data.get("scope")) or {},
89
+ scope_hash=data["scope_hash"],
90
+ tool_name=data["tool_name"],
91
+ dedup_key=data["dedup_key"],
92
+ status=PendingToolCallStatus(data["status"]),
93
+ question_text=data["question_text"],
94
+ args=_json_loads(data.get("args")) or {},
95
+ tags=_json_loads(data.get("tags")) or [],
96
+ user_id=data.get("user_id"),
97
+ answer_format=data.get("answer_format"),
98
+ result=_json_loads(data.get("result")),
99
+ embedding=_json_loads(data.get("embedding")),
100
+ superseded_by=data.get("superseded_by"),
101
+ created_at=_dt(data.get("created_at")),
102
+ resolved_at=_dt(data.get("resolved_at")),
103
+ expires_at=_dt(data.get("expires_at")),
104
+ cache_until=_dt(data.get("cache_until")),
105
+ valid_until=_dt(data.get("valid_until")),
106
+ )
107
+
108
+
109
+ def _record_to_prior_answer_match(
110
+ record: PendingToolCallRecord,
111
+ *,
112
+ query_embedding: list[float] | None = None,
113
+ ) -> PriorAnswerMatch:
114
+ return PriorAnswerMatch(
115
+ pending_tool_call_id=record.id,
116
+ status=record.status,
117
+ question_text=record.question_text,
118
+ result=record.result,
119
+ valid_until=record.valid_until,
120
+ answer_format=record.answer_format,
121
+ created_at=record.created_at,
122
+ resolved_at=record.resolved_at,
123
+ expires_at=record.expires_at,
124
+ similarity=embedding_similarity(query_embedding, record.embedding),
125
+ )
126
+
127
+
128
+ def _row_to_run_tool_dependency(row: sqlite3.Row) -> RunToolDependencyRecord:
129
+ data = dict(row)
130
+ return RunToolDependencyRecord(
131
+ run_id=data["run_id"],
132
+ pending_tool_call_id=data["pending_tool_call_id"],
133
+ dependency_kind=RunToolDependencyKind(data["dependency_kind"]),
134
+ resolved_at=_dt(data.get("resolved_at")),
135
+ consumed_at=_dt(data.get("consumed_at")),
136
+ created_at=_dt(data.get("created_at")),
137
+ )
138
+
139
+
140
+ class SQLiteAgentRunMixin:
141
+ """SQLite-backed resumable extraction run storage."""
142
+
143
+ _lock: Any
144
+ conn: sqlite3.Connection
145
+ _fetchone: Any
146
+ _fetchall: Any
147
+ _current_timestamp: Any
148
+ org_id: str
149
+
150
+ def _finalize_runs_without_pending_dependencies_unlocked(self, now_s: str) -> None:
151
+ self.conn.execute(
152
+ """
153
+ UPDATE _agent_runs
154
+ SET status = ?,
155
+ finalized_at = COALESCE(finalized_at, ?),
156
+ updated_at = ?
157
+ WHERE status = ?
158
+ AND NOT EXISTS (
159
+ SELECT 1
160
+ FROM _run_tool_dependencies d
161
+ JOIN _pending_tool_calls p
162
+ ON p.id = d.pending_tool_call_id
163
+ WHERE d.run_id = _agent_runs.id
164
+ AND d.resolved_at IS NULL
165
+ AND d.consumed_at IS NULL
166
+ AND p.status = ?
167
+ )
168
+ """,
169
+ (
170
+ AgentRunStatus.FINALIZED.value,
171
+ now_s,
172
+ now_s,
173
+ AgentRunStatus.FINALIZED_PENDING_TOOL.value,
174
+ PendingToolCallStatus.PENDING.value,
175
+ ),
176
+ )
177
+
178
+ def _mark_runs_ready_with_actionable_dependencies_unlocked(
179
+ self, now_s: str, *, pending_tool_call_id: str
180
+ ) -> None:
181
+ self.conn.execute(
182
+ """
183
+ UPDATE _agent_runs
184
+ SET status = ?,
185
+ updated_at = ?
186
+ WHERE status IN (?, ?)
187
+ AND EXISTS (
188
+ SELECT 1
189
+ FROM _run_tool_dependencies changed
190
+ WHERE changed.run_id = _agent_runs.id
191
+ AND changed.pending_tool_call_id = ?
192
+ )
193
+ AND EXISTS (
194
+ SELECT 1
195
+ FROM _run_tool_dependencies d
196
+ JOIN _pending_tool_calls p
197
+ ON p.id = d.pending_tool_call_id
198
+ WHERE d.run_id = _agent_runs.id
199
+ AND d.resolved_at IS NOT NULL
200
+ AND d.consumed_at IS NULL
201
+ AND p.status = ?
202
+ AND COALESCE(json_extract(p.result, '$.not_applicable'), 0) != 1
203
+ )
204
+ """,
205
+ (
206
+ AgentRunStatus.RESUME_READY.value,
207
+ now_s,
208
+ AgentRunStatus.FINALIZED.value,
209
+ AgentRunStatus.FINALIZED_PENDING_TOOL.value,
210
+ pending_tool_call_id,
211
+ PendingToolCallStatus.RESOLVED.value,
212
+ ),
213
+ )
214
+
215
+ def _finalize_runs_without_actionable_dependencies_unlocked(
216
+ self, now_s: str, *, pending_tool_call_id: str
217
+ ) -> None:
218
+ self.conn.execute(
219
+ """
220
+ UPDATE _agent_runs
221
+ SET status = ?,
222
+ finalized_at = COALESCE(finalized_at, ?),
223
+ updated_at = ?
224
+ WHERE status IN (?, ?)
225
+ AND EXISTS (
226
+ SELECT 1
227
+ FROM _run_tool_dependencies changed
228
+ WHERE changed.run_id = _agent_runs.id
229
+ AND changed.pending_tool_call_id = ?
230
+ )
231
+ AND NOT EXISTS (
232
+ SELECT 1
233
+ FROM _run_tool_dependencies d
234
+ JOIN _pending_tool_calls p
235
+ ON p.id = d.pending_tool_call_id
236
+ WHERE d.run_id = _agent_runs.id
237
+ AND d.resolved_at IS NULL
238
+ AND d.consumed_at IS NULL
239
+ AND p.status = ?
240
+ )
241
+ AND NOT EXISTS (
242
+ SELECT 1
243
+ FROM _run_tool_dependencies d
244
+ JOIN _pending_tool_calls p
245
+ ON p.id = d.pending_tool_call_id
246
+ WHERE d.run_id = _agent_runs.id
247
+ AND d.resolved_at IS NOT NULL
248
+ AND d.consumed_at IS NULL
249
+ AND p.status = ?
250
+ AND COALESCE(json_extract(p.result, '$.not_applicable'), 0) != 1
251
+ )
252
+ """,
253
+ (
254
+ AgentRunStatus.FINALIZED.value,
255
+ now_s,
256
+ now_s,
257
+ AgentRunStatus.FINALIZED_PENDING_TOOL.value,
258
+ AgentRunStatus.RESUME_READY.value,
259
+ pending_tool_call_id,
260
+ PendingToolCallStatus.PENDING.value,
261
+ PendingToolCallStatus.RESOLVED.value,
262
+ ),
263
+ )
264
+
265
+ @SQLiteStorageBase.handle_exceptions
266
+ def create_agent_run(self, record: AgentRunRecord) -> AgentRunRecord:
267
+ binding = record.binding
268
+ with self._lock:
269
+ self.conn.execute(
270
+ """
271
+ INSERT INTO _agent_runs (
272
+ id, org_id, extractor_kind, user_id,
273
+ request_id, agent_version, source, source_interaction_ids,
274
+ window_start_interaction_id, window_end_interaction_id,
275
+ extractor_config_hash, status, generation_request_snapshot,
276
+ service_config_snapshot, agent_context_snapshot,
277
+ committed_output, pending_tool_call_ids, max_steps_remaining,
278
+ resume_attempts, finalization_attempts, next_resume_at,
279
+ claimed_by, claimed_at, agent_completed_at, finalized_at,
280
+ expires_at, last_error
281
+ ) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
282
+ """,
283
+ (
284
+ record.id,
285
+ binding.org_id,
286
+ binding.extractor_kind,
287
+ binding.user_id,
288
+ binding.request_id,
289
+ binding.agent_version,
290
+ binding.source,
291
+ _json_dumps(binding.source_interaction_ids),
292
+ binding.window_start_interaction_id,
293
+ binding.window_end_interaction_id,
294
+ binding.extractor_config_hash,
295
+ record.status.value,
296
+ _json_dumps(record.generation_request_snapshot),
297
+ _json_dumps(record.service_config_snapshot),
298
+ record.agent_context_snapshot,
299
+ _json_dumps(record.committed_output),
300
+ _json_dumps(record.pending_tool_call_ids),
301
+ record.max_steps_remaining,
302
+ record.resume_attempts,
303
+ record.finalization_attempts,
304
+ _dt_str(record.next_resume_at),
305
+ record.claimed_by,
306
+ _dt_str(record.claimed_at),
307
+ _dt_str(record.agent_completed_at),
308
+ _dt_str(record.finalized_at),
309
+ _dt_str(record.expires_at),
310
+ record.last_error,
311
+ ),
312
+ )
313
+ self.conn.commit()
314
+ stored = self.get_agent_run(record.id)
315
+ if stored is None: # pragma: no cover
316
+ raise RuntimeError(f"Failed to create agent run {record.id}")
317
+ return stored
318
+
319
+ @SQLiteStorageBase.handle_exceptions
320
+ def get_agent_run(self, run_id: str) -> AgentRunRecord | None:
321
+ row = self._fetchone("SELECT * FROM _agent_runs WHERE id = ?", (run_id,))
322
+ return _row_to_agent_run(row) if row else None
323
+
324
+ @SQLiteStorageBase.handle_exceptions
325
+ def update_agent_run_status(
326
+ self,
327
+ run_id: str,
328
+ status: AgentRunStatus,
329
+ *,
330
+ committed_output: dict[str, Any] | None = None,
331
+ pending_tool_call_ids: list[str] | None = None,
332
+ max_steps_remaining: int | None = None,
333
+ next_resume_at: datetime | None = None,
334
+ last_error: str | None = None,
335
+ increment_finalization_attempts: bool = False,
336
+ expected_statuses: tuple[AgentRunStatus, ...] | None = None,
337
+ ) -> AgentRunRecord | None:
338
+ current_timestamp = self._current_timestamp()
339
+ assignments = ["status = ?", "updated_at = ?"]
340
+ params: list[Any] = [status.value, current_timestamp]
341
+ if committed_output is not None:
342
+ assignments.append("committed_output = ?")
343
+ params.append(_json_dumps(committed_output))
344
+ if pending_tool_call_ids is not None:
345
+ assignments.append("pending_tool_call_ids = ?")
346
+ params.append(_json_dumps(pending_tool_call_ids))
347
+ if max_steps_remaining is not None:
348
+ assignments.append("max_steps_remaining = ?")
349
+ params.append(max(0, max_steps_remaining))
350
+ if next_resume_at is not None:
351
+ assignments.append("next_resume_at = ?")
352
+ params.append(_dt_str(next_resume_at))
353
+ if last_error is not None:
354
+ assignments.append("last_error = ?")
355
+ params.append(last_error)
356
+ if increment_finalization_attempts:
357
+ assignments.append("finalization_attempts = finalization_attempts + 1")
358
+ if status == AgentRunStatus.AGENT_COMPLETED:
359
+ assignments.append("agent_completed_at = ?")
360
+ params.append(current_timestamp)
361
+ if status in (AgentRunStatus.FINALIZED, AgentRunStatus.FINALIZED_PENDING_TOOL):
362
+ assignments.append("finalized_at = ?")
363
+ params.append(current_timestamp)
364
+ params.append(run_id)
365
+ status_filter = ""
366
+ if expected_statuses:
367
+ placeholders = ",".join("?" for _ in expected_statuses)
368
+ status_filter = f" AND status IN ({placeholders})"
369
+ params.extend(expected.value for expected in expected_statuses)
370
+ with self._lock:
371
+ self.conn.execute(
372
+ f"UPDATE _agent_runs SET {', '.join(assignments)} WHERE id = ?{status_filter}",
373
+ params,
374
+ )
375
+ self.conn.commit()
376
+ return self.get_agent_run(run_id)
377
+
378
+ @SQLiteStorageBase.handle_exceptions
379
+ def fail_running_agent_runs_for_request(
380
+ self,
381
+ *,
382
+ org_id: str,
383
+ extractor_kind: str,
384
+ user_id: str | None,
385
+ request_id: str,
386
+ last_error: str,
387
+ ) -> int:
388
+ current_timestamp = self._current_timestamp()
389
+ with self._lock:
390
+ cursor = self.conn.execute(
391
+ """
392
+ UPDATE _agent_runs
393
+ SET status = ?,
394
+ updated_at = ?,
395
+ last_error = ?
396
+ WHERE org_id = ?
397
+ AND extractor_kind = ?
398
+ AND user_id IS ?
399
+ AND request_id = ?
400
+ AND status IN (?, ?)
401
+ """,
402
+ (
403
+ AgentRunStatus.FAILED.value,
404
+ current_timestamp,
405
+ last_error,
406
+ org_id,
407
+ extractor_kind,
408
+ user_id,
409
+ request_id,
410
+ AgentRunStatus.RUNNING.value,
411
+ AgentRunStatus.RESUMING.value,
412
+ ),
413
+ )
414
+ self.conn.commit()
415
+ return cursor.rowcount
416
+
417
+ @SQLiteStorageBase.handle_exceptions
418
+ def create_pending_tool_call(
419
+ self, record: PendingToolCallRecord
420
+ ) -> PendingToolCallRecord:
421
+ with self._lock:
422
+ self.conn.execute(
423
+ """
424
+ INSERT INTO _pending_tool_calls (
425
+ id, org_id, user_id, scope, scope_hash, tool_name, dedup_key,
426
+ status, question_text, answer_format, args, tags, result,
427
+ embedding, superseded_by, resolved_at, expires_at, cache_until,
428
+ valid_until
429
+ ) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
430
+ """,
431
+ (
432
+ record.id,
433
+ record.org_id,
434
+ record.user_id,
435
+ _json_dumps(record.scope),
436
+ record.scope_hash,
437
+ record.tool_name,
438
+ record.dedup_key,
439
+ record.status.value,
440
+ record.question_text,
441
+ record.answer_format,
442
+ _json_dumps(record.args),
443
+ _json_dumps(record.tags),
444
+ _json_dumps(record.result),
445
+ _json_dumps(record.embedding),
446
+ record.superseded_by,
447
+ _dt_str(record.resolved_at),
448
+ _dt_str(record.expires_at),
449
+ _dt_str(record.cache_until),
450
+ _dt_str(record.valid_until),
451
+ ),
452
+ )
453
+ self.conn.commit()
454
+ stored = self.get_pending_tool_call(record.id)
455
+ if stored is None: # pragma: no cover
456
+ raise RuntimeError(f"Failed to create pending tool call {record.id}")
457
+ return stored
458
+
459
+ def _insert_pending_tool_call_unlocked(self, record: PendingToolCallRecord) -> None:
460
+ self.conn.execute(
461
+ """
462
+ INSERT INTO _pending_tool_calls (
463
+ id, org_id, user_id, scope, scope_hash, tool_name, dedup_key,
464
+ status, question_text, answer_format, args, tags, result,
465
+ embedding, superseded_by, resolved_at, expires_at, cache_until,
466
+ valid_until
467
+ ) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
468
+ """,
469
+ (
470
+ record.id,
471
+ record.org_id,
472
+ record.user_id,
473
+ _json_dumps(record.scope),
474
+ record.scope_hash,
475
+ record.tool_name,
476
+ record.dedup_key,
477
+ record.status.value,
478
+ record.question_text,
479
+ record.answer_format,
480
+ _json_dumps(record.args),
481
+ _json_dumps(record.tags),
482
+ _json_dumps(record.result),
483
+ _json_dumps(record.embedding),
484
+ record.superseded_by,
485
+ _dt_str(record.resolved_at),
486
+ _dt_str(record.expires_at),
487
+ _dt_str(record.cache_until),
488
+ _dt_str(record.valid_until),
489
+ ),
490
+ )
491
+
492
+ @SQLiteStorageBase.handle_exceptions
493
+ def create_or_attach_pending_tool_call(
494
+ self,
495
+ *,
496
+ record: PendingToolCallRecord,
497
+ dependency: RunToolDependencyRecord,
498
+ now: datetime | None = None,
499
+ ) -> PendingToolCallUpsertResult:
500
+ current = now or datetime.now(UTC)
501
+ created = False
502
+ pending_tool_call_id = record.id
503
+ with self._lock:
504
+ self.conn.execute("BEGIN IMMEDIATE")
505
+ try:
506
+ row = self.conn.execute(
507
+ """
508
+ SELECT * FROM _pending_tool_calls
509
+ WHERE org_id = ?
510
+ AND scope_hash = ?
511
+ AND tool_name = ?
512
+ AND dedup_key = ?
513
+ AND status = ?
514
+ AND cache_until > ?
515
+ ORDER BY created_at ASC
516
+ LIMIT 1
517
+ """,
518
+ (
519
+ record.org_id,
520
+ record.scope_hash,
521
+ record.tool_name,
522
+ record.dedup_key,
523
+ PendingToolCallStatus.PENDING.value,
524
+ _dt_str(current),
525
+ ),
526
+ ).fetchone()
527
+ pending_tool_call_id = row["id"] if row is not None else record.id
528
+ if row is None:
529
+ self._insert_pending_tool_call_unlocked(record)
530
+ created = True
531
+ self.conn.execute(
532
+ """
533
+ INSERT OR IGNORE INTO _run_tool_dependencies (
534
+ run_id, pending_tool_call_id, dependency_kind,
535
+ resolved_at, consumed_at
536
+ ) VALUES (?,?,?,?,?)
537
+ """,
538
+ (
539
+ dependency.run_id,
540
+ pending_tool_call_id,
541
+ dependency.dependency_kind.value,
542
+ _dt_str(dependency.resolved_at),
543
+ _dt_str(dependency.consumed_at),
544
+ ),
545
+ )
546
+ except Exception:
547
+ self.conn.rollback()
548
+ raise
549
+ else:
550
+ self.conn.commit()
551
+
552
+ stored = self.get_pending_tool_call(pending_tool_call_id)
553
+ if stored is None: # pragma: no cover
554
+ raise RuntimeError("Failed to create or attach pending tool call")
555
+ return PendingToolCallUpsertResult(pending_tool_call=stored, created=created)
556
+
557
+ @SQLiteStorageBase.handle_exceptions
558
+ def get_pending_tool_call(self, call_id: str) -> PendingToolCallRecord | None:
559
+ row = self._fetchone(
560
+ "SELECT * FROM _pending_tool_calls WHERE id = ?", (call_id,)
561
+ )
562
+ return _row_to_pending_tool_call(row) if row else None
563
+
564
+ @SQLiteStorageBase.handle_exceptions
565
+ def list_pending_tool_calls(
566
+ self,
567
+ *,
568
+ status: PendingToolCallStatus | None = None,
569
+ limit: int = 100,
570
+ ) -> list[PendingToolCallRecord]:
571
+ bounded_limit = max(1, min(limit, 500))
572
+ params: list[Any] = [self.org_id]
573
+ status_clause = ""
574
+ if status is not None:
575
+ status_clause = "AND status = ?"
576
+ params.append(status.value)
577
+ params.append(bounded_limit)
578
+ rows = self._fetchall(
579
+ f"""
580
+ SELECT * FROM _pending_tool_calls
581
+ WHERE org_id = ?
582
+ {status_clause}
583
+ ORDER BY created_at DESC, id ASC
584
+ LIMIT ?
585
+ """,
586
+ tuple(params),
587
+ )
588
+ return [_row_to_pending_tool_call(row) for row in rows]
589
+
590
+ @SQLiteStorageBase.handle_exceptions
591
+ def cancel_pending_tool_call(
592
+ self,
593
+ call_id: str,
594
+ *,
595
+ cancelled_at: datetime | None = None,
596
+ ) -> PendingToolCallRecord | None:
597
+ now = cancelled_at or datetime.now(UTC)
598
+ now_s = _dt_str(now)
599
+ with self._lock:
600
+ self.conn.execute(
601
+ """
602
+ UPDATE _pending_tool_calls
603
+ SET status = ?
604
+ WHERE id = ?
605
+ AND status = ?
606
+ """,
607
+ (
608
+ PendingToolCallStatus.CANCELLED.value,
609
+ call_id,
610
+ PendingToolCallStatus.PENDING.value,
611
+ ),
612
+ )
613
+ self.conn.execute(
614
+ """
615
+ UPDATE _run_tool_dependencies
616
+ SET resolved_at = ?
617
+ WHERE pending_tool_call_id = ? AND resolved_at IS NULL
618
+ """,
619
+ (now_s, call_id),
620
+ )
621
+ self._finalize_runs_without_pending_dependencies_unlocked(now_s or "")
622
+ self.conn.commit()
623
+ return self.get_pending_tool_call(call_id)
624
+
625
+ @SQLiteStorageBase.handle_exceptions
626
+ def expire_pending_tool_calls(
627
+ self,
628
+ *,
629
+ now: datetime | None = None,
630
+ limit: int = 100,
631
+ ) -> int:
632
+ current = now or datetime.now(UTC)
633
+ now_s = _dt_str(current)
634
+ bounded_limit = max(1, min(limit, 500))
635
+ with self._lock:
636
+ self.conn.execute("BEGIN IMMEDIATE")
637
+ try:
638
+ rows = self.conn.execute(
639
+ """
640
+ SELECT id
641
+ FROM _pending_tool_calls
642
+ WHERE status = ?
643
+ AND expires_at IS NOT NULL
644
+ AND expires_at <= ?
645
+ ORDER BY expires_at ASC, created_at ASC, id ASC
646
+ LIMIT ?
647
+ """,
648
+ (
649
+ PendingToolCallStatus.PENDING.value,
650
+ now_s,
651
+ bounded_limit,
652
+ ),
653
+ ).fetchall()
654
+ call_ids = [row["id"] for row in rows]
655
+ if not call_ids:
656
+ self.conn.commit()
657
+ return 0
658
+
659
+ placeholders = ",".join("?" for _ in call_ids)
660
+ self.conn.execute(
661
+ f"""
662
+ UPDATE _pending_tool_calls
663
+ SET status = ?
664
+ WHERE id IN ({placeholders})
665
+ AND status = ?
666
+ """,
667
+ (
668
+ PendingToolCallStatus.EXPIRED.value,
669
+ *call_ids,
670
+ PendingToolCallStatus.PENDING.value,
671
+ ),
672
+ )
673
+ self.conn.execute(
674
+ f"""
675
+ UPDATE _run_tool_dependencies
676
+ SET resolved_at = ?
677
+ WHERE pending_tool_call_id IN ({placeholders})
678
+ AND resolved_at IS NULL
679
+ """,
680
+ (now_s, *call_ids),
681
+ )
682
+ self._finalize_runs_without_pending_dependencies_unlocked(now_s or "")
683
+ except Exception:
684
+ self.conn.rollback()
685
+ raise
686
+ else:
687
+ self.conn.commit()
688
+ return len(call_ids)
689
+
690
+ @SQLiteStorageBase.handle_exceptions
691
+ def find_active_pending_tool_call(
692
+ self,
693
+ *,
694
+ org_id: str,
695
+ scope_hash: str,
696
+ tool_name: str,
697
+ dedup_key: str,
698
+ now: datetime | None = None,
699
+ ) -> PendingToolCallRecord | None:
700
+ now_s = _dt_str(now or datetime.now(UTC))
701
+ row = self._fetchone(
702
+ """
703
+ SELECT * FROM _pending_tool_calls
704
+ WHERE org_id = ?
705
+ AND scope_hash = ?
706
+ AND tool_name = ?
707
+ AND dedup_key = ?
708
+ AND status = ?
709
+ AND cache_until > ?
710
+ ORDER BY created_at ASC
711
+ LIMIT 1
712
+ """,
713
+ (
714
+ org_id,
715
+ scope_hash,
716
+ tool_name,
717
+ dedup_key,
718
+ PendingToolCallStatus.PENDING.value,
719
+ now_s,
720
+ ),
721
+ )
722
+ return _row_to_pending_tool_call(row) if row else None
723
+
724
+ @SQLiteStorageBase.handle_exceptions
725
+ def search_prior_tool_calls(
726
+ self,
727
+ *,
728
+ org_id: str,
729
+ scope_hash: str,
730
+ tool_name: str,
731
+ query_embedding: list[float] | None = None,
732
+ now: datetime | None = None,
733
+ limit: int = 8,
734
+ ) -> list[PriorAnswerMatch]:
735
+ current = now or datetime.now(UTC)
736
+ bounded_limit = max(1, min(limit, 50))
737
+ rows = self._fetchall(
738
+ """
739
+ SELECT * FROM _pending_tool_calls
740
+ WHERE org_id = ?
741
+ AND scope_hash = ?
742
+ AND tool_name = ?
743
+ AND (
744
+ (
745
+ status = ?
746
+ AND (expires_at IS NULL OR expires_at > ?)
747
+ )
748
+ OR (
749
+ status = ?
750
+ AND (valid_until IS NULL OR valid_until > ?)
751
+ )
752
+ )
753
+ ORDER BY
754
+ CASE status WHEN ? THEN 0 ELSE 1 END,
755
+ COALESCE(resolved_at, created_at) DESC,
756
+ id ASC
757
+ """,
758
+ (
759
+ org_id,
760
+ scope_hash,
761
+ tool_name,
762
+ PendingToolCallStatus.PENDING.value,
763
+ _dt_str(current),
764
+ PendingToolCallStatus.RESOLVED.value,
765
+ _dt_str(current),
766
+ PendingToolCallStatus.RESOLVED.value,
767
+ ),
768
+ )
769
+ seen_resolved_dedup_keys: set[str] = set()
770
+ records: list[PendingToolCallRecord] = []
771
+ for row in rows:
772
+ record = _row_to_pending_tool_call(row)
773
+ if record.status == PendingToolCallStatus.RESOLVED:
774
+ if record.dedup_key in seen_resolved_dedup_keys:
775
+ continue
776
+ seen_resolved_dedup_keys.add(record.dedup_key)
777
+ records.append(record)
778
+ matches = [
779
+ _record_to_prior_answer_match(record, query_embedding=query_embedding)
780
+ for record in records
781
+ ]
782
+ if query_embedding:
783
+ matches.sort(
784
+ key=lambda match: (
785
+ match.similarity is not None,
786
+ match.similarity or -1.0,
787
+ match.resolved_at
788
+ or match.created_at
789
+ or datetime.min.replace(tzinfo=UTC),
790
+ ),
791
+ reverse=True,
792
+ )
793
+ return matches[:bounded_limit]
794
+
795
+ @SQLiteStorageBase.handle_exceptions
796
+ def attach_run_tool_dependency(
797
+ self, record: RunToolDependencyRecord
798
+ ) -> RunToolDependencyRecord:
799
+ with self._lock:
800
+ self.conn.execute(
801
+ """
802
+ INSERT OR IGNORE INTO _run_tool_dependencies (
803
+ run_id, pending_tool_call_id, dependency_kind, resolved_at,
804
+ consumed_at
805
+ ) VALUES (?,?,?,?,?)
806
+ """,
807
+ (
808
+ record.run_id,
809
+ record.pending_tool_call_id,
810
+ record.dependency_kind.value,
811
+ _dt_str(record.resolved_at),
812
+ _dt_str(record.consumed_at),
813
+ ),
814
+ )
815
+ self.conn.commit()
816
+ row = self._fetchone(
817
+ """
818
+ SELECT * FROM _run_tool_dependencies
819
+ WHERE run_id = ? AND pending_tool_call_id = ?
820
+ """,
821
+ (record.run_id, record.pending_tool_call_id),
822
+ )
823
+ if row is None: # pragma: no cover
824
+ raise RuntimeError("Failed to attach run tool dependency")
825
+ return _row_to_run_tool_dependency(row)
826
+
827
+ @SQLiteStorageBase.handle_exceptions
828
+ def count_unresolved_followup_dependencies(
829
+ self,
830
+ *,
831
+ org_id: str,
832
+ extractor_kind: str,
833
+ tool_name: str,
834
+ ) -> int:
835
+ row = self._fetchone(
836
+ """
837
+ SELECT COUNT(*) AS count
838
+ FROM _run_tool_dependencies d
839
+ JOIN _agent_runs r ON r.id = d.run_id
840
+ JOIN _pending_tool_calls p ON p.id = d.pending_tool_call_id
841
+ WHERE r.org_id = ?
842
+ AND r.extractor_kind = ?
843
+ AND p.tool_name = ?
844
+ AND p.status = ?
845
+ AND d.resolved_at IS NULL
846
+ AND d.consumed_at IS NULL
847
+ """,
848
+ (
849
+ org_id,
850
+ extractor_kind,
851
+ tool_name,
852
+ PendingToolCallStatus.PENDING.value,
853
+ ),
854
+ )
855
+ return int(row["count"]) if row is not None else 0
856
+
857
+ @SQLiteStorageBase.handle_exceptions
858
+ def list_run_tool_dependencies(self, run_id: str) -> list[RunToolDependencyRecord]:
859
+ rows = self._fetchall(
860
+ "SELECT * FROM _run_tool_dependencies WHERE run_id = ?",
861
+ (run_id,),
862
+ )
863
+ return [_row_to_run_tool_dependency(row) for row in rows]
864
+
865
+ @SQLiteStorageBase.handle_exceptions
866
+ def resolve_pending_tool_call(
867
+ self,
868
+ call_id: str,
869
+ *,
870
+ result: dict[str, Any],
871
+ resolved_at: datetime | None = None,
872
+ valid_for_seconds: int,
873
+ ) -> PendingToolCallRecord | None:
874
+ resolved = resolved_at or datetime.now(UTC)
875
+ valid_until = resolved + timedelta(seconds=valid_for_seconds)
876
+ with self._lock:
877
+ cur = self.conn.execute(
878
+ """
879
+ UPDATE _pending_tool_calls
880
+ SET status = ?, result = ?, resolved_at = ?, valid_until = ?
881
+ WHERE id = ?
882
+ AND status = ?
883
+ """,
884
+ (
885
+ PendingToolCallStatus.RESOLVED.value,
886
+ _json_dumps(result),
887
+ _dt_str(resolved),
888
+ _dt_str(valid_until),
889
+ call_id,
890
+ PendingToolCallStatus.PENDING.value,
891
+ ),
892
+ )
893
+ if cur.rowcount == 0:
894
+ self.conn.commit()
895
+ return self.get_pending_tool_call(call_id)
896
+ self.conn.execute(
897
+ """
898
+ UPDATE _pending_tool_calls
899
+ SET status = ?,
900
+ superseded_by = ?
901
+ WHERE id != ?
902
+ AND status = ?
903
+ AND (valid_until IS NULL OR valid_until > ?)
904
+ AND (org_id, scope_hash, tool_name, dedup_key) = (
905
+ SELECT org_id, scope_hash, tool_name, dedup_key
906
+ FROM _pending_tool_calls
907
+ WHERE id = ?
908
+ )
909
+ """,
910
+ (
911
+ PendingToolCallStatus.SUPERSEDED.value,
912
+ call_id,
913
+ call_id,
914
+ PendingToolCallStatus.RESOLVED.value,
915
+ _dt_str(resolved),
916
+ call_id,
917
+ ),
918
+ )
919
+ self.conn.execute(
920
+ """
921
+ UPDATE _run_tool_dependencies
922
+ SET resolved_at = ?
923
+ WHERE pending_tool_call_id = ? AND resolved_at IS NULL
924
+ """,
925
+ (_dt_str(resolved), call_id),
926
+ )
927
+ self.conn.execute(
928
+ """
929
+ UPDATE _agent_runs
930
+ SET status = ?, updated_at = ?
931
+ WHERE status IN (?, ?)
932
+ AND EXISTS (
933
+ SELECT 1
934
+ FROM _run_tool_dependencies d
935
+ WHERE d.run_id = _agent_runs.id
936
+ AND d.pending_tool_call_id = ?
937
+ AND d.resolved_at IS NOT NULL
938
+ AND d.consumed_at IS NULL
939
+ )
940
+ """,
941
+ (
942
+ AgentRunStatus.RESUME_READY.value,
943
+ self._current_timestamp(),
944
+ AgentRunStatus.FINALIZED.value,
945
+ AgentRunStatus.FINALIZED_PENDING_TOOL.value,
946
+ call_id,
947
+ ),
948
+ )
949
+ self.conn.commit()
950
+ return self.get_pending_tool_call(call_id)
951
+
952
+ @SQLiteStorageBase.handle_exceptions
953
+ def update_resolved_pending_tool_call_result(
954
+ self,
955
+ call_id: str,
956
+ *,
957
+ result: dict[str, Any],
958
+ resolved_at: datetime | None = None,
959
+ valid_for_seconds: int,
960
+ ) -> PendingToolCallRecord | None:
961
+ resolved = resolved_at or datetime.now(UTC)
962
+ valid_until = resolved + timedelta(seconds=valid_for_seconds)
963
+ now_s = _dt_str(resolved) or self._current_timestamp()
964
+ with self._lock:
965
+ self.conn.execute("BEGIN IMMEDIATE")
966
+ try:
967
+ cur = self.conn.execute(
968
+ """
969
+ UPDATE _pending_tool_calls
970
+ SET result = ?, resolved_at = ?, valid_until = ?
971
+ WHERE id = ?
972
+ AND status = ?
973
+ """,
974
+ (
975
+ _json_dumps(result),
976
+ _dt_str(resolved),
977
+ _dt_str(valid_until),
978
+ call_id,
979
+ PendingToolCallStatus.RESOLVED.value,
980
+ ),
981
+ )
982
+ if cur.rowcount == 0:
983
+ self.conn.commit()
984
+ return self.get_pending_tool_call(call_id)
985
+ self.conn.execute(
986
+ """
987
+ UPDATE _pending_tool_calls
988
+ SET status = ?,
989
+ superseded_by = ?
990
+ WHERE id != ?
991
+ AND status = ?
992
+ AND (valid_until IS NULL OR valid_until > ?)
993
+ AND (org_id, scope_hash, tool_name, dedup_key) = (
994
+ SELECT org_id, scope_hash, tool_name, dedup_key
995
+ FROM _pending_tool_calls
996
+ WHERE id = ?
997
+ )
998
+ """,
999
+ (
1000
+ PendingToolCallStatus.SUPERSEDED.value,
1001
+ call_id,
1002
+ call_id,
1003
+ PendingToolCallStatus.RESOLVED.value,
1004
+ _dt_str(resolved),
1005
+ call_id,
1006
+ ),
1007
+ )
1008
+ self.conn.execute(
1009
+ """
1010
+ UPDATE _run_tool_dependencies
1011
+ SET resolved_at = ?,
1012
+ consumed_at = NULL
1013
+ WHERE pending_tool_call_id = ?
1014
+ """,
1015
+ (_dt_str(resolved), call_id),
1016
+ )
1017
+ self._mark_runs_ready_with_actionable_dependencies_unlocked(
1018
+ now_s, pending_tool_call_id=call_id
1019
+ )
1020
+ except Exception:
1021
+ self.conn.rollback()
1022
+ raise
1023
+ else:
1024
+ self.conn.commit()
1025
+ return self.get_pending_tool_call(call_id)
1026
+
1027
+ @SQLiteStorageBase.handle_exceptions
1028
+ def mark_pending_tool_call_not_applicable(
1029
+ self,
1030
+ call_id: str,
1031
+ *,
1032
+ resolved_at: datetime | None = None,
1033
+ valid_for_seconds: int,
1034
+ ) -> PendingToolCallRecord | None:
1035
+ resolved = resolved_at or datetime.now(UTC)
1036
+ valid_until = resolved + timedelta(seconds=valid_for_seconds)
1037
+ now_s = _dt_str(resolved) or self._current_timestamp()
1038
+ with self._lock:
1039
+ self.conn.execute("BEGIN IMMEDIATE")
1040
+ try:
1041
+ cur = self.conn.execute(
1042
+ """
1043
+ UPDATE _pending_tool_calls
1044
+ SET status = ?, result = ?, resolved_at = ?, valid_until = ?
1045
+ WHERE id = ?
1046
+ AND status IN (?, ?)
1047
+ """,
1048
+ (
1049
+ PendingToolCallStatus.RESOLVED.value,
1050
+ _json_dumps(not_applicable_tool_result()),
1051
+ _dt_str(resolved),
1052
+ _dt_str(valid_until),
1053
+ call_id,
1054
+ PendingToolCallStatus.PENDING.value,
1055
+ PendingToolCallStatus.RESOLVED.value,
1056
+ ),
1057
+ )
1058
+ if cur.rowcount == 0:
1059
+ self.conn.commit()
1060
+ return self.get_pending_tool_call(call_id)
1061
+ self.conn.execute(
1062
+ """
1063
+ UPDATE _run_tool_dependencies
1064
+ SET resolved_at = COALESCE(resolved_at, ?),
1065
+ consumed_at = ?
1066
+ WHERE pending_tool_call_id = ?
1067
+ AND consumed_at IS NULL
1068
+ """,
1069
+ (_dt_str(resolved), _dt_str(resolved), call_id),
1070
+ )
1071
+ self._mark_runs_ready_with_actionable_dependencies_unlocked(
1072
+ now_s, pending_tool_call_id=call_id
1073
+ )
1074
+ self._finalize_runs_without_actionable_dependencies_unlocked(
1075
+ now_s, pending_tool_call_id=call_id
1076
+ )
1077
+ except Exception:
1078
+ self.conn.rollback()
1079
+ raise
1080
+ else:
1081
+ self.conn.commit()
1082
+ return self.get_pending_tool_call(call_id)
1083
+
1084
+ @SQLiteStorageBase.handle_exceptions
1085
+ def claim_ready_agent_run(
1086
+ self,
1087
+ *,
1088
+ org_id: str,
1089
+ worker_id: str,
1090
+ now: datetime | None = None,
1091
+ claim_ttl_seconds: int = 600,
1092
+ ) -> AgentRunRecord | None:
1093
+ current = now or datetime.now(UTC)
1094
+ stale_before = current - timedelta(seconds=claim_ttl_seconds)
1095
+ with self._lock:
1096
+ row = self.conn.execute(
1097
+ """
1098
+ SELECT r.*
1099
+ FROM _agent_runs r
1100
+ WHERE r.org_id = ?
1101
+ AND (
1102
+ r.status = ?
1103
+ OR (r.status = ? AND r.claimed_at < ?)
1104
+ )
1105
+ AND (r.next_resume_at IS NULL OR r.next_resume_at <= ?)
1106
+ AND EXISTS (
1107
+ SELECT 1
1108
+ FROM _run_tool_dependencies d
1109
+ JOIN _pending_tool_calls p
1110
+ ON p.id = d.pending_tool_call_id
1111
+ WHERE d.run_id = r.id
1112
+ AND d.resolved_at IS NOT NULL
1113
+ AND d.consumed_at IS NULL
1114
+ AND p.status = ?
1115
+ AND COALESCE(json_extract(p.result, '$.not_applicable'), 0) != 1
1116
+ )
1117
+ ORDER BY
1118
+ r.org_id ASC,
1119
+ r.extractor_kind ASC,
1120
+ COALESCE(r.user_id, '') ASC,
1121
+ COALESCE(r.window_start_interaction_id, 0) ASC,
1122
+ r.updated_at ASC
1123
+ LIMIT 1
1124
+ """,
1125
+ (
1126
+ org_id,
1127
+ AgentRunStatus.RESUME_READY.value,
1128
+ AgentRunStatus.RESUMING.value,
1129
+ _dt_str(stale_before),
1130
+ _dt_str(current),
1131
+ PendingToolCallStatus.RESOLVED.value,
1132
+ ),
1133
+ ).fetchone()
1134
+ if row is None:
1135
+ return None
1136
+ self.conn.execute(
1137
+ """
1138
+ UPDATE _agent_runs
1139
+ SET status = ?,
1140
+ claimed_by = ?,
1141
+ claimed_at = ?,
1142
+ resume_attempts = resume_attempts + 1,
1143
+ updated_at = ?
1144
+ WHERE id = ?
1145
+ """,
1146
+ (
1147
+ AgentRunStatus.RESUMING.value,
1148
+ worker_id,
1149
+ _dt_str(current),
1150
+ self._current_timestamp(),
1151
+ row["id"],
1152
+ ),
1153
+ )
1154
+ self.conn.commit()
1155
+ return self.get_agent_run(row["id"])
1156
+
1157
+ @SQLiteStorageBase.handle_exceptions
1158
+ def claim_finalization_failed_agent_run(
1159
+ self,
1160
+ *,
1161
+ org_id: str,
1162
+ worker_id: str,
1163
+ now: datetime | None = None,
1164
+ claim_ttl_seconds: int = 600,
1165
+ ) -> AgentRunRecord | None:
1166
+ current = now or datetime.now(UTC)
1167
+ stale_before = current - timedelta(seconds=claim_ttl_seconds)
1168
+ with self._lock:
1169
+ # Claim runs that still need their committed output finalized:
1170
+ # - FINALIZATION_FAILED: an explicit retry is due.
1171
+ # - stale FINALIZING: a worker crashed mid-finalize.
1172
+ # - stale AGENT_COMPLETED: publish-time finalization never ran or
1173
+ # crashed before flipping the status (the run row carries
1174
+ # committed_output but was orphaned); the staleness guard ensures
1175
+ # we never race an in-flight publish-time finalize.
1176
+ row = self.conn.execute(
1177
+ """
1178
+ SELECT *
1179
+ FROM _agent_runs
1180
+ WHERE org_id = ?
1181
+ AND (
1182
+ status = ?
1183
+ OR (status = ? AND claimed_at < ?)
1184
+ OR (status = ? AND updated_at < ?)
1185
+ )
1186
+ AND committed_output IS NOT NULL
1187
+ AND (next_resume_at IS NULL OR next_resume_at <= ?)
1188
+ ORDER BY
1189
+ org_id ASC,
1190
+ extractor_kind ASC,
1191
+ COALESCE(user_id, '') ASC,
1192
+ COALESCE(window_start_interaction_id, 0) ASC,
1193
+ updated_at ASC
1194
+ LIMIT 1
1195
+ """,
1196
+ (
1197
+ org_id,
1198
+ AgentRunStatus.FINALIZATION_FAILED.value,
1199
+ AgentRunStatus.FINALIZING.value,
1200
+ _dt_str(stale_before),
1201
+ AgentRunStatus.AGENT_COMPLETED.value,
1202
+ _dt_str(stale_before),
1203
+ _dt_str(current),
1204
+ ),
1205
+ ).fetchone()
1206
+ if row is None:
1207
+ return None
1208
+ self.conn.execute(
1209
+ """
1210
+ UPDATE _agent_runs
1211
+ SET status = ?,
1212
+ claimed_by = ?,
1213
+ claimed_at = ?,
1214
+ updated_at = ?
1215
+ WHERE id = ?
1216
+ """,
1217
+ (
1218
+ AgentRunStatus.FINALIZING.value,
1219
+ worker_id,
1220
+ _dt_str(current),
1221
+ self._current_timestamp(),
1222
+ row["id"],
1223
+ ),
1224
+ )
1225
+ self.conn.commit()
1226
+ return self.get_agent_run(row["id"])
1227
+
1228
+ @SQLiteStorageBase.handle_exceptions
1229
+ def consume_run_tool_dependencies(self, run_id: str) -> int:
1230
+ consumed_at = self._current_timestamp()
1231
+ with self._lock:
1232
+ cur = self.conn.execute(
1233
+ """
1234
+ UPDATE _run_tool_dependencies
1235
+ SET consumed_at = ?
1236
+ WHERE run_id = ?
1237
+ AND resolved_at IS NOT NULL
1238
+ AND consumed_at IS NULL
1239
+ """,
1240
+ (consumed_at, run_id),
1241
+ )
1242
+ self.conn.commit()
1243
+ return int(cur.rowcount)
1244
+
1245
+ @SQLiteStorageBase.handle_exceptions
1246
+ def list_resumable_work_org_ids(
1247
+ self,
1248
+ *,
1249
+ now: datetime | None = None,
1250
+ limit: int = 1000,
1251
+ ) -> list[str]:
1252
+ current = now or datetime.now(UTC)
1253
+ now_s = _dt_str(current)
1254
+ bounded_limit = max(1, min(limit, 10_000))
1255
+ # Cross-org maintenance query. Surfaces any org that has a run ready to
1256
+ # resume / awaiting finalization, or a pending tool call due to expire.
1257
+ rows = self._fetchall(
1258
+ """
1259
+ SELECT DISTINCT org_id FROM (
1260
+ SELECT r.org_id
1261
+ FROM _agent_runs r
1262
+ WHERE r.status IN (?, ?)
1263
+ AND EXISTS (
1264
+ SELECT 1
1265
+ FROM _run_tool_dependencies d
1266
+ JOIN _pending_tool_calls p
1267
+ ON p.id = d.pending_tool_call_id
1268
+ WHERE d.run_id = r.id
1269
+ AND d.resolved_at IS NOT NULL
1270
+ AND d.consumed_at IS NULL
1271
+ AND p.status = ?
1272
+ AND COALESCE(json_extract(p.result, '$.not_applicable'), 0) != 1
1273
+ )
1274
+ UNION
1275
+ SELECT org_id FROM _agent_runs
1276
+ WHERE status IN (?, ?, ?)
1277
+ UNION
1278
+ SELECT org_id FROM _pending_tool_calls
1279
+ WHERE status = ?
1280
+ AND expires_at IS NOT NULL
1281
+ AND expires_at <= ?
1282
+ )
1283
+ ORDER BY org_id ASC
1284
+ LIMIT ?
1285
+ """,
1286
+ (
1287
+ AgentRunStatus.RESUME_READY.value,
1288
+ AgentRunStatus.RESUMING.value,
1289
+ PendingToolCallStatus.RESOLVED.value,
1290
+ AgentRunStatus.FINALIZATION_FAILED.value,
1291
+ AgentRunStatus.FINALIZING.value,
1292
+ AgentRunStatus.AGENT_COMPLETED.value,
1293
+ PendingToolCallStatus.PENDING.value,
1294
+ now_s,
1295
+ bounded_limit,
1296
+ ),
1297
+ )
1298
+ return [row["org_id"] for row in rows]