Sign in

    posoco

    Zero-dependency LLM Agent framework with hexagonal (ports-and-adapters) architecture. Defines 7 traits + Agent loop.

    llm
    agent
    framework
    ports-and-adapters
    ai-runtime
    Download zip
    Author
    Version
    0.4.1
    License
    Apache-2.0
    Last updated
    3 months ago
    Downloads
    3K

    #colmugx/posoco

    零依赖的 LLM Agent 框架 — 基于 Ports 架构。

    Posoco 定义了 8 个 trait 接口 + 一个 Agent::run_turn 循环。所有非核心功能通过扩展包提供,核心零运行时依赖。


    #快速一览

    // 1. 实现 trait
    let model = MyModelPort::{ .. }
    let tools = MyToolProvider::{ .. }
    let runtime = MyToolRuntime::{ .. }
    let store = MySessionStore::{ .. }
    let observer = MyObserver::{ .. }

    // 2. 构建 Agent(9 个参数)
    let agent = Agent::new(model, tools, runtime, store, observer,
    NoopCompressor::{ }, None, None, AgentConfig::{ max_tool_rounds: 10, .. })

    // 3. 运行
    let result = agent.run_turn(user_msg("hello"), "session_1")


    #核心概念

    #8 个 Trait(Port 接口)

    Trait职责典型实现
    ModelPortLLM 聊天补全OpenAIModelPort(HTTP → OpenAI/Anthropic)
    ToolProvider列出可用工具MCPToolProvider(MCP 协议发现)
    ToolRuntime执行工具调用WasmToolRuntime(沙箱执行)
    SessionStore持久化会话SQLiteSessionStore
    Observer实时事件订阅EventBusObserver(事件总线)
    Compressor 可选上下文压缩AdaptiveCompressor(自适应压缩策略)
    PipelineHook 可选流水线拦截RTKHook(通过 RTK 重写 shell 命令)
    MemoryPort 可选长期记忆存储向量数据库插件

    #架构分层

    Layer 3 — Agent(程序 / 组合) Agent struct 持有 &Trait 引用(无泛型) run_turn() 编排整个循环 Layer 2 — Interpreters(运行时 / 注入) | 扩展包实现 trait(MCP、RTK……) Layer 1 — Traits(效应接口 / 纯声明) 8 个 trait + 共享类型(Message、ToolCall、Session……)

    #Agent.run_turn 循环

    run_turn(input, session_id) │ ├─ PipelineHook.on_stage(BeforeTurn) ├─ Observer.on_event(TurnStarted) ├─ SessionStore.load(session_id) ├─ Compressor.compress(messages) ← 每轮 LLM 前执行 ├─ PipelineHook.on_stage(BeforeModel) ├─ ModelPort.chat(messages, tools) ← LLM 调用 ├─ PipelineHook.on_stage(AfterModel) │ ├─ [工具循环] max_tool_rounds 上限 │ ├─ PipelineHook.on_stage(BeforeTool) ← ✦ RTKHook 在此拦截 │ ├─ ToolRuntime.execute(tool_call) │ ├─ PipelineHook.on_stage(AfterTool) │ └─ Observer.on_event(ToolCallResult) │ ├─ SessionStore.save(session) ├─ Observer.on_event(TurnCompleted) └─ PipelineHook.on_stage(AfterTurn)


    #安装

    // moon.mod.json { "deps": { "colmugx/posoco": { "path": "../posoco" } } }

    // moon.pkg
    import { "colmugx/posoco" }


    #API 参考

    #Agent

    /// Agent 配置
    pub(all) struct AgentConfig {
    max_tool_rounds : Int // 工具循环上限(默认 10)
    temperature : Double? // LLM 温度
    max_output_tokens : Int? // 最大输出 Token
    tool_choice : ToolChoice? // 工具选择策略
    thinking : Bool? // 是否启用思考
    model_context_window : Int? // 模型上下文窗口(传给 Compressor)
    }

    /// Agent — 中央运行时
    pub(all) struct Agent {
    model_port : &ModelPort
    tool_provider : &ToolProvider
    tool_runtime : &ToolRuntime
    session_store : &SessionStore
    observer : &Observer
    compressor : &Compressor
    pipeline_hook : &PipelineHook?
    memory_port : &MemoryPort?
    config : AgentConfig
    }

    /// 构造 Agent(传入 9 个 trait 引用)
    pub fn Agent::new(
    model_port : &ModelPort,
    tool_provider : &ToolProvider,
    tool_runtime : &ToolRuntime,
    session_store : &SessionStore,
    observer : &Observer,
    compressor : &Compressor,
    pipeline_hook : &PipelineHook?,
    memory_port : &MemoryPort?,
    config : AgentConfig,
    ) -> Agent

    /// 运行一轮(错误通过 raise 传播)
    pub fn Agent::run_turn(self : Agent, input : Message, session_id : String) -> TurnResult raise AgentError

    #核心类型

    ///|
    /// 角色
    pub(all) enum Role {
    System
    User
    Assistant
    Tool
    }

    ///|
    /// 消息内容
    pub(all) enum Content {
    Text(String)
    Image(String, String)
    }

    ///|
    /// 工具调用
    pub(all) struct ToolCall {
    id : String
    name : String
    arguments : Json
    }

    ///|
    /// 消息
    pub(all) struct Message {
    role : Role
    content : Array[Content]
    tool_calls : Array[ToolCall]
    tool_call_id : String?
    name : String?
    }

    ///|
    /// 工具定义
    pub(all) struct ToolDef {
    name : String
    description : String
    input_schema : Json
    }

    ///|
    /// 工具执行结果
    pub(all) struct ToolResult {
    content : String
    structured : Json?
    is_error : Bool
    }

    ///|
    /// 模型响应
    pub(all) struct ModelResponse {
    message : Message
    tool_calls : Array[ToolCall]
    finish_reason : FinishReason
    usage : Usage?
    reasoning_summary : String?
    }

    ///|
    /// 会话
    pub(all) struct Session {
    messages : Array[Message]
    metadata : Map[String, Json]
    }

    ///|
    /// 一轮结果
    pub(all) struct TurnResult {
    message : Message
    tool_results : Array[ToolResult]
    final_session_id : String
    }

    #8 个 Trait 定义

    ///|
    /// LLM 模型端口
    pub(open) trait ModelPort {
    fn chat(
    Self,
    messages : Array[Message],
    tools : Array[ToolDef],
    options : ChatOptions,
    ) -> ModelResponse raise ModelError
    }

    ///|
    /// 工具发现
    pub(open) trait ToolProvider {
    fn list_tools(Self) -> Array[ToolDef]
    }

    ///|
    /// 工具执行
    pub(open) trait ToolRuntime {
    fn execute(Self, name : String, call : ToolCall) -> Result[
    ToolResult,
    RuntimeError,
    ]
    }

    ///|
    /// 会话存储
    pub(open) trait SessionStore {
    fn load(Self, id : String) -> Result[Session, SessionError]
    fn save(Self, id : String, session : Session) -> Result[Unit, SessionError]
    }

    ///|
    /// 事件观察者(只读,永不阻塞)
    pub(open) trait Observer {
    fn on_event(Self, event : TurnEvent) -> Unit
    }

    ///|
    /// 上下文压缩器(纯函数,返回指令)
    pub(open) trait Compressor {
    fn compress(Self, messages : Array[Message], ctx : CompressContext) -> CompressAction
    }

    ///|
    /// 流水线钩子(可变更 Stage,返回 Result,可中止)
    pub(open) trait PipelineHook {
    fn on_stage(Self, stage : Stage) -> Result[Stage, HookError]
    }

    ///|
    /// 长期记忆端口(可选)
    pub(open) trait MemoryPort {
    fn store(Self, entry : MemoryEntry) -> String raise MemoryError
    fn search(Self, query : MemoryQuery) -> Array[MemoryEntry] raise MemoryError
    fn delete(Self, id : String) -> Unit raise MemoryError
    }

    #PipelineStage(9 个阶段)

    ///|
    pub(all) enum Stage {
    BeforeTurn // 轮次开始
    BeforeModel(Array[Message]) // LLM 调用前
    AfterModel(ModelResponse) // LLM 响应后
    ModelError(String) // LLM 出错(错误信息)
    BeforeTool(ToolCall) // 工具执行前 ← ✦ RTKHook 目标
    AfterTool(ToolCall, ToolResult) // 工具执行后
    ToolError(ToolCall, RuntimeError) // 工具出错
    AfterTurn(TurnResult) // 轮次结束
    TurnError(AgentError) // 轮次出错
    }

    #TurnEvent(9 个变体)

    pub(all) enum TurnEvent {
    TurnStarted
    ToolCallPending(ToolCall)
    ToolCallResult(call~ , result~ , is_error~ : Bool)
    ModelResponseReceived(message~ , usage~ , reasoning_summary~ )
    SessionRedirect(from~ , to~ , messages_before~ , messages_after~ )
    TurnCompleted
    TurnFailed(String)
    ToolCallDeferred(call~ , reason~ : String) // 工具被延迟执行
    Custom(source~ , label~ , data~ : Json) // 自定义事件
    }

    #内置实现

    /// 无操作压缩器 — 始终返回 None
    pub(all) struct NoopCompressor {}

    /// 无操作钩子 — 全部放行
    pub(all) struct NoopHook {}

    /// 钩子链 — 按顺序执行多个 PipelineHook
    pub(all) struct HookChain { hooks : Array[&PipelineHook] }

    /// 组合多个 ToolProvider
    pub(all) struct CompositeToolProvider { providers : Array[&ToolProvider] }

    /// 动态注册工具
    pub(all) struct ToolRegistry { mut tools : Map[String, ToolDef] }

    /// 无操作记忆端口
    pub(all) struct NoopMemoryPort {}

    /// 构建工具结果消息
    pub fn build_tool_result_message(call : ToolCall, result : ToolResult) -> Message

    /// 构建工具错误消息
    pub fn build_error_message(call : ToolCall, error : RuntimeError) -> Message


    #错误模型

    工具失败是非致命的 — 错误变成消息传给 LLM。只有模型错误会终止本轮。

    ///|
    /// Agent 顶层错误(使用 suberror 而非 enum)
    pub(all) suberror AgentError {
    Model(String) // LLM 错误 → 终止本轮
    Session(SessionError) // 会话错误 → 终止本轮
    Runtime(RuntimeError) // 运行时错误 → 非致命,转为消息
    ToolLoopExceeded // 工具循环超限 → 终止本轮
    HookAborted(String) // Hook 中止 → 终止本轮
    }

    ///|
    /// Hook 错误(可中止或延迟)
    pub(all) enum HookError {
    Aborted(String) // 终止轮次
    Deferred(String) // 延迟执行,通知 LLM
    }

    ///|
    /// ModelError、SessionError、RuntimeError、MemoryError 都使用 suberror
    pub(all) suberror ModelError {
    RequestBuild(String)
    Transport(String)
    ResponseParse(String)
    }

    错误通过 raise 传播,而非 Result 返回:

    // ModelPort::chat 返回 ModelResponse raise ModelError
    // Agent::run_turn 返回 TurnResult raise AgentError
    pub fn Agent::run_turn(self : Agent, input : Message, session_id : String) -> TurnResult raise AgentError


    #扩展机制

    Posoco 所有非核心功能通过扩展包提供。核心零运行时依赖扩展。

    扩展类型Trait示例包
    ProviderModelPortposoco-ext-openai
    Tool DiscoveryToolProviderposoco-ext-mcp
    Tool ExecutionToolRuntimeposoco-ext-wasm
    SessionSessionStoreposoco-ext-sqlite
    ObserverObserverposoco-ext-vein
    CompressorCompressorposoco-ext-adaptive
    HookPipelineHookposoco-ext-rtk
    MemoryMemoryPortposoco-ext-vector

    完整扩展指南 → docs/EXTENSIONS.md


    #许可证

    Apache-2.0

    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.

    Compressor

    pub(open) trait Compressor {
    fn compress(Self, messages : Array[Message], ctx : CompressContext) -> CompressAction
    }

    Compressor: pure function that decides how to shrink the message context. Returns instructions (None/Replace/NewThread) — never saves sessions or emits events directly; the Agent and Observer handle side effects.

    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

    pub(open) trait MemoryPort {
    fn store(Self, entry : MemoryEntry) -> String raise MemoryError
    fn search(Self, query : MemoryQuery) -> Array[MemoryEntry] raise MemoryError
    fn delete(Self, id : String) -> Unit raise MemoryError
    }

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

    ModelPort

    pub(open) trait ModelPort {
    async fn chat(Self, messages : Array[Message], tools : Array[ToolDef], options : ChatOptions) -> ModelResponse raise ModelError
    async fn chat_streaming(Self, messages : Array[Message], tools : Array[ToolDef], options : ChatOptions, on_chunk : (StreamChunk) -> Unit) -> ModelResponse raise ModelError = _
    }

    ModelPort: the LLM calling interface. Every ext-deepseek / ext-openai / ext-anthropic implements this. chat is the primary call; chat_streaming has a default that ignores on_chunk and falls back to chat().

    Observer

    pub(open) trait Observer {
    fn on_event(Self, event : TurnEvent) -> Unit
    }

    Observer: read-only event listener. Never blocks or mutates — use PipelineHook for interception/abortion. Receives TurnEvent for every significant turn lifecycle event.

    PipelineHook

    pub(open) trait PipelineHook {
    async fn on_stage(Self, stage : Stage) -> Result[Stage, HookError]
    }

    Pipeline hook: observe or mutate stages before/after each phase. Before* stages may rewrite or abort; After*/*Error stages run under finally semantics (always invoked) in reverse hook order.

    SessionStore

    pub(open) trait SessionStore {
    fn load(Self, id : String) -> Result[Session, SessionError]
    fn save(Self, id : String, session : Session) -> Result[Unit, SessionError]
    }

    SessionStore: persist and load conversation sessions by id.

    ToolProvider

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

    Unified tool interface: declare tools via list_tools(), execute via execute().

    AgentError

    pub(all) suberror AgentError {
    Model(String)
    Session(SessionError)
    Runtime(RuntimeError)
    ToolLoopExceeded
    HookAborted(String)
    } derive(
    Debug
    )

    impl Show for AgentError

    CommandError

    pub(all) suberror CommandError {
    NotFound(String)
    InvalidArgs(reason~ : String)
    ExecutionFailed(String)
    } derive(
    Debug
    )

    Errors raised by CommandPort.invoke: unknown command id, invalid args, or execution failure.

    CompositionError

    pub(all) suberror CompositionError {
    ToolCollision(String, String)
    EmptyPort(String)
    } derive(
    Debug
    )

    Composition errors raised during Agent::new (tool collisions, empty required ports).

    MemoryError

    pub(all) suberror MemoryError {
    Store(String)
    Search(String)
    Delete(String)
    } derive(
    Debug
    )

    impl Show for MemoryError

    ModelError

    pub(all) suberror ModelError {
    RequestBuild(String)
    Transport(String)
    ResponseParse(String)
    } derive(
    Debug
    )

    impl Show for ModelError

    RuntimeError

    pub(all) suberror RuntimeError {
    UnknownTool(String)
    InvocationFailed(String)
    Transport(String)
    } derive(
    Debug
    )

    SessionError

    pub(all) suberror SessionError {
    Load(String)
    Save(String)
    } derive(
    Debug
    )

    Agent

    pub(all) struct Agent {
    model_port : &ModelPort
    tool_routing : ToolRouting
    pipeline_hook : &PipelineHook?
    compressors : Array[&Compressor]
    observers : Array[&Observer]
    memories : Array[&MemoryPort]
    sessions : Array[&SessionStore]
    lifecycle : &Lifecycle?
    config : AgentConfig
    }

    Agent — array-based composition: tools routed by name, hooks chained, observers fan-out, compressors first-wins, sessions write-all/read-first.

    Agent::new

    fn Agent::new(model_port : &ModelPort, tools~ : Array[&ToolProvider], hooks~ : Array[&PipelineHook], memory~ : Array[&MemoryPort], compressors~ : Array[&Compressor], observers~ : Array[&Observer], sessions~ : Array[&SessionStore], lifecycle~ : &Lifecycle?, config~ : AgentConfig) -> Agent

    Construct an Agent with array-based composition.

    • tools: routed by tool name (last-wins on collision)
    • hooks: chained in order (first runs first)
    • memory: fan-out store/search
    • compressors: first non-None wins
    • observers: all receive every event
    • sessions: write-all, read-first

    Agent::run_turn

    async fn Agent::run_turn(self : Agent, input : Message, session_id : String) -> TurnResult raise AgentError

    Run one full agent turn. Tool calls execute in parallel via @async.all.

    Agent::shutdown

    async fn Agent::shutdown(self : Agent) -> Unit

    Agent::tool_collisions

    fn Agent::tool_collisions(self : Agent) -> Array[String]

    AgentConfig

    pub(all) struct AgentConfig {
    max_tool_rounds : Int
    temperature : Double?
    max_output_tokens : Int?
    tool_choice : ToolChoice?
    reasoning_effort : ReasoningEffort?
    model_context_window : Int?
    }

    AgentConfig::to_chat_options

    fn AgentConfig::to_chat_options(self : AgentConfig) -> ChatOptions

    ChatOptions

    pub(all) struct ChatOptions {
    temperature : Double?
    max_output_tokens : Int?
    tool_choice : ToolChoice?
    reasoning_effort : ReasoningEffort?
    } derive(Eq,
    Debug
    )

    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.

    CompositeToolProvider

    pub(all) struct CompositeToolProvider {
    providers : Array[&ToolProvider]
    }

    Internal: merges multiple providers' tool lists. Agent routes via ToolRouting instead.

    CompressAction

    pub(all) enum CompressAction {
    None
    Replace(Array[Message])
    NewThread(messages~ : Array[Message], new_session_id~ : String, messages_before~ : Int, messages_after~ : Int)
    } derive(Eq,
    Debug
    )

    Action returned by a Compressor. None = no compression; Replace = substitute the message list; NewThread = start a fresh session with a summary carried over.

    CompressContext

    pub(all) struct CompressContext {
    session_id : String
    model_context_window : Int?
    } derive(Eq,
    Debug
    )

    Context passed to Compressor::compress.

    Content

    pub(all) enum Content {
    Text(String)
    Image(String, String)
    } derive(Eq,
    Debug
    )

    FinishReason

    pub(all) enum FinishReason {
    Stop
    Length
    ToolCalls
    Other(String)
    } derive(Eq,
    Debug
    )

    HookChain

    pub(all) struct HookChain {
    hooks : Array[&PipelineHook]
    }

    Internal: chains multiple hooks in sequence.

    HookError

    pub(all) enum HookError {
    Aborted(String)
    Deferred(reason~ : String)
    } derive(Eq,
    Debug
    )

    impl Show for HookError

    MemoryEntry

    pub(all) struct MemoryEntry {
    id : String
    content : String
    metadata : Map[String, Json]
    score : Double?
    } derive(Eq,
    Debug
    )

    MemoryQuery

    pub(all) struct MemoryQuery {
    query : String
    top_k : Int
    threshold : Double?
    filter : Map[String, Json]
    } derive(Eq,
    Debug
    )

    Message

    pub(all) struct Message {
    role : Role
    content : Array[Content]
    tool_calls : Array[ToolCall]
    tool_call_id : String?
    name : String?
    reasoning_content : String?
    } derive(Eq,
    Debug
    )

    A conversation message. reasoning_content carries provider reasoning (e.g. DeepSeek deepseek-reasoner思维链, OpenAI o-series reasoning content). Providers that don't produce reasoning leave it None. Wire encoders decide whether to resend it in subsequent turns (DeepSeek: must resend for turns with tool_calls; optional otherwise).

    ModelResponse

    pub(all) struct ModelResponse {
    message : Message
    tool_calls : Array[ToolCall]
    finish_reason : FinishReason
    usage : Usage?
    reasoning_content : String?
    } derive(Eq,
    Debug
    )

    NoopCommandPort

    pub(all) struct NoopCommandPort {
    }

    No-op CommandPort: declares no commands, invoke always raises NotFound. Useful as a default or for testing.

    NoopCompressor

    pub(all) struct NoopCompressor {
    }

    NoopHook

    pub(all) struct NoopHook {
    }

    NoopLifecycle

    pub(all) struct NoopLifecycle {
    }

    NoopMemoryPort

    pub(all) struct NoopMemoryPort {
    }

    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.

    ReasoningEffort

    pub(all) enum ReasoningEffort {
    Off
    Minimal
    Low
    Medium
    High
    Maximum
    } derive(Eq,
    Debug
    )

    Reasoning effort requested of the model. Provider-agnostic: each ext maps these to its own effort tiers (e.g. DeepSeek collapses to on/off via model choice; OpenAI/Anthropic have explicit tiers). Maximum means the provider's absolute top tier (Claude ultra, DeepSeek Max, OpenAI high).

    Role

    pub(all) enum Role {
    System
    User
    Assistant
    Tool
    } derive(Eq,
    Debug
    )

    Session

    pub(all) struct Session {
    messages : Array[Message]
    metadata : Map[String, Json]
    } derive(Eq,
    Debug
    )

    Stage

    pub(all) enum Stage {
    BeforeTurn
    BeforeModel(Array[Message])
    AfterModel(ModelResponse)
    ModelError(String)
    BeforeTool(ToolCall)
    AfterTool(ToolCall, ToolResult)
    ToolError(ToolCall, RuntimeError)
    AfterTurn(TurnResult)
    TurnError(AgentError)
    } derive(
    Debug
    )

    Pipeline hook stages. The Before* variants run hooks in forward order (short-circuit on error); the After*/*Error variants run in reverse order (onion semantics) without short-circuiting (finally semantics).

    StreamAccumulator

    pub(all) struct StreamAccumulator {
    text : String
    reasoning : String
    tool_calls : Array[ToolCallBuilder]
    finish_reason : FinishReason
    usage : Usage?
    } derive(
    Debug
    )

    Accumulates StreamChunk events into a ModelResponse.

    StreamAccumulator::new

    StreamAccumulator::push

    fn StreamAccumulator::push(self : StreamAccumulator, chunk : StreamChunk) -> Unit

    StreamAccumulator::to_response

    StreamChunk

    pub(all) enum StreamChunk {
    TextDelta(token~ : String)
    ReasoningDelta(token~ : String)
    ToolCallDelta(index~ : Int, id~ : String?, name~ : String?, arguments_delta~ : String?)
    Usage(Usage)
    Finish(reason~ : FinishReason)
    } derive(Eq,
    Debug
    )

    ToolCall

    pub(all) struct ToolCall {
    id : String
    name : String
    arguments : Json
    } derive(Eq,
    Debug
    )

    ToolCallBuilder

    pub(all) struct ToolCallBuilder {
    id : String
    name : String
    arguments_json : String
    } derive(
    Debug
    )

    ToolCallBuilder::new

    ToolChoice

    pub(all) enum ToolChoice {
    Auto
    NoTool
    Required
    NamedTool(String)
    } derive(Eq,
    Debug
    )

    ToolDef

    pub(all) struct ToolDef {
    name : String
    description : String
    input_schema : Json
    metadata : Json?
    source : String?
    } derive(Eq,
    Debug
    )

    ToolRegistry

    pub(all) struct ToolRegistry {
    tools : Map[String, ToolDef]
    executors : Map[String, (ToolCall) -> ToolResult]
    }

    Dynamic runtime tool registration. Implements ToolProvider.

    ToolRegistry::new

    ToolRegistry::register

    fn ToolRegistry::register(self : ToolRegistry, tool : ToolDef, executor : (ToolCall) -> ToolResult) -> Unit

    ToolRegistry::unregister

    fn ToolRegistry::unregister(self : ToolRegistry, name : String) -> Unit

    ToolResult

    pub(all) struct ToolResult {
    content : String
    structured : Json?
    is_error : Bool
    } derive(Eq,
    Debug
    )

    ToolRouting

    type ToolRouting

    Internal routing table: tool name → owning provider. Built during Agent::new.

    TurnEvent

    pub(all) enum TurnEvent {
    TurnStarted
    ToolCallPending(ToolCall)
    ToolCallResult(call~ : ToolCall, result~ : ToolResult, is_error~ : Bool)
    ModelResponseReceived(message~ : Message, usage~ : Usage?, reasoning_content~ : String?)
    SessionRedirect(from~ : String, to~ : String, messages_before~ : Int, messages_after~ : Int)
    TurnCompleted
    TurnFailed(String)
    ToolCallDeferred(call~ : ToolCall, reason~ : String)
    StreamChunkReceived(chunk~ : StreamChunk)
    Custom(source~ : String, label~ : String, data~ : Json)
    } derive(Eq,
    Debug
    )

    impl Show for TurnEvent

    TurnResult

    pub(all) struct TurnResult {
    message : Message
    tool_results : Array[ToolResult]
    final_session_id : String
    } derive(Eq,
    Debug
    )

    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.

    Usage

    pub(all) struct Usage {
    input_tokens : Int?
    output_tokens : Int?
    total_tokens : Int?
    } derive(Eq,
    Debug
    )

    build_error_message

    fn build_error_message(call : ToolCall, error : RuntimeError) -> Message

    build_tool_result_message

    fn build_tool_result_message(call : ToolCall, result : ToolResult) -> Message

    parse_slash_args

    fn parse_slash_args(text : String, params : Array[CommandParam]) -> Result[Json, String]

    Parse a slash argument string into a JSON object according to CommandParam definitions. Positional params fill in declaration order; non-positional params expect key=value form. Applies defaults for missing optional params. Returns Err with a reason string on type conversion failure or missing required params. Pure function (no IO).

    validate_args

    fn validate_args(def : CommandDef, args : Json) -> Result[Json, String]

    Validate a JSON object of args against a CommandDef's params. Checks required presence; optional params with no value and no default are omitted (not an error). Returns the (possibly defaulted) args on Ok. Pure function (no IO).