@codexhost/cli-win32-arm64 0.12.2 → 0.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/README.md +1 -1
  2. package/THIRD_PARTY_NOTICES.txt +9 -1
  3. package/app/codexhost-distribution.json +1 -1
  4. package/app/console-server.mjs +319 -64
  5. package/app/console-web.js +5089 -1367
  6. package/app/desktop-controller.mjs +259 -41
  7. package/app/host-runtime.mjs +5735 -1564
  8. package/app/plugins/antigravity/manifest.json +18 -1
  9. package/app/plugins/antigravity/plugin.mjs +282 -41
  10. package/app/plugins/claude-code/manifest.json +28 -0
  11. package/app/plugins/claude-code/plugin.mjs +35300 -23918
  12. package/app/plugins/codebuddy/manifest.json +17 -0
  13. package/app/plugins/codebuddy/plugin.mjs +5971 -5355
  14. package/app/plugins/codex-usage/manifest.json +9 -0
  15. package/app/plugins/codex-usage/plugin.mjs +17324 -0
  16. package/app/plugins/cursor-cli/manifest.json +17 -0
  17. package/app/plugins/cursor-cli/plugin.mjs +357 -76
  18. package/app/plugins/deepseek-harness/assets/icon.svg +1 -0
  19. package/app/plugins/deepseek-harness/manifest.json +35 -2
  20. package/app/plugins/deepseek-harness/plugin.mjs +822 -241
  21. package/app/plugins/enabled.json +2 -1
  22. package/app/plugins/grok/manifest.json +21 -1
  23. package/app/plugins/grok/plugin.mjs +918 -258
  24. package/app/plugins/hermes/assets/icon.png +0 -0
  25. package/app/plugins/hermes/manifest.json +30 -2
  26. package/app/plugins/hermes/plugin.mjs +284 -32
  27. package/app/plugins/kimi-code/assets/icon.svg +5 -0
  28. package/app/plugins/kimi-code/manifest.json +25 -2
  29. package/app/plugins/kimi-code/plugin.mjs +561 -35
  30. package/app/plugins/kiro-cli/assets/kiro-agent.svg +11 -0
  31. package/app/plugins/kiro-cli/manifest.json +18 -1
  32. package/app/plugins/kiro-cli/plugin.mjs +290 -21
  33. package/app/plugins/omp/manifest.json +21 -1
  34. package/app/plugins/omp/plugin.mjs +2765 -1960
  35. package/app/plugins/opencode/manifest.json +18 -1
  36. package/app/plugins/opencode/plugin.mjs +723 -146
  37. package/app/plugins/pi/manifest.json +32 -0
  38. package/app/plugins/pi/plugin.mjs +2908 -2312
  39. package/app/plugins/qoder/manifest.json +18 -1
  40. package/app/plugins/qoder/plugin.mjs +634 -80
  41. package/app/plugins/qoder-cn/manifest.json +18 -1
  42. package/app/plugins/qoder-cn/plugin.mjs +634 -80
  43. package/app/plugins/workbuddy/manifest.json +18 -0
  44. package/app/plugins/workbuddy/plugin.mjs +6361 -5747
  45. package/app/plugins/zcode/manifest.json +19 -1
  46. package/app/plugins/zcode/plugin.mjs +1098 -259
  47. package/app/renderer-extension.js +13080 -8884
  48. package/bin/codexhost.exe +0 -0
  49. package/libexec/codexhost-node-repl.exe +0 -0
  50. package/libexec/codexhost-shim.exe +0 -0
  51. package/libexec/codexhost-updater.exe +0 -0
  52. package/licenses/Cordis-LICENSE.txt +21 -0
  53. package/licenses/Cosmokit-LICENSE.txt +21 -0
  54. package/package.json +1 -1
  55. package/app/plugins/kiro-cli/assets/icon.svg +0 -5
@@ -396,7 +396,7 @@ async function npmInstallation(command, packages, environment) {
396
396
  const npm = resolveHarnessExecutable({ id: "npm", command: "npm", installRoots: { posix: [], windows: [] } }, { environment: env });
397
397
  let globalInstall = modules === globalRoot;
398
398
  if (process.platform === "win32") {
399
- const project = (await Promise.all(["package.json", "package-lock.json", "npm-shrinkwrap.json"].map((file2) => readFile(path4.join(prefix, file2), "utf8").then(() => true, (error51) => error51.code !== "ENOENT")))).some(Boolean);
399
+ const project2 = (await Promise.all(["package.json", "package-lock.json", "npm-shrinkwrap.json"].map((file2) => readFile(path4.join(prefix, file2), "utf8").then(() => true, (error51) => error51.code !== "ENOENT")))).some(Boolean);
400
400
  const bins = typeof metadata.bin === "string" ? { [name.split("/").at(-1) ?? name]: metadata.bin } : metadata.bin ?? {};
401
401
  const launchers = await Promise.all(Object.entries(bins).map(async ([bin, entry]) => {
402
402
  if (path4.basename(bin) !== bin || entry.includes(".."))
@@ -407,7 +407,7 @@ async function npmInstallation(command, packages, environment) {
407
407
  const shim = await readFile(path4.join(prefix, `${bin}.cmd`), "utf8").catch(() => "");
408
408
  return referencesNpmBin(shim, name, entry);
409
409
  }));
410
- globalInstall = globalInstall && !project && launchers.some(Boolean);
410
+ globalInstall = globalInstall && !project2 && launchers.some(Boolean);
411
411
  }
412
412
  const canUpdate = globalInstall && !!npm;
413
413
  return {
@@ -487,10 +487,66 @@ function createHarnessInstaller(environment, source) {
487
487
  };
488
488
  }
489
489
 
490
- // dist/opencode-adapter.js
491
- import { randomUUID } from "node:crypto";
492
- import fs4 from "node:fs";
493
- import path8 from "node:path";
490
+ // dist/usage-statistics.js
491
+ import os2 from "node:os";
492
+ import path5 from "node:path";
493
+ import { lstat } from "node:fs/promises";
494
+
495
+ // ../../harness-adapter/dist/usage-statistics.js
496
+ var MAX_SESSION_ID_LENGTH = 512;
497
+ var MAX_CWD_LENGTH = 4096;
498
+ var MAX_SESSION_TITLE_LENGTH = 200;
499
+ function usageSessionTitle(value) {
500
+ if (typeof value !== "string")
501
+ return void 0;
502
+ const normalized = value.replaceAll("\0", "").replaceAll(/\s+/gu, " ").trim();
503
+ if (!normalized)
504
+ return void 0;
505
+ if (normalized.length <= MAX_SESSION_TITLE_LENGTH)
506
+ return normalized;
507
+ const characters = [...normalized];
508
+ if (characters.length <= MAX_SESSION_TITLE_LENGTH)
509
+ return normalized;
510
+ return `${characters.slice(0, MAX_SESSION_TITLE_LENGTH - 1).join("").trimEnd()}…`;
511
+ }
512
+ function sessionIdOk(value) {
513
+ return typeof value === "string" && value.length > 0 && value.length <= MAX_SESSION_ID_LENGTH;
514
+ }
515
+ function cwdOk(value) {
516
+ return typeof value === "string" && value.length > 0 && value.length <= MAX_CWD_LENGTH && !value.includes("\0") && (value.startsWith("/") || /^[A-Za-z]:[\\/]/u.test(value) || value.startsWith("\\\\"));
517
+ }
518
+ function withUsageSession(entry, session) {
519
+ const sessionId = sessionIdOk(session.sessionId) ? session.sessionId : void 0;
520
+ const cwd = cwdOk(session.cwd) ? session.cwd : void 0;
521
+ const sessionTitle = sessionId !== void 0 ? usageSessionTitle(session.title) : void 0;
522
+ if (sessionId === void 0 && cwd === void 0)
523
+ return entry;
524
+ return {
525
+ ...entry,
526
+ ...sessionId !== void 0 ? { sessionId } : {},
527
+ ...cwd !== void 0 ? { cwd } : {},
528
+ ...sessionTitle !== void 0 ? { sessionTitle } : {}
529
+ };
530
+ }
531
+ function usageEntryFromRequest(request, occurredAtMs) {
532
+ if (!Number.isSafeInteger(occurredAtMs) || occurredAtMs <= 0)
533
+ return null;
534
+ return {
535
+ id: request.requestId,
536
+ occurredAtMs,
537
+ ...request.model !== void 0 ? { model: request.model } : {},
538
+ inputTokens: request.inputTokens,
539
+ ...request.cachedInputTokens !== void 0 ? { cachedInputTokens: request.cachedInputTokens } : {},
540
+ ...request.cacheWriteInputTokens !== void 0 ? { cacheWriteInputTokens: request.cacheWriteInputTokens } : {},
541
+ ...request.cacheWrite1hInputTokens !== void 0 ? { cacheWrite1hInputTokens: request.cacheWrite1hInputTokens } : {},
542
+ outputTokens: request.outputTokens,
543
+ ...request.reasoningOutputTokens !== void 0 ? { reasoningOutputTokens: request.reasoningOutputTokens } : {}
544
+ };
545
+ }
546
+ function nativeTimeMs(value) {
547
+ const time3 = typeof value === "number" ? value : typeof value === "string" ? Date.parse(value) : Number.NaN;
548
+ return Number.isFinite(time3) && time3 > 0 ? Math.floor(time3) : null;
549
+ }
494
550
 
495
551
  // ../../../node_modules/zod/v4/classic/external.js
496
552
  var external_exports = {};
@@ -1258,10 +1314,10 @@ function mergeDefs(...defs) {
1258
1314
  function cloneDef(schema) {
1259
1315
  return mergeDefs(schema._zod.def);
1260
1316
  }
1261
- function getElementAtPath(obj, path13) {
1262
- if (!path13)
1317
+ function getElementAtPath(obj, path14) {
1318
+ if (!path14)
1263
1319
  return obj;
1264
- return path13.reduce((acc, key) => acc?.[key], obj);
1320
+ return path14.reduce((acc, key) => acc?.[key], obj);
1265
1321
  }
1266
1322
  function promiseAllObject(promisesObj) {
1267
1323
  const keys = Object.keys(promisesObj);
@@ -1670,11 +1726,11 @@ function explicitlyAborted(x, startIndex = 0) {
1670
1726
  }
1671
1727
  return false;
1672
1728
  }
1673
- function prefixIssues(path13, issues) {
1729
+ function prefixIssues(path14, issues) {
1674
1730
  return issues.map((iss) => {
1675
1731
  var _a3;
1676
1732
  (_a3 = iss).path ?? (_a3.path = []);
1677
- iss.path.unshift(path13);
1733
+ iss.path.unshift(path14);
1678
1734
  return iss;
1679
1735
  });
1680
1736
  }
@@ -1821,16 +1877,16 @@ function flattenError(error51, mapper = (issue2) => issue2.message) {
1821
1877
  }
1822
1878
  function formatError(error51, mapper = (issue2) => issue2.message) {
1823
1879
  const fieldErrors = { _errors: [] };
1824
- const processError = (error52, path13 = []) => {
1880
+ const processError = (error52, path14 = []) => {
1825
1881
  for (const issue2 of error52.issues) {
1826
1882
  if (issue2.code === "invalid_union" && issue2.errors.length) {
1827
- issue2.errors.map((issues) => processError({ issues }, [...path13, ...issue2.path]));
1883
+ issue2.errors.map((issues) => processError({ issues }, [...path14, ...issue2.path]));
1828
1884
  } else if (issue2.code === "invalid_key") {
1829
- processError({ issues: issue2.issues }, [...path13, ...issue2.path]);
1885
+ processError({ issues: issue2.issues }, [...path14, ...issue2.path]);
1830
1886
  } else if (issue2.code === "invalid_element") {
1831
- processError({ issues: issue2.issues }, [...path13, ...issue2.path]);
1887
+ processError({ issues: issue2.issues }, [...path14, ...issue2.path]);
1832
1888
  } else {
1833
- const fullpath = [...path13, ...issue2.path];
1889
+ const fullpath = [...path14, ...issue2.path];
1834
1890
  if (fullpath.length === 0) {
1835
1891
  fieldErrors._errors.push(mapper(issue2));
1836
1892
  } else {
@@ -1857,17 +1913,17 @@ function formatError(error51, mapper = (issue2) => issue2.message) {
1857
1913
  }
1858
1914
  function treeifyError(error51, mapper = (issue2) => issue2.message) {
1859
1915
  const result = { errors: [] };
1860
- const processError = (error52, path13 = []) => {
1916
+ const processError = (error52, path14 = []) => {
1861
1917
  var _a3, _b;
1862
1918
  for (const issue2 of error52.issues) {
1863
1919
  if (issue2.code === "invalid_union" && issue2.errors.length) {
1864
- issue2.errors.map((issues) => processError({ issues }, [...path13, ...issue2.path]));
1920
+ issue2.errors.map((issues) => processError({ issues }, [...path14, ...issue2.path]));
1865
1921
  } else if (issue2.code === "invalid_key") {
1866
- processError({ issues: issue2.issues }, [...path13, ...issue2.path]);
1922
+ processError({ issues: issue2.issues }, [...path14, ...issue2.path]);
1867
1923
  } else if (issue2.code === "invalid_element") {
1868
- processError({ issues: issue2.issues }, [...path13, ...issue2.path]);
1924
+ processError({ issues: issue2.issues }, [...path14, ...issue2.path]);
1869
1925
  } else {
1870
- const fullpath = [...path13, ...issue2.path];
1926
+ const fullpath = [...path14, ...issue2.path];
1871
1927
  if (fullpath.length === 0) {
1872
1928
  result.errors.push(mapper(issue2));
1873
1929
  continue;
@@ -1899,8 +1955,8 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
1899
1955
  }
1900
1956
  function toDotPath(_path) {
1901
1957
  const segs = [];
1902
- const path13 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
1903
- for (const seg of path13) {
1958
+ const path14 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
1959
+ for (const seg of path14) {
1904
1960
  if (typeof seg === "number")
1905
1961
  segs.push(`[${seg}]`);
1906
1962
  else if (typeof seg === "symbol")
@@ -5128,8 +5184,8 @@ function az_default() {
5128
5184
  }
5129
5185
 
5130
5186
  // ../../../node_modules/zod/v4/locales/be.js
5131
- function getBelarusianPlural(count, one, few, many) {
5132
- const absCount = Math.abs(count);
5187
+ function getBelarusianPlural(count2, one, few, many) {
5188
+ const absCount = Math.abs(count2);
5133
5189
  const lastDigit = absCount % 10;
5134
5190
  const lastTwoDigits = absCount % 100;
5135
5191
  if (lastTwoDigits >= 11 && lastTwoDigits <= 19) {
@@ -7207,8 +7263,8 @@ function hu_default() {
7207
7263
  }
7208
7264
 
7209
7265
  // ../../../node_modules/zod/v4/locales/hy.js
7210
- function getArmenianPlural(count, one, many) {
7211
- return Math.abs(count) === 1 ? one : many;
7266
+ function getArmenianPlural(count2, one, many) {
7267
+ return Math.abs(count2) === 1 ? one : many;
7212
7268
  }
7213
7269
  function withDefiniteArticle(word) {
7214
7270
  if (!word)
@@ -9336,8 +9392,8 @@ function ro_default() {
9336
9392
  }
9337
9393
 
9338
9394
  // ../../../node_modules/zod/v4/locales/ru.js
9339
- function getRussianPlural(count, one, few, many) {
9340
- const absCount = Math.abs(count);
9395
+ function getRussianPlural(count2, one, few, many) {
9396
+ const absCount = Math.abs(count2);
9341
9397
  const lastDigit = absCount % 10;
9342
9398
  const lastTwoDigits = absCount % 100;
9343
9399
  if (lastTwoDigits >= 11 && lastTwoDigits <= 19) {
@@ -14592,13 +14648,13 @@ function resolveRef(ref, ctx) {
14592
14648
  if (!ref.startsWith("#")) {
14593
14649
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
14594
14650
  }
14595
- const path13 = ref.slice(1).split("/").filter(Boolean);
14596
- if (path13.length === 0) {
14651
+ const path14 = ref.slice(1).split("/").filter(Boolean);
14652
+ if (path14.length === 0) {
14597
14653
  return ctx.rootSchema;
14598
14654
  }
14599
14655
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
14600
- if (path13[0] === defsKey) {
14601
- const key = path13[1];
14656
+ if (path14[0] === defsKey) {
14657
+ const key = path14[1];
14602
14658
  if (!key || !ctx.defs[key]) {
14603
14659
  throw new Error(`Reference not found: ${ref}`);
14604
14660
  }
@@ -15006,6 +15062,15 @@ function date4(params) {
15006
15062
  // ../../../node_modules/zod/v4/classic/external.js
15007
15063
  config(en_default());
15008
15064
 
15065
+ // ../../shared-contracts/dist/delegation-read.js
15066
+ var REMOTE_THREAD_REPLY_MAX_BYTES = 16 * 1024 * 1024;
15067
+ var delegationReadParamsSchema = external_exports.strictObject({
15068
+ threadId: external_exports.string().min(1).max(1024),
15069
+ view: external_exports.enum(["result", "messages"]),
15070
+ cursor: external_exports.string().min(1).max(8192).optional(),
15071
+ limit: external_exports.number().int().min(1).max(100).optional()
15072
+ });
15073
+
15009
15074
  // ../../shared-contracts/dist/credential-imports.js
15010
15075
  var credentialSourceSchema = external_exports.object({
15011
15076
  id: external_exports.string().min(1).max(256),
@@ -15076,6 +15141,160 @@ var harnessDisplaySetSchema = external_exports.strictObject({
15076
15141
  initializeOnly: external_exports.boolean().optional()
15077
15142
  });
15078
15143
 
15144
+ // ../../shared-contracts/dist/model-price-overrides.js
15145
+ var usdPerMillionTokens = external_exports.number().finite().nonnegative();
15146
+ var modelPriceOverrideSchema = external_exports.strictObject({
15147
+ input: usdPerMillionTokens,
15148
+ output: usdPerMillionTokens,
15149
+ cacheRead: usdPerMillionTokens.optional(),
15150
+ cacheWrite: usdPerMillionTokens.optional(),
15151
+ cacheWrite1h: usdPerMillionTokens.optional()
15152
+ });
15153
+ var modelPriceKeySchema = external_exports.string().max(512).refine((key) => key.trim() === key && key.length > 0, "Model ID is required");
15154
+ var modelPriceOverridesSchema = external_exports.strictObject({
15155
+ /** Absolute path of the Host's `pricing.json`. */
15156
+ path: external_exports.string(),
15157
+ entries: external_exports.array(external_exports.strictObject({ key: external_exports.string(), price: modelPriceOverrideSchema })),
15158
+ /** Why the existing file is unusable; the Host then ignores it and refuses to overwrite it. */
15159
+ error: external_exports.string().nullable()
15160
+ });
15161
+ var modelPriceOverridesGetSchema = external_exports.strictObject({});
15162
+ var modelPriceOverridesSetSchema = external_exports.strictObject({
15163
+ key: modelPriceKeySchema,
15164
+ price: modelPriceOverrideSchema.nullable(),
15165
+ previousKey: modelPriceKeySchema.optional()
15166
+ });
15167
+ var modelPriceDefaultParamsSchema = external_exports.strictObject({
15168
+ model: modelPriceKeySchema
15169
+ });
15170
+ var modelPriceSuggestionSchema = external_exports.strictObject({
15171
+ /** Verified from catalog canonical links, not inferred from names or equal prices. */
15172
+ official: external_exports.boolean().optional(),
15173
+ canonicalModelId: external_exports.string().min(1).optional(),
15174
+ provider: external_exports.string().min(1).max(512),
15175
+ model: external_exports.string().min(1).max(512),
15176
+ price: modelPriceOverrideSchema
15177
+ });
15178
+ var modelPriceDefaultResultSchema = external_exports.strictObject({
15179
+ price: modelPriceOverrideSchema.nullable(),
15180
+ /** Local catalog suggestions only; never used automatically for pricing. */
15181
+ suggestions: external_exports.array(modelPriceSuggestionSchema).max(6).optional()
15182
+ });
15183
+
15184
+ // ../../shared-contracts/dist/usage-statistics.js
15185
+ var USAGE_STATISTICS_RANGES = ["today", "7d", "30d", "90d", "all"];
15186
+ var count = external_exports.number().int().min(0).max(Number.MAX_SAFE_INTEGER);
15187
+ var localDate = external_exports.string().regex(/^\d{4}-\d{2}-\d{2}$/u);
15188
+ var harness = external_exports.string().min(1).max(128);
15189
+ var model = external_exports.string().min(1).max(512);
15190
+ var project = external_exports.string().min(1).max(4096);
15191
+ var usageStatisticsParamsSchema = external_exports.strictObject({
15192
+ range: external_exports.enum(USAGE_STATISTICS_RANGES),
15193
+ harness: harness.optional(),
15194
+ model: model.nullable().optional(),
15195
+ project: project.nullable().optional(),
15196
+ /** One local date inside the range. */
15197
+ date: localDate.optional()
15198
+ });
15199
+ var usageStatisticsTotalsSchema = external_exports.strictObject({
15200
+ requests: count,
15201
+ inputTokens: count,
15202
+ cachedInputTokens: count,
15203
+ cacheWriteInputTokens: count,
15204
+ cacheKnownInputTokens: count,
15205
+ outputTokens: count,
15206
+ reasoningOutputTokens: count,
15207
+ /** USD at the public price, summed over the priced requests only. */
15208
+ costUsd: external_exports.number().finite().min(0),
15209
+ /** Requests without a price (or the cache data the price needs); not in `costUsd`. */
15210
+ unpricedRequests: count,
15211
+ /**
15212
+ * Requests whose storage gave no token counts: counted as requests, with neither tokens nor
15213
+ * cost, and not as unpriced.
15214
+ */
15215
+ unmeteredRequests: count
15216
+ });
15217
+ var totals = usageStatisticsTotalsSchema.shape;
15218
+ var usageStatisticsSessionSchema = external_exports.strictObject({
15219
+ harness,
15220
+ sessionId: external_exports.string().min(1).max(512),
15221
+ project: project.nullable(),
15222
+ /** Persisted Desktop title for a mapped Session, otherwise its native name; may be absent. */
15223
+ title: external_exports.string().min(1).max(200).optional(),
15224
+ /** Models the session used, most requests first. */
15225
+ models: external_exports.array(model).max(16),
15226
+ firstAtMs: count,
15227
+ lastAtMs: count,
15228
+ ...totals
15229
+ });
15230
+ var usageStatisticsResultSchema = external_exports.strictObject({
15231
+ range: external_exports.enum(USAGE_STATISTICS_RANGES),
15232
+ /** First local date of the range; null for all time. */
15233
+ from: localDate.nullable(),
15234
+ to: localDate,
15235
+ /** The filters these figures apply, echoed so a late answer to older filters is recognised. */
15236
+ filters: external_exports.strictObject({
15237
+ harness: harness.nullable(),
15238
+ model: model.nullable(),
15239
+ unknownModel: external_exports.boolean(),
15240
+ project: project.nullable(),
15241
+ unknownProject: external_exports.boolean(),
15242
+ date: localDate.nullable()
15243
+ }),
15244
+ /** Reading of native storage: incomplete while the first read, or a refresh, is under way. */
15245
+ reading: external_exports.strictObject({ complete: external_exports.boolean(), sources: count, read: count }),
15246
+ /** Harnesses whose storage could not be read; their part is missing, the rest is complete. */
15247
+ failures: external_exports.array(external_exports.strictObject({ harness, message: external_exports.string().max(2048) })).max(256),
15248
+ /** Every value seen in the range, ignoring the filters, to choose filters from. */
15249
+ options: external_exports.strictObject({
15250
+ harnesses: external_exports.array(harness).max(256),
15251
+ models: external_exports.array(model.nullable()).max(4096),
15252
+ projects: external_exports.array(project.nullable()).max(4096)
15253
+ }),
15254
+ /** Current display metadata, scoped by Harness. Never a pricing or filter identity.
15255
+ * Optional for older Hosts; missing/conflicting labels fall back to the native model ID.
15256
+ */
15257
+ modelLabels: external_exports.array(external_exports.strictObject({ harness, model, label: model })).max(4096).optional(),
15258
+ /** Native credits, all filters applied, never combined across Harnesses or treated as USD.
15259
+ * Only groups with at least one reported value appear; reportedRequests exposes partial data.
15260
+ */
15261
+ credits: external_exports.array(external_exports.strictObject({
15262
+ harness,
15263
+ model: model.nullable(),
15264
+ credits: external_exports.number().finite().min(0),
15265
+ reportedRequests: count,
15266
+ requests: count
15267
+ })).max(4096).optional(),
15268
+ /** All filters applied. */
15269
+ totals: usageStatisticsTotalsSchema,
15270
+ /** Each date and Harness of the range: every filter except the day. */
15271
+ daily: external_exports.array(external_exports.strictObject({ date: localDate, harness, ...totals })).max(2e5),
15272
+ /** Local weekday (0 = Sunday) and hour, all filters applied. */
15273
+ hourly: external_exports.array(external_exports.strictObject({
15274
+ weekday: external_exports.number().int().min(0).max(6),
15275
+ hour: external_exports.number().int().min(0).max(23),
15276
+ ...totals
15277
+ })).max(168),
15278
+ byHarness: external_exports.array(external_exports.strictObject({ harness, ...totals })).max(256),
15279
+ byModel: external_exports.array(external_exports.strictObject({
15280
+ model: model.nullable(),
15281
+ ...totals,
15282
+ /** Requests priced by what the Harness recorded they cost, not by a price list. */
15283
+ harnessPricedRequests: count
15284
+ })).max(4096),
15285
+ byProject: external_exports.array(external_exports.strictObject({ project: project.nullable(), ...totals })).max(4096),
15286
+ /**
15287
+ * The sessions that spent the most, by cost and by tokens without cache, all filters applied;
15288
+ * requests without a native session are not listed here but count everywhere else.
15289
+ */
15290
+ sessions: external_exports.array(usageStatisticsSessionSchema).max(100),
15291
+ /**
15292
+ * The sessions last active in the range, newest first, all filters applied. Optional for older
15293
+ * Hosts; missing is treated as no list rather than no sessions.
15294
+ */
15295
+ recentSessions: external_exports.array(usageStatisticsSessionSchema).max(100).optional()
15296
+ });
15297
+
15079
15298
  // ../../shared-contracts/dist/ids.js
15080
15299
  var opaqueIdSchema = external_exports.string().refine((value) => value.trim().length > 0, {
15081
15300
  message: "Identifier must not be empty or whitespace"
@@ -15123,6 +15342,10 @@ var CONSOLE_HOST_METHODS = Object.freeze([
15123
15342
  "codexhost/account/list",
15124
15343
  "codexhost/account/refresh",
15125
15344
  "codexhost/account/usage/inspect",
15345
+ "codexhost/usage/model-prices/get",
15346
+ "codexhost/usage/model-prices/set",
15347
+ "codexhost/usage/model-prices/default",
15348
+ "codexhost/usage/statistics/get",
15126
15349
  "codexhost/update/check",
15127
15350
  "codexhost/update/start",
15128
15351
  "codexhost/update/status"
@@ -15187,6 +15410,7 @@ var threadUsageSnapshotSchema = external_exports.object({
15187
15410
  cacheWriteInputTokens: nonNegativeSafeIntegerSchema.optional(),
15188
15411
  outputTokens: nonNegativeSafeIntegerSchema.optional(),
15189
15412
  outputTokensPerSecond: finiteNonNegativeNumberSchema.optional(),
15413
+ apiOutputTokensPerSecond: finiteNonNegativeNumberSchema.optional(),
15190
15414
  reasoningOutputTokens: nonNegativeSafeIntegerSchema.optional(),
15191
15415
  totalTokens: nonNegativeSafeIntegerSchema.optional(),
15192
15416
  totalCostUsd: finiteNonNegativeNumberSchema.optional(),
@@ -15198,7 +15422,11 @@ var threadUsageSnapshotSchema = external_exports.object({
15198
15422
  planFiveHourUsedPercent: cacheHitRatePercentSchema.optional(),
15199
15423
  planFiveHourResetsAtUnix: nonNegativeSafeIntegerSchema.optional(),
15200
15424
  planSevenDayUsedPercent: cacheHitRatePercentSchema.optional(),
15201
- planSevenDayResetsAtUnix: nonNegativeSafeIntegerSchema.optional()
15425
+ planSevenDayResetsAtUnix: nonNegativeSafeIntegerSchema.optional(),
15426
+ sessionCacheHitRatePercent: cacheHitRatePercentSchema.optional(),
15427
+ timeToFirstOutputMs: nonNegativeSafeIntegerSchema.optional(),
15428
+ costSource: external_exports.enum(["publicPrice", "native"]).optional(),
15429
+ unpricedModels: external_exports.array(external_exports.string().min(1)).min(1).optional()
15202
15430
  }).strict().superRefine((usage, context) => {
15203
15431
  if (Object.keys(usage).length === 0) {
15204
15432
  context.addIssue({ code: "custom", message: "Thread Usage must contain a reliable field" });
@@ -15219,6 +15447,20 @@ var threadUsageSnapshotSchema = external_exports.object({
15219
15447
  path: ["contextWindowTokens"]
15220
15448
  });
15221
15449
  }
15450
+ if (usage.costSource !== void 0 && usage.totalCostUsd === void 0) {
15451
+ context.addIssue({
15452
+ code: "custom",
15453
+ message: "Thread Usage costSource must be provided with totalCostUsd",
15454
+ path: ["costSource"]
15455
+ });
15456
+ }
15457
+ if (usage.unpricedModels !== void 0 && usage.costSource !== "publicPrice") {
15458
+ context.addIssue({
15459
+ code: "custom",
15460
+ message: "Thread Usage unpricedModels must be provided with a publicPrice cost",
15461
+ path: ["unpricedModels"]
15462
+ });
15463
+ }
15222
15464
  if (usage.planFiveHourResetsAtUnix !== void 0 && usage.planFiveHourUsedPercent === void 0) {
15223
15465
  context.addIssue({
15224
15466
  code: "custom",
@@ -15473,27 +15715,27 @@ var harnessModelCatalogSchema = external_exports.object({
15473
15715
  }).strict().superRefine((catalog, context) => {
15474
15716
  const refs = /* @__PURE__ */ new Set();
15475
15717
  const thinkingIds = new Set(catalog.thinkingOptions.map(({ id }) => id));
15476
- for (const [index, model] of catalog.models.entries()) {
15477
- if (refs.has(model.ref.id)) {
15718
+ for (const [index, model2] of catalog.models.entries()) {
15719
+ if (refs.has(model2.ref.id)) {
15478
15720
  context.addIssue({
15479
15721
  code: "custom",
15480
15722
  message: "Model Catalog refs must be unique",
15481
15723
  path: ["models", index, "ref", "id"]
15482
15724
  });
15483
15725
  }
15484
- refs.add(model.ref.id);
15485
- if (model.fastModel) {
15486
- if (refs.has(model.fastModel.id) || catalog.models.some((entry) => entry.ref.id === model.fastModel?.id)) {
15726
+ refs.add(model2.ref.id);
15727
+ if (model2.fastModel) {
15728
+ if (refs.has(model2.fastModel.id) || catalog.models.some((entry) => entry.ref.id === model2.fastModel?.id)) {
15487
15729
  context.addIssue({
15488
15730
  code: "custom",
15489
15731
  message: "Fast Model refs must be distinct and unique",
15490
15732
  path: ["models", index, "fastModel"]
15491
15733
  });
15492
15734
  }
15493
- refs.add(model.fastModel.id);
15735
+ refs.add(model2.fastModel.id);
15494
15736
  }
15495
15737
  const supportedThinkingIds = /* @__PURE__ */ new Set();
15496
- for (const [optionIndex, optionId] of (model.supportedThinkingOptionIds ?? []).entries()) {
15738
+ for (const [optionIndex, optionId] of (model2.supportedThinkingOptionIds ?? []).entries()) {
15497
15739
  if (supportedThinkingIds.has(optionId)) {
15498
15740
  context.addIssue({
15499
15741
  code: "custom",
@@ -15687,13 +15929,40 @@ var HARNESS_PLUGIN_LIMIT = 128;
15687
15929
  var harnessPluginIdSchema = external_exports.string().min(1).max(128).regex(/^[a-z][a-z0-9]*(?:[._-][a-z0-9]+)*$/u).refine((id) => id !== "codex", "The official Codex identity is reserved").pipe(harnessIdSchema);
15688
15930
  var relativeResourceSchema = external_exports.string().min(1).max(512).refine((value) => !/[\\\u0000:#?]/u.test(value) && !value.startsWith("/") && !value.split("/").some((part) => part === ".." || part === ""), "Plugin resources must be relative paths inside the plugin");
15689
15931
  var documentationUrlSchema = external_exports.url().refine((value) => /^https:\/\/[^/?#@]+(?:[/?#]|$)/iu.test(value), "Plugin documentation links must be credential-free HTTPS URLs");
15932
+ var localizedTextSchema = external_exports.object({ en: external_exports.string().max(4096), "zh-CN": external_exports.string().max(4096).optional() }).strict();
15933
+ var harnessInstallationGuideSchema = external_exports.object({
15934
+ commands: external_exports.array(external_exports.object({ terminal: external_exports.string().max(256), command: external_exports.string().max(4096) }).strict()).max(8),
15935
+ before: localizedTextSchema.optional(),
15936
+ after: localizedTextSchema.optional(),
15937
+ downloads: external_exports.array(external_exports.object({ label: external_exports.string().max(128), url: documentationUrlSchema }).strict()).max(8).optional()
15938
+ }).strict();
15690
15939
  var pluginPresentationShape = {
15691
15940
  id: harnessPluginIdSchema,
15692
15941
  name: external_exports.string().trim().min(1).max(128),
15693
15942
  version: external_exports.string().min(1).max(128),
15943
+ /** Capability-only plugin; omitted for existing session-capable plugins. */
15944
+ kind: external_exports.literal("usage").optional(),
15945
+ /** Original image styling or bounded vector paths; never arbitrary CSS/SVG markup. */
15946
+ iconStyle: external_exports.object({
15947
+ vector: external_exports.object({
15948
+ viewBox: external_exports.string().max(128).regex(/^-?\d+(?:\.\d+)?(?: +-?\d+(?:\.\d+)?){3}$/u),
15949
+ color: external_exports.string().regex(/^(?:currentColor|#[a-f0-9]{6})$/iu),
15950
+ paths: external_exports.array(external_exports.object({
15951
+ d: external_exports.string().min(1).max(32768).regex(/^[MmLlHhVvCcSsQqTtAaZz\d.,+eE\s-]+$/u),
15952
+ fillRule: external_exports.enum(["evenodd", "nonzero"]).optional(),
15953
+ fill: external_exports.string().regex(/^(?:currentColor|#[a-f0-9]{6})$/iu).optional()
15954
+ }).strict()).min(1).max(32)
15955
+ }).strict().optional(),
15956
+ background: external_exports.string().regex(/^#[a-f0-9]{6}$/iu).optional(),
15957
+ borderRadius: external_exports.number().min(0).max(50).optional(),
15958
+ paddingRatio: external_exports.number().min(0).max(0.25).optional()
15959
+ }).strict().optional(),
15960
+ installation: harnessInstallationGuideSchema.optional(),
15961
+ notice: localizedTextSchema.optional(),
15694
15962
  /** The factory accepts a persisted local entrypoint through its construction context. */
15695
15963
  launchCommand: external_exports.literal(true).optional(),
15696
15964
  links: external_exports.object({
15965
+ website: documentationUrlSchema.optional(),
15697
15966
  documentation: documentationUrlSchema.optional(),
15698
15967
  installation: documentationUrlSchema.optional()
15699
15968
  }).strict().optional()
@@ -16112,6 +16381,11 @@ var codexSshDraftSchema = external_exports.strictObject({
16112
16381
  });
16113
16382
  var remoteConnectionsRequestSchema = external_exports.discriminatedUnion("action", [
16114
16383
  external_exports.strictObject({ action: external_exports.literal("list") }),
16384
+ external_exports.strictObject({
16385
+ action: external_exports.literal("read-thread"),
16386
+ hostId: hostId.refine((id) => id !== "local"),
16387
+ input: delegationReadParamsSchema
16388
+ }),
16115
16389
  external_exports.strictObject({
16116
16390
  action: external_exports.literal("save"),
16117
16391
  draft: codexSshDraftSchema,
@@ -16250,10 +16524,12 @@ var tokenFields = [
16250
16524
  ];
16251
16525
  var safeIntegerFields = [
16252
16526
  "planFiveHourResetsAtUnix",
16253
- "planSevenDayResetsAtUnix"
16527
+ "planSevenDayResetsAtUnix",
16528
+ "timeToFirstOutputMs"
16254
16529
  ];
16255
16530
  var percentFields = [
16256
16531
  "cacheHitRatePercent",
16532
+ "sessionCacheHitRatePercent",
16257
16533
  "planFiveHourUsedPercent",
16258
16534
  "planSevenDayUsedPercent"
16259
16535
  ];
@@ -16261,10 +16537,14 @@ var usageFields = /* @__PURE__ */ new Set([
16261
16537
  ...tokenFields,
16262
16538
  ...safeIntegerFields,
16263
16539
  ...percentFields,
16540
+ "sessionCacheUsage",
16264
16541
  "totalCostUsd",
16265
16542
  "totalCredits",
16266
16543
  "contextUsagePercent",
16267
- "outputTokensPerSecond"
16544
+ "outputTokensPerSecond",
16545
+ "apiOutputTokensPerSecond",
16546
+ "costSource",
16547
+ "unpricedModels"
16268
16548
  ]);
16269
16549
  function isRecord(value) {
16270
16550
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -16280,7 +16560,11 @@ function parseHostUsage(value) {
16280
16560
  throw new Error(`Harness Usage contains unknown field '${key}'`);
16281
16561
  }
16282
16562
  }
16283
- for (const field of ["totalCredits", "contextUsagePercent"]) {
16563
+ for (const field of [
16564
+ "totalCredits",
16565
+ "contextUsagePercent",
16566
+ "apiOutputTokensPerSecond"
16567
+ ]) {
16284
16568
  const candidate2 = value[field];
16285
16569
  if (candidate2 !== void 0 && (typeof candidate2 !== "number" || !Number.isFinite(candidate2) || candidate2 < 0)) {
16286
16570
  throw new Error(`Harness Usage '${field}' must be a finite non-negative number`);
@@ -16310,6 +16594,12 @@ function parseHostUsage(value) {
16310
16594
  throw new Error(`Harness Usage '${field}' must be between 0 and 100`);
16311
16595
  }
16312
16596
  }
16597
+ if (value.sessionCacheUsage !== void 0) {
16598
+ const totals2 = value.sessionCacheUsage;
16599
+ if (!isRecord(totals2) || Object.keys(totals2).some((key) => key !== "inputTokens" && key !== "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) {
16600
+ throw new Error("Harness Usage 'sessionCacheUsage' must contain complete bounded token totals");
16601
+ }
16602
+ }
16313
16603
  const hasContextUsed = value.contextUsedTokens !== void 0;
16314
16604
  const hasContextWindow = value.contextWindowTokens !== void 0;
16315
16605
  if (hasContextUsed !== hasContextWindow) {
@@ -16324,8 +16614,224 @@ function parseHostUsage(value) {
16324
16614
  if (value.planSevenDayResetsAtUnix !== void 0 && value.planSevenDayUsedPercent === void 0) {
16325
16615
  throw new Error("Harness Usage 'planSevenDayResetsAtUnix' must be provided with 'planSevenDayUsedPercent'");
16326
16616
  }
16617
+ if (value.costSource !== void 0) {
16618
+ if (value.costSource !== "publicPrice" && value.costSource !== "native") {
16619
+ throw new Error("Harness Usage 'costSource' must be 'publicPrice' or 'native'");
16620
+ }
16621
+ if (value.totalCostUsd === void 0) {
16622
+ throw new Error("Harness Usage 'costSource' must be provided with 'totalCostUsd'");
16623
+ }
16624
+ }
16625
+ if (value.unpricedModels !== void 0) {
16626
+ const models = value.unpricedModels;
16627
+ if (!Array.isArray(models) || models.length === 0 || !models.every((model2) => typeof model2 === "string" && model2.length > 0)) {
16628
+ throw new Error("Harness Usage 'unpricedModels' must be a non-empty list of Model IDs");
16629
+ }
16630
+ if (value.costSource !== "publicPrice") {
16631
+ throw new Error("Harness Usage 'unpricedModels' must be provided with a 'publicPrice' cost");
16632
+ }
16633
+ return { ...value, unpricedModels: [...models] };
16634
+ }
16327
16635
  return { ...value };
16328
16636
  }
16637
+ var requestTokenFields = [
16638
+ "inputTokens",
16639
+ "cachedInputTokens",
16640
+ "cacheWriteInputTokens",
16641
+ "cacheWrite1hInputTokens",
16642
+ "outputTokens",
16643
+ "reasoningOutputTokens",
16644
+ "startedAtMs",
16645
+ "completedAtMs"
16646
+ ];
16647
+ var requestFields = /* @__PURE__ */ new Set([
16648
+ ...requestTokenFields,
16649
+ "requestId",
16650
+ "historical",
16651
+ "model",
16652
+ "provider"
16653
+ ]);
16654
+ function parseHostUsageRequest(value) {
16655
+ if (!isRecord(value))
16656
+ throw new Error("Harness Usage request must be an object");
16657
+ for (const key of Object.keys(value)) {
16658
+ if (!requestFields.has(key)) {
16659
+ throw new Error(`Harness Usage request contains unknown field '${key}'`);
16660
+ }
16661
+ }
16662
+ if (typeof value.requestId !== "string" || value.requestId.length === 0) {
16663
+ throw new Error("Harness Usage request 'requestId' must be a non-empty string");
16664
+ }
16665
+ for (const field of ["model", "provider"]) {
16666
+ const candidate2 = value[field];
16667
+ if (candidate2 !== void 0 && (typeof candidate2 !== "string" || candidate2.length === 0)) {
16668
+ throw new Error(`Harness Usage request '${field}' must be a non-empty string`);
16669
+ }
16670
+ }
16671
+ if (value.historical !== void 0 && typeof value.historical !== "boolean") {
16672
+ throw new Error("Harness Usage request 'historical' must be a boolean");
16673
+ }
16674
+ for (const field of requestTokenFields) {
16675
+ const candidate2 = value[field];
16676
+ if (candidate2 !== void 0 && (typeof candidate2 !== "number" || !Number.isSafeInteger(candidate2) || candidate2 < 0)) {
16677
+ throw new Error(`Harness Usage request '${field}' must be a non-negative safe integer`);
16678
+ }
16679
+ }
16680
+ if (value.inputTokens === void 0 || value.outputTokens === void 0) {
16681
+ throw new Error("Harness Usage request must contain 'inputTokens' and 'outputTokens'");
16682
+ }
16683
+ const request = value;
16684
+ if ((request.cachedInputTokens ?? 0) + (request.cacheWriteInputTokens ?? 0) > request.inputTokens) {
16685
+ throw new Error("Harness Usage request cache Tokens must not exceed 'inputTokens'");
16686
+ }
16687
+ if ((request.reasoningOutputTokens ?? 0) > request.outputTokens) {
16688
+ throw new Error("Harness Usage request 'reasoningOutputTokens' must not exceed 'outputTokens'");
16689
+ }
16690
+ if (request.cacheWrite1hInputTokens !== void 0 && request.cacheWrite1hInputTokens > (request.cacheWriteInputTokens ?? -1)) {
16691
+ throw new Error("Harness Usage request 'cacheWrite1hInputTokens' must not exceed 'cacheWriteInputTokens'");
16692
+ }
16693
+ if (request.startedAtMs === void 0 !== (request.completedAtMs === void 0)) {
16694
+ throw new Error("Harness Usage request timing fields must be provided together");
16695
+ }
16696
+ if (request.startedAtMs !== void 0 && request.completedAtMs !== void 0 && request.completedAtMs < request.startedAtMs) {
16697
+ throw new Error("Harness Usage request 'completedAtMs' must not precede 'startedAtMs'");
16698
+ }
16699
+ return { ...request };
16700
+ }
16701
+
16702
+ // dist/v2/usage.js
16703
+ function v2UsageRequest(message, historical, firstOutputAtMs) {
16704
+ if (message.type !== "assistant" || !message.tokens)
16705
+ return null;
16706
+ const { input, output, reasoning, cache } = message.tokens;
16707
+ const { streamed } = message.time;
16708
+ return parseHostUsageRequest({
16709
+ requestId: message.id,
16710
+ ...historical ? { historical: true } : {},
16711
+ model: message.model.id,
16712
+ // Built-in OpenCode providers use models.dev IDs. A custom provider ID matches no price
16713
+ // entry, so Host falls back to the exact model ID.
16714
+ provider: message.model.providerID,
16715
+ inputTokens: input + cache.read + cache.write,
16716
+ cachedInputTokens: cache.read,
16717
+ cacheWriteInputTokens: cache.write,
16718
+ outputTokens: output + reasoning,
16719
+ reasoningOutputTokens: reasoning,
16720
+ // End at stream completion, not step completion, which can include tool execution.
16721
+ // Without a native stream end, keep the usage but do not guess its generation duration.
16722
+ ...!historical && firstOutputAtMs !== void 0 && streamed !== void 0 && streamed >= firstOutputAtMs ? { startedAtMs: firstOutputAtMs, completedAtMs: streamed } : {}
16723
+ });
16724
+ }
16725
+
16726
+ // dist/usage-statistics.js
16727
+ function parseRowData(data) {
16728
+ try {
16729
+ return JSON.parse(String(data));
16730
+ } catch {
16731
+ return null;
16732
+ }
16733
+ }
16734
+ function isRecord2(value) {
16735
+ return typeof value === "object" && value !== null && !Array.isArray(value);
16736
+ }
16737
+ function openCodeDatabasePath(environment) {
16738
+ const home = environment.HOME || environment.USERPROFILE || os2.homedir();
16739
+ const directory = path5.join(environment.XDG_DATA_HOME || path5.join(home, ".local", "share"), "opencode");
16740
+ const configured = environment.OPENCODE_DB;
16741
+ if (configured === ":memory:")
16742
+ return null;
16743
+ return configured ? path5.resolve(directory, configured) : path5.join(directory, "opencode.db");
16744
+ }
16745
+ async function fingerprint(file2) {
16746
+ const info = await lstat(file2).catch(() => null);
16747
+ if (!info?.isFile())
16748
+ return null;
16749
+ const wal = await lstat(`${file2}-wal`).catch(() => null);
16750
+ return `${info.ino}:${info.size}:${info.mtimeMs}:${wal ? `${wal.size}:${wal.mtimeMs}` : "-"}`;
16751
+ }
16752
+ async function readOpenCodeUsage(file2) {
16753
+ const { DatabaseSync } = await import("node:sqlite");
16754
+ const database = new DatabaseSync(file2, { readOnly: true });
16755
+ try {
16756
+ const tables = new Set(database.prepare("SELECT name FROM sqlite_master WHERE type = 'table'").all().map((row) => String(row.name)));
16757
+ const directories = /* @__PURE__ */ new Map();
16758
+ const titles = /* @__PURE__ */ new Map();
16759
+ for (const table of ["session", "session_v2"]) {
16760
+ if (!tables.has(table))
16761
+ continue;
16762
+ const columns = new Set(database.prepare(`PRAGMA table_info(${table})`).all().map((row) => String(row.name)));
16763
+ const titleColumn = columns.has("title") ? ", title" : "";
16764
+ for (const row of database.prepare(`SELECT id, directory${titleColumn} FROM ${table}`).iterate()) {
16765
+ if (typeof row.directory === "string")
16766
+ directories.set(String(row.id), row.directory);
16767
+ const title = usageSessionTitle(row.title);
16768
+ if (title)
16769
+ titles.set(String(row.id), title);
16770
+ }
16771
+ }
16772
+ const entries = /* @__PURE__ */ new Map();
16773
+ const add = (message, createdAtMs, session) => {
16774
+ let request;
16775
+ try {
16776
+ request = v2UsageRequest(message, true);
16777
+ } catch {
16778
+ return;
16779
+ }
16780
+ const at = nativeTimeMs(createdAtMs);
16781
+ const entry = request && at !== null && usageEntryFromRequest(request, at);
16782
+ if (entry && !entries.has(entry.id)) {
16783
+ const sessionId = typeof session === "string" ? session : void 0;
16784
+ entries.set(entry.id, withUsageSession(entry, {
16785
+ sessionId,
16786
+ cwd: sessionId === void 0 ? void 0 : directories.get(sessionId),
16787
+ title: sessionId === void 0 ? void 0 : titles.get(sessionId)
16788
+ }));
16789
+ }
16790
+ };
16791
+ if (tables.has("session_message")) {
16792
+ for (const row of database.prepare("SELECT id, session_id, data FROM session_message WHERE type = 'assistant'").iterate()) {
16793
+ const data = parseRowData(row.data);
16794
+ if (!isRecord2(data))
16795
+ continue;
16796
+ add({ ...data, id: String(row.id), type: "assistant" }, isRecord2(data.time) ? data.time.created : void 0, row.session_id);
16797
+ }
16798
+ }
16799
+ const migrated = tables.has("kv") && /"phase"\s*:\s*"completed"/u.test(String(database.prepare("SELECT value FROM kv WHERE key = 'migration.v1-v2'").get()?.value ?? ""));
16800
+ if (tables.has("message") && !migrated) {
16801
+ for (const row of database.prepare("SELECT id, session_id, data FROM message").iterate()) {
16802
+ const data = parseRowData(row.data);
16803
+ if (!isRecord2(data) || data.role !== "assistant" || typeof data.modelID !== "string") {
16804
+ continue;
16805
+ }
16806
+ add({
16807
+ id: String(row.id),
16808
+ type: "assistant",
16809
+ model: { id: data.modelID, providerID: data.providerID },
16810
+ tokens: data.tokens,
16811
+ time: data.time
16812
+ }, isRecord2(data.time) ? data.time.created : void 0, row.session_id);
16813
+ }
16814
+ }
16815
+ return [...entries.values()];
16816
+ } finally {
16817
+ database.close();
16818
+ }
16819
+ }
16820
+ function createOpenCodeUsageStatistics(environment) {
16821
+ return Object.freeze({
16822
+ async listSources() {
16823
+ const file2 = openCodeDatabasePath(environment);
16824
+ const stamp = file2 ? await fingerprint(file2) : null;
16825
+ return file2 && stamp ? [{ id: file2, fingerprint: stamp }] : [];
16826
+ },
16827
+ readSource: (id) => readOpenCodeUsage(id)
16828
+ });
16829
+ }
16830
+
16831
+ // dist/opencode-adapter.js
16832
+ import { randomUUID } from "node:crypto";
16833
+ import fs4 from "node:fs";
16834
+ import path9 from "node:path";
16329
16835
 
16330
16836
  // dist/model-catalog.js
16331
16837
  import { Buffer as Buffer2 } from "node:buffer";
@@ -16336,10 +16842,10 @@ function assertNonBlank(value, label) {
16336
16842
  if (!value.trim())
16337
16843
  throw new Error(`OpenCode ${label} must not be empty`);
16338
16844
  }
16339
- function encodeOpenCodeModelRef(model) {
16340
- assertNonBlank(model.providerID, "Provider ID");
16341
- assertNonBlank(model.modelID, "Model ID");
16342
- const encoded = Buffer2.from(JSON.stringify([model.providerID, model.modelID]), "utf8").toString("base64url");
16845
+ function encodeOpenCodeModelRef(model2) {
16846
+ assertNonBlank(model2.providerID, "Provider ID");
16847
+ assertNonBlank(model2.modelID, "Model ID");
16848
+ const encoded = Buffer2.from(JSON.stringify([model2.providerID, model2.modelID]), "utf8").toString("base64url");
16343
16849
  const id = `${MODEL_REF_PREFIX}${encoded}`;
16344
16850
  if (id.length > HARNESS_MODEL_REF_MAX_LENGTH) {
16345
16851
  throw new Error("OpenCode Model identity is too long for a Model Ref");
@@ -16397,26 +16903,26 @@ function compareText(left, right) {
16397
16903
  }
16398
16904
  function modelsForConnectedProviders(catalog) {
16399
16905
  const connected = new Set(catalog.connected);
16400
- return catalog.all.flatMap((provider) => connected.has(provider.id) ? Object.values(provider.models).map((model) => ({ provider, model })) : []);
16906
+ return catalog.all.flatMap((provider) => connected.has(provider.id) ? Object.values(provider.models).map((model2) => ({ provider, model: model2 })) : []);
16401
16907
  }
16402
16908
  function normalizeOpenCodeModelCatalog(catalog) {
16403
16909
  const nativeModels = modelsForConnectedProviders(catalog);
16404
16910
  const variants = /* @__PURE__ */ new Set();
16405
- for (const { model } of nativeModels) {
16406
- for (const variant of Object.keys(model.variants ?? {}))
16911
+ for (const { model: model2 } of nativeModels) {
16912
+ for (const variant of Object.keys(model2.variants ?? {}))
16407
16913
  variants.add(variant);
16408
16914
  }
16409
16915
  const thinkingOptions = [
16410
16916
  variantOption(void 0),
16411
16917
  ...[...variants].sort(compareText).map((variant) => variantOption(variant))
16412
16918
  ];
16413
- const models = nativeModels.map(({ provider, model }) => ({
16414
- ref: encodeOpenCodeModelRef({ providerID: provider.id, modelID: model.id }),
16415
- label: `${provider.name} / ${model.name}`,
16416
- resolvedModelLabel: `${provider.id}/${model.id}`,
16919
+ const models = nativeModels.map(({ provider, model: model2 }) => ({
16920
+ ref: encodeOpenCodeModelRef({ providerID: provider.id, modelID: model2.id }),
16921
+ label: `${provider.name} / ${model2.name}`,
16922
+ resolvedModelLabel: `${provider.id}/${model2.id}`,
16417
16923
  supportedThinkingOptionIds: [
16418
16924
  DEFAULT_VARIANT_ID,
16419
- ...Object.keys(model.variants ?? {}).sort(compareText).map(encodeOpenCodeVariant)
16925
+ ...Object.keys(model2.variants ?? {}).sort(compareText).map(encodeOpenCodeVariant)
16420
16926
  ]
16421
16927
  })).sort((left, right) => compareText(left.label, right.label) || compareText(left.ref.id, right.ref.id));
16422
16928
  const defaultNative = catalog.connected.map((providerID) => ({ providerID, modelID: catalog.default[providerID] })).find((candidate2) => typeof candidate2.modelID === "string" && candidate2.modelID.length > 0);
@@ -16428,9 +16934,9 @@ function normalizeOpenCodeModelCatalog(catalog) {
16428
16934
  defaultThinkingOptionId: DEFAULT_VARIANT_ID
16429
16935
  });
16430
16936
  }
16431
- function openCodeContextWindow(catalog, model) {
16432
- const provider = catalog.all.find(({ id }) => id === model.providerID);
16433
- const context = provider?.models[model.modelID]?.limit.context;
16937
+ function openCodeContextWindow(catalog, model2) {
16938
+ const provider = catalog.all.find(({ id }) => id === model2.providerID);
16939
+ const context = provider?.models[model2.modelID]?.limit.context;
16434
16940
  return typeof context === "number" && Number.isSafeInteger(context) && context > 0 ? context : void 0;
16435
16941
  }
16436
16942
 
@@ -16753,7 +17259,7 @@ import { spawn, spawnSync } from "node:child_process";
16753
17259
  import fs2 from "node:fs";
16754
17260
  import { createServer } from "node:net";
16755
17261
  import { homedir as homedir2, tmpdir as tmpdir2 } from "node:os";
16756
- import path6 from "node:path";
17262
+ import path7 from "node:path";
16757
17263
 
16758
17264
  // ../../../node_modules/@opencode-ai/sdk/dist/v2/gen/core/serverSentEvents.gen.js
16759
17265
  var createSseClient = ({ onRequest, onSseError, onSseEvent, responseTransformer, responseValidator, sseDefaultRetryDelay, sseMaxRetryAttempts, sseMaxRetryDelay, sseSleepFn, url: url2, ...options }) => {
@@ -16979,7 +17485,7 @@ var serializeObjectParam = ({ allowReserved, explode, name, style, value, valueO
16979
17485
 
16980
17486
  // ../../../node_modules/@opencode-ai/sdk/dist/v2/gen/core/utils.gen.js
16981
17487
  var PATH_PARAM_RE = /\{[^{}]+\}/g;
16982
- var defaultPathSerializer = ({ path: path13, url: _url2 }) => {
17488
+ var defaultPathSerializer = ({ path: path14, url: _url2 }) => {
16983
17489
  let url2 = _url2;
16984
17490
  const matches = _url2.match(PATH_PARAM_RE);
16985
17491
  if (matches) {
@@ -16998,7 +17504,7 @@ var defaultPathSerializer = ({ path: path13, url: _url2 }) => {
16998
17504
  name = name.substring(1);
16999
17505
  style = "matrix";
17000
17506
  }
17001
- const value = path13[name];
17507
+ const value = path14[name];
17002
17508
  if (value === void 0 || value === null) {
17003
17509
  continue;
17004
17510
  }
@@ -17029,11 +17535,11 @@ var defaultPathSerializer = ({ path: path13, url: _url2 }) => {
17029
17535
  }
17030
17536
  return url2;
17031
17537
  };
17032
- var getUrl = ({ baseUrl, path: path13, query, querySerializer, url: _url2 }) => {
17538
+ var getUrl = ({ baseUrl, path: path14, query, querySerializer, url: _url2 }) => {
17033
17539
  const pathUrl = _url2.startsWith("/") ? _url2 : `/${_url2}`;
17034
17540
  let url2 = (baseUrl ?? "") + pathUrl;
17035
- if (path13) {
17036
- url2 = defaultPathSerializer({ path: path13, url: url2 });
17541
+ if (path14) {
17542
+ url2 = defaultPathSerializer({ path: path14, url: url2 });
17037
17543
  }
17038
17544
  let search = query ? querySerializer(query) : "";
17039
17545
  if (search.startsWith("?")) {
@@ -22156,7 +22662,7 @@ function createOpencodeClient(config2) {
22156
22662
  }
22157
22663
 
22158
22664
  // dist/command.js
22159
- import path5 from "node:path";
22665
+ import path6 from "node:path";
22160
22666
  var OpenCodeExecutableError = class extends Error {
22161
22667
  code = "OPENCODE_NOT_FOUND";
22162
22668
  constructor(message = "OpenCode CLI is not installed") {
@@ -22190,7 +22696,7 @@ function resolveOpenCodeExecutable(input = {}, dependencies = {}) {
22190
22696
  }, dependencies);
22191
22697
  if (!resolution)
22192
22698
  throw new OpenCodeExecutableError();
22193
- return targetPath(platform).isAbsolute(resolution.executable) ? resolution.executable : path5.resolve(resolution.executable);
22699
+ return targetPath(platform).isAbsolute(resolution.executable) ? resolution.executable : path6.resolve(resolution.executable);
22194
22700
  }
22195
22701
  function openCodeServerInvocation(executable, environment, platform = process.platform, port = 0) {
22196
22702
  return commandInvocation(executable, ["serve", "--hostname=127.0.0.1", `--port=${port}`], environment, platform);
@@ -22218,14 +22724,14 @@ function freeLoopbackPort() {
22218
22724
  });
22219
22725
  });
22220
22726
  }
22221
- function isRecord2(value) {
22727
+ function isRecord3(value) {
22222
22728
  return typeof value === "object" && value !== null && !Array.isArray(value);
22223
22729
  }
22224
22730
  function errorText(error51) {
22225
22731
  return error51 instanceof Error ? error51.message : String(error51);
22226
22732
  }
22227
22733
  function isMissingExecutable(error51) {
22228
- return isRecord2(error51) && error51.code === "ENOENT";
22734
+ return isRecord3(error51) && error51.code === "ENOENT";
22229
22735
  }
22230
22736
  function classifySdkError(error51, operation) {
22231
22737
  if (error51 instanceof OpenCodeTransportError)
@@ -22280,14 +22786,14 @@ function signalProcessTree(child, signal) {
22280
22786
  try {
22281
22787
  process.kill(-child.pid, signal);
22282
22788
  } catch (error51) {
22283
- if (!isRecord2(error51) || error51.code !== "ESRCH")
22789
+ if (!isRecord3(error51) || error51.code !== "ESRCH")
22284
22790
  throw error51;
22285
22791
  }
22286
22792
  }
22287
22793
  function safeOpenCodeServerCwd(environment) {
22288
22794
  const candidates = [environment.USERPROFILE, environment.HOME, homedir2(), tmpdir2()];
22289
22795
  for (const candidate2 of candidates) {
22290
- if (!candidate2 || !path6.isAbsolute(candidate2))
22796
+ if (!candidate2 || !path7.isAbsolute(candidate2))
22291
22797
  continue;
22292
22798
  try {
22293
22799
  if (!fs2.statSync(candidate2).isDirectory())
@@ -22311,7 +22817,7 @@ function managedOpenCodeEnvironment(environment, executionPolicy = "default") {
22311
22817
  if (existing !== void 0) {
22312
22818
  try {
22313
22819
  const parsed = JSON.parse(existing);
22314
- if (!isRecord2(parsed))
22820
+ if (!isRecord3(parsed))
22315
22821
  throw new Error("OpenCode config content must be an object");
22316
22822
  config2 = { ...parsed };
22317
22823
  } catch (error51) {
@@ -22634,11 +23140,11 @@ var SdkOpenCodeTransport = class {
22634
23140
  parts: [{ type: "text", text: input.text }]
22635
23141
  }), this.#commandTimeoutMs, "OpenCode prompt admission"), "prompt admission");
22636
23142
  }
22637
- async summarize(sessionID, model) {
23143
+ async summarize(sessionID, model2) {
22638
23144
  const client2 = await this.#getClient();
22639
23145
  responseData2(await withTimeout2(client2.session.summarize({
22640
23146
  sessionID,
22641
- ...model ? { providerID: model.providerID, modelID: model.modelID } : {}
23147
+ ...model2 ? { providerID: model2.providerID, modelID: model2.modelID } : {}
22642
23148
  }), this.#commandTimeoutMs, "OpenCode context compaction"), "context compaction");
22643
23149
  }
22644
23150
  async abort(sessionID) {
@@ -22736,7 +23242,7 @@ var SdkOpenCodeTransport = class {
22736
23242
 
22737
23243
  // dist/file-change-verification.js
22738
23244
  import fs3 from "node:fs";
22739
- import path7 from "node:path";
23245
+ import path8 from "node:path";
22740
23246
  function missingPath(error51) {
22741
23247
  if (!error51 || typeof error51 !== "object")
22742
23248
  return false;
@@ -22748,33 +23254,33 @@ function hasDotPathSegment(value) {
22748
23254
  return segments.some((segment) => segment === "." || segment === "..");
22749
23255
  }
22750
23256
  function canonicalDirectoryPath(value) {
22751
- let candidate2 = path7.resolve(value);
23257
+ let candidate2 = path8.resolve(value);
22752
23258
  const missingSegments = [];
22753
23259
  for (; ; ) {
22754
23260
  try {
22755
- return path7.join(fs3.realpathSync.native(candidate2), ...missingSegments);
23261
+ return path8.join(fs3.realpathSync.native(candidate2), ...missingSegments);
22756
23262
  } catch (error51) {
22757
23263
  if (!missingPath(error51))
22758
23264
  return void 0;
22759
- const parent = path7.dirname(candidate2);
23265
+ const parent = path8.dirname(candidate2);
22760
23266
  if (parent === candidate2)
22761
23267
  return void 0;
22762
- missingSegments.unshift(path7.basename(candidate2));
23268
+ missingSegments.unshift(path8.basename(candidate2));
22763
23269
  candidate2 = parent;
22764
23270
  }
22765
23271
  }
22766
23272
  }
22767
23273
  function canonicalFilePath(value) {
22768
- const absolute = path7.resolve(value);
22769
- const parent = canonicalDirectoryPath(path7.dirname(absolute));
22770
- return parent ? path7.join(parent, path7.basename(absolute)) : void 0;
23274
+ const absolute = path8.resolve(value);
23275
+ const parent = canonicalDirectoryPath(path8.dirname(absolute));
23276
+ return parent ? path8.join(parent, path8.basename(absolute)) : void 0;
22771
23277
  }
22772
23278
  function pathIsWithin(root, candidate2, allowRoot) {
22773
- const relative = path7.relative(root, candidate2);
22774
- return allowRoot && relative === "" || relative !== "" && relative !== ".." && !relative.startsWith(`..${path7.sep}`) && !path7.isAbsolute(relative);
23279
+ const relative = path8.relative(root, candidate2);
23280
+ return allowRoot && relative === "" || relative !== "" && relative !== ".." && !relative.startsWith(`..${path8.sep}`) && !path8.isAbsolute(relative);
22775
23281
  }
22776
23282
  function verifiedOpenCodeWorktree(sessionDirectory, paths) {
22777
- if (!paths || typeof paths !== "object" || typeof sessionDirectory !== "string" || typeof paths.directory !== "string" || typeof paths.worktree !== "string" || hasDotPathSegment(sessionDirectory) || hasDotPathSegment(paths.directory) || hasDotPathSegment(paths.worktree) || !path7.isAbsolute(sessionDirectory) || !path7.isAbsolute(paths.directory) || !path7.isAbsolute(paths.worktree)) {
23283
+ if (!paths || typeof paths !== "object" || typeof sessionDirectory !== "string" || typeof paths.directory !== "string" || typeof paths.worktree !== "string" || hasDotPathSegment(sessionDirectory) || hasDotPathSegment(paths.directory) || hasDotPathSegment(paths.worktree) || !path8.isAbsolute(sessionDirectory) || !path8.isAbsolute(paths.directory) || !path8.isAbsolute(paths.worktree)) {
22778
23284
  return void 0;
22779
23285
  }
22780
23286
  const session = canonicalDirectoryPath(sessionDirectory);
@@ -22786,10 +23292,10 @@ function verifiedOpenCodeWorktree(sessionDirectory, paths) {
22786
23292
  return worktree;
22787
23293
  }
22788
23294
  function openCodeFileIdentity(file2, worktree) {
22789
- if (typeof file2 !== "string" || typeof worktree !== "string" || hasDotPathSegment(file2) || hasDotPathSegment(worktree) || !path7.isAbsolute(worktree) || !file2 || file2.includes("\0") || file2.includes("\n") || file2.includes("\r")) {
23295
+ if (typeof file2 !== "string" || typeof worktree !== "string" || hasDotPathSegment(file2) || hasDotPathSegment(worktree) || !path8.isAbsolute(worktree) || !file2 || file2.includes("\0") || file2.includes("\n") || file2.includes("\r")) {
22790
23296
  return void 0;
22791
23297
  }
22792
- const candidate2 = canonicalFilePath(path7.isAbsolute(file2) ? file2 : path7.resolve(worktree, file2));
23298
+ const candidate2 = canonicalFilePath(path8.isAbsolute(file2) ? file2 : path8.resolve(worktree, file2));
22793
23299
  return candidate2 && pathIsWithin(worktree, candidate2, false) ? candidate2 : void 0;
22794
23300
  }
22795
23301
 
@@ -22847,12 +23353,12 @@ function storedSelection(session) {
22847
23353
  }
22848
23354
  return { providerID, modelID, ...typeof variant === "string" ? { variant } : {} };
22849
23355
  }
22850
- function selectionMetadata(session, model, variant) {
23356
+ function selectionMetadata(session, model2, variant) {
22851
23357
  return {
22852
23358
  ...session.metadata,
22853
23359
  [SELECTION_METADATA_KEY]: {
22854
- providerID: model.providerID,
22855
- modelID: model.modelID,
23360
+ providerID: model2.providerID,
23361
+ modelID: model2.modelID,
22856
23362
  ...variant ? { variant } : {}
22857
23363
  }
22858
23364
  };
@@ -22948,17 +23454,17 @@ async function readProjection(transport, sessionID, providerCatalog, toolOutputL
22948
23454
  diffsByUserMessageId: new Map(diffEntries),
22949
23455
  toolOutputLimit
22950
23456
  });
22951
- const model = nativeModelFromSession(session, messages);
23457
+ const model2 = nativeModelFromSession(session, messages);
22952
23458
  const nativeVariant = nativeVariantFromSession(session, messages);
22953
- const nativeModel = model ? providerCatalog.all.find(({ id }) => id === model.providerID)?.models[model.modelID] : void 0;
23459
+ const nativeModel = model2 ? providerCatalog.all.find(({ id }) => id === model2.providerID)?.models[model2.modelID] : void 0;
22954
23460
  const variant = nativeVariant === "default" && nativeModel && !Object.hasOwn(nativeModel.variants ?? {}, "default") ? void 0 : nativeVariant;
22955
- const usage = projectOpenCodeUsage(openCodeAssistantMessages(session, messages), model ? openCodeContextWindow(providerCatalog, model) : void 0);
23461
+ const usage = projectOpenCodeUsage(openCodeAssistantMessages(session, messages), model2 ? openCodeContextWindow(providerCatalog, model2) : void 0);
22956
23462
  return {
22957
23463
  session,
22958
23464
  messages,
22959
23465
  snapshot,
22960
23466
  usage,
22961
- ...model ? { model } : {},
23467
+ ...model2 ? { model: model2 } : {},
22962
23468
  ...variant ? { variant } : {}
22963
23469
  };
22964
23470
  }
@@ -23084,13 +23590,13 @@ function sameCwd(left, right) {
23084
23590
  try {
23085
23591
  return fs4.realpathSync.native(value);
23086
23592
  } catch {
23087
- return path8.resolve(value);
23593
+ return path9.resolve(value);
23088
23594
  }
23089
23595
  };
23090
23596
  return canonical(left) === canonical(right);
23091
23597
  }
23092
- function sessionState(session, modelCatalog, model, variant, executionPolicy = "default", permissionModeId = OPENCODE_DEFAULT_PERMISSION_MODE_ID) {
23093
- const effectiveModel = model ? encodeOpenCodeModelRef(model) : void 0;
23598
+ function sessionState(session, modelCatalog, model2, variant, executionPolicy = "default", permissionModeId = OPENCODE_DEFAULT_PERMISSION_MODE_ID) {
23599
+ const effectiveModel = model2 ? encodeOpenCodeModelRef(model2) : void 0;
23094
23600
  const modelEntry = effectiveModel ? modelCatalog.models.find(({ ref }) => ref.id === effectiveModel.id) : void 0;
23095
23601
  const availableThinkingOptions = modelEntry?.supportedThinkingOptionIds?.map((id) => modelCatalog.thinkingOptions.find((option) => option.id === id)).filter((option) => Boolean(option));
23096
23602
  const effectiveThinkingOptionId = encodeOpenCodeVariant(variant);
@@ -23492,8 +23998,8 @@ var OpenCodeHarnessSession = class {
23492
23998
  };
23493
23999
  }
23494
24000
  const modelRef = this.#model ? encodeOpenCodeModelRef(this.#model) : void 0;
23495
- const model = modelRef ? this.#modelCatalog.models.find(({ ref }) => ref.id === modelRef.id) : void 0;
23496
- if (!model?.supportedThinkingOptionIds?.includes(command.thinkingOptionId)) {
24001
+ const model2 = modelRef ? this.#modelCatalog.models.find(({ ref }) => ref.id === modelRef.id) : void 0;
24002
+ if (!model2?.supportedThinkingOptionIds?.includes(command.thinkingOptionId)) {
23497
24003
  return {
23498
24004
  ok: false,
23499
24005
  error: {
@@ -24343,14 +24849,14 @@ var OpenCodeAdapter = class {
24343
24849
  const modelCatalog = normalizeOpenCodeModelCatalog(providers);
24344
24850
  let session;
24345
24851
  if (input.kind === "create") {
24346
- const model = input.model ? decodeOpenCodeModelRef(input.model) : void 0;
24852
+ const model2 = input.model ? decodeOpenCodeModelRef(input.model) : void 0;
24347
24853
  const variant = input.thinkingOptionId ? decodeOpenCodeVariant(input.thinkingOptionId) : void 0;
24348
24854
  if (input.model && !modelCatalog.models.some(({ ref }) => ref.id === input.model?.id)) {
24349
24855
  throw new OpenCodeTransportError("protocolError", "Requested OpenCode Model is absent from the connected Provider catalog");
24350
24856
  }
24351
24857
  const permission = requestedPermissionMode && requestedPermissionMode !== "default" ? requestedPermissionRules(void 0, requestedPermissionMode) : void 0;
24352
24858
  session = await transport.createSession({
24353
- ...model ? { model } : {},
24859
+ ...model2 ? { model: model2 } : {},
24354
24860
  ...variant ? { variant } : {},
24355
24861
  ...permission ? { permission } : {}
24356
24862
  });
@@ -24465,7 +24971,7 @@ import { isDeepStrictEqual as isDeepStrictEqual2 } from "node:util";
24465
24971
 
24466
24972
  // dist/v2/state.js
24467
24973
  import fs5 from "node:fs";
24468
- import path9 from "node:path";
24974
+ import path10 from "node:path";
24469
24975
  var harnessId = harnessIdSchema.parse("opencode");
24470
24976
  function failure(message, code = "nativeFailure") {
24471
24977
  return {
@@ -24485,7 +24991,7 @@ function sameDirectory(left, right) {
24485
24991
  try {
24486
24992
  return fs5.realpathSync(value);
24487
24993
  } catch {
24488
- return path9.resolve(value);
24994
+ return path10.resolve(value);
24489
24995
  }
24490
24996
  };
24491
24997
  return canonical(left) === canonical(right);
@@ -24507,17 +25013,17 @@ function v2Locator(ref) {
24507
25013
  return { directory: locator.directory, executionPolicy: locator.executionPolicy };
24508
25014
  }
24509
25015
  function v2Catalog(models, defaultModel) {
24510
- const enabled = models.filter((model) => model.enabled);
24511
- const variants = [...new Set(enabled.flatMap((model) => model.variants.map((v) => v.id)))].sort();
24512
- const ref = (model) => encodeOpenCodeModelRef({ providerID: model.providerID, modelID: model.id });
25016
+ const enabled = models.filter((model2) => model2.enabled);
25017
+ const variants = [...new Set(enabled.flatMap((model2) => model2.variants.map((v) => v.id)))].sort();
25018
+ const ref = (model2) => encodeOpenCodeModelRef({ providerID: model2.providerID, modelID: model2.id });
24513
25019
  return harnessModelCatalogSchema.parse({
24514
- models: enabled.map((model) => ({
24515
- ref: ref(model),
24516
- label: `${model.providerID} / ${model.name}`,
24517
- resolvedModelLabel: `${model.providerID}/${model.id}`,
25020
+ models: enabled.map((model2) => ({
25021
+ ref: ref(model2),
25022
+ label: `${model2.providerID} / ${model2.name}`,
25023
+ resolvedModelLabel: `${model2.providerID}/${model2.id}`,
24518
25024
  supportedThinkingOptionIds: [
24519
25025
  encodeOpenCodeVariant(void 0),
24520
- ...model.variants.map((v) => encodeOpenCodeVariant(v.id))
25026
+ ...model2.variants.map((v) => encodeOpenCodeVariant(v.id))
24521
25027
  ]
24522
25028
  })),
24523
25029
  ...defaultModel && enabled.some((m) => ref(m).id === ref(defaultModel).id) ? { defaultModel: ref(defaultModel) } : {},
@@ -24534,12 +25040,12 @@ function v2Permissions(current = [], mode) {
24534
25040
  return selected === "default" ? rules : [...rules, { action: "*", resource: "*", effect: selected }];
24535
25041
  }
24536
25042
  function v2State(session, catalog, policy) {
24537
- const model = session.model ? encodeOpenCodeModelRef({ providerID: session.model.providerID, modelID: session.model.id }) : void 0;
24538
- const entry = catalog.models.find((candidate2) => candidate2.ref.id === model?.id);
25043
+ const model2 = session.model ? encodeOpenCodeModelRef({ providerID: session.model.providerID, modelID: session.model.id }) : void 0;
25044
+ const entry = catalog.models.find((candidate2) => candidate2.ref.id === model2?.id);
24539
25045
  const mode = session.permissions?.findLast((rule) => rule.action === "*" && rule.resource === "*");
24540
25046
  return {
24541
25047
  nativeRef: v2Ref(session, policy),
24542
- ...model ? { effectiveModel: model } : {},
25048
+ ...model2 ? { effectiveModel: model2 } : {},
24543
25049
  ...entry ? {
24544
25050
  resolvedModelLabel: entry.resolvedModelLabel ?? entry.label,
24545
25051
  availableThinkingOptions: catalog.thinkingOptions.filter((v) => entry.supportedThinkingOptionIds?.includes(v.id)),
@@ -26400,7 +26906,7 @@ var V2Connection = class {
26400
26906
  };
26401
26907
 
26402
26908
  // dist/v2/history.js
26403
- import path10 from "node:path";
26909
+ import path11 from "node:path";
26404
26910
  async function readMessages(client2, sessionID) {
26405
26911
  const messages = [];
26406
26912
  let cursor;
@@ -26419,11 +26925,12 @@ async function readMessages(client2, sessionID) {
26419
26925
  } while (cursor);
26420
26926
  return messages;
26421
26927
  }
26422
- var contentId = (messageID, index) => hostItemIdSchema.parse(`${messageID}:${index}`);
26928
+ var contentId = (messageID, type, ordinal) => hostItemIdSchema.parse(`${messageID}:${type}:${ordinal}`);
26423
26929
  function assistantItems2(message, limit) {
26424
- return message.content.map((part, index) => {
26425
- const itemId2 = contentId(message.id, index);
26930
+ const ordinals = { text: 0, reasoning: 0 };
26931
+ return message.content.map((part) => {
26426
26932
  if (part.type === "text" || part.type === "reasoning") {
26933
+ const itemId2 = contentId(message.id, part.type, ordinals[part.type]++);
26427
26934
  return {
26428
26935
  item: {
26429
26936
  type: part.type === "text" ? "agentMessage" : "reasoning",
@@ -26438,7 +26945,7 @@ function assistantItems2(message, limit) {
26438
26945
  return {
26439
26946
  item: {
26440
26947
  type: "toolExecution",
26441
- itemId: itemId2,
26948
+ itemId: hostItemIdSchema.parse(`${message.id}:tool:${part.id}`),
26442
26949
  toolName: part.name,
26443
26950
  namespace: "opencode",
26444
26951
  arguments: typeof state.input === "string" ? state.input : state.input,
@@ -26525,9 +27032,9 @@ function fileChanges(session, turn, diffs) {
26525
27032
  if (!diffs.length)
26526
27033
  return void 0;
26527
27034
  const changes = diffs.map((diff) => {
26528
- const absolute = path10.resolve(session.location.directory, diff.file);
26529
- const relative = path10.relative(session.location.directory, absolute);
26530
- if (relative.startsWith(`..${path10.sep}`) || relative === ".." || path10.isAbsolute(relative))
27035
+ const absolute = path11.resolve(session.location.directory, diff.file);
27036
+ const relative = path11.relative(session.location.directory, absolute);
27037
+ if (relative.startsWith(`..${path11.sep}`) || relative === ".." || path11.isAbsolute(relative))
26531
27038
  throw new Error("OpenCode v2 diff escaped the Session directory");
26532
27039
  return {
26533
27040
  path: absolute,
@@ -26570,7 +27077,7 @@ function terminalItemOutcome(outcome) {
26570
27077
 
26571
27078
  // dist/v2/session-import.js
26572
27079
  import fs6 from "node:fs";
26573
- import path11 from "node:path";
27080
+ import path12 from "node:path";
26574
27081
 
26575
27082
  // ../../harness-adapter/dist/session-import.js
26576
27083
  function sessionImportTitle(value, maxLength = HARNESS_SESSION_IMPORT_TITLE_MAX_LENGTH) {
@@ -26603,7 +27110,7 @@ function sessionImportCandidate(input) {
26603
27110
  var PAGE_SIZE = 200;
26604
27111
  var MAX_PAGES = 1e4;
26605
27112
  function existingDirectory(directory) {
26606
- if (typeof directory !== "string" || !path11.isAbsolute(directory))
27113
+ if (typeof directory !== "string" || !path12.isAbsolute(directory))
26607
27114
  return null;
26608
27115
  try {
26609
27116
  return fs6.statSync(directory).isDirectory() ? directory : null;
@@ -26803,6 +27310,14 @@ function v2Capabilities(catalog) {
26803
27310
  history: { fork: true, forkAcrossCwd: false, rollbackLastTurn: true }
26804
27311
  };
26805
27312
  }
27313
+ var FIRST_OUTPUT_EVENTS = /* @__PURE__ */ new Set([
27314
+ "session.reasoning.started",
27315
+ "session.reasoning.delta",
27316
+ "session.text.started",
27317
+ "session.text.delta",
27318
+ "session.tool.input.started",
27319
+ "session.tool.input.delta"
27320
+ ]);
26806
27321
  var V2Session = class {
26807
27322
  client;
26808
27323
  connection;
@@ -26826,6 +27341,11 @@ var V2Session = class {
26826
27341
  #reconciling;
26827
27342
  #timer;
26828
27343
  #configuring = false;
27344
+ #cacheHitRatePercent;
27345
+ /** Assistant messages already published as usage requests. */
27346
+ #meteredMessages = /* @__PURE__ */ new Set();
27347
+ /** Adapter receive time of each assistant message's first reasoning, text or tool input. */
27348
+ #firstOutputAt = /* @__PURE__ */ new Map();
26829
27349
  constructor(client2, connection, info, catalog, policy, limit, onClosed) {
26830
27350
  this.client = client2;
26831
27351
  this.connection = connection;
@@ -26870,6 +27390,7 @@ var V2Session = class {
26870
27390
  }
26871
27391
  })();
26872
27392
  });
27393
+ await this.#replayUsage();
26873
27394
  this.#timer = setInterval(() => {
26874
27395
  void this.#refresh();
26875
27396
  }, 400);
@@ -26938,22 +27459,22 @@ var V2Session = class {
26938
27459
  permissions: v2Permissions(this.info.permissions, command.permissionModeId)
26939
27460
  });
26940
27461
  } else {
26941
- let model = this.info.model;
27462
+ let model2 = this.info.model;
26942
27463
  if (command.type === "model.select") {
26943
27464
  if (!this.catalog.models.some((m) => m.ref.id === command.model.id))
26944
27465
  throw new Error("OpenCode Model is not available");
26945
27466
  const decoded = decodeOpenCodeModelRef(command.model);
26946
- model = { providerID: decoded.providerID, id: decoded.modelID };
27467
+ model2 = { providerID: decoded.providerID, id: decoded.modelID };
26947
27468
  } else {
26948
- if (!model)
27469
+ if (!model2)
26949
27470
  throw new Error("Select a Model before a Thinking option");
26950
27471
  const state = v2State(this.info, this.catalog, this.policy);
26951
27472
  if (!state.availableThinkingOptions?.some((v) => v.id === command.thinkingOptionId))
26952
27473
  throw new Error("Thinking option is unavailable for this Model");
26953
27474
  const variant = decodeOpenCodeVariant(command.thinkingOptionId);
26954
- model = { providerID: model.providerID, id: model.id, ...variant ? { variant } : {} };
27475
+ model2 = { providerID: model2.providerID, id: model2.id, ...variant ? { variant } : {} };
26955
27476
  }
26956
- await this.client.session.switchModel({ sessionID: this.info.id, model });
27477
+ await this.client.session.switchModel({ sessionID: this.info.id, model: model2 });
26957
27478
  }
26958
27479
  this.info = await this.client.session.get({ sessionID: this.info.id });
26959
27480
  this.#emit({
@@ -27036,11 +27557,14 @@ var V2Session = class {
27036
27557
  const active = this.#active;
27037
27558
  if (!active || !("data" in event) || !("sessionID" in event.data) || event.data.sessionID !== this.info.id)
27038
27559
  return;
27560
+ if (FIRST_OUTPUT_EVENTS.has(event.type) && "assistantMessageID" in event.data && typeof event.data.assistantMessageID === "string" && !this.#firstOutputAt.has(event.data.assistantMessageID))
27561
+ this.#firstOutputAt.set(event.data.assistantMessageID, Date.now());
27039
27562
  if (event.type === "session.step.started")
27040
27563
  active.assistants.add(event.data.assistantMessageID);
27041
27564
  if ((event.type === "session.text.delta" || event.type === "session.reasoning.delta") && active.assistants.has(event.data.assistantMessageID)) {
27042
- const itemId2 = contentId(event.data.assistantMessageID, event.data.ordinal);
27043
- const type = event.type === "session.text.delta" ? "agentMessage" : "reasoning";
27565
+ const contentType = event.type === "session.text.delta" ? "text" : "reasoning";
27566
+ const itemId2 = contentId(event.data.assistantMessageID, contentType, event.data.ordinal);
27567
+ const type = contentType === "text" ? "agentMessage" : "reasoning";
27044
27568
  const previous = active.transient.get(itemId2);
27045
27569
  const item = {
27046
27570
  type,
@@ -27054,6 +27578,56 @@ var V2Session = class {
27054
27578
  if (!event.type.endsWith(".delta"))
27055
27579
  void this.#refresh();
27056
27580
  }
27581
+ /** Publishes assistant messages not yet metered; returns false when one is unusable. */
27582
+ #meterUsage(messages, historical) {
27583
+ let usable = true;
27584
+ const previousCacheHitRate = this.#cacheHitRatePercent;
27585
+ for (const message of messages) {
27586
+ let request;
27587
+ try {
27588
+ request = v2UsageRequest(message, historical, this.#firstOutputAt.get(message.id));
27589
+ } catch {
27590
+ this.#cacheHitRatePercent = void 0;
27591
+ if (this.#meteredMessages.has(message.id))
27592
+ continue;
27593
+ this.#meteredMessages.add(message.id);
27594
+ if (!historical)
27595
+ this.#emit({ type: "usage.history", complete: false });
27596
+ usable = false;
27597
+ continue;
27598
+ }
27599
+ if (!request)
27600
+ continue;
27601
+ this.#cacheHitRatePercent = request.inputTokens > 0 && request.cachedInputTokens !== void 0 ? request.cachedInputTokens / request.inputTokens * 100 : void 0;
27602
+ if (this.#meteredMessages.has(message.id))
27603
+ continue;
27604
+ this.#meteredMessages.add(message.id);
27605
+ this.#firstOutputAt.delete(message.id);
27606
+ this.#emit({ type: "usage.request", request });
27607
+ }
27608
+ if (this.#cacheHitRatePercent !== previousCacheHitRate)
27609
+ this.#emit({
27610
+ type: "session.usage.changed",
27611
+ usage: this.#usage()
27612
+ });
27613
+ return usable;
27614
+ }
27615
+ #usage() {
27616
+ const usage = v2Usage(this.info);
27617
+ if (this.#cacheHitRatePercent !== void 0)
27618
+ usage.cacheHitRatePercent = this.#cacheHitRatePercent;
27619
+ return usage;
27620
+ }
27621
+ /** Replays every metered request of the native history, then declares it complete. */
27622
+ async #replayUsage() {
27623
+ let complete = false;
27624
+ try {
27625
+ complete = this.#meterUsage(await readMessages(this.client, this.info.id), true);
27626
+ } catch {
27627
+ complete = false;
27628
+ }
27629
+ this.#emit({ type: "usage.history", complete });
27630
+ }
27057
27631
  #refresh() {
27058
27632
  if (this.#closed || !this.#active?.admitted)
27059
27633
  return Promise.resolve();
@@ -27072,6 +27646,7 @@ var V2Session = class {
27072
27646
  if (this.#active !== active || this.#closed)
27073
27647
  return;
27074
27648
  this.info = info;
27649
+ this.#meterUsage(messages, false);
27075
27650
  const turns = snapshot.turns.filter((t) => !active.baseline.has(t.nativeTurnRef.nativeTurnKey));
27076
27651
  if (turns.length > 1)
27077
27652
  throw new Error("Concurrent native prompts cannot be assigned to one Host Turn");
@@ -27168,7 +27743,7 @@ var V2Session = class {
27168
27743
  });
27169
27744
  this.#emit({
27170
27745
  type: "session.usage.changed",
27171
- usage: v2Usage(this.info),
27746
+ usage: this.#usage(),
27172
27747
  observedForTurnId: active.turnId
27173
27748
  });
27174
27749
  this.#active = void 0;
@@ -27364,10 +27939,10 @@ var V2Adapter = class {
27364
27939
  const catalog = await this.#catalog(client2, input.cwd);
27365
27940
  let info;
27366
27941
  if (input.kind === "create") {
27367
- const model = selectedModel(input, catalog);
27942
+ const model2 = selectedModel(input, catalog);
27368
27943
  info = await client2.session.create({
27369
27944
  location: { directory: input.cwd },
27370
- ...model ? { model } : {},
27945
+ ...model2 ? { model: model2 } : {},
27371
27946
  permissions: v2Permissions([], input.permissionModeId ?? harnessPermissionModeIdSchema.parse(policy === "unattended-full-access" ? "allow" : "default"))
27372
27947
  });
27373
27948
  created = info.id;
@@ -27384,19 +27959,19 @@ var V2Adapter = class {
27384
27959
  if (input.kind !== "resume") {
27385
27960
  const parent = info;
27386
27961
  const turns = source.snapshot.turns;
27387
- let count;
27962
+ let count2;
27388
27963
  if (input.kind === "rollbackLastTurn") {
27389
27964
  if (!turns.length)
27390
27965
  throw new Error("OpenCode history is empty");
27391
- count = turns.length - 1;
27966
+ count2 = turns.length - 1;
27392
27967
  } else {
27393
27968
  if (input.checkpoint.harnessId !== harnessId || input.checkpoint.nativeSessionId !== info.id)
27394
27969
  throw new Error("Checkpoint belongs to another Session");
27395
- count = turns.findIndex((turn) => turn.checkpoint?.checkpointId === input.checkpoint.checkpointId) + 1;
27396
- if (!count || JSON.stringify(turns[count - 1]?.checkpoint) !== JSON.stringify(input.checkpoint))
27970
+ count2 = turns.findIndex((turn) => turn.checkpoint?.checkpointId === input.checkpoint.checkpointId) + 1;
27971
+ if (!count2 || JSON.stringify(turns[count2 - 1]?.checkpoint) !== JSON.stringify(input.checkpoint))
27397
27972
  throw new Error("OpenCode checkpoint does not match native history");
27398
27973
  }
27399
- const before = turns[count]?.nativeTurnRef.nativeTurnKey;
27974
+ const before = turns[count2]?.nativeTurnRef.nativeTurnKey;
27400
27975
  info = await client2.session.fork({ sessionID: parent.id, ...before ? { before } : {} });
27401
27976
  if (info.id === parent.id)
27402
27977
  throw new Error("OpenCode fork returned its source Session");
@@ -27411,7 +27986,7 @@ var V2Adapter = class {
27411
27986
  await client2.session.switchModel({ sessionID: info.id, model: parent.model });
27412
27987
  info = await client2.session.get({ sessionID: info.id });
27413
27988
  const derived = await readHistory(client2, info, this.options.toolOutputLimit ?? 64e3);
27414
- if (historyContent(derived.snapshot, false) !== historyContent({ turns: turns.slice(0, count) }, false))
27989
+ if (historyContent(derived.snapshot, false) !== historyContent({ turns: turns.slice(0, count2) }, false))
27415
27990
  throw new Error("OpenCode fork did not preserve the exact history prefix");
27416
27991
  if (!isDeepStrictEqual2(info.permissions ?? [], parent.permissions ?? []) || !isDeepStrictEqual2(info.model, parent.model))
27417
27992
  throw new Error("OpenCode fork did not preserve the current configuration");
@@ -27460,14 +28035,14 @@ function selectedModel(input, catalog) {
27460
28035
  throw new Error("Thinking selection requires a Model");
27461
28036
  return void 0;
27462
28037
  }
27463
- const entry = catalog.models.find((model2) => model2.ref.id === selected.id);
28038
+ const entry = catalog.models.find((model3) => model3.ref.id === selected.id);
27464
28039
  if (!entry)
27465
28040
  throw new Error("OpenCode Model is not available");
27466
28041
  if (input.thinkingOptionId && !entry.supportedThinkingOptionIds?.includes(input.thinkingOptionId))
27467
28042
  throw new Error("OpenCode Thinking option is not available for this Model");
27468
- const model = decodeOpenCodeModelRef(selected);
28043
+ const model2 = decodeOpenCodeModelRef(selected);
27469
28044
  const variant = input.thinkingOptionId ? decodeOpenCodeVariant(input.thinkingOptionId) : void 0;
27470
- return { providerID: model.providerID, id: model.modelID, ...variant ? { variant } : {} };
28045
+ return { providerID: model2.providerID, id: model2.modelID, ...variant ? { variant } : {} };
27471
28046
  }
27472
28047
  function historyContent(snapshot, identities = true) {
27473
28048
  return JSON.stringify(snapshot.turns.map((turn) => ({
@@ -27491,9 +28066,11 @@ var OpenCodeAdapter2 = class {
27491
28066
  commandCatalog = openCodeCommandCatalog;
27492
28067
  #adapters = /* @__PURE__ */ new Set();
27493
28068
  #closed = false;
28069
+ usageStatistics;
27494
28070
  constructor(options = {}, dependencies) {
27495
28071
  this.options = options;
27496
28072
  this.dependencies = dependencies;
28073
+ this.usageStatistics = createOpenCodeUsageStatistics(options.environment ?? process.env);
27497
28074
  }
27498
28075
  async #select(environment, input) {
27499
28076
  if (this.#closed)
@@ -27603,7 +28180,7 @@ function normalize(error51) {
27603
28180
  // dist/installation.js
27604
28181
  import { realpath as realpath2 } from "node:fs/promises";
27605
28182
  import { homedir as homedir3 } from "node:os";
27606
- import path12 from "node:path";
28183
+ import path13 from "node:path";
27607
28184
  function createOpenCodeInstallation(environment, command) {
27608
28185
  let update;
27609
28186
  return createInstallationManager({
@@ -27624,8 +28201,8 @@ function createOpenCodeInstallation(environment, command) {
27624
28201
  const release = JSON.parse(await fetchInstallationText("https://api.github.com/repos/anomalyco/opencode/releases/latest"));
27625
28202
  latestVersion = installationVersion(release.tag_name);
27626
28203
  const home = environment.HOME ?? environment.USERPROFILE ?? homedir3();
27627
- const standard = path12.join(home, ".opencode", "bin", process.platform === "win32" ? "opencode.exe" : "opencode");
27628
- canUpdate = process.platform !== "win32" && path12.resolve(executable) === standard && await realpath2(executable) === standard;
28204
+ const standard = path13.join(home, ".opencode", "bin", process.platform === "win32" ? "opencode.exe" : "opencode");
28205
+ canUpdate = process.platform !== "win32" && path13.resolve(executable) === standard && await realpath2(executable) === standard;
27629
28206
  update = async (version2) => {
27630
28207
  await runInstallationCommand(executable, ["upgrade", version2, "--method", "curl"], environment, 3e5);
27631
28208
  };