@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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @ai-matrx/coding-sessions
2
2
 
3
+ ## 0.1.86
4
+
5
+ Automatic changed-only republish (docs/metadata drift since the last tag — see
6
+ `git diff npm/coding-sessions/v0.1.85..npm/coding-sessions/v0.1.86 -- apps/shared/coding-sessions`).
7
+ No source changes intended and no consumer action required.
8
+
3
9
  ## 0.1.85
4
10
 
5
11
  Automatic changed-only republish (docs/metadata drift since the last tag — see
@@ -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,
@@ -162,6 +164,22 @@ function createAiMatrxRawBackupSink(options) {
162
164
  // src/engine/agent-attach.ts
163
165
  var import_promises2 = __toESM(require("fs/promises"), 1);
164
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
165
183
  var AGENT_MCP_SERVER_NAME = "matrx-agent";
166
184
  var AGENT_PLUGIN_NAME = "matrx-agent";
167
185
  function escapeAttr(value) {
@@ -212,6 +230,17 @@ function renderCodexSkillsList(bundle, skillsRoot) {
212
230
  ];
213
231
  return lines.join("\n");
214
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
+ }
215
244
  function tomlString(value) {
216
245
  const scalar = value.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, "\uFFFD");
217
246
  return JSON.stringify(scalar).replace(/\u007f/g, "\\u007f");
@@ -327,9 +356,29 @@ function sessionAgentRecord(bundle, options) {
327
356
  name: bundle.name,
328
357
  definitionHash: bundle.definition_hash,
329
358
  loaded,
330
- unavailable
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 } : {}
331
363
  };
332
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
+ }
333
382
 
334
383
  // src/engine/agents.ts
335
384
  var import_node_crypto2 = require("crypto");
@@ -337,6 +386,7 @@ var import_promises3 = __toESM(require("fs/promises"), 1);
337
386
  var import_node_path2 = __toESM(require("path"), 1);
338
387
  var DEFAULT_SERVER_URL = "https://server.app.matrxserver.com";
339
388
  var DEFAULT_SUPABASE_URL = "https://db.matrxserver.com";
389
+ var DEFAULT_SUPABASE_KEY = "sb_publishable_scm0jzEJiFUB5ItiqOx5Fg_8BOjB6oA";
340
390
  var AGENT_CATALOG_RPCS = /* @__PURE__ */ new Set([
341
391
  "agx_get_list_full",
342
392
  "agx_search",
@@ -354,6 +404,10 @@ var agentsClient = async () => {
354
404
  ]);
355
405
  return { ...matrx, ...portable, ...mcp };
356
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;
357
411
  var SIGNED_OUT_AGENT_MESSAGE = "Sign in to AI Matrx to use your agents.";
358
412
  function userIdOf(credential) {
359
413
  if (!credential || credential.kind !== "user") return null;
@@ -370,21 +424,26 @@ function createAgentConnection(options, dataDir) {
370
424
  const credentials = options?.credentials ?? null;
371
425
  const serverUrl = (options?.serverUrl ?? DEFAULT_SERVER_URL).replace(/\/+$/, "");
372
426
  const supabaseUrl = (options?.supabaseUrl ?? DEFAULT_SUPABASE_URL).replace(/\/+$/, "");
373
- const supabaseKey = options?.supabaseKey ?? null;
427
+ const supabaseKey = options?.supabaseKey ?? (options?.supabaseUrl ? null : DEFAULT_SUPABASE_KEY);
374
428
  const root = import_node_path2.default.join(dataDir, "agents");
375
429
  let mcpBaseUrl = null;
376
430
  const handlers = /* @__PURE__ */ new Map();
377
431
  const secretToSession = /* @__PURE__ */ new Map();
378
- const organizationId = () => {
379
- const raw = typeof options?.organizationId === "function" ? options.organizationId() : options?.organizationId;
432
+ const organizationId = async () => {
433
+ const raw = typeof options?.organizationId === "function" ? await options.organizationId() : options?.organizationId;
380
434
  return raw && raw.trim() ? raw.trim() : null;
381
435
  };
382
- const transport = async () => (await agentsClient()).createMatrxTransport({
383
- baseUrl: serverUrl,
384
- ...credentials ? { credentials } : {},
385
- ...organizationId() ? { organizationId: () => organizationId() } : {},
386
- source: "codingSessionsAgents"
387
- });
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");
388
447
  const credential = async () => credentials ? credentials.get() : null;
389
448
  const requireSignedIn = async () => {
390
449
  if (!await credential()) throw new Error(SIGNED_OUT_AGENT_MESSAGE);
@@ -420,7 +479,7 @@ function createAgentConnection(options, dataDir) {
420
479
  configured: credentials !== null && supabaseKey !== null,
421
480
  signedIn: userId !== null,
422
481
  userId,
423
- organizationId: organizationId()
482
+ organizationId: await organizationId().catch(() => null)
424
483
  };
425
484
  },
426
485
  async catalogRpc(request) {
@@ -467,27 +526,39 @@ function createAgentConnection(options, dataDir) {
467
526
  },
468
527
  async details(agentId) {
469
528
  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
- ]);
529
+ const summary = await (await agentsClient()).fetchPortableAgentSummary(await transport(), agentId);
475
530
  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
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
481
537
  };
482
538
  },
483
539
  async loadBundle(selection) {
484
540
  await requireSignedIn();
485
- const { fetchPortableAgent } = await agentsClient();
486
- return fetchPortableAgent(
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(
487
553
  await transport(),
488
- selection.versionId ? { versionId: selection.versionId } : { agentId: selection.agentId },
489
- { variables: selection.variables ?? {} }
554
+ versionId ? { versionId } : { agentId: selection.agentId },
555
+ { variables: selection.variables ?? {}, surface }
490
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;
491
562
  },
492
563
  async saveSessionBundle(sessionId, bundle) {
493
564
  await import_promises3.default.mkdir(sessionDir(sessionId), { recursive: true, mode: 448 });
@@ -902,7 +973,7 @@ function rolloutItem(item, fallbackId, at) {
902
973
  const id = str2(item.id) ?? fallbackId;
903
974
  switch (item.type) {
904
975
  case "UserMessage": {
905
- const text = stripCodexInjectedContext(itemText(item.content));
976
+ const text = stripCodexInjectedContext(stripAgentBlock(itemText(item.content)));
906
977
  return text.trim() ? [{ kind: "user", id, text, at }] : [];
907
978
  }
908
979
  case "AgentMessage": {
@@ -985,7 +1056,7 @@ function createCodexRolloutNormalizer() {
985
1056
  return rolloutItem(payload.item, fallbackId, at);
986
1057
  }
987
1058
  if (payload.type === "user_message" && !appItems) {
988
- const text = stripCodexInjectedContext(str2(payload.message) ?? "");
1059
+ const text = stripCodexInjectedContext(stripAgentBlock(str2(payload.message) ?? ""));
989
1060
  return text.trim() ? [{ kind: "user", id: fallbackId, text, at }] : [];
990
1061
  }
991
1062
  return [];
@@ -2463,6 +2534,12 @@ async function createCodingSessionsEngine(options = {}) {
2463
2534
  if (file) fileOffset = (await import_promises8.default.stat(file).catch(() => null))?.size ?? null;
2464
2535
  }
2465
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
+ }
2466
2543
  if (session.agent) {
2467
2544
  const bundle = await agents.readSessionBundle(session.id);
2468
2545
  if (!bundle) {
@@ -2480,6 +2557,10 @@ async function createCodingSessionsEngine(options = {}) {
2480
2557
  mcpUrl: await agents.mcpUrlFor(session.id)
2481
2558
  });
2482
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 } };
2483
2564
  }
2484
2565
  const child = forkWorker();
2485
2566
  const liveRun = {
@@ -2495,6 +2576,10 @@ async function createCodingSessionsEngine(options = {}) {
2495
2576
  };
2496
2577
  live.set(runId, liveRun);
2497
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
+ }
2498
2583
  const splitter = createLineSplitter();
2499
2584
  const claude = provider === "claude" ? createClaudeNormalizer() : null;
2500
2585
  const codex = provider === "codex" ? createCodexStreamNormalizer(runId.slice(0, 8)) : null;
@@ -2533,6 +2618,7 @@ async function createCodingSessionsEngine(options = {}) {
2533
2618
  }
2534
2619
  }
2535
2620
  void pruneDriverScratch();
2621
+ if (provider === "codex" && final.status === "completed") void checkCompaction(session.id);
2536
2622
  };
2537
2623
  let settled = false;
2538
2624
  child.on("message", (raw) => {
@@ -2647,7 +2733,7 @@ async function createCodingSessionsEngine(options = {}) {
2647
2733
  provider,
2648
2734
  runId,
2649
2735
  cwd: session.cwd,
2650
- prompt,
2736
+ prompt: cliPrompt,
2651
2737
  model,
2652
2738
  effort: session.effort ?? null,
2653
2739
  resumeSessionId: session.providerSessionId,
@@ -2658,6 +2744,28 @@ async function createCodingSessionsEngine(options = {}) {
2658
2744
  });
2659
2745
  return run;
2660
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
+ }
2661
2769
  function killGroup(liveRun) {
2662
2770
  const target = liveRun.pgid;
2663
2771
  if (target === null) return;
@@ -2914,7 +3022,6 @@ async function createCodingSessionsEngine(options = {}) {
2914
3022
  const prompt = input.prompt.trim();
2915
3023
  if (!prompt) throw new Error("Type what you want done before starting.");
2916
3024
  const cwd = await assertFolder(input.cwd);
2917
- const bundle = input.agent ? await agents.loadBundle(input.agent) : null;
2918
3025
  const draft = newSession({
2919
3026
  provider: input.provider,
2920
3027
  cwd,
@@ -2924,28 +3031,40 @@ async function createCodingSessionsEngine(options = {}) {
2924
3031
  origin: "started",
2925
3032
  providerSessionId: null
2926
3033
  });
2927
- if (bundle) {
2928
- await agents.saveSessionBundle(draft.id, bundle);
2929
- draft.agent = sessionAgentRecord(bundle, { toolsServed: await agents.mcpUrlFor(draft.id) !== null });
2930
- }
3034
+ if (input.agent) draft.agent = await attachAgent(draft.id, input.agent, { blockDue: false });
2931
3035
  const session = saveSession(draft);
2932
3036
  const run = await startRun(session, prompt);
2933
3037
  return { session: index.session(session.id) ?? session, run, queued: false };
2934
3038
  },
2935
3039
  async takeover(input) {
2936
3040
  const existing = index.session(input.providerSessionId);
2937
- if (existing) return existing;
3041
+ if (existing) {
3042
+ if (!input.agent) return existing;
3043
+ return engine.setAgent(existing.id, input.agent);
3044
+ }
2938
3045
  const cwd = await assertFolder(input.cwd);
2939
- return saveSession(
2940
- newSession({
2941
- provider: input.provider,
2942
- cwd,
2943
- title: input.title?.trim() || "Taken-over chat",
2944
- model: null,
2945
- origin: "takeover",
2946
- providerSessionId: input.providerSessionId
2947
- })
2948
- );
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 });
2949
3068
  },
2950
3069
  async send(sessionId, rawPrompt) {
2951
3070
  const prompt = rawPrompt.trim();
@@ -3061,6 +3180,8 @@ function openFolderIn(folder, target) {
3061
3180
  }
3062
3181
  // Annotate the CommonJS export names for ESM import in node:
3063
3182
  0 && (module.exports = {
3183
+ DEFAULT_SERVER_URL,
3184
+ DEFAULT_SUPABASE_URL,
3064
3185
  EFFORT_LEVELS,
3065
3186
  PreferenceStore,
3066
3187
  RAW_TRANSCRIPT_MAX_BYTES,