Skip to content

UAP Specification

UAP-1.1: Unified Agent Protocol Specification

Section titled “UAP-1.1: Unified Agent Protocol Specification”

Version: 1.1.0 Status: Draft Built on: UPP-1.3 (Unified Provider Protocol) Authors: UAP Working Group


The Unified Agent Protocol (UAP) is a specification for building AI agents on top of the Unified Provider Protocol (UPP). This document defines the protocol semantics, data structures, and implementation requirements for building UAP-compliant agents, sessions, and execution strategies.

UAP extends UPP with agent-level abstractions including decoupled execution strategies (ReAct, Plan, Loop), functional state management, sub-agent composition, and middleware pipelines. UAP preserves complete type uniformity with UPP, using all types directly without abstraction or re-export.

Core Philosophy: UAP is a pipe, not a nanny. The protocol provides orchestration primitives; the developer provides the constraints.


  1. Introduction
  2. Design Principles
  3. Core Concepts
  4. Agent Interface
  5. Execution Strategies
  6. State Management
  7. Thread Trees
  8. Sub-Agent Protocol
  9. Middleware
  10. Strategy Hooks
  11. Streaming
  12. Serialization
  13. Data Types
  14. Conformance
  15. Security Considerations

AI agents require orchestration beyond simple LLM inference. UAP establishes a standard protocol that:

  • Provides agent abstractions built on UPP primitives
  • Decouples execution strategies from agent definitions
  • Uses functional state management with explicit data flow
  • Supports hierarchical agent composition (sub-agents)
  • Provides middleware for cross-cutting concerns
  • Places full control and responsibility with the developer

This specification covers:

  • The agent() function interface
  • Execution strategies (react(), plan(), loop())
  • Functional state management
  • Thread tree structures for branching conversations
  • Sub-agent communication
  • Middleware composition
  • Serialization and checkpointing

The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “NOT RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.

UAP builds on UPP and MUST use the following types directly:

  • llm, LLMInstance, LLMOptions
  • Thread, Turn, TokenUsage
  • Message, UserMessage, AssistantMessage, ToolResultMessage
  • Tool, ToolCall, ToolResult, ToolExecution, ToolUseStrategy
  • StreamResult, StreamEvent, StreamEventType
  • UPPError, ErrorCode
  • All provider factories

UAP MUST NOT re-export, wrap, or abstract these types.

TermDefinition
AgentAn AI entity with a model, execution strategy, tools, and optional sub-agents
AgentStateAn immutable snapshot of agent execution state
StepA single cycle of an execution strategy
Sub-AgentAn agent declared as a tool dependency for another agent
MiddlewareA composable function that wraps agent execution

UAP MUST NOT create abstractions around UPP types. All data flows through standard UPP types directly.

Agent execution is a pure transformation:

(Input, State) → (Turn, NewState)
  • AgentState is immutable—each operation returns a new state
  • State is explicitly passed and returned, never mutated internally
  • Developer controls what state persists between calls
  • Agents define WHAT (model, tools, system prompt)
  • Strategies define HOW (ReAct loop, plan-then-execute, simple loop)
  • Strategies are interchangeable without changing agent definition

UAP SHALL NOT impose artificial execution limits. Defaults:

ParameterDefault
maxIterationsInfinity
maxStepsInfinity
toolStrategy.maxIterationsInfinity
timeoutundefined

Developers who want limits MUST explicitly configure them.

UAP places full control and full responsibility with the developer:

  • The protocol provides orchestration primitives
  • The developer provides safety constraints
  • Runaway agents are a developer concern, not a protocol concern

Sub-agents are tools. Tools require explicit schemas. Therefore, sub-agents require explicit schemas. UAP MUST NOT auto-generate tool schemas from agent definitions.


┌─────────────────────────────────────────────────────────────┐
│ Application Code │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────┐
│ agent() │
└─────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Middleware Pipeline │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Execution Strategy │
│ react() | plan() | loop() │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ UPP (llm, Turn, Tool, etc.) │
└─────────────────────────────────────────────────────────────┘
// Agent SDK imports
import { agent, AgentState } from "agents"
import { react, plan, loop } from "agents/execution"
import { logging } from "agents/middleware"
// UPP imports (used directly, never re-exported)
import { llm, Thread, UserMessage, Tool } from "upp"
import anthropic from "upp/anthropic"
Input + State₀ → Agent.generate() → Turn + State₁
│
├── Middleware.before()
├── Strategy.execute()
│ ├── llm.generate()
│ └── Tool execution
└── Middleware.after()
EntityID TypeDescription
AgentUUIDv4Unique agent instance ID
AgentStateUUIDv4State snapshot ID (changes on mutation)
SessionUUIDv4Session identifier for checkpointing
CheckpointUUIDv4Unique checkpoint snapshot ID

agent(options: AgentOptions) → Agent

AgentOptions extends LLMOptions from UPP for full passthrough.

UAP-Specific Fields:

FieldTypeRequiredDescription
modelModelReferenceYesModel reference from UPP provider
executionExecutionStrategyNoExecution strategy (default: loop())
middlewareListNoOrdered middleware pipeline
strategyAgentStrategyNoAgent lifecycle hooks
checkpointsCheckpointStoreNoCheckpoint store for persistence
sessionIdStringNoSession identifier

LLM Passthrough Fields: params, config, tools, system, structure, toolStrategy

MethodDescription
generate(input, state)Execute agent, return Turn and new state
stream(input, state)Execute with streaming
ask(input, state)Multi-turn execution, history preserved
query(input)Stateless single-turn execution
generate(input: String | Message, state: AgentState) → Promise<GenerateResult>
GenerateResult = { turn: Turn, state: AgentState }
stream(input: String | Message, state: AgentState) → AgentStreamResult

AgentStreamResult is an async iterable of AgentStreamEvent with a result promise.

Multi-turn execution where history is preserved in state. Appends input to state history, calls generate(), appends response to returned state.

Stateless single-turn execution. Creates ephemeral state, executes, discards state.

query(input: String | Message) → Promise<Turn>

interface ExecutionStrategy {
name: String
execute(context: ExecutionContext) → Promise<ExecutionResult>
stream(context: ExecutionContext) → AgentStreamResult
}

The simplest strategy—equivalent to UPP’s tool loop behavior.

OptionTypeDefaultDescription
maxIterationsIntegerInfinityMaximum tool execution rounds

ReAct (Reason-Act-Observe) loop with explicit reasoning phases.

OptionTypeDefaultDescription
maxStepsIntegerInfinityMaximum reason-act-observe cycles
reasoningPromptString(default)Prompt for reasoning phase

Behavior:

  1. Reason: LLM outputs reasoning
  2. Act: LLM executes tool(s)
  3. Observe: Tool results formatted as observations
  4. Repeat until stop condition or no more actions

Plan-then-execute strategy with upfront planning phase.

OptionTypeDefaultDescription
maxPlanStepsIntegerInfinityMaximum steps in a plan
allowReplanBooleantrueAllow replanning on failure
planSchemaJSONSchema(default)Schema for plan structure

Implementations MUST allow custom execution strategies conforming to the ExecutionStrategy interface.


FieldTypeDescription
idStringState snapshot ID (UUIDv4)
messagesListConversation history
stepIntegerCurrent step number
metadataMapUser-defined metadata
reasoningListReasoning traces (for ReAct)
planList?Execution plan (for Plan strategy)
subagentTracesList?Sub-agent execution traces

All operations return new state instances:

MethodDescription
AgentState.initial()Create initial state
withMessage(message)Append single message
withMessages(messages)Append multiple messages
withContext(messages)Replace entire message history
withStep(step)Update step counter
withMetadata(key, value)Add metadata
withReasoning(reasoning)Add reasoning trace
withPlan(plan)Set execution plan
toJSON()Serialize state
AgentState.fromJSON(json)Deserialize state

withContext(messages) enables context window management via middleware. UAP does not provide token estimation—developers manage their model’s constraints.

Common Patterns:

  • Prune old tool outputs
  • Sliding window (keep N recent messages)
  • Summarize old context

Thread trees provide OPTIONAL tree-structured conversation management built on AgentState.

Property/MethodDescription
rootRoot node
currentCurrently active node
branch(fromId, name?)Create branch, returns node ID
checkout(nodeId)Switch active node
history()Get AgentState from root to current
toJSON()Serialize tree
FieldTypeDescription
idStringNode ID (UUIDv4)
parentIdString?Parent node ID
stateAgentStateState snapshot
nameString?Optional branch name
childrenListChild node IDs

Sub-agents are agents used as tools. They are regular UPP Tools with an implementation that calls another agent.

UAP MUST NOT auto-generate tool schemas from agents.

explorer = agent({
model: anthropic("claude-haiku-4-20250514"),
tools: [Glob, Grep, Read],
})
explorerTool: Tool = {
name: "explore_codebase",
description: "Explores codebase structure",
parameters: {
type: "object",
properties: {
query: { type: "string", description: "What to search for" },
},
required: ["query"],
},
run: async (params) => {
turn = await explorer.query(params.query)
return turn.response.text
},
}
FieldTypeDefaultDescription
sequentialBooleanfalseMust complete before other tools
dependsOnList[]Tool names that must complete first

When sub-agents execute via streaming, events SHOULD be propagated to the parent stream.

Event Types:

TypeDescription
subagent_startSub-agent execution began
subagent_eventForwarded event from sub-agent
subagent_endSub-agent execution completed

Sub-agent execution traces MUST be persisted in AgentState for checkpoint recovery.


interface Middleware {
name: String
before?(context) → Promise<MiddlewareContext | void>
after?(context, result) → Promise<GenerateResult>
onError?(context, error) → Promise<GenerateResult | void>
}

Middleware executes in order for before, reverse order for after:

// Execution order for [first, second, third]:
first.before() → second.before() → third.before()
→ Agent execution →
third.after() → second.after() → first.after()
OptionTypeDefaultDescription
levelString”info”Log level
loggerFunctionconsole.logCustom logger
includeMessagesBooleanfalseLog full content
includeTimingBooleantrueLog timing

HookDescription
stopCondition(state)Evaluate if execution should stop
onStepStart(step, state)Called when step begins
onReason(step, reasoning)Called during reasoning (ReAct)
onAct(step, actions)Called during action phase
onObserve(step, observations)Called during observation
onStepEnd(step, result)Called when step completes
onComplete(result)Called when execution completes
onError(error, state)Called on error

Since UAP defaults to infinite execution, stopCondition is the primary termination control:

strategy: {
stopCondition: (state) => {
if (state.metadata.taskComplete) return true
if (state.metadata.totalTokens > 50000) return true
return false
},
}

toolStrategy passes directly to the underlying llm() instance for real-time tool execution hooks. These complement UAP-level AgentStrategy hooks:

  • AgentStrategy hooks fire at step boundaries
  • ToolUseStrategy hooks fire immediately during tool execution

interface AgentStreamResult {
[Symbol.asyncIterator](): AsyncIterator<AgentStreamEvent>
result: Promise<GenerateResult>
abort(): void
}
interface AgentStreamEvent {
source: "uap" | "upp"
uap?: { type: UAPEventType, step: Integer, agentId: String, data: Map }
upp?: StreamEvent
}

UAPEventType Values:

step_start, step_end, reasoning, action, observation, subagent_start, subagent_event, subagent_end

The state returned by stream.result MUST include the complete execution history and be identical to what generate() would return.


FieldTypeRequiredDescription
versionStringYesUAP version
idStringYesState ID
messagesListYesUPP Message serialization
stepIntegerYesStep number
metadataMapYesUser metadata
reasoningListNoReasoning traces
planListNoExecution plan
subagentTracesListNoSub-agent traces
interface CheckpointStore {
save(sessionId, state) → Promise<void>
load(sessionId) → Promise<AgentStateJSON | null>
loadMetadata(sessionId) → Promise<CheckpointMetadata | null>
delete(sessionId) → Promise<void>
list() → Promise<List<String>>
}

When checkpoints is provided to agent():

  1. Checkpoints MUST be saved after each step_end event
  2. sessionId MUST be generated if not provided (UUIDv4)
  3. Failed saves SHOULD log errors but MUST NOT throw

Reference implementation for file-based checkpointing:

fileCheckpoints(options?: { dir?: String }) → CheckpointStore

Default directory: .checkpoints


llm, LLMInstance, LLMOptions, ProviderConfig, ModelReference, Message, UserMessage, AssistantMessage, ToolResultMessage, Turn, TokenUsage, Tool, ToolCall, ToolResult, ToolExecution, ToolUseStrategy, StreamResult, StreamEvent, StreamEventType, UPPError, ErrorCode, Thread, ThreadJSON

Core:

  • Agent, AgentOptions, GenerateResult
  • AgentState, AgentStateJSON

Execution:

  • ExecutionStrategy, ExecutionContext, ExecutionResult
  • LoopOptions, ReactOptions, PlanOptions, PlanStep

Streaming:

  • AgentStreamResult, AgentStreamEvent, UAPEventType

Middleware:

  • Middleware, MiddlewareContext

Hooks:

  • AgentStrategy

Checkpointing:

  • CheckpointStore, CheckpointMetadata

Thread Trees:

  • ThreadTree, ThreadNode

Sub-agents:

  • SubagentExecutionTrace, ToolExecutionTrace

LevelRequirements
Core (Required)agent(), generate(), stream(), ask(), query(), AgentState, loop()
Advanced Execution (Required)react(), plan(), custom strategies, infinite defaults
Thread Trees (Optional)ThreadTree, branching, checkout, history
Middleware (Required)Pipeline, logging(), custom middleware

MUST:

  • Use UPP types directly without wrapping
  • NOT re-export UPP types
  • Implement immutable AgentState
  • Default maxIterations/maxSteps/toolStrategy.maxIterations to Infinity
  • NOT auto-generate tool schemas from agents
  • Use UUIDv4 for all IDs
  • Fully serialize state via toJSON()
  • Extend LLMOptions for full UPP passthrough

MUST NOT:

  • Impose artificial execution limits by default
  • Mutate state internally
  • Auto-generate sub-agent tool schemas
  • Hide conversation history in implicit state

  • Runaway Agents: Developer must implement stopCondition or explicit limits
  • Resource Exhaustion: Developer must implement budget middleware
  • Cost Control: Developer must track token usage
  • Sub-agent tools execute with their run function’s permissions
  • Stop conditions do not automatically propagate to sub-agents
  • Serialized state may contain sensitive data
  • State SHOULD be encrypted at rest
  • Deserialization MUST validate structure

All UPP tool security considerations apply. Tools with sequential or dependsOn create execution ordering with potential security implications.


  • Reformatted specification for improved clarity and usability
  • Updated RFC 2119 compliance with BCP 14 and RFC 8174 references
  • Consolidated scattered MUST/SHOULD requirements into Section 14
  • Streamlined conformance requirements into tabular format
  • Simplified verbose pseudocode examples throughout
  • Consolidated state operations into single table (Section 6.2)
  • Removed duplicative Appendix A (Migration) content
  • Removed Appendix B (Complete Example) - moved to separate cookbook
  • Simplified Section 8 (Sub-Agent Protocol) while preserving semantics
  • Consolidated event types into tables rather than prose
  • Clarified MUST/SHOULD/MAY usage throughout per RFC 2119/8174
  • Reduced specification length while maintaining semantic completeness
  • Updated to reference UPP-1.3
  • Initial approved specification
  • agent() function with generate(), stream(), ask(), query() methods
  • Functional AgentState with immutable operations
  • Execution strategies: loop(), react(), plan()
  • Custom strategy support
  • Infinite defaults (maxIterations, maxSteps)
  • Explicit sub-agent tool declaration (no auto-generation)
  • Thread trees for branching conversations
  • Middleware pipeline with logging() middleware
  • Agent strategy hooks (stopCondition, onStep*, etc.)
  • ToolUseStrategy passthrough to UPP
  • Streaming with UAP and UPP event types
  • Sub-agent event propagation
  • Sub-agent trace persistence
  • Serialization format for AgentState
  • Checkpointing with CheckpointStore interface
  • fileCheckpoints() reference implementation
  • Complete type uniformity with UPP (no re-exports)