Skip to main content

Sessions

A session is the record of one conversation: every message, every model call, and every tool call, in order.

In most agent frameworks, the agent process keeps that record in its own memory. In the Cosmonic Agent SDK, the agent harness never keeps the record: instead, session management falls to the host, and the store behind it is a deployment choice, not a code change.

Why the host keeps the session​

The agent harness writes to its session through the cosmonic:agent/session interface and holds nothing between runs. That one choice does three jobs.

  • Any instance can serve the next run. Because nothing lives in the harness's memory, the host can tear an idle instance down and start a fresh one on the next message, on the same host or another. The new instance reads the session and carries on. This is what lets an agent scale to zero without losing its place, and survive a crash or a restart.
  • A session can't be reached by the wrong code. The interface takes no session name. The host binds each component to its session when it links it, from the component's identity, so a harness cannot open another workload's conversation, and it cannot pretend to be another component: the host stamps each entry with its author and time.
  • There is one record. Every model call and tool call is written to the session as it starts and as it ends, so what happened in a run is in one place, in order.

The conversation is a journal​

A session's record is a journal: an append-only list of entries the host keeps on the harness's behalf. Each person's message, each model reply and each tool call and result is an entry.

Next to the journal, a harness can keep its own working state, such as a compact form of the context it sends to the model. The host stores that state too, along with which part of the journal it covers.

Sessions survive restarts​

A harness must not rely on anything in its own memory surviving between runs. The next run may be served by a different instance, after a restart, or on another host. Everything a harness needs to continue is in its session.

Two design choices make that safe:

  • Conditional commits. A harness writes entries and state together, on the condition that nothing changed since it last read. If another writer got there first, the write is refused and the harness gets the current snapshot to merge with. Two writers never silently overwrite each other.
  • Idempotent retries. Each write can carry an ID. If the reply to a write is lost, the harness can send it again with the same ID, and the host will not apply it twice.

The journal also records each model and tool call before it starts and after it returns. If a harness restarts in the middle of a call, it can see exactly what was in flight. A model call can simply be made again. A tool call whose effects are unknown, such as a write that may or may not have happened, is marked unresolved for a person to look at rather than repeated blindly.

Compaction​

Every model has a limit on how much context it can read at once, and long conversations reach it. Compaction is how a harness handles that: it asks the model to summarize the older part of the conversation and replaces that context with the summary, keeping the most recent messages as they were.

A harness compacts when the context crosses a threshold, when a request overflows, or when the driver asks it to between runs. Each compaction is recorded in the journal with the summary, the range of entries it covers and how many tokens it saved.

Compaction never deletes history. The original entries stay in the journal, so the full conversation can always be redrawn. The summary is treated as model output, never as instructions from a person or the system.

Where sessions live​

The store is a deployment choice. Nothing in an agent names a database, a file path, or a connection string. It calls commit, read and follow, and the host decides where the bytes go. The same agent image runs on each host without changes.

HostWhere sessions are storedEncrypted at restShared with
Cosmonic DesktopFiles in Desktop's state directory, one directory per workloadYes. Each session has its own key, kept under a master key in your system keychain. Deleting a session deletes its keyThis machine
Cosmonic ControlA NATS JetStream key-value bucket on the cluster's NATS, with as many replicas as you configureYes, before anything reaches NATSEvery host and every replica of the workload

Moving sessions to another store changes the host, not the agent.

What a store has to do​

The session interface is an open contract, written in cosmonic:agent. A host can serve it from any store that can do these five things:

  1. Write with a condition. A commit appends entries and replaces the agent's working state together, and only if the journal end and the state version are still what the writer last saw. If not, nothing is written and the writer gets the current snapshot to merge with. This is compare-and-swap, and it is what keeps two writers from silently overwriting each other.
  2. Recognize a retried write. Each commit can carry an ID. The store remembers at least the latest 1,024, so a write sent again after a lost reply returns the first result and is not applied twice.
  3. Number entries without reuse. Sequence numbers increase and are never reused, even after the session is cleared, so a reader can always resume from the last number it saw.
  4. Tell readers about new entries, so follow can stream them as they are committed.
  5. Refuse a write that is too large rather than truncate it.

A SQL database with transactions, a key-value store with compare-and-swap, and object storage with conditional writes can all do this. In wasmCloud, a host serves an interface like this one through a host plugin. Desktop and Control each have their own, and both encrypt a session before the store receives it, so the store holds only ciphertext.

Sharing a session​

follow reads a session from a given entry and then streams each new entry as it is committed. On Control it sees commits made on any host. A chat window, a dashboard or a person can watch a run live this way.

Several writers can work in the same session safely, because every write is conditional: the one that loses gets the current snapshot and merges. Runs themselves take turns. The driver starts one run at a time per session, so two runs never interleave a conversation.

Tools get stores of their own​

A tool sometimes needs to remember something between calls, such as a cursor or a cache. It uses the same session interface, but it should never see the conversation. The workload's session entry keeps the two apart:

hostInterfaces:
  - namespace: cosmonic
    package: agent
    interfaces: [session]
    config:
      components: agent # works in the workload's session, the conversation
      tool-components: notes # gets a store of its own
  • components names the components that work in the conversation.
  • tool-components gives each component it names a store of its own, with the same interface, encryption and limits, scoped to the workload and that component. It cannot read the conversation.
  • A component named in both is refused when the workload starts, and a component named in neither is not linked to sessions at all.