@cerefox/memory 1.13.0 → 1.13.2
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 +33 -33
- package/AGENT_QUICK_REFERENCE.md +19 -19
- package/dist/bin/cerefox.js +124 -133
- 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 +4 -4
- 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-get-audit-log/index.ts +7 -5
- package/dist/server-assets/supabase/functions/cerefox-get-document/index.ts +5 -4
- package/dist/server-assets/supabase/functions/cerefox-ingest/index.ts +7 -3
- package/dist/server-assets/supabase/functions/cerefox-list-projects/index.ts +6 -5
- package/dist/server-assets/supabase/functions/cerefox-list-versions/index.ts +5 -4
- package/dist/server-assets/supabase/functions/cerefox-mcp/index.ts +6 -6
- package/dist/server-assets/supabase/functions/cerefox-metadata/index.ts +5 -4
- package/dist/server-assets/supabase/functions/cerefox-metadata-search/index.ts +5 -4
- package/dist/server-assets/supabase/functions/cerefox-search/index.ts +7 -4
- package/docs/guides/cli.md +20 -20
- package/docs/guides/configuration.md +37 -25
- package/docs/guides/connect-agents.md +47 -31
- package/docs/guides/upgrading.md +11 -0
- package/package.json +1 -1
|
@@ -38,6 +38,7 @@ import {
|
|
|
38
38
|
import { activeEmbedderName, embedBatch, resolveEmbedderKind } from "../embeddings/index.ts";
|
|
39
39
|
import { extractConflictHashes, isMissingFunctionError, logUsage } from "./_utils.ts";
|
|
40
40
|
import { McpInvalidParams, type MCPSupabaseClient, type ToolContext, type ToolDefinition } from "./types.ts";
|
|
41
|
+
import { AUTHOR_PARAM_WRITE, DEFAULT_IDENTITY, callerIdentity } from "./identity.ts";
|
|
41
42
|
|
|
42
43
|
/**
|
|
43
44
|
* Who to record as the author. Derived from the access path rather than taken
|
|
@@ -440,7 +441,7 @@ async function insertHandler(
|
|
|
440
441
|
documentId,
|
|
441
442
|
operations,
|
|
442
443
|
expectedHash,
|
|
443
|
-
requestor: (args
|
|
444
|
+
requestor: callerIdentity(args) ?? defaultRequestor(ctx),
|
|
444
445
|
toolLabel: "insert",
|
|
445
446
|
authorType: resolveAuthorType(ctx, args),
|
|
446
447
|
});
|
|
@@ -493,10 +494,7 @@ export const insertTool: ToolDefinition = {
|
|
|
493
494
|
description:
|
|
494
495
|
"content_hash of the version you are basing this on. Required — no last-write-wins.",
|
|
495
496
|
},
|
|
496
|
-
|
|
497
|
-
type: "string",
|
|
498
|
-
description: 'Agent or user making this request. Recorded in the usage log. Defaults to "mcp-agent".',
|
|
499
|
-
},
|
|
497
|
+
author: AUTHOR_PARAM_WRITE,
|
|
500
498
|
author_type: {
|
|
501
499
|
type: "string",
|
|
502
500
|
enum: ["user", "agent"],
|
|
@@ -538,7 +536,7 @@ async function editHandler(
|
|
|
538
536
|
documentId,
|
|
539
537
|
operations,
|
|
540
538
|
expectedHash,
|
|
541
|
-
requestor: (args
|
|
539
|
+
requestor: callerIdentity(args) ?? defaultRequestor(ctx),
|
|
542
540
|
toolLabel: "edit",
|
|
543
541
|
authorType: resolveAuthorType(ctx, args),
|
|
544
542
|
});
|
|
@@ -618,10 +616,7 @@ export const editTool: ToolDefinition = {
|
|
|
618
616
|
description:
|
|
619
617
|
"content_hash of the version you are basing these edits on. Required — no last-write-wins.",
|
|
620
618
|
},
|
|
621
|
-
|
|
622
|
-
type: "string",
|
|
623
|
-
description: 'Agent or user making this request. Recorded in the usage log. Defaults to "mcp-agent".',
|
|
624
|
-
},
|
|
619
|
+
author: AUTHOR_PARAM_WRITE,
|
|
625
620
|
author_type: {
|
|
626
621
|
type: "string",
|
|
627
622
|
enum: ["user", "agent"],
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
import { logUsage } from "./_utils.ts";
|
|
16
16
|
import { McpInvalidParams, type ToolContext, type ToolDefinition } from "./types.ts";
|
|
17
17
|
import type { MCPSupabaseClient } from "./types.ts";
|
|
18
|
+
import { AUTHOR_PARAM_READ, AUTHOR_PARAM_WRITE, DEFAULT_IDENTITY, callerIdentity } from "./identity.ts";
|
|
18
19
|
|
|
19
20
|
/** Relation types that carry behaviour; any other string is accepted too. */
|
|
20
21
|
const KNOWN_TYPES =
|
|
@@ -44,7 +45,7 @@ async function setHandler(
|
|
|
44
45
|
p_source_id: source,
|
|
45
46
|
p_target_id: target,
|
|
46
47
|
p_rel_type: relType,
|
|
47
|
-
p_author: (args
|
|
48
|
+
p_author: callerIdentity(args) ?? DEFAULT_IDENTITY,
|
|
48
49
|
p_author_type: "agent",
|
|
49
50
|
p_metadata: (args.metadata as Record<string, unknown> | undefined) ?? {},
|
|
50
51
|
});
|
|
@@ -57,7 +58,7 @@ async function setHandler(
|
|
|
57
58
|
logUsage(supabase, {
|
|
58
59
|
operation: "set_relation",
|
|
59
60
|
accessPath: ctx.accessPath,
|
|
60
|
-
requestor: args
|
|
61
|
+
requestor: callerIdentity(args),
|
|
61
62
|
document_id: source,
|
|
62
63
|
});
|
|
63
64
|
|
|
@@ -103,8 +104,7 @@ export const setRelationTool: ToolDefinition = {
|
|
|
103
104
|
type: "object",
|
|
104
105
|
description: "Optional JSON context for the edge (note, confidence, …)",
|
|
105
106
|
},
|
|
106
|
-
author:
|
|
107
|
-
requestor: { type: "string", description: "Name of the agent making this request" },
|
|
107
|
+
author: AUTHOR_PARAM_WRITE,
|
|
108
108
|
},
|
|
109
109
|
},
|
|
110
110
|
handler: setHandler,
|
|
@@ -126,7 +126,7 @@ async function deleteHandler(
|
|
|
126
126
|
p_source_id: source,
|
|
127
127
|
p_target_id: target,
|
|
128
128
|
p_rel_type: relType,
|
|
129
|
-
p_author: (args
|
|
129
|
+
p_author: callerIdentity(args) ?? DEFAULT_IDENTITY,
|
|
130
130
|
p_author_type: "agent",
|
|
131
131
|
});
|
|
132
132
|
if (error) throw new Error(`RPC error: ${error.message}`);
|
|
@@ -135,7 +135,7 @@ async function deleteHandler(
|
|
|
135
135
|
logUsage(supabase, {
|
|
136
136
|
operation: "delete_relation",
|
|
137
137
|
accessPath: ctx.accessPath,
|
|
138
|
-
requestor: args
|
|
138
|
+
requestor: callerIdentity(args),
|
|
139
139
|
document_id: source,
|
|
140
140
|
});
|
|
141
141
|
|
|
@@ -166,8 +166,7 @@ export const deleteRelationTool: ToolDefinition = {
|
|
|
166
166
|
source_id: { type: "string", description: "UUID of the source document" },
|
|
167
167
|
target_id: { type: "string", description: "UUID of the target document" },
|
|
168
168
|
rel_type: { type: "string", description: "Relation type to remove" },
|
|
169
|
-
author:
|
|
170
|
-
requestor: { type: "string", description: "Name of the agent making this request" },
|
|
169
|
+
author: AUTHOR_PARAM_WRITE,
|
|
171
170
|
},
|
|
172
171
|
},
|
|
173
172
|
handler: deleteHandler,
|
|
@@ -197,7 +196,7 @@ async function getRelationsHandler(
|
|
|
197
196
|
logUsage(supabase, {
|
|
198
197
|
operation: "get_relations",
|
|
199
198
|
accessPath: ctx.accessPath,
|
|
200
|
-
requestor: args
|
|
199
|
+
requestor: callerIdentity(args),
|
|
201
200
|
document_id: docId,
|
|
202
201
|
result_count: rows.length,
|
|
203
202
|
});
|
|
@@ -231,7 +230,7 @@ export const getRelationsTool: ToolDefinition = {
|
|
|
231
230
|
required: ["document_id"],
|
|
232
231
|
properties: {
|
|
233
232
|
document_id: { type: "string", description: "UUID of the document" },
|
|
234
|
-
|
|
233
|
+
author: AUTHOR_PARAM_READ,
|
|
235
234
|
},
|
|
236
235
|
},
|
|
237
236
|
handler: getRelationsHandler,
|
|
@@ -268,7 +267,7 @@ async function getNeighborsHandler(
|
|
|
268
267
|
logUsage(supabase, {
|
|
269
268
|
operation: "get_neighbors",
|
|
270
269
|
accessPath: ctx.accessPath,
|
|
271
|
-
requestor: args
|
|
270
|
+
requestor: callerIdentity(args),
|
|
272
271
|
document_id: docId,
|
|
273
272
|
result_count: rows.length,
|
|
274
273
|
});
|
|
@@ -307,7 +306,7 @@ export const getNeighborsTool: ToolDefinition = {
|
|
|
307
306
|
from_time: { type: "string", description: "ISO-8601: only neighbours created on/after" },
|
|
308
307
|
to_time: { type: "string", description: "ISO-8601: only neighbours created on/before" },
|
|
309
308
|
limit: { type: "integer", description: "Max documents to return (default 50, max 200)" },
|
|
310
|
-
|
|
309
|
+
author: AUTHOR_PARAM_READ,
|
|
311
310
|
},
|
|
312
311
|
},
|
|
313
312
|
handler: getNeighborsHandler,
|
|
@@ -21,6 +21,7 @@ import type { MCPSupabaseClient } from "./types.ts";
|
|
|
21
21
|
|
|
22
22
|
import { isDocumentNotFoundError, isMissingFunctionError, logUsage } from "./_utils.ts";
|
|
23
23
|
import { McpInvalidParams, type ToolContext, type ToolDefinition } from "./types.ts";
|
|
24
|
+
import { AUTHOR_PARAM_WRITE, DEFAULT_IDENTITY, callerIdentity } from "./identity.ts";
|
|
24
25
|
|
|
25
26
|
async function handler(
|
|
26
27
|
supabase: MCPSupabaseClient,
|
|
@@ -32,7 +33,7 @@ async function handler(
|
|
|
32
33
|
|
|
33
34
|
if (!document_id) throw new McpInvalidParams("document_id is required");
|
|
34
35
|
|
|
35
|
-
const author = (args
|
|
36
|
+
const author = callerIdentity(args);
|
|
36
37
|
// Derived from the transport, never taken from the caller: an agent must not
|
|
37
38
|
// be able to record itself as a user. Matches the delete handler.
|
|
38
39
|
const authorType = ctx.accessPath === "cli" ? "user" : "agent";
|
|
@@ -81,7 +82,7 @@ async function handler(
|
|
|
81
82
|
logUsage(supabase, {
|
|
82
83
|
operation: "restore",
|
|
83
84
|
accessPath: ctx.accessPath,
|
|
84
|
-
requestor:
|
|
85
|
+
requestor: author,
|
|
85
86
|
document_id,
|
|
86
87
|
result_count: 1,
|
|
87
88
|
});
|
|
@@ -116,14 +117,7 @@ export const restoreDocumentTool: ToolDefinition = {
|
|
|
116
117
|
description:
|
|
117
118
|
"Why this document is being restored. Recorded in the audit-log entry. Short and specific beats long.",
|
|
118
119
|
},
|
|
119
|
-
author:
|
|
120
|
-
type: "string",
|
|
121
|
-
description: "Who is making this change. Recorded in the audit log.",
|
|
122
|
-
},
|
|
123
|
-
requestor: {
|
|
124
|
-
type: "string",
|
|
125
|
-
description: "Name of the agent or user making this request. Recorded in the usage log.",
|
|
126
|
-
},
|
|
120
|
+
author: AUTHOR_PARAM_WRITE,
|
|
127
121
|
},
|
|
128
122
|
},
|
|
129
123
|
handler,
|
|
@@ -22,6 +22,7 @@ import { applyByteBudget, getConfiguredMinSearchScore, getConfiguredSearchAlpha,
|
|
|
22
22
|
getMaxResponseBytes, getMinTermCoverage, logUsage } from "./_utils.ts";
|
|
23
23
|
import { lookupProjectId } from "./_projects.ts";
|
|
24
24
|
import { McpInvalidParams, type ToolContext, type ToolDefinition } from "./types.ts";
|
|
25
|
+
import { AUTHOR_PARAM_READ, callerIdentity } from "./identity.ts";
|
|
25
26
|
|
|
26
27
|
async function handler(
|
|
27
28
|
supabase: MCPSupabaseClient,
|
|
@@ -135,7 +136,7 @@ async function handler(
|
|
|
135
136
|
logUsage(supabase, {
|
|
136
137
|
operation: "search",
|
|
137
138
|
accessPath: ctx.accessPath,
|
|
138
|
-
requestor: args
|
|
139
|
+
requestor: callerIdentity(args),
|
|
139
140
|
query_text: query,
|
|
140
141
|
project_id: projectId,
|
|
141
142
|
result_count: accepted.length,
|
|
@@ -244,11 +245,7 @@ export const searchTool: ToolDefinition = {
|
|
|
244
245
|
description:
|
|
245
246
|
"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.",
|
|
246
247
|
},
|
|
247
|
-
|
|
248
|
-
type: "string",
|
|
249
|
-
description:
|
|
250
|
-
'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.',
|
|
251
|
-
},
|
|
248
|
+
author: AUTHOR_PARAM_READ,
|
|
252
249
|
},
|
|
253
250
|
},
|
|
254
251
|
handler,
|
|
@@ -21,6 +21,7 @@ import type { MCPSupabaseClient } from "./types.ts";
|
|
|
21
21
|
|
|
22
22
|
import { logUsage } from "./_utils.ts";
|
|
23
23
|
import { McpInvalidParams, type ToolContext, type ToolDefinition } from "./types.ts";
|
|
24
|
+
import { AUTHOR_PARAM_WRITE, DEFAULT_IDENTITY, callerIdentity } from "./identity.ts";
|
|
24
25
|
|
|
25
26
|
async function handler(
|
|
26
27
|
supabase: MCPSupabaseClient,
|
|
@@ -48,7 +49,7 @@ async function handler(
|
|
|
48
49
|
);
|
|
49
50
|
}
|
|
50
51
|
|
|
51
|
-
const author = (args
|
|
52
|
+
const author = callerIdentity(args);
|
|
52
53
|
// Derived from the transport, never taken from the caller: an agent must not
|
|
53
54
|
// be able to record itself as a user. Matches the partial-edit handlers.
|
|
54
55
|
const authorType = ctx.accessPath === "cli" ? "user" : "agent";
|
|
@@ -71,7 +72,7 @@ async function handler(
|
|
|
71
72
|
logUsage(supabase, {
|
|
72
73
|
operation: "update_metadata",
|
|
73
74
|
accessPath: ctx.accessPath,
|
|
74
|
-
requestor:
|
|
75
|
+
requestor: author,
|
|
75
76
|
document_id,
|
|
76
77
|
result_count: 1,
|
|
77
78
|
});
|
|
@@ -119,14 +120,7 @@ export const setDocumentMetadataTool: ToolDefinition = {
|
|
|
119
120
|
description:
|
|
120
121
|
"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.",
|
|
121
122
|
},
|
|
122
|
-
author:
|
|
123
|
-
type: "string",
|
|
124
|
-
description: "Who is making this change. Recorded in the audit log.",
|
|
125
|
-
},
|
|
126
|
-
requestor: {
|
|
127
|
-
type: "string",
|
|
128
|
-
description: "Name of the agent or user making this request. Recorded in the usage log.",
|
|
129
|
-
},
|
|
123
|
+
author: AUTHOR_PARAM_WRITE,
|
|
130
124
|
},
|
|
131
125
|
},
|
|
132
126
|
handler,
|
|
@@ -13,6 +13,7 @@ import type { MCPSupabaseClient } from "./types.ts";
|
|
|
13
13
|
|
|
14
14
|
import { replaceDocumentProjects } from "./_projects.ts";
|
|
15
15
|
import { McpInvalidParams, type ToolContext, type ToolDefinition } from "./types.ts";
|
|
16
|
+
import { AUTHOR_PARAM_WRITE, DEFAULT_IDENTITY, callerIdentity } from "./identity.ts";
|
|
16
17
|
|
|
17
18
|
async function handler(
|
|
18
19
|
supabase: MCPSupabaseClient,
|
|
@@ -21,7 +22,7 @@ async function handler(
|
|
|
21
22
|
): Promise<string> {
|
|
22
23
|
const document_id = (args.document_id as string | undefined)?.trim();
|
|
23
24
|
const project_names_raw = args.project_names;
|
|
24
|
-
const author = (args
|
|
25
|
+
const author = callerIdentity(args) ?? DEFAULT_IDENTITY;
|
|
25
26
|
|
|
26
27
|
if (!document_id) {
|
|
27
28
|
throw new McpInvalidParams(
|
|
@@ -96,11 +97,7 @@ export const setDocumentProjectsTool: ToolDefinition = {
|
|
|
96
97
|
description:
|
|
97
98
|
"Explicit list of project names. Each created if absent. Order is preserved. Empty list = remove from all projects.",
|
|
98
99
|
},
|
|
99
|
-
author:
|
|
100
|
-
type: "string",
|
|
101
|
-
description:
|
|
102
|
-
'Agent or tool name recorded in the audit log. Defaults to "mcp-agent". May be enforced via server config.',
|
|
103
|
-
},
|
|
100
|
+
author: AUTHOR_PARAM_WRITE,
|
|
104
101
|
},
|
|
105
102
|
},
|
|
106
103
|
handler,
|
|
@@ -7,8 +7,10 @@
|
|
|
7
7
|
-- write ever overrides a value an operator has set (ON CONFLICT DO NOTHING).
|
|
8
8
|
--
|
|
9
9
|
-- The decision "agent write → pending_review" moves out of the six client call
|
|
10
|
-
-- sites and into cerefox_ingest_document
|
|
11
|
-
--
|
|
10
|
+
-- sites and into cerefox_ingest_document. That RPC lives in rpcs.sql, which
|
|
11
|
+
-- `cerefox server deploy` re-applies. (0.16.0 also had the RPC read this flag
|
|
12
|
+
-- on write and store 'approved' for everyone while off; 0.16.1 removed that —
|
|
13
|
+
-- the flag governs visibility only, the stored value follows author_type.)
|
|
12
14
|
--
|
|
13
15
|
-- #240: cerefox_hybrid_search / cerefox_search_docs gain p_review_status so a
|
|
14
16
|
-- filtered search is applied before the limit, not after. A new argument is a
|
|
@@ -1395,9 +1395,10 @@ $$;
|
|
|
1395
1395
|
-- update keeps the existing metadata (v0.11.1). Pass '{}'
|
|
1396
1396
|
-- explicitly to clear all metadata.
|
|
1397
1397
|
-- p_review_status : ACCEPTED AND IGNORED since 0.16.0 (#241). The status is
|
|
1398
|
-
-- decided here from p_author_type
|
|
1399
|
-
--
|
|
1400
|
-
-- behaves the same
|
|
1398
|
+
-- decided here from p_author_type alone (agent →
|
|
1399
|
+
-- pending_review, user → approved), so every transport
|
|
1400
|
+
-- behaves the same. The `review_workflow_enabled` flag
|
|
1401
|
+
-- only governs what surfaces show (0.16.1).
|
|
1401
1402
|
-- Kept in the signature so no caller breaks.
|
|
1402
1403
|
-- p_chunks : JSONB array of chunk objects, each with:
|
|
1403
1404
|
-- chunk_index, heading_path, heading_level, title,
|
|
@@ -1583,15 +1584,17 @@ BEGIN
|
|
|
1583
1584
|
USING ERRCODE = '22023'; -- deterministic; never a retryable SQLSTATE
|
|
1584
1585
|
END IF;
|
|
1585
1586
|
|
|
1586
|
-
-- Review status is decided HERE, not by the caller (#241)
|
|
1587
|
-
--
|
|
1588
|
-
--
|
|
1589
|
-
--
|
|
1590
|
-
--
|
|
1591
|
-
--
|
|
1592
|
-
--
|
|
1587
|
+
-- Review status is decided HERE, not by the caller (#241), from the
|
|
1588
|
+
-- author type alone: an agent write is 'pending_review', a person's is
|
|
1589
|
+
-- 'approved'. The `review_workflow_enabled` flag is deliberately NOT
|
|
1590
|
+
-- consulted (0.16.1): it governs what the surfaces show and enforce, not
|
|
1591
|
+
-- what is stored, so a store that turns the workflow off and later back on
|
|
1592
|
+
-- sees exactly the statuses it would have had all along. 0.16.0 wrote
|
|
1593
|
+
-- 'approved' for every author while the flag was off; that made a stored
|
|
1594
|
+
-- 'approved' mean two different things depending on when it was written.
|
|
1595
|
+
-- p_review_status is not consulted either — six clients used to compute
|
|
1596
|
+
-- it and they could not have agreed on a store-level policy.
|
|
1593
1597
|
v_status := CASE
|
|
1594
|
-
WHEN NOT cerefox_config_bool('review_workflow_enabled', FALSE) THEN 'approved'
|
|
1595
1598
|
WHEN p_author_type = 'agent' THEN 'pending_review'
|
|
1596
1599
|
ELSE 'approved'
|
|
1597
1600
|
END;
|
|
@@ -3088,6 +3091,10 @@ SET search_path = public, pg_catalog
|
|
|
3088
3091
|
AS $$
|
|
3089
3092
|
-- Keep in lockstep with the `@version:` marker in schema.sql (cut_release.ts
|
|
3090
3093
|
-- enforces it). Bump whenever schema.sql OR rpcs.sql changes.
|
|
3094
|
+
-- 0.16.1 (v1.13.1): cerefox_ingest_document no longer consults
|
|
3095
|
+
-- `review_workflow_enabled` — review_status follows author_type whatever
|
|
3096
|
+
-- the flag says; the flag governs visibility/enforcement only. RPC-only,
|
|
3097
|
+
-- no migration (server deploy re-applies rpcs.sql).
|
|
3091
3098
|
-- 0.16.0 (#241, #240): `review_workflow_enabled` config key (seeded false
|
|
3092
3099
|
-- on fresh installs, true by migration 0031 on existing ones);
|
|
3093
3100
|
-- cerefox_ingest_document decides review_status itself from author_type
|
|
@@ -3117,7 +3124,7 @@ AS $$
|
|
|
3117
3124
|
-- 0.11.0 supersedes 0.10.6 (v1.2.1, #191): this branch carries that fix plus
|
|
3118
3125
|
-- the partial-edit surface, and both migrations (0019, 0020) are in the
|
|
3119
3126
|
-- sequence, so a store deploying this gets everything from both lines.
|
|
3120
|
-
SELECT '0.16.
|
|
3127
|
+
SELECT '0.16.1'::TEXT;
|
|
3121
3128
|
$$;
|
|
3122
3129
|
|
|
3123
3130
|
-- ── cerefox_find_dead_links ──────────────────────────────────────────────────
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
-- Requires extensions: vector (pgvector), uuid-ossp
|
|
6
6
|
-- These are enabled at the top of db_deploy.py before this file is applied.
|
|
7
7
|
--
|
|
8
|
-
-- @version: 0.16.
|
|
8
|
+
-- @version: 0.16.1
|
|
9
9
|
-- The `@version` marker above is read by the schema-version-mismatch banner
|
|
10
10
|
-- (see /api/v1/schema-version). Bump it whenever schema.sql OR rpcs.sql
|
|
11
11
|
-- changes in a way that requires `cerefox server deploy` to be re-run —
|
|
@@ -56,9 +56,9 @@ CREATE TABLE IF NOT EXISTS cerefox_documents (
|
|
|
56
56
|
-- review_status: human governance flag. 'approved' = validated by human,
|
|
57
57
|
-- 'pending_review' = modified by agent, not yet reviewed.
|
|
58
58
|
-- Content is searchable in both states. Written ONLY by
|
|
59
|
-
-- cerefox_ingest_document,
|
|
60
|
-
-- (#241)
|
|
61
|
-
--
|
|
59
|
+
-- cerefox_ingest_document, from author_type alone. The store-level
|
|
60
|
+
-- `review_workflow_enabled` flag (#241) governs whether any surface shows
|
|
61
|
+
-- or enforces the column; it never changes what is stored (0.16.1).
|
|
62
62
|
review_status TEXT NOT NULL DEFAULT 'approved',
|
|
63
63
|
-- lifecycle_status: where this document stands relative to the graph —
|
|
64
64
|
-- 'active' | 'superseded' | 'stale' | 'archived'. Distinct from
|
|
@@ -2,6 +2,7 @@ import "jsr:@supabase/functions-js/edge-runtime.d.ts";
|
|
|
2
2
|
import { createClient } from "jsr:@supabase/supabase-js@2";
|
|
3
3
|
import { isVersionRequest, versionResponse } from "../../../_shared/ef-meta/index.ts";
|
|
4
4
|
import { efAuthGate } from "../../../_shared/ef-auth/index.ts";
|
|
5
|
+
import { callerIdentity } from "../../../_shared/mcp-tools/identity.ts";
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* cerefox-get-audit-log -- Supabase Edge Function
|
|
@@ -61,9 +62,9 @@ Deno.serve(async (req: Request): Promise<Response> => {
|
|
|
61
62
|
|
|
62
63
|
const body = await req.json().catch(() => ({}));
|
|
63
64
|
|
|
64
|
-
// Configurable requestor
|
|
65
|
-
const identityField = "
|
|
66
|
-
const identityValue = body
|
|
65
|
+
// Configurable caller-identity enforcement: `author`, or `requestor` as the pre-1.13.2 alias (#244)
|
|
66
|
+
const identityField = "author";
|
|
67
|
+
const identityValue = callerIdentity(body as Record<string, unknown>);
|
|
67
68
|
const { data: reqConfig } = await supabase.rpc("cerefox_get_config", { p_key: "require_requestor_identity" });
|
|
68
69
|
if (reqConfig === "true") {
|
|
69
70
|
if (!identityValue || (typeof identityValue === "string" && identityValue.trim() === "")) {
|
|
@@ -85,7 +86,8 @@ Deno.serve(async (req: Request): Promise<Response> => {
|
|
|
85
86
|
|
|
86
87
|
const params: Record<string, unknown> = {};
|
|
87
88
|
if (body.document_id) params.p_document_id = body.document_id;
|
|
88
|
-
|
|
89
|
+
// v1.13.2: the entries filter is `by_author`; `author` is the caller's identity (#244).
|
|
90
|
+
if (body.by_author) params.p_author = body.by_author;
|
|
89
91
|
if (body.operation) params.p_operation = body.operation;
|
|
90
92
|
if (body.since) params.p_since = body.since;
|
|
91
93
|
if (body.until) params.p_until = body.until;
|
|
@@ -107,7 +109,7 @@ Deno.serve(async (req: Request): Promise<Response> => {
|
|
|
107
109
|
Promise.resolve(supabase.rpc("cerefox_log_usage", {
|
|
108
110
|
p_operation: "get_audit_log",
|
|
109
111
|
p_access_path: "edge-function",
|
|
110
|
-
p_requestor:
|
|
112
|
+
p_requestor: identityValue ?? null,
|
|
111
113
|
p_result_count: (data ?? []).length,
|
|
112
114
|
})).catch(() => {});
|
|
113
115
|
|
|
@@ -2,6 +2,7 @@ import "jsr:@supabase/functions-js/edge-runtime.d.ts";
|
|
|
2
2
|
import { createClient } from "jsr:@supabase/supabase-js@2";
|
|
3
3
|
import { isVersionRequest, versionResponse } from "../../../_shared/ef-meta/index.ts";
|
|
4
4
|
import { efAuthGate } from "../../../_shared/ef-auth/index.ts";
|
|
5
|
+
import { callerIdentity } from "../../../_shared/mcp-tools/identity.ts";
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* cerefox-get-document — Supabase Edge Function
|
|
@@ -67,9 +68,9 @@ Deno.serve(async (req: Request): Promise<Response> => {
|
|
|
67
68
|
const supabaseKey = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!;
|
|
68
69
|
const supabase = createClient(supabaseUrl, supabaseKey);
|
|
69
70
|
|
|
70
|
-
// Configurable requestor
|
|
71
|
-
const identityField = "
|
|
72
|
-
const identityValue = body
|
|
71
|
+
// Configurable caller-identity enforcement: `author`, or `requestor` as the pre-1.13.2 alias (#244)
|
|
72
|
+
const identityField = "author";
|
|
73
|
+
const identityValue = callerIdentity(body as Record<string, unknown>);
|
|
73
74
|
const { data: reqConfig } = await supabase.rpc("cerefox_get_config", { p_key: "require_requestor_identity" });
|
|
74
75
|
if (reqConfig === "true") {
|
|
75
76
|
if (!identityValue || (typeof identityValue === "string" && identityValue.trim() === "")) {
|
|
@@ -120,7 +121,7 @@ Deno.serve(async (req: Request): Promise<Response> => {
|
|
|
120
121
|
Promise.resolve(supabase.rpc("cerefox_log_usage", {
|
|
121
122
|
p_operation: "get_document",
|
|
122
123
|
p_access_path: "edge-function",
|
|
123
|
-
p_requestor:
|
|
124
|
+
p_requestor: identityValue ?? null,
|
|
124
125
|
p_document_id: document_id,
|
|
125
126
|
p_result_count: 1,
|
|
126
127
|
})).catch(() => {});
|
|
@@ -2,6 +2,7 @@ import "jsr:@supabase/functions-js/edge-runtime.d.ts";
|
|
|
2
2
|
import { createClient } from "jsr:@supabase/supabase-js@2";
|
|
3
3
|
import { isVersionRequest, versionResponse } from "../../../_shared/ef-meta/index.ts";
|
|
4
4
|
import { efAuthGate } from "../../../_shared/ef-auth/index.ts";
|
|
5
|
+
import { callerIdentity } from "../../../_shared/mcp-tools/identity.ts";
|
|
5
6
|
import { capEmbeddingInput } from "../../../_shared/embeddings/index.ts";
|
|
6
7
|
import {
|
|
7
8
|
ensureDocumentInProject,
|
|
@@ -52,6 +53,8 @@ interface IngestRequest {
|
|
|
52
53
|
metadata?: Record<string, unknown>;
|
|
53
54
|
update_if_exists?: boolean;
|
|
54
55
|
author?: string;
|
|
56
|
+
/** Alias of `author` (#244). */
|
|
57
|
+
requestor?: string;
|
|
55
58
|
author_type?: string; // 'user' | 'agent'
|
|
56
59
|
// Optimistic concurrency (iter-32): REQUIRED on content updates — the
|
|
57
60
|
// content_hash of the version this edit was based on. Conflict → HTTP 409.
|
|
@@ -275,7 +278,8 @@ Deno.serve(async (req: Request) => {
|
|
|
275
278
|
// metadata: null = "not provided" — the RPC keeps existing metadata on
|
|
276
279
|
// update and uses {} on create (v0.11.1; a `= {}` default here used to wipe
|
|
277
280
|
// a document's tags on every content update that didn't re-pass them).
|
|
278
|
-
const { title, content, document_id = null, project_name, source = "agent", metadata = null, update_if_exists = false,
|
|
281
|
+
const { title, content, document_id = null, project_name, source = "agent", metadata = null, update_if_exists = false, author_type = "agent", expected_content_hash = null, last_write_wins = false } = body;
|
|
282
|
+
const author = callerIdentity(body as unknown as Record<string, unknown>) ?? "agent";
|
|
279
283
|
|
|
280
284
|
// metadata must be a plain JSON object (or absent). A scalar/array stored in
|
|
281
285
|
// the JSONB column poisons cerefox_list_metadata_keys for the whole dataset
|
|
@@ -303,10 +307,10 @@ Deno.serve(async (req: Request) => {
|
|
|
303
307
|
const supabaseKey = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!;
|
|
304
308
|
const supabase = createClient(supabaseUrl, supabaseKey);
|
|
305
309
|
|
|
306
|
-
// Configurable requestor
|
|
310
|
+
// Configurable caller-identity enforcement: `author`, or `requestor` as the alias (#244)
|
|
307
311
|
{
|
|
308
312
|
const identityField = "author";
|
|
309
|
-
const identityValue = body
|
|
313
|
+
const identityValue = callerIdentity(body as unknown as Record<string, unknown>);
|
|
310
314
|
const { data: reqConfig } = await supabase.rpc("cerefox_get_config", { p_key: "require_requestor_identity" });
|
|
311
315
|
if (reqConfig === "true") {
|
|
312
316
|
if (!identityValue || (typeof identityValue === "string" && identityValue.trim() === "")) {
|
|
@@ -2,6 +2,7 @@ import "jsr:@supabase/functions-js/edge-runtime.d.ts";
|
|
|
2
2
|
import { createClient } from "jsr:@supabase/supabase-js@2";
|
|
3
3
|
import { isVersionRequest, versionResponse } from "../../../_shared/ef-meta/index.ts";
|
|
4
4
|
import { efAuthGate } from "../../../_shared/ef-auth/index.ts";
|
|
5
|
+
import { callerIdentity } from "../../../_shared/mcp-tools/identity.ts";
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* cerefox-list-projects -- Supabase Edge Function
|
|
@@ -15,7 +16,7 @@ import { efAuthGate } from "../../../_shared/ef-auth/index.ts";
|
|
|
15
16
|
*
|
|
16
17
|
* Note: cerefox-mcp calls the RPC directly (not this Edge Function).
|
|
17
18
|
*
|
|
18
|
-
* Request body (JSON): {} or {
|
|
19
|
+
* Request body (JSON): {} or { author?: string }
|
|
19
20
|
* Response (200): Array of { id, name, description }
|
|
20
21
|
*/
|
|
21
22
|
|
|
@@ -51,9 +52,9 @@ Deno.serve(async (req: Request): Promise<Response> => {
|
|
|
51
52
|
const supabaseKey = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!;
|
|
52
53
|
const supabase = createClient(supabaseUrl, supabaseKey);
|
|
53
54
|
|
|
54
|
-
// Configurable requestor
|
|
55
|
-
const identityField = "
|
|
56
|
-
const identityValue = body
|
|
55
|
+
// Configurable caller-identity enforcement: `author`, or `requestor` as the pre-1.13.2 alias (#244)
|
|
56
|
+
const identityField = "author";
|
|
57
|
+
const identityValue = callerIdentity(body as Record<string, unknown>);
|
|
57
58
|
const { data: reqConfig } = await supabase.rpc("cerefox_get_config", { p_key: "require_requestor_identity" });
|
|
58
59
|
if (reqConfig === "true") {
|
|
59
60
|
if (!identityValue || (typeof identityValue === "string" && identityValue.trim() === "")) {
|
|
@@ -86,7 +87,7 @@ Deno.serve(async (req: Request): Promise<Response> => {
|
|
|
86
87
|
Promise.resolve(supabase.rpc("cerefox_log_usage", {
|
|
87
88
|
p_operation: "list_projects",
|
|
88
89
|
p_access_path: "edge-function",
|
|
89
|
-
p_requestor:
|
|
90
|
+
p_requestor: identityValue ?? null,
|
|
90
91
|
p_result_count: (data ?? []).length,
|
|
91
92
|
})).catch(() => {});
|
|
92
93
|
|
|
@@ -2,6 +2,7 @@ import "jsr:@supabase/functions-js/edge-runtime.d.ts";
|
|
|
2
2
|
import { createClient } from "jsr:@supabase/supabase-js@2";
|
|
3
3
|
import { isVersionRequest, versionResponse } from "../../../_shared/ef-meta/index.ts";
|
|
4
4
|
import { efAuthGate } from "../../../_shared/ef-auth/index.ts";
|
|
5
|
+
import { callerIdentity } from "../../../_shared/mcp-tools/identity.ts";
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* cerefox-list-versions — Supabase Edge Function
|
|
@@ -65,9 +66,9 @@ Deno.serve(async (req: Request): Promise<Response> => {
|
|
|
65
66
|
const supabaseKey = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!;
|
|
66
67
|
const supabase = createClient(supabaseUrl, supabaseKey);
|
|
67
68
|
|
|
68
|
-
// Configurable requestor
|
|
69
|
-
const identityField = "
|
|
70
|
-
const identityValue = body
|
|
69
|
+
// Configurable caller-identity enforcement: `author`, or `requestor` as the pre-1.13.2 alias (#244)
|
|
70
|
+
const identityField = "author";
|
|
71
|
+
const identityValue = callerIdentity(body as Record<string, unknown>);
|
|
71
72
|
const { data: reqConfig } = await supabase.rpc("cerefox_get_config", { p_key: "require_requestor_identity" });
|
|
72
73
|
if (reqConfig === "true") {
|
|
73
74
|
if (!identityValue || (typeof identityValue === "string" && identityValue.trim() === "")) {
|
|
@@ -102,7 +103,7 @@ Deno.serve(async (req: Request): Promise<Response> => {
|
|
|
102
103
|
Promise.resolve(supabase.rpc("cerefox_log_usage", {
|
|
103
104
|
p_operation: "list_versions",
|
|
104
105
|
p_access_path: "edge-function",
|
|
105
|
-
p_requestor:
|
|
106
|
+
p_requestor: identityValue ?? null,
|
|
106
107
|
p_document_id: document_id,
|
|
107
108
|
p_result_count: (data ?? []).length,
|
|
108
109
|
})).catch(() => {});
|
|
@@ -105,12 +105,12 @@ async function handleToolsCall(
|
|
|
105
105
|
|
|
106
106
|
// Configurable caller identity enforcement.
|
|
107
107
|
// When require_requestor_identity is "true" in cerefox_config, all tool calls
|
|
108
|
-
// must include
|
|
109
|
-
//
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
const identityValue = args
|
|
108
|
+
// must include the caller's identity; when requestor_identity_format is set,
|
|
109
|
+
// the value must match the regex. Since v1.13.1 every tool takes `author`
|
|
110
|
+
// and silently accepts `requestor` as the pre-1.13.1 alias (mirrors
|
|
111
|
+
// `callerIdentity()` in _shared/mcp-tools/identity.ts).
|
|
112
|
+
const identityParam = "author";
|
|
113
|
+
const identityValue = (args.author ?? args.requestor) as string | undefined;
|
|
114
114
|
|
|
115
115
|
// deno-lint-ignore no-explicit-any
|
|
116
116
|
const supabase: any = makeSupabaseClient();
|
|
@@ -2,6 +2,7 @@ import "jsr:@supabase/functions-js/edge-runtime.d.ts";
|
|
|
2
2
|
import { createClient } from "jsr:@supabase/supabase-js@2";
|
|
3
3
|
import { isVersionRequest, versionResponse } from "../../../_shared/ef-meta/index.ts";
|
|
4
4
|
import { efAuthGate } from "../../../_shared/ef-auth/index.ts";
|
|
5
|
+
import { callerIdentity } from "../../../_shared/mcp-tools/identity.ts";
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* cerefox-metadata — Supabase Edge Function
|
|
@@ -54,9 +55,9 @@ Deno.serve(async (req: Request): Promise<Response> => {
|
|
|
54
55
|
const supabaseKey = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!;
|
|
55
56
|
const supabase = createClient(supabaseUrl, supabaseKey);
|
|
56
57
|
|
|
57
|
-
// Configurable requestor
|
|
58
|
-
const identityField = "
|
|
59
|
-
const identityValue = body
|
|
58
|
+
// Configurable caller-identity enforcement: `author`, or `requestor` as the pre-1.13.2 alias (#244)
|
|
59
|
+
const identityField = "author";
|
|
60
|
+
const identityValue = callerIdentity(body as Record<string, unknown>);
|
|
60
61
|
const { data: reqConfig } = await supabase.rpc("cerefox_get_config", { p_key: "require_requestor_identity" });
|
|
61
62
|
if (reqConfig === "true") {
|
|
62
63
|
if (!identityValue || (typeof identityValue === "string" && identityValue.trim() === "")) {
|
|
@@ -89,7 +90,7 @@ Deno.serve(async (req: Request): Promise<Response> => {
|
|
|
89
90
|
Promise.resolve(supabase.rpc("cerefox_log_usage", {
|
|
90
91
|
p_operation: "list_metadata_keys",
|
|
91
92
|
p_access_path: "edge-function",
|
|
92
|
-
p_requestor:
|
|
93
|
+
p_requestor: identityValue ?? null,
|
|
93
94
|
p_result_count: (data ?? []).length,
|
|
94
95
|
})).catch(() => {});
|
|
95
96
|
|