@agentmemory/agentmemory 0.9.21 → 0.9.23
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 +7 -2
- package/README.md +288 -33
- package/dist/cli.d.mts +5 -1
- package/dist/cli.d.mts.map +1 -0
- package/dist/cli.mjs +128 -703
- package/dist/cli.mjs.map +1 -1
- package/dist/connect-Cf9bmBqO.mjs +1020 -0
- package/dist/connect-Cf9bmBqO.mjs.map +1 -0
- package/dist/hooks/notification.mjs +46 -21
- package/dist/hooks/notification.mjs.map +1 -1
- package/dist/hooks/post-tool-failure.mjs +47 -21
- package/dist/hooks/post-tool-failure.mjs.map +1 -1
- package/dist/hooks/post-tool-use.mjs +57 -22
- package/dist/hooks/post-tool-use.mjs.map +1 -1
- package/dist/hooks/pre-compact.mjs +26 -2
- package/dist/hooks/pre-compact.mjs.map +1 -1
- package/dist/hooks/pre-tool-use.mjs +19 -12
- package/dist/hooks/pre-tool-use.mjs.map +1 -1
- package/dist/hooks/prompt-submit.mjs +39 -16
- package/dist/hooks/prompt-submit.mjs.map +1 -1
- package/dist/hooks/session-end.mjs +26 -33
- package/dist/hooks/session-end.mjs.map +1 -1
- package/dist/hooks/session-start.mjs +28 -3
- package/dist/hooks/session-start.mjs.map +1 -1
- package/dist/hooks/stop.mjs +14 -9
- package/dist/hooks/stop.mjs.map +1 -1
- package/dist/hooks/subagent-start.mjs +31 -4
- package/dist/hooks/subagent-start.mjs.map +1 -1
- package/dist/hooks/subagent-stop.mjs +45 -20
- package/dist/hooks/subagent-stop.mjs.map +1 -1
- package/dist/hooks/task-completed.mjs +44 -21
- package/dist/hooks/task-completed.mjs.map +1 -1
- package/dist/iii-config.docker.yaml +3 -2
- package/dist/iii-config.yaml +11 -2
- package/dist/{image-refs-R3tin9MR.mjs → image-refs-CJS5B9Gq.mjs} +2 -2
- package/dist/{image-refs-R3tin9MR.mjs.map → image-refs-CJS5B9Gq.mjs.map} +1 -1
- package/dist/{image-store-DyrKZKqZ.mjs → image-store-CdE0amb1.mjs} +1 -1
- package/dist/index.mjs +866 -380
- package/dist/index.mjs.map +1 -1
- package/dist/logger-xlVlvCWX.mjs +43 -0
- package/dist/logger-xlVlvCWX.mjs.map +1 -0
- package/dist/schema-BkALl7Z_.mjs +74 -0
- package/dist/schema-BkALl7Z_.mjs.map +1 -0
- package/dist/{src-D5arboxc.mjs → src-DvS3bhMe.mjs} +844 -395
- package/dist/src-DvS3bhMe.mjs.map +1 -0
- package/dist/{standalone-C7BgzzIN.mjs → standalone-DHQcPX_g.mjs} +107 -14
- package/dist/standalone-DHQcPX_g.mjs.map +1 -0
- package/dist/standalone.d.mts.map +1 -1
- package/dist/standalone.mjs +108 -12
- package/dist/standalone.mjs.map +1 -1
- package/dist/{tools-registry-CRTWUFw9.mjs → tools-registry-DJizX9Az.mjs} +51 -12
- package/dist/tools-registry-DJizX9Az.mjs.map +1 -0
- package/dist/version-BPfyI4Kc.mjs +6 -0
- package/dist/version-BPfyI4Kc.mjs.map +1 -0
- package/dist/viewer/index.html +85 -10
- package/iii-config.docker.yaml +3 -2
- package/iii-config.yaml +11 -2
- package/package.json +6 -4
- package/plugin/.claude-plugin/plugin.json +2 -2
- package/plugin/.codex-plugin/plugin.json +2 -2
- package/plugin/.mcp.copilot.json +15 -0
- package/plugin/.mcp.json +3 -2
- package/plugin/hooks/hooks.copilot.json +72 -0
- package/plugin/opencode/agentmemory-capture.ts +34 -9
- package/plugin/plugin.json +15 -0
- package/plugin/scripts/diagnostics.d.mts +17 -0
- package/plugin/scripts/diagnostics.d.mts.map +1 -0
- package/plugin/scripts/diagnostics.mjs.map +1 -0
- package/plugin/scripts/notification.mjs +46 -21
- package/plugin/scripts/notification.mjs.map +1 -1
- package/plugin/scripts/post-tool-failure.mjs +47 -21
- package/plugin/scripts/post-tool-failure.mjs.map +1 -1
- package/plugin/scripts/post-tool-use.mjs +57 -22
- package/plugin/scripts/post-tool-use.mjs.map +1 -1
- package/plugin/scripts/pre-compact.mjs +26 -2
- package/plugin/scripts/pre-compact.mjs.map +1 -1
- package/plugin/scripts/pre-tool-use.mjs +19 -12
- package/plugin/scripts/pre-tool-use.mjs.map +1 -1
- package/plugin/scripts/prompt-submit.mjs +39 -16
- package/plugin/scripts/prompt-submit.mjs.map +1 -1
- package/plugin/scripts/session-end.mjs +26 -33
- package/plugin/scripts/session-end.mjs.map +1 -1
- package/plugin/scripts/session-start.mjs +28 -3
- package/plugin/scripts/session-start.mjs.map +1 -1
- package/plugin/scripts/stop.mjs +14 -9
- package/plugin/scripts/stop.mjs.map +1 -1
- package/plugin/scripts/subagent-start.mjs +31 -4
- package/plugin/scripts/subagent-start.mjs.map +1 -1
- package/plugin/scripts/subagent-stop.mjs +45 -20
- package/plugin/scripts/subagent-stop.mjs.map +1 -1
- package/plugin/scripts/task-completed.mjs +44 -21
- package/plugin/scripts/task-completed.mjs.map +1 -1
- package/dist/src-D5arboxc.mjs.map +0 -1
- package/dist/standalone-C7BgzzIN.mjs.map +0 -1
- package/dist/tools-registry-CRTWUFw9.mjs.map +0 -1
package/dist/cli.mjs
CHANGED
|
@@ -1,101 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { n as resolveAdapter, r as runAdapter } from "./connect-Cf9bmBqO.mjs";
|
|
3
|
+
import { i as generateId } from "./schema-BkALl7Z_.mjs";
|
|
4
|
+
import { r as setBootVerbose } from "./logger-xlVlvCWX.mjs";
|
|
5
|
+
import { t as VERSION } from "./version-BPfyI4Kc.mjs";
|
|
3
6
|
import { execFileSync, spawn, spawnSync } from "node:child_process";
|
|
4
|
-
import { closeSync, constants,
|
|
7
|
+
import { closeSync, constants, existsSync, fsyncSync, mkdirSync, openSync, readFileSync, readdirSync, readlinkSync, renameSync, rmSync, statSync, unlinkSync, writeFileSync, writeSync } from "node:fs";
|
|
5
8
|
import { delimiter, dirname, join } from "node:path";
|
|
6
9
|
import { fileURLToPath } from "node:url";
|
|
7
10
|
import { homedir, platform } from "node:os";
|
|
8
11
|
import * as p from "@clack/prompts";
|
|
9
|
-
import { createHash } from "node:crypto";
|
|
10
12
|
import { copyFile, mkdir } from "node:fs/promises";
|
|
11
13
|
|
|
12
|
-
//#region \0rolldown/runtime.js
|
|
13
|
-
var __defProp = Object.defineProperty;
|
|
14
|
-
var __exportAll = (all, no_symbols) => {
|
|
15
|
-
let target = {};
|
|
16
|
-
for (var name in all) {
|
|
17
|
-
__defProp(target, name, {
|
|
18
|
-
get: all[name],
|
|
19
|
-
enumerable: true
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
if (!no_symbols) {
|
|
23
|
-
__defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
24
|
-
}
|
|
25
|
-
return target;
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
//#endregion
|
|
29
|
-
//#region src/state/schema.ts
|
|
30
|
-
const KV = {
|
|
31
|
-
sessions: "mem:sessions",
|
|
32
|
-
observations: (sessionId) => `mem:obs:${sessionId}`,
|
|
33
|
-
memories: "mem:memories",
|
|
34
|
-
summaries: "mem:summaries",
|
|
35
|
-
config: "mem:config",
|
|
36
|
-
metrics: "mem:metrics",
|
|
37
|
-
health: "mem:health",
|
|
38
|
-
embeddings: (obsId) => `mem:emb:${obsId}`,
|
|
39
|
-
bm25Index: "mem:index:bm25",
|
|
40
|
-
relations: "mem:relations",
|
|
41
|
-
profiles: "mem:profiles",
|
|
42
|
-
claudeBridge: "mem:claude-bridge",
|
|
43
|
-
graphNodes: "mem:graph:nodes",
|
|
44
|
-
graphEdges: "mem:graph:edges",
|
|
45
|
-
semantic: "mem:semantic",
|
|
46
|
-
procedural: "mem:procedural",
|
|
47
|
-
teamShared: (teamId) => `mem:team:${teamId}:shared`,
|
|
48
|
-
teamUsers: (teamId, userId) => `mem:team:${teamId}:users:${userId}`,
|
|
49
|
-
teamProfile: (teamId) => `mem:team:${teamId}:profile`,
|
|
50
|
-
audit: "mem:audit",
|
|
51
|
-
actions: "mem:actions",
|
|
52
|
-
actionEdges: "mem:action-edges",
|
|
53
|
-
leases: "mem:leases",
|
|
54
|
-
routines: "mem:routines",
|
|
55
|
-
routineRuns: "mem:routine-runs",
|
|
56
|
-
signals: "mem:signals",
|
|
57
|
-
checkpoints: "mem:checkpoints",
|
|
58
|
-
mesh: "mem:mesh",
|
|
59
|
-
sketches: "mem:sketches",
|
|
60
|
-
facets: "mem:facets",
|
|
61
|
-
sentinels: "mem:sentinels",
|
|
62
|
-
crystals: "mem:crystals",
|
|
63
|
-
lessons: "mem:lessons",
|
|
64
|
-
insights: "mem:insights",
|
|
65
|
-
graphEdgeHistory: "mem:graph:edge-history",
|
|
66
|
-
enrichedChunks: (sessionId) => `mem:enriched:${sessionId}`,
|
|
67
|
-
latentEmbeddings: (obsId) => `mem:latent:${obsId}`,
|
|
68
|
-
retentionScores: "mem:retention",
|
|
69
|
-
accessLog: "mem:access",
|
|
70
|
-
imageRefs: "mem:image-refs",
|
|
71
|
-
imageEmbeddings: "mem:image-embeddings",
|
|
72
|
-
slots: "mem:slots",
|
|
73
|
-
globalSlots: "mem:slots:global",
|
|
74
|
-
state: "mem:state",
|
|
75
|
-
commits: "mem:commits"
|
|
76
|
-
};
|
|
77
|
-
const STREAM = {
|
|
78
|
-
name: "mem-live",
|
|
79
|
-
group: (sessionId) => sessionId,
|
|
80
|
-
viewerGroup: "viewer"
|
|
81
|
-
};
|
|
82
|
-
function generateId(prefix) {
|
|
83
|
-
return `${prefix}_${Date.now().toString(36)}_${crypto.randomUUID().replace(/-/g, "").slice(0, 12)}`;
|
|
84
|
-
}
|
|
85
|
-
function fingerprintId(prefix, content) {
|
|
86
|
-
return `${prefix}_${createHash("sha256").update(content).digest("hex").slice(0, 16)}`;
|
|
87
|
-
}
|
|
88
|
-
function jaccardSimilarity(a, b) {
|
|
89
|
-
const setA = new Set(a.split(/\s+/).filter((t) => t.length > 2));
|
|
90
|
-
const setB = new Set(b.split(/\s+/).filter((t) => t.length > 2));
|
|
91
|
-
if (setA.size === 0 && setB.size === 0) return 1;
|
|
92
|
-
if (setA.size === 0 || setB.size === 0) return 0;
|
|
93
|
-
let intersection = 0;
|
|
94
|
-
for (const word of setA) if (setB.has(word)) intersection++;
|
|
95
|
-
return intersection / (setA.size + setB.size - intersection);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
//#endregion
|
|
99
14
|
//#region src/cli/doctor-diagnostics.ts
|
|
100
15
|
/** Common placeholder values shipped in .env.example. */
|
|
101
16
|
const PLACEHOLDER_VALUES = new Set([
|
|
@@ -323,7 +238,7 @@ function envPath(home) {
|
|
|
323
238
|
function preferencesPath(home) {
|
|
324
239
|
return join(home, ".agentmemory", "preferences.json");
|
|
325
240
|
}
|
|
326
|
-
function backupsDir
|
|
241
|
+
function backupsDir(home) {
|
|
327
242
|
return join(home, ".agentmemory", "backups");
|
|
328
243
|
}
|
|
329
244
|
function dataDir(home) {
|
|
@@ -399,9 +314,9 @@ function buildRemovePlan(ctx, options) {
|
|
|
399
314
|
plan.push({
|
|
400
315
|
id: "backups",
|
|
401
316
|
description: "Delete backups/ directory (connect manifest + backups)",
|
|
402
|
-
path: backupsDir
|
|
317
|
+
path: backupsDir(home),
|
|
403
318
|
alwaysAsk: false,
|
|
404
|
-
applicable: !options.keepData && pathExists(backupsDir
|
|
319
|
+
applicable: !options.keepData && pathExists(backupsDir(home)),
|
|
405
320
|
sizeBytes: -1
|
|
406
321
|
});
|
|
407
322
|
if (connectManifest?.installed?.length) for (const entry of connectManifest.installed) plan.push({
|
|
@@ -564,544 +479,6 @@ function isFirstRun() {
|
|
|
564
479
|
return readPrefs().firstRunAt === null;
|
|
565
480
|
}
|
|
566
481
|
|
|
567
|
-
//#endregion
|
|
568
|
-
//#region src/cli/connect/util.ts
|
|
569
|
-
const AGENTMEMORY_MCP_BLOCK = {
|
|
570
|
-
command: "npx",
|
|
571
|
-
args: ["-y", "@agentmemory/mcp"],
|
|
572
|
-
env: {
|
|
573
|
-
AGENTMEMORY_URL: "${AGENTMEMORY_URL}",
|
|
574
|
-
AGENTMEMORY_SECRET: "${AGENTMEMORY_SECRET}"
|
|
575
|
-
}
|
|
576
|
-
};
|
|
577
|
-
function backupsDir() {
|
|
578
|
-
return join(homedir(), ".agentmemory", "backups");
|
|
579
|
-
}
|
|
580
|
-
function ensureBackupsDir() {
|
|
581
|
-
const dir = backupsDir();
|
|
582
|
-
mkdirSync(dir, { recursive: true });
|
|
583
|
-
return dir;
|
|
584
|
-
}
|
|
585
|
-
function timestampSlug() {
|
|
586
|
-
return (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
587
|
-
}
|
|
588
|
-
function backupFile(sourcePath, agent, ext = "json") {
|
|
589
|
-
ensureBackupsDir();
|
|
590
|
-
const stamp = timestampSlug();
|
|
591
|
-
const target = join(backupsDir(), `${agent}-${stamp}.${ext}`);
|
|
592
|
-
copyFileSync(sourcePath, target);
|
|
593
|
-
return target;
|
|
594
|
-
}
|
|
595
|
-
function readJsonSafe(path) {
|
|
596
|
-
if (!existsSync(path)) return null;
|
|
597
|
-
try {
|
|
598
|
-
return JSON.parse(readFileSync(path, "utf-8"));
|
|
599
|
-
} catch {
|
|
600
|
-
return null;
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
|
-
function writeJsonAtomic(path, value) {
|
|
604
|
-
mkdirSync(dirname(path), { recursive: true });
|
|
605
|
-
const tmp = `${path}.tmp-${process.pid}-${Date.now()}`;
|
|
606
|
-
writeFileSync(tmp, `${JSON.stringify(value, null, 2)}\n`, "utf-8");
|
|
607
|
-
renameSync(tmp, path);
|
|
608
|
-
}
|
|
609
|
-
function logInstalled(label, target) {
|
|
610
|
-
p.log.success(`${label} → wired into ${target}`);
|
|
611
|
-
}
|
|
612
|
-
function logAlreadyWired(label, target) {
|
|
613
|
-
p.log.info(`${label} already wired in ${target} (use --force to re-install)`);
|
|
614
|
-
}
|
|
615
|
-
function logBackup(target) {
|
|
616
|
-
p.log.info(`Backup: ${target}`);
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
//#endregion
|
|
620
|
-
//#region src/cli/connect/claude-code.ts
|
|
621
|
-
const CLAUDE_DIR = join(homedir(), ".claude");
|
|
622
|
-
const CLAUDE_JSON = join(homedir(), ".claude.json");
|
|
623
|
-
function entryMatches$1(entry) {
|
|
624
|
-
if (!entry || typeof entry !== "object") return false;
|
|
625
|
-
const e = entry;
|
|
626
|
-
if (e["command"] !== "npx") return false;
|
|
627
|
-
return (Array.isArray(e["args"]) ? e["args"] : []).includes("@agentmemory/mcp");
|
|
628
|
-
}
|
|
629
|
-
const adapter$7 = {
|
|
630
|
-
name: "claude-code",
|
|
631
|
-
displayName: "Claude Code",
|
|
632
|
-
docs: "https://github.com/rohitg00/agentmemory#claude-code-one-block-paste-it",
|
|
633
|
-
protocolNote: "→ Using MCP. Hooks are also available — see docs/claude-code.md.",
|
|
634
|
-
detect() {
|
|
635
|
-
return existsSync(CLAUDE_DIR);
|
|
636
|
-
},
|
|
637
|
-
async install(opts) {
|
|
638
|
-
const existing = readJsonSafe(CLAUDE_JSON);
|
|
639
|
-
const next = existing ? { ...existing } : {};
|
|
640
|
-
const servers = { ...next.mcpServers ?? {} };
|
|
641
|
-
const alreadyHas = entryMatches$1(servers["agentmemory"]);
|
|
642
|
-
if (alreadyHas && !opts.force) {
|
|
643
|
-
logAlreadyWired("Claude Code", CLAUDE_JSON);
|
|
644
|
-
return {
|
|
645
|
-
kind: "already-wired",
|
|
646
|
-
mutatedPath: CLAUDE_JSON
|
|
647
|
-
};
|
|
648
|
-
}
|
|
649
|
-
if (opts.dryRun) {
|
|
650
|
-
p.log.info(`[dry-run] Would ${alreadyHas ? "overwrite" : "add"} mcpServers.agentmemory in ${CLAUDE_JSON}`);
|
|
651
|
-
return {
|
|
652
|
-
kind: "installed",
|
|
653
|
-
mutatedPath: CLAUDE_JSON
|
|
654
|
-
};
|
|
655
|
-
}
|
|
656
|
-
let backupPath;
|
|
657
|
-
if (existsSync(CLAUDE_JSON)) {
|
|
658
|
-
backupPath = backupFile(CLAUDE_JSON, "claude-code");
|
|
659
|
-
logBackup(backupPath);
|
|
660
|
-
} else {
|
|
661
|
-
mkdirSync(CLAUDE_DIR, { recursive: true });
|
|
662
|
-
writeFileSync(CLAUDE_JSON, "{}\n", "utf-8");
|
|
663
|
-
}
|
|
664
|
-
servers["agentmemory"] = AGENTMEMORY_MCP_BLOCK;
|
|
665
|
-
next.mcpServers = servers;
|
|
666
|
-
writeJsonAtomic(CLAUDE_JSON, next);
|
|
667
|
-
if (!entryMatches$1(readJsonSafe(CLAUDE_JSON)?.mcpServers?.["agentmemory"])) {
|
|
668
|
-
p.log.error(`Verification failed: ${CLAUDE_JSON} did not contain mcpServers.agentmemory after write.`);
|
|
669
|
-
return {
|
|
670
|
-
kind: "skipped",
|
|
671
|
-
reason: "verification-failed"
|
|
672
|
-
};
|
|
673
|
-
}
|
|
674
|
-
logInstalled("Claude Code", CLAUDE_JSON);
|
|
675
|
-
p.log.info("Restart Claude Code (or run `/mcp` inside a session) to pick up the new server.");
|
|
676
|
-
return {
|
|
677
|
-
kind: "installed",
|
|
678
|
-
mutatedPath: CLAUDE_JSON,
|
|
679
|
-
backupPath
|
|
680
|
-
};
|
|
681
|
-
}
|
|
682
|
-
};
|
|
683
|
-
|
|
684
|
-
//#endregion
|
|
685
|
-
//#region src/cli/connect/codex.ts
|
|
686
|
-
const CODEX_DIR = join(homedir(), ".codex");
|
|
687
|
-
const CODEX_TOML = join(CODEX_DIR, "config.toml");
|
|
688
|
-
const TOML_BLOCK = `[mcp_servers.agentmemory]
|
|
689
|
-
command = "npx"
|
|
690
|
-
args = ["-y", "@agentmemory/mcp"]
|
|
691
|
-
|
|
692
|
-
[mcp_servers.agentmemory.env]
|
|
693
|
-
AGENTMEMORY_URL = "http://localhost:3111"
|
|
694
|
-
`;
|
|
695
|
-
const SECTION_HEADER = "[mcp_servers.agentmemory]";
|
|
696
|
-
function isWiredText(toml) {
|
|
697
|
-
return toml.includes(SECTION_HEADER);
|
|
698
|
-
}
|
|
699
|
-
function stripExistingBlock(toml) {
|
|
700
|
-
const lines = toml.split(/\r?\n/);
|
|
701
|
-
const out = [];
|
|
702
|
-
let skipping = false;
|
|
703
|
-
for (const line of lines) {
|
|
704
|
-
const trimmed = line.trim();
|
|
705
|
-
if (trimmed === SECTION_HEADER || trimmed === "[mcp_servers.agentmemory.env]") {
|
|
706
|
-
skipping = true;
|
|
707
|
-
continue;
|
|
708
|
-
}
|
|
709
|
-
if (skipping && trimmed.startsWith("[") && trimmed !== "[mcp_servers.agentmemory.env]") skipping = false;
|
|
710
|
-
if (!skipping) out.push(line);
|
|
711
|
-
}
|
|
712
|
-
return out.join("\n").replace(/\n{3,}$/, "\n\n").trimEnd() + "\n";
|
|
713
|
-
}
|
|
714
|
-
const adapter$6 = {
|
|
715
|
-
name: "codex",
|
|
716
|
-
displayName: "Codex CLI",
|
|
717
|
-
docs: "https://github.com/rohitg00/agentmemory#codex-cli-codex-plugin-platform",
|
|
718
|
-
protocolNote: "→ Using MCP. Hooks are also available — see docs/codex.md.",
|
|
719
|
-
detect() {
|
|
720
|
-
return existsSync(CODEX_DIR);
|
|
721
|
-
},
|
|
722
|
-
async install(opts) {
|
|
723
|
-
const exists = existsSync(CODEX_TOML);
|
|
724
|
-
const current = exists ? readFileSync(CODEX_TOML, "utf-8") : "";
|
|
725
|
-
const wired = isWiredText(current);
|
|
726
|
-
if (wired && !opts.force) {
|
|
727
|
-
logAlreadyWired("Codex CLI", CODEX_TOML);
|
|
728
|
-
return {
|
|
729
|
-
kind: "already-wired",
|
|
730
|
-
mutatedPath: CODEX_TOML
|
|
731
|
-
};
|
|
732
|
-
}
|
|
733
|
-
if (opts.dryRun) {
|
|
734
|
-
p.log.info(`[dry-run] Would ${wired ? "rewrite" : "append"} [mcp_servers.agentmemory] in ${CODEX_TOML}`);
|
|
735
|
-
return {
|
|
736
|
-
kind: "installed",
|
|
737
|
-
mutatedPath: CODEX_TOML
|
|
738
|
-
};
|
|
739
|
-
}
|
|
740
|
-
let backupPath;
|
|
741
|
-
if (exists) {
|
|
742
|
-
backupPath = backupFile(CODEX_TOML, "codex", "toml");
|
|
743
|
-
logBackup(backupPath);
|
|
744
|
-
} else mkdirSync(dirname(CODEX_TOML), { recursive: true });
|
|
745
|
-
const cleaned = wired ? stripExistingBlock(current) : current;
|
|
746
|
-
writeFileSync(CODEX_TOML, `${cleaned}${cleaned.length === 0 || cleaned.endsWith("\n") ? "" : "\n"}${cleaned.length > 0 ? "\n" : ""}${TOML_BLOCK}`, "utf-8");
|
|
747
|
-
if (!isWiredText(readFileSync(CODEX_TOML, "utf-8"))) {
|
|
748
|
-
p.log.error(`Verification failed: ${CODEX_TOML} did not contain ${SECTION_HEADER} after write.`);
|
|
749
|
-
return {
|
|
750
|
-
kind: "skipped",
|
|
751
|
-
reason: "verification-failed"
|
|
752
|
-
};
|
|
753
|
-
}
|
|
754
|
-
logInstalled("Codex CLI", CODEX_TOML);
|
|
755
|
-
p.log.info("Codex picks up MCP servers on next launch. For the deeper plugin install, run: codex plugin marketplace add rohitg00/agentmemory && codex plugin install agentmemory");
|
|
756
|
-
return {
|
|
757
|
-
kind: "installed",
|
|
758
|
-
mutatedPath: CODEX_TOML,
|
|
759
|
-
...backupPath !== void 0 && { backupPath }
|
|
760
|
-
};
|
|
761
|
-
}
|
|
762
|
-
};
|
|
763
|
-
|
|
764
|
-
//#endregion
|
|
765
|
-
//#region src/cli/connect/json-mcp-adapter.ts
|
|
766
|
-
function entryMatches(entry) {
|
|
767
|
-
if (!entry || typeof entry !== "object") return false;
|
|
768
|
-
const e = entry;
|
|
769
|
-
if (e["command"] !== "npx") return false;
|
|
770
|
-
return (Array.isArray(e["args"]) ? e["args"] : []).includes("@agentmemory/mcp");
|
|
771
|
-
}
|
|
772
|
-
function createJsonMcpAdapter(config) {
|
|
773
|
-
return {
|
|
774
|
-
name: config.name,
|
|
775
|
-
displayName: config.displayName,
|
|
776
|
-
...config.docs !== void 0 && { docs: config.docs },
|
|
777
|
-
...config.protocolNote !== void 0 && { protocolNote: config.protocolNote },
|
|
778
|
-
detect() {
|
|
779
|
-
return existsSync(config.detectDir);
|
|
780
|
-
},
|
|
781
|
-
async install(opts) {
|
|
782
|
-
const existing = readJsonSafe(config.configPath);
|
|
783
|
-
const next = existing ? { ...existing } : {};
|
|
784
|
-
const servers = { ...next.mcpServers ?? {} };
|
|
785
|
-
const alreadyHas = entryMatches(servers["agentmemory"]);
|
|
786
|
-
if (alreadyHas && !opts.force) {
|
|
787
|
-
logAlreadyWired(config.displayName, config.configPath);
|
|
788
|
-
return {
|
|
789
|
-
kind: "already-wired",
|
|
790
|
-
mutatedPath: config.configPath
|
|
791
|
-
};
|
|
792
|
-
}
|
|
793
|
-
if (opts.dryRun) {
|
|
794
|
-
p.log.info(`[dry-run] Would ${alreadyHas ? "overwrite" : "add"} mcpServers.agentmemory in ${config.configPath}`);
|
|
795
|
-
return {
|
|
796
|
-
kind: "installed",
|
|
797
|
-
mutatedPath: config.configPath
|
|
798
|
-
};
|
|
799
|
-
}
|
|
800
|
-
let backupPath;
|
|
801
|
-
if (existsSync(config.configPath)) {
|
|
802
|
-
backupPath = backupFile(config.configPath, config.name);
|
|
803
|
-
logBackup(backupPath);
|
|
804
|
-
} else mkdirSync(dirname(config.configPath), { recursive: true });
|
|
805
|
-
servers["agentmemory"] = AGENTMEMORY_MCP_BLOCK;
|
|
806
|
-
next.mcpServers = servers;
|
|
807
|
-
writeJsonAtomic(config.configPath, next);
|
|
808
|
-
if (!entryMatches(readJsonSafe(config.configPath)?.mcpServers?.["agentmemory"])) {
|
|
809
|
-
p.log.error(`Verification failed: ${config.configPath} did not contain mcpServers.agentmemory after write.`);
|
|
810
|
-
return {
|
|
811
|
-
kind: "skipped",
|
|
812
|
-
reason: "verification-failed"
|
|
813
|
-
};
|
|
814
|
-
}
|
|
815
|
-
logInstalled(config.displayName, config.configPath);
|
|
816
|
-
return {
|
|
817
|
-
kind: "installed",
|
|
818
|
-
mutatedPath: config.configPath,
|
|
819
|
-
...backupPath !== void 0 && { backupPath }
|
|
820
|
-
};
|
|
821
|
-
}
|
|
822
|
-
};
|
|
823
|
-
}
|
|
824
|
-
|
|
825
|
-
//#endregion
|
|
826
|
-
//#region src/cli/connect/cursor.ts
|
|
827
|
-
const adapter$5 = createJsonMcpAdapter({
|
|
828
|
-
name: "cursor",
|
|
829
|
-
displayName: "Cursor",
|
|
830
|
-
detectDir: join(homedir(), ".cursor"),
|
|
831
|
-
configPath: join(homedir(), ".cursor", "mcp.json"),
|
|
832
|
-
docs: "https://github.com/rohitg00/agentmemory#other-agents",
|
|
833
|
-
protocolNote: "→ Using MCP (the only protocol Cursor speaks). Memory bridge runs at :3111 underneath."
|
|
834
|
-
});
|
|
835
|
-
|
|
836
|
-
//#endregion
|
|
837
|
-
//#region src/cli/connect/gemini-cli.ts
|
|
838
|
-
const adapter$4 = createJsonMcpAdapter({
|
|
839
|
-
name: "gemini-cli",
|
|
840
|
-
displayName: "Gemini CLI",
|
|
841
|
-
detectDir: join(homedir(), ".gemini"),
|
|
842
|
-
configPath: join(homedir(), ".gemini", "settings.json"),
|
|
843
|
-
docs: "https://github.com/rohitg00/agentmemory#other-agents",
|
|
844
|
-
protocolNote: "→ Using MCP (the only protocol Gemini CLI speaks). Memory bridge runs at :3111 underneath."
|
|
845
|
-
});
|
|
846
|
-
|
|
847
|
-
//#endregion
|
|
848
|
-
//#region src/cli/connect/hermes.ts
|
|
849
|
-
const HERMES_DIR = join(homedir(), ".hermes");
|
|
850
|
-
const HERMES_CONFIG = join(HERMES_DIR, "config.yaml");
|
|
851
|
-
const DOCS$2 = "https://github.com/rohitg00/agentmemory/tree/main/integrations/hermes";
|
|
852
|
-
const adapter$3 = {
|
|
853
|
-
name: "hermes",
|
|
854
|
-
displayName: "Hermes Agent",
|
|
855
|
-
docs: DOCS$2,
|
|
856
|
-
protocolNote: "→ Using MCP. Hooks are also available — see docs/hermes.md.",
|
|
857
|
-
detect() {
|
|
858
|
-
return existsSync(HERMES_DIR);
|
|
859
|
-
},
|
|
860
|
-
async install(_opts) {
|
|
861
|
-
p.log.warn("Hermes uses YAML config. Automated merge isn't implemented yet — manual install required.");
|
|
862
|
-
p.note([
|
|
863
|
-
`Add to ${HERMES_CONFIG}:`,
|
|
864
|
-
"",
|
|
865
|
-
" mcp_servers:",
|
|
866
|
-
" agentmemory:",
|
|
867
|
-
" command: npx",
|
|
868
|
-
" args: [\"-y\", \"@agentmemory/mcp\"]",
|
|
869
|
-
"",
|
|
870
|
-
" memory:",
|
|
871
|
-
" provider: agentmemory",
|
|
872
|
-
"",
|
|
873
|
-
`Full guide: ${DOCS$2}`
|
|
874
|
-
].join("\n"), "Hermes manual install");
|
|
875
|
-
return {
|
|
876
|
-
kind: "stub",
|
|
877
|
-
reason: "yaml-merge-not-implemented"
|
|
878
|
-
};
|
|
879
|
-
}
|
|
880
|
-
};
|
|
881
|
-
|
|
882
|
-
//#endregion
|
|
883
|
-
//#region src/cli/connect/openclaw.ts
|
|
884
|
-
const adapter$2 = createJsonMcpAdapter({
|
|
885
|
-
name: "openclaw",
|
|
886
|
-
displayName: "OpenClaw",
|
|
887
|
-
detectDir: join(homedir(), ".openclaw"),
|
|
888
|
-
configPath: join(homedir(), ".openclaw", "openclaw.json"),
|
|
889
|
-
docs: "https://github.com/rohitg00/agentmemory/tree/main/integrations/openclaw",
|
|
890
|
-
protocolNote: "→ Using MCP. Hooks are also available — see docs/openclaw.md."
|
|
891
|
-
});
|
|
892
|
-
|
|
893
|
-
//#endregion
|
|
894
|
-
//#region src/cli/connect/openhuman.ts
|
|
895
|
-
const OPENHUMAN_DIR = join(homedir(), ".openhuman");
|
|
896
|
-
const DOCS$1 = "https://github.com/tinyhumansai/openhuman";
|
|
897
|
-
const adapter$1 = {
|
|
898
|
-
name: "openhuman",
|
|
899
|
-
displayName: "OpenHuman",
|
|
900
|
-
docs: DOCS$1,
|
|
901
|
-
protocolNote: "→ Using native hooks (REST API at :3111). MCP not required.",
|
|
902
|
-
detect() {
|
|
903
|
-
return existsSync(OPENHUMAN_DIR);
|
|
904
|
-
},
|
|
905
|
-
async install(_opts) {
|
|
906
|
-
p.log.warn("OpenHuman integration is not yet automated. No `integrations/openhuman/` folder exists in the agentmemory repo today.");
|
|
907
|
-
p.note([
|
|
908
|
-
"OpenHuman is a Memory-trait host. The expected wiring is the REST",
|
|
909
|
-
"proxy at http://localhost:3111 plus an OpenHuman-side Memory trait",
|
|
910
|
-
"impl. Once integrations/openhuman/ lands in agentmemory we'll wire",
|
|
911
|
-
"this up automatically.",
|
|
912
|
-
"",
|
|
913
|
-
`Tracking: ${DOCS$1}`
|
|
914
|
-
].join("\n"), "OpenHuman manual install");
|
|
915
|
-
return {
|
|
916
|
-
kind: "stub",
|
|
917
|
-
reason: "no-integration-folder-yet"
|
|
918
|
-
};
|
|
919
|
-
}
|
|
920
|
-
};
|
|
921
|
-
|
|
922
|
-
//#endregion
|
|
923
|
-
//#region src/cli/connect/pi.ts
|
|
924
|
-
const PI_DIR = join(homedir(), ".pi");
|
|
925
|
-
const PI_EXT_DIR = join(PI_DIR, "agent", "extensions", "agentmemory");
|
|
926
|
-
const DOCS = "https://github.com/rohitg00/agentmemory/tree/main/integrations/pi";
|
|
927
|
-
const adapter = {
|
|
928
|
-
name: "pi",
|
|
929
|
-
displayName: "pi",
|
|
930
|
-
docs: DOCS,
|
|
931
|
-
protocolNote: "→ Using native hooks (REST API at :3111). MCP not required.",
|
|
932
|
-
detect() {
|
|
933
|
-
return existsSync(PI_DIR);
|
|
934
|
-
},
|
|
935
|
-
async install(_opts) {
|
|
936
|
-
p.log.warn("pi uses a TypeScript extension file. Automated copy + register isn't implemented yet — manual install required.");
|
|
937
|
-
p.note([
|
|
938
|
-
"Run these from the agentmemory repo root:",
|
|
939
|
-
"",
|
|
940
|
-
` mkdir -p ${PI_EXT_DIR}`,
|
|
941
|
-
` cp integrations/pi/index.ts ${PI_EXT_DIR}/index.ts`,
|
|
942
|
-
` cp integrations/pi/security.ts ${PI_EXT_DIR}/security.ts`,
|
|
943
|
-
"",
|
|
944
|
-
"Then add to ~/.pi/agent/settings.json:",
|
|
945
|
-
" { \"extensions\": [\"~/.pi/agent/extensions/agentmemory\"] }",
|
|
946
|
-
"",
|
|
947
|
-
`Full guide: ${DOCS}`
|
|
948
|
-
].join("\n"), "pi manual install");
|
|
949
|
-
return {
|
|
950
|
-
kind: "stub",
|
|
951
|
-
reason: "ts-extension-copy-not-implemented"
|
|
952
|
-
};
|
|
953
|
-
}
|
|
954
|
-
};
|
|
955
|
-
|
|
956
|
-
//#endregion
|
|
957
|
-
//#region src/cli/connect/index.ts
|
|
958
|
-
var connect_exports = /* @__PURE__ */ __exportAll({
|
|
959
|
-
ADAPTERS: () => ADAPTERS,
|
|
960
|
-
knownAgents: () => knownAgents,
|
|
961
|
-
resolveAdapter: () => resolveAdapter,
|
|
962
|
-
runAdapter: () => runAdapter,
|
|
963
|
-
runConnect: () => runConnect
|
|
964
|
-
});
|
|
965
|
-
const ADAPTERS = [
|
|
966
|
-
adapter$7,
|
|
967
|
-
adapter$6,
|
|
968
|
-
adapter$5,
|
|
969
|
-
adapter$4,
|
|
970
|
-
adapter$2,
|
|
971
|
-
adapter$3,
|
|
972
|
-
adapter,
|
|
973
|
-
adapter$1
|
|
974
|
-
];
|
|
975
|
-
function resolveAdapter(name) {
|
|
976
|
-
const lower = name.toLowerCase();
|
|
977
|
-
return ADAPTERS.find((a) => a.name === lower) ?? null;
|
|
978
|
-
}
|
|
979
|
-
function knownAgents() {
|
|
980
|
-
return ADAPTERS.map((a) => a.name);
|
|
981
|
-
}
|
|
982
|
-
function parseFlags(args) {
|
|
983
|
-
const positional = [];
|
|
984
|
-
let dryRun = false;
|
|
985
|
-
let force = false;
|
|
986
|
-
let all = false;
|
|
987
|
-
for (const a of args) if (a === "--dry-run") dryRun = true;
|
|
988
|
-
else if (a === "--force") force = true;
|
|
989
|
-
else if (a === "--all") all = true;
|
|
990
|
-
else if (!a.startsWith("-")) positional.push(a);
|
|
991
|
-
return {
|
|
992
|
-
dryRun,
|
|
993
|
-
force,
|
|
994
|
-
all,
|
|
995
|
-
positional
|
|
996
|
-
};
|
|
997
|
-
}
|
|
998
|
-
async function runAdapter(adapter, opts) {
|
|
999
|
-
if (!adapter.detect()) {
|
|
1000
|
-
p.log.warn(`${adapter.displayName}: not detected on this machine (skipping).${adapter.docs ? ` Docs: ${adapter.docs}` : ""}`);
|
|
1001
|
-
return {
|
|
1002
|
-
kind: "skipped",
|
|
1003
|
-
reason: "not-detected"
|
|
1004
|
-
};
|
|
1005
|
-
}
|
|
1006
|
-
p.log.step(`Wiring ${adapter.displayName}…`);
|
|
1007
|
-
if (adapter.protocolNote) p.log.message(adapter.protocolNote);
|
|
1008
|
-
try {
|
|
1009
|
-
return await adapter.install(opts);
|
|
1010
|
-
} catch (err) {
|
|
1011
|
-
p.log.error(`${adapter.displayName}: ${err instanceof Error ? err.message : String(err)}`);
|
|
1012
|
-
return {
|
|
1013
|
-
kind: "skipped",
|
|
1014
|
-
reason: "exception"
|
|
1015
|
-
};
|
|
1016
|
-
}
|
|
1017
|
-
}
|
|
1018
|
-
async function runConnect(args) {
|
|
1019
|
-
if (platform() === "win32") {
|
|
1020
|
-
p.intro("agentmemory connect");
|
|
1021
|
-
p.log.warn("Windows: automated `connect` is not supported yet. See https://github.com/rohitg00/agentmemory#other-agents for manual install steps.");
|
|
1022
|
-
p.outro("Windows: manual install required — see docs");
|
|
1023
|
-
return;
|
|
1024
|
-
}
|
|
1025
|
-
const { dryRun, force, all, positional } = parseFlags(args);
|
|
1026
|
-
const opts = {
|
|
1027
|
-
dryRun,
|
|
1028
|
-
force
|
|
1029
|
-
};
|
|
1030
|
-
p.intro("agentmemory connect");
|
|
1031
|
-
if (positional.length === 0 && !all) {
|
|
1032
|
-
const detected = ADAPTERS.filter((a) => a.detect());
|
|
1033
|
-
if (detected.length === 0) {
|
|
1034
|
-
p.log.error("No supported agents detected on this machine.");
|
|
1035
|
-
p.outro(`Supported: ${knownAgents().join(", ")}`);
|
|
1036
|
-
process.exit(1);
|
|
1037
|
-
}
|
|
1038
|
-
const picked = await p.multiselect({
|
|
1039
|
-
message: "Wire agentmemory into which agents?",
|
|
1040
|
-
options: detected.map((a) => ({
|
|
1041
|
-
value: a.name,
|
|
1042
|
-
label: a.displayName
|
|
1043
|
-
})),
|
|
1044
|
-
required: true
|
|
1045
|
-
});
|
|
1046
|
-
if (p.isCancel(picked)) {
|
|
1047
|
-
p.cancel("Cancelled.");
|
|
1048
|
-
return;
|
|
1049
|
-
}
|
|
1050
|
-
const results = [];
|
|
1051
|
-
for (const name of picked) {
|
|
1052
|
-
const adapter = resolveAdapter(name);
|
|
1053
|
-
if (!adapter) continue;
|
|
1054
|
-
results.push({
|
|
1055
|
-
name,
|
|
1056
|
-
result: await runAdapter(adapter, opts)
|
|
1057
|
-
});
|
|
1058
|
-
}
|
|
1059
|
-
summarize(results);
|
|
1060
|
-
return;
|
|
1061
|
-
}
|
|
1062
|
-
if (all) {
|
|
1063
|
-
const detected = ADAPTERS.filter((a) => a.detect());
|
|
1064
|
-
if (detected.length === 0) {
|
|
1065
|
-
p.log.error("No supported agents detected on this machine.");
|
|
1066
|
-
process.exit(1);
|
|
1067
|
-
}
|
|
1068
|
-
const results = [];
|
|
1069
|
-
for (const adapter of detected) results.push({
|
|
1070
|
-
name: adapter.name,
|
|
1071
|
-
result: await runAdapter(adapter, opts)
|
|
1072
|
-
});
|
|
1073
|
-
summarize(results);
|
|
1074
|
-
return;
|
|
1075
|
-
}
|
|
1076
|
-
const agentName = positional[0];
|
|
1077
|
-
const adapter = resolveAdapter(agentName);
|
|
1078
|
-
if (!adapter) {
|
|
1079
|
-
p.log.error(`Unknown agent: ${agentName}`);
|
|
1080
|
-
p.outro(`Supported: ${knownAgents().join(", ")}`);
|
|
1081
|
-
process.exit(1);
|
|
1082
|
-
}
|
|
1083
|
-
const result = await runAdapter(adapter, opts);
|
|
1084
|
-
summarize([{
|
|
1085
|
-
name: agentName,
|
|
1086
|
-
result
|
|
1087
|
-
}]);
|
|
1088
|
-
if (result.kind === "skipped" && result.reason !== "not-detected") process.exit(1);
|
|
1089
|
-
}
|
|
1090
|
-
function summarize(results) {
|
|
1091
|
-
const lines = results.map(({ name, result }) => {
|
|
1092
|
-
switch (result.kind) {
|
|
1093
|
-
case "installed": return ` ✓ ${name}${result.mutatedPath ? ` → ${result.mutatedPath}` : ""}`;
|
|
1094
|
-
case "already-wired": return ` ✓ ${name} (already wired)`;
|
|
1095
|
-
case "stub": return ` ⚠ ${name} (manual install required: ${result.reason})`;
|
|
1096
|
-
case "skipped": return ` ✗ ${name} (skipped: ${result.reason})`;
|
|
1097
|
-
}
|
|
1098
|
-
});
|
|
1099
|
-
p.note(lines.join("\n"), "summary");
|
|
1100
|
-
const stubs = results.filter((r) => r.result.kind === "stub");
|
|
1101
|
-
if (stubs.length > 0) p.log.info(`${stubs.length} agent(s) require manual install — see docs links above.`);
|
|
1102
|
-
p.outro("Restart any wired agent (or open a new session) to pick up agentmemory.");
|
|
1103
|
-
}
|
|
1104
|
-
|
|
1105
482
|
//#endregion
|
|
1106
483
|
//#region src/cli/onboarding.ts
|
|
1107
484
|
const __dirname$1 = dirname(fileURLToPath(import.meta.url));
|
|
@@ -1111,6 +488,11 @@ const NATIVE_AGENTS = [
|
|
|
1111
488
|
label: "Claude Code",
|
|
1112
489
|
glyph: "⟁"
|
|
1113
490
|
},
|
|
491
|
+
{
|
|
492
|
+
value: "copilot-cli",
|
|
493
|
+
label: "GitHub Copilot CLI",
|
|
494
|
+
glyph: "◈"
|
|
495
|
+
},
|
|
1114
496
|
{
|
|
1115
497
|
value: "codex",
|
|
1116
498
|
label: "Codex",
|
|
@@ -1232,6 +614,10 @@ function buildAgentOptions() {
|
|
|
1232
614
|
hint: "MCP server"
|
|
1233
615
|
}))];
|
|
1234
616
|
}
|
|
617
|
+
function getInitialAgentValues(env = process.env) {
|
|
618
|
+
if (env["COPILOT_CLI"] === "1" || env["COPILOT_AGENT_SESSION_ID"]) return ["copilot-cli"];
|
|
619
|
+
return ["claude-code"];
|
|
620
|
+
}
|
|
1235
621
|
function findEnvExample$1() {
|
|
1236
622
|
const candidates = [
|
|
1237
623
|
join(__dirname$1, "..", "..", ".env.example"),
|
|
@@ -1293,7 +679,7 @@ async function runOnboarding() {
|
|
|
1293
679
|
message: "Which agents will use agentmemory? (space to toggle, enter to confirm)",
|
|
1294
680
|
options: buildAgentOptions(),
|
|
1295
681
|
required: false,
|
|
1296
|
-
initialValues:
|
|
682
|
+
initialValues: getInitialAgentValues()
|
|
1297
683
|
});
|
|
1298
684
|
if (p.isCancel(agentsPicked)) {
|
|
1299
685
|
p.cancel("Setup cancelled. Re-run any time with: agentmemory --reset");
|
|
@@ -1302,7 +688,7 @@ async function runOnboarding() {
|
|
|
1302
688
|
if ((agentsPicked ?? []).length > 0) p.note([
|
|
1303
689
|
"━ how this works ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━",
|
|
1304
690
|
"All selected agents share the same memory at :3111.",
|
|
1305
|
-
"A memory saved by Claude Code is visible to Codex + Cursor instantly.",
|
|
691
|
+
"A memory saved by Claude Code is visible to Copilot + Codex + Cursor instantly.",
|
|
1306
692
|
"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
1307
693
|
].join("\n"));
|
|
1308
694
|
const providerPicked = await p.select({
|
|
@@ -1412,51 +798,6 @@ async function wireSelectedAgents(agents) {
|
|
|
1412
798
|
p.note(summary.join("\n"), "wire summary");
|
|
1413
799
|
}
|
|
1414
800
|
|
|
1415
|
-
//#endregion
|
|
1416
|
-
//#region src/logger.ts
|
|
1417
|
-
function fmt(level, msg, fields) {
|
|
1418
|
-
if (!fields || Object.keys(fields).length === 0) return `[agentmemory] ${level} ${msg}`;
|
|
1419
|
-
try {
|
|
1420
|
-
return `[agentmemory] ${level} ${msg} ${JSON.stringify(fields)}`;
|
|
1421
|
-
} catch {
|
|
1422
|
-
return `[agentmemory] ${level} ${msg}`;
|
|
1423
|
-
}
|
|
1424
|
-
}
|
|
1425
|
-
function emit(level, msg, fields) {
|
|
1426
|
-
try {
|
|
1427
|
-
process.stderr.write(fmt(level, msg, fields) + "\n");
|
|
1428
|
-
} catch {}
|
|
1429
|
-
}
|
|
1430
|
-
const logger = {
|
|
1431
|
-
info(msg, fields) {
|
|
1432
|
-
emit("info", msg, fields);
|
|
1433
|
-
},
|
|
1434
|
-
warn(msg, fields) {
|
|
1435
|
-
emit("warn", msg, fields);
|
|
1436
|
-
},
|
|
1437
|
-
error(msg, fields) {
|
|
1438
|
-
emit("error", msg, fields);
|
|
1439
|
-
}
|
|
1440
|
-
};
|
|
1441
|
-
let bootVerbose = process.env["AGENTMEMORY_VERBOSE"] === "1" || process.env["AGENTMEMORY_VERBOSE"] === "true";
|
|
1442
|
-
const bootBuffer = [];
|
|
1443
|
-
function setBootVerbose(enabled) {
|
|
1444
|
-
bootVerbose = enabled;
|
|
1445
|
-
}
|
|
1446
|
-
function bootLog(msg) {
|
|
1447
|
-
if (bootVerbose) {
|
|
1448
|
-
try {
|
|
1449
|
-
process.stderr.write(`[agentmemory] ${msg}\n`);
|
|
1450
|
-
} catch {}
|
|
1451
|
-
return;
|
|
1452
|
-
}
|
|
1453
|
-
if (bootBuffer.length < 500) bootBuffer.push(msg);
|
|
1454
|
-
}
|
|
1455
|
-
|
|
1456
|
-
//#endregion
|
|
1457
|
-
//#region src/version.ts
|
|
1458
|
-
const VERSION = "0.9.21";
|
|
1459
|
-
|
|
1460
801
|
//#endregion
|
|
1461
802
|
//#region src/cli.ts
|
|
1462
803
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
@@ -1495,8 +836,9 @@ Usage: agentmemory [command] [options]
|
|
|
1495
836
|
Commands:
|
|
1496
837
|
(default) Start agentmemory worker
|
|
1497
838
|
init Copy bundled .env.example to ~/.agentmemory/.env if absent
|
|
1498
|
-
connect [agent] Wire agentmemory into an installed agent (claude-code,
|
|
1499
|
-
cursor, gemini-cli, openclaw,
|
|
839
|
+
connect [agent] Wire agentmemory into an installed agent (claude-code,
|
|
840
|
+
copilot-cli, codex, cursor, gemini-cli, openclaw,
|
|
841
|
+
hermes, pi, openhuman).
|
|
1500
842
|
No arg = interactive picker. --all wires every detected agent.
|
|
1501
843
|
--dry-run shows what would change. --force re-installs.
|
|
1502
844
|
status Show connection status, memory count, flags, and health
|
|
@@ -1521,7 +863,7 @@ Options:
|
|
|
1521
863
|
--help, -h Show this help
|
|
1522
864
|
--verbose, -v Show engine stderr, boot log, and diagnostic info
|
|
1523
865
|
--reset Wipe ~/.agentmemory/preferences.json and re-run onboarding
|
|
1524
|
-
--tools all|core Tool visibility (default: core =
|
|
866
|
+
--tools all|core Tool visibility (default: all = 51 tools; core = 8 essentials)
|
|
1525
867
|
--no-engine Skip auto-starting iii-engine
|
|
1526
868
|
--port <N> Override REST port (default: 3111)
|
|
1527
869
|
|
|
@@ -1561,9 +903,26 @@ function getBaseUrl() {
|
|
|
1561
903
|
if (url) return url.replace(/\/+$/, "");
|
|
1562
904
|
return `http://localhost:${getRestPort()}`;
|
|
1563
905
|
}
|
|
906
|
+
let discoveredViewerPort = null;
|
|
907
|
+
async function discoverViewerPort() {
|
|
908
|
+
if (discoveredViewerPort !== null) return;
|
|
909
|
+
try {
|
|
910
|
+
const res = await fetch(`${getBaseUrl()}/agentmemory/livez`, { signal: AbortSignal.timeout(1e3) });
|
|
911
|
+
if (res.ok) {
|
|
912
|
+
const data = await res.json();
|
|
913
|
+
if (typeof data.viewerPort === "number") discoveredViewerPort = data.viewerPort;
|
|
914
|
+
}
|
|
915
|
+
} catch {}
|
|
916
|
+
}
|
|
1564
917
|
function getViewerUrl() {
|
|
1565
918
|
const envUrl = process.env["AGENTMEMORY_VIEWER_URL"];
|
|
1566
919
|
if (envUrl) return envUrl.replace(/\/+$/, "");
|
|
920
|
+
if (discoveredViewerPort !== null) try {
|
|
921
|
+
const u = new URL(getBaseUrl());
|
|
922
|
+
return `${u.protocol}//${u.hostname}:${discoveredViewerPort}`;
|
|
923
|
+
} catch {
|
|
924
|
+
return `http://localhost:${discoveredViewerPort}`;
|
|
925
|
+
}
|
|
1567
926
|
try {
|
|
1568
927
|
const u = new URL(getBaseUrl());
|
|
1569
928
|
const vPort = parseInt(process.env["III_VIEWER_PORT"] || "", 10) || (parseInt(u.port || "3111", 10) || 3111) + 2;
|
|
@@ -1595,16 +954,31 @@ async function isEngineRunning() {
|
|
|
1595
954
|
}
|
|
1596
955
|
async function isAgentmemoryReady() {
|
|
1597
956
|
try {
|
|
1598
|
-
|
|
957
|
+
const res = await fetch(`${getBaseUrl()}/agentmemory/livez`, { signal: AbortSignal.timeout(2e3) });
|
|
958
|
+
if (!res.ok) return false;
|
|
959
|
+
try {
|
|
960
|
+
const data = await res.json();
|
|
961
|
+
if (typeof data.viewerPort === "number") {
|
|
962
|
+
discoveredViewerPort = data.viewerPort;
|
|
963
|
+
return true;
|
|
964
|
+
}
|
|
965
|
+
if (data.viewerSkipped) return true;
|
|
966
|
+
return false;
|
|
967
|
+
} catch {
|
|
968
|
+
return false;
|
|
969
|
+
}
|
|
1599
970
|
} catch {
|
|
1600
971
|
return false;
|
|
1601
972
|
}
|
|
1602
973
|
}
|
|
1603
974
|
function findIiiConfig() {
|
|
975
|
+
const envPath = process.env["AGENTMEMORY_III_CONFIG"];
|
|
1604
976
|
const candidates = [
|
|
977
|
+
...envPath ? [envPath] : [],
|
|
978
|
+
join(process.cwd(), "iii-config.yaml"),
|
|
979
|
+
join(homedir(), ".agentmemory", "iii-config.yaml"),
|
|
1605
980
|
join(__dirname, "iii-config.yaml"),
|
|
1606
|
-
join(__dirname, "..", "iii-config.yaml")
|
|
1607
|
-
join(process.cwd(), "iii-config.yaml")
|
|
981
|
+
join(__dirname, "..", "iii-config.yaml")
|
|
1608
982
|
];
|
|
1609
983
|
for (const c of candidates) if (existsSync(c)) return c;
|
|
1610
984
|
return "";
|
|
@@ -1689,6 +1063,23 @@ function clearEnginePidfile() {
|
|
|
1689
1063
|
unlinkSync(enginePidfilePath());
|
|
1690
1064
|
} catch {}
|
|
1691
1065
|
}
|
|
1066
|
+
function workerPidfilePath() {
|
|
1067
|
+
return join(homedir(), ".agentmemory", "worker.pid");
|
|
1068
|
+
}
|
|
1069
|
+
function readWorkerPidfile() {
|
|
1070
|
+
try {
|
|
1071
|
+
const pidStr = readFileSync(workerPidfilePath(), "utf-8").trim();
|
|
1072
|
+
const pid = parseInt(pidStr, 10);
|
|
1073
|
+
return Number.isFinite(pid) && pid > 0 ? pid : null;
|
|
1074
|
+
} catch {
|
|
1075
|
+
return null;
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
function clearWorkerPidfile() {
|
|
1079
|
+
try {
|
|
1080
|
+
unlinkSync(workerPidfilePath());
|
|
1081
|
+
} catch {}
|
|
1082
|
+
}
|
|
1692
1083
|
function writeEngineState(state) {
|
|
1693
1084
|
try {
|
|
1694
1085
|
const statePath = engineStatePath();
|
|
@@ -1770,7 +1161,7 @@ function detectIiiConsole() {
|
|
|
1770
1161
|
};
|
|
1771
1162
|
return { kind: "missing" };
|
|
1772
1163
|
}
|
|
1773
|
-
const III_CONSOLE_INSTALL_CMD = "curl -fsSL https://install.iii.dev/
|
|
1164
|
+
const III_CONSOLE_INSTALL_CMD = "curl -fsSL https://install.iii.dev/iii/main/install.sh | sh";
|
|
1774
1165
|
async function ensureIiiConsole() {
|
|
1775
1166
|
const state = detectIiiConsole();
|
|
1776
1167
|
if (state.kind === "installed") return state;
|
|
@@ -2110,7 +1501,7 @@ async function main() {
|
|
|
2110
1501
|
if (firstRun || IS_RESET) await runOnboarding();
|
|
2111
1502
|
if (skipEngine) {
|
|
2112
1503
|
if (IS_VERBOSE) p.log.info("Skipping engine check (--no-engine)");
|
|
2113
|
-
await import("./src-
|
|
1504
|
+
await import("./src-DvS3bhMe.mjs");
|
|
2114
1505
|
if (await waitForAgentmemoryReady(15e3)) {
|
|
2115
1506
|
const consoleState = await ensureIiiConsole();
|
|
2116
1507
|
await maybeOfferGlobalInstall();
|
|
@@ -2122,7 +1513,7 @@ async function main() {
|
|
|
2122
1513
|
if (IS_VERBOSE) p.log.success("iii-engine is running");
|
|
2123
1514
|
warnIfEngineVersionMismatch(whichBinary("iii") ?? fallbackIiiPaths().find((p) => existsSync(p)) ?? null);
|
|
2124
1515
|
adoptRunningEngine();
|
|
2125
|
-
await import("./src-
|
|
1516
|
+
await import("./src-DvS3bhMe.mjs");
|
|
2126
1517
|
if (await waitForAgentmemoryReady(15e3)) {
|
|
2127
1518
|
const consoleState = await ensureIiiConsole();
|
|
2128
1519
|
await maybeOfferGlobalInstall();
|
|
@@ -2171,7 +1562,7 @@ async function main() {
|
|
|
2171
1562
|
process.exit(1);
|
|
2172
1563
|
}
|
|
2173
1564
|
s.stop("iii-engine is ready");
|
|
2174
|
-
await import("./src-
|
|
1565
|
+
await import("./src-DvS3bhMe.mjs");
|
|
2175
1566
|
if (await waitForAgentmemoryReady(15e3)) {
|
|
2176
1567
|
const consoleState = await ensureIiiConsole();
|
|
2177
1568
|
await maybeOfferGlobalInstall();
|
|
@@ -2206,20 +1597,22 @@ async function runStatus() {
|
|
|
2206
1597
|
apiFetch(base, "health"),
|
|
2207
1598
|
apiFetch(base, "sessions"),
|
|
2208
1599
|
apiFetch(base, "graph/stats"),
|
|
2209
|
-
apiFetch(base, "
|
|
1600
|
+
apiFetch(base, "memories?count=true"),
|
|
2210
1601
|
apiFetch(base, "config/flags")
|
|
2211
1602
|
]);
|
|
1603
|
+
if (typeof healthRes?.viewerPort === "number") discoveredViewerPort = healthRes.viewerPort;
|
|
2212
1604
|
const h = healthRes?.health;
|
|
2213
1605
|
const status = healthRes?.status || "unknown";
|
|
2214
1606
|
const version = healthRes?.version || "?";
|
|
2215
|
-
const
|
|
1607
|
+
const sessionList = Array.isArray(sessionsRes?.sessions) ? sessionsRes.sessions : [];
|
|
1608
|
+
const sessions = sessionList.length;
|
|
2216
1609
|
const nodes = Number(graphRes?.totalNodes ?? graphRes?.nodes ?? graphRes?.nodeCount ?? 0);
|
|
2217
1610
|
const edges = Number(graphRes?.totalEdges ?? graphRes?.edges ?? graphRes?.edgeCount ?? 0);
|
|
2218
1611
|
const cb = healthRes?.circuitBreaker?.state || "closed";
|
|
2219
1612
|
const heapMB = h?.memory ? Math.round(h.memory.heapUsed / 1048576) : 0;
|
|
2220
1613
|
const uptime = h?.uptimeSeconds ? Math.round(h.uptimeSeconds) : 0;
|
|
2221
|
-
const obsCount =
|
|
2222
|
-
const memCount = memoriesRes?.
|
|
1614
|
+
const obsCount = sessionList.reduce((sum, s) => sum + (Number(s?.observationCount) || 0), 0);
|
|
1615
|
+
const memCount = Number(memoriesRes?.latestCount ?? memoriesRes?.total ?? 0) || 0;
|
|
2223
1616
|
const estFullTokens = obsCount * 80;
|
|
2224
1617
|
const estInjectedTokens = Math.min(obsCount, 50) * 38;
|
|
2225
1618
|
const tokensSaved = estFullTokens - estInjectedTokens;
|
|
@@ -2328,6 +1721,7 @@ function buildDoctorEffects() {
|
|
|
2328
1721
|
iiiBinaryVersion: (binPath) => iiiBinVersion(binPath),
|
|
2329
1722
|
viewerReachable: async (timeoutMs = 2e3) => {
|
|
2330
1723
|
try {
|
|
1724
|
+
await discoverViewerPort();
|
|
2331
1725
|
return (await fetch(getViewerUrl(), { signal: AbortSignal.timeout(timeoutMs) })).ok;
|
|
2332
1726
|
} catch {
|
|
2333
1727
|
return false;
|
|
@@ -2877,14 +2271,14 @@ async function runUpgrade() {
|
|
|
2877
2271
|
};
|
|
2878
2272
|
if (hasPackageJson) if (!!pnpmBin && hasPnpmLock && pnpmBin) {
|
|
2879
2273
|
requireSuccess(runCommand(pnpmBin, ["install"], { label: "Refreshing dependencies (pnpm install)" }), "pnpm install");
|
|
2880
|
-
runCommand(pnpmBin, ["up", "iii-sdk@
|
|
2881
|
-
label: "
|
|
2274
|
+
runCommand(pnpmBin, ["up", "iii-sdk@0.11.2"], {
|
|
2275
|
+
label: "Pinning iii-sdk@0.11.2",
|
|
2882
2276
|
optional: true
|
|
2883
2277
|
});
|
|
2884
2278
|
} else if (npmBin) {
|
|
2885
2279
|
requireSuccess(runCommand(npmBin, ["install"], { label: "Refreshing dependencies (npm install)" }), "npm install");
|
|
2886
|
-
runCommand(npmBin, ["install", "iii-sdk@
|
|
2887
|
-
label: "
|
|
2280
|
+
runCommand(npmBin, ["install", "iii-sdk@0.11.2"], {
|
|
2281
|
+
label: "Pinning iii-sdk@0.11.2",
|
|
2888
2282
|
optional: true
|
|
2889
2283
|
});
|
|
2890
2284
|
} else p.log.warn("No package manager found (pnpm/npm). Skipping JS dependency upgrade.");
|
|
@@ -2992,6 +2386,7 @@ async function stopDockerEngine(composeFile, port) {
|
|
|
2992
2386
|
], { label: `docker compose -f ${composeFile} down` });
|
|
2993
2387
|
clearEnginePidfile();
|
|
2994
2388
|
clearEngineState();
|
|
2389
|
+
clearWorkerPidfile();
|
|
2995
2390
|
if (!ok) {
|
|
2996
2391
|
p.log.error(`docker compose down failed. The engine may still be running on :${port}. Inspect with:\n docker compose -f ${composeFile} ps`);
|
|
2997
2392
|
process.exit(1);
|
|
@@ -3009,6 +2404,7 @@ async function runStop() {
|
|
|
3009
2404
|
p.log.info(`No engine responding on port ${port}.`);
|
|
3010
2405
|
clearEnginePidfile();
|
|
3011
2406
|
clearEngineState();
|
|
2407
|
+
clearWorkerPidfile();
|
|
3012
2408
|
p.outro("Nothing to stop.");
|
|
3013
2409
|
return;
|
|
3014
2410
|
}
|
|
@@ -3017,17 +2413,35 @@ async function runStop() {
|
|
|
3017
2413
|
}
|
|
3018
2414
|
const portPids = findEnginePidsByPort(port);
|
|
3019
2415
|
const pidfilePid = readEnginePidfile();
|
|
2416
|
+
const workerPid = readWorkerPidfile();
|
|
3020
2417
|
if (!running) {
|
|
3021
|
-
if (portPids.length === 0 && pidfilePid === null) {
|
|
2418
|
+
if (portPids.length === 0 && pidfilePid === null && workerPid === null) {
|
|
3022
2419
|
clearEnginePidfile();
|
|
3023
2420
|
clearEngineState();
|
|
2421
|
+
clearWorkerPidfile();
|
|
3024
2422
|
p.outro("Nothing to stop.");
|
|
3025
2423
|
return;
|
|
3026
2424
|
}
|
|
2425
|
+
if (workerPid !== null && portPids.length === 0 && pidfilePid === null) {
|
|
2426
|
+
const s = p.spinner();
|
|
2427
|
+
s.start(`Stopping orphaned agentmemory worker (pid ${workerPid})...`);
|
|
2428
|
+
const ok = await signalAndWait(workerPid, "SIGTERM", 3e3);
|
|
2429
|
+
s.stop(ok ? `Stopped worker pid ${workerPid}` : `Failed to stop worker pid ${workerPid}`);
|
|
2430
|
+
clearEnginePidfile();
|
|
2431
|
+
clearEngineState();
|
|
2432
|
+
clearWorkerPidfile();
|
|
2433
|
+
if (!ok) {
|
|
2434
|
+
p.log.error(`Worker pid ${workerPid} survived SIGKILL. Investigate with \`ps\`.`);
|
|
2435
|
+
process.exit(1);
|
|
2436
|
+
}
|
|
2437
|
+
p.outro("Stopped orphaned worker. Memories persisted to disk.");
|
|
2438
|
+
return;
|
|
2439
|
+
}
|
|
3027
2440
|
const survivors = new Set(portPids);
|
|
3028
2441
|
if (pidfilePid) survivors.add(pidfilePid);
|
|
2442
|
+
if (workerPid) survivors.add(workerPid);
|
|
3029
2443
|
p.log.warn(`Engine not responding on :${port}, but ${survivors.size} process(es) still hold the port or pidfile: ${[...survivors].join(", ")}`);
|
|
3030
|
-
p.log.info(`Preserving ~/.agentmemory/iii.pid. Investigate before manual cleanup:\n ps -p ${[...survivors].join(",")} -o pid,ppid,comm,etime\n ${IS_WINDOWS ? "netstat -ano | findstr :" + port : "lsof -i :" + port}`);
|
|
2444
|
+
p.log.info(`Preserving ~/.agentmemory/iii.pid + worker.pid. Investigate before manual cleanup:\n ps -p ${[...survivors].join(",")} -o pid,ppid,comm,etime\n ${IS_WINDOWS ? "netstat -ano | findstr :" + port : "lsof -i :" + port}`);
|
|
3031
2445
|
process.exit(1);
|
|
3032
2446
|
}
|
|
3033
2447
|
if (!state) {
|
|
@@ -3041,7 +2455,9 @@ async function runStop() {
|
|
|
3041
2455
|
const candidates = /* @__PURE__ */ new Set();
|
|
3042
2456
|
if (pidfilePid) candidates.add(pidfilePid);
|
|
3043
2457
|
for (const pid of portPids) candidates.add(pid);
|
|
3044
|
-
|
|
2458
|
+
const workerCandidates = /* @__PURE__ */ new Set();
|
|
2459
|
+
if (workerPid) workerCandidates.add(workerPid);
|
|
2460
|
+
if (candidates.size === 0 && workerCandidates.size === 0) {
|
|
3045
2461
|
p.log.error(`Could not locate engine process. Try:\n ${IS_WINDOWS ? "netstat -ano | findstr :" + port : "lsof -i :" + port + " -t | xargs kill -9"}`);
|
|
3046
2462
|
process.exit(1);
|
|
3047
2463
|
}
|
|
@@ -3053,19 +2469,28 @@ async function runStop() {
|
|
|
3053
2469
|
s.stop(ok ? `Stopped pid ${pid}` : `Failed to stop pid ${pid}`);
|
|
3054
2470
|
if (!ok) allStopped = false;
|
|
3055
2471
|
}
|
|
2472
|
+
for (const pid of workerCandidates) {
|
|
2473
|
+
if (candidates.has(pid)) continue;
|
|
2474
|
+
const s = p.spinner();
|
|
2475
|
+
s.start(`Stopping agentmemory worker (pid ${pid})...`);
|
|
2476
|
+
const ok = await signalAndWait(pid, "SIGTERM", 3e3);
|
|
2477
|
+
s.stop(ok ? `Stopped worker pid ${pid}` : `Failed to stop worker pid ${pid}`);
|
|
2478
|
+
if (!ok) allStopped = false;
|
|
2479
|
+
}
|
|
3056
2480
|
clearEnginePidfile();
|
|
3057
2481
|
clearEngineState();
|
|
2482
|
+
clearWorkerPidfile();
|
|
3058
2483
|
if (!allStopped) {
|
|
3059
|
-
p.log.error("One or more
|
|
2484
|
+
p.log.error("One or more processes survived SIGKILL. Investigate with `ps`.");
|
|
3060
2485
|
process.exit(1);
|
|
3061
2486
|
}
|
|
3062
2487
|
p.outro("Stopped. Memories persisted to disk; restart anytime with: npx @agentmemory/agentmemory");
|
|
3063
2488
|
}
|
|
3064
2489
|
async function runMcp() {
|
|
3065
|
-
await import("./standalone-
|
|
2490
|
+
await import("./standalone-DHQcPX_g.mjs");
|
|
3066
2491
|
}
|
|
3067
2492
|
async function runConnectCmd() {
|
|
3068
|
-
const { runConnect } = await
|
|
2493
|
+
const { runConnect } = await import("./connect-Cf9bmBqO.mjs").then((n) => n.t);
|
|
3069
2494
|
await runConnect(args.slice(1));
|
|
3070
2495
|
}
|
|
3071
2496
|
async function runImportJsonl() {
|
|
@@ -3298,5 +2723,5 @@ async function runRemove() {
|
|
|
3298
2723
|
});
|
|
3299
2724
|
|
|
3300
2725
|
//#endregion
|
|
3301
|
-
export {
|
|
2726
|
+
export { discoverViewerPort };
|
|
3302
2727
|
//# sourceMappingURL=cli.mjs.map
|