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
@@ -1,263 +0,0 @@
1
- import { useState, useMemo } from 'react'
2
- import { useStore, usePONotifications, useEnvData } from '../store'
3
- import { useResize } from '../hooks/useResize'
4
- import { MethodList } from './MethodList'
5
- import { SourcePane } from './SourcePane'
6
- import { EnvDataPane } from './EnvDataPane'
7
- import { Workspace } from './Workspace'
8
- import { ContextMenu } from './ContextMenu'
9
- import { PaneSlot } from './PaneSlot'
10
- import type { PromptObject, CapabilityInfo } from '../types'
11
-
12
- interface InspectorProps {
13
- po: PromptObject
14
- sendMessage: (target: string, content: string, newThread?: boolean) => void
15
- createSession?: (target: string, name?: string) => void
16
- switchSession: (target: string, sessionId: string) => void
17
- createThread: (target: string) => void
18
- updatePrompt: (target: string, prompt: string) => void
19
- requestUsage?: (sessionId: string, includeTree?: boolean) => void
20
- exportThread?: (sessionId: string, format?: string) => void
21
- requestEnvData: (sessionId: string) => void
22
- }
23
-
24
- export function Inspector({
25
- po,
26
- sendMessage,
27
- switchSession,
28
- createThread,
29
- updatePrompt,
30
- requestUsage,
31
- exportThread,
32
- requestEnvData,
33
- }: InspectorProps) {
34
- const [selectedCapability, setSelectedCapability] = useState<CapabilityInfo | null>(null)
35
- const [threadMenuOpen, setThreadMenuOpen] = useState(false)
36
- const [contextMenu, setContextMenu] = useState<{ x: number; y: number; sessionId: string } | null>(null)
37
- const notifications = usePONotifications(po.name)
38
- const topPaneCollapsed = useStore((s) => s.topPaneCollapsed)
39
- const toggleTopPane = useStore((s) => s.toggleTopPane)
40
- const envDataPaneCollapsed = useStore((s) => s.envDataPaneCollapsed)
41
- const toggleEnvDataPane = useStore((s) => s.toggleEnvDataPane)
42
-
43
- const topPaneResize = useResize({
44
- direction: 'vertical',
45
- initialSize: 260,
46
- minSize: 120,
47
- maxSize: 600,
48
- })
49
-
50
- const envDataResize = useResize({
51
- direction: 'vertical',
52
- initialSize: 160,
53
- minSize: 80,
54
- maxSize: 400,
55
- })
56
-
57
- const methodListResize = useResize({
58
- direction: 'horizontal',
59
- initialSize: 192,
60
- minSize: 120,
61
- maxSize: 320,
62
- })
63
-
64
- const sessions = po.sessions || []
65
- const currentSessionId = po.current_session?.id
66
- const sessionRootMap = useStore((s) => s.sessionRootMap)
67
- const rootThreadId = currentSessionId ? sessionRootMap[currentSessionId] : undefined
68
- const envDataEntries = useEnvData(rootThreadId)
69
-
70
- // Sort sessions: current first, then by updated_at desc
71
- const sortedSessions = useMemo(() => {
72
- return [...sessions].sort((a, b) => {
73
- if (a.id === currentSessionId) return -1
74
- if (b.id === currentSessionId) return 1
75
- return (b.updated_at || '').localeCompare(a.updated_at || '')
76
- })
77
- }, [sessions, currentSessionId])
78
-
79
- const isActive = po.status !== 'idle'
80
-
81
- const statusDot = {
82
- idle: 'bg-po-status-idle',
83
- thinking: 'bg-po-status-active',
84
- calling_tool: 'bg-po-status-calling',
85
- }[po.status] || 'bg-po-status-idle'
86
-
87
- const statusGlow = {
88
- idle: '',
89
- thinking: 'shadow-[0_0_6px_rgba(212,149,42,0.7)]',
90
- calling_tool: 'shadow-[0_0_6px_rgba(59,154,110,0.7)]',
91
- }[po.status] || ''
92
-
93
- const statusLabelColor = {
94
- idle: 'text-po-text-ghost',
95
- thinking: 'text-po-status-active',
96
- calling_tool: 'text-po-status-calling',
97
- }[po.status] || 'text-po-text-ghost'
98
-
99
- const statusLabel = {
100
- idle: 'idle',
101
- thinking: 'thinking...',
102
- calling_tool: 'calling tool...',
103
- }[po.status] || po.status
104
-
105
- const handleThreadContextMenu = (e: React.MouseEvent, sessionId: string) => {
106
- e.preventDefault()
107
- setContextMenu({ x: e.clientX, y: e.clientY, sessionId })
108
- }
109
-
110
- return (
111
- <div className="h-full flex flex-col">
112
- {/* Inspector Header */}
113
- <div className="h-8 bg-po-surface-2 border-b border-po-border flex items-center px-3 gap-2 flex-shrink-0">
114
- <div className="relative flex-shrink-0">
115
- <div className={`w-2 h-2 rounded-full ${statusDot} ${statusGlow} ${isActive ? 'animate-pulse' : ''}`} />
116
- </div>
117
- <span className="font-mono text-xs text-po-text-primary font-medium">{po.name}</span>
118
- <span className={`text-2xs font-medium truncate ${statusLabelColor} ${isActive ? 'animate-pulse' : ''}`}>{statusLabel}</span>
119
- {po.description && (
120
- <span className="text-2xs text-po-text-ghost truncate hidden sm:inline">{po.description}</span>
121
- )}
122
- {notifications.length > 0 && (
123
- <span className="text-2xs font-mono bg-po-warning text-po-bg px-1 rounded font-bold">
124
- {notifications.length}
125
- </span>
126
- )}
127
-
128
- <div className="flex-1" />
129
-
130
- {/* Thread picker */}
131
- <div className="relative">
132
- <button
133
- onClick={() => setThreadMenuOpen(!threadMenuOpen)}
134
- className="flex items-center gap-1 text-2xs text-po-text-secondary hover:text-po-text-primary transition-colors duration-150"
135
- >
136
- <span className="font-mono">
137
- {currentSessionId
138
- ? sessions.find(s => s.id === currentSessionId)?.name || `Thread ${currentSessionId.slice(0, 6)}`
139
- : 'No thread'}
140
- </span>
141
- <svg className={`w-3 h-3 transition-transform ${threadMenuOpen ? 'rotate-180' : ''}`} fill="none" stroke="currentColor" viewBox="0 0 24 24">
142
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
143
- </svg>
144
- </button>
145
-
146
- {threadMenuOpen && (
147
- <div className="absolute right-0 top-full mt-1 w-56 bg-po-surface-2 border border-po-border rounded shadow-xl z-50 overflow-hidden">
148
- <button
149
- onClick={() => { createThread(po.name); setThreadMenuOpen(false) }}
150
- className="w-full text-left px-2.5 py-1.5 text-xs text-po-accent hover:bg-po-surface-3 transition-colors duration-150 border-b border-po-border"
151
- >
152
- + New Thread
153
- </button>
154
- <div className="max-h-48 overflow-auto">
155
- {sortedSessions.map((session) => (
156
- <button
157
- key={session.id}
158
- onClick={() => { switchSession(po.name, session.id); setThreadMenuOpen(false) }}
159
- onContextMenu={(e) => handleThreadContextMenu(e, session.id)}
160
- className={`w-full text-left px-2.5 py-1.5 text-xs transition-colors duration-150 ${
161
- session.id === currentSessionId
162
- ? 'bg-po-accent-wash text-po-accent'
163
- : 'text-po-text-secondary hover:bg-po-surface-3'
164
- }`}
165
- >
166
- <div className="flex items-center gap-1.5">
167
- {session.thread_type === 'delegation' && <span className="text-po-status-delegated">&#8627;</span>}
168
- <span className="font-mono truncate flex-1">
169
- {session.name || `Thread ${session.id.slice(0, 6)}`}
170
- </span>
171
- <span className="text-2xs text-po-text-ghost">{session.message_count}m</span>
172
- </div>
173
- {session.parent_po && (
174
- <div className="text-2xs text-po-status-delegated mt-0.5">from {session.parent_po}</div>
175
- )}
176
- </button>
177
- ))}
178
- </div>
179
- </div>
180
- )}
181
- </div>
182
- </div>
183
-
184
- {/* Top: Methods + Source (collapsible, resizable height) */}
185
- <PaneSlot
186
- label="Methods | Source"
187
- collapsed={topPaneCollapsed}
188
- onToggle={toggleTopPane}
189
- height={topPaneResize.size}
190
- resizeHandle={
191
- <div
192
- className="resize-handle-h"
193
- onMouseDown={topPaneResize.onMouseDown}
194
- />
195
- }
196
- >
197
- <div className="flex h-full">
198
- {/* Method List (resizable width) */}
199
- <div style={{ width: methodListResize.size }} className="flex-shrink-0">
200
- <MethodList
201
- po={po}
202
- selectedCapability={selectedCapability}
203
- onSelectCapability={setSelectedCapability}
204
- />
205
- </div>
206
-
207
- {/* Resize handle */}
208
- <div
209
- className="resize-handle"
210
- onMouseDown={methodListResize.onMouseDown}
211
- />
212
-
213
- {/* Source Pane */}
214
- <SourcePane
215
- po={po}
216
- selectedCapability={selectedCapability}
217
- onSave={(prompt) => updatePrompt(po.name, prompt)}
218
- />
219
- </div>
220
- </PaneSlot>
221
-
222
- {/* Middle: Env Data (collapsible, resizable height) */}
223
- <PaneSlot
224
- label={`Env Data${envDataEntries.length > 0 ? ` (${envDataEntries.length})` : ''}`}
225
- collapsed={envDataPaneCollapsed}
226
- onToggle={toggleEnvDataPane}
227
- height={envDataResize.size}
228
- resizeHandle={
229
- <div
230
- className="resize-handle-h"
231
- onMouseDown={envDataResize.onMouseDown}
232
- />
233
- }
234
- >
235
- <EnvDataPane sessionId={currentSessionId} requestEnvData={requestEnvData} />
236
- </PaneSlot>
237
-
238
- {/* Bottom: Workspace */}
239
- <div className="flex-1 overflow-hidden">
240
- <Workspace po={po} sendMessage={sendMessage} />
241
- </div>
242
-
243
- {/* Context menu for thread right-click */}
244
- {contextMenu && (
245
- <ContextMenu
246
- x={contextMenu.x}
247
- y={contextMenu.y}
248
- onClose={() => setContextMenu(null)}
249
- items={[
250
- ...(requestUsage ? [
251
- { label: 'View Usage', onClick: () => requestUsage(contextMenu.sessionId) },
252
- { label: 'View Tree Usage', onClick: () => requestUsage(contextMenu.sessionId, true) },
253
- ] : []),
254
- ...(exportThread ? [
255
- { label: 'Export Markdown', onClick: () => exportThread(contextMenu.sessionId, 'markdown') },
256
- { label: 'Export JSON', onClick: () => exportThread(contextMenu.sessionId, 'json') },
257
- ] : []),
258
- ]}
259
- />
260
- )}
261
- </div>
262
- )
263
- }
@@ -1,155 +0,0 @@
1
- import ReactMarkdown from 'react-markdown'
2
- import remarkGfm from 'remark-gfm'
3
- import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'
4
- import { oneDark } from 'react-syntax-highlighter/dist/esm/styles/prism'
5
- import { useState } from 'react'
6
-
7
- interface MarkdownMessageProps {
8
- content: string
9
- className?: string
10
- }
11
-
12
- export function MarkdownMessage({ content, className = '' }: MarkdownMessageProps) {
13
- return (
14
- <div className={`markdown-content ${className}`}>
15
- <ReactMarkdown
16
- remarkPlugins={[remarkGfm]}
17
- components={{
18
- // Code blocks with syntax highlighting
19
- code({ className, children, ...props }) {
20
- const match = /language-(\w+)/.exec(className || '')
21
- const language = match ? match[1] : ''
22
- const codeString = String(children).replace(/\n$/, '')
23
- const isBlock = codeString.includes('\n') || match
24
-
25
- if (isBlock) {
26
- return (
27
- <CodeBlock language={language} code={codeString} />
28
- )
29
- }
30
-
31
- return (
32
- <code
33
- className="bg-po-surface-2 px-1 py-0.5 rounded text-po-accent font-mono text-[0.9em]"
34
- {...props}
35
- >
36
- {children}
37
- </code>
38
- )
39
- },
40
- // Styled links
41
- a({ href, children }) {
42
- return (
43
- <a
44
- href={href}
45
- target="_blank"
46
- rel="noopener noreferrer"
47
- className="text-po-accent hover:underline"
48
- >
49
- {children}
50
- </a>
51
- )
52
- },
53
- // Styled paragraphs
54
- p({ children }) {
55
- return <p className="mb-2 last:mb-0">{children}</p>
56
- },
57
- // Styled lists
58
- ul({ children }) {
59
- return <ul className="list-disc list-inside mb-2 space-y-0.5">{children}</ul>
60
- },
61
- ol({ children }) {
62
- return <ol className="list-decimal list-inside mb-2 space-y-0.5">{children}</ol>
63
- },
64
- // Styled headings
65
- h1({ children }) {
66
- return <h1 className="text-base font-bold mb-1.5 mt-3 first:mt-0 text-po-text-primary">{children}</h1>
67
- },
68
- h2({ children }) {
69
- return <h2 className="text-sm font-bold mb-1.5 mt-2 first:mt-0 text-po-text-primary">{children}</h2>
70
- },
71
- h3({ children }) {
72
- return <h3 className="text-xs font-bold mb-1 mt-1.5 first:mt-0 text-po-text-primary">{children}</h3>
73
- },
74
- // Styled blockquotes
75
- blockquote({ children }) {
76
- return (
77
- <blockquote className="border-l-2 border-po-accent pl-3 my-2 text-po-text-secondary italic">
78
- {children}
79
- </blockquote>
80
- )
81
- },
82
- // Styled tables
83
- table({ children }) {
84
- return (
85
- <div className="overflow-x-auto my-2">
86
- <table className="min-w-full border border-po-border text-xs">{children}</table>
87
- </div>
88
- )
89
- },
90
- th({ children }) {
91
- return (
92
- <th className="border border-po-border bg-po-surface-2 px-2 py-1 text-left font-medium text-po-text-primary">
93
- {children}
94
- </th>
95
- )
96
- },
97
- td({ children }) {
98
- return (
99
- <td className="border border-po-border px-2 py-1 text-po-text-secondary">{children}</td>
100
- )
101
- },
102
- // Horizontal rule
103
- hr() {
104
- return <hr className="border-po-border my-3" />
105
- },
106
- }}
107
- >
108
- {content}
109
- </ReactMarkdown>
110
- </div>
111
- )
112
- }
113
-
114
- function CodeBlock({ language, code }: { language: string; code: string }) {
115
- const [copied, setCopied] = useState(false)
116
-
117
- const handleCopy = async () => {
118
- await navigator.clipboard.writeText(code)
119
- setCopied(true)
120
- setTimeout(() => setCopied(false), 2000)
121
- }
122
-
123
- return (
124
- <div className="relative group my-2">
125
- {/* Language label and copy button */}
126
- <div className="flex items-center justify-between bg-po-surface-2 px-2.5 py-1 rounded-t border border-b-0 border-po-border">
127
- <span className="text-2xs text-po-text-ghost font-mono">
128
- {language || 'text'}
129
- </span>
130
- <button
131
- onClick={handleCopy}
132
- className="text-2xs text-po-text-ghost hover:text-po-text-primary transition-colors duration-150"
133
- >
134
- {copied ? 'Copied' : 'Copy'}
135
- </button>
136
- </div>
137
- {/* Code with syntax highlighting */}
138
- <SyntaxHighlighter
139
- style={oneDark}
140
- language={language || 'text'}
141
- PreTag="div"
142
- customStyle={{
143
- margin: 0,
144
- borderRadius: '0 0 0.25rem 0.25rem',
145
- border: '1px solid #3d3a37',
146
- borderTop: 'none',
147
- fontSize: '11px',
148
- background: '#222120',
149
- }}
150
- >
151
- {code}
152
- </SyntaxHighlighter>
153
- </div>
154
- )
155
- }
@@ -1,90 +0,0 @@
1
- import type { PromptObject, CapabilityInfo } from '../types'
2
-
3
- interface MethodListProps {
4
- po: PromptObject
5
- selectedCapability: CapabilityInfo | null
6
- onSelectCapability: (cap: CapabilityInfo | null) => void
7
- }
8
-
9
- export function MethodList({ po, selectedCapability, onSelectCapability }: MethodListProps) {
10
- const capabilities = po.capabilities || []
11
- const universalCapabilities = po.universal_capabilities || []
12
-
13
- const handleClick = (cap: CapabilityInfo) => {
14
- if (selectedCapability?.name === cap.name) {
15
- onSelectCapability(null) // Toggle off
16
- } else {
17
- onSelectCapability(cap)
18
- }
19
- }
20
-
21
- return (
22
- <div className="h-full border-r border-po-border overflow-auto bg-po-surface">
23
- {/* Source (prompt view) */}
24
- <button
25
- onClick={() => onSelectCapability(null)}
26
- className={`w-full text-left px-2.5 py-1 text-xs font-mono border-b border-po-border transition-colors duration-150 ${
27
- selectedCapability === null
28
- ? 'bg-po-accent-wash text-po-accent'
29
- : 'text-po-text-secondary hover:bg-po-surface-2'
30
- }`}
31
- >
32
- Source
33
- </button>
34
-
35
- {/* Declared capabilities */}
36
- {capabilities.length > 0 && (
37
- <div>
38
- <div className="px-2.5 py-1.5 border-b border-po-border">
39
- <span className="text-2xs font-medium text-po-text-ghost uppercase tracking-wider">
40
- Methods ({capabilities.length})
41
- </span>
42
- </div>
43
- {capabilities.map((cap) => (
44
- <button
45
- key={cap.name}
46
- onClick={() => handleClick(cap)}
47
- className={`w-full text-left px-2.5 py-1 text-xs font-mono transition-colors duration-150 ${
48
- selectedCapability?.name === cap.name
49
- ? 'bg-po-accent-wash text-po-accent'
50
- : 'text-po-accent hover:bg-po-surface-2'
51
- }`}
52
- >
53
- {cap.name}
54
- </button>
55
- ))}
56
- </div>
57
- )}
58
-
59
- {/* Universal capabilities */}
60
- {universalCapabilities.length > 0 && (
61
- <div>
62
- <div className="px-2.5 py-1.5 border-b border-po-border border-t border-po-border">
63
- <span className="text-2xs font-medium text-po-text-ghost uppercase tracking-wider">
64
- Universal ({universalCapabilities.length})
65
- </span>
66
- </div>
67
- {universalCapabilities.map((cap) => (
68
- <button
69
- key={cap.name}
70
- onClick={() => handleClick(cap)}
71
- className={`w-full text-left px-2.5 py-1 text-xs font-mono transition-colors duration-150 ${
72
- selectedCapability?.name === cap.name
73
- ? 'bg-po-accent-wash text-po-text-secondary'
74
- : 'text-po-text-secondary hover:bg-po-surface-2'
75
- }`}
76
- >
77
- {cap.name}
78
- </button>
79
- ))}
80
- </div>
81
- )}
82
-
83
- {capabilities.length === 0 && universalCapabilities.length === 0 && (
84
- <div className="px-2.5 py-4 text-2xs text-po-text-ghost text-center">
85
- No capabilities
86
- </div>
87
- )}
88
- </div>
89
- )
90
- }
@@ -1,115 +0,0 @@
1
- import { useState, useRef, useEffect } from 'react'
2
- import { useStore } from '../store'
3
-
4
- interface Props {
5
- switchLLM: (provider: string, model?: string) => void
6
- }
7
-
8
- export function ModelSelector({ switchLLM }: Props) {
9
- const { llmConfig } = useStore()
10
- const [isOpen, setIsOpen] = useState(false)
11
- const dropdownRef = useRef<HTMLDivElement>(null)
12
-
13
- // Close dropdown when clicking outside
14
- useEffect(() => {
15
- function handleClickOutside(event: MouseEvent) {
16
- if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
17
- setIsOpen(false)
18
- }
19
- }
20
- document.addEventListener('mousedown', handleClickOutside)
21
- return () => document.removeEventListener('mousedown', handleClickOutside)
22
- }, [])
23
-
24
- if (!llmConfig) return null
25
-
26
- const handleSelectModel = (provider: string, model: string) => {
27
- switchLLM(provider, model)
28
- setIsOpen(false)
29
- }
30
-
31
- const providerNames: Record<string, string> = {
32
- openai: 'OpenAI',
33
- anthropic: 'Anthropic',
34
- gemini: 'Gemini',
35
- ollama: 'Ollama',
36
- openrouter: 'OpenRouter',
37
- }
38
-
39
- return (
40
- <div className="relative" ref={dropdownRef}>
41
- <button
42
- onClick={() => setIsOpen(!isOpen)}
43
- className="flex items-center gap-1.5 px-2 py-0.5 text-xs bg-po-surface-2 border border-po-border rounded hover:border-po-border-focus transition-colors duration-150"
44
- >
45
- <span className="text-po-text-tertiary">
46
- {providerNames[llmConfig.current_provider] || llmConfig.current_provider}
47
- </span>
48
- <span className="font-mono text-po-text-primary">{llmConfig.current_model}</span>
49
- <svg
50
- className={`w-3 h-3 text-po-text-ghost transition-transform ${isOpen ? 'rotate-180' : ''}`}
51
- fill="none"
52
- stroke="currentColor"
53
- viewBox="0 0 24 24"
54
- >
55
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
56
- </svg>
57
- </button>
58
-
59
- {isOpen && (
60
- <div className="absolute right-0 top-full mt-1 w-60 bg-po-surface-2 border border-po-border rounded shadow-xl z-50 overflow-hidden">
61
- {llmConfig.providers.map((provider) => (
62
- <div key={provider.name}>
63
- <div className="px-2.5 py-1.5 bg-po-surface text-2xs font-medium text-po-text-ghost uppercase tracking-wider flex items-center justify-between">
64
- <span>{providerNames[provider.name] || provider.name}</span>
65
- {!provider.available && (
66
- <span className="text-po-error text-2xs normal-case">
67
- {provider.name === 'ollama' ? 'Not Running' : 'No API Key'}
68
- </span>
69
- )}
70
- </div>
71
- {provider.models.map((model) => {
72
- const isSelected =
73
- provider.name === llmConfig.current_provider &&
74
- model === llmConfig.current_model
75
- const isAvailable = provider.available
76
- const isDefault = model === provider.default_model
77
-
78
- return (
79
- <button
80
- key={`${provider.name}-${model}`}
81
- onClick={() => isAvailable && handleSelectModel(provider.name, model)}
82
- disabled={!isAvailable}
83
- className={`w-full px-2.5 py-1.5 text-left text-xs font-mono flex items-center justify-between transition-colors duration-150 ${
84
- isSelected
85
- ? 'bg-po-accent-wash text-po-accent'
86
- : isAvailable
87
- ? 'text-po-text-secondary hover:bg-po-surface-3'
88
- : 'text-po-text-ghost cursor-not-allowed'
89
- }`}
90
- >
91
- <span className="flex items-center gap-2">
92
- {model}
93
- {isDefault && (
94
- <span className="text-2xs text-po-text-ghost">(default)</span>
95
- )}
96
- </span>
97
- {isSelected && (
98
- <svg className="w-3 h-3" fill="currentColor" viewBox="0 0 20 20">
99
- <path
100
- fillRule="evenodd"
101
- d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
102
- clipRule="evenodd"
103
- />
104
- </svg>
105
- )}
106
- </button>
107
- )
108
- })}
109
- </div>
110
- ))}
111
- </div>
112
- )}
113
- </div>
114
- )
115
- }