///|
/// Normalize generic async failures once at the Agent endpoint boundary.
fn agent_handler_error(method_name : String, error : Error) -> HandlerError {
if @async.is_cancellation_error(error) {
Cancelled
} else {
match error {
HandlerError::Application(message~) =>
Application(message=method_name + ": " + message)
HandlerError::Cancelled => Cancelled
HandlerError::UnavailableOperation(method_name~) =>
UnavailableOperation(method_name~)
_ => Application(message="agent handler failed")
}
}
}
///|
async fn[T] agent_handler_call(
method_name : String,
handler : async () -> T,
) -> T raise HandlerError {
handler() catch {
error => raise agent_handler_error(method_name, error)
}
}
///|
/// Typed async handler signatures for the required session operations.
pub type AgentNewSessionHandler = async (AgentContext, NewSessionParams) -> NewSessionResult
///|
pub type AgentPromptHandler = async (AgentContext, PromptParams) -> PromptResult
///|
pub type AgentCancelHandler = async (AgentContext, CancelParams) -> Unit
///|
/// Typed async handler signatures for optional session operations.
pub type AgentLoadSessionHandler = async (AgentContext, LoadSessionParams) -> LoadSessionResult
///|
pub type AgentResumeSessionHandler = async (AgentContext, ResumeSessionParams) -> ResumeSessionResult
///|
pub type AgentListSessionsHandler = async (AgentContext, ListSessionsParams) -> ListSessionsResult
///|
pub type AgentDeleteSessionHandler = async (AgentContext, DeleteSessionParams) -> DeleteSessionResult
///|
pub type AgentCloseSessionHandler = async (AgentContext, CloseSessionParams) -> CloseSessionResult
///|
pub type AgentSetModeHandler = async (AgentContext, SetSessionModeParams) -> SetSessionModeResult
///|
pub type AgentSetConfigOptionHandler = async (
AgentContext,
SetSessionConfigOptionParams,
) -> SetSessionConfigOptionResult
///|
/// Typed async authentication handlers.
pub type AgentAuthenticateHandler = async (AgentContext, AuthenticateParams) -> AuthenticateResult
///|
pub type AgentLogoutHandler = async (AgentContext, LogoutParams) -> LogoutResult
///|
/// Immutable required/optional session service. The fields remain private so
/// callers cannot construct a half-populated service record.
pub struct AgentSessionService {
new_handler : AgentNewSessionHandler
prompt_handler : AgentPromptHandler
cancel_handler : AgentCancelHandler
load_handler : AgentLoadSessionHandler?
resume_handler : AgentResumeSessionHandler?
list_handler : AgentListSessionsHandler?
delete_handler : AgentDeleteSessionHandler?
close_handler : AgentCloseSessionHandler?
set_mode_handler : AgentSetModeHandler?
set_config_option_handler : AgentSetConfigOptionHandler?
}
///|
/// Compose all required session callbacks once. Optional callbacks are
/// represented by `None`; there is no mutable registration phase.
pub fn agent_session_service(
new_session~ : AgentNewSessionHandler,
prompt~ : AgentPromptHandler,
cancel~ : AgentCancelHandler,
load? : AgentLoadSessionHandler,
resume_session? : AgentResumeSessionHandler,
list? : AgentListSessionsHandler,
delete? : AgentDeleteSessionHandler,
close? : AgentCloseSessionHandler,
set_mode? : AgentSetModeHandler,
set_config_option? : AgentSetConfigOptionHandler,
) -> AgentSessionService {
{
new_handler: new_session,
prompt_handler: prompt,
cancel_handler: cancel,
load_handler: load,
resume_handler: resume_session,
list_handler: list,
delete_handler: delete,
close_handler: close,
set_mode_handler: set_mode,
set_config_option_handler: set_config_option,
}
}
///|
/// Immutable authentication service. `methods` is copied and checked before
/// the service can be used by an endpoint.
pub struct AgentAuthService {
methods : Array[AuthMethod]
authenticate_handler : AgentAuthenticateHandler
logout_handler : AgentLogoutHandler?
}
///|
pub fn agent_auth_service(
methods~ : Array[AuthMethod],
authenticate~ : AgentAuthenticateHandler,
logout? : AgentLogoutHandler,
) -> AgentAuthService raise AgentCompositionError {
if methods.length() == 0 {
raise InvalidAuthMethods(reason="at least one method is required")
}
let seen : Map[String, Bool] = Map([])
for auth_method in methods {
if auth_method.id.length() == 0 {
raise InvalidAuthMethods(reason="method id must not be empty")
}
if seen.contains(auth_method.id) {
raise InvalidAuthMethods(reason="duplicate method id: " + auth_method.id)
}
seen[auth_method.id] = true
}
{
methods: methods.copy(),
authenticate_handler: authenticate,
logout_handler: logout,
}
}
///|
/// Non-service support declarations used to derive prompt/MCP capabilities.
/// These are immutable values, not a second manually supplied capability map.
pub struct AgentSupport {
prompt_image : Bool
prompt_audio : Bool
prompt_embedded_context : Bool
mcp_http : Bool
mcp_sse : Bool
additional_directories : Bool
}
///|
pub fn agent_support(
prompt_image? : Bool = false,
prompt_audio? : Bool = false,
prompt_embedded_context? : Bool = false,
mcp_http? : Bool = false,
mcp_sse? : Bool = false,
additional_directories? : Bool = false,
) -> AgentSupport {
{
prompt_image,
prompt_audio,
prompt_embedded_context,
mcp_http,
mcp_sse,
additional_directories,
}
}
///|
/// Immutable specification consumed by the Reader composition program.
pub struct AgentSpec {
info : Implementation
sessions : AgentSessionService
support : AgentSupport
auth : AgentAuthService?
}
///|
pub fn agent_spec(
info~ : Implementation,
sessions~ : AgentSessionService,
support~ : AgentSupport,
auth? : AgentAuthService,
) -> Result[AgentSpec, AgentCompositionError] {
if info.name.length() == 0 {
Err(InvalidImplementation(reason="agent name must not be empty"))
} else if info.version.length() == 0 {
Err(InvalidImplementation(reason="agent version must not be empty"))
} else {
Ok({ info, sessions, support, auth })
}
}