Class: PiAgent::Session
- Inherits:
-
Object
- Object
- PiAgent::Session
- Defined in:
- lib/pi_agent/session.rb
Overview
High-level agent session. Wraps a Client and exposes prompt-flow ergonomics: submit a prompt and iterate the resulting event stream.
PiAgent.session do |session|
session.prompt("Write a haiku").each do |event|
print event.delta if event.type == :message_update
end
end
A pi RPC process hosts exactly one session, so there is no create/select step — the Session is the running pi process.
v1 limitation: prompt streams one agent cycle (agent_start..agent_end).
A message queued with follow_up runs in a later cycle; pass a block to
follow_up to drain that cycle race-free (it subscribes before sending).
events is a prompt-less drain of the same stream for when you have
already subscribed before the cycle starts. Bidirectional extension UI
is not yet surfaced here.
Constant Summary collapse
- DEFAULT_EVENT_TIMEOUT =
Max time to wait for the next event before assuming the agent stalled.
300- DEFAULT_ACK_TIMEOUT =
Max time to wait for a command to be acknowledged.
30
Instance Attribute Summary collapse
-
#client ⇒ Object
readonly
Returns the value of attribute client.
Instance Method Summary collapse
-
#abort ⇒ Object
Abort the current agent run.
-
#available_models ⇒ Object
All configured models, as an array of Model hashes.
-
#clone_session ⇒ Object
Duplicate the current active branch into a new session at the current position.
- #close ⇒ Object
-
#compact(custom_instructions: nil) ⇒ Object
Manually compact the conversation context to reduce token usage.
-
#cycle_model ⇒ Object
Switch to the next configured model.
-
#entries(since: nil) ⇒ Object
Session entries in append order (excluding the session header).
-
#events(event_timeout: DEFAULT_EVENT_TIMEOUT, &block) ⇒ Object
Drain the event stream without submitting a new prompt.
-
#follow_up(message, images: nil, event_timeout: DEFAULT_EVENT_TIMEOUT, &block) ⇒ Object
Queue a follow-up message, delivered only after the agent stops.
-
#fork(entry_id) ⇒ Object
Fork a new branch from a previous user message (an entryId from
fork_messages). -
#fork_messages ⇒ Object
List user messages available for forking.
- #get_state ⇒ Object
-
#initialize(client) ⇒ Session
constructor
A new instance of Session.
-
#last_assistant_text ⇒ Object
Text of the last assistant message, or nil if there is none.
-
#messages ⇒ Object
Full conversation history, as an array of AgentMessage hashes.
-
#new_session(parent_session: nil) ⇒ Object
Start a fresh session in the same pi process.
-
#prompt(message, images: nil, event_timeout: DEFAULT_EVENT_TIMEOUT, &block) ⇒ Object
Submit a user prompt.
-
#run(message, images: nil, event_timeout: DEFAULT_EVENT_TIMEOUT) ⇒ Object
Single-shot helper mirroring pi's print mode: submit
message, drain the whole event stream, and return the final assistant text (nil if the agent produced none). -
#session_stats ⇒ Object
Token usage, cost, and context-window stats for the current session.
-
#set_model(provider, model_id = nil) ⇒ Object
Switch to a specific model.
- #set_session_name(name) ⇒ Object
-
#set_thinking(level) ⇒ Object
Set the reasoning level: "off", "minimal", "low", "medium", "high", or "xhigh" (xhigh is OpenAI codex-max only).
-
#steer(message, images: nil) ⇒ Object
Queue a steering message while the agent is running.
-
#switch_session(path) ⇒ Object
Load a different session file into this process.
-
#tree ⇒ Object
The session as a tree of entries.
Constructor Details
#initialize(client) ⇒ Session
Returns a new instance of Session.
30 31 32 |
# File 'lib/pi_agent/session.rb', line 30 def initialize(client) @client = client end |
Instance Attribute Details
#client ⇒ Object (readonly)
Returns the value of attribute client.
28 29 30 |
# File 'lib/pi_agent/session.rb', line 28 def client @client end |
Instance Method Details
#abort ⇒ Object
Abort the current agent run. Fire-and-forget.
108 109 110 111 |
# File 'lib/pi_agent/session.rb', line 108 def abort @client.notify("abort") self end |
#available_models ⇒ Object
All configured models, as an array of Model hashes.
130 131 132 |
# File 'lib/pi_agent/session.rb', line 130 def available_models request_data("get_available_models").fetch("models", []) end |
#clone_session ⇒ Object
Duplicate the current active branch into a new session at the
current position. Returns { "cancelled" => bool }. Maps to the
clone RPC command (named clone_session to avoid shadowing
Object#clone).
220 221 222 |
# File 'lib/pi_agent/session.rb', line 220 def clone_session request_data("clone") end |
#close ⇒ Object
229 230 231 |
# File 'lib/pi_agent/session.rb', line 229 def close @client.close end |
#compact(custom_instructions: nil) ⇒ Object
Manually compact the conversation context to reduce token usage. Returns the result hash ({ "summary" =>, "firstKeptEntryId" =>, "tokensBefore" => }).
158 159 160 161 162 |
# File 'lib/pi_agent/session.rb', line 158 def compact(custom_instructions: nil) params = {} params[:customInstructions] = custom_instructions if custom_instructions request_data("compact", params) end |
#cycle_model ⇒ Object
Switch to the next configured model. Returns the new { "model" =>, "thinkingLevel" =>, "isScoped" => } hash, or {} when only one model is available.
125 126 127 |
# File 'lib/pi_agent/session.rb', line 125 def cycle_model request_data("cycle_model") end |
#entries(since: nil) ⇒ Object
Session entries in append order (excluding the session header). Unlike
messages, this includes pre-compaction history and abandoned branches.
Entry ids are stable, so pass since: (the last entry id you have seen)
to get only entries strictly after it — a durable cursor across restarts.
Returns { "entries" => [...], "leafId" =>
196 197 198 199 200 |
# File 'lib/pi_agent/session.rb', line 196 def entries(since: nil) params = {} params[:since] = since if since request_data("get_entries", params) end |
#events(event_timeout: DEFAULT_EVENT_TIMEOUT, &block) ⇒ Object
Drain the event stream without submitting a new prompt. With a block, yields each Event until the cycle finishes (agent_end) and returns self; without a block, returns an Enumerator of Events.
The subscription is established lazily, when iteration begins — so any
cycle triggered before you call events may have already emitted
events that are then missed. To drain a follow_up cycle race-free,
pass a block to follow_up instead. Use events only when you
subscribe before the cycle starts (e.g. from a thread that begins
iterating, then trigger the cycle). With nothing queued, it blocks
for event_timeout (300s default).
60 61 62 63 64 65 66 67 |
# File 'lib/pi_agent/session.rb', line 60 def events(event_timeout: DEFAULT_EVENT_TIMEOUT, &block) stream = subscribed_stream(event_timeout: event_timeout) return stream unless block stream.each(&block) self end |
#follow_up(message, images: nil, event_timeout: DEFAULT_EVENT_TIMEOUT, &block) ⇒ Object
Queue a follow-up message, delivered only after the agent stops.
Without a block this is fire-and-forget: it queues the message and
returns self. With a block it drains the resulting agent cycle
race-free — the subscription is established before the message is
sent, so no events are missed — yielding each Event until agent_end
and returning self. Prefer the block form to consume a follow-up;
the standalone events drain only works if you subscribe first.
85 86 87 88 89 90 91 92 93 94 |
# File 'lib/pi_agent/session.rb', line 85 def follow_up(, images: nil, event_timeout: DEFAULT_EVENT_TIMEOUT, &block) params = (, images) unless block @client.request("follow_up", params).value!(timeout: DEFAULT_ACK_TIMEOUT) return self end event_stream("follow_up", params, event_timeout: event_timeout).each(&block) self end |
#fork(entry_id) ⇒ Object
Fork a new branch from a previous user message (an entryId from
fork_messages). Returns { "text" => cancelled is true if an extension vetoed it.
212 213 214 |
# File 'lib/pi_agent/session.rb', line 212 def fork(entry_id) request_data("fork", entryId: entry_id) end |
#fork_messages ⇒ Object
List user messages available for forking. Returns an array of { "entryId" => ..., "text" => ... } hashes.
187 188 189 |
# File 'lib/pi_agent/session.rb', line 187 def request_data("get_fork_messages").fetch("messages", []) end |
#get_state ⇒ Object
141 142 143 |
# File 'lib/pi_agent/session.rb', line 141 def get_state @client.request("get_state").value!(timeout: DEFAULT_ACK_TIMEOUT) end |
#last_assistant_text ⇒ Object
Text of the last assistant message, or nil if there is none.
151 152 153 |
# File 'lib/pi_agent/session.rb', line 151 def last_assistant_text request_data("get_last_assistant_text")["text"] end |
#messages ⇒ Object
Full conversation history, as an array of AgentMessage hashes.
146 147 148 |
# File 'lib/pi_agent/session.rb', line 146 def request_data("get_messages").fetch("messages", []) end |
#new_session(parent_session: nil) ⇒ Object
Start a fresh session in the same pi process. Pass parent_session:
(a session file path) to record provenance. Returns
{ "cancelled" => bool }; cancelled is true if an extension vetoed it.
167 168 169 170 171 |
# File 'lib/pi_agent/session.rb', line 167 def new_session(parent_session: nil) params = {} params[:parentSession] = parent_session if parent_session request_data("new_session", params) end |
#prompt(message, images: nil, event_timeout: DEFAULT_EVENT_TIMEOUT, &block) ⇒ Object
Submit a user prompt. With a block, yields each Event until the agent finishes (agent_end), then returns self. Without a block, returns an Enumerator of Events.
images accepts PiAgent::Image objects, file path strings, or
raw ImageContent hashes — in any mix.
40 41 42 43 44 45 46 47 |
# File 'lib/pi_agent/session.rb', line 40 def prompt(, images: nil, event_timeout: DEFAULT_EVENT_TIMEOUT, &block) stream = event_stream("prompt", (, images), event_timeout: event_timeout) return stream unless block stream.each(&block) self end |
#run(message, images: nil, event_timeout: DEFAULT_EVENT_TIMEOUT) ⇒ Object
Single-shot helper mirroring pi's print mode: submit message,
drain the whole event stream, and return the final assistant text
(nil if the agent produced none). Yields each Event to an optional
block while the stream drains.
100 101 102 103 104 105 |
# File 'lib/pi_agent/session.rb', line 100 def run(, images: nil, event_timeout: DEFAULT_EVENT_TIMEOUT) prompt(, images: images, event_timeout: event_timeout) do |event| yield event if block_given? end last_assistant_text end |
#session_stats ⇒ Object
Token usage, cost, and context-window stats for the current session. Returns the data hash, including "sessionId" and "sessionFile".
181 182 183 |
# File 'lib/pi_agent/session.rb', line 181 def session_stats request_data("get_session_stats") end |
#set_model(provider, model_id = nil) ⇒ Object
Switch to a specific model. Accepts either a single "provider/modelId" string or the two parts as separate arguments.
115 116 117 118 119 120 |
# File 'lib/pi_agent/session.rb', line 115 def set_model(provider, model_id = nil) provider, model_id = provider.split("/", 2) if model_id.nil? @client.request("set_model", provider: provider, modelId: model_id) .value!(timeout: DEFAULT_ACK_TIMEOUT) self end |
#set_session_name(name) ⇒ Object
224 225 226 227 |
# File 'lib/pi_agent/session.rb', line 224 def set_session_name(name) @client.request("set_session_name", name: name).value!(timeout: DEFAULT_ACK_TIMEOUT) self end |
#set_thinking(level) ⇒ Object
Set the reasoning level: "off", "minimal", "low", "medium", "high", or "xhigh" (xhigh is OpenAI codex-max only).
136 137 138 139 |
# File 'lib/pi_agent/session.rb', line 136 def set_thinking(level) @client.request("set_thinking_level", level: level).value!(timeout: DEFAULT_ACK_TIMEOUT) self end |
#steer(message, images: nil) ⇒ Object
Queue a steering message while the agent is running. Delivered after the current assistant turn finishes its tool calls, before the next LLM call. Fire-and-forget; raises on rejection.
72 73 74 75 |
# File 'lib/pi_agent/session.rb', line 72 def steer(, images: nil) @client.request("steer", (, images)).value!(timeout: DEFAULT_ACK_TIMEOUT) self end |
#switch_session(path) ⇒ Object
Load a different session file into this process. Returns { "cancelled" => bool }; cancelled is true if an extension vetoed it.
175 176 177 |
# File 'lib/pi_agent/session.rb', line 175 def switch_session(path) request_data("switch_session", sessionPath: path) end |
#tree ⇒ Object
The session as a tree of entries. Each node is
{ "entry" =>, "children" => [...], "label"? =>, "labelTimestamp"? => }.
Returns { "tree" => [...], "leafId" =>
205 206 207 |
# File 'lib/pi_agent/session.rb', line 205 def tree request_data("get_tree") end |