Skip to main content

okbot_core/
lib.rs

1//! Domain model and ports (traits) of okbot; the SDK for third-party adapters.
2//!
3//! okbot follows a ports-and-adapters architecture (ADR 0001). This crate holds the types every
4//! other crate speaks and the traits adapters implement. It performs no I/O.
5//!
6//! | Port | Implemented by | Used by |
7//! |---|---|---|
8//! | [`Channel`] | one crate per chat platform | the runtime |
9//! | [`InboundSink`] | the runtime | channels, to deliver events |
10//! | [`AgentBackend`] | agent CLIs, fallback chains | the runtime |
11//! | [`ConversationStore`] | SQLite store | the runtime |
12//! | [`KnowledgeProvider`] | okbase integration | the runtime |
13//! | [`MemoryStore`] | okbase integration | the runtime (chat commands) |
14//! | [`TaskStore`] | okbase integration | the runtime (chat commands, scheduler) |
15//! | [`Renderer`] | Markdown dialect renderer | the runtime |
16//! | [`Clock`] | [`SystemClock`], test clocks | everything that reads time |
17//!
18//! # Writing a channel
19//!
20//! A platform adapter implements [`Channel`]: receive events and hand them to the [`InboundSink`],
21//! send rendered replies. Everything else (policy, agents, knowledge) is the runtime's job.
22//!
23//! ```
24//! use std::sync::Arc;
25//! use okbot_core::{
26//!     async_trait, CancellationToken, Channel, ChannelCaps, ChannelError, ChannelId, InboundSink,
27//!     MessageId, RenderedMessage, ReplyTarget, SentRef,
28//! };
29//!
30//! struct EchoChannel { id: ChannelId, caps: ChannelCaps }
31//!
32//! #[async_trait]
33//! impl Channel for EchoChannel {
34//!     fn id(&self) -> &ChannelId { &self.id }
35//!     fn caps(&self) -> &ChannelCaps { &self.caps }
36//!
37//!     async fn run(&self, _sink: Arc<dyn InboundSink>, shutdown: CancellationToken)
38//!         -> Result<(), ChannelError> {
39//!         // Connect to the platform and call `sink.deliver(event)` for each message.
40//!         shutdown.cancelled().await;
41//!         Ok(())
42//!     }
43//!
44//!     async fn send(&self, to: &ReplyTarget, message: &RenderedMessage)
45//!         -> Result<SentRef, ChannelError> {
46//!         println!("{}", message.text);
47//!         Ok(SentRef { target: to.clone(), message_id: MessageId::new("1") })
48//!     }
49//! }
50//!
51//! let channel = EchoChannel { id: "echo:demo".parse().unwrap(), caps: ChannelCaps::basic() };
52//! assert_eq!(channel.id().to_string(), "echo:demo");
53//! ```
54#![deny(missing_docs)]
55
56mod agent;
57mod caps;
58mod channel;
59mod clock;
60mod id;
61mod knowledge;
62mod memory;
63mod message;
64mod prompt;
65mod render;
66mod secret;
67mod store;
68mod task;
69mod webhook;
70
71pub use agent::{
72    AgentBackend, AgentError, AgentProgress, AgentReply, AgentTurn, McpServer, ProgressFn, Resume,
73    TokenUsage,
74};
75pub use caps::{AgentCaps, ChannelCaps, LengthUnit, MarkdownDialect, ToolPolicy};
76pub use channel::{Channel, ChannelError, CommandInfo, CommandParam, InboundSink, SinkClosed};
77pub use clock::{Clock, SystemClock};
78pub use id::{ChannelId, ConversationKey, IdError, MessageId, RunId, UserId, UserRef};
79pub use knowledge::{
80    KnowledgeContext, KnowledgeError, KnowledgeProvider, Person, Requester, RunGrant, RunInfo,
81};
82pub use memory::{MemoryEntry, MemoryError, MemoryNote, MemoryScope, MemoryStore};
83pub use message::{
84    Attachment, InboundEvent, OutboundMessage, RenderedMessage, ReplyTarget, SentRef,
85};
86pub use prompt::{
87    ButtonStyle, FieldStyle, Form, FormField, MAX_BUTTONS, MAX_FIELDS, Prompt, PromptAnswer,
88    PromptButton,
89};
90pub use render::Renderer;
91pub use secret::{Redactor, Secret};
92pub use store::{
93    AgentRunRecord, ConversationStore, Fire, FireState, MessageRole, NewMessage,
94    PAIRING_MAX_FAILURES, StoreError, StoredMessage,
95};
96pub use task::{
97    NewTask, Repeat, Task, TaskChange, TaskError, TaskKind, TaskStatus, TaskStore, format_time,
98    parse_time, sort_tasks,
99};
100pub use webhook::{WebhookHandler, WebhookRequest, WebhookResponse};
101
102/// Re-exported so adapters use the same cancellation type as the runtime.
103pub use tokio_util::sync::CancellationToken;
104
105/// Re-exported so adapters implement the async traits with the same macro version.
106pub use async_trait::async_trait;
107
108/// A boxed error from an adapter, kept as the `source` of port errors.
109pub type BoxError = Box<dyn std::error::Error + Send + Sync + 'static>;