@ai-matrx/coding-sessions 0.1.85 → 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.
@@ -117,6 +117,22 @@ function createRawBackupScheduler(options) {
117
117
  // src/engine/agent-attach.ts
118
118
  var import_promises2 = __toESM(require("fs/promises"), 1);
119
119
  var import_node_path = __toESM(require("path"), 1);
120
+
121
+ // src/normalize/agent-block.ts
122
+ var AGENT_BLOCK_MARKER = "<ai_matrx_agent ";
123
+ function stripAgentBlock(text) {
124
+ const removed = text.match(/^<ai_matrx_agent_removed>[\s\S]*?<\/ai_matrx_agent_removed>\s*/);
125
+ if (removed) return text.slice(removed[0].length);
126
+ if (!text.startsWith(AGENT_BLOCK_MARKER)) return text;
127
+ const end = text.indexOf("</ai_matrx_agent>");
128
+ if (end < 0) return text;
129
+ let rest = text.slice(end + "</ai_matrx_agent>".length);
130
+ const skills = rest.match(/^\s*<skills_instructions>[\s\S]*?<\/skills_instructions>/);
131
+ if (skills) rest = rest.slice(skills[0].length);
132
+ return rest.replace(/^\s+/, "");
133
+ }
134
+
135
+ // src/engine/agent-attach.ts
120
136
  var AGENT_MCP_SERVER_NAME = "matrx-agent";
121
137
  var AGENT_PLUGIN_NAME = "matrx-agent";
122
138
  function escapeAttr(value) {
@@ -167,6 +183,17 @@ function renderCodexSkillsList(bundle, skillsRoot) {
167
183
  ];
168
184
  return lines.join("\n");
169
185
  }
186
+ function renderFallbackBlock(bundle, skillsRoot) {
187
+ const skills = renderCodexSkillsList(bundle, skillsRoot);
188
+ 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.");
189
+ return skills ? `${body}
190
+
191
+ ${skills}
192
+
193
+ ` : `${body}
194
+
195
+ `;
196
+ }
170
197
  function tomlString(value) {
171
198
  const scalar = value.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, "\uFFFD");
172
199
  return JSON.stringify(scalar).replace(/\u007f/g, "\\u007f");
@@ -282,9 +309,29 @@ function sessionAgentRecord(bundle, options) {
282
309
  name: bundle.name,
283
310
  definitionHash: bundle.definition_hash,
284
311
  loaded,
285
- unavailable
312
+ unavailable,
313
+ skills: bundle.skills.map((skill) => skill.label),
314
+ tools: options.toolsServed ? bundle.tools.map((tool) => tool.canonical_name) : [],
315
+ ...options.variables ? { variables: options.variables } : {}
286
316
  };
287
317
  }
318
+ 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";
319
+ async function codexCompactionDroppedAgent(rolloutFile, sinceIso) {
320
+ const text = await import_promises2.default.readFile(rolloutFile, "utf8").catch(() => "");
321
+ if (!text.includes('"type":"compacted"')) return false;
322
+ let latest = null;
323
+ for (const line of text.split("\n")) {
324
+ if (!line.includes('"type":"compacted"')) continue;
325
+ try {
326
+ const entry = JSON.parse(line);
327
+ if (entry.type === "compacted") latest = entry;
328
+ } catch {
329
+ }
330
+ }
331
+ if (!latest) return false;
332
+ if (sinceIso && latest.timestamp && latest.timestamp <= sinceIso) return false;
333
+ return !JSON.stringify(latest.payload?.replacement_history ?? "").includes(AGENT_BLOCK_MARKER.trim());
334
+ }
288
335
 
289
336
  // src/engine/agents.ts
290
337
  var import_node_crypto2 = require("crypto");
@@ -292,6 +339,7 @@ var import_promises3 = __toESM(require("fs/promises"), 1);
292
339
  var import_node_path2 = __toESM(require("path"), 1);
293
340
  var DEFAULT_SERVER_URL = "https://server.app.matrxserver.com";
294
341
  var DEFAULT_SUPABASE_URL = "https://db.matrxserver.com";
342
+ var DEFAULT_SUPABASE_KEY = "sb_publishable_scm0jzEJiFUB5ItiqOx5Fg_8BOjB6oA";
295
343
  var AGENT_CATALOG_RPCS = /* @__PURE__ */ new Set([
296
344
  "agx_get_list_full",
297
345
  "agx_search",
@@ -309,6 +357,10 @@ var agentsClient = async () => {
309
357
  ]);
310
358
  return { ...matrx, ...portable, ...mcp };
311
359
  };
360
+ function sortedValues(values) {
361
+ return Object.keys(values).sort().map((key) => [key, values[key]]);
362
+ }
363
+ var BUNDLE_CACHE_MS = 24 * 60 * 60 * 1e3;
312
364
  var SIGNED_OUT_AGENT_MESSAGE = "Sign in to AI Matrx to use your agents.";
313
365
  function userIdOf(credential) {
314
366
  if (!credential || credential.kind !== "user") return null;
@@ -325,21 +377,26 @@ function createAgentConnection(options, dataDir) {
325
377
  const credentials = options?.credentials ?? null;
326
378
  const serverUrl = (options?.serverUrl ?? DEFAULT_SERVER_URL).replace(/\/+$/, "");
327
379
  const supabaseUrl = (options?.supabaseUrl ?? DEFAULT_SUPABASE_URL).replace(/\/+$/, "");
328
- const supabaseKey = options?.supabaseKey ?? null;
380
+ const supabaseKey = options?.supabaseKey ?? (options?.supabaseUrl ? null : DEFAULT_SUPABASE_KEY);
329
381
  const root = import_node_path2.default.join(dataDir, "agents");
330
382
  let mcpBaseUrl = null;
331
383
  const handlers = /* @__PURE__ */ new Map();
332
384
  const secretToSession = /* @__PURE__ */ new Map();
333
- const organizationId = () => {
334
- const raw = typeof options?.organizationId === "function" ? options.organizationId() : options?.organizationId;
385
+ const organizationId = async () => {
386
+ const raw = typeof options?.organizationId === "function" ? await options.organizationId() : options?.organizationId;
335
387
  return raw && raw.trim() ? raw.trim() : null;
336
388
  };
337
- const transport = async () => (await agentsClient()).createMatrxTransport({
338
- baseUrl: serverUrl,
339
- ...credentials ? { credentials } : {},
340
- ...organizationId() ? { organizationId: () => organizationId() } : {},
341
- source: "codingSessionsAgents"
342
- });
389
+ const transport = async () => {
390
+ const org = await organizationId();
391
+ return (await agentsClient()).createMatrxTransport({
392
+ baseUrl: serverUrl,
393
+ ...credentials ? { credentials } : {},
394
+ ...org ? { organizationId: org } : {},
395
+ source: "codingSessionsAgents"
396
+ });
397
+ };
398
+ const cacheDir = import_node_path2.default.join(dataDir, "agents", ".bundles");
399
+ const cacheKey = (definitionHash, selection, surface) => (0, import_node_crypto2.createHash)("sha256").update(JSON.stringify([definitionHash, sortedValues(selection.variables ?? {}), surface])).digest("hex");
343
400
  const credential = async () => credentials ? credentials.get() : null;
344
401
  const requireSignedIn = async () => {
345
402
  if (!await credential()) throw new Error(SIGNED_OUT_AGENT_MESSAGE);
@@ -375,7 +432,7 @@ function createAgentConnection(options, dataDir) {
375
432
  configured: credentials !== null && supabaseKey !== null,
376
433
  signedIn: userId !== null,
377
434
  userId,
378
- organizationId: organizationId()
435
+ organizationId: await organizationId().catch(() => null)
379
436
  };
380
437
  },
381
438
  async catalogRpc(request) {
@@ -422,27 +479,39 @@ function createAgentConnection(options, dataDir) {
422
479
  },
423
480
  async details(agentId) {
424
481
  await requireSignedIn();
425
- const { fetchPortableAgent, fetchPortableAgentVersions } = await agentsClient();
426
- const [bundle, versions] = await Promise.all([
427
- fetchPortableAgent(await transport(), { agentId }),
428
- fetchPortableAgentVersions(await transport(), agentId).catch(() => [])
429
- ]);
482
+ const summary = await (await agentsClient()).fetchPortableAgentSummary(await transport(), agentId);
430
483
  return {
431
- agentId: bundle.agent_id,
432
- name: bundle.name,
433
- variables: bundle.variables,
434
- versions: versions.map((v) => ({ id: v.id, versionNumber: v.version_number, changedAt: v.changed_at })).sort((a, b) => b.versionNumber - a.versionNumber),
435
- latestVersionNumber: bundle.version_number
484
+ agentId: summary.agent_id,
485
+ name: summary.name,
486
+ variables: summary.variables,
487
+ versions: summary.versions.map((v) => ({ id: v.id, versionNumber: v.version_number, changedAt: v.changed_at })),
488
+ latestVersionNumber: summary.version_number,
489
+ latestVersionId: summary.version_id
436
490
  };
437
491
  },
438
492
  async loadBundle(selection) {
439
493
  await requireSignedIn();
440
- const { fetchPortableAgent } = await agentsClient();
441
- return fetchPortableAgent(
494
+ const client = await agentsClient();
495
+ const summary = await client.fetchPortableAgentSummary(await transport(), selection.agentId);
496
+ const versionId = selection.versionId ?? summary.version_id;
497
+ const definitionHash = versionId ? summary.versions.find((v) => v.id === versionId)?.definition_hash ?? null : summary.definition_hash;
498
+ const surface = "matrx-user/code-editor";
499
+ const file = definitionHash ? import_node_path2.default.join(cacheDir, `${cacheKey(definitionHash, selection, surface)}.json`) : null;
500
+ if (file) {
501
+ const cached = await import_promises3.default.readFile(file, "utf8").then((text) => JSON.parse(text)).catch(() => null);
502
+ const fresh = await import_promises3.default.stat(file).then((st) => Date.now() - st.mtimeMs < BUNDLE_CACHE_MS).catch(() => false);
503
+ if (cached && fresh && cached.definition_hash === definitionHash) return cached;
504
+ }
505
+ const bundle = await client.fetchPortableAgent(
442
506
  await transport(),
443
- selection.versionId ? { versionId: selection.versionId } : { agentId: selection.agentId },
444
- { variables: selection.variables ?? {} }
507
+ versionId ? { versionId } : { agentId: selection.agentId },
508
+ { variables: selection.variables ?? {}, surface }
445
509
  );
510
+ if (file && bundle.definition_hash === definitionHash) {
511
+ await import_promises3.default.mkdir(cacheDir, { recursive: true, mode: 448 });
512
+ await import_promises3.default.writeFile(file, JSON.stringify(bundle), { mode: 384 });
513
+ }
514
+ return bundle;
446
515
  },
447
516
  async saveSessionBundle(sessionId, bundle) {
448
517
  await import_promises3.default.mkdir(sessionDir(sessionId), { recursive: true, mode: 448 });
@@ -857,7 +926,7 @@ function rolloutItem(item, fallbackId, at) {
857
926
  const id = str2(item.id) ?? fallbackId;
858
927
  switch (item.type) {
859
928
  case "UserMessage": {
860
- const text = stripCodexInjectedContext(itemText(item.content));
929
+ const text = stripCodexInjectedContext(stripAgentBlock(itemText(item.content)));
861
930
  return text.trim() ? [{ kind: "user", id, text, at }] : [];
862
931
  }
863
932
  case "AgentMessage": {
@@ -940,7 +1009,7 @@ function createCodexRolloutNormalizer() {
940
1009
  return rolloutItem(payload.item, fallbackId, at);
941
1010
  }
942
1011
  if (payload.type === "user_message" && !appItems) {
943
- const text = stripCodexInjectedContext(str2(payload.message) ?? "");
1012
+ const text = stripCodexInjectedContext(stripAgentBlock(str2(payload.message) ?? ""));
944
1013
  return text.trim() ? [{ kind: "user", id: fallbackId, text, at }] : [];
945
1014
  }
946
1015
  return [];
@@ -2418,6 +2487,12 @@ async function createCodingSessionsEngine(options = {}) {
2418
2487
  if (file) fileOffset = (await import_promises8.default.stat(file).catch(() => null))?.size ?? null;
2419
2488
  }
2420
2489
  let agentArgs = [];
2490
+ let cliPrompt = prompt;
2491
+ let delivered = null;
2492
+ if (!session.agent && session.agentRemovedDue) {
2493
+ if (provider === "codex") cliPrompt = `${AGENT_REMOVED_BLOCK}${prompt}`;
2494
+ delivered = { agentRemovedDue: false };
2495
+ }
2421
2496
  if (session.agent) {
2422
2497
  const bundle = await agents.readSessionBundle(session.id);
2423
2498
  if (!bundle) {
@@ -2435,6 +2510,10 @@ async function createCodingSessionsEngine(options = {}) {
2435
2510
  mcpUrl: await agents.mcpUrlFor(session.id)
2436
2511
  });
2437
2512
  agentArgs = agentArgsFor(provider, attachment, { resuming: session.providerSessionId !== null });
2513
+ if (provider === "codex" && session.agent.blockDue) {
2514
+ cliPrompt = `${renderFallbackBlock(bundle, import_node_path9.default.join(attachment.dir, "skills"))}${prompt}`;
2515
+ }
2516
+ delivered = { agent: { ...session.agent, blockDue: false, deliveredAt: run.startedAt } };
2438
2517
  }
2439
2518
  const child = forkWorker();
2440
2519
  const liveRun = {
@@ -2450,6 +2529,10 @@ async function createCodingSessionsEngine(options = {}) {
2450
2529
  };
2451
2530
  live.set(runId, liveRun);
2452
2531
  emitItem(liveRun, { kind: "user", id: `run-${runId}`, text: prompt, at: run.startedAt });
2532
+ if (delivered) {
2533
+ const current = index.session(session.id);
2534
+ if (current) saveSession({ ...current, ...delivered });
2535
+ }
2453
2536
  const splitter = createLineSplitter();
2454
2537
  const claude = provider === "claude" ? createClaudeNormalizer() : null;
2455
2538
  const codex = provider === "codex" ? createCodexStreamNormalizer(runId.slice(0, 8)) : null;
@@ -2488,6 +2571,7 @@ async function createCodingSessionsEngine(options = {}) {
2488
2571
  }
2489
2572
  }
2490
2573
  void pruneDriverScratch();
2574
+ if (provider === "codex" && final.status === "completed") void checkCompaction(session.id);
2491
2575
  };
2492
2576
  let settled = false;
2493
2577
  child.on("message", (raw) => {
@@ -2602,7 +2686,7 @@ async function createCodingSessionsEngine(options = {}) {
2602
2686
  provider,
2603
2687
  runId,
2604
2688
  cwd: session.cwd,
2605
- prompt,
2689
+ prompt: cliPrompt,
2606
2690
  model,
2607
2691
  effort: session.effort ?? null,
2608
2692
  resumeSessionId: session.providerSessionId,
@@ -2613,6 +2697,28 @@ async function createCodingSessionsEngine(options = {}) {
2613
2697
  });
2614
2698
  return run;
2615
2699
  }
2700
+ async function checkCompaction(sessionId) {
2701
+ const session = index.session(sessionId);
2702
+ if (!session?.agent || session.agent.blockDue || !session.providerSessionId) return;
2703
+ const file = await findSessionFile("codex", session.providerSessionId, homes);
2704
+ if (!file || !await codexCompactionDroppedAgent(file, session.agent.deliveredAt ?? null)) return;
2705
+ const current = index.session(sessionId);
2706
+ if (current?.agent) {
2707
+ saveSession({ ...current, agent: { ...current.agent, blockDue: true } });
2708
+ }
2709
+ }
2710
+ async function attachAgent(sessionId, selection, options2) {
2711
+ const bundle = await agents.loadBundle(selection);
2712
+ await agents.saveSessionBundle(sessionId, bundle);
2713
+ return {
2714
+ ...sessionAgentRecord(bundle, {
2715
+ toolsServed: await agents.mcpUrlFor(sessionId) !== null,
2716
+ variables: selection.variables ?? {}
2717
+ }),
2718
+ blockDue: options2.blockDue,
2719
+ deliveredAt: null
2720
+ };
2721
+ }
2616
2722
  function killGroup(liveRun) {
2617
2723
  const target = liveRun.pgid;
2618
2724
  if (target === null) return;
@@ -2869,7 +2975,6 @@ async function createCodingSessionsEngine(options = {}) {
2869
2975
  const prompt = input.prompt.trim();
2870
2976
  if (!prompt) throw new Error("Type what you want done before starting.");
2871
2977
  const cwd = await assertFolder(input.cwd);
2872
- const bundle = input.agent ? await agents.loadBundle(input.agent) : null;
2873
2978
  const draft = newSession({
2874
2979
  provider: input.provider,
2875
2980
  cwd,
@@ -2879,28 +2984,40 @@ async function createCodingSessionsEngine(options = {}) {
2879
2984
  origin: "started",
2880
2985
  providerSessionId: null
2881
2986
  });
2882
- if (bundle) {
2883
- await agents.saveSessionBundle(draft.id, bundle);
2884
- draft.agent = sessionAgentRecord(bundle, { toolsServed: await agents.mcpUrlFor(draft.id) !== null });
2885
- }
2987
+ if (input.agent) draft.agent = await attachAgent(draft.id, input.agent, { blockDue: false });
2886
2988
  const session = saveSession(draft);
2887
2989
  const run = await startRun(session, prompt);
2888
2990
  return { session: index.session(session.id) ?? session, run, queued: false };
2889
2991
  },
2890
2992
  async takeover(input) {
2891
2993
  const existing = index.session(input.providerSessionId);
2892
- if (existing) return existing;
2994
+ if (existing) {
2995
+ if (!input.agent) return existing;
2996
+ return engine.setAgent(existing.id, input.agent);
2997
+ }
2893
2998
  const cwd = await assertFolder(input.cwd);
2894
- return saveSession(
2895
- newSession({
2896
- provider: input.provider,
2897
- cwd,
2898
- title: input.title?.trim() || "Taken-over chat",
2899
- model: null,
2900
- origin: "takeover",
2901
- providerSessionId: input.providerSessionId
2902
- })
2903
- );
2999
+ const draft = newSession({
3000
+ provider: input.provider,
3001
+ cwd,
3002
+ title: input.title?.trim() || "Taken-over chat",
3003
+ model: null,
3004
+ origin: "takeover",
3005
+ providerSessionId: input.providerSessionId
3006
+ });
3007
+ if (input.agent) draft.agent = await attachAgent(draft.id, input.agent, { blockDue: input.provider === "codex" });
3008
+ return saveSession(draft);
3009
+ },
3010
+ async setAgent(sessionId, selection) {
3011
+ const session = index.session(String(sessionId ?? ""));
3012
+ if (!session) throw new Error("This session is not in the list anymore.");
3013
+ if (!selection) {
3014
+ const told = session.provider === "codex" && session.agent?.deliveredAt != null;
3015
+ return saveSession({ ...session, agent: null, agentRemovedDue: told });
3016
+ }
3017
+ const agent = await attachAgent(session.id, selection, {
3018
+ blockDue: session.provider === "codex" && session.providerSessionId !== null
3019
+ });
3020
+ return saveSession({ ...session, agent, agentRemovedDue: false });
2904
3021
  },
2905
3022
  async send(sessionId, rawPrompt) {
2906
3023
  const prompt = rawPrompt.trim();
@@ -3039,7 +3156,8 @@ var BRIDGE_METHODS = [
3039
3156
  "setSessionOptions",
3040
3157
  "agentAccess",
3041
3158
  "agentCatalogRpc",
3042
- "agentDetails"
3159
+ "agentDetails",
3160
+ "setAgent"
3043
3161
  ];
3044
3162
  var BridgeError = class extends Error {
3045
3163
  constructor(message, code) {