///|
/// Public runtime seam (experimental): `Runtime` is the supported
/// correspondence of the internal effect-execution boundary. Advanced hosts
/// implement or wrap it to customize how effects are executed — cancellation
/// propagation, effect correlation, remote execution — while Posoco keeps
/// owning the run loop, journal, mailbox, and session commit.
///
/// Vocabulary: **Ports** are for extension authors (default path);
/// **Runtime** is for host builders (`Agent::with_runtime`). The run loop
/// itself is never public.
///|
/// Stable execution context for a single tool effect. `effect_id` is the
/// reducer-allocated identity of the `ExecuteTool` effect — the same id the
/// runtime later receives in `cancel_effects`. Runtimes that propagate
/// cancellation MUST key their in-flight execution state (e.g. an
/// `AbortController`) by `effect_id`, not by execution order or call content.
pub(all) struct EffectContext {
effect_id : @kernel.EffectId
owner : @kernel.OwnerId
call_id : @kernel.CallId
tool_name : @kernel.ToolName
arguments : Json
}
///|
/// The public effect-execution contract. Mirrors the internal boundary 1:1;
/// Posoco adapts it mechanically (a thin shim packs/unpacks `EffectContext`),
/// so this trait never grows semantics the internal loop does not have.
///
/// Implementors usually wrap `PortRuntime` and override only the methods
/// they need (typically `execute_tool` + `cancel_effects`); see
/// `docs/RUNTIME.md`.
pub(open) trait Runtime {
/// Execute a `CallModel` effect. `on_chunk` is the live streaming sink;
/// `None` means the product did not enable streaming. `scope` carries the
/// run/session identity; `scope.effect_id` is always `Some` here and hosts
/// that propagate cancellation MUST key in-flight model calls by it,
/// mirroring `EffectContext.effect_id` on the tool side.
async fn call_model(
Self,
scope : @kernel.InvocationScope,
messages : Array[@kernel.Message],
tool_definitions : Array[@kernel.ToolDef],
call_options : Json,
on_chunk : ((Json) -> Unit)?,
) -> Result[@kernel.ModelCallResult, @kernel.ModelFailure]
/// Execute a single tool call described by `ctx`. Business errors map to
/// `ToolReportedError`; transport/runtime errors map to `RuntimeFailure`.
/// A well-formed runtime never returns `NotExecuted` (that is a
/// reducer-side category).
async fn execute_tool(Self, ctx : EffectContext) -> @kernel.ToolOutcome
/// Best-effort cancel. Fire-and-forget from the loop's perspective; the
/// returned dispositions are recorded for observability. MUST be
/// idempotent per effect id: cancelling an already-settled effect reports
/// `AlreadySettled`, never raises.
async fn cancel_effects(
Self,
effect_ids : Array[@kernel.EffectId],
reason : @kernel.CancelReason,
) -> Array[(@kernel.EffectId, @kernel.CancelDisposition)]
/// Compact the conversation. Forwarded to the model side; `trigger` lets
/// the implementation pick a strategy (Auto = cheap elision, Manual =
/// thorough). `scope.effect_id` is always `None` here: compact is
/// host-driven, not a reducer-allocated effect.
async fn compact(
Self,
scope : @kernel.InvocationScope,
messages : Array[@kernel.Message],
call_options : Json,
trigger : @kernel.CompactTrigger,
) -> Result[@kernel.CompactResult, @kernel.ModelFailure]
}