prompt_objects 0.4.0 → 0.5.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 (63) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +17 -0
  3. data/CLAUDE.md +112 -44
  4. data/README.md +5 -0
  5. data/frontend/index.html +5 -1
  6. data/frontend/src/App.tsx +70 -78
  7. data/frontend/src/canvas/CanvasView.tsx +5 -5
  8. data/frontend/src/canvas/constants.ts +31 -31
  9. data/frontend/src/canvas/inspector/InspectorPanel.tsx +4 -4
  10. data/frontend/src/canvas/inspector/POInspector.tsx +35 -35
  11. data/frontend/src/canvas/inspector/ToolCallInspector.tsx +13 -13
  12. data/frontend/src/canvas/nodes/PONode.ts +2 -2
  13. data/frontend/src/components/ContextMenu.tsx +5 -4
  14. data/frontend/src/components/Inspector.tsx +232 -0
  15. data/frontend/src/components/MarkdownMessage.tsx +22 -20
  16. data/frontend/src/components/MethodList.tsx +90 -0
  17. data/frontend/src/components/ModelSelector.tsx +13 -14
  18. data/frontend/src/components/NotificationPanel.tsx +29 -33
  19. data/frontend/src/components/ObjectList.tsx +78 -0
  20. data/frontend/src/components/PaneSlot.tsx +30 -0
  21. data/frontend/src/components/SourcePane.tsx +202 -0
  22. data/frontend/src/components/SystemBar.tsx +74 -0
  23. data/frontend/src/components/Transcript.tsx +76 -0
  24. data/frontend/src/components/UsagePanel.tsx +27 -27
  25. data/frontend/src/components/Workspace.tsx +260 -0
  26. data/frontend/src/components/index.ts +10 -9
  27. data/frontend/src/hooks/useResize.ts +55 -0
  28. data/frontend/src/hooks/useWebSocket.ts +28 -0
  29. data/frontend/src/index.css +28 -10
  30. data/frontend/src/store/index.ts +4 -0
  31. data/frontend/src/types/index.ts +2 -0
  32. data/frontend/tailwind.config.js +28 -9
  33. data/lib/prompt_objects/capability.rb +23 -1
  34. data/lib/prompt_objects/connectors/mcp.rb +2 -16
  35. data/lib/prompt_objects/llm/openai_adapter.rb +22 -0
  36. data/lib/prompt_objects/mcp/tools/inspect_po.rb +1 -31
  37. data/lib/prompt_objects/mcp/tools/list_prompt_objects.rb +1 -6
  38. data/lib/prompt_objects/prompt_object.rb +126 -0
  39. data/lib/prompt_objects/server/api/routes.rb +3 -48
  40. data/lib/prompt_objects/server/public/assets/{index-xvyeb-5Z.js → index-D1myxE0l.js} +211 -211
  41. data/lib/prompt_objects/server/public/assets/index-DdCcwC-Z.css +1 -0
  42. data/lib/prompt_objects/server/public/index.html +7 -3
  43. data/lib/prompt_objects/server/websocket_handler.rb +23 -100
  44. data/lib/prompt_objects/server.rb +6 -62
  45. data/prompt_objects.gemspec +1 -1
  46. data/templates/arc-agi-1/primitives/find_objects.rb +1 -1
  47. data/templates/arc-agi-1/primitives/grid_diff.rb +2 -2
  48. data/templates/arc-agi-1/primitives/grid_info.rb +1 -1
  49. data/templates/arc-agi-1/primitives/grid_transform.rb +1 -1
  50. data/templates/arc-agi-1/primitives/render_grid.rb +1 -0
  51. data/templates/arc-agi-1/primitives/test_solution.rb +3 -0
  52. metadata +12 -13
  53. data/frontend/src/components/CapabilitiesPanel.tsx +0 -141
  54. data/frontend/src/components/ChatPanel.tsx +0 -296
  55. data/frontend/src/components/Dashboard.tsx +0 -83
  56. data/frontend/src/components/Header.tsx +0 -153
  57. data/frontend/src/components/MessageBus.tsx +0 -56
  58. data/frontend/src/components/POCard.tsx +0 -56
  59. data/frontend/src/components/PODetail.tsx +0 -124
  60. data/frontend/src/components/PromptPanel.tsx +0 -156
  61. data/frontend/src/components/SessionsPanel.tsx +0 -174
  62. data/frontend/src/components/ThreadsSidebar.tsx +0 -163
  63. data/lib/prompt_objects/server/public/assets/index-6y64NXFy.css +0 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 92cf8b8492d42dee0adb37a4fb41692e6b228ab7a364f9176444c60cd81eb63d
4
- data.tar.gz: 45e8828d53f7b9c4893fb6374535bbb099c77e92371a3e111bda07d9e35db93e
3
+ metadata.gz: 6dd3c14942ef80d1979ded542802ad9d1a24b32cd479aa4ba0a08bb0bf2e349d
4
+ data.tar.gz: 3642d5424aa827754dfefde82a9e9e5c770935ebf08e36cfc429d8f86e5e8502
5
5
  SHA512:
6
- metadata.gz: 123244bee715f744b7b377bbf4e2912ed28cbb966ce6b92bb7d43a5ea053bf2460e7bb10c35acf09ab5f4cfa8f0afeeb78198be62acfbde44b86f3c5f0559deb
7
- data.tar.gz: 15ee859102e87e971df1e38b756d776a0f913148554974fc2fe9ae79b12853cf9028793aadfaabb92aa1d74961739c97a51c258c0e9e2a38cbf77fb5f931b46d
6
+ metadata.gz: f4e4449d2a8764a762c13ea14b6ccac17b07d1ae06a053a94e5b66ce7ebf38b8ca2903c140f0bb06810e09fa830bdac1dcfb8485d2f6f7fe32f7b0dffdf374bb
7
+ data.tar.gz: 1faa31a8c32f067cdfb133c06e6c3d45e55fec3381d86f649e8845b9d2c58bc367e2bed3821cbec4a92a18776b723ccf69c276f9e913347198816005c4608353
data/CHANGELOG.md CHANGED
@@ -2,6 +2,23 @@
2
2
 
3
3
  All notable changes to PromptObjects are documented in this file.
4
4
 
5
+ ## [0.5.0] - 2026-02-13
6
+
7
+ ### Added
8
+
9
+ - **Smalltalk System Browser redesign** — Complete frontend overhaul replacing the chat-app UI with a multi-pane object browser. POs are treated as live objects in a running image: permanent ObjectList (left pane), multi-pane Inspector with MethodList + SourcePane, REPL-style Workspace, and bottom Transcript. Warm charcoal + amber palette with Geist fonts. All panels resizable via drag handles.
10
+ - **Collapsible inspector top pane** — The Methods + Source pane has a thin header bar with a collapse/expand toggle. When collapsed, the Workspace fills the full inspector height. Collapse state persists across PO switches.
11
+ - **Source entry in method list** — A "Source" entry at the top of the method list provides a clear way to navigate back to the PO's prompt after inspecting a capability.
12
+ - **Dynamic Ollama model discovery** — LLM config now queries the Ollama API for installed models instead of using a static list.
13
+
14
+ ### Fixed
15
+
16
+ - **Capabilities disappearing on file save** — `po_modified` events were sending capabilities as plain string names instead of rich objects, overwriting the store. All serialization paths now emit consistent `{name, description, parameters}` objects.
17
+ - **Centralized PO serialization** — Moved duplicated state/message/session serialization from WebSocketHandler and API routes into `PromptObject` (`to_state_hash`, `to_summary_hash`, `to_inspect_hash`). Eliminates inconsistent serialization as a class of bug.
18
+ - **Missing items field in array tool schemas** — LLM APIs reject array parameters without an `items` field. Added a defensive sanitizer in `Capability#descriptor` as a fallback.
19
+ - **OpenAI adapter error details** — 4xx errors from Ollama now surface the actual rejection reason instead of just "status 400".
20
+ - **All WebSocket message types handled** — Added frontend handlers for `prompt_updated`, `llm_error`, `session_created`, and `session_switched`. Removed defensive normalization workarounds.
21
+
5
22
  ## [0.4.0] - 2026-02-11
6
23
 
7
24
  ### Added
data/CLAUDE.md CHANGED
@@ -6,17 +6,21 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
6
6
 
7
7
  **PromptObjects** is a Ruby framework where markdown files with LLM-backed behavior act as first-class autonomous entities. The core insight: **everything is a capability**—primitives (Ruby code) and Prompt-Objects (markdown files) share the same interface, differing only in interpretation complexity.
8
8
 
9
- **Current Status**: Design phase—`design-doc-v2.md` contains the full specification, `IMPLEMENTATION_PLAN.md` has detailed phased build plans. No implementation code exists yet.
9
+ **Current Status**: v0.5.0 — The core framework is fully implemented and functional. The original 6-phase implementation plan is complete. Active development is focused on visualization, developer experience, and exploring new primitives. See `CHANGELOG.md` for release history and `design-doc-v2.md` / `IMPLEMENTATION_PLAN.md` for original design context.
10
10
 
11
11
  ## Architecture
12
12
 
13
13
  ```
14
- ENVIRONMENT
14
+ RUNTIME (Environment)
15
15
  ├── CAPABILITY REGISTRY
16
16
  │ ├── PRIMITIVES (Ruby) - deterministic interpretation
17
- └── PROMPT-OBJECTS (Markdown) - semantic interpretation via LLM
18
- ├── MESSAGE BUS - routes messages, logs for visualization
19
- └── TERMINAL UI (Charm) - capability bar, message log, conversation, input
17
+ ├── PROMPT-OBJECTS (Markdown) - semantic interpretation via LLM
18
+ │ └── UNIVERSAL CAPABILITIES - available to all POs automatically
19
+ ├── MESSAGE BUS - routes messages, logs to SQLite for replay
20
+ ├── SESSION STORE (SQLite) - persistent conversation threads, delegation tracking
21
+ ├── HUMAN QUEUE - non-blocking ask_human requests
22
+ ├── WEB SERVER (Sinatra + WebSocket) - serves React frontend
23
+ └── MCP SERVER - exposes POs as tools via Model Context Protocol
20
24
  ```
21
25
 
22
26
  ### Unified Capability Interface
@@ -53,56 +57,120 @@ You are a careful, thoughtful file reader...
53
57
  Available to all Prompt-Objects automatically (no frontmatter declaration needed):
54
58
  - `ask_human` - pause for human input/confirmation
55
59
  - `think` - internal reasoning (not shown to human)
56
- - `request_capability` - ask environment for new capability
60
+ - `create_capability` / `add_capability` / `remove_capability` - self-modification
61
+ - `list_capabilities` / `list_primitives` - introspection
62
+ - `create_primitive` / `add_primitive` / `delete_primitive` / `verify_primitive` / `modify_primitive` / `request_primitive` - primitive management
63
+ - `modify_prompt` - rewrite own system prompt at runtime
57
64
 
58
- ## Technology Stack
59
-
60
- - **Ruby** - core implementation
61
- - **LLM APIs** - OpenAI, Anthropic, Gemini (adapter pattern)
62
- - **Charm** - Terminal UI (Bubble Tea for interaction, Lipgloss for styling, Glamour for markdown)
63
- - **MCP** - Model Context Protocol integration
65
+ ### PO-to-PO Delegation
66
+ When a PO calls another PO, the system creates an isolated delegation thread in the target PO. The caller's context is tracked so messages show correct provenance. Delegation start/complete events are broadcast via WebSocket for real-time UI updates.
64
67
 
65
- ### TUI Architecture Note
68
+ ## Technology Stack
66
69
 
67
- The TUI uses a **single Bubble Tea program** with internal screen states (picker, wizard, main). Do NOT run multiple sequential `Bubbletea.run` calls—this causes terminal state corruption. See `docs/phase-8-environments.md` for details.
70
+ - **Ruby** (>= 3.2, tested through Ruby 4) - core implementation
71
+ - **LLM APIs** - OpenAI, Anthropic, Gemini, Ollama, OpenRouter (adapter pattern via `LLM::Factory`)
72
+ - **Sinatra** - web server for REST API and static file serving
73
+ - **faye-websocket** - real-time WebSocket communication
74
+ - **React + TypeScript** - web frontend (dashboard, chat, capabilities panel)
75
+ - **Three.js** - spatial canvas visualization (force-directed PO graph)
76
+ - **SQLite** - session persistence and event log storage
77
+ - **MCP** - Model Context Protocol server mode
68
78
 
69
- ## Planned File Structure
79
+ ## File Structure
70
80
 
71
81
  ```
72
82
  prompt_objects/
73
- ├── exe/prompt_objects # CLI entrypoint
83
+ ├── exe/prompt_objects # CLI entrypoint
74
84
  ├── lib/
75
- │ ├── prompt_objects.rb # Main entry
85
+ │ ├── prompt_objects.rb # Main entry, requires all modules
76
86
  │ └── prompt_objects/
77
- │ ├── environment.rb # Runtime container
78
- │ ├── capability.rb # Base interface
79
- │ ├── prompt_object.rb # PO implementation
80
- │ ├── primitive.rb # Primitive tool wrapper
81
- │ ├── loader.rb # Parses frontmatter + body
82
- │ ├── registry.rb # Capability registration
83
- │ ├── message_bus.rb # Message routing and logging
84
- │ ├── llm/ # LLM adapters
85
- │ ├── primitives/ # Built-in primitives (read_file, etc.)
86
- │ ├── universal/ # Universal capabilities
87
- │ ├── mcp/ # MCP integration
88
- └── ui/ # Charm-based terminal UI
89
- ├── objects/ # Where Prompt-Objects live (.md files)
90
- └── primitives/ # Optional user-defined primitives
87
+ │ ├── environment.rb # Runtime container (registry, bus, LLM, sessions)
88
+ │ ├── capability.rb # Base capability interface
89
+ │ ├── prompt_object.rb # PO implementation (LLM conversation loop)
90
+ │ ├── primitive.rb # Primitive tool wrapper
91
+ │ ├── loader.rb # Parses frontmatter + body from .md files
92
+ │ ├── registry.rb # Capability registration and lookup
93
+ │ ├── message_bus.rb # Message routing, logging, SQLite persistence
94
+ │ ├── human_queue.rb # Non-blocking human interaction queue
95
+ │ ├── cli.rb # CLI command definitions
96
+ │ ├── server.rb # Web server setup
97
+ │ ├── server/
98
+ │ ├── app.rb # Sinatra application
99
+ │ │ ├── api/routes.rb # REST API endpoints
100
+ │ │ ├── websocket_handler.rb # WebSocket event handling
101
+ │ │ └── file_watcher.rb # Live .md file change detection
102
+ │ ├── llm/
103
+ │ │ ├── factory.rb # Provider/model selection
104
+ │ │ ├── response.rb # Unified response object
105
+ │ │ ├── pricing.rb # Token cost calculation
106
+ │ │ ├── openai_adapter.rb # OpenAI + Ollama + OpenRouter
107
+ │ │ ├── anthropic_adapter.rb
108
+ │ │ └── gemini_adapter.rb
109
+ │ ├── primitives/ # Built-in: read_file, list_files, write_file, http_get
110
+ │ ├── universal/ # 14 universal capabilities (see list above)
111
+ │ ├── connectors/ # Interface adapters (base, mcp)
112
+ │ ├── mcp/ # MCP server and tool definitions
113
+ │ ├── session/
114
+ │ │ └── store.rb # SQLite session/thread persistence
115
+ │ └── environment/
116
+ │ ├── manager.rb # Create/list/clone environments
117
+ │ ├── manifest.rb # Environment metadata (manifest.yml)
118
+ │ ├── git.rb # Auto-commit integration
119
+ │ ├── exporter.rb # Environment export
120
+ │ └── importer.rb # Environment import
121
+ ├── frontend/ # React + TypeScript web UI
122
+ │ └── src/
123
+ │ ├── App.tsx
124
+ │ ├── components/ # Dashboard, chat, capabilities panel
125
+ │ ├── canvas/ # Three.js spatial visualization
126
+ │ ├── hooks/ # WebSocket, state management hooks
127
+ │ ├── store/ # Frontend state
128
+ │ └── types/ # TypeScript type definitions
129
+ ├── objects/ # Default POs: greeter, reader, coordinator
130
+ ├── templates/ # Environment templates (basic, developer, writer, arc-agi-1, etc.)
131
+ ├── tools/ # Development tooling
132
+ └── test/ # Unit and integration tests
91
133
  ```
92
134
 
93
- ## Implementation Phases
94
-
95
- 1. **Core Loop**: Capability base, PromptObject, Loader, single LLM adapter, simple REPL
96
- 2. **Primitives & Binding**: Primitive base, built-in primitives, Registry
97
- 3. **Multi-Capability**: Message bus with logging, PO↔PO communication
98
- 4. **Self-Modification**: Universal capabilities including create_capability
99
- 5. **Polish & UI**: Full Charm integration, all UI components
100
- 6. **Demo Ready**: Error handling, testing, practice runs
101
-
102
135
  ## Key Concepts
103
136
 
104
137
  - **Semantic Binding**: Natural language → capability call (visible in message log)
105
- - **PO↔PO Communication**: Prompt-Objects can call each other as capabilities
106
- - **Self-Modification**: System can create new Prompt-Objects at runtime (with human approval)
107
- - **Human-in-the-Loop**: POs ask for confirmation on dangerous actions, clarification when ambiguous
108
- - **Non-blocking Human Queue**: When POs need human input (`ask_human`), they suspend (via Fibers) and queue a notification. Multiple POs can wait simultaneously. Human responds via notification panel, PO resumes automatically.
138
+ - **PO↔PO Communication**: Prompt-Objects call each other as capabilities through isolated delegation threads
139
+ - **Self-Modification**: POs can create new POs and primitives at runtime (with human approval)
140
+ - **Human-in-the-Loop**: POs use `ask_human` to pause and queue notifications; human responds asynchronously via the web UI
141
+ - **Environments**: Isolated collections of POs with their own sessions, git history, and configuration. Created from templates via CLI.
142
+ - **Thread Export**: Conversation threads (including delegation chains) exportable as Markdown or JSON
143
+
144
+ ## Development
145
+
146
+ ```bash
147
+ # Install dependencies
148
+ bundle install
149
+ cd frontend && npm install && cd ..
150
+
151
+ # Run tests
152
+ bundle exec rake test
153
+
154
+ # Serve an environment with web UI
155
+ prompt_objects serve <env-name> --open
156
+
157
+ # Environment management
158
+ prompt_objects env create <name> --template basic
159
+ prompt_objects env list
160
+ prompt_objects env info <name>
161
+ ```
162
+
163
+ ## Releases
164
+
165
+ Always tag releases. Pushing a version tag triggers the Discord notification workflow (`.github/workflows/discord-release.yml`), which extracts the matching section from `CHANGELOG.md` and posts it.
166
+
167
+ ```bash
168
+ # 1. Update version in prompt_objects.gemspec
169
+ # 2. Add changelog entry under ## [X.Y.Z] - YYYY-MM-DD
170
+ # 3. Commit: "Release vX.Y.Z — short description"
171
+ # 4. Tag and push:
172
+ git tag vX.Y.Z
173
+ git push && git push origin vX.Y.Z
174
+ # 5. Publish gem:
175
+ gem build prompt_objects.gemspec && gem push prompt_objects-X.Y.Z.gem
176
+ ```
data/README.md CHANGED
@@ -12,6 +12,11 @@ Prompt Objects applies this to AI. Instead of treating LLMs as external APIs you
12
12
 
13
13
  This is a new computing primitive: semantic late binding at runtime, where natural language becomes the interface between intelligent components.
14
14
 
15
+ Blog Posts
16
+ ==========
17
+
18
+ - [What if we took message passing seriously?](https://worksonmymachine.ai/p/what-if-we-took-message-passing-seriously) — The origin story and motivation behind Prompt Objects.
19
+
15
20
  Who
16
21
  ===
17
22
 
data/frontend/index.html CHANGED
@@ -4,9 +4,13 @@
4
4
  <meta charset="UTF-8" />
5
5
  <link rel="icon" type="image/svg+xml" href="/vite.svg" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin />
8
+ <link href="https://cdn.jsdelivr.net/fontsource/fonts/geist@latest/latin-400-normal.woff2" as="font" type="font/woff2" crossorigin />
9
+ <link href="https://cdn.jsdelivr.net/npm/geist@1/dist/fonts/geist-sans/style.css" rel="stylesheet" />
10
+ <link href="https://cdn.jsdelivr.net/npm/geist@1/dist/fonts/geist-mono/style.css" rel="stylesheet" />
7
11
  <title>PromptObjects</title>
8
12
  </head>
9
- <body class="bg-po-bg text-gray-100">
13
+ <body class="bg-po-bg text-po-text-primary font-ui">
10
14
  <div id="root"></div>
11
15
  <script type="module" src="/src/main.tsx"></script>
12
16
  </body>
data/frontend/src/App.tsx CHANGED
@@ -1,12 +1,11 @@
1
- import { useState } from 'react'
2
1
  import { useWebSocket } from './hooks/useWebSocket'
3
2
  import { useStore, useSelectedPO } from './store'
4
- import { Header } from './components/Header'
5
- import { Dashboard } from './components/Dashboard'
6
- import { PODetail } from './components/PODetail'
7
- import { MessageBus } from './components/MessageBus'
3
+ import { useResize } from './hooks/useResize'
4
+ import { SystemBar } from './components/SystemBar'
5
+ import { ObjectList } from './components/ObjectList'
6
+ import { Inspector } from './components/Inspector'
7
+ import { Transcript } from './components/Transcript'
8
8
  import { NotificationPanel } from './components/NotificationPanel'
9
- import { ThreadsSidebar } from './components/ThreadsSidebar'
10
9
  import { UsagePanel } from './components/UsagePanel'
11
10
  import { CanvasView } from './canvas/CanvasView'
12
11
 
@@ -15,89 +14,82 @@ export default function App() {
15
14
  useWebSocket()
16
15
  const { selectedPO, busOpen, notifications, usageData, clearUsageData, currentView } = useStore()
17
16
  const selectedPOData = useSelectedPO()
18
- const [splitView, setSplitView] = useState(true) // Default to split view
17
+
18
+ const objectListResize = useResize({
19
+ direction: 'horizontal',
20
+ initialSize: 192,
21
+ minSize: 120,
22
+ maxSize: 320,
23
+ })
24
+
25
+ const transcriptResize = useResize({
26
+ direction: 'vertical',
27
+ initialSize: 180,
28
+ minSize: 80,
29
+ maxSize: 400,
30
+ inverted: true,
31
+ })
19
32
 
20
33
  return (
21
34
  <div className="h-screen flex flex-col bg-po-bg">
22
- <Header switchLLM={switchLLM} />
35
+ <SystemBar switchLLM={switchLLM} />
23
36
 
24
- <div className="flex-1 flex overflow-hidden">
25
- {currentView === 'canvas' ? (
26
- <CanvasView />
27
- ) : (
28
- <>
29
- {/* Split view: Dashboard sidebar on left when PO selected */}
30
- {splitView && selectedPO && (
31
- <>
32
- {/* PO List */}
33
- <aside className="w-56 border-r border-po-border bg-po-surface overflow-hidden flex flex-col">
34
- <div className="p-3 border-b border-po-border flex items-center justify-between">
35
- <h2 className="text-sm font-medium text-gray-400">Prompt Objects</h2>
36
- <button
37
- onClick={() => setSplitView(false)}
38
- className="text-xs text-gray-500 hover:text-white"
39
- title="Hide sidebar"
40
- >
41
-
42
- </button>
43
- </div>
44
- <div className="flex-1 overflow-auto">
45
- <Dashboard compact />
46
- </div>
47
- </aside>
48
-
49
- {/* Threads List for selected PO */}
50
- {selectedPOData && (
51
- <aside className="w-56 border-r border-po-border bg-po-bg overflow-hidden">
52
- <ThreadsSidebar
53
- po={selectedPOData}
54
- switchSession={switchSession}
55
- createThread={createThread}
56
- requestUsage={requestUsage}
57
- exportThread={exportThread}
58
- />
59
- </aside>
60
- )}
61
- </>
62
- )}
37
+ <div className="flex-1 flex flex-col overflow-hidden">
38
+ {/* Main view area */}
39
+ <div className="flex-1 flex overflow-hidden">
40
+ {currentView === 'canvas' ? (
41
+ <CanvasView />
42
+ ) : (
43
+ <>
44
+ {/* Object List - resizable */}
45
+ <div style={{ width: objectListResize.size }} className="flex-shrink-0">
46
+ <ObjectList />
47
+ </div>
63
48
 
64
- {/* Main content */}
65
- <main className="flex-1 overflow-hidden flex flex-col">
66
- {/* Show expand button when sidebar is hidden */}
67
- {!splitView && selectedPO && (
68
- <button
69
- onClick={() => setSplitView(true)}
70
- className="absolute left-2 top-16 z-10 bg-po-surface border border-po-border rounded px-2 py-1 text-xs text-gray-400 hover:text-white hover:border-po-accent transition-colors"
71
- title="Show dashboard sidebar"
72
- >
73
- ☰ POs
74
- </button>
75
- )}
49
+ {/* Resize handle */}
50
+ <div
51
+ className="resize-handle"
52
+ onMouseDown={objectListResize.onMouseDown}
53
+ />
76
54
 
77
- {selectedPO ? (
78
- <PODetail
79
- sendMessage={sendMessage}
80
- createSession={createSession}
81
- switchSession={switchSession}
82
- createThread={createThread}
83
- updatePrompt={updatePrompt}
84
- />
85
- ) : (
86
- <Dashboard />
87
- )}
88
- </main>
89
- </>
90
- )}
55
+ {/* Main content */}
56
+ <main className="flex-1 overflow-hidden flex flex-col">
57
+ {selectedPO && selectedPOData ? (
58
+ <Inspector
59
+ po={selectedPOData}
60
+ sendMessage={sendMessage}
61
+ createSession={createSession}
62
+ switchSession={switchSession}
63
+ createThread={createThread}
64
+ updatePrompt={updatePrompt}
65
+ requestUsage={requestUsage}
66
+ exportThread={exportThread}
67
+ />
68
+ ) : (
69
+ <div className="h-full flex items-center justify-center text-po-text-ghost">
70
+ <span className="font-mono text-xs">Select an object</span>
71
+ </div>
72
+ )}
73
+ </main>
74
+ </>
75
+ )}
76
+ </div>
91
77
 
92
- {/* Message Bus sidebar */}
78
+ {/* Transcript - resizable bottom pane, visible in both views */}
93
79
  {busOpen && (
94
- <aside className="w-80 flex-shrink-0 border-l border-po-border bg-po-surface overflow-hidden">
95
- <MessageBus />
96
- </aside>
80
+ <>
81
+ <div
82
+ className="resize-handle-h"
83
+ onMouseDown={transcriptResize.onMouseDown}
84
+ />
85
+ <div style={{ height: transcriptResize.size }} className="flex-shrink-0">
86
+ <Transcript />
87
+ </div>
88
+ </>
97
89
  )}
98
90
  </div>
99
91
 
100
- {/* Notification panel */}
92
+ {/* Notification panel - floating */}
101
93
  {notifications.length > 0 && (
102
94
  <NotificationPanel respondToNotification={respondToNotification} />
103
95
  )}
@@ -83,17 +83,17 @@ export function CanvasView() {
83
83
  <div className="absolute top-3 left-3 flex gap-2 z-10">
84
84
  <button
85
85
  onClick={() => sceneRef.current?.fitAll()}
86
- className="px-3 py-1.5 text-sm bg-po-surface/80 backdrop-blur border border-po-border rounded hover:border-po-accent transition-colors text-gray-300 hover:text-white"
86
+ className="px-2.5 py-1 text-xs bg-po-surface-2/80 backdrop-blur border border-po-border rounded hover:border-po-accent transition-colors duration-150 text-po-text-secondary hover:text-po-text-primary"
87
87
  title="Fit all nodes (F)"
88
88
  >
89
89
  Fit All
90
90
  </button>
91
91
  <button
92
92
  onClick={toggleLabels}
93
- className={`px-3 py-1.5 text-sm backdrop-blur border rounded transition-colors ${
93
+ className={`px-2.5 py-1 text-xs backdrop-blur border rounded transition-colors duration-150 ${
94
94
  showLabels
95
- ? 'bg-po-accent/20 border-po-accent text-white'
96
- : 'bg-po-surface/80 border-po-border text-gray-300 hover:text-white'
95
+ ? 'bg-po-accent-wash border-po-accent text-po-accent'
96
+ : 'bg-po-surface-2/80 border-po-border text-po-text-secondary hover:text-po-text-primary'
97
97
  }`}
98
98
  title="Toggle labels"
99
99
  >
@@ -102,7 +102,7 @@ export function CanvasView() {
102
102
  </div>
103
103
 
104
104
  {/* Help hint */}
105
- <div className="absolute bottom-3 left-3 text-xs text-gray-500 z-10">
105
+ <div className="absolute bottom-3 left-3 text-2xs text-po-text-ghost z-10 font-mono">
106
106
  Scroll to zoom · Shift+drag to pan · F to fit · Click node to inspect
107
107
  </div>
108
108
 
@@ -1,46 +1,46 @@
1
1
  // Canvas visualization constants
2
2
 
3
- // Colors (hex values matching po-* palette)
3
+ // Colors (hex values matching warm po-* palette)
4
4
  export const COLORS = {
5
5
  // Node colors
6
- background: 0x0f0f1a,
7
- surface: 0x1a1a2e,
8
- border: 0x2d2d44,
9
- accent: 0x7c3aed,
10
- accentHover: 0x9061f9,
11
- success: 0x22c55e,
12
- warning: 0xf59e0b,
13
- error: 0xef4444,
6
+ background: 0x1a1918,
7
+ surface: 0x222120,
8
+ border: 0x3d3a37,
9
+ accent: 0xd4952a,
10
+ accentHover: 0xe0a940,
11
+ success: 0x3b9a6e,
12
+ warning: 0xd4952a,
13
+ error: 0xc45c4a,
14
14
 
15
15
  // Status colors
16
- statusIdle: 0x6b7280,
17
- statusThinking: 0x7c3aed,
18
- statusCallingTool: 0xf59e0b,
16
+ statusIdle: 0x78726a,
17
+ statusThinking: 0xd4952a,
18
+ statusCallingTool: 0x3b9a6e,
19
19
 
20
20
  // Canvas-specific
21
- nodeFill: 0x1a1a2e,
22
- nodeGlow: 0x7c3aed,
23
- toolCallFill: 0x3b82f6,
24
- arcColor: 0x7c3aed,
25
- particleColor: 0xc084fc,
26
- gridColor: 0x1a1a2e,
21
+ nodeFill: 0x222120,
22
+ nodeGlow: 0xd4952a,
23
+ toolCallFill: 0x3b9a6e,
24
+ arcColor: 0xd4952a,
25
+ particleColor: 0xe0a940,
26
+ gridColor: 0x222120,
27
27
  } as const
28
28
 
29
29
  // CSS color strings (for CSS2DRenderer elements)
30
30
  export const CSS_COLORS = {
31
- accent: '#7c3aed',
32
- accentHover: '#9061f9',
33
- warning: '#f59e0b',
34
- success: '#22c55e',
35
- error: '#ef4444',
36
- textPrimary: '#ffffff',
37
- textSecondary: '#9ca3af',
38
- textMuted: '#6b7280',
39
- surface: '#1a1a2e',
40
- border: '#2d2d44',
41
- statusIdle: '#6b7280',
42
- statusThinking: '#7c3aed',
43
- statusCallingTool: '#f59e0b',
31
+ accent: '#d4952a',
32
+ accentHover: '#e0a940',
33
+ warning: '#d4952a',
34
+ success: '#3b9a6e',
35
+ error: '#c45c4a',
36
+ textPrimary: '#e8e2da',
37
+ textSecondary: '#a8a29a',
38
+ textMuted: '#78726a',
39
+ surface: '#222120',
40
+ border: '#3d3a37',
41
+ statusIdle: '#78726a',
42
+ statusThinking: '#d4952a',
43
+ statusCallingTool: '#3b9a6e',
44
44
  } as const
45
45
 
46
46
  // Node dimensions
@@ -9,14 +9,14 @@ export function InspectorPanel() {
9
9
 
10
10
  return (
11
11
  <aside className="w-80 border-l border-po-border bg-po-surface overflow-hidden flex flex-col">
12
- <div className="p-3 border-b border-po-border flex items-center justify-between">
13
- <h2 className="text-sm font-medium text-gray-400">Inspector</h2>
12
+ <div className="h-8 bg-po-surface-2 border-b border-po-border flex items-center px-3">
13
+ <span className="text-2xs font-medium text-po-text-ghost uppercase tracking-wider flex-1">Inspector</span>
14
14
  <button
15
15
  onClick={() => useCanvasStore.getState().selectNode(null)}
16
- className="text-xs text-gray-500 hover:text-white"
16
+ className="text-2xs text-po-text-ghost hover:text-po-text-secondary transition-colors duration-150"
17
17
  title="Close inspector"
18
18
  >
19
-
19
+ {'\u2715'}
20
20
  </button>
21
21
  </div>
22
22
  <div className="flex-1 overflow-auto">