@ai-matrx/coding-sessions 0.1.83 → 0.1.86

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.
@@ -30,6 +30,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/engine/index.ts
31
31
  var engine_exports = {};
32
32
  __export(engine_exports, {
33
+ DEFAULT_SERVER_URL: () => DEFAULT_SERVER_URL,
34
+ DEFAULT_SUPABASE_URL: () => DEFAULT_SUPABASE_URL,
33
35
  EFFORT_LEVELS: () => EFFORT_LEVELS,
34
36
  PreferenceStore: () => PreferenceStore,
35
37
  RAW_TRANSCRIPT_MAX_BYTES: () => RAW_TRANSCRIPT_MAX_BYTES,
@@ -61,7 +63,7 @@ var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
61
63
 
62
64
  // src/engine/index.ts
63
65
  var import_node_child_process3 = require("child_process");
64
- var import_node_crypto4 = require("crypto");
66
+ var import_node_crypto5 = require("crypto");
65
67
  var import_node_fs4 = require("fs");
66
68
  var import_format = require("@ai-matrx/kit/format");
67
69
 
@@ -159,10 +161,448 @@ function createAiMatrxRawBackupSink(options) {
159
161
  };
160
162
  }
161
163
 
164
+ // src/engine/agent-attach.ts
165
+ var import_promises2 = __toESM(require("fs/promises"), 1);
166
+ var import_node_path = __toESM(require("path"), 1);
167
+
168
+ // src/normalize/agent-block.ts
169
+ var AGENT_BLOCK_MARKER = "<ai_matrx_agent ";
170
+ function stripAgentBlock(text) {
171
+ const removed = text.match(/^<ai_matrx_agent_removed>[\s\S]*?<\/ai_matrx_agent_removed>\s*/);
172
+ if (removed) return text.slice(removed[0].length);
173
+ if (!text.startsWith(AGENT_BLOCK_MARKER)) return text;
174
+ const end = text.indexOf("</ai_matrx_agent>");
175
+ if (end < 0) return text;
176
+ let rest = text.slice(end + "</ai_matrx_agent>".length);
177
+ const skills = rest.match(/^\s*<skills_instructions>[\s\S]*?<\/skills_instructions>/);
178
+ if (skills) rest = rest.slice(skills[0].length);
179
+ return rest.replace(/^\s+/, "");
180
+ }
181
+
182
+ // src/engine/agent-attach.ts
183
+ var AGENT_MCP_SERVER_NAME = "matrx-agent";
184
+ var AGENT_PLUGIN_NAME = "matrx-agent";
185
+ function escapeAttr(value) {
186
+ return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;");
187
+ }
188
+ function versionWords(bundle) {
189
+ return bundle.version_number !== null ? String(bundle.version_number) : "latest";
190
+ }
191
+ function renderAgentInstructions(bundle, options = {}) {
192
+ const included = bundle.skills.filter((skill) => skill.tier === "included");
193
+ const listed2 = bundle.skills.filter((skill) => skill.tier === "listed");
194
+ const parts = [`<ai_matrx_agent name="${escapeAttr(bundle.name)}" version="${versionWords(bundle)}">`];
195
+ if (included.length > 0) {
196
+ parts.push(
197
+ "## Skills (always in effect)",
198
+ ...included.map((skill) => `### ${skill.label}
199
+
200
+ ${skill.body.trim()}`)
201
+ );
202
+ }
203
+ if (options.claudeSkills && listed2.length > 0) {
204
+ parts.push(
205
+ [
206
+ "## Your skills",
207
+ "When a request matches one of these, load it with your Skill tool and follow it before answering:",
208
+ ...listed2.map((skill) => `- ${AGENT_PLUGIN_NAME}:${skill.slug} \u2014 ${skill.description || skill.label}`)
209
+ ].join("\n")
210
+ );
211
+ }
212
+ if (included.length > 0 || options.claudeSkills && listed2.length > 0) parts.push("## Your instructions");
213
+ parts.push(bundle.instructions.trim());
214
+ parts.push("</ai_matrx_agent>");
215
+ return `${parts.filter(Boolean).join("\n\n")}
216
+ `;
217
+ }
218
+ function renderCodexSkillsList(bundle, skillsRoot) {
219
+ const listed2 = bundle.skills.filter((skill) => skill.tier === "listed");
220
+ if (listed2.length === 0) return "";
221
+ const lines = [
222
+ "<skills_instructions>",
223
+ "## AI Matrx agent skills",
224
+ "A skill is a set of instructions stored in a `SKILL.md` file. Each entry has a name, a description and the absolute path of its file. When a task matches a skill's description, read that file and follow it.",
225
+ "### Available skills",
226
+ ...listed2.map(
227
+ (skill) => `- ${skill.slug}: ${skill.description || skill.label} (file: ${import_node_path.default.join(skillsRoot, skill.slug, "SKILL.md")})`
228
+ ),
229
+ "</skills_instructions>"
230
+ ];
231
+ return lines.join("\n");
232
+ }
233
+ function renderFallbackBlock(bundle, skillsRoot) {
234
+ const skills = renderCodexSkillsList(bundle, skillsRoot);
235
+ const body = renderAgentInstructions(bundle).trim().replace(/^(<ai_matrx_agent [^>]*>)/, "$1\n\nThe instructions below are your AI Matrx agent for this chat from now on; they replace any AI Matrx agent instructions given earlier.");
236
+ return skills ? `${body}
237
+
238
+ ${skills}
239
+
240
+ ` : `${body}
241
+
242
+ `;
243
+ }
244
+ function tomlString(value) {
245
+ const scalar = value.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, "\uFFFD");
246
+ return JSON.stringify(scalar).replace(/\u007f/g, "\\u007f");
247
+ }
248
+ async function writeAgentAttachment(dir, bundle, options) {
249
+ const pluginDir = import_node_path.default.join(dir, "plugin");
250
+ const skillsRoot = import_node_path.default.join(dir, "skills");
251
+ await import_promises2.default.mkdir(import_node_path.default.join(pluginDir, ".claude-plugin"), { recursive: true, mode: 448 });
252
+ await import_promises2.default.mkdir(skillsRoot, { recursive: true, mode: 448 });
253
+ const instructions = renderAgentInstructions(bundle);
254
+ const instructionsFile = import_node_path.default.join(dir, "instructions.md");
255
+ await import_promises2.default.writeFile(instructionsFile, renderAgentInstructions(bundle, { claudeSkills: true }), { mode: 384 });
256
+ await import_promises2.default.writeFile(
257
+ import_node_path.default.join(pluginDir, ".claude-plugin", "plugin.json"),
258
+ `${JSON.stringify(
259
+ {
260
+ name: AGENT_PLUGIN_NAME,
261
+ description: `Skills of the AI Matrx agent "${bundle.name}"`,
262
+ version: versionWords(bundle) === "latest" ? "1.0.0" : `${versionWords(bundle)}.0.0`
263
+ },
264
+ null,
265
+ 2
266
+ )}
267
+ `,
268
+ { mode: 384 }
269
+ );
270
+ const listed2 = bundle.skills.filter((skill) => skill.tier === "listed");
271
+ const wanted = new Set(listed2.map((skill) => skill.slug));
272
+ for (const root of [import_node_path.default.join(pluginDir, "skills"), skillsRoot]) {
273
+ await import_promises2.default.mkdir(root, { recursive: true, mode: 448 });
274
+ for (const existing of await import_promises2.default.readdir(root).catch(() => [])) {
275
+ if (!wanted.has(existing)) await import_promises2.default.rm(import_node_path.default.join(root, existing), { recursive: true, force: true });
276
+ }
277
+ for (const skill of listed2) {
278
+ const skillDir = import_node_path.default.join(root, skill.slug);
279
+ await import_promises2.default.mkdir(skillDir, { recursive: true, mode: 448 });
280
+ await import_promises2.default.writeFile(import_node_path.default.join(skillDir, "SKILL.md"), skill.content, { mode: 384 });
281
+ }
282
+ }
283
+ let mcpConfigFile = null;
284
+ const mcpUrl = options.mcpUrl && bundle.tools.length > 0 ? options.mcpUrl : null;
285
+ const mcpPath = import_node_path.default.join(dir, "mcp.json");
286
+ if (mcpUrl) {
287
+ mcpConfigFile = mcpPath;
288
+ await import_promises2.default.writeFile(
289
+ mcpPath,
290
+ `${JSON.stringify({ mcpServers: { [AGENT_MCP_SERVER_NAME]: { type: "http", url: mcpUrl } } }, null, 2)}
291
+ `,
292
+ { mode: 384 }
293
+ );
294
+ } else {
295
+ await import_promises2.default.rm(mcpPath, { force: true });
296
+ }
297
+ const skillsList = renderCodexSkillsList(bundle, skillsRoot);
298
+ return {
299
+ dir,
300
+ instructionsFile,
301
+ pluginDir,
302
+ mcpConfigFile,
303
+ mcpUrl,
304
+ // Same order as Claude's: the skills list first, the agent's words last.
305
+ codexInstructions: skillsList ? `${skillsList}
306
+
307
+ ${instructions}` : instructions
308
+ };
309
+ }
310
+ function agentArgsFor(provider, attachment, options) {
311
+ if (provider === "claude") {
312
+ const args2 = [
313
+ // Every turn: without snapshot off, a resumed Claude drops appended instructions.
314
+ "--append-system-prompt-file",
315
+ attachment.instructionsFile,
316
+ "--system-prompt-snapshot",
317
+ "off",
318
+ "--plugin-dir",
319
+ attachment.pluginDir
320
+ ];
321
+ if (attachment.mcpConfigFile) args2.push("--mcp-config", attachment.mcpConfigFile);
322
+ return args2;
323
+ }
324
+ const args = [];
325
+ if (!options.resuming) args.push("-c", `developer_instructions=${tomlString(attachment.codexInstructions)}`);
326
+ if (attachment.mcpUrl) {
327
+ const key = `mcp_servers.${AGENT_MCP_SERVER_NAME}`;
328
+ args.push(
329
+ "-c",
330
+ `${key}.url=${tomlString(attachment.mcpUrl)}`,
331
+ "-c",
332
+ `${key}.default_tools_approval_mode="approve"`
333
+ );
334
+ }
335
+ return args;
336
+ }
337
+ function sessionAgentRecord(bundle, options) {
338
+ const unavailable = bundle.unavailable.map((u) => ({ kind: u.kind, name: u.name, reason: u.reason }));
339
+ const missingSkills = unavailable.filter((u) => u.kind === "skill").length;
340
+ const missingTools = unavailable.filter((u) => u.kind === "tool").length;
341
+ if (!options.toolsServed && bundle.tools.length > 0) {
342
+ for (const tool of bundle.tools) {
343
+ unavailable.push({ kind: "tool", name: tool.canonical_name, reason: "Tools need the coding sessions host" });
344
+ }
345
+ }
346
+ const toolsLoaded = options.toolsServed ? bundle.tools.length : 0;
347
+ const loaded = [
348
+ { part: "instructions", loaded: bundle.instructions.trim() ? 1 : 0, total: 1 },
349
+ { part: "skills", loaded: bundle.skills.length, total: bundle.skills.length + missingSkills },
350
+ { part: "tools", loaded: toolsLoaded, total: bundle.tools.length + missingTools }
351
+ ];
352
+ return {
353
+ agentId: bundle.agent_id,
354
+ versionId: bundle.version_id,
355
+ versionNumber: bundle.version_number,
356
+ name: bundle.name,
357
+ definitionHash: bundle.definition_hash,
358
+ loaded,
359
+ unavailable,
360
+ skills: bundle.skills.map((skill) => skill.label),
361
+ tools: options.toolsServed ? bundle.tools.map((tool) => tool.canonical_name) : [],
362
+ ...options.variables ? { variables: options.variables } : {}
363
+ };
364
+ }
365
+ var AGENT_REMOVED_BLOCK = "<ai_matrx_agent_removed>The AI Matrx agent instructions given earlier in this chat no longer apply.</ai_matrx_agent_removed>\n\n";
366
+ async function codexCompactionDroppedAgent(rolloutFile, sinceIso) {
367
+ const text = await import_promises2.default.readFile(rolloutFile, "utf8").catch(() => "");
368
+ if (!text.includes('"type":"compacted"')) return false;
369
+ let latest = null;
370
+ for (const line of text.split("\n")) {
371
+ if (!line.includes('"type":"compacted"')) continue;
372
+ try {
373
+ const entry = JSON.parse(line);
374
+ if (entry.type === "compacted") latest = entry;
375
+ } catch {
376
+ }
377
+ }
378
+ if (!latest) return false;
379
+ if (sinceIso && latest.timestamp && latest.timestamp <= sinceIso) return false;
380
+ return !JSON.stringify(latest.payload?.replacement_history ?? "").includes(AGENT_BLOCK_MARKER.trim());
381
+ }
382
+
383
+ // src/engine/agents.ts
384
+ var import_node_crypto2 = require("crypto");
385
+ var import_promises3 = __toESM(require("fs/promises"), 1);
386
+ var import_node_path2 = __toESM(require("path"), 1);
387
+ var DEFAULT_SERVER_URL = "https://server.app.matrxserver.com";
388
+ var DEFAULT_SUPABASE_URL = "https://db.matrxserver.com";
389
+ var DEFAULT_SUPABASE_KEY = "sb_publishable_scm0jzEJiFUB5ItiqOx5Fg_8BOjB6oA";
390
+ var AGENT_CATALOG_RPCS = /* @__PURE__ */ new Set([
391
+ "agx_get_list_full",
392
+ "agx_search",
393
+ "agx_resolve_agent_address",
394
+ "ues_get_bulk",
395
+ "ues_list",
396
+ "ues_set",
397
+ "ues_touch"
398
+ ]);
399
+ var agentsClient = async () => {
400
+ const [matrx, portable, mcp] = await Promise.all([
401
+ import("@ai-matrx/agents/matrx"),
402
+ import("@ai-matrx/agents/portable"),
403
+ import("@ai-matrx/agents/portable/mcp")
404
+ ]);
405
+ return { ...matrx, ...portable, ...mcp };
406
+ };
407
+ function sortedValues(values) {
408
+ return Object.keys(values).sort().map((key) => [key, values[key]]);
409
+ }
410
+ var BUNDLE_CACHE_MS = 24 * 60 * 60 * 1e3;
411
+ var SIGNED_OUT_AGENT_MESSAGE = "Sign in to AI Matrx to use your agents.";
412
+ function userIdOf(credential) {
413
+ if (!credential || credential.kind !== "user") return null;
414
+ const payload = credential.accessToken.split(".")[1];
415
+ if (!payload) return null;
416
+ try {
417
+ const claims = JSON.parse(Buffer.from(payload, "base64url").toString("utf8"));
418
+ return typeof claims.sub === "string" ? claims.sub : null;
419
+ } catch {
420
+ return null;
421
+ }
422
+ }
423
+ function createAgentConnection(options, dataDir) {
424
+ const credentials = options?.credentials ?? null;
425
+ const serverUrl = (options?.serverUrl ?? DEFAULT_SERVER_URL).replace(/\/+$/, "");
426
+ const supabaseUrl = (options?.supabaseUrl ?? DEFAULT_SUPABASE_URL).replace(/\/+$/, "");
427
+ const supabaseKey = options?.supabaseKey ?? (options?.supabaseUrl ? null : DEFAULT_SUPABASE_KEY);
428
+ const root = import_node_path2.default.join(dataDir, "agents");
429
+ let mcpBaseUrl = null;
430
+ const handlers = /* @__PURE__ */ new Map();
431
+ const secretToSession = /* @__PURE__ */ new Map();
432
+ const organizationId = async () => {
433
+ const raw = typeof options?.organizationId === "function" ? await options.organizationId() : options?.organizationId;
434
+ return raw && raw.trim() ? raw.trim() : null;
435
+ };
436
+ const transport = async () => {
437
+ const org = await organizationId();
438
+ return (await agentsClient()).createMatrxTransport({
439
+ baseUrl: serverUrl,
440
+ ...credentials ? { credentials } : {},
441
+ ...org ? { organizationId: org } : {},
442
+ source: "codingSessionsAgents"
443
+ });
444
+ };
445
+ const cacheDir = import_node_path2.default.join(dataDir, "agents", ".bundles");
446
+ const cacheKey = (definitionHash, selection, surface) => (0, import_node_crypto2.createHash)("sha256").update(JSON.stringify([definitionHash, sortedValues(selection.variables ?? {}), surface])).digest("hex");
447
+ const credential = async () => credentials ? credentials.get() : null;
448
+ const requireSignedIn = async () => {
449
+ if (!await credential()) throw new Error(SIGNED_OUT_AGENT_MESSAGE);
450
+ };
451
+ const sessionDir = (sessionId) => import_node_path2.default.join(root, sessionId.replace(/[^A-Za-z0-9_-]/g, "_"));
452
+ async function meta(sessionId) {
453
+ const file = import_node_path2.default.join(sessionDir(sessionId), "session.json");
454
+ const existing = await import_promises3.default.readFile(file, "utf8").then((text) => JSON.parse(text)).catch(() => null);
455
+ if (existing?.mcpSecret && existing.conversationId) return existing;
456
+ const created = { mcpSecret: (0, import_node_crypto2.randomBytes)(24).toString("base64url"), conversationId: (0, import_node_crypto2.randomUUID)() };
457
+ await import_promises3.default.mkdir(sessionDir(sessionId), { recursive: true, mode: 448 });
458
+ await import_promises3.default.writeFile(file, JSON.stringify(created), { mode: 384 });
459
+ return created;
460
+ }
461
+ async function findSessionBySecret(secret) {
462
+ for (const name of await import_promises3.default.readdir(root).catch(() => [])) {
463
+ const found = await import_promises3.default.readFile(import_node_path2.default.join(root, name, "session.json"), "utf8").then((text) => JSON.parse(text).mcpSecret === secret).catch(() => false);
464
+ if (found) {
465
+ secretToSession.set(secret, name);
466
+ return name;
467
+ }
468
+ }
469
+ return null;
470
+ }
471
+ async function readSessionBundle(sessionId) {
472
+ return import_promises3.default.readFile(import_node_path2.default.join(sessionDir(sessionId), "bundle.json"), "utf8").then((text) => JSON.parse(text)).catch(() => null);
473
+ }
474
+ return {
475
+ async access() {
476
+ const current = await credential().catch(() => null);
477
+ const userId = userIdOf(current);
478
+ return {
479
+ configured: credentials !== null && supabaseKey !== null,
480
+ signedIn: userId !== null,
481
+ userId,
482
+ organizationId: await organizationId().catch(() => null)
483
+ };
484
+ },
485
+ async catalogRpc(request) {
486
+ if (!AGENT_CATALOG_RPCS.has(request.fn)) {
487
+ return { data: null, error: { message: `Not an agent catalogue read: ${request.fn}` } };
488
+ }
489
+ const current = await credential();
490
+ if (!current || current.kind !== "user") return { data: null, error: { message: SIGNED_OUT_AGENT_MESSAGE, code: "signed_out" } };
491
+ if (!supabaseKey) return { data: null, error: { message: "This host has no AI Matrx database key.", code: "not_configured" } };
492
+ const query = new URLSearchParams();
493
+ if (request.order) query.set("order", `${request.order.column}.${request.order.ascending === false ? "desc" : "asc"}`);
494
+ if (request.range) {
495
+ query.set("offset", String(request.range[0]));
496
+ query.set("limit", String(request.range[1] - request.range[0] + 1));
497
+ }
498
+ const headers = {
499
+ apikey: supabaseKey,
500
+ Authorization: `Bearer ${current.accessToken}`,
501
+ "Content-Type": "application/json",
502
+ // Every catalogue RPC lives in `public`; the database's default
503
+ // exposed schema is not `public`, so name it (supabase-js does).
504
+ "Content-Profile": "public",
505
+ "Accept-Profile": "public"
506
+ };
507
+ if (request.count) headers.Prefer = `count=${request.count}`;
508
+ const qs = query.toString();
509
+ const response = await fetch(`${supabaseUrl}/rest/v1/rpc/${encodeURIComponent(request.fn)}${qs ? `?${qs}` : ""}`, {
510
+ method: "POST",
511
+ headers,
512
+ body: JSON.stringify(request.args ?? {})
513
+ });
514
+ const text = await response.text();
515
+ const body = text ? JSON.parse(text) : null;
516
+ if (!response.ok) {
517
+ const err = body ?? {};
518
+ return {
519
+ data: null,
520
+ error: { message: err.message ?? `HTTP ${response.status}`, code: err.code, details: err.details ?? null, hint: err.hint ?? null }
521
+ };
522
+ }
523
+ const range = response.headers.get("content-range");
524
+ const total = range?.split("/")[1];
525
+ return { data: body, error: null, count: total && total !== "*" ? Number(total) : null };
526
+ },
527
+ async details(agentId) {
528
+ await requireSignedIn();
529
+ const summary = await (await agentsClient()).fetchPortableAgentSummary(await transport(), agentId);
530
+ return {
531
+ agentId: summary.agent_id,
532
+ name: summary.name,
533
+ variables: summary.variables,
534
+ versions: summary.versions.map((v) => ({ id: v.id, versionNumber: v.version_number, changedAt: v.changed_at })),
535
+ latestVersionNumber: summary.version_number,
536
+ latestVersionId: summary.version_id
537
+ };
538
+ },
539
+ async loadBundle(selection) {
540
+ await requireSignedIn();
541
+ const client = await agentsClient();
542
+ const summary = await client.fetchPortableAgentSummary(await transport(), selection.agentId);
543
+ const versionId = selection.versionId ?? summary.version_id;
544
+ const definitionHash = versionId ? summary.versions.find((v) => v.id === versionId)?.definition_hash ?? null : summary.definition_hash;
545
+ const surface = "matrx-user/code-editor";
546
+ const file = definitionHash ? import_node_path2.default.join(cacheDir, `${cacheKey(definitionHash, selection, surface)}.json`) : null;
547
+ if (file) {
548
+ const cached = await import_promises3.default.readFile(file, "utf8").then((text) => JSON.parse(text)).catch(() => null);
549
+ const fresh = await import_promises3.default.stat(file).then((st) => Date.now() - st.mtimeMs < BUNDLE_CACHE_MS).catch(() => false);
550
+ if (cached && fresh && cached.definition_hash === definitionHash) return cached;
551
+ }
552
+ const bundle = await client.fetchPortableAgent(
553
+ await transport(),
554
+ versionId ? { versionId } : { agentId: selection.agentId },
555
+ { variables: selection.variables ?? {}, surface }
556
+ );
557
+ if (file && bundle.definition_hash === definitionHash) {
558
+ await import_promises3.default.mkdir(cacheDir, { recursive: true, mode: 448 });
559
+ await import_promises3.default.writeFile(file, JSON.stringify(bundle), { mode: 384 });
560
+ }
561
+ return bundle;
562
+ },
563
+ async saveSessionBundle(sessionId, bundle) {
564
+ await import_promises3.default.mkdir(sessionDir(sessionId), { recursive: true, mode: 448 });
565
+ await import_promises3.default.writeFile(import_node_path2.default.join(sessionDir(sessionId), "bundle.json"), JSON.stringify(bundle), { mode: 384 });
566
+ await meta(sessionId);
567
+ },
568
+ readSessionBundle,
569
+ sessionDir,
570
+ async mcpUrlFor(sessionId) {
571
+ if (!mcpBaseUrl) return null;
572
+ const { mcpSecret } = await meta(sessionId);
573
+ secretToSession.set(mcpSecret, sessionId);
574
+ return `${mcpBaseUrl}/mcp/${mcpSecret}`;
575
+ },
576
+ setMcpBaseUrl(baseUrl) {
577
+ mcpBaseUrl = baseUrl ? baseUrl.replace(/\/+$/, "") : null;
578
+ },
579
+ async handleMcp(secret, body) {
580
+ let handler = handlers.get(secret);
581
+ if (!handler) {
582
+ const sessionId = secretToSession.get(secret) ?? await findSessionBySecret(secret);
583
+ if (!sessionId) return null;
584
+ const bundle = await readSessionBundle(sessionId);
585
+ if (!bundle) return null;
586
+ const { conversationId } = await meta(sessionId);
587
+ const { createAgentToolsMcpHandler } = await agentsClient();
588
+ handler = createAgentToolsMcpHandler({
589
+ bundle,
590
+ credentials,
591
+ organizationId: () => organizationId(),
592
+ baseUrl: serverUrl,
593
+ conversationId
594
+ });
595
+ handlers.set(secret, handler);
596
+ }
597
+ return handler.handle(body);
598
+ }
599
+ };
600
+ }
601
+
162
602
  // src/engine/index.ts
163
- var import_promises6 = __toESM(require("fs/promises"), 1);
603
+ var import_promises8 = __toESM(require("fs/promises"), 1);
164
604
  var import_node_os5 = __toESM(require("os"), 1);
165
- var import_node_path7 = __toESM(require("path"), 1);
605
+ var import_node_path9 = __toESM(require("path"), 1);
166
606
  var import_node_url = require("url");
167
607
 
168
608
  // src/normalize/claude.ts
@@ -533,7 +973,7 @@ function rolloutItem(item, fallbackId, at) {
533
973
  const id = str2(item.id) ?? fallbackId;
534
974
  switch (item.type) {
535
975
  case "UserMessage": {
536
- const text = stripCodexInjectedContext(itemText(item.content));
976
+ const text = stripCodexInjectedContext(stripAgentBlock(itemText(item.content)));
537
977
  return text.trim() ? [{ kind: "user", id, text, at }] : [];
538
978
  }
539
979
  case "AgentMessage": {
@@ -616,7 +1056,7 @@ function createCodexRolloutNormalizer() {
616
1056
  return rolloutItem(payload.item, fallbackId, at);
617
1057
  }
618
1058
  if (payload.type === "user_message" && !appItems) {
619
- const text = stripCodexInjectedContext(str2(payload.message) ?? "");
1059
+ const text = stripCodexInjectedContext(stripAgentBlock(str2(payload.message) ?? ""));
620
1060
  return text.trim() ? [{ kind: "user", id: fallbackId, text, at }] : [];
621
1061
  }
622
1062
  return [];
@@ -832,7 +1272,7 @@ function describeProblem(provider, problem) {
832
1272
  var import_node_child_process = require("child_process");
833
1273
  var import_node_fs = require("fs");
834
1274
  var import_node_os = __toESM(require("os"), 1);
835
- var import_node_path = __toESM(require("path"), 1);
1275
+ var import_node_path3 = __toESM(require("path"), 1);
836
1276
  var SOURCE_LABEL = {
837
1277
  codex: { "desktop-app": "from ChatGPT app", path: "from PATH", "npm-global": "from npm", configured: "set by host" },
838
1278
  claude: { "desktop-app": "from Claude app", path: "from PATH", "npm-global": "from npm", configured: "set by host" }
@@ -881,10 +1321,10 @@ function exeName(name, platform) {
881
1321
  }
882
1322
  function allOnPath(name, pathValue, platform = process.platform) {
883
1323
  const found = [];
884
- for (const dir of pathValue.split(import_node_path.default.delimiter)) {
1324
+ for (const dir of pathValue.split(import_node_path3.default.delimiter)) {
885
1325
  if (!dir) continue;
886
1326
  for (const file of exeName(name, platform)) {
887
- const candidate = import_node_path.default.join(dir, file);
1327
+ const candidate = import_node_path3.default.join(dir, file);
888
1328
  if (executable(candidate)) {
889
1329
  found.push(candidate);
890
1330
  break;
@@ -897,17 +1337,17 @@ function desktopAppCandidates(provider, platform, home, applications) {
897
1337
  if (platform !== "darwin") return [];
898
1338
  if (provider === "codex") {
899
1339
  return applications.map(
900
- (apps) => import_node_path.default.join(apps, "ChatGPT.app", "Contents", "Resources", "codex-cli", "bin", "codex")
1340
+ (apps) => import_node_path3.default.join(apps, "ChatGPT.app", "Contents", "Resources", "codex-cli", "bin", "codex")
901
1341
  );
902
1342
  }
903
- const root = import_node_path.default.join(home, "Library", "Application Support", "Claude", "claude-code");
1343
+ const root = import_node_path3.default.join(home, "Library", "Application Support", "Claude", "claude-code");
904
1344
  let versions = [];
905
1345
  try {
906
1346
  versions = (0, import_node_fs.readdirSync)(root);
907
1347
  } catch {
908
1348
  return [];
909
1349
  }
910
- return versions.map((v) => import_node_path.default.join(root, v, "claude.app", "Contents", "MacOS", "claude"));
1350
+ return versions.map((v) => import_node_path3.default.join(root, v, "claude.app", "Contents", "MacOS", "claude"));
911
1351
  }
912
1352
  function npmGlobalPrefix(env) {
913
1353
  const fromEnv = env.npm_config_prefix ?? env.NPM_CONFIG_PREFIX;
@@ -925,13 +1365,13 @@ function declaredVersion(provider, source, file) {
925
1365
  if (source !== "desktop-app") return null;
926
1366
  if (provider === "codex") {
927
1367
  try {
928
- const pkg = JSON.parse((0, import_node_fs.readFileSync)(import_node_path.default.join(import_node_path.default.dirname(import_node_path.default.dirname(file)), "codex-package.json"), "utf8"));
1368
+ const pkg = JSON.parse((0, import_node_fs.readFileSync)(import_node_path3.default.join(import_node_path3.default.dirname(import_node_path3.default.dirname(file)), "codex-package.json"), "utf8"));
929
1369
  return typeof pkg.version === "string" ? parseVersion(pkg.version) : null;
930
1370
  } catch {
931
1371
  return null;
932
1372
  }
933
1373
  }
934
- return parseVersion(import_node_path.default.basename(import_node_path.default.resolve(file, "..", "..", "..", "..")));
1374
+ return parseVersion(import_node_path3.default.basename(import_node_path3.default.resolve(file, "..", "..", "..", "..")));
935
1375
  }
936
1376
  function runVersion(file, env) {
937
1377
  return new Promise((resolve) => {
@@ -945,14 +1385,14 @@ async function discoverBinaries(provider, options) {
945
1385
  const platform = options.platform ?? process.platform;
946
1386
  const home = options.home ?? options.env.HOME ?? import_node_os.default.homedir();
947
1387
  const listed2 = [
948
- ...desktopAppCandidates(provider, platform, home, options.applications ?? ["/Applications", import_node_path.default.join(home, "Applications")]).filter(executable).map((p) => ({ path: p, source: "desktop-app" })),
1388
+ ...desktopAppCandidates(provider, platform, home, options.applications ?? ["/Applications", import_node_path3.default.join(home, "Applications")]).filter(executable).map((p) => ({ path: p, source: "desktop-app" })),
949
1389
  ...allOnPath(provider, options.env.PATH ?? "", platform).map((p) => ({ path: p, source: "path" }))
950
1390
  ];
951
1391
  const prefix = await npmGlobalPrefix(options.env);
952
1392
  if (prefix) {
953
- const dir = platform === "win32" ? prefix : import_node_path.default.join(prefix, "bin");
1393
+ const dir = platform === "win32" ? prefix : import_node_path3.default.join(prefix, "bin");
954
1394
  for (const file of exeName(provider, platform)) {
955
- const candidate = import_node_path.default.join(dir, file);
1395
+ const candidate = import_node_path3.default.join(dir, file);
956
1396
  if (executable(candidate)) {
957
1397
  listed2.push({ path: candidate, source: "npm-global" });
958
1398
  break;
@@ -998,7 +1438,7 @@ async function resolveBinary(provider, options) {
998
1438
 
999
1439
  // src/engine/clean-env.ts
1000
1440
  var import_node_os2 = __toESM(require("os"), 1);
1001
- var import_node_path2 = __toESM(require("path"), 1);
1441
+ var import_node_path4 = __toESM(require("path"), 1);
1002
1442
  var ALLOWED_NAMES = /* @__PURE__ */ new Set([
1003
1443
  "HOME",
1004
1444
  "USER",
@@ -1020,11 +1460,11 @@ var ALLOWED_NAMES = /* @__PURE__ */ new Set([
1020
1460
  var ALLOWED_PREFIXES = ["LC_"];
1021
1461
  function wellKnownBinDirs(home) {
1022
1462
  return [
1023
- import_node_path2.default.join(home, ".local", "bin"),
1024
- import_node_path2.default.join(home, ".claude", "local"),
1025
- import_node_path2.default.join(home, ".npm-global", "bin"),
1026
- import_node_path2.default.join(home, ".bun", "bin"),
1027
- import_node_path2.default.join(home, ".volta", "bin"),
1463
+ import_node_path4.default.join(home, ".local", "bin"),
1464
+ import_node_path4.default.join(home, ".claude", "local"),
1465
+ import_node_path4.default.join(home, ".npm-global", "bin"),
1466
+ import_node_path4.default.join(home, ".bun", "bin"),
1467
+ import_node_path4.default.join(home, ".volta", "bin"),
1028
1468
  "/opt/homebrew/bin",
1029
1469
  "/usr/local/bin",
1030
1470
  "/usr/bin",
@@ -1044,21 +1484,21 @@ function buildCleanEnv(hostEnv, extra = {}) {
1044
1484
  }
1045
1485
  const home = out.HOME ?? import_node_os2.default.homedir();
1046
1486
  out.HOME = home;
1047
- const hostPath = (hostEnv.PATH ?? "").split(import_node_path2.default.delimiter).filter(Boolean);
1487
+ const hostPath = (hostEnv.PATH ?? "").split(import_node_path4.default.delimiter).filter(Boolean);
1048
1488
  const merged = [];
1049
1489
  for (const dir of [...hostPath, ...wellKnownBinDirs(home)]) {
1050
1490
  if (!merged.includes(dir)) merged.push(dir);
1051
1491
  }
1052
- out.PATH = merged.join(import_node_path2.default.delimiter);
1492
+ out.PATH = merged.join(import_node_path4.default.delimiter);
1053
1493
  for (const [name, value] of Object.entries(extra)) out[name] = value;
1054
1494
  return out;
1055
1495
  }
1056
1496
 
1057
1497
  // src/engine/files.ts
1058
1498
  var import_node_fs2 = require("fs");
1059
- var import_promises2 = __toESM(require("fs/promises"), 1);
1499
+ var import_promises4 = __toESM(require("fs/promises"), 1);
1060
1500
  var import_node_os3 = __toESM(require("os"), 1);
1061
- var import_node_path3 = __toESM(require("path"), 1);
1501
+ var import_node_path5 = __toESM(require("path"), 1);
1062
1502
 
1063
1503
  // src/engine/activity.ts
1064
1504
  var import_node_child_process2 = require("child_process");
@@ -1127,15 +1567,15 @@ function describeActivity(input) {
1127
1567
  // src/engine/files.ts
1128
1568
  function defaultProviderHomes(home = import_node_os3.default.homedir()) {
1129
1569
  return {
1130
- claudeProjects: import_node_path3.default.join(home, ".claude", "projects"),
1131
- codexSessions: import_node_path3.default.join(home, ".codex", "sessions"),
1132
- codexIndex: import_node_path3.default.join(home, ".codex", "session_index.jsonl")
1570
+ claudeProjects: import_node_path5.default.join(home, ".claude", "projects"),
1571
+ codexSessions: import_node_path5.default.join(home, ".codex", "sessions"),
1572
+ codexIndex: import_node_path5.default.join(home, ".codex", "session_index.jsonl")
1133
1573
  };
1134
1574
  }
1135
1575
  var UUIDISH = /([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.jsonl$/i;
1136
1576
  async function safeReaddir(dir) {
1137
1577
  try {
1138
- return await import_promises2.default.readdir(dir, { withFileTypes: true });
1578
+ return await import_promises4.default.readdir(dir, { withFileTypes: true });
1139
1579
  } catch {
1140
1580
  return [];
1141
1581
  }
@@ -1144,7 +1584,7 @@ async function statCandidate(provider, file) {
1144
1584
  const match = UUIDISH.exec(file);
1145
1585
  if (!match?.[1]) return null;
1146
1586
  try {
1147
- const stat = await import_promises2.default.stat(file);
1587
+ const stat = await import_promises4.default.stat(file);
1148
1588
  return { provider, file, id: match[1], mtimeMs: stat.mtimeMs, size: stat.size };
1149
1589
  } catch {
1150
1590
  return null;
@@ -1154,10 +1594,10 @@ async function claudeCandidates(root) {
1154
1594
  const out = [];
1155
1595
  for (const project of await safeReaddir(root)) {
1156
1596
  if (!project.isDirectory()) continue;
1157
- const dir = import_node_path3.default.join(root, project.name);
1597
+ const dir = import_node_path5.default.join(root, project.name);
1158
1598
  const entries = await safeReaddir(dir);
1159
1599
  const stats = await Promise.all(
1160
- entries.filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => statCandidate("claude", import_node_path3.default.join(dir, e.name)))
1600
+ entries.filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => statCandidate("claude", import_node_path5.default.join(dir, e.name)))
1161
1601
  );
1162
1602
  for (const s of stats) if (s) out.push(s);
1163
1603
  }
@@ -1167,14 +1607,14 @@ async function codexCandidates(root) {
1167
1607
  const out = [];
1168
1608
  for (const year of await safeReaddir(root)) {
1169
1609
  if (!year.isDirectory()) continue;
1170
- for (const month of await safeReaddir(import_node_path3.default.join(root, year.name))) {
1610
+ for (const month of await safeReaddir(import_node_path5.default.join(root, year.name))) {
1171
1611
  if (!month.isDirectory()) continue;
1172
- for (const day of await safeReaddir(import_node_path3.default.join(root, year.name, month.name))) {
1612
+ for (const day of await safeReaddir(import_node_path5.default.join(root, year.name, month.name))) {
1173
1613
  if (!day.isDirectory()) continue;
1174
- const dir = import_node_path3.default.join(root, year.name, month.name, day.name);
1614
+ const dir = import_node_path5.default.join(root, year.name, month.name, day.name);
1175
1615
  const entries = await safeReaddir(dir);
1176
1616
  const stats = await Promise.all(
1177
- entries.filter((e) => e.isFile() && e.name.startsWith("rollout-") && e.name.endsWith(".jsonl")).map((e) => statCandidate("codex", import_node_path3.default.join(dir, e.name)))
1617
+ entries.filter((e) => e.isFile() && e.name.startsWith("rollout-") && e.name.endsWith(".jsonl")).map((e) => statCandidate("codex", import_node_path5.default.join(dir, e.name)))
1178
1618
  );
1179
1619
  for (const s of stats) if (s) out.push(s);
1180
1620
  }
@@ -1183,7 +1623,7 @@ async function codexCandidates(root) {
1183
1623
  return out;
1184
1624
  }
1185
1625
  async function readSlice(file, start, length) {
1186
- const handle = await import_promises2.default.open(file, "r");
1626
+ const handle = await import_promises4.default.open(file, "r");
1187
1627
  try {
1188
1628
  const buffer = Buffer.alloc(length);
1189
1629
  const { bytesRead } = await handle.read(buffer, 0, length, start);
@@ -1256,9 +1696,9 @@ async function readCodexIndex(file) {
1256
1696
  const names = /* @__PURE__ */ new Map();
1257
1697
  let text;
1258
1698
  try {
1259
- const stat = await import_promises2.default.stat(file);
1699
+ const stat = await import_promises4.default.stat(file);
1260
1700
  const budget = 4 * 1024 * 1024;
1261
- text = stat.size <= budget ? await import_promises2.default.readFile(file, "utf8") : completeLines(await readSlice(file, stat.size - budget, budget), true);
1701
+ text = stat.size <= budget ? await import_promises4.default.readFile(file, "utf8") : completeLines(await readSlice(file, stat.size - budget, budget), true);
1262
1702
  } catch {
1263
1703
  return names;
1264
1704
  }
@@ -1379,7 +1819,7 @@ var MAX_HISTORY_BUDGET = 256 * 1024 * 1024;
1379
1819
  async function readSessionFile(provider, file, endOffset, maxBytes = HISTORY_BUDGET) {
1380
1820
  let size;
1381
1821
  try {
1382
- size = (await import_promises2.default.stat(file)).size;
1822
+ size = (await import_promises4.default.stat(file)).size;
1383
1823
  } catch {
1384
1824
  return {
1385
1825
  items: [],
@@ -1433,9 +1873,9 @@ async function describeSessionFile(candidate, codexNames) {
1433
1873
  }
1434
1874
 
1435
1875
  // src/engine/preferences.ts
1436
- var import_node_crypto2 = require("crypto");
1437
- var import_promises3 = __toESM(require("fs/promises"), 1);
1438
- var import_node_path4 = __toESM(require("path"), 1);
1876
+ var import_node_crypto3 = require("crypto");
1877
+ var import_promises5 = __toESM(require("fs/promises"), 1);
1878
+ var import_node_path6 = __toESM(require("path"), 1);
1439
1879
  var PreferenceStore = class {
1440
1880
  constructor(dir) {
1441
1881
  this.dir = dir;
@@ -1444,11 +1884,11 @@ var PreferenceStore = class {
1444
1884
  data = {};
1445
1885
  writing = Promise.resolve();
1446
1886
  get file() {
1447
- return import_node_path4.default.join(this.dir, "preferences.json");
1887
+ return import_node_path6.default.join(this.dir, "preferences.json");
1448
1888
  }
1449
1889
  async load() {
1450
1890
  try {
1451
- const parsed = JSON.parse(await import_promises3.default.readFile(this.file, "utf8"));
1891
+ const parsed = JSON.parse(await import_promises5.default.readFile(this.file, "utf8"));
1452
1892
  this.data = typeof parsed === "object" && parsed !== null ? parsed : {};
1453
1893
  } catch {
1454
1894
  this.data = {};
@@ -1486,10 +1926,10 @@ var PreferenceStore = class {
1486
1926
  persist() {
1487
1927
  const snapshot = JSON.stringify(this.data, null, 1);
1488
1928
  this.writing = this.writing.then(async () => {
1489
- await import_promises3.default.mkdir(this.dir, { recursive: true });
1490
- const tmp = `${this.file}.${(0, import_node_crypto2.randomUUID)()}.tmp`;
1491
- await import_promises3.default.writeFile(tmp, snapshot, { mode: 384 });
1492
- await import_promises3.default.rename(tmp, this.file);
1929
+ await import_promises5.default.mkdir(this.dir, { recursive: true });
1930
+ const tmp = `${this.file}.${(0, import_node_crypto3.randomUUID)()}.tmp`;
1931
+ await import_promises5.default.writeFile(tmp, snapshot, { mode: 384 });
1932
+ await import_promises5.default.rename(tmp, this.file);
1493
1933
  }).catch(() => {
1494
1934
  });
1495
1935
  }
@@ -1646,9 +2086,9 @@ async function searchLocalChats(query, options = {}) {
1646
2086
  }
1647
2087
 
1648
2088
  // src/engine/store.ts
1649
- var import_node_crypto3 = require("crypto");
1650
- var import_promises4 = __toESM(require("fs/promises"), 1);
1651
- var import_node_path5 = __toESM(require("path"), 1);
2089
+ var import_node_crypto4 = require("crypto");
2090
+ var import_promises6 = __toESM(require("fs/promises"), 1);
2091
+ var import_node_path7 = __toESM(require("path"), 1);
1652
2092
  var MAX_RUNS = 2e3;
1653
2093
  var RunIndex = class {
1654
2094
  constructor(dir) {
@@ -1658,12 +2098,12 @@ var RunIndex = class {
1658
2098
  data = { version: 1, sessions: [], runs: [] };
1659
2099
  writing = Promise.resolve();
1660
2100
  get file() {
1661
- return import_node_path5.default.join(this.dir, "index.json");
2101
+ return import_node_path7.default.join(this.dir, "index.json");
1662
2102
  }
1663
2103
  async load() {
1664
- await import_promises4.default.mkdir(this.dir, { recursive: true });
2104
+ await import_promises6.default.mkdir(this.dir, { recursive: true });
1665
2105
  try {
1666
- const parsed = JSON.parse(await import_promises4.default.readFile(this.file, "utf8"));
2106
+ const parsed = JSON.parse(await import_promises6.default.readFile(this.file, "utf8"));
1667
2107
  this.data = {
1668
2108
  version: 1,
1669
2109
  sessions: Array.isArray(parsed.sessions) ? parsed.sessions : [],
@@ -1705,9 +2145,9 @@ var RunIndex = class {
1705
2145
  persist() {
1706
2146
  const snapshot = JSON.stringify(this.data, null, 1);
1707
2147
  this.writing = this.writing.then(async () => {
1708
- const tmp = `${this.file}.${(0, import_node_crypto3.randomUUID)()}.tmp`;
1709
- await import_promises4.default.writeFile(tmp, snapshot, { mode: 384 });
1710
- await import_promises4.default.rename(tmp, this.file);
2148
+ const tmp = `${this.file}.${(0, import_node_crypto4.randomUUID)()}.tmp`;
2149
+ await import_promises6.default.writeFile(tmp, snapshot, { mode: 384 });
2150
+ await import_promises6.default.rename(tmp, this.file);
1711
2151
  }).catch(() => {
1712
2152
  });
1713
2153
  }
@@ -1717,27 +2157,27 @@ var RunIndex = class {
1717
2157
  /** A stable Paperclip agent/company identity for this install, so the
1718
2158
  * driver's scratch state is reused rather than growing per run. */
1719
2159
  async identity() {
1720
- const file = import_node_path5.default.join(this.dir, "identity.json");
2160
+ const file = import_node_path7.default.join(this.dir, "identity.json");
1721
2161
  try {
1722
- const parsed = JSON.parse(await import_promises4.default.readFile(file, "utf8"));
2162
+ const parsed = JSON.parse(await import_promises6.default.readFile(file, "utf8"));
1723
2163
  if (typeof parsed.companyId === "string" && typeof parsed.agentId === "string") {
1724
2164
  return { companyId: parsed.companyId, agentId: parsed.agentId };
1725
2165
  }
1726
2166
  } catch {
1727
2167
  }
1728
- const identity = { companyId: (0, import_node_crypto3.randomUUID)(), agentId: (0, import_node_crypto3.randomUUID)() };
1729
- await import_promises4.default.writeFile(file, JSON.stringify(identity), { mode: 384 });
2168
+ const identity = { companyId: (0, import_node_crypto4.randomUUID)(), agentId: (0, import_node_crypto4.randomUUID)() };
2169
+ await import_promises6.default.writeFile(file, JSON.stringify(identity), { mode: 384 });
1730
2170
  return identity;
1731
2171
  }
1732
2172
  };
1733
2173
 
1734
2174
  // src/engine/user-defaults.ts
1735
- var import_promises5 = __toESM(require("fs/promises"), 1);
2175
+ var import_promises7 = __toESM(require("fs/promises"), 1);
1736
2176
  var import_node_os4 = __toESM(require("os"), 1);
1737
- var import_node_path6 = __toESM(require("path"), 1);
2177
+ var import_node_path8 = __toESM(require("path"), 1);
1738
2178
  async function readJson(file) {
1739
2179
  try {
1740
- const parsed = JSON.parse(await import_promises5.default.readFile(file, "utf8"));
2180
+ const parsed = JSON.parse(await import_promises7.default.readFile(file, "utf8"));
1741
2181
  return typeof parsed === "object" && parsed !== null ? parsed : null;
1742
2182
  } catch {
1743
2183
  return null;
@@ -1748,8 +2188,8 @@ function str3(value) {
1748
2188
  }
1749
2189
  async function claudeDefaults(cwd, home = import_node_os4.default.homedir()) {
1750
2190
  const files = [
1751
- ...cwd ? [import_node_path6.default.join(cwd, ".claude", "settings.local.json"), import_node_path6.default.join(cwd, ".claude", "settings.json")] : [],
1752
- import_node_path6.default.join(home, ".claude", "settings.json")
2191
+ ...cwd ? [import_node_path8.default.join(cwd, ".claude", "settings.local.json"), import_node_path8.default.join(cwd, ".claude", "settings.json")] : [],
2192
+ import_node_path8.default.join(home, ".claude", "settings.json")
1753
2193
  ];
1754
2194
  let model = null;
1755
2195
  let effort = null;
@@ -1765,7 +2205,7 @@ async function claudeDefaults(cwd, home = import_node_os4.default.homedir()) {
1765
2205
  async function codexDefaults(home = import_node_os4.default.homedir()) {
1766
2206
  let text;
1767
2207
  try {
1768
- text = await import_promises5.default.readFile(import_node_path6.default.join(home, ".codex", "config.toml"), "utf8");
2208
+ text = await import_promises7.default.readFile(import_node_path8.default.join(home, ".codex", "config.toml"), "utf8");
1769
2209
  } catch {
1770
2210
  return { model: null, effort: null };
1771
2211
  }
@@ -1789,31 +2229,31 @@ var EFFORT_LEVELS = {
1789
2229
  // src/engine/index.ts
1790
2230
  var RAW_TRANSCRIPT_MAX_BYTES = 200 * 1048576;
1791
2231
  function moduleDir() {
1792
- return import_node_path7.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
2232
+ return import_node_path9.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
1793
2233
  }
1794
2234
  function resolveWorkerPath() {
1795
2235
  const dir = moduleDir();
1796
2236
  for (const candidate of [
1797
- import_node_path7.default.join(dir, "worker.js"),
1798
- import_node_path7.default.join(dir, "worker.ts"),
1799
- import_node_path7.default.join(dir, "..", "engine", "worker.js"),
1800
- import_node_path7.default.join(dir, "..", "engine", "worker.ts")
2237
+ import_node_path9.default.join(dir, "worker.js"),
2238
+ import_node_path9.default.join(dir, "worker.ts"),
2239
+ import_node_path9.default.join(dir, "..", "engine", "worker.js"),
2240
+ import_node_path9.default.join(dir, "..", "engine", "worker.ts")
1801
2241
  ]) {
1802
2242
  if ((0, import_node_fs4.existsSync)(candidate)) return candidate;
1803
2243
  }
1804
2244
  throw new Error(`[coding-sessions] The driver worker is missing next to ${dir}.`);
1805
2245
  }
1806
2246
  async function installShim(dataDir) {
1807
- const source = import_node_path7.default.resolve(moduleDir(), "..", "..", "bin", "cli-shim.sh");
2247
+ const source = import_node_path9.default.resolve(moduleDir(), "..", "..", "bin", "cli-shim.sh");
1808
2248
  if (!(0, import_node_fs4.existsSync)(source)) throw new Error(`[coding-sessions] The CLI shim is missing at ${source}.`);
1809
- const body = await import_promises6.default.readFile(source, "utf8");
1810
- const target = import_node_path7.default.join(dataDir, "bin", "cli-shim.sh");
1811
- const current = await import_promises6.default.readFile(target, "utf8").catch(() => null);
2249
+ const body = await import_promises8.default.readFile(source, "utf8");
2250
+ const target = import_node_path9.default.join(dataDir, "bin", "cli-shim.sh");
2251
+ const current = await import_promises8.default.readFile(target, "utf8").catch(() => null);
1812
2252
  if (current !== body) {
1813
- await import_promises6.default.mkdir(import_node_path7.default.dirname(target), { recursive: true });
1814
- await import_promises6.default.writeFile(target, body, { mode: 493 });
2253
+ await import_promises8.default.mkdir(import_node_path9.default.dirname(target), { recursive: true });
2254
+ await import_promises8.default.writeFile(target, body, { mode: 493 });
1815
2255
  }
1816
- await import_promises6.default.chmod(target, 493);
2256
+ await import_promises8.default.chmod(target, 493);
1817
2257
  return target;
1818
2258
  }
1819
2259
  function nowIso() {
@@ -1913,29 +2353,30 @@ function describeAuthStatus(provider, probe) {
1913
2353
  };
1914
2354
  }
1915
2355
  async function createCodingSessionsEngine(options = {}) {
1916
- const dataDir = options.dataDir ?? import_node_path7.default.join(import_node_os5.default.homedir(), ".matrx", "coding-sessions");
1917
- const stateDir = import_node_path7.default.join(dataDir, "paperclip-state");
2356
+ const dataDir = options.dataDir ?? import_node_path9.default.join(import_node_os5.default.homedir(), ".matrx", "coding-sessions");
2357
+ const stateDir = import_node_path9.default.join(dataDir, "paperclip-state");
1918
2358
  const homes = options.homes ?? defaultProviderHomes();
1919
2359
  const index = new RunIndex(dataDir);
1920
2360
  await index.load();
1921
2361
  const preferences = new PreferenceStore(dataDir);
1922
2362
  await preferences.load();
1923
- await import_promises6.default.mkdir(stateDir, { recursive: true });
2363
+ await import_promises8.default.mkdir(stateDir, { recursive: true });
1924
2364
  const identity = await index.identity();
2365
+ const agents = createAgentConnection(options.agents, dataDir);
1925
2366
  const backups = options.rawBackup ? createRawBackupScheduler({
1926
2367
  sink: options.rawBackup,
1927
2368
  findFile: (provider, providerSessionId) => findSessionFile(provider, providerSessionId, homes),
1928
2369
  onError: (error) => console.warn("[coding-sessions] raw transcript backup failed:", error)
1929
2370
  }) : null;
1930
2371
  const discoveredSeen = /* @__PURE__ */ new Map();
1931
- const healthFile = import_node_path7.default.join(dataDir, "provider-health.json");
2372
+ const healthFile = import_node_path9.default.join(dataDir, "provider-health.json");
1932
2373
  let health = {};
1933
2374
  try {
1934
- health = JSON.parse(await import_promises6.default.readFile(healthFile, "utf8"));
2375
+ health = JSON.parse(await import_promises8.default.readFile(healthFile, "utf8"));
1935
2376
  } catch {
1936
2377
  health = {};
1937
2378
  }
1938
- const saveHealth = () => import_promises6.default.writeFile(healthFile, JSON.stringify(health), { mode: 384 }).catch(() => void 0);
2379
+ const saveHealth = () => import_promises8.default.writeFile(healthFile, JSON.stringify(health), { mode: 384 }).catch(() => void 0);
1939
2380
  function noteRunOutcome(provider, status, texts) {
1940
2381
  if (status === "completed") {
1941
2382
  if (health[provider]) {
@@ -2052,7 +2493,7 @@ async function createCodingSessionsEngine(options = {}) {
2052
2493
  async function startRun(session, prompt) {
2053
2494
  const provider = session.provider;
2054
2495
  const realCommand = await commandFor(provider);
2055
- const runId = (0, import_node_crypto4.randomUUID)();
2496
+ const runId = (0, import_node_crypto5.randomUUID)();
2056
2497
  const model = session.model ?? (provider === "claude" ? (await userDefaults(provider, session.cwd)).model ?? null : null);
2057
2498
  const run = {
2058
2499
  runId,
@@ -2090,7 +2531,36 @@ async function createCodingSessionsEngine(options = {}) {
2090
2531
  let fileOffset = null;
2091
2532
  if (session.providerSessionId) {
2092
2533
  const file = await findSessionFile(provider, session.providerSessionId, homes);
2093
- if (file) fileOffset = (await import_promises6.default.stat(file).catch(() => null))?.size ?? null;
2534
+ if (file) fileOffset = (await import_promises8.default.stat(file).catch(() => null))?.size ?? null;
2535
+ }
2536
+ let agentArgs = [];
2537
+ let cliPrompt = prompt;
2538
+ let delivered = null;
2539
+ if (!session.agent && session.agentRemovedDue) {
2540
+ if (provider === "codex") cliPrompt = `${AGENT_REMOVED_BLOCK}${prompt}`;
2541
+ delivered = { agentRemovedDue: false };
2542
+ }
2543
+ if (session.agent) {
2544
+ const bundle = await agents.readSessionBundle(session.id);
2545
+ if (!bundle) {
2546
+ const failed = saveRun({
2547
+ ...run,
2548
+ status: "failed",
2549
+ endedAt: nowIso(),
2550
+ error: `The agent ${session.agent.name} could not be loaded on this computer anymore. Start a new session to load it again.`
2551
+ });
2552
+ const current = index.session(session.id);
2553
+ if (current) saveSession({ ...current, lastRunStatus: "failed" });
2554
+ return failed;
2555
+ }
2556
+ const attachment = await writeAgentAttachment(agents.sessionDir(session.id), bundle, {
2557
+ mcpUrl: await agents.mcpUrlFor(session.id)
2558
+ });
2559
+ agentArgs = agentArgsFor(provider, attachment, { resuming: session.providerSessionId !== null });
2560
+ if (provider === "codex" && session.agent.blockDue) {
2561
+ cliPrompt = `${renderFallbackBlock(bundle, import_node_path9.default.join(attachment.dir, "skills"))}${prompt}`;
2562
+ }
2563
+ delivered = { agent: { ...session.agent, blockDue: false, deliveredAt: run.startedAt } };
2094
2564
  }
2095
2565
  const child = forkWorker();
2096
2566
  const liveRun = {
@@ -2106,6 +2576,10 @@ async function createCodingSessionsEngine(options = {}) {
2106
2576
  };
2107
2577
  live.set(runId, liveRun);
2108
2578
  emitItem(liveRun, { kind: "user", id: `run-${runId}`, text: prompt, at: run.startedAt });
2579
+ if (delivered) {
2580
+ const current = index.session(session.id);
2581
+ if (current) saveSession({ ...current, ...delivered });
2582
+ }
2109
2583
  const splitter = createLineSplitter();
2110
2584
  const claude = provider === "claude" ? createClaudeNormalizer() : null;
2111
2585
  const codex = provider === "codex" ? createCodexStreamNormalizer(runId.slice(0, 8)) : null;
@@ -2144,6 +2618,7 @@ async function createCodingSessionsEngine(options = {}) {
2144
2618
  }
2145
2619
  }
2146
2620
  void pruneDriverScratch();
2621
+ if (provider === "codex" && final.status === "completed") void checkCompaction(session.id);
2147
2622
  };
2148
2623
  let settled = false;
2149
2624
  child.on("message", (raw) => {
@@ -2258,16 +2733,39 @@ async function createCodingSessionsEngine(options = {}) {
2258
2733
  provider,
2259
2734
  runId,
2260
2735
  cwd: session.cwd,
2261
- prompt,
2736
+ prompt: cliPrompt,
2262
2737
  model,
2263
2738
  effort: session.effort ?? null,
2264
2739
  resumeSessionId: session.providerSessionId,
2265
2740
  shimPath,
2266
2741
  realCommand,
2267
- identity
2742
+ identity,
2743
+ agentArgs
2268
2744
  });
2269
2745
  return run;
2270
2746
  }
2747
+ async function checkCompaction(sessionId) {
2748
+ const session = index.session(sessionId);
2749
+ if (!session?.agent || session.agent.blockDue || !session.providerSessionId) return;
2750
+ const file = await findSessionFile("codex", session.providerSessionId, homes);
2751
+ if (!file || !await codexCompactionDroppedAgent(file, session.agent.deliveredAt ?? null)) return;
2752
+ const current = index.session(sessionId);
2753
+ if (current?.agent) {
2754
+ saveSession({ ...current, agent: { ...current.agent, blockDue: true } });
2755
+ }
2756
+ }
2757
+ async function attachAgent(sessionId, selection, options2) {
2758
+ const bundle = await agents.loadBundle(selection);
2759
+ await agents.saveSessionBundle(sessionId, bundle);
2760
+ return {
2761
+ ...sessionAgentRecord(bundle, {
2762
+ toolsServed: await agents.mcpUrlFor(sessionId) !== null,
2763
+ variables: selection.variables ?? {}
2764
+ }),
2765
+ blockDue: options2.blockDue,
2766
+ deliveredAt: null
2767
+ };
2768
+ }
2271
2769
  function killGroup(liveRun) {
2272
2770
  const target = liveRun.pgid;
2273
2771
  if (target === null) return;
@@ -2283,7 +2781,7 @@ async function createCodingSessionsEngine(options = {}) {
2283
2781
  }, 3e3);
2284
2782
  }
2285
2783
  async function pruneDriverScratch() {
2286
- const runsDir = import_node_path7.default.join(
2784
+ const runsDir = import_node_path9.default.join(
2287
2785
  stateDir,
2288
2786
  "instances",
2289
2787
  "default",
@@ -2294,9 +2792,9 @@ async function createCodingSessionsEngine(options = {}) {
2294
2792
  "claude-runtime",
2295
2793
  "runs"
2296
2794
  );
2297
- const entries = await import_promises6.default.readdir(runsDir).catch(() => []);
2795
+ const entries = await import_promises8.default.readdir(runsDir).catch(() => []);
2298
2796
  await Promise.all(
2299
- entries.filter((name) => !live.has(name)).map((name) => import_promises6.default.rm(import_node_path7.default.join(runsDir, name), { recursive: true, force: true }).catch(() => void 0))
2797
+ entries.filter((name) => !live.has(name)).map((name) => import_promises8.default.rm(import_node_path9.default.join(runsDir, name), { recursive: true, force: true }).catch(() => void 0))
2300
2798
  );
2301
2799
  }
2302
2800
  async function probe() {
@@ -2337,7 +2835,7 @@ async function createCodingSessionsEngine(options = {}) {
2337
2835
  function newSession(input) {
2338
2836
  const at = nowIso();
2339
2837
  return {
2340
- id: (0, import_node_crypto4.randomUUID)(),
2838
+ id: (0, import_node_crypto5.randomUUID)(),
2341
2839
  provider: input.provider,
2342
2840
  providerSessionId: input.providerSessionId,
2343
2841
  cwd: input.cwd,
@@ -2355,17 +2853,17 @@ async function createCodingSessionsEngine(options = {}) {
2355
2853
  function resolveFolder(raw) {
2356
2854
  const trimmed = raw.trim();
2357
2855
  if (!trimmed) return null;
2358
- return import_node_path7.default.resolve(trimmed.replace(/^~(?=$|\/)/, import_node_os5.default.homedir()));
2856
+ return import_node_path9.default.resolve(trimmed.replace(/^~(?=$|\/)/, import_node_os5.default.homedir()));
2359
2857
  }
2360
2858
  async function checkFolder(raw) {
2361
2859
  const target = resolveFolder(raw);
2362
2860
  if (!target) return { path: "", exists: false, isDirectory: false };
2363
- const stat = await import_promises6.default.stat(target).catch(() => null);
2861
+ const stat = await import_promises8.default.stat(target).catch(() => null);
2364
2862
  return { path: target, exists: stat !== null, isDirectory: stat?.isDirectory() === true };
2365
2863
  }
2366
2864
  async function assertFolder(cwd) {
2367
- const resolved = import_node_path7.default.resolve(cwd.replace(/^~(?=$|\/)/, import_node_os5.default.homedir()));
2368
- const stat = await import_promises6.default.stat(resolved).catch(() => null);
2865
+ const resolved = import_node_path9.default.resolve(cwd.replace(/^~(?=$|\/)/, import_node_os5.default.homedir()));
2866
+ const stat = await import_promises8.default.stat(resolved).catch(() => null);
2369
2867
  if (!stat?.isDirectory()) {
2370
2868
  throw new Error(`The folder ${resolved} doesn't exist on this computer.`);
2371
2869
  }
@@ -2427,7 +2925,7 @@ async function createCodingSessionsEngine(options = {}) {
2427
2925
  folders.map(async ([folder, lastUsedAt]) => ({
2428
2926
  path: folder,
2429
2927
  lastUsedAt,
2430
- exists: (await import_promises6.default.stat(folder).catch(() => null))?.isDirectory() === true
2928
+ exists: (await import_promises8.default.stat(folder).catch(() => null))?.isDirectory() === true
2431
2929
  }))
2432
2930
  );
2433
2931
  },
@@ -2472,7 +2970,7 @@ async function createCodingSessionsEngine(options = {}) {
2472
2970
  async createFolder(raw) {
2473
2971
  const target = resolveFolder(raw);
2474
2972
  if (!target) throw new Error("Type a folder path first.");
2475
- await import_promises6.default.mkdir(target, { recursive: true });
2973
+ await import_promises8.default.mkdir(target, { recursive: true });
2476
2974
  return checkFolder(target);
2477
2975
  },
2478
2976
  async sessionActivity(provider, providerSessionId) {
@@ -2510,48 +3008,63 @@ async function createCodingSessionsEngine(options = {}) {
2510
3008
  reason: `${PROVIDER_LABEL[provider]} has no saved transcript for this chat on this computer.`
2511
3009
  };
2512
3010
  }
2513
- const stat = await import_promises6.default.stat(file);
3011
+ const stat = await import_promises8.default.stat(file);
2514
3012
  if (stat.size > RAW_TRANSCRIPT_MAX_BYTES) {
2515
3013
  return {
2516
3014
  available: false,
2517
3015
  reason: `This transcript is ${(0, import_format.formatFileSize)(stat.size)}, larger than the ${(0, import_format.formatFileSize)(RAW_TRANSCRIPT_MAX_BYTES)} this page can hand over. It is at ${file}.`
2518
3016
  };
2519
3017
  }
2520
- const content = await import_promises6.default.readFile(file, "utf8");
2521
- return { available: true, fileName: import_node_path7.default.basename(file), bytes: stat.size, content };
3018
+ const content = await import_promises8.default.readFile(file, "utf8");
3019
+ return { available: true, fileName: import_node_path9.default.basename(file), bytes: stat.size, content };
2522
3020
  },
2523
3021
  async start(input) {
2524
3022
  const prompt = input.prompt.trim();
2525
3023
  if (!prompt) throw new Error("Type what you want done before starting.");
2526
3024
  const cwd = await assertFolder(input.cwd);
2527
- const session = saveSession(
2528
- newSession({
2529
- provider: input.provider,
2530
- cwd,
2531
- title: titleFrom(prompt),
2532
- model: input.model?.trim() || null,
2533
- effort: input.effort?.trim() || null,
2534
- origin: "started",
2535
- providerSessionId: null
2536
- })
2537
- );
3025
+ const draft = newSession({
3026
+ provider: input.provider,
3027
+ cwd,
3028
+ title: titleFrom(prompt),
3029
+ model: input.model?.trim() || null,
3030
+ effort: input.effort?.trim() || null,
3031
+ origin: "started",
3032
+ providerSessionId: null
3033
+ });
3034
+ if (input.agent) draft.agent = await attachAgent(draft.id, input.agent, { blockDue: false });
3035
+ const session = saveSession(draft);
2538
3036
  const run = await startRun(session, prompt);
2539
3037
  return { session: index.session(session.id) ?? session, run, queued: false };
2540
3038
  },
2541
3039
  async takeover(input) {
2542
3040
  const existing = index.session(input.providerSessionId);
2543
- if (existing) return existing;
3041
+ if (existing) {
3042
+ if (!input.agent) return existing;
3043
+ return engine.setAgent(existing.id, input.agent);
3044
+ }
2544
3045
  const cwd = await assertFolder(input.cwd);
2545
- return saveSession(
2546
- newSession({
2547
- provider: input.provider,
2548
- cwd,
2549
- title: input.title?.trim() || "Taken-over chat",
2550
- model: null,
2551
- origin: "takeover",
2552
- providerSessionId: input.providerSessionId
2553
- })
2554
- );
3046
+ const draft = newSession({
3047
+ provider: input.provider,
3048
+ cwd,
3049
+ title: input.title?.trim() || "Taken-over chat",
3050
+ model: null,
3051
+ origin: "takeover",
3052
+ providerSessionId: input.providerSessionId
3053
+ });
3054
+ if (input.agent) draft.agent = await attachAgent(draft.id, input.agent, { blockDue: input.provider === "codex" });
3055
+ return saveSession(draft);
3056
+ },
3057
+ async setAgent(sessionId, selection) {
3058
+ const session = index.session(String(sessionId ?? ""));
3059
+ if (!session) throw new Error("This session is not in the list anymore.");
3060
+ if (!selection) {
3061
+ const told = session.provider === "codex" && session.agent?.deliveredAt != null;
3062
+ return saveSession({ ...session, agent: null, agentRemovedDue: told });
3063
+ }
3064
+ const agent = await attachAgent(session.id, selection, {
3065
+ blockDue: session.provider === "codex" && session.providerSessionId !== null
3066
+ });
3067
+ return saveSession({ ...session, agent, agentRemovedDue: false });
2555
3068
  },
2556
3069
  async send(sessionId, rawPrompt) {
2557
3070
  const prompt = rawPrompt.trim();
@@ -2603,6 +3116,21 @@ async function createCodingSessionsEngine(options = {}) {
2603
3116
  if (opts && "effort" in opts) next.effort = typeof opts.effort === "string" && opts.effort.trim() ? opts.effort.trim() : null;
2604
3117
  return saveSession(next);
2605
3118
  },
3119
+ async agentAccess() {
3120
+ return agents.access();
3121
+ },
3122
+ async agentCatalogRpc(request) {
3123
+ return agents.catalogRpc(request);
3124
+ },
3125
+ async agentDetails(agentId) {
3126
+ return agents.details(String(agentId ?? ""));
3127
+ },
3128
+ setMcpBaseUrl(baseUrl) {
3129
+ agents.setMcpBaseUrl(baseUrl);
3130
+ },
3131
+ async handleMcp(secret, body) {
3132
+ return agents.handleMcp(secret, body);
3133
+ },
2606
3134
  async dispose() {
2607
3135
  for (const liveRun of live.values()) {
2608
3136
  liveRun.stopRequested = true;
@@ -2652,6 +3180,8 @@ function openFolderIn(folder, target) {
2652
3180
  }
2653
3181
  // Annotate the CommonJS export names for ESM import in node:
2654
3182
  0 && (module.exports = {
3183
+ DEFAULT_SERVER_URL,
3184
+ DEFAULT_SUPABASE_URL,
2655
3185
  EFFORT_LEVELS,
2656
3186
  PreferenceStore,
2657
3187
  RAW_TRANSCRIPT_MAX_BYTES,