class LittleGhost::Runtime
Owns the shared services that assemblies reuse across many Runs.
Most applications do not construct this class. Configure LittleGhost once and call a named Agent or Assembly; the first standalone call lazily builds LittleGhost.runtime, and later calls reuse it automatically. Each call still receives a fresh Run, bound participants, Tools, workspace, and sandbox.
Construct Runtime directly when one process intentionally hosts an isolated LittleGhost setup:
configuration = LittleGhost::Configuration.new( root: Dir.pwd, providers: { openrouter: {adapter: :openrouter, api_key: ENV.fetch("OPENROUTER_API_KEY")} }, models: {customer_support: {target: "openrouter:openai/gpt-5.6-luna"}}, default_model: :customer_support, service_name: "support-api" ) runtime = LittleGhost::Runtime.new(configuration: configuration) CustomerSupportAgent.new(runtime: runtime) .ask("Where is order 481?") .response
Explicit construction snapshots the supplied Configuration but does not replace LittleGhost’s shared default Runtime.
A Runtime may build independent Runs concurrently. Each Run gets fresh participants and Tools. By default, it also gets a Runtime-created Workspace and Sandbox that the Run owns. Instances supplied by the application remain caller-owned.
Advanced construction and ownership
Normal construction reads the application’s configured definitions and builds shared model resolution, persistence, hooks, and resource factories. The settings form and build are lower-level extension points for deriving another Runtime from an existing configuration snapshot.
build_run creates a workspace and sandbox when needed. Once the Run owns them, it closes them; if construction stops halfway through, Runtime closes the partial resources. Startup failures are reported to instrumentation and then raised. Session actor resolution must use authenticated application identity. The default Sandboxes::Unrestricted uses host permissions and is not a security boundary for untrusted work.
Shared stores, resolvers, hooks, subscribers, providers, and resource factories may receive concurrent calls. Calls can overlap on different threads, or fibers can take turns entering the same object on one thread. Extensions must protect shared mutable state without relying on thread identity. One SessionStore instance serializes calls for the same Session. A store must provide its own coordination across processes.
See Running in Production for choosing a concurrency backend and protecting shared extensions.
Runtime has no shutdown operation. Runs close resources created for their request. The application shuts down shared services and process-wide Instrumentation subscribers with the rest of the process.
Attributes
Default code-mode declaration for enabled agents.
Configuration object used to construct this Runtime.
Loader used for conventional application definitions.
Resolver that turns model roles and targets into executable Models.
Ordered directories searched for prompt templates.
Canonical application root.
Runtime hooks called around request and session preparation.
Configured Sandbox provider symbol, callable, or declaration.
Shared store used to open per-Run Sessions.
Settings snapshot used by new Runs.
Ordered directories searched for skill definitions.
Root used for skill-owned resources, when configured.
Configured Workspace provider symbol, callable, or declaration.
Public Class Methods
# File lib/little_ghost/runtime.rb, line 98 def initialize(configuration:, settings: nil) @startup_started_at = monotonic_time @startup_phase = "configuration" @startup_reported = false begin raise ArgumentError, "configuration must be a LittleGhost::Configuration" unless configuration.is_a?(Configuration) @configuration = configuration if settings @settings = settings else bootstrap_root = canonical_application_root(configuration.root) configuration.load_file!(root: bootstrap_root) @settings = configuration.settings(root: bootstrap_root) end @task_runner = Support::TaskRunner.new( backend: @settings.fetch(:concurrency_backend, :auto) ) report_startup(status: "starting") @startup_reported = true @root = canonical_application_root(@settings.fetch(:root)) @skill_resource_root = @settings[:skill_resource_root] @workspace_declaration = @settings.fetch(:workspace) @sandbox_declaration = @settings.fetch(:sandbox) @code_mode_configuration = @settings[:code_mode] @runtime_hooks = build_runtime_hooks(@settings[:runtime_hooks]) @startup_phase = "instrumentation" subscribe_instrumentation(@settings[:instrumentation_subscribers]) emit_startup(:runtime_start) @startup_phase = "loader" @loader = @settings[:loader] || Support::Loader.new(root: @root) loader.setup loader.eager_load @startup_phase = "model_resolver" @invocation_class = @settings[:invocation] || Invocation @model_resolver = @settings.fetch(:model_resolver) @default_model = @settings.fetch(:default_model, "default").to_s @model_operations = ModelOperations.new(model_resolver:) @startup_phase = "session_store" @session_store = build_session_store(@settings[:session_store]) @session_actor = @settings[:session_actor] @startup_phase = "prompts" @prompt_paths = build_lookup_paths(:prompt_paths) @skill_paths = build_lookup_paths(:skill_paths) @startup_phase = "agent_factory" @agent_factory = AgentFactory.new( runtime: self, prompt_paths: @prompt_paths, resolve_agent: method(:resolve_agent_class) ) @startup_phase = "complete" emit_startup(:runtime_stop, outcome: "ready") report_startup(status: "ready") rescue => error unless @startup_reported report_startup(status: "starting") @startup_reported = true end emit_startup(:runtime_stop, outcome: "failed", error:) Instrumentation.flush report_startup(status: "failed", error:) raise end end
Starts a runtime from configuration or an existing settings snapshot.
Public Instance Methods
Source
# File lib/little_ghost/runtime.rb, line 172 def build(**overrides) values = @settings.merge(overrides) values[:root] = canonical_application_root(values.fetch(:root)) values[:loader] = loader unless overrides.key?(:loader) || overrides.key?(:root) self.class.new( configuration:, settings: values ) end
Creates a sibling runtime with explicit setting overrides.
# File lib/little_ghost/runtime.rb, line 215 def build_run( payload, 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 agent_class ||= entrypoint_class if entrypoint_class <= Agent owned_resources = [] invocation = parse(payload) workspace ||= build_workspace(invocation:).tap { |resource| owned_resources << resource } sandbox ||= build_sandbox(workspace:, invocation:).tap { |resource| owned_resources << resource } run = Run.new( invocation:, runtime: self, agent_class:, entrypoint_class:, execution_class:, cancellation_token:, workspace:, sandbox:, include_agent_events_by_default: ) owned_resources.each { |resource| run.register(resource) } prepare_run(run) rescue if run run.close else close_resources(owned_resources) end raise end
Creates a Run that owns any workspace and sandbox built for the request.
include_agent_events_by_default is trusted stream policy for the Run returned by this build. It applies only when the Invocation omits include_agent_events and must not be forwarded to auxiliary Runs built while preparing the request.
# File lib/little_ghost/runtime.rb, line 277 def build_sandbox(workspace:, invocation: nil) declaration = sandbox_declaration return Sandboxes::Unrestricted.new(workspace:) unless declaration provider, options = component_provider(declaration) provider = Sandbox.resolve_provider(provider) if provider.is_a?(Symbol) policy_options = options.slice(*Sandbox::Policy::COMMON_KEYS) policy_options.each_key { |key| options.delete(key) } policy_value = options.delete(:policy) if policy_value || !policy_options.empty? options[:policy] = Sandbox::Policy.coerce(policy_value, **policy_options) end build_component(provider, options, runtime: self, invocation:, workspace:) end
Instantiates the configured sandbox around workspace, or an unrestricted sandbox by default.
# File lib/little_ghost/runtime.rb, line 258 def build_workspace(invocation: nil) declaration = workspace_declaration unless declaration paths = artifacts_enabled? ? {artifacts: "artifacts"} : {} return Workspace.new(root: root, paths:) end provider, options = component_provider(declaration) provider = Workspace.resolve_provider(provider) if provider.is_a?(Symbol) options[:root] = resolve_component_path(options[:root]) if options.key?(:root) options[:root] ||= root.to_s if provider == Workspace if artifacts_enabled? && provider == Workspace options[:paths] = {artifacts: "artifacts"}.merge(options.fetch(:paths, {})) end build_component(provider, options, runtime: self, invocation:) end
Instantiates the configured workspace, or a root-scoped Workspace by default.
# File lib/little_ghost/runtime.rb, line 205 def embed(**arguments) @model_operations.embed(**arguments) end
Embeds text through this Runtime’s model resolver.
Returns an Embeddings::Response without creating a Run or invoking runtime hooks. See LittleGhost.embed for the operation contract.
# File lib/little_ghost/runtime.rb, line 194 def generate(**arguments) @model_operations.generate(**arguments) end
Generates one response through this Runtime’s model resolver.
Returns a RunResult without creating a Run or invoking runtime hooks. See LittleGhost.generate for the operation contract.
Source
# File lib/little_ghost/runtime.rb, line 183 def parse(payload) payload.is_a?(@invocation_class) ? payload : @invocation_class.new(payload) end
Coerces an application payload into the configured Invocation class.