@codexhost/cli-darwin-x64 0.12.2 → 0.13.1

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.
Files changed (54) hide show
  1. package/README.md +1 -1
  2. package/THIRD_PARTY_NOTICES.txt +9 -1
  3. package/app/codexhost-distribution.json +1 -1
  4. package/app/console-server.mjs +319 -64
  5. package/app/console-web.js +5089 -1367
  6. package/app/desktop-controller.mjs +259 -41
  7. package/app/host-runtime.mjs +5735 -1564
  8. package/app/plugins/antigravity/manifest.json +18 -1
  9. package/app/plugins/antigravity/plugin.mjs +282 -41
  10. package/app/plugins/claude-code/manifest.json +28 -0
  11. package/app/plugins/claude-code/plugin.mjs +35300 -23918
  12. package/app/plugins/codebuddy/manifest.json +17 -0
  13. package/app/plugins/codebuddy/plugin.mjs +5971 -5355
  14. package/app/plugins/codex-usage/manifest.json +9 -0
  15. package/app/plugins/codex-usage/plugin.mjs +17324 -0
  16. package/app/plugins/cursor-cli/manifest.json +17 -0
  17. package/app/plugins/cursor-cli/plugin.mjs +357 -76
  18. package/app/plugins/deepseek-harness/assets/icon.svg +1 -0
  19. package/app/plugins/deepseek-harness/manifest.json +35 -2
  20. package/app/plugins/deepseek-harness/plugin.mjs +822 -241
  21. package/app/plugins/enabled.json +2 -1
  22. package/app/plugins/grok/manifest.json +21 -1
  23. package/app/plugins/grok/plugin.mjs +918 -258
  24. package/app/plugins/hermes/assets/icon.png +0 -0
  25. package/app/plugins/hermes/manifest.json +30 -2
  26. package/app/plugins/hermes/plugin.mjs +284 -32
  27. package/app/plugins/kimi-code/assets/icon.svg +5 -0
  28. package/app/plugins/kimi-code/manifest.json +25 -2
  29. package/app/plugins/kimi-code/plugin.mjs +561 -35
  30. package/app/plugins/kiro-cli/assets/kiro-agent.svg +11 -0
  31. package/app/plugins/kiro-cli/manifest.json +18 -1
  32. package/app/plugins/kiro-cli/plugin.mjs +290 -21
  33. package/app/plugins/omp/manifest.json +21 -1
  34. package/app/plugins/omp/plugin.mjs +2765 -1960
  35. package/app/plugins/opencode/manifest.json +18 -1
  36. package/app/plugins/opencode/plugin.mjs +723 -146
  37. package/app/plugins/pi/manifest.json +32 -0
  38. package/app/plugins/pi/plugin.mjs +2908 -2312
  39. package/app/plugins/qoder/manifest.json +18 -1
  40. package/app/plugins/qoder/plugin.mjs +634 -80
  41. package/app/plugins/qoder-cn/manifest.json +18 -1
  42. package/app/plugins/qoder-cn/plugin.mjs +634 -80
  43. package/app/plugins/workbuddy/manifest.json +18 -0
  44. package/app/plugins/workbuddy/plugin.mjs +6361 -5747
  45. package/app/plugins/zcode/manifest.json +19 -1
  46. package/app/plugins/zcode/plugin.mjs +1098 -259
  47. package/app/renderer-extension.js +13080 -8884
  48. package/bin/codexhost +0 -0
  49. package/libexec/codexhost-shim +0 -0
  50. package/libexec/codexhost-updater +0 -0
  51. package/licenses/Cordis-LICENSE.txt +21 -0
  52. package/licenses/Cosmokit-LICENSE.txt +21 -0
  53. package/package.json +1 -1
  54. package/app/plugins/kiro-cli/assets/icon.svg +0 -5
package/README.md CHANGED
@@ -7,7 +7,7 @@ Run Pi and Claude Code as first-class external harnesses inside Codex Desktop.
7
7
  ## Install
8
8
 
9
9
  ```bash
10
- npm install -g @codexhost/cli@0.12.2
10
+ npm install -g @codexhost/cli@0.13.1
11
11
  ```
12
12
 
13
13
  Do not install this package directly. npm selects it through the optional dependencies of `@codexhost/cli`.
@@ -1,10 +1,18 @@
1
1
  codexhost npm package third-party notices
2
2
 
3
+ @deepseek-ai/cordis 4.0.4
4
+ License: MIT
5
+ License text: licenses/Cordis-LICENSE.txt
6
+
7
+ @deepseek-ai/cosmokit 1.8.5
8
+ License: MIT
9
+ License text: licenses/Cosmokit-LICENSE.txt
10
+
3
11
  @agentclientprotocol/sdk 1.3.0
4
12
  License: Apache-2.0
5
13
  License text: licenses/Agent-Client-Protocol-SDK-LICENSE.txt
6
14
 
7
- @anthropic-ai/claude-agent-sdk 0.3.220
15
+ @anthropic-ai/claude-agent-sdk 0.3.273
8
16
  License: SEE LICENSE IN README.md
9
17
  License text: licenses/Claude-Agent-SDK-LICENSE.md
10
18
 
@@ -1 +1 @@
1
- {"schemaVersion":1,"version":"0.12.2","distribution":"npm","target":"macos-x64"}
1
+ {"schemaVersion":1,"version":"0.13.1","distribution":"npm","target":"macos-x64"}
@@ -358,39 +358,25 @@ async function regularFile(filePath) {
358
358
  throw error51;
359
359
  }
360
360
  }
361
- async function isUpdateOperationActive(stateDirectory) {
361
+ async function discoverActiveUpdateStatus(stateDirectory) {
362
362
  if (!path4.isAbsolute(stateDirectory))
363
363
  throw new Error("update state directory must be absolute");
364
- return regularFile(path4.join(stateDirectory, LOCK_FILE));
365
- }
366
- async function discoverLatestUpdateStatus(stateDirectory) {
367
- if (!path4.isAbsolute(stateDirectory))
368
- throw new Error("update state directory must be absolute");
369
- let entries;
364
+ const lockPath = path4.join(stateDirectory, LOCK_FILE);
365
+ if (!await regularFile(lockPath))
366
+ return null;
370
367
  try {
371
- entries = await readdir(stateDirectory, { withFileTypes: true });
372
- } catch (error51) {
373
- if (error51.code === "ENOENT")
368
+ const lock = JSON.parse(await readFile2(lockPath, "utf8"));
369
+ if (typeof lock.statusPath !== "string" || !path4.isAbsolute(lock.statusPath))
374
370
  return null;
375
- throw error51;
376
- }
377
- const candidates = [];
378
- for (const entry of entries) {
379
- if (!entry.isDirectory() || entry.isSymbolicLink() || !entry.name.startsWith("update-"))
380
- continue;
381
- const statusPath = path4.join(stateDirectory, entry.name, STATUS_FILE);
382
- if (!await regularFile(statusPath))
383
- continue;
384
- try {
385
- candidates.push({
386
- statusPath,
387
- status: parseUpdateStatus(JSON.parse(await readFile2(statusPath, "utf8")))
388
- });
389
- } catch {
390
- }
371
+ if (!await regularFile(lock.statusPath))
372
+ return null;
373
+ const status = parseUpdateStatus(JSON.parse(await readFile2(lock.statusPath, "utf8")));
374
+ if (TERMINAL_PHASES.has(status.phase))
375
+ return null;
376
+ return { statusPath: lock.statusPath, status };
377
+ } catch {
378
+ return null;
391
379
  }
392
- candidates.sort((left, right) => right.status.updatedAt - left.status.updatedAt);
393
- return candidates[0] ?? null;
394
380
  }
395
381
  async function cleanupTerminalUpdateState(stateDirectory, options = {}) {
396
382
  const now = Math.floor((options.now ?? Date.now()) / 1e3);
@@ -588,6 +574,48 @@ async function verifyDownloadedArtifact(source, filePath, result) {
588
574
  }
589
575
  }
590
576
 
577
+ // packages/update-manager/dist/updater-ready.js
578
+ import { setTimeout as delay } from "node:timers/promises";
579
+ async function waitForUpdaterReady(child, readStatus, timeoutMs = 1e4) {
580
+ let failure;
581
+ const onError = (error51) => {
582
+ failure = error51;
583
+ };
584
+ const onExit = () => {
585
+ failure = new Error("Background Updater exited before completing handoff");
586
+ };
587
+ child.on("error", onError);
588
+ child.on("exit", onExit);
589
+ const deadline = performance.now() + timeoutMs;
590
+ try {
591
+ while (true) {
592
+ const status = await readStatus();
593
+ if (status?.phase === "failed")
594
+ throw new Error(status.error ?? "Background Updater failed");
595
+ if (failure)
596
+ throw failure;
597
+ if (child.exitCode != null || child.signalCode != null) {
598
+ throw new Error("Background Updater exited before completing handoff");
599
+ }
600
+ if (status?.phase === "waiting-for-exit")
601
+ return;
602
+ if (status && status.phase !== "prepared") {
603
+ throw new Error(`Unexpected Updater phase during handoff: ${status.phase}`);
604
+ }
605
+ if (performance.now() >= deadline) {
606
+ throw new Error("Background Updater did not confirm handoff before the startup timeout");
607
+ }
608
+ await delay(20);
609
+ }
610
+ } catch (error51) {
611
+ child.kill();
612
+ throw error51;
613
+ } finally {
614
+ child.removeListener("error", onError);
615
+ child.removeListener("exit", onExit);
616
+ }
617
+ }
618
+
591
619
  // packages/update-manager/dist/update-manager.js
592
620
  var REQUEST_SCHEMA_VERSION = 1;
593
621
  var WINDOWS_RENAME_RETRY_DELAYS_MS = [10, 30, 70, 150, 300];
@@ -597,7 +625,7 @@ function errorMessage(error51) {
597
625
  function systemErrorCode(error51) {
598
626
  return typeof error51 === "object" && error51 !== null && "code" in error51 ? String(error51.code) : null;
599
627
  }
600
- function delay(milliseconds) {
628
+ function delay2(milliseconds) {
601
629
  return new Promise((resolve) => setTimeout(resolve, milliseconds));
602
630
  }
603
631
  async function replaceStatusFile(temporaryPath, statusPath) {
@@ -613,7 +641,7 @@ async function replaceStatusFile(temporaryPath, statusPath) {
613
641
  throw error51;
614
642
  }
615
643
  retryIndex += 1;
616
- await delay(retryDelay);
644
+ await delay2(retryDelay);
617
645
  }
618
646
  }
619
647
  }
@@ -833,14 +861,20 @@ function createBackgroundUpdateManager(dependencies = {}) {
833
861
  app_path: appPath
834
862
  }, artifact.artifactPath);
835
863
  },
836
- start(prepared) {
864
+ async start(prepared) {
837
865
  if (!preparedRequests.delete(prepared.requestPath)) {
838
866
  throw new Error("background update was not prepared by this manager or was already started");
839
867
  }
840
- const child = spawnUpdater(prepared.helperPath, prepared.requestPath);
841
- if (child.pid === void 0)
842
- throw new Error("background Updater did not report a process ID");
843
- return Object.freeze({ ...prepared, updaterPid: child.pid });
868
+ try {
869
+ const child = spawnUpdater(prepared.helperPath, prepared.requestPath);
870
+ await waitForUpdaterReady(child, () => this.readStatus(prepared.statusPath));
871
+ if (child.pid === void 0)
872
+ throw new Error("background Updater did not report a process ID");
873
+ return Object.freeze({ ...prepared, updaterPid: child.pid });
874
+ } catch (error51) {
875
+ await writeFailedStatus(prepared.statusPath, prepared.version, prepared.installation, error51);
876
+ throw error51;
877
+ }
844
878
  },
845
879
  async readStatus(statusPathValue) {
846
880
  const statusPath = requireAbsolutePath(statusPathValue, "update status path");
@@ -5498,8 +5532,8 @@ function az_default() {
5498
5532
  }
5499
5533
 
5500
5534
  // node_modules/zod/v4/locales/be.js
5501
- function getBelarusianPlural(count, one, few, many) {
5502
- const absCount = Math.abs(count);
5535
+ function getBelarusianPlural(count2, one, few, many) {
5536
+ const absCount = Math.abs(count2);
5503
5537
  const lastDigit = absCount % 10;
5504
5538
  const lastTwoDigits = absCount % 100;
5505
5539
  if (lastTwoDigits >= 11 && lastTwoDigits <= 19) {
@@ -7577,8 +7611,8 @@ function hu_default() {
7577
7611
  }
7578
7612
 
7579
7613
  // node_modules/zod/v4/locales/hy.js
7580
- function getArmenianPlural(count, one, many) {
7581
- return Math.abs(count) === 1 ? one : many;
7614
+ function getArmenianPlural(count2, one, many) {
7615
+ return Math.abs(count2) === 1 ? one : many;
7582
7616
  }
7583
7617
  function withDefiniteArticle(word) {
7584
7618
  if (!word)
@@ -9706,8 +9740,8 @@ function ro_default() {
9706
9740
  }
9707
9741
 
9708
9742
  // node_modules/zod/v4/locales/ru.js
9709
- function getRussianPlural(count, one, few, many) {
9710
- const absCount = Math.abs(count);
9743
+ function getRussianPlural(count2, one, few, many) {
9744
+ const absCount = Math.abs(count2);
9711
9745
  const lastDigit = absCount % 10;
9712
9746
  const lastTwoDigits = absCount % 100;
9713
9747
  if (lastTwoDigits >= 11 && lastTwoDigits <= 19) {
@@ -15376,6 +15410,15 @@ function date4(params) {
15376
15410
  // node_modules/zod/v4/classic/external.js
15377
15411
  config(en_default());
15378
15412
 
15413
+ // packages/shared-contracts/dist/delegation-read.js
15414
+ var REMOTE_THREAD_REPLY_MAX_BYTES = 16 * 1024 * 1024;
15415
+ var delegationReadParamsSchema = external_exports.strictObject({
15416
+ threadId: external_exports.string().min(1).max(1024),
15417
+ view: external_exports.enum(["result", "messages"]),
15418
+ cursor: external_exports.string().min(1).max(8192).optional(),
15419
+ limit: external_exports.number().int().min(1).max(100).optional()
15420
+ });
15421
+
15379
15422
  // packages/shared-contracts/dist/credential-imports.js
15380
15423
  var credentialSourceSchema = external_exports.object({
15381
15424
  id: external_exports.string().min(1).max(256),
@@ -15446,6 +15489,160 @@ var harnessDisplaySetSchema = external_exports.strictObject({
15446
15489
  initializeOnly: external_exports.boolean().optional()
15447
15490
  });
15448
15491
 
15492
+ // packages/shared-contracts/dist/model-price-overrides.js
15493
+ var usdPerMillionTokens = external_exports.number().finite().nonnegative();
15494
+ var modelPriceOverrideSchema = external_exports.strictObject({
15495
+ input: usdPerMillionTokens,
15496
+ output: usdPerMillionTokens,
15497
+ cacheRead: usdPerMillionTokens.optional(),
15498
+ cacheWrite: usdPerMillionTokens.optional(),
15499
+ cacheWrite1h: usdPerMillionTokens.optional()
15500
+ });
15501
+ var modelPriceKeySchema = external_exports.string().max(512).refine((key) => key.trim() === key && key.length > 0, "Model ID is required");
15502
+ var modelPriceOverridesSchema = external_exports.strictObject({
15503
+ /** Absolute path of the Host's `pricing.json`. */
15504
+ path: external_exports.string(),
15505
+ entries: external_exports.array(external_exports.strictObject({ key: external_exports.string(), price: modelPriceOverrideSchema })),
15506
+ /** Why the existing file is unusable; the Host then ignores it and refuses to overwrite it. */
15507
+ error: external_exports.string().nullable()
15508
+ });
15509
+ var modelPriceOverridesGetSchema = external_exports.strictObject({});
15510
+ var modelPriceOverridesSetSchema = external_exports.strictObject({
15511
+ key: modelPriceKeySchema,
15512
+ price: modelPriceOverrideSchema.nullable(),
15513
+ previousKey: modelPriceKeySchema.optional()
15514
+ });
15515
+ var modelPriceDefaultParamsSchema = external_exports.strictObject({
15516
+ model: modelPriceKeySchema
15517
+ });
15518
+ var modelPriceSuggestionSchema = external_exports.strictObject({
15519
+ /** Verified from catalog canonical links, not inferred from names or equal prices. */
15520
+ official: external_exports.boolean().optional(),
15521
+ canonicalModelId: external_exports.string().min(1).optional(),
15522
+ provider: external_exports.string().min(1).max(512),
15523
+ model: external_exports.string().min(1).max(512),
15524
+ price: modelPriceOverrideSchema
15525
+ });
15526
+ var modelPriceDefaultResultSchema = external_exports.strictObject({
15527
+ price: modelPriceOverrideSchema.nullable(),
15528
+ /** Local catalog suggestions only; never used automatically for pricing. */
15529
+ suggestions: external_exports.array(modelPriceSuggestionSchema).max(6).optional()
15530
+ });
15531
+
15532
+ // packages/shared-contracts/dist/usage-statistics.js
15533
+ var USAGE_STATISTICS_RANGES = ["today", "7d", "30d", "90d", "all"];
15534
+ var count = external_exports.number().int().min(0).max(Number.MAX_SAFE_INTEGER);
15535
+ var localDate = external_exports.string().regex(/^\d{4}-\d{2}-\d{2}$/u);
15536
+ var harness = external_exports.string().min(1).max(128);
15537
+ var model = external_exports.string().min(1).max(512);
15538
+ var project = external_exports.string().min(1).max(4096);
15539
+ var usageStatisticsParamsSchema = external_exports.strictObject({
15540
+ range: external_exports.enum(USAGE_STATISTICS_RANGES),
15541
+ harness: harness.optional(),
15542
+ model: model.nullable().optional(),
15543
+ project: project.nullable().optional(),
15544
+ /** One local date inside the range. */
15545
+ date: localDate.optional()
15546
+ });
15547
+ var usageStatisticsTotalsSchema = external_exports.strictObject({
15548
+ requests: count,
15549
+ inputTokens: count,
15550
+ cachedInputTokens: count,
15551
+ cacheWriteInputTokens: count,
15552
+ cacheKnownInputTokens: count,
15553
+ outputTokens: count,
15554
+ reasoningOutputTokens: count,
15555
+ /** USD at the public price, summed over the priced requests only. */
15556
+ costUsd: external_exports.number().finite().min(0),
15557
+ /** Requests without a price (or the cache data the price needs); not in `costUsd`. */
15558
+ unpricedRequests: count,
15559
+ /**
15560
+ * Requests whose storage gave no token counts: counted as requests, with neither tokens nor
15561
+ * cost, and not as unpriced.
15562
+ */
15563
+ unmeteredRequests: count
15564
+ });
15565
+ var totals = usageStatisticsTotalsSchema.shape;
15566
+ var usageStatisticsSessionSchema = external_exports.strictObject({
15567
+ harness,
15568
+ sessionId: external_exports.string().min(1).max(512),
15569
+ project: project.nullable(),
15570
+ /** Persisted Desktop title for a mapped Session, otherwise its native name; may be absent. */
15571
+ title: external_exports.string().min(1).max(200).optional(),
15572
+ /** Models the session used, most requests first. */
15573
+ models: external_exports.array(model).max(16),
15574
+ firstAtMs: count,
15575
+ lastAtMs: count,
15576
+ ...totals
15577
+ });
15578
+ var usageStatisticsResultSchema = external_exports.strictObject({
15579
+ range: external_exports.enum(USAGE_STATISTICS_RANGES),
15580
+ /** First local date of the range; null for all time. */
15581
+ from: localDate.nullable(),
15582
+ to: localDate,
15583
+ /** The filters these figures apply, echoed so a late answer to older filters is recognised. */
15584
+ filters: external_exports.strictObject({
15585
+ harness: harness.nullable(),
15586
+ model: model.nullable(),
15587
+ unknownModel: external_exports.boolean(),
15588
+ project: project.nullable(),
15589
+ unknownProject: external_exports.boolean(),
15590
+ date: localDate.nullable()
15591
+ }),
15592
+ /** Reading of native storage: incomplete while the first read, or a refresh, is under way. */
15593
+ reading: external_exports.strictObject({ complete: external_exports.boolean(), sources: count, read: count }),
15594
+ /** Harnesses whose storage could not be read; their part is missing, the rest is complete. */
15595
+ failures: external_exports.array(external_exports.strictObject({ harness, message: external_exports.string().max(2048) })).max(256),
15596
+ /** Every value seen in the range, ignoring the filters, to choose filters from. */
15597
+ options: external_exports.strictObject({
15598
+ harnesses: external_exports.array(harness).max(256),
15599
+ models: external_exports.array(model.nullable()).max(4096),
15600
+ projects: external_exports.array(project.nullable()).max(4096)
15601
+ }),
15602
+ /** Current display metadata, scoped by Harness. Never a pricing or filter identity.
15603
+ * Optional for older Hosts; missing/conflicting labels fall back to the native model ID.
15604
+ */
15605
+ modelLabels: external_exports.array(external_exports.strictObject({ harness, model, label: model })).max(4096).optional(),
15606
+ /** Native credits, all filters applied, never combined across Harnesses or treated as USD.
15607
+ * Only groups with at least one reported value appear; reportedRequests exposes partial data.
15608
+ */
15609
+ credits: external_exports.array(external_exports.strictObject({
15610
+ harness,
15611
+ model: model.nullable(),
15612
+ credits: external_exports.number().finite().min(0),
15613
+ reportedRequests: count,
15614
+ requests: count
15615
+ })).max(4096).optional(),
15616
+ /** All filters applied. */
15617
+ totals: usageStatisticsTotalsSchema,
15618
+ /** Each date and Harness of the range: every filter except the day. */
15619
+ daily: external_exports.array(external_exports.strictObject({ date: localDate, harness, ...totals })).max(2e5),
15620
+ /** Local weekday (0 = Sunday) and hour, all filters applied. */
15621
+ hourly: external_exports.array(external_exports.strictObject({
15622
+ weekday: external_exports.number().int().min(0).max(6),
15623
+ hour: external_exports.number().int().min(0).max(23),
15624
+ ...totals
15625
+ })).max(168),
15626
+ byHarness: external_exports.array(external_exports.strictObject({ harness, ...totals })).max(256),
15627
+ byModel: external_exports.array(external_exports.strictObject({
15628
+ model: model.nullable(),
15629
+ ...totals,
15630
+ /** Requests priced by what the Harness recorded they cost, not by a price list. */
15631
+ harnessPricedRequests: count
15632
+ })).max(4096),
15633
+ byProject: external_exports.array(external_exports.strictObject({ project: project.nullable(), ...totals })).max(4096),
15634
+ /**
15635
+ * The sessions that spent the most, by cost and by tokens without cache, all filters applied;
15636
+ * requests without a native session are not listed here but count everywhere else.
15637
+ */
15638
+ sessions: external_exports.array(usageStatisticsSessionSchema).max(100),
15639
+ /**
15640
+ * The sessions last active in the range, newest first, all filters applied. Optional for older
15641
+ * Hosts; missing is treated as no list rather than no sessions.
15642
+ */
15643
+ recentSessions: external_exports.array(usageStatisticsSessionSchema).max(100).optional()
15644
+ });
15645
+
15449
15646
  // packages/shared-contracts/dist/ids.js
15450
15647
  var opaqueIdSchema = external_exports.string().refine((value) => value.trim().length > 0, {
15451
15648
  message: "Identifier must not be empty or whitespace"
@@ -15493,6 +15690,10 @@ var CONSOLE_HOST_METHODS = Object.freeze([
15493
15690
  "codexhost/account/list",
15494
15691
  "codexhost/account/refresh",
15495
15692
  "codexhost/account/usage/inspect",
15693
+ "codexhost/usage/model-prices/get",
15694
+ "codexhost/usage/model-prices/set",
15695
+ "codexhost/usage/model-prices/default",
15696
+ "codexhost/usage/statistics/get",
15496
15697
  "codexhost/update/check",
15497
15698
  "codexhost/update/start",
15498
15699
  "codexhost/update/status"
@@ -15560,6 +15761,7 @@ var threadUsageSnapshotSchema = external_exports.object({
15560
15761
  cacheWriteInputTokens: nonNegativeSafeIntegerSchema.optional(),
15561
15762
  outputTokens: nonNegativeSafeIntegerSchema.optional(),
15562
15763
  outputTokensPerSecond: finiteNonNegativeNumberSchema.optional(),
15764
+ apiOutputTokensPerSecond: finiteNonNegativeNumberSchema.optional(),
15563
15765
  reasoningOutputTokens: nonNegativeSafeIntegerSchema.optional(),
15564
15766
  totalTokens: nonNegativeSafeIntegerSchema.optional(),
15565
15767
  totalCostUsd: finiteNonNegativeNumberSchema.optional(),
@@ -15571,7 +15773,11 @@ var threadUsageSnapshotSchema = external_exports.object({
15571
15773
  planFiveHourUsedPercent: cacheHitRatePercentSchema.optional(),
15572
15774
  planFiveHourResetsAtUnix: nonNegativeSafeIntegerSchema.optional(),
15573
15775
  planSevenDayUsedPercent: cacheHitRatePercentSchema.optional(),
15574
- planSevenDayResetsAtUnix: nonNegativeSafeIntegerSchema.optional()
15776
+ planSevenDayResetsAtUnix: nonNegativeSafeIntegerSchema.optional(),
15777
+ sessionCacheHitRatePercent: cacheHitRatePercentSchema.optional(),
15778
+ timeToFirstOutputMs: nonNegativeSafeIntegerSchema.optional(),
15779
+ costSource: external_exports.enum(["publicPrice", "native"]).optional(),
15780
+ unpricedModels: external_exports.array(external_exports.string().min(1)).min(1).optional()
15575
15781
  }).strict().superRefine((usage2, context) => {
15576
15782
  if (Object.keys(usage2).length === 0) {
15577
15783
  context.addIssue({ code: "custom", message: "Thread Usage must contain a reliable field" });
@@ -15592,6 +15798,20 @@ var threadUsageSnapshotSchema = external_exports.object({
15592
15798
  path: ["contextWindowTokens"]
15593
15799
  });
15594
15800
  }
15801
+ if (usage2.costSource !== void 0 && usage2.totalCostUsd === void 0) {
15802
+ context.addIssue({
15803
+ code: "custom",
15804
+ message: "Thread Usage costSource must be provided with totalCostUsd",
15805
+ path: ["costSource"]
15806
+ });
15807
+ }
15808
+ if (usage2.unpricedModels !== void 0 && usage2.costSource !== "publicPrice") {
15809
+ context.addIssue({
15810
+ code: "custom",
15811
+ message: "Thread Usage unpricedModels must be provided with a publicPrice cost",
15812
+ path: ["unpricedModels"]
15813
+ });
15814
+ }
15595
15815
  if (usage2.planFiveHourResetsAtUnix !== void 0 && usage2.planFiveHourUsedPercent === void 0) {
15596
15816
  context.addIssue({
15597
15817
  code: "custom",
@@ -15846,27 +16066,27 @@ var harnessModelCatalogSchema = external_exports.object({
15846
16066
  }).strict().superRefine((catalog, context) => {
15847
16067
  const refs = /* @__PURE__ */ new Set();
15848
16068
  const thinkingIds = new Set(catalog.thinkingOptions.map(({ id }) => id));
15849
- for (const [index, model] of catalog.models.entries()) {
15850
- if (refs.has(model.ref.id)) {
16069
+ for (const [index, model2] of catalog.models.entries()) {
16070
+ if (refs.has(model2.ref.id)) {
15851
16071
  context.addIssue({
15852
16072
  code: "custom",
15853
16073
  message: "Model Catalog refs must be unique",
15854
16074
  path: ["models", index, "ref", "id"]
15855
16075
  });
15856
16076
  }
15857
- refs.add(model.ref.id);
15858
- if (model.fastModel) {
15859
- if (refs.has(model.fastModel.id) || catalog.models.some((entry) => entry.ref.id === model.fastModel?.id)) {
16077
+ refs.add(model2.ref.id);
16078
+ if (model2.fastModel) {
16079
+ if (refs.has(model2.fastModel.id) || catalog.models.some((entry) => entry.ref.id === model2.fastModel?.id)) {
15860
16080
  context.addIssue({
15861
16081
  code: "custom",
15862
16082
  message: "Fast Model refs must be distinct and unique",
15863
16083
  path: ["models", index, "fastModel"]
15864
16084
  });
15865
16085
  }
15866
- refs.add(model.fastModel.id);
16086
+ refs.add(model2.fastModel.id);
15867
16087
  }
15868
16088
  const supportedThinkingIds = /* @__PURE__ */ new Set();
15869
- for (const [optionIndex, optionId] of (model.supportedThinkingOptionIds ?? []).entries()) {
16089
+ for (const [optionIndex, optionId] of (model2.supportedThinkingOptionIds ?? []).entries()) {
15870
16090
  if (supportedThinkingIds.has(optionId)) {
15871
16091
  context.addIssue({
15872
16092
  code: "custom",
@@ -16060,13 +16280,40 @@ var HARNESS_PLUGIN_LIMIT = 128;
16060
16280
  var harnessPluginIdSchema = external_exports.string().min(1).max(128).regex(/^[a-z][a-z0-9]*(?:[._-][a-z0-9]+)*$/u).refine((id) => id !== "codex", "The official Codex identity is reserved").pipe(harnessIdSchema);
16061
16281
  var relativeResourceSchema = external_exports.string().min(1).max(512).refine((value) => !/[\\\u0000:#?]/u.test(value) && !value.startsWith("/") && !value.split("/").some((part) => part === ".." || part === ""), "Plugin resources must be relative paths inside the plugin");
16062
16282
  var documentationUrlSchema = external_exports.url().refine((value) => /^https:\/\/[^/?#@]+(?:[/?#]|$)/iu.test(value), "Plugin documentation links must be credential-free HTTPS URLs");
16283
+ var localizedTextSchema = external_exports.object({ en: external_exports.string().max(4096), "zh-CN": external_exports.string().max(4096).optional() }).strict();
16284
+ var harnessInstallationGuideSchema = external_exports.object({
16285
+ commands: external_exports.array(external_exports.object({ terminal: external_exports.string().max(256), command: external_exports.string().max(4096) }).strict()).max(8),
16286
+ before: localizedTextSchema.optional(),
16287
+ after: localizedTextSchema.optional(),
16288
+ downloads: external_exports.array(external_exports.object({ label: external_exports.string().max(128), url: documentationUrlSchema }).strict()).max(8).optional()
16289
+ }).strict();
16063
16290
  var pluginPresentationShape = {
16064
16291
  id: harnessPluginIdSchema,
16065
16292
  name: external_exports.string().trim().min(1).max(128),
16066
16293
  version: external_exports.string().min(1).max(128),
16294
+ /** Capability-only plugin; omitted for existing session-capable plugins. */
16295
+ kind: external_exports.literal("usage").optional(),
16296
+ /** Original image styling or bounded vector paths; never arbitrary CSS/SVG markup. */
16297
+ iconStyle: external_exports.object({
16298
+ vector: external_exports.object({
16299
+ viewBox: external_exports.string().max(128).regex(/^-?\d+(?:\.\d+)?(?: +-?\d+(?:\.\d+)?){3}$/u),
16300
+ color: external_exports.string().regex(/^(?:currentColor|#[a-f0-9]{6})$/iu),
16301
+ paths: external_exports.array(external_exports.object({
16302
+ d: external_exports.string().min(1).max(32768).regex(/^[MmLlHhVvCcSsQqTtAaZz\d.,+eE\s-]+$/u),
16303
+ fillRule: external_exports.enum(["evenodd", "nonzero"]).optional(),
16304
+ fill: external_exports.string().regex(/^(?:currentColor|#[a-f0-9]{6})$/iu).optional()
16305
+ }).strict()).min(1).max(32)
16306
+ }).strict().optional(),
16307
+ background: external_exports.string().regex(/^#[a-f0-9]{6}$/iu).optional(),
16308
+ borderRadius: external_exports.number().min(0).max(50).optional(),
16309
+ paddingRatio: external_exports.number().min(0).max(0.25).optional()
16310
+ }).strict().optional(),
16311
+ installation: harnessInstallationGuideSchema.optional(),
16312
+ notice: localizedTextSchema.optional(),
16067
16313
  /** The factory accepts a persisted local entrypoint through its construction context. */
16068
16314
  launchCommand: external_exports.literal(true).optional(),
16069
16315
  links: external_exports.object({
16316
+ website: documentationUrlSchema.optional(),
16070
16317
  documentation: documentationUrlSchema.optional(),
16071
16318
  installation: documentationUrlSchema.optional()
16072
16319
  }).strict().optional()
@@ -16480,6 +16727,11 @@ var codexSshDraftSchema = external_exports.strictObject({
16480
16727
  });
16481
16728
  var remoteConnectionsRequestSchema = external_exports.discriminatedUnion("action", [
16482
16729
  external_exports.strictObject({ action: external_exports.literal("list") }),
16730
+ external_exports.strictObject({
16731
+ action: external_exports.literal("read-thread"),
16732
+ hostId: hostId.refine((id) => id !== "local"),
16733
+ input: delegationReadParamsSchema
16734
+ }),
16483
16735
  external_exports.strictObject({
16484
16736
  action: external_exports.literal("save"),
16485
16737
  draft: codexSshDraftSchema,
@@ -16772,14 +17024,14 @@ function createConsoleHarnesses(roots, environment = process.env) {
16772
17024
  return {
16773
17025
  list,
16774
17026
  async setLaunchPath(id, value) {
16775
- const harness = (await list()).find((candidate) => candidate.id === id);
16776
- if (!harness) throw new ConsoleHarnessError(404, `Harness '${id}' is not installed`);
16777
- if (!harness.launchCommand) {
17027
+ const harness2 = (await list()).find((candidate) => candidate.id === id);
17028
+ if (!harness2) throw new ConsoleHarnessError(404, `Harness '${id}' is not installed`);
17029
+ if (!harness2.launchCommand) {
16778
17030
  throw new ConsoleHarnessError(400, `Harness '${id}' does not accept an installation path`);
16779
17031
  }
16780
17032
  try {
16781
17033
  const settings = await store.set(id, value);
16782
- return { ...harness, launchPath: settings.path };
17034
+ return { ...harness2, launchPath: settings.path };
16783
17035
  } catch (error51) {
16784
17036
  throw new ConsoleHarnessError(400, error51 instanceof Error ? error51.message : String(error51));
16785
17037
  }
@@ -17358,14 +17610,15 @@ function createConsoleUpdates(options) {
17358
17610
  });
17359
17611
  let candidate = null;
17360
17612
  let handedOff = false;
17613
+ let currentStatusPath = null;
17361
17614
  async function latestStatus() {
17362
- const directory = stateDirectory();
17363
- const discovered = await discoverLatestUpdateStatus(directory);
17364
- if (!discovered) return null;
17365
- if (discovered.status.phase !== "succeeded" && discovered.status.phase !== "failed" && !await isUpdateOperationActive(directory)) {
17366
- return null;
17615
+ if (!currentStatusPath) {
17616
+ const active = await discoverActiveUpdateStatus(stateDirectory());
17617
+ if (!active) return null;
17618
+ currentStatusPath = active.statusPath;
17367
17619
  }
17368
- return publicStatus(discovered.status);
17620
+ const status = await manager.readStatus(currentStatusPath);
17621
+ return status ? publicStatus(status) : null;
17369
17622
  }
17370
17623
  function installable(metadata, release) {
17371
17624
  const kind = installationKind(metadata);
@@ -17492,6 +17745,7 @@ function createConsoleUpdates(options) {
17492
17745
  if (existing) return { status: existing };
17493
17746
  throw new ConsoleUpdateError("busy", "Another update operation is already active");
17494
17747
  }
17748
+ currentStatusPath = null;
17495
17749
  try {
17496
17750
  const release = await fetchLatest();
17497
17751
  if (compareSemanticVersions(metadata.version, release.version) >= 0 || candidate && candidate.version !== release.version) {
@@ -17510,9 +17764,10 @@ function createConsoleUpdates(options) {
17510
17764
  try {
17511
17765
  const prepared = await prepare(metadata, target, release, async (info) => {
17512
17766
  await lock.setStatusPath(info.statusPath);
17767
+ currentStatusPath = info.statusPath;
17513
17768
  resolvePrepared(info.statusPath);
17514
17769
  });
17515
- manager.start(prepared);
17770
+ await manager.start(prepared);
17516
17771
  handedOff = true;
17517
17772
  options.onHandedOff();
17518
17773
  } catch (error51) {
@@ -17840,8 +18095,8 @@ function startConsoleServer(options) {
17840
18095
  return;
17841
18096
  }
17842
18097
  try {
17843
- const harness = await options.harnesses.setLaunchPath(id, value === "" ? null : value);
17844
- sendJson(response, 200, { harness });
18098
+ const harness2 = await options.harnesses.setLaunchPath(id, value === "" ? null : value);
18099
+ sendJson(response, 200, { harness: harness2 });
17845
18100
  } catch (error51) {
17846
18101
  sendJson(response, error51 instanceof ConsoleHarnessError ? error51.status : 500, {
17847
18102
  error: error51 instanceof Error ? error51.message : String(error51)
@@ -17948,7 +18203,7 @@ async function consoleBuildId(entryPath2, environment = process.env) {
17948
18203
  }
17949
18204
  const runtime = await readRuntimeMetadata(entryPath2, environment).catch(() => null);
17950
18205
  if (runtime?.distribution === "development") parts.push(runtime.version);
17951
- return parts.join(".");
18206
+ return JSON.stringify([parts.join("."), dataDirectory(environment)]);
17952
18207
  }
17953
18208
  function startDetachedServer(entryPath2, environment) {
17954
18209
  const child = spawn3(process.execPath, [entryPath2, "serve"], {
@@ -18019,7 +18274,7 @@ async function runUpdateCommand(target, updates, write = console.log) {
18019
18274
  const { status } = await updates.start(target);
18020
18275
  if (status.phase === "failed") throw new Error(status.error ?? "Update failed");
18021
18276
  write(
18022
- `Update ${status.version}: ${status.phase}. The background updater will finish installation and restart codexhost.`
18277
+ `Update ${status.version}: ${status.phase}. The background updater has accepted the handoff; installation is not complete yet. It will install the update and restart codexhost.`
18023
18278
  );
18024
18279
  }
18025
18280
  async function updateFromCommand(appDirectory2, arguments_) {