class LittleGhost::Run
Observe one top-level assembly execution from start to finish. A run records its response, outcome, usage, error, and owned resources.
run = CustomerSupportAgent.ask("Why is transfer 481 pending?") run.completed? # => true run.outcome # => "completed" run.response # => "Transfer 481 is waiting for the receiving bank."
ask returns the Run after work finishes. stream_ask yields StreamEvent objects as work happens, then returns the same finished Run. A Run executes only once.
stream = CustomerSupportAgent.stream_ask("Where is transfer 481?") run = stream.each do |event| publish(event) if event.type == :text_delta end run.completed? # => true run.response
Outcomes
Completion, failure, deadline, and cancellation become the completed, failed, partial, and cancelled outcomes. Ordinary execution failures are available through error and the terminal stream event. Failures while closing resources, delivering events, or reporting instrumentation may still raise because LittleGhost cannot report a reliable ending.
Tool validation and ToolError failures return safe Tool results to the model, which may recover and complete the Run. Input, configuration, or resource construction can raise before a Run exists. Once execution begins, terminal events are run_stop, run_error, run_partial, and run_cancel.
Owned resources
The Run opens its workspace, sandbox, Session, and Assembly entrypoint, then closes registered resources in reverse order. register adds application resources to that cleanup sequence. Interjection is available only while one Agent entrypoint is active.
Nested Agent events
A composite Assembly stream observes every Agent that shares the Run. Each :agent_stream event carries an AgentStreamSource in data[:source] and a copied, frozen Agent StreamEvent in data[:event]. An inner :invocation_start also includes the copied, frozen Message sent to that Agent in data[:input]. Event consumers cannot change the running work.
Parallel Agents may interleave, but the Run invokes the stream consumer serially. Contextual events expose data from every participating Agent, so applications should enable include_agent_events only for destinations that may see every participant’s data.
Attributes
Token that cooperatively stops this Run and its children.
Exception that caused a failed, partial, or cancelled outcome.
Normalized request carried by this Run.
Unique identifier for this top-level operation.
Terminal String: completed, failed, partial, or cancelled.
Caller-facing final text, or the partial text preserved at a deadline.
Request-scoped sandbox owned or supplied by the Run.
Session opened for this invocation, when persistence is configured.
Request-scoped workspace owned or supplied by the Run.
Public Class Methods
# File lib/little_ghost/run.rb, line 92 def initialize(invocation:, runtime:, agent_class: nil, assembly_class: nil, entrypoint_class: nil, execution_class: nil, cancellation_token: Support::CancellationToken.new, workspace: nil, sandbox: nil, include_agent_events_by_default: false) entrypoint_class ||= assembly_class || agent_class raise ArgumentError, "entrypoint_class is required" unless entrypoint_class execution_class ||= assembly_class || entrypoint_class @runtime = runtime @agent_class = agent_class @entrypoint_class = entrypoint_class @execution_class = execution_class @invocation = invocation @cancellation_token = cancellation_token @workspace = workspace @sandbox = sandbox @operation_id = SecureRandom.uuid @resources = [] @shared_resources = {} @shared_resource_condition = ConditionVariable.new @closed = false @started = false @mutex = Mutex.new @subagent_instrumentation_mutex = Mutex.new @subagent_instrumentation = {} @assembly_step_instrumentation_mutex = Mutex.new @assembly_step_instrumentation = {} @exclusive_tools_mutex = Mutex.new @once_mutex = Mutex.new @once_keys = {} @interjection_mutex = Mutex.new @interjection_condition = ConditionVariable.new @interjection_state = :not_started @active_interjections = 0 @entrypoint = nil @usage = Usage.new include_agent_events = invocation[:include_agent_events] unless include_agent_events.nil? || include_agent_events == true || include_agent_events == false raise InvocationError, "include_agent_events must be true or false" end @include_agent_events = include_agent_events.nil? ? include_agent_events_by_default : include_agent_events end
Creates a dormant run for invocation.
Public Instance Methods
Source
# File lib/little_ghost/run.rb, line 136 def call each { |_event| } self end
Consumes the event stream and returns self.
Source
# File lib/little_ghost/run.rb, line 170 def cancelled? = outcome == "cancelled"
True when cancellation stopped the run without a response.
Source
# File lib/little_ghost/run.rb, line 322 def close callbacks = @mutex.synchronize do return if @closed @closed = true @shared_resources.clear @shared_resource_condition.broadcast @resources.reverse end errors = [] callbacks.each do |callback| callback.call rescue => error errors << error end cleanup_error = errors.find { |caught| caught.is_a?(CleanupError) } || errors.first begin finish_remaining_subagent_instrumentation( outcome: cleanup_error ? :error : :cancelled, error_type: cleanup_error&.class&.name ) finish_remaining_assembly_step_instrumentation( outcome: cleanup_error ? :error : :cancelled, error_type: cleanup_error&.class&.name ) rescue => error errors << error end error = errors.find { |caught| caught.is_a?(CleanupError) } || errors.first raise error if error end
Closes registered resources in reverse order.
The operation is idempotent. It attempts every closer and then raises the first LittleGhost::CleanupError, or otherwise the first cleanup exception.
Source
# File lib/little_ghost/run.rb, line 161 def completed? = outcome == "completed"
True after successful completion.
# File lib/little_ghost/run.rb, line 226 def context(state: {}, metadata: {}) RunContext.new( state:, cancellation_token:, deadline: invocation.deadline_at, metadata: ) end
Creates a RunContext with this run’s cancellation token and deadline.
Source
# File lib/little_ghost/run.rb, line 144 def each return enum_for(__method__) unless block_given? begin_execution! dispatcher = Support::SerializedDispatcher.new do |event| yield_event(event) { |value| yield value } end @emitter = dispatcher.method(:call) Instrumentation.with_context(correlation_attributes.except(:operation_id)) do execute { |event| @emitter.call(event) } end self ensure @emitter = nil end
Yields events and returns self after the terminal event.
Without a block, returns an Enumerator. A second execution raises Error.
Source
# File lib/little_ghost/run.rb, line 164 def failed? = outcome == "failed"
True after execution or cleanup failed.
Source
# File lib/little_ghost/run.rb, line 174 def include_agent_events? = @include_agent_events
# File lib/little_ghost/run.rb, line 180 def interject( message, interjection_id: nil, batch_key: nil, metadata: {}, cancellation_token: Support::CancellationToken.new, deadline: nil ) interject_with do [ message, { interjection_id:, batch_key:, metadata:, cancellation_token:, deadline: } ] end end
Adds an interjection to the active entrypoint and waits for its response.
Raises LittleGhost::AgentInterjectionError before the entrypoint is ready or after it finishes.
Source
# File lib/little_ghost/run.rb, line 304 def once(key) @once_mutex.synchronize do return if @once_keys.key?(key) value = yield @once_keys[key] = true value end end
Performs the block at most once successfully for key during this run.
Concurrent callers are serialized. The caller that performs the block receives its value; later callers receive nil. If the block raises, the key is not recorded and a later call may retry it.
Source
# File lib/little_ghost/run.rb, line 167 def partial? = outcome == "partial"
True when the deadline preserved a partial response.
# File lib/little_ghost/run.rb, line 246 def register(resource = nil, &closer) callback = closer || close_callback(resource) @mutex.synchronize do raise Error, "run is already closed" if @closed @resources << callback end resource end
Adds a resource or closer to reverse-order cleanup and returns the resource.
A resource must respond to close unless a block supplies the cleanup operation. Registering after the run has closed raises Error.