Releases: markmhendrickson/neotoma
v0.18.2
Install
npm install -g neotoma@0.18.2| npm | https://www.npmjs.com/package/neotoma/v/0.18.2 |
| Compare | v0.18.1 → v0.18.2 — view diff |
Two targeted bug fixes: entity resolution no longer accepts provenance labels as an entity's canonical name, and SOURCE_PRIORITY_IGNORED warnings now tell you exactly which fields are affected and why.
Highlights
- Offline/auto-discovered entities no longer get named after a provenance label. Fields like
source_name,data_source, andoriginare now rejected as canonical name candidates alongside the existingsourceblock — preventing unrelated entities from merging under a shared import label (e.g. every auto-resolved entity named "gmail import"). Fixed insrc/services/entity_resolution.ts. - SOURCE_PRIORITY_IGNORED warnings are now self-diagnosing. The warning message now names each written field that ignores
source_priorityand states its effective merge strategy (e.g.'title' uses last_write), so you know exactly which fields to target inreducer_config.merge_policieswithout re-reading the schema. Fixed insrc/services/source_priority_warning.ts.
What changed for npm package users
Runtime / data layer
- Entity resolution:
source_name,data_source, andoriginare now included in the provenance-label deny-list for canonical name derivation. If your store calls were producing entities with names like "gmail import" or "stale cache" after a background sync, this release corrects the root cause. No schema or client changes are required. store_warnings[]shape forSOURCE_PRIORITY_IGNORED: themessagefield now includes a per-field breakdown ('field' uses strategy) and a policy note distinguishing between "no schema" and "schema present but nohighest_priorityentry". Thecode,observation_index,entity_type, andentity_idfields are unchanged.
Shipped artifacts
openapi.yaml— unchanged; no route or schema additions in this patch.dist/— updated for the two changed source files.
API surface & contracts
No OpenAPI changes. The store_warnings[] entry for SOURCE_PRIORITY_IGNORED carries a richer message string but the same structure. Callers that parse only the code field are unaffected.
Behavior changes
- Entities that were previously auto-named after
source_name,data_source, ororiginfield values will resolve differently going forward. If you have existing entities with provenance-label names, you may want to re-merge or split them — no automatic migration is applied. - The text of
SOURCE_PRIORITY_IGNOREDwarnings changes. Callers that parse themessagestring for display or logging will see richer output.
Fixes
- #1821 —
fix(entity-resolution): reject source_name/data_source/origin as canonical_name. Provenance labels could become an entity's identity when no better name field was present, causing unrelated entities to merge under a shared import label. - #1822 —
fix(store): enrich SOURCE_PRIORITY_IGNORED warning with field names and strategies. The warning previously identified the entity type and source priority but did not name which fields were ignoring it or why, forcing callers to cross-reference the schema to understand the fix.
Tests and validation
tests/services/entity_resolution.test.ts— 58 new assertions covering the three new provenance-label rejections and their interaction with the existing deny-list.tests/unit/source_priority_ignored_warning.test.ts— 165 new unit assertions coveringignoredFieldStrategiesand the enrichedbuildSourcePriorityIgnoredWarningmessage shape across no-schema, partial-schema, andhighest_priorityconfigurations.tests/integration/store_source_priority_ignored_warning.test.ts— 9 new integration assertions validating the end-to-end warning emission path.- Security gates: G1 (
security:classify-diff) sensitive=false, G2 (security:lint) 0 errors, G3 (security:manifest:check+test:security:auth-matrix) passed.
Security hardening
Not security-sensitive. npm run security:classify-diff -- --base v0.18.1 --head HEAD reported sensitive=false (5 changed files, all in entity-resolution logic and warning formatting — no middleware, auth, or route-registration paths).
Security review artifact: docs/releases/in_progress/v0.18.2/security_review.md — verdict: yes (no findings, no residual risks).
No security-sensitive surfaces touched.
Breaking changes
No breaking changes. No OpenAPI validation tightening, no request-shape changes, no field promotions or removals. Patch bump is correct per SemVer.
Commits (v0.18.1 → v0.18.2)
f38dd77chore(release): bump version to v0.18.2 + supplement (#1836)aee422efix(store): enrich SOURCE_PRIORITY_IGNORED warning with field names and strategies (#1822) (#1835)a0b6c68fix(entity-resolution): reject source_name/data_source/origin as canonical_name (#1821) (#1834)
Full compare: v0.18.1...v0.18.2
v0.18.1
v0.18.1 Release Supplement
Summary
Patch release that connects the two last-mile pipes on v0.18.0 by-reference source storage (#1775), so it is reachable over HTTP and through the natural "file + its entities" call shape. Both gaps were found by a developer-release evaluator who validated the engine end-to-end (zero-byte writes, SOURCE_UNAVAILABLE / SOURCE_REFERENCE_STALE drift cases) against a production DB.
Fixes
- By-reference storage reachable over HTTP (#1826).
source_storagewas missing fromopenapi.yamlStoreRequest(which isadditionalProperties: false), so the edge guard rejectedPOST /storewithERR_UNKNOWN_FIELD— the field only worked over the/mcproute. Addedsource_storage(enum: [inline, reference]) to the HTTP contract and regeneratedopenapi_types.ts. - No more silent inline fallback with
entities[]+ file (#1827).store({ entities, file_path, source_storage: "reference" })silently stored inline and copied the bytes, because thehasEntities && hasUnstructuredbranches recursed intostore()for the file leg without forwardingsource_storage. It is now propagated, so the natural "file + its entities" call stores by reference as intended.
Behavior changes
- Both surfaces (HTTP
POST /storeand the file+entities call shape) now honorsource_storage: "reference"identically to the single-file MCP path. No other behavior change;inlineremains the default.
Breaking changes
- None.
Install: npm install neotoma@0.18.1 · npx neotoma@0.18.1
Compare: v0.18.0...v0.18.1
v0.18.0
v0.18.0 Release Supplement
Highlights
v0.18.0 closes the "my files and my edits are part of the loop" gap for local-first operators with two long-standing evaluator asks — by-reference source storage and disk-to-entity write-back — alongside the Bundles capability system (definition → runtime → activation → surfacing), richer sandbox showcase packs, and a set of cross-user read-leak security fixes.
What changed for npm package users
Sources / storage — by-reference (source_storage: "reference") (closes #1775)
store(andparse_file) acceptsource_storage: "inline" | "reference". Withreference, Neotoma reads the file once to compute itscontent_hash+ metadata and persists asourcesrow without copying the bytes — so a large or local file (e.g. a PDF on a space-constrained machine) becomes first-class in the graph without inflating the DB.- Retrieval resolves the path at read time; a moved/deleted file surfaces a structured
SOURCE_UNAVAILABLE(never a misleading empty blob), and content drift surfacesSOURCE_REFERENCE_STALE. Content-addressing, dedup, and interpretation linkage are unchanged. - Default
inline⇒ zero behavior change for existing callers.
Mirror profiles — disk-to-entity write-back (neotoma mirror push) (closes #1776)
- New
neotoma mirror push <path|profile>round-trips operator edits of a mirrored markdown file back into Neotoma ascorrect()-ions, behind an opt-in per-profileallow_disk_writeback. - Editable fields only (generated/frontmatter regions are ignored); a 3-way diff against the last-synced base flags conflicts instead of overwriting;
--checkpreviews the exact corrections; edits are stampedobservation_source: "human".
Bundles
- A capability-bundle system landed end-to-end: definition lock (m1), runtime loader + enforcement + linter (m2), activation state store + CLI +
manage_bundlesMCP tool (m3), and/bundlessurfacing — HTTP route + Inspector panel + scaffold tooling (m4).
Docs / onboarding
docs/foundation/what_to_store.mdnow leads with "you don't keep Neotoma clean — Neotoma keeps your state clean for you" and recasts the Tier 1/2/3 tables as a guide, not a gate.- New
docs/vocabulary/plain_language.md— a plain-language primitives cheat-sheet (record/entity, observation, source, interpretation, relationship) with a "you say → Neotoma says" mapping.
Behavior changes
- All additive / opt-in.
source_storagedefaults toinline;allow_disk_writebackdefaults off; bundles enforce only when installed.
Security hardening
- Scoped pre-existing cross-user read leaks in MCP handlers + aggregates (#1795).
- Sandbox: degrade an unresolvable session bearer to anonymous instead of 401 (#1790); exempt internal seeding from the write rate limit (#1797).
Breaking changes
- None. Every new surface is opt-in and backward-compatible.
Documentation
v0.17.0
Install
npm install -g neotoma@0.17.0| npm | https://www.npmjs.com/package/neotoma/v/0.17.0 |
| Compare | v0.16.0 → v0.17.0 — view diff |
v0.17.0 Release Supplement
v0.17.0 ships four agent-facing capabilities surfaced from a high-volume production integration — a single-call entity-identity resolver, machine-readable per-release capability delta, discard-by-default high-velocity intake with read-time sightings aggregation, and an embeddable (chrome-less) Inspector graph — alongside auth parity for AAuth-admitted MCP sessions, proxy session resilience, multi-user data isolation fixes, and a large expansion of the agentic eval harness. All new data surfaces are additive and backward-compatible.
Highlights
- Resolve entity identity from any combination of signals in one call.
identify_entity_by_signalsaccepts a bundle of name, email, company, domain, phone, and open-ended properties and returns a best-match entity with an identity score, resolution band (high/medium/low/unresolved), and top-N candidates — so an agent can auto-merge on high confidence, ask a human on medium, and create-new on low, without chaining three separate retrieve calls. - Know which tools an upgrade adds or removes before touching production.
npm_check_updategains an opt-ininclude_capability_delta: trueflag that returnsnew_toolsandremoved_toolsarrays alongside a one-linecapability_delta_recommendation, letting auto-upgrading agents enumerate newly available MCP tools in the same update-check call. - Keep high-velocity sources out of the graph without losing data.
storeacceptsintake: { mode: "overflow" }to append raw payloads to a configurable JSONL sink (NEOTOMA_OVERFLOW_SINK) instead of creating entities; a newcanonical_key+collapse_bymechanism deduplicates sightings at read time without merging or deleting stored rows. - Embed the Inspector graph anywhere via iframe. The new chrome-less
/embed/graph?apiBase=<url>&node=<id>route renders the graph explorer with no sidebar or header, inherits CSS-variable skin tokens, and emitspostMessageon node double-click so a host page can react — zero behavior change for the existing in-app/graphroute. - AAuth-admitted agents can now use MCP tools directly. An agent with a valid
agent_grant(AAuth-signed requests) can now authenticate an MCP session without a Bearer token, at exact parity with the REST direct-write endpoints. Capability scope ((op, entity_type)ceiling) is enforced per tool call, identical to the REST path. - Enforce allowed values, numeric bounds, and patterns at write time.
register_schemanow accepts per-fieldconstraints(min/max,enum,pattern,banned) and a schema-levelconstraint_violation_policy("reject"aborts the write;"warn"stores a non-blocking advisory). Schemas with no constraints are completely unaffected. (#1756)
What changed for npm package users
CLI (neotoma, neotoma request, …)
neotoma requestgains a--aauthflag (#1748, #1752). Pass it to authenticate via AAuth request signature instead of a bearer token — the CLI sends noAuthorizationheader so the per-agent JWK signature is the credential. RequiresNEOTOMA_AAUTH_PRIVATE_JWK_PATH+NEOTOMA_AAUTH_SUB/NEOTOMA_AAUTH_KID. Useful for testing agent attribution from the CLI.- Per-agent AAuth JWK path override (
NEOTOMA_AAUTH_PRIVATE_JWK_PATH) lets the CLI signer resolve the private JWK from an env-configured path rather than the global default (#1744). Useful when running multiple agent identities from the same machine. neotoma digest import --from-ndjsonis the consuming counterpart to the observer NDJSON feed (#1777). Reads an NDJSON file and imports entities into the local database.
Runtime / data layer
- Proxy resilience: the stdio MCP proxy (
src/proxy/mcp_stdio_proxy.ts) now recovers MCP sessions across backend restarts and non-sticky replica routing (#1750). A bounded re-initialize + retry loop (DEFAULT_MAX_ATTEMPTS=4, exponential backoff) replaces the single-shot 503 recovery; per-request timeouts (DEFAULT_REQUEST_TIMEOUT_MS=15s) fail fast into a retry instead of stalling. Configurable viaNEOTOMA_MCP_PROXY_TIMEOUT_MSandNEOTOMA_MCP_PROXY_MAX_ATTEMPTS. - Multi-user data isolation fix: observation existence checks and snapshot computation queries in
storeare now scoped byuser_id(#1753). Previously, a content-addressed observation from a different user with the same ID could silently suppress the current user's insert; snapshot computation could bleed cross-user observations into a snapshot. Both are fixed. - Prod server non-watch default:
neotoma api startin production now defaults to non-watch mode (#1751), matching how most operators run the server and avoiding accidental file-watcher overhead on--env prod. - rc-autodeploy follow-ups:
fix(ops)tidies the autodeploy flow after the v0.16.0 rc-autodeploy feature review (#1754). SOURCE_PRIORITY_IGNOREDstore warning (#1774): when an observation carries a non-defaultsource_prioritybut every field on the entity type uses a merge strategy that ignores it (last_write,merge_array, ormost_specificwithouttie_breaker: source_priority), the priority value is now surfaced as a non-blocking advisory warning in the store response instead of being silently discarded.
Shipped artifacts
openapi.yamlis updated with a newPOST /identify_entity_by_signalsoperation and updatedstore/npm_check_updateschemas. See API surface section below.src/shared/capability_manifest.jsonis a new generated artifact (built byscripts/generate-capability-manifest.ts). It is included in the npm package and is the source of truth fornpm_check_updatecapability delta computation.
API surface & contracts
New endpoints
POST /identify_entity_by_signals(MCP:identify_entity_by_signals) — multi-signal entity resolver. Requires bearer auth;sandbox_allowed: none. Request body:{ signals: { name?, email?, company?, domain?, phone?, additional_signals? }, entity_type?, top_n? }. Response:{ best_match: { entity, identity_score, resolution_band, matched_signals } | null, candidates: [...] }.
Updated endpoints / tools
POST /store(MCP:store) — new optionalintakefield:{ mode: "graph" | "overflow", reason? }. Default"graph"is unchanged.mode: "overflow"skips graph writes and appends to the JSONL sink; response shape changes to{ overflowed: true, sink_path, line_offset }. Newcanonical_keyandsighting_source_idfields on observation inputs are accepted and indexed. NewSOURCE_PRIORITY_IGNOREDadvisory warning whensource_priorityis set but has no effect under the schema's merge strategies (#1774).GET /entities(MCP:retrieve_entities) — new optionalcollapse_by: "canonical_key"parameter. When set, groups observations sharing acanonical_keyat read time; response carries asightings[]array on each synthesized result.npm_check_update(MCP tool) — new optionalinclude_capability_delta: boolean(defaultfalse). When true, response gainsnew_tools,removed_tools,capability_delta_recommendation, and (on degraded computation)capability_delta_note.POST /register_schema(MCP:register_schema) — new optional per-fieldconstraintsobject (min,max,enum,pattern,banned) and schema-levelconstraint_violation_policy("reject"|"warn", default"reject"). Schema registration validates constraint fields at registration time. Schemas without constraints are unaffected (#1756).GET /retrieve_entity_snapshot(MCP:retrieve_entity_snapshot) — new optionalat_ingestedparameter: ingestion-time cutoff ("what did we know by T"), excluding observations whosecreated_atis after the cutoff even if theirobserved_atpredates it (#1777). Supplying bothatandat_ingestedANDs the bounds.
OpenAPI security
/identify_entity_by_signals has security: [bearerAuth: []] and is present in scripts/security/protected_routes_manifest.json with requires_auth: true. The manifest has been regenerated and is in sync with openapi.yaml.
MCP initialize version fix
The serverInfo.version field in the MCP initialize response now reports the real server package version (e.g. 0.17.0) instead of the hardcoded "1.0.0" (#1689). Clients that inspect serverInfo.version for drift detection now get accurate data.
Behavior changes
- AAuth MCP authentication parity: AAuth-admitted agents (active
agent_grant, verified signature) can authenticate an MCP session without a Bearer token. The tool-call(op, entity_type)capability ceiling and governance-type guard apply identically to the REST path. Existing Bearer/OAuth callers are unaffected. --strictmerge gate expanded:storewith--strictnow permits merges viacanonical_name,email, and stable internal/external ID fields (message_id,turn_key,thread_id, etc.), in addition to schema-declaredcanonical_name_fields. Heuristic fallbacks (name,title, fuzzy matches) are still refused. The updated error message explains all allowed paths (#1753).- Prod server watch mode:
neotoma api start --env prodnow defaults to non-watch mode. - Proxy retry semantics: MCP proxy sessions that previously fell permanently "unavailable" after a backend restart or replica routing event now recover transparently with bounded retries. The observable change is that tool calls succeed (with a short delay) across restarts rather than failing with a 503 or timeout.
- Ingestion-time snapshot cutoff (
at_ingested):retrieve_entity_snapshotgains anat_ingestedparameter (ingestion-time cutoff: "what did we know by T", excluding observations whose `crea...
v0.16.0
Install
npm install -g neotoma@0.16.0| npm | https://www.npmjs.com/package/neotoma/v/0.16.0 |
| Compare | v0.15.0 → v0.16.0 — view diff |
v0.16.0 Release Supplement
Summary
v0.16.0 ships the first-party agent SDK + memory-protocol layer, inspector skinning for embedders, merge-relationship hardening, override-policy enforcement for agent_definition writes, and the remaining PR-salvage queue from the v0.16.0 consolidation pass.
@neotoma/agentharness SDK (#318) — protocol-enforcing TypeScript SDK withwithMemory/NeotomaMemoryturn lifecycle (bounded retrieval → user-phase store → assistant-phase store), deterministic idempotency keys, andREFERS_TO/PART_OFedges by construction.- Python
NeotomaMemorylayer (#322) — parity memory protocol forneotoma-client(with_memory, sync + async), Claude Code hook integration, and published SDK docs (docs/developer/sdk_python.md, site pages). - Inspector skinning (#1585) — configurable palette via
NEOTOMA_INSPECTOR_SKIN(bundled presets underinspector/public/skins/) orNEOTOMA_INSPECTOR_SKIN_CONFIG(arbitrary JSON path); server injects sanitized CSS variables before first paint. - Merge relationship repoint (#1534 / #1507) —
mergeEntitiesrepointsrelationship_observationsto the survivor, returnsrelationships_repointedin the OpenAPI merge response, and collapses duplicate edges byrelationship_key(not metadata hash). override_validationservice (#1634 / #398) — per-field write policies onagent_definitionentities enforced at observation/correction write time viaenforceOverridePolicy.- Issues repo discovery (#1617) — M2
.well-known/neotoma.jsonresolver for cross-repo issue targeting. - Peer-sync runbook (#1560) — cloud availability / Stop-hook peer documentation from salvage Wave 2.
- Site UI salvage (#396, #395) — remove MDX locale fallback banner; docs link in header nav + mobile FAB visibility fix.
- Inspector pinned dashboard panel — home page (
/) shows aPinnedDashboardPanelgrid when the API URL is configured; sidebar caps visible pins at 8 with a Show all (N) link back to home.
RC cycle fixes (post-rc.1)
The RC smoke-test loop produced two additional merges on top of the supplement scope above. Both are user-facing and worth calling out in the GitHub release notes.
Inspector pre-merge UI audit (#1674)
Operator-focused Inspector polish + design-system consistency pass landed before tagging:
- Operator home, mobile shell, drawer — refreshed Inspector home for operators, mobile drawer + responsive polish across pages.
- Design-system consistency — filters, selects, code blocks, recent feeds, and design surfaces migrated onto the canonical primitives. New reusable
EmptyState,ApiNotConfiguredState,ListSurface,SegmentedControl,FiltersCard,MobileFilterPopover,ActiveFilterBadges,CopyableCodeBlockprimitives wired across every index page. - Catch-all 404 route (
inspector/src/pages/not_found.tsx) plus light/dark color audit (chart-1..5tokens,success/warningutilities, dark-mode entity colors). - Backend
/usageroute + dashboard stats service (src/services/dashboard_stats.ts,src/services/timeline_query.ts) backing the dashboard with a real usage service. - Ops fixes —
/mepath redaction,dev:full:prodtoken loading, chokidar-polling API watcher.
Windows spawn EINVAL regression fix (#1676 / #1677)
Field-reported regression: on Windows with modern Node.js (post CVE-2024-27980 patch — Node ≥ 18.20.2 / 20.12.2 / 21.7.2, all v22+), child_process.spawn('npm.cmd', ...) and other .cmd shims (npx.cmd, claude.cmd, tsx.cmd, neotoma.cmd) threw EINVAL unless shell: true was set. Most user-visible consequence: neotoma api start failed on Windows, so the local data API never came up and CLI writes were effectively blocked.
- New helper
src/shared/spawn_platform.ts(IS_WINDOWS,WIN_SHELL,shellOnWin()) centralizes theshell: true-on-win32 decision with explicit security reasoning (args stay an argv array — no command-string concatenation, no shell-injection vector). - Updated spawn sites —
src/cli/index.ts,src/cli/hooks.ts,src/cli/doctor.ts,src/services/schema_registry.ts,src/mcp_dev_shim.tsnow use the helper.doctor.tsalso mapswhich→whereon Windows. - Tests —
src/shared/spawn_platform.test.tscovers both platform branches and the spread-into-options shape.
Session identity inspector origin (#1591 / #1688)
Agents were defaulting Inspector links to sandbox.neotoma.io when no configured origin was available. GET /session and MCP get_session_identity now expose origins.inspector_origin and origins.app_origin from NEOTOMA_PUBLIC_BASE_URL when set, otherwise from the inbound request — never a hardcoded sandbox guess. MCP instructions require agents to use session-provided origins for turn-summary links.
- Server —
src/services/session_info.tsbuilds normalized origin info; OpenAPI documentsSessionOriginInfo. - Tests — unit coverage for empty/malformed origins; integration test confirms configured URL wins over request-derived host headers.
Known issues (won't ship a fix this release)
These were surfaced during the RC audit and are tracked for follow-up; they do not block v0.16.0 but are worth user awareness.
- #1668 —
store: twoagent_messagerows in one request can silently merge whenmessage_idis omitted. A single/storerequest with both a user and assistantagent_message(orconversation_message) sharing oneturn_keyand nomessage_idcollapses to one entity (matched_existingaction) instead of two distinct rows. Server-side protection (ERR_CONVERSATION_MESSAGE_ROLE_CONFLICT) has been in place since v0.12.0 for the standard ordering and now has additional single-batch regression coverage; if the guard does not fire in your environment, ensure you are on the latest server. Workaround: use the:assistantsuffix on the closing message (turn_key: "{conversation_id}:{turn_id}:assistant") — the canonical pattern the MCP turn lifecycle already documents. - #1598 / #1587 — auto-enhance queue / schema drift errors in server logs (
AUTO_ENHANCE_QUEUE). Visible in production logs asAPIError:storeevents on certain entity types (e.g.checkpoint_brief); does not corrupt stored data — observations are preserved, but the snapshot reducer may exclude undeclared fields. Useaudit_undeclared_fragments(MCP) orneotoma schemas audit-fragments(CLI) to triage; promote high-occurrence fields viaupdate_schema_incremental/register_schemaper the per-storeunknown_fieldshint. - #1667 — public endpoints returning 401 / tight CORS posture. RC audit identified some endpoints that surface 401 where 200/anonymous would be more appropriate, and a tighter-than-needed CORS configuration for public read paths. Read paths intended for public consumption should continue to be inspected against the auth posture documented in
docs/subsystems/auth.mdbefore relying on them anonymously.
What changed for npm package users
@neotoma/agent protocol-enforcing harness SDK (#318)
Provider-agnostic agent harness that wraps @neotoma/client with the canonical Neotoma turn protocol so custom agent loops get correct memory behavior without hand-implementing MCP instruction rules.
- New package
packages/agent/(@neotoma/agent):withMemory()wrapper, explicitNeotomaMemory(open_turn/close_turn),turn_helpers,turn_report, anddiagnoseutilities. - Turn lifecycle by construction — on each call: bounded retrieval from the user message, user-phase
conversation+conversation_messagestore withPART_OFandREFERS_TO, agent invocation withctx.retrieved, assistant-phase store with separateturn_keysuffix and idempotency key. - Contract tests —
tests/contract/sdk_client_store_shape.test.tsandtests/unit/agent_memory.test.ts/tests/integration/agent_memory_turn_lifecycle.test.tslock store payload shapes against the live client. - Docs —
docs/developer/sdk_agent.mdand site pagedocs/site/pages/en/sdk-agent.mdx.
npm install @neotoma/agent @neotoma/clientimport { HttpTransport } from "@neotoma/client";
import { withMemory } from "@neotoma/agent";
const wrapped = withMemory(yourAgentFn, {
transport: new HttpTransport({ baseUrl, token }),
conversationId: "conv-2026-05-20",
platform: "my-agent",
});Python NeotomaMemory protocol layer (#322)
Python parity for the same store-first turn protocol — HTTP-only client reaching the Node Neotoma engine over REST.
- New modules in
packages/client-python/:memory.py,with_memory.py,helpers.pywith sync and asyncwith_memory/NeotomaMemory.open_turn/close_turn. - Claude Code hooks —
packages/claude-code-plugin/hooks/neotoma_client/gains the same memory layer for Python hook plugins. - Tests —
packages/client-python/tests/test_memory.py. - Docs —
docs/developer/sdk_python.mdand site pagedocs/site/pages/en/sdk-python.mdx.
pip install neotoma-clientfrom neotoma_client import NeotomaClient, with_memory
client = NeotomaClient(base_url="http://127.0.0.1:3080", token="dev-local")
wrapped = with_memory(my_agent, transport=client, conversation_id="conv-1")
result = wrapped("Tell me about Acme Corp")Inspector skinning via NEOTOMA_INSPECTOR_SKIN (#1585)
Embedders and operators can theme the bundled Inspector SPA without forking the React app.
NEOTOMA_INSPECTOR_SKIN=<name>— load a bundled preset fromdist/inspector/skins/<name>.json(source:inspector/public/skins/). Ships with a neutralsamplepreset for verification.- **`NEO...
v0.15.0
Install
npm install -g neotoma@0.15.0| npm | https://www.npmjs.com/package/neotoma/v/0.15.0 |
| Compare | v0.14.0 → v0.15.0 — view diff |
v0.15.0
Summary
This release seeds a first-class pull_request entity type, extends agent grants to cover GitHub-harness operations, hardens the issue-submission path for keyless and guest agents, and fixes a graph-query bug that silently dropped source records. It also restores the main CI baseline (broken inspector submodule pin) and ships the LaunchAgent deployment tooling for running Neotoma daemons under launchd.
What changed for npm package users
- New
pull_requestentity type. The schema registry now seeds apull_requesttype with declared fields (resolves #158), so agents can store and retrieve pull requests as first-class entities instead of untyped records. retrieve_graph_neighborhoodnow returns source records correctly. Thenode_type: "source"branch and the entity-branchinclude_sourcessub-path queried a singularsourcetable that does not exist, so they silently returned no rows for every user. Both now query the canonicalsourcestable (resolves #389, #394).- Faster full re-mirror. The canonical mirror no longer performs a dynamic
import()once per entity inside the per-profile render loop; the renderer is imported once at module load (resolves #371). - New
/endsession-close audit skill that surfaces remaining work and verifies session data intended for Neotoma is actually stored before context is lost (#373). - MCP transport preset
eadded for the MCP server, with Node-version pinning in the LaunchAgent run scripts.
API surface & contracts
- Additive only.
npm run openapi:bc-diffagainst v0.14.0 reports no breaking changes. pull_requestis a new declared schema; no existing request or response shapes were narrowed.
Behavior changes
- Agents that query
retrieve_graph_neighborhoodfor a source node, or requestinclude_sourceson an entity node, now receive the source rows they previously did not. submit_issueno longer hard-fails for agents without an AAuth keypair: it skips AAuth when no keypair is present and retries as an unsigned guest when AAuth returnsAUTH_REQUIRED(resolves #944, #937). Issue submission also orders Neotoma-first.- Agent grants can now authorize
github_harnessoperations and repo scopes via an extendedAgentCapabilityOp(closes #934), enabling attributed GitHub actions through the harness.
Docs site & CI / tooling
- CI baseline restored.
main's baseline lane had failed since 2026-05-25 because theinspectorsubmodule was pinned to a commit never pushed to the inspector remote. The pin is repointed to the last-good published commit (#1471). - Husky v10 readiness. Removed the deprecated v9 shebang lines from
.husky/pre-committhat printed a deprecation warning on every commit and would fail under Husky v10 (resolves #400). - Instruction docs updated for mandatory extraction, GitHub entity types, and an awaiting-reply rule (#174, #175, #176).
Internal changes
- LaunchAgent deployment tooling. Templatized LaunchAgent plists for the prod server, dev server, issues-sync, and watch-build daemons, with an
install.sh, a README covering install/load/unload/logs/template vars, and.gitignorerules so only.tmplsources are tracked. NEOTOMA_LOCAL_PORT_DISK_PROFILEnow overrides the write-side disk profile for the local HTTP port file.- The prod-server LaunchAgent bypasses
pick-port.js(resolves ateles#10), andNEOTOMA_TRUST_PROD_LOOPBACK=1is set in both LaunchAgent run scripts. - Machine-specific configs are gitignored;
.cursor/uses relative symlinks; MCP configs synced. content_fieldheading-skip fix inrenderEntityMarkdown(resolves #262).
Fixes
- #389 / #394 —
retrieve_graph_neighborhoodqueried a nonexistent singularsourcetable; now usessources. - #371 — dynamic import inside the per-entity mirror render loop, hoisted to module load.
- #400 — deprecated Husky v9 shebang removed from
.husky/pre-commit. - #262 —
content_fieldheading-skip inrenderEntityMarkdown. - #944 / #937 —
submit_issuekeyless/guest handling. - ateles#10 — prod-server LaunchAgent port-pick bypass.
Tests and validation
- New HTTP-level integration regression (
tests/integration/graph_neighborhood_source_branch.test.ts) boots the Express app and asserts the source branch returns rows; verified to fail against the singular table and pass after the fix. - Mirror and markdown suites pass (54 tests) after the import hoist.
- New
pull_requestschema covered bytests/unit/pull_request_schema.test.ts. - Automated test catalog regenerated (400 files).
npm run type-check, lint (0 errors), Prettier, and site-copy lint all clean.
Security hardening
The diff classifier flagged this release as sensitive because src/actions.ts is in the diff (the v0.11.1 auth-bypass surface heuristic). The actual change is two db.from("source") → db.from("sources") substitutions; adversarial review of all six prompt axes (alternate-path auth, proxy trust, local-dev widening, unauth public route, guest-access policy, AAuth downgrade) found no security regression. See docs/releases/in_progress/v0.15.0/security_review.md for the full walkthrough and sign-off verdict (with-caveats). No advisories opened or referenced by this release.
Breaking changes
No breaking changes.
Commits (v0.14.0 → v0.15.0)
60cbe8bBump version to v0.15.0b0e9c21Merge pull request #1474 from markmhendrickson/release/v0.15.00347e22chore(release): prettier-format regenerated openapi_types.tsf8befc1chore(release): v0.15.0 release artifacts + openapi AgentCapabilityEntry fixe9ee621fix: graph source-table bug (#389/#394) + mirror import perf (#371) + Husky v9 deprecation (#400) (#1469)902d1ccfix(inspector): repoint submodule to last-good published pin 206e14e (#1471)03df670chore(merge): resolve conflicts from #936 fix mergedd001b1fix(issues): Neotoma-first ordering + skip AAuth when no keypair (resolves #944)0a98c7dfix(issues): retry submit_issue as unsigned guest when AAuth returns AUTH_REQUIRED (#937)85e932bMerge branch 'main' of https://github.com/markmhendrickson/neotoma7818ec0feat(mcp): add transport preset e + LaunchAgent node-version pinning172f8e4Add NEOTOMA_TRUST_PROD_LOOPBACK=1 to both LaunchAgent run scriptsd6ee8c8chore(inspector): bump submodule to logo path fix3cd06f4chore(inspector): bump submodule to basename fix43e7116fix(local-port-file): NEOTOMA_LOCAL_PORT_DISK_PROFILE overrides write-side disk profile2dab6c3chore(config): gitignore machine-specific configs; relative symlinks in .cursor/; sync MCP configs8907b62feat(agent-grants): extend AgentCapabilityOp for github_harness ops (closes #934)302a51bfix(launchd): bypass pick-port.js in prod server launchagent (resolves ateles#10)353328afeat(schema): seed pull_request entity type (resolves #158) (#929)c06142bdocs(instructions): mandatory extraction, GitHub entity types, awaiting-reply rule (#174 #175 #176) (#931)8582ba0fix(mirror): content_field heading-skip in renderEntityMarkdown (resolves #262) (#930)e5c42d6chore(deploy): gitignore rendered plists — only track .tmpl sourcesb8c5864docs(deploy): add README for launchagents — install, load/unload, logs, template varsda268d0chore(deploy): templatize launchagent plists; add install.sh25be539chore(deploy): add launchagent plists for prod-server, dev-server, issues-sync, watch-build38e8b00feat(skills): add /end session-close audit skill (#373)94a6b25fix(release): correct nested path for v0.14.0 probe report48adc81chore(release): move v0.14.0 release artifacts to completed0537296Merge branch 'main' of github.com:markmhendrickson/neotomad07c060fix(release-skill): tighten Step 5.2 GHSA publication to check live API stateb3b197aMerge pull request #391 from markmhendrickson/fix/release-skill-review-gate0392013chore(release): archive v0.14.0 supplement and security review to completed/055576afix(release-skill): add @claude review gate on release candidate PR (Step 3.7.2b)
Full compare: v0.14.0...v0.15.0
v0.14.0
v0.14.0 ships substantial improvements across search, ingestion, agent ergonomics, and security. Headlines: smarter entity search that ranks by entity-type intent and hides chat bookkeeping; persistent issue-filing consent; paginated graph neighborhood traversal; structured cold-start error responses on update_schema_incremental; per-user data scoping on relationship query endpoints (GHSA-wrr4-782v-jhwh); a /review skill wired into the automated PR review workflow; the preference entity schema is now registered; and neotoma schemas repair-plural-types is fixed for global / npx installs.
Highlights
- Smarter entity search. When a query token names an entity type (e.g. "plans", "tasks", "transactions"),
retrieve_entitiesnow boosts that type's results by 280 points and filters by type instead of matching the token against snapshot text — so "newest plans" actually surfaces plans, not chat messages mentioning the word "plans". Singular/plural normalization included. - Chat bookkeeping no longer pollutes product search. The Inspector header search and
/searchautomatically excludeconversation,conversation_message, and related bookkeeping entity types unless the caller explicitly filters by one of those types. Passexclude_bookkeeping: trueonretrieve_entitiesto apply the same filter from any caller. - Issue-filing preference persists across sessions. On first encounter, agents ask once and store your choice (
always/ask/never) as apreferenceentity so future sessions skip the prompt.neotoma issues config --modestill sets the runtime flag; the preference entity is the cross-session layer that feeds it. - Paginate large graph neighborhood results.
retrieve_graph_neighborhoodandPOST /retrieve_graph_neighborhoodnow acceptlimit(default 100, max 500) andoffsetparameters and returntotal_countandhas_more, so callers can page through densely-connected nodes without truncation. - Tenant isolation fix on relationship query endpoints. GHSA-wrr4-782v-jhwh —
/list_relationshipsand/retrieve_graph_neighborhoodpreviously authenticated callers but did not scope database queries to the requesting user. An authenticated user with a known cross-user entity ID could read another user's relationship metadata. Severity Low under current single-tenant deployments; escalates to Medium for multi-tenant. Fixed with explicit.eq("user_id", userId)on every query and a newtenant_isolation_matrix.test.tsgate. /reviewskill wired into automated PR review. The PR review workflow now invokes a versioned/reviewskill that walks the full architectural + product-principles + agent-instruction coherence checklist instead of a hand-rolled prompt. The same skill runs as a hard gate inside/releaseStep 3.6.preferenceentity schema registered. The schema is now seeded at boot sopreferenceentities (used by the issue-filing consent flow, future agent settings) have proper canonical-name derivation, last-write merge policy, and field declarations.neotoma schemas repair-plural-typesworks from a global or npx install. A dynamic import in the compiled CLI used the wrong relative path (../../services/instead of../services/), which silently failed after installation. Fixed, with new dist-level smoke tests covering this class of bug going forward.
What changed for npm package users
CLI (neotoma)
neotoma schemas repair-plural-types— fixed: the dist-level dynamic import path forplural_type_repairwas one directory level off (../../services/plural_type_repair.jsinstead of../services/plural_type_repair.js). The bug was invisible to TypeScript compilation and source-level tests because both resolve fromsrc/; only a subprocess spawned against the compileddist/catches it. New smoke tests attests/contract/cli_handler_dist_smoke.test.tscover this class of regression forschemas repair-plural-types,schemas audit,schemas list, andstatus.
Runtime / data layer
- Entity search ranking and bookkeeping exclusion (
retrieve_entities,POST /retrieve_entities) — substantial rework ofsrc/shared/action_handlers/entity_handlers.ts(~430 lines):- New
ENTITY_TYPE_KEYWORD_BOOST = 280. When a search token equals an entity_type's name (canonical or singular-normalized viasuggestSingular), matching entities of that type get ranked 280 points higher in the result list. - New
exclude_bookkeepingparameter onretrieve_entities(boolean, default false on direct API calls; default true for Inspector header and/search). When set,conversation,conversation_message, and relatedBOOKKEEPING_ENTITY_TYPESare omitted from results. - New
buildEntityTypeFilterTokens(searchTokens, knownEntityTypes)andtextTokensForEntityMatch(searchTokens, typeFilterTokens)helpers: when the query names a known entity_type, that token is removed from snapshot-text matching and becomes a type filter instead. The remaining tokens still match snapshot content. - New
buildEntityLexicalSearchText(canonicalName, snapshot, rawFragmentText)builds the canonical lexical haystack (canonical name + snapshot + raw fragments) used for ranking. lexicalSearchEntityIdsnow resolves active entity types fromschema_registryto drive the type-filter logic; falls back to a warning log if the schema registry query fails.- Net effect: a query like "newest plans" now narrows the candidate query to
entity_type='plan'and boosts ranking accordingly, rather than fuzzy-matching the literal string "plans" against every entity's snapshot.
- New
retrieve_graph_neighborhoodpagination — acceptslimit(1–500, default 100) andoffset(default 0) query parameters. Response now includestotal_count(total relationships for the node before pagination) andhas_more. Use these to page through high-degree nodes.list_relationshipsfilter expansion + OpenAPI declaration —src/actions.ts: the handler now supportssource_entity_idandtarget_entity_idas direct filter params in addition to the legacyentity_id + directionpattern. Pagination (limit,offset) added. The request schema requires at least one ofentity_id,source_entity_id,target_entity_id, orrelationship_type. Optionaluser_idfilter added.openapi.yamlnow declares the full request and response shape including the closedrelationship_typeenum (matchingRelationshipTypeSchema) and ananyOf"at least one identifying field" constraint. Non-breaking addition.- Per-user data scoping on
/list_relationshipsand/retrieve_graph_neighborhood(GHSA-wrr4-782v-jhwh) — both endpoints now resolvegetAuthenticatedUserIdand apply.eq("user_id", userId)to every relationship, entity, observation, and source lookup. Closes #365 and the tenant-isolation portion of #366. Test gate added:tests/security/tenant_isolation_matrix.test.ts. update_schema_incrementalcold-start handling — when called for an entity_type with no registered or code-defined schema, previously threw an opaqueMcpError(-32603); now returns a structured non-throwing response witherror_code: ERR_NO_SCHEMA_FOR_ENTITY_TYPE,no_schema_for_entity_type: true, and a stablehint("Pickregister_schemafor the new entity_type. The MCP tool isregister_schema; the HTTP route isPOST /register_schema...") that no longer interpolates per-type strings. When the entity_type has an existing schema that lacks bothcanonical_name_fieldsandidentity_opt_out, the R2 error is now surfaced asERR_SCHEMA_MISSING_IDENTITY_CONFIGwith a hint to callregister_schemawith a complete schema definition.preferenceentity schema — registered at boot undersrc/services/schema_definitions.ts. Fields:title(canonical name source),value(last-write merge policy),scope,description. Backs the cross-session issue-filing consent flow and is available for any future agent-settings entity. Closes #339.- Profile mirror render correctness (
rebuildProfile/mirrorEntity) —mirrorEntitynow dispatches torenderProfileEntitywhenrender_modeisfrontmatter_contentorcontent_only, matchingrebuildProfilebehavior. Previously the live-write path always calledrenderEntityMarkdown, ignoringrender_mode,frontmatter_fields, andcontent_field. Whencontent_fieldwas"body", this produced a spurious## bodyheading in the output. Fixed via dynamic import ofrenderProfileEntityfromcanonical_markdown.js. Closes #262. - Graceful
entity_typesfallback in search (src/shared/action_handlers/entity_handlers.ts) — whenschema_registryqueries fail (cold start, transient DB error),resolveEntityTypesForTypeFiltersnow logs a warning and returns an empty set rather than throwing. Search falls back to the un-typed match path instead of failing the whole request. Closes #342.
Shipped artifacts
openapi.yaml— substantial additions:POST /retrieve_graph_neighborhoodgainslimit,offset,user_idrequest fields andtotal_count,has_more,node_id,node_type,entity,relationships,related_entities,observations,sourcesresponse fields.POST /list_relationshipsgains a fully-declared request body (entity_id,source_entity_id,target_entity_id,direction,relationship_typeclosed enum,limit,offset,user_id) withanyOf"at least one identifying field" constraint, plus full response shape (relationships,total,limit,offset).
.dockerignore— substantially trimmed: build context reduced from ~15 GB to ~50 MB by excluding non-source dirs (backups,data_backups,tmp,site_pages,writ,packages,mcp,public,frontend,.claude,...
v0.13.0
v0.13.0 Release Supplement
Summary
v0.13.0 is a substantial feature release covering everything since v0.12.1. Headline additions:
- Schema-level and per-entity agent instructions — entity types and individual entities can carry behavioral guidance that agents must apply when the entity is retrieved.
- Multi-harness preflight CLI — replaces the multi-prompt onboarding flow.
- Bulk harness-transcript import from
~/.claude/,~/.codex/, and~/.cursor/. - MCP integration guides for Continue, Windsurf, VS Code, and Letta.
- Bidirectional column-encryption migration command for self-hosted deployments.
- Plan-entity refactor — replaces feature units with first-class
planentities and aneotoma-plansmirror profile. - Server-rendered
/docsindex over repo docs, with fail-closed visibility. - Prettier baseline + CI format gate.
- Selective Claude PR review on substantial PRs.
- Corrective fixes to schema discovery, idempotency, intra-batch relationships, raw_fragments routing, and timeline determinism.
GitHub Releases now stage as draft and publish only after the sandbox deployment is verified. The release flow opens an RC branch + PR for public review before execute.
What changed for npm package users
New CLI commands
neotoma preflight [--tool <harness>] [--apply] [--scope project|user|both] [--dry-run]— prints a copy-paste allowlist block for the specified harness or writes it directly with--apply. Allowlist writes are supported forclaude-code,cursor, andcodex; for MCP-only harnesses (claude-desktop,openclaw,windsurf,continue,vscode) the command prints a redirect toneotoma setup. Forclaude-code,--scopeselects project (.claude/settings.local.json), user (~/.claude/settings.json), or both. Replaces the multi-prompt manual setup that previously plagued the onboarding flow.neotoma db migrate-encryption <encrypt|decrypt>— bidirectional bulk column encryption migration that operates directly on the SQLite file (no running server). Migratesobservations.fields,entity_snapshots.snapshot/provenance,relationship_snapshots.snapshot/provenance,raw_fragments.fragment_value/fragment_envelope, andschema_recommendations.fields_*/converters_to_add. Uses the same key configuration as the runtime (NEOTOMA_KEY_FILE_PATHorNEOTOMA_MNEMONIC+ optionalNEOTOMA_MNEMONIC_PASSPHRASE). Covered bytests/cli/db_migrate_encryption.test.ts(encrypt→decrypt identity, idempotency, dry-run non-mutation, NULL preservation, missing-table skip, wrong-key per-row failure, INTEGER PRIMARY KEY rowid-aliasing fix).neotoma db repair-schema-lag [--dry-run] [--types <entity_types>] [--rollback <run_id>]— audit and repairraw_fragmentsrows that were misrouted due to the schema-projection-lag bug (#142). Rows whosefragment_keynow matches a declared field in the active schema are promoted to observations and snapshots recomputed. Every inserted observation carries a_migration_run_idfield for rollback. Covered bytests/cli/db_repair_schema_lag.test.ts.neotoma db migrate-env-split— repairs npm-installed deployments that had previously written user data to a dev database. Subsequent installs now default to a production DB; this command moves existing data into the production location (#172, #244).neotoma schemas repair-plural-types— coalesces accidentally-plural entity types (e.g.posts→post) back to the canonical singular form, declaring the plural as an alias on the canonical schema (#162).neotoma onboarding import-transcriptsandneotoma init --import-transcripts— bulk-import all detectable harness transcripts in one step during onboarding (#143).neotoma reporter setup— guided setup for the issue reporter flow (auth, scope, default labels), supersedes the multi-step manual procedure (#199).
New CLI flags on existing commands
neotoma discover --harness-transcripts— also detects Claude Code (~/.claude/projects/*/conv-*.jsonl), Codex (~/.codex/archived_sessions/*.jsonl), and Cursor (~/.cursor/chats/**/store.db, plusstate.vscdb) transcript locations. Prints a structured summary with file counts, sample titles, date ranges, and SQLite requirement flags.neotoma ingest-transcript [path] --harness <claude-code|codex|cursor>—pathis now optional. When--harnessis given andpathis omitted, bulk-imports all transcripts from the corresponding well-known location.neotoma cursor-hooks --global— installs cursor hooks into~/.cursor/hooks.jsoninstead of the project-local.cursor/hooks.json(#144).neotoma store --turn-key <key>— stable per-turn idempotency key surface for replay-safe stores; used internally by the test suite and supported for external callers.neotoma sync-issues --push— new push leg that mirrors local public Neotoma issues to GitHub with PII redaction applied at the boundary.
Behavior changes in existing commands
neotoma store/retrieve_entities— entity responses now include two new nullable string fields:schema_instructions— markdown sourced fromSchemaDefinition.agent_instructionsfor the entity's registered schema.entity_instructions— markdown sourced from the entity's own snapshotagent_instructionsfield.
Agents MUST treat these as behavioral context for the entity type and apply them to the current turn.
neotoma store— unknown-field surfacing improved: the response now includes a concrete list of unknown field names (not just a count) when fields are dropped toraw_fragments, and emits a structured recovery hint pointing the caller at the right corrective action (#185, #187). A pre-resolution pass prevents partial batch writes — if any entity in a batch fails to resolve, none are written (#203). Intra-batch relationships are honored: a relationship targeting another entity created in the samestorecall now resolves correctly (#203, #221).neotoma store— schema/type discovery is required before the store completes; callers receive a clear error when targeting an unregistered entity type instead of silently routing all fields toraw_fragments(#196, #208). When the target type has no registered schema, the response surfaces the no-schema state explicitly so callers can register a schema and retry (#164).neotoma store— idempotency key reuse with a different payload is now detected via content-hash comparison and returnsERR_IDEMPOTENCY_MISMATCHinstead of silently overwriting (#186).neotoma storealiases — the MCP dispatcher now recognisesstore_structuredandstore_unstructuredaliases for the unifiedstoretool.neotoma storeranking — store-tool ranking improved so agents surface the right entry point when multiple variants match (#181, #182, #183, #191, #206).neotoma retrieve/list_timeline_events—list_timeline_eventsreturns an empty result for an unknownevent_typeinstead of erroring (#207).neotoma sourcesaccess mutation commands now fail fast when--base-urlor--api-onlyare present (these commands are local-only).neotoma issue add-messagetreats partial success (GitHub write succeeded, remote Neotoma append failed) as success, returning a non-nullremote_submission_errorso callers can react without an end-to-end failure (#43, #90).neotoma cli access reset-issue-policycorrectly writes the default policy to disk (#40, #89).neotoma doctor— reports the source of the resolveddata_dirso users can tell when the path came from an env var, default, or explicit flag (#170).neotoma upload—--localcorrectly uses the local data dir for source persistence (#168).neotoma submit-issue/add-issue-message—reporter_app_versionis auto-populated when not supplied;AUTH_REQUIREDerrors now surface a structuredhintdirecting the caller at the right next step (#181, #182).neotoma plans— new top-level surface backed byplanentities (see Plan refactor below).
New seeded entity types
plan— first-class entity for engineering and design plans, replacing the previous filesystem-only feature units. Plans are mirrored back toplans/(wasdocs/plans/) via the newneotoma-plansmirror profile.gist— GitHub Gists and similar code/text snippets shared via URL. Identity uses gist URL; preserves description casing; last-write merge for description (#72, #75, #77, #84).neotoma_repair— records a repair or remediation action taken within Neotoma. Identity is composed of action + timestamp + actor.external_link— generic external URL entity with gist-style metadata fields (title, description, source).conversation— promoted from inferred to bootstrap-registered schema;session_uuidfield added as a bridge to coalesce slug-style conversation IDs with harness-issued UUIDs (#138, #145, #256).workout_session— new schema with anexercisesfield usingmerge_arraysemantics (#209).product_feedback— adds afeedback_sourcediscriminator and tightened identity validation (#136, #137).
Plan refactor
Plans are now first-class Neotoma entities, not filesystem documents. Highlights:
- Plans live as
planentities with structured fields, retrieval routing, and graph edges to objectives, gates, and feedback. - The new
neotoma-plansmirror profile snapshotsplanentities into the repo with canonical-name filenames and YAML frontmatter for harness compatibility. - Plan content moved out of
.cursor/plans/anddocs/plans/flat files into Neotoma; the mirror writes them back toplans/for harness visibility (b1d163696). - Selective mirror profiles support snapshot field tokens, slug truncation, and a remote API client for profile-based hydration (
8d39d1000,7a441614f). - Plan-entity re...
v0.12.1
Install
npm install -g neotoma@0.12.1| npm | https://www.npmjs.com/package/neotoma/v/0.12.1 |
| Compare | v0.12.0 → v0.12.1 — view diff |
v0.12.1 hardens the v0.12.0 surface area without changing its shape. It closes a guest-token validation gap on the auth path, restores Codex / OpenAI function-tool compatibility for the issue MCP tools (server-side enforcement is unchanged), tightens the prepublish Inspector build, and ships the operator-facing v0.12 documentation that the v0.12.0 tag advertised but did not yet include — a Doctor CLI reference, the operator hardening knobs, the LaunchAgent zombie-cleanup flag, the documented bulk_close_issues / bulk_remove_issues tools, and dedicated site pages for peer sync, substrate subscriptions, issue reporting, and security hardening.
Highlights
- Guest auth: invalid bearer tokens no longer downgrade to anonymous.
maybeStampGuestPrincipalis now async and explicitly validates anAuthorization: Bearer …guest access token againstvalidateGuestAccessTokenbefore stamping the request principal. Guest-scoped entity reads also re-assert the token via a newassertValidGuestAccessTokenhelper. The error envelope on a missing or invalid guest principal now readsNot authenticated - guest principal cannot resolve a user_id …so operators can grep for it. Behavior under a valid token is unchanged. Regression coverage:tests/integration/guest_invalid_bearer_routes.test.tsasserts401for/issues/submit,/issues/add_message,/issues/status,/subscribe,/unsubscribe,/list_subscriptions,/get_subscription_status,/events/stream, and/entities/duplicateswhen the bearer is not a valid guest token. - Codex / OpenAI function-tool compatibility for the issue MCP tools.
submit_issue,add_issue_message, andget_issue_statusno longer carry a top-levelanyOfin their JSON Schema, because OpenAI's function-tool registry rejects schemas with a top-level combinator. Server-side enforcement is unchanged:submit_issuestill returns400 ERR_REPORTER_ENVIRONMENT_REQUIREDwhen neitherreporter_git_shanorreporter_app_versionis supplied (declared indetails.acceptable_field_groups), andadd_issue_message/get_issue_statusstill requireentity_idorissue_number. Tool descriptions indocs/developer/mcp/tool_descriptions.yamlnow describe the constraints in prose so MCP clients see the same contract without the schema combinator. - Operator hardening knobs are now documented in the security tree.
SECURITY.mdanddocs/security/threat_model.mdadd theOperator hardening knobs (v0.12+)section:NEOTOMA_GUEST_WRITE_RATE_LIMIT_PER_MIN(default30/min, key precedence AAuth thumbprint > hashed guest token > IP),NEOTOMA_GUEST_TOKEN_TTL_SECONDS(default2592000, withrevoked_at-aware revocation),NEOTOMA_HOSTED_MODE=1(rejects private/loopback/link-localsender_peer_urlon inbound peer-sync), andMCP_PROXY_FAIL_CLOSED=1(refuses unsigned downstream requests on AAuth proxies). All four were already shipping behaviors in v0.12.0; this release is what makes them findable from the security landing page. neotoma doctor --jsonis now spec-documented.docs/developer/cli_reference.mdadds a Doctor section that describes the--jsonshape, everydata.risks[].code(icloud_drive,macos_synced_desktop_or_documents,prior_sqlite_repair_artifacts),suggested_safe_data_dir, the migration command (neotoma storage set-data-dir <path> --move-db-files), and the--tooloverride consumed byneotoma setup --output json.reload_neotoma_launchagents.sh --kill-zombiesis documented end to end.docs/developer/launchd_dev_servers.mdanddocs/developer/scripts_reference.mddescribe the four orphan patterns the flag SIGTERMs (legacynode dist/index.jsadopted by launchd,with_branch_ports.js → tsx → src/actions.tschains,tsx watch … src/actions.tswatchers from integration tests, andnpm exec tsx … src/actions.tsdebug runs) and call out that the steady-state orphan rate after the v0.12.0 process-group fix is near zero —--kill-zombiesis the recovery path for pre-v0.12.0 leftovers and force-killed test parents.bulk_close_issues/bulk_remove_issuesare now declared in the spec table.docs/specs/MCP_SPEC.mdadds rows for both tools with their HTTP twins (POST /issues/bulk_close,POST /issues/bulk_remove);docs/subsystems/issues.mddocuments their MCP signatures and notes thatbulk_remove_issuesis a soft delete viadeleteEntityobservations and restoration goes throughrestore_entity. The tools shipped in v0.12.0; the spec table omitted them.storewrite-classification, peer attribution, and external-actor fields are spelled out in the spec.docs/specs/MCP_SPEC.mddocuments the v0.12+observation_source(sensor/workflow_state/llm_summary/human/import/sync),source_peer_id, andexternal_actorfields on thestorerequest, including the reducer tie-break behavior (source_priorityfirst, thenobservation_source) and the AAuth-vs-external_actordistinction (signing agent vs the human/account on whose behalf the agent wrote).- Inspector packaging fails closed.
scripts/build_inspector.jsnow exits non-zero when theinspector/submodule is missing duringprepublishOnly/pack:local, instead of silently skipping. This prevents shipping an npm tarball without the bundled Inspector.tests/contract/package_scripts.test.tspins the Inspector Vite entrypoints (dev:vite,build:vite,build:watch,preview) to--config vite.config.tsso the build cannot regress to a stalevite.config.js. - Four new operator-facing site pages cover the v0.12 subsystems.
/peer-sync,/subscriptions,/issue-reporting, and/security-hardeningare wired into the docs nav, the SEO metadata, the route table, and the doc manifest. Each page links back to its repo source of truth (docs/subsystems/peer_sync.md,docs/subsystems/subscriptions.md,docs/subsystems/issues.md,SECURITY.md+docs/security/threat_model.md). The changelog page surfaces a v0.12.0 highlight banner pointing at these pages and the breakingsubmit_issuereporter-provenance contract. - Release skill now hard-gates security review.
.cursor/skills/release/SKILL.mdand.claude/skills/release/SKILL.mdadd a hard gate before Step 4 that requires explicit G1 / G2 / G3 / G4 evidence and a populatedsecurity_review.mdwith the supplement linked under aSecurity hardeningsection. The skill also requires re-running Step 3.5 when any commit is added after the security review ran. This is the workflow change that produced this v0.12.1 release plan.
What changed for npm package users
MCP tools
submit_issue,add_issue_message,get_issue_statusJSON Schemas no longer carry a top-levelanyOf. The server still returns400 ERR_REPORTER_ENVIRONMENT_REQUIREDonsubmit_issuewhen neitherreporter_git_shanorreporter_app_versionis supplied, and still requiresentity_idorissue_numberon the other two. Codex and other strict OpenAI function-tool consumers can now register the schemas without rejection. Existing MCP clients that already pass a valid payload see no behavior change.- Updated tool descriptions surface the reporter-environment requirement and the
entity_id-or-issue_numberrequirement in prose.
HTTP API
- No new operations. No
openapi:bc-diffbreaking changes vsv0.12.0. tests/integration/guest_invalid_bearer_routes.test.tscodifies that the following routes return401(not anonymous downgrade) when called withAuthorization: Bearer <invalid-guest-token>:GET /entities/duplicates,GET /events/stream,POST /get_subscription_status,POST /issues/add_message,POST /issues/status,POST /issues/submit,POST /list_subscriptions,POST /subscribe,POST /unsubscribe.
CLI
neotoma api start --helpnow surfaces the--env dev/--env prodrequirement explicitly.tests/cli/cli_infra_commands.test.tspins the help output to mention--env dev or --env prodand theneotoma api start --env prodinvocation.
API surface & contracts
- OpenAPI: unchanged vs
v0.12.0.npm run openapi:bc-diffreports no breaking changes. - MCP tool schemas:
submit_issue,add_issue_message,get_issue_statusdrop the top-levelanyOffor OpenAI / Codex compatibility (server-side enforcement preserved).tests/contract/openclaw_plugin.test.tscodifies the absence of top-levelanyOfand the presence ofproperties.reporter_git_sha/properties.reporter_app_versiononsubmit_issue. - Schema seeding: unchanged.
storerequest schema documentation: the optional v0.12+ fieldsobservation_source,source_peer_id, andexternal_actorare now described indocs/specs/MCP_SPEC.mdtogether with their reducer semantics.
Behavior changes
- Guest auth path treats invalid bearer tokens as
401, not as anonymous downgrade. Routes that accept guests now require the guest principal to validate against the persisted token grant before being stamped on the request. This closes a gap where a syntactically valid but unrecognized bearer could bypass the validation step on guest-capable surfaces. prepublishOnly/pack:localfail closed when the Inspector submodule is missing. Operators who package or publish must rungit submodule update --init inspectorfirst; the previous "skip and continue" behavior is gone.
Agent-facing instruction changes
.cursor/skills/release/SKILL.mdand.claude/skills/release/SKILL.mdadd the Hard gate before Step 4 with the G1–G5 evidence checklist and the explicit "rerun Step 3.5 if any commit is added after the security review" rule.docs/specs/MCP_SPEC.mddocuments the `bu...
v0.12.0
Install
npm install -g neotoma@0.12.0| npm | https://www.npmjs.com/package/neotoma/v/0.12.0 |
| Compare | v0.11.1 → v0.12.0 — view diff |
v0.12.0 turns Neotoma into a multi-instance State Layer. Substrate subscriptions, peer-to-peer sync, and a first-class issues subsystem replace the legacy feedback path; the auth hardening introduced in v0.11.1 is carried forward here and extended with pre-release gates G1–G5, stricter proxy/loopback handling, and clearer operator guidance; and the dev/prod LaunchAgents, MCP stdio shims, and CLI surfaces are reworked to stop orphaning processes, stop drifting off canonical ports, and give operators a real processes servers view, plans capture/list, and a doctor that flags risky data directories.
Highlights
- Cross-instance peer sync becomes a first-class subsystem.
add_peer/list_peers/get_peer_status/remove_peer/sync_peership withPOST /sync/webhookinbound delivery (HMAC-signedX-Neotoma-Sync-Signature-256), AAuth-thumbprint loop prevention, andresolve_sync_conflictfor prefer-local vs prefer-remote reconciliation.get_peer_statusnow includesremote_health(/healthprobe +neotoma compatsemver check) so operators can see immediately when a peer is offline or runs an incompatible version.applyInboundSyncWebhookrejects mismatchedsender_peer_urlvalues and blocks private/loopback hostnames whenNEOTOMA_HOSTED_MODE=1. Seedocs/subsystems/peer_sync.md. - Substrate subscriptions with webhook + SSE transport.
subscribe/unsubscribe/list_subscriptions/get_subscription_statusplusGET /events/stream(SSE) let agents and external services watch entity / event / relationship changes for specificentity_types,entity_ids, orevent_types. Webhook deliveries are HMAC-signed; SSE uses the same auth as the API. Loop-preventionsync_peer_idis wired through both transports. - Issues replace the feedback subsystem end-to-end.
submit_issue,add_issue_message,get_issue_status,sync_issues,bulk_close_issues,bulk_remove_issuesare the canonical reporting flow, with GitHub mirror + operator forwarding + guest read-back tokens. Public submit + public-thread message paths run through the existingscanAndRedactPII guard as a backstop, ISO-date literals no longer false-positive as phone numbers, andloadIssueThreadMessagesdeduplicates across remote + local + multi-conversation threads. New CI workflows auto-label issues fromissue_kindand post upgrade-guidance comments on releases. - Reporter environment is required on
submit_issue, soft-required onadd_issue_message. The MCPsubmit_issueJSON Schema gainsanyOf: [{ required: [reporter_git_sha] }, { required: [reporter_app_version] }], so MCP clients fail validation locally before sending; the HTTPPOST /issues/submitroute returns400 ERR_REPORTER_ENVIRONMENT_REQUIREDwithdetails.acceptable_field_groups. On publicadd_issue_messagethreads, the server emits a stderr warning but still persists the message. - Security follow-through after v0.11.1. This branch carries forward the auth-bypass hardening from the v0.11.1 hotfix line and adds the work that makes it operationally durable:
isLocalRequestkeeps the stricter forwarded-hop handling, production loopback remains non-local by default (setNEOTOMA_TRUST_PROD_LOOPBACK=1to opt back in), a new CIsecurity_gatesjob runs G1 (security:classify-diff), G2 (security:lint), G3 (security:manifest:check+test:security:auth-matrix) on every PR, the sandbox weekly reset workflow now runs G5 deployed probes againsthttps://sandbox.neotoma.io,SECURITY.mdis rewritten with the disclosure flow and advisories index, anddocs/developer/mcp/proxy.mddocuments the operator-requiredMCP_PROXY_FAIL_CLOSED=1for AAuth-signed hosted deployments. - Operator security middleware. A dedicated guest write-rate limiter keys by AAuth thumbprint → hashed guest token → IP (
NEOTOMA_GUEST_WRITE_RATE_LIMIT_PER_MIN, default 30/min)./mcpnow validates OAuthBearertokens and responds with a proper401 invalid_tokenenvelope andWWW-Authenticateresource-metadata header when the token is unknown or expired. Guest access tokens carry an explicit TTL (NEOTOMA_GUEST_TOKEN_TTL_SECONDS, default 30 days) and arevoked_atfield, and persistence errors are no longer swallowed silently. - LaunchAgent and MCP shim reliability. The dev-server chokidar watcher now spawns the API as a process-group leader and SIGTERMs the whole group on reload, eliminating the orphan-process explosion that accumulated 50+ stale dev API instances on ports 3145–3179. Both LaunchAgents pre-kill incumbents on
3080/3180so dev and prod always bind their canonical ports.scripts/reload_neotoma_launchagents.sh --kill-zombiesrecognizes the actual orphan patterns observed in the wild. All MCP stdio shim wrappers (signed / unsigned, dev / prod, Cursor / Claude Code / Codex) source the new shared helpers (scripts/lib/neotoma_mcp_source_env.sh,scripts/lib/neotoma_mcp_resolve_downstream_url.sh), the resolver treats an explicitNEOTOMA_MCP_LOCAL_HTTP_PORT_PROFILE=dev|prodas sufficient to enable port-file mode (Cursor strips"1"flag values), and the downstream resolver warns on (but never honors) cross-profileMCP_PROXY_DOWNSTREAM_URLleakage. - CLI: new
processes servers,plans capture/list,access list/set/reset, doctor risk detection.neotoma processes serverscollapses parent-child process trees into one row per listener, surfacesLAUNCHAGENT/DATA_DIR/LOGS, and supports--watch.neotoma plans capture <file>/--allingests harness.plan.mdfiles (.cursor/plans,.claude/plans,.codex/plans,.openclaw/plans) via the canonical combinedstorepath.neotoma access listshows the winning policy source (env / schema_metadata / config_file / default);access set/reset/enable-issues/disable-issueswrite throughSchemaMetadata.guest_access_policyas the canonical source (the legacy config file is now a deprecated fallback only).neotoma doctor --jsonaddsdata.risksfor iCloud-synced / Documents / Desktop data directories and prior SQLite repair artifacts, plus asuggested_safe_data_dir. - OpenClaw plugin entry point. A new
src/openclaw_entry.tsregisters Neotoma MCP tools as OpenClaw agent tools, declareskind: "memory"so users can wire it viaplugins.slots.memory = "neotoma", and persists session lifecycle events (session.start,session.end,afterToolCall) into Neotoma using the same store-first contract as MCP. - Site, Inspector, agent-instruction expansion. ~80 new MDX pages under
docs/site/pages/en/cover personas (CRM, customer-ops, financial-ops, government, healthcare, logistics, compliance, diligence, crypto-engineering), comparisons (vs database, vs files, vs mem0, vs platform memory, vs RAG, vs Zep), integrations (Claude / Claude Code / Codex / Cursor / ChatGPT remote MCP), and risk/guarantee primitives (auditable change log, deterministic state evolution, human inspectability, conflicting-facts risk, false-closure risk, memory guarantees, non-destructive testing). The Inspector submodule advances to its peers + access-policy UI commit. Agent-facing instructions (docs/developer/mcp/instructions.md,docs/specs/MCP_SPEC.md,docs/developer/cli_reference.md) gain Inspector-linked display headings, anISSUE REPORTINGsection keyed offissues.reporting_mode(proactive / consent / off), and explicit reporter-env requirements.
What changed for npm package users
MCP tools
- New:
subscribe,unsubscribe,list_subscriptions,get_subscription_status,add_peer,list_peers,get_peer_status,remove_peer,sync_peer,resolve_sync_conflict,bulk_close_issues,bulk_remove_issues,restore_entity,restore_relationship,analyze_schema_candidates,get_schema_recommendations,update_schema_incremental. submit_issue: requiredanyOf: [reporter_git_sha, reporter_app_version]at the JSON Schema level. Acceptsvisibility: "advisory"as a hidden alias for"private"for one minor release; response includes_deprecation: "visibility 'advisory' is deprecated; use 'private' instead.". Per-rowdata_sourceandexternal_actorpropagated through the store contract.add_issue_message: acceptsreporter_git_sha,reporter_git_ref,reporter_channel,reporter_app_version. On public threads the server emits a stderr warning when bothreporter_git_shaandreporter_app_versionare missing; the message still persists.get_issue_status/add_issue_message: both accept eitherentity_idorissue_numberviaanyOf(issue_number is an integer).store: request shape gains optionalexternal_actor(GitHub upstream author for sync-replayed observations) andsource_peer_id(peer that originated the observation).observation_sourceenum gains"sync"for cross-instance replayed writes./mcpBearer auth: invalid or expired OAuth tokens now return401 invalid_tokenwith aWWW-Authenticateheader and a JSON-RPC error envelope instead of silently falling through to anonymous.
HTTP API
- 27 new operations added since v0.11.1. Issues:
issuesSubmit,issuesAddMessage,issuesGetStatus,issuesSync,bulkCloseIssues,bulkRemoveIssues. Subscriptions:subscribe,unsubscribe,listSubscriptions,getSubscriptionStatus,eventsStream(SSE). Peers + sync:addPeer,listPeers,getPeerStatus,removePeer,syncPeer,applySyncWebhook,listSyncEntities,resolveSyncConflict. Lifecycle:deleteEntity,deleteRelationship,restoreEntity,restoreRelationship. Schema: `analyzeSc...