Breaking changes in 2.0.0
Every call, route and tool that changes or goes away in SmartMemory 2.0.0, with the replacement for each.
Not released yet. SmartMemory 2.0.0 has not been released. This is the planned list. Check it against the release notes when 2.0.0 ships.
Planned for 2.1. Four changes on this page are not in 2.0.0. They move to SmartMemory 2.1: the quick
202save return (#1), the removal of/memory/ingest/fulland/ingest/async/{run_id}(part of #3), and the revision check on content edits (#6). Each one is marked "planned for 2.1" below. None is dropped.
The list
| # | Change | Who is affected |
|---|---|---|
| 1 | Planned for 2.1: saves return quickly with 202 for every caller | REST, SDK and MCP callers that assume a save is finished when the call returns |
| 2 | The save receipt replaces the id string | Python and JS SDK callers |
| 3 | Three routes are removed with no alias (public /memory/ingest/structured in 2.0.0, the other two planned for 2.1) | REST callers of /memory/ingest/full, /ingest/async/{run_id} and public /memory/ingest/structured |
| 4 | Six MCP save tools are removed and replaced by three | Agents, hooks and prompts that name the old tools |
| 5 | Public pipeline-stage calls return receipts | Python client, JS PipelineAPI and REST callers that chain stages |
| 6 | Planned for 2.1: content edits need the revision you opened | Clients that edit item content |
| 7 | The promotion "remove source items" option is gone | Callers that promote and delete the source |
Saves return quickly
Planned for 2.1. This is not part of 2.0.0. Until 2.1, saves keep the answer they give today.
Every caller gets the quick-return default at once. A save is accepted and the response comes back with a receipt. Extraction and linking finish in the background. Nothing about this is opt-in.
What to do: if your code reads the extraction result straight after a save, ask for it with
wait. wait=stored returns when the item is stored. wait=extracted returns when extraction has
finished, with the extraction result attached to the receipt.
The save receipt replaces the id string
Every save-shaped call returns one versioned receipt instead of a bare id. The receipt carries the
item id, the run id, the processing stage (received, stored, extracted or failed) and a
status URL scoped to you. Both SDKs return a typed receipt object. The receipt exposes .item_id.
Before:
item_id = client.add("Met Ada, she prefers async standups")
memory = client.get(item_id)After:
receipt = client.add("Met Ada, she prefers async standups")
memory = client.get(receipt.item_id)The string form of a receipt shows the id and the current status, for example mem_123 (stored).
It never implies the work is complete.
A wait that runs out of time does not raise. It returns the still-pending receipt with
client_timed_out set to true, so you keep the handle and can wait again.
The receipt carries a schema_version. Readers accept any 1.x version and keep fields they do not
know as opaque data, so a later 1.x server does not break your client.
Lite: ingest results and re-imports
In Lite, ingest() returns a receipt for a new save, and you read the id from .item_id. Importing
the same source_path a second time is different. For now that call returns the 1.x dict instead,
with an item_id and a status of unchanged, replaced or repaired. A later release will
return a receipt here too. Until then, one line reads the id in both cases:
r = memory.ingest(text, context={"source_path": path})
item_id = r.item_id if hasattr(r, "item_id") else r["item_id"]Known issue: re-importing an edited note replaces the stored text but keeps the old enrichment. Lite
logs a warning that the stored item is left for the reextract command. Run smartmemory-core reextract
to queue a refresh of the enrichment.
Removed routes
Planned for 2.1: the removal of POST /memory/ingest/full and GET /ingest/async/{run_id}. Both routes keep
working in 2.0.0. Only the public POST /memory/ingest/structured route is removed at 2.0.0.
Once removed, these routes have no alias. A caller of /memory/ingest/full will get a 404.
| Removed | Use instead |
|---|---|
POST /memory/ingest/full | POST /memory/ingest with wait=extracted. The Python ingest_full() becomes ingest(wait="extracted"). |
GET /ingest/async/{run_id} | The scoped status route that the receipt's status URL points to. Both SDKs and MCP ship an await helper that polls it. |
POST /memory/ingest/structured (public route) | The single save request with data and a memory_type. |
The live progress stream stays available for interfaces that want to show progress. Nothing needs it for correctness. Reading pipeline state is a different call from polling a save, and it does not change.
MCP save tools
Six tools stop working on upgrade day. They have no aliases. Hooks, prompts and saved agent instructions that call them must change the same day.
| Old tool | New tool |
|---|---|
memory_add | memory_save (use memory_type="pending" to store the text as is) |
memory_ingest | memory_save with content |
memory_ingest_structured | memory_save with data and memory_type set to the registered schema name |
memory_ingest_conversation | memory_save_conversation with the transcript in turns |
memory_distill | memory_save_conversation with exactly one user turn and one assistant turn |
memory_ingest_document | memory_save_document with source set to a URL or, in local mode, a file path |
You choose the tool by the input you hold. There is no extraction setting for agents to set.
Free text through memory_save is extracted, except memory_type="pending", which is stored as is.
Each tool returns a one-line summary and structured content with status (saved, held, queued
or failed), the saved id and the receipt. A held status means the memory was held for review
and reason says why.
The tools for decisions, plans, anchors and the failure log do not change. memory_import does not
change.
Pipeline stage calls return receipts
The public stage calls (run_extraction_stage and the other run_*_stage methods in the Python
client, the matching PipelineAPI methods in the JS SDK, and the REST stage routes) used to
return the stage result straight away. In 2.0.0 each call returns a job receipt.
Before, each stage result fed the next:
extracted = client.run_extraction_stage(text)
stored = client.run_storage_stage(extracted)After, each stage returns a receipt. You receive it, wait for its terminal result within your scope, and only then pass that result to the next stage:
- Call the extraction stage and keep the receipt it returns.
- Await the receipt until it reaches a terminal state. The await helper polls the scoped status route.
- Pass the completed result to the storage stage, and repeat for each later stage.
Preview profiles are rejected on public saves and public stage calls, with an error that points to the Studio lab. A note that is held returns a receipt that says it is held, not stored, and when the hold expires. It never carries an id for something that was not stored.
Studio's lab stepping stays synchronous and does not change.
Content edits need the revision you opened
Planned for 2.1. This is not part of 2.0.0. Until 2.1, content edits do not need a revision.
Every edit that changes content, including properties.content, must say which revision it
started from. If the item has changed since, the edit is rejected with a conflict response scoped to
you, and no run is created. Keep the draft, show the current text, and let the person decide
whether to retry. Retrying an edit that was already accepted returns the original receipt.
Existing items get a stable current revision at the cutover, so their first edit after the upgrade has a real revision to point at.
Promotion no longer deletes sources
The option to remove source items after promoting a procedure is removed. The evidence behind the promotion is kept. If you passed that option, drop it.