> ## Documentation Index
> Fetch the complete documentation index at: https://docs.maximem.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Strands Agents

> Synap as a native MemoryStore, short-term context hook, tools, and real-time anticipation feed for Strands Agents.

Give a [Strands Agents](https://strandsagents.com/) agent persistent memory through Synap. Unlike bolt-on integrations, Synap plugs into Strands' own extension points: a native `MemoryStore` for long-term memory, hooks for short-term context and real-time anticipation, and `@tool` functions for explicit control.

<Note>
  Requires Python 3.11+ and `strands-agents>=1.48`.
</Note>

## Overview

Four surfaces, each mapped onto a native Strands extension point. Adopt only the ones you need.

| Surface              | Strands extension point                     | Purpose                                                                        |
| -------------------- | ------------------------------------------- | ------------------------------------------------------------------------------ |
| `SynapMemoryStore`   | `MemoryStore` → `MemoryManager`             | Long-term memory: recall, automatic prompt injection, server-side extraction   |
| `SynapShortTermHook` | `HookProvider` (`BeforeInvocationEvent`)    | Inject Synap's working-memory summary before each turn                         |
| `create_synap_tools` | `@tool`                                     | Explicit `search_memory` / `store_memory` for agents not using `MemoryManager` |
| `SynapStreamHook`    | `HookProvider` (message + tool-call events) | Feed turns and tool intent onto Synap's gRPC Listen / anticipation stream      |

All four take an already-constructed `MaximemSynapSDK`. Your app owns the SDK, its credentials, and (for streaming) its connection lifecycle.

## Setup

<CodeGroup>
  ```bash pip theme={null}
  pip install maximem-synap-strands-agents strands-agents
  ```

  ```bash uv theme={null}
  uv add maximem-synap-strands-agents strands-agents
  ```
</CodeGroup>

## Basic integration

Register Synap as a Strands `MemoryStore`; `MemoryManager` then handles recall, automatic prompt injection, and extraction.

```python theme={null}
from strands import Agent
from strands.memory import MemoryManager
from maximem_synap import MaximemSynapSDK
from synap_strands_agents import SynapMemoryStore

sdk = MaximemSynapSDK(api_key="sk-...")
store = SynapMemoryStore(sdk, user_id="alice", customer_id="acme")

agent = Agent(memory_manager=MemoryManager(stores=[store]))
result = agent("What did we decide about the rollout?")
```

## Core concepts

### SynapMemoryStore

A structural implementation of Strands' `MemoryStore` protocol.

* **`search`** reads Synap's long-term layer via `sdk.fetch` and returns structured `MemoryEntry` objects (facts, preferences, episodes, emotions, temporal events). Reads degrade gracefully: a Synap blip returns `[]` rather than crashing recall.
* **`add_messages`** is the primary write sink. `MemoryManager` hands it conversation batches; it ingests the assembled transcript via `sdk.memories.create` (server-side extraction, no extra model call) under a **stable `document_id`**, so repeated extraction submissions update one document instead of duplicating.
* **`add`** ingests a single fact via `sdk.memories.create`.

Recorded conversation messages feed only Synap's short-term compaction, not the long-term extraction layer `search` reads, which is why writes deliberately go through `memories.create`, not conversation recording.

### SynapShortTermHook

Strands' `system_prompt` is a static string, so short-term context is injected via a hook on `BeforeInvocationEvent` (the one before-event whose messages are writable). It folds Synap's working-memory summary into the current turn's first user message.

```python theme={null}
from synap_strands_agents import SynapShortTermHook

agent = Agent(
    system_prompt="You are a support agent.",
    hooks=[SynapShortTermHook(sdk, conversation_id="conv_abc")],
)
```

`conversation_id` is required and explicit. Empty context is a no-op; SDK failures are swallowed by default (`on_error="fallback"`), or set `on_error="raise"` for strict environments.

<Note>
  Strands exposes no ephemeral per-call injection hook to user code, so injected context becomes part of the conversation the agent persists. The hook folds one context block into each turn's user message (with an idempotency guard) rather than adding throwaway turns.
</Note>

### create\_synap\_tools

For agents that want explicit control instead of `MemoryManager`. Returns `search_memory` and `store_memory` as Strands `@tool` functions.

```python theme={null}
from synap_strands_agents import create_synap_tools

agent = Agent(
    system_prompt="You are a helpful assistant.",
    tools=create_synap_tools(sdk, user_id="alice", customer_id="acme"),
)
```

### SynapStreamHook

Makes the agent a participant in Synap's real-time anticipation pipeline by feeding its turns and tool-call intent onto the gRPC Listen stream. See [Advanced patterns](#advanced-patterns).

## Complete example: multi-user support agent

```python theme={null}
from strands import Agent
from strands.memory import MemoryManager
from maximem_synap import MaximemSynapSDK
from synap_strands_agents import SynapMemoryStore, SynapShortTermHook

sdk = MaximemSynapSDK(api_key="sk-...")

def build_agent(user_id: str, conversation_id: str) -> Agent:
    store = SynapMemoryStore(sdk, user_id=user_id, customer_id="acme",
                             conversation_id=conversation_id)
    return Agent(
        system_prompt="You are a concise, friendly support agent.",
        memory_manager=MemoryManager(stores=[store]),
        hooks=[SynapShortTermHook(sdk, conversation_id=conversation_id)],
    )

agent = build_agent("alice", "conv_alice_001")
print(agent("Remind me what plan I'm on and my open ticket."))
# → recalls prior context from Synap, answers, and extracts new memories.
```

## Advanced patterns

### Real-time anticipation with the Listen stream

`SynapStreamHook` feeds the agent's turns and tool-call intent onto Synap's gRPC Listen stream so the Anticipation Agent can pre-fetch context before the agent asks. Your app owns the stream lifecycle; the hook only feeds an already-open stream and no-ops when none is active. See [Real-Time Anticipation](/concepts/real-time-anticipation) for the model, and [Real-Time Anticipation in a Server](/patterns/real-time-anticipation-server) for running it in a multi-tenant process.

```python theme={null}
import uuid
from synap_strands_agents import SynapStreamHook, SynapShortTermHook

conversation_id = str(uuid.uuid4())              # must be a valid UUID

await sdk.instance.listen()                      # app opens the stream
hook = SynapStreamHook(sdk, conversation_id=conversation_id, user_id="alice")
agent = Agent(hooks=[hook, SynapShortTermHook(sdk, conversation_id=conversation_id)])
# ... run the agent ...
await sdk.instance.stop_listening()              # app closes it on shutdown
```

<Note>
  Use a real UUID here. `send_message()` does not validate `conversation_id`, so a free-form id like `"conv_abc"` appears to work on the stream, but every other call that takes a `conversation_id` (`fetch`, `record_message`, compaction) rejects it with `InvalidConversationIdError`, leaving you with a conversation you cannot read back.
</Note>

<Warning>
  Run on one event loop. The stream's background tasks bind to the loop `listen()` ran on: construct the SDK, call `listen()`, and run the agent on that same asyncio loop.

  The hook itself performs no ingestion: durable memory is `SynapMemoryStore` or the tools. Note, though, that the turns it feeds onto the stream are persisted server-side and are promoted into long-term memory when the conversation compacts, so pairing the hook with a per-turn store extracts the same content twice. See [What the stream does to memory](/concepts/real-time-anticipation#what-the-stream-does-to-memory).
</Warning>

### Not `SessionManager`

This integration does not implement Strands' `SessionManager` / snapshot storage. That persists opaque conversation snapshots for replay, a different concern from Synap's semantic memory. Use `FileSessionManager` / `S3SessionManager` for durable sessions *and* a `SynapMemoryStore` for memory; they compose.

## Going further

* **Error policy.** Reads (`search`, `search_memory`) degrade and return empty; writes (`add`, `add_messages`, `store_memory`) raise `SynapIntegrationError`; stream sends log and never raise.
* **Scoping.** `user_id` and `customer_id` flow straight to Synap; per-user isolation and B2C/B2B scope are derived server-side from the ids you pass.

## Next steps

<CardGroup cols={2}>
  <Card title="SDK: Ingestion" icon="upload" href="/sdk/ingestion">
    How `memories.create` extraction and conversation recording differ.
  </Card>

  <Card title="Integrations overview" icon="grid-2" href="/integrations/overview">
    Every framework Synap plugs into.
  </Card>
</CardGroup>
