Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
c40c04d
feat(cli): add better-memory top-level CLI dispatcher with agentcore …
emp3thy May 27, 2026
6e74107
style(cli): drop unused subprocess/sys imports from test_main
emp3thy May 27, 2026
0146ebb
feat(cli): implement agentcore init — create both memories and write …
emp3thy May 27, 2026
3eaefe9
feat(cli): implement agentcore status — print memory + strategy state
emp3thy May 27, 2026
7f9cd7f
feat(cli): implement agentcore smoke — observe + retrieve verificatio…
emp3thy May 27, 2026
9f364e8
fix(cli): move _build_data_client into _handle_smoke try block
emp3thy May 27, 2026
2f4e5f7
feat(cli): stub agentcore migrate-from-sqlite with deferred-spec pointer
emp3thy May 27, 2026
4155a0c
feat(hooks): fire AgentCore closure event from Stop hook in agentcore…
emp3thy May 27, 2026
e7f8496
test(integration): real-AWS roundtrip for AgentCoreBackend (gated by …
emp3thy May 27, 2026
5d99550
fix(integration): register atexit BEFORE first create so orphans clea…
emp3thy May 27, 2026
e843df3
docs(readme): add storage-backends section pointing to agentcore setup
emp3thy May 27, 2026
6a720d6
docs(configuration): document agentcore env vars
emp3thy May 27, 2026
621abc5
docs(agentcore): publish setup walkthrough
emp3thy May 27, 2026
ffd01f2
docs(troubleshooting): publish agentcore common-error guide
emp3thy May 27, 2026
ac0ae29
docs(mcp-tools): annotate per-tool agentcore-mode behaviour
emp3thy May 27, 2026
703e40d
docs(architecture): add storage-backends section with sqlite-vs-agent…
emp3thy May 27, 2026
672dc80
style(cli): use collections.abc.Sequence instead of typing.Sequence
emp3thy May 27, 2026
aca1e3b
Merge remote-tracking branch 'origin/main' into worktree-agentcore-plan3
emp3thy May 30, 2026
0e1cd88
fix(cli): track raw memory_ids for orphan cleanup + guard smoke[0] index
emp3thy May 30, 2026
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
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,19 @@ It's a memory layer for Claude Code that runs entirely on your machine. Your obs

SQLite ships with Python; `sqlite-vec` is installed as a pip dependency — nothing else to set up.

## Storage backends

better-memory has two storage backends. Pick one:

| Backend | When to pick | Setup |
|---|---|---|
| **`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 AWS account with Bedrock AgentCore Memory enabled in `eu-west-2`. See [AgentCore setup](website/agentcore-setup.md). |

Switch backends via `BETTER_MEMORY_STORAGE_BACKEND=agentcore` (default: `sqlite`). The MCP server reads the env var at startup and dispatches accordingly. Switching is one-way today — there is no bulk migration tool (deferred; clean start in agentcore mode is the supported path).

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

## Quick start

```bash
Expand Down
5 changes: 5 additions & 0 deletions better_memory/cli/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
"""CLI subcommands for better-memory."""

from better_memory.cli.main import main

__all__ = ["main"]
103 changes: 103 additions & 0 deletions better_memory/cli/_agentcore_strategies.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
"""Memory-strategy definitions used by `agentcore init` and the smoke script.

Lifted from `scripts/agentcore_smoke.py` so the CLI and the smoke share a
single source of truth — diverging the two has bitten us before.
"""

from __future__ import annotations

# Episodic memory: extracts reflections from session events. Metadata schema
# carries the rating counters + polarity classification.
EPISODIC_METADATA_SCHEMA: list[dict] = [
{
"key": "polarity",
"type": "STRING",
"extractionConfig": {
"llmExtractionConfig": {
"definition": (
"Whether this reflection prescribes a positive practice "
"('do'), warns against a negative practice ('dont'), or "
"is informational only ('neutral')."
),
"llmExtractionInstruction": (
"Classify this reflection as 'do', 'dont', or 'neutral'."
),
"validation": {
"stringValidation": {
"allowedValues": ["do", "dont", "neutral"]
}
},
}
},
},
{"key": "useful_count", "type": "NUMBER"},
{"key": "missed_count", "type": "NUMBER"},
{"key": "ignored_count", "type": "NUMBER"},
{"key": "times_misled", "type": "NUMBER"},
{"key": "overlooked_count", "type": "NUMBER"},
{"key": "last_credited_at", "type": "STRING"},
{"key": "status", "type": "STRING"},
]

SEMANTIC_METADATA_SCHEMA: list[dict] = [
{"key": "useful_count", "type": "NUMBER"},
{"key": "missed_count", "type": "NUMBER"},
{"key": "ignored_count", "type": "NUMBER"},
{"key": "times_misled", "type": "NUMBER"},
{"key": "overlooked_count", "type": "NUMBER"},
{"key": "last_credited_at", "type": "STRING"},
{"key": "status", "type": "STRING"},
]

INDEXED_KEYS: list[dict] = [
{"key": "status", "type": "STRING"},
{"key": "last_credited_at", "type": "STRING"},
{"key": "overlooked_count", "type": "NUMBER"},
]

# Names — must match the AWS regex `[a-zA-Z][a-zA-Z0-9_]{0,47}` (no dashes!).
DEFAULT_EPISODIC_NAME = "better_memory_episodic"
DEFAULT_SEMANTIC_NAME = "better_memory_semantic"
DEFAULT_EPISODIC_STRATEGY_NAME = "episodicReflections"
DEFAULT_SEMANTIC_STRATEGY_NAME = "userPreference"

# Event TTL: episodic events are kept ~90 days (long enough to span a multi-
# month project); semantic records last ~365 days.
DEFAULT_EPISODIC_EVENT_EXPIRY_DAYS = 90
DEFAULT_SEMANTIC_EVENT_EXPIRY_DAYS = 365


def episodic_strategy_block(
*,
name: str = DEFAULT_EPISODIC_STRATEGY_NAME,
) -> dict:
return {
"episodicMemoryStrategy": {
"name": name,
"namespaces": ["projects/{actorId}/reflections/"],
"namespaceTemplates": ["projects/{actorId}/reflections/"],
"reflectionConfiguration": {
"namespaces": ["projects/{actorId}/reflections/"],
"namespaceTemplates": ["projects/{actorId}/reflections/"],
"memoryRecordSchema": {
"metadataSchema": EPISODIC_METADATA_SCHEMA
},
},
}
}


def semantic_strategy_block(
*,
name: str = DEFAULT_SEMANTIC_STRATEGY_NAME,
) -> dict:
return {
"userPreferenceMemoryStrategy": {
"name": name,
"namespaces": ["projects/{actorId}/semantic/"],
"namespaceTemplates": ["projects/{actorId}/semantic/"],
"memoryRecordSchema": {
"metadataSchema": SEMANTIC_METADATA_SCHEMA
},
}
}
Loading