Skip to documentation
Documentation navigation

Documentation navigation

Documentation / guides

Start and message delegates

Start delegated Loops and exchange bounded messages.

developer

The runtime exposes delegation through one parent-scoped tool.DelegateController. The built internal AgentTools bundle is injected when a Loop has at least one declared delegate. Application code does not add that bundle by hand and the tool receives no session-wide controller.

// This controller is supplied to the runtime-built AgentTools bundle.
// A normal application invokes Session operations, not this internal binding.
result, err := delegateController.Execute(ctx, tool.DelegateRequest{
	Operation:       tool.DelegateStart,
	AgentType:       "worker",
	Name:            "indexer-1",
	Message:         "Index the files named in the parent request.",
	WaitForResponse: true,
})
if err != nil {
	return fmt.Errorf("start delegate: %w", err)
}
fmt.Println(result.AgentID, result.DeliveryStatus, result.ResponseStatus)

Operations and exact fields

OperationRequired request fieldsOptional request fieldsResult focus
DelegateStartAgentType, MessageName, AgentMode, WaitForResponse, TimeoutSeconds, ParentToolUseID, RuntimeNew AgentID, state, delivery, and optional response.
DelegateSendAgentID, MessageWaitForResponse, TimeoutSeconds, ParentToolUseIDExisting child delivery and optional response.
DelegateInterruptAgentIDNonePreviousState and State: AgentStateIdle.
DelegateStatusNone, or AgentID for one childNoneOne child or sorted direct-child snapshots; Truncated bounds a list.

The request type is:

// package tool
type DelegateRequest struct {
	Operation       tool.DelegateOperation
	AgentID         uuid.UUID
	AgentType       string
	Name            string
	AgentMode       string
	Message         string
	WaitForResponse bool
	TimeoutSeconds  *int
	ParentToolUseID string
	Runtime         *tool.DelegateRuntime
}

DelegateResult contains AgentID, Name, State, DeliveryStatus, Response, ResponseStatus, PreviousState, Agents, and Truncated. CorrelationID exists for session orchestration and is not model-facing.

Proof: public delegate request and result contracts and exact field tests.

Tool bundle and modes

The injected bundle is named AgentTools and contains exactly ListAgents, MessageAgent, StartAgent, and StopAgent. StartAgent requires agent_type and instructions; MessageAgent requires agent_id and message; ListAgents can filter by agent_id; StopAgent requires agent_id. When a runtime catalog is configured, start also exposes the catalog’s harness, source, model, effort, and mode selectors.

DelegationSyncOnly forces wait_for_response to true and refuses send, interrupt, status, or a non-waiting start with the typed DelegateActionUnavailable error. Managed style admits all four operations.

Proof: internal AgentTools schemas and controller style checks.

Start and message flow

Start is transactional: the child handle, backend, initial command, and first LoopStarted record are admitted before child events or gates can cross the barrier. Message subscribes before enqueue, so TurnStarted and TurnFoldedInto cannot race past the response observer.

%%{init: {"theme":"dark"}}%%
sequenceDiagram
    participant P as parent AgentTools
    participant C as scoped controller
    participant S as session runtime
    participant L as child Loop
    P->>C: DelegateStart or DelegateSend
    C->>C: style, declaration, mode, ownership checks
    C->>S: durable admission / intent
    S->>L: initial command or message
    L-->>S: turn terminal or queued delivery
    S-->>C: DelegateResult
    C-->>P: delivery and response status

Proof: start and send implementation and message delivery tests.

Delegation failures

An AgentTools preparation failure is rejected before any child starts, and the runner turns it into an error-marked tool result. An AgentTools execution failure happens after preparation, either because the delegate controller refused the operation before any child existed or because a started child reached a failure terminal, and the tool itself returns the error-marked result. Either way a failed AgentTools call reaches model history as an error-marked tool result, a content.ToolResultMessage whose IsError field is set.

An invalid runtime selector fails preparation with an error that names the rejected field and its value. An unavailable runtime selector fails preparation with an error that names the rejected selector and the value that matched no configured runtime.

A child failure cause survives foreground and background delegation, native and foreign child loops, and session restore, so every reader preserves the same cause. An ACP child is a foreign loop, so it takes the foreign path rather than a separate ACP route. Background delegation is the other shape to know: its hand-back arrives as a user-role message carrying the same cause, so it is not an error-marked tool result and carries no IsError field. A tombstoned child is the single documented gap: restore reports it as failed with no cause text.

Each failure detail is bounded and normalized to valid UTF-8 before it is stored. A preparation diagnostic is capped at 1 KiB and its detail at 512 bytes, while a child failure cause and an agent result are each capped at 256 KiB. The detail is preserved regardless of credential or model-facing classification, so no classifier filters it before the model sees it.

Source and proof

← back to documentation