@codexhost/cli-win32-arm64 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.
@@ -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) {
@@ -16493,32 +16543,84 @@ async function copyRelation(a, b, signal) {
16493
16543
  // dist/usage-statistics.js
16494
16544
  var ROLLOUT = /^rollout-.*?([0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12})(?:[_-][0-9a-f-]+)?\.jsonl(?:\.zst)?$/iu;
16495
16545
  var text = (value) => typeof value === "string" ? value : "";
16546
+ var ROW_KINDS = /* @__PURE__ */ new Set([
16547
+ "session_meta",
16548
+ "turn_context",
16549
+ "token_usage_record",
16550
+ "compacted",
16551
+ "event_msg",
16552
+ "response_item"
16553
+ ]);
16554
+ var HEAD_TYPE = /"type"\s*:\s*"([^"]+)"/u;
16555
+ var TAIL_TYPE = /"type"\s*:\s*"([^"]+)"\s*\}\s*$/u;
16556
+ function rowKind(line) {
16557
+ const head = HEAD_TYPE.exec(line.slice(0, 1024))?.[1];
16558
+ if (head && ROW_KINDS.has(head))
16559
+ return head;
16560
+ return TAIL_TYPE.exec(line.slice(-1024))?.[1] ?? head;
16561
+ }
16496
16562
  async function stamp(file2) {
16497
16563
  const s = await lstat(file2, { bigint: true });
16498
16564
  if (!s.isFile())
16499
16565
  throw new Error("Codex rollout is not a regular file");
16500
16566
  return `${s.dev}:${s.ino}:${s.size}:${s.mtimeNs}:${s.ctimeNs}`;
16501
16567
  }
16502
- function fingerprint(files) {
16503
- return createHash("sha256").update(JSON.stringify(files)).digest("hex");
16568
+ function fingerprint(files, extra = "") {
16569
+ return createHash("sha256").update(JSON.stringify(files)).update(extra).digest("hex");
16504
16570
  }
16505
- async function readCodexRollouts(files, thread, signal) {
16571
+ function typedPrompt(value) {
16572
+ if (typeof value !== "string")
16573
+ return void 0;
16574
+ const text2 = value.trim();
16575
+ if (!text2 || text2.startsWith("<") || text2.startsWith("# AGENTS.md") || text2.startsWith("<!--")) {
16576
+ return void 0;
16577
+ }
16578
+ return usageSessionTitle(text2);
16579
+ }
16580
+ function userItemPrompt(payload) {
16581
+ if (!payload || payload.type !== "message" || payload.role !== "user")
16582
+ return void 0;
16583
+ if (!Array.isArray(payload.content))
16584
+ return typedPrompt(payload.content);
16585
+ for (const part of payload.content) {
16586
+ if (typeof part !== "object" || part === null)
16587
+ continue;
16588
+ const block = part;
16589
+ if (block.type === "input_text" || block.type === "text") {
16590
+ const prompt = typedPrompt(block.text);
16591
+ if (prompt)
16592
+ return prompt;
16593
+ }
16594
+ }
16595
+ return void 0;
16596
+ }
16597
+ async function readThreadNames(home, signal) {
16598
+ const names = /* @__PURE__ */ new Map();
16599
+ try {
16600
+ for await (const line of jsonlRecords(path.join(home, "session_index.jsonl"), '"thread_name"', signal)) {
16601
+ const id = text(line.id);
16602
+ const name = usageSessionTitle(line.thread_name);
16603
+ if (id && name)
16604
+ names.set(id, name);
16605
+ }
16606
+ } catch (error51) {
16607
+ if (error51.code !== "ENOENT")
16608
+ throw error51;
16609
+ }
16610
+ return names;
16611
+ }
16612
+ async function readCodexRollouts(files, thread, signal, options = {}) {
16506
16613
  const counters = new CodexCounters();
16507
- let model2 = "", cwd = "", ownFrom = null, firstMeta = false, forkAt = 0, seen = 0;
16614
+ let model2 = "", cwd = "", ownFrom = null, firstMeta = false, forkAt = 0, seen = 0, firstPrompt;
16508
16615
  for (const file2 of files) {
16509
16616
  for await (const line of rolloutLines(file2, signal)) {
16510
16617
  signal.throwIfAborted();
16511
16618
  if (++seen % 128 === 0)
16512
16619
  await yieldToEventLoop(void 0, { signal });
16513
- const kind = /"type"\s*:\s*"([^"]+)"/u.exec(line.slice(0, 1024))?.[1];
16514
- if (!kind || ![
16515
- "session_meta",
16516
- "turn_context",
16517
- "token_usage_record",
16518
- "compacted",
16519
- "event_msg",
16520
- "response_item"
16521
- ].includes(kind))
16620
+ const kind = rowKind(line);
16621
+ if (!kind && line.trim())
16622
+ options.onIncomplete?.();
16623
+ if (!kind || !ROW_KINDS.has(kind))
16522
16624
  continue;
16523
16625
  if (kind === "response_item" && !/"role"\s*:\s*"user"/u.test(line.slice(0, 1024)))
16524
16626
  continue;
@@ -16528,19 +16630,24 @@ async function readCodexRollouts(files, thread, signal) {
16528
16630
  try {
16529
16631
  row = object2(JSON.parse(line));
16530
16632
  } catch {
16633
+ options.onIncomplete?.();
16531
16634
  continue;
16532
16635
  }
16533
16636
  const p = object2(row?.payload);
16534
16637
  const at = nativeTimeMs(row?.timestamp);
16535
- if (!row || !p || at === null)
16638
+ if (!row || !p || at === null) {
16639
+ options.onIncomplete?.();
16536
16640
  continue;
16641
+ }
16537
16642
  switch (row.type) {
16538
16643
  case "session_meta": {
16539
16644
  if (!firstMeta) {
16540
16645
  if (text(p.id) !== thread)
16541
16646
  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)
16647
+ if (p.forked_from_id || p.parent_thread_id || p.subagent_history_start_ordinal) {
16543
16648
  forkAt = nativeTimeMs(p.timestamp) ?? at;
16649
+ options.onIncomplete?.();
16650
+ }
16544
16651
  cwd = text(p.cwd);
16545
16652
  firstMeta = true;
16546
16653
  }
@@ -16554,30 +16661,41 @@ async function readCodexRollouts(files, thread, signal) {
16554
16661
  counters.context("", text(p.turn_id), model2);
16555
16662
  break;
16556
16663
  case "token_usage_record":
16664
+ if (!usage(p.usage) || !text(p.response_id))
16665
+ options.onIncomplete?.();
16557
16666
  counters.record(at, model2, p);
16558
16667
  break;
16559
16668
  case "compacted": {
16560
16669
  const record2 = object2(p.latest_token_usage_record);
16561
- if (record2 && p.compaction_response_id && record2.response_id === p.compaction_response_id)
16670
+ if (record2 && p.compaction_response_id && record2.response_id === p.compaction_response_id) {
16671
+ if (!usage(record2.usage))
16672
+ options.onIncomplete?.();
16562
16673
  counters.record(at, model2, record2, true);
16674
+ }
16563
16675
  break;
16564
16676
  }
16565
16677
  case "response_item":
16566
16678
  counters.boundary();
16679
+ firstPrompt ??= userItemPrompt(p);
16567
16680
  break;
16568
16681
  case "event_msg":
16569
16682
  if (p.type === "task_started") {
16570
16683
  counters.context("", text(p.turn_id), "");
16571
16684
  counters.boundary();
16572
16685
  }
16573
- if (p.type === "user_message")
16686
+ if (p.type === "user_message") {
16574
16687
  counters.boundary();
16688
+ firstPrompt ??= typedPrompt(p.message);
16689
+ }
16575
16690
  if (p.type === "thread_settings_applied") {
16576
16691
  model2 = text(object2(p.thread_settings)?.model) || model2;
16577
16692
  counters.context("", "", model2);
16578
16693
  }
16579
16694
  if (p.type === "token_count") {
16580
16695
  const info = object2(p.info);
16696
+ options.onTokenCount?.(info);
16697
+ if (info && !usage(info.total_token_usage))
16698
+ options.onIncomplete?.();
16581
16699
  if (info)
16582
16700
  counters.count(at, model2, usage(info.total_token_usage), usage(info.last_token_usage));
16583
16701
  }
@@ -16604,12 +16722,22 @@ async function readCodexRollouts(files, thread, signal) {
16604
16722
  ...v.usage.known & 8 ? { cacheWriteInputTokens: written } : {},
16605
16723
  ...v.usage.known & 16 ? { reasoningOutputTokens: reasoning } : {}
16606
16724
  });
16607
- return entry ? [withUsageSession(entry, { sessionId: thread, cwd })] : [];
16725
+ return entry ? [withUsageSession(entry, { sessionId: thread, cwd, title: firstPrompt })] : [];
16608
16726
  });
16609
16727
  }
16610
- function createCodexUsageStatistics(environment) {
16728
+ function createCodexUsageStatistics(environment, options = {}) {
16611
16729
  const home = path.resolve(environment.CODEX_HOME || path.join(environment.HOME || environment.USERPROFILE || os.homedir(), ".codex"));
16612
16730
  const groups = /* @__PURE__ */ new Map();
16731
+ let names = /* @__PURE__ */ new Map();
16732
+ let namesStamp = "";
16733
+ async function threadNames(signal) {
16734
+ const current = await stamp(path.join(home, "session_index.jsonl")).catch(() => "-");
16735
+ if (namesStamp === current)
16736
+ return names;
16737
+ names = await readThreadNames(home, signal);
16738
+ namesStamp = current;
16739
+ return names;
16740
+ }
16613
16741
  return {
16614
16742
  async listSources(signal) {
16615
16743
  const found = /* @__PURE__ */ new Map();
@@ -16628,7 +16756,7 @@ function createCodexUsageStatistics(environment) {
16628
16756
  if (!entry.isFile())
16629
16757
  continue;
16630
16758
  const thread = ROLLOUT.exec(entry.name)?.[1];
16631
- if (!thread)
16759
+ if (!thread || options.sessionId && thread !== options.sessionId)
16632
16760
  continue;
16633
16761
  const list = found.get(thread) ?? [];
16634
16762
  list.push({ file: file2, fingerprint: await stamp(file2) });
@@ -16637,13 +16765,14 @@ function createCodexUsageStatistics(environment) {
16637
16765
  }
16638
16766
  await visit(path.join(home, "sessions"), 0);
16639
16767
  await visit(path.join(home, "archived_sessions"), 0);
16768
+ const indexStamp = options.sessionId ? "" : await stamp(path.join(home, "session_index.jsonl")).catch(() => "-");
16640
16769
  const result = [];
16641
16770
  groups.clear();
16642
16771
  for (const [thread, files] of found) {
16643
16772
  files.sort((a, b) => path.basename(a.file).localeCompare(path.basename(b.file)) || a.file.localeCompare(b.file));
16644
16773
  const id = path.join(home, `usage-thread-${thread}`);
16645
16774
  groups.set(id, { thread, files });
16646
- result.push({ id, fingerprint: fingerprint(files) });
16775
+ result.push({ id, fingerprint: fingerprint(files, indexStamp) });
16647
16776
  }
16648
16777
  return result;
16649
16778
  },
@@ -16674,11 +16803,511 @@ function createCodexUsageStatistics(environment) {
16674
16803
  if (!duplicate)
16675
16804
  kept.push(candidate);
16676
16805
  }
16677
- const entries = await readCodexRollouts(kept.map(({ file: file2 }) => file2), group.thread, signal);
16806
+ const entries = await readCodexRollouts(kept.map(({ file: file2 }) => file2), group.thread, signal, options);
16678
16807
  for (const file2 of group.files)
16679
16808
  if (await stamp(file2.file) !== file2.fingerprint)
16680
16809
  throw new Error("Codex rollout changed while reading; refresh to retry");
16681
- return entries;
16810
+ const named = options.sessionId ? void 0 : (await threadNames(signal)).get(group.thread);
16811
+ return named ? entries.map((entry) => withUsageSession(entry, {
16812
+ sessionId: entry.sessionId,
16813
+ cwd: entry.cwd,
16814
+ title: named
16815
+ })) : entries;
16816
+ }
16817
+ };
16818
+ }
16819
+
16820
+ // dist/session-usage.js
16821
+ import { setTimeout as delay } from "node:timers/promises";
16822
+
16823
+ // ../../harness-adapter/dist/usage.js
16824
+ var tokenFields = [
16825
+ "inputTokens",
16826
+ "cachedInputTokens",
16827
+ "cacheWriteInputTokens",
16828
+ "outputTokens",
16829
+ "reasoningOutputTokens",
16830
+ "totalTokens",
16831
+ "contextWindowTokens",
16832
+ "contextUsedTokens"
16833
+ ];
16834
+ var safeIntegerFields = [
16835
+ "planFiveHourResetsAtUnix",
16836
+ "planSevenDayResetsAtUnix",
16837
+ "timeToFirstOutputMs"
16838
+ ];
16839
+ var percentFields = [
16840
+ "cacheHitRatePercent",
16841
+ "sessionCacheHitRatePercent",
16842
+ "planFiveHourUsedPercent",
16843
+ "planSevenDayUsedPercent"
16844
+ ];
16845
+ var usageFields = /* @__PURE__ */ new Set([
16846
+ ...tokenFields,
16847
+ ...safeIntegerFields,
16848
+ ...percentFields,
16849
+ "sessionCacheUsage",
16850
+ "totalCostUsd",
16851
+ "totalCredits",
16852
+ "contextUsagePercent",
16853
+ "outputTokensPerSecond",
16854
+ "apiOutputTokensPerSecond",
16855
+ "costSource",
16856
+ "unpricedModels"
16857
+ ]);
16858
+ function isRecord(value) {
16859
+ return typeof value === "object" && value !== null && !Array.isArray(value);
16860
+ }
16861
+ function parseHostUsage(value) {
16862
+ if (!isRecord(value))
16863
+ throw new Error("Harness Usage must be an object");
16864
+ const keys = Object.keys(value);
16865
+ if (keys.length === 0)
16866
+ throw new Error("Harness Usage must contain a reliable field");
16867
+ for (const key2 of keys) {
16868
+ if (!usageFields.has(key2)) {
16869
+ throw new Error(`Harness Usage contains unknown field '${key2}'`);
16870
+ }
16871
+ }
16872
+ for (const field of [
16873
+ "totalCredits",
16874
+ "contextUsagePercent",
16875
+ "apiOutputTokensPerSecond"
16876
+ ]) {
16877
+ const candidate = value[field];
16878
+ if (candidate !== void 0 && (typeof candidate !== "number" || !Number.isFinite(candidate) || candidate < 0)) {
16879
+ throw new Error(`Harness Usage '${field}' must be a finite non-negative number`);
16880
+ }
16881
+ }
16882
+ for (const field of tokenFields) {
16883
+ const candidate = value[field];
16884
+ if (candidate !== void 0 && (typeof candidate !== "number" || !Number.isSafeInteger(candidate) || candidate < 0)) {
16885
+ throw new Error(`Harness Usage '${field}' must be a non-negative safe integer`);
16886
+ }
16887
+ }
16888
+ for (const field of safeIntegerFields) {
16889
+ const candidate = value[field];
16890
+ if (candidate !== void 0 && (typeof candidate !== "number" || !Number.isSafeInteger(candidate) || candidate < 0)) {
16891
+ throw new Error(`Harness Usage '${field}' must be a non-negative safe integer`);
16892
+ }
16893
+ }
16894
+ if (value.outputTokensPerSecond !== void 0 && (typeof value.outputTokensPerSecond !== "number" || !Number.isFinite(value.outputTokensPerSecond) || value.outputTokensPerSecond < 0)) {
16895
+ throw new Error("Harness Usage 'outputTokensPerSecond' must be a finite non-negative number");
16896
+ }
16897
+ if (value.totalCostUsd !== void 0 && (typeof value.totalCostUsd !== "number" || !Number.isFinite(value.totalCostUsd) || value.totalCostUsd < 0)) {
16898
+ throw new Error("Harness Usage 'totalCostUsd' must be a finite non-negative number");
16899
+ }
16900
+ for (const field of percentFields) {
16901
+ const candidate = value[field];
16902
+ if (candidate !== void 0 && (typeof candidate !== "number" || !Number.isFinite(candidate) || candidate < 0 || candidate > 100)) {
16903
+ throw new Error(`Harness Usage '${field}' must be between 0 and 100`);
16904
+ }
16905
+ }
16906
+ if (value.sessionCacheUsage !== void 0) {
16907
+ const totals2 = value.sessionCacheUsage;
16908
+ 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) {
16909
+ throw new Error("Harness Usage 'sessionCacheUsage' must contain complete bounded token totals");
16910
+ }
16911
+ }
16912
+ const hasContextUsed = value.contextUsedTokens !== void 0;
16913
+ const hasContextWindow = value.contextWindowTokens !== void 0;
16914
+ if (hasContextUsed !== hasContextWindow) {
16915
+ throw new Error("Harness Usage context fields must be provided together");
16916
+ }
16917
+ if (hasContextWindow && value.contextWindowTokens === 0) {
16918
+ throw new Error("Harness Usage 'contextWindowTokens' must be greater than zero");
16919
+ }
16920
+ if (value.planFiveHourResetsAtUnix !== void 0 && value.planFiveHourUsedPercent === void 0) {
16921
+ throw new Error("Harness Usage 'planFiveHourResetsAtUnix' must be provided with 'planFiveHourUsedPercent'");
16922
+ }
16923
+ if (value.planSevenDayResetsAtUnix !== void 0 && value.planSevenDayUsedPercent === void 0) {
16924
+ throw new Error("Harness Usage 'planSevenDayResetsAtUnix' must be provided with 'planSevenDayUsedPercent'");
16925
+ }
16926
+ if (value.costSource !== void 0) {
16927
+ if (value.costSource !== "publicPrice" && value.costSource !== "native") {
16928
+ throw new Error("Harness Usage 'costSource' must be 'publicPrice' or 'native'");
16929
+ }
16930
+ if (value.totalCostUsd === void 0) {
16931
+ throw new Error("Harness Usage 'costSource' must be provided with 'totalCostUsd'");
16932
+ }
16933
+ }
16934
+ if (value.unpricedModels !== void 0) {
16935
+ const models = value.unpricedModels;
16936
+ if (!Array.isArray(models) || models.length === 0 || !models.every((model2) => typeof model2 === "string" && model2.length > 0)) {
16937
+ throw new Error("Harness Usage 'unpricedModels' must be a non-empty list of Model IDs");
16938
+ }
16939
+ if (value.costSource !== "publicPrice") {
16940
+ throw new Error("Harness Usage 'unpricedModels' must be provided with a 'publicPrice' cost");
16941
+ }
16942
+ return { ...value, unpricedModels: [...models] };
16943
+ }
16944
+ return { ...value };
16945
+ }
16946
+ var requestTokenFields = [
16947
+ "inputTokens",
16948
+ "cachedInputTokens",
16949
+ "cacheWriteInputTokens",
16950
+ "cacheWrite1hInputTokens",
16951
+ "outputTokens",
16952
+ "reasoningOutputTokens",
16953
+ "startedAtMs",
16954
+ "completedAtMs"
16955
+ ];
16956
+ var requestFields = /* @__PURE__ */ new Set([
16957
+ ...requestTokenFields,
16958
+ "requestId",
16959
+ "historical",
16960
+ "model",
16961
+ "provider"
16962
+ ]);
16963
+ function parseHostUsageRequest(value) {
16964
+ if (!isRecord(value))
16965
+ throw new Error("Harness Usage request must be an object");
16966
+ for (const key2 of Object.keys(value)) {
16967
+ if (!requestFields.has(key2)) {
16968
+ throw new Error(`Harness Usage request contains unknown field '${key2}'`);
16969
+ }
16970
+ }
16971
+ if (typeof value.requestId !== "string" || value.requestId.length === 0) {
16972
+ throw new Error("Harness Usage request 'requestId' must be a non-empty string");
16973
+ }
16974
+ for (const field of ["model", "provider"]) {
16975
+ const candidate = value[field];
16976
+ if (candidate !== void 0 && (typeof candidate !== "string" || candidate.length === 0)) {
16977
+ throw new Error(`Harness Usage request '${field}' must be a non-empty string`);
16978
+ }
16979
+ }
16980
+ if (value.historical !== void 0 && typeof value.historical !== "boolean") {
16981
+ throw new Error("Harness Usage request 'historical' must be a boolean");
16982
+ }
16983
+ for (const field of requestTokenFields) {
16984
+ const candidate = value[field];
16985
+ if (candidate !== void 0 && (typeof candidate !== "number" || !Number.isSafeInteger(candidate) || candidate < 0)) {
16986
+ throw new Error(`Harness Usage request '${field}' must be a non-negative safe integer`);
16987
+ }
16988
+ }
16989
+ if (value.inputTokens === void 0 || value.outputTokens === void 0) {
16990
+ throw new Error("Harness Usage request must contain 'inputTokens' and 'outputTokens'");
16991
+ }
16992
+ const request = value;
16993
+ if ((request.cachedInputTokens ?? 0) + (request.cacheWriteInputTokens ?? 0) > request.inputTokens) {
16994
+ throw new Error("Harness Usage request cache Tokens must not exceed 'inputTokens'");
16995
+ }
16996
+ if ((request.reasoningOutputTokens ?? 0) > request.outputTokens) {
16997
+ throw new Error("Harness Usage request 'reasoningOutputTokens' must not exceed 'outputTokens'");
16998
+ }
16999
+ if (request.cacheWrite1hInputTokens !== void 0 && request.cacheWrite1hInputTokens > (request.cacheWriteInputTokens ?? -1)) {
17000
+ throw new Error("Harness Usage request 'cacheWrite1hInputTokens' must not exceed 'cacheWriteInputTokens'");
17001
+ }
17002
+ if (request.startedAtMs === void 0 !== (request.completedAtMs === void 0)) {
17003
+ throw new Error("Harness Usage request timing fields must be provided together");
17004
+ }
17005
+ if (request.startedAtMs !== void 0 && request.completedAtMs !== void 0 && request.completedAtMs < request.startedAtMs) {
17006
+ throw new Error("Harness Usage request 'completedAtMs' must not precede 'startedAtMs'");
17007
+ }
17008
+ return { ...request };
17009
+ }
17010
+
17011
+ // dist/session-observation.js
17012
+ var integer2 = (value) => typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : void 0;
17013
+ function tokenSnapshot(value) {
17014
+ const token = object2(value);
17015
+ if (!token)
17016
+ return null;
17017
+ const total = object2(token.total);
17018
+ const last = object2(token.last);
17019
+ const result = {};
17020
+ for (const field of [
17021
+ "totalTokens",
17022
+ "inputTokens",
17023
+ "cachedInputTokens",
17024
+ "cacheWriteInputTokens",
17025
+ "outputTokens",
17026
+ "reasoningOutputTokens"
17027
+ ]) {
17028
+ const n = integer2(total?.[field]);
17029
+ if (n !== void 0)
17030
+ result[field] = n;
17031
+ }
17032
+ const used = integer2(last?.totalTokens);
17033
+ const window = integer2(token.modelContextWindow);
17034
+ if (used !== void 0 && window !== void 0 && window > 0) {
17035
+ result.contextUsedTokens = used;
17036
+ result.contextWindowTokens = window;
17037
+ }
17038
+ const input = integer2(last?.inputTokens);
17039
+ const cached2 = integer2(last?.cachedInputTokens);
17040
+ if (input && cached2 !== void 0 && cached2 <= input)
17041
+ result.cacheHitRatePercent = cached2 / input * 100;
17042
+ if (result.inputTokens && result.cachedInputTokens !== void 0) {
17043
+ result.sessionCacheUsage = {
17044
+ inputTokens: result.inputTokens,
17045
+ cachedInputTokens: result.cachedInputTokens
17046
+ };
17047
+ }
17048
+ try {
17049
+ return Object.keys(result).length ? parseHostUsage(result) : null;
17050
+ } catch {
17051
+ return null;
17052
+ }
17053
+ }
17054
+ function observeSessionUsage(message) {
17055
+ const frame = object2(message);
17056
+ const params = object2(frame?.params);
17057
+ if (!params || typeof params.threadId !== "string" || !params.threadId)
17058
+ return null;
17059
+ const sessionId = params.threadId;
17060
+ if (frame?.method === "thread/tokenUsage/updated") {
17061
+ const usage2 = tokenSnapshot(params.tokenUsage);
17062
+ return usage2 ? { sessionId, usage: usage2 } : null;
17063
+ }
17064
+ if (frame?.method === "turn/started" || frame?.method === "turn/completed") {
17065
+ const turn = object2(params.turn);
17066
+ return typeof turn?.id === "string" && turn.id ? {
17067
+ sessionId,
17068
+ turn: { id: turn.id, phase: frame.method === "turn/started" ? "started" : "completed" }
17069
+ } : null;
17070
+ }
17071
+ if (typeof params.turnId !== "string" || !params.turnId)
17072
+ return null;
17073
+ if ([
17074
+ "item/agentMessage/delta",
17075
+ "item/reasoning/textDelta",
17076
+ "item/reasoning/summaryTextDelta"
17077
+ ].includes(String(frame?.method)) && typeof params.delta === "string" && params.delta.length > 0) {
17078
+ return { sessionId, turn: { id: params.turnId, phase: "output" } };
17079
+ }
17080
+ return null;
17081
+ }
17082
+ function rolloutTokenSnapshot(info) {
17083
+ const record2 = object2(info);
17084
+ if (!record2)
17085
+ return null;
17086
+ const convert = (value) => {
17087
+ const u = object2(value);
17088
+ return u && {
17089
+ totalTokens: u.total_tokens,
17090
+ inputTokens: u.input_tokens,
17091
+ cachedInputTokens: u.cached_input_tokens,
17092
+ cacheWriteInputTokens: u.cache_write_input_tokens,
17093
+ outputTokens: u.output_tokens,
17094
+ reasoningOutputTokens: u.reasoning_output_tokens
17095
+ };
17096
+ };
17097
+ return tokenSnapshot({
17098
+ total: convert(record2.total_token_usage),
17099
+ last: convert(record2.last_token_usage),
17100
+ modelContextWindow: record2.model_context_window
17101
+ });
17102
+ }
17103
+
17104
+ // dist/response-timing.js
17105
+ var nonNegativeInteger = (value) => typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
17106
+ var CodexResponseTiming = class {
17107
+ #turns = /* @__PURE__ */ new Map();
17108
+ reset() {
17109
+ this.#turns.clear();
17110
+ }
17111
+ observe(message, now) {
17112
+ const frame = object2(message);
17113
+ const params = object2(frame?.params);
17114
+ if (!params || typeof params.threadId !== "string")
17115
+ return null;
17116
+ const sessionId = params.threadId;
17117
+ const turn = object2(params.turn);
17118
+ if (frame?.method === "turn/started" && typeof turn?.id === "string") {
17119
+ if (this.#turns.get(sessionId)?.id !== turn.id) {
17120
+ this.#turns.set(sessionId, {
17121
+ id: turn.id,
17122
+ pending: false,
17123
+ unavailable: false,
17124
+ seen: /* @__PURE__ */ new Set()
17125
+ });
17126
+ }
17127
+ return null;
17128
+ }
17129
+ const state = this.#turns.get(sessionId);
17130
+ if (!state)
17131
+ return null;
17132
+ const unavailable = () => {
17133
+ state.unavailable = true;
17134
+ return { sessionId, timingUnavailable: { turnId: state.id } };
17135
+ };
17136
+ if (frame?.method === "thread/closed") {
17137
+ this.#turns.delete(sessionId);
17138
+ return unavailable();
17139
+ }
17140
+ if (frame?.method === "turn/completed" && turn?.id === state.id) {
17141
+ this.#turns.delete(sessionId);
17142
+ return state.pending || state.unavailable ? unavailable() : null;
17143
+ }
17144
+ if (params.turnId !== state.id)
17145
+ return null;
17146
+ if (frame?.method === "error")
17147
+ return unavailable();
17148
+ if (frame?.method === "rawResponse/completed") {
17149
+ const id = params.responseId;
17150
+ if (typeof id !== "string" || !id)
17151
+ return unavailable();
17152
+ if (state.seen.has(id))
17153
+ return null;
17154
+ state.seen.add(id);
17155
+ const usage2 = object2(params.usage);
17156
+ const startedAtMs = state.firstOutputAtMs;
17157
+ state.firstOutputAtMs = void 0;
17158
+ state.pending = false;
17159
+ if (state.unavailable || startedAtMs === void 0 || !nonNegativeInteger(now) || now <= startedAtMs || !nonNegativeInteger(usage2?.outputTokens) || usage2.reasoningOutputTokens != null && (!nonNegativeInteger(usage2.reasoningOutputTokens) || usage2.reasoningOutputTokens > usage2.outputTokens)) {
17160
+ return unavailable();
17161
+ }
17162
+ return {
17163
+ sessionId,
17164
+ requestTiming: {
17165
+ requestId: id,
17166
+ turnId: state.id,
17167
+ outputTokens: usage2.outputTokens,
17168
+ startedAtMs,
17169
+ completedAtMs: now
17170
+ }
17171
+ };
17172
+ }
17173
+ if (frame?.method === "rawResponseItem/completed") {
17174
+ const item = object2(params.item);
17175
+ if (item?.type === "message" && item.role === "assistant" || [
17176
+ "reasoning",
17177
+ "function_call",
17178
+ "custom_tool_call",
17179
+ "local_shell_call",
17180
+ "web_search_call",
17181
+ "image_generation_call",
17182
+ "tool_search_call"
17183
+ ].includes(String(item?.type)))
17184
+ state.pending = true;
17185
+ return null;
17186
+ }
17187
+ if (frame?.method === "item/started") {
17188
+ const item = object2(params.item);
17189
+ if (item?.type === "agentMessage" || item?.type === "reasoning" || item?.type === "plan") {
17190
+ state.pending = true;
17191
+ state.firstOutputAtMs ??= now;
17192
+ }
17193
+ return null;
17194
+ }
17195
+ const reasoning = frame?.method === "item/reasoning/textDelta" || frame?.method === "item/reasoning/summaryTextDelta";
17196
+ if ((reasoning || frame?.method === "item/agentMessage/delta" || frame?.method === "item/plan/delta") && typeof params.delta === "string" && params.delta.length > 0) {
17197
+ state.pending = true;
17198
+ state.firstOutputAtMs ??= now;
17199
+ }
17200
+ return null;
17201
+ }
17202
+ };
17203
+
17204
+ // dist/session-usage.js
17205
+ function createCodexSessionUsage(environment, closed) {
17206
+ const timing = new CodexResponseTiming();
17207
+ const rawCompleted = "rawResponse/completed";
17208
+ let hideRawCompleted = false;
17209
+ const live = /* @__PURE__ */ new Map();
17210
+ const cache = /* @__PURE__ */ new Map();
17211
+ return {
17212
+ requestOptions({ method, params, initializeParams }) {
17213
+ if (closed.aborted)
17214
+ return null;
17215
+ if (method === "initialize") {
17216
+ const capabilities2 = object2(params.capabilities);
17217
+ const excluded2 = capabilities2?.optOutNotificationMethods;
17218
+ hideRawCompleted = false;
17219
+ if (capabilities2?.experimentalApi !== true || !Array.isArray(excluded2))
17220
+ return null;
17221
+ if (!excluded2.includes(rawCompleted))
17222
+ return null;
17223
+ hideRawCompleted = true;
17224
+ return {
17225
+ capabilities: {
17226
+ ...capabilities2,
17227
+ optOutNotificationMethods: excluded2.filter((method2) => method2 !== rawCompleted)
17228
+ }
17229
+ };
17230
+ }
17231
+ const capabilities = object2(initializeParams?.capabilities);
17232
+ const excluded = capabilities?.optOutNotificationMethods;
17233
+ return method === "thread/start" && capabilities?.experimentalApi === true && !(Array.isArray(excluded) && excluded.includes(rawCompleted)) && (params.experimentalRawEvents === void 0 || params.experimentalRawEvents === false) ? { experimentalRawEvents: true } : null;
17234
+ },
17235
+ shouldForwardNotification(message) {
17236
+ return closed.aborted || !hideRawCompleted || object2(message)?.method !== rawCompleted;
17237
+ },
17238
+ reset() {
17239
+ timing.reset();
17240
+ },
17241
+ observe(message, observedAtMs = Date.now()) {
17242
+ if (closed.aborted)
17243
+ return null;
17244
+ const observation = observeSessionUsage(message);
17245
+ const measured = timing.observe(message, observedAtMs);
17246
+ if (observation?.usage)
17247
+ live.set(observation.sessionId, observation.usage);
17248
+ return measured ? { ...observation, ...measured } : observation;
17249
+ },
17250
+ async read(sessionId, signal) {
17251
+ signal = AbortSignal.any([signal, closed]);
17252
+ signal.throwIfAborted();
17253
+ for (let attempt = 0; ; attempt++) {
17254
+ let snapshot = null;
17255
+ let complete = true;
17256
+ const reader = createCodexUsageStatistics(environment, {
17257
+ sessionId,
17258
+ onTokenCount: (info) => {
17259
+ snapshot = rolloutTokenSnapshot(info) ?? snapshot;
17260
+ },
17261
+ onIncomplete: () => {
17262
+ complete = false;
17263
+ }
17264
+ });
17265
+ const sources = await reader.listSources(signal);
17266
+ const source = sources[0];
17267
+ if (!source)
17268
+ return null;
17269
+ let history = cache.get(sessionId);
17270
+ if (history?.fingerprint !== source.fingerprint) {
17271
+ const entries = await reader.readSource(source.id, signal);
17272
+ const requests = entries.map((entry) => parseHostUsageRequest({
17273
+ requestId: entry.id,
17274
+ historical: true,
17275
+ ...entry.model ? { model: entry.model } : {},
17276
+ inputTokens: entry.inputTokens,
17277
+ outputTokens: entry.outputTokens,
17278
+ ...entry.cachedInputTokens !== void 0 ? { cachedInputTokens: entry.cachedInputTokens } : {},
17279
+ // Codex/OpenAI cache has no separately billed cache creation bucket.
17280
+ cacheWriteInputTokens: entry.cacheWriteInputTokens ?? 0,
17281
+ ...entry.reasoningOutputTokens !== void 0 ? { reasoningOutputTokens: entry.reasoningOutputTokens } : {}
17282
+ }));
17283
+ const inputTokens = requests.reduce((sum, r) => sum + r.inputTokens, 0);
17284
+ const outputTokens = requests.reduce((sum, r) => sum + r.outputTokens, 0);
17285
+ const cachedInputTokens = requests.every((r) => r.cachedInputTokens !== void 0) ? requests.reduce((sum, r) => sum + (r.cachedInputTokens ?? 0), 0) : void 0;
17286
+ const last = requests.at(-1);
17287
+ const usage2 = requests.length ? parseHostUsage({
17288
+ ...snapshot ?? {},
17289
+ inputTokens,
17290
+ outputTokens,
17291
+ totalTokens: inputTokens + outputTokens,
17292
+ ...cachedInputTokens !== void 0 ? { cachedInputTokens } : {},
17293
+ ...complete && cachedInputTokens !== void 0 ? { sessionCacheUsage: { inputTokens, cachedInputTokens } } : {},
17294
+ ...last?.inputTokens && last.cachedInputTokens !== void 0 ? { cacheHitRatePercent: last.cachedInputTokens / last.inputTokens * 100 } : {}
17295
+ }) : snapshot;
17296
+ history = {
17297
+ fingerprint: source.fingerprint,
17298
+ history: { usage: usage2, requests, complete: complete && requests.length > 0 }
17299
+ };
17300
+ cache.set(sessionId, history);
17301
+ }
17302
+ const result = history.history;
17303
+ const latest = live.get(sessionId);
17304
+ const caughtUp = !latest || (latest.inputTokens ?? 0) <= (result.usage?.inputTokens ?? 0) && (latest.outputTokens ?? 0) <= (result.usage?.outputTokens ?? 0);
17305
+ if (!caughtUp && attempt < 2) {
17306
+ await delay(50 * (attempt + 1), void 0, { signal });
17307
+ continue;
17308
+ }
17309
+ return { ...result, complete: result.complete && caughtUp };
17310
+ }
16682
17311
  }
16683
17312
  };
16684
17313
  }
@@ -16689,6 +17318,7 @@ function createUsageStatisticsAdapter(context) {
16689
17318
  const reader = createCodexUsageStatistics(context.environment);
16690
17319
  return {
16691
17320
  harnessId: harnessIdSchema.parse("codex-usage"),
17321
+ sessionUsage: createCodexSessionUsage(context.environment, abort.signal),
16692
17322
  usageStatistics: {
16693
17323
  listSources: (signal) => reader.listSources(AbortSignal.any([signal, abort.signal])),
16694
17324
  readSource: (id, signal) => reader.readSource(id, AbortSignal.any([signal, abort.signal]))