remote-codex 0.11.21 → 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,8 @@ 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" }
11677
11679
  ],
11678
11680
  defaultReasoningEffort: "medium"
11679
11681
  },
@@ -11688,7 +11690,8 @@ var DEFAULT_CLAUDE_MODELS = [
11688
11690
  { reasoningEffort: "low", description: "Low effort" },
11689
11691
  { reasoningEffort: "medium", description: "Medium effort" },
11690
11692
  { reasoningEffort: "high", description: "High effort" },
11691
- { reasoningEffort: "xhigh", description: "Extra high effort" }
11693
+ { reasoningEffort: "xhigh", description: "Extra high effort" },
11694
+ { reasoningEffort: "max", description: "Maximum effort" }
11692
11695
  ],
11693
11696
  defaultReasoningEffort: "medium"
11694
11697
  },
@@ -11703,7 +11706,8 @@ var DEFAULT_CLAUDE_MODELS = [
11703
11706
  { reasoningEffort: "low", description: "Low effort" },
11704
11707
  { reasoningEffort: "medium", description: "Medium effort" },
11705
11708
  { reasoningEffort: "high", description: "High effort" },
11706
- { reasoningEffort: "xhigh", description: "Extra high effort" }
11709
+ { reasoningEffort: "xhigh", description: "Extra high effort" },
11710
+ { reasoningEffort: "max", description: "Maximum effort" }
11707
11711
  ],
11708
11712
  defaultReasoningEffort: "medium"
11709
11713
  },
@@ -11752,7 +11756,7 @@ function mapModelInfo(model, index) {
11752
11756
  isDefault: index === 0,
11753
11757
  hidden: false,
11754
11758
  supportedReasoningEfforts: (model.supportedEffortLevels ?? []).map((effort) => ({
11755
- reasoningEffort: effort === "max" ? "xhigh" : effort,
11759
+ reasoningEffort: effort,
11756
11760
  description: `${effort} effort`
11757
11761
  })),
11758
11762
  defaultReasoningEffort: model.supportsEffort ? "medium" : null
@@ -12131,7 +12135,7 @@ function queryOptionsForRuntime(input) {
12131
12135
  options.betas = ["context-1m-2025-08-07"];
12132
12136
  }
12133
12137
  if (input.reasoningEffort) {
12134
- const effort = input.reasoningEffort === "xhigh" ? "max" : input.reasoningEffort;
12138
+ const effort = input.reasoningEffort;
12135
12139
  if (["low", "medium", "high", "xhigh", "max"].includes(effort)) {
12136
12140
  options.effort = effort;
12137
12141
  }
@@ -17563,6 +17567,7 @@ var ThreadRuntimeEventProjector = class {
17563
17567
  sequence,
17564
17568
  createdAt
17565
17569
  });
17570
+ callbacks.invalidateThreadDetailCache(record.id);
17566
17571
  callbacks.emitThreadEvent("thread.output.delta", record.id, {
17567
17572
  turnId: displayTurnId,
17568
17573
  itemId: event.itemId,
@@ -18102,14 +18107,23 @@ var ThreadDetailAssembler = class {
18102
18107
  if (remoteSession.turns.length > 0 && remoteSession.turns.every((turn) => turn.items.length === 0)) {
18103
18108
  remoteSession = await this.input.callbacks.resumeRemoteSession(input.record);
18104
18109
  }
18105
- this.input.callbacks.updateThreadRecord(
18106
- input.record.id,
18107
- this.input.callbacks.buildThreadPatch(
18108
- remoteSession,
18109
- input.record.model,
18110
- input.record.reasoningEffort
18111
- )
18110
+ const threadPatch = this.input.callbacks.buildThreadPatch(
18111
+ remoteSession,
18112
+ input.record.model,
18113
+ input.record.reasoningEffort
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
18112
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);
18113
18127
  const updated = this.input.callbacks.getUpdatedThreadRecord(input.record.id);
18114
18128
  this.input.callbacks.syncAfterRemoteSession(updated.id, remoteSession);
18115
18129
  const deferredDetails = /* @__PURE__ */ new Map();
@@ -18382,6 +18396,7 @@ function normalizeReasoningEffort(value) {
18382
18396
  case "medium":
18383
18397
  case "high":
18384
18398
  case "xhigh":
18399
+ case "max":
18385
18400
  return value;
18386
18401
  default:
18387
18402
  return null;
@@ -18397,6 +18412,7 @@ function normalizeReasoningEffort2(value) {
18397
18412
  case "medium":
18398
18413
  case "high":
18399
18414
  case "xhigh":
18415
+ case "max":
18400
18416
  return value;
18401
18417
  default:
18402
18418
  return null;
@@ -19520,27 +19536,41 @@ var ThreadDeletionCoordinator = class {
19520
19536
  };
19521
19537
 
19522
19538
  // src/exports/thread-pdf-export.ts
19523
- import { spawn as spawn2 } from "child_process";
19524
19539
  import fs11 from "fs";
19525
- import os3 from "os";
19540
+ import { createRequire as createRequire3 } from "module";
19526
19541
  import path15 from "path";
19527
- import { pathToFileURL as pathToFileURL3 } from "url";
19528
19542
  import puppeteer from "puppeteer-core";
19529
19543
  import { marked } from "marked";
19530
19544
  var MAX_TEXT_CHARS = 12e3;
19531
19545
  var MAX_COMMAND_OUTPUT_CHARS = 2400;
19532
19546
  var MAX_DETAIL_LINES = 8;
19533
- var EXPORT_FONT_FAMILY = '"Segoe UI", "RemoteCodexCJK", "Microsoft YaHei", "DengXian", "SimSun", "Noto Sans CJK SC", "Noto Sans CJK", "DejaVu Sans", Arial, sans-serif';
19534
- 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';
19535
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 },
19536
19556
  { path: "/mnt/c/Windows/Fonts/simhei.ttf", format: "truetype", weight: 400 },
19537
19557
  { path: "/mnt/c/Windows/Fonts/Deng.ttf", format: "truetype", weight: 400 },
19538
19558
  { path: "/mnt/c/Windows/Fonts/msyh.ttc", format: "truetype", weight: 400 }
19539
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
+ ];
19540
19568
  var PUPPETEER_CHANNEL = "chrome";
19541
19569
  var PDF_EXPORT_TIMEOUT_MS = 45e3;
19542
- var MAX_CHROME_STDERR_CHARS = 4e3;
19543
- 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();
19544
19574
  function escapeHtml(value) {
19545
19575
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
19546
19576
  }
@@ -19549,21 +19579,21 @@ marked.use({
19549
19579
  breaks: true,
19550
19580
  gfm: true
19551
19581
  });
19552
- function renderEmbeddedCjkFontCss() {
19582
+ function renderEmbeddedSystemCjkFontCss() {
19553
19583
  if (process.env.NODE_ENV === "test" || process.env.VITEST) {
19554
19584
  return "";
19555
19585
  }
19556
- if (embeddedCjkFontCss !== null) {
19557
- return embeddedCjkFontCss;
19586
+ if (embeddedSystemCjkFontCss !== null) {
19587
+ return embeddedSystemCjkFontCss;
19558
19588
  }
19559
- embeddedCjkFontCss = "";
19589
+ embeddedSystemCjkFontCss = "";
19560
19590
  for (const candidate of EMBEDDED_CJK_FONT_CANDIDATES) {
19561
19591
  try {
19562
19592
  if (!fs11.existsSync(candidate.path)) {
19563
19593
  continue;
19564
19594
  }
19565
19595
  const font = fs11.readFileSync(candidate.path);
19566
- embeddedCjkFontCss = `
19596
+ embeddedSystemCjkFontCss = `
19567
19597
  @font-face {
19568
19598
  font-family: "RemoteCodexCJK";
19569
19599
  src: url("data:font/${candidate.format};base64,${font.toString("base64")}") format("${candidate.format}");
@@ -19572,10 +19602,123 @@ function renderEmbeddedCjkFontCss() {
19572
19602
  }`;
19573
19603
  break;
19574
19604
  } catch {
19575
- 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;
19576
19652
  }
19577
19653
  }
19578
- 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
+ }
19579
19722
  }
19580
19723
  function formatDateTime(value) {
19581
19724
  if (!value) {
@@ -19938,7 +20081,7 @@ function totalPrice(snapshot) {
19938
20081
  0
19939
20082
  );
19940
20083
  }
19941
- function renderThreadExportHtml(snapshot) {
20084
+ function renderThreadExportHtml(snapshot, options = {}) {
19942
20085
  const turnNumbers = snapshot.turns.map((turn) => snapshot.selectedTurnNumbers.get(turn.id)).filter((value) => typeof value === "number");
19943
20086
  const sortedTurnNumbers = [...turnNumbers].sort((left, right) => left - right);
19944
20087
  const isContiguous = sortedTurnNumbers.length > 1 && sortedTurnNumbers.every((value, index) => index === 0 || value === sortedTurnNumbers[index - 1] + 1);
@@ -19952,7 +20095,7 @@ function renderThreadExportHtml(snapshot) {
19952
20095
  <meta charset="utf-8" />
19953
20096
  <title>${escapeHtml(snapshot.thread.title)} transcript</title>
19954
20097
  <style>
19955
- ${renderEmbeddedCjkFontCss()}
20098
+ ${options.embedFonts ? renderPdfEmbeddedFontCss(snapshot) : ""}
19956
20099
  @page { margin: 0.46in 0.45in 0.5in; }
19957
20100
  * { box-sizing: border-box; }
19958
20101
  body {
@@ -20239,7 +20382,6 @@ function renderThreadExportStandaloneHtml(snapshot) {
20239
20382
  <meta name="viewport" content="width=device-width, initial-scale=1" />
20240
20383
  <title>${escapeHtml(snapshot.thread.title)} transcript</title>
20241
20384
  <style>
20242
- ${renderEmbeddedCjkFontCss()}
20243
20385
  :root {
20244
20386
  color-scheme: light;
20245
20387
  --page: rgb(244 239 231);
@@ -20566,7 +20708,7 @@ endobj
20566
20708
  `
20567
20709
  );
20568
20710
  }
20569
- return renderPdfWithChromeCli(renderThreadExportHtml(snapshot));
20711
+ return renderPdfWithChrome(renderThreadExportHtml(snapshot, { embedFonts: true }));
20570
20712
  }
20571
20713
  function resolvePdfBrowserExecutablePath() {
20572
20714
  try {
@@ -20582,88 +20724,52 @@ function resolvePdfBrowserExecutablePath() {
20582
20724
  );
20583
20725
  }
20584
20726
  }
20585
- async function renderPdfWithChromeCli(html) {
20727
+ async function renderPdfWithChrome(html) {
20586
20728
  const executablePath = resolvePdfBrowserExecutablePath();
20587
- const tempDir = await fs11.promises.mkdtemp(path15.join(os3.tmpdir(), "remote-codex-pdf-"));
20588
- const htmlPath = path15.join(tempDir, "thread-export.html");
20589
- 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
+ });
20590
20741
  try {
20591
- await fs11.promises.writeFile(htmlPath, html, "utf8");
20592
- await printHtmlToPdf({
20593
- executablePath,
20594
- htmlPath,
20595
- 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
+ }
20596
20753
  });
20597
- 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
+ }));
20598
20766
  if (pdf.length === 0 || !pdf.subarray(0, 4).equals(Buffer.from("%PDF"))) {
20599
20767
  throw new Error("Chrome did not produce a valid PDF file.");
20600
20768
  }
20601
20769
  return pdf;
20602
20770
  } finally {
20603
- await fs11.promises.rm(tempDir, { force: true, recursive: true });
20604
- }
20605
- }
20606
- async function printHtmlToPdf(input) {
20607
- const args = [
20608
- "--headless",
20609
- "--disable-gpu",
20610
- "--disable-dev-shm-usage",
20611
- "--no-sandbox",
20612
- "--disable-setuid-sandbox",
20613
- "--font-render-hinting=none",
20614
- "--no-pdf-header-footer",
20615
- "--print-to-pdf-no-header",
20616
- `--print-to-pdf=${input.pdfPath}`,
20617
- pathToFileURL3(input.htmlPath).href
20618
- ];
20619
- await new Promise((resolve, reject) => {
20620
- const child = spawn2(input.executablePath, args, {
20621
- stdio: ["ignore", "ignore", "pipe"]
20622
- });
20623
- let settled = false;
20624
- let stderr = "";
20625
- const complete = (error) => {
20626
- if (settled) {
20627
- return;
20628
- }
20629
- settled = true;
20630
- clearTimeout(timeout);
20631
- child.kill("SIGTERM");
20632
- if (error) {
20633
- reject(error);
20634
- } else {
20635
- resolve();
20636
- }
20637
- };
20638
- const timeout = setTimeout(() => {
20639
- child.kill("SIGKILL");
20640
- complete(new Error(`Chrome PDF export timed out after ${PDF_EXPORT_TIMEOUT_MS}ms.`));
20641
- }, PDF_EXPORT_TIMEOUT_MS);
20642
- child.stderr.on("data", (chunk) => {
20643
- stderr = truncateChromeStderr(`${stderr}${chunk.toString("utf8")}`);
20644
- if (stderr.includes("bytes written to file")) {
20645
- complete();
20646
- }
20647
- });
20648
- child.on("error", (error) => {
20649
- complete(error);
20650
- });
20651
- child.on("close", (code, signal) => {
20652
- if (code === 0) {
20653
- complete();
20654
- return;
20655
- }
20656
- const reason = signal ? `signal ${signal}` : `exit code ${code ?? "unknown"}`;
20657
- const detail = stderr ? ` ${stderr}` : "";
20658
- complete(new Error(`Chrome PDF export failed with ${reason}.${detail}`));
20659
- });
20660
- });
20661
- }
20662
- function truncateChromeStderr(value) {
20663
- if (value.length <= MAX_CHROME_STDERR_CHARS) {
20664
- return value;
20771
+ await browser.close();
20665
20772
  }
20666
- return value.slice(value.length - MAX_CHROME_STDERR_CHARS);
20667
20773
  }
20668
20774
 
20669
20775
  // src/thread-turn-metadata.ts
@@ -21023,33 +21129,22 @@ async function resolveComparablePath2(absPath) {
21023
21129
  const resolvedParent = await resolveComparablePath2(parentPath);
21024
21130
  return path17.join(resolvedParent, path17.basename(resolved));
21025
21131
  }
21026
- async function resolveImportedWorkspacePath(workspaceRoot, candidatePath) {
21132
+ async function resolveImportedWorkspacePath(candidatePath) {
21027
21133
  if (!path17.isAbsolute(candidatePath)) {
21028
21134
  throw new HttpError(400, {
21029
21135
  code: "bad_request",
21030
21136
  message: "Imported session path must be absolute."
21031
21137
  });
21032
21138
  }
21033
- const resolvedRoot = await resolveComparablePath2(workspaceRoot);
21034
- const resolvedCandidate = await resolveComparablePath2(candidatePath);
21035
- const normalizedRoot = resolvedRoot.endsWith(path17.sep) ? resolvedRoot : `${resolvedRoot}${path17.sep}`;
21036
- if (resolvedCandidate !== resolvedRoot && !resolvedCandidate.startsWith(normalizedRoot)) {
21037
- throw new HttpError(403, {
21038
- code: "forbidden",
21039
- message: "Imported session path must stay within the configured workspace root."
21040
- });
21041
- }
21042
- return resolvedCandidate;
21139
+ return resolveComparablePath2(candidatePath);
21043
21140
  }
21044
21141
  var ThreadImportCoordinator = class {
21045
- constructor(db, sessionCoordinator, workspaceRoot) {
21142
+ constructor(db, sessionCoordinator, _workspaceRoot) {
21046
21143
  this.db = db;
21047
21144
  this.sessionCoordinator = sessionCoordinator;
21048
- this.workspaceRoot = workspaceRoot;
21049
21145
  }
21050
21146
  db;
21051
21147
  sessionCoordinator;
21052
- workspaceRoot;
21053
21148
  async importLocalThread(input) {
21054
21149
  const normalizedSessionId = input.sessionId.trim();
21055
21150
  if (!normalizedSessionId) {
@@ -21077,10 +21172,7 @@ var ThreadImportCoordinator = class {
21077
21172
  message: "Session not found on this machine."
21078
21173
  });
21079
21174
  }
21080
- const importedPath = await resolveImportedWorkspacePath(
21081
- this.workspaceRoot,
21082
- importSession.cwd
21083
- );
21175
+ const importedPath = await resolveImportedWorkspacePath(importSession.cwd);
21084
21176
  let workspace = getWorkspaceRecordByPath(this.db, importedPath);
21085
21177
  if (!workspace) {
21086
21178
  workspace = createWorkspaceRecord(this.db, {
@@ -22307,7 +22399,7 @@ var ThreadService = class {
22307
22399
 
22308
22400
  // src/routes/agent-runtimes.ts
22309
22401
  import fs15 from "fs/promises";
22310
- import { spawn as spawn3 } from "child_process";
22402
+ import { spawn as spawn2 } from "child_process";
22311
22403
  import path18 from "path";
22312
22404
  import { fileURLToPath } from "url";
22313
22405
  import { z as z3 } from "zod";
@@ -22654,7 +22746,7 @@ function commandFailureMessage(command, result) {
22654
22746
  }
22655
22747
  function runShellCommand(command, timeoutMs = 0) {
22656
22748
  return new Promise((resolve) => {
22657
- const child = spawn3(command, {
22749
+ const child = spawn2(command, {
22658
22750
  shell: true,
22659
22751
  stdio: ["ignore", "pipe", "pipe"]
22660
22752
  });
@@ -22954,19 +23046,28 @@ async function registerSystemRoutes(app2) {
22954
23046
  import fs16 from "fs/promises";
22955
23047
  import path19 from "path";
22956
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
+ ];
22957
23058
  var createThreadSchema = z5.object({
22958
23059
  workspaceId: z5.string().uuid(),
22959
23060
  title: z5.string().optional(),
22960
23061
  provider: agentBackendIdSchema.optional(),
22961
23062
  model: z5.string().min(1),
22962
- reasoningEffort: z5.enum(["none", "minimal", "low", "medium", "high", "xhigh"]).nullable().optional(),
23063
+ reasoningEffort: z5.enum(reasoningEffortValues).nullable().optional(),
22963
23064
  approvalMode: z5.enum(["yolo", "guarded"]).default("yolo")
22964
23065
  });
22965
23066
  var promptSchema = z5.object({
22966
23067
  prompt: z5.string().min(1),
22967
23068
  clientRequestId: z5.string().min(1).optional(),
22968
23069
  model: z5.string().min(1).optional(),
22969
- reasoningEffort: z5.enum(["none", "minimal", "low", "medium", "high", "xhigh"]).nullable().optional(),
23070
+ reasoningEffort: z5.enum(reasoningEffortValues).nullable().optional(),
22970
23071
  collaborationMode: z5.enum(["default", "plan"]).optional()
22971
23072
  });
22972
23073
  var promptAttachmentManifestEntrySchema = z5.object({
@@ -22980,7 +23081,7 @@ var updateThreadSchema = z5.object({
22980
23081
  });
22981
23082
  var updateThreadSettingsSchema = z5.object({
22982
23083
  model: z5.string().min(1).optional(),
22983
- reasoningEffort: z5.enum(["none", "minimal", "low", "medium", "high", "xhigh"]).nullable().optional(),
23084
+ reasoningEffort: z5.enum(reasoningEffortValues).nullable().optional(),
22984
23085
  fastMode: z5.boolean().optional(),
22985
23086
  collaborationMode: z5.enum(["default", "plan"]).optional()
22986
23087
  }).refine((body) => Object.keys(body).length > 0, {
@@ -23648,9 +23749,9 @@ async function deleteWorkspaceArtifact(record, artifactId) {
23648
23749
  }
23649
23750
 
23650
23751
  // src/workspace-file-service.ts
23651
- import { spawn as spawn4 } from "child_process";
23752
+ import { spawn as spawn3 } from "child_process";
23652
23753
  import fs18 from "fs/promises";
23653
- import os4 from "os";
23754
+ import os3 from "os";
23654
23755
  import path21 from "path";
23655
23756
  var PREVIEW_DEFAULT_LIMIT_BYTES = 5e4;
23656
23757
  var WORKSPACE_UPLOAD_MAX_BYTES = 50 * 1024 * 1024;
@@ -23969,7 +24070,7 @@ async function createFolderZipFile(rootPath, folderPath) {
23969
24070
  endRecord.writeUInt32LE(centralSize, 12);
23970
24071
  endRecord.writeUInt32LE(offset, 16);
23971
24072
  endRecord.writeUInt16LE(0, 20);
23972
- 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-"));
23973
24074
  const zipPath = path21.join(tempDir, `${path21.basename(folderPath) || "workspace-folder"}.zip`);
23974
24075
  await fs18.writeFile(zipPath, Buffer.concat([...localParts, ...centralParts, endRecord]));
23975
24076
  return { zipPath, tempDir };
@@ -24014,7 +24115,7 @@ async function pathExists4(absPath) {
24014
24115
  }
24015
24116
  function cloneRepository(gitUrl, targetPath) {
24016
24117
  return new Promise((resolve, reject) => {
24017
- const child = spawn4("git", ["clone", gitUrl, targetPath], {
24118
+ const child = spawn3("git", ["clone", gitUrl, targetPath], {
24018
24119
  stdio: ["ignore", "ignore", "pipe"]
24019
24120
  });
24020
24121
  let stderr = "";
@@ -24882,7 +24983,7 @@ import fs22 from "fs/promises";
24882
24983
 
24883
24984
  // src/shell/shell-prompt.ts
24884
24985
  import fs21 from "fs/promises";
24885
- import os5 from "os";
24986
+ import os4 from "os";
24886
24987
  import path24 from "path";
24887
24988
  function basenameFromPath2(filePath) {
24888
24989
  if (!filePath) {
@@ -25054,7 +25155,7 @@ async function ensureShellPromptInitScript(command) {
25054
25155
  const normalized = command.trim().toLowerCase();
25055
25156
  const extension = normalized === "zsh" ? "zsh" : "sh";
25056
25157
  const filePath = path24.join(
25057
- os5.tmpdir(),
25158
+ os4.tmpdir(),
25058
25159
  `remote-codex-shell-prompt.${extension}`
25059
25160
  );
25060
25161
  await fs21.writeFile(filePath, buildShellPromptInitScriptContents(command), "utf8");
@@ -26214,7 +26315,7 @@ var BackendPluginHost = class {
26214
26315
 
26215
26316
  // src/shell/pty-shell-backend.ts
26216
26317
  import path26 from "path";
26217
- import { spawn as spawn5 } from "@homebridge/node-pty-prebuilt-multiarch";
26318
+ import { spawn as spawn4 } from "@homebridge/node-pty-prebuilt-multiarch";
26218
26319
 
26219
26320
  // src/shell/default-shell.ts
26220
26321
  import fs24 from "fs";
@@ -26283,7 +26384,7 @@ var PtyShellBackend = class {
26283
26384
  if (this.sessions.has(input.sessionId)) {
26284
26385
  return;
26285
26386
  }
26286
- const pty = spawn5(this.shell, shellArgs(this.shell), {
26387
+ const pty = spawn4(this.shell, shellArgs(this.shell), {
26287
26388
  name: "xterm-256color",
26288
26389
  cwd: input.cwd,
26289
26390
  cols: input.cols ?? 120,
@@ -27569,7 +27670,7 @@ function createServiceLifecycle() {
27569
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."
27570
27671
  });
27571
27672
  }
27572
- const child = spawn6(process.execPath, [restartScript, "launch"], {
27673
+ const child = spawn5(process.execPath, [restartScript, "launch"], {
27573
27674
  cwd: repoRoot,
27574
27675
  detached: true,
27575
27676
  env: process.env,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "remote-codex",
3
- "version": "0.11.21",
3
+ "version": "0.11.22",
4
4
  "description": "Local web supervisor for Codex workspaces and threads.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -62,6 +62,8 @@
62
62
  "dependencies": {
63
63
  "@fastify/multipart": "^9.0.3",
64
64
  "@fastify/websocket": "^11.0.2",
65
+ "@fontsource/noto-sans": "^5.2.10",
66
+ "@fontsource/noto-sans-sc": "^5.2.9",
65
67
  "@homebridge/node-pty-prebuilt-multiarch": "^0.13.1",
66
68
  "@modelcontextprotocol/sdk": "^1.29.0",
67
69
  "better-sqlite3": "^12.10.0",
@@ -604,6 +604,9 @@ describe('ClaudeRuntimeAdapter', () => {
604
604
  model: 'fable',
605
605
  displayName: 'Claude Fable',
606
606
  defaultReasoningEffort: 'medium',
607
+ supportedReasoningEfforts: expect.arrayContaining([
608
+ expect.objectContaining({ reasoningEffort: 'max' }),
609
+ ]),
607
610
  }),
608
611
  ]),
609
612
  );
@@ -870,7 +873,7 @@ describe('ClaudeRuntimeAdapter', () => {
870
873
  providerSessionId: 'claude-session-1',
871
874
  prompt: 'Say hello and run pwd',
872
875
  model: 'sonnet',
873
- reasoningEffort: 'xhigh',
876
+ reasoningEffort: 'max',
874
877
  workspacePath: '/tmp/workspace',
875
878
  });
876
879
  expect(started.status).toBe('inProgress');
@@ -494,6 +494,7 @@ const DEFAULT_CLAUDE_MODELS: AgentModel[] = [
494
494
  { reasoningEffort: 'medium', description: 'Medium effort' },
495
495
  { reasoningEffort: 'high', description: 'High effort' },
496
496
  { reasoningEffort: 'xhigh', description: 'Extra high effort' },
497
+ { reasoningEffort: 'max', description: 'Maximum effort' },
497
498
  ],
498
499
  defaultReasoningEffort: 'medium',
499
500
  },
@@ -509,6 +510,7 @@ const DEFAULT_CLAUDE_MODELS: AgentModel[] = [
509
510
  { reasoningEffort: 'medium', description: 'Medium effort' },
510
511
  { reasoningEffort: 'high', description: 'High effort' },
511
512
  { reasoningEffort: 'xhigh', description: 'Extra high effort' },
513
+ { reasoningEffort: 'max', description: 'Maximum effort' },
512
514
  ],
513
515
  defaultReasoningEffort: 'medium',
514
516
  },
@@ -524,6 +526,7 @@ const DEFAULT_CLAUDE_MODELS: AgentModel[] = [
524
526
  { reasoningEffort: 'medium', description: 'Medium effort' },
525
527
  { reasoningEffort: 'high', description: 'High effort' },
526
528
  { reasoningEffort: 'xhigh', description: 'Extra high effort' },
529
+ { reasoningEffort: 'max', description: 'Maximum effort' },
527
530
  ],
528
531
  defaultReasoningEffort: 'medium',
529
532
  },
@@ -539,6 +542,7 @@ const DEFAULT_CLAUDE_MODELS: AgentModel[] = [
539
542
  { reasoningEffort: 'medium', description: 'Medium effort' },
540
543
  { reasoningEffort: 'high', description: 'High effort' },
541
544
  { reasoningEffort: 'xhigh', description: 'Extra high effort' },
545
+ { reasoningEffort: 'max', description: 'Maximum effort' },
542
546
  ],
543
547
  defaultReasoningEffort: 'medium',
544
548
  },
@@ -593,7 +597,7 @@ function mapModelInfo(model: ModelInfo, index: number): AgentModel {
593
597
  isDefault: index === 0,
594
598
  hidden: false,
595
599
  supportedReasoningEfforts: (model.supportedEffortLevels ?? []).map((effort) => ({
596
- reasoningEffort: effort === 'max' ? 'xhigh' : effort,
600
+ reasoningEffort: effort,
597
601
  description: `${effort} effort`,
598
602
  })),
599
603
  defaultReasoningEffort: model.supportsEffort ? 'medium' : null,
@@ -1072,7 +1076,7 @@ function queryOptionsForRuntime(
1072
1076
  options.betas = ['context-1m-2025-08-07'];
1073
1077
  }
1074
1078
  if (input.reasoningEffort) {
1075
- const effort = input.reasoningEffort === 'xhigh' ? 'max' : input.reasoningEffort;
1079
+ const effort = input.reasoningEffort;
1076
1080
  if (['low', 'medium', 'high', 'xhigh', 'max'].includes(effort)) {
1077
1081
  options.effort = effort as NonNullable<ClaudeQueryOptions['effort']>;
1078
1082
  }
@@ -518,7 +518,14 @@ export type ThreadWorkspaceUploadResultDto =
518
518
  };
519
519
 
520
520
  export type ApprovalMode = 'yolo' | 'guarded';
521
- export type ReasoningEffortDto = 'none' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh';
521
+ export type ReasoningEffortDto =
522
+ | 'none'
523
+ | 'minimal'
524
+ | 'low'
525
+ | 'medium'
526
+ | 'high'
527
+ | 'xhigh'
528
+ | 'max';
522
529
  export type CollaborationModeDto = 'default' | 'plan';
523
530
  export type SandboxModeDto = 'read-only' | 'workspace-write' | 'danger-full-access';
524
531