pub struct AgentDialer { /* private fields */ }Expand description
Reusable handle for dialing the polychrome control plane.
Implementations§
Source§impl AgentDialer
impl AgentDialer
Sourcepub fn new(addr: &str) -> Result<Self, DialError>
pub fn new(addr: &str) -> Result<Self, DialError>
Build a dialer pointed at addr (expects http://host:port).
Unauthenticated: no bearer header rides its calls. This constructor is
suitable only for non-ingress methods such as health/classification;
Self::receive_ingress refuses it before network I/O.
§Errors
Returns DialError::InvalidAddress if addr isn’t a valid URI.
Sourcepub fn with_credentials(
addr: &str,
creds: EdgeCredentials,
) -> Result<Self, DialError>
pub fn with_credentials( addr: &str, creds: EdgeCredentials, ) -> Result<Self, DialError>
Build a dialer pointed at addr, authenticated with creds.
Every call this dialer makes carries an Authorization: Bearer <creds.bearer()> header, and every turn it dials signs a fresh
AssertedAttribution envelope (creds.edge_id(), this turn’s
conversation_id, a per-call nonce/timestamp, and the caller
Attribution passed to the turn) onto AgentStart.asserted_attribution.
§Errors
Returns DialError::InvalidAddress if addr isn’t a valid URI, or
DialError::InvalidBearer if creds.bearer() can’t be encoded as
an HTTP header value.
Sourcepub fn approval_dialer(addr: &str) -> Result<ApprovalDialer, DialError>
pub fn approval_dialer(addr: &str) -> Result<ApprovalDialer, DialError>
Build an ApprovalDialer for the SAME control-plane endpoint.
AgentService and ApprovalService are served on one Connect port, so
an approval client reuses the agent address — callers that already hold
an AgentDialer don’t need to thread a second address.
§Errors
Returns DialError::InvalidAddress if addr isn’t a valid URI.
Sourcepub fn approval_dialer_with_credentials(
&self,
addr: &str,
) -> Result<ApprovalDialer, DialError>
pub fn approval_dialer_with_credentials( &self, addr: &str, ) -> Result<ApprovalDialer, DialError>
Build an ApprovalDialer for the SAME control-plane endpoint,
sharing this dialer’s edge credentials — the bearer on every call, and
the identity key that signs an approval’s AssertedApproval
(#1553). The authenticated sibling of Self::approval_dialer, for
an edge that has already built its AgentDialer.
On the unauthenticated Self::new path there are no credentials to
share, so this returns exactly what Self::approval_dialer does:
no bearer, and no responder asserted on any decision.
§Errors
Returns DialError::InvalidAddress if addr isn’t a valid URI, or
DialError::InvalidBearer if the credentials’ bearer can’t be
encoded as an HTTP header value.
Sourcepub fn question_dialer_with_credentials(
&self,
addr: &str,
) -> Result<QuestionDialer, DialError>
pub fn question_dialer_with_credentials( &self, addr: &str, ) -> Result<QuestionDialer, DialError>
Build a QuestionDialer for the SAME control-plane endpoint,
carrying this dialer’s edge bearer. The question sibling of
Self::approval_dialer_with_credentials.
Shares the bearer and NOT the signing key. An approval decision is
signed; a question answer is not, because answering authorizes
nothing (see QuestionDialer::respond).
The bearer is still required. QuestionService sits behind the
control plane’s require_edge_bearer layer like every other RPC on
that listener, so an edge that builds an unauthenticated dialer sees
every answer fail closed with 401 — the #1660 incident, described on
QuestionDialer::new.
On the unauthenticated Self::new path there is no bearer to share,
so this returns exactly what QuestionDialer::new does.
§Errors
Returns DialError::InvalidAddress if addr isn’t a valid URI, or
DialError::InvalidBearer if the credentials’ bearer can’t be
encoded as an HTTP header value.
Sourcepub async fn receive_ingress(
&self,
ingress: TurnIngress,
) -> Result<DurablyReceivedTurn, DialError>
pub async fn receive_ingress( &self, ingress: TurnIngress, ) -> Result<DurablyReceivedTurn, DialError>
Durably receives one source event without waiting for its turn output.
The returned DurablyReceivedTurn is acknowledgement authority: it
exists only after the unary ReceiveIngress response carries a
nonempty State receipt and a matching signed source identity. Response
consumption starts separately through Self::attach_ingress.
§Errors
Returns DialError::MissingIngressCredentials before network I/O for
an unauthenticated dialer, DialError::Connect for transport errors,
or DialError::InvalidIngressReceipt for an incomplete or mismatched
success response.
Sourcepub async fn attach_ingress(
&self,
received: &DurablyReceivedTurn,
) -> Result<impl Stream<Item = Result<TurnEvent, DialError>> + use<>, DialError>
pub async fn attach_ingress( &self, received: &DurablyReceivedTurn, ) -> Result<impl Stream<Item = Result<TurnEvent, DialError>> + use<>, DialError>
Attaches to a durably received turn and projects its response stream.
The proof-carrying DurablyReceivedTurn is required; a dispatch id
string or a successfully opened transport cannot call this method.
Reattaching with the same proof never starts the turn twice. Until a
durable output outbox exists, Control fails that duplicate attachment
closed rather than pretending it can replay response bytes it did not
persist; callers must treat a lost response stream as outcome-unknown.
§Errors
The outer result returns DialError::Connect if attachment fails.
Each stream item carries response transport/decode failures inline.
Sourcepub async fn attach_ingress_buffered(
&self,
received: &DurablyReceivedTurn,
) -> Result<BufferedTurn, DialError>
pub async fn attach_ingress_buffered( &self, received: &DurablyReceivedTurn, ) -> Result<BufferedTurn, DialError>
Attaches to a durably received turn and collects its buffered result.
This is the response half for request/response edges: they await
Self::receive_ingress, acknowledge the source using that proof, and
may then collect text and pending interactions without keeping source
acknowledgement coupled to turn execution.
§Errors
Returns DialError::Connect for attachment, stream, or decode
failures.
Sourcepub async fn run_turn(
&self,
conversation_id: &str,
exec_id: &str,
source_identity: IngressIdentity,
namespace: ClaimedNamespace,
user_text: &str,
) -> Result<String, DialError>
pub async fn run_turn( &self, conversation_id: &str, exec_id: &str, source_identity: IngressIdentity, namespace: ClaimedNamespace, user_text: &str, ) -> Result<String, DialError>
Run one turn against the control plane and collect the aggregated text response.
conversation_id is the stable id for the conversation (the Slack
adapter derives it from the thread; the CLI takes it as an argument).
user_text is the user message with any bot mention already stripped.
Returns the concatenated assistant text across every batch in the
response stream, or Ok(String::new()) if the turn produced no text.
Non-text content variants (tool calls, tool results, thoughts) are
rendered as bracketed placeholders.
§Errors
Returns DialError::Connect for any transport/stream/encoding
error from the AgentService call.
Sourcepub async fn run_turn_with(
&self,
conversation_id: &str,
exec_id: &str,
source_identity: IngressIdentity,
namespace: ClaimedNamespace,
user_text: &str,
attribution: Attribution,
) -> Result<String, DialError>
pub async fn run_turn_with( &self, conversation_id: &str, exec_id: &str, source_identity: IngressIdentity, namespace: ClaimedNamespace, user_text: &str, attribution: Attribution, ) -> Result<String, DialError>
Like run_turn but attributes the turn to a caller
(and participants). Non-streaming edges that resolve an identity via
EdgeAdapter::caller use this so their turns populate
AgentStart.caller (persona attribution) — the buffered analog of
run_turn_streaming_messages_with.
§Errors
Returns DialError::Connect for any transport/stream/encoding error.
Sourcepub async fn run_turn_with_approvals(
&self,
conversation_id: &str,
exec_id: &str,
source_identity: IngressIdentity,
namespace: ClaimedNamespace,
user_text: &str,
attribution: Attribution,
ingress_directive: IngressDirective,
) -> Result<BufferedTurn, DialError>
pub async fn run_turn_with_approvals( &self, conversation_id: &str, exec_id: &str, source_identity: IngressIdentity, namespace: ClaimedNamespace, user_text: &str, attribution: Attribution, ingress_directive: IngressDirective, ) -> Result<BufferedTurn, DialError>
Like run_turn_with but ALSO surfaces any
PendingApprovalPrompts the turn paused on, mirroring the streaming
path’s TurnEvent::ApprovalPending projection of the same terminal
AgentEnd.pending_approvals field.
Buffered edges (Discord/email/trigger/A2A) that don’t drive the
streaming API still need to render an approve/deny affordance instead
of losing a gated call to the scaffolding-placeholder fallback — this
is the buffered-API variant that lets them. Call
ApprovalDialer::respond for a decision, then re-drive with this
same method (empty user_text) to resume the turn.
ingress_directive is the edge’s own policy for this turn (#68) —
a step-budget cap, an advisory priority, and/or a required approver.
An edge with no such policy passes IngressDirective::default
(empty; byte-for-byte unaffected).
§Errors
Returns DialError::Connect for any transport/stream/encoding
error from the AgentService call.
Sourcepub async fn run_routine_turn(
&self,
ingress: TurnIngress,
) -> Result<BufferedTurn, DialError>
pub async fn run_routine_turn( &self, ingress: TurnIngress, ) -> Result<BufferedTurn, DialError>
Drive one routine fire turn: like
run_turn_with_approvals, but the
conversation declares ephemeral history (#843), so the control plane
starts the model on an empty transcript instead of replaying prior fires.
Used by the trigger edge’s fan-out — each periodic firing is independent,
never re-feeding past digests / tool loops into context. The event log
still records every fire for forensics, and a gated call still surfaces
its PendingApprovalPrompts so the pause is discoverable.
The caller builds ingress with TurnIngress::with_ephemeral_history,
TurnIngress::with_occurrence, and the routine’s policy through
TurnIngress::with_ingress_directive. Keeping those fields in one
required-source-identity value prevents a routine variant from dropping
its idempotency key as its options grow.
§Errors
Returns DialError::Connect for any transport/stream/encoding error.
Sourcepub async fn run_turn_streaming(
&self,
conversation_id: &str,
exec_id: &str,
source_identity: IngressIdentity,
namespace: ClaimedNamespace,
user_text: &str,
) -> Result<impl Stream<Item = Result<TurnEvent, DialError>>, DialError>
pub async fn run_turn_streaming( &self, conversation_id: &str, exec_id: &str, source_identity: IngressIdentity, namespace: ClaimedNamespace, user_text: &str, ) -> Result<impl Stream<Item = Result<TurnEvent, DialError>>, DialError>
Run one turn against the control plane and stream each meaningful step
as a TurnEvent, for live surfaces that update in place.
The request is built identically to AgentDialer::run_turn; see that
method for the meaning of conversation_id, exec_id, and
user_text. The returned stream yields:
TurnEvent::TextDeltafor each model/assistant-role text block,TurnEvent::ToolStartedwhen a tool call begins, andTurnEvent::Doneonce at end-of-turn, after which the stream ends.
Tool-role result echoes and empty/non-textual blocks produce no event.
§Errors
The outer Result carries a DialError::Connect if opening the
stream fails. Each item is a Result so per-message transport/decode
errors surface inline without tearing down the whole stream type.
Sourcepub async fn run_turn_streaming_messages(
&self,
conversation_id: &str,
exec_id: &str,
source_identity: IngressIdentity,
namespace: ClaimedNamespace,
messages: Vec<Message>,
) -> Result<impl Stream<Item = Result<TurnEvent, DialError>>, DialError>
pub async fn run_turn_streaming_messages( &self, conversation_id: &str, exec_id: &str, source_identity: IngressIdentity, namespace: ClaimedNamespace, messages: Vec<Message>, ) -> Result<impl Stream<Item = Result<TurnEvent, DialError>>, DialError>
Like Self::run_turn_streaming but takes a pre-built (e.g. attributed
multi-party) message list as the turn input.
§Errors
Returns DialError::Connect for any transport/stream/encoding error.
Sourcepub async fn run_turn_streaming_messages_with(
&self,
conversation_id: &str,
exec_id: &str,
source_identity: IngressIdentity,
namespace: ClaimedNamespace,
messages: Vec<Message>,
payment_receipt: Option<PaymentReceipt>,
attribution: Attribution,
ingress_directive: IngressDirective,
) -> Result<impl Stream<Item = Result<TurnEvent, DialError>>, DialError>
pub async fn run_turn_streaming_messages_with( &self, conversation_id: &str, exec_id: &str, source_identity: IngressIdentity, namespace: ClaimedNamespace, messages: Vec<Message>, payment_receipt: Option<PaymentReceipt>, attribution: Attribution, ingress_directive: IngressDirective, ) -> Result<impl Stream<Item = Result<TurnEvent, DialError>>, DialError>
The full-fidelity variant: one method carries everything a turn’s
request can — a settled inbound PaymentReceipt (the control plane
persists a signed payment_receipt event in the turn’s atomic batch),
the caller Attribution (resolved to durable personas and recorded
as caller/participant events), and the edge’s own
IngressDirective (#68) — a step-budget cap, an advisory
priority, and/or a required approver. One method rather than a matrix
of variants, so a paid and attributed and directed edge can’t
silently drop one of the three. An edge with no ingress policy passes
IngressDirective::default.
§Errors
Returns DialError::Connect for any transport/stream/encoding error.
Sourcepub async fn run_turn_streaming_ingress(
&self,
ingress: TurnIngress,
) -> Result<impl Stream<Item = Result<TurnEvent, DialError>>, DialError>
pub async fn run_turn_streaming_ingress( &self, ingress: TurnIngress, ) -> Result<impl Stream<Item = Result<TurnEvent, DialError>>, DialError>
Runs a fully assembled durable ingress turn.
Prefer this variant when optional turn fields are naturally composed
through TurnIngress’s builders.
§Errors
Returns DialError::Connect for any transport/stream/encoding error.
Sourcepub async fn should_respond(
&self,
conversation_id: &str,
bot_name: &str,
surface: &str,
transcript: Vec<ParticipantMessage>,
source_identity: &IngressIdentity,
namespace: &ClaimedNamespace,
) -> Result<bool, DialError>
pub async fn should_respond( &self, conversation_id: &str, bot_name: &str, surface: &str, transcript: Vec<ParticipantMessage>, source_identity: &IngressIdentity, namespace: &ClaimedNamespace, ) -> Result<bool, DialError>
Ask the control plane’s participation gate whether to reply to the
latest message of a (multi-party) thread. Returns true on respond
and false on ignore. The gate runs a cheap classifier model
server-side and never runs a turn.
surface names the calling edge’s surface (for example "Slack");
it is rendered into the classifier prompt so the gate reads the
thread in its real setting. Empty keeps the prompt surface-neutral.
source_identity names the source event this evaluation belongs to. It
travels inside the signed envelope, which is what lets the control
plane derive a model tenant: the gate spends against one, so it
authenticates its caller exactly as a turn does.
§Errors
Returns DialError::MissingIngressCredentials when this client holds
no edge credential to sign with, and DialError::Connect for any
transport/encoding error.
Sourcepub async fn interrupt(&self, conversation_id: &str) -> Result<bool, DialError>
pub async fn interrupt(&self, conversation_id: &str) -> Result<bool, DialError>
Cancel the conversation’s in-flight turn without dropping a Connect
stream. Returns true if a running turn was found and signalled to
cancel; false is the idempotent no-op (no turn running on the replica
that served this call).
§Errors
Returns DialError::Connect for any transport/encoding error.
Trait Implementations§
Source§impl Clone for AgentDialer
impl Clone for AgentDialer
Source§fn clone(&self) -> AgentDialer
fn clone(&self) -> AgentDialer
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreAuto Trait Implementations§
impl !RefUnwindSafe for AgentDialer
impl !UnwindSafe for AgentDialer
impl Freeze for AgentDialer
impl Send for AgentDialer
impl Sync for AgentDialer
impl Unpin for AgentDialer
impl UnsafeUnpin for AgentDialer
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> FutureExt for T
impl<T> FutureExt for T
Source§fn with_context(self, otel_cx: Context) -> WithContext<Self> ⓘ
fn with_context(self, otel_cx: Context) -> WithContext<Self> ⓘ
Source§fn with_current_context(self) -> WithContext<Self> ⓘ
fn with_current_context(self) -> WithContext<Self> ⓘ
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> IntoRequest<T> for T
impl<T> IntoRequest<T> for T
Source§fn into_request(self) -> Request<T>
fn into_request(self) -> Request<T>
T in a tonic::Request