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.


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