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//! | [`Peers`] | the peer adapter (ADR 0014) | the runtime (delegation) |
17//! | [`Clock`] | [`SystemClock`], test clocks | everything that reads time |
18//!
19//! # Writing a channel
20//!
21//! A platform adapter implements [`Channel`]: receive events and hand them to the [`InboundSink`],
22//! send rendered replies. Everything else (policy, agents, knowledge) is the runtime's job.
23//!
24//! ```
25//! use std::sync::Arc;
26//! use okbot_core::{
27//! async_trait, CancellationToken, Channel, ChannelCaps, ChannelError, ChannelId, InboundSink,
28//! MessageId, RenderedMessage, ReplyTarget, SentRef,
29//! };
30//!
31//! struct EchoChannel { id: ChannelId, caps: ChannelCaps }
32//!
33//! #[async_trait]
34//! impl Channel for EchoChannel {
35//! fn id(&self) -> &ChannelId { &self.id }
36//! fn caps(&self) -> &ChannelCaps { &self.caps }
37//!
38//! async fn run(&self, _sink: Arc<dyn InboundSink>, shutdown: CancellationToken)
39//! -> Result<(), ChannelError> {
40//! // Connect to the platform and call `sink.deliver(event)` for each message.
41//! shutdown.cancelled().await;
42//! Ok(())
43//! }
44//!
45//! async fn send(&self, to: &ReplyTarget, message: &RenderedMessage)
46//! -> Result<SentRef, ChannelError> {
47//! println!("{}", message.text);
48//! Ok(SentRef { target: to.clone(), message_id: MessageId::new("1") })
49//! }
50//! }
51//!
52//! let channel = EchoChannel { id: "echo:demo".parse().unwrap(), caps: ChannelCaps::basic() };
53//! assert_eq!(channel.id().to_string(), "echo:demo");
54//! ```
55#![deny(missing_docs)]
56
57mod agent;
58mod caps;
59mod channel;
60mod chart;
61mod clock;
62mod id;
63mod knowledge;
64mod memory;
65mod message;
66mod notify;
67mod peer;
68mod prompt;
69mod render;
70mod secret;
71mod store;
72mod task;
73mod webhook;
74
75pub use agent::{
76 AgentBackend, AgentError, AgentProgress, AgentReply, AgentTurn, McpServer, ProgressFn, Resume,
77 TokenUsage,
78};
79pub use caps::{
80 AgentCaps, ChannelCaps, LengthUnit, MarkdownDialect, ReplyMode, ToolPolicy, UiCaps,
81};
82pub use channel::{Channel, ChannelError, CommandInfo, CommandParam, InboundSink, SinkClosed};
83pub use chart::{
84 Chart, ChartError, ChartKind, ChartRenderer, Image, MAX_BARS, MAX_CHART_LABEL, MAX_CHART_VALUE,
85 MAX_COLUMNS, MAX_LINE_POINTS, MAX_SERIES, Series,
86};
87pub use clock::{Clock, SystemClock};
88pub use id::{ChannelId, ConversationKey, IdError, MessageId, RunId, UserId, UserRef};
89pub use knowledge::{
90 KnowledgeContext, KnowledgeError, KnowledgeProvider, Person, Requester, RunGrant, RunInfo,
91};
92pub use memory::{MemoryEntry, MemoryError, MemoryNote, MemoryScope, MemoryStore};
93pub use message::{
94 Attachment, InboundEvent, OutboundMessage, RenderedMessage, ReplyTarget, SentRef,
95};
96pub use notify::NotifyTo;
97pub use peer::{
98 Budget, Delegation, DelegationContext, Origin, PEER_CANCEL_PREFIX, PEER_PLATFORM, PeerCaller,
99 PeerError, PeerInfo, PeerPrompt, PeerTarget, PeerTask, PeerTaskRecord, PeerTaskState, Peers,
100};
101pub use prompt::{
102 ButtonStyle, Card, FieldStyle, Form, FormField, MAX_BUTTONS, MAX_CARDS, MAX_FIELDS,
103 MAX_OPTIONS, MAX_PROMPT_CHARS, Prompt, PromptAnswer, PromptButton, Select, SelectOption, Tone,
104};
105pub use render::Renderer;
106pub use secret::{Redactor, Secret};
107pub use store::{
108 AgentRunRecord, AutomationRun, ConversationStore, Fire, FireState, MessageRole, NewMessage,
109 PAIRING_MAX_FAILURES, RoleGrant, SealedEntry, StoreError, StoredMessage, ToolCallRecord,
110 ToolDecision, Usage, person_key,
111};
112pub use task::{
113 NewTask, Repeat, Task, TaskChange, TaskError, TaskKind, TaskStatus, TaskStore, format_time,
114 parse_time, sort_tasks,
115};
116pub use webhook::{WebhookHandler, WebhookRequest, WebhookResponse};
117
118/// Re-exported so adapters use the same cancellation type as the runtime.
119pub use tokio_util::sync::CancellationToken;
120
121/// Re-exported so adapters implement the async traits with the same macro version.
122pub use async_trait::async_trait;
123
124/// A boxed error from an adapter, kept as the `source` of port errors.
125pub type BoxError = Box<dyn std::error::Error + Send + Sync + 'static>;