@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.
- package/CHANGELOG.md +12 -0
- package/dist/engine/index.cjs +521 -112
- package/dist/engine/index.cjs.map +1 -1
- package/dist/engine/index.d.cts +4 -1
- package/dist/engine/index.d.ts +4 -1
- package/dist/engine/index.js +521 -112
- package/dist/engine/index.js.map +1 -1
- package/dist/engine/worker.js +2 -2
- package/dist/engine/worker.js.map +1 -1
- package/dist/{index-DQ0teQvw.d.cts → index-DEPwyr2j.d.cts} +130 -0
- package/dist/{index-DQ0teQvw.d.ts → index-DEPwyr2j.d.ts} +130 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +87 -1
- package/dist/index.d.ts +87 -1
- package/dist/index.js.map +1 -1
- package/dist/react/index.cjs +666 -408
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +87 -7
- package/dist/react/index.d.ts +87 -7
- package/dist/react/index.js +674 -403
- package/dist/react/index.js.map +1 -1
- package/dist/server/index.cjs +588 -127
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.d.cts +4 -1
- package/dist/server/index.d.ts +4 -1
- package/dist/server/index.js +587 -126
- package/dist/server/index.js.map +1 -1
- package/dist/ui/assets/index-DSWoryQZ.js +72 -0
- package/dist/ui/assets/index-Gp2bMDtx.css +2 -0
- package/dist/ui/index.html +2 -2
- package/package.json +4 -2
- package/dist/ui/assets/index-C-_1qjy9.css +0 -2
- package/dist/ui/assets/index-CmFgFvLa.js +0 -69
package/dist/engine/index.cjs
CHANGED
|
@@ -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
|
|
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, "&").replace(/"/g, """).replace(/</g, "<");
|
|
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
|
|
532
|
+
var import_promises8 = __toESM(require("fs/promises"), 1);
|
|
164
533
|
var import_node_os5 = __toESM(require("os"), 1);
|
|
165
|
-
var
|
|
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
|
|
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(
|
|
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 =
|
|
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) =>
|
|
1269
|
+
(apps) => import_node_path3.default.join(apps, "ChatGPT.app", "Contents", "Resources", "codex-cli", "bin", "codex")
|
|
901
1270
|
);
|
|
902
1271
|
}
|
|
903
|
-
const root =
|
|
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) =>
|
|
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)(
|
|
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(
|
|
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",
|
|
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 :
|
|
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 =
|
|
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
|
|
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
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
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(
|
|
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(
|
|
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
|
|
1428
|
+
var import_promises4 = __toESM(require("fs/promises"), 1);
|
|
1060
1429
|
var import_node_os3 = __toESM(require("os"), 1);
|
|
1061
|
-
var
|
|
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:
|
|
1131
|
-
codexSessions:
|
|
1132
|
-
codexIndex:
|
|
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
|
|
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
|
|
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 =
|
|
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",
|
|
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(
|
|
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(
|
|
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 =
|
|
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",
|
|
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
|
|
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
|
|
1628
|
+
const stat = await import_promises4.default.stat(file);
|
|
1260
1629
|
const budget = 4 * 1024 * 1024;
|
|
1261
|
-
text = stat.size <= budget ? await
|
|
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
|
|
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
|
|
1437
|
-
var
|
|
1438
|
-
var
|
|
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
|
|
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
|
|
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
|
|
1490
|
-
const tmp = `${this.file}.${(0,
|
|
1491
|
-
await
|
|
1492
|
-
await
|
|
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
|
|
1650
|
-
var
|
|
1651
|
-
var
|
|
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
|
|
2030
|
+
return import_node_path7.default.join(this.dir, "index.json");
|
|
1662
2031
|
}
|
|
1663
2032
|
async load() {
|
|
1664
|
-
await
|
|
2033
|
+
await import_promises6.default.mkdir(this.dir, { recursive: true });
|
|
1665
2034
|
try {
|
|
1666
|
-
const parsed = JSON.parse(await
|
|
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,
|
|
1709
|
-
await
|
|
1710
|
-
await
|
|
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 =
|
|
2089
|
+
const file = import_node_path7.default.join(this.dir, "identity.json");
|
|
1721
2090
|
try {
|
|
1722
|
-
const parsed = JSON.parse(await
|
|
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,
|
|
1729
|
-
await
|
|
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
|
|
2104
|
+
var import_promises7 = __toESM(require("fs/promises"), 1);
|
|
1736
2105
|
var import_node_os4 = __toESM(require("os"), 1);
|
|
1737
|
-
var
|
|
2106
|
+
var import_node_path8 = __toESM(require("path"), 1);
|
|
1738
2107
|
async function readJson(file) {
|
|
1739
2108
|
try {
|
|
1740
|
-
const parsed = JSON.parse(await
|
|
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 ? [
|
|
1752
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
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 =
|
|
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
|
|
1810
|
-
const target =
|
|
1811
|
-
const current = await
|
|
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
|
|
1814
|
-
await
|
|
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
|
|
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 ??
|
|
1917
|
-
const stateDir =
|
|
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
|
|
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 =
|
|
2301
|
+
const healthFile = import_node_path9.default.join(dataDir, "provider-health.json");
|
|
1932
2302
|
let health = {};
|
|
1933
2303
|
try {
|
|
1934
|
-
health = JSON.parse(await
|
|
2304
|
+
health = JSON.parse(await import_promises8.default.readFile(healthFile, "utf8"));
|
|
1935
2305
|
} catch {
|
|
1936
2306
|
health = {};
|
|
1937
2307
|
}
|
|
1938
|
-
const saveHealth = () =>
|
|
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,
|
|
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
|
|
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 =
|
|
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
|
|
2687
|
+
const entries = await import_promises8.default.readdir(runsDir).catch(() => []);
|
|
2298
2688
|
await Promise.all(
|
|
2299
|
-
entries.filter((name) => !live.has(name)).map((name) =>
|
|
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,
|
|
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
|
|
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
|
|
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 =
|
|
2368
|
-
const stat = await
|
|
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
|
|
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
|
|
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
|
|
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
|
|
2521
|
-
return { available: true, fileName:
|
|
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
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
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;
|