@granica/myelin-beam-client 0.1.0-alpha.0

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.
@@ -0,0 +1,319 @@
1
+ import { promises as fs } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { createHash } from "node:crypto";
5
+ /// Encode a cwd the way Claude Code does. Absolute path only; relative
6
+ /// or `~`-prefixed paths won't match a real project directory.
7
+ export function encodeCwdForClaude(cwd) {
8
+ // Claude replaces every `/` with `-`, keeping the leading `/`'s dash.
9
+ // We do the same, then strip control chars for safety.
10
+ return cwd.replace(/\//g, "-").replace(/[\r\n\t]/g, "");
11
+ }
12
+ /// Locate the newest-mtime rollout under the current cwd. Returns null
13
+ /// when the directory doesn't exist (fresh cwd, no prior session) or
14
+ /// when no jsonl files match.
15
+ export async function findCurrentClaudeRollout(cwd) {
16
+ const projectCwd = cwd || process.cwd();
17
+ const encoded = encodeCwdForClaude(projectCwd);
18
+ const dir = join(homedir(), ".claude", "projects", encoded);
19
+ let entries;
20
+ try {
21
+ entries = await fs.readdir(dir);
22
+ }
23
+ catch {
24
+ return null;
25
+ }
26
+ const jsonls = entries.filter((e) => e.endsWith(".jsonl"));
27
+ if (jsonls.length === 0)
28
+ return null;
29
+ let newest = null;
30
+ for (const name of jsonls) {
31
+ const path = join(dir, name);
32
+ try {
33
+ const st = await fs.stat(path);
34
+ const mtime = st.mtimeMs;
35
+ if (!newest || mtime > newest.mtime) {
36
+ newest = { path, mtime, name };
37
+ }
38
+ }
39
+ catch {
40
+ // Ignore unreadable entries.
41
+ }
42
+ }
43
+ if (!newest)
44
+ return null;
45
+ return {
46
+ agent: "claude",
47
+ path: newest.path,
48
+ session_uuid: newest.name.replace(/\.jsonl$/, ""),
49
+ project_cwd: projectCwd,
50
+ };
51
+ }
52
+ /// Locate the current Codex rollout under `~/.codex/sessions/`.
53
+ ///
54
+ /// Codex layout: `~/.codex/sessions/<YYYY>/<MM>/<DD>/rollout-<ts>-<uuid>.jsonl`.
55
+ /// Unlike Claude, the tree is date-partitioned rather than cwd-encoded;
56
+ /// each rollout embeds its own `cwd` inside a session_meta header:
57
+ /// {"type":"session_meta","payload":{"id":"<uuid>","cwd":"<abs-path>",...}}
58
+ ///
59
+ /// Strategy: pick the newest rollout by mtime. Header cwd is used to
60
+ /// populate project_cwd (so the router gets the user's ACTUAL cwd, not
61
+ /// the MCP subprocess's spawned cwd — which may differ). We do NOT
62
+ /// filter by matching process.cwd() to the header cwd — Codex has been
63
+ /// observed to spawn MCP subprocesses with a different working directory
64
+ /// than the user's session cwd, and a strict match would silently drop
65
+ /// the intended rollout. The docs already flag "concurrent sessions →
66
+ /// newest wins" as the ambiguity the user has to manage.
67
+ export async function findCurrentCodexRollout(cwd) {
68
+ const root = join(homedir(), ".codex", "sessions");
69
+ const files = await walkJsonlFiles(root, 4);
70
+ if (files.length === 0)
71
+ return null;
72
+ // Prefer newest mtime globally. Bounded walk (year/month/day/file).
73
+ let best = null;
74
+ for (const path of files) {
75
+ if (!/rollout-.*\.jsonl$/.test(path))
76
+ continue;
77
+ let st;
78
+ try {
79
+ st = await fs.stat(path);
80
+ }
81
+ catch {
82
+ continue;
83
+ }
84
+ if (!best || st.mtimeMs > best.mtime) {
85
+ best = { path, mtime: st.mtimeMs };
86
+ }
87
+ }
88
+ if (!best)
89
+ return null;
90
+ const header = await readFirstJsonlLine(best.path);
91
+ const parsed = header ? safeParseHeader(header) : null;
92
+ if (!parsed)
93
+ return null;
94
+ // If the caller passed an explicit cwd, honor it (test hook).
95
+ // Otherwise use the rollout's own recorded cwd — that's the path
96
+ // the user actually cd'd into and it must be what the router
97
+ // receives so agent-launcher.sh's workspace slug resolves correctly.
98
+ const projectCwd = cwd || parsed.cwd;
99
+ return {
100
+ agent: "codex",
101
+ path: best.path,
102
+ session_uuid: parsed.id,
103
+ project_cwd: projectCwd,
104
+ };
105
+ }
106
+ async function walkJsonlFiles(root, maxDepth) {
107
+ const out = [];
108
+ async function visit(dir, depth) {
109
+ if (depth > maxDepth)
110
+ return;
111
+ let entries;
112
+ try {
113
+ entries = await fs.readdir(dir, { withFileTypes: true });
114
+ }
115
+ catch {
116
+ return;
117
+ }
118
+ for (const e of entries) {
119
+ const full = join(dir, e.name);
120
+ if (e.isDirectory()) {
121
+ await visit(full, depth + 1);
122
+ }
123
+ else if (e.isFile() && e.name.endsWith(".jsonl")) {
124
+ out.push(full);
125
+ }
126
+ }
127
+ }
128
+ await visit(root, 0);
129
+ return out;
130
+ }
131
+ async function readFirstJsonlLine(path) {
132
+ // Codex session_meta headers embed the full system prompt in
133
+ // payload.base_instructions.text (observed at 8+ KiB, 2026-07).
134
+ // A single-shot 8 KiB read truncated the JSON and the whole locator
135
+ // silently returned null. Stream chunks until we hit a newline;
136
+ // cap at 1 MiB defensively so a pathological file (missing newline)
137
+ // doesn't OOM us.
138
+ const CHUNK = 65536;
139
+ const MAX = 1024 * 1024;
140
+ let handle;
141
+ try {
142
+ handle = await fs.open(path, "r");
143
+ let offset = 0;
144
+ let acc = "";
145
+ while (offset < MAX) {
146
+ const buf = Buffer.alloc(CHUNK);
147
+ const { bytesRead } = await handle.read(buf, 0, buf.length, offset);
148
+ if (bytesRead === 0)
149
+ break;
150
+ const text = buf.slice(0, bytesRead).toString("utf8");
151
+ const nl = text.indexOf("\n");
152
+ if (nl >= 0)
153
+ return acc + text.slice(0, nl);
154
+ acc += text;
155
+ offset += bytesRead;
156
+ }
157
+ // Hit the cap without seeing a newline — treat as unparseable.
158
+ return null;
159
+ }
160
+ catch {
161
+ return null;
162
+ }
163
+ finally {
164
+ await handle?.close().catch(() => { });
165
+ }
166
+ }
167
+ function safeParseHeader(line) {
168
+ try {
169
+ const obj = JSON.parse(line);
170
+ // Codex writes session_meta as the first line. Some builds nest
171
+ // under `payload`, others put fields at the top level — accept
172
+ // both shapes rather than pinning to one version.
173
+ const payload = obj?.payload ?? obj;
174
+ const id = typeof payload?.id === "string" ? payload.id :
175
+ typeof payload?.session_id === "string" ? payload.session_id :
176
+ null;
177
+ const cwd = typeof payload?.cwd === "string" ? payload.cwd : null;
178
+ if (!id || !cwd)
179
+ return null;
180
+ return { id, cwd };
181
+ }
182
+ catch {
183
+ return null;
184
+ }
185
+ }
186
+ /// Read the rollout bytes for upload. Returns raw bytes; caller PUTs to
187
+ /// the transcript signed URL as-is (no re-encoding).
188
+ export async function readRolloutBytes(hit) {
189
+ return fs.readFile(hit.path);
190
+ }
191
+ /// Drop any incomplete trailing turn from a rollout before uploading.
192
+ ///
193
+ /// Beam-up-from-local runs INSIDE a turn: the last event the local agent
194
+ /// wrote is the `beam_up` tool call itself, and the matching tool result
195
+ /// hasn't happened yet (it can't — `beam_up` returning IS the upload
196
+ /// finishing). Uploading the raw rollout leaves a dangling tool call in
197
+ /// the beam's transcript, which Codex renders as "Conversation
198
+ /// interrupted" and Claude silently drops.
199
+ ///
200
+ /// Trim to the last COMPLETE turn — the between-turn snapshot state
201
+ /// that fork already benefits from naturally (fork happens on a button
202
+ /// click, always between turns).
203
+ ///
204
+ /// Conservative: on any parse trouble, returns the input unchanged.
205
+ /// The only failure mode is "we uploaded slightly too much" which is
206
+ /// the same UX as before this fix — never worse.
207
+ export function trimIncompleteTail(bytes, agentKind) {
208
+ const text = bytes.toString("utf8");
209
+ const trimmed = agentKind === "codex" ? trimCodexTail(text) : trimClaudeTail(text);
210
+ return Buffer.from(trimmed, "utf8");
211
+ }
212
+ /// Codex: trim to the last `event_msg / task_complete` marker.
213
+ ///
214
+ /// Codex emits an explicit `task_complete` event when a turn (user
215
+ /// message + assistant reply + any tool_calls) is done. Anything after
216
+ /// the last `task_complete` is by definition mid-turn — including
217
+ /// dangling function_calls, in-flight assistant messages, or pending
218
+ /// tool_call_output events. Truncating at the last `task_complete`
219
+ /// gives a guaranteed clean state that codex's `resume --last` renders
220
+ /// without the "Conversation interrupted" banner.
221
+ ///
222
+ /// Prior version of this trim only dropped dangling function_calls
223
+ /// (call_id with no matching function_call_output). That still left
224
+ /// the transcript ending on a `token_count` or `mcp_tool_call_end`
225
+ /// event inside the same turn, so codex still saw an open turn and
226
+ /// showed the banner. This tighter boundary is unambiguous.
227
+ ///
228
+ /// Trade-off (approved by design): the last user turn — the one that
229
+ /// contained the `beam_up` tool_call — is dropped from the resumed
230
+ /// session. Users see prior completed turns cleanly; the beam-up
231
+ /// action itself doesn't render in the beam (they already see the
232
+ /// URL locally, so no user-facing loss).
233
+ ///
234
+ /// If no `task_complete` exists at all (fresh session with no
235
+ /// completed turns yet), returns just the session_meta header so the
236
+ /// pod's `codex resume --last` still finds a syntactically-valid
237
+ /// rollout to pick up as "recent session" — the transcript will be
238
+ /// empty but the sentinel + agent boot won't error.
239
+ function trimCodexTail(text) {
240
+ const lines = text.split("\n");
241
+ let lastTaskCompleteLine = -1;
242
+ for (let i = 0; i < lines.length; i++) {
243
+ const line = lines[i];
244
+ if (!line)
245
+ continue;
246
+ let obj;
247
+ try {
248
+ obj = JSON.parse(line);
249
+ }
250
+ catch {
251
+ continue;
252
+ }
253
+ const p = obj?.payload;
254
+ if (p?.type === "task_complete") {
255
+ lastTaskCompleteLine = i;
256
+ }
257
+ }
258
+ if (lastTaskCompleteLine === -1) {
259
+ // No completed turn on record. Keep only the session_meta
260
+ // header (line 0) so the file remains a valid codex rollout.
261
+ // Everything past that is by definition mid-turn.
262
+ const headerLine = lines.find((l) => l.length > 0);
263
+ return headerLine ? headerLine + "\n" : "";
264
+ }
265
+ const kept = lines.slice(0, lastTaskCompleteLine + 1);
266
+ return kept.join("\n") + "\n";
267
+ }
268
+ /// Claude: trim to the last `assistant` message with `stop_reason:
269
+ /// "end_turn"` — Claude's explicit turn-closed marker.
270
+ ///
271
+ /// Symmetric to codex's task_complete boundary. `end_turn` is the
272
+ /// only stop_reason that means "the assistant fully responded and
273
+ /// isn't waiting on anything." Other values (`tool_use`,
274
+ /// `max_tokens`, `stop_sequence`, `pause_turn`) all leave the turn
275
+ /// mid-flight from the resume perspective — the model is either
276
+ /// awaiting a tool result or was cut off before finishing.
277
+ ///
278
+ /// Same trade-off as codex: the user's mid-turn "beam this up"
279
+ /// question gets dropped from the resumed session. Users see the
280
+ /// beam URL locally so no user-facing loss; the resumed session
281
+ /// ends on a coherent last assistant reply.
282
+ ///
283
+ /// If no `end_turn` line exists, returns empty — Claude Code
284
+ /// tolerates an empty transcript and fresh-boots the session.
285
+ function trimClaudeTail(text) {
286
+ const lines = text.split("\n");
287
+ let lastEndTurnLine = -1;
288
+ for (let i = 0; i < lines.length; i++) {
289
+ const line = lines[i];
290
+ if (!line)
291
+ continue;
292
+ let obj;
293
+ try {
294
+ obj = JSON.parse(line);
295
+ }
296
+ catch {
297
+ continue;
298
+ }
299
+ if (obj?.type === "assistant" &&
300
+ obj?.message?.stop_reason === "end_turn") {
301
+ lastEndTurnLine = i;
302
+ }
303
+ }
304
+ if (lastEndTurnLine === -1) {
305
+ return "";
306
+ }
307
+ const kept = lines.slice(0, lastEndTurnLine + 1);
308
+ return kept.join("\n") + "\n";
309
+ }
310
+ /// SHA-256 hash of a file's bytes. Used for harness manifest entries.
311
+ /// Small files only (allowlist covers ~KB-to-MB). Caller enforces size
312
+ /// caps before hashing.
313
+ export async function sha256File(path) {
314
+ const bytes = await fs.readFile(path);
315
+ const h = createHash("sha256");
316
+ h.update(bytes);
317
+ return h.digest("hex");
318
+ }
319
+ //# sourceMappingURL=rollout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rollout.js","sourceRoot":"","sources":["../src/rollout.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export interface McpConfig {
2
+ silo_audit_url: string;
3
+ token: string;
4
+ email: string;
5
+ minted_at_ms: number;
6
+ }
7
+ export type AgentKind = "claude" | "codex";
8
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,SAAS;IAGxB,cAAc,EAAE,MAAM,CAAC;IAEvB,KAAK,EAAE,MAAM,CAAC;IAGd,KAAK,EAAE,MAAM,CAAC;IAId,YAAY,EAAE,MAAM,CAAC;CACtB;AAMD,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAC"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@granica/myelin-beam-client",
3
+ "version": "0.1.0-alpha.0",
4
+ "description": "Shared Node client for Myelin beam-up/down endpoints. Consumed by @granica/myelin-beam-mcp (and future CLI). No agent-specific code.",
5
+ "keywords": [
6
+ "myelin",
7
+ "beam",
8
+ "client"
9
+ ],
10
+ "homepage": "https://myelin.granica.ai",
11
+ "bugs": {
12
+ "url": "https://github.com/granica-ai/myelin/issues",
13
+ "email": "security@granica.ai"
14
+ },
15
+ "license": "Apache-2.0",
16
+ "author": "Granica <team@granica.ai>",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/granica-ai/myelin.git",
20
+ "directory": "packages/beam-client"
21
+ },
22
+ "type": "module",
23
+ "main": "dist/index.js",
24
+ "types": "dist/index.d.ts",
25
+ "exports": {
26
+ ".": {
27
+ "import": "./dist/index.js",
28
+ "types": "./dist/index.d.ts"
29
+ }
30
+ },
31
+ "files": [
32
+ "dist",
33
+ "LICENSE",
34
+ "README.md"
35
+ ],
36
+ "publishConfig": {
37
+ "access": "public",
38
+ "provenance": true,
39
+ "tag": "alpha"
40
+ },
41
+ "scripts": {
42
+ "build": "tsc -p tsconfig.json",
43
+ "clean": "rm -rf dist"
44
+ },
45
+ "engines": {
46
+ "node": ">=20.0.0"
47
+ },
48
+ "dependencies": {
49
+ "undici": "6.21.1"
50
+ },
51
+ "devDependencies": {
52
+ "@types/node": "20.19.9",
53
+ "typescript": "5.6.3"
54
+ }
55
+ }