@agentmemory/agentmemory 0.9.24 → 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 +125 -65
- package/dist/cli.mjs.map +1 -1
- package/dist/{connect-Cf9bmBqO.mjs → connect-bmZ5eqYN.mjs} +17 -56
- package/dist/{connect-Cf9bmBqO.mjs.map → connect-bmZ5eqYN.mjs.map} +1 -1
- package/dist/hooks/notification.mjs +2 -4
- package/dist/hooks/notification.mjs.map +1 -1
- package/dist/hooks/post-commit.mjs +2 -3
- package/dist/hooks/post-commit.mjs.map +1 -1
- package/dist/hooks/post-tool-failure.mjs +2 -4
- package/dist/hooks/post-tool-failure.mjs.map +1 -1
- package/dist/hooks/post-tool-use.mjs +2 -4
- package/dist/hooks/post-tool-use.mjs.map +1 -1
- package/dist/hooks/pre-compact.mjs +2 -4
- package/dist/hooks/pre-compact.mjs.map +1 -1
- package/dist/hooks/pre-tool-use.mjs +2 -2
- package/dist/hooks/pre-tool-use.mjs.map +1 -1
- package/dist/hooks/prompt-submit.mjs +2 -4
- package/dist/hooks/prompt-submit.mjs.map +1 -1
- package/dist/hooks/session-end.mjs +2 -2
- package/dist/hooks/session-start.mjs +2 -4
- package/dist/hooks/session-start.mjs.map +1 -1
- package/dist/hooks/stop.mjs +2 -2
- package/dist/hooks/subagent-start.mjs +2 -4
- package/dist/hooks/subagent-start.mjs.map +1 -1
- package/dist/hooks/subagent-stop.mjs +2 -4
- package/dist/hooks/subagent-stop.mjs.map +1 -1
- package/dist/hooks/task-completed.mjs +2 -4
- package/dist/hooks/task-completed.mjs.map +1 -1
- package/dist/image-refs-C7h9L5wx.mjs +52 -0
- package/dist/image-refs-C7h9L5wx.mjs.map +1 -0
- package/dist/{image-refs-CJS5B9Gq.mjs → image-store-Gpo2mgM9.mjs} +11 -42
- package/dist/image-store-Gpo2mgM9.mjs.map +1 -0
- package/dist/index.mjs +941 -493
- package/dist/index.mjs.map +1 -1
- package/dist/{logger-xlVlvCWX.mjs → logger-yHTcEBAI.mjs} +2 -2
- package/dist/{logger-xlVlvCWX.mjs.map → logger-yHTcEBAI.mjs.map} +1 -1
- package/dist/rolldown-runtime-twds-ZHy.mjs +14 -0
- package/dist/{schema-BkALl7Z_.mjs → schema-Dsr_V2Wp.mjs} +4 -4
- package/dist/schema-Dsr_V2Wp.mjs.map +1 -0
- package/dist/{src-B8J9Exum.mjs → src-fQOMXeCp.mjs} +936 -483
- package/dist/src-fQOMXeCp.mjs.map +1 -0
- package/dist/{standalone-CPfsVTBA.mjs → standalone-BzfA1zu8.mjs} +6 -10
- package/dist/{standalone-CPfsVTBA.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-B8J9Exum.mjs.map +0 -1
- package/dist/tools-registry-DJizX9Az.mjs.map +0 -1
- package/dist/version-BWEBnKAp.mjs +0 -6
- package/dist/version-BWEBnKAp.mjs.map +0 -1
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { a as jaccardSimilarity, i as generateId, n as STREAM, r as fingerprintId, t as KV } from "./schema-
|
|
2
|
-
import { n as logger, t as bootLog } from "./logger-
|
|
3
|
-
import { t as VERSION } from "./version-
|
|
4
|
-
import { a as isManagedImagePath,
|
|
5
|
-
import {
|
|
1
|
+
import { a as jaccardSimilarity, i as generateId, n as STREAM, r as fingerprintId, t as KV } from "./schema-Dsr_V2Wp.mjs";
|
|
2
|
+
import { n as logger, t as bootLog } from "./logger-yHTcEBAI.mjs";
|
|
3
|
+
import { t as VERSION } from "./version-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) {
|
|
@@ -6446,7 +6827,8 @@ function registerExportImportFunction(sdk, kv) {
|
|
|
6446
6827
|
"0.9.21",
|
|
6447
6828
|
"0.9.22",
|
|
6448
6829
|
"0.9.23",
|
|
6449
|
-
"0.9.24"
|
|
6830
|
+
"0.9.24",
|
|
6831
|
+
"0.9.25"
|
|
6450
6832
|
]).has(importData.version)) return {
|
|
6451
6833
|
success: false,
|
|
6452
6834
|
error: `Unsupported export version: ${importData.version}`
|
|
@@ -6764,7 +7146,6 @@ function registerExportImportFunction(sdk, kv) {
|
|
|
6764
7146
|
};
|
|
6765
7147
|
});
|
|
6766
7148
|
}
|
|
6767
|
-
|
|
6768
7149
|
//#endregion
|
|
6769
7150
|
//#region src/functions/enrich.ts
|
|
6770
7151
|
const MAX_CONTEXT_LENGTH = 4e3;
|
|
@@ -6825,7 +7206,6 @@ function registerEnrichFunction(sdk, kv) {
|
|
|
6825
7206
|
};
|
|
6826
7207
|
});
|
|
6827
7208
|
}
|
|
6828
|
-
|
|
6829
7209
|
//#endregion
|
|
6830
7210
|
//#region src/functions/claude-bridge.ts
|
|
6831
7211
|
function parseMemoryMd(content) {
|
|
@@ -6946,7 +7326,6 @@ function registerClaudeBridgeFunction(sdk, kv, config) {
|
|
|
6946
7326
|
}
|
|
6947
7327
|
});
|
|
6948
7328
|
}
|
|
6949
|
-
|
|
6950
7329
|
//#endregion
|
|
6951
7330
|
//#region src/prompts/graph-extraction.ts
|
|
6952
7331
|
const GRAPH_EXTRACTION_SYSTEM = `You are a knowledge graph extraction engine. Given a compressed observation from a coding session, extract entities and relationships.
|
|
@@ -6969,9 +7348,41 @@ Rules:
|
|
|
6969
7348
|
function buildGraphExtractionPrompt(observations) {
|
|
6970
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")}`;
|
|
6971
7350
|
}
|
|
6972
|
-
|
|
6973
7351
|
//#endregion
|
|
6974
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
|
+
}
|
|
6975
7386
|
function parseAttrs(raw) {
|
|
6976
7387
|
const attrs = {};
|
|
6977
7388
|
const attrRegex = /([A-Za-z_][\w:-]*)="([^"]*)"/g;
|
|
@@ -7108,15 +7519,10 @@ function registerGraphFunction(sdk, kv, provider) {
|
|
|
7108
7519
|
const allNodes = (await kv.list(KV.graphNodes)).filter((n) => !n.stale);
|
|
7109
7520
|
const allEdges = (await kv.list(KV.graphEdges)).filter((e) => !e.stale);
|
|
7110
7521
|
const maxDepth = Math.min(data.maxDepth || 3, 5);
|
|
7522
|
+
const { limit, offset } = resolvePagination(data.limit, data.offset);
|
|
7111
7523
|
if (data.query) {
|
|
7112
7524
|
const lower = data.query.toLowerCase();
|
|
7113
|
-
|
|
7114
|
-
const nodeIds = new Set(matchingNodes.map((n) => n.id));
|
|
7115
|
-
return {
|
|
7116
|
-
nodes: matchingNodes,
|
|
7117
|
-
edges: allEdges.filter((e) => nodeIds.has(e.sourceNodeId) || nodeIds.has(e.targetNodeId)),
|
|
7118
|
-
depth: 0
|
|
7119
|
-
};
|
|
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);
|
|
7120
7526
|
}
|
|
7121
7527
|
if (data.startNodeId) {
|
|
7122
7528
|
const visited = /* @__PURE__ */ new Set();
|
|
@@ -7148,19 +7554,11 @@ function registerGraphFunction(sdk, kv, provider) {
|
|
|
7148
7554
|
});
|
|
7149
7555
|
}
|
|
7150
7556
|
}
|
|
7151
|
-
return
|
|
7152
|
-
nodes: resultNodes,
|
|
7153
|
-
edges: resultEdges,
|
|
7154
|
-
depth: maxDepth
|
|
7155
|
-
};
|
|
7557
|
+
return paginate(resultNodes, resultEdges, maxDepth, limit, offset);
|
|
7156
7558
|
}
|
|
7157
7559
|
let filtered = allNodes;
|
|
7158
7560
|
if (data.nodeType) filtered = allNodes.filter((n) => n.type === data.nodeType);
|
|
7159
|
-
return
|
|
7160
|
-
nodes: filtered,
|
|
7161
|
-
edges: allEdges,
|
|
7162
|
-
depth: 0
|
|
7163
|
-
};
|
|
7561
|
+
return paginate(rankByDegree(filtered, allEdges), allEdges, 0, limit, offset);
|
|
7164
7562
|
});
|
|
7165
7563
|
sdk.registerFunction("mem::graph-stats", async () => {
|
|
7166
7564
|
const nodes = await kv.list(KV.graphNodes);
|
|
@@ -7177,7 +7575,6 @@ function registerGraphFunction(sdk, kv, provider) {
|
|
|
7177
7575
|
};
|
|
7178
7576
|
});
|
|
7179
7577
|
}
|
|
7180
|
-
|
|
7181
7578
|
//#endregion
|
|
7182
7579
|
//#region src/prompts/consolidation.ts
|
|
7183
7580
|
const SEMANTIC_MERGE_SYSTEM = `You are a memory consolidation engine. Given overlapping episodic memories (session summaries), extract stable factual knowledge.
|
|
@@ -7212,7 +7609,6 @@ Rules:
|
|
|
7212
7609
|
function buildProceduralExtractionPrompt(patterns) {
|
|
7213
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")}`;
|
|
7214
7611
|
}
|
|
7215
|
-
|
|
7216
7612
|
//#endregion
|
|
7217
7613
|
//#region src/functions/consolidation-pipeline.ts
|
|
7218
7614
|
function applyDecay(items, decayDays) {
|
|
@@ -7406,7 +7802,6 @@ function registerConsolidationPipelineFunction(sdk, kv, provider) {
|
|
|
7406
7802
|
};
|
|
7407
7803
|
});
|
|
7408
7804
|
}
|
|
7409
|
-
|
|
7410
7805
|
//#endregion
|
|
7411
7806
|
//#region src/functions/team.ts
|
|
7412
7807
|
const VALID_ITEM_TYPES = new Set([
|
|
@@ -7510,7 +7905,6 @@ function registerTeamFunction(sdk, kv, config) {
|
|
|
7510
7905
|
return profile;
|
|
7511
7906
|
});
|
|
7512
7907
|
}
|
|
7513
|
-
|
|
7514
7908
|
//#endregion
|
|
7515
7909
|
//#region src/functions/governance.ts
|
|
7516
7910
|
function registerGovernanceFunction(sdk, kv) {
|
|
@@ -7619,7 +8013,6 @@ function registerGovernanceFunction(sdk, kv) {
|
|
|
7619
8013
|
return queryAudit(kv, data);
|
|
7620
8014
|
});
|
|
7621
8015
|
}
|
|
7622
|
-
|
|
7623
8016
|
//#endregion
|
|
7624
8017
|
//#region src/functions/snapshot.ts
|
|
7625
8018
|
const COMMIT_HASH_RE = /^[0-9a-f]{7,40}$/i;
|
|
@@ -7784,7 +8177,6 @@ function registerSnapshotFunction(sdk, kv, snapshotDir) {
|
|
|
7784
8177
|
}
|
|
7785
8178
|
});
|
|
7786
8179
|
}
|
|
7787
|
-
|
|
7788
8180
|
//#endregion
|
|
7789
8181
|
//#region src/functions/actions.ts
|
|
7790
8182
|
function registerActionsFunction(sdk, kv) {
|
|
@@ -7986,7 +8378,6 @@ async function propagateCompletion(kv, completedActionId) {
|
|
|
7986
8378
|
});
|
|
7987
8379
|
}
|
|
7988
8380
|
}
|
|
7989
|
-
|
|
7990
8381
|
//#endregion
|
|
7991
8382
|
//#region src/functions/frontier.ts
|
|
7992
8383
|
function registerFrontierFunction(sdk, kv) {
|
|
@@ -8091,7 +8482,6 @@ function computeScore(action, edges, now) {
|
|
|
8091
8482
|
if (action.status === "active") score += 15;
|
|
8092
8483
|
return Math.round(score * 100) / 100;
|
|
8093
8484
|
}
|
|
8094
|
-
|
|
8095
8485
|
//#endregion
|
|
8096
8486
|
//#region src/functions/leases.ts
|
|
8097
8487
|
const DEFAULT_LEASE_TTL_MS = 600 * 1e3;
|
|
@@ -8272,7 +8662,6 @@ function registerLeasesFunction(sdk, kv) {
|
|
|
8272
8662
|
};
|
|
8273
8663
|
});
|
|
8274
8664
|
}
|
|
8275
|
-
|
|
8276
8665
|
//#endregion
|
|
8277
8666
|
//#region src/functions/routines.ts
|
|
8278
8667
|
function registerRoutinesFunction(sdk, kv) {
|
|
@@ -8519,7 +8908,6 @@ function registerRoutinesFunction(sdk, kv) {
|
|
|
8519
8908
|
});
|
|
8520
8909
|
});
|
|
8521
8910
|
}
|
|
8522
|
-
|
|
8523
8911
|
//#endregion
|
|
8524
8912
|
//#region src/functions/signals.ts
|
|
8525
8913
|
function registerSignalsFunction(sdk, kv) {
|
|
@@ -8651,7 +9039,6 @@ function registerSignalsFunction(sdk, kv) {
|
|
|
8651
9039
|
};
|
|
8652
9040
|
});
|
|
8653
9041
|
}
|
|
8654
|
-
|
|
8655
9042
|
//#endregion
|
|
8656
9043
|
//#region src/functions/checkpoints.ts
|
|
8657
9044
|
function registerCheckpointsFunction(sdk, kv) {
|
|
@@ -8824,7 +9211,6 @@ function registerCheckpointsFunction(sdk, kv) {
|
|
|
8824
9211
|
};
|
|
8825
9212
|
});
|
|
8826
9213
|
}
|
|
8827
|
-
|
|
8828
9214
|
//#endregion
|
|
8829
9215
|
//#region src/functions/flow-compress.ts
|
|
8830
9216
|
const FLOW_COMPRESS_SYSTEM = `You are a workflow summarizer. Given a completed action chain, produce a concise summary capturing:
|
|
@@ -8969,7 +9355,6 @@ function extractFiles(actions) {
|
|
|
8969
9355
|
}
|
|
8970
9356
|
return Array.from(files);
|
|
8971
9357
|
}
|
|
8972
|
-
|
|
8973
9358
|
//#endregion
|
|
8974
9359
|
//#region src/functions/mesh.ts
|
|
8975
9360
|
function isPrivateIP(ip) {
|
|
@@ -9286,7 +9671,6 @@ async function applySyncData(kv, data, scopes) {
|
|
|
9286
9671
|
if (scopes.includes("graph:edges")) applied += await lwwMergeList(kv, KV.graphEdges, data.graphEdges, "mem:gedge", "createdAt");
|
|
9287
9672
|
return applied;
|
|
9288
9673
|
}
|
|
9289
|
-
|
|
9290
9674
|
//#endregion
|
|
9291
9675
|
//#region src/functions/branch-aware.ts
|
|
9292
9676
|
function execAsync(cmd, args, cwd) {
|
|
@@ -9401,7 +9785,6 @@ function registerBranchAwareFunction(sdk, kv) {
|
|
|
9401
9785
|
};
|
|
9402
9786
|
});
|
|
9403
9787
|
}
|
|
9404
|
-
|
|
9405
9788
|
//#endregion
|
|
9406
9789
|
//#region src/functions/sentinels.ts
|
|
9407
9790
|
const VALID_TYPES = [
|
|
@@ -9722,7 +10105,6 @@ async function unblockLinkedActions(kv, sentinel) {
|
|
|
9722
10105
|
});
|
|
9723
10106
|
return unblockedCount;
|
|
9724
10107
|
}
|
|
9725
|
-
|
|
9726
10108
|
//#endregion
|
|
9727
10109
|
//#region src/functions/sketches.ts
|
|
9728
10110
|
function registerSketchesFunction(sdk, kv) {
|
|
@@ -9964,7 +10346,6 @@ function registerSketchesFunction(sdk, kv) {
|
|
|
9964
10346
|
};
|
|
9965
10347
|
});
|
|
9966
10348
|
}
|
|
9967
|
-
|
|
9968
10349
|
//#endregion
|
|
9969
10350
|
//#region src/functions/crystallize.ts
|
|
9970
10351
|
const CRYSTALLIZE_SYSTEM = `You are summarizing a completed chain of agent actions into a compact digest.
|
|
@@ -10161,7 +10542,6 @@ function parseDigest(response) {
|
|
|
10161
10542
|
};
|
|
10162
10543
|
}
|
|
10163
10544
|
}
|
|
10164
|
-
|
|
10165
10545
|
//#endregion
|
|
10166
10546
|
//#region src/functions/diagnostics.ts
|
|
10167
10547
|
const ALL_CATEGORIES = [
|
|
@@ -10875,7 +11255,6 @@ function registerDiagnosticsFunction(sdk, kv) {
|
|
|
10875
11255
|
};
|
|
10876
11256
|
});
|
|
10877
11257
|
}
|
|
10878
|
-
|
|
10879
11258
|
//#endregion
|
|
10880
11259
|
//#region src/functions/facets.ts
|
|
10881
11260
|
function registerFacetsFunction(sdk, kv) {
|
|
@@ -11047,7 +11426,6 @@ function registerFacetsFunction(sdk, kv) {
|
|
|
11047
11426
|
};
|
|
11048
11427
|
});
|
|
11049
11428
|
}
|
|
11050
|
-
|
|
11051
11429
|
//#endregion
|
|
11052
11430
|
//#region src/functions/verify.ts
|
|
11053
11431
|
function registerVerifyFunction(sdk, kv) {
|
|
@@ -11142,7 +11520,6 @@ async function findObservation(kv, obsId, hintSessionIds) {
|
|
|
11142
11520
|
}
|
|
11143
11521
|
return null;
|
|
11144
11522
|
}
|
|
11145
|
-
|
|
11146
11523
|
//#endregion
|
|
11147
11524
|
//#region src/functions/cascade.ts
|
|
11148
11525
|
function registerCascadeFunction(sdk, kv) {
|
|
@@ -11212,7 +11589,6 @@ function registerCascadeFunction(sdk, kv) {
|
|
|
11212
11589
|
};
|
|
11213
11590
|
});
|
|
11214
11591
|
}
|
|
11215
|
-
|
|
11216
11592
|
//#endregion
|
|
11217
11593
|
//#region src/functions/lessons.ts
|
|
11218
11594
|
function reinforceLesson(lesson) {
|
|
@@ -11398,7 +11774,6 @@ function registerLessonsFunctions(sdk, kv) {
|
|
|
11398
11774
|
};
|
|
11399
11775
|
});
|
|
11400
11776
|
}
|
|
11401
|
-
|
|
11402
11777
|
//#endregion
|
|
11403
11778
|
//#region src/functions/obsidian-export.ts
|
|
11404
11779
|
const DEFAULT_EXPORT_ROOT = join(homedir(), ".agentmemory");
|
|
@@ -11414,6 +11789,20 @@ function resolveVaultDir(vaultDir) {
|
|
|
11414
11789
|
function sanitize(name) {
|
|
11415
11790
|
return name.replace(/[<>:"/\\|?*\x00-\x1f]/g, "_").slice(0, 100);
|
|
11416
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
|
+
}
|
|
11417
11806
|
function toFrontmatter(obj) {
|
|
11418
11807
|
const lines = ["---"];
|
|
11419
11808
|
for (const [key, value] of Object.entries(obj)) {
|
|
@@ -11425,6 +11814,11 @@ function toFrontmatter(obj) {
|
|
|
11425
11814
|
return lines.join("\n");
|
|
11426
11815
|
}
|
|
11427
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);
|
|
11428
11822
|
const fm = toFrontmatter({
|
|
11429
11823
|
id: m.id,
|
|
11430
11824
|
type: m.type,
|
|
@@ -11432,24 +11826,28 @@ function memoryToMd(m) {
|
|
|
11432
11826
|
updated: m.updatedAt,
|
|
11433
11827
|
strength: m.strength,
|
|
11434
11828
|
version: m.version,
|
|
11435
|
-
concepts
|
|
11436
|
-
files
|
|
11829
|
+
concepts,
|
|
11830
|
+
files
|
|
11437
11831
|
});
|
|
11438
|
-
const
|
|
11439
|
-
const
|
|
11832
|
+
const relatedLines = relatedIds.map((id) => `- [[${id}]]`).join("\n");
|
|
11833
|
+
const supersedesLines = supersedes.map((id) => `- [[${id}]] (superseded)`).join("\n");
|
|
11440
11834
|
const sections = [
|
|
11441
11835
|
fm,
|
|
11442
11836
|
"",
|
|
11443
|
-
`# ${
|
|
11837
|
+
`# ${title}`,
|
|
11444
11838
|
"",
|
|
11445
|
-
m.content
|
|
11839
|
+
safeString(m.content)
|
|
11446
11840
|
];
|
|
11447
|
-
if (
|
|
11448
|
-
if (
|
|
11449
|
-
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);
|
|
11450
11844
|
return sections.join("\n");
|
|
11451
11845
|
}
|
|
11452
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;
|
|
11453
11851
|
const fm = toFrontmatter({
|
|
11454
11852
|
id: l.id,
|
|
11455
11853
|
type: "lesson",
|
|
@@ -11459,66 +11857,76 @@ function lessonToMd(l) {
|
|
|
11459
11857
|
created: l.createdAt,
|
|
11460
11858
|
updated: l.updatedAt,
|
|
11461
11859
|
project: l.project,
|
|
11462
|
-
tags
|
|
11860
|
+
tags,
|
|
11463
11861
|
decayRate: l.decayRate
|
|
11464
11862
|
});
|
|
11465
|
-
const sourceLinks =
|
|
11863
|
+
const sourceLinks = sourceIds.map((id) => `- [[${id}]]`).join("\n");
|
|
11466
11864
|
const sections = [
|
|
11467
11865
|
fm,
|
|
11468
11866
|
"",
|
|
11469
|
-
`# Lesson: ${
|
|
11867
|
+
`# Lesson: ${headline}`,
|
|
11470
11868
|
"",
|
|
11471
|
-
|
|
11869
|
+
content
|
|
11472
11870
|
];
|
|
11473
11871
|
if (l.context) sections.push("", "## Context", l.context);
|
|
11474
|
-
if (
|
|
11872
|
+
if (tags.length > 0) sections.push("", "## Tags", tags.map((t) => `#${t.replace(/\s+/g, "-")}`).join(" "));
|
|
11475
11873
|
if (sourceLinks) sections.push("", "## Sources", sourceLinks);
|
|
11476
11874
|
return sections.join("\n");
|
|
11477
11875
|
}
|
|
11478
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;
|
|
11479
11883
|
const fm = toFrontmatter({
|
|
11480
11884
|
id: c.id,
|
|
11481
11885
|
type: "crystal",
|
|
11482
11886
|
created: c.createdAt,
|
|
11483
11887
|
project: c.project,
|
|
11484
11888
|
sessionId: c.sessionId,
|
|
11485
|
-
filesAffected
|
|
11889
|
+
filesAffected
|
|
11486
11890
|
});
|
|
11487
|
-
const actionLinks =
|
|
11891
|
+
const actionLinks = sourceActionIds.map((id) => `- [[${id}]]`).join("\n");
|
|
11488
11892
|
const sections = [
|
|
11489
11893
|
fm,
|
|
11490
11894
|
"",
|
|
11491
|
-
`# Crystal: ${
|
|
11895
|
+
`# Crystal: ${headline}`,
|
|
11492
11896
|
"",
|
|
11493
|
-
|
|
11897
|
+
narrative,
|
|
11494
11898
|
"",
|
|
11495
11899
|
"## Key Outcomes",
|
|
11496
|
-
...
|
|
11900
|
+
...keyOutcomes.map((o) => `- ${o}`)
|
|
11497
11901
|
];
|
|
11498
|
-
if (
|
|
11499
|
-
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}\``));
|
|
11500
11904
|
if (actionLinks) sections.push("", "## Source Actions", actionLinks);
|
|
11501
11905
|
return sections.join("\n");
|
|
11502
11906
|
}
|
|
11503
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, "");
|
|
11504
11912
|
return [
|
|
11505
11913
|
toFrontmatter({
|
|
11506
11914
|
id: s.id,
|
|
11507
11915
|
type: "session",
|
|
11508
|
-
project
|
|
11509
|
-
status
|
|
11510
|
-
started:
|
|
11916
|
+
project,
|
|
11917
|
+
status,
|
|
11918
|
+
started: startedAt || void 0,
|
|
11511
11919
|
ended: s.endedAt,
|
|
11512
11920
|
observations: s.observationCount
|
|
11513
11921
|
}),
|
|
11514
11922
|
"",
|
|
11515
|
-
`# Session: ${
|
|
11923
|
+
`# Session: ${project}`,
|
|
11516
11924
|
"",
|
|
11517
|
-
`**Status:** ${
|
|
11518
|
-
`**Started:** ${
|
|
11925
|
+
`**Status:** ${status}`,
|
|
11926
|
+
startedAt ? `**Started:** ${startedAt}` : "",
|
|
11519
11927
|
s.endedAt ? `**Ended:** ${s.endedAt}` : "",
|
|
11520
|
-
`**Observations:** ${s.observationCount}`,
|
|
11521
|
-
`**CWD:** \`${
|
|
11928
|
+
`**Observations:** ${s.observationCount ?? 0}`,
|
|
11929
|
+
cwd ? `**CWD:** \`${cwd}\`` : ""
|
|
11522
11930
|
].filter(Boolean).join("\n");
|
|
11523
11931
|
}
|
|
11524
11932
|
function registerObsidianExportFunction(sdk, kv) {
|
|
@@ -11554,122 +11962,131 @@ function registerObsidianExportFunction(sdk, kv) {
|
|
|
11554
11962
|
crystals: join(vaultDir, "crystals"),
|
|
11555
11963
|
sessions: join(vaultDir, "sessions")
|
|
11556
11964
|
};
|
|
11557
|
-
|
|
11558
|
-
|
|
11559
|
-
|
|
11560
|
-
|
|
11561
|
-
|
|
11562
|
-
|
|
11563
|
-
|
|
11564
|
-
|
|
11565
|
-
|
|
11566
|
-
|
|
11567
|
-
|
|
11568
|
-
|
|
11569
|
-
|
|
11570
|
-
|
|
11571
|
-
|
|
11572
|
-
|
|
11573
|
-
|
|
11574
|
-
|
|
11575
|
-
|
|
11576
|
-
const
|
|
11577
|
-
|
|
11578
|
-
|
|
11579
|
-
|
|
11580
|
-
|
|
11581
|
-
|
|
11582
|
-
|
|
11583
|
-
|
|
11584
|
-
|
|
11585
|
-
|
|
11586
|
-
|
|
11587
|
-
|
|
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
|
+
}
|
|
11588
11998
|
}
|
|
11589
|
-
|
|
11590
|
-
|
|
11591
|
-
|
|
11592
|
-
|
|
11593
|
-
|
|
11594
|
-
|
|
11595
|
-
|
|
11596
|
-
|
|
11597
|
-
|
|
11598
|
-
|
|
11599
|
-
|
|
11600
|
-
|
|
11601
|
-
|
|
11602
|
-
|
|
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
|
+
}
|
|
11603
12014
|
}
|
|
11604
|
-
|
|
11605
|
-
|
|
11606
|
-
|
|
11607
|
-
|
|
11608
|
-
|
|
11609
|
-
|
|
11610
|
-
|
|
11611
|
-
|
|
11612
|
-
|
|
11613
|
-
|
|
11614
|
-
|
|
11615
|
-
|
|
11616
|
-
|
|
11617
|
-
|
|
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
|
+
}
|
|
11618
12030
|
}
|
|
11619
|
-
|
|
11620
|
-
|
|
11621
|
-
|
|
11622
|
-
|
|
11623
|
-
|
|
11624
|
-
|
|
11625
|
-
|
|
11626
|
-
|
|
11627
|
-
|
|
11628
|
-
|
|
11629
|
-
|
|
11630
|
-
|
|
11631
|
-
|
|
11632
|
-
|
|
11633
|
-
}
|
|
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
|
+
}
|
|
11634
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
|
+
};
|
|
11635
12087
|
}
|
|
11636
|
-
const exportedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
11637
|
-
const moc = [
|
|
11638
|
-
"---",
|
|
11639
|
-
"type: moc",
|
|
11640
|
-
`exported: ${exportedAt}`,
|
|
11641
|
-
"---",
|
|
11642
|
-
"",
|
|
11643
|
-
"# agentmemory vault",
|
|
11644
|
-
"",
|
|
11645
|
-
`Exported: ${exportedAt}`,
|
|
11646
|
-
"",
|
|
11647
|
-
`## Memories (${stats.memories})`,
|
|
11648
|
-
...memoryMoc,
|
|
11649
|
-
"",
|
|
11650
|
-
`## Lessons (${stats.lessons})`,
|
|
11651
|
-
...lessonMoc,
|
|
11652
|
-
"",
|
|
11653
|
-
`## Crystals (${stats.crystals})`,
|
|
11654
|
-
...crystalMoc,
|
|
11655
|
-
"",
|
|
11656
|
-
`## Sessions (${stats.sessions})`,
|
|
11657
|
-
...sessionMoc
|
|
11658
|
-
].join("\n");
|
|
11659
|
-
await writeFile(join(vaultDir, "MOC.md"), moc);
|
|
11660
|
-
await recordAudit(kv, "obsidian_export", "mem::obsidian-export", [], {
|
|
11661
|
-
vaultDir,
|
|
11662
|
-
stats
|
|
11663
|
-
});
|
|
11664
|
-
return {
|
|
11665
|
-
success: true,
|
|
11666
|
-
exported: stats,
|
|
11667
|
-
errors: errors.length > 0 ? errors : void 0,
|
|
11668
|
-
vaultDir
|
|
11669
|
-
};
|
|
11670
12088
|
});
|
|
11671
12089
|
}
|
|
11672
|
-
|
|
11673
12090
|
//#endregion
|
|
11674
12091
|
//#region src/prompts/reflect.ts
|
|
11675
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.
|
|
@@ -11697,7 +12114,6 @@ function buildReflectPrompt(cluster) {
|
|
|
11697
12114
|
if (cluster.crystalNarratives.length > 0) sections.push("\n## Completed Work Summaries", ...cluster.crystalNarratives.map((n) => `- ${n}`));
|
|
11698
12115
|
return `Synthesize higher-order insights from this cluster of related memories:\n\n${sections.join("\n")}`;
|
|
11699
12116
|
}
|
|
11700
|
-
|
|
11701
12117
|
//#endregion
|
|
11702
12118
|
//#region src/functions/reflect.ts
|
|
11703
12119
|
function reinforceInsight(insight) {
|
|
@@ -11990,7 +12406,6 @@ function registerReflectFunctions(sdk, kv, provider) {
|
|
|
11990
12406
|
};
|
|
11991
12407
|
});
|
|
11992
12408
|
}
|
|
11993
|
-
|
|
11994
12409
|
//#endregion
|
|
11995
12410
|
//#region src/functions/working-memory.ts
|
|
11996
12411
|
const CORE_SCOPE = "mem:core-memory";
|
|
@@ -12165,7 +12580,6 @@ function registerWorkingMemoryFunctions(sdk, kv, tokenBudget) {
|
|
|
12165
12580
|
};
|
|
12166
12581
|
});
|
|
12167
12582
|
}
|
|
12168
|
-
|
|
12169
12583
|
//#endregion
|
|
12170
12584
|
//#region src/functions/skill-extract.ts
|
|
12171
12585
|
const SKILL_EXTRACT_SYSTEM = `You are a skill extraction engine. Given a completed multi-step task session, extract a reusable procedural skill document.
|
|
@@ -12368,7 +12782,6 @@ function registerSkillExtractFunctions(sdk, kv, provider) {
|
|
|
12368
12782
|
};
|
|
12369
12783
|
});
|
|
12370
12784
|
}
|
|
12371
|
-
|
|
12372
12785
|
//#endregion
|
|
12373
12786
|
//#region src/functions/sliding-window.ts
|
|
12374
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.
|
|
@@ -12555,7 +12968,6 @@ function registerSlidingWindowFunction(sdk, kv, provider) {
|
|
|
12555
12968
|
};
|
|
12556
12969
|
});
|
|
12557
12970
|
}
|
|
12558
|
-
|
|
12559
12971
|
//#endregion
|
|
12560
12972
|
//#region src/functions/temporal-graph.ts
|
|
12561
12973
|
const TEMPORAL_EXTRACTION_SYSTEM = `You are a temporal knowledge extraction engine. Given observations, extract entities AND their temporal relationships with full context metadata.
|
|
@@ -12819,7 +13231,6 @@ function buildTimeline(edges) {
|
|
|
12819
13231
|
context: e.context
|
|
12820
13232
|
}));
|
|
12821
13233
|
}
|
|
12822
|
-
|
|
12823
13234
|
//#endregion
|
|
12824
13235
|
//#region src/functions/retention.ts
|
|
12825
13236
|
const DEFAULT_DECAY = {
|
|
@@ -12983,7 +13394,7 @@ function registerRetentionFunctions(sdk, kv) {
|
|
|
12983
13394
|
const threshold = typeof data?.threshold === "number" && Number.isFinite(data.threshold) ? data.threshold : DEFAULT_DECAY.tierThresholds.cold;
|
|
12984
13395
|
const maxEvictRaw = typeof data?.maxEvict === "number" && Number.isInteger(data.maxEvict) ? data.maxEvict : 50;
|
|
12985
13396
|
const maxEvict = Math.min(1e3, Math.max(0, maxEvictRaw));
|
|
12986
|
-
const { decrementImageRef } = await import("./image-refs-
|
|
13397
|
+
const { decrementImageRef } = await import("./image-refs-C7h9L5wx.mjs").then((n) => n.n);
|
|
12987
13398
|
const candidates = (await kv.list(KV.retentionScores)).filter((s) => s.score < threshold).sort((a, b) => a.score - b.score).slice(0, maxEvict);
|
|
12988
13399
|
if (data?.dryRun) return {
|
|
12989
13400
|
success: true,
|
|
@@ -13053,7 +13464,6 @@ function registerRetentionFunctions(sdk, kv) {
|
|
|
13053
13464
|
};
|
|
13054
13465
|
});
|
|
13055
13466
|
}
|
|
13056
|
-
|
|
13057
13467
|
//#endregion
|
|
13058
13468
|
//#region src/functions/compress-file.ts
|
|
13059
13469
|
const SENSITIVE_PATH_TERMS = [
|
|
@@ -13195,7 +13605,6 @@ function registerCompressFileFunction(sdk, kv, provider) {
|
|
|
13195
13605
|
};
|
|
13196
13606
|
});
|
|
13197
13607
|
}
|
|
13198
|
-
|
|
13199
13608
|
//#endregion
|
|
13200
13609
|
//#region src/replay/jsonl-parser.ts
|
|
13201
13610
|
function deriveProject(cwd) {
|
|
@@ -13322,7 +13731,6 @@ function parseJsonlText(text, fallbackSessionId) {
|
|
|
13322
13731
|
observations
|
|
13323
13732
|
};
|
|
13324
13733
|
}
|
|
13325
|
-
|
|
13326
13734
|
//#endregion
|
|
13327
13735
|
//#region src/replay/timeline.ts
|
|
13328
13736
|
const DEFAULT_CHARS_PER_SEC = 40;
|
|
@@ -13417,10 +13825,6 @@ function projectTimeline(observations) {
|
|
|
13417
13825
|
events
|
|
13418
13826
|
};
|
|
13419
13827
|
}
|
|
13420
|
-
|
|
13421
|
-
//#endregion
|
|
13422
|
-
//#region src/functions/replay.ts
|
|
13423
|
-
const MAX_FILES_DEFAULT = 200;
|
|
13424
13828
|
const MAX_FILES_UPPER_BOUND = 1e3;
|
|
13425
13829
|
const SENSITIVE_PATH_PATTERNS = [
|
|
13426
13830
|
/(^|[\\/_.-])secret([\\/_.-]|s?$)/i,
|
|
@@ -13640,7 +14044,7 @@ function registerReplayFunctions(sdk, kv) {
|
|
|
13640
14044
|
error: "path not found"
|
|
13641
14045
|
};
|
|
13642
14046
|
}
|
|
13643
|
-
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;
|
|
13644
14048
|
let files = [];
|
|
13645
14049
|
let truncated = false;
|
|
13646
14050
|
let discovered = 0;
|
|
@@ -13696,7 +14100,8 @@ function registerReplayFunctions(sdk, kv) {
|
|
|
13696
14100
|
const existingTags = existing.tags || [];
|
|
13697
14101
|
if (!existingTags.includes("jsonl-import")) existing.tags = [...existingTags, "jsonl-import"];
|
|
13698
14102
|
if (!existing.firstPrompt && firstPrompt) existing.firstPrompt = firstPrompt;
|
|
13699
|
-
|
|
14103
|
+
if (!existing.id) existing.id = parsed.sessionId;
|
|
14104
|
+
await kv.set(KV.sessions, parsed.sessionId, existing);
|
|
13700
14105
|
} else {
|
|
13701
14106
|
const session = {
|
|
13702
14107
|
id: parsed.sessionId,
|
|
@@ -13742,7 +14147,6 @@ function registerReplayFunctions(sdk, kv) {
|
|
|
13742
14147
|
};
|
|
13743
14148
|
});
|
|
13744
14149
|
}
|
|
13745
|
-
|
|
13746
14150
|
//#endregion
|
|
13747
14151
|
//#region src/health/thresholds.ts
|
|
13748
14152
|
const DEFAULTS = {
|
|
@@ -13801,7 +14205,6 @@ function evaluateHealth(snapshot, config = {}) {
|
|
|
13801
14205
|
notes
|
|
13802
14206
|
};
|
|
13803
14207
|
}
|
|
13804
|
-
|
|
13805
14208
|
//#endregion
|
|
13806
14209
|
//#region src/health/monitor.ts
|
|
13807
14210
|
function registerHealthMonitor(sdk, kv) {
|
|
@@ -13889,7 +14292,6 @@ function registerHealthMonitor(sdk, kv) {
|
|
|
13889
14292
|
async function getLatestHealth(kv) {
|
|
13890
14293
|
return kv.get(KV.health, "latest");
|
|
13891
14294
|
}
|
|
13892
|
-
|
|
13893
14295
|
//#endregion
|
|
13894
14296
|
//#region src/auth.ts
|
|
13895
14297
|
const hmacKey = randomBytes(32);
|
|
@@ -13915,7 +14317,6 @@ function buildViewerCsp(nonce) {
|
|
|
13915
14317
|
"font-src 'self'"
|
|
13916
14318
|
].join("; ");
|
|
13917
14319
|
}
|
|
13918
|
-
|
|
13919
14320
|
//#endregion
|
|
13920
14321
|
//#region src/viewer/document.ts
|
|
13921
14322
|
const VIEWER_VERSION_PLACEHOLDER = "__AGENTMEMORY_VERSION__";
|
|
@@ -13941,7 +14342,6 @@ function renderViewerDocument() {
|
|
|
13941
14342
|
csp: buildViewerCsp(nonce)
|
|
13942
14343
|
};
|
|
13943
14344
|
}
|
|
13944
|
-
|
|
13945
14345
|
//#endregion
|
|
13946
14346
|
//#region src/viewer/server.ts
|
|
13947
14347
|
function loadViewerFavicon() {
|
|
@@ -13956,18 +14356,30 @@ function loadViewerFavicon() {
|
|
|
13956
14356
|
} catch {}
|
|
13957
14357
|
return null;
|
|
13958
14358
|
}
|
|
14359
|
+
const VIEWER_FAVICON = loadViewerFavicon();
|
|
13959
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());
|
|
13960
|
-
|
|
13961
|
-
|
|
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") {
|
|
13962
14372
|
const hosts = /* @__PURE__ */ new Set();
|
|
13963
|
-
|
|
13964
|
-
const
|
|
13965
|
-
|
|
13966
|
-
|
|
13967
|
-
|
|
13968
|
-
|
|
13969
|
-
|
|
13970
|
-
|
|
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);
|
|
13971
14383
|
return hosts;
|
|
13972
14384
|
}
|
|
13973
14385
|
function isHostAllowed(headerHost, allowed) {
|
|
@@ -13976,11 +14388,16 @@ function isHostAllowed(headerHost, allowed) {
|
|
|
13976
14388
|
if (!lower) return false;
|
|
13977
14389
|
return allowed.has(lower);
|
|
13978
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
|
+
}
|
|
13979
14397
|
function corsHeaders(req) {
|
|
13980
14398
|
const origin = req.headers.origin || "";
|
|
13981
|
-
const allowed = ALLOWED_ORIGINS.includes(origin) ? origin : ALLOWED_ORIGINS[0];
|
|
13982
14399
|
return {
|
|
13983
|
-
"Access-Control-Allow-Origin":
|
|
14400
|
+
"Access-Control-Allow-Origin": ALLOWED_ORIGINS.includes(origin) ? origin : ALLOWED_ORIGINS[0],
|
|
13984
14401
|
"Access-Control-Allow-Methods": "GET, POST, DELETE, OPTIONS",
|
|
13985
14402
|
"Access-Control-Allow-Headers": "Content-Type, Authorization",
|
|
13986
14403
|
Vary: "Origin"
|
|
@@ -14024,16 +14441,29 @@ function getBoundViewerPort() {
|
|
|
14024
14441
|
function getViewerSkipped() {
|
|
14025
14442
|
return viewerSkipped;
|
|
14026
14443
|
}
|
|
14444
|
+
var ViewerConfigError = class extends Error {
|
|
14445
|
+
constructor(message) {
|
|
14446
|
+
super(message);
|
|
14447
|
+
this.name = "ViewerConfigError";
|
|
14448
|
+
}
|
|
14449
|
+
};
|
|
14027
14450
|
function startViewerServer(port, _kv, _sdk, secret, restPort) {
|
|
14028
14451
|
boundViewerPort = null;
|
|
14029
14452
|
viewerSkipped = false;
|
|
14030
14453
|
const resolvedRestPort = restPort ?? port - 2;
|
|
14031
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
|
+
}
|
|
14032
14462
|
let allowedHosts = null;
|
|
14033
14463
|
const server = createServer(async (req, res) => {
|
|
14034
14464
|
if (!allowedHosts) {
|
|
14035
14465
|
const addr = server.address();
|
|
14036
|
-
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);
|
|
14037
14467
|
}
|
|
14038
14468
|
if (!isHostAllowed(req.headers.host, allowedHosts)) {
|
|
14039
14469
|
res.writeHead(403, { "Content-Type": "text/plain" });
|
|
@@ -14069,19 +14499,26 @@ function startViewerServer(port, _kv, _sdk, secret, restPort) {
|
|
|
14069
14499
|
return;
|
|
14070
14500
|
}
|
|
14071
14501
|
if (method === "GET" && pathname === "/favicon.svg") {
|
|
14072
|
-
|
|
14073
|
-
if (favicon) {
|
|
14502
|
+
if (VIEWER_FAVICON) {
|
|
14074
14503
|
res.writeHead(200, {
|
|
14075
14504
|
"Content-Type": "image/svg+xml",
|
|
14076
14505
|
"Cache-Control": "public, max-age=3600"
|
|
14077
14506
|
});
|
|
14078
|
-
res.end(
|
|
14507
|
+
res.end(VIEWER_FAVICON);
|
|
14079
14508
|
return;
|
|
14080
14509
|
}
|
|
14081
14510
|
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
14082
14511
|
res.end("favicon not found");
|
|
14083
14512
|
return;
|
|
14084
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
|
+
}
|
|
14085
14522
|
try {
|
|
14086
14523
|
await proxyToRestApi(resolvedRestPort, pathname, qs, method, req, res, secret);
|
|
14087
14524
|
} catch (err) {
|
|
@@ -14092,17 +14529,23 @@ function startViewerServer(port, _kv, _sdk, secret, restPort) {
|
|
|
14092
14529
|
let attempt = 0;
|
|
14093
14530
|
let currentPort = requestedPort;
|
|
14094
14531
|
const tryListen = () => {
|
|
14095
|
-
server.listen(currentPort,
|
|
14532
|
+
server.listen(currentPort, host);
|
|
14096
14533
|
};
|
|
14097
14534
|
server.on("listening", () => {
|
|
14098
14535
|
const addr = server.address();
|
|
14099
|
-
|
|
14536
|
+
const actualPort = addr && typeof addr === "object" && "port" in addr ? addr.port : currentPort;
|
|
14537
|
+
boundViewerPort = actualPort;
|
|
14100
14538
|
viewerSkipped = false;
|
|
14101
|
-
if (
|
|
14102
|
-
|
|
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})`);
|
|
14103
14546
|
});
|
|
14104
14547
|
server.on("error", (err) => {
|
|
14105
|
-
if (err.code === "EADDRINUSE" && attempt < MAX_VIEWER_PORT_RETRIES) {
|
|
14548
|
+
if (err.code === "EADDRINUSE" && inboundSecret === null && attempt < MAX_VIEWER_PORT_RETRIES) {
|
|
14106
14549
|
attempt++;
|
|
14107
14550
|
currentPort = requestedPort + attempt;
|
|
14108
14551
|
setImmediate(tryListen);
|
|
@@ -14111,7 +14554,8 @@ function startViewerServer(port, _kv, _sdk, secret, restPort) {
|
|
|
14111
14554
|
if (err.code === "EADDRINUSE") {
|
|
14112
14555
|
boundViewerPort = null;
|
|
14113
14556
|
viewerSkipped = true;
|
|
14114
|
-
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.`);
|
|
14115
14559
|
} else {
|
|
14116
14560
|
boundViewerPort = null;
|
|
14117
14561
|
viewerSkipped = true;
|
|
@@ -14156,7 +14600,6 @@ async function proxyToRestApi(restPort, pathname, qs, method, req, res, secret)
|
|
|
14156
14600
|
res.writeHead(upstream.status, responseHeaders);
|
|
14157
14601
|
res.end(responseBody);
|
|
14158
14602
|
}
|
|
14159
|
-
|
|
14160
14603
|
//#endregion
|
|
14161
14604
|
//#region src/triggers/api.ts
|
|
14162
14605
|
function parseOptionalInt(raw) {
|
|
@@ -14579,7 +15022,7 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
14579
15022
|
}
|
|
14580
15023
|
if (body.maxFiles !== void 0) {
|
|
14581
15024
|
const n = body.maxFiles;
|
|
14582
|
-
if (!Number.isInteger(n) || n < 1 || n >
|
|
15025
|
+
if (!Number.isInteger(n) || n < 1 || n > 1e3) return {
|
|
14583
15026
|
status_code: 400,
|
|
14584
15027
|
body: { error: `maxFiles must be an integer between 1 and ${MAX_FILES_UPPER_BOUND}` }
|
|
14585
15028
|
};
|
|
@@ -14663,9 +15106,13 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
14663
15106
|
value: "completed"
|
|
14664
15107
|
}]);
|
|
14665
15108
|
try {
|
|
14666
|
-
sdk.
|
|
15109
|
+
sdk.trigger({
|
|
15110
|
+
function_id: "event::session::stopped",
|
|
15111
|
+
payload: { sessionId },
|
|
15112
|
+
action: TriggerAction.Void()
|
|
15113
|
+
});
|
|
14667
15114
|
} catch (err) {
|
|
14668
|
-
logger.warn("event::session::stopped
|
|
15115
|
+
logger.warn("event::session::stopped trigger failed", {
|
|
14669
15116
|
sessionId,
|
|
14670
15117
|
error: err instanceof Error ? err.message : String(err)
|
|
14671
15118
|
});
|
|
@@ -15085,11 +15532,24 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
15085
15532
|
status_code: 400,
|
|
15086
15533
|
body: { error: "query or expandIds is required" }
|
|
15087
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
|
+
};
|
|
15088
15548
|
return {
|
|
15089
15549
|
status_code: 200,
|
|
15090
15550
|
body: await sdk.trigger({
|
|
15091
15551
|
function_id: "mem::smart-search",
|
|
15092
|
-
payload
|
|
15552
|
+
payload
|
|
15093
15553
|
})
|
|
15094
15554
|
};
|
|
15095
15555
|
});
|
|
@@ -15101,6 +15561,29 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
15101
15561
|
http_method: "POST"
|
|
15102
15562
|
}
|
|
15103
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
|
+
});
|
|
15104
15587
|
sdk.registerFunction("api::timeline", async (req) => {
|
|
15105
15588
|
const authErr = checkAuth(req, secret);
|
|
15106
15589
|
if (authErr) return authErr;
|
|
@@ -15322,12 +15805,20 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
15322
15805
|
sdk.registerFunction("api::graph-query", async (req) => {
|
|
15323
15806
|
const authErr = checkAuth(req, secret);
|
|
15324
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
|
+
};
|
|
15325
15816
|
try {
|
|
15326
15817
|
return {
|
|
15327
15818
|
status_code: 200,
|
|
15328
15819
|
body: await sdk.trigger({
|
|
15329
15820
|
function_id: "mem::graph-query",
|
|
15330
|
-
payload
|
|
15821
|
+
payload
|
|
15331
15822
|
})
|
|
15332
15823
|
};
|
|
15333
15824
|
} catch {
|
|
@@ -17608,7 +18099,6 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
17608
18099
|
}
|
|
17609
18100
|
});
|
|
17610
18101
|
}
|
|
17611
|
-
|
|
17612
18102
|
//#endregion
|
|
17613
18103
|
//#region src/triggers/events.ts
|
|
17614
18104
|
function registerEventTriggers(sdk, kv) {
|
|
@@ -17653,18 +18143,26 @@ function registerEventTriggers(sdk, kv) {
|
|
|
17653
18143
|
payload: data
|
|
17654
18144
|
});
|
|
17655
18145
|
if (isReflectEnabled()) try {
|
|
17656
|
-
sdk.
|
|
18146
|
+
sdk.trigger({
|
|
18147
|
+
function_id: "mem::slot-reflect",
|
|
18148
|
+
payload: { sessionId: data.sessionId },
|
|
18149
|
+
action: TriggerAction.Void()
|
|
18150
|
+
});
|
|
17657
18151
|
} catch (err) {
|
|
17658
|
-
logger.warn("slot-reflect
|
|
18152
|
+
logger.warn("slot-reflect trigger failed", {
|
|
17659
18153
|
sessionId: data.sessionId,
|
|
17660
18154
|
error: err instanceof Error ? err.message : String(err)
|
|
17661
18155
|
});
|
|
17662
18156
|
}
|
|
17663
18157
|
if (isGraphExtractionEnabled()) try {
|
|
17664
18158
|
const compressed = (await kv.list(KV.observations(data.sessionId))).filter((o) => o.title);
|
|
17665
|
-
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
|
+
});
|
|
17666
18164
|
} catch (err) {
|
|
17667
|
-
logger.warn("graph-extract
|
|
18165
|
+
logger.warn("graph-extract trigger failed", {
|
|
17668
18166
|
sessionId: data.sessionId,
|
|
17669
18167
|
error: err instanceof Error ? err.message : String(err)
|
|
17670
18168
|
});
|
|
@@ -17722,7 +18220,6 @@ function registerEventTriggers(sdk, kv) {
|
|
|
17722
18220
|
config: { scope: KV.sessions }
|
|
17723
18221
|
});
|
|
17724
18222
|
}
|
|
17725
|
-
|
|
17726
18223
|
//#endregion
|
|
17727
18224
|
//#region src/mcp/server.ts
|
|
17728
18225
|
function asNonEmptyString(value) {
|
|
@@ -19359,7 +19856,6 @@ function registerMcpEndpoints(sdk, kv, secret) {
|
|
|
19359
19856
|
}
|
|
19360
19857
|
});
|
|
19361
19858
|
}
|
|
19362
|
-
|
|
19363
19859
|
//#endregion
|
|
19364
19860
|
//#region src/eval/metrics-store.ts
|
|
19365
19861
|
var MetricsStore = class {
|
|
@@ -19402,7 +19898,6 @@ var MetricsStore = class {
|
|
|
19402
19898
|
return Array.from(merged.values());
|
|
19403
19899
|
}
|
|
19404
19900
|
};
|
|
19405
|
-
|
|
19406
19901
|
//#endregion
|
|
19407
19902
|
//#region src/functions/dedup.ts
|
|
19408
19903
|
const TTL_MS = 300 * 1e3;
|
|
@@ -19444,57 +19939,6 @@ var DedupMap = class {
|
|
|
19444
19939
|
return this.entries.size;
|
|
19445
19940
|
}
|
|
19446
19941
|
};
|
|
19447
|
-
|
|
19448
|
-
//#endregion
|
|
19449
|
-
//#region src/telemetry/setup.ts
|
|
19450
|
-
const OTEL_CONFIG = {
|
|
19451
|
-
serviceName: "agentmemory",
|
|
19452
|
-
serviceVersion: VERSION,
|
|
19453
|
-
metricsExportIntervalMs: 3e4
|
|
19454
|
-
};
|
|
19455
|
-
let counters = null;
|
|
19456
|
-
let histograms = null;
|
|
19457
|
-
const NOOP_COUNTER = { add: () => {} };
|
|
19458
|
-
const NOOP_HISTOGRAM = { record: () => {} };
|
|
19459
|
-
const COUNTER_NAMES = [
|
|
19460
|
-
["observationsTotal", "observations.total"],
|
|
19461
|
-
["compressionSuccess", "compression.success"],
|
|
19462
|
-
["compressionFailure", "compression.failure"],
|
|
19463
|
-
["searchTotal", "search.total"],
|
|
19464
|
-
["dedupSkipped", "dedup.skipped"],
|
|
19465
|
-
["evictionTotal", "eviction.total"],
|
|
19466
|
-
["circuitBreakerOpen", "circuit_breaker.open"],
|
|
19467
|
-
["embeddingSuccess", "embedding.success"],
|
|
19468
|
-
["embeddingFailure", "embedding.failure"],
|
|
19469
|
-
["vectorSearchTotal", "vector_search.total"],
|
|
19470
|
-
["autoForgetTotal", "auto_forget.total"],
|
|
19471
|
-
["profileGenerated", "profile.generated"],
|
|
19472
|
-
["claudeBridgeSync", "claude_bridge.sync"],
|
|
19473
|
-
["graphExtraction", "graph.extraction"],
|
|
19474
|
-
["consolidationRun", "consolidation.run"],
|
|
19475
|
-
["teamShare", "team.share"],
|
|
19476
|
-
["auditLog", "audit.log"],
|
|
19477
|
-
["snapshotCreate", "snapshot.create"],
|
|
19478
|
-
["governanceDelete", "governance.delete"]
|
|
19479
|
-
];
|
|
19480
|
-
const HISTOGRAM_NAMES = [
|
|
19481
|
-
["compressionLatency", "compression.latency_ms"],
|
|
19482
|
-
["searchLatency", "search.latency_ms"],
|
|
19483
|
-
["contextTokens", "context.tokens"],
|
|
19484
|
-
["qualityScore", "quality.score"],
|
|
19485
|
-
["embeddingLatency", "embedding.latency_ms"],
|
|
19486
|
-
["vectorSearchLatency", "vector_search.latency_ms"]
|
|
19487
|
-
];
|
|
19488
|
-
function initMetrics(getMeter) {
|
|
19489
|
-
const meter = getMeter?.("agentmemory");
|
|
19490
|
-
counters = Object.fromEntries(COUNTER_NAMES.map(([key, name]) => [key, meter ? meter.createCounter(name) : NOOP_COUNTER]));
|
|
19491
|
-
histograms = Object.fromEntries(HISTOGRAM_NAMES.map(([key, name]) => [key, meter ? meter.createHistogram(name) : NOOP_HISTOGRAM]));
|
|
19492
|
-
return {
|
|
19493
|
-
counters,
|
|
19494
|
-
histograms
|
|
19495
|
-
};
|
|
19496
|
-
}
|
|
19497
|
-
|
|
19498
19942
|
//#endregion
|
|
19499
19943
|
//#region src/index.ts
|
|
19500
19944
|
function workerPidfilePath() {
|
|
@@ -19643,6 +20087,7 @@ async function main() {
|
|
|
19643
20087
|
const graphWeight = parseFloat(getEnvVar("AGENTMEMORY_GRAPH_WEIGHT") || "0.3");
|
|
19644
20088
|
const hybridSearch = new HybridSearch(bm25Index, vectorIndex, embeddingProvider, kv, embeddingConfig.bm25Weight, embeddingConfig.vectorWeight, graphWeight);
|
|
19645
20089
|
registerSmartSearchFunction(sdk, kv, (query, limit) => hybridSearch.search(query, limit));
|
|
20090
|
+
registerRecentSearchesSweepFunction(sdk, kv);
|
|
19646
20091
|
registerApiTriggers(sdk, kv, secret, metricsStore, provider);
|
|
19647
20092
|
registerEventTriggers(sdk, kv);
|
|
19648
20093
|
registerMcpEndpoints(sdk, kv, secret);
|
|
@@ -19710,7 +20155,7 @@ async function main() {
|
|
|
19710
20155
|
console.warn(`[agentmemory] Failed to backfill memories into BM25:`, err);
|
|
19711
20156
|
}
|
|
19712
20157
|
bootLog(`Ready. ${embeddingProvider ? "Triple-stream (BM25+Vector+Graph)" : "BM25+Graph"} search active.`);
|
|
19713
|
-
bootLog(`REST API:
|
|
20158
|
+
bootLog(`REST API: 126 endpoints at http://localhost:${config.restPort}/agentmemory/*`);
|
|
19714
20159
|
bootLog(`MCP surface (opt-in via \`npx @agentmemory/mcp\`): ${getAllTools().length} tools · 6 resources · 3 prompts`);
|
|
19715
20160
|
const viewerServer = startViewerServer(config.restPort + 2, kv, sdk, secret, config.restPort);
|
|
19716
20161
|
const autoForgetIntervalMs = parseInt(process.env.AUTO_FORGET_INTERVAL_MS || "3600000", 10);
|
|
@@ -19745,6 +20190,14 @@ async function main() {
|
|
|
19745
20190
|
});
|
|
19746
20191
|
} catch {}
|
|
19747
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();
|
|
19748
20201
|
if (isConsolidationEnabled()) {
|
|
19749
20202
|
setInterval(async () => {
|
|
19750
20203
|
try {
|
|
@@ -19776,7 +20229,7 @@ main().catch((err) => {
|
|
|
19776
20229
|
console.error(`[agentmemory] Fatal:`, err);
|
|
19777
20230
|
process.exit(1);
|
|
19778
20231
|
});
|
|
19779
|
-
|
|
19780
20232
|
//#endregion
|
|
19781
|
-
export {
|
|
19782
|
-
|
|
20233
|
+
export {};
|
|
20234
|
+
|
|
20235
|
+
//# sourceMappingURL=src-fQOMXeCp.mjs.map
|