Event normalization
Convert native platform messages, mentions, slash commands, or replies into a shared
ConversationEvent.
How Slack, Discord, Telegram, and GitHub adapters convert platform events into mikan's shared runtime format.
Their goal is to keep src/runtime/* and src/agent.ts from knowing each platform’s SDK, thread rules, message update API, or file download behavior.
Event normalization
Convert native platform messages, mentions, slash commands, or replies into a shared
ConversationEvent.
Session routing
Compute sessionKey from channels, threads, and reply chains so conversations enter the right
session.
Response capability
ConversationResponder wraps replies, updates, typing/working state, and file uploads.
ConversationEvent and ConversationMessage values.sessionKey so different channels, threads, and replies map to the right session.attachments/ directory.ConversationResponder that wraps replies, updates, typing/working state, and file uploads.MessagingEventHandler, which is ConversationRuntime.Shared types are exported from src/adapter.ts; implementation types live in src/types.ts and src/adapters/types.ts.
Adapters keep their platform’s raw conversation id at the external I/O boundary — that is what they
receive from the SDK and what they send back. Storage does not: platform plus the raw id form an
OfficeAddress, which the src/office/ module hashes into the office key naming the conversation’s
directory, its host-side state, and its vault. Adapters do not construct those paths themselves;
they pass the address and receive an Office value. One consequence worth knowing when reading
disk: a Slack channel C0123456789 lives at <workspace>/v1-slack-c0123456789-<digest>/, not at
<workspace>/C0123456789/.
Slash-command registration and routing derive from src/commands/manifest.ts rather than per-adapter
inventories, so a new command reaches every platform that opts in from one entry.
sequenceDiagram participant P as Platform SDK participant A as Platform adapter participant I as processMessageIntake() participant R as ConversationRuntime participant C as ConversationResponder
P->>A: native message / slash command / reply A->>A: parse conversation, user, thread, attachments A->>I: ConversationEvent base + trigger/log/attachment hooks I->>I: auto-reply / trigger decision I->>A: download attachments when needed I->>R: handler.handleEvent(event, bot, context) R->>C: respond / replaceResponse / setWorking / uploadFile C->>P: platform formatting and send| File | Purpose |
|---|---|
src/adapter.ts | Exports shared interfaces such as MessagingBot, ConversationEvent, ConversationMessage, and ConversationResponder. |
src/adapters/intake.ts | Shared message intake flow: magic word, trigger policy, attachments, log, busy policy, queue, then dispatch. |
src/adapters/shared.ts | Shared retry, queue, long message splitting, stop target resolution, and the two office write paths (log.jsonl, attachments). |
src/adapters/progressive-renderer.ts | Owns progressive response buffering, tool progress, finalization, platform write serialization, and response-error reporting. |
saveIncomingAttachments() owns the whole attachment convention — a sanitized
<timestamp>_<name> under the office’s attachments/ directory and an office-relative path handed
to the runtime — so no adapter composes conversation paths itself.
The core runtime depends only on these shared abstractions:
ConversationEvent: one user or platform event.MessagingBot: platform messaging bot capabilities, such as post message and upload file.ConversationContext: message, responder, and platform metadata attached to an event.ConversationResponder: the channel used by the agent to reply.When adding a platform, implement these abstractions first. Do not bring platform SDK details into src/runtime/* or src/agent.ts.