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.
-
#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.
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).
202 203 204 |
# File 'lib/pi_agent/session.rb', line 202 def clone_session request_data("clone") end |
#close ⇒ Object
211 212 213 |
# File 'lib/pi_agent/session.rb', line 211 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 |
#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” => <forked-from text>, “cancelled” => bool }; `cancelled` is true if an extension vetoed it.
194 195 196 |
# File 'lib/pi_agent/session.rb', line 194 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
206 207 208 209 |
# File 'lib/pi_agent/session.rb', line 206 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 |