prompt_objects 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +142 -0
- data/README.md +211 -19
- data/exe/poop +5 -0
- data/exe/prompt_objects +1 -1108
- data/exe/prompt_objects_mcp +5 -3
- data/lib/prompt_objects/capability.rb +33 -0
- data/lib/prompt_objects/cli/diagnostics_command.rb +69 -0
- data/lib/prompt_objects/cli/env_command.rb +435 -0
- data/lib/prompt_objects/cli/events_command.rb +145 -0
- data/lib/prompt_objects/cli/explore_command.rb +155 -0
- data/lib/prompt_objects/cli/helpers.rb +91 -0
- data/lib/prompt_objects/cli/message_command.rb +216 -0
- data/lib/prompt_objects/cli/repl_command.rb +366 -0
- data/lib/prompt_objects/cli/serve_command.rb +162 -0
- data/lib/prompt_objects/cli.rb +64 -422
- data/lib/prompt_objects/connectors/base.rb +16 -29
- data/lib/prompt_objects/connectors/mcp.rb +82 -98
- data/lib/prompt_objects/domain/records.rb +45 -0
- data/lib/prompt_objects/environment/exporter.rb +5 -4
- data/lib/prompt_objects/environment/git.rb +21 -1
- data/lib/prompt_objects/environment/importer.rb +5 -4
- data/lib/prompt_objects/environment/manager.rb +44 -6
- data/lib/prompt_objects/environment/manifest.rb +26 -4
- data/lib/prompt_objects/environment.rb +1350 -18
- data/lib/prompt_objects/execution/blocking_executor.rb +88 -0
- data/lib/prompt_objects/execution/context.rb +137 -0
- data/lib/prompt_objects/execution/contracts.rb +59 -0
- data/lib/prompt_objects/execution/permit_pool.rb +49 -0
- data/lib/prompt_objects/execution/policy.rb +47 -0
- data/lib/prompt_objects/execution/resource_lock_manager.rb +63 -0
- data/lib/prompt_objects/execution/runner.rb +498 -0
- data/lib/prompt_objects/execution/scheduler.rb +325 -0
- data/lib/prompt_objects/llm/factory.rb +4 -4
- data/lib/prompt_objects/llm/gemini_adapter.rb +25 -8
- data/lib/prompt_objects/llm/openai_adapter.rb +9 -1
- data/lib/prompt_objects/llm/pricing.rb +9 -2
- data/lib/prompt_objects/llm/response.rb +9 -4
- data/lib/prompt_objects/loader.rb +28 -1
- data/lib/prompt_objects/mcp/server.rb +6 -3
- data/lib/prompt_objects/mcp/tools/get_conversation.rb +15 -11
- data/lib/prompt_objects/mcp/tools/get_pending_requests.rb +2 -2
- data/lib/prompt_objects/mcp/tools/respond_to_request.rb +3 -3
- data/lib/prompt_objects/mcp/tools/send_message.rb +42 -32
- data/lib/prompt_objects/operations/diagnostics.rb +115 -0
- data/lib/prompt_objects/operations/retention.rb +75 -0
- data/lib/prompt_objects/operations/run_trace_builder.rb +152 -0
- data/lib/prompt_objects/primitives/http_get.rb +2 -0
- data/lib/prompt_objects/primitives/list_files.rb +2 -0
- data/lib/prompt_objects/primitives/read_file.rb +2 -0
- data/lib/prompt_objects/primitives/think.rb +36 -0
- data/lib/prompt_objects/primitives/write_file.rb +9 -0
- data/lib/prompt_objects/prompt_object.rb +190 -432
- data/lib/prompt_objects/repositories/artifact_repository.rb +109 -0
- data/lib/prompt_objects/repositories/base_repository.rb +37 -0
- data/lib/prompt_objects/repositories/definition_repository.rb +36 -0
- data/lib/prompt_objects/repositories/env_data_repository.rb +103 -0
- data/lib/prompt_objects/repositories/event_repository.rb +105 -0
- data/lib/prompt_objects/repositories/message_repository.rb +73 -0
- data/lib/prompt_objects/repositories/run_repository.rb +254 -0
- data/lib/prompt_objects/repositories/thread_repository.rb +87 -0
- data/lib/prompt_objects/repositories/tool_execution_repository.rb +88 -0
- data/lib/prompt_objects/repositories/workspace_session_repository.rb +61 -0
- data/lib/prompt_objects/server/api/routes.rb +561 -52
- data/lib/prompt_objects/server/app.rb +134 -1
- data/lib/prompt_objects/server/file_watcher.rb +9 -0
- data/lib/prompt_objects/server/public/assets/index-BjKlODDD.js +90 -0
- data/lib/prompt_objects/server/public/assets/index-w3fpIb5c.css +1 -0
- data/lib/prompt_objects/server/public/index.html +2 -2
- data/lib/prompt_objects/server/websocket_handler.rb +202 -207
- data/lib/prompt_objects/server.rb +170 -1
- data/lib/prompt_objects/service.rb +129 -0
- data/lib/prompt_objects/session/store.rb +774 -15
- data/lib/prompt_objects/universal/add_capability.rb +1 -1
- data/lib/prompt_objects/universal/create_capability.rb +4 -2
- data/lib/prompt_objects/universal/delete_env_data.rb +78 -0
- data/lib/prompt_objects/universal/delete_primitive.rb +1 -1
- data/lib/prompt_objects/universal/dispatch_po.rb +54 -0
- data/lib/prompt_objects/universal/get_env_data.rb +58 -0
- data/lib/prompt_objects/universal/get_run_status.rb +30 -0
- data/lib/prompt_objects/universal/join_runs.rb +35 -0
- data/lib/prompt_objects/universal/list_env_data.rb +57 -0
- data/lib/prompt_objects/universal/modify_primitive.rb +1 -1
- data/lib/prompt_objects/universal/render_artifact.rb +91 -0
- data/lib/prompt_objects/universal/request_primitive.rb +1 -1
- data/lib/prompt_objects/universal/set_env_data.rb +101 -0
- data/lib/prompt_objects/version.rb +5 -0
- data/lib/prompt_objects.rb +36 -6
- data/objects/coordinator.md +8 -4
- data/templates/arc-agi-1/objects/data_manager.md +2 -2
- data/templates/arc-agi-1/objects/observer.md +6 -2
- data/templates/arc-agi-1/objects/solver.md +14 -4
- data/templates/arc-agi-1/objects/verifier.md +6 -2
- data/templates/arc-agi-3/manifest.yml +19 -0
- data/templates/arc-agi-3/objects/analyst.md +79 -0
- data/templates/arc-agi-3/objects/player.md +76 -0
- data/templates/arc-agi-3/objects/strategist.md +97 -0
- data/templates/arc-agi-3/primitives/action_log.rb +133 -0
- data/templates/arc-agi-3/primitives/arc3_api.rb +223 -0
- data/templates/arc-agi-3/primitives/arc3_scorecard.rb +131 -0
- data/templates/arc-agi-3/primitives/frame_diff.rb +143 -0
- data/templates/arc-agi-3/primitives/frame_info.rb +96 -0
- data/templates/arc-agi-3/primitives/render_frame.rb +140 -0
- data/templates/basic/objects/basic.md +11 -3
- data/templates/chiptune/extras/sonic_pi_boot.rb +220 -0
- data/templates/chiptune/manifest.yml +20 -0
- data/templates/chiptune/objects/conductor.md +184 -0
- data/templates/chiptune/objects/sonic_pi.md +65 -0
- data/templates/chiptune/objects/voice_bass.md +44 -0
- data/templates/chiptune/objects/voice_lead.md +44 -0
- data/templates/chiptune/objects/voice_pad.md +43 -0
- data/templates/chiptune/objects/voice_perc.md +45 -0
- data/templates/chiptune/primitives/play_note.rb +59 -0
- data/templates/chiptune/primitives/play_sequence.rb +55 -0
- data/templates/chiptune/primitives/query_sonic_pi.rb +141 -0
- data/templates/chiptune/primitives/set_synth.rb +57 -0
- data/templates/chiptune/primitives/set_tempo.rb +45 -0
- data/templates/chiptune/primitives/stop_voice.rb +46 -0
- data/templates/chiptune/primitives/support/osc_bridge.rb +33 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/LICENSE +19 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/address_pattern.rb +58 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/broadcast_client.rb +24 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/bundle.rb +46 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/client.rb +13 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/core_ext/time.rb +6 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/em_server.rb +63 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/message.rb +46 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/network_packet.rb +42 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_argument.rb +26 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_packet.rb +140 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_types.rb +53 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/server.rb +91 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby.rb +18 -0
- data/templates/developer/objects/code_reviewer.md +1 -1
- data/templates/developer/objects/coordinator.md +16 -3
- data/templates/developer/objects/debugger.md +3 -3
- data/templates/empty/objects/assistant.md +10 -5
- data/templates/minimal/objects/assistant.md +10 -5
- data/templates/music/extras/sonic_pi_boot.rb +220 -0
- data/templates/music/manifest.yml +16 -0
- data/templates/music/objects/conductor.md +154 -0
- data/templates/music/objects/sonic_pi.md +65 -0
- data/templates/music/primitives/play_note.rb +55 -0
- data/templates/music/primitives/play_sequence.rb +51 -0
- data/templates/music/primitives/query_sonic_pi.rb +139 -0
- data/templates/music/primitives/set_synth.rb +53 -0
- data/templates/music/primitives/set_tempo.rb +43 -0
- data/templates/music/primitives/stop_voice.rb +44 -0
- data/templates/music/primitives/support/osc_bridge.rb +33 -0
- data/templates/music/vendor/osc-ruby-1.1.5/LICENSE +19 -0
- data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/address_pattern.rb +58 -0
- data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/broadcast_client.rb +24 -0
- data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/bundle.rb +46 -0
- data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/client.rb +13 -0
- data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/core_ext/time.rb +6 -0
- data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/em_server.rb +63 -0
- data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/message.rb +46 -0
- data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/network_packet.rb +42 -0
- data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_argument.rb +26 -0
- data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_packet.rb +140 -0
- data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_types.rb +53 -0
- data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/server.rb +91 -0
- data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby.rb +18 -0
- data/templates/music-beginner/README.md +100 -0
- data/templates/music-beginner/extras/README.md +27 -0
- data/templates/music-beginner/extras/sonic_pi_boot.rb +297 -0
- data/templates/music-beginner/manifest.yml +25 -0
- data/templates/music-beginner/objects/conductor.md +222 -0
- data/templates/music-beginner/objects/engine_setup.md +54 -0
- data/templates/music-beginner/objects/voice_bass.md +43 -0
- data/templates/music-beginner/objects/voice_keys.md +48 -0
- data/templates/music-beginner/objects/voice_pad.md +46 -0
- data/templates/music-beginner/objects/voice_percussion.md +45 -0
- data/templates/music-beginner/objects/voice_texture.md +37 -0
- data/templates/music-beginner/primitives/play_pattern.rb +118 -0
- data/templates/music-beginner/primitives/query_music_engine.rb +94 -0
- data/templates/music-beginner/primitives/set_instrument.rb +47 -0
- data/templates/music-beginner/primitives/set_tempo.rb +43 -0
- data/templates/music-beginner/primitives/set_voice_level.rb +46 -0
- data/templates/music-beginner/primitives/stop_voice.rb +37 -0
- data/templates/music-beginner/primitives/support/osc_bridge.rb +25 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/LICENSE +19 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/address_pattern.rb +58 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/broadcast_client.rb +24 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/bundle.rb +46 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/client.rb +13 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/core_ext/time.rb +6 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/em_server.rb +63 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/message.rb +46 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/network_packet.rb +42 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_argument.rb +26 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_packet.rb +140 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_types.rb +53 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/server.rb +91 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby.rb +18 -0
- data/templates/music-pro/extras/AUDITION.md +100 -0
- data/templates/music-pro/extras/sonic_pi_boot.rb +546 -0
- data/templates/music-pro/manifest.yml +19 -0
- data/templates/music-pro/objects/conductor.md +126 -0
- data/templates/music-pro/objects/sonic_pi.md +35 -0
- data/templates/music-pro/objects/voice_harmony.md +48 -0
- data/templates/music-pro/objects/voice_rhythm.md +43 -0
- data/templates/music-pro/objects/voice_texture.md +42 -0
- data/templates/music-pro/primitives/define_patch.rb +46 -0
- data/templates/music-pro/primitives/query_music_engine.rb +69 -0
- data/templates/music-pro/primitives/set_live_pattern.rb +70 -0
- data/templates/music-pro/primitives/set_tempo.rb +31 -0
- data/templates/music-pro/primitives/stop_voice.rb +31 -0
- data/templates/music-pro/primitives/support/music_protocol.rb +136 -0
- data/templates/music-pro/primitives/support/osc_bridge.rb +33 -0
- data/templates/music-pro/primitives/trigger_event.rb +44 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/LICENSE +19 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/address_pattern.rb +58 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/broadcast_client.rb +24 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/bundle.rb +46 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/client.rb +13 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/core_ext/time.rb +6 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/em_server.rb +63 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/message.rb +46 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/network_packet.rb +42 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_argument.rb +26 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_packet.rb +140 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_types.rb +53 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/server.rb +91 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby.rb +18 -0
- data/templates/writer/objects/coordinator.md +14 -3
- data/templates/writer/objects/editor.md +1 -1
- data/templates/writer/objects/researcher.md +2 -2
- data/tools/thread-explorer.html +28 -1
- metadata +175 -65
- data/CLAUDE.md +0 -176
- data/Gemfile +0 -10
- data/Gemfile.lock +0 -233
- data/IMPLEMENTATION_PLAN.md +0 -1073
- data/Rakefile +0 -27
- data/design-doc-v2.md +0 -1232
- data/frontend/.gitignore +0 -3
- data/frontend/index.html +0 -17
- data/frontend/package-lock.json +0 -4540
- data/frontend/package.json +0 -36
- data/frontend/postcss.config.js +0 -6
- data/frontend/src/App.tsx +0 -103
- data/frontend/src/canvas/CanvasView.tsx +0 -113
- data/frontend/src/canvas/ForceLayout.ts +0 -115
- data/frontend/src/canvas/SceneManager.ts +0 -587
- data/frontend/src/canvas/canvasStore.ts +0 -47
- data/frontend/src/canvas/constants.ts +0 -95
- data/frontend/src/canvas/controls/CameraControls.ts +0 -98
- data/frontend/src/canvas/edges/MessageArc.ts +0 -149
- data/frontend/src/canvas/inspector/InspectorPanel.tsx +0 -31
- data/frontend/src/canvas/inspector/POInspector.tsx +0 -262
- data/frontend/src/canvas/inspector/ToolCallInspector.tsx +0 -67
- data/frontend/src/canvas/nodes/PONode.ts +0 -249
- data/frontend/src/canvas/nodes/ToolCallNode.ts +0 -116
- data/frontend/src/canvas/types.ts +0 -24
- data/frontend/src/components/ContextMenu.tsx +0 -68
- data/frontend/src/components/Inspector.tsx +0 -232
- data/frontend/src/components/MarkdownMessage.tsx +0 -155
- data/frontend/src/components/MethodList.tsx +0 -90
- data/frontend/src/components/ModelSelector.tsx +0 -115
- data/frontend/src/components/NotificationPanel.tsx +0 -130
- data/frontend/src/components/ObjectList.tsx +0 -78
- data/frontend/src/components/PaneSlot.tsx +0 -30
- data/frontend/src/components/SourcePane.tsx +0 -202
- data/frontend/src/components/SystemBar.tsx +0 -74
- data/frontend/src/components/Transcript.tsx +0 -76
- data/frontend/src/components/UsagePanel.tsx +0 -105
- data/frontend/src/components/Workspace.tsx +0 -260
- data/frontend/src/components/index.ts +0 -12
- data/frontend/src/hooks/useResize.ts +0 -55
- data/frontend/src/hooks/useWebSocket.ts +0 -517
- data/frontend/src/index.css +0 -103
- data/frontend/src/main.tsx +0 -10
- data/frontend/src/store/index.ts +0 -279
- data/frontend/src/types/index.ts +0 -165
- data/frontend/tailwind.config.js +0 -44
- data/frontend/tsconfig.json +0 -30
- data/frontend/vite.config.ts +0 -29
- data/lib/prompt_objects/server/public/assets/index-D1myxE0l.js +0 -4345
- data/lib/prompt_objects/server/public/assets/index-DdCcwC-Z.css +0 -1
- data/lib/prompt_objects/universal/add_primitive.rb +0 -118
- data/lib/prompt_objects/universal/ask_human.rb +0 -109
- data/lib/prompt_objects/universal/create_primitive.rb +0 -174
- data/lib/prompt_objects/universal/list_primitives.rb +0 -145
- data/lib/prompt_objects/universal/think.rb +0 -41
- data/prompt_objects.gemspec +0 -50
- data/templates/basic/.gitignore +0 -2
- data/templates/developer/.gitignore +0 -5
- data/templates/empty/.gitignore +0 -5
- data/templates/empty/objects/.gitkeep +0 -0
- data/templates/minimal/.gitignore +0 -5
- data/templates/writer/.gitignore +0 -5
|
@@ -0,0 +1 @@
|
|
|
1
|
+
*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:Geist Mono,IBM Plex Mono,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.container{width:100%}@media(min-width:640px){.container{max-width:640px}}@media(min-width:768px){.container{max-width:768px}}@media(min-width:1024px){.container{max-width:1024px}}@media(min-width:1280px){.container{max-width:1280px}}@media(min-width:1536px){.container{max-width:1536px}}.pointer-events-none{pointer-events:none}.visible{visibility:visible}.collapse{visibility:collapse}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{top:0;right:0;bottom:0;left:0}.bottom-0{bottom:0}.bottom-3{bottom:.75rem}.bottom-4{bottom:1rem}.left-0{left:0}.left-2{left:.5rem}.right-0{right:0}.right-3{right:.75rem}.right-4{right:1rem}.top-0{top:0}.top-3{top:.75rem}.top-full{top:100%}.z-10{z-index:10}.z-20{z-index:20}.z-50{z-index:50}.mx-2{margin-left:.5rem;margin-right:.5rem}.my-0\.5{margin-top:.125rem;margin-bottom:.125rem}.my-1{margin-top:.25rem;margin-bottom:.25rem}.my-2{margin-top:.5rem;margin-bottom:.5rem}.my-3{margin-top:.75rem;margin-bottom:.75rem}.mb-0\.5{margin-bottom:.125rem}.mb-1{margin-bottom:.25rem}.mb-1\.5{margin-bottom:.375rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.ml-0\.5{margin-left:.125rem}.ml-1{margin-left:.25rem}.ml-2{margin-left:.5rem}.ml-\[4\.5rem\]{margin-left:4.5rem}.ml-auto{margin-left:auto}.mr-2{margin-right:.5rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-1\.5{margin-top:.375rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-5{margin-top:1.25rem}.line-clamp-2{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2}.line-clamp-4{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:4}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.table{display:table}.grid{display:grid}.contents{display:contents}.hidden{display:none}.h-1\.5{height:.375rem}.h-10{height:2.5rem}.h-12{height:3rem}.h-2{height:.5rem}.h-2\.5{height:.625rem}.h-3{height:.75rem}.h-3\.5{height:.875rem}.h-6{height:1.5rem}.h-7{height:1.75rem}.h-8{height:2rem}.h-9{height:2.25rem}.h-full{height:100%}.h-screen{height:100vh}.max-h-48{max-height:12rem}.max-h-52{max-height:13rem}.max-h-96{max-height:24rem}.max-h-\[60vh\]{max-height:60vh}.max-h-\[80vh\]{max-height:80vh}.min-h-0{min-height:0px}.min-h-12{min-height:3rem}.min-h-9{min-height:2.25rem}.min-h-\[22rem\]{min-height:22rem}.min-h-\[28rem\]{min-height:28rem}.w-1\.5{width:.375rem}.w-2{width:.5rem}.w-2\.5{width:.625rem}.w-3{width:.75rem}.w-56{width:14rem}.w-6{width:1.5rem}.w-60{width:15rem}.w-64{width:16rem}.w-96{width:24rem}.w-\[400px\]{width:400px}.w-\[420px\]{width:420px}.w-full{width:100%}.min-w-0{min-width:0px}.min-w-10{min-width:2.5rem}.min-w-20{min-width:5rem}.min-w-\[140px\]{min-width:140px}.min-w-\[68rem\]{min-width:68rem}.min-w-\[72rem\]{min-width:72rem}.min-w-full{min-width:100%}.max-w-36{max-width:9rem}.max-w-3xl{max-width:48rem}.max-w-\[60px\]{max-width:60px}.max-w-md{max-width:28rem}.max-w-sm{max-width:24rem}.flex-1{flex:1 1 0%}.flex-shrink-0{flex-shrink:0}.rotate-180{--tw-rotate: 180deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}.cursor-grab{cursor:grab}.cursor-not-allowed{cursor:not-allowed}.cursor-nwse-resize{cursor:nwse-resize}.cursor-pointer{cursor:pointer}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.resize-none{resize:none}.\!resize{resize:both!important}.resize{resize:both}.list-inside{list-style-position:inside}.list-decimal{list-style-type:decimal}.list-disc{list-style-type:disc}.auto-rows-\[minmax\(22rem\,auto\)\]{grid-auto-rows:minmax(22rem,auto)}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.grid-cols-\[14rem_minmax\(30rem\,1fr\)_22rem\]{grid-template-columns:14rem minmax(30rem,1fr) 22rem}.grid-cols-\[16rem_minmax\(34rem\,1fr\)_22rem\]{grid-template-columns:16rem minmax(34rem,1fr) 22rem}.grid-cols-\[17rem_minmax\(25rem\,1fr\)_minmax\(20rem\,0\.8fr\)\]{grid-template-columns:17rem minmax(25rem,1fr) minmax(20rem,.8fr)}.grid-cols-\[6rem_1fr\]{grid-template-columns:6rem 1fr}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-center{align-items:center}.items-baseline{align-items:baseline}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-px{gap:1px}.gap-x-2{-moz-column-gap:.5rem;column-gap:.5rem}.gap-y-1\.5{row-gap:.375rem}.space-y-0\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.125rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.125rem * var(--tw-space-y-reverse))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-1\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.375rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.375rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-visible{overflow:visible}.overflow-x-auto{overflow-x:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-b{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.rounded-r{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.rounded-t{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.border{border-width:1px}.border-0{border-width:0px}.border-b{border-bottom-width:1px}.border-b-0{border-bottom-width:0px}.border-l{border-left-width:1px}.border-l-2{border-left-width:2px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-dashed{border-style:dashed}.border-po-accent{--tw-border-opacity: 1;border-color:rgb(212 149 42 / var(--tw-border-opacity, 1))}.border-po-accent-muted{--tw-border-opacity: 1;border-color:rgb(154 109 32 / var(--tw-border-opacity, 1))}.border-po-accent\/40{border-color:#d4952a66}.border-po-border{--tw-border-opacity: 1;border-color:rgb(61 58 55 / var(--tw-border-opacity, 1))}.border-po-border-focus{--tw-border-opacity: 1;border-color:rgb(92 87 82 / var(--tw-border-opacity, 1))}.border-po-error{--tw-border-opacity: 1;border-color:rgb(196 92 74 / var(--tw-border-opacity, 1))}.border-po-error\/40{border-color:#c45c4a66}.border-po-error\/60{border-color:#c45c4a99}.border-po-status-active{--tw-border-opacity: 1;border-color:rgb(212 149 42 / var(--tw-border-opacity, 1))}.border-po-status-calling,.border-po-success{--tw-border-opacity: 1;border-color:rgb(59 154 110 / var(--tw-border-opacity, 1))}.border-po-success\/40{border-color:#3b9a6e66}.border-po-success\/50{border-color:#3b9a6e80}.border-po-warning{--tw-border-opacity: 1;border-color:rgb(212 149 42 / var(--tw-border-opacity, 1))}.border-po-warning\/30{border-color:#d4952a4d}.border-po-warning\/40{border-color:#d4952a66}.border-po-warning\/50{border-color:#d4952a80}.border-transparent{border-color:transparent}.border-violet-400\/50{border-color:#a78bfa80}.bg-\[linear-gradient\(180deg\,rgba\(74\,167\,200\,0\.06\)\,transparent_35\%\)\,\#171513\]{background-color:linear-gradient(180deg,rgba(74,167,200,.06),transparent 35%),#171513}.bg-black\/50{background-color:#00000080}.bg-po-accent{--tw-bg-opacity: 1;background-color:rgb(212 149 42 / var(--tw-bg-opacity, 1))}.bg-po-accent-wash{background-color:#d4952a14}.bg-po-bg{--tw-bg-opacity: 1;background-color:rgb(26 25 24 / var(--tw-bg-opacity, 1))}.bg-po-bg\/50{background-color:#1a191880}.bg-po-bg\/60{background-color:#1a191899}.bg-po-bg\/70{background-color:#1a1918b3}.bg-po-bg\/80{background-color:#1a1918cc}.bg-po-bg\/95{background-color:#1a1918f2}.bg-po-error{--tw-bg-opacity: 1;background-color:rgb(196 92 74 / var(--tw-bg-opacity, 1))}.bg-po-error\/10{background-color:#c45c4a1a}.bg-po-status-active{--tw-bg-opacity: 1;background-color:rgb(212 149 42 / var(--tw-bg-opacity, 1))}.bg-po-status-calling{--tw-bg-opacity: 1;background-color:rgb(59 154 110 / var(--tw-bg-opacity, 1))}.bg-po-status-idle{--tw-bg-opacity: 1;background-color:rgb(120 114 106 / var(--tw-bg-opacity, 1))}.bg-po-success{--tw-bg-opacity: 1;background-color:rgb(59 154 110 / var(--tw-bg-opacity, 1))}.bg-po-surface{--tw-bg-opacity: 1;background-color:rgb(34 33 32 / var(--tw-bg-opacity, 1))}.bg-po-surface-2{--tw-bg-opacity: 1;background-color:rgb(44 42 40 / var(--tw-bg-opacity, 1))}.bg-po-surface-2\/90{background-color:#2c2a28e6}.bg-po-surface\/55{background-color:#2221208c}.bg-po-surface\/60{background-color:#22212099}.bg-po-surface\/70{background-color:#222120b3}.bg-po-surface\/80{background-color:#222120cc}.bg-po-surface\/85{background-color:#222120d9}.bg-po-surface\/90{background-color:#222120e6}.bg-po-surface\/95{background-color:#222120f2}.bg-po-text-ghost{--tw-bg-opacity: 1;background-color:rgb(82 78 72 / var(--tw-bg-opacity, 1))}.bg-po-warning{--tw-bg-opacity: 1;background-color:rgb(212 149 42 / var(--tw-bg-opacity, 1))}.bg-po-warning\/5{background-color:#d4952a0d}.bg-transparent{background-color:transparent}.bg-\[linear-gradient\(135deg\,transparent_46\%\,\#5b5752_47\%\,\#5b5752_54\%\,transparent_55\%\,transparent_66\%\,\#5b5752_67\%\,\#5b5752_74\%\,transparent_75\%\)\]{background-image:linear-gradient(135deg,transparent 46%,#5b5752 47%,#5b5752 54%,transparent 55%,transparent 66%,#5b5752 67%,#5b5752 74%,transparent 75%)}.bg-\[radial-gradient\(circle\,\#2c2a28_1px\,transparent_1px\)\]{background-image:radial-gradient(circle,#2c2a28 1px,transparent 1px)}.bg-gradient-to-b{background-image:linear-gradient(to bottom,var(--tw-gradient-stops))}.from-amber-900{--tw-gradient-from: #78350f var(--tw-gradient-from-position);--tw-gradient-to: rgb(120 53 15 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.to-slate-900{--tw-gradient-to: #0f172a var(--tw-gradient-to-position)}.p-2{padding:.5rem}.p-2\.5{padding:.625rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.p-6{padding:1.5rem}.p-8{padding:2rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-2\.5{padding-left:.625rem;padding-right:.625rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-3\.5{padding-left:.875rem;padding-right:.875rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.pb-1\.5{padding-bottom:.375rem}.pb-2{padding-bottom:.5rem}.pl-2{padding-left:.5rem}.pl-3{padding-left:.75rem}.pl-4{padding-left:1rem}.pl-8{padding-left:2rem}.pl-\[4\.5rem\]{padding-left:4.5rem}.pt-2{padding-top:.5rem}.text-left{text-align:left}.text-center{text-align:center}.align-text-bottom{vertical-align:text-bottom}.font-mono{font-family:Geist Mono,IBM Plex Mono,monospace}.font-ui{font-family:Geist,system-ui,sans-serif}.text-2xs{font-size:11px;line-height:15px}.text-\[0\.9em\]{font-size:.9em}.text-base{font-size:1rem;line-height:1.5rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.uppercase{text-transform:uppercase}.capitalize{text-transform:capitalize}.normal-case{text-transform:none}.italic{font-style:italic}.leading-relaxed{line-height:1.625}.tracking-wide{letter-spacing:.025em}.tracking-wider{letter-spacing:.05em}.text-po-accent{--tw-text-opacity: 1;color:rgb(212 149 42 / var(--tw-text-opacity, 1))}.text-po-bg{--tw-text-opacity: 1;color:rgb(26 25 24 / var(--tw-text-opacity, 1))}.text-po-error{--tw-text-opacity: 1;color:rgb(196 92 74 / var(--tw-text-opacity, 1))}.text-po-status-active{--tw-text-opacity: 1;color:rgb(212 149 42 / var(--tw-text-opacity, 1))}.text-po-status-calling{--tw-text-opacity: 1;color:rgb(59 154 110 / var(--tw-text-opacity, 1))}.text-po-status-delegated{--tw-text-opacity: 1;color:rgb(90 143 194 / var(--tw-text-opacity, 1))}.text-po-success{--tw-text-opacity: 1;color:rgb(59 154 110 / var(--tw-text-opacity, 1))}.text-po-text-ghost{--tw-text-opacity: 1;color:rgb(82 78 72 / var(--tw-text-opacity, 1))}.text-po-text-primary{--tw-text-opacity: 1;color:rgb(232 226 218 / var(--tw-text-opacity, 1))}.text-po-text-secondary{--tw-text-opacity: 1;color:rgb(168 162 154 / var(--tw-text-opacity, 1))}.text-po-text-tertiary{--tw-text-opacity: 1;color:rgb(120 114 106 / var(--tw-text-opacity, 1))}.text-po-warning{--tw-text-opacity: 1;color:rgb(212 149 42 / var(--tw-text-opacity, 1))}.text-violet-300{--tw-text-opacity: 1;color:rgb(196 181 253 / var(--tw-text-opacity, 1))}.placeholder-po-text-ghost::-moz-placeholder{--tw-placeholder-opacity: 1;color:rgb(82 78 72 / var(--tw-placeholder-opacity, 1))}.placeholder-po-text-ghost::placeholder{--tw-placeholder-opacity: 1;color:rgb(82 78 72 / var(--tw-placeholder-opacity, 1))}.opacity-0{opacity:0}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.opacity-80{opacity:.8}.shadow-2xl{--tw-shadow: 0 25px 50px -12px rgb(0 0 0 / .25);--tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-\[0_0_5px_rgba\(212\,149\,42\,0\.6\)\]{--tw-shadow: 0 0 5px rgba(212,149,42,.6);--tw-shadow-colored: 0 0 5px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-\[0_0_5px_rgba\(59\,154\,110\,0\.6\)\]{--tw-shadow: 0 0 5px rgba(59,154,110,.6);--tw-shadow-colored: 0 0 5px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-\[0_0_6px_rgba\(212\,149\,42\,0\.7\)\]{--tw-shadow: 0 0 6px rgba(212,149,42,.7);--tw-shadow-colored: 0 0 6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-\[0_0_6px_rgba\(59\,154\,110\,0\.7\)\]{--tw-shadow: 0 0 6px rgba(59,154,110,.7);--tw-shadow-colored: 0 0 6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-xl{--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / .1), 0 8px 10px -6px rgb(0 0 0 / .1);--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.ring{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-1{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-4{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-po-accent{--tw-ring-opacity: 1;--tw-ring-color: rgb(212 149 42 / var(--tw-ring-opacity, 1))}.ring-po-bg{--tw-ring-opacity: 1;--tw-ring-color: rgb(26 25 24 / var(--tw-ring-opacity, 1))}.ring-po-border{--tw-ring-opacity: 1;--tw-ring-color: rgb(61 58 55 / var(--tw-ring-opacity, 1))}.blur{--tw-blur: blur(8px);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-150{transition-duration:.15s}.\[background-size\:28px_28px\]{background-size:28px 28px}html{color-scheme:dark}body{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}::-webkit-scrollbar{width:6px;height:6px}::-webkit-scrollbar-track{--tw-bg-opacity: 1;background-color:rgb(26 25 24 / var(--tw-bg-opacity, 1))}::-webkit-scrollbar-thumb{border-radius:9999px;--tw-bg-opacity: 1;background-color:rgb(61 58 55 / var(--tw-bg-opacity, 1))}::-webkit-scrollbar-thumb:hover{--tw-bg-opacity: 1;background-color:rgb(92 87 82 / var(--tw-bg-opacity, 1))}::-moz-selection{background:#d4952a4d}::selection{background:#d4952a4d}.resize-handle{width:.25rem;cursor:col-resize;border-left-width:1px;--tw-border-opacity: 1;border-color:rgb(61 58 55 / var(--tw-border-opacity, 1));transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.resize-handle:hover{background-color:#d4952a4d}.resize-handle:active{background-color:#d4952a80}.resize-handle-h{height:.25rem;cursor:row-resize;border-top-width:1px;--tw-border-opacity: 1;border-color:rgb(61 58 55 / var(--tw-border-opacity, 1));transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.resize-handle-h:hover{background-color:#d4952a4d}.resize-handle-h:active{background-color:#d4952a80}.floor-surface{background-color:#1a1918;background-image:radial-gradient(circle,#2c2a28 1px,transparent 1px);background-size:28px 28px}@keyframes station-enter{0%{opacity:0;scale:.96}to{opacity:1;scale:1}}.station-enter{animation:station-enter .24s cubic-bezier(.16,1,.3,1)}@keyframes drawer-enter{0%{transform:translate(24px);opacity:0}to{transform:translate(0);opacity:1}}.drawer-enter{animation:drawer-enter .18s cubic-bezier(.16,1,.3,1)}@keyframes delegation-dash{to{stroke-dashoffset:-24}}.delegation-edge{animation:delegation-dash .9s linear infinite}@media(prefers-reduced-motion:reduce){.station-enter,.drawer-enter,.delegation-edge{animation:none}}.before\:absolute:before{content:var(--tw-content);position:absolute}.before\:bottom-3:before{content:var(--tw-content);bottom:.75rem}.before\:left-\[0\.72rem\]:before{content:var(--tw-content);left:.72rem}.before\:top-3:before{content:var(--tw-content);top:.75rem}.before\:w-px:before{content:var(--tw-content);width:1px}.before\:bg-po-border:before{content:var(--tw-content);--tw-bg-opacity: 1;background-color:rgb(61 58 55 / var(--tw-bg-opacity, 1))}.first\:mt-0:first-child{margin-top:0}.last\:mb-0:last-child{margin-bottom:0}.last\:border-b-0:last-child{border-bottom-width:0px}.hover\:border-po-accent:hover{--tw-border-opacity: 1;border-color:rgb(212 149 42 / var(--tw-border-opacity, 1))}.hover\:border-po-border-focus:hover{--tw-border-opacity: 1;border-color:rgb(92 87 82 / var(--tw-border-opacity, 1))}.hover\:bg-po-accent-muted:hover{--tw-bg-opacity: 1;background-color:rgb(154 109 32 / var(--tw-bg-opacity, 1))}.hover\:bg-po-surface-2:hover{--tw-bg-opacity: 1;background-color:rgb(44 42 40 / var(--tw-bg-opacity, 1))}.hover\:bg-po-surface-3:hover{--tw-bg-opacity: 1;background-color:rgb(54 52 50 / var(--tw-bg-opacity, 1))}.hover\:text-po-accent:hover{--tw-text-opacity: 1;color:rgb(212 149 42 / var(--tw-text-opacity, 1))}.hover\:text-po-error:hover{--tw-text-opacity: 1;color:rgb(196 92 74 / var(--tw-text-opacity, 1))}.hover\:text-po-text-primary:hover{--tw-text-opacity: 1;color:rgb(232 226 218 / var(--tw-text-opacity, 1))}.hover\:text-po-text-secondary:hover{--tw-text-opacity: 1;color:rgb(168 162 154 / var(--tw-text-opacity, 1))}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-100:hover{opacity:1}.focus\:border-po-accent:focus{--tw-border-opacity: 1;border-color:rgb(212 149 42 / var(--tw-border-opacity, 1))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.active\:cursor-grabbing:active{cursor:grabbing}.disabled\:opacity-30:disabled{opacity:.3}.disabled\:opacity-40:disabled{opacity:.4}.disabled\:opacity-50:disabled{opacity:.5}.group:hover .group-hover\:opacity-100{opacity:1}@media(min-width:640px){.sm\:inline{display:inline}}@media(min-width:1280px){.xl\:col-span-2{grid-column:span 2 / span 2}.xl\:min-h-\[30rem\]{min-height:30rem}.xl\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}@media(min-width:1536px){.\32xl\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
<link href="https://cdn.jsdelivr.net/npm/geist@1/dist/fonts/geist-sans/style.css" rel="stylesheet" />
|
|
10
10
|
<link href="https://cdn.jsdelivr.net/npm/geist@1/dist/fonts/geist-mono/style.css" rel="stylesheet" />
|
|
11
11
|
<title>PromptObjects</title>
|
|
12
|
-
<script type="module" crossorigin src="/assets/index-
|
|
13
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
12
|
+
<script type="module" crossorigin src="/assets/index-BjKlODDD.js"></script>
|
|
13
|
+
<link rel="stylesheet" crossorigin href="/assets/index-w3fpIb5c.css">
|
|
14
14
|
</head>
|
|
15
15
|
<body class="bg-po-bg text-po-text-primary font-ui">
|
|
16
16
|
<div id="root"></div>
|
|
@@ -8,51 +8,14 @@ module PromptObjects
|
|
|
8
8
|
# Handles WebSocket connections for real-time communication with the frontend.
|
|
9
9
|
# Subscribes to MessageBus for state updates and handles client messages.
|
|
10
10
|
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
# IMPORTANT: All user actions must provide immediate visual feedback BEFORE
|
|
14
|
-
# async operations complete. This makes the UI feel "alive" and responsive.
|
|
15
|
-
#
|
|
16
|
-
# When implementing new features, follow this pattern:
|
|
17
|
-
#
|
|
18
|
-
# 1. SWITCH CONTEXT FIRST
|
|
19
|
-
# When creating/switching threads, sessions, etc., send the context switch
|
|
20
|
-
# message FIRST so the frontend displays the new context immediately.
|
|
21
|
-
# Example: thread_created → frontend switches to new thread
|
|
22
|
-
#
|
|
23
|
-
# 2. UPDATE NAVIGATION
|
|
24
|
-
# Send updated lists (sessions, threads) immediately so sidebars reflect
|
|
25
|
-
# the change without waiting for async work to complete.
|
|
26
|
-
# Example: po_state with sessions list → ThreadsSidebar shows new thread
|
|
27
|
-
#
|
|
28
|
-
# 3. SHOW USER INPUT
|
|
29
|
-
# Send the user's input back immediately so they see it in the UI.
|
|
30
|
-
# Don't wait for the AI to respond before showing what the user typed.
|
|
31
|
-
# Example: session_updated with user message → chat shows "You: ..."
|
|
32
|
-
#
|
|
33
|
-
# 4. SHOW PROGRESS
|
|
34
|
-
# Update status indicators during work (thinking, calling_tool, etc.).
|
|
35
|
-
# Use streaming for incremental content when supported.
|
|
36
|
-
# Example: po_state status "thinking" → spinner/animation shown
|
|
37
|
-
#
|
|
38
|
-
# 5. CONFIRM COMPLETION
|
|
39
|
-
# Send final authoritative state after async work completes.
|
|
40
|
-
# Example: session_updated with full messages → final chat state
|
|
41
|
-
#
|
|
42
|
-
# Message flow for "send message with new thread":
|
|
43
|
-
# thread_created → switch to new thread
|
|
44
|
-
# po_state (sessions) → update sidebar
|
|
45
|
-
# session_updated → show user message
|
|
46
|
-
# po_state (thinking) → show progress indicator
|
|
47
|
-
# [stream chunks] → incremental AI response
|
|
48
|
-
# po_response → complete AI response
|
|
49
|
-
# session_updated → final messages
|
|
50
|
-
# po_state (idle) → clear progress indicator
|
|
11
|
+
# Runtime snapshots establish authoritative state; versioned events then
|
|
12
|
+
# incrementally update runs, messages, artifacts, and environment data.
|
|
51
13
|
class WebSocketHandler
|
|
52
14
|
def initialize(runtime:, connection:, app: nil)
|
|
53
15
|
@runtime = runtime
|
|
54
16
|
@connection = connection
|
|
55
17
|
@app = app
|
|
18
|
+
@write_mutex = Mutex.new
|
|
56
19
|
@subscribed = false
|
|
57
20
|
@bus_subscription = nil
|
|
58
21
|
end
|
|
@@ -68,8 +31,10 @@ module PromptObjects
|
|
|
68
31
|
# Send a message to this client (public for broadcasting).
|
|
69
32
|
def send_message(data)
|
|
70
33
|
json = JSON.generate(data)
|
|
71
|
-
@
|
|
72
|
-
|
|
34
|
+
@write_mutex.synchronize do
|
|
35
|
+
@connection.write(json)
|
|
36
|
+
@connection.flush
|
|
37
|
+
end
|
|
73
38
|
rescue => e
|
|
74
39
|
puts "WebSocket write error: #{e.message}" if ENV["DEBUG"]
|
|
75
40
|
end
|
|
@@ -141,7 +106,8 @@ module PromptObjects
|
|
|
141
106
|
name: @runtime.name,
|
|
142
107
|
path: @runtime.env_path,
|
|
143
108
|
po_count: @runtime.registry.prompt_objects.size,
|
|
144
|
-
primitive_count: @runtime.registry.primitives.size
|
|
109
|
+
primitive_count: @runtime.registry.primitives.size,
|
|
110
|
+
runtime_model: @runtime.runtime_model
|
|
145
111
|
}
|
|
146
112
|
)
|
|
147
113
|
|
|
@@ -161,6 +127,13 @@ module PromptObjects
|
|
|
161
127
|
)
|
|
162
128
|
end
|
|
163
129
|
|
|
130
|
+
# Autonomous scheduler tasks may have started before this client
|
|
131
|
+
# connected. Replay their latest state so countdowns are correct after
|
|
132
|
+
# page load or reconnect.
|
|
133
|
+
@runtime.autonomous_schedule_states.each_value do |schedule|
|
|
134
|
+
send_message(type: "autonomous_schedule", payload: schedule)
|
|
135
|
+
end
|
|
136
|
+
|
|
164
137
|
# Send pending human requests (notifications)
|
|
165
138
|
@runtime.human_queue.all_pending.each do |request|
|
|
166
139
|
send_message(
|
|
@@ -182,6 +155,8 @@ module PromptObjects
|
|
|
182
155
|
}
|
|
183
156
|
)
|
|
184
157
|
end
|
|
158
|
+
|
|
159
|
+
@runtime.runtime_snapshot_envelopes.each { |envelope| send_message(envelope) }
|
|
185
160
|
end
|
|
186
161
|
|
|
187
162
|
# === Client Message Handling ===
|
|
@@ -203,12 +178,20 @@ module PromptObjects
|
|
|
203
178
|
message = JSON.parse(data)
|
|
204
179
|
|
|
205
180
|
case message["type"]
|
|
181
|
+
when "enqueue_run"
|
|
182
|
+
handle_enqueue_run(message["payload"])
|
|
183
|
+
when "cancel_run"
|
|
184
|
+
handle_cancel_run(message["payload"])
|
|
185
|
+
when "create_runtime_thread"
|
|
186
|
+
handle_create_runtime_thread(message["payload"])
|
|
187
|
+
when "rename_runtime_thread"
|
|
188
|
+
handle_rename_runtime_thread(message["payload"])
|
|
189
|
+
when "archive_runtime_thread"
|
|
190
|
+
handle_archive_runtime_thread(message["payload"])
|
|
206
191
|
when "send_message"
|
|
207
192
|
handle_send_message(message["payload"])
|
|
208
193
|
when "respond_to_notification"
|
|
209
194
|
handle_notification_response(message["payload"])
|
|
210
|
-
when "update_po"
|
|
211
|
-
handle_update_po(message["payload"])
|
|
212
195
|
when "create_session"
|
|
213
196
|
handle_create_session(message["payload"])
|
|
214
197
|
when "switch_session"
|
|
@@ -223,6 +206,8 @@ module PromptObjects
|
|
|
223
206
|
handle_switch_llm(message["payload"])
|
|
224
207
|
when "update_prompt"
|
|
225
208
|
handle_update_prompt(message["payload"])
|
|
209
|
+
when "update_po_source"
|
|
210
|
+
handle_update_po_source(message["payload"])
|
|
226
211
|
when "get_session_usage"
|
|
227
212
|
handle_get_session_usage(message["payload"])
|
|
228
213
|
when "export_thread"
|
|
@@ -240,155 +225,150 @@ module PromptObjects
|
|
|
240
225
|
puts e.backtrace.first(5).join("\n") if ENV["DEBUG"]
|
|
241
226
|
end
|
|
242
227
|
|
|
243
|
-
def
|
|
244
|
-
|
|
228
|
+
def handle_enqueue_run(payload)
|
|
229
|
+
target_po = payload["target_po"]
|
|
245
230
|
content = payload["content"]
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
thread_id
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
type: "thread_created",
|
|
261
|
-
payload: {
|
|
262
|
-
target: po_name,
|
|
263
|
-
thread_id: thread_id,
|
|
264
|
-
thread_type: "root"
|
|
265
|
-
}
|
|
266
|
-
)
|
|
267
|
-
# Also send updated sessions list so ThreadsSidebar shows it immediately
|
|
268
|
-
send_message(
|
|
269
|
-
type: "po_state",
|
|
270
|
-
payload: {
|
|
271
|
-
name: po_name,
|
|
272
|
-
state: { sessions: po.list_sessions.map { |s| PromptObject.serialize_session(s) } }
|
|
273
|
-
}
|
|
274
|
-
)
|
|
275
|
-
end
|
|
276
|
-
|
|
277
|
-
# Capture the session_id at request start so response goes to correct session
|
|
278
|
-
request_session_id = po.session_id
|
|
279
|
-
|
|
280
|
-
# Update PO state to working AND send the user message for immediate UI feedback
|
|
281
|
-
send_message(
|
|
282
|
-
type: "po_state",
|
|
283
|
-
payload: { name: po_name, state: { status: "thinking" } }
|
|
231
|
+
client_request_id = payload["client_request_id"]
|
|
232
|
+
raise Error, "client_request_id is required" if client_request_id.to_s.empty?
|
|
233
|
+
raise Error, "content is required" if content.to_s.empty?
|
|
234
|
+
|
|
235
|
+
source = payload["source"] || { "type" => "human" }
|
|
236
|
+
from_source = protocol_source(source)
|
|
237
|
+
run = @runtime.enqueue_run(
|
|
238
|
+
target_po: target_po,
|
|
239
|
+
content: content,
|
|
240
|
+
from_source: from_source,
|
|
241
|
+
workspace_session_id: payload["workspace_session_id"],
|
|
242
|
+
thread_id: payload["thread_id"],
|
|
243
|
+
client_request_id: client_request_id,
|
|
244
|
+
deadline: payload["deadline"]
|
|
284
245
|
)
|
|
285
|
-
|
|
286
|
-
# Send immediate session update showing the user's message
|
|
287
|
-
# This gives instant feedback before the AI responds
|
|
288
|
-
# Include existing messages so we don't clear the chat when continuing a thread
|
|
289
|
-
existing_messages = session_messages(po, request_session_id)
|
|
290
|
-
new_user_message = { role: "user", content: content, from: "human" }
|
|
291
246
|
send_message(
|
|
292
|
-
type: "
|
|
293
|
-
payload:
|
|
294
|
-
target: po_name,
|
|
295
|
-
session_id: request_session_id,
|
|
296
|
-
messages: existing_messages + [new_user_message]
|
|
297
|
-
}
|
|
247
|
+
type: "run_acknowledged",
|
|
248
|
+
payload: serialize_runtime_value(run.to_h)
|
|
298
249
|
)
|
|
299
250
|
|
|
300
|
-
# Run in async context
|
|
301
251
|
Async do
|
|
302
|
-
# Use tui_mode: true so ask_human uses HumanQueue (blocking) instead of REPL mode
|
|
303
|
-
context = @runtime.context(tui_mode: true)
|
|
304
|
-
context.current_capability = "human"
|
|
305
|
-
|
|
306
|
-
# Set up callback for real-time tool call updates
|
|
307
|
-
# This fires after each tool call iteration in the receive loop
|
|
308
|
-
po.on_history_updated = ->(po_obj, session_id, history) {
|
|
309
|
-
send_message(
|
|
310
|
-
type: "session_updated",
|
|
311
|
-
payload: {
|
|
312
|
-
target: po_obj.name,
|
|
313
|
-
session_id: session_id,
|
|
314
|
-
messages: history.map { |m| PromptObject.serialize_message(m) }
|
|
315
|
-
}
|
|
316
|
-
)
|
|
317
|
-
}
|
|
318
|
-
|
|
319
252
|
begin
|
|
320
|
-
response =
|
|
321
|
-
|
|
322
|
-
# Auto-name the thread if it doesn't have a name yet
|
|
323
|
-
auto_name_thread_if_needed(po, request_session_id, content)
|
|
253
|
+
response = execute_blocking { @runtime.execute_run(run.id, tui_mode: false) }
|
|
254
|
+
next if response == :queued
|
|
324
255
|
|
|
325
|
-
# Send the complete response with session_id for correct routing
|
|
326
256
|
send_message(
|
|
327
257
|
type: "po_response",
|
|
328
|
-
payload: {
|
|
329
|
-
target: po_name,
|
|
330
|
-
session_id: request_session_id,
|
|
331
|
-
content: response
|
|
332
|
-
}
|
|
333
|
-
)
|
|
334
|
-
|
|
335
|
-
# Send session update for the session where messages were added
|
|
336
|
-
# This ensures the correct session is updated even if user switched
|
|
337
|
-
if request_session_id
|
|
338
|
-
send_message(
|
|
339
|
-
type: "session_updated",
|
|
340
|
-
payload: {
|
|
341
|
-
target: po_name,
|
|
342
|
-
session_id: request_session_id,
|
|
343
|
-
messages: session_messages(po, request_session_id)
|
|
344
|
-
}
|
|
345
|
-
)
|
|
346
|
-
end
|
|
347
|
-
|
|
348
|
-
# Also send updated sessions list (for thread panel)
|
|
349
|
-
send_message(
|
|
350
|
-
type: "po_state",
|
|
351
|
-
payload: {
|
|
352
|
-
name: po_name,
|
|
353
|
-
state: { sessions: po.list_sessions.map { |s| PromptObject.serialize_session(s) } }
|
|
354
|
-
}
|
|
258
|
+
payload: { target: target_po, thread_id: run.thread_id, content: response }
|
|
355
259
|
)
|
|
356
260
|
rescue => e
|
|
357
|
-
send_error("Error from #{
|
|
358
|
-
|
|
359
|
-
# Broadcast rich error context for diagnostics
|
|
360
|
-
llm_config = @runtime.llm_config
|
|
261
|
+
send_error("Error from #{target_po}: #{e.message}")
|
|
361
262
|
send_message(
|
|
362
263
|
type: "llm_error",
|
|
363
264
|
payload: {
|
|
364
|
-
po_name:
|
|
365
|
-
provider:
|
|
366
|
-
model:
|
|
265
|
+
po_name: target_po,
|
|
266
|
+
provider: run.provider,
|
|
267
|
+
model: run.model,
|
|
367
268
|
error: e.message,
|
|
368
269
|
error_class: e.class.name
|
|
369
270
|
}
|
|
370
271
|
)
|
|
371
|
-
ensure
|
|
372
|
-
# Clean up the callback
|
|
373
|
-
po.on_history_updated = nil
|
|
374
|
-
|
|
375
|
-
# Update PO state back to idle (status only, not session data)
|
|
376
|
-
send_message(
|
|
377
|
-
type: "po_state",
|
|
378
|
-
payload: { name: po_name, state: { status: "idle" } }
|
|
379
|
-
)
|
|
380
272
|
end
|
|
381
273
|
end
|
|
382
274
|
end
|
|
383
275
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
276
|
+
def handle_cancel_run(payload)
|
|
277
|
+
run_id = payload["run_id"]
|
|
278
|
+
raise Error, "run_id is required" if run_id.to_s.empty?
|
|
279
|
+
|
|
280
|
+
cancelled = @runtime.cancel_run(run_id, reason: payload["reason"] || "client request")
|
|
281
|
+
send_message(
|
|
282
|
+
type: "run_cancellation_acknowledged",
|
|
283
|
+
payload: { run_id: run_id, cancelled: cancelled }
|
|
284
|
+
)
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
def handle_create_runtime_thread(payload)
|
|
288
|
+
thread = @runtime.create_runtime_thread(
|
|
289
|
+
workspace_session_id: payload["workspace_session_id"],
|
|
290
|
+
po_name: payload["po_name"],
|
|
291
|
+
thread_type: payload["thread_type"] || "human",
|
|
292
|
+
name: payload["name"]
|
|
293
|
+
)
|
|
294
|
+
send_message(
|
|
295
|
+
type: "runtime_thread_created",
|
|
296
|
+
payload: serialize_runtime_value(thread.to_h)
|
|
297
|
+
)
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
def handle_rename_runtime_thread(payload)
|
|
301
|
+
thread = @runtime.rename_runtime_thread(payload["thread_id"], name: payload["name"])
|
|
302
|
+
send_message(
|
|
303
|
+
type: "runtime_thread_renamed",
|
|
304
|
+
payload: serialize_runtime_value(thread.to_h)
|
|
305
|
+
)
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
def handle_archive_runtime_thread(payload)
|
|
309
|
+
thread = @runtime.archive_runtime_thread(payload["thread_id"])
|
|
310
|
+
send_message(
|
|
311
|
+
type: "runtime_thread_archived",
|
|
312
|
+
payload: serialize_runtime_value(thread.to_h)
|
|
313
|
+
)
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
def handle_send_message(payload)
|
|
317
|
+
po_name = payload["target"]
|
|
318
|
+
content = payload["content"]
|
|
319
|
+
new_thread = payload["new_thread"] # If true, create a new thread first
|
|
320
|
+
|
|
321
|
+
po = @runtime.registry.get(po_name)
|
|
322
|
+
|
|
323
|
+
unless po.is_a?(PromptObject)
|
|
324
|
+
send_error("Unknown prompt object: #{po_name}")
|
|
325
|
+
return
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
workspace_id = payload["workspace_session_id"] || @runtime.active_session(initiated_by: "human")[:id]
|
|
329
|
+
thread_id = payload["thread_id"] || payload["session_id"]
|
|
330
|
+
if new_thread
|
|
331
|
+
thread = @runtime.create_runtime_thread(
|
|
332
|
+
workspace_session_id: workspace_id,
|
|
333
|
+
po_name: po_name,
|
|
334
|
+
thread_type: "human",
|
|
335
|
+
name: payload["name"]
|
|
336
|
+
)
|
|
337
|
+
thread_id = thread.id
|
|
338
|
+
send_message(type: "runtime_thread_created", payload: serialize_runtime_value(thread.to_h))
|
|
339
|
+
end
|
|
340
|
+
handle_enqueue_run(
|
|
341
|
+
"target_po" => po_name,
|
|
342
|
+
"content" => content,
|
|
343
|
+
"client_request_id" => payload["client_request_id"] || SecureRandom.uuid,
|
|
344
|
+
"workspace_session_id" => workspace_id,
|
|
345
|
+
"thread_id" => thread_id,
|
|
346
|
+
"source" => { "type" => "human" }
|
|
347
|
+
)
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
def execute_blocking(&block)
|
|
351
|
+
return @app.blocking_executor.call(&block) if @app&.respond_to?(:blocking_executor)
|
|
352
|
+
|
|
353
|
+
block.call
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
def protocol_source(source)
|
|
357
|
+
type = source.fetch("type", "human")
|
|
358
|
+
name = source["name"]
|
|
359
|
+
return type if %w[human api].include?(type)
|
|
360
|
+
return "#{type}:#{name}" if %w[timer artifact service].include?(type) && !name.to_s.empty?
|
|
387
361
|
|
|
388
|
-
|
|
389
|
-
|
|
362
|
+
raise Error, "Unsupported run source: #{type}"
|
|
363
|
+
end
|
|
390
364
|
|
|
391
|
-
|
|
365
|
+
def serialize_runtime_value(value)
|
|
366
|
+
case value
|
|
367
|
+
when Time then value.iso8601
|
|
368
|
+
when Hash then value.to_h { |key, nested| [key, serialize_runtime_value(nested)] }
|
|
369
|
+
when Array then value.map { |nested| serialize_runtime_value(nested) }
|
|
370
|
+
else value
|
|
371
|
+
end
|
|
392
372
|
end
|
|
393
373
|
|
|
394
374
|
def handle_notification_response(payload)
|
|
@@ -403,18 +383,39 @@ module PromptObjects
|
|
|
403
383
|
)
|
|
404
384
|
end
|
|
405
385
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
386
|
+
# Replace a PO's entire .md source (frontmatter + body) from the editor.
|
|
387
|
+
# Re-parses in place so autonomous/delegates_to/tools changes take effect,
|
|
388
|
+
# and re-keys the registry if the frontmatter name changed.
|
|
389
|
+
def handle_update_po_source(payload)
|
|
390
|
+
po_name = payload["target"]
|
|
391
|
+
source = payload["source"]
|
|
409
392
|
|
|
410
393
|
po = @runtime.registry.get(po_name)
|
|
411
394
|
return send_error("Unknown prompt object: #{po_name}") unless po.is_a?(PromptObject)
|
|
395
|
+
return send_error("#{po_name} has no source file to edit") unless po.path
|
|
396
|
+
|
|
397
|
+
old_name = po.name
|
|
398
|
+
if po.update_source(source)
|
|
399
|
+
if po.name != old_name
|
|
400
|
+
@runtime.registry.unregister(old_name)
|
|
401
|
+
@runtime.registry.register(po)
|
|
402
|
+
|
|
403
|
+
# Keep the filename in sync with the new name so the file and the
|
|
404
|
+
# registered PO don't diverge (update_source wrote to the old path).
|
|
405
|
+
new_path = File.join(File.dirname(po.path), "#{po.name}.md")
|
|
406
|
+
po.relocate(new_path)
|
|
407
|
+
end
|
|
412
408
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
409
|
+
# Broadcasts full PO state (incl. the re-read source) to all clients.
|
|
410
|
+
@runtime.notify_po_modified(po)
|
|
411
|
+
|
|
412
|
+
send_message(
|
|
413
|
+
type: "po_source_updated",
|
|
414
|
+
payload: { target: po.name, previous_name: old_name, success: true }
|
|
415
|
+
)
|
|
416
|
+
else
|
|
417
|
+
send_error("Failed to update #{po_name} — check the YAML frontmatter")
|
|
418
|
+
end
|
|
418
419
|
end
|
|
419
420
|
|
|
420
421
|
def handle_create_session(payload)
|
|
@@ -424,22 +425,25 @@ module PromptObjects
|
|
|
424
425
|
po = @runtime.registry.get(po_name)
|
|
425
426
|
return send_error("Unknown prompt object: #{po_name}") unless po.is_a?(PromptObject)
|
|
426
427
|
|
|
427
|
-
|
|
428
|
+
workspace_id = payload["workspace_session_id"] || @runtime.active_session(initiated_by: "human")[:id]
|
|
429
|
+
thread = @runtime.create_runtime_thread(
|
|
430
|
+
workspace_session_id: workspace_id,
|
|
431
|
+
po_name: po_name,
|
|
432
|
+
thread_type: "human",
|
|
433
|
+
name: session_name
|
|
434
|
+
)
|
|
428
435
|
|
|
429
436
|
send_message(
|
|
430
437
|
type: "session_created",
|
|
431
438
|
payload: {
|
|
432
439
|
target: po_name,
|
|
433
|
-
session_id:
|
|
440
|
+
session_id: thread.id,
|
|
441
|
+
thread_id: thread.id,
|
|
442
|
+
workspace_session_id: thread.workspace_session_id,
|
|
434
443
|
name: session_name
|
|
435
444
|
}
|
|
436
445
|
)
|
|
437
|
-
|
|
438
|
-
# Also send updated PO state
|
|
439
|
-
send_message(
|
|
440
|
-
type: "po_state",
|
|
441
|
-
payload: { name: po_name, state: po.to_state_hash(registry: @runtime.registry) }
|
|
442
|
-
)
|
|
446
|
+
send_message(type: "runtime_thread_created", payload: serialize_runtime_value(thread.to_h))
|
|
443
447
|
end
|
|
444
448
|
|
|
445
449
|
def handle_switch_session(payload)
|
|
@@ -449,17 +453,13 @@ module PromptObjects
|
|
|
449
453
|
po = @runtime.registry.get(po_name)
|
|
450
454
|
return send_error("Unknown prompt object: #{po_name}") unless po.is_a?(PromptObject)
|
|
451
455
|
|
|
452
|
-
|
|
456
|
+
thread = Repositories::ThreadRepository.new(@runtime.session_store).get(session_id)
|
|
457
|
+
if thread && thread.po_name == po_name
|
|
453
458
|
send_message(
|
|
454
459
|
type: "session_switched",
|
|
455
460
|
payload: { target: po_name, session_id: session_id }
|
|
456
461
|
)
|
|
457
462
|
|
|
458
|
-
# Send updated PO state with new session's messages
|
|
459
|
-
send_message(
|
|
460
|
-
type: "po_state",
|
|
461
|
-
payload: { name: po_name, state: po.to_state_hash(registry: @runtime.registry) }
|
|
462
|
-
)
|
|
463
463
|
else
|
|
464
464
|
send_error("Could not switch to session: #{session_id}")
|
|
465
465
|
end
|
|
@@ -473,22 +473,17 @@ module PromptObjects
|
|
|
473
473
|
po = @runtime.registry.get(po_name)
|
|
474
474
|
return send_error("Unknown prompt object: #{po_name}") unless po.is_a?(PromptObject)
|
|
475
475
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
thread_id: thread_id,
|
|
483
|
-
name: thread_name,
|
|
484
|
-
thread_type: thread_type
|
|
485
|
-
}
|
|
476
|
+
workspace_id = payload["workspace_session_id"] || @runtime.active_session(initiated_by: "human")[:id]
|
|
477
|
+
thread = @runtime.create_runtime_thread(
|
|
478
|
+
workspace_session_id: workspace_id,
|
|
479
|
+
po_name: po_name,
|
|
480
|
+
thread_type: thread_type == "root" ? "human" : thread_type,
|
|
481
|
+
name: thread_name
|
|
486
482
|
)
|
|
487
483
|
|
|
488
|
-
# Also send updated PO state
|
|
489
484
|
send_message(
|
|
490
|
-
type: "
|
|
491
|
-
payload:
|
|
485
|
+
type: "runtime_thread_created",
|
|
486
|
+
payload: serialize_runtime_value(thread.to_h)
|
|
492
487
|
)
|
|
493
488
|
end
|
|
494
489
|
|
|
@@ -666,9 +661,9 @@ module PromptObjects
|
|
|
666
661
|
def request_to_hash(request)
|
|
667
662
|
{
|
|
668
663
|
id: request.id,
|
|
669
|
-
po_name: request.capability,
|
|
670
|
-
type:
|
|
671
|
-
message: request.question,
|
|
664
|
+
po_name: request.capability,
|
|
665
|
+
type: request.capability,
|
|
666
|
+
message: request.question,
|
|
672
667
|
options: request.options || []
|
|
673
668
|
}
|
|
674
669
|
end
|