prompt_objects 0.6.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 (291) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +126 -0
  3. data/README.md +93 -22
  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 +1337 -20
  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 +171 -519
  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 +549 -53
  65. data/lib/prompt_objects/server/app.rb +133 -14
  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 -227
  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 +600 -13
  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 +23 -15
  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 +8 -14
  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 +10 -14
  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/{store_env_data.rb → set_env_data.rb} +34 -20
  87. data/lib/prompt_objects/version.rb +5 -0
  88. data/lib/prompt_objects.rb +33 -8
  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 +2 -2
  92. data/templates/arc-agi-1/objects/solver.md +6 -5
  93. data/templates/arc-agi-1/objects/verifier.md +2 -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 +1 -1
  230. metadata +172 -65
  231. data/CLAUDE.md +0 -177
  232. data/Gemfile +0 -10
  233. data/Rakefile +0 -27
  234. data/frontend/.gitignore +0 -3
  235. data/frontend/index.html +0 -17
  236. data/frontend/package-lock.json +0 -4540
  237. data/frontend/package.json +0 -36
  238. data/frontend/postcss.config.js +0 -6
  239. data/frontend/src/App.tsx +0 -104
  240. data/frontend/src/canvas/CanvasView.tsx +0 -113
  241. data/frontend/src/canvas/ForceLayout.ts +0 -115
  242. data/frontend/src/canvas/SceneManager.ts +0 -587
  243. data/frontend/src/canvas/canvasStore.ts +0 -47
  244. data/frontend/src/canvas/constants.ts +0 -95
  245. data/frontend/src/canvas/controls/CameraControls.ts +0 -98
  246. data/frontend/src/canvas/edges/MessageArc.ts +0 -149
  247. data/frontend/src/canvas/inspector/InspectorPanel.tsx +0 -31
  248. data/frontend/src/canvas/inspector/POInspector.tsx +0 -262
  249. data/frontend/src/canvas/inspector/ToolCallInspector.tsx +0 -67
  250. data/frontend/src/canvas/nodes/PONode.ts +0 -249
  251. data/frontend/src/canvas/nodes/ToolCallNode.ts +0 -116
  252. data/frontend/src/canvas/types.ts +0 -24
  253. data/frontend/src/components/ContextMenu.tsx +0 -68
  254. data/frontend/src/components/EnvDataPane.tsx +0 -69
  255. data/frontend/src/components/Inspector.tsx +0 -263
  256. data/frontend/src/components/MarkdownMessage.tsx +0 -155
  257. data/frontend/src/components/MethodList.tsx +0 -90
  258. data/frontend/src/components/ModelSelector.tsx +0 -115
  259. data/frontend/src/components/NotificationPanel.tsx +0 -130
  260. data/frontend/src/components/ObjectList.tsx +0 -78
  261. data/frontend/src/components/PaneSlot.tsx +0 -30
  262. data/frontend/src/components/SourcePane.tsx +0 -202
  263. data/frontend/src/components/SystemBar.tsx +0 -74
  264. data/frontend/src/components/Transcript.tsx +0 -76
  265. data/frontend/src/components/UsagePanel.tsx +0 -105
  266. data/frontend/src/components/Workspace.tsx +0 -260
  267. data/frontend/src/components/index.ts +0 -12
  268. data/frontend/src/hooks/useResize.ts +0 -55
  269. data/frontend/src/hooks/useWebSocket.ts +0 -559
  270. data/frontend/src/index.css +0 -102
  271. data/frontend/src/main.tsx +0 -10
  272. data/frontend/src/store/index.ts +0 -311
  273. data/frontend/src/types/index.ts +0 -176
  274. data/frontend/tailwind.config.js +0 -44
  275. data/frontend/tsconfig.json +0 -30
  276. data/frontend/vite.config.ts +0 -29
  277. data/lib/prompt_objects/server/public/assets/index-DEPawnfZ.js +0 -4345
  278. data/lib/prompt_objects/server/public/assets/index-oMrRce1m.css +0 -1
  279. data/lib/prompt_objects/universal/add_primitive.rb +0 -118
  280. data/lib/prompt_objects/universal/ask_human.rb +0 -109
  281. data/lib/prompt_objects/universal/create_primitive.rb +0 -174
  282. data/lib/prompt_objects/universal/list_primitives.rb +0 -145
  283. data/lib/prompt_objects/universal/think.rb +0 -41
  284. data/lib/prompt_objects/universal/update_env_data.rb +0 -88
  285. data/prompt_objects.gemspec +0 -50
  286. data/templates/basic/.gitignore +0 -2
  287. data/templates/developer/.gitignore +0 -5
  288. data/templates/empty/.gitignore +0 -5
  289. data/templates/empty/objects/.gitkeep +0 -0
  290. data/templates/minimal/.gitignore +0 -5
  291. data/templates/writer/.gitignore +0 -5
@@ -89,9 +89,12 @@ module PromptObjects
89
89
  po = @runtime.registry.get(po_name)
90
90
  return [{ uri: "po://#{po_name}/conversation", mimeType: "text/plain", text: "PO not found" }] unless po
91
91
 
92
- history = po.history.map do |msg|
93
- { role: msg[:role].to_s, content: msg[:content] }
94
- end
92
+ workspace_id = @runtime.active_session(initiated_by: "service:mcp")[:id]
93
+ thread = Repositories::ThreadRepository.new(@runtime.session_store)
94
+ .list(workspace_session_id: workspace_id, po_name: po_name).last
95
+ history = thread ? Repositories::MessageRepository.new(@runtime.session_store)
96
+ .list(thread_id: thread.id, limit: 10_000)
97
+ .map { |msg| { role: msg[:role].to_s, content: msg[:content] } } : []
95
98
 
96
99
  [{
97
100
  uri: "po://#{po_name}/conversation",
@@ -143,7 +146,10 @@ module PromptObjects
143
146
  def read_sessions(po_name)
144
147
  return [{ uri: "sessions://#{po_name}", mimeType: "text/plain", text: "Sessions not available" }] unless @runtime.session_store
145
148
 
146
- sessions = @runtime.session_store.list_sessions(po_name: po_name)
149
+ workspace_id = @runtime.active_session(initiated_by: "service:mcp")[:id]
150
+ sessions = Repositories::ThreadRepository.new(@runtime.session_store)
151
+ .list(workspace_session_id: workspace_id, po_name: po_name)
152
+ .map(&:to_h)
147
153
  [{
148
154
  uri: "sessions://#{po_name}",
149
155
  mimeType: "application/json",
@@ -154,7 +160,10 @@ module PromptObjects
154
160
  def read_all_sessions
155
161
  return [{ uri: "sessions://all", mimeType: "text/plain", text: "Sessions not available" }] unless @runtime.session_store
156
162
 
157
- sessions = @runtime.session_store.list_all_sessions
163
+ workspace_id = @runtime.active_session(initiated_by: "service:mcp")[:id]
164
+ sessions = Repositories::ThreadRepository.new(@runtime.session_store)
165
+ .list(workspace_session_id: workspace_id)
166
+ .map(&:to_h)
158
167
  [{
159
168
  uri: "sessions://all",
160
169
  mimeType: "application/json",
@@ -211,17 +220,27 @@ module PromptObjects
211
220
  },
212
221
  session_id: {
213
222
  type: "string",
214
- description: "Optional session ID to continue. If not provided, uses or creates a default session."
223
+ description: "Compatibility alias for thread_id"
224
+ },
225
+ thread_id: {
226
+ type: "string",
227
+ description: "Optional runtime thread ID to continue"
228
+ },
229
+ workspace_session_id: {
230
+ type: "string",
231
+ description: "Optional workspace session ID"
232
+ },
233
+ client_request_id: {
234
+ type: "string",
235
+ description: "Optional idempotency key"
215
236
  }
216
237
  },
217
238
  required: %w[po_name message]
218
239
  )
219
240
 
220
- def self.call(po_name:, message:, session_id: nil, server_context:)
241
+ def self.call(po_name:, message:, session_id: nil, thread_id: nil,
242
+ workspace_session_id: nil, client_request_id: nil, server_context:)
221
243
  env = server_context[:env]
222
- context = server_context[:context]
223
- connector = server_context[:connector]
224
-
225
244
  po = env.registry.get(po_name)
226
245
  unless po.is_a?(PromptObjects::PromptObject)
227
246
  return ::MCP::Tool::Response.new([{
@@ -230,69 +249,30 @@ module PromptObjects
230
249
  }])
231
250
  end
232
251
 
233
- # Handle session
234
- current_session_id = setup_session(po, session_id, env, connector)
235
-
236
- # Log to message bus
237
- env.bus.publish(from: "mcp_client", to: po_name, message: message)
238
-
239
- # Set context for this interaction
240
- context.current_capability = po_name
241
- po.state = :working
242
-
243
- begin
244
- response = po.receive(message, context: context)
245
- po.state = :idle
246
-
247
- # Update session source
248
- connector&.send(:update_session_source, current_session_id) if current_session_id
249
-
250
- # Log response
251
- env.bus.publish(from: po_name, to: "mcp_client", message: response)
252
-
253
- ::MCP::Tool::Response.new([{
254
- type: "text",
255
- text: JSON.generate({
256
- po_name: po_name,
257
- response: response,
258
- session_id: current_session_id,
259
- history_length: po.history.length
260
- })
261
- }])
262
- rescue StandardError => e
263
- po.state = :idle
264
- ::MCP::Tool::Response.new([{
265
- type: "text",
266
- text: JSON.generate({ error: e.message, backtrace: e.backtrace.first(5) })
267
- }])
268
- end
269
- end
270
-
271
- def self.setup_session(po, session_id, env, connector)
272
- return nil unless env.session_store
273
-
274
- if session_id
275
- # Switch to specified session
276
- session = env.session_store.get_session(session_id)
277
- if session && session[:po_name] == po.name
278
- po.switch_session(session_id)
279
- return session_id
280
- end
281
- end
282
-
283
- # Use existing session or create new one
284
- existing_id = po.instance_variable_get(:@session_id)
285
- return existing_id if existing_id
286
-
287
- # Create new MCP session
288
- new_id = env.session_store.create_session(
289
- po_name: po.name,
290
- name: "MCP Session #{Time.now.strftime('%H:%M')}",
291
- source: connector&.source_name || "mcp"
252
+ run, response = env.execute_message_run(
253
+ target_po: po_name,
254
+ content: message,
255
+ from_source: "service:mcp",
256
+ workspace_session_id: workspace_session_id,
257
+ thread_id: thread_id || session_id,
258
+ client_request_id: client_request_id,
259
+ tui_mode: true
292
260
  )
293
- po.instance_variable_set(:@session_id, new_id)
294
- po.send(:reload_history_from_session) if po.respond_to?(:reload_history_from_session, true)
295
- new_id
261
+ history_length = Repositories::MessageRepository.new(env.session_store)
262
+ .list(thread_id: run.thread_id, limit: 10_000).length
263
+ ::MCP::Tool::Response.new([{
264
+ type: "text",
265
+ text: JSON.generate({
266
+ po_name: po_name,
267
+ response: response,
268
+ workspace_session_id: run.workspace_session_id,
269
+ thread_id: run.thread_id,
270
+ session_id: run.thread_id,
271
+ run_id: run.id,
272
+ status: run.status,
273
+ history_length: history_length
274
+ })
275
+ }])
296
276
  end
297
277
  end
298
278
 
@@ -327,18 +307,20 @@ module PromptObjects
327
307
  }])
328
308
  end
329
309
 
330
- history = if session_id && env.session_store
331
- messages = env.session_store.get_messages(session_id)
332
- messages.map { |m| { role: m[:role], content: m[:content] } }
333
- else
334
- po.history.map { |msg| { role: msg[:role].to_s, content: msg[:content] } }
335
- end
310
+ workspace_id = env.active_session(initiated_by: "service:mcp")[:id]
311
+ threads = Repositories::ThreadRepository.new(env.session_store)
312
+ .list(workspace_session_id: workspace_id, po_name: po_name)
313
+ thread = session_id ? threads.find { |candidate| candidate.id == session_id } : threads.last
314
+ history = thread ? Repositories::MessageRepository.new(env.session_store)
315
+ .list(thread_id: thread.id, limit: 10_000)
316
+ .map { |message| { role: message[:role].to_s, content: message[:content] } } : []
336
317
 
337
318
  ::MCP::Tool::Response.new([{
338
319
  type: "text",
339
320
  text: JSON.generate({
340
321
  po_name: po_name,
341
- session_id: session_id || po.instance_variable_get(:@session_id),
322
+ session_id: thread&.id,
323
+ thread_id: thread&.id,
342
324
  history: history
343
325
  })
344
326
  }])
@@ -374,35 +356,37 @@ module PromptObjects
374
356
  }])
375
357
  end
376
358
 
377
- sessions = if po_name
378
- env.session_store.list_sessions(po_name: po_name)
379
- else
380
- env.session_store.list_all_sessions(source: source)
381
- end
382
-
383
- sessions_data = sessions.map do |s|
359
+ workspace_id = env.active_session(initiated_by: "service:mcp")[:id]
360
+ threads = Repositories::ThreadRepository.new(env.session_store)
361
+ .list(workspace_session_id: workspace_id, po_name: po_name)
362
+ threads = threads.select { |thread| thread.thread_type == source } if source
363
+ messages = Repositories::MessageRepository.new(env.session_store)
364
+ sessions_data = threads.map do |thread|
384
365
  {
385
- id: s[:id],
386
- po_name: s[:po_name],
387
- name: s[:name],
388
- source: s[:source],
389
- message_count: env.session_store.message_count(s[:id]),
390
- created_at: s[:created_at]&.iso8601,
391
- updated_at: s[:updated_at]&.iso8601
366
+ id: thread.id,
367
+ thread_id: thread.id,
368
+ workspace_session_id: thread.workspace_session_id,
369
+ po_name: thread.po_name,
370
+ name: thread.name,
371
+ source: thread.thread_type,
372
+ thread_type: thread.thread_type,
373
+ message_count: messages.list(thread_id: thread.id, limit: 10_000).length,
374
+ created_at: thread.created_at&.iso8601,
375
+ updated_at: thread.updated_at&.iso8601
392
376
  }
393
377
  end
394
378
 
395
379
  ::MCP::Tool::Response.new([{
396
380
  type: "text",
397
- text: JSON.generate({ sessions: sessions_data })
381
+ text: JSON.generate({ sessions: sessions_data, threads: sessions_data })
398
382
  }])
399
383
  end
400
384
  end
401
385
 
402
- # Get pending human requests
386
+ # Get pending capability-approval requests.
403
387
  class GetPendingRequests < ::MCP::Tool
404
388
  tool_name "get_pending_requests"
405
- description "Get all pending human requests across all prompt objects"
389
+ description "Get pending capability-approval requests"
406
390
 
407
391
  input_schema(
408
392
  type: "object",
@@ -428,10 +412,10 @@ module PromptObjects
428
412
  end
429
413
  end
430
414
 
431
- # Respond to a human request
415
+ # Respond to a capability-approval request.
432
416
  class RespondToRequest < ::MCP::Tool
433
417
  tool_name "respond_to_request"
434
- description "Respond to a pending human request"
418
+ description "Respond to a pending capability-approval request"
435
419
 
436
420
  input_schema(
437
421
  type: "object",
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PromptObjects
4
+ module Domain
5
+ WorkspaceSession = Data.define(:id, :name, :status, :created_at, :updated_at, :metadata)
6
+
7
+ Thread = Data.define(
8
+ :id, :workspace_session_id, :po_name, :thread_type, :name,
9
+ :parent_thread_id, :parent_run_id, :retention_policy, :archived,
10
+ :created_at, :updated_at, :last_run_status, :last_activity_at
11
+ )
12
+
13
+ Run = Data.define(
14
+ :id, :client_request_id, :workspace_session_id, :thread_id, :po_name,
15
+ :turn_ordinal, :input_content, :input_message_id, :history_cutoff_message_id,
16
+ :definition_version, :provider, :model, :capability_policy_version,
17
+ :source_type, :source_name, :parent_run_id, :root_run_id, :status,
18
+ :priority, :enqueued_at, :started_at, :finished_at, :cancellation_reason,
19
+ :final_response, :usage, :error_class, :error_message, :metadata
20
+ )
21
+
22
+ RunEvent = Data.define(
23
+ :id, :run_id, :sequence, :workspace_cursor, :workspace_session_id,
24
+ :thread_id, :kind, :timestamp, :data
25
+ )
26
+
27
+ ToolExecution = Data.define(
28
+ :id, :provider_tool_call_id, :run_id, :batch_id, :batch_position,
29
+ :capability_name, :capability_policy_version, :status, :resource_keys,
30
+ :arguments, :result, :error_class, :error_message, :cancellation_reason,
31
+ :started_at, :finished_at
32
+ )
33
+
34
+ Artifact = Data.define(
35
+ :id, :workspace_session_id, :owner_po, :owner_thread_id,
36
+ :producing_run_id, :title, :html, :revision, :mode,
37
+ :created_at, :updated_at, :metadata
38
+ )
39
+
40
+ PromptObjectDefinition = Data.define(
41
+ :po_name, :version, :prompt, :config, :provider, :model,
42
+ :capability_policy_version
43
+ )
44
+ end
45
+ end
@@ -2,7 +2,8 @@
2
2
 
3
3
  module PromptObjects
4
4
  module Env
5
- # Exports an environment as a git bundle (.poenv file).
5
+ # Exports an environment as a git bundle (.dump file
6
+ # Declarative Unified Memory Package).
6
7
  # The bundle contains all commits, objects, primitives, and manifest.
7
8
  # Sessions are NOT included (private data).
8
9
  class Exporter
@@ -13,7 +14,7 @@ module PromptObjects
13
14
  end
14
15
 
15
16
  # Export the environment to a git bundle.
16
- # @param output_path [String] Path for the output .poenv file
17
+ # @param output_path [String] Path for the output .dump file
17
18
  # @param commit_message [String, nil] Message for any uncommitted changes
18
19
  # @return [Hash] Export result with :success, :path, :stats
19
20
  def export(output_path, commit_message: nil)
@@ -60,8 +61,8 @@ module PromptObjects
60
61
  end
61
62
 
62
63
  def normalize_output_path(path)
63
- # Add .poenv extension if not present
64
- path = "#{path}.poenv" unless path.end_with?(".poenv")
64
+ # Add .dump extension if not present (.poenv accepted for legacy paths)
65
+ path = "#{path}.dump" unless path.end_with?(".dump", ".poenv")
65
66
 
66
67
  # Expand to absolute path
67
68
  File.expand_path(path)
@@ -5,6 +5,9 @@ module PromptObjects
5
5
  # Git operations for environments.
6
6
  # Each environment is a git repository for built-in versioning.
7
7
  module Git
8
+ DEFAULT_USER_NAME = "Prompt Objects"
9
+ DEFAULT_USER_EMAIL = "prompt-objects@localhost"
10
+
8
11
  # Check if a directory is a git repository.
9
12
  # @param path [String]
10
13
  # @return [Boolean]
@@ -15,7 +18,9 @@ module PromptObjects
15
18
  # Initialize a git repository.
16
19
  # @param path [String]
17
20
  def self.init(path)
18
- Dir.chdir(path) { system("git init --quiet") }
21
+ initialized = Dir.chdir(path) { system("git", "init", "--quiet") }
22
+ ensure_identity(path) if initialized
23
+ initialized
19
24
  end
20
25
 
21
26
  # Stage all changes.
@@ -29,6 +34,7 @@ module PromptObjects
29
34
  # @param message [String]
30
35
  # @return [Boolean] Success
31
36
  def self.commit(path, message)
37
+ ensure_identity(path)
32
38
  Dir.chdir(path) do
33
39
  system("git add -A")
34
40
  system("git", "commit", "--quiet", "-m", message)
@@ -113,6 +119,20 @@ module PromptObjects
113
119
  commit(path, message)
114
120
  true
115
121
  end
122
+
123
+ # New users may not have a global Git identity yet. Environment history
124
+ # should still work without modifying their global configuration, so use
125
+ # a repository-local automation identity only when no identity resolves.
126
+ def self.ensure_identity(path)
127
+ Dir.chdir(path) do
128
+ unless system("git", "config", "user.name", out: File::NULL, err: File::NULL)
129
+ system("git", "config", "user.name", DEFAULT_USER_NAME)
130
+ end
131
+ unless system("git", "config", "user.email", out: File::NULL, err: File::NULL)
132
+ system("git", "config", "user.email", DEFAULT_USER_EMAIL)
133
+ end
134
+ end
135
+ end
116
136
  end
117
137
  end
118
138
  end
@@ -5,7 +5,8 @@ require "tmpdir"
5
5
 
6
6
  module PromptObjects
7
7
  module Env
8
- # Imports an environment from a git bundle (.poenv file).
8
+ # Imports an environment from a git bundle (.dump file
9
+ # Declarative Unified Memory Package; legacy .poenv bundles work too).
9
10
  # Handles security concerns around custom primitives.
10
11
  class Importer
11
12
  # Result of inspecting a bundle before import
@@ -20,7 +21,7 @@ module PromptObjects
20
21
  keyword_init: true
21
22
  )
22
23
 
23
- # @param bundle_path [String] Path to the .poenv bundle file
24
+ # @param bundle_path [String] Path to the .dump (or legacy .poenv) bundle file
24
25
  def initialize(bundle_path)
25
26
  @bundle_path = File.expand_path(bundle_path)
26
27
  end
@@ -38,7 +39,7 @@ module PromptObjects
38
39
  end
39
40
 
40
41
  # Clone to temp dir to inspect contents
41
- Dir.mktmpdir("poenv_inspect_") do |temp_dir|
42
+ Dir.mktmpdir("dump_inspect_") do |temp_dir|
42
43
  unless Git.clone_bundle(@bundle_path, temp_dir)
43
44
  return InspectResult.new(valid: false, error: "Failed to extract bundle")
44
45
  end
@@ -127,7 +128,7 @@ module PromptObjects
127
128
 
128
129
  InspectResult.new(
129
130
  valid: true,
130
- name: manifest_name || File.basename(@bundle_path, ".poenv"),
131
+ name: manifest_name || File.basename(@bundle_path, ".*"),
131
132
  description: manifest_desc,
132
133
  objects: objects,
133
134
  primitives: primitives,
@@ -127,6 +127,7 @@ module PromptObjects
127
127
  FileUtils.mkdir_p(env_path)
128
128
  FileUtils.mkdir_p(File.join(env_path, "objects"))
129
129
  FileUtils.mkdir_p(File.join(env_path, "primitives"))
130
+ FileUtils.mkdir_p(File.join(env_path, "services"))
130
131
 
131
132
  # Copy template if specified
132
133
  template_manifest = copy_template(env_path, template) if template
@@ -136,7 +137,10 @@ module PromptObjects
136
137
  name: name,
137
138
  description: description || template_manifest&.dig("description"),
138
139
  icon: template_manifest&.dig("icon") || "📦",
139
- color: template_manifest&.dig("color") || "#4A90D9"
140
+ color: template_manifest&.dig("color") || "#4A90D9",
141
+ default_po: template_manifest&.dig("default_po"),
142
+ dependencies: template_manifest&.dig("dependencies"),
143
+ llm: template_manifest&.dig("llm")
140
144
  )
141
145
  manifest.save_to_dir(env_path)
142
146
 
@@ -252,6 +256,22 @@ module PromptObjects
252
256
  FileUtils.rm_rf(path)
253
257
  end
254
258
 
259
+ # Permanently delete an ACTIVE environment (its directory and all data).
260
+ # Clears the default if this was it. For a recoverable flow, archive first.
261
+ # @param name [String]
262
+ def destroy(name)
263
+ raise Error, "Environment '#{name}' not found" unless environment_exists?(name)
264
+ raise Error, "Cannot delete the development environment" if name == DEV_ENV_NAME
265
+
266
+ FileUtils.rm_rf(environment_path(name))
267
+
268
+ if default_environment == name
269
+ config = global_config
270
+ config.delete("default_environment")
271
+ save_global_config(config)
272
+ end
273
+ end
274
+
255
275
  # Clone an environment.
256
276
  # @param source_name [String]
257
277
  # @param new_name [String]
@@ -348,6 +368,27 @@ module PromptObjects
348
368
  FileUtils.cp_r(Dir.glob("#{template_primitives}/*"), File.join(env_path, "primitives"))
349
369
  end
350
370
 
371
+ # Copy services if present
372
+ template_services = File.join(template_path, "services")
373
+ if Dir.exist?(template_services)
374
+ FileUtils.mkdir_p(File.join(env_path, "services"))
375
+ FileUtils.cp_r(Dir.glob("#{template_services}/*"), File.join(env_path, "services"))
376
+ end
377
+
378
+ # Copy vendor (bundled gem dependencies) if present
379
+ template_vendor = File.join(template_path, "vendor")
380
+ if Dir.exist?(template_vendor)
381
+ FileUtils.mkdir_p(File.join(env_path, "vendor"))
382
+ FileUtils.cp_r(Dir.glob("#{template_vendor}/*"), File.join(env_path, "vendor"))
383
+ end
384
+
385
+ # Copy extras if present
386
+ template_extras = File.join(template_path, "extras")
387
+ if Dir.exist?(template_extras)
388
+ FileUtils.mkdir_p(File.join(env_path, "extras"))
389
+ FileUtils.cp_r(Dir.glob("#{template_extras}/*"), File.join(env_path, "extras"))
390
+ end
391
+
351
392
  template_manifest
352
393
  end
353
394
 
@@ -380,11 +421,8 @@ module PromptObjects
380
421
  # Initialize git repository.
381
422
  # @param env_path [String]
382
423
  def init_git(env_path)
383
- Dir.chdir(env_path) do
384
- system("git init --quiet")
385
- system("git add .")
386
- system("git commit --quiet -m 'Initial environment setup'")
387
- end
424
+ Git.init(env_path)
425
+ Git.commit(env_path, "Initial environment setup")
388
426
  end
389
427
 
390
428
  # Ensure global config file exists.
@@ -14,7 +14,8 @@ module PromptObjects
14
14
 
15
15
  attr_accessor :name, :description, :created_at, :updated_at, :last_opened,
16
16
  :icon, :color, :tags, :default_po, :stats, :version,
17
- :archived_at, :imported_from, :imported_at
17
+ :archived_at, :imported_from, :imported_at, :dependencies,
18
+ :runtime_model, :llm
18
19
 
19
20
  def initialize(
20
21
  name:,
@@ -30,7 +31,10 @@ module PromptObjects
30
31
  version: nil,
31
32
  archived_at: nil,
32
33
  imported_from: nil,
33
- imported_at: nil
34
+ imported_at: nil,
35
+ dependencies: nil,
36
+ runtime_model: nil,
37
+ llm: nil
34
38
  )
35
39
  @name = name
36
40
  @description = description
@@ -46,6 +50,9 @@ module PromptObjects
46
50
  @archived_at = archived_at
47
51
  @imported_from = imported_from
48
52
  @imported_at = imported_at
53
+ @dependencies = dependencies || {}
54
+ @runtime_model = runtime_model || "runs"
55
+ @llm = llm || {}
49
56
  end
50
57
 
51
58
  # Load manifest from a file path.
@@ -83,7 +90,10 @@ module PromptObjects
83
90
  version: data["version"],
84
91
  archived_at: parse_time(data["archived_at"]),
85
92
  imported_from: data["imported_from"],
86
- imported_at: parse_time(data["imported_at"])
93
+ imported_at: parse_time(data["imported_at"]),
94
+ dependencies: data["dependencies"],
95
+ runtime_model: data["runtime_model"],
96
+ llm: data["llm"]
87
97
  )
88
98
  end
89
99
 
@@ -116,7 +126,10 @@ module PromptObjects
116
126
  "color" => @color,
117
127
  "tags" => @tags.any? ? @tags : nil,
118
128
  "default_po" => @default_po,
119
- "stats" => @stats
129
+ "stats" => @stats,
130
+ "dependencies" => @dependencies&.any? ? @dependencies : nil,
131
+ "runtime_model" => @runtime_model,
132
+ "llm" => @llm&.any? ? @llm : nil
120
133
  }.compact
121
134
  end
122
135
 
@@ -189,6 +202,7 @@ module PromptObjects
189
202
  lines << " Created: #{@created_at&.strftime('%Y-%m-%d %H:%M')}"
190
203
  lines << " Last opened: #{@last_opened&.strftime('%Y-%m-%d %H:%M')}" if @last_opened
191
204
  lines << " Updated: #{@updated_at&.strftime('%Y-%m-%d %H:%M')}" if @updated_at
205
+ lines << " LLM: #{@llm['provider']} / #{@llm['model']}" if @llm&.any?
192
206
 
193
207
  if imported?
194
208
  lines << ""
@@ -207,6 +221,14 @@ module PromptObjects
207
221
  lines << " Sessions: #{@stats['total_sessions'] || 0}"
208
222
  lines << " Messages: #{@stats['total_messages'] || 0}"
209
223
 
224
+ if @dependencies&.dig("gems")&.any?
225
+ lines << ""
226
+ lines << " Dependencies:"
227
+ @dependencies["gems"].each do |gem_info|
228
+ lines << " #{gem_info['name']} (#{gem_info['version']}) — #{gem_info['purpose']}"
229
+ end
230
+ end
231
+
210
232
  lines << ""
211
233
  lines << " Tags: #{@tags.join(', ')}" if @tags&.any?
212
234
  lines << " Format version: #{@version}"