@pyxmate/memory 1.17.16 → 1.17.17

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.
@@ -0,0 +1,1404 @@
1
+ // ../shared/src/graph/extraction.ts
2
+ function normalizeGraphLabel(value, fallback) {
3
+ const normalized = value.trim().toUpperCase().replace(/[^A-Z0-9]+/g, "_").replace(/^_+|_+$/g, "");
4
+ return normalized.length > 0 ? normalized : fallback;
5
+ }
6
+ function normalizeNameKey(name) {
7
+ return name.trim().toLowerCase().replace(/\s+/g, " ");
8
+ }
9
+ function requireGraphRecord(value, field) {
10
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
11
+ throw new Error(`${field} must be an object`);
12
+ }
13
+ return value;
14
+ }
15
+ function requireNonemptyGraphString(value, field) {
16
+ if (typeof value !== "string" || value.trim().length === 0) {
17
+ throw new Error(`${field} must be a non-empty string`);
18
+ }
19
+ return value;
20
+ }
21
+ function assertGraphExtractionPayload(value, field = "graph extraction payload") {
22
+ const record = requireGraphRecord(value, field);
23
+ if (!Array.isArray(record.entities)) throw new Error(`${field}.entities must be an array`);
24
+ if (!Array.isArray(record.relationships)) {
25
+ throw new Error(`${field}.relationships must be an array`);
26
+ }
27
+ const entities = record.entities.map((item, index) => {
28
+ const entity = requireGraphRecord(item, `${field}.entities[${index}]`);
29
+ requireNonemptyGraphString(entity.name, `${field}.entities[${index}].name`);
30
+ requireNonemptyGraphString(entity.type, `${field}.entities[${index}].type`);
31
+ if (entity.properties !== void 0 && (!entity.properties || typeof entity.properties !== "object" || Array.isArray(entity.properties))) {
32
+ throw new Error(`${field}.entities[${index}].properties must be an object`);
33
+ }
34
+ return item;
35
+ });
36
+ const entityNames = new Set(entities.map((entity) => normalizeNameKey(entity.name)));
37
+ const relationships = record.relationships.map((item, index) => {
38
+ const relationship = requireGraphRecord(item, `${field}.relationships[${index}]`);
39
+ const source = requireNonemptyGraphString(
40
+ relationship.source,
41
+ `${field}.relationships[${index}].source`
42
+ );
43
+ const target = requireNonemptyGraphString(
44
+ relationship.target,
45
+ `${field}.relationships[${index}].target`
46
+ );
47
+ requireNonemptyGraphString(relationship.type, `${field}.relationships[${index}].type`);
48
+ if (!entityNames.has(normalizeNameKey(source)) || !entityNames.has(normalizeNameKey(target))) {
49
+ throw new Error(
50
+ `${field}.relationships[${index}] endpoints must reference declared entity names`
51
+ );
52
+ }
53
+ if (relationship.properties !== void 0 && (!relationship.properties || typeof relationship.properties !== "object" || Array.isArray(relationship.properties))) {
54
+ throw new Error(`${field}.relationships[${index}].properties must be an object`);
55
+ }
56
+ return item;
57
+ });
58
+ return { entities, relationships };
59
+ }
60
+ function relationshipKey(relationship) {
61
+ return [
62
+ relationship.source.trim().toLowerCase(),
63
+ relationship.target.trim().toLowerCase(),
64
+ normalizeGraphLabel(relationship.type, "RELATED_TO")
65
+ ].join("\0");
66
+ }
67
+ function mergeExtractedEntities(callerEntities, callerRelationships, extracted) {
68
+ const entities = [...callerEntities ?? []];
69
+ const relationships = [...callerRelationships ?? []];
70
+ const nameByLowercase = /* @__PURE__ */ new Map();
71
+ for (const entity of entities) {
72
+ const key = entity.name.toLowerCase();
73
+ if (!nameByLowercase.has(key)) nameByLowercase.set(key, entity.name);
74
+ }
75
+ for (const entity of extracted.entities) {
76
+ const key = entity.name.toLowerCase();
77
+ if (nameByLowercase.has(key)) continue;
78
+ entities.push({ ...entity, type: normalizeGraphLabel(entity.type, "CONCEPT") });
79
+ nameByLowercase.set(key, entity.name);
80
+ }
81
+ for (const relationship of extracted.relations) {
82
+ const source = nameByLowercase.get(relationship.source.toLowerCase());
83
+ const target = nameByLowercase.get(relationship.target.toLowerCase());
84
+ if (source && target) {
85
+ relationships.push({
86
+ ...relationship,
87
+ source,
88
+ target,
89
+ type: normalizeGraphLabel(relationship.type, "RELATED_TO")
90
+ });
91
+ }
92
+ }
93
+ const seenRelationships = /* @__PURE__ */ new Set();
94
+ const dedupedRelationships = [];
95
+ for (const relationship of relationships) {
96
+ const key = relationshipKey(relationship);
97
+ if (seenRelationships.has(key)) continue;
98
+ seenRelationships.add(key);
99
+ dedupedRelationships.push(relationship);
100
+ }
101
+ return { entities, relationships: dedupedRelationships };
102
+ }
103
+
104
+ // ../shared/src/data-plane-contract.ts
105
+ import { createHash } from "crypto";
106
+ var DATA_PLANE_CONTRACT_VERSION = "pyx-data-plane-contract-v1";
107
+ var DATA_PLANE_CATALOG_VERSION = "maas-request-v1";
108
+ var DATA_PLANE_MANIFEST_VERSION = "pyx-data-plane-manifest-v1";
109
+ var DATA_PLANE_SEARCH_EMBEDDING_CONTRACT = "single_precomputed_query_v1";
110
+ var DATA_PLANE_STEP_INPUT_FRAME = "pyx-data-plane-step-input-v1\0";
111
+ var DATA_PLANE_MANIFEST_FRAME = "pyx-data-plane-manifest-v1\0";
112
+ var DATA_PLANE_HTTP_RESPONSE_FRAME = "pyx-data-plane-http-response-v1\0";
113
+ var DATA_PLANE_COMPILER_LIMITS = {
114
+ operationIdBytes: 64,
115
+ identityBytes: 256,
116
+ semanticTextBytes: 128e3,
117
+ canonicalInputBytes: 1048576,
118
+ profileContentBytes: 8192,
119
+ graphEntities: 1e3,
120
+ graphRelationships: 2e3
121
+ };
122
+ var MEMORY_TYPES = ["short-term", "long-term", "working", "episodic", "summary"];
123
+ var ENTRY_STATUSES = ["active", "superseded", "archived"];
124
+ var SENSITIVITY_LEVELS = ["public", "internal", "secret"];
125
+ var PRINCIPAL_KINDS = ["api_key", "user"];
126
+ var TENANT_MODES = ["single", "multi"];
127
+ var ENTITY_TYPES = ["PERSON", "ORGANIZATION", "CONCEPT", "TOOL", "LOCATION", "EVENT"];
128
+ var DATA_PLANE_OPERATION_COVERAGE = {
129
+ "rest:store": {
130
+ support: "journal_multi_step"
131
+ },
132
+ "rest:search": {
133
+ support: "journal_multi_step"
134
+ },
135
+ "rest:query_as_of": { support: "sqlite_atomic_one_step" },
136
+ "rest:query_by_event_time": { support: "sqlite_atomic_one_step" },
137
+ "rest:log": { support: "sqlite_atomic_one_step" },
138
+ "rest:lineage": {
139
+ support: "journal_multi_step"
140
+ },
141
+ "rest:reinforce": {
142
+ support: "journal_multi_step"
143
+ },
144
+ "rest:record_correction": { support: "sqlite_atomic_one_step" },
145
+ "rest:fetch_corrections": { support: "sqlite_atomic_one_step" },
146
+ "rest:synthesis_entity_refresh": {
147
+ support: "not_execution_ready",
148
+ reasonCode: "external_io_execution_not_ready",
149
+ reason: "entity synthesis refresh invokes an external LLM with data-dependent output and is not execution-ready in data-plane contract v1"
150
+ },
151
+ "rest:get_synthesis_entity": { support: "sqlite_atomic_one_step" },
152
+ "rest:list": { support: "sqlite_atomic_one_step" },
153
+ "rest:batch_store": {
154
+ support: "journal_multi_step"
155
+ },
156
+ "rest:get": { support: "sqlite_atomic_one_step" },
157
+ "rest:delete": {
158
+ support: "journal_multi_step"
159
+ },
160
+ "rest:graph_nodes": {
161
+ support: "journal_multi_step"
162
+ },
163
+ "rest:graph_relationships": {
164
+ support: "journal_multi_step"
165
+ },
166
+ "rest:graph_subgraph": {
167
+ support: "journal_multi_step"
168
+ },
169
+ "rest:embedding_map": {
170
+ support: "not_execution_ready",
171
+ reasonCode: "corpus_dependent_execution_not_ready",
172
+ reason: "embedding map reads cache state and may compute PCA over a data-dependent corpus, so it is not execution-ready in data-plane contract v1"
173
+ },
174
+ "mcp:search_memories": {
175
+ support: "journal_multi_step"
176
+ },
177
+ "mcp:store_memory": {
178
+ support: "journal_multi_step"
179
+ },
180
+ "mcp:get_memory": { support: "sqlite_atomic_one_step" },
181
+ "mcp:lineage": {
182
+ support: "journal_multi_step"
183
+ },
184
+ "mcp:reinforce": {
185
+ support: "journal_multi_step"
186
+ },
187
+ "mcp:list_memories": { support: "sqlite_atomic_one_step" },
188
+ "mcp:delete_memory": {
189
+ support: "journal_multi_step"
190
+ },
191
+ "mcp:ingest_memory_file": {
192
+ support: "not_execution_ready",
193
+ reasonCode: "external_io_execution_not_ready",
194
+ reason: "ingest_memory_file reads caller-local files and is not execution-ready in data-plane contract v1"
195
+ },
196
+ "mcp:summarize_memory_entity": { support: "sqlite_atomic_one_step" },
197
+ "mcp:get_taxonomy_state": {
198
+ support: "journal_multi_step"
199
+ },
200
+ "mcp:name_cluster": {
201
+ support: "journal_multi_step"
202
+ },
203
+ "mcp:status": {
204
+ support: "not_execution_ready",
205
+ reasonCode: "nonchargeable_operation",
206
+ reason: "status is nonchargeable and must bypass data-plane reservation"
207
+ },
208
+ "mcp:get_user_profile": { support: "sqlite_atomic_one_step" },
209
+ "mcp:upsert_user_profile": { support: "sqlite_atomic_one_step" },
210
+ "mcp:record_correction": { support: "sqlite_atomic_one_step" },
211
+ "mcp:fetch_applicable_corrections": { support: "sqlite_atomic_one_step" },
212
+ "mcp:fetch_due_facts": { support: "sqlite_atomic_one_step" }
213
+ };
214
+ function isPlainObject(value) {
215
+ return typeof value === "object" && value !== null && !Array.isArray(value) && Object.getPrototypeOf(value) === Object.prototype;
216
+ }
217
+ function requireObject(value, field) {
218
+ if (!isPlainObject(value)) throw new Error(`${field} must be a plain object`);
219
+ return value;
220
+ }
221
+ function requireExactKeys(value, allowed, field) {
222
+ const allowedKeys = new Set(allowed);
223
+ for (const key of Object.keys(value)) {
224
+ requireWellFormedUnicode(key, `${field} key`);
225
+ if (!allowedKeys.has(key)) throw new Error(`${field} contains unknown field: ${key}`);
226
+ }
227
+ }
228
+ function requireWellFormedUnicode(value, field) {
229
+ if (!value.isWellFormed()) throw new Error(`${field} contains an unpaired UTF-16 surrogate`);
230
+ }
231
+ function requireString(value, field, options = {}) {
232
+ if (typeof value !== "string") throw new Error(`${field} must be a string`);
233
+ requireWellFormedUnicode(value, field);
234
+ if (options.nonempty !== false && value.length === 0)
235
+ throw new Error(`${field} must not be empty`);
236
+ const maxBytes = options.maxBytes ?? DATA_PLANE_COMPILER_LIMITS.semanticTextBytes;
237
+ if (Buffer.byteLength(value, "utf8") > maxBytes)
238
+ throw new Error(`${field} exceeds ${maxBytes} UTF-8 bytes`);
239
+ return value;
240
+ }
241
+ function optionalString(value, field) {
242
+ return value === void 0 ? void 0 : requireString(value, field);
243
+ }
244
+ function optionalNonemptyString(value, field) {
245
+ if (value === void 0) return void 0;
246
+ const text = requireString(value, field, { nonempty: false });
247
+ return text.length === 0 ? void 0 : text;
248
+ }
249
+ function requireEnum(value, values, field) {
250
+ if (typeof value !== "string" || !values.includes(value)) throw new Error(`${field} is invalid`);
251
+ return value;
252
+ }
253
+ function optionalEnum(value, values, field) {
254
+ return value === void 0 ? void 0 : requireEnum(value, values, field);
255
+ }
256
+ function optionalEnumOrEmpty(value, values, field) {
257
+ return value === "" ? void 0 : optionalEnum(value, values, field);
258
+ }
259
+ function normalizeAgentId(value, field, defaultAgentId, emptyMeansNoFilter) {
260
+ if (value === void 0) return defaultAgentId ?? void 0;
261
+ const agentId = requireString(value, field, { nonempty: false });
262
+ if (agentId.length > 0) return agentId;
263
+ return emptyMeansNoFilter ? null : defaultAgentId ?? void 0;
264
+ }
265
+ function requireInteger(value, field, minimum, maximum) {
266
+ if (!Number.isSafeInteger(value) || value < minimum || value > maximum) {
267
+ throw new Error(`${field} must be a safe integer between ${minimum} and ${maximum}`);
268
+ }
269
+ return Object.is(value, -0) ? 0 : value;
270
+ }
271
+ function optionalInteger(value, field, minimum, maximum) {
272
+ return value === void 0 ? void 0 : requireInteger(value, field, minimum, maximum);
273
+ }
274
+ function optionalClampedPositiveInteger(value, field, maximum) {
275
+ if (value === void 0) return void 0;
276
+ return Math.min(requireInteger(value, field, 1, Number.MAX_SAFE_INTEGER), maximum);
277
+ }
278
+ function requireFiniteNumber(value, field) {
279
+ if (typeof value !== "number" || !Number.isFinite(value)) {
280
+ throw new Error(`${field} must be a finite number`);
281
+ }
282
+ return Object.is(value, -0) ? 0 : value;
283
+ }
284
+ function normalizeRestLimit(value, field, originMaximum) {
285
+ const requested = value === void 0 ? 50 : requireFiniteNumber(value, field);
286
+ const gatewayLimit = Math.min(Math.max(requested || 50, 1), 200);
287
+ const originLimit = Number.parseInt(String(gatewayLimit), 10);
288
+ return Math.min(originMaximum, Math.max(1, originLimit));
289
+ }
290
+ function normalizeRestPagination(limitValue, offsetValue) {
291
+ const requestedLimit = limitValue === void 0 ? 50 : requireFiniteNumber(limitValue, "input.limit");
292
+ const requestedOffset = offsetValue === void 0 ? 0 : requireFiniteNumber(offsetValue, "input.offset");
293
+ const gatewayLimit = Math.min(requestedLimit || 50, 200);
294
+ const gatewayOffset = Math.max(requestedOffset || 0, 0);
295
+ const mappedPage = Math.floor(gatewayOffset / gatewayLimit) + 1;
296
+ const originPage = Number.parseInt(String(mappedPage), 10);
297
+ const originLimit = Number.parseInt(String(gatewayLimit), 10);
298
+ if (!Number.isSafeInteger(originPage) || !Number.isSafeInteger(originLimit)) {
299
+ throw new Error("REST pagination cannot be represented as safe origin integers");
300
+ }
301
+ return {
302
+ page: Math.max(1, originPage),
303
+ limit: Math.min(100, Math.max(1, originLimit))
304
+ };
305
+ }
306
+ function requireBoolean(value, field) {
307
+ if (typeof value !== "boolean") throw new Error(`${field} must be a boolean`);
308
+ return value;
309
+ }
310
+ function requireCanonicalIso(value, field) {
311
+ const text = requireString(value, field, { maxBytes: 32 });
312
+ const parsed = Date.parse(text);
313
+ if (!Number.isFinite(parsed) || new Date(parsed).toISOString() !== text) {
314
+ throw new Error(`${field} must be a canonical absolute ISO-8601 timestamp`);
315
+ }
316
+ return text;
317
+ }
318
+ function normalizeIso(value, field) {
319
+ const text = requireString(value, field, { maxBytes: 64 });
320
+ const parsed = Date.parse(text);
321
+ if (!Number.isFinite(parsed)) throw new Error(`${field} must be a valid ISO-8601 timestamp`);
322
+ return new Date(parsed).toISOString();
323
+ }
324
+ function withOptional(target, key, value) {
325
+ if (value !== void 0) target[key] = value;
326
+ }
327
+ function normalizeTemporalFilters(value, prefix, defaultAgentId) {
328
+ const normalized = {
329
+ limit: normalizeRestLimit(value.limit, `${prefix}.limit`, 100)
330
+ };
331
+ withOptional(
332
+ normalized,
333
+ "agentId",
334
+ normalizeAgentId(value.agentId, `${prefix}.agentId`, defaultAgentId, false)
335
+ );
336
+ withOptional(normalized, "type", optionalEnumOrEmpty(value.type, MEMORY_TYPES, `${prefix}.type`));
337
+ return normalized;
338
+ }
339
+ function normalizeQueryAsOf(input, defaultAgentId) {
340
+ const value = requireObject(input, "input");
341
+ requireExactKeys(value, ["asOf", "limit", "type", "agentId"], "input");
342
+ return {
343
+ asOf: normalizeIso(value.asOf, "input.asOf"),
344
+ ...normalizeTemporalFilters(value, "input", defaultAgentId)
345
+ };
346
+ }
347
+ function normalizeQueryByEventTime(input, defaultAgentId) {
348
+ const value = requireObject(input, "input");
349
+ requireExactKeys(value, ["startTime", "endTime", "limit", "type", "agentId"], "input");
350
+ const startTime = normalizeIso(value.startTime, "input.startTime");
351
+ const endTime = normalizeIso(value.endTime, "input.endTime");
352
+ if (endTime < startTime) throw new Error("input.endTime must not be before input.startTime");
353
+ return { startTime, endTime, ...normalizeTemporalFilters(value, "input", defaultAgentId) };
354
+ }
355
+ function normalizeLog(input, defaultAgentId) {
356
+ const value = requireObject(input, "input");
357
+ requireExactKeys(value, ["since", "limit", "type", "agentId"], "input");
358
+ const normalized = {
359
+ ...normalizeTemporalFilters(value, "input", defaultAgentId)
360
+ };
361
+ withOptional(
362
+ normalized,
363
+ "since",
364
+ value.since === void 0 ? void 0 : normalizeIso(value.since, "input.since")
365
+ );
366
+ return normalized;
367
+ }
368
+ function normalizeCorrectionRecord(input, defaultAgentId) {
369
+ const value = requireObject(input, "input");
370
+ const keys = ["whatWasWrong", "whatToDoInstead", "appliesWhen", "project", "taskShape"];
371
+ requireExactKeys(value, keys, "input");
372
+ const normalized = {
373
+ whatWasWrong: requireString(value.whatWasWrong, "input.whatWasWrong"),
374
+ whatToDoInstead: requireString(value.whatToDoInstead, "input.whatToDoInstead"),
375
+ appliesWhen: requireString(value.appliesWhen, "input.appliesWhen")
376
+ };
377
+ withOptional(normalized, "agentId", defaultAgentId ?? void 0);
378
+ withOptional(normalized, "project", optionalNonemptyString(value.project, "input.project"));
379
+ withOptional(normalized, "taskShape", optionalNonemptyString(value.taskShape, "input.taskShape"));
380
+ return normalized;
381
+ }
382
+ function normalizeCorrectionFetch(input, rest) {
383
+ const value = requireObject(input, "input");
384
+ requireExactKeys(value, ["taskShape", "project", "limit"], "input");
385
+ const normalized = {
386
+ taskShape: requireString(value.taskShape, "input.taskShape"),
387
+ limit: rest ? normalizeRestLimit(value.limit, "input.limit", 5) : optionalClampedPositiveInteger(value.limit, "input.limit", 5) ?? 5
388
+ };
389
+ withOptional(normalized, "project", optionalNonemptyString(value.project, "input.project"));
390
+ return normalized;
391
+ }
392
+ function normalizeEntitySynthesisGet(input, mcp) {
393
+ const value = requireObject(input, "input");
394
+ requireExactKeys(value, mcp ? ["entityName", "entityType", "refresh"] : ["name"], "input");
395
+ const nameField = mcp ? "input.entityName" : "input.name";
396
+ const name = requireString(mcp ? value.entityName : value.name, nameField).trim().toLowerCase();
397
+ if (name.length === 0) throw new Error(`${nameField} must not be blank`);
398
+ if (!mcp) return { name };
399
+ const refresh = value.refresh === void 0 ? false : requireBoolean(value.refresh, "input.refresh");
400
+ if (refresh) throw new Error("mcp:summarize_memory_entity refresh=true is not execution-ready");
401
+ if (value.entityType !== void 0)
402
+ requireEnum(value.entityType, ENTITY_TYPES, "input.entityType");
403
+ return { name };
404
+ }
405
+ var LIST_CURSOR_MAX_TOKEN_CHARS = 4096;
406
+ var BASE64URL_TOKEN_RE = /^[A-Za-z0-9_-]+$/;
407
+ var ISO_DATE_TIME_PREFIX_RE = /^\d{4}-\d{2}-\d{2}T/;
408
+ function encodeListCursorToken(entry) {
409
+ const token = Buffer.from(JSON.stringify({ c: entry.createdAt, i: entry.id }), "utf8").toString(
410
+ "base64url"
411
+ );
412
+ if (token.length > LIST_CURSOR_MAX_TOKEN_CHARS) {
413
+ throw new Error("entry key is too large to encode as a cursor");
414
+ }
415
+ parseListCursorToken(token);
416
+ return token;
417
+ }
418
+ function parseListCursorToken(token) {
419
+ if (token.length === 0 || token.length > LIST_CURSOR_MAX_TOKEN_CHARS) {
420
+ throw new Error("cursor token is empty or exceeds the size bound");
421
+ }
422
+ if (!BASE64URL_TOKEN_RE.test(token)) {
423
+ throw new Error("cursor token contains non-base64url characters");
424
+ }
425
+ let decoded;
426
+ try {
427
+ decoded = JSON.parse(Buffer.from(token, "base64url").toString("utf8"));
428
+ } catch {
429
+ throw new Error("cursor token is not base64url-encoded JSON");
430
+ }
431
+ if (typeof decoded !== "object" || decoded === null || Array.isArray(decoded)) {
432
+ throw new Error("cursor token payload must be an object");
433
+ }
434
+ const { c, i } = decoded;
435
+ if (typeof c !== "string" || !ISO_DATE_TIME_PREFIX_RE.test(c) || Number.isNaN(Date.parse(c))) {
436
+ throw new Error("cursor token field `c` must be an ISO-8601 timestamp");
437
+ }
438
+ if (typeof i !== "string" || i.length === 0) {
439
+ throw new Error("cursor token field `i` must be a non-empty entry id");
440
+ }
441
+ return { createdAt: c, id: i };
442
+ }
443
+ function normalizeRestList(input) {
444
+ const value = requireObject(input, "input");
445
+ requireExactKeys(value, ["limit", "offset", "type"], "input");
446
+ const normalized = normalizeRestPagination(
447
+ value.limit,
448
+ value.offset
449
+ );
450
+ withOptional(normalized, "type", optionalEnumOrEmpty(value.type, MEMORY_TYPES, "input.type"));
451
+ return normalized;
452
+ }
453
+ function normalizeGet(input) {
454
+ const value = requireObject(input, "input");
455
+ requireExactKeys(value, ["id"], "input");
456
+ return {
457
+ id: requireString(value.id, "input.id", { maxBytes: DATA_PLANE_COMPILER_LIMITS.identityBytes })
458
+ };
459
+ }
460
+ var CALLER_ENTRY_ID_RESERVED_PREFIXES = ["synthesis:", "user-profile:", "correction:"];
461
+ function optionalCallerEntryId(value, field) {
462
+ if (value === void 0) return void 0;
463
+ const id = requireString(value, field, { maxBytes: DATA_PLANE_COMPILER_LIMITS.identityBytes });
464
+ if (/\p{Cc}/u.test(id)) throw new Error(`${field} must not contain control characters`);
465
+ for (const prefix of CALLER_ENTRY_ID_RESERVED_PREFIXES) {
466
+ if (id.startsWith(prefix)) {
467
+ throw new Error(`${field} must not use the platform-reserved '${prefix}' id prefix`);
468
+ }
469
+ }
470
+ return id;
471
+ }
472
+ function normalizeMcpDelete(input) {
473
+ const value = requireObject(input, "input");
474
+ requireExactKeys(value, ["id", "reason"], "input");
475
+ return {
476
+ id: requireString(value.id, "input.id", {
477
+ maxBytes: DATA_PLANE_COMPILER_LIMITS.identityBytes
478
+ }),
479
+ reason: requireString(value.reason, "input.reason")
480
+ };
481
+ }
482
+ function normalizeMcpListEntries(value, normalized) {
483
+ if (value.since !== void 0) requireString(value.since, "input.since", { nonempty: false });
484
+ withOptional(normalized, "status", optionalEnum(value.status, ENTRY_STATUSES, "input.status"));
485
+ if (value.cursor !== void 0) {
486
+ if (value.page !== void 0) {
487
+ throw new Error("input.cursor and input.page are mutually exclusive");
488
+ }
489
+ if (typeof value.cursor !== "string") throw new Error("input.cursor must be a string");
490
+ parseListCursorToken(value.cursor);
491
+ normalized.cursor = value.cursor;
492
+ } else {
493
+ normalized.page = optionalInteger(value.page, "input.page", 1, Number.MAX_SAFE_INTEGER) ?? 1;
494
+ }
495
+ normalized.limit = optionalClampedPositiveInteger(value.limit, "input.limit", 100) ?? 20;
496
+ withOptional(
497
+ normalized,
498
+ "agentId",
499
+ normalizeAgentId(value.agentId, "input.agentId", null, false)
500
+ );
501
+ }
502
+ function normalizeMcpList(input, defaultAgentId) {
503
+ const value = requireObject(input, "input");
504
+ requireExactKeys(
505
+ value,
506
+ ["mode", "page", "limit", "cursor", "status", "type", "agentId", "since"],
507
+ "input"
508
+ );
509
+ const mode = value.mode === void 0 ? "entries" : requireEnum(value.mode, ["entries", "log"], "input.mode");
510
+ if (mode === "log") {
511
+ if (value.cursor !== void 0) throw new Error("input.cursor is only valid in entries mode");
512
+ if (value.status !== void 0) throw new Error("input.status is only valid in entries mode");
513
+ }
514
+ const normalized = { mode };
515
+ withOptional(normalized, "type", optionalEnum(value.type, MEMORY_TYPES, "input.type"));
516
+ if (mode === "entries") {
517
+ normalizeMcpListEntries(value, normalized);
518
+ } else {
519
+ if (value.page !== void 0)
520
+ requireInteger(value.page, "input.page", 1, Number.MAX_SAFE_INTEGER);
521
+ withOptional(
522
+ normalized,
523
+ "agentId",
524
+ normalizeAgentId(value.agentId, "input.agentId", defaultAgentId, true)
525
+ );
526
+ withOptional(
527
+ normalized,
528
+ "limit",
529
+ optionalClampedPositiveInteger(value.limit, "input.limit", 100)
530
+ );
531
+ withOptional(
532
+ normalized,
533
+ "since",
534
+ value.since === void 0 ? void 0 : normalizeIso(value.since, "input.since")
535
+ );
536
+ }
537
+ return normalized;
538
+ }
539
+ function normalizeProfile(input, write, defaultAgentId) {
540
+ const value = requireObject(input, "input");
541
+ requireExactKeys(value, write ? ["content"] : [], "input");
542
+ const normalized = {};
543
+ if (write) {
544
+ normalized.content = requireString(value.content, "input.content", {
545
+ maxBytes: DATA_PLANE_COMPILER_LIMITS.profileContentBytes
546
+ });
547
+ withOptional(normalized, "agentId", defaultAgentId ?? void 0);
548
+ }
549
+ return normalized;
550
+ }
551
+ function normalizeDue(input, context) {
552
+ const value = requireObject(input, "input");
553
+ requireExactKeys(value, ["windowDays", "from", "limit"], "input");
554
+ const windowDays = requireInteger(value.windowDays, "input.windowDays", 1, 370);
555
+ const from = value.from === void 0 ? context.resolvedAt : normalizeIso(value.from, "input.from");
556
+ const to = new Date(Date.parse(from) + windowDays * 864e5).toISOString();
557
+ const normalized = {
558
+ from,
559
+ to,
560
+ limit: optionalClampedPositiveInteger(value.limit, "input.limit", 100) ?? 20
561
+ };
562
+ return normalized;
563
+ }
564
+ function canonicalValue(value, field) {
565
+ try {
566
+ return JSON.parse(canonicalDataPlaneJson(value));
567
+ } catch (error) {
568
+ throw new Error(`${field} is not canonical JSON data`, { cause: error });
569
+ }
570
+ }
571
+ function normalizeStoreTargets(value) {
572
+ const order = ["sqlite", "vector", "graph"];
573
+ if (value === void 0) return [...order];
574
+ if (!Array.isArray(value) || value.length < 1 || value.length > 3) {
575
+ throw new Error("input.targets must contain 1-3 storage targets");
576
+ }
577
+ const targets = value.map((target) => requireEnum(target, order, "input.targets item"));
578
+ if (new Set(targets).size !== targets.length) throw new Error("input.targets must be unique");
579
+ return order.filter((target) => targets.includes(target));
580
+ }
581
+ function normalizeGraphEntities(value) {
582
+ if (value === void 0) return void 0;
583
+ if (!Array.isArray(value) || value.length > DATA_PLANE_COMPILER_LIMITS.graphEntities) {
584
+ throw new Error(
585
+ `input.entities must be an array of at most ${DATA_PLANE_COMPILER_LIMITS.graphEntities} items`
586
+ );
587
+ }
588
+ return value.map((item, index) => {
589
+ const entity = requireObject(item, `input.entities[${index}]`);
590
+ requireExactKeys(entity, ["name", "type", "properties"], `input.entities[${index}]`);
591
+ const normalized = {
592
+ name: requireString(entity.name, `input.entities[${index}].name`),
593
+ type: normalizeGraphLabel(
594
+ requireString(entity.type, `input.entities[${index}].type`, { maxBytes: 100 }),
595
+ "CONCEPT"
596
+ )
597
+ };
598
+ if (entity.properties !== void 0) {
599
+ normalized.properties = canonicalValue(
600
+ requireObject(entity.properties, `input.entities[${index}].properties`),
601
+ `input.entities[${index}].properties`
602
+ );
603
+ }
604
+ return normalized;
605
+ });
606
+ }
607
+ function normalizeGraphRelationships(value) {
608
+ if (value === void 0) return void 0;
609
+ if (!Array.isArray(value) || value.length > DATA_PLANE_COMPILER_LIMITS.graphRelationships) {
610
+ throw new Error(
611
+ `input.relationships must be an array of at most ${DATA_PLANE_COMPILER_LIMITS.graphRelationships} items`
612
+ );
613
+ }
614
+ return value.map((item, index) => {
615
+ const relationship = requireObject(item, `input.relationships[${index}]`);
616
+ requireExactKeys(
617
+ relationship,
618
+ ["source", "target", "type", "properties"],
619
+ `input.relationships[${index}]`
620
+ );
621
+ const normalized = {
622
+ source: requireString(relationship.source, `input.relationships[${index}].source`),
623
+ target: requireString(relationship.target, `input.relationships[${index}].target`),
624
+ type: requireString(relationship.type, `input.relationships[${index}].type`)
625
+ };
626
+ if (relationship.properties !== void 0) {
627
+ normalized.properties = canonicalValue(
628
+ requireObject(relationship.properties, `input.relationships[${index}].properties`),
629
+ `input.relationships[${index}].properties`
630
+ );
631
+ }
632
+ return normalized;
633
+ });
634
+ }
635
+ var MCP_SERVER_OWNED_METADATA_KEYS = ["source", "topic", "project", "_kind"];
636
+ function normalizeMcpStoreMetadata(value) {
637
+ const serverOwned = {
638
+ source: "agent",
639
+ topic: requireString(value.topic, "input.topic"),
640
+ project: requireString(value.project, "input.project")
641
+ };
642
+ if (value.metadata === void 0) return serverOwned;
643
+ const caller = canonicalValue(
644
+ requireObject(value.metadata, "input.metadata"),
645
+ "input.metadata"
646
+ );
647
+ for (const key of MCP_SERVER_OWNED_METADATA_KEYS) {
648
+ if (key in caller) {
649
+ throw new Error(`input.metadata.${key} is server-owned and cannot be caller-supplied`);
650
+ }
651
+ }
652
+ return { ...caller, ...serverOwned };
653
+ }
654
+ function mergeCanonicalGraphInput(entities, relationships, triples) {
655
+ const uniqueEntities = /* @__PURE__ */ new Map();
656
+ const addEntity = (entity) => {
657
+ const key = `${normalizeNameKey(entity.name)}\0${normalizeGraphLabel(entity.type, "CONCEPT")}`;
658
+ if (!uniqueEntities.has(key)) uniqueEntities.set(key, entity);
659
+ };
660
+ const uniqueRelationships = /* @__PURE__ */ new Map();
661
+ const addRelationship = (relationship) => {
662
+ const key = [
663
+ normalizeNameKey(relationship.source),
664
+ normalizeNameKey(relationship.target),
665
+ normalizeGraphLabel(relationship.type, "RELATED_TO")
666
+ ].join("\0");
667
+ if (!uniqueRelationships.has(key)) uniqueRelationships.set(key, relationship);
668
+ };
669
+ for (const entity of entities) addEntity(entity);
670
+ for (const relationship of relationships) addRelationship(relationship);
671
+ for (const triple of triples) {
672
+ addEntity(triple.subject);
673
+ addEntity(triple.object);
674
+ addRelationship({
675
+ source: triple.subject.name,
676
+ target: triple.object.name,
677
+ type: normalizeGraphLabel(triple.relation, "RELATED_TO")
678
+ });
679
+ }
680
+ return {
681
+ entities: [...uniqueEntities.values()],
682
+ relationships: [...uniqueRelationships.values()]
683
+ };
684
+ }
685
+ function canonicalizeStorePayload(input, resolvedAt, mcp, compileAdmission) {
686
+ const value = requireObject(input, "input");
687
+ const common = [
688
+ "id",
689
+ "content",
690
+ "type",
691
+ "targets",
692
+ "source",
693
+ "importance",
694
+ "eventTime",
695
+ "agentId",
696
+ "namespaceId",
697
+ "sessionId",
698
+ "parentId",
699
+ "entities",
700
+ "relationships",
701
+ "extractEntities"
702
+ ];
703
+ requireExactKeys(
704
+ value,
705
+ mcp ? [...common, "topic", "project", "metadata", "triples", "entitiesOnly"] : [...common, "metadata"],
706
+ "input"
707
+ );
708
+ compileAdmission?.(value);
709
+ const normalized = {
710
+ content: requireString(value.content, "input.content"),
711
+ type: optionalEnum(value.type, MEMORY_TYPES, "input.type") ?? "long-term",
712
+ targets: normalizeStoreTargets(value.targets),
713
+ metadata: mcp ? normalizeMcpStoreMetadata(value) : canonicalValue(value.metadata ?? {}, "input.metadata"),
714
+ createdAt: resolvedAt,
715
+ ingestTime: resolvedAt,
716
+ extractEntities: false
717
+ };
718
+ withOptional(normalized, "id", optionalCallerEntryId(value.id, "input.id"));
719
+ withOptional(normalized, "source", optionalNonemptyString(value.source, "input.source"));
720
+ withOptional(normalized, "agentId", optionalNonemptyString(value.agentId, "input.agentId"));
721
+ withOptional(normalized, "sessionId", optionalNonemptyString(value.sessionId, "input.sessionId"));
722
+ withOptional(normalized, "parentId", optionalNonemptyString(value.parentId, "input.parentId"));
723
+ withOptional(
724
+ normalized,
725
+ "eventTime",
726
+ value.eventTime === void 0 ? void 0 : normalizeIso(value.eventTime, "input.eventTime")
727
+ );
728
+ if (value.importance !== void 0) {
729
+ const importance = requireFiniteNumber(value.importance, "input.importance");
730
+ if (importance < 0 || importance > 10) throw new Error("input.importance must be 0-10");
731
+ normalized.importance = importance;
732
+ }
733
+ const entities = normalizeGraphEntities(value.entities) ?? [];
734
+ const relationships = normalizeGraphRelationships(value.relationships) ?? [];
735
+ const triples = [];
736
+ if (mcp && value.triples !== void 0) {
737
+ if (!Array.isArray(value.triples) || value.triples.length > 256) {
738
+ throw new Error("input.triples must be an array of at most 256 items");
739
+ }
740
+ for (const [index, item] of value.triples.entries()) {
741
+ const triple = requireObject(item, `input.triples[${index}]`);
742
+ requireExactKeys(triple, ["subject", "relation", "object"], `input.triples[${index}]`);
743
+ const tripleEntities = normalizeGraphEntities([triple.subject, triple.object]);
744
+ const subject = tripleEntities?.[0];
745
+ const object = tripleEntities?.[1];
746
+ if (!subject || !object) throw new Error(`input.triples[${index}] has invalid endpoints`);
747
+ triples.push({
748
+ subject,
749
+ relation: requireString(triple.relation, `input.triples[${index}].relation`),
750
+ object
751
+ });
752
+ }
753
+ }
754
+ const merged = mergeCanonicalGraphInput(entities, relationships, triples);
755
+ const graphTargeted = normalized.targets.includes(
756
+ "graph"
757
+ );
758
+ const entityNames = new Set(merged.entities.map((entity) => normalizeNameKey(entity.name)));
759
+ const hasResolvableRelationship = merged.relationships.some((relationship) => {
760
+ const source = normalizeNameKey(relationship.source);
761
+ const target = normalizeNameKey(relationship.target);
762
+ return source !== target && entityNames.has(source) && entityNames.has(target);
763
+ });
764
+ const entitiesOnly = mcp && value.entitiesOnly !== void 0 ? requireBoolean(value.entitiesOnly, "input.entitiesOnly") : false;
765
+ if (mcp && graphTargeted && merged.entities.length >= 2 && !hasResolvableRelationship && !entitiesOnly) {
766
+ throw new Error(
767
+ "GRAPH_RELATIONSHIPS_REQUIRED: graph stores with two or more entities require a relationship that connects declared entity names, or entitiesOnly=true"
768
+ );
769
+ }
770
+ if (merged.entities.length > 0) normalized.entities = merged.entities;
771
+ if (merged.relationships.length > 0) normalized.relationships = merged.relationships;
772
+ return normalized;
773
+ }
774
+ function normalizeStore(input, context, mcp) {
775
+ return canonicalizeStorePayload(input, context.resolvedAt, mcp, (value) => {
776
+ if (value.namespaceId !== void 0 && value.namespaceId !== context.scope.namespaceId) {
777
+ throw new Error("input.namespaceId does not match trusted context.scope.namespaceId");
778
+ }
779
+ if (value.extractEntities === true) {
780
+ throw new Error(
781
+ "input.extractEntities=true requires external I/O and is not execution-ready"
782
+ );
783
+ }
784
+ });
785
+ }
786
+ function normalizeBatchStore(input, context) {
787
+ const value = requireObject(input, "input");
788
+ requireExactKeys(value, ["entries"], "input");
789
+ if (!Array.isArray(value.entries) || value.entries.length < 1 || value.entries.length > 100) {
790
+ throw new Error("input.entries must contain 1-100 entries");
791
+ }
792
+ const entries = value.entries.map((entry, batchIndex) => ({
793
+ ...normalizeStore(entry, context, false),
794
+ batchIndex
795
+ }));
796
+ const callerIds = /* @__PURE__ */ new Set();
797
+ for (const entry of entries) {
798
+ if (typeof entry.id !== "string") continue;
799
+ if (callerIds.has(entry.id)) {
800
+ throw new Error(`input.entries contains duplicate entry id: ${entry.id}`);
801
+ }
802
+ callerIds.add(entry.id);
803
+ }
804
+ return { entries };
805
+ }
806
+ function normalizeSearch(input, mcp) {
807
+ const value = requireObject(input, "input");
808
+ const keys = [
809
+ "query",
810
+ "limit",
811
+ "strategy",
812
+ "effort",
813
+ "type",
814
+ "agentId",
815
+ "abstentionThreshold",
816
+ "eventTimeStart",
817
+ "eventTimeEnd",
818
+ "asOf",
819
+ "anchorTime",
820
+ "enumerationConcept",
821
+ "enableRerank"
822
+ ];
823
+ requireExactKeys(value, keys, "input");
824
+ if (value.enumerationConcept !== void 0) {
825
+ throw new Error(
826
+ "input.enumerationConcept requires additional embeddings and is not execution-ready"
827
+ );
828
+ }
829
+ const normalized = {
830
+ query: requireString(value.query, "input.query"),
831
+ limit: mcp ? optionalClampedPositiveInteger(value.limit, "input.limit", 100) ?? 10 : normalizeRestLimit(value.limit, "input.limit", 100),
832
+ strategy: optionalEnum(value.strategy, ["naive", "graph", "hybrid"], "input.strategy") ?? "hybrid"
833
+ };
834
+ withOptional(
835
+ normalized,
836
+ "effort",
837
+ optionalEnum(value.effort, ["quick", "medium", "deep"], "input.effort")
838
+ );
839
+ withOptional(normalized, "type", optionalEnum(value.type, MEMORY_TYPES, "input.type"));
840
+ withOptional(normalized, "agentId", optionalNonemptyString(value.agentId, "input.agentId"));
841
+ for (const field of ["asOf", "anchorTime"]) {
842
+ if (value[field] !== void 0)
843
+ normalized[field] = normalizeIso(value[field], `input.${field}`);
844
+ }
845
+ if (value.eventTimeStart === void 0 !== (value.eventTimeEnd === void 0)) {
846
+ throw new Error("input.eventTimeStart and input.eventTimeEnd must be paired");
847
+ }
848
+ if (value.eventTimeStart !== void 0 && value.eventTimeEnd !== void 0) {
849
+ normalized.eventTimeRange = [
850
+ normalizeIso(value.eventTimeStart, "input.eventTimeStart"),
851
+ normalizeIso(value.eventTimeEnd, "input.eventTimeEnd")
852
+ ];
853
+ }
854
+ if (value.abstentionThreshold !== void 0) {
855
+ const threshold = requireFiniteNumber(value.abstentionThreshold, "input.abstentionThreshold");
856
+ if (threshold < 0 || threshold > 1) throw new Error("input.abstentionThreshold must be 0-1");
857
+ normalized.abstentionThreshold = threshold;
858
+ }
859
+ if (value.enableRerank !== void 0) {
860
+ normalized.enableRerank = requireBoolean(value.enableRerank, "input.enableRerank");
861
+ }
862
+ return normalized;
863
+ }
864
+ function normalizeLineage(input, mcp) {
865
+ const value = requireObject(input, "input");
866
+ requireExactKeys(
867
+ value,
868
+ [
869
+ "subject",
870
+ "relation",
871
+ "entryId",
872
+ "asOf",
873
+ "beforeValue",
874
+ "eventTimeStart",
875
+ "eventTimeEnd",
876
+ "limit"
877
+ ],
878
+ "input"
879
+ );
880
+ if (value.subject === void 0 && value.entryId === void 0) {
881
+ throw new Error("input.subject or input.entryId is required");
882
+ }
883
+ const normalized = {};
884
+ for (const field of ["subject", "relation", "entryId", "beforeValue"]) {
885
+ withOptional(normalized, field, optionalNonemptyString(value[field], `input.${field}`));
886
+ }
887
+ if (value.asOf !== void 0) normalized.asOf = normalizeIso(value.asOf, "input.asOf");
888
+ if (value.eventTimeStart === void 0 !== (value.eventTimeEnd === void 0)) {
889
+ throw new Error("input.eventTimeStart and input.eventTimeEnd must be paired");
890
+ }
891
+ if (value.eventTimeStart !== void 0 && value.eventTimeEnd !== void 0) {
892
+ normalized.eventTimeRange = [
893
+ normalizeIso(value.eventTimeStart, "input.eventTimeStart"),
894
+ normalizeIso(value.eventTimeEnd, "input.eventTimeEnd")
895
+ ];
896
+ }
897
+ normalized.limit = mcp ? optionalInteger(value.limit, "input.limit", 1, 200) ?? 50 : normalizeRestLimit(value.limit, "input.limit", 200);
898
+ return normalized;
899
+ }
900
+ function normalizeReinforce(input) {
901
+ const value = requireObject(input, "input");
902
+ requireExactKeys(value, ["entryIds", "signal", "at"], "input");
903
+ if (!Array.isArray(value.entryIds) || value.entryIds.length < 1 || value.entryIds.length > 100) {
904
+ throw new Error("input.entryIds must contain 1-100 ids");
905
+ }
906
+ const normalized = {
907
+ entryIds: value.entryIds.map(
908
+ (id, index) => requireString(id, `input.entryIds[${index}]`, {
909
+ maxBytes: DATA_PLANE_COMPILER_LIMITS.identityBytes
910
+ })
911
+ ),
912
+ signal: requireEnum(
913
+ value.signal,
914
+ ["context_included", "cited", "explicit_positive"],
915
+ "input.signal"
916
+ )
917
+ };
918
+ if (value.at !== void 0) {
919
+ normalized.at = typeof value.at === "number" ? requireInteger(value.at, "input.at", 0, Number.MAX_SAFE_INTEGER) : normalizeIso(value.at, "input.at");
920
+ }
921
+ return normalized;
922
+ }
923
+ function normalizeGraphRead(input, operation) {
924
+ const value = requireObject(input, "input");
925
+ if (operation === "rest:graph_nodes") {
926
+ requireExactKeys(value, ["name", "type", "limit"], "input");
927
+ const normalized = {
928
+ limit: optionalClampedPositiveInteger(value.limit, "input.limit", 1e3) ?? 100
929
+ };
930
+ withOptional(normalized, "name", optionalNonemptyString(value.name, "input.name"));
931
+ withOptional(normalized, "type", optionalNonemptyString(value.type, "input.type"));
932
+ return normalized;
933
+ }
934
+ const field = operation === "rest:graph_subgraph" ? "edges" : "limit";
935
+ requireExactKeys(
936
+ value,
937
+ operation === "rest:graph_subgraph" ? ["edges", "nodes"] : [field],
938
+ "input"
939
+ );
940
+ if (operation === "rest:graph_subgraph") {
941
+ const normalized = {
942
+ edges: optionalClampedPositiveInteger(value.edges, "input.edges", 1e4) ?? 2e3
943
+ };
944
+ withOptional(
945
+ normalized,
946
+ "nodes",
947
+ optionalClampedPositiveInteger(value.nodes, "input.nodes", 1e4)
948
+ );
949
+ return normalized;
950
+ }
951
+ return {
952
+ [field]: optionalClampedPositiveInteger(value[field], `input.${field}`, 1e3) ?? 200
953
+ };
954
+ }
955
+ function normalizeNameCluster(input) {
956
+ const value = requireObject(input, "input");
957
+ requireExactKeys(value, ["name", "memberNames", "previousName"], "input");
958
+ const name = requireString(value.name, "input.name").trim();
959
+ if (name.length < 1 || name.length > 40) {
960
+ throw new Error("input.name must contain 1-40 characters after trimming");
961
+ }
962
+ if (!Array.isArray(value.memberNames) || value.memberNames.length > 30) {
963
+ throw new Error("input.memberNames must be an array of at most 30 names");
964
+ }
965
+ const normalized = {
966
+ name,
967
+ memberNames: value.memberNames.map(
968
+ (memberName, index) => requireString(memberName, `input.memberNames[${index}]`)
969
+ )
970
+ };
971
+ withOptional(
972
+ normalized,
973
+ "previousName",
974
+ optionalString(value.previousName, "input.previousName")
975
+ );
976
+ return normalized;
977
+ }
978
+ function normalizeSupportedInput(operation, input, context) {
979
+ const defaultAgentId = context.runtimeAttestation.defaultAgentId;
980
+ switch (operation) {
981
+ case "rest:store":
982
+ return normalizeStore(input, context, false);
983
+ case "mcp:store_memory":
984
+ return normalizeStore(input, context, true);
985
+ case "rest:batch_store":
986
+ return normalizeBatchStore(input, context);
987
+ case "rest:search":
988
+ return normalizeSearch(input, false);
989
+ case "mcp:search_memories":
990
+ return normalizeSearch(input, true);
991
+ case "rest:lineage":
992
+ return normalizeLineage(input, false);
993
+ case "mcp:lineage":
994
+ return normalizeLineage(input, true);
995
+ case "rest:reinforce":
996
+ case "mcp:reinforce":
997
+ return normalizeReinforce(input);
998
+ case "rest:delete":
999
+ return normalizeGet(input);
1000
+ case "mcp:delete_memory":
1001
+ return normalizeMcpDelete(input);
1002
+ case "rest:graph_nodes":
1003
+ case "rest:graph_relationships":
1004
+ case "rest:graph_subgraph":
1005
+ return normalizeGraphRead(input, operation);
1006
+ case "mcp:get_taxonomy_state": {
1007
+ const value = requireObject(input, "input");
1008
+ requireExactKeys(value, [], "input");
1009
+ return {};
1010
+ }
1011
+ case "mcp:name_cluster":
1012
+ return normalizeNameCluster(input);
1013
+ case "rest:query_as_of":
1014
+ return normalizeQueryAsOf(input, defaultAgentId);
1015
+ case "rest:query_by_event_time":
1016
+ return normalizeQueryByEventTime(input, defaultAgentId);
1017
+ case "rest:log":
1018
+ return normalizeLog(input, defaultAgentId);
1019
+ case "rest:record_correction":
1020
+ return normalizeCorrectionRecord(input, defaultAgentId);
1021
+ case "rest:fetch_corrections":
1022
+ return normalizeCorrectionFetch(input, true);
1023
+ case "rest:get_synthesis_entity":
1024
+ return normalizeEntitySynthesisGet(input, false);
1025
+ case "rest:list":
1026
+ return normalizeRestList(input);
1027
+ case "rest:get":
1028
+ return normalizeGet(input);
1029
+ case "mcp:get_memory":
1030
+ return normalizeGet(input);
1031
+ case "mcp:list_memories":
1032
+ return normalizeMcpList(input, defaultAgentId);
1033
+ case "mcp:get_user_profile":
1034
+ return normalizeProfile(input, false, defaultAgentId);
1035
+ case "mcp:upsert_user_profile":
1036
+ return normalizeProfile(input, true, defaultAgentId);
1037
+ case "mcp:record_correction":
1038
+ return normalizeCorrectionRecord(input, defaultAgentId);
1039
+ case "mcp:fetch_applicable_corrections":
1040
+ return normalizeCorrectionFetch(input, false);
1041
+ case "mcp:fetch_due_facts":
1042
+ return normalizeDue(input, context);
1043
+ case "mcp:summarize_memory_entity":
1044
+ return normalizeEntitySynthesisGet(input, true);
1045
+ }
1046
+ }
1047
+ function requireExecutionScope(operation, context) {
1048
+ const sharedFallbackSafe = /* @__PURE__ */ new Set([
1049
+ "rest:query_as_of",
1050
+ "rest:query_by_event_time",
1051
+ "rest:log",
1052
+ "rest:get"
1053
+ ]);
1054
+ if (context.runtimeAttestation.sharedFallback && !sharedFallbackSafe.has(operation)) {
1055
+ throw new Error(`${operation} is not execution-ready on the shared fallback runtime`);
1056
+ }
1057
+ const namespaceRequiredInMulti = /* @__PURE__ */ new Set([
1058
+ "rest:record_correction",
1059
+ "rest:fetch_corrections",
1060
+ "mcp:record_correction",
1061
+ "mcp:fetch_applicable_corrections",
1062
+ "mcp:fetch_due_facts"
1063
+ ]);
1064
+ if (context.runtimeAttestation.tenantMode === "multi" && namespaceRequiredInMulti.has(operation) && context.scope.namespaceId === void 0) {
1065
+ throw new Error(`${operation} requires trusted context.scope.namespaceId in multi-tenant mode`);
1066
+ }
1067
+ if (operation === "mcp:get_user_profile" || operation === "mcp:upsert_user_profile") {
1068
+ if (context.scope.namespaceId === void 0) {
1069
+ throw new Error(`${operation} requires trusted context.scope.namespaceId`);
1070
+ }
1071
+ if (context.scope.userId === void 0) {
1072
+ throw new Error(`${operation} requires trusted context.scope.userId`);
1073
+ }
1074
+ }
1075
+ }
1076
+ function stripAndVerifyRawScope(input, context) {
1077
+ if (!isPlainObject(input)) return input;
1078
+ const value = { ...input };
1079
+ const bindings = {
1080
+ tenantId: context.scope.tenantId,
1081
+ namespaceId: context.scope.namespaceId,
1082
+ userId: context.scope.userId,
1083
+ teamId: context.scope.teamId,
1084
+ callerAccessLevel: context.scope.callerAccessLevel
1085
+ };
1086
+ for (const [field, trusted] of Object.entries(bindings)) {
1087
+ if (value[field] !== void 0 && value[field] !== trusted) {
1088
+ throw new Error(`input.${field} does not match trusted context.scope.${field}`);
1089
+ }
1090
+ delete value[field];
1091
+ }
1092
+ return value;
1093
+ }
1094
+ function normalizeContext(input) {
1095
+ const value = requireObject(input, "context");
1096
+ requireExactKeys(
1097
+ value,
1098
+ [
1099
+ "organizationId",
1100
+ "projectId",
1101
+ "instanceId",
1102
+ "operationId",
1103
+ "principal",
1104
+ "scope",
1105
+ "resolvedAt",
1106
+ "runtimeAttestation"
1107
+ ],
1108
+ "context"
1109
+ );
1110
+ const principal = requireObject(value.principal, "context.principal");
1111
+ requireExactKeys(principal, ["kind", "id"], "context.principal");
1112
+ const scope = requireObject(value.scope, "context.scope");
1113
+ requireExactKeys(
1114
+ scope,
1115
+ ["tenantId", "namespaceId", "userId", "teamId", "callerAccessLevel"],
1116
+ "context.scope"
1117
+ );
1118
+ const runtime = requireObject(value.runtimeAttestation, "context.runtimeAttestation");
1119
+ requireExactKeys(
1120
+ runtime,
1121
+ [
1122
+ "contractVersion",
1123
+ "tenantMode",
1124
+ "defaultAgentId",
1125
+ "effectiveSource",
1126
+ "sharedFallback",
1127
+ "sqliteAtomicOneStep"
1128
+ ],
1129
+ "context.runtimeAttestation"
1130
+ );
1131
+ if (runtime.contractVersion !== DATA_PLANE_CONTRACT_VERSION)
1132
+ throw new Error("context.runtimeAttestation.contractVersion is invalid");
1133
+ if (runtime.sqliteAtomicOneStep !== true)
1134
+ throw new Error("context.runtimeAttestation.sqliteAtomicOneStep must be true");
1135
+ const normalizedScope = {
1136
+ tenantId: requireString(scope.tenantId, "context.scope.tenantId", {
1137
+ maxBytes: DATA_PLANE_COMPILER_LIMITS.identityBytes
1138
+ })
1139
+ };
1140
+ const namespaceId = optionalString(scope.namespaceId, "context.scope.namespaceId");
1141
+ const userId = optionalString(scope.userId, "context.scope.userId");
1142
+ const teamId = optionalString(scope.teamId, "context.scope.teamId");
1143
+ const callerAccessLevel = optionalEnum(
1144
+ scope.callerAccessLevel,
1145
+ SENSITIVITY_LEVELS,
1146
+ "context.scope.callerAccessLevel"
1147
+ );
1148
+ if (namespaceId !== void 0) normalizedScope.namespaceId = namespaceId;
1149
+ if (userId !== void 0) normalizedScope.userId = userId;
1150
+ if (teamId !== void 0) normalizedScope.teamId = teamId;
1151
+ if (callerAccessLevel !== void 0) normalizedScope.callerAccessLevel = callerAccessLevel;
1152
+ const organizationId = requireString(value.organizationId, "context.organizationId", {
1153
+ maxBytes: DATA_PLANE_COMPILER_LIMITS.identityBytes
1154
+ });
1155
+ const projectId = requireString(value.projectId, "context.projectId", {
1156
+ maxBytes: DATA_PLANE_COMPILER_LIMITS.identityBytes
1157
+ });
1158
+ const effectiveSource = requireString(
1159
+ runtime.effectiveSource,
1160
+ "context.runtimeAttestation.effectiveSource"
1161
+ );
1162
+ if (effectiveSource !== `tenant:${organizationId}:${projectId}`) {
1163
+ throw new Error(
1164
+ "context.runtimeAttestation.effectiveSource must match the trusted organization/project source"
1165
+ );
1166
+ }
1167
+ return {
1168
+ organizationId,
1169
+ projectId,
1170
+ instanceId: requireString(value.instanceId, "context.instanceId", {
1171
+ maxBytes: DATA_PLANE_COMPILER_LIMITS.identityBytes
1172
+ }),
1173
+ operationId: requireString(value.operationId, "context.operationId", {
1174
+ maxBytes: DATA_PLANE_COMPILER_LIMITS.operationIdBytes
1175
+ }),
1176
+ principal: {
1177
+ kind: requireEnum(principal.kind, PRINCIPAL_KINDS, "context.principal.kind"),
1178
+ id: requireString(principal.id, "context.principal.id", {
1179
+ maxBytes: DATA_PLANE_COMPILER_LIMITS.identityBytes
1180
+ })
1181
+ },
1182
+ scope: normalizedScope,
1183
+ resolvedAt: requireCanonicalIso(value.resolvedAt, "context.resolvedAt"),
1184
+ runtimeAttestation: {
1185
+ contractVersion: DATA_PLANE_CONTRACT_VERSION,
1186
+ tenantMode: requireEnum(
1187
+ runtime.tenantMode,
1188
+ TENANT_MODES,
1189
+ "context.runtimeAttestation.tenantMode"
1190
+ ),
1191
+ defaultAgentId: runtime.defaultAgentId === null ? null : requireString(runtime.defaultAgentId, "context.runtimeAttestation.defaultAgentId", {
1192
+ maxBytes: DATA_PLANE_COMPILER_LIMITS.identityBytes
1193
+ }),
1194
+ effectiveSource,
1195
+ sharedFallback: requireBoolean(
1196
+ runtime.sharedFallback,
1197
+ "context.runtimeAttestation.sharedFallback"
1198
+ ),
1199
+ sqliteAtomicOneStep: true
1200
+ }
1201
+ };
1202
+ }
1203
+ function normalizeCanonicalValue(value, path) {
1204
+ if (value === null || typeof value === "boolean") return value;
1205
+ if (typeof value === "string") {
1206
+ requireWellFormedUnicode(value, path);
1207
+ return value;
1208
+ }
1209
+ if (typeof value === "bigint") return value.toString(10);
1210
+ if (typeof value === "number") {
1211
+ if (!Number.isSafeInteger(value)) throw new Error(`${path} must be a safe integer`);
1212
+ return Object.is(value, -0) ? 0 : value;
1213
+ }
1214
+ if (Array.isArray(value)) {
1215
+ return Array.from({ length: value.length }, (_, index) => {
1216
+ if (!Object.hasOwn(value, index))
1217
+ throw new Error(`${path}[${index}] must not be a sparse hole`);
1218
+ return normalizeCanonicalValue(value[index], `${path}[${index}]`);
1219
+ });
1220
+ }
1221
+ const record = requireObject(value, path);
1222
+ const normalized = /* @__PURE__ */ Object.create(null);
1223
+ for (const key of Object.keys(record).sort()) {
1224
+ requireWellFormedUnicode(key, `${path} key`);
1225
+ if (record[key] === void 0) throw new Error(`${path}.${key} must not be undefined`);
1226
+ normalized[key] = normalizeCanonicalValue(record[key], `${path}.${key}`);
1227
+ }
1228
+ return normalized;
1229
+ }
1230
+ function canonicalDataPlaneJson(value) {
1231
+ return JSON.stringify(normalizeCanonicalValue(value, "$"));
1232
+ }
1233
+ function framedSha256(frame, canonicalJson, maxBytes) {
1234
+ const payload = Buffer.from(canonicalJson, "utf8");
1235
+ if (payload.byteLength > maxBytes)
1236
+ throw new Error(`canonical payload exceeds ${maxBytes} UTF-8 bytes`);
1237
+ if (payload.byteLength > 4294967295)
1238
+ throw new Error("canonical payload exceeds U32 framing capacity");
1239
+ const length = Buffer.allocUnsafe(4);
1240
+ length.writeUInt32BE(payload.byteLength, 0);
1241
+ return createHash("sha256").update(frame, "utf8").update(length).update(payload).digest("hex");
1242
+ }
1243
+ function hashDataPlaneStepInputV1(value) {
1244
+ const canonicalJson = canonicalDataPlaneJson(value);
1245
+ return {
1246
+ canonicalJson,
1247
+ sha256: framedSha256(
1248
+ DATA_PLANE_STEP_INPUT_FRAME,
1249
+ canonicalJson,
1250
+ DATA_PLANE_COMPILER_LIMITS.canonicalInputBytes
1251
+ )
1252
+ };
1253
+ }
1254
+ function hashDataPlaneManifestV1(value) {
1255
+ const canonicalJson = canonicalDataPlaneJson(value);
1256
+ return { canonicalJson, sha256: framedSha256(DATA_PLANE_MANIFEST_FRAME, canonicalJson, 32768) };
1257
+ }
1258
+ function hashDataPlaneHttpResponseV1(statusCode, contentType, body) {
1259
+ if (!Number.isSafeInteger(statusCode) || statusCode < 100 || statusCode > 599) {
1260
+ throw new Error("statusCode must be an integer from 100 through 599");
1261
+ }
1262
+ if (typeof contentType !== "string" || contentType.length === 0 || Buffer.byteLength(contentType, "utf8") > 256 || !/^[\x20-\x7e]+$/.test(contentType) || /[\r\n]/.test(contentType)) {
1263
+ throw new Error("contentType must be 1-256 visible ASCII bytes without CR or LF");
1264
+ }
1265
+ if (!(body instanceof Uint8Array)) throw new Error("body must be a Uint8Array");
1266
+ const status = Buffer.from(String(statusCode), "ascii");
1267
+ const contentTypeBytes = Buffer.from(contentType, "ascii");
1268
+ const statusLength = Buffer.allocUnsafe(4);
1269
+ statusLength.writeUInt32BE(status.byteLength);
1270
+ const contentTypeLength = Buffer.allocUnsafe(4);
1271
+ contentTypeLength.writeUInt32BE(contentTypeBytes.byteLength);
1272
+ const bodyLength = Buffer.allocUnsafe(8);
1273
+ bodyLength.writeBigUInt64BE(BigInt(body.byteLength));
1274
+ return createHash("sha256").update(DATA_PLANE_HTTP_RESPONSE_FRAME, "utf8").update(statusLength).update(status).update(contentTypeLength).update(contentTypeBytes).update(bodyLength).update(body).digest("hex");
1275
+ }
1276
+ function compileDataPlaneRequest(request) {
1277
+ const outer = requireObject(request, "request");
1278
+ requireExactKeys(outer, ["catalogVersion", "operation", "context", "input"], "request");
1279
+ if (outer.catalogVersion !== DATA_PLANE_CATALOG_VERSION)
1280
+ throw new Error(`unknown data-plane catalog version: ${String(outer.catalogVersion)}`);
1281
+ if (typeof outer.operation !== "string" || !Object.hasOwn(DATA_PLANE_OPERATION_COVERAGE, outer.operation)) {
1282
+ throw new Error(`unknown data-plane catalog operation: ${String(outer.operation)}`);
1283
+ }
1284
+ const operation = outer.operation;
1285
+ const entry = DATA_PLANE_OPERATION_COVERAGE[operation];
1286
+ if (entry.support === "not_execution_ready") {
1287
+ return {
1288
+ kind: "unsupported",
1289
+ catalogVersion: DATA_PLANE_CATALOG_VERSION,
1290
+ operation,
1291
+ reasonCode: entry.reasonCode,
1292
+ reason: entry.reason
1293
+ };
1294
+ }
1295
+ const supportedOperation = operation;
1296
+ const context = normalizeContext(outer.context);
1297
+ requireExecutionScope(supportedOperation, context);
1298
+ const normalizedInput = normalizeSupportedInput(
1299
+ supportedOperation,
1300
+ stripAndVerifyRawScope(outer.input, context),
1301
+ context
1302
+ );
1303
+ const stepInput = {
1304
+ contractVersion: DATA_PLANE_CONTRACT_VERSION,
1305
+ catalogVersion: DATA_PLANE_CATALOG_VERSION,
1306
+ operation: supportedOperation,
1307
+ context,
1308
+ input: normalizedInput
1309
+ };
1310
+ const step = hashDataPlaneStepInputV1(stepInput);
1311
+ const storeEntries = supportedOperation === "rest:batch_store" ? normalizedInput.entries : supportedOperation === "rest:store" || supportedOperation === "mcp:store_memory" ? [normalizedInput] : [];
1312
+ const storeHasTarget = (target) => storeEntries.some(
1313
+ (entry2) => entry2.targets.includes(target)
1314
+ );
1315
+ const multiStepKinds = (() => {
1316
+ switch (supportedOperation) {
1317
+ case "rest:store":
1318
+ case "mcp:store_memory":
1319
+ case "rest:batch_store": {
1320
+ const kinds = [];
1321
+ if (storeHasTarget("vector")) kinds.push("embedding", "vector");
1322
+ if (storeHasTarget("graph")) kinds.push("graph");
1323
+ kinds.push("sqlite");
1324
+ return kinds;
1325
+ }
1326
+ case "rest:search":
1327
+ case "mcp:search_memories":
1328
+ return ["embedding", "sqlite"];
1329
+ case "rest:delete":
1330
+ case "mcp:delete_memory":
1331
+ return ["vector", "graph", "sqlite"];
1332
+ case "rest:lineage":
1333
+ case "mcp:lineage":
1334
+ case "rest:graph_nodes":
1335
+ case "rest:graph_relationships":
1336
+ case "rest:graph_subgraph":
1337
+ case "mcp:get_taxonomy_state":
1338
+ case "mcp:name_cluster":
1339
+ return ["graph", "sqlite"];
1340
+ case "rest:reinforce":
1341
+ case "mcp:reinforce":
1342
+ return ["vector", "sqlite"];
1343
+ default:
1344
+ return ["sqlite"];
1345
+ }
1346
+ })();
1347
+ const executionPlan = {
1348
+ mode: "journal",
1349
+ version: DATA_PLANE_MANIFEST_VERSION,
1350
+ steps: multiStepKinds.map((kind, sequence) => ({
1351
+ sequence,
1352
+ stepKey: multiStepKinds.length === 1 ? supportedOperation : `${supportedOperation}:${kind}`,
1353
+ kind,
1354
+ execution: kind === "sqlite" ? "sqlite_atomic" : "idempotent_adapter",
1355
+ inputSha256: multiStepKinds.length === 1 ? step.sha256 : hashDataPlaneStepInputV1({ ...stepInput, step: { kind, sequence } }).sha256
1356
+ }))
1357
+ };
1358
+ const manifest = hashDataPlaneManifestV1(executionPlan);
1359
+ const embeddingUnicodeScalars = supportedOperation === "rest:store" || supportedOperation === "mcp:store_memory" ? storeHasTarget("vector") ? Array.from(normalizedInput.content).length : 0 : supportedOperation === "rest:batch_store" ? storeEntries.reduce(
1360
+ (total, entry2) => total + (entry2.targets.includes("vector") ? Array.from(entry2.content).length : 0),
1361
+ 0
1362
+ ) : supportedOperation === "rest:search" || supportedOperation === "mcp:search_memories" ? Array.from(normalizedInput.query).length : 0;
1363
+ return {
1364
+ kind: "compiled",
1365
+ contractVersion: DATA_PLANE_CONTRACT_VERSION,
1366
+ catalogVersion: DATA_PLANE_CATALOG_VERSION,
1367
+ operation: supportedOperation,
1368
+ context,
1369
+ normalizedInput,
1370
+ stepInputCanonicalJson: step.canonicalJson,
1371
+ stepInputSha256: step.sha256,
1372
+ executionPlan,
1373
+ manifestCanonicalJson: manifest.canonicalJson,
1374
+ manifestSha256: manifest.sha256,
1375
+ quoteDimensions: {
1376
+ databaseOperations: supportedOperation === "rest:batch_store" ? normalizedInput.entries.length : 1,
1377
+ embeddingUnicodeScalars,
1378
+ embeddingEvents: embeddingUnicodeScalars > 0 ? 1 : 0
1379
+ }
1380
+ };
1381
+ }
1382
+
1383
+ export {
1384
+ normalizeGraphLabel,
1385
+ normalizeNameKey,
1386
+ assertGraphExtractionPayload,
1387
+ mergeExtractedEntities,
1388
+ DATA_PLANE_CONTRACT_VERSION,
1389
+ DATA_PLANE_CATALOG_VERSION,
1390
+ DATA_PLANE_MANIFEST_VERSION,
1391
+ DATA_PLANE_SEARCH_EMBEDDING_CONTRACT,
1392
+ DATA_PLANE_STEP_INPUT_FRAME,
1393
+ DATA_PLANE_MANIFEST_FRAME,
1394
+ DATA_PLANE_HTTP_RESPONSE_FRAME,
1395
+ DATA_PLANE_COMPILER_LIMITS,
1396
+ DATA_PLANE_OPERATION_COVERAGE,
1397
+ encodeListCursorToken,
1398
+ parseListCursorToken,
1399
+ canonicalDataPlaneJson,
1400
+ hashDataPlaneStepInputV1,
1401
+ hashDataPlaneManifestV1,
1402
+ hashDataPlaneHttpResponseV1,
1403
+ compileDataPlaneRequest
1404
+ };