@agentmemory/agentmemory 0.9.24 → 0.9.26
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 +125 -65
- 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-B8J9Exum.mjs → src-u7kAEUC0.mjs} +937 -483
- package/dist/src-u7kAEUC0.mjs.map +1 -0
- package/dist/{standalone-CPfsVTBA.mjs → standalone-C1yPO519.mjs} +6 -10
- package/dist/{standalone-CPfsVTBA.mjs.map → standalone-C1yPO519.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-BMFYFFut.mjs +6 -0
- package/dist/version-BMFYFFut.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-B8J9Exum.mjs.map +0 -1
- package/dist/tools-registry-DJizX9Az.mjs.map +0 -1
- package/dist/version-BWEBnKAp.mjs +0 -6
- package/dist/version-BWEBnKAp.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 && typeof manifest.value === "object") 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.26";
|
|
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.24";
|
|
6797
|
-
|
|
6798
7175
|
//#endregion
|
|
6799
7176
|
//#region src/functions/export-import.ts
|
|
6800
7177
|
function registerExportImportFunction(sdk, kv) {
|
|
@@ -6935,7 +7312,9 @@ function registerExportImportFunction(sdk, kv) {
|
|
|
6935
7312
|
"0.9.21",
|
|
6936
7313
|
"0.9.22",
|
|
6937
7314
|
"0.9.23",
|
|
6938
|
-
"0.9.24"
|
|
7315
|
+
"0.9.24",
|
|
7316
|
+
"0.9.25",
|
|
7317
|
+
"0.9.26"
|
|
6939
7318
|
]).has(importData.version)) return {
|
|
6940
7319
|
success: false,
|
|
6941
7320
|
error: `Unsupported export version: ${importData.version}`
|
|
@@ -7253,7 +7632,6 @@ function registerExportImportFunction(sdk, kv) {
|
|
|
7253
7632
|
};
|
|
7254
7633
|
});
|
|
7255
7634
|
}
|
|
7256
|
-
|
|
7257
7635
|
//#endregion
|
|
7258
7636
|
//#region src/functions/enrich.ts
|
|
7259
7637
|
const MAX_CONTEXT_LENGTH = 4e3;
|
|
@@ -7314,7 +7692,6 @@ function registerEnrichFunction(sdk, kv) {
|
|
|
7314
7692
|
};
|
|
7315
7693
|
});
|
|
7316
7694
|
}
|
|
7317
|
-
|
|
7318
7695
|
//#endregion
|
|
7319
7696
|
//#region src/functions/claude-bridge.ts
|
|
7320
7697
|
function parseMemoryMd(content) {
|
|
@@ -7435,7 +7812,6 @@ function registerClaudeBridgeFunction(sdk, kv, config) {
|
|
|
7435
7812
|
}
|
|
7436
7813
|
});
|
|
7437
7814
|
}
|
|
7438
|
-
|
|
7439
7815
|
//#endregion
|
|
7440
7816
|
//#region src/prompts/graph-extraction.ts
|
|
7441
7817
|
const GRAPH_EXTRACTION_SYSTEM = `You are a knowledge graph extraction engine. Given a compressed observation from a coding session, extract entities and relationships.
|
|
@@ -7458,9 +7834,41 @@ Rules:
|
|
|
7458
7834
|
function buildGraphExtractionPrompt(observations) {
|
|
7459
7835
|
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")}`;
|
|
7460
7836
|
}
|
|
7461
|
-
|
|
7462
7837
|
//#endregion
|
|
7463
7838
|
//#region src/functions/graph.ts
|
|
7839
|
+
const DEFAULT_GRAPH_QUERY_LIMIT = 500;
|
|
7840
|
+
const MAX_GRAPH_QUERY_LIMIT = 5e3;
|
|
7841
|
+
function resolvePagination(rawLimit, rawOffset) {
|
|
7842
|
+
return {
|
|
7843
|
+
limit: Math.max(1, Math.min(typeof rawLimit === "number" && Number.isFinite(rawLimit) ? Math.floor(rawLimit) : DEFAULT_GRAPH_QUERY_LIMIT, MAX_GRAPH_QUERY_LIMIT)),
|
|
7844
|
+
offset: Math.max(0, typeof rawOffset === "number" && Number.isFinite(rawOffset) ? Math.floor(rawOffset) : 0)
|
|
7845
|
+
};
|
|
7846
|
+
}
|
|
7847
|
+
function rankByDegree(nodes, edges) {
|
|
7848
|
+
const degree = /* @__PURE__ */ new Map();
|
|
7849
|
+
for (const edge of edges) {
|
|
7850
|
+
degree.set(edge.sourceNodeId, (degree.get(edge.sourceNodeId) ?? 0) + 1);
|
|
7851
|
+
degree.set(edge.targetNodeId, (degree.get(edge.targetNodeId) ?? 0) + 1);
|
|
7852
|
+
}
|
|
7853
|
+
return [...nodes].sort((a, b) => (degree.get(b.id) ?? 0) - (degree.get(a.id) ?? 0));
|
|
7854
|
+
}
|
|
7855
|
+
function paginate(nodes, allEdges, depth, limit, offset) {
|
|
7856
|
+
const totalNodes = nodes.length;
|
|
7857
|
+
const pageNodes = nodes.slice(offset, offset + limit);
|
|
7858
|
+
const pageNodeIds = new Set(pageNodes.map((n) => n.id));
|
|
7859
|
+
const pageEdges = allEdges.filter((e) => pageNodeIds.has(e.sourceNodeId) && pageNodeIds.has(e.targetNodeId));
|
|
7860
|
+
const universeIds = new Set(nodes.map((n) => n.id));
|
|
7861
|
+
return {
|
|
7862
|
+
nodes: pageNodes,
|
|
7863
|
+
edges: pageEdges,
|
|
7864
|
+
depth,
|
|
7865
|
+
totalNodes,
|
|
7866
|
+
totalEdges: allEdges.reduce((count, e) => universeIds.has(e.sourceNodeId) && universeIds.has(e.targetNodeId) ? count + 1 : count, 0),
|
|
7867
|
+
truncated: totalNodes > pageNodes.length,
|
|
7868
|
+
limit,
|
|
7869
|
+
offset
|
|
7870
|
+
};
|
|
7871
|
+
}
|
|
7464
7872
|
function parseAttrs(raw) {
|
|
7465
7873
|
const attrs = {};
|
|
7466
7874
|
const attrRegex = /([A-Za-z_][\w:-]*)="([^"]*)"/g;
|
|
@@ -7597,15 +8005,10 @@ function registerGraphFunction(sdk, kv, provider) {
|
|
|
7597
8005
|
const allNodes = (await kv.list(KV.graphNodes)).filter((n) => !n.stale);
|
|
7598
8006
|
const allEdges = (await kv.list(KV.graphEdges)).filter((e) => !e.stale);
|
|
7599
8007
|
const maxDepth = Math.min(data.maxDepth || 3, 5);
|
|
8008
|
+
const { limit, offset } = resolvePagination(data.limit, data.offset);
|
|
7600
8009
|
if (data.query) {
|
|
7601
8010
|
const lower = data.query.toLowerCase();
|
|
7602
|
-
|
|
7603
|
-
const nodeIds = new Set(matchingNodes.map((n) => n.id));
|
|
7604
|
-
return {
|
|
7605
|
-
nodes: matchingNodes,
|
|
7606
|
-
edges: allEdges.filter((e) => nodeIds.has(e.sourceNodeId) || nodeIds.has(e.targetNodeId)),
|
|
7607
|
-
depth: 0
|
|
7608
|
-
};
|
|
8011
|
+
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);
|
|
7609
8012
|
}
|
|
7610
8013
|
if (data.startNodeId) {
|
|
7611
8014
|
const visited = /* @__PURE__ */ new Set();
|
|
@@ -7637,19 +8040,11 @@ function registerGraphFunction(sdk, kv, provider) {
|
|
|
7637
8040
|
});
|
|
7638
8041
|
}
|
|
7639
8042
|
}
|
|
7640
|
-
return
|
|
7641
|
-
nodes: resultNodes,
|
|
7642
|
-
edges: resultEdges,
|
|
7643
|
-
depth: maxDepth
|
|
7644
|
-
};
|
|
8043
|
+
return paginate(resultNodes, resultEdges, maxDepth, limit, offset);
|
|
7645
8044
|
}
|
|
7646
8045
|
let filtered = allNodes;
|
|
7647
8046
|
if (data.nodeType) filtered = allNodes.filter((n) => n.type === data.nodeType);
|
|
7648
|
-
return
|
|
7649
|
-
nodes: filtered,
|
|
7650
|
-
edges: allEdges,
|
|
7651
|
-
depth: 0
|
|
7652
|
-
};
|
|
8047
|
+
return paginate(rankByDegree(filtered, allEdges), allEdges, 0, limit, offset);
|
|
7653
8048
|
});
|
|
7654
8049
|
sdk.registerFunction("mem::graph-stats", async () => {
|
|
7655
8050
|
const nodes = await kv.list(KV.graphNodes);
|
|
@@ -7666,7 +8061,6 @@ function registerGraphFunction(sdk, kv, provider) {
|
|
|
7666
8061
|
};
|
|
7667
8062
|
});
|
|
7668
8063
|
}
|
|
7669
|
-
|
|
7670
8064
|
//#endregion
|
|
7671
8065
|
//#region src/prompts/consolidation.ts
|
|
7672
8066
|
const SEMANTIC_MERGE_SYSTEM = `You are a memory consolidation engine. Given overlapping episodic memories (session summaries), extract stable factual knowledge.
|
|
@@ -7701,7 +8095,6 @@ Rules:
|
|
|
7701
8095
|
function buildProceduralExtractionPrompt(patterns) {
|
|
7702
8096
|
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")}`;
|
|
7703
8097
|
}
|
|
7704
|
-
|
|
7705
8098
|
//#endregion
|
|
7706
8099
|
//#region src/functions/consolidation-pipeline.ts
|
|
7707
8100
|
function applyDecay(items, decayDays) {
|
|
@@ -7895,7 +8288,6 @@ function registerConsolidationPipelineFunction(sdk, kv, provider) {
|
|
|
7895
8288
|
};
|
|
7896
8289
|
});
|
|
7897
8290
|
}
|
|
7898
|
-
|
|
7899
8291
|
//#endregion
|
|
7900
8292
|
//#region src/functions/team.ts
|
|
7901
8293
|
const VALID_ITEM_TYPES = new Set([
|
|
@@ -7999,7 +8391,6 @@ function registerTeamFunction(sdk, kv, config) {
|
|
|
7999
8391
|
return profile;
|
|
8000
8392
|
});
|
|
8001
8393
|
}
|
|
8002
|
-
|
|
8003
8394
|
//#endregion
|
|
8004
8395
|
//#region src/functions/governance.ts
|
|
8005
8396
|
function registerGovernanceFunction(sdk, kv) {
|
|
@@ -8108,7 +8499,6 @@ function registerGovernanceFunction(sdk, kv) {
|
|
|
8108
8499
|
return queryAudit(kv, data);
|
|
8109
8500
|
});
|
|
8110
8501
|
}
|
|
8111
|
-
|
|
8112
8502
|
//#endregion
|
|
8113
8503
|
//#region src/functions/snapshot.ts
|
|
8114
8504
|
const COMMIT_HASH_RE = /^[0-9a-f]{7,40}$/i;
|
|
@@ -8273,7 +8663,6 @@ function registerSnapshotFunction(sdk, kv, snapshotDir) {
|
|
|
8273
8663
|
}
|
|
8274
8664
|
});
|
|
8275
8665
|
}
|
|
8276
|
-
|
|
8277
8666
|
//#endregion
|
|
8278
8667
|
//#region src/functions/actions.ts
|
|
8279
8668
|
function registerActionsFunction(sdk, kv) {
|
|
@@ -8475,7 +8864,6 @@ async function propagateCompletion(kv, completedActionId) {
|
|
|
8475
8864
|
});
|
|
8476
8865
|
}
|
|
8477
8866
|
}
|
|
8478
|
-
|
|
8479
8867
|
//#endregion
|
|
8480
8868
|
//#region src/functions/frontier.ts
|
|
8481
8869
|
function registerFrontierFunction(sdk, kv) {
|
|
@@ -8580,7 +8968,6 @@ function computeScore(action, edges, now) {
|
|
|
8580
8968
|
if (action.status === "active") score += 15;
|
|
8581
8969
|
return Math.round(score * 100) / 100;
|
|
8582
8970
|
}
|
|
8583
|
-
|
|
8584
8971
|
//#endregion
|
|
8585
8972
|
//#region src/functions/leases.ts
|
|
8586
8973
|
const DEFAULT_LEASE_TTL_MS = 600 * 1e3;
|
|
@@ -8761,7 +9148,6 @@ function registerLeasesFunction(sdk, kv) {
|
|
|
8761
9148
|
};
|
|
8762
9149
|
});
|
|
8763
9150
|
}
|
|
8764
|
-
|
|
8765
9151
|
//#endregion
|
|
8766
9152
|
//#region src/functions/routines.ts
|
|
8767
9153
|
function registerRoutinesFunction(sdk, kv) {
|
|
@@ -9008,7 +9394,6 @@ function registerRoutinesFunction(sdk, kv) {
|
|
|
9008
9394
|
});
|
|
9009
9395
|
});
|
|
9010
9396
|
}
|
|
9011
|
-
|
|
9012
9397
|
//#endregion
|
|
9013
9398
|
//#region src/functions/signals.ts
|
|
9014
9399
|
function registerSignalsFunction(sdk, kv) {
|
|
@@ -9140,7 +9525,6 @@ function registerSignalsFunction(sdk, kv) {
|
|
|
9140
9525
|
};
|
|
9141
9526
|
});
|
|
9142
9527
|
}
|
|
9143
|
-
|
|
9144
9528
|
//#endregion
|
|
9145
9529
|
//#region src/functions/checkpoints.ts
|
|
9146
9530
|
function registerCheckpointsFunction(sdk, kv) {
|
|
@@ -9313,7 +9697,6 @@ function registerCheckpointsFunction(sdk, kv) {
|
|
|
9313
9697
|
};
|
|
9314
9698
|
});
|
|
9315
9699
|
}
|
|
9316
|
-
|
|
9317
9700
|
//#endregion
|
|
9318
9701
|
//#region src/functions/flow-compress.ts
|
|
9319
9702
|
const FLOW_COMPRESS_SYSTEM = `You are a workflow summarizer. Given a completed action chain, produce a concise summary capturing:
|
|
@@ -9458,7 +9841,6 @@ function extractFiles(actions) {
|
|
|
9458
9841
|
}
|
|
9459
9842
|
return Array.from(files);
|
|
9460
9843
|
}
|
|
9461
|
-
|
|
9462
9844
|
//#endregion
|
|
9463
9845
|
//#region src/functions/mesh.ts
|
|
9464
9846
|
function isPrivateIP(ip) {
|
|
@@ -9775,7 +10157,6 @@ async function applySyncData(kv, data, scopes) {
|
|
|
9775
10157
|
if (scopes.includes("graph:edges")) applied += await lwwMergeList(kv, KV.graphEdges, data.graphEdges, "mem:gedge", "createdAt");
|
|
9776
10158
|
return applied;
|
|
9777
10159
|
}
|
|
9778
|
-
|
|
9779
10160
|
//#endregion
|
|
9780
10161
|
//#region src/functions/branch-aware.ts
|
|
9781
10162
|
function execAsync(cmd, args, cwd) {
|
|
@@ -9890,7 +10271,6 @@ function registerBranchAwareFunction(sdk, kv) {
|
|
|
9890
10271
|
};
|
|
9891
10272
|
});
|
|
9892
10273
|
}
|
|
9893
|
-
|
|
9894
10274
|
//#endregion
|
|
9895
10275
|
//#region src/functions/sentinels.ts
|
|
9896
10276
|
const VALID_TYPES = [
|
|
@@ -10211,7 +10591,6 @@ async function unblockLinkedActions(kv, sentinel) {
|
|
|
10211
10591
|
});
|
|
10212
10592
|
return unblockedCount;
|
|
10213
10593
|
}
|
|
10214
|
-
|
|
10215
10594
|
//#endregion
|
|
10216
10595
|
//#region src/functions/sketches.ts
|
|
10217
10596
|
function registerSketchesFunction(sdk, kv) {
|
|
@@ -10453,7 +10832,6 @@ function registerSketchesFunction(sdk, kv) {
|
|
|
10453
10832
|
};
|
|
10454
10833
|
});
|
|
10455
10834
|
}
|
|
10456
|
-
|
|
10457
10835
|
//#endregion
|
|
10458
10836
|
//#region src/functions/crystallize.ts
|
|
10459
10837
|
const CRYSTALLIZE_SYSTEM = `You are summarizing a completed chain of agent actions into a compact digest.
|
|
@@ -10650,7 +11028,6 @@ function parseDigest(response) {
|
|
|
10650
11028
|
};
|
|
10651
11029
|
}
|
|
10652
11030
|
}
|
|
10653
|
-
|
|
10654
11031
|
//#endregion
|
|
10655
11032
|
//#region src/functions/diagnostics.ts
|
|
10656
11033
|
const ALL_CATEGORIES = [
|
|
@@ -11364,7 +11741,6 @@ function registerDiagnosticsFunction(sdk, kv) {
|
|
|
11364
11741
|
};
|
|
11365
11742
|
});
|
|
11366
11743
|
}
|
|
11367
|
-
|
|
11368
11744
|
//#endregion
|
|
11369
11745
|
//#region src/functions/facets.ts
|
|
11370
11746
|
function registerFacetsFunction(sdk, kv) {
|
|
@@ -11536,7 +11912,6 @@ function registerFacetsFunction(sdk, kv) {
|
|
|
11536
11912
|
};
|
|
11537
11913
|
});
|
|
11538
11914
|
}
|
|
11539
|
-
|
|
11540
11915
|
//#endregion
|
|
11541
11916
|
//#region src/functions/verify.ts
|
|
11542
11917
|
function registerVerifyFunction(sdk, kv) {
|
|
@@ -11631,7 +12006,6 @@ async function findObservation(kv, obsId, hintSessionIds) {
|
|
|
11631
12006
|
}
|
|
11632
12007
|
return null;
|
|
11633
12008
|
}
|
|
11634
|
-
|
|
11635
12009
|
//#endregion
|
|
11636
12010
|
//#region src/functions/cascade.ts
|
|
11637
12011
|
function registerCascadeFunction(sdk, kv) {
|
|
@@ -11701,7 +12075,6 @@ function registerCascadeFunction(sdk, kv) {
|
|
|
11701
12075
|
};
|
|
11702
12076
|
});
|
|
11703
12077
|
}
|
|
11704
|
-
|
|
11705
12078
|
//#endregion
|
|
11706
12079
|
//#region src/functions/lessons.ts
|
|
11707
12080
|
function reinforceLesson(lesson) {
|
|
@@ -11887,7 +12260,6 @@ function registerLessonsFunctions(sdk, kv) {
|
|
|
11887
12260
|
};
|
|
11888
12261
|
});
|
|
11889
12262
|
}
|
|
11890
|
-
|
|
11891
12263
|
//#endregion
|
|
11892
12264
|
//#region src/functions/obsidian-export.ts
|
|
11893
12265
|
const DEFAULT_EXPORT_ROOT = join(homedir(), ".agentmemory");
|
|
@@ -11903,6 +12275,20 @@ function resolveVaultDir(vaultDir) {
|
|
|
11903
12275
|
function sanitize(name) {
|
|
11904
12276
|
return name.replace(/[<>:"/\\|?*\x00-\x1f]/g, "_").slice(0, 100);
|
|
11905
12277
|
}
|
|
12278
|
+
function hasExportId(item) {
|
|
12279
|
+
return !!item && typeof item.id === "string" && item.id.length > 0;
|
|
12280
|
+
}
|
|
12281
|
+
function safeArray(value) {
|
|
12282
|
+
return Array.isArray(value) ? value : [];
|
|
12283
|
+
}
|
|
12284
|
+
function safeString(value, fallback = "") {
|
|
12285
|
+
return typeof value === "string" ? value : fallback;
|
|
12286
|
+
}
|
|
12287
|
+
function safeTimestamp(value) {
|
|
12288
|
+
if (typeof value !== "string") return 0;
|
|
12289
|
+
const time = new Date(value).getTime();
|
|
12290
|
+
return Number.isFinite(time) ? time : 0;
|
|
12291
|
+
}
|
|
11906
12292
|
function toFrontmatter(obj) {
|
|
11907
12293
|
const lines = ["---"];
|
|
11908
12294
|
for (const [key, value] of Object.entries(obj)) {
|
|
@@ -11914,6 +12300,11 @@ function toFrontmatter(obj) {
|
|
|
11914
12300
|
return lines.join("\n");
|
|
11915
12301
|
}
|
|
11916
12302
|
function memoryToMd(m) {
|
|
12303
|
+
const concepts = safeArray(m.concepts);
|
|
12304
|
+
const files = safeArray(m.files);
|
|
12305
|
+
const relatedIds = safeArray(m.relatedIds);
|
|
12306
|
+
const supersedes = safeArray(m.supersedes);
|
|
12307
|
+
const title = safeString(m.title, m.id);
|
|
11917
12308
|
const fm = toFrontmatter({
|
|
11918
12309
|
id: m.id,
|
|
11919
12310
|
type: m.type,
|
|
@@ -11921,24 +12312,28 @@ function memoryToMd(m) {
|
|
|
11921
12312
|
updated: m.updatedAt,
|
|
11922
12313
|
strength: m.strength,
|
|
11923
12314
|
version: m.version,
|
|
11924
|
-
concepts
|
|
11925
|
-
files
|
|
12315
|
+
concepts,
|
|
12316
|
+
files
|
|
11926
12317
|
});
|
|
11927
|
-
const
|
|
11928
|
-
const
|
|
12318
|
+
const relatedLines = relatedIds.map((id) => `- [[${id}]]`).join("\n");
|
|
12319
|
+
const supersedesLines = supersedes.map((id) => `- [[${id}]] (superseded)`).join("\n");
|
|
11929
12320
|
const sections = [
|
|
11930
12321
|
fm,
|
|
11931
12322
|
"",
|
|
11932
|
-
`# ${
|
|
12323
|
+
`# ${title}`,
|
|
11933
12324
|
"",
|
|
11934
|
-
m.content
|
|
12325
|
+
safeString(m.content)
|
|
11935
12326
|
];
|
|
11936
|
-
if (
|
|
11937
|
-
if (
|
|
11938
|
-
if (
|
|
12327
|
+
if (concepts.length > 0) sections.push("", "## Concepts", concepts.map((c) => `#${c.replace(/\s+/g, "-")}`).join(" "));
|
|
12328
|
+
if (relatedLines) sections.push("", "## Related", relatedLines);
|
|
12329
|
+
if (supersedesLines) sections.push("", "## Supersedes", supersedesLines);
|
|
11939
12330
|
return sections.join("\n");
|
|
11940
12331
|
}
|
|
11941
12332
|
function lessonToMd(l) {
|
|
12333
|
+
const tags = safeArray(l.tags);
|
|
12334
|
+
const sourceIds = safeArray(l.sourceIds);
|
|
12335
|
+
const content = safeString(l.content);
|
|
12336
|
+
const headline = content ? content.slice(0, 80) : l.id;
|
|
11942
12337
|
const fm = toFrontmatter({
|
|
11943
12338
|
id: l.id,
|
|
11944
12339
|
type: "lesson",
|
|
@@ -11948,66 +12343,76 @@ function lessonToMd(l) {
|
|
|
11948
12343
|
created: l.createdAt,
|
|
11949
12344
|
updated: l.updatedAt,
|
|
11950
12345
|
project: l.project,
|
|
11951
|
-
tags
|
|
12346
|
+
tags,
|
|
11952
12347
|
decayRate: l.decayRate
|
|
11953
12348
|
});
|
|
11954
|
-
const sourceLinks =
|
|
12349
|
+
const sourceLinks = sourceIds.map((id) => `- [[${id}]]`).join("\n");
|
|
11955
12350
|
const sections = [
|
|
11956
12351
|
fm,
|
|
11957
12352
|
"",
|
|
11958
|
-
`# Lesson: ${
|
|
12353
|
+
`# Lesson: ${headline}`,
|
|
11959
12354
|
"",
|
|
11960
|
-
|
|
12355
|
+
content
|
|
11961
12356
|
];
|
|
11962
12357
|
if (l.context) sections.push("", "## Context", l.context);
|
|
11963
|
-
if (
|
|
12358
|
+
if (tags.length > 0) sections.push("", "## Tags", tags.map((t) => `#${t.replace(/\s+/g, "-")}`).join(" "));
|
|
11964
12359
|
if (sourceLinks) sections.push("", "## Sources", sourceLinks);
|
|
11965
12360
|
return sections.join("\n");
|
|
11966
12361
|
}
|
|
11967
12362
|
function crystalToMd(c) {
|
|
12363
|
+
const keyOutcomes = safeArray(c.keyOutcomes);
|
|
12364
|
+
const lessons = safeArray(c.lessons);
|
|
12365
|
+
const filesAffected = safeArray(c.filesAffected);
|
|
12366
|
+
const sourceActionIds = safeArray(c.sourceActionIds);
|
|
12367
|
+
const narrative = safeString(c.narrative);
|
|
12368
|
+
const headline = narrative ? narrative.slice(0, 80) : c.id;
|
|
11968
12369
|
const fm = toFrontmatter({
|
|
11969
12370
|
id: c.id,
|
|
11970
12371
|
type: "crystal",
|
|
11971
12372
|
created: c.createdAt,
|
|
11972
12373
|
project: c.project,
|
|
11973
12374
|
sessionId: c.sessionId,
|
|
11974
|
-
filesAffected
|
|
12375
|
+
filesAffected
|
|
11975
12376
|
});
|
|
11976
|
-
const actionLinks =
|
|
12377
|
+
const actionLinks = sourceActionIds.map((id) => `- [[${id}]]`).join("\n");
|
|
11977
12378
|
const sections = [
|
|
11978
12379
|
fm,
|
|
11979
12380
|
"",
|
|
11980
|
-
`# Crystal: ${
|
|
12381
|
+
`# Crystal: ${headline}`,
|
|
11981
12382
|
"",
|
|
11982
|
-
|
|
12383
|
+
narrative,
|
|
11983
12384
|
"",
|
|
11984
12385
|
"## Key Outcomes",
|
|
11985
|
-
...
|
|
12386
|
+
...keyOutcomes.map((o) => `- ${o}`)
|
|
11986
12387
|
];
|
|
11987
|
-
if (
|
|
11988
|
-
if (
|
|
12388
|
+
if (lessons.length > 0) sections.push("", "## Lessons", ...lessons.map((l) => `- ${l}`));
|
|
12389
|
+
if (filesAffected.length > 0) sections.push("", "## Files", ...filesAffected.map((f) => `- \`${f}\``));
|
|
11989
12390
|
if (actionLinks) sections.push("", "## Source Actions", actionLinks);
|
|
11990
12391
|
return sections.join("\n");
|
|
11991
12392
|
}
|
|
11992
12393
|
function sessionToMd(s) {
|
|
12394
|
+
const project = safeString(s.project, "unknown");
|
|
12395
|
+
const status = safeString(s.status, "unknown");
|
|
12396
|
+
const startedAt = safeString(s.startedAt, "");
|
|
12397
|
+
const cwd = safeString(s.cwd, "");
|
|
11993
12398
|
return [
|
|
11994
12399
|
toFrontmatter({
|
|
11995
12400
|
id: s.id,
|
|
11996
12401
|
type: "session",
|
|
11997
|
-
project
|
|
11998
|
-
status
|
|
11999
|
-
started:
|
|
12402
|
+
project,
|
|
12403
|
+
status,
|
|
12404
|
+
started: startedAt || void 0,
|
|
12000
12405
|
ended: s.endedAt,
|
|
12001
12406
|
observations: s.observationCount
|
|
12002
12407
|
}),
|
|
12003
12408
|
"",
|
|
12004
|
-
`# Session: ${
|
|
12409
|
+
`# Session: ${project}`,
|
|
12005
12410
|
"",
|
|
12006
|
-
`**Status:** ${
|
|
12007
|
-
`**Started:** ${
|
|
12411
|
+
`**Status:** ${status}`,
|
|
12412
|
+
startedAt ? `**Started:** ${startedAt}` : "",
|
|
12008
12413
|
s.endedAt ? `**Ended:** ${s.endedAt}` : "",
|
|
12009
|
-
`**Observations:** ${s.observationCount}`,
|
|
12010
|
-
`**CWD:** \`${
|
|
12414
|
+
`**Observations:** ${s.observationCount ?? 0}`,
|
|
12415
|
+
cwd ? `**CWD:** \`${cwd}\`` : ""
|
|
12011
12416
|
].filter(Boolean).join("\n");
|
|
12012
12417
|
}
|
|
12013
12418
|
function registerObsidianExportFunction(sdk, kv) {
|
|
@@ -12043,122 +12448,131 @@ function registerObsidianExportFunction(sdk, kv) {
|
|
|
12043
12448
|
crystals: join(vaultDir, "crystals"),
|
|
12044
12449
|
sessions: join(vaultDir, "sessions")
|
|
12045
12450
|
};
|
|
12046
|
-
|
|
12047
|
-
|
|
12048
|
-
|
|
12049
|
-
|
|
12050
|
-
|
|
12051
|
-
|
|
12052
|
-
|
|
12053
|
-
|
|
12054
|
-
|
|
12055
|
-
|
|
12056
|
-
|
|
12057
|
-
|
|
12058
|
-
|
|
12059
|
-
|
|
12060
|
-
|
|
12061
|
-
|
|
12062
|
-
|
|
12063
|
-
|
|
12064
|
-
|
|
12065
|
-
const
|
|
12066
|
-
|
|
12067
|
-
|
|
12068
|
-
|
|
12069
|
-
|
|
12070
|
-
|
|
12071
|
-
|
|
12072
|
-
|
|
12073
|
-
|
|
12074
|
-
|
|
12075
|
-
|
|
12076
|
-
|
|
12451
|
+
try {
|
|
12452
|
+
await Promise.all(Object.values(dirs).map((dir) => mkdir(dir, { recursive: true })));
|
|
12453
|
+
const stats = {
|
|
12454
|
+
memories: 0,
|
|
12455
|
+
lessons: 0,
|
|
12456
|
+
crystals: 0,
|
|
12457
|
+
sessions: 0
|
|
12458
|
+
};
|
|
12459
|
+
const errors = [];
|
|
12460
|
+
const memoryMoc = [];
|
|
12461
|
+
const lessonMoc = [];
|
|
12462
|
+
const crystalMoc = [];
|
|
12463
|
+
const sessionMoc = [];
|
|
12464
|
+
const [memories, lessons, crystals, sessions] = await Promise.all([
|
|
12465
|
+
exportTypes.has("memories") ? kv.list(KV.memories) : Promise.resolve([]),
|
|
12466
|
+
exportTypes.has("lessons") ? kv.list(KV.lessons) : Promise.resolve([]),
|
|
12467
|
+
exportTypes.has("crystals") ? kv.list(KV.crystals) : Promise.resolve([]),
|
|
12468
|
+
exportTypes.has("sessions") ? kv.list(KV.sessions) : Promise.resolve([])
|
|
12469
|
+
]);
|
|
12470
|
+
for (const m of memories.filter((m) => hasExportId(m) && m.isLatest === true)) {
|
|
12471
|
+
const filename = `${sanitize(m.id)}.md`;
|
|
12472
|
+
const filepath = join(dirs.memories, filename);
|
|
12473
|
+
try {
|
|
12474
|
+
await writeFile(filepath, memoryToMd(m));
|
|
12475
|
+
stats.memories++;
|
|
12476
|
+
memoryMoc.push(`- [[memories/${sanitize(m.id)}|${safeString(m.title, m.id)}]] (${m.type}, strength: ${m.strength ?? 0})`);
|
|
12477
|
+
} catch (err) {
|
|
12478
|
+
errors.push({
|
|
12479
|
+
id: m.id,
|
|
12480
|
+
path: filepath,
|
|
12481
|
+
error: err instanceof Error ? err.message : String(err)
|
|
12482
|
+
});
|
|
12483
|
+
}
|
|
12077
12484
|
}
|
|
12078
|
-
|
|
12079
|
-
|
|
12080
|
-
|
|
12081
|
-
|
|
12082
|
-
|
|
12083
|
-
|
|
12084
|
-
|
|
12085
|
-
|
|
12086
|
-
|
|
12087
|
-
|
|
12088
|
-
|
|
12089
|
-
|
|
12090
|
-
|
|
12091
|
-
|
|
12485
|
+
for (const l of lessons.filter((l) => hasExportId(l) && !l.deleted)) {
|
|
12486
|
+
const filename = `${sanitize(l.id)}.md`;
|
|
12487
|
+
const filepath = join(dirs.lessons, filename);
|
|
12488
|
+
try {
|
|
12489
|
+
await writeFile(filepath, lessonToMd(l));
|
|
12490
|
+
stats.lessons++;
|
|
12491
|
+
const headline = safeString(l.content).slice(0, 60) || l.id;
|
|
12492
|
+
lessonMoc.push(`- [[lessons/${sanitize(l.id)}|${headline}]] (confidence: ${l.confidence ?? 0})`);
|
|
12493
|
+
} catch (err) {
|
|
12494
|
+
errors.push({
|
|
12495
|
+
id: l.id,
|
|
12496
|
+
path: filepath,
|
|
12497
|
+
error: err instanceof Error ? err.message : String(err)
|
|
12498
|
+
});
|
|
12499
|
+
}
|
|
12092
12500
|
}
|
|
12093
|
-
|
|
12094
|
-
|
|
12095
|
-
|
|
12096
|
-
|
|
12097
|
-
|
|
12098
|
-
|
|
12099
|
-
|
|
12100
|
-
|
|
12101
|
-
|
|
12102
|
-
|
|
12103
|
-
|
|
12104
|
-
|
|
12105
|
-
|
|
12106
|
-
|
|
12501
|
+
for (const c of crystals.filter(hasExportId)) {
|
|
12502
|
+
const filename = `${sanitize(c.id)}.md`;
|
|
12503
|
+
const filepath = join(dirs.crystals, filename);
|
|
12504
|
+
try {
|
|
12505
|
+
await writeFile(filepath, crystalToMd(c));
|
|
12506
|
+
stats.crystals++;
|
|
12507
|
+
const headline = safeString(c.narrative).slice(0, 60) || c.id;
|
|
12508
|
+
crystalMoc.push(`- [[crystals/${sanitize(c.id)}|${headline}]]`);
|
|
12509
|
+
} catch (err) {
|
|
12510
|
+
errors.push({
|
|
12511
|
+
id: c.id,
|
|
12512
|
+
path: filepath,
|
|
12513
|
+
error: err instanceof Error ? err.message : String(err)
|
|
12514
|
+
});
|
|
12515
|
+
}
|
|
12107
12516
|
}
|
|
12108
|
-
|
|
12109
|
-
|
|
12110
|
-
|
|
12111
|
-
|
|
12112
|
-
|
|
12113
|
-
|
|
12114
|
-
|
|
12115
|
-
|
|
12116
|
-
|
|
12117
|
-
|
|
12118
|
-
|
|
12119
|
-
|
|
12120
|
-
|
|
12121
|
-
|
|
12122
|
-
}
|
|
12517
|
+
const recent = sessions.filter(hasExportId).sort((a, b) => safeTimestamp(b.startedAt) - safeTimestamp(a.startedAt)).slice(0, 50);
|
|
12518
|
+
for (const s of recent) {
|
|
12519
|
+
const filename = `${sanitize(s.id)}.md`;
|
|
12520
|
+
const filepath = join(dirs.sessions, filename);
|
|
12521
|
+
try {
|
|
12522
|
+
await writeFile(filepath, sessionToMd(s));
|
|
12523
|
+
stats.sessions++;
|
|
12524
|
+
sessionMoc.push(`- [[sessions/${sanitize(s.id)}|${safeString(s.project, "unknown")} (${safeString(s.status, "unknown")})]]`);
|
|
12525
|
+
} catch (err) {
|
|
12526
|
+
errors.push({
|
|
12527
|
+
id: s.id,
|
|
12528
|
+
path: filepath,
|
|
12529
|
+
error: err instanceof Error ? err.message : String(err)
|
|
12530
|
+
});
|
|
12531
|
+
}
|
|
12123
12532
|
}
|
|
12533
|
+
const exportedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
12534
|
+
const moc = [
|
|
12535
|
+
"---",
|
|
12536
|
+
"type: moc",
|
|
12537
|
+
`exported: ${exportedAt}`,
|
|
12538
|
+
"---",
|
|
12539
|
+
"",
|
|
12540
|
+
"# agentmemory vault",
|
|
12541
|
+
"",
|
|
12542
|
+
`Exported: ${exportedAt}`,
|
|
12543
|
+
"",
|
|
12544
|
+
`## Memories (${stats.memories})`,
|
|
12545
|
+
...memoryMoc,
|
|
12546
|
+
"",
|
|
12547
|
+
`## Lessons (${stats.lessons})`,
|
|
12548
|
+
...lessonMoc,
|
|
12549
|
+
"",
|
|
12550
|
+
`## Crystals (${stats.crystals})`,
|
|
12551
|
+
...crystalMoc,
|
|
12552
|
+
"",
|
|
12553
|
+
`## Sessions (${stats.sessions})`,
|
|
12554
|
+
...sessionMoc
|
|
12555
|
+
].join("\n");
|
|
12556
|
+
await writeFile(join(vaultDir, "MOC.md"), moc);
|
|
12557
|
+
await recordAudit(kv, "obsidian_export", "mem::obsidian-export", [], {
|
|
12558
|
+
vaultDir,
|
|
12559
|
+
stats
|
|
12560
|
+
});
|
|
12561
|
+
return {
|
|
12562
|
+
success: true,
|
|
12563
|
+
exported: stats,
|
|
12564
|
+
errors: errors.length > 0 ? errors : void 0,
|
|
12565
|
+
vaultDir
|
|
12566
|
+
};
|
|
12567
|
+
} catch (err) {
|
|
12568
|
+
return {
|
|
12569
|
+
success: false,
|
|
12570
|
+
error: err instanceof Error ? err.message : String(err),
|
|
12571
|
+
vaultDir
|
|
12572
|
+
};
|
|
12124
12573
|
}
|
|
12125
|
-
const exportedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
12126
|
-
const moc = [
|
|
12127
|
-
"---",
|
|
12128
|
-
"type: moc",
|
|
12129
|
-
`exported: ${exportedAt}`,
|
|
12130
|
-
"---",
|
|
12131
|
-
"",
|
|
12132
|
-
"# agentmemory vault",
|
|
12133
|
-
"",
|
|
12134
|
-
`Exported: ${exportedAt}`,
|
|
12135
|
-
"",
|
|
12136
|
-
`## Memories (${stats.memories})`,
|
|
12137
|
-
...memoryMoc,
|
|
12138
|
-
"",
|
|
12139
|
-
`## Lessons (${stats.lessons})`,
|
|
12140
|
-
...lessonMoc,
|
|
12141
|
-
"",
|
|
12142
|
-
`## Crystals (${stats.crystals})`,
|
|
12143
|
-
...crystalMoc,
|
|
12144
|
-
"",
|
|
12145
|
-
`## Sessions (${stats.sessions})`,
|
|
12146
|
-
...sessionMoc
|
|
12147
|
-
].join("\n");
|
|
12148
|
-
await writeFile(join(vaultDir, "MOC.md"), moc);
|
|
12149
|
-
await recordAudit(kv, "obsidian_export", "mem::obsidian-export", [], {
|
|
12150
|
-
vaultDir,
|
|
12151
|
-
stats
|
|
12152
|
-
});
|
|
12153
|
-
return {
|
|
12154
|
-
success: true,
|
|
12155
|
-
exported: stats,
|
|
12156
|
-
errors: errors.length > 0 ? errors : void 0,
|
|
12157
|
-
vaultDir
|
|
12158
|
-
};
|
|
12159
12574
|
});
|
|
12160
12575
|
}
|
|
12161
|
-
|
|
12162
12576
|
//#endregion
|
|
12163
12577
|
//#region src/prompts/reflect.ts
|
|
12164
12578
|
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.
|
|
@@ -12186,7 +12600,6 @@ function buildReflectPrompt(cluster) {
|
|
|
12186
12600
|
if (cluster.crystalNarratives.length > 0) sections.push("\n## Completed Work Summaries", ...cluster.crystalNarratives.map((n) => `- ${n}`));
|
|
12187
12601
|
return `Synthesize higher-order insights from this cluster of related memories:\n\n${sections.join("\n")}`;
|
|
12188
12602
|
}
|
|
12189
|
-
|
|
12190
12603
|
//#endregion
|
|
12191
12604
|
//#region src/functions/reflect.ts
|
|
12192
12605
|
function reinforceInsight(insight) {
|
|
@@ -12479,7 +12892,6 @@ function registerReflectFunctions(sdk, kv, provider) {
|
|
|
12479
12892
|
};
|
|
12480
12893
|
});
|
|
12481
12894
|
}
|
|
12482
|
-
|
|
12483
12895
|
//#endregion
|
|
12484
12896
|
//#region src/functions/working-memory.ts
|
|
12485
12897
|
const CORE_SCOPE = "mem:core-memory";
|
|
@@ -12654,7 +13066,6 @@ function registerWorkingMemoryFunctions(sdk, kv, tokenBudget) {
|
|
|
12654
13066
|
};
|
|
12655
13067
|
});
|
|
12656
13068
|
}
|
|
12657
|
-
|
|
12658
13069
|
//#endregion
|
|
12659
13070
|
//#region src/functions/skill-extract.ts
|
|
12660
13071
|
const SKILL_EXTRACT_SYSTEM = `You are a skill extraction engine. Given a completed multi-step task session, extract a reusable procedural skill document.
|
|
@@ -12857,7 +13268,6 @@ function registerSkillExtractFunctions(sdk, kv, provider) {
|
|
|
12857
13268
|
};
|
|
12858
13269
|
});
|
|
12859
13270
|
}
|
|
12860
|
-
|
|
12861
13271
|
//#endregion
|
|
12862
13272
|
//#region src/functions/sliding-window.ts
|
|
12863
13273
|
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.
|
|
@@ -13044,7 +13454,6 @@ function registerSlidingWindowFunction(sdk, kv, provider) {
|
|
|
13044
13454
|
};
|
|
13045
13455
|
});
|
|
13046
13456
|
}
|
|
13047
|
-
|
|
13048
13457
|
//#endregion
|
|
13049
13458
|
//#region src/functions/temporal-graph.ts
|
|
13050
13459
|
const TEMPORAL_EXTRACTION_SYSTEM = `You are a temporal knowledge extraction engine. Given observations, extract entities AND their temporal relationships with full context metadata.
|
|
@@ -13308,7 +13717,6 @@ function buildTimeline(edges) {
|
|
|
13308
13717
|
context: e.context
|
|
13309
13718
|
}));
|
|
13310
13719
|
}
|
|
13311
|
-
|
|
13312
13720
|
//#endregion
|
|
13313
13721
|
//#region src/functions/retention.ts
|
|
13314
13722
|
const DEFAULT_DECAY = {
|
|
@@ -13542,7 +13950,6 @@ function registerRetentionFunctions(sdk, kv) {
|
|
|
13542
13950
|
};
|
|
13543
13951
|
});
|
|
13544
13952
|
}
|
|
13545
|
-
|
|
13546
13953
|
//#endregion
|
|
13547
13954
|
//#region src/functions/compress-file.ts
|
|
13548
13955
|
const SENSITIVE_PATH_TERMS = [
|
|
@@ -13684,7 +14091,6 @@ function registerCompressFileFunction(sdk, kv, provider) {
|
|
|
13684
14091
|
};
|
|
13685
14092
|
});
|
|
13686
14093
|
}
|
|
13687
|
-
|
|
13688
14094
|
//#endregion
|
|
13689
14095
|
//#region src/replay/jsonl-parser.ts
|
|
13690
14096
|
function deriveProject(cwd) {
|
|
@@ -13811,7 +14217,6 @@ function parseJsonlText(text, fallbackSessionId) {
|
|
|
13811
14217
|
observations
|
|
13812
14218
|
};
|
|
13813
14219
|
}
|
|
13814
|
-
|
|
13815
14220
|
//#endregion
|
|
13816
14221
|
//#region src/replay/timeline.ts
|
|
13817
14222
|
const DEFAULT_CHARS_PER_SEC = 40;
|
|
@@ -13906,10 +14311,6 @@ function projectTimeline(observations) {
|
|
|
13906
14311
|
events
|
|
13907
14312
|
};
|
|
13908
14313
|
}
|
|
13909
|
-
|
|
13910
|
-
//#endregion
|
|
13911
|
-
//#region src/functions/replay.ts
|
|
13912
|
-
const MAX_FILES_DEFAULT = 200;
|
|
13913
14314
|
const MAX_FILES_UPPER_BOUND = 1e3;
|
|
13914
14315
|
const SENSITIVE_PATH_PATTERNS = [
|
|
13915
14316
|
/(^|[\\/_.-])secret([\\/_.-]|s?$)/i,
|
|
@@ -14129,7 +14530,7 @@ function registerReplayFunctions(sdk, kv) {
|
|
|
14129
14530
|
error: "path not found"
|
|
14130
14531
|
};
|
|
14131
14532
|
}
|
|
14132
|
-
const maxFiles = Number.isInteger(data.maxFiles) && data.maxFiles > 0 ? Math.min(data.maxFiles, MAX_FILES_UPPER_BOUND) :
|
|
14533
|
+
const maxFiles = Number.isInteger(data.maxFiles) && data.maxFiles > 0 ? Math.min(data.maxFiles, MAX_FILES_UPPER_BOUND) : 200;
|
|
14133
14534
|
let files = [];
|
|
14134
14535
|
let truncated = false;
|
|
14135
14536
|
let discovered = 0;
|
|
@@ -14185,7 +14586,8 @@ function registerReplayFunctions(sdk, kv) {
|
|
|
14185
14586
|
const existingTags = existing.tags || [];
|
|
14186
14587
|
if (!existingTags.includes("jsonl-import")) existing.tags = [...existingTags, "jsonl-import"];
|
|
14187
14588
|
if (!existing.firstPrompt && firstPrompt) existing.firstPrompt = firstPrompt;
|
|
14188
|
-
|
|
14589
|
+
if (!existing.id) existing.id = parsed.sessionId;
|
|
14590
|
+
await kv.set(KV.sessions, parsed.sessionId, existing);
|
|
14189
14591
|
} else {
|
|
14190
14592
|
const session = {
|
|
14191
14593
|
id: parsed.sessionId,
|
|
@@ -14231,7 +14633,6 @@ function registerReplayFunctions(sdk, kv) {
|
|
|
14231
14633
|
};
|
|
14232
14634
|
});
|
|
14233
14635
|
}
|
|
14234
|
-
|
|
14235
14636
|
//#endregion
|
|
14236
14637
|
//#region src/health/thresholds.ts
|
|
14237
14638
|
const DEFAULTS = {
|
|
@@ -14290,7 +14691,6 @@ function evaluateHealth(snapshot, config = {}) {
|
|
|
14290
14691
|
notes
|
|
14291
14692
|
};
|
|
14292
14693
|
}
|
|
14293
|
-
|
|
14294
14694
|
//#endregion
|
|
14295
14695
|
//#region src/health/monitor.ts
|
|
14296
14696
|
function registerHealthMonitor(sdk, kv) {
|
|
@@ -14378,7 +14778,6 @@ function registerHealthMonitor(sdk, kv) {
|
|
|
14378
14778
|
async function getLatestHealth(kv) {
|
|
14379
14779
|
return kv.get(KV.health, "latest");
|
|
14380
14780
|
}
|
|
14381
|
-
|
|
14382
14781
|
//#endregion
|
|
14383
14782
|
//#region src/auth.ts
|
|
14384
14783
|
const hmacKey = randomBytes(32);
|
|
@@ -14404,7 +14803,6 @@ function buildViewerCsp(nonce) {
|
|
|
14404
14803
|
"font-src 'self'"
|
|
14405
14804
|
].join("; ");
|
|
14406
14805
|
}
|
|
14407
|
-
|
|
14408
14806
|
//#endregion
|
|
14409
14807
|
//#region src/viewer/document.ts
|
|
14410
14808
|
const VIEWER_VERSION_PLACEHOLDER = "__AGENTMEMORY_VERSION__";
|
|
@@ -14430,7 +14828,6 @@ function renderViewerDocument() {
|
|
|
14430
14828
|
csp: buildViewerCsp(nonce)
|
|
14431
14829
|
};
|
|
14432
14830
|
}
|
|
14433
|
-
|
|
14434
14831
|
//#endregion
|
|
14435
14832
|
//#region src/viewer/server.ts
|
|
14436
14833
|
function loadViewerFavicon() {
|
|
@@ -14445,18 +14842,30 @@ function loadViewerFavicon() {
|
|
|
14445
14842
|
} catch {}
|
|
14446
14843
|
return null;
|
|
14447
14844
|
}
|
|
14845
|
+
const VIEWER_FAVICON = loadViewerFavicon();
|
|
14448
14846
|
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());
|
|
14449
|
-
|
|
14450
|
-
|
|
14847
|
+
function readAllowedHostsOverride() {
|
|
14848
|
+
return (process.env.VIEWER_ALLOWED_HOSTS || "").split(",").map((h) => h.trim().toLowerCase()).filter(Boolean);
|
|
14849
|
+
}
|
|
14850
|
+
function resolveViewerHost() {
|
|
14851
|
+
return process.env.AGENTMEMORY_VIEWER_HOST?.trim() || "127.0.0.1";
|
|
14852
|
+
}
|
|
14853
|
+
function isLoopbackHost(host) {
|
|
14854
|
+
const h = host.trim().toLowerCase();
|
|
14855
|
+
return h === "127.0.0.1" || h === "::1" || h === "localhost";
|
|
14856
|
+
}
|
|
14857
|
+
function buildAllowedHosts(origins, listenPort, bindHost = "127.0.0.1") {
|
|
14451
14858
|
const hosts = /* @__PURE__ */ new Set();
|
|
14452
|
-
|
|
14453
|
-
const
|
|
14454
|
-
|
|
14455
|
-
|
|
14456
|
-
|
|
14457
|
-
|
|
14458
|
-
|
|
14459
|
-
|
|
14859
|
+
if (isLoopbackHost(bindHost)) {
|
|
14860
|
+
for (const o of origins) try {
|
|
14861
|
+
const parsed = new URL(o);
|
|
14862
|
+
if (parsed.host) hosts.add(parsed.host.toLowerCase());
|
|
14863
|
+
} catch {}
|
|
14864
|
+
hosts.add(`localhost:${listenPort}`);
|
|
14865
|
+
hosts.add(`127.0.0.1:${listenPort}`);
|
|
14866
|
+
hosts.add(`[::1]:${listenPort}`);
|
|
14867
|
+
}
|
|
14868
|
+
for (const h of readAllowedHostsOverride()) hosts.add(h);
|
|
14460
14869
|
return hosts;
|
|
14461
14870
|
}
|
|
14462
14871
|
function isHostAllowed(headerHost, allowed) {
|
|
@@ -14465,11 +14874,16 @@ function isHostAllowed(headerHost, allowed) {
|
|
|
14465
14874
|
if (!lower) return false;
|
|
14466
14875
|
return allowed.has(lower);
|
|
14467
14876
|
}
|
|
14877
|
+
function requireInboundBearer(authHeader, secret) {
|
|
14878
|
+
if (typeof authHeader !== "string") return false;
|
|
14879
|
+
const match = /^Bearer\s+(\S+)\s*$/i.exec(authHeader);
|
|
14880
|
+
if (!match) return false;
|
|
14881
|
+
return timingSafeCompare(match[1], secret);
|
|
14882
|
+
}
|
|
14468
14883
|
function corsHeaders(req) {
|
|
14469
14884
|
const origin = req.headers.origin || "";
|
|
14470
|
-
const allowed = ALLOWED_ORIGINS.includes(origin) ? origin : ALLOWED_ORIGINS[0];
|
|
14471
14885
|
return {
|
|
14472
|
-
"Access-Control-Allow-Origin":
|
|
14886
|
+
"Access-Control-Allow-Origin": ALLOWED_ORIGINS.includes(origin) ? origin : ALLOWED_ORIGINS[0],
|
|
14473
14887
|
"Access-Control-Allow-Methods": "GET, POST, DELETE, OPTIONS",
|
|
14474
14888
|
"Access-Control-Allow-Headers": "Content-Type, Authorization",
|
|
14475
14889
|
Vary: "Origin"
|
|
@@ -14513,16 +14927,29 @@ function getBoundViewerPort() {
|
|
|
14513
14927
|
function getViewerSkipped() {
|
|
14514
14928
|
return viewerSkipped;
|
|
14515
14929
|
}
|
|
14930
|
+
var ViewerConfigError = class extends Error {
|
|
14931
|
+
constructor(message) {
|
|
14932
|
+
super(message);
|
|
14933
|
+
this.name = "ViewerConfigError";
|
|
14934
|
+
}
|
|
14935
|
+
};
|
|
14516
14936
|
function startViewerServer(port, _kv, _sdk, secret, restPort) {
|
|
14517
14937
|
boundViewerPort = null;
|
|
14518
14938
|
viewerSkipped = false;
|
|
14519
14939
|
const resolvedRestPort = restPort ?? port - 2;
|
|
14520
14940
|
const requestedPort = port;
|
|
14941
|
+
const host = resolveViewerHost();
|
|
14942
|
+
let inboundSecret = null;
|
|
14943
|
+
if (!isLoopbackHost(host)) {
|
|
14944
|
+
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.`);
|
|
14945
|
+
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.`);
|
|
14946
|
+
inboundSecret = secret;
|
|
14947
|
+
}
|
|
14521
14948
|
let allowedHosts = null;
|
|
14522
14949
|
const server = createServer(async (req, res) => {
|
|
14523
14950
|
if (!allowedHosts) {
|
|
14524
14951
|
const addr = server.address();
|
|
14525
|
-
allowedHosts = buildAllowedHosts(ALLOWED_ORIGINS, addr && typeof addr === "object" && "port" in addr ? addr.port : port);
|
|
14952
|
+
allowedHosts = buildAllowedHosts(ALLOWED_ORIGINS, addr && typeof addr === "object" && "port" in addr ? addr.port : port, host);
|
|
14526
14953
|
}
|
|
14527
14954
|
if (!isHostAllowed(req.headers.host, allowedHosts)) {
|
|
14528
14955
|
res.writeHead(403, { "Content-Type": "text/plain" });
|
|
@@ -14558,19 +14985,26 @@ function startViewerServer(port, _kv, _sdk, secret, restPort) {
|
|
|
14558
14985
|
return;
|
|
14559
14986
|
}
|
|
14560
14987
|
if (method === "GET" && pathname === "/favicon.svg") {
|
|
14561
|
-
|
|
14562
|
-
if (favicon) {
|
|
14988
|
+
if (VIEWER_FAVICON) {
|
|
14563
14989
|
res.writeHead(200, {
|
|
14564
14990
|
"Content-Type": "image/svg+xml",
|
|
14565
14991
|
"Cache-Control": "public, max-age=3600"
|
|
14566
14992
|
});
|
|
14567
|
-
res.end(
|
|
14993
|
+
res.end(VIEWER_FAVICON);
|
|
14568
14994
|
return;
|
|
14569
14995
|
}
|
|
14570
14996
|
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
14571
14997
|
res.end("favicon not found");
|
|
14572
14998
|
return;
|
|
14573
14999
|
}
|
|
15000
|
+
if (inboundSecret !== null && !requireInboundBearer(req.headers.authorization, inboundSecret)) {
|
|
15001
|
+
res.writeHead(401, {
|
|
15002
|
+
"Content-Type": "text/plain",
|
|
15003
|
+
"WWW-Authenticate": "Bearer realm=\"agentmemory-viewer\""
|
|
15004
|
+
});
|
|
15005
|
+
res.end("unauthorized");
|
|
15006
|
+
return;
|
|
15007
|
+
}
|
|
14574
15008
|
try {
|
|
14575
15009
|
await proxyToRestApi(resolvedRestPort, pathname, qs, method, req, res, secret);
|
|
14576
15010
|
} catch (err) {
|
|
@@ -14581,17 +15015,23 @@ function startViewerServer(port, _kv, _sdk, secret, restPort) {
|
|
|
14581
15015
|
let attempt = 0;
|
|
14582
15016
|
let currentPort = requestedPort;
|
|
14583
15017
|
const tryListen = () => {
|
|
14584
|
-
server.listen(currentPort,
|
|
15018
|
+
server.listen(currentPort, host);
|
|
14585
15019
|
};
|
|
14586
15020
|
server.on("listening", () => {
|
|
14587
15021
|
const addr = server.address();
|
|
14588
|
-
|
|
15022
|
+
const actualPort = addr && typeof addr === "object" && "port" in addr ? addr.port : currentPort;
|
|
15023
|
+
boundViewerPort = actualPort;
|
|
14589
15024
|
viewerSkipped = false;
|
|
14590
|
-
if (
|
|
14591
|
-
|
|
15025
|
+
if (inboundSecret !== null) {
|
|
15026
|
+
const allowedHosts = readAllowedHostsOverride().join(", ");
|
|
15027
|
+
console.log(`[agentmemory] Viewer: http://localhost:${actualPort} (bound to ${host}; inbound Bearer required; allowed Host headers: ${allowedHosts})`);
|
|
15028
|
+
return;
|
|
15029
|
+
}
|
|
15030
|
+
if (actualPort === requestedPort) console.log(`[agentmemory] Viewer: http://localhost:${actualPort}`);
|
|
15031
|
+
else console.log(`[agentmemory] Viewer started on http://localhost:${actualPort} (fallback from ${requestedPort})`);
|
|
14592
15032
|
});
|
|
14593
15033
|
server.on("error", (err) => {
|
|
14594
|
-
if (err.code === "EADDRINUSE" && attempt < MAX_VIEWER_PORT_RETRIES) {
|
|
15034
|
+
if (err.code === "EADDRINUSE" && inboundSecret === null && attempt < MAX_VIEWER_PORT_RETRIES) {
|
|
14595
15035
|
attempt++;
|
|
14596
15036
|
currentPort = requestedPort + attempt;
|
|
14597
15037
|
setImmediate(tryListen);
|
|
@@ -14600,7 +15040,8 @@ function startViewerServer(port, _kv, _sdk, secret, restPort) {
|
|
|
14600
15040
|
if (err.code === "EADDRINUSE") {
|
|
14601
15041
|
boundViewerPort = null;
|
|
14602
15042
|
viewerSkipped = true;
|
|
14603
|
-
console.warn(`[agentmemory] Viewer
|
|
15043
|
+
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.`);
|
|
15044
|
+
else console.warn(`[agentmemory] Viewer ports ${requestedPort}-${requestedPort + MAX_VIEWER_PORT_RETRIES} all in use, skipping viewer.`);
|
|
14604
15045
|
} else {
|
|
14605
15046
|
boundViewerPort = null;
|
|
14606
15047
|
viewerSkipped = true;
|
|
@@ -14645,7 +15086,6 @@ async function proxyToRestApi(restPort, pathname, qs, method, req, res, secret)
|
|
|
14645
15086
|
res.writeHead(upstream.status, responseHeaders);
|
|
14646
15087
|
res.end(responseBody);
|
|
14647
15088
|
}
|
|
14648
|
-
|
|
14649
15089
|
//#endregion
|
|
14650
15090
|
//#region src/triggers/api.ts
|
|
14651
15091
|
function parseOptionalInt(raw) {
|
|
@@ -15068,7 +15508,7 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
15068
15508
|
}
|
|
15069
15509
|
if (body.maxFiles !== void 0) {
|
|
15070
15510
|
const n = body.maxFiles;
|
|
15071
|
-
if (!Number.isInteger(n) || n < 1 || n >
|
|
15511
|
+
if (!Number.isInteger(n) || n < 1 || n > 1e3) return {
|
|
15072
15512
|
status_code: 400,
|
|
15073
15513
|
body: { error: `maxFiles must be an integer between 1 and ${MAX_FILES_UPPER_BOUND}` }
|
|
15074
15514
|
};
|
|
@@ -15152,9 +15592,13 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
15152
15592
|
value: "completed"
|
|
15153
15593
|
}]);
|
|
15154
15594
|
try {
|
|
15155
|
-
sdk.
|
|
15595
|
+
sdk.trigger({
|
|
15596
|
+
function_id: "event::session::stopped",
|
|
15597
|
+
payload: { sessionId },
|
|
15598
|
+
action: TriggerAction.Void()
|
|
15599
|
+
});
|
|
15156
15600
|
} catch (err) {
|
|
15157
|
-
logger.warn("event::session::stopped
|
|
15601
|
+
logger.warn("event::session::stopped trigger failed", {
|
|
15158
15602
|
sessionId,
|
|
15159
15603
|
error: err instanceof Error ? err.message : String(err)
|
|
15160
15604
|
});
|
|
@@ -15574,11 +16018,24 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
15574
16018
|
status_code: 400,
|
|
15575
16019
|
body: { error: "query or expandIds is required" }
|
|
15576
16020
|
};
|
|
16021
|
+
const headers = req.headers || {};
|
|
16022
|
+
const sourceHeader = headers["x-agentmemory-source"] ?? headers["X-Agentmemory-Source"];
|
|
16023
|
+
const sourceFromHeader = Array.isArray(sourceHeader) ? sourceHeader[0] : sourceHeader;
|
|
16024
|
+
const payload = {
|
|
16025
|
+
query: req.body?.query,
|
|
16026
|
+
expandIds: req.body?.expandIds,
|
|
16027
|
+
limit: req.body?.limit,
|
|
16028
|
+
project: req.body?.project,
|
|
16029
|
+
includeLessons: req.body?.includeLessons,
|
|
16030
|
+
agentId: req.body?.agentId,
|
|
16031
|
+
sessionId: req.body?.sessionId,
|
|
16032
|
+
source: req.body?.source ?? sourceFromHeader
|
|
16033
|
+
};
|
|
15577
16034
|
return {
|
|
15578
16035
|
status_code: 200,
|
|
15579
16036
|
body: await sdk.trigger({
|
|
15580
16037
|
function_id: "mem::smart-search",
|
|
15581
|
-
payload
|
|
16038
|
+
payload
|
|
15582
16039
|
})
|
|
15583
16040
|
};
|
|
15584
16041
|
});
|
|
@@ -15590,6 +16047,29 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
15590
16047
|
http_method: "POST"
|
|
15591
16048
|
}
|
|
15592
16049
|
});
|
|
16050
|
+
sdk.registerFunction("api::diagnostic-followup", async (req) => {
|
|
16051
|
+
const authErr = checkAuth(req, secret);
|
|
16052
|
+
if (authErr) return authErr;
|
|
16053
|
+
return {
|
|
16054
|
+
status_code: 200,
|
|
16055
|
+
body: {
|
|
16056
|
+
...await sdk.trigger({
|
|
16057
|
+
function_id: "mem::diagnostic::followup-stats",
|
|
16058
|
+
payload: {}
|
|
16059
|
+
}),
|
|
16060
|
+
caveat: "Directional signal: overcounts on legitimate query refinement. Tune via AGENTMEMORY_FOLLOWUP_WINDOW_SECONDS."
|
|
16061
|
+
}
|
|
16062
|
+
};
|
|
16063
|
+
});
|
|
16064
|
+
sdk.registerTrigger({
|
|
16065
|
+
type: "http",
|
|
16066
|
+
function_id: "api::diagnostic-followup",
|
|
16067
|
+
config: {
|
|
16068
|
+
api_path: "/agentmemory/diagnostics/followup",
|
|
16069
|
+
http_method: "GET",
|
|
16070
|
+
middleware_function_ids: ["middleware::api-auth"]
|
|
16071
|
+
}
|
|
16072
|
+
});
|
|
15593
16073
|
sdk.registerFunction("api::timeline", async (req) => {
|
|
15594
16074
|
const authErr = checkAuth(req, secret);
|
|
15595
16075
|
if (authErr) return authErr;
|
|
@@ -15811,12 +16291,20 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
15811
16291
|
sdk.registerFunction("api::graph-query", async (req) => {
|
|
15812
16292
|
const authErr = checkAuth(req, secret);
|
|
15813
16293
|
if (authErr) return authErr;
|
|
16294
|
+
const payload = {
|
|
16295
|
+
startNodeId: req.body?.startNodeId,
|
|
16296
|
+
nodeType: req.body?.nodeType,
|
|
16297
|
+
maxDepth: req.body?.maxDepth,
|
|
16298
|
+
query: req.body?.query,
|
|
16299
|
+
limit: req.body?.limit,
|
|
16300
|
+
offset: req.body?.offset
|
|
16301
|
+
};
|
|
15814
16302
|
try {
|
|
15815
16303
|
return {
|
|
15816
16304
|
status_code: 200,
|
|
15817
16305
|
body: await sdk.trigger({
|
|
15818
16306
|
function_id: "mem::graph-query",
|
|
15819
|
-
payload
|
|
16307
|
+
payload
|
|
15820
16308
|
})
|
|
15821
16309
|
};
|
|
15822
16310
|
} catch {
|
|
@@ -18097,7 +18585,6 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
18097
18585
|
}
|
|
18098
18586
|
});
|
|
18099
18587
|
}
|
|
18100
|
-
|
|
18101
18588
|
//#endregion
|
|
18102
18589
|
//#region src/triggers/events.ts
|
|
18103
18590
|
function registerEventTriggers(sdk, kv) {
|
|
@@ -18142,18 +18629,26 @@ function registerEventTriggers(sdk, kv) {
|
|
|
18142
18629
|
payload: data
|
|
18143
18630
|
});
|
|
18144
18631
|
if (isReflectEnabled()) try {
|
|
18145
|
-
sdk.
|
|
18632
|
+
sdk.trigger({
|
|
18633
|
+
function_id: "mem::slot-reflect",
|
|
18634
|
+
payload: { sessionId: data.sessionId },
|
|
18635
|
+
action: TriggerAction.Void()
|
|
18636
|
+
});
|
|
18146
18637
|
} catch (err) {
|
|
18147
|
-
logger.warn("slot-reflect
|
|
18638
|
+
logger.warn("slot-reflect trigger failed", {
|
|
18148
18639
|
sessionId: data.sessionId,
|
|
18149
18640
|
error: err instanceof Error ? err.message : String(err)
|
|
18150
18641
|
});
|
|
18151
18642
|
}
|
|
18152
18643
|
if (isGraphExtractionEnabled()) try {
|
|
18153
18644
|
const compressed = (await kv.list(KV.observations(data.sessionId))).filter((o) => o.title);
|
|
18154
|
-
if (compressed.length > 0) sdk.
|
|
18645
|
+
if (compressed.length > 0) sdk.trigger({
|
|
18646
|
+
function_id: "mem::graph-extract",
|
|
18647
|
+
payload: { observations: compressed },
|
|
18648
|
+
action: TriggerAction.Void()
|
|
18649
|
+
});
|
|
18155
18650
|
} catch (err) {
|
|
18156
|
-
logger.warn("graph-extract
|
|
18651
|
+
logger.warn("graph-extract trigger failed", {
|
|
18157
18652
|
sessionId: data.sessionId,
|
|
18158
18653
|
error: err instanceof Error ? err.message : String(err)
|
|
18159
18654
|
});
|
|
@@ -18211,7 +18706,6 @@ function registerEventTriggers(sdk, kv) {
|
|
|
18211
18706
|
config: { scope: KV.sessions }
|
|
18212
18707
|
});
|
|
18213
18708
|
}
|
|
18214
|
-
|
|
18215
18709
|
//#endregion
|
|
18216
18710
|
//#region src/mcp/tools-registry.ts
|
|
18217
18711
|
const CORE_TOOLS = [
|
|
@@ -19321,7 +19815,6 @@ function getVisibleTools() {
|
|
|
19321
19815
|
if ((process.env["AGENTMEMORY_TOOLS"] || "all") === "core") return getAllTools().filter((t) => ESSENTIAL_TOOLS.has(t.name));
|
|
19322
19816
|
return getAllTools();
|
|
19323
19817
|
}
|
|
19324
|
-
|
|
19325
19818
|
//#endregion
|
|
19326
19819
|
//#region src/mcp/server.ts
|
|
19327
19820
|
function asNonEmptyString(value) {
|
|
@@ -20958,7 +21451,6 @@ function registerMcpEndpoints(sdk, kv, secret) {
|
|
|
20958
21451
|
}
|
|
20959
21452
|
});
|
|
20960
21453
|
}
|
|
20961
|
-
|
|
20962
21454
|
//#endregion
|
|
20963
21455
|
//#region src/eval/metrics-store.ts
|
|
20964
21456
|
var MetricsStore = class {
|
|
@@ -21001,7 +21493,6 @@ var MetricsStore = class {
|
|
|
21001
21493
|
return Array.from(merged.values());
|
|
21002
21494
|
}
|
|
21003
21495
|
};
|
|
21004
|
-
|
|
21005
21496
|
//#endregion
|
|
21006
21497
|
//#region src/functions/dedup.ts
|
|
21007
21498
|
const TTL_MS = 300 * 1e3;
|
|
@@ -21043,57 +21534,6 @@ var DedupMap = class {
|
|
|
21043
21534
|
return this.entries.size;
|
|
21044
21535
|
}
|
|
21045
21536
|
};
|
|
21046
|
-
|
|
21047
|
-
//#endregion
|
|
21048
|
-
//#region src/telemetry/setup.ts
|
|
21049
|
-
const OTEL_CONFIG = {
|
|
21050
|
-
serviceName: "agentmemory",
|
|
21051
|
-
serviceVersion: VERSION,
|
|
21052
|
-
metricsExportIntervalMs: 3e4
|
|
21053
|
-
};
|
|
21054
|
-
let counters = null;
|
|
21055
|
-
let histograms = null;
|
|
21056
|
-
const NOOP_COUNTER = { add: () => {} };
|
|
21057
|
-
const NOOP_HISTOGRAM = { record: () => {} };
|
|
21058
|
-
const COUNTER_NAMES = [
|
|
21059
|
-
["observationsTotal", "observations.total"],
|
|
21060
|
-
["compressionSuccess", "compression.success"],
|
|
21061
|
-
["compressionFailure", "compression.failure"],
|
|
21062
|
-
["searchTotal", "search.total"],
|
|
21063
|
-
["dedupSkipped", "dedup.skipped"],
|
|
21064
|
-
["evictionTotal", "eviction.total"],
|
|
21065
|
-
["circuitBreakerOpen", "circuit_breaker.open"],
|
|
21066
|
-
["embeddingSuccess", "embedding.success"],
|
|
21067
|
-
["embeddingFailure", "embedding.failure"],
|
|
21068
|
-
["vectorSearchTotal", "vector_search.total"],
|
|
21069
|
-
["autoForgetTotal", "auto_forget.total"],
|
|
21070
|
-
["profileGenerated", "profile.generated"],
|
|
21071
|
-
["claudeBridgeSync", "claude_bridge.sync"],
|
|
21072
|
-
["graphExtraction", "graph.extraction"],
|
|
21073
|
-
["consolidationRun", "consolidation.run"],
|
|
21074
|
-
["teamShare", "team.share"],
|
|
21075
|
-
["auditLog", "audit.log"],
|
|
21076
|
-
["snapshotCreate", "snapshot.create"],
|
|
21077
|
-
["governanceDelete", "governance.delete"]
|
|
21078
|
-
];
|
|
21079
|
-
const HISTOGRAM_NAMES = [
|
|
21080
|
-
["compressionLatency", "compression.latency_ms"],
|
|
21081
|
-
["searchLatency", "search.latency_ms"],
|
|
21082
|
-
["contextTokens", "context.tokens"],
|
|
21083
|
-
["qualityScore", "quality.score"],
|
|
21084
|
-
["embeddingLatency", "embedding.latency_ms"],
|
|
21085
|
-
["vectorSearchLatency", "vector_search.latency_ms"]
|
|
21086
|
-
];
|
|
21087
|
-
function initMetrics(getMeter) {
|
|
21088
|
-
const meter = getMeter?.("agentmemory");
|
|
21089
|
-
counters = Object.fromEntries(COUNTER_NAMES.map(([key, name]) => [key, meter ? meter.createCounter(name) : NOOP_COUNTER]));
|
|
21090
|
-
histograms = Object.fromEntries(HISTOGRAM_NAMES.map(([key, name]) => [key, meter ? meter.createHistogram(name) : NOOP_HISTOGRAM]));
|
|
21091
|
-
return {
|
|
21092
|
-
counters,
|
|
21093
|
-
histograms
|
|
21094
|
-
};
|
|
21095
|
-
}
|
|
21096
|
-
|
|
21097
21537
|
//#endregion
|
|
21098
21538
|
//#region src/index.ts
|
|
21099
21539
|
function workerPidfilePath() {
|
|
@@ -21242,6 +21682,7 @@ async function main() {
|
|
|
21242
21682
|
const graphWeight = parseFloat(getEnvVar("AGENTMEMORY_GRAPH_WEIGHT") || "0.3");
|
|
21243
21683
|
const hybridSearch = new HybridSearch(bm25Index, vectorIndex, embeddingProvider, kv, embeddingConfig.bm25Weight, embeddingConfig.vectorWeight, graphWeight);
|
|
21244
21684
|
registerSmartSearchFunction(sdk, kv, (query, limit) => hybridSearch.search(query, limit));
|
|
21685
|
+
registerRecentSearchesSweepFunction(sdk, kv);
|
|
21245
21686
|
registerApiTriggers(sdk, kv, secret, metricsStore, provider);
|
|
21246
21687
|
registerEventTriggers(sdk, kv);
|
|
21247
21688
|
registerMcpEndpoints(sdk, kv, secret);
|
|
@@ -21309,7 +21750,7 @@ async function main() {
|
|
|
21309
21750
|
console.warn(`[agentmemory] Failed to backfill memories into BM25:`, err);
|
|
21310
21751
|
}
|
|
21311
21752
|
bootLog(`Ready. ${embeddingProvider ? "Triple-stream (BM25+Vector+Graph)" : "BM25+Graph"} search active.`);
|
|
21312
|
-
bootLog(`REST API:
|
|
21753
|
+
bootLog(`REST API: 126 endpoints at http://localhost:${config.restPort}/agentmemory/*`);
|
|
21313
21754
|
bootLog(`MCP surface (opt-in via \`npx @agentmemory/mcp\`): ${getAllTools().length} tools · 6 resources · 3 prompts`);
|
|
21314
21755
|
const viewerServer = startViewerServer(config.restPort + 2, kv, sdk, secret, config.restPort);
|
|
21315
21756
|
const autoForgetIntervalMs = parseInt(process.env.AUTO_FORGET_INTERVAL_MS || "3600000", 10);
|
|
@@ -21344,6 +21785,14 @@ async function main() {
|
|
|
21344
21785
|
});
|
|
21345
21786
|
} catch {}
|
|
21346
21787
|
}, 864e5).unref();
|
|
21788
|
+
setInterval(async () => {
|
|
21789
|
+
try {
|
|
21790
|
+
await sdk.trigger({
|
|
21791
|
+
function_id: "mem::diagnostic::recent-searches-sweep",
|
|
21792
|
+
payload: {}
|
|
21793
|
+
});
|
|
21794
|
+
} catch {}
|
|
21795
|
+
}, 3600 * 1e3).unref();
|
|
21347
21796
|
if (isConsolidationEnabled()) {
|
|
21348
21797
|
setInterval(async () => {
|
|
21349
21798
|
try {
|
|
@@ -21375,7 +21824,7 @@ main().catch((err) => {
|
|
|
21375
21824
|
console.error(`[agentmemory] Fatal:`, err);
|
|
21376
21825
|
process.exit(1);
|
|
21377
21826
|
});
|
|
21378
|
-
|
|
21379
21827
|
//#endregion
|
|
21380
|
-
export {
|
|
21828
|
+
export {};
|
|
21829
|
+
|
|
21381
21830
|
//# sourceMappingURL=index.mjs.map
|