@pleri/olam-cli 0.1.205 → 0.1.207

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.
@@ -1,4 +1,4 @@
1
1
  {
2
- "bundledAt": "2026-06-02T08:06:35.843Z",
2
+ "bundledAt": "2026-06-02T16:46:23.219Z",
3
3
  "kgFirstSha": "29a9ccce1b115d049e375c4a90eb5cf7c123e610e2d0590270a4db2cdbc64a28"
4
4
  }
@@ -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:501037587e33d0a1c13df88d72ad9b855508e4c0163cb65673211cd3ddd9227b
121
+ image: ghcr.io/pleri/olam-host-cp@sha256:0d1869ed3c823e6ea1b5d885d2f73d7418134c1b82c9dd70719044fd04d74027
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:06c9cd39405e650141a78927f1701bf7b06a86cd98eeaef9b22eb53d46f6f523
73
+ image: ghcr.io/pleri/olam-auth@sha256:9f8195a20727ec844d7393696c298b42e3e83e841fe684787ee9c6d1e301b5e8
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:a739f0fdf60aef0d26b51314698a07bba423c3d43571616cffd7b1195abb205d
64
+ image: ghcr.io/pleri/olam-kg-service@sha256:0b1f78a566675509f371e4e0636a76859f800c1bec787f06742e3a4f116d7c90
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:cdeb8813619f0198dbbcc80b2c98dbeb3a842e8d1ec38796d459ad254c5e1d98
71
+ image: ghcr.io/pleri/olam-mcp-auth@sha256:63ecc6bdd5c3def600a78046f0e7ab36938e88bc6fc7d31a446d7cec84a27a3a
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:b10649ba9b56bb11b99e5142559d4b8a37a2b8ae2ad8ce38fb8b535449d26f85
73
+ image: ghcr.io/pleri/olam-memory-service@sha256:673156cc638fc9af096e73fc7cd7c666373f92e63e566a57f51271b691aa9d74
74
74
  imagePullPolicy: IfNotPresent
75
75
  securityContext:
76
76
  runAsNonRoot: true
@@ -2241,6 +2241,35 @@ const server = http.createServer(instrumentHandler('host-cp', async (req, res) =
2241
2241
  ...(burstKgWorkspace ? { kgWorkspace: burstKgWorkspace } : {}),
2242
2242
  ...(burstHostCpUrl ? { hostCpUrl: burstHostCpUrl } : {}),
2243
2243
  ...(burstChunksSinkUrl ? { chunksSinkUrl: burstChunksSinkUrl } : {}),
2244
+ // attachments → runner materializes operator-pasted file/image bytes
2245
+ // from R2 into /workspace/<sid>/.attachments (plan-chat-spa-attachments
2246
+ // Phase A, #1614). The runner reads body.attachments; sandbox-dispatch-client
2247
+ // forwards it on the plan-DO path, so cloud-burst must too — else a
2248
+ // sandbox dispatch carrying attachments silently runs without the files
2249
+ // (drift parity with sandbox-dispatch-client — same class as #1605/#1622).
2250
+ ...(Array.isArray(parsedBody.attachments) && parsedBody.attachments.length > 0 ? { attachments: parsedBody.attachments } : {}),
2251
+ // Agent-capability fields the runner /spawn handler reads and
2252
+ // sandbox-dispatch-client (cell #4) already forwards. The cloud-burst
2253
+ // path (cell #2) builds an explicit spawnBody, so it must forward them
2254
+ // too or a local-mode→sandbox dispatch carrying any of these silently
2255
+ // loses the capability (same drift class as submodules/attachments).
2256
+ // Body-supplied (the cell #2 SPA send is the only source on this path);
2257
+ // absent → spread to nothing, so today's bare {prompt,session_id,world_id}
2258
+ // burst is byte-for-byte unchanged.
2259
+ // skillSources → runner lays the operator's /100x chain into ~/.claude.
2260
+ // mcpServers → runner writes ~/.claude.json so the agent has MCP tools.
2261
+ // interactive → runner launches a tmux REPL instead of headless -p.
2262
+ // operatorSub → in-container poster stamps chunks with the owner sub
2263
+ // (runner reads body.operatorSub ?? body.operator_sub;
2264
+ // forwarding the camelCase alias satisfies both reads —
2265
+ // operator_sub is a documented EXCLUSIONS alias).
2266
+ // chunksSinkBearer → per-dispatch chunk-sink auth (NEVER logged); runner
2267
+ // prefers it over its deploy-time env.CHUNKS_INGEST_BEARER.
2268
+ ...(Array.isArray(parsedBody.skillSources) && parsedBody.skillSources.length > 0 ? { skillSources: parsedBody.skillSources } : {}),
2269
+ ...(parsedBody.mcpServers && typeof parsedBody.mcpServers === 'object' && Object.keys(parsedBody.mcpServers).length > 0 ? { mcpServers: parsedBody.mcpServers } : {}),
2270
+ ...(parsedBody.interactive ? { interactive: true } : {}),
2271
+ ...(parsedBody.operatorSub ? { operatorSub: parsedBody.operatorSub } : {}),
2272
+ ...(parsedBody.chunksSinkBearer ? { chunksSinkBearer: parsedBody.chunksSinkBearer } : {}),
2244
2273
  idempotencyKey,
2245
2274
  ...safeOptions,
2246
2275
  };
@@ -4064,7 +4093,7 @@ const _spaCacheByKey = new Map();
4064
4093
  // audit FAIL. Keep it as the canonical HN/WP-array parity source until
4065
4094
  // that audit is repointed at worldFetch.ts directly (follow-up).
4066
4095
  // eslint-disable-next-line no-unused-vars -- retained as HN/WP parity-audit source
4067
- const BOOTSTRAP_SCRIPT = `<script>(function(){function ck(){var m=document.cookie.match(/olam_host_cp_token=([^;]+)/);return m?m[1]:'';}function sw(t){document.cookie='olam_host_cp_token='+t+'; path=/; samesite=strict';}try{var x=new XMLHttpRequest();x.open('GET','/api/bootstrap',false);x.send();if(x.status===200){var d=JSON.parse(x.responseText);sw(d.token);}}catch(e){console.error('[host-cp bootstrap]',e);}var reloading=false;function recover(){if(reloading)return;try{var x=new XMLHttpRequest();x.open('GET','/api/bootstrap',false);x.send();if(x.status===200){var d=JSON.parse(x.responseText);if(d.token&&ck()!==d.token){reloading=true;sw(d.token);console.warn('[host-cp auth recover] token rotated; reloading');location.reload();}}}catch(e){console.error('[host-cp auth recover]',e);}}var HN=['/api/bootstrap','/api/worlds','/api/projects','/api/workspaces','/api/workspaces/match','/api/repos','/api/runbooks','/api/auth','/api/host-stream','/api/plan-chat','/api/plan/agent-runtime','/health'];var WP=['/api/','/session/','/hooks/','/dispatch','/lanes','/codex/','/review/'];function sr(p){if(typeof p!=='string')return false;if(p.startsWith('/api/world/'))return false;for(var i=0;i<HN.length;i++){var n=HN[i];if(p===n||p.startsWith(n+'?')||p.startsWith(n+'/'))return false;}for(var j=0;j<WP.length;j++){var w=WP[j];if(p===w||p===w.replace(/\\/$/,'')||p.startsWith(w)||p.startsWith(w.replace(/\\/$/,'')+'?')||p.startsWith(w.replace(/\\/$/,'')+'/'))return true;}return false;}function wid(){var p=location.pathname;var m=p.match(/^\\/(world|inbox|session)\\/([^/?#]+)/);if(m)return m[2];if(/^\\/(?:worlds?|workspaces?|world|sandbox|session|inbox|plan|design|repos|runbooks|assets|api|health|favicon)($|\\/|\\?)/.test(p))return null;var r=p.match(/^\\/([a-z][a-z0-9-]+)(?:\\/|$|\\?)/);return r?r[1]:null;}function rw(p){var w=wid();return w?'/api/world/'+w+p:p;}var of=window.fetch.bind(window);window.fetch=function(input,init){var pr;if(typeof input==='string'&&sr(input))pr=of(rw(input),init);else if(input&&typeof input.url==='string'&&sr(input.url))pr=of(new Request(rw(input.url),input),init);else pr=of(input,init);return pr.then(function(res){if(res&&res.status===401)recover();return res;});};var OE=window.EventSource;if(OE){window.EventSource=function(u,i){var s=u;if(typeof s==='string'&&sr(s))s=rw(s);var es=new OE(s,i);es.addEventListener('error',function(){if(es.readyState===OE.CLOSED)recover();});return es;};window.EventSource.prototype=OE.prototype;window.EventSource.CONNECTING=OE.CONNECTING;window.EventSource.OPEN=OE.OPEN;window.EventSource.CLOSED=OE.CLOSED;}})();</script>`;
4096
+ const BOOTSTRAP_SCRIPT = `<script>(function(){function ck(){var m=document.cookie.match(/olam_host_cp_token=([^;]+)/);return m?m[1]:'';}function sw(t){document.cookie='olam_host_cp_token='+t+'; path=/; samesite=strict';}try{var x=new XMLHttpRequest();x.open('GET','/api/bootstrap',false);x.send();if(x.status===200){var d=JSON.parse(x.responseText);sw(d.token);}}catch(e){console.error('[host-cp bootstrap]',e);}var reloading=false;function recover(){if(reloading)return;try{var x=new XMLHttpRequest();x.open('GET','/api/bootstrap',false);x.send();if(x.status===200){var d=JSON.parse(x.responseText);if(d.token&&ck()!==d.token){reloading=true;sw(d.token);console.warn('[host-cp auth recover] token rotated; reloading');location.reload();}}}catch(e){console.error('[host-cp auth recover]',e);}}var HN=['/api/bootstrap','/api/whoami','/api/worlds','/api/projects','/api/chats','/api/workspaces','/api/workspaces/match','/api/repos','/api/runbooks','/api/auth','/api/host-stream','/api/plan-chat','/api/plan/agent-runtime','/health'];var WP=['/api/','/session/','/hooks/','/dispatch','/lanes','/codex/','/review/'];function sr(p){if(typeof p!=='string')return false;if(p.startsWith('/api/world/'))return false;for(var i=0;i<HN.length;i++){var n=HN[i];if(p===n||p.startsWith(n+'?')||p.startsWith(n+'/'))return false;}for(var j=0;j<WP.length;j++){var w=WP[j];if(p===w||p===w.replace(/\\/$/,'')||p.startsWith(w)||p.startsWith(w.replace(/\\/$/,'')+'?')||p.startsWith(w.replace(/\\/$/,'')+'/'))return true;}return false;}function wid(){var p=location.pathname;var m=p.match(/^\\/(world|inbox|session)\\/([^/?#]+)/);if(m)return m[2];if(/^\\/(?:worlds?|workspaces?|world|sandbox|session|inbox|plan|design|repos|runbooks|assets|api|health|favicon)($|\\/|\\?)/.test(p))return null;var r=p.match(/^\\/([a-z][a-z0-9-]+)(?:\\/|$|\\?)/);return r?r[1]:null;}function rw(p){var w=wid();return w?'/api/world/'+w+p:p;}var of=window.fetch.bind(window);window.fetch=function(input,init){var pr;if(typeof input==='string'&&sr(input))pr=of(rw(input),init);else if(input&&typeof input.url==='string'&&sr(input.url))pr=of(new Request(rw(input.url),input),init);else pr=of(input,init);return pr.then(function(res){if(res&&res.status===401)recover();return res;});};var OE=window.EventSource;if(OE){window.EventSource=function(u,i){var s=u;if(typeof s==='string'&&sr(s))s=rw(s);var es=new OE(s,i);es.addEventListener('error',function(){if(es.readyState===OE.CLOSED)recover();});return es;};window.EventSource.prototype=OE.prototype;window.EventSource.CONNECTING=OE.CONNECTING;window.EventSource.OPEN=OE.OPEN;window.EventSource.CLOSED=OE.CLOSED;}})();</script>`;
4068
4097
 
4069
4098
  /**
4070
4099
  * Build the plan-chat bearer injection script. Reads
@@ -204,6 +204,11 @@ async function main() {
204
204
  const additionalContext = formatAdditionalContext(response, prompt);
205
205
  if (!additionalContext) return;
206
206
 
207
+ const count = response?.results?.length ?? 0;
208
+ process.stderr.write(
209
+ `\x1b[34m[🧠⇣ Memory recalled]\x1b[0m (${count} memories · "${prompt.slice(0, 60)}${prompt.length > 60 ? '…' : ''}")\n`
210
+ );
211
+
207
212
  process.stdout.write(JSON.stringify({
208
213
  hookSpecificOutput: {
209
214
  hookEventName: 'PreToolUse',
@@ -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
+ });
@@ -321,6 +321,10 @@ async function main() {
321
321
  const additionalContext =
322
322
  `## Recalled from agent memory (query: "${query}")\n\n${lines}\n\n${nudge}`;
323
323
 
324
+ process.stderr.write(
325
+ `\x1b[34m[🧠⇣ Memory recalled]\x1b[0m (${result.results.length} memories · "${query}")\n`
326
+ );
327
+
324
328
  process.stdout.write(JSON.stringify({
325
329
  hookSpecificOutput: {
326
330
  hookEventName: 'SessionStart',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pleri/olam-cli",
3
- "version": "0.1.205",
3
+ "version": "0.1.207",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "olam": "./bin/olam.cjs"