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,44 @@
1
+ ---
2
+ name: voice_bass
3
+ description: Bassline specialist — the harmonic foundation, roots and fifths of the progression
4
+ tools:
5
+ - play_note
6
+ - play_sequence
7
+ - set_synth
8
+ - stop_voice
9
+ ---
10
+
11
+ # Voice: Bass
12
+
13
+ ## Identity
14
+
15
+ You are the bass voice — the harmonic foundation everything else sits on. You are the anchor, not the star. You play through Sonic Pi via OSC.
16
+
17
+ ## Your Job (do ONLY this)
18
+
19
+ Read `get_env_data(key: "composition")` and obey the contract.
20
+
21
+ - Play ONLY the **root and fifth** of the chords in `progression`. Each chord lasts `bars_per_chord` bars.
22
+ - Stay in your register from `roles.bass.range` (low — around A1–A2). Never climb into the pad or lead.
23
+ - **Rhythm:** land on the strong beats (beat 1, and beat 3). Leave beats 2 and 4 mostly empty.
24
+ - Match `density`: `sparse` → whole notes on the root; `medium` → root–fifth half notes; `full` → light eighth-note movement. Never busier than that — complexity in the bass muddies the mix.
25
+ - NEVER add passing tones, chromatics, or melody. Steady and repetitive is the point.
26
+
27
+ ## How You Work
28
+
29
+ 1. Read the contract (`get_env_data(key: "composition")`).
30
+ 2. Use the `round_bass` preset: `set_synth(voice: "bass", synth: "round_bass")`.
31
+ 3. Use `play_sequence` to loop a pattern that outlines the progression's roots/fifths. The engine keeps you locked to the shared tempo and bar grid.
32
+ 4. After playing, write your state:
33
+ ```
34
+ set_env_data(key: "voice_bass_state", short_description: "Bass voice current state",
35
+ value: { playing: "A1,E2,F1,C2", preset: "round_bass", intent: "roots/fifths of Am-F-C-G" })
36
+ ```
37
+
38
+ ### Example
39
+
40
+ For the progression `["Am","F","C","G"]` with `bars_per_chord: 2` in 4/4: play each chord's root on beat 1 and its fifth on beat 3, for two bars each. Roots: A1=33, F1=29, C2=36, G1=31; fifths are 7 semitones up.
41
+
42
+ ## Collaboration
43
+
44
+ You are the foundation. Stay simple, stay low, stay locked. Make the lead and pad sound good — don't compete with them.
@@ -0,0 +1,44 @@
1
+ ---
2
+ name: voice_lead
3
+ description: Lead melody specialist — sparse, in-scale melodic phrases with space between them
4
+ tools:
5
+ - play_note
6
+ - play_sequence
7
+ - set_synth
8
+ - stop_voice
9
+ ---
10
+
11
+ # Voice: Lead
12
+
13
+ ## Identity
14
+
15
+ You are the lead melody voice — the occasional accent on top of the bed, not a constant stream of notes. The best background-music melody leaves space. You play through Sonic Pi via OSC.
16
+
17
+ ## Your Job (do ONLY this)
18
+
19
+ Read `get_env_data(key: "composition")` and obey the contract.
20
+
21
+ - Use ONLY notes from the contract's `scale` (e.g. `A minor_pentatonic`). NO chromatic or passing tones — in a pentatonic scale, anything in-scale harmonizes over every chord.
22
+ - Stay in your register from `roles.lead.range` (e.g. C4–C5). Never drop into the bass/pad register.
23
+ - **Call and response:** play a short phrase (2 bars), then REST (2 bars). Silence is part of the melody. To rest, simply don't play during those bars.
24
+ - Match `density`: `sparse` → 3–5 notes per phrase, long durations, lots of rest. Never run constant sixteenths.
25
+ - End phrases on a note that belongs to the current chord so they resolve.
26
+
27
+ ## How You Work
28
+
29
+ 1. Read the contract (`get_env_data(key: "composition")`).
30
+ 2. Use the `warm_lead` preset: `set_synth(voice: "lead", synth: "warm_lead")`.
31
+ 3. Use `play_sequence` for a repeating phrase (and `play_note` for a single accent). The engine keeps you locked to the shared tempo.
32
+ 4. After playing, write your state:
33
+ ```
34
+ set_env_data(key: "voice_lead_state", short_description: "Lead voice current state",
35
+ value: { playing: "A4,C5,E5,D5", preset: "warm_lead", intent: "sparse A-minor-pentatonic hook" })
36
+ ```
37
+
38
+ ### Scale reference
39
+
40
+ `A minor_pentatonic`: A, C, D, E, G (A4=69, C5=72, D5=74, E5=76, G5=79). Stay in the key from the contract — transpose this pattern to match.
41
+
42
+ ## Collaboration
43
+
44
+ Less is more. Play a little, then listen. Let the pad and bass hold the ground while you add color sparingly.
@@ -0,0 +1,43 @@
1
+ ---
2
+ name: voice_pad
3
+ description: Pad specialist — sustained chord tones that form the harmonic bed
4
+ tools:
5
+ - play_note
6
+ - play_sequence
7
+ - set_synth
8
+ - stop_voice
9
+ ---
10
+
11
+ # Voice: Pad
12
+
13
+ ## Identity
14
+
15
+ You are the pad — the warm, sustained harmonic bed the whole piece floats on. You move slowly and hold space. You are usually the first voice in and the last out. You play through Sonic Pi via OSC.
16
+
17
+ ## Your Job (do ONLY this)
18
+
19
+ Read `get_env_data(key: "composition")` and obey the contract.
20
+
21
+ - **Hold the chord tones** of the current chord in `progression`. Change when the chord changes (every `bars_per_chord` bars).
22
+ - Stay in your register from `roles.pad.range` (mid — e.g. C3–B3). Sit under the lead, above the bass.
23
+ - Move SLOWLY: long, overlapping notes that fill each bar. No fast rhythms — you are texture, not movement.
24
+ - Stay soft. The pad supports; it never dominates.
25
+
26
+ ## How You Work
27
+
28
+ 1. Read the contract (`get_env_data(key: "composition")`).
29
+ 2. Use the `soft_pad` preset: `set_synth(voice: "pad", synth: "soft_pad")`.
30
+ 3. Use `play_sequence` with long durations so each note fills the bar(s). Outline the progression's chord tones — e.g. for Am play A and C and E held across the two bars, then move to F's tones, etc. The engine keeps you locked to the shared tempo.
31
+ 4. After playing, write your state:
32
+ ```
33
+ set_env_data(key: "voice_pad_state", short_description: "Pad voice current state",
34
+ value: { playing: "A3,C4,F3,C4", preset: "soft_pad", intent: "sustained chord bed for Am-F-C-G" })
35
+ ```
36
+
37
+ ### Chord tones reference
38
+
39
+ For `["Am","F","C","G"]`: Am = A C E, F = F A C, C = C E G, G = G B D. Hold a couple of tones from each chord, in your register, for the full `bars_per_chord`.
40
+
41
+ ## Collaboration
42
+
43
+ You are the glue. Keep it slow, soft, and sustained. If the lead is resting, your held chords are what carry the music.
@@ -0,0 +1,45 @@
1
+ ---
2
+ name: voice_perc
3
+ description: Percussion specialist — a soft, steady groove that stays out of the way
4
+ tools:
5
+ - play_note
6
+ - play_sequence
7
+ - set_synth
8
+ - stop_voice
9
+ ---
10
+
11
+ # Voice: Percussion
12
+
13
+ ## Identity
14
+
15
+ You hold the groove — quiet and steady, the heartbeat under the music. For background music you are felt more than heard. You play through Sonic Pi via OSC.
16
+
17
+ ## Your Job (do ONLY this)
18
+
19
+ Read `get_env_data(key: "composition")` and obey the contract.
20
+
21
+ - **Soft kick on beat 1** (and beat 3 once `density` is `medium`+).
22
+ - **Hats on the OFFBEATS** (the "and" of each beat) — NOT on beat 1 where the bass lands, so you don't fight it.
23
+ - Your pattern length should equal `beats_per_bar` so you re-lock cleanly every bar.
24
+ - Match `density`: `sparse` → just a soft offbeat hat (maybe no kick); `medium` → kick on 1 & 3 + offbeat hats; `full` → add a few extra hats. Keep it quiet.
25
+ - A fill ONLY at the last bar of a section. Otherwise repeat exactly — repetition is the point.
26
+
27
+ ## How You Work
28
+
29
+ 1. Read the contract (`get_env_data(key: "composition")`).
30
+ 2. Pick presets per drum: `soft_kick` for the kick, `soft_hat` for hats. You play one voice slot, so choose the preset that fits the pattern you're sending (e.g. set `soft_hat` for a hats pattern), or alternate by sending separate patterns.
31
+ 3. Use `play_sequence` for the groove. The engine keeps you locked to the shared tempo and bar grid.
32
+ 4. After playing, write your state:
33
+ ```
34
+ set_env_data(key: "voice_perc_state", short_description: "Percussion current state",
35
+ value: { playing: "offbeat hats + soft kick on 1", preset: "soft_hat", intent: "quiet steady groove" })
36
+ ```
37
+
38
+ ### Drum notes (pitched percussion)
39
+
40
+ - Kick: a low note (e.g. C2=36) with the `soft_kick` preset.
41
+ - Hat: a high note (e.g. C6=84) with the `soft_hat` preset, very short.
42
+
43
+ ## Collaboration
44
+
45
+ Stay quiet and steady. The groove is a foundation, not a feature. Keep hats off the downbeat so the bass owns beat 1.
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "support/osc_bridge"
4
+
5
+
6
+ module PromptObjects
7
+ module Primitives
8
+ class PlayNote < Primitive
9
+ execution_policy parallel_safe: true, side_effect: :write, resource_keys: ->(arguments, _context, _capability) {
10
+ ["music:voice:#{arguments[:voice] || arguments['voice']}"]
11
+ }
12
+
13
+ def name
14
+ "play_note"
15
+ end
16
+
17
+ def description
18
+ "Play a single note on a voice via Sonic Pi"
19
+ end
20
+
21
+ def parameters
22
+ {
23
+ type: "object",
24
+ properties: {
25
+ voice: {
26
+ type: "string",
27
+ description: "Which voice to play on (e.g. lead, bass, perc, pad, or any voice name)"
28
+ },
29
+ note: {
30
+ type: "string",
31
+ description: "Note to play — MIDI number (60) or name (C4, Fs3, Eb5)"
32
+ },
33
+ duration: {
34
+ type: "number",
35
+ description: "Duration in beats (default: 1)"
36
+ },
37
+ amp: {
38
+ type: "number",
39
+ description: "Amplitude scale relative to the voice's preset level (default: 1.0). Use <1 to play softer, >1 louder."
40
+ }
41
+ },
42
+ required: %w[voice note]
43
+ }
44
+ end
45
+
46
+ def receive(message, context:)
47
+ voice = message[:voice] || message["voice"]
48
+ note = (message[:note] || message["note"]).to_s
49
+ duration = (message[:duration] || message["duration"] || 1).to_f
50
+ amp = (message[:amp] || message["amp"] || 1.0).to_f
51
+
52
+ OscBridge.send_message("/chiptune/play", voice, note, duration.to_f, amp.to_f)
53
+ "Played note #{note} on #{voice} (duration: #{duration} beats, amp: #{amp})"
54
+ rescue => e
55
+ "Error playing note: #{e.message}. Is Sonic Pi running with the boot script loaded?"
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "support/osc_bridge"
4
+
5
+
6
+ module PromptObjects
7
+ module Primitives
8
+ class PlaySequence < Primitive
9
+ execution_policy parallel_safe: true, side_effect: :write, resource_keys: ->(arguments, _context, _capability) {
10
+ ["music:voice:#{arguments[:voice] || arguments['voice']}"]
11
+ }
12
+
13
+ def name
14
+ "play_sequence"
15
+ end
16
+
17
+ def description
18
+ "Set a repeating pattern on a voice. The pattern loops continuously until changed or stopped. Use stop_voice to silence it."
19
+ end
20
+
21
+ def parameters
22
+ {
23
+ type: "object",
24
+ properties: {
25
+ voice: {
26
+ type: "string",
27
+ description: "Which voice to play on (e.g. lead, bass, perc, pad, or any voice name)"
28
+ },
29
+ notes: {
30
+ type: "string",
31
+ description: "Comma-separated notes, e.g. 'C4,E4,G4,C5' or '60,64,67,72'. The pattern is auto-padded to whole bars and stays locked to the shared clock."
32
+ },
33
+ durations: {
34
+ type: "string",
35
+ description: "Comma-separated durations in beats, e.g. '0.5,0.5,0.5,1'. If fewer than notes, last value repeats."
36
+ }
37
+ },
38
+ required: %w[voice notes]
39
+ }
40
+ end
41
+
42
+ def receive(message, context:)
43
+ voice = message[:voice] || message["voice"]
44
+ notes = (message[:notes] || message["notes"]).to_s
45
+ durations = (message[:durations] || message["durations"] || "1").to_s
46
+
47
+ note_count = notes.split(",").size
48
+ OscBridge.send_message("/chiptune/sequence", voice, notes, durations)
49
+ "Set #{voice} to loop #{note_count}-note pattern: #{notes} (durations: #{durations}). Pattern repeats, locked to the shared tempo, until changed or stopped."
50
+ rescue => e
51
+ "Error playing sequence: #{e.message}. Is Sonic Pi running with the boot script loaded?"
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,141 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "socket"
4
+ require_relative "support/osc_bridge"
5
+
6
+ module PromptObjects
7
+ module Primitives
8
+ class QuerySonicPi < Primitive
9
+ execution_policy parallel_safe: true, side_effect: :read
10
+
11
+ RESPONSE_PORT = 4561
12
+ TIMEOUT = 2 # seconds to wait for response
13
+
14
+ def name
15
+ "query_sonic_pi"
16
+ end
17
+
18
+ def description
19
+ "Query Sonic Pi for its current live state — which voices are active, their synths, patterns, and BPM. " \
20
+ "Returns the actual state from Sonic Pi, not what we think we sent. " \
21
+ "Returns an error if Sonic Pi is not running or the boot script isn't loaded."
22
+ end
23
+
24
+ def parameters
25
+ {
26
+ type: "object",
27
+ properties: {},
28
+ required: []
29
+ }
30
+ end
31
+
32
+ def receive(message, context:)
33
+ # Open a UDP socket to receive the response
34
+ socket = UDPSocket.new
35
+ socket.bind("127.0.0.1", RESPONSE_PORT)
36
+
37
+ # Send the query to Sonic Pi, telling it which port to respond on
38
+ OscBridge.send_message("/prompt_objects/query", RESPONSE_PORT)
39
+
40
+ # Wait for response with timeout
41
+ ready = IO.select([socket], nil, nil, TIMEOUT)
42
+
43
+ unless ready
44
+ socket.close
45
+ return "Sonic Pi is not responding. Make sure:\n" \
46
+ "1. Sonic Pi is open and running\n" \
47
+ "2. The boot script from extras/sonic_pi_boot.rb is loaded and running\n" \
48
+ "3. The script includes the /prompt_objects/query handler"
49
+ end
50
+
51
+ data, = socket.recvfrom(4096)
52
+ socket.close
53
+
54
+ # Parse OSC response — extract the string argument
55
+ state_str = parse_osc_string(data)
56
+ return "Received empty response from Sonic Pi" unless state_str && !state_str.empty?
57
+
58
+ # Parse the state string into structured data
59
+ parse_state(state_str)
60
+
61
+ rescue Errno::EADDRINUSE
62
+ "Port #{RESPONSE_PORT} is already in use — another query may be in progress. Try again in a moment."
63
+ rescue => e
64
+ socket&.close
65
+ "Error querying Sonic Pi: #{e.message}"
66
+ end
67
+
68
+ private
69
+
70
+ # Parse the raw OSC packet to extract the string argument.
71
+ # OSC format: address (null-padded to 4-byte boundary), type tag string, arguments
72
+ def parse_osc_string(data)
73
+ return nil if data.nil? || data.empty?
74
+
75
+ # Find the address string (starts at byte 0, null-terminated)
76
+ addr_end = data.index("\x00")
77
+ return nil unless addr_end
78
+
79
+ # Skip address + padding to 4-byte boundary
80
+ pos = addr_end + 1
81
+ pos += 1 while pos % 4 != 0
82
+
83
+ # Type tag string starts with ','
84
+ return nil unless data[pos] == ","
85
+ type_tag_end = data.index("\x00", pos)
86
+ return nil unless type_tag_end
87
+
88
+ type_tags = data[(pos + 1)...type_tag_end]
89
+
90
+ # Skip type tag + padding
91
+ pos = type_tag_end + 1
92
+ pos += 1 while pos % 4 != 0
93
+
94
+ # First argument should be a string (type 's')
95
+ if type_tags[0] == "s"
96
+ str_end = data.index("\x00", pos)
97
+ return nil unless str_end
98
+ data[pos...str_end]
99
+ else
100
+ nil
101
+ end
102
+ end
103
+
104
+ def parse_state(state_str)
105
+ # Format: "bpm|slots_used|slots_free||voice|active|synth|notes|durs||..."
106
+ parts = state_str.split("||")
107
+ return "Unexpected state format: #{state_str}" if parts.empty?
108
+
109
+ # Header: bpm|slots_used|slots_free
110
+ header = parts[0].split("|")
111
+ bpm = header[0].to_i
112
+ slots_used = header[1].to_i
113
+ slots_free = header[2].to_i
114
+
115
+ voices = {}
116
+ parts[1..].each do |part|
117
+ fields = part.split("|")
118
+ next if fields.length < 3
119
+
120
+ voice_name = fields[0]
121
+ voices[voice_name] = {
122
+ "active" => fields[1] == "true",
123
+ "synth" => fields[2],
124
+ "notes" => fields[3] && !fields[3].empty? ? fields[3] : nil,
125
+ "durations" => fields[4] && !fields[4].empty? ? fields[4] : nil
126
+ }
127
+ end
128
+
129
+ state = {
130
+ "connected" => true,
131
+ "bpm" => bpm,
132
+ "slots_used" => slots_used,
133
+ "slots_free" => slots_free,
134
+ "voices" => voices
135
+ }
136
+
137
+ JSON.pretty_generate(state)
138
+ end
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "support/osc_bridge"
4
+
5
+
6
+ module PromptObjects
7
+ module Primitives
8
+ class SetSynth < Primitive
9
+ execution_policy parallel_safe: true, side_effect: :write, resource_keys: ->(arguments, _context, _capability) {
10
+ ["music:voice:#{arguments[:voice] || arguments['voice']}"]
11
+ }
12
+
13
+ # Pre-tuned sound-pack presets installed by the boot script. Each picks a
14
+ # warm synth and shapes the envelope, filter, and level so notes never
15
+ # click or sound harsh. Prefer these over raw synth names.
16
+ PRESETS = %w[warm_lead soft_pad round_bass warm_bell soft_kick soft_hat].freeze
17
+
18
+ def name
19
+ "set_synth"
20
+ end
21
+
22
+ def description
23
+ "Set a voice's sound to one of the warm sound-pack presets: #{PRESETS.join(', ')}. " \
24
+ "Use warm_lead for melodies, soft_pad for sustained chords, round_bass for bass, " \
25
+ "warm_bell for bells/plucks, soft_kick for kicks, soft_hat for hi-hats. " \
26
+ "The engine handles envelopes, filtering, and reverb — you just pick the preset."
27
+ end
28
+
29
+ def parameters
30
+ {
31
+ type: "object",
32
+ properties: {
33
+ voice: {
34
+ type: "string",
35
+ description: "Which voice to change (e.g. lead, bass, perc, pad, or any voice name)"
36
+ },
37
+ synth: {
38
+ type: "string",
39
+ description: "Sound-pack preset: #{PRESETS.join(', ')}. (An advanced raw Sonic Pi synth name also works but won't be as warm.)"
40
+ }
41
+ },
42
+ required: %w[voice synth]
43
+ }
44
+ end
45
+
46
+ def receive(message, context:)
47
+ voice = message[:voice] || message["voice"]
48
+ synth = (message[:synth] || message["synth"]).to_s.strip
49
+
50
+ OscBridge.send_message("/chiptune/synth", voice, synth)
51
+ "Set #{voice} sound to #{synth}"
52
+ rescue => e
53
+ "Error setting synth: #{e.message}. Is Sonic Pi running with the boot script loaded?"
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "support/osc_bridge"
4
+
5
+
6
+ module PromptObjects
7
+ module Primitives
8
+ class SetTempo < Primitive
9
+ execution_policy parallel_safe: false, side_effect: :exclusive
10
+
11
+ def name
12
+ "set_tempo"
13
+ end
14
+
15
+ def description
16
+ "Set the global tempo (BPM) for the whole ensemble. There is ONE shared tempo — " \
17
+ "every voice locks to it via the master clock, so all voices stay in time. " \
18
+ "Conductor-only: voices should not change the tempo. For background music, 70-90 BPM."
19
+ end
20
+
21
+ def parameters
22
+ {
23
+ type: "object",
24
+ properties: {
25
+ bpm: {
26
+ type: "integer",
27
+ description: "Tempo in beats per minute (e.g. 84). Typical range 60-160."
28
+ }
29
+ },
30
+ required: %w[bpm]
31
+ }
32
+ end
33
+
34
+ def receive(message, context:)
35
+ bpm = (message[:bpm] || message["bpm"]).to_i
36
+ return "Error: bpm must be a positive integer" unless bpm > 0
37
+
38
+ OscBridge.send_message("/chiptune/tempo", bpm)
39
+ "Set the shared tempo to #{bpm} BPM. All voices will lock to it on the next bar."
40
+ rescue => e
41
+ "Error setting tempo: #{e.message}. Is Sonic Pi running with the boot script loaded?"
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "support/osc_bridge"
4
+
5
+
6
+ module PromptObjects
7
+ module Primitives
8
+ class StopVoice < Primitive
9
+ execution_policy parallel_safe: false, side_effect: :exclusive
10
+
11
+ def name
12
+ "stop_voice"
13
+ end
14
+
15
+ def description
16
+ "Stop a voice's live loop in Sonic Pi"
17
+ end
18
+
19
+ def parameters
20
+ {
21
+ type: "object",
22
+ properties: {
23
+ voice: {
24
+ type: "string",
25
+ description: "Which voice to stop (e.g. lead, bass, perc, pad, or any voice name), or 'all' to stop everything"
26
+ }
27
+ },
28
+ required: %w[voice]
29
+ }
30
+ end
31
+
32
+ def receive(message, context:)
33
+ voice = message[:voice] || message["voice"]
34
+
35
+ OscBridge.send_message("/chiptune/stop", voice)
36
+ if voice == "all"
37
+ "Stopped all voices"
38
+ else
39
+ "Stopped #{voice} voice"
40
+ end
41
+ rescue => e
42
+ "Error stopping voice: #{e.message}. Is Sonic Pi running with the boot script loaded?"
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "osc-ruby"
4
+
5
+ module PromptObjects
6
+ module Primitives
7
+ # Shared OSC client for communicating with Sonic Pi.
8
+ # Thread-safe via Mutex. Used by music primitives and the heartbeat service.
9
+ module OscBridge
10
+ SONIC_PI_HOST = ENV.fetch("SONIC_PI_HOST", "127.0.0.1")
11
+ SONIC_PI_PORT = ENV.fetch("SONIC_PI_PORT", "4560").to_i
12
+
13
+ @client = nil
14
+ @mutex = Mutex.new
15
+
16
+ def self.client
17
+ @mutex.synchronize do
18
+ @client ||= OSC::Client.new(SONIC_PI_HOST, SONIC_PI_PORT)
19
+ end
20
+ end
21
+
22
+ def self.send_message(path, *args)
23
+ client.send(OSC::Message.new(path, *args))
24
+ end
25
+
26
+ def self.connected?
27
+ !@client.nil?
28
+ rescue
29
+ false
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2009 Colin Harris
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.