@pleri/olam-cli 0.1.206 → 0.1.208
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/dist/image-digests.json +8 -8
- package/dist/index.js +2907 -1932
- package/dist/index.js.map +1 -1
- package/dist/mcp-server.js +1292 -932
- package/hermes-bundle/version.json +1 -1
- package/host-cp/k8s/manifests/50-deployment.yaml +1 -1
- package/host-cp/k8s/manifests/auth-service/50-deployment.yaml +1 -1
- package/host-cp/k8s/manifests/kg-service/50-deployment.yaml +1 -1
- package/host-cp/k8s/manifests/mcp-auth-service/50-deployment.yaml +1 -1
- package/host-cp/k8s/manifests/memory-service/50-deployment.yaml +1 -1
- package/memory-hooks/agentmemory-classify-queue.mjs +4 -0
- package/memory-hooks/agentmemory-recall-trigger.mjs +4 -0
- package/memory-hooks/agentmemory-save.mjs +138 -0
- package/memory-hooks/agentmemory-session-recall.js +27 -0
- package/package.json +1 -1
|
@@ -118,7 +118,7 @@ spec:
|
|
|
118
118
|
# k3d), started by `olam upgrade` Step 0.7 — not inside this Pod.
|
|
119
119
|
containers:
|
|
120
120
|
- name: olam-host-cp
|
|
121
|
-
image: ghcr.io/pleri/olam-host-cp@sha256:
|
|
121
|
+
image: ghcr.io/pleri/olam-host-cp@sha256:ddeb5b82c7a1d72d78070b299ea2266683e71ff832c5b399fe25ba28f2285d6f
|
|
122
122
|
imagePullPolicy: IfNotPresent
|
|
123
123
|
securityContext:
|
|
124
124
|
runAsNonRoot: true
|
|
@@ -70,7 +70,7 @@ spec:
|
|
|
70
70
|
mountPath: /data
|
|
71
71
|
containers:
|
|
72
72
|
- name: olam-auth-service
|
|
73
|
-
image: ghcr.io/pleri/olam-auth@sha256:
|
|
73
|
+
image: ghcr.io/pleri/olam-auth@sha256:238ef8b58198684f84ea7c2bf91b23a3ac216660a40f8ef6b4db11c83e555b3a
|
|
74
74
|
imagePullPolicy: IfNotPresent
|
|
75
75
|
securityContext:
|
|
76
76
|
runAsNonRoot: true
|
|
@@ -61,7 +61,7 @@ spec:
|
|
|
61
61
|
mountPath: /data
|
|
62
62
|
containers:
|
|
63
63
|
- name: olam-kg-service
|
|
64
|
-
image: ghcr.io/pleri/olam-kg-service@sha256:
|
|
64
|
+
image: ghcr.io/pleri/olam-kg-service@sha256:8fa1ef59a27f9295b7f7375ccda8e1efa110c9a309603301c073076e919e35c3
|
|
65
65
|
imagePullPolicy: IfNotPresent
|
|
66
66
|
securityContext:
|
|
67
67
|
runAsNonRoot: true
|
|
@@ -68,7 +68,7 @@ spec:
|
|
|
68
68
|
mountPath: /data
|
|
69
69
|
containers:
|
|
70
70
|
- name: olam-mcp-auth-service
|
|
71
|
-
image: ghcr.io/pleri/olam-mcp-auth@sha256:
|
|
71
|
+
image: ghcr.io/pleri/olam-mcp-auth@sha256:56964905f2da39c19f462b5e5f5f84e74a930c9a70a4518463606ce91276272b
|
|
72
72
|
imagePullPolicy: IfNotPresent
|
|
73
73
|
securityContext:
|
|
74
74
|
runAsNonRoot: true
|
|
@@ -70,7 +70,7 @@ spec:
|
|
|
70
70
|
# bootstrap-placeholder comment + run `npm run refresh:manifest-digests`
|
|
71
71
|
# once ghcr.io/pleri/olam-memory-service has a real published digest.
|
|
72
72
|
# bootstrap-placeholder: pre-publish; refresh after first release
|
|
73
|
-
image: ghcr.io/pleri/olam-memory-service@sha256:
|
|
73
|
+
image: ghcr.io/pleri/olam-memory-service@sha256:581f763fcdc2e7e056c805883efa5e510ce460c6c2096445a909f35fcb38f04b
|
|
74
74
|
imagePullPolicy: IfNotPresent
|
|
75
75
|
securityContext:
|
|
76
76
|
runAsNonRoot: true
|
|
@@ -60,6 +60,7 @@ import { homedir } from 'node:os';
|
|
|
60
60
|
import { join } from 'node:path';
|
|
61
61
|
import { randomBytes } from 'node:crypto';
|
|
62
62
|
import { fileURLToPath } from 'node:url';
|
|
63
|
+
import { isAgentMemoryEnabled } from './agent-memory-gate.mjs';
|
|
63
64
|
|
|
64
65
|
/** Max bytes of captured text persisted per candidate. Keeps queue files small. */
|
|
65
66
|
export const MAX_CAPTURED_CHARS = 4000;
|
|
@@ -315,6 +316,9 @@ function readContentField(obj) {
|
|
|
315
316
|
}
|
|
316
317
|
|
|
317
318
|
async function main() {
|
|
319
|
+
// Gate: memory is off by default — exit silently when not enabled.
|
|
320
|
+
if (!isAgentMemoryEnabled()) return;
|
|
321
|
+
|
|
318
322
|
let event = {};
|
|
319
323
|
try {
|
|
320
324
|
event = JSON.parse(readFileSync(0, 'utf-8'));
|
|
@@ -33,6 +33,7 @@ import { existsSync, readFileSync, realpathSync } from 'node:fs';
|
|
|
33
33
|
import { homedir } from 'node:os';
|
|
34
34
|
import { join } from 'node:path';
|
|
35
35
|
import { fileURLToPath } from 'node:url';
|
|
36
|
+
import { isAgentMemoryEnabled } from './agent-memory-gate.mjs';
|
|
36
37
|
|
|
37
38
|
// Built-in tool-event filter: only fire on tool calls where recall has
|
|
38
39
|
// signal. Bash + Edit + MultiEdit + Read on substantive paths qualify;
|
|
@@ -190,6 +191,9 @@ export function formatAdditionalContext(response, prompt) {
|
|
|
190
191
|
}
|
|
191
192
|
|
|
192
193
|
async function main() {
|
|
194
|
+
// Gate: memory is off by default — exit silently when not enabled.
|
|
195
|
+
if (!isAgentMemoryEnabled()) return;
|
|
196
|
+
|
|
193
197
|
let event = {};
|
|
194
198
|
try {
|
|
195
199
|
event = JSON.parse(readFileSync(0, 'utf-8'));
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* agentmemory-save.mjs — PostToolUse hook: persist agent-written memory notes.
|
|
4
|
+
*
|
|
5
|
+
* AM_SENTINEL=olam-agent-memory-hook-v2
|
|
6
|
+
*
|
|
7
|
+
* This is the FILE-BASED port of the legacy inline python SAVE blob that used
|
|
8
|
+
* to live directly in ~/.claude/settings.json under
|
|
9
|
+
* `AM_SENTINEL=olam-agent-memory-hook-v1; python3 -c '…'`. The installer
|
|
10
|
+
* (`olam hooks install-all`, invoked by `olam skills sync`) copies this file to
|
|
11
|
+
* ~/.claude/scripts/hooks/ and registers a settings.json entry pointing at it,
|
|
12
|
+
* REPLACING the inline blob. Keeping the logic in a real source file means it
|
|
13
|
+
* is version-controlled, testable, bundled into @pleri/olam-cli, and owned by
|
|
14
|
+
* sync — not stranded as a string in the operator's settings.
|
|
15
|
+
*
|
|
16
|
+
* Behaviour (byte-faithful port of the v1 blob):
|
|
17
|
+
* - Reads the PostToolUse JSON payload from stdin.
|
|
18
|
+
* - Acts ONLY when tool_name === "Write" AND the written file_path matches
|
|
19
|
+
* /\/\.claude\/projects\/.*\/memory\/.*\.md$/ ; otherwise bails (exit 0).
|
|
20
|
+
* - Requires OLAM_AGENT_MEMORY_BEARER (trimmed, non-empty); else bails.
|
|
21
|
+
* - Parses YAML-ish frontmatter delimited by leading "---":
|
|
22
|
+
* description: → title (default "memory") ← keyed on description, per v1
|
|
23
|
+
* type: → type (default "fact")
|
|
24
|
+
* remainder → body
|
|
25
|
+
* - POSTs {title, content: body, type} to
|
|
26
|
+
* <OLAM_AGENT_MEMORY_URL|default>/agentmemory/remember
|
|
27
|
+
* with Authorization: Bearer <bearer>, ~3s timeout.
|
|
28
|
+
* - On success prints to STDERR the green status line:
|
|
29
|
+
* [🧠⇡ Memory saved] "<title first 60 chars>"
|
|
30
|
+
* - Fail-open: ANY error → silent, exit 0. Never throws to the caller.
|
|
31
|
+
*
|
|
32
|
+
* Node built-ins only (global fetch + AbortController, available on Node ≥18).
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
import { readFileSync } from 'node:fs';
|
|
36
|
+
|
|
37
|
+
const DEFAULT_URL = 'https://atlas-agent-memory.atlas-kitchen.workers.dev';
|
|
38
|
+
const MEMORY_PATH_RE = /\/\.claude\/projects\/.*\/memory\/.*\.md$/;
|
|
39
|
+
const TIMEOUT_MS = 3000;
|
|
40
|
+
|
|
41
|
+
/** Read all of stdin as a UTF-8 string. */
|
|
42
|
+
function readStdin() {
|
|
43
|
+
try {
|
|
44
|
+
return readFileSync(0, 'utf8');
|
|
45
|
+
} catch {
|
|
46
|
+
return '';
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Parse leading "---"-delimited frontmatter. Returns {title, type, body}.
|
|
52
|
+
* Mirrors the v1 blob: title comes from `description:`, type from `type:`.
|
|
53
|
+
*/
|
|
54
|
+
function parseNote(raw) {
|
|
55
|
+
let title = 'memory';
|
|
56
|
+
let type = 'fact';
|
|
57
|
+
let body = raw;
|
|
58
|
+
if (raw.startsWith('---')) {
|
|
59
|
+
// Split into [before-first-delim, frontmatter, …rest]. JS's limited split
|
|
60
|
+
// truncates the remainder, so slice manually to keep the full body.
|
|
61
|
+
const firstDelimEnd = 3; // length of leading "---"
|
|
62
|
+
const secondDelim = raw.indexOf('---', firstDelimEnd);
|
|
63
|
+
if (secondDelim !== -1) {
|
|
64
|
+
const fm = raw.slice(firstDelimEnd, secondDelim);
|
|
65
|
+
body = raw.slice(secondDelim + 3).trim();
|
|
66
|
+
for (const ln of fm.split('\n')) {
|
|
67
|
+
const s = ln.trim();
|
|
68
|
+
if (s.startsWith('description:')) {
|
|
69
|
+
title = stripQuotes(s.slice(s.indexOf(':') + 1).trim());
|
|
70
|
+
} else if (s.startsWith('type:')) {
|
|
71
|
+
type = stripQuotes(s.slice(s.indexOf(':') + 1).trim());
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return { title, type, body };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function stripQuotes(v) {
|
|
80
|
+
return v.replace(/^["']/, '').replace(/["']$/, '');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async function main() {
|
|
84
|
+
const payloadRaw = readStdin();
|
|
85
|
+
let payload;
|
|
86
|
+
try {
|
|
87
|
+
payload = JSON.parse(payloadRaw);
|
|
88
|
+
} catch {
|
|
89
|
+
return; // not parseable → fail-open
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const toolName = payload?.tool_name ?? '';
|
|
93
|
+
const filePath = payload?.tool_input?.file_path ?? '';
|
|
94
|
+
if (toolName !== 'Write' || !MEMORY_PATH_RE.test(filePath)) return;
|
|
95
|
+
|
|
96
|
+
const bearer = (process.env['OLAM_AGENT_MEMORY_BEARER'] ?? '').trim();
|
|
97
|
+
if (!bearer) return;
|
|
98
|
+
|
|
99
|
+
let raw;
|
|
100
|
+
try {
|
|
101
|
+
raw = readFileSync(filePath, 'utf8');
|
|
102
|
+
} catch {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const { title, type, body } = parseNote(raw);
|
|
107
|
+
const base = (process.env['OLAM_AGENT_MEMORY_URL'] ?? DEFAULT_URL).replace(/\/$/, '');
|
|
108
|
+
const url = `${base}/agentmemory/remember`;
|
|
109
|
+
|
|
110
|
+
const controller = new AbortController();
|
|
111
|
+
const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
|
|
112
|
+
try {
|
|
113
|
+
const resp = await fetch(url, {
|
|
114
|
+
method: 'POST',
|
|
115
|
+
headers: {
|
|
116
|
+
'Content-Type': 'application/json',
|
|
117
|
+
Authorization: `Bearer ${bearer}`,
|
|
118
|
+
'User-Agent': 'olam-agent-memory-hook/2',
|
|
119
|
+
},
|
|
120
|
+
body: JSON.stringify({ title, content: body, type }),
|
|
121
|
+
signal: controller.signal,
|
|
122
|
+
});
|
|
123
|
+
// Treat any non-throwing response as delivered (the v1 blob did the same —
|
|
124
|
+
// it printed on a non-exception urlopen regardless of status code).
|
|
125
|
+
if (resp) {
|
|
126
|
+
const label = title.slice(0, 60);
|
|
127
|
+
process.stderr.write(`\x1b[32m[\u{1F9E0}\u{21E1} Memory saved]\x1b[0m "${label}"\n`);
|
|
128
|
+
}
|
|
129
|
+
} catch {
|
|
130
|
+
// Fail-open: network error / timeout / abort → silent.
|
|
131
|
+
} finally {
|
|
132
|
+
clearTimeout(timer);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
main().catch(() => {
|
|
137
|
+
/* fail-open: never propagate to the caller */
|
|
138
|
+
});
|
|
@@ -40,6 +40,30 @@ const fs = require('fs');
|
|
|
40
40
|
const os = require('os');
|
|
41
41
|
const path = require('path');
|
|
42
42
|
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
// Opt-in gate — inline re-implementation of agent-memory-gate.mjs contract
|
|
45
|
+
// (CJS cannot import ESM at the top level; inline ensures same fail-closed
|
|
46
|
+
// semantics without a dynamic import or a build step).
|
|
47
|
+
// ---------------------------------------------------------------------------
|
|
48
|
+
const _GATE_TRUTHY_ENV = new Set(['1', 'true', 'on']);
|
|
49
|
+
const _GATE_TRUTHY_FILE = new Set(['true', '1']);
|
|
50
|
+
function _isAgentMemoryEnabled() {
|
|
51
|
+
const envVal = process.env['OLAM_AGENT_MEMORY'];
|
|
52
|
+
if (envVal !== undefined && envVal.length > 0) {
|
|
53
|
+
return _GATE_TRUTHY_ENV.has(envVal.trim().toLowerCase());
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
const fromEnv = process.env['OLAM_HOME'];
|
|
57
|
+
const olamHome = (fromEnv && fromEnv.length > 0) ? fromEnv : path.join(os.homedir(), '.olam');
|
|
58
|
+
const flagPath = path.join(olamHome, 'agent-memory-enabled');
|
|
59
|
+
const raw = fs.readFileSync(flagPath, 'utf8').trim().toLowerCase();
|
|
60
|
+
return _GATE_TRUTHY_FILE.has(raw);
|
|
61
|
+
} catch {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
|
|
43
67
|
// Resolve the bridge URL: operator env wins, then the `olam memory connect`
|
|
44
68
|
// artifact (~/.olam/memory-connection.json), then the LIVE memory-service
|
|
45
69
|
// Worker. The prior default (olam-agent-memory.ernestcodes.workers.dev) is a
|
|
@@ -276,6 +300,9 @@ async function recall(query, limit = 5) {
|
|
|
276
300
|
}
|
|
277
301
|
|
|
278
302
|
async function main() {
|
|
303
|
+
// Gate: memory is off by default — exit silently when not enabled.
|
|
304
|
+
if (!_isAgentMemoryEnabled()) return;
|
|
305
|
+
|
|
279
306
|
const raw = require('fs').readFileSync(0, 'utf8');
|
|
280
307
|
let event = {};
|
|
281
308
|
try { event = JSON.parse(raw); } catch (_) {}
|