@agentmemory/agentmemory 0.8.12 → 0.9.1
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/README.md +70 -3
- package/dist/cli.mjs +80 -15
- package/dist/cli.mjs.map +1 -1
- package/dist/index.mjs +627 -31
- package/dist/index.mjs.map +1 -1
- package/dist/{src-68MXysnV.mjs → src-Dw_gJcCy.mjs} +619 -27
- package/dist/src-Dw_gJcCy.mjs.map +1 -0
- package/dist/standalone-BEWvWM5P.mjs +457 -0
- package/dist/standalone-BEWvWM5P.mjs.map +1 -0
- package/dist/standalone.d.mts.map +1 -1
- package/dist/standalone.mjs +210 -67
- package/dist/standalone.mjs.map +1 -1
- package/dist/{tools-registry-D96ukJg4.mjs → tools-registry-BvWNlj6u.mjs} +11 -7
- package/dist/tools-registry-BvWNlj6u.mjs.map +1 -0
- package/dist/viewer/index.html +265 -1
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/dist/src-68MXysnV.mjs.map +0 -1
- package/dist/standalone-c2xiEQJ9.mjs +0 -314
- package/dist/standalone-c2xiEQJ9.mjs.map +0 -1
- package/dist/tools-registry-D96ukJg4.mjs.map +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { a as jaccardSimilarity, i as generateId, n as STREAM, r as fingerprintId, t as KV } from "./cli.mjs";
|
|
2
|
-
import { a as getEnvVar, c as isConsolidationEnabled, d as loadClaudeBridgeConfig, f as loadConfig, g as loadTeamConfig, h as loadSnapshotConfig, i as getConsolidationDecayDays, l as isContextInjectionEnabled, m as loadFallbackConfig, n as VERSION, p as loadEmbeddingConfig, r as detectEmbeddingProvider, s as isAutoCompressEnabled, t as getVisibleTools, u as isGraphExtractionEnabled } from "./tools-registry-
|
|
2
|
+
import { a as getEnvVar, c as isConsolidationEnabled, d as loadClaudeBridgeConfig, f as loadConfig, g as loadTeamConfig, h as loadSnapshotConfig, i as getConsolidationDecayDays, l as isContextInjectionEnabled, m as loadFallbackConfig, n as VERSION, p as loadEmbeddingConfig, r as detectEmbeddingProvider, s as isAutoCompressEnabled, t as getVisibleTools, u as isGraphExtractionEnabled } from "./tools-registry-BvWNlj6u.mjs";
|
|
3
3
|
import { execFile } from "node:child_process";
|
|
4
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { constants, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
5
5
|
import { basename, dirname, extname, join, resolve, sep } from "node:path";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
7
|
import { homedir } from "node:os";
|
|
@@ -12,7 +12,7 @@ import { z } from "zod";
|
|
|
12
12
|
import { promisify } from "node:util";
|
|
13
13
|
import { lookup } from "node:dns/promises";
|
|
14
14
|
import { isIP } from "node:net";
|
|
15
|
-
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
15
|
+
import { lstat, mkdir, open, readFile, readdir, writeFile } from "node:fs/promises";
|
|
16
16
|
import { createServer } from "node:http";
|
|
17
17
|
|
|
18
18
|
//#region src/providers/agent-sdk.ts
|
|
@@ -564,7 +564,11 @@ function createBaseProvider(config) {
|
|
|
564
564
|
switch (config.provider) {
|
|
565
565
|
case "minimax": return new MinimaxProvider(requireEnvVar("MINIMAX_API_KEY"), config.model, config.maxTokens);
|
|
566
566
|
case "anthropic": return new AnthropicProvider(requireEnvVar("ANTHROPIC_API_KEY"), config.model, config.maxTokens, config.baseURL);
|
|
567
|
-
case "gemini":
|
|
567
|
+
case "gemini": {
|
|
568
|
+
const geminiKey = getEnvVar("GEMINI_API_KEY") || getEnvVar("GOOGLE_API_KEY");
|
|
569
|
+
if (!geminiKey) throw new Error("GEMINI_API_KEY (or GOOGLE_API_KEY) is required for the gemini provider");
|
|
570
|
+
return new OpenRouterProvider(geminiKey, config.model, config.maxTokens, "https://generativelanguage.googleapis.com/v1beta/openai/chat/completions");
|
|
571
|
+
}
|
|
568
572
|
case "openrouter": return new OpenRouterProvider(requireEnvVar("OPENROUTER_API_KEY"), config.model, config.maxTokens, "https://openrouter.ai/api/v1/chat/completions");
|
|
569
573
|
default: return new AgentSDKProvider();
|
|
570
574
|
}
|
|
@@ -1895,7 +1899,7 @@ function stringifyForNarrative(v) {
|
|
|
1895
1899
|
return String(v);
|
|
1896
1900
|
}
|
|
1897
1901
|
}
|
|
1898
|
-
function truncate$
|
|
1902
|
+
function truncate$2(s, n) {
|
|
1899
1903
|
return s.length > n ? s.slice(0, n - 1) + "…" : s;
|
|
1900
1904
|
}
|
|
1901
1905
|
function buildSyntheticCompression(raw) {
|
|
@@ -1912,10 +1916,10 @@ function buildSyntheticCompression(raw) {
|
|
|
1912
1916
|
sessionId: raw.sessionId,
|
|
1913
1917
|
timestamp: raw.timestamp,
|
|
1914
1918
|
type: inferType(toolName, raw.hookType),
|
|
1915
|
-
title: truncate$
|
|
1916
|
-
subtitle: inputStr ? truncate$
|
|
1919
|
+
title: truncate$2(toolName || "observation", 80),
|
|
1920
|
+
subtitle: inputStr ? truncate$2(inputStr, 120) : void 0,
|
|
1917
1921
|
facts: [],
|
|
1918
|
-
narrative: truncate$
|
|
1922
|
+
narrative: truncate$2(narrativeParts.join(" | "), 400),
|
|
1919
1923
|
concepts: [],
|
|
1920
1924
|
files: extractFiles$1(raw.toolInput),
|
|
1921
1925
|
importance: 5,
|
|
@@ -2339,16 +2343,16 @@ function buildCompressionPrompt(observation) {
|
|
|
2339
2343
|
if (observation.toolName) parts.push(`Tool: ${observation.toolName}`);
|
|
2340
2344
|
if (observation.toolInput) {
|
|
2341
2345
|
const input = typeof observation.toolInput === "string" ? observation.toolInput : JSON.stringify(observation.toolInput, null, 2);
|
|
2342
|
-
parts.push(`Input:\n${truncate(input, 4e3)}`);
|
|
2346
|
+
parts.push(`Input:\n${truncate$1(input, 4e3)}`);
|
|
2343
2347
|
}
|
|
2344
2348
|
if (observation.toolOutput) {
|
|
2345
2349
|
const output = typeof observation.toolOutput === "string" ? observation.toolOutput : JSON.stringify(observation.toolOutput, null, 2);
|
|
2346
|
-
parts.push(`Output:\n${truncate(output, 4e3)}`);
|
|
2350
|
+
parts.push(`Output:\n${truncate$1(output, 4e3)}`);
|
|
2347
2351
|
}
|
|
2348
|
-
if (observation.userPrompt) parts.push(`User prompt:\n${truncate(observation.userPrompt, 2e3)}`);
|
|
2352
|
+
if (observation.userPrompt) parts.push(`User prompt:\n${truncate$1(observation.userPrompt, 2e3)}`);
|
|
2349
2353
|
return parts.join("\n\n");
|
|
2350
2354
|
}
|
|
2351
|
-
function truncate(s, max) {
|
|
2355
|
+
function truncate$1(s, max) {
|
|
2352
2356
|
return s.length > max ? s.slice(0, max) + "\n[...truncated]" : s;
|
|
2353
2357
|
}
|
|
2354
2358
|
|
|
@@ -3513,25 +3517,40 @@ function registerRememberFunction(sdk, kv) {
|
|
|
3513
3517
|
});
|
|
3514
3518
|
sdk.registerFunction("mem::forget", async (data) => {
|
|
3515
3519
|
let deleted = 0;
|
|
3520
|
+
const deletedMemoryIds = [];
|
|
3521
|
+
const deletedObservationIds = [];
|
|
3522
|
+
let deletedSession = false;
|
|
3516
3523
|
if (data.memoryId) {
|
|
3517
3524
|
await kv.delete(KV.memories, data.memoryId);
|
|
3518
3525
|
await deleteAccessLog(kv, data.memoryId);
|
|
3526
|
+
deletedMemoryIds.push(data.memoryId);
|
|
3519
3527
|
deleted++;
|
|
3520
3528
|
}
|
|
3521
3529
|
if (data.sessionId && data.observationIds && data.observationIds.length > 0) for (const obsId of data.observationIds) {
|
|
3522
3530
|
await kv.delete(KV.observations(data.sessionId), obsId);
|
|
3531
|
+
deletedObservationIds.push(obsId);
|
|
3523
3532
|
deleted++;
|
|
3524
3533
|
}
|
|
3525
3534
|
if (data.sessionId && (!data.observationIds || data.observationIds.length === 0) && !data.memoryId) {
|
|
3526
3535
|
const observations = await kv.list(KV.observations(data.sessionId));
|
|
3527
3536
|
for (const obs of observations) {
|
|
3528
3537
|
await kv.delete(KV.observations(data.sessionId), obs.id);
|
|
3538
|
+
deletedObservationIds.push(obs.id);
|
|
3529
3539
|
deleted++;
|
|
3530
3540
|
}
|
|
3531
3541
|
await kv.delete(KV.sessions, data.sessionId);
|
|
3532
3542
|
await kv.delete(KV.summaries, data.sessionId);
|
|
3543
|
+
deletedSession = true;
|
|
3533
3544
|
deleted += 2;
|
|
3534
3545
|
}
|
|
3546
|
+
if (deleted > 0) await recordAudit(kv, "forget", "mem::forget", [...deletedMemoryIds, ...deletedObservationIds], {
|
|
3547
|
+
sessionId: data.sessionId,
|
|
3548
|
+
deleted,
|
|
3549
|
+
memoriesDeleted: deletedMemoryIds.length,
|
|
3550
|
+
observationsDeleted: deletedObservationIds.length,
|
|
3551
|
+
sessionDeleted: deletedSession,
|
|
3552
|
+
reason: "user-initiated forget"
|
|
3553
|
+
});
|
|
3535
3554
|
logger.info("Memory forgotten", { deleted });
|
|
3536
3555
|
return {
|
|
3537
3556
|
success: true,
|
|
@@ -4363,7 +4382,10 @@ function registerExportImportFunction(sdk, kv) {
|
|
|
4363
4382
|
"0.8.9",
|
|
4364
4383
|
"0.8.10",
|
|
4365
4384
|
"0.8.11",
|
|
4366
|
-
"0.8.12"
|
|
4385
|
+
"0.8.12",
|
|
4386
|
+
"0.8.13",
|
|
4387
|
+
"0.9.0",
|
|
4388
|
+
"0.9.1"
|
|
4367
4389
|
]).has(importData.version)) return {
|
|
4368
4390
|
success: false,
|
|
4369
4391
|
error: `Unsupported export version: ${importData.version}`
|
|
@@ -10735,8 +10757,8 @@ function registerRetentionFunctions(sdk, kv) {
|
|
|
10735
10757
|
let evictedSemantic = 0;
|
|
10736
10758
|
const evictedIds = [];
|
|
10737
10759
|
for (const candidate of candidates) try {
|
|
10738
|
-
let scope;
|
|
10739
|
-
let resolvedSource;
|
|
10760
|
+
let scope = null;
|
|
10761
|
+
let resolvedSource = null;
|
|
10740
10762
|
if (candidate.source === "semantic") {
|
|
10741
10763
|
scope = KV.semantic;
|
|
10742
10764
|
resolvedSource = "semantic";
|
|
@@ -10746,10 +10768,11 @@ function registerRetentionFunctions(sdk, kv) {
|
|
|
10746
10768
|
} else if (await kv.get(KV.memories, candidate.memoryId) !== null) {
|
|
10747
10769
|
scope = KV.memories;
|
|
10748
10770
|
resolvedSource = "episodic";
|
|
10749
|
-
} else {
|
|
10771
|
+
} else if (await kv.get(KV.semantic, candidate.memoryId) !== null) {
|
|
10750
10772
|
scope = KV.semantic;
|
|
10751
10773
|
resolvedSource = "semantic";
|
|
10752
10774
|
}
|
|
10775
|
+
if (!scope || !resolvedSource) continue;
|
|
10753
10776
|
await kv.delete(scope, candidate.memoryId);
|
|
10754
10777
|
await kv.delete(KV.retentionScores, candidate.memoryId);
|
|
10755
10778
|
await deleteAccessLog(kv, candidate.memoryId);
|
|
@@ -10836,7 +10859,7 @@ function validateCompression(original, compressed) {
|
|
|
10836
10859
|
}
|
|
10837
10860
|
function resolveBackupPath(filePath) {
|
|
10838
10861
|
const base = basename(filePath, extname(filePath));
|
|
10839
|
-
const name = base.endsWith(".original") ? base : `${base}.original`;
|
|
10862
|
+
const name = base.endsWith(".original") ? `${base}.backup` : `${base}.original`;
|
|
10840
10863
|
return join(dirname(filePath), `${name}.md`);
|
|
10841
10864
|
}
|
|
10842
10865
|
function registerCompressFileFunction(sdk, kv, provider) {
|
|
@@ -10855,6 +10878,17 @@ function registerCompressFileFunction(sdk, kv, provider) {
|
|
|
10855
10878
|
success: false,
|
|
10856
10879
|
error: "refusing to process sensitive-looking path"
|
|
10857
10880
|
};
|
|
10881
|
+
try {
|
|
10882
|
+
if ((await lstat(absolutePath)).isSymbolicLink()) return {
|
|
10883
|
+
success: false,
|
|
10884
|
+
error: "symlinks are not supported"
|
|
10885
|
+
};
|
|
10886
|
+
} catch {
|
|
10887
|
+
return {
|
|
10888
|
+
success: false,
|
|
10889
|
+
error: "file not found"
|
|
10890
|
+
};
|
|
10891
|
+
}
|
|
10858
10892
|
let original;
|
|
10859
10893
|
try {
|
|
10860
10894
|
original = await readFile(absolutePath, "utf-8");
|
|
@@ -10878,7 +10912,23 @@ function registerCompressFileFunction(sdk, kv, provider) {
|
|
|
10878
10912
|
};
|
|
10879
10913
|
const backupPath = resolveBackupPath(absolutePath);
|
|
10880
10914
|
await writeFile(backupPath, original, "utf-8");
|
|
10881
|
-
|
|
10915
|
+
let fd = null;
|
|
10916
|
+
try {
|
|
10917
|
+
fd = await open(absolutePath, constants.O_WRONLY | constants.O_CREAT | constants.O_TRUNC | constants.O_NOFOLLOW);
|
|
10918
|
+
await fd.writeFile(compressed, "utf-8");
|
|
10919
|
+
} catch (err) {
|
|
10920
|
+
const code = err.code;
|
|
10921
|
+
if (code === "ELOOP" || code === "EINVAL") return {
|
|
10922
|
+
success: false,
|
|
10923
|
+
error: "symlinks are not supported"
|
|
10924
|
+
};
|
|
10925
|
+
return {
|
|
10926
|
+
success: false,
|
|
10927
|
+
error: "failed to write compressed file"
|
|
10928
|
+
};
|
|
10929
|
+
} finally {
|
|
10930
|
+
await fd?.close().catch(() => {});
|
|
10931
|
+
}
|
|
10882
10932
|
try {
|
|
10883
10933
|
await recordAudit(kv, "compress", "mem::compress-file", [], {
|
|
10884
10934
|
filePath: absolutePath,
|
|
@@ -10897,6 +10947,419 @@ function registerCompressFileFunction(sdk, kv, provider) {
|
|
|
10897
10947
|
});
|
|
10898
10948
|
}
|
|
10899
10949
|
|
|
10950
|
+
//#endregion
|
|
10951
|
+
//#region src/replay/jsonl-parser.ts
|
|
10952
|
+
function deriveProject(cwd) {
|
|
10953
|
+
if (!cwd) return "unknown";
|
|
10954
|
+
const parts = cwd.split("/").filter(Boolean);
|
|
10955
|
+
return parts[parts.length - 1] || "unknown";
|
|
10956
|
+
}
|
|
10957
|
+
function toText(content) {
|
|
10958
|
+
if (typeof content === "string") return content;
|
|
10959
|
+
if (!Array.isArray(content)) return "";
|
|
10960
|
+
const parts = [];
|
|
10961
|
+
for (const item of content) {
|
|
10962
|
+
if (!item || typeof item !== "object") continue;
|
|
10963
|
+
const entry = item;
|
|
10964
|
+
if (entry.type === "text" && typeof entry.text === "string") parts.push(entry.text);
|
|
10965
|
+
}
|
|
10966
|
+
return parts.join("\n");
|
|
10967
|
+
}
|
|
10968
|
+
function extractToolUses(content) {
|
|
10969
|
+
if (!Array.isArray(content)) return [];
|
|
10970
|
+
const out = [];
|
|
10971
|
+
for (const item of content) {
|
|
10972
|
+
if (!item || typeof item !== "object") continue;
|
|
10973
|
+
const entry = item;
|
|
10974
|
+
if (entry.type === "tool_use") out.push({
|
|
10975
|
+
id: typeof entry.id === "string" ? entry.id : "",
|
|
10976
|
+
name: typeof entry.name === "string" ? entry.name : "unknown",
|
|
10977
|
+
input: entry.input
|
|
10978
|
+
});
|
|
10979
|
+
}
|
|
10980
|
+
return out;
|
|
10981
|
+
}
|
|
10982
|
+
function extractToolResults(content) {
|
|
10983
|
+
if (!Array.isArray(content)) return [];
|
|
10984
|
+
const out = [];
|
|
10985
|
+
for (const item of content) {
|
|
10986
|
+
if (!item || typeof item !== "object") continue;
|
|
10987
|
+
const entry = item;
|
|
10988
|
+
if (entry.type === "tool_result") out.push({
|
|
10989
|
+
toolUseId: typeof entry.tool_use_id === "string" ? entry.tool_use_id : "",
|
|
10990
|
+
output: entry.content,
|
|
10991
|
+
isError: entry.is_error === true
|
|
10992
|
+
});
|
|
10993
|
+
}
|
|
10994
|
+
return out;
|
|
10995
|
+
}
|
|
10996
|
+
function parseJsonlText(text, fallbackSessionId) {
|
|
10997
|
+
const lines = text.split("\n").filter((l) => l.trim().length > 0);
|
|
10998
|
+
const entries = [];
|
|
10999
|
+
for (const line of lines) try {
|
|
11000
|
+
const parsed = JSON.parse(line);
|
|
11001
|
+
if (parsed && typeof parsed === "object") entries.push(parsed);
|
|
11002
|
+
} catch {}
|
|
11003
|
+
let sessionId = fallbackSessionId || "";
|
|
11004
|
+
let cwd = "";
|
|
11005
|
+
let firstTs = "";
|
|
11006
|
+
let lastTs = "";
|
|
11007
|
+
const observations = [];
|
|
11008
|
+
for (const entry of entries) {
|
|
11009
|
+
if (entry.sessionId && !sessionId) sessionId = entry.sessionId;
|
|
11010
|
+
if (entry.cwd && !cwd) cwd = entry.cwd;
|
|
11011
|
+
const ts = entry.timestamp || (/* @__PURE__ */ new Date()).toISOString();
|
|
11012
|
+
if (!firstTs) firstTs = ts;
|
|
11013
|
+
lastTs = ts;
|
|
11014
|
+
const role = entry.message?.role;
|
|
11015
|
+
const content = entry.message?.content;
|
|
11016
|
+
if (entry.type === "user" && role === "user") {
|
|
11017
|
+
const toolResults = extractToolResults(content);
|
|
11018
|
+
if (toolResults.length > 0) for (const result of toolResults) observations.push({
|
|
11019
|
+
id: generateId("obs"),
|
|
11020
|
+
sessionId: sessionId || "imported",
|
|
11021
|
+
timestamp: ts,
|
|
11022
|
+
hookType: result.isError ? "post_tool_failure" : "post_tool_use",
|
|
11023
|
+
toolName: void 0,
|
|
11024
|
+
toolInput: { toolUseId: result.toolUseId },
|
|
11025
|
+
toolOutput: result.output,
|
|
11026
|
+
raw: entry
|
|
11027
|
+
});
|
|
11028
|
+
else {
|
|
11029
|
+
const text = toText(content);
|
|
11030
|
+
if (text.trim().length > 0) observations.push({
|
|
11031
|
+
id: generateId("obs"),
|
|
11032
|
+
sessionId: sessionId || "imported",
|
|
11033
|
+
timestamp: ts,
|
|
11034
|
+
hookType: "prompt_submit",
|
|
11035
|
+
userPrompt: text,
|
|
11036
|
+
raw: entry
|
|
11037
|
+
});
|
|
11038
|
+
}
|
|
11039
|
+
} else if (entry.type === "assistant" && role === "assistant") {
|
|
11040
|
+
const text = toText(content);
|
|
11041
|
+
const tools = extractToolUses(content);
|
|
11042
|
+
if (text.trim().length > 0) observations.push({
|
|
11043
|
+
id: generateId("obs"),
|
|
11044
|
+
sessionId: sessionId || "imported",
|
|
11045
|
+
timestamp: ts,
|
|
11046
|
+
hookType: "stop",
|
|
11047
|
+
assistantResponse: text,
|
|
11048
|
+
raw: entry
|
|
11049
|
+
});
|
|
11050
|
+
for (const tool of tools) observations.push({
|
|
11051
|
+
id: generateId("obs"),
|
|
11052
|
+
sessionId: sessionId || "imported",
|
|
11053
|
+
timestamp: ts,
|
|
11054
|
+
hookType: "pre_tool_use",
|
|
11055
|
+
toolName: tool.name,
|
|
11056
|
+
toolInput: tool.input,
|
|
11057
|
+
raw: {
|
|
11058
|
+
toolUseId: tool.id,
|
|
11059
|
+
entry
|
|
11060
|
+
}
|
|
11061
|
+
});
|
|
11062
|
+
} else if (entry.type === "summary" || entry.type === "system") {}
|
|
11063
|
+
}
|
|
11064
|
+
const effectiveSessionId = sessionId || generateId("sess");
|
|
11065
|
+
for (const obs of observations) if (obs.sessionId === "imported") obs.sessionId = effectiveSessionId;
|
|
11066
|
+
const nowIso = (/* @__PURE__ */ new Date()).toISOString();
|
|
11067
|
+
return {
|
|
11068
|
+
sessionId: effectiveSessionId,
|
|
11069
|
+
project: deriveProject(cwd),
|
|
11070
|
+
cwd: cwd || process.cwd(),
|
|
11071
|
+
startedAt: firstTs || nowIso,
|
|
11072
|
+
endedAt: lastTs || nowIso,
|
|
11073
|
+
observations
|
|
11074
|
+
};
|
|
11075
|
+
}
|
|
11076
|
+
|
|
11077
|
+
//#endregion
|
|
11078
|
+
//#region src/replay/timeline.ts
|
|
11079
|
+
const DEFAULT_CHARS_PER_SEC = 40;
|
|
11080
|
+
const MIN_EVENT_MS = 300;
|
|
11081
|
+
const MAX_EVENT_MS = 2e4;
|
|
11082
|
+
function kindFromHook(obs) {
|
|
11083
|
+
switch (obs.hookType) {
|
|
11084
|
+
case "session_start": return "session_start";
|
|
11085
|
+
case "session_end": return "session_end";
|
|
11086
|
+
case "prompt_submit": return "prompt";
|
|
11087
|
+
case "stop": return obs.assistantResponse ? "response" : "hook";
|
|
11088
|
+
case "pre_tool_use": return "tool_call";
|
|
11089
|
+
case "post_tool_use": return "tool_result";
|
|
11090
|
+
case "post_tool_failure": return "tool_error";
|
|
11091
|
+
default: return "hook";
|
|
11092
|
+
}
|
|
11093
|
+
}
|
|
11094
|
+
function labelFor(obs, kind) {
|
|
11095
|
+
switch (kind) {
|
|
11096
|
+
case "prompt": return truncate(obs.userPrompt || "User prompt", 80);
|
|
11097
|
+
case "response": return truncate(obs.assistantResponse || "Assistant response", 80);
|
|
11098
|
+
case "tool_call": return `${obs.toolName || "tool"} ▸ call`;
|
|
11099
|
+
case "tool_result": return `${obs.toolName || "tool"} ▸ result`;
|
|
11100
|
+
case "tool_error": return `${obs.toolName || "tool"} ▸ error`;
|
|
11101
|
+
case "session_start": return "Session start";
|
|
11102
|
+
case "session_end": return "Session end";
|
|
11103
|
+
default: return obs.hookType;
|
|
11104
|
+
}
|
|
11105
|
+
}
|
|
11106
|
+
function truncate(text, max) {
|
|
11107
|
+
if (text.length <= max) return text;
|
|
11108
|
+
return text.slice(0, max - 1) + "…";
|
|
11109
|
+
}
|
|
11110
|
+
function bodyFor(obs, kind) {
|
|
11111
|
+
if (kind === "prompt") return obs.userPrompt;
|
|
11112
|
+
if (kind === "response") return obs.assistantResponse;
|
|
11113
|
+
}
|
|
11114
|
+
function estimateDurationMs(ev) {
|
|
11115
|
+
const chars = (ev.body?.length || 0) + (typeof ev.toolInput === "string" ? ev.toolInput.length : 0) + (typeof ev.toolOutput === "string" ? ev.toolOutput.length : 0);
|
|
11116
|
+
if (chars === 0) return MIN_EVENT_MS;
|
|
11117
|
+
const ms = Math.round(chars / DEFAULT_CHARS_PER_SEC * 1e3);
|
|
11118
|
+
return Math.max(MIN_EVENT_MS, Math.min(MAX_EVENT_MS, ms));
|
|
11119
|
+
}
|
|
11120
|
+
function projectTimeline(observations) {
|
|
11121
|
+
if (observations.length === 0) {
|
|
11122
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
11123
|
+
return {
|
|
11124
|
+
sessionId: "",
|
|
11125
|
+
startedAt: now,
|
|
11126
|
+
endedAt: now,
|
|
11127
|
+
totalDurationMs: 0,
|
|
11128
|
+
eventCount: 0,
|
|
11129
|
+
events: []
|
|
11130
|
+
};
|
|
11131
|
+
}
|
|
11132
|
+
const sorted = [...observations].sort((a, b) => a.timestamp.localeCompare(b.timestamp));
|
|
11133
|
+
const startedAt = sorted[0].timestamp;
|
|
11134
|
+
const startMs = Date.parse(startedAt);
|
|
11135
|
+
const events = [];
|
|
11136
|
+
let syntheticOffset = 0;
|
|
11137
|
+
const allSameTs = sorted.every((o) => o.timestamp === startedAt);
|
|
11138
|
+
for (const obs of sorted) {
|
|
11139
|
+
const kind = kindFromHook(obs);
|
|
11140
|
+
const body = bodyFor(obs, kind);
|
|
11141
|
+
const obsMs = Date.parse(obs.timestamp);
|
|
11142
|
+
const offsetMs = allSameTs ? syntheticOffset : Number.isFinite(obsMs) && Number.isFinite(startMs) ? Math.max(0, obsMs - startMs) : syntheticOffset;
|
|
11143
|
+
const event = {
|
|
11144
|
+
id: obs.id,
|
|
11145
|
+
sessionId: obs.sessionId,
|
|
11146
|
+
ts: obs.timestamp,
|
|
11147
|
+
offsetMs,
|
|
11148
|
+
durationMs: 0,
|
|
11149
|
+
kind,
|
|
11150
|
+
label: labelFor(obs, kind),
|
|
11151
|
+
body,
|
|
11152
|
+
toolName: obs.toolName,
|
|
11153
|
+
toolInput: obs.toolInput,
|
|
11154
|
+
toolOutput: obs.toolOutput
|
|
11155
|
+
};
|
|
11156
|
+
event.durationMs = estimateDurationMs(event);
|
|
11157
|
+
events.push(event);
|
|
11158
|
+
syntheticOffset += event.durationMs;
|
|
11159
|
+
}
|
|
11160
|
+
const last = events[events.length - 1];
|
|
11161
|
+
const totalDurationMs = last.offsetMs + last.durationMs;
|
|
11162
|
+
return {
|
|
11163
|
+
sessionId: sorted[0].sessionId,
|
|
11164
|
+
startedAt,
|
|
11165
|
+
endedAt: sorted[sorted.length - 1].timestamp,
|
|
11166
|
+
totalDurationMs,
|
|
11167
|
+
eventCount: events.length,
|
|
11168
|
+
events
|
|
11169
|
+
};
|
|
11170
|
+
}
|
|
11171
|
+
|
|
11172
|
+
//#endregion
|
|
11173
|
+
//#region src/functions/replay.ts
|
|
11174
|
+
const SENSITIVE_PATH_PATTERNS = [
|
|
11175
|
+
/(^|[\\/_.-])secret([\\/_.-]|s?$)/i,
|
|
11176
|
+
/(^|[\\/_.-])credentials?([\\/_.-]|$)/i,
|
|
11177
|
+
/(^|[\\/_.-])private[_-]?key([\\/_.-]|$)/i,
|
|
11178
|
+
/(^|[\\/])\.env(\.[\w-]+)?$/i,
|
|
11179
|
+
/(^|[\\/_.-])id_rsa([\\/_.-]|$)/i,
|
|
11180
|
+
/(^|[\\/])auth[_-]?token([\\/_.-]|$)/i,
|
|
11181
|
+
/(^|[\\/])bearer[_-]?token([\\/_.-]|$)/i,
|
|
11182
|
+
/(^|[\\/])access[_-]?token([\\/_.-]|$)/i,
|
|
11183
|
+
/(^|[\\/])api[_-]?token([\\/_.-]|$)/i
|
|
11184
|
+
];
|
|
11185
|
+
function isSensitive(path) {
|
|
11186
|
+
return SENSITIVE_PATH_PATTERNS.some((re) => re.test(path));
|
|
11187
|
+
}
|
|
11188
|
+
async function isSymlink(path) {
|
|
11189
|
+
try {
|
|
11190
|
+
return (await lstat(path)).isSymbolicLink();
|
|
11191
|
+
} catch {
|
|
11192
|
+
return false;
|
|
11193
|
+
}
|
|
11194
|
+
}
|
|
11195
|
+
function rawFromCompressed(obs) {
|
|
11196
|
+
return {
|
|
11197
|
+
id: obs.id,
|
|
11198
|
+
sessionId: obs.sessionId,
|
|
11199
|
+
timestamp: obs.timestamp,
|
|
11200
|
+
hookType: "post_tool_use",
|
|
11201
|
+
toolName: void 0,
|
|
11202
|
+
toolInput: void 0,
|
|
11203
|
+
toolOutput: void 0,
|
|
11204
|
+
userPrompt: obs.type === "conversation" ? obs.narrative : void 0,
|
|
11205
|
+
assistantResponse: void 0,
|
|
11206
|
+
raw: {
|
|
11207
|
+
title: obs.title,
|
|
11208
|
+
narrative: obs.narrative,
|
|
11209
|
+
facts: obs.facts
|
|
11210
|
+
}
|
|
11211
|
+
};
|
|
11212
|
+
}
|
|
11213
|
+
function isRawShape(o) {
|
|
11214
|
+
if (!o || typeof o !== "object") return false;
|
|
11215
|
+
return typeof o.hookType === "string";
|
|
11216
|
+
}
|
|
11217
|
+
async function loadObservations(kv, sessionId) {
|
|
11218
|
+
return (await kv.list(KV.observations(sessionId))).map((r) => isRawShape(r) ? r : rawFromCompressed(r));
|
|
11219
|
+
}
|
|
11220
|
+
async function findJsonlFiles(root, limit = 200) {
|
|
11221
|
+
const out = [];
|
|
11222
|
+
async function walk(dir) {
|
|
11223
|
+
if (out.length >= limit) return;
|
|
11224
|
+
let names;
|
|
11225
|
+
try {
|
|
11226
|
+
names = await readdir(dir);
|
|
11227
|
+
} catch {
|
|
11228
|
+
return;
|
|
11229
|
+
}
|
|
11230
|
+
for (const name of names) {
|
|
11231
|
+
if (out.length >= limit) return;
|
|
11232
|
+
const full = join(dir, name);
|
|
11233
|
+
let st;
|
|
11234
|
+
try {
|
|
11235
|
+
st = await lstat(full);
|
|
11236
|
+
} catch {
|
|
11237
|
+
continue;
|
|
11238
|
+
}
|
|
11239
|
+
if (st.isSymbolicLink()) continue;
|
|
11240
|
+
if (st.isDirectory()) await walk(full);
|
|
11241
|
+
else if (st.isFile() && name.endsWith(".jsonl")) out.push(full);
|
|
11242
|
+
}
|
|
11243
|
+
}
|
|
11244
|
+
await walk(root);
|
|
11245
|
+
return out;
|
|
11246
|
+
}
|
|
11247
|
+
function registerReplayFunctions(sdk, kv) {
|
|
11248
|
+
sdk.registerFunction("mem::replay::load", async (data) => {
|
|
11249
|
+
if (!data?.sessionId || typeof data.sessionId !== "string") return {
|
|
11250
|
+
success: false,
|
|
11251
|
+
error: "sessionId is required"
|
|
11252
|
+
};
|
|
11253
|
+
const session = await kv.get(KV.sessions, data.sessionId);
|
|
11254
|
+
return {
|
|
11255
|
+
success: true,
|
|
11256
|
+
timeline: projectTimeline(await loadObservations(kv, data.sessionId)),
|
|
11257
|
+
session
|
|
11258
|
+
};
|
|
11259
|
+
});
|
|
11260
|
+
sdk.registerFunction("mem::replay::sessions", async () => {
|
|
11261
|
+
const sessions = await kv.list(KV.sessions);
|
|
11262
|
+
sessions.sort((a, b) => (b.startedAt || "").localeCompare(a.startedAt || ""));
|
|
11263
|
+
return {
|
|
11264
|
+
success: true,
|
|
11265
|
+
sessions
|
|
11266
|
+
};
|
|
11267
|
+
});
|
|
11268
|
+
sdk.registerFunction("mem::replay::import-jsonl", async (data = {}) => {
|
|
11269
|
+
const defaultRoot = join(homedir(), ".claude", "projects");
|
|
11270
|
+
const rawPath = data.path || defaultRoot;
|
|
11271
|
+
if (typeof rawPath !== "string" || rawPath.length === 0) return {
|
|
11272
|
+
success: false,
|
|
11273
|
+
error: "path must be a non-empty string"
|
|
11274
|
+
};
|
|
11275
|
+
const abs = resolve(rawPath.startsWith("~") ? join(homedir(), rawPath.slice(1)) : rawPath);
|
|
11276
|
+
if (isSensitive(abs)) return {
|
|
11277
|
+
success: false,
|
|
11278
|
+
error: "refusing to process sensitive-looking path"
|
|
11279
|
+
};
|
|
11280
|
+
if (await isSymlink(abs)) return {
|
|
11281
|
+
success: false,
|
|
11282
|
+
error: "symlinks are not supported"
|
|
11283
|
+
};
|
|
11284
|
+
let stat;
|
|
11285
|
+
try {
|
|
11286
|
+
stat = await lstat(abs);
|
|
11287
|
+
} catch {
|
|
11288
|
+
return {
|
|
11289
|
+
success: false,
|
|
11290
|
+
error: "path not found"
|
|
11291
|
+
};
|
|
11292
|
+
}
|
|
11293
|
+
let files = [];
|
|
11294
|
+
if (stat.isDirectory()) files = await findJsonlFiles(abs, data.maxFiles || 200);
|
|
11295
|
+
else if (stat.isFile() && abs.endsWith(".jsonl")) files = [abs];
|
|
11296
|
+
else return {
|
|
11297
|
+
success: false,
|
|
11298
|
+
error: "path must be a .jsonl file or directory"
|
|
11299
|
+
};
|
|
11300
|
+
if (files.length === 0) return {
|
|
11301
|
+
success: true,
|
|
11302
|
+
imported: 0,
|
|
11303
|
+
sessionIds: [],
|
|
11304
|
+
observations: 0
|
|
11305
|
+
};
|
|
11306
|
+
const sessionIds = [];
|
|
11307
|
+
let observationCount = 0;
|
|
11308
|
+
for (const file of files) {
|
|
11309
|
+
if (isSensitive(file)) continue;
|
|
11310
|
+
if (await isSymlink(file)) continue;
|
|
11311
|
+
let text;
|
|
11312
|
+
try {
|
|
11313
|
+
text = await readFile(file, "utf-8");
|
|
11314
|
+
} catch (err) {
|
|
11315
|
+
logger.warn("replay: failed to read jsonl", {
|
|
11316
|
+
file,
|
|
11317
|
+
error: err instanceof Error ? err.message : String(err)
|
|
11318
|
+
});
|
|
11319
|
+
continue;
|
|
11320
|
+
}
|
|
11321
|
+
const parsed = parseJsonlText(text, generateId("sess"));
|
|
11322
|
+
if (parsed.observations.length === 0) continue;
|
|
11323
|
+
const existing = await kv.get(KV.sessions, parsed.sessionId);
|
|
11324
|
+
if (existing) {
|
|
11325
|
+
existing.observationCount = (existing.observationCount || 0) + parsed.observations.length;
|
|
11326
|
+
if (parsed.endedAt > (existing.endedAt || "")) existing.endedAt = parsed.endedAt;
|
|
11327
|
+
if (existing.status === "active") existing.status = "completed";
|
|
11328
|
+
const existingTags = existing.tags || [];
|
|
11329
|
+
if (!existingTags.includes("jsonl-import")) existing.tags = [...existingTags, "jsonl-import"];
|
|
11330
|
+
await kv.set(KV.sessions, existing.id, existing);
|
|
11331
|
+
} else {
|
|
11332
|
+
const session = {
|
|
11333
|
+
id: parsed.sessionId,
|
|
11334
|
+
project: parsed.project,
|
|
11335
|
+
cwd: parsed.cwd,
|
|
11336
|
+
startedAt: parsed.startedAt,
|
|
11337
|
+
endedAt: parsed.endedAt,
|
|
11338
|
+
status: "completed",
|
|
11339
|
+
observationCount: parsed.observations.length,
|
|
11340
|
+
tags: ["jsonl-import"]
|
|
11341
|
+
};
|
|
11342
|
+
await kv.set(KV.sessions, session.id, session);
|
|
11343
|
+
}
|
|
11344
|
+
await Promise.all(parsed.observations.map((obs) => kv.set(KV.observations(parsed.sessionId), obs.id, obs)));
|
|
11345
|
+
observationCount += parsed.observations.length;
|
|
11346
|
+
sessionIds.push(parsed.sessionId);
|
|
11347
|
+
}
|
|
11348
|
+
await safeAudit(kv, "import", "mem::replay::import-jsonl", sessionIds, {
|
|
11349
|
+
source: "jsonl",
|
|
11350
|
+
path: abs,
|
|
11351
|
+
files: files.length,
|
|
11352
|
+
observations: observationCount
|
|
11353
|
+
});
|
|
11354
|
+
return {
|
|
11355
|
+
success: true,
|
|
11356
|
+
imported: files.length,
|
|
11357
|
+
sessionIds,
|
|
11358
|
+
observations: observationCount
|
|
11359
|
+
};
|
|
11360
|
+
});
|
|
11361
|
+
}
|
|
11362
|
+
|
|
10900
11363
|
//#endregion
|
|
10901
11364
|
//#region src/health/thresholds.ts
|
|
10902
11365
|
const DEFAULTS = {
|
|
@@ -10905,7 +11368,8 @@ const DEFAULTS = {
|
|
|
10905
11368
|
cpuWarnPercent: 80,
|
|
10906
11369
|
cpuCriticalPercent: 90,
|
|
10907
11370
|
memoryWarnPercent: 80,
|
|
10908
|
-
memoryCriticalPercent: 95
|
|
11371
|
+
memoryCriticalPercent: 95,
|
|
11372
|
+
memoryRssFloorBytes: 512 * 1024 * 1024
|
|
10909
11373
|
};
|
|
10910
11374
|
function evaluateHealth(snapshot, config = {}) {
|
|
10911
11375
|
const cfg = {
|
|
@@ -10937,13 +11401,16 @@ function evaluateHealth(snapshot, config = {}) {
|
|
|
10937
11401
|
degraded = true;
|
|
10938
11402
|
}
|
|
10939
11403
|
const memPercent = snapshot.memory.heapTotal > 0 ? snapshot.memory.heapUsed / snapshot.memory.heapTotal * 100 : 0;
|
|
10940
|
-
|
|
10941
|
-
|
|
11404
|
+
const rss = snapshot.memory.rss ?? 0;
|
|
11405
|
+
const rssAboveFloor = rss >= cfg.memoryRssFloorBytes;
|
|
11406
|
+
const memMb = Math.round(rss / (1024 * 1024));
|
|
11407
|
+
if (memPercent > cfg.memoryCriticalPercent && rssAboveFloor) {
|
|
11408
|
+
alerts.push(`memory_critical_${Math.round(memPercent)}%_rss${memMb}mb`);
|
|
10942
11409
|
critical = true;
|
|
10943
|
-
} else if (memPercent > cfg.memoryWarnPercent) {
|
|
10944
|
-
alerts.push(`memory_warn_${Math.round(memPercent)}
|
|
11410
|
+
} else if (memPercent > cfg.memoryWarnPercent && rssAboveFloor) {
|
|
11411
|
+
alerts.push(`memory_warn_${Math.round(memPercent)}%_rss${memMb}mb`);
|
|
10945
11412
|
degraded = true;
|
|
10946
|
-
}
|
|
11413
|
+
} else if (memPercent > cfg.memoryWarnPercent) alerts.push(`memory_heap_tight_${Math.round(memPercent)}%_rss${memMb}mb`);
|
|
10947
11414
|
return {
|
|
10948
11415
|
status: critical ? "critical" : degraded ? "degraded" : "healthy",
|
|
10949
11416
|
alerts
|
|
@@ -11065,6 +11532,7 @@ function buildViewerCsp(nonce) {
|
|
|
11065
11532
|
|
|
11066
11533
|
//#endregion
|
|
11067
11534
|
//#region src/viewer/document.ts
|
|
11535
|
+
const VIEWER_VERSION_PLACEHOLDER = "__AGENTMEMORY_VERSION__";
|
|
11068
11536
|
function loadViewerTemplate() {
|
|
11069
11537
|
const base = dirname(fileURLToPath(import.meta.url));
|
|
11070
11538
|
const candidates = [
|
|
@@ -11083,7 +11551,7 @@ function renderViewerDocument() {
|
|
|
11083
11551
|
const nonce = createViewerNonce();
|
|
11084
11552
|
return {
|
|
11085
11553
|
found: true,
|
|
11086
|
-
html: template.replaceAll(VIEWER_NONCE_PLACEHOLDER, nonce),
|
|
11554
|
+
html: template.replaceAll(VIEWER_NONCE_PLACEHOLDER, nonce).replaceAll(VIEWER_VERSION_PLACEHOLDER, VERSION),
|
|
11087
11555
|
csp: buildViewerCsp(nonce)
|
|
11088
11556
|
};
|
|
11089
11557
|
}
|
|
@@ -11338,6 +11806,81 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
11338
11806
|
http_method: "POST"
|
|
11339
11807
|
}
|
|
11340
11808
|
});
|
|
11809
|
+
sdk.registerFunction("api::replay::load", async (req) => {
|
|
11810
|
+
const authErr = checkAuth(req, secret);
|
|
11811
|
+
if (authErr) return authErr;
|
|
11812
|
+
const sessionId = asNonEmptyString$1(req.query_params?.["sessionId"]);
|
|
11813
|
+
if (!sessionId) return {
|
|
11814
|
+
status_code: 400,
|
|
11815
|
+
body: { error: "sessionId is required" }
|
|
11816
|
+
};
|
|
11817
|
+
return {
|
|
11818
|
+
status_code: 200,
|
|
11819
|
+
body: await sdk.trigger({
|
|
11820
|
+
function_id: "mem::replay::load",
|
|
11821
|
+
payload: { sessionId }
|
|
11822
|
+
})
|
|
11823
|
+
};
|
|
11824
|
+
});
|
|
11825
|
+
sdk.registerTrigger({
|
|
11826
|
+
type: "http",
|
|
11827
|
+
function_id: "api::replay::load",
|
|
11828
|
+
config: {
|
|
11829
|
+
api_path: "/agentmemory/replay/load",
|
|
11830
|
+
http_method: "GET"
|
|
11831
|
+
}
|
|
11832
|
+
});
|
|
11833
|
+
sdk.registerFunction("api::replay::sessions", async (req) => {
|
|
11834
|
+
const authErr = checkAuth(req, secret);
|
|
11835
|
+
if (authErr) return authErr;
|
|
11836
|
+
return {
|
|
11837
|
+
status_code: 200,
|
|
11838
|
+
body: await sdk.trigger({ function_id: "mem::replay::sessions" })
|
|
11839
|
+
};
|
|
11840
|
+
});
|
|
11841
|
+
sdk.registerTrigger({
|
|
11842
|
+
type: "http",
|
|
11843
|
+
function_id: "api::replay::sessions",
|
|
11844
|
+
config: {
|
|
11845
|
+
api_path: "/agentmemory/replay/sessions",
|
|
11846
|
+
http_method: "GET"
|
|
11847
|
+
}
|
|
11848
|
+
});
|
|
11849
|
+
sdk.registerFunction("api::replay::import", async (req) => {
|
|
11850
|
+
const authErr = checkAuth(req, secret);
|
|
11851
|
+
if (authErr) return authErr;
|
|
11852
|
+
const body = req.body ?? {};
|
|
11853
|
+
const payload = {};
|
|
11854
|
+
if (body.path !== void 0) {
|
|
11855
|
+
if (typeof body.path !== "string" || body.path.trim().length === 0) return {
|
|
11856
|
+
status_code: 400,
|
|
11857
|
+
body: { error: "path must be a non-empty string" }
|
|
11858
|
+
};
|
|
11859
|
+
payload.path = body.path.trim();
|
|
11860
|
+
}
|
|
11861
|
+
if (body.maxFiles !== void 0) {
|
|
11862
|
+
if (!Number.isInteger(body.maxFiles) || body.maxFiles < 1) return {
|
|
11863
|
+
status_code: 400,
|
|
11864
|
+
body: { error: "maxFiles must be a positive integer" }
|
|
11865
|
+
};
|
|
11866
|
+
payload.maxFiles = body.maxFiles;
|
|
11867
|
+
}
|
|
11868
|
+
return {
|
|
11869
|
+
status_code: 202,
|
|
11870
|
+
body: await sdk.trigger({
|
|
11871
|
+
function_id: "mem::replay::import-jsonl",
|
|
11872
|
+
payload
|
|
11873
|
+
})
|
|
11874
|
+
};
|
|
11875
|
+
});
|
|
11876
|
+
sdk.registerTrigger({
|
|
11877
|
+
type: "http",
|
|
11878
|
+
function_id: "api::replay::import",
|
|
11879
|
+
config: {
|
|
11880
|
+
api_path: "/agentmemory/replay/import-jsonl",
|
|
11881
|
+
http_method: "POST"
|
|
11882
|
+
}
|
|
11883
|
+
});
|
|
11341
11884
|
sdk.registerFunction("api::session::start", async (req) => {
|
|
11342
11885
|
const body = req.body ?? {};
|
|
11343
11886
|
const sessionId = asNonEmptyString$1(body.sessionId);
|
|
@@ -12245,6 +12788,54 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
12245
12788
|
http_method: "GET"
|
|
12246
12789
|
}
|
|
12247
12790
|
});
|
|
12791
|
+
sdk.registerFunction("api::semantic-list", async (req) => {
|
|
12792
|
+
const authErr = checkAuth(req, secret);
|
|
12793
|
+
if (authErr) return authErr;
|
|
12794
|
+
return {
|
|
12795
|
+
status_code: 200,
|
|
12796
|
+
body: { semantic: await kv.list(KV.semantic) }
|
|
12797
|
+
};
|
|
12798
|
+
});
|
|
12799
|
+
sdk.registerTrigger({
|
|
12800
|
+
type: "http",
|
|
12801
|
+
function_id: "api::semantic-list",
|
|
12802
|
+
config: {
|
|
12803
|
+
api_path: "/agentmemory/semantic",
|
|
12804
|
+
http_method: "GET"
|
|
12805
|
+
}
|
|
12806
|
+
});
|
|
12807
|
+
sdk.registerFunction("api::procedural-list", async (req) => {
|
|
12808
|
+
const authErr = checkAuth(req, secret);
|
|
12809
|
+
if (authErr) return authErr;
|
|
12810
|
+
return {
|
|
12811
|
+
status_code: 200,
|
|
12812
|
+
body: { procedural: await kv.list(KV.procedural) }
|
|
12813
|
+
};
|
|
12814
|
+
});
|
|
12815
|
+
sdk.registerTrigger({
|
|
12816
|
+
type: "http",
|
|
12817
|
+
function_id: "api::procedural-list",
|
|
12818
|
+
config: {
|
|
12819
|
+
api_path: "/agentmemory/procedural",
|
|
12820
|
+
http_method: "GET"
|
|
12821
|
+
}
|
|
12822
|
+
});
|
|
12823
|
+
sdk.registerFunction("api::relations-list", async (req) => {
|
|
12824
|
+
const authErr = checkAuth(req, secret);
|
|
12825
|
+
if (authErr) return authErr;
|
|
12826
|
+
return {
|
|
12827
|
+
status_code: 200,
|
|
12828
|
+
body: { relations: await kv.list(KV.relations) }
|
|
12829
|
+
};
|
|
12830
|
+
});
|
|
12831
|
+
sdk.registerTrigger({
|
|
12832
|
+
type: "http",
|
|
12833
|
+
function_id: "api::relations-list",
|
|
12834
|
+
config: {
|
|
12835
|
+
api_path: "/agentmemory/relations",
|
|
12836
|
+
http_method: "GET"
|
|
12837
|
+
}
|
|
12838
|
+
});
|
|
12248
12839
|
sdk.registerFunction("api::action-create", async (req) => {
|
|
12249
12840
|
const authErr = checkAuth(req, secret);
|
|
12250
12841
|
if (authErr) return authErr;
|
|
@@ -15590,6 +16181,7 @@ async function main() {
|
|
|
15590
16181
|
registerTemporalGraphFunctions(sdk, kv, provider);
|
|
15591
16182
|
registerRetentionFunctions(sdk, kv);
|
|
15592
16183
|
registerCompressFileFunction(sdk, kv, provider);
|
|
16184
|
+
registerReplayFunctions(sdk, kv);
|
|
15593
16185
|
console.log(`[agentmemory] v0.6 advanced retrieval: sliding-window, query-expansion, temporal-graph, retention-scoring`);
|
|
15594
16186
|
console.log(`[agentmemory] Orchestration layer: actions, frontier, leases, routines, signals, checkpoints, flow-compress, mesh, branch-aware, sentinels, sketches, crystallize, diagnostics, facets`);
|
|
15595
16187
|
const snapshotConfig = loadSnapshotConfig();
|
|
@@ -15629,7 +16221,7 @@ async function main() {
|
|
|
15629
16221
|
}
|
|
15630
16222
|
}
|
|
15631
16223
|
console.log(`[agentmemory] Ready. ${embeddingProvider ? "Triple-stream (BM25+Vector+Graph)" : "BM25+Graph"} search active.`);
|
|
15632
|
-
console.log(`[agentmemory] Endpoints:
|
|
16224
|
+
console.log(`[agentmemory] Endpoints: 107 REST + 44 MCP tools + 6 MCP resources + 3 MCP prompts`);
|
|
15633
16225
|
const viewerServer = startViewerServer(config.restPort + 2, kv, sdk, secret, config.restPort);
|
|
15634
16226
|
const autoForgetIntervalMs = parseInt(process.env.AUTO_FORGET_INTERVAL_MS || "3600000", 10);
|
|
15635
16227
|
const consolidationIntervalMs = parseInt(process.env.CONSOLIDATION_INTERVAL_MS || "7200000", 10);
|
|
@@ -15696,4 +16288,4 @@ main().catch((err) => {
|
|
|
15696
16288
|
|
|
15697
16289
|
//#endregion
|
|
15698
16290
|
export { };
|
|
15699
|
-
//# sourceMappingURL=src-
|
|
16291
|
+
//# sourceMappingURL=src-Dw_gJcCy.mjs.map
|