Skip to content

agent

Agent Module

This module provides the protocol-neutral runtime used by PromptFleet agents.

  • config: Agent configuration and communication patterns
  • message: Message types, contexts, and classification
  • skill: Skill registration, execution, and metadata management
  • core: Main Agent struct and core functionality
  • Single Responsibility: Each module handles one architectural concern
  • Maintainability: Smaller, focused modules (200-400 lines each)
  • Testability: Easier to test individual components in isolation
  • Clean Dependencies: Follows dependency inversion principles
  • Extensibility: Easy to add new features without affecting other areas
type NotificationHandler = Arc<dyn Fn + Send + Sync>;

Notification handler: fire-and-forget, no response.

type SkillHandler = Arc<dyn Fn + Send + Sync>;

Skill handler: receives JSON parameters, returns JSON result.

type MessageHandlerFn = Arc<dyn Fn + Send + Sync>;

Unified message handler function type.

Agent Configuration

Configuration for agent behavior, capabilities, and response patterns.

The agent supports different response patterns:

  • Stateful (default): Creates Task responses for conversation continuity
  • Stateless: Prefers lightweight Message responses for API-style interactions
use agent_sdk::agent::AgentConfig;
// Default stateful agent
let config = AgentConfig::default();
// Stateless agent for API-style interactions
let config = AgentConfig::new("api-agent", "API Agent")
.stateless();

Fields

FieldTypeDescription
nameString
descriptionString
versionString
storage_prefixOption&lt;String&gt;Optional storage namespace prefix used by shared task-storage backends.
max_message_sizeu64
streamingbool
batch_processingbool
concurrent_tasksOption&lt;u32&gt;
stateless_methodsboolPrefer stateless responses (Message) over stateful (Task) for methods
base_urlOption&lt;String&gt;Base URL for constructing absolute AgentInterface URLs in the agent card.
history_policyOption&lt;HistoryPolicyConfig&gt;Optional history policy used for durable continuation preparation.

Methods

fn new<impl Into<String>, impl Into<String>>(name: impl Into, description: impl Into) -> Self

Create a new agent configuration

  • name - Agent name
  • description - Agent description
use agent_sdk::agent::AgentConfig;
let config = AgentConfig::new("my-agent", "My Agent Description");
fn stateless(self) -> Self

Configure agent for stateless method responses

When enabled, methods will prefer lightweight Message responses over Task responses for API-style interactions.

use agent_sdk::agent::AgentConfig;
let config = AgentConfig::new("api-agent", "API Agent")
.stateless();
fn stateful(self) -> Self

Configure agent for stateful method responses (default)

Methods will create Task responses for conversation continuity.

use agent_sdk::agent::AgentConfig;
let config = AgentConfig::default()
.stateful(); // Explicit stateful (default behavior)
fn with_base_url<impl Into<String>>(self, url: impl Into) -> Self

Set the base URL for absolute AgentInterface URLs in the agent card.

The A2A v1.0 spec requires absolute URLs in AgentInterface.url. When set, the SDK produces {base_url}/jsonrpc instead of /jsonrpc.

fn with_history_policy(self, policy: HistoryPolicyConfig) -> Self

Attach a history policy for pre/post-turn continuation preparation.

Agent-level history policy configuration.

Fields

FieldTypeDescription
modeHistoryPolicyMode
strategyHistoryStrategyKind
context_window_tokensu32
max_output_tokensu32
enable_summarizationbool
enable_long_term_memorybool
recall_top_kusize
memory_token_budgetu32

Core Agent implementation

The Agent struct is the main runtime entry point for registering skills, wiring handlers, and dispatching runtime messages. Protocol surfaces such as A2A and AG-UI compose around this type via adapter modules.

Methods

fn new_runtime(name: &str) -> SdkResult<Self>

Create a new runtime-first agent.

fn new(name: &str) -> SdkResult<Self>

Create a new agent (alias of new_runtime)

fn new_with_config(config: AgentConfig) -> SdkResult<Self>

Create agent with custom configuration

fn with_service<T>(self, service: T) -> Self
fn get_service<T>(&self) -> Option<Arc<T>>
fn has_service<T>(&self) -> bool
fn add_skill(&mut self, skill_id: &str) -> SkillEntryBuilder<'_>

Register a skill via the fluent builder (handler optional — omit for metadata-only).

fn skill<F, Fut>(&mut self, name: &str, handler: F) -> SkillEntryBuilder<'_>
where
F: Fn + Send + Sync + ?,
Fut: Future + Send + ?

Register a skill with a handler (add_skill(name).handler(handler)).

fn register_notification<F, Fut>(&mut self, name: &str, handler: F) -> SdkResult<()>
where
F: Fn + Send + Sync + ?,
Fut: Future + Send + ?

Register a notification handler.

fn config(&self) -> &AgentConfig
fn list_skills(&self) -> Vec<String>
fn list_notifications(&self) -> Vec<String>
fn skill_registry(&self) -> &SkillRegistry

Get read-only reference to the skill registry.

fn set_message_handler<F, Fut>(&mut self, handler: F)
where
F: Fn + Send + Sync + ?,
Fut: Future + Send + ?
async fn dispatch_message(&self, msg_ctx: MessageContext, task_ctx: Option<TaskContext>) -> SdkResult<RuntimeResponse>

Unified entrypoint to route a built MessageContext through the active handler.

Message Context for Immediate Processing

Contains all the information needed to process an incoming message, with pre-computed classifications and extractions.

Fields

FieldTypeDescription
runtime_messageagent_core::AgentMessageProtocol-neutral message used by runtime logic.
message_typeMessageTypeClassified message type (Data/Text/Mixed/File)
text_contentOption&lt;String&gt;Extracted text content if present
skill_callOption&lt;SkillCall&gt;Extracted skill call if present
skill_hintsHashMap&lt;String, serde_json::Value&gt;Skill hints from message part metadata
user_metadataHashMap&lt;String, serde_json::Value&gt;Message-level metadata
stateless_modeboolStateless mode preference
skill_executorOption&lt;SkillExecutor&gt;NEW: Skill execution capability for custom handlers

Methods

fn from_agent_message(msg: AgentMessage) -> Self

Build a MessageContext from a protocol-agnostic AgentMessage.

This is the primary entry point for consumers that don’t want to import a2a_protocol_core types.

fn from_text(text: &str) -> Self

Convenience: build from plain text (user role).

let ctx = MessageContext::from_text("What's the weather?");
fn classify_runtime_message(message: &AgentMessage) -> MessageType

Classify message type based on runtime content parts.

fn extract_runtime_skill_call(message: &AgentMessage) -> Option<SkillCall>

Extract skill call information from runtime data parts.

fn extract_runtime_text_content(message: &AgentMessage) -> Option<String>

Extract text content from runtime message parts.

fn from_runtime_message(runtime_message: AgentMessage, skill_hints: HashMap<String, serde_json::Value>, user_metadata: HashMap<String, serde_json::Value>, stateless_mode: bool, skill_executor: Option<SkillExecutor>) -> Self

Build a runtime-native MessageContext.

Skill Call Information

Extracted from DataPart when a structured skill call is detected.

Fields

FieldTypeDescription
skill_idString
parametersserde_json::Value

Skill Execution Capability for Custom Handlers

Provides lightweight skill execution without requiring full agent access. This eliminates the architectural discrepancy between LLM and custom handlers.

Methods

async fn execute_text(&self, skill_id: &str, parameters: Value) -> Result<SkillOutput, SkillError>

Transport-agnostic execution producing string-first output for prompt/context injection

async fn execute_text_with_ctx(&self, skill_id: &str, parameters: Value, message_ctx: MessageContext, task_ctx: Option<TaskContext>) -> Result<SkillOutput, SkillError>

Transport-agnostic execution with optional message/task context for handlers that need it

fn has_skill(&self, skill_id: &str) -> bool

Check if a skill exists in the registry

fn list_skills(&self) -> Vec<String>

Get list of available skills

Task Context for Conversation/Session State

Contains conversation history and task state information, provided only when conversation continuity is needed.

Fields

FieldTypeDescription
task_idStringUnique task identifier
context_idOption&lt;String&gt;Optional conversation context identifier
runtime_historyVec&lt;agent_core::AgentMessage&gt;Previous messages in conversation in protocol-neutral form.
task_phaseagent_core::TaskPhaseCurrent task phase in protocol-neutral form.
artifactsVec&lt;RuntimeArtifact&gt;Task artifacts in runtime form.
task_metadataHashMap&lt;String, serde_json::Value&gt;Task-level metadata
created_atOption&lt;String&gt;Task creation timestamp (from status.timestamp)
updated_atOption&lt;String&gt;Task last update timestamp (from status.timestamp)
continuationOption&lt;ContinuationState&gt;Durable derived continuation metadata from the runtime snapshot store.

Methods

fn from_conversation(ctx: ConversationContext) -> Self

Build a TaskContext from a protocol-agnostic ConversationContext.

fn from_text_turns(turns: &[(&str, &str)]) -> Self

Convenience: build from simple text turns.

let ctx = TaskContext::from_text_turns(&[
("user", "hello"),
("assistant", "hi there"),
]);
fn create_new(context_id: Option<String>) -> Self

Create new task context with basic ISO 8601 timestamp

Resolved skill context ready for LLM message injection.

Produced by SkillRegistry::resolve_skill_context(). Contains the handler’s output (if any) and the skill’s instructions (if any).

Fields

FieldTypeDescription
skill_idString
handler_outputOption&lt;String&gt;
instructionsOption&lt;String&gt;

Protocol-independent skill definition.

This is the single source of truth for skill metadata inside the SDK. Protocol adapters (A2A AgentSkill, MCP, etc.) convert from this type.

Fields

FieldTypeDescription
idString
nameString
descriptionString
input_modesVec&lt;String&gt;
output_modesVec&lt;String&gt;
schemaOption&lt;serde_json::Value&gt;
examplesOption&lt;Vec&lt;String&gt;&gt;
tagsOption&lt;Vec&lt;String&gt;&gt;
instructionsOption&lt;String&gt;Behavioral guidance injected into LLM context when this skill is activated.
exposeboolWhether the skill is visible on the discovery card (default: true).
llm_callableboolWhether the LLM can invoke this skill’s handler via read_skill tool (default: false).

Fluent builder for skill registration.

Created via [SkillRegistry::add_skill] / [crate::Agent::add_skill], or [SkillRegistry::skill] / [crate::Agent::skill] when providing a handler. Finalize with .register().

Methods

fn handler<F, Fut>(self, handler: F) -> Self
where
F: Fn + Send + Sync + ?,
Fut: Future + Send + ?

Attach an async handler. Omit for metadata-only skills (discovery card + LLM awareness).

fn display_name<S>(self, name: S) -> Self
fn description<S>(self, desc: S) -> Self
fn schema(self, schema: Value) -> Self
fn examples(self, examples: Vec<String>) -> Self
fn example<S>(self, example: S) -> Self
fn tags(self, tags: &[&str]) -> Self
fn tag<S>(self, tag: S) -> Self
fn input_modes(self, modes: &[&str]) -> Self
fn output_modes(self, modes: &[&str]) -> Self
fn json_only(self) -> Self
fn text_only(self) -> Self
fn instructions<S>(self, text: S) -> Self

Set behavioral instructions (injected into LLM context when skill is activated).

fn expose(self, visible: bool) -> Self

Control whether the skill is visible on the discovery card (default: true).

fn llm_callable(self, callable: bool) -> Self

Control whether the LLM can invoke this skill via read_skill tool (default: false).

fn register(self) -> SdkResult<()>

Finalize registration.

Manages skill handlers, definitions, and notifications.

Protocol-independent: no A2A types stored. Protocol adapters read SkillDefinition values via accessor methods.

Methods

fn new() -> Self
fn add_skill(&mut self, skill_id: &str) -> SkillEntryBuilder<'_>

Start registering a skill (optional handler — metadata-only if you omit .handler()).

fn skill<F, Fut>(&mut self, name: &str, handler: F) -> SkillEntryBuilder<'_>
where
F: Fn + Send + Sync + ?,
Fut: Future + Send + ?

Register a skill with a handler (convenience — same as add_skill(name).handler(handler)).

async fn execute_skill(&self, skill_id: &str, parameters: &Value) -> Result<Value, String>

Execute a skill by ID (simple handler path).

async fn execute_skill_with_ctx(&self, skill_id: &str, parameters: &Value, exec_ctx: &SkillExecutionContext) -> Result<Value, String>

Execute with optional execution context (prefers context-aware handler).

async fn execute_skill_text(&self, skill_id: &str, parameters: &Value) -> Result<SkillOutput, SkillError>

Execute and return string-first output.

async fn execute_skill_text_with_ctx(&self, skill_id: &str, parameters: &Value, exec_ctx: &SkillExecutionContext) -> Result<SkillOutput, SkillError>

Execute with context and return string-first output.

async fn resolve_skill_context(&self, skill_id: &str, parameters: &Value) -> Option<SkillContext>

Resolve skill context: run handler (if present) and collect instructions.

Returns None when neither handler output nor instructions exist (metadata-only skill — nothing to inject).

fn register_notification<F, Fut>(&mut self, name: &str, handler: F) -> SdkResult<()>
where
F: Fn + Send + Sync + ?,
Fut: Future + Send + ?
fn list_skills(&self) -> Vec<String>
fn list_notifications(&self) -> Vec<String>
fn get_skill_definitions(&self) -> &HashMap<String, SkillDefinition>

All skill definitions (protocol-independent).

fn get_exposed_skills(&self) -> Vec<&SkillDefinition>

Skills with expose == true (for discovery card adapters).

fn get_llm_callable_skills(&self) -> Vec<&SkillDefinition>

Skills with llm_callable == true (for read_skill tool generation).

fn get_instructions(&self, skill_id: &str) -> Option<&str>

Get instructions for a skill.

fn get_skill_handlers(&self) -> &HashMap<String, SkillHandler>
fn get_notification_handlers(&self) -> &HashMap<String, NotificationHandler>

Manages message handlers and coordinates message processing.

Methods

fn new(skill_registry: Arc<SkillRegistry>) -> Self
fn initialize_default_handler(&mut self)

Initialize default handler (skill dispatch + conversational fallback).

fn set_custom_handler<F, Fut>(&mut self, handler: F)
where
F: Fn + Send + Sync + ?,
Fut: Future + Send + ?
async fn handle_message(&self, msg_ctx: MessageContext, task_ctx: Option<TaskContext>) -> SdkResult<RuntimeResponse>

Handle message using the active handler.

Explicit runtime response API.

Methods

fn message_parts(parts: Vec<ContentPart>, msg_meta: Option<HashMap<String, Value>>, context_id: Option<String>) -> SdkResult<RuntimeResponse>
fn message_text<impl Into<String>>(text: impl Into, _part_meta: Option<HashMap<String, Value>>, msg_meta: Option<HashMap<String, Value>>, context_id: Option<String>) -> SdkResult<RuntimeResponse>
fn task(opts: TaskOpts, msg_ctx: &MessageContext, task_ctx: Option<TaskContext>) -> SdkResult<RuntimeResponse>

Fields

FieldTypeDescription
nameString
descriptionOption&lt;String&gt;
dataserde_json::Value

Methods

fn data<impl Into<String>>(name: impl Into, data: Value) -> Self

Fields

FieldTypeDescription
messageagent_core::AgentMessage
context_idOption&lt;String&gt;
metadataOption&lt;HashMap&lt;String, serde_json::Value&gt;&gt;

Fields

FieldTypeDescription
task_idString
context_idString
historyVec&lt;agent_core::AgentMessage&gt;
artifactsVec&lt;RuntimeArtifact&gt;
metadataHashMap&lt;String, serde_json::Value&gt;
phaseagent_core::TaskPhase
status_textOption&lt;String&gt;
continuation_updateOption&lt;RuntimeContinuationUpdate&gt;

Options for constructing a runtime task response.

Fields

FieldTypeDescription
artifactsVec&lt;RuntimeArtifact&gt;
stateOption&lt;agent_core::TaskPhase&gt;
status_textOption&lt;String&gt;
task_metaOption&lt;HashMap&lt;String, serde_json::Value&gt;&gt;
history_partsOption&lt;Vec&lt;agent_core::ContentPart&gt;&gt;

Response Builder

Provides static methods for creating different types of runtime responses. Follows the builder pattern and maintains consistency across response types.

Methods

fn create_skill_success_response(skill_call: &SkillCall, result: Value, msg_ctx: &MessageContext, task_ctx: Option<TaskContext>) -> SdkResult<RuntimeResponse>

Create skill success response

Generates a runtime response when a skill executes successfully. Handles both stateful (Task) and stateless (Message) response modes.

fn create_skill_not_implemented_response(skill_call: &SkillCall, _msg_ctx: &MessageContext, task_ctx: Option<TaskContext>) -> SdkResult<RuntimeResponse>

Create response for skill that’s advertised but not implemented

Provides a helpful conversational response when a skill is in the agent card but doesn’t have an automated handler implementation.

fn create_conversational_response(msg_ctx: &MessageContext, task_ctx: Option<TaskContext>) -> SdkResult<RuntimeResponse>

Create conversational response for non-skill messages

Handles general conversational messages that don’t involve skill calls. Provides appropriate responses based on stateful vs stateless mode.

Task Manager

Handles task context creation, retrieval, and persistence for conversation continuity. Provides a clean abstraction over the runtime task-store seam.

Minimal tool execution result for MVP

Fields

FieldTypeDescription
nameString
outputserde_json::Value

Minimal registry for LLM tools

Methods

fn new() -> Self
fn register(&mut self, tool: ToolSpec)
fn get(&self, name: &str) -> Option<&ToolSpec>
fn list(&self) -> Vec<&ToolSpec>
fn merge(&mut self, other: ToolRegistry)

Merge another registry into this one. Later registrations overwrite earlier ones with the same name.

fn len(&self) -> usize

Number of registered tools.

fn is_empty(&self) -> bool

Whether the registry is empty.

async fn execute(&self, name: &str, args: serde_json::Value) -> Result<ToolExecutionResult, String>

Execute a tool by name (serial MVP)

Tool specification exposed to the LLM (separate from skills)

Fields

FieldTypeDescription
nameString
descriptionOption&lt;String&gt;
parametersserde_json::Value
strictboolHints
parallel_okbool
executorToolExecutor

Agent Module Documentation

This module provides the core runtime implementation with the following capabilities:

  • Agent Creation: Runtime-first constructors for different use cases
  • Skill Management: Register, execute, and manage agent skills
  • Message Processing: Route runtime messages and integrate optional LLM orchestration
  • Response Modes: Control response types (stateful vs stateless)
  • Service Injection: Type-safe dependency injection for external services
use agent_sdk::agent::{Agent, AgentConfig};
use serde_json::json;
// Simple runtime agent
let mut agent = Agent::new_runtime("my-agent")?;
// Custom configuration
let config = AgentConfig::new("api-agent", "API-only agent").stateless();
let mut agent = Agent::new_with_config(config)?;
# Ok::<(), agent_sdk::SdkError>(())
# use agent_sdk::agent::Agent;
# use serde_json::json;
# let mut agent = Agent::new_runtime("test")?;
// Simple skill registration
agent.skill("get_weather", |params| async move {
let location = params["location"].as_str().unwrap_or("unknown");
Ok(json!({"location": location, "temp": 22}))
}).register()?;
// Fluent builder with metadata
agent.skill("analyze", |params| async move {
Ok(json!({"analysis": "complete"}))
})
.display_name("Data Analysis")
.schema(json!({"type": "object", "properties": {"data": {"type": "string"}}}))
.tags(&["analytics", "data"])
.register()?;
# Ok::<(), agent_sdk::SdkError>(())
# use agent_sdk::agent::AgentConfig;
// Stateful agent (default) - creates Tasks for conversation continuity
let config = AgentConfig::default();
// Stateless agent - prefers lightweight Message responses
let config = AgentConfig::new("api-agent", "API Agent").stateless();

History preparation mode for durable continuation.

Variants

VariantDescription
PassThrough
HistoryManager

Strategy name exposed through SDK config without leaking llm_context_core types.

Variants

VariantDescription
SlidingWindow
SlidingWindowWithSummary
PriorityBased

Message Type Classification

Classifies incoming messages based on their Part composition to determine the appropriate handling strategy (tool-like vs conversational vs hybrid).

Variants

VariantDescription
DataPure DataPart - structured tool calls
TextPure TextPart - conversational interactions
MixedMultiple part types - hybrid interactions

Variants

VariantDescription
Message(RuntimeMessage)
Task(RuntimeTask)

Methods

fn text_content(&self) -> Option<String>

Variants

VariantDescription
Simple(Arc<dyn Fn + Send + Sync>)