Skip to the content.

Application Runtime and Sessions

core/runtime owns a built deployment and exposes transport-neutral sessions. It does not implement HTTP, WebSocket, WebRTC, a CLI, or an application protocol.

Build

Create a registry, register every referenced resource factory, then build:

reg := resource.NewRegistry()
reg.MustRegister(event.NewFactory())
reg.MustRegister(graphresource.Factory())
reg.MustRegister(driverDeepSeek.Factory())

app, err := runtime.NewBuilder(reg).Build(ctx, doc)
if err != nil {
    return err
}
defer app.Close()

Runtime owns its deploy.Result, event router, and session manager. Close releases the deployment (resources and deployed agents), the session manager, and any dynamically registered agents.

Subscribing to runtime events

Runtime.Attach subscribes a sink to the runtime's event router without resolving the deployment document's event_bus resource:

detach, err := app.Attach(ctx, session.PatternPromptRequested(), sink)
if err != nil {
    return err
}
defer detach()

Attachments are torn down when the Runtime closes, and Attach fails with NotAvailable afterwards. External attachments inherit the bus default backpressure (DropNewest); pass event.WithAttachBackpressure to override per subscription. See prompt.md for the prompt lifecycle events, and the "Dynamic agent registry" section below for the runtime.agent.* lifecycle events published on dynamic registration/removal.

Accessing deployment resources

Runtime.Resource borrows a built resource value by its deployment name from the current generation, mirroring Agent for the resource view:

db, ok := app.Resource("db")
if !ok {
    return errors.New("deployment has no db resource")
}

Values are borrowed: the Runtime owns the deployment and closes resources when it closes, and a Reload retires the previous generation's values. Use this for access to deployment-built services such as a database pool. If the application must own a value's lifecycle or keep it across reloads, construct it outside the runtime and inject it through a resource factory registered in the registry instead.

Runtime config

runtime:
  event_bus: events
  checkpoint_store: checkpoints   # optional
  sessions:
    idle_timeout: 10m
    sink_buffer: 256
    delivery_concurrency: 8
    speculative_buffer_events: 1024
    speculative_buffer_bytes: 1048576
    max_sessions: 1024
    resume: false
  dynamic_catalog:
    tools:
      default: shared_tools
      researcher: research_tools

Rules:

Checkpoint stores

checkpoint_store names a resource implementing agent.CheckpointStore. The core backend is the workspace store:

resources:
  cps:
    kind: checkpoint.Store
    impl: workspace
    deps:
      workspace: ws
    settings:
      prefix: agent/checkpoints  # optional; default "agent/checkpoints"

Checkpoint files live under the workspace's prefix directory. sessions.resume: true requires a store, and reloads additionally require it to implement agent.CheckpointDeleter (the core workspace store does). Concrete alternative backends are app-registered outside core/ and own their settings schema.

Sessions

lease, err := app.Sessions().Open(ctx, session.Key{
    AgentID:   "assistant",
    ContextID: "conversation-1",
})
if err != nil {
    return err
}
defer lease.Close()

turn, err := lease.Session().Start(ctx, agent.Request{
    Message: message.NewTextMessage(message.RoleUser, "hello"),
}, session.SinkSpec{ID: "console", Sink: streamSink})
if err != nil {
    return err
}

result, err := turn.Wait(ctx)

SinkSpec controls streaming delivery, visibility, authority, and queue size. delivery_concurrency bounds in-flight sink callbacks.

Dynamic agent registry

Agents are normally declared in the deployment document and fixed for the life of the Runtime. Runtime also exposes a live registry so agents can be registered and removed at runtime without rebuilding:

instance, err := app.RegisterAgent(ctx, "qa", agent.Definition{
    Card:   agent.AgentCard{Name: "Ticket QA"},
    Engine: agent.EngineRef{Kind: "agent.Engine", Impl: "graph"},
}, runtime.WithToolAssembly("shared_tools")) // optional tool catalog
if err != nil {
    return err
}

lease, err := app.Sessions().GetOrCreate(ctx, session.Key{
    AgentID: "qa", ContextID: "user-7",
})
// ... Start / Wait, exactly like a deployed agent

if err := app.UnregisterAgent(ctx, "qa",
    runtime.WithRemoveTimeout(30*time.Second)); err != nil {
    return err
}

Semantics:

Manager.RemoveAgent / Manager.ReopenAgent are the session-manager level primitives behind removal and re-registration.

Reload

Runtime.Reload transactionally replaces the deployment document without rebuilding the runtime:

result, err := app.Reload(ctx, newDoc)
if err != nil {
    // the previous generation keeps serving
    return err
}
_ = result.GenerationID

Semantics:

Host decorators

runtime.Builder.WithHostFactory wraps the base host factory. The decorator must delegate any method it does not override.

runtime.Builder.WithResultHostFactory wraps the factory a second time with access to the fully assembled deployment, after WithHostFactory has run. This is the seam for deployment-built, run-scoped services: applications opt in and decide which services to expose on every turn host, and the runtime itself stays neutral to them. For example, exposing a delegation service:

builder.WithResultHostFactory(func(result *deploy.Result, factory session.HostFactory) (session.HostFactory, error) {
    return hostwrap.Wrap(factory, result) // delegation.Service onto every turn host
})

The decorator is retained across reloads and re-applied to each new generation's host factory with that generation's deployment.

See deploy.md and resource.md.