@codexhost/cli-darwin-x64 0.13.0 → 0.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -14676,6 +14676,8 @@ var usageStatisticsSessionSchema = external_exports.strictObject({
14676
14676
  harness,
14677
14677
  sessionId: external_exports.string().min(1).max(512),
14678
14678
  project: project.nullable(),
14679
+ /** Persisted Desktop title for a mapped Session, otherwise its native name; may be absent. */
14680
+ title: external_exports.string().min(1).max(200).optional(),
14679
14681
  /** Models the session used, most requests first. */
14680
14682
  models: external_exports.array(model).max(16),
14681
14683
  firstAtMs: count,
@@ -14742,7 +14744,12 @@ var usageStatisticsResultSchema = external_exports.strictObject({
14742
14744
  * The sessions that spent the most, by cost and by tokens without cache, all filters applied;
14743
14745
  * requests without a native session are not listed here but count everywhere else.
14744
14746
  */
14745
- sessions: external_exports.array(usageStatisticsSessionSchema).max(100)
14747
+ sessions: external_exports.array(usageStatisticsSessionSchema).max(100),
14748
+ /**
14749
+ * The sessions last active in the range, newest first, all filters applied. Optional for older
14750
+ * Hosts; missing is treated as no list rather than no sessions.
14751
+ */
14752
+ recentSessions: external_exports.array(usageStatisticsSessionSchema).max(100).optional()
14746
14753
  });
14747
14754
 
14748
14755
  // ../../shared-contracts/dist/ids.js
@@ -15862,8 +15869,24 @@ import path from "node:path";
15862
15869
  import { setImmediate as yieldToEventLoop } from "node:timers/promises";
15863
15870
 
15864
15871
  // ../../harness-adapter/dist/usage-statistics.js
15872
+ import { createReadStream } from "node:fs";
15873
+ import { createInterface } from "node:readline";
15865
15874
  var MAX_SESSION_ID_LENGTH = 512;
15866
15875
  var MAX_CWD_LENGTH = 4096;
15876
+ var MAX_SESSION_TITLE_LENGTH = 200;
15877
+ function usageSessionTitle(value) {
15878
+ if (typeof value !== "string")
15879
+ return void 0;
15880
+ const normalized = value.replaceAll("\0", "").replaceAll(/\s+/gu, " ").trim();
15881
+ if (!normalized)
15882
+ return void 0;
15883
+ if (normalized.length <= MAX_SESSION_TITLE_LENGTH)
15884
+ return normalized;
15885
+ const characters = [...normalized];
15886
+ if (characters.length <= MAX_SESSION_TITLE_LENGTH)
15887
+ return normalized;
15888
+ return `${characters.slice(0, MAX_SESSION_TITLE_LENGTH - 1).join("").trimEnd()}…`;
15889
+ }
15867
15890
  function sessionIdOk(value) {
15868
15891
  return typeof value === "string" && value.length > 0 && value.length <= MAX_SESSION_ID_LENGTH;
15869
15892
  }
@@ -15873,12 +15896,14 @@ function cwdOk(value) {
15873
15896
  function withUsageSession(entry, session) {
15874
15897
  const sessionId = sessionIdOk(session.sessionId) ? session.sessionId : void 0;
15875
15898
  const cwd = cwdOk(session.cwd) ? session.cwd : void 0;
15899
+ const sessionTitle = sessionId !== void 0 ? usageSessionTitle(session.title) : void 0;
15876
15900
  if (sessionId === void 0 && cwd === void 0)
15877
15901
  return entry;
15878
15902
  return {
15879
15903
  ...entry,
15880
15904
  ...sessionId !== void 0 ? { sessionId } : {},
15881
- ...cwd !== void 0 ? { cwd } : {}
15905
+ ...cwd !== void 0 ? { cwd } : {},
15906
+ ...sessionTitle !== void 0 ? { sessionTitle } : {}
15882
15907
  };
15883
15908
  }
15884
15909
  function tokenCount(value) {
@@ -15918,6 +15943,12 @@ function parseHarnessUsageEntry(value) {
15918
15943
  return null;
15919
15944
  if (entry.cwd !== void 0 && !cwdOk(entry.cwd))
15920
15945
  return null;
15946
+ if (entry.sessionTitle !== void 0) {
15947
+ const sessionTitle = usageSessionTitle(entry.sessionTitle);
15948
+ if (sessionTitle === void 0)
15949
+ return null;
15950
+ entry.sessionTitle = sessionTitle;
15951
+ }
15921
15952
  if (entry.tokensUnknown !== void 0 && entry.tokensUnknown !== true)
15922
15953
  return null;
15923
15954
  if (entry.costUsd !== void 0 && (typeof entry.costUsd !== "number" || !Number.isFinite(entry.costUsd) || entry.costUsd < 0)) {
@@ -15928,6 +15959,25 @@ function parseHarnessUsageEntry(value) {
15928
15959
  }
15929
15960
  return entry;
15930
15961
  }
15962
+ async function* jsonlRecords(file2, marker, signal) {
15963
+ const lines = createInterface({
15964
+ input: createReadStream(file2, { encoding: "utf8", signal }),
15965
+ crlfDelay: Infinity
15966
+ });
15967
+ for await (const line of lines) {
15968
+ if (!line.includes(marker))
15969
+ continue;
15970
+ let value;
15971
+ try {
15972
+ value = JSON.parse(line);
15973
+ } catch {
15974
+ continue;
15975
+ }
15976
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) {
15977
+ yield value;
15978
+ }
15979
+ }
15980
+ }
15931
15981
  function nativeTimeMs(value) {
15932
15982
  const time3 = typeof value === "number" ? value : typeof value === "string" ? Date.parse(value) : Number.NaN;
15933
15983
  return Number.isFinite(time3) && time3 > 0 ? Math.floor(time3) : null;
@@ -16336,7 +16386,7 @@ var CodexCounters = class {
16336
16386
  };
16337
16387
 
16338
16388
  // dist/rollout-io.js
16339
- import { createReadStream } from "node:fs";
16389
+ import { createReadStream as createReadStream2 } from "node:fs";
16340
16390
  import { StringDecoder } from "node:string_decoder";
16341
16391
  import { promisify } from "node:util";
16342
16392
  import { zstdDecompress } from "node:zlib";
@@ -16401,7 +16451,7 @@ function frameSize(data) {
16401
16451
  }
16402
16452
  async function* rolloutBytes(file2, signal) {
16403
16453
  signal.throwIfAborted();
16404
- const stream = createReadStream(file2, { signal });
16454
+ const stream = createReadStream2(file2, { signal });
16405
16455
  let pending = Buffer.alloc(0);
16406
16456
  try {
16407
16457
  for await (const value of stream) {
@@ -16499,18 +16549,61 @@ async function stamp(file2) {
16499
16549
  throw new Error("Codex rollout is not a regular file");
16500
16550
  return `${s.dev}:${s.ino}:${s.size}:${s.mtimeNs}:${s.ctimeNs}`;
16501
16551
  }
16502
- function fingerprint(files) {
16503
- return createHash("sha256").update(JSON.stringify(files)).digest("hex");
16552
+ function fingerprint(files, extra = "") {
16553
+ return createHash("sha256").update(JSON.stringify(files)).update(extra).digest("hex");
16554
+ }
16555
+ function typedPrompt(value) {
16556
+ if (typeof value !== "string")
16557
+ return void 0;
16558
+ const text2 = value.trim();
16559
+ if (!text2 || text2.startsWith("<") || text2.startsWith("# AGENTS.md") || text2.startsWith("<!--")) {
16560
+ return void 0;
16561
+ }
16562
+ return usageSessionTitle(text2);
16563
+ }
16564
+ function userItemPrompt(payload) {
16565
+ if (!payload || payload.type !== "message" || payload.role !== "user")
16566
+ return void 0;
16567
+ if (!Array.isArray(payload.content))
16568
+ return typedPrompt(payload.content);
16569
+ for (const part of payload.content) {
16570
+ if (typeof part !== "object" || part === null)
16571
+ continue;
16572
+ const block = part;
16573
+ if (block.type === "input_text" || block.type === "text") {
16574
+ const prompt = typedPrompt(block.text);
16575
+ if (prompt)
16576
+ return prompt;
16577
+ }
16578
+ }
16579
+ return void 0;
16580
+ }
16581
+ async function readThreadNames(home, signal) {
16582
+ const names = /* @__PURE__ */ new Map();
16583
+ try {
16584
+ for await (const line of jsonlRecords(path.join(home, "session_index.jsonl"), '"thread_name"', signal)) {
16585
+ const id = text(line.id);
16586
+ const name = usageSessionTitle(line.thread_name);
16587
+ if (id && name)
16588
+ names.set(id, name);
16589
+ }
16590
+ } catch (error51) {
16591
+ if (error51.code !== "ENOENT")
16592
+ throw error51;
16593
+ }
16594
+ return names;
16504
16595
  }
16505
- async function readCodexRollouts(files, thread, signal) {
16596
+ async function readCodexRollouts(files, thread, signal, options = {}) {
16506
16597
  const counters = new CodexCounters();
16507
- let model2 = "", cwd = "", ownFrom = null, firstMeta = false, forkAt = 0, seen = 0;
16598
+ let model2 = "", cwd = "", ownFrom = null, firstMeta = false, forkAt = 0, seen = 0, firstPrompt;
16508
16599
  for (const file2 of files) {
16509
16600
  for await (const line of rolloutLines(file2, signal)) {
16510
16601
  signal.throwIfAborted();
16511
16602
  if (++seen % 128 === 0)
16512
16603
  await yieldToEventLoop(void 0, { signal });
16513
16604
  const kind = /"type"\s*:\s*"([^"]+)"/u.exec(line.slice(0, 1024))?.[1];
16605
+ if (!kind && line.trim())
16606
+ options.onIncomplete?.();
16514
16607
  if (!kind || ![
16515
16608
  "session_meta",
16516
16609
  "turn_context",
@@ -16528,19 +16621,24 @@ async function readCodexRollouts(files, thread, signal) {
16528
16621
  try {
16529
16622
  row = object2(JSON.parse(line));
16530
16623
  } catch {
16624
+ options.onIncomplete?.();
16531
16625
  continue;
16532
16626
  }
16533
16627
  const p = object2(row?.payload);
16534
16628
  const at = nativeTimeMs(row?.timestamp);
16535
- if (!row || !p || at === null)
16629
+ if (!row || !p || at === null) {
16630
+ options.onIncomplete?.();
16536
16631
  continue;
16632
+ }
16537
16633
  switch (row.type) {
16538
16634
  case "session_meta": {
16539
16635
  if (!firstMeta) {
16540
16636
  if (text(p.id) !== thread)
16541
16637
  throw new Error("Codex rollout identity does not match its source");
16542
- if (p.forked_from_id || p.parent_thread_id || p.subagent_history_start_ordinal)
16638
+ if (p.forked_from_id || p.parent_thread_id || p.subagent_history_start_ordinal) {
16543
16639
  forkAt = nativeTimeMs(p.timestamp) ?? at;
16640
+ options.onIncomplete?.();
16641
+ }
16544
16642
  cwd = text(p.cwd);
16545
16643
  firstMeta = true;
16546
16644
  }
@@ -16554,30 +16652,41 @@ async function readCodexRollouts(files, thread, signal) {
16554
16652
  counters.context("", text(p.turn_id), model2);
16555
16653
  break;
16556
16654
  case "token_usage_record":
16655
+ if (!usage(p.usage) || !text(p.response_id))
16656
+ options.onIncomplete?.();
16557
16657
  counters.record(at, model2, p);
16558
16658
  break;
16559
16659
  case "compacted": {
16560
16660
  const record2 = object2(p.latest_token_usage_record);
16561
- if (record2 && p.compaction_response_id && record2.response_id === p.compaction_response_id)
16661
+ if (record2 && p.compaction_response_id && record2.response_id === p.compaction_response_id) {
16662
+ if (!usage(record2.usage))
16663
+ options.onIncomplete?.();
16562
16664
  counters.record(at, model2, record2, true);
16665
+ }
16563
16666
  break;
16564
16667
  }
16565
16668
  case "response_item":
16566
16669
  counters.boundary();
16670
+ firstPrompt ??= userItemPrompt(p);
16567
16671
  break;
16568
16672
  case "event_msg":
16569
16673
  if (p.type === "task_started") {
16570
16674
  counters.context("", text(p.turn_id), "");
16571
16675
  counters.boundary();
16572
16676
  }
16573
- if (p.type === "user_message")
16677
+ if (p.type === "user_message") {
16574
16678
  counters.boundary();
16679
+ firstPrompt ??= typedPrompt(p.message);
16680
+ }
16575
16681
  if (p.type === "thread_settings_applied") {
16576
16682
  model2 = text(object2(p.thread_settings)?.model) || model2;
16577
16683
  counters.context("", "", model2);
16578
16684
  }
16579
16685
  if (p.type === "token_count") {
16580
16686
  const info = object2(p.info);
16687
+ options.onTokenCount?.(info);
16688
+ if (info && !usage(info.total_token_usage))
16689
+ options.onIncomplete?.();
16581
16690
  if (info)
16582
16691
  counters.count(at, model2, usage(info.total_token_usage), usage(info.last_token_usage));
16583
16692
  }
@@ -16604,12 +16713,22 @@ async function readCodexRollouts(files, thread, signal) {
16604
16713
  ...v.usage.known & 8 ? { cacheWriteInputTokens: written } : {},
16605
16714
  ...v.usage.known & 16 ? { reasoningOutputTokens: reasoning } : {}
16606
16715
  });
16607
- return entry ? [withUsageSession(entry, { sessionId: thread, cwd })] : [];
16716
+ return entry ? [withUsageSession(entry, { sessionId: thread, cwd, title: firstPrompt })] : [];
16608
16717
  });
16609
16718
  }
16610
- function createCodexUsageStatistics(environment) {
16719
+ function createCodexUsageStatistics(environment, options = {}) {
16611
16720
  const home = path.resolve(environment.CODEX_HOME || path.join(environment.HOME || environment.USERPROFILE || os.homedir(), ".codex"));
16612
16721
  const groups = /* @__PURE__ */ new Map();
16722
+ let names = /* @__PURE__ */ new Map();
16723
+ let namesStamp = "";
16724
+ async function threadNames(signal) {
16725
+ const current = await stamp(path.join(home, "session_index.jsonl")).catch(() => "-");
16726
+ if (namesStamp === current)
16727
+ return names;
16728
+ names = await readThreadNames(home, signal);
16729
+ namesStamp = current;
16730
+ return names;
16731
+ }
16613
16732
  return {
16614
16733
  async listSources(signal) {
16615
16734
  const found = /* @__PURE__ */ new Map();
@@ -16628,7 +16747,7 @@ function createCodexUsageStatistics(environment) {
16628
16747
  if (!entry.isFile())
16629
16748
  continue;
16630
16749
  const thread = ROLLOUT.exec(entry.name)?.[1];
16631
- if (!thread)
16750
+ if (!thread || options.sessionId && thread !== options.sessionId)
16632
16751
  continue;
16633
16752
  const list = found.get(thread) ?? [];
16634
16753
  list.push({ file: file2, fingerprint: await stamp(file2) });
@@ -16637,13 +16756,14 @@ function createCodexUsageStatistics(environment) {
16637
16756
  }
16638
16757
  await visit(path.join(home, "sessions"), 0);
16639
16758
  await visit(path.join(home, "archived_sessions"), 0);
16759
+ const indexStamp = options.sessionId ? "" : await stamp(path.join(home, "session_index.jsonl")).catch(() => "-");
16640
16760
  const result = [];
16641
16761
  groups.clear();
16642
16762
  for (const [thread, files] of found) {
16643
16763
  files.sort((a, b) => path.basename(a.file).localeCompare(path.basename(b.file)) || a.file.localeCompare(b.file));
16644
16764
  const id = path.join(home, `usage-thread-${thread}`);
16645
16765
  groups.set(id, { thread, files });
16646
- result.push({ id, fingerprint: fingerprint(files) });
16766
+ result.push({ id, fingerprint: fingerprint(files, indexStamp) });
16647
16767
  }
16648
16768
  return result;
16649
16769
  },
@@ -16674,11 +16794,511 @@ function createCodexUsageStatistics(environment) {
16674
16794
  if (!duplicate)
16675
16795
  kept.push(candidate);
16676
16796
  }
16677
- const entries = await readCodexRollouts(kept.map(({ file: file2 }) => file2), group.thread, signal);
16797
+ const entries = await readCodexRollouts(kept.map(({ file: file2 }) => file2), group.thread, signal, options);
16678
16798
  for (const file2 of group.files)
16679
16799
  if (await stamp(file2.file) !== file2.fingerprint)
16680
16800
  throw new Error("Codex rollout changed while reading; refresh to retry");
16681
- return entries;
16801
+ const named = options.sessionId ? void 0 : (await threadNames(signal)).get(group.thread);
16802
+ return named ? entries.map((entry) => withUsageSession(entry, {
16803
+ sessionId: entry.sessionId,
16804
+ cwd: entry.cwd,
16805
+ title: named
16806
+ })) : entries;
16807
+ }
16808
+ };
16809
+ }
16810
+
16811
+ // dist/session-usage.js
16812
+ import { setTimeout as delay } from "node:timers/promises";
16813
+
16814
+ // ../../harness-adapter/dist/usage.js
16815
+ var tokenFields = [
16816
+ "inputTokens",
16817
+ "cachedInputTokens",
16818
+ "cacheWriteInputTokens",
16819
+ "outputTokens",
16820
+ "reasoningOutputTokens",
16821
+ "totalTokens",
16822
+ "contextWindowTokens",
16823
+ "contextUsedTokens"
16824
+ ];
16825
+ var safeIntegerFields = [
16826
+ "planFiveHourResetsAtUnix",
16827
+ "planSevenDayResetsAtUnix",
16828
+ "timeToFirstOutputMs"
16829
+ ];
16830
+ var percentFields = [
16831
+ "cacheHitRatePercent",
16832
+ "sessionCacheHitRatePercent",
16833
+ "planFiveHourUsedPercent",
16834
+ "planSevenDayUsedPercent"
16835
+ ];
16836
+ var usageFields = /* @__PURE__ */ new Set([
16837
+ ...tokenFields,
16838
+ ...safeIntegerFields,
16839
+ ...percentFields,
16840
+ "sessionCacheUsage",
16841
+ "totalCostUsd",
16842
+ "totalCredits",
16843
+ "contextUsagePercent",
16844
+ "outputTokensPerSecond",
16845
+ "apiOutputTokensPerSecond",
16846
+ "costSource",
16847
+ "unpricedModels"
16848
+ ]);
16849
+ function isRecord(value) {
16850
+ return typeof value === "object" && value !== null && !Array.isArray(value);
16851
+ }
16852
+ function parseHostUsage(value) {
16853
+ if (!isRecord(value))
16854
+ throw new Error("Harness Usage must be an object");
16855
+ const keys = Object.keys(value);
16856
+ if (keys.length === 0)
16857
+ throw new Error("Harness Usage must contain a reliable field");
16858
+ for (const key2 of keys) {
16859
+ if (!usageFields.has(key2)) {
16860
+ throw new Error(`Harness Usage contains unknown field '${key2}'`);
16861
+ }
16862
+ }
16863
+ for (const field of [
16864
+ "totalCredits",
16865
+ "contextUsagePercent",
16866
+ "apiOutputTokensPerSecond"
16867
+ ]) {
16868
+ const candidate = value[field];
16869
+ if (candidate !== void 0 && (typeof candidate !== "number" || !Number.isFinite(candidate) || candidate < 0)) {
16870
+ throw new Error(`Harness Usage '${field}' must be a finite non-negative number`);
16871
+ }
16872
+ }
16873
+ for (const field of tokenFields) {
16874
+ const candidate = value[field];
16875
+ if (candidate !== void 0 && (typeof candidate !== "number" || !Number.isSafeInteger(candidate) || candidate < 0)) {
16876
+ throw new Error(`Harness Usage '${field}' must be a non-negative safe integer`);
16877
+ }
16878
+ }
16879
+ for (const field of safeIntegerFields) {
16880
+ const candidate = value[field];
16881
+ if (candidate !== void 0 && (typeof candidate !== "number" || !Number.isSafeInteger(candidate) || candidate < 0)) {
16882
+ throw new Error(`Harness Usage '${field}' must be a non-negative safe integer`);
16883
+ }
16884
+ }
16885
+ if (value.outputTokensPerSecond !== void 0 && (typeof value.outputTokensPerSecond !== "number" || !Number.isFinite(value.outputTokensPerSecond) || value.outputTokensPerSecond < 0)) {
16886
+ throw new Error("Harness Usage 'outputTokensPerSecond' must be a finite non-negative number");
16887
+ }
16888
+ if (value.totalCostUsd !== void 0 && (typeof value.totalCostUsd !== "number" || !Number.isFinite(value.totalCostUsd) || value.totalCostUsd < 0)) {
16889
+ throw new Error("Harness Usage 'totalCostUsd' must be a finite non-negative number");
16890
+ }
16891
+ for (const field of percentFields) {
16892
+ const candidate = value[field];
16893
+ if (candidate !== void 0 && (typeof candidate !== "number" || !Number.isFinite(candidate) || candidate < 0 || candidate > 100)) {
16894
+ throw new Error(`Harness Usage '${field}' must be between 0 and 100`);
16895
+ }
16896
+ }
16897
+ if (value.sessionCacheUsage !== void 0) {
16898
+ const totals2 = value.sessionCacheUsage;
16899
+ if (!isRecord(totals2) || Object.keys(totals2).some((key2) => key2 !== "inputTokens" && key2 !== "cachedInputTokens") || typeof totals2.inputTokens !== "number" || !Number.isSafeInteger(totals2.inputTokens) || totals2.inputTokens < 0 || typeof totals2.cachedInputTokens !== "number" || !Number.isSafeInteger(totals2.cachedInputTokens) || totals2.cachedInputTokens < 0 || totals2.cachedInputTokens > totals2.inputTokens) {
16900
+ throw new Error("Harness Usage 'sessionCacheUsage' must contain complete bounded token totals");
16901
+ }
16902
+ }
16903
+ const hasContextUsed = value.contextUsedTokens !== void 0;
16904
+ const hasContextWindow = value.contextWindowTokens !== void 0;
16905
+ if (hasContextUsed !== hasContextWindow) {
16906
+ throw new Error("Harness Usage context fields must be provided together");
16907
+ }
16908
+ if (hasContextWindow && value.contextWindowTokens === 0) {
16909
+ throw new Error("Harness Usage 'contextWindowTokens' must be greater than zero");
16910
+ }
16911
+ if (value.planFiveHourResetsAtUnix !== void 0 && value.planFiveHourUsedPercent === void 0) {
16912
+ throw new Error("Harness Usage 'planFiveHourResetsAtUnix' must be provided with 'planFiveHourUsedPercent'");
16913
+ }
16914
+ if (value.planSevenDayResetsAtUnix !== void 0 && value.planSevenDayUsedPercent === void 0) {
16915
+ throw new Error("Harness Usage 'planSevenDayResetsAtUnix' must be provided with 'planSevenDayUsedPercent'");
16916
+ }
16917
+ if (value.costSource !== void 0) {
16918
+ if (value.costSource !== "publicPrice" && value.costSource !== "native") {
16919
+ throw new Error("Harness Usage 'costSource' must be 'publicPrice' or 'native'");
16920
+ }
16921
+ if (value.totalCostUsd === void 0) {
16922
+ throw new Error("Harness Usage 'costSource' must be provided with 'totalCostUsd'");
16923
+ }
16924
+ }
16925
+ if (value.unpricedModels !== void 0) {
16926
+ const models = value.unpricedModels;
16927
+ if (!Array.isArray(models) || models.length === 0 || !models.every((model2) => typeof model2 === "string" && model2.length > 0)) {
16928
+ throw new Error("Harness Usage 'unpricedModels' must be a non-empty list of Model IDs");
16929
+ }
16930
+ if (value.costSource !== "publicPrice") {
16931
+ throw new Error("Harness Usage 'unpricedModels' must be provided with a 'publicPrice' cost");
16932
+ }
16933
+ return { ...value, unpricedModels: [...models] };
16934
+ }
16935
+ return { ...value };
16936
+ }
16937
+ var requestTokenFields = [
16938
+ "inputTokens",
16939
+ "cachedInputTokens",
16940
+ "cacheWriteInputTokens",
16941
+ "cacheWrite1hInputTokens",
16942
+ "outputTokens",
16943
+ "reasoningOutputTokens",
16944
+ "startedAtMs",
16945
+ "completedAtMs"
16946
+ ];
16947
+ var requestFields = /* @__PURE__ */ new Set([
16948
+ ...requestTokenFields,
16949
+ "requestId",
16950
+ "historical",
16951
+ "model",
16952
+ "provider"
16953
+ ]);
16954
+ function parseHostUsageRequest(value) {
16955
+ if (!isRecord(value))
16956
+ throw new Error("Harness Usage request must be an object");
16957
+ for (const key2 of Object.keys(value)) {
16958
+ if (!requestFields.has(key2)) {
16959
+ throw new Error(`Harness Usage request contains unknown field '${key2}'`);
16960
+ }
16961
+ }
16962
+ if (typeof value.requestId !== "string" || value.requestId.length === 0) {
16963
+ throw new Error("Harness Usage request 'requestId' must be a non-empty string");
16964
+ }
16965
+ for (const field of ["model", "provider"]) {
16966
+ const candidate = value[field];
16967
+ if (candidate !== void 0 && (typeof candidate !== "string" || candidate.length === 0)) {
16968
+ throw new Error(`Harness Usage request '${field}' must be a non-empty string`);
16969
+ }
16970
+ }
16971
+ if (value.historical !== void 0 && typeof value.historical !== "boolean") {
16972
+ throw new Error("Harness Usage request 'historical' must be a boolean");
16973
+ }
16974
+ for (const field of requestTokenFields) {
16975
+ const candidate = value[field];
16976
+ if (candidate !== void 0 && (typeof candidate !== "number" || !Number.isSafeInteger(candidate) || candidate < 0)) {
16977
+ throw new Error(`Harness Usage request '${field}' must be a non-negative safe integer`);
16978
+ }
16979
+ }
16980
+ if (value.inputTokens === void 0 || value.outputTokens === void 0) {
16981
+ throw new Error("Harness Usage request must contain 'inputTokens' and 'outputTokens'");
16982
+ }
16983
+ const request = value;
16984
+ if ((request.cachedInputTokens ?? 0) + (request.cacheWriteInputTokens ?? 0) > request.inputTokens) {
16985
+ throw new Error("Harness Usage request cache Tokens must not exceed 'inputTokens'");
16986
+ }
16987
+ if ((request.reasoningOutputTokens ?? 0) > request.outputTokens) {
16988
+ throw new Error("Harness Usage request 'reasoningOutputTokens' must not exceed 'outputTokens'");
16989
+ }
16990
+ if (request.cacheWrite1hInputTokens !== void 0 && request.cacheWrite1hInputTokens > (request.cacheWriteInputTokens ?? -1)) {
16991
+ throw new Error("Harness Usage request 'cacheWrite1hInputTokens' must not exceed 'cacheWriteInputTokens'");
16992
+ }
16993
+ if (request.startedAtMs === void 0 !== (request.completedAtMs === void 0)) {
16994
+ throw new Error("Harness Usage request timing fields must be provided together");
16995
+ }
16996
+ if (request.startedAtMs !== void 0 && request.completedAtMs !== void 0 && request.completedAtMs < request.startedAtMs) {
16997
+ throw new Error("Harness Usage request 'completedAtMs' must not precede 'startedAtMs'");
16998
+ }
16999
+ return { ...request };
17000
+ }
17001
+
17002
+ // dist/session-observation.js
17003
+ var integer2 = (value) => typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : void 0;
17004
+ function tokenSnapshot(value) {
17005
+ const token = object2(value);
17006
+ if (!token)
17007
+ return null;
17008
+ const total = object2(token.total);
17009
+ const last = object2(token.last);
17010
+ const result = {};
17011
+ for (const field of [
17012
+ "totalTokens",
17013
+ "inputTokens",
17014
+ "cachedInputTokens",
17015
+ "cacheWriteInputTokens",
17016
+ "outputTokens",
17017
+ "reasoningOutputTokens"
17018
+ ]) {
17019
+ const n = integer2(total?.[field]);
17020
+ if (n !== void 0)
17021
+ result[field] = n;
17022
+ }
17023
+ const used = integer2(last?.totalTokens);
17024
+ const window = integer2(token.modelContextWindow);
17025
+ if (used !== void 0 && window !== void 0 && window > 0) {
17026
+ result.contextUsedTokens = used;
17027
+ result.contextWindowTokens = window;
17028
+ }
17029
+ const input = integer2(last?.inputTokens);
17030
+ const cached2 = integer2(last?.cachedInputTokens);
17031
+ if (input && cached2 !== void 0 && cached2 <= input)
17032
+ result.cacheHitRatePercent = cached2 / input * 100;
17033
+ if (result.inputTokens && result.cachedInputTokens !== void 0) {
17034
+ result.sessionCacheUsage = {
17035
+ inputTokens: result.inputTokens,
17036
+ cachedInputTokens: result.cachedInputTokens
17037
+ };
17038
+ }
17039
+ try {
17040
+ return Object.keys(result).length ? parseHostUsage(result) : null;
17041
+ } catch {
17042
+ return null;
17043
+ }
17044
+ }
17045
+ function observeSessionUsage(message) {
17046
+ const frame = object2(message);
17047
+ const params = object2(frame?.params);
17048
+ if (!params || typeof params.threadId !== "string" || !params.threadId)
17049
+ return null;
17050
+ const sessionId = params.threadId;
17051
+ if (frame?.method === "thread/tokenUsage/updated") {
17052
+ const usage2 = tokenSnapshot(params.tokenUsage);
17053
+ return usage2 ? { sessionId, usage: usage2 } : null;
17054
+ }
17055
+ if (frame?.method === "turn/started" || frame?.method === "turn/completed") {
17056
+ const turn = object2(params.turn);
17057
+ return typeof turn?.id === "string" && turn.id ? {
17058
+ sessionId,
17059
+ turn: { id: turn.id, phase: frame.method === "turn/started" ? "started" : "completed" }
17060
+ } : null;
17061
+ }
17062
+ if (typeof params.turnId !== "string" || !params.turnId)
17063
+ return null;
17064
+ if ([
17065
+ "item/agentMessage/delta",
17066
+ "item/reasoning/textDelta",
17067
+ "item/reasoning/summaryTextDelta"
17068
+ ].includes(String(frame?.method)) && typeof params.delta === "string" && params.delta.length > 0) {
17069
+ return { sessionId, turn: { id: params.turnId, phase: "output" } };
17070
+ }
17071
+ return null;
17072
+ }
17073
+ function rolloutTokenSnapshot(info) {
17074
+ const record2 = object2(info);
17075
+ if (!record2)
17076
+ return null;
17077
+ const convert = (value) => {
17078
+ const u = object2(value);
17079
+ return u && {
17080
+ totalTokens: u.total_tokens,
17081
+ inputTokens: u.input_tokens,
17082
+ cachedInputTokens: u.cached_input_tokens,
17083
+ cacheWriteInputTokens: u.cache_write_input_tokens,
17084
+ outputTokens: u.output_tokens,
17085
+ reasoningOutputTokens: u.reasoning_output_tokens
17086
+ };
17087
+ };
17088
+ return tokenSnapshot({
17089
+ total: convert(record2.total_token_usage),
17090
+ last: convert(record2.last_token_usage),
17091
+ modelContextWindow: record2.model_context_window
17092
+ });
17093
+ }
17094
+
17095
+ // dist/response-timing.js
17096
+ var nonNegativeInteger = (value) => typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
17097
+ var CodexResponseTiming = class {
17098
+ #turns = /* @__PURE__ */ new Map();
17099
+ reset() {
17100
+ this.#turns.clear();
17101
+ }
17102
+ observe(message, now) {
17103
+ const frame = object2(message);
17104
+ const params = object2(frame?.params);
17105
+ if (!params || typeof params.threadId !== "string")
17106
+ return null;
17107
+ const sessionId = params.threadId;
17108
+ const turn = object2(params.turn);
17109
+ if (frame?.method === "turn/started" && typeof turn?.id === "string") {
17110
+ if (this.#turns.get(sessionId)?.id !== turn.id) {
17111
+ this.#turns.set(sessionId, {
17112
+ id: turn.id,
17113
+ pending: false,
17114
+ unavailable: false,
17115
+ seen: /* @__PURE__ */ new Set()
17116
+ });
17117
+ }
17118
+ return null;
17119
+ }
17120
+ const state = this.#turns.get(sessionId);
17121
+ if (!state)
17122
+ return null;
17123
+ const unavailable = () => {
17124
+ state.unavailable = true;
17125
+ return { sessionId, timingUnavailable: { turnId: state.id } };
17126
+ };
17127
+ if (frame?.method === "thread/closed") {
17128
+ this.#turns.delete(sessionId);
17129
+ return unavailable();
17130
+ }
17131
+ if (frame?.method === "turn/completed" && turn?.id === state.id) {
17132
+ this.#turns.delete(sessionId);
17133
+ return state.pending || state.unavailable ? unavailable() : null;
17134
+ }
17135
+ if (params.turnId !== state.id)
17136
+ return null;
17137
+ if (frame?.method === "error")
17138
+ return unavailable();
17139
+ if (frame?.method === "rawResponse/completed") {
17140
+ const id = params.responseId;
17141
+ if (typeof id !== "string" || !id)
17142
+ return unavailable();
17143
+ if (state.seen.has(id))
17144
+ return null;
17145
+ state.seen.add(id);
17146
+ const usage2 = object2(params.usage);
17147
+ const startedAtMs = state.firstOutputAtMs;
17148
+ state.firstOutputAtMs = void 0;
17149
+ state.pending = false;
17150
+ if (state.unavailable || startedAtMs === void 0 || !nonNegativeInteger(now) || now <= startedAtMs || !nonNegativeInteger(usage2?.outputTokens) || usage2.reasoningOutputTokens != null && (!nonNegativeInteger(usage2.reasoningOutputTokens) || usage2.reasoningOutputTokens > usage2.outputTokens)) {
17151
+ return unavailable();
17152
+ }
17153
+ return {
17154
+ sessionId,
17155
+ requestTiming: {
17156
+ requestId: id,
17157
+ turnId: state.id,
17158
+ outputTokens: usage2.outputTokens,
17159
+ startedAtMs,
17160
+ completedAtMs: now
17161
+ }
17162
+ };
17163
+ }
17164
+ if (frame?.method === "rawResponseItem/completed") {
17165
+ const item = object2(params.item);
17166
+ if (item?.type === "message" && item.role === "assistant" || [
17167
+ "reasoning",
17168
+ "function_call",
17169
+ "custom_tool_call",
17170
+ "local_shell_call",
17171
+ "web_search_call",
17172
+ "image_generation_call",
17173
+ "tool_search_call"
17174
+ ].includes(String(item?.type)))
17175
+ state.pending = true;
17176
+ return null;
17177
+ }
17178
+ if (frame?.method === "item/started") {
17179
+ const item = object2(params.item);
17180
+ if (item?.type === "agentMessage" || item?.type === "reasoning" || item?.type === "plan") {
17181
+ state.pending = true;
17182
+ state.firstOutputAtMs ??= now;
17183
+ }
17184
+ return null;
17185
+ }
17186
+ const reasoning = frame?.method === "item/reasoning/textDelta" || frame?.method === "item/reasoning/summaryTextDelta";
17187
+ if ((reasoning || frame?.method === "item/agentMessage/delta" || frame?.method === "item/plan/delta") && typeof params.delta === "string" && params.delta.length > 0) {
17188
+ state.pending = true;
17189
+ state.firstOutputAtMs ??= now;
17190
+ }
17191
+ return null;
17192
+ }
17193
+ };
17194
+
17195
+ // dist/session-usage.js
17196
+ function createCodexSessionUsage(environment, closed) {
17197
+ const timing = new CodexResponseTiming();
17198
+ const rawCompleted = "rawResponse/completed";
17199
+ let hideRawCompleted = false;
17200
+ const live = /* @__PURE__ */ new Map();
17201
+ const cache = /* @__PURE__ */ new Map();
17202
+ return {
17203
+ requestOptions({ method, params, initializeParams }) {
17204
+ if (closed.aborted)
17205
+ return null;
17206
+ if (method === "initialize") {
17207
+ const capabilities2 = object2(params.capabilities);
17208
+ const excluded2 = capabilities2?.optOutNotificationMethods;
17209
+ hideRawCompleted = false;
17210
+ if (capabilities2?.experimentalApi !== true || !Array.isArray(excluded2))
17211
+ return null;
17212
+ if (!excluded2.includes(rawCompleted))
17213
+ return null;
17214
+ hideRawCompleted = true;
17215
+ return {
17216
+ capabilities: {
17217
+ ...capabilities2,
17218
+ optOutNotificationMethods: excluded2.filter((method2) => method2 !== rawCompleted)
17219
+ }
17220
+ };
17221
+ }
17222
+ const capabilities = object2(initializeParams?.capabilities);
17223
+ const excluded = capabilities?.optOutNotificationMethods;
17224
+ return method === "thread/start" && capabilities?.experimentalApi === true && !(Array.isArray(excluded) && excluded.includes(rawCompleted)) && (params.experimentalRawEvents === void 0 || params.experimentalRawEvents === false) ? { experimentalRawEvents: true } : null;
17225
+ },
17226
+ shouldForwardNotification(message) {
17227
+ return closed.aborted || !hideRawCompleted || object2(message)?.method !== rawCompleted;
17228
+ },
17229
+ reset() {
17230
+ timing.reset();
17231
+ },
17232
+ observe(message, observedAtMs = Date.now()) {
17233
+ if (closed.aborted)
17234
+ return null;
17235
+ const observation = observeSessionUsage(message);
17236
+ const measured = timing.observe(message, observedAtMs);
17237
+ if (observation?.usage)
17238
+ live.set(observation.sessionId, observation.usage);
17239
+ return measured ? { ...observation, ...measured } : observation;
17240
+ },
17241
+ async read(sessionId, signal) {
17242
+ signal = AbortSignal.any([signal, closed]);
17243
+ signal.throwIfAborted();
17244
+ for (let attempt = 0; ; attempt++) {
17245
+ let snapshot = null;
17246
+ let complete = true;
17247
+ const reader = createCodexUsageStatistics(environment, {
17248
+ sessionId,
17249
+ onTokenCount: (info) => {
17250
+ snapshot = rolloutTokenSnapshot(info) ?? snapshot;
17251
+ },
17252
+ onIncomplete: () => {
17253
+ complete = false;
17254
+ }
17255
+ });
17256
+ const sources = await reader.listSources(signal);
17257
+ const source = sources[0];
17258
+ if (!source)
17259
+ return null;
17260
+ let history = cache.get(sessionId);
17261
+ if (history?.fingerprint !== source.fingerprint) {
17262
+ const entries = await reader.readSource(source.id, signal);
17263
+ const requests = entries.map((entry) => parseHostUsageRequest({
17264
+ requestId: entry.id,
17265
+ historical: true,
17266
+ ...entry.model ? { model: entry.model } : {},
17267
+ inputTokens: entry.inputTokens,
17268
+ outputTokens: entry.outputTokens,
17269
+ ...entry.cachedInputTokens !== void 0 ? { cachedInputTokens: entry.cachedInputTokens } : {},
17270
+ // Codex/OpenAI cache has no separately billed cache creation bucket.
17271
+ cacheWriteInputTokens: entry.cacheWriteInputTokens ?? 0,
17272
+ ...entry.reasoningOutputTokens !== void 0 ? { reasoningOutputTokens: entry.reasoningOutputTokens } : {}
17273
+ }));
17274
+ const inputTokens = requests.reduce((sum, r) => sum + r.inputTokens, 0);
17275
+ const outputTokens = requests.reduce((sum, r) => sum + r.outputTokens, 0);
17276
+ const cachedInputTokens = requests.every((r) => r.cachedInputTokens !== void 0) ? requests.reduce((sum, r) => sum + (r.cachedInputTokens ?? 0), 0) : void 0;
17277
+ const last = requests.at(-1);
17278
+ const usage2 = requests.length ? parseHostUsage({
17279
+ ...snapshot ?? {},
17280
+ inputTokens,
17281
+ outputTokens,
17282
+ totalTokens: inputTokens + outputTokens,
17283
+ ...cachedInputTokens !== void 0 ? { cachedInputTokens } : {},
17284
+ ...complete && cachedInputTokens !== void 0 ? { sessionCacheUsage: { inputTokens, cachedInputTokens } } : {},
17285
+ ...last?.inputTokens && last.cachedInputTokens !== void 0 ? { cacheHitRatePercent: last.cachedInputTokens / last.inputTokens * 100 } : {}
17286
+ }) : snapshot;
17287
+ history = {
17288
+ fingerprint: source.fingerprint,
17289
+ history: { usage: usage2, requests, complete: complete && requests.length > 0 }
17290
+ };
17291
+ cache.set(sessionId, history);
17292
+ }
17293
+ const result = history.history;
17294
+ const latest = live.get(sessionId);
17295
+ const caughtUp = !latest || (latest.inputTokens ?? 0) <= (result.usage?.inputTokens ?? 0) && (latest.outputTokens ?? 0) <= (result.usage?.outputTokens ?? 0);
17296
+ if (!caughtUp && attempt < 2) {
17297
+ await delay(50 * (attempt + 1), void 0, { signal });
17298
+ continue;
17299
+ }
17300
+ return { ...result, complete: result.complete && caughtUp };
17301
+ }
16682
17302
  }
16683
17303
  };
16684
17304
  }
@@ -16689,6 +17309,7 @@ function createUsageStatisticsAdapter(context) {
16689
17309
  const reader = createCodexUsageStatistics(context.environment);
16690
17310
  return {
16691
17311
  harnessId: harnessIdSchema.parse("codex-usage"),
17312
+ sessionUsage: createCodexSessionUsage(context.environment, abort.signal),
16692
17313
  usageStatistics: {
16693
17314
  listSources: (signal) => reader.listSources(AbortSignal.any([signal, abort.signal])),
16694
17315
  readSource: (id, signal) => reader.readSource(id, AbortSignal.any([signal, abort.signal]))