Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ better-memory has two storage backends. Pick one:
| **`sqlite`** (default) | Single-machine usage; full offline operation; no cloud cost. | None — works out of the box. |
| **`agentcore`** | Multi-machine syncing; managed extraction by AWS; team-shared memory bucket. | Requires an AWS account with Bedrock AgentCore Memory available in your chosen region (`init --region` defaults to `eu-west-2`). See [AgentCore setup](website/agentcore-setup.md). |

Switching to agentcore is done by `better-memory agentcore init`, which provisions the AWS memories and activates the backend by writing `{"storage_backend": "agentcore"}` to `$BETTER_MEMORY_HOME/settings.json`. The MCP server, hooks, and CLI all resolve the backend the same way: `BETTER_MEMORY_STORAGE_BACKEND` env var if set (always wins), else `settings.json`, else `sqlite`. To revert, remove the `storage_backend` key from `settings.json` or set the env var to `sqlite`. Switching is one-way today — there is no bulk migration tool (deferred; clean start in agentcore mode is the supported path).
Switching to agentcore is done by `better-memory agentcore init`, which provisions the AWS memories and activates the backend by writing `{"storage_backend": "agentcore"}` to `$BETTER_MEMORY_HOME/settings.json`. The MCP server, hooks, and CLI all resolve the backend the same way: `BETTER_MEMORY_STORAGE_BACKEND` env var if set (always wins), else `settings.json`, else `sqlite`. To revert, remove the `storage_backend` key from `settings.json` or set the env var to `sqlite`. To carry existing sqlite memory across, `better-memory agentcore migrate` bulk-copies your distilled reflections and semantic memories into the AgentCore memories (idempotent, re-runnable; `--dry-run` previews the plan). Migration and activation are independent — `migrate` only writes records to AWS and never flips the backend, so use `init` (or set `storage_backend` yourself) to actually switch. See [AgentCore setup](website/agentcore-setup.md).

`agentcore` mode needs the optional dependency group: `pip install 'better-memory[agentcore]'` (or `uv pip install '.[agentcore]'`). Sqlite-only installs skip boto3 entirely.

Expand Down
22 changes: 22 additions & 0 deletions better_memory/cli/_agentcore_strategies.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,24 @@
{"key": "status", "type": "STRING"},
]

# userPreference (semantic) memoryRecordSchema. Unlike the episodic schema
# (which governs only AWS-EXTRACTED records), this top-level schema governs
# CLIENT BASE writes: keys declared here are retained on create, survive
# updates, and are listable; UNDECLARED keys (e.g. source_row_id) are silently
# dropped by AWS (design §1b, proven live). The migration idempotency key
# source_row_id therefore MUST be declared here or semantic reconcile-by-
# source_row_id (design §3.2/§5.3) breaks — the migrator could not detect an
# already-migrated row and would duplicate it on re-run.
#
# OPERATIONAL NOTE for the migrate/--provision path (T5): this schema is baked
# into the userPreferenceMemoryStrategy block at CREATE_MEMORY time. A semantic
# memory PROVISIONED BEFORE source_row_id was added here does NOT have it
# declared, so client writes of source_row_id there are still dropped. Before
# writing, the migrate/--provision path must guarantee the target's live schema
# declares these keys — either widen it in place via update_memory_strategy
# (widening the metadataSchema; verify AWS permits post-hoc schema extension —
# unconfirmed as of §1b's open item) or re-provision a fresh semantic memory.
# Fresh `init` provisions with this (correct) schema and needs no migration.
SEMANTIC_METADATA_SCHEMA: list[dict] = [
{"key": "useful_count", "type": "NUMBER"},
{"key": "missed_count", "type": "NUMBER"},
Expand All @@ -47,6 +65,10 @@
{"key": "overlooked_count", "type": "NUMBER"},
{"key": "last_credited_at", "type": "STRING"},
{"key": "status", "type": "STRING"},
# Migration idempotency key (design §3.2). STRING: it holds the SQLite row
# id. Not server-indexed (indexed keys are frozen at provision, §5.3) —
# reconcile scans it client-side.
{"key": "source_row_id", "type": "STRING"},
]

INDEXED_KEYS: list[dict] = [
Expand Down
Loading