Class: PiAgent::Session

Inherits:
Object
  • Object
show all
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). Messages queued mid-flight via `follow_up`/`steer` run in subsequent cycles; consume those by calling `prompt`-less `events` or another `prompt`. 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

Instance Method Summary collapse

Constructor Details

#initialize(client) ⇒ Session

Returns a new instance of Session.



28
29
30
# File 'lib/pi_agent/session.rb', line 28

def initialize(client)
  @client = client
end

Instance Attribute Details

#clientObject (readonly)

Returns the value of attribute client.



26
27
28
# File 'lib/pi_agent/session.rb', line 26

def client
  @client
end

Instance Method Details

#abortObject

Abort the current agent run. Fire-and-forget.



73
74
75
76
# File 'lib/pi_agent/session.rb', line 73

def abort
  @client.notify("abort")
  self
end

#available_modelsObject

All configured models, as an array of Model hashes.



95
96
97
# File 'lib/pi_agent/session.rb', line 95

def available_models
  request_data("get_available_models").fetch("models", [])
end

#clone_sessionObject

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).



167
168
169
# File 'lib/pi_agent/session.rb', line 167

def clone_session
  request_data("clone")
end

#closeObject



176
177
178
# File 'lib/pi_agent/session.rb', line 176

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” => }).



123
124
125
126
127
# File 'lib/pi_agent/session.rb', line 123

def compact(custom_instructions: nil)
  params = {}
  params[:customInstructions] = custom_instructions if custom_instructions
  request_data("compact", params)
end

#cycle_modelObject

Switch to the next configured model. Returns the new { “model” =>, “thinkingLevel” =>, “isScoped” => } hash, or {} when only one model is available.



90
91
92
# File 'lib/pi_agent/session.rb', line 90

def cycle_model
  request_data("cycle_model")
end

#follow_up(message, images: nil) ⇒ Object

Queue a follow-up message, delivered only after the agent stops.



56
57
58
59
# File 'lib/pi_agent/session.rb', line 56

def follow_up(message, images: nil)
  @client.request("follow_up", message_params(message, images)).value!(timeout: DEFAULT_ACK_TIMEOUT)
  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.



159
160
161
# File 'lib/pi_agent/session.rb', line 159

def fork(entry_id)
  request_data("fork", entryId: entry_id)
end

#fork_messagesObject

List user messages available for forking. Returns an array of { “entryId” => …, “text” => … } hashes.



152
153
154
# File 'lib/pi_agent/session.rb', line 152

def fork_messages
  request_data("get_fork_messages").fetch("messages", [])
end

#get_stateObject



106
107
108
# File 'lib/pi_agent/session.rb', line 106

def get_state
  @client.request("get_state").value!(timeout: DEFAULT_ACK_TIMEOUT)
end

#last_assistant_textObject

Text of the last assistant message, or nil if there is none.



116
117
118
# File 'lib/pi_agent/session.rb', line 116

def last_assistant_text
  request_data("get_last_assistant_text")["text"]
end

#messagesObject

Full conversation history, as an array of AgentMessage hashes.



111
112
113
# File 'lib/pi_agent/session.rb', line 111

def messages
  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.



132
133
134
135
136
# File 'lib/pi_agent/session.rb', line 132

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.



38
39
40
41
42
43
44
45
# File 'lib/pi_agent/session.rb', line 38

def prompt(message, images: nil, event_timeout: DEFAULT_EVENT_TIMEOUT, &block)
  stream = event_stream("prompt", message_params(message, 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.



65
66
67
68
69
70
# File 'lib/pi_agent/session.rb', line 65

def run(message, images: nil, event_timeout: DEFAULT_EVENT_TIMEOUT)
  prompt(message, images: images, event_timeout: event_timeout) do |event|
    yield event if block_given?
  end
  last_assistant_text
end

#session_statsObject

Token usage, cost, and context-window stats for the current session. Returns the data hash, including “sessionId” and “sessionFile”.



146
147
148
# File 'lib/pi_agent/session.rb', line 146

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.



80
81
82
83
84
85
# File 'lib/pi_agent/session.rb', line 80

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



171
172
173
174
# File 'lib/pi_agent/session.rb', line 171

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).



101
102
103
104
# File 'lib/pi_agent/session.rb', line 101

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.



50
51
52
53
# File 'lib/pi_agent/session.rb', line 50

def steer(message, images: nil)
  @client.request("steer", message_params(message, 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.



140
141
142
# File 'lib/pi_agent/session.rb', line 140

def switch_session(path)
  request_data("switch_session", sessionPath: path)
end