Skip to content

Think: Is "one agent, many concurrent chat sessions" supposed to work as a product model in Think? #1349

Closed

Description

@Konan69

I'm building a multi-thread chat product on Think where users have one (or more) persistent agents and multiple conversation threads open at once, like a desktop chat app. Think's session model and SessionManager seem designed for exactly this. I'm asking this as a question rather than filing a bug, because I think I might be missing the intended identity model.

On the client side, useAgentChat keys chat state off agent URL path and name, not the Think sessionId. I gave each chat panel its own path with a distinct sessionId, which seemed right, and the hook did generate different URLs per panel. But the keying still feels like it's pushing me toward encoding session identity into routes or agent names, which from a product perspective feels like work that should belong to the session layer. I spent a while thinking this was a client cache key issue before I realized a flaw?

From @cloudflare/ai-chat/dist/react.js:

// useAgentChat cache key — keyed by origin + pathname + agent class + instance name
const initialMessagesCacheKey = `${agentUrl.origin}${agentUrl.pathname}|${agent.agent ?? ""}|${agent.name ?? ""}`;

No sessionId in the key. Two panels pointing at the same agent with different sessions share the same cache entry.

I looked into the server side, and got confused. Think serves /get-messages from this.messages, which reads this.session.getHistory(). My agent switches sessions by assigning this.session = this.sessions.getSession(sessionId), which is the pattern the API suggests. But that mutates a shared pointer on the DO instance. With two chat panels open against the same agent, requests race on that pointer and whichever one calls selectSession() last wins. I verified this end to end: two panels, two distinct sessionIds, two separate get-messages requests returning 200, both coming back with the exact same 14-message history. The collision isn't client caching or URL deduplication. It's the shared mutable this.session inside the DO responding to concurrent requests.

From @cloudflare/think/dist/think.js:

// this.messages is a getter that reads from the single active session
get messages() {
    return this.session.getHistory();
}
// /get-messages always returns the one active session's history
this.onRequest = async (request) => {
    const url = new URL(request.url);
    if (url.pathname === "/get-messages" || url.pathname.endsWith("/get-messages"))
        return Response.json(this.messages);
    return _onRequest(request);
};
// onStart sets up a single session — no per-request session resolution
this.onStart = async () => {
    // ...
    const baseSession = Session$1.create(this);
    this.session = await this.configureSession(baseSession);
    this.session.getHistory();
    // ...
};

What's confusing is that the storage layer seems to get this right. SessionManager supports multiple named sessions in one DO, sessions store their own history, and the API shape maps naturally to "one persistent agent, many threads." But the runtime layer still funnels everything through a single active session pointer that isn't safe to switch under concurrent requests. So the model the API advertises and the model the runtime actually supports seem to disagree.

I'm left wondering whether I'm using this wrong. Is "one agent, many concurrently open sessions" actually intended as a first-class pattern? If so, is there a supported way to resolve session context per request without mutating shared state, something where Think's built-in endpoints like /get-messages and turn handling are session-aware on each request instead of reading from one mutable field? Or is the intended boundary closer to one chat identity per agent route or name, meaning one DO per ai agent, kind of locking you out of using shared memory or linking sessions, and SessionManager is meant for switching between sessions sequentially rather than serving them in parallel? I can build around either answer. I just can't tell which one Think is designed for right now, and the gap had me nerd sniped for a few hours before I found the actual collision point.

Activity

  1. changed the title [-]Question: Is "one agent, many concurrent chat sessions" supposed to work as a product model in Think?[/-] [+]Think: Is "one agent, many concurrent chat sessions" supposed to work as a product model in Think?[/+] on Apr 21, 2026
  2. barnjamin commented on Apr 21, 2026

    @barnjamin

    I also spent too much time on this yesterday.

    This makes it seem like it is supported
    https://github.com/cloudflare/agents/blob/main/design/think-sessions.md#multi-session-support

    The second bullet here sounds like it was removed in a refactor a few weeks ago
    #1237

    Image
  3. threepointone commented on Apr 21, 2026

    @threepointone
    Contributor

    I'll have a longer answer soon, but yesL the plan is to have support for multiple sessions/chats inside a Think instance, and I'm trying to figure out the best way atm. I'm currently leaning to having subAgents <> sessions. RFC (wip, might change some details: https://github.com/cloudflare/agents/blob/main/design/rfc-think-multi-session.md)

  4. threepointone commented on Apr 21, 2026

    @threepointone
    Contributor

    I'm at a conference all week so it'll take a bit. I'll aim for something out there next week / as soon as I can.

  5. barnjamin commented on Apr 30, 2026

    @barnjamin

    Heads up @Konan69, the assistant example now allows for multiple chat sessions, each using a (separate) Think agent.

  6. threepointone commented on Apr 30, 2026

    @threepointone
    Contributor

    yeah this works, and I've updated docs and examples. @Konan69 try it out, if it looks good I'll close this issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions