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