> ## 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.

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

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

<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 feeds real-time *signal*; it does not durably store memories (that's `SynapMemoryStore` or the tools).
</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>
