@ai-matrx/coding-sessions 0.1.85 → 0.1.87

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.
@@ -1,4 +1,4 @@
1
- export { c as CodingSessionsEngine, d as CodingSessionsEngineOptions, E as EFFORT_LEVELS, P as PreferenceStore, e as ProviderHomes, R as RAW_TRANSCRIPT_MAX_BYTES, f as RawBackupPolicy, g as RawBackupSink, h as RunStatus, i as buildCleanEnv, j as claudeDefaults, k as codexDefaults, l as createAiMatrxRawBackupSink, m as createCodingSessionsEngine, n as createRawBackupScheduler, o as defaultProviderHomes, p as describeActivity, q as describeProviderStatus, r as discoverLocalSessions, s as endsWithOpenToolCall, t as findDrivingProcess, u as findSessionFile, v as isAllowedEnvName, w as listProcesses, x as readSessionFile, y as searchLocalChats, z as sessionActivity, A as snippetAround } from '../index-DEPwyr2j.cjs';
1
+ export { e as CodingSessionsAgentsOptions, c as CodingSessionsEngine, d as CodingSessionsEngineOptions, D as DEFAULT_SERVER_URL, f as DEFAULT_SUPABASE_URL, E as EFFORT_LEVELS, P as PreferenceStore, g as ProviderHomes, R as RAW_TRANSCRIPT_MAX_BYTES, h as RawBackupPolicy, i as RawBackupSink, j as RunStatus, k as buildCleanEnv, l as claudeDefaults, m as codexDefaults, n as createAiMatrxRawBackupSink, o as createCodingSessionsEngine, p as createRawBackupScheduler, q as defaultProviderHomes, r as describeActivity, s as describeProviderStatus, t as discoverLocalSessions, u as endsWithOpenToolCall, v as findDrivingProcess, w as findSessionFile, x as isAllowedEnvName, y as listProcesses, z as readSessionFile, A as searchLocalChats, B as sessionActivity, F as snippetAround } from '../index-CDeDKQHe.cjs';
2
2
  import '@ai-matrx/agents/portable/mcp';
3
3
  import '@ai-matrx/agents/portable';
4
4
  import '@ai-matrx/data';
@@ -1,4 +1,4 @@
1
- export { c as CodingSessionsEngine, d as CodingSessionsEngineOptions, E as EFFORT_LEVELS, P as PreferenceStore, e as ProviderHomes, R as RAW_TRANSCRIPT_MAX_BYTES, f as RawBackupPolicy, g as RawBackupSink, h as RunStatus, i as buildCleanEnv, j as claudeDefaults, k as codexDefaults, l as createAiMatrxRawBackupSink, m as createCodingSessionsEngine, n as createRawBackupScheduler, o as defaultProviderHomes, p as describeActivity, q as describeProviderStatus, r as discoverLocalSessions, s as endsWithOpenToolCall, t as findDrivingProcess, u as findSessionFile, v as isAllowedEnvName, w as listProcesses, x as readSessionFile, y as searchLocalChats, z as sessionActivity, A as snippetAround } from '../index-DEPwyr2j.js';
1
+ export { e as CodingSessionsAgentsOptions, c as CodingSessionsEngine, d as CodingSessionsEngineOptions, D as DEFAULT_SERVER_URL, f as DEFAULT_SUPABASE_URL, E as EFFORT_LEVELS, P as PreferenceStore, g as ProviderHomes, R as RAW_TRANSCRIPT_MAX_BYTES, h as RawBackupPolicy, i as RawBackupSink, j as RunStatus, k as buildCleanEnv, l as claudeDefaults, m as codexDefaults, n as createAiMatrxRawBackupSink, o as createCodingSessionsEngine, p as createRawBackupScheduler, q as defaultProviderHomes, r as describeActivity, s as describeProviderStatus, t as discoverLocalSessions, u as endsWithOpenToolCall, v as findDrivingProcess, w as findSessionFile, x as isAllowedEnvName, y as listProcesses, z as readSessionFile, A as searchLocalChats, B as sessionActivity, F as snippetAround } from '../index-CDeDKQHe.js';
2
2
  import '@ai-matrx/agents/portable/mcp';
3
3
  import '@ai-matrx/agents/portable';
4
4
  import '@ai-matrx/data';
@@ -101,8 +101,25 @@ function createAiMatrxRawBackupSink(options) {
101
101
  // src/engine/agent-attach.ts
102
102
  import fs2 from "fs/promises";
103
103
  import path from "path";
104
+
105
+ // src/normalize/agent-block.ts
106
+ var AGENT_BLOCK_MARKER = "<ai_matrx_agent ";
107
+ function stripAgentBlock(text) {
108
+ const removed = text.match(/^<ai_matrx_agent_removed>[\s\S]*?<\/ai_matrx_agent_removed>\s*/);
109
+ if (removed) return text.slice(removed[0].length);
110
+ if (!text.startsWith(AGENT_BLOCK_MARKER)) return text;
111
+ const end = text.indexOf("</ai_matrx_agent>");
112
+ if (end < 0) return text;
113
+ let rest = text.slice(end + "</ai_matrx_agent>".length);
114
+ const skills = rest.match(/^\s*<skills_instructions>[\s\S]*?<\/skills_instructions>/);
115
+ if (skills) rest = rest.slice(skills[0].length);
116
+ return rest.replace(/^\s+/, "");
117
+ }
118
+
119
+ // src/engine/agent-attach.ts
104
120
  var AGENT_MCP_SERVER_NAME = "matrx-agent";
105
121
  var AGENT_PLUGIN_NAME = "matrx-agent";
122
+ var CODEX_REPLACEMENT_CLAUSE = "If a later message in this chat begins with an <ai_matrx_agent> block, that block replaces these AI Matrx agent instructions from then on; an <ai_matrx_agent_removed> block withdraws them.";
106
123
  function escapeAttr(value) {
107
124
  return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;");
108
125
  }
@@ -151,6 +168,20 @@ function renderCodexSkillsList(bundle, skillsRoot) {
151
168
  ];
152
169
  return lines.join("\n");
153
170
  }
171
+ function renderFallbackBlock(bundle, skillsRoot, options = {}) {
172
+ const skills = renderCodexSkillsList(bundle, skillsRoot);
173
+ const lead = options.replaces ? `These instructions REPLACE the earlier AI Matrx agent "${options.replaces}" from this message on. Where they differ from anything said before \u2014 including how earlier replies ended \u2014 follow these.` : "The instructions below are your AI Matrx agent for this chat from now on; they replace any AI Matrx agent instructions given earlier.";
174
+ const body = renderAgentInstructions(bundle).trim().replace(/^(<ai_matrx_agent [^>]*>)/, `$1
175
+
176
+ ${lead}`);
177
+ return skills ? `${body}
178
+
179
+ ${skills}
180
+
181
+ ` : `${body}
182
+
183
+ `;
184
+ }
154
185
  function tomlString(value) {
155
186
  const scalar = value.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, "\uFFFD");
156
187
  return JSON.stringify(scalar).replace(/\u007f/g, "\\u007f");
@@ -211,10 +242,16 @@ async function writeAgentAttachment(dir, bundle, options) {
211
242
  pluginDir,
212
243
  mcpConfigFile,
213
244
  mcpUrl,
214
- // Same order as Claude's: the skills list first, the agent's words last.
215
- codexInstructions: skillsList ? `${skillsList}
245
+ // Same order as Claude's: the skills list first, the agent's words last —
246
+ // then the one sentence that lets a later block replace them. Codex ranks
247
+ // developer instructions above a user message, so without this permission
248
+ // a mid-chat switch or update is ignored (measured live: the v2 block was
249
+ // delivered and Codex kept ending with v1's line).
250
+ codexInstructions: `${skillsList ? `${skillsList}
216
251
 
217
- ${instructions}` : instructions
252
+ ` : ""}${instructions}
253
+ ${CODEX_REPLACEMENT_CLAUSE}
254
+ `
218
255
  };
219
256
  }
220
257
  function agentArgsFor(provider, attachment, options) {
@@ -266,16 +303,37 @@ function sessionAgentRecord(bundle, options) {
266
303
  name: bundle.name,
267
304
  definitionHash: bundle.definition_hash,
268
305
  loaded,
269
- unavailable
306
+ unavailable,
307
+ skills: bundle.skills.map((skill) => skill.label),
308
+ tools: options.toolsServed ? bundle.tools.map((tool) => tool.canonical_name) : [],
309
+ ...options.variables ? { variables: options.variables } : {}
270
310
  };
271
311
  }
312
+ var AGENT_REMOVED_BLOCK = "<ai_matrx_agent_removed>The AI Matrx agent instructions given earlier in this chat no longer apply \u2014 including any rule about how replies end. Answer normally from this message on.</ai_matrx_agent_removed>\n\n";
313
+ async function codexCompactionDroppedAgent(rolloutFile, sinceIso) {
314
+ const text = await fs2.readFile(rolloutFile, "utf8").catch(() => "");
315
+ if (!text.includes('"type":"compacted"')) return false;
316
+ let latest = null;
317
+ for (const line of text.split("\n")) {
318
+ if (!line.includes('"type":"compacted"')) continue;
319
+ try {
320
+ const entry = JSON.parse(line);
321
+ if (entry.type === "compacted") latest = entry;
322
+ } catch {
323
+ }
324
+ }
325
+ if (!latest) return false;
326
+ if (sinceIso && latest.timestamp && latest.timestamp <= sinceIso) return false;
327
+ return !JSON.stringify(latest.payload?.replacement_history ?? "").includes(AGENT_BLOCK_MARKER.trim());
328
+ }
272
329
 
273
330
  // src/engine/agents.ts
274
- import { randomBytes, randomUUID } from "crypto";
331
+ import { createHash as createHash2, randomBytes, randomUUID } from "crypto";
275
332
  import fs3 from "fs/promises";
276
333
  import path2 from "path";
277
334
  var DEFAULT_SERVER_URL = "https://server.app.matrxserver.com";
278
335
  var DEFAULT_SUPABASE_URL = "https://db.matrxserver.com";
336
+ var DEFAULT_SUPABASE_KEY = "sb_publishable_scm0jzEJiFUB5ItiqOx5Fg_8BOjB6oA";
279
337
  var AGENT_CATALOG_RPCS = /* @__PURE__ */ new Set([
280
338
  "agx_get_list_full",
281
339
  "agx_search",
@@ -293,6 +351,10 @@ var agentsClient = async () => {
293
351
  ]);
294
352
  return { ...matrx, ...portable, ...mcp };
295
353
  };
354
+ function sortedValues(values) {
355
+ return Object.keys(values).sort().map((key) => [key, values[key]]);
356
+ }
357
+ var BUNDLE_CACHE_MS = 24 * 60 * 60 * 1e3;
296
358
  var SIGNED_OUT_AGENT_MESSAGE = "Sign in to AI Matrx to use your agents.";
297
359
  function userIdOf(credential) {
298
360
  if (!credential || credential.kind !== "user") return null;
@@ -309,21 +371,26 @@ function createAgentConnection(options, dataDir) {
309
371
  const credentials = options?.credentials ?? null;
310
372
  const serverUrl = (options?.serverUrl ?? DEFAULT_SERVER_URL).replace(/\/+$/, "");
311
373
  const supabaseUrl = (options?.supabaseUrl ?? DEFAULT_SUPABASE_URL).replace(/\/+$/, "");
312
- const supabaseKey = options?.supabaseKey ?? null;
374
+ const supabaseKey = options?.supabaseKey ?? (options?.supabaseUrl ? null : DEFAULT_SUPABASE_KEY);
313
375
  const root = path2.join(dataDir, "agents");
314
376
  let mcpBaseUrl = null;
315
377
  const handlers = /* @__PURE__ */ new Map();
316
378
  const secretToSession = /* @__PURE__ */ new Map();
317
- const organizationId = () => {
318
- const raw = typeof options?.organizationId === "function" ? options.organizationId() : options?.organizationId;
379
+ const organizationId = async () => {
380
+ const raw = typeof options?.organizationId === "function" ? await options.organizationId() : options?.organizationId;
319
381
  return raw && raw.trim() ? raw.trim() : null;
320
382
  };
321
- const transport = async () => (await agentsClient()).createMatrxTransport({
322
- baseUrl: serverUrl,
323
- ...credentials ? { credentials } : {},
324
- ...organizationId() ? { organizationId: () => organizationId() } : {},
325
- source: "codingSessionsAgents"
326
- });
383
+ const transport = async () => {
384
+ const org = await organizationId();
385
+ return (await agentsClient()).createMatrxTransport({
386
+ baseUrl: serverUrl,
387
+ ...credentials ? { credentials } : {},
388
+ ...org ? { organizationId: org } : {},
389
+ source: "codingSessionsAgents"
390
+ });
391
+ };
392
+ const cacheDir = path2.join(dataDir, "agents", ".bundles");
393
+ const cacheKey = (definitionHash, selection, surface) => createHash2("sha256").update(JSON.stringify([definitionHash, sortedValues(selection.variables ?? {}), surface])).digest("hex");
327
394
  const credential = async () => credentials ? credentials.get() : null;
328
395
  const requireSignedIn = async () => {
329
396
  if (!await credential()) throw new Error(SIGNED_OUT_AGENT_MESSAGE);
@@ -359,7 +426,7 @@ function createAgentConnection(options, dataDir) {
359
426
  configured: credentials !== null && supabaseKey !== null,
360
427
  signedIn: userId !== null,
361
428
  userId,
362
- organizationId: organizationId()
429
+ organizationId: await organizationId().catch(() => null)
363
430
  };
364
431
  },
365
432
  async catalogRpc(request) {
@@ -406,27 +473,39 @@ function createAgentConnection(options, dataDir) {
406
473
  },
407
474
  async details(agentId) {
408
475
  await requireSignedIn();
409
- const { fetchPortableAgent, fetchPortableAgentVersions } = await agentsClient();
410
- const [bundle, versions] = await Promise.all([
411
- fetchPortableAgent(await transport(), { agentId }),
412
- fetchPortableAgentVersions(await transport(), agentId).catch(() => [])
413
- ]);
476
+ const summary = await (await agentsClient()).fetchPortableAgentSummary(await transport(), agentId);
414
477
  return {
415
- agentId: bundle.agent_id,
416
- name: bundle.name,
417
- variables: bundle.variables,
418
- versions: versions.map((v) => ({ id: v.id, versionNumber: v.version_number, changedAt: v.changed_at })).sort((a, b) => b.versionNumber - a.versionNumber),
419
- latestVersionNumber: bundle.version_number
478
+ agentId: summary.agent_id,
479
+ name: summary.name,
480
+ variables: summary.variables,
481
+ versions: summary.versions.map((v) => ({ id: v.id, versionNumber: v.version_number, changedAt: v.changed_at })),
482
+ latestVersionNumber: summary.version_number,
483
+ latestVersionId: summary.version_id
420
484
  };
421
485
  },
422
486
  async loadBundle(selection) {
423
487
  await requireSignedIn();
424
- const { fetchPortableAgent } = await agentsClient();
425
- return fetchPortableAgent(
488
+ const client = await agentsClient();
489
+ const summary = await client.fetchPortableAgentSummary(await transport(), selection.agentId);
490
+ const versionId = selection.versionId ?? summary.version_id;
491
+ const definitionHash = versionId ? summary.versions.find((v) => v.id === versionId)?.definition_hash ?? null : summary.definition_hash;
492
+ const surface = "matrx-user/code-editor";
493
+ const file = definitionHash ? path2.join(cacheDir, `${cacheKey(definitionHash, selection, surface)}.json`) : null;
494
+ if (file) {
495
+ const cached = await fs3.readFile(file, "utf8").then((text) => JSON.parse(text)).catch(() => null);
496
+ const fresh = await fs3.stat(file).then((st) => Date.now() - st.mtimeMs < BUNDLE_CACHE_MS).catch(() => false);
497
+ if (cached && fresh && cached.definition_hash === definitionHash) return cached;
498
+ }
499
+ const bundle = await client.fetchPortableAgent(
426
500
  await transport(),
427
- selection.versionId ? { versionId: selection.versionId } : { agentId: selection.agentId },
428
- { variables: selection.variables ?? {} }
501
+ versionId ? { versionId } : { agentId: selection.agentId },
502
+ { variables: selection.variables ?? {}, surface }
429
503
  );
504
+ if (file && bundle.definition_hash === definitionHash) {
505
+ await fs3.mkdir(cacheDir, { recursive: true, mode: 448 });
506
+ await fs3.writeFile(file, JSON.stringify(bundle), { mode: 384 });
507
+ }
508
+ return bundle;
430
509
  },
431
510
  async saveSessionBundle(sessionId, bundle) {
432
511
  await fs3.mkdir(sessionDir(sessionId), { recursive: true, mode: 448 });
@@ -562,7 +641,7 @@ function createClaudeNormalizer() {
562
641
  if (typeof content === "string") {
563
642
  if (isSyntheticClaudeUserText(content) || content.trim().length === 0) return [];
564
643
  userCounter += 1;
565
- return [{ kind: "user", id: str(entry.uuid) ?? `user-${userCounter}`, text: content, at }];
644
+ return [{ kind: "user", id: str(entry.uuid) ?? `user-${userCounter}`, text: stripAgentBlock(content), at }];
566
645
  }
567
646
  if (!Array.isArray(content)) return [];
568
647
  const items = [];
@@ -586,7 +665,7 @@ function createClaudeNormalizer() {
586
665
  items.unshift({
587
666
  kind: "user",
588
667
  id: str(entry.uuid) ?? `user-${userCounter}`,
589
- text: typed.join("\n\n"),
668
+ text: stripAgentBlock(typed.join("\n\n")),
590
669
  at
591
670
  });
592
671
  }
@@ -841,7 +920,7 @@ function rolloutItem(item, fallbackId, at) {
841
920
  const id = str2(item.id) ?? fallbackId;
842
921
  switch (item.type) {
843
922
  case "UserMessage": {
844
- const text = stripCodexInjectedContext(itemText(item.content));
923
+ const text = stripCodexInjectedContext(stripAgentBlock(itemText(item.content)));
845
924
  return text.trim() ? [{ kind: "user", id, text, at }] : [];
846
925
  }
847
926
  case "AgentMessage": {
@@ -924,7 +1003,7 @@ function createCodexRolloutNormalizer() {
924
1003
  return rolloutItem(payload.item, fallbackId, at);
925
1004
  }
926
1005
  if (payload.type === "user_message" && !appItems) {
927
- const text = stripCodexInjectedContext(str2(payload.message) ?? "");
1006
+ const text = stripCodexInjectedContext(stripAgentBlock(str2(payload.message) ?? ""));
928
1007
  return text.trim() ? [{ kind: "user", id: fallbackId, text, at }] : [];
929
1008
  }
930
1009
  return [];
@@ -2402,6 +2481,12 @@ async function createCodingSessionsEngine(options = {}) {
2402
2481
  if (file) fileOffset = (await fs8.stat(file).catch(() => null))?.size ?? null;
2403
2482
  }
2404
2483
  let agentArgs = [];
2484
+ let cliPrompt = prompt;
2485
+ let delivered = null;
2486
+ if (!session.agent && session.agentRemovedDue) {
2487
+ cliPrompt = `${AGENT_REMOVED_BLOCK}${prompt}`;
2488
+ delivered = { agentRemovedDue: false };
2489
+ }
2405
2490
  if (session.agent) {
2406
2491
  const bundle = await agents.readSessionBundle(session.id);
2407
2492
  if (!bundle) {
@@ -2419,6 +2504,10 @@ async function createCodingSessionsEngine(options = {}) {
2419
2504
  mcpUrl: await agents.mcpUrlFor(session.id)
2420
2505
  });
2421
2506
  agentArgs = agentArgsFor(provider, attachment, { resuming: session.providerSessionId !== null });
2507
+ if (session.agent.blockDue) {
2508
+ cliPrompt = `${renderFallbackBlock(bundle, path9.join(attachment.dir, "skills"), { replaces: session.agent.replaces })}${prompt}`;
2509
+ }
2510
+ delivered = { agent: { ...session.agent, blockDue: false, deliveredAt: run.startedAt } };
2422
2511
  }
2423
2512
  const child = forkWorker();
2424
2513
  const liveRun = {
@@ -2434,6 +2523,10 @@ async function createCodingSessionsEngine(options = {}) {
2434
2523
  };
2435
2524
  live.set(runId, liveRun);
2436
2525
  emitItem(liveRun, { kind: "user", id: `run-${runId}`, text: prompt, at: run.startedAt });
2526
+ if (delivered) {
2527
+ const current = index.session(session.id);
2528
+ if (current) saveSession({ ...current, ...delivered });
2529
+ }
2437
2530
  const splitter = createLineSplitter();
2438
2531
  const claude = provider === "claude" ? createClaudeNormalizer() : null;
2439
2532
  const codex = provider === "codex" ? createCodexStreamNormalizer(runId.slice(0, 8)) : null;
@@ -2472,6 +2565,7 @@ async function createCodingSessionsEngine(options = {}) {
2472
2565
  }
2473
2566
  }
2474
2567
  void pruneDriverScratch();
2568
+ if (provider === "codex" && final.status === "completed") void checkCompaction(session.id);
2475
2569
  };
2476
2570
  let settled = false;
2477
2571
  child.on("message", (raw) => {
@@ -2586,7 +2680,7 @@ async function createCodingSessionsEngine(options = {}) {
2586
2680
  provider,
2587
2681
  runId,
2588
2682
  cwd: session.cwd,
2589
- prompt,
2683
+ prompt: cliPrompt,
2590
2684
  model,
2591
2685
  effort: session.effort ?? null,
2592
2686
  resumeSessionId: session.providerSessionId,
@@ -2597,6 +2691,29 @@ async function createCodingSessionsEngine(options = {}) {
2597
2691
  });
2598
2692
  return run;
2599
2693
  }
2694
+ async function checkCompaction(sessionId) {
2695
+ const session = index.session(sessionId);
2696
+ if (!session?.agent || session.agent.blockDue || !session.providerSessionId) return;
2697
+ const file = await findSessionFile("codex", session.providerSessionId, homes);
2698
+ if (!file || !await codexCompactionDroppedAgent(file, session.agent.deliveredAt ?? null)) return;
2699
+ const current = index.session(sessionId);
2700
+ if (current?.agent) {
2701
+ saveSession({ ...current, agent: { ...current.agent, blockDue: true } });
2702
+ }
2703
+ }
2704
+ async function attachAgent(sessionId, selection, options2) {
2705
+ const bundle = await agents.loadBundle(selection);
2706
+ await agents.saveSessionBundle(sessionId, bundle);
2707
+ return {
2708
+ ...sessionAgentRecord(bundle, {
2709
+ toolsServed: await agents.mcpUrlFor(sessionId) !== null,
2710
+ variables: selection.variables ?? {}
2711
+ }),
2712
+ blockDue: options2.blockDue,
2713
+ deliveredAt: null,
2714
+ replaces: options2.replaces ?? null
2715
+ };
2716
+ }
2600
2717
  function killGroup(liveRun) {
2601
2718
  const target = liveRun.pgid;
2602
2719
  if (target === null) return;
@@ -2853,7 +2970,6 @@ async function createCodingSessionsEngine(options = {}) {
2853
2970
  const prompt = input.prompt.trim();
2854
2971
  if (!prompt) throw new Error("Type what you want done before starting.");
2855
2972
  const cwd = await assertFolder(input.cwd);
2856
- const bundle = input.agent ? await agents.loadBundle(input.agent) : null;
2857
2973
  const draft = newSession({
2858
2974
  provider: input.provider,
2859
2975
  cwd,
@@ -2863,28 +2979,43 @@ async function createCodingSessionsEngine(options = {}) {
2863
2979
  origin: "started",
2864
2980
  providerSessionId: null
2865
2981
  });
2866
- if (bundle) {
2867
- await agents.saveSessionBundle(draft.id, bundle);
2868
- draft.agent = sessionAgentRecord(bundle, { toolsServed: await agents.mcpUrlFor(draft.id) !== null });
2869
- }
2982
+ if (input.agent) draft.agent = await attachAgent(draft.id, input.agent, { blockDue: false });
2870
2983
  const session = saveSession(draft);
2871
2984
  const run = await startRun(session, prompt);
2872
2985
  return { session: index.session(session.id) ?? session, run, queued: false };
2873
2986
  },
2874
2987
  async takeover(input) {
2875
2988
  const existing = index.session(input.providerSessionId);
2876
- if (existing) return existing;
2989
+ if (existing) {
2990
+ if (!input.agent) return existing;
2991
+ return engine.setAgent(existing.id, input.agent);
2992
+ }
2877
2993
  const cwd = await assertFolder(input.cwd);
2878
- return saveSession(
2879
- newSession({
2880
- provider: input.provider,
2881
- cwd,
2882
- title: input.title?.trim() || "Taken-over chat",
2883
- model: null,
2884
- origin: "takeover",
2885
- providerSessionId: input.providerSessionId
2886
- })
2887
- );
2994
+ const draft = newSession({
2995
+ provider: input.provider,
2996
+ cwd,
2997
+ title: input.title?.trim() || "Taken-over chat",
2998
+ model: null,
2999
+ origin: "takeover",
3000
+ providerSessionId: input.providerSessionId
3001
+ });
3002
+ if (input.agent) draft.agent = await attachAgent(draft.id, input.agent, { blockDue: input.provider === "codex" });
3003
+ return saveSession(draft);
3004
+ },
3005
+ async setAgent(sessionId, selection) {
3006
+ const session = index.session(String(sessionId ?? ""));
3007
+ if (!session) throw new Error("This session is not in the list anymore.");
3008
+ if (!selection) {
3009
+ const told = session.agent?.deliveredAt != null;
3010
+ return saveSession({ ...session, agent: null, agentRemovedDue: told });
3011
+ }
3012
+ const previous = session.agent && session.agent.deliveredAt != null ? session.agent : null;
3013
+ const started = session.providerSessionId !== null;
3014
+ const agent = await attachAgent(session.id, selection, {
3015
+ blockDue: started,
3016
+ replaces: previous ? `${previous.name}${previous.versionNumber !== null ? ` v${previous.versionNumber}` : ""}` : null
3017
+ });
3018
+ return saveSession({ ...session, agent, agentRemovedDue: false });
2888
3019
  },
2889
3020
  async send(sessionId, rawPrompt) {
2890
3021
  const prompt = rawPrompt.trim();
@@ -2999,6 +3130,8 @@ function openFolderIn(folder, target) {
2999
3130
  });
3000
3131
  }
3001
3132
  export {
3133
+ DEFAULT_SERVER_URL,
3134
+ DEFAULT_SUPABASE_URL,
3002
3135
  EFFORT_LEVELS,
3003
3136
  PreferenceStore,
3004
3137
  RAW_TRANSCRIPT_MAX_BYTES,