@agentmemory/agentmemory 0.9.23 → 0.9.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +1 -1
- package/README.md +1 -1
- package/dist/cli.d.mts.map +1 -1
- package/dist/cli.mjs +129 -66
- package/dist/cli.mjs.map +1 -1
- package/dist/{connect-Cf9bmBqO.mjs → connect-bmZ5eqYN.mjs} +17 -56
- package/dist/{connect-Cf9bmBqO.mjs.map → connect-bmZ5eqYN.mjs.map} +1 -1
- package/dist/hooks/notification.mjs +2 -4
- package/dist/hooks/notification.mjs.map +1 -1
- package/dist/hooks/post-commit.mjs +2 -3
- package/dist/hooks/post-commit.mjs.map +1 -1
- package/dist/hooks/post-tool-failure.mjs +2 -4
- package/dist/hooks/post-tool-failure.mjs.map +1 -1
- package/dist/hooks/post-tool-use.mjs +2 -4
- package/dist/hooks/post-tool-use.mjs.map +1 -1
- package/dist/hooks/pre-compact.mjs +2 -4
- package/dist/hooks/pre-compact.mjs.map +1 -1
- package/dist/hooks/pre-tool-use.mjs +2 -2
- package/dist/hooks/pre-tool-use.mjs.map +1 -1
- package/dist/hooks/prompt-submit.mjs +2 -4
- package/dist/hooks/prompt-submit.mjs.map +1 -1
- package/dist/hooks/session-end.mjs +2 -2
- package/dist/hooks/session-start.mjs +2 -4
- package/dist/hooks/session-start.mjs.map +1 -1
- package/dist/hooks/stop.mjs +2 -2
- package/dist/hooks/subagent-start.mjs +2 -4
- package/dist/hooks/subagent-start.mjs.map +1 -1
- package/dist/hooks/subagent-stop.mjs +2 -4
- package/dist/hooks/subagent-stop.mjs.map +1 -1
- package/dist/hooks/task-completed.mjs +2 -4
- package/dist/hooks/task-completed.mjs.map +1 -1
- package/dist/image-refs-C7h9L5wx.mjs +52 -0
- package/dist/image-refs-C7h9L5wx.mjs.map +1 -0
- package/dist/{image-refs-CJS5B9Gq.mjs → image-store-Gpo2mgM9.mjs} +11 -42
- package/dist/image-store-Gpo2mgM9.mjs.map +1 -0
- package/dist/index.mjs +942 -493
- package/dist/index.mjs.map +1 -1
- package/dist/{logger-xlVlvCWX.mjs → logger-yHTcEBAI.mjs} +2 -2
- package/dist/{logger-xlVlvCWX.mjs.map → logger-yHTcEBAI.mjs.map} +1 -1
- package/dist/rolldown-runtime-twds-ZHy.mjs +14 -0
- package/dist/{schema-BkALl7Z_.mjs → schema-Dsr_V2Wp.mjs} +4 -4
- package/dist/schema-Dsr_V2Wp.mjs.map +1 -0
- package/dist/{src-DvS3bhMe.mjs → src-fQOMXeCp.mjs} +937 -483
- package/dist/src-fQOMXeCp.mjs.map +1 -0
- package/dist/{standalone-DHQcPX_g.mjs → standalone-BzfA1zu8.mjs} +6 -10
- package/dist/{standalone-DHQcPX_g.mjs.map → standalone-BzfA1zu8.mjs.map} +1 -1
- package/dist/standalone.mjs +3 -14
- package/dist/standalone.mjs.map +1 -1
- package/dist/{tools-registry-DJizX9Az.mjs → tools-registry-Dzxv9iUu.mjs} +7 -5
- package/dist/tools-registry-Dzxv9iUu.mjs.map +1 -0
- package/dist/version-C3hZKw8n.mjs +6 -0
- package/dist/version-C3hZKw8n.mjs.map +1 -0
- package/dist/viewer/index.html +155 -9
- package/package.json +9 -4
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/plugin.json +1 -1
- package/plugin/scripts/notification.mjs +2 -4
- package/plugin/scripts/notification.mjs.map +1 -1
- package/plugin/scripts/post-commit.mjs +2 -3
- package/plugin/scripts/post-commit.mjs.map +1 -1
- package/plugin/scripts/post-tool-failure.mjs +2 -4
- package/plugin/scripts/post-tool-failure.mjs.map +1 -1
- package/plugin/scripts/post-tool-use.mjs +2 -4
- package/plugin/scripts/post-tool-use.mjs.map +1 -1
- package/plugin/scripts/pre-compact.mjs +2 -4
- package/plugin/scripts/pre-compact.mjs.map +1 -1
- package/plugin/scripts/pre-tool-use.mjs +2 -2
- package/plugin/scripts/pre-tool-use.mjs.map +1 -1
- package/plugin/scripts/prompt-submit.mjs +2 -4
- package/plugin/scripts/prompt-submit.mjs.map +1 -1
- package/plugin/scripts/session-end.mjs +2 -2
- package/plugin/scripts/session-start.mjs +2 -4
- package/plugin/scripts/session-start.mjs.map +1 -1
- package/plugin/scripts/stop.mjs +2 -2
- package/plugin/scripts/subagent-start.mjs +2 -4
- package/plugin/scripts/subagent-start.mjs.map +1 -1
- package/plugin/scripts/subagent-stop.mjs +2 -4
- package/plugin/scripts/subagent-stop.mjs.map +1 -1
- package/plugin/scripts/task-completed.mjs +2 -4
- package/plugin/scripts/task-completed.mjs.map +1 -1
- package/dist/image-refs-CJS5B9Gq.mjs.map +0 -1
- package/dist/image-store-CdE0amb1.mjs +0 -3
- package/dist/schema-BkALl7Z_.mjs.map +0 -1
- package/dist/src-DvS3bhMe.mjs.map +0 -1
- package/dist/tools-registry-DJizX9Az.mjs.map +0 -1
- package/dist/version-BPfyI4Kc.mjs +0 -6
- package/dist/version-BPfyI4Kc.mjs.map +0 -1
|
@@ -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-C3hZKw8n.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) 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) {
|
|
@@ -6445,7 +6826,9 @@ function registerExportImportFunction(sdk, kv) {
|
|
|
6445
6826
|
"0.9.20",
|
|
6446
6827
|
"0.9.21",
|
|
6447
6828
|
"0.9.22",
|
|
6448
|
-
"0.9.23"
|
|
6829
|
+
"0.9.23",
|
|
6830
|
+
"0.9.24",
|
|
6831
|
+
"0.9.25"
|
|
6449
6832
|
]).has(importData.version)) return {
|
|
6450
6833
|
success: false,
|
|
6451
6834
|
error: `Unsupported export version: ${importData.version}`
|
|
@@ -6763,7 +7146,6 @@ function registerExportImportFunction(sdk, kv) {
|
|
|
6763
7146
|
};
|
|
6764
7147
|
});
|
|
6765
7148
|
}
|
|
6766
|
-
|
|
6767
7149
|
//#endregion
|
|
6768
7150
|
//#region src/functions/enrich.ts
|
|
6769
7151
|
const MAX_CONTEXT_LENGTH = 4e3;
|
|
@@ -6824,7 +7206,6 @@ function registerEnrichFunction(sdk, kv) {
|
|
|
6824
7206
|
};
|
|
6825
7207
|
});
|
|
6826
7208
|
}
|
|
6827
|
-
|
|
6828
7209
|
//#endregion
|
|
6829
7210
|
//#region src/functions/claude-bridge.ts
|
|
6830
7211
|
function parseMemoryMd(content) {
|
|
@@ -6945,7 +7326,6 @@ function registerClaudeBridgeFunction(sdk, kv, config) {
|
|
|
6945
7326
|
}
|
|
6946
7327
|
});
|
|
6947
7328
|
}
|
|
6948
|
-
|
|
6949
7329
|
//#endregion
|
|
6950
7330
|
//#region src/prompts/graph-extraction.ts
|
|
6951
7331
|
const GRAPH_EXTRACTION_SYSTEM = `You are a knowledge graph extraction engine. Given a compressed observation from a coding session, extract entities and relationships.
|
|
@@ -6968,9 +7348,41 @@ Rules:
|
|
|
6968
7348
|
function buildGraphExtractionPrompt(observations) {
|
|
6969
7349
|
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")}`;
|
|
6970
7350
|
}
|
|
6971
|
-
|
|
6972
7351
|
//#endregion
|
|
6973
7352
|
//#region src/functions/graph.ts
|
|
7353
|
+
const DEFAULT_GRAPH_QUERY_LIMIT = 500;
|
|
7354
|
+
const MAX_GRAPH_QUERY_LIMIT = 5e3;
|
|
7355
|
+
function resolvePagination(rawLimit, rawOffset) {
|
|
7356
|
+
return {
|
|
7357
|
+
limit: Math.max(1, Math.min(typeof rawLimit === "number" && Number.isFinite(rawLimit) ? Math.floor(rawLimit) : DEFAULT_GRAPH_QUERY_LIMIT, MAX_GRAPH_QUERY_LIMIT)),
|
|
7358
|
+
offset: Math.max(0, typeof rawOffset === "number" && Number.isFinite(rawOffset) ? Math.floor(rawOffset) : 0)
|
|
7359
|
+
};
|
|
7360
|
+
}
|
|
7361
|
+
function rankByDegree(nodes, edges) {
|
|
7362
|
+
const degree = /* @__PURE__ */ new Map();
|
|
7363
|
+
for (const edge of edges) {
|
|
7364
|
+
degree.set(edge.sourceNodeId, (degree.get(edge.sourceNodeId) ?? 0) + 1);
|
|
7365
|
+
degree.set(edge.targetNodeId, (degree.get(edge.targetNodeId) ?? 0) + 1);
|
|
7366
|
+
}
|
|
7367
|
+
return [...nodes].sort((a, b) => (degree.get(b.id) ?? 0) - (degree.get(a.id) ?? 0));
|
|
7368
|
+
}
|
|
7369
|
+
function paginate(nodes, allEdges, depth, limit, offset) {
|
|
7370
|
+
const totalNodes = nodes.length;
|
|
7371
|
+
const pageNodes = nodes.slice(offset, offset + limit);
|
|
7372
|
+
const pageNodeIds = new Set(pageNodes.map((n) => n.id));
|
|
7373
|
+
const pageEdges = allEdges.filter((e) => pageNodeIds.has(e.sourceNodeId) && pageNodeIds.has(e.targetNodeId));
|
|
7374
|
+
const universeIds = new Set(nodes.map((n) => n.id));
|
|
7375
|
+
return {
|
|
7376
|
+
nodes: pageNodes,
|
|
7377
|
+
edges: pageEdges,
|
|
7378
|
+
depth,
|
|
7379
|
+
totalNodes,
|
|
7380
|
+
totalEdges: allEdges.reduce((count, e) => universeIds.has(e.sourceNodeId) && universeIds.has(e.targetNodeId) ? count + 1 : count, 0),
|
|
7381
|
+
truncated: totalNodes > pageNodes.length,
|
|
7382
|
+
limit,
|
|
7383
|
+
offset
|
|
7384
|
+
};
|
|
7385
|
+
}
|
|
6974
7386
|
function parseAttrs(raw) {
|
|
6975
7387
|
const attrs = {};
|
|
6976
7388
|
const attrRegex = /([A-Za-z_][\w:-]*)="([^"]*)"/g;
|
|
@@ -7107,15 +7519,10 @@ function registerGraphFunction(sdk, kv, provider) {
|
|
|
7107
7519
|
const allNodes = (await kv.list(KV.graphNodes)).filter((n) => !n.stale);
|
|
7108
7520
|
const allEdges = (await kv.list(KV.graphEdges)).filter((e) => !e.stale);
|
|
7109
7521
|
const maxDepth = Math.min(data.maxDepth || 3, 5);
|
|
7522
|
+
const { limit, offset } = resolvePagination(data.limit, data.offset);
|
|
7110
7523
|
if (data.query) {
|
|
7111
7524
|
const lower = data.query.toLowerCase();
|
|
7112
|
-
|
|
7113
|
-
const nodeIds = new Set(matchingNodes.map((n) => n.id));
|
|
7114
|
-
return {
|
|
7115
|
-
nodes: matchingNodes,
|
|
7116
|
-
edges: allEdges.filter((e) => nodeIds.has(e.sourceNodeId) || nodeIds.has(e.targetNodeId)),
|
|
7117
|
-
depth: 0
|
|
7118
|
-
};
|
|
7525
|
+
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);
|
|
7119
7526
|
}
|
|
7120
7527
|
if (data.startNodeId) {
|
|
7121
7528
|
const visited = /* @__PURE__ */ new Set();
|
|
@@ -7147,19 +7554,11 @@ function registerGraphFunction(sdk, kv, provider) {
|
|
|
7147
7554
|
});
|
|
7148
7555
|
}
|
|
7149
7556
|
}
|
|
7150
|
-
return
|
|
7151
|
-
nodes: resultNodes,
|
|
7152
|
-
edges: resultEdges,
|
|
7153
|
-
depth: maxDepth
|
|
7154
|
-
};
|
|
7557
|
+
return paginate(resultNodes, resultEdges, maxDepth, limit, offset);
|
|
7155
7558
|
}
|
|
7156
7559
|
let filtered = allNodes;
|
|
7157
7560
|
if (data.nodeType) filtered = allNodes.filter((n) => n.type === data.nodeType);
|
|
7158
|
-
return
|
|
7159
|
-
nodes: filtered,
|
|
7160
|
-
edges: allEdges,
|
|
7161
|
-
depth: 0
|
|
7162
|
-
};
|
|
7561
|
+
return paginate(rankByDegree(filtered, allEdges), allEdges, 0, limit, offset);
|
|
7163
7562
|
});
|
|
7164
7563
|
sdk.registerFunction("mem::graph-stats", async () => {
|
|
7165
7564
|
const nodes = await kv.list(KV.graphNodes);
|
|
@@ -7176,7 +7575,6 @@ function registerGraphFunction(sdk, kv, provider) {
|
|
|
7176
7575
|
};
|
|
7177
7576
|
});
|
|
7178
7577
|
}
|
|
7179
|
-
|
|
7180
7578
|
//#endregion
|
|
7181
7579
|
//#region src/prompts/consolidation.ts
|
|
7182
7580
|
const SEMANTIC_MERGE_SYSTEM = `You are a memory consolidation engine. Given overlapping episodic memories (session summaries), extract stable factual knowledge.
|
|
@@ -7211,7 +7609,6 @@ Rules:
|
|
|
7211
7609
|
function buildProceduralExtractionPrompt(patterns) {
|
|
7212
7610
|
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")}`;
|
|
7213
7611
|
}
|
|
7214
|
-
|
|
7215
7612
|
//#endregion
|
|
7216
7613
|
//#region src/functions/consolidation-pipeline.ts
|
|
7217
7614
|
function applyDecay(items, decayDays) {
|
|
@@ -7405,7 +7802,6 @@ function registerConsolidationPipelineFunction(sdk, kv, provider) {
|
|
|
7405
7802
|
};
|
|
7406
7803
|
});
|
|
7407
7804
|
}
|
|
7408
|
-
|
|
7409
7805
|
//#endregion
|
|
7410
7806
|
//#region src/functions/team.ts
|
|
7411
7807
|
const VALID_ITEM_TYPES = new Set([
|
|
@@ -7509,7 +7905,6 @@ function registerTeamFunction(sdk, kv, config) {
|
|
|
7509
7905
|
return profile;
|
|
7510
7906
|
});
|
|
7511
7907
|
}
|
|
7512
|
-
|
|
7513
7908
|
//#endregion
|
|
7514
7909
|
//#region src/functions/governance.ts
|
|
7515
7910
|
function registerGovernanceFunction(sdk, kv) {
|
|
@@ -7618,7 +8013,6 @@ function registerGovernanceFunction(sdk, kv) {
|
|
|
7618
8013
|
return queryAudit(kv, data);
|
|
7619
8014
|
});
|
|
7620
8015
|
}
|
|
7621
|
-
|
|
7622
8016
|
//#endregion
|
|
7623
8017
|
//#region src/functions/snapshot.ts
|
|
7624
8018
|
const COMMIT_HASH_RE = /^[0-9a-f]{7,40}$/i;
|
|
@@ -7783,7 +8177,6 @@ function registerSnapshotFunction(sdk, kv, snapshotDir) {
|
|
|
7783
8177
|
}
|
|
7784
8178
|
});
|
|
7785
8179
|
}
|
|
7786
|
-
|
|
7787
8180
|
//#endregion
|
|
7788
8181
|
//#region src/functions/actions.ts
|
|
7789
8182
|
function registerActionsFunction(sdk, kv) {
|
|
@@ -7985,7 +8378,6 @@ async function propagateCompletion(kv, completedActionId) {
|
|
|
7985
8378
|
});
|
|
7986
8379
|
}
|
|
7987
8380
|
}
|
|
7988
|
-
|
|
7989
8381
|
//#endregion
|
|
7990
8382
|
//#region src/functions/frontier.ts
|
|
7991
8383
|
function registerFrontierFunction(sdk, kv) {
|
|
@@ -8090,7 +8482,6 @@ function computeScore(action, edges, now) {
|
|
|
8090
8482
|
if (action.status === "active") score += 15;
|
|
8091
8483
|
return Math.round(score * 100) / 100;
|
|
8092
8484
|
}
|
|
8093
|
-
|
|
8094
8485
|
//#endregion
|
|
8095
8486
|
//#region src/functions/leases.ts
|
|
8096
8487
|
const DEFAULT_LEASE_TTL_MS = 600 * 1e3;
|
|
@@ -8271,7 +8662,6 @@ function registerLeasesFunction(sdk, kv) {
|
|
|
8271
8662
|
};
|
|
8272
8663
|
});
|
|
8273
8664
|
}
|
|
8274
|
-
|
|
8275
8665
|
//#endregion
|
|
8276
8666
|
//#region src/functions/routines.ts
|
|
8277
8667
|
function registerRoutinesFunction(sdk, kv) {
|
|
@@ -8518,7 +8908,6 @@ function registerRoutinesFunction(sdk, kv) {
|
|
|
8518
8908
|
});
|
|
8519
8909
|
});
|
|
8520
8910
|
}
|
|
8521
|
-
|
|
8522
8911
|
//#endregion
|
|
8523
8912
|
//#region src/functions/signals.ts
|
|
8524
8913
|
function registerSignalsFunction(sdk, kv) {
|
|
@@ -8650,7 +9039,6 @@ function registerSignalsFunction(sdk, kv) {
|
|
|
8650
9039
|
};
|
|
8651
9040
|
});
|
|
8652
9041
|
}
|
|
8653
|
-
|
|
8654
9042
|
//#endregion
|
|
8655
9043
|
//#region src/functions/checkpoints.ts
|
|
8656
9044
|
function registerCheckpointsFunction(sdk, kv) {
|
|
@@ -8823,7 +9211,6 @@ function registerCheckpointsFunction(sdk, kv) {
|
|
|
8823
9211
|
};
|
|
8824
9212
|
});
|
|
8825
9213
|
}
|
|
8826
|
-
|
|
8827
9214
|
//#endregion
|
|
8828
9215
|
//#region src/functions/flow-compress.ts
|
|
8829
9216
|
const FLOW_COMPRESS_SYSTEM = `You are a workflow summarizer. Given a completed action chain, produce a concise summary capturing:
|
|
@@ -8968,7 +9355,6 @@ function extractFiles(actions) {
|
|
|
8968
9355
|
}
|
|
8969
9356
|
return Array.from(files);
|
|
8970
9357
|
}
|
|
8971
|
-
|
|
8972
9358
|
//#endregion
|
|
8973
9359
|
//#region src/functions/mesh.ts
|
|
8974
9360
|
function isPrivateIP(ip) {
|
|
@@ -9285,7 +9671,6 @@ async function applySyncData(kv, data, scopes) {
|
|
|
9285
9671
|
if (scopes.includes("graph:edges")) applied += await lwwMergeList(kv, KV.graphEdges, data.graphEdges, "mem:gedge", "createdAt");
|
|
9286
9672
|
return applied;
|
|
9287
9673
|
}
|
|
9288
|
-
|
|
9289
9674
|
//#endregion
|
|
9290
9675
|
//#region src/functions/branch-aware.ts
|
|
9291
9676
|
function execAsync(cmd, args, cwd) {
|
|
@@ -9400,7 +9785,6 @@ function registerBranchAwareFunction(sdk, kv) {
|
|
|
9400
9785
|
};
|
|
9401
9786
|
});
|
|
9402
9787
|
}
|
|
9403
|
-
|
|
9404
9788
|
//#endregion
|
|
9405
9789
|
//#region src/functions/sentinels.ts
|
|
9406
9790
|
const VALID_TYPES = [
|
|
@@ -9721,7 +10105,6 @@ async function unblockLinkedActions(kv, sentinel) {
|
|
|
9721
10105
|
});
|
|
9722
10106
|
return unblockedCount;
|
|
9723
10107
|
}
|
|
9724
|
-
|
|
9725
10108
|
//#endregion
|
|
9726
10109
|
//#region src/functions/sketches.ts
|
|
9727
10110
|
function registerSketchesFunction(sdk, kv) {
|
|
@@ -9963,7 +10346,6 @@ function registerSketchesFunction(sdk, kv) {
|
|
|
9963
10346
|
};
|
|
9964
10347
|
});
|
|
9965
10348
|
}
|
|
9966
|
-
|
|
9967
10349
|
//#endregion
|
|
9968
10350
|
//#region src/functions/crystallize.ts
|
|
9969
10351
|
const CRYSTALLIZE_SYSTEM = `You are summarizing a completed chain of agent actions into a compact digest.
|
|
@@ -10160,7 +10542,6 @@ function parseDigest(response) {
|
|
|
10160
10542
|
};
|
|
10161
10543
|
}
|
|
10162
10544
|
}
|
|
10163
|
-
|
|
10164
10545
|
//#endregion
|
|
10165
10546
|
//#region src/functions/diagnostics.ts
|
|
10166
10547
|
const ALL_CATEGORIES = [
|
|
@@ -10874,7 +11255,6 @@ function registerDiagnosticsFunction(sdk, kv) {
|
|
|
10874
11255
|
};
|
|
10875
11256
|
});
|
|
10876
11257
|
}
|
|
10877
|
-
|
|
10878
11258
|
//#endregion
|
|
10879
11259
|
//#region src/functions/facets.ts
|
|
10880
11260
|
function registerFacetsFunction(sdk, kv) {
|
|
@@ -11046,7 +11426,6 @@ function registerFacetsFunction(sdk, kv) {
|
|
|
11046
11426
|
};
|
|
11047
11427
|
});
|
|
11048
11428
|
}
|
|
11049
|
-
|
|
11050
11429
|
//#endregion
|
|
11051
11430
|
//#region src/functions/verify.ts
|
|
11052
11431
|
function registerVerifyFunction(sdk, kv) {
|
|
@@ -11141,7 +11520,6 @@ async function findObservation(kv, obsId, hintSessionIds) {
|
|
|
11141
11520
|
}
|
|
11142
11521
|
return null;
|
|
11143
11522
|
}
|
|
11144
|
-
|
|
11145
11523
|
//#endregion
|
|
11146
11524
|
//#region src/functions/cascade.ts
|
|
11147
11525
|
function registerCascadeFunction(sdk, kv) {
|
|
@@ -11211,7 +11589,6 @@ function registerCascadeFunction(sdk, kv) {
|
|
|
11211
11589
|
};
|
|
11212
11590
|
});
|
|
11213
11591
|
}
|
|
11214
|
-
|
|
11215
11592
|
//#endregion
|
|
11216
11593
|
//#region src/functions/lessons.ts
|
|
11217
11594
|
function reinforceLesson(lesson) {
|
|
@@ -11397,7 +11774,6 @@ function registerLessonsFunctions(sdk, kv) {
|
|
|
11397
11774
|
};
|
|
11398
11775
|
});
|
|
11399
11776
|
}
|
|
11400
|
-
|
|
11401
11777
|
//#endregion
|
|
11402
11778
|
//#region src/functions/obsidian-export.ts
|
|
11403
11779
|
const DEFAULT_EXPORT_ROOT = join(homedir(), ".agentmemory");
|
|
@@ -11413,6 +11789,20 @@ function resolveVaultDir(vaultDir) {
|
|
|
11413
11789
|
function sanitize(name) {
|
|
11414
11790
|
return name.replace(/[<>:"/\\|?*\x00-\x1f]/g, "_").slice(0, 100);
|
|
11415
11791
|
}
|
|
11792
|
+
function hasExportId(item) {
|
|
11793
|
+
return !!item && typeof item.id === "string" && item.id.length > 0;
|
|
11794
|
+
}
|
|
11795
|
+
function safeArray(value) {
|
|
11796
|
+
return Array.isArray(value) ? value : [];
|
|
11797
|
+
}
|
|
11798
|
+
function safeString(value, fallback = "") {
|
|
11799
|
+
return typeof value === "string" ? value : fallback;
|
|
11800
|
+
}
|
|
11801
|
+
function safeTimestamp(value) {
|
|
11802
|
+
if (typeof value !== "string") return 0;
|
|
11803
|
+
const time = new Date(value).getTime();
|
|
11804
|
+
return Number.isFinite(time) ? time : 0;
|
|
11805
|
+
}
|
|
11416
11806
|
function toFrontmatter(obj) {
|
|
11417
11807
|
const lines = ["---"];
|
|
11418
11808
|
for (const [key, value] of Object.entries(obj)) {
|
|
@@ -11424,6 +11814,11 @@ function toFrontmatter(obj) {
|
|
|
11424
11814
|
return lines.join("\n");
|
|
11425
11815
|
}
|
|
11426
11816
|
function memoryToMd(m) {
|
|
11817
|
+
const concepts = safeArray(m.concepts);
|
|
11818
|
+
const files = safeArray(m.files);
|
|
11819
|
+
const relatedIds = safeArray(m.relatedIds);
|
|
11820
|
+
const supersedes = safeArray(m.supersedes);
|
|
11821
|
+
const title = safeString(m.title, m.id);
|
|
11427
11822
|
const fm = toFrontmatter({
|
|
11428
11823
|
id: m.id,
|
|
11429
11824
|
type: m.type,
|
|
@@ -11431,24 +11826,28 @@ function memoryToMd(m) {
|
|
|
11431
11826
|
updated: m.updatedAt,
|
|
11432
11827
|
strength: m.strength,
|
|
11433
11828
|
version: m.version,
|
|
11434
|
-
concepts
|
|
11435
|
-
files
|
|
11829
|
+
concepts,
|
|
11830
|
+
files
|
|
11436
11831
|
});
|
|
11437
|
-
const
|
|
11438
|
-
const
|
|
11832
|
+
const relatedLines = relatedIds.map((id) => `- [[${id}]]`).join("\n");
|
|
11833
|
+
const supersedesLines = supersedes.map((id) => `- [[${id}]] (superseded)`).join("\n");
|
|
11439
11834
|
const sections = [
|
|
11440
11835
|
fm,
|
|
11441
11836
|
"",
|
|
11442
|
-
`# ${
|
|
11837
|
+
`# ${title}`,
|
|
11443
11838
|
"",
|
|
11444
|
-
m.content
|
|
11839
|
+
safeString(m.content)
|
|
11445
11840
|
];
|
|
11446
|
-
if (
|
|
11447
|
-
if (
|
|
11448
|
-
if (
|
|
11841
|
+
if (concepts.length > 0) sections.push("", "## Concepts", concepts.map((c) => `#${c.replace(/\s+/g, "-")}`).join(" "));
|
|
11842
|
+
if (relatedLines) sections.push("", "## Related", relatedLines);
|
|
11843
|
+
if (supersedesLines) sections.push("", "## Supersedes", supersedesLines);
|
|
11449
11844
|
return sections.join("\n");
|
|
11450
11845
|
}
|
|
11451
11846
|
function lessonToMd(l) {
|
|
11847
|
+
const tags = safeArray(l.tags);
|
|
11848
|
+
const sourceIds = safeArray(l.sourceIds);
|
|
11849
|
+
const content = safeString(l.content);
|
|
11850
|
+
const headline = content ? content.slice(0, 80) : l.id;
|
|
11452
11851
|
const fm = toFrontmatter({
|
|
11453
11852
|
id: l.id,
|
|
11454
11853
|
type: "lesson",
|
|
@@ -11458,66 +11857,76 @@ function lessonToMd(l) {
|
|
|
11458
11857
|
created: l.createdAt,
|
|
11459
11858
|
updated: l.updatedAt,
|
|
11460
11859
|
project: l.project,
|
|
11461
|
-
tags
|
|
11860
|
+
tags,
|
|
11462
11861
|
decayRate: l.decayRate
|
|
11463
11862
|
});
|
|
11464
|
-
const sourceLinks =
|
|
11863
|
+
const sourceLinks = sourceIds.map((id) => `- [[${id}]]`).join("\n");
|
|
11465
11864
|
const sections = [
|
|
11466
11865
|
fm,
|
|
11467
11866
|
"",
|
|
11468
|
-
`# Lesson: ${
|
|
11867
|
+
`# Lesson: ${headline}`,
|
|
11469
11868
|
"",
|
|
11470
|
-
|
|
11869
|
+
content
|
|
11471
11870
|
];
|
|
11472
11871
|
if (l.context) sections.push("", "## Context", l.context);
|
|
11473
|
-
if (
|
|
11872
|
+
if (tags.length > 0) sections.push("", "## Tags", tags.map((t) => `#${t.replace(/\s+/g, "-")}`).join(" "));
|
|
11474
11873
|
if (sourceLinks) sections.push("", "## Sources", sourceLinks);
|
|
11475
11874
|
return sections.join("\n");
|
|
11476
11875
|
}
|
|
11477
11876
|
function crystalToMd(c) {
|
|
11877
|
+
const keyOutcomes = safeArray(c.keyOutcomes);
|
|
11878
|
+
const lessons = safeArray(c.lessons);
|
|
11879
|
+
const filesAffected = safeArray(c.filesAffected);
|
|
11880
|
+
const sourceActionIds = safeArray(c.sourceActionIds);
|
|
11881
|
+
const narrative = safeString(c.narrative);
|
|
11882
|
+
const headline = narrative ? narrative.slice(0, 80) : c.id;
|
|
11478
11883
|
const fm = toFrontmatter({
|
|
11479
11884
|
id: c.id,
|
|
11480
11885
|
type: "crystal",
|
|
11481
11886
|
created: c.createdAt,
|
|
11482
11887
|
project: c.project,
|
|
11483
11888
|
sessionId: c.sessionId,
|
|
11484
|
-
filesAffected
|
|
11889
|
+
filesAffected
|
|
11485
11890
|
});
|
|
11486
|
-
const actionLinks =
|
|
11891
|
+
const actionLinks = sourceActionIds.map((id) => `- [[${id}]]`).join("\n");
|
|
11487
11892
|
const sections = [
|
|
11488
11893
|
fm,
|
|
11489
11894
|
"",
|
|
11490
|
-
`# Crystal: ${
|
|
11895
|
+
`# Crystal: ${headline}`,
|
|
11491
11896
|
"",
|
|
11492
|
-
|
|
11897
|
+
narrative,
|
|
11493
11898
|
"",
|
|
11494
11899
|
"## Key Outcomes",
|
|
11495
|
-
...
|
|
11900
|
+
...keyOutcomes.map((o) => `- ${o}`)
|
|
11496
11901
|
];
|
|
11497
|
-
if (
|
|
11498
|
-
if (
|
|
11902
|
+
if (lessons.length > 0) sections.push("", "## Lessons", ...lessons.map((l) => `- ${l}`));
|
|
11903
|
+
if (filesAffected.length > 0) sections.push("", "## Files", ...filesAffected.map((f) => `- \`${f}\``));
|
|
11499
11904
|
if (actionLinks) sections.push("", "## Source Actions", actionLinks);
|
|
11500
11905
|
return sections.join("\n");
|
|
11501
11906
|
}
|
|
11502
11907
|
function sessionToMd(s) {
|
|
11908
|
+
const project = safeString(s.project, "unknown");
|
|
11909
|
+
const status = safeString(s.status, "unknown");
|
|
11910
|
+
const startedAt = safeString(s.startedAt, "");
|
|
11911
|
+
const cwd = safeString(s.cwd, "");
|
|
11503
11912
|
return [
|
|
11504
11913
|
toFrontmatter({
|
|
11505
11914
|
id: s.id,
|
|
11506
11915
|
type: "session",
|
|
11507
|
-
project
|
|
11508
|
-
status
|
|
11509
|
-
started:
|
|
11916
|
+
project,
|
|
11917
|
+
status,
|
|
11918
|
+
started: startedAt || void 0,
|
|
11510
11919
|
ended: s.endedAt,
|
|
11511
11920
|
observations: s.observationCount
|
|
11512
11921
|
}),
|
|
11513
11922
|
"",
|
|
11514
|
-
`# Session: ${
|
|
11923
|
+
`# Session: ${project}`,
|
|
11515
11924
|
"",
|
|
11516
|
-
`**Status:** ${
|
|
11517
|
-
`**Started:** ${
|
|
11925
|
+
`**Status:** ${status}`,
|
|
11926
|
+
startedAt ? `**Started:** ${startedAt}` : "",
|
|
11518
11927
|
s.endedAt ? `**Ended:** ${s.endedAt}` : "",
|
|
11519
|
-
`**Observations:** ${s.observationCount}`,
|
|
11520
|
-
`**CWD:** \`${
|
|
11928
|
+
`**Observations:** ${s.observationCount ?? 0}`,
|
|
11929
|
+
cwd ? `**CWD:** \`${cwd}\`` : ""
|
|
11521
11930
|
].filter(Boolean).join("\n");
|
|
11522
11931
|
}
|
|
11523
11932
|
function registerObsidianExportFunction(sdk, kv) {
|
|
@@ -11553,122 +11962,131 @@ function registerObsidianExportFunction(sdk, kv) {
|
|
|
11553
11962
|
crystals: join(vaultDir, "crystals"),
|
|
11554
11963
|
sessions: join(vaultDir, "sessions")
|
|
11555
11964
|
};
|
|
11556
|
-
|
|
11557
|
-
|
|
11558
|
-
|
|
11559
|
-
|
|
11560
|
-
|
|
11561
|
-
|
|
11562
|
-
|
|
11563
|
-
|
|
11564
|
-
|
|
11565
|
-
|
|
11566
|
-
|
|
11567
|
-
|
|
11568
|
-
|
|
11569
|
-
|
|
11570
|
-
|
|
11571
|
-
|
|
11572
|
-
|
|
11573
|
-
|
|
11574
|
-
|
|
11575
|
-
const
|
|
11576
|
-
|
|
11577
|
-
|
|
11578
|
-
|
|
11579
|
-
|
|
11580
|
-
|
|
11581
|
-
|
|
11582
|
-
|
|
11583
|
-
|
|
11584
|
-
|
|
11585
|
-
|
|
11586
|
-
|
|
11965
|
+
try {
|
|
11966
|
+
await Promise.all(Object.values(dirs).map((dir) => mkdir(dir, { recursive: true })));
|
|
11967
|
+
const stats = {
|
|
11968
|
+
memories: 0,
|
|
11969
|
+
lessons: 0,
|
|
11970
|
+
crystals: 0,
|
|
11971
|
+
sessions: 0
|
|
11972
|
+
};
|
|
11973
|
+
const errors = [];
|
|
11974
|
+
const memoryMoc = [];
|
|
11975
|
+
const lessonMoc = [];
|
|
11976
|
+
const crystalMoc = [];
|
|
11977
|
+
const sessionMoc = [];
|
|
11978
|
+
const [memories, lessons, crystals, sessions] = await Promise.all([
|
|
11979
|
+
exportTypes.has("memories") ? kv.list(KV.memories) : Promise.resolve([]),
|
|
11980
|
+
exportTypes.has("lessons") ? kv.list(KV.lessons) : Promise.resolve([]),
|
|
11981
|
+
exportTypes.has("crystals") ? kv.list(KV.crystals) : Promise.resolve([]),
|
|
11982
|
+
exportTypes.has("sessions") ? kv.list(KV.sessions) : Promise.resolve([])
|
|
11983
|
+
]);
|
|
11984
|
+
for (const m of memories.filter((m) => hasExportId(m) && m.isLatest === true)) {
|
|
11985
|
+
const filename = `${sanitize(m.id)}.md`;
|
|
11986
|
+
const filepath = join(dirs.memories, filename);
|
|
11987
|
+
try {
|
|
11988
|
+
await writeFile(filepath, memoryToMd(m));
|
|
11989
|
+
stats.memories++;
|
|
11990
|
+
memoryMoc.push(`- [[memories/${sanitize(m.id)}|${safeString(m.title, m.id)}]] (${m.type}, strength: ${m.strength ?? 0})`);
|
|
11991
|
+
} catch (err) {
|
|
11992
|
+
errors.push({
|
|
11993
|
+
id: m.id,
|
|
11994
|
+
path: filepath,
|
|
11995
|
+
error: err instanceof Error ? err.message : String(err)
|
|
11996
|
+
});
|
|
11997
|
+
}
|
|
11587
11998
|
}
|
|
11588
|
-
|
|
11589
|
-
|
|
11590
|
-
|
|
11591
|
-
|
|
11592
|
-
|
|
11593
|
-
|
|
11594
|
-
|
|
11595
|
-
|
|
11596
|
-
|
|
11597
|
-
|
|
11598
|
-
|
|
11599
|
-
|
|
11600
|
-
|
|
11601
|
-
|
|
11999
|
+
for (const l of lessons.filter((l) => hasExportId(l) && !l.deleted)) {
|
|
12000
|
+
const filename = `${sanitize(l.id)}.md`;
|
|
12001
|
+
const filepath = join(dirs.lessons, filename);
|
|
12002
|
+
try {
|
|
12003
|
+
await writeFile(filepath, lessonToMd(l));
|
|
12004
|
+
stats.lessons++;
|
|
12005
|
+
const headline = safeString(l.content).slice(0, 60) || l.id;
|
|
12006
|
+
lessonMoc.push(`- [[lessons/${sanitize(l.id)}|${headline}]] (confidence: ${l.confidence ?? 0})`);
|
|
12007
|
+
} catch (err) {
|
|
12008
|
+
errors.push({
|
|
12009
|
+
id: l.id,
|
|
12010
|
+
path: filepath,
|
|
12011
|
+
error: err instanceof Error ? err.message : String(err)
|
|
12012
|
+
});
|
|
12013
|
+
}
|
|
11602
12014
|
}
|
|
11603
|
-
|
|
11604
|
-
|
|
11605
|
-
|
|
11606
|
-
|
|
11607
|
-
|
|
11608
|
-
|
|
11609
|
-
|
|
11610
|
-
|
|
11611
|
-
|
|
11612
|
-
|
|
11613
|
-
|
|
11614
|
-
|
|
11615
|
-
|
|
11616
|
-
|
|
12015
|
+
for (const c of crystals.filter(hasExportId)) {
|
|
12016
|
+
const filename = `${sanitize(c.id)}.md`;
|
|
12017
|
+
const filepath = join(dirs.crystals, filename);
|
|
12018
|
+
try {
|
|
12019
|
+
await writeFile(filepath, crystalToMd(c));
|
|
12020
|
+
stats.crystals++;
|
|
12021
|
+
const headline = safeString(c.narrative).slice(0, 60) || c.id;
|
|
12022
|
+
crystalMoc.push(`- [[crystals/${sanitize(c.id)}|${headline}]]`);
|
|
12023
|
+
} catch (err) {
|
|
12024
|
+
errors.push({
|
|
12025
|
+
id: c.id,
|
|
12026
|
+
path: filepath,
|
|
12027
|
+
error: err instanceof Error ? err.message : String(err)
|
|
12028
|
+
});
|
|
12029
|
+
}
|
|
11617
12030
|
}
|
|
11618
|
-
|
|
11619
|
-
|
|
11620
|
-
|
|
11621
|
-
|
|
11622
|
-
|
|
11623
|
-
|
|
11624
|
-
|
|
11625
|
-
|
|
11626
|
-
|
|
11627
|
-
|
|
11628
|
-
|
|
11629
|
-
|
|
11630
|
-
|
|
11631
|
-
|
|
11632
|
-
}
|
|
12031
|
+
const recent = sessions.filter(hasExportId).sort((a, b) => safeTimestamp(b.startedAt) - safeTimestamp(a.startedAt)).slice(0, 50);
|
|
12032
|
+
for (const s of recent) {
|
|
12033
|
+
const filename = `${sanitize(s.id)}.md`;
|
|
12034
|
+
const filepath = join(dirs.sessions, filename);
|
|
12035
|
+
try {
|
|
12036
|
+
await writeFile(filepath, sessionToMd(s));
|
|
12037
|
+
stats.sessions++;
|
|
12038
|
+
sessionMoc.push(`- [[sessions/${sanitize(s.id)}|${safeString(s.project, "unknown")} (${safeString(s.status, "unknown")})]]`);
|
|
12039
|
+
} catch (err) {
|
|
12040
|
+
errors.push({
|
|
12041
|
+
id: s.id,
|
|
12042
|
+
path: filepath,
|
|
12043
|
+
error: err instanceof Error ? err.message : String(err)
|
|
12044
|
+
});
|
|
12045
|
+
}
|
|
11633
12046
|
}
|
|
12047
|
+
const exportedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
12048
|
+
const moc = [
|
|
12049
|
+
"---",
|
|
12050
|
+
"type: moc",
|
|
12051
|
+
`exported: ${exportedAt}`,
|
|
12052
|
+
"---",
|
|
12053
|
+
"",
|
|
12054
|
+
"# agentmemory vault",
|
|
12055
|
+
"",
|
|
12056
|
+
`Exported: ${exportedAt}`,
|
|
12057
|
+
"",
|
|
12058
|
+
`## Memories (${stats.memories})`,
|
|
12059
|
+
...memoryMoc,
|
|
12060
|
+
"",
|
|
12061
|
+
`## Lessons (${stats.lessons})`,
|
|
12062
|
+
...lessonMoc,
|
|
12063
|
+
"",
|
|
12064
|
+
`## Crystals (${stats.crystals})`,
|
|
12065
|
+
...crystalMoc,
|
|
12066
|
+
"",
|
|
12067
|
+
`## Sessions (${stats.sessions})`,
|
|
12068
|
+
...sessionMoc
|
|
12069
|
+
].join("\n");
|
|
12070
|
+
await writeFile(join(vaultDir, "MOC.md"), moc);
|
|
12071
|
+
await recordAudit(kv, "obsidian_export", "mem::obsidian-export", [], {
|
|
12072
|
+
vaultDir,
|
|
12073
|
+
stats
|
|
12074
|
+
});
|
|
12075
|
+
return {
|
|
12076
|
+
success: true,
|
|
12077
|
+
exported: stats,
|
|
12078
|
+
errors: errors.length > 0 ? errors : void 0,
|
|
12079
|
+
vaultDir
|
|
12080
|
+
};
|
|
12081
|
+
} catch (err) {
|
|
12082
|
+
return {
|
|
12083
|
+
success: false,
|
|
12084
|
+
error: err instanceof Error ? err.message : String(err),
|
|
12085
|
+
vaultDir
|
|
12086
|
+
};
|
|
11634
12087
|
}
|
|
11635
|
-
const exportedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
11636
|
-
const moc = [
|
|
11637
|
-
"---",
|
|
11638
|
-
"type: moc",
|
|
11639
|
-
`exported: ${exportedAt}`,
|
|
11640
|
-
"---",
|
|
11641
|
-
"",
|
|
11642
|
-
"# agentmemory vault",
|
|
11643
|
-
"",
|
|
11644
|
-
`Exported: ${exportedAt}`,
|
|
11645
|
-
"",
|
|
11646
|
-
`## Memories (${stats.memories})`,
|
|
11647
|
-
...memoryMoc,
|
|
11648
|
-
"",
|
|
11649
|
-
`## Lessons (${stats.lessons})`,
|
|
11650
|
-
...lessonMoc,
|
|
11651
|
-
"",
|
|
11652
|
-
`## Crystals (${stats.crystals})`,
|
|
11653
|
-
...crystalMoc,
|
|
11654
|
-
"",
|
|
11655
|
-
`## Sessions (${stats.sessions})`,
|
|
11656
|
-
...sessionMoc
|
|
11657
|
-
].join("\n");
|
|
11658
|
-
await writeFile(join(vaultDir, "MOC.md"), moc);
|
|
11659
|
-
await recordAudit(kv, "obsidian_export", "mem::obsidian-export", [], {
|
|
11660
|
-
vaultDir,
|
|
11661
|
-
stats
|
|
11662
|
-
});
|
|
11663
|
-
return {
|
|
11664
|
-
success: true,
|
|
11665
|
-
exported: stats,
|
|
11666
|
-
errors: errors.length > 0 ? errors : void 0,
|
|
11667
|
-
vaultDir
|
|
11668
|
-
};
|
|
11669
12088
|
});
|
|
11670
12089
|
}
|
|
11671
|
-
|
|
11672
12090
|
//#endregion
|
|
11673
12091
|
//#region src/prompts/reflect.ts
|
|
11674
12092
|
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.
|
|
@@ -11696,7 +12114,6 @@ function buildReflectPrompt(cluster) {
|
|
|
11696
12114
|
if (cluster.crystalNarratives.length > 0) sections.push("\n## Completed Work Summaries", ...cluster.crystalNarratives.map((n) => `- ${n}`));
|
|
11697
12115
|
return `Synthesize higher-order insights from this cluster of related memories:\n\n${sections.join("\n")}`;
|
|
11698
12116
|
}
|
|
11699
|
-
|
|
11700
12117
|
//#endregion
|
|
11701
12118
|
//#region src/functions/reflect.ts
|
|
11702
12119
|
function reinforceInsight(insight) {
|
|
@@ -11989,7 +12406,6 @@ function registerReflectFunctions(sdk, kv, provider) {
|
|
|
11989
12406
|
};
|
|
11990
12407
|
});
|
|
11991
12408
|
}
|
|
11992
|
-
|
|
11993
12409
|
//#endregion
|
|
11994
12410
|
//#region src/functions/working-memory.ts
|
|
11995
12411
|
const CORE_SCOPE = "mem:core-memory";
|
|
@@ -12164,7 +12580,6 @@ function registerWorkingMemoryFunctions(sdk, kv, tokenBudget) {
|
|
|
12164
12580
|
};
|
|
12165
12581
|
});
|
|
12166
12582
|
}
|
|
12167
|
-
|
|
12168
12583
|
//#endregion
|
|
12169
12584
|
//#region src/functions/skill-extract.ts
|
|
12170
12585
|
const SKILL_EXTRACT_SYSTEM = `You are a skill extraction engine. Given a completed multi-step task session, extract a reusable procedural skill document.
|
|
@@ -12367,7 +12782,6 @@ function registerSkillExtractFunctions(sdk, kv, provider) {
|
|
|
12367
12782
|
};
|
|
12368
12783
|
});
|
|
12369
12784
|
}
|
|
12370
|
-
|
|
12371
12785
|
//#endregion
|
|
12372
12786
|
//#region src/functions/sliding-window.ts
|
|
12373
12787
|
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.
|
|
@@ -12554,7 +12968,6 @@ function registerSlidingWindowFunction(sdk, kv, provider) {
|
|
|
12554
12968
|
};
|
|
12555
12969
|
});
|
|
12556
12970
|
}
|
|
12557
|
-
|
|
12558
12971
|
//#endregion
|
|
12559
12972
|
//#region src/functions/temporal-graph.ts
|
|
12560
12973
|
const TEMPORAL_EXTRACTION_SYSTEM = `You are a temporal knowledge extraction engine. Given observations, extract entities AND their temporal relationships with full context metadata.
|
|
@@ -12818,7 +13231,6 @@ function buildTimeline(edges) {
|
|
|
12818
13231
|
context: e.context
|
|
12819
13232
|
}));
|
|
12820
13233
|
}
|
|
12821
|
-
|
|
12822
13234
|
//#endregion
|
|
12823
13235
|
//#region src/functions/retention.ts
|
|
12824
13236
|
const DEFAULT_DECAY = {
|
|
@@ -12982,7 +13394,7 @@ function registerRetentionFunctions(sdk, kv) {
|
|
|
12982
13394
|
const threshold = typeof data?.threshold === "number" && Number.isFinite(data.threshold) ? data.threshold : DEFAULT_DECAY.tierThresholds.cold;
|
|
12983
13395
|
const maxEvictRaw = typeof data?.maxEvict === "number" && Number.isInteger(data.maxEvict) ? data.maxEvict : 50;
|
|
12984
13396
|
const maxEvict = Math.min(1e3, Math.max(0, maxEvictRaw));
|
|
12985
|
-
const { decrementImageRef } = await import("./image-refs-
|
|
13397
|
+
const { decrementImageRef } = await import("./image-refs-C7h9L5wx.mjs").then((n) => n.n);
|
|
12986
13398
|
const candidates = (await kv.list(KV.retentionScores)).filter((s) => s.score < threshold).sort((a, b) => a.score - b.score).slice(0, maxEvict);
|
|
12987
13399
|
if (data?.dryRun) return {
|
|
12988
13400
|
success: true,
|
|
@@ -13052,7 +13464,6 @@ function registerRetentionFunctions(sdk, kv) {
|
|
|
13052
13464
|
};
|
|
13053
13465
|
});
|
|
13054
13466
|
}
|
|
13055
|
-
|
|
13056
13467
|
//#endregion
|
|
13057
13468
|
//#region src/functions/compress-file.ts
|
|
13058
13469
|
const SENSITIVE_PATH_TERMS = [
|
|
@@ -13194,7 +13605,6 @@ function registerCompressFileFunction(sdk, kv, provider) {
|
|
|
13194
13605
|
};
|
|
13195
13606
|
});
|
|
13196
13607
|
}
|
|
13197
|
-
|
|
13198
13608
|
//#endregion
|
|
13199
13609
|
//#region src/replay/jsonl-parser.ts
|
|
13200
13610
|
function deriveProject(cwd) {
|
|
@@ -13321,7 +13731,6 @@ function parseJsonlText(text, fallbackSessionId) {
|
|
|
13321
13731
|
observations
|
|
13322
13732
|
};
|
|
13323
13733
|
}
|
|
13324
|
-
|
|
13325
13734
|
//#endregion
|
|
13326
13735
|
//#region src/replay/timeline.ts
|
|
13327
13736
|
const DEFAULT_CHARS_PER_SEC = 40;
|
|
@@ -13416,10 +13825,6 @@ function projectTimeline(observations) {
|
|
|
13416
13825
|
events
|
|
13417
13826
|
};
|
|
13418
13827
|
}
|
|
13419
|
-
|
|
13420
|
-
//#endregion
|
|
13421
|
-
//#region src/functions/replay.ts
|
|
13422
|
-
const MAX_FILES_DEFAULT = 200;
|
|
13423
13828
|
const MAX_FILES_UPPER_BOUND = 1e3;
|
|
13424
13829
|
const SENSITIVE_PATH_PATTERNS = [
|
|
13425
13830
|
/(^|[\\/_.-])secret([\\/_.-]|s?$)/i,
|
|
@@ -13639,7 +14044,7 @@ function registerReplayFunctions(sdk, kv) {
|
|
|
13639
14044
|
error: "path not found"
|
|
13640
14045
|
};
|
|
13641
14046
|
}
|
|
13642
|
-
const maxFiles = Number.isInteger(data.maxFiles) && data.maxFiles > 0 ? Math.min(data.maxFiles, MAX_FILES_UPPER_BOUND) :
|
|
14047
|
+
const maxFiles = Number.isInteger(data.maxFiles) && data.maxFiles > 0 ? Math.min(data.maxFiles, MAX_FILES_UPPER_BOUND) : 200;
|
|
13643
14048
|
let files = [];
|
|
13644
14049
|
let truncated = false;
|
|
13645
14050
|
let discovered = 0;
|
|
@@ -13695,7 +14100,8 @@ function registerReplayFunctions(sdk, kv) {
|
|
|
13695
14100
|
const existingTags = existing.tags || [];
|
|
13696
14101
|
if (!existingTags.includes("jsonl-import")) existing.tags = [...existingTags, "jsonl-import"];
|
|
13697
14102
|
if (!existing.firstPrompt && firstPrompt) existing.firstPrompt = firstPrompt;
|
|
13698
|
-
|
|
14103
|
+
if (!existing.id) existing.id = parsed.sessionId;
|
|
14104
|
+
await kv.set(KV.sessions, parsed.sessionId, existing);
|
|
13699
14105
|
} else {
|
|
13700
14106
|
const session = {
|
|
13701
14107
|
id: parsed.sessionId,
|
|
@@ -13741,7 +14147,6 @@ function registerReplayFunctions(sdk, kv) {
|
|
|
13741
14147
|
};
|
|
13742
14148
|
});
|
|
13743
14149
|
}
|
|
13744
|
-
|
|
13745
14150
|
//#endregion
|
|
13746
14151
|
//#region src/health/thresholds.ts
|
|
13747
14152
|
const DEFAULTS = {
|
|
@@ -13800,7 +14205,6 @@ function evaluateHealth(snapshot, config = {}) {
|
|
|
13800
14205
|
notes
|
|
13801
14206
|
};
|
|
13802
14207
|
}
|
|
13803
|
-
|
|
13804
14208
|
//#endregion
|
|
13805
14209
|
//#region src/health/monitor.ts
|
|
13806
14210
|
function registerHealthMonitor(sdk, kv) {
|
|
@@ -13888,7 +14292,6 @@ function registerHealthMonitor(sdk, kv) {
|
|
|
13888
14292
|
async function getLatestHealth(kv) {
|
|
13889
14293
|
return kv.get(KV.health, "latest");
|
|
13890
14294
|
}
|
|
13891
|
-
|
|
13892
14295
|
//#endregion
|
|
13893
14296
|
//#region src/auth.ts
|
|
13894
14297
|
const hmacKey = randomBytes(32);
|
|
@@ -13914,7 +14317,6 @@ function buildViewerCsp(nonce) {
|
|
|
13914
14317
|
"font-src 'self'"
|
|
13915
14318
|
].join("; ");
|
|
13916
14319
|
}
|
|
13917
|
-
|
|
13918
14320
|
//#endregion
|
|
13919
14321
|
//#region src/viewer/document.ts
|
|
13920
14322
|
const VIEWER_VERSION_PLACEHOLDER = "__AGENTMEMORY_VERSION__";
|
|
@@ -13940,7 +14342,6 @@ function renderViewerDocument() {
|
|
|
13940
14342
|
csp: buildViewerCsp(nonce)
|
|
13941
14343
|
};
|
|
13942
14344
|
}
|
|
13943
|
-
|
|
13944
14345
|
//#endregion
|
|
13945
14346
|
//#region src/viewer/server.ts
|
|
13946
14347
|
function loadViewerFavicon() {
|
|
@@ -13955,18 +14356,30 @@ function loadViewerFavicon() {
|
|
|
13955
14356
|
} catch {}
|
|
13956
14357
|
return null;
|
|
13957
14358
|
}
|
|
14359
|
+
const VIEWER_FAVICON = loadViewerFavicon();
|
|
13958
14360
|
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());
|
|
13959
|
-
|
|
13960
|
-
|
|
14361
|
+
function readAllowedHostsOverride() {
|
|
14362
|
+
return (process.env.VIEWER_ALLOWED_HOSTS || "").split(",").map((h) => h.trim().toLowerCase()).filter(Boolean);
|
|
14363
|
+
}
|
|
14364
|
+
function resolveViewerHost() {
|
|
14365
|
+
return process.env.AGENTMEMORY_VIEWER_HOST?.trim() || "127.0.0.1";
|
|
14366
|
+
}
|
|
14367
|
+
function isLoopbackHost(host) {
|
|
14368
|
+
const h = host.trim().toLowerCase();
|
|
14369
|
+
return h === "127.0.0.1" || h === "::1" || h === "localhost";
|
|
14370
|
+
}
|
|
14371
|
+
function buildAllowedHosts(origins, listenPort, bindHost = "127.0.0.1") {
|
|
13961
14372
|
const hosts = /* @__PURE__ */ new Set();
|
|
13962
|
-
|
|
13963
|
-
const
|
|
13964
|
-
|
|
13965
|
-
|
|
13966
|
-
|
|
13967
|
-
|
|
13968
|
-
|
|
13969
|
-
|
|
14373
|
+
if (isLoopbackHost(bindHost)) {
|
|
14374
|
+
for (const o of origins) try {
|
|
14375
|
+
const parsed = new URL(o);
|
|
14376
|
+
if (parsed.host) hosts.add(parsed.host.toLowerCase());
|
|
14377
|
+
} catch {}
|
|
14378
|
+
hosts.add(`localhost:${listenPort}`);
|
|
14379
|
+
hosts.add(`127.0.0.1:${listenPort}`);
|
|
14380
|
+
hosts.add(`[::1]:${listenPort}`);
|
|
14381
|
+
}
|
|
14382
|
+
for (const h of readAllowedHostsOverride()) hosts.add(h);
|
|
13970
14383
|
return hosts;
|
|
13971
14384
|
}
|
|
13972
14385
|
function isHostAllowed(headerHost, allowed) {
|
|
@@ -13975,11 +14388,16 @@ function isHostAllowed(headerHost, allowed) {
|
|
|
13975
14388
|
if (!lower) return false;
|
|
13976
14389
|
return allowed.has(lower);
|
|
13977
14390
|
}
|
|
14391
|
+
function requireInboundBearer(authHeader, secret) {
|
|
14392
|
+
if (typeof authHeader !== "string") return false;
|
|
14393
|
+
const match = /^Bearer\s+(\S+)\s*$/i.exec(authHeader);
|
|
14394
|
+
if (!match) return false;
|
|
14395
|
+
return timingSafeCompare(match[1], secret);
|
|
14396
|
+
}
|
|
13978
14397
|
function corsHeaders(req) {
|
|
13979
14398
|
const origin = req.headers.origin || "";
|
|
13980
|
-
const allowed = ALLOWED_ORIGINS.includes(origin) ? origin : ALLOWED_ORIGINS[0];
|
|
13981
14399
|
return {
|
|
13982
|
-
"Access-Control-Allow-Origin":
|
|
14400
|
+
"Access-Control-Allow-Origin": ALLOWED_ORIGINS.includes(origin) ? origin : ALLOWED_ORIGINS[0],
|
|
13983
14401
|
"Access-Control-Allow-Methods": "GET, POST, DELETE, OPTIONS",
|
|
13984
14402
|
"Access-Control-Allow-Headers": "Content-Type, Authorization",
|
|
13985
14403
|
Vary: "Origin"
|
|
@@ -14023,16 +14441,29 @@ function getBoundViewerPort() {
|
|
|
14023
14441
|
function getViewerSkipped() {
|
|
14024
14442
|
return viewerSkipped;
|
|
14025
14443
|
}
|
|
14444
|
+
var ViewerConfigError = class extends Error {
|
|
14445
|
+
constructor(message) {
|
|
14446
|
+
super(message);
|
|
14447
|
+
this.name = "ViewerConfigError";
|
|
14448
|
+
}
|
|
14449
|
+
};
|
|
14026
14450
|
function startViewerServer(port, _kv, _sdk, secret, restPort) {
|
|
14027
14451
|
boundViewerPort = null;
|
|
14028
14452
|
viewerSkipped = false;
|
|
14029
14453
|
const resolvedRestPort = restPort ?? port - 2;
|
|
14030
14454
|
const requestedPort = port;
|
|
14455
|
+
const host = resolveViewerHost();
|
|
14456
|
+
let inboundSecret = null;
|
|
14457
|
+
if (!isLoopbackHost(host)) {
|
|
14458
|
+
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.`);
|
|
14459
|
+
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.`);
|
|
14460
|
+
inboundSecret = secret;
|
|
14461
|
+
}
|
|
14031
14462
|
let allowedHosts = null;
|
|
14032
14463
|
const server = createServer(async (req, res) => {
|
|
14033
14464
|
if (!allowedHosts) {
|
|
14034
14465
|
const addr = server.address();
|
|
14035
|
-
allowedHosts = buildAllowedHosts(ALLOWED_ORIGINS, addr && typeof addr === "object" && "port" in addr ? addr.port : port);
|
|
14466
|
+
allowedHosts = buildAllowedHosts(ALLOWED_ORIGINS, addr && typeof addr === "object" && "port" in addr ? addr.port : port, host);
|
|
14036
14467
|
}
|
|
14037
14468
|
if (!isHostAllowed(req.headers.host, allowedHosts)) {
|
|
14038
14469
|
res.writeHead(403, { "Content-Type": "text/plain" });
|
|
@@ -14068,19 +14499,26 @@ function startViewerServer(port, _kv, _sdk, secret, restPort) {
|
|
|
14068
14499
|
return;
|
|
14069
14500
|
}
|
|
14070
14501
|
if (method === "GET" && pathname === "/favicon.svg") {
|
|
14071
|
-
|
|
14072
|
-
if (favicon) {
|
|
14502
|
+
if (VIEWER_FAVICON) {
|
|
14073
14503
|
res.writeHead(200, {
|
|
14074
14504
|
"Content-Type": "image/svg+xml",
|
|
14075
14505
|
"Cache-Control": "public, max-age=3600"
|
|
14076
14506
|
});
|
|
14077
|
-
res.end(
|
|
14507
|
+
res.end(VIEWER_FAVICON);
|
|
14078
14508
|
return;
|
|
14079
14509
|
}
|
|
14080
14510
|
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
14081
14511
|
res.end("favicon not found");
|
|
14082
14512
|
return;
|
|
14083
14513
|
}
|
|
14514
|
+
if (inboundSecret !== null && !requireInboundBearer(req.headers.authorization, inboundSecret)) {
|
|
14515
|
+
res.writeHead(401, {
|
|
14516
|
+
"Content-Type": "text/plain",
|
|
14517
|
+
"WWW-Authenticate": "Bearer realm=\"agentmemory-viewer\""
|
|
14518
|
+
});
|
|
14519
|
+
res.end("unauthorized");
|
|
14520
|
+
return;
|
|
14521
|
+
}
|
|
14084
14522
|
try {
|
|
14085
14523
|
await proxyToRestApi(resolvedRestPort, pathname, qs, method, req, res, secret);
|
|
14086
14524
|
} catch (err) {
|
|
@@ -14091,17 +14529,23 @@ function startViewerServer(port, _kv, _sdk, secret, restPort) {
|
|
|
14091
14529
|
let attempt = 0;
|
|
14092
14530
|
let currentPort = requestedPort;
|
|
14093
14531
|
const tryListen = () => {
|
|
14094
|
-
server.listen(currentPort,
|
|
14532
|
+
server.listen(currentPort, host);
|
|
14095
14533
|
};
|
|
14096
14534
|
server.on("listening", () => {
|
|
14097
14535
|
const addr = server.address();
|
|
14098
|
-
|
|
14536
|
+
const actualPort = addr && typeof addr === "object" && "port" in addr ? addr.port : currentPort;
|
|
14537
|
+
boundViewerPort = actualPort;
|
|
14099
14538
|
viewerSkipped = false;
|
|
14100
|
-
if (
|
|
14101
|
-
|
|
14539
|
+
if (inboundSecret !== null) {
|
|
14540
|
+
const allowedHosts = readAllowedHostsOverride().join(", ");
|
|
14541
|
+
console.log(`[agentmemory] Viewer: http://localhost:${actualPort} (bound to ${host}; inbound Bearer required; allowed Host headers: ${allowedHosts})`);
|
|
14542
|
+
return;
|
|
14543
|
+
}
|
|
14544
|
+
if (actualPort === requestedPort) console.log(`[agentmemory] Viewer: http://localhost:${actualPort}`);
|
|
14545
|
+
else console.log(`[agentmemory] Viewer started on http://localhost:${actualPort} (fallback from ${requestedPort})`);
|
|
14102
14546
|
});
|
|
14103
14547
|
server.on("error", (err) => {
|
|
14104
|
-
if (err.code === "EADDRINUSE" && attempt < MAX_VIEWER_PORT_RETRIES) {
|
|
14548
|
+
if (err.code === "EADDRINUSE" && inboundSecret === null && attempt < MAX_VIEWER_PORT_RETRIES) {
|
|
14105
14549
|
attempt++;
|
|
14106
14550
|
currentPort = requestedPort + attempt;
|
|
14107
14551
|
setImmediate(tryListen);
|
|
@@ -14110,7 +14554,8 @@ function startViewerServer(port, _kv, _sdk, secret, restPort) {
|
|
|
14110
14554
|
if (err.code === "EADDRINUSE") {
|
|
14111
14555
|
boundViewerPort = null;
|
|
14112
14556
|
viewerSkipped = true;
|
|
14113
|
-
console.warn(`[agentmemory] Viewer
|
|
14557
|
+
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.`);
|
|
14558
|
+
else console.warn(`[agentmemory] Viewer ports ${requestedPort}-${requestedPort + MAX_VIEWER_PORT_RETRIES} all in use, skipping viewer.`);
|
|
14114
14559
|
} else {
|
|
14115
14560
|
boundViewerPort = null;
|
|
14116
14561
|
viewerSkipped = true;
|
|
@@ -14155,7 +14600,6 @@ async function proxyToRestApi(restPort, pathname, qs, method, req, res, secret)
|
|
|
14155
14600
|
res.writeHead(upstream.status, responseHeaders);
|
|
14156
14601
|
res.end(responseBody);
|
|
14157
14602
|
}
|
|
14158
|
-
|
|
14159
14603
|
//#endregion
|
|
14160
14604
|
//#region src/triggers/api.ts
|
|
14161
14605
|
function parseOptionalInt(raw) {
|
|
@@ -14578,7 +15022,7 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
14578
15022
|
}
|
|
14579
15023
|
if (body.maxFiles !== void 0) {
|
|
14580
15024
|
const n = body.maxFiles;
|
|
14581
|
-
if (!Number.isInteger(n) || n < 1 || n >
|
|
15025
|
+
if (!Number.isInteger(n) || n < 1 || n > 1e3) return {
|
|
14582
15026
|
status_code: 400,
|
|
14583
15027
|
body: { error: `maxFiles must be an integer between 1 and ${MAX_FILES_UPPER_BOUND}` }
|
|
14584
15028
|
};
|
|
@@ -14662,9 +15106,13 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
14662
15106
|
value: "completed"
|
|
14663
15107
|
}]);
|
|
14664
15108
|
try {
|
|
14665
|
-
sdk.
|
|
15109
|
+
sdk.trigger({
|
|
15110
|
+
function_id: "event::session::stopped",
|
|
15111
|
+
payload: { sessionId },
|
|
15112
|
+
action: TriggerAction.Void()
|
|
15113
|
+
});
|
|
14666
15114
|
} catch (err) {
|
|
14667
|
-
logger.warn("event::session::stopped
|
|
15115
|
+
logger.warn("event::session::stopped trigger failed", {
|
|
14668
15116
|
sessionId,
|
|
14669
15117
|
error: err instanceof Error ? err.message : String(err)
|
|
14670
15118
|
});
|
|
@@ -15084,11 +15532,24 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
15084
15532
|
status_code: 400,
|
|
15085
15533
|
body: { error: "query or expandIds is required" }
|
|
15086
15534
|
};
|
|
15535
|
+
const headers = req.headers || {};
|
|
15536
|
+
const sourceHeader = headers["x-agentmemory-source"] ?? headers["X-Agentmemory-Source"];
|
|
15537
|
+
const sourceFromHeader = Array.isArray(sourceHeader) ? sourceHeader[0] : sourceHeader;
|
|
15538
|
+
const payload = {
|
|
15539
|
+
query: req.body?.query,
|
|
15540
|
+
expandIds: req.body?.expandIds,
|
|
15541
|
+
limit: req.body?.limit,
|
|
15542
|
+
project: req.body?.project,
|
|
15543
|
+
includeLessons: req.body?.includeLessons,
|
|
15544
|
+
agentId: req.body?.agentId,
|
|
15545
|
+
sessionId: req.body?.sessionId,
|
|
15546
|
+
source: req.body?.source ?? sourceFromHeader
|
|
15547
|
+
};
|
|
15087
15548
|
return {
|
|
15088
15549
|
status_code: 200,
|
|
15089
15550
|
body: await sdk.trigger({
|
|
15090
15551
|
function_id: "mem::smart-search",
|
|
15091
|
-
payload
|
|
15552
|
+
payload
|
|
15092
15553
|
})
|
|
15093
15554
|
};
|
|
15094
15555
|
});
|
|
@@ -15100,6 +15561,29 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
15100
15561
|
http_method: "POST"
|
|
15101
15562
|
}
|
|
15102
15563
|
});
|
|
15564
|
+
sdk.registerFunction("api::diagnostic-followup", async (req) => {
|
|
15565
|
+
const authErr = checkAuth(req, secret);
|
|
15566
|
+
if (authErr) return authErr;
|
|
15567
|
+
return {
|
|
15568
|
+
status_code: 200,
|
|
15569
|
+
body: {
|
|
15570
|
+
...await sdk.trigger({
|
|
15571
|
+
function_id: "mem::diagnostic::followup-stats",
|
|
15572
|
+
payload: {}
|
|
15573
|
+
}),
|
|
15574
|
+
caveat: "Directional signal: overcounts on legitimate query refinement. Tune via AGENTMEMORY_FOLLOWUP_WINDOW_SECONDS."
|
|
15575
|
+
}
|
|
15576
|
+
};
|
|
15577
|
+
});
|
|
15578
|
+
sdk.registerTrigger({
|
|
15579
|
+
type: "http",
|
|
15580
|
+
function_id: "api::diagnostic-followup",
|
|
15581
|
+
config: {
|
|
15582
|
+
api_path: "/agentmemory/diagnostics/followup",
|
|
15583
|
+
http_method: "GET",
|
|
15584
|
+
middleware_function_ids: ["middleware::api-auth"]
|
|
15585
|
+
}
|
|
15586
|
+
});
|
|
15103
15587
|
sdk.registerFunction("api::timeline", async (req) => {
|
|
15104
15588
|
const authErr = checkAuth(req, secret);
|
|
15105
15589
|
if (authErr) return authErr;
|
|
@@ -15321,12 +15805,20 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
15321
15805
|
sdk.registerFunction("api::graph-query", async (req) => {
|
|
15322
15806
|
const authErr = checkAuth(req, secret);
|
|
15323
15807
|
if (authErr) return authErr;
|
|
15808
|
+
const payload = {
|
|
15809
|
+
startNodeId: req.body?.startNodeId,
|
|
15810
|
+
nodeType: req.body?.nodeType,
|
|
15811
|
+
maxDepth: req.body?.maxDepth,
|
|
15812
|
+
query: req.body?.query,
|
|
15813
|
+
limit: req.body?.limit,
|
|
15814
|
+
offset: req.body?.offset
|
|
15815
|
+
};
|
|
15324
15816
|
try {
|
|
15325
15817
|
return {
|
|
15326
15818
|
status_code: 200,
|
|
15327
15819
|
body: await sdk.trigger({
|
|
15328
15820
|
function_id: "mem::graph-query",
|
|
15329
|
-
payload
|
|
15821
|
+
payload
|
|
15330
15822
|
})
|
|
15331
15823
|
};
|
|
15332
15824
|
} catch {
|
|
@@ -17607,7 +18099,6 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
17607
18099
|
}
|
|
17608
18100
|
});
|
|
17609
18101
|
}
|
|
17610
|
-
|
|
17611
18102
|
//#endregion
|
|
17612
18103
|
//#region src/triggers/events.ts
|
|
17613
18104
|
function registerEventTriggers(sdk, kv) {
|
|
@@ -17652,18 +18143,26 @@ function registerEventTriggers(sdk, kv) {
|
|
|
17652
18143
|
payload: data
|
|
17653
18144
|
});
|
|
17654
18145
|
if (isReflectEnabled()) try {
|
|
17655
|
-
sdk.
|
|
18146
|
+
sdk.trigger({
|
|
18147
|
+
function_id: "mem::slot-reflect",
|
|
18148
|
+
payload: { sessionId: data.sessionId },
|
|
18149
|
+
action: TriggerAction.Void()
|
|
18150
|
+
});
|
|
17656
18151
|
} catch (err) {
|
|
17657
|
-
logger.warn("slot-reflect
|
|
18152
|
+
logger.warn("slot-reflect trigger failed", {
|
|
17658
18153
|
sessionId: data.sessionId,
|
|
17659
18154
|
error: err instanceof Error ? err.message : String(err)
|
|
17660
18155
|
});
|
|
17661
18156
|
}
|
|
17662
18157
|
if (isGraphExtractionEnabled()) try {
|
|
17663
18158
|
const compressed = (await kv.list(KV.observations(data.sessionId))).filter((o) => o.title);
|
|
17664
|
-
if (compressed.length > 0) sdk.
|
|
18159
|
+
if (compressed.length > 0) sdk.trigger({
|
|
18160
|
+
function_id: "mem::graph-extract",
|
|
18161
|
+
payload: { observations: compressed },
|
|
18162
|
+
action: TriggerAction.Void()
|
|
18163
|
+
});
|
|
17665
18164
|
} catch (err) {
|
|
17666
|
-
logger.warn("graph-extract
|
|
18165
|
+
logger.warn("graph-extract trigger failed", {
|
|
17667
18166
|
sessionId: data.sessionId,
|
|
17668
18167
|
error: err instanceof Error ? err.message : String(err)
|
|
17669
18168
|
});
|
|
@@ -17721,7 +18220,6 @@ function registerEventTriggers(sdk, kv) {
|
|
|
17721
18220
|
config: { scope: KV.sessions }
|
|
17722
18221
|
});
|
|
17723
18222
|
}
|
|
17724
|
-
|
|
17725
18223
|
//#endregion
|
|
17726
18224
|
//#region src/mcp/server.ts
|
|
17727
18225
|
function asNonEmptyString(value) {
|
|
@@ -19358,7 +19856,6 @@ function registerMcpEndpoints(sdk, kv, secret) {
|
|
|
19358
19856
|
}
|
|
19359
19857
|
});
|
|
19360
19858
|
}
|
|
19361
|
-
|
|
19362
19859
|
//#endregion
|
|
19363
19860
|
//#region src/eval/metrics-store.ts
|
|
19364
19861
|
var MetricsStore = class {
|
|
@@ -19401,7 +19898,6 @@ var MetricsStore = class {
|
|
|
19401
19898
|
return Array.from(merged.values());
|
|
19402
19899
|
}
|
|
19403
19900
|
};
|
|
19404
|
-
|
|
19405
19901
|
//#endregion
|
|
19406
19902
|
//#region src/functions/dedup.ts
|
|
19407
19903
|
const TTL_MS = 300 * 1e3;
|
|
@@ -19443,57 +19939,6 @@ var DedupMap = class {
|
|
|
19443
19939
|
return this.entries.size;
|
|
19444
19940
|
}
|
|
19445
19941
|
};
|
|
19446
|
-
|
|
19447
|
-
//#endregion
|
|
19448
|
-
//#region src/telemetry/setup.ts
|
|
19449
|
-
const OTEL_CONFIG = {
|
|
19450
|
-
serviceName: "agentmemory",
|
|
19451
|
-
serviceVersion: VERSION,
|
|
19452
|
-
metricsExportIntervalMs: 3e4
|
|
19453
|
-
};
|
|
19454
|
-
let counters = null;
|
|
19455
|
-
let histograms = null;
|
|
19456
|
-
const NOOP_COUNTER = { add: () => {} };
|
|
19457
|
-
const NOOP_HISTOGRAM = { record: () => {} };
|
|
19458
|
-
const COUNTER_NAMES = [
|
|
19459
|
-
["observationsTotal", "observations.total"],
|
|
19460
|
-
["compressionSuccess", "compression.success"],
|
|
19461
|
-
["compressionFailure", "compression.failure"],
|
|
19462
|
-
["searchTotal", "search.total"],
|
|
19463
|
-
["dedupSkipped", "dedup.skipped"],
|
|
19464
|
-
["evictionTotal", "eviction.total"],
|
|
19465
|
-
["circuitBreakerOpen", "circuit_breaker.open"],
|
|
19466
|
-
["embeddingSuccess", "embedding.success"],
|
|
19467
|
-
["embeddingFailure", "embedding.failure"],
|
|
19468
|
-
["vectorSearchTotal", "vector_search.total"],
|
|
19469
|
-
["autoForgetTotal", "auto_forget.total"],
|
|
19470
|
-
["profileGenerated", "profile.generated"],
|
|
19471
|
-
["claudeBridgeSync", "claude_bridge.sync"],
|
|
19472
|
-
["graphExtraction", "graph.extraction"],
|
|
19473
|
-
["consolidationRun", "consolidation.run"],
|
|
19474
|
-
["teamShare", "team.share"],
|
|
19475
|
-
["auditLog", "audit.log"],
|
|
19476
|
-
["snapshotCreate", "snapshot.create"],
|
|
19477
|
-
["governanceDelete", "governance.delete"]
|
|
19478
|
-
];
|
|
19479
|
-
const HISTOGRAM_NAMES = [
|
|
19480
|
-
["compressionLatency", "compression.latency_ms"],
|
|
19481
|
-
["searchLatency", "search.latency_ms"],
|
|
19482
|
-
["contextTokens", "context.tokens"],
|
|
19483
|
-
["qualityScore", "quality.score"],
|
|
19484
|
-
["embeddingLatency", "embedding.latency_ms"],
|
|
19485
|
-
["vectorSearchLatency", "vector_search.latency_ms"]
|
|
19486
|
-
];
|
|
19487
|
-
function initMetrics(getMeter) {
|
|
19488
|
-
const meter = getMeter?.("agentmemory");
|
|
19489
|
-
counters = Object.fromEntries(COUNTER_NAMES.map(([key, name]) => [key, meter ? meter.createCounter(name) : NOOP_COUNTER]));
|
|
19490
|
-
histograms = Object.fromEntries(HISTOGRAM_NAMES.map(([key, name]) => [key, meter ? meter.createHistogram(name) : NOOP_HISTOGRAM]));
|
|
19491
|
-
return {
|
|
19492
|
-
counters,
|
|
19493
|
-
histograms
|
|
19494
|
-
};
|
|
19495
|
-
}
|
|
19496
|
-
|
|
19497
19942
|
//#endregion
|
|
19498
19943
|
//#region src/index.ts
|
|
19499
19944
|
function workerPidfilePath() {
|
|
@@ -19642,6 +20087,7 @@ async function main() {
|
|
|
19642
20087
|
const graphWeight = parseFloat(getEnvVar("AGENTMEMORY_GRAPH_WEIGHT") || "0.3");
|
|
19643
20088
|
const hybridSearch = new HybridSearch(bm25Index, vectorIndex, embeddingProvider, kv, embeddingConfig.bm25Weight, embeddingConfig.vectorWeight, graphWeight);
|
|
19644
20089
|
registerSmartSearchFunction(sdk, kv, (query, limit) => hybridSearch.search(query, limit));
|
|
20090
|
+
registerRecentSearchesSweepFunction(sdk, kv);
|
|
19645
20091
|
registerApiTriggers(sdk, kv, secret, metricsStore, provider);
|
|
19646
20092
|
registerEventTriggers(sdk, kv);
|
|
19647
20093
|
registerMcpEndpoints(sdk, kv, secret);
|
|
@@ -19709,7 +20155,7 @@ async function main() {
|
|
|
19709
20155
|
console.warn(`[agentmemory] Failed to backfill memories into BM25:`, err);
|
|
19710
20156
|
}
|
|
19711
20157
|
bootLog(`Ready. ${embeddingProvider ? "Triple-stream (BM25+Vector+Graph)" : "BM25+Graph"} search active.`);
|
|
19712
|
-
bootLog(`REST API:
|
|
20158
|
+
bootLog(`REST API: 126 endpoints at http://localhost:${config.restPort}/agentmemory/*`);
|
|
19713
20159
|
bootLog(`MCP surface (opt-in via \`npx @agentmemory/mcp\`): ${getAllTools().length} tools · 6 resources · 3 prompts`);
|
|
19714
20160
|
const viewerServer = startViewerServer(config.restPort + 2, kv, sdk, secret, config.restPort);
|
|
19715
20161
|
const autoForgetIntervalMs = parseInt(process.env.AUTO_FORGET_INTERVAL_MS || "3600000", 10);
|
|
@@ -19744,6 +20190,14 @@ async function main() {
|
|
|
19744
20190
|
});
|
|
19745
20191
|
} catch {}
|
|
19746
20192
|
}, 864e5).unref();
|
|
20193
|
+
setInterval(async () => {
|
|
20194
|
+
try {
|
|
20195
|
+
await sdk.trigger({
|
|
20196
|
+
function_id: "mem::diagnostic::recent-searches-sweep",
|
|
20197
|
+
payload: {}
|
|
20198
|
+
});
|
|
20199
|
+
} catch {}
|
|
20200
|
+
}, 3600 * 1e3).unref();
|
|
19747
20201
|
if (isConsolidationEnabled()) {
|
|
19748
20202
|
setInterval(async () => {
|
|
19749
20203
|
try {
|
|
@@ -19775,7 +20229,7 @@ main().catch((err) => {
|
|
|
19775
20229
|
console.error(`[agentmemory] Fatal:`, err);
|
|
19776
20230
|
process.exit(1);
|
|
19777
20231
|
});
|
|
19778
|
-
|
|
19779
20232
|
//#endregion
|
|
19780
|
-
export {
|
|
19781
|
-
|
|
20233
|
+
export {};
|
|
20234
|
+
|
|
20235
|
+
//# sourceMappingURL=src-fQOMXeCp.mjs.map
|