Platform chat history
log.jsonl keeps human-readable platform messages used to bootstrap thread or reply context.
How mikan separates platform chat history from the agent harness's structured sessions.
Platform chat history
log.jsonl keeps human-readable platform messages used to bootstrap thread or reply context.
Harness structured context
Host-only session files store tool results and agent turns so the mikan agent harness can continue work.
Fixed scope
Threads, reply chains, and shared channels map to fixed session files so different conversations do not contaminate each other.
A session key is conversationId on its own, or conversationId:suffix for a scoped session. The suffix is whatever the platform uses to identify a thread or reply target.
| Platform | sessionKey rule | Notes |
|---|---|---|
| Slack | top-level and DM: channelId; thread (including in a DM): channelId:threadTs | thread sessions are fixed files bootstrapped from recent chat history |
| Discord | DM and shared top-level: channelId; thread: parentChannelId:threadChannelId; reply: channelId:repliedMessageId | a guild thread is addressed by its parent channel plus the thread’s own id |
| Telegram | private chat: chatId; shared top-level: chatId:messageId; shared reply: chatId:repliedMessageId | no native thread model, so the message replied to is the scope |
| GitHub | GH_<owner>_<repo>_<number> | one issue or pull request is one flat session; inline review threads included |
Session keys stay raw platform values — the grammar in src/sessions/session-key.ts never sees an office key. Runtime state is addressed by the conversation’s office plus its session key, so two platforms that share a raw conversation id can never select each other’s runners, queues, or stop state. Storage paths follow the office: session files live under ~/.mikan/conversations/<officeKey>/sessions/, which no sandbox sees.
A session key always belongs to exactly one conversation. Platform adapters may supply a scoped key, but conversation intake and the runtime reject a key whose conversation prefix differs from the incoming conversation. Conversation identities and scoped suffixes also reject path separators, control characters, and the special . / .. segments before they can influence session storage. Thread session files keep their existing names and persistence behavior; the sessions module additionally rejects symlink session targets and proves derived paths remain inside the office’s sessions directory. Paths recorded in session headers are never opened; a parent is found by its session id.
Each office keeps its sessions in one pi-durable SQLite storage: the top-level session and one session per thread or reply scope.
A thread starts from what caused it. Each run records the message that started it, and the log records its answer together with that message and the session that ran it. A thread opened under a run’s answer, or under the message that started the run, forks that run: it begins with the session’s context up to the end of the run, including its tool calls and their output, so the agent can say what a command printed without running it again. Any other thread starts from its root message and its own replies. Threads that existed before this behavior keep the context they started with.
A session’s own answers are its memory. Chat sync adds other people’s and other bots’ messages, and mikan notices, as attributed chat lines; answers that a run in another session posted are left to that session. Older history stays searchable in log.jsonl, and the agent’s history tool searches every session of the conversation and the chat log, including tool calls and their output.
mikan migrate imports 0.5.3 session files into the office’s sessions.db and moves the original sessions/ directory to sessions-v3/; see State migrations.
Top-level sessions are long-lived, and Pi’s automatic compaction keeps the model context within the window. /new resets the session at once: the model starts from an empty context, and the earlier entries stay in storage, readable by the history tool and the session view. It does not summarize the predecessor or wait for memory processing. The conversation MEMORY.md remains available independently.
Memory capture records knowledge as soon as it is stated. After a human-initiated run finishes, mikan asks Jev whether the user established durable knowledge, such as a standing rule, a delivery preference, a durable fact about a system or workflow, or a correction. When it did, the conversation’s model adds or updates individual lines under ## Captured knowledge in the conversation MEMORY.md, stamped with the capture date and source message. Scheduled-event runs are skipped, workspace-wide memory is never written, and capture is disabled when no Jev provider has a key. Ask the agent to correct or remove a captured line as with any other memory.
The conversation MEMORY.md, the Memory anchor, is written by the agent during runs and by Memory capture after them. Nothing rewrites or consolidates it on a schedule. It is compact, revisable orientation rather than final truth. Newer conversation evidence takes precedence. Mutable external state must be verified from its Live source or current API in the answering run; if that source is unavailable, mikan should report that it cannot verify the current value rather than treating memory as current truth.