Conversations¶
octomate.schemas.thread ¶
The thread ledger: threads, their messages, message bindings and handoffs.
ATTRIBUTABLE_KINDS
module-attribute
¶
ChannelThreadId
module-attribute
¶
ThreadKey ¶
The channel's identity for a thread: channel, chat type, chat id, and the platform's thread id.
Source code in octomate/schemas/thread.py
kind
property
¶
What this key names — the chat type, unless a native client owns it.
from_address
classmethod
¶
The address with the person and the visibility stripped off.
Source code in octomate/schemas/thread.py
address ¶
The surface this key names, spoken to user_id — from_address back.
Source code in octomate/schemas/thread.py
ThreadMessageFTS ¶
Bases: BaseTransmuter
A row of the ledger's full-text index, with its search rank.
Source code in octomate/schemas/thread.py
ThreadLedger ¶
Bases: BaseTransmuter
A ledger a thread shows: the messages delivered to one thread, up to a cut.
Source code in octomate/schemas/thread.py
ledger_id
class-attribute
instance-attribute
¶
ledger_id: Annotated[UUID7, Identity] = Field(
description="The thread whose delivered messages make up this ledger."
)
cut_message_id
class-attribute
instance-attribute
¶
cut_message_id: UUID7 | None = Field(
default=None,
description="The ledger's last message the thread shows, frozen at the fork that shared it; None for the thread's own ledger, shown whole as it grows.",
)
ThreadMessage ¶
Bases: BaseTransmuter
One line of the thread ledger: who said what, when, and the model messages it is bound to.
Source code in octomate/schemas/thread.py
id
class-attribute
instance-attribute
¶
kind
class-attribute
instance-attribute
¶
kind: Literal["message"] = Field(
default="message",
frozen=True,
description="Whether this ledger row is a chat message, explicit command, or artifact reference.",
)
happened_at
class-attribute
instance-attribute
¶
sender_id
class-attribute
instance-attribute
¶
sender_id: UUID7 = Field(
description="The sender's registry profile row (user_profiles); `sender` resolves it. Inbound: the platform account; outbound: the channel bot or a native session's pseudo-user."
)
sender
class-attribute
instance-attribute
¶
segments
class-attribute
instance-attribute
¶
created_at
class-attribute
instance-attribute
¶
model_messages
class-attribute
instance-attribute
¶
ThreadCommand ¶
Bases: ThreadMessage
A durable command receipt and its outcome, never ordinary prompt input.
The inherited delivery key identifies retries. Record the receipt before invoking the runtime; an absent outcome does not make replay safe.
Source code in octomate/schemas/thread.py
kind
class-attribute
instance-attribute
¶
conversation_id
class-attribute
instance-attribute
¶
conversation_id: UUID7 | None = Field(
description="The command's target conversation; null after that conversation is deleted."
)
invocation
class-attribute
instance-attribute
¶
invocation: CommandInvocation = Field(
description="Explicit command intent with raw arguments and resolved attachments."
)
outcome
class-attribute
instance-attribute
¶
outcome: CommandOutcome | None = Field(
default=None,
description="Recorded command outcome; null means no terminal outcome was recorded, not permission to retry.",
)
has_delivery_id ¶
A durable receipt must identify the delivery it protects from replay.
Source code in octomate/schemas/thread.py
ThreadArtifact ¶
Bases: ThreadMessage
A ledger card referencing separately owned artifact content.
Generic ledger responses contain artifact_id only. The row's own id
identifies this appearance for live/history deduplication and fork cuts.
Artifact data requires an independently authorized read.
Source code in octomate/schemas/thread.py
kind
class-attribute
instance-attribute
¶
artifact_id
class-attribute
instance-attribute
¶
artifact_id: UUID7 = Field(
frozen=True,
description="Referenced artifact UUID, distinct from this ledger row's UUID.",
)
artifact
class-attribute
instance-attribute
¶
artifact: Relation[Artifact] = Field(
default_factory=Relation,
exclude=True,
repr=False,
description="Content relation for persistence, never included in ledger serialization.",
)
MessageBinding ¶
Bases: BaseTransmuter
A link between a ledger message and a model message, by kind and run.
Source code in octomate/schemas/thread.py
created_at
class-attribute
instance-attribute
¶
Handoff ¶
Bases: BaseTransmuter
A recorded change of the agent owning a thread, and where it came from.
Source code in octomate/schemas/thread.py
id
class-attribute
instance-attribute
¶
source_agent_tentacle_id
class-attribute
instance-attribute
¶
source_conversation_id
class-attribute
instance-attribute
¶
target_conversation_id
class-attribute
instance-attribute
¶
source_model_message_id
class-attribute
instance-attribute
¶
created_at
class-attribute
instance-attribute
¶
Thread ¶
Bases: BaseTransmuter
One chat surface in the ledger: its kind and channel key, the project its work is in, and the handoffs that say which agent owns it.
Source code in octomate/schemas/thread.py
291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 | |
id
class-attribute
instance-attribute
¶
kind
class-attribute
instance-attribute
¶
kind: ThreadKind = Field(
frozen=True,
description="Which surface this thread is. Set from `ThreadKey.kind` when the row is created; only a `thread` and a `native_thread` may carry a project.",
)
channel_thread_id
class-attribute
instance-attribute
¶
channel_thread_id: str | None = Field(
default=None,
description="The platform's own thread id; None unless `kind` is `thread`.",
)
parent_thread_id
class-attribute
instance-attribute
¶
parent_thread_id: UUID7 | None = Field(
default=None,
description="The thread this one was opened inside — a kick in a chat room works in a thread of its own, while the chat everyone sees stays on the chat room. None for a thread nobody opened; one level deep, never a chain.",
)
title
class-attribute
instance-attribute
¶
title: str | None = Field(
default=None,
description="What this thread goes by in a listing. Taken from the first thing a person said in it, and replaced by a name the runtime grabbed for itself — a Claude session's own ai-title. None until the thread has been spoken in, and a listing then falls back to the surface.",
)
project_id
class-attribute
instance-attribute
¶
project_id: UUID7 | None = Field(
default=None,
description="The declared project this thread's work is in; None is unattributed, which is what a chat thread and a directory no project claims both produce. Set once — when the row is created, or later by `ThreadManager.bind` for a thread that had none — and never moved after that: an external session's history is full of absolute paths, so a thread that changed project would resume its sessions somewhere they do not fit. Not frozen, because settable once from None is a rule the manager can state and a field cannot.",
)
source_cursor_message_id
class-attribute
instance-attribute
¶
created_at
class-attribute
instance-attribute
¶
updated_at
class-attribute
instance-attribute
¶
project
class-attribute
instance-attribute
¶
project: Relation[Project | None] = Field(
default_factory=Relation,
frozen=True,
exclude=True,
description="The project this thread's work is in, eagerly loaded.",
)
parent
class-attribute
instance-attribute
¶
messages
class-attribute
instance-attribute
¶
handoffs
class-attribute
instance-attribute
¶
conversations
class-attribute
instance-attribute
¶
kind_agrees_with_the_key ¶
The channel's key is the fact; this row is our copy of it.
A copy that contradicts the fact is corrupt, and it would answer questions — chiefly whether a project may be attached — with the wrong surface.
Source code in octomate/schemas/thread.py
octomate.schemas.conversation ¶
Channel addresses and agent conversations.
AddressMetadata ¶
Bases: TypedDict
What a channel adds to an address to show it; each channel types its own.
Source code in octomate/schemas/conversation.py
ChannelAddress ¶
One surface on a channel — a DM, a group, a thread — and the user spoken to on it.
Source code in octomate/schemas/conversation.py
metadata
class-attribute
instance-attribute
¶
ConversationKey ¶
Bases: NamedTuple
Identity of an agent conversation: the owning thread, the agent, and —
for a context spawned by one of that agent's runs — the subagent. The
empty subagent_id is the agent's own conversation (the
Thread.channel_thread_id sentinel convention): a thread owns one bare
conversation per agent, so
every sender in a group thread keys to that one, independent of who woke
it; each subagent keys to its own.
Source code in octomate/schemas/conversation.py
ConversationRun ¶
Bases: BaseTransmuter
A run in a conversation's history: one it ran, or one it was forked with.
Source code in octomate/schemas/conversation.py
Conversation ¶
Bases: BaseTransmuter
One agent's model context within a thread, keyed by thread, agent and subagent.
Source code in octomate/schemas/conversation.py
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 | |
id
class-attribute
instance-attribute
¶
transcript_file_id
class-attribute
instance-attribute
¶
transcript_file_id: UUID7 | None = Field(
default=None,
description="Live native transcript, or the independent starting copy of an imported fork.",
)
thread_id
class-attribute
instance-attribute
¶
thread_id: UUID7 = Field(
frozen=True,
description="The owning thread; with agent_tentacle_id it is the conversation's identity.",
)
agent_tentacle_id
class-attribute
instance-attribute
¶
agent_tentacle_id: str = Field(
frozen=True,
description="The owning agent; with thread_id it is the conversation's identity.",
)
subagent_id
class-attribute
instance-attribute
¶
subagent_id: str = Field(
default="",
frozen=True,
description="Empty for the agent's own conversation in the thread; a subagent's stable identity (Claude agentId, Codex child thread id, a commission's name) for a context spawned by one run.",
)
parent_conversation_id
class-attribute
instance-attribute
¶
parent_conversation_id: UUID7 | None = Field(
default=None,
frozen=True,
description="The conversation whose run spawned this subagent's context — set iff subagent_id is. Which parent turn drove each child run stays on the run (parent_run_id).",
)
permission_mode
class-attribute
instance-attribute
¶
permission_mode: AgentPermissionMode | None = Field(
default=None,
description="Approval posture this conversation's agent works under, in that agent's own vocabulary. None declares nothing, and the agent's configured default decides. Seeded from the project at creation and owned here after, so a change made mid-thread is never revoked by a later ensure.",
)
effort
class-attribute
instance-attribute
¶
effort: str | None = Field(
default=None,
description="Reasoning effort this conversation's runs ask for, one of the levels its agent's route claims for the model. None declares nothing, and the runtime's own default decides. A run given an effort of its own, as a summon is, uses that one instead.",
)
allowed_tools
class-attribute
instance-attribute
¶
allowed_tools: list[str] = Field(
default_factory=list,
description="Tools the user allowed for the life of this conversation ('allow for session'), auto-approved without raising a card again.",
)
runs
class-attribute
instance-attribute
¶
messages
class-attribute
instance-attribute
¶
native_run_ids
property
¶
Native runs with a completed transcript range, safe to skip on replay.
Empty hook reservations and incomplete imports still need their transcript.
latest_run
property
¶
The last run in the loaded, ordered history, or None before the first run.
octomate.schemas.runs ¶
Agent runs: one per agent.run(), driven by Octomate or replayed from an
external transcript.
AgentRun ¶
Bases: BaseTransmuter
One agent.run() invocation. PK is the pydantic-ai run_id (a uuid7 string).
The octomate variant of the polymorphic run — driven by Octomate. A native
run replayed from an external transcript is the ExternalAgentRun variant.
Source code in octomate/schemas/runs.py
cwd
class-attribute
instance-attribute
¶
cwd: Path | None = Field(
default=None,
description="The directory this run happened in, as its source reported it — spelled by `Path`, so a trailing slash or a `.` segment is normalized away. None when the source reported none, which is never a guess.",
)
native_id
class-attribute
instance-attribute
¶
native_id: str | None = Field(
default=None,
description="The native runtime owning the native session and turn IDs.",
)
native_turn_id
class-attribute
instance-attribute
¶
native_turn_id: str | None = Field(
default=None,
description="The native ingest turn key used to recognize a replayed driven run.",
)
messages
class-attribute
instance-attribute
¶
ExternalAgentRun ¶
Bases: AgentRun
A run rebuilt from an external runtime's transcript (native Claude session). Carries the transcript coordinates its offsets check-point ingest against.
Source code in octomate/schemas/runs.py
octomate.schemas.messages ¶
Persisted model messages: pydantic-ai's request and response, blessed to the
polymorphic model_messages table.
dataclass_config
module-attribute
¶
dataclass_config = ConfigDict(
populate_by_name=True,
validate_by_name=True,
validate_by_alias=True,
)
ModelMessage ¶
Bases: BaseTransmuter, ABC
Polymorphic parent of the message transmuters — just the shared, queryable
columns. Blessed to the abstract model_messages base so arcanus can run one
polymorphic select that adapts each row to its ModelRequest/ModelResponse
subtype (keyed on kind). Abstract and never instantiated; the subtypes can't
subclass it (each already subclasses its pydantic-ai message type), so it is a
sibling registration that the polymorphic result adapter discovers by kind.
Source code in octomate/schemas/messages.py
ModelRequest ¶
Bases: Transmuter, ModelRequest
A persisted pydantic-ai request; role and message_text are derived from
its parts.
Source code in octomate/schemas/messages.py
ModelResponse ¶
Bases: Transmuter, ModelResponse
A persisted pydantic-ai response; message_text is its spoken text,
including what it delivered through send.
Source code in octomate/schemas/messages.py
id
class-attribute
instance-attribute
¶
timestamp
class-attribute
instance-attribute
¶
metadata
class-attribute
instance-attribute
¶
workspace_ref
class-attribute
instance-attribute
¶
workspace_ref: WorkspaceRef | None = Field(
default=None,
description="The provider and identifier of the response's workspace, without credentials.",
)
thread_messages
class-attribute
instance-attribute
¶
thread_messages: RelationCollection[
ThreadMessage | ThreadCommand | ThreadArtifact
] = Relationships()
octomate.schemas.events ¶
The inbound message event a channel's chromo produces.
MessageEvent
pydantic-model
¶
Bases: BaseModel
An inbound platform message as a channel's chromo translated it: who sent it, on which surface, and the segments it carries.
Config:
extra:ignorecoerce_numbers_to_str:True
Fields:
-
tentacle_id(str) -
message_id(str) -
channel_thread_id(str | None) -
reply_id(str) -
timestamp(float) -
user_id(str) -
chat_id(str) -
chat_type(ChatType) -
chat_name(str | None) -
shared(bool) -
self_id(str) -
sender(UserProfile) -
segments(list[MessageSegment]) -
raw(str)
Source code in octomate/schemas/events.py
model_config
class-attribute
instance-attribute
¶
chat_name
pydantic-field
¶
The platform's chat name, when supplied by the inbound event.
shared
pydantic-field
¶
Whether anyone besides the sender can read this surface. Separate from chat_type, which a thread overwrites and which therefore cannot tell a thread in a group chat from one inside a DM. Only the channel knows: it is what the mention gate asks before it drops an unaddressed message.
sender_label
property
¶
The parenthesized ids of the sender line: the raw platform id, plus
the user:{username} marker (the owning user) that equates one human
across channels. The owner is present once the sender is a registry row.
is_at ¶
replies_to ¶
text_parts ¶
octomate.schemas.segments ¶
Message segments: the typed pieces — text, mentions, images, markdown, replies, files, cards — a message is made of on every platform.
CARD_TEXT_KEYS
module-attribute
¶
MessageSegment
module-attribute
¶
MessageSegment = Annotated[
TextSegment
| AtSegment
| ImageSegment
| MarkdownSegment
| ReplySegment
| FileSegment
| CardSegment,
Discriminator("type"),
]
TextData ¶
AtData
pydantic-model
¶
Bases: BaseModel
The mentioned user's platform id, and a display name when known.
Fields:
Source code in octomate/schemas/segments.py
ImageData
pydantic-model
¶
Bases: BaseModel
Image payload. file is always a local file path (str).
Inbound images are downloaded to local storage by the tentacle layer.
Outbound images are read from local files and uploaded by the tentacle.
Use the path property to get it as a Path object.
Fields:
Validators:
-
_coerce_path→file
Source code in octomate/schemas/segments.py
MarkdownData ¶
ReplyData ¶
Bases: TypedDict
The replied-to message's id, with its content and author when known.
Source code in octomate/schemas/segments.py
FileData
pydantic-model
¶
Bases: BaseModel
A local file path, the name it is shown as, and its size in bytes.
Fields:
Source code in octomate/schemas/segments.py
CardData
pydantic-model
¶
Segment
pydantic-model
¶
Bases: BaseModel
Base class for all message segments.
Source code in octomate/schemas/segments.py
TextSegment
pydantic-model
¶
Bases: Segment
Plain text content. Set data.text to the message text.
Fields:
Source code in octomate/schemas/segments.py
AtSegment
pydantic-model
¶
Bases: Segment
Mention/at a user. Set data.user_id to the user's platform ID (as string). Optionally set data.name.
Fields:
Source code in octomate/schemas/segments.py
ImageSegment
pydantic-model
¶
Bases: Segment
Send an image. Set data.file to a local file path. Never use http/https URLs — the system manages file storage automatically.
Fields:
Source code in octomate/schemas/segments.py
to_content ¶
MarkdownSegment
pydantic-model
¶
Bases: Segment
Markdown-formatted text. Set data.text to the markdown content.
Platforms that support markdown render it natively; others strip it to plain text.
Images can be embedded inline with ; no separate ImageSegment is needed.
Fields:
-
type(Literal['markdown']) -
data(MarkdownData)
Source code in octomate/schemas/segments.py
ReplySegment
pydantic-model
¶
Bases: Segment
Quote/reply to a previous message so the answer threads onto it. Set data.id
to the target's message id — the #msg:<id> handle shown beside each message in
the conversation. Put it first; the message threads onto the first reply it carries.
Fields:
Source code in octomate/schemas/segments.py
FileSegment
pydantic-model
¶
Bases: Segment
Send a file. Set data.file to a local file path and data.name to the name it is shown as.
Fields:
Source code in octomate/schemas/segments.py
CardSegment
pydantic-model
¶
Bases: Segment
A platform-native card. Set data.payload to the card JSON the platform renders.
Fields:
Source code in octomate/schemas/segments.py
octomate.schemas.actions ¶
Message-level action payloads: the composed agent message, confirmations, questions and todos.
AgentMessage
pydantic-model
¶
Bases: BaseModel
A single outgoing message composed of one or more segments.
Fields:
-
segments(list[MessageSegment])
Source code in octomate/schemas/actions.py
ConfirmAction
pydantic-model
¶
Bases: BaseModel
A pending confirmation of a tool call, with its approvers and expiry.
Fields:
-
confirmation_id(str) -
conversation_address(ChannelAddress) -
tool_name(str) -
tool_call_id(str) -
args(JsonObject) -
title(str) -
description(str) -
skill(str) -
approvers(list[str]) -
created_at(float) -
expires_at(float) -
status(Literal['pending', 'approved', 'denied', 'expired'])
Source code in octomate/schemas/actions.py
QuestionAction
pydantic-model
¶
Bases: BaseModel
A pending question with its options and expiry.
Fields:
-
question_id(str) -
conversation_address(ChannelAddress) -
text(str) -
options(list[str]) -
multi_select(bool) -
created_at(float) -
expires_at(float) -
status(Literal['pending', 'answered', 'expired'])
Source code in octomate/schemas/actions.py
QuestionResponse
pydantic-model
¶
Bases: BaseModel
A user's answer to a question.
Fields:
-
question_id(str) -
answer(str) -
responder_id(str)