Time Slices
Most AI products organize you by conversation thread: you decide when to start a new chat, what to name it, and which one to dig back into later. Previously makes no such assumption. It recognizes exactly one natural unit — time. You start talking, and a time slice opens; time passes, and it closes. There is no "new chat" button, because your life doesn't have one either.
A time slice is the smallest complete unit of Previously's memory: one contiguous burst of conversation, kept verbatim in a small directory — plain Markdown with YAML, no database, no binary format.
What's inside a time slice
Ask Previously: how does one of my conversations actually get remembered?
11:47 PM
Take the July 16 slice: its focus is "half-marathon completion and personal growth"; the emotional tone reads proud, reflective; open_loops still holds "fitness goals you might pick up next"; and the full conversation sits untouched in timeline/core.md.
The moment a slice closes, the turn analyzer marks it: focus, summary, tags, emotional tone. And if the model falls short that time, a deterministic fallback steps in — no slice ever closes empty.
Every slice is a directory under memory/episodic/slices/YYYY/MM/DD/HHMM/ — the path itself is the moment it happened. The HHMM is UTC, taken from the first message's timestamp; your local timezone lives separately in the timezone frontmatter field, so paths stay stable across time zones and daylight-saving shifts. The files inside have distinct jobs:
memory/episodic/slices/2026/08/10/2130/
timeline/
core.md -- the shared record: YAML frontmatter + verbatim conversation turns
agent.md -- its thinking: extracted reasoning, tool calls (ok/error), thinkDeep reports
appendix.md -- raw lines that didn't parse into turns, kept as-is
previously.md -- a snapshot of the user card, frozen as this slice closed
timeline/core.mdis the shared conversation record and the single source of truth: what you and Previously actually said, word for word.timeline/agent.mdis the agent's own line — the cognition behind each of its turns, mechanically extracted, never injected back as conversation context.timeline/appendix.mdis the honest fallback: lines the parser couldn't digest are not dropped, they go to the appendix.previously.mdis a snapshot of the user card frozen at the moment the slice closed — the "you" it knew at the time.
Slicing answers to time alone
There is no "topic detection" anywhere in a slice's lifecycle. That is deliberate: deciding "did the topic just change" is exactly where a model makes mistakes, and time never lies.
A slice can end three ways, and all of them are recorded in the closed_by frontmatter field:
- Slice age cap (
time_cap) — the slice force-closesmaxSliceMinutes(default 30, adjustable in Settings) after its start. Note: from the start, not from the last activity — a slice is a span of time with a beginning and an end, not the outcome of a silence detector. - Context loss (
context_lost) — you refreshed the page or switched devices; the client history came back without the agent's replies while the slice on disk has them. The slice cannot be honestly continued, so it closes. - Turn cap (
capacity) —maxTurnsPerSlice(default 50) is a pure safety valve against a runaway session. In normal life you will never touch it.
Closes are lazy: the signal is checked when your next message arrives, and the slice's end is stamped with the last turn's real timestamp, never the detection time. That next message opens a fresh slice — which is why the chat history window is slice-aligned too: the server trims client history to the current slice instead of a fixed "recent N".
The moment a slice closes
Before a slice seals, the turn analyzer (the analysis colleague that runs once per turn) writes its marking: focus (a one-sentence topic), summary (a recap of at most 100 characters), refined tags, and an emotional_tone.
One hard rule applies here: a slice never closes dry. If the model fails to produce a marking this time, the system builds one deterministically from the slice itself — first-turn topic plus tags — rather than leaving empty fields. The model adds quality; the code guarantees the floor.
Older "dry" slices (ones that predate this discipline and have no focus/summary) don't stay anonymous forever either: on slice-close boundaries, an opportunistic backfill pass marks up to 3 dry slices at a time. Close boundaries are the only moment backfill is allowed to run — it never interrupts a conversation in progress.
The frontmatter
Every core.md opens with --- delimited YAML. focus, summary, and emotional_tone are written at close (with the deterministic fallback); everything else is mechanical:
| Field | What it carries |
|---|---|
slice_id | YYYY-MM-DD-HHMM, the UTC datetime of the first message |
status | "active" or "closed" |
start / end | UTC ISO 8601 timestamps of the first and last turn (no end while active) |
timezone | Your IANA timezone at the time of interaction, e.g. "Asia/Shanghai" |
focus | The core topic, one sentence (written at close) |
summary | A recap, at most 100 characters (written at close) |
tags | Semantic keywords — woven into the strand index when the slice closes |
open_loops | Questions left unresolved |
decisions | Conclusions reached during this slice |
related_slices | Relative paths of related slices — may be empty |
loops | IDs of background loops spawned from this slice — may be empty |
emotional_tone | "positive", "neutral", "negative", or "mixed", assessed at close |
closed_by | The closing signal: "time_cap", "context_lost", "capacity", or "user_explicit"; older slices may carry the legacy value "time_silence" |
evolution_summary | A one-sentence summary of the card evolution that ran as this slice began — replayed verbatim into the system prompt, so the prompt stays byte-identical for the slice's whole life |
Empty strings and undefined fields are stripped from the YAML (which is why an active slice has no focus/summary); empty arrays are serialized as-is.
The turn body
After the frontmatter, each message becomes a level-2 heading:
## Turn mVhV2g — 2026-08-10T21:30:22.811Z (user)
Your message text…
## Turn mVhV2g — 2026-08-10T21:30:43.641Z (agent)
Previously's reply…
The user message and the agent reply of the same round share one 6-character turnId, so every exchange is addressable — tools can read an individual turn by it.
Why plain Markdown
Every slice is a Markdown file with YAML frontmatter, and that choice is deliberate: readable in any editor, cleanly diffed in git, parseable by any tool that understands Markdown. All of a turn's writes land in one commit — your message is safely in your memory folder before anything streams back. For what the whole thing looks like on disk, see Your Memory Is a Folder.
Related
- The Timeline — the catalog over all slices: a projection, never a second truth
- Strands — the semantic index woven from slice tags
- Recall — the colleague who searches these slices for you
- The Memory Model — where slices sit in the full memory architecture