@codexhost/cli-darwin-x64 0.13.0 → 0.13.2

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.
@@ -4295,9 +4295,11 @@ async function runDelegationCli(input) {
4295
4295
  "INVALID_ARGUMENT",
4296
4296
  "--host conflicts with the Thread reference"
4297
4297
  );
4298
+ const caller = command === "read" ? callerThreadId(environment) : void 0;
4298
4299
  const body = {
4299
4300
  ...reference,
4300
4301
  ...hostId2 ? { hostId: hostId2 } : {},
4302
+ ...caller ? { callerThreadId: normalizeThreadId(caller) } : {},
4301
4303
  view,
4302
4304
  ...value(parsed2, "--cursor") ? { cursor: value(parsed2, "--cursor") } : {},
4303
4305
  ...value(parsed2, "--limit") ? { limit: positiveInteger(value(parsed2, "--limit"), "--limit", MAX_LIMIT) } : {},
@@ -4434,6 +4436,7 @@ import {
4434
4436
  realpath,
4435
4437
  stat,
4436
4438
  readdir as readdir2,
4439
+ unlink,
4437
4440
  writeFile as writeFile3
4438
4441
  } from "node:fs/promises";
4439
4442
  import { homedir } from "node:os";
@@ -4894,39 +4897,25 @@ async function regularFile(filePath) {
4894
4897
  throw error51;
4895
4898
  }
4896
4899
  }
4897
- async function isUpdateOperationActive(stateDirectory) {
4900
+ async function discoverActiveUpdateStatus(stateDirectory) {
4898
4901
  if (!path4.isAbsolute(stateDirectory))
4899
4902
  throw new Error("update state directory must be absolute");
4900
- return regularFile(path4.join(stateDirectory, LOCK_FILE));
4901
- }
4902
- async function discoverLatestUpdateStatus(stateDirectory) {
4903
- if (!path4.isAbsolute(stateDirectory))
4904
- throw new Error("update state directory must be absolute");
4905
- let entries;
4903
+ const lockPath = path4.join(stateDirectory, LOCK_FILE);
4904
+ if (!await regularFile(lockPath))
4905
+ return null;
4906
4906
  try {
4907
- entries = await readdir(stateDirectory, { withFileTypes: true });
4908
- } catch (error51) {
4909
- if (error51.code === "ENOENT")
4907
+ const lock = JSON.parse(await readFile3(lockPath, "utf8"));
4908
+ if (typeof lock.statusPath !== "string" || !path4.isAbsolute(lock.statusPath))
4910
4909
  return null;
4911
- throw error51;
4912
- }
4913
- const candidates = [];
4914
- for (const entry of entries) {
4915
- if (!entry.isDirectory() || entry.isSymbolicLink() || !entry.name.startsWith("update-"))
4916
- continue;
4917
- const statusPath = path4.join(stateDirectory, entry.name, STATUS_FILE);
4918
- if (!await regularFile(statusPath))
4919
- continue;
4920
- try {
4921
- candidates.push({
4922
- statusPath,
4923
- status: parseUpdateStatus(JSON.parse(await readFile3(statusPath, "utf8")))
4924
- });
4925
- } catch {
4926
- }
4910
+ if (!await regularFile(lock.statusPath))
4911
+ return null;
4912
+ const status2 = parseUpdateStatus(JSON.parse(await readFile3(lock.statusPath, "utf8")));
4913
+ if (TERMINAL_PHASES.has(status2.phase))
4914
+ return null;
4915
+ return { statusPath: lock.statusPath, status: status2 };
4916
+ } catch {
4917
+ return null;
4927
4918
  }
4928
- candidates.sort((left, right) => right.status.updatedAt - left.status.updatedAt);
4929
- return candidates[0] ?? null;
4930
4919
  }
4931
4920
  async function cleanupTerminalUpdateState(stateDirectory, options2 = {}) {
4932
4921
  const now = Math.floor((options2.now ?? Date.now()) / 1e3);
@@ -5124,6 +5113,48 @@ async function verifyDownloadedArtifact(source, filePath, result) {
5124
5113
  }
5125
5114
  }
5126
5115
 
5116
+ // packages/update-manager/dist/updater-ready.js
5117
+ import { setTimeout as delay } from "node:timers/promises";
5118
+ async function waitForUpdaterReady(child, readStatus, timeoutMs = 1e4) {
5119
+ let failure;
5120
+ const onError = (error51) => {
5121
+ failure = error51;
5122
+ };
5123
+ const onExit = () => {
5124
+ failure = new Error("Background Updater exited before completing handoff");
5125
+ };
5126
+ child.on("error", onError);
5127
+ child.on("exit", onExit);
5128
+ const deadline = performance.now() + timeoutMs;
5129
+ try {
5130
+ while (true) {
5131
+ const status2 = await readStatus();
5132
+ if (status2?.phase === "failed")
5133
+ throw new Error(status2.error ?? "Background Updater failed");
5134
+ if (failure)
5135
+ throw failure;
5136
+ if (child.exitCode != null || child.signalCode != null) {
5137
+ throw new Error("Background Updater exited before completing handoff");
5138
+ }
5139
+ if (status2?.phase === "waiting-for-exit")
5140
+ return;
5141
+ if (status2 && status2.phase !== "prepared") {
5142
+ throw new Error(`Unexpected Updater phase during handoff: ${status2.phase}`);
5143
+ }
5144
+ if (performance.now() >= deadline) {
5145
+ throw new Error("Background Updater did not confirm handoff before the startup timeout");
5146
+ }
5147
+ await delay(20);
5148
+ }
5149
+ } catch (error51) {
5150
+ child.kill();
5151
+ throw error51;
5152
+ } finally {
5153
+ child.removeListener("error", onError);
5154
+ child.removeListener("exit", onExit);
5155
+ }
5156
+ }
5157
+
5127
5158
  // packages/update-manager/dist/update-manager.js
5128
5159
  var REQUEST_SCHEMA_VERSION = 1;
5129
5160
  var WINDOWS_RENAME_RETRY_DELAYS_MS = [10, 30, 70, 150, 300];
@@ -5133,7 +5164,7 @@ function errorMessage(error51) {
5133
5164
  function systemErrorCode(error51) {
5134
5165
  return typeof error51 === "object" && error51 !== null && "code" in error51 ? String(error51.code) : null;
5135
5166
  }
5136
- function delay(milliseconds) {
5167
+ function delay2(milliseconds) {
5137
5168
  return new Promise((resolve) => setTimeout(resolve, milliseconds));
5138
5169
  }
5139
5170
  async function replaceStatusFile(temporaryPath, statusPath) {
@@ -5149,7 +5180,7 @@ async function replaceStatusFile(temporaryPath, statusPath) {
5149
5180
  throw error51;
5150
5181
  }
5151
5182
  retryIndex += 1;
5152
- await delay(retryDelay);
5183
+ await delay2(retryDelay);
5153
5184
  }
5154
5185
  }
5155
5186
  }
@@ -5369,14 +5400,20 @@ function createBackgroundUpdateManager(dependencies2 = {}) {
5369
5400
  app_path: appPath
5370
5401
  }, artifact.artifactPath);
5371
5402
  },
5372
- start(prepared) {
5403
+ async start(prepared) {
5373
5404
  if (!preparedRequests.delete(prepared.requestPath)) {
5374
5405
  throw new Error("background update was not prepared by this manager or was already started");
5375
5406
  }
5376
- const child = spawnUpdater(prepared.helperPath, prepared.requestPath);
5377
- if (child.pid === void 0)
5378
- throw new Error("background Updater did not report a process ID");
5379
- return Object.freeze({ ...prepared, updaterPid: child.pid });
5407
+ try {
5408
+ const child = spawnUpdater(prepared.helperPath, prepared.requestPath);
5409
+ await waitForUpdaterReady(child, () => this.readStatus(prepared.statusPath));
5410
+ if (child.pid === void 0)
5411
+ throw new Error("background Updater did not report a process ID");
5412
+ return Object.freeze({ ...prepared, updaterPid: child.pid });
5413
+ } catch (error51) {
5414
+ await writeFailedStatus(prepared.statusPath, prepared.version, prepared.installation, error51);
5415
+ throw error51;
5416
+ }
5380
5417
  },
5381
5418
  async readStatus(statusPathValue) {
5382
5419
  const statusPath = requireAbsolutePath(statusPathValue, "update status path");
@@ -19909,6 +19946,7 @@ config(en_default());
19909
19946
  var REMOTE_THREAD_REPLY_MAX_BYTES = 16 * 1024 * 1024;
19910
19947
  var REMOTE_THREAD_CONTROL_TIMEOUT_MS = 4e4;
19911
19948
  var DELEGATION_READ_METHOD = "codexhost/thread/delegation-read";
19949
+ var THREAD_REFERENCE_SOURCE_HOST_PARAM = "codexhostSourceHostId";
19912
19950
  var delegationReadParamsSchema = external_exports.strictObject({
19913
19951
  threadId: external_exports.string().min(1).max(1024),
19914
19952
  view: external_exports.enum(["result", "messages"]),
@@ -20071,6 +20109,8 @@ var usageStatisticsSessionSchema = external_exports.strictObject({
20071
20109
  harness,
20072
20110
  sessionId: external_exports.string().min(1).max(512),
20073
20111
  project: project.nullable(),
20112
+ /** Persisted Desktop title for a mapped Session, otherwise its native name; may be absent. */
20113
+ title: external_exports.string().min(1).max(200).optional(),
20074
20114
  /** Models the session used, most requests first. */
20075
20115
  models: external_exports.array(model).max(16),
20076
20116
  firstAtMs: count,
@@ -20137,7 +20177,12 @@ var usageStatisticsResultSchema = external_exports.strictObject({
20137
20177
  * The sessions that spent the most, by cost and by tokens without cache, all filters applied;
20138
20178
  * requests without a native session are not listed here but count everywhere else.
20139
20179
  */
20140
- sessions: external_exports.array(usageStatisticsSessionSchema).max(100)
20180
+ sessions: external_exports.array(usageStatisticsSessionSchema).max(100),
20181
+ /**
20182
+ * The sessions last active in the range, newest first, all filters applied. Optional for older
20183
+ * Hosts; missing is treated as no list rather than no sessions.
20184
+ */
20185
+ recentSessions: external_exports.array(usageStatisticsSessionSchema).max(100).optional()
20141
20186
  });
20142
20187
 
20143
20188
  // packages/shared-contracts/dist/ids.js
@@ -21426,14 +21471,37 @@ var RuntimeMaintenance = class {
21426
21471
  options2.environment.CODEXHOST_DATA_DIR ?? path7.join(home, ".codexhost", "remote", "data"),
21427
21472
  "remote-update.json"
21428
21473
  );
21474
+ this.#cancelPath = path7.join(path7.dirname(this.#statusPath), "remote-update.cancel");
21429
21475
  }
21430
21476
  options;
21431
21477
  #running;
21432
21478
  #statusPath;
21479
+ #cancelPath;
21433
21480
  #update = { phase: "idle", targetVersion: null, error: null };
21434
21481
  #blocked = false;
21482
+ #watch = null;
21435
21483
  #starting = null;
21436
21484
  #startingVersion = null;
21485
+ #ensureWatching() {
21486
+ if (this.#watch) return;
21487
+ this.#watch = setInterval(() => {
21488
+ void this.status().catch(() => void 0).then(() => {
21489
+ if (!this.#blocked && this.#watch) {
21490
+ clearInterval(this.#watch);
21491
+ this.#watch = null;
21492
+ }
21493
+ });
21494
+ }, UPDATER_WATCH_INTERVAL_MS);
21495
+ this.#watch.unref();
21496
+ }
21497
+ async #recordStartFailure(version2, failure) {
21498
+ await writeFile3(this.#cancelPath, JSON.stringify({ targetVersion: version2 }), { mode: 384 });
21499
+ await writeFile3(
21500
+ this.#statusPath,
21501
+ JSON.stringify({ phase: "failed", targetVersion: version2, error: failure }),
21502
+ { mode: 384 }
21503
+ );
21504
+ }
21437
21505
  #sshPending = /* @__PURE__ */ new Set();
21438
21506
  async setupSsh(input) {
21439
21507
  if (this.options.remote) throw new Error("SSH installation must run from the local computer");
@@ -21494,7 +21562,7 @@ var RuntimeMaintenance = class {
21494
21562
  installation(this.options.runtimePath, this.options.environment),
21495
21563
  this.#resources()
21496
21564
  ]);
21497
- if (this.#update.phase === "idle" || this.#blocked) {
21565
+ if (this.#update.phase === "idle" || this.#blocked || this.#update.phase === "failed") {
21498
21566
  try {
21499
21567
  const value2 = JSON.parse(await readFile5(this.#statusPath, "utf8"));
21500
21568
  const parsed2 = runtimeStatusSchema.shape.update.parse(value2);
@@ -21517,8 +21585,15 @@ var RuntimeMaintenance = class {
21517
21585
  parsed2.error = "The previous remote update was interrupted; retry the update";
21518
21586
  }
21519
21587
  }
21520
- if (this.#update === observedUpdate) this.#update = parsed2;
21521
- if (["failed", "succeeded"].includes(this.#update.phase)) this.#blocked = false;
21588
+ if (this.#update === observedUpdate) {
21589
+ this.#update = parsed2;
21590
+ if (["installing", "restarting"].includes(this.#update.phase)) {
21591
+ this.#blocked = true;
21592
+ this.#ensureWatching();
21593
+ } else if (["failed", "succeeded"].includes(this.#update.phase)) {
21594
+ this.#blocked = false;
21595
+ }
21596
+ }
21522
21597
  } catch {
21523
21598
  }
21524
21599
  }
@@ -21590,6 +21665,7 @@ var RuntimeMaintenance = class {
21590
21665
  await chmod2(helper, 448);
21591
21666
  const request = path7.join(work, "request.json");
21592
21667
  this.#update = { phase: "installing", targetVersion: version2, error: null };
21668
+ await unlink(this.#cancelPath).catch(() => void 0);
21593
21669
  await writeFile3(this.#statusPath, JSON.stringify(this.#update), { mode: 384 });
21594
21670
  await writeFile3(
21595
21671
  request,
@@ -21608,35 +21684,38 @@ var RuntimeMaintenance = class {
21608
21684
  );
21609
21685
  const starter = spawn3(
21610
21686
  "/bin/sh",
21611
- ["-c", '"$0" remote --request "$1" </dev/null >/dev/null 2>&1 &', helper, request],
21612
- { detached: true, stdio: "ignore", env: this.options.environment }
21687
+ ["-c", '"$0" remote --request "$1" </dev/null >/dev/null 2>&1 & echo $!', helper, request],
21688
+ { detached: true, stdio: ["ignore", "pipe", "ignore"], env: this.options.environment }
21613
21689
  );
21690
+ let pidOutput = "";
21691
+ starter.stdout?.on("data", (chunk) => {
21692
+ pidOutput += typeof chunk === "string" ? chunk : chunk.toString("utf8");
21693
+ });
21614
21694
  await new Promise((resolve, reject) => {
21615
21695
  starter.once("error", reject);
21616
21696
  starter.once(
21617
- "exit",
21697
+ "close",
21618
21698
  (code) => code === 0 ? resolve() : reject(new Error("Remote updater could not be started"))
21619
21699
  );
21620
21700
  });
21701
+ const helperPid = Number.parseInt(pidOutput.trim(), 10);
21702
+ if (!Number.isSafeInteger(helperPid) || helperPid <= 0) {
21703
+ throw new Error("Remote updater could not be started");
21704
+ }
21621
21705
  const deadline = Date.now() + UPDATER_START_TIMEOUT_MS;
21622
21706
  while (!await this.#helperReported()) {
21623
21707
  if (Date.now() >= deadline) {
21708
+ try {
21709
+ process.kill(helperPid);
21710
+ } catch {
21711
+ }
21624
21712
  const failure = "Remote updater did not start";
21625
- await writeFile3(
21626
- this.#statusPath,
21627
- JSON.stringify({ phase: "failed", targetVersion: version2, error: failure }),
21628
- { mode: 384 }
21629
- );
21713
+ await this.#recordStartFailure(version2, failure);
21630
21714
  throw new Error(failure);
21631
21715
  }
21632
21716
  await new Promise((resolve) => setTimeout(resolve, 50));
21633
21717
  }
21634
- const watch = setInterval(() => {
21635
- void this.status().catch(() => void 0).then(() => {
21636
- if (!this.#blocked) clearInterval(watch);
21637
- });
21638
- }, UPDATER_WATCH_INTERVAL_MS);
21639
- watch.unref();
21718
+ this.#ensureWatching();
21640
21719
  }
21641
21720
  async #helperReported() {
21642
21721
  try {
@@ -21650,6 +21729,7 @@ var RuntimeMaintenance = class {
21650
21729
 
21651
21730
  // packages/host-runtime/src/run-host-runtime.ts
21652
21731
  import { randomBytes as randomBytes4 } from "node:crypto";
21732
+ import { statSync } from "node:fs";
21653
21733
  import path37 from "node:path";
21654
21734
  import { homedir as homedir4 } from "node:os";
21655
21735
  import { fileURLToPath as fileURLToPath2 } from "node:url";
@@ -26894,61 +26974,6 @@ function finitePercent(value2) {
26894
26974
  function optionalReset(value2) {
26895
26975
  return nonNegativeSafeInteger(value2);
26896
26976
  }
26897
- function addBreakdown(target, source) {
26898
- if (!source)
26899
- return;
26900
- const fields = [
26901
- ["totalTokens", "totalTokens"],
26902
- ["inputTokens", "inputTokens"],
26903
- ["cachedInputTokens", "cachedInputTokens"],
26904
- ["cacheWriteInputTokens", "cacheWriteInputTokens"],
26905
- ["outputTokens", "outputTokens"],
26906
- ["reasoningOutputTokens", "reasoningOutputTokens"]
26907
- ];
26908
- for (const [sourceField, targetField] of fields) {
26909
- const value2 = nonNegativeSafeInteger(source[sourceField]);
26910
- if (value2 !== void 0)
26911
- target[targetField] = value2;
26912
- }
26913
- }
26914
- function observeCodexTokenUsage(value2) {
26915
- if (!isRecord9(value2) || value2.method !== "thread/tokenUsage/updated")
26916
- return null;
26917
- const params = value2.params;
26918
- if (!isRecord9(params))
26919
- return null;
26920
- const threadId2 = hostThreadIdSchema.safeParse(params.threadId);
26921
- const turnId = hostTurnIdSchema.safeParse(params.turnId);
26922
- if (!threadId2.success || !turnId.success)
26923
- return null;
26924
- const tokenUsage = params.tokenUsage;
26925
- if (!isRecord9(tokenUsage))
26926
- return null;
26927
- const total = isRecord9(tokenUsage.total) ? tokenUsage.total : void 0;
26928
- const last = isRecord9(tokenUsage.last) ? tokenUsage.last : void 0;
26929
- const usage = {};
26930
- addBreakdown(usage, total);
26931
- const contextUsedTokens = nonNegativeSafeInteger(last?.totalTokens);
26932
- const contextWindowTokens = nonNegativeSafeInteger(tokenUsage.modelContextWindow);
26933
- if (contextUsedTokens !== void 0 && contextWindowTokens !== void 0 && contextWindowTokens > 0) {
26934
- usage.contextUsedTokens = contextUsedTokens;
26935
- usage.contextWindowTokens = contextWindowTokens;
26936
- }
26937
- const inputTokens = nonNegativeSafeInteger(last?.inputTokens);
26938
- const cachedInputTokens = nonNegativeSafeInteger(last?.cachedInputTokens);
26939
- if (inputTokens !== void 0 && cachedInputTokens !== void 0 && inputTokens > 0) {
26940
- usage.cacheHitRatePercent = Math.min(100, cachedInputTokens / inputTokens * 100);
26941
- }
26942
- try {
26943
- return {
26944
- threadId: threadId2.data,
26945
- turnId: turnId.data,
26946
- usage: parseHostUsage(usage)
26947
- };
26948
- } catch {
26949
- return null;
26950
- }
26951
- }
26952
26977
  function parseRateLimitWindow(value2) {
26953
26978
  if (!isRecord9(value2))
26954
26979
  return null;
@@ -31759,6 +31784,16 @@ var ExternalThreadIdleRelease = class {
31759
31784
  const state = this.#activity.get(thread);
31760
31785
  if (state) state.outputFailed = true;
31761
31786
  }
31787
+ /** After a successful Session revival, allow idle release again. */
31788
+ clearOutputFailed(thread) {
31789
+ const state = this.#activity.get(thread);
31790
+ if (state) {
31791
+ state.outputFailed = false;
31792
+ state.lastActivity = Date.now();
31793
+ } else {
31794
+ this.touch(thread);
31795
+ }
31796
+ }
31762
31797
  failure(thread) {
31763
31798
  return this.#activity.get(thread)?.closeFailed ? CLOSE_FAILURE : void 0;
31764
31799
  }
@@ -31907,6 +31942,80 @@ var ExternalThreadIdleRelease = class {
31907
31942
  }
31908
31943
  };
31909
31944
 
31945
+ // packages/host-runtime/src/external-thread-lifecycle.ts
31946
+ var RELEASE_TIMEOUT_MS = 6e4;
31947
+ var ExternalSessionReleaseError = class extends Error {
31948
+ constructor(cause) {
31949
+ super(
31950
+ "External Session shutdown could not be confirmed. Retry to confirm release; recovery is blocked until the old Session closes and its output drains. If the Harness cannot retry shutdown, stop residual processes and restart Codex Desktop.",
31951
+ { cause }
31952
+ );
31953
+ this.name = "ExternalSessionReleaseError";
31954
+ }
31955
+ };
31956
+ var ExternalThreadLifecycle = class {
31957
+ #pending = /* @__PURE__ */ new Map();
31958
+ #closing = /* @__PURE__ */ new WeakMap();
31959
+ #releasing = /* @__PURE__ */ new Map();
31960
+ busy(id2) {
31961
+ return this.#pending.has(id2) || this.#releasing.has(id2);
31962
+ }
31963
+ pending() {
31964
+ return [...this.#pending];
31965
+ }
31966
+ run(id2, operation) {
31967
+ const previous = this.#pending.get(id2) ?? Promise.resolve();
31968
+ const task = previous.then(async () => {
31969
+ const releasing = this.#releasing.get(id2);
31970
+ if (releasing) await this.release(id2, releasing.session, releasing.output);
31971
+ return operation();
31972
+ });
31973
+ const settled = task.then(
31974
+ () => void 0,
31975
+ () => void 0
31976
+ );
31977
+ this.#pending.set(id2, settled);
31978
+ void settled.then(() => {
31979
+ if (this.#pending.get(id2) === settled) this.#pending.delete(id2);
31980
+ });
31981
+ return task;
31982
+ }
31983
+ /** Call inside the Thread's lifecycle operation. A timeout never cancels the owned close. */
31984
+ async release(id2, session, output) {
31985
+ this.#releasing.set(id2, { session, output });
31986
+ let closing = this.#closing.get(session);
31987
+ if (!closing) {
31988
+ closing = Promise.resolve().then(() => session.close());
31989
+ this.#closing.set(session, closing);
31990
+ const attempt = closing;
31991
+ void closing.catch(() => {
31992
+ if (this.#closing.get(session) === attempt) this.#closing.delete(session);
31993
+ });
31994
+ }
31995
+ let timeout;
31996
+ try {
31997
+ await Promise.race([
31998
+ closing.then(() => output),
31999
+ new Promise((_, reject) => {
32000
+ timeout = setTimeout(
32001
+ () => reject(new Error("Session shutdown timed out")),
32002
+ RELEASE_TIMEOUT_MS
32003
+ );
32004
+ timeout.unref();
32005
+ })
32006
+ ]);
32007
+ this.#releasing.delete(id2);
32008
+ } catch (error51) {
32009
+ throw new ExternalSessionReleaseError(error51);
32010
+ } finally {
32011
+ if (timeout) clearTimeout(timeout);
32012
+ }
32013
+ }
32014
+ async drain() {
32015
+ while (this.#pending.size) await Promise.all(this.#pending.values());
32016
+ }
32017
+ };
32018
+
31910
32019
  // packages/host-runtime/src/usage-metering.ts
31911
32020
  var TOKENS_PER_PRICE_UNIT = 1e6;
31912
32021
  var UsageMeter = class {
@@ -31922,6 +32031,8 @@ var UsageMeter = class {
31922
32031
  #turnOutputMs = 0;
31923
32032
  #timeToFirstOutputMs;
31924
32033
  #outputTokensPerSecond;
32034
+ #timingUnavailable = false;
32035
+ #timedRequests = /* @__PURE__ */ new Set();
31925
32036
  get metered() {
31926
32037
  return this.#metered;
31927
32038
  }
@@ -31938,12 +32049,53 @@ var UsageMeter = class {
31938
32049
  this.#requests.set(request.requestId, request);
31939
32050
  const duration4 = request.startedAtMs !== void 0 && request.completedAtMs !== void 0 ? request.completedAtMs - request.startedAtMs : 0;
31940
32051
  if (!request.historical && duration4 > 0 && activeTurnId !== null && activeTurnId === this.#turnId) {
31941
- this.#turnOutputTokens += request.outputTokens;
31942
- this.#turnOutputMs += duration4;
31943
- this.#outputTokensPerSecond = this.#turnOutputTokens / (this.#turnOutputMs / 1e3);
32052
+ this.recordOutputTiming(
32053
+ {
32054
+ requestId: request.requestId,
32055
+ outputTokens: request.outputTokens,
32056
+ startedAtMs: request.startedAtMs ?? 0,
32057
+ completedAtMs: request.completedAtMs ?? 0
32058
+ },
32059
+ activeTurnId
32060
+ );
31944
32061
  }
31945
32062
  return true;
31946
32063
  }
32064
+ /** Live timing is independent of history replacement and cannot create billing entries. */
32065
+ recordOutputTiming(request, turnId) {
32066
+ if (turnId !== this.#turnId || this.#timingUnavailable || this.#timedRequests.has(request.requestId))
32067
+ return false;
32068
+ if (!request.requestId || ![request.outputTokens, request.startedAtMs, request.completedAtMs].every(
32069
+ (value2) => Number.isSafeInteger(value2) && value2 >= 0
32070
+ ) || request.completedAtMs <= request.startedAtMs) {
32071
+ this.invalidateOutputTiming(turnId);
32072
+ return true;
32073
+ }
32074
+ this.#timedRequests.add(request.requestId);
32075
+ this.#turnOutputTokens += request.outputTokens;
32076
+ this.#turnOutputMs += request.completedAtMs - request.startedAtMs;
32077
+ this.#outputTokensPerSecond = this.#turnOutputTokens / (this.#turnOutputMs / 1e3);
32078
+ return true;
32079
+ }
32080
+ invalidateOutputTiming(turnId) {
32081
+ if (turnId !== this.#turnId) return;
32082
+ this.#timingUnavailable = true;
32083
+ }
32084
+ /** Discard in-flight timing across connections, but keep the last valid displayed speed. */
32085
+ resetTurnTiming() {
32086
+ this.#turnId = null;
32087
+ this.#turnStartedAtMs = null;
32088
+ this.#timeToFirstOutputMs = void 0;
32089
+ this.#timedRequests.clear();
32090
+ this.#timingUnavailable = false;
32091
+ }
32092
+ /** Replace a read-only session history after recovery, append, rollback or file replacement. */
32093
+ replaceHistory(requests, complete) {
32094
+ this.#requests.clear();
32095
+ this.#invalidRecord = false;
32096
+ for (const request of requests) this.recordRequest({ ...request, historical: true }, null);
32097
+ this.recordHistory(complete);
32098
+ }
31947
32099
  recordHistory(complete) {
31948
32100
  this.#metered = true;
31949
32101
  this.#historyComplete = complete;
@@ -31954,6 +32106,8 @@ var UsageMeter = class {
31954
32106
  this.#turnOutputObserved = false;
31955
32107
  this.#turnOutputTokens = 0;
31956
32108
  this.#turnOutputMs = 0;
32109
+ this.#timedRequests.clear();
32110
+ this.#timingUnavailable = false;
31957
32111
  }
31958
32112
  /** Returns true when this is the Turn's first visible output. */
31959
32113
  outputObserved(turnId, nowMs) {
@@ -31966,7 +32120,6 @@ var UsageMeter = class {
31966
32120
  }
31967
32121
  turnCompleted(turnId) {
31968
32122
  if (turnId !== this.#turnId) return;
31969
- if (this.#turnOutputMs === 0) this.#outputTokensPerSecond = void 0;
31970
32123
  this.#turnId = null;
31971
32124
  this.#turnStartedAtMs = null;
31972
32125
  }
@@ -32001,12 +32154,12 @@ var UsageMeter = class {
32001
32154
  const cacheHitRate = this.#sessionCacheHitRatePercent();
32002
32155
  if (cacheHitRate !== null) usage.sessionCacheHitRatePercent = cacheHitRate;
32003
32156
  }
32004
- if (this.#outputTokensPerSecond !== void 0) {
32005
- usage.outputTokensPerSecond = this.#outputTokensPerSecond;
32006
- }
32007
32157
  } else if (usage.totalCostUsd !== void 0) {
32008
32158
  usage.costSource = "native";
32009
32159
  }
32160
+ if (this.#timingUnavailable) delete usage.outputTokensPerSecond;
32161
+ if (this.#outputTokensPerSecond !== void 0)
32162
+ usage.outputTokensPerSecond = this.#outputTokensPerSecond;
32010
32163
  if (this.#timeToFirstOutputMs !== void 0) {
32011
32164
  usage.timeToFirstOutputMs = this.#timeToFirstOutputMs;
32012
32165
  }
@@ -32145,6 +32298,11 @@ var ExternalThreadOpenError = class extends Error {
32145
32298
  function errorMessage3(error51) {
32146
32299
  return error51 instanceof Error ? error51.message : String(error51);
32147
32300
  }
32301
+ function recoveryError(error51) {
32302
+ if (error51 instanceof ExternalThreadOpenError) return error51.rpcError;
32303
+ if (error51 instanceof ExternalSessionReleaseError) return { code: -32075, message: error51.message };
32304
+ return { code: -32076, message: "External Thread recovery failed; retry the request" };
32305
+ }
32148
32306
  var ExternalThreadRuntime = class {
32149
32307
  idleRelease;
32150
32308
  #adapters;
@@ -32152,7 +32310,8 @@ var ExternalThreadRuntime = class {
32152
32310
  #diagnose;
32153
32311
  #environment;
32154
32312
  #repository;
32155
- #restores = /* @__PURE__ */ new Map();
32313
+ #lifecycle = new ExternalThreadLifecycle();
32314
+ #hasRunningDescendants;
32156
32315
  #subagentRunning;
32157
32316
  #threads = /* @__PURE__ */ new Map();
32158
32317
  constructor(input) {
@@ -32162,12 +32321,13 @@ var ExternalThreadRuntime = class {
32162
32321
  this.#consumeOutputs = input.consumeOutputs;
32163
32322
  this.#diagnose = input.diagnose;
32164
32323
  this.#subagentRunning = input.subagentRunning ?? (() => false);
32324
+ this.#hasRunningDescendants = input.hasRunningDescendants ?? (() => false);
32165
32325
  this.idleRelease = new ExternalThreadIdleRelease({
32166
32326
  threads: () => this.values(),
32167
32327
  get: (id2) => this.get(id2),
32168
32328
  remove: (id2) => this.remove(id2),
32169
32329
  queue: input.idleRelease?.queue ?? new DesktopRequestQueue(),
32170
- canRelease: input.idleRelease?.canRelease ?? (() => false),
32330
+ canRelease: (thread) => !thread.sessionUnavailable && !this.#lifecycle.busy(thread.id) && (input.idleRelease?.canRelease(thread) ?? false),
32171
32331
  ...input.idleRelease?.onClosed ? { onClosed: input.idleRelease.onClosed } : {},
32172
32332
  diagnose: input.diagnose
32173
32333
  });
@@ -32183,7 +32343,45 @@ var ExternalThreadRuntime = class {
32183
32343
  }
32184
32344
  clear() {
32185
32345
  this.#threads.clear();
32186
- this.#restores.clear();
32346
+ }
32347
+ drain() {
32348
+ return this.#lifecycle.drain();
32349
+ }
32350
+ #withLifecycle(threadId2, operation) {
32351
+ return this.#lifecycle.run(threadId2, async () => {
32352
+ await this.idleRelease.waitForClose(threadId2);
32353
+ return operation();
32354
+ });
32355
+ }
32356
+ markSessionUnavailable(thread) {
32357
+ thread.sessionUnavailable = true;
32358
+ }
32359
+ #idleForRestore(thread) {
32360
+ if (thread.running || thread.activeTurnId !== null) return false;
32361
+ if (thread.session.hasBackgroundWork?.()) return false;
32362
+ if (this.#hasRunningDescendants(thread.id)) return false;
32363
+ return true;
32364
+ }
32365
+ async #dropIdleSession(thread) {
32366
+ if (this.#threads.get(thread.id) !== thread) return;
32367
+ thread.sessionUnavailable = true;
32368
+ await this.#lifecycle.release(thread.id, thread.session, thread.outputTask);
32369
+ if (this.#threads.get(thread.id) !== thread) return;
32370
+ await this.#retireSubagents(thread);
32371
+ if (this.#threads.get(thread.id) === thread) this.#threads.delete(thread.id);
32372
+ }
32373
+ /** Called without awaiting from the output consumer: release itself drains that consumer. */
32374
+ async discardUnavailableIdle(thread) {
32375
+ const session = thread.session;
32376
+ try {
32377
+ await this.#withLifecycle(thread.id, async () => {
32378
+ if (this.#threads.get(thread.id) !== thread || thread.session !== session || !thread.sessionUnavailable || !this.#idleForRestore(thread))
32379
+ return;
32380
+ await this.#dropIdleSession(thread);
32381
+ });
32382
+ } catch (error51) {
32383
+ this.#diagnose(error51);
32384
+ }
32187
32385
  }
32188
32386
  register(input) {
32189
32387
  const harnessId = input.record.harnessId;
@@ -32228,7 +32426,8 @@ var ExternalThreadRuntime = class {
32228
32426
  ephemeralTurnIds: /* @__PURE__ */ new Set(),
32229
32427
  persistenceError: null,
32230
32428
  projectedTerminalTurnId: null,
32231
- ignoredInteractionIds: /* @__PURE__ */ new Set()
32429
+ ignoredInteractionIds: /* @__PURE__ */ new Set(),
32430
+ sessionUnavailable: false
32232
32431
  };
32233
32432
  this.idleRelease.touch(externalThread);
32234
32433
  externalThread.outputTask = this.#consumeOutputs(externalThread);
@@ -32255,11 +32454,12 @@ var ExternalThreadRuntime = class {
32255
32454
  } catch (error51) {
32256
32455
  this.#diagnose(error51);
32257
32456
  }
32258
- await this.#retireSubagents(current.id);
32457
+ await this.#retireSubagents(current, true);
32259
32458
  this.#threads.delete(current.id);
32260
32459
  return this.register(input);
32261
32460
  }
32262
- async #retireSubagents(parentId) {
32461
+ async #retireSubagents(parent, bestEffort = false) {
32462
+ const parentId = parent.id;
32263
32463
  const ancestors = /* @__PURE__ */ new Map();
32264
32464
  const descendant = async (record4) => {
32265
32465
  const visited = /* @__PURE__ */ new Set();
@@ -32276,24 +32476,35 @@ var ExternalThreadRuntime = class {
32276
32476
  }
32277
32477
  return false;
32278
32478
  };
32279
- for (const [id2, restoring] of [...this.#restores]) {
32280
- if (await descendant(await this.#repository.find(id2))) {
32281
- await restoring.catch(this.#diagnose);
32282
- }
32479
+ for (const [id2, pending] of this.#lifecycle.pending()) {
32480
+ if (id2 !== parentId && await descendant(await this.#repository.find(id2))) await pending;
32283
32481
  }
32284
- for (const child of this.#threads.values()) {
32482
+ for (const child of [...this.#threads.values()]) {
32285
32483
  if (!await descendant(child.record)) continue;
32286
- this.#threads.delete(child.id);
32287
- try {
32288
- await child.session.close();
32289
- await child.outputTask;
32290
- } catch (error51) {
32291
- this.#diagnose(error51);
32292
- }
32484
+ const retire = async () => {
32485
+ if (this.#threads.get(parentId) !== parent || this.#threads.get(child.id) !== child) return;
32486
+ if (bestEffort) {
32487
+ this.#threads.delete(child.id);
32488
+ await child.session.close();
32489
+ await child.outputTask;
32490
+ } else {
32491
+ child.sessionUnavailable = true;
32492
+ await this.#lifecycle.release(child.id, child.session, child.outputTask);
32493
+ if (this.#threads.get(child.id) === child) this.#threads.delete(child.id);
32494
+ }
32495
+ };
32496
+ if (bestEffort) await retire().catch(this.#diagnose);
32497
+ else await this.#withLifecycle(child.id, retire);
32293
32498
  }
32294
32499
  }
32295
32500
  async locate(threadId2) {
32296
- await this.idleRelease.waitForClose(threadId2);
32501
+ try {
32502
+ return await this.#withLifecycle(threadId2, () => this.#locate(threadId2));
32503
+ } catch (error51) {
32504
+ return { kind: "error", error: recoveryError(error51) };
32505
+ }
32506
+ }
32507
+ async #locate(threadId2) {
32297
32508
  const loaded = this.#threads.get(threadId2);
32298
32509
  if (loaded) {
32299
32510
  const failure = this.idleRelease.failure(loaded);
@@ -32319,37 +32530,61 @@ var ExternalThreadRuntime = class {
32319
32530
  return { kind: "external", record: record4, thread: null };
32320
32531
  }
32321
32532
  async resolve(threadId2) {
32322
- const location = await this.locate(threadId2);
32323
- if (location.kind !== "external") return location;
32324
- if (location.thread) {
32325
- this.idleRelease.touch(location.thread);
32326
- return { kind: "external", thread: location.thread, historyFresh: false };
32327
- }
32328
- const { record: record4 } = location;
32329
- let restoring = this.#restores.get(threadId2);
32330
- if (!restoring) {
32331
- const restored = this.#threads.get(threadId2);
32332
- if (restored) {
32333
- this.idleRelease.touch(restored);
32334
- return { kind: "external", thread: restored, historyFresh: false };
32335
- }
32336
- restoring = this.#restore(record4).finally(() => {
32337
- this.#restores.delete(threadId2);
32533
+ try {
32534
+ return await this.#withLifecycle(threadId2, async () => {
32535
+ const location = await this.#locate(threadId2);
32536
+ if (location.kind !== "external") return location;
32537
+ if (location.thread) {
32538
+ if (location.thread.sessionUnavailable && this.#idleForRestore(location.thread)) {
32539
+ await this.#dropIdleSession(location.thread);
32540
+ const current = this.#threads.get(threadId2);
32541
+ if (current) return { kind: "external", thread: current, historyFresh: false };
32542
+ } else {
32543
+ this.idleRelease.touch(location.thread);
32544
+ return { kind: "external", thread: location.thread, historyFresh: false };
32545
+ }
32546
+ }
32547
+ return {
32548
+ kind: "external",
32549
+ thread: await this.#restore(location.record),
32550
+ historyFresh: true
32551
+ };
32338
32552
  });
32339
- this.#restores.set(threadId2, restoring);
32553
+ } catch (error51) {
32554
+ return { kind: "error", error: recoveryError(error51) };
32340
32555
  }
32556
+ }
32557
+ async refresh(thread) {
32341
32558
  try {
32342
- return { kind: "external", thread: await restoring, historyFresh: true };
32559
+ return await this.#withLifecycle(thread.id, () => this.#refresh(thread));
32343
32560
  } catch (error51) {
32561
+ return recoveryError(error51);
32562
+ }
32563
+ }
32564
+ async #refresh(thread) {
32565
+ if (this.#threads.get(thread.id) !== thread) {
32344
32566
  return {
32345
- kind: "error",
32346
- error: error51 instanceof ExternalThreadOpenError ? error51.rpcError : { code: -32076, message: "External Thread recovery failed" }
32567
+ code: -32076,
32568
+ message: "External Session changed during recovery; retry the request"
32347
32569
  };
32348
32570
  }
32349
- }
32350
- async refresh(thread) {
32351
- const snapshot2 = await thread.session.readSnapshot();
32352
- if (!snapshot2.ok) return mapExternalThreadHarnessError(snapshot2.error, "read");
32571
+ const failure = this.idleRelease.failure(thread);
32572
+ if (failure) return { code: -32075, message: failure };
32573
+ if (thread.sessionUnavailable && this.#idleForRestore(thread)) {
32574
+ const revived = await this.#reviveIdleSession(thread);
32575
+ if (revived) return revived;
32576
+ }
32577
+ let snapshot2 = await thread.session.readSnapshot();
32578
+ if (!snapshot2.ok) {
32579
+ if (snapshot2.error.code === "invalidState" && this.#idleForRestore(thread) && this.#threads.get(thread.id) === thread) {
32580
+ const revived = await this.#reviveIdleSession(thread);
32581
+ if (revived) return revived;
32582
+ snapshot2 = await thread.session.readSnapshot();
32583
+ if (!snapshot2.ok) return mapExternalThreadHarnessError(snapshot2.error, "read");
32584
+ } else {
32585
+ return mapExternalThreadHarnessError(snapshot2.error, "read");
32586
+ }
32587
+ }
32353
32588
  try {
32354
32589
  const aligned = await this.#repository.alignSnapshot(thread.record, snapshot2.value);
32355
32590
  thread.record = aligned.record;
@@ -32366,6 +32601,122 @@ var ExternalThreadRuntime = class {
32366
32601
  return { code: -32081, message: "External Thread history could not be persisted" };
32367
32602
  }
32368
32603
  }
32604
+ async #reviveIdleSession(thread) {
32605
+ thread.sessionUnavailable = true;
32606
+ const harnessId = thread.record.harnessId;
32607
+ const adapter = this.#adapters.get(harnessId);
32608
+ if (!adapter || !thread.record.nativeSessionRef) {
32609
+ await this.#dropIdleSession(thread);
32610
+ return { code: -32077, message: "External Harness is unavailable" };
32611
+ }
32612
+ await this.#lifecycle.release(thread.id, thread.session, thread.outputTask);
32613
+ await this.#retireSubagents(thread);
32614
+ if (this.#threads.get(thread.id) !== thread) {
32615
+ return { code: -32076, message: "External Thread recovery failed" };
32616
+ }
32617
+ const restoredSelection = decodeExternalTransportSelection(
32618
+ harnessId,
32619
+ thread.record.transportModelId
32620
+ );
32621
+ const opened = await adapter.open({
32622
+ kind: "resume",
32623
+ cwd: thread.record.cwd,
32624
+ environment: { ...this.#environment, [DELEGATION_THREAD_ID_ENV]: thread.record.hostThreadId },
32625
+ nativeRef: thread.record.nativeSessionRef,
32626
+ knownTurnRefs: thread.record.turnMappings.map(({ nativeTurnRef }) => nativeTurnRef),
32627
+ ...restoredSelection?.model ? { model: restoredSelection.model } : {},
32628
+ ...restoredSelection?.thinkingOptionId ? { thinkingOptionId: restoredSelection.thinkingOptionId } : {},
32629
+ ...restoredSelection?.permissionModeId ? { permissionModeId: restoredSelection.permissionModeId } : {}
32630
+ });
32631
+ if (!opened.ok) {
32632
+ await this.#dropIdleSession(thread);
32633
+ return mapExternalThreadHarnessError(opened.error, "resume");
32634
+ }
32635
+ const session = opened.value;
32636
+ if (this.#threads.get(thread.id) !== thread) {
32637
+ await this.#lifecycle.release(thread.id, session, Promise.resolve());
32638
+ return { code: -32076, message: "External Thread recovery failed" };
32639
+ }
32640
+ try {
32641
+ const snapshot2 = await session.readSnapshot();
32642
+ if (!snapshot2.ok) {
32643
+ await this.#lifecycle.release(thread.id, session, Promise.resolve());
32644
+ await this.#dropIdleSession(thread);
32645
+ return mapExternalThreadHarnessError(snapshot2.error, "read");
32646
+ }
32647
+ let aligned = await this.#repository.alignSnapshot(thread.record, snapshot2.value);
32648
+ if (this.#threads.get(thread.id) !== thread) {
32649
+ await this.#lifecycle.release(thread.id, session, Promise.resolve());
32650
+ return { code: -32076, message: "External Thread recovery failed" };
32651
+ }
32652
+ const restoredState = snapshot2.value.state;
32653
+ const effectiveModel = restoredState ? restoredState.effectiveModel : restoredSelection?.model;
32654
+ const effectiveThinkingOptionId = restoredState ? restoredState.effectiveThinkingOptionId : restoredSelection?.thinkingOptionId;
32655
+ const effectivePermissionModeId = restoredState ? restoredState.effectivePermissionModeId : restoredSelection?.permissionModeId;
32656
+ let transportModelId = aligned.record.transportModelId;
32657
+ if (restoredState && effectiveModel) {
32658
+ const liveSelection = {
32659
+ model: effectiveModel,
32660
+ ...effectiveThinkingOptionId ? { thinkingOptionId: effectiveThinkingOptionId } : {},
32661
+ ...effectivePermissionModeId ? { permissionModeId: effectivePermissionModeId } : {}
32662
+ };
32663
+ transportModelId = encodeExternalTransportSelection(harnessId, liveSelection);
32664
+ if (transportModelId !== aligned.record.transportModelId) {
32665
+ try {
32666
+ aligned = {
32667
+ ...aligned,
32668
+ record: await this.#repository.setTransportModelId(
32669
+ aligned.record.hostThreadId,
32670
+ transportModelId
32671
+ )
32672
+ };
32673
+ } catch (error51) {
32674
+ this.#diagnose(error51);
32675
+ }
32676
+ }
32677
+ }
32678
+ if (this.#threads.get(thread.id) !== thread) {
32679
+ await this.#lifecycle.release(thread.id, session, Promise.resolve());
32680
+ return { code: -32076, message: "External Thread recovery failed" };
32681
+ }
32682
+ const initialState = restoredState ?? session.initialState;
32683
+ const observerState = {
32684
+ ...initialState,
32685
+ ...effectiveModel ? { effectiveModel } : {},
32686
+ ...effectiveThinkingOptionId ? { effectiveThinkingOptionId } : {},
32687
+ ...effectivePermissionModeId ? { effectivePermissionModeId } : {}
32688
+ };
32689
+ thread.session = session;
32690
+ thread.record = aligned.record;
32691
+ thread.turns = aligned.turns;
32692
+ thread.historyHydrated = true;
32693
+ thread.sessionUnavailable = false;
32694
+ thread.persistenceError = null;
32695
+ thread.transportModelId = transportModelId;
32696
+ if (effectiveModel) thread.requestedModel = effectiveModel;
32697
+ if (effectiveThinkingOptionId) thread.requestedThinkingOptionId = effectiveThinkingOptionId;
32698
+ if (effectivePermissionModeId) thread.requestedPermissionModeId = effectivePermissionModeId;
32699
+ thread.stateObserver = new SessionStateObserver(observerState);
32700
+ thread.latestUsage = session.initialUsage;
32701
+ thread.usageMeter = new UsageMeter();
32702
+ thread.usageTurnId = null;
32703
+ thread.thread = externalThreadValue({
32704
+ record: aligned.record,
32705
+ turns: aligned.turns,
32706
+ sessionId: thread.sessionId,
32707
+ running: thread.running
32708
+ });
32709
+ thread.outputTask = this.#consumeOutputs(thread);
32710
+ this.idleRelease.clearOutputFailed(thread);
32711
+ return null;
32712
+ } catch (error51) {
32713
+ if (error51 instanceof ExternalSessionReleaseError) throw error51;
32714
+ await this.#lifecycle.release(thread.id, session, Promise.resolve());
32715
+ this.#diagnose(error51);
32716
+ await this.#dropIdleSession(thread);
32717
+ return { code: -32081, message: "External Thread history could not be persisted" };
32718
+ }
32719
+ }
32369
32720
  async persistTerminalIdentity(thread, event) {
32370
32721
  if (thread.persistenceError) return thread.persistenceError;
32371
32722
  if (!event.nativeTurnRef) {
@@ -32504,7 +32855,8 @@ var ExternalThreadRuntime = class {
32504
32855
  transportModelId
32505
32856
  });
32506
32857
  } catch (error51) {
32507
- await session.close().catch(() => void 0);
32858
+ if (error51 instanceof ExternalSessionReleaseError) throw error51;
32859
+ await this.#lifecycle.release(record4.hostThreadId, session, Promise.resolve());
32508
32860
  throw error51;
32509
32861
  }
32510
32862
  }
@@ -32905,7 +33257,7 @@ function projectDelegationThreadSnapshot(input) {
32905
33257
  // packages/host-runtime/src/harness-delegation-coordinator.ts
32906
33258
  var IMPLICIT_DEDUPLICATION_MS = 3e4;
32907
33259
  var NATIVE_REF_TIMEOUT_MS = 1e4;
32908
- function delay2(milliseconds) {
33260
+ function delay3(milliseconds) {
32909
33261
  return new Promise((resolve) => setTimeout(resolve, milliseconds));
32910
33262
  }
32911
33263
  function terminal(status2) {
@@ -33295,7 +33647,7 @@ var HarnessDelegationCoordinator = class {
33295
33647
  if (terminal(snapshot2.status)) return { ...snapshot2, timedOut: false };
33296
33648
  const remaining = deadline - Date.now();
33297
33649
  if (remaining <= 0) return { ...snapshot2, timedOut: true };
33298
- await delay2(Math.min(100, remaining));
33650
+ await delay3(Math.min(100, remaining));
33299
33651
  }
33300
33652
  }
33301
33653
  async list(input) {
@@ -35429,15 +35781,21 @@ var HarnessPluginRegistry = class {
35429
35781
  };
35430
35782
 
35431
35783
  // packages/host-runtime/src/harness-plugin-loader.ts
35784
+ function hasValidSessionUsage(value2) {
35785
+ const capability = Reflect.get(value2, "sessionUsage");
35786
+ return capability === void 0 || capability !== null && typeof capability === "object" && ["observe", "read"].every((key) => typeof Reflect.get(capability, key) === "function") && ["requestOptions", "shouldForwardNotification", "reset"].every(
35787
+ (key) => Reflect.get(capability, key) === void 0 || typeof Reflect.get(capability, key) === "function"
35788
+ );
35789
+ }
35432
35790
  function isAdapter(value2) {
35433
35791
  if (!value2 || typeof value2 !== "object") return false;
35434
- return ["inspect", "open", "close"].every((key) => typeof Reflect.get(value2, key) === "function");
35792
+ return hasValidSessionUsage(value2) && ["inspect", "open", "close"].every((key) => typeof Reflect.get(value2, key) === "function");
35435
35793
  }
35436
35794
  function isUsageAdapter(value2) {
35437
35795
  if (!value2 || typeof value2 !== "object" || typeof Reflect.get(value2, "close") !== "function")
35438
35796
  return false;
35439
35797
  const capability = Reflect.get(value2, "usageStatistics");
35440
- return !!capability && typeof capability === "object" && ["listSources", "readSource"].every(
35798
+ return hasValidSessionUsage(value2) && !!capability && typeof capability === "object" && ["listSources", "readSource"].every(
35441
35799
  (key) => typeof Reflect.get(capability, key) === "function"
35442
35800
  ) && !("open" in value2) && !("inspect" in value2);
35443
35801
  }
@@ -35578,11 +35936,12 @@ async function loadHarnessPlugins(options2) {
35578
35936
  }
35579
35937
  if (options2.signal?.aborted) return registry2;
35580
35938
  let next = 0;
35939
+ let batch = pending;
35581
35940
  const loaded = /* @__PURE__ */ new Map();
35582
35941
  const worker = async () => {
35583
35942
  for (; ; ) {
35584
35943
  if (options2.signal?.aborted) return;
35585
- const candidate = pending[next++];
35944
+ const candidate = batch[next++];
35586
35945
  if (!candidate) return;
35587
35946
  if (options2.signal?.aborted) return;
35588
35947
  const { manifest } = candidate;
@@ -35628,10 +35987,19 @@ async function loadHarnessPlugins(options2) {
35628
35987
  loaded.set(candidate, { descriptor, adapter });
35629
35988
  }
35630
35989
  };
35631
- await Promise.all(Array.from({ length: Math.min(4, pending.length) }, () => worker()));
35632
- for (const candidate of pending) {
35633
- const entry = loaded.get(candidate);
35634
- if (entry) await registry2.register(entry.descriptor, entry.adapter);
35990
+ const batches = options2.onUsageAdaptersLoaded ? [
35991
+ pending.filter((c) => c.manifest.kind === "usage"),
35992
+ pending.filter((c) => c.manifest.kind !== "usage")
35993
+ ] : [pending];
35994
+ for (const [index, candidates2] of batches.entries()) {
35995
+ batch = candidates2;
35996
+ next = 0;
35997
+ await Promise.all(Array.from({ length: Math.min(4, batch.length) }, () => worker()));
35998
+ for (const candidate of batch) {
35999
+ const entry = loaded.get(candidate);
36000
+ if (entry) await registry2.register(entry.descriptor, entry.adapter);
36001
+ }
36002
+ if (index === 0) options2.onUsageAdaptersLoaded?.(registry2.usageAdapters);
35635
36003
  }
35636
36004
  return registry2;
35637
36005
  }
@@ -36533,16 +36901,16 @@ var OfficialRuntimeScope = class {
36533
36901
  #scheduleRecovery() {
36534
36902
  if (!this.#recovery || this.#closed || this.#recoveryTimer || this.#starting) return;
36535
36903
  const { delaysMs } = this.#recovery;
36536
- const delay5 = delaysMs[Math.min(this.#recoveryAttempt, delaysMs.length - 1)] ?? 0;
36904
+ const delay6 = delaysMs[Math.min(this.#recoveryAttempt, delaysMs.length - 1)] ?? 0;
36537
36905
  this.#recoveryAttempt++;
36538
36906
  this.#diagnosticOutput.write(
36539
- `codexhost: official Codex is unavailable; restarting in ${String(delay5)}ms
36907
+ `codexhost: official Codex is unavailable; restarting in ${String(delay6)}ms
36540
36908
  `
36541
36909
  );
36542
36910
  const timer = setTimeout(() => {
36543
36911
  this.#recoveryTimer = void 0;
36544
36912
  this.start().catch(() => void 0);
36545
- }, delay5);
36913
+ }, delay6);
36546
36914
  timer.unref();
36547
36915
  this.#recoveryTimer = timer;
36548
36916
  }
@@ -37289,11 +37657,163 @@ async function moveThreadSection(input) {
37289
37657
  }
37290
37658
  }
37291
37659
 
37660
+ // packages/host-runtime/src/observed-session-usage.ts
37661
+ var ObservedSessionUsage = class {
37662
+ constructor(providers, prices, diagnose) {
37663
+ this.providers = providers;
37664
+ this.prices = prices;
37665
+ this.diagnose = diagnose;
37666
+ }
37667
+ providers;
37668
+ prices;
37669
+ diagnose;
37670
+ #states = /* @__PURE__ */ new Map();
37671
+ #pending = /* @__PURE__ */ new Map();
37672
+ #abort = new AbortController();
37673
+ #initializeParams = null;
37674
+ initialized(params) {
37675
+ this.#initializeParams = params;
37676
+ for (const state of this.#states.values()) {
37677
+ state.meter.resetTurnTiming();
37678
+ state.activeTurnId = null;
37679
+ }
37680
+ for (const provider of this.providers()) {
37681
+ try {
37682
+ provider.reset?.();
37683
+ } catch (error51) {
37684
+ this.diagnose(error51);
37685
+ }
37686
+ }
37687
+ }
37688
+ requestOptions(method, params) {
37689
+ if (this.#abort.signal.aborted) return null;
37690
+ let result = null;
37691
+ for (const provider of this.providers()) {
37692
+ try {
37693
+ const patch = provider.requestOptions?.({
37694
+ method,
37695
+ params,
37696
+ initializeParams: this.#initializeParams
37697
+ });
37698
+ if (patch) result = Object.assign(result ?? {}, jsonObjectSchema.parse(patch));
37699
+ } catch (error51) {
37700
+ this.diagnose(error51);
37701
+ }
37702
+ }
37703
+ return result;
37704
+ }
37705
+ shouldForwardNotification(message2) {
37706
+ if (this.#abort.signal.aborted) return true;
37707
+ for (const provider of this.providers()) {
37708
+ try {
37709
+ if (provider.shouldForwardNotification?.(message2) === false) return false;
37710
+ } catch (error51) {
37711
+ this.diagnose(error51);
37712
+ }
37713
+ }
37714
+ return true;
37715
+ }
37716
+ observe(message2, now = Date.now()) {
37717
+ if (this.#abort.signal.aborted) return [];
37718
+ const changed = /* @__PURE__ */ new Set();
37719
+ for (const provider of this.providers()) {
37720
+ try {
37721
+ const observation = provider.observe(message2, now);
37722
+ if (!observation) continue;
37723
+ const state = this.#state(observation.sessionId, provider);
37724
+ if (state.provider !== provider) continue;
37725
+ if (observation.usage) {
37726
+ state.live = parseHostUsage(observation.usage);
37727
+ state.revision++;
37728
+ }
37729
+ const turn = observation.turn;
37730
+ if (turn?.phase === "started" && state.activeTurnId !== turn.id) {
37731
+ state.meter.resetTurnTiming();
37732
+ state.meter.turnStarted(turn.id, now);
37733
+ state.activeTurnId = turn.id;
37734
+ }
37735
+ const timing = observation.requestTiming;
37736
+ if (timing) state.meter.recordOutputTiming(timing, timing.turnId);
37737
+ if (observation.timingUnavailable)
37738
+ state.meter.invalidateOutputTiming(observation.timingUnavailable.turnId);
37739
+ if (turn?.phase === "completed") {
37740
+ state.meter.turnCompleted(turn.id);
37741
+ if (state.activeTurnId === turn.id) state.activeTurnId = null;
37742
+ }
37743
+ const firstOutput = turn?.phase === "output" && state.meter.outputObserved(turn.id, now);
37744
+ if (observation.usage || firstOutput || timing || observation.timingUnavailable || turn?.phase === "started" || turn?.phase === "completed")
37745
+ changed.add(observation.sessionId);
37746
+ } catch (error51) {
37747
+ this.diagnose(error51);
37748
+ }
37749
+ }
37750
+ return [...changed];
37751
+ }
37752
+ async read(sessionId) {
37753
+ if (this.#abort.signal.aborted) return null;
37754
+ let pending = this.#pending.get(sessionId);
37755
+ if (!pending) {
37756
+ pending = this.#refresh(sessionId).finally(() => this.#pending.delete(sessionId));
37757
+ this.#pending.set(sessionId, pending);
37758
+ }
37759
+ await pending;
37760
+ const state = this.#states.get(sessionId);
37761
+ if (!state || this.#abort.signal.aborted) return null;
37762
+ const native = state.live ?? state.history;
37763
+ const result = state.meter.derive(native, await this.prices.lookup());
37764
+ if (result?.unpricedModels?.length) this.prices.missing();
37765
+ return result;
37766
+ }
37767
+ async #refresh(sessionId) {
37768
+ const existing = this.#states.get(sessionId);
37769
+ for (const provider of existing ? [existing.provider] : this.providers()) {
37770
+ const revision = existing?.revision ?? 0;
37771
+ try {
37772
+ const history = await provider.read(sessionId, this.#abort.signal);
37773
+ if (this.#abort.signal.aborted) return;
37774
+ if (!history) {
37775
+ if (existing?.meter.metered) existing.meter.recordHistory(false);
37776
+ continue;
37777
+ }
37778
+ const state = this.#state(sessionId, provider);
37779
+ state.history = history.usage ? parseHostUsage(history.usage) : null;
37780
+ state.meter.replaceHistory(
37781
+ history.requests,
37782
+ history.complete && state.revision === revision
37783
+ );
37784
+ return;
37785
+ } catch (error51) {
37786
+ existing?.meter.recordHistory(false);
37787
+ if (!this.#abort.signal.aborted) this.diagnose(error51);
37788
+ }
37789
+ }
37790
+ }
37791
+ #state(id2, provider) {
37792
+ let state = this.#states.get(id2);
37793
+ if (!state) {
37794
+ state = {
37795
+ provider,
37796
+ meter: new UsageMeter(),
37797
+ live: null,
37798
+ history: null,
37799
+ revision: 0,
37800
+ activeTurnId: null
37801
+ };
37802
+ this.#states.set(id2, state);
37803
+ }
37804
+ return state;
37805
+ }
37806
+ close() {
37807
+ this.#abort.abort();
37808
+ this.#states.clear();
37809
+ }
37810
+ };
37811
+
37292
37812
  // packages/host-runtime/src/app-server-host.ts
37293
37813
  var CONSOLE_REQUEST_TIMEOUT_MS = 12e4;
37294
37814
  var SUBAGENT_TERMINAL_REFRESH_DELAYS_MS = [0, 50, 100, 150];
37295
37815
  var THREAD_USAGE_UPDATED_METHOD = "codexhost/thread/usage/updated";
37296
- function delay3(milliseconds) {
37816
+ function delay4(milliseconds) {
37297
37817
  return new Promise((resolve) => setTimeout(resolve, milliseconds));
37298
37818
  }
37299
37819
  function isRecord18(value2) {
@@ -37486,10 +38006,12 @@ var AppServerHost = class {
37486
38006
  #launchSettings;
37487
38007
  #accountInspections = new HarnessAccountInspectionCache();
37488
38008
  #externalRuntime;
38009
+ #executionHosts = /* @__PURE__ */ new WeakMap();
37489
38010
  #externalPrewarms = new ExternalThreadPrewarms();
37490
38011
  #externalSteering = new ExternalTurnSteering();
37491
38012
  #liveCommandCache = new LiveCommandCatalogCache();
37492
38013
  #modelPrices;
38014
+ #observedSessionUsage;
37493
38015
  #repository;
37494
38016
  #pendingDesktopApprovals = /* @__PURE__ */ new Map();
37495
38017
  #pendingDesktopQuestions = /* @__PURE__ */ new Map();
@@ -37506,7 +38028,6 @@ var AppServerHost = class {
37506
38028
  #activeWorkDrainWaiters = /* @__PURE__ */ new Set();
37507
38029
  #pendingOfficialDelegationThreads = /* @__PURE__ */ new Set();
37508
38030
  #pendingOfficialTerminalStatuses = /* @__PURE__ */ new Map();
37509
- #officialUsageByThread = /* @__PURE__ */ new Map();
37510
38031
  #officialRateLimits = new AccountRateLimits();
37511
38032
  #routeObservationTracker = new RequestRouteObservationTracker();
37512
38033
  #officialServerRequests = /* @__PURE__ */ new Map();
@@ -37524,6 +38045,7 @@ var AppServerHost = class {
37524
38045
  #closeRequested = false;
37525
38046
  #pluginLoadAbort = new AbortController();
37526
38047
  #pluginLoading;
38048
+ #usagePluginsReady;
37527
38049
  #desktopRequests = new DesktopRequestQueue();
37528
38050
  #drainActiveWorkOnInputEnd = false;
37529
38051
  #desktopInputEnded = false;
@@ -37535,6 +38057,13 @@ var AppServerHost = class {
37535
38057
  ...options2
37536
38058
  };
37537
38059
  this.#modelPrices = options2.modelPrices ?? new ModelPriceCatalog();
38060
+ this.#observedSessionUsage = new ObservedSessionUsage(
38061
+ () => [...this.#externalAdapters.values(), ...this.#usageOnlyAdapters].flatMap(
38062
+ (adapter) => adapter.sessionUsage ? [adapter.sessionUsage] : []
38063
+ ),
38064
+ this.#modelPrices,
38065
+ (error51) => this.#diagnose(error51)
38066
+ );
37538
38067
  this.#writer = new OrderedWriter(this.#options.desktopOutput, (value2) => {
37539
38068
  this.#noteDesktopReply(value2);
37540
38069
  return this.#takeConsoleReply(value2);
@@ -37612,6 +38141,7 @@ var AppServerHost = class {
37612
38141
  consumeOutputs: (thread) => this.#consumeHarnessOutputs(thread),
37613
38142
  diagnose: (error51) => this.#diagnose(error51),
37614
38143
  subagentRunning: (threadId2) => this.#subagentThreadStatuses.get(threadId2) === "active",
38144
+ hasRunningDescendants: (threadId2) => this.#hasRunningSubagents(threadId2),
37615
38145
  idleRelease: {
37616
38146
  queue: this.#desktopRequests,
37617
38147
  onClosed: async (thread) => {
@@ -37670,7 +38200,11 @@ var AppServerHost = class {
37670
38200
  wait: (input) => this.#waitForPlugins().then(() => this.#delegationCoordinator.wait(input)),
37671
38201
  list: (input) => this.#waitForPlugins().then(() => this.#delegationCoordinator.list(input)),
37672
38202
  canHandleStart: (input) => this.#canHandleDelegationStart(input),
37673
- ownsThread: (threadId2) => this.#ownsDelegationThread(threadId2)
38203
+ ownsThread: (threadId2) => this.#ownsDelegationThread(threadId2),
38204
+ executionHostId: (threadId2) => {
38205
+ const thread = this.#externalRuntime.get(threadId2);
38206
+ return thread ? this.#executionHosts.get(thread) : void 0;
38207
+ }
37674
38208
  });
37675
38209
  this.#unregisterDelegationApi = typeof unregisterDelegationApi === "function" ? unregisterDelegationApi : void 0;
37676
38210
  }
@@ -37702,18 +38236,29 @@ var AppServerHost = class {
37702
38236
  this.#options.usageStatistics?.attach(
37703
38237
  () => [.../* @__PURE__ */ new Set([...this.#externalAdapters.values(), ...this.#usageOnlyAdapters])].flatMap(
37704
38238
  (adapter) => adapter.usageStatistics ? [{ harness: adapter.harnessId, capability: adapter.usageStatistics }] : []
37705
- )
38239
+ ),
38240
+ () => this.#repository.list()
37706
38241
  );
37707
38242
  }
37708
38243
  #waitForPlugins() {
37709
- return this.#pluginLoading ??= this.#loadInstalledPlugins().catch((error51) => {
37710
- this.#diagnose(`Harness plugin load failed: ${errorMessage4(error51)}`);
37711
- });
38244
+ if (!this.#pluginLoading) {
38245
+ const ready = Promise.withResolvers();
38246
+ const usageReady = () => ready.resolve(void 0);
38247
+ this.#usagePluginsReady = ready.promise;
38248
+ this.#pluginLoading = this.#loadInstalledPlugins(usageReady).catch((error51) => {
38249
+ this.#diagnose(`Harness plugin load failed: ${errorMessage4(error51)}`);
38250
+ }).finally(usageReady);
38251
+ }
38252
+ return this.#pluginLoading;
37712
38253
  }
37713
- async #loadInstalledPlugins() {
38254
+ async #loadInstalledPlugins(usageReady) {
37714
38255
  if (!this.#options.pluginRoots || this.#pluginLoadAbort.signal.aborted) return;
37715
38256
  const plugins = await loadHarnessPlugins({
37716
38257
  roots: this.#options.pluginRoots,
38258
+ onUsageAdaptersLoaded: (adapters) => {
38259
+ if (!this.#pluginLoadAbort.signal.aborted) this.#usageOnlyAdapters = [...adapters.values()];
38260
+ usageReady();
38261
+ },
37717
38262
  launchCommandForPlugin: (id2) => this.#launchSettings.initialCommand(id2),
37718
38263
  context: this.#options.pluginContext ?? {
37719
38264
  environment: this.#options.environment ?? process.env,
@@ -37734,6 +38279,7 @@ var AppServerHost = class {
37734
38279
  for (const [id2, adapter] of plugins.adapters) this.#externalAdapters.set(id2, adapter);
37735
38280
  }
37736
38281
  async #closeAdapters() {
38282
+ this.#observedSessionUsage.close();
37737
38283
  const results = await Promise.allSettled([
37738
38284
  this.#plugins?.close(),
37739
38285
  ...[...new Set(this.#options.externalAdapters?.values())].map(
@@ -37795,6 +38341,7 @@ var AppServerHost = class {
37795
38341
  await Promise.allSettled(threads.map(({ session }) => session.close()));
37796
38342
  await Promise.allSettled(threads.map(({ outputTask }) => outputTask));
37797
38343
  await this.#closeAdapters();
38344
+ await this.#externalRuntime.drain();
37798
38345
  for (const pending of [...this.#pendingDesktopApprovals.values()]) {
37799
38346
  await this.#resolveDesktopApproval(pending.interaction.interactionId).catch(
37800
38347
  () => void 0
@@ -37937,7 +38484,13 @@ var AppServerHost = class {
37937
38484
  if (request.method === "initialize") {
37938
38485
  try {
37939
38486
  const nativeGeneration = this.#officialRuntimeScope.gate.phase === "ready" ? this.#officialRuntimeScope.owner.generation : void 0;
37940
- const response = await this.#officialRuntime.initializeProtocol(requestObject(request));
38487
+ void this.#waitForPlugins();
38488
+ await this.#usagePluginsReady;
38489
+ const params = requestObject(request);
38490
+ const options2 = this.#observedSessionUsage.requestOptions("initialize", params);
38491
+ const nativeParams = options2 ? { ...params, ...options2 } : params;
38492
+ const response = await this.#officialRuntime.initializeProtocol(nativeParams);
38493
+ if (!isRecord18(response.error)) this.#observedSessionUsage.initialized(nativeParams);
37941
38494
  await this.#writer.json({ ...response, id: request.id });
37942
38495
  this.#nativeAccountObserver?.initialized(nativeGeneration);
37943
38496
  } catch (error51) {
@@ -38768,6 +39321,15 @@ var AppServerHost = class {
38768
39321
  return;
38769
39322
  }
38770
39323
  try {
39324
+ const options2 = isRecord18(request.params) ? this.#observedSessionUsage.requestOptions(request.method, request.params) : null;
39325
+ if (options2) {
39326
+ const original = parseJsonFrame(frame);
39327
+ if (isRecord18(original))
39328
+ frame = encodeJsonFrame({
39329
+ ...original,
39330
+ params: { ...isRecord18(original.params) ? original.params : {}, ...options2 }
39331
+ });
39332
+ }
38771
39333
  await this.#officialRuntime.sendFrame(frame);
38772
39334
  this.#markDesktopRequestAnswered(request.id);
38773
39335
  } catch {
@@ -38786,6 +39348,7 @@ var AppServerHost = class {
38786
39348
  return;
38787
39349
  }
38788
39350
  const parsed2 = input.value;
39351
+ const observedAtMs = Date.now();
38789
39352
  this.#observeOfficialTurnStartResponse(parsed2);
38790
39353
  let forwarded = parsed2;
38791
39354
  if (isRecord18(parsed2) && typeof parsed2.method === "string" && "id" in parsed2) {
@@ -38800,17 +39363,6 @@ var AppServerHost = class {
38800
39363
  if (accountScopedNotification) {
38801
39364
  if (parsed2.method === "account/updated") this.#officialRateLimits.reset(input.accountId);
38802
39365
  }
38803
- const tokenUsage = observeCodexTokenUsage(parsed2);
38804
- if (tokenUsage) {
38805
- const previous = this.#officialUsageByThread.get(tokenUsage.threadId);
38806
- try {
38807
- this.#officialUsageByThread.set(
38808
- tokenUsage.threadId,
38809
- parseHostUsage({ ...previous ?? {}, ...tokenUsage.usage })
38810
- );
38811
- } catch {
38812
- }
38813
- }
38814
39366
  const rateLimits = observeCodexRateLimits(parsed2);
38815
39367
  if (rateLimits) this.#officialRateLimits.observe(input.accountId, rateLimits);
38816
39368
  try {
@@ -38819,8 +39371,16 @@ var AppServerHost = class {
38819
39371
  this.#diagnose(error51);
38820
39372
  }
38821
39373
  this.#routeObservationTracker.bindOfficialResponse(parsed2);
38822
- if (forwarded === parsed2) await this.#writer.frame(input.frame);
38823
- else await this.#writer.json(forwarded);
39374
+ const privateTelemetry = isRecord18(parsed2) && typeof parsed2.method === "string" && !("id" in parsed2) && !this.#observedSessionUsage.shouldForwardNotification(parsed2);
39375
+ if (!privateTelemetry) {
39376
+ if (forwarded === parsed2) await this.#writer.frame(input.frame);
39377
+ else await this.#writer.json(forwarded);
39378
+ }
39379
+ void (this.#usagePluginsReady ?? Promise.resolve()).then(async () => {
39380
+ for (const threadId2 of this.#observedSessionUsage.observe(parsed2, observedAtMs)) {
39381
+ await this.#writer.json({ method: THREAD_USAGE_UPDATED_METHOD, params: { threadId: threadId2 } });
39382
+ }
39383
+ }).catch((error51) => this.#diagnose(error51));
38824
39384
  this.#nativeAccountObserver?.observe(parsed2);
38825
39385
  const deletedProjectId = observeDeletedProject(parsed2);
38826
39386
  if (deletedProjectId) {
@@ -39761,12 +40321,13 @@ var AppServerHost = class {
39761
40321
  );
39762
40322
  return;
39763
40323
  }
40324
+ await this.#waitForPlugins();
39764
40325
  const accountId = await this.#currentCodexAccountId();
39765
40326
  if (accountId) await this.#refreshOfficialRateLimits(accountId);
39766
40327
  const accountCredits = this.#officialAccountCredits(accountId ?? void 0);
39767
40328
  const result2 = threadUsageInspectionSchema.parse({
39768
40329
  threadId: params.data.threadId,
39769
- usage: this.#officialUsageByThread.get(params.data.threadId) ?? null,
40330
+ usage: await this.#observedSessionUsage.read(params.data.threadId),
39770
40331
  ...accountCredits ? { accountCredits } : {}
39771
40332
  });
39772
40333
  await this.#writer.json(rpcEnvelope(request, { result: jsonValueSchema.parse(result2) }));
@@ -39974,7 +40535,7 @@ var AppServerHost = class {
39974
40535
  this.#pendingExternalCommandRequests.delete(thread.id);
39975
40536
  }
39976
40537
  }
39977
- async #beginExternalCommand(thread, descriptor, arguments_2, requestedTurnId) {
40538
+ async #beginExternalCommand(thread, descriptor, arguments_2, requestedTurnId, sourceHostId) {
39978
40539
  const commands = thread.session.commands;
39979
40540
  if (!commands) {
39980
40541
  throw new ExternalCommandError(-32078, "External Harness does not expose commands");
@@ -40003,6 +40564,7 @@ var AppServerHost = class {
40003
40564
  thread.ephemeralTurnIds.add(turnId);
40004
40565
  if (descriptor.invocation === "/compact") this.#manualCompactionTurns.set(thread, turnId);
40005
40566
  else this.#manualCompactionTurns.delete(thread);
40567
+ const restoreExecutionHost = this.#setExecutionHost(thread, sourceHostId);
40006
40568
  try {
40007
40569
  if (thread.unsubmittedPrewarm && await this.#externalRuntime.submitPrewarm(thread)) {
40008
40570
  await this.#notifyExternalThreadStarted(thread.thread);
@@ -40015,6 +40577,7 @@ var AppServerHost = class {
40015
40577
  if (!result.ok) throw new ExternalCommandError(-32073, result.error.message);
40016
40578
  return { turnId: result.value.turnId, turn: projection.projector.pendingTurn(), gate };
40017
40579
  } catch (error51) {
40580
+ restoreExecutionHost();
40018
40581
  thread.running = false;
40019
40582
  thread.activeTurnId = null;
40020
40583
  thread.projectedTurns.delete(turnId);
@@ -40794,7 +41357,8 @@ var AppServerHost = class {
40794
41357
  thread,
40795
41358
  text,
40796
41359
  void 0,
40797
- typeof params.clientUserMessageId === "string" ? params.clientUserMessageId : void 0
41360
+ typeof params.clientUserMessageId === "string" ? params.clientUserMessageId : void 0,
41361
+ params[THREAD_REFERENCE_SOURCE_HOST_PARAM]
40798
41362
  );
40799
41363
  try {
40800
41364
  await this.#writer.json(rpcEnvelope(request, { result: { turn: started.turn } }));
@@ -40820,7 +41384,8 @@ var AppServerHost = class {
40820
41384
  thread,
40821
41385
  text,
40822
41386
  assertActive,
40823
- typeof requestObject(request).clientUserMessageId === "string" ? requestObject(request).clientUserMessageId : void 0
41387
+ typeof requestObject(request).clientUserMessageId === "string" ? requestObject(request).clientUserMessageId : void 0,
41388
+ requestObject(request)[THREAD_REFERENCE_SOURCE_HOST_PARAM]
40824
41389
  )
40825
41390
  );
40826
41391
  try {
@@ -40841,12 +41406,12 @@ var AppServerHost = class {
40841
41406
  }
40842
41407
  }
40843
41408
  /** Keep command restoration and dispatch identical for start and steer submissions. */
40844
- async #beginExternalInputTurn(thread, inputText, assertActive, clientUserMessageId) {
41409
+ async #beginExternalInputTurn(thread, inputText, assertActive, clientUserMessageId, sourceHostId) {
40845
41410
  const text = restoreHarnessCommandMentions(inputText);
40846
41411
  const commands = thread.session.commands;
40847
41412
  if (!commands || !isExternalCommandCandidate(text)) {
40848
41413
  assertActive?.();
40849
- return this.#beginExternalTurn(thread, text, clientUserMessageId);
41414
+ return this.#beginExternalTurn(thread, text, clientUserMessageId, sourceHostId);
40850
41415
  }
40851
41416
  this.#pendingExternalCommandRequests.add(thread.id);
40852
41417
  try {
@@ -40857,9 +41422,15 @@ var AppServerHost = class {
40857
41422
  assertActive?.();
40858
41423
  if (!command) {
40859
41424
  this.#pendingExternalCommandRequests.delete(thread.id);
40860
- return await this.#beginExternalTurn(thread, text, clientUserMessageId);
41425
+ return await this.#beginExternalTurn(thread, text, clientUserMessageId, sourceHostId);
40861
41426
  }
40862
- return await this.#beginExternalCommand(thread, command.descriptor, command.arguments);
41427
+ return await this.#beginExternalCommand(
41428
+ thread,
41429
+ command.descriptor,
41430
+ command.arguments,
41431
+ void 0,
41432
+ sourceHostId
41433
+ );
40863
41434
  } catch (error51) {
40864
41435
  if (error51 instanceof ExternalCommandError || error51 instanceof ExternalSteerError) throw error51;
40865
41436
  this.#diagnose(error51);
@@ -40871,7 +41442,20 @@ var AppServerHost = class {
40871
41442
  this.#pendingExternalCommandRequests.delete(thread.id);
40872
41443
  }
40873
41444
  }
40874
- async #beginExternalTurn(thread, text, clientUserMessageId) {
41445
+ /** Apply context before native execution; a rejected start restores the prior identity. */
41446
+ #setExecutionHost(thread, sourceHostId) {
41447
+ const previousHostId = this.#executionHosts.get(thread);
41448
+ if (typeof sourceHostId === "string" && sourceHostId.length > 0 && sourceHostId.length <= 1024 && !/[\u0000-\u0020\u007f]/u.test(sourceHostId)) {
41449
+ this.#executionHosts.set(thread, sourceHostId);
41450
+ } else {
41451
+ this.#executionHosts.delete(thread);
41452
+ }
41453
+ return () => {
41454
+ if (previousHostId === void 0) this.#executionHosts.delete(thread);
41455
+ else this.#executionHosts.set(thread, previousHostId);
41456
+ };
41457
+ }
41458
+ async #beginExternalTurn(thread, text, clientUserMessageId, sourceHostId) {
40875
41459
  if (this.#closeRequested || this.#externalRuntime.get(thread.id) !== thread) {
40876
41460
  throw new ExternalSteerError(-32073, "External Thread is no longer available");
40877
41461
  }
@@ -40898,6 +41482,7 @@ var AppServerHost = class {
40898
41482
  thread.projectedTerminalTurnId = null;
40899
41483
  thread.projectedTurns.set(turnId, projection);
40900
41484
  thread.responseGates.set(turnId, gate);
41485
+ const restoreExecutionHost = this.#setExecutionHost(thread, sourceHostId);
40901
41486
  try {
40902
41487
  if (thread.unsubmittedPrewarm && await this.#externalRuntime.submitPrewarm(thread)) {
40903
41488
  await this.#notifyExternalThreadStarted(thread.thread);
@@ -40910,6 +41495,7 @@ var AppServerHost = class {
40910
41495
  if (!result.ok) throw new ExternalSteerError(-32073, result.error.message);
40911
41496
  return { turnId, turn: projection.projector.pendingTurn(), gate };
40912
41497
  } catch (error51) {
41498
+ restoreExecutionHost();
40913
41499
  thread.running = false;
40914
41500
  thread.activeTurnId = null;
40915
41501
  thread.projectedTurns.delete(turnId);
@@ -41138,7 +41724,11 @@ var AppServerHost = class {
41138
41724
  if (event.type === "session.faulted") {
41139
41725
  this.#externalSteering.fault(thread.id, new Error(event.error.message));
41140
41726
  thread.stateObserver.fault(new Error(event.error.message));
41727
+ this.#externalRuntime.markSessionUnavailable(thread);
41141
41728
  this.#diagnose(`${thread.harnessId} Harness Session faulted: ${event.error.message}`);
41729
+ if (!thread.running && !thread.activeTurnId && !this.#hasRunningSubagents(thread.id) && !thread.session.hasBackgroundWork?.()) {
41730
+ void this.#externalRuntime.discardUnavailableIdle(thread);
41731
+ }
41142
41732
  return;
41143
41733
  }
41144
41734
  if (event.type === "turn.autonomous.started") {
@@ -41267,6 +41857,11 @@ var AppServerHost = class {
41267
41857
  return { ...subagent, subagentId: record4.hostThreadId };
41268
41858
  }
41269
41859
  async #refreshOpenSubagentThread(threadId2, terminal3 = true) {
41860
+ await this.#desktopRequests.run(threadId2, async () => {
41861
+ await this.#refreshOpenSubagentThreadBody(threadId2, terminal3);
41862
+ });
41863
+ }
41864
+ async #refreshOpenSubagentThreadBody(threadId2, terminal3) {
41270
41865
  const child = this.#externalRuntime.get(threadId2);
41271
41866
  if (!child) return;
41272
41867
  const previousItems = new Map(
@@ -41366,7 +41961,7 @@ var AppServerHost = class {
41366
41961
  }
41367
41962
  if (status2 === "idle" && previousStatus === "active") {
41368
41963
  for (const [index, waitMs] of SUBAGENT_TERMINAL_REFRESH_DELAYS_MS.entries()) {
41369
- if (waitMs > 0) await delay3(waitMs);
41964
+ if (waitMs > 0) await delay4(waitMs);
41370
41965
  await this.#refreshOpenSubagentThread(
41371
41966
  threadId2,
41372
41967
  index === SUBAGENT_TERMINAL_REFRESH_DELAYS_MS.length - 1
@@ -41744,7 +42339,7 @@ import { randomUUID as randomUUID15 } from "node:crypto";
41744
42339
  import { chmod as chmod3, lstat as lstat5, mkdir as mkdir12, open as open9, readFile as readFile16, readdir as readdir5, rename as rename11, rm as rm11 } from "node:fs/promises";
41745
42340
  import { uptime } from "node:os";
41746
42341
  import path26 from "node:path";
41747
- import { setTimeout as delay4 } from "node:timers/promises";
42342
+ import { setTimeout as delay5 } from "node:timers/promises";
41748
42343
  var LOCK_RETRY_COUNT = 200;
41749
42344
  var LOCK_RETRY_DELAY_MS = 25;
41750
42345
  var LOCK_MALFORMED_GRACE_MS = 1e3;
@@ -42000,7 +42595,7 @@ async function withRemoteAppServerSocketInitializationLock(socketPath, action) {
42000
42595
  return await action();
42001
42596
  }
42002
42597
  }
42003
- await delay4(LOCK_RETRY_DELAY_MS);
42598
+ await delay5(LOCK_RETRY_DELAY_MS);
42004
42599
  }
42005
42600
  throw new Error(
42006
42601
  `Remote app-server socket initialization is already in progress at ${socketPath}`
@@ -43291,15 +43886,28 @@ var DelegationControlRegistry = class {
43291
43886
  return (await this.#registrationForThread(input.threadId)).cancel(input);
43292
43887
  }
43293
43888
  async read(input) {
43294
- if (input.hostId !== void 0 && input.hostId !== "local") {
43295
- if (!input.hostId || !this.#remoteRead)
43889
+ const { callerThreadId: callerThreadId2, ...read } = input;
43890
+ if (read.hostId !== void 0 && read.hostId !== "local") {
43891
+ if (typeof callerThreadId2 === "string" && callerThreadId2) {
43892
+ const callers = await this.#matching(
43893
+ (registration) => registration.executionHostId?.(callerThreadId2) === read.hostId && registration.ownsThread(callerThreadId2)
43894
+ );
43895
+ if (callers.length > 1)
43896
+ only(callers, "Calling Thread has ambiguous execution Host context");
43897
+ if (callers.length === 1) {
43898
+ const { hostId: hostId2, ...local } = read;
43899
+ const snapshot2 = await (await this.#registrationForThread(local.threadId)).read(local);
43900
+ return { ...snapshot2, hostId: hostId2 };
43901
+ }
43902
+ }
43903
+ if (!read.hostId || !this.#remoteRead)
43296
43904
  throw new DelegationControlError(
43297
43905
  "RUNTIME_UNREACHABLE",
43298
43906
  "Remote Thread reading is unavailable"
43299
43907
  );
43300
- return this.#remoteRead(input);
43908
+ return this.#remoteRead(read);
43301
43909
  }
43302
- return (await this.#registrationForThread(input.threadId)).read(input);
43910
+ return (await this.#registrationForThread(read.threadId)).read(read);
43303
43911
  }
43304
43912
  async wait(input) {
43305
43913
  if (input.hostId !== void 0 && input.hostId !== "local")
@@ -44462,13 +45070,15 @@ function createHostUpdateCoordinator(options2) {
44462
45070
  return authenticated ?? fetchLatestGitHubRelease({ signal: requestSignal });
44463
45071
  });
44464
45072
  let candidate = null;
45073
+ let currentStatusPath = null;
44465
45074
  async function latestStatus(context) {
44466
- const discovered = await discoverLatestUpdateStatus(context.common.stateDirectory);
44467
- if (!discovered) return null;
44468
- if (discovered.status.phase !== "succeeded" && discovered.status.phase !== "failed" && !await isUpdateOperationActive(context.common.stateDirectory)) {
44469
- return null;
45075
+ if (!currentStatusPath) {
45076
+ const active = await discoverActiveUpdateStatus(context.common.stateDirectory);
45077
+ if (!active) return null;
45078
+ currentStatusPath = active.statusPath;
44470
45079
  }
44471
- return publicStatus(discovered.status);
45080
+ const status2 = await manager.readStatus(currentStatusPath);
45081
+ return status2 ? publicStatus(status2) : null;
44472
45082
  }
44473
45083
  async function installable(context, release) {
44474
45084
  if (context.installation.kind === "npm") return true;
@@ -44552,6 +45162,7 @@ function createHostUpdateCoordinator(options2) {
44552
45162
  if (existing) return { status: existing };
44553
45163
  throw new Error("Another update operation is already active");
44554
45164
  }
45165
+ currentStatusPath = null;
44555
45166
  let resolvePrepared;
44556
45167
  let rejectPrepared;
44557
45168
  const preparedReady = new Promise((resolve, reject) => {
@@ -44565,6 +45176,7 @@ function createHostUpdateCoordinator(options2) {
44565
45176
  }
44566
45177
  const onPrepared = async (info) => {
44567
45178
  await lock.setStatusPath(info.statusPath);
45179
+ currentStatusPath = info.statusPath;
44568
45180
  resolvePrepared(info);
44569
45181
  };
44570
45182
  const prepareAndStart = async () => {
@@ -44594,7 +45206,7 @@ function createHostUpdateCoordinator(options2) {
44594
45206
  onPrepared
44595
45207
  });
44596
45208
  }
44597
- if (platform !== "darwin") manager.start(prepared2);
45209
+ if (platform !== "darwin") await manager.start(prepared2);
44598
45210
  } catch (error51) {
44599
45211
  await lock.release();
44600
45212
  rejectPrepared(error51);
@@ -44631,6 +45243,20 @@ import { setImmediate as yieldToEventLoop } from "node:timers/promises";
44631
45243
  // packages/harness-adapter/dist/usage-statistics.js
44632
45244
  var MAX_SESSION_ID_LENGTH = 512;
44633
45245
  var MAX_CWD_LENGTH = 4096;
45246
+ var MAX_SESSION_TITLE_LENGTH = 200;
45247
+ function usageSessionTitle(value2) {
45248
+ if (typeof value2 !== "string")
45249
+ return void 0;
45250
+ const normalized = value2.replaceAll("\0", "").replaceAll(/\s+/gu, " ").trim();
45251
+ if (!normalized)
45252
+ return void 0;
45253
+ if (normalized.length <= MAX_SESSION_TITLE_LENGTH)
45254
+ return normalized;
45255
+ const characters = [...normalized];
45256
+ if (characters.length <= MAX_SESSION_TITLE_LENGTH)
45257
+ return normalized;
45258
+ return `${characters.slice(0, MAX_SESSION_TITLE_LENGTH - 1).join("").trimEnd()}…`;
45259
+ }
44634
45260
  function sessionIdOk(value2) {
44635
45261
  return typeof value2 === "string" && value2.length > 0 && value2.length <= MAX_SESSION_ID_LENGTH;
44636
45262
  }
@@ -44674,6 +45300,12 @@ function parseHarnessUsageEntry(value2) {
44674
45300
  return null;
44675
45301
  if (entry.cwd !== void 0 && !cwdOk(entry.cwd))
44676
45302
  return null;
45303
+ if (entry.sessionTitle !== void 0) {
45304
+ const sessionTitle = usageSessionTitle(entry.sessionTitle);
45305
+ if (sessionTitle === void 0)
45306
+ return null;
45307
+ entry.sessionTitle = sessionTitle;
45308
+ }
44677
45309
  if (entry.tokensUnknown !== void 0 && entry.tokensUnknown !== true)
44678
45310
  return null;
44679
45311
  if (entry.costUsd !== void 0 && (typeof entry.costUsd !== "number" || !Number.isFinite(entry.costUsd) || entry.costUsd < 0)) {
@@ -44685,8 +45317,31 @@ function parseHarnessUsageEntry(value2) {
44685
45317
  return entry;
44686
45318
  }
44687
45319
 
45320
+ // packages/host-runtime/src/usage-session-titles.ts
45321
+ function withHostSessionTitles(result, records) {
45322
+ const titles = /* @__PURE__ */ new Map();
45323
+ for (const record4 of records) {
45324
+ const ref = record4.nativeSessionRef;
45325
+ const title = usageSessionTitle(record4.title);
45326
+ if (record4.state !== "ready" || !ref || ref.harnessId !== record4.harnessId || !title) continue;
45327
+ const key = `${record4.harnessId}\0${ref.nativeSessionId}`;
45328
+ const previous = titles.get(key);
45329
+ if (!previous || record4.updatedAt > previous.updatedAt)
45330
+ titles.set(key, { title, updatedAt: record4.updatedAt });
45331
+ }
45332
+ const decorate = (sessions) => sessions.map((session) => {
45333
+ const title = titles.get(`${session.harness}\0${session.sessionId}`)?.title;
45334
+ return title ? { ...session, title } : session;
45335
+ });
45336
+ return {
45337
+ ...result,
45338
+ sessions: decorate(result.sessions),
45339
+ ...result.recentSessions ? { recentSessions: decorate(result.recentSessions) } : {}
45340
+ };
45341
+ }
45342
+
44688
45343
  // packages/host-runtime/src/usage-statistics.ts
44689
- var CACHE_VERSION = 6;
45344
+ var CACHE_VERSION = 7;
44690
45345
  var STALE_AFTER_MS = 1e4;
44691
45346
  var WARM_DELAY_MS = 3e3;
44692
45347
  var PERSIST_INTERVAL_MS = 6e4;
@@ -44706,7 +45361,8 @@ function compact(entry) {
44706
45361
  entry.cwd ?? null,
44707
45362
  entry.costUsd ?? null,
44708
45363
  entry.credits ?? null,
44709
- entry.tokensUnknown === true
45364
+ entry.tokensUnknown === true,
45365
+ entry.sessionTitle ?? null
44710
45366
  ];
44711
45367
  }
44712
45368
  function interner() {
@@ -44720,9 +45376,9 @@ function interner() {
44720
45376
  };
44721
45377
  }
44722
45378
  function expand(value2, intern) {
44723
- if (!Array.isArray(value2) || value2.length !== 14) return null;
45379
+ if (!Array.isArray(value2) || value2.length !== 15) return null;
44724
45380
  const [id2, occurredAtMs, model2, input, cached2, written, written1h, output, reasoning] = value2;
44725
- const [sessionId, cwd, costUsd, credits, tokensUnknown] = value2.slice(9);
45381
+ const [sessionId, cwd, costUsd, credits, tokensUnknown, sessionTitle] = value2.slice(9);
44726
45382
  return parseHarnessUsageEntry({
44727
45383
  id: id2,
44728
45384
  occurredAtMs,
@@ -44737,7 +45393,8 @@ function expand(value2, intern) {
44737
45393
  ...cwd !== null ? { cwd: intern(cwd) } : {},
44738
45394
  ...costUsd !== null ? { costUsd } : {},
44739
45395
  ...credits !== null ? { credits } : {},
44740
- ...tokensUnknown === true ? { tokensUnknown } : {}
45396
+ ...tokensUnknown === true ? { tokensUnknown } : {},
45397
+ ...sessionTitle !== null ? { sessionTitle: intern(sessionTitle) } : {}
44741
45398
  });
44742
45399
  }
44743
45400
  function message(error51) {
@@ -44828,6 +45485,7 @@ var UsageStatistics = class {
44828
45485
  #caches = /* @__PURE__ */ new Map();
44829
45486
  #failures = /* @__PURE__ */ new Map();
44830
45487
  #sources = null;
45488
+ #readThreadTitles;
44831
45489
  #loaded = null;
44832
45490
  #refreshing = null;
44833
45491
  #refreshedAtMs = 0;
@@ -44842,9 +45500,10 @@ var UsageStatistics = class {
44842
45500
  this.#options = options2;
44843
45501
  this.#directory = path36.join(options2.directory, `v${CACHE_VERSION}`);
44844
45502
  }
44845
- /** The Harnesses to read; call once their plugins are loaded. */
44846
- attach(sources) {
45503
+ /** Attach native storage and, optionally, fresh persisted Desktop title metadata. */
45504
+ attach(sources, readThreadTitles) {
44847
45505
  this.#sources = sources;
45506
+ this.#readThreadTitles = readThreadTitles;
44848
45507
  this.#scheduleWarm();
44849
45508
  }
44850
45509
  /** Reads every source in the background soon after the sources are attached. */
@@ -44871,11 +45530,19 @@ var UsageStatistics = class {
44871
45530
  }
44872
45531
  const now = this.#now();
44873
45532
  if (!this.#refreshing && now - this.#refreshedAtMs > STALE_AFTER_MS) this.#startRefresh();
44874
- return this.#aggregate(
45533
+ const result = this.#aggregate(
44875
45534
  typeof params === "string" ? { range: params } : params,
44876
45535
  now,
44877
45536
  await this.#options.prices.lookup()
44878
45537
  );
45538
+ if (this.#readThreadTitles && (result.sessions.length || result.recentSessions?.length)) {
45539
+ try {
45540
+ return withHostSessionTitles(result, await this.#readThreadTitles());
45541
+ } catch (error51) {
45542
+ this.#options.diagnose?.(`Usage statistics Thread titles unavailable: ${message(error51)}`);
45543
+ }
45544
+ }
45545
+ return result;
44879
45546
  }
44880
45547
  /** Completes the current refresh, for tests and callers that need a settled result. */
44881
45548
  async settled() {
@@ -45153,6 +45820,10 @@ var UsageStatistics = class {
45153
45820
  sessions.set(key, session);
45154
45821
  }
45155
45822
  session.project ??= project2;
45823
+ if (entry.sessionTitle && (session.titleAtMs === void 0 || entry.occurredAtMs >= session.titleAtMs)) {
45824
+ session.title = entry.sessionTitle;
45825
+ session.titleAtMs = entry.occurredAtMs;
45826
+ }
45156
45827
  if (model2 !== null) session.models.set(model2, (session.models.get(model2) ?? 0) + 1);
45157
45828
  session.firstAtMs = Math.min(session.firstAtMs, entry.occurredAtMs);
45158
45829
  session.lastAtMs = Math.max(session.lastAtMs, entry.occurredAtMs);
@@ -45160,21 +45831,26 @@ var UsageStatistics = class {
45160
45831
  }
45161
45832
  }
45162
45833
  const allSessions = [...sessions.values()];
45163
- const top = /* @__PURE__ */ new Set([
45164
- ...[...allSessions].sort((left, right) => right.totals.costUsd - left.totals.costUsd).slice(0, SESSIONS_PER_MEASURE),
45165
- ...[...allSessions].sort((left, right) => tokensWithoutCache(right.totals) - tokensWithoutCache(left.totals)).slice(0, SESSIONS_PER_MEASURE)
45166
- ]);
45167
- const sessionList = [...top].map((session) => ({
45834
+ const toSession = (session) => ({
45168
45835
  harness: session.harness,
45169
45836
  sessionId: session.sessionId,
45170
45837
  project: session.project,
45838
+ ...session.title ? { title: session.title } : {},
45171
45839
  models: [...session.models].sort((left, right) => right[1] - left[1]).slice(0, MODELS_PER_SESSION).map(([name]) => name),
45172
45840
  firstAtMs: session.firstAtMs,
45173
45841
  lastAtMs: session.lastAtMs,
45174
45842
  ...session.totals
45175
- })).sort(
45843
+ });
45844
+ const top = /* @__PURE__ */ new Set([
45845
+ ...[...allSessions].sort((left, right) => right.totals.costUsd - left.totals.costUsd).slice(0, SESSIONS_PER_MEASURE),
45846
+ ...[...allSessions].sort((left, right) => tokensWithoutCache(right.totals) - tokensWithoutCache(left.totals)).slice(0, SESSIONS_PER_MEASURE)
45847
+ ]);
45848
+ const sessionList = [...top].map(toSession).sort(
45176
45849
  (left, right) => right.costUsd - left.costUsd || tokensWithoutCache(right) - tokensWithoutCache(left)
45177
45850
  );
45851
+ const recentSessions = [...allSessions].sort(
45852
+ (left, right) => right.lastAtMs - left.lastAtMs || right.firstAtMs - left.firstAtMs || left.sessionId.localeCompare(right.sessionId)
45853
+ ).slice(0, SESSIONS_PER_MEASURE).map(toSession);
45178
45854
  if (unlistedModel) this.#options.prices.missing();
45179
45855
  const byName = (left, right) => left === right ? 0 : left === null ? 1 : right === null ? -1 : left.localeCompare(right);
45180
45856
  return {
@@ -45219,7 +45895,8 @@ var UsageStatistics = class {
45219
45895
  byHarness: grouped(byHarness, (name) => ({ harness: name })),
45220
45896
  byModel: grouped(byModel, (name) => ({ model: name })).slice(0, 4096).map((row) => ({ ...row, harnessPricedRequests: harnessPriced.get(row.model) ?? 0 })),
45221
45897
  byProject: grouped(byProject, (name) => ({ project: name })).slice(0, 4096),
45222
- sessions: sessionList
45898
+ sessions: sessionList,
45899
+ recentSessions
45223
45900
  };
45224
45901
  }
45225
45902
  };
@@ -45254,9 +45931,21 @@ function requiredRuntimeConfiguration(environment) {
45254
45931
  if (!stockCodexPath) throw new Error(`${STOCK_CODEX_PATH_ENV} is required`);
45255
45932
  return { stockCodexPath };
45256
45933
  }
45257
- function delegationCliEnvironment(environment) {
45258
- const cliPath = environment[DELEGATION_CLI_PATH_ENV] ?? environment[UPDATE_RUNTIME_ENV.launcherExecutable];
45259
- const nodePath2 = environment[UPDATE_RUNTIME_ENV.npmNodePath];
45934
+ function delegationCliEnvironment(environment, hostRuntimePath) {
45935
+ let packagedCli;
45936
+ if (hostRuntimePath && path37.isAbsolute(hostRuntimePath)) {
45937
+ const candidate = path37.resolve(
45938
+ path37.dirname(hostRuntimePath),
45939
+ "../bin",
45940
+ process.platform === "win32" ? "codexhost.exe" : "codexhost"
45941
+ );
45942
+ try {
45943
+ if (statSync(candidate).isFile()) packagedCli = candidate;
45944
+ } catch {
45945
+ }
45946
+ }
45947
+ const cliPath = environment[DELEGATION_CLI_PATH_ENV] ?? environment[UPDATE_RUNTIME_ENV.launcherExecutable] ?? packagedCli;
45948
+ const nodePath2 = environment[DELEGATION_CLI_NODE_PATH_ENV] ?? environment[UPDATE_RUNTIME_ENV.npmNodePath] ?? (packagedCli && cliPath === packagedCli ? process.execPath : void 0);
45260
45949
  return {
45261
45950
  ...cliPath ? { [DELEGATION_CLI_PATH_ENV]: cliPath } : {},
45262
45951
  ...nodePath2 && path37.isAbsolute(nodePath2) ? { [DELEGATION_CLI_NODE_PATH_ENV]: nodePath2 } : {}
@@ -45274,7 +45963,7 @@ async function prepareDelegationRuntime(input) {
45274
45963
  const server = await startDelegationControlServer({ token, api: registry2, watchApi: registry2 });
45275
45964
  const environment = {
45276
45965
  ...input.environment,
45277
- ...delegationCliEnvironment(input.environment),
45966
+ ...delegationCliEnvironment(input.environment, input.hostRuntimePath),
45278
45967
  [DELEGATION_RUNTIME_ENDPOINT_ENV]: server.endpoint,
45279
45968
  [DELEGATION_RUNTIME_TOKEN_ENV]: token
45280
45969
  };
@@ -45349,6 +46038,7 @@ async function runHostRuntime(input) {
45349
46038
  const environment = remoteControlPlan?.environment ?? input.environment;
45350
46039
  return prepareDelegationRuntime({
45351
46040
  environment,
46041
+ ...hostRuntimePath ? { hostRuntimePath } : {},
45352
46042
  createHost: async (delegationEnvironment, onDelegationApi, registry2) => {
45353
46043
  const official = await prepareLocalCodex({
45354
46044
  stockCodexPath,
@@ -45457,6 +46147,7 @@ async function runHostRuntime(input) {
45457
46147
  if (!listenUrl) throw new Error("Remote app-server listener URL is unavailable");
45458
46148
  return prepareDelegationRuntime({
45459
46149
  environment: input.environment,
46150
+ ...hostRuntimePath ? { hostRuntimePath } : {},
45460
46151
  createHost: async (delegationEnvironment, _onDelegationApi, registry2) => {
45461
46152
  const socketPath = remoteAppServerSocketPath(delegationEnvironment, listenUrl);
45462
46153
  const officialPlan = createRemoteOfficialAppServerPlan(input.arguments, socketPath);
@@ -47690,7 +48381,7 @@ import {
47690
48381
  readdirSync,
47691
48382
  renameSync,
47692
48383
  rmSync,
47693
- statSync,
48384
+ statSync as statSync2,
47694
48385
  writeFileSync
47695
48386
  } from "node:fs";
47696
48387
  import os12 from "node:os";
@@ -47720,7 +48411,7 @@ function pruneRuntimeLogs(input) {
47720
48411
  if (!Number.isSafeInteger(pid)) continue;
47721
48412
  try {
47722
48413
  const filePath = path42.join(input.directory, entry.name);
47723
- const stats = statSync(filePath);
48414
+ const stats = statSync2(filePath);
47724
48415
  files.push({
47725
48416
  filePath,
47726
48417
  pid,