Skip to content

Storage overview

AgentSquad separates memory from routing. Each agent receives its own scoped history; the Orchestrator also reads a merged cross-agent view so the Classifier can see the full conversation when selecting the next agent.

public protocol ChatStorage: Sendable {
func fetch(
userId: String,
sessionId: String,
agentId: String,
maxMessages: Int?
) async throws -> [ConversationMessage]
func save(
_ message: ConversationMessage,
userId: String,
sessionId: String,
agentId: String,
maxMessages: Int?
) async throws
func saveMessages(
_ messages: [ConversationMessage],
userId: String,
sessionId: String,
agentId: String,
maxMessages: Int?
) async throws
/// Merged, timestamp-ordered history across all agents; assistant messages `[agentId]`-prefixed.
func fetchAllChats(userId: String, sessionId: String) async throws -> [ConversationMessage]
}

fetch / save / saveMessages are keyed by a (userId, sessionId, agentId) triple and feed the selected agent. fetchAllChats returns a merged, timestamp-ordered view across all agents for the session; assistant messages are prefixed with [agentId] so the Classifier can attribute them.

See Messages & events for the ConversationMessage type.

public enum ChatStorageDefaults {
public static let maxMessages = 100 // counts messages, not pairs
}

maxMessages counts individual messages, not user/assistant pairs. The framework rounds any budget down to an even number so a user/assistant pair is never split. Pass nil to keep an unbounded history.

Two helpers are provided as protocol extensions — call them in your own store, do not reimplement them:

  • trimToEvenPairs(_:maxMessages:) — trims to the most recent maxMessages, rounding down to even.
  • isConsecutiveSameRole(_:_:) — returns true when a new message repeats the last stored role; stores drop such saves.

Passing store: nil to the Orchestrator or voice assistant disables persistence entirely — every session starts fresh. Useful during development or for ephemeral flows.


StorePlatformPersistenceMulti-agent [agentId] attribution
InMemoryChatStorageiOS 16+Session onlyNo
FileChatStorageiOS 16+Disk (JSON)Yes
DeviceChatStorageiOS 17+ / macOS 14+Disk (SwiftData)Yes
store: nilAnyNone

For voice sessions the same stores apply — pass the chosen instance to the voice assistant’s store: parameter exactly as you would for a text Orchestrator.

Any of them can be wrapped in TransformingChatStorage to scrub PII or reshape messages before they are persisted, or in SummarizingChatStorage to automatically compress long histories so agent context stays small.


Need a remote database, an encrypted keychain bucket, or a shared app-group container? Write a custom store.