Skip to main content
  • Major versions introduce breaking changes to the API or SDK interfaces
  • Minor versions add new features in a backward-compatible manner
  • Patch versions contain backward-compatible bug fixes
Subscribe to release notifications by watching the maximem_synap_sdk repository on GitHub, or join the #releases channel on Discord for real-time updates.

Compatibility Matrix

Use this table to pick compatible versions when pinning dependencies.

Core SDK

JavaScript / TypeScript SDK

Integration packages

Pin numbers above reflect the minimum tested. Newer minor/patch versions of the framework will typically work, but if a framework ships a breaking change, the integration package will pin around it explicitly. Check the integration package’s own changelog for the exact pin if you suspect a compatibility issue.

Cloud API

The Cloud API is versioned at the URL prefix (/v1/...). The current SDK targets v1 exclusively. v2 is not on the near-term roadmap.

JavaScript / TypeScript SDK

Releases of @maximem/synap-js-sdk. Versioned independently of the Python SDK.

js v0.4.6, 2026-08-27

Fixed

  • The options added in 0.4.5 accept undefined explicitly. logger, sdk_st_authoritative and st_verbatim_overlay were declared as plain optional, so under exactOptionalPropertyTypes you could not pass a value that might be absent, which is the normal shape when configuration comes from the environment. 0.4.4 had already done this for apiKey, clientId, instanceId and baseUrl; these now match.

js v0.4.5, 2026-08-27

Closes the surface the JavaScript SDK was missing against Python. Each item below was previously documented as “Python only”, which was the wrong place to fix a missing feature.

Added

  • sdk_st_authoritative, and the SYNAP_SDK_ST_AUTHORITATIVE environment variable. With it on, conversation.context.get_context_for_prompt() and get_compacted() render from the SDK’s own short-term store when the conversation is warm, skipping the cloud round trip. That is a metered call saved as well as a round trip, and JavaScript had no way to ask for it. Off by default. The three prompt styles (structured, narrative, bullet_points) produce byte-identical output to the Python formatter, so an application that switches languages does not silently change what its model sees.
  • st_verbatim_overlay as a client option. The behaviour existed but could only be reached through SYNAP_ST_VERBATIM_OVERLAY. The environment variable still wins over the option, in both directions, matching Python.
  • logger, on the constructor and on configure(). Every diagnostic the SDK emits goes through it; the default remains console.warn. Python routes these through the stdlib logging module, which is why it takes log_level and logger; there is no global logger in JavaScript, so this takes the sink directly. log_level remains accepted and ignored.
  • record_thinking({ step_index, thought_type }). Both are folded into the event’s metadata map under those exact keys, which is how Python transmits them: the proto has no dedicated fields. A caller-supplied metadata entry of the same name is not overwritten.
  • TimeoutConfig.streamIdle, defaulting to 60 seconds as in Python.
  • api_base_url as an alias for baseUrl, so one configuration object works against either SDK unchanged.

js v0.4.4, 2026-08-27

Fixed

  • retryPolicy: null disables retries, as the documentation always said it did. It restored the default three-attempt policy instead, and the constructor rejected null outright, so there was no way to turn retries off from JavaScript at all. Python’s SDKConfig(retry_policy=None) leaves max_attempts at 1 and this now matches. If you pass null to configure() expecting the defaults back, pass the policy explicitly instead.
  • Insufficient-credit errors carry the top-up links again. The 402 handler read a required_credits key the server never sends (it sends minimum_required_credits), so requiredCredits was always null against a real server, and recovery_url and redeem_url were dropped entirely. InsufficientCreditsError now exposes requiredCredits, recoveryUrl and redeemUrl.
  • instance.send_message reports tool calls. tool_name and tool_args were missing from the options, though the proto carries both fields and the Python SDK sends them, so a tool call could not be reported from JavaScript. tool_args is JSON-encoded into tool_args_json, matching Python.
  • credits.get_ledger accepts its filters. entry_type, from_time and to_time were absent, and the default limit was 50 against Python’s 100, so the same call returned different pages in the two SDKs.
  • context_mode: "conversation-summary" is reachable. The mode was documented and typed but its parameters were never forwarded, so the call fell through to a normal in-conversation fetch. include_profile and last_n_conversations are forwarded, and an unknown mode now raises instead of being ignored.
  • Context fetches apply the B2C check. Retrieval skipped the scope validation the write paths perform, so a customer_id sent to a B2C instance reached the server and came back as an opaque HTTP 400.

Changed

  • Response types replace Record<string, unknown>. credits.get_balance, get_ledger, estimate and redeem, plus conversation.ingest_transcript and memories.batch_create, returned bare JSON, so every field read was unknown in TypeScript. They now return types mirroring the Python models: CreditBalance, CreditLedgerPage, CreditEstimate, RedeemResult, TranscriptIngestResult, BatchCreateResult.
  • CreateMemoryResult requires ingestion_id, document_id, status and queued_at, matching Python’s CreateMemoryResponse. All four were optional, which made the commonest two-line pattern in the documentation, create() then wait_for_completion(result.ingestion_id), fail to compile under strict.
  • apiKey, clientId, instanceId and baseUrl accept undefined explicitly, so new SynapClient({ apiKey: process.env.SYNAP_API_KEY }) compiles under exactOptionalPropertyTypes. Undefined already meant “read the environment”; the type now says so.

Added

  • COMPACTION_LEVELS and the CompactionLevel type, mirroring Python’s enum. compaction_level was an untyped string, so a typo reached the server rather than the compiler.

js v0.4.3, 2026-08-26

Fixed

  • B2C instances take user_id alone. memories.create, memories.create_from_file, conversation.record_message and record_messages_batch all required a customer_id, so on a B2C instance, where the server rejects one, the only correct call could not be expressed: two of them threw customer_id is required before a request was ever made. A batch validates every message before sending any, so one bad message cannot half-apply a batch. Where the server is too old to report its isolation mode, nothing is enforced, so a new SDK against an old server refuses nothing.

js v0.4.2, 2026-08-25

Fixed

  • SYNAP_BASE_URL is honoured again. 0.4.0 and 0.4.1 ignored it and used the Synap Cloud default, so a deployment configured through the environment sent its requests to Synap Cloud instead. Nothing failed, which is what made it hard to spot. An explicit baseUrl passed to the constructor still wins over the environment.
  • SYNAP_GRPC_TLS is accepted as well as SYNAP_GRPC_USE_TLS. A plaintext deployment configured with the former was attempting TLS and could not connect.

js v0.4.1, 2026-08-25

Fixed

  • Anticipated bundles are now read on the fetch path. The stream stored them and no fetch consulted the store, so every retrieval was a cloud call and the stream had no effect on anything. Scope widening follows the same rules as the Python SDK: a customer-scope lookup widens to client-shared bundles but never to user-scoped ones.
  • One client per identity, per process. Each new SynapClient() built its own anticipation cache, so an application constructing a client per request never saw a warm one. Clients for the same identity now share state, and the instance id resolved during initialize() becomes an additional identity so constructing by id later finds the same client.
  • A turn is visible to the next fetch. Messages recorded through conversation.record_message or the stream are held locally and merged into the following conversation-scope fetch, so a fast follow-up sees the turn that preceded it rather than waiting for the server to compact. SYNAP_ST_VERBATIM_OVERLAY=0 disables it.
  • Periodic user-summary injection. A cached user summary is folded into the response every fifth turn of a conversation, matching the Python SDK. Requires a user_id on the fetch; without one the summary cannot be scoped and is skipped.
  • context_used and context_assembled are emitted. These drive per-prefetch scoring and the Requests-page audit. Both ride the anticipation stream and are skipped when it is not open.

js v0.4.0, 2026-08-24

Changed

  • The SDK is now a native TypeScript client. Earlier versions were a Node.js wrapper that spawned the Python SDK as a subprocess, which meant Python 3.11+ on every host, no support for Edge runtimes or Workers, and one call at a time through a single pipe. The SDK now talks to the API directly.
  • Node.js 20+ is required.
  • The runtime setup step is gone. npx synap-js-sdk setup no longer does anything and can be removed from install and CI scripts. It still exits 0, so leaving it in place does not break a build. ~/.synap-js-sdk is orphaned and safe to delete.
  • The package ships both ES modules and CommonJS. require() and import both work, each with its own type definitions.
  • listen() is opt-in. Earlier versions opened an anticipation stream automatically during initialization. It now starts only when you call instance.listen(), and needs @grpc/grpc-js and @grpc/proto-loader installed.
  • The local cache no longer persists across restarts. It was a SQLite file on disk; it is now in memory, so that a native module does not compromise serverless portability.

Removed

  • setupPythonRuntime() and setupTypeScriptExtension(). Neither has anything to do.
  • createClient(options), which was an alias for new SynapClient(options).
  • resolveInstanceId(). initialize() resolves the instance id from the API key; read it from client.instance_id.
  • createSynapError(). Construct the error classes directly, or branch on error.code.
  • Deleting memories by user id. It relied on a list held in the bridge process, so it silently deleted nothing in serverless while reporting success. Pass an explicit memory_id.
  • The five SYNAP_PYTHON_* and SYNAP_PY_SDK_* environment variables, along with SYNAP_JS_SDK_HOME.
Upgrading from 0.3.x? No client methods were removed, and require() still works, so most projects change nothing beyond the install and dropping the setup step.

Python SDK

v0.4.3, 2026-08-05

Fixed

  • The local cache is now scoped to the instance, not just the account. It lived at ~/.synap/{client_id}/ and addressed its files by scope and entity id alone. client_id identifies the Synap account, which can own several instances, and those are separate memory stores sharing no data (so two instances under one account wrote to the same users/{user_id}.db, and where the same customer_id or user_id appeared under both, one instance could be served the other’s cached context. The path is now ~/.synap/{client_id}/{instance_id}/ and the instance is part of the cache key. Server-side scoping was never affected) this was local disk only, and it needed two instances of one account live in the same process, which only became a supported shape in 0.4.1.
Your cache starts cold after this upgrade and refills on demand; entries under the old path are not migrated. That is deliberate: a pre-0.4.3 directory may hold entries from more than one instance, and there is no way to tell which belongs to which, so moving them would carry the contamination forward. Nothing is deleted; you can remove the old ~/.synap/{client_id}/*.db, customers/ and users/ entries once you have upgraded. No server-side data is involved.

v0.4.2, 2026-08-05

Fixed

  • One Synap instance no longer ends up with two live SDKs. An SDK built from an API key alone is identified by that key, because its instance_id does not exist yet; it is resolved from the key during initialize(). The resolved id was never recorded, so a later MaximemSynapSDK(instance_id="inst_…") for that same instance missed the lookup and built a second SDK: two anticipation caches, two short-term stores, two Listen streams and a lower local cache hit rate for one instance. initialize() now records the resolved id as an additional identity for the SDK. Constructing by API key keeps working exactly as before, and an instance_id already held by another live SDK is never taken from it.
  • Concurrent first constructions no longer diverge. Several threads racing to build the first SDK for one identity could all miss the registry and the last one to finish would win, leaving every other caller holding an SDK the registry did not know about, with its own connections and caches. Construction now claims its slot atomically; the callers that lose the race receive the winner. This was reachable on a normal cold start in a multi-threaded server.
  • shutdown() releases every identity the SDK holds, so nothing hands out an SDK whose transports are already closed.
  • Test modules are no longer published inside the package. maximem_synap-0.4.1 shipped six test_*.py modules into site-packages, one importing an internal server module that does not exist outside our monorepo, which could fail any test run that collected them. The 0.4.2 wheel and sdist contain none.
  • maximem-synap-nemo-agent-toolkit: SDK teardown never ran. Both entry points probed for a close() method the SDK does not have (it is shutdown()), so the guard silently did nothing and every workflow leaked its HTTP transport, gRPC stream and telemetry collector.
No API was added, removed or renamed, and there is no data migration. Callers passing an explicit instance_id see byte-identical behaviour.

v0.4.1, 2026-08-04

Fixed

  • Cross-tenant credential and state sharing when one process used more than one API key. The SDK keeps one instance per Synap instance, keyed on instance_id, but instance_id is optional and empty at construction time, because it is resolved from the API key later, during initialize(). Every MaximemSynapSDK(api_key=...) therefore landed in the same registry slot, and the second one adopted the first’s entire state, credentials included. In a process serving two tenants, tenant B’s SDK authenticated as tenant A: B’s reads returned A’s memory and B’s writes were committed into A’s instance. It was silent (no exception, no warning, no log line) because the server correctly authenticated the key it received; it simply received the wrong one. The registry now derives its key from the credential when no instance_id is given (stored as a truncated SHA-256 digest, never in plaintext).
  • shutdown() left a stale registry entry. Registration used the constructor’s instance_id and unregistration used self.instance_id, which initialize() may have replaced in between. The entry survived teardown, so the registry kept handing out an SDK whose transports, stream and cache were already closed.
  • A _force_new SDK’s shutdown() evicted the real singleton. Unregistration had no ownership check, so a throwaway SDK (a test fixture, an adapter, the JS bridge) removed whatever sat on its key, taking the application’s live SDK out of the registry mid-conversation.
Affects maximem-synap ≤ 0.4.0, and only a process that constructs the SDK with two or more different API keys: a backend holding a key per customer, a process talking to two Synap environments at once, or a worker that switches keys between tasks. A process that uses a single API key is unaffected, which is the common case. Upgrade with pip install --upgrade "maximem-synap>=0.4.1"; no code changes and no data migration are required. If you did run more than one key on 0.4.0 or earlier, memory written in that period may have been stored against the first key’s instance; contact support and we will help you check what was written where.

Changed

  • The registry now derives its key from the API key, so each distinct key gets its own SDK, including two keys issued against the same instance, which each authenticate as themselves. Only an explicitly passed instance_id still collapses several keys onto one SDK, unchanged from before. If you rotate a key inside a long-running process and construct by instance_id, call await sdk.shutdown() before reconstructing, or restart the worker. See Rotating a key in a long-running process.

v0.4.0, 2026-07-17

Added

  • conversation.ingest_transcript(...): one-shot async push of a full conversation transcript (string or typed TranscriptTurn list) plus optional client analysis JSON and metadata. Returns immediately with a TranscriptIngestResponse (ingestion_id, status, summary_status, …); poll with memories.status() / wait_for_completion(). Idempotent on (conversation_id, transcript): an identical re-push returns status="duplicate" with the original ingestion_id; a different transcript under the same conversation_id raises TranscriptConflictError. Unlike record_message, conversation_id is an arbitrary client string (no UUID validation); the server coerces it and echoes the original as external_conversation_id.
  • Conversation-summary fetch: fetch(...), user.context.fetch(...) and the unified sdk.fetch(...) gain context_mode ("in-conversation" default / "conversation-summary"), include_profile (default True) and last_n_conversations (default 1, range 0–20). In summary mode the response carries a caller profile and previous-conversation summaries instead of item lists: the call-start read for async integrations. In the unified sdk.fetch, these three params are forwarded only to the user-scope sub-fetch.
  • user.get_profile(user_id, customer_id=None): convenience getter returning a typed UserProfileModel (client-defined critical attributes + free-text overview). Raises ContextNotFoundError (404) when no profile exists.
  • New typed models: TranscriptTurn, TranscriptIngestResponse, UserProfileModel, ProfileAttributeModel, ConversationSummaryModel, all with a .raw escape hatch and unknown-field tolerance. ContextResponse and UnifiedContextResponse gain optional profile / conversations fields, and UnifiedContextResponse.format_for_prompt() renders ## Caller Profile and ## Previous Conversations sections when present (byte-identical output when absent).
  • New exceptions: ConflictError and TranscriptConflictError (both permanent). InsufficientCreditsError is now exported at the top level.

Changed

  • HTTP 409 and 422 mapping: the transport now maps 422 → InvalidInputError and 409 → ConflictError (discriminated on a {"detail": {"code": "transcript_conflict"}} body to TranscriptConflictError). Both are permanent and never retried. Previously both status codes fell through to a retryable transient error.
  • conversation.compact() 409 semantics (behavioral): a “Compaction already in progress” 409 now raises a permanent ConflictError immediately instead of being retried as a transient error and eventually surfacing as one. Intentional: a 409 here means another compaction already holds the lock, and retrying cannot change that. Catch ConflictError (or its base SynapPermanentError) where you previously caught the transient/retry-exhausted error.

v0.2.0, 2026-07-06

Added

  • precision_level fetch parameter: All context fetch calls now accept an optional precision_level parameter ("high" or "medium"). With "high" (the default), behavior is unchanged: results go through an additional relevance-refinement pass before being returned. "medium" skips the refinement pass for faster responses; recall isn’t impacted (the same candidate memories are searched), but outputs are less precisely filtered.

v0.1.2, 2025-01-15

Added

  • Entity Resolution integration: The ingestion pipeline now resolves entities across conversations. Mentions of the same entity (e.g., “John”, “John Smith”, “my manager”) are linked to a canonical entity record.
  • Review queue for entity resolution: Ambiguous entity matches are routed to a review queue for human verification via the Dashboard.
  • Auto-registration of unresolved entities: New entities that do not match any existing record are automatically registered at the CUSTOMER scope for future lookups.
  • Semantic entity matching: Entity resolution uses semantic similarity matching, catching variations that exact string matching would miss.

Changed

  • Entity resolution is now integrated before persistence in the ingestion flow.
  • Entity resolution runs as a graceful degradation feature: if unavailable, the pipeline continues without resolution and logs a warning.

Fixed

  • Fixed non-deterministic test behavior caused by Python set iteration order in pipeline stage tests.

v0.1.1, 2025-01-10

Added

  • Memory Architecture Configuration (MACA) system: Full configuration lifecycle with init, update, review, apply, and rollback operations.
  • Admin API Groups A-D: Client lifecycle (10 endpoints), instance lifecycle (12 endpoints), config management (10 endpoints), and setup/onboarding (6 endpoints).
  • Dashboard API routes: Configuration detail and history endpoints for the web UI.
  • Configuration persistence and workflow improvements: Added storage and workflow support for configuration metadata, approvals, and history.
  • Configuration validation improvements: Stronger schema and business-rule validation for submitted configuration files.
  • Setup and architecture management improvements: Better onboarding and dashboard-facing configuration management flows.

Changed

  • Admin API response shapes were standardized for consistency across dashboard routes.
  • Configuration version numbers are parsed from semantic version strings (“1.0.0” becomes version 1).

Fixed

  • Resolved circular import issues in admin_api/__init__.py by using lazy imports (inside methods) for cross-manager dependencies.

v0.1.0, 2025-01-05

Added

  • Initial release of the Synap SDK and API.
  • Memory ingestion pipeline: Four-stage async pipeline (extraction, categorization, entity resolution, storage) with fast and long-range processing modes.
  • Context retrieval: POST /v1/context/fetch with vector search, graph traversal, and re-ranking. Supports fast and accurate retrieval modes.
  • Context compaction: POST /v1/context/compact with adaptive, aggressive, balanced, and conservative strategies.
  • Instance management: Full CRUD for instances via the Dashboard API, including API key generation and revocation.
  • API key authentication: synap_<random> keys issued from the Dashboard, used as Authorization: Bearer for all SDK communication (REST and gRPC).
  • Cloud auth layer: Production-ready authentication and authorization foundation for SDK and dashboard operations.
  • Memory Architecture Configurators: Initial configuration system for storage, ingestion, and retrieval controls.
  • Scope system: Four-level scope chain (USER > CUSTOMER > CLIENT > WORLD) with proper isolation and inheritance.
  • Webhook system: Five event types (conversation.started, conversation.ended, context.retrieved, config.applied, compaction.completed) with HMAC-SHA256 signature verification.
  • Analytics: Usage metrics, latency percentiles, and token tracking with minute/hour/day rollup buckets.
  • Python SDK: Fully async SDK with typed exceptions and automatic retries.
  • PostgreSQL backend: Persistent storage for clients, instances, credentials, memories, and analytics data.
  • Production key management integration: Cloud-integrated key management support.

Security

  • API keys are hashed (SHA-256) at rest and shown to the user only once at generation time.
  • Revoking an API key takes effect immediately.
  • AuthContext is immutable once created, preventing tampering.

Versions prior to 1.0.0 may include breaking changes in minor version increments as the API stabilizes. We recommend pinning to a specific version in production and testing upgrades in staging first.