@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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @ai-matrx/coding-sessions
2
2
 
3
+ ## 0.1.87
4
+
5
+ Automatic changed-only republish (docs/metadata drift since the last tag — see
6
+ `git diff npm/coding-sessions/v0.1.86..npm/coding-sessions/v0.1.87 -- apps/shared/coding-sessions`).
7
+ No source changes intended and no consumer action required.
8
+
9
+ ## 0.1.86
10
+
11
+ Automatic changed-only republish (docs/metadata drift since the last tag — see
12
+ `git diff npm/coding-sessions/v0.1.85..npm/coding-sessions/v0.1.86 -- apps/shared/coding-sessions`).
13
+ No source changes intended and no consumer action required.
14
+
3
15
  ## 0.1.85
4
16
 
5
17
  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,8 +164,25 @@ 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";
185
+ 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.";
167
186
  function escapeAttr(value) {
168
187
  return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;");
169
188
  }
@@ -212,6 +231,20 @@ function renderCodexSkillsList(bundle, skillsRoot) {
212
231
  ];
213
232
  return lines.join("\n");
214
233
  }
234
+ function renderFallbackBlock(bundle, skillsRoot, options = {}) {
235
+ const skills = renderCodexSkillsList(bundle, skillsRoot);
236
+ 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.";
237
+ const body = renderAgentInstructions(bundle).trim().replace(/^(<ai_matrx_agent [^>]*>)/, `$1
238
+
239
+ ${lead}`);
240
+ return skills ? `${body}
241
+
242
+ ${skills}
243
+
244
+ ` : `${body}
245
+
246
+ `;
247
+ }
215
248
  function tomlString(value) {
216
249
  const scalar = value.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, "\uFFFD");
217
250
  return JSON.stringify(scalar).replace(/\u007f/g, "\\u007f");
@@ -272,10 +305,16 @@ async function writeAgentAttachment(dir, bundle, options) {
272
305
  pluginDir,
273
306
  mcpConfigFile,
274
307
  mcpUrl,
275
- // Same order as Claude's: the skills list first, the agent's words last.
276
- codexInstructions: skillsList ? `${skillsList}
308
+ // Same order as Claude's: the skills list first, the agent's words last —
309
+ // then the one sentence that lets a later block replace them. Codex ranks
310
+ // developer instructions above a user message, so without this permission
311
+ // a mid-chat switch or update is ignored (measured live: the v2 block was
312
+ // delivered and Codex kept ending with v1's line).
313
+ codexInstructions: `${skillsList ? `${skillsList}
277
314
 
278
- ${instructions}` : instructions
315
+ ` : ""}${instructions}
316
+ ${CODEX_REPLACEMENT_CLAUSE}
317
+ `
279
318
  };
280
319
  }
281
320
  function agentArgsFor(provider, attachment, options) {
@@ -327,9 +366,29 @@ function sessionAgentRecord(bundle, options) {
327
366
  name: bundle.name,
328
367
  definitionHash: bundle.definition_hash,
329
368
  loaded,
330
- unavailable
369
+ unavailable,
370
+ skills: bundle.skills.map((skill) => skill.label),
371
+ tools: options.toolsServed ? bundle.tools.map((tool) => tool.canonical_name) : [],
372
+ ...options.variables ? { variables: options.variables } : {}
331
373
  };
332
374
  }
375
+ 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";
376
+ async function codexCompactionDroppedAgent(rolloutFile, sinceIso) {
377
+ const text = await import_promises2.default.readFile(rolloutFile, "utf8").catch(() => "");
378
+ if (!text.includes('"type":"compacted"')) return false;
379
+ let latest = null;
380
+ for (const line of text.split("\n")) {
381
+ if (!line.includes('"type":"compacted"')) continue;
382
+ try {
383
+ const entry = JSON.parse(line);
384
+ if (entry.type === "compacted") latest = entry;
385
+ } catch {
386
+ }
387
+ }
388
+ if (!latest) return false;
389
+ if (sinceIso && latest.timestamp && latest.timestamp <= sinceIso) return false;
390
+ return !JSON.stringify(latest.payload?.replacement_history ?? "").includes(AGENT_BLOCK_MARKER.trim());
391
+ }
333
392
 
334
393
  // src/engine/agents.ts
335
394
  var import_node_crypto2 = require("crypto");
@@ -337,6 +396,7 @@ var import_promises3 = __toESM(require("fs/promises"), 1);
337
396
  var import_node_path2 = __toESM(require("path"), 1);
338
397
  var DEFAULT_SERVER_URL = "https://server.app.matrxserver.com";
339
398
  var DEFAULT_SUPABASE_URL = "https://db.matrxserver.com";
399
+ var DEFAULT_SUPABASE_KEY = "sb_publishable_scm0jzEJiFUB5ItiqOx5Fg_8BOjB6oA";
340
400
  var AGENT_CATALOG_RPCS = /* @__PURE__ */ new Set([
341
401
  "agx_get_list_full",
342
402
  "agx_search",
@@ -354,6 +414,10 @@ var agentsClient = async () => {
354
414
  ]);
355
415
  return { ...matrx, ...portable, ...mcp };
356
416
  };
417
+ function sortedValues(values) {
418
+ return Object.keys(values).sort().map((key) => [key, values[key]]);
419
+ }
420
+ var BUNDLE_CACHE_MS = 24 * 60 * 60 * 1e3;
357
421
  var SIGNED_OUT_AGENT_MESSAGE = "Sign in to AI Matrx to use your agents.";
358
422
  function userIdOf(credential) {
359
423
  if (!credential || credential.kind !== "user") return null;
@@ -370,21 +434,26 @@ function createAgentConnection(options, dataDir) {
370
434
  const credentials = options?.credentials ?? null;
371
435
  const serverUrl = (options?.serverUrl ?? DEFAULT_SERVER_URL).replace(/\/+$/, "");
372
436
  const supabaseUrl = (options?.supabaseUrl ?? DEFAULT_SUPABASE_URL).replace(/\/+$/, "");
373
- const supabaseKey = options?.supabaseKey ?? null;
437
+ const supabaseKey = options?.supabaseKey ?? (options?.supabaseUrl ? null : DEFAULT_SUPABASE_KEY);
374
438
  const root = import_node_path2.default.join(dataDir, "agents");
375
439
  let mcpBaseUrl = null;
376
440
  const handlers = /* @__PURE__ */ new Map();
377
441
  const secretToSession = /* @__PURE__ */ new Map();
378
- const organizationId = () => {
379
- const raw = typeof options?.organizationId === "function" ? options.organizationId() : options?.organizationId;
442
+ const organizationId = async () => {
443
+ const raw = typeof options?.organizationId === "function" ? await options.organizationId() : options?.organizationId;
380
444
  return raw && raw.trim() ? raw.trim() : null;
381
445
  };
382
- const transport = async () => (await agentsClient()).createMatrxTransport({
383
- baseUrl: serverUrl,
384
- ...credentials ? { credentials } : {},
385
- ...organizationId() ? { organizationId: () => organizationId() } : {},
386
- source: "codingSessionsAgents"
387
- });
446
+ const transport = async () => {
447
+ const org = await organizationId();
448
+ return (await agentsClient()).createMatrxTransport({
449
+ baseUrl: serverUrl,
450
+ ...credentials ? { credentials } : {},
451
+ ...org ? { organizationId: org } : {},
452
+ source: "codingSessionsAgents"
453
+ });
454
+ };
455
+ const cacheDir = import_node_path2.default.join(dataDir, "agents", ".bundles");
456
+ const cacheKey = (definitionHash, selection, surface) => (0, import_node_crypto2.createHash)("sha256").update(JSON.stringify([definitionHash, sortedValues(selection.variables ?? {}), surface])).digest("hex");
388
457
  const credential = async () => credentials ? credentials.get() : null;
389
458
  const requireSignedIn = async () => {
390
459
  if (!await credential()) throw new Error(SIGNED_OUT_AGENT_MESSAGE);
@@ -420,7 +489,7 @@ function createAgentConnection(options, dataDir) {
420
489
  configured: credentials !== null && supabaseKey !== null,
421
490
  signedIn: userId !== null,
422
491
  userId,
423
- organizationId: organizationId()
492
+ organizationId: await organizationId().catch(() => null)
424
493
  };
425
494
  },
426
495
  async catalogRpc(request) {
@@ -467,27 +536,39 @@ function createAgentConnection(options, dataDir) {
467
536
  },
468
537
  async details(agentId) {
469
538
  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
- ]);
539
+ const summary = await (await agentsClient()).fetchPortableAgentSummary(await transport(), agentId);
475
540
  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
541
+ agentId: summary.agent_id,
542
+ name: summary.name,
543
+ variables: summary.variables,
544
+ versions: summary.versions.map((v) => ({ id: v.id, versionNumber: v.version_number, changedAt: v.changed_at })),
545
+ latestVersionNumber: summary.version_number,
546
+ latestVersionId: summary.version_id
481
547
  };
482
548
  },
483
549
  async loadBundle(selection) {
484
550
  await requireSignedIn();
485
- const { fetchPortableAgent } = await agentsClient();
486
- return fetchPortableAgent(
551
+ const client = await agentsClient();
552
+ const summary = await client.fetchPortableAgentSummary(await transport(), selection.agentId);
553
+ const versionId = selection.versionId ?? summary.version_id;
554
+ const definitionHash = versionId ? summary.versions.find((v) => v.id === versionId)?.definition_hash ?? null : summary.definition_hash;
555
+ const surface = "matrx-user/code-editor";
556
+ const file = definitionHash ? import_node_path2.default.join(cacheDir, `${cacheKey(definitionHash, selection, surface)}.json`) : null;
557
+ if (file) {
558
+ const cached = await import_promises3.default.readFile(file, "utf8").then((text) => JSON.parse(text)).catch(() => null);
559
+ const fresh = await import_promises3.default.stat(file).then((st) => Date.now() - st.mtimeMs < BUNDLE_CACHE_MS).catch(() => false);
560
+ if (cached && fresh && cached.definition_hash === definitionHash) return cached;
561
+ }
562
+ const bundle = await client.fetchPortableAgent(
487
563
  await transport(),
488
- selection.versionId ? { versionId: selection.versionId } : { agentId: selection.agentId },
489
- { variables: selection.variables ?? {} }
564
+ versionId ? { versionId } : { agentId: selection.agentId },
565
+ { variables: selection.variables ?? {}, surface }
490
566
  );
567
+ if (file && bundle.definition_hash === definitionHash) {
568
+ await import_promises3.default.mkdir(cacheDir, { recursive: true, mode: 448 });
569
+ await import_promises3.default.writeFile(file, JSON.stringify(bundle), { mode: 384 });
570
+ }
571
+ return bundle;
491
572
  },
492
573
  async saveSessionBundle(sessionId, bundle) {
493
574
  await import_promises3.default.mkdir(sessionDir(sessionId), { recursive: true, mode: 448 });
@@ -623,7 +704,7 @@ function createClaudeNormalizer() {
623
704
  if (typeof content === "string") {
624
705
  if (isSyntheticClaudeUserText(content) || content.trim().length === 0) return [];
625
706
  userCounter += 1;
626
- return [{ kind: "user", id: str(entry.uuid) ?? `user-${userCounter}`, text: content, at }];
707
+ return [{ kind: "user", id: str(entry.uuid) ?? `user-${userCounter}`, text: stripAgentBlock(content), at }];
627
708
  }
628
709
  if (!Array.isArray(content)) return [];
629
710
  const items = [];
@@ -647,7 +728,7 @@ function createClaudeNormalizer() {
647
728
  items.unshift({
648
729
  kind: "user",
649
730
  id: str(entry.uuid) ?? `user-${userCounter}`,
650
- text: typed.join("\n\n"),
731
+ text: stripAgentBlock(typed.join("\n\n")),
651
732
  at
652
733
  });
653
734
  }
@@ -902,7 +983,7 @@ function rolloutItem(item, fallbackId, at) {
902
983
  const id = str2(item.id) ?? fallbackId;
903
984
  switch (item.type) {
904
985
  case "UserMessage": {
905
- const text = stripCodexInjectedContext(itemText(item.content));
986
+ const text = stripCodexInjectedContext(stripAgentBlock(itemText(item.content)));
906
987
  return text.trim() ? [{ kind: "user", id, text, at }] : [];
907
988
  }
908
989
  case "AgentMessage": {
@@ -985,7 +1066,7 @@ function createCodexRolloutNormalizer() {
985
1066
  return rolloutItem(payload.item, fallbackId, at);
986
1067
  }
987
1068
  if (payload.type === "user_message" && !appItems) {
988
- const text = stripCodexInjectedContext(str2(payload.message) ?? "");
1069
+ const text = stripCodexInjectedContext(stripAgentBlock(str2(payload.message) ?? ""));
989
1070
  return text.trim() ? [{ kind: "user", id: fallbackId, text, at }] : [];
990
1071
  }
991
1072
  return [];
@@ -2463,6 +2544,12 @@ async function createCodingSessionsEngine(options = {}) {
2463
2544
  if (file) fileOffset = (await import_promises8.default.stat(file).catch(() => null))?.size ?? null;
2464
2545
  }
2465
2546
  let agentArgs = [];
2547
+ let cliPrompt = prompt;
2548
+ let delivered = null;
2549
+ if (!session.agent && session.agentRemovedDue) {
2550
+ cliPrompt = `${AGENT_REMOVED_BLOCK}${prompt}`;
2551
+ delivered = { agentRemovedDue: false };
2552
+ }
2466
2553
  if (session.agent) {
2467
2554
  const bundle = await agents.readSessionBundle(session.id);
2468
2555
  if (!bundle) {
@@ -2480,6 +2567,10 @@ async function createCodingSessionsEngine(options = {}) {
2480
2567
  mcpUrl: await agents.mcpUrlFor(session.id)
2481
2568
  });
2482
2569
  agentArgs = agentArgsFor(provider, attachment, { resuming: session.providerSessionId !== null });
2570
+ if (session.agent.blockDue) {
2571
+ cliPrompt = `${renderFallbackBlock(bundle, import_node_path9.default.join(attachment.dir, "skills"), { replaces: session.agent.replaces })}${prompt}`;
2572
+ }
2573
+ delivered = { agent: { ...session.agent, blockDue: false, deliveredAt: run.startedAt } };
2483
2574
  }
2484
2575
  const child = forkWorker();
2485
2576
  const liveRun = {
@@ -2495,6 +2586,10 @@ async function createCodingSessionsEngine(options = {}) {
2495
2586
  };
2496
2587
  live.set(runId, liveRun);
2497
2588
  emitItem(liveRun, { kind: "user", id: `run-${runId}`, text: prompt, at: run.startedAt });
2589
+ if (delivered) {
2590
+ const current = index.session(session.id);
2591
+ if (current) saveSession({ ...current, ...delivered });
2592
+ }
2498
2593
  const splitter = createLineSplitter();
2499
2594
  const claude = provider === "claude" ? createClaudeNormalizer() : null;
2500
2595
  const codex = provider === "codex" ? createCodexStreamNormalizer(runId.slice(0, 8)) : null;
@@ -2533,6 +2628,7 @@ async function createCodingSessionsEngine(options = {}) {
2533
2628
  }
2534
2629
  }
2535
2630
  void pruneDriverScratch();
2631
+ if (provider === "codex" && final.status === "completed") void checkCompaction(session.id);
2536
2632
  };
2537
2633
  let settled = false;
2538
2634
  child.on("message", (raw) => {
@@ -2647,7 +2743,7 @@ async function createCodingSessionsEngine(options = {}) {
2647
2743
  provider,
2648
2744
  runId,
2649
2745
  cwd: session.cwd,
2650
- prompt,
2746
+ prompt: cliPrompt,
2651
2747
  model,
2652
2748
  effort: session.effort ?? null,
2653
2749
  resumeSessionId: session.providerSessionId,
@@ -2658,6 +2754,29 @@ async function createCodingSessionsEngine(options = {}) {
2658
2754
  });
2659
2755
  return run;
2660
2756
  }
2757
+ async function checkCompaction(sessionId) {
2758
+ const session = index.session(sessionId);
2759
+ if (!session?.agent || session.agent.blockDue || !session.providerSessionId) return;
2760
+ const file = await findSessionFile("codex", session.providerSessionId, homes);
2761
+ if (!file || !await codexCompactionDroppedAgent(file, session.agent.deliveredAt ?? null)) return;
2762
+ const current = index.session(sessionId);
2763
+ if (current?.agent) {
2764
+ saveSession({ ...current, agent: { ...current.agent, blockDue: true } });
2765
+ }
2766
+ }
2767
+ async function attachAgent(sessionId, selection, options2) {
2768
+ const bundle = await agents.loadBundle(selection);
2769
+ await agents.saveSessionBundle(sessionId, bundle);
2770
+ return {
2771
+ ...sessionAgentRecord(bundle, {
2772
+ toolsServed: await agents.mcpUrlFor(sessionId) !== null,
2773
+ variables: selection.variables ?? {}
2774
+ }),
2775
+ blockDue: options2.blockDue,
2776
+ deliveredAt: null,
2777
+ replaces: options2.replaces ?? null
2778
+ };
2779
+ }
2661
2780
  function killGroup(liveRun) {
2662
2781
  const target = liveRun.pgid;
2663
2782
  if (target === null) return;
@@ -2914,7 +3033,6 @@ async function createCodingSessionsEngine(options = {}) {
2914
3033
  const prompt = input.prompt.trim();
2915
3034
  if (!prompt) throw new Error("Type what you want done before starting.");
2916
3035
  const cwd = await assertFolder(input.cwd);
2917
- const bundle = input.agent ? await agents.loadBundle(input.agent) : null;
2918
3036
  const draft = newSession({
2919
3037
  provider: input.provider,
2920
3038
  cwd,
@@ -2924,28 +3042,43 @@ async function createCodingSessionsEngine(options = {}) {
2924
3042
  origin: "started",
2925
3043
  providerSessionId: null
2926
3044
  });
2927
- if (bundle) {
2928
- await agents.saveSessionBundle(draft.id, bundle);
2929
- draft.agent = sessionAgentRecord(bundle, { toolsServed: await agents.mcpUrlFor(draft.id) !== null });
2930
- }
3045
+ if (input.agent) draft.agent = await attachAgent(draft.id, input.agent, { blockDue: false });
2931
3046
  const session = saveSession(draft);
2932
3047
  const run = await startRun(session, prompt);
2933
3048
  return { session: index.session(session.id) ?? session, run, queued: false };
2934
3049
  },
2935
3050
  async takeover(input) {
2936
3051
  const existing = index.session(input.providerSessionId);
2937
- if (existing) return existing;
3052
+ if (existing) {
3053
+ if (!input.agent) return existing;
3054
+ return engine.setAgent(existing.id, input.agent);
3055
+ }
2938
3056
  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
- );
3057
+ const draft = newSession({
3058
+ provider: input.provider,
3059
+ cwd,
3060
+ title: input.title?.trim() || "Taken-over chat",
3061
+ model: null,
3062
+ origin: "takeover",
3063
+ providerSessionId: input.providerSessionId
3064
+ });
3065
+ if (input.agent) draft.agent = await attachAgent(draft.id, input.agent, { blockDue: input.provider === "codex" });
3066
+ return saveSession(draft);
3067
+ },
3068
+ async setAgent(sessionId, selection) {
3069
+ const session = index.session(String(sessionId ?? ""));
3070
+ if (!session) throw new Error("This session is not in the list anymore.");
3071
+ if (!selection) {
3072
+ const told = session.agent?.deliveredAt != null;
3073
+ return saveSession({ ...session, agent: null, agentRemovedDue: told });
3074
+ }
3075
+ const previous = session.agent && session.agent.deliveredAt != null ? session.agent : null;
3076
+ const started = session.providerSessionId !== null;
3077
+ const agent = await attachAgent(session.id, selection, {
3078
+ blockDue: started,
3079
+ replaces: previous ? `${previous.name}${previous.versionNumber !== null ? ` v${previous.versionNumber}` : ""}` : null
3080
+ });
3081
+ return saveSession({ ...session, agent, agentRemovedDue: false });
2949
3082
  },
2950
3083
  async send(sessionId, rawPrompt) {
2951
3084
  const prompt = rawPrompt.trim();
@@ -3061,6 +3194,8 @@ function openFolderIn(folder, target) {
3061
3194
  }
3062
3195
  // Annotate the CommonJS export names for ESM import in node:
3063
3196
  0 && (module.exports = {
3197
+ DEFAULT_SERVER_URL,
3198
+ DEFAULT_SUPABASE_URL,
3064
3199
  EFFORT_LEVELS,
3065
3200
  PreferenceStore,
3066
3201
  RAW_TRANSCRIPT_MAX_BYTES,