README

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 = _
}

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.

#
Lifecycle

pub(open) trait Lifecycle {
async fn on_shutdown(Self) -> Unit
}

Lifecycle: resource cleanup hook. Called once on agent shutdown to release connections, flush buffers, etc. async to allow IO during cleanup.

#
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 = _
}

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

  • Agent-level (on_event): 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.

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.

#
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.

#
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]
}

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

#
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

#
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