@agentmemory/agentmemory 0.9.23 → 0.9.25
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/AGENTS.md +1 -1
- package/README.md +1 -1
- package/dist/cli.d.mts.map +1 -1
- package/dist/cli.mjs +129 -66
- package/dist/cli.mjs.map +1 -1
- package/dist/{connect-Cf9bmBqO.mjs → connect-bmZ5eqYN.mjs} +17 -56
- package/dist/{connect-Cf9bmBqO.mjs.map → connect-bmZ5eqYN.mjs.map} +1 -1
- package/dist/hooks/notification.mjs +2 -4
- package/dist/hooks/notification.mjs.map +1 -1
- package/dist/hooks/post-commit.mjs +2 -3
- package/dist/hooks/post-commit.mjs.map +1 -1
- package/dist/hooks/post-tool-failure.mjs +2 -4
- package/dist/hooks/post-tool-failure.mjs.map +1 -1
- package/dist/hooks/post-tool-use.mjs +2 -4
- package/dist/hooks/post-tool-use.mjs.map +1 -1
- package/dist/hooks/pre-compact.mjs +2 -4
- package/dist/hooks/pre-compact.mjs.map +1 -1
- package/dist/hooks/pre-tool-use.mjs +2 -2
- package/dist/hooks/pre-tool-use.mjs.map +1 -1
- package/dist/hooks/prompt-submit.mjs +2 -4
- package/dist/hooks/prompt-submit.mjs.map +1 -1
- package/dist/hooks/session-end.mjs +2 -2
- package/dist/hooks/session-start.mjs +2 -4
- package/dist/hooks/session-start.mjs.map +1 -1
- package/dist/hooks/stop.mjs +2 -2
- package/dist/hooks/subagent-start.mjs +2 -4
- package/dist/hooks/subagent-start.mjs.map +1 -1
- package/dist/hooks/subagent-stop.mjs +2 -4
- package/dist/hooks/subagent-stop.mjs.map +1 -1
- package/dist/hooks/task-completed.mjs +2 -4
- package/dist/hooks/task-completed.mjs.map +1 -1
- package/dist/image-refs-C7h9L5wx.mjs +52 -0
- package/dist/image-refs-C7h9L5wx.mjs.map +1 -0
- package/dist/{image-refs-CJS5B9Gq.mjs → image-store-Gpo2mgM9.mjs} +11 -42
- package/dist/image-store-Gpo2mgM9.mjs.map +1 -0
- package/dist/index.mjs +942 -493
- package/dist/index.mjs.map +1 -1
- package/dist/{logger-xlVlvCWX.mjs → logger-yHTcEBAI.mjs} +2 -2
- package/dist/{logger-xlVlvCWX.mjs.map → logger-yHTcEBAI.mjs.map} +1 -1
- package/dist/rolldown-runtime-twds-ZHy.mjs +14 -0
- package/dist/{schema-BkALl7Z_.mjs → schema-Dsr_V2Wp.mjs} +4 -4
- package/dist/schema-Dsr_V2Wp.mjs.map +1 -0
- package/dist/{src-DvS3bhMe.mjs → src-fQOMXeCp.mjs} +937 -483
- package/dist/src-fQOMXeCp.mjs.map +1 -0
- package/dist/{standalone-DHQcPX_g.mjs → standalone-BzfA1zu8.mjs} +6 -10
- package/dist/{standalone-DHQcPX_g.mjs.map → standalone-BzfA1zu8.mjs.map} +1 -1
- package/dist/standalone.mjs +3 -14
- package/dist/standalone.mjs.map +1 -1
- package/dist/{tools-registry-DJizX9Az.mjs → tools-registry-Dzxv9iUu.mjs} +7 -5
- package/dist/tools-registry-Dzxv9iUu.mjs.map +1 -0
- package/dist/version-C3hZKw8n.mjs +6 -0
- package/dist/version-C3hZKw8n.mjs.map +1 -0
- package/dist/viewer/index.html +155 -9
- package/package.json +9 -4
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/plugin.json +1 -1
- package/plugin/scripts/notification.mjs +2 -4
- package/plugin/scripts/notification.mjs.map +1 -1
- package/plugin/scripts/post-commit.mjs +2 -3
- package/plugin/scripts/post-commit.mjs.map +1 -1
- package/plugin/scripts/post-tool-failure.mjs +2 -4
- package/plugin/scripts/post-tool-failure.mjs.map +1 -1
- package/plugin/scripts/post-tool-use.mjs +2 -4
- package/plugin/scripts/post-tool-use.mjs.map +1 -1
- package/plugin/scripts/pre-compact.mjs +2 -4
- package/plugin/scripts/pre-compact.mjs.map +1 -1
- package/plugin/scripts/pre-tool-use.mjs +2 -2
- package/plugin/scripts/pre-tool-use.mjs.map +1 -1
- package/plugin/scripts/prompt-submit.mjs +2 -4
- package/plugin/scripts/prompt-submit.mjs.map +1 -1
- package/plugin/scripts/session-end.mjs +2 -2
- package/plugin/scripts/session-start.mjs +2 -4
- package/plugin/scripts/session-start.mjs.map +1 -1
- package/plugin/scripts/stop.mjs +2 -2
- package/plugin/scripts/subagent-start.mjs +2 -4
- package/plugin/scripts/subagent-start.mjs.map +1 -1
- package/plugin/scripts/subagent-stop.mjs +2 -4
- package/plugin/scripts/subagent-stop.mjs.map +1 -1
- package/plugin/scripts/task-completed.mjs +2 -4
- package/plugin/scripts/task-completed.mjs.map +1 -1
- package/dist/image-refs-CJS5B9Gq.mjs.map +0 -1
- package/dist/image-store-CdE0amb1.mjs +0 -3
- package/dist/schema-BkALl7Z_.mjs.map +0 -1
- package/dist/src-DvS3bhMe.mjs.map +0 -1
- package/dist/tools-registry-DJizX9Az.mjs.map +0 -1
- package/dist/version-BPfyI4Kc.mjs +0 -6
- package/dist/version-BPfyI4Kc.mjs.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -4,6 +4,7 @@ import { TriggerAction, registerWorker } from "iii-sdk";
|
|
|
4
4
|
import { constants, existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
5
5
|
import { basename, dirname, extname, join, resolve, sep } from "node:path";
|
|
6
6
|
import { homedir } from "node:os";
|
|
7
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
7
8
|
import Anthropic from "@anthropic-ai/sdk";
|
|
8
9
|
import { lstat, mkdir, open, readFile, readdir, stat, unlink, utimes, writeFile } from "node:fs/promises";
|
|
9
10
|
import { createHash, createHmac, randomBytes, timingSafeEqual } from "node:crypto";
|
|
@@ -14,23 +15,17 @@ import { lookup } from "node:dns/promises";
|
|
|
14
15
|
import { isIP } from "node:net";
|
|
15
16
|
import { fileURLToPath } from "node:url";
|
|
16
17
|
import { createServer } from "node:http";
|
|
17
|
-
|
|
18
18
|
//#region \0rolldown/runtime.js
|
|
19
19
|
var __defProp = Object.defineProperty;
|
|
20
20
|
var __exportAll = (all, no_symbols) => {
|
|
21
21
|
let target = {};
|
|
22
|
-
for (var name in all) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
if (!no_symbols) {
|
|
29
|
-
__defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
30
|
-
}
|
|
22
|
+
for (var name in all) __defProp(target, name, {
|
|
23
|
+
get: all[name],
|
|
24
|
+
enumerable: true
|
|
25
|
+
});
|
|
26
|
+
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
31
27
|
return target;
|
|
32
28
|
};
|
|
33
|
-
|
|
34
29
|
//#endregion
|
|
35
30
|
//#region src/config.ts
|
|
36
31
|
function safeParseInt(value, fallback) {
|
|
@@ -232,6 +227,10 @@ function loadSnapshotConfig() {
|
|
|
232
227
|
function isGraphExtractionEnabled() {
|
|
233
228
|
return getMergedEnv()["GRAPH_EXTRACTION_ENABLED"] === "true";
|
|
234
229
|
}
|
|
230
|
+
const FOLLOWUP_WINDOW_DEFAULT_SECONDS = 30;
|
|
231
|
+
function getFollowupWindowSeconds() {
|
|
232
|
+
return safeParseInt(getMergedEnv()["AGENTMEMORY_FOLLOWUP_WINDOW_SECONDS"], FOLLOWUP_WINDOW_DEFAULT_SECONDS);
|
|
233
|
+
}
|
|
235
234
|
function isConsolidationEnabled() {
|
|
236
235
|
const env = getMergedEnv();
|
|
237
236
|
const explicit = env["CONSOLIDATION_ENABLED"];
|
|
@@ -274,11 +273,18 @@ function loadFallbackConfig() {
|
|
|
274
273
|
return true;
|
|
275
274
|
}) };
|
|
276
275
|
}
|
|
277
|
-
|
|
278
276
|
//#endregion
|
|
279
277
|
//#region src/providers/agent-sdk.ts
|
|
278
|
+
const sdkChildContext = new AsyncLocalStorage();
|
|
279
|
+
let sdkActiveCount = 0;
|
|
280
|
+
let sdkOriginalEnv;
|
|
280
281
|
var AgentSDKProvider = class {
|
|
281
282
|
name = "agent-sdk";
|
|
283
|
+
sdkPromise = null;
|
|
284
|
+
loadSdk() {
|
|
285
|
+
if (!this.sdkPromise) this.sdkPromise = import("@anthropic-ai/claude-agent-sdk");
|
|
286
|
+
return this.sdkPromise;
|
|
287
|
+
}
|
|
282
288
|
async compress(systemPrompt, userPrompt) {
|
|
283
289
|
return this.query(systemPrompt, userPrompt);
|
|
284
290
|
}
|
|
@@ -286,29 +292,37 @@ var AgentSDKProvider = class {
|
|
|
286
292
|
return this.query(systemPrompt, userPrompt);
|
|
287
293
|
}
|
|
288
294
|
async query(systemPrompt, userPrompt) {
|
|
289
|
-
if (
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
295
|
+
if (sdkChildContext.getStore()) return "";
|
|
296
|
+
return sdkChildContext.run(true, async () => {
|
|
297
|
+
if (sdkActiveCount === 0) {
|
|
298
|
+
sdkOriginalEnv = process.env.AGENTMEMORY_SDK_CHILD;
|
|
299
|
+
process.env.AGENTMEMORY_SDK_CHILD = "1";
|
|
300
|
+
}
|
|
301
|
+
sdkActiveCount++;
|
|
302
|
+
try {
|
|
303
|
+
const { query } = await this.loadSdk();
|
|
304
|
+
const messages = query({
|
|
305
|
+
prompt: userPrompt,
|
|
306
|
+
options: {
|
|
307
|
+
systemPrompt,
|
|
308
|
+
maxTurns: 1,
|
|
309
|
+
allowedTools: []
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
let result = "";
|
|
313
|
+
for await (const msg of messages) if (msg.type === "result") result = msg.result ?? "";
|
|
314
|
+
return result;
|
|
315
|
+
} finally {
|
|
316
|
+
sdkActiveCount--;
|
|
317
|
+
if (sdkActiveCount === 0) {
|
|
318
|
+
if (sdkOriginalEnv === void 0) delete process.env.AGENTMEMORY_SDK_CHILD;
|
|
319
|
+
else process.env.AGENTMEMORY_SDK_CHILD = sdkOriginalEnv;
|
|
320
|
+
sdkOriginalEnv = void 0;
|
|
300
321
|
}
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
for await (const msg of messages) if (msg.type === "result") result = msg.result ?? "";
|
|
304
|
-
return result;
|
|
305
|
-
} finally {
|
|
306
|
-
if (prev === void 0) delete process.env.AGENTMEMORY_SDK_CHILD;
|
|
307
|
-
else process.env.AGENTMEMORY_SDK_CHILD = prev;
|
|
308
|
-
}
|
|
322
|
+
}
|
|
323
|
+
});
|
|
309
324
|
}
|
|
310
325
|
};
|
|
311
|
-
|
|
312
326
|
//#endregion
|
|
313
327
|
//#region src/providers/anthropic.ts
|
|
314
328
|
var AnthropicProvider = class {
|
|
@@ -362,7 +376,6 @@ var AnthropicProvider = class {
|
|
|
362
376
|
})).content.find((b) => b.type === "text")?.text ?? "";
|
|
363
377
|
}
|
|
364
378
|
};
|
|
365
|
-
|
|
366
379
|
//#endregion
|
|
367
380
|
//#region src/providers/_fetch.ts
|
|
368
381
|
function fetchWithTimeout(url, init, timeoutMs) {
|
|
@@ -376,7 +389,6 @@ function fetchWithTimeout(url, init, timeoutMs) {
|
|
|
376
389
|
signal
|
|
377
390
|
}).finally(() => clearTimeout(t));
|
|
378
391
|
}
|
|
379
|
-
|
|
380
392
|
//#endregion
|
|
381
393
|
//#region src/providers/minimax.ts
|
|
382
394
|
/**
|
|
@@ -436,7 +448,6 @@ var MinimaxProvider = class {
|
|
|
436
448
|
return ((await response.json()).content?.find((b) => b.type === "text"))?.text ?? "";
|
|
437
449
|
}
|
|
438
450
|
};
|
|
439
|
-
|
|
440
451
|
//#endregion
|
|
441
452
|
//#region src/providers/noop.ts
|
|
442
453
|
/**
|
|
@@ -455,11 +466,6 @@ var NoopProvider = class {
|
|
|
455
466
|
return "";
|
|
456
467
|
}
|
|
457
468
|
};
|
|
458
|
-
|
|
459
|
-
//#endregion
|
|
460
|
-
//#region src/providers/_openai-shared.ts
|
|
461
|
-
const DEFAULT_OPENAI_BASE_URL = "https://api.openai.com";
|
|
462
|
-
const DEFAULT_AZURE_API_VERSION = "2024-08-01-preview";
|
|
463
469
|
function detectAzure(baseUrl) {
|
|
464
470
|
try {
|
|
465
471
|
return new URL(baseUrl).hostname.endsWith(".openai.azure.com");
|
|
@@ -519,9 +525,8 @@ function buildAuthHeaders(apiKey, isAzure) {
|
|
|
519
525
|
};
|
|
520
526
|
}
|
|
521
527
|
function normalizeBaseUrl(raw) {
|
|
522
|
-
return (raw ||
|
|
528
|
+
return (raw || "https://api.openai.com").replace(/\/+$/, "");
|
|
523
529
|
}
|
|
524
|
-
|
|
525
530
|
//#endregion
|
|
526
531
|
//#region src/providers/openai.ts
|
|
527
532
|
const DEFAULT_TIMEOUT_MS = 6e4;
|
|
@@ -574,7 +579,7 @@ var OpenAIProvider = class {
|
|
|
574
579
|
this.baseUrl = normalizeBaseUrl(baseURL || getEnvVar("OPENAI_BASE_URL"));
|
|
575
580
|
this.reasoningEffort = getEnvVar("OPENAI_REASONING_EFFORT") || void 0;
|
|
576
581
|
this.timeoutMs = resolveTimeout();
|
|
577
|
-
this.azureApiVersion = getEnvVar("OPENAI_API_VERSION") ||
|
|
582
|
+
this.azureApiVersion = getEnvVar("OPENAI_API_VERSION") || "2024-08-01-preview";
|
|
578
583
|
this.isAzure = detectAzure(this.baseUrl);
|
|
579
584
|
}
|
|
580
585
|
async compress(systemPrompt, userPrompt) {
|
|
@@ -636,7 +641,6 @@ function parsePositiveInt(raw) {
|
|
|
636
641
|
const n = Number(trimmed);
|
|
637
642
|
return Number.isFinite(n) && n > 0 ? n : void 0;
|
|
638
643
|
}
|
|
639
|
-
|
|
640
644
|
//#endregion
|
|
641
645
|
//#region src/providers/openrouter.ts
|
|
642
646
|
var OpenRouterProvider = class {
|
|
@@ -688,7 +692,6 @@ var OpenRouterProvider = class {
|
|
|
688
692
|
return content;
|
|
689
693
|
}
|
|
690
694
|
};
|
|
691
|
-
|
|
692
695
|
//#endregion
|
|
693
696
|
//#region src/providers/circuit-breaker.ts
|
|
694
697
|
function positiveFinite(val, fallback) {
|
|
@@ -750,7 +753,6 @@ var CircuitBreaker = class {
|
|
|
750
753
|
};
|
|
751
754
|
}
|
|
752
755
|
};
|
|
753
|
-
|
|
754
756
|
//#endregion
|
|
755
757
|
//#region src/providers/resilient.ts
|
|
756
758
|
var ResilientProvider = class {
|
|
@@ -781,7 +783,6 @@ var ResilientProvider = class {
|
|
|
781
783
|
return this.breaker.getState();
|
|
782
784
|
}
|
|
783
785
|
};
|
|
784
|
-
|
|
785
786
|
//#endregion
|
|
786
787
|
//#region src/providers/fallback-chain.ts
|
|
787
788
|
var FallbackChainProvider = class {
|
|
@@ -806,7 +807,6 @@ var FallbackChainProvider = class {
|
|
|
806
807
|
throw lastError || /* @__PURE__ */ new Error("No providers available");
|
|
807
808
|
}
|
|
808
809
|
};
|
|
809
|
-
|
|
810
810
|
//#endregion
|
|
811
811
|
//#region src/providers/embedding/gemini.ts
|
|
812
812
|
const BATCH_LIMIT = 100;
|
|
@@ -862,7 +862,6 @@ function l2Normalize(vec) {
|
|
|
862
862
|
for (let i = 0; i < vec.length; i++) vec[i] = vec[i] / norm;
|
|
863
863
|
return vec;
|
|
864
864
|
}
|
|
865
|
-
|
|
866
865
|
//#endregion
|
|
867
866
|
//#region src/providers/embedding/openai.ts
|
|
868
867
|
const DEFAULT_MODEL$1 = "text-embedding-3-small";
|
|
@@ -936,7 +935,7 @@ var OpenAIEmbeddingProvider = class {
|
|
|
936
935
|
this.model = getEnvVar("OPENAI_EMBEDDING_MODEL") || DEFAULT_MODEL$1;
|
|
937
936
|
this.dimensions = resolveDimensions(this.model, getEnvVar("OPENAI_EMBEDDING_DIMENSIONS"));
|
|
938
937
|
this.isAzure = detectAzure(this.baseUrl);
|
|
939
|
-
this.azureApiVersion = getEnvVar("OPENAI_API_VERSION") ||
|
|
938
|
+
this.azureApiVersion = getEnvVar("OPENAI_API_VERSION") || "2024-08-01-preview";
|
|
940
939
|
}
|
|
941
940
|
async embed(text) {
|
|
942
941
|
const [result] = await this.embedBatch([text]);
|
|
@@ -958,7 +957,6 @@ var OpenAIEmbeddingProvider = class {
|
|
|
958
957
|
return (await response.json()).data.map((d) => new Float32Array(d.embedding));
|
|
959
958
|
}
|
|
960
959
|
};
|
|
961
|
-
|
|
962
960
|
//#endregion
|
|
963
961
|
//#region src/providers/embedding/voyage.ts
|
|
964
962
|
const API_URL$2 = "https://api.voyageai.com/v1/embeddings";
|
|
@@ -994,7 +992,6 @@ var VoyageEmbeddingProvider = class {
|
|
|
994
992
|
return (await response.json()).data.map((d) => new Float32Array(d.embedding));
|
|
995
993
|
}
|
|
996
994
|
};
|
|
997
|
-
|
|
998
995
|
//#endregion
|
|
999
996
|
//#region src/providers/embedding/cohere.ts
|
|
1000
997
|
const API_URL$1 = "https://api.cohere.ai/v1/embed";
|
|
@@ -1030,7 +1027,6 @@ var CohereEmbeddingProvider = class {
|
|
|
1030
1027
|
return (await response.json()).embeddings.map((e) => new Float32Array(e));
|
|
1031
1028
|
}
|
|
1032
1029
|
};
|
|
1033
|
-
|
|
1034
1030
|
//#endregion
|
|
1035
1031
|
//#region src/providers/embedding/openrouter.ts
|
|
1036
1032
|
const API_URL = "https://openrouter.ai/api/v1/embeddings";
|
|
@@ -1067,7 +1063,6 @@ var OpenRouterEmbeddingProvider = class {
|
|
|
1067
1063
|
return (await response.json()).data.map((d) => new Float32Array(d.embedding));
|
|
1068
1064
|
}
|
|
1069
1065
|
};
|
|
1070
|
-
|
|
1071
1066
|
//#endregion
|
|
1072
1067
|
//#region src/providers/embedding/local.ts
|
|
1073
1068
|
var LocalEmbeddingProvider = class {
|
|
@@ -1096,7 +1091,6 @@ var LocalEmbeddingProvider = class {
|
|
|
1096
1091
|
return this.extractor;
|
|
1097
1092
|
}
|
|
1098
1093
|
};
|
|
1099
|
-
|
|
1100
1094
|
//#endregion
|
|
1101
1095
|
//#region src/providers/embedding/clip.ts
|
|
1102
1096
|
const DEFAULT_MODEL = "Xenova/clip-vit-base-patch32";
|
|
@@ -1137,12 +1131,14 @@ var ClipEmbeddingProvider = class {
|
|
|
1137
1131
|
}
|
|
1138
1132
|
async getTextExtractor() {
|
|
1139
1133
|
if (this.textExtractor) return this.textExtractor;
|
|
1140
|
-
|
|
1134
|
+
const t = await this.getTransformers();
|
|
1135
|
+
this.textExtractor = await t.pipeline("feature-extraction", this.modelId);
|
|
1141
1136
|
return this.textExtractor;
|
|
1142
1137
|
}
|
|
1143
1138
|
async getImageExtractor() {
|
|
1144
1139
|
if (this.imageExtractor) return this.imageExtractor;
|
|
1145
|
-
|
|
1140
|
+
const t = await this.getTransformers();
|
|
1141
|
+
this.imageExtractor = await t.pipeline("image-feature-extraction", this.modelId);
|
|
1146
1142
|
return this.imageExtractor;
|
|
1147
1143
|
}
|
|
1148
1144
|
};
|
|
@@ -1167,7 +1163,6 @@ function normalize(vec) {
|
|
|
1167
1163
|
for (let i = 0; i < vec.length; i++) out[i] = vec[i] / norm;
|
|
1168
1164
|
return out;
|
|
1169
1165
|
}
|
|
1170
|
-
|
|
1171
1166
|
//#endregion
|
|
1172
1167
|
//#region src/providers/embedding/index.ts
|
|
1173
1168
|
let imageEmbeddingProvider = null;
|
|
@@ -1206,7 +1201,6 @@ function withDimensionGuard(provider) {
|
|
|
1206
1201
|
if (provider.embedImage) wrapped.embedImage = async (s) => check(await provider.embedImage(s), "embedImage");
|
|
1207
1202
|
return wrapped;
|
|
1208
1203
|
}
|
|
1209
|
-
|
|
1210
1204
|
//#endregion
|
|
1211
1205
|
//#region src/providers/index.ts
|
|
1212
1206
|
function requireEnvVar(key) {
|
|
@@ -1214,6 +1208,17 @@ function requireEnvVar(key) {
|
|
|
1214
1208
|
if (!value) throw new Error(`Missing required environment variable: ${key}. Set it in ~/.agentmemory/.env or as an environment variable.`);
|
|
1215
1209
|
return value;
|
|
1216
1210
|
}
|
|
1211
|
+
function defaultModelFor(providerType) {
|
|
1212
|
+
switch (providerType) {
|
|
1213
|
+
case "openai": return getEnvVar("OPENAI_MODEL") || "gpt-4o-mini";
|
|
1214
|
+
case "anthropic": return getEnvVar("ANTHROPIC_MODEL") || "claude-sonnet-4-20250514";
|
|
1215
|
+
case "gemini": return getEnvVar("GEMINI_MODEL") || "gemini-2.5-flash";
|
|
1216
|
+
case "openrouter": return getEnvVar("OPENROUTER_MODEL") || "anthropic/claude-sonnet-4-20250514";
|
|
1217
|
+
case "minimax": return getEnvVar("MINIMAX_MODEL") || "MiniMax-M2.7";
|
|
1218
|
+
case "agent-sdk": return "claude-sonnet-4-20250514";
|
|
1219
|
+
default: return "noop";
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1217
1222
|
function createProvider(config) {
|
|
1218
1223
|
return new ResilientProvider(createBaseProvider(config));
|
|
1219
1224
|
}
|
|
@@ -1225,7 +1230,7 @@ function createFallbackProvider(config, fallbackConfig) {
|
|
|
1225
1230
|
try {
|
|
1226
1231
|
const fbConfig = {
|
|
1227
1232
|
provider: providerType,
|
|
1228
|
-
model:
|
|
1233
|
+
model: defaultModelFor(providerType),
|
|
1229
1234
|
maxTokens: config.maxTokens
|
|
1230
1235
|
};
|
|
1231
1236
|
providers.push(createBaseProvider(fbConfig));
|
|
@@ -1253,7 +1258,6 @@ function createBaseProvider(config) {
|
|
|
1253
1258
|
default: return new AgentSDKProvider();
|
|
1254
1259
|
}
|
|
1255
1260
|
}
|
|
1256
|
-
|
|
1257
1261
|
//#endregion
|
|
1258
1262
|
//#region src/state/kv.ts
|
|
1259
1263
|
var StateKV = class {
|
|
@@ -1305,7 +1309,6 @@ var StateKV = class {
|
|
|
1305
1309
|
});
|
|
1306
1310
|
}
|
|
1307
1311
|
};
|
|
1308
|
-
|
|
1309
1312
|
//#endregion
|
|
1310
1313
|
//#region src/state/schema.ts
|
|
1311
1314
|
const KV = {
|
|
@@ -1353,7 +1356,8 @@ const KV = {
|
|
|
1353
1356
|
slots: "mem:slots",
|
|
1354
1357
|
globalSlots: "mem:slots:global",
|
|
1355
1358
|
state: "mem:state",
|
|
1356
|
-
commits: "mem:commits"
|
|
1359
|
+
commits: "mem:commits",
|
|
1360
|
+
recentSearches: "mem:recent-searches"
|
|
1357
1361
|
};
|
|
1358
1362
|
const STREAM = {
|
|
1359
1363
|
name: "mem-live",
|
|
@@ -1375,7 +1379,6 @@ function jaccardSimilarity(a, b) {
|
|
|
1375
1379
|
for (const word of setA) if (setB.has(word)) intersection++;
|
|
1376
1380
|
return intersection / (setA.size + setB.size - intersection);
|
|
1377
1381
|
}
|
|
1378
|
-
|
|
1379
1382
|
//#endregion
|
|
1380
1383
|
//#region src/state/vector-index.ts
|
|
1381
1384
|
function float32ToBase64(arr) {
|
|
@@ -1498,7 +1501,6 @@ var VectorIndex = class VectorIndex {
|
|
|
1498
1501
|
return idx;
|
|
1499
1502
|
}
|
|
1500
1503
|
};
|
|
1501
|
-
|
|
1502
1504
|
//#endregion
|
|
1503
1505
|
//#region src/state/memory-utils.ts
|
|
1504
1506
|
function memoryToObservation(memory) {
|
|
@@ -1515,7 +1517,6 @@ function memoryToObservation(memory) {
|
|
|
1515
1517
|
importance: memory.strength
|
|
1516
1518
|
};
|
|
1517
1519
|
}
|
|
1518
|
-
|
|
1519
1520
|
//#endregion
|
|
1520
1521
|
//#region src/functions/graph-retrieval.ts
|
|
1521
1522
|
function buildGraphContext(path) {
|
|
@@ -1760,7 +1761,6 @@ var MinHeap = class {
|
|
|
1760
1761
|
}
|
|
1761
1762
|
}
|
|
1762
1763
|
};
|
|
1763
|
-
|
|
1764
1764
|
//#endregion
|
|
1765
1765
|
//#region src/logger.ts
|
|
1766
1766
|
function fmt(level, msg, fields) {
|
|
@@ -1798,7 +1798,6 @@ function bootLog(msg) {
|
|
|
1798
1798
|
}
|
|
1799
1799
|
if (bootBuffer.length < 500) bootBuffer.push(msg);
|
|
1800
1800
|
}
|
|
1801
|
-
|
|
1802
1801
|
//#endregion
|
|
1803
1802
|
//#region src/functions/query-expansion.ts
|
|
1804
1803
|
const QUERY_EXPANSION_SYSTEM = `You are a query expansion engine for a memory retrieval system. Given a user query, generate diverse reformulations to maximize recall.
|
|
@@ -1955,7 +1954,6 @@ function extractEntitiesFromQuery(query) {
|
|
|
1955
1954
|
}
|
|
1956
1955
|
return [...new Set(entities)];
|
|
1957
1956
|
}
|
|
1958
|
-
|
|
1959
1957
|
//#endregion
|
|
1960
1958
|
//#region src/state/reranker.ts
|
|
1961
1959
|
let pipeline = null;
|
|
@@ -2009,7 +2007,6 @@ async function rerank(query, results, topK = 20) {
|
|
|
2009
2007
|
rerankPosition: i + 1
|
|
2010
2008
|
}));
|
|
2011
2009
|
}
|
|
2012
|
-
|
|
2013
2010
|
//#endregion
|
|
2014
2011
|
//#region src/state/hybrid-search.ts
|
|
2015
2012
|
const RRF_K = 60;
|
|
@@ -2180,7 +2177,6 @@ var HybridSearch = class {
|
|
|
2180
2177
|
return enriched;
|
|
2181
2178
|
}
|
|
2182
2179
|
};
|
|
2183
|
-
|
|
2184
2180
|
//#endregion
|
|
2185
2181
|
//#region src/state/stemmer.ts
|
|
2186
2182
|
const step2map = {
|
|
@@ -2276,7 +2272,6 @@ function stem(word) {
|
|
|
2276
2272
|
if (endsDoubleConsonant(w) && w.endsWith("l") && measure(w.slice(0, -1)) > 1) w = w.slice(0, -1);
|
|
2277
2273
|
return w;
|
|
2278
2274
|
}
|
|
2279
|
-
|
|
2280
2275
|
//#endregion
|
|
2281
2276
|
//#region src/state/synonyms.ts
|
|
2282
2277
|
const SYNONYM_GROUPS = [
|
|
@@ -2438,7 +2433,6 @@ function getSynonyms(stemmedTerm) {
|
|
|
2438
2433
|
const syns = synonymMap.get(stemmedTerm);
|
|
2439
2434
|
return syns ? [...syns] : [];
|
|
2440
2435
|
}
|
|
2441
|
-
|
|
2442
2436
|
//#endregion
|
|
2443
2437
|
//#region src/state/cjk-segmenter.ts
|
|
2444
2438
|
const cjkRequire = createRequire(import.meta.url);
|
|
@@ -2542,7 +2536,6 @@ function segmentCjk(text) {
|
|
|
2542
2536
|
}
|
|
2543
2537
|
return out;
|
|
2544
2538
|
}
|
|
2545
|
-
|
|
2546
2539
|
//#endregion
|
|
2547
2540
|
//#region src/state/search-index.ts
|
|
2548
2541
|
var SearchIndex = class SearchIndex {
|
|
@@ -2742,18 +2735,92 @@ var SearchIndex = class SearchIndex {
|
|
|
2742
2735
|
return lo;
|
|
2743
2736
|
}
|
|
2744
2737
|
};
|
|
2745
|
-
|
|
2738
|
+
//#endregion
|
|
2739
|
+
//#region src/functions/audit.ts
|
|
2740
|
+
async function recordAudit(kv, operation, functionId, targetIds, details = {}, qualityScore, userId) {
|
|
2741
|
+
const entry = {
|
|
2742
|
+
id: generateId("aud"),
|
|
2743
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2744
|
+
operation,
|
|
2745
|
+
userId,
|
|
2746
|
+
functionId,
|
|
2747
|
+
targetIds,
|
|
2748
|
+
details,
|
|
2749
|
+
qualityScore
|
|
2750
|
+
};
|
|
2751
|
+
await kv.set(KV.audit, entry.id, entry);
|
|
2752
|
+
return entry;
|
|
2753
|
+
}
|
|
2754
|
+
async function safeAudit(kv, operation, functionId, targetIds, details = {}, qualityScore, userId) {
|
|
2755
|
+
try {
|
|
2756
|
+
await recordAudit(kv, operation, functionId, targetIds, details, qualityScore, userId);
|
|
2757
|
+
} catch (err) {
|
|
2758
|
+
try {
|
|
2759
|
+
logger.warn("audit write failed", {
|
|
2760
|
+
functionId,
|
|
2761
|
+
operation,
|
|
2762
|
+
targetIds,
|
|
2763
|
+
error: err instanceof Error ? err.message : String(err)
|
|
2764
|
+
});
|
|
2765
|
+
} catch {}
|
|
2766
|
+
}
|
|
2767
|
+
}
|
|
2768
|
+
async function queryAudit(kv, filter) {
|
|
2769
|
+
let entries = [...await kv.list(KV.audit)].sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
|
|
2770
|
+
if (filter?.operation) entries = entries.filter((e) => e.operation === filter.operation);
|
|
2771
|
+
if (filter?.dateFrom) {
|
|
2772
|
+
const from = new Date(filter.dateFrom).getTime();
|
|
2773
|
+
if (Number.isNaN(from)) throw new Error(`Invalid dateFrom: ${filter.dateFrom}`);
|
|
2774
|
+
entries = entries.filter((e) => new Date(e.timestamp).getTime() >= from);
|
|
2775
|
+
}
|
|
2776
|
+
if (filter?.dateTo) {
|
|
2777
|
+
const to = new Date(filter.dateTo).getTime();
|
|
2778
|
+
if (Number.isNaN(to)) throw new Error(`Invalid dateTo: ${filter.dateTo}`);
|
|
2779
|
+
entries = entries.filter((e) => new Date(e.timestamp).getTime() <= to);
|
|
2780
|
+
}
|
|
2781
|
+
return entries.slice(0, filter?.limit || 100);
|
|
2782
|
+
}
|
|
2746
2783
|
//#endregion
|
|
2747
2784
|
//#region src/state/index-persistence.ts
|
|
2748
2785
|
const DEBOUNCE_MS = 5e3;
|
|
2749
2786
|
const FAILURE_LOG_THROTTLE_MS = 6e4;
|
|
2787
|
+
const INDEX_PERSISTENCE_FUNCTION_ID = "mem::index-persistence";
|
|
2788
|
+
const BM25_KEY = "data";
|
|
2789
|
+
const BM25_MANIFEST_KEY = "data:manifest";
|
|
2790
|
+
const BM25_SHARD_SCOPE_PREFIX = `${KV.bm25Index}:bm25:`;
|
|
2791
|
+
const VECTOR_KEY = "vectors";
|
|
2792
|
+
const VECTOR_MANIFEST_KEY = "vectors:manifest";
|
|
2793
|
+
const VECTOR_SHARD_SCOPE_PREFIX = `${KV.bm25Index}:vectors:`;
|
|
2794
|
+
const INDEX_SHARD_KEY = "data";
|
|
2795
|
+
const DEFAULT_INDEX_SHARD_CHARS = 2e6;
|
|
2796
|
+
function shardChars(options) {
|
|
2797
|
+
const configured = options.shardChars;
|
|
2798
|
+
if (typeof configured !== "number" || !Number.isFinite(configured)) return DEFAULT_INDEX_SHARD_CHARS;
|
|
2799
|
+
const wholeChars = Math.floor(configured);
|
|
2800
|
+
return wholeChars >= 1 ? wholeChars : DEFAULT_INDEX_SHARD_CHARS;
|
|
2801
|
+
}
|
|
2802
|
+
function createIndexGeneration() {
|
|
2803
|
+
return generateId("idx");
|
|
2804
|
+
}
|
|
2805
|
+
function statePath(scope, key) {
|
|
2806
|
+
return `${scope}/${key}`;
|
|
2807
|
+
}
|
|
2808
|
+
function errorMessage(err) {
|
|
2809
|
+
return err instanceof Error ? err.message : String(err);
|
|
2810
|
+
}
|
|
2811
|
+
function isValidShardDescriptor(shard) {
|
|
2812
|
+
if (!shard || typeof shard !== "object") return false;
|
|
2813
|
+
const candidate = shard;
|
|
2814
|
+
return typeof candidate.scope === "string" && candidate.scope.length > 0 && typeof candidate.key === "string" && candidate.key.length > 0 && Number.isInteger(candidate.chars) && candidate.chars >= 0;
|
|
2815
|
+
}
|
|
2750
2816
|
var IndexPersistence = class {
|
|
2751
2817
|
timer = null;
|
|
2752
2818
|
lastFailureLogAt = 0;
|
|
2753
|
-
constructor(kv, bm25, vector) {
|
|
2819
|
+
constructor(kv, bm25, vector, options = {}) {
|
|
2754
2820
|
this.kv = kv;
|
|
2755
2821
|
this.bm25 = bm25;
|
|
2756
2822
|
this.vector = vector;
|
|
2823
|
+
this.options = options;
|
|
2757
2824
|
}
|
|
2758
2825
|
scheduleSave() {
|
|
2759
2826
|
if (this.timer) clearTimeout(this.timer);
|
|
@@ -2767,8 +2834,8 @@ var IndexPersistence = class {
|
|
|
2767
2834
|
this.timer = null;
|
|
2768
2835
|
}
|
|
2769
2836
|
try {
|
|
2770
|
-
await this.
|
|
2771
|
-
if (this.vector
|
|
2837
|
+
await this.saveBm25Index(this.bm25.serialize());
|
|
2838
|
+
if (this.vector) await this.saveVectorIndex(this.vector.serialize());
|
|
2772
2839
|
} catch (err) {
|
|
2773
2840
|
this.logFailure(err);
|
|
2774
2841
|
}
|
|
@@ -2776,9 +2843,9 @@ var IndexPersistence = class {
|
|
|
2776
2843
|
async load() {
|
|
2777
2844
|
let bm25 = null;
|
|
2778
2845
|
let vector = null;
|
|
2779
|
-
const bm25Data = await this.
|
|
2846
|
+
const bm25Data = await this.loadBm25Data();
|
|
2780
2847
|
if (bm25Data && typeof bm25Data === "string") bm25 = SearchIndex.deserialize(bm25Data);
|
|
2781
|
-
const vecData = await this.
|
|
2848
|
+
const vecData = await this.loadVectorData();
|
|
2782
2849
|
if (vecData && typeof vecData === "string") vector = VectorIndex.deserialize(vecData);
|
|
2783
2850
|
return {
|
|
2784
2851
|
bm25,
|
|
@@ -2803,8 +2870,190 @@ var IndexPersistence = class {
|
|
|
2803
2870
|
hint: code === "TIMEOUT" ? "iii-engine state::set timed out; recent index updates remain in memory and will retry on the next debounce flush" : void 0
|
|
2804
2871
|
});
|
|
2805
2872
|
}
|
|
2873
|
+
async saveBm25Index(serialized) {
|
|
2874
|
+
await this.saveShardedIndex(serialized, BM25_MANIFEST_KEY, BM25_KEY, BM25_SHARD_SCOPE_PREFIX);
|
|
2875
|
+
}
|
|
2876
|
+
async saveVectorIndex(serialized) {
|
|
2877
|
+
await this.saveShardedIndex(serialized, VECTOR_MANIFEST_KEY, VECTOR_KEY, VECTOR_SHARD_SCOPE_PREFIX);
|
|
2878
|
+
}
|
|
2879
|
+
async saveShardedIndex(serialized, manifestKey, legacyKey, scopePrefix) {
|
|
2880
|
+
const previous = await this.kv.get(KV.bm25Index, manifestKey).catch(() => null);
|
|
2881
|
+
const generation = this.options.createGeneration?.() ?? createIndexGeneration();
|
|
2882
|
+
const chunkChars = shardChars(this.options);
|
|
2883
|
+
const shards = [];
|
|
2884
|
+
const chunks = [];
|
|
2885
|
+
for (let offset = 0; offset < serialized.length; offset += chunkChars) {
|
|
2886
|
+
const shardIndex = shards.length;
|
|
2887
|
+
const scope = `${scopePrefix}${generation}:${String(shardIndex).padStart(5, "0")}`;
|
|
2888
|
+
const chunk = serialized.slice(offset, offset + chunkChars);
|
|
2889
|
+
shards.push({
|
|
2890
|
+
scope,
|
|
2891
|
+
key: INDEX_SHARD_KEY,
|
|
2892
|
+
chars: chunk.length
|
|
2893
|
+
});
|
|
2894
|
+
chunks.push(chunk);
|
|
2895
|
+
}
|
|
2896
|
+
const failedWrite = (await Promise.allSettled(shards.map(async (shard, index) => {
|
|
2897
|
+
const chunk = chunks[index] ?? "";
|
|
2898
|
+
await this.kv.set(shard.scope, shard.key, chunk);
|
|
2899
|
+
await this.auditIndexPersistence("shard_write", [statePath(shard.scope, shard.key)], {
|
|
2900
|
+
scope: shard.scope,
|
|
2901
|
+
key: shard.key,
|
|
2902
|
+
manifestKey,
|
|
2903
|
+
generation,
|
|
2904
|
+
chars: chunk.length
|
|
2905
|
+
});
|
|
2906
|
+
}))).find((result) => result.status === "rejected");
|
|
2907
|
+
if (failedWrite) {
|
|
2908
|
+
await this.deleteShards(shards, "shard_write_rollback");
|
|
2909
|
+
throw failedWrite.reason;
|
|
2910
|
+
}
|
|
2911
|
+
const nextManifest = {
|
|
2912
|
+
v: 1,
|
|
2913
|
+
generation,
|
|
2914
|
+
shards,
|
|
2915
|
+
chars: serialized.length
|
|
2916
|
+
};
|
|
2917
|
+
try {
|
|
2918
|
+
await this.kv.set(KV.bm25Index, manifestKey, nextManifest);
|
|
2919
|
+
await this.auditIndexPersistence("manifest_publish", [statePath(KV.bm25Index, manifestKey)], {
|
|
2920
|
+
manifestKey,
|
|
2921
|
+
generation,
|
|
2922
|
+
chars: serialized.length,
|
|
2923
|
+
shards: shards.length,
|
|
2924
|
+
result: "committed"
|
|
2925
|
+
});
|
|
2926
|
+
} catch (err) {
|
|
2927
|
+
if (await this.isManifestPublished(manifestKey, nextManifest)) await this.auditIndexPersistence("manifest_publish", [statePath(KV.bm25Index, manifestKey)], {
|
|
2928
|
+
manifestKey,
|
|
2929
|
+
generation,
|
|
2930
|
+
chars: serialized.length,
|
|
2931
|
+
shards: shards.length,
|
|
2932
|
+
result: "committed_after_error",
|
|
2933
|
+
error: errorMessage(err)
|
|
2934
|
+
});
|
|
2935
|
+
else await this.deleteShards(shards, "manifest_publish_rollback");
|
|
2936
|
+
throw err;
|
|
2937
|
+
}
|
|
2938
|
+
await this.deleteKey(KV.bm25Index, legacyKey, "legacy_cleanup");
|
|
2939
|
+
if (previous?.v === 1 && Array.isArray(previous.shards)) {
|
|
2940
|
+
const currentShardIds = new Set(shards.map((shard) => `${shard.scope}\0${shard.key}`));
|
|
2941
|
+
for (const shard of previous.shards) {
|
|
2942
|
+
if (currentShardIds.has(`${shard.scope}\0${shard.key}`)) continue;
|
|
2943
|
+
await this.deleteShards([shard], "previous_generation_cleanup");
|
|
2944
|
+
}
|
|
2945
|
+
}
|
|
2946
|
+
}
|
|
2947
|
+
async auditIndexPersistence(action, targetIds, details) {
|
|
2948
|
+
await safeAudit(this.kv, "index_persist", INDEX_PERSISTENCE_FUNCTION_ID, targetIds, {
|
|
2949
|
+
action,
|
|
2950
|
+
...details
|
|
2951
|
+
});
|
|
2952
|
+
}
|
|
2953
|
+
async deleteKey(scope, key, reason) {
|
|
2954
|
+
let result = "deleted";
|
|
2955
|
+
let error;
|
|
2956
|
+
try {
|
|
2957
|
+
await this.kv.delete(scope, key);
|
|
2958
|
+
} catch (err) {
|
|
2959
|
+
result = "failed";
|
|
2960
|
+
error = errorMessage(err);
|
|
2961
|
+
}
|
|
2962
|
+
await this.auditIndexPersistence("delete", [statePath(scope, key)], {
|
|
2963
|
+
scope,
|
|
2964
|
+
key,
|
|
2965
|
+
reason,
|
|
2966
|
+
result,
|
|
2967
|
+
error
|
|
2968
|
+
});
|
|
2969
|
+
}
|
|
2970
|
+
async deleteShards(shards, reason) {
|
|
2971
|
+
for (const shard of shards) await this.deleteKey(shard.scope, shard.key, reason);
|
|
2972
|
+
}
|
|
2973
|
+
async isManifestPublished(manifestKey, expected) {
|
|
2974
|
+
const published = await this.kv.get(KV.bm25Index, manifestKey).catch(() => null);
|
|
2975
|
+
if (published?.v !== 1 || published.generation !== expected.generation || published.chars !== expected.chars || !Array.isArray(published.shards) || published.shards.length !== expected.shards.length) return false;
|
|
2976
|
+
return published.shards.every((shard, index) => {
|
|
2977
|
+
const expectedShard = expected.shards[index];
|
|
2978
|
+
if (!expectedShard) return false;
|
|
2979
|
+
return shard.scope === expectedShard.scope && shard.key === expectedShard.key && shard.chars === expectedShard.chars;
|
|
2980
|
+
});
|
|
2981
|
+
}
|
|
2982
|
+
async loadBm25Data() {
|
|
2983
|
+
return this.loadShardedData(BM25_KEY, BM25_MANIFEST_KEY, "BM25");
|
|
2984
|
+
}
|
|
2985
|
+
async loadVectorData() {
|
|
2986
|
+
return this.loadShardedData(VECTOR_KEY, VECTOR_MANIFEST_KEY, "vector");
|
|
2987
|
+
}
|
|
2988
|
+
async loadShardedData(legacyKey, manifestKey, label) {
|
|
2989
|
+
const manifest = await this.readIndexValue(KV.bm25Index, manifestKey, label, "manifest");
|
|
2990
|
+
if (!manifest.ok) return null;
|
|
2991
|
+
if (manifest.value !== null) return this.loadManifestData(manifest.value, label);
|
|
2992
|
+
const legacy = await this.readIndexValue(KV.bm25Index, legacyKey, label, "legacy");
|
|
2993
|
+
if (!legacy.ok) return null;
|
|
2994
|
+
if (legacy.value && typeof legacy.value === "string") return legacy.value;
|
|
2995
|
+
return null;
|
|
2996
|
+
}
|
|
2997
|
+
async readIndexValue(scope, key, label, source) {
|
|
2998
|
+
try {
|
|
2999
|
+
return {
|
|
3000
|
+
ok: true,
|
|
3001
|
+
value: await this.kv.get(scope, key)
|
|
3002
|
+
};
|
|
3003
|
+
} catch (err) {
|
|
3004
|
+
logger.warn(`index persistence: ${label} ${source} read failed`, {
|
|
3005
|
+
scope,
|
|
3006
|
+
key,
|
|
3007
|
+
message: errorMessage(err)
|
|
3008
|
+
});
|
|
3009
|
+
return { ok: false };
|
|
3010
|
+
}
|
|
3011
|
+
}
|
|
3012
|
+
async loadManifestData(manifest, label) {
|
|
3013
|
+
if (manifest.v !== 1 || !Array.isArray(manifest.shards) || manifest.shards.length === 0 || !Number.isInteger(manifest.chars) || manifest.chars < 0) {
|
|
3014
|
+
logger.warn(`index persistence: ${label} shard manifest invalid`);
|
|
3015
|
+
return null;
|
|
3016
|
+
}
|
|
3017
|
+
for (const shard of manifest.shards) if (!isValidShardDescriptor(shard)) {
|
|
3018
|
+
logger.warn(`index persistence: ${label} shard manifest invalid`);
|
|
3019
|
+
return null;
|
|
3020
|
+
}
|
|
3021
|
+
const loadedShards = await Promise.all(manifest.shards.map(async (shard) => ({
|
|
3022
|
+
shard,
|
|
3023
|
+
chunk: await this.kv.get(shard.scope, shard.key).catch(() => null)
|
|
3024
|
+
})));
|
|
3025
|
+
const chunks = [];
|
|
3026
|
+
let chars = 0;
|
|
3027
|
+
for (const { shard, chunk } of loadedShards) {
|
|
3028
|
+
if (typeof chunk !== "string") {
|
|
3029
|
+
logger.warn(`index persistence: ${label} shard missing`, {
|
|
3030
|
+
scope: shard.scope,
|
|
3031
|
+
key: shard.key
|
|
3032
|
+
});
|
|
3033
|
+
return null;
|
|
3034
|
+
}
|
|
3035
|
+
if (chunk.length !== shard.chars) {
|
|
3036
|
+
logger.warn(`index persistence: ${label} shard length mismatch`, {
|
|
3037
|
+
scope: shard.scope,
|
|
3038
|
+
key: shard.key,
|
|
3039
|
+
expected: shard.chars,
|
|
3040
|
+
actual: chunk.length
|
|
3041
|
+
});
|
|
3042
|
+
return null;
|
|
3043
|
+
}
|
|
3044
|
+
chunks.push(chunk);
|
|
3045
|
+
chars += chunk.length;
|
|
3046
|
+
}
|
|
3047
|
+
if (chars !== manifest.chars) {
|
|
3048
|
+
logger.warn(`index persistence: ${label} total length mismatch`, {
|
|
3049
|
+
expected: manifest.chars,
|
|
3050
|
+
actual: chars
|
|
3051
|
+
});
|
|
3052
|
+
return null;
|
|
3053
|
+
}
|
|
3054
|
+
return chunks.join("");
|
|
3055
|
+
}
|
|
2806
3056
|
};
|
|
2807
|
-
|
|
2808
3057
|
//#endregion
|
|
2809
3058
|
//#region src/functions/privacy.ts
|
|
2810
3059
|
const PRIVATE_TAG_RE = /<private>[\s\S]*?<\/private>/gi;
|
|
@@ -2841,7 +3090,6 @@ function registerPrivacyFunction(sdk) {
|
|
|
2841
3090
|
return { output: stripPrivateData(data.input) };
|
|
2842
3091
|
});
|
|
2843
3092
|
}
|
|
2844
|
-
|
|
2845
3093
|
//#endregion
|
|
2846
3094
|
//#region src/state/keyed-mutex.ts
|
|
2847
3095
|
const locks = /* @__PURE__ */ new Map();
|
|
@@ -2854,7 +3102,6 @@ function withKeyedLock(key, fn) {
|
|
|
2854
3102
|
});
|
|
2855
3103
|
return next;
|
|
2856
3104
|
}
|
|
2857
|
-
|
|
2858
3105
|
//#endregion
|
|
2859
3106
|
//#region src/functions/compress-synthetic.ts
|
|
2860
3107
|
function inferType(toolName, hookType) {
|
|
@@ -2950,7 +3197,6 @@ function buildSyntheticCompression(raw) {
|
|
|
2950
3197
|
if (raw.agentId) result.agentId = raw.agentId;
|
|
2951
3198
|
return result;
|
|
2952
3199
|
}
|
|
2953
|
-
|
|
2954
3200
|
//#endregion
|
|
2955
3201
|
//#region src/functions/access-tracker.ts
|
|
2956
3202
|
const RECENT_CAP = 20;
|
|
@@ -3021,7 +3267,6 @@ async function deleteAccessLog(kv, memoryId) {
|
|
|
3021
3267
|
});
|
|
3022
3268
|
} catch {}
|
|
3023
3269
|
}
|
|
3024
|
-
|
|
3025
3270
|
//#endregion
|
|
3026
3271
|
//#region src/functions/search.ts
|
|
3027
3272
|
let index = null;
|
|
@@ -3377,7 +3622,6 @@ function registerSearchFunction(sdk, kv) {
|
|
|
3377
3622
|
};
|
|
3378
3623
|
});
|
|
3379
3624
|
}
|
|
3380
|
-
|
|
3381
3625
|
//#endregion
|
|
3382
3626
|
//#region src/functions/observe.ts
|
|
3383
3627
|
function extractImage(d) {
|
|
@@ -3461,11 +3705,19 @@ function registerObserveFunction(sdk, kv, dedupMap, maxObservationsPerSession) {
|
|
|
3461
3705
|
raw.imageData = filePath;
|
|
3462
3706
|
const { incrementImageRef } = await Promise.resolve().then(() => image_refs_exports);
|
|
3463
3707
|
await incrementImageRef(kv, filePath);
|
|
3464
|
-
sdk.
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3708
|
+
sdk.trigger({
|
|
3709
|
+
function_id: "mem::disk-size-delta",
|
|
3710
|
+
payload: { deltaBytes: bytesWritten },
|
|
3711
|
+
action: TriggerAction.Void()
|
|
3712
|
+
});
|
|
3713
|
+
if (process.env["AGENTMEMORY_IMAGE_EMBEDDINGS"] === "true") sdk.trigger({
|
|
3714
|
+
function_id: "mem::vision-embed",
|
|
3715
|
+
payload: {
|
|
3716
|
+
imageRef: filePath,
|
|
3717
|
+
sessionId: payload.sessionId,
|
|
3718
|
+
observationId: obsId
|
|
3719
|
+
},
|
|
3720
|
+
action: TriggerAction.Void()
|
|
3469
3721
|
});
|
|
3470
3722
|
}
|
|
3471
3723
|
try {
|
|
@@ -3474,7 +3726,11 @@ function registerObserveFunction(sdk, kv, dedupMap, maxObservationsPerSession) {
|
|
|
3474
3726
|
if (raw.imageData) {
|
|
3475
3727
|
const { deleteImage } = await Promise.resolve().then(() => image_store_exports);
|
|
3476
3728
|
const { deletedBytes } = await deleteImage(raw.imageData);
|
|
3477
|
-
if (deletedBytes > 0) sdk.
|
|
3729
|
+
if (deletedBytes > 0) sdk.trigger({
|
|
3730
|
+
function_id: "mem::disk-size-delta",
|
|
3731
|
+
payload: { deltaBytes: -deletedBytes },
|
|
3732
|
+
action: TriggerAction.Void()
|
|
3733
|
+
});
|
|
3478
3734
|
}
|
|
3479
3735
|
throw error;
|
|
3480
3736
|
}
|
|
@@ -3594,7 +3850,6 @@ function registerObserveFunction(sdk, kv, dedupMap, maxObservationsPerSession) {
|
|
|
3594
3850
|
});
|
|
3595
3851
|
});
|
|
3596
3852
|
}
|
|
3597
|
-
|
|
3598
3853
|
//#endregion
|
|
3599
3854
|
//#region src/utils/image-store.ts
|
|
3600
3855
|
var image_store_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -3671,7 +3926,6 @@ async function touchImage(filePath) {
|
|
|
3671
3926
|
}
|
|
3672
3927
|
} catch (err) {}
|
|
3673
3928
|
}
|
|
3674
|
-
|
|
3675
3929
|
//#endregion
|
|
3676
3930
|
//#region src/functions/image-refs.ts
|
|
3677
3931
|
var image_refs_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -3697,11 +3951,14 @@ async function decrementImageRef(kv, sdk, filePath) {
|
|
|
3697
3951
|
await kv.delete(KV.imageEmbeddings, filePath);
|
|
3698
3952
|
await kv.delete(KV.imageRefs, filePath);
|
|
3699
3953
|
const { deletedBytes } = await deleteImage(filePath);
|
|
3700
|
-
if (deletedBytes > 0) sdk.
|
|
3954
|
+
if (deletedBytes > 0) sdk.trigger({
|
|
3955
|
+
function_id: "mem::disk-size-delta",
|
|
3956
|
+
payload: { deltaBytes: -deletedBytes },
|
|
3957
|
+
action: TriggerAction.Void()
|
|
3958
|
+
});
|
|
3701
3959
|
} else await kv.set(KV.imageRefs, filePath, current - 1);
|
|
3702
3960
|
});
|
|
3703
3961
|
}
|
|
3704
|
-
|
|
3705
3962
|
//#endregion
|
|
3706
3963
|
//#region src/functions/image-quota-cleanup.ts
|
|
3707
3964
|
const GRACE_PERIOD_MS = 3e4;
|
|
@@ -3761,7 +4018,11 @@ function registerImageQuotaCleanup(sdk, kv) {
|
|
|
3761
4018
|
if (refCount > 0) return;
|
|
3762
4019
|
const { deletedBytes } = await deleteImage(f.filePath);
|
|
3763
4020
|
if (deletedBytes > 0) {
|
|
3764
|
-
sdk.
|
|
4021
|
+
sdk.trigger({
|
|
4022
|
+
function_id: "mem::disk-size-delta",
|
|
4023
|
+
payload: { deltaBytes: -deletedBytes },
|
|
4024
|
+
action: TriggerAction.Void()
|
|
4025
|
+
});
|
|
3765
4026
|
totalToFree -= deletedBytes;
|
|
3766
4027
|
freedBytes += deletedBytes;
|
|
3767
4028
|
evicted++;
|
|
@@ -3783,53 +4044,6 @@ function registerImageQuotaCleanup(sdk, kv) {
|
|
|
3783
4044
|
});
|
|
3784
4045
|
});
|
|
3785
4046
|
}
|
|
3786
|
-
|
|
3787
|
-
//#endregion
|
|
3788
|
-
//#region src/functions/audit.ts
|
|
3789
|
-
async function recordAudit(kv, operation, functionId, targetIds, details = {}, qualityScore, userId) {
|
|
3790
|
-
const entry = {
|
|
3791
|
-
id: generateId("aud"),
|
|
3792
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3793
|
-
operation,
|
|
3794
|
-
userId,
|
|
3795
|
-
functionId,
|
|
3796
|
-
targetIds,
|
|
3797
|
-
details,
|
|
3798
|
-
qualityScore
|
|
3799
|
-
};
|
|
3800
|
-
await kv.set(KV.audit, entry.id, entry);
|
|
3801
|
-
return entry;
|
|
3802
|
-
}
|
|
3803
|
-
async function safeAudit(kv, operation, functionId, targetIds, details = {}, qualityScore, userId) {
|
|
3804
|
-
try {
|
|
3805
|
-
await recordAudit(kv, operation, functionId, targetIds, details, qualityScore, userId);
|
|
3806
|
-
} catch (err) {
|
|
3807
|
-
try {
|
|
3808
|
-
logger.warn("audit write failed", {
|
|
3809
|
-
functionId,
|
|
3810
|
-
operation,
|
|
3811
|
-
targetIds,
|
|
3812
|
-
error: err instanceof Error ? err.message : String(err)
|
|
3813
|
-
});
|
|
3814
|
-
} catch {}
|
|
3815
|
-
}
|
|
3816
|
-
}
|
|
3817
|
-
async function queryAudit(kv, filter) {
|
|
3818
|
-
let entries = [...await kv.list(KV.audit)].sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
|
|
3819
|
-
if (filter?.operation) entries = entries.filter((e) => e.operation === filter.operation);
|
|
3820
|
-
if (filter?.dateFrom) {
|
|
3821
|
-
const from = new Date(filter.dateFrom).getTime();
|
|
3822
|
-
if (Number.isNaN(from)) throw new Error(`Invalid dateFrom: ${filter.dateFrom}`);
|
|
3823
|
-
entries = entries.filter((e) => new Date(e.timestamp).getTime() >= from);
|
|
3824
|
-
}
|
|
3825
|
-
if (filter?.dateTo) {
|
|
3826
|
-
const to = new Date(filter.dateTo).getTime();
|
|
3827
|
-
if (Number.isNaN(to)) throw new Error(`Invalid dateTo: ${filter.dateTo}`);
|
|
3828
|
-
entries = entries.filter((e) => new Date(e.timestamp).getTime() <= to);
|
|
3829
|
-
}
|
|
3830
|
-
return entries.slice(0, filter?.limit || 100);
|
|
3831
|
-
}
|
|
3832
|
-
|
|
3833
4047
|
//#endregion
|
|
3834
4048
|
//#region src/functions/vision-search.ts
|
|
3835
4049
|
function registerVisionSearchFunctions(sdk, kv, imageProvider) {
|
|
@@ -3953,7 +4167,6 @@ function cosine(a, b) {
|
|
|
3953
4167
|
const denom = Math.sqrt(normA) * Math.sqrt(normB);
|
|
3954
4168
|
return denom === 0 ? 0 : dot / denom;
|
|
3955
4169
|
}
|
|
3956
|
-
|
|
3957
4170
|
//#endregion
|
|
3958
4171
|
//#region src/functions/slots.ts
|
|
3959
4172
|
const DEFAULT_SIZE_LIMIT = 2e3;
|
|
@@ -4392,7 +4605,6 @@ function registerSlotsFunctions(sdk, kv) {
|
|
|
4392
4605
|
};
|
|
4393
4606
|
});
|
|
4394
4607
|
}
|
|
4395
|
-
|
|
4396
4608
|
//#endregion
|
|
4397
4609
|
//#region src/functions/disk-size-manager.ts
|
|
4398
4610
|
const DISK_SIZE_KEY = "system:currentDiskSize";
|
|
@@ -4407,7 +4619,11 @@ function registerDiskSizeManager(sdk, kv) {
|
|
|
4407
4619
|
if (newTotal < 0) newTotal = 0;
|
|
4408
4620
|
await kv.set(KV.state, DISK_SIZE_KEY, newTotal);
|
|
4409
4621
|
if (data.deltaBytes > 0 && newTotal > getMaxBytes()) {
|
|
4410
|
-
sdk.
|
|
4622
|
+
sdk.trigger({
|
|
4623
|
+
function_id: "mem::image-quota-cleanup",
|
|
4624
|
+
payload: {},
|
|
4625
|
+
action: TriggerAction.Void()
|
|
4626
|
+
});
|
|
4411
4627
|
logger.info("Disk quota exceeded, cleanup triggered", {
|
|
4412
4628
|
currentBytes: newTotal,
|
|
4413
4629
|
maxBytes: getMaxBytes()
|
|
@@ -4420,7 +4636,6 @@ function registerDiskSizeManager(sdk, kv) {
|
|
|
4420
4636
|
});
|
|
4421
4637
|
});
|
|
4422
4638
|
}
|
|
4423
|
-
|
|
4424
4639
|
//#endregion
|
|
4425
4640
|
//#region src/prompts/compression.ts
|
|
4426
4641
|
const COMPRESSION_SYSTEM = `You are a memory compression engine for an AI coding agent. Your job is to extract the essential information from a tool usage observation and compress it into structured data.
|
|
@@ -4468,7 +4683,6 @@ function buildCompressionPrompt(observation) {
|
|
|
4468
4683
|
function truncate$1(s, max) {
|
|
4469
4684
|
return s.length > max ? s.slice(0, max) + "\n[...truncated]" : s;
|
|
4470
4685
|
}
|
|
4471
|
-
|
|
4472
4686
|
//#endregion
|
|
4473
4687
|
//#region src/prompts/vision.ts
|
|
4474
4688
|
const VISION_DESCRIPTION_PROMPT = `Describe what this image shows in the context of software development. Extract:
|
|
@@ -4479,7 +4693,6 @@ const VISION_DESCRIPTION_PROMPT = `Describe what this image shows in the context
|
|
|
4479
4693
|
- Text content visible in the image
|
|
4480
4694
|
|
|
4481
4695
|
Be concise but preserve all technically relevant details. Output plain text, no XML.`;
|
|
4482
|
-
|
|
4483
4696
|
//#endregion
|
|
4484
4697
|
//#region src/prompts/xml.ts
|
|
4485
4698
|
const VALID_TAG = /^[a-zA-Z_][a-zA-Z0-9_-]*$/;
|
|
@@ -4498,7 +4711,6 @@ function getXmlChildren(xml, parentTag, childTag) {
|
|
|
4498
4711
|
while ((m = re.exec(parentMatch[1])) !== null) items.push(m[1].trim());
|
|
4499
4712
|
return items;
|
|
4500
4713
|
}
|
|
4501
|
-
|
|
4502
4714
|
//#endregion
|
|
4503
4715
|
//#region src/eval/schemas.ts
|
|
4504
4716
|
const HookTypeEnum = z.enum([
|
|
@@ -4531,7 +4743,7 @@ const ObservationTypeEnum = z.enum([
|
|
|
4531
4743
|
"task",
|
|
4532
4744
|
"other"
|
|
4533
4745
|
]);
|
|
4534
|
-
|
|
4746
|
+
z.object({
|
|
4535
4747
|
hookType: HookTypeEnum,
|
|
4536
4748
|
sessionId: z.string().min(1),
|
|
4537
4749
|
project: z.string().min(1),
|
|
@@ -4556,16 +4768,16 @@ const SummaryOutputSchema = z.object({
|
|
|
4556
4768
|
filesModified: z.array(z.string()),
|
|
4557
4769
|
concepts: z.array(z.string())
|
|
4558
4770
|
});
|
|
4559
|
-
|
|
4771
|
+
z.object({
|
|
4560
4772
|
query: z.string().min(1),
|
|
4561
4773
|
limit: z.number().int().positive().optional()
|
|
4562
4774
|
});
|
|
4563
|
-
|
|
4775
|
+
z.object({
|
|
4564
4776
|
sessionId: z.string().min(1),
|
|
4565
4777
|
project: z.string().min(1),
|
|
4566
4778
|
budget: z.number().positive().optional()
|
|
4567
4779
|
});
|
|
4568
|
-
|
|
4780
|
+
z.object({
|
|
4569
4781
|
content: z.string().min(1),
|
|
4570
4782
|
type: z.enum([
|
|
4571
4783
|
"pattern",
|
|
@@ -4578,22 +4790,22 @@ const RememberInputSchema = z.object({
|
|
|
4578
4790
|
concepts: z.array(z.string()).optional(),
|
|
4579
4791
|
files: z.array(z.string()).optional()
|
|
4580
4792
|
});
|
|
4581
|
-
|
|
4793
|
+
z.object({
|
|
4582
4794
|
query: z.string().optional(),
|
|
4583
4795
|
expandIds: z.array(z.string()).optional(),
|
|
4584
4796
|
limit: z.number().int().positive().optional()
|
|
4585
4797
|
});
|
|
4586
|
-
|
|
4798
|
+
z.object({
|
|
4587
4799
|
anchor: z.string().min(1),
|
|
4588
4800
|
project: z.string().optional(),
|
|
4589
4801
|
before: z.number().int().nonnegative().optional(),
|
|
4590
4802
|
after: z.number().int().nonnegative().optional()
|
|
4591
4803
|
});
|
|
4592
|
-
|
|
4804
|
+
z.object({
|
|
4593
4805
|
project: z.string().min(1),
|
|
4594
4806
|
refresh: z.boolean().optional()
|
|
4595
4807
|
});
|
|
4596
|
-
|
|
4808
|
+
z.object({
|
|
4597
4809
|
sourceId: z.string().min(1),
|
|
4598
4810
|
targetId: z.string().min(1),
|
|
4599
4811
|
type: z.enum([
|
|
@@ -4604,12 +4816,12 @@ const RelateInputSchema = z.object({
|
|
|
4604
4816
|
"related"
|
|
4605
4817
|
])
|
|
4606
4818
|
});
|
|
4607
|
-
|
|
4819
|
+
z.object({
|
|
4608
4820
|
memoryId: z.string().min(1),
|
|
4609
4821
|
newContent: z.string().min(1),
|
|
4610
4822
|
newTitle: z.string().optional()
|
|
4611
4823
|
});
|
|
4612
|
-
|
|
4824
|
+
z.object({
|
|
4613
4825
|
exportData: z.object({
|
|
4614
4826
|
version: z.union([z.literal("0.3.0"), z.literal("0.4.0")]),
|
|
4615
4827
|
exportedAt: z.string(),
|
|
@@ -4625,7 +4837,6 @@ const ExportImportInputSchema = z.object({
|
|
|
4625
4837
|
"skip"
|
|
4626
4838
|
]).optional()
|
|
4627
4839
|
});
|
|
4628
|
-
|
|
4629
4840
|
//#endregion
|
|
4630
4841
|
//#region src/eval/validator.ts
|
|
4631
4842
|
function validateInput(schema, data, functionId) {
|
|
@@ -4648,7 +4859,6 @@ function validateInput(schema, data, functionId) {
|
|
|
4648
4859
|
function validateOutput(schema, data, functionId) {
|
|
4649
4860
|
return validateInput(schema, data, functionId);
|
|
4650
4861
|
}
|
|
4651
|
-
|
|
4652
4862
|
//#endregion
|
|
4653
4863
|
//#region src/eval/quality.ts
|
|
4654
4864
|
function scoreCompression(obs) {
|
|
@@ -4672,7 +4882,6 @@ function scoreSummary(summary) {
|
|
|
4672
4882
|
if (summary.concepts && summary.concepts.length > 0) score += 15;
|
|
4673
4883
|
return Math.min(100, score);
|
|
4674
4884
|
}
|
|
4675
|
-
|
|
4676
4885
|
//#endregion
|
|
4677
4886
|
//#region src/eval/self-correct.ts
|
|
4678
4887
|
const STRICTER_SUFFIX = `
|
|
@@ -4696,7 +4905,6 @@ async function compressWithRetry(provider, systemPrompt, userPrompt, validator,
|
|
|
4696
4905
|
retried: true
|
|
4697
4906
|
};
|
|
4698
4907
|
}
|
|
4699
|
-
|
|
4700
4908
|
//#endregion
|
|
4701
4909
|
//#region src/functions/compress.ts
|
|
4702
4910
|
const VALID_TYPES$1 = new Set([
|
|
@@ -4877,7 +5085,6 @@ function registerCompressFunction(sdk, kv, provider, metricsStore) {
|
|
|
4877
5085
|
}
|
|
4878
5086
|
});
|
|
4879
5087
|
}
|
|
4880
|
-
|
|
4881
5088
|
//#endregion
|
|
4882
5089
|
//#region src/functions/context.ts
|
|
4883
5090
|
function estimateTokens$1(text) {
|
|
@@ -5002,7 +5209,6 @@ function registerContextFunction(sdk, kv, tokenBudget) {
|
|
|
5002
5209
|
};
|
|
5003
5210
|
});
|
|
5004
5211
|
}
|
|
5005
|
-
|
|
5006
5212
|
//#endregion
|
|
5007
5213
|
//#region src/prompts/summary.ts
|
|
5008
5214
|
const SUMMARY_SYSTEM = `You are a session summarizer for an AI coding agent's memory system. Given all compressed observations from a coding session, produce a concise session summary.
|
|
@@ -5074,7 +5280,6 @@ Concepts: ${concepts}`;
|
|
|
5074
5280
|
});
|
|
5075
5281
|
return `Partial summaries (${partials.length} chunks of one session, chronological):\n\n${sections.join("\n\n---\n\n")}`;
|
|
5076
5282
|
}
|
|
5077
|
-
|
|
5078
5283
|
//#endregion
|
|
5079
5284
|
//#region src/functions/summarize.ts
|
|
5080
5285
|
const CHUNK_SIZE_DEFAULT = 400;
|
|
@@ -5163,18 +5368,29 @@ async function produceSummaryXml(provider, compressed, sessionId, project) {
|
|
|
5163
5368
|
skipped
|
|
5164
5369
|
};
|
|
5165
5370
|
}
|
|
5371
|
+
function stripXmlWrappers(raw) {
|
|
5372
|
+
if (!raw) return "";
|
|
5373
|
+
let cleaned = raw.trim();
|
|
5374
|
+
cleaned = cleaned.replace(/```\s*xml\s*\n?/gi, "");
|
|
5375
|
+
cleaned = cleaned.replace(/```/g, "");
|
|
5376
|
+
cleaned = cleaned.trim();
|
|
5377
|
+
const rootMatch = cleaned.match(/(<[a-zA-Z_][a-zA-Z0-9_-]*>[\s\S]*<\/[a-zA-Z_][a-zA-Z0-9_-]*>)/);
|
|
5378
|
+
if (rootMatch && rootMatch[1]) return rootMatch[1].trim();
|
|
5379
|
+
return cleaned;
|
|
5380
|
+
}
|
|
5166
5381
|
function parseSummaryXml(xml, sessionId, project, obsCount) {
|
|
5167
|
-
const
|
|
5382
|
+
const cleaned = stripXmlWrappers(xml);
|
|
5383
|
+
const title = getXmlTag(cleaned, "title");
|
|
5168
5384
|
if (!title) return null;
|
|
5169
5385
|
return {
|
|
5170
5386
|
sessionId,
|
|
5171
5387
|
project,
|
|
5172
5388
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5173
5389
|
title,
|
|
5174
|
-
narrative: getXmlTag(
|
|
5175
|
-
keyDecisions: getXmlChildren(
|
|
5176
|
-
filesModified: getXmlChildren(
|
|
5177
|
-
concepts: getXmlChildren(
|
|
5390
|
+
narrative: getXmlTag(cleaned, "narrative"),
|
|
5391
|
+
keyDecisions: getXmlChildren(cleaned, "decisions", "decision"),
|
|
5392
|
+
filesModified: getXmlChildren(cleaned, "files", "file"),
|
|
5393
|
+
concepts: getXmlChildren(cleaned, "concepts", "concept"),
|
|
5178
5394
|
observationCount: obsCount
|
|
5179
5395
|
};
|
|
5180
5396
|
}
|
|
@@ -5211,27 +5427,44 @@ function registerSummarizeFunction(sdk, kv, provider, metricsStore) {
|
|
|
5211
5427
|
};
|
|
5212
5428
|
}
|
|
5213
5429
|
try {
|
|
5214
|
-
|
|
5430
|
+
let summary = null;
|
|
5431
|
+
let response = "";
|
|
5432
|
+
let mode = "single";
|
|
5433
|
+
let chunks = 1;
|
|
5434
|
+
for (let attempt = 1; attempt <= 2; attempt++) {
|
|
5435
|
+
const produced = await produceSummaryXml(provider, compressed, sessionId, session.project);
|
|
5436
|
+
response = produced.response;
|
|
5437
|
+
mode = produced.mode;
|
|
5438
|
+
chunks = produced.chunks;
|
|
5439
|
+
if (!response || !response.trim()) {
|
|
5440
|
+
logger.warn("Empty provider response on summarize", {
|
|
5441
|
+
sessionId,
|
|
5442
|
+
provider: provider.name,
|
|
5443
|
+
mode,
|
|
5444
|
+
chunks,
|
|
5445
|
+
observationCount: compressed.length,
|
|
5446
|
+
attempt
|
|
5447
|
+
});
|
|
5448
|
+
continue;
|
|
5449
|
+
}
|
|
5450
|
+
summary = parseSummaryXml(response, sessionId, session.project, compressed.length);
|
|
5451
|
+
if (summary) break;
|
|
5452
|
+
logger.warn("Failed to parse summary XML", {
|
|
5453
|
+
sessionId,
|
|
5454
|
+
attempt
|
|
5455
|
+
});
|
|
5456
|
+
}
|
|
5215
5457
|
if (!response || !response.trim()) {
|
|
5216
5458
|
const latencyMs = Date.now() - startMs;
|
|
5217
5459
|
if (metricsStore) await metricsStore.record("mem::summarize", latencyMs, false);
|
|
5218
|
-
logger.warn("Empty provider response on summarize", {
|
|
5219
|
-
sessionId,
|
|
5220
|
-
provider: provider.name,
|
|
5221
|
-
mode,
|
|
5222
|
-
chunks,
|
|
5223
|
-
observationCount: compressed.length
|
|
5224
|
-
});
|
|
5225
5460
|
return {
|
|
5226
5461
|
success: false,
|
|
5227
5462
|
error: "empty_provider_response"
|
|
5228
5463
|
};
|
|
5229
5464
|
}
|
|
5230
|
-
const summary = parseSummaryXml(response, sessionId, session.project, compressed.length);
|
|
5231
5465
|
if (!summary) {
|
|
5232
5466
|
const latencyMs = Date.now() - startMs;
|
|
5233
5467
|
if (metricsStore) await metricsStore.record("mem::summarize", latencyMs, false);
|
|
5234
|
-
logger.warn("Failed to parse summary XML", { sessionId });
|
|
5235
5468
|
return {
|
|
5236
5469
|
success: false,
|
|
5237
5470
|
error: "parse_failed"
|
|
@@ -5292,7 +5525,6 @@ function registerSummarizeFunction(sdk, kv, provider, metricsStore) {
|
|
|
5292
5525
|
}
|
|
5293
5526
|
});
|
|
5294
5527
|
}
|
|
5295
|
-
|
|
5296
5528
|
//#endregion
|
|
5297
5529
|
//#region src/functions/migrate.ts
|
|
5298
5530
|
const ALLOWED_DIRS = [resolve(homedir(), ".agentmemory")];
|
|
@@ -5493,7 +5725,6 @@ function safeJsonParse(value, fallback) {
|
|
|
5493
5725
|
}
|
|
5494
5726
|
return fallback;
|
|
5495
5727
|
}
|
|
5496
|
-
|
|
5497
5728
|
//#endregion
|
|
5498
5729
|
//#region src/functions/file-index.ts
|
|
5499
5730
|
function registerFileIndexFunction(sdk, kv) {
|
|
@@ -5563,7 +5794,6 @@ function registerFileIndexFunction(sdk, kv) {
|
|
|
5563
5794
|
return { context };
|
|
5564
5795
|
});
|
|
5565
5796
|
}
|
|
5566
|
-
|
|
5567
5797
|
//#endregion
|
|
5568
5798
|
//#region src/functions/consolidate.ts
|
|
5569
5799
|
const CONSOLIDATION_SYSTEM = `You are a memory consolidation engine. Given a set of related observations from coding sessions, synthesize them into a single long-term memory.
|
|
@@ -5715,7 +5945,6 @@ function registerConsolidateFunction(sdk, kv, provider) {
|
|
|
5715
5945
|
};
|
|
5716
5946
|
});
|
|
5717
5947
|
}
|
|
5718
|
-
|
|
5719
5948
|
//#endregion
|
|
5720
5949
|
//#region src/functions/patterns.ts
|
|
5721
5950
|
function registerPatternsFunction(sdk, kv) {
|
|
@@ -5799,7 +6028,6 @@ function registerPatternsFunction(sdk, kv) {
|
|
|
5799
6028
|
return { rules };
|
|
5800
6029
|
});
|
|
5801
6030
|
}
|
|
5802
|
-
|
|
5803
6031
|
//#endregion
|
|
5804
6032
|
//#region src/functions/remember.ts
|
|
5805
6033
|
function registerRememberFunction(sdk, kv) {
|
|
@@ -5960,7 +6188,6 @@ function registerRememberFunction(sdk, kv) {
|
|
|
5960
6188
|
};
|
|
5961
6189
|
});
|
|
5962
6190
|
}
|
|
5963
|
-
|
|
5964
6191
|
//#endregion
|
|
5965
6192
|
//#region src/functions/evict.ts
|
|
5966
6193
|
const MS_PER_DAY$1 = 1440 * 60 * 1e3;
|
|
@@ -6191,7 +6418,6 @@ function registerEvictFunction(sdk, kv) {
|
|
|
6191
6418
|
return stats;
|
|
6192
6419
|
});
|
|
6193
6420
|
}
|
|
6194
|
-
|
|
6195
6421
|
//#endregion
|
|
6196
6422
|
//#region src/functions/relations.ts
|
|
6197
6423
|
function computeConfidence(source, target, relationType) {
|
|
@@ -6379,7 +6605,6 @@ function registerRelationsFunction(sdk, kv) {
|
|
|
6379
6605
|
return { results: result };
|
|
6380
6606
|
});
|
|
6381
6607
|
}
|
|
6382
|
-
|
|
6383
6608
|
//#endregion
|
|
6384
6609
|
//#region src/functions/timeline.ts
|
|
6385
6610
|
function registerTimelineFunction(sdk, kv) {
|
|
@@ -6461,9 +6686,78 @@ async function findByKeyword(kv, keyword, project) {
|
|
|
6461
6686
|
}
|
|
6462
6687
|
return matches.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
|
|
6463
6688
|
}
|
|
6464
|
-
|
|
6689
|
+
//#endregion
|
|
6690
|
+
//#region src/version.ts
|
|
6691
|
+
const VERSION = "0.9.25";
|
|
6692
|
+
//#endregion
|
|
6693
|
+
//#region src/telemetry/setup.ts
|
|
6694
|
+
const OTEL_CONFIG = {
|
|
6695
|
+
serviceName: "agentmemory",
|
|
6696
|
+
serviceVersion: VERSION,
|
|
6697
|
+
metricsExportIntervalMs: 3e4
|
|
6698
|
+
};
|
|
6699
|
+
let counters = null;
|
|
6700
|
+
let histograms = null;
|
|
6701
|
+
const NOOP_COUNTER = { add: () => {} };
|
|
6702
|
+
const NOOP_HISTOGRAM = { record: () => {} };
|
|
6703
|
+
const COUNTER_NAMES = [
|
|
6704
|
+
["observationsTotal", "observations.total"],
|
|
6705
|
+
["compressionSuccess", "compression.success"],
|
|
6706
|
+
["compressionFailure", "compression.failure"],
|
|
6707
|
+
["searchTotal", "search.total"],
|
|
6708
|
+
["dedupSkipped", "dedup.skipped"],
|
|
6709
|
+
["evictionTotal", "eviction.total"],
|
|
6710
|
+
["circuitBreakerOpen", "circuit_breaker.open"],
|
|
6711
|
+
["embeddingSuccess", "embedding.success"],
|
|
6712
|
+
["embeddingFailure", "embedding.failure"],
|
|
6713
|
+
["vectorSearchTotal", "vector_search.total"],
|
|
6714
|
+
["autoForgetTotal", "auto_forget.total"],
|
|
6715
|
+
["profileGenerated", "profile.generated"],
|
|
6716
|
+
["claudeBridgeSync", "claude_bridge.sync"],
|
|
6717
|
+
["graphExtraction", "graph.extraction"],
|
|
6718
|
+
["consolidationRun", "consolidation.run"],
|
|
6719
|
+
["teamShare", "team.share"],
|
|
6720
|
+
["auditLog", "audit.log"],
|
|
6721
|
+
["snapshotCreate", "snapshot.create"],
|
|
6722
|
+
["governanceDelete", "governance.delete"],
|
|
6723
|
+
["smartSearchFollowupWithinWindow", "smart_search.followup_within_window_total"],
|
|
6724
|
+
["readerFailureWithEvidence", "reader_failure_with_evidence_total"]
|
|
6725
|
+
];
|
|
6726
|
+
const HISTOGRAM_NAMES = [
|
|
6727
|
+
["compressionLatency", "compression.latency_ms"],
|
|
6728
|
+
["searchLatency", "search.latency_ms"],
|
|
6729
|
+
["contextTokens", "context.tokens"],
|
|
6730
|
+
["qualityScore", "quality.score"],
|
|
6731
|
+
["embeddingLatency", "embedding.latency_ms"],
|
|
6732
|
+
["vectorSearchLatency", "vector_search.latency_ms"]
|
|
6733
|
+
];
|
|
6734
|
+
function getCounters() {
|
|
6735
|
+
if (counters) return counters;
|
|
6736
|
+
return Object.fromEntries(COUNTER_NAMES.map(([key]) => [key, NOOP_COUNTER]));
|
|
6737
|
+
}
|
|
6738
|
+
function initMetrics(getMeter) {
|
|
6739
|
+
const meter = getMeter?.("agentmemory");
|
|
6740
|
+
counters = Object.fromEntries(COUNTER_NAMES.map(([key, name]) => [key, meter ? meter.createCounter(name) : NOOP_COUNTER]));
|
|
6741
|
+
histograms = Object.fromEntries(HISTOGRAM_NAMES.map(([key, name]) => [key, meter ? meter.createHistogram(name) : NOOP_HISTOGRAM]));
|
|
6742
|
+
return {
|
|
6743
|
+
counters,
|
|
6744
|
+
histograms
|
|
6745
|
+
};
|
|
6746
|
+
}
|
|
6465
6747
|
//#endregion
|
|
6466
6748
|
//#region src/functions/smart-search.ts
|
|
6749
|
+
const followupStats = {
|
|
6750
|
+
followupWithinWindow: 0,
|
|
6751
|
+
agentInitiatedSearches: 0
|
|
6752
|
+
};
|
|
6753
|
+
const pendingFollowups = /* @__PURE__ */ new Set();
|
|
6754
|
+
function getFollowupStats() {
|
|
6755
|
+
const total = followupStats.agentInitiatedSearches;
|
|
6756
|
+
return {
|
|
6757
|
+
...followupStats,
|
|
6758
|
+
rate: total > 0 ? followupStats.followupWithinWindow / total : 0
|
|
6759
|
+
};
|
|
6760
|
+
}
|
|
6467
6761
|
const LESSON_CONTENT_PREVIEW_CHARS = 240;
|
|
6468
6762
|
function registerSmartSearchFunction(sdk, kv, searchFn) {
|
|
6469
6763
|
sdk.registerFunction("mem::smart-search", async (data) => {
|
|
@@ -6525,6 +6819,21 @@ function registerSmartSearchFunction(sdk, kv, searchFn) {
|
|
|
6525
6819
|
timestamp: r.observation.timestamp
|
|
6526
6820
|
}));
|
|
6527
6821
|
recordAccessBatch(kv, compact.map((r) => r.obsId));
|
|
6822
|
+
if (data.sessionId && typeof data.sessionId === "string" && data.source !== "viewer" && compact.length > 0) {
|
|
6823
|
+
followupStats.agentInitiatedSearches++;
|
|
6824
|
+
const sessionIdForFollowup = data.sessionId;
|
|
6825
|
+
const queryForFollowup = data.query;
|
|
6826
|
+
const compactForFollowup = compact;
|
|
6827
|
+
const detection = withKeyedLock(`recent-searches:${sessionIdForFollowup}`, () => detectFollowup(kv, sessionIdForFollowup, queryForFollowup, compactForFollowup)).catch((err) => {
|
|
6828
|
+
logger.warn("Smart search followup detection failed", {
|
|
6829
|
+
sessionId: sessionIdForFollowup,
|
|
6830
|
+
error: err instanceof Error ? err.message : String(err)
|
|
6831
|
+
});
|
|
6832
|
+
}).finally(() => {
|
|
6833
|
+
pendingFollowups.delete(detection);
|
|
6834
|
+
});
|
|
6835
|
+
pendingFollowups.add(detection);
|
|
6836
|
+
}
|
|
6528
6837
|
logger.info("Smart search compact", {
|
|
6529
6838
|
query: data.query,
|
|
6530
6839
|
results: compact.length,
|
|
@@ -6563,6 +6872,35 @@ async function recallLessons(sdk, query, limit, project) {
|
|
|
6563
6872
|
return [];
|
|
6564
6873
|
}
|
|
6565
6874
|
}
|
|
6875
|
+
async function detectFollowup(kv, sessionId, query, compact) {
|
|
6876
|
+
const now = Date.now();
|
|
6877
|
+
const windowMs = Math.max(1, getFollowupWindowSeconds()) * 1e3;
|
|
6878
|
+
const currentIds = compact.map((r) => r.obsId);
|
|
6879
|
+
const current = {
|
|
6880
|
+
sessionId,
|
|
6881
|
+
query,
|
|
6882
|
+
resultIds: currentIds,
|
|
6883
|
+
at: now
|
|
6884
|
+
};
|
|
6885
|
+
const prior = await kv.get(KV.recentSearches, sessionId).catch(() => null);
|
|
6886
|
+
await kv.set(KV.recentSearches, sessionId, current);
|
|
6887
|
+
if (!prior || typeof prior.at !== "number") return;
|
|
6888
|
+
if (now - prior.at > windowMs) return;
|
|
6889
|
+
if (typeof prior.query === "string" && prior.query === query) return;
|
|
6890
|
+
const priorIds = Array.isArray(prior.resultIds) ? prior.resultIds : [];
|
|
6891
|
+
const priorSet = new Set(priorIds);
|
|
6892
|
+
if (currentIds.some((id) => priorSet.has(id))) return;
|
|
6893
|
+
getCounters().smartSearchFollowupWithinWindow.add(1);
|
|
6894
|
+
followupStats.followupWithinWindow++;
|
|
6895
|
+
logger.info("Smart search followup detected", {
|
|
6896
|
+
sessionId,
|
|
6897
|
+
windowSeconds: Math.round(windowMs / 1e3),
|
|
6898
|
+
priorQuery: prior.query,
|
|
6899
|
+
nextQuery: query,
|
|
6900
|
+
priorResultCount: priorIds.length,
|
|
6901
|
+
nextResultCount: currentIds.length
|
|
6902
|
+
});
|
|
6903
|
+
}
|
|
6566
6904
|
async function findObservation$1(kv, obsId, sessionIdHint) {
|
|
6567
6905
|
if (sessionIdHint) {
|
|
6568
6906
|
const obs = await kv.get(KV.observations(sessionIdHint), obsId).catch(() => null);
|
|
@@ -6576,7 +6914,52 @@ async function findObservation$1(kv, obsId, sessionIdHint) {
|
|
|
6576
6914
|
}
|
|
6577
6915
|
return null;
|
|
6578
6916
|
}
|
|
6579
|
-
|
|
6917
|
+
//#endregion
|
|
6918
|
+
//#region src/functions/recent-searches-sweep.ts
|
|
6919
|
+
const RETENTION_MS = 1440 * 60 * 1e3;
|
|
6920
|
+
function registerRecentSearchesSweepFunction(sdk, kv) {
|
|
6921
|
+
sdk.registerFunction("mem::diagnostic::recent-searches-sweep", async () => {
|
|
6922
|
+
const cutoff = Date.now() - RETENTION_MS;
|
|
6923
|
+
const rows = await kv.list(KV.recentSearches).catch(() => []);
|
|
6924
|
+
let swept = 0;
|
|
6925
|
+
let skipped = 0;
|
|
6926
|
+
for (const row of rows) {
|
|
6927
|
+
if (!row || typeof row.sessionId !== "string" || !row.sessionId) {
|
|
6928
|
+
skipped++;
|
|
6929
|
+
continue;
|
|
6930
|
+
}
|
|
6931
|
+
if ((typeof row.at === "number" ? row.at : 0) >= cutoff) continue;
|
|
6932
|
+
try {
|
|
6933
|
+
await kv.delete(KV.recentSearches, row.sessionId);
|
|
6934
|
+
swept++;
|
|
6935
|
+
} catch (err) {
|
|
6936
|
+
logger.warn("recent-searches sweep delete failed", {
|
|
6937
|
+
sessionId: row.sessionId,
|
|
6938
|
+
error: err instanceof Error ? err.message : String(err)
|
|
6939
|
+
});
|
|
6940
|
+
}
|
|
6941
|
+
}
|
|
6942
|
+
if (swept > 0 || skipped > 0) logger.info("Recent-searches sweep complete", {
|
|
6943
|
+
swept,
|
|
6944
|
+
skipped
|
|
6945
|
+
});
|
|
6946
|
+
return {
|
|
6947
|
+
success: true,
|
|
6948
|
+
swept,
|
|
6949
|
+
skipped
|
|
6950
|
+
};
|
|
6951
|
+
});
|
|
6952
|
+
sdk.registerFunction("mem::diagnostic::followup-stats", async () => {
|
|
6953
|
+
const stats = getFollowupStats();
|
|
6954
|
+
return {
|
|
6955
|
+
success: true,
|
|
6956
|
+
windowSeconds: getFollowupWindowSeconds(),
|
|
6957
|
+
agentInitiatedSearches: stats.agentInitiatedSearches,
|
|
6958
|
+
followupWithinWindow: stats.followupWithinWindow,
|
|
6959
|
+
rate: stats.rate
|
|
6960
|
+
};
|
|
6961
|
+
});
|
|
6962
|
+
}
|
|
6580
6963
|
//#endregion
|
|
6581
6964
|
//#region src/functions/profile.ts
|
|
6582
6965
|
function registerProfileFunction(sdk, kv) {
|
|
@@ -6664,7 +7047,6 @@ function extractConventions(concepts, files) {
|
|
|
6664
7047
|
for (const { concept, frequency } of concepts.slice(0, 5)) if (frequency >= 3) conventions.push(`Frequently uses: ${concept}`);
|
|
6665
7048
|
return conventions;
|
|
6666
7049
|
}
|
|
6667
|
-
|
|
6668
7050
|
//#endregion
|
|
6669
7051
|
//#region src/functions/auto-forget.ts
|
|
6670
7052
|
const MS_PER_DAY = 1440 * 60 * 1e3;
|
|
@@ -6790,11 +7172,6 @@ function registerAutoForgetFunction(sdk, kv) {
|
|
|
6790
7172
|
return result;
|
|
6791
7173
|
});
|
|
6792
7174
|
}
|
|
6793
|
-
|
|
6794
|
-
//#endregion
|
|
6795
|
-
//#region src/version.ts
|
|
6796
|
-
const VERSION = "0.9.23";
|
|
6797
|
-
|
|
6798
7175
|
//#endregion
|
|
6799
7176
|
//#region src/functions/export-import.ts
|
|
6800
7177
|
function registerExportImportFunction(sdk, kv) {
|
|
@@ -6934,7 +7311,9 @@ function registerExportImportFunction(sdk, kv) {
|
|
|
6934
7311
|
"0.9.20",
|
|
6935
7312
|
"0.9.21",
|
|
6936
7313
|
"0.9.22",
|
|
6937
|
-
"0.9.23"
|
|
7314
|
+
"0.9.23",
|
|
7315
|
+
"0.9.24",
|
|
7316
|
+
"0.9.25"
|
|
6938
7317
|
]).has(importData.version)) return {
|
|
6939
7318
|
success: false,
|
|
6940
7319
|
error: `Unsupported export version: ${importData.version}`
|
|
@@ -7252,7 +7631,6 @@ function registerExportImportFunction(sdk, kv) {
|
|
|
7252
7631
|
};
|
|
7253
7632
|
});
|
|
7254
7633
|
}
|
|
7255
|
-
|
|
7256
7634
|
//#endregion
|
|
7257
7635
|
//#region src/functions/enrich.ts
|
|
7258
7636
|
const MAX_CONTEXT_LENGTH = 4e3;
|
|
@@ -7313,7 +7691,6 @@ function registerEnrichFunction(sdk, kv) {
|
|
|
7313
7691
|
};
|
|
7314
7692
|
});
|
|
7315
7693
|
}
|
|
7316
|
-
|
|
7317
7694
|
//#endregion
|
|
7318
7695
|
//#region src/functions/claude-bridge.ts
|
|
7319
7696
|
function parseMemoryMd(content) {
|
|
@@ -7434,7 +7811,6 @@ function registerClaudeBridgeFunction(sdk, kv, config) {
|
|
|
7434
7811
|
}
|
|
7435
7812
|
});
|
|
7436
7813
|
}
|
|
7437
|
-
|
|
7438
7814
|
//#endregion
|
|
7439
7815
|
//#region src/prompts/graph-extraction.ts
|
|
7440
7816
|
const GRAPH_EXTRACTION_SYSTEM = `You are a knowledge graph extraction engine. Given a compressed observation from a coding session, extract entities and relationships.
|
|
@@ -7457,9 +7833,41 @@ Rules:
|
|
|
7457
7833
|
function buildGraphExtractionPrompt(observations) {
|
|
7458
7834
|
return `Extract entities and relationships from these observations:\n\n${observations.map((o, i) => `[${i + 1}] Type: ${o.type}\nTitle: ${o.title}\nNarrative: ${o.narrative}\nConcepts: ${(o.concepts ?? []).join(", ")}\nFiles: ${(o.files ?? []).join(", ")}`).join("\n\n")}`;
|
|
7459
7835
|
}
|
|
7460
|
-
|
|
7461
7836
|
//#endregion
|
|
7462
7837
|
//#region src/functions/graph.ts
|
|
7838
|
+
const DEFAULT_GRAPH_QUERY_LIMIT = 500;
|
|
7839
|
+
const MAX_GRAPH_QUERY_LIMIT = 5e3;
|
|
7840
|
+
function resolvePagination(rawLimit, rawOffset) {
|
|
7841
|
+
return {
|
|
7842
|
+
limit: Math.max(1, Math.min(typeof rawLimit === "number" && Number.isFinite(rawLimit) ? Math.floor(rawLimit) : DEFAULT_GRAPH_QUERY_LIMIT, MAX_GRAPH_QUERY_LIMIT)),
|
|
7843
|
+
offset: Math.max(0, typeof rawOffset === "number" && Number.isFinite(rawOffset) ? Math.floor(rawOffset) : 0)
|
|
7844
|
+
};
|
|
7845
|
+
}
|
|
7846
|
+
function rankByDegree(nodes, edges) {
|
|
7847
|
+
const degree = /* @__PURE__ */ new Map();
|
|
7848
|
+
for (const edge of edges) {
|
|
7849
|
+
degree.set(edge.sourceNodeId, (degree.get(edge.sourceNodeId) ?? 0) + 1);
|
|
7850
|
+
degree.set(edge.targetNodeId, (degree.get(edge.targetNodeId) ?? 0) + 1);
|
|
7851
|
+
}
|
|
7852
|
+
return [...nodes].sort((a, b) => (degree.get(b.id) ?? 0) - (degree.get(a.id) ?? 0));
|
|
7853
|
+
}
|
|
7854
|
+
function paginate(nodes, allEdges, depth, limit, offset) {
|
|
7855
|
+
const totalNodes = nodes.length;
|
|
7856
|
+
const pageNodes = nodes.slice(offset, offset + limit);
|
|
7857
|
+
const pageNodeIds = new Set(pageNodes.map((n) => n.id));
|
|
7858
|
+
const pageEdges = allEdges.filter((e) => pageNodeIds.has(e.sourceNodeId) && pageNodeIds.has(e.targetNodeId));
|
|
7859
|
+
const universeIds = new Set(nodes.map((n) => n.id));
|
|
7860
|
+
return {
|
|
7861
|
+
nodes: pageNodes,
|
|
7862
|
+
edges: pageEdges,
|
|
7863
|
+
depth,
|
|
7864
|
+
totalNodes,
|
|
7865
|
+
totalEdges: allEdges.reduce((count, e) => universeIds.has(e.sourceNodeId) && universeIds.has(e.targetNodeId) ? count + 1 : count, 0),
|
|
7866
|
+
truncated: totalNodes > pageNodes.length,
|
|
7867
|
+
limit,
|
|
7868
|
+
offset
|
|
7869
|
+
};
|
|
7870
|
+
}
|
|
7463
7871
|
function parseAttrs(raw) {
|
|
7464
7872
|
const attrs = {};
|
|
7465
7873
|
const attrRegex = /([A-Za-z_][\w:-]*)="([^"]*)"/g;
|
|
@@ -7596,15 +8004,10 @@ function registerGraphFunction(sdk, kv, provider) {
|
|
|
7596
8004
|
const allNodes = (await kv.list(KV.graphNodes)).filter((n) => !n.stale);
|
|
7597
8005
|
const allEdges = (await kv.list(KV.graphEdges)).filter((e) => !e.stale);
|
|
7598
8006
|
const maxDepth = Math.min(data.maxDepth || 3, 5);
|
|
8007
|
+
const { limit, offset } = resolvePagination(data.limit, data.offset);
|
|
7599
8008
|
if (data.query) {
|
|
7600
8009
|
const lower = data.query.toLowerCase();
|
|
7601
|
-
|
|
7602
|
-
const nodeIds = new Set(matchingNodes.map((n) => n.id));
|
|
7603
|
-
return {
|
|
7604
|
-
nodes: matchingNodes,
|
|
7605
|
-
edges: allEdges.filter((e) => nodeIds.has(e.sourceNodeId) || nodeIds.has(e.targetNodeId)),
|
|
7606
|
-
depth: 0
|
|
7607
|
-
};
|
|
8010
|
+
return paginate(allNodes.filter((n) => n.name.toLowerCase().includes(lower) || Object.values(n.properties).some((v) => typeof v === "string" && v.toLowerCase().includes(lower))), allEdges, 0, limit, offset);
|
|
7608
8011
|
}
|
|
7609
8012
|
if (data.startNodeId) {
|
|
7610
8013
|
const visited = /* @__PURE__ */ new Set();
|
|
@@ -7636,19 +8039,11 @@ function registerGraphFunction(sdk, kv, provider) {
|
|
|
7636
8039
|
});
|
|
7637
8040
|
}
|
|
7638
8041
|
}
|
|
7639
|
-
return
|
|
7640
|
-
nodes: resultNodes,
|
|
7641
|
-
edges: resultEdges,
|
|
7642
|
-
depth: maxDepth
|
|
7643
|
-
};
|
|
8042
|
+
return paginate(resultNodes, resultEdges, maxDepth, limit, offset);
|
|
7644
8043
|
}
|
|
7645
8044
|
let filtered = allNodes;
|
|
7646
8045
|
if (data.nodeType) filtered = allNodes.filter((n) => n.type === data.nodeType);
|
|
7647
|
-
return
|
|
7648
|
-
nodes: filtered,
|
|
7649
|
-
edges: allEdges,
|
|
7650
|
-
depth: 0
|
|
7651
|
-
};
|
|
8046
|
+
return paginate(rankByDegree(filtered, allEdges), allEdges, 0, limit, offset);
|
|
7652
8047
|
});
|
|
7653
8048
|
sdk.registerFunction("mem::graph-stats", async () => {
|
|
7654
8049
|
const nodes = await kv.list(KV.graphNodes);
|
|
@@ -7665,7 +8060,6 @@ function registerGraphFunction(sdk, kv, provider) {
|
|
|
7665
8060
|
};
|
|
7666
8061
|
});
|
|
7667
8062
|
}
|
|
7668
|
-
|
|
7669
8063
|
//#endregion
|
|
7670
8064
|
//#region src/prompts/consolidation.ts
|
|
7671
8065
|
const SEMANTIC_MERGE_SYSTEM = `You are a memory consolidation engine. Given overlapping episodic memories (session summaries), extract stable factual knowledge.
|
|
@@ -7700,7 +8094,6 @@ Rules:
|
|
|
7700
8094
|
function buildProceduralExtractionPrompt(patterns) {
|
|
7701
8095
|
return `Extract reusable procedures from these recurring patterns:\n\n${patterns.map((p, i) => `[Pattern ${i + 1}] (seen ${p.frequency}x)\n${p.content}`).join("\n\n")}`;
|
|
7702
8096
|
}
|
|
7703
|
-
|
|
7704
8097
|
//#endregion
|
|
7705
8098
|
//#region src/functions/consolidation-pipeline.ts
|
|
7706
8099
|
function applyDecay(items, decayDays) {
|
|
@@ -7894,7 +8287,6 @@ function registerConsolidationPipelineFunction(sdk, kv, provider) {
|
|
|
7894
8287
|
};
|
|
7895
8288
|
});
|
|
7896
8289
|
}
|
|
7897
|
-
|
|
7898
8290
|
//#endregion
|
|
7899
8291
|
//#region src/functions/team.ts
|
|
7900
8292
|
const VALID_ITEM_TYPES = new Set([
|
|
@@ -7998,7 +8390,6 @@ function registerTeamFunction(sdk, kv, config) {
|
|
|
7998
8390
|
return profile;
|
|
7999
8391
|
});
|
|
8000
8392
|
}
|
|
8001
|
-
|
|
8002
8393
|
//#endregion
|
|
8003
8394
|
//#region src/functions/governance.ts
|
|
8004
8395
|
function registerGovernanceFunction(sdk, kv) {
|
|
@@ -8107,7 +8498,6 @@ function registerGovernanceFunction(sdk, kv) {
|
|
|
8107
8498
|
return queryAudit(kv, data);
|
|
8108
8499
|
});
|
|
8109
8500
|
}
|
|
8110
|
-
|
|
8111
8501
|
//#endregion
|
|
8112
8502
|
//#region src/functions/snapshot.ts
|
|
8113
8503
|
const COMMIT_HASH_RE = /^[0-9a-f]{7,40}$/i;
|
|
@@ -8272,7 +8662,6 @@ function registerSnapshotFunction(sdk, kv, snapshotDir) {
|
|
|
8272
8662
|
}
|
|
8273
8663
|
});
|
|
8274
8664
|
}
|
|
8275
|
-
|
|
8276
8665
|
//#endregion
|
|
8277
8666
|
//#region src/functions/actions.ts
|
|
8278
8667
|
function registerActionsFunction(sdk, kv) {
|
|
@@ -8474,7 +8863,6 @@ async function propagateCompletion(kv, completedActionId) {
|
|
|
8474
8863
|
});
|
|
8475
8864
|
}
|
|
8476
8865
|
}
|
|
8477
|
-
|
|
8478
8866
|
//#endregion
|
|
8479
8867
|
//#region src/functions/frontier.ts
|
|
8480
8868
|
function registerFrontierFunction(sdk, kv) {
|
|
@@ -8579,7 +8967,6 @@ function computeScore(action, edges, now) {
|
|
|
8579
8967
|
if (action.status === "active") score += 15;
|
|
8580
8968
|
return Math.round(score * 100) / 100;
|
|
8581
8969
|
}
|
|
8582
|
-
|
|
8583
8970
|
//#endregion
|
|
8584
8971
|
//#region src/functions/leases.ts
|
|
8585
8972
|
const DEFAULT_LEASE_TTL_MS = 600 * 1e3;
|
|
@@ -8760,7 +9147,6 @@ function registerLeasesFunction(sdk, kv) {
|
|
|
8760
9147
|
};
|
|
8761
9148
|
});
|
|
8762
9149
|
}
|
|
8763
|
-
|
|
8764
9150
|
//#endregion
|
|
8765
9151
|
//#region src/functions/routines.ts
|
|
8766
9152
|
function registerRoutinesFunction(sdk, kv) {
|
|
@@ -9007,7 +9393,6 @@ function registerRoutinesFunction(sdk, kv) {
|
|
|
9007
9393
|
});
|
|
9008
9394
|
});
|
|
9009
9395
|
}
|
|
9010
|
-
|
|
9011
9396
|
//#endregion
|
|
9012
9397
|
//#region src/functions/signals.ts
|
|
9013
9398
|
function registerSignalsFunction(sdk, kv) {
|
|
@@ -9139,7 +9524,6 @@ function registerSignalsFunction(sdk, kv) {
|
|
|
9139
9524
|
};
|
|
9140
9525
|
});
|
|
9141
9526
|
}
|
|
9142
|
-
|
|
9143
9527
|
//#endregion
|
|
9144
9528
|
//#region src/functions/checkpoints.ts
|
|
9145
9529
|
function registerCheckpointsFunction(sdk, kv) {
|
|
@@ -9312,7 +9696,6 @@ function registerCheckpointsFunction(sdk, kv) {
|
|
|
9312
9696
|
};
|
|
9313
9697
|
});
|
|
9314
9698
|
}
|
|
9315
|
-
|
|
9316
9699
|
//#endregion
|
|
9317
9700
|
//#region src/functions/flow-compress.ts
|
|
9318
9701
|
const FLOW_COMPRESS_SYSTEM = `You are a workflow summarizer. Given a completed action chain, produce a concise summary capturing:
|
|
@@ -9457,7 +9840,6 @@ function extractFiles(actions) {
|
|
|
9457
9840
|
}
|
|
9458
9841
|
return Array.from(files);
|
|
9459
9842
|
}
|
|
9460
|
-
|
|
9461
9843
|
//#endregion
|
|
9462
9844
|
//#region src/functions/mesh.ts
|
|
9463
9845
|
function isPrivateIP(ip) {
|
|
@@ -9774,7 +10156,6 @@ async function applySyncData(kv, data, scopes) {
|
|
|
9774
10156
|
if (scopes.includes("graph:edges")) applied += await lwwMergeList(kv, KV.graphEdges, data.graphEdges, "mem:gedge", "createdAt");
|
|
9775
10157
|
return applied;
|
|
9776
10158
|
}
|
|
9777
|
-
|
|
9778
10159
|
//#endregion
|
|
9779
10160
|
//#region src/functions/branch-aware.ts
|
|
9780
10161
|
function execAsync(cmd, args, cwd) {
|
|
@@ -9889,7 +10270,6 @@ function registerBranchAwareFunction(sdk, kv) {
|
|
|
9889
10270
|
};
|
|
9890
10271
|
});
|
|
9891
10272
|
}
|
|
9892
|
-
|
|
9893
10273
|
//#endregion
|
|
9894
10274
|
//#region src/functions/sentinels.ts
|
|
9895
10275
|
const VALID_TYPES = [
|
|
@@ -10210,7 +10590,6 @@ async function unblockLinkedActions(kv, sentinel) {
|
|
|
10210
10590
|
});
|
|
10211
10591
|
return unblockedCount;
|
|
10212
10592
|
}
|
|
10213
|
-
|
|
10214
10593
|
//#endregion
|
|
10215
10594
|
//#region src/functions/sketches.ts
|
|
10216
10595
|
function registerSketchesFunction(sdk, kv) {
|
|
@@ -10452,7 +10831,6 @@ function registerSketchesFunction(sdk, kv) {
|
|
|
10452
10831
|
};
|
|
10453
10832
|
});
|
|
10454
10833
|
}
|
|
10455
|
-
|
|
10456
10834
|
//#endregion
|
|
10457
10835
|
//#region src/functions/crystallize.ts
|
|
10458
10836
|
const CRYSTALLIZE_SYSTEM = `You are summarizing a completed chain of agent actions into a compact digest.
|
|
@@ -10649,7 +11027,6 @@ function parseDigest(response) {
|
|
|
10649
11027
|
};
|
|
10650
11028
|
}
|
|
10651
11029
|
}
|
|
10652
|
-
|
|
10653
11030
|
//#endregion
|
|
10654
11031
|
//#region src/functions/diagnostics.ts
|
|
10655
11032
|
const ALL_CATEGORIES = [
|
|
@@ -11363,7 +11740,6 @@ function registerDiagnosticsFunction(sdk, kv) {
|
|
|
11363
11740
|
};
|
|
11364
11741
|
});
|
|
11365
11742
|
}
|
|
11366
|
-
|
|
11367
11743
|
//#endregion
|
|
11368
11744
|
//#region src/functions/facets.ts
|
|
11369
11745
|
function registerFacetsFunction(sdk, kv) {
|
|
@@ -11535,7 +11911,6 @@ function registerFacetsFunction(sdk, kv) {
|
|
|
11535
11911
|
};
|
|
11536
11912
|
});
|
|
11537
11913
|
}
|
|
11538
|
-
|
|
11539
11914
|
//#endregion
|
|
11540
11915
|
//#region src/functions/verify.ts
|
|
11541
11916
|
function registerVerifyFunction(sdk, kv) {
|
|
@@ -11630,7 +12005,6 @@ async function findObservation(kv, obsId, hintSessionIds) {
|
|
|
11630
12005
|
}
|
|
11631
12006
|
return null;
|
|
11632
12007
|
}
|
|
11633
|
-
|
|
11634
12008
|
//#endregion
|
|
11635
12009
|
//#region src/functions/cascade.ts
|
|
11636
12010
|
function registerCascadeFunction(sdk, kv) {
|
|
@@ -11700,7 +12074,6 @@ function registerCascadeFunction(sdk, kv) {
|
|
|
11700
12074
|
};
|
|
11701
12075
|
});
|
|
11702
12076
|
}
|
|
11703
|
-
|
|
11704
12077
|
//#endregion
|
|
11705
12078
|
//#region src/functions/lessons.ts
|
|
11706
12079
|
function reinforceLesson(lesson) {
|
|
@@ -11886,7 +12259,6 @@ function registerLessonsFunctions(sdk, kv) {
|
|
|
11886
12259
|
};
|
|
11887
12260
|
});
|
|
11888
12261
|
}
|
|
11889
|
-
|
|
11890
12262
|
//#endregion
|
|
11891
12263
|
//#region src/functions/obsidian-export.ts
|
|
11892
12264
|
const DEFAULT_EXPORT_ROOT = join(homedir(), ".agentmemory");
|
|
@@ -11902,6 +12274,20 @@ function resolveVaultDir(vaultDir) {
|
|
|
11902
12274
|
function sanitize(name) {
|
|
11903
12275
|
return name.replace(/[<>:"/\\|?*\x00-\x1f]/g, "_").slice(0, 100);
|
|
11904
12276
|
}
|
|
12277
|
+
function hasExportId(item) {
|
|
12278
|
+
return !!item && typeof item.id === "string" && item.id.length > 0;
|
|
12279
|
+
}
|
|
12280
|
+
function safeArray(value) {
|
|
12281
|
+
return Array.isArray(value) ? value : [];
|
|
12282
|
+
}
|
|
12283
|
+
function safeString(value, fallback = "") {
|
|
12284
|
+
return typeof value === "string" ? value : fallback;
|
|
12285
|
+
}
|
|
12286
|
+
function safeTimestamp(value) {
|
|
12287
|
+
if (typeof value !== "string") return 0;
|
|
12288
|
+
const time = new Date(value).getTime();
|
|
12289
|
+
return Number.isFinite(time) ? time : 0;
|
|
12290
|
+
}
|
|
11905
12291
|
function toFrontmatter(obj) {
|
|
11906
12292
|
const lines = ["---"];
|
|
11907
12293
|
for (const [key, value] of Object.entries(obj)) {
|
|
@@ -11913,6 +12299,11 @@ function toFrontmatter(obj) {
|
|
|
11913
12299
|
return lines.join("\n");
|
|
11914
12300
|
}
|
|
11915
12301
|
function memoryToMd(m) {
|
|
12302
|
+
const concepts = safeArray(m.concepts);
|
|
12303
|
+
const files = safeArray(m.files);
|
|
12304
|
+
const relatedIds = safeArray(m.relatedIds);
|
|
12305
|
+
const supersedes = safeArray(m.supersedes);
|
|
12306
|
+
const title = safeString(m.title, m.id);
|
|
11916
12307
|
const fm = toFrontmatter({
|
|
11917
12308
|
id: m.id,
|
|
11918
12309
|
type: m.type,
|
|
@@ -11920,24 +12311,28 @@ function memoryToMd(m) {
|
|
|
11920
12311
|
updated: m.updatedAt,
|
|
11921
12312
|
strength: m.strength,
|
|
11922
12313
|
version: m.version,
|
|
11923
|
-
concepts
|
|
11924
|
-
files
|
|
12314
|
+
concepts,
|
|
12315
|
+
files
|
|
11925
12316
|
});
|
|
11926
|
-
const
|
|
11927
|
-
const
|
|
12317
|
+
const relatedLines = relatedIds.map((id) => `- [[${id}]]`).join("\n");
|
|
12318
|
+
const supersedesLines = supersedes.map((id) => `- [[${id}]] (superseded)`).join("\n");
|
|
11928
12319
|
const sections = [
|
|
11929
12320
|
fm,
|
|
11930
12321
|
"",
|
|
11931
|
-
`# ${
|
|
12322
|
+
`# ${title}`,
|
|
11932
12323
|
"",
|
|
11933
|
-
m.content
|
|
12324
|
+
safeString(m.content)
|
|
11934
12325
|
];
|
|
11935
|
-
if (
|
|
11936
|
-
if (
|
|
11937
|
-
if (
|
|
12326
|
+
if (concepts.length > 0) sections.push("", "## Concepts", concepts.map((c) => `#${c.replace(/\s+/g, "-")}`).join(" "));
|
|
12327
|
+
if (relatedLines) sections.push("", "## Related", relatedLines);
|
|
12328
|
+
if (supersedesLines) sections.push("", "## Supersedes", supersedesLines);
|
|
11938
12329
|
return sections.join("\n");
|
|
11939
12330
|
}
|
|
11940
12331
|
function lessonToMd(l) {
|
|
12332
|
+
const tags = safeArray(l.tags);
|
|
12333
|
+
const sourceIds = safeArray(l.sourceIds);
|
|
12334
|
+
const content = safeString(l.content);
|
|
12335
|
+
const headline = content ? content.slice(0, 80) : l.id;
|
|
11941
12336
|
const fm = toFrontmatter({
|
|
11942
12337
|
id: l.id,
|
|
11943
12338
|
type: "lesson",
|
|
@@ -11947,66 +12342,76 @@ function lessonToMd(l) {
|
|
|
11947
12342
|
created: l.createdAt,
|
|
11948
12343
|
updated: l.updatedAt,
|
|
11949
12344
|
project: l.project,
|
|
11950
|
-
tags
|
|
12345
|
+
tags,
|
|
11951
12346
|
decayRate: l.decayRate
|
|
11952
12347
|
});
|
|
11953
|
-
const sourceLinks =
|
|
12348
|
+
const sourceLinks = sourceIds.map((id) => `- [[${id}]]`).join("\n");
|
|
11954
12349
|
const sections = [
|
|
11955
12350
|
fm,
|
|
11956
12351
|
"",
|
|
11957
|
-
`# Lesson: ${
|
|
12352
|
+
`# Lesson: ${headline}`,
|
|
11958
12353
|
"",
|
|
11959
|
-
|
|
12354
|
+
content
|
|
11960
12355
|
];
|
|
11961
12356
|
if (l.context) sections.push("", "## Context", l.context);
|
|
11962
|
-
if (
|
|
12357
|
+
if (tags.length > 0) sections.push("", "## Tags", tags.map((t) => `#${t.replace(/\s+/g, "-")}`).join(" "));
|
|
11963
12358
|
if (sourceLinks) sections.push("", "## Sources", sourceLinks);
|
|
11964
12359
|
return sections.join("\n");
|
|
11965
12360
|
}
|
|
11966
12361
|
function crystalToMd(c) {
|
|
12362
|
+
const keyOutcomes = safeArray(c.keyOutcomes);
|
|
12363
|
+
const lessons = safeArray(c.lessons);
|
|
12364
|
+
const filesAffected = safeArray(c.filesAffected);
|
|
12365
|
+
const sourceActionIds = safeArray(c.sourceActionIds);
|
|
12366
|
+
const narrative = safeString(c.narrative);
|
|
12367
|
+
const headline = narrative ? narrative.slice(0, 80) : c.id;
|
|
11967
12368
|
const fm = toFrontmatter({
|
|
11968
12369
|
id: c.id,
|
|
11969
12370
|
type: "crystal",
|
|
11970
12371
|
created: c.createdAt,
|
|
11971
12372
|
project: c.project,
|
|
11972
12373
|
sessionId: c.sessionId,
|
|
11973
|
-
filesAffected
|
|
12374
|
+
filesAffected
|
|
11974
12375
|
});
|
|
11975
|
-
const actionLinks =
|
|
12376
|
+
const actionLinks = sourceActionIds.map((id) => `- [[${id}]]`).join("\n");
|
|
11976
12377
|
const sections = [
|
|
11977
12378
|
fm,
|
|
11978
12379
|
"",
|
|
11979
|
-
`# Crystal: ${
|
|
12380
|
+
`# Crystal: ${headline}`,
|
|
11980
12381
|
"",
|
|
11981
|
-
|
|
12382
|
+
narrative,
|
|
11982
12383
|
"",
|
|
11983
12384
|
"## Key Outcomes",
|
|
11984
|
-
...
|
|
12385
|
+
...keyOutcomes.map((o) => `- ${o}`)
|
|
11985
12386
|
];
|
|
11986
|
-
if (
|
|
11987
|
-
if (
|
|
12387
|
+
if (lessons.length > 0) sections.push("", "## Lessons", ...lessons.map((l) => `- ${l}`));
|
|
12388
|
+
if (filesAffected.length > 0) sections.push("", "## Files", ...filesAffected.map((f) => `- \`${f}\``));
|
|
11988
12389
|
if (actionLinks) sections.push("", "## Source Actions", actionLinks);
|
|
11989
12390
|
return sections.join("\n");
|
|
11990
12391
|
}
|
|
11991
12392
|
function sessionToMd(s) {
|
|
12393
|
+
const project = safeString(s.project, "unknown");
|
|
12394
|
+
const status = safeString(s.status, "unknown");
|
|
12395
|
+
const startedAt = safeString(s.startedAt, "");
|
|
12396
|
+
const cwd = safeString(s.cwd, "");
|
|
11992
12397
|
return [
|
|
11993
12398
|
toFrontmatter({
|
|
11994
12399
|
id: s.id,
|
|
11995
12400
|
type: "session",
|
|
11996
|
-
project
|
|
11997
|
-
status
|
|
11998
|
-
started:
|
|
12401
|
+
project,
|
|
12402
|
+
status,
|
|
12403
|
+
started: startedAt || void 0,
|
|
11999
12404
|
ended: s.endedAt,
|
|
12000
12405
|
observations: s.observationCount
|
|
12001
12406
|
}),
|
|
12002
12407
|
"",
|
|
12003
|
-
`# Session: ${
|
|
12408
|
+
`# Session: ${project}`,
|
|
12004
12409
|
"",
|
|
12005
|
-
`**Status:** ${
|
|
12006
|
-
`**Started:** ${
|
|
12410
|
+
`**Status:** ${status}`,
|
|
12411
|
+
startedAt ? `**Started:** ${startedAt}` : "",
|
|
12007
12412
|
s.endedAt ? `**Ended:** ${s.endedAt}` : "",
|
|
12008
|
-
`**Observations:** ${s.observationCount}`,
|
|
12009
|
-
`**CWD:** \`${
|
|
12413
|
+
`**Observations:** ${s.observationCount ?? 0}`,
|
|
12414
|
+
cwd ? `**CWD:** \`${cwd}\`` : ""
|
|
12010
12415
|
].filter(Boolean).join("\n");
|
|
12011
12416
|
}
|
|
12012
12417
|
function registerObsidianExportFunction(sdk, kv) {
|
|
@@ -12042,122 +12447,131 @@ function registerObsidianExportFunction(sdk, kv) {
|
|
|
12042
12447
|
crystals: join(vaultDir, "crystals"),
|
|
12043
12448
|
sessions: join(vaultDir, "sessions")
|
|
12044
12449
|
};
|
|
12045
|
-
|
|
12046
|
-
|
|
12047
|
-
|
|
12048
|
-
|
|
12049
|
-
|
|
12050
|
-
|
|
12051
|
-
|
|
12052
|
-
|
|
12053
|
-
|
|
12054
|
-
|
|
12055
|
-
|
|
12056
|
-
|
|
12057
|
-
|
|
12058
|
-
|
|
12059
|
-
|
|
12060
|
-
|
|
12061
|
-
|
|
12062
|
-
|
|
12063
|
-
|
|
12064
|
-
const
|
|
12065
|
-
|
|
12066
|
-
|
|
12067
|
-
|
|
12068
|
-
|
|
12069
|
-
|
|
12070
|
-
|
|
12071
|
-
|
|
12072
|
-
|
|
12073
|
-
|
|
12074
|
-
|
|
12075
|
-
|
|
12450
|
+
try {
|
|
12451
|
+
await Promise.all(Object.values(dirs).map((dir) => mkdir(dir, { recursive: true })));
|
|
12452
|
+
const stats = {
|
|
12453
|
+
memories: 0,
|
|
12454
|
+
lessons: 0,
|
|
12455
|
+
crystals: 0,
|
|
12456
|
+
sessions: 0
|
|
12457
|
+
};
|
|
12458
|
+
const errors = [];
|
|
12459
|
+
const memoryMoc = [];
|
|
12460
|
+
const lessonMoc = [];
|
|
12461
|
+
const crystalMoc = [];
|
|
12462
|
+
const sessionMoc = [];
|
|
12463
|
+
const [memories, lessons, crystals, sessions] = await Promise.all([
|
|
12464
|
+
exportTypes.has("memories") ? kv.list(KV.memories) : Promise.resolve([]),
|
|
12465
|
+
exportTypes.has("lessons") ? kv.list(KV.lessons) : Promise.resolve([]),
|
|
12466
|
+
exportTypes.has("crystals") ? kv.list(KV.crystals) : Promise.resolve([]),
|
|
12467
|
+
exportTypes.has("sessions") ? kv.list(KV.sessions) : Promise.resolve([])
|
|
12468
|
+
]);
|
|
12469
|
+
for (const m of memories.filter((m) => hasExportId(m) && m.isLatest === true)) {
|
|
12470
|
+
const filename = `${sanitize(m.id)}.md`;
|
|
12471
|
+
const filepath = join(dirs.memories, filename);
|
|
12472
|
+
try {
|
|
12473
|
+
await writeFile(filepath, memoryToMd(m));
|
|
12474
|
+
stats.memories++;
|
|
12475
|
+
memoryMoc.push(`- [[memories/${sanitize(m.id)}|${safeString(m.title, m.id)}]] (${m.type}, strength: ${m.strength ?? 0})`);
|
|
12476
|
+
} catch (err) {
|
|
12477
|
+
errors.push({
|
|
12478
|
+
id: m.id,
|
|
12479
|
+
path: filepath,
|
|
12480
|
+
error: err instanceof Error ? err.message : String(err)
|
|
12481
|
+
});
|
|
12482
|
+
}
|
|
12076
12483
|
}
|
|
12077
|
-
|
|
12078
|
-
|
|
12079
|
-
|
|
12080
|
-
|
|
12081
|
-
|
|
12082
|
-
|
|
12083
|
-
|
|
12084
|
-
|
|
12085
|
-
|
|
12086
|
-
|
|
12087
|
-
|
|
12088
|
-
|
|
12089
|
-
|
|
12090
|
-
|
|
12484
|
+
for (const l of lessons.filter((l) => hasExportId(l) && !l.deleted)) {
|
|
12485
|
+
const filename = `${sanitize(l.id)}.md`;
|
|
12486
|
+
const filepath = join(dirs.lessons, filename);
|
|
12487
|
+
try {
|
|
12488
|
+
await writeFile(filepath, lessonToMd(l));
|
|
12489
|
+
stats.lessons++;
|
|
12490
|
+
const headline = safeString(l.content).slice(0, 60) || l.id;
|
|
12491
|
+
lessonMoc.push(`- [[lessons/${sanitize(l.id)}|${headline}]] (confidence: ${l.confidence ?? 0})`);
|
|
12492
|
+
} catch (err) {
|
|
12493
|
+
errors.push({
|
|
12494
|
+
id: l.id,
|
|
12495
|
+
path: filepath,
|
|
12496
|
+
error: err instanceof Error ? err.message : String(err)
|
|
12497
|
+
});
|
|
12498
|
+
}
|
|
12091
12499
|
}
|
|
12092
|
-
|
|
12093
|
-
|
|
12094
|
-
|
|
12095
|
-
|
|
12096
|
-
|
|
12097
|
-
|
|
12098
|
-
|
|
12099
|
-
|
|
12100
|
-
|
|
12101
|
-
|
|
12102
|
-
|
|
12103
|
-
|
|
12104
|
-
|
|
12105
|
-
|
|
12500
|
+
for (const c of crystals.filter(hasExportId)) {
|
|
12501
|
+
const filename = `${sanitize(c.id)}.md`;
|
|
12502
|
+
const filepath = join(dirs.crystals, filename);
|
|
12503
|
+
try {
|
|
12504
|
+
await writeFile(filepath, crystalToMd(c));
|
|
12505
|
+
stats.crystals++;
|
|
12506
|
+
const headline = safeString(c.narrative).slice(0, 60) || c.id;
|
|
12507
|
+
crystalMoc.push(`- [[crystals/${sanitize(c.id)}|${headline}]]`);
|
|
12508
|
+
} catch (err) {
|
|
12509
|
+
errors.push({
|
|
12510
|
+
id: c.id,
|
|
12511
|
+
path: filepath,
|
|
12512
|
+
error: err instanceof Error ? err.message : String(err)
|
|
12513
|
+
});
|
|
12514
|
+
}
|
|
12106
12515
|
}
|
|
12107
|
-
|
|
12108
|
-
|
|
12109
|
-
|
|
12110
|
-
|
|
12111
|
-
|
|
12112
|
-
|
|
12113
|
-
|
|
12114
|
-
|
|
12115
|
-
|
|
12116
|
-
|
|
12117
|
-
|
|
12118
|
-
|
|
12119
|
-
|
|
12120
|
-
|
|
12121
|
-
}
|
|
12516
|
+
const recent = sessions.filter(hasExportId).sort((a, b) => safeTimestamp(b.startedAt) - safeTimestamp(a.startedAt)).slice(0, 50);
|
|
12517
|
+
for (const s of recent) {
|
|
12518
|
+
const filename = `${sanitize(s.id)}.md`;
|
|
12519
|
+
const filepath = join(dirs.sessions, filename);
|
|
12520
|
+
try {
|
|
12521
|
+
await writeFile(filepath, sessionToMd(s));
|
|
12522
|
+
stats.sessions++;
|
|
12523
|
+
sessionMoc.push(`- [[sessions/${sanitize(s.id)}|${safeString(s.project, "unknown")} (${safeString(s.status, "unknown")})]]`);
|
|
12524
|
+
} catch (err) {
|
|
12525
|
+
errors.push({
|
|
12526
|
+
id: s.id,
|
|
12527
|
+
path: filepath,
|
|
12528
|
+
error: err instanceof Error ? err.message : String(err)
|
|
12529
|
+
});
|
|
12530
|
+
}
|
|
12122
12531
|
}
|
|
12532
|
+
const exportedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
12533
|
+
const moc = [
|
|
12534
|
+
"---",
|
|
12535
|
+
"type: moc",
|
|
12536
|
+
`exported: ${exportedAt}`,
|
|
12537
|
+
"---",
|
|
12538
|
+
"",
|
|
12539
|
+
"# agentmemory vault",
|
|
12540
|
+
"",
|
|
12541
|
+
`Exported: ${exportedAt}`,
|
|
12542
|
+
"",
|
|
12543
|
+
`## Memories (${stats.memories})`,
|
|
12544
|
+
...memoryMoc,
|
|
12545
|
+
"",
|
|
12546
|
+
`## Lessons (${stats.lessons})`,
|
|
12547
|
+
...lessonMoc,
|
|
12548
|
+
"",
|
|
12549
|
+
`## Crystals (${stats.crystals})`,
|
|
12550
|
+
...crystalMoc,
|
|
12551
|
+
"",
|
|
12552
|
+
`## Sessions (${stats.sessions})`,
|
|
12553
|
+
...sessionMoc
|
|
12554
|
+
].join("\n");
|
|
12555
|
+
await writeFile(join(vaultDir, "MOC.md"), moc);
|
|
12556
|
+
await recordAudit(kv, "obsidian_export", "mem::obsidian-export", [], {
|
|
12557
|
+
vaultDir,
|
|
12558
|
+
stats
|
|
12559
|
+
});
|
|
12560
|
+
return {
|
|
12561
|
+
success: true,
|
|
12562
|
+
exported: stats,
|
|
12563
|
+
errors: errors.length > 0 ? errors : void 0,
|
|
12564
|
+
vaultDir
|
|
12565
|
+
};
|
|
12566
|
+
} catch (err) {
|
|
12567
|
+
return {
|
|
12568
|
+
success: false,
|
|
12569
|
+
error: err instanceof Error ? err.message : String(err),
|
|
12570
|
+
vaultDir
|
|
12571
|
+
};
|
|
12123
12572
|
}
|
|
12124
|
-
const exportedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
12125
|
-
const moc = [
|
|
12126
|
-
"---",
|
|
12127
|
-
"type: moc",
|
|
12128
|
-
`exported: ${exportedAt}`,
|
|
12129
|
-
"---",
|
|
12130
|
-
"",
|
|
12131
|
-
"# agentmemory vault",
|
|
12132
|
-
"",
|
|
12133
|
-
`Exported: ${exportedAt}`,
|
|
12134
|
-
"",
|
|
12135
|
-
`## Memories (${stats.memories})`,
|
|
12136
|
-
...memoryMoc,
|
|
12137
|
-
"",
|
|
12138
|
-
`## Lessons (${stats.lessons})`,
|
|
12139
|
-
...lessonMoc,
|
|
12140
|
-
"",
|
|
12141
|
-
`## Crystals (${stats.crystals})`,
|
|
12142
|
-
...crystalMoc,
|
|
12143
|
-
"",
|
|
12144
|
-
`## Sessions (${stats.sessions})`,
|
|
12145
|
-
...sessionMoc
|
|
12146
|
-
].join("\n");
|
|
12147
|
-
await writeFile(join(vaultDir, "MOC.md"), moc);
|
|
12148
|
-
await recordAudit(kv, "obsidian_export", "mem::obsidian-export", [], {
|
|
12149
|
-
vaultDir,
|
|
12150
|
-
stats
|
|
12151
|
-
});
|
|
12152
|
-
return {
|
|
12153
|
-
success: true,
|
|
12154
|
-
exported: stats,
|
|
12155
|
-
errors: errors.length > 0 ? errors : void 0,
|
|
12156
|
-
vaultDir
|
|
12157
|
-
};
|
|
12158
12573
|
});
|
|
12159
12574
|
}
|
|
12160
|
-
|
|
12161
12575
|
//#endregion
|
|
12162
12576
|
//#region src/prompts/reflect.ts
|
|
12163
12577
|
const REFLECT_SYSTEM = `You are a higher-order reasoning engine. Given a cluster of related concepts, facts, lessons, and action outcomes, synthesize cross-cutting insights that span multiple individual memories.
|
|
@@ -12185,7 +12599,6 @@ function buildReflectPrompt(cluster) {
|
|
|
12185
12599
|
if (cluster.crystalNarratives.length > 0) sections.push("\n## Completed Work Summaries", ...cluster.crystalNarratives.map((n) => `- ${n}`));
|
|
12186
12600
|
return `Synthesize higher-order insights from this cluster of related memories:\n\n${sections.join("\n")}`;
|
|
12187
12601
|
}
|
|
12188
|
-
|
|
12189
12602
|
//#endregion
|
|
12190
12603
|
//#region src/functions/reflect.ts
|
|
12191
12604
|
function reinforceInsight(insight) {
|
|
@@ -12478,7 +12891,6 @@ function registerReflectFunctions(sdk, kv, provider) {
|
|
|
12478
12891
|
};
|
|
12479
12892
|
});
|
|
12480
12893
|
}
|
|
12481
|
-
|
|
12482
12894
|
//#endregion
|
|
12483
12895
|
//#region src/functions/working-memory.ts
|
|
12484
12896
|
const CORE_SCOPE = "mem:core-memory";
|
|
@@ -12653,7 +13065,6 @@ function registerWorkingMemoryFunctions(sdk, kv, tokenBudget) {
|
|
|
12653
13065
|
};
|
|
12654
13066
|
});
|
|
12655
13067
|
}
|
|
12656
|
-
|
|
12657
13068
|
//#endregion
|
|
12658
13069
|
//#region src/functions/skill-extract.ts
|
|
12659
13070
|
const SKILL_EXTRACT_SYSTEM = `You are a skill extraction engine. Given a completed multi-step task session, extract a reusable procedural skill document.
|
|
@@ -12856,7 +13267,6 @@ function registerSkillExtractFunctions(sdk, kv, provider) {
|
|
|
12856
13267
|
};
|
|
12857
13268
|
});
|
|
12858
13269
|
}
|
|
12859
|
-
|
|
12860
13270
|
//#endregion
|
|
12861
13271
|
//#region src/functions/sliding-window.ts
|
|
12862
13272
|
const SLIDING_WINDOW_SYSTEM = `You are a contextual enrichment engine. Given a primary observation and its surrounding context window (previous and next observations from the same session), produce an enriched version.
|
|
@@ -13043,7 +13453,6 @@ function registerSlidingWindowFunction(sdk, kv, provider) {
|
|
|
13043
13453
|
};
|
|
13044
13454
|
});
|
|
13045
13455
|
}
|
|
13046
|
-
|
|
13047
13456
|
//#endregion
|
|
13048
13457
|
//#region src/functions/temporal-graph.ts
|
|
13049
13458
|
const TEMPORAL_EXTRACTION_SYSTEM = `You are a temporal knowledge extraction engine. Given observations, extract entities AND their temporal relationships with full context metadata.
|
|
@@ -13307,7 +13716,6 @@ function buildTimeline(edges) {
|
|
|
13307
13716
|
context: e.context
|
|
13308
13717
|
}));
|
|
13309
13718
|
}
|
|
13310
|
-
|
|
13311
13719
|
//#endregion
|
|
13312
13720
|
//#region src/functions/retention.ts
|
|
13313
13721
|
const DEFAULT_DECAY = {
|
|
@@ -13541,7 +13949,6 @@ function registerRetentionFunctions(sdk, kv) {
|
|
|
13541
13949
|
};
|
|
13542
13950
|
});
|
|
13543
13951
|
}
|
|
13544
|
-
|
|
13545
13952
|
//#endregion
|
|
13546
13953
|
//#region src/functions/compress-file.ts
|
|
13547
13954
|
const SENSITIVE_PATH_TERMS = [
|
|
@@ -13683,7 +14090,6 @@ function registerCompressFileFunction(sdk, kv, provider) {
|
|
|
13683
14090
|
};
|
|
13684
14091
|
});
|
|
13685
14092
|
}
|
|
13686
|
-
|
|
13687
14093
|
//#endregion
|
|
13688
14094
|
//#region src/replay/jsonl-parser.ts
|
|
13689
14095
|
function deriveProject(cwd) {
|
|
@@ -13810,7 +14216,6 @@ function parseJsonlText(text, fallbackSessionId) {
|
|
|
13810
14216
|
observations
|
|
13811
14217
|
};
|
|
13812
14218
|
}
|
|
13813
|
-
|
|
13814
14219
|
//#endregion
|
|
13815
14220
|
//#region src/replay/timeline.ts
|
|
13816
14221
|
const DEFAULT_CHARS_PER_SEC = 40;
|
|
@@ -13905,10 +14310,6 @@ function projectTimeline(observations) {
|
|
|
13905
14310
|
events
|
|
13906
14311
|
};
|
|
13907
14312
|
}
|
|
13908
|
-
|
|
13909
|
-
//#endregion
|
|
13910
|
-
//#region src/functions/replay.ts
|
|
13911
|
-
const MAX_FILES_DEFAULT = 200;
|
|
13912
14313
|
const MAX_FILES_UPPER_BOUND = 1e3;
|
|
13913
14314
|
const SENSITIVE_PATH_PATTERNS = [
|
|
13914
14315
|
/(^|[\\/_.-])secret([\\/_.-]|s?$)/i,
|
|
@@ -14128,7 +14529,7 @@ function registerReplayFunctions(sdk, kv) {
|
|
|
14128
14529
|
error: "path not found"
|
|
14129
14530
|
};
|
|
14130
14531
|
}
|
|
14131
|
-
const maxFiles = Number.isInteger(data.maxFiles) && data.maxFiles > 0 ? Math.min(data.maxFiles, MAX_FILES_UPPER_BOUND) :
|
|
14532
|
+
const maxFiles = Number.isInteger(data.maxFiles) && data.maxFiles > 0 ? Math.min(data.maxFiles, MAX_FILES_UPPER_BOUND) : 200;
|
|
14132
14533
|
let files = [];
|
|
14133
14534
|
let truncated = false;
|
|
14134
14535
|
let discovered = 0;
|
|
@@ -14184,7 +14585,8 @@ function registerReplayFunctions(sdk, kv) {
|
|
|
14184
14585
|
const existingTags = existing.tags || [];
|
|
14185
14586
|
if (!existingTags.includes("jsonl-import")) existing.tags = [...existingTags, "jsonl-import"];
|
|
14186
14587
|
if (!existing.firstPrompt && firstPrompt) existing.firstPrompt = firstPrompt;
|
|
14187
|
-
|
|
14588
|
+
if (!existing.id) existing.id = parsed.sessionId;
|
|
14589
|
+
await kv.set(KV.sessions, parsed.sessionId, existing);
|
|
14188
14590
|
} else {
|
|
14189
14591
|
const session = {
|
|
14190
14592
|
id: parsed.sessionId,
|
|
@@ -14230,7 +14632,6 @@ function registerReplayFunctions(sdk, kv) {
|
|
|
14230
14632
|
};
|
|
14231
14633
|
});
|
|
14232
14634
|
}
|
|
14233
|
-
|
|
14234
14635
|
//#endregion
|
|
14235
14636
|
//#region src/health/thresholds.ts
|
|
14236
14637
|
const DEFAULTS = {
|
|
@@ -14289,7 +14690,6 @@ function evaluateHealth(snapshot, config = {}) {
|
|
|
14289
14690
|
notes
|
|
14290
14691
|
};
|
|
14291
14692
|
}
|
|
14292
|
-
|
|
14293
14693
|
//#endregion
|
|
14294
14694
|
//#region src/health/monitor.ts
|
|
14295
14695
|
function registerHealthMonitor(sdk, kv) {
|
|
@@ -14377,7 +14777,6 @@ function registerHealthMonitor(sdk, kv) {
|
|
|
14377
14777
|
async function getLatestHealth(kv) {
|
|
14378
14778
|
return kv.get(KV.health, "latest");
|
|
14379
14779
|
}
|
|
14380
|
-
|
|
14381
14780
|
//#endregion
|
|
14382
14781
|
//#region src/auth.ts
|
|
14383
14782
|
const hmacKey = randomBytes(32);
|
|
@@ -14403,7 +14802,6 @@ function buildViewerCsp(nonce) {
|
|
|
14403
14802
|
"font-src 'self'"
|
|
14404
14803
|
].join("; ");
|
|
14405
14804
|
}
|
|
14406
|
-
|
|
14407
14805
|
//#endregion
|
|
14408
14806
|
//#region src/viewer/document.ts
|
|
14409
14807
|
const VIEWER_VERSION_PLACEHOLDER = "__AGENTMEMORY_VERSION__";
|
|
@@ -14429,7 +14827,6 @@ function renderViewerDocument() {
|
|
|
14429
14827
|
csp: buildViewerCsp(nonce)
|
|
14430
14828
|
};
|
|
14431
14829
|
}
|
|
14432
|
-
|
|
14433
14830
|
//#endregion
|
|
14434
14831
|
//#region src/viewer/server.ts
|
|
14435
14832
|
function loadViewerFavicon() {
|
|
@@ -14444,18 +14841,30 @@ function loadViewerFavicon() {
|
|
|
14444
14841
|
} catch {}
|
|
14445
14842
|
return null;
|
|
14446
14843
|
}
|
|
14844
|
+
const VIEWER_FAVICON = loadViewerFavicon();
|
|
14447
14845
|
const ALLOWED_ORIGINS = (process.env.VIEWER_ALLOWED_ORIGINS || "http://localhost:3111,http://localhost:3113,http://127.0.0.1:3111,http://127.0.0.1:3113").split(",").map((o) => o.trim());
|
|
14448
|
-
|
|
14449
|
-
|
|
14846
|
+
function readAllowedHostsOverride() {
|
|
14847
|
+
return (process.env.VIEWER_ALLOWED_HOSTS || "").split(",").map((h) => h.trim().toLowerCase()).filter(Boolean);
|
|
14848
|
+
}
|
|
14849
|
+
function resolveViewerHost() {
|
|
14850
|
+
return process.env.AGENTMEMORY_VIEWER_HOST?.trim() || "127.0.0.1";
|
|
14851
|
+
}
|
|
14852
|
+
function isLoopbackHost(host) {
|
|
14853
|
+
const h = host.trim().toLowerCase();
|
|
14854
|
+
return h === "127.0.0.1" || h === "::1" || h === "localhost";
|
|
14855
|
+
}
|
|
14856
|
+
function buildAllowedHosts(origins, listenPort, bindHost = "127.0.0.1") {
|
|
14450
14857
|
const hosts = /* @__PURE__ */ new Set();
|
|
14451
|
-
|
|
14452
|
-
const
|
|
14453
|
-
|
|
14454
|
-
|
|
14455
|
-
|
|
14456
|
-
|
|
14457
|
-
|
|
14458
|
-
|
|
14858
|
+
if (isLoopbackHost(bindHost)) {
|
|
14859
|
+
for (const o of origins) try {
|
|
14860
|
+
const parsed = new URL(o);
|
|
14861
|
+
if (parsed.host) hosts.add(parsed.host.toLowerCase());
|
|
14862
|
+
} catch {}
|
|
14863
|
+
hosts.add(`localhost:${listenPort}`);
|
|
14864
|
+
hosts.add(`127.0.0.1:${listenPort}`);
|
|
14865
|
+
hosts.add(`[::1]:${listenPort}`);
|
|
14866
|
+
}
|
|
14867
|
+
for (const h of readAllowedHostsOverride()) hosts.add(h);
|
|
14459
14868
|
return hosts;
|
|
14460
14869
|
}
|
|
14461
14870
|
function isHostAllowed(headerHost, allowed) {
|
|
@@ -14464,11 +14873,16 @@ function isHostAllowed(headerHost, allowed) {
|
|
|
14464
14873
|
if (!lower) return false;
|
|
14465
14874
|
return allowed.has(lower);
|
|
14466
14875
|
}
|
|
14876
|
+
function requireInboundBearer(authHeader, secret) {
|
|
14877
|
+
if (typeof authHeader !== "string") return false;
|
|
14878
|
+
const match = /^Bearer\s+(\S+)\s*$/i.exec(authHeader);
|
|
14879
|
+
if (!match) return false;
|
|
14880
|
+
return timingSafeCompare(match[1], secret);
|
|
14881
|
+
}
|
|
14467
14882
|
function corsHeaders(req) {
|
|
14468
14883
|
const origin = req.headers.origin || "";
|
|
14469
|
-
const allowed = ALLOWED_ORIGINS.includes(origin) ? origin : ALLOWED_ORIGINS[0];
|
|
14470
14884
|
return {
|
|
14471
|
-
"Access-Control-Allow-Origin":
|
|
14885
|
+
"Access-Control-Allow-Origin": ALLOWED_ORIGINS.includes(origin) ? origin : ALLOWED_ORIGINS[0],
|
|
14472
14886
|
"Access-Control-Allow-Methods": "GET, POST, DELETE, OPTIONS",
|
|
14473
14887
|
"Access-Control-Allow-Headers": "Content-Type, Authorization",
|
|
14474
14888
|
Vary: "Origin"
|
|
@@ -14512,16 +14926,29 @@ function getBoundViewerPort() {
|
|
|
14512
14926
|
function getViewerSkipped() {
|
|
14513
14927
|
return viewerSkipped;
|
|
14514
14928
|
}
|
|
14929
|
+
var ViewerConfigError = class extends Error {
|
|
14930
|
+
constructor(message) {
|
|
14931
|
+
super(message);
|
|
14932
|
+
this.name = "ViewerConfigError";
|
|
14933
|
+
}
|
|
14934
|
+
};
|
|
14515
14935
|
function startViewerServer(port, _kv, _sdk, secret, restPort) {
|
|
14516
14936
|
boundViewerPort = null;
|
|
14517
14937
|
viewerSkipped = false;
|
|
14518
14938
|
const resolvedRestPort = restPort ?? port - 2;
|
|
14519
14939
|
const requestedPort = port;
|
|
14940
|
+
const host = resolveViewerHost();
|
|
14941
|
+
let inboundSecret = null;
|
|
14942
|
+
if (!isLoopbackHost(host)) {
|
|
14943
|
+
if (!secret) throw new ViewerConfigError(`AGENTMEMORY_VIEWER_HOST=${host} requires AGENTMEMORY_SECRET to be set so the viewer can validate inbound bearer tokens. To fix: unset AGENTMEMORY_VIEWER_HOST to keep the safe loopback bind, or set AGENTMEMORY_SECRET. For Fly images, it is printed on first boot; see deploy/fly/README.md.`);
|
|
14944
|
+
if (readAllowedHostsOverride().length === 0) throw new ViewerConfigError(`AGENTMEMORY_VIEWER_HOST=${host} requires VIEWER_ALLOWED_HOSTS because non-loopback viewer binds only trust explicit Host headers. To fix: set VIEWER_ALLOWED_HOSTS to a comma-separated list of trusted Host header values (e.g. "localhost:3113" for fly proxy), or unset AGENTMEMORY_VIEWER_HOST to keep the safe loopback bind.`);
|
|
14945
|
+
inboundSecret = secret;
|
|
14946
|
+
}
|
|
14520
14947
|
let allowedHosts = null;
|
|
14521
14948
|
const server = createServer(async (req, res) => {
|
|
14522
14949
|
if (!allowedHosts) {
|
|
14523
14950
|
const addr = server.address();
|
|
14524
|
-
allowedHosts = buildAllowedHosts(ALLOWED_ORIGINS, addr && typeof addr === "object" && "port" in addr ? addr.port : port);
|
|
14951
|
+
allowedHosts = buildAllowedHosts(ALLOWED_ORIGINS, addr && typeof addr === "object" && "port" in addr ? addr.port : port, host);
|
|
14525
14952
|
}
|
|
14526
14953
|
if (!isHostAllowed(req.headers.host, allowedHosts)) {
|
|
14527
14954
|
res.writeHead(403, { "Content-Type": "text/plain" });
|
|
@@ -14557,19 +14984,26 @@ function startViewerServer(port, _kv, _sdk, secret, restPort) {
|
|
|
14557
14984
|
return;
|
|
14558
14985
|
}
|
|
14559
14986
|
if (method === "GET" && pathname === "/favicon.svg") {
|
|
14560
|
-
|
|
14561
|
-
if (favicon) {
|
|
14987
|
+
if (VIEWER_FAVICON) {
|
|
14562
14988
|
res.writeHead(200, {
|
|
14563
14989
|
"Content-Type": "image/svg+xml",
|
|
14564
14990
|
"Cache-Control": "public, max-age=3600"
|
|
14565
14991
|
});
|
|
14566
|
-
res.end(
|
|
14992
|
+
res.end(VIEWER_FAVICON);
|
|
14567
14993
|
return;
|
|
14568
14994
|
}
|
|
14569
14995
|
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
14570
14996
|
res.end("favicon not found");
|
|
14571
14997
|
return;
|
|
14572
14998
|
}
|
|
14999
|
+
if (inboundSecret !== null && !requireInboundBearer(req.headers.authorization, inboundSecret)) {
|
|
15000
|
+
res.writeHead(401, {
|
|
15001
|
+
"Content-Type": "text/plain",
|
|
15002
|
+
"WWW-Authenticate": "Bearer realm=\"agentmemory-viewer\""
|
|
15003
|
+
});
|
|
15004
|
+
res.end("unauthorized");
|
|
15005
|
+
return;
|
|
15006
|
+
}
|
|
14573
15007
|
try {
|
|
14574
15008
|
await proxyToRestApi(resolvedRestPort, pathname, qs, method, req, res, secret);
|
|
14575
15009
|
} catch (err) {
|
|
@@ -14580,17 +15014,23 @@ function startViewerServer(port, _kv, _sdk, secret, restPort) {
|
|
|
14580
15014
|
let attempt = 0;
|
|
14581
15015
|
let currentPort = requestedPort;
|
|
14582
15016
|
const tryListen = () => {
|
|
14583
|
-
server.listen(currentPort,
|
|
15017
|
+
server.listen(currentPort, host);
|
|
14584
15018
|
};
|
|
14585
15019
|
server.on("listening", () => {
|
|
14586
15020
|
const addr = server.address();
|
|
14587
|
-
|
|
15021
|
+
const actualPort = addr && typeof addr === "object" && "port" in addr ? addr.port : currentPort;
|
|
15022
|
+
boundViewerPort = actualPort;
|
|
14588
15023
|
viewerSkipped = false;
|
|
14589
|
-
if (
|
|
14590
|
-
|
|
15024
|
+
if (inboundSecret !== null) {
|
|
15025
|
+
const allowedHosts = readAllowedHostsOverride().join(", ");
|
|
15026
|
+
console.log(`[agentmemory] Viewer: http://localhost:${actualPort} (bound to ${host}; inbound Bearer required; allowed Host headers: ${allowedHosts})`);
|
|
15027
|
+
return;
|
|
15028
|
+
}
|
|
15029
|
+
if (actualPort === requestedPort) console.log(`[agentmemory] Viewer: http://localhost:${actualPort}`);
|
|
15030
|
+
else console.log(`[agentmemory] Viewer started on http://localhost:${actualPort} (fallback from ${requestedPort})`);
|
|
14591
15031
|
});
|
|
14592
15032
|
server.on("error", (err) => {
|
|
14593
|
-
if (err.code === "EADDRINUSE" && attempt < MAX_VIEWER_PORT_RETRIES) {
|
|
15033
|
+
if (err.code === "EADDRINUSE" && inboundSecret === null && attempt < MAX_VIEWER_PORT_RETRIES) {
|
|
14594
15034
|
attempt++;
|
|
14595
15035
|
currentPort = requestedPort + attempt;
|
|
14596
15036
|
setImmediate(tryListen);
|
|
@@ -14599,7 +15039,8 @@ function startViewerServer(port, _kv, _sdk, secret, restPort) {
|
|
|
14599
15039
|
if (err.code === "EADDRINUSE") {
|
|
14600
15040
|
boundViewerPort = null;
|
|
14601
15041
|
viewerSkipped = true;
|
|
14602
|
-
console.warn(`[agentmemory] Viewer
|
|
15042
|
+
if (inboundSecret !== null) console.warn(`[agentmemory] Viewer port ${requestedPort} is in use while bound to ${host}; not retrying because non-loopback viewer binds require VIEWER_ALLOWED_HOSTS to match the exact port. Free the port, choose another viewer port, or unset AGENTMEMORY_VIEWER_HOST to keep the safe loopback bind.`);
|
|
15043
|
+
else console.warn(`[agentmemory] Viewer ports ${requestedPort}-${requestedPort + MAX_VIEWER_PORT_RETRIES} all in use, skipping viewer.`);
|
|
14603
15044
|
} else {
|
|
14604
15045
|
boundViewerPort = null;
|
|
14605
15046
|
viewerSkipped = true;
|
|
@@ -14644,7 +15085,6 @@ async function proxyToRestApi(restPort, pathname, qs, method, req, res, secret)
|
|
|
14644
15085
|
res.writeHead(upstream.status, responseHeaders);
|
|
14645
15086
|
res.end(responseBody);
|
|
14646
15087
|
}
|
|
14647
|
-
|
|
14648
15088
|
//#endregion
|
|
14649
15089
|
//#region src/triggers/api.ts
|
|
14650
15090
|
function parseOptionalInt(raw) {
|
|
@@ -15067,7 +15507,7 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
15067
15507
|
}
|
|
15068
15508
|
if (body.maxFiles !== void 0) {
|
|
15069
15509
|
const n = body.maxFiles;
|
|
15070
|
-
if (!Number.isInteger(n) || n < 1 || n >
|
|
15510
|
+
if (!Number.isInteger(n) || n < 1 || n > 1e3) return {
|
|
15071
15511
|
status_code: 400,
|
|
15072
15512
|
body: { error: `maxFiles must be an integer between 1 and ${MAX_FILES_UPPER_BOUND}` }
|
|
15073
15513
|
};
|
|
@@ -15151,9 +15591,13 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
15151
15591
|
value: "completed"
|
|
15152
15592
|
}]);
|
|
15153
15593
|
try {
|
|
15154
|
-
sdk.
|
|
15594
|
+
sdk.trigger({
|
|
15595
|
+
function_id: "event::session::stopped",
|
|
15596
|
+
payload: { sessionId },
|
|
15597
|
+
action: TriggerAction.Void()
|
|
15598
|
+
});
|
|
15155
15599
|
} catch (err) {
|
|
15156
|
-
logger.warn("event::session::stopped
|
|
15600
|
+
logger.warn("event::session::stopped trigger failed", {
|
|
15157
15601
|
sessionId,
|
|
15158
15602
|
error: err instanceof Error ? err.message : String(err)
|
|
15159
15603
|
});
|
|
@@ -15573,11 +16017,24 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
15573
16017
|
status_code: 400,
|
|
15574
16018
|
body: { error: "query or expandIds is required" }
|
|
15575
16019
|
};
|
|
16020
|
+
const headers = req.headers || {};
|
|
16021
|
+
const sourceHeader = headers["x-agentmemory-source"] ?? headers["X-Agentmemory-Source"];
|
|
16022
|
+
const sourceFromHeader = Array.isArray(sourceHeader) ? sourceHeader[0] : sourceHeader;
|
|
16023
|
+
const payload = {
|
|
16024
|
+
query: req.body?.query,
|
|
16025
|
+
expandIds: req.body?.expandIds,
|
|
16026
|
+
limit: req.body?.limit,
|
|
16027
|
+
project: req.body?.project,
|
|
16028
|
+
includeLessons: req.body?.includeLessons,
|
|
16029
|
+
agentId: req.body?.agentId,
|
|
16030
|
+
sessionId: req.body?.sessionId,
|
|
16031
|
+
source: req.body?.source ?? sourceFromHeader
|
|
16032
|
+
};
|
|
15576
16033
|
return {
|
|
15577
16034
|
status_code: 200,
|
|
15578
16035
|
body: await sdk.trigger({
|
|
15579
16036
|
function_id: "mem::smart-search",
|
|
15580
|
-
payload
|
|
16037
|
+
payload
|
|
15581
16038
|
})
|
|
15582
16039
|
};
|
|
15583
16040
|
});
|
|
@@ -15589,6 +16046,29 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
15589
16046
|
http_method: "POST"
|
|
15590
16047
|
}
|
|
15591
16048
|
});
|
|
16049
|
+
sdk.registerFunction("api::diagnostic-followup", async (req) => {
|
|
16050
|
+
const authErr = checkAuth(req, secret);
|
|
16051
|
+
if (authErr) return authErr;
|
|
16052
|
+
return {
|
|
16053
|
+
status_code: 200,
|
|
16054
|
+
body: {
|
|
16055
|
+
...await sdk.trigger({
|
|
16056
|
+
function_id: "mem::diagnostic::followup-stats",
|
|
16057
|
+
payload: {}
|
|
16058
|
+
}),
|
|
16059
|
+
caveat: "Directional signal: overcounts on legitimate query refinement. Tune via AGENTMEMORY_FOLLOWUP_WINDOW_SECONDS."
|
|
16060
|
+
}
|
|
16061
|
+
};
|
|
16062
|
+
});
|
|
16063
|
+
sdk.registerTrigger({
|
|
16064
|
+
type: "http",
|
|
16065
|
+
function_id: "api::diagnostic-followup",
|
|
16066
|
+
config: {
|
|
16067
|
+
api_path: "/agentmemory/diagnostics/followup",
|
|
16068
|
+
http_method: "GET",
|
|
16069
|
+
middleware_function_ids: ["middleware::api-auth"]
|
|
16070
|
+
}
|
|
16071
|
+
});
|
|
15592
16072
|
sdk.registerFunction("api::timeline", async (req) => {
|
|
15593
16073
|
const authErr = checkAuth(req, secret);
|
|
15594
16074
|
if (authErr) return authErr;
|
|
@@ -15810,12 +16290,20 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
15810
16290
|
sdk.registerFunction("api::graph-query", async (req) => {
|
|
15811
16291
|
const authErr = checkAuth(req, secret);
|
|
15812
16292
|
if (authErr) return authErr;
|
|
16293
|
+
const payload = {
|
|
16294
|
+
startNodeId: req.body?.startNodeId,
|
|
16295
|
+
nodeType: req.body?.nodeType,
|
|
16296
|
+
maxDepth: req.body?.maxDepth,
|
|
16297
|
+
query: req.body?.query,
|
|
16298
|
+
limit: req.body?.limit,
|
|
16299
|
+
offset: req.body?.offset
|
|
16300
|
+
};
|
|
15813
16301
|
try {
|
|
15814
16302
|
return {
|
|
15815
16303
|
status_code: 200,
|
|
15816
16304
|
body: await sdk.trigger({
|
|
15817
16305
|
function_id: "mem::graph-query",
|
|
15818
|
-
payload
|
|
16306
|
+
payload
|
|
15819
16307
|
})
|
|
15820
16308
|
};
|
|
15821
16309
|
} catch {
|
|
@@ -18096,7 +18584,6 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
18096
18584
|
}
|
|
18097
18585
|
});
|
|
18098
18586
|
}
|
|
18099
|
-
|
|
18100
18587
|
//#endregion
|
|
18101
18588
|
//#region src/triggers/events.ts
|
|
18102
18589
|
function registerEventTriggers(sdk, kv) {
|
|
@@ -18141,18 +18628,26 @@ function registerEventTriggers(sdk, kv) {
|
|
|
18141
18628
|
payload: data
|
|
18142
18629
|
});
|
|
18143
18630
|
if (isReflectEnabled()) try {
|
|
18144
|
-
sdk.
|
|
18631
|
+
sdk.trigger({
|
|
18632
|
+
function_id: "mem::slot-reflect",
|
|
18633
|
+
payload: { sessionId: data.sessionId },
|
|
18634
|
+
action: TriggerAction.Void()
|
|
18635
|
+
});
|
|
18145
18636
|
} catch (err) {
|
|
18146
|
-
logger.warn("slot-reflect
|
|
18637
|
+
logger.warn("slot-reflect trigger failed", {
|
|
18147
18638
|
sessionId: data.sessionId,
|
|
18148
18639
|
error: err instanceof Error ? err.message : String(err)
|
|
18149
18640
|
});
|
|
18150
18641
|
}
|
|
18151
18642
|
if (isGraphExtractionEnabled()) try {
|
|
18152
18643
|
const compressed = (await kv.list(KV.observations(data.sessionId))).filter((o) => o.title);
|
|
18153
|
-
if (compressed.length > 0) sdk.
|
|
18644
|
+
if (compressed.length > 0) sdk.trigger({
|
|
18645
|
+
function_id: "mem::graph-extract",
|
|
18646
|
+
payload: { observations: compressed },
|
|
18647
|
+
action: TriggerAction.Void()
|
|
18648
|
+
});
|
|
18154
18649
|
} catch (err) {
|
|
18155
|
-
logger.warn("graph-extract
|
|
18650
|
+
logger.warn("graph-extract trigger failed", {
|
|
18156
18651
|
sessionId: data.sessionId,
|
|
18157
18652
|
error: err instanceof Error ? err.message : String(err)
|
|
18158
18653
|
});
|
|
@@ -18210,7 +18705,6 @@ function registerEventTriggers(sdk, kv) {
|
|
|
18210
18705
|
config: { scope: KV.sessions }
|
|
18211
18706
|
});
|
|
18212
18707
|
}
|
|
18213
|
-
|
|
18214
18708
|
//#endregion
|
|
18215
18709
|
//#region src/mcp/tools-registry.ts
|
|
18216
18710
|
const CORE_TOOLS = [
|
|
@@ -19320,7 +19814,6 @@ function getVisibleTools() {
|
|
|
19320
19814
|
if ((process.env["AGENTMEMORY_TOOLS"] || "all") === "core") return getAllTools().filter((t) => ESSENTIAL_TOOLS.has(t.name));
|
|
19321
19815
|
return getAllTools();
|
|
19322
19816
|
}
|
|
19323
|
-
|
|
19324
19817
|
//#endregion
|
|
19325
19818
|
//#region src/mcp/server.ts
|
|
19326
19819
|
function asNonEmptyString(value) {
|
|
@@ -20957,7 +21450,6 @@ function registerMcpEndpoints(sdk, kv, secret) {
|
|
|
20957
21450
|
}
|
|
20958
21451
|
});
|
|
20959
21452
|
}
|
|
20960
|
-
|
|
20961
21453
|
//#endregion
|
|
20962
21454
|
//#region src/eval/metrics-store.ts
|
|
20963
21455
|
var MetricsStore = class {
|
|
@@ -21000,7 +21492,6 @@ var MetricsStore = class {
|
|
|
21000
21492
|
return Array.from(merged.values());
|
|
21001
21493
|
}
|
|
21002
21494
|
};
|
|
21003
|
-
|
|
21004
21495
|
//#endregion
|
|
21005
21496
|
//#region src/functions/dedup.ts
|
|
21006
21497
|
const TTL_MS = 300 * 1e3;
|
|
@@ -21042,57 +21533,6 @@ var DedupMap = class {
|
|
|
21042
21533
|
return this.entries.size;
|
|
21043
21534
|
}
|
|
21044
21535
|
};
|
|
21045
|
-
|
|
21046
|
-
//#endregion
|
|
21047
|
-
//#region src/telemetry/setup.ts
|
|
21048
|
-
const OTEL_CONFIG = {
|
|
21049
|
-
serviceName: "agentmemory",
|
|
21050
|
-
serviceVersion: VERSION,
|
|
21051
|
-
metricsExportIntervalMs: 3e4
|
|
21052
|
-
};
|
|
21053
|
-
let counters = null;
|
|
21054
|
-
let histograms = null;
|
|
21055
|
-
const NOOP_COUNTER = { add: () => {} };
|
|
21056
|
-
const NOOP_HISTOGRAM = { record: () => {} };
|
|
21057
|
-
const COUNTER_NAMES = [
|
|
21058
|
-
["observationsTotal", "observations.total"],
|
|
21059
|
-
["compressionSuccess", "compression.success"],
|
|
21060
|
-
["compressionFailure", "compression.failure"],
|
|
21061
|
-
["searchTotal", "search.total"],
|
|
21062
|
-
["dedupSkipped", "dedup.skipped"],
|
|
21063
|
-
["evictionTotal", "eviction.total"],
|
|
21064
|
-
["circuitBreakerOpen", "circuit_breaker.open"],
|
|
21065
|
-
["embeddingSuccess", "embedding.success"],
|
|
21066
|
-
["embeddingFailure", "embedding.failure"],
|
|
21067
|
-
["vectorSearchTotal", "vector_search.total"],
|
|
21068
|
-
["autoForgetTotal", "auto_forget.total"],
|
|
21069
|
-
["profileGenerated", "profile.generated"],
|
|
21070
|
-
["claudeBridgeSync", "claude_bridge.sync"],
|
|
21071
|
-
["graphExtraction", "graph.extraction"],
|
|
21072
|
-
["consolidationRun", "consolidation.run"],
|
|
21073
|
-
["teamShare", "team.share"],
|
|
21074
|
-
["auditLog", "audit.log"],
|
|
21075
|
-
["snapshotCreate", "snapshot.create"],
|
|
21076
|
-
["governanceDelete", "governance.delete"]
|
|
21077
|
-
];
|
|
21078
|
-
const HISTOGRAM_NAMES = [
|
|
21079
|
-
["compressionLatency", "compression.latency_ms"],
|
|
21080
|
-
["searchLatency", "search.latency_ms"],
|
|
21081
|
-
["contextTokens", "context.tokens"],
|
|
21082
|
-
["qualityScore", "quality.score"],
|
|
21083
|
-
["embeddingLatency", "embedding.latency_ms"],
|
|
21084
|
-
["vectorSearchLatency", "vector_search.latency_ms"]
|
|
21085
|
-
];
|
|
21086
|
-
function initMetrics(getMeter) {
|
|
21087
|
-
const meter = getMeter?.("agentmemory");
|
|
21088
|
-
counters = Object.fromEntries(COUNTER_NAMES.map(([key, name]) => [key, meter ? meter.createCounter(name) : NOOP_COUNTER]));
|
|
21089
|
-
histograms = Object.fromEntries(HISTOGRAM_NAMES.map(([key, name]) => [key, meter ? meter.createHistogram(name) : NOOP_HISTOGRAM]));
|
|
21090
|
-
return {
|
|
21091
|
-
counters,
|
|
21092
|
-
histograms
|
|
21093
|
-
};
|
|
21094
|
-
}
|
|
21095
|
-
|
|
21096
21536
|
//#endregion
|
|
21097
21537
|
//#region src/index.ts
|
|
21098
21538
|
function workerPidfilePath() {
|
|
@@ -21241,6 +21681,7 @@ async function main() {
|
|
|
21241
21681
|
const graphWeight = parseFloat(getEnvVar("AGENTMEMORY_GRAPH_WEIGHT") || "0.3");
|
|
21242
21682
|
const hybridSearch = new HybridSearch(bm25Index, vectorIndex, embeddingProvider, kv, embeddingConfig.bm25Weight, embeddingConfig.vectorWeight, graphWeight);
|
|
21243
21683
|
registerSmartSearchFunction(sdk, kv, (query, limit) => hybridSearch.search(query, limit));
|
|
21684
|
+
registerRecentSearchesSweepFunction(sdk, kv);
|
|
21244
21685
|
registerApiTriggers(sdk, kv, secret, metricsStore, provider);
|
|
21245
21686
|
registerEventTriggers(sdk, kv);
|
|
21246
21687
|
registerMcpEndpoints(sdk, kv, secret);
|
|
@@ -21308,7 +21749,7 @@ async function main() {
|
|
|
21308
21749
|
console.warn(`[agentmemory] Failed to backfill memories into BM25:`, err);
|
|
21309
21750
|
}
|
|
21310
21751
|
bootLog(`Ready. ${embeddingProvider ? "Triple-stream (BM25+Vector+Graph)" : "BM25+Graph"} search active.`);
|
|
21311
|
-
bootLog(`REST API:
|
|
21752
|
+
bootLog(`REST API: 126 endpoints at http://localhost:${config.restPort}/agentmemory/*`);
|
|
21312
21753
|
bootLog(`MCP surface (opt-in via \`npx @agentmemory/mcp\`): ${getAllTools().length} tools · 6 resources · 3 prompts`);
|
|
21313
21754
|
const viewerServer = startViewerServer(config.restPort + 2, kv, sdk, secret, config.restPort);
|
|
21314
21755
|
const autoForgetIntervalMs = parseInt(process.env.AUTO_FORGET_INTERVAL_MS || "3600000", 10);
|
|
@@ -21343,6 +21784,14 @@ async function main() {
|
|
|
21343
21784
|
});
|
|
21344
21785
|
} catch {}
|
|
21345
21786
|
}, 864e5).unref();
|
|
21787
|
+
setInterval(async () => {
|
|
21788
|
+
try {
|
|
21789
|
+
await sdk.trigger({
|
|
21790
|
+
function_id: "mem::diagnostic::recent-searches-sweep",
|
|
21791
|
+
payload: {}
|
|
21792
|
+
});
|
|
21793
|
+
} catch {}
|
|
21794
|
+
}, 3600 * 1e3).unref();
|
|
21346
21795
|
if (isConsolidationEnabled()) {
|
|
21347
21796
|
setInterval(async () => {
|
|
21348
21797
|
try {
|
|
@@ -21374,7 +21823,7 @@ main().catch((err) => {
|
|
|
21374
21823
|
console.error(`[agentmemory] Fatal:`, err);
|
|
21375
21824
|
process.exit(1);
|
|
21376
21825
|
});
|
|
21377
|
-
|
|
21378
21826
|
//#endregion
|
|
21379
|
-
export {
|
|
21827
|
+
export {};
|
|
21828
|
+
|
|
21380
21829
|
//# sourceMappingURL=index.mjs.map
|