pi-agent-rb 0.1.18 → 0.2.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +56 -0
- data/README.md +26 -10
- data/lib/pi_agent/event.rb +5 -4
- data/lib/pi_agent/session.rb +48 -21
- data/lib/pi_agent/version.rb +2 -2
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: eb219f216edc0f9a1077814bc561d50ced56bf59265665b5f62e508029421d64
|
|
4
|
+
data.tar.gz: e17ce2b8d2766358b5504abd6fcff507633c3835d60db1256f15bdeb25e326ff
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 334fbe076244a764f72daa7778e1ae19734c13a9ff414ced6691d5854c9704dc1462c08f0be66f90ac319bfaace117c0ef8c836f36a69d1483477ae3b79004ee
|
|
7
|
+
data.tar.gz: d5d2e098f31f88615808708245776cf6c33b9c0d48ed70a87dbc0261dc53fe4e77d1e8b711e0a69a2b1600d0f55c7bbfd99239fdadb14d6addcfc29ff8db5890
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,62 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.2.1] - 2026-07-25
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
- Bumped pinned upstream `pi-coding-agent` version to `0.82.1` (from
|
|
14
|
+
`0.81.1`, rolling up `0.82.0` and `0.82.1`). This is a provider/tooling
|
|
15
|
+
and reliability batch; none of the changes alter the JSONL RPC
|
|
16
|
+
command/response shapes this gem drives, so no gem API change is required.
|
|
17
|
+
- `0.82.1` adds Claude Opus 5 (Anthropic and Amazon Bedrock, with
|
|
18
|
+
adaptive thinking), `ANTHROPIC_AUTH_TOKEN` bearer auth for
|
|
19
|
+
Anthropic-compatible gateways, and faster/more resilient model
|
|
20
|
+
catalogs (pi.dev revalidates with `If-None-Match`). It exposes the
|
|
21
|
+
`outputPad` setting to custom message renderers and fixes
|
|
22
|
+
compaction/branch summaries for header-only auth providers, `/models`
|
|
23
|
+
hiding of unavailable scoped models, startup context-file `EISDIR`
|
|
24
|
+
warnings, and llama.cpp catalog persistence. All provider/SDK/TUI
|
|
25
|
+
concerns, leaving the RPC contract unchanged.
|
|
26
|
+
- `0.82.0` details follow.
|
|
27
|
+
- Adds constrained tool sampling (`Tool.constrainedSampling` with strict
|
|
28
|
+
JSON Schema and OpenAI Lark/regex grammars) plus `supportsGrammarTools`
|
|
29
|
+
/ `supportsStrictTools` model capability metadata. This is an
|
|
30
|
+
SDK/extension tool-configuration concern, not part of the RPC surface.
|
|
31
|
+
- Adds OpenRouter OAuth PKCE and Kimi Code subscription sign-in via
|
|
32
|
+
`/login`, and exposes `PI_SESSION_ID`, `PI_SESSION_FILE`, `PI_PROVIDER`,
|
|
33
|
+
`PI_MODEL`, and `PI_REASONING_LEVEL` to commands run by built-in and
|
|
34
|
+
factory-created bash tools.
|
|
35
|
+
- Adds streaming `bash_execution_update` events for direct RPC bash
|
|
36
|
+
commands, correlated with request IDs. These flow through
|
|
37
|
+
`PiAgent::Event` transparently — it preserves the native payload on
|
|
38
|
+
`#raw` and exposes `#type` as a symbol — so no gem change is needed to
|
|
39
|
+
consume them.
|
|
40
|
+
- The remainder are inherited provider/retry/model-catalog fixes (DNS
|
|
41
|
+
lookup retries, OpenRouter cache breakpoints, protobufjs 7.6.5 security
|
|
42
|
+
bump, catalog mtime and llama.cpp context fixes) that leave the RPC
|
|
43
|
+
contract unchanged.
|
|
44
|
+
|
|
45
|
+
## [0.2.0] - 2026-07-23
|
|
46
|
+
|
|
47
|
+
Minor (not patch) release: besides the stream-truncation fix, the block form
|
|
48
|
+
of `Session#follow_up` and stream concurrency behave differently (see
|
|
49
|
+
Changed).
|
|
50
|
+
|
|
51
|
+
### Fixed
|
|
52
|
+
- `Session#prompt`, `#follow_up`, `#events`, and `#run` now drain through
|
|
53
|
+
`agent_settled` instead of stopping at the first `agent_end`. Since pi
|
|
54
|
+
0.80.4, `agent_end` marks only one low-level run and may be followed by an
|
|
55
|
+
automatic retry, compaction retry, or queued continuation; stopping there
|
|
56
|
+
could unsubscribe early and silently drop the final response events.
|
|
57
|
+
|
|
58
|
+
### Changed
|
|
59
|
+
- The block form of `Session#follow_up` now uses pi's streaming-aware `prompt`
|
|
60
|
+
command, so sequential follow-ups start when the session is idle while
|
|
61
|
+
the blockless form remains a direct, queue-only `follow_up` RPC command for
|
|
62
|
+
active runs. High-level streams are now single-flight per session because
|
|
63
|
+
pi events have no run IDs; overlapping streams raise `SessionError` instead
|
|
64
|
+
of potentially consuming another run's `agent_settled` event.
|
|
65
|
+
|
|
10
66
|
## [0.1.18] - 2026-07-21
|
|
11
67
|
|
|
12
68
|
### Added
|
data/README.md
CHANGED
|
@@ -13,8 +13,8 @@ building interactive agent UIs (web, TUI) on top of pi.
|
|
|
13
13
|
## Requirements
|
|
14
14
|
|
|
15
15
|
- Ruby 3.3+
|
|
16
|
-
- `pi` on `PATH` (install via `npm i -g @earendil-works/pi-coding-agent`)
|
|
17
|
-
- This gem is pinned against pi `0.
|
|
16
|
+
- `pi` 0.80.4+ on `PATH` (install via `npm i -g @earendil-works/pi-coding-agent`)
|
|
17
|
+
- This gem is pinned against pi `0.82.1`; other versions may work but are not verified.
|
|
18
18
|
|
|
19
19
|
## Installation
|
|
20
20
|
|
|
@@ -47,8 +47,10 @@ end
|
|
|
47
47
|
A pi RPC process hosts one session, so there is no create/select step —
|
|
48
48
|
`PiAgent.session` spawns `pi --mode rpc` and the session *is* that process.
|
|
49
49
|
|
|
50
|
-
`prompt` yields each [`Event`](lib/pi_agent/event.rb) until the agent
|
|
51
|
-
finishes (`
|
|
50
|
+
`prompt` yields each [`Event`](lib/pi_agent/event.rb) until the agent fully
|
|
51
|
+
finishes (`agent_settled`). Unlike `agent_end`, this includes any automatic
|
|
52
|
+
retry, compaction retry, or queued continuation. Without a block it returns
|
|
53
|
+
an `Enumerator`:
|
|
52
54
|
|
|
53
55
|
```ruby
|
|
54
56
|
PiAgent.session do |session|
|
|
@@ -79,9 +81,10 @@ Other session methods:
|
|
|
79
81
|
`set_model` accepts either `set_model("anthropic/claude-sonnet-4-5")` or
|
|
80
82
|
`set_model("anthropic", "claude-sonnet-4-5")`.
|
|
81
83
|
|
|
82
|
-
A `prompt` streams
|
|
83
|
-
|
|
84
|
-
|
|
84
|
+
A `prompt` streams the complete session-level run
|
|
85
|
+
(`agent_start`..`agent_settled`). Pass a block to `follow_up` for a sequential
|
|
86
|
+
follow-up that starts immediately when idle (or queues when an agent is
|
|
87
|
+
already running) and drains each `Event` through `agent_settled`:
|
|
85
88
|
|
|
86
89
|
```ruby
|
|
87
90
|
PiAgent.session do |session|
|
|
@@ -90,14 +93,27 @@ PiAgent.session do |session|
|
|
|
90
93
|
end
|
|
91
94
|
```
|
|
92
95
|
|
|
93
|
-
The block form is race-free: `follow_up` subscribes to the event stream
|
|
94
|
-
|
|
96
|
+
The block form is race-free: `follow_up` subscribes to the event stream before
|
|
97
|
+
sending a `prompt` with pi's `streamingBehavior: "followUp"`, so none of the
|
|
98
|
+
cycle's events are missed. Use it only after the previous high-level stream has
|
|
99
|
+
settled; pi events have no run IDs, so a session permits only one high-level
|
|
100
|
+
event stream at a time. While a stream is active, use blockless `follow_up` to
|
|
101
|
+
queue a continuation—the active stream will include it through
|
|
102
|
+
`agent_settled`. The block form accepts plain agent input, not slash commands.
|
|
103
|
+
|
|
104
|
+
Pi emits `agent_end` after each low-level agent run, but may then retry,
|
|
105
|
+
compact and retry, or process queued continuations. It emits
|
|
106
|
+
`agent_settled` only when no automatic work remains, so high-level streams
|
|
107
|
+
use that as their completion boundary. Upstream added the RPC event in pi
|
|
108
|
+
0.80.4; it is available in this gem's pinned pi 0.82.1.
|
|
95
109
|
|
|
96
110
|
`events` is a lower-level, prompt-less drain of the same stream. Because it
|
|
97
111
|
subscribes lazily when iteration begins, it only works when you subscribe
|
|
98
112
|
*before* the cycle starts — e.g. begin iterating it from a thread, then
|
|
99
113
|
trigger the cycle. For the common follow-up case, prefer the block form
|
|
100
|
-
above.
|
|
114
|
+
above. `prompt`, block-form `follow_up`, and `events` are single-flight on a
|
|
115
|
+
session; starting another before the current stream settles raises
|
|
116
|
+
`PiAgent::SessionError` rather than consuming an unrelated settlement event.
|
|
101
117
|
|
|
102
118
|
### Images
|
|
103
119
|
|
data/lib/pi_agent/event.rb
CHANGED
|
@@ -6,12 +6,13 @@ module PiAgent
|
|
|
6
6
|
# dedicated accessor yet.
|
|
7
7
|
#
|
|
8
8
|
# Event types are exposed as Ruby symbols (e.g. `:text_delta`,
|
|
9
|
-
# `:
|
|
9
|
+
# `:agent_settled`) matching the upstream protocol's `type` field.
|
|
10
10
|
class Event
|
|
11
11
|
# Event types that terminate a single prompt's event stream.
|
|
12
|
-
# `agent_end`
|
|
13
|
-
#
|
|
14
|
-
|
|
12
|
+
# `agent_end` only finishes one low-level run; retries, compaction, or
|
|
13
|
+
# queued continuations may follow. `agent_settled` is emitted once all
|
|
14
|
+
# automatic work has finished.
|
|
15
|
+
TERMINAL_TYPES = %i[agent_settled].freeze
|
|
15
16
|
|
|
16
17
|
attr_reader :raw, :type
|
|
17
18
|
|
data/lib/pi_agent/session.rb
CHANGED
|
@@ -13,12 +13,12 @@ module PiAgent
|
|
|
13
13
|
# A pi RPC process hosts exactly one session, so there is no
|
|
14
14
|
# create/select step — the Session *is* the running pi process.
|
|
15
15
|
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
# `follow_up`
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
16
|
+
# `prompt` streams until the session-level run settles, including retries,
|
|
17
|
+
# automatic compaction retries, and queued continuations. A message queued
|
|
18
|
+
# with blockless `follow_up` runs after an active agent; pass a block to
|
|
19
|
+
# submit it as a streaming-aware prompt and drain it race-free. `events` is
|
|
20
|
+
# a prompt-less drain for when you have already subscribed before processing
|
|
21
|
+
# starts.
|
|
22
22
|
class Session
|
|
23
23
|
# Max time to wait for the next event before assuming the agent stalled.
|
|
24
24
|
DEFAULT_EVENT_TIMEOUT = 300
|
|
@@ -29,11 +29,14 @@ module PiAgent
|
|
|
29
29
|
|
|
30
30
|
def initialize(client)
|
|
31
31
|
@client = client
|
|
32
|
+
@stream_state_mutex = Mutex.new
|
|
33
|
+
@event_stream_active = false
|
|
32
34
|
end
|
|
33
35
|
|
|
34
|
-
# Submit a user prompt. With a block, yields each Event until the
|
|
35
|
-
#
|
|
36
|
-
#
|
|
36
|
+
# Submit a user prompt. With a block, yields each Event until the agent
|
|
37
|
+
# fully settles (agent_settled), then returns self. This includes any
|
|
38
|
+
# automatic retry, compaction retry, or queued continuation. Without a
|
|
39
|
+
# block, returns an Enumerator of Events.
|
|
37
40
|
#
|
|
38
41
|
# `images` accepts PiAgent::Image objects, file path strings, or
|
|
39
42
|
# raw ImageContent hashes — in any mix.
|
|
@@ -47,8 +50,8 @@ module PiAgent
|
|
|
47
50
|
end
|
|
48
51
|
|
|
49
52
|
# Drain the event stream without submitting a new prompt. With a block,
|
|
50
|
-
# yields each Event until the
|
|
51
|
-
# self; without a block, returns an Enumerator of Events.
|
|
53
|
+
# yields each Event until the run fully settles (agent_settled) and
|
|
54
|
+
# returns self; without a block, returns an Enumerator of Events.
|
|
52
55
|
#
|
|
53
56
|
# The subscription is established lazily, when iteration begins — so any
|
|
54
57
|
# cycle triggered *before* you call `events` may have already emitted
|
|
@@ -76,12 +79,12 @@ module PiAgent
|
|
|
76
79
|
|
|
77
80
|
# Queue a follow-up message, delivered only after the agent stops.
|
|
78
81
|
#
|
|
79
|
-
# Without a block this
|
|
80
|
-
# returns self
|
|
81
|
-
#
|
|
82
|
-
#
|
|
83
|
-
#
|
|
84
|
-
#
|
|
82
|
+
# Without a block this maps to pi's raw `follow_up` command: it only queues
|
|
83
|
+
# the message and returns self, so call it while an agent run is active.
|
|
84
|
+
# With a block it uses pi's streaming-aware `prompt` command, which starts
|
|
85
|
+
# a run when idle or queues a follow-up when busy. The subscription is
|
|
86
|
+
# established before sending, then yields through agent_settled and returns
|
|
87
|
+
# self. Prefer this form for a sequential, consumable follow-up.
|
|
85
88
|
def follow_up(message, images: nil, event_timeout: DEFAULT_EVENT_TIMEOUT, &block)
|
|
86
89
|
params = message_params(message, images)
|
|
87
90
|
unless block
|
|
@@ -89,7 +92,12 @@ module PiAgent
|
|
|
89
92
|
return self
|
|
90
93
|
end
|
|
91
94
|
|
|
92
|
-
|
|
95
|
+
if message.start_with?("/")
|
|
96
|
+
raise SessionError, "Block-form follow_up does not support slash commands; use prompt instead"
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
params[:streamingBehavior] = "followUp"
|
|
100
|
+
event_stream("prompt", params, event_timeout: event_timeout).each(&block)
|
|
93
101
|
self
|
|
94
102
|
end
|
|
95
103
|
|
|
@@ -280,17 +288,36 @@ module PiAgent
|
|
|
280
288
|
# iteration of the returned Enumerator so cleanup is deterministic.
|
|
281
289
|
def subscribed_stream(event_timeout:, &before_pump)
|
|
282
290
|
Enumerator.new do |yielder|
|
|
283
|
-
|
|
284
|
-
|
|
291
|
+
claim_event_stream!
|
|
292
|
+
|
|
293
|
+
handle = nil
|
|
285
294
|
begin
|
|
295
|
+
queue = Queue.new
|
|
296
|
+
handle = @client.subscribe { |msg| queue << msg }
|
|
286
297
|
before_pump&.call
|
|
287
298
|
pump_events(queue, yielder, event_timeout)
|
|
288
299
|
ensure
|
|
289
|
-
@client.unsubscribe(handle)
|
|
300
|
+
@client.unsubscribe(handle) if handle
|
|
301
|
+
release_event_stream
|
|
290
302
|
end
|
|
291
303
|
end
|
|
292
304
|
end
|
|
293
305
|
|
|
306
|
+
def claim_event_stream!
|
|
307
|
+
claimed = @stream_state_mutex.synchronize do
|
|
308
|
+
next false if @event_stream_active
|
|
309
|
+
|
|
310
|
+
@event_stream_active = true
|
|
311
|
+
end
|
|
312
|
+
return if claimed
|
|
313
|
+
|
|
314
|
+
raise SessionError, "Another event stream is active; queue with blockless follow_up or wait for it to settle"
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
def release_event_stream
|
|
318
|
+
@stream_state_mutex.synchronize { @event_stream_active = false }
|
|
319
|
+
end
|
|
320
|
+
|
|
294
321
|
def pump_events(queue, yielder, event_timeout)
|
|
295
322
|
loop do
|
|
296
323
|
msg = queue.pop(timeout: event_timeout)
|
data/lib/pi_agent/version.rb
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module PiAgent
|
|
4
|
-
VERSION = "0.1
|
|
4
|
+
VERSION = "0.2.1"
|
|
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.
|
|
8
|
+
SUPPORTED_PI_VERSION = "0.82.1"
|
|
9
9
|
end
|