Cosmo Realtime SDK
Production

Session limits and resume

Wall-clock caps, the ending-soon warning, and continuing a conversation in a new session.

Sessions don't run forever. A wall-clock cap protects you from runaway costs (a forgotten tab, a voice mail system that never hangs up) and lets the platform bound resources. The SDK tells you the effective cap, warns before it fires, and gives you a resume path when a conversation legitimately needs more time.

The session cap

The wire protocol accepts a requested cap per run (max_session_seconds, 60–14400). The server enforces the minimum of your request and its own limits — you can shorten a session, never extend one. Swift exposes the request directly:

let session = try await agent.start(maxSessionSeconds: 900)   // 15 minutes

TypeScript and Python sessions currently run under the server's default cap. In every SDK, the effective value is reported on the ready event — read it from there rather than assuming a request was granted:

session.on('ready', ({ maxSessionSeconds }) => {
  scheduleWrapUpTimer(maxSessionSeconds);
});
case ReadyEvent():
    print(f"cap: {event.max_session_seconds}s")

The shutdown sequence

As the deadline approaches, the server pushes warnings, then ends the session:

  1. session-ending-soon — carries seconds_remaining and a reason slug. This is your window to have the agent wrap up naturally, save state, or offer continuation.
  2. session-ended — the server's deliberate goodbye, with reason (for example, max_session_duration).
  3. The stream finishes with the terminal session-ended item, as on every exit path.

A good voice UX treats session-ending-soon as a conversational event, not just a log line — inject a wrap-up cue with send_text or surface a "continue?" affordance in the UI.

Handling it per language: TypeScript — session.on('session_ending_soon', …). Python — match SessionEndingSoonEvent on the event stream. Swift — match .sessionEndingSoon. Every form carries seconds_remaining and reason.

Conversation resume

resume_session_id starts a new session that continues a prior one's conversation:

first = await agent.start()
# … session ends (cap, network, user closed the app) …

again = await agent.start(resume_session_id=first.session_id)
# the agent remembers the conversation

Under the hood the server does whichever is possible: native resumption when the upstream model session is still warm, or seeded continuation (replaying the transcript into a fresh model session) when it isn't. Either way the agent picks up with context; either way it does not re-speak its greeting — greetings belong to first contact.

resume_session_id is an experimental session param: it works today, but its shape may change between releases. Pin your SDK version if you build a core flow on it.

Resume is per-conversation continuity, not durable memory: it stitches one user's interrupted conversation back together. Long-term memory across days and sessions belongs in your own store, injected through instructions, a SessionStart hook, or session state capture.

Wall-clock caps interact with the platform's metering: concurrent-session and usage limits are account-level — see Limits for the free-tier caps. Transport-level drops within a session's lifetime are a different topic — see Reconnects.

On this page