Dagger.Agent (dagger v1.0.0-beta.15)

Copy Markdown View Source

EXPERIMENTAL: Agent APIs are likely to change.

A conversation loop running as an addressable, long-lived entity within the session. The conversation itself remains observable at any time as an immutable LLM value.

Summary

Functions

Why the loop failed, for a FAILED agent; empty otherwise.

The opaque runtime handle minted by the spawn that created this agent.

A unique identifier for this Agent.

Look up a previously sent message by its ref.

Display label for the agent; carries no identity.

Subscribe another agent to this agent's lifecycle: each transition into one of the given states enqueues an event message to the subscriber — steering its open turn, or waking it if idle, like any other message.

Stop draining the mailbox once the in-flight step completes, or immediately with interrupt.

Replace this instance's committed conversation with the given one, keeping the entry: identity, mailbox, and lifecycle state are untouched. A paused suspended turn is abandoned and its consumed messages are resolved before replacement.

Resume draining the mailbox: a suspended turn continues from the last committed step, and queued messages drain.

Enqueue a message, on the record: it is consumed at a step boundary, appends to the agent's history, and steers the running turn or opens a new one.

The conversation as of the last committed step: immutable, branchable, persistable.

Computed lifecycle state; never stored.

Release the agent's runtime. The tombstone (state, snapshot) stays readable for the rest of the session.

Block until the agent settles: IDLE, FAILED, or STOPPED. Read which from state afterwards.

Types

t()

@type t() :: %Dagger.Agent{client: term(), query_builder: term()}

Functions

error(agent)

@spec error(t()) :: {:ok, String.t()} | {:error, term()}

Why the loop failed, for a FAILED agent; empty otherwise.

The snapshot holds the completed prefix — send or resume retries from it.

Experimental

"Agent APIs are likely to change."

handle(agent)

@spec handle(t()) :: {:ok, String.t()} | {:error, term()}

The opaque runtime handle minted by the spawn that created this agent.

It is the same value the agent's loop span publishes as dagger.io/agent.id, so a client can correlate the agent with what it discovers in the trace. Two spawns of an identical composition have different handles; a display name is shared freely.

Experimental

"Agent APIs are likely to change."

id(agent)

@spec id(t()) :: {:ok, String.t()} | {:error, term()}

A unique identifier for this Agent.

message(agent, ref)

@spec message(t(), String.t()) :: Dagger.AgentMessage.t()

Look up a previously sent message by its ref.

This is the lookup send pins its result's identity through: the returned handle's ID is an honest, replayable chain, addressable from any request in the session (the cancel-and-request-again contract).

Fails if the agent has no runtime entry in this session, or no record of the given ref.

Experimental

"Agent APIs are likely to change."

name(agent)

@spec name(t()) :: {:ok, String.t()} | {:error, term()}

Display label for the agent; carries no identity.

Experimental

"Agent APIs are likely to change."

notify(agent, subscriber, optional_args \\ [])

@spec notify(t(), t(), [{:on, [Dagger.AgentState.t()]}]) ::
  {:ok, t()} | {:error, term()}

Subscribe another agent to this agent's lifecycle: each transition into one of the given states enqueues an event message to the subscriber — steering its open turn, or waking it if idle, like any other message.

This is how a supervisor hears every completion and failure without polling or blocking: subscribe at spawn time, keep working, and events arrive as attributed messages.

Events never relaunch a stopped subscriber, and an already-reached state fires immediately at subscribe time, so a fast agent settling before the subscription lands is not missed.

A restored agent that nothing has sent to, started, or resumed yet is the exception: its state was reached in the session it was restored from, so subscribing to it announces nothing until it next transitions. This is how a restore reinstalls recorded subscriptions without waking their subscribers.

Idempotent per subscriber; re-subscribing replaces the state set.

Experimental

"Agent APIs are likely to change."

pause(agent, optional_args \\ [])

@spec pause(t(), [{:interrupt, boolean() | nil}]) :: {:ok, t()} | {:error, term()}

Stop draining the mailbox once the in-flight step completes, or immediately with interrupt.

Pause takes priority over pending work: a mid-turn pause suspends the turn, which resume continues. Messages sent while paused enqueue with QUEUED delivery until a resume.

Pausing a never-started agent leaves it paused for its eventual resume; pausing a failed agent is allowed (resume decides the retry); pausing a stopped agent fails.

Experimental

"Agent APIs are likely to change."

reseed(agent, conversation)

@spec reseed(t(), Dagger.LLM.t()) :: {:ok, t()} | {:error, term()}

Replace this instance's committed conversation with the given one, keeping the entry: identity, mailbox, and lifecycle state are untouched. A paused suspended turn is abandoned and its consumed messages are resolved before replacement.

This is the continuity verb. Compaction, a workspace rebind, a model change, or rewinding an interrupted prompt produce a new conversation value for the SAME agent; reseed swaps it in place, where a stop-and-respawn would mint a successor instance and split the agent across two roster entries. It is the client-facing form of what a continuation tool already does mid-turn: the agent adopts a new conversation without changing who it is.

The next turn continues from the reseeded conversation, and queued messages drain onto it. A FAILED agent keeps its error — resume retries from the new conversation.

Fails if the instance has no runtime entry in this session (only a spawned or re-hydrated instance holds a conversation to replace), if a step is in flight, or if the agent is stopped.

Experimental

"Agent APIs are likely to change."

resume(agent)

@spec resume(t()) :: {:ok, t()} | {:error, term()}

Resume draining the mailbox: a suspended turn continues from the last committed step, and queued messages drain.

Resuming a never-started agent starts its evaluation loop, detached from the calling request: it steps the conversation while input is pending, then idles awaiting further lifecycle operations. Resuming a FAILED agent retries its pending step. Resuming a STOPPED agent relaunches the same instance from its last committed snapshot.

No-op on a running or idle agent.

Experimental

"Agent APIs are likely to change."

send(agent, message, optional_args \\ [])

@spec send(t(), String.t(),
  reply_to: String.t() | nil,
  content: [Dagger.LLMContentBlockInput.t()]
) ::
  {:ok, Dagger.AgentMessage.t()} | {:error, term()}

Enqueue a message, on the record: it is consumed at a step boundary, appends to the agent's history, and steers the running turn or opens a new one.

Never blocks, never drops; concurrent sends queue in order.

The returned message is pinned through the message lookup field, so its handle is re-addressable from any request in the session: cancel a response request and request it again freely.

Sending to a never-started agent starts it (signal-with-start). Sending to a stopped agent restarts the same instance from its last committed snapshot. Sending to a paused or failed agent enqueues with QUEUED delivery, to be drained by a resume.

Experimental

"Agent APIs are likely to change."

snapshot(agent)

@spec snapshot(t()) :: Dagger.LLM.t()

The conversation as of the last committed step: immutable, branchable, persistable.

The seed conversation if the agent never stepped.

Branching from it does not affect the agent.

Experimental

"Agent APIs are likely to change."

state(agent)

@spec state(t()) :: {:ok, Dagger.AgentState.t()} | {:error, term()}

Computed lifecycle state; never stored.

An agent that was never started reports IDLE: its mailbox is empty and no turn is open.

Experimental

"Agent APIs are likely to change."

stop(agent, optional_args \\ [])

@spec stop(t(), [{:kill, boolean() | nil}]) :: {:ok, t()} | {:error, term()}

Release the agent's runtime. The tombstone (state, snapshot) stays readable for the rest of the session.

Experimental

"Agent APIs are likely to change."

wait(agent)

@spec wait(t()) :: {:ok, t()} | {:error, term()}

Block until the agent settles: IDLE, FAILED, or STOPPED. Read which from state afterwards.

Unlike waiting for one exact state, this cannot hang merely because the agent settled in a different outcome.

Experimental

"Agent APIs are likely to change."