@ai-matrx/coding-sessions 0.1.83 → 0.1.85

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.
@@ -61,7 +61,7 @@ var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
61
61
 
62
62
  // src/engine/index.ts
63
63
  var import_node_child_process3 = require("child_process");
64
- var import_node_crypto4 = require("crypto");
64
+ var import_node_crypto5 = require("crypto");
65
65
  var import_node_fs4 = require("fs");
66
66
  var import_format = require("@ai-matrx/kit/format");
67
67
 
@@ -159,10 +159,379 @@ function createAiMatrxRawBackupSink(options) {
159
159
  };
160
160
  }
161
161
 
162
+ // src/engine/agent-attach.ts
163
+ var import_promises2 = __toESM(require("fs/promises"), 1);
164
+ var import_node_path = __toESM(require("path"), 1);
165
+ var AGENT_MCP_SERVER_NAME = "matrx-agent";
166
+ var AGENT_PLUGIN_NAME = "matrx-agent";
167
+ function escapeAttr(value) {
168
+ return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;");
169
+ }
170
+ function versionWords(bundle) {
171
+ return bundle.version_number !== null ? String(bundle.version_number) : "latest";
172
+ }
173
+ function renderAgentInstructions(bundle, options = {}) {
174
+ const included = bundle.skills.filter((skill) => skill.tier === "included");
175
+ const listed2 = bundle.skills.filter((skill) => skill.tier === "listed");
176
+ const parts = [`<ai_matrx_agent name="${escapeAttr(bundle.name)}" version="${versionWords(bundle)}">`];
177
+ if (included.length > 0) {
178
+ parts.push(
179
+ "## Skills (always in effect)",
180
+ ...included.map((skill) => `### ${skill.label}
181
+
182
+ ${skill.body.trim()}`)
183
+ );
184
+ }
185
+ if (options.claudeSkills && listed2.length > 0) {
186
+ parts.push(
187
+ [
188
+ "## Your skills",
189
+ "When a request matches one of these, load it with your Skill tool and follow it before answering:",
190
+ ...listed2.map((skill) => `- ${AGENT_PLUGIN_NAME}:${skill.slug} \u2014 ${skill.description || skill.label}`)
191
+ ].join("\n")
192
+ );
193
+ }
194
+ if (included.length > 0 || options.claudeSkills && listed2.length > 0) parts.push("## Your instructions");
195
+ parts.push(bundle.instructions.trim());
196
+ parts.push("</ai_matrx_agent>");
197
+ return `${parts.filter(Boolean).join("\n\n")}
198
+ `;
199
+ }
200
+ function renderCodexSkillsList(bundle, skillsRoot) {
201
+ const listed2 = bundle.skills.filter((skill) => skill.tier === "listed");
202
+ if (listed2.length === 0) return "";
203
+ const lines = [
204
+ "<skills_instructions>",
205
+ "## AI Matrx agent skills",
206
+ "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.",
207
+ "### Available skills",
208
+ ...listed2.map(
209
+ (skill) => `- ${skill.slug}: ${skill.description || skill.label} (file: ${import_node_path.default.join(skillsRoot, skill.slug, "SKILL.md")})`
210
+ ),
211
+ "</skills_instructions>"
212
+ ];
213
+ return lines.join("\n");
214
+ }
215
+ function tomlString(value) {
216
+ const scalar = value.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, "\uFFFD");
217
+ return JSON.stringify(scalar).replace(/\u007f/g, "\\u007f");
218
+ }
219
+ async function writeAgentAttachment(dir, bundle, options) {
220
+ const pluginDir = import_node_path.default.join(dir, "plugin");
221
+ const skillsRoot = import_node_path.default.join(dir, "skills");
222
+ await import_promises2.default.mkdir(import_node_path.default.join(pluginDir, ".claude-plugin"), { recursive: true, mode: 448 });
223
+ await import_promises2.default.mkdir(skillsRoot, { recursive: true, mode: 448 });
224
+ const instructions = renderAgentInstructions(bundle);
225
+ const instructionsFile = import_node_path.default.join(dir, "instructions.md");
226
+ await import_promises2.default.writeFile(instructionsFile, renderAgentInstructions(bundle, { claudeSkills: true }), { mode: 384 });
227
+ await import_promises2.default.writeFile(
228
+ import_node_path.default.join(pluginDir, ".claude-plugin", "plugin.json"),
229
+ `${JSON.stringify(
230
+ {
231
+ name: AGENT_PLUGIN_NAME,
232
+ description: `Skills of the AI Matrx agent "${bundle.name}"`,
233
+ version: versionWords(bundle) === "latest" ? "1.0.0" : `${versionWords(bundle)}.0.0`
234
+ },
235
+ null,
236
+ 2
237
+ )}
238
+ `,
239
+ { mode: 384 }
240
+ );
241
+ const listed2 = bundle.skills.filter((skill) => skill.tier === "listed");
242
+ const wanted = new Set(listed2.map((skill) => skill.slug));
243
+ for (const root of [import_node_path.default.join(pluginDir, "skills"), skillsRoot]) {
244
+ await import_promises2.default.mkdir(root, { recursive: true, mode: 448 });
245
+ for (const existing of await import_promises2.default.readdir(root).catch(() => [])) {
246
+ if (!wanted.has(existing)) await import_promises2.default.rm(import_node_path.default.join(root, existing), { recursive: true, force: true });
247
+ }
248
+ for (const skill of listed2) {
249
+ const skillDir = import_node_path.default.join(root, skill.slug);
250
+ await import_promises2.default.mkdir(skillDir, { recursive: true, mode: 448 });
251
+ await import_promises2.default.writeFile(import_node_path.default.join(skillDir, "SKILL.md"), skill.content, { mode: 384 });
252
+ }
253
+ }
254
+ let mcpConfigFile = null;
255
+ const mcpUrl = options.mcpUrl && bundle.tools.length > 0 ? options.mcpUrl : null;
256
+ const mcpPath = import_node_path.default.join(dir, "mcp.json");
257
+ if (mcpUrl) {
258
+ mcpConfigFile = mcpPath;
259
+ await import_promises2.default.writeFile(
260
+ mcpPath,
261
+ `${JSON.stringify({ mcpServers: { [AGENT_MCP_SERVER_NAME]: { type: "http", url: mcpUrl } } }, null, 2)}
262
+ `,
263
+ { mode: 384 }
264
+ );
265
+ } else {
266
+ await import_promises2.default.rm(mcpPath, { force: true });
267
+ }
268
+ const skillsList = renderCodexSkillsList(bundle, skillsRoot);
269
+ return {
270
+ dir,
271
+ instructionsFile,
272
+ pluginDir,
273
+ mcpConfigFile,
274
+ mcpUrl,
275
+ // Same order as Claude's: the skills list first, the agent's words last.
276
+ codexInstructions: skillsList ? `${skillsList}
277
+
278
+ ${instructions}` : instructions
279
+ };
280
+ }
281
+ function agentArgsFor(provider, attachment, options) {
282
+ if (provider === "claude") {
283
+ const args2 = [
284
+ // Every turn: without snapshot off, a resumed Claude drops appended instructions.
285
+ "--append-system-prompt-file",
286
+ attachment.instructionsFile,
287
+ "--system-prompt-snapshot",
288
+ "off",
289
+ "--plugin-dir",
290
+ attachment.pluginDir
291
+ ];
292
+ if (attachment.mcpConfigFile) args2.push("--mcp-config", attachment.mcpConfigFile);
293
+ return args2;
294
+ }
295
+ const args = [];
296
+ if (!options.resuming) args.push("-c", `developer_instructions=${tomlString(attachment.codexInstructions)}`);
297
+ if (attachment.mcpUrl) {
298
+ const key = `mcp_servers.${AGENT_MCP_SERVER_NAME}`;
299
+ args.push(
300
+ "-c",
301
+ `${key}.url=${tomlString(attachment.mcpUrl)}`,
302
+ "-c",
303
+ `${key}.default_tools_approval_mode="approve"`
304
+ );
305
+ }
306
+ return args;
307
+ }
308
+ function sessionAgentRecord(bundle, options) {
309
+ const unavailable = bundle.unavailable.map((u) => ({ kind: u.kind, name: u.name, reason: u.reason }));
310
+ const missingSkills = unavailable.filter((u) => u.kind === "skill").length;
311
+ const missingTools = unavailable.filter((u) => u.kind === "tool").length;
312
+ if (!options.toolsServed && bundle.tools.length > 0) {
313
+ for (const tool of bundle.tools) {
314
+ unavailable.push({ kind: "tool", name: tool.canonical_name, reason: "Tools need the coding sessions host" });
315
+ }
316
+ }
317
+ const toolsLoaded = options.toolsServed ? bundle.tools.length : 0;
318
+ const loaded = [
319
+ { part: "instructions", loaded: bundle.instructions.trim() ? 1 : 0, total: 1 },
320
+ { part: "skills", loaded: bundle.skills.length, total: bundle.skills.length + missingSkills },
321
+ { part: "tools", loaded: toolsLoaded, total: bundle.tools.length + missingTools }
322
+ ];
323
+ return {
324
+ agentId: bundle.agent_id,
325
+ versionId: bundle.version_id,
326
+ versionNumber: bundle.version_number,
327
+ name: bundle.name,
328
+ definitionHash: bundle.definition_hash,
329
+ loaded,
330
+ unavailable
331
+ };
332
+ }
333
+
334
+ // src/engine/agents.ts
335
+ var import_node_crypto2 = require("crypto");
336
+ var import_promises3 = __toESM(require("fs/promises"), 1);
337
+ var import_node_path2 = __toESM(require("path"), 1);
338
+ var DEFAULT_SERVER_URL = "https://server.app.matrxserver.com";
339
+ var DEFAULT_SUPABASE_URL = "https://db.matrxserver.com";
340
+ var AGENT_CATALOG_RPCS = /* @__PURE__ */ new Set([
341
+ "agx_get_list_full",
342
+ "agx_search",
343
+ "agx_resolve_agent_address",
344
+ "ues_get_bulk",
345
+ "ues_list",
346
+ "ues_set",
347
+ "ues_touch"
348
+ ]);
349
+ var agentsClient = async () => {
350
+ const [matrx, portable, mcp] = await Promise.all([
351
+ import("@ai-matrx/agents/matrx"),
352
+ import("@ai-matrx/agents/portable"),
353
+ import("@ai-matrx/agents/portable/mcp")
354
+ ]);
355
+ return { ...matrx, ...portable, ...mcp };
356
+ };
357
+ var SIGNED_OUT_AGENT_MESSAGE = "Sign in to AI Matrx to use your agents.";
358
+ function userIdOf(credential) {
359
+ if (!credential || credential.kind !== "user") return null;
360
+ const payload = credential.accessToken.split(".")[1];
361
+ if (!payload) return null;
362
+ try {
363
+ const claims = JSON.parse(Buffer.from(payload, "base64url").toString("utf8"));
364
+ return typeof claims.sub === "string" ? claims.sub : null;
365
+ } catch {
366
+ return null;
367
+ }
368
+ }
369
+ function createAgentConnection(options, dataDir) {
370
+ const credentials = options?.credentials ?? null;
371
+ const serverUrl = (options?.serverUrl ?? DEFAULT_SERVER_URL).replace(/\/+$/, "");
372
+ const supabaseUrl = (options?.supabaseUrl ?? DEFAULT_SUPABASE_URL).replace(/\/+$/, "");
373
+ const supabaseKey = options?.supabaseKey ?? null;
374
+ const root = import_node_path2.default.join(dataDir, "agents");
375
+ let mcpBaseUrl = null;
376
+ const handlers = /* @__PURE__ */ new Map();
377
+ const secretToSession = /* @__PURE__ */ new Map();
378
+ const organizationId = () => {
379
+ const raw = typeof options?.organizationId === "function" ? options.organizationId() : options?.organizationId;
380
+ return raw && raw.trim() ? raw.trim() : null;
381
+ };
382
+ const transport = async () => (await agentsClient()).createMatrxTransport({
383
+ baseUrl: serverUrl,
384
+ ...credentials ? { credentials } : {},
385
+ ...organizationId() ? { organizationId: () => organizationId() } : {},
386
+ source: "codingSessionsAgents"
387
+ });
388
+ const credential = async () => credentials ? credentials.get() : null;
389
+ const requireSignedIn = async () => {
390
+ if (!await credential()) throw new Error(SIGNED_OUT_AGENT_MESSAGE);
391
+ };
392
+ const sessionDir = (sessionId) => import_node_path2.default.join(root, sessionId.replace(/[^A-Za-z0-9_-]/g, "_"));
393
+ async function meta(sessionId) {
394
+ const file = import_node_path2.default.join(sessionDir(sessionId), "session.json");
395
+ const existing = await import_promises3.default.readFile(file, "utf8").then((text) => JSON.parse(text)).catch(() => null);
396
+ if (existing?.mcpSecret && existing.conversationId) return existing;
397
+ const created = { mcpSecret: (0, import_node_crypto2.randomBytes)(24).toString("base64url"), conversationId: (0, import_node_crypto2.randomUUID)() };
398
+ await import_promises3.default.mkdir(sessionDir(sessionId), { recursive: true, mode: 448 });
399
+ await import_promises3.default.writeFile(file, JSON.stringify(created), { mode: 384 });
400
+ return created;
401
+ }
402
+ async function findSessionBySecret(secret) {
403
+ for (const name of await import_promises3.default.readdir(root).catch(() => [])) {
404
+ 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);
405
+ if (found) {
406
+ secretToSession.set(secret, name);
407
+ return name;
408
+ }
409
+ }
410
+ return null;
411
+ }
412
+ async function readSessionBundle(sessionId) {
413
+ return import_promises3.default.readFile(import_node_path2.default.join(sessionDir(sessionId), "bundle.json"), "utf8").then((text) => JSON.parse(text)).catch(() => null);
414
+ }
415
+ return {
416
+ async access() {
417
+ const current = await credential().catch(() => null);
418
+ const userId = userIdOf(current);
419
+ return {
420
+ configured: credentials !== null && supabaseKey !== null,
421
+ signedIn: userId !== null,
422
+ userId,
423
+ organizationId: organizationId()
424
+ };
425
+ },
426
+ async catalogRpc(request) {
427
+ if (!AGENT_CATALOG_RPCS.has(request.fn)) {
428
+ return { data: null, error: { message: `Not an agent catalogue read: ${request.fn}` } };
429
+ }
430
+ const current = await credential();
431
+ if (!current || current.kind !== "user") return { data: null, error: { message: SIGNED_OUT_AGENT_MESSAGE, code: "signed_out" } };
432
+ if (!supabaseKey) return { data: null, error: { message: "This host has no AI Matrx database key.", code: "not_configured" } };
433
+ const query = new URLSearchParams();
434
+ if (request.order) query.set("order", `${request.order.column}.${request.order.ascending === false ? "desc" : "asc"}`);
435
+ if (request.range) {
436
+ query.set("offset", String(request.range[0]));
437
+ query.set("limit", String(request.range[1] - request.range[0] + 1));
438
+ }
439
+ const headers = {
440
+ apikey: supabaseKey,
441
+ Authorization: `Bearer ${current.accessToken}`,
442
+ "Content-Type": "application/json",
443
+ // Every catalogue RPC lives in `public`; the database's default
444
+ // exposed schema is not `public`, so name it (supabase-js does).
445
+ "Content-Profile": "public",
446
+ "Accept-Profile": "public"
447
+ };
448
+ if (request.count) headers.Prefer = `count=${request.count}`;
449
+ const qs = query.toString();
450
+ const response = await fetch(`${supabaseUrl}/rest/v1/rpc/${encodeURIComponent(request.fn)}${qs ? `?${qs}` : ""}`, {
451
+ method: "POST",
452
+ headers,
453
+ body: JSON.stringify(request.args ?? {})
454
+ });
455
+ const text = await response.text();
456
+ const body = text ? JSON.parse(text) : null;
457
+ if (!response.ok) {
458
+ const err = body ?? {};
459
+ return {
460
+ data: null,
461
+ error: { message: err.message ?? `HTTP ${response.status}`, code: err.code, details: err.details ?? null, hint: err.hint ?? null }
462
+ };
463
+ }
464
+ const range = response.headers.get("content-range");
465
+ const total = range?.split("/")[1];
466
+ return { data: body, error: null, count: total && total !== "*" ? Number(total) : null };
467
+ },
468
+ async details(agentId) {
469
+ await requireSignedIn();
470
+ const { fetchPortableAgent, fetchPortableAgentVersions } = await agentsClient();
471
+ const [bundle, versions] = await Promise.all([
472
+ fetchPortableAgent(await transport(), { agentId }),
473
+ fetchPortableAgentVersions(await transport(), agentId).catch(() => [])
474
+ ]);
475
+ return {
476
+ agentId: bundle.agent_id,
477
+ name: bundle.name,
478
+ variables: bundle.variables,
479
+ versions: versions.map((v) => ({ id: v.id, versionNumber: v.version_number, changedAt: v.changed_at })).sort((a, b) => b.versionNumber - a.versionNumber),
480
+ latestVersionNumber: bundle.version_number
481
+ };
482
+ },
483
+ async loadBundle(selection) {
484
+ await requireSignedIn();
485
+ const { fetchPortableAgent } = await agentsClient();
486
+ return fetchPortableAgent(
487
+ await transport(),
488
+ selection.versionId ? { versionId: selection.versionId } : { agentId: selection.agentId },
489
+ { variables: selection.variables ?? {} }
490
+ );
491
+ },
492
+ async saveSessionBundle(sessionId, bundle) {
493
+ await import_promises3.default.mkdir(sessionDir(sessionId), { recursive: true, mode: 448 });
494
+ await import_promises3.default.writeFile(import_node_path2.default.join(sessionDir(sessionId), "bundle.json"), JSON.stringify(bundle), { mode: 384 });
495
+ await meta(sessionId);
496
+ },
497
+ readSessionBundle,
498
+ sessionDir,
499
+ async mcpUrlFor(sessionId) {
500
+ if (!mcpBaseUrl) return null;
501
+ const { mcpSecret } = await meta(sessionId);
502
+ secretToSession.set(mcpSecret, sessionId);
503
+ return `${mcpBaseUrl}/mcp/${mcpSecret}`;
504
+ },
505
+ setMcpBaseUrl(baseUrl) {
506
+ mcpBaseUrl = baseUrl ? baseUrl.replace(/\/+$/, "") : null;
507
+ },
508
+ async handleMcp(secret, body) {
509
+ let handler = handlers.get(secret);
510
+ if (!handler) {
511
+ const sessionId = secretToSession.get(secret) ?? await findSessionBySecret(secret);
512
+ if (!sessionId) return null;
513
+ const bundle = await readSessionBundle(sessionId);
514
+ if (!bundle) return null;
515
+ const { conversationId } = await meta(sessionId);
516
+ const { createAgentToolsMcpHandler } = await agentsClient();
517
+ handler = createAgentToolsMcpHandler({
518
+ bundle,
519
+ credentials,
520
+ organizationId: () => organizationId(),
521
+ baseUrl: serverUrl,
522
+ conversationId
523
+ });
524
+ handlers.set(secret, handler);
525
+ }
526
+ return handler.handle(body);
527
+ }
528
+ };
529
+ }
530
+
162
531
  // src/engine/index.ts
163
- var import_promises6 = __toESM(require("fs/promises"), 1);
532
+ var import_promises8 = __toESM(require("fs/promises"), 1);
164
533
  var import_node_os5 = __toESM(require("os"), 1);
165
- var import_node_path7 = __toESM(require("path"), 1);
534
+ var import_node_path9 = __toESM(require("path"), 1);
166
535
  var import_node_url = require("url");
167
536
 
168
537
  // src/normalize/claude.ts
@@ -832,7 +1201,7 @@ function describeProblem(provider, problem) {
832
1201
  var import_node_child_process = require("child_process");
833
1202
  var import_node_fs = require("fs");
834
1203
  var import_node_os = __toESM(require("os"), 1);
835
- var import_node_path = __toESM(require("path"), 1);
1204
+ var import_node_path3 = __toESM(require("path"), 1);
836
1205
  var SOURCE_LABEL = {
837
1206
  codex: { "desktop-app": "from ChatGPT app", path: "from PATH", "npm-global": "from npm", configured: "set by host" },
838
1207
  claude: { "desktop-app": "from Claude app", path: "from PATH", "npm-global": "from npm", configured: "set by host" }
@@ -881,10 +1250,10 @@ function exeName(name, platform) {
881
1250
  }
882
1251
  function allOnPath(name, pathValue, platform = process.platform) {
883
1252
  const found = [];
884
- for (const dir of pathValue.split(import_node_path.default.delimiter)) {
1253
+ for (const dir of pathValue.split(import_node_path3.default.delimiter)) {
885
1254
  if (!dir) continue;
886
1255
  for (const file of exeName(name, platform)) {
887
- const candidate = import_node_path.default.join(dir, file);
1256
+ const candidate = import_node_path3.default.join(dir, file);
888
1257
  if (executable(candidate)) {
889
1258
  found.push(candidate);
890
1259
  break;
@@ -897,17 +1266,17 @@ function desktopAppCandidates(provider, platform, home, applications) {
897
1266
  if (platform !== "darwin") return [];
898
1267
  if (provider === "codex") {
899
1268
  return applications.map(
900
- (apps) => import_node_path.default.join(apps, "ChatGPT.app", "Contents", "Resources", "codex-cli", "bin", "codex")
1269
+ (apps) => import_node_path3.default.join(apps, "ChatGPT.app", "Contents", "Resources", "codex-cli", "bin", "codex")
901
1270
  );
902
1271
  }
903
- const root = import_node_path.default.join(home, "Library", "Application Support", "Claude", "claude-code");
1272
+ const root = import_node_path3.default.join(home, "Library", "Application Support", "Claude", "claude-code");
904
1273
  let versions = [];
905
1274
  try {
906
1275
  versions = (0, import_node_fs.readdirSync)(root);
907
1276
  } catch {
908
1277
  return [];
909
1278
  }
910
- return versions.map((v) => import_node_path.default.join(root, v, "claude.app", "Contents", "MacOS", "claude"));
1279
+ return versions.map((v) => import_node_path3.default.join(root, v, "claude.app", "Contents", "MacOS", "claude"));
911
1280
  }
912
1281
  function npmGlobalPrefix(env) {
913
1282
  const fromEnv = env.npm_config_prefix ?? env.NPM_CONFIG_PREFIX;
@@ -925,13 +1294,13 @@ function declaredVersion(provider, source, file) {
925
1294
  if (source !== "desktop-app") return null;
926
1295
  if (provider === "codex") {
927
1296
  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"));
1297
+ 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
1298
  return typeof pkg.version === "string" ? parseVersion(pkg.version) : null;
930
1299
  } catch {
931
1300
  return null;
932
1301
  }
933
1302
  }
934
- return parseVersion(import_node_path.default.basename(import_node_path.default.resolve(file, "..", "..", "..", "..")));
1303
+ return parseVersion(import_node_path3.default.basename(import_node_path3.default.resolve(file, "..", "..", "..", "..")));
935
1304
  }
936
1305
  function runVersion(file, env) {
937
1306
  return new Promise((resolve) => {
@@ -945,14 +1314,14 @@ async function discoverBinaries(provider, options) {
945
1314
  const platform = options.platform ?? process.platform;
946
1315
  const home = options.home ?? options.env.HOME ?? import_node_os.default.homedir();
947
1316
  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" })),
1317
+ ...desktopAppCandidates(provider, platform, home, options.applications ?? ["/Applications", import_node_path3.default.join(home, "Applications")]).filter(executable).map((p) => ({ path: p, source: "desktop-app" })),
949
1318
  ...allOnPath(provider, options.env.PATH ?? "", platform).map((p) => ({ path: p, source: "path" }))
950
1319
  ];
951
1320
  const prefix = await npmGlobalPrefix(options.env);
952
1321
  if (prefix) {
953
- const dir = platform === "win32" ? prefix : import_node_path.default.join(prefix, "bin");
1322
+ const dir = platform === "win32" ? prefix : import_node_path3.default.join(prefix, "bin");
954
1323
  for (const file of exeName(provider, platform)) {
955
- const candidate = import_node_path.default.join(dir, file);
1324
+ const candidate = import_node_path3.default.join(dir, file);
956
1325
  if (executable(candidate)) {
957
1326
  listed2.push({ path: candidate, source: "npm-global" });
958
1327
  break;
@@ -998,7 +1367,7 @@ async function resolveBinary(provider, options) {
998
1367
 
999
1368
  // src/engine/clean-env.ts
1000
1369
  var import_node_os2 = __toESM(require("os"), 1);
1001
- var import_node_path2 = __toESM(require("path"), 1);
1370
+ var import_node_path4 = __toESM(require("path"), 1);
1002
1371
  var ALLOWED_NAMES = /* @__PURE__ */ new Set([
1003
1372
  "HOME",
1004
1373
  "USER",
@@ -1020,11 +1389,11 @@ var ALLOWED_NAMES = /* @__PURE__ */ new Set([
1020
1389
  var ALLOWED_PREFIXES = ["LC_"];
1021
1390
  function wellKnownBinDirs(home) {
1022
1391
  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"),
1392
+ import_node_path4.default.join(home, ".local", "bin"),
1393
+ import_node_path4.default.join(home, ".claude", "local"),
1394
+ import_node_path4.default.join(home, ".npm-global", "bin"),
1395
+ import_node_path4.default.join(home, ".bun", "bin"),
1396
+ import_node_path4.default.join(home, ".volta", "bin"),
1028
1397
  "/opt/homebrew/bin",
1029
1398
  "/usr/local/bin",
1030
1399
  "/usr/bin",
@@ -1044,21 +1413,21 @@ function buildCleanEnv(hostEnv, extra = {}) {
1044
1413
  }
1045
1414
  const home = out.HOME ?? import_node_os2.default.homedir();
1046
1415
  out.HOME = home;
1047
- const hostPath = (hostEnv.PATH ?? "").split(import_node_path2.default.delimiter).filter(Boolean);
1416
+ const hostPath = (hostEnv.PATH ?? "").split(import_node_path4.default.delimiter).filter(Boolean);
1048
1417
  const merged = [];
1049
1418
  for (const dir of [...hostPath, ...wellKnownBinDirs(home)]) {
1050
1419
  if (!merged.includes(dir)) merged.push(dir);
1051
1420
  }
1052
- out.PATH = merged.join(import_node_path2.default.delimiter);
1421
+ out.PATH = merged.join(import_node_path4.default.delimiter);
1053
1422
  for (const [name, value] of Object.entries(extra)) out[name] = value;
1054
1423
  return out;
1055
1424
  }
1056
1425
 
1057
1426
  // src/engine/files.ts
1058
1427
  var import_node_fs2 = require("fs");
1059
- var import_promises2 = __toESM(require("fs/promises"), 1);
1428
+ var import_promises4 = __toESM(require("fs/promises"), 1);
1060
1429
  var import_node_os3 = __toESM(require("os"), 1);
1061
- var import_node_path3 = __toESM(require("path"), 1);
1430
+ var import_node_path5 = __toESM(require("path"), 1);
1062
1431
 
1063
1432
  // src/engine/activity.ts
1064
1433
  var import_node_child_process2 = require("child_process");
@@ -1127,15 +1496,15 @@ function describeActivity(input) {
1127
1496
  // src/engine/files.ts
1128
1497
  function defaultProviderHomes(home = import_node_os3.default.homedir()) {
1129
1498
  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")
1499
+ claudeProjects: import_node_path5.default.join(home, ".claude", "projects"),
1500
+ codexSessions: import_node_path5.default.join(home, ".codex", "sessions"),
1501
+ codexIndex: import_node_path5.default.join(home, ".codex", "session_index.jsonl")
1133
1502
  };
1134
1503
  }
1135
1504
  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
1505
  async function safeReaddir(dir) {
1137
1506
  try {
1138
- return await import_promises2.default.readdir(dir, { withFileTypes: true });
1507
+ return await import_promises4.default.readdir(dir, { withFileTypes: true });
1139
1508
  } catch {
1140
1509
  return [];
1141
1510
  }
@@ -1144,7 +1513,7 @@ async function statCandidate(provider, file) {
1144
1513
  const match = UUIDISH.exec(file);
1145
1514
  if (!match?.[1]) return null;
1146
1515
  try {
1147
- const stat = await import_promises2.default.stat(file);
1516
+ const stat = await import_promises4.default.stat(file);
1148
1517
  return { provider, file, id: match[1], mtimeMs: stat.mtimeMs, size: stat.size };
1149
1518
  } catch {
1150
1519
  return null;
@@ -1154,10 +1523,10 @@ async function claudeCandidates(root) {
1154
1523
  const out = [];
1155
1524
  for (const project of await safeReaddir(root)) {
1156
1525
  if (!project.isDirectory()) continue;
1157
- const dir = import_node_path3.default.join(root, project.name);
1526
+ const dir = import_node_path5.default.join(root, project.name);
1158
1527
  const entries = await safeReaddir(dir);
1159
1528
  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)))
1529
+ entries.filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => statCandidate("claude", import_node_path5.default.join(dir, e.name)))
1161
1530
  );
1162
1531
  for (const s of stats) if (s) out.push(s);
1163
1532
  }
@@ -1167,14 +1536,14 @@ async function codexCandidates(root) {
1167
1536
  const out = [];
1168
1537
  for (const year of await safeReaddir(root)) {
1169
1538
  if (!year.isDirectory()) continue;
1170
- for (const month of await safeReaddir(import_node_path3.default.join(root, year.name))) {
1539
+ for (const month of await safeReaddir(import_node_path5.default.join(root, year.name))) {
1171
1540
  if (!month.isDirectory()) continue;
1172
- for (const day of await safeReaddir(import_node_path3.default.join(root, year.name, month.name))) {
1541
+ for (const day of await safeReaddir(import_node_path5.default.join(root, year.name, month.name))) {
1173
1542
  if (!day.isDirectory()) continue;
1174
- const dir = import_node_path3.default.join(root, year.name, month.name, day.name);
1543
+ const dir = import_node_path5.default.join(root, year.name, month.name, day.name);
1175
1544
  const entries = await safeReaddir(dir);
1176
1545
  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)))
1546
+ 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
1547
  );
1179
1548
  for (const s of stats) if (s) out.push(s);
1180
1549
  }
@@ -1183,7 +1552,7 @@ async function codexCandidates(root) {
1183
1552
  return out;
1184
1553
  }
1185
1554
  async function readSlice(file, start, length) {
1186
- const handle = await import_promises2.default.open(file, "r");
1555
+ const handle = await import_promises4.default.open(file, "r");
1187
1556
  try {
1188
1557
  const buffer = Buffer.alloc(length);
1189
1558
  const { bytesRead } = await handle.read(buffer, 0, length, start);
@@ -1256,9 +1625,9 @@ async function readCodexIndex(file) {
1256
1625
  const names = /* @__PURE__ */ new Map();
1257
1626
  let text;
1258
1627
  try {
1259
- const stat = await import_promises2.default.stat(file);
1628
+ const stat = await import_promises4.default.stat(file);
1260
1629
  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);
1630
+ text = stat.size <= budget ? await import_promises4.default.readFile(file, "utf8") : completeLines(await readSlice(file, stat.size - budget, budget), true);
1262
1631
  } catch {
1263
1632
  return names;
1264
1633
  }
@@ -1379,7 +1748,7 @@ var MAX_HISTORY_BUDGET = 256 * 1024 * 1024;
1379
1748
  async function readSessionFile(provider, file, endOffset, maxBytes = HISTORY_BUDGET) {
1380
1749
  let size;
1381
1750
  try {
1382
- size = (await import_promises2.default.stat(file)).size;
1751
+ size = (await import_promises4.default.stat(file)).size;
1383
1752
  } catch {
1384
1753
  return {
1385
1754
  items: [],
@@ -1433,9 +1802,9 @@ async function describeSessionFile(candidate, codexNames) {
1433
1802
  }
1434
1803
 
1435
1804
  // 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);
1805
+ var import_node_crypto3 = require("crypto");
1806
+ var import_promises5 = __toESM(require("fs/promises"), 1);
1807
+ var import_node_path6 = __toESM(require("path"), 1);
1439
1808
  var PreferenceStore = class {
1440
1809
  constructor(dir) {
1441
1810
  this.dir = dir;
@@ -1444,11 +1813,11 @@ var PreferenceStore = class {
1444
1813
  data = {};
1445
1814
  writing = Promise.resolve();
1446
1815
  get file() {
1447
- return import_node_path4.default.join(this.dir, "preferences.json");
1816
+ return import_node_path6.default.join(this.dir, "preferences.json");
1448
1817
  }
1449
1818
  async load() {
1450
1819
  try {
1451
- const parsed = JSON.parse(await import_promises3.default.readFile(this.file, "utf8"));
1820
+ const parsed = JSON.parse(await import_promises5.default.readFile(this.file, "utf8"));
1452
1821
  this.data = typeof parsed === "object" && parsed !== null ? parsed : {};
1453
1822
  } catch {
1454
1823
  this.data = {};
@@ -1486,10 +1855,10 @@ var PreferenceStore = class {
1486
1855
  persist() {
1487
1856
  const snapshot = JSON.stringify(this.data, null, 1);
1488
1857
  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);
1858
+ await import_promises5.default.mkdir(this.dir, { recursive: true });
1859
+ const tmp = `${this.file}.${(0, import_node_crypto3.randomUUID)()}.tmp`;
1860
+ await import_promises5.default.writeFile(tmp, snapshot, { mode: 384 });
1861
+ await import_promises5.default.rename(tmp, this.file);
1493
1862
  }).catch(() => {
1494
1863
  });
1495
1864
  }
@@ -1646,9 +2015,9 @@ async function searchLocalChats(query, options = {}) {
1646
2015
  }
1647
2016
 
1648
2017
  // 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);
2018
+ var import_node_crypto4 = require("crypto");
2019
+ var import_promises6 = __toESM(require("fs/promises"), 1);
2020
+ var import_node_path7 = __toESM(require("path"), 1);
1652
2021
  var MAX_RUNS = 2e3;
1653
2022
  var RunIndex = class {
1654
2023
  constructor(dir) {
@@ -1658,12 +2027,12 @@ var RunIndex = class {
1658
2027
  data = { version: 1, sessions: [], runs: [] };
1659
2028
  writing = Promise.resolve();
1660
2029
  get file() {
1661
- return import_node_path5.default.join(this.dir, "index.json");
2030
+ return import_node_path7.default.join(this.dir, "index.json");
1662
2031
  }
1663
2032
  async load() {
1664
- await import_promises4.default.mkdir(this.dir, { recursive: true });
2033
+ await import_promises6.default.mkdir(this.dir, { recursive: true });
1665
2034
  try {
1666
- const parsed = JSON.parse(await import_promises4.default.readFile(this.file, "utf8"));
2035
+ const parsed = JSON.parse(await import_promises6.default.readFile(this.file, "utf8"));
1667
2036
  this.data = {
1668
2037
  version: 1,
1669
2038
  sessions: Array.isArray(parsed.sessions) ? parsed.sessions : [],
@@ -1705,9 +2074,9 @@ var RunIndex = class {
1705
2074
  persist() {
1706
2075
  const snapshot = JSON.stringify(this.data, null, 1);
1707
2076
  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);
2077
+ const tmp = `${this.file}.${(0, import_node_crypto4.randomUUID)()}.tmp`;
2078
+ await import_promises6.default.writeFile(tmp, snapshot, { mode: 384 });
2079
+ await import_promises6.default.rename(tmp, this.file);
1711
2080
  }).catch(() => {
1712
2081
  });
1713
2082
  }
@@ -1717,27 +2086,27 @@ var RunIndex = class {
1717
2086
  /** A stable Paperclip agent/company identity for this install, so the
1718
2087
  * driver's scratch state is reused rather than growing per run. */
1719
2088
  async identity() {
1720
- const file = import_node_path5.default.join(this.dir, "identity.json");
2089
+ const file = import_node_path7.default.join(this.dir, "identity.json");
1721
2090
  try {
1722
- const parsed = JSON.parse(await import_promises4.default.readFile(file, "utf8"));
2091
+ const parsed = JSON.parse(await import_promises6.default.readFile(file, "utf8"));
1723
2092
  if (typeof parsed.companyId === "string" && typeof parsed.agentId === "string") {
1724
2093
  return { companyId: parsed.companyId, agentId: parsed.agentId };
1725
2094
  }
1726
2095
  } catch {
1727
2096
  }
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 });
2097
+ const identity = { companyId: (0, import_node_crypto4.randomUUID)(), agentId: (0, import_node_crypto4.randomUUID)() };
2098
+ await import_promises6.default.writeFile(file, JSON.stringify(identity), { mode: 384 });
1730
2099
  return identity;
1731
2100
  }
1732
2101
  };
1733
2102
 
1734
2103
  // src/engine/user-defaults.ts
1735
- var import_promises5 = __toESM(require("fs/promises"), 1);
2104
+ var import_promises7 = __toESM(require("fs/promises"), 1);
1736
2105
  var import_node_os4 = __toESM(require("os"), 1);
1737
- var import_node_path6 = __toESM(require("path"), 1);
2106
+ var import_node_path8 = __toESM(require("path"), 1);
1738
2107
  async function readJson(file) {
1739
2108
  try {
1740
- const parsed = JSON.parse(await import_promises5.default.readFile(file, "utf8"));
2109
+ const parsed = JSON.parse(await import_promises7.default.readFile(file, "utf8"));
1741
2110
  return typeof parsed === "object" && parsed !== null ? parsed : null;
1742
2111
  } catch {
1743
2112
  return null;
@@ -1748,8 +2117,8 @@ function str3(value) {
1748
2117
  }
1749
2118
  async function claudeDefaults(cwd, home = import_node_os4.default.homedir()) {
1750
2119
  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")
2120
+ ...cwd ? [import_node_path8.default.join(cwd, ".claude", "settings.local.json"), import_node_path8.default.join(cwd, ".claude", "settings.json")] : [],
2121
+ import_node_path8.default.join(home, ".claude", "settings.json")
1753
2122
  ];
1754
2123
  let model = null;
1755
2124
  let effort = null;
@@ -1765,7 +2134,7 @@ async function claudeDefaults(cwd, home = import_node_os4.default.homedir()) {
1765
2134
  async function codexDefaults(home = import_node_os4.default.homedir()) {
1766
2135
  let text;
1767
2136
  try {
1768
- text = await import_promises5.default.readFile(import_node_path6.default.join(home, ".codex", "config.toml"), "utf8");
2137
+ text = await import_promises7.default.readFile(import_node_path8.default.join(home, ".codex", "config.toml"), "utf8");
1769
2138
  } catch {
1770
2139
  return { model: null, effort: null };
1771
2140
  }
@@ -1789,31 +2158,31 @@ var EFFORT_LEVELS = {
1789
2158
  // src/engine/index.ts
1790
2159
  var RAW_TRANSCRIPT_MAX_BYTES = 200 * 1048576;
1791
2160
  function moduleDir() {
1792
- return import_node_path7.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
2161
+ return import_node_path9.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
1793
2162
  }
1794
2163
  function resolveWorkerPath() {
1795
2164
  const dir = moduleDir();
1796
2165
  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")
2166
+ import_node_path9.default.join(dir, "worker.js"),
2167
+ import_node_path9.default.join(dir, "worker.ts"),
2168
+ import_node_path9.default.join(dir, "..", "engine", "worker.js"),
2169
+ import_node_path9.default.join(dir, "..", "engine", "worker.ts")
1801
2170
  ]) {
1802
2171
  if ((0, import_node_fs4.existsSync)(candidate)) return candidate;
1803
2172
  }
1804
2173
  throw new Error(`[coding-sessions] The driver worker is missing next to ${dir}.`);
1805
2174
  }
1806
2175
  async function installShim(dataDir) {
1807
- const source = import_node_path7.default.resolve(moduleDir(), "..", "..", "bin", "cli-shim.sh");
2176
+ const source = import_node_path9.default.resolve(moduleDir(), "..", "..", "bin", "cli-shim.sh");
1808
2177
  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);
2178
+ const body = await import_promises8.default.readFile(source, "utf8");
2179
+ const target = import_node_path9.default.join(dataDir, "bin", "cli-shim.sh");
2180
+ const current = await import_promises8.default.readFile(target, "utf8").catch(() => null);
1812
2181
  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 });
2182
+ await import_promises8.default.mkdir(import_node_path9.default.dirname(target), { recursive: true });
2183
+ await import_promises8.default.writeFile(target, body, { mode: 493 });
1815
2184
  }
1816
- await import_promises6.default.chmod(target, 493);
2185
+ await import_promises8.default.chmod(target, 493);
1817
2186
  return target;
1818
2187
  }
1819
2188
  function nowIso() {
@@ -1913,29 +2282,30 @@ function describeAuthStatus(provider, probe) {
1913
2282
  };
1914
2283
  }
1915
2284
  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");
2285
+ const dataDir = options.dataDir ?? import_node_path9.default.join(import_node_os5.default.homedir(), ".matrx", "coding-sessions");
2286
+ const stateDir = import_node_path9.default.join(dataDir, "paperclip-state");
1918
2287
  const homes = options.homes ?? defaultProviderHomes();
1919
2288
  const index = new RunIndex(dataDir);
1920
2289
  await index.load();
1921
2290
  const preferences = new PreferenceStore(dataDir);
1922
2291
  await preferences.load();
1923
- await import_promises6.default.mkdir(stateDir, { recursive: true });
2292
+ await import_promises8.default.mkdir(stateDir, { recursive: true });
1924
2293
  const identity = await index.identity();
2294
+ const agents = createAgentConnection(options.agents, dataDir);
1925
2295
  const backups = options.rawBackup ? createRawBackupScheduler({
1926
2296
  sink: options.rawBackup,
1927
2297
  findFile: (provider, providerSessionId) => findSessionFile(provider, providerSessionId, homes),
1928
2298
  onError: (error) => console.warn("[coding-sessions] raw transcript backup failed:", error)
1929
2299
  }) : null;
1930
2300
  const discoveredSeen = /* @__PURE__ */ new Map();
1931
- const healthFile = import_node_path7.default.join(dataDir, "provider-health.json");
2301
+ const healthFile = import_node_path9.default.join(dataDir, "provider-health.json");
1932
2302
  let health = {};
1933
2303
  try {
1934
- health = JSON.parse(await import_promises6.default.readFile(healthFile, "utf8"));
2304
+ health = JSON.parse(await import_promises8.default.readFile(healthFile, "utf8"));
1935
2305
  } catch {
1936
2306
  health = {};
1937
2307
  }
1938
- const saveHealth = () => import_promises6.default.writeFile(healthFile, JSON.stringify(health), { mode: 384 }).catch(() => void 0);
2308
+ const saveHealth = () => import_promises8.default.writeFile(healthFile, JSON.stringify(health), { mode: 384 }).catch(() => void 0);
1939
2309
  function noteRunOutcome(provider, status, texts) {
1940
2310
  if (status === "completed") {
1941
2311
  if (health[provider]) {
@@ -2052,7 +2422,7 @@ async function createCodingSessionsEngine(options = {}) {
2052
2422
  async function startRun(session, prompt) {
2053
2423
  const provider = session.provider;
2054
2424
  const realCommand = await commandFor(provider);
2055
- const runId = (0, import_node_crypto4.randomUUID)();
2425
+ const runId = (0, import_node_crypto5.randomUUID)();
2056
2426
  const model = session.model ?? (provider === "claude" ? (await userDefaults(provider, session.cwd)).model ?? null : null);
2057
2427
  const run = {
2058
2428
  runId,
@@ -2090,7 +2460,26 @@ async function createCodingSessionsEngine(options = {}) {
2090
2460
  let fileOffset = null;
2091
2461
  if (session.providerSessionId) {
2092
2462
  const file = await findSessionFile(provider, session.providerSessionId, homes);
2093
- if (file) fileOffset = (await import_promises6.default.stat(file).catch(() => null))?.size ?? null;
2463
+ if (file) fileOffset = (await import_promises8.default.stat(file).catch(() => null))?.size ?? null;
2464
+ }
2465
+ let agentArgs = [];
2466
+ if (session.agent) {
2467
+ const bundle = await agents.readSessionBundle(session.id);
2468
+ if (!bundle) {
2469
+ const failed = saveRun({
2470
+ ...run,
2471
+ status: "failed",
2472
+ endedAt: nowIso(),
2473
+ error: `The agent ${session.agent.name} could not be loaded on this computer anymore. Start a new session to load it again.`
2474
+ });
2475
+ const current = index.session(session.id);
2476
+ if (current) saveSession({ ...current, lastRunStatus: "failed" });
2477
+ return failed;
2478
+ }
2479
+ const attachment = await writeAgentAttachment(agents.sessionDir(session.id), bundle, {
2480
+ mcpUrl: await agents.mcpUrlFor(session.id)
2481
+ });
2482
+ agentArgs = agentArgsFor(provider, attachment, { resuming: session.providerSessionId !== null });
2094
2483
  }
2095
2484
  const child = forkWorker();
2096
2485
  const liveRun = {
@@ -2264,7 +2653,8 @@ async function createCodingSessionsEngine(options = {}) {
2264
2653
  resumeSessionId: session.providerSessionId,
2265
2654
  shimPath,
2266
2655
  realCommand,
2267
- identity
2656
+ identity,
2657
+ agentArgs
2268
2658
  });
2269
2659
  return run;
2270
2660
  }
@@ -2283,7 +2673,7 @@ async function createCodingSessionsEngine(options = {}) {
2283
2673
  }, 3e3);
2284
2674
  }
2285
2675
  async function pruneDriverScratch() {
2286
- const runsDir = import_node_path7.default.join(
2676
+ const runsDir = import_node_path9.default.join(
2287
2677
  stateDir,
2288
2678
  "instances",
2289
2679
  "default",
@@ -2294,9 +2684,9 @@ async function createCodingSessionsEngine(options = {}) {
2294
2684
  "claude-runtime",
2295
2685
  "runs"
2296
2686
  );
2297
- const entries = await import_promises6.default.readdir(runsDir).catch(() => []);
2687
+ const entries = await import_promises8.default.readdir(runsDir).catch(() => []);
2298
2688
  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))
2689
+ 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
2690
  );
2301
2691
  }
2302
2692
  async function probe() {
@@ -2337,7 +2727,7 @@ async function createCodingSessionsEngine(options = {}) {
2337
2727
  function newSession(input) {
2338
2728
  const at = nowIso();
2339
2729
  return {
2340
- id: (0, import_node_crypto4.randomUUID)(),
2730
+ id: (0, import_node_crypto5.randomUUID)(),
2341
2731
  provider: input.provider,
2342
2732
  providerSessionId: input.providerSessionId,
2343
2733
  cwd: input.cwd,
@@ -2355,17 +2745,17 @@ async function createCodingSessionsEngine(options = {}) {
2355
2745
  function resolveFolder(raw) {
2356
2746
  const trimmed = raw.trim();
2357
2747
  if (!trimmed) return null;
2358
- return import_node_path7.default.resolve(trimmed.replace(/^~(?=$|\/)/, import_node_os5.default.homedir()));
2748
+ return import_node_path9.default.resolve(trimmed.replace(/^~(?=$|\/)/, import_node_os5.default.homedir()));
2359
2749
  }
2360
2750
  async function checkFolder(raw) {
2361
2751
  const target = resolveFolder(raw);
2362
2752
  if (!target) return { path: "", exists: false, isDirectory: false };
2363
- const stat = await import_promises6.default.stat(target).catch(() => null);
2753
+ const stat = await import_promises8.default.stat(target).catch(() => null);
2364
2754
  return { path: target, exists: stat !== null, isDirectory: stat?.isDirectory() === true };
2365
2755
  }
2366
2756
  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);
2757
+ const resolved = import_node_path9.default.resolve(cwd.replace(/^~(?=$|\/)/, import_node_os5.default.homedir()));
2758
+ const stat = await import_promises8.default.stat(resolved).catch(() => null);
2369
2759
  if (!stat?.isDirectory()) {
2370
2760
  throw new Error(`The folder ${resolved} doesn't exist on this computer.`);
2371
2761
  }
@@ -2427,7 +2817,7 @@ async function createCodingSessionsEngine(options = {}) {
2427
2817
  folders.map(async ([folder, lastUsedAt]) => ({
2428
2818
  path: folder,
2429
2819
  lastUsedAt,
2430
- exists: (await import_promises6.default.stat(folder).catch(() => null))?.isDirectory() === true
2820
+ exists: (await import_promises8.default.stat(folder).catch(() => null))?.isDirectory() === true
2431
2821
  }))
2432
2822
  );
2433
2823
  },
@@ -2472,7 +2862,7 @@ async function createCodingSessionsEngine(options = {}) {
2472
2862
  async createFolder(raw) {
2473
2863
  const target = resolveFolder(raw);
2474
2864
  if (!target) throw new Error("Type a folder path first.");
2475
- await import_promises6.default.mkdir(target, { recursive: true });
2865
+ await import_promises8.default.mkdir(target, { recursive: true });
2476
2866
  return checkFolder(target);
2477
2867
  },
2478
2868
  async sessionActivity(provider, providerSessionId) {
@@ -2510,31 +2900,35 @@ async function createCodingSessionsEngine(options = {}) {
2510
2900
  reason: `${PROVIDER_LABEL[provider]} has no saved transcript for this chat on this computer.`
2511
2901
  };
2512
2902
  }
2513
- const stat = await import_promises6.default.stat(file);
2903
+ const stat = await import_promises8.default.stat(file);
2514
2904
  if (stat.size > RAW_TRANSCRIPT_MAX_BYTES) {
2515
2905
  return {
2516
2906
  available: false,
2517
2907
  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
2908
  };
2519
2909
  }
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 };
2910
+ const content = await import_promises8.default.readFile(file, "utf8");
2911
+ return { available: true, fileName: import_node_path9.default.basename(file), bytes: stat.size, content };
2522
2912
  },
2523
2913
  async start(input) {
2524
2914
  const prompt = input.prompt.trim();
2525
2915
  if (!prompt) throw new Error("Type what you want done before starting.");
2526
2916
  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
- );
2917
+ const bundle = input.agent ? await agents.loadBundle(input.agent) : null;
2918
+ const draft = newSession({
2919
+ provider: input.provider,
2920
+ cwd,
2921
+ title: titleFrom(prompt),
2922
+ model: input.model?.trim() || null,
2923
+ effort: input.effort?.trim() || null,
2924
+ origin: "started",
2925
+ providerSessionId: null
2926
+ });
2927
+ if (bundle) {
2928
+ await agents.saveSessionBundle(draft.id, bundle);
2929
+ draft.agent = sessionAgentRecord(bundle, { toolsServed: await agents.mcpUrlFor(draft.id) !== null });
2930
+ }
2931
+ const session = saveSession(draft);
2538
2932
  const run = await startRun(session, prompt);
2539
2933
  return { session: index.session(session.id) ?? session, run, queued: false };
2540
2934
  },
@@ -2603,6 +2997,21 @@ async function createCodingSessionsEngine(options = {}) {
2603
2997
  if (opts && "effort" in opts) next.effort = typeof opts.effort === "string" && opts.effort.trim() ? opts.effort.trim() : null;
2604
2998
  return saveSession(next);
2605
2999
  },
3000
+ async agentAccess() {
3001
+ return agents.access();
3002
+ },
3003
+ async agentCatalogRpc(request) {
3004
+ return agents.catalogRpc(request);
3005
+ },
3006
+ async agentDetails(agentId) {
3007
+ return agents.details(String(agentId ?? ""));
3008
+ },
3009
+ setMcpBaseUrl(baseUrl) {
3010
+ agents.setMcpBaseUrl(baseUrl);
3011
+ },
3012
+ async handleMcp(secret, body) {
3013
+ return agents.handleMcp(secret, body);
3014
+ },
2606
3015
  async dispose() {
2607
3016
  for (const liveRun of live.values()) {
2608
3017
  liveRun.stopRequested = true;