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