simple_acp 0.0.1

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 +7 -0
  2. data/.envrc +1 -0
  3. data/CHANGELOG.md +5 -0
  4. data/COMMITS.md +196 -0
  5. data/LICENSE.txt +21 -0
  6. data/README.md +385 -0
  7. data/Rakefile +13 -0
  8. data/docs/api/client-base.md +383 -0
  9. data/docs/api/index.md +159 -0
  10. data/docs/api/models.md +286 -0
  11. data/docs/api/server-base.md +379 -0
  12. data/docs/api/storage.md +347 -0
  13. data/docs/assets/images/simple_acp.jpg +0 -0
  14. data/docs/client/index.md +279 -0
  15. data/docs/client/sessions.md +324 -0
  16. data/docs/client/streaming.md +345 -0
  17. data/docs/client/sync-async.md +308 -0
  18. data/docs/core-concepts/agents.md +253 -0
  19. data/docs/core-concepts/events.md +337 -0
  20. data/docs/core-concepts/index.md +147 -0
  21. data/docs/core-concepts/messages.md +211 -0
  22. data/docs/core-concepts/runs.md +278 -0
  23. data/docs/core-concepts/sessions.md +281 -0
  24. data/docs/examples.md +659 -0
  25. data/docs/getting-started/configuration.md +166 -0
  26. data/docs/getting-started/index.md +62 -0
  27. data/docs/getting-started/installation.md +95 -0
  28. data/docs/getting-started/quick-start.md +189 -0
  29. data/docs/index.md +119 -0
  30. data/docs/server/creating-agents.md +360 -0
  31. data/docs/server/http-endpoints.md +411 -0
  32. data/docs/server/index.md +218 -0
  33. data/docs/server/multi-turn.md +329 -0
  34. data/docs/server/streaming.md +315 -0
  35. data/docs/storage/custom.md +414 -0
  36. data/docs/storage/index.md +176 -0
  37. data/docs/storage/memory.md +198 -0
  38. data/docs/storage/postgresql.md +350 -0
  39. data/docs/storage/redis.md +287 -0
  40. data/examples/01_basic/client.rb +88 -0
  41. data/examples/01_basic/server.rb +100 -0
  42. data/examples/02_async_execution/client.rb +107 -0
  43. data/examples/02_async_execution/server.rb +56 -0
  44. data/examples/03_run_management/client.rb +115 -0
  45. data/examples/03_run_management/server.rb +84 -0
  46. data/examples/04_rich_messages/client.rb +160 -0
  47. data/examples/04_rich_messages/server.rb +180 -0
  48. data/examples/05_await_resume/client.rb +164 -0
  49. data/examples/05_await_resume/server.rb +114 -0
  50. data/examples/06_agent_metadata/client.rb +188 -0
  51. data/examples/06_agent_metadata/server.rb +192 -0
  52. data/examples/README.md +252 -0
  53. data/examples/run_demo.sh +137 -0
  54. data/lib/simple_acp/client/base.rb +448 -0
  55. data/lib/simple_acp/client/sse.rb +141 -0
  56. data/lib/simple_acp/models/agent_manifest.rb +129 -0
  57. data/lib/simple_acp/models/await.rb +123 -0
  58. data/lib/simple_acp/models/base.rb +147 -0
  59. data/lib/simple_acp/models/errors.rb +102 -0
  60. data/lib/simple_acp/models/events.rb +256 -0
  61. data/lib/simple_acp/models/message.rb +235 -0
  62. data/lib/simple_acp/models/message_part.rb +225 -0
  63. data/lib/simple_acp/models/metadata.rb +161 -0
  64. data/lib/simple_acp/models/run.rb +298 -0
  65. data/lib/simple_acp/models/session.rb +137 -0
  66. data/lib/simple_acp/models/types.rb +210 -0
  67. data/lib/simple_acp/server/agent.rb +116 -0
  68. data/lib/simple_acp/server/app.rb +264 -0
  69. data/lib/simple_acp/server/base.rb +510 -0
  70. data/lib/simple_acp/server/context.rb +210 -0
  71. data/lib/simple_acp/server/falcon_runner.rb +61 -0
  72. data/lib/simple_acp/storage/base.rb +129 -0
  73. data/lib/simple_acp/storage/memory.rb +108 -0
  74. data/lib/simple_acp/storage/postgresql.rb +233 -0
  75. data/lib/simple_acp/storage/redis.rb +178 -0
  76. data/lib/simple_acp/version.rb +5 -0
  77. data/lib/simple_acp.rb +91 -0
  78. data/mkdocs.yml +152 -0
  79. data/sig/simple_acp.rbs +4 -0
  80. metadata +418 -0
@@ -0,0 +1,115 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Run Management Example - Client
5
+ #
6
+ # Demonstrates:
7
+ # - Cancelling a running task with run_cancel
8
+ # - Retrieving run event history with run_events
9
+ # - Pagination of events
10
+ #
11
+ # First start the server: ruby examples/03_run_management/server.rb
12
+ # Then run this client: ruby examples/03_run_management/client.rb
13
+
14
+ require_relative "../../lib/simple_acp"
15
+
16
+ client = SimpleAcp::Client::Base.new(base_url: "http://localhost:8000")
17
+
18
+ puts "=== Run Management Demo ==="
19
+ puts
20
+
21
+ # Verify server is running
22
+ unless client.ping
23
+ puts "Server is not responding"
24
+ exit 1
25
+ end
26
+ puts "Server is healthy"
27
+ puts
28
+
29
+ # --- Demo 1: Cancelling a running task ---
30
+ puts "--- Demo 1: Run Cancellation ---"
31
+ puts
32
+
33
+ puts "Starting a long-running cancellable task..."
34
+ run = client.run_async(agent: "cancellable-task", input: "data processing job")
35
+ puts "Run ID: #{run.run_id}"
36
+ puts
37
+
38
+ puts "Letting it run for 1.5 seconds..."
39
+ sleep(1.5)
40
+
41
+ status = client.run_status(run.run_id)
42
+ puts "Current status: #{status.status}"
43
+ puts
44
+
45
+ puts "Cancelling the run..."
46
+ cancelled_run = client.run_cancel(run.run_id)
47
+ puts "Cancel requested. Status: #{cancelled_run.status}"
48
+ puts
49
+
50
+ # Wait a moment for cancellation to complete
51
+ sleep(0.5)
52
+
53
+ final_status = client.run_status(run.run_id)
54
+ puts "Final status: #{final_status.status}"
55
+ puts "Output messages:"
56
+ final_status.output.each do |message|
57
+ puts " #{message.text_content}"
58
+ end
59
+ puts
60
+
61
+ # --- Demo 2: Let a task complete normally for comparison ---
62
+ puts "--- Demo 2: Normal Completion (for comparison) ---"
63
+ puts
64
+
65
+ puts "Starting a shorter task (will complete normally)..."
66
+ run = client.run_sync(agent: "cancellable-task", input: "quick task")
67
+ puts "Status: #{run.status}"
68
+ puts "Final message: #{run.output.last&.text_content}"
69
+ puts
70
+
71
+ # --- Demo 3: Event History ---
72
+ puts "--- Demo 3: Event History with run_events ---"
73
+ puts
74
+
75
+ puts "Generating events..."
76
+ run = client.run_sync(agent: "event-generator", input: "8")
77
+ puts "Run completed with #{run.output.length} output messages"
78
+ puts
79
+
80
+ puts "Retrieving all events for this run..."
81
+ events = client.run_events(run.run_id)
82
+ puts "Total events retrieved: #{events.length}"
83
+ puts
84
+ puts "Event types:"
85
+ event_counts = events.group_by { |e| e.class.name.split("::").last }
86
+ event_counts.each do |type, evts|
87
+ puts " #{type}: #{evts.length}"
88
+ end
89
+ puts
90
+
91
+ puts "First 5 events:"
92
+ events.first(5).each_with_index do |event, i|
93
+ event_type = event.class.name.split("::").last
94
+ puts " #{i + 1}. #{event_type}"
95
+ end
96
+ puts
97
+
98
+ # --- Demo 4: Event Pagination ---
99
+ puts "--- Demo 4: Event Pagination ---"
100
+ puts
101
+
102
+ puts "Retrieving events with pagination (limit: 5, offset: 0)..."
103
+ page1 = client.run_events(run.run_id, limit: 5, offset: 0)
104
+ puts "Page 1: #{page1.length} events"
105
+
106
+ puts "Retrieving events with pagination (limit: 5, offset: 5)..."
107
+ page2 = client.run_events(run.run_id, limit: 5, offset: 5)
108
+ puts "Page 2: #{page2.length} events"
109
+
110
+ puts "Retrieving events with pagination (limit: 5, offset: 10)..."
111
+ page3 = client.run_events(run.run_id, limit: 5, offset: 10)
112
+ puts "Page 3: #{page3.length} events"
113
+ puts
114
+
115
+ puts "=== Run Management Demo Complete ==="
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Run Management Example - Server
5
+ #
6
+ # Demonstrates run cancellation and event tracking.
7
+ #
8
+ # Run with: ruby examples/03_run_management/server.rb
9
+ # Then connect with: ruby examples/03_run_management/client.rb
10
+
11
+ require_relative "../../lib/simple_acp"
12
+
13
+ server = SimpleAcp::Server::Base.new
14
+
15
+ # Long-running cancellable agent
16
+ server.agent("cancellable-task",
17
+ description: "A long task that can be cancelled (10 iterations)"
18
+ ) do |context|
19
+ task_name = context.input.first&.text_content || "unnamed task"
20
+
21
+ Enumerator.new do |yielder|
22
+ yielder << SimpleAcp::Server::RunYield.new(
23
+ SimpleAcp::Models::Message.agent("Starting: #{task_name}")
24
+ )
25
+
26
+ 10.times do |i|
27
+ # Check for cancellation before each iteration
28
+ if context.cancelled?
29
+ yielder << SimpleAcp::Server::RunYield.new(
30
+ SimpleAcp::Models::Message.agent("Task cancelled at iteration #{i + 1}")
31
+ )
32
+ break
33
+ end
34
+
35
+ # Simulate work
36
+ if defined?(Async::Task) && Async::Task.current?
37
+ Async::Task.current.sleep(0.5)
38
+ else
39
+ sleep(0.5)
40
+ end
41
+
42
+ yielder << SimpleAcp::Server::RunYield.new(
43
+ SimpleAcp::Models::Message.agent("Iteration #{i + 1}/10 complete")
44
+ )
45
+ end
46
+
47
+ unless context.cancelled?
48
+ yielder << SimpleAcp::Server::RunYield.new(
49
+ SimpleAcp::Models::Message.agent("Task '#{task_name}' finished successfully!")
50
+ )
51
+ end
52
+ end
53
+ end
54
+
55
+ # Agent that generates many events for event history demo
56
+ server.agent("event-generator",
57
+ description: "Generates multiple events for history tracking"
58
+ ) do |context|
59
+ count = context.input.first&.text_content&.to_i || 5
60
+ count = [count, 20].min # Cap at 20
61
+
62
+ Enumerator.new do |yielder|
63
+ count.times do |i|
64
+ yielder << SimpleAcp::Server::RunYield.new(
65
+ SimpleAcp::Models::Message.agent("Event #{i + 1}: Generated at #{Time.now.strftime('%H:%M:%S.%L')}")
66
+ )
67
+
68
+ if defined?(Async::Task) && Async::Task.current?
69
+ Async::Task.current.sleep(0.1)
70
+ else
71
+ sleep(0.1)
72
+ end
73
+ end
74
+ end
75
+ end
76
+
77
+ puts "Starting Run Management Demo Server..."
78
+ puts "Available agents:"
79
+ server.agents.each do |name, agent|
80
+ puts " - #{name}: #{agent.description}"
81
+ end
82
+ puts
83
+
84
+ server.run(port: 8000)
@@ -0,0 +1,160 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Rich Messages Example - Client
5
+ #
6
+ # Demonstrates:
7
+ # - Receiving JSON content in message parts
8
+ # - Receiving base64 encoded data (images)
9
+ # - Receiving URL references
10
+ # - Multi-part messages with different content types
11
+ #
12
+ # First start the server: ruby examples/04_rich_messages/server.rb
13
+ # Then run this client: ruby examples/04_rich_messages/client.rb
14
+
15
+ require_relative "../../lib/simple_acp"
16
+ require "json"
17
+ require "base64"
18
+
19
+ client = SimpleAcp::Client::Base.new(base_url: "http://localhost:8000")
20
+
21
+ puts "=== Rich Messages Demo ==="
22
+ puts
23
+
24
+ # Verify server is running
25
+ unless client.ping
26
+ puts "Server is not responding"
27
+ exit 1
28
+ end
29
+ puts "Server is healthy"
30
+ puts
31
+
32
+ # --- Demo 1: JSON Data ---
33
+ puts "--- Demo 1: JSON Content ---"
34
+ puts
35
+
36
+ puts "Querying for 'users' data..."
37
+ run = client.run_sync(agent: "json-data", input: "users")
38
+
39
+ run.output.each do |message|
40
+ message.parts.each do |part|
41
+ case part.content_type
42
+ when "application/json"
43
+ puts "JSON Part:"
44
+ data = JSON.parse(part.content)
45
+ puts " Query: #{data['query']}"
46
+ puts " Count: #{data['count']}"
47
+ puts " Results:"
48
+ data["results"]&.each do |user|
49
+ puts " - #{user['name']} (#{user['role']})"
50
+ end
51
+ when "text/plain"
52
+ puts "Text Part: #{part.content}"
53
+ end
54
+ end
55
+ end
56
+ puts
57
+
58
+ puts "Querying for 'stats' data..."
59
+ run = client.run_sync(agent: "json-data", input: "stats")
60
+
61
+ run.output.each do |message|
62
+ message.parts.each do |part|
63
+ if part.content_type == "application/json"
64
+ data = JSON.parse(part.content)
65
+ puts "Stats (#{data['timestamp']}):"
66
+ data["metrics"]&.each do |key, value|
67
+ puts " #{key}: #{value}"
68
+ end
69
+ end
70
+ end
71
+ end
72
+ puts
73
+
74
+ # --- Demo 2: Image/Binary Data ---
75
+ puts "--- Demo 2: Base64 Encoded Image ---"
76
+ puts
77
+
78
+ %w[red green blue].each do |color|
79
+ puts "Generating #{color} circle..."
80
+ run = client.run_sync(agent: "image-generator", input: color)
81
+
82
+ run.output.each do |message|
83
+ message.parts.each do |part|
84
+ case part.content_type
85
+ when "image/svg+xml"
86
+ # Decode and show first line of SVG
87
+ svg = Base64.decode64(part.content)
88
+ puts " SVG data received (#{part.content.length} bytes base64)"
89
+ puts " First line: #{svg.lines.first.strip}"
90
+ when "text/plain"
91
+ puts " #{part.content}"
92
+ end
93
+ end
94
+ end
95
+ end
96
+ puts
97
+
98
+ # --- Demo 3: URL References ---
99
+ puts "--- Demo 3: URL References ---"
100
+ puts
101
+
102
+ %w[ruby python javascript].each do |topic|
103
+ puts "Getting links for '#{topic}'..."
104
+ run = client.run_sync(agent: "link-provider", input: topic)
105
+
106
+ run.output.each do |message|
107
+ message.parts.each do |part|
108
+ if part.metadata && part.metadata["url"]
109
+ puts " #{part.metadata['title']}"
110
+ puts " URL: #{part.metadata['url']}"
111
+ elsif part.content&.include?("Found")
112
+ puts " #{part.content}"
113
+ end
114
+ end
115
+ end
116
+ puts
117
+ end
118
+
119
+ # --- Demo 4: Multi-Format Response ---
120
+ puts "--- Demo 4: Multi-Format Response ---"
121
+ puts
122
+
123
+ puts "Requesting greeting in multiple formats..."
124
+ run = client.run_sync(agent: "multi-format", input: "Developer")
125
+
126
+ run.output.each do |message|
127
+ puts "Message has #{message.parts.length} parts:"
128
+ message.parts.each_with_index do |part, i|
129
+ puts
130
+ puts " Part #{i + 1} (#{part.content_type}):"
131
+ case part.content_type
132
+ when "application/json"
133
+ data = JSON.parse(part.content)
134
+ puts " #{data.inspect}"
135
+ when "text/html"
136
+ # Show HTML without rendering
137
+ puts " #{part.content.gsub("\n", " ").strip}"
138
+ else
139
+ puts " #{part.content}"
140
+ end
141
+ end
142
+ end
143
+ puts
144
+
145
+ # --- Demo 5: Content Type Inspection ---
146
+ puts "--- Demo 5: Agent Content Type Capabilities ---"
147
+ puts
148
+
149
+ puts "Listing agents and their content types:"
150
+ agents_response = client.agents
151
+ agents_response.agents.each do |agent|
152
+ puts
153
+ puts "#{agent.name}:"
154
+ puts " Description: #{agent.description}"
155
+ puts " Input types: #{agent.input_content_types&.join(', ') || 'any'}"
156
+ puts " Output types: #{agent.output_content_types&.join(', ') || 'any'}"
157
+ end
158
+ puts
159
+
160
+ puts "=== Rich Messages Demo Complete ==="
@@ -0,0 +1,180 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Rich Messages Example - Server
5
+ #
6
+ # Demonstrates different message part types:
7
+ # - Text content
8
+ # - JSON content
9
+ # - Base64 encoded data
10
+ # - URL references
11
+ #
12
+ # Run with: ruby examples/04_rich_messages/server.rb
13
+ # Then connect with: ruby examples/04_rich_messages/client.rb
14
+
15
+ require_relative "../../lib/simple_acp"
16
+ require "json"
17
+ require "base64"
18
+
19
+ server = SimpleAcp::Server::Base.new
20
+
21
+ # Agent that returns JSON data
22
+ server.agent("json-data",
23
+ description: "Returns structured JSON data",
24
+ output_content_types: ["application/json", "text/plain"]
25
+ ) do |context|
26
+ query = context.input.first&.text_content || "users"
27
+
28
+ # Simulate different data queries
29
+ data = case query.downcase
30
+ when "users"
31
+ {
32
+ query: "users",
33
+ count: 3,
34
+ results: [
35
+ { id: 1, name: "Alice", role: "admin" },
36
+ { id: 2, name: "Bob", role: "user" },
37
+ { id: 3, name: "Charlie", role: "user" }
38
+ ]
39
+ }
40
+ when "stats"
41
+ {
42
+ query: "stats",
43
+ timestamp: Time.now.iso8601,
44
+ metrics: {
45
+ requests_today: 1234,
46
+ active_users: 42,
47
+ uptime_percent: 99.9
48
+ }
49
+ }
50
+ else
51
+ {
52
+ query: query,
53
+ error: "Unknown query type",
54
+ available: %w[users stats]
55
+ }
56
+ end
57
+
58
+ # Return message with both JSON and text parts
59
+ message = SimpleAcp::Models::Message.new(
60
+ role: "agent",
61
+ parts: [
62
+ SimpleAcp::Models::MessagePart.json(data),
63
+ SimpleAcp::Models::MessagePart.text("Query '#{query}' returned #{data[:results]&.length || 0} results")
64
+ ]
65
+ )
66
+
67
+ message
68
+ end
69
+
70
+ # Agent that returns a simple image (small SVG as base64)
71
+ server.agent("image-generator",
72
+ description: "Generates a simple SVG image",
73
+ output_content_types: ["image/svg+xml", "text/plain"]
74
+ ) do |context|
75
+ color = context.input.first&.text_content || "blue"
76
+
77
+ # Create a simple SVG
78
+ svg = <<~SVG
79
+ <svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
80
+ <rect width="100" height="100" fill="transparent"/>
81
+ <circle cx="50" cy="50" r="40" fill="#{color}" opacity="0.8"/>
82
+ <text x="50" y="55" text-anchor="middle" fill="white" font-size="12">#{color}</text>
83
+ </svg>
84
+ SVG
85
+
86
+ encoded = Base64.strict_encode64(svg)
87
+
88
+ message = SimpleAcp::Models::Message.new(
89
+ role: "agent",
90
+ parts: [
91
+ SimpleAcp::Models::MessagePart.new(
92
+ kind: "data",
93
+ content: encoded,
94
+ content_type: "image/svg+xml"
95
+ ),
96
+ SimpleAcp::Models::MessagePart.text("Generated #{color} circle SVG (#{encoded.length} bytes base64)")
97
+ ]
98
+ )
99
+
100
+ message
101
+ end
102
+
103
+ # Agent that returns URL references
104
+ server.agent("link-provider",
105
+ description: "Provides relevant URLs and references",
106
+ output_content_types: ["text/uri-list", "text/plain"]
107
+ ) do |context|
108
+ topic = context.input.first&.text_content || "ruby"
109
+
110
+ links = case topic.downcase
111
+ when "ruby"
112
+ [
113
+ { url: "https://www.ruby-lang.org/", title: "Ruby Official Site" },
114
+ { url: "https://rubygems.org/", title: "RubyGems" },
115
+ { url: "https://guides.rubyonrails.org/", title: "Rails Guides" }
116
+ ]
117
+ when "python"
118
+ [
119
+ { url: "https://www.python.org/", title: "Python Official Site" },
120
+ { url: "https://pypi.org/", title: "PyPI" },
121
+ { url: "https://docs.python.org/", title: "Python Docs" }
122
+ ]
123
+ else
124
+ [
125
+ { url: "https://www.google.com/search?q=#{topic}", title: "Search for #{topic}" }
126
+ ]
127
+ end
128
+
129
+ # Create message with URL parts
130
+ parts = links.map do |link|
131
+ SimpleAcp::Models::MessagePart.new(
132
+ kind: "text",
133
+ content: "#{link[:title]}: #{link[:url]}",
134
+ content_type: "text/plain",
135
+ metadata: { url: link[:url], title: link[:title] }
136
+ )
137
+ end
138
+
139
+ parts << SimpleAcp::Models::MessagePart.text("Found #{links.length} links for '#{topic}'")
140
+
141
+ SimpleAcp::Models::Message.new(role: "agent", parts: parts)
142
+ end
143
+
144
+ # Agent demonstrating multi-part responses
145
+ server.agent("multi-format",
146
+ description: "Returns data in multiple formats",
147
+ output_content_types: ["application/json", "text/plain", "text/html"]
148
+ ) do |context|
149
+ name = context.input.first&.text_content || "World"
150
+
151
+ # Return same greeting in multiple formats
152
+ message = SimpleAcp::Models::Message.new(
153
+ role: "agent",
154
+ parts: [
155
+ SimpleAcp::Models::MessagePart.text("Hello, #{name}!"),
156
+ SimpleAcp::Models::MessagePart.json({ greeting: "Hello", recipient: name, timestamp: Time.now.iso8601 }),
157
+ SimpleAcp::Models::MessagePart.new(
158
+ kind: "text",
159
+ content: "<h1>Hello, #{name}!</h1><p>Welcome to the multi-format demo.</p>",
160
+ content_type: "text/html"
161
+ )
162
+ ]
163
+ )
164
+
165
+ message
166
+ end
167
+
168
+ puts "Starting Rich Messages Demo Server..."
169
+ puts "Available agents:"
170
+ server.agents.each do |name, agent|
171
+ puts " - #{name}: #{agent.description}"
172
+ puts " Input types: #{agent.manifest.input_content_types.join(', ')}"
173
+ puts " Output types: #{agent.manifest.output_content_types.join(', ')}"
174
+ if agent.manifest.metadata
175
+ puts " Has metadata: yes"
176
+ end
177
+ end
178
+ puts
179
+
180
+ server.run(port: 8000)
@@ -0,0 +1,164 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Await/Resume Example - Client
5
+ #
6
+ # Demonstrates:
7
+ # - Detecting when a run is awaiting input
8
+ # - Resuming a run with run_resume_sync
9
+ # - Multi-step interactive flows
10
+ # - Streaming resume with run_resume_stream
11
+ #
12
+ # First start the server: ruby examples/05_await_resume/server.rb
13
+ # Then run this client: ruby examples/05_await_resume/client.rb
14
+
15
+ require_relative "../../lib/simple_acp"
16
+
17
+ client = SimpleAcp::Client::Base.new(base_url: "http://localhost:8000")
18
+
19
+ puts "=== Await/Resume Demo ==="
20
+ puts
21
+
22
+ # Verify server is running
23
+ unless client.ping
24
+ puts "Server is not responding"
25
+ exit 1
26
+ end
27
+ puts "Server is healthy"
28
+ puts
29
+
30
+ # --- Demo 1: Simple greeter with await/resume ---
31
+ puts "--- Demo 1: Simple Greeter (Single Await) ---"
32
+ puts
33
+
34
+ puts "Starting greeter agent..."
35
+ run = client.run_sync(agent: "greeter", input: "start")
36
+
37
+ if run.status == "awaiting"
38
+ puts "Status: #{run.status}"
39
+ puts "Agent asks: #{run.await_request&.message&.text_content}"
40
+ puts
41
+
42
+ # Resume with our response
43
+ puts "Responding with: 'Alice'"
44
+ resume = SimpleAcp::Models::MessageAwaitResume.new(
45
+ message: SimpleAcp::Models::Message.user("Alice")
46
+ )
47
+
48
+ completed_run = client.run_resume_sync(run_id: run.run_id, await_resume: resume)
49
+ puts "Status: #{completed_run.status}"
50
+ puts "Agent says: #{completed_run.output.last&.text_content}"
51
+ else
52
+ puts "Unexpected status: #{run.status}"
53
+ end
54
+ puts
55
+
56
+ # --- Demo 2: Multi-step survey ---
57
+ puts "--- Demo 2: Multi-Step Survey (Multiple Awaits) ---"
58
+ puts
59
+
60
+ puts "Starting survey..."
61
+ run = client.run_sync(agent: "survey", input: "begin")
62
+
63
+ responses = ["Bob", "blue", "42"]
64
+ response_index = 0
65
+
66
+ while run.status == "awaiting"
67
+ puts "Agent asks: #{run.await_request&.message&.text_content}"
68
+
69
+ if response_index < responses.length
70
+ response = responses[response_index]
71
+ puts "Responding with: '#{response}'"
72
+ puts
73
+
74
+ resume = SimpleAcp::Models::MessageAwaitResume.new(
75
+ message: SimpleAcp::Models::Message.user(response)
76
+ )
77
+
78
+ run = client.run_resume_sync(run_id: run.run_id, await_resume: resume)
79
+ response_index += 1
80
+ else
81
+ puts "ERROR: Ran out of responses!"
82
+ break
83
+ end
84
+ end
85
+
86
+ if run.status == "completed"
87
+ puts "Survey completed!"
88
+ puts run.output.last&.text_content
89
+ else
90
+ puts "Unexpected final status: #{run.status}"
91
+ end
92
+ puts
93
+
94
+ # --- Demo 3: Confirmation agent - Yes case ---
95
+ puts "--- Demo 3: Confirmation (Yes) ---"
96
+ puts
97
+
98
+ puts "Requesting action: 'delete all files'"
99
+ run = client.run_sync(agent: "confirmer", input: "delete all files")
100
+
101
+ if run.status == "awaiting"
102
+ puts "Agent asks: #{run.await_request&.message&.text_content}"
103
+ puts "Responding with: 'yes'"
104
+
105
+ resume = SimpleAcp::Models::MessageAwaitResume.new(
106
+ message: SimpleAcp::Models::Message.user("yes")
107
+ )
108
+
109
+ completed_run = client.run_resume_sync(run_id: run.run_id, await_resume: resume)
110
+ puts "Result: #{completed_run.output.last&.text_content}"
111
+ end
112
+ puts
113
+
114
+ # --- Demo 4: Confirmation agent - No case ---
115
+ puts "--- Demo 4: Confirmation (No) ---"
116
+ puts
117
+
118
+ puts "Requesting action: 'format hard drive'"
119
+ run = client.run_sync(agent: "confirmer", input: "format hard drive")
120
+
121
+ if run.status == "awaiting"
122
+ puts "Agent asks: #{run.await_request&.message&.text_content}"
123
+ puts "Responding with: 'no'"
124
+
125
+ resume = SimpleAcp::Models::MessageAwaitResume.new(
126
+ message: SimpleAcp::Models::Message.user("no")
127
+ )
128
+
129
+ completed_run = client.run_resume_sync(run_id: run.run_id, await_resume: resume)
130
+ puts "Result: #{completed_run.output.last&.text_content}"
131
+ end
132
+ puts
133
+
134
+ # --- Demo 5: Streaming resume ---
135
+ puts "--- Demo 5: Streaming Resume ---"
136
+ puts
137
+
138
+ puts "Starting greeter with streaming resume..."
139
+ run = client.run_sync(agent: "greeter", input: "start")
140
+
141
+ if run.status == "awaiting"
142
+ puts "Agent asks: #{run.await_request&.message&.text_content}"
143
+ puts "Resuming with streaming..."
144
+
145
+ resume = SimpleAcp::Models::MessageAwaitResume.new(
146
+ message: SimpleAcp::Models::Message.user("Charlie")
147
+ )
148
+
149
+ print "Events: "
150
+ client.run_resume_stream(run_id: run.run_id, await_resume: resume) do |event|
151
+ case event
152
+ when SimpleAcp::Models::RunInProgressEvent
153
+ print "[in_progress] "
154
+ when SimpleAcp::Models::MessageCreatedEvent
155
+ print "[message] "
156
+ when SimpleAcp::Models::RunCompletedEvent
157
+ puts "[completed]"
158
+ puts "Final output: #{event.run.output.last&.text_content}"
159
+ end
160
+ end
161
+ end
162
+ puts
163
+
164
+ puts "=== Await/Resume Demo Complete ==="