@cerefox/memory 1.1.1 → 1.2.1

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.
@@ -7438,7 +7438,7 @@ var exports_meta = {};
7438
7438
  __export(exports_meta, {
7439
7439
  PKG_VERSION: () => PKG_VERSION
7440
7440
  });
7441
- var PKG_VERSION = "1.1.1";
7441
+ var PKG_VERSION = "1.2.1";
7442
7442
  var init_meta = () => {};
7443
7443
 
7444
7444
  // ../../_shared/config/paths.ts
@@ -25858,7 +25858,7 @@ async function checkServerCompatibility(opts) {
25858
25858
  var COMPATIBILITY;
25859
25859
  var init_compatibility = __esm(() => {
25860
25860
  COMPATIBILITY = {
25861
- minSchema: "0.10.3",
25861
+ minSchema: "0.10.5",
25862
25862
  minEdgeFunctions: "0.6.0"
25863
25863
  };
25864
25864
  });
@@ -55148,6 +55148,12 @@ var init_audit_log = __esm(() => {
55148
55148
  auditLogTool = {
55149
55149
  name: "cerefox_get_audit_log",
55150
55150
  description: "Retrieve audit log entries showing who changed what and when. Supports filtering by document, author, operation type, and time range. Returns entries with document titles, author attribution, size changes, and descriptions.",
55151
+ annotations: {
55152
+ title: "Read audit log",
55153
+ readOnlyHint: true,
55154
+ idempotentHint: true,
55155
+ openWorldHint: false
55156
+ },
55151
55157
  inputSchema: {
55152
55158
  type: "object",
55153
55159
  required: [],
@@ -55346,6 +55352,13 @@ var init_relations = __esm(() => {
55346
55352
  setRelationTool = {
55347
55353
  name: "cerefox_set_relation",
55348
55354
  description: "Link two documents with a typed, directed relation (source → target). " + `Known types with behaviour: ${KNOWN_TYPES}. Symmetric types (related_to, ` + "contradicts, duplicates) write both directions. `supersedes` marks the target " + "superseded; `contradicts` marks both stale. Any other type string is accepted " + "and stored, just without special behaviour. Re-setting the same edge updates it.",
55355
+ annotations: {
55356
+ title: "Link two documents",
55357
+ readOnlyHint: false,
55358
+ destructiveHint: false,
55359
+ idempotentHint: true,
55360
+ openWorldHint: false
55361
+ },
55349
55362
  inputSchema: {
55350
55363
  type: "object",
55351
55364
  required: ["source_id", "target_id", "rel_type"],
@@ -55369,6 +55382,13 @@ var init_relations = __esm(() => {
55369
55382
  deleteRelationTool = {
55370
55383
  name: "cerefox_delete_relation",
55371
55384
  description: "Remove a typed relation between two documents. Symmetric types remove both " + "directions. Lifecycle status set by an earlier relation is NOT reverted.",
55385
+ annotations: {
55386
+ title: "Remove a relation",
55387
+ readOnlyHint: false,
55388
+ destructiveHint: true,
55389
+ idempotentHint: true,
55390
+ openWorldHint: false
55391
+ },
55372
55392
  inputSchema: {
55373
55393
  type: "object",
55374
55394
  required: ["source_id", "target_id", "rel_type"],
@@ -55385,6 +55405,12 @@ var init_relations = __esm(() => {
55385
55405
  getRelationsTool = {
55386
55406
  name: "cerefox_get_relations",
55387
55407
  description: "List every relation touching a document, in both directions (→ outbound, " + "← inbound). Shows each neighbour's title and lifecycle status, so an agent " + "can tell whether retrieved knowledge has been superseded or contradicted.",
55408
+ annotations: {
55409
+ title: "List a document's relations",
55410
+ readOnlyHint: true,
55411
+ idempotentHint: true,
55412
+ openWorldHint: false
55413
+ },
55388
55414
  inputSchema: {
55389
55415
  type: "object",
55390
55416
  required: ["document_id"],
@@ -55398,6 +55424,12 @@ var init_relations = __esm(() => {
55398
55424
  getNeighborsTool = {
55399
55425
  name: "cerefox_get_neighbors",
55400
55426
  description: "Walk the relation graph outward from a document along ONE relation type. " + "Use after cerefox_get_relations shows which types exist. depth > 1 follows " + "chains (useful for follows / reply_to); cycles terminate safely. Optional " + "from_time / to_time filter neighbours by their creation time.",
55427
+ annotations: {
55428
+ title: "Walk the relation graph",
55429
+ readOnlyHint: true,
55430
+ idempotentHint: true,
55431
+ openWorldHint: false
55432
+ },
55401
55433
  inputSchema: {
55402
55434
  type: "object",
55403
55435
  required: ["document_id", "rel_type"],
@@ -55450,6 +55482,12 @@ var init_get_document = __esm(() => {
55450
55482
  getDocumentTool = {
55451
55483
  name: "cerefox_get_document",
55452
55484
  description: "Retrieve the full reconstructed content of a document. Pass version_id to retrieve an archived version; omit it (or pass null) for the current version. Version UUIDs are returned by cerefox_list_versions. The response header includes the document's current content_hash — pass it back as expected_content_hash when updating via cerefox_ingest (optimistic concurrency).",
55485
+ annotations: {
55486
+ title: "Read document",
55487
+ readOnlyHint: true,
55488
+ idempotentHint: true,
55489
+ openWorldHint: false
55490
+ },
55453
55491
  inputSchema: {
55454
55492
  type: "object",
55455
55493
  required: ["document_id"],
@@ -55535,6 +55573,12 @@ var init_get_help = __esm(() => {
55535
55573
  getHelpTool = {
55536
55574
  name: "cerefox_get_help",
55537
55575
  description: "Retrieve Cerefox's own agent-usage guidance (the AGENT_QUICK_REFERENCE.md content). Call with no arguments to get the full reference + a section index. Call with `topic` to get a single section (case-insensitive substring match against H2 headings). Use this whenever you're uncertain about Cerefox conventions — link forms, project-membership semantics, update workflows, etc.",
55576
+ annotations: {
55577
+ title: "Read Cerefox conventions",
55578
+ readOnlyHint: true,
55579
+ idempotentHint: true,
55580
+ openWorldHint: false
55581
+ },
55538
55582
  inputSchema: {
55539
55583
  type: "object",
55540
55584
  properties: {
@@ -55789,6 +55833,13 @@ var init_ingest = __esm(() => {
55789
55833
  ingestTool = {
55790
55834
  name: "cerefox_ingest",
55791
55835
  description: "Save a note or document to the Cerefox knowledge base.",
55836
+ annotations: {
55837
+ title: "Save or update a document",
55838
+ readOnlyHint: false,
55839
+ destructiveHint: true,
55840
+ idempotentHint: false,
55841
+ openWorldHint: false
55842
+ },
55792
55843
  inputSchema: {
55793
55844
  type: "object",
55794
55845
  required: ["title", "content"],
@@ -55853,6 +55904,12 @@ var init_list_metadata_keys = __esm(() => {
55853
55904
  listMetadataKeysTool = {
55854
55905
  name: "cerefox_list_metadata_keys",
55855
55906
  description: "List all metadata keys currently in use across documents in the Cerefox knowledge base. Returns each key with its document count and up to 5 example values.",
55907
+ annotations: {
55908
+ title: "List metadata keys",
55909
+ readOnlyHint: true,
55910
+ idempotentHint: true,
55911
+ openWorldHint: false
55912
+ },
55856
55913
  inputSchema: {
55857
55914
  type: "object",
55858
55915
  properties: {
@@ -55894,6 +55951,12 @@ var init_list_projects = __esm(() => {
55894
55951
  listProjectsTool = {
55895
55952
  name: "cerefox_list_projects",
55896
55953
  description: "List all projects with their names and IDs. Use this to discover available projects before filtering by project_name in other tools.",
55954
+ annotations: {
55955
+ title: "List projects",
55956
+ readOnlyHint: true,
55957
+ idempotentHint: true,
55958
+ openWorldHint: false
55959
+ },
55897
55960
  inputSchema: {
55898
55961
  type: "object",
55899
55962
  properties: {
@@ -55939,6 +56002,12 @@ var init_list_versions = __esm(() => {
55939
56002
  listVersionsTool = {
55940
56003
  name: "cerefox_list_versions",
55941
56004
  description: "List all archived versions of a document, newest first. Returns version_id (use with cerefox_get_document), version_number, source, chunk_count, total_chars, and created_at.",
56005
+ annotations: {
56006
+ title: "List document versions",
56007
+ readOnlyHint: true,
56008
+ idempotentHint: true,
56009
+ openWorldHint: false
56010
+ },
55942
56011
  inputSchema: {
55943
56012
  type: "object",
55944
56013
  required: ["document_id"],
@@ -56032,6 +56101,12 @@ var init_metadata_search = __esm(() => {
56032
56101
  metadataSearchTool = {
56033
56102
  name: "cerefox_metadata_search",
56034
56103
  description: "Find or list documents by metadata key-value criteria without a text search term. Use to discover documents tagged with specific attributes, browse by taxonomy, retrieve messages/tasks by type and status, or list all documents in a project (pass project_name alone). At least one of metadata_filter, project_name, updated_since, or created_since must be supplied; results are ordered newest-updated first.",
56104
+ annotations: {
56105
+ title: "Find documents by metadata",
56106
+ readOnlyHint: true,
56107
+ idempotentHint: true,
56108
+ openWorldHint: false
56109
+ },
56035
56110
  inputSchema: {
56036
56111
  type: "object",
56037
56112
  properties: {
@@ -56192,6 +56267,12 @@ var init_search = __esm(() => {
56192
56267
  searchTool = {
56193
56268
  name: "cerefox_search",
56194
56269
  description: "Search the Cerefox personal knowledge base. Returns complete documents ranked by hybrid (FTS + semantic) relevance.",
56270
+ annotations: {
56271
+ title: "Search knowledge base",
56272
+ readOnlyHint: true,
56273
+ idempotentHint: true,
56274
+ openWorldHint: false
56275
+ },
56195
56276
  inputSchema: {
56196
56277
  type: "object",
56197
56278
  required: ["query"],
@@ -56277,6 +56358,13 @@ var init_set_document_projects = __esm(() => {
56277
56358
  setDocumentProjectsTool = {
56278
56359
  name: "cerefox_set_document_projects",
56279
56360
  description: "Set the document's project memberships to EXACTLY the given list. Destructive replace: any existing memberships not in this list are removed. Pass an empty list to clear all project memberships. Projects are looked up by name (case-insensitive); missing projects are created. Logged as update-metadata in the audit log — content is untouched. Use cerefox_ingest with project_names if you want to set memberships AND update content in one call. Use this tool when you only need to change project membership without re-writing the document body.",
56361
+ annotations: {
56362
+ title: "Replace project memberships",
56363
+ readOnlyHint: false,
56364
+ destructiveHint: true,
56365
+ idempotentHint: true,
56366
+ openWorldHint: false
56367
+ },
56280
56368
  inputSchema: {
56281
56369
  type: "object",
56282
56370
  required: ["document_id", "project_names"],
@@ -69491,7 +69579,8 @@ function buildServer() {
69491
69579
  tools: (await listEnabledTools(supabase)).map((t) => ({
69492
69580
  name: t.name,
69493
69581
  description: t.description,
69494
- inputSchema: t.inputSchema
69582
+ inputSchema: t.inputSchema,
69583
+ ...t.annotations ? { annotations: t.annotations } : {}
69495
69584
  }))
69496
69585
  }));
69497
69586
  server.setRequestHandler(CallToolRequestSchema, async (req) => {
@@ -75610,8 +75699,8 @@ import { homedir as homedir6 } from "node:os";
75610
75699
  import { join as join9 } from "node:path";
75611
75700
 
75612
75701
  // ../../_shared/ef-meta/index.ts
75613
- var EF_VERSION = "1.1.1";
75614
- var EF_LAST_CHANGED = "1.1.0-beta.7";
75702
+ var EF_VERSION = "1.2.1";
75703
+ var EF_LAST_CHANGED = "1.2.0";
75615
75704
 
75616
75705
  // src/cli/util/checks.ts
75617
75706
  init_config();
@@ -76838,6 +76927,7 @@ class IngestionPipeline {
76838
76927
  text,
76839
76928
  title,
76840
76929
  source = "paste",
76930
+ sourceLabel,
76841
76931
  sourcePath: sourcePathOpt,
76842
76932
  projectName,
76843
76933
  projectId,
@@ -76868,6 +76958,7 @@ class IngestionPipeline {
76868
76958
  text,
76869
76959
  title,
76870
76960
  source,
76961
+ sourceLabel,
76871
76962
  projectIds: fullSetResolved,
76872
76963
  metadata,
76873
76964
  author,
@@ -76906,6 +76997,7 @@ class IngestionPipeline {
76906
76997
  text,
76907
76998
  title,
76908
76999
  source,
77000
+ sourceLabel,
76909
77001
  projectIds: fullSetResolved,
76910
77002
  metadata,
76911
77003
  author,
@@ -76993,6 +77085,7 @@ class IngestionPipeline {
76993
77085
  text,
76994
77086
  title,
76995
77087
  source = "manual",
77088
+ sourceLabel,
76996
77089
  projectId,
76997
77090
  projectIds,
76998
77091
  metadata,
@@ -77109,7 +77202,7 @@ class IngestionPipeline {
77109
77202
  contentFormat: CONTENT_FORMAT_BLIND_STITCH,
77110
77203
  author,
77111
77204
  authorType,
77112
- sourceLabel: source,
77205
+ sourceLabel: sourceLabel ?? source,
77113
77206
  expectedContentHash: expectedContentHash ?? (forceRechunk && contentUnchanged ? existing.content_hash : null),
77114
77207
  lastWriteWins
77115
77208
  });
@@ -78286,7 +78379,8 @@ async function action28(options) {
78286
78379
  text: doc2.full_content,
78287
78380
  title: doc2.doc_title,
78288
78381
  documentId: id,
78289
- source: "migrate-format",
78382
+ source: doc2.doc_source,
78383
+ sourceLabel: "migrate-format",
78290
78384
  author,
78291
78385
  authorType,
78292
78386
  expectedContentHash: doc2.content_hash,
@@ -18,7 +18,7 @@
18
18
  * doesn't touch `supabase/functions/` leaves it alone).
19
19
  */
20
20
 
21
- export const EF_VERSION = "1.1.1";
21
+ export const EF_VERSION = "1.2.1";
22
22
 
23
23
  /**
24
24
  * The most recent version whose EF-side SOURCE actually changed (#127).
@@ -28,7 +28,7 @@ export const EF_VERSION = "1.1.1";
28
28
  * `cut_release.ts` ONLY when EF source changed since the last tag; doctor
29
29
  * uses it to stay silent on label-only drift.
30
30
  */
31
- export const EF_LAST_CHANGED = "1.1.0-beta.7";
31
+ export const EF_LAST_CHANGED = "1.2.0";
32
32
 
33
33
  /**
34
34
  * The 8 peer EFs the cerefox-mcp aggregator probes (excludes cerefox-mcp
@@ -65,6 +65,13 @@ export const auditLogTool: ToolDefinition = {
65
65
  name: "cerefox_get_audit_log",
66
66
  description:
67
67
  "Retrieve audit log entries showing who changed what and when. Supports filtering by document, author, operation type, and time range. Returns entries with document titles, author attribution, size changes, and descriptions.",
68
+ // Read-only: touches nothing. Safe for a client to run without prompting.
69
+ annotations: {
70
+ title: "Read audit log",
71
+ readOnlyHint: true,
72
+ idempotentHint: true,
73
+ openWorldHint: false,
74
+ },
68
75
  inputSchema: {
69
76
  type: "object",
70
77
  required: [],
@@ -57,6 +57,13 @@ export const getDocumentTool: ToolDefinition = {
57
57
  name: "cerefox_get_document",
58
58
  description:
59
59
  "Retrieve the full reconstructed content of a document. Pass version_id to retrieve an archived version; omit it (or pass null) for the current version. Version UUIDs are returned by cerefox_list_versions. The response header includes the document's current content_hash — pass it back as expected_content_hash when updating via cerefox_ingest (optimistic concurrency).",
60
+ // Read-only: touches nothing. Safe for a client to run without prompting.
61
+ annotations: {
62
+ title: "Read document",
63
+ readOnlyHint: true,
64
+ idempotentHint: true,
65
+ openWorldHint: false,
66
+ },
60
67
  inputSchema: {
61
68
  type: "object",
62
69
  required: ["document_id"],
@@ -71,6 +71,13 @@ export const getHelpTool: ToolDefinition = {
71
71
  name: "cerefox_get_help",
72
72
  description:
73
73
  "Retrieve Cerefox's own agent-usage guidance (the AGENT_QUICK_REFERENCE.md content). Call with no arguments to get the full reference + a section index. Call with `topic` to get a single section (case-insensitive substring match against H2 headings). Use this whenever you're uncertain about Cerefox conventions — link forms, project-membership semantics, update workflows, etc.",
74
+ // Read-only: touches nothing. Safe for a client to run without prompting.
75
+ annotations: {
76
+ title: "Read Cerefox conventions",
77
+ readOnlyHint: true,
78
+ idempotentHint: true,
79
+ openWorldHint: false,
80
+ },
74
81
  inputSchema: {
75
82
  type: "object",
76
83
  properties: {
@@ -350,6 +350,17 @@ async function handler(
350
350
  export const ingestTool: ToolDefinition = {
351
351
  name: "cerefox_ingest",
352
352
  description: "Save a note or document to the Cerefox knowledge base.",
353
+ /** Destructive: `project_names` REPLACES the document's project memberships, and
354
+ * memberships have no version history — a partial list silently drops the rest.
355
+ * Content itself is version-snapshotted and guarded by expected_content_hash, so
356
+ * the destructive part is the membership replace, not the body. */
357
+ annotations: {
358
+ title: "Save or update a document",
359
+ readOnlyHint: false,
360
+ destructiveHint: true,
361
+ idempotentHint: false,
362
+ openWorldHint: false,
363
+ },
353
364
  inputSchema: {
354
365
  type: "object",
355
366
  required: ["title", "content"],
@@ -41,6 +41,13 @@ export const listMetadataKeysTool: ToolDefinition = {
41
41
  name: "cerefox_list_metadata_keys",
42
42
  description:
43
43
  "List all metadata keys currently in use across documents in the Cerefox knowledge base. Returns each key with its document count and up to 5 example values.",
44
+ // Read-only: touches nothing. Safe for a client to run without prompting.
45
+ annotations: {
46
+ title: "List metadata keys",
47
+ readOnlyHint: true,
48
+ idempotentHint: true,
49
+ openWorldHint: false,
50
+ },
44
51
  inputSchema: {
45
52
  type: "object",
46
53
  properties: {
@@ -45,6 +45,13 @@ export const listProjectsTool: ToolDefinition = {
45
45
  name: "cerefox_list_projects",
46
46
  description:
47
47
  "List all projects with their names and IDs. Use this to discover available projects before filtering by project_name in other tools.",
48
+ // Read-only: touches nothing. Safe for a client to run without prompting.
49
+ annotations: {
50
+ title: "List projects",
51
+ readOnlyHint: true,
52
+ idempotentHint: true,
53
+ openWorldHint: false,
54
+ },
48
55
  inputSchema: {
49
56
  type: "object",
50
57
  properties: {
@@ -53,6 +53,13 @@ export const listVersionsTool: ToolDefinition = {
53
53
  name: "cerefox_list_versions",
54
54
  description:
55
55
  "List all archived versions of a document, newest first. Returns version_id (use with cerefox_get_document), version_number, source, chunk_count, total_chars, and created_at.",
56
+ // Read-only: touches nothing. Safe for a client to run without prompting.
57
+ annotations: {
58
+ title: "List document versions",
59
+ readOnlyHint: true,
60
+ idempotentHint: true,
61
+ openWorldHint: false,
62
+ },
56
63
  inputSchema: {
57
64
  type: "object",
58
65
  required: ["document_id"],
@@ -124,6 +124,13 @@ export const metadataSearchTool: ToolDefinition = {
124
124
  name: "cerefox_metadata_search",
125
125
  description:
126
126
  "Find or list documents by metadata key-value criteria without a text search term. Use to discover documents tagged with specific attributes, browse by taxonomy, retrieve messages/tasks by type and status, or list all documents in a project (pass project_name alone). At least one of metadata_filter, project_name, updated_since, or created_since must be supplied; results are ordered newest-updated first.",
127
+ // Read-only: touches nothing. Safe for a client to run without prompting.
128
+ annotations: {
129
+ title: "Find documents by metadata",
130
+ readOnlyHint: true,
131
+ idempotentHint: true,
132
+ openWorldHint: false,
133
+ },
127
134
  inputSchema: {
128
135
  type: "object",
129
136
  properties: {
@@ -81,6 +81,14 @@ export const setRelationTool: ToolDefinition = {
81
81
  "contradicts, duplicates) write both directions. `supersedes` marks the target " +
82
82
  "superseded; `contradicts` marks both stale. Any other type string is accepted " +
83
83
  "and stored, just without special behaviour. Re-setting the same edge updates it.",
84
+ /** Upsert: re-running with the same edge changes nothing. Adds an edge; removes nothing. */
85
+ annotations: {
86
+ title: "Link two documents",
87
+ readOnlyHint: false,
88
+ destructiveHint: false,
89
+ idempotentHint: true,
90
+ openWorldHint: false,
91
+ },
84
92
  inputSchema: {
85
93
  type: "object",
86
94
  required: ["source_id", "target_id", "rel_type"],
@@ -143,6 +151,14 @@ export const deleteRelationTool: ToolDefinition = {
143
151
  description:
144
152
  "Remove a typed relation between two documents. Symmetric types remove both " +
145
153
  "directions. Lifecycle status set by an earlier relation is NOT reverted.",
154
+ /** Removes an edge (and its mirror for symmetric types). Relations are not versioned, so the edge is gone. */
155
+ annotations: {
156
+ title: "Remove a relation",
157
+ readOnlyHint: false,
158
+ destructiveHint: true,
159
+ idempotentHint: true,
160
+ openWorldHint: false,
161
+ },
146
162
  inputSchema: {
147
163
  type: "object",
148
164
  required: ["source_id", "target_id", "rel_type"],
@@ -203,6 +219,13 @@ export const getRelationsTool: ToolDefinition = {
203
219
  "List every relation touching a document, in both directions (→ outbound, " +
204
220
  "← inbound). Shows each neighbour's title and lifecycle status, so an agent " +
205
221
  "can tell whether retrieved knowledge has been superseded or contradicted.",
222
+ // Read-only: traversal only, mutates nothing.
223
+ annotations: {
224
+ title: "List a document's relations",
225
+ readOnlyHint: true,
226
+ idempotentHint: true,
227
+ openWorldHint: false,
228
+ },
206
229
  inputSchema: {
207
230
  type: "object",
208
231
  required: ["document_id"],
@@ -267,6 +290,13 @@ export const getNeighborsTool: ToolDefinition = {
267
290
  "Use after cerefox_get_relations shows which types exist. depth > 1 follows " +
268
291
  "chains (useful for follows / reply_to); cycles terminate safely. Optional " +
269
292
  "from_time / to_time filter neighbours by their creation time.",
293
+ // Read-only: traversal only, mutates nothing.
294
+ annotations: {
295
+ title: "Walk the relation graph",
296
+ readOnlyHint: true,
297
+ idempotentHint: true,
298
+ openWorldHint: false,
299
+ },
270
300
  inputSchema: {
271
301
  type: "object",
272
302
  required: ["document_id", "rel_type"],
@@ -192,6 +192,13 @@ export const searchTool: ToolDefinition = {
192
192
  name: "cerefox_search",
193
193
  description:
194
194
  "Search the Cerefox personal knowledge base. Returns complete documents ranked by hybrid (FTS + semantic) relevance.",
195
+ // Read-only: touches nothing. Safe for a client to run without prompting.
196
+ annotations: {
197
+ title: "Search knowledge base",
198
+ readOnlyHint: true,
199
+ idempotentHint: true,
200
+ openWorldHint: false,
201
+ },
195
202
  inputSchema: {
196
203
  type: "object",
197
204
  required: ["query"],
@@ -72,6 +72,15 @@ export const setDocumentProjectsTool: ToolDefinition = {
72
72
  name: "cerefox_set_document_projects",
73
73
  description:
74
74
  "Set the document's project memberships to EXACTLY the given list. Destructive replace: any existing memberships not in this list are removed. Pass an empty list to clear all project memberships. Projects are looked up by name (case-insensitive); missing projects are created. Logged as update-metadata in the audit log — content is untouched. Use cerefox_ingest with project_names if you want to set memberships AND update content in one call. Use this tool when you only need to change project membership without re-writing the document body.",
75
+ /** Destructive by contract: any membership not in the list is removed, and an
76
+ * empty list clears all of them. No version history for memberships. */
77
+ annotations: {
78
+ title: "Replace project memberships",
79
+ readOnlyHint: false,
80
+ destructiveHint: true,
81
+ idempotentHint: true,
82
+ openWorldHint: false,
83
+ },
75
84
  inputSchema: {
76
85
  type: "object",
77
86
  required: ["document_id", "project_names"],
@@ -66,10 +66,44 @@ export interface ToolContext {
66
66
  accessPath: AccessPath;
67
67
  }
68
68
 
69
+ /**
70
+ * MCP tool annotations (spec revision 2025-03-26).
71
+ *
72
+ * Hints that let a client reason about a tool BEFORE calling it. Without them a
73
+ * tool inherits the spec defaults `readOnlyHint: false` and
74
+ * `destructiveHint: true` — i.e. "may do something irreversible" — so declaring
75
+ * nothing tells every client that `cerefox_search` is as dangerous as a
76
+ * destructive write. The usual result is that users blanket-approve the server,
77
+ * which drains the meaning from the prompt on the tools that genuinely warrant
78
+ * one.
79
+ *
80
+ * These are hints from a server the client may not trust, so a client must not
81
+ * use them as a security boundary. They exist to inform UX, not to enforce it.
82
+ */
83
+ export interface ToolAnnotations {
84
+ /** Human-readable label for UIs. */
85
+ title?: string;
86
+ /** The tool does not modify anything. */
87
+ readOnlyHint?: boolean;
88
+ /** The tool may perform IRREVERSIBLE updates. Only meaningful when the tool
89
+ * is not read-only. Static per tool: if any argument shape can destroy, the
90
+ * tool is destructive. */
91
+ destructiveHint?: boolean;
92
+ /** Repeated calls with the same arguments have no additional effect. */
93
+ idempotentHint?: boolean;
94
+ /** The tool reaches external entities (a web search) rather than a closed
95
+ * domain. False throughout Cerefox: every tool talks to the operator's own
96
+ * store. */
97
+ openWorldHint?: boolean;
98
+ }
99
+
69
100
  export interface ToolDefinition {
70
101
  name: string;
71
102
  description: string;
72
103
  inputSchema: JsonSchema;
104
+ /** See `ToolAnnotations`. Required in practice: a unit test fails if a tool
105
+ * omits it, so adding a tool forces the read-only/destructive decision. */
106
+ annotations?: ToolAnnotations;
73
107
  /** Returns the MCP `TextContent.text` body. Tools that fail throw; the
74
108
  * consumer's request wrapper translates thrown errors into JSON-RPC
75
109
  * `-32603` (internal error) responses, or `-32602` (invalid params)
@@ -0,0 +1,48 @@
1
+ -- 0018_ingest_defers_retention_to_config.sql — make the store's retention policy
2
+ -- actually take effect on the write path (#183, reported by @tdebasis).
3
+ --
4
+ -- v1.1.0 moved version retention into `cerefox_config` and changed
5
+ -- `cerefox_snapshot_version` to default its parameters to NULL so that
6
+ -- COALESCE(param, config, default) could fall through to the store's policy.
7
+ --
8
+ -- That half worked. `cerefox_ingest_document` — snapshot_version's ONLY caller —
9
+ -- kept the pre-1.1.0 concrete defaults:
10
+ --
11
+ -- p_retention_hours INT DEFAULT 48
12
+ -- p_cleanup_enabled BOOLEAN DEFAULT TRUE
13
+ --
14
+ -- and passed them straight through. So snapshot_version never once received NULL
15
+ -- on a real write, and never once consulted `cerefox_config`. The store-level
16
+ -- switch was inert on the only path that matters.
17
+ --
18
+ -- Consequences, all silent:
19
+ --
20
+ -- * `version_cleanup_enabled = false` did nothing. Pruning ran anyway.
21
+ -- * Migration 0016's fail-safe — which seeds `false` on existing stores during
22
+ -- the 1.1.0 upgrade precisely so history could not be quietly discarded —
23
+ -- was defeated by this.
24
+ -- * `version_retention_hours` did nothing. The window stayed at 48 hours, not
25
+ -- the configured value (or the 120h default from migration 0017).
26
+ -- * The v1.1.0 release notes promised "Nothing is deleted." That was false.
27
+ --
28
+ -- The damage is per-document and latent rather than immediate: cleanup only runs
29
+ -- for a document when THAT document is next written. So versions older than the
30
+ -- window survive until their document is edited, then vanish. On the maintainer's
31
+ -- store, 362 of 397 versions were older than 48h and still intact when this was
32
+ -- found — every one of them was one edit away from being pruned.
33
+ --
34
+ -- Fix: `cerefox_ingest_document`'s parameters default to NULL, so the store's
35
+ -- policy resolves. An explicit value still overrides for a single call.
36
+ --
37
+ -- Lives in rpcs.sql, which `cerefox server deploy` re-applies. This migration
38
+ -- exists so the schema version moves and operators are told to redeploy.
39
+ --
40
+ -- Idempotent: safe to re-run.
41
+
42
+ DO $$
43
+ BEGIN
44
+ RAISE NOTICE
45
+ 'Migration 0018: cerefox_ingest_document now defers version retention to '
46
+ 'cerefox_config. Before this, version_cleanup_enabled and '
47
+ 'version_retention_hours were silently ignored on every write (#183).';
48
+ END $$;
@@ -0,0 +1,59 @@
1
+ -- 0020_ingest_preserves_source.sql — stop content updates from silently
2
+ -- overwriting a document's provenance (#191, reported by @tdebasis).
3
+ --
4
+ -- `cerefox_ingest_document`'s UPDATE branch assigned the source column
5
+ -- unconditionally:
6
+ --
7
+ -- source = p_source -- unconditional
8
+ -- source_path = COALESCE(p_source_path, source_path) -- preserved
9
+ -- metadata = COALESCE(p_metadata, metadata) -- preserved
10
+ --
11
+ -- with `p_source TEXT DEFAULT 'agent'` in the signature. The two columns either
12
+ -- side of it already implement the "absent means keep" rule — metadata got it in
13
+ -- v0.11.1, after content updates without metadata were found to be wiping tags.
14
+ -- source was left out of that fix.
15
+ --
16
+ -- Two consequences, both silent:
17
+ --
18
+ -- * Any caller that updates a document without passing p_source rewrites that
19
+ -- document's source to the parameter default 'agent'. Nothing in the RPC's
20
+ -- output, the audit entry, or the version row records that it happened: the
21
+ -- audit operation is 'update-content', and version rows carry their own
22
+ -- source label rather than the document's prior value.
23
+ -- * `cerefox server migrate-format` hit this at corpus scale. It hardcoded
24
+ -- source: "migrate-format" for every document it converted, even though it
25
+ -- reads each document first and cerefox_get_document returns doc_source. A
26
+ -- format conversion is not a change of origin, so every converted document
27
+ -- lost the label it came in with.
28
+ --
29
+ -- Reported impact on one store: 1,317 documents rewritten to 'migrate-format' in
30
+ -- a single run, 201 of which carried no metadata.source_agent and so had no
31
+ -- other provenance field to fall back on. A second store on the same instance
32
+ -- independently reported 509 of 553. Recovery required a point-in-time dump from
33
+ -- before the run.
34
+ --
35
+ -- Fix: p_source defaults to NULL and the UPDATE branch coalesces, matching
36
+ -- metadata and source_path exactly. The CREATE path keeps 'agent' as its
37
+ -- concrete fallback via COALESCE(p_source, 'agent'), so new documents are
38
+ -- unchanged. An explicit value still relabels, so deliberate callers are
39
+ -- unaffected.
40
+ --
41
+ -- Note the distinction this preserves: p_source is the document's origin, while
42
+ -- p_source_label records how a particular write was triggered and is stored on
43
+ -- the version row. migrate-format now passes the document's own source for the
44
+ -- former and keeps "migrate-format" for the latter, so the version history still
45
+ -- shows which run performed the conversion.
46
+ --
47
+ -- Lives in rpcs.sql, which `cerefox server deploy` re-applies. This migration
48
+ -- exists so the schema version moves and operators are told to redeploy.
49
+ --
50
+ -- Idempotent: safe to re-run.
51
+
52
+ DO $$
53
+ BEGIN
54
+ RAISE NOTICE
55
+ 'Migration 0020: cerefox_ingest_document now preserves a document''s '
56
+ 'source when p_source is omitted. Before this, any content update '
57
+ 'without an explicit source silently rewrote provenance to ''agent'', '
58
+ 'and migrate-format relabelled every document it converted (#191).';
59
+ END $$;
@@ -1258,7 +1258,12 @@ $$;
1258
1258
  --
1259
1259
  -- Parameters:
1260
1260
  -- p_document_id : NULL for create, UUID for update
1261
- -- p_title, p_source, p_source_path, p_content_hash : document fields
1261
+ -- p_title, p_source_path, p_content_hash : document fields
1262
+ -- p_source : origin label. NULL = "not provided" → create uses
1263
+ -- 'agent', update keeps the existing source (#191). Pass a
1264
+ -- value explicitly to relabel. Distinct from
1265
+ -- p_source_label, which records how THIS write was
1266
+ -- triggered and is stored on the version row.
1262
1267
  -- p_metadata : JSONB metadata. NULL = "not provided" → create uses '{}',
1263
1268
  -- update keeps the existing metadata (v0.11.1). Pass '{}'
1264
1269
  -- explicitly to clear all metadata.
@@ -1268,8 +1273,10 @@ $$;
1268
1273
  -- content, char_count, embedding (float[]), embedder (text)
1269
1274
  -- p_author, p_author_type : for audit entry
1270
1275
  -- p_source_label : version source label for snapshot ('file','paste','agent','manual')
1271
- -- p_retention_hours : for version cleanup (default 48)
1272
- -- p_cleanup_enabled : whether version cleanup runs (default true)
1276
+ -- p_retention_hours : version-cleanup window. NULL (default) = use the store's
1277
+ -- `version_retention_hours` from cerefox_config.
1278
+ -- p_cleanup_enabled : whether cleanup runs. NULL (default) = use the store's
1279
+ -- `version_cleanup_enabled`.
1273
1280
  -- p_expected_content_hash : optimistic-concurrency token (iter-32). On the UPDATE
1274
1281
  -- path this must equal the document's current content_hash —
1275
1282
  -- the caller proves they based their edit on the live version.
@@ -1289,7 +1296,10 @@ DROP FUNCTION IF EXISTS cerefox_ingest_document(UUID, TEXT, TEXT, TEXT, TEXT, JS
1289
1296
  CREATE FUNCTION cerefox_ingest_document(
1290
1297
  p_document_id UUID DEFAULT NULL,
1291
1298
  p_title TEXT DEFAULT 'Untitled',
1292
- p_source TEXT DEFAULT 'agent',
1299
+ -- NULL = "not provided": create uses 'agent', update KEEPS the existing
1300
+ -- source (#191 — a content update without a source used to overwrite the
1301
+ -- document's provenance). Pass a value to relabel deliberately.
1302
+ p_source TEXT DEFAULT NULL,
1293
1303
  p_source_path TEXT DEFAULT NULL,
1294
1304
  p_content_hash TEXT DEFAULT '',
1295
1305
  -- NULL = "not provided": create uses '{}', update KEEPS existing metadata
@@ -1301,8 +1311,14 @@ CREATE FUNCTION cerefox_ingest_document(
1301
1311
  p_author TEXT DEFAULT 'unknown',
1302
1312
  p_author_type TEXT DEFAULT 'user',
1303
1313
  p_source_label TEXT DEFAULT 'manual',
1304
- p_retention_hours INT DEFAULT 48,
1305
- p_cleanup_enabled BOOLEAN DEFAULT TRUE,
1314
+ -- NULL, so `cerefox_snapshot_version` falls through to the store's policy in
1315
+ -- cerefox_config. These carried concrete defaults (48 / TRUE) until v1.1.2,
1316
+ -- which silently defeated the whole store-level retention feature: this is
1317
+ -- snapshot_version's ONLY caller, so it never once received NULL and never
1318
+ -- once consulted the config (#183, reported by @tdebasis). A caller may still
1319
+ -- pass explicit values to override the store for one call.
1320
+ p_retention_hours INT DEFAULT NULL,
1321
+ p_cleanup_enabled BOOLEAN DEFAULT NULL,
1306
1322
  p_expected_content_hash TEXT DEFAULT NULL,
1307
1323
  p_last_write_wins BOOLEAN DEFAULT FALSE,
1308
1324
  -- content_format for the chunks being written (iter-28D). 2 = exact-partition
@@ -1437,9 +1453,11 @@ BEGIN
1437
1453
 
1438
1454
  -- Update document record. metadata: NULL = keep existing (v0.11.1 —
1439
1455
  -- a content update without metadata must not wipe the document's tags).
1456
+ -- source: same rule, same reason (#191 — a content update without a
1457
+ -- source must not wipe the document's provenance).
1440
1458
  UPDATE cerefox_documents SET
1441
1459
  title = p_title,
1442
- source = p_source,
1460
+ source = COALESCE(p_source, source),
1443
1461
  source_path = COALESCE(p_source_path, source_path),
1444
1462
  content_hash = p_content_hash,
1445
1463
  metadata = COALESCE(p_metadata, metadata),
@@ -1457,7 +1475,7 @@ BEGIN
1457
1475
  title, source, source_path, content_hash, metadata,
1458
1476
  chunk_count, total_chars, review_status
1459
1477
  ) VALUES (
1460
- p_title, p_source, p_source_path, p_content_hash, COALESCE(p_metadata, '{}'::JSONB),
1478
+ p_title, COALESCE(p_source, 'agent'), p_source_path, p_content_hash, COALESCE(p_metadata, '{}'::JSONB),
1461
1479
  v_chunk_count, v_total_chars, v_status
1462
1480
  )
1463
1481
  RETURNING id INTO v_doc_id;
@@ -2325,7 +2343,7 @@ SET search_path = public, pg_catalog
2325
2343
  AS $$
2326
2344
  -- Keep in lockstep with the `@version:` marker in schema.sql (cut_release.ts
2327
2345
  -- enforces it). Bump whenever schema.sql OR rpcs.sql changes.
2328
- SELECT '0.10.4'::TEXT;
2346
+ SELECT '0.10.6'::TEXT;
2329
2347
  $$;
2330
2348
 
2331
2349
  -- ── cerefox_content_format_stats ─────────────────────────────────────────────
@@ -5,7 +5,7 @@
5
5
  -- Requires extensions: vector (pgvector), uuid-ossp
6
6
  -- These are enabled at the top of db_deploy.py before this file is applied.
7
7
  --
8
- -- @version: 0.10.4
8
+ -- @version: 0.10.6
9
9
  -- The `@version` marker above is read by the schema-version-mismatch banner
10
10
  -- (see /api/v1/schema-version). Bump it whenever schema.sql OR rpcs.sql
11
11
  -- changes in a way that requires `cerefox server deploy` to be re-run —
@@ -68,6 +68,8 @@ async function buildToolList(supabase: MCPSupabaseClient) {
68
68
  name: t.name,
69
69
  description: t.description,
70
70
  inputSchema: t.inputSchema,
71
+ // MCP 2025-03-26 tool annotations; MCP_VERSION already declares that revision.
72
+ ...(t.annotations ? { annotations: t.annotations } : {}),
71
73
  }));
72
74
  }
73
75
 
@@ -145,13 +145,20 @@ cerefox-local start # start a stopped container
145
145
  cerefox-local stop # stop it (your data persists in the Docker volume)
146
146
  cerefox-local restart
147
147
  cerefox-local logs -f # follow the logs
148
- cerefox-local upgrade # upgrade to the newest release + recreate (keeps data + OPENAI key)
148
+ cerefox-local upgrade # newest stable release + recreate (keeps data + OPENAI key)
149
149
  cerefox-local upgrade v1.2.3 # pin an exact version (also how you downgrade)
150
150
  cerefox-local upgrade --latest # follow the moving :latest tag from now on
151
151
  cerefox-local uninstall # remove the container, KEEP the data volume
152
152
  cerefox-local uninstall --purge # remove the container AND delete the data volume
153
153
  ```
154
154
 
155
+ > **Coming from an image older than v1.1.0?** Run `cerefox-local upgrade v1.1.1`
156
+ > (an explicit tag) once. A bare `upgrade` on those versions re-pulled the pinned
157
+ > tag and reported success without changing anything (#153), and it cannot fix
158
+ > itself: the launcher on your host is refreshed out of the container image, so an
159
+ > old image keeps reinstalling the old launcher. See
160
+ > [`upgrading.md`](upgrading.md).
161
+
155
162
  `upgrade` is the single update path: it pulls the newest image, recreates the container,
156
163
  and refreshes the `cerefox-local` script itself. Because the CLI, web server, PostgREST,
157
164
  and database schema all ship together in one versioned image, they never drift out of
@@ -18,6 +18,33 @@ to re-run.
18
18
  > repo `.env` to `~/.cerefox/.env`, deploy the server, and wire up your agent.
19
19
  > After that you're on the end-user path below.
20
20
 
21
+ > ### Cerefox Local on an image older than v1.1.0: upgrade once with an explicit tag
22
+ >
23
+ > If your container is on **v1.0.6 or earlier**, run this once:
24
+ >
25
+ > ```bash
26
+ > cerefox-local upgrade v1.1.1 # or any newer tag
27
+ > ```
28
+ >
29
+ > A bare `cerefox-local upgrade` will **not** get you off an old image, and it
30
+ > fails quietly: it prints "Pulling …", "container (re)started" and "refreshed
31
+ > cerefox-local from the new image", having changed nothing. Before v1.1.0 a bare
32
+ > `upgrade` re-pulled the **pinned** tag rather than resolving the newest release
33
+ > (#153).
34
+ >
35
+ > The reason it cannot fix itself is a bootstrap loop: the `cerefox-local`
36
+ > launcher on your host is refreshed *out of the container image* at the end of
37
+ > every upgrade, so an old image keeps reinstalling the old launcher. Naming a
38
+ > tag explicitly bypasses the pin, pulls the new image, and the new launcher
39
+ > comes with it. `curl … install-local.sh | sh` also works, since the installer
40
+ > writes the launcher directly.
41
+ >
42
+ > **After that, a bare `cerefox-local upgrade` behaves as you would expect**: it
43
+ > resolves the newest *stable* release and pins that exact version. It does not
44
+ > start following the moving `:latest` tag — that is what `upgrade --latest`
45
+ > does, and automatic operations (`init`, `start`, `restart`) never follow a
46
+ > moving tag by design (#100).
47
+
21
48
  ## End-user upgrade
22
49
 
23
50
  > ### Upgrading to v1.1.0 — `cerefox server deploy` is required
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cerefox/memory",
3
- "version": "1.1.1",
3
+ "version": "1.2.1",
4
4
  "description": "Cerefox — user-owned shared memory for AI agents. CLI + stdio MCP server + web UI + ingestion for a knowledge base on your own Supabase project (or fully self-hosted with Cerefox Local).",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://github.com/fstamatelopoulos/cerefox",