@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.
Files changed (39) hide show
  1. package/AGENT_GUIDE.md +33 -33
  2. package/AGENT_QUICK_REFERENCE.md +19 -19
  3. package/dist/bin/cerefox.js +124 -133
  4. package/dist/server-assets/_shared/ef-meta/index.ts +3 -3
  5. package/dist/server-assets/_shared/mcp-tools/audit-log.ts +10 -8
  6. package/dist/server-assets/_shared/mcp-tools/delete-document.ts +4 -10
  7. package/dist/server-assets/_shared/mcp-tools/feature-flags.ts +4 -3
  8. package/dist/server-assets/_shared/mcp-tools/get-document.ts +3 -6
  9. package/dist/server-assets/_shared/mcp-tools/get-help-content.ts +4 -4
  10. package/dist/server-assets/_shared/mcp-tools/get-help.ts +3 -6
  11. package/dist/server-assets/_shared/mcp-tools/identity.ts +48 -0
  12. package/dist/server-assets/_shared/mcp-tools/ingest.ts +3 -6
  13. package/dist/server-assets/_shared/mcp-tools/list-metadata-keys.ts +3 -6
  14. package/dist/server-assets/_shared/mcp-tools/list-projects.ts +3 -6
  15. package/dist/server-assets/_shared/mcp-tools/list-versions.ts +3 -6
  16. package/dist/server-assets/_shared/mcp-tools/metadata-search.ts +3 -6
  17. package/dist/server-assets/_shared/mcp-tools/partial-edits.ts +5 -10
  18. package/dist/server-assets/_shared/mcp-tools/relations.ts +11 -12
  19. package/dist/server-assets/_shared/mcp-tools/restore-document.ts +4 -10
  20. package/dist/server-assets/_shared/mcp-tools/search.ts +3 -6
  21. package/dist/server-assets/_shared/mcp-tools/set-document-metadata.ts +4 -10
  22. package/dist/server-assets/_shared/mcp-tools/set-document-projects.ts +3 -6
  23. package/dist/server-assets/db/migrations/0031_review_workflow_toggle.sql +4 -2
  24. package/dist/server-assets/db/rpcs.sql +19 -12
  25. package/dist/server-assets/db/schema.sql +4 -4
  26. package/dist/server-assets/supabase/functions/cerefox-get-audit-log/index.ts +7 -5
  27. package/dist/server-assets/supabase/functions/cerefox-get-document/index.ts +5 -4
  28. package/dist/server-assets/supabase/functions/cerefox-ingest/index.ts +7 -3
  29. package/dist/server-assets/supabase/functions/cerefox-list-projects/index.ts +6 -5
  30. package/dist/server-assets/supabase/functions/cerefox-list-versions/index.ts +5 -4
  31. package/dist/server-assets/supabase/functions/cerefox-mcp/index.ts +6 -6
  32. package/dist/server-assets/supabase/functions/cerefox-metadata/index.ts +5 -4
  33. package/dist/server-assets/supabase/functions/cerefox-metadata-search/index.ts +5 -4
  34. package/dist/server-assets/supabase/functions/cerefox-search/index.ts +7 -4
  35. package/docs/guides/cli.md +20 -20
  36. package/docs/guides/configuration.md +37 -25
  37. package/docs/guides/connect-agents.md +47 -31
  38. package/docs/guides/upgrading.md +11 -0
  39. 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.requestor as string | undefined) ?? defaultRequestor(ctx),
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
- requestor: {
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.requestor as string | undefined) ?? defaultRequestor(ctx),
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
- requestor: {
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.author as string | undefined) ?? "mcp-agent",
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.requestor as string | undefined,
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: { type: "string", description: "Who is creating this relation" },
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.author as string | undefined) ?? "mcp-agent",
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.requestor as string | undefined,
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: { type: "string", description: "Who is removing this relation" },
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.requestor as string | undefined,
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
- requestor: { type: "string", description: "Name of the agent making this request" },
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.requestor as string | undefined,
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
- requestor: { type: "string", description: "Name of the agent making this request" },
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.author as string | undefined) ?? (args.requestor as string | undefined);
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: args.requestor as string | undefined,
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.requestor as string | undefined,
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
- requestor: {
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.author as string | undefined) ?? (args.requestor as string | undefined);
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: args.requestor as string | undefined,
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.author as string | undefined) ?? "mcp-agent";
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, which reads this flag. That RPC
11
- -- lives in rpcs.sql, which `cerefox server deploy` re-applies.
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 and the store's
1399
- -- `review_workflow_enabled` flag, so every transport
1400
- -- behaves the same and a toggle needs no client change.
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). With the
1587
- -- workflow on, an agent write is queued for a person to look at; with it
1588
- -- off, every write lands approved and no surface shows the column. The
1589
- -- fallback FALSE matches the fresh-install seed; migration 0031 seeds TRUE
1590
- -- on stores that predate the flag, so it only applies if the row is gone.
1591
- -- p_review_status is deliberately not consulted six clients used to
1592
- -- compute it and they could not have agreed on a store-level policy.
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.0'::TEXT;
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.0
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, which consults `review_workflow_enabled`
60
- -- (#241): with the workflow off every write lands 'approved' and every
61
- -- surface hides the column; existing values are left as they are.
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 enforcement
65
- const identityField = "requestor";
66
- const identityValue = body[identityField];
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
- if (body.author) params.p_author = body.author;
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: body.requestor ?? null,
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 enforcement
71
- const identityField = "requestor";
72
- const identityValue = body[identityField];
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: body.requestor ?? null,
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, author = "agent", author_type = "agent", expected_content_hash = null, last_write_wins = false } = body;
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 enforcement
310
+ // Configurable caller-identity enforcement: `author`, or `requestor` as the alias (#244)
307
311
  {
308
312
  const identityField = "author";
309
- const identityValue = body[identityField as keyof IngestRequest] as string | undefined;
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 { requestor?: string }
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 enforcement
55
- const identityField = "requestor";
56
- const identityValue = body[identityField];
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: body.requestor ?? null,
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 enforcement
69
- const identityField = "requestor";
70
- const identityValue = body[identityField];
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: body.requestor ?? null,
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 a requestor (reads) or author (writes) parameter.
109
- // When requestor_identity_format is set, the value must match the regex.
110
- const identityParam = toolName === "cerefox_ingest" || toolName === "cerefox_set_document_projects"
111
- ? "author"
112
- : "requestor";
113
- const identityValue = args[identityParam] as string | undefined;
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 enforcement
58
- const identityField = "requestor";
59
- const identityValue = body[identityField];
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: body.requestor ?? null,
93
+ p_requestor: identityValue ?? null,
93
94
  p_result_count: (data ?? []).length,
94
95
  })).catch(() => {});
95
96