@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
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { a as jaccardSimilarity, i as generateId, n as STREAM, r as fingerprintId, t as KV } from "./schema-
|
|
2
|
-
import { n as logger, t as bootLog } from "./logger-
|
|
3
|
-
import { t as VERSION } from "./version-
|
|
4
|
-
import { a as isManagedImagePath,
|
|
5
|
-
import {
|
|
1
|
+
import { a as jaccardSimilarity, i as generateId, n as STREAM, r as fingerprintId, t as KV } from "./schema-Dsr_V2Wp.mjs";
|
|
2
|
+
import { n as logger, t as bootLog } from "./logger-yHTcEBAI.mjs";
|
|
3
|
+
import { t as VERSION } from "./version-BMFYFFut.mjs";
|
|
4
|
+
import { a as isManagedImagePath, n as deleteImage, r as getMaxBytes, t as IMAGES_DIR } from "./image-store-Gpo2mgM9.mjs";
|
|
5
|
+
import { r as withKeyedLock, t as getImageRefCount } from "./image-refs-C7h9L5wx.mjs";
|
|
6
|
+
import { _ as loadConfig, a as getAgentId, b as loadSnapshotConfig, c as getFollowupWindowSeconds, d as isAutoCompressEnabled, f as isConsolidationEnabled, g as loadClaudeBridgeConfig, h as isGraphExtractionEnabled, i as detectLlmProviderKind, m as isDropStaleIndexEnabled, n as getVisibleTools, o as getConsolidationDecayDays, p as isContextInjectionEnabled, r as detectEmbeddingProvider, s as getEnvVar, t as getAllTools, u as isAgentScopeIsolated, v as loadEmbeddingConfig, x as loadTeamConfig, y as loadFallbackConfig } from "./tools-registry-Dzxv9iUu.mjs";
|
|
6
7
|
import { createRequire } from "node:module";
|
|
7
8
|
import { execFile } from "node:child_process";
|
|
8
9
|
import { constants, existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
@@ -12,16 +13,24 @@ import { homedir } from "node:os";
|
|
|
12
13
|
import { createHash, createHmac, randomBytes, timingSafeEqual } from "node:crypto";
|
|
13
14
|
import { lstat, mkdir, open, readFile, readdir, stat, writeFile } from "node:fs/promises";
|
|
14
15
|
import { TriggerAction, registerWorker } from "iii-sdk";
|
|
16
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
15
17
|
import Anthropic from "@anthropic-ai/sdk";
|
|
16
18
|
import { z } from "zod";
|
|
17
19
|
import { promisify } from "node:util";
|
|
18
20
|
import { lookup } from "node:dns/promises";
|
|
19
21
|
import { isIP } from "node:net";
|
|
20
22
|
import { createServer } from "node:http";
|
|
21
|
-
|
|
22
23
|
//#region src/providers/agent-sdk.ts
|
|
24
|
+
const sdkChildContext = new AsyncLocalStorage();
|
|
25
|
+
let sdkActiveCount = 0;
|
|
26
|
+
let sdkOriginalEnv;
|
|
23
27
|
var AgentSDKProvider = class {
|
|
24
28
|
name = "agent-sdk";
|
|
29
|
+
sdkPromise = null;
|
|
30
|
+
loadSdk() {
|
|
31
|
+
if (!this.sdkPromise) this.sdkPromise = import("@anthropic-ai/claude-agent-sdk");
|
|
32
|
+
return this.sdkPromise;
|
|
33
|
+
}
|
|
25
34
|
async compress(systemPrompt, userPrompt) {
|
|
26
35
|
return this.query(systemPrompt, userPrompt);
|
|
27
36
|
}
|
|
@@ -29,29 +38,37 @@ var AgentSDKProvider = class {
|
|
|
29
38
|
return this.query(systemPrompt, userPrompt);
|
|
30
39
|
}
|
|
31
40
|
async query(systemPrompt, userPrompt) {
|
|
32
|
-
if (
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
if (sdkChildContext.getStore()) return "";
|
|
42
|
+
return sdkChildContext.run(true, async () => {
|
|
43
|
+
if (sdkActiveCount === 0) {
|
|
44
|
+
sdkOriginalEnv = process.env.AGENTMEMORY_SDK_CHILD;
|
|
45
|
+
process.env.AGENTMEMORY_SDK_CHILD = "1";
|
|
46
|
+
}
|
|
47
|
+
sdkActiveCount++;
|
|
48
|
+
try {
|
|
49
|
+
const { query } = await this.loadSdk();
|
|
50
|
+
const messages = query({
|
|
51
|
+
prompt: userPrompt,
|
|
52
|
+
options: {
|
|
53
|
+
systemPrompt,
|
|
54
|
+
maxTurns: 1,
|
|
55
|
+
allowedTools: []
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
let result = "";
|
|
59
|
+
for await (const msg of messages) if (msg.type === "result") result = msg.result ?? "";
|
|
60
|
+
return result;
|
|
61
|
+
} finally {
|
|
62
|
+
sdkActiveCount--;
|
|
63
|
+
if (sdkActiveCount === 0) {
|
|
64
|
+
if (sdkOriginalEnv === void 0) delete process.env.AGENTMEMORY_SDK_CHILD;
|
|
65
|
+
else process.env.AGENTMEMORY_SDK_CHILD = sdkOriginalEnv;
|
|
66
|
+
sdkOriginalEnv = void 0;
|
|
43
67
|
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
for await (const msg of messages) if (msg.type === "result") result = msg.result ?? "";
|
|
47
|
-
return result;
|
|
48
|
-
} finally {
|
|
49
|
-
if (prev === void 0) delete process.env.AGENTMEMORY_SDK_CHILD;
|
|
50
|
-
else process.env.AGENTMEMORY_SDK_CHILD = prev;
|
|
51
|
-
}
|
|
68
|
+
}
|
|
69
|
+
});
|
|
52
70
|
}
|
|
53
71
|
};
|
|
54
|
-
|
|
55
72
|
//#endregion
|
|
56
73
|
//#region src/providers/anthropic.ts
|
|
57
74
|
var AnthropicProvider = class {
|
|
@@ -105,7 +122,6 @@ var AnthropicProvider = class {
|
|
|
105
122
|
})).content.find((b) => b.type === "text")?.text ?? "";
|
|
106
123
|
}
|
|
107
124
|
};
|
|
108
|
-
|
|
109
125
|
//#endregion
|
|
110
126
|
//#region src/providers/_fetch.ts
|
|
111
127
|
function fetchWithTimeout(url, init, timeoutMs) {
|
|
@@ -119,7 +135,6 @@ function fetchWithTimeout(url, init, timeoutMs) {
|
|
|
119
135
|
signal
|
|
120
136
|
}).finally(() => clearTimeout(t));
|
|
121
137
|
}
|
|
122
|
-
|
|
123
138
|
//#endregion
|
|
124
139
|
//#region src/providers/minimax.ts
|
|
125
140
|
/**
|
|
@@ -179,7 +194,6 @@ var MinimaxProvider = class {
|
|
|
179
194
|
return ((await response.json()).content?.find((b) => b.type === "text"))?.text ?? "";
|
|
180
195
|
}
|
|
181
196
|
};
|
|
182
|
-
|
|
183
197
|
//#endregion
|
|
184
198
|
//#region src/providers/noop.ts
|
|
185
199
|
/**
|
|
@@ -198,11 +212,6 @@ var NoopProvider = class {
|
|
|
198
212
|
return "";
|
|
199
213
|
}
|
|
200
214
|
};
|
|
201
|
-
|
|
202
|
-
//#endregion
|
|
203
|
-
//#region src/providers/_openai-shared.ts
|
|
204
|
-
const DEFAULT_OPENAI_BASE_URL = "https://api.openai.com";
|
|
205
|
-
const DEFAULT_AZURE_API_VERSION = "2024-08-01-preview";
|
|
206
215
|
function detectAzure(baseUrl) {
|
|
207
216
|
try {
|
|
208
217
|
return new URL(baseUrl).hostname.endsWith(".openai.azure.com");
|
|
@@ -262,9 +271,8 @@ function buildAuthHeaders(apiKey, isAzure) {
|
|
|
262
271
|
};
|
|
263
272
|
}
|
|
264
273
|
function normalizeBaseUrl(raw) {
|
|
265
|
-
return (raw ||
|
|
274
|
+
return (raw || "https://api.openai.com").replace(/\/+$/, "");
|
|
266
275
|
}
|
|
267
|
-
|
|
268
276
|
//#endregion
|
|
269
277
|
//#region src/providers/openai.ts
|
|
270
278
|
const DEFAULT_TIMEOUT_MS = 6e4;
|
|
@@ -317,7 +325,7 @@ var OpenAIProvider = class {
|
|
|
317
325
|
this.baseUrl = normalizeBaseUrl(baseURL || getEnvVar("OPENAI_BASE_URL"));
|
|
318
326
|
this.reasoningEffort = getEnvVar("OPENAI_REASONING_EFFORT") || void 0;
|
|
319
327
|
this.timeoutMs = resolveTimeout();
|
|
320
|
-
this.azureApiVersion = getEnvVar("OPENAI_API_VERSION") ||
|
|
328
|
+
this.azureApiVersion = getEnvVar("OPENAI_API_VERSION") || "2024-08-01-preview";
|
|
321
329
|
this.isAzure = detectAzure(this.baseUrl);
|
|
322
330
|
}
|
|
323
331
|
async compress(systemPrompt, userPrompt) {
|
|
@@ -379,7 +387,6 @@ function parsePositiveInt(raw) {
|
|
|
379
387
|
const n = Number(trimmed);
|
|
380
388
|
return Number.isFinite(n) && n > 0 ? n : void 0;
|
|
381
389
|
}
|
|
382
|
-
|
|
383
390
|
//#endregion
|
|
384
391
|
//#region src/providers/openrouter.ts
|
|
385
392
|
var OpenRouterProvider = class {
|
|
@@ -431,7 +438,6 @@ var OpenRouterProvider = class {
|
|
|
431
438
|
return content;
|
|
432
439
|
}
|
|
433
440
|
};
|
|
434
|
-
|
|
435
441
|
//#endregion
|
|
436
442
|
//#region src/providers/circuit-breaker.ts
|
|
437
443
|
function positiveFinite(val, fallback) {
|
|
@@ -493,7 +499,6 @@ var CircuitBreaker = class {
|
|
|
493
499
|
};
|
|
494
500
|
}
|
|
495
501
|
};
|
|
496
|
-
|
|
497
502
|
//#endregion
|
|
498
503
|
//#region src/providers/resilient.ts
|
|
499
504
|
var ResilientProvider = class {
|
|
@@ -524,7 +529,6 @@ var ResilientProvider = class {
|
|
|
524
529
|
return this.breaker.getState();
|
|
525
530
|
}
|
|
526
531
|
};
|
|
527
|
-
|
|
528
532
|
//#endregion
|
|
529
533
|
//#region src/providers/fallback-chain.ts
|
|
530
534
|
var FallbackChainProvider = class {
|
|
@@ -549,7 +553,6 @@ var FallbackChainProvider = class {
|
|
|
549
553
|
throw lastError || /* @__PURE__ */ new Error("No providers available");
|
|
550
554
|
}
|
|
551
555
|
};
|
|
552
|
-
|
|
553
556
|
//#endregion
|
|
554
557
|
//#region src/providers/embedding/gemini.ts
|
|
555
558
|
const BATCH_LIMIT = 100;
|
|
@@ -605,7 +608,6 @@ function l2Normalize(vec) {
|
|
|
605
608
|
for (let i = 0; i < vec.length; i++) vec[i] = vec[i] / norm;
|
|
606
609
|
return vec;
|
|
607
610
|
}
|
|
608
|
-
|
|
609
611
|
//#endregion
|
|
610
612
|
//#region src/providers/embedding/openai.ts
|
|
611
613
|
const DEFAULT_MODEL$1 = "text-embedding-3-small";
|
|
@@ -679,7 +681,7 @@ var OpenAIEmbeddingProvider = class {
|
|
|
679
681
|
this.model = getEnvVar("OPENAI_EMBEDDING_MODEL") || DEFAULT_MODEL$1;
|
|
680
682
|
this.dimensions = resolveDimensions(this.model, getEnvVar("OPENAI_EMBEDDING_DIMENSIONS"));
|
|
681
683
|
this.isAzure = detectAzure(this.baseUrl);
|
|
682
|
-
this.azureApiVersion = getEnvVar("OPENAI_API_VERSION") ||
|
|
684
|
+
this.azureApiVersion = getEnvVar("OPENAI_API_VERSION") || "2024-08-01-preview";
|
|
683
685
|
}
|
|
684
686
|
async embed(text) {
|
|
685
687
|
const [result] = await this.embedBatch([text]);
|
|
@@ -701,7 +703,6 @@ var OpenAIEmbeddingProvider = class {
|
|
|
701
703
|
return (await response.json()).data.map((d) => new Float32Array(d.embedding));
|
|
702
704
|
}
|
|
703
705
|
};
|
|
704
|
-
|
|
705
706
|
//#endregion
|
|
706
707
|
//#region src/providers/embedding/voyage.ts
|
|
707
708
|
const API_URL$2 = "https://api.voyageai.com/v1/embeddings";
|
|
@@ -737,7 +738,6 @@ var VoyageEmbeddingProvider = class {
|
|
|
737
738
|
return (await response.json()).data.map((d) => new Float32Array(d.embedding));
|
|
738
739
|
}
|
|
739
740
|
};
|
|
740
|
-
|
|
741
741
|
//#endregion
|
|
742
742
|
//#region src/providers/embedding/cohere.ts
|
|
743
743
|
const API_URL$1 = "https://api.cohere.ai/v1/embed";
|
|
@@ -773,7 +773,6 @@ var CohereEmbeddingProvider = class {
|
|
|
773
773
|
return (await response.json()).embeddings.map((e) => new Float32Array(e));
|
|
774
774
|
}
|
|
775
775
|
};
|
|
776
|
-
|
|
777
776
|
//#endregion
|
|
778
777
|
//#region src/providers/embedding/openrouter.ts
|
|
779
778
|
const API_URL = "https://openrouter.ai/api/v1/embeddings";
|
|
@@ -810,7 +809,6 @@ var OpenRouterEmbeddingProvider = class {
|
|
|
810
809
|
return (await response.json()).data.map((d) => new Float32Array(d.embedding));
|
|
811
810
|
}
|
|
812
811
|
};
|
|
813
|
-
|
|
814
812
|
//#endregion
|
|
815
813
|
//#region src/providers/embedding/local.ts
|
|
816
814
|
var LocalEmbeddingProvider = class {
|
|
@@ -839,7 +837,6 @@ var LocalEmbeddingProvider = class {
|
|
|
839
837
|
return this.extractor;
|
|
840
838
|
}
|
|
841
839
|
};
|
|
842
|
-
|
|
843
840
|
//#endregion
|
|
844
841
|
//#region src/providers/embedding/clip.ts
|
|
845
842
|
const DEFAULT_MODEL = "Xenova/clip-vit-base-patch32";
|
|
@@ -880,12 +877,14 @@ var ClipEmbeddingProvider = class {
|
|
|
880
877
|
}
|
|
881
878
|
async getTextExtractor() {
|
|
882
879
|
if (this.textExtractor) return this.textExtractor;
|
|
883
|
-
|
|
880
|
+
const t = await this.getTransformers();
|
|
881
|
+
this.textExtractor = await t.pipeline("feature-extraction", this.modelId);
|
|
884
882
|
return this.textExtractor;
|
|
885
883
|
}
|
|
886
884
|
async getImageExtractor() {
|
|
887
885
|
if (this.imageExtractor) return this.imageExtractor;
|
|
888
|
-
|
|
886
|
+
const t = await this.getTransformers();
|
|
887
|
+
this.imageExtractor = await t.pipeline("image-feature-extraction", this.modelId);
|
|
889
888
|
return this.imageExtractor;
|
|
890
889
|
}
|
|
891
890
|
};
|
|
@@ -910,7 +909,6 @@ function normalize(vec) {
|
|
|
910
909
|
for (let i = 0; i < vec.length; i++) out[i] = vec[i] / norm;
|
|
911
910
|
return out;
|
|
912
911
|
}
|
|
913
|
-
|
|
914
912
|
//#endregion
|
|
915
913
|
//#region src/providers/embedding/index.ts
|
|
916
914
|
let imageEmbeddingProvider = null;
|
|
@@ -949,7 +947,6 @@ function withDimensionGuard(provider) {
|
|
|
949
947
|
if (provider.embedImage) wrapped.embedImage = async (s) => check(await provider.embedImage(s), "embedImage");
|
|
950
948
|
return wrapped;
|
|
951
949
|
}
|
|
952
|
-
|
|
953
950
|
//#endregion
|
|
954
951
|
//#region src/providers/index.ts
|
|
955
952
|
function requireEnvVar(key) {
|
|
@@ -957,6 +954,17 @@ function requireEnvVar(key) {
|
|
|
957
954
|
if (!value) throw new Error(`Missing required environment variable: ${key}. Set it in ~/.agentmemory/.env or as an environment variable.`);
|
|
958
955
|
return value;
|
|
959
956
|
}
|
|
957
|
+
function defaultModelFor(providerType) {
|
|
958
|
+
switch (providerType) {
|
|
959
|
+
case "openai": return getEnvVar("OPENAI_MODEL") || "gpt-4o-mini";
|
|
960
|
+
case "anthropic": return getEnvVar("ANTHROPIC_MODEL") || "claude-sonnet-4-20250514";
|
|
961
|
+
case "gemini": return getEnvVar("GEMINI_MODEL") || "gemini-2.5-flash";
|
|
962
|
+
case "openrouter": return getEnvVar("OPENROUTER_MODEL") || "anthropic/claude-sonnet-4-20250514";
|
|
963
|
+
case "minimax": return getEnvVar("MINIMAX_MODEL") || "MiniMax-M2.7";
|
|
964
|
+
case "agent-sdk": return "claude-sonnet-4-20250514";
|
|
965
|
+
default: return "noop";
|
|
966
|
+
}
|
|
967
|
+
}
|
|
960
968
|
function createProvider(config) {
|
|
961
969
|
return new ResilientProvider(createBaseProvider(config));
|
|
962
970
|
}
|
|
@@ -968,7 +976,7 @@ function createFallbackProvider(config, fallbackConfig) {
|
|
|
968
976
|
try {
|
|
969
977
|
const fbConfig = {
|
|
970
978
|
provider: providerType,
|
|
971
|
-
model:
|
|
979
|
+
model: defaultModelFor(providerType),
|
|
972
980
|
maxTokens: config.maxTokens
|
|
973
981
|
};
|
|
974
982
|
providers.push(createBaseProvider(fbConfig));
|
|
@@ -996,7 +1004,6 @@ function createBaseProvider(config) {
|
|
|
996
1004
|
default: return new AgentSDKProvider();
|
|
997
1005
|
}
|
|
998
1006
|
}
|
|
999
|
-
|
|
1000
1007
|
//#endregion
|
|
1001
1008
|
//#region src/state/kv.ts
|
|
1002
1009
|
var StateKV = class {
|
|
@@ -1048,7 +1055,6 @@ var StateKV = class {
|
|
|
1048
1055
|
});
|
|
1049
1056
|
}
|
|
1050
1057
|
};
|
|
1051
|
-
|
|
1052
1058
|
//#endregion
|
|
1053
1059
|
//#region src/state/vector-index.ts
|
|
1054
1060
|
function float32ToBase64(arr) {
|
|
@@ -1171,7 +1177,6 @@ var VectorIndex = class VectorIndex {
|
|
|
1171
1177
|
return idx;
|
|
1172
1178
|
}
|
|
1173
1179
|
};
|
|
1174
|
-
|
|
1175
1180
|
//#endregion
|
|
1176
1181
|
//#region src/state/memory-utils.ts
|
|
1177
1182
|
function memoryToObservation(memory) {
|
|
@@ -1188,7 +1193,6 @@ function memoryToObservation(memory) {
|
|
|
1188
1193
|
importance: memory.strength
|
|
1189
1194
|
};
|
|
1190
1195
|
}
|
|
1191
|
-
|
|
1192
1196
|
//#endregion
|
|
1193
1197
|
//#region src/functions/graph-retrieval.ts
|
|
1194
1198
|
function buildGraphContext(path) {
|
|
@@ -1433,7 +1437,6 @@ var MinHeap = class {
|
|
|
1433
1437
|
}
|
|
1434
1438
|
}
|
|
1435
1439
|
};
|
|
1436
|
-
|
|
1437
1440
|
//#endregion
|
|
1438
1441
|
//#region src/functions/query-expansion.ts
|
|
1439
1442
|
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.
|
|
@@ -1590,7 +1593,6 @@ function extractEntitiesFromQuery(query) {
|
|
|
1590
1593
|
}
|
|
1591
1594
|
return [...new Set(entities)];
|
|
1592
1595
|
}
|
|
1593
|
-
|
|
1594
1596
|
//#endregion
|
|
1595
1597
|
//#region src/state/reranker.ts
|
|
1596
1598
|
let pipeline = null;
|
|
@@ -1644,7 +1646,6 @@ async function rerank(query, results, topK = 20) {
|
|
|
1644
1646
|
rerankPosition: i + 1
|
|
1645
1647
|
}));
|
|
1646
1648
|
}
|
|
1647
|
-
|
|
1648
1649
|
//#endregion
|
|
1649
1650
|
//#region src/state/hybrid-search.ts
|
|
1650
1651
|
const RRF_K = 60;
|
|
@@ -1815,7 +1816,6 @@ var HybridSearch = class {
|
|
|
1815
1816
|
return enriched;
|
|
1816
1817
|
}
|
|
1817
1818
|
};
|
|
1818
|
-
|
|
1819
1819
|
//#endregion
|
|
1820
1820
|
//#region src/state/stemmer.ts
|
|
1821
1821
|
const step2map = {
|
|
@@ -1911,7 +1911,6 @@ function stem(word) {
|
|
|
1911
1911
|
if (endsDoubleConsonant(w) && w.endsWith("l") && measure(w.slice(0, -1)) > 1) w = w.slice(0, -1);
|
|
1912
1912
|
return w;
|
|
1913
1913
|
}
|
|
1914
|
-
|
|
1915
1914
|
//#endregion
|
|
1916
1915
|
//#region src/state/synonyms.ts
|
|
1917
1916
|
const SYNONYM_GROUPS = [
|
|
@@ -2073,7 +2072,6 @@ function getSynonyms(stemmedTerm) {
|
|
|
2073
2072
|
const syns = synonymMap.get(stemmedTerm);
|
|
2074
2073
|
return syns ? [...syns] : [];
|
|
2075
2074
|
}
|
|
2076
|
-
|
|
2077
2075
|
//#endregion
|
|
2078
2076
|
//#region src/state/cjk-segmenter.ts
|
|
2079
2077
|
const cjkRequire = createRequire(import.meta.url);
|
|
@@ -2177,7 +2175,6 @@ function segmentCjk(text) {
|
|
|
2177
2175
|
}
|
|
2178
2176
|
return out;
|
|
2179
2177
|
}
|
|
2180
|
-
|
|
2181
2178
|
//#endregion
|
|
2182
2179
|
//#region src/state/search-index.ts
|
|
2183
2180
|
var SearchIndex = class SearchIndex {
|
|
@@ -2377,18 +2374,92 @@ var SearchIndex = class SearchIndex {
|
|
|
2377
2374
|
return lo;
|
|
2378
2375
|
}
|
|
2379
2376
|
};
|
|
2380
|
-
|
|
2377
|
+
//#endregion
|
|
2378
|
+
//#region src/functions/audit.ts
|
|
2379
|
+
async function recordAudit(kv, operation, functionId, targetIds, details = {}, qualityScore, userId) {
|
|
2380
|
+
const entry = {
|
|
2381
|
+
id: generateId("aud"),
|
|
2382
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2383
|
+
operation,
|
|
2384
|
+
userId,
|
|
2385
|
+
functionId,
|
|
2386
|
+
targetIds,
|
|
2387
|
+
details,
|
|
2388
|
+
qualityScore
|
|
2389
|
+
};
|
|
2390
|
+
await kv.set(KV.audit, entry.id, entry);
|
|
2391
|
+
return entry;
|
|
2392
|
+
}
|
|
2393
|
+
async function safeAudit(kv, operation, functionId, targetIds, details = {}, qualityScore, userId) {
|
|
2394
|
+
try {
|
|
2395
|
+
await recordAudit(kv, operation, functionId, targetIds, details, qualityScore, userId);
|
|
2396
|
+
} catch (err) {
|
|
2397
|
+
try {
|
|
2398
|
+
logger.warn("audit write failed", {
|
|
2399
|
+
functionId,
|
|
2400
|
+
operation,
|
|
2401
|
+
targetIds,
|
|
2402
|
+
error: err instanceof Error ? err.message : String(err)
|
|
2403
|
+
});
|
|
2404
|
+
} catch {}
|
|
2405
|
+
}
|
|
2406
|
+
}
|
|
2407
|
+
async function queryAudit(kv, filter) {
|
|
2408
|
+
let entries = [...await kv.list(KV.audit)].sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
|
|
2409
|
+
if (filter?.operation) entries = entries.filter((e) => e.operation === filter.operation);
|
|
2410
|
+
if (filter?.dateFrom) {
|
|
2411
|
+
const from = new Date(filter.dateFrom).getTime();
|
|
2412
|
+
if (Number.isNaN(from)) throw new Error(`Invalid dateFrom: ${filter.dateFrom}`);
|
|
2413
|
+
entries = entries.filter((e) => new Date(e.timestamp).getTime() >= from);
|
|
2414
|
+
}
|
|
2415
|
+
if (filter?.dateTo) {
|
|
2416
|
+
const to = new Date(filter.dateTo).getTime();
|
|
2417
|
+
if (Number.isNaN(to)) throw new Error(`Invalid dateTo: ${filter.dateTo}`);
|
|
2418
|
+
entries = entries.filter((e) => new Date(e.timestamp).getTime() <= to);
|
|
2419
|
+
}
|
|
2420
|
+
return entries.slice(0, filter?.limit || 100);
|
|
2421
|
+
}
|
|
2381
2422
|
//#endregion
|
|
2382
2423
|
//#region src/state/index-persistence.ts
|
|
2383
2424
|
const DEBOUNCE_MS = 5e3;
|
|
2384
2425
|
const FAILURE_LOG_THROTTLE_MS = 6e4;
|
|
2426
|
+
const INDEX_PERSISTENCE_FUNCTION_ID = "mem::index-persistence";
|
|
2427
|
+
const BM25_KEY = "data";
|
|
2428
|
+
const BM25_MANIFEST_KEY = "data:manifest";
|
|
2429
|
+
const BM25_SHARD_SCOPE_PREFIX = `${KV.bm25Index}:bm25:`;
|
|
2430
|
+
const VECTOR_KEY = "vectors";
|
|
2431
|
+
const VECTOR_MANIFEST_KEY = "vectors:manifest";
|
|
2432
|
+
const VECTOR_SHARD_SCOPE_PREFIX = `${KV.bm25Index}:vectors:`;
|
|
2433
|
+
const INDEX_SHARD_KEY = "data";
|
|
2434
|
+
const DEFAULT_INDEX_SHARD_CHARS = 2e6;
|
|
2435
|
+
function shardChars(options) {
|
|
2436
|
+
const configured = options.shardChars;
|
|
2437
|
+
if (typeof configured !== "number" || !Number.isFinite(configured)) return DEFAULT_INDEX_SHARD_CHARS;
|
|
2438
|
+
const wholeChars = Math.floor(configured);
|
|
2439
|
+
return wholeChars >= 1 ? wholeChars : DEFAULT_INDEX_SHARD_CHARS;
|
|
2440
|
+
}
|
|
2441
|
+
function createIndexGeneration() {
|
|
2442
|
+
return generateId("idx");
|
|
2443
|
+
}
|
|
2444
|
+
function statePath(scope, key) {
|
|
2445
|
+
return `${scope}/${key}`;
|
|
2446
|
+
}
|
|
2447
|
+
function errorMessage(err) {
|
|
2448
|
+
return err instanceof Error ? err.message : String(err);
|
|
2449
|
+
}
|
|
2450
|
+
function isValidShardDescriptor(shard) {
|
|
2451
|
+
if (!shard || typeof shard !== "object") return false;
|
|
2452
|
+
const candidate = shard;
|
|
2453
|
+
return typeof candidate.scope === "string" && candidate.scope.length > 0 && typeof candidate.key === "string" && candidate.key.length > 0 && Number.isInteger(candidate.chars) && candidate.chars >= 0;
|
|
2454
|
+
}
|
|
2385
2455
|
var IndexPersistence = class {
|
|
2386
2456
|
timer = null;
|
|
2387
2457
|
lastFailureLogAt = 0;
|
|
2388
|
-
constructor(kv, bm25, vector) {
|
|
2458
|
+
constructor(kv, bm25, vector, options = {}) {
|
|
2389
2459
|
this.kv = kv;
|
|
2390
2460
|
this.bm25 = bm25;
|
|
2391
2461
|
this.vector = vector;
|
|
2462
|
+
this.options = options;
|
|
2392
2463
|
}
|
|
2393
2464
|
scheduleSave() {
|
|
2394
2465
|
if (this.timer) clearTimeout(this.timer);
|
|
@@ -2402,8 +2473,8 @@ var IndexPersistence = class {
|
|
|
2402
2473
|
this.timer = null;
|
|
2403
2474
|
}
|
|
2404
2475
|
try {
|
|
2405
|
-
await this.
|
|
2406
|
-
if (this.vector
|
|
2476
|
+
await this.saveBm25Index(this.bm25.serialize());
|
|
2477
|
+
if (this.vector) await this.saveVectorIndex(this.vector.serialize());
|
|
2407
2478
|
} catch (err) {
|
|
2408
2479
|
this.logFailure(err);
|
|
2409
2480
|
}
|
|
@@ -2411,9 +2482,9 @@ var IndexPersistence = class {
|
|
|
2411
2482
|
async load() {
|
|
2412
2483
|
let bm25 = null;
|
|
2413
2484
|
let vector = null;
|
|
2414
|
-
const bm25Data = await this.
|
|
2485
|
+
const bm25Data = await this.loadBm25Data();
|
|
2415
2486
|
if (bm25Data && typeof bm25Data === "string") bm25 = SearchIndex.deserialize(bm25Data);
|
|
2416
|
-
const vecData = await this.
|
|
2487
|
+
const vecData = await this.loadVectorData();
|
|
2417
2488
|
if (vecData && typeof vecData === "string") vector = VectorIndex.deserialize(vecData);
|
|
2418
2489
|
return {
|
|
2419
2490
|
bm25,
|
|
@@ -2438,8 +2509,190 @@ var IndexPersistence = class {
|
|
|
2438
2509
|
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
|
|
2439
2510
|
});
|
|
2440
2511
|
}
|
|
2512
|
+
async saveBm25Index(serialized) {
|
|
2513
|
+
await this.saveShardedIndex(serialized, BM25_MANIFEST_KEY, BM25_KEY, BM25_SHARD_SCOPE_PREFIX);
|
|
2514
|
+
}
|
|
2515
|
+
async saveVectorIndex(serialized) {
|
|
2516
|
+
await this.saveShardedIndex(serialized, VECTOR_MANIFEST_KEY, VECTOR_KEY, VECTOR_SHARD_SCOPE_PREFIX);
|
|
2517
|
+
}
|
|
2518
|
+
async saveShardedIndex(serialized, manifestKey, legacyKey, scopePrefix) {
|
|
2519
|
+
const previous = await this.kv.get(KV.bm25Index, manifestKey).catch(() => null);
|
|
2520
|
+
const generation = this.options.createGeneration?.() ?? createIndexGeneration();
|
|
2521
|
+
const chunkChars = shardChars(this.options);
|
|
2522
|
+
const shards = [];
|
|
2523
|
+
const chunks = [];
|
|
2524
|
+
for (let offset = 0; offset < serialized.length; offset += chunkChars) {
|
|
2525
|
+
const shardIndex = shards.length;
|
|
2526
|
+
const scope = `${scopePrefix}${generation}:${String(shardIndex).padStart(5, "0")}`;
|
|
2527
|
+
const chunk = serialized.slice(offset, offset + chunkChars);
|
|
2528
|
+
shards.push({
|
|
2529
|
+
scope,
|
|
2530
|
+
key: INDEX_SHARD_KEY,
|
|
2531
|
+
chars: chunk.length
|
|
2532
|
+
});
|
|
2533
|
+
chunks.push(chunk);
|
|
2534
|
+
}
|
|
2535
|
+
const failedWrite = (await Promise.allSettled(shards.map(async (shard, index) => {
|
|
2536
|
+
const chunk = chunks[index] ?? "";
|
|
2537
|
+
await this.kv.set(shard.scope, shard.key, chunk);
|
|
2538
|
+
await this.auditIndexPersistence("shard_write", [statePath(shard.scope, shard.key)], {
|
|
2539
|
+
scope: shard.scope,
|
|
2540
|
+
key: shard.key,
|
|
2541
|
+
manifestKey,
|
|
2542
|
+
generation,
|
|
2543
|
+
chars: chunk.length
|
|
2544
|
+
});
|
|
2545
|
+
}))).find((result) => result.status === "rejected");
|
|
2546
|
+
if (failedWrite) {
|
|
2547
|
+
await this.deleteShards(shards, "shard_write_rollback");
|
|
2548
|
+
throw failedWrite.reason;
|
|
2549
|
+
}
|
|
2550
|
+
const nextManifest = {
|
|
2551
|
+
v: 1,
|
|
2552
|
+
generation,
|
|
2553
|
+
shards,
|
|
2554
|
+
chars: serialized.length
|
|
2555
|
+
};
|
|
2556
|
+
try {
|
|
2557
|
+
await this.kv.set(KV.bm25Index, manifestKey, nextManifest);
|
|
2558
|
+
await this.auditIndexPersistence("manifest_publish", [statePath(KV.bm25Index, manifestKey)], {
|
|
2559
|
+
manifestKey,
|
|
2560
|
+
generation,
|
|
2561
|
+
chars: serialized.length,
|
|
2562
|
+
shards: shards.length,
|
|
2563
|
+
result: "committed"
|
|
2564
|
+
});
|
|
2565
|
+
} catch (err) {
|
|
2566
|
+
if (await this.isManifestPublished(manifestKey, nextManifest)) await this.auditIndexPersistence("manifest_publish", [statePath(KV.bm25Index, manifestKey)], {
|
|
2567
|
+
manifestKey,
|
|
2568
|
+
generation,
|
|
2569
|
+
chars: serialized.length,
|
|
2570
|
+
shards: shards.length,
|
|
2571
|
+
result: "committed_after_error",
|
|
2572
|
+
error: errorMessage(err)
|
|
2573
|
+
});
|
|
2574
|
+
else await this.deleteShards(shards, "manifest_publish_rollback");
|
|
2575
|
+
throw err;
|
|
2576
|
+
}
|
|
2577
|
+
await this.deleteKey(KV.bm25Index, legacyKey, "legacy_cleanup");
|
|
2578
|
+
if (previous?.v === 1 && Array.isArray(previous.shards)) {
|
|
2579
|
+
const currentShardIds = new Set(shards.map((shard) => `${shard.scope}\0${shard.key}`));
|
|
2580
|
+
for (const shard of previous.shards) {
|
|
2581
|
+
if (currentShardIds.has(`${shard.scope}\0${shard.key}`)) continue;
|
|
2582
|
+
await this.deleteShards([shard], "previous_generation_cleanup");
|
|
2583
|
+
}
|
|
2584
|
+
}
|
|
2585
|
+
}
|
|
2586
|
+
async auditIndexPersistence(action, targetIds, details) {
|
|
2587
|
+
await safeAudit(this.kv, "index_persist", INDEX_PERSISTENCE_FUNCTION_ID, targetIds, {
|
|
2588
|
+
action,
|
|
2589
|
+
...details
|
|
2590
|
+
});
|
|
2591
|
+
}
|
|
2592
|
+
async deleteKey(scope, key, reason) {
|
|
2593
|
+
let result = "deleted";
|
|
2594
|
+
let error;
|
|
2595
|
+
try {
|
|
2596
|
+
await this.kv.delete(scope, key);
|
|
2597
|
+
} catch (err) {
|
|
2598
|
+
result = "failed";
|
|
2599
|
+
error = errorMessage(err);
|
|
2600
|
+
}
|
|
2601
|
+
await this.auditIndexPersistence("delete", [statePath(scope, key)], {
|
|
2602
|
+
scope,
|
|
2603
|
+
key,
|
|
2604
|
+
reason,
|
|
2605
|
+
result,
|
|
2606
|
+
error
|
|
2607
|
+
});
|
|
2608
|
+
}
|
|
2609
|
+
async deleteShards(shards, reason) {
|
|
2610
|
+
for (const shard of shards) await this.deleteKey(shard.scope, shard.key, reason);
|
|
2611
|
+
}
|
|
2612
|
+
async isManifestPublished(manifestKey, expected) {
|
|
2613
|
+
const published = await this.kv.get(KV.bm25Index, manifestKey).catch(() => null);
|
|
2614
|
+
if (published?.v !== 1 || published.generation !== expected.generation || published.chars !== expected.chars || !Array.isArray(published.shards) || published.shards.length !== expected.shards.length) return false;
|
|
2615
|
+
return published.shards.every((shard, index) => {
|
|
2616
|
+
const expectedShard = expected.shards[index];
|
|
2617
|
+
if (!expectedShard) return false;
|
|
2618
|
+
return shard.scope === expectedShard.scope && shard.key === expectedShard.key && shard.chars === expectedShard.chars;
|
|
2619
|
+
});
|
|
2620
|
+
}
|
|
2621
|
+
async loadBm25Data() {
|
|
2622
|
+
return this.loadShardedData(BM25_KEY, BM25_MANIFEST_KEY, "BM25");
|
|
2623
|
+
}
|
|
2624
|
+
async loadVectorData() {
|
|
2625
|
+
return this.loadShardedData(VECTOR_KEY, VECTOR_MANIFEST_KEY, "vector");
|
|
2626
|
+
}
|
|
2627
|
+
async loadShardedData(legacyKey, manifestKey, label) {
|
|
2628
|
+
const manifest = await this.readIndexValue(KV.bm25Index, manifestKey, label, "manifest");
|
|
2629
|
+
if (!manifest.ok) return null;
|
|
2630
|
+
if (manifest.value != null && typeof manifest.value === "object") return this.loadManifestData(manifest.value, label);
|
|
2631
|
+
const legacy = await this.readIndexValue(KV.bm25Index, legacyKey, label, "legacy");
|
|
2632
|
+
if (!legacy.ok) return null;
|
|
2633
|
+
if (legacy.value && typeof legacy.value === "string") return legacy.value;
|
|
2634
|
+
return null;
|
|
2635
|
+
}
|
|
2636
|
+
async readIndexValue(scope, key, label, source) {
|
|
2637
|
+
try {
|
|
2638
|
+
return {
|
|
2639
|
+
ok: true,
|
|
2640
|
+
value: await this.kv.get(scope, key)
|
|
2641
|
+
};
|
|
2642
|
+
} catch (err) {
|
|
2643
|
+
logger.warn(`index persistence: ${label} ${source} read failed`, {
|
|
2644
|
+
scope,
|
|
2645
|
+
key,
|
|
2646
|
+
message: errorMessage(err)
|
|
2647
|
+
});
|
|
2648
|
+
return { ok: false };
|
|
2649
|
+
}
|
|
2650
|
+
}
|
|
2651
|
+
async loadManifestData(manifest, label) {
|
|
2652
|
+
if (manifest.v !== 1 || !Array.isArray(manifest.shards) || manifest.shards.length === 0 || !Number.isInteger(manifest.chars) || manifest.chars < 0) {
|
|
2653
|
+
logger.warn(`index persistence: ${label} shard manifest invalid`);
|
|
2654
|
+
return null;
|
|
2655
|
+
}
|
|
2656
|
+
for (const shard of manifest.shards) if (!isValidShardDescriptor(shard)) {
|
|
2657
|
+
logger.warn(`index persistence: ${label} shard manifest invalid`);
|
|
2658
|
+
return null;
|
|
2659
|
+
}
|
|
2660
|
+
const loadedShards = await Promise.all(manifest.shards.map(async (shard) => ({
|
|
2661
|
+
shard,
|
|
2662
|
+
chunk: await this.kv.get(shard.scope, shard.key).catch(() => null)
|
|
2663
|
+
})));
|
|
2664
|
+
const chunks = [];
|
|
2665
|
+
let chars = 0;
|
|
2666
|
+
for (const { shard, chunk } of loadedShards) {
|
|
2667
|
+
if (typeof chunk !== "string") {
|
|
2668
|
+
logger.warn(`index persistence: ${label} shard missing`, {
|
|
2669
|
+
scope: shard.scope,
|
|
2670
|
+
key: shard.key
|
|
2671
|
+
});
|
|
2672
|
+
return null;
|
|
2673
|
+
}
|
|
2674
|
+
if (chunk.length !== shard.chars) {
|
|
2675
|
+
logger.warn(`index persistence: ${label} shard length mismatch`, {
|
|
2676
|
+
scope: shard.scope,
|
|
2677
|
+
key: shard.key,
|
|
2678
|
+
expected: shard.chars,
|
|
2679
|
+
actual: chunk.length
|
|
2680
|
+
});
|
|
2681
|
+
return null;
|
|
2682
|
+
}
|
|
2683
|
+
chunks.push(chunk);
|
|
2684
|
+
chars += chunk.length;
|
|
2685
|
+
}
|
|
2686
|
+
if (chars !== manifest.chars) {
|
|
2687
|
+
logger.warn(`index persistence: ${label} total length mismatch`, {
|
|
2688
|
+
expected: manifest.chars,
|
|
2689
|
+
actual: chars
|
|
2690
|
+
});
|
|
2691
|
+
return null;
|
|
2692
|
+
}
|
|
2693
|
+
return chunks.join("");
|
|
2694
|
+
}
|
|
2441
2695
|
};
|
|
2442
|
-
|
|
2443
2696
|
//#endregion
|
|
2444
2697
|
//#region src/functions/privacy.ts
|
|
2445
2698
|
const PRIVATE_TAG_RE = /<private>[\s\S]*?<\/private>/gi;
|
|
@@ -2476,7 +2729,6 @@ function registerPrivacyFunction(sdk) {
|
|
|
2476
2729
|
return { output: stripPrivateData(data.input) };
|
|
2477
2730
|
});
|
|
2478
2731
|
}
|
|
2479
|
-
|
|
2480
2732
|
//#endregion
|
|
2481
2733
|
//#region src/functions/compress-synthetic.ts
|
|
2482
2734
|
function inferType(toolName, hookType) {
|
|
@@ -2572,7 +2824,6 @@ function buildSyntheticCompression(raw) {
|
|
|
2572
2824
|
if (raw.agentId) result.agentId = raw.agentId;
|
|
2573
2825
|
return result;
|
|
2574
2826
|
}
|
|
2575
|
-
|
|
2576
2827
|
//#endregion
|
|
2577
2828
|
//#region src/functions/access-tracker.ts
|
|
2578
2829
|
const RECENT_CAP = 20;
|
|
@@ -2643,7 +2894,6 @@ async function deleteAccessLog(kv, memoryId) {
|
|
|
2643
2894
|
});
|
|
2644
2895
|
} catch {}
|
|
2645
2896
|
}
|
|
2646
|
-
|
|
2647
2897
|
//#endregion
|
|
2648
2898
|
//#region src/functions/search.ts
|
|
2649
2899
|
let index = null;
|
|
@@ -2999,7 +3249,6 @@ function registerSearchFunction(sdk, kv) {
|
|
|
2999
3249
|
};
|
|
3000
3250
|
});
|
|
3001
3251
|
}
|
|
3002
|
-
|
|
3003
3252
|
//#endregion
|
|
3004
3253
|
//#region src/functions/observe.ts
|
|
3005
3254
|
function extractImage(d) {
|
|
@@ -3078,25 +3327,37 @@ function registerObserveFunction(sdk, kv, dedupMap, maxObservationsPerSession) {
|
|
|
3078
3327
|
const inheritedAgentId = existingSession ? existingSession.agentId : getAgentId();
|
|
3079
3328
|
if (inheritedAgentId) raw.agentId = inheritedAgentId;
|
|
3080
3329
|
if (pendingImageData && (pendingImageData.startsWith("data:image/") || pendingImageData.startsWith("iVBORw0KGgo") || pendingImageData.startsWith("/9j/"))) {
|
|
3081
|
-
const { saveImageToDisk } = await import("./image-store-
|
|
3330
|
+
const { saveImageToDisk } = await import("./image-store-Gpo2mgM9.mjs").then((n) => n.i);
|
|
3082
3331
|
const { filePath, bytesWritten } = await saveImageToDisk(pendingImageData);
|
|
3083
3332
|
raw.imageData = filePath;
|
|
3084
|
-
const { incrementImageRef } = await import("./image-refs-
|
|
3333
|
+
const { incrementImageRef } = await import("./image-refs-C7h9L5wx.mjs").then((n) => n.n);
|
|
3085
3334
|
await incrementImageRef(kv, filePath);
|
|
3086
|
-
sdk.
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3335
|
+
sdk.trigger({
|
|
3336
|
+
function_id: "mem::disk-size-delta",
|
|
3337
|
+
payload: { deltaBytes: bytesWritten },
|
|
3338
|
+
action: TriggerAction.Void()
|
|
3339
|
+
});
|
|
3340
|
+
if (process.env["AGENTMEMORY_IMAGE_EMBEDDINGS"] === "true") sdk.trigger({
|
|
3341
|
+
function_id: "mem::vision-embed",
|
|
3342
|
+
payload: {
|
|
3343
|
+
imageRef: filePath,
|
|
3344
|
+
sessionId: payload.sessionId,
|
|
3345
|
+
observationId: obsId
|
|
3346
|
+
},
|
|
3347
|
+
action: TriggerAction.Void()
|
|
3091
3348
|
});
|
|
3092
3349
|
}
|
|
3093
3350
|
try {
|
|
3094
3351
|
await kv.set(KV.observations(payload.sessionId), obsId, raw);
|
|
3095
3352
|
} catch (error) {
|
|
3096
3353
|
if (raw.imageData) {
|
|
3097
|
-
const { deleteImage } = await import("./image-store-
|
|
3354
|
+
const { deleteImage } = await import("./image-store-Gpo2mgM9.mjs").then((n) => n.i);
|
|
3098
3355
|
const { deletedBytes } = await deleteImage(raw.imageData);
|
|
3099
|
-
if (deletedBytes > 0) sdk.
|
|
3356
|
+
if (deletedBytes > 0) sdk.trigger({
|
|
3357
|
+
function_id: "mem::disk-size-delta",
|
|
3358
|
+
payload: { deltaBytes: -deletedBytes },
|
|
3359
|
+
action: TriggerAction.Void()
|
|
3360
|
+
});
|
|
3100
3361
|
}
|
|
3101
3362
|
throw error;
|
|
3102
3363
|
}
|
|
@@ -3216,7 +3477,6 @@ function registerObserveFunction(sdk, kv, dedupMap, maxObservationsPerSession) {
|
|
|
3216
3477
|
});
|
|
3217
3478
|
});
|
|
3218
3479
|
}
|
|
3219
|
-
|
|
3220
3480
|
//#endregion
|
|
3221
3481
|
//#region src/functions/image-quota-cleanup.ts
|
|
3222
3482
|
const GRACE_PERIOD_MS = 3e4;
|
|
@@ -3276,7 +3536,11 @@ function registerImageQuotaCleanup(sdk, kv) {
|
|
|
3276
3536
|
if (refCount > 0) return;
|
|
3277
3537
|
const { deletedBytes } = await deleteImage(f.filePath);
|
|
3278
3538
|
if (deletedBytes > 0) {
|
|
3279
|
-
sdk.
|
|
3539
|
+
sdk.trigger({
|
|
3540
|
+
function_id: "mem::disk-size-delta",
|
|
3541
|
+
payload: { deltaBytes: -deletedBytes },
|
|
3542
|
+
action: TriggerAction.Void()
|
|
3543
|
+
});
|
|
3280
3544
|
totalToFree -= deletedBytes;
|
|
3281
3545
|
freedBytes += deletedBytes;
|
|
3282
3546
|
evicted++;
|
|
@@ -3298,53 +3562,6 @@ function registerImageQuotaCleanup(sdk, kv) {
|
|
|
3298
3562
|
});
|
|
3299
3563
|
});
|
|
3300
3564
|
}
|
|
3301
|
-
|
|
3302
|
-
//#endregion
|
|
3303
|
-
//#region src/functions/audit.ts
|
|
3304
|
-
async function recordAudit(kv, operation, functionId, targetIds, details = {}, qualityScore, userId) {
|
|
3305
|
-
const entry = {
|
|
3306
|
-
id: generateId("aud"),
|
|
3307
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3308
|
-
operation,
|
|
3309
|
-
userId,
|
|
3310
|
-
functionId,
|
|
3311
|
-
targetIds,
|
|
3312
|
-
details,
|
|
3313
|
-
qualityScore
|
|
3314
|
-
};
|
|
3315
|
-
await kv.set(KV.audit, entry.id, entry);
|
|
3316
|
-
return entry;
|
|
3317
|
-
}
|
|
3318
|
-
async function safeAudit(kv, operation, functionId, targetIds, details = {}, qualityScore, userId) {
|
|
3319
|
-
try {
|
|
3320
|
-
await recordAudit(kv, operation, functionId, targetIds, details, qualityScore, userId);
|
|
3321
|
-
} catch (err) {
|
|
3322
|
-
try {
|
|
3323
|
-
logger.warn("audit write failed", {
|
|
3324
|
-
functionId,
|
|
3325
|
-
operation,
|
|
3326
|
-
targetIds,
|
|
3327
|
-
error: err instanceof Error ? err.message : String(err)
|
|
3328
|
-
});
|
|
3329
|
-
} catch {}
|
|
3330
|
-
}
|
|
3331
|
-
}
|
|
3332
|
-
async function queryAudit(kv, filter) {
|
|
3333
|
-
let entries = [...await kv.list(KV.audit)].sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
|
|
3334
|
-
if (filter?.operation) entries = entries.filter((e) => e.operation === filter.operation);
|
|
3335
|
-
if (filter?.dateFrom) {
|
|
3336
|
-
const from = new Date(filter.dateFrom).getTime();
|
|
3337
|
-
if (Number.isNaN(from)) throw new Error(`Invalid dateFrom: ${filter.dateFrom}`);
|
|
3338
|
-
entries = entries.filter((e) => new Date(e.timestamp).getTime() >= from);
|
|
3339
|
-
}
|
|
3340
|
-
if (filter?.dateTo) {
|
|
3341
|
-
const to = new Date(filter.dateTo).getTime();
|
|
3342
|
-
if (Number.isNaN(to)) throw new Error(`Invalid dateTo: ${filter.dateTo}`);
|
|
3343
|
-
entries = entries.filter((e) => new Date(e.timestamp).getTime() <= to);
|
|
3344
|
-
}
|
|
3345
|
-
return entries.slice(0, filter?.limit || 100);
|
|
3346
|
-
}
|
|
3347
|
-
|
|
3348
3565
|
//#endregion
|
|
3349
3566
|
//#region src/functions/vision-search.ts
|
|
3350
3567
|
function registerVisionSearchFunctions(sdk, kv, imageProvider) {
|
|
@@ -3468,7 +3685,6 @@ function cosine(a, b) {
|
|
|
3468
3685
|
const denom = Math.sqrt(normA) * Math.sqrt(normB);
|
|
3469
3686
|
return denom === 0 ? 0 : dot / denom;
|
|
3470
3687
|
}
|
|
3471
|
-
|
|
3472
3688
|
//#endregion
|
|
3473
3689
|
//#region src/functions/slots.ts
|
|
3474
3690
|
const DEFAULT_SIZE_LIMIT = 2e3;
|
|
@@ -3907,7 +4123,6 @@ function registerSlotsFunctions(sdk, kv) {
|
|
|
3907
4123
|
};
|
|
3908
4124
|
});
|
|
3909
4125
|
}
|
|
3910
|
-
|
|
3911
4126
|
//#endregion
|
|
3912
4127
|
//#region src/functions/disk-size-manager.ts
|
|
3913
4128
|
const DISK_SIZE_KEY = "system:currentDiskSize";
|
|
@@ -3922,7 +4137,11 @@ function registerDiskSizeManager(sdk, kv) {
|
|
|
3922
4137
|
if (newTotal < 0) newTotal = 0;
|
|
3923
4138
|
await kv.set(KV.state, DISK_SIZE_KEY, newTotal);
|
|
3924
4139
|
if (data.deltaBytes > 0 && newTotal > getMaxBytes()) {
|
|
3925
|
-
sdk.
|
|
4140
|
+
sdk.trigger({
|
|
4141
|
+
function_id: "mem::image-quota-cleanup",
|
|
4142
|
+
payload: {},
|
|
4143
|
+
action: TriggerAction.Void()
|
|
4144
|
+
});
|
|
3926
4145
|
logger.info("Disk quota exceeded, cleanup triggered", {
|
|
3927
4146
|
currentBytes: newTotal,
|
|
3928
4147
|
maxBytes: getMaxBytes()
|
|
@@ -3935,7 +4154,6 @@ function registerDiskSizeManager(sdk, kv) {
|
|
|
3935
4154
|
});
|
|
3936
4155
|
});
|
|
3937
4156
|
}
|
|
3938
|
-
|
|
3939
4157
|
//#endregion
|
|
3940
4158
|
//#region src/prompts/compression.ts
|
|
3941
4159
|
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.
|
|
@@ -3983,7 +4201,6 @@ function buildCompressionPrompt(observation) {
|
|
|
3983
4201
|
function truncate$1(s, max) {
|
|
3984
4202
|
return s.length > max ? s.slice(0, max) + "\n[...truncated]" : s;
|
|
3985
4203
|
}
|
|
3986
|
-
|
|
3987
4204
|
//#endregion
|
|
3988
4205
|
//#region src/prompts/vision.ts
|
|
3989
4206
|
const VISION_DESCRIPTION_PROMPT = `Describe what this image shows in the context of software development. Extract:
|
|
@@ -3994,7 +4211,6 @@ const VISION_DESCRIPTION_PROMPT = `Describe what this image shows in the context
|
|
|
3994
4211
|
- Text content visible in the image
|
|
3995
4212
|
|
|
3996
4213
|
Be concise but preserve all technically relevant details. Output plain text, no XML.`;
|
|
3997
|
-
|
|
3998
4214
|
//#endregion
|
|
3999
4215
|
//#region src/prompts/xml.ts
|
|
4000
4216
|
const VALID_TAG = /^[a-zA-Z_][a-zA-Z0-9_-]*$/;
|
|
@@ -4013,7 +4229,6 @@ function getXmlChildren(xml, parentTag, childTag) {
|
|
|
4013
4229
|
while ((m = re.exec(parentMatch[1])) !== null) items.push(m[1].trim());
|
|
4014
4230
|
return items;
|
|
4015
4231
|
}
|
|
4016
|
-
|
|
4017
4232
|
//#endregion
|
|
4018
4233
|
//#region src/eval/schemas.ts
|
|
4019
4234
|
const HookTypeEnum = z.enum([
|
|
@@ -4046,7 +4261,7 @@ const ObservationTypeEnum = z.enum([
|
|
|
4046
4261
|
"task",
|
|
4047
4262
|
"other"
|
|
4048
4263
|
]);
|
|
4049
|
-
|
|
4264
|
+
z.object({
|
|
4050
4265
|
hookType: HookTypeEnum,
|
|
4051
4266
|
sessionId: z.string().min(1),
|
|
4052
4267
|
project: z.string().min(1),
|
|
@@ -4071,16 +4286,16 @@ const SummaryOutputSchema = z.object({
|
|
|
4071
4286
|
filesModified: z.array(z.string()),
|
|
4072
4287
|
concepts: z.array(z.string())
|
|
4073
4288
|
});
|
|
4074
|
-
|
|
4289
|
+
z.object({
|
|
4075
4290
|
query: z.string().min(1),
|
|
4076
4291
|
limit: z.number().int().positive().optional()
|
|
4077
4292
|
});
|
|
4078
|
-
|
|
4293
|
+
z.object({
|
|
4079
4294
|
sessionId: z.string().min(1),
|
|
4080
4295
|
project: z.string().min(1),
|
|
4081
4296
|
budget: z.number().positive().optional()
|
|
4082
4297
|
});
|
|
4083
|
-
|
|
4298
|
+
z.object({
|
|
4084
4299
|
content: z.string().min(1),
|
|
4085
4300
|
type: z.enum([
|
|
4086
4301
|
"pattern",
|
|
@@ -4093,22 +4308,22 @@ const RememberInputSchema = z.object({
|
|
|
4093
4308
|
concepts: z.array(z.string()).optional(),
|
|
4094
4309
|
files: z.array(z.string()).optional()
|
|
4095
4310
|
});
|
|
4096
|
-
|
|
4311
|
+
z.object({
|
|
4097
4312
|
query: z.string().optional(),
|
|
4098
4313
|
expandIds: z.array(z.string()).optional(),
|
|
4099
4314
|
limit: z.number().int().positive().optional()
|
|
4100
4315
|
});
|
|
4101
|
-
|
|
4316
|
+
z.object({
|
|
4102
4317
|
anchor: z.string().min(1),
|
|
4103
4318
|
project: z.string().optional(),
|
|
4104
4319
|
before: z.number().int().nonnegative().optional(),
|
|
4105
4320
|
after: z.number().int().nonnegative().optional()
|
|
4106
4321
|
});
|
|
4107
|
-
|
|
4322
|
+
z.object({
|
|
4108
4323
|
project: z.string().min(1),
|
|
4109
4324
|
refresh: z.boolean().optional()
|
|
4110
4325
|
});
|
|
4111
|
-
|
|
4326
|
+
z.object({
|
|
4112
4327
|
sourceId: z.string().min(1),
|
|
4113
4328
|
targetId: z.string().min(1),
|
|
4114
4329
|
type: z.enum([
|
|
@@ -4119,12 +4334,12 @@ const RelateInputSchema = z.object({
|
|
|
4119
4334
|
"related"
|
|
4120
4335
|
])
|
|
4121
4336
|
});
|
|
4122
|
-
|
|
4337
|
+
z.object({
|
|
4123
4338
|
memoryId: z.string().min(1),
|
|
4124
4339
|
newContent: z.string().min(1),
|
|
4125
4340
|
newTitle: z.string().optional()
|
|
4126
4341
|
});
|
|
4127
|
-
|
|
4342
|
+
z.object({
|
|
4128
4343
|
exportData: z.object({
|
|
4129
4344
|
version: z.union([z.literal("0.3.0"), z.literal("0.4.0")]),
|
|
4130
4345
|
exportedAt: z.string(),
|
|
@@ -4140,7 +4355,6 @@ const ExportImportInputSchema = z.object({
|
|
|
4140
4355
|
"skip"
|
|
4141
4356
|
]).optional()
|
|
4142
4357
|
});
|
|
4143
|
-
|
|
4144
4358
|
//#endregion
|
|
4145
4359
|
//#region src/eval/validator.ts
|
|
4146
4360
|
function validateInput(schema, data, functionId) {
|
|
@@ -4163,7 +4377,6 @@ function validateInput(schema, data, functionId) {
|
|
|
4163
4377
|
function validateOutput(schema, data, functionId) {
|
|
4164
4378
|
return validateInput(schema, data, functionId);
|
|
4165
4379
|
}
|
|
4166
|
-
|
|
4167
4380
|
//#endregion
|
|
4168
4381
|
//#region src/eval/quality.ts
|
|
4169
4382
|
function scoreCompression(obs) {
|
|
@@ -4187,7 +4400,6 @@ function scoreSummary(summary) {
|
|
|
4187
4400
|
if (summary.concepts && summary.concepts.length > 0) score += 15;
|
|
4188
4401
|
return Math.min(100, score);
|
|
4189
4402
|
}
|
|
4190
|
-
|
|
4191
4403
|
//#endregion
|
|
4192
4404
|
//#region src/eval/self-correct.ts
|
|
4193
4405
|
const STRICTER_SUFFIX = `
|
|
@@ -4211,7 +4423,6 @@ async function compressWithRetry(provider, systemPrompt, userPrompt, validator,
|
|
|
4211
4423
|
retried: true
|
|
4212
4424
|
};
|
|
4213
4425
|
}
|
|
4214
|
-
|
|
4215
4426
|
//#endregion
|
|
4216
4427
|
//#region src/functions/compress.ts
|
|
4217
4428
|
const VALID_TYPES$1 = new Set([
|
|
@@ -4392,7 +4603,6 @@ function registerCompressFunction(sdk, kv, provider, metricsStore) {
|
|
|
4392
4603
|
}
|
|
4393
4604
|
});
|
|
4394
4605
|
}
|
|
4395
|
-
|
|
4396
4606
|
//#endregion
|
|
4397
4607
|
//#region src/functions/context.ts
|
|
4398
4608
|
function estimateTokens$1(text) {
|
|
@@ -4517,7 +4727,6 @@ function registerContextFunction(sdk, kv, tokenBudget) {
|
|
|
4517
4727
|
};
|
|
4518
4728
|
});
|
|
4519
4729
|
}
|
|
4520
|
-
|
|
4521
4730
|
//#endregion
|
|
4522
4731
|
//#region src/prompts/summary.ts
|
|
4523
4732
|
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.
|
|
@@ -4589,7 +4798,6 @@ Concepts: ${concepts}`;
|
|
|
4589
4798
|
});
|
|
4590
4799
|
return `Partial summaries (${partials.length} chunks of one session, chronological):\n\n${sections.join("\n\n---\n\n")}`;
|
|
4591
4800
|
}
|
|
4592
|
-
|
|
4593
4801
|
//#endregion
|
|
4594
4802
|
//#region src/functions/summarize.ts
|
|
4595
4803
|
const CHUNK_SIZE_DEFAULT = 400;
|
|
@@ -4678,18 +4886,29 @@ async function produceSummaryXml(provider, compressed, sessionId, project) {
|
|
|
4678
4886
|
skipped
|
|
4679
4887
|
};
|
|
4680
4888
|
}
|
|
4889
|
+
function stripXmlWrappers(raw) {
|
|
4890
|
+
if (!raw) return "";
|
|
4891
|
+
let cleaned = raw.trim();
|
|
4892
|
+
cleaned = cleaned.replace(/```\s*xml\s*\n?/gi, "");
|
|
4893
|
+
cleaned = cleaned.replace(/```/g, "");
|
|
4894
|
+
cleaned = cleaned.trim();
|
|
4895
|
+
const rootMatch = cleaned.match(/(<[a-zA-Z_][a-zA-Z0-9_-]*>[\s\S]*<\/[a-zA-Z_][a-zA-Z0-9_-]*>)/);
|
|
4896
|
+
if (rootMatch && rootMatch[1]) return rootMatch[1].trim();
|
|
4897
|
+
return cleaned;
|
|
4898
|
+
}
|
|
4681
4899
|
function parseSummaryXml(xml, sessionId, project, obsCount) {
|
|
4682
|
-
const
|
|
4900
|
+
const cleaned = stripXmlWrappers(xml);
|
|
4901
|
+
const title = getXmlTag(cleaned, "title");
|
|
4683
4902
|
if (!title) return null;
|
|
4684
4903
|
return {
|
|
4685
4904
|
sessionId,
|
|
4686
4905
|
project,
|
|
4687
4906
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4688
4907
|
title,
|
|
4689
|
-
narrative: getXmlTag(
|
|
4690
|
-
keyDecisions: getXmlChildren(
|
|
4691
|
-
filesModified: getXmlChildren(
|
|
4692
|
-
concepts: getXmlChildren(
|
|
4908
|
+
narrative: getXmlTag(cleaned, "narrative"),
|
|
4909
|
+
keyDecisions: getXmlChildren(cleaned, "decisions", "decision"),
|
|
4910
|
+
filesModified: getXmlChildren(cleaned, "files", "file"),
|
|
4911
|
+
concepts: getXmlChildren(cleaned, "concepts", "concept"),
|
|
4693
4912
|
observationCount: obsCount
|
|
4694
4913
|
};
|
|
4695
4914
|
}
|
|
@@ -4726,27 +4945,44 @@ function registerSummarizeFunction(sdk, kv, provider, metricsStore) {
|
|
|
4726
4945
|
};
|
|
4727
4946
|
}
|
|
4728
4947
|
try {
|
|
4729
|
-
|
|
4948
|
+
let summary = null;
|
|
4949
|
+
let response = "";
|
|
4950
|
+
let mode = "single";
|
|
4951
|
+
let chunks = 1;
|
|
4952
|
+
for (let attempt = 1; attempt <= 2; attempt++) {
|
|
4953
|
+
const produced = await produceSummaryXml(provider, compressed, sessionId, session.project);
|
|
4954
|
+
response = produced.response;
|
|
4955
|
+
mode = produced.mode;
|
|
4956
|
+
chunks = produced.chunks;
|
|
4957
|
+
if (!response || !response.trim()) {
|
|
4958
|
+
logger.warn("Empty provider response on summarize", {
|
|
4959
|
+
sessionId,
|
|
4960
|
+
provider: provider.name,
|
|
4961
|
+
mode,
|
|
4962
|
+
chunks,
|
|
4963
|
+
observationCount: compressed.length,
|
|
4964
|
+
attempt
|
|
4965
|
+
});
|
|
4966
|
+
continue;
|
|
4967
|
+
}
|
|
4968
|
+
summary = parseSummaryXml(response, sessionId, session.project, compressed.length);
|
|
4969
|
+
if (summary) break;
|
|
4970
|
+
logger.warn("Failed to parse summary XML", {
|
|
4971
|
+
sessionId,
|
|
4972
|
+
attempt
|
|
4973
|
+
});
|
|
4974
|
+
}
|
|
4730
4975
|
if (!response || !response.trim()) {
|
|
4731
4976
|
const latencyMs = Date.now() - startMs;
|
|
4732
4977
|
if (metricsStore) await metricsStore.record("mem::summarize", latencyMs, false);
|
|
4733
|
-
logger.warn("Empty provider response on summarize", {
|
|
4734
|
-
sessionId,
|
|
4735
|
-
provider: provider.name,
|
|
4736
|
-
mode,
|
|
4737
|
-
chunks,
|
|
4738
|
-
observationCount: compressed.length
|
|
4739
|
-
});
|
|
4740
4978
|
return {
|
|
4741
4979
|
success: false,
|
|
4742
4980
|
error: "empty_provider_response"
|
|
4743
4981
|
};
|
|
4744
4982
|
}
|
|
4745
|
-
const summary = parseSummaryXml(response, sessionId, session.project, compressed.length);
|
|
4746
4983
|
if (!summary) {
|
|
4747
4984
|
const latencyMs = Date.now() - startMs;
|
|
4748
4985
|
if (metricsStore) await metricsStore.record("mem::summarize", latencyMs, false);
|
|
4749
|
-
logger.warn("Failed to parse summary XML", { sessionId });
|
|
4750
4986
|
return {
|
|
4751
4987
|
success: false,
|
|
4752
4988
|
error: "parse_failed"
|
|
@@ -4807,7 +5043,6 @@ function registerSummarizeFunction(sdk, kv, provider, metricsStore) {
|
|
|
4807
5043
|
}
|
|
4808
5044
|
});
|
|
4809
5045
|
}
|
|
4810
|
-
|
|
4811
5046
|
//#endregion
|
|
4812
5047
|
//#region src/functions/migrate.ts
|
|
4813
5048
|
const ALLOWED_DIRS = [resolve(homedir(), ".agentmemory")];
|
|
@@ -5008,7 +5243,6 @@ function safeJsonParse(value, fallback) {
|
|
|
5008
5243
|
}
|
|
5009
5244
|
return fallback;
|
|
5010
5245
|
}
|
|
5011
|
-
|
|
5012
5246
|
//#endregion
|
|
5013
5247
|
//#region src/functions/file-index.ts
|
|
5014
5248
|
function registerFileIndexFunction(sdk, kv) {
|
|
@@ -5078,7 +5312,6 @@ function registerFileIndexFunction(sdk, kv) {
|
|
|
5078
5312
|
return { context };
|
|
5079
5313
|
});
|
|
5080
5314
|
}
|
|
5081
|
-
|
|
5082
5315
|
//#endregion
|
|
5083
5316
|
//#region src/functions/consolidate.ts
|
|
5084
5317
|
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.
|
|
@@ -5230,7 +5463,6 @@ function registerConsolidateFunction(sdk, kv, provider) {
|
|
|
5230
5463
|
};
|
|
5231
5464
|
});
|
|
5232
5465
|
}
|
|
5233
|
-
|
|
5234
5466
|
//#endregion
|
|
5235
5467
|
//#region src/functions/patterns.ts
|
|
5236
5468
|
function registerPatternsFunction(sdk, kv) {
|
|
@@ -5314,7 +5546,6 @@ function registerPatternsFunction(sdk, kv) {
|
|
|
5314
5546
|
return { rules };
|
|
5315
5547
|
});
|
|
5316
5548
|
}
|
|
5317
|
-
|
|
5318
5549
|
//#endregion
|
|
5319
5550
|
//#region src/functions/remember.ts
|
|
5320
5551
|
function registerRememberFunction(sdk, kv) {
|
|
@@ -5420,7 +5651,7 @@ function registerRememberFunction(sdk, kv) {
|
|
|
5420
5651
|
const deletedMemoryIds = [];
|
|
5421
5652
|
const deletedObservationIds = [];
|
|
5422
5653
|
let deletedSession = false;
|
|
5423
|
-
const { decrementImageRef } = await import("./image-refs-
|
|
5654
|
+
const { decrementImageRef } = await import("./image-refs-C7h9L5wx.mjs").then((n) => n.n);
|
|
5424
5655
|
if (data.memoryId) {
|
|
5425
5656
|
const mem = await kv.get(KV.memories, data.memoryId);
|
|
5426
5657
|
await kv.delete(KV.memories, data.memoryId);
|
|
@@ -5475,7 +5706,6 @@ function registerRememberFunction(sdk, kv) {
|
|
|
5475
5706
|
};
|
|
5476
5707
|
});
|
|
5477
5708
|
}
|
|
5478
|
-
|
|
5479
5709
|
//#endregion
|
|
5480
5710
|
//#region src/functions/evict.ts
|
|
5481
5711
|
const MS_PER_DAY$1 = 1440 * 60 * 1e3;
|
|
@@ -5528,7 +5758,7 @@ async function runRecoveredSessionConsolidation(sdk) {
|
|
|
5528
5758
|
function registerEvictFunction(sdk, kv) {
|
|
5529
5759
|
sdk.registerFunction("mem::evict", async (data) => {
|
|
5530
5760
|
const dryRun = data?.dryRun ?? false;
|
|
5531
|
-
const { decrementImageRef } = await import("./image-refs-
|
|
5761
|
+
const { decrementImageRef } = await import("./image-refs-C7h9L5wx.mjs").then((n) => n.n);
|
|
5532
5762
|
const configOverride = await kv.get(KV.config, "eviction").catch(() => null);
|
|
5533
5763
|
const cfg = {
|
|
5534
5764
|
...DEFAULTS$1,
|
|
@@ -5706,7 +5936,6 @@ function registerEvictFunction(sdk, kv) {
|
|
|
5706
5936
|
return stats;
|
|
5707
5937
|
});
|
|
5708
5938
|
}
|
|
5709
|
-
|
|
5710
5939
|
//#endregion
|
|
5711
5940
|
//#region src/functions/relations.ts
|
|
5712
5941
|
function computeConfidence(source, target, relationType) {
|
|
@@ -5894,7 +6123,6 @@ function registerRelationsFunction(sdk, kv) {
|
|
|
5894
6123
|
return { results: result };
|
|
5895
6124
|
});
|
|
5896
6125
|
}
|
|
5897
|
-
|
|
5898
6126
|
//#endregion
|
|
5899
6127
|
//#region src/functions/timeline.ts
|
|
5900
6128
|
function registerTimelineFunction(sdk, kv) {
|
|
@@ -5976,9 +6204,75 @@ async function findByKeyword(kv, keyword, project) {
|
|
|
5976
6204
|
}
|
|
5977
6205
|
return matches.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
|
|
5978
6206
|
}
|
|
5979
|
-
|
|
6207
|
+
//#endregion
|
|
6208
|
+
//#region src/telemetry/setup.ts
|
|
6209
|
+
const OTEL_CONFIG = {
|
|
6210
|
+
serviceName: "agentmemory",
|
|
6211
|
+
serviceVersion: VERSION,
|
|
6212
|
+
metricsExportIntervalMs: 3e4
|
|
6213
|
+
};
|
|
6214
|
+
let counters = null;
|
|
6215
|
+
let histograms = null;
|
|
6216
|
+
const NOOP_COUNTER = { add: () => {} };
|
|
6217
|
+
const NOOP_HISTOGRAM = { record: () => {} };
|
|
6218
|
+
const COUNTER_NAMES = [
|
|
6219
|
+
["observationsTotal", "observations.total"],
|
|
6220
|
+
["compressionSuccess", "compression.success"],
|
|
6221
|
+
["compressionFailure", "compression.failure"],
|
|
6222
|
+
["searchTotal", "search.total"],
|
|
6223
|
+
["dedupSkipped", "dedup.skipped"],
|
|
6224
|
+
["evictionTotal", "eviction.total"],
|
|
6225
|
+
["circuitBreakerOpen", "circuit_breaker.open"],
|
|
6226
|
+
["embeddingSuccess", "embedding.success"],
|
|
6227
|
+
["embeddingFailure", "embedding.failure"],
|
|
6228
|
+
["vectorSearchTotal", "vector_search.total"],
|
|
6229
|
+
["autoForgetTotal", "auto_forget.total"],
|
|
6230
|
+
["profileGenerated", "profile.generated"],
|
|
6231
|
+
["claudeBridgeSync", "claude_bridge.sync"],
|
|
6232
|
+
["graphExtraction", "graph.extraction"],
|
|
6233
|
+
["consolidationRun", "consolidation.run"],
|
|
6234
|
+
["teamShare", "team.share"],
|
|
6235
|
+
["auditLog", "audit.log"],
|
|
6236
|
+
["snapshotCreate", "snapshot.create"],
|
|
6237
|
+
["governanceDelete", "governance.delete"],
|
|
6238
|
+
["smartSearchFollowupWithinWindow", "smart_search.followup_within_window_total"],
|
|
6239
|
+
["readerFailureWithEvidence", "reader_failure_with_evidence_total"]
|
|
6240
|
+
];
|
|
6241
|
+
const HISTOGRAM_NAMES = [
|
|
6242
|
+
["compressionLatency", "compression.latency_ms"],
|
|
6243
|
+
["searchLatency", "search.latency_ms"],
|
|
6244
|
+
["contextTokens", "context.tokens"],
|
|
6245
|
+
["qualityScore", "quality.score"],
|
|
6246
|
+
["embeddingLatency", "embedding.latency_ms"],
|
|
6247
|
+
["vectorSearchLatency", "vector_search.latency_ms"]
|
|
6248
|
+
];
|
|
6249
|
+
function getCounters() {
|
|
6250
|
+
if (counters) return counters;
|
|
6251
|
+
return Object.fromEntries(COUNTER_NAMES.map(([key]) => [key, NOOP_COUNTER]));
|
|
6252
|
+
}
|
|
6253
|
+
function initMetrics(getMeter) {
|
|
6254
|
+
const meter = getMeter?.("agentmemory");
|
|
6255
|
+
counters = Object.fromEntries(COUNTER_NAMES.map(([key, name]) => [key, meter ? meter.createCounter(name) : NOOP_COUNTER]));
|
|
6256
|
+
histograms = Object.fromEntries(HISTOGRAM_NAMES.map(([key, name]) => [key, meter ? meter.createHistogram(name) : NOOP_HISTOGRAM]));
|
|
6257
|
+
return {
|
|
6258
|
+
counters,
|
|
6259
|
+
histograms
|
|
6260
|
+
};
|
|
6261
|
+
}
|
|
5980
6262
|
//#endregion
|
|
5981
6263
|
//#region src/functions/smart-search.ts
|
|
6264
|
+
const followupStats = {
|
|
6265
|
+
followupWithinWindow: 0,
|
|
6266
|
+
agentInitiatedSearches: 0
|
|
6267
|
+
};
|
|
6268
|
+
const pendingFollowups = /* @__PURE__ */ new Set();
|
|
6269
|
+
function getFollowupStats() {
|
|
6270
|
+
const total = followupStats.agentInitiatedSearches;
|
|
6271
|
+
return {
|
|
6272
|
+
...followupStats,
|
|
6273
|
+
rate: total > 0 ? followupStats.followupWithinWindow / total : 0
|
|
6274
|
+
};
|
|
6275
|
+
}
|
|
5982
6276
|
const LESSON_CONTENT_PREVIEW_CHARS = 240;
|
|
5983
6277
|
function registerSmartSearchFunction(sdk, kv, searchFn) {
|
|
5984
6278
|
sdk.registerFunction("mem::smart-search", async (data) => {
|
|
@@ -6040,6 +6334,21 @@ function registerSmartSearchFunction(sdk, kv, searchFn) {
|
|
|
6040
6334
|
timestamp: r.observation.timestamp
|
|
6041
6335
|
}));
|
|
6042
6336
|
recordAccessBatch(kv, compact.map((r) => r.obsId));
|
|
6337
|
+
if (data.sessionId && typeof data.sessionId === "string" && data.source !== "viewer" && compact.length > 0) {
|
|
6338
|
+
followupStats.agentInitiatedSearches++;
|
|
6339
|
+
const sessionIdForFollowup = data.sessionId;
|
|
6340
|
+
const queryForFollowup = data.query;
|
|
6341
|
+
const compactForFollowup = compact;
|
|
6342
|
+
const detection = withKeyedLock(`recent-searches:${sessionIdForFollowup}`, () => detectFollowup(kv, sessionIdForFollowup, queryForFollowup, compactForFollowup)).catch((err) => {
|
|
6343
|
+
logger.warn("Smart search followup detection failed", {
|
|
6344
|
+
sessionId: sessionIdForFollowup,
|
|
6345
|
+
error: err instanceof Error ? err.message : String(err)
|
|
6346
|
+
});
|
|
6347
|
+
}).finally(() => {
|
|
6348
|
+
pendingFollowups.delete(detection);
|
|
6349
|
+
});
|
|
6350
|
+
pendingFollowups.add(detection);
|
|
6351
|
+
}
|
|
6043
6352
|
logger.info("Smart search compact", {
|
|
6044
6353
|
query: data.query,
|
|
6045
6354
|
results: compact.length,
|
|
@@ -6078,6 +6387,35 @@ async function recallLessons(sdk, query, limit, project) {
|
|
|
6078
6387
|
return [];
|
|
6079
6388
|
}
|
|
6080
6389
|
}
|
|
6390
|
+
async function detectFollowup(kv, sessionId, query, compact) {
|
|
6391
|
+
const now = Date.now();
|
|
6392
|
+
const windowMs = Math.max(1, getFollowupWindowSeconds()) * 1e3;
|
|
6393
|
+
const currentIds = compact.map((r) => r.obsId);
|
|
6394
|
+
const current = {
|
|
6395
|
+
sessionId,
|
|
6396
|
+
query,
|
|
6397
|
+
resultIds: currentIds,
|
|
6398
|
+
at: now
|
|
6399
|
+
};
|
|
6400
|
+
const prior = await kv.get(KV.recentSearches, sessionId).catch(() => null);
|
|
6401
|
+
await kv.set(KV.recentSearches, sessionId, current);
|
|
6402
|
+
if (!prior || typeof prior.at !== "number") return;
|
|
6403
|
+
if (now - prior.at > windowMs) return;
|
|
6404
|
+
if (typeof prior.query === "string" && prior.query === query) return;
|
|
6405
|
+
const priorIds = Array.isArray(prior.resultIds) ? prior.resultIds : [];
|
|
6406
|
+
const priorSet = new Set(priorIds);
|
|
6407
|
+
if (currentIds.some((id) => priorSet.has(id))) return;
|
|
6408
|
+
getCounters().smartSearchFollowupWithinWindow.add(1);
|
|
6409
|
+
followupStats.followupWithinWindow++;
|
|
6410
|
+
logger.info("Smart search followup detected", {
|
|
6411
|
+
sessionId,
|
|
6412
|
+
windowSeconds: Math.round(windowMs / 1e3),
|
|
6413
|
+
priorQuery: prior.query,
|
|
6414
|
+
nextQuery: query,
|
|
6415
|
+
priorResultCount: priorIds.length,
|
|
6416
|
+
nextResultCount: currentIds.length
|
|
6417
|
+
});
|
|
6418
|
+
}
|
|
6081
6419
|
async function findObservation$1(kv, obsId, sessionIdHint) {
|
|
6082
6420
|
if (sessionIdHint) {
|
|
6083
6421
|
const obs = await kv.get(KV.observations(sessionIdHint), obsId).catch(() => null);
|
|
@@ -6091,7 +6429,52 @@ async function findObservation$1(kv, obsId, sessionIdHint) {
|
|
|
6091
6429
|
}
|
|
6092
6430
|
return null;
|
|
6093
6431
|
}
|
|
6094
|
-
|
|
6432
|
+
//#endregion
|
|
6433
|
+
//#region src/functions/recent-searches-sweep.ts
|
|
6434
|
+
const RETENTION_MS = 1440 * 60 * 1e3;
|
|
6435
|
+
function registerRecentSearchesSweepFunction(sdk, kv) {
|
|
6436
|
+
sdk.registerFunction("mem::diagnostic::recent-searches-sweep", async () => {
|
|
6437
|
+
const cutoff = Date.now() - RETENTION_MS;
|
|
6438
|
+
const rows = await kv.list(KV.recentSearches).catch(() => []);
|
|
6439
|
+
let swept = 0;
|
|
6440
|
+
let skipped = 0;
|
|
6441
|
+
for (const row of rows) {
|
|
6442
|
+
if (!row || typeof row.sessionId !== "string" || !row.sessionId) {
|
|
6443
|
+
skipped++;
|
|
6444
|
+
continue;
|
|
6445
|
+
}
|
|
6446
|
+
if ((typeof row.at === "number" ? row.at : 0) >= cutoff) continue;
|
|
6447
|
+
try {
|
|
6448
|
+
await kv.delete(KV.recentSearches, row.sessionId);
|
|
6449
|
+
swept++;
|
|
6450
|
+
} catch (err) {
|
|
6451
|
+
logger.warn("recent-searches sweep delete failed", {
|
|
6452
|
+
sessionId: row.sessionId,
|
|
6453
|
+
error: err instanceof Error ? err.message : String(err)
|
|
6454
|
+
});
|
|
6455
|
+
}
|
|
6456
|
+
}
|
|
6457
|
+
if (swept > 0 || skipped > 0) logger.info("Recent-searches sweep complete", {
|
|
6458
|
+
swept,
|
|
6459
|
+
skipped
|
|
6460
|
+
});
|
|
6461
|
+
return {
|
|
6462
|
+
success: true,
|
|
6463
|
+
swept,
|
|
6464
|
+
skipped
|
|
6465
|
+
};
|
|
6466
|
+
});
|
|
6467
|
+
sdk.registerFunction("mem::diagnostic::followup-stats", async () => {
|
|
6468
|
+
const stats = getFollowupStats();
|
|
6469
|
+
return {
|
|
6470
|
+
success: true,
|
|
6471
|
+
windowSeconds: getFollowupWindowSeconds(),
|
|
6472
|
+
agentInitiatedSearches: stats.agentInitiatedSearches,
|
|
6473
|
+
followupWithinWindow: stats.followupWithinWindow,
|
|
6474
|
+
rate: stats.rate
|
|
6475
|
+
};
|
|
6476
|
+
});
|
|
6477
|
+
}
|
|
6095
6478
|
//#endregion
|
|
6096
6479
|
//#region src/functions/profile.ts
|
|
6097
6480
|
function registerProfileFunction(sdk, kv) {
|
|
@@ -6179,7 +6562,6 @@ function extractConventions(concepts, files) {
|
|
|
6179
6562
|
for (const { concept, frequency } of concepts.slice(0, 5)) if (frequency >= 3) conventions.push(`Frequently uses: ${concept}`);
|
|
6180
6563
|
return conventions;
|
|
6181
6564
|
}
|
|
6182
|
-
|
|
6183
6565
|
//#endregion
|
|
6184
6566
|
//#region src/functions/auto-forget.ts
|
|
6185
6567
|
const MS_PER_DAY = 1440 * 60 * 1e3;
|
|
@@ -6188,7 +6570,7 @@ function registerAutoForgetFunction(sdk, kv) {
|
|
|
6188
6570
|
sdk.registerFunction("mem::auto-forget", async (data) => {
|
|
6189
6571
|
const dryRun = data?.dryRun ?? false;
|
|
6190
6572
|
const now = Date.now();
|
|
6191
|
-
const { decrementImageRef } = await import("./image-refs-
|
|
6573
|
+
const { decrementImageRef } = await import("./image-refs-C7h9L5wx.mjs").then((n) => n.n);
|
|
6192
6574
|
const result = {
|
|
6193
6575
|
ttlExpired: [],
|
|
6194
6576
|
contradictions: [],
|
|
@@ -6305,7 +6687,6 @@ function registerAutoForgetFunction(sdk, kv) {
|
|
|
6305
6687
|
return result;
|
|
6306
6688
|
});
|
|
6307
6689
|
}
|
|
6308
|
-
|
|
6309
6690
|
//#endregion
|
|
6310
6691
|
//#region src/functions/export-import.ts
|
|
6311
6692
|
function registerExportImportFunction(sdk, kv) {
|
|
@@ -6446,7 +6827,9 @@ function registerExportImportFunction(sdk, kv) {
|
|
|
6446
6827
|
"0.9.21",
|
|
6447
6828
|
"0.9.22",
|
|
6448
6829
|
"0.9.23",
|
|
6449
|
-
"0.9.24"
|
|
6830
|
+
"0.9.24",
|
|
6831
|
+
"0.9.25",
|
|
6832
|
+
"0.9.26"
|
|
6450
6833
|
]).has(importData.version)) return {
|
|
6451
6834
|
success: false,
|
|
6452
6835
|
error: `Unsupported export version: ${importData.version}`
|
|
@@ -6764,7 +7147,6 @@ function registerExportImportFunction(sdk, kv) {
|
|
|
6764
7147
|
};
|
|
6765
7148
|
});
|
|
6766
7149
|
}
|
|
6767
|
-
|
|
6768
7150
|
//#endregion
|
|
6769
7151
|
//#region src/functions/enrich.ts
|
|
6770
7152
|
const MAX_CONTEXT_LENGTH = 4e3;
|
|
@@ -6825,7 +7207,6 @@ function registerEnrichFunction(sdk, kv) {
|
|
|
6825
7207
|
};
|
|
6826
7208
|
});
|
|
6827
7209
|
}
|
|
6828
|
-
|
|
6829
7210
|
//#endregion
|
|
6830
7211
|
//#region src/functions/claude-bridge.ts
|
|
6831
7212
|
function parseMemoryMd(content) {
|
|
@@ -6946,7 +7327,6 @@ function registerClaudeBridgeFunction(sdk, kv, config) {
|
|
|
6946
7327
|
}
|
|
6947
7328
|
});
|
|
6948
7329
|
}
|
|
6949
|
-
|
|
6950
7330
|
//#endregion
|
|
6951
7331
|
//#region src/prompts/graph-extraction.ts
|
|
6952
7332
|
const GRAPH_EXTRACTION_SYSTEM = `You are a knowledge graph extraction engine. Given a compressed observation from a coding session, extract entities and relationships.
|
|
@@ -6969,9 +7349,41 @@ Rules:
|
|
|
6969
7349
|
function buildGraphExtractionPrompt(observations) {
|
|
6970
7350
|
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")}`;
|
|
6971
7351
|
}
|
|
6972
|
-
|
|
6973
7352
|
//#endregion
|
|
6974
7353
|
//#region src/functions/graph.ts
|
|
7354
|
+
const DEFAULT_GRAPH_QUERY_LIMIT = 500;
|
|
7355
|
+
const MAX_GRAPH_QUERY_LIMIT = 5e3;
|
|
7356
|
+
function resolvePagination(rawLimit, rawOffset) {
|
|
7357
|
+
return {
|
|
7358
|
+
limit: Math.max(1, Math.min(typeof rawLimit === "number" && Number.isFinite(rawLimit) ? Math.floor(rawLimit) : DEFAULT_GRAPH_QUERY_LIMIT, MAX_GRAPH_QUERY_LIMIT)),
|
|
7359
|
+
offset: Math.max(0, typeof rawOffset === "number" && Number.isFinite(rawOffset) ? Math.floor(rawOffset) : 0)
|
|
7360
|
+
};
|
|
7361
|
+
}
|
|
7362
|
+
function rankByDegree(nodes, edges) {
|
|
7363
|
+
const degree = /* @__PURE__ */ new Map();
|
|
7364
|
+
for (const edge of edges) {
|
|
7365
|
+
degree.set(edge.sourceNodeId, (degree.get(edge.sourceNodeId) ?? 0) + 1);
|
|
7366
|
+
degree.set(edge.targetNodeId, (degree.get(edge.targetNodeId) ?? 0) + 1);
|
|
7367
|
+
}
|
|
7368
|
+
return [...nodes].sort((a, b) => (degree.get(b.id) ?? 0) - (degree.get(a.id) ?? 0));
|
|
7369
|
+
}
|
|
7370
|
+
function paginate(nodes, allEdges, depth, limit, offset) {
|
|
7371
|
+
const totalNodes = nodes.length;
|
|
7372
|
+
const pageNodes = nodes.slice(offset, offset + limit);
|
|
7373
|
+
const pageNodeIds = new Set(pageNodes.map((n) => n.id));
|
|
7374
|
+
const pageEdges = allEdges.filter((e) => pageNodeIds.has(e.sourceNodeId) && pageNodeIds.has(e.targetNodeId));
|
|
7375
|
+
const universeIds = new Set(nodes.map((n) => n.id));
|
|
7376
|
+
return {
|
|
7377
|
+
nodes: pageNodes,
|
|
7378
|
+
edges: pageEdges,
|
|
7379
|
+
depth,
|
|
7380
|
+
totalNodes,
|
|
7381
|
+
totalEdges: allEdges.reduce((count, e) => universeIds.has(e.sourceNodeId) && universeIds.has(e.targetNodeId) ? count + 1 : count, 0),
|
|
7382
|
+
truncated: totalNodes > pageNodes.length,
|
|
7383
|
+
limit,
|
|
7384
|
+
offset
|
|
7385
|
+
};
|
|
7386
|
+
}
|
|
6975
7387
|
function parseAttrs(raw) {
|
|
6976
7388
|
const attrs = {};
|
|
6977
7389
|
const attrRegex = /([A-Za-z_][\w:-]*)="([^"]*)"/g;
|
|
@@ -7108,15 +7520,10 @@ function registerGraphFunction(sdk, kv, provider) {
|
|
|
7108
7520
|
const allNodes = (await kv.list(KV.graphNodes)).filter((n) => !n.stale);
|
|
7109
7521
|
const allEdges = (await kv.list(KV.graphEdges)).filter((e) => !e.stale);
|
|
7110
7522
|
const maxDepth = Math.min(data.maxDepth || 3, 5);
|
|
7523
|
+
const { limit, offset } = resolvePagination(data.limit, data.offset);
|
|
7111
7524
|
if (data.query) {
|
|
7112
7525
|
const lower = data.query.toLowerCase();
|
|
7113
|
-
|
|
7114
|
-
const nodeIds = new Set(matchingNodes.map((n) => n.id));
|
|
7115
|
-
return {
|
|
7116
|
-
nodes: matchingNodes,
|
|
7117
|
-
edges: allEdges.filter((e) => nodeIds.has(e.sourceNodeId) || nodeIds.has(e.targetNodeId)),
|
|
7118
|
-
depth: 0
|
|
7119
|
-
};
|
|
7526
|
+
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);
|
|
7120
7527
|
}
|
|
7121
7528
|
if (data.startNodeId) {
|
|
7122
7529
|
const visited = /* @__PURE__ */ new Set();
|
|
@@ -7148,19 +7555,11 @@ function registerGraphFunction(sdk, kv, provider) {
|
|
|
7148
7555
|
});
|
|
7149
7556
|
}
|
|
7150
7557
|
}
|
|
7151
|
-
return
|
|
7152
|
-
nodes: resultNodes,
|
|
7153
|
-
edges: resultEdges,
|
|
7154
|
-
depth: maxDepth
|
|
7155
|
-
};
|
|
7558
|
+
return paginate(resultNodes, resultEdges, maxDepth, limit, offset);
|
|
7156
7559
|
}
|
|
7157
7560
|
let filtered = allNodes;
|
|
7158
7561
|
if (data.nodeType) filtered = allNodes.filter((n) => n.type === data.nodeType);
|
|
7159
|
-
return
|
|
7160
|
-
nodes: filtered,
|
|
7161
|
-
edges: allEdges,
|
|
7162
|
-
depth: 0
|
|
7163
|
-
};
|
|
7562
|
+
return paginate(rankByDegree(filtered, allEdges), allEdges, 0, limit, offset);
|
|
7164
7563
|
});
|
|
7165
7564
|
sdk.registerFunction("mem::graph-stats", async () => {
|
|
7166
7565
|
const nodes = await kv.list(KV.graphNodes);
|
|
@@ -7177,7 +7576,6 @@ function registerGraphFunction(sdk, kv, provider) {
|
|
|
7177
7576
|
};
|
|
7178
7577
|
});
|
|
7179
7578
|
}
|
|
7180
|
-
|
|
7181
7579
|
//#endregion
|
|
7182
7580
|
//#region src/prompts/consolidation.ts
|
|
7183
7581
|
const SEMANTIC_MERGE_SYSTEM = `You are a memory consolidation engine. Given overlapping episodic memories (session summaries), extract stable factual knowledge.
|
|
@@ -7212,7 +7610,6 @@ Rules:
|
|
|
7212
7610
|
function buildProceduralExtractionPrompt(patterns) {
|
|
7213
7611
|
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")}`;
|
|
7214
7612
|
}
|
|
7215
|
-
|
|
7216
7613
|
//#endregion
|
|
7217
7614
|
//#region src/functions/consolidation-pipeline.ts
|
|
7218
7615
|
function applyDecay(items, decayDays) {
|
|
@@ -7406,7 +7803,6 @@ function registerConsolidationPipelineFunction(sdk, kv, provider) {
|
|
|
7406
7803
|
};
|
|
7407
7804
|
});
|
|
7408
7805
|
}
|
|
7409
|
-
|
|
7410
7806
|
//#endregion
|
|
7411
7807
|
//#region src/functions/team.ts
|
|
7412
7808
|
const VALID_ITEM_TYPES = new Set([
|
|
@@ -7510,7 +7906,6 @@ function registerTeamFunction(sdk, kv, config) {
|
|
|
7510
7906
|
return profile;
|
|
7511
7907
|
});
|
|
7512
7908
|
}
|
|
7513
|
-
|
|
7514
7909
|
//#endregion
|
|
7515
7910
|
//#region src/functions/governance.ts
|
|
7516
7911
|
function registerGovernanceFunction(sdk, kv) {
|
|
@@ -7619,7 +8014,6 @@ function registerGovernanceFunction(sdk, kv) {
|
|
|
7619
8014
|
return queryAudit(kv, data);
|
|
7620
8015
|
});
|
|
7621
8016
|
}
|
|
7622
|
-
|
|
7623
8017
|
//#endregion
|
|
7624
8018
|
//#region src/functions/snapshot.ts
|
|
7625
8019
|
const COMMIT_HASH_RE = /^[0-9a-f]{7,40}$/i;
|
|
@@ -7784,7 +8178,6 @@ function registerSnapshotFunction(sdk, kv, snapshotDir) {
|
|
|
7784
8178
|
}
|
|
7785
8179
|
});
|
|
7786
8180
|
}
|
|
7787
|
-
|
|
7788
8181
|
//#endregion
|
|
7789
8182
|
//#region src/functions/actions.ts
|
|
7790
8183
|
function registerActionsFunction(sdk, kv) {
|
|
@@ -7986,7 +8379,6 @@ async function propagateCompletion(kv, completedActionId) {
|
|
|
7986
8379
|
});
|
|
7987
8380
|
}
|
|
7988
8381
|
}
|
|
7989
|
-
|
|
7990
8382
|
//#endregion
|
|
7991
8383
|
//#region src/functions/frontier.ts
|
|
7992
8384
|
function registerFrontierFunction(sdk, kv) {
|
|
@@ -8091,7 +8483,6 @@ function computeScore(action, edges, now) {
|
|
|
8091
8483
|
if (action.status === "active") score += 15;
|
|
8092
8484
|
return Math.round(score * 100) / 100;
|
|
8093
8485
|
}
|
|
8094
|
-
|
|
8095
8486
|
//#endregion
|
|
8096
8487
|
//#region src/functions/leases.ts
|
|
8097
8488
|
const DEFAULT_LEASE_TTL_MS = 600 * 1e3;
|
|
@@ -8272,7 +8663,6 @@ function registerLeasesFunction(sdk, kv) {
|
|
|
8272
8663
|
};
|
|
8273
8664
|
});
|
|
8274
8665
|
}
|
|
8275
|
-
|
|
8276
8666
|
//#endregion
|
|
8277
8667
|
//#region src/functions/routines.ts
|
|
8278
8668
|
function registerRoutinesFunction(sdk, kv) {
|
|
@@ -8519,7 +8909,6 @@ function registerRoutinesFunction(sdk, kv) {
|
|
|
8519
8909
|
});
|
|
8520
8910
|
});
|
|
8521
8911
|
}
|
|
8522
|
-
|
|
8523
8912
|
//#endregion
|
|
8524
8913
|
//#region src/functions/signals.ts
|
|
8525
8914
|
function registerSignalsFunction(sdk, kv) {
|
|
@@ -8651,7 +9040,6 @@ function registerSignalsFunction(sdk, kv) {
|
|
|
8651
9040
|
};
|
|
8652
9041
|
});
|
|
8653
9042
|
}
|
|
8654
|
-
|
|
8655
9043
|
//#endregion
|
|
8656
9044
|
//#region src/functions/checkpoints.ts
|
|
8657
9045
|
function registerCheckpointsFunction(sdk, kv) {
|
|
@@ -8824,7 +9212,6 @@ function registerCheckpointsFunction(sdk, kv) {
|
|
|
8824
9212
|
};
|
|
8825
9213
|
});
|
|
8826
9214
|
}
|
|
8827
|
-
|
|
8828
9215
|
//#endregion
|
|
8829
9216
|
//#region src/functions/flow-compress.ts
|
|
8830
9217
|
const FLOW_COMPRESS_SYSTEM = `You are a workflow summarizer. Given a completed action chain, produce a concise summary capturing:
|
|
@@ -8969,7 +9356,6 @@ function extractFiles(actions) {
|
|
|
8969
9356
|
}
|
|
8970
9357
|
return Array.from(files);
|
|
8971
9358
|
}
|
|
8972
|
-
|
|
8973
9359
|
//#endregion
|
|
8974
9360
|
//#region src/functions/mesh.ts
|
|
8975
9361
|
function isPrivateIP(ip) {
|
|
@@ -9286,7 +9672,6 @@ async function applySyncData(kv, data, scopes) {
|
|
|
9286
9672
|
if (scopes.includes("graph:edges")) applied += await lwwMergeList(kv, KV.graphEdges, data.graphEdges, "mem:gedge", "createdAt");
|
|
9287
9673
|
return applied;
|
|
9288
9674
|
}
|
|
9289
|
-
|
|
9290
9675
|
//#endregion
|
|
9291
9676
|
//#region src/functions/branch-aware.ts
|
|
9292
9677
|
function execAsync(cmd, args, cwd) {
|
|
@@ -9401,7 +9786,6 @@ function registerBranchAwareFunction(sdk, kv) {
|
|
|
9401
9786
|
};
|
|
9402
9787
|
});
|
|
9403
9788
|
}
|
|
9404
|
-
|
|
9405
9789
|
//#endregion
|
|
9406
9790
|
//#region src/functions/sentinels.ts
|
|
9407
9791
|
const VALID_TYPES = [
|
|
@@ -9722,7 +10106,6 @@ async function unblockLinkedActions(kv, sentinel) {
|
|
|
9722
10106
|
});
|
|
9723
10107
|
return unblockedCount;
|
|
9724
10108
|
}
|
|
9725
|
-
|
|
9726
10109
|
//#endregion
|
|
9727
10110
|
//#region src/functions/sketches.ts
|
|
9728
10111
|
function registerSketchesFunction(sdk, kv) {
|
|
@@ -9964,7 +10347,6 @@ function registerSketchesFunction(sdk, kv) {
|
|
|
9964
10347
|
};
|
|
9965
10348
|
});
|
|
9966
10349
|
}
|
|
9967
|
-
|
|
9968
10350
|
//#endregion
|
|
9969
10351
|
//#region src/functions/crystallize.ts
|
|
9970
10352
|
const CRYSTALLIZE_SYSTEM = `You are summarizing a completed chain of agent actions into a compact digest.
|
|
@@ -10161,7 +10543,6 @@ function parseDigest(response) {
|
|
|
10161
10543
|
};
|
|
10162
10544
|
}
|
|
10163
10545
|
}
|
|
10164
|
-
|
|
10165
10546
|
//#endregion
|
|
10166
10547
|
//#region src/functions/diagnostics.ts
|
|
10167
10548
|
const ALL_CATEGORIES = [
|
|
@@ -10875,7 +11256,6 @@ function registerDiagnosticsFunction(sdk, kv) {
|
|
|
10875
11256
|
};
|
|
10876
11257
|
});
|
|
10877
11258
|
}
|
|
10878
|
-
|
|
10879
11259
|
//#endregion
|
|
10880
11260
|
//#region src/functions/facets.ts
|
|
10881
11261
|
function registerFacetsFunction(sdk, kv) {
|
|
@@ -11047,7 +11427,6 @@ function registerFacetsFunction(sdk, kv) {
|
|
|
11047
11427
|
};
|
|
11048
11428
|
});
|
|
11049
11429
|
}
|
|
11050
|
-
|
|
11051
11430
|
//#endregion
|
|
11052
11431
|
//#region src/functions/verify.ts
|
|
11053
11432
|
function registerVerifyFunction(sdk, kv) {
|
|
@@ -11142,7 +11521,6 @@ async function findObservation(kv, obsId, hintSessionIds) {
|
|
|
11142
11521
|
}
|
|
11143
11522
|
return null;
|
|
11144
11523
|
}
|
|
11145
|
-
|
|
11146
11524
|
//#endregion
|
|
11147
11525
|
//#region src/functions/cascade.ts
|
|
11148
11526
|
function registerCascadeFunction(sdk, kv) {
|
|
@@ -11212,7 +11590,6 @@ function registerCascadeFunction(sdk, kv) {
|
|
|
11212
11590
|
};
|
|
11213
11591
|
});
|
|
11214
11592
|
}
|
|
11215
|
-
|
|
11216
11593
|
//#endregion
|
|
11217
11594
|
//#region src/functions/lessons.ts
|
|
11218
11595
|
function reinforceLesson(lesson) {
|
|
@@ -11398,7 +11775,6 @@ function registerLessonsFunctions(sdk, kv) {
|
|
|
11398
11775
|
};
|
|
11399
11776
|
});
|
|
11400
11777
|
}
|
|
11401
|
-
|
|
11402
11778
|
//#endregion
|
|
11403
11779
|
//#region src/functions/obsidian-export.ts
|
|
11404
11780
|
const DEFAULT_EXPORT_ROOT = join(homedir(), ".agentmemory");
|
|
@@ -11414,6 +11790,20 @@ function resolveVaultDir(vaultDir) {
|
|
|
11414
11790
|
function sanitize(name) {
|
|
11415
11791
|
return name.replace(/[<>:"/\\|?*\x00-\x1f]/g, "_").slice(0, 100);
|
|
11416
11792
|
}
|
|
11793
|
+
function hasExportId(item) {
|
|
11794
|
+
return !!item && typeof item.id === "string" && item.id.length > 0;
|
|
11795
|
+
}
|
|
11796
|
+
function safeArray(value) {
|
|
11797
|
+
return Array.isArray(value) ? value : [];
|
|
11798
|
+
}
|
|
11799
|
+
function safeString(value, fallback = "") {
|
|
11800
|
+
return typeof value === "string" ? value : fallback;
|
|
11801
|
+
}
|
|
11802
|
+
function safeTimestamp(value) {
|
|
11803
|
+
if (typeof value !== "string") return 0;
|
|
11804
|
+
const time = new Date(value).getTime();
|
|
11805
|
+
return Number.isFinite(time) ? time : 0;
|
|
11806
|
+
}
|
|
11417
11807
|
function toFrontmatter(obj) {
|
|
11418
11808
|
const lines = ["---"];
|
|
11419
11809
|
for (const [key, value] of Object.entries(obj)) {
|
|
@@ -11425,6 +11815,11 @@ function toFrontmatter(obj) {
|
|
|
11425
11815
|
return lines.join("\n");
|
|
11426
11816
|
}
|
|
11427
11817
|
function memoryToMd(m) {
|
|
11818
|
+
const concepts = safeArray(m.concepts);
|
|
11819
|
+
const files = safeArray(m.files);
|
|
11820
|
+
const relatedIds = safeArray(m.relatedIds);
|
|
11821
|
+
const supersedes = safeArray(m.supersedes);
|
|
11822
|
+
const title = safeString(m.title, m.id);
|
|
11428
11823
|
const fm = toFrontmatter({
|
|
11429
11824
|
id: m.id,
|
|
11430
11825
|
type: m.type,
|
|
@@ -11432,24 +11827,28 @@ function memoryToMd(m) {
|
|
|
11432
11827
|
updated: m.updatedAt,
|
|
11433
11828
|
strength: m.strength,
|
|
11434
11829
|
version: m.version,
|
|
11435
|
-
concepts
|
|
11436
|
-
files
|
|
11830
|
+
concepts,
|
|
11831
|
+
files
|
|
11437
11832
|
});
|
|
11438
|
-
const
|
|
11439
|
-
const
|
|
11833
|
+
const relatedLines = relatedIds.map((id) => `- [[${id}]]`).join("\n");
|
|
11834
|
+
const supersedesLines = supersedes.map((id) => `- [[${id}]] (superseded)`).join("\n");
|
|
11440
11835
|
const sections = [
|
|
11441
11836
|
fm,
|
|
11442
11837
|
"",
|
|
11443
|
-
`# ${
|
|
11838
|
+
`# ${title}`,
|
|
11444
11839
|
"",
|
|
11445
|
-
m.content
|
|
11840
|
+
safeString(m.content)
|
|
11446
11841
|
];
|
|
11447
|
-
if (
|
|
11448
|
-
if (
|
|
11449
|
-
if (
|
|
11842
|
+
if (concepts.length > 0) sections.push("", "## Concepts", concepts.map((c) => `#${c.replace(/\s+/g, "-")}`).join(" "));
|
|
11843
|
+
if (relatedLines) sections.push("", "## Related", relatedLines);
|
|
11844
|
+
if (supersedesLines) sections.push("", "## Supersedes", supersedesLines);
|
|
11450
11845
|
return sections.join("\n");
|
|
11451
11846
|
}
|
|
11452
11847
|
function lessonToMd(l) {
|
|
11848
|
+
const tags = safeArray(l.tags);
|
|
11849
|
+
const sourceIds = safeArray(l.sourceIds);
|
|
11850
|
+
const content = safeString(l.content);
|
|
11851
|
+
const headline = content ? content.slice(0, 80) : l.id;
|
|
11453
11852
|
const fm = toFrontmatter({
|
|
11454
11853
|
id: l.id,
|
|
11455
11854
|
type: "lesson",
|
|
@@ -11459,66 +11858,76 @@ function lessonToMd(l) {
|
|
|
11459
11858
|
created: l.createdAt,
|
|
11460
11859
|
updated: l.updatedAt,
|
|
11461
11860
|
project: l.project,
|
|
11462
|
-
tags
|
|
11861
|
+
tags,
|
|
11463
11862
|
decayRate: l.decayRate
|
|
11464
11863
|
});
|
|
11465
|
-
const sourceLinks =
|
|
11864
|
+
const sourceLinks = sourceIds.map((id) => `- [[${id}]]`).join("\n");
|
|
11466
11865
|
const sections = [
|
|
11467
11866
|
fm,
|
|
11468
11867
|
"",
|
|
11469
|
-
`# Lesson: ${
|
|
11868
|
+
`# Lesson: ${headline}`,
|
|
11470
11869
|
"",
|
|
11471
|
-
|
|
11870
|
+
content
|
|
11472
11871
|
];
|
|
11473
11872
|
if (l.context) sections.push("", "## Context", l.context);
|
|
11474
|
-
if (
|
|
11873
|
+
if (tags.length > 0) sections.push("", "## Tags", tags.map((t) => `#${t.replace(/\s+/g, "-")}`).join(" "));
|
|
11475
11874
|
if (sourceLinks) sections.push("", "## Sources", sourceLinks);
|
|
11476
11875
|
return sections.join("\n");
|
|
11477
11876
|
}
|
|
11478
11877
|
function crystalToMd(c) {
|
|
11878
|
+
const keyOutcomes = safeArray(c.keyOutcomes);
|
|
11879
|
+
const lessons = safeArray(c.lessons);
|
|
11880
|
+
const filesAffected = safeArray(c.filesAffected);
|
|
11881
|
+
const sourceActionIds = safeArray(c.sourceActionIds);
|
|
11882
|
+
const narrative = safeString(c.narrative);
|
|
11883
|
+
const headline = narrative ? narrative.slice(0, 80) : c.id;
|
|
11479
11884
|
const fm = toFrontmatter({
|
|
11480
11885
|
id: c.id,
|
|
11481
11886
|
type: "crystal",
|
|
11482
11887
|
created: c.createdAt,
|
|
11483
11888
|
project: c.project,
|
|
11484
11889
|
sessionId: c.sessionId,
|
|
11485
|
-
filesAffected
|
|
11890
|
+
filesAffected
|
|
11486
11891
|
});
|
|
11487
|
-
const actionLinks =
|
|
11892
|
+
const actionLinks = sourceActionIds.map((id) => `- [[${id}]]`).join("\n");
|
|
11488
11893
|
const sections = [
|
|
11489
11894
|
fm,
|
|
11490
11895
|
"",
|
|
11491
|
-
`# Crystal: ${
|
|
11896
|
+
`# Crystal: ${headline}`,
|
|
11492
11897
|
"",
|
|
11493
|
-
|
|
11898
|
+
narrative,
|
|
11494
11899
|
"",
|
|
11495
11900
|
"## Key Outcomes",
|
|
11496
|
-
...
|
|
11901
|
+
...keyOutcomes.map((o) => `- ${o}`)
|
|
11497
11902
|
];
|
|
11498
|
-
if (
|
|
11499
|
-
if (
|
|
11903
|
+
if (lessons.length > 0) sections.push("", "## Lessons", ...lessons.map((l) => `- ${l}`));
|
|
11904
|
+
if (filesAffected.length > 0) sections.push("", "## Files", ...filesAffected.map((f) => `- \`${f}\``));
|
|
11500
11905
|
if (actionLinks) sections.push("", "## Source Actions", actionLinks);
|
|
11501
11906
|
return sections.join("\n");
|
|
11502
11907
|
}
|
|
11503
11908
|
function sessionToMd(s) {
|
|
11909
|
+
const project = safeString(s.project, "unknown");
|
|
11910
|
+
const status = safeString(s.status, "unknown");
|
|
11911
|
+
const startedAt = safeString(s.startedAt, "");
|
|
11912
|
+
const cwd = safeString(s.cwd, "");
|
|
11504
11913
|
return [
|
|
11505
11914
|
toFrontmatter({
|
|
11506
11915
|
id: s.id,
|
|
11507
11916
|
type: "session",
|
|
11508
|
-
project
|
|
11509
|
-
status
|
|
11510
|
-
started:
|
|
11917
|
+
project,
|
|
11918
|
+
status,
|
|
11919
|
+
started: startedAt || void 0,
|
|
11511
11920
|
ended: s.endedAt,
|
|
11512
11921
|
observations: s.observationCount
|
|
11513
11922
|
}),
|
|
11514
11923
|
"",
|
|
11515
|
-
`# Session: ${
|
|
11924
|
+
`# Session: ${project}`,
|
|
11516
11925
|
"",
|
|
11517
|
-
`**Status:** ${
|
|
11518
|
-
`**Started:** ${
|
|
11926
|
+
`**Status:** ${status}`,
|
|
11927
|
+
startedAt ? `**Started:** ${startedAt}` : "",
|
|
11519
11928
|
s.endedAt ? `**Ended:** ${s.endedAt}` : "",
|
|
11520
|
-
`**Observations:** ${s.observationCount}`,
|
|
11521
|
-
`**CWD:** \`${
|
|
11929
|
+
`**Observations:** ${s.observationCount ?? 0}`,
|
|
11930
|
+
cwd ? `**CWD:** \`${cwd}\`` : ""
|
|
11522
11931
|
].filter(Boolean).join("\n");
|
|
11523
11932
|
}
|
|
11524
11933
|
function registerObsidianExportFunction(sdk, kv) {
|
|
@@ -11554,122 +11963,131 @@ function registerObsidianExportFunction(sdk, kv) {
|
|
|
11554
11963
|
crystals: join(vaultDir, "crystals"),
|
|
11555
11964
|
sessions: join(vaultDir, "sessions")
|
|
11556
11965
|
};
|
|
11557
|
-
|
|
11558
|
-
|
|
11559
|
-
|
|
11560
|
-
|
|
11561
|
-
|
|
11562
|
-
|
|
11563
|
-
|
|
11564
|
-
|
|
11565
|
-
|
|
11566
|
-
|
|
11567
|
-
|
|
11568
|
-
|
|
11569
|
-
|
|
11570
|
-
|
|
11571
|
-
|
|
11572
|
-
|
|
11573
|
-
|
|
11574
|
-
|
|
11575
|
-
|
|
11576
|
-
const
|
|
11577
|
-
|
|
11578
|
-
|
|
11579
|
-
|
|
11580
|
-
|
|
11581
|
-
|
|
11582
|
-
|
|
11583
|
-
|
|
11584
|
-
|
|
11585
|
-
|
|
11586
|
-
|
|
11587
|
-
|
|
11966
|
+
try {
|
|
11967
|
+
await Promise.all(Object.values(dirs).map((dir) => mkdir(dir, { recursive: true })));
|
|
11968
|
+
const stats = {
|
|
11969
|
+
memories: 0,
|
|
11970
|
+
lessons: 0,
|
|
11971
|
+
crystals: 0,
|
|
11972
|
+
sessions: 0
|
|
11973
|
+
};
|
|
11974
|
+
const errors = [];
|
|
11975
|
+
const memoryMoc = [];
|
|
11976
|
+
const lessonMoc = [];
|
|
11977
|
+
const crystalMoc = [];
|
|
11978
|
+
const sessionMoc = [];
|
|
11979
|
+
const [memories, lessons, crystals, sessions] = await Promise.all([
|
|
11980
|
+
exportTypes.has("memories") ? kv.list(KV.memories) : Promise.resolve([]),
|
|
11981
|
+
exportTypes.has("lessons") ? kv.list(KV.lessons) : Promise.resolve([]),
|
|
11982
|
+
exportTypes.has("crystals") ? kv.list(KV.crystals) : Promise.resolve([]),
|
|
11983
|
+
exportTypes.has("sessions") ? kv.list(KV.sessions) : Promise.resolve([])
|
|
11984
|
+
]);
|
|
11985
|
+
for (const m of memories.filter((m) => hasExportId(m) && m.isLatest === true)) {
|
|
11986
|
+
const filename = `${sanitize(m.id)}.md`;
|
|
11987
|
+
const filepath = join(dirs.memories, filename);
|
|
11988
|
+
try {
|
|
11989
|
+
await writeFile(filepath, memoryToMd(m));
|
|
11990
|
+
stats.memories++;
|
|
11991
|
+
memoryMoc.push(`- [[memories/${sanitize(m.id)}|${safeString(m.title, m.id)}]] (${m.type}, strength: ${m.strength ?? 0})`);
|
|
11992
|
+
} catch (err) {
|
|
11993
|
+
errors.push({
|
|
11994
|
+
id: m.id,
|
|
11995
|
+
path: filepath,
|
|
11996
|
+
error: err instanceof Error ? err.message : String(err)
|
|
11997
|
+
});
|
|
11998
|
+
}
|
|
11588
11999
|
}
|
|
11589
|
-
|
|
11590
|
-
|
|
11591
|
-
|
|
11592
|
-
|
|
11593
|
-
|
|
11594
|
-
|
|
11595
|
-
|
|
11596
|
-
|
|
11597
|
-
|
|
11598
|
-
|
|
11599
|
-
|
|
11600
|
-
|
|
11601
|
-
|
|
11602
|
-
|
|
12000
|
+
for (const l of lessons.filter((l) => hasExportId(l) && !l.deleted)) {
|
|
12001
|
+
const filename = `${sanitize(l.id)}.md`;
|
|
12002
|
+
const filepath = join(dirs.lessons, filename);
|
|
12003
|
+
try {
|
|
12004
|
+
await writeFile(filepath, lessonToMd(l));
|
|
12005
|
+
stats.lessons++;
|
|
12006
|
+
const headline = safeString(l.content).slice(0, 60) || l.id;
|
|
12007
|
+
lessonMoc.push(`- [[lessons/${sanitize(l.id)}|${headline}]] (confidence: ${l.confidence ?? 0})`);
|
|
12008
|
+
} catch (err) {
|
|
12009
|
+
errors.push({
|
|
12010
|
+
id: l.id,
|
|
12011
|
+
path: filepath,
|
|
12012
|
+
error: err instanceof Error ? err.message : String(err)
|
|
12013
|
+
});
|
|
12014
|
+
}
|
|
11603
12015
|
}
|
|
11604
|
-
|
|
11605
|
-
|
|
11606
|
-
|
|
11607
|
-
|
|
11608
|
-
|
|
11609
|
-
|
|
11610
|
-
|
|
11611
|
-
|
|
11612
|
-
|
|
11613
|
-
|
|
11614
|
-
|
|
11615
|
-
|
|
11616
|
-
|
|
11617
|
-
|
|
12016
|
+
for (const c of crystals.filter(hasExportId)) {
|
|
12017
|
+
const filename = `${sanitize(c.id)}.md`;
|
|
12018
|
+
const filepath = join(dirs.crystals, filename);
|
|
12019
|
+
try {
|
|
12020
|
+
await writeFile(filepath, crystalToMd(c));
|
|
12021
|
+
stats.crystals++;
|
|
12022
|
+
const headline = safeString(c.narrative).slice(0, 60) || c.id;
|
|
12023
|
+
crystalMoc.push(`- [[crystals/${sanitize(c.id)}|${headline}]]`);
|
|
12024
|
+
} catch (err) {
|
|
12025
|
+
errors.push({
|
|
12026
|
+
id: c.id,
|
|
12027
|
+
path: filepath,
|
|
12028
|
+
error: err instanceof Error ? err.message : String(err)
|
|
12029
|
+
});
|
|
12030
|
+
}
|
|
11618
12031
|
}
|
|
11619
|
-
|
|
11620
|
-
|
|
11621
|
-
|
|
11622
|
-
|
|
11623
|
-
|
|
11624
|
-
|
|
11625
|
-
|
|
11626
|
-
|
|
11627
|
-
|
|
11628
|
-
|
|
11629
|
-
|
|
11630
|
-
|
|
11631
|
-
|
|
11632
|
-
|
|
11633
|
-
}
|
|
12032
|
+
const recent = sessions.filter(hasExportId).sort((a, b) => safeTimestamp(b.startedAt) - safeTimestamp(a.startedAt)).slice(0, 50);
|
|
12033
|
+
for (const s of recent) {
|
|
12034
|
+
const filename = `${sanitize(s.id)}.md`;
|
|
12035
|
+
const filepath = join(dirs.sessions, filename);
|
|
12036
|
+
try {
|
|
12037
|
+
await writeFile(filepath, sessionToMd(s));
|
|
12038
|
+
stats.sessions++;
|
|
12039
|
+
sessionMoc.push(`- [[sessions/${sanitize(s.id)}|${safeString(s.project, "unknown")} (${safeString(s.status, "unknown")})]]`);
|
|
12040
|
+
} catch (err) {
|
|
12041
|
+
errors.push({
|
|
12042
|
+
id: s.id,
|
|
12043
|
+
path: filepath,
|
|
12044
|
+
error: err instanceof Error ? err.message : String(err)
|
|
12045
|
+
});
|
|
12046
|
+
}
|
|
11634
12047
|
}
|
|
12048
|
+
const exportedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
12049
|
+
const moc = [
|
|
12050
|
+
"---",
|
|
12051
|
+
"type: moc",
|
|
12052
|
+
`exported: ${exportedAt}`,
|
|
12053
|
+
"---",
|
|
12054
|
+
"",
|
|
12055
|
+
"# agentmemory vault",
|
|
12056
|
+
"",
|
|
12057
|
+
`Exported: ${exportedAt}`,
|
|
12058
|
+
"",
|
|
12059
|
+
`## Memories (${stats.memories})`,
|
|
12060
|
+
...memoryMoc,
|
|
12061
|
+
"",
|
|
12062
|
+
`## Lessons (${stats.lessons})`,
|
|
12063
|
+
...lessonMoc,
|
|
12064
|
+
"",
|
|
12065
|
+
`## Crystals (${stats.crystals})`,
|
|
12066
|
+
...crystalMoc,
|
|
12067
|
+
"",
|
|
12068
|
+
`## Sessions (${stats.sessions})`,
|
|
12069
|
+
...sessionMoc
|
|
12070
|
+
].join("\n");
|
|
12071
|
+
await writeFile(join(vaultDir, "MOC.md"), moc);
|
|
12072
|
+
await recordAudit(kv, "obsidian_export", "mem::obsidian-export", [], {
|
|
12073
|
+
vaultDir,
|
|
12074
|
+
stats
|
|
12075
|
+
});
|
|
12076
|
+
return {
|
|
12077
|
+
success: true,
|
|
12078
|
+
exported: stats,
|
|
12079
|
+
errors: errors.length > 0 ? errors : void 0,
|
|
12080
|
+
vaultDir
|
|
12081
|
+
};
|
|
12082
|
+
} catch (err) {
|
|
12083
|
+
return {
|
|
12084
|
+
success: false,
|
|
12085
|
+
error: err instanceof Error ? err.message : String(err),
|
|
12086
|
+
vaultDir
|
|
12087
|
+
};
|
|
11635
12088
|
}
|
|
11636
|
-
const exportedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
11637
|
-
const moc = [
|
|
11638
|
-
"---",
|
|
11639
|
-
"type: moc",
|
|
11640
|
-
`exported: ${exportedAt}`,
|
|
11641
|
-
"---",
|
|
11642
|
-
"",
|
|
11643
|
-
"# agentmemory vault",
|
|
11644
|
-
"",
|
|
11645
|
-
`Exported: ${exportedAt}`,
|
|
11646
|
-
"",
|
|
11647
|
-
`## Memories (${stats.memories})`,
|
|
11648
|
-
...memoryMoc,
|
|
11649
|
-
"",
|
|
11650
|
-
`## Lessons (${stats.lessons})`,
|
|
11651
|
-
...lessonMoc,
|
|
11652
|
-
"",
|
|
11653
|
-
`## Crystals (${stats.crystals})`,
|
|
11654
|
-
...crystalMoc,
|
|
11655
|
-
"",
|
|
11656
|
-
`## Sessions (${stats.sessions})`,
|
|
11657
|
-
...sessionMoc
|
|
11658
|
-
].join("\n");
|
|
11659
|
-
await writeFile(join(vaultDir, "MOC.md"), moc);
|
|
11660
|
-
await recordAudit(kv, "obsidian_export", "mem::obsidian-export", [], {
|
|
11661
|
-
vaultDir,
|
|
11662
|
-
stats
|
|
11663
|
-
});
|
|
11664
|
-
return {
|
|
11665
|
-
success: true,
|
|
11666
|
-
exported: stats,
|
|
11667
|
-
errors: errors.length > 0 ? errors : void 0,
|
|
11668
|
-
vaultDir
|
|
11669
|
-
};
|
|
11670
12089
|
});
|
|
11671
12090
|
}
|
|
11672
|
-
|
|
11673
12091
|
//#endregion
|
|
11674
12092
|
//#region src/prompts/reflect.ts
|
|
11675
12093
|
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.
|
|
@@ -11697,7 +12115,6 @@ function buildReflectPrompt(cluster) {
|
|
|
11697
12115
|
if (cluster.crystalNarratives.length > 0) sections.push("\n## Completed Work Summaries", ...cluster.crystalNarratives.map((n) => `- ${n}`));
|
|
11698
12116
|
return `Synthesize higher-order insights from this cluster of related memories:\n\n${sections.join("\n")}`;
|
|
11699
12117
|
}
|
|
11700
|
-
|
|
11701
12118
|
//#endregion
|
|
11702
12119
|
//#region src/functions/reflect.ts
|
|
11703
12120
|
function reinforceInsight(insight) {
|
|
@@ -11990,7 +12407,6 @@ function registerReflectFunctions(sdk, kv, provider) {
|
|
|
11990
12407
|
};
|
|
11991
12408
|
});
|
|
11992
12409
|
}
|
|
11993
|
-
|
|
11994
12410
|
//#endregion
|
|
11995
12411
|
//#region src/functions/working-memory.ts
|
|
11996
12412
|
const CORE_SCOPE = "mem:core-memory";
|
|
@@ -12165,7 +12581,6 @@ function registerWorkingMemoryFunctions(sdk, kv, tokenBudget) {
|
|
|
12165
12581
|
};
|
|
12166
12582
|
});
|
|
12167
12583
|
}
|
|
12168
|
-
|
|
12169
12584
|
//#endregion
|
|
12170
12585
|
//#region src/functions/skill-extract.ts
|
|
12171
12586
|
const SKILL_EXTRACT_SYSTEM = `You are a skill extraction engine. Given a completed multi-step task session, extract a reusable procedural skill document.
|
|
@@ -12368,7 +12783,6 @@ function registerSkillExtractFunctions(sdk, kv, provider) {
|
|
|
12368
12783
|
};
|
|
12369
12784
|
});
|
|
12370
12785
|
}
|
|
12371
|
-
|
|
12372
12786
|
//#endregion
|
|
12373
12787
|
//#region src/functions/sliding-window.ts
|
|
12374
12788
|
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.
|
|
@@ -12555,7 +12969,6 @@ function registerSlidingWindowFunction(sdk, kv, provider) {
|
|
|
12555
12969
|
};
|
|
12556
12970
|
});
|
|
12557
12971
|
}
|
|
12558
|
-
|
|
12559
12972
|
//#endregion
|
|
12560
12973
|
//#region src/functions/temporal-graph.ts
|
|
12561
12974
|
const TEMPORAL_EXTRACTION_SYSTEM = `You are a temporal knowledge extraction engine. Given observations, extract entities AND their temporal relationships with full context metadata.
|
|
@@ -12819,7 +13232,6 @@ function buildTimeline(edges) {
|
|
|
12819
13232
|
context: e.context
|
|
12820
13233
|
}));
|
|
12821
13234
|
}
|
|
12822
|
-
|
|
12823
13235
|
//#endregion
|
|
12824
13236
|
//#region src/functions/retention.ts
|
|
12825
13237
|
const DEFAULT_DECAY = {
|
|
@@ -12983,7 +13395,7 @@ function registerRetentionFunctions(sdk, kv) {
|
|
|
12983
13395
|
const threshold = typeof data?.threshold === "number" && Number.isFinite(data.threshold) ? data.threshold : DEFAULT_DECAY.tierThresholds.cold;
|
|
12984
13396
|
const maxEvictRaw = typeof data?.maxEvict === "number" && Number.isInteger(data.maxEvict) ? data.maxEvict : 50;
|
|
12985
13397
|
const maxEvict = Math.min(1e3, Math.max(0, maxEvictRaw));
|
|
12986
|
-
const { decrementImageRef } = await import("./image-refs-
|
|
13398
|
+
const { decrementImageRef } = await import("./image-refs-C7h9L5wx.mjs").then((n) => n.n);
|
|
12987
13399
|
const candidates = (await kv.list(KV.retentionScores)).filter((s) => s.score < threshold).sort((a, b) => a.score - b.score).slice(0, maxEvict);
|
|
12988
13400
|
if (data?.dryRun) return {
|
|
12989
13401
|
success: true,
|
|
@@ -13053,7 +13465,6 @@ function registerRetentionFunctions(sdk, kv) {
|
|
|
13053
13465
|
};
|
|
13054
13466
|
});
|
|
13055
13467
|
}
|
|
13056
|
-
|
|
13057
13468
|
//#endregion
|
|
13058
13469
|
//#region src/functions/compress-file.ts
|
|
13059
13470
|
const SENSITIVE_PATH_TERMS = [
|
|
@@ -13195,7 +13606,6 @@ function registerCompressFileFunction(sdk, kv, provider) {
|
|
|
13195
13606
|
};
|
|
13196
13607
|
});
|
|
13197
13608
|
}
|
|
13198
|
-
|
|
13199
13609
|
//#endregion
|
|
13200
13610
|
//#region src/replay/jsonl-parser.ts
|
|
13201
13611
|
function deriveProject(cwd) {
|
|
@@ -13322,7 +13732,6 @@ function parseJsonlText(text, fallbackSessionId) {
|
|
|
13322
13732
|
observations
|
|
13323
13733
|
};
|
|
13324
13734
|
}
|
|
13325
|
-
|
|
13326
13735
|
//#endregion
|
|
13327
13736
|
//#region src/replay/timeline.ts
|
|
13328
13737
|
const DEFAULT_CHARS_PER_SEC = 40;
|
|
@@ -13417,10 +13826,6 @@ function projectTimeline(observations) {
|
|
|
13417
13826
|
events
|
|
13418
13827
|
};
|
|
13419
13828
|
}
|
|
13420
|
-
|
|
13421
|
-
//#endregion
|
|
13422
|
-
//#region src/functions/replay.ts
|
|
13423
|
-
const MAX_FILES_DEFAULT = 200;
|
|
13424
13829
|
const MAX_FILES_UPPER_BOUND = 1e3;
|
|
13425
13830
|
const SENSITIVE_PATH_PATTERNS = [
|
|
13426
13831
|
/(^|[\\/_.-])secret([\\/_.-]|s?$)/i,
|
|
@@ -13640,7 +14045,7 @@ function registerReplayFunctions(sdk, kv) {
|
|
|
13640
14045
|
error: "path not found"
|
|
13641
14046
|
};
|
|
13642
14047
|
}
|
|
13643
|
-
const maxFiles = Number.isInteger(data.maxFiles) && data.maxFiles > 0 ? Math.min(data.maxFiles, MAX_FILES_UPPER_BOUND) :
|
|
14048
|
+
const maxFiles = Number.isInteger(data.maxFiles) && data.maxFiles > 0 ? Math.min(data.maxFiles, MAX_FILES_UPPER_BOUND) : 200;
|
|
13644
14049
|
let files = [];
|
|
13645
14050
|
let truncated = false;
|
|
13646
14051
|
let discovered = 0;
|
|
@@ -13696,7 +14101,8 @@ function registerReplayFunctions(sdk, kv) {
|
|
|
13696
14101
|
const existingTags = existing.tags || [];
|
|
13697
14102
|
if (!existingTags.includes("jsonl-import")) existing.tags = [...existingTags, "jsonl-import"];
|
|
13698
14103
|
if (!existing.firstPrompt && firstPrompt) existing.firstPrompt = firstPrompt;
|
|
13699
|
-
|
|
14104
|
+
if (!existing.id) existing.id = parsed.sessionId;
|
|
14105
|
+
await kv.set(KV.sessions, parsed.sessionId, existing);
|
|
13700
14106
|
} else {
|
|
13701
14107
|
const session = {
|
|
13702
14108
|
id: parsed.sessionId,
|
|
@@ -13742,7 +14148,6 @@ function registerReplayFunctions(sdk, kv) {
|
|
|
13742
14148
|
};
|
|
13743
14149
|
});
|
|
13744
14150
|
}
|
|
13745
|
-
|
|
13746
14151
|
//#endregion
|
|
13747
14152
|
//#region src/health/thresholds.ts
|
|
13748
14153
|
const DEFAULTS = {
|
|
@@ -13801,7 +14206,6 @@ function evaluateHealth(snapshot, config = {}) {
|
|
|
13801
14206
|
notes
|
|
13802
14207
|
};
|
|
13803
14208
|
}
|
|
13804
|
-
|
|
13805
14209
|
//#endregion
|
|
13806
14210
|
//#region src/health/monitor.ts
|
|
13807
14211
|
function registerHealthMonitor(sdk, kv) {
|
|
@@ -13889,7 +14293,6 @@ function registerHealthMonitor(sdk, kv) {
|
|
|
13889
14293
|
async function getLatestHealth(kv) {
|
|
13890
14294
|
return kv.get(KV.health, "latest");
|
|
13891
14295
|
}
|
|
13892
|
-
|
|
13893
14296
|
//#endregion
|
|
13894
14297
|
//#region src/auth.ts
|
|
13895
14298
|
const hmacKey = randomBytes(32);
|
|
@@ -13915,7 +14318,6 @@ function buildViewerCsp(nonce) {
|
|
|
13915
14318
|
"font-src 'self'"
|
|
13916
14319
|
].join("; ");
|
|
13917
14320
|
}
|
|
13918
|
-
|
|
13919
14321
|
//#endregion
|
|
13920
14322
|
//#region src/viewer/document.ts
|
|
13921
14323
|
const VIEWER_VERSION_PLACEHOLDER = "__AGENTMEMORY_VERSION__";
|
|
@@ -13941,7 +14343,6 @@ function renderViewerDocument() {
|
|
|
13941
14343
|
csp: buildViewerCsp(nonce)
|
|
13942
14344
|
};
|
|
13943
14345
|
}
|
|
13944
|
-
|
|
13945
14346
|
//#endregion
|
|
13946
14347
|
//#region src/viewer/server.ts
|
|
13947
14348
|
function loadViewerFavicon() {
|
|
@@ -13956,18 +14357,30 @@ function loadViewerFavicon() {
|
|
|
13956
14357
|
} catch {}
|
|
13957
14358
|
return null;
|
|
13958
14359
|
}
|
|
14360
|
+
const VIEWER_FAVICON = loadViewerFavicon();
|
|
13959
14361
|
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());
|
|
13960
|
-
|
|
13961
|
-
|
|
14362
|
+
function readAllowedHostsOverride() {
|
|
14363
|
+
return (process.env.VIEWER_ALLOWED_HOSTS || "").split(",").map((h) => h.trim().toLowerCase()).filter(Boolean);
|
|
14364
|
+
}
|
|
14365
|
+
function resolveViewerHost() {
|
|
14366
|
+
return process.env.AGENTMEMORY_VIEWER_HOST?.trim() || "127.0.0.1";
|
|
14367
|
+
}
|
|
14368
|
+
function isLoopbackHost(host) {
|
|
14369
|
+
const h = host.trim().toLowerCase();
|
|
14370
|
+
return h === "127.0.0.1" || h === "::1" || h === "localhost";
|
|
14371
|
+
}
|
|
14372
|
+
function buildAllowedHosts(origins, listenPort, bindHost = "127.0.0.1") {
|
|
13962
14373
|
const hosts = /* @__PURE__ */ new Set();
|
|
13963
|
-
|
|
13964
|
-
const
|
|
13965
|
-
|
|
13966
|
-
|
|
13967
|
-
|
|
13968
|
-
|
|
13969
|
-
|
|
13970
|
-
|
|
14374
|
+
if (isLoopbackHost(bindHost)) {
|
|
14375
|
+
for (const o of origins) try {
|
|
14376
|
+
const parsed = new URL(o);
|
|
14377
|
+
if (parsed.host) hosts.add(parsed.host.toLowerCase());
|
|
14378
|
+
} catch {}
|
|
14379
|
+
hosts.add(`localhost:${listenPort}`);
|
|
14380
|
+
hosts.add(`127.0.0.1:${listenPort}`);
|
|
14381
|
+
hosts.add(`[::1]:${listenPort}`);
|
|
14382
|
+
}
|
|
14383
|
+
for (const h of readAllowedHostsOverride()) hosts.add(h);
|
|
13971
14384
|
return hosts;
|
|
13972
14385
|
}
|
|
13973
14386
|
function isHostAllowed(headerHost, allowed) {
|
|
@@ -13976,11 +14389,16 @@ function isHostAllowed(headerHost, allowed) {
|
|
|
13976
14389
|
if (!lower) return false;
|
|
13977
14390
|
return allowed.has(lower);
|
|
13978
14391
|
}
|
|
14392
|
+
function requireInboundBearer(authHeader, secret) {
|
|
14393
|
+
if (typeof authHeader !== "string") return false;
|
|
14394
|
+
const match = /^Bearer\s+(\S+)\s*$/i.exec(authHeader);
|
|
14395
|
+
if (!match) return false;
|
|
14396
|
+
return timingSafeCompare(match[1], secret);
|
|
14397
|
+
}
|
|
13979
14398
|
function corsHeaders(req) {
|
|
13980
14399
|
const origin = req.headers.origin || "";
|
|
13981
|
-
const allowed = ALLOWED_ORIGINS.includes(origin) ? origin : ALLOWED_ORIGINS[0];
|
|
13982
14400
|
return {
|
|
13983
|
-
"Access-Control-Allow-Origin":
|
|
14401
|
+
"Access-Control-Allow-Origin": ALLOWED_ORIGINS.includes(origin) ? origin : ALLOWED_ORIGINS[0],
|
|
13984
14402
|
"Access-Control-Allow-Methods": "GET, POST, DELETE, OPTIONS",
|
|
13985
14403
|
"Access-Control-Allow-Headers": "Content-Type, Authorization",
|
|
13986
14404
|
Vary: "Origin"
|
|
@@ -14024,16 +14442,29 @@ function getBoundViewerPort() {
|
|
|
14024
14442
|
function getViewerSkipped() {
|
|
14025
14443
|
return viewerSkipped;
|
|
14026
14444
|
}
|
|
14445
|
+
var ViewerConfigError = class extends Error {
|
|
14446
|
+
constructor(message) {
|
|
14447
|
+
super(message);
|
|
14448
|
+
this.name = "ViewerConfigError";
|
|
14449
|
+
}
|
|
14450
|
+
};
|
|
14027
14451
|
function startViewerServer(port, _kv, _sdk, secret, restPort) {
|
|
14028
14452
|
boundViewerPort = null;
|
|
14029
14453
|
viewerSkipped = false;
|
|
14030
14454
|
const resolvedRestPort = restPort ?? port - 2;
|
|
14031
14455
|
const requestedPort = port;
|
|
14456
|
+
const host = resolveViewerHost();
|
|
14457
|
+
let inboundSecret = null;
|
|
14458
|
+
if (!isLoopbackHost(host)) {
|
|
14459
|
+
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.`);
|
|
14460
|
+
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.`);
|
|
14461
|
+
inboundSecret = secret;
|
|
14462
|
+
}
|
|
14032
14463
|
let allowedHosts = null;
|
|
14033
14464
|
const server = createServer(async (req, res) => {
|
|
14034
14465
|
if (!allowedHosts) {
|
|
14035
14466
|
const addr = server.address();
|
|
14036
|
-
allowedHosts = buildAllowedHosts(ALLOWED_ORIGINS, addr && typeof addr === "object" && "port" in addr ? addr.port : port);
|
|
14467
|
+
allowedHosts = buildAllowedHosts(ALLOWED_ORIGINS, addr && typeof addr === "object" && "port" in addr ? addr.port : port, host);
|
|
14037
14468
|
}
|
|
14038
14469
|
if (!isHostAllowed(req.headers.host, allowedHosts)) {
|
|
14039
14470
|
res.writeHead(403, { "Content-Type": "text/plain" });
|
|
@@ -14069,19 +14500,26 @@ function startViewerServer(port, _kv, _sdk, secret, restPort) {
|
|
|
14069
14500
|
return;
|
|
14070
14501
|
}
|
|
14071
14502
|
if (method === "GET" && pathname === "/favicon.svg") {
|
|
14072
|
-
|
|
14073
|
-
if (favicon) {
|
|
14503
|
+
if (VIEWER_FAVICON) {
|
|
14074
14504
|
res.writeHead(200, {
|
|
14075
14505
|
"Content-Type": "image/svg+xml",
|
|
14076
14506
|
"Cache-Control": "public, max-age=3600"
|
|
14077
14507
|
});
|
|
14078
|
-
res.end(
|
|
14508
|
+
res.end(VIEWER_FAVICON);
|
|
14079
14509
|
return;
|
|
14080
14510
|
}
|
|
14081
14511
|
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
14082
14512
|
res.end("favicon not found");
|
|
14083
14513
|
return;
|
|
14084
14514
|
}
|
|
14515
|
+
if (inboundSecret !== null && !requireInboundBearer(req.headers.authorization, inboundSecret)) {
|
|
14516
|
+
res.writeHead(401, {
|
|
14517
|
+
"Content-Type": "text/plain",
|
|
14518
|
+
"WWW-Authenticate": "Bearer realm=\"agentmemory-viewer\""
|
|
14519
|
+
});
|
|
14520
|
+
res.end("unauthorized");
|
|
14521
|
+
return;
|
|
14522
|
+
}
|
|
14085
14523
|
try {
|
|
14086
14524
|
await proxyToRestApi(resolvedRestPort, pathname, qs, method, req, res, secret);
|
|
14087
14525
|
} catch (err) {
|
|
@@ -14092,17 +14530,23 @@ function startViewerServer(port, _kv, _sdk, secret, restPort) {
|
|
|
14092
14530
|
let attempt = 0;
|
|
14093
14531
|
let currentPort = requestedPort;
|
|
14094
14532
|
const tryListen = () => {
|
|
14095
|
-
server.listen(currentPort,
|
|
14533
|
+
server.listen(currentPort, host);
|
|
14096
14534
|
};
|
|
14097
14535
|
server.on("listening", () => {
|
|
14098
14536
|
const addr = server.address();
|
|
14099
|
-
|
|
14537
|
+
const actualPort = addr && typeof addr === "object" && "port" in addr ? addr.port : currentPort;
|
|
14538
|
+
boundViewerPort = actualPort;
|
|
14100
14539
|
viewerSkipped = false;
|
|
14101
|
-
if (
|
|
14102
|
-
|
|
14540
|
+
if (inboundSecret !== null) {
|
|
14541
|
+
const allowedHosts = readAllowedHostsOverride().join(", ");
|
|
14542
|
+
console.log(`[agentmemory] Viewer: http://localhost:${actualPort} (bound to ${host}; inbound Bearer required; allowed Host headers: ${allowedHosts})`);
|
|
14543
|
+
return;
|
|
14544
|
+
}
|
|
14545
|
+
if (actualPort === requestedPort) console.log(`[agentmemory] Viewer: http://localhost:${actualPort}`);
|
|
14546
|
+
else console.log(`[agentmemory] Viewer started on http://localhost:${actualPort} (fallback from ${requestedPort})`);
|
|
14103
14547
|
});
|
|
14104
14548
|
server.on("error", (err) => {
|
|
14105
|
-
if (err.code === "EADDRINUSE" && attempt < MAX_VIEWER_PORT_RETRIES) {
|
|
14549
|
+
if (err.code === "EADDRINUSE" && inboundSecret === null && attempt < MAX_VIEWER_PORT_RETRIES) {
|
|
14106
14550
|
attempt++;
|
|
14107
14551
|
currentPort = requestedPort + attempt;
|
|
14108
14552
|
setImmediate(tryListen);
|
|
@@ -14111,7 +14555,8 @@ function startViewerServer(port, _kv, _sdk, secret, restPort) {
|
|
|
14111
14555
|
if (err.code === "EADDRINUSE") {
|
|
14112
14556
|
boundViewerPort = null;
|
|
14113
14557
|
viewerSkipped = true;
|
|
14114
|
-
console.warn(`[agentmemory] Viewer
|
|
14558
|
+
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.`);
|
|
14559
|
+
else console.warn(`[agentmemory] Viewer ports ${requestedPort}-${requestedPort + MAX_VIEWER_PORT_RETRIES} all in use, skipping viewer.`);
|
|
14115
14560
|
} else {
|
|
14116
14561
|
boundViewerPort = null;
|
|
14117
14562
|
viewerSkipped = true;
|
|
@@ -14156,7 +14601,6 @@ async function proxyToRestApi(restPort, pathname, qs, method, req, res, secret)
|
|
|
14156
14601
|
res.writeHead(upstream.status, responseHeaders);
|
|
14157
14602
|
res.end(responseBody);
|
|
14158
14603
|
}
|
|
14159
|
-
|
|
14160
14604
|
//#endregion
|
|
14161
14605
|
//#region src/triggers/api.ts
|
|
14162
14606
|
function parseOptionalInt(raw) {
|
|
@@ -14579,7 +15023,7 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
14579
15023
|
}
|
|
14580
15024
|
if (body.maxFiles !== void 0) {
|
|
14581
15025
|
const n = body.maxFiles;
|
|
14582
|
-
if (!Number.isInteger(n) || n < 1 || n >
|
|
15026
|
+
if (!Number.isInteger(n) || n < 1 || n > 1e3) return {
|
|
14583
15027
|
status_code: 400,
|
|
14584
15028
|
body: { error: `maxFiles must be an integer between 1 and ${MAX_FILES_UPPER_BOUND}` }
|
|
14585
15029
|
};
|
|
@@ -14663,9 +15107,13 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
14663
15107
|
value: "completed"
|
|
14664
15108
|
}]);
|
|
14665
15109
|
try {
|
|
14666
|
-
sdk.
|
|
15110
|
+
sdk.trigger({
|
|
15111
|
+
function_id: "event::session::stopped",
|
|
15112
|
+
payload: { sessionId },
|
|
15113
|
+
action: TriggerAction.Void()
|
|
15114
|
+
});
|
|
14667
15115
|
} catch (err) {
|
|
14668
|
-
logger.warn("event::session::stopped
|
|
15116
|
+
logger.warn("event::session::stopped trigger failed", {
|
|
14669
15117
|
sessionId,
|
|
14670
15118
|
error: err instanceof Error ? err.message : String(err)
|
|
14671
15119
|
});
|
|
@@ -15085,11 +15533,24 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
15085
15533
|
status_code: 400,
|
|
15086
15534
|
body: { error: "query or expandIds is required" }
|
|
15087
15535
|
};
|
|
15536
|
+
const headers = req.headers || {};
|
|
15537
|
+
const sourceHeader = headers["x-agentmemory-source"] ?? headers["X-Agentmemory-Source"];
|
|
15538
|
+
const sourceFromHeader = Array.isArray(sourceHeader) ? sourceHeader[0] : sourceHeader;
|
|
15539
|
+
const payload = {
|
|
15540
|
+
query: req.body?.query,
|
|
15541
|
+
expandIds: req.body?.expandIds,
|
|
15542
|
+
limit: req.body?.limit,
|
|
15543
|
+
project: req.body?.project,
|
|
15544
|
+
includeLessons: req.body?.includeLessons,
|
|
15545
|
+
agentId: req.body?.agentId,
|
|
15546
|
+
sessionId: req.body?.sessionId,
|
|
15547
|
+
source: req.body?.source ?? sourceFromHeader
|
|
15548
|
+
};
|
|
15088
15549
|
return {
|
|
15089
15550
|
status_code: 200,
|
|
15090
15551
|
body: await sdk.trigger({
|
|
15091
15552
|
function_id: "mem::smart-search",
|
|
15092
|
-
payload
|
|
15553
|
+
payload
|
|
15093
15554
|
})
|
|
15094
15555
|
};
|
|
15095
15556
|
});
|
|
@@ -15101,6 +15562,29 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
15101
15562
|
http_method: "POST"
|
|
15102
15563
|
}
|
|
15103
15564
|
});
|
|
15565
|
+
sdk.registerFunction("api::diagnostic-followup", async (req) => {
|
|
15566
|
+
const authErr = checkAuth(req, secret);
|
|
15567
|
+
if (authErr) return authErr;
|
|
15568
|
+
return {
|
|
15569
|
+
status_code: 200,
|
|
15570
|
+
body: {
|
|
15571
|
+
...await sdk.trigger({
|
|
15572
|
+
function_id: "mem::diagnostic::followup-stats",
|
|
15573
|
+
payload: {}
|
|
15574
|
+
}),
|
|
15575
|
+
caveat: "Directional signal: overcounts on legitimate query refinement. Tune via AGENTMEMORY_FOLLOWUP_WINDOW_SECONDS."
|
|
15576
|
+
}
|
|
15577
|
+
};
|
|
15578
|
+
});
|
|
15579
|
+
sdk.registerTrigger({
|
|
15580
|
+
type: "http",
|
|
15581
|
+
function_id: "api::diagnostic-followup",
|
|
15582
|
+
config: {
|
|
15583
|
+
api_path: "/agentmemory/diagnostics/followup",
|
|
15584
|
+
http_method: "GET",
|
|
15585
|
+
middleware_function_ids: ["middleware::api-auth"]
|
|
15586
|
+
}
|
|
15587
|
+
});
|
|
15104
15588
|
sdk.registerFunction("api::timeline", async (req) => {
|
|
15105
15589
|
const authErr = checkAuth(req, secret);
|
|
15106
15590
|
if (authErr) return authErr;
|
|
@@ -15322,12 +15806,20 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
15322
15806
|
sdk.registerFunction("api::graph-query", async (req) => {
|
|
15323
15807
|
const authErr = checkAuth(req, secret);
|
|
15324
15808
|
if (authErr) return authErr;
|
|
15809
|
+
const payload = {
|
|
15810
|
+
startNodeId: req.body?.startNodeId,
|
|
15811
|
+
nodeType: req.body?.nodeType,
|
|
15812
|
+
maxDepth: req.body?.maxDepth,
|
|
15813
|
+
query: req.body?.query,
|
|
15814
|
+
limit: req.body?.limit,
|
|
15815
|
+
offset: req.body?.offset
|
|
15816
|
+
};
|
|
15325
15817
|
try {
|
|
15326
15818
|
return {
|
|
15327
15819
|
status_code: 200,
|
|
15328
15820
|
body: await sdk.trigger({
|
|
15329
15821
|
function_id: "mem::graph-query",
|
|
15330
|
-
payload
|
|
15822
|
+
payload
|
|
15331
15823
|
})
|
|
15332
15824
|
};
|
|
15333
15825
|
} catch {
|
|
@@ -17608,7 +18100,6 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
17608
18100
|
}
|
|
17609
18101
|
});
|
|
17610
18102
|
}
|
|
17611
|
-
|
|
17612
18103
|
//#endregion
|
|
17613
18104
|
//#region src/triggers/events.ts
|
|
17614
18105
|
function registerEventTriggers(sdk, kv) {
|
|
@@ -17653,18 +18144,26 @@ function registerEventTriggers(sdk, kv) {
|
|
|
17653
18144
|
payload: data
|
|
17654
18145
|
});
|
|
17655
18146
|
if (isReflectEnabled()) try {
|
|
17656
|
-
sdk.
|
|
18147
|
+
sdk.trigger({
|
|
18148
|
+
function_id: "mem::slot-reflect",
|
|
18149
|
+
payload: { sessionId: data.sessionId },
|
|
18150
|
+
action: TriggerAction.Void()
|
|
18151
|
+
});
|
|
17657
18152
|
} catch (err) {
|
|
17658
|
-
logger.warn("slot-reflect
|
|
18153
|
+
logger.warn("slot-reflect trigger failed", {
|
|
17659
18154
|
sessionId: data.sessionId,
|
|
17660
18155
|
error: err instanceof Error ? err.message : String(err)
|
|
17661
18156
|
});
|
|
17662
18157
|
}
|
|
17663
18158
|
if (isGraphExtractionEnabled()) try {
|
|
17664
18159
|
const compressed = (await kv.list(KV.observations(data.sessionId))).filter((o) => o.title);
|
|
17665
|
-
if (compressed.length > 0) sdk.
|
|
18160
|
+
if (compressed.length > 0) sdk.trigger({
|
|
18161
|
+
function_id: "mem::graph-extract",
|
|
18162
|
+
payload: { observations: compressed },
|
|
18163
|
+
action: TriggerAction.Void()
|
|
18164
|
+
});
|
|
17666
18165
|
} catch (err) {
|
|
17667
|
-
logger.warn("graph-extract
|
|
18166
|
+
logger.warn("graph-extract trigger failed", {
|
|
17668
18167
|
sessionId: data.sessionId,
|
|
17669
18168
|
error: err instanceof Error ? err.message : String(err)
|
|
17670
18169
|
});
|
|
@@ -17722,7 +18221,6 @@ function registerEventTriggers(sdk, kv) {
|
|
|
17722
18221
|
config: { scope: KV.sessions }
|
|
17723
18222
|
});
|
|
17724
18223
|
}
|
|
17725
|
-
|
|
17726
18224
|
//#endregion
|
|
17727
18225
|
//#region src/mcp/server.ts
|
|
17728
18226
|
function asNonEmptyString(value) {
|
|
@@ -19359,7 +19857,6 @@ function registerMcpEndpoints(sdk, kv, secret) {
|
|
|
19359
19857
|
}
|
|
19360
19858
|
});
|
|
19361
19859
|
}
|
|
19362
|
-
|
|
19363
19860
|
//#endregion
|
|
19364
19861
|
//#region src/eval/metrics-store.ts
|
|
19365
19862
|
var MetricsStore = class {
|
|
@@ -19402,7 +19899,6 @@ var MetricsStore = class {
|
|
|
19402
19899
|
return Array.from(merged.values());
|
|
19403
19900
|
}
|
|
19404
19901
|
};
|
|
19405
|
-
|
|
19406
19902
|
//#endregion
|
|
19407
19903
|
//#region src/functions/dedup.ts
|
|
19408
19904
|
const TTL_MS = 300 * 1e3;
|
|
@@ -19444,57 +19940,6 @@ var DedupMap = class {
|
|
|
19444
19940
|
return this.entries.size;
|
|
19445
19941
|
}
|
|
19446
19942
|
};
|
|
19447
|
-
|
|
19448
|
-
//#endregion
|
|
19449
|
-
//#region src/telemetry/setup.ts
|
|
19450
|
-
const OTEL_CONFIG = {
|
|
19451
|
-
serviceName: "agentmemory",
|
|
19452
|
-
serviceVersion: VERSION,
|
|
19453
|
-
metricsExportIntervalMs: 3e4
|
|
19454
|
-
};
|
|
19455
|
-
let counters = null;
|
|
19456
|
-
let histograms = null;
|
|
19457
|
-
const NOOP_COUNTER = { add: () => {} };
|
|
19458
|
-
const NOOP_HISTOGRAM = { record: () => {} };
|
|
19459
|
-
const COUNTER_NAMES = [
|
|
19460
|
-
["observationsTotal", "observations.total"],
|
|
19461
|
-
["compressionSuccess", "compression.success"],
|
|
19462
|
-
["compressionFailure", "compression.failure"],
|
|
19463
|
-
["searchTotal", "search.total"],
|
|
19464
|
-
["dedupSkipped", "dedup.skipped"],
|
|
19465
|
-
["evictionTotal", "eviction.total"],
|
|
19466
|
-
["circuitBreakerOpen", "circuit_breaker.open"],
|
|
19467
|
-
["embeddingSuccess", "embedding.success"],
|
|
19468
|
-
["embeddingFailure", "embedding.failure"],
|
|
19469
|
-
["vectorSearchTotal", "vector_search.total"],
|
|
19470
|
-
["autoForgetTotal", "auto_forget.total"],
|
|
19471
|
-
["profileGenerated", "profile.generated"],
|
|
19472
|
-
["claudeBridgeSync", "claude_bridge.sync"],
|
|
19473
|
-
["graphExtraction", "graph.extraction"],
|
|
19474
|
-
["consolidationRun", "consolidation.run"],
|
|
19475
|
-
["teamShare", "team.share"],
|
|
19476
|
-
["auditLog", "audit.log"],
|
|
19477
|
-
["snapshotCreate", "snapshot.create"],
|
|
19478
|
-
["governanceDelete", "governance.delete"]
|
|
19479
|
-
];
|
|
19480
|
-
const HISTOGRAM_NAMES = [
|
|
19481
|
-
["compressionLatency", "compression.latency_ms"],
|
|
19482
|
-
["searchLatency", "search.latency_ms"],
|
|
19483
|
-
["contextTokens", "context.tokens"],
|
|
19484
|
-
["qualityScore", "quality.score"],
|
|
19485
|
-
["embeddingLatency", "embedding.latency_ms"],
|
|
19486
|
-
["vectorSearchLatency", "vector_search.latency_ms"]
|
|
19487
|
-
];
|
|
19488
|
-
function initMetrics(getMeter) {
|
|
19489
|
-
const meter = getMeter?.("agentmemory");
|
|
19490
|
-
counters = Object.fromEntries(COUNTER_NAMES.map(([key, name]) => [key, meter ? meter.createCounter(name) : NOOP_COUNTER]));
|
|
19491
|
-
histograms = Object.fromEntries(HISTOGRAM_NAMES.map(([key, name]) => [key, meter ? meter.createHistogram(name) : NOOP_HISTOGRAM]));
|
|
19492
|
-
return {
|
|
19493
|
-
counters,
|
|
19494
|
-
histograms
|
|
19495
|
-
};
|
|
19496
|
-
}
|
|
19497
|
-
|
|
19498
19943
|
//#endregion
|
|
19499
19944
|
//#region src/index.ts
|
|
19500
19945
|
function workerPidfilePath() {
|
|
@@ -19643,6 +20088,7 @@ async function main() {
|
|
|
19643
20088
|
const graphWeight = parseFloat(getEnvVar("AGENTMEMORY_GRAPH_WEIGHT") || "0.3");
|
|
19644
20089
|
const hybridSearch = new HybridSearch(bm25Index, vectorIndex, embeddingProvider, kv, embeddingConfig.bm25Weight, embeddingConfig.vectorWeight, graphWeight);
|
|
19645
20090
|
registerSmartSearchFunction(sdk, kv, (query, limit) => hybridSearch.search(query, limit));
|
|
20091
|
+
registerRecentSearchesSweepFunction(sdk, kv);
|
|
19646
20092
|
registerApiTriggers(sdk, kv, secret, metricsStore, provider);
|
|
19647
20093
|
registerEventTriggers(sdk, kv);
|
|
19648
20094
|
registerMcpEndpoints(sdk, kv, secret);
|
|
@@ -19710,7 +20156,7 @@ async function main() {
|
|
|
19710
20156
|
console.warn(`[agentmemory] Failed to backfill memories into BM25:`, err);
|
|
19711
20157
|
}
|
|
19712
20158
|
bootLog(`Ready. ${embeddingProvider ? "Triple-stream (BM25+Vector+Graph)" : "BM25+Graph"} search active.`);
|
|
19713
|
-
bootLog(`REST API:
|
|
20159
|
+
bootLog(`REST API: 126 endpoints at http://localhost:${config.restPort}/agentmemory/*`);
|
|
19714
20160
|
bootLog(`MCP surface (opt-in via \`npx @agentmemory/mcp\`): ${getAllTools().length} tools · 6 resources · 3 prompts`);
|
|
19715
20161
|
const viewerServer = startViewerServer(config.restPort + 2, kv, sdk, secret, config.restPort);
|
|
19716
20162
|
const autoForgetIntervalMs = parseInt(process.env.AUTO_FORGET_INTERVAL_MS || "3600000", 10);
|
|
@@ -19745,6 +20191,14 @@ async function main() {
|
|
|
19745
20191
|
});
|
|
19746
20192
|
} catch {}
|
|
19747
20193
|
}, 864e5).unref();
|
|
20194
|
+
setInterval(async () => {
|
|
20195
|
+
try {
|
|
20196
|
+
await sdk.trigger({
|
|
20197
|
+
function_id: "mem::diagnostic::recent-searches-sweep",
|
|
20198
|
+
payload: {}
|
|
20199
|
+
});
|
|
20200
|
+
} catch {}
|
|
20201
|
+
}, 3600 * 1e3).unref();
|
|
19748
20202
|
if (isConsolidationEnabled()) {
|
|
19749
20203
|
setInterval(async () => {
|
|
19750
20204
|
try {
|
|
@@ -19776,7 +20230,7 @@ main().catch((err) => {
|
|
|
19776
20230
|
console.error(`[agentmemory] Fatal:`, err);
|
|
19777
20231
|
process.exit(1);
|
|
19778
20232
|
});
|
|
19779
|
-
|
|
19780
20233
|
//#endregion
|
|
19781
|
-
export {
|
|
19782
|
-
|
|
20234
|
+
export {};
|
|
20235
|
+
|
|
20236
|
+
//# sourceMappingURL=src-u7kAEUC0.mjs.map
|