@agentmemory/agentmemory 0.9.28 → 0.9.30
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/.env.example +98 -28
- package/AGENTS.md +19 -26
- package/README.md +619 -274
- package/dist/.env.example +98 -28
- package/dist/cli.d.mts.map +1 -1
- package/dist/cli.mjs +1328 -244
- package/dist/cli.mjs.map +1 -1
- package/dist/{connect-JnsJXc68.mjs → connect-B4_ca6BD.mjs} +619 -70
- package/dist/connect-B4_ca6BD.mjs.map +1 -0
- package/dist/docker-compose.yml +42 -16
- package/dist/hooks/_capture.d.mts +22 -0
- package/dist/hooks/_capture.d.mts.map +1 -0
- package/dist/hooks/_capture.mjs +908 -0
- package/dist/hooks/_capture.mjs.map +1 -0
- package/dist/hooks/antigravity-bridge.d.mts +8 -0
- package/dist/hooks/antigravity-bridge.d.mts.map +1 -0
- package/dist/hooks/antigravity-bridge.mjs +123 -0
- package/dist/hooks/antigravity-bridge.mjs.map +1 -0
- package/dist/hooks/notification.mjs +34 -27
- package/dist/hooks/notification.mjs.map +1 -1
- package/dist/hooks/post-commit.mjs +84 -2
- package/dist/hooks/post-commit.mjs.map +1 -1
- package/dist/hooks/post-tool-failure.mjs +71 -26
- package/dist/hooks/post-tool-failure.mjs.map +1 -1
- package/dist/hooks/post-tool-use.mjs +99 -36
- package/dist/hooks/post-tool-use.mjs.map +1 -1
- package/dist/hooks/pre-compact.mjs +95 -5
- package/dist/hooks/pre-compact.mjs.map +1 -1
- package/dist/hooks/pre-tool-use.mjs +72 -2
- package/dist/hooks/pre-tool-use.mjs.map +1 -1
- package/dist/hooks/prompt-submit.mjs +25 -22
- package/dist/hooks/prompt-submit.mjs.map +1 -1
- package/dist/hooks/session-end.mjs +93 -24
- package/dist/hooks/session-end.mjs.map +1 -1
- package/dist/hooks/session-start.mjs +101 -6
- package/dist/hooks/session-start.mjs.map +1 -1
- package/dist/hooks/stop.mjs +72 -8
- package/dist/hooks/stop.mjs.map +1 -1
- package/dist/hooks/subagent-start.mjs +28 -26
- package/dist/hooks/subagent-start.mjs.map +1 -1
- package/dist/hooks/subagent-stop.mjs +29 -26
- package/dist/hooks/subagent-stop.mjs.map +1 -1
- package/dist/hooks/task-completed.mjs +31 -28
- package/dist/hooks/task-completed.mjs.map +1 -1
- package/dist/iii-config.docker.yaml +10 -10
- package/dist/iii-config.yaml +3 -1
- package/dist/{image-refs-BKLmp_sD.mjs → image-refs-BaT-9oMU.mjs} +2 -2
- package/dist/image-refs-BaT-9oMU.mjs.map +1 -0
- package/dist/{image-refs-C2YU7BIm.mjs → image-refs-T_urbwHM.mjs} +3 -3
- package/dist/image-refs-T_urbwHM.mjs.map +1 -0
- package/dist/{keyed-mutex-DTHtcZrt.mjs → image-store-BsN4vyjr.mjs} +2 -14
- package/dist/image-store-BsN4vyjr.mjs.map +1 -0
- package/dist/index.mjs +14021 -6494
- package/dist/index.mjs.map +1 -1
- package/dist/keyed-mutex-DchXsyfP.mjs +221 -0
- package/dist/keyed-mutex-DchXsyfP.mjs.map +1 -0
- package/dist/secret-store-BnY3A50C.mjs +2348 -0
- package/dist/secret-store-BnY3A50C.mjs.map +1 -0
- package/dist/{src-CzgoepGU.mjs → src-Jsq9LCeH.mjs} +7349 -2940
- package/dist/src-Jsq9LCeH.mjs.map +1 -0
- package/dist/{standalone-YsZFFQBM.mjs → standalone-DKCdrf1d.mjs} +62 -24
- package/dist/standalone-DKCdrf1d.mjs.map +1 -0
- package/dist/standalone.d.mts.map +1 -1
- package/dist/standalone.mjs +298 -27
- package/dist/standalone.mjs.map +1 -1
- package/dist/status-IRz7ta8-.mjs +2344 -0
- package/dist/status-IRz7ta8-.mjs.map +1 -0
- package/dist/viewer/favicon.svg +35 -1
- package/dist/viewer/index.html +7428 -2608
- package/docker-compose.yml +42 -16
- package/iii-config.docker.yaml +10 -10
- package/iii-config.yaml +3 -1
- package/integrations/pi/README.md +77 -0
- package/integrations/pi/index.ts +402 -0
- package/integrations/pi/package.json +32 -0
- package/integrations/pi/security.ts +87 -0
- package/package.json +15 -7
- package/plugin/.claude-plugin/plugin.json +2 -2
- package/plugin/.codex-plugin/plugin.json +26 -4
- package/plugin/.devin-plugin/plugin.json +32 -0
- package/plugin/.mcp.codex.json +12 -0
- package/plugin/.mcp.copilot.json +2 -7
- package/plugin/.mcp.json +2 -7
- package/plugin/assets/icon.svg +35 -0
- package/plugin/cursor/hooks.json +41 -0
- package/plugin/cursor/mcp.json +12 -0
- package/plugin/hooks/hooks.antigravity.json +43 -0
- package/plugin/hooks/hooks.copilot.json +11 -11
- package/plugin/hooks/hooks.devin.json +67 -0
- package/plugin/hooks/hooks.droid.json +55 -0
- package/plugin/opencode/README.md +267 -31
- package/plugin/opencode/agentmemory-capture.ts +644 -15
- package/plugin/opencode/plugin.json +1 -1
- package/plugin/plugin.json +2 -2
- package/plugin/scripts/_capture.d.mts +22 -0
- package/plugin/scripts/_capture.d.mts.map +1 -0
- package/plugin/scripts/_capture.mjs +908 -0
- package/plugin/scripts/_capture.mjs.map +1 -0
- package/plugin/scripts/antigravity-bridge.d.mts +8 -0
- package/plugin/scripts/antigravity-bridge.d.mts.map +1 -0
- package/plugin/scripts/antigravity-bridge.mjs +123 -0
- package/plugin/scripts/antigravity-bridge.mjs.map +1 -0
- package/plugin/scripts/notification.mjs +34 -27
- package/plugin/scripts/notification.mjs.map +1 -1
- package/plugin/scripts/plugin-bridge.mjs +378 -0
- package/plugin/scripts/post-commit.mjs +84 -2
- package/plugin/scripts/post-commit.mjs.map +1 -1
- package/plugin/scripts/post-tool-failure.mjs +71 -26
- package/plugin/scripts/post-tool-failure.mjs.map +1 -1
- package/plugin/scripts/post-tool-use.mjs +99 -36
- package/plugin/scripts/post-tool-use.mjs.map +1 -1
- package/plugin/scripts/pre-compact.mjs +95 -5
- package/plugin/scripts/pre-compact.mjs.map +1 -1
- package/plugin/scripts/pre-tool-use.mjs +72 -2
- package/plugin/scripts/pre-tool-use.mjs.map +1 -1
- package/plugin/scripts/prompt-submit.mjs +25 -22
- package/plugin/scripts/prompt-submit.mjs.map +1 -1
- package/plugin/scripts/session-end.mjs +93 -24
- package/plugin/scripts/session-end.mjs.map +1 -1
- package/plugin/scripts/session-start.mjs +101 -6
- package/plugin/scripts/session-start.mjs.map +1 -1
- package/plugin/scripts/stop.mjs +72 -8
- package/plugin/scripts/stop.mjs.map +1 -1
- package/plugin/scripts/subagent-start.mjs +28 -26
- package/plugin/scripts/subagent-start.mjs.map +1 -1
- package/plugin/scripts/subagent-stop.mjs +29 -26
- package/plugin/scripts/subagent-stop.mjs.map +1 -1
- package/plugin/scripts/task-completed.mjs +31 -28
- package/plugin/scripts/task-completed.mjs.map +1 -1
- package/plugin/skills/_shared/TROUBLESHOOTING.md +22 -6
- package/plugin/skills/agentmemory-agents/REFERENCE.md +8 -5
- package/plugin/skills/agentmemory-config/REFERENCE.md +52 -1
- package/plugin/skills/agentmemory-config/SKILL.md +44 -7
- package/plugin/skills/agentmemory-mcp-tools/REFERENCE.md +4 -3
- package/plugin/skills/agentmemory-rest-api/REFERENCE.md +23 -2
- package/plugin/skills/forget/SKILL.md +4 -1
- package/plugin/skills/handoff/SKILL.md +4 -2
- package/plugin/skills/lesson/SKILL.md +64 -0
- package/plugin/skills/memory-discipline/SKILL.md +65 -0
- package/plugin/skills/recall/SKILL.md +4 -1
- package/plugin/skills/remember/SKILL.md +6 -1
- package/dist/config-ChC9EtgV.mjs +0 -259
- package/dist/config-ChC9EtgV.mjs.map +0 -1
- package/dist/connect-JnsJXc68.mjs.map +0 -1
- package/dist/image-refs-BKLmp_sD.mjs.map +0 -1
- package/dist/image-refs-C2YU7BIm.mjs.map +0 -1
- package/dist/keyed-mutex-DTHtcZrt.mjs.map +0 -1
- package/dist/logger-BEB7pCI9.mjs +0 -43
- package/dist/logger-BEB7pCI9.mjs.map +0 -1
- package/dist/schema-CNiCWzV9.mjs +0 -78
- package/dist/schema-CNiCWzV9.mjs.map +0 -1
- package/dist/src-CzgoepGU.mjs.map +0 -1
- package/dist/standalone-YsZFFQBM.mjs.map +0 -1
- package/dist/tools-registry-D3NEoe5m.mjs +0 -1115
- package/dist/tools-registry-D3NEoe5m.mjs.map +0 -1
- package/plugin/scripts/diagnostics.d.mts +0 -17
- package/plugin/scripts/diagnostics.d.mts.map +0 -1
- package/plugin/scripts/diagnostics.mjs.map +0 -1
|
@@ -1,4 +1,74 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
//#region src/secret-store.ts
|
|
6
|
+
const SECRET_KEY = "AGENTMEMORY_SECRET";
|
|
7
|
+
function agentmemoryHomeDir() {
|
|
8
|
+
return join(homedir(), ".agentmemory");
|
|
9
|
+
}
|
|
10
|
+
function secretFilePath() {
|
|
11
|
+
return join(agentmemoryHomeDir(), "secret");
|
|
12
|
+
}
|
|
13
|
+
function usable(value) {
|
|
14
|
+
if (typeof value !== "string") return "";
|
|
15
|
+
const trimmed = value.trim();
|
|
16
|
+
if (!trimmed) return "";
|
|
17
|
+
if (trimmed.startsWith("${") && trimmed.endsWith("}")) return "";
|
|
18
|
+
return trimmed;
|
|
19
|
+
}
|
|
20
|
+
function unquote(value) {
|
|
21
|
+
const quote = value[0];
|
|
22
|
+
if ((quote === "\"" || quote === "'") && value.length > 1) {
|
|
23
|
+
const close = value.indexOf(quote, 1);
|
|
24
|
+
if (close !== -1) return value.slice(1, close);
|
|
25
|
+
}
|
|
26
|
+
const hash = value.indexOf(" #");
|
|
27
|
+
return hash === -1 ? value : value.slice(0, hash).trim();
|
|
28
|
+
}
|
|
29
|
+
function readEnvFileSecret() {
|
|
30
|
+
let content;
|
|
31
|
+
try {
|
|
32
|
+
content = readFileSync(join(agentmemoryHomeDir(), ".env"), "utf-8");
|
|
33
|
+
} catch {
|
|
34
|
+
return "";
|
|
35
|
+
}
|
|
36
|
+
if (typeof content !== "string") return "";
|
|
37
|
+
let found = "";
|
|
38
|
+
for (const line of content.split("\n")) {
|
|
39
|
+
const trimmed = line.trim();
|
|
40
|
+
if (trimmed.startsWith("#")) continue;
|
|
41
|
+
const eq = trimmed.indexOf("=");
|
|
42
|
+
if (eq === -1) continue;
|
|
43
|
+
if (trimmed.slice(0, eq).replace(/^export\s+/, "").trim() !== SECRET_KEY) continue;
|
|
44
|
+
found = usable(unquote(trimmed.slice(eq + 1).trim()));
|
|
45
|
+
}
|
|
46
|
+
return found;
|
|
47
|
+
}
|
|
48
|
+
function readStoredSecret() {
|
|
49
|
+
try {
|
|
50
|
+
return usable(readFileSync(secretFilePath(), "utf-8"));
|
|
51
|
+
} catch {
|
|
52
|
+
return "";
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function isLoopbackUrl(url) {
|
|
56
|
+
let hostname;
|
|
57
|
+
try {
|
|
58
|
+
hostname = new URL(url).hostname.toLowerCase();
|
|
59
|
+
} catch {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
const bare = hostname.replace(/^\[|\]$/g, "");
|
|
63
|
+
return bare === "localhost" || bare === "::1" || /^127(?:\.\d{1,3}){3}$/.test(bare);
|
|
64
|
+
}
|
|
65
|
+
function resolveClientSecret(baseUrl, env = process.env) {
|
|
66
|
+
const fromEnv = usable(env[SECRET_KEY]);
|
|
67
|
+
if (fromEnv) return fromEnv;
|
|
68
|
+
if (!isLoopbackUrl(baseUrl)) return "";
|
|
69
|
+
return readEnvFileSecret() || readStoredSecret();
|
|
70
|
+
}
|
|
71
|
+
//#endregion
|
|
2
72
|
//#region src/hooks/pre-tool-use.ts
|
|
3
73
|
function isSdkChildContext(payload) {
|
|
4
74
|
if (process.env["AGENTMEMORY_SDK_CHILD"] === "1") return true;
|
|
@@ -7,7 +77,7 @@ function isSdkChildContext(payload) {
|
|
|
7
77
|
}
|
|
8
78
|
const INJECT_CONTEXT = process.env["AGENTMEMORY_INJECT_CONTEXT"] === "true";
|
|
9
79
|
const REST_URL = process.env["AGENTMEMORY_URL"] || "http://localhost:3111";
|
|
10
|
-
const SECRET =
|
|
80
|
+
const SECRET = resolveClientSecret(REST_URL);
|
|
11
81
|
function authHeaders() {
|
|
12
82
|
const h = { "Content-Type": "application/json" };
|
|
13
83
|
if (SECRET) h["Authorization"] = `Bearer ${SECRET}`;
|
|
@@ -56,7 +126,7 @@ async function main() {
|
|
|
56
126
|
const pattern = toolInput["pattern"];
|
|
57
127
|
if (typeof pattern === "string" && pattern.length > 0) terms.push(pattern);
|
|
58
128
|
}
|
|
59
|
-
const rawSessionId = data.session_id || data.sessionId;
|
|
129
|
+
const rawSessionId = data.session_id || data.sessionId || data.conversation_id;
|
|
60
130
|
const sessionId = typeof rawSessionId === "string" && rawSessionId.length > 0 ? rawSessionId : "unknown";
|
|
61
131
|
const project = typeof data.project === "string" && data.project.trim().length > 0 ? data.project.trim() : void 0;
|
|
62
132
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pre-tool-use.mjs","names":[],"sources":["../../src/hooks/pre-tool-use.ts"],"sourcesContent":["#!/usr/bin/env node\n\nfunction isSdkChildContext(payload: unknown): boolean {\n if (process.env[\"AGENTMEMORY_SDK_CHILD\"] === \"1\") return true;\n if (!payload || typeof payload !== \"object\") return false;\n return (payload as { entrypoint?: unknown }).entrypoint === \"sdk-ts\";\n}\n\n// Pre-tool-use enrichment hook.\n//\n// THIS HOOK IS A NO-OP BY DEFAULT AS OF 0.8.10 (#143). Previously it\n// fired /agentmemory/enrich on every Edit/Write/Read/Glob/Grep tool call\n// and wrote up to 4000 chars of context to stdout. Claude Code reads\n// PreToolUse stdout and prepends it to the model's next turn, which meant\n// agentmemory was silently injecting ~1000 tokens into every tool turn\n// via the user's Claude Code session. On Claude Pro that burned entire\n// allocations in a handful of messages (@adrianricardo, #143).\n//\n// Users who explicitly want pre-tool enrichment opt in with:\n// AGENTMEMORY_INJECT_CONTEXT=true in ~/.agentmemory/.env\n// and restart Claude Code. Expect your session input token count to grow\n// proportionally with the number of file-touching tool calls per turn.\nconst INJECT_CONTEXT = process.env[\"AGENTMEMORY_INJECT_CONTEXT\"] === \"true\";\n\nconst REST_URL = process.env[\"AGENTMEMORY_URL\"] || \"http://localhost:3111\";\nconst SECRET = process.env[\"AGENTMEMORY_SECRET\"] || \"\";\n\nfunction authHeaders(): Record<string, string> {\n const h: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (SECRET) h[\"Authorization\"] = `Bearer ${SECRET}`;\n return h;\n}\n\nasync function main() {\n // Default off: exit immediately so we don't even open stdin. This keeps\n // Claude Code's tool-call hot path as cheap as possible.\n if (!INJECT_CONTEXT) return;\n\n let input = \"\";\n for await (const chunk of process.stdin) {\n input += chunk;\n }\n\n let data: Record<string, unknown>;\n try {\n data = JSON.parse(input);\n } catch {\n return;\n }\n\n if (!data || typeof data !== \"object\") return;\n if (isSdkChildContext(data)) return;\n\n const toolName =\n typeof data.tool_name === \"string\"\n ? data.tool_name\n : typeof data.toolName === \"string\"\n ? data.toolName\n : undefined;\n if (!toolName) return;\n\n const normalizedToolName = toolName.toLowerCase();\n const fileTools = [\"edit\", \"write\", \"create\", \"read\", \"view\", \"glob\", \"grep\"];\n if (!fileTools.includes(normalizedToolName)) return;\n\n const rawToolInput = data.tool_input ?? data.toolArgs;\n const toolInput =\n typeof rawToolInput === \"object\" &&\n rawToolInput !== null &&\n !Array.isArray(rawToolInput)\n ? (rawToolInput as Record<string, unknown>)\n : {};\n const files: string[] = [];\n const fileKeys =\n normalizedToolName === \"grep\"\n ? [\"path\", \"file\"]\n : [\"file_path\", \"path\", \"file\", \"pattern\"];\n for (const key of fileKeys) {\n const val = toolInput[key];\n if (typeof val === \"string\" && val.length > 0) files.push(val);\n }\n if (files.length === 0) return;\n\n const terms: string[] = [];\n if (normalizedToolName === \"grep\" || normalizedToolName === \"glob\") {\n const pattern = toolInput[\"pattern\"];\n if (typeof pattern === \"string\" && pattern.length > 0) {\n terms.push(pattern);\n }\n }\n\n const rawSessionId = data.session_id || data.sessionId;\n const sessionId =\n typeof rawSessionId === \"string\" && rawSessionId.length > 0\n ? rawSessionId\n : \"unknown\";\n const project =\n typeof data.project === \"string\" && data.project.trim().length > 0\n ? data.project.trim()\n : undefined;\n\n try {\n const res = await fetch(`${REST_URL}/agentmemory/enrich`, {\n method: \"POST\",\n headers: authHeaders(),\n body: JSON.stringify({\n sessionId,\n files,\n terms,\n toolName,\n ...(project !== undefined && { project }),\n }),\n signal: AbortSignal.timeout(2000),\n });\n\n if (res.ok) {\n const result = (await res.json()) as { context?: string };\n if (result.context) {\n process.stdout.write(result.context);\n }\n }\n } catch {\n // don't block tool execution\n }\n}\n\nmain().catch(() => process.exit(0));\n"],"mappings":";;AAEA,SAAS,kBAAkB,SAA2B;AACpD,KAAI,QAAQ,IAAI,6BAA6B,IAAK,QAAO;AACzD,KAAI,CAAC,WAAW,OAAO,YAAY,SAAU,QAAO;AACpD,QAAQ,QAAqC,eAAe;;AAiB9D,MAAM,iBAAiB,QAAQ,IAAI,kCAAkC;AAErE,MAAM,WAAW,QAAQ,IAAI,sBAAsB;AACnD,MAAM,SAAS,QAAQ,IAAI,yBAAyB;AAEpD,SAAS,cAAsC;CAC7C,MAAM,IAA4B,EAAE,gBAAgB,oBAAoB;AACxE,KAAI,OAAQ,GAAE,mBAAmB,UAAU;AAC3C,QAAO;;AAGT,eAAe,OAAO;AAGpB,KAAI,CAAC,eAAgB;CAErB,IAAI,QAAQ;AACZ,YAAW,MAAM,SAAS,QAAQ,MAChC,UAAS;CAGX,IAAI;AACJ,KAAI;AACF,SAAO,KAAK,MAAM,MAAM;SAClB;AACN;;AAGF,KAAI,CAAC,QAAQ,OAAO,SAAS,SAAU;AACvC,KAAI,kBAAkB,KAAK,CAAE;CAE7B,MAAM,WACJ,OAAO,KAAK,cAAc,WACtB,KAAK,YACL,OAAO,KAAK,aAAa,WACvB,KAAK,WACL,KAAA;AACR,KAAI,CAAC,SAAU;CAEf,MAAM,qBAAqB,SAAS,aAAa;AAEjD,KAAI,CAAC;EADc;EAAQ;EAAS;EAAU;EAAQ;EAAQ;EAAQ;EACxD,CAAC,SAAS,mBAAmB,CAAE;CAE7C,MAAM,eAAe,KAAK,cAAc,KAAK;CAC7C,MAAM,YACJ,OAAO,iBAAiB,YACxB,iBAAiB,QACjB,CAAC,MAAM,QAAQ,aAAa,GACvB,eACD,EAAE;CACR,MAAM,QAAkB,EAAE;CAC1B,MAAM,WACJ,uBAAuB,SACnB,CAAC,QAAQ,OAAO,GAChB;EAAC;EAAa;EAAQ;EAAQ;EAAU;AAC9C,MAAK,MAAM,OAAO,UAAU;EAC1B,MAAM,MAAM,UAAU;AACtB,MAAI,OAAO,QAAQ,YAAY,IAAI,SAAS,EAAG,OAAM,KAAK,IAAI;;AAEhE,KAAI,MAAM,WAAW,EAAG;CAExB,MAAM,QAAkB,EAAE;AAC1B,KAAI,uBAAuB,UAAU,uBAAuB,QAAQ;EAClE,MAAM,UAAU,UAAU;AAC1B,MAAI,OAAO,YAAY,YAAY,QAAQ,SAAS,EAClD,OAAM,KAAK,QAAQ;;CAIvB,MAAM,eAAe,KAAK,cAAc,KAAK;CAC7C,MAAM,YACJ,OAAO,iBAAiB,YAAY,aAAa,SAAS,IACtD,eACA;CACN,MAAM,UACJ,OAAO,KAAK,YAAY,YAAY,KAAK,QAAQ,MAAM,CAAC,SAAS,IAC7D,KAAK,QAAQ,MAAM,GACnB,KAAA;AAEN,KAAI;EACF,MAAM,MAAM,MAAM,MAAM,GAAG,SAAS,sBAAsB;GACxD,QAAQ;GACR,SAAS,aAAa;GACtB,MAAM,KAAK,UAAU;IACnB;IACA;IACA;IACA;IACA,GAAI,YAAY,KAAA,KAAa,EAAE,SAAS;IACzC,CAAC;GACF,QAAQ,YAAY,QAAQ,IAAK;GAClC,CAAC;AAEF,MAAI,IAAI,IAAI;GACV,MAAM,SAAU,MAAM,IAAI,MAAM;AAChC,OAAI,OAAO,QACT,SAAQ,OAAO,MAAM,OAAO,QAAQ;;SAGlC;;AAKV,MAAM,CAAC,YAAY,QAAQ,KAAK,EAAE,CAAC"}
|
|
1
|
+
{"version":3,"file":"pre-tool-use.mjs","names":[],"sources":["../../src/secret-store.ts","../../src/hooks/pre-tool-use.ts"],"sourcesContent":["import {\n chmodSync,\n closeSync,\n linkSync,\n mkdirSync,\n openSync,\n readFileSync,\n unlinkSync,\n writeSync,\n} from \"node:fs\";\nimport { randomBytes } from \"node:crypto\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nconst SECRET_KEY = \"AGENTMEMORY_SECRET\";\n\nexport function agentmemoryHomeDir(): string {\n return join(homedir(), \".agentmemory\");\n}\n\nexport function secretFilePath(): string {\n return join(agentmemoryHomeDir(), \"secret\");\n}\n\nfunction usable(value: string | undefined): string {\n if (typeof value !== \"string\") return \"\";\n const trimmed = value.trim();\n if (!trimmed) return \"\";\n if (trimmed.startsWith(\"${\") && trimmed.endsWith(\"}\")) return \"\";\n return trimmed;\n}\n\nfunction unquote(value: string): string {\n const quote = value[0];\n if ((quote === '\"' || quote === \"'\") && value.length > 1) {\n const close = value.indexOf(quote, 1);\n if (close !== -1) return value.slice(1, close);\n }\n const hash = value.indexOf(\" #\");\n return hash === -1 ? value : value.slice(0, hash).trim();\n}\n\nexport function readEnvFileSecret(): string {\n let content: unknown;\n try {\n content = readFileSync(join(agentmemoryHomeDir(), \".env\"), \"utf-8\");\n } catch {\n return \"\";\n }\n if (typeof content !== \"string\") return \"\";\n let found = \"\";\n for (const line of content.split(\"\\n\")) {\n const trimmed = line.trim();\n if (trimmed.startsWith(\"#\")) continue;\n const eq = trimmed.indexOf(\"=\");\n if (eq === -1) continue;\n const key = trimmed.slice(0, eq).replace(/^export\\s+/, \"\").trim();\n if (key !== SECRET_KEY) continue;\n found = usable(unquote(trimmed.slice(eq + 1).trim()));\n }\n return found;\n}\n\nexport function readStoredSecret(): string {\n try {\n return usable(readFileSync(secretFilePath(), \"utf-8\"));\n } catch {\n return \"\";\n }\n}\n\nexport function explicitSecret(env: NodeJS.ProcessEnv = process.env): string {\n return usable(env[SECRET_KEY]) || readEnvFileSecret();\n}\n\nexport function isLoopbackUrl(url: string): boolean {\n let hostname: string;\n try {\n hostname = new URL(url).hostname.toLowerCase();\n } catch {\n return false;\n }\n const bare = hostname.replace(/^\\[|\\]$/g, \"\");\n return bare === \"localhost\" || bare === \"::1\" || /^127(?:\\.\\d{1,3}){3}$/.test(bare);\n}\n\nexport function resolveClientSecret(\n baseUrl: string,\n env: NodeJS.ProcessEnv = process.env,\n): string {\n const fromEnv = usable(env[SECRET_KEY]);\n if (fromEnv) return fromEnv;\n if (!isLoopbackUrl(baseUrl)) return \"\";\n return readEnvFileSecret() || readStoredSecret();\n}\n\nexport function ensureServerSecret(env: NodeJS.ProcessEnv = process.env): {\n secret: string;\n source: \"env\" | \"file\" | \"generated\";\n} {\n const configured = explicitSecret(env);\n if (configured) return { secret: configured, source: \"env\" };\n const stored = readStoredSecret();\n if (stored) return { secret: stored, source: \"file\" };\n const generated = randomBytes(32).toString(\"hex\");\n const path = secretFilePath();\n mkdirSync(agentmemoryHomeDir(), { recursive: true, mode: 0o700 });\n const staging = `${path}.${process.pid}.${randomBytes(4).toString(\"hex\")}.tmp`;\n const fd = openSync(staging, \"wx\", 0o600);\n try {\n writeSync(fd, `${generated}\\n`);\n } finally {\n closeSync(fd);\n }\n try {\n chmodSync(staging, 0o600);\n linkSync(staging, path);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"EEXIST\") throw err;\n const raced = readStoredSecret();\n if (!raced) throw err;\n return { secret: raced, source: \"file\" };\n } finally {\n try {\n unlinkSync(staging);\n } catch {}\n }\n return { secret: generated, source: \"generated\" };\n}\n\nexport function bearerHeaders(\n baseUrl: string,\n env: NodeJS.ProcessEnv = process.env,\n): Record<string, string> {\n const secret = resolveClientSecret(baseUrl, env);\n return secret ? { Authorization: `Bearer ${secret}` } : {};\n}\n","#!/usr/bin/env node\nimport { resolveClientSecret } from \"../secret-store.js\";\n\nfunction isSdkChildContext(payload: unknown): boolean {\n if (process.env[\"AGENTMEMORY_SDK_CHILD\"] === \"1\") return true;\n if (!payload || typeof payload !== \"object\") return false;\n return (payload as { entrypoint?: unknown }).entrypoint === \"sdk-ts\";\n}\n\nconst INJECT_CONTEXT = process.env[\"AGENTMEMORY_INJECT_CONTEXT\"] === \"true\";\n\nconst REST_URL = process.env[\"AGENTMEMORY_URL\"] || \"http://localhost:3111\";\nconst SECRET = resolveClientSecret(REST_URL);\n\nfunction authHeaders(): Record<string, string> {\n const h: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (SECRET) h[\"Authorization\"] = `Bearer ${SECRET}`;\n return h;\n}\n\nasync function main() {\n // Default off: exit immediately so we don't even open stdin. This keeps\n // Claude Code's tool-call hot path as cheap as possible.\n if (!INJECT_CONTEXT) return;\n\n let input = \"\";\n for await (const chunk of process.stdin) {\n input += chunk;\n }\n\n let data: Record<string, unknown>;\n try {\n data = JSON.parse(input);\n } catch {\n return;\n }\n\n if (!data || typeof data !== \"object\") return;\n if (isSdkChildContext(data)) return;\n\n const toolName =\n typeof data.tool_name === \"string\"\n ? data.tool_name\n : typeof data.toolName === \"string\"\n ? data.toolName\n : undefined;\n if (!toolName) return;\n\n const normalizedToolName = toolName.toLowerCase();\n const fileTools = [\"edit\", \"write\", \"create\", \"read\", \"view\", \"glob\", \"grep\"];\n if (!fileTools.includes(normalizedToolName)) return;\n\n const rawToolInput = data.tool_input ?? data.toolArgs;\n const toolInput =\n typeof rawToolInput === \"object\" &&\n rawToolInput !== null &&\n !Array.isArray(rawToolInput)\n ? (rawToolInput as Record<string, unknown>)\n : {};\n const files: string[] = [];\n const fileKeys =\n normalizedToolName === \"grep\"\n ? [\"path\", \"file\"]\n : [\"file_path\", \"path\", \"file\", \"pattern\"];\n for (const key of fileKeys) {\n const val = toolInput[key];\n if (typeof val === \"string\" && val.length > 0) files.push(val);\n }\n if (files.length === 0) return;\n\n const terms: string[] = [];\n if (normalizedToolName === \"grep\" || normalizedToolName === \"glob\") {\n const pattern = toolInput[\"pattern\"];\n if (typeof pattern === \"string\" && pattern.length > 0) {\n terms.push(pattern);\n }\n }\n\n const rawSessionId = data.session_id || data.sessionId || data.conversation_id;\n const sessionId =\n typeof rawSessionId === \"string\" && rawSessionId.length > 0\n ? rawSessionId\n : \"unknown\";\n const project =\n typeof data.project === \"string\" && data.project.trim().length > 0\n ? data.project.trim()\n : undefined;\n\n try {\n const res = await fetch(`${REST_URL}/agentmemory/enrich`, {\n method: \"POST\",\n headers: authHeaders(),\n body: JSON.stringify({\n sessionId,\n files,\n terms,\n toolName,\n ...(project !== undefined && { project }),\n }),\n signal: AbortSignal.timeout(2000),\n });\n\n if (res.ok) {\n const result = (await res.json()) as { context?: string };\n if (result.context) {\n process.stdout.write(result.context);\n }\n }\n } catch {\n // don't block tool execution\n }\n}\n\nmain().catch(() => process.exit(0));\n"],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { execSync } from "node:child_process";
|
|
3
3
|
import { basename } from "node:path";
|
|
4
|
+
import { captureObservation, isDrainChild, runDrainChild, withEventId } from "./_capture.mjs";
|
|
4
5
|
//#region src/hooks/_project.ts
|
|
5
6
|
function resolveProject(cwd) {
|
|
6
7
|
const explicit = process.env["AGENTMEMORY_PROJECT_NAME"];
|
|
@@ -20,6 +21,16 @@ function resolveProject(cwd) {
|
|
|
20
21
|
} catch {}
|
|
21
22
|
return basename(dir);
|
|
22
23
|
}
|
|
24
|
+
function hookCwd(data) {
|
|
25
|
+
if (!data || typeof data !== "object") return void 0;
|
|
26
|
+
if (typeof data.cwd === "string" && data.cwd.trim()) return data.cwd;
|
|
27
|
+
const roots = data.workspace_roots;
|
|
28
|
+
if (Array.isArray(roots)) {
|
|
29
|
+
for (const root of roots) if (typeof root === "string" && root.trim()) return root;
|
|
30
|
+
}
|
|
31
|
+
const projectDir = process.env["DEVIN_PROJECT_DIR"] || process.env["CLAUDE_PROJECT_DIR"];
|
|
32
|
+
if (projectDir && projectDir.trim()) return projectDir;
|
|
33
|
+
}
|
|
23
34
|
//#endregion
|
|
24
35
|
//#region src/hooks/prompt-submit.ts
|
|
25
36
|
function isSdkChildContext(payload) {
|
|
@@ -27,14 +38,10 @@ function isSdkChildContext(payload) {
|
|
|
27
38
|
if (!payload || typeof payload !== "object") return false;
|
|
28
39
|
return payload.entrypoint === "sdk-ts";
|
|
29
40
|
}
|
|
30
|
-
const
|
|
31
|
-
const
|
|
32
|
-
function authHeaders() {
|
|
33
|
-
const h = { "Content-Type": "application/json" };
|
|
34
|
-
if (SECRET) h["Authorization"] = `Bearer ${SECRET}`;
|
|
35
|
-
return h;
|
|
36
|
-
}
|
|
41
|
+
const OBSERVE_TIMEOUT_MS = 3e3;
|
|
42
|
+
const EXIT_CAP_MS = 3500;
|
|
37
43
|
async function main() {
|
|
44
|
+
if (isDrainChild()) return runDrainChild();
|
|
38
45
|
let input = "";
|
|
39
46
|
for await (const chunk of process.stdin) input += chunk;
|
|
40
47
|
let data;
|
|
@@ -45,21 +52,17 @@ async function main() {
|
|
|
45
52
|
}
|
|
46
53
|
if (!data || typeof data !== "object") return;
|
|
47
54
|
if (isSdkChildContext(data)) return;
|
|
48
|
-
const sessionId = data.session_id || data.sessionId || "unknown";
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}),
|
|
60
|
-
signal: AbortSignal.timeout(3e3)
|
|
61
|
-
}).catch(() => {});
|
|
62
|
-
setTimeout(() => process.exit(0), 500).unref();
|
|
55
|
+
const sessionId = data.session_id || data.sessionId || data.conversation_id || "unknown";
|
|
56
|
+
const cwd = hookCwd(data) || process.cwd();
|
|
57
|
+
captureObservation(withEventId({
|
|
58
|
+
hookType: "prompt_submit",
|
|
59
|
+
sessionId,
|
|
60
|
+
project: resolveProject(cwd),
|
|
61
|
+
cwd,
|
|
62
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
63
|
+
data: { prompt: data.prompt ?? data.userPrompt }
|
|
64
|
+
}, data), OBSERVE_TIMEOUT_MS);
|
|
65
|
+
setTimeout(() => process.exit(0), EXIT_CAP_MS).unref();
|
|
63
66
|
}
|
|
64
67
|
main().catch(() => process.exit(0));
|
|
65
68
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompt-submit.mjs","names":[],"sources":["../../src/hooks/_project.ts","../../src/hooks/prompt-submit.ts"],"sourcesContent":["import { execSync } from \"node:child_process\";\nimport { basename } from \"node:path\";\n\n// Resolution order: AGENTMEMORY_PROJECT_NAME env → git toplevel basename → cwd basename.\nexport function resolveProject(cwd?: string): string {\n const explicit = process.env[\"AGENTMEMORY_PROJECT_NAME\"];\n if (explicit && explicit.trim()) return explicit.trim();\n const dir = cwd && cwd.trim() ? cwd : process.cwd();\n try {\n const top = execSync(\"git rev-parse --show-toplevel\", {\n cwd: dir,\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n timeout: 500,\n })\n .toString()\n .trim();\n if (top) return basename(top);\n } catch {}\n return basename(dir);\n}\n","#!/usr/bin/env node\nimport { resolveProject } from \"./_project.js\";\n\nfunction isSdkChildContext(payload: unknown): boolean {\n if (process.env[\"AGENTMEMORY_SDK_CHILD\"] === \"1\") return true;\n if (!payload || typeof payload !== \"object\") return false;\n return (payload as { entrypoint?: unknown }).entrypoint === \"sdk-ts\";\n}\n\nconst
|
|
1
|
+
{"version":3,"file":"prompt-submit.mjs","names":[],"sources":["../../src/hooks/_project.ts","../../src/hooks/prompt-submit.ts"],"sourcesContent":["import { execSync } from \"node:child_process\";\nimport { basename } from \"node:path\";\n\n// Resolution order: AGENTMEMORY_PROJECT_NAME env → git toplevel basename → cwd basename.\nexport function resolveProject(cwd?: string): string {\n const explicit = process.env[\"AGENTMEMORY_PROJECT_NAME\"];\n if (explicit && explicit.trim()) return explicit.trim();\n const dir = cwd && cwd.trim() ? cwd : process.cwd();\n try {\n const top = execSync(\"git rev-parse --show-toplevel\", {\n cwd: dir,\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n timeout: 500,\n })\n .toString()\n .trim();\n if (top) return basename(top);\n } catch {}\n return basename(dir);\n}\n\nexport function hookCwd(data: Record<string, unknown> | null | undefined): string | undefined {\n if (!data || typeof data !== \"object\") return undefined;\n if (typeof data.cwd === \"string\" && data.cwd.trim()) return data.cwd;\n const roots = data.workspace_roots;\n if (Array.isArray(roots)) {\n for (const root of roots) {\n if (typeof root === \"string\" && root.trim()) return root;\n }\n }\n const projectDir =\n process.env[\"DEVIN_PROJECT_DIR\"] || process.env[\"CLAUDE_PROJECT_DIR\"];\n if (projectDir && projectDir.trim()) return projectDir;\n return undefined;\n}\n","#!/usr/bin/env node\nimport { resolveProject, hookCwd } from \"./_project.js\";\nimport { captureObservation, isDrainChild, runDrainChild, withEventId } from \"./_capture.js\";\n\nfunction isSdkChildContext(payload: unknown): boolean {\n if (process.env[\"AGENTMEMORY_SDK_CHILD\"] === \"1\") return true;\n if (!payload || typeof payload !== \"object\") return false;\n return (payload as { entrypoint?: unknown }).entrypoint === \"sdk-ts\";\n}\n\nconst OBSERVE_TIMEOUT_MS = 3000;\nconst EXIT_CAP_MS = 3500;\n\nasync function main() {\n if (isDrainChild()) return runDrainChild();\n let input = \"\";\n for await (const chunk of process.stdin) {\n input += chunk;\n }\n\n let data: Record<string, unknown>;\n try {\n data = JSON.parse(input);\n } catch {\n return;\n }\n\n if (!data || typeof data !== \"object\") return;\n if (isSdkChildContext(data)) return;\n\n const sessionId = ((data.session_id || data.sessionId || data.conversation_id) as string) || \"unknown\";\n\n const cwd = hookCwd(data) || process.cwd();\n\n void captureObservation(\n withEventId(\n {\n hookType: \"prompt_submit\",\n sessionId,\n project: resolveProject(cwd),\n cwd,\n timestamp: new Date().toISOString(),\n data: { prompt: data.prompt ?? data.userPrompt },\n },\n data,\n ),\n OBSERVE_TIMEOUT_MS,\n );\n setTimeout(() => process.exit(0), EXIT_CAP_MS).unref();\n}\n\nmain().catch(() => process.exit(0));\n"],"mappings":";;;;;AAIA,SAAgB,eAAe,KAAsB;CACnD,MAAM,WAAW,QAAQ,IAAI;AAC7B,KAAI,YAAY,SAAS,MAAM,CAAE,QAAO,SAAS,MAAM;CACvD,MAAM,MAAM,OAAO,IAAI,MAAM,GAAG,MAAM,QAAQ,KAAK;AACnD,KAAI;EACF,MAAM,MAAM,SAAS,iCAAiC;GACpD,KAAK;GACL,OAAO;IAAC;IAAU;IAAQ;IAAS;GACnC,SAAS;GACV,CAAC,CACC,UAAU,CACV,MAAM;AACT,MAAI,IAAK,QAAO,SAAS,IAAI;SACvB;AACR,QAAO,SAAS,IAAI;;AAGtB,SAAgB,QAAQ,MAAsE;AAC5F,KAAI,CAAC,QAAQ,OAAO,SAAS,SAAU,QAAO,KAAA;AAC9C,KAAI,OAAO,KAAK,QAAQ,YAAY,KAAK,IAAI,MAAM,CAAE,QAAO,KAAK;CACjE,MAAM,QAAQ,KAAK;AACnB,KAAI,MAAM,QAAQ,MAAM;OACjB,MAAM,QAAQ,MACjB,KAAI,OAAO,SAAS,YAAY,KAAK,MAAM,CAAE,QAAO;;CAGxD,MAAM,aACJ,QAAQ,IAAI,wBAAwB,QAAQ,IAAI;AAClD,KAAI,cAAc,WAAW,MAAM,CAAE,QAAO;;;;AC5B9C,SAAS,kBAAkB,SAA2B;AACpD,KAAI,QAAQ,IAAI,6BAA6B,IAAK,QAAO;AACzD,KAAI,CAAC,WAAW,OAAO,YAAY,SAAU,QAAO;AACpD,QAAQ,QAAqC,eAAe;;AAG9D,MAAM,qBAAqB;AAC3B,MAAM,cAAc;AAEpB,eAAe,OAAO;AACpB,KAAI,cAAc,CAAE,QAAO,eAAe;CAC1C,IAAI,QAAQ;AACZ,YAAW,MAAM,SAAS,QAAQ,MAChC,UAAS;CAGX,IAAI;AACJ,KAAI;AACF,SAAO,KAAK,MAAM,MAAM;SAClB;AACN;;AAGF,KAAI,CAAC,QAAQ,OAAO,SAAS,SAAU;AACvC,KAAI,kBAAkB,KAAK,CAAE;CAE7B,MAAM,YAAc,KAAK,cAAc,KAAK,aAAa,KAAK,mBAA+B;CAE7F,MAAM,MAAM,QAAQ,KAAK,IAAI,QAAQ,KAAK;AAErC,oBACH,YACE;EACE,UAAU;EACV;EACA,SAAS,eAAe,IAAI;EAC5B;EACA,4BAAW,IAAI,MAAM,EAAC,aAAa;EACnC,MAAM,EAAE,QAAQ,KAAK,UAAU,KAAK,YAAY;EACjD,EACD,KACD,EACD,mBACD;AACD,kBAAiB,QAAQ,KAAK,EAAE,EAAE,YAAY,CAAC,OAAO;;AAGxD,MAAM,CAAC,YAAY,QAAQ,KAAK,EAAE,CAAC"}
|
|
@@ -1,18 +1,82 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { execSync } from "node:child_process";
|
|
4
|
+
import { basename } from "node:path";
|
|
5
|
+
import { REST_URL, authHeaders, captureObservation, isDrainChild, runDrainChild, withEventId } from "./_capture.mjs";
|
|
6
|
+
//#region src/hooks/_project.ts
|
|
7
|
+
function resolveProject(cwd) {
|
|
8
|
+
const explicit = process.env["AGENTMEMORY_PROJECT_NAME"];
|
|
9
|
+
if (explicit && explicit.trim()) return explicit.trim();
|
|
10
|
+
const dir = cwd && cwd.trim() ? cwd : process.cwd();
|
|
11
|
+
try {
|
|
12
|
+
const top = execSync("git rev-parse --show-toplevel", {
|
|
13
|
+
cwd: dir,
|
|
14
|
+
stdio: [
|
|
15
|
+
"ignore",
|
|
16
|
+
"pipe",
|
|
17
|
+
"ignore"
|
|
18
|
+
],
|
|
19
|
+
timeout: 500
|
|
20
|
+
}).toString().trim();
|
|
21
|
+
if (top) return basename(top);
|
|
22
|
+
} catch {}
|
|
23
|
+
return basename(dir);
|
|
24
|
+
}
|
|
25
|
+
function hookCwd(data) {
|
|
26
|
+
if (!data || typeof data !== "object") return void 0;
|
|
27
|
+
if (typeof data.cwd === "string" && data.cwd.trim()) return data.cwd;
|
|
28
|
+
const roots = data.workspace_roots;
|
|
29
|
+
if (Array.isArray(roots)) {
|
|
30
|
+
for (const root of roots) if (typeof root === "string" && root.trim()) return root;
|
|
31
|
+
}
|
|
32
|
+
const projectDir = process.env["DEVIN_PROJECT_DIR"] || process.env["CLAUDE_PROJECT_DIR"];
|
|
33
|
+
if (projectDir && projectDir.trim()) return projectDir;
|
|
34
|
+
}
|
|
35
|
+
//#endregion
|
|
2
36
|
//#region src/hooks/session-end.ts
|
|
3
37
|
function isSdkChildContext(payload) {
|
|
4
38
|
if (process.env["AGENTMEMORY_SDK_CHILD"] === "1") return true;
|
|
5
39
|
if (!payload || typeof payload !== "object") return false;
|
|
6
40
|
return payload.entrypoint === "sdk-ts";
|
|
7
41
|
}
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
42
|
+
function extractTranscriptPrompts(data) {
|
|
43
|
+
const path = data.transcript_path;
|
|
44
|
+
if (typeof path !== "string" || !path.endsWith(".jsonl")) return [];
|
|
45
|
+
let raw;
|
|
46
|
+
try {
|
|
47
|
+
raw = readFileSync(path, "utf-8");
|
|
48
|
+
} catch {
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
const prompts = [];
|
|
52
|
+
for (const line of raw.split("\n")) {
|
|
53
|
+
if (!line.trim()) continue;
|
|
54
|
+
let msg;
|
|
55
|
+
try {
|
|
56
|
+
msg = JSON.parse(line);
|
|
57
|
+
} catch {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
if (msg.type === "USER_INPUT" && msg.source === "USER_EXPLICIT" && typeof msg.content === "string") {
|
|
61
|
+
if (prompts.length >= 50) return prompts;
|
|
62
|
+
const match = msg.content.match(/<USER_REQUEST>\n?([\s\S]*?)\n?<\/USER_REQUEST>/);
|
|
63
|
+
const text = (match ? match[1] : msg.content).trim();
|
|
64
|
+
if (text) prompts.push(text.slice(0, 8e3));
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
if (msg.role !== "user") continue;
|
|
68
|
+
for (const block of msg.message?.content ?? []) {
|
|
69
|
+
if (prompts.length >= 50) return prompts;
|
|
70
|
+
if (block.type !== "text" || typeof block.text !== "string") continue;
|
|
71
|
+
const m = block.text.match(/<user_query>\n?([\s\S]*?)\n?<\/user_query>/);
|
|
72
|
+
const text = (m ? m[1] : block.text).trim();
|
|
73
|
+
if (text) prompts.push(text.slice(0, 8e3));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return prompts;
|
|
14
77
|
}
|
|
15
78
|
async function main() {
|
|
79
|
+
if (isDrainChild()) return runDrainChild();
|
|
16
80
|
let input = "";
|
|
17
81
|
for await (const chunk of process.stdin) input += chunk;
|
|
18
82
|
let data;
|
|
@@ -23,30 +87,35 @@ async function main() {
|
|
|
23
87
|
}
|
|
24
88
|
if (!data || typeof data !== "object") return;
|
|
25
89
|
if (isSdkChildContext(data)) return;
|
|
26
|
-
const sessionId = data.session_id || data.sessionId || "unknown";
|
|
90
|
+
const sessionId = data.session_id || data.sessionId || data.conversation_id || "unknown";
|
|
91
|
+
const transcriptPrompts = extractTranscriptPrompts(data);
|
|
92
|
+
if (transcriptPrompts.length > 0) {
|
|
93
|
+
const cwd = hookCwd(data) || process.cwd();
|
|
94
|
+
const project = resolveProject(cwd);
|
|
95
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
96
|
+
await Promise.allSettled(transcriptPrompts.map((prompt, index) => captureObservation(withEventId({
|
|
97
|
+
hookType: "prompt_submit",
|
|
98
|
+
sessionId,
|
|
99
|
+
project,
|
|
100
|
+
cwd,
|
|
101
|
+
timestamp,
|
|
102
|
+
data: {
|
|
103
|
+
prompt,
|
|
104
|
+
backfill: true
|
|
105
|
+
}
|
|
106
|
+
}, {}, {
|
|
107
|
+
source: "transcript",
|
|
108
|
+
transcript: data.transcript_path,
|
|
109
|
+
index,
|
|
110
|
+
prompt
|
|
111
|
+
}, { stable: true }), 3e3)));
|
|
112
|
+
}
|
|
27
113
|
fetch(`${REST_URL}/agentmemory/session/end`, {
|
|
28
114
|
method: "POST",
|
|
29
115
|
headers: authHeaders(),
|
|
30
116
|
body: JSON.stringify({ sessionId }),
|
|
31
117
|
signal: AbortSignal.timeout(3e4)
|
|
32
118
|
}).catch(() => {});
|
|
33
|
-
if (process.env["CONSOLIDATION_ENABLED"] === "true") {
|
|
34
|
-
fetch(`${REST_URL}/agentmemory/crystals/auto`, {
|
|
35
|
-
method: "POST",
|
|
36
|
-
headers: authHeaders(),
|
|
37
|
-
body: JSON.stringify({ olderThanDays: 0 }),
|
|
38
|
-
signal: AbortSignal.timeout(6e4)
|
|
39
|
-
}).catch(() => {});
|
|
40
|
-
fetch(`${REST_URL}/agentmemory/consolidate-pipeline`, {
|
|
41
|
-
method: "POST",
|
|
42
|
-
headers: authHeaders(),
|
|
43
|
-
body: JSON.stringify({
|
|
44
|
-
tier: "all",
|
|
45
|
-
force: true
|
|
46
|
-
}),
|
|
47
|
-
signal: AbortSignal.timeout(12e4)
|
|
48
|
-
}).catch(() => {});
|
|
49
|
-
}
|
|
50
119
|
if (process.env["CLAUDE_MEMORY_BRIDGE"] === "true") fetch(`${REST_URL}/agentmemory/claude-bridge/sync`, {
|
|
51
120
|
method: "POST",
|
|
52
121
|
headers: authHeaders(),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-end.mjs","names":[],"sources":["../../src/hooks/session-end.ts"],"sourcesContent":["#!/usr/bin/env node\n\nfunction isSdkChildContext(payload: unknown): boolean {\n if (process.env[\"AGENTMEMORY_SDK_CHILD\"] === \"1\") return true;\n if (!payload || typeof payload !== \"object\") return false;\n return (payload as { entrypoint?: unknown }).entrypoint === \"sdk-ts\";\n}\n\
|
|
1
|
+
{"version":3,"file":"session-end.mjs","names":[],"sources":["../../src/hooks/_project.ts","../../src/hooks/session-end.ts"],"sourcesContent":["import { execSync } from \"node:child_process\";\nimport { basename } from \"node:path\";\n\n// Resolution order: AGENTMEMORY_PROJECT_NAME env → git toplevel basename → cwd basename.\nexport function resolveProject(cwd?: string): string {\n const explicit = process.env[\"AGENTMEMORY_PROJECT_NAME\"];\n if (explicit && explicit.trim()) return explicit.trim();\n const dir = cwd && cwd.trim() ? cwd : process.cwd();\n try {\n const top = execSync(\"git rev-parse --show-toplevel\", {\n cwd: dir,\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n timeout: 500,\n })\n .toString()\n .trim();\n if (top) return basename(top);\n } catch {}\n return basename(dir);\n}\n\nexport function hookCwd(data: Record<string, unknown> | null | undefined): string | undefined {\n if (!data || typeof data !== \"object\") return undefined;\n if (typeof data.cwd === \"string\" && data.cwd.trim()) return data.cwd;\n const roots = data.workspace_roots;\n if (Array.isArray(roots)) {\n for (const root of roots) {\n if (typeof root === \"string\" && root.trim()) return root;\n }\n }\n const projectDir =\n process.env[\"DEVIN_PROJECT_DIR\"] || process.env[\"CLAUDE_PROJECT_DIR\"];\n if (projectDir && projectDir.trim()) return projectDir;\n return undefined;\n}\n","#!/usr/bin/env node\nimport { readFileSync } from \"node:fs\";\nimport { resolveProject, hookCwd } from \"./_project.js\";\nimport { REST_URL, authHeaders, captureObservation, isDrainChild, runDrainChild, withEventId } from \"./_capture.js\";\n\nfunction isSdkChildContext(payload: unknown): boolean {\n if (process.env[\"AGENTMEMORY_SDK_CHILD\"] === \"1\") return true;\n if (!payload || typeof payload !== \"object\") return false;\n return (payload as { entrypoint?: unknown }).entrypoint === \"sdk-ts\";\n}\n\nfunction extractTranscriptPrompts(data: Record<string, unknown>): string[] {\n const path = data.transcript_path;\n if (typeof path !== \"string\" || !path.endsWith(\".jsonl\")) return [];\n let raw: string;\n try {\n raw = readFileSync(path, \"utf-8\");\n } catch {\n return [];\n }\n const prompts: string[] = [];\n for (const line of raw.split(\"\\n\")) {\n if (!line.trim()) continue;\n let msg: {\n role?: string;\n type?: string;\n source?: string;\n content?: unknown;\n message?: { content?: Array<{ type?: string; text?: string }> };\n };\n try {\n msg = JSON.parse(line);\n } catch {\n continue;\n }\n if (msg.type === \"USER_INPUT\" && msg.source === \"USER_EXPLICIT\" && typeof msg.content === \"string\") {\n if (prompts.length >= 50) return prompts;\n const match = msg.content.match(/<USER_REQUEST>\\n?([\\s\\S]*?)\\n?<\\/USER_REQUEST>/);\n const text = (match ? match[1] : msg.content).trim();\n if (text) prompts.push(text.slice(0, 8000));\n continue;\n }\n if (msg.role !== \"user\") continue;\n for (const block of msg.message?.content ?? []) {\n if (prompts.length >= 50) return prompts;\n if (block.type !== \"text\" || typeof block.text !== \"string\") continue;\n const m = block.text.match(/<user_query>\\n?([\\s\\S]*?)\\n?<\\/user_query>/);\n const text = (m ? m[1] : block.text).trim();\n if (text) prompts.push(text.slice(0, 8000));\n }\n }\n return prompts;\n}\n\nasync function main() {\n if (isDrainChild()) return runDrainChild();\n let input = \"\";\n for await (const chunk of process.stdin) {\n input += chunk;\n }\n\n let data: Record<string, unknown>;\n try {\n data = JSON.parse(input);\n } catch {\n return;\n }\n\n if (!data || typeof data !== \"object\") return;\n if (isSdkChildContext(data)) return;\n\n const sessionId = ((data.session_id || data.sessionId || data.conversation_id) as string) || \"unknown\";\n\n const transcriptPrompts = extractTranscriptPrompts(data);\n if (transcriptPrompts.length > 0) {\n const cwd = hookCwd(data) || process.cwd();\n const project = resolveProject(cwd);\n const timestamp = new Date().toISOString();\n await Promise.allSettled(\n transcriptPrompts.map((prompt, index) =>\n captureObservation(\n withEventId(\n { hookType: \"prompt_submit\", sessionId, project, cwd, timestamp, data: { prompt, backfill: true } },\n {},\n { source: \"transcript\", transcript: data.transcript_path, index, prompt },\n { stable: true },\n ),\n 3000,\n ),\n ),\n );\n }\n\n fetch(`${REST_URL}/agentmemory/session/end`, {\n method: \"POST\",\n headers: authHeaders(),\n body: JSON.stringify({ sessionId }),\n signal: AbortSignal.timeout(30000),\n }).catch(() => {});\n\n if (process.env[\"CLAUDE_MEMORY_BRIDGE\"] === \"true\") {\n fetch(`${REST_URL}/agentmemory/claude-bridge/sync`, {\n method: \"POST\",\n headers: authHeaders(),\n signal: AbortSignal.timeout(30000),\n }).catch(() => {});\n }\n\n setTimeout(() => process.exit(0), 1500).unref();\n}\n\nmain().catch(() => process.exit(0));\n"],"mappings":"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"}
|
|
@@ -1,6 +1,75 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { basename, join } from "node:path";
|
|
2
5
|
import { execSync } from "node:child_process";
|
|
3
|
-
|
|
6
|
+
//#region src/secret-store.ts
|
|
7
|
+
const SECRET_KEY = "AGENTMEMORY_SECRET";
|
|
8
|
+
function agentmemoryHomeDir() {
|
|
9
|
+
return join(homedir(), ".agentmemory");
|
|
10
|
+
}
|
|
11
|
+
function secretFilePath() {
|
|
12
|
+
return join(agentmemoryHomeDir(), "secret");
|
|
13
|
+
}
|
|
14
|
+
function usable(value) {
|
|
15
|
+
if (typeof value !== "string") return "";
|
|
16
|
+
const trimmed = value.trim();
|
|
17
|
+
if (!trimmed) return "";
|
|
18
|
+
if (trimmed.startsWith("${") && trimmed.endsWith("}")) return "";
|
|
19
|
+
return trimmed;
|
|
20
|
+
}
|
|
21
|
+
function unquote(value) {
|
|
22
|
+
const quote = value[0];
|
|
23
|
+
if ((quote === "\"" || quote === "'") && value.length > 1) {
|
|
24
|
+
const close = value.indexOf(quote, 1);
|
|
25
|
+
if (close !== -1) return value.slice(1, close);
|
|
26
|
+
}
|
|
27
|
+
const hash = value.indexOf(" #");
|
|
28
|
+
return hash === -1 ? value : value.slice(0, hash).trim();
|
|
29
|
+
}
|
|
30
|
+
function readEnvFileSecret() {
|
|
31
|
+
let content;
|
|
32
|
+
try {
|
|
33
|
+
content = readFileSync(join(agentmemoryHomeDir(), ".env"), "utf-8");
|
|
34
|
+
} catch {
|
|
35
|
+
return "";
|
|
36
|
+
}
|
|
37
|
+
if (typeof content !== "string") return "";
|
|
38
|
+
let found = "";
|
|
39
|
+
for (const line of content.split("\n")) {
|
|
40
|
+
const trimmed = line.trim();
|
|
41
|
+
if (trimmed.startsWith("#")) continue;
|
|
42
|
+
const eq = trimmed.indexOf("=");
|
|
43
|
+
if (eq === -1) continue;
|
|
44
|
+
if (trimmed.slice(0, eq).replace(/^export\s+/, "").trim() !== SECRET_KEY) continue;
|
|
45
|
+
found = usable(unquote(trimmed.slice(eq + 1).trim()));
|
|
46
|
+
}
|
|
47
|
+
return found;
|
|
48
|
+
}
|
|
49
|
+
function readStoredSecret() {
|
|
50
|
+
try {
|
|
51
|
+
return usable(readFileSync(secretFilePath(), "utf-8"));
|
|
52
|
+
} catch {
|
|
53
|
+
return "";
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function isLoopbackUrl(url) {
|
|
57
|
+
let hostname;
|
|
58
|
+
try {
|
|
59
|
+
hostname = new URL(url).hostname.toLowerCase();
|
|
60
|
+
} catch {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
const bare = hostname.replace(/^\[|\]$/g, "");
|
|
64
|
+
return bare === "localhost" || bare === "::1" || /^127(?:\.\d{1,3}){3}$/.test(bare);
|
|
65
|
+
}
|
|
66
|
+
function resolveClientSecret(baseUrl, env = process.env) {
|
|
67
|
+
const fromEnv = usable(env[SECRET_KEY]);
|
|
68
|
+
if (fromEnv) return fromEnv;
|
|
69
|
+
if (!isLoopbackUrl(baseUrl)) return "";
|
|
70
|
+
return readEnvFileSecret() || readStoredSecret();
|
|
71
|
+
}
|
|
72
|
+
//#endregion
|
|
4
73
|
//#region src/hooks/_project.ts
|
|
5
74
|
function resolveProject(cwd) {
|
|
6
75
|
const explicit = process.env["AGENTMEMORY_PROJECT_NAME"];
|
|
@@ -20,6 +89,16 @@ function resolveProject(cwd) {
|
|
|
20
89
|
} catch {}
|
|
21
90
|
return basename(dir);
|
|
22
91
|
}
|
|
92
|
+
function hookCwd(data) {
|
|
93
|
+
if (!data || typeof data !== "object") return void 0;
|
|
94
|
+
if (typeof data.cwd === "string" && data.cwd.trim()) return data.cwd;
|
|
95
|
+
const roots = data.workspace_roots;
|
|
96
|
+
if (Array.isArray(roots)) {
|
|
97
|
+
for (const root of roots) if (typeof root === "string" && root.trim()) return root;
|
|
98
|
+
}
|
|
99
|
+
const projectDir = process.env["DEVIN_PROJECT_DIR"] || process.env["CLAUDE_PROJECT_DIR"];
|
|
100
|
+
if (projectDir && projectDir.trim()) return projectDir;
|
|
101
|
+
}
|
|
23
102
|
//#endregion
|
|
24
103
|
//#region src/hooks/session-start.ts
|
|
25
104
|
function isSdkChildContext(payload) {
|
|
@@ -29,7 +108,7 @@ function isSdkChildContext(payload) {
|
|
|
29
108
|
}
|
|
30
109
|
const INJECT_CONTEXT = process.env["AGENTMEMORY_INJECT_CONTEXT"] === "true";
|
|
31
110
|
const REST_URL = process.env["AGENTMEMORY_URL"] || "http://localhost:3111";
|
|
32
|
-
const SECRET =
|
|
111
|
+
const SECRET = resolveClientSecret(REST_URL);
|
|
33
112
|
const INJECT_TIMEOUT_MS = 1500;
|
|
34
113
|
const REGISTER_TIMEOUT_MS = 800;
|
|
35
114
|
function authHeaders() {
|
|
@@ -37,6 +116,22 @@ function authHeaders() {
|
|
|
37
116
|
if (SECRET) h["Authorization"] = `Bearer ${SECRET}`;
|
|
38
117
|
return h;
|
|
39
118
|
}
|
|
119
|
+
function isPlainTextHost() {
|
|
120
|
+
return Boolean(process.env["FACTORY_PROJECT_DIR"] || process.env["DROID_PLUGIN_ROOT"]);
|
|
121
|
+
}
|
|
122
|
+
function wantsStructuredOutput(data) {
|
|
123
|
+
if (process.env["DEVIN_PROJECT_DIR"] || data.prompt_id !== void 0) return true;
|
|
124
|
+
return data.hook_event_name === "SessionStart" && !isPlainTextHost();
|
|
125
|
+
}
|
|
126
|
+
function contextPayload(data, context) {
|
|
127
|
+
if (process.env["COPILOT_PLUGIN_ROOT"] && !data.hook_event_name) return JSON.stringify({ additionalContext: context });
|
|
128
|
+
if (typeof data.cursor_version === "string" || data.hook_event_name === "sessionStart") return JSON.stringify({ additional_context: context });
|
|
129
|
+
if (wantsStructuredOutput(data)) return JSON.stringify({ hookSpecificOutput: {
|
|
130
|
+
hookEventName: "SessionStart",
|
|
131
|
+
additionalContext: context
|
|
132
|
+
} });
|
|
133
|
+
return context;
|
|
134
|
+
}
|
|
40
135
|
async function main() {
|
|
41
136
|
let input = "";
|
|
42
137
|
for await (const chunk of process.stdin) input += chunk;
|
|
@@ -48,9 +143,9 @@ async function main() {
|
|
|
48
143
|
}
|
|
49
144
|
if (!data || typeof data !== "object") return;
|
|
50
145
|
if (isSdkChildContext(data)) return;
|
|
51
|
-
const sessionId = data.session_id || data.sessionId || `ses_${Date.now().toString(36)}`;
|
|
52
|
-
const cwd = data
|
|
53
|
-
const project = resolveProject(
|
|
146
|
+
const sessionId = data.session_id || data.sessionId || data.conversation_id || `ses_${Date.now().toString(36)}`;
|
|
147
|
+
const cwd = hookCwd(data) || process.cwd();
|
|
148
|
+
const project = resolveProject(cwd);
|
|
54
149
|
const url = `${REST_URL}/agentmemory/session/start`;
|
|
55
150
|
const init = {
|
|
56
151
|
method: "POST",
|
|
@@ -75,7 +170,7 @@ async function main() {
|
|
|
75
170
|
});
|
|
76
171
|
if (res.ok) {
|
|
77
172
|
const result = await res.json();
|
|
78
|
-
if (result.context) process.stdout.write(result.context);
|
|
173
|
+
if (result.context) process.stdout.write(contextPayload(data, result.context));
|
|
79
174
|
}
|
|
80
175
|
} catch {}
|
|
81
176
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-start.mjs","names":[],"sources":["../../src/hooks/_project.ts","../../src/hooks/session-start.ts"],"sourcesContent":["import { execSync } from \"node:child_process\";\nimport { basename } from \"node:path\";\n\n// Resolution order: AGENTMEMORY_PROJECT_NAME env → git toplevel basename → cwd basename.\nexport function resolveProject(cwd?: string): string {\n const explicit = process.env[\"AGENTMEMORY_PROJECT_NAME\"];\n if (explicit && explicit.trim()) return explicit.trim();\n const dir = cwd && cwd.trim() ? cwd : process.cwd();\n try {\n const top = execSync(\"git rev-parse --show-toplevel\", {\n cwd: dir,\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n timeout: 500,\n })\n .toString()\n .trim();\n if (top) return basename(top);\n } catch {}\n return basename(dir);\n}\n","#!/usr/bin/env node\nimport { resolveProject } from \"./_project.js\";\n\n// Inlined from ./sdk-guard so each hook bundles to a single self-contained\n// .mjs (matches the pattern used by every other hook entry in tsdown.config).\nfunction isSdkChildContext(payload: unknown): boolean {\n if (process.env[\"AGENTMEMORY_SDK_CHILD\"] === \"1\") return true;\n if (!payload || typeof payload !== \"object\") return false;\n return (payload as { entrypoint?: unknown }).entrypoint === \"sdk-ts\";\n}\n\n// Session-start hook.\n//\n// Always registers the session for observation tracking (so memories\n// captured on PostToolUse get attached to the right session). Only writes\n// project context to stdout — which Claude Code prepends to the very first\n// turn — when AGENTMEMORY_INJECT_CONTEXT=true. Default off as of 0.8.10\n// (#143); see pre-tool-use.ts for the full explanation.\nconst INJECT_CONTEXT = process.env[\"AGENTMEMORY_INJECT_CONTEXT\"] === \"true\";\n\nconst REST_URL = process.env[\"AGENTMEMORY_URL\"] || \"http://localhost:3111\";\nconst SECRET = process.env[\"AGENTMEMORY_SECRET\"] || \"\";\n\n// When the server is unreachable a 5s timeout multiplies hard under\n// concurrent fan-out (Slack bots, multi-agent harnesses) and becomes a\n// positive feedback loop that OOM-kills iii-engine (#221). Cap tight on\n// both paths and skip the await entirely when the response is unused.\nconst INJECT_TIMEOUT_MS = 1500;\nconst REGISTER_TIMEOUT_MS = 800;\n\nfunction authHeaders(): Record<string, string> {\n const h: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (SECRET) h[\"Authorization\"] = `Bearer ${SECRET}`;\n return h;\n}\n\nasync function main() {\n let input = \"\";\n for await (const chunk of process.stdin) {\n input += chunk;\n }\n\n let data: Record<string, unknown>;\n try {\n data = JSON.parse(input);\n } catch {\n return;\n }\n\n if (!data || typeof data !== \"object\") return;\n if (isSdkChildContext(data)) return;\n\n const sessionId =\n ((data.session_id || data.sessionId) as string) ||\n `ses_${Date.now().toString(36)}`;\n const cwd = (data.cwd as string) || process.cwd();\n const project = resolveProject(data.cwd as string | undefined);\n\n const url = `${REST_URL}/agentmemory/session/start`;\n const init: RequestInit = {\n method: \"POST\",\n headers: authHeaders(),\n body: JSON.stringify({ sessionId, project, cwd }),\n };\n\n if (!INJECT_CONTEXT) {\n // Pure telemetry path: caller never reads the response, so don't\n // block on it. AbortSignal.timeout caps the wait the event loop\n // gives the pending socket before exit.\n fetch(url, {\n ...init,\n signal: AbortSignal.timeout(REGISTER_TIMEOUT_MS),\n }).catch(() => {});\n return;\n }\n\n try {\n const res = await fetch(url, {\n ...init,\n signal: AbortSignal.timeout(INJECT_TIMEOUT_MS),\n });\n if (res.ok) {\n const result = (await res.json()) as { context?: string };\n if (result.context) {\n process.stdout.write(result.context);\n }\n }\n } catch {\n // silently fail -- don't block Claude Code startup\n }\n}\n\nmain().catch(() => process.exit(0));\n"],"mappings":";;;;AAIA,SAAgB,eAAe,KAAsB;CACnD,MAAM,WAAW,QAAQ,IAAI;AAC7B,KAAI,YAAY,SAAS,MAAM,CAAE,QAAO,SAAS,MAAM;CACvD,MAAM,MAAM,OAAO,IAAI,MAAM,GAAG,MAAM,QAAQ,KAAK;AACnD,KAAI;EACF,MAAM,MAAM,SAAS,iCAAiC;GACpD,KAAK;GACL,OAAO;IAAC;IAAU;IAAQ;IAAS;GACnC,SAAS;GACV,CAAC,CACC,UAAU,CACV,MAAM;AACT,MAAI,IAAK,QAAO,SAAS,IAAI;SACvB;AACR,QAAO,SAAS,IAAI;;;;ACbtB,SAAS,kBAAkB,SAA2B;AACpD,KAAI,QAAQ,IAAI,6BAA6B,IAAK,QAAO;AACzD,KAAI,CAAC,WAAW,OAAO,YAAY,SAAU,QAAO;AACpD,QAAQ,QAAqC,eAAe;;AAU9D,MAAM,iBAAiB,QAAQ,IAAI,kCAAkC;AAErE,MAAM,WAAW,QAAQ,IAAI,sBAAsB;AACnD,MAAM,SAAS,QAAQ,IAAI,yBAAyB;AAMpD,MAAM,oBAAoB;AAC1B,MAAM,sBAAsB;AAE5B,SAAS,cAAsC;CAC7C,MAAM,IAA4B,EAAE,gBAAgB,oBAAoB;AACxE,KAAI,OAAQ,GAAE,mBAAmB,UAAU;AAC3C,QAAO;;AAGT,eAAe,OAAO;CACpB,IAAI,QAAQ;AACZ,YAAW,MAAM,SAAS,QAAQ,MAChC,UAAS;CAGX,IAAI;AACJ,KAAI;AACF,SAAO,KAAK,MAAM,MAAM;SAClB;AACN;;AAGF,KAAI,CAAC,QAAQ,OAAO,SAAS,SAAU;AACvC,KAAI,kBAAkB,KAAK,CAAE;CAE7B,MAAM,YACF,KAAK,cAAc,KAAK,aAC1B,OAAO,KAAK,KAAK,CAAC,SAAS,GAAG;CAChC,MAAM,MAAO,KAAK,OAAkB,QAAQ,KAAK;CACjD,MAAM,UAAU,eAAe,KAAK,IAA0B;CAE9D,MAAM,MAAM,GAAG,SAAS;CACxB,MAAM,OAAoB;EACxB,QAAQ;EACR,SAAS,aAAa;EACtB,MAAM,KAAK,UAAU;GAAE;GAAW;GAAS;GAAK,CAAC;EAClD;AAED,KAAI,CAAC,gBAAgB;AAInB,QAAM,KAAK;GACT,GAAG;GACH,QAAQ,YAAY,QAAQ,oBAAoB;GACjD,CAAC,CAAC,YAAY,GAAG;AAClB;;AAGF,KAAI;EACF,MAAM,MAAM,MAAM,MAAM,KAAK;GAC3B,GAAG;GACH,QAAQ,YAAY,QAAQ,kBAAkB;GAC/C,CAAC;AACF,MAAI,IAAI,IAAI;GACV,MAAM,SAAU,MAAM,IAAI,MAAM;AAChC,OAAI,OAAO,QACT,SAAQ,OAAO,MAAM,OAAO,QAAQ;;SAGlC;;AAKV,MAAM,CAAC,YAAY,QAAQ,KAAK,EAAE,CAAC"}
|
|
1
|
+
{"version":3,"file":"session-start.mjs","names":[],"sources":["../../src/secret-store.ts","../../src/hooks/_project.ts","../../src/hooks/session-start.ts"],"sourcesContent":["import {\n chmodSync,\n closeSync,\n linkSync,\n mkdirSync,\n openSync,\n readFileSync,\n unlinkSync,\n writeSync,\n} from \"node:fs\";\nimport { randomBytes } from \"node:crypto\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nconst SECRET_KEY = \"AGENTMEMORY_SECRET\";\n\nexport function agentmemoryHomeDir(): string {\n return join(homedir(), \".agentmemory\");\n}\n\nexport function secretFilePath(): string {\n return join(agentmemoryHomeDir(), \"secret\");\n}\n\nfunction usable(value: string | undefined): string {\n if (typeof value !== \"string\") return \"\";\n const trimmed = value.trim();\n if (!trimmed) return \"\";\n if (trimmed.startsWith(\"${\") && trimmed.endsWith(\"}\")) return \"\";\n return trimmed;\n}\n\nfunction unquote(value: string): string {\n const quote = value[0];\n if ((quote === '\"' || quote === \"'\") && value.length > 1) {\n const close = value.indexOf(quote, 1);\n if (close !== -1) return value.slice(1, close);\n }\n const hash = value.indexOf(\" #\");\n return hash === -1 ? value : value.slice(0, hash).trim();\n}\n\nexport function readEnvFileSecret(): string {\n let content: unknown;\n try {\n content = readFileSync(join(agentmemoryHomeDir(), \".env\"), \"utf-8\");\n } catch {\n return \"\";\n }\n if (typeof content !== \"string\") return \"\";\n let found = \"\";\n for (const line of content.split(\"\\n\")) {\n const trimmed = line.trim();\n if (trimmed.startsWith(\"#\")) continue;\n const eq = trimmed.indexOf(\"=\");\n if (eq === -1) continue;\n const key = trimmed.slice(0, eq).replace(/^export\\s+/, \"\").trim();\n if (key !== SECRET_KEY) continue;\n found = usable(unquote(trimmed.slice(eq + 1).trim()));\n }\n return found;\n}\n\nexport function readStoredSecret(): string {\n try {\n return usable(readFileSync(secretFilePath(), \"utf-8\"));\n } catch {\n return \"\";\n }\n}\n\nexport function explicitSecret(env: NodeJS.ProcessEnv = process.env): string {\n return usable(env[SECRET_KEY]) || readEnvFileSecret();\n}\n\nexport function isLoopbackUrl(url: string): boolean {\n let hostname: string;\n try {\n hostname = new URL(url).hostname.toLowerCase();\n } catch {\n return false;\n }\n const bare = hostname.replace(/^\\[|\\]$/g, \"\");\n return bare === \"localhost\" || bare === \"::1\" || /^127(?:\\.\\d{1,3}){3}$/.test(bare);\n}\n\nexport function resolveClientSecret(\n baseUrl: string,\n env: NodeJS.ProcessEnv = process.env,\n): string {\n const fromEnv = usable(env[SECRET_KEY]);\n if (fromEnv) return fromEnv;\n if (!isLoopbackUrl(baseUrl)) return \"\";\n return readEnvFileSecret() || readStoredSecret();\n}\n\nexport function ensureServerSecret(env: NodeJS.ProcessEnv = process.env): {\n secret: string;\n source: \"env\" | \"file\" | \"generated\";\n} {\n const configured = explicitSecret(env);\n if (configured) return { secret: configured, source: \"env\" };\n const stored = readStoredSecret();\n if (stored) return { secret: stored, source: \"file\" };\n const generated = randomBytes(32).toString(\"hex\");\n const path = secretFilePath();\n mkdirSync(agentmemoryHomeDir(), { recursive: true, mode: 0o700 });\n const staging = `${path}.${process.pid}.${randomBytes(4).toString(\"hex\")}.tmp`;\n const fd = openSync(staging, \"wx\", 0o600);\n try {\n writeSync(fd, `${generated}\\n`);\n } finally {\n closeSync(fd);\n }\n try {\n chmodSync(staging, 0o600);\n linkSync(staging, path);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"EEXIST\") throw err;\n const raced = readStoredSecret();\n if (!raced) throw err;\n return { secret: raced, source: \"file\" };\n } finally {\n try {\n unlinkSync(staging);\n } catch {}\n }\n return { secret: generated, source: \"generated\" };\n}\n\nexport function bearerHeaders(\n baseUrl: string,\n env: NodeJS.ProcessEnv = process.env,\n): Record<string, string> {\n const secret = resolveClientSecret(baseUrl, env);\n return secret ? { Authorization: `Bearer ${secret}` } : {};\n}\n","import { execSync } from \"node:child_process\";\nimport { basename } from \"node:path\";\n\n// Resolution order: AGENTMEMORY_PROJECT_NAME env → git toplevel basename → cwd basename.\nexport function resolveProject(cwd?: string): string {\n const explicit = process.env[\"AGENTMEMORY_PROJECT_NAME\"];\n if (explicit && explicit.trim()) return explicit.trim();\n const dir = cwd && cwd.trim() ? cwd : process.cwd();\n try {\n const top = execSync(\"git rev-parse --show-toplevel\", {\n cwd: dir,\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n timeout: 500,\n })\n .toString()\n .trim();\n if (top) return basename(top);\n } catch {}\n return basename(dir);\n}\n\nexport function hookCwd(data: Record<string, unknown> | null | undefined): string | undefined {\n if (!data || typeof data !== \"object\") return undefined;\n if (typeof data.cwd === \"string\" && data.cwd.trim()) return data.cwd;\n const roots = data.workspace_roots;\n if (Array.isArray(roots)) {\n for (const root of roots) {\n if (typeof root === \"string\" && root.trim()) return root;\n }\n }\n const projectDir =\n process.env[\"DEVIN_PROJECT_DIR\"] || process.env[\"CLAUDE_PROJECT_DIR\"];\n if (projectDir && projectDir.trim()) return projectDir;\n return undefined;\n}\n","#!/usr/bin/env node\nimport { resolveClientSecret } from \"../secret-store.js\";\nimport { resolveProject, hookCwd } from \"./_project.js\";\n\n// Inlined from ./sdk-guard so each hook bundles to a single self-contained\n// .mjs (matches the pattern used by every other hook entry in tsdown.config).\nfunction isSdkChildContext(payload: unknown): boolean {\n if (process.env[\"AGENTMEMORY_SDK_CHILD\"] === \"1\") return true;\n if (!payload || typeof payload !== \"object\") return false;\n return (payload as { entrypoint?: unknown }).entrypoint === \"sdk-ts\";\n}\n\nconst INJECT_CONTEXT = process.env[\"AGENTMEMORY_INJECT_CONTEXT\"] === \"true\";\n\nconst REST_URL = process.env[\"AGENTMEMORY_URL\"] || \"http://localhost:3111\";\nconst SECRET = resolveClientSecret(REST_URL);\n\nconst INJECT_TIMEOUT_MS = 1500;\nconst REGISTER_TIMEOUT_MS = 800;\n\nfunction authHeaders(): Record<string, string> {\n const h: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (SECRET) h[\"Authorization\"] = `Bearer ${SECRET}`;\n return h;\n}\n\nfunction isPlainTextHost(): boolean {\n return Boolean(\n process.env[\"FACTORY_PROJECT_DIR\"] || process.env[\"DROID_PLUGIN_ROOT\"],\n );\n}\n\nfunction wantsStructuredOutput(data: Record<string, unknown>): boolean {\n if (process.env[\"DEVIN_PROJECT_DIR\"] || data.prompt_id !== undefined) {\n return true;\n }\n return data.hook_event_name === \"SessionStart\" && !isPlainTextHost();\n}\n\nfunction contextPayload(data: Record<string, unknown>, context: string): string {\n if (process.env[\"COPILOT_PLUGIN_ROOT\"] && !data.hook_event_name) {\n return JSON.stringify({ additionalContext: context });\n }\n if (\n typeof data.cursor_version === \"string\" ||\n data.hook_event_name === \"sessionStart\"\n ) {\n return JSON.stringify({ additional_context: context });\n }\n if (wantsStructuredOutput(data)) {\n return JSON.stringify({\n hookSpecificOutput: {\n hookEventName: \"SessionStart\",\n additionalContext: context,\n },\n });\n }\n return context;\n}\n\nasync function main() {\n let input = \"\";\n for await (const chunk of process.stdin) {\n input += chunk;\n }\n\n let data: Record<string, unknown>;\n try {\n data = JSON.parse(input);\n } catch {\n return;\n }\n\n if (!data || typeof data !== \"object\") return;\n if (isSdkChildContext(data)) return;\n\n const sessionId =\n ((data.session_id || data.sessionId || data.conversation_id) as string) ||\n `ses_${Date.now().toString(36)}`;\n const cwd = hookCwd(data) || process.cwd();\n const project = resolveProject(cwd);\n\n const url = `${REST_URL}/agentmemory/session/start`;\n const init: RequestInit = {\n method: \"POST\",\n headers: authHeaders(),\n body: JSON.stringify({ sessionId, project, cwd }),\n };\n\n if (!INJECT_CONTEXT) {\n // Pure telemetry path: caller never reads the response, so don't\n // block on it. AbortSignal.timeout caps the wait the event loop\n // gives the pending socket before exit.\n fetch(url, {\n ...init,\n signal: AbortSignal.timeout(REGISTER_TIMEOUT_MS),\n }).catch(() => {});\n return;\n }\n\n try {\n const res = await fetch(url, {\n ...init,\n signal: AbortSignal.timeout(INJECT_TIMEOUT_MS),\n });\n if (res.ok) {\n const result = (await res.json()) as { context?: string };\n if (result.context) {\n process.stdout.write(contextPayload(data, result.context));\n }\n }\n } catch {\n // silently fail -- don't block Claude Code startup\n }\n}\n\nmain().catch(() => process.exit(0));\n"],"mappings":"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"}
|