Skip to content

feat(experimental): session-memory and multi-session-agent examples#1168

Open
mattzcarey wants to merge 5 commits intofeat/session-managerfrom
feat/session-examples
Open

feat(experimental): session-memory and multi-session-agent examples#1168
mattzcarey wants to merge 5 commits intofeat/session-managerfrom
feat/session-examples

Conversation

@mattzcarey
Copy link
Contributor

@mattzcarey mattzcarey commented Mar 23, 2026

Summary

Two experimental examples demonstrating the Session API builder:

session-memory — Single session

  • Session.create(this).withContext("memory", ...).withContext("todos", ...).withCachedPrompt()
  • Context blocks (memory + todos) with update_context tool
  • Non-destructive compaction with head/tail protection
  • Chat UI with tool call rendering

multi-session-agent — Multiple sessions

  • SessionManager.create(this).withContext("soul", ...).withContext("memory", ...).withCachedPrompt()
  • Sidebar with chat list, create/delete, cross-session search
  • Per-session context blocks with auto-namespaced providers
  • Tool merging: { ...await session.tools(), ...manager.tools() }

Both use Workers AI (Kimi K2.5) via workers-ai-provider.

Stack

1/4 — #1166 Session API core
2/4 — #1167 SessionManager
3/4 — this PR
4/4 — #1169 Think integration (depends on this)

Test plan

  • Deploy session-memory example, verify context blocks persist across DO eviction
  • Deploy multi-session-agent, verify session isolation and cross-session search

@changeset-bot
Copy link

changeset-bot bot commented Mar 23, 2026

⚠️ No Changeset found

Latest commit: 8aa9411

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Copy link
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 potential issues.

View 4 additional findings in Devin Review.

Open in Devin Review

Comment on lines +43 to +49
private compactFn = createCompactFunction({
summarize: (prompt) =>
generateText({ model: this.getAI(), prompt }).then((r) => r.text),
protectHead: 1,
minTailMessages: 2,
tailTokenBudget: 100,
});
Copy link
Contributor

@devin-ai-integration devin-ai-integration bot Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Shared compactFn closure leaks previousSummary state across sessions

compactFn is a single class property created by createCompactFunction() at server.ts:33-39. Inside the closure, previousSummary is a mutable variable captured once (packages/agents/src/experimental/memory/utils/compaction-helpers.ts:435). When session A is compacted, previousSummary is set to session A's summary (compaction-helpers.ts:463). If session B is compacted next, buildSummaryPrompt receives session A's summary as the previousSummary parameter, causing the LLM to incorporate session A's context into session B's compaction summary — cross-session data contamination.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@@ -0,0 +1,329 @@
import { useState, useRef, useCallback } from "react";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Missing useEffect for auto-scroll leaves messagesEndRef unused

messagesEndRef is created at line 93 and attached to a sentinel <div> at line 293, clearly intended for auto-scrolling to the bottom when new messages arrive. However, unlike the session-memory client (experimental/session-memory/src/client.tsx:99) which has useEffect(() => { messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }); }, [messages]);, the multi-session-agent client never imports useEffect and never calls scrollIntoView. The chat area will not auto-scroll when new messages are added.

Suggested change
import { useState, useRef, useCallback } from "react";
import { useState, useEffect, useRef, useCallback } from "react";
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 new potential issues.

View 10 additional findings in Devin Review.

Open in Devin Review

Comment on lines +120 to +134
if (removed.length > 0) {
const summaryMsg = compacted.find((m) =>
m.id.startsWith("compaction-summary-")
);
if (summaryMsg) {
const summaryText = summaryMsg.parts
.filter((p) => p.type === "text")
.map((p) => (p as { text: string }).text)
.join("\n");

this.session.addCompaction(
summaryText,
removed[0].id,
removed[removed.length - 1].id
);
Copy link
Contributor

@devin-ai-integration devin-ai-integration bot Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Second+ compaction silently fails because addCompaction stores virtual overlay message IDs that raw DB lookup can never match

The compact method calls session.getHistory() (line 131) which returns messages after applying compaction overlays — including virtual messages with IDs like compaction_<uuid> (packages/agents/src/experimental/memory/session/providers/agent.ts:297). After the first compaction, a subsequent compact call finds these virtual messages in its removed list (line 137), and passes removed[0].id (a virtual compaction_<uuid> ID) as fromMessageId to addCompaction (line 150). When getHistory() is later called, applyCompactions (agent.ts:284-315) iterates raw DB messages looking for this virtual ID — it will never find it, so the second compaction overlay is silently skipped. This means messages that should have been compacted persist in the history, causing the context window to grow unboundedly after the first compaction.

Trace of the failure
  1. First compaction: addCompaction(summary, realMsgId, realMsgId) → works correctly
  2. getHistory() now returns: [msg1, compaction_<uuid>, msg4, ..., msg12] (overlay applied)
  3. Second compaction's removed[0].id = "compaction_<uuid>" (virtual ID)
  4. addCompaction(summary, "compaction_<uuid>", msg10.id) stores this
  5. Next getHistory()applyCompactions scans raw DB messages for fromMessageId = "compaction_<uuid>" → no match → overlay never applied
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@mattzcarey mattzcarey force-pushed the feat/session-manager branch from 265f0dd to 4a81e4d Compare March 23, 2026 23:40
@mattzcarey mattzcarey force-pushed the feat/session-examples branch 2 times, most recently from caf066c to c45ac4a Compare March 24, 2026 00:17
@mattzcarey mattzcarey force-pushed the feat/session-manager branch from 068ba87 to 4a29fa3 Compare March 24, 2026 00:24
Two examples demonstrating the Session API:

session-memory: Single-session chat agent
- Context blocks (soul + memory + todos) with frozen system prompt
- update_context tool — AI saves facts to memory
- Non-destructive compaction with hermes-style summary
- Tool calls rendered inline (expandable cards)
- Compact button to demo compaction

multi-session-agent: Multi-session chat with sidebar UI
- SessionManager for create/list/delete/switch sessions
- Each session has independent memory and compaction
- Cross-session FTS search via manager.tools()
- Sidebar with chat list, search, create/delete
…Manager

Update session-memory and multi-session-agent examples to use
Session.create() and SessionManager.create() instead of manual
provider construction. Much cleaner DX.
@mattzcarey mattzcarey force-pushed the feat/session-examples branch from c45ac4a to 8aa9411 Compare March 24, 2026 00:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant