@cerefox/memory 1.13.0 → 1.13.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.
- package/AGENT_GUIDE.md +26 -26
- package/AGENT_QUICK_REFERENCE.md +9 -9
- package/dist/bin/cerefox.js +93 -113
- package/dist/server-assets/_shared/ef-meta/index.ts +3 -3
- package/dist/server-assets/_shared/mcp-tools/audit-log.ts +10 -8
- package/dist/server-assets/_shared/mcp-tools/delete-document.ts +4 -10
- package/dist/server-assets/_shared/mcp-tools/feature-flags.ts +4 -3
- package/dist/server-assets/_shared/mcp-tools/get-document.ts +3 -6
- package/dist/server-assets/_shared/mcp-tools/get-help-content.ts +3 -3
- package/dist/server-assets/_shared/mcp-tools/get-help.ts +3 -6
- package/dist/server-assets/_shared/mcp-tools/identity.ts +48 -0
- package/dist/server-assets/_shared/mcp-tools/ingest.ts +3 -6
- package/dist/server-assets/_shared/mcp-tools/list-metadata-keys.ts +3 -6
- package/dist/server-assets/_shared/mcp-tools/list-projects.ts +3 -6
- package/dist/server-assets/_shared/mcp-tools/list-versions.ts +3 -6
- package/dist/server-assets/_shared/mcp-tools/metadata-search.ts +3 -6
- package/dist/server-assets/_shared/mcp-tools/partial-edits.ts +5 -10
- package/dist/server-assets/_shared/mcp-tools/relations.ts +11 -12
- package/dist/server-assets/_shared/mcp-tools/restore-document.ts +4 -10
- package/dist/server-assets/_shared/mcp-tools/search.ts +3 -6
- package/dist/server-assets/_shared/mcp-tools/set-document-metadata.ts +4 -10
- package/dist/server-assets/_shared/mcp-tools/set-document-projects.ts +3 -6
- package/dist/server-assets/db/migrations/0031_review_workflow_toggle.sql +4 -2
- package/dist/server-assets/db/rpcs.sql +19 -12
- package/dist/server-assets/db/schema.sql +4 -4
- package/dist/server-assets/supabase/functions/cerefox-mcp/index.ts +6 -6
- package/docs/guides/cli.md +11 -11
- package/docs/guides/configuration.md +33 -24
- package/docs/guides/connect-agents.md +6 -6
- package/docs/guides/upgrading.md +11 -0
- package/package.json +1 -1
package/dist/bin/cerefox.js
CHANGED
|
@@ -7400,7 +7400,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
7400
7400
|
});
|
|
7401
7401
|
|
|
7402
7402
|
// src/meta.ts
|
|
7403
|
-
var PKG_VERSION = "1.13.
|
|
7403
|
+
var PKG_VERSION = "1.13.1";
|
|
7404
7404
|
var init_meta = () => {};
|
|
7405
7405
|
|
|
7406
7406
|
// ../../_shared/config/paths.ts
|
|
@@ -25774,7 +25774,7 @@ var init_bundled_docs = __esm(() => {
|
|
|
25774
25774
|
});
|
|
25775
25775
|
|
|
25776
25776
|
// ../../_shared/ef-meta/index.ts
|
|
25777
|
-
var EF_VERSION = "1.13.
|
|
25777
|
+
var EF_VERSION = "1.13.1", CEREFOX_VERSION = "1.13.1", EF_LAST_CHANGED = "1.13.1";
|
|
25778
25778
|
var init_ef_meta = () => {};
|
|
25779
25779
|
|
|
25780
25780
|
// ../../_shared/compatibility/index.ts
|
|
@@ -54746,6 +54746,27 @@ var require_underline = __commonJS(function(exports2) {
|
|
|
54746
54746
|
}
|
|
54747
54747
|
});
|
|
54748
54748
|
|
|
54749
|
+
// ../../_shared/mcp-tools/identity.ts
|
|
54750
|
+
function callerIdentity(args) {
|
|
54751
|
+
for (const key of ["author", "requestor"]) {
|
|
54752
|
+
const v = args[key];
|
|
54753
|
+
if (typeof v === "string" && v.trim() !== "")
|
|
54754
|
+
return v;
|
|
54755
|
+
}
|
|
54756
|
+
return;
|
|
54757
|
+
}
|
|
54758
|
+
var DEFAULT_IDENTITY = "mcp-agent", EXAMPLE = 'e.g. "Claude Code", "archiver"', AUTHOR_PARAM_WRITE, AUTHOR_PARAM_READ;
|
|
54759
|
+
var init_identity = __esm(() => {
|
|
54760
|
+
AUTHOR_PARAM_WRITE = {
|
|
54761
|
+
type: "string",
|
|
54762
|
+
description: `Your name (agent or user), ${EXAMPLE}. Recorded as the author in the audit log and ` + `in the usage log. Defaults to "${DEFAULT_IDENTITY}" if not provided. May be enforced ` + "via server config."
|
|
54763
|
+
};
|
|
54764
|
+
AUTHOR_PARAM_READ = {
|
|
54765
|
+
type: "string",
|
|
54766
|
+
description: `Your name (agent or user), ${EXAMPLE}. Recorded in the usage log. Defaults to ` + `"${DEFAULT_IDENTITY}" if not provided. May be enforced via server config.`
|
|
54767
|
+
};
|
|
54768
|
+
});
|
|
54769
|
+
|
|
54749
54770
|
// ../../_shared/mcp-tools/audit-log.ts
|
|
54750
54771
|
function utcStamp2(iso) {
|
|
54751
54772
|
const trimmed = iso.slice(0, 19);
|
|
@@ -54755,8 +54776,8 @@ async function handler(supabase, args, ctx) {
|
|
|
54755
54776
|
const params = {};
|
|
54756
54777
|
if (args.document_id)
|
|
54757
54778
|
params.p_document_id = args.document_id;
|
|
54758
|
-
if (args.
|
|
54759
|
-
params.p_author = args.
|
|
54779
|
+
if (args.by_author)
|
|
54780
|
+
params.p_author = args.by_author;
|
|
54760
54781
|
if (args.operation)
|
|
54761
54782
|
params.p_operation = args.operation;
|
|
54762
54783
|
if (args.since)
|
|
@@ -54772,7 +54793,7 @@ async function handler(supabase, args, ctx) {
|
|
|
54772
54793
|
logUsage(supabase, {
|
|
54773
54794
|
operation: "get_audit_log",
|
|
54774
54795
|
accessPath: ctx.accessPath,
|
|
54775
|
-
requestor: args
|
|
54796
|
+
requestor: callerIdentity(args),
|
|
54776
54797
|
result_count: entries.length
|
|
54777
54798
|
});
|
|
54778
54799
|
if (!entries.length)
|
|
@@ -54790,6 +54811,7 @@ ${lines.join(`
|
|
|
54790
54811
|
var auditLogTool;
|
|
54791
54812
|
var init_audit_log = __esm(() => {
|
|
54792
54813
|
init__utils();
|
|
54814
|
+
init_identity();
|
|
54793
54815
|
auditLogTool = {
|
|
54794
54816
|
name: "cerefox_get_audit_log",
|
|
54795
54817
|
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.",
|
|
@@ -54804,7 +54826,10 @@ var init_audit_log = __esm(() => {
|
|
|
54804
54826
|
required: [],
|
|
54805
54827
|
properties: {
|
|
54806
54828
|
document_id: { type: "string", description: "Filter by document UUID (optional)" },
|
|
54807
|
-
|
|
54829
|
+
by_author: {
|
|
54830
|
+
type: "string",
|
|
54831
|
+
description: "Filter: only entries written by this author name (optional)"
|
|
54832
|
+
},
|
|
54808
54833
|
operation: {
|
|
54809
54834
|
type: "string",
|
|
54810
54835
|
description: `Filter by operation type: ${AUDIT_OPERATIONS.join(", ")} (optional)`
|
|
@@ -54817,10 +54842,7 @@ var init_audit_log = __esm(() => {
|
|
|
54817
54842
|
type: "integer",
|
|
54818
54843
|
description: "Maximum number of entries to return (default: 50, max: 200)"
|
|
54819
54844
|
},
|
|
54820
|
-
|
|
54821
|
-
type: "string",
|
|
54822
|
-
description: 'Name of the agent or user making this request. Recorded in the usage log. Defaults to "mcp-agent" if not provided. May be enforced via server config.'
|
|
54823
|
-
}
|
|
54845
|
+
author: AUTHOR_PARAM_READ
|
|
54824
54846
|
}
|
|
54825
54847
|
},
|
|
54826
54848
|
handler
|
|
@@ -54855,7 +54877,7 @@ async function handler2(supabase, args, ctx) {
|
|
|
54855
54877
|
if (!expected_content_hash) {
|
|
54856
54878
|
throw new McpInvalidParams("expected_content_hash is required: the content_hash of the document as you read it " + "(returned by cerefox_get_document, cerefox_search, and cerefox_metadata_search). " + "If you have not read the document, read it first — deletion requires knowing what " + "you are deleting.");
|
|
54857
54879
|
}
|
|
54858
|
-
const author = args
|
|
54880
|
+
const author = callerIdentity(args);
|
|
54859
54881
|
const authorType = ctx.accessPath === "cli" ? "user" : "agent";
|
|
54860
54882
|
const { data, error } = await supabase.rpc("cerefox_delete_document", {
|
|
54861
54883
|
p_document_id: document_id,
|
|
@@ -54887,7 +54909,7 @@ async function handler2(supabase, args, ctx) {
|
|
|
54887
54909
|
logUsage(supabase, {
|
|
54888
54910
|
operation: "delete",
|
|
54889
54911
|
accessPath: ctx.accessPath,
|
|
54890
|
-
requestor:
|
|
54912
|
+
requestor: author,
|
|
54891
54913
|
document_id,
|
|
54892
54914
|
result_count: 1
|
|
54893
54915
|
});
|
|
@@ -54899,6 +54921,7 @@ var deleteDocumentTool;
|
|
|
54899
54921
|
var init_delete_document = __esm(() => {
|
|
54900
54922
|
init__utils();
|
|
54901
54923
|
init_types3();
|
|
54924
|
+
init_identity();
|
|
54902
54925
|
deleteDocumentTool = {
|
|
54903
54926
|
name: "cerefox_delete_document",
|
|
54904
54927
|
description: "SOFT-delete a document: it leaves search results and lands in the trash, recoverable until a human purges it. Requires expected_content_hash — the content_hash of the document AS YOU READ IT — so a delete always follows a read; if the document changed in between, the call fails with a conflict and you should re-read before deciding again. A mistaken delete can be undone with cerefox_restore_document; permanent purge is human-only (web UI). ALWAYS tell your user what you deleted and why. Pass a short reason — it is recorded in the audit log for the human reviewing the trash. Prefer this over ingesting empty/placeholder content when a document should go away.",
|
|
@@ -54922,14 +54945,7 @@ var init_delete_document = __esm(() => {
|
|
|
54922
54945
|
type: "string",
|
|
54923
54946
|
description: "Why this document is being deleted. Recorded in the audit log entry, where it is the main thing the human reviewing the trash has to go on. Short and specific beats long."
|
|
54924
54947
|
},
|
|
54925
|
-
author:
|
|
54926
|
-
type: "string",
|
|
54927
|
-
description: "Who is making this change. Recorded in the audit log."
|
|
54928
|
-
},
|
|
54929
|
-
requestor: {
|
|
54930
|
-
type: "string",
|
|
54931
|
-
description: "Name of the agent or user making this request. Recorded in the usage log."
|
|
54932
|
-
}
|
|
54948
|
+
author: AUTHOR_PARAM_WRITE
|
|
54933
54949
|
}
|
|
54934
54950
|
},
|
|
54935
54951
|
handler: handler2
|
|
@@ -54942,7 +54958,7 @@ async function handler3(supabase, args, ctx) {
|
|
|
54942
54958
|
const reason = args.reason;
|
|
54943
54959
|
if (!document_id)
|
|
54944
54960
|
throw new McpInvalidParams("document_id is required");
|
|
54945
|
-
const author = args
|
|
54961
|
+
const author = callerIdentity(args);
|
|
54946
54962
|
const authorType = ctx.accessPath === "cli" ? "user" : "agent";
|
|
54947
54963
|
const { data, error } = await supabase.rpc("cerefox_restore_document", {
|
|
54948
54964
|
p_document_id: document_id,
|
|
@@ -54969,7 +54985,7 @@ async function handler3(supabase, args, ctx) {
|
|
|
54969
54985
|
logUsage(supabase, {
|
|
54970
54986
|
operation: "restore",
|
|
54971
54987
|
accessPath: ctx.accessPath,
|
|
54972
|
-
requestor:
|
|
54988
|
+
requestor: author,
|
|
54973
54989
|
document_id,
|
|
54974
54990
|
result_count: 1
|
|
54975
54991
|
});
|
|
@@ -54980,6 +54996,7 @@ var restoreDocumentTool;
|
|
|
54980
54996
|
var init_restore_document = __esm(() => {
|
|
54981
54997
|
init__utils();
|
|
54982
54998
|
init_types3();
|
|
54999
|
+
init_identity();
|
|
54983
55000
|
restoreDocumentTool = {
|
|
54984
55001
|
name: "cerefox_restore_document",
|
|
54985
55002
|
description: "Restore a soft-deleted document from the trash — the inverse of cerefox_delete_document. The document becomes searchable again; the restore is recorded in the audit log with your identity. Restoring a document that is not deleted is a reported no-op. Pass a short reason — like a delete's reason, it is what the human reviewing the audit trail goes on. Permanent purge has no agent surface: once a human purges a document from the web UI, it is gone and cannot be restored.",
|
|
@@ -54999,14 +55016,7 @@ var init_restore_document = __esm(() => {
|
|
|
54999
55016
|
type: "string",
|
|
55000
55017
|
description: "Why this document is being restored. Recorded in the audit-log entry. Short and specific beats long."
|
|
55001
55018
|
},
|
|
55002
|
-
author:
|
|
55003
|
-
type: "string",
|
|
55004
|
-
description: "Who is making this change. Recorded in the audit log."
|
|
55005
|
-
},
|
|
55006
|
-
requestor: {
|
|
55007
|
-
type: "string",
|
|
55008
|
-
description: "Name of the agent or user making this request. Recorded in the usage log."
|
|
55009
|
-
}
|
|
55019
|
+
author: AUTHOR_PARAM_WRITE
|
|
55010
55020
|
}
|
|
55011
55021
|
},
|
|
55012
55022
|
handler: handler3
|
|
@@ -55070,7 +55080,7 @@ async function setHandler(supabase, args, ctx) {
|
|
|
55070
55080
|
p_source_id: source,
|
|
55071
55081
|
p_target_id: target,
|
|
55072
55082
|
p_rel_type: relType,
|
|
55073
|
-
p_author: args
|
|
55083
|
+
p_author: callerIdentity(args) ?? DEFAULT_IDENTITY,
|
|
55074
55084
|
p_author_type: "agent",
|
|
55075
55085
|
p_metadata: args.metadata ?? {}
|
|
55076
55086
|
});
|
|
@@ -55080,7 +55090,7 @@ async function setHandler(supabase, args, ctx) {
|
|
|
55080
55090
|
logUsage(supabase, {
|
|
55081
55091
|
operation: "set_relation",
|
|
55082
55092
|
accessPath: ctx.accessPath,
|
|
55083
|
-
requestor: args
|
|
55093
|
+
requestor: callerIdentity(args),
|
|
55084
55094
|
document_id: source
|
|
55085
55095
|
});
|
|
55086
55096
|
const both = row?.is_symmetric ? " (symmetric — the reverse edge was written too)" : "";
|
|
@@ -55099,7 +55109,7 @@ async function deleteHandler(supabase, args, ctx) {
|
|
|
55099
55109
|
p_source_id: source,
|
|
55100
55110
|
p_target_id: target,
|
|
55101
55111
|
p_rel_type: relType,
|
|
55102
|
-
p_author: args
|
|
55112
|
+
p_author: callerIdentity(args) ?? DEFAULT_IDENTITY,
|
|
55103
55113
|
p_author_type: "agent"
|
|
55104
55114
|
});
|
|
55105
55115
|
if (error)
|
|
@@ -55108,7 +55118,7 @@ async function deleteHandler(supabase, args, ctx) {
|
|
|
55108
55118
|
logUsage(supabase, {
|
|
55109
55119
|
operation: "delete_relation",
|
|
55110
55120
|
accessPath: ctx.accessPath,
|
|
55111
|
-
requestor: args
|
|
55121
|
+
requestor: callerIdentity(args),
|
|
55112
55122
|
document_id: source
|
|
55113
55123
|
});
|
|
55114
55124
|
if (removed === 0)
|
|
@@ -55126,7 +55136,7 @@ async function getRelationsHandler(supabase, args, ctx) {
|
|
|
55126
55136
|
logUsage(supabase, {
|
|
55127
55137
|
operation: "get_relations",
|
|
55128
55138
|
accessPath: ctx.accessPath,
|
|
55129
|
-
requestor: args
|
|
55139
|
+
requestor: callerIdentity(args),
|
|
55130
55140
|
document_id: docId,
|
|
55131
55141
|
result_count: rows.length
|
|
55132
55142
|
});
|
|
@@ -55161,7 +55171,7 @@ async function getNeighborsHandler(supabase, args, ctx) {
|
|
|
55161
55171
|
logUsage(supabase, {
|
|
55162
55172
|
operation: "get_neighbors",
|
|
55163
55173
|
accessPath: ctx.accessPath,
|
|
55164
|
-
requestor: args
|
|
55174
|
+
requestor: callerIdentity(args),
|
|
55165
55175
|
document_id: docId,
|
|
55166
55176
|
result_count: rows.length
|
|
55167
55177
|
});
|
|
@@ -55179,6 +55189,7 @@ var KNOWN_TYPES = "related_to, references, supersedes, contradicts, duplicates,
|
|
|
55179
55189
|
var init_relations = __esm(() => {
|
|
55180
55190
|
init__utils();
|
|
55181
55191
|
init_types3();
|
|
55192
|
+
init_identity();
|
|
55182
55193
|
setRelationTool = {
|
|
55183
55194
|
name: "cerefox_set_relation",
|
|
55184
55195
|
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.",
|
|
@@ -55203,8 +55214,7 @@ var init_relations = __esm(() => {
|
|
|
55203
55214
|
type: "object",
|
|
55204
55215
|
description: "Optional JSON context for the edge (note, confidence, …)"
|
|
55205
55216
|
},
|
|
55206
|
-
author:
|
|
55207
|
-
requestor: { type: "string", description: "Name of the agent making this request" }
|
|
55217
|
+
author: AUTHOR_PARAM_WRITE
|
|
55208
55218
|
}
|
|
55209
55219
|
},
|
|
55210
55220
|
handler: setHandler
|
|
@@ -55226,8 +55236,7 @@ var init_relations = __esm(() => {
|
|
|
55226
55236
|
source_id: { type: "string", description: "UUID of the source document" },
|
|
55227
55237
|
target_id: { type: "string", description: "UUID of the target document" },
|
|
55228
55238
|
rel_type: { type: "string", description: "Relation type to remove" },
|
|
55229
|
-
author:
|
|
55230
|
-
requestor: { type: "string", description: "Name of the agent making this request" }
|
|
55239
|
+
author: AUTHOR_PARAM_WRITE
|
|
55231
55240
|
}
|
|
55232
55241
|
},
|
|
55233
55242
|
handler: deleteHandler
|
|
@@ -55246,7 +55255,7 @@ var init_relations = __esm(() => {
|
|
|
55246
55255
|
required: ["document_id"],
|
|
55247
55256
|
properties: {
|
|
55248
55257
|
document_id: { type: "string", description: "UUID of the document" },
|
|
55249
|
-
|
|
55258
|
+
author: AUTHOR_PARAM_READ
|
|
55250
55259
|
}
|
|
55251
55260
|
},
|
|
55252
55261
|
handler: getRelationsHandler
|
|
@@ -55270,7 +55279,7 @@ var init_relations = __esm(() => {
|
|
|
55270
55279
|
from_time: { type: "string", description: "ISO-8601: only neighbours created on/after" },
|
|
55271
55280
|
to_time: { type: "string", description: "ISO-8601: only neighbours created on/before" },
|
|
55272
55281
|
limit: { type: "integer", description: "Max documents to return (default 50, max 200)" },
|
|
55273
|
-
|
|
55282
|
+
author: AUTHOR_PARAM_READ
|
|
55274
55283
|
}
|
|
55275
55284
|
},
|
|
55276
55285
|
handler: getNeighborsHandler
|
|
@@ -55304,7 +55313,7 @@ async function handler4(supabase, args, ctx) {
|
|
|
55304
55313
|
logUsage(supabase, {
|
|
55305
55314
|
operation: "get_document",
|
|
55306
55315
|
accessPath: ctx.accessPath,
|
|
55307
|
-
requestor: args
|
|
55316
|
+
requestor: callerIdentity(args),
|
|
55308
55317
|
document_id,
|
|
55309
55318
|
result_count: 1
|
|
55310
55319
|
});
|
|
@@ -55355,6 +55364,7 @@ var init_get_document = __esm(() => {
|
|
|
55355
55364
|
init_partial_edits();
|
|
55356
55365
|
init__utils();
|
|
55357
55366
|
init_types3();
|
|
55367
|
+
init_identity();
|
|
55358
55368
|
getDocumentTool = {
|
|
55359
55369
|
name: "cerefox_get_document",
|
|
55360
55370
|
description: `Retrieve a document at one of three zoom levels: the whole reconstructed content (default), its heading structure with outline=true (much cheaper, and the headings are the anchors the edit tools take), or one section's text with section="## Heading" (what a replace_section on that anchor would overwrite — read it before replacing a section you did not write). 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. Every non-archived response carries the document's current content_hash — pass it back as expected_content_hash when updating (optimistic concurrency).`,
|
|
@@ -55386,10 +55396,7 @@ var init_get_document = __esm(() => {
|
|
|
55386
55396
|
enum: ["own_body", "subtree"],
|
|
55387
55397
|
description: "Only when the target section HAS CHILD SECTIONS, and it means the same here as on the edit tools: own_body = up to the first child, subtree = everything nested underneath. The read refuses without it for exactly the cases the write refuses, so that what you read is what you would replace. Omit it otherwise; you will be told (with both options) whenever it is needed."
|
|
55388
55398
|
},
|
|
55389
|
-
|
|
55390
|
-
type: "string",
|
|
55391
|
-
description: 'Name of the agent or user making this request. Recorded in the usage log. Defaults to "mcp-agent" if not provided. May be enforced via server config.'
|
|
55392
|
-
}
|
|
55399
|
+
author: AUTHOR_PARAM_READ
|
|
55393
55400
|
}
|
|
55394
55401
|
},
|
|
55395
55402
|
handler: handler4
|
|
@@ -55588,7 +55595,7 @@ async function insertHandler(supabase, args, ctx) {
|
|
|
55588
55595
|
documentId,
|
|
55589
55596
|
operations,
|
|
55590
55597
|
expectedHash,
|
|
55591
|
-
requestor: args
|
|
55598
|
+
requestor: callerIdentity(args) ?? defaultRequestor(ctx),
|
|
55592
55599
|
toolLabel: "insert",
|
|
55593
55600
|
authorType: resolveAuthorType2(ctx, args)
|
|
55594
55601
|
});
|
|
@@ -55611,7 +55618,7 @@ async function editHandler(supabase, args, ctx) {
|
|
|
55611
55618
|
documentId,
|
|
55612
55619
|
operations,
|
|
55613
55620
|
expectedHash,
|
|
55614
|
-
requestor: args
|
|
55621
|
+
requestor: callerIdentity(args) ?? defaultRequestor(ctx),
|
|
55615
55622
|
toolLabel: "edit",
|
|
55616
55623
|
authorType: resolveAuthorType2(ctx, args)
|
|
55617
55624
|
});
|
|
@@ -55622,6 +55629,7 @@ var init_partial_edits2 = __esm(() => {
|
|
|
55622
55629
|
init__chunker();
|
|
55623
55630
|
init__utils();
|
|
55624
55631
|
init_types3();
|
|
55632
|
+
init_identity();
|
|
55625
55633
|
AUDIT_OP = {
|
|
55626
55634
|
insert: "insert",
|
|
55627
55635
|
replace_section: "replace-section",
|
|
@@ -55662,10 +55670,7 @@ var init_partial_edits2 = __esm(() => {
|
|
|
55662
55670
|
type: "string",
|
|
55663
55671
|
description: "content_hash of the version you are basing this on. Required — no last-write-wins."
|
|
55664
55672
|
},
|
|
55665
|
-
|
|
55666
|
-
type: "string",
|
|
55667
|
-
description: 'Agent or user making this request. Recorded in the usage log. Defaults to "mcp-agent".'
|
|
55668
|
-
},
|
|
55673
|
+
author: AUTHOR_PARAM_WRITE,
|
|
55669
55674
|
author_type: {
|
|
55670
55675
|
type: "string",
|
|
55671
55676
|
enum: ["user", "agent"],
|
|
@@ -55733,10 +55738,7 @@ var init_partial_edits2 = __esm(() => {
|
|
|
55733
55738
|
type: "string",
|
|
55734
55739
|
description: "content_hash of the version you are basing these edits on. Required — no last-write-wins."
|
|
55735
55740
|
},
|
|
55736
|
-
|
|
55737
|
-
type: "string",
|
|
55738
|
-
description: 'Agent or user making this request. Recorded in the usage log. Defaults to "mcp-agent".'
|
|
55739
|
-
},
|
|
55741
|
+
author: AUTHOR_PARAM_WRITE,
|
|
55740
55742
|
author_type: {
|
|
55741
55743
|
type: "string",
|
|
55742
55744
|
enum: ["user", "agent"],
|
|
@@ -55749,12 +55751,12 @@ var init_partial_edits2 = __esm(() => {
|
|
|
55749
55751
|
});
|
|
55750
55752
|
|
|
55751
55753
|
// ../../_shared/mcp-tools/get-help-content.ts
|
|
55752
|
-
var HELP_FULL = '# Cerefox Knowledge Base -- Agent Quick Reference\n\nCerefox is a persistent, shared knowledge base. You have **15 core MCP tools** (14 with CLI equivalents — `cerefox_get_help` is MCP-only), plus 4 dormant relation tools that appear only when `relations_enabled` is on. For the full guide, search Cerefox for "How AI Agents Use Cerefox" or call `cerefox_get_help` to retrieve this content over MCP.\n\n## Tools\n\n| Tool | Purpose | Key params |\n|------|---------|------------|\n| `cerefox_search` | Find documents (hybrid FTS + semantic) | `query` (required), `project_name`, `metadata_filter`, `requestor` |\n| `cerefox_ingest` | Save or update a document | `title`, `content` (required), `document_id` (update by ID), `expected_content_hash` (**required on content updates** — see rule 9), `last_write_wins`, `update_if_exists`, `project_name` (single, non-destructive add on update), `project_names` (list, destructive replace on update), `metadata` (omit on update to keep existing tags; `{}` clears), `author` |\n| `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` |\n| `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) |\n| `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` |\n| `cerefox_restore_document` | Restore a soft-deleted document from the trash (audited inverse of delete; no-op if not deleted) | `document_id` (required), `reason` (recorded in the audit log), `author` |\n| `cerefox_get_document` | Get full document by ID (header includes `content_hash` — the update token), or with `outline: true` just its heading paths, sizes and hash, or with `section: "## Heading"` one section\'s text | `document_id` (required), `outline`, `section`, `section_part` |\n| `cerefox_list_versions` | Version history of a document | `document_id` (required) |\n| `cerefox_set_relation` ⚑ | Link two documents (`source --rel_type--> target`) | `source_id`, `target_id`, `rel_type` (required), `metadata`, `author` |\n| `cerefox_delete_relation` ⚑ | Remove a relation | `source_id`, `target_id`, `rel_type` |\n| `cerefox_get_relations` ⚑ | All relations touching a document, both directions | `document_id` |\n| `cerefox_get_neighbors` ⚑ | Walk the graph along ONE relation type | `document_id`, `rel_type` (required), `depth`, `from_time`, `to_time`, `limit` |\n| `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 |\n| `cerefox_list_metadata_keys` | Discover available metadata keys | (none required) |\n| `cerefox_list_projects` | List all projects | (none required) |\n| `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` |\n| `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) |\n| `cerefox_get_audit_log` | Query write operation history | `document_id`, `author`, `operation`, `since` |\n| `cerefox_get_help` | Retrieve Cerefox conventions (this reference) over MCP. **Call this whenever uncertain.** | `topic` (optional, case-insensitive H2 substring match) |\n\n⚑ **Opt-in — usually absent.** The four relation tools are hidden unless the\noperator enables them (`relations_enabled`). **Trust your own tool list**: if\nthey are not in it, the feature is switched off for this deployment. That is\nnormal, not an error, and not something to work around.\n\n## Editing part of a document (prefer this over re-sending)\n\n**Re-sending a whole document to change part of it is the main way agents lose\ndata.** You have to reproduce the untouched remainder verbatim, and any drift\nsilently rewrites content nobody asked you to touch — which the caller cannot\ndiff. Use the partial-edit tools instead:\n\n1. **Learn the anchors** — `cerefox_get_document(document_id, outline: true)`.\n Returns heading paths, per-section sizes and the `content_hash`, without the\n body. The paths it returns are exactly what `anchor_heading` accepts.\n2. **Add** → `cerefox_insert`. `end_of_document` is a plain append;\n `end_of_section` adds inside a named section. It is structurally incapable of\n removing anything, so "I meant to append" cannot become "I replaced the file".\n3. **Look before you overwrite** — `cerefox_get_document(document_id,\n section: "## Heading")` returns exactly the text a `replace_section` on that\n anchor would destroy. The outline gives you a section\'s *size*, never its\n *text*, so on a document you did not write yourself this is the difference\n between a replace and a blind overwrite.\n4. **Change or remove** → `cerefox_edit`. Put changes that belong together in\n ONE call: they apply atomically, so a table row and the total it feeds cannot\n end up disagreeing. To change a single line, `replace_section` on its\n smallest enclosing heading — that is the intended granularity, not a\n workaround. To fix a stale heading (`## OPEN TODOs (as of ...)`), use\n `rename_section`: it changes the heading text and leaves the body and\n position alone.\n5. All of them require `expected_content_hash` and **have no last-write-wins**. A\n conflict means someone else changed the document; re-read and decide, do not\n force it.\n\n**A section runs to the next same-or-higher heading, or to the end of the\ndocument.** So `end_of_document` inserts land inside the *last* section, and\nreplacing or deleting that section removes them too. A large shrink in the\nresponse is your warning; `cerefox_list_versions` has the previous content.\n\n**When an anchor is ambiguous the tool refuses and hands you the options** — a\nrepeated heading returns the qualifying paths, and a section with both its own\ncontent and sub-sections returns both `section_part` choices. That is a\nrecoverable answer, not a failure: retry with what it gave you.\n\n## Essential Rules\n\n1. **Search before ingesting** -- check if the document exists first.\n2. **Prefer ID-based updates** -- pass `document_id` from search results for deterministic updates. Falls back to title-matching with `update_if_exists: true`.\n3. **Set `author`/`requestor`** to your name on every call (e.g., "Claude Code", "archiver"). On MCP, pass as parameters. On CLI, pass `--author`/`--author-type`/`--requestor` flags, or rely on `CEREFOX_AUTHOR_NAME`/`CEREFOX_AUTHOR_TYPE`/`CEREFOX_REQUESTOR_NAME` env vars set in the user\'s `.env`.\n4. **Use `document_id` from search results** `[id: uuid]` for get_document and list_versions.\n5. **Add metadata** -- at minimum `type` ("decision-log", "research", "design-doc") and `status` ("active", "draft").\n6. **Write structured Markdown** with H1/H2/H3 headings for good chunking and search.\n7. **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.\n8. **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.\n9. **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.\n10. **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.\n11. **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 behaviour. When a search result or `cerefox_get_relations` shows a neighbour marked `[superseded]` or `[stale]`, say so rather than presenting it as current.\n12. **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`.\n\n## Update Workflow (ID-based -- preferred)\n\n```\nsearch("topic") -> find doc [id: abc123] -> get_document(abc123) -> note its content_hash -> modify ->\ningest(title="Same Title", content="...", document_id="abc123",\n expected_content_hash="<the hash you read>", author="my-agent")\n```\n\nOn a **conflict** error: get_document again (fresh content + fresh hash) -> merge your changes -> retry with the new hash.\n\n## Update Workflow (title-based -- fallback)\n\n```\nsearch("topic") -> find doc (note its hash) -> modify ->\ningest(title="Same Title", content="...", update_if_exists=true,\n expected_content_hash="<the hash you read>", author="my-agent")\n```\n\n## Catch-Up Workflow\n\n```\nmetadata_search(metadata_filter={"type": "decision-log"}, updated_since="2026-03-28T00:00:00Z")\n```\n\n## CLI fallback (when MCP is unavailable)\n\nIf `cerefox_search` is not in your tool list, your user has likely installed the Cerefox CLI. The canonical invocation is plain **`cerefox <subcommand>`** (the TypeScript CLI, installed via `npm install -g @cerefox/memory`). It uses a resource-verb shape (`cerefox document get`, `cerefox project list`, …).\n\nSame operations, same conventions. Full reference: [`docs/guides/cli.md`](docs/guides/cli.md). CLI flag names match MCP parameter names exactly (e.g. `metadata_filter` ↔ `--metadata-filter`); common flags also have single-letter short forms (`-f`, `-p`, `-c`, `-m`, `-u`, `-a`, `-r`). Use the canonical long name (what `--help` shows) or its short form — there are no long-form aliases like `--filter` or `--count`.\n\n| MCP tool | CLI |\n|---|---|\n| `cerefox_search` | `cerefox search "<q>" --requestor "<your-name>"` |\n| `cerefox_ingest` (paste) | `printf \'...\' \\| cerefox document ingest --paste --title "<t>" --author "<your-name>" --author-type agent` |\n| `cerefox_ingest` (update by ID) | `printf \'...\' \\| cerefox document ingest --paste --title "<t>" --document-id "<uuid>" --expected-content-hash "<hash>" --author "<your-name>" --author-type agent` |\n| `cerefox_get_document` | `cerefox document get <id> --version-id <vid> --requestor "<your-name>"` |\n| `cerefox_list_versions` | `cerefox document version list <id> --requestor "<your-name>"` |\n| `cerefox_list_projects` | `cerefox project list --requestor "<your-name>"` |\n| `cerefox_list_metadata_keys` | `cerefox metadata keys` |\n| `cerefox_insert` | `cerefox document insert <id> -t "<text>" -p <position> -a "<anchor-heading>" -e "<hash>" --requestor "<your-name>" --author-type agent` |\n| `cerefox_edit` | `cerefox document edit-parts <id> --operations \'<json>\' -e "<hash>" --requestor "<your-name>" --author-type agent` |\n| `cerefox_delete_document` | `cerefox document delete <id> --reason "<why>" --author "<your-name>" --author-type agent --yes` (confirms interactively instead of requiring the hash) |\n| `cerefox_restore_document` | `cerefox document restore <id> --reason "<why>" --author "<your-name>" --author-type agent` |\n| `cerefox_metadata_search` | `cerefox metadata search --metadata-filter \'<json>\' --requestor "<your-name>"` (list a project: `cerefox document list --project <name>`) |\n| `cerefox_set_document_metadata` | `cerefox document set-metadata <id> --set key=value` (also `--remove key`, `--json \'{...}\'`, `--replace`) |\n| `cerefox_set_document_projects` | `cerefox document set-projects <id> <name...> --author "<your-name>" --author-type agent` (or `--clear` to remove all) |\n| `cerefox_get_audit_log` | `cerefox audit list --requestor "<your-name>"` (add `--json` for scripted access) |\n| `cerefox_get_help` | `cerefox guides show agent-quick-reference` (or `cerefox guides list` for the full bundled-docs index) |\n\n**Set identity on every call**, exactly as you would on MCP:\n- Writes (`document ingest`, `document ingest-dir`): `--author "<your-name>" --author-type agent`\n- Reads: `--requestor "<your-name>"`\n\nOr have your user set `CEREFOX_AUTHOR_NAME` / `CEREFOX_AUTHOR_TYPE` / `CEREFOX_REQUESTOR_NAME` in their `.env` to apply defaults once.\n\n## Timestamps are UTC\n\nEvery timestamp Cerefox returns — `created_at` on audit entries, version\nhistory, document metadata — is **UTC**, and now carries its `Z` marker so it\ncannot be mistaken for local time.\n\n**When you write a date into a document\'s CONTENT, use your own clock, not a\nCerefox timestamp.** These are different things: a timestamp records when the\nserver stored something; a date in a log entry or a heading is authored content\nand belongs to your timezone. An agent working a Pacific afternoon read\n`2026-08-11` from version history, wrote "8/11" into its entries, and put a\nday\'s work in the future — the timestamp was correct, and copying it into\ncontent was not.\n\nCerefox deliberately does not convert to local time on the API or MCP paths.\n"Local" has no server-side meaning: the remote MCP server runs in a cloud\nfunction whose local time *is* UTC, while a local MCP server runs in yours, so\nthe same document would report two different times depending on transport. The\nweb UI converts because a browser knows the viewer\'s timezone; nothing\nserver-side does.\n\n## Mistakes that have actually happened\n\nEach of these comes from a real agent session, and each is easy to make.\n\n- **`cerefox_ingest` always replaces the ENTIRE document.** Never a section.\n Before sending, check that the tool name matches the intent: if the intent is\n "change one section", the call is `cerefox_edit` with `replace_section`. A\n section-sized edit sent as a full ingest truncated a 13,000-character index to\n a single word. It was recovered from version history within the minute, but\n only because it was noticed immediately.\n\n- **Do not include the anchor\'s own heading in your text.** `replace_section`\n keeps the heading and `insert` places your text inside the section, so\n including it produces two. This is now refused rather than silently applied,\n but the shape is worth knowing: it happened twice in one session, the second\n time while trying to repair the first. A *deeper* sub-heading inside your text\n is fine.\n\n- **Content between sections belongs to the section ABOVE it.** A section runs\n to the next heading of the same or higher level, so a `---` rule, a note, or\n any trailing text sitting just above the next heading is part of the section\n before it — even when it visually reads as belonging below. Replacing that\n section takes it too. An agent hit exactly this: a `---` that separated two\n major sections disappeared when the section above it was replaced. The write\n was correct by the addressing rules; the surprise is that "the end of this\n section" is further down the page than it looks. Note the loss warning will\n not catch it if your replacement text is longer than what it replaced, since\n there is then no net loss to report.\n\n- **To change only tags, use `cerefox_set_document_metadata`, never `cerefox_ingest`.**\n Ingest replaces the whole document, so re-sending it to set one tag carries the\n full transcription risk for no reason. The metadata tool merges: the keys you\n pass are set, everything else is left alone, so you do not need to read the\n document first and cannot drop a tag another agent set. Pass `null` as a value\n to remove a key.\n\n- **Never partial-edit to fix a partial edit.** If a write leaves unexpected\n structure, stop. Use `cerefox_list_versions`, retrieve the last good version,\n and re-ingest cleanly. Repairing edits with more edits compounds the damage.\n\n- **A rejected batch is safe.** Operations in one `cerefox_edit` are\n all-or-nothing: if any is invalid, nothing is written. A refusal costs you a\n retry, not data — so prefer one call for changes that belong together, and do\n not split a batch to "make it more likely to succeed".\n\n- **Read before replacing.** `cerefox_get_document(section: "## Heading")`\n returns exactly what a `replace_section` on that anchor would overwrite. Use it\n for any section you did not write in this session. The outline gives a\n section\'s *size*, never its *text*.\n\n- **Verify after writing** — read the result back before reporting success, and\n report what the read actually shows.\n\n- **Partial edits cannot change a document\'s stored TITLE.** `rename_section`\n changes a heading inside the content; the title is a separate field and still\n needs `cerefox_ingest`.\n\n- **If a capability seems missing from one server, suspect your client first.**\n Local and remote run the same code. **Every `cerefox_get_help()` response\n begins with the server\'s version and the operations it registers** — you do\n not need a special topic, and the *absence* of that block is itself an answer:\n a server that does not print it predates v1.5.0. If that\n disagrees with your tool list, the client is holding a list it fetched before\n an upgrade — clients cache it at connect time. Ask the user to restart the\n client. Do not record a capability difference between servers as a fact; every\n such report so far has been a stale client.\n- Long inline bodies can arrive with literal `\\n`/`\\"` (the author over-escaped; Cerefox stores bytes faithfully). For long or quote-dense content, ingest from a file or build incrementally with `cerefox_insert`; read back multi-line writes.\n', HELP_SECTIONS, HELP_SECTION_HEADINGS;
|
|
55754
|
+
var HELP_FULL = '# Cerefox Knowledge Base -- Agent Quick Reference\n\nCerefox is a persistent, shared knowledge base. You have **15 core MCP tools** (14 with CLI equivalents — `cerefox_get_help` is MCP-only), plus 4 dormant relation tools that appear only when `relations_enabled` is on. For the full guide, search Cerefox for "How AI Agents Use Cerefox" or call `cerefox_get_help` to retrieve this content over MCP.\n\n## Tools\n\n| Tool | Purpose | Key params |\n|------|---------|------------|\n| `cerefox_search` | Find documents (hybrid FTS + semantic) | `query` (required), `project_name`, `metadata_filter`, `author` |\n| `cerefox_ingest` | Save or update a document | `title`, `content` (required), `document_id` (update by ID), `expected_content_hash` (**required on content updates** — see rule 9), `last_write_wins`, `update_if_exists`, `project_name` (single, non-destructive add on update), `project_names` (list, destructive replace on update), `metadata` (omit on update to keep existing tags; `{}` clears), `author` |\n| `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` |\n| `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` |\n| `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` |\n| `cerefox_restore_document` | Restore a soft-deleted document from the trash (audited inverse of delete; no-op if not deleted) | `document_id` (required), `reason` (recorded in the audit log), `author` |\n| `cerefox_get_document` | Get full document by ID (header includes `content_hash` — the update token), or with `outline: true` just its heading paths, sizes and hash, or with `section: "## Heading"` one section\'s text | `document_id` (required), `outline`, `section`, `section_part`, `author` |\n| `cerefox_list_versions` | Version history of a document | `document_id` (required), `author` |\n| `cerefox_set_relation` ⚑ | Link two documents (`source --rel_type--> target`) | `source_id`, `target_id`, `rel_type` (required), `metadata`, `author` |\n| `cerefox_delete_relation` ⚑ | Remove a relation | `source_id`, `target_id`, `rel_type`, `author` |\n| `cerefox_get_relations` ⚑ | All relations touching a document, both directions | `document_id` |\n| `cerefox_get_neighbors` ⚑ | Walk the graph along ONE relation type | `document_id`, `rel_type` (required), `depth`, `from_time`, `to_time`, `limit` |\n| `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 |\n| `cerefox_list_metadata_keys` | Discover available metadata keys | (none required) |\n| `cerefox_list_projects` | List all projects | (none required) |\n| `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` |\n| `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` |\n| `cerefox_get_audit_log` | Query write operation history | `document_id`, `by_author` (filter), `operation`, `since`, `author` |\n| `cerefox_get_help` | Retrieve Cerefox conventions (this reference) over MCP. **Call this whenever uncertain.** | `topic` (optional, case-insensitive H2 substring match) |\n\n⚑ **Opt-in — usually absent.** The four relation tools are hidden unless the\noperator enables them (`relations_enabled`). **Trust your own tool list**: if\nthey are not in it, the feature is switched off for this deployment. That is\nnormal, not an error, and not something to work around.\n\n## Editing part of a document (prefer this over re-sending)\n\n**Re-sending a whole document to change part of it is the main way agents lose\ndata.** You have to reproduce the untouched remainder verbatim, and any drift\nsilently rewrites content nobody asked you to touch — which the caller cannot\ndiff. Use the partial-edit tools instead:\n\n1. **Learn the anchors** — `cerefox_get_document(document_id, outline: true)`.\n Returns heading paths, per-section sizes and the `content_hash`, without the\n body. The paths it returns are exactly what `anchor_heading` accepts.\n2. **Add** → `cerefox_insert`. `end_of_document` is a plain append;\n `end_of_section` adds inside a named section. It is structurally incapable of\n removing anything, so "I meant to append" cannot become "I replaced the file".\n3. **Look before you overwrite** — `cerefox_get_document(document_id,\n section: "## Heading")` returns exactly the text a `replace_section` on that\n anchor would destroy. The outline gives you a section\'s *size*, never its\n *text*, so on a document you did not write yourself this is the difference\n between a replace and a blind overwrite.\n4. **Change or remove** → `cerefox_edit`. Put changes that belong together in\n ONE call: they apply atomically, so a table row and the total it feeds cannot\n end up disagreeing. To change a single line, `replace_section` on its\n smallest enclosing heading — that is the intended granularity, not a\n workaround. To fix a stale heading (`## OPEN TODOs (as of ...)`), use\n `rename_section`: it changes the heading text and leaves the body and\n position alone.\n5. All of them require `expected_content_hash` and **have no last-write-wins**. A\n conflict means someone else changed the document; re-read and decide, do not\n force it.\n\n**A section runs to the next same-or-higher heading, or to the end of the\ndocument.** So `end_of_document` inserts land inside the *last* section, and\nreplacing or deleting that section removes them too. A large shrink in the\nresponse is your warning; `cerefox_list_versions` has the previous content.\n\n**When an anchor is ambiguous the tool refuses and hands you the options** — a\nrepeated heading returns the qualifying paths, and a section with both its own\ncontent and sub-sections returns both `section_part` choices. That is a\nrecoverable answer, not a failure: retry with what it gave you.\n\n## Essential Rules\n\n1. **Search before ingesting** -- check if the document exists first.\n2. **Prefer ID-based updates** -- pass `document_id` from search results for deterministic updates. Falls back to title-matching with `update_if_exists: true`.\n3. **Set `author`** to your name on every call, reads and writes alike (e.g., "Claude Code", "archiver"). Same parameter on every tool. (`requestor` is still accepted everywhere as the pre-1.13.1 alias.) On CLI, pass `--author`/`--author-type` on writes and `--requestor` on reads, or rely on `CEREFOX_AUTHOR_NAME`/`CEREFOX_AUTHOR_TYPE`/`CEREFOX_REQUESTOR_NAME` env vars set in the user\'s `.env`.\n4. **Use `document_id` from search results** `[id: uuid]` for get_document and list_versions.\n5. **Add metadata** -- at minimum `type` ("decision-log", "research", "design-doc") and `status` ("active", "draft").\n6. **Write structured Markdown** with H1/H2/H3 headings for good chunking and search.\n7. **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.\n8. **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.\n9. **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.\n10. **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.\n11. **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 behaviour. When a search result or `cerefox_get_relations` shows a neighbour marked `[superseded]` or `[stale]`, say so rather than presenting it as current.\n12. **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`.\n\n## Update Workflow (ID-based -- preferred)\n\n```\nsearch("topic") -> find doc [id: abc123] -> get_document(abc123) -> note its content_hash -> modify ->\ningest(title="Same Title", content="...", document_id="abc123",\n expected_content_hash="<the hash you read>", author="my-agent")\n```\n\nOn a **conflict** error: get_document again (fresh content + fresh hash) -> merge your changes -> retry with the new hash.\n\n## Update Workflow (title-based -- fallback)\n\n```\nsearch("topic") -> find doc (note its hash) -> modify ->\ningest(title="Same Title", content="...", update_if_exists=true,\n expected_content_hash="<the hash you read>", author="my-agent")\n```\n\n## Catch-Up Workflow\n\n```\nmetadata_search(metadata_filter={"type": "decision-log"}, updated_since="2026-03-28T00:00:00Z")\n```\n\n## CLI fallback (when MCP is unavailable)\n\nIf `cerefox_search` is not in your tool list, your user has likely installed the Cerefox CLI. The canonical invocation is plain **`cerefox <subcommand>`** (the TypeScript CLI, installed via `npm install -g @cerefox/memory`). It uses a resource-verb shape (`cerefox document get`, `cerefox project list`, …).\n\nSame operations, same conventions. Full reference: [`docs/guides/cli.md`](docs/guides/cli.md). CLI flag names match MCP parameter names exactly (e.g. `metadata_filter` ↔ `--metadata-filter`); common flags also have single-letter short forms (`-f`, `-p`, `-c`, `-m`, `-u`, `-a`, `-r`). Use the canonical long name (what `--help` shows) or its short form — there are no long-form aliases like `--filter` or `--count`.\n\n| MCP tool | CLI |\n|---|---|\n| `cerefox_search` | `cerefox search "<q>" --requestor "<your-name>"` |\n| `cerefox_ingest` (paste) | `printf \'...\' \\| cerefox document ingest --paste --title "<t>" --author "<your-name>" --author-type agent` |\n| `cerefox_ingest` (update by ID) | `printf \'...\' \\| cerefox document ingest --paste --title "<t>" --document-id "<uuid>" --expected-content-hash "<hash>" --author "<your-name>" --author-type agent` |\n| `cerefox_get_document` | `cerefox document get <id> --version-id <vid> --requestor "<your-name>"` |\n| `cerefox_list_versions` | `cerefox document version list <id> --requestor "<your-name>"` |\n| `cerefox_list_projects` | `cerefox project list --requestor "<your-name>"` |\n| `cerefox_list_metadata_keys` | `cerefox metadata keys` |\n| `cerefox_insert` | `cerefox document insert <id> -t "<text>" -p <position> -a "<anchor-heading>" -e "<hash>" --requestor "<your-name>" --author-type agent` |\n| `cerefox_edit` | `cerefox document edit-parts <id> --operations \'<json>\' -e "<hash>" --requestor "<your-name>" --author-type agent` |\n| `cerefox_delete_document` | `cerefox document delete <id> --reason "<why>" --author "<your-name>" --author-type agent --yes` (confirms interactively instead of requiring the hash) |\n| `cerefox_restore_document` | `cerefox document restore <id> --reason "<why>" --author "<your-name>" --author-type agent` |\n| `cerefox_metadata_search` | `cerefox metadata search --metadata-filter \'<json>\' --requestor "<your-name>"` (list a project: `cerefox document list --project <name>`) |\n| `cerefox_set_document_metadata` | `cerefox document set-metadata <id> --set key=value` (also `--remove key`, `--json \'{...}\'`, `--replace`) |\n| `cerefox_set_document_projects` | `cerefox document set-projects <id> <name...> --author "<your-name>" --author-type agent` (or `--clear` to remove all) |\n| `cerefox_get_audit_log` | `cerefox audit list --requestor "<your-name>"` (add `--json` for scripted access) |\n| `cerefox_get_help` | `cerefox guides show agent-quick-reference` (or `cerefox guides list` for the full bundled-docs index) |\n\n**Set identity on every call**, exactly as you would on MCP:\n- Writes (`document ingest`, `document ingest-dir`): `--author "<your-name>" --author-type agent`\n- Reads: `--requestor "<your-name>"`\n\nOr have your user set `CEREFOX_AUTHOR_NAME` / `CEREFOX_AUTHOR_TYPE` / `CEREFOX_REQUESTOR_NAME` in their `.env` to apply defaults once.\n\n## Timestamps are UTC\n\nEvery timestamp Cerefox returns — `created_at` on audit entries, version\nhistory, document metadata — is **UTC**, and now carries its `Z` marker so it\ncannot be mistaken for local time.\n\n**When you write a date into a document\'s CONTENT, use your own clock, not a\nCerefox timestamp.** These are different things: a timestamp records when the\nserver stored something; a date in a log entry or a heading is authored content\nand belongs to your timezone. An agent working a Pacific afternoon read\n`2026-08-11` from version history, wrote "8/11" into its entries, and put a\nday\'s work in the future — the timestamp was correct, and copying it into\ncontent was not.\n\nCerefox deliberately does not convert to local time on the API or MCP paths.\n"Local" has no server-side meaning: the remote MCP server runs in a cloud\nfunction whose local time *is* UTC, while a local MCP server runs in yours, so\nthe same document would report two different times depending on transport. The\nweb UI converts because a browser knows the viewer\'s timezone; nothing\nserver-side does.\n\n## Mistakes that have actually happened\n\nEach of these comes from a real agent session, and each is easy to make.\n\n- **`cerefox_ingest` always replaces the ENTIRE document.** Never a section.\n Before sending, check that the tool name matches the intent: if the intent is\n "change one section", the call is `cerefox_edit` with `replace_section`. A\n section-sized edit sent as a full ingest truncated a 13,000-character index to\n a single word. It was recovered from version history within the minute, but\n only because it was noticed immediately.\n\n- **Do not include the anchor\'s own heading in your text.** `replace_section`\n keeps the heading and `insert` places your text inside the section, so\n including it produces two. This is now refused rather than silently applied,\n but the shape is worth knowing: it happened twice in one session, the second\n time while trying to repair the first. A *deeper* sub-heading inside your text\n is fine.\n\n- **Content between sections belongs to the section ABOVE it.** A section runs\n to the next heading of the same or higher level, so a `---` rule, a note, or\n any trailing text sitting just above the next heading is part of the section\n before it — even when it visually reads as belonging below. Replacing that\n section takes it too. An agent hit exactly this: a `---` that separated two\n major sections disappeared when the section above it was replaced. The write\n was correct by the addressing rules; the surprise is that "the end of this\n section" is further down the page than it looks. Note the loss warning will\n not catch it if your replacement text is longer than what it replaced, since\n there is then no net loss to report.\n\n- **To change only tags, use `cerefox_set_document_metadata`, never `cerefox_ingest`.**\n Ingest replaces the whole document, so re-sending it to set one tag carries the\n full transcription risk for no reason. The metadata tool merges: the keys you\n pass are set, everything else is left alone, so you do not need to read the\n document first and cannot drop a tag another agent set. Pass `null` as a value\n to remove a key.\n\n- **Never partial-edit to fix a partial edit.** If a write leaves unexpected\n structure, stop. Use `cerefox_list_versions`, retrieve the last good version,\n and re-ingest cleanly. Repairing edits with more edits compounds the damage.\n\n- **A rejected batch is safe.** Operations in one `cerefox_edit` are\n all-or-nothing: if any is invalid, nothing is written. A refusal costs you a\n retry, not data — so prefer one call for changes that belong together, and do\n not split a batch to "make it more likely to succeed".\n\n- **Read before replacing.** `cerefox_get_document(section: "## Heading")`\n returns exactly what a `replace_section` on that anchor would overwrite. Use it\n for any section you did not write in this session. The outline gives a\n section\'s *size*, never its *text*.\n\n- **Verify after writing** — read the result back before reporting success, and\n report what the read actually shows.\n\n- **Partial edits cannot change a document\'s stored TITLE.** `rename_section`\n changes a heading inside the content; the title is a separate field and still\n needs `cerefox_ingest`.\n\n- **If a capability seems missing from one server, suspect your client first.**\n Local and remote run the same code. **Every `cerefox_get_help()` response\n begins with the server\'s version and the operations it registers** — you do\n not need a special topic, and the *absence* of that block is itself an answer:\n a server that does not print it predates v1.5.0. If that\n disagrees with your tool list, the client is holding a list it fetched before\n an upgrade — clients cache it at connect time. Ask the user to restart the\n client. Do not record a capability difference between servers as a fact; every\n such report so far has been a stale client.\n- Long inline bodies can arrive with literal `\\n`/`\\"` (the author over-escaped; Cerefox stores bytes faithfully). For long or quote-dense content, ingest from a file or build incrementally with `cerefox_insert`; read back multi-line writes.\n', HELP_SECTIONS, HELP_SECTION_HEADINGS;
|
|
55753
55755
|
var init_get_help_content = __esm(() => {
|
|
55754
55756
|
HELP_SECTIONS = {
|
|
55755
|
-
Tools: "## Tools\n\n| Tool | Purpose | Key params |\n|------|---------|------------|\n| `cerefox_search` | Find documents (hybrid FTS + semantic) | `query` (required), `project_name`, `metadata_filter`, `
|
|
55757
|
+
Tools: "## Tools\n\n| Tool | Purpose | Key params |\n|------|---------|------------|\n| `cerefox_search` | Find documents (hybrid FTS + semantic) | `query` (required), `project_name`, `metadata_filter`, `author` |\n| `cerefox_ingest` | Save or update a document | `title`, `content` (required), `document_id` (update by ID), `expected_content_hash` (**required on content updates** — see rule 9), `last_write_wins`, `update_if_exists`, `project_name` (single, non-destructive add on update), `project_names` (list, destructive replace on update), `metadata` (omit on update to keep existing tags; `{}` clears), `author` |\n| `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` |\n| `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` |\n| `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` |\n| `cerefox_restore_document` | Restore a soft-deleted document from the trash (audited inverse of delete; no-op if not deleted) | `document_id` (required), `reason` (recorded in the audit log), `author` |\n| `cerefox_get_document` | Get full document by ID (header includes `content_hash` — the update token), or with `outline: true` just its heading paths, sizes and hash, or with `section: \"## Heading\"` one section's text | `document_id` (required), `outline`, `section`, `section_part`, `author` |\n| `cerefox_list_versions` | Version history of a document | `document_id` (required), `author` |\n| `cerefox_set_relation` ⚑ | Link two documents (`source --rel_type--> target`) | `source_id`, `target_id`, `rel_type` (required), `metadata`, `author` |\n| `cerefox_delete_relation` ⚑ | Remove a relation | `source_id`, `target_id`, `rel_type`, `author` |\n| `cerefox_get_relations` ⚑ | All relations touching a document, both directions | `document_id` |\n| `cerefox_get_neighbors` ⚑ | Walk the graph along ONE relation type | `document_id`, `rel_type` (required), `depth`, `from_time`, `to_time`, `limit` |\n| `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 |\n| `cerefox_list_metadata_keys` | Discover available metadata keys | (none required) |\n| `cerefox_list_projects` | List all projects | (none required) |\n| `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` |\n| `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` |\n| `cerefox_get_audit_log` | Query write operation history | `document_id`, `by_author` (filter), `operation`, `since`, `author` |\n| `cerefox_get_help` | Retrieve Cerefox conventions (this reference) over MCP. **Call this whenever uncertain.** | `topic` (optional, case-insensitive H2 substring match) |\n\n⚑ **Opt-in — usually absent.** The four relation tools are hidden unless the\noperator enables them (`relations_enabled`). **Trust your own tool list**: if\nthey are not in it, the feature is switched off for this deployment. That is\nnormal, not an error, and not something to work around.",
|
|
55756
55758
|
"Editing part of a document (prefer this over re-sending)": '## Editing part of a document (prefer this over re-sending)\n\n**Re-sending a whole document to change part of it is the main way agents lose\ndata.** You have to reproduce the untouched remainder verbatim, and any drift\nsilently rewrites content nobody asked you to touch — which the caller cannot\ndiff. Use the partial-edit tools instead:\n\n1. **Learn the anchors** — `cerefox_get_document(document_id, outline: true)`.\n Returns heading paths, per-section sizes and the `content_hash`, without the\n body. The paths it returns are exactly what `anchor_heading` accepts.\n2. **Add** → `cerefox_insert`. `end_of_document` is a plain append;\n `end_of_section` adds inside a named section. It is structurally incapable of\n removing anything, so "I meant to append" cannot become "I replaced the file".\n3. **Look before you overwrite** — `cerefox_get_document(document_id,\n section: "## Heading")` returns exactly the text a `replace_section` on that\n anchor would destroy. The outline gives you a section\'s *size*, never its\n *text*, so on a document you did not write yourself this is the difference\n between a replace and a blind overwrite.\n4. **Change or remove** → `cerefox_edit`. Put changes that belong together in\n ONE call: they apply atomically, so a table row and the total it feeds cannot\n end up disagreeing. To change a single line, `replace_section` on its\n smallest enclosing heading — that is the intended granularity, not a\n workaround. To fix a stale heading (`## OPEN TODOs (as of ...)`), use\n `rename_section`: it changes the heading text and leaves the body and\n position alone.\n5. All of them require `expected_content_hash` and **have no last-write-wins**. A\n conflict means someone else changed the document; re-read and decide, do not\n force it.\n\n**A section runs to the next same-or-higher heading, or to the end of the\ndocument.** So `end_of_document` inserts land inside the *last* section, and\nreplacing or deleting that section removes them too. A large shrink in the\nresponse is your warning; `cerefox_list_versions` has the previous content.\n\n**When an anchor is ambiguous the tool refuses and hands you the options** — a\nrepeated heading returns the qualifying paths, and a section with both its own\ncontent and sub-sections returns both `section_part` choices. That is a\nrecoverable answer, not a failure: retry with what it gave you.',
|
|
55757
|
-
"Essential Rules": '## Essential Rules\n\n1. **Search before ingesting** -- check if the document exists first.\n2. **Prefer ID-based updates** -- pass `document_id` from search results for deterministic updates. Falls back to title-matching with `update_if_exists: true`.\n3. **Set `author
|
|
55759
|
+
"Essential Rules": '## Essential Rules\n\n1. **Search before ingesting** -- check if the document exists first.\n2. **Prefer ID-based updates** -- pass `document_id` from search results for deterministic updates. Falls back to title-matching with `update_if_exists: true`.\n3. **Set `author`** to your name on every call, reads and writes alike (e.g., "Claude Code", "archiver"). Same parameter on every tool. (`requestor` is still accepted everywhere as the pre-1.13.1 alias.) On CLI, pass `--author`/`--author-type` on writes and `--requestor` on reads, or rely on `CEREFOX_AUTHOR_NAME`/`CEREFOX_AUTHOR_TYPE`/`CEREFOX_REQUESTOR_NAME` env vars set in the user\'s `.env`.\n4. **Use `document_id` from search results** `[id: uuid]` for get_document and list_versions.\n5. **Add metadata** -- at minimum `type` ("decision-log", "research", "design-doc") and `status` ("active", "draft").\n6. **Write structured Markdown** with H1/H2/H3 headings for good chunking and search.\n7. **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.\n8. **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.\n9. **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.\n10. **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.\n11. **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 behaviour. When a search result or `cerefox_get_relations` shows a neighbour marked `[superseded]` or `[stale]`, say so rather than presenting it as current.\n12. **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`.',
|
|
55758
55760
|
"Update Workflow (ID-based -- preferred)": `## Update Workflow (ID-based -- preferred)
|
|
55759
55761
|
|
|
55760
55762
|
\`\`\`
|
|
@@ -55885,7 +55887,7 @@ async function handler5(supabase, args, ctx) {
|
|
|
55885
55887
|
logUsage(supabase, {
|
|
55886
55888
|
operation: "get_help",
|
|
55887
55889
|
accessPath: ctx.accessPath,
|
|
55888
|
-
requestor: args
|
|
55890
|
+
requestor: callerIdentity(args),
|
|
55889
55891
|
query_text: topic ?? null,
|
|
55890
55892
|
result_count: 1
|
|
55891
55893
|
});
|
|
@@ -55937,6 +55939,7 @@ var init_get_help = __esm(() => {
|
|
|
55937
55939
|
init_partial_edits2();
|
|
55938
55940
|
init__utils();
|
|
55939
55941
|
init_get_help_content();
|
|
55942
|
+
init_identity();
|
|
55940
55943
|
getHelpTool = {
|
|
55941
55944
|
name: "cerefox_get_help",
|
|
55942
55945
|
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.",
|
|
@@ -55953,10 +55956,7 @@ var init_get_help = __esm(() => {
|
|
|
55953
55956
|
type: "string",
|
|
55954
55957
|
description: 'Optional. Case-insensitive substring against H2 headings (e.g. "links", "update", "metadata"). Omit for the full reference.'
|
|
55955
55958
|
},
|
|
55956
|
-
|
|
55957
|
-
type: "string",
|
|
55958
|
-
description: 'Name of the agent or user making this request. Recorded in the usage log. Defaults to "mcp-agent".'
|
|
55959
|
-
}
|
|
55959
|
+
author: AUTHOR_PARAM_READ
|
|
55960
55960
|
}
|
|
55961
55961
|
},
|
|
55962
55962
|
handler: handler5
|
|
@@ -55998,7 +55998,7 @@ async function handler6(supabase, args, ctx) {
|
|
|
55998
55998
|
throw new McpInvalidParams('metadata must be a JSON object of key/value pairs, e.g. {"type":"note"} — not a string, number, or array');
|
|
55999
55999
|
}
|
|
56000
56000
|
const update_if_exists = args.update_if_exists ?? false;
|
|
56001
|
-
const author = args
|
|
56001
|
+
const author = callerIdentity(args) ?? DEFAULT_IDENTITY;
|
|
56002
56002
|
const author_type = "agent";
|
|
56003
56003
|
const expected_content_hash = args.expected_content_hash?.trim() || null;
|
|
56004
56004
|
const last_write_wins = args.last_write_wins ?? false;
|
|
@@ -56198,6 +56198,7 @@ var init_ingest = __esm(() => {
|
|
|
56198
56198
|
init__projects();
|
|
56199
56199
|
init__utils();
|
|
56200
56200
|
init_types3();
|
|
56201
|
+
init_identity();
|
|
56201
56202
|
ingestTool = {
|
|
56202
56203
|
name: "cerefox_ingest",
|
|
56203
56204
|
description: "Save a note or document to the Cerefox knowledge base. Updating an existing document REPLACES its whole content, so every unchanged character has to be reproduced exactly. Prefer cerefox_insert / cerefox_edit for any change to part of a document, and especially where the untouched content cannot be checked by reading it — document IDs, hashes, numeric tables, indexes and registries. Drifting prose is obvious on review; one wrong character in a UUID is not, and it silently breaks the reference.",
|
|
@@ -56241,10 +56242,7 @@ var init_ingest = __esm(() => {
|
|
|
56241
56242
|
description: "Explicitly skip the concurrency check and overwrite regardless of concurrent changes (default: false). Use ONLY when an external source of truth makes conflicts meaningless (e.g. re-syncing from files). Recorded in the audit log."
|
|
56242
56243
|
},
|
|
56243
56244
|
metadata: { type: "object", description: "Arbitrary JSON metadata (optional)" },
|
|
56244
|
-
author:
|
|
56245
|
-
type: "string",
|
|
56246
|
-
description: 'Name of the agent or tool performing the ingestion (e.g., "Claude Code", "archiver"). Recorded in the audit log for attribution. Defaults to "mcp-agent" if not provided. May be enforced via server config.'
|
|
56247
|
-
}
|
|
56245
|
+
author: AUTHOR_PARAM_WRITE
|
|
56248
56246
|
}
|
|
56249
56247
|
},
|
|
56250
56248
|
handler: handler6
|
|
@@ -56260,7 +56258,7 @@ async function handler7(supabase, args, ctx) {
|
|
|
56260
56258
|
logUsage(supabase, {
|
|
56261
56259
|
operation: "list_metadata_keys",
|
|
56262
56260
|
accessPath: ctx.accessPath,
|
|
56263
|
-
requestor: args
|
|
56261
|
+
requestor: callerIdentity(args),
|
|
56264
56262
|
result_count: keys.length
|
|
56265
56263
|
});
|
|
56266
56264
|
if (keys.length === 0)
|
|
@@ -56270,6 +56268,7 @@ async function handler7(supabase, args, ctx) {
|
|
|
56270
56268
|
var listMetadataKeysTool;
|
|
56271
56269
|
var init_list_metadata_keys = __esm(() => {
|
|
56272
56270
|
init__utils();
|
|
56271
|
+
init_identity();
|
|
56273
56272
|
listMetadataKeysTool = {
|
|
56274
56273
|
name: "cerefox_list_metadata_keys",
|
|
56275
56274
|
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.",
|
|
@@ -56282,10 +56281,7 @@ var init_list_metadata_keys = __esm(() => {
|
|
|
56282
56281
|
inputSchema: {
|
|
56283
56282
|
type: "object",
|
|
56284
56283
|
properties: {
|
|
56285
|
-
|
|
56286
|
-
type: "string",
|
|
56287
|
-
description: 'Name of the agent or user making this request. Recorded in the usage log. Defaults to "mcp-agent" if not provided. May be enforced via server config.'
|
|
56288
|
-
}
|
|
56284
|
+
author: AUTHOR_PARAM_READ
|
|
56289
56285
|
}
|
|
56290
56286
|
},
|
|
56291
56287
|
handler: handler7
|
|
@@ -56301,7 +56297,7 @@ async function handler8(supabase, args, ctx) {
|
|
|
56301
56297
|
logUsage(supabase, {
|
|
56302
56298
|
operation: "list_projects",
|
|
56303
56299
|
accessPath: ctx.accessPath,
|
|
56304
|
-
requestor: args
|
|
56300
|
+
requestor: callerIdentity(args),
|
|
56305
56301
|
result_count: projects.length
|
|
56306
56302
|
});
|
|
56307
56303
|
if (projects.length === 0)
|
|
@@ -56318,6 +56314,7 @@ ${lines.join(`
|
|
|
56318
56314
|
var listProjectsTool;
|
|
56319
56315
|
var init_list_projects = __esm(() => {
|
|
56320
56316
|
init__utils();
|
|
56317
|
+
init_identity();
|
|
56321
56318
|
listProjectsTool = {
|
|
56322
56319
|
name: "cerefox_list_projects",
|
|
56323
56320
|
description: "List all projects with their names and IDs. Use this to discover available projects before filtering by project_name in other tools.",
|
|
@@ -56330,10 +56327,7 @@ var init_list_projects = __esm(() => {
|
|
|
56330
56327
|
inputSchema: {
|
|
56331
56328
|
type: "object",
|
|
56332
56329
|
properties: {
|
|
56333
|
-
|
|
56334
|
-
type: "string",
|
|
56335
|
-
description: 'Name of the agent or user making this request. Recorded in the usage log. Defaults to "mcp-agent" if not provided. May be enforced via server config.'
|
|
56336
|
-
}
|
|
56330
|
+
author: AUTHOR_PARAM_READ
|
|
56337
56331
|
}
|
|
56338
56332
|
},
|
|
56339
56333
|
handler: handler8
|
|
@@ -56358,7 +56352,7 @@ async function handler9(supabase, args, ctx) {
|
|
|
56358
56352
|
logUsage(supabase, {
|
|
56359
56353
|
operation: "list_versions",
|
|
56360
56354
|
accessPath: ctx.accessPath,
|
|
56361
|
-
requestor: args
|
|
56355
|
+
requestor: callerIdentity(args),
|
|
56362
56356
|
document_id,
|
|
56363
56357
|
result_count: versions.length
|
|
56364
56358
|
});
|
|
@@ -56374,6 +56368,7 @@ var listVersionsTool;
|
|
|
56374
56368
|
var init_list_versions = __esm(() => {
|
|
56375
56369
|
init__utils();
|
|
56376
56370
|
init_types3();
|
|
56371
|
+
init_identity();
|
|
56377
56372
|
listVersionsTool = {
|
|
56378
56373
|
name: "cerefox_list_versions",
|
|
56379
56374
|
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.",
|
|
@@ -56391,10 +56386,7 @@ var init_list_versions = __esm(() => {
|
|
|
56391
56386
|
type: "string",
|
|
56392
56387
|
description: "UUID of the document whose version history to list"
|
|
56393
56388
|
},
|
|
56394
|
-
|
|
56395
|
-
type: "string",
|
|
56396
|
-
description: 'Name of the agent or user making this request. Recorded in the usage log. Defaults to "mcp-agent" if not provided. May be enforced via server config.'
|
|
56397
|
-
}
|
|
56389
|
+
author: AUTHOR_PARAM_READ
|
|
56398
56390
|
}
|
|
56399
56391
|
},
|
|
56400
56392
|
handler: handler9
|
|
@@ -56442,7 +56434,7 @@ async function handler10(supabase, args, ctx) {
|
|
|
56442
56434
|
logUsage(supabase, {
|
|
56443
56435
|
operation: "metadata_search",
|
|
56444
56436
|
accessPath: ctx.accessPath,
|
|
56445
|
-
requestor: args
|
|
56437
|
+
requestor: callerIdentity(args),
|
|
56446
56438
|
query_text: JSON.stringify(metadata_filter ?? {}),
|
|
56447
56439
|
project_id: projectId,
|
|
56448
56440
|
result_count: rows.length
|
|
@@ -56476,6 +56468,7 @@ var init_metadata_search = __esm(() => {
|
|
|
56476
56468
|
init__projects();
|
|
56477
56469
|
init_feature_flags();
|
|
56478
56470
|
init_types3();
|
|
56471
|
+
init_identity();
|
|
56479
56472
|
metadataSearchTool = {
|
|
56480
56473
|
name: "cerefox_metadata_search",
|
|
56481
56474
|
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.",
|
|
@@ -56511,10 +56504,7 @@ var init_metadata_search = __esm(() => {
|
|
|
56511
56504
|
type: "integer",
|
|
56512
56505
|
description: "Soft cap on total response bytes when include_content is true. Defaults to server maximum (200000)."
|
|
56513
56506
|
},
|
|
56514
|
-
|
|
56515
|
-
type: "string",
|
|
56516
|
-
description: 'Name of the agent or user making this request. Recorded in the usage log. Defaults to "mcp-agent" if not provided. May be enforced via server config.'
|
|
56517
|
-
}
|
|
56507
|
+
author: AUTHOR_PARAM_READ
|
|
56518
56508
|
}
|
|
56519
56509
|
},
|
|
56520
56510
|
handler: handler10
|
|
@@ -56600,7 +56590,7 @@ async function handler11(supabase, args, ctx) {
|
|
|
56600
56590
|
logUsage(supabase, {
|
|
56601
56591
|
operation: "search",
|
|
56602
56592
|
accessPath: ctx.accessPath,
|
|
56603
|
-
requestor: args
|
|
56593
|
+
requestor: callerIdentity(args),
|
|
56604
56594
|
query_text: query,
|
|
56605
56595
|
project_id: projectId,
|
|
56606
56596
|
result_count: accepted.length
|
|
@@ -56643,6 +56633,7 @@ var init_search = __esm(() => {
|
|
|
56643
56633
|
init__utils();
|
|
56644
56634
|
init__projects();
|
|
56645
56635
|
init_types3();
|
|
56636
|
+
init_identity();
|
|
56646
56637
|
searchTool = {
|
|
56647
56638
|
name: "cerefox_search",
|
|
56648
56639
|
description: "Search the Cerefox personal knowledge base. Returns complete documents ranked by hybrid (FTS + semantic) relevance.",
|
|
@@ -56691,10 +56682,7 @@ var init_search = __esm(() => {
|
|
|
56691
56682
|
type: "integer",
|
|
56692
56683
|
description: "Optional response size budget in bytes. Results are dropped whole until the budget is satisfied; a truncated flag is set when results are dropped. Defaults to the server maximum (200000). Pass a smaller value if your context window is limited. Values above the server maximum are silently capped."
|
|
56693
56684
|
},
|
|
56694
|
-
|
|
56695
|
-
type: "string",
|
|
56696
|
-
description: 'Name of the agent or user making this request (e.g., "Claude Code", "archiver"). Recorded in the usage log for attribution. Defaults to "mcp-agent" if not provided. May be enforced via server config.'
|
|
56697
|
-
}
|
|
56685
|
+
author: AUTHOR_PARAM_READ
|
|
56698
56686
|
}
|
|
56699
56687
|
},
|
|
56700
56688
|
handler: handler11
|
|
@@ -56717,7 +56705,7 @@ async function handler12(supabase, args, ctx) {
|
|
|
56717
56705
|
if (Object.keys(metadata).length === 0 && !replace) {
|
|
56718
56706
|
throw new McpInvalidParams("metadata is empty, which would change nothing. To clear all metadata pass replace: true with {}.");
|
|
56719
56707
|
}
|
|
56720
|
-
const author = args
|
|
56708
|
+
const author = callerIdentity(args);
|
|
56721
56709
|
const authorType = ctx.accessPath === "cli" ? "user" : "agent";
|
|
56722
56710
|
const { data, error } = await supabase.rpc("cerefox_set_document_metadata", {
|
|
56723
56711
|
p_document_id: document_id,
|
|
@@ -56734,7 +56722,7 @@ async function handler12(supabase, args, ctx) {
|
|
|
56734
56722
|
logUsage(supabase, {
|
|
56735
56723
|
operation: "update_metadata",
|
|
56736
56724
|
accessPath: ctx.accessPath,
|
|
56737
|
-
requestor:
|
|
56725
|
+
requestor: author,
|
|
56738
56726
|
document_id,
|
|
56739
56727
|
result_count: 1
|
|
56740
56728
|
});
|
|
@@ -56749,6 +56737,7 @@ var setDocumentMetadataTool;
|
|
|
56749
56737
|
var init_set_document_metadata = __esm(() => {
|
|
56750
56738
|
init__utils();
|
|
56751
56739
|
init_types3();
|
|
56740
|
+
init_identity();
|
|
56752
56741
|
setDocumentMetadataTool = {
|
|
56753
56742
|
name: "cerefox_set_document_metadata",
|
|
56754
56743
|
description: `Change a document's metadata WITHOUT resending its content. MERGES by default: the keys you pass are set, every other key is left alone — so you do not need to read the document first, and you cannot accidentally drop tags another agent set. To REMOVE a key, pass it with a null value ({"stale_key": null}). Pass replace: true to set the metadata to exactly the object given, discarding everything else (rare; the same destructive contract as cerefox_set_document_projects). Content, chunks and embeddings are untouched and no new version is created. Use this instead of cerefox_ingest whenever only the tags are changing.`,
|
|
@@ -56772,14 +56761,7 @@ var init_set_document_metadata = __esm(() => {
|
|
|
56772
56761
|
type: "boolean",
|
|
56773
56762
|
description: "Set the metadata to EXACTLY this object, discarding any key not listed. Defaults to false (merge). Use only when you mean to reset a document's tags wholesale."
|
|
56774
56763
|
},
|
|
56775
|
-
author:
|
|
56776
|
-
type: "string",
|
|
56777
|
-
description: "Who is making this change. Recorded in the audit log."
|
|
56778
|
-
},
|
|
56779
|
-
requestor: {
|
|
56780
|
-
type: "string",
|
|
56781
|
-
description: "Name of the agent or user making this request. Recorded in the usage log."
|
|
56782
|
-
}
|
|
56764
|
+
author: AUTHOR_PARAM_WRITE
|
|
56783
56765
|
}
|
|
56784
56766
|
},
|
|
56785
56767
|
handler: handler12
|
|
@@ -56790,7 +56772,7 @@ var init_set_document_metadata = __esm(() => {
|
|
|
56790
56772
|
async function handler13(supabase, args, ctx) {
|
|
56791
56773
|
const document_id = args.document_id?.trim();
|
|
56792
56774
|
const project_names_raw = args.project_names;
|
|
56793
|
-
const author = args
|
|
56775
|
+
const author = callerIdentity(args) ?? DEFAULT_IDENTITY;
|
|
56794
56776
|
if (!document_id) {
|
|
56795
56777
|
throw new McpInvalidParams("Missing required argument: document_id (UUID from a prior cerefox_search result).");
|
|
56796
56778
|
}
|
|
@@ -56819,6 +56801,7 @@ var setDocumentProjectsTool;
|
|
|
56819
56801
|
var init_set_document_projects = __esm(() => {
|
|
56820
56802
|
init__projects();
|
|
56821
56803
|
init_types3();
|
|
56804
|
+
init_identity();
|
|
56822
56805
|
setDocumentProjectsTool = {
|
|
56823
56806
|
name: "cerefox_set_document_projects",
|
|
56824
56807
|
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.",
|
|
@@ -56842,10 +56825,7 @@ var init_set_document_projects = __esm(() => {
|
|
|
56842
56825
|
items: { type: "string" },
|
|
56843
56826
|
description: "Explicit list of project names. Each created if absent. Order is preserved. Empty list = remove from all projects."
|
|
56844
56827
|
},
|
|
56845
|
-
author:
|
|
56846
|
-
type: "string",
|
|
56847
|
-
description: 'Agent or tool name recorded in the audit log. Defaults to "mcp-agent". May be enforced via server config.'
|
|
56848
|
-
}
|
|
56828
|
+
author: AUTHOR_PARAM_WRITE
|
|
56849
56829
|
}
|
|
56850
56830
|
},
|
|
56851
56831
|
handler: handler13
|
|
@@ -71580,12 +71560,12 @@ var CONFIG_CATALOG = [
|
|
|
71580
71560
|
},
|
|
71581
71561
|
{
|
|
71582
71562
|
key: "review_workflow_enabled",
|
|
71583
|
-
description: "
|
|
71563
|
+
description: "Show and enforce the review status of documents: agent-authored writes are marked 'pending review' for a person to approve. Off: no surface shows a review status and nothing enforces it; the status is still recorded and reappears unchanged when turned back on. Off on a fresh install; on for stores that predate the flag.",
|
|
71584
71564
|
kind: "boolean",
|
|
71585
71565
|
defaultValue: "false",
|
|
71586
71566
|
group: "Governance",
|
|
71587
71567
|
highImpact: true,
|
|
71588
|
-
impactNote: "Turning this OFF hides the review badges, the approve control and the search filter everywhere (web, API, MCP, CLI)
|
|
71568
|
+
impactNote: "Turning this OFF hides the review badges, the approve control and the search filter everywhere (web, API, MCP, CLI). It changes nothing stored and nothing about how writes are recorded: agent writes are still marked pending behind the scenes, so turning it back ON shows exactly the statuses the store would have had all along."
|
|
71589
71569
|
}
|
|
71590
71570
|
];
|
|
71591
71571
|
function configKeySpec(key) {
|
|
@@ -77857,7 +77837,7 @@ async function checkReviewWorkflow() {
|
|
|
77857
77837
|
return {
|
|
77858
77838
|
name,
|
|
77859
77839
|
status: "ok",
|
|
77860
|
-
detail: on ? "ON — agent writes land pending_review; review_status is shown everywhere" : "OFF —
|
|
77840
|
+
detail: on ? "ON — agent writes land pending_review; review_status is shown everywhere" : "OFF — review_status is hidden everywhere (still recorded from author_type)",
|
|
77861
77841
|
hint: on ? undefined : "Turn it on with `cerefox config set review_workflow_enabled true`."
|
|
77862
77842
|
};
|
|
77863
77843
|
}
|