@cerefox/memory 1.18.0 → 1.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENT_GUIDE.md +8 -3
- package/AGENT_QUICK_REFERENCE.md +3 -3
- package/dist/bin/cerefox.js +846 -657
- package/dist/frontend/assets/index-01k5C3sG.css +1 -0
- package/dist/frontend/assets/index-HNs8rgY1.js +121 -0
- package/dist/frontend/assets/index-HNs8rgY1.js.map +1 -0
- package/dist/frontend/index.html +2 -2
- package/dist/server-assets/_shared/ef-meta/index.ts +3 -3
- package/dist/server-assets/_shared/embeddings/onnx-embedder.ts +25 -9
- package/dist/server-assets/_shared/mcp-tools/_document-meta.ts +24 -2
- package/dist/server-assets/_shared/mcp-tools/_utils.ts +92 -0
- package/dist/server-assets/_shared/mcp-tools/audit-log.ts +6 -2
- package/dist/server-assets/_shared/mcp-tools/audit-ops.ts +27 -0
- package/dist/server-assets/_shared/mcp-tools/get-document.ts +11 -1
- package/dist/server-assets/_shared/mcp-tools/get-help-content.ts +3 -3
- package/dist/server-assets/_shared/mcp-tools/ingest.ts +55 -17
- package/dist/server-assets/_shared/mcp-tools/metadata-search.ts +3 -1
- package/dist/server-assets/_shared/mcp-tools/partial-edits.ts +8 -13
- package/dist/server-assets/_shared/mcp-tools/search.ts +11 -2
- package/dist/server-assets/_shared/mcp-tools/set-document-metadata.ts +31 -5
- package/dist/server-assets/db/migrations/0035_drop_content_hash_unique.sql +21 -0
- package/dist/server-assets/db/migrations/0036_metadata_revision.sql +43 -0
- package/dist/server-assets/db/rpcs.sql +252 -53
- package/dist/server-assets/db/schema.sql +41 -4
- package/dist/server-assets/supabase/functions/cerefox-get-document/index.ts +8 -1
- package/dist/server-assets/supabase/functions/cerefox-ingest/index.ts +42 -6
- package/docs/api/openapi.json +61 -1
- package/docs/guides/cli.md +3 -1
- package/docs/guides/configuration.md +1 -1
- package/docs/guides/connect-agents.md +18 -7
- package/docs/guides/ops-scripts.md +1 -1
- package/docs/guides/setup-local.md +30 -6
- package/docs/guides/upgrading.md +14 -0
- package/package.json +6 -6
- package/dist/frontend/assets/index-B0H_N4if.css +0 -1
- package/dist/frontend/assets/index-CxMT7hjH.js +0 -121
- package/dist/frontend/assets/index-CxMT7hjH.js.map +0 -1
package/AGENT_GUIDE.md
CHANGED
|
@@ -74,7 +74,8 @@ Save a new document or update an existing one.
|
|
|
74
74
|
| `last_write_wins` | No | Explicitly skip the concurrency check (default `false`). Use ONLY when an external source of truth makes conflicts meaningless (file re-sync). Recorded in the audit log. **Never use it to silence a conflict.** |
|
|
75
75
|
| `project_name` | No | **Single** project name (created if absent). On update: **non-destructive add** — ensures this membership exists, preserves others. See "Project membership semantics" below. |
|
|
76
76
|
| `project_names` | No | **List** of project names (each created if absent). On update: **destructive replace** — sets the document's full project set to exactly this list. Use when you want to set multiple projects at once, or deliberately change the membership list. Wins over `project_name` when both are passed. |
|
|
77
|
-
| `metadata` | No | Arbitrary JSON. Use at minimum: `type` and `status`. **On update
|
|
77
|
+
| `metadata` | No | Arbitrary JSON. Use at minimum: `type` and `status`. **On update it MERGES** (v1.19.0): keys you pass are set, a `null` value removes that key, keys you omit are kept; omitting the field keeps the metadata as is. To clear or reset tags wholesale, use `cerefox_set_document_metadata` with `replace: true`. |
|
|
78
|
+
| `expected_metadata_revision` | No | With `metadata` on an update: the `metadata_revision` you read. The write fails with a metadata conflict if the tags changed since. |
|
|
78
79
|
| `author` | No | Your agent name for audit attribution. Always set this. |
|
|
79
80
|
| `source` | No | Origin label (default "agent"). |
|
|
80
81
|
|
|
@@ -89,7 +90,7 @@ Save a new document or update an existing one.
|
|
|
89
90
|
2. Call `cerefox_ingest` with the **exact same title**, `update_if_exists: true`, and `expected_content_hash`.
|
|
90
91
|
3. If you use a different title, a **new** document is created (the old one remains). This is almost never what you want when revising.
|
|
91
92
|
|
|
92
|
-
**Deduplication**: Content is SHA-256 hashed.
|
|
93
|
+
**Deduplication**: Content is SHA-256 hashed. Re-saving a document with its own unchanged content is skipped (no re-indexing, no concurrency check needed — identical content cannot lose data). Creating a new document whose content matches a live document returns that document instead of a copy. Nothing else de-duplicates: an update may leave two documents with the same content, and a document in the trash never blocks a write. Metadata-only changes update metadata without creating a version.
|
|
93
94
|
|
|
94
95
|
#### Concurrent writers (optimistic concurrency)
|
|
95
96
|
|
|
@@ -307,9 +308,13 @@ Change a document's metadata (tags) **without resending its content** (v1.6.0, #
|
|
|
307
308
|
|-----------|----------|-------------|
|
|
308
309
|
| `document_id` | Yes | UUID of the document to tag. |
|
|
309
310
|
| `metadata` | Yes | Keys to set. Values are JSON strings by convention (a `metadata_filter` matches JSONB as strings). A `null` value REMOVES that key. Keys you do not mention are left alone unless `replace` is true. |
|
|
310
|
-
| `replace` | No | Set the metadata to EXACTLY this object, discarding any key not listed. Rare
|
|
311
|
+
| `replace` | No | Set the metadata to EXACTLY this object, discarding any key not listed. Rare. Requires `expected_metadata_revision` (or `last_write_wins`). |
|
|
312
|
+
| `expected_metadata_revision` | No | The `metadata_revision` you read (shown next to `content_hash` by `cerefox_get_document`, `cerefox_search` and `cerefox_metadata_search`). Optional on a merge: pass it when your change depends on the current values (a status transition, a counter), and the write fails if anyone changed the tags since. Required with `replace`. |
|
|
313
|
+
| `last_write_wins` | No | Replace without a revision, regardless of concurrent changes. Recorded in the audit log. |
|
|
311
314
|
| `author` | No | Your agent name (audit + usage log). |
|
|
312
315
|
|
|
316
|
+
**Metadata has its own concurrency token** (v1.19.0, #333): `metadata_revision`, separate from `content_hash`, so a tag change never invalidates a body edit and vice versa. A stale one returns a *metadata conflict*: re-read, reapply your change, retry with the new value. Every metadata change is recorded in the audit log with the values before and after, so a mistake can be undone.
|
|
317
|
+
|
|
313
318
|
Content, chunks and embeddings are untouched; no new version is created. The response reports what *changed* (keys set / removed), not what was asked for. Use this instead of `cerefox_ingest` whenever only the tags are changing.
|
|
314
319
|
|
|
315
320
|
---
|
package/AGENT_QUICK_REFERENCE.md
CHANGED
|
@@ -7,7 +7,7 @@ Cerefox is a persistent, shared knowledge base. You have **15 core MCP tools** (
|
|
|
7
7
|
| Tool | Purpose | Key params |
|
|
8
8
|
|------|---------|------------|
|
|
9
9
|
| `cerefox_search` | Find documents (hybrid FTS + semantic) | `query` (required), `project_name`, `metadata_filter`, `author` |
|
|
10
|
-
| `cerefox_ingest` | Save or update a document | `title`, `content` (required), `document_id` (update by ID), `expected_content_hash` (**required on content updates** — see rule 10), `last_write_wins`, `update_if_exists`, `project_name` (single, non-destructive add on update), `project_names` (list, destructive replace on update), `metadata` (
|
|
10
|
+
| `cerefox_ingest` | Save or update a document | `title`, `content` (required), `document_id` (update by ID), `expected_content_hash` (**required on content updates** — see rule 10), `last_write_wins`, `update_if_exists`, `project_name` (single, non-destructive add on update), `project_names` (list, destructive replace on update), `metadata` (on update it **merges**: `null` removes a key, omitted keys are kept), `expected_metadata_revision` (optional, see rule 10), `author` |
|
|
11
11
|
| `cerefox_insert` | **Add** to a document without resending it. Cannot destroy content. | `document_id`, `text`, `position` (`end_of_document`/`end_of_section`/`after_heading`/`before_heading`), `expected_content_hash` (required), `anchor_heading` (unless `end_of_document`), `section_part`, `author` |
|
|
12
12
|
| `cerefox_edit` | **Change** parts of a document: 1..n operations applied atomically | `document_id`, `operations` (`insert`/`replace_section`/`delete_section`/`rename_section`), `expected_content_hash` (required), `author` |
|
|
13
13
|
| `cerefox_delete_document` | **Soft**-delete a document (to trash; excluded from search; permanent purge is human-only) | `document_id`, `expected_content_hash` (**required** — a delete must follow a read), `reason` (recorded in the audit log — give one), `author` |
|
|
@@ -21,7 +21,7 @@ Cerefox is a persistent, shared knowledge base. You have **15 core MCP tools** (
|
|
|
21
21
|
| `cerefox_metadata_search` | Find or list docs by metadata, project, or time (no text query) | `metadata_filter`, `project_name` (list a project's docs), `updated_since`, `include_content` — **at least one** of metadata_filter/project_name/updated_since/created_since |
|
|
22
22
|
| `cerefox_list_metadata_keys` | Discover available metadata keys | (none required) |
|
|
23
23
|
| `cerefox_list_projects` | List all projects | (none required) |
|
|
24
|
-
| `cerefox_set_document_metadata` | Change tags WITHOUT resending content. **Merges** by default; a `null` value removes a key | `document_id`, `metadata` (required), `replace` (rare: set exactly this object), `author` |
|
|
24
|
+
| `cerefox_set_document_metadata` | Change tags WITHOUT resending content. **Merges** by default; a `null` value removes a key | `document_id`, `metadata` (required), `replace` (rare: set exactly this object; needs `expected_metadata_revision`), `expected_metadata_revision`, `author` |
|
|
25
25
|
| `cerefox_set_document_projects` | Set doc's project memberships to exactly the given list (destructive replace; metadata-only, no content change) | `document_id`, `project_names` (required), `author` |
|
|
26
26
|
| `cerefox_get_audit_log` | Query write operation history | `document_id`, `by_author` (filter), `operation`, `since`, `author` |
|
|
27
27
|
| `cerefox_get_help` | Retrieve Cerefox conventions (this reference) over MCP. **Call this whenever uncertain.** | `topic` (optional, case-insensitive H2 substring match) |
|
|
@@ -81,7 +81,7 @@ recoverable answer, not a failure: retry with what it gave you.
|
|
|
81
81
|
7. **Write for retrieval: the title does real work.** The document title is indexed at the **highest weight** *and* prepended to every chunk before it is embedded, so it steers both halves of hybrid search (keyword and semantic) for every chunk in the document. Name the document with the distinctive terms someone would actually search for -- "Cerefox Response Size Limits", not "Notes" or "Update 3". Renaming re-indexes and re-embeds, so correcting a vague title is cheap and takes effect immediately. Write the characters themselves: `&` and `<>`, never `&` or `<>` (entities are stored literally and the write reply will say so). Inside the content, write structured Markdown with H1/H2/H3 headings: the chunker follows them, and a chunk's own heading carries the same weight as the title.
|
|
82
82
|
8. **Deletes are soft (recoverable); permanent purge is web-UI-only.** `cerefox_delete_document` requires the document's `content_hash` as you read it (read before you delete) and takes a `reason` — give one; it is what the human reviewing the trash sees. `cerefox_restore_document` undoes a mistaken delete (also audited, also takes a `reason`). Always surface deletes AND restores to the user. Once a human purges from the web UI, the document is gone for good; so is one auto-purged after `trash_retention_days`, if the operator has turned trash auto-purge on.
|
|
83
83
|
9. **Cross-doc links inside content**: **always use `[Text](document-uuid)`.** UUIDs are the only fully reliable link form — stable across title changes, never ambiguous, no encoding gotchas. Every `cerefox_search` result shows `[id: <uuid>]` after the title; grab it and use it. Title-based linking (`[Text](<Title With Spaces>)`) is fragile (breaks on colons, parens, ampersands, brackets — silently navigates to wrong page) — **don't write title-based links**; do an extra search to get the UUID instead. Repo-path forms (`[Text](docs/path.md)`) exist for repo-ingested files; don't construct manually. **The server validates `](uuid)` links on every write** (v1.7.0): a link to a nonexistent id rejects the write, naming the offender — that means you mangled the UUID; re-read the source and correct it, do not retry unchanged. Example ids go in backticks (code is not validated). `[[Wikilinks]]` may dangle. See `AGENT_GUIDE.md → Writing linkable content` for the full rule.
|
|
84
|
-
10. **Concurrency: content updates require `expected_content_hash`.** Pass the `content_hash` you last saw — every read shows one (`cerefox_get_document` incl. outline mode, `cerefox_search`, `cerefox_metadata_search`) and **every write returns the new one, including create** (v1.3.0, #189), so after writing you already hold the token for your next edit; no re-read needed. If it's stale you get a **conflict** — re-read the document, merge your changes into the latest content, retry with the new hash. **Never resolve a conflict by overwriting blindly** — the current content includes another writer's work. `last_write_wins: true` skips the check; use it ONLY when an external source of truth makes conflicts meaningless (file re-sync), never to silence a conflict.
|
|
84
|
+
10. **Concurrency: content updates require `expected_content_hash`.** Pass the `content_hash` you last saw — every read shows one (`cerefox_get_document` incl. outline mode, `cerefox_search`, `cerefox_metadata_search`) and **every write returns the new one, including create** (v1.3.0, #189), so after writing you already hold the token for your next edit; no re-read needed. If it's stale you get a **conflict** — re-read the document, merge your changes into the latest content, retry with the new hash. **Never resolve a conflict by overwriting blindly** — the current content includes another writer's work. `last_write_wins: true` skips the check; use it ONLY when an external source of truth makes conflicts meaningless (file re-sync), never to silence a conflict. **Metadata has its own token, `metadata_revision`** (shown next to `content_hash` wherever it appears). It is optional on a merge (pass it when your change depends on the current values, e.g. a status transition) and **required with `replace: true`**. A stale one is a *metadata conflict*: re-read, reapply, retry.
|
|
85
85
|
11. **Search: prefer a few distinctive terms; heed `below confidence`.** When nothing clears the relevance threshold, `cerefox_search` returns the closest candidates prefixed with a `below confidence` warning instead of an empty set — that flag means **weak signal, not absent knowledge**: check the candidates' scores and titles before concluding the KB lacks the content. A truly empty response means nothing even weakly related exists.
|
|
86
86
|
12. **Relations express how documents relate; lifecycle tells you if knowledge is still good.** Use `cerefox_set_relation` when one document supersedes, contradicts, references, or continues another. `supersedes` marks the target **superseded**; `contradicts` marks **both** stale; `related_to`/`duplicates`/`contradicts` are symmetric (both directions written). Any other type string is accepted without special behavior. When a search result or `cerefox_get_relations` shows a neighbour marked `[superseded]` or `[stale]`, say so rather than presenting it as current.
|
|
87
87
|
13. **Project memberships — non-destructive by default**: on `cerefox_ingest` updates, **`project_name` (singular) is a non-destructive add** (ensures membership, preserves others). Use **`project_names` (list)** when you want to set the doc's full project set in one call (destructive replace). For metadata-only project changes without writing content, use **`cerefox_set_document_projects(document_id, project_names)`** — that tool is the destructive-replace contract made explicit. Never call `cerefox_set_document_projects` with a single name when you mean "add" — that would REMOVE the doc from all other projects. When in doubt, use `cerefox_ingest` with singular `project_name`.
|