prompt_objects 0.4.0 → 0.6.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 (80) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +33 -0
  3. data/CLAUDE.md +113 -44
  4. data/README.md +140 -14
  5. data/frontend/index.html +5 -1
  6. data/frontend/src/App.tsx +72 -79
  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/EnvDataPane.tsx +69 -0
  15. data/frontend/src/components/Inspector.tsx +263 -0
  16. data/frontend/src/components/MarkdownMessage.tsx +22 -20
  17. data/frontend/src/components/MethodList.tsx +90 -0
  18. data/frontend/src/components/ModelSelector.tsx +13 -14
  19. data/frontend/src/components/NotificationPanel.tsx +29 -33
  20. data/frontend/src/components/ObjectList.tsx +78 -0
  21. data/frontend/src/components/PaneSlot.tsx +30 -0
  22. data/frontend/src/components/SourcePane.tsx +202 -0
  23. data/frontend/src/components/SystemBar.tsx +74 -0
  24. data/frontend/src/components/Transcript.tsx +76 -0
  25. data/frontend/src/components/UsagePanel.tsx +27 -27
  26. data/frontend/src/components/Workspace.tsx +260 -0
  27. data/frontend/src/components/index.ts +10 -9
  28. data/frontend/src/hooks/useResize.ts +55 -0
  29. data/frontend/src/hooks/useWebSocket.ts +70 -0
  30. data/frontend/src/index.css +27 -10
  31. data/frontend/src/store/index.ts +36 -0
  32. data/frontend/src/types/index.ts +13 -0
  33. data/frontend/tailwind.config.js +28 -9
  34. data/lib/prompt_objects/capability.rb +23 -1
  35. data/lib/prompt_objects/connectors/mcp.rb +2 -16
  36. data/lib/prompt_objects/environment.rb +15 -0
  37. data/lib/prompt_objects/llm/openai_adapter.rb +22 -0
  38. data/lib/prompt_objects/mcp/tools/inspect_po.rb +1 -31
  39. data/lib/prompt_objects/mcp/tools/list_prompt_objects.rb +1 -6
  40. data/lib/prompt_objects/prompt_object.rb +239 -7
  41. data/lib/prompt_objects/server/api/routes.rb +16 -48
  42. data/lib/prompt_objects/server/app.rb +14 -0
  43. data/lib/prompt_objects/server/public/assets/{index-xvyeb-5Z.js → index-DEPawnfZ.js} +206 -206
  44. data/lib/prompt_objects/server/public/assets/index-oMrRce1m.css +1 -0
  45. data/lib/prompt_objects/server/public/index.html +7 -3
  46. data/lib/prompt_objects/server/websocket_handler.rb +41 -98
  47. data/lib/prompt_objects/server.rb +6 -62
  48. data/lib/prompt_objects/session/store.rb +176 -4
  49. data/lib/prompt_objects/universal/delete_env_data.rb +70 -0
  50. data/lib/prompt_objects/universal/get_env_data.rb +64 -0
  51. data/lib/prompt_objects/universal/list_env_data.rb +61 -0
  52. data/lib/prompt_objects/universal/store_env_data.rb +87 -0
  53. data/lib/prompt_objects/universal/update_env_data.rb +88 -0
  54. data/lib/prompt_objects.rb +6 -1
  55. data/prompt_objects.gemspec +1 -1
  56. data/templates/arc-agi-1/objects/observer.md +4 -0
  57. data/templates/arc-agi-1/objects/solver.md +10 -1
  58. data/templates/arc-agi-1/objects/verifier.md +4 -0
  59. data/templates/arc-agi-1/primitives/find_objects.rb +1 -1
  60. data/templates/arc-agi-1/primitives/grid_diff.rb +2 -2
  61. data/templates/arc-agi-1/primitives/grid_info.rb +1 -1
  62. data/templates/arc-agi-1/primitives/grid_transform.rb +1 -1
  63. data/templates/arc-agi-1/primitives/render_grid.rb +1 -0
  64. data/templates/arc-agi-1/primitives/test_solution.rb +3 -0
  65. data/tools/thread-explorer.html +27 -0
  66. metadata +18 -16
  67. data/Gemfile.lock +0 -233
  68. data/IMPLEMENTATION_PLAN.md +0 -1073
  69. data/design-doc-v2.md +0 -1232
  70. data/frontend/src/components/CapabilitiesPanel.tsx +0 -141
  71. data/frontend/src/components/ChatPanel.tsx +0 -296
  72. data/frontend/src/components/Dashboard.tsx +0 -83
  73. data/frontend/src/components/Header.tsx +0 -153
  74. data/frontend/src/components/MessageBus.tsx +0 -56
  75. data/frontend/src/components/POCard.tsx +0 -56
  76. data/frontend/src/components/PODetail.tsx +0 -124
  77. data/frontend/src/components/PromptPanel.tsx +0 -156
  78. data/frontend/src/components/SessionsPanel.tsx +0 -174
  79. data/frontend/src/components/ThreadsSidebar.tsx +0 -163
  80. data/lib/prompt_objects/server/public/assets/index-6y64NXFy.css +0 -1
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PromptObjects
4
+ module Universal
5
+ # Universal capability to delete a key from the shared environment data.
6
+ class DeleteEnvData < Primitive
7
+ def name
8
+ "delete_env_data"
9
+ end
10
+
11
+ def description
12
+ "Delete a key from the shared environment data for this delegation chain."
13
+ end
14
+
15
+ def parameters
16
+ {
17
+ type: "object",
18
+ properties: {
19
+ key: {
20
+ type: "string",
21
+ description: "The key to delete"
22
+ }
23
+ },
24
+ required: ["key"]
25
+ }
26
+ end
27
+
28
+ def receive(message, context:)
29
+ key = message[:key] || message["key"]
30
+ return "Error: 'key' is required" unless key
31
+
32
+ root_thread_id = resolve_root_thread(context)
33
+ return "Error: Could not resolve thread scope (no active session)" unless root_thread_id
34
+
35
+ store = context.env.session_store
36
+ return "Error: Session store not available" unless store
37
+
38
+ deleted = store.delete_env_data(root_thread_id: root_thread_id, key: key)
39
+
40
+ unless deleted
41
+ return "Key '#{key}' not found in environment data."
42
+ end
43
+
44
+ stored_by = context.calling_po || "unknown"
45
+
46
+ context.bus.publish(
47
+ from: stored_by,
48
+ to: "env_data",
49
+ message: { action: "delete", key: key }
50
+ )
51
+
52
+ context.env.notify_env_data_changed(action: "delete", root_thread_id: root_thread_id, key: key, stored_by: stored_by)
53
+
54
+ "Deleted '#{key}' from environment data."
55
+ end
56
+
57
+ private
58
+
59
+ def resolve_root_thread(context)
60
+ store = context.env.session_store
61
+ return nil unless store
62
+
63
+ po = context.env.registry.get(context.calling_po)
64
+ return nil unless po&.respond_to?(:session_id) && po.session_id
65
+
66
+ store.resolve_root_thread(po.session_id)
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PromptObjects
4
+ module Universal
5
+ # Universal capability to retrieve shared data by key from the current delegation chain.
6
+ class GetEnvData < Primitive
7
+ def name
8
+ "get_env_data"
9
+ end
10
+
11
+ def description
12
+ "Retrieve a specific key's full value from the shared environment data. " \
13
+ "Use list_env_data first to see what keys are available."
14
+ end
15
+
16
+ def parameters
17
+ {
18
+ type: "object",
19
+ properties: {
20
+ key: {
21
+ type: "string",
22
+ description: "The key to retrieve"
23
+ }
24
+ },
25
+ required: ["key"]
26
+ }
27
+ end
28
+
29
+ def receive(message, context:)
30
+ key = message[:key] || message["key"]
31
+ return "Error: 'key' is required" unless key
32
+
33
+ root_thread_id = resolve_root_thread(context)
34
+ return "Error: Could not resolve thread scope (no active session)" unless root_thread_id
35
+
36
+ store = context.env.session_store
37
+ return "Error: Session store not available" unless store
38
+
39
+ entry = store.get_env_data(root_thread_id: root_thread_id, key: key)
40
+ return "Key '#{key}' not found in environment data." unless entry
41
+
42
+ context.bus.publish(
43
+ from: context.calling_po || "unknown",
44
+ to: "env_data",
45
+ message: { action: "get", key: key }
46
+ )
47
+
48
+ JSON.generate(entry[:value])
49
+ end
50
+
51
+ private
52
+
53
+ def resolve_root_thread(context)
54
+ store = context.env.session_store
55
+ return nil unless store
56
+
57
+ po = context.env.registry.get(context.calling_po)
58
+ return nil unless po&.respond_to?(:session_id) && po.session_id
59
+
60
+ store.resolve_root_thread(po.session_id)
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PromptObjects
4
+ module Universal
5
+ # Universal capability to list all shared environment data keys and descriptions.
6
+ # Returns a lightweight manifest without loading full values.
7
+ class ListEnvData < Primitive
8
+ def name
9
+ "list_env_data"
10
+ end
11
+
12
+ def description
13
+ "List all keys and descriptions in the shared environment data for this delegation chain. " \
14
+ "Returns keys and short descriptions only (no values). " \
15
+ "Use get_env_data to retrieve a specific key's full value."
16
+ end
17
+
18
+ def parameters
19
+ {
20
+ type: "object",
21
+ properties: {},
22
+ required: []
23
+ }
24
+ end
25
+
26
+ def receive(message, context:)
27
+ root_thread_id = resolve_root_thread(context)
28
+ return "Error: Could not resolve thread scope (no active session)" unless root_thread_id
29
+
30
+ store = context.env.session_store
31
+ return "Error: Session store not available" unless store
32
+
33
+ entries = store.list_env_data(root_thread_id: root_thread_id)
34
+
35
+ if entries.empty?
36
+ return "No environment data stored for this delegation chain."
37
+ end
38
+
39
+ context.bus.publish(
40
+ from: context.calling_po || "unknown",
41
+ to: "env_data",
42
+ message: { action: "list", count: entries.size }
43
+ )
44
+
45
+ JSON.generate(entries)
46
+ end
47
+
48
+ private
49
+
50
+ def resolve_root_thread(context)
51
+ store = context.env.session_store
52
+ return nil unless store
53
+
54
+ po = context.env.registry.get(context.calling_po)
55
+ return nil unless po&.respond_to?(:session_id) && po.session_id
56
+
57
+ store.resolve_root_thread(po.session_id)
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PromptObjects
4
+ module Universal
5
+ # Universal capability to store shared data scoped to the current delegation chain.
6
+ # Any PO in the same delegation tree can read this data via get_env_data or list_env_data.
7
+ class StoreEnvData < Primitive
8
+ def name
9
+ "store_env_data"
10
+ end
11
+
12
+ def description
13
+ "Store a key-value pair in the shared environment data for this delegation chain. " \
14
+ "All POs in the same delegation tree can access this data. " \
15
+ "If the key already exists, it will be overwritten."
16
+ end
17
+
18
+ def parameters
19
+ {
20
+ type: "object",
21
+ properties: {
22
+ key: {
23
+ type: "string",
24
+ description: "Namespaced identifier for the data (e.g. 'arc_task', 'findings')"
25
+ },
26
+ short_description: {
27
+ type: "string",
28
+ description: "1-2 sentence summary of what this data contains (for discoverability via list_env_data)"
29
+ },
30
+ value: {
31
+ description: "The data to store (any JSON-serializable value: string, number, object, array)"
32
+ }
33
+ },
34
+ required: %w[key short_description value]
35
+ }
36
+ end
37
+
38
+ def receive(message, context:)
39
+ key = message[:key] || message["key"]
40
+ short_description = message[:short_description] || message["short_description"]
41
+ value = message[:value] || message["value"]
42
+
43
+ return "Error: 'key' is required" unless key
44
+ return "Error: 'short_description' is required" unless short_description
45
+ return "Error: 'value' is required" if value.nil?
46
+
47
+ root_thread_id = resolve_root_thread(context)
48
+ return "Error: Could not resolve thread scope (no active session)" unless root_thread_id
49
+
50
+ store = context.env.session_store
51
+ return "Error: Session store not available" unless store
52
+
53
+ stored_by = context.calling_po || "unknown"
54
+
55
+ store.store_env_data(
56
+ root_thread_id: root_thread_id,
57
+ key: key,
58
+ short_description: short_description,
59
+ value: value,
60
+ stored_by: stored_by
61
+ )
62
+
63
+ context.bus.publish(
64
+ from: stored_by,
65
+ to: "env_data",
66
+ message: { action: "store", key: key, short_description: short_description }
67
+ )
68
+
69
+ context.env.notify_env_data_changed(action: "store", root_thread_id: root_thread_id, key: key, stored_by: stored_by)
70
+
71
+ "Stored '#{key}' in environment data."
72
+ end
73
+
74
+ private
75
+
76
+ def resolve_root_thread(context)
77
+ store = context.env.session_store
78
+ return nil unless store
79
+
80
+ po = context.env.registry.get(context.calling_po)
81
+ return nil unless po&.respond_to?(:session_id) && po.session_id
82
+
83
+ store.resolve_root_thread(po.session_id)
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PromptObjects
4
+ module Universal
5
+ # Universal capability to update an existing shared environment data entry.
6
+ # Fails if the key doesn't exist (use store_env_data for create-or-replace).
7
+ class UpdateEnvData < Primitive
8
+ def name
9
+ "update_env_data"
10
+ end
11
+
12
+ def description
13
+ "Update an existing key's value and/or description in the shared environment data. " \
14
+ "Fails if the key doesn't exist — use store_env_data to create new entries."
15
+ end
16
+
17
+ def parameters
18
+ {
19
+ type: "object",
20
+ properties: {
21
+ key: {
22
+ type: "string",
23
+ description: "The key to update (must already exist)"
24
+ },
25
+ short_description: {
26
+ type: "string",
27
+ description: "New description (keeps existing if omitted)"
28
+ },
29
+ value: {
30
+ description: "New value (keeps existing if omitted)"
31
+ }
32
+ },
33
+ required: ["key"]
34
+ }
35
+ end
36
+
37
+ def receive(message, context:)
38
+ key = message[:key] || message["key"]
39
+ short_description = message[:short_description] || message["short_description"]
40
+ value = message[:value] || message["value"]
41
+
42
+ return "Error: 'key' is required" unless key
43
+
44
+ root_thread_id = resolve_root_thread(context)
45
+ return "Error: Could not resolve thread scope (no active session)" unless root_thread_id
46
+
47
+ store = context.env.session_store
48
+ return "Error: Session store not available" unless store
49
+
50
+ stored_by = context.calling_po || "unknown"
51
+
52
+ updated = store.update_env_data(
53
+ root_thread_id: root_thread_id,
54
+ key: key,
55
+ short_description: short_description,
56
+ value: value,
57
+ stored_by: stored_by
58
+ )
59
+
60
+ unless updated
61
+ return "Key '#{key}' not found in environment data. Use store_env_data to create it."
62
+ end
63
+
64
+ context.bus.publish(
65
+ from: stored_by,
66
+ to: "env_data",
67
+ message: { action: "update", key: key }
68
+ )
69
+
70
+ context.env.notify_env_data_changed(action: "update", root_thread_id: root_thread_id, key: key, stored_by: stored_by)
71
+
72
+ "Updated '#{key}' in environment data."
73
+ end
74
+
75
+ private
76
+
77
+ def resolve_root_thread(context)
78
+ store = context.env.session_store
79
+ return nil unless store
80
+
81
+ po = context.env.registry.get(context.calling_po)
82
+ return nil unless po&.respond_to?(:session_id) && po.session_id
83
+
84
+ store.resolve_root_thread(po.session_id)
85
+ end
86
+ end
87
+ end
88
+ end
@@ -11,7 +11,7 @@ module PromptObjects
11
11
  class Error < StandardError; end
12
12
 
13
13
  # Universal capabilities available to all prompt objects (don't need to be declared)
14
- UNIVERSAL_CAPABILITIES = %w[ask_human think create_capability add_capability remove_capability list_capabilities list_primitives add_primitive create_primitive delete_primitive verify_primitive modify_primitive request_primitive modify_prompt].freeze
14
+ UNIVERSAL_CAPABILITIES = %w[ask_human think create_capability add_capability remove_capability list_capabilities list_primitives add_primitive create_primitive delete_primitive verify_primitive modify_primitive request_primitive modify_prompt store_env_data get_env_data list_env_data update_env_data delete_env_data].freeze
15
15
  end
16
16
 
17
17
  require_relative "prompt_objects/capability"
@@ -60,6 +60,11 @@ require_relative "prompt_objects/universal/verify_primitive"
60
60
  require_relative "prompt_objects/universal/modify_primitive"
61
61
  require_relative "prompt_objects/universal/request_primitive"
62
62
  require_relative "prompt_objects/universal/modify_prompt"
63
+ require_relative "prompt_objects/universal/store_env_data"
64
+ require_relative "prompt_objects/universal/get_env_data"
65
+ require_relative "prompt_objects/universal/list_env_data"
66
+ require_relative "prompt_objects/universal/update_env_data"
67
+ require_relative "prompt_objects/universal/delete_env_data"
63
68
 
64
69
  # Connectors (different interfaces to environments)
65
70
  require_relative "prompt_objects/connectors/base"
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |spec|
4
4
  spec.name = "prompt_objects"
5
- spec.version = "0.4.0"
5
+ spec.version = "0.6.0"
6
6
  spec.authors = ["Scott Werner"]
7
7
  spec.email = ["scott@sublayer.com"]
8
8
 
@@ -19,6 +19,10 @@ You are an observation specialist for ARC-AGI grid puzzles. Your job is to look
19
19
 
20
20
  When given grid pairs to analyze, you produce a structured observation report. You use your tools — don't try to analyze from descriptions alone. Render the grids, run grid_info, find the objects, diff the pairs.
21
21
 
22
+ ### Check Shared Data First
23
+
24
+ When you're called by another PO (like the solver), start by calling `list_env_data` to see if task data, grids, or other context has been stored for you. If the solver has stored the loaded task data, you can retrieve it with `get_env_data` instead of relying only on what was included in the message. This is especially useful when you need to compare across multiple training pairs.
25
+
22
26
  ## Observation Framework
23
27
 
24
28
  For each training pair, analyze and report on ALL of these dimensions:
@@ -26,10 +26,17 @@ Each ARC task gives you 2-5 training pairs (input grid → output grid) and 1-3
26
26
 
27
27
  ## Solving Process
28
28
 
29
- ### Step 1: Load and Render
29
+ ### Step 1: Load and Store
30
30
 
31
31
  Load the task with `load_arc_task`, then render every grid. Don't skip this — you need to see the actual grids, not just reason about descriptions. Use `grid_info` on each grid to get dimensions and color distributions.
32
32
 
33
+ After loading, use `store_env_data` to store key information so your delegates (observer, verifier) can access it directly:
34
+ - **`task_id`** — the task identifier
35
+ - **`task_data`** — the full loaded task (training pairs, test inputs, grids)
36
+ - **`task_summary`** — a brief description of the task (pair count, grid dimensions)
37
+
38
+ This way, when you delegate to the observer or verifier, they can call `list_env_data` and `get_env_data` to retrieve the grids directly instead of relying solely on what you pass in the message.
39
+
33
40
  ### Step 2: Observe (delegate to observer)
34
41
 
35
42
  Send each training pair to the **observer** and ask it to analyze the transformation. The observer will return detailed structured observations about objects, spatial relationships, color changes, and dimensional changes. Read these carefully.
@@ -87,6 +94,8 @@ If verification fails:
87
94
  - Revise your hypothesis to account for the discrepancy
88
95
  - Test again
89
96
 
97
+ After each verification round, store your current hypothesis and its result with `store_env_data` (e.g., key `current_hypothesis`). This lets delegates see what's already been tried and what failed.
98
+
90
99
  Iterate. Most tasks are solved within 2-4 hypothesis cycles.
91
100
 
92
101
  ### Step 6: Apply to Test Input
@@ -20,6 +20,10 @@ You are a rigorous verification specialist for ARC-AGI puzzle solving. When give
20
20
 
21
21
  When the solver sends you a hypothesis to verify:
22
22
 
23
+ ### 0. Check Shared Data
24
+
25
+ Start by calling `list_env_data` to see what context is available. The solver typically stores the loaded task data (`task_data`), task summary (`task_summary`), and current hypothesis (`current_hypothesis`). Use `get_env_data` to retrieve what you need — this gives you direct access to the grids and training pairs rather than relying solely on what the solver includes in its message.
26
+
23
27
  ### 1. Understand the Hypothesis
24
28
 
25
29
  Restate the proposed rule in your own words to confirm understanding. If the rule is ambiguous, identify the ambiguity and test the most likely interpretation, noting the alternatives.
@@ -15,7 +15,7 @@ module PromptObjects
15
15
  {
16
16
  type: "object",
17
17
  properties: {
18
- grid: { type: "array", description: "2D array of integers" },
18
+ grid: { type: "array", items: { type: "array", items: { type: "integer" } }, description: "2D array of integers" },
19
19
  background: { type: "integer", description: "Background color to ignore (default: 0)" }
20
20
  },
21
21
  required: ["grid"]
@@ -15,8 +15,8 @@ module PromptObjects
15
15
  {
16
16
  type: "object",
17
17
  properties: {
18
- grid_a: { type: "array", description: "First grid (2D array)" },
19
- grid_b: { type: "array", description: "Second grid (2D array)" }
18
+ grid_a: { type: "array", items: { type: "array", items: { type: "integer" } }, description: "First grid (2D array)" },
19
+ grid_b: { type: "array", items: { type: "array", items: { type: "integer" } }, description: "Second grid (2D array)" }
20
20
  },
21
21
  required: ["grid_a", "grid_b"]
22
22
  }
@@ -15,7 +15,7 @@ module PromptObjects
15
15
  {
16
16
  type: "object",
17
17
  properties: {
18
- grid: { type: "array", description: "2D array of integers" }
18
+ grid: { type: "array", items: { type: "array", items: { type: "integer" } }, description: "2D array of integers" }
19
19
  },
20
20
  required: ["grid"]
21
21
  }
@@ -15,7 +15,7 @@ module PromptObjects
15
15
  {
16
16
  type: "object",
17
17
  properties: {
18
- grid: { type: "array", description: "2D array of integers" },
18
+ grid: { type: "array", items: { type: "array", items: { type: "integer" } }, description: "2D array of integers" },
19
19
  operation: {
20
20
  type: "string",
21
21
  enum: %w[rotate_90 rotate_180 rotate_270 flip_h flip_v transpose],
@@ -27,6 +27,7 @@ module PromptObjects
27
27
  properties: {
28
28
  grid: {
29
29
  type: "array",
30
+ items: { type: "array", items: { type: "integer" } },
30
31
  description: "2D array of integers 0-9"
31
32
  },
32
33
  label: {
@@ -21,14 +21,17 @@ module PromptObjects
21
21
  },
22
22
  grid: {
23
23
  type: "array",
24
+ items: { type: "array", items: { type: "integer" } },
24
25
  description: "A grid to compare directly against expected output (for quick checks)"
25
26
  },
26
27
  expected: {
27
28
  type: "array",
29
+ items: { type: "array", items: { type: "integer" } },
28
30
  description: "Expected output grid (used with 'grid' parameter)"
29
31
  },
30
32
  train: {
31
33
  type: "array",
34
+ items: { type: "object" },
32
35
  description: "Training pairs array (used with 'primitive_name'). Each element has 'input' and 'output' grids."
33
36
  }
34
37
  },
@@ -298,6 +298,7 @@ button { font-family:var(--mono); cursor:pointer; }
298
298
 
299
299
  // --- State ---
300
300
  let treeData = null;
301
+ let envData = []; // root-level env data entries
301
302
  let events = []; // flattened event sequence
302
303
  let actors = []; // ordered actor names
303
304
  let poNames = new Set();
@@ -335,6 +336,7 @@ function loadFile(file) {
335
336
 
336
337
  function loadData(data) {
337
338
  treeData = data;
339
+ envData = data.env_data || [];
338
340
  allNodes = {};
339
341
  indexNodes(data);
340
342
  poNames = extractPONames(data);
@@ -503,9 +505,11 @@ function renderHeader() {
503
505
  const usage = sumUsageTree(treeData);
504
506
  const nEvents = events.length;
505
507
  const nSessions = Object.keys(allNodes).length;
508
+ const envCount = envData.length;
506
509
  document.getElementById('h-stats').innerHTML =
507
510
  `<span><span class="v">${nSessions}</span> sessions</span>`+
508
511
  `<span><span class="v">${nEvents}</span> events</span>`+
512
+ (envCount > 0 ? `<span style="color:var(--amber)"><span class="v">${envCount}</span> env data</span>` : '')+
509
513
  `<span><span class="v">${fmtNum(usage.input)}</span> in</span>`+
510
514
  `<span><span class="v">${fmtNum(usage.output)}</span> out</span>`;
511
515
  }
@@ -817,6 +821,29 @@ function renderDetail() {
817
821
  html += `<div class="stats"><span><span class="v">${fmtNum(usage.input)}</span> in</span><span><span class="v">${fmtNum(usage.output)}</span> out</span></div>`;
818
822
  html += `</div>`;
819
823
 
824
+ // Env Data section (only on root session when env_data exists)
825
+ if (type === 'root' && envData.length > 0) {
826
+ html += '<div style="padding:8px 16px 0">';
827
+ html += '<div style="font-size:10px;text-transform:uppercase;letter-spacing:0.5px;color:var(--amber);margin-bottom:6px;font-weight:600">Env Data</div>';
828
+ envData.forEach(entry => {
829
+ const eKey = esc(entry.key);
830
+ const eDesc = esc(entry.short_description||'');
831
+ const eBy = esc(entry.stored_by||'');
832
+ const eVal = typeof entry.value === 'string' ? esc(entry.value) : esc(JSON.stringify(entry.value,null,2));
833
+ html += `<div class="tc">`;
834
+ html += `<div class="tc-head" onclick="toggleTc(this)" style="border-left:2px solid var(--amber)">`;
835
+ html += `<span class="chv">&#9654;</span>`;
836
+ html += `<span class="tc-name" style="color:var(--amber)">${eKey}</span>`;
837
+ html += `<span class="tc-label">${eDesc}</span>`;
838
+ html += `<span class="tc-label" style="margin-left:auto">by ${eBy}</span>`;
839
+ html += `</div>`;
840
+ html += `<div class="tc-body">`;
841
+ html += `<div class="tc-section"><div class="tc-section-label">Value</div><pre style="color:var(--amber)">${eVal}</pre></div>`;
842
+ html += `</div></div>`;
843
+ });
844
+ html += '</div>';
845
+ }
846
+
820
847
  // Messages
821
848
  html += '<div class="messages">';
822
849
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: prompt_objects
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Scott Werner
@@ -204,12 +204,9 @@ files:
204
204
  - CHANGELOG.md
205
205
  - CLAUDE.md
206
206
  - Gemfile
207
- - Gemfile.lock
208
- - IMPLEMENTATION_PLAN.md
209
207
  - LICENSE
210
208
  - README.md
211
209
  - Rakefile
212
- - design-doc-v2.md
213
210
  - exe/prompt_objects
214
211
  - exe/prompt_objects_mcp
215
212
  - frontend/.gitignore
@@ -231,22 +228,22 @@ files:
231
228
  - frontend/src/canvas/nodes/PONode.ts
232
229
  - frontend/src/canvas/nodes/ToolCallNode.ts
233
230
  - frontend/src/canvas/types.ts
234
- - frontend/src/components/CapabilitiesPanel.tsx
235
- - frontend/src/components/ChatPanel.tsx
236
231
  - frontend/src/components/ContextMenu.tsx
237
- - frontend/src/components/Dashboard.tsx
238
- - frontend/src/components/Header.tsx
232
+ - frontend/src/components/EnvDataPane.tsx
233
+ - frontend/src/components/Inspector.tsx
239
234
  - frontend/src/components/MarkdownMessage.tsx
240
- - frontend/src/components/MessageBus.tsx
235
+ - frontend/src/components/MethodList.tsx
241
236
  - frontend/src/components/ModelSelector.tsx
242
237
  - frontend/src/components/NotificationPanel.tsx
243
- - frontend/src/components/POCard.tsx
244
- - frontend/src/components/PODetail.tsx
245
- - frontend/src/components/PromptPanel.tsx
246
- - frontend/src/components/SessionsPanel.tsx
247
- - frontend/src/components/ThreadsSidebar.tsx
238
+ - frontend/src/components/ObjectList.tsx
239
+ - frontend/src/components/PaneSlot.tsx
240
+ - frontend/src/components/SourcePane.tsx
241
+ - frontend/src/components/SystemBar.tsx
242
+ - frontend/src/components/Transcript.tsx
248
243
  - frontend/src/components/UsagePanel.tsx
244
+ - frontend/src/components/Workspace.tsx
249
245
  - frontend/src/components/index.ts
246
+ - frontend/src/hooks/useResize.ts
250
247
  - frontend/src/hooks/useWebSocket.ts
251
248
  - frontend/src/index.css
252
249
  - frontend/src/main.tsx
@@ -293,8 +290,8 @@ files:
293
290
  - lib/prompt_objects/server/api/routes.rb
294
291
  - lib/prompt_objects/server/app.rb
295
292
  - lib/prompt_objects/server/file_watcher.rb
296
- - lib/prompt_objects/server/public/assets/index-6y64NXFy.css
297
- - lib/prompt_objects/server/public/assets/index-xvyeb-5Z.js
293
+ - lib/prompt_objects/server/public/assets/index-DEPawnfZ.js
294
+ - lib/prompt_objects/server/public/assets/index-oMrRce1m.css
298
295
  - lib/prompt_objects/server/public/index.html
299
296
  - lib/prompt_objects/server/websocket_handler.rb
300
297
  - lib/prompt_objects/session/store.rb
@@ -303,14 +300,19 @@ files:
303
300
  - lib/prompt_objects/universal/ask_human.rb
304
301
  - lib/prompt_objects/universal/create_capability.rb
305
302
  - lib/prompt_objects/universal/create_primitive.rb
303
+ - lib/prompt_objects/universal/delete_env_data.rb
306
304
  - lib/prompt_objects/universal/delete_primitive.rb
305
+ - lib/prompt_objects/universal/get_env_data.rb
307
306
  - lib/prompt_objects/universal/list_capabilities.rb
307
+ - lib/prompt_objects/universal/list_env_data.rb
308
308
  - lib/prompt_objects/universal/list_primitives.rb
309
309
  - lib/prompt_objects/universal/modify_primitive.rb
310
310
  - lib/prompt_objects/universal/modify_prompt.rb
311
311
  - lib/prompt_objects/universal/remove_capability.rb
312
312
  - lib/prompt_objects/universal/request_primitive.rb
313
+ - lib/prompt_objects/universal/store_env_data.rb
313
314
  - lib/prompt_objects/universal/think.rb
315
+ - lib/prompt_objects/universal/update_env_data.rb
314
316
  - lib/prompt_objects/universal/verify_primitive.rb
315
317
  - objects/coordinator.md
316
318
  - objects/greeter.md