In the previous chapter, Memory Manifest Injection, we taught our agent to check the "pantry" (existing files) before going shopping.
Now we face a new problem: How much of the conversation should the agent read?
Imagine you are reading a very long book.
If an AI reads the entire conversation history every time it tries to save a memory, two bad things happen:
To solve this, we use a Cursor. Think of it exactly like a bookmark in a book.
In our code, this bookmark is a variable called lastMemoryMessageUuid.
Scenario:
Goal: When the agent wakes up for Turn 2, it should look at the bookmark and realize: "I have already processed Turn 1. I only need to analyze Turn 2."
Here is how the cursor moves over time.
The logic for this is simple but powerful. It happens in three steps within extractMemories.ts.
First, we need a place to store our bookmark. We use a variable that sits outside the main function loop so it remembers its value between turns.
// extractMemories.ts
// This variable persists across the entire session
let lastMemoryMessageUuid: string | undefined
Explanation: Initially, it is undefined (no bookmark yet). As the chat progresses, this will hold the ID of the last message we successfully analyzed.
When the extraction process starts, we don't send the full conversation to the background agent. We only tell it how many new messages to look at.
We use a helper function to count messages that happened after our bookmark.
// extractMemories.ts
// 'messages' is the full history
// 'lastMemoryMessageUuid' is our bookmark
const newMessageCount = countModelVisibleMessagesSince(
messages,
lastMemoryMessageUuid,
)
Explanation: If the history has 50 messages, but our bookmark is at message 48, newMessageCount will be 2.
This number is then injected into the prompt (remember Chapter 1?) so the agent sees:
"Analyze the most recent ~2 messages..."
Critically, we only move the bookmark if the extraction process finishes successfully. If the agent crashes or gets stuck, the bookmark stays put, and we try again next time.
// extractMemories.ts (inside runExtraction)
// ... agent performs work ...
// Get the very last message in the current batch
const lastMessage = messages.at(-1)
// Update the bookmark to this new ID
if (lastMessage?.uuid) {
lastMemoryMessageUuid = lastMessage.uuid
}
Explanation: We successfully processed up to lastMessage. We save its ID. Next time the user talks, we will start counting after this ID.
How does countModelVisibleMessagesSince actually work? It iterates through the list of messages looking for our bookmark.
// extractMemories.ts (Simplified)
function countModelVisibleMessagesSince(allMessages, bookmarkId) {
// If no bookmark, count everything!
if (!bookmarkId) return allMessages.length
let foundBookmark = false
let count = 0
for (const msg of allMessages) {
// 1. We are still looking for the bookmark...
if (!foundBookmark) {
if (msg.uuid === bookmarkId) foundBookmark = true
continue // Skip the bookmark itself
}
// 2. We passed the bookmark! Start counting.
count++
}
return count
}
bookmarkId.This architecture creates a Stateful experience on top of a Stateless LLM.
The Incremental Context Cursor is the engine that keeps our memory system efficient.
newMessageCount.Now we have a Prompt (Chapter 1), a File Manifest (Chapter 2), and a Cursor (Chapter 3). We have all the data ready.
But waitβif we run this logic, won't the user have to wait for the memory agent to finish thinking before they can type again? We don't want to block the chat!
In the next chapter, we will learn how to run this process in the background using a Forked Agent.
Next Chapter: Forked Agent Execution
Generated by Code IQ