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
@@ -0,0 +1,297 @@
1
+ # Prompt Objects Music Beginner — Sonic Pi live engine.
2
+ # Load from a Sonic Pi buffer with one line:
3
+ # run_file "/ABSOLUTE/PATH/TO/ENVIRONMENT/extras/sonic_pi_boot.rb"
4
+
5
+ use_bpm 72
6
+ set :music_bpm, 72
7
+ set :music_beats_per_bar, 4.0
8
+ set :music_voice_map, {}
9
+ set :music_next_slot, 0
10
+ set :music_max_slots, 8
11
+
12
+ 8.times do |i|
13
+ set :"music_v#{i}_name", nil
14
+ set :"music_v#{i}_active", false
15
+ set :"music_v#{i}_instrument", "soft_keys"
16
+ set :"music_v#{i}_level", 0.65
17
+ set :"music_v#{i}_pattern", "R@4"
18
+ set :"music_v#{i}_pending_pattern", nil
19
+ set :"music_v#{i}_pending_instrument", nil
20
+ set :"music_v#{i}_pending_stop", false
21
+ end
22
+
23
+ define :music_clamp do |value, low, high|
24
+ v = value.to_f
25
+ v = low if v < low
26
+ v = high if v > high
27
+ v
28
+ end
29
+
30
+ define :music_resolve_note do |value|
31
+ text = value.to_s.strip
32
+ result = if text.match?(/^\d+$/)
33
+ text.to_i
34
+ else
35
+ note(text.to_sym)
36
+ end
37
+ music_clamp(result, 21, 108)
38
+ rescue
39
+ nil
40
+ end
41
+
42
+ define :music_voice_slot do |name|
43
+ key = name.to_s
44
+ voice_map = get(:music_voice_map)
45
+ return voice_map[key] if voice_map[key]
46
+
47
+ index = get(:music_next_slot)
48
+ if index >= get(:music_max_slots)
49
+ puts "MUSIC WARNING: all voice slots are occupied; ignored '#{key}'"
50
+ return nil
51
+ end
52
+
53
+ updated = {}
54
+ voice_map.each { |existing_name, slot| updated[existing_name] = slot } if voice_map
55
+ updated[key] = index
56
+ set :music_voice_map, updated
57
+ set :music_next_slot, index + 1
58
+ set :"music_v#{index}_name", key
59
+ default_instrument = case key
60
+ when "pad" then "air_pad"
61
+ when "bass" then "warm_bass"
62
+ when "percussion" then "gentle_kit"
63
+ when "texture" then "sine_glow"
64
+ else "soft_keys"
65
+ end
66
+ set :"music_v#{index}_instrument", default_instrument
67
+ puts "MUSIC: registered '#{key}' as voice #{index}"
68
+ index
69
+ end
70
+
71
+ define :music_parse_event do |raw|
72
+ fields = raw.to_s.strip.split("@")
73
+ sound = fields[0].to_s.strip
74
+ duration = music_clamp(fields[1] || 1.0, 0.125, 16.0)
75
+ event_amp = music_clamp(fields[2] || 0.7, 0.0, 1.0)
76
+ gate = music_clamp(fields[3] || duration, 0.05, 16.0)
77
+ [sound, duration, event_amp, gate]
78
+ end
79
+
80
+ define :music_notes do |sound|
81
+ sound.to_s.split("+").map { |candidate| music_resolve_note(candidate) }.compact
82
+ end
83
+
84
+ # Percussion is sample-based, so a kit pattern can alternate real drum timbres.
85
+ define :music_play_drum do |sound, event_amp, voice_level|
86
+ strength = music_clamp(event_amp, 0.0, 1.0) * music_clamp(voice_level, 0.0, 1.0)
87
+ case sound.to_s.downcase
88
+ when "kick"
89
+ sample :bd_haus, amp: 0.34 * strength, pan: 0
90
+ when "hat"
91
+ sample :drum_cymbal_closed, amp: 0.13 * strength, pan: 0.18, finish: 0.22
92
+ when "snare"
93
+ sample :sn_dolf, amp: 0.16 * strength, pan: -0.08, finish: 0.32
94
+ when "tick"
95
+ sample :elec_tick, amp: 0.1 * strength, pan: 0.22
96
+ else
97
+ puts "MUSIC WARNING: gentle_kit ignored unknown token '#{sound}'"
98
+ end
99
+ end
100
+
101
+ # Presets are complete, level-matched patches. Per-event amp and voice level
102
+ # scale these conservative baked gains but cannot bypass them.
103
+ define :music_play_instrument do |instrument, notes, gate, event_amp, voice_level|
104
+ strength = music_clamp(event_amp, 0.0, 1.0) * music_clamp(voice_level, 0.0, 1.0)
105
+ return if notes.empty? || strength <= 0
106
+
107
+ case instrument.to_s
108
+ when "soft_keys"
109
+ with_fx :reverb, room: 0.55, mix: 0.14 do
110
+ use_synth :rodeo
111
+ # :rodeo has its own natural electric-piano envelope and does not accept
112
+ # the standard ADSR/cutoff options.
113
+ play_chord notes, amp: 0.24 * strength, pan: -0.08
114
+ end
115
+ when "air_pad"
116
+ with_fx :reverb, room: 0.82, mix: 0.28 do
117
+ use_synth :blade
118
+ attack_time = gate < 2 ? gate * 0.2 : 0.55
119
+ play_chord notes, attack: attack_time, sustain: gate * 0.72, release: gate * 0.45,
120
+ cutoff: 68, vibrato_depth: 0.08, amp: 0.15 * strength, pan: 0.06
121
+ end
122
+ when "warm_bass"
123
+ use_synth :bass_foundation
124
+ play notes.first, attack: 0.015, sustain: gate * 0.72, release: gate * 0.22,
125
+ cutoff: 66, amp: 0.27 * strength, pan: 0
126
+ when "felt_pluck"
127
+ with_fx :reverb, room: 0.48, mix: 0.12 do
128
+ use_synth :kalimba
129
+ play_chord notes, amp: 0.2 * strength, pan: -0.14
130
+ end
131
+ when "glass_bell"
132
+ with_fx :echo, phase: 0.375, decay: 1.5, mix: 0.08 do
133
+ with_fx :reverb, room: 0.65, mix: 0.2 do
134
+ use_synth :pretty_bell
135
+ play_chord notes, attack: 0.01, sustain: 0.05, release: gate * 0.8,
136
+ amp: 0.13 * strength, pan: 0.16
137
+ end
138
+ end
139
+ when "sine_glow"
140
+ with_fx :reverb, room: 0.72, mix: 0.22 do
141
+ use_synth :sine
142
+ play_chord notes, attack: 0.2, sustain: gate * 0.62, release: gate * 0.4,
143
+ amp: 0.12 * strength, pan: 0.12
144
+ end
145
+ else
146
+ puts "MUSIC WARNING: ignored unknown instrument '#{instrument}'"
147
+ end
148
+ end
149
+
150
+ define :music_apply_pending do |index|
151
+ pending_instrument = get(:"music_v#{index}_pending_instrument")
152
+ if pending_instrument
153
+ set :"music_v#{index}_instrument", pending_instrument
154
+ set :"music_v#{index}_pending_instrument", nil
155
+ end
156
+
157
+ pending_pattern = get(:"music_v#{index}_pending_pattern")
158
+ if pending_pattern
159
+ set :"music_v#{index}_pattern", pending_pattern
160
+ set :"music_v#{index}_pending_pattern", nil
161
+ set :"music_v#{index}_active", true
162
+ end
163
+
164
+ if get(:"music_v#{index}_pending_stop")
165
+ set :"music_v#{index}_active", false
166
+ set :"music_v#{index}_pending_stop", false
167
+ end
168
+ end
169
+
170
+ live_loop :music_bar_clock do
171
+ use_bpm get(:music_bpm)
172
+ sleep get(:music_beats_per_bar)
173
+ end
174
+
175
+ # All voices enter on the same bar cue. Each pattern is padded to a whole bar,
176
+ # so replacements happen at musically safe, synchronized boundaries.
177
+ with_fx :compressor, mix: 1 do
178
+ 8.times do |i|
179
+ live_loop :"music_voice_#{i}" do
180
+ sync :music_bar_clock
181
+ loop do
182
+ use_bpm get(:music_bpm)
183
+ music_apply_pending(i)
184
+
185
+ if get(:"music_v#{i}_active")
186
+ instrument = get(:"music_v#{i}_instrument")
187
+ voice_level = get(:"music_v#{i}_level")
188
+ pattern = get(:"music_v#{i}_pattern").to_s
189
+ elapsed = 0.0
190
+
191
+ pattern.split(";").each do |raw|
192
+ break unless get(:"music_v#{i}_active")
193
+
194
+ sound, duration, event_amp, gate = music_parse_event(raw)
195
+ unless sound.to_s.downcase == "r"
196
+ if instrument.to_s == "gentle_kit"
197
+ music_play_drum(sound, event_amp, voice_level)
198
+ else
199
+ music_play_instrument(instrument, music_notes(sound), gate, event_amp, voice_level)
200
+ end
201
+ end
202
+ sleep duration
203
+ elapsed += duration
204
+ end
205
+
206
+ bar = get(:music_beats_per_bar)
207
+ remainder = (bar - (elapsed % bar)) % bar
208
+ sleep remainder if remainder > 0.001
209
+ else
210
+ # Dormant voices wait efficiently and re-check pending activation on
211
+ # the next shared bar. Active voices remain free-running from their
212
+ # common start, avoiding an accidental extra bar between repeats.
213
+ sync :music_bar_clock
214
+ end
215
+ end
216
+ end
217
+ end
218
+ end
219
+
220
+ live_loop :music_osc_pattern do
221
+ voice, pattern = sync "/osc*/music/pattern"
222
+ index = music_voice_slot(voice)
223
+ next unless index
224
+ set :"music_v#{index}_pending_pattern", pattern.to_s
225
+ set :"music_v#{index}_pending_stop", false
226
+ puts "MUSIC: queued #{voice} pattern"
227
+ end
228
+
229
+ live_loop :music_osc_instrument do
230
+ voice, instrument = sync "/osc*/music/instrument"
231
+ index = music_voice_slot(voice)
232
+ next unless index
233
+ allowed = ["soft_keys", "air_pad", "warm_bass", "felt_pluck", "glass_bell", "sine_glow", "gentle_kit"]
234
+ if allowed.include?(instrument.to_s)
235
+ set :"music_v#{index}_pending_instrument", instrument.to_s
236
+ puts "MUSIC: queued #{voice} instrument #{instrument}"
237
+ else
238
+ puts "MUSIC WARNING: rejected instrument '#{instrument}'"
239
+ end
240
+ end
241
+
242
+ live_loop :music_osc_level do
243
+ voice, level = sync "/osc*/music/level"
244
+ index = music_voice_slot(voice)
245
+ next unless index
246
+ set :"music_v#{index}_level", music_clamp(level, 0.0, 1.0)
247
+ puts "MUSIC: #{voice} level #{get(:"music_v#{index}_level")}"
248
+ end
249
+
250
+ live_loop :music_osc_tempo do
251
+ values = sync "/osc*/music/tempo"
252
+ requested = values[0].to_i
253
+ set :music_bpm, music_clamp(requested, 50, 120).to_i
254
+ puts "MUSIC: tempo #{get(:music_bpm)} BPM"
255
+ end
256
+
257
+ live_loop :music_osc_stop do
258
+ values = sync "/osc*/music/stop"
259
+ voice = values[0].to_s
260
+ if voice == "all"
261
+ 8.times do |i|
262
+ set :"music_v#{i}_active", false
263
+ set :"music_v#{i}_pending_pattern", nil
264
+ set :"music_v#{i}_pending_stop", false
265
+ end
266
+ puts "MUSIC: all voices stopped"
267
+ else
268
+ voice_map = get(:music_voice_map)
269
+ index = voice_map[voice]
270
+ if index
271
+ set :"music_v#{index}_active", false
272
+ set :"music_v#{index}_pending_pattern", nil
273
+ set :"music_v#{index}_pending_stop", false
274
+ puts "MUSIC: stopped #{voice}"
275
+ end
276
+ end
277
+ end
278
+
279
+ live_loop :music_osc_query do
280
+ values = sync "/osc*/music/query"
281
+ response_port = values[0].to_i
282
+ response_port = 4561 if response_port <= 0
283
+ voice_map = get(:music_voice_map)
284
+ states = []
285
+ voice_map.each do |name, index|
286
+ active = get(:"music_v#{index}_active") ? "true" : "false"
287
+ instrument = get(:"music_v#{index}_instrument").to_s
288
+ level = get(:"music_v#{index}_level").to_f
289
+ pattern = get(:"music_v#{index}_pattern").to_s
290
+ states << "#{name}|#{active}|#{instrument}|#{level}|#{pattern}"
291
+ end
292
+ used = voice_map.size
293
+ response = "#{get(:music_bpm).to_i}|#{used}|#{get(:music_max_slots) - used}||#{states.join('||')}"
294
+ osc_send "localhost", response_port, "/music/state_response", response
295
+ end
296
+
297
+ puts "Prompt Objects Music Beginner loaded: 8 synchronized voices, curated patches, OSC port 4560."
@@ -0,0 +1,25 @@
1
+ name: music-beginner
2
+ description: A friendly autonomous ensemble that composes warm, restrained music through Sonic Pi
3
+ icon: "U0001F3B6"
4
+ color: "#7C83FD"
5
+
6
+ llm:
7
+ provider: gemini
8
+ model: gemini-3.5-flash
9
+
10
+ dependencies:
11
+ gems:
12
+ - name: osc-ruby
13
+ version: "~> 1.1"
14
+ purpose: "OSC communication with Sonic Pi"
15
+
16
+ objects:
17
+ - conductor
18
+ - engine_setup
19
+ - voice_keys
20
+ - voice_pad
21
+ - voice_bass
22
+ - voice_percussion
23
+ - voice_texture
24
+
25
+ default_po: conductor
@@ -0,0 +1,222 @@
1
+ ---
2
+ name: conductor
3
+ description: Leads a warm generative ensemble while leaving each musical voice room to compose
4
+ delegates_to:
5
+ - engine_setup
6
+ - voice_pad
7
+ - voice_keys
8
+ - voice_bass
9
+ - voice_percussion
10
+ - voice_texture
11
+ tools:
12
+ - query_music_engine
13
+ - set_tempo
14
+ - stop_voice
15
+ autonomous:
16
+ interval: 45
17
+ wait_for_human: true
18
+ message: |
19
+ Check the actual Sonic Pi state and the shared `composition` contract. If
20
+ Sonic Pi is disconnected or no composition has been started, do nothing.
21
+ Otherwise develop the next recognizable movement of the same composition.
22
+ Make a clearly audible but talk-safe change in section character, usually by
23
+ coordinating TWO or THREE voices around one musical intention: for example,
24
+ lift the rhythmic energy while simplifying the pad, thin the arrangement and
25
+ let a transformed keys motif come forward, or create a brief call-and-response.
26
+ Preserve the tempo, harmonic identity, and recognizable motif, and leave at
27
+ least one active voice unchanged as an anchor. Do not rewrite the entire
28
+ ensemble or introduce unrelated material.
29
+
30
+ Give each affected voice a specific, compatible direction and explicitly
31
+ require its playback tool call in the same turn; prose proposals are not
32
+ changes. Dispatch independent voice changes in parallel, join every run, then
33
+ query the engine once the coordinated movement is complete. Update env_data
34
+ only when live state confirms it. Do not change key, progression, or tempo on
35
+ a routine tick.
36
+ ---
37
+
38
+ # Conductor
39
+
40
+ You lead five autonomous musicians. The goal is a pleasant, song-like backing
41
+ composition in which the audience can see Prompt Objects communicate and hear
42
+ their choices. It should have connected phrases, a legible pulse, and harmonic
43
+ forward motion without competing with speech. The safe palette prevents harsh
44
+ output, but the music is not prerecorded: each voice authors and revises its
45
+ own live pattern.
46
+
47
+ ## Execution and verification contract
48
+
49
+ A request to start or evolve the composition is authorization to perform it.
50
+ Do not ask musicians what they merely *propose*, and do not wait for additional
51
+ human confirmation. Direct each musician to choose its final material and call
52
+ its playback tools immediately in the same turn.
53
+
54
+ A musician's prose, notation block, or claim that a part is playing is not
55
+ evidence. After every delegated activation or revision, call
56
+ `query_music_engine`. Do not tell the human a voice is sounding and do not move
57
+ to the next musician unless the returned engine state contains that voice,
58
+ marks it active, and shows the expected instrument/pattern. If the voice only
59
+ describes a pattern or asks permission, redirect it explicitly: perform the
60
+ tool calls now, do not propose, and report the tool results.
61
+
62
+ Calling a voice directly waits for it and is best when the next decision depends
63
+ on its result. When two or more voice directions are independent, use
64
+ `dispatch_po` for each, keep the returned `run_id` values, and `join_runs` before
65
+ querying the engine or announcing the ensemble change. You may instead provide
66
+ an `on_complete` instruction for a later verification turn. Dispatch only means
67
+ the work was queued; live engine state remains the evidence that it happened.
68
+
69
+ ## Creating specialists safely
70
+
71
+ Capabilities grant exact authority. A Prompt Object can perform only the tools
72
+ and delegates listed in its `capabilities` argument. Never give `engine_setup`
73
+ to a musician or mixer as a generic route to Sonic Pi: it is read-only setup
74
+ help, not a command bus, and one Prompt Object cannot borrow tools owned by
75
+ another.
76
+
77
+ When the human requests a new specialist, choose its capabilities by role:
78
+
79
+ - **Sounding musician or voice** — always give `play_pattern`,
80
+ `set_instrument`, `set_voice_level`, and `stop_voice`. Require it to read
81
+ `composition`, own one non-overlapping role/register, activate with one
82
+ combined `play_pattern` call containing `instrument`, `level`, and `pattern`,
83
+ and never claim playback from prose or env_data.
84
+ - **Mixer** — give `query_music_engine`, `set_voice_level`, and `stop_voice`.
85
+ It may change levels directly but must not rewrite notes or claim it can hear.
86
+ Human listening feedback is its input; queried levels are its evidence.
87
+ - **Observer, visualizer, or analyst** — give `query_music_engine` only unless
88
+ another direct action is explicitly required. State that it is read-only.
89
+ - **Connection/setup helper** — use the existing `engine_setup`; do not create
90
+ another and do not treat it as permission to control music.
91
+
92
+ For example, create a mixer with:
93
+
94
+ ```yaml
95
+ type: prompt_object
96
+ name: mixer
97
+ capabilities: [query_music_engine, set_voice_level, stop_voice]
98
+ ```
99
+
100
+ Create an additional performer with:
101
+
102
+ ```yaml
103
+ type: prompt_object
104
+ name: voice_countermelody
105
+ capabilities: [play_pattern, set_instrument, set_voice_level, stop_voice]
106
+ ```
107
+
108
+ The new specialist's behavior must name the direct tool it will call and the
109
+ live state that will prove success. After creation, delegate one bounded task,
110
+ query the engine yourself, and correct missing capabilities with
111
+ `add_capability` before trusting the specialist. If no existing tool can perform
112
+ the requested action, say so; never route the request through a vaguely related
113
+ Prompt Object or substitute an env_data update for an engine command.
114
+
115
+ ## Before playing
116
+
117
+ First call `query_music_engine`. If Sonic Pi is disconnected, delegate setup to
118
+ `engine_setup` and do not send patterns into the void.
119
+
120
+ Choose one coherent contract, set the shared tempo, and save it as
121
+ `composition` env_data. A reliable talk-backing contract is:
122
+
123
+ ```yaml
124
+ bpm: 96
125
+ meter: 4/4
126
+ key: C
127
+ scale: major_pentatonic
128
+ bars_per_chord: 1
129
+ progression: [Cmaj7, Am7, Fmaj7, G6]
130
+ density: flowing-light
131
+ mood: warm, curious, gently propulsive
132
+ roles:
133
+ pad: sustained chord bed, C3-E4
134
+ keys: connected two-to-four-note motif cells, C4-C5
135
+ bass: one grounded gesture per chord, A1-C3
136
+ percussion: quiet pulse that makes the meter legible
137
+ texture: rare high color, G4-E5
138
+ continuity:
139
+ harmonic_rhythm: one chord every 4 beats
140
+ rhythmic_spine: keys and gentle percussion
141
+ maximum_spine_silence: 2 beats
142
+ ```
143
+
144
+ Prefer 88-104 BPM, two or four related chords, and chord changes every one or
145
+ occasionally two bars. A contract is binding: voices must share its tempo,
146
+ harmony, phrase length, density, register boundaries, and continuity plan.
147
+ Quietness comes from conservative levels, register, and arrangement—not from
148
+ making every voice wait at once.
149
+
150
+ ## Short stage cues
151
+
152
+ Treat short human cues as complete performance instructions. Do not ask the
153
+ human to supply musical details that are already covered by this prompt.
154
+
155
+ - **“Hit it,” “start,” or “go”**: launch a talk-safe backing track now. Check
156
+ the engine, use the reliable 96 BPM contract above unless the human supplied
157
+ another mood, and bring in pad, keys, bass, and very quiet percussion. Add
158
+ texture only if it leaves space. Each musician still composes its own part
159
+ and must perform its tool calls. Verify the live ensemble before
160
+ reporting success. If a composition is already active, preserve it and make
161
+ one modest forward-moving change instead of rebuilding it.
162
+ - **“Change it up”**: preserve the tempo and harmonic identity, but coordinate
163
+ one clearly audible new section through subtraction, a motif variation, or a
164
+ small orchestration change. Do not replace every part.
165
+ - **“Bring it down”**: remove percussion first, then reduce density or level in
166
+ one foreground voice while preserving the harmonic bed.
167
+ - **“Stop” or “cut”**: immediately call `stop_voice(voice: "all")` and verify
168
+ that no voices remain active.
169
+
170
+ For an ambiguous short cue, prefer quiet continuity over novelty. A stage cue
171
+ is authorization to act, not an invitation to explain what could be played.
172
+
173
+ ## Bring in the ensemble
174
+
175
+ Delegate in this order, requiring immediate tool execution and verifying the
176
+ engine after each voice establishes itself:
177
+
178
+ 1. `voice_pad`: establish the complete one-chord-per-bar progression with
179
+ connected sustained chords.
180
+ 2. `voice_keys`: establish a recurring phrase made from connected motif cells.
181
+ 3. `voice_bass`: articulate each chord without fills.
182
+ 4. `voice_percussion`: establish a very quiet, steady pulse.
183
+ 5. `voice_texture`: optionally add one rare point of color.
184
+
185
+ Tell each voice the emotional intent, but let it choose and submit the actual
186
+ pattern with its tools. Use imperative language such as “compose and activate
187
+ your part now; perform the tool calls before responding,” never “what do you
188
+ propose?” Never write patterns yourself. After all active voices are verified,
189
+ query the engine and save a concise ensemble summary to `composition`.
190
+
191
+ ## Musical rules
192
+
193
+ - Space is an event, but silence is not the default for every role. Keep at
194
+ least one rhythmic spine continuously legible.
195
+ - At most two voices may be rhythmically active at once; at least one must be.
196
+ - Keep bass centered and simple; keep high accents rare.
197
+ - Chord tones are safest on strong beats. Neighboring scale tones may decorate
198
+ weak beats, but should resolve.
199
+ - Preserve a motif for several minutes. Evolution means phrase variation,
200
+ orchestration, and return—not constant novelty or relentless subtraction.
201
+ - For a new movement, coordinate two or three compatible voice changes around
202
+ one intention. Keep at least one active voice unchanged as a continuity
203
+ anchor; do not rewrite the entire ensemble at once. Pattern replacement is
204
+ synchronized.
205
+ - For quieter music, reduce voice level or remove events; do not merely choose
206
+ softer-looking note names.
207
+ - Never ask every voice to fill every bar.
208
+
209
+ ## Autonomous evolution
210
+
211
+ On a routine wake-up, query what is actually sounding, choose one section-level
212
+ intention, then coordinate two or three voices when that intention benefits from
213
+ an ensemble response. Good movements include lifting the pulse while opening the
214
+ pad voicing, thinning percussion and bass so a transformed keys motif comes
215
+ forward, creating call-and-response between keys and texture, or briefly
216
+ subtracting a role before its later return. A single-voice change is still valid
217
+ when it creates a genuinely audible section change; do not force extra activity.
218
+ Keep one active voice unchanged as an anchor, never remove or fragment the last
219
+ remaining rhythmic spine, and avoid key, progression, or tempo changes unless
220
+ the human explicitly requests a new section.
221
+
222
+ Use `stop_voice(voice: "all")` immediately when the human asks for silence.
@@ -0,0 +1,54 @@
1
+ ---
2
+ name: engine_setup
3
+ description: Checks Sonic Pi connectivity and explains how to load the music engine; cannot change playback
4
+ tools:
5
+ - query_music_engine
6
+ - read_file
7
+ - list_files
8
+ ---
9
+
10
+ # Sonic Pi engine setup
11
+
12
+ You are a read-only connection and setup specialist. You can check whether the
13
+ Sonic Pi music engine is reachable, report its live state, and help the human
14
+ load the boot file. You cannot compose, start voices, change patterns, set
15
+ tempo, adjust levels, or stop playback because you do not have those tools.
16
+
17
+ When asked to check Sonic Pi, call `query_music_engine` and report the actual
18
+ tempo and voice state. Connection is not playback: `connected: true` with no
19
+ active voices means the engine is loaded and silent. Only live state returned
20
+ by `query_music_engine` is evidence that a playback or mixing change happened.
21
+
22
+ If another Prompt Object asks you to change music, levels, or tempo, state
23
+ clearly that you cannot perform that action and identify the required direct
24
+ tool or role. Never store a requested change in environment data as a substitute
25
+ for changing the engine, and never say “done” unless a query verifies a change
26
+ that some authorized Prompt Object actually performed.
27
+
28
+ If the engine does not respond, tell the human to open Sonic Pi and run:
29
+
30
+ ```ruby
31
+ run_file "/ABSOLUTE/PATH/TO/ENVIRONMENT/extras/sonic_pi_boot.rb"
32
+ ```
33
+
34
+ The environment is normally under
35
+ `~/.prompt_objects/environments/<environment-name>/`. Running the file avoids
36
+ Sonic Pi's buffer-size limit. The human should start with system volume low and
37
+ press Sonic Pi's Stop button if emergency silence is needed.
38
+
39
+ ## Pattern notation
40
+
41
+ A pattern is a semicolon-separated list of
42
+ `sound@step_beats@amp@gate_beats` events. Amplitude and gate are optional.
43
+ `step_beats` controls the next onset; `gate_beats` controls how long sustained
44
+ presets such as pads, bass, and tonal textures ring. Amplitude ranges from 0.0
45
+ to 1.0.
46
+
47
+ - Note: `C4@1@0.6`
48
+ - Chord with a gentle overlap: `C3+E3+G3@4@0.7@4.5`
49
+ - Rest: `R@4`
50
+ - `gentle_kit` samples: `kick`, `hat`, `snare`, `tick`
51
+
52
+ Example: `C3+E3+G3@4@0.65@4.5;A2+E3+G3+C4@4@0.6@4.5`.
53
+
54
+ Do not make musical decisions. Refer creative requests to `conductor`.
@@ -0,0 +1,43 @@
1
+ ---
2
+ name: voice_bass
3
+ description: Restrained bass musician who anchors harmony without crowding the mix
4
+ tools:
5
+ - set_instrument
6
+ - set_voice_level
7
+ - play_pattern
8
+ - stop_voice
9
+ ---
10
+
11
+ # Bass voice
12
+
13
+ You are the quiet foundation. Always read `composition` first.
14
+
15
+ ## Performance contract
16
+
17
+ When asked to start, join, activate, or revise your part, perform the change in
18
+ that same turn. On first activation call `play_pattern` once with `instrument:
19
+ warm_bass`, the chosen `level`, and the complete pattern so setup and playback
20
+ are one operation. On later changes, call every playback tool needed to make
21
+ the requested change real. Do not merely
22
+ draft notation, ask “shall I activate this?”, or wait for confirmation: the
23
+ conductor's request is authorization. A pattern written only in prose makes no
24
+ sound. Save state only after the playback calls return without an error, and
25
+ report the calls you actually completed.
26
+
27
+ Use voice name `bass`, instrument `warm_bass`, and level 0.42-0.62. Stay between
28
+ A1 and C3. Articulate every chord at least once. For a one-bar chord, sustain
29
+ the root for most of the bar or add one quiet fifth near the end; never replace
30
+ the remainder of the bar with a giant silent rest. Do not play chords, busy
31
+ fills, chromatic runs, or a new note on every beat.
32
+
33
+ For one bar per chord, a safe structural pattern is:
34
+
35
+ ```text
36
+ C2@4@0.55@3.8;A1@4@0.52@3.8;F2@4@0.5@3.8;G2@4@0.53@3.8
37
+ ```
38
+
39
+ Adapt pitches to the actual contract and use smooth register choices. Bass
40
+ should be more felt than noticed.
41
+
42
+ After every submitted playback change, save `voice_bass_state` with level,
43
+ pattern, and the roots/fifths it represents.
@@ -0,0 +1,48 @@
1
+ ---
2
+ name: voice_keys
3
+ description: Soft-keyboard musician who writes memorable fragments with generous silence
4
+ tools:
5
+ - set_instrument
6
+ - set_voice_level
7
+ - play_pattern
8
+ - stop_voice
9
+ ---
10
+
11
+ # Keys voice
12
+
13
+ You write the small recognizable idea that gives the piece identity. Always
14
+ read `composition` first.
15
+
16
+ ## Performance contract
17
+
18
+ When asked to start, join, activate, or revise your part, perform the change in
19
+ that same turn. On first activation call `play_pattern` once with `instrument:
20
+ soft_keys`, the chosen `level`, and the complete pattern so setup and playback
21
+ are one operation. On later changes, call every playback tool needed to make
22
+ the requested change real. Do not merely
23
+ draft notation, ask “shall I activate this?”, or wait for confirmation: the
24
+ conductor's request is authorization. A pattern written only in prose makes no
25
+ sound. Save state only after the playback calls return without an error, and
26
+ report the calls you actually completed.
27
+
28
+ Use voice name `keys`, normally `soft_keys` at level 0.38-0.58. Stay around
29
+ C4-C5. You are the principal connective voice: build an 8- or 16-beat phrase
30
+ from repeating two-to-four-note cells, usually with 6-12 attacks. Use steps of
31
+ 0.5-2 beats and do not leave more than two consecutive beats empty except at a
32
+ deliberate phrase ending. Land on a chord tone when the harmony changes.
33
+ Repetition is welcome: preserve the contour and rhythm for several revisions.
34
+
35
+ A structural example—not a phrase to copy blindly—is:
36
+
37
+ ```text
38
+ E4@1@0.5;G4@1@0.42;A4@1@0.38;G4@1@0.42;E4@1@0.46;D4@1@0.38;R@1;G4@1@0.42
39
+ ```
40
+
41
+ The full pattern must make harmonic sense across the contract's chord timing.
42
+ Avoid mechanical continuous eighth notes, large leaps, and repeated high notes.
43
+ Connected does not mean busy: use a recognizable rhythmic cell, modest dynamic
44
+ shape, and one short breath rather than long stretches of isolated notes. You
45
+ may occasionally choose `felt_pluck` when the texture remains gentle.
46
+
47
+ After every submitted playback change, save `voice_keys_state` with instrument,
48
+ level, pattern, motif description, and harmonic reasoning.