///|
/// The closed set of typed results that a Client can return to an Agent.
///
/// Constructor names are deliberately prefixed so this facade cannot be
/// confused with the corresponding wire request or endpoint constructors.
pub(all) enum AgentOutboundReply {
AgentReplyRequestPermission(RequestPermissionResponse)
AgentReplyFsReadTextFile(ReadTextFileResult)
AgentReplyFsWriteTextFile(WriteTextFileResult)
AgentReplyTerminalCreate(TerminalCreateResult)
AgentReplyTerminalOutput(TerminalOutputResult)
AgentReplyTerminalWaitForExit(TerminalWaitForExitResult)
AgentReplyTerminalKill(TerminalKillResult)
AgentReplyTerminalRelease(TerminalReleaseResult)
AgentReplyElicitationCreate(ElicitationCreateResult)
} derive(Eq, Debug)
///|
/// Failures crossing an Agent-to-Client context port. Runtime adapters may
/// construct these values from negotiated capability gates or transport
/// cancellation, while application code never handles untyped JSON here.
pub(all) suberror AgentContextError {
AgentContextUnavailable(method_name~ : String)
AgentContextCancelled(method_name~ : String)
AgentContextBrokerFailure(method_name~ : String)
AgentContextReplyMismatch(method_name~ : String)
} derive(Eq, Debug)
///|
/// The request-side composition seam. It is a one-shot broker, not a queue
/// or a mutable registry; connection state remains owned by the runtime.
pub type AgentContextRequestBroker = async (ClientRequest) -> Result[
AgentOutboundReply,
AgentContextError,
]
///|
/// The notification-side composition seam. Notifications have no response
/// value, so success is represented only by `Unit`.
pub type AgentContextNotificationBroker = async (ClientNotification) -> Result[
Unit,
AgentContextError,
]
///|
/// Opaque typed outbound context supplied to one Agent invocation. It owns
/// only the two immutable broker closures; it contains no connection state,
/// queue, task, global registry, or raw protocol value.
pub struct AgentContext {
request_broker : AgentContextRequestBroker
notification_broker : AgentContextNotificationBroker
}
///|
/// Construct one immutable context around caller-owned typed transport ports.
/// This is intentionally a one-shot constructor rather than a builder.
pub fn agent_context(
request_broker~ : AgentContextRequestBroker,
notification_broker~ : AgentContextNotificationBroker,
) -> AgentContext {
{ request_broker, notification_broker }
}
///|
fn agent_context_exception(
method_name~ : String,
error : Error,
) -> AgentContextError {
if @async.is_cancellation_error(error) {
AgentContextCancelled(method_name~)
} else {
AgentContextBrokerFailure(method_name~)
}
}
///|
async fn agent_context_request(
context : AgentContext,
request : ClientRequest,
) -> AgentOutboundReply raise AgentContextError {
let method_name = request.method_name()
let result = try (context.request_broker)(request) catch {
error => Err(agent_context_exception(method_name~, error))
} noraise {
value => value
}
match result {
Ok(reply) => reply
Err(error) => raise error
}
}
///|
async fn agent_context_notification(
context : AgentContext,
notification : ClientNotification,
) -> Unit raise AgentContextError {
let method_name = notification.method_name()
let result = try (context.notification_broker)(notification) catch {
error => Err(agent_context_exception(method_name~, error))
} noraise {
value => value
}
match result {
Ok(_) => ()
Err(error) => raise error
}
}
///|
/// Ask the Client for permission to execute a tool call.
pub async fn AgentContext::request_permission(
self : AgentContext,
request : RequestPermissionRequest,
) -> RequestPermissionResponse raise AgentContextError {
match agent_context_request(self, SessionRequestPermission(request)) {
AgentReplyRequestPermission(reply) => reply
_ =>
raise AgentContextReplyMismatch(method_name="session/request_permission")
}
}
///|
/// Read one absolute filesystem path through the negotiated Client service.
pub async fn AgentContext::read_text_file(
self : AgentContext,
params : ReadTextFileParams,
) -> ReadTextFileResult raise AgentContextError {
match agent_context_request(self, FsReadTextFile(params)) {
AgentReplyFsReadTextFile(reply) => reply
_ => raise AgentContextReplyMismatch(method_name="fs/read_text_file")
}
}
///|
/// Write one absolute filesystem path through the negotiated Client service.
pub async fn AgentContext::write_text_file(
self : AgentContext,
params : WriteTextFileParams,
) -> WriteTextFileResult raise AgentContextError {
match agent_context_request(self, FsWriteTextFile(params)) {
AgentReplyFsWriteTextFile(reply) => reply
_ => raise AgentContextReplyMismatch(method_name="fs/write_text_file")
}
}
///|
/// Create a terminal owned by the caller's Client runtime.
pub async fn AgentContext::terminal_create(
self : AgentContext,
params : TerminalCreateParams,
) -> TerminalCreateResult raise AgentContextError {
match agent_context_request(self, TerminalCreate(params)) {
AgentReplyTerminalCreate(reply) => reply
_ => raise AgentContextReplyMismatch(method_name="terminal/create")
}
}
///|
/// Read accumulated output from a typed terminal handle.
pub async fn AgentContext::terminal_output(
self : AgentContext,
params : TerminalOutputParams,
) -> TerminalOutputResult raise AgentContextError {
match agent_context_request(self, TerminalOutput(params)) {
AgentReplyTerminalOutput(reply) => reply
_ => raise AgentContextReplyMismatch(method_name="terminal/output")
}
}
///|
/// Wait for a typed terminal to exit.
pub async fn AgentContext::terminal_wait_for_exit(
self : AgentContext,
params : TerminalWaitForExitParams,
) -> TerminalWaitForExitResult raise AgentContextError {
match agent_context_request(self, TerminalWaitForExit(params)) {
AgentReplyTerminalWaitForExit(reply) => reply
_ => raise AgentContextReplyMismatch(method_name="terminal/wait_for_exit")
}
}
///|
/// Kill a typed terminal process.
pub async fn AgentContext::terminal_kill(
self : AgentContext,
params : TerminalKillParams,
) -> TerminalKillResult raise AgentContextError {
match agent_context_request(self, TerminalKill(params)) {
AgentReplyTerminalKill(reply) => reply
_ => raise AgentContextReplyMismatch(method_name="terminal/kill")
}
}
///|
/// Release a typed terminal handle.
pub async fn AgentContext::terminal_release(
self : AgentContext,
params : TerminalReleaseParams,
) -> TerminalReleaseResult raise AgentContextError {
match agent_context_request(self, TerminalRelease(params)) {
AgentReplyTerminalRelease(reply) => reply
_ => raise AgentContextReplyMismatch(method_name="terminal/release")
}
}
///|
/// Create either form- or URL-mode elicitation using the stable union.
pub async fn AgentContext::elicitation_create(
self : AgentContext,
params : ElicitationCreateParams,
) -> ElicitationCreateResult raise AgentContextError {
match agent_context_request(self, ElicitationCreate(params)) {
AgentReplyElicitationCreate(reply) => reply
_ => raise AgentContextReplyMismatch(method_name="elicitation/create")
}
}
///|
/// Deliver a typed session update notification. No response is synthesized.
pub async fn AgentContext::session_update(
self : AgentContext,
params : SessionUpdateParams,
) -> Unit raise AgentContextError {
agent_context_notification(self, SessionUpdate(params))
}
///|
/// Complete a typed elicitation notification. No response is synthesized.
pub async fn AgentContext::elicitation_complete(
self : AgentContext,
params : ElicitationCompleteParams,
) -> Unit raise AgentContextError {
agent_context_notification(self, ElicitationComplete(params))
}
///|
/// Request cancellation of one in-flight typed request. The runtime decides
/// how to cancel the task; this facade only emits the typed notification.
pub async fn AgentContext::cancel_request(
self : AgentContext,
request_id : RequestId,
) -> Unit raise AgentContextError {
agent_context_notification(self, CancelRequest(request_id))
}