Sign in

    colmugx/posoco/port does not have a README file

    CommandPort

    pub(open) trait CommandPort {
    fn commands(Self) -> Array[CommandDef]
    async fn invoke(Self, id : String, args : Json) -> CommandOutcome raise
    CommandError

    }

    User-initiated, enumerable, side-effecting commands. Distinct from ToolProvider (LLM-initiated, results re-enter session). Both panel TUIs and slash command lines consume this without knowing command semantics. pub(open) so any ext can declare commands.

    Extension

    pub(open) trait Extension {
    fn extension_id(Self) -> String
    fn manifest(Self) -> ExtensionManifest
    }

    Hook

    pub(open) trait Hook {
    fn before_model(Self, messages : Array[
    Message
    ]) -> Array[
    Message
    ] raise HookAbort = _
    async fn before_tool(Self, call :
    ToolCall
    ) -> ToolHookDecision = _
    fn on_post_event(Self, stage : HookStage) -> Unit = _
    fn on_post_event_at(Self, scope :
    EventScope
    ?, stage : HookStage) -> Unit = _
    }

    Pipeline interception points. Registration is unified: an extension puts self (or any implementor) into the manifest's single hooks array and overrides the methods for the points it cares about.

    • before_model: invoked before the model is called. Can rewrite messages or raise HookAbort. Multiple hooks form a chained rewrite pipeline — each hook sees the previous hook's output.
    • before_tool: invoked before every tool execution. Approve (with optional rewrite), Defer, or Reject. First non-Approve decision wins.
    • on_post_event: invoked after every effect completes. Read-only and non-raising — all registered hooks run in registration order; a handler cannot fail in a typed way, and a contract violation aborts loudly by design. For interception or abortion use before_model / before_tool.
    • on_post_event_at: scoped variant of on_post_event, additionally carrying the EventScope attribution of the run that committed the effect. The core dispatches ONLY this variant; its default delegates to on_post_event, so pre-scope hooks keep working unchanged.

    Lifecycle

    pub(open) trait Lifecycle {
    fn on_compose(Self, ctx : CompositionView) -> Unit raise
    CompositionError
    = _
    async fn on_start(Self) -> Unit = _
    async fn on_shutdown(Self) -> Unit
    }

    Lifecycle: the extension-facing agent lifecycle, three phases.

    • on_compose: called once, synchronously, after every composition gate has passed (catalog atomic snapshot, model cardinality, collision checks) and before the Agent is ready. This is where the composed capabilities declared in the manifest's requires are delivered via CompositionView. Sync on purpose: ModelPort::chat and UiPort::request are async, so a sync composition point cannot use what it wires — the type system enforces "wire, don't act". Raise CompositionError::ExtensionComposeFailed to fail the composition loudly; no partial Agent is produced.
    • on_start: called once per Agent lifetime, inside the first run_turn (an async context), before the first TurnStarted is projected. This is the legitimate birth point for loading persisted state or spawning background loops — construction time has no async root. Not re-fired after Suspended resumes. Like other async slots the body may raise; the Agent wraps a raised error loudly and the first turn fails before any terminal event is emitted.
    • on_shutdown: resource cleanup hook, called once on agent shutdown in reverse registration order; cleanup failure propagates (loud).

    All methods have defaults: an extension overrides only the phases it cares about, and adding a phase is non-breaking (same pattern as Hook).

    MemoryPort

    MemoryPort: durable cross-session memory store/search/delete. Distinct from SessionStore (conversation history) — memory holds durable decisions/knowledge (e.g. brain.md integration).

    ModelPort

    ModelPort: LLM calling interface. Decides HOW to compact; posoco decides WHEN. chat returns ModelCallResult with processed_messages.

    Both model-side entry points receive an InvocationScope identifying the session/run the call serves (chat additionally carries the reducer- allocated effect id). Adapters that key behaviour on session identity — continuity, telemetry, cost attribution, per-session policy — read it from scope; scope-agnostic adapters ignore it. Treat it as read-only.

    Observer

    pub(open) trait Observer {
    fn on_event(Self, event :
    TurnEvent
    ) -> Unit = _
    fn on_event_at(Self, scope :
    EventScope
    ?, event :
    TurnEvent
    ) -> Unit = _
    }

    Observer: read-only event listener. One event channel:

    • Agent-level (on_event / on_event_at): TurnStarted / model response / tool pending and result / TurnCompleted / TurnFailed. Agent projects these directly from committed runtime events; it never reconstructs them by scanning a terminal transcript. on_event_at additionally carries the EventScope attribution (session/run/turn); the core dispatches only the scoped variant and its default delegates here.

    The channel is read-only — never blocks or mutates. For interception or abortion, implement Hook::before_model / Hook::before_tool (they carry block capability via HookAbort / ToolHookDecision); for real-time per-effect telemetry implement Hook::on_post_event — the former effect-level observer channel was folded into Hook (D5/BR-7).

    SessionStore

    pub(open) trait SessionStore {
    async fn load(Self, id : String) ->
    Session
    raise
    SessionError

    async fn save(Self, id : String, session :
    Session
    ) -> Unit raise
    SessionError

    }

    SessionStore: persist and load conversation sessions by id.

    Methods use the raise style (matching ModelPort/CommandPort) instead of returning Result. This eliminates the Result-plus-async redundancy and lets js-target adapters (e.g. JsonlSessionStore) implement the trait directly instead of going through load_async/save_async side-paths.

    SystemPromptContributor

    pub(open) trait SystemPromptContributor {
    fn system_prompt(Self) -> String
    }

    Returns a section of the system prompt. Empty strings are skipped.

    ToolProvider

    pub(open) trait ToolProvider {
    fn list_tools(Self) -> Array[
    ToolDef
    ]
    async fn execute(Self, name : String, call :
    ToolCall
    ) ->
    ToolOutcome
    raise
    RuntimeError

    }

    ToolProvider: declare tools via list_tools(), execute via execute().

    R3 M3.7: signatures use canonical kernel types directly. ToolDef carries owner + policy at the catalog level; list_tools returns the kernel ToolDef minus those (they are filled in by the catalog builder, since they depend on the agent's tool-routing configuration, not on the tool itself). execute returns ToolOutcome so business errors, runtime errors, and not-executed cases are all distinguishable without a boolean.

    UiPort

    pub(open) trait UiPort {
    fn ui_descriptor(Self) -> UiDescriptor
    fn render(Self, intent : UiRender) -> Unit
    async fn request(Self, req : UiRequest) -> UiResponse raise
    UiError

    }

    The UI extension contract. Implemented by host packages (cetas-js, cetas-native) or testkit.

    HookAbort

    pub(all) suberror HookAbort {
    Aborted(reason~ : String)
    }

    Raised by Hook::before_model to stop the run before the model is called.
    impl Show for HookAbort

    AutocompleteItem

    pub(all) struct AutocompleteItem {
    label : String
    insert_text : String
    detail : String?
    kind : String
    } derive(
    Debug
    )

    AutocompleteSource

    pub(all) struct AutocompleteSource {
    trigger : String
    kind : String
    fetch : (String) -> Array[AutocompleteItem]
    } derive(
    Debug
    )

    A source of autocomplete suggestions tied to a trigger character.

    Capability

    pub(all) enum Capability {
    Model
    Ui
    } derive(Eq,
    Debug
    )

    A composed capability an extension declares it consumes. Declaring is what populates the CompositionView delivered at Lifecycle::on_compose — the view gates on this declaration, so an undeclared capability reads as None. Extensible by adding variants; existing manifests are unaffected.

    CommandDef

    pub(all) struct CommandDef {
    id : String
    label : String
    description : String
    category : String
    ctype : CommandType
    params : Array[CommandParam]
    aliases : Array[String]
    shortcut : String?
    icon : String?
    visible : Bool
    metadata : Json?
    } derive(Eq,
    Debug
    )

    Command declaration. params is the source of truth for argument rendering (panel form) and parsing (slash). Redundant fields (aliases/shortcut/icon/ visible/metadata) are deliberate "escape hatches" for ext authors to use without changing the trait — meta does not define their semantics.

    CommandOutcome

    pub(all) enum CommandOutcome {
    Success(feedback~ : String, structured~ : Json?, ui_hint~ : UiHint?)
    Failure(reason~ : String)
    NeedsInput(prompt~ : String)
    } derive(Eq,
    Debug
    )

    Outcome of invoking a command. feedback is for slash (print directly); structured + ui_hint are for panel (deep render + UI action). NeedsInput supports multi-step commands.

    CommandParam

    pub(all) struct CommandParam {
    name : String
    label : String
    description : String
    ptype : ParamType
    required : Bool
    default : Json?
    choices : Array[String]?
    positional : Bool
    } derive(Eq,
    Debug
    )

    A single command parameter. Explicit and human-facing, in contrast to ToolDef.input_schema (which is JSON Schema for LLM consumption).

    CommandType

    pub(all) enum CommandType {
    Action
    Toggle
    Select
    Input
    MultiStep
    } derive(Eq,
    Debug
    )

    Semantic command type. Determines how a panel renders the control (button / toggle / dropdown / input / multi-step form) and how a slash parser expects arguments.

    CompositionView

    pub struct CompositionView {
    // private fields
    }

    CompositionView: the curated read-only view of composed capabilities delivered to Lifecycle::on_compose after every composition gate has passed. This is the consumption direction of the manifest contract — an extension declares requires and the corresponding getter reads Some; undeclared capabilities read None.

    Curation discipline: the view only ever carries capabilities whose use cannot bypass governance. model calls still carry InvocationScope attribution; ui has no governance surface. The view NEVER exposes tool invocation (would bypass Hook::before_tool), session stores (state ownership), or observer-event emission (core-owned). Fields are private with getters so new capabilities can be added without breaking extensions that destructure or construct views in tests.

    CompositionView::model

    The single composed ModelPort — present only when the extension declared Capability::Model in its manifest's requires. This is the unique outlet that passed the composition cardinality gate, source-agnostic (the extension cannot tell a plain provider from a routing meta-extension, and must not).

    CompositionView::resolve

    fn CompositionView::resolve(requires~ : Array[Capability], model~ : &ModelPort, ui~ : &UiPort) -> CompositionView

    Gate a view on a manifest's declared requires: a declared capability is populated from the composed ports, an undeclared one reads None. Called by the composition after every gate has passed, so model and ui are guaranteed to be the final composed values.

    CompositionView::ui

    The composed UiPort (single contributor, CompositeUiPort, or NoopUiPort) — present only when the extension declared Capability::Ui. Human-interaction requests (UiRequest::Confirm / Select / Input) go here; do not occupy the ui manifest slot just to consume the composed UI.

    ExtensionManifest

    pub(all) struct ExtensionManifest {
    id : String
    models : Array[&ModelPort]
    tools : Array[&ToolProvider]
    sessions : Array[&SessionStore]
    observers : Array[&Observer]
    hooks : Array[&Hook]
    memory : Array[&MemoryPort]
    lifecycle : Array[&Lifecycle]
    commands : Array[&CommandPort]
    ui : Array[&UiPort]
    prompt_contributors : Array[&SystemPromptContributor]
    requires : Array[Capability]
    }

    Self-reported port contributions. All fields default to empty arrays; multi-trait extensions put self under each applicable field.

    requires is the consumption direction: the extension declares which composed capabilities it wants delivered at Lifecycle::on_compose. The declaration is load-bearing (the view gates on it) and auditable (composition can answer "which extensions declared model access").

    ExtensionManifest::empty

    fn ExtensionManifest::empty(id~ : String) -> ExtensionManifest

    Construct an empty manifest with only the id set. Fields can then be assigned individually.

    HookStage

    pub(all) enum HookStage {
    ModelCompleted(completion~ :
    Completion
    )
    ToolCompleted(call~ :
    ToolCall
    , outcome~ :
    ToolOutcome
    )
    ToolFailed(call~ :
    ToolCall
    , reason~ : String)
    ModelFailed(failure~ :
    ModelFailure
    )
    } derive(
    Debug
    )

    Describes what just completed, delivered after the effect is committed.
    impl Show for HookStage

    ParamType

    pub(all) enum ParamType {
    Str
    Int
    Bool
    Strs
    } derive(Eq,
    Debug
    )

    Value type of a command parameter. Human-friendly (unlike JSON Schema which targets LLMs). Panel/slash consumers render and parse directly from this.

    ProviderConfig

    pub(all) struct ProviderConfig {
    reasoning_effort_values : Array[String]
    } derive(Eq,
    Debug
    )

    ProviderConfig: modelport's self-description of accepted config values. A String array (not typed enum) because each provider has its own vocabulary for e.g. reasoning_effort values.

    ProviderConfig::ProviderConfig

    fn ProviderConfig::ProviderConfig(reasoning_effort_values~ : Array[String]) -> ProviderConfig

    ProviderConfig::accepts_reasoning_effort

    fn ProviderConfig::accepts_reasoning_effort(self : ProviderConfig, value : String) -> Bool

    Check if the given reasoning_effort value is accepted.

    ProviderConfig::empty

    Empty config: no reasoning_effort values accepted.

    ToolHookDecision

    pub(all) enum ToolHookDecision {
    Approve(call~ :
    ToolCall
    )
    Defer(reason~ : String)
    Reject(reason~ : String)
    } derive(
    Debug
    )

    The hook's decision for a pending tool call.

    UiBody

    pub(all) enum UiBody {
    Text(String)
    Lines(Array[String])
    KeyValue(Array[(String, String)])
    Progress(Double)
    Markdown(String)
    } derive(
    Debug
    )

    Structured payload a render intent carries. Host decides layout and color.
    impl Show for UiBody

    UiBody::to_string

    fn UiBody::to_string(self : UiBody) -> String

    UiDescriptor

    pub(all) struct UiDescriptor {
    autocomplete_sources : Array[AutocompleteSource]
    } derive(
    Debug
    )

    Collected at composition time: the host aggregates every UiPort's descriptors to know which autocomplete triggers to honor.

    UiDescriptor::empty

    fn UiDescriptor::empty() -> UiDescriptor

    Empty descriptor used by UIs that contribute no autocomplete sources.

    UiHint

    pub(all) enum UiHint {
    RefreshModelList
    RefreshSettings
    ClosePanel
    Toast
    None
    } derive(Eq,
    Debug
    )

    Hint to a panel consumer about what UI action to take after invoke. Slash consumers ignore this. Fixed enum + None fallback; ext long-tail needs go through CommandDef.metadata.

    UiRender

    pub(all) struct UiRender {
    slot : UiSlot
    key : String
    title : String?
    body : UiBody
    ttl_ms : Int?
    } derive(
    Debug
    )

    One render intent. Same key replaces a prior intent in the same UiSlot.
    impl Show for UiRender

    UiRequest

    pub(all) enum UiRequest {
    Input(prompt~ : String, default~ : String?)
    Confirm(prompt~ : String)
    Select(prompt~ : String, options~ : Array[String], default~ : Int?)
    } derive(
    Debug
    )

    User interaction primitives. Hosts that lack a variant raise UiError::Unsupported.
    impl Show for UiRequest

    UiResponse

    pub(all) enum UiResponse {
    Text(String)
    Yes
    No
    Selected(Int)
    Cancelled
    } derive(Eq,
    Debug
    )

    Response to a UiRequest. Cancelled = user dismissed; no UI = UiError::Unsupported.
    impl Show for UiResponse

    UiSlot

    pub(all) enum UiSlot {
    Status
    Notice
    Widget
    } derive(Eq,
    Debug
    )

    Where a render intent lands: Status (single-line bar), Notice (transient toast), Widget (persistent region).
    impl Show for UiSlot

    UiSlot::to_string

    fn UiSlot::to_string(self : UiSlot) -> String