remote-codex 0.11.20 → 0.11.22

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.
@@ -11,7 +11,7 @@ import fs27 from "fs";
11
11
  import Fastify from "fastify";
12
12
  import multipart from "@fastify/multipart";
13
13
  import websocket from "@fastify/websocket";
14
- import { spawn as spawn6 } from "child_process";
14
+ import { spawn as spawn5 } from "child_process";
15
15
  import fs26 from "fs";
16
16
  import path28 from "path";
17
17
  import { ZodError } from "zod";
@@ -11658,7 +11658,8 @@ var DEFAULT_CLAUDE_MODELS = [
11658
11658
  { reasoningEffort: "low", description: "Low effort" },
11659
11659
  { reasoningEffort: "medium", description: "Medium effort" },
11660
11660
  { reasoningEffort: "high", description: "High effort" },
11661
- { reasoningEffort: "xhigh", description: "Extra high effort" }
11661
+ { reasoningEffort: "xhigh", description: "Extra high effort" },
11662
+ { reasoningEffort: "max", description: "Maximum effort" }
11662
11663
  ],
11663
11664
  defaultReasoningEffort: "medium"
11664
11665
  },
@@ -11673,7 +11674,24 @@ var DEFAULT_CLAUDE_MODELS = [
11673
11674
  { reasoningEffort: "low", description: "Low effort" },
11674
11675
  { reasoningEffort: "medium", description: "Medium effort" },
11675
11676
  { reasoningEffort: "high", description: "High effort" },
11676
- { reasoningEffort: "xhigh", description: "Extra high effort" }
11677
+ { reasoningEffort: "xhigh", description: "Extra high effort" },
11678
+ { reasoningEffort: "max", description: "Maximum effort" }
11679
+ ],
11680
+ defaultReasoningEffort: "medium"
11681
+ },
11682
+ {
11683
+ id: "fable",
11684
+ model: "fable",
11685
+ displayName: "Claude Fable",
11686
+ description: "Claude Code Fable model alias.",
11687
+ isDefault: false,
11688
+ hidden: false,
11689
+ supportedReasoningEfforts: [
11690
+ { reasoningEffort: "low", description: "Low effort" },
11691
+ { reasoningEffort: "medium", description: "Medium effort" },
11692
+ { reasoningEffort: "high", description: "High effort" },
11693
+ { reasoningEffort: "xhigh", description: "Extra high effort" },
11694
+ { reasoningEffort: "max", description: "Maximum effort" }
11677
11695
  ],
11678
11696
  defaultReasoningEffort: "medium"
11679
11697
  },
@@ -11688,7 +11706,8 @@ var DEFAULT_CLAUDE_MODELS = [
11688
11706
  { reasoningEffort: "low", description: "Low effort" },
11689
11707
  { reasoningEffort: "medium", description: "Medium effort" },
11690
11708
  { reasoningEffort: "high", description: "High effort" },
11691
- { reasoningEffort: "xhigh", description: "Extra high effort" }
11709
+ { reasoningEffort: "xhigh", description: "Extra high effort" },
11710
+ { reasoningEffort: "max", description: "Maximum effort" }
11692
11711
  ],
11693
11712
  defaultReasoningEffort: "medium"
11694
11713
  },
@@ -11737,7 +11756,7 @@ function mapModelInfo(model, index) {
11737
11756
  isDefault: index === 0,
11738
11757
  hidden: false,
11739
11758
  supportedReasoningEfforts: (model.supportedEffortLevels ?? []).map((effort) => ({
11740
- reasoningEffort: effort === "max" ? "xhigh" : effort,
11759
+ reasoningEffort: effort,
11741
11760
  description: `${effort} effort`
11742
11761
  })),
11743
11762
  defaultReasoningEffort: model.supportsEffort ? "medium" : null
@@ -11747,6 +11766,7 @@ function withClaudeCodeModelAliases(models) {
11747
11766
  const output = [...models];
11748
11767
  const defaultSonnet = DEFAULT_CLAUDE_MODELS[0];
11749
11768
  const oneMillionSonnet = DEFAULT_CLAUDE_MODELS[1];
11769
+ const fable = DEFAULT_CLAUDE_MODELS[2];
11750
11770
  const hasSonnetAlias = output.some((model) => model.model === "sonnet");
11751
11771
  if (!hasSonnetAlias) {
11752
11772
  output.unshift(defaultSonnet);
@@ -11754,6 +11774,9 @@ function withClaudeCodeModelAliases(models) {
11754
11774
  if (!output.some((model) => model.model === "sonnet[1m]")) {
11755
11775
  output.splice(1, 0, oneMillionSonnet);
11756
11776
  }
11777
+ if (!output.some((model) => model.model === "fable")) {
11778
+ output.splice(2, 0, fable);
11779
+ }
11757
11780
  return output.map((model, index) => ({
11758
11781
  ...model,
11759
11782
  isDefault: index === 0
@@ -12112,7 +12135,7 @@ function queryOptionsForRuntime(input) {
12112
12135
  options.betas = ["context-1m-2025-08-07"];
12113
12136
  }
12114
12137
  if (input.reasoningEffort) {
12115
- const effort = input.reasoningEffort === "xhigh" ? "max" : input.reasoningEffort;
12138
+ const effort = input.reasoningEffort;
12116
12139
  if (["low", "medium", "high", "xhigh", "max"].includes(effort)) {
12117
12140
  options.effort = effort;
12118
12141
  }
@@ -16814,6 +16837,13 @@ var ThreadLiveStateStore = class {
16814
16837
  const visibleTurnIds = new Set(visibleTurns.map((turn) => turn.id));
16815
16838
  return visibleTurnIds.has(reconciled.turnId) ? reconciled : null;
16816
16839
  }
16840
+ getLiveItemsForTurn(localThreadId, turnId) {
16841
+ if (!turnId) {
16842
+ return null;
16843
+ }
16844
+ const current = this.threadLiveItems.get(localThreadId);
16845
+ return current?.turnId === turnId ? current : null;
16846
+ }
16817
16847
  upsertLiveItem(localThreadId, turnId, item) {
16818
16848
  const current = this.threadLiveItems.get(localThreadId);
16819
16849
  const currentItems = current?.turnId === turnId ? current.items : [];
@@ -17386,12 +17416,20 @@ var ThreadRuntimeEventProjector = class {
17386
17416
  lastError: null,
17387
17417
  lastTurnStartedAt: (/* @__PURE__ */ new Date()).toISOString()
17388
17418
  });
17419
+ liveState.setLiveItems(record.id, null);
17389
17420
  liveState.resetRecordedTurnItemOrder(record.id, turnId);
17390
17421
  for (const item of event.turn.items) {
17391
- liveState.recordTurnItemOrder(record.id, turnId, item.id);
17422
+ const sequence = liveState.recordTurnItemOrder(record.id, turnId, item.id);
17423
+ const eventTimestamp = (/* @__PURE__ */ new Date()).toISOString();
17424
+ const orderedLiveItem = {
17425
+ ...withHistoryItemCreatedAt(item, eventTimestamp),
17426
+ sequence
17427
+ };
17428
+ const transportLiveItem = deferHistoryItemDetailForTransport(orderedLiveItem);
17429
+ callbacks.persistLiveHistoryItem(record.id, turnId, orderedLiveItem);
17430
+ liveState.upsertLiveItem(record.id, turnId, transportLiveItem);
17392
17431
  }
17393
17432
  liveState.setLivePlan(record.id, null);
17394
- liveState.setLiveItems(record.id, null);
17395
17433
  if (shouldResetThreadContextUsageForTurnStart(callbacks.getThreadContextUsage(record.id))) {
17396
17434
  callbacks.resetThreadContextUsage(record.id, true);
17397
17435
  }
@@ -17529,6 +17567,7 @@ var ThreadRuntimeEventProjector = class {
17529
17567
  sequence,
17530
17568
  createdAt
17531
17569
  });
17570
+ callbacks.invalidateThreadDetailCache(record.id);
17532
17571
  callbacks.emitThreadEvent("thread.output.delta", record.id, {
17533
17572
  turnId: displayTurnId,
17534
17573
  itemId: event.itemId,
@@ -18068,14 +18107,23 @@ var ThreadDetailAssembler = class {
18068
18107
  if (remoteSession.turns.length > 0 && remoteSession.turns.every((turn) => turn.items.length === 0)) {
18069
18108
  remoteSession = await this.input.callbacks.resumeRemoteSession(input.record);
18070
18109
  }
18071
- this.input.callbacks.updateThreadRecord(
18072
- input.record.id,
18073
- this.input.callbacks.buildThreadPatch(
18074
- remoteSession,
18075
- input.record.model,
18076
- input.record.reasoningEffort
18077
- )
18110
+ const threadPatch = this.input.callbacks.buildThreadPatch(
18111
+ remoteSession,
18112
+ input.record.model,
18113
+ input.record.reasoningEffort
18078
18114
  );
18115
+ const activeDisplayTurnId = this.input.liveState.displayTurnIdForRuntimeTurn(
18116
+ input.localThreadId,
18117
+ input.record.providerTurnId
18118
+ ) ?? input.record.providerTurnId;
18119
+ const activeLiveItems = this.input.liveState.getLiveItemsForTurn(
18120
+ input.localThreadId,
18121
+ activeDisplayTurnId
18122
+ );
18123
+ if (input.record.providerTurnId && threadPatch.status === "idle" && activeLiveItems && activeLiveItems.items.length > 0) {
18124
+ threadPatch.status = "running";
18125
+ }
18126
+ this.input.callbacks.updateThreadRecord(input.record.id, threadPatch);
18079
18127
  const updated = this.input.callbacks.getUpdatedThreadRecord(input.record.id);
18080
18128
  this.input.callbacks.syncAfterRemoteSession(updated.id, remoteSession);
18081
18129
  const deferredDetails = /* @__PURE__ */ new Map();
@@ -18091,9 +18139,16 @@ var ThreadDetailAssembler = class {
18091
18139
  remoteSession.turns
18092
18140
  );
18093
18141
  const visibleTurns = this.input.liveState.visibleRemoteTurns(input.localThreadId, remoteSession.turns).map((turn) => agentTurnToThreadTurnDto(turn, deferredDetails));
18094
- const orderedVisibleTurns = applyLiveAgentMessageOrderingHints(
18142
+ const visibleTurnsWithActiveLiveTurn = appendActiveLiveTurnIfMissing(
18095
18143
  visibleTurns,
18096
18144
  input.localThreadId,
18145
+ updated.providerTurnId,
18146
+ this.input.liveState,
18147
+ input.turnMetadataById
18148
+ );
18149
+ const orderedVisibleTurns = applyLiveAgentMessageOrderingHints(
18150
+ visibleTurnsWithActiveLiveTurn,
18151
+ input.localThreadId,
18097
18152
  this.input.liveState
18098
18153
  );
18099
18154
  const resolvedTurnMetadataById = resolveTurnMetadataByVisibleTurnId(
@@ -18223,6 +18278,26 @@ function applyLiveAgentMessageOrderingHints(turns, localThreadId, liveState) {
18223
18278
  } : turn;
18224
18279
  });
18225
18280
  }
18281
+ function appendActiveLiveTurnIfMissing(turns, localThreadId, providerTurnId, liveState, metadataById) {
18282
+ const displayTurnId = liveState.displayTurnIdForRuntimeTurn(localThreadId, providerTurnId) ?? providerTurnId;
18283
+ if (!displayTurnId || turns.some((turn) => turn.id === displayTurnId)) {
18284
+ return turns;
18285
+ }
18286
+ const liveItems = liveState.getLiveItemsForTurn(localThreadId, displayTurnId);
18287
+ if (!liveItems || liveItems.items.length === 0) {
18288
+ return turns;
18289
+ }
18290
+ return [
18291
+ ...turns,
18292
+ {
18293
+ id: displayTurnId,
18294
+ startedAt: metadataById.get(displayTurnId)?.createdAt ?? null,
18295
+ status: "inProgress",
18296
+ error: null,
18297
+ items: sortHistoryItemsBySequence(liveItems.items)
18298
+ }
18299
+ ];
18300
+ }
18226
18301
  function buildTurnDto(turn, metadata) {
18227
18302
  const tokenUsage = parseThreadTurnTokenUsageJson(metadata?.tokenUsageJson);
18228
18303
  const displayPrompt = metadata?.displayPrompt?.trim();
@@ -18321,6 +18396,7 @@ function normalizeReasoningEffort(value) {
18321
18396
  case "medium":
18322
18397
  case "high":
18323
18398
  case "xhigh":
18399
+ case "max":
18324
18400
  return value;
18325
18401
  default:
18326
18402
  return null;
@@ -18336,6 +18412,7 @@ function normalizeReasoningEffort2(value) {
18336
18412
  case "medium":
18337
18413
  case "high":
18338
18414
  case "xhigh":
18415
+ case "max":
18339
18416
  return value;
18340
18417
  default:
18341
18418
  return null;
@@ -18764,7 +18841,6 @@ var ThreadPromptTurnCoordinator = class {
18764
18841
  }
18765
18842
  updateThreadRecord(this.db, localThreadId, patch);
18766
18843
  this.liveState.setLivePlan(localThreadId, null);
18767
- this.liveState.setLiveItems(localThreadId, null);
18768
18844
  if (shouldResetThreadContextUsageForTurnStart(
18769
18845
  this.callbacks.getThreadContextUsage(localThreadId)
18770
18846
  )) {
@@ -19460,27 +19536,41 @@ var ThreadDeletionCoordinator = class {
19460
19536
  };
19461
19537
 
19462
19538
  // src/exports/thread-pdf-export.ts
19463
- import { spawn as spawn2 } from "child_process";
19464
19539
  import fs11 from "fs";
19465
- import os3 from "os";
19540
+ import { createRequire as createRequire3 } from "module";
19466
19541
  import path15 from "path";
19467
- import { pathToFileURL as pathToFileURL3 } from "url";
19468
19542
  import puppeteer from "puppeteer-core";
19469
19543
  import { marked } from "marked";
19470
19544
  var MAX_TEXT_CHARS = 12e3;
19471
19545
  var MAX_COMMAND_OUTPUT_CHARS = 2400;
19472
19546
  var MAX_DETAIL_LINES = 8;
19473
- var EXPORT_FONT_FAMILY = '"Segoe UI", "RemoteCodexCJK", "Microsoft YaHei", "DengXian", "SimSun", "Noto Sans CJK SC", "Noto Sans CJK", "DejaVu Sans", Arial, sans-serif';
19474
- var EXPORT_MONO_FONT_FAMILY = '"SFMono-Regular", Consolas, "Liberation Mono", "DejaVu Sans Mono", "RemoteCodexCJK", monospace';
19547
+ var EXPORT_FONT_FAMILY = '"Noto Sans", "Noto Sans SC", "Segoe UI", "RemoteCodexCJK", "Microsoft YaHei", "DengXian", "SimSun", "Noto Sans CJK SC", "Noto Sans CJK", "DejaVu Sans", Arial, sans-serif';
19548
+ var EXPORT_MONO_FONT_FAMILY = '"SFMono-Regular", Consolas, "Liberation Mono", "DejaVu Sans Mono", "Noto Sans SC", "RemoteCodexCJK", monospace';
19475
19549
  var EMBEDDED_CJK_FONT_CANDIDATES = [
19550
+ { path: "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc", format: "truetype", weight: 400 },
19551
+ { path: "/usr/share/fonts/opentype/noto/NotoSansCJKsc-Regular.otf", format: "opentype", weight: 400 },
19552
+ { path: "/usr/share/fonts/truetype/noto/NotoSansCJK-Regular.ttc", format: "truetype", weight: 400 },
19553
+ { path: "/usr/share/fonts/truetype/wqy/wqy-microhei.ttc", format: "truetype", weight: 400 },
19554
+ { path: "/System/Library/Fonts/PingFang.ttc", format: "truetype", weight: 400 },
19555
+ { path: "/System/Library/Fonts/Supplemental/Arial Unicode.ttf", format: "truetype", weight: 400 },
19476
19556
  { path: "/mnt/c/Windows/Fonts/simhei.ttf", format: "truetype", weight: 400 },
19477
19557
  { path: "/mnt/c/Windows/Fonts/Deng.ttf", format: "truetype", weight: 400 },
19478
19558
  { path: "/mnt/c/Windows/Fonts/msyh.ttc", format: "truetype", weight: 400 }
19479
19559
  ];
19560
+ var BUNDLED_LATIN_FONT_CSS_FILES = [
19561
+ { packageName: "@fontsource/noto-sans", cssFile: "latin-400.css" },
19562
+ { packageName: "@fontsource/noto-sans", cssFile: "latin-700.css" }
19563
+ ];
19564
+ var BUNDLED_CJK_FONT_CSS_FILES = [
19565
+ { packageName: "@fontsource/noto-sans-sc", cssFile: "400.css" },
19566
+ { packageName: "@fontsource/noto-sans-sc", cssFile: "700.css" }
19567
+ ];
19480
19568
  var PUPPETEER_CHANNEL = "chrome";
19481
19569
  var PDF_EXPORT_TIMEOUT_MS = 45e3;
19482
- var MAX_CHROME_STDERR_CHARS = 4e3;
19483
- var embeddedCjkFontCss = null;
19570
+ var require2 = createRequire3(import.meta.url);
19571
+ var embeddedSystemCjkFontCss = null;
19572
+ var packageRootCache = /* @__PURE__ */ new Map();
19573
+ var fontFaceBlockCache = /* @__PURE__ */ new Map();
19484
19574
  function escapeHtml(value) {
19485
19575
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
19486
19576
  }
@@ -19489,21 +19579,21 @@ marked.use({
19489
19579
  breaks: true,
19490
19580
  gfm: true
19491
19581
  });
19492
- function renderEmbeddedCjkFontCss() {
19582
+ function renderEmbeddedSystemCjkFontCss() {
19493
19583
  if (process.env.NODE_ENV === "test" || process.env.VITEST) {
19494
19584
  return "";
19495
19585
  }
19496
- if (embeddedCjkFontCss !== null) {
19497
- return embeddedCjkFontCss;
19586
+ if (embeddedSystemCjkFontCss !== null) {
19587
+ return embeddedSystemCjkFontCss;
19498
19588
  }
19499
- embeddedCjkFontCss = "";
19589
+ embeddedSystemCjkFontCss = "";
19500
19590
  for (const candidate of EMBEDDED_CJK_FONT_CANDIDATES) {
19501
19591
  try {
19502
19592
  if (!fs11.existsSync(candidate.path)) {
19503
19593
  continue;
19504
19594
  }
19505
19595
  const font = fs11.readFileSync(candidate.path);
19506
- embeddedCjkFontCss = `
19596
+ embeddedSystemCjkFontCss = `
19507
19597
  @font-face {
19508
19598
  font-family: "RemoteCodexCJK";
19509
19599
  src: url("data:font/${candidate.format};base64,${font.toString("base64")}") format("${candidate.format}");
@@ -19512,10 +19602,123 @@ function renderEmbeddedCjkFontCss() {
19512
19602
  }`;
19513
19603
  break;
19514
19604
  } catch {
19515
- embeddedCjkFontCss = "";
19605
+ embeddedSystemCjkFontCss = "";
19606
+ }
19607
+ }
19608
+ return embeddedSystemCjkFontCss;
19609
+ }
19610
+ function renderPdfEmbeddedFontCss(snapshot) {
19611
+ if (process.env.NODE_ENV === "test" || process.env.VITEST) {
19612
+ return "";
19613
+ }
19614
+ const usedCodePoints = collectSnapshotCodePoints(snapshot);
19615
+ const latinCss = BUNDLED_LATIN_FONT_CSS_FILES.flatMap((source) => renderBundledFontCss(source));
19616
+ const cjkCss = BUNDLED_CJK_FONT_CSS_FILES.flatMap((source) => renderBundledFontCss(source, usedCodePoints));
19617
+ const bundledCss = [...latinCss, ...cjkCss].join("\n");
19618
+ const needsCjkFallback = containsCjkCodePoint(usedCodePoints) && cjkCss.length === 0;
19619
+ return [bundledCss, needsCjkFallback ? renderEmbeddedSystemCjkFontCss() : ""].filter(Boolean).join("\n");
19620
+ }
19621
+ function collectSnapshotCodePoints(snapshot) {
19622
+ const values = [
19623
+ snapshot.thread.title,
19624
+ snapshot.workspace.label,
19625
+ snapshot.workspace.absPath,
19626
+ snapshot.thread.model ?? "",
19627
+ snapshot.exportedAt,
19628
+ ...snapshot.turns.flatMap((turn) => [
19629
+ turn.error ?? "",
19630
+ turn.status,
19631
+ turn.startedAt,
19632
+ ...turn.items.flatMap((item) => [
19633
+ item.text ?? "",
19634
+ item.previewText ?? "",
19635
+ item.detailText ?? "",
19636
+ item.status ?? ""
19637
+ ])
19638
+ ])
19639
+ ].filter((value) => typeof value === "string");
19640
+ const codePoints = /* @__PURE__ */ new Set();
19641
+ for (const value of values) {
19642
+ for (const character of value) {
19643
+ codePoints.add(character.codePointAt(0));
19644
+ }
19645
+ }
19646
+ return codePoints;
19647
+ }
19648
+ function containsCjkCodePoint(codePoints) {
19649
+ for (const codePoint of codePoints) {
19650
+ if (codePoint >= 13312 && codePoint <= 40959 || codePoint >= 63744 && codePoint <= 64255 || codePoint >= 131072 && codePoint <= 195103) {
19651
+ return true;
19516
19652
  }
19517
19653
  }
19518
- return embeddedCjkFontCss;
19654
+ return false;
19655
+ }
19656
+ function renderBundledFontCss(source, usedCodePoints) {
19657
+ const packageRoot = resolvePackageRoot3(source.packageName);
19658
+ const cssPath = path15.join(packageRoot, source.cssFile);
19659
+ const blocks = getFontFaceBlocks(cssPath);
19660
+ return blocks.filter((block) => !usedCodePoints || fontFaceIntersects(block.ranges, usedCodePoints)).map((block) => inlineFontFaceBlock(block, packageRoot)).filter(Boolean);
19661
+ }
19662
+ function resolvePackageRoot3(packageName) {
19663
+ const cached = packageRootCache.get(packageName);
19664
+ if (cached) {
19665
+ return cached;
19666
+ }
19667
+ const packageRoot = path15.dirname(require2.resolve(`${packageName}/package.json`));
19668
+ packageRootCache.set(packageName, packageRoot);
19669
+ return packageRoot;
19670
+ }
19671
+ function getFontFaceBlocks(cssPath) {
19672
+ const cached = fontFaceBlockCache.get(cssPath);
19673
+ if (cached) {
19674
+ return cached;
19675
+ }
19676
+ const css = fs11.readFileSync(cssPath, "utf8");
19677
+ const blocks = Array.from(css.matchAll(/@font-face\s*{[\s\S]*?}/g)).map((match) => {
19678
+ const block = match[0];
19679
+ const fontPath = block.match(/url\((['"]?)(\.\/files\/[^)'"]+\.woff2)\1\)\s*format\((['"]?)woff2\3\)/)?.[2] ?? "";
19680
+ return {
19681
+ block,
19682
+ fontPath,
19683
+ ranges: parseUnicodeRanges(block.match(/unicode-range:\s*([^;]+);/)?.[1])
19684
+ };
19685
+ }).filter((block) => block.fontPath);
19686
+ fontFaceBlockCache.set(cssPath, blocks);
19687
+ return blocks;
19688
+ }
19689
+ function parseUnicodeRanges(value) {
19690
+ if (!value) {
19691
+ return [{ start: 0, end: 1114111 }];
19692
+ }
19693
+ return value.split(",").flatMap((part) => {
19694
+ const match = part.trim().match(/^U\+([0-9a-f?]+)(?:-([0-9a-f]+))?$/i);
19695
+ if (!match) {
19696
+ return [];
19697
+ }
19698
+ const startText = match[1].replace(/\?/g, "0");
19699
+ const endText = match[2] ?? match[1].replace(/\?/g, "F");
19700
+ return [{
19701
+ start: Number.parseInt(startText, 16),
19702
+ end: Number.parseInt(endText, 16)
19703
+ }];
19704
+ });
19705
+ }
19706
+ function fontFaceIntersects(ranges, usedCodePoints) {
19707
+ for (const codePoint of usedCodePoints) {
19708
+ if (ranges.some((range) => codePoint >= range.start && codePoint <= range.end)) {
19709
+ return true;
19710
+ }
19711
+ }
19712
+ return false;
19713
+ }
19714
+ function inlineFontFaceBlock(block, packageRoot) {
19715
+ const fontPath = path15.join(packageRoot, block.fontPath);
19716
+ try {
19717
+ const font = fs11.readFileSync(fontPath);
19718
+ return block.block.replace(/font-display:\s*swap;/g, "font-display: block;").replace(/src:\s*[^;]+;/, `src: url("data:font/woff2;base64,${font.toString("base64")}") format("woff2");`);
19719
+ } catch {
19720
+ return "";
19721
+ }
19519
19722
  }
19520
19723
  function formatDateTime(value) {
19521
19724
  if (!value) {
@@ -19878,7 +20081,7 @@ function totalPrice(snapshot) {
19878
20081
  0
19879
20082
  );
19880
20083
  }
19881
- function renderThreadExportHtml(snapshot) {
20084
+ function renderThreadExportHtml(snapshot, options = {}) {
19882
20085
  const turnNumbers = snapshot.turns.map((turn) => snapshot.selectedTurnNumbers.get(turn.id)).filter((value) => typeof value === "number");
19883
20086
  const sortedTurnNumbers = [...turnNumbers].sort((left, right) => left - right);
19884
20087
  const isContiguous = sortedTurnNumbers.length > 1 && sortedTurnNumbers.every((value, index) => index === 0 || value === sortedTurnNumbers[index - 1] + 1);
@@ -19892,7 +20095,7 @@ function renderThreadExportHtml(snapshot) {
19892
20095
  <meta charset="utf-8" />
19893
20096
  <title>${escapeHtml(snapshot.thread.title)} transcript</title>
19894
20097
  <style>
19895
- ${renderEmbeddedCjkFontCss()}
20098
+ ${options.embedFonts ? renderPdfEmbeddedFontCss(snapshot) : ""}
19896
20099
  @page { margin: 0.46in 0.45in 0.5in; }
19897
20100
  * { box-sizing: border-box; }
19898
20101
  body {
@@ -20179,7 +20382,6 @@ function renderThreadExportStandaloneHtml(snapshot) {
20179
20382
  <meta name="viewport" content="width=device-width, initial-scale=1" />
20180
20383
  <title>${escapeHtml(snapshot.thread.title)} transcript</title>
20181
20384
  <style>
20182
- ${renderEmbeddedCjkFontCss()}
20183
20385
  :root {
20184
20386
  color-scheme: light;
20185
20387
  --page: rgb(244 239 231);
@@ -20506,7 +20708,7 @@ endobj
20506
20708
  `
20507
20709
  );
20508
20710
  }
20509
- return renderPdfWithChromeCli(renderThreadExportHtml(snapshot));
20711
+ return renderPdfWithChrome(renderThreadExportHtml(snapshot, { embedFonts: true }));
20510
20712
  }
20511
20713
  function resolvePdfBrowserExecutablePath() {
20512
20714
  try {
@@ -20522,88 +20724,52 @@ function resolvePdfBrowserExecutablePath() {
20522
20724
  );
20523
20725
  }
20524
20726
  }
20525
- async function renderPdfWithChromeCli(html) {
20727
+ async function renderPdfWithChrome(html) {
20526
20728
  const executablePath = resolvePdfBrowserExecutablePath();
20527
- const tempDir = await fs11.promises.mkdtemp(path15.join(os3.tmpdir(), "remote-codex-pdf-"));
20528
- const htmlPath = path15.join(tempDir, "thread-export.html");
20529
- const pdfPath = path15.join(tempDir, "thread-export.pdf");
20729
+ const browser = await puppeteer.launch({
20730
+ executablePath,
20731
+ headless: true,
20732
+ timeout: PDF_EXPORT_TIMEOUT_MS,
20733
+ args: [
20734
+ "--disable-gpu",
20735
+ "--disable-dev-shm-usage",
20736
+ "--no-sandbox",
20737
+ "--disable-setuid-sandbox",
20738
+ "--font-render-hinting=none"
20739
+ ]
20740
+ });
20530
20741
  try {
20531
- await fs11.promises.writeFile(htmlPath, html, "utf8");
20532
- await printHtmlToPdf({
20533
- executablePath,
20534
- htmlPath,
20535
- pdfPath
20742
+ const page = await browser.newPage();
20743
+ page.setDefaultTimeout(PDF_EXPORT_TIMEOUT_MS);
20744
+ await page.setContent(html, {
20745
+ waitUntil: "load",
20746
+ timeout: PDF_EXPORT_TIMEOUT_MS
20747
+ });
20748
+ await page.evaluate(async () => {
20749
+ const fonts = document.fonts;
20750
+ if (fonts?.ready) {
20751
+ await fonts.ready;
20752
+ }
20536
20753
  });
20537
- const pdf = await fs11.promises.readFile(pdfPath);
20754
+ const pdf = Buffer.from(await page.pdf({
20755
+ format: "Letter",
20756
+ margin: {
20757
+ top: "0px",
20758
+ right: "0px",
20759
+ bottom: "0px",
20760
+ left: "0px"
20761
+ },
20762
+ preferCSSPageSize: true,
20763
+ printBackground: true,
20764
+ timeout: PDF_EXPORT_TIMEOUT_MS
20765
+ }));
20538
20766
  if (pdf.length === 0 || !pdf.subarray(0, 4).equals(Buffer.from("%PDF"))) {
20539
20767
  throw new Error("Chrome did not produce a valid PDF file.");
20540
20768
  }
20541
20769
  return pdf;
20542
20770
  } finally {
20543
- await fs11.promises.rm(tempDir, { force: true, recursive: true });
20544
- }
20545
- }
20546
- async function printHtmlToPdf(input) {
20547
- const args = [
20548
- "--headless",
20549
- "--disable-gpu",
20550
- "--disable-dev-shm-usage",
20551
- "--no-sandbox",
20552
- "--disable-setuid-sandbox",
20553
- "--font-render-hinting=none",
20554
- "--no-pdf-header-footer",
20555
- "--print-to-pdf-no-header",
20556
- `--print-to-pdf=${input.pdfPath}`,
20557
- pathToFileURL3(input.htmlPath).href
20558
- ];
20559
- await new Promise((resolve, reject) => {
20560
- const child = spawn2(input.executablePath, args, {
20561
- stdio: ["ignore", "ignore", "pipe"]
20562
- });
20563
- let settled = false;
20564
- let stderr = "";
20565
- const complete = (error) => {
20566
- if (settled) {
20567
- return;
20568
- }
20569
- settled = true;
20570
- clearTimeout(timeout);
20571
- child.kill("SIGTERM");
20572
- if (error) {
20573
- reject(error);
20574
- } else {
20575
- resolve();
20576
- }
20577
- };
20578
- const timeout = setTimeout(() => {
20579
- child.kill("SIGKILL");
20580
- complete(new Error(`Chrome PDF export timed out after ${PDF_EXPORT_TIMEOUT_MS}ms.`));
20581
- }, PDF_EXPORT_TIMEOUT_MS);
20582
- child.stderr.on("data", (chunk) => {
20583
- stderr = truncateChromeStderr(`${stderr}${chunk.toString("utf8")}`);
20584
- if (stderr.includes("bytes written to file")) {
20585
- complete();
20586
- }
20587
- });
20588
- child.on("error", (error) => {
20589
- complete(error);
20590
- });
20591
- child.on("close", (code, signal) => {
20592
- if (code === 0) {
20593
- complete();
20594
- return;
20595
- }
20596
- const reason = signal ? `signal ${signal}` : `exit code ${code ?? "unknown"}`;
20597
- const detail = stderr ? ` ${stderr}` : "";
20598
- complete(new Error(`Chrome PDF export failed with ${reason}.${detail}`));
20599
- });
20600
- });
20601
- }
20602
- function truncateChromeStderr(value) {
20603
- if (value.length <= MAX_CHROME_STDERR_CHARS) {
20604
- return value;
20771
+ await browser.close();
20605
20772
  }
20606
- return value.slice(value.length - MAX_CHROME_STDERR_CHARS);
20607
20773
  }
20608
20774
 
20609
20775
  // src/thread-turn-metadata.ts
@@ -20963,33 +21129,22 @@ async function resolveComparablePath2(absPath) {
20963
21129
  const resolvedParent = await resolveComparablePath2(parentPath);
20964
21130
  return path17.join(resolvedParent, path17.basename(resolved));
20965
21131
  }
20966
- async function resolveImportedWorkspacePath(workspaceRoot, candidatePath) {
21132
+ async function resolveImportedWorkspacePath(candidatePath) {
20967
21133
  if (!path17.isAbsolute(candidatePath)) {
20968
21134
  throw new HttpError(400, {
20969
21135
  code: "bad_request",
20970
21136
  message: "Imported session path must be absolute."
20971
21137
  });
20972
21138
  }
20973
- const resolvedRoot = await resolveComparablePath2(workspaceRoot);
20974
- const resolvedCandidate = await resolveComparablePath2(candidatePath);
20975
- const normalizedRoot = resolvedRoot.endsWith(path17.sep) ? resolvedRoot : `${resolvedRoot}${path17.sep}`;
20976
- if (resolvedCandidate !== resolvedRoot && !resolvedCandidate.startsWith(normalizedRoot)) {
20977
- throw new HttpError(403, {
20978
- code: "forbidden",
20979
- message: "Imported session path must stay within the configured workspace root."
20980
- });
20981
- }
20982
- return resolvedCandidate;
21139
+ return resolveComparablePath2(candidatePath);
20983
21140
  }
20984
21141
  var ThreadImportCoordinator = class {
20985
- constructor(db, sessionCoordinator, workspaceRoot) {
21142
+ constructor(db, sessionCoordinator, _workspaceRoot) {
20986
21143
  this.db = db;
20987
21144
  this.sessionCoordinator = sessionCoordinator;
20988
- this.workspaceRoot = workspaceRoot;
20989
21145
  }
20990
21146
  db;
20991
21147
  sessionCoordinator;
20992
- workspaceRoot;
20993
21148
  async importLocalThread(input) {
20994
21149
  const normalizedSessionId = input.sessionId.trim();
20995
21150
  if (!normalizedSessionId) {
@@ -21017,10 +21172,7 @@ var ThreadImportCoordinator = class {
21017
21172
  message: "Session not found on this machine."
21018
21173
  });
21019
21174
  }
21020
- const importedPath = await resolveImportedWorkspacePath(
21021
- this.workspaceRoot,
21022
- importSession.cwd
21023
- );
21175
+ const importedPath = await resolveImportedWorkspacePath(importSession.cwd);
21024
21176
  let workspace = getWorkspaceRecordByPath(this.db, importedPath);
21025
21177
  if (!workspace) {
21026
21178
  workspace = createWorkspaceRecord(this.db, {
@@ -22247,7 +22399,7 @@ var ThreadService = class {
22247
22399
 
22248
22400
  // src/routes/agent-runtimes.ts
22249
22401
  import fs15 from "fs/promises";
22250
- import { spawn as spawn3 } from "child_process";
22402
+ import { spawn as spawn2 } from "child_process";
22251
22403
  import path18 from "path";
22252
22404
  import { fileURLToPath } from "url";
22253
22405
  import { z as z3 } from "zod";
@@ -22594,7 +22746,7 @@ function commandFailureMessage(command, result) {
22594
22746
  }
22595
22747
  function runShellCommand(command, timeoutMs = 0) {
22596
22748
  return new Promise((resolve) => {
22597
- const child = spawn3(command, {
22749
+ const child = spawn2(command, {
22598
22750
  shell: true,
22599
22751
  stdio: ["ignore", "pipe", "pipe"]
22600
22752
  });
@@ -22894,19 +23046,28 @@ async function registerSystemRoutes(app2) {
22894
23046
  import fs16 from "fs/promises";
22895
23047
  import path19 from "path";
22896
23048
  import { z as z5 } from "zod";
23049
+ var reasoningEffortValues = [
23050
+ "none",
23051
+ "minimal",
23052
+ "low",
23053
+ "medium",
23054
+ "high",
23055
+ "xhigh",
23056
+ "max"
23057
+ ];
22897
23058
  var createThreadSchema = z5.object({
22898
23059
  workspaceId: z5.string().uuid(),
22899
23060
  title: z5.string().optional(),
22900
23061
  provider: agentBackendIdSchema.optional(),
22901
23062
  model: z5.string().min(1),
22902
- reasoningEffort: z5.enum(["none", "minimal", "low", "medium", "high", "xhigh"]).nullable().optional(),
23063
+ reasoningEffort: z5.enum(reasoningEffortValues).nullable().optional(),
22903
23064
  approvalMode: z5.enum(["yolo", "guarded"]).default("yolo")
22904
23065
  });
22905
23066
  var promptSchema = z5.object({
22906
23067
  prompt: z5.string().min(1),
22907
23068
  clientRequestId: z5.string().min(1).optional(),
22908
23069
  model: z5.string().min(1).optional(),
22909
- reasoningEffort: z5.enum(["none", "minimal", "low", "medium", "high", "xhigh"]).nullable().optional(),
23070
+ reasoningEffort: z5.enum(reasoningEffortValues).nullable().optional(),
22910
23071
  collaborationMode: z5.enum(["default", "plan"]).optional()
22911
23072
  });
22912
23073
  var promptAttachmentManifestEntrySchema = z5.object({
@@ -22920,7 +23081,7 @@ var updateThreadSchema = z5.object({
22920
23081
  });
22921
23082
  var updateThreadSettingsSchema = z5.object({
22922
23083
  model: z5.string().min(1).optional(),
22923
- reasoningEffort: z5.enum(["none", "minimal", "low", "medium", "high", "xhigh"]).nullable().optional(),
23084
+ reasoningEffort: z5.enum(reasoningEffortValues).nullable().optional(),
22924
23085
  fastMode: z5.boolean().optional(),
22925
23086
  collaborationMode: z5.enum(["default", "plan"]).optional()
22926
23087
  }).refine((body) => Object.keys(body).length > 0, {
@@ -23588,9 +23749,9 @@ async function deleteWorkspaceArtifact(record, artifactId) {
23588
23749
  }
23589
23750
 
23590
23751
  // src/workspace-file-service.ts
23591
- import { spawn as spawn4 } from "child_process";
23752
+ import { spawn as spawn3 } from "child_process";
23592
23753
  import fs18 from "fs/promises";
23593
- import os4 from "os";
23754
+ import os3 from "os";
23594
23755
  import path21 from "path";
23595
23756
  var PREVIEW_DEFAULT_LIMIT_BYTES = 5e4;
23596
23757
  var WORKSPACE_UPLOAD_MAX_BYTES = 50 * 1024 * 1024;
@@ -23909,7 +24070,7 @@ async function createFolderZipFile(rootPath, folderPath) {
23909
24070
  endRecord.writeUInt32LE(centralSize, 12);
23910
24071
  endRecord.writeUInt32LE(offset, 16);
23911
24072
  endRecord.writeUInt16LE(0, 20);
23912
- const tempDir = await fs18.mkdtemp(path21.join(os4.tmpdir(), "remote-codex-folder-download-"));
24073
+ const tempDir = await fs18.mkdtemp(path21.join(os3.tmpdir(), "remote-codex-folder-download-"));
23913
24074
  const zipPath = path21.join(tempDir, `${path21.basename(folderPath) || "workspace-folder"}.zip`);
23914
24075
  await fs18.writeFile(zipPath, Buffer.concat([...localParts, ...centralParts, endRecord]));
23915
24076
  return { zipPath, tempDir };
@@ -23954,7 +24115,7 @@ async function pathExists4(absPath) {
23954
24115
  }
23955
24116
  function cloneRepository(gitUrl, targetPath) {
23956
24117
  return new Promise((resolve, reject) => {
23957
- const child = spawn4("git", ["clone", gitUrl, targetPath], {
24118
+ const child = spawn3("git", ["clone", gitUrl, targetPath], {
23958
24119
  stdio: ["ignore", "ignore", "pipe"]
23959
24120
  });
23960
24121
  let stderr = "";
@@ -24822,7 +24983,7 @@ import fs22 from "fs/promises";
24822
24983
 
24823
24984
  // src/shell/shell-prompt.ts
24824
24985
  import fs21 from "fs/promises";
24825
- import os5 from "os";
24986
+ import os4 from "os";
24826
24987
  import path24 from "path";
24827
24988
  function basenameFromPath2(filePath) {
24828
24989
  if (!filePath) {
@@ -24994,7 +25155,7 @@ async function ensureShellPromptInitScript(command) {
24994
25155
  const normalized = command.trim().toLowerCase();
24995
25156
  const extension = normalized === "zsh" ? "zsh" : "sh";
24996
25157
  const filePath = path24.join(
24997
- os5.tmpdir(),
25158
+ os4.tmpdir(),
24998
25159
  `remote-codex-shell-prompt.${extension}`
24999
25160
  );
25000
25161
  await fs21.writeFile(filePath, buildShellPromptInitScriptContents(command), "utf8");
@@ -26154,7 +26315,7 @@ var BackendPluginHost = class {
26154
26315
 
26155
26316
  // src/shell/pty-shell-backend.ts
26156
26317
  import path26 from "path";
26157
- import { spawn as spawn5 } from "@homebridge/node-pty-prebuilt-multiarch";
26318
+ import { spawn as spawn4 } from "@homebridge/node-pty-prebuilt-multiarch";
26158
26319
 
26159
26320
  // src/shell/default-shell.ts
26160
26321
  import fs24 from "fs";
@@ -26223,7 +26384,7 @@ var PtyShellBackend = class {
26223
26384
  if (this.sessions.has(input.sessionId)) {
26224
26385
  return;
26225
26386
  }
26226
- const pty = spawn5(this.shell, shellArgs(this.shell), {
26387
+ const pty = spawn4(this.shell, shellArgs(this.shell), {
26227
26388
  name: "xterm-256color",
26228
26389
  cwd: input.cwd,
26229
26390
  cols: input.cols ?? 120,
@@ -27509,7 +27670,7 @@ function createServiceLifecycle() {
27509
27670
  message: "Build and restart requires a Remote Codex source checkout. Set REMOTE_CODEX_REPO_ROOT to the checkout path, or update the npm package with npm install -g remote-codex@latest."
27510
27671
  });
27511
27672
  }
27512
- const child = spawn6(process.execPath, [restartScript, "launch"], {
27673
+ const child = spawn5(process.execPath, [restartScript, "launch"], {
27513
27674
  cwd: repoRoot,
27514
27675
  detached: true,
27515
27676
  env: process.env,