pi-agent-rb 0.1.9 → 0.1.10

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 14badc81403e563a04b5280ad81fb0205d16c5ff0e413c8f80025a1a57b559d9
4
- data.tar.gz: da9584bec2e0e637f4ca59162ce809a27168b1551d81236afdb4324b003eb255
3
+ metadata.gz: 764c10abd215cb94244b349a446dd3743637196556fe3b302abf0c221c78d554
4
+ data.tar.gz: 19cc0cc64de81a17d120dceffe88ad862c959c210d791400b1883e160648a0ea
5
5
  SHA512:
6
- metadata.gz: ad277fa02847fffc564c066646fdadaa539e8781897aa81d93d15f666593ee3eb62ee9285034fbc28678739ec4f8054adbf285aeb8dc0a2374cf609d3343568b
7
- data.tar.gz: 5d72e811dfd4317954296f288f467b5c74ff81fc2a736b1661c0f1d1ee0beacac11615acbdba2c5892b65ae0d46be0982acae73e28c2fef57ec078cb04e66d9f
6
+ metadata.gz: 22022aa55d47d49cd2d9b547a2ee846e590dd4bbe3133a577b579662b806d560a99a852f0ee9667c5bdd9138cd32c6292f2d217cedf8e1d2acd1a76fd77ffbf3
7
+ data.tar.gz: aaa78bfe30ee3e2b9db5aab67b0eba26bbd6c8e62a3d3626aff496a0c345815fde43fe001894bccfbec01497fc8586244140a7f597d22cb73d825f034414e441
data/CHANGELOG.md CHANGED
@@ -7,6 +7,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.1.10] - 2026-06-25
11
+
12
+ ### Changed
13
+ - Bumped pinned upstream `pi-coding-agent` version to `0.80.2` (rolls up
14
+ 0.80.0–0.80.2: the old global `@earendil-works/pi-ai` API moved to the
15
+ `@earendil-works/pi-ai/compat` entrypoint and the selective `/base`
16
+ provider entrypoints were removed; plus provider/auth resolution fixes
17
+ (Bedrock, Cloudflare, Fireworks, OpenAI Responses/Codex), session-name
18
+ newline normalization, and a `Ctrl+J` newline keybinding). These are
19
+ SDK/library-level changes; no changes to the RPC protocol surface or the
20
+ `--approve`/`--no-approve` flags this gem drives.
21
+
22
+ ### Added
23
+ - `Session#follow_up` now accepts a block to drain the agent cycle the queued
24
+ message triggers, mirroring `prompt`'s block contract. It is race-free —
25
+ the event subscription is established before the message is sent, so none
26
+ of the cycle's events are missed.
27
+ - `Session#events`: a lower-level, prompt-less drain of the agent event
28
+ stream, for callers that subscribe before the cycle starts (e.g. from a
29
+ thread). Mirrors `prompt`'s block/Enumerator contract. (The class docstring
30
+ already referenced this method; it now exists.)
31
+
10
32
  ## [0.1.9] - 2026-06-22
11
33
 
12
34
  ### Changed
data/README.md CHANGED
@@ -14,7 +14,7 @@ building interactive agent UIs (web, TUI) on top of pi.
14
14
 
15
15
  - Ruby 3.3+
16
16
  - `pi` on `PATH` (install via `npm i -g @earendil-works/pi-coding-agent`)
17
- - This gem is pinned against pi `0.79.10`; other versions may work but are not verified.
17
+ - This gem is pinned against pi `0.80.2`; other versions may work but are not verified.
18
18
 
19
19
  ## Installation
20
20
 
@@ -69,7 +69,7 @@ end
69
69
 
70
70
  Other session methods:
71
71
 
72
- - Prompting: `steer`, `follow_up`, `abort`
72
+ - Prompting: `steer`, `follow_up`, `events`, `abort`
73
73
  - Model: `set_model`, `cycle_model`, `available_models`, `set_thinking`
74
74
  - State: `get_state`, `messages`, `last_assistant_text`, `session_stats`
75
75
  - Context: `compact`
@@ -79,6 +79,26 @@ Other session methods:
79
79
  `set_model` accepts either `set_model("anthropic/claude-sonnet-4-5")` or
80
80
  `set_model("anthropic", "claude-sonnet-4-5")`.
81
81
 
82
+ A `prompt` streams one agent cycle (`agent_start`..`agent_end`). A message
83
+ queued with `follow_up` runs in a *later* cycle; pass a block to `follow_up`
84
+ to drain that cycle. Like `prompt`, it yields each `Event` until `agent_end`:
85
+
86
+ ```ruby
87
+ PiAgent.session do |session|
88
+ session.prompt("Draft a haiku") { |e| print e.delta if e.type == :message_update }
89
+ session.follow_up("Now translate it to French") { |e| print e.delta if e.type == :message_update }
90
+ end
91
+ ```
92
+
93
+ The block form is race-free: `follow_up` subscribes to the event stream
94
+ *before* sending the message, so none of the cycle's events are missed.
95
+
96
+ `events` is a lower-level, prompt-less drain of the same stream. Because it
97
+ subscribes lazily when iteration begins, it only works when you subscribe
98
+ *before* the cycle starts — e.g. begin iterating it from a thread, then
99
+ trigger the cycle. For the common follow-up case, prefer the block form
100
+ above.
101
+
82
102
  ### Images
83
103
 
84
104
  `prompt`, `steer`, and `follow_up` accept an `images:` array. Entries
@@ -14,9 +14,11 @@ module PiAgent
14
14
  # create/select step — the Session *is* the running pi process.
15
15
  #
16
16
  # v1 limitation: `prompt` streams one agent cycle (agent_start..agent_end).
17
- # Messages queued mid-flight via `follow_up`/`steer` run in subsequent
18
- # cycles; consume those by calling `prompt`-less `events` or another
19
- # `prompt`. Bidirectional extension UI is not yet surfaced here.
17
+ # A message queued with `follow_up` runs in a later cycle; pass a block to
18
+ # `follow_up` to drain that cycle race-free (it subscribes before sending).
19
+ # `events` is a prompt-less drain of the same stream for when you have
20
+ # already subscribed before the cycle starts. Bidirectional extension UI
21
+ # is not yet surfaced here.
20
22
  class Session
21
23
  # Max time to wait for the next event before assuming the agent stalled.
22
24
  DEFAULT_EVENT_TIMEOUT = 300
@@ -44,6 +46,26 @@ module PiAgent
44
46
  self
45
47
  end
46
48
 
49
+ # Drain the event stream without submitting a new prompt. With a block,
50
+ # yields each Event until the cycle finishes (agent_end) and returns
51
+ # self; without a block, returns an Enumerator of Events.
52
+ #
53
+ # The subscription is established lazily, when iteration begins — so any
54
+ # cycle triggered *before* you call `events` may have already emitted
55
+ # events that are then missed. To drain a `follow_up` cycle race-free,
56
+ # pass a block to `follow_up` instead. Use `events` only when you
57
+ # subscribe before the cycle starts (e.g. from a thread that begins
58
+ # iterating, then trigger the cycle). With nothing queued, it blocks
59
+ # for `event_timeout` (300s default).
60
+ def events(event_timeout: DEFAULT_EVENT_TIMEOUT, &block)
61
+ stream = subscribed_stream(event_timeout: event_timeout)
62
+
63
+ return stream unless block
64
+
65
+ stream.each(&block)
66
+ self
67
+ end
68
+
47
69
  # Queue a steering message while the agent is running. Delivered after
48
70
  # the current assistant turn finishes its tool calls, before the next
49
71
  # LLM call. Fire-and-forget; raises on rejection.
@@ -53,8 +75,21 @@ module PiAgent
53
75
  end
54
76
 
55
77
  # Queue a follow-up message, delivered only after the agent stops.
56
- def follow_up(message, images: nil)
57
- @client.request("follow_up", message_params(message, images)).value!(timeout: DEFAULT_ACK_TIMEOUT)
78
+ #
79
+ # Without a block this is fire-and-forget: it queues the message and
80
+ # returns self. With a block it drains the resulting agent cycle
81
+ # race-free — the subscription is established *before* the message is
82
+ # sent, so no events are missed — yielding each Event until agent_end
83
+ # and returning self. Prefer the block form to consume a follow-up;
84
+ # the standalone `events` drain only works if you subscribe first.
85
+ def follow_up(message, images: nil, event_timeout: DEFAULT_EVENT_TIMEOUT, &block)
86
+ params = message_params(message, images)
87
+ unless block
88
+ @client.request("follow_up", params).value!(timeout: DEFAULT_ACK_TIMEOUT)
89
+ return self
90
+ end
91
+
92
+ event_stream("follow_up", params, event_timeout: event_timeout).each(&block)
58
93
  self
59
94
  end
60
95
 
@@ -207,14 +242,23 @@ module PiAgent
207
242
  end
208
243
 
209
244
  # Subscribe, send the command, then yield Events from the notification
210
- # stream until a terminal event. The subscription is scoped to one
211
- # iteration of the returned Enumerator so cleanup is deterministic.
245
+ # stream until a terminal event.
212
246
  def event_stream(type, params, event_timeout:)
247
+ subscribed_stream(event_timeout: event_timeout) do
248
+ @client.request(type, params).value!(timeout: DEFAULT_ACK_TIMEOUT)
249
+ end
250
+ end
251
+
252
+ # Subscribe, run `before_pump` (e.g. send a command) once the
253
+ # subscription is live so no events are missed in the gap, then yield
254
+ # Events until a terminal event. The subscription is scoped to one
255
+ # iteration of the returned Enumerator so cleanup is deterministic.
256
+ def subscribed_stream(event_timeout:, &before_pump)
213
257
  Enumerator.new do |yielder|
214
258
  queue = Queue.new
215
259
  handle = @client.subscribe { |msg| queue << msg }
216
260
  begin
217
- @client.request(type, params).value!(timeout: DEFAULT_ACK_TIMEOUT)
261
+ before_pump&.call
218
262
  pump_events(queue, yielder, event_timeout)
219
263
  ensure
220
264
  @client.unsubscribe(handle)
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PiAgent
4
- VERSION = "0.1.9"
4
+ VERSION = "0.1.10"
5
5
 
6
6
  # Pinned upstream pi-coding-agent version this gem is verified against.
7
7
  # See: https://www.npmjs.com/package/@earendil-works/pi-coding-agent
8
- SUPPORTED_PI_VERSION = "0.79.10"
8
+ SUPPORTED_PI_VERSION = "0.80.2"
9
9
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pi-agent-rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.9
4
+ version: 0.1.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - chagel