feat(experimental): SessionManager — multi-session lifecycle and search#1167
feat(experimental): SessionManager — multi-session lifecycle and search#1167mattzcarey wants to merge 6 commits intofeat/session-api-corefrom
Conversation
|
| deleteMessages(messageIds: string[]): void { | ||
| for (const session of this._sessions.values()) { | ||
| session.deleteMessages(messageIds); | ||
| } | ||
| } |
There was a problem hiding this comment.
🔴 deleteMessages silently no-ops when no sessions have been accessed via getSession()
SessionManager.deleteMessages iterates over this._sessions.values(), which is an in-memory cache populated only by prior getSession() calls. If no sessions have been accessed yet, the map is empty and the loop body never executes — messages remain in the database untouched. Even when sessions are cached, the same SQL DELETE FROM assistant_messages WHERE id = ${id} (packages/agents/src/experimental/memory/session/providers/agent.ts:182) runs redundantly once per cached session, since it doesn't filter by session_id. The method should execute the deletes directly against the database rather than depending on cached session instances.
Was this helpful? React with 👍 or 👎 to provide feedback.
1731b1e to
9b4b630
Compare
9b4b630 to
265f0dd
Compare
|
|
||
| export { AgentContextProvider } from "./providers/agent-context"; | ||
|
|
||
| export { SessionManager, type SessionInfo, type SessionManagerOptions } from "./manager"; |
There was a problem hiding this comment.
🔴 Missing changeset for public API addition of SessionManager
The AGENTS.md rule states: "Changes to packages/ that affect the public API or fix bugs need a changeset." This PR adds SessionManager, SessionInfo, and SessionManagerOptions to the public export of agents/experimental/memory/session (a published entry point in package.json). The .changeset/ directory contains only the auto-generated README.md and config.json — no changeset file was created for this change.
Was this helpful? React with 👍 or 👎 to provide feedback.
265f0dd to
4a81e4d
Compare
| upsert(sessionId: string, message: UIMessage, parentId?: string): string { | ||
| return this.append(sessionId, message, parentId); | ||
| } |
There was a problem hiding this comment.
🟡 upsert() silently drops updates to existing messages due to INSERT OR IGNORE semantics
upsert() at line 203-205 simply delegates to append(), which calls Session.appendMessage() → AgentSessionProvider.appendMessage() which uses INSERT OR IGNORE (providers/agent.ts:166). This means if a message with the same id already exists, the entire operation is silently ignored — the message content is NOT updated. A method named "upsert" should update the existing message if the ID already exists, not silently discard the new content. Callers relying on upsert semantics would lose data without any error.
Prompt for agents
In packages/agents/src/experimental/memory/session/manager.ts line 203-205, the upsert() method delegates to append(), which uses INSERT OR IGNORE and silently drops updates to existing messages. To implement true upsert semantics, upsert() should first check if the message exists (via session.getMessage(message.id)), and if so, call session.updateMessage(message) instead of appendMessage(). Alternatively, rename the method to something that doesn't imply update semantics, like appendIfNew().
Was this helpful? React with 👍 or 👎 to provide feedback.
agents
@cloudflare/ai-chat
@cloudflare/codemode
hono-agents
@cloudflare/shell
@cloudflare/think
@cloudflare/voice
@cloudflare/worker-bundler
commit: |
SessionManager for managing multiple named sessions: - create/get/getSession/list/delete/rename - Convenience: append/appendAll/getHistory/clearMessages by session ID - compactAndSplit — end session, create new one seeded with summary - addUsage — token tracking per session - search() — cross-session FTS (queries shared table directly) - tools() — returns session_search tool for LLM use - Uses assistant_* table names for Think backward compat - 8 new tests: create/get, list, delete, rename, search, search tool, context block proxies, AgentContextProvider persistence
…viders SessionManager.create(agent) returns a SessionManager with chainable config — .withContext(), .withCachedPrompt(), .maxContextMessages(). getSession(id) now uses Session.create().forSession(id) internally, so each session automatically gets namespaced provider keys (e.g. memory_chat-123, _system_prompt_chat-123). Removes sessionOptions from constructor — context and cachedPrompt are now top-level builder methods matching Session's API.
- upsert: check-then-update or insert (was silently dropping updates) - fork: pass parentSessionId to create() for session lineage - _ensureTable: also create assistant_fts (search could fail before any session access)
068ba87 to
4a29fa3
Compare
Summary
SessionManager: registry of named sessions with lifecycle (create/get/list/delete/rename)SessionManager.create(agent).withContext(...).withCachedPrompt()getSession(id)usesSession.create().forSession(id)internally — each session gets auto-namespaced provider keys (e.g.memory_chat-123,_system_prompt_chat-123)session_searchtoolsession.tools()→update_context,manager.tools()→session_searchStack
1/4 — #1166 Session API core
2/4 — this PR
3/4 — #1168 Examples (depends on this)
4/4 — #1169 Think integration (depends on 3)
Test plan