prompt_objects 0.5.0 → 0.7.0

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 (292) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +142 -0
  3. data/README.md +211 -19
  4. data/exe/poop +5 -0
  5. data/exe/prompt_objects +1 -1108
  6. data/exe/prompt_objects_mcp +5 -3
  7. data/lib/prompt_objects/capability.rb +33 -0
  8. data/lib/prompt_objects/cli/diagnostics_command.rb +69 -0
  9. data/lib/prompt_objects/cli/env_command.rb +435 -0
  10. data/lib/prompt_objects/cli/events_command.rb +145 -0
  11. data/lib/prompt_objects/cli/explore_command.rb +155 -0
  12. data/lib/prompt_objects/cli/helpers.rb +91 -0
  13. data/lib/prompt_objects/cli/message_command.rb +216 -0
  14. data/lib/prompt_objects/cli/repl_command.rb +366 -0
  15. data/lib/prompt_objects/cli/serve_command.rb +162 -0
  16. data/lib/prompt_objects/cli.rb +64 -422
  17. data/lib/prompt_objects/connectors/base.rb +16 -29
  18. data/lib/prompt_objects/connectors/mcp.rb +82 -98
  19. data/lib/prompt_objects/domain/records.rb +45 -0
  20. data/lib/prompt_objects/environment/exporter.rb +5 -4
  21. data/lib/prompt_objects/environment/git.rb +21 -1
  22. data/lib/prompt_objects/environment/importer.rb +5 -4
  23. data/lib/prompt_objects/environment/manager.rb +44 -6
  24. data/lib/prompt_objects/environment/manifest.rb +26 -4
  25. data/lib/prompt_objects/environment.rb +1350 -18
  26. data/lib/prompt_objects/execution/blocking_executor.rb +88 -0
  27. data/lib/prompt_objects/execution/context.rb +137 -0
  28. data/lib/prompt_objects/execution/contracts.rb +59 -0
  29. data/lib/prompt_objects/execution/permit_pool.rb +49 -0
  30. data/lib/prompt_objects/execution/policy.rb +47 -0
  31. data/lib/prompt_objects/execution/resource_lock_manager.rb +63 -0
  32. data/lib/prompt_objects/execution/runner.rb +498 -0
  33. data/lib/prompt_objects/execution/scheduler.rb +325 -0
  34. data/lib/prompt_objects/llm/factory.rb +4 -4
  35. data/lib/prompt_objects/llm/gemini_adapter.rb +25 -8
  36. data/lib/prompt_objects/llm/openai_adapter.rb +9 -1
  37. data/lib/prompt_objects/llm/pricing.rb +9 -2
  38. data/lib/prompt_objects/llm/response.rb +9 -4
  39. data/lib/prompt_objects/loader.rb +28 -1
  40. data/lib/prompt_objects/mcp/server.rb +6 -3
  41. data/lib/prompt_objects/mcp/tools/get_conversation.rb +15 -11
  42. data/lib/prompt_objects/mcp/tools/get_pending_requests.rb +2 -2
  43. data/lib/prompt_objects/mcp/tools/respond_to_request.rb +3 -3
  44. data/lib/prompt_objects/mcp/tools/send_message.rb +42 -32
  45. data/lib/prompt_objects/operations/diagnostics.rb +115 -0
  46. data/lib/prompt_objects/operations/retention.rb +75 -0
  47. data/lib/prompt_objects/operations/run_trace_builder.rb +152 -0
  48. data/lib/prompt_objects/primitives/http_get.rb +2 -0
  49. data/lib/prompt_objects/primitives/list_files.rb +2 -0
  50. data/lib/prompt_objects/primitives/read_file.rb +2 -0
  51. data/lib/prompt_objects/primitives/think.rb +36 -0
  52. data/lib/prompt_objects/primitives/write_file.rb +9 -0
  53. data/lib/prompt_objects/prompt_object.rb +190 -432
  54. data/lib/prompt_objects/repositories/artifact_repository.rb +109 -0
  55. data/lib/prompt_objects/repositories/base_repository.rb +37 -0
  56. data/lib/prompt_objects/repositories/definition_repository.rb +36 -0
  57. data/lib/prompt_objects/repositories/env_data_repository.rb +103 -0
  58. data/lib/prompt_objects/repositories/event_repository.rb +105 -0
  59. data/lib/prompt_objects/repositories/message_repository.rb +73 -0
  60. data/lib/prompt_objects/repositories/run_repository.rb +254 -0
  61. data/lib/prompt_objects/repositories/thread_repository.rb +87 -0
  62. data/lib/prompt_objects/repositories/tool_execution_repository.rb +88 -0
  63. data/lib/prompt_objects/repositories/workspace_session_repository.rb +61 -0
  64. data/lib/prompt_objects/server/api/routes.rb +561 -52
  65. data/lib/prompt_objects/server/app.rb +134 -1
  66. data/lib/prompt_objects/server/file_watcher.rb +9 -0
  67. data/lib/prompt_objects/server/public/assets/index-BjKlODDD.js +90 -0
  68. data/lib/prompt_objects/server/public/assets/index-w3fpIb5c.css +1 -0
  69. data/lib/prompt_objects/server/public/index.html +2 -2
  70. data/lib/prompt_objects/server/websocket_handler.rb +202 -207
  71. data/lib/prompt_objects/server.rb +170 -1
  72. data/lib/prompt_objects/service.rb +129 -0
  73. data/lib/prompt_objects/session/store.rb +774 -15
  74. data/lib/prompt_objects/universal/add_capability.rb +1 -1
  75. data/lib/prompt_objects/universal/create_capability.rb +4 -2
  76. data/lib/prompt_objects/universal/delete_env_data.rb +78 -0
  77. data/lib/prompt_objects/universal/delete_primitive.rb +1 -1
  78. data/lib/prompt_objects/universal/dispatch_po.rb +54 -0
  79. data/lib/prompt_objects/universal/get_env_data.rb +58 -0
  80. data/lib/prompt_objects/universal/get_run_status.rb +30 -0
  81. data/lib/prompt_objects/universal/join_runs.rb +35 -0
  82. data/lib/prompt_objects/universal/list_env_data.rb +57 -0
  83. data/lib/prompt_objects/universal/modify_primitive.rb +1 -1
  84. data/lib/prompt_objects/universal/render_artifact.rb +91 -0
  85. data/lib/prompt_objects/universal/request_primitive.rb +1 -1
  86. data/lib/prompt_objects/universal/set_env_data.rb +101 -0
  87. data/lib/prompt_objects/version.rb +5 -0
  88. data/lib/prompt_objects.rb +36 -6
  89. data/objects/coordinator.md +8 -4
  90. data/templates/arc-agi-1/objects/data_manager.md +2 -2
  91. data/templates/arc-agi-1/objects/observer.md +6 -2
  92. data/templates/arc-agi-1/objects/solver.md +14 -4
  93. data/templates/arc-agi-1/objects/verifier.md +6 -2
  94. data/templates/arc-agi-3/manifest.yml +19 -0
  95. data/templates/arc-agi-3/objects/analyst.md +79 -0
  96. data/templates/arc-agi-3/objects/player.md +76 -0
  97. data/templates/arc-agi-3/objects/strategist.md +97 -0
  98. data/templates/arc-agi-3/primitives/action_log.rb +133 -0
  99. data/templates/arc-agi-3/primitives/arc3_api.rb +223 -0
  100. data/templates/arc-agi-3/primitives/arc3_scorecard.rb +131 -0
  101. data/templates/arc-agi-3/primitives/frame_diff.rb +143 -0
  102. data/templates/arc-agi-3/primitives/frame_info.rb +96 -0
  103. data/templates/arc-agi-3/primitives/render_frame.rb +140 -0
  104. data/templates/basic/objects/basic.md +11 -3
  105. data/templates/chiptune/extras/sonic_pi_boot.rb +220 -0
  106. data/templates/chiptune/manifest.yml +20 -0
  107. data/templates/chiptune/objects/conductor.md +184 -0
  108. data/templates/chiptune/objects/sonic_pi.md +65 -0
  109. data/templates/chiptune/objects/voice_bass.md +44 -0
  110. data/templates/chiptune/objects/voice_lead.md +44 -0
  111. data/templates/chiptune/objects/voice_pad.md +43 -0
  112. data/templates/chiptune/objects/voice_perc.md +45 -0
  113. data/templates/chiptune/primitives/play_note.rb +59 -0
  114. data/templates/chiptune/primitives/play_sequence.rb +55 -0
  115. data/templates/chiptune/primitives/query_sonic_pi.rb +141 -0
  116. data/templates/chiptune/primitives/set_synth.rb +57 -0
  117. data/templates/chiptune/primitives/set_tempo.rb +45 -0
  118. data/templates/chiptune/primitives/stop_voice.rb +46 -0
  119. data/templates/chiptune/primitives/support/osc_bridge.rb +33 -0
  120. data/templates/chiptune/vendor/osc-ruby-1.1.5/LICENSE +19 -0
  121. data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/address_pattern.rb +58 -0
  122. data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/broadcast_client.rb +24 -0
  123. data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/bundle.rb +46 -0
  124. data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/client.rb +13 -0
  125. data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/core_ext/time.rb +6 -0
  126. data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/em_server.rb +63 -0
  127. data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/message.rb +46 -0
  128. data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/network_packet.rb +42 -0
  129. data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_argument.rb +26 -0
  130. data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_packet.rb +140 -0
  131. data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_types.rb +53 -0
  132. data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/server.rb +91 -0
  133. data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby.rb +18 -0
  134. data/templates/developer/objects/code_reviewer.md +1 -1
  135. data/templates/developer/objects/coordinator.md +16 -3
  136. data/templates/developer/objects/debugger.md +3 -3
  137. data/templates/empty/objects/assistant.md +10 -5
  138. data/templates/minimal/objects/assistant.md +10 -5
  139. data/templates/music/extras/sonic_pi_boot.rb +220 -0
  140. data/templates/music/manifest.yml +16 -0
  141. data/templates/music/objects/conductor.md +154 -0
  142. data/templates/music/objects/sonic_pi.md +65 -0
  143. data/templates/music/primitives/play_note.rb +55 -0
  144. data/templates/music/primitives/play_sequence.rb +51 -0
  145. data/templates/music/primitives/query_sonic_pi.rb +139 -0
  146. data/templates/music/primitives/set_synth.rb +53 -0
  147. data/templates/music/primitives/set_tempo.rb +43 -0
  148. data/templates/music/primitives/stop_voice.rb +44 -0
  149. data/templates/music/primitives/support/osc_bridge.rb +33 -0
  150. data/templates/music/vendor/osc-ruby-1.1.5/LICENSE +19 -0
  151. data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/address_pattern.rb +58 -0
  152. data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/broadcast_client.rb +24 -0
  153. data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/bundle.rb +46 -0
  154. data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/client.rb +13 -0
  155. data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/core_ext/time.rb +6 -0
  156. data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/em_server.rb +63 -0
  157. data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/message.rb +46 -0
  158. data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/network_packet.rb +42 -0
  159. data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_argument.rb +26 -0
  160. data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_packet.rb +140 -0
  161. data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_types.rb +53 -0
  162. data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/server.rb +91 -0
  163. data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby.rb +18 -0
  164. data/templates/music-beginner/README.md +100 -0
  165. data/templates/music-beginner/extras/README.md +27 -0
  166. data/templates/music-beginner/extras/sonic_pi_boot.rb +297 -0
  167. data/templates/music-beginner/manifest.yml +25 -0
  168. data/templates/music-beginner/objects/conductor.md +222 -0
  169. data/templates/music-beginner/objects/engine_setup.md +54 -0
  170. data/templates/music-beginner/objects/voice_bass.md +43 -0
  171. data/templates/music-beginner/objects/voice_keys.md +48 -0
  172. data/templates/music-beginner/objects/voice_pad.md +46 -0
  173. data/templates/music-beginner/objects/voice_percussion.md +45 -0
  174. data/templates/music-beginner/objects/voice_texture.md +37 -0
  175. data/templates/music-beginner/primitives/play_pattern.rb +118 -0
  176. data/templates/music-beginner/primitives/query_music_engine.rb +94 -0
  177. data/templates/music-beginner/primitives/set_instrument.rb +47 -0
  178. data/templates/music-beginner/primitives/set_tempo.rb +43 -0
  179. data/templates/music-beginner/primitives/set_voice_level.rb +46 -0
  180. data/templates/music-beginner/primitives/stop_voice.rb +37 -0
  181. data/templates/music-beginner/primitives/support/osc_bridge.rb +25 -0
  182. data/templates/music-beginner/vendor/osc-ruby-1.1.5/LICENSE +19 -0
  183. data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/address_pattern.rb +58 -0
  184. data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/broadcast_client.rb +24 -0
  185. data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/bundle.rb +46 -0
  186. data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/client.rb +13 -0
  187. data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/core_ext/time.rb +6 -0
  188. data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/em_server.rb +63 -0
  189. data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/message.rb +46 -0
  190. data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/network_packet.rb +42 -0
  191. data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_argument.rb +26 -0
  192. data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_packet.rb +140 -0
  193. data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_types.rb +53 -0
  194. data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/server.rb +91 -0
  195. data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby.rb +18 -0
  196. data/templates/music-pro/extras/AUDITION.md +100 -0
  197. data/templates/music-pro/extras/sonic_pi_boot.rb +546 -0
  198. data/templates/music-pro/manifest.yml +19 -0
  199. data/templates/music-pro/objects/conductor.md +126 -0
  200. data/templates/music-pro/objects/sonic_pi.md +35 -0
  201. data/templates/music-pro/objects/voice_harmony.md +48 -0
  202. data/templates/music-pro/objects/voice_rhythm.md +43 -0
  203. data/templates/music-pro/objects/voice_texture.md +42 -0
  204. data/templates/music-pro/primitives/define_patch.rb +46 -0
  205. data/templates/music-pro/primitives/query_music_engine.rb +69 -0
  206. data/templates/music-pro/primitives/set_live_pattern.rb +70 -0
  207. data/templates/music-pro/primitives/set_tempo.rb +31 -0
  208. data/templates/music-pro/primitives/stop_voice.rb +31 -0
  209. data/templates/music-pro/primitives/support/music_protocol.rb +136 -0
  210. data/templates/music-pro/primitives/support/osc_bridge.rb +33 -0
  211. data/templates/music-pro/primitives/trigger_event.rb +44 -0
  212. data/templates/music-pro/vendor/osc-ruby-1.1.5/LICENSE +19 -0
  213. data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/address_pattern.rb +58 -0
  214. data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/broadcast_client.rb +24 -0
  215. data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/bundle.rb +46 -0
  216. data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/client.rb +13 -0
  217. data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/core_ext/time.rb +6 -0
  218. data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/em_server.rb +63 -0
  219. data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/message.rb +46 -0
  220. data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/network_packet.rb +42 -0
  221. data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_argument.rb +26 -0
  222. data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_packet.rb +140 -0
  223. data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_types.rb +53 -0
  224. data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/server.rb +91 -0
  225. data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby.rb +18 -0
  226. data/templates/writer/objects/coordinator.md +14 -3
  227. data/templates/writer/objects/editor.md +1 -1
  228. data/templates/writer/objects/researcher.md +2 -2
  229. data/tools/thread-explorer.html +28 -1
  230. metadata +175 -65
  231. data/CLAUDE.md +0 -176
  232. data/Gemfile +0 -10
  233. data/Gemfile.lock +0 -233
  234. data/IMPLEMENTATION_PLAN.md +0 -1073
  235. data/Rakefile +0 -27
  236. data/design-doc-v2.md +0 -1232
  237. data/frontend/.gitignore +0 -3
  238. data/frontend/index.html +0 -17
  239. data/frontend/package-lock.json +0 -4540
  240. data/frontend/package.json +0 -36
  241. data/frontend/postcss.config.js +0 -6
  242. data/frontend/src/App.tsx +0 -103
  243. data/frontend/src/canvas/CanvasView.tsx +0 -113
  244. data/frontend/src/canvas/ForceLayout.ts +0 -115
  245. data/frontend/src/canvas/SceneManager.ts +0 -587
  246. data/frontend/src/canvas/canvasStore.ts +0 -47
  247. data/frontend/src/canvas/constants.ts +0 -95
  248. data/frontend/src/canvas/controls/CameraControls.ts +0 -98
  249. data/frontend/src/canvas/edges/MessageArc.ts +0 -149
  250. data/frontend/src/canvas/inspector/InspectorPanel.tsx +0 -31
  251. data/frontend/src/canvas/inspector/POInspector.tsx +0 -262
  252. data/frontend/src/canvas/inspector/ToolCallInspector.tsx +0 -67
  253. data/frontend/src/canvas/nodes/PONode.ts +0 -249
  254. data/frontend/src/canvas/nodes/ToolCallNode.ts +0 -116
  255. data/frontend/src/canvas/types.ts +0 -24
  256. data/frontend/src/components/ContextMenu.tsx +0 -68
  257. data/frontend/src/components/Inspector.tsx +0 -232
  258. data/frontend/src/components/MarkdownMessage.tsx +0 -155
  259. data/frontend/src/components/MethodList.tsx +0 -90
  260. data/frontend/src/components/ModelSelector.tsx +0 -115
  261. data/frontend/src/components/NotificationPanel.tsx +0 -130
  262. data/frontend/src/components/ObjectList.tsx +0 -78
  263. data/frontend/src/components/PaneSlot.tsx +0 -30
  264. data/frontend/src/components/SourcePane.tsx +0 -202
  265. data/frontend/src/components/SystemBar.tsx +0 -74
  266. data/frontend/src/components/Transcript.tsx +0 -76
  267. data/frontend/src/components/UsagePanel.tsx +0 -105
  268. data/frontend/src/components/Workspace.tsx +0 -260
  269. data/frontend/src/components/index.ts +0 -12
  270. data/frontend/src/hooks/useResize.ts +0 -55
  271. data/frontend/src/hooks/useWebSocket.ts +0 -517
  272. data/frontend/src/index.css +0 -103
  273. data/frontend/src/main.tsx +0 -10
  274. data/frontend/src/store/index.ts +0 -279
  275. data/frontend/src/types/index.ts +0 -165
  276. data/frontend/tailwind.config.js +0 -44
  277. data/frontend/tsconfig.json +0 -30
  278. data/frontend/vite.config.ts +0 -29
  279. data/lib/prompt_objects/server/public/assets/index-D1myxE0l.js +0 -4345
  280. data/lib/prompt_objects/server/public/assets/index-DdCcwC-Z.css +0 -1
  281. data/lib/prompt_objects/universal/add_primitive.rb +0 -118
  282. data/lib/prompt_objects/universal/ask_human.rb +0 -109
  283. data/lib/prompt_objects/universal/create_primitive.rb +0 -174
  284. data/lib/prompt_objects/universal/list_primitives.rb +0 -145
  285. data/lib/prompt_objects/universal/think.rb +0 -41
  286. data/prompt_objects.gemspec +0 -50
  287. data/templates/basic/.gitignore +0 -2
  288. data/templates/developer/.gitignore +0 -5
  289. data/templates/empty/.gitignore +0 -5
  290. data/templates/empty/objects/.gitkeep +0 -0
  291. data/templates/minimal/.gitignore +0 -5
  292. data/templates/writer/.gitignore +0 -5
@@ -0,0 +1,254 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PromptObjects
4
+ module Repositories
5
+ class RunRepository < BaseRepository
6
+ def enqueue(workspace_session_id:, thread_id:, po_name:, input_content:,
7
+ definition_version:, source_type:, source_name: nil,
8
+ client_request_id: nil, provider: nil, model: nil,
9
+ capability_policy_version: nil, parent_run_id: nil,
10
+ priority: 0, metadata: {}, id: SecureRandom.uuid)
11
+ enqueue_with_result(
12
+ workspace_session_id: workspace_session_id,
13
+ thread_id: thread_id,
14
+ po_name: po_name,
15
+ input_content: input_content,
16
+ definition_version: definition_version,
17
+ source_type: source_type,
18
+ source_name: source_name,
19
+ client_request_id: client_request_id,
20
+ provider: provider,
21
+ model: model,
22
+ capability_policy_version: capability_policy_version,
23
+ parent_run_id: parent_run_id,
24
+ priority: priority,
25
+ metadata: metadata,
26
+ id: id
27
+ ).first
28
+ end
29
+
30
+ def enqueue_with_result(workspace_session_id:, thread_id:, po_name:, input_content:,
31
+ definition_version:, source_type:, source_name: nil,
32
+ client_request_id: nil, provider: nil, model: nil,
33
+ capability_policy_version: nil, parent_run_id: nil,
34
+ priority: 0, metadata: {}, id: SecureRandom.uuid)
35
+ transaction do |db|
36
+ if client_request_id
37
+ existing = find_idempotent(db, workspace_session_id, source_type, client_request_id)
38
+ next [parse(existing), false] if existing
39
+ end
40
+
41
+ turn_ordinal = db.get_first_value(
42
+ "SELECT COALESCE(MAX(turn_ordinal), 0) + 1 FROM runs WHERE thread_id = ?",
43
+ [thread_id]
44
+ )
45
+ root_run_id = parent_run_id ? root_for(db, parent_run_id) : id
46
+ now = Time.now.utc.iso8601
47
+ params = [
48
+ id, client_request_id, workspace_session_id, thread_id, po_name,
49
+ turn_ordinal, input_content, definition_version, provider, model,
50
+ capability_policy_version, source_type, source_name, parent_run_id,
51
+ root_run_id, priority, now, JSON.generate(metadata)
52
+ ]
53
+ db.execute(<<~SQL, params)
54
+ INSERT INTO runs (
55
+ id, client_request_id, workspace_session_id, thread_id, po_name,
56
+ turn_ordinal, input_content, definition_version, provider, model,
57
+ capability_policy_version, source_type, source_name, parent_run_id,
58
+ root_run_id, status, priority, enqueued_at, metadata
59
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'queued', ?, ?, ?)
60
+ SQL
61
+ [parse(db.get_first_row("SELECT * FROM runs WHERE id = ?", [id])), true]
62
+ end
63
+ rescue SQLite3::ConstraintException
64
+ existing = client_request_id && get_by_client_request(
65
+ workspace_session_id: workspace_session_id,
66
+ source_type: source_type,
67
+ client_request_id: client_request_id
68
+ )
69
+ return [existing, false] if existing
70
+
71
+ raise
72
+ end
73
+
74
+ def get(id)
75
+ row = read { |db| db.get_first_row("SELECT * FROM runs WHERE id = ?", [id]) }
76
+ row && parse(row)
77
+ end
78
+
79
+ def get_by_client_request(workspace_session_id:, source_type:, client_request_id:)
80
+ row = read do |db|
81
+ find_idempotent(db, workspace_session_id, source_type, client_request_id)
82
+ end
83
+ row && parse(row)
84
+ end
85
+
86
+ def list(workspace_session_id:, thread_id: nil, statuses: nil, limit: 100)
87
+ conditions = ["workspace_session_id = ?"]
88
+ params = [workspace_session_id]
89
+ if thread_id
90
+ conditions << "thread_id = ?"
91
+ params << thread_id
92
+ end
93
+ if statuses&.any?
94
+ unknown = statuses.map(&:to_s) - Execution::Contracts::RUN_STATUSES
95
+ raise ArgumentError, "unknown run status: #{unknown.first}" if unknown.any?
96
+
97
+ conditions << "status IN (#{Array.new(statuses.size, '?').join(', ')})"
98
+ params.concat(statuses.map(&:to_s))
99
+ end
100
+ params << limit
101
+ sql = "SELECT * FROM runs WHERE #{conditions.join(' AND ')} ORDER BY enqueued_at DESC LIMIT ?"
102
+ read { |db| db.execute(sql, params) }.map { |row| parse(row) }
103
+ end
104
+
105
+ def tree(root_run_id:)
106
+ read do |db|
107
+ db.execute(<<~SQL, [root_run_id, root_run_id, root_run_id]).map { |row| parse(row) }
108
+ SELECT * FROM runs
109
+ WHERE id = ? OR root_run_id = ?
110
+ ORDER BY CASE WHEN id = ? THEN 0 ELSE 1 END,
111
+ enqueued_at ASC, turn_ordinal ASC, id ASC
112
+ SQL
113
+ end
114
+ end
115
+
116
+ def active_for_source(workspace_session_id:, po_name:, source_type:, source_name:)
117
+ row = read do |db|
118
+ db.get_first_row(<<~SQL, [workspace_session_id, po_name, source_type, source_name])
119
+ SELECT * FROM runs
120
+ WHERE workspace_session_id = ? AND po_name = ? AND source_type = ?
121
+ AND source_name = ? AND status IN ('queued', 'running', 'waiting')
122
+ ORDER BY turn_ordinal ASC
123
+ LIMIT 1
124
+ SQL
125
+ end
126
+ row && parse(row)
127
+ end
128
+
129
+ def thread_head?(run)
130
+ read do |db|
131
+ count = db.get_first_value(<<~SQL, [run.thread_id, run.turn_ordinal, run.id])
132
+ SELECT COUNT(*) FROM runs
133
+ WHERE thread_id = ? AND turn_ordinal < ? AND id != ?
134
+ AND status IN ('queued', 'running', 'waiting')
135
+ SQL
136
+ count.to_i.zero?
137
+ end
138
+ end
139
+
140
+ def active_count(workspace_session_id:, po_name: nil)
141
+ conditions = ["workspace_session_id = ?", "status IN ('queued', 'running', 'waiting')"]
142
+ params = [workspace_session_id]
143
+ if po_name
144
+ conditions << "po_name = ?"
145
+ params << po_name
146
+ end
147
+ read do |db|
148
+ db.get_first_value(
149
+ "SELECT COUNT(*) FROM runs WHERE #{conditions.join(' AND ')}",
150
+ params
151
+ ).to_i
152
+ end
153
+ end
154
+
155
+ def active_children(parent_run_id:)
156
+ read do |db|
157
+ db.execute(<<~SQL, [parent_run_id]).map { |row| parse(row) }
158
+ SELECT * FROM runs
159
+ WHERE parent_run_id = ? AND status IN ('queued', 'running', 'waiting')
160
+ ORDER BY enqueued_at ASC
161
+ SQL
162
+ end
163
+ end
164
+
165
+ def descendant_count(root_run_id:)
166
+ read do |db|
167
+ db.get_first_value(
168
+ "SELECT COUNT(*) FROM runs WHERE root_run_id = ? AND id != ?",
169
+ [root_run_id, root_run_id]
170
+ ).to_i
171
+ end
172
+ end
173
+
174
+ def transition(id, status:, final_response: nil, cancellation_reason: nil,
175
+ usage: nil, error: nil, history_cutoff_message_id: nil,
176
+ input_message_id: nil)
177
+ validate_status!(status)
178
+ now = Time.now.utc.iso8601
179
+ transaction do |db|
180
+ current = db.get_first_row("SELECT * FROM runs WHERE id = ?", [id])
181
+ next nil unless current
182
+
183
+ started_at = status.to_s == "running" ? (current["started_at"] || now) : current["started_at"]
184
+ finished_at = terminal_status?(status) ? now : current["finished_at"]
185
+ params = [
186
+ status.to_s, started_at, finished_at, final_response,
187
+ cancellation_reason, usage && JSON.generate(usage),
188
+ error&.class&.name, error&.message, history_cutoff_message_id,
189
+ input_message_id, id
190
+ ]
191
+ db.execute(<<~SQL, params)
192
+ UPDATE runs SET
193
+ status = ?, started_at = ?, finished_at = ?, final_response = ?,
194
+ cancellation_reason = ?, usage = ?, error_class = ?, error_message = ?,
195
+ history_cutoff_message_id = COALESCE(?, history_cutoff_message_id),
196
+ input_message_id = COALESCE(?, input_message_id)
197
+ WHERE id = ?
198
+ SQL
199
+ db.execute(
200
+ "UPDATE sessions SET last_run_status = ?, last_activity_at = ?, updated_at = ? WHERE id = ?",
201
+ [status.to_s, now, now, current["thread_id"]]
202
+ )
203
+ parse(db.get_first_row("SELECT * FROM runs WHERE id = ?", [id]))
204
+ end
205
+ end
206
+
207
+ private
208
+
209
+ def find_idempotent(db, workspace_session_id, source_type, client_request_id)
210
+ db.get_first_row(<<~SQL, [workspace_session_id, source_type, client_request_id])
211
+ SELECT * FROM runs
212
+ WHERE workspace_session_id = ? AND source_type = ? AND client_request_id = ?
213
+ LIMIT 1
214
+ SQL
215
+ end
216
+
217
+ def root_for(db, parent_run_id)
218
+ parent = db.get_first_row("SELECT id, root_run_id FROM runs WHERE id = ?", [parent_run_id])
219
+ raise ArgumentError, "unknown parent run: #{parent_run_id}" unless parent
220
+
221
+ parent["root_run_id"] || parent["id"]
222
+ end
223
+
224
+ def validate_status!(status)
225
+ return if Execution::Contracts::RUN_STATUSES.include?(status.to_s)
226
+
227
+ raise ArgumentError, "unknown run status: #{status}"
228
+ end
229
+
230
+ def terminal_status?(status)
231
+ %w[completed failed cancelled skipped interrupted].include?(status.to_s)
232
+ end
233
+
234
+ def parse(row)
235
+ Domain::Run.new(
236
+ id: row["id"], client_request_id: row["client_request_id"],
237
+ workspace_session_id: row["workspace_session_id"], thread_id: row["thread_id"],
238
+ po_name: row["po_name"], turn_ordinal: row["turn_ordinal"],
239
+ input_content: row["input_content"], input_message_id: row["input_message_id"],
240
+ history_cutoff_message_id: row["history_cutoff_message_id"],
241
+ definition_version: row["definition_version"], provider: row["provider"], model: row["model"],
242
+ capability_policy_version: row["capability_policy_version"],
243
+ source_type: row["source_type"], source_name: row["source_name"],
244
+ parent_run_id: row["parent_run_id"], root_run_id: row["root_run_id"], status: row["status"],
245
+ priority: row["priority"], enqueued_at: timestamp(row["enqueued_at"]),
246
+ started_at: timestamp(row["started_at"]), finished_at: timestamp(row["finished_at"]),
247
+ cancellation_reason: row["cancellation_reason"], final_response: row["final_response"],
248
+ usage: json(row["usage"], default: nil), error_class: row["error_class"],
249
+ error_message: row["error_message"], metadata: json(row["metadata"])
250
+ )
251
+ end
252
+ end
253
+ end
254
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PromptObjects
4
+ module Repositories
5
+ class ThreadRepository < BaseRepository
6
+ def create(workspace_session_id:, po_name:, thread_type:, name: nil,
7
+ parent_thread_id: nil, parent_run_id: nil, retention_policy: nil,
8
+ source: "web", id: SecureRandom.uuid)
9
+ validate_thread_type!(thread_type)
10
+ now = Time.now.utc.iso8601
11
+ transaction do |db|
12
+ params = [
13
+ id, po_name, name, source, source, now, now, parent_thread_id,
14
+ po_name_for_parent(db, parent_thread_id), thread_type, workspace_session_id,
15
+ parent_run_id, retention_policy, now
16
+ ]
17
+ db.execute(<<~SQL, params)
18
+ INSERT INTO sessions (
19
+ id, po_name, name, source, last_message_source, created_at, updated_at,
20
+ parent_session_id, parent_po, thread_type, session_type, workspace_session_id,
21
+ parent_run_id, retention_policy, archived, last_activity_at
22
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'thread', ?, ?, ?, 0, ?)
23
+ SQL
24
+ end
25
+ get(id)
26
+ end
27
+
28
+ def get(id)
29
+ row = read { |db| db.get_first_row("SELECT * FROM sessions WHERE id = ? AND po_name IS NOT NULL", [id]) }
30
+ row && parse(row)
31
+ end
32
+
33
+ def list(workspace_session_id:, po_name: nil, include_archived: false)
34
+ conditions = ["workspace_session_id = ?", "po_name IS NOT NULL"]
35
+ params = [workspace_session_id]
36
+ if po_name
37
+ conditions << "po_name = ?"
38
+ params << po_name
39
+ end
40
+ conditions << "archived = 0" unless include_archived
41
+ sql = "SELECT * FROM sessions WHERE #{conditions.join(' AND ')} ORDER BY updated_at DESC"
42
+ read { |db| db.execute(sql, params) }.map { |row| parse(row) }
43
+ end
44
+
45
+ def archive(id)
46
+ now = Time.now.utc.iso8601
47
+ transaction do |db|
48
+ db.execute("UPDATE sessions SET archived = 1, updated_at = ? WHERE id = ?", [now, id])
49
+ end
50
+ get(id)
51
+ end
52
+
53
+ def rename(id, name)
54
+ now = Time.now.utc.iso8601
55
+ transaction do |db|
56
+ db.execute("UPDATE sessions SET name = ?, updated_at = ? WHERE id = ?", [name, now, id])
57
+ end
58
+ get(id)
59
+ end
60
+
61
+ private
62
+
63
+ def validate_thread_type!(thread_type)
64
+ return if Execution::Contracts::THREAD_TYPES.include?(thread_type.to_s)
65
+
66
+ raise ArgumentError, "unknown thread type: #{thread_type}"
67
+ end
68
+
69
+ def po_name_for_parent(db, parent_thread_id)
70
+ return nil unless parent_thread_id
71
+
72
+ db.get_first_value("SELECT po_name FROM sessions WHERE id = ?", [parent_thread_id])
73
+ end
74
+
75
+ def parse(row)
76
+ Domain::Thread.new(
77
+ id: row["id"], workspace_session_id: row["workspace_session_id"],
78
+ po_name: row["po_name"], thread_type: row["thread_type"], name: row["name"],
79
+ parent_thread_id: row["parent_session_id"], parent_run_id: row["parent_run_id"],
80
+ retention_policy: row["retention_policy"], archived: row["archived"].to_i == 1,
81
+ created_at: timestamp(row["created_at"]), updated_at: timestamp(row["updated_at"]),
82
+ last_run_status: row["last_run_status"], last_activity_at: timestamp(row["last_activity_at"])
83
+ )
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PromptObjects
4
+ module Repositories
5
+ class ToolExecutionRepository < BaseRepository
6
+ def create(run_id:, provider_tool_call_id:, batch_id:, batch_position:,
7
+ capability_name:, arguments:, capability_policy_version: nil,
8
+ resource_keys: [], id: SecureRandom.uuid)
9
+ transaction do |db|
10
+ params = [
11
+ id, provider_tool_call_id, run_id, batch_id, batch_position,
12
+ capability_name, capability_policy_version, JSON.generate(resource_keys),
13
+ JSON.generate(arguments)
14
+ ]
15
+ db.execute(<<~SQL, params)
16
+ INSERT INTO tool_executions (
17
+ id, provider_tool_call_id, run_id, batch_id, batch_position,
18
+ capability_name, capability_policy_version, status, resource_keys, arguments
19
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, 'pending', ?, ?)
20
+ SQL
21
+ end
22
+ get(id)
23
+ end
24
+
25
+ def get(id)
26
+ row = read { |db| db.get_first_row("SELECT * FROM tool_executions WHERE id = ?", [id]) }
27
+ row && parse(row)
28
+ end
29
+
30
+ def list(run_id:)
31
+ read do |db|
32
+ db.execute(
33
+ "SELECT * FROM tool_executions WHERE run_id = ? ORDER BY batch_position ASC",
34
+ [run_id]
35
+ ).map { |row| parse(row) }
36
+ end
37
+ end
38
+
39
+ def transition(id, status:, result: nil, error: nil, cancellation_reason: nil)
40
+ validate_status!(status)
41
+ now = Time.now.utc.iso8601
42
+ transaction do |db|
43
+ current = db.get_first_row("SELECT * FROM tool_executions WHERE id = ?", [id])
44
+ next nil unless current
45
+
46
+ started_at = status.to_s == "running" ? (current["started_at"] || now) : current["started_at"]
47
+ finished_at = terminal_status?(status) ? now : current["finished_at"]
48
+ params = [
49
+ status.to_s, result.nil? ? nil : JSON.generate(result), error&.class&.name,
50
+ error&.message, cancellation_reason, started_at, finished_at, id
51
+ ]
52
+ db.execute(<<~SQL, params)
53
+ UPDATE tool_executions SET
54
+ status = ?, result = ?, error_class = ?, error_message = ?,
55
+ cancellation_reason = ?, started_at = ?, finished_at = ?
56
+ WHERE id = ?
57
+ SQL
58
+ parse(db.get_first_row("SELECT * FROM tool_executions WHERE id = ?", [id]))
59
+ end
60
+ end
61
+
62
+ private
63
+
64
+ def validate_status!(status)
65
+ return if Execution::Contracts::TOOL_EXECUTION_STATUSES.include?(status.to_s)
66
+
67
+ raise ArgumentError, "unknown tool execution status: #{status}"
68
+ end
69
+
70
+ def terminal_status?(status)
71
+ %w[completed failed cancelled timed_out].include?(status.to_s)
72
+ end
73
+
74
+ def parse(row)
75
+ Domain::ToolExecution.new(
76
+ id: row["id"], provider_tool_call_id: row["provider_tool_call_id"],
77
+ run_id: row["run_id"], batch_id: row["batch_id"], batch_position: row["batch_position"],
78
+ capability_name: row["capability_name"],
79
+ capability_policy_version: row["capability_policy_version"], status: row["status"],
80
+ resource_keys: json_array(row["resource_keys"]), arguments: json(row["arguments"]),
81
+ result: json(row["result"], default: nil), error_class: row["error_class"],
82
+ error_message: row["error_message"], cancellation_reason: row["cancellation_reason"],
83
+ started_at: timestamp(row["started_at"]), finished_at: timestamp(row["finished_at"])
84
+ )
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PromptObjects
4
+ module Repositories
5
+ class WorkspaceSessionRepository < BaseRepository
6
+ def create(name: nil, status: "active", metadata: {}, id: SecureRandom.uuid)
7
+ now = Time.now.utc.iso8601
8
+ transaction do |db|
9
+ db.execute(<<~SQL, [id, name, status, now, now, JSON.generate(metadata)])
10
+ INSERT INTO workspace_sessions (id, name, status, created_at, updated_at, metadata)
11
+ VALUES (?, ?, ?, ?, ?, ?)
12
+ SQL
13
+ end
14
+ get(id)
15
+ end
16
+
17
+ def get(id)
18
+ row = read { |db| db.get_first_row("SELECT * FROM workspace_sessions WHERE id = ?", [id]) }
19
+ row && parse(row)
20
+ end
21
+
22
+ def list(status: nil)
23
+ rows = read do |db|
24
+ if status
25
+ db.execute("SELECT * FROM workspace_sessions WHERE status = ? ORDER BY updated_at DESC", [status])
26
+ else
27
+ db.execute("SELECT * FROM workspace_sessions ORDER BY updated_at DESC")
28
+ end
29
+ end
30
+ rows.map { |row| parse(row) }
31
+ end
32
+
33
+ def update(id, name: nil, status: nil, metadata: nil)
34
+ current = get(id)
35
+ return nil unless current
36
+
37
+ next_name = name.nil? ? current.name : name
38
+ next_status = status || current.status
39
+ next_metadata = metadata ? current.metadata.merge(metadata) : current.metadata
40
+ now = Time.now.utc.iso8601
41
+ transaction do |db|
42
+ db.execute(
43
+ "UPDATE workspace_sessions SET name = ?, status = ?, metadata = ?, updated_at = ? WHERE id = ?",
44
+ [next_name, next_status, JSON.generate(next_metadata), now, id]
45
+ )
46
+ end
47
+ get(id)
48
+ end
49
+
50
+ private
51
+
52
+ def parse(row)
53
+ Domain::WorkspaceSession.new(
54
+ id: row["id"], name: row["name"], status: row["status"],
55
+ created_at: timestamp(row["created_at"]), updated_at: timestamp(row["updated_at"]),
56
+ metadata: json(row["metadata"])
57
+ )
58
+ end
59
+ end
60
+ end
61
+ end