Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
a35345e
feat(memory): preserve durable Agent tool history
icecrasher321 Sep 19, 2026
94da520
docs(agent): explain durable tool history and context limits
icecrasher321 Sep 19, 2026
53160eb
feat(memory): bound durable context and retrieve retained tool detail
icecrasher321 Sep 19, 2026
18557c1
Merge staging and regenerate durable memory migration
icecrasher321 Sep 19, 2026
40cf7d5
fix(memory): validate checkpoint recovery and bounded summary coverage
icecrasher321 Sep 19, 2026
3174e38
Merge remote-tracking branch 'origin/staging' into codex/durable-agen…
icecrasher321 Sep 19, 2026
dcebd6b
chore(db): format durable memory migration metadata
icecrasher321 Sep 19, 2026
8880eca
Merge remote-tracking branch 'origin/staging' into codex/durable-agen…
icecrasher321 Sep 19, 2026
8b21ea4
Merge remote-tracking branch 'origin/staging' into codex/durable-agen…
icecrasher321 Sep 19, 2026
c738da8
docs(memory): remove redundant internal README
icecrasher321 Sep 19, 2026
2a6faa6
Merge remote-tracking branch 'origin/staging' into codex/durable-agen…
icecrasher321 Sep 19, 2026
bfc2c4e
fix(memory): preserve tool loops and harden durable history
icecrasher321 Sep 19, 2026
38d7552
fix(memory): preserve stream usage and bound portable history
icecrasher321 Sep 19, 2026
40a8923
fix(memory): surface bounded history and validate replay inputs
icecrasher321 Sep 19, 2026
f912fde
fix(memory): retain legacy function call exchanges
icecrasher321 Sep 19, 2026
7ccce6a
fix(memory): admit only complete stored tool exchanges
icecrasher321 Sep 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/test-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,8 @@ jobs:
bunx vitest run
lib/table/rows/secret-provenance.postgres.test.ts
lib/memory/message-provenance.postgres.test.ts
lib/memory/conversation-store.postgres.test.ts
lib/memory/summary-store.postgres.test.ts
executor/handlers/agent/memory-harness.postgres.test.ts

- name: Verify Search vector projection upgrade in PostgreSQL
Expand Down
14 changes: 9 additions & 5 deletions apps/docs/content/docs/academy/agents/memory.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ import { AV_MEMORY_WORKFLOW } from '@/components/workflow-preview/academy-video-
<div className="mt-8 grid grid-cols-1 items-start gap-10 lg:grid-cols-[1fr_300px]">
<div>

By default, an agent keeps nothing between runs: every conversation starts completely fresh. The Memory setting changes that: choose Conversation, give it a conversation ID, and everything said under that key is kept and loaded back before the model runs.
By default, an agent keeps nothing between runs: every conversation starts completely fresh. The Memory setting changes that: choose Conversation, give it a conversation ID, and the agent saves the conversation under that key. Later runs load history according to the memory mode and the model's context limits.

When durable tool history is enabled for your workspace, that history also includes completed tool calls and their results or errors. The agent can remember what it did, such as looking up an order, even when the run failed before giving its final answer. See the [Agent block's memory settings](/workflows/blocks/agent#memory) for tool-history and retry behavior.

Uploaded attachments stay linked to the message that included them. Memory stores file references; each later run reads the accessible files again and prepares them for the selected provider. Attachments follow the selected memory window and the source file's storage retention. A replay can include up to 20 attachment references; use a smaller memory window for longer file-heavy conversations. Files omitted by older versions of memory need to be attached again.

Expand All @@ -32,7 +34,7 @@ Uploaded attachments stay linked to the message that included them. Memory store
},
{
title: 'Recall happens before the model runs',
body: 'On the next run, everything stored under the key is loaded back into the conversation first: so the agent answers like no time has passed.',
body: 'On the next run, selected history under the key is loaded into the conversation before the model answers.',
},
{
title: 'Keys are separate threads',
Expand Down Expand Up @@ -65,20 +67,22 @@ Here is the agent from the video with Memory set on the block:

## The same agent, with and without memory

The video runs the same agent twice, side by side: once with no memory and once with the conversation ID. The same follow-up question arrives in both. Without the key, the agent starts from zero and has to ask for everything again; with it, everything stored under the key was loaded back before the model saw the new message, and the answer picks up exactly where yesterday stopped.
The video runs the same agent twice, side by side: once with no memory and once with the conversation ID. The same follow-up question arrives in both. Without the key, the agent starts from zero and has to ask for everything again; with it, the earlier conversation supplies the context needed to answer the follow-up.

## When conversations grow

Memory can also be a sliding window, keeping the most recent messages, or the most recent tokens, while the oldest quietly fall away. The stored transcript keeps every turn; the window controls how much of it rides into the model on each run.

Tool exchanges stay together when history is selected. They do not each count as a message in a message window, but their contents still use input tokens. Large results may appear as previews, and history is not automatically summarized. A token window gives more direct control over recalled context than a message count; neither setting caps the total cost of a run that makes further model or tool calls.

## When to use memory

Enable memory when a follow-up needs earlier conversation context, such as a support ticket or sales conversation. Keep classification and extraction stateless when each input contains everything the task needs.

<FAQ items={[
{ question: "What memory modes does the Agent block support?", answer: "Conversation keeps every turn under a conversation ID. Sliding window (messages) keeps the most recent messages, and sliding window (tokens) keeps as much recent history as fits a token budget." },
{ question: "What memory modes does the Agent block support?", answer: "Conversation recalls stored history subject to history-loading and model context limits. Sliding window (messages) selects the most recent messages, and sliding window (tokens) selects recent history against a token budget. Tool-call groups stay together." },
{ question: "What should I use as a conversation ID?", answer: "Any stable key that identifies the thread: a customer ID, a ticket number, a channel ID. Runs that share the key share the conversation; runs with different keys never see each other's history." },
{ question: "Does agent memory cost extra tokens?", answer: "Recalled history is loaded into the model's context, so it is billed as input tokens like any other message. Sliding-window modes exist to cap that cost on long-running conversations." },
{ question: "Does agent memory cost extra tokens?", answer: "Recalled messages, tool arguments, and tool results use input tokens. Sliding-window modes reduce how much history is recalled, but a message count is not a token limit and a memory window does not cap the total cost of the run." },
{ question: "Where is the conversation stored?", answer: "In your Sim workspace, as a stored transcript per conversation ID. You can inspect it, and the agent reads from it automatically before each run." },
]} />

Expand Down
24 changes: 21 additions & 3 deletions apps/docs/content/docs/workflows/blocks/agent.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,30 @@ To pick the mode when the workflow runs, use the switch next to Permission Mode
Built-in conversation memory, kept across runs by a conversation ID:

- **None.** Each run is independent.
- **Conversation.** The full history for that conversation ID.
- **Conversation.** Stored history for that conversation ID, subject to history-loading and model context limits.
- **Sliding window (messages).** The most recent N messages.
- **Sliding window (tokens).** Recent messages up to a token budget.
- **Sliding window (tokens).** Recent history selected against a token budget, keeping tool exchanges together.

Memory needs a conversation ID to persist between runs. For memory that's shared across workflows or managed as its own store, use the [Memory](/integrations/memory) block instead.

When durable tool history is enabled for your workspace, memory also keeps the assistant messages that led to tool calls, the calls' original arguments, and their results or errors. Later runs can use completed tool exchanges even if the run that produced them failed before answering. Older conversations remain readable; tool history is captured on new runs after the feature is enabled.

Tool calls and their results are selected together, including parallel calls. They do not each use another slot in a message-count window, but their arguments and results still consume input tokens. Use a token window when the amount of recalled context matters more than the number of messages. Windowing changes what the model receives, not what is stored.

Large results are retained separately, with their first 8,000 characters in model context and a notice when the result is truncated. The built-in `agent_memory_read` tool lets the agent search retained history and read omitted result details in small pages. It can only read the current conversation. When exact details matter, ask the agent to check the original result instead of relying on its preview.

Sim normally targets up to 16,000 estimated tokens of recalled history, subject to your memory window and the model's available context. Large or difficult-to-tokenize content uses a conservative estimate. It checks the input before every model generation, including generations after tool calls and on fallback models, leaving room for instructions, tool definitions, attachments, and output. The current request and required tool exchanges stay intact. If their estimated size uses up the available budget, Sim omits optional history and still sends the current request; the provider enforces its actual context limit. These estimates guide recalled context per generation, not the total tokens used across a run.

When a generation would omit older history, Sim can create a concise summary while keeping recent exchanges, including during long tool loops. Summaries can omit details and do not replace the stored conversation. Generating one uses an additional model call whose tokens and cost are included in the Agent's usage; a cached summary can be reused when its source history is unchanged. If summarization is unavailable, the Agent continues with bounded history selection.

The Memory API and Memory block still return plain conversation messages. Internal tool history, retry state, and cached summaries are not added to their `data` responses.

#### Retries and fallbacks

With durable tool history enabled, retries and fallback models continue the same Agent invocation using recorded tool results. For example, if a tool returns an order number and final generation fails, the fallback receives that result without calling the tool again. A new workflow execution or loop iteration is a separate invocation.

A recorded terminal outcome is kept even when its stored details become unavailable; the Agent does not repeat that action merely to recover the missing details. A call whose outcome was not recorded can execute again, including when an external action succeeded just before a failure. Use tools that safely handle repeated requests for actions that must not happen twice. If durable history is disabled or persistence is unavailable, saved-progress recovery is not guaranteed. This does not restart crashed workflows, override cancellation, or retry failures marked nonretryable. A failure after streaming output has started is not restarted on another model.

### Response Format

Give the agent a JSON Schema to force structured output. The response is constrained to match the schema, and each field becomes its own output you read by name, like `<agent.sentiment>`. Without a response format, the agent returns plain text in `content`.
Expand Down Expand Up @@ -88,7 +106,7 @@ Some settings live under advanced, or appear only for models that support them:
- **Prompt caching.** For Anthropic Claude models, reuses the system prompt and tool definitions between runs instead of re-reading them every time. Cached input costs a tenth of the normal rate, but writing the cache costs 1.25x, so leave it off for one-off runs and turn it on when the same agent runs repeatedly. The cache covers a prefix only if it reaches 1,024 tokens (2,048 on Haiku) — below that Anthropic ignores it and nothing changes. Entries expire after five minutes of no use.
- **API key.** Your key for the chosen provider. Hidden on hosted Sim, which supplies one.
- **Fallback models.** An ordered list of up to five models to try when the request to the selected model fails, whether the provider is overloaded, rate-limited, or down. Sim tries the 2nd choice, then the 3rd, and so on, once each, and `<agent.model>` reports the model that answered. On hosted Sim, hosted models use your workspace's BYOK or platform credentials; local and self-hosted installations may still require a key. A model that needs its own key takes it from a workspace environment variable you pick on the row; a model on the same provider as the selected model reuses the block's key. A stored row key stops applying when its key field is hidden. Providers that require family-specific credentials, such as Vertex, can only be fallbacks for a selected model of the same family. The Auto model cannot be a fallback. A fallback runs with the selected model's settings where its provider accepts them: temperature and max output tokens are clamped to the fallback's limits, and when the fallback has a reasoning effort, thinking level, or verbosity setting that the selected model's value does not fit, the row shows that field so you can pick a value for it; leave it empty and the provider's default applies.
- **Retry on fail.** Retries the selected model after a failure, up to a maximum number of tries with a wait between them. When its tries run out, the fallback models are tried in order, once each, with no wait before the first of them. A fallback is never retried. A failure that happens after the model already called a tool runs that conversation again on the next try or the next model, so keep fallbacks and retry off for agents whose tools must not repeat.
- **Retry on fail.** Retries the selected model after a failure, up to a maximum number of tries with a wait between them. When its tries run out, the fallback models are tried in order, once each, with no wait before the first of them. A fallback is never retried. See [Retries and fallbacks](#retries-and-fallbacks) for how recorded tool results are reused and when a tool can execute again.

OpenAI and Gemini cache automatically at no extra cost and need no setting; their discount is already reflected in what you are charged.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ During a live tool loop, the model can’t be classified mid-turn: text it emits
- **Clients sending the protocol header** (no event policy required) receive answer text as `chunk` frames **live**, token by token. If the turn then resolves to tool calls, a `chunk_reset` frame tells the client to discard that block’s streamed text — the final turn re-streams live after tools settle. Append `chunk`, honor `chunk_reset`, and the displayed answer always converges to the block’s final content.
- **Clients without the header** never see provisional text: only settled final-turn text is emitted as `chunk`, delivered in one piece when the turn completes. Honoring `chunk_reset` is what buys live cadence, so send the header if you want it.

Logs, memory, and the blocks `content` output always contain final-turn text only — intermediate preamble is never persisted.
The block's `content` output and plain-message memory view contain the final response text. With [durable tool history](/workflows/blocks/agent#memory) enabled, internal memory also preserves complete assistant messages that lead to tool calls and their results. It records completed provider messages, not individual streamed text deltas, and does not add these internal exchanges to the Memory API's `data` response.

### Abort

Expand Down
9 changes: 0 additions & 9 deletions apps/sim/executor/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -242,15 +242,6 @@ export const MCP = {
TOOL_PREFIX: 'mcp-',
} as const

export const MEMORY = {
DEFAULT_SLIDING_WINDOW_SIZE: 10,
DEFAULT_SLIDING_WINDOW_TOKENS: 4000,
CONTEXT_WINDOW_UTILIZATION: 0.9,
MAX_CONVERSATION_ID_LENGTH: 255,
MAX_MESSAGE_CONTENT_BYTES: 100 * 1024,
MAX_REPLAY_FILE_REFERENCES: 20,
} as const

export const ROUTER = {
DEFAULT_MODEL: 'claude-sonnet-5',
DEFAULT_TEMPERATURE: 0,
Expand Down
Loading
Loading