@pyxmate/memory 1.17.16 → 1.17.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-contract.d.ts +2 -1
- package/dist/agent-contract.mjs +3 -1
- package/dist/chunk-3OLH3HYR.mjs +1404 -0
- package/dist/{chunk-FQW6BQ2N.mjs → chunk-EZERG25I.mjs} +2 -0
- package/dist/{chunk-MDFUZ3V2.mjs → chunk-H6ZLMPZH.mjs} +232 -41
- package/dist/{chunk-ZVI7DCB4.mjs → chunk-T35ZOOES.mjs} +5 -3
- package/dist/cli/pyx-mem.mjs +3 -2
- package/dist/dashboard.d.ts +3 -1
- package/dist/dashboard.mjs +3 -3
- package/dist/data-plane-contract-fDvTm9bF.d.ts +245 -0
- package/dist/data-plane-contract.d.ts +1 -235
- package/dist/data-plane-contract.mjs +15 -1255
- package/dist/index.d.ts +101 -28
- package/dist/index.mjs +13 -3
- package/dist/react.mjs +3 -3
- package/package.json +1 -1
- package/dist/chunk-34MTVIYK.mjs +0 -109
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// src/contract/index.ts
|
|
2
2
|
var PYX_MEMORY_INSTRUCTIONS = `Use pyx-memory for durable memory across sessions, proactively \u2014 do not wait to be told. SEARCH before assuming: at the start of a task, and before proposing an approach or re-deriving a past decision, search memory for the project and topic; resolve relative times ("last year", "\uB450 \uB2EC \uC804") to an absolute ISO-8601 anchorTime so results rank by proximity to that time. STORE when durable state settles \u2014 usually before your final response or handoff; if nothing durable changed, store nothing. Otherwise capture a fact (correction, bug fix + root cause, design/architecture decision + reasoning, integration/API/auth/endpoint detail, gotcha, preference, "remember this"), a reusable process once stable, a milestone snapshot (type episodic, not a running log), or a changed decision (store the new state with eventTime, never a silent overwrite, so lineage traces it) \u2014 concise facts not deliberation, each with topic and project. Pass eventTime (ISO-8601, when the fact took effect) for anything that can change or go stale; recency ordering, "as of" queries, and stale-vs-current resolution all key off it. After a memory informs your work, call reinforce so it stays in the quick/medium tiers (idle never revives it). When the user corrects you, call record_correction; before a task, call fetch_applicable_corrections (pyx never auto-applies them). When content names people, organizations, tools, places, events, or key concepts, pass entities and relationships \u2014 you build the graph, the server does not extract it; a multi-entity store with no connecting edge is refused. Match search effort to need: quick (default, strongest) for routine recall, deep for full/archived history; use lineage to trace how a fact changed. userId/teamId/agentId and callerAccessLevel are attribution filters and sensitivity redaction, not a security isolation boundary \u2014 tenant/namespace isolation is enforced server-side (namespaces + ReBAC grants; see docs/access-control.md).`;
|
|
3
|
+
var PYX_MEMORY_COMPANION_CORE_INSTRUCTIONS = `Use pyx-memory for durable memory across sessions, proactively \u2014 do not wait to be told. SEARCH (search_memories) before assuming: at the start of a task, and before re-deriving a past decision, search memory for the project and topic; resolve relative times ("last year", "\uB450 \uB2EC \uC804") to an absolute ISO-8601 anchorTime so results rank by proximity to that time. Match search effort to need: quick (default, strongest) for routine recall, deep for full/archived history. STORE (store_memory) when durable state settles \u2014 usually before your final response; if nothing durable changed, store nothing. Capture concise facts, not deliberation \u2014 a correction, bug fix + root cause, decision + reasoning, integration detail, gotcha, preference, or an explicit "remember this" \u2014 each with topic and project. Pass eventTime (ISO-8601, when the fact took effect) for anything that can change or go stale; recency ordering and stale-vs-current resolution key off it. When content names people, organizations, tools, places, events, or key concepts, pass entities and relationships \u2014 you build the graph; a multi-entity store with no connecting edge is refused. Recalled memories reflect what was true when written \u2014 verify named files, flags, and versions before acting.`;
|
|
3
4
|
var PERSISTENT_MEMORY_SECTION = [
|
|
4
5
|
"## Persistent Memory",
|
|
5
6
|
"",
|
|
@@ -159,6 +160,7 @@ function buildAgentSnippet() {
|
|
|
159
160
|
|
|
160
161
|
export {
|
|
161
162
|
PYX_MEMORY_INSTRUCTIONS,
|
|
163
|
+
PYX_MEMORY_COMPANION_CORE_INSTRUCTIONS,
|
|
162
164
|
PERSISTENT_MEMORY_SECTION,
|
|
163
165
|
buildDesignGuide,
|
|
164
166
|
AGENT_TARGETS,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
assertGraphExtractionPayload,
|
|
3
3
|
mergeExtractedEntities
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-3OLH3HYR.mjs";
|
|
5
5
|
|
|
6
6
|
// ../shared/src/constants/defaults.ts
|
|
7
7
|
var DEFAULTS = {
|
|
@@ -11,6 +11,21 @@ var DEFAULTS = {
|
|
|
11
11
|
};
|
|
12
12
|
var TAXONOMY_MAX_CATEGORIES = 10;
|
|
13
13
|
|
|
14
|
+
// ../shared/src/document-source.ts
|
|
15
|
+
import { createHash } from "crypto";
|
|
16
|
+
function documentSource(prefix, documentKey) {
|
|
17
|
+
return `${prefix}:${createHash("sha256").update(documentKey).digest("hex")}`;
|
|
18
|
+
}
|
|
19
|
+
function documentGraphSource(documentKey) {
|
|
20
|
+
return documentSource("document-graph", documentKey);
|
|
21
|
+
}
|
|
22
|
+
function documentContentSource(documentKey) {
|
|
23
|
+
return documentSource("document-content", documentKey);
|
|
24
|
+
}
|
|
25
|
+
function documentImageSource(documentKey) {
|
|
26
|
+
return documentSource("document-image", documentKey);
|
|
27
|
+
}
|
|
28
|
+
|
|
14
29
|
// ../shared/src/mcp/search-response.ts
|
|
15
30
|
var STRIPPED_ENTRY_FIELDS = /* @__PURE__ */ new Set(["contentHash", "embedding", "tenantId", "userId", "teamId"]);
|
|
16
31
|
function isRecord(value) {
|
|
@@ -228,12 +243,54 @@ var DisabledMemory = class {
|
|
|
228
243
|
};
|
|
229
244
|
|
|
230
245
|
// ../client/src/memory-client.ts
|
|
246
|
+
function isRecord3(value) {
|
|
247
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
248
|
+
}
|
|
249
|
+
function normalizeRetryAfter(retryAfter, retryAfterSeconds) {
|
|
250
|
+
const exact = typeof retryAfter === "string" ? retryAfter.trim() : "";
|
|
251
|
+
if (exact) {
|
|
252
|
+
const parsed = /^\d+$/.test(exact) ? Number(exact) : Math.ceil((Date.parse(exact) - Date.now()) / 1e3);
|
|
253
|
+
return {
|
|
254
|
+
retryAfter: exact,
|
|
255
|
+
...Number.isSafeInteger(parsed) && parsed >= 0 ? { retryAfterSeconds: parsed } : {}
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
return Number.isSafeInteger(retryAfterSeconds) && retryAfterSeconds >= 0 ? { retryAfterSeconds } : {};
|
|
259
|
+
}
|
|
260
|
+
function structuralHttpStatus(error) {
|
|
261
|
+
if (!isRecord3(error) || typeof error.status !== "number") return void 0;
|
|
262
|
+
return Number.isSafeInteger(error.status) && error.status >= 400 && error.status < 600 ? error.status : void 0;
|
|
263
|
+
}
|
|
264
|
+
function isFileIngestResult(value) {
|
|
265
|
+
if (!isRecord3(value)) return false;
|
|
266
|
+
if (typeof value.filename !== "string" || typeof value.fileType !== "string" || typeof value.chunks !== "number" || !Number.isSafeInteger(value.chunks) || value.chunks < 0 || !Array.isArray(value.entryIds) || !value.entryIds.every((id) => typeof id === "string") || typeof value.totalCharacters !== "number" || !Number.isSafeInteger(value.totalCharacters) || value.totalCharacters < 0) {
|
|
267
|
+
return false;
|
|
268
|
+
}
|
|
269
|
+
if (value.graphAnchorEntryId !== void 0 && typeof value.graphAnchorEntryId !== "string") {
|
|
270
|
+
return false;
|
|
271
|
+
}
|
|
272
|
+
if (value.enrichment !== void 0 && !isRecord3(value.enrichment)) return false;
|
|
273
|
+
return true;
|
|
274
|
+
}
|
|
275
|
+
function hasEnrichmentCallback(callbacks) {
|
|
276
|
+
return Boolean(callbacks?.describeImage || callbacks?.extractEntitiesV2);
|
|
277
|
+
}
|
|
231
278
|
var MemoryServerError = class extends Error {
|
|
232
279
|
status;
|
|
233
|
-
|
|
280
|
+
/** Stable machine-readable discriminator returned by the memory server. */
|
|
281
|
+
code;
|
|
282
|
+
/** Exact HTTP Retry-After value returned by the memory server. */
|
|
283
|
+
retryAfter;
|
|
284
|
+
/** Retry delay normalized to seconds when Retry-After is parseable. */
|
|
285
|
+
retryAfterSeconds;
|
|
286
|
+
constructor(message, status, code, retryAfter) {
|
|
234
287
|
super(message);
|
|
235
288
|
this.name = "MemoryServerError";
|
|
236
289
|
this.status = status;
|
|
290
|
+
this.code = code;
|
|
291
|
+
const retry = normalizeRetryAfter(retryAfter);
|
|
292
|
+
this.retryAfter = retry.retryAfter;
|
|
293
|
+
this.retryAfterSeconds = retry.retryAfterSeconds;
|
|
237
294
|
}
|
|
238
295
|
/** True when the server returned HTTP 404 (not found). */
|
|
239
296
|
get isNotFound() {
|
|
@@ -273,12 +330,42 @@ var MemoryClient = class {
|
|
|
273
330
|
}
|
|
274
331
|
authorityHeaders(options) {
|
|
275
332
|
if (!options) return {};
|
|
333
|
+
const headers = {};
|
|
276
334
|
const key = options.idempotencyKey;
|
|
277
|
-
if (key
|
|
278
|
-
|
|
279
|
-
|
|
335
|
+
if (key !== void 0) {
|
|
336
|
+
if (typeof key !== "string" || key.length === 0 || !key.isWellFormed() || new TextEncoder().encode(key).byteLength > 256) {
|
|
337
|
+
throw new Error("idempotencyKey must be 1-256 well-formed UTF-8 bytes");
|
|
338
|
+
}
|
|
339
|
+
headers["Idempotency-Key"] = key;
|
|
340
|
+
}
|
|
341
|
+
for (const [headerName, value] of [
|
|
342
|
+
["X-Tenant-Id", options.tenantId],
|
|
343
|
+
["X-Namespace-Id", options.namespaceId]
|
|
344
|
+
]) {
|
|
345
|
+
if (value === void 0) continue;
|
|
346
|
+
const configured = Object.entries(this._authHeaders).find(
|
|
347
|
+
([name]) => name.toLowerCase() === headerName.toLowerCase()
|
|
348
|
+
)?.[1];
|
|
349
|
+
if (configured !== void 0) {
|
|
350
|
+
if (configured !== value) {
|
|
351
|
+
throw new Error(
|
|
352
|
+
`${headerName} conflicts with the MemoryClient default scope; create a client for the requested scope`
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
continue;
|
|
356
|
+
}
|
|
357
|
+
headers[headerName] = value;
|
|
280
358
|
}
|
|
281
|
-
return
|
|
359
|
+
return headers;
|
|
360
|
+
}
|
|
361
|
+
exactReadAuthority(namespaceId, authority) {
|
|
362
|
+
if (namespaceId !== void 0 && authority?.namespaceId !== void 0 && namespaceId !== authority.namespaceId) {
|
|
363
|
+
throw new Error("X-Namespace-Id conflicts with the request authority scope");
|
|
364
|
+
}
|
|
365
|
+
return {
|
|
366
|
+
...authority,
|
|
367
|
+
...namespaceId !== void 0 ? { namespaceId } : {}
|
|
368
|
+
};
|
|
282
369
|
}
|
|
283
370
|
async initialize() {
|
|
284
371
|
const response = await fetch(`${this.baseUrl}/health`, {
|
|
@@ -337,7 +424,7 @@ var MemoryClient = class {
|
|
|
337
424
|
if (params.abstentionThreshold != null)
|
|
338
425
|
searchParams.set("abstentionThreshold", String(params.abstentionThreshold));
|
|
339
426
|
return this.fetchApi(`/api/memory/search?${searchParams}`, {
|
|
340
|
-
headers: this.authorityHeaders(authority)
|
|
427
|
+
headers: this.authorityHeaders(this.exactReadAuthority(params.namespaceId, authority))
|
|
341
428
|
});
|
|
342
429
|
}
|
|
343
430
|
async get(id, authority) {
|
|
@@ -403,7 +490,7 @@ var MemoryClient = class {
|
|
|
403
490
|
}
|
|
404
491
|
const qs = searchParams.toString();
|
|
405
492
|
return this.fetchApi(`/api/memory/entries${qs ? `?${qs}` : ""}`, {
|
|
406
|
-
headers: this.authorityHeaders(authority)
|
|
493
|
+
headers: this.authorityHeaders(this.exactReadAuthority(params.namespaceId, authority))
|
|
407
494
|
});
|
|
408
495
|
}
|
|
409
496
|
// --- File ingest ---
|
|
@@ -426,6 +513,16 @@ var MemoryClient = class {
|
|
|
426
513
|
* method by design (one wire format, one SDK method).
|
|
427
514
|
*/
|
|
428
515
|
async *ingestFileEvents(file, options) {
|
|
516
|
+
if (options?.documentKey !== void 0) {
|
|
517
|
+
if (typeof options.documentKey !== "string" || options.documentKey.trim().length === 0) {
|
|
518
|
+
throw new Error("ingestFileEvents requires a non-empty documentKey when provided");
|
|
519
|
+
}
|
|
520
|
+
if (!options.enrichment?.extractEntitiesV2) {
|
|
521
|
+
throw new Error(
|
|
522
|
+
"ingestFileEvents with documentKey requires enrichment.extractEntitiesV2 so the stable graph replacement can be finalized"
|
|
523
|
+
);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
429
526
|
const controller = new AbortController();
|
|
430
527
|
const relayAbort = () => controller.abort(options?.signal?.reason);
|
|
431
528
|
if (options?.signal?.aborted) relayAbort();
|
|
@@ -435,6 +532,12 @@ var MemoryClient = class {
|
|
|
435
532
|
formData.append("file", file);
|
|
436
533
|
if (options?.description) formData.append("description", options.description);
|
|
437
534
|
if (options?.namespaceId) formData.append("namespaceId", options.namespaceId);
|
|
535
|
+
if (options?.documentKey !== void 0) {
|
|
536
|
+
formData.append("documentKey", options.documentKey);
|
|
537
|
+
}
|
|
538
|
+
if (options?.catalogEntryId !== void 0) {
|
|
539
|
+
formData.append("catalogEntryId", options.catalogEntryId);
|
|
540
|
+
}
|
|
438
541
|
const wantsTextWindows = Boolean(options?.enrichment?.extractEntitiesV2);
|
|
439
542
|
const headers = {
|
|
440
543
|
Accept: "application/x-ndjson",
|
|
@@ -490,6 +593,9 @@ var MemoryClient = class {
|
|
|
490
593
|
const formData = new FormData();
|
|
491
594
|
formData.append("file", file);
|
|
492
595
|
formData.append("documentKey", options.documentKey);
|
|
596
|
+
if (options.catalogEntryId !== void 0) {
|
|
597
|
+
formData.append("catalogEntryId", options.catalogEntryId);
|
|
598
|
+
}
|
|
493
599
|
if (options.namespaceId) formData.append("namespaceId", options.namespaceId);
|
|
494
600
|
const headers = {
|
|
495
601
|
Accept: "application/x-ndjson",
|
|
@@ -519,7 +625,7 @@ var MemoryClient = class {
|
|
|
519
625
|
"graph-only"
|
|
520
626
|
);
|
|
521
627
|
const data = outcome.data;
|
|
522
|
-
if (data?.purpose !== "graph-only" || !Array.isArray(data.entryIds)) {
|
|
628
|
+
if (data?.purpose !== "graph-only" || !Array.isArray(data.entryIds) || data.entryIds.length !== 1 || typeof data.entryIds[0] !== "string" || data.entryIds[0].length === 0 || typeof data.graphAnchorEntryId !== "string" || data.graphAnchorEntryId.length === 0) {
|
|
523
629
|
throw new MemoryServerError(
|
|
524
630
|
"Graph enrichment finalization returned an unexpected result",
|
|
525
631
|
0
|
|
@@ -557,6 +663,17 @@ var MemoryClient = class {
|
|
|
557
663
|
return null;
|
|
558
664
|
}
|
|
559
665
|
const contentType = res.headers.get("content-type") ?? "";
|
|
666
|
+
if (!res.ok) {
|
|
667
|
+
let error;
|
|
668
|
+
try {
|
|
669
|
+
await this.parseApiResponse(res);
|
|
670
|
+
error = new MemoryServerError(`Memory server error: ${res.status}`, res.status);
|
|
671
|
+
} catch (cause) {
|
|
672
|
+
error = cause;
|
|
673
|
+
}
|
|
674
|
+
yield this.ingestErrorEvent(error, "parsing");
|
|
675
|
+
return null;
|
|
676
|
+
}
|
|
560
677
|
if (!contentType.includes("application/x-ndjson")) {
|
|
561
678
|
yield this.ingestErrorEvent(
|
|
562
679
|
new MemoryServerError(
|
|
@@ -602,6 +719,8 @@ var MemoryClient = class {
|
|
|
602
719
|
break;
|
|
603
720
|
}
|
|
604
721
|
if (type === "error") {
|
|
722
|
+
const partialResult = isFileIngestResult(raw.partialResult) ? raw.partialResult : void 0;
|
|
723
|
+
const retry = normalizeRetryAfter(raw.retryAfter, raw.retryAfterSeconds);
|
|
605
724
|
yield {
|
|
606
725
|
schemaVersion: 1,
|
|
607
726
|
type: "error",
|
|
@@ -609,7 +728,9 @@ var MemoryClient = class {
|
|
|
609
728
|
error: typeof raw.error === "string" ? raw.error : "File ingest failed",
|
|
610
729
|
message: typeof raw.message === "string" ? raw.message : void 0,
|
|
611
730
|
code: typeof raw.code === "string" || typeof raw.code === "number" ? raw.code : void 0,
|
|
612
|
-
status: typeof raw.status === "number" ? raw.status : void 0
|
|
731
|
+
status: typeof raw.status === "number" ? raw.status : void 0,
|
|
732
|
+
...retry,
|
|
733
|
+
...partialResult ? { partialResult } : {}
|
|
613
734
|
};
|
|
614
735
|
return null;
|
|
615
736
|
}
|
|
@@ -640,26 +761,48 @@ var MemoryClient = class {
|
|
|
640
761
|
*/
|
|
641
762
|
async *completeIngestFileEvents(file, result, options, signal) {
|
|
642
763
|
try {
|
|
643
|
-
|
|
764
|
+
const callbacks = options?.enrichment;
|
|
765
|
+
if (options?.documentKey !== void 0 && !result.enrichment) {
|
|
766
|
+
throw new MemoryServerError(
|
|
767
|
+
"documentKey-aware ingest response omitted required enrichment preparation; stable graph finalization did not run",
|
|
768
|
+
502
|
|
769
|
+
);
|
|
770
|
+
}
|
|
771
|
+
if (!result.enrichment || !hasEnrichmentCallback(callbacks)) {
|
|
644
772
|
yield { schemaVersion: 1, type: "result", stage: "complete", ...result };
|
|
645
773
|
return;
|
|
646
774
|
}
|
|
647
775
|
const outcome = yield* this.runEnrichmentCallbacks(
|
|
648
776
|
file,
|
|
649
777
|
result.enrichment,
|
|
650
|
-
|
|
778
|
+
callbacks,
|
|
651
779
|
signal,
|
|
652
|
-
"ingest"
|
|
780
|
+
"ingest",
|
|
781
|
+
options?.documentKey !== void 0
|
|
653
782
|
);
|
|
654
783
|
if (outcome.posted) {
|
|
655
784
|
const enrichData = outcome.data;
|
|
785
|
+
if (options?.documentKey !== void 0 && !enrichData.graphAnchorEntryId) {
|
|
786
|
+
throw new MemoryServerError(
|
|
787
|
+
"documentKey-aware enrichment response omitted graphAnchorEntryId; server does not support stable document replacement",
|
|
788
|
+
502
|
|
789
|
+
);
|
|
790
|
+
}
|
|
656
791
|
result.entryIds.push(...enrichData.entryIds);
|
|
792
|
+
if (enrichData.graphAnchorEntryId) {
|
|
793
|
+
result.graphAnchorEntryId = enrichData.graphAnchorEntryId;
|
|
794
|
+
}
|
|
795
|
+
if ((enrichData.relationshipsDropped ?? 0) > 0) {
|
|
796
|
+
result.relationshipsDropped = enrichData.relationshipsDropped;
|
|
797
|
+
result.droppedRelationships = enrichData.droppedRelationships;
|
|
798
|
+
}
|
|
657
799
|
result.chunks += outcome.descriptions.length;
|
|
658
800
|
result.totalCharacters += outcome.descriptions.reduce(
|
|
659
801
|
(sum, d) => sum + d.description.length,
|
|
660
802
|
0
|
|
661
803
|
);
|
|
662
804
|
}
|
|
805
|
+
delete result.enrichment;
|
|
663
806
|
yield { schemaVersion: 1, type: "result", stage: "complete", ...result };
|
|
664
807
|
} catch (err) {
|
|
665
808
|
yield this.ingestErrorEvent(err, "enrichment", result);
|
|
@@ -673,16 +816,24 @@ var MemoryClient = class {
|
|
|
673
816
|
* heartbeat events around each slow step. Throws on any failure; the
|
|
674
817
|
* purpose-specific wrappers translate that into their terminal error.
|
|
675
818
|
*
|
|
676
|
-
*
|
|
677
|
-
*
|
|
678
|
-
*
|
|
679
|
-
*
|
|
819
|
+
* Legacy ingest keeps its existing partial add-on behavior. Graph-only and
|
|
820
|
+
* documentKey-aware full ingest may replace a prior graph only from a
|
|
821
|
+
* complete input set (no truncated text windows or undescribed images). A
|
|
822
|
+
* complete extractor result containing zero entities remains a valid clear.
|
|
680
823
|
*/
|
|
681
824
|
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: image-describe / extract-entities-v2 / no-op are documented decision branches; the fan-out covers the v0.15.0 enrichment phase + heartbeat plumbing.
|
|
682
|
-
async *runEnrichmentCallbacks(file, enrichment, callbacks, signal, purpose) {
|
|
825
|
+
async *runEnrichmentCallbacks(file, enrichment, callbacks, signal, purpose, replaceEmptyGraph = false) {
|
|
683
826
|
const { fileId, token, expiresAt, images } = enrichment;
|
|
684
827
|
const isV2 = "version" in enrichment;
|
|
685
828
|
const textWindows = isV2 ? enrichment.textWindows : [];
|
|
829
|
+
const textWindowsTruncation = isV2 ? enrichment.textWindowsTruncation : void 0;
|
|
830
|
+
const requiresCompleteGraphReplacement = purpose === "graph-only" || replaceEmptyGraph;
|
|
831
|
+
if (requiresCompleteGraphReplacement && textWindowsTruncation?.truncated) {
|
|
832
|
+
throw new MemoryServerError(
|
|
833
|
+
`Graph enrichment input was truncated (${textWindowsTruncation.reason ?? "unknown limit"}); prior graph state was preserved and the pending session remains retryable`,
|
|
834
|
+
422
|
|
835
|
+
);
|
|
836
|
+
}
|
|
686
837
|
const descriptions = [];
|
|
687
838
|
const describeImage = callbacks.describeImage;
|
|
688
839
|
if (describeImage && images.length > 0) {
|
|
@@ -731,6 +882,12 @@ var MemoryClient = class {
|
|
|
731
882
|
};
|
|
732
883
|
}
|
|
733
884
|
}
|
|
885
|
+
if (requiresCompleteGraphReplacement && descriptions.length < images.length) {
|
|
886
|
+
throw new MemoryServerError(
|
|
887
|
+
`Graph enrichment described only ${descriptions.length}/${images.length} images; prior graph state was preserved and the pending session remains retryable`,
|
|
888
|
+
422
|
|
889
|
+
);
|
|
890
|
+
}
|
|
734
891
|
let entities;
|
|
735
892
|
let relationships;
|
|
736
893
|
const imageDescriptionTexts = descriptions.map((d) => d.description);
|
|
@@ -773,7 +930,7 @@ var MemoryClient = class {
|
|
|
773
930
|
}
|
|
774
931
|
const hasGraph = (entities?.length ?? 0) > 0;
|
|
775
932
|
const hasImages = descriptions.length > 0;
|
|
776
|
-
if (!hasGraph && !hasImages && purpose === "ingest") {
|
|
933
|
+
if (!hasGraph && !hasImages && purpose === "ingest" && !replaceEmptyGraph) {
|
|
777
934
|
return { posted: false, data: null, descriptions };
|
|
778
935
|
}
|
|
779
936
|
yield {
|
|
@@ -793,17 +950,14 @@ var MemoryClient = class {
|
|
|
793
950
|
...this._authHeaders
|
|
794
951
|
},
|
|
795
952
|
signal,
|
|
796
|
-
body: JSON.stringify({
|
|
953
|
+
body: JSON.stringify({
|
|
954
|
+
imageDescriptions: descriptions,
|
|
955
|
+
entities: entities ?? [],
|
|
956
|
+
relationships: relationships ?? []
|
|
957
|
+
})
|
|
797
958
|
}),
|
|
798
959
|
signal
|
|
799
960
|
);
|
|
800
|
-
if (!enrichRes.ok) {
|
|
801
|
-
const body = await enrichRes.json().catch(() => ({}));
|
|
802
|
-
throw new MemoryServerError(
|
|
803
|
-
body.error ?? `Enrichment failed: ${enrichRes.status}`,
|
|
804
|
-
enrichRes.status
|
|
805
|
-
);
|
|
806
|
-
}
|
|
807
961
|
const data = await this.parseApiResponse(enrichRes);
|
|
808
962
|
return { posted: true, data, descriptions };
|
|
809
963
|
}
|
|
@@ -869,15 +1023,20 @@ var MemoryClient = class {
|
|
|
869
1023
|
return result;
|
|
870
1024
|
}
|
|
871
1025
|
ingestErrorEvent(error, stage, partialResult) {
|
|
872
|
-
const
|
|
1026
|
+
const structuralError = isRecord3(error) ? error : void 0;
|
|
1027
|
+
const status = error instanceof MemoryServerError ? error.status : error instanceof Error && error.name === "AbortError" ? 499 : structuralHttpStatus(error);
|
|
873
1028
|
const message = error instanceof Error ? error.message : String(error);
|
|
1029
|
+
const code = error instanceof MemoryServerError ? error.code : void 0;
|
|
1030
|
+
const retry = error instanceof MemoryServerError ? { retryAfter: error.retryAfter, retryAfterSeconds: error.retryAfterSeconds } : normalizeRetryAfter(structuralError?.retryAfter, structuralError?.retryAfterSeconds);
|
|
874
1031
|
return {
|
|
875
1032
|
schemaVersion: 1,
|
|
876
1033
|
type: "error",
|
|
877
1034
|
stage,
|
|
878
1035
|
error: message,
|
|
879
1036
|
message,
|
|
880
|
-
...status != null ? { status, code: status } : {},
|
|
1037
|
+
...status != null ? { status, code: code ?? status } : {},
|
|
1038
|
+
...retry.retryAfter !== void 0 ? { retryAfter: retry.retryAfter } : {},
|
|
1039
|
+
...retry.retryAfterSeconds !== void 0 ? { retryAfterSeconds: retry.retryAfterSeconds } : {},
|
|
881
1040
|
...partialResult ? { partialResult } : {}
|
|
882
1041
|
};
|
|
883
1042
|
}
|
|
@@ -885,15 +1044,24 @@ var MemoryClient = class {
|
|
|
885
1044
|
* Get the download URL for an uploaded file.
|
|
886
1045
|
* Returns a URL that serves the original file binary with proper Content-Type.
|
|
887
1046
|
*/
|
|
888
|
-
getFileDownloadUrl(filename) {
|
|
889
|
-
|
|
1047
|
+
getFileDownloadUrl(filename, options) {
|
|
1048
|
+
const url = new URL(
|
|
1049
|
+
`${this.baseUrl}/api/memory/files/download/${encodeURIComponent(filename)}`
|
|
1050
|
+
);
|
|
1051
|
+
if (options?.documentKey !== void 0) {
|
|
1052
|
+
url.searchParams.set("documentKey", options.documentKey);
|
|
1053
|
+
}
|
|
1054
|
+
if (options?.namespaceId !== void 0) {
|
|
1055
|
+
url.searchParams.set("namespaceId", options.namespaceId);
|
|
1056
|
+
}
|
|
1057
|
+
return url.toString();
|
|
890
1058
|
}
|
|
891
1059
|
/**
|
|
892
1060
|
* Download an uploaded file by filename.
|
|
893
1061
|
* Returns the raw Response (caller handles the body — arrayBuffer, blob, stream, etc.).
|
|
894
1062
|
*/
|
|
895
|
-
async downloadFile(filename) {
|
|
896
|
-
const url = this.getFileDownloadUrl(filename);
|
|
1063
|
+
async downloadFile(filename, options) {
|
|
1064
|
+
const url = this.getFileDownloadUrl(filename, options);
|
|
897
1065
|
const res = await fetch(url, { headers: this._authHeaders });
|
|
898
1066
|
if (!res.ok) {
|
|
899
1067
|
throw new MemoryServerError(`File download failed: ${res.status}`, res.status);
|
|
@@ -971,10 +1139,10 @@ var MemoryClient = class {
|
|
|
971
1139
|
async repairGraph() {
|
|
972
1140
|
return this.fetchApi("/api/memory/graph/repair", { method: "POST" });
|
|
973
1141
|
}
|
|
974
|
-
async deleteBySource(source) {
|
|
1142
|
+
async deleteBySource(source, authority) {
|
|
975
1143
|
const result = await this.fetchApi(
|
|
976
1144
|
`/api/memory/source/${this.encodePathSegment(source)}`,
|
|
977
|
-
{ method: "DELETE" }
|
|
1145
|
+
{ method: "DELETE", headers: this.authorityHeaders(authority) }
|
|
978
1146
|
);
|
|
979
1147
|
return result.deleted;
|
|
980
1148
|
}
|
|
@@ -987,14 +1155,20 @@ var MemoryClient = class {
|
|
|
987
1155
|
}
|
|
988
1156
|
);
|
|
989
1157
|
}
|
|
990
|
-
async queryAsOf(asOfDate, filters = {}) {
|
|
1158
|
+
async queryAsOf(asOfDate, filters = {}, authority) {
|
|
1159
|
+
if (filters.cursor !== void 0 && filters.offset !== void 0) {
|
|
1160
|
+
throw new Error("queryAsOf cursor and offset are mutually exclusive");
|
|
1161
|
+
}
|
|
991
1162
|
const params = new URLSearchParams({ asOf: asOfDate });
|
|
992
1163
|
if (filters.type) params.set("type", filters.type);
|
|
993
1164
|
if (filters.agentId) params.set("agentId", filters.agentId);
|
|
994
1165
|
if (filters.source) params.set("source", filters.source);
|
|
995
1166
|
if (filters.limit) params.set("limit", String(filters.limit));
|
|
1167
|
+
if (filters.offset !== void 0) params.set("offset", String(filters.offset));
|
|
1168
|
+
if (filters.cursor !== void 0) params.set("cursor", filters.cursor);
|
|
996
1169
|
const result = await this.fetchApi(
|
|
997
|
-
`/api/memory/query-as-of?${params}
|
|
1170
|
+
`/api/memory/query-as-of?${params}`,
|
|
1171
|
+
{ headers: this.authorityHeaders(authority) }
|
|
998
1172
|
);
|
|
999
1173
|
return result.entries;
|
|
1000
1174
|
}
|
|
@@ -1036,11 +1210,15 @@ var MemoryClient = class {
|
|
|
1036
1210
|
return result.entries;
|
|
1037
1211
|
}
|
|
1038
1212
|
async queryByEventTime(startTime, endTime, filters = {}) {
|
|
1213
|
+
if (filters.cursor !== void 0) {
|
|
1214
|
+
throw new Error("queryByEventTime does not support a createdAt cursor; use offset");
|
|
1215
|
+
}
|
|
1039
1216
|
const params = new URLSearchParams({ startTime, endTime });
|
|
1040
1217
|
if (filters.type) params.set("type", filters.type);
|
|
1041
1218
|
if (filters.agentId) params.set("agentId", filters.agentId);
|
|
1042
1219
|
if (filters.source) params.set("source", filters.source);
|
|
1043
1220
|
if (filters.limit) params.set("limit", String(filters.limit));
|
|
1221
|
+
if (filters.offset !== void 0) params.set("offset", String(filters.offset));
|
|
1044
1222
|
const result = await this.fetchApi(
|
|
1045
1223
|
`/api/memory/query-by-event-time?${params}`
|
|
1046
1224
|
);
|
|
@@ -1164,23 +1342,33 @@ var MemoryClient = class {
|
|
|
1164
1342
|
}
|
|
1165
1343
|
/** Parse and validate a JSON API response, throwing MemoryServerError on any failure. */
|
|
1166
1344
|
async parseApiResponse(res) {
|
|
1345
|
+
const retryAfter = res.headers.get("Retry-After") ?? void 0;
|
|
1167
1346
|
let body;
|
|
1168
1347
|
try {
|
|
1169
1348
|
body = await res.json();
|
|
1170
1349
|
} catch {
|
|
1171
1350
|
throw new MemoryServerError(
|
|
1172
1351
|
`Memory server error: invalid JSON response (${res.status})`,
|
|
1173
|
-
res.status
|
|
1352
|
+
res.status,
|
|
1353
|
+
void 0,
|
|
1354
|
+
retryAfter
|
|
1174
1355
|
);
|
|
1175
1356
|
}
|
|
1176
|
-
if (!body?.success || body.data == null) {
|
|
1357
|
+
if (!res.ok || !body?.success || body.data == null) {
|
|
1177
1358
|
if (res.ok && body?.error === void 0) {
|
|
1178
1359
|
throw new MemoryServerError(
|
|
1179
1360
|
`Memory server returned HTTP ${res.status} without the expected { success, data } API envelope. Check that the base URL points to a pyx-memory instance server; hosted pyx-memory access uses MCP.`,
|
|
1180
|
-
res.status
|
|
1361
|
+
res.status,
|
|
1362
|
+
void 0,
|
|
1363
|
+
retryAfter
|
|
1181
1364
|
);
|
|
1182
1365
|
}
|
|
1183
|
-
throw new MemoryServerError(
|
|
1366
|
+
throw new MemoryServerError(
|
|
1367
|
+
body?.error ?? `Memory server error: ${res.status}`,
|
|
1368
|
+
res.status,
|
|
1369
|
+
body?.code,
|
|
1370
|
+
retryAfter
|
|
1371
|
+
);
|
|
1184
1372
|
}
|
|
1185
1373
|
return body.data;
|
|
1186
1374
|
}
|
|
@@ -1189,6 +1377,9 @@ var MemoryClient = class {
|
|
|
1189
1377
|
export {
|
|
1190
1378
|
DEFAULTS,
|
|
1191
1379
|
TAXONOMY_MAX_CATEGORIES,
|
|
1380
|
+
documentGraphSource,
|
|
1381
|
+
documentContentSource,
|
|
1382
|
+
documentImageSource,
|
|
1192
1383
|
projectSearchResponseForMcp,
|
|
1193
1384
|
secretElevationNoticeFor,
|
|
1194
1385
|
withSecretElevationNotice,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
MemoryClient
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-H6ZLMPZH.mjs";
|
|
4
4
|
|
|
5
5
|
// ../dashboard/src/aggregations/consolidation-analytics.ts
|
|
6
6
|
function analyzeConsolidationLog(entries) {
|
|
@@ -178,8 +178,10 @@ var DashboardClient = class extends MemoryClient {
|
|
|
178
178
|
* arbitrary N-node sample were silently dropped, collapsing dense graphs to
|
|
179
179
|
* a handful of rendered links.
|
|
180
180
|
*/
|
|
181
|
-
async graphSubgraph(edges = 2e3) {
|
|
182
|
-
return this.fetchApi(
|
|
181
|
+
async graphSubgraph(edges = 2e3, nodes = 2e3) {
|
|
182
|
+
return this.fetchApi(
|
|
183
|
+
`/api/memory/graph/subgraph?edges=${edges}&nodes=${nodes}`
|
|
184
|
+
);
|
|
183
185
|
}
|
|
184
186
|
async fetchHealthRaw() {
|
|
185
187
|
return this.fetchApi("/health");
|
package/dist/cli/pyx-mem.mjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
PERSISTENT_MEMORY_SECTION,
|
|
4
4
|
buildDesignGuide
|
|
5
|
-
} from "../chunk-
|
|
5
|
+
} from "../chunk-EZERG25I.mjs";
|
|
6
6
|
|
|
7
7
|
// src/cli/exit-codes.ts
|
|
8
8
|
var EXIT = {
|
|
@@ -597,6 +597,7 @@ function createHttpClient(credentials, fetchImpl = fetch) {
|
|
|
597
597
|
headers.set("Accept", "application/json");
|
|
598
598
|
if (contentType) headers.set("Content-Type", contentType);
|
|
599
599
|
if (scope?.tenantId) headers.set("X-Tenant-Id", scope.tenantId);
|
|
600
|
+
if (scope?.namespaceId) headers.set("X-Namespace-Id", scope.namespaceId);
|
|
600
601
|
if (scope?.userId) headers.set("X-User-Id", scope.userId);
|
|
601
602
|
if (scope?.teamId) headers.set("X-Team-Id", scope.teamId);
|
|
602
603
|
if (scope?.callerAccessLevel) headers.set("X-Caller-Access-Level", scope.callerAccessLevel);
|
|
@@ -835,7 +836,7 @@ function createProxyServer(client, version, uploadLocalFile) {
|
|
|
835
836
|
return server;
|
|
836
837
|
}
|
|
837
838
|
async function runMcpProxyServer(opts) {
|
|
838
|
-
const version = opts.version ?? (true ? "1.17.
|
|
839
|
+
const version = opts.version ?? (true ? "1.17.18" : "0.0.0-dev");
|
|
839
840
|
const read = await opts.readCredentials();
|
|
840
841
|
if (!read.ok) {
|
|
841
842
|
const text = read.result.content.map((c) => c.type === "text" ? c.text : "").join(" ").trim();
|
package/dist/dashboard.d.ts
CHANGED
|
@@ -21,6 +21,8 @@ interface SubgraphResponse {
|
|
|
21
21
|
totalNodes: number;
|
|
22
22
|
totalEdges: number;
|
|
23
23
|
truncated: boolean;
|
|
24
|
+
nodesTruncated?: boolean;
|
|
25
|
+
edgesTruncated?: boolean;
|
|
24
26
|
}
|
|
25
27
|
interface HealthData {
|
|
26
28
|
status: 'ok' | 'degraded' | 'unreachable';
|
|
@@ -191,7 +193,7 @@ declare class DashboardClient extends MemoryClient {
|
|
|
191
193
|
* arbitrary N-node sample were silently dropped, collapsing dense graphs to
|
|
192
194
|
* a handful of rendered links.
|
|
193
195
|
*/
|
|
194
|
-
graphSubgraph(edges?: number): Promise<SubgraphResponse>;
|
|
196
|
+
graphSubgraph(edges?: number, nodes?: number): Promise<SubgraphResponse>;
|
|
195
197
|
fetchHealthRaw(): Promise<RawHealthResponse>;
|
|
196
198
|
}
|
|
197
199
|
|
package/dist/dashboard.mjs
CHANGED
|
@@ -11,9 +11,9 @@ import {
|
|
|
11
11
|
toGraphologyFormat,
|
|
12
12
|
transformGraphData,
|
|
13
13
|
unreachableHealth
|
|
14
|
-
} from "./chunk-
|
|
15
|
-
import "./chunk-
|
|
16
|
-
import "./chunk-
|
|
14
|
+
} from "./chunk-T35ZOOES.mjs";
|
|
15
|
+
import "./chunk-H6ZLMPZH.mjs";
|
|
16
|
+
import "./chunk-3OLH3HYR.mjs";
|
|
17
17
|
export {
|
|
18
18
|
DashboardClient,
|
|
19
19
|
Poller,
|