Class: Ask::AppServer::AgentAdapter
- Inherits:
-
Object
- Object
- Ask::AppServer::AgentAdapter
- Defined in:
- lib/ask/app_server/agent_adapter.rb
Overview
Wraps an Ask::Agent::Session and translates its events into canonical Ask::SessionProtocol events via EventTranslator.
Each wrapper is associated with one session and maintains an EventTranslator that clients poll or subscribe to. The adapter also owns the approval queue wiring (approval events surface as approval.required / approval.updated) and the interaction controls (approve/reject by id, plan approve/reject) that any client can call.
Instance Attribute Summary collapse
-
#created_at ⇒ Object
readonly
When the session was created.
-
#running ⇒ Object
readonly
Whether a turn is currently in progress.
-
#session ⇒ Object
readonly
The underlying ask-agent session.
-
#session_id ⇒ Object
readonly
The session ID (same as ask-agent session id).
-
#translator ⇒ Object
readonly
The event translator that accumulates protocol events.
Instance Method Summary collapse
-
#abort_turn! ⇒ Object
Request abort of the current turn.
-
#approve_all_interactions ⇒ Integer
Approve every pending approval interaction.
-
#approve_interaction(interaction_id) ⇒ Boolean
Approve a pending approval interaction by canonical id ("act_N").
-
#close! ⇒ Object
Close the session: delete its state and emit session.ended.
-
#drain_events ⇒ Object
Drain and return pending events.
-
#events_after(after_seq) ⇒ Object
Events after a given sequence number.
-
#idle? ⇒ Boolean
Whether this session is idle (no turn running).
-
#initialize(model:, tools: nil, system_prompt: nil, agent_dir: nil, approval: :off, require_approval: nil, **session_opts) ⇒ AgentAdapter
constructor
A new instance of AgentAdapter.
-
#last_seq ⇒ Object
Last sequence number.
-
#on_event(&block) ⇒ Object
Register an observer for every canonical event this session emits (translations plus approval/plan/session-lifecycle emissions).
-
#pending_events ⇒ Object
All events since last drain.
-
#pending_interactions ⇒ Object
Pending approval interactions, as canonical Interaction objects.
-
#plan_approve ⇒ Boolean
Approve the pending plan proposal.
-
#plan_reject ⇒ Boolean
Reject the pending plan proposal; the agent stays in plan mode.
-
#reject_all_interactions ⇒ Integer
Reject every pending approval interaction.
-
#reject_interaction(interaction_id) ⇒ Boolean
Reject a pending approval interaction by canonical id ("act_N").
-
#resume(session) ⇒ Object
Resume an existing session (re-attach event handler).
-
#send_message(content, expected_turn_id: nil) ⇒ Hash
Send a message and start processing (idle session) or inject it mid-run (running session).
-
#start_session ⇒ Object
Start a new ask-agent session.
-
#streaming_text ⇒ Object
The accumulated streaming text from the current/last turn.
-
#wait_for_turn(timeout: 600) ⇒ Object
Wait for the current turn to complete (with timeout).
Constructor Details
#initialize(model:, tools: nil, system_prompt: nil, agent_dir: nil, approval: :off, require_approval: nil, **session_opts) ⇒ AgentAdapter
Returns a new instance of AgentAdapter.
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
# File 'lib/ask/app_server/agent_adapter.rb', line 38 def initialize(model:, tools: nil, system_prompt: nil, agent_dir: nil, approval: :off, require_approval: nil, **session_opts) @model = model @system_prompt = system_prompt @tools = resolve_tools(tools) @session_opts = session_opts @approval = approval @require_approval = require_approval @agent_dir = agent_dir @session = nil @translator = nil @on_event_block = nil @session_id = nil @running = false @running_mutex = Mutex.new @run_thread = nil @abort_requested = false @created_at = Time.now @logger = Logger.new($stdout, level: ENV["DEBUG"] ? Logger::DEBUG : Logger::WARN) end |
Instance Attribute Details
#created_at ⇒ Object (readonly)
When the session was created.
27 28 29 |
# File 'lib/ask/app_server/agent_adapter.rb', line 27 def created_at @created_at end |
#running ⇒ Object (readonly)
Whether a turn is currently in progress.
24 25 26 |
# File 'lib/ask/app_server/agent_adapter.rb', line 24 def running @running end |
#session ⇒ Object (readonly)
The underlying ask-agent session.
15 16 17 |
# File 'lib/ask/app_server/agent_adapter.rb', line 15 def session @session end |
#session_id ⇒ Object (readonly)
The session ID (same as ask-agent session id).
21 22 23 |
# File 'lib/ask/app_server/agent_adapter.rb', line 21 def session_id @session_id end |
#translator ⇒ Object (readonly)
The event translator that accumulates protocol events.
18 19 20 |
# File 'lib/ask/app_server/agent_adapter.rb', line 18 def translator @translator end |
Instance Method Details
#abort_turn! ⇒ Object
Request abort of the current turn.
117 118 119 120 |
# File 'lib/ask/app_server/agent_adapter.rb', line 117 def abort_turn! @abort_requested = true @session&.abort if @session end |
#approve_all_interactions ⇒ Integer
Approve every pending approval interaction.
169 170 171 172 173 174 |
# File 'lib/ask/app_server/agent_adapter.rb', line 169 def approve_all_interactions queue = @session&.approval_queue return 0 unless queue queue.approve_all.size end |
#approve_interaction(interaction_id) ⇒ Boolean
Approve a pending approval interaction by canonical id ("act_N").
157 158 159 |
# File 'lib/ask/app_server/agent_adapter.rb', line 157 def approve_interaction(interaction_id) apply_interaction(interaction_id) { |queue, id| queue.approve(id) } end |
#close! ⇒ Object
Close the session: delete its state and emit session.ended.
208 209 210 211 212 |
# File 'lib/ask/app_server/agent_adapter.rb', line 208 def close! @session&.delete if @session.respond_to?(:delete) @translator&.session_ended(@session_id, reason: "closed") true end |
#drain_events ⇒ Object
Drain and return pending events.
225 226 227 |
# File 'lib/ask/app_server/agent_adapter.rb', line 225 def drain_events @translator&.drain_events || [] end |
#events_after(after_seq) ⇒ Object
Events after a given sequence number.
235 236 237 |
# File 'lib/ask/app_server/agent_adapter.rb', line 235 def events_after(after_seq) pending_events.select { |e| e.seq > after_seq } end |
#idle? ⇒ Boolean
Whether this session is idle (no turn running).
133 134 135 |
# File 'lib/ask/app_server/agent_adapter.rb', line 133 def idle? !@running end |
#last_seq ⇒ Object
Last sequence number.
230 231 232 |
# File 'lib/ask/app_server/agent_adapter.rb', line 230 def last_seq @translator&.last_seq || 0 end |
#on_event(&block) ⇒ Object
Register an observer for every canonical event this session emits (translations plus approval/plan/session-lifecycle emissions). May be called before start_session; the block is applied when the translator exists so the observer sees session.created.
85 86 87 88 89 90 91 |
# File 'lib/ask/app_server/agent_adapter.rb', line 85 def on_event(&block) if @translator @translator.on_event = block else @on_event_block = block end end |
#pending_events ⇒ Object
All events since last drain.
220 221 222 |
# File 'lib/ask/app_server/agent_adapter.rb', line 220 def pending_events @translator&.pending_events || [] end |
#pending_interactions ⇒ Object
Pending approval interactions, as canonical Interaction objects.
140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
# File 'lib/ask/app_server/agent_adapter.rb', line 140 def pending_interactions queue = @session&.approval_queue return [] unless queue queue.pending_actions.map do |action| payload = { "toolName" => action.tool_name.to_s } payload["args"] = action.args if action.args payload["message"] = action. if action. payload["autoApprovable"] = action.auto_approvable unless action.auto_approvable.nil? Ask::SessionProtocol::Interactions.interaction( id: "act_#{action.id}", kind: "approval", status: "pending", payload: payload ) end end |
#plan_approve ⇒ Boolean
Approve the pending plan proposal.
189 190 191 192 193 194 |
# File 'lib/ask/app_server/agent_adapter.rb', line 189 def plan_approve queue = @session&.plan_queue return false unless queue queue.approve_all.any? end |
#plan_reject ⇒ Boolean
Reject the pending plan proposal; the agent stays in plan mode.
198 199 200 201 202 203 |
# File 'lib/ask/app_server/agent_adapter.rb', line 198 def plan_reject queue = @session&.plan_queue return false unless queue queue.reject_all.any? end |
#reject_all_interactions ⇒ Integer
Reject every pending approval interaction.
178 179 180 181 182 183 |
# File 'lib/ask/app_server/agent_adapter.rb', line 178 def reject_all_interactions queue = @session&.approval_queue return 0 unless queue queue.reject_all.size end |
#reject_interaction(interaction_id) ⇒ Boolean
Reject a pending approval interaction by canonical id ("act_N").
163 164 165 |
# File 'lib/ask/app_server/agent_adapter.rb', line 163 def reject_interaction(interaction_id) apply_interaction(interaction_id) { |queue, id| queue.reject(id) } end |
#resume(session) ⇒ Object
Resume an existing session (re-attach event handler).
72 73 74 75 76 77 78 79 |
# File 'lib/ask/app_server/agent_adapter.rb', line 72 def resume(session) @session = session @session_id = session.id @translator = EventTranslator.new @translator.on_event = @on_event_block if @on_event_block @session.on_event { |event| handle_agent_event(event) } @session_id end |
#send_message(content, expected_turn_id: nil) ⇒ Hash
Send a message and start processing (idle session) or inject it mid-run (running session). Uses ask-agent's steer semantics:
steered — the message was added to the conversation immediately
queued — the session is running; the message is queued for the
next turn boundary (no abort)
stale — the caller's expected_turn_id no longer matches
104 105 106 107 108 109 110 111 112 113 114 |
# File 'lib/ask/app_server/agent_adapter.rb', line 104 def (content, expected_turn_id: nil) raise "Session not started" unless @session if @running result = @session.steer(content, expected_turn_id: expected_turn_id) { status: result[:status].to_s, turn_id: result[:turn_id] } else start_run(content) { status: "steered", turn_id: @session.turn_id } end end |
#start_session ⇒ Object
Start a new ask-agent session. Returns the session ID.
61 62 63 64 65 66 67 68 69 |
# File 'lib/ask/app_server/agent_adapter.rb', line 61 def start_session @translator = EventTranslator.new @translator.on_event = @on_event_block if @on_event_block @session = build_session @session_id = @session.id @session.on_event { |event| handle_agent_event(event) } @translator.session_created(@session_id) @session_id end |
#streaming_text ⇒ Object
The accumulated streaming text from the current/last turn.
215 216 217 |
# File 'lib/ask/app_server/agent_adapter.rb', line 215 def streaming_text @translator&.instance_variable_get(:@streaming_text).to_s end |
#wait_for_turn(timeout: 600) ⇒ Object
Wait for the current turn to complete (with timeout). Returns true if completed, false if timed out.
124 125 126 127 128 129 130 |
# File 'lib/ask/app_server/agent_adapter.rb', line 124 def wait_for_turn(timeout: 600) thread = @run_thread return true unless thread thread.join(timeout) !thread.alive? end |