SynapCores v1.8.9 — agent memory that can change its mind

Published on July 10, 2026

SynapCores v1.8.9 — agent memory that can change its mind

In v1.8.5 we shipped agent memory as three SQL functions — MEMORY_STORE, MEMORY_RECALL, MEMORY_FORGET. They gave any agent durable, semantically-searchable memory without a second service. But MEMORY_STORE could only ever append.

That's a real gap. An agent that learns "the user is now pescatarian" can't supersede its earlier "vegetarian" — the two just pile up. Store the same fact twice and you get two rows. To revise a belief you had to hand-write a fetch-then-conditional-update loop, and to know whether anything actually changed you diffed the table before and after.

v1.8.9 adds the missing verb: MEMORY_UPSERT.

One call that revises, retracts, or does nothing

-- Revise a belief, but only if you're now more confident
SELECT MEMORY_UPSERT('default', 'User is pescatarian',
                     json_object('key', 'dietary',
                                 'policy', 'replace_higher_confidence',
                                 'confidence', 0.95)) AS action;   -- 'UPDATE'

It returns the action it took — 'ADD', 'UPDATE', 'DELETE', or 'NOOP' — so the caller knows exactly what happened without inspecting the table. Under the hood it finds the row this content should supersede, applies a policy, and writes (or doesn't).

Five policies cover the cases that matter:

  • replace_higher_confidence — a low-confidence guess never clobbers a high-confidence fact. It returns NOOP and leaves the stronger belief in place.
  • merge_max_confidence — write the new content, keep the higher confidence.
  • append_history — never overwrite; insert a superseding row and bump a _version. The whole belief timeline survives.
  • noop_if_equal — the "did anything actually change?" case.
  • replace — plain overwrite.

Semantic keying is the part that makes it usable by an actual agent. With no explicit key, MEMORY_UPSERT matches the row whose embedding is within a similarity threshold (default 0.95) of the new content — so an agent can revise a belief it never bothered to name.

Retraction is first-class: deleted: true (or confidence: 0) removes the matched row and returns 'DELETE'. Retracting a belief the agent never held is a NOOP, not an error — which is what you want in a loop.

No migration, and an audit trail

Confidence, key, and version live inside the existing metadata JSON under reserved keys, so namespaces you created with v1.8.5 MEMORY_STORE keep working with zero migration. And every non-NOOP change is recorded in _system_agent_memory_audit — so "show me everywhere this user's dietary preference changed" is one query, not a forensic diff.

If you just want MEMORY_STORE to stop making duplicates, there's a smaller knob: MEMORY_STORE(ns, content, NULL, json_object('dedup', true)) routes through the upsert path and returns the existing row's id. The two- and three-argument forms are unchanged.

Bounding an agent call

AGENT_RUN gained an optional third argument to cap a single run:

SELECT AGENT_RUN('aidb-assistant', 'Investigate the slowest query',
                 json_object('max_iterations', 3, 'timeout_ms', 60000)) AS reply;

max_iterations (1–10) and timeout_ms (1s–10min). Out-of-range values clamp; typos in option names error. Two things are deliberately not settable from SQL: allow_writes (the agent's tools stay read-only — a per-query write flag would let any caller escalate a read-only agent; that capability belongs to an operator, and arrives with durable agents in v1.9) and model (the engine resolves it persona-then-config, so a per-call override would be silently ignored — we'd rather error than lie).

Security hardening

This release also carries a batch of gateway hardening. None of it is a live exploit in Community Edition — CE is single-tenant — but one change is operator-visible and worth calling out:

If you run SynapCores behind a reverse proxy, set trusted_proxies. X-Forwarded-For is now honored only from proxy IPs you list in the new server.trusted_proxies (default empty). A plain docker run is unaffected — a direct client sends no forwarded header, so nothing changes. But behind nginx or a k8s ingress, add the proxy's IP or per-client rate-limiting and audit granularity collapse onto the proxy. In exchange, an X-Forwarded-For spoofing rate-limit bypass is closed. WebSocket auth also moved to Authorization / Sec-WebSocket-Protocol (the ?token= query string is deprecated), with an optional Origin allowlist.

Get it

curl -fsSL https://get.synapcores.com | sh
# or
docker pull synapcores/community:v1.8.9-ce   # or :latest

No data migration; everything is additive. The full SQL surface is on the SQL reference, and the v1.8.9 release notes have the complete change list.