The web app11 of 12
Sessions and history
On this page
This page is mostly about what the browser does not keep. It is short on purpose.
A session is one page load
The browser mints a fresh session id when the companion mounts, and holds it for as long as the page
is open (components/CompanionExperience.tsx:935). Reloading the tab, opening a second tab, or
navigating away and back all produce a different session.
The id is a UUID v4. On an insecure origin — http://<lan-ip>:3000, which is one self-host away —
crypto.randomUUID does not exist, so it is built from crypto.getRandomValues instead: the same
entropy source and the same shape, with no fallback to Math.random
(CompanionExperience.tsx:53-67).
Calling and hanging up does not change the session. You can leave a call and start another in the same page and she is still in the same conversation.
What survives a page reload, and what does not
| Thing | Survives a reload? | Where it lives |
|---|---|---|
| Files | Yes | ~/.kotoba/files on disk |
Report .html files | Yes | the file library, under reports/ |
| Memory topics | Yes | the backend's store, listed in Settings → Memory |
| Reminders | Yes | the backend's store, listed in Settings → Reminders |
| Saved keys and standing command grants | Yes | the backend |
| Settings | Yes | ~/.kotoba/settings.yaml |
| The transcript on screen | No | the page's own memory |
| The terminal rows | No | the page's own memory, and cleared on hang-up |
| The plan and the helper chibis | No | the same |
| The live report the panel shows | No | a process-level dictionary keyed by session |
| The conversation thread she is given | No | the backend keys turns by session id |
That last row is the one that surprises people. Turns are written down — the backend stores every
turn and reads recent ones back to build her context — but the read is scoped to the session id
(api/src/kotoba/db/database.py:252-260). A new page load means a new session id, so the thread starts
empty.
What carries across a reload is what she deliberately remembered, not what was said. That is the memory system, and it is a different mechanism with its own surface in Settings → Memory.
There is no history browser
The web app has no conversation list, no session picker and no way to reopen a past conversation.
Nothing in app/, components/ or lib/ fetches a list of sessions or a past transcript.
The backend can list past conversations — list_sessions returns them newest first
(api/src/kotoba/db/database.py:282-287) — and the terminal client has a /sessions listing built on
it. No HTTP route exposes it and no browser code calls it.
Even in the terminal that listing does not reopen anything. It is a list of when each conversation
started and how much is in it, and it says so: "these don't reopen — say what you're after and she'll
go and find it in them" (api/src/kotoba/cli/slash.py:807-817).
What does get re-read
Not history, but worth knowing: when the events channel reconnects mid-call, the browser re-reads
three pieces of live state so the screen matches the backend again — the microphone's mute flag, the
current task list, and whether work is still running
(CompanionExperience.tsx:311-350). The mute resync is skipped in local voice mode, where the
browser owns the microphone (CompanionExperience.tsx:313). The work read is repair only: it may
clear the working flag but never set it, because a REST read can be older than a live frame.
And when a call connects, the browser asks whether a background job finished while nothing was
listening, so she can tell you about it rather than letting it pass unmentioned
(CompanionExperience.tsx:385-394).
Signing out
Signing out ends the browser session and does a full page navigation, which discards the session id
along with everything else in memory. It touches nothing on disk: her memory and her files are
untouched, and the panel says so (components/panels/SettingsPanel.tsx:698-701).
