@ai-development-environment/control-agent 0.0.111 → 0.0.113

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 (2) hide show
  1. package/dist/control-agent.js +1070 -248
  2. package/package.json +1 -1
@@ -3814,12 +3814,12 @@ var require_isexe = __commonJS({
3814
3814
  if (typeof Promise !== "function") {
3815
3815
  throw new TypeError("callback not provided");
3816
3816
  }
3817
- return new Promise(function(resolve10, reject) {
3817
+ return new Promise(function(resolve11, reject) {
3818
3818
  isexe(path, options || {}, function(er2, is) {
3819
3819
  if (er2) {
3820
3820
  reject(er2);
3821
3821
  } else {
3822
- resolve10(is);
3822
+ resolve11(is);
3823
3823
  }
3824
3824
  });
3825
3825
  });
@@ -3885,27 +3885,27 @@ var require_which = __commonJS({
3885
3885
  opt = {};
3886
3886
  const { pathEnv, pathExt, pathExtExe } = getPathInfo(cmd, opt);
3887
3887
  const found = [];
3888
- const step = (i) => new Promise((resolve10, reject) => {
3888
+ const step = (i) => new Promise((resolve11, reject) => {
3889
3889
  if (i === pathEnv.length)
3890
- return opt.all && found.length ? resolve10(found) : reject(getNotFoundError(cmd));
3890
+ return opt.all && found.length ? resolve11(found) : reject(getNotFoundError(cmd));
3891
3891
  const ppRaw = pathEnv[i];
3892
3892
  const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw;
3893
3893
  const pCmd = path.join(pathPart, cmd);
3894
3894
  const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
3895
- resolve10(subStep(p, i, 0));
3895
+ resolve11(subStep(p, i, 0));
3896
3896
  });
3897
- const subStep = (p, i, ii) => new Promise((resolve10, reject) => {
3897
+ const subStep = (p, i, ii) => new Promise((resolve11, reject) => {
3898
3898
  if (ii === pathExt.length)
3899
- return resolve10(step(i + 1));
3899
+ return resolve11(step(i + 1));
3900
3900
  const ext = pathExt[ii];
3901
3901
  isexe(p + ext, { pathExt: pathExtExe }, (er2, is) => {
3902
3902
  if (!er2 && is) {
3903
3903
  if (opt.all)
3904
3904
  found.push(p + ext);
3905
3905
  else
3906
- return resolve10(p + ext);
3906
+ return resolve11(p + ext);
3907
3907
  }
3908
- return resolve10(subStep(p, i, ii + 1));
3908
+ return resolve11(subStep(p, i, ii + 1));
3909
3909
  });
3910
3910
  });
3911
3911
  return cb2 ? step(0).then((res) => cb2(null, res), cb2) : step(0);
@@ -4511,8 +4511,8 @@ function createClient(options) {
4511
4511
  retryWait = async function randomisedExponentialBackoff(retries2) {
4512
4512
  const retryDelaySeconds = Math.pow(2, retries2);
4513
4513
  await new Promise(
4514
- (resolve10) => setTimeout(
4515
- resolve10,
4514
+ (resolve11) => setTimeout(
4515
+ resolve11,
4516
4516
  retryDelaySeconds * 1e3 + // add random timeout from 300ms to 3s
4517
4517
  Math.floor(Math.random() * (3e3 - 300) + 300)
4518
4518
  )
@@ -4754,14 +4754,14 @@ function createClient(options) {
4754
4754
  if (socket.readyState === WebSocketImpl.CLOSING) await throwOnClose;
4755
4755
  let release2 = () => {
4756
4756
  };
4757
- const released = new Promise((resolve10) => release2 = resolve10);
4757
+ const released = new Promise((resolve11) => release2 = resolve11);
4758
4758
  return [
4759
4759
  socket,
4760
4760
  release2,
4761
4761
  // resolves on release or rejects on error/close, whichever comes first.
4762
4762
  // not racing with throwOnClose on purpose, it lives as long as the socket
4763
4763
  // and every race would leak memory per operation
4764
- new Promise((resolve10, reject) => {
4764
+ new Promise((resolve11, reject) => {
4765
4765
  const unlisten = errorOrClosed(reject);
4766
4766
  if (socket.readyState !== WebSocketImpl.OPEN)
4767
4767
  throwOnClose.catch(reject);
@@ -4777,7 +4777,7 @@ function createClient(options) {
4777
4777
  complete();
4778
4778
  }
4779
4779
  }
4780
- resolve10();
4780
+ resolve11();
4781
4781
  });
4782
4782
  })
4783
4783
  ];
@@ -4922,7 +4922,7 @@ function createClient(options) {
4922
4922
  const iterator = (async function* iterator2() {
4923
4923
  for (; ; ) {
4924
4924
  if (!pending.length) {
4925
- await new Promise((resolve10) => deferred.resolve = resolve10);
4925
+ await new Promise((resolve11) => deferred.resolve = resolve11);
4926
4926
  }
4927
4927
  while (pending.length) {
4928
4928
  yield pending.shift();
@@ -5026,11 +5026,11 @@ async function writeArtifactTransferBytes(writer, bytes, position) {
5026
5026
  }
5027
5027
  }
5028
5028
  async function artifactTransferRetryDelay(attempt, signal) {
5029
- await new Promise((resolve10, reject) => {
5029
+ await new Promise((resolve11, reject) => {
5030
5030
  const timeout = setTimeout(
5031
5031
  () => {
5032
5032
  signal.removeEventListener("abort", abort);
5033
- resolve10();
5033
+ resolve11();
5034
5034
  },
5035
5035
  Math.min(4e3, 250 * 2 ** attempt)
5036
5036
  );
@@ -5441,7 +5441,7 @@ var AgentGraphQLClient = class {
5441
5441
  metadata.error || `Artifact transfer ${metadata.status.toLowerCase()}`
5442
5442
  );
5443
5443
  }
5444
- await new Promise((resolve10) => setTimeout(resolve10, 1e3));
5444
+ await new Promise((resolve11) => setTimeout(resolve11, 1e3));
5445
5445
  }
5446
5446
  if (!Number.isSafeInteger(metadata.uploadLength) || !metadata.uploadLength || !metadata.checksum || !metadata.filename) {
5447
5447
  throw new Error("Artifact transfer metadata is incomplete");
@@ -5815,7 +5815,7 @@ function createAgentSubscriptionClient(config) {
5815
5815
  shouldRetry: () => true,
5816
5816
  retryWait: async (retries) => {
5817
5817
  const delay = Math.min(3e4, 1e3 * 2 ** Math.min(retries, 5));
5818
- await new Promise((resolve10) => setTimeout(resolve10, delay));
5818
+ await new Promise((resolve11) => setTimeout(resolve11, delay));
5819
5819
  },
5820
5820
  on: {
5821
5821
  connected: () => console.log("Connected to control-plane WebSocket"),
@@ -7954,6 +7954,16 @@ var DSYMS_ARTIFACT_FILENAME = "dSYMs.zip";
7954
7954
  function resolveCollectDsyms(settings, action) {
7955
7955
  return settings.collectDsyms ?? action === "ARCHIVE";
7956
7956
  }
7957
+ var BUILD_ENDPOINT_PATHS = {
7958
+ consoleLogs: "/api/telemetry/console-logs",
7959
+ analyticsEvents: "/api/telemetry/analytics-events",
7960
+ apnsRegistration: "/api/ios/apns-devices",
7961
+ crashReports: "/api/public/crashes",
7962
+ dsyms: "/api/dsyms",
7963
+ dsymUploads: "/api/dsyms/uploads",
7964
+ dsymUpload: "/api/dsyms/uploads/{uploadId}",
7965
+ dsymUploadComplete: "/api/dsyms/uploads/{uploadId}/complete"
7966
+ };
7957
7967
  var BUILD_REPORT_KINDS = ["TEST_RESULTS", "CODE_COVERAGE"];
7958
7968
  function objectValue7(value, name) {
7959
7969
  if (!value || typeof value !== "object" || Array.isArray(value)) {
@@ -8218,26 +8228,6 @@ function parseBuildTelemetrySettings(value) {
8218
8228
  if (value === void 0 || value === null) return void 0;
8219
8229
  const input = objectValue7(value, "build job payload.telemetry");
8220
8230
  return {
8221
- localBaseUrl: httpUrl(
8222
- input.localBaseUrl,
8223
- "build job payload.telemetry.localBaseUrl"
8224
- ),
8225
- remoteBaseUrl: httpUrl(
8226
- input.remoteBaseUrl,
8227
- "build job payload.telemetry.remoteBaseUrl"
8228
- ),
8229
- selectedBaseUrl: httpUrl(
8230
- input.selectedBaseUrl,
8231
- "build job payload.telemetry.selectedBaseUrl"
8232
- ),
8233
- consoleLogsUrl: httpUrl(
8234
- input.consoleLogsUrl,
8235
- "build job payload.telemetry.consoleLogsUrl"
8236
- ),
8237
- analyticsEventsUrl: httpUrl(
8238
- input.analyticsEventsUrl,
8239
- "build job payload.telemetry.analyticsEventsUrl"
8240
- ),
8241
8231
  consoleCollectionEnabled: booleanValue2(
8242
8232
  input.consoleCollectionEnabled,
8243
8233
  "build job payload.telemetry.consoleCollectionEnabled"
@@ -8248,6 +8238,40 @@ function parseBuildTelemetrySettings(value) {
8248
8238
  )
8249
8239
  };
8250
8240
  }
8241
+ function parseBuildServer(value) {
8242
+ if (value == null) return void 0;
8243
+ const input = objectValue7(value, "build job payload.server");
8244
+ const selectedUrlKind = input.selectedUrlKind;
8245
+ if (selectedUrlKind !== "LOCAL" && selectedUrlKind !== "REMOTE" && selectedUrlKind !== "PROXY")
8246
+ throw new Error("Invalid build server URL kind");
8247
+ const origin2 = (value2, name) => {
8248
+ const result = httpUrl(value2, name);
8249
+ const parsed = new URL(result);
8250
+ if (parsed.pathname !== "/" || parsed.search || parsed.hash || parsed.username || parsed.password)
8251
+ throw new Error(`${name} must be an HTTP(S) origin`);
8252
+ return parsed.origin;
8253
+ };
8254
+ const server = {
8255
+ localBaseUrl: origin2(input.localBaseUrl, "server.localBaseUrl"),
8256
+ remoteBaseUrl: origin2(input.remoteBaseUrl, "server.remoteBaseUrl"),
8257
+ proxyBaseUrl: input.proxyBaseUrl == null ? null : origin2(input.proxyBaseUrl, "server.proxyBaseUrl"),
8258
+ selectedUrlKind,
8259
+ selectedBaseUrl: origin2(input.selectedBaseUrl, "server.selectedBaseUrl")
8260
+ };
8261
+ const selected = selectedUrlKind === "LOCAL" ? server.localBaseUrl : selectedUrlKind === "REMOTE" ? server.remoteBaseUrl : server.proxyBaseUrl;
8262
+ if (selected !== server.selectedBaseUrl)
8263
+ throw new Error("Selected server URL must match its configured base URL");
8264
+ return server;
8265
+ }
8266
+ function parseBuildEndpointPaths(value) {
8267
+ if (value == null) return void 0;
8268
+ const input = objectValue7(value, "build job payload.endpointPaths");
8269
+ for (const [key, expected] of Object.entries(BUILD_ENDPOINT_PATHS)) {
8270
+ if (input[key] !== expected)
8271
+ throw new Error(`Invalid build endpoint path: ${key}`);
8272
+ }
8273
+ return { ...BUILD_ENDPOINT_PATHS };
8274
+ }
8251
8275
  function parseBuildSourceDiscoverPayload(value) {
8252
8276
  return worktreeIdentity(objectValue7(value, "source discovery payload"));
8253
8277
  }
@@ -8336,6 +8360,8 @@ function parseBuildJobPayload(value) {
8336
8360
  advancedSettings,
8337
8361
  scripts,
8338
8362
  telemetry: parseBuildTelemetrySettings(input.telemetry),
8363
+ server: parseBuildServer(input.server),
8364
+ endpointPaths: parseBuildEndpointPaths(input.endpointPaths),
8339
8365
  worktreeCoverage: input.worktreeCoverage === void 0 ? false : booleanValue2(
8340
8366
  input.worktreeCoverage,
8341
8367
  "build job payload.worktreeCoverage"
@@ -9567,7 +9593,7 @@ function captureCommand(options) {
9567
9593
  outputTruncated: false
9568
9594
  });
9569
9595
  }
9570
- return new Promise((resolve10, reject) => {
9596
+ return new Promise((resolve11, reject) => {
9571
9597
  const stdoutChunks = [];
9572
9598
  const stderrChunks = [];
9573
9599
  let stdoutBytes = 0;
@@ -9663,7 +9689,7 @@ function captureCommand(options) {
9663
9689
  clearTimeout(timeout);
9664
9690
  if (killTimer) clearTimeout(killTimer);
9665
9691
  options.signal.removeEventListener("abort", abort);
9666
- resolve10({
9692
+ resolve11({
9667
9693
  exitCode,
9668
9694
  signal,
9669
9695
  timedOut,
@@ -9751,11 +9777,11 @@ async function reapOrphanedCommandProcesses(agentId, signal = new AbortControlle
9751
9777
  console.error(
9752
9778
  `Terminating ${signalled.length} command process(es) left by a previous agent run.`
9753
9779
  );
9754
- await new Promise((resolve10) => {
9755
- const timer = setTimeout(resolve10, TERMINATE_GRACE_MS);
9780
+ await new Promise((resolve11) => {
9781
+ const timer = setTimeout(resolve11, TERMINATE_GRACE_MS);
9756
9782
  timer.unref();
9757
- if (signal.aborted) resolve10();
9758
- else signal.addEventListener("abort", () => resolve10(), { once: true });
9783
+ if (signal.aborted) resolve11();
9784
+ else signal.addEventListener("abort", () => resolve11(), { once: true });
9759
9785
  });
9760
9786
  for (const orphan of signalled) signalGroup(orphan.processGroup, "SIGKILL");
9761
9787
  return signalled;
@@ -9811,14 +9837,14 @@ var runCommand = async (rawPayload, _timeoutMs, signal, _onLog, context) => {
9811
9837
  `Could not append command output; retrying in ${delay}ms:`,
9812
9838
  error instanceof Error ? error.message : error
9813
9839
  );
9814
- await new Promise((resolve10) => {
9840
+ await new Promise((resolve11) => {
9815
9841
  const onAbort = () => {
9816
9842
  clearTimeout(retryTimer);
9817
- resolve10();
9843
+ resolve11();
9818
9844
  };
9819
9845
  const retryTimer = setTimeout(() => {
9820
9846
  signal.removeEventListener("abort", onAbort);
9821
- resolve10();
9847
+ resolve11();
9822
9848
  }, delay);
9823
9849
  retryTimer.unref();
9824
9850
  signal.addEventListener("abort", onAbort, { once: true });
@@ -9856,7 +9882,7 @@ var runCommand = async (rawPayload, _timeoutMs, signal, _onLog, context) => {
9856
9882
  }
9857
9883
  };
9858
9884
  try {
9859
- return await new Promise((resolve10, reject) => {
9885
+ return await new Promise((resolve11, reject) => {
9860
9886
  let cancelled = false;
9861
9887
  let settled = false;
9862
9888
  const child = spawn2(process.env.SHELL || "/bin/sh", ["-l", scriptPath], {
@@ -9923,7 +9949,7 @@ var runCommand = async (rawPayload, _timeoutMs, signal, _onLog, context) => {
9923
9949
  signal.removeEventListener("abort", abort);
9924
9950
  flush();
9925
9951
  void uploadChain.then(
9926
- () => resolve10({
9952
+ () => resolve11({
9927
9953
  exitCode,
9928
9954
  signal: closeSignal,
9929
9955
  timedOut: false,
@@ -9964,8 +9990,8 @@ var RepositoryCoordinator = class {
9964
9990
  async run(key, task) {
9965
9991
  const previous = this.tails.get(key) ?? Promise.resolve();
9966
9992
  let release2;
9967
- const current = new Promise((resolve10) => {
9968
- release2 = resolve10;
9993
+ const current = new Promise((resolve11) => {
9994
+ release2 = resolve11;
9969
9995
  });
9970
9996
  const tail = previous.then(() => current);
9971
9997
  this.tails.set(key, tail);
@@ -12688,12 +12714,22 @@ if (typeof hookModule.default === "function") {
12688
12714
  BUILD_PHASE: options.phase,
12689
12715
  BUILD_CONTEXT_PATH: options.contextPath,
12690
12716
  BUILD_ARTIFACT_DIRECTORY: options.input.artifactDirectory,
12717
+ ...options.input.server ? {
12718
+ SERVER_LOCAL_BASE_URL: options.input.server.localBaseUrl,
12719
+ SERVER_REMOTE_BASE_URL: options.input.server.remoteBaseUrl,
12720
+ SERVER_PROXY_BASE_URL: options.input.server.proxyBaseUrl ?? "",
12721
+ SERVER_BASE_URL: options.input.server.selectedBaseUrl,
12722
+ SERVER_URL_KIND: options.input.server.selectedUrlKind
12723
+ } : {},
12724
+ ...Object.fromEntries(
12725
+ Object.entries(options.input.endpointPaths ?? {}).map(
12726
+ ([key, value]) => [
12727
+ `${key.replace(/[A-Z]/g, (letter) => `_${letter}`).toUpperCase()}_PATH`,
12728
+ value
12729
+ ]
12730
+ )
12731
+ ),
12691
12732
  ...options.input.telemetry ? {
12692
- TELEMETRY_LOCAL_BASE_URL: options.input.telemetry.localBaseUrl,
12693
- TELEMETRY_REMOTE_BASE_URL: options.input.telemetry.remoteBaseUrl,
12694
- TELEMETRY_BASE_URL: options.input.telemetry.selectedBaseUrl,
12695
- CONSOLE_LOGS_URL: options.input.telemetry.consoleLogsUrl,
12696
- ANALYTICS_EVENTS_URL: options.input.telemetry.analyticsEventsUrl,
12697
12733
  CONSOLE_LOG_COLLECTION_ENABLED: String(
12698
12734
  options.input.telemetry.consoleCollectionEnabled
12699
12735
  ),
@@ -12879,14 +12915,14 @@ function normalizeTestResults(value) {
12879
12915
  suite: null
12880
12916
  });
12881
12917
  }
12882
- const count = (result) => tests.filter((test) => test.result === result).length;
12918
+ const count3 = (result) => tests.filter((test) => test.result === result).length;
12883
12919
  return {
12884
12920
  summary: {
12885
12921
  total: tests.length,
12886
- passed: count("Passed"),
12887
- failed: count("Failed"),
12888
- skipped: count("Skipped"),
12889
- expectedFailures: count("Expected Failure"),
12922
+ passed: count3("Passed"),
12923
+ failed: count3("Failed"),
12924
+ skipped: count3("Skipped"),
12925
+ expectedFailures: count3("Expected Failure"),
12890
12926
  unknown: tests.filter(
12891
12927
  (test) => !["Passed", "Failed", "Skipped", "Expected Failure"].includes(
12892
12928
  String(test.result)
@@ -13018,9 +13054,9 @@ function changedLinesFromPatch(value) {
13018
13054
  const match = /\+(\d+)(?:,(\d+))?/.exec(line);
13019
13055
  if (!match) continue;
13020
13056
  const start = Number(match[1]);
13021
- const count = match[2] === void 0 ? 1 : Number(match[2]);
13057
+ const count3 = match[2] === void 0 ? 1 : Number(match[2]);
13022
13058
  const lines = result.get(path);
13023
- for (let number = start; number < start + count; number += 1) {
13059
+ for (let number = start; number < start + count3; number += 1) {
13024
13060
  lines.add(number);
13025
13061
  }
13026
13062
  }
@@ -13516,7 +13552,9 @@ var runIosBuild = async (payload, timeoutMs, signal, onLog, context) => {
13516
13552
  source: input.source,
13517
13553
  scheme: input.scheme,
13518
13554
  configuration: input.configuration,
13519
- telemetry: input.telemetry ?? null
13555
+ telemetry: input.telemetry ?? null,
13556
+ server: input.server ?? null,
13557
+ endpointPaths: input.endpointPaths ?? null
13520
13558
  };
13521
13559
  let buildResult = null;
13522
13560
  let preflightResult = null;
@@ -15396,7 +15434,7 @@ function prependPathDirectory(directory, env = process.env) {
15396
15434
  // src/process-runner.ts
15397
15435
  import { spawn as spawn5 } from "node:child_process";
15398
15436
  async function runProcess(options) {
15399
- return new Promise((resolve10, reject) => {
15437
+ return new Promise((resolve11, reject) => {
15400
15438
  let sequence = 0;
15401
15439
  let timedOut = false;
15402
15440
  let cancelled = false;
@@ -15491,7 +15529,7 @@ async function runProcess(options) {
15491
15529
  clearTimeout(timeout);
15492
15530
  options.signal.removeEventListener("abort", abort);
15493
15531
  void logChain.then(
15494
- () => resolve10({ exitCode, signal, timedOut, cancelled })
15532
+ () => resolve11({ exitCode, signal, timedOut, cancelled })
15495
15533
  );
15496
15534
  });
15497
15535
  });
@@ -16409,8 +16447,8 @@ async function fileExists(path) {
16409
16447
  return false;
16410
16448
  }
16411
16449
  }
16412
- async function findClaudeSessionFile(nativeId, home) {
16413
- const projectsDir = join12(home, ".claude", "projects");
16450
+ async function findClaudeSessionFile(nativeId, home, claudeHome = join12(home, ".claude")) {
16451
+ const projectsDir = join12(claudeHome, "projects");
16414
16452
  let entries;
16415
16453
  try {
16416
16454
  entries = await readdir7(projectsDir, { withFileTypes: true });
@@ -16425,8 +16463,8 @@ async function findClaudeSessionFile(nativeId, home) {
16425
16463
  }
16426
16464
  return null;
16427
16465
  }
16428
- async function findCodexSessionFile(nativeId, home) {
16429
- const sessionsDir = join12(home, ".codex", "sessions");
16466
+ async function findCodexSessionFile(nativeId, home, codexHome = join12(home, ".codex")) {
16467
+ const sessionsDir = join12(codexHome, "sessions");
16430
16468
  const suffix = `-${nativeId}.jsonl`;
16431
16469
  const walk = async (dir) => {
16432
16470
  let entries;
@@ -16446,10 +16484,14 @@ async function findCodexSessionFile(nativeId, home) {
16446
16484
  }
16447
16485
  return null;
16448
16486
  };
16449
- return walk(sessionsDir);
16487
+ return await walk(sessionsDir) ?? walk(join12(codexHome, "archived_sessions"));
16450
16488
  }
16451
16489
  async function locateSessionFile(payload, home) {
16452
- return payload.provider === "CLAUDE" ? findClaudeSessionFile(payload.nativeId, home) : findCodexSessionFile(payload.nativeId, home);
16490
+ return payload.provider === "CLAUDE" ? findClaudeSessionFile(
16491
+ payload.nativeId,
16492
+ home,
16493
+ process.env.CLAUDE_CONFIG_DIR
16494
+ ) : findCodexSessionFile(payload.nativeId, home, process.env.CODEX_HOME);
16453
16495
  }
16454
16496
  var readRunSession = async (payload) => {
16455
16497
  const input = parseRunSessionReadPayload(payload);
@@ -16514,7 +16556,7 @@ async function optionalGit(cwd, args) {
16514
16556
  }
16515
16557
  function cappedGit(cwd, args, limit) {
16516
16558
  const budget = limit - TRUNCATION_RESERVE;
16517
- return new Promise((resolve10, reject) => {
16559
+ return new Promise((resolve11, reject) => {
16518
16560
  const child = spawn6("git", args, {
16519
16561
  cwd,
16520
16562
  env: {
@@ -16564,7 +16606,7 @@ function cappedGit(cwd, args, limit) {
16564
16606
  return;
16565
16607
  }
16566
16608
  const output = Buffer.concat(chunks).toString("utf8");
16567
- resolve10(
16609
+ resolve11(
16568
16610
  truncated ? `${output.trimEnd()}
16569
16611
 
16570
16612
  [Truncated after ${budget} bytes]` : output.trim()
@@ -20107,11 +20149,11 @@ var mutationQueues = /* @__PURE__ */ new Map();
20107
20149
  function objectValue15(value) {
20108
20150
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
20109
20151
  }
20110
- function resolveTailscaleExecutable(platform2 = process.platform, resolve10 = findExecutable, isExecutable2 = (path) => {
20152
+ function resolveTailscaleExecutable(platform2 = process.platform, resolve11 = findExecutable, isExecutable2 = (path) => {
20111
20153
  accessSync2(path, constants2.X_OK);
20112
20154
  return true;
20113
20155
  }) {
20114
- const resolved = resolve10("tailscale", {
20156
+ const resolved = resolve11("tailscale", {
20115
20157
  overrideVariable: "CONTROL_AGENT_TAILSCALE_EXECUTABLE"
20116
20158
  });
20117
20159
  if (resolved) return resolved;
@@ -20320,8 +20362,8 @@ function tailscaleUpsertCommandSequence(route, previousRoute) {
20320
20362
  async function serialized(agentId, work) {
20321
20363
  const previous = mutationQueues.get(agentId) ?? Promise.resolve();
20322
20364
  let release2;
20323
- const current = new Promise((resolve10) => {
20324
- release2 = resolve10;
20365
+ const current = new Promise((resolve11) => {
20366
+ release2 = resolve11;
20325
20367
  });
20326
20368
  const tail = previous.then(() => current);
20327
20369
  mutationQueues.set(agentId, tail);
@@ -20581,7 +20623,7 @@ var JobExecutor = class {
20581
20623
  );
20582
20624
  if (this.stopping) return;
20583
20625
  const delay = Math.min(3e4, 1e3 * 2 ** Math.min(retry++, 5));
20584
- await new Promise((resolve10) => setTimeout(resolve10, delay));
20626
+ await new Promise((resolve11) => setTimeout(resolve11, delay));
20585
20627
  }
20586
20628
  }
20587
20629
  }
@@ -20870,6 +20912,254 @@ var RunJournal = class {
20870
20912
 
20871
20913
  // src/runs/claude-adapter.ts
20872
20914
  import { randomUUID as randomUUID6 } from "node:crypto";
20915
+ import { homedir as homedir11 } from "node:os";
20916
+
20917
+ // src/runs/claude-import.ts
20918
+ import { createReadStream as createReadStream4 } from "node:fs";
20919
+ import { createInterface } from "node:readline";
20920
+
20921
+ // src/runs/provider.ts
20922
+ function asRecord(value) {
20923
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
20924
+ }
20925
+ function firstString(value) {
20926
+ const keys = [
20927
+ "text",
20928
+ "result",
20929
+ "message",
20930
+ "summary",
20931
+ "content",
20932
+ "output",
20933
+ "items",
20934
+ "parts"
20935
+ ];
20936
+ const pending = [value];
20937
+ const visited = /* @__PURE__ */ new WeakSet();
20938
+ while (pending.length) {
20939
+ const candidate = pending.pop();
20940
+ if (typeof candidate === "string" && candidate.trim()) return candidate;
20941
+ if (!candidate || typeof candidate !== "object") continue;
20942
+ if (visited.has(candidate)) continue;
20943
+ visited.add(candidate);
20944
+ if (Array.isArray(candidate)) {
20945
+ for (let index = candidate.length - 1; index >= 0; index -= 1) {
20946
+ pending.push(candidate[index]);
20947
+ }
20948
+ continue;
20949
+ }
20950
+ const record = asRecord(candidate);
20951
+ for (let index = keys.length - 1; index >= 0; index -= 1) {
20952
+ pending.push(record[keys[index]]);
20953
+ }
20954
+ }
20955
+ return void 0;
20956
+ }
20957
+ function answerArrays(value) {
20958
+ if (Array.isArray(value)) {
20959
+ return value.map(
20960
+ (entry) => Array.isArray(entry) ? entry.map(String) : typeof entry === "object" && entry ? Array.isArray(entry.answers) ? entry.answers.map(
20961
+ String
20962
+ ) : [String(entry.answer ?? "")] : [String(entry)]
20963
+ );
20964
+ }
20965
+ const record = asRecord(value);
20966
+ return Object.values(record).map(
20967
+ (entry) => Array.isArray(asRecord(entry).answers) ? asRecord(entry).answers.map(String) : Array.isArray(entry) ? entry.map(String) : [String(entry ?? "")]
20968
+ );
20969
+ }
20970
+
20971
+ // src/runs/claude-import.ts
20972
+ var tokenFields = [
20973
+ "inputTokens",
20974
+ "outputTokens",
20975
+ "reasoningTokens",
20976
+ "cacheReadTokens",
20977
+ "cacheWriteTokens"
20978
+ ];
20979
+ function amount(value) {
20980
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
20981
+ }
20982
+ function count(value) {
20983
+ return Math.floor(amount(value) ?? 0);
20984
+ }
20985
+ function textBlocks(content) {
20986
+ if (typeof content === "string") return content.trim();
20987
+ return Array.isArray(content) ? content.map(asRecord).filter(
20988
+ (block) => block.type === "text" && typeof block.text === "string"
20989
+ ).map((block) => String(block.text).trim()).filter(Boolean).join("\n\n") : "";
20990
+ }
20991
+ function timestamp(value) {
20992
+ const date = typeof value === "string" || typeof value === "number" ? new Date(value) : void 0;
20993
+ return date && Number.isFinite(date.getTime()) ? date.toISOString() : void 0;
20994
+ }
20995
+ function claudeTranscriptMetadata(values) {
20996
+ const records = values.map(asRecord);
20997
+ const requests = /* @__PURE__ */ new Map();
20998
+ let model2;
20999
+ let effort;
21000
+ let lastAssistant = -1;
21001
+ let costIndex = -1;
21002
+ let costState;
21003
+ for (const [index, record] of records.entries()) {
21004
+ if (record.isSidechain === true) continue;
21005
+ if (record.type === "cost-state" && Object.keys(asRecord(record.modelUsage)).length) {
21006
+ costIndex = index;
21007
+ costState = record;
21008
+ }
21009
+ const message = asRecord(record.message);
21010
+ if (record.type !== "assistant" || typeof message.model !== "string" || message.model.startsWith("<"))
21011
+ continue;
21012
+ model2 = message.model;
21013
+ lastAssistant = index;
21014
+ const recordedEffort = record.perTurnEffort ?? record.effort;
21015
+ if (typeof recordedEffort === "string" || typeof recordedEffort === "number")
21016
+ effort = String(recordedEffort);
21017
+ if (!message.usage) continue;
21018
+ const usage2 = asRecord(message.usage);
21019
+ const key = `${model2}:${String(message.id ?? record.uuid ?? index)}`;
21020
+ const previous = requests.get(key);
21021
+ const current = {
21022
+ model: model2,
21023
+ inputTokens: count(usage2.input_tokens),
21024
+ outputTokens: count(usage2.output_tokens),
21025
+ reasoningTokens: count(
21026
+ asRecord(usage2.output_tokens_details).thinking_tokens
21027
+ ),
21028
+ cacheReadTokens: count(usage2.cache_read_input_tokens),
21029
+ cacheWriteTokens: count(usage2.cache_creation_input_tokens),
21030
+ pricingSource: "claude-transcript"
21031
+ };
21032
+ if (previous)
21033
+ for (const field of tokenFields)
21034
+ current[field] = Math.max(previous[field] ?? 0, current[field] ?? 0);
21035
+ requests.set(key, current);
21036
+ }
21037
+ if (costState && costIndex >= lastAssistant) {
21038
+ const usage2 = Object.entries(asRecord(costState.modelUsage)).map(
21039
+ ([model3, value]) => {
21040
+ const item = asRecord(value);
21041
+ return {
21042
+ model: model3,
21043
+ inputTokens: count(item.inputTokens),
21044
+ outputTokens: count(item.outputTokens),
21045
+ reasoningTokens: count(item.thinkingTokens),
21046
+ cacheReadTokens: count(item.cacheReadInputTokens),
21047
+ cacheWriteTokens: count(item.cacheCreationInputTokens),
21048
+ ...costState.hasUnknownModelCost !== true && amount(item.costUSD) !== void 0 ? { estimatedCost: amount(item.costUSD) } : {},
21049
+ pricingSource: "claude-transcript"
21050
+ };
21051
+ }
21052
+ );
21053
+ return {
21054
+ model: model2,
21055
+ effort,
21056
+ usage: usage2,
21057
+ ...costState.hasUnknownModelCost !== true && amount(costState.totalCostUSD) !== void 0 ? {
21058
+ estimatedCost: amount(costState.totalCostUSD),
21059
+ pricingSource: "claude-transcript"
21060
+ } : {}
21061
+ };
21062
+ }
21063
+ const totals = /* @__PURE__ */ new Map();
21064
+ for (const request of requests.values()) {
21065
+ const total = totals.get(request.model) ?? {
21066
+ model: request.model,
21067
+ pricingSource: "claude-transcript"
21068
+ };
21069
+ for (const field of tokenFields)
21070
+ total[field] = (total[field] ?? 0) + (request[field] ?? 0);
21071
+ totals.set(request.model, total);
21072
+ }
21073
+ return {
21074
+ model: model2,
21075
+ effort,
21076
+ ...totals.size ? { usage: [...totals.values()] } : {}
21077
+ };
21078
+ }
21079
+ async function readClaudeTranscript(path) {
21080
+ const stream = createReadStream4(path, { encoding: "utf8" });
21081
+ const lines = createInterface({ input: stream, crlfDelay: Infinity });
21082
+ const records = [];
21083
+ try {
21084
+ for await (const line of lines) {
21085
+ try {
21086
+ const record = JSON.parse(line);
21087
+ if (["user", "assistant", "system", "result", "cost-state"].includes(
21088
+ record.type
21089
+ ))
21090
+ records.push(record);
21091
+ } catch {
21092
+ }
21093
+ }
21094
+ return records;
21095
+ } finally {
21096
+ lines.close();
21097
+ stream.destroy();
21098
+ }
21099
+ }
21100
+ function claudeImportedHistory(messageValues, transcriptValues = []) {
21101
+ const metadata = /* @__PURE__ */ new Map();
21102
+ for (const value of transcriptValues) {
21103
+ const record = asRecord(value);
21104
+ if (typeof record.uuid === "string") metadata.set(record.uuid, record);
21105
+ }
21106
+ const messages = /* @__PURE__ */ new Map();
21107
+ for (const [index, value] of messageValues.entries()) {
21108
+ const message = asRecord(value);
21109
+ const id2 = String(message.uuid ?? index);
21110
+ messages.set(id2, { ...message, ...metadata.get(id2) });
21111
+ }
21112
+ const events = [];
21113
+ const replies = /* @__PURE__ */ new Map();
21114
+ let prompt;
21115
+ let finalReplyId;
21116
+ let kind = "SESSION";
21117
+ for (const [id2, record] of messages) {
21118
+ const message = asRecord(record.message);
21119
+ if (record.permissionMode === "plan") kind = "PLAN";
21120
+ const content = typeof message.content === "string" ? [{ type: "text", text: message.content }] : Array.isArray(message.content) ? message.content : [];
21121
+ if (record.type === "user" && record.isMeta !== true && !prompt)
21122
+ prompt = textBlocks(content) || void 0;
21123
+ if (record.type === "assistant") {
21124
+ const reply = textBlocks(content);
21125
+ if (reply) {
21126
+ const replyId = String(message.id ?? id2);
21127
+ const parts = replies.get(replyId) ?? /* @__PURE__ */ new Map();
21128
+ parts.set(id2, reply);
21129
+ replies.set(replyId, parts);
21130
+ finalReplyId = replyId;
21131
+ }
21132
+ }
21133
+ const blocks = content.length ? content.map(asRecord) : [void 0];
21134
+ for (const [index, block] of blocks.entries()) {
21135
+ const detail = block?.type === "text" && typeof block.text === "string" ? block.text : void 0;
21136
+ const summary = detail || (block?.type === "tool_use" ? String(block.name ?? "Tool call") : block?.type === "tool_result" ? textBlocks(block.content) || "Tool result" : String(
21137
+ block?.type ?? record.subtype ?? record.type ?? "Claude message"
21138
+ ));
21139
+ events.push({
21140
+ id: `${id2}:${String(block?.id ?? index)}`,
21141
+ sequence: events.length,
21142
+ type: String(record.type ?? "message").toUpperCase(),
21143
+ summary: (summary.trim() || "Claude message").slice(0, 2e3),
21144
+ detailMarkdown: detail,
21145
+ createdAt: timestamp(record.timestamp),
21146
+ raw: {
21147
+ ...record,
21148
+ ...block ? { message: { ...message, content: [block] } } : {}
21149
+ }
21150
+ });
21151
+ }
21152
+ }
21153
+ return {
21154
+ ...claudeTranscriptMetadata(
21155
+ transcriptValues.length ? transcriptValues : [...messages.values()]
21156
+ ),
21157
+ kind,
21158
+ prompt,
21159
+ events,
21160
+ finalOutput: finalReplyId ? [...replies.get(finalReplyId).values()].join("\n\n") : void 0
21161
+ };
21162
+ }
20873
21163
 
20874
21164
  // ../../node_modules/@anthropic-ai/claude-agent-sdk/sdk.mjs
20875
21165
  import { createRequire as nV } from "node:module";
@@ -46806,56 +47096,6 @@ async function Lze(e, t, n) {
46806
47096
  return wD(o, { limit: n.limit, offset: n.offset, includeSystemMessages: n.includeSystemMessages });
46807
47097
  }
46808
47098
 
46809
- // src/runs/provider.ts
46810
- function asRecord(value) {
46811
- return value && typeof value === "object" && !Array.isArray(value) ? value : {};
46812
- }
46813
- function firstString(value) {
46814
- const keys = [
46815
- "text",
46816
- "result",
46817
- "message",
46818
- "summary",
46819
- "content",
46820
- "output",
46821
- "items",
46822
- "parts"
46823
- ];
46824
- const pending = [value];
46825
- const visited = /* @__PURE__ */ new WeakSet();
46826
- while (pending.length) {
46827
- const candidate = pending.pop();
46828
- if (typeof candidate === "string" && candidate.trim()) return candidate;
46829
- if (!candidate || typeof candidate !== "object") continue;
46830
- if (visited.has(candidate)) continue;
46831
- visited.add(candidate);
46832
- if (Array.isArray(candidate)) {
46833
- for (let index = candidate.length - 1; index >= 0; index -= 1) {
46834
- pending.push(candidate[index]);
46835
- }
46836
- continue;
46837
- }
46838
- const record = asRecord(candidate);
46839
- for (let index = keys.length - 1; index >= 0; index -= 1) {
46840
- pending.push(record[keys[index]]);
46841
- }
46842
- }
46843
- return void 0;
46844
- }
46845
- function answerArrays(value) {
46846
- if (Array.isArray(value)) {
46847
- return value.map(
46848
- (entry) => Array.isArray(entry) ? entry.map(String) : typeof entry === "object" && entry ? Array.isArray(entry.answers) ? entry.answers.map(
46849
- String
46850
- ) : [String(entry.answer ?? "")] : [String(entry)]
46851
- );
46852
- }
46853
- const record = asRecord(value);
46854
- return Object.values(record).map(
46855
- (entry) => Array.isArray(asRecord(entry).answers) ? asRecord(entry).answers.map(String) : Array.isArray(entry) ? entry.map(String) : [String(entry ?? "")]
46856
- );
46857
- }
46858
-
46859
47099
  // src/runs/claude-adapter.ts
46860
47100
  function findClaudeCodeExecutable(options = {}) {
46861
47101
  return findExecutable("claude", {
@@ -46895,7 +47135,7 @@ var MessageQueue = class {
46895
47135
  if (value) return Promise.resolve({ value, done: false });
46896
47136
  if (this.closed)
46897
47137
  return Promise.resolve({ value: void 0, done: true });
46898
- return new Promise((resolve10) => this.waiters.push(resolve10));
47138
+ return new Promise((resolve11) => this.waiters.push(resolve11));
46899
47139
  }
46900
47140
  };
46901
47141
  }
@@ -47065,8 +47305,8 @@ var ClaudeAdapter = class {
47065
47305
  const requestId = options.toolUseID || randomUUID6();
47066
47306
  const questions = questionsFromInput(toolInput);
47067
47307
  await callbacks.onQuestion(requestId, questions);
47068
- const answers = await new Promise((resolve10) => {
47069
- pendingQuestions.set(requestId, { input: toolInput, resolve: resolve10 });
47308
+ const answers = await new Promise((resolve11) => {
47309
+ pendingQuestions.set(requestId, { input: toolInput, resolve: resolve11 });
47070
47310
  });
47071
47311
  return {
47072
47312
  behavior: "allow",
@@ -47160,39 +47400,51 @@ var ClaudeAdapter = class {
47160
47400
  async discover(worktrees) {
47161
47401
  const results = [];
47162
47402
  for (const worktree of worktrees) {
47163
- const sessions = await AEt({ dir: worktree.folder, limit: 500 });
47403
+ const sessions = await AEt({
47404
+ dir: worktree.folder,
47405
+ includeWorktrees: false
47406
+ });
47164
47407
  for (const session of sessions) {
47165
- const lastModifiedKey = String(session.lastModified ?? "");
47408
+ const lastModifiedKey = `${session.lastModified ?? ""}:${session.fileSize ?? ""}`;
47166
47409
  const cached = this.hydrationCache.get(session.sessionId);
47167
- let kind = "SESSION";
47168
- let finalOutput;
47410
+ let history = {};
47169
47411
  if (cached && cached.lastModified === lastModifiedKey) {
47170
- kind = cached.kind;
47171
- finalOutput = cached.finalOutput;
47412
+ history = cached.history;
47172
47413
  } else {
47414
+ let transcript = [];
47173
47415
  try {
47174
- const history = await xEt(session.sessionId, {
47416
+ const path = await findClaudeSessionFile(
47417
+ session.sessionId,
47418
+ homedir11(),
47419
+ process.env.CLAUDE_CONFIG_DIR
47420
+ );
47421
+ if (path) transcript = await readClaudeTranscript(path);
47422
+ history = claudeTranscriptMetadata(transcript);
47423
+ } catch {
47424
+ }
47425
+ try {
47426
+ const messages = await xEt(session.sessionId, {
47175
47427
  dir: worktree.folder,
47176
- limit: 500
47177
- });
47178
- const serialized2 = JSON.stringify(history);
47179
- if (serialized2.includes('"permissionMode":"plan"')) kind = "PLAN";
47180
- finalOutput = firstString([...history].reverse());
47181
- this.hydrationCache.set(session.sessionId, {
47182
- lastModified: lastModifiedKey,
47183
- kind,
47184
- finalOutput
47428
+ includeSystemMessages: true
47185
47429
  });
47430
+ if (messages.length) {
47431
+ history = claudeImportedHistory(messages, transcript);
47432
+ if (history.model && history.usage) {
47433
+ this.hydrationCache.set(session.sessionId, {
47434
+ lastModified: lastModifiedKey,
47435
+ history
47436
+ });
47437
+ }
47438
+ }
47186
47439
  } catch {
47187
47440
  }
47188
47441
  }
47189
47442
  results.push({
47190
47443
  nativeId: session.sessionId,
47191
47444
  worktreeId: worktree.id,
47192
- kind,
47445
+ ...history,
47193
47446
  status: "COMPLETED",
47194
- prompt: session.firstPrompt || session.summary,
47195
- finalOutput: finalOutput || session.summary,
47447
+ prompt: history.prompt || session.firstPrompt || session.summary,
47196
47448
  branch: session.gitBranch || worktree.branch || void 0,
47197
47449
  createdAt: session.createdAt ? new Date(session.createdAt).toISOString() : void 0,
47198
47450
  updatedAt: new Date(session.lastModified).toISOString(),
@@ -47210,10 +47462,192 @@ import {
47210
47462
  spawn as spawn8
47211
47463
  } from "node:child_process";
47212
47464
  import { mkdtemp as mkdtemp8, rm as rm14, writeFile as writeFile10 } from "node:fs/promises";
47213
- import { tmpdir as tmpdir8 } from "node:os";
47465
+ import { homedir as homedir12, tmpdir as tmpdir8 } from "node:os";
47214
47466
  import { join as join19 } from "node:path";
47215
- import { createInterface } from "node:readline";
47467
+ import { createInterface as createInterface3 } from "node:readline";
47216
47468
  import { promisify as promisify3 } from "node:util";
47469
+
47470
+ // src/runs/codex-import.ts
47471
+ import { createReadStream as createReadStream5 } from "node:fs";
47472
+ import { createInterface as createInterface2 } from "node:readline";
47473
+ var tokenFields2 = [
47474
+ "inputTokens",
47475
+ "outputTokens",
47476
+ "reasoningTokens",
47477
+ "cacheReadTokens",
47478
+ "cacheWriteTokens"
47479
+ ];
47480
+ function count2(value) {
47481
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 ? Math.floor(value) : 0;
47482
+ }
47483
+ function tokens(value, model2) {
47484
+ const raw = asRecord(value);
47485
+ const cached = count2(raw.cached_input_tokens);
47486
+ return {
47487
+ model: model2,
47488
+ inputTokens: Math.max(0, count2(raw.input_tokens) - cached),
47489
+ cacheReadTokens: cached,
47490
+ cacheWriteTokens: count2(raw.cache_write_input_tokens),
47491
+ outputTokens: count2(raw.output_tokens),
47492
+ reasoningTokens: count2(raw.reasoning_output_tokens),
47493
+ pricingSource: "codex-transcript"
47494
+ };
47495
+ }
47496
+ function codexTranscriptMetadata(records) {
47497
+ let model2;
47498
+ let effort;
47499
+ const turnModels = /* @__PURE__ */ new Map();
47500
+ const responseTurns = /* @__PURE__ */ new Set();
47501
+ let contextTurn = "unknown-turn";
47502
+ for (const record of records) {
47503
+ const context = asRecord(record.payload);
47504
+ if (record.type === "turn_context") {
47505
+ contextTurn = String(context.turn_id ?? "unknown-turn");
47506
+ if (typeof context.model === "string") model2 = context.model;
47507
+ if (typeof context.effort === "string") effort = context.effort;
47508
+ if (model2) turnModels.set(contextTurn, model2);
47509
+ }
47510
+ if (record.type === "token_usage_record" && context.usage)
47511
+ responseTurns.add(String(context.turn_id ?? contextTurn));
47512
+ }
47513
+ const totals = /* @__PURE__ */ new Map();
47514
+ const seenResponses = /* @__PURE__ */ new Set();
47515
+ let currentModel;
47516
+ let currentTurn = "unknown-turn";
47517
+ let previous = { model: "unknown" };
47518
+ const add = (usage2) => {
47519
+ const total = totals.get(usage2.model) ?? {
47520
+ model: usage2.model,
47521
+ pricingSource: usage2.pricingSource
47522
+ };
47523
+ for (const field of tokenFields2)
47524
+ total[field] = (total[field] ?? 0) + (usage2[field] ?? 0);
47525
+ totals.set(usage2.model, total);
47526
+ };
47527
+ for (const record of records) {
47528
+ const payload = asRecord(record.payload);
47529
+ if (record.type === "turn_context") {
47530
+ currentTurn = String(payload.turn_id ?? "unknown-turn");
47531
+ if (typeof payload.model === "string") currentModel = payload.model;
47532
+ }
47533
+ if (record.type === "token_usage_record") {
47534
+ const responseId = typeof payload.response_id === "string" ? payload.response_id : void 0;
47535
+ if (responseId && seenResponses.has(responseId)) continue;
47536
+ if (responseId) seenResponses.add(responseId);
47537
+ const responseModel = typeof payload.model === "string" ? payload.model : turnModels.get(String(payload.turn_id)) ?? currentModel;
47538
+ if (responseModel && payload.usage)
47539
+ add(tokens(payload.usage, responseModel));
47540
+ } else if (record.type === "event_msg" && payload.type === "token_count") {
47541
+ const raw = asRecord(payload.info).total_token_usage;
47542
+ if (!raw) continue;
47543
+ const cumulative = tokens(raw, currentModel ?? "unknown");
47544
+ const delta = {
47545
+ model: cumulative.model,
47546
+ pricingSource: "codex-transcript"
47547
+ };
47548
+ for (const field of tokenFields2)
47549
+ delta[field] = Math.max(
47550
+ 0,
47551
+ (cumulative[field] ?? 0) - (previous[field] ?? 0)
47552
+ );
47553
+ previous = cumulative;
47554
+ if (!responseTurns.has(currentTurn)) add(delta);
47555
+ }
47556
+ }
47557
+ return {
47558
+ model: model2,
47559
+ effort,
47560
+ ...totals.size ? { usage: [...totals.values()] } : {}
47561
+ };
47562
+ }
47563
+ async function readCodexTranscriptMetadata(path) {
47564
+ const records = [];
47565
+ const stream = createReadStream5(path, { encoding: "utf8" });
47566
+ const lines = createInterface2({ input: stream, crlfDelay: Infinity });
47567
+ try {
47568
+ for await (const line of lines) {
47569
+ try {
47570
+ const record = JSON.parse(line);
47571
+ if (record.type === "turn_context" || record.type === "token_usage_record" || record.type === "event_msg" && asRecord(record.payload).type === "token_count")
47572
+ records.push(record);
47573
+ } catch {
47574
+ }
47575
+ }
47576
+ return codexTranscriptMetadata(records);
47577
+ } finally {
47578
+ lines.close();
47579
+ stream.destroy();
47580
+ }
47581
+ }
47582
+ function timestamp2(value) {
47583
+ const date = typeof value === "number" ? new Date(value < 1e12 ? value * 1e3 : value) : typeof value === "string" ? new Date(value) : void 0;
47584
+ return date && Number.isFinite(date.getTime()) ? date.toISOString() : void 0;
47585
+ }
47586
+ function codexImportedHistory(threadValue) {
47587
+ const thread = asRecord(threadValue);
47588
+ const turns = Array.isArray(thread.turns) ? thread.turns : [];
47589
+ const events = [];
47590
+ let finalOutput;
47591
+ let kind = "SESSION";
47592
+ let status2;
47593
+ for (const value of turns) {
47594
+ const turn = asRecord(value);
47595
+ for (const itemValue of Array.isArray(turn.items) ? turn.items : []) {
47596
+ const item = asRecord(itemValue);
47597
+ if (item.type === "plan") kind = "PLAN";
47598
+ if (item.type === "agentMessage" && (item.phase === "final_answer" || item.phase == null) || item.type === "plan") {
47599
+ if (typeof item.text === "string") finalOutput = item.text;
47600
+ }
47601
+ const detail = typeof item.text === "string" ? item.text : typeof item.command === "string" ? item.command : void 0;
47602
+ const inProgress = [
47603
+ "inProgress",
47604
+ "in_progress",
47605
+ "running",
47606
+ "pending"
47607
+ ].includes(String(item.status));
47608
+ events.push({
47609
+ id: `${String(turn.id ?? turns.indexOf(value))}:${String(item.id ?? events.length)}`,
47610
+ sequence: events.length,
47611
+ type: inProgress ? "ITEM_STARTED" : "ITEM_COMPLETED",
47612
+ summary: (detail || String(item.type ?? "Codex item")).slice(0, 2e3),
47613
+ detailMarkdown: detail,
47614
+ createdAt: timestamp2(
47615
+ item.createdAt ?? turn.startedAt ?? thread.createdAt
47616
+ ),
47617
+ raw: {
47618
+ method: inProgress ? "item/started" : "item/completed",
47619
+ params: { threadId: thread.id, turnId: turn.id, item }
47620
+ }
47621
+ });
47622
+ }
47623
+ if (typeof turn.status === "string") {
47624
+ const inProgress = turn.status === "inProgress";
47625
+ status2 = turn.status === "failed" ? "FAILED" : turn.status === "interrupted" ? "CANCELLED" : void 0;
47626
+ const error = asRecord(turn.error);
47627
+ const detail = typeof error.message === "string" ? error.message : typeof turn.error === "string" ? turn.error : void 0;
47628
+ events.push({
47629
+ id: `${String(turn.id ?? turns.indexOf(value))}:status`,
47630
+ sequence: events.length,
47631
+ type: inProgress ? "TURN_STARTED" : "TURN_COMPLETED",
47632
+ summary: detail || `Turn ${turn.status}`,
47633
+ detailMarkdown: detail,
47634
+ createdAt: timestamp2(
47635
+ turn.completedAt ?? turn.startedAt ?? thread.updatedAt ?? thread.createdAt
47636
+ ),
47637
+ raw: {
47638
+ method: inProgress ? "turn/started" : "turn/completed",
47639
+ params: {
47640
+ threadId: thread.id,
47641
+ turn: { ...turn, items: void 0, error: detail ?? turn.error }
47642
+ }
47643
+ }
47644
+ });
47645
+ }
47646
+ }
47647
+ return { events, finalOutput, kind, status: status2 };
47648
+ }
47649
+
47650
+ // src/runs/codex-adapter.ts
47217
47651
  var execFileAsync2 = promisify3(execFile3);
47218
47652
  function codexAppServerArgs(modelCatalogPath) {
47219
47653
  return [
@@ -47309,10 +47743,10 @@ var CodexAppServer = class {
47309
47743
  const text2 = String(chunk).trim();
47310
47744
  if (text2) console.error(`codex app-server: ${text2}`);
47311
47745
  });
47312
- const lines = createInterface({ input: child.stdout });
47746
+ const lines = createInterface3({ input: child.stdout });
47313
47747
  lines.on("line", (line) => this.receive(line));
47314
- await new Promise((resolve10, reject) => {
47315
- child.once("spawn", resolve10);
47748
+ await new Promise((resolve11, reject) => {
47749
+ child.once("spawn", resolve11);
47316
47750
  child.once("error", reject);
47317
47751
  });
47318
47752
  await this.requestDirect("initialize", {
@@ -47410,8 +47844,8 @@ var CodexAppServer = class {
47410
47844
  }
47411
47845
  requestDirect(method, params) {
47412
47846
  const id2 = this.nextId++;
47413
- return new Promise((resolve10, reject) => {
47414
- this.pending.set(id2, { resolve: resolve10, reject });
47847
+ return new Promise((resolve11, reject) => {
47848
+ this.pending.set(id2, { resolve: resolve11, reject });
47415
47849
  this.write({ id: id2, method, params });
47416
47850
  });
47417
47851
  }
@@ -47574,8 +48008,8 @@ var CodexAdapter = class {
47574
48008
  let lastUsageSignature = "";
47575
48009
  const pendingQuestions = /* @__PURE__ */ new Map();
47576
48010
  let resolveCompletion;
47577
- const completion = new Promise((resolve10) => {
47578
- resolveCompletion = resolve10;
48011
+ const completion = new Promise((resolve11) => {
48012
+ resolveCompletion = resolve11;
47579
48013
  });
47580
48014
  const unsubscribe = this.server.listen(nativeId, (message) => {
47581
48015
  void (async () => {
@@ -47720,7 +48154,8 @@ var CodexAdapter = class {
47720
48154
  archived,
47721
48155
  cursor,
47722
48156
  limit: 100,
47723
- sortDirection: "asc"
48157
+ sortDirection: "asc",
48158
+ sourceKinds: ["cli", "vscode", "exec", "appServer"]
47724
48159
  })
47725
48160
  );
47726
48161
  const threads = Array.isArray(page.data) ? page.data : [];
@@ -47728,12 +48163,13 @@ var CodexAdapter = class {
47728
48163
  const thread = asRecord(value);
47729
48164
  const updatedAtKey = String(thread.updatedAt ?? "");
47730
48165
  const cached = this.hydrationCache.get(String(thread.id));
47731
- let finalOutput;
47732
- let kind = "SESSION";
47733
- if (cached && cached.updatedAt === updatedAtKey) {
47734
- kind = cached.kind;
47735
- finalOutput = cached.finalOutput;
48166
+ let history = {};
48167
+ const active = asRecord(thread.status).type === "active";
48168
+ if (active) this.hydrationCache.delete(String(thread.id));
48169
+ if (cached && cached.updatedAt === updatedAtKey && !active) {
48170
+ history = cached.history;
47736
48171
  } else {
48172
+ let hydratedSuccessfully = false;
47737
48173
  try {
47738
48174
  const detail = asRecord(
47739
48175
  await this.server.request("thread/read", {
@@ -47742,31 +48178,41 @@ var CodexAdapter = class {
47742
48178
  })
47743
48179
  );
47744
48180
  const hydrated = asRecord(detail.thread);
47745
- const serialized2 = JSON.stringify(hydrated.turns ?? []);
47746
- if (serialized2.includes('"type":"plan"')) kind = "PLAN";
47747
- finalOutput = firstString(
47748
- Array.isArray(hydrated.turns) ? [...hydrated.turns].reverse() : []
48181
+ if (Array.isArray(hydrated.turns)) {
48182
+ history = codexImportedHistory(hydrated);
48183
+ hydratedSuccessfully = true;
48184
+ }
48185
+ } catch {
48186
+ }
48187
+ try {
48188
+ const path = typeof thread.path === "string" ? thread.path : await findCodexSessionFile(
48189
+ String(thread.id),
48190
+ homedir12(),
48191
+ process.env.CODEX_HOME
47749
48192
  );
48193
+ if (path)
48194
+ Object.assign(
48195
+ history,
48196
+ await readCodexTranscriptMetadata(path)
48197
+ );
48198
+ } catch {
48199
+ }
48200
+ if (!active && hydratedSuccessfully && history.model && history.usage) {
47750
48201
  this.hydrationCache.set(String(thread.id), {
47751
48202
  updatedAt: updatedAtKey,
47752
- kind,
47753
- finalOutput
48203
+ history
47754
48204
  });
47755
- } catch {
47756
48205
  }
47757
48206
  }
47758
48207
  results.push({
47759
48208
  nativeId: String(thread.id),
47760
48209
  worktreeId: worktree.id,
47761
- kind,
47762
- status: String(
47763
- asRecord(thread.status).type ?? thread.status ?? "COMPLETED"
47764
- ).toUpperCase(),
48210
+ ...history,
48211
+ status: active ? "IN_PROGRESS" : asRecord(thread.status).type === "systemError" ? "FAILED" : history.status ?? "COMPLETED",
47765
48212
  archived,
47766
48213
  prompt: String(
47767
48214
  thread.preview ?? thread.name ?? "Imported Codex thread"
47768
48215
  ),
47769
- finalOutput,
47770
48216
  branch: firstString(asRecord(thread.gitInfo).branch) || worktree.branch || void 0,
47771
48217
  createdAt: thread.createdAt ? new Date(Number(thread.createdAt) * 1e3).toISOString() : void 0,
47772
48218
  updatedAt: thread.updatedAt ? new Date(Number(thread.updatedAt) * 1e3).toISOString() : void 0,
@@ -47785,13 +48231,13 @@ var CodexAdapter = class {
47785
48231
  };
47786
48232
 
47787
48233
  // src/runs/opencode-adapter.ts
47788
- import { dirname as dirname12 } from "node:path";
48234
+ import { dirname as dirname12, resolve as resolve10 } from "node:path";
47789
48235
  import { pathToFileURL } from "node:url";
47790
48236
 
47791
48237
  // ../../node_modules/@opencode-ai/sdk/dist/v2/gen/core/serverSentEvents.gen.js
47792
48238
  var createSseClient = ({ onRequest, onSseError, onSseEvent, responseTransformer, responseValidator, sseDefaultRetryDelay, sseMaxRetryAttempts, sseMaxRetryDelay, sseSleepFn, url, ...options }) => {
47793
48239
  let lastEventId;
47794
- const sleep = sseSleepFn ?? ((ms) => new Promise((resolve10) => setTimeout(resolve10, ms)));
48240
+ const sleep = sseSleepFn ?? ((ms) => new Promise((resolve11) => setTimeout(resolve11, ms)));
47795
48241
  const createStream = async function* () {
47796
48242
  let retryDelay = sseDefaultRetryDelay ?? 3e3;
47797
48243
  let attempt = 0;
@@ -53243,7 +53689,7 @@ async function createOpencodeServer(options) {
53243
53689
  });
53244
53690
  let clear = () => {
53245
53691
  };
53246
- const url = await new Promise((resolve10, reject) => {
53692
+ const url = await new Promise((resolve11, reject) => {
53247
53693
  const id2 = setTimeout(() => {
53248
53694
  clear();
53249
53695
  stop(proc);
@@ -53268,7 +53714,7 @@ async function createOpencodeServer(options) {
53268
53714
  }
53269
53715
  clearTimeout(id2);
53270
53716
  resolved = true;
53271
- resolve10(match[1]);
53717
+ resolve11(match[1]);
53272
53718
  return;
53273
53719
  }
53274
53720
  }
@@ -53317,10 +53763,231 @@ async function createOpencode(options) {
53317
53763
  };
53318
53764
  }
53319
53765
 
53766
+ // src/runs/opencode-import.ts
53767
+ function opencodeModel(value) {
53768
+ const model2 = asRecord(value);
53769
+ const id2 = model2.modelID ?? model2.id;
53770
+ return typeof model2.providerID === "string" && typeof id2 === "string" ? `${model2.providerID}/${id2}` : void 0;
53771
+ }
53772
+ function opencodeTimestamp(value) {
53773
+ const date = typeof value === "number" ? new Date(value) : void 0;
53774
+ return date && Number.isFinite(date.getTime()) ? date.toISOString() : void 0;
53775
+ }
53776
+ function amount2(value) {
53777
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
53778
+ }
53779
+ function tokenUsage(value, model2) {
53780
+ const tokens2 = asRecord(value);
53781
+ const cache = asRecord(tokens2.cache);
53782
+ const count3 = (value2) => Math.floor(amount2(value2) ?? 0);
53783
+ return {
53784
+ model: model2,
53785
+ inputTokens: count3(tokens2.input),
53786
+ outputTokens: count3(tokens2.output),
53787
+ reasoningTokens: count3(tokens2.reasoning),
53788
+ cacheReadTokens: count3(cache.read),
53789
+ cacheWriteTokens: count3(cache.write),
53790
+ pricingSource: "opencode-history"
53791
+ };
53792
+ }
53793
+ var tokenFields3 = [
53794
+ "inputTokens",
53795
+ "outputTokens",
53796
+ "reasoningTokens",
53797
+ "cacheReadTokens",
53798
+ "cacheWriteTokens"
53799
+ ];
53800
+ function textParts(parts) {
53801
+ return parts.filter((part) => part.type === "text" && typeof part.text === "string").map((part) => String(part.text).trim()).filter(Boolean).join("\n\n");
53802
+ }
53803
+ function userPrompt(parts) {
53804
+ const originalParts = parts.filter((part) => part.synthetic !== true);
53805
+ return textParts(originalParts) || originalParts.filter(
53806
+ (part) => part.type === "subtask" && typeof part.prompt === "string"
53807
+ ).map((part) => String(part.prompt).trim()).filter(Boolean).join("\n\n");
53808
+ }
53809
+ function nativePart(value) {
53810
+ const part = asRecord(value);
53811
+ if (part.type !== "tool") return part;
53812
+ const state = asRecord(part.state);
53813
+ const time = asRecord(part.time);
53814
+ const content = Array.isArray(state.content) ? state.content.map(asRecord) : [];
53815
+ return {
53816
+ ...part,
53817
+ tool: part.name,
53818
+ state: {
53819
+ ...state,
53820
+ output: textParts(content),
53821
+ metadata: state.structured,
53822
+ time: { start: time.ran ?? time.created, end: time.completed }
53823
+ }
53824
+ };
53825
+ }
53826
+ function opencodeImportedHistory(sessionValue, messageValues) {
53827
+ const session = asRecord(sessionValue);
53828
+ const messages = /* @__PURE__ */ new Map();
53829
+ for (const [index, value] of messageValues.entries()) {
53830
+ const message = asRecord(value);
53831
+ const info = asRecord(message.info ?? message);
53832
+ messages.set(String(info.id ?? index), message);
53833
+ }
53834
+ const ordered = [...messages.entries()].sort(([, left], [, right]) => {
53835
+ const leftInfo = asRecord(left.info ?? left);
53836
+ const rightInfo = asRecord(right.info ?? right);
53837
+ return Number(asRecord(leftInfo.time).created ?? 0) - Number(asRecord(rightInfo.time).created ?? 0);
53838
+ });
53839
+ const events = [];
53840
+ const totals = /* @__PURE__ */ new Map();
53841
+ const unpricedModels = /* @__PURE__ */ new Set();
53842
+ let model2;
53843
+ let effort;
53844
+ let prompt;
53845
+ let finalOutput;
53846
+ let status2;
53847
+ let requestedModel;
53848
+ let requestedVariant;
53849
+ let agent;
53850
+ const addUsage = (usage2) => {
53851
+ const total = totals.get(usage2.model) ?? {
53852
+ model: usage2.model,
53853
+ pricingSource: "opencode-history"
53854
+ };
53855
+ for (const field of tokenFields3)
53856
+ total[field] = (total[field] ?? 0) + (usage2[field] ?? 0);
53857
+ if (usage2.estimatedCost === void 0) unpricedModels.add(usage2.model);
53858
+ else total.estimatedCost = (total.estimatedCost ?? 0) + usage2.estimatedCost;
53859
+ totals.set(usage2.model, total);
53860
+ };
53861
+ const emit = (id2, type, summary, properties, createdAt, detailMarkdown) => {
53862
+ events.push({
53863
+ id: id2,
53864
+ sequence: events.length,
53865
+ type: type.toUpperCase().replaceAll(".", "_"),
53866
+ summary: (summary.trim() || type).slice(0, 2e3),
53867
+ detailMarkdown,
53868
+ createdAt,
53869
+ raw: { type, properties }
53870
+ });
53871
+ };
53872
+ for (const [id2, message] of ordered) {
53873
+ const legacy = !!message.info;
53874
+ const original = asRecord(message.info ?? message);
53875
+ const role = original.role ?? original.type;
53876
+ const time = asRecord(original.time);
53877
+ const createdAt = opencodeTimestamp(
53878
+ time.created ?? asRecord(session.time).created
53879
+ );
53880
+ const parts = legacy ? Array.isArray(message.parts) ? message.parts.map(asRecord) : [] : Array.isArray(original.content) ? original.content.map(nativePart) : [];
53881
+ if (!legacy && ["user", "synthetic", "system", "compaction"].includes(String(role)))
53882
+ parts.push({
53883
+ id: "text",
53884
+ type: "text",
53885
+ text: role === "user" ? original.text : original.text ?? original.summary
53886
+ });
53887
+ if (!legacy && role === "shell")
53888
+ parts.push({
53889
+ id: original.callID ?? "shell",
53890
+ type: "tool",
53891
+ tool: "bash",
53892
+ state: {
53893
+ input: { command: original.command },
53894
+ output: original.output,
53895
+ status: time.completed ? "completed" : "running",
53896
+ time: { start: time.created, end: time.completed }
53897
+ }
53898
+ });
53899
+ const info = {
53900
+ ...original,
53901
+ id: id2,
53902
+ sessionID: session.id,
53903
+ role,
53904
+ modelID: original.modelID ?? asRecord(original.model).id
53905
+ };
53906
+ delete info.content;
53907
+ emit(
53908
+ `message:${id2}`,
53909
+ "message.updated",
53910
+ `${String(role ?? "Message")} message`,
53911
+ { info },
53912
+ createdAt
53913
+ );
53914
+ for (const [index, part] of parts.entries()) {
53915
+ const state = asRecord(part.state);
53916
+ const detail = typeof part.text === "string" ? part.text : void 0;
53917
+ const summary = detail ?? (part.type === "tool" ? String(part.tool ?? "Tool") : String(part.type ?? "Message part"));
53918
+ emit(
53919
+ `${id2}:part:${String(part.id ?? index)}`,
53920
+ "message.part.updated",
53921
+ summary,
53922
+ { part: { ...part, sessionID: session.id, messageID: id2 } },
53923
+ opencodeTimestamp(
53924
+ asRecord(state.time).start ?? asRecord(part.time).created
53925
+ ) ?? createdAt,
53926
+ detail
53927
+ );
53928
+ }
53929
+ if (role === "user" || role === "model-switched") {
53930
+ requestedModel = opencodeModel(original.model);
53931
+ requestedVariant = typeof asRecord(original.model).variant === "string" ? String(asRecord(original.model).variant) : void 0;
53932
+ if (role === "user" && !prompt) prompt = userPrompt(parts) || void 0;
53933
+ }
53934
+ if (role !== "assistant") continue;
53935
+ const actualModel = opencodeModel(original) ?? opencodeModel(original.model);
53936
+ if (actualModel) {
53937
+ model2 = actualModel;
53938
+ effort = typeof original.variant === "string" ? original.variant : typeof asRecord(original.model).variant === "string" ? String(asRecord(original.model).variant) : actualModel === requestedModel ? requestedVariant ?? "auto" : "auto";
53939
+ }
53940
+ agent = typeof original.agent === "string" ? original.agent : agent;
53941
+ const error = asRecord(original.error);
53942
+ status2 = original.error ? error.name === "MessageAbortedError" ? "CANCELLED" : "FAILED" : "COMPLETED";
53943
+ const responseText = textParts(parts);
53944
+ if (responseText && original.summary !== true && !["tool-calls", "tool_calls"].includes(String(original.finish)))
53945
+ finalOutput = responseText;
53946
+ const steps = parts.filter((part) => part.type === "step-finish");
53947
+ if (original.tokens || steps.length || amount2(original.cost) !== void 0) {
53948
+ const usage2 = tokenUsage(original.tokens, actualModel ?? "unknown");
53949
+ if (!original.tokens)
53950
+ for (const step of steps) {
53951
+ const stepUsage = tokenUsage(step.tokens, usage2.model);
53952
+ for (const field of tokenFields3)
53953
+ usage2[field] = (usage2[field] ?? 0) + (stepUsage[field] ?? 0);
53954
+ }
53955
+ usage2.estimatedCost = amount2(original.cost) ?? (steps.length && steps.every((step) => amount2(step.cost) !== void 0) ? steps.reduce((sum, step) => sum + amount2(step.cost), 0) : void 0);
53956
+ addUsage(usage2);
53957
+ }
53958
+ }
53959
+ if (session.tokens) {
53960
+ const usage2 = tokenUsage(session.tokens, "unknown");
53961
+ for (const field of tokenFields3)
53962
+ usage2[field] = Math.max(
53963
+ 0,
53964
+ (usage2[field] ?? 0) - [...totals.values()].reduce((sum, row) => sum + (row[field] ?? 0), 0)
53965
+ );
53966
+ if (tokenFields3.some((field) => usage2[field])) addUsage(usage2);
53967
+ }
53968
+ for (const model3 of unpricedModels) {
53969
+ const usage2 = totals.get(model3);
53970
+ if (usage2) delete usage2.estimatedCost;
53971
+ }
53972
+ const estimatedCost = amount2(session.cost);
53973
+ return {
53974
+ events,
53975
+ ...totals.size || session.tokens ? { usage: [...totals.values()] } : {},
53976
+ model: model2 ?? requestedModel ?? opencodeModel(session.model),
53977
+ effort: effort ?? requestedVariant ?? (typeof asRecord(session.model).variant === "string" ? String(asRecord(session.model).variant) : void 0),
53978
+ prompt,
53979
+ finalOutput,
53980
+ status: status2,
53981
+ kind: (agent ?? session.agent) === "plan" ? "PLAN" : "SESSION",
53982
+ ...estimatedCost !== void 0 ? { estimatedCost, pricingSource: "opencode-history" } : {}
53983
+ };
53984
+ }
53985
+
53320
53986
  // src/runs/opencode-adapter.ts
53321
53987
  var QUESTION_RECONCILIATION_INTERVAL_MS = 1e3;
53322
53988
  var QUESTION_RECONCILIATION_TIMEOUT_MS = 5e3;
53323
53989
  var POST_ANSWER_COMPLETION_TIMEOUT_MS = 15 * 6e4;
53990
+ var IMPORT_PAGE_SIZE = 200;
53324
53991
  function resultData(value) {
53325
53992
  const record = asRecord(value);
53326
53993
  return "data" in record ? record.data : value;
@@ -53516,11 +54183,11 @@ function responseItems(value) {
53516
54183
  }
53517
54184
  function waitForPoll(signal) {
53518
54185
  if (signal.aborted) return Promise.resolve();
53519
- return new Promise((resolve10) => {
54186
+ return new Promise((resolve11) => {
53520
54187
  const finish = () => {
53521
54188
  clearTimeout(timer);
53522
54189
  signal.removeEventListener("abort", finish);
53523
- resolve10();
54190
+ resolve11();
53524
54191
  };
53525
54192
  const timer = setTimeout(finish, QUESTION_RECONCILIATION_INTERVAL_MS);
53526
54193
  timer.unref();
@@ -53539,6 +54206,7 @@ var OpenCodeAdapter = class {
53539
54206
  nativeDelete: true
53540
54207
  };
53541
54208
  runtime;
54209
+ hydrationCache = /* @__PURE__ */ new Map();
53542
54210
  async client() {
53543
54211
  if (!this.runtime) {
53544
54212
  const executable = findExecutable("opencode", {
@@ -53838,14 +54506,14 @@ var OpenCodeAdapter = class {
53838
54506
  }
53839
54507
  const finalOutput = opencodeResponseText(response);
53840
54508
  const info = asRecord(asRecord(response).info);
53841
- const tokens = asRecord(info.tokens);
54509
+ const tokens2 = asRecord(info.tokens);
53842
54510
  await callbacks.onUsage({
53843
54511
  model: input.run.model,
53844
- inputTokens: Number(tokens.input ?? 0),
53845
- outputTokens: Number(tokens.output ?? 0),
53846
- reasoningTokens: Number(tokens.reasoning ?? 0),
53847
- cacheReadTokens: Number(asRecord(tokens.cache).read ?? 0),
53848
- cacheWriteTokens: Number(asRecord(tokens.cache).write ?? 0),
54512
+ inputTokens: Number(tokens2.input ?? 0),
54513
+ outputTokens: Number(tokens2.output ?? 0),
54514
+ reasoningTokens: Number(tokens2.reasoning ?? 0),
54515
+ cacheReadTokens: Number(asRecord(tokens2.cache).read ?? 0),
54516
+ cacheWriteTokens: Number(asRecord(tokens2.cache).write ?? 0),
53849
54517
  estimatedCost: Number(info.cost ?? 0),
53850
54518
  pricingSource: "opencode-sdk"
53851
54519
  });
@@ -53913,64 +54581,209 @@ var OpenCodeAdapter = class {
53913
54581
  const client2 = await this.client();
53914
54582
  await client2.session.delete({ sessionID: nativeId, directory: cwd });
53915
54583
  }
54584
+ async legacyHistory(client2, nativeId, directory) {
54585
+ const messages = [];
54586
+ const seenCursors = /* @__PURE__ */ new Set();
54587
+ let before;
54588
+ do {
54589
+ const response = await client2.session.messages(
54590
+ {
54591
+ sessionID: nativeId,
54592
+ directory,
54593
+ limit: IMPORT_PAGE_SIZE,
54594
+ ...before ? { before } : {}
54595
+ },
54596
+ { throwOnError: true }
54597
+ );
54598
+ const page = resultData(response);
54599
+ if (!Array.isArray(page))
54600
+ throw new Error("Invalid legacy OpenCode message history");
54601
+ messages.push(...page);
54602
+ before = response.response?.headers.get("x-next-cursor") ?? void 0;
54603
+ if (before && seenCursors.has(before))
54604
+ throw new Error("Repeated legacy OpenCode message cursor");
54605
+ if (before) seenCursors.add(before);
54606
+ } while (before);
54607
+ return messages;
54608
+ }
54609
+ async importedHistory(client2, nativeId, directory, surface) {
54610
+ if (surface === "LEGACY")
54611
+ return this.legacyHistory(client2, nativeId, directory);
54612
+ const messages = [];
54613
+ let cursor;
54614
+ const seenCursors = /* @__PURE__ */ new Set();
54615
+ try {
54616
+ do {
54617
+ const page = asRecord(
54618
+ resultData(
54619
+ await client2.v2.session.messages(
54620
+ {
54621
+ sessionID: nativeId,
54622
+ limit: IMPORT_PAGE_SIZE,
54623
+ ...cursor ? { cursor } : { order: "asc" }
54624
+ },
54625
+ { throwOnError: true }
54626
+ )
54627
+ )
54628
+ );
54629
+ if (!Array.isArray(page.data))
54630
+ throw new Error("Invalid OpenCode message history");
54631
+ messages.push(...page.data);
54632
+ cursor = typeof asRecord(page.cursor).next === "string" ? String(asRecord(page.cursor).next) : void 0;
54633
+ if (cursor && seenCursors.has(cursor))
54634
+ throw new Error("Repeated OpenCode message cursor");
54635
+ if (cursor) seenCursors.add(cursor);
54636
+ } while (cursor);
54637
+ } catch (error) {
54638
+ if (messages.length || cursor) throw error;
54639
+ return this.legacyHistory(client2, nativeId, directory);
54640
+ }
54641
+ if (messages.length) return messages;
54642
+ try {
54643
+ return await this.legacyHistory(client2, nativeId, directory);
54644
+ } catch (error) {
54645
+ const name = String(asRecord(error)._tag ?? asRecord(error).name ?? "");
54646
+ if (["NotFoundError", "SessionNotFoundError"].includes(name))
54647
+ return messages;
54648
+ throw error;
54649
+ }
54650
+ }
53916
54651
  async discover(worktrees) {
53917
54652
  const client2 = await this.client();
53918
54653
  const results = [];
53919
- const activeResponse = asRecord(
53920
- resultData(await client2.v2.session.active())
53921
- );
53922
- const activeIds = new Set(Object.keys(asRecord(activeResponse.data)));
54654
+ const hydrationFailures = [];
54655
+ let hydratedCount = 0;
54656
+ const activeIds = /* @__PURE__ */ new Set();
54657
+ try {
54658
+ const active = asRecord(
54659
+ resultData(await client2.v2.session.active({ throwOnError: true }))
54660
+ );
54661
+ for (const id2 of Object.keys(asRecord(active.data))) activeIds.add(id2);
54662
+ } catch {
54663
+ }
53923
54664
  for (const worktree of worktrees) {
53924
- let cursor;
53925
- do {
53926
- const response = asRecord(
53927
- resultData(
53928
- await client2.v2.session.list({
53929
- directory: worktree.folder,
53930
- limit: 500,
53931
- order: "asc",
53932
- cursor
53933
- })
54665
+ const sessions = /* @__PURE__ */ new Map();
54666
+ let listed = false;
54667
+ let listError;
54668
+ try {
54669
+ let cursor;
54670
+ const seenCursors = /* @__PURE__ */ new Set();
54671
+ do {
54672
+ const response = asRecord(
54673
+ resultData(
54674
+ await client2.v2.session.list(
54675
+ {
54676
+ directory: worktree.folder,
54677
+ limit: IMPORT_PAGE_SIZE,
54678
+ ...cursor ? { cursor } : { order: "asc" }
54679
+ },
54680
+ { throwOnError: true }
54681
+ )
54682
+ )
54683
+ );
54684
+ if (!Array.isArray(response.data))
54685
+ throw new Error("Invalid OpenCode session list");
54686
+ for (const value of response.data) {
54687
+ const session = asRecord(value);
54688
+ if (typeof session.id === "string")
54689
+ sessions.set(session.id, { session, surface: "V2" });
54690
+ }
54691
+ cursor = typeof asRecord(response.cursor).next === "string" ? String(asRecord(response.cursor).next) : void 0;
54692
+ if (cursor && seenCursors.has(cursor))
54693
+ throw new Error("Repeated OpenCode session cursor");
54694
+ if (cursor) seenCursors.add(cursor);
54695
+ } while (cursor);
54696
+ listed = true;
54697
+ } catch (error) {
54698
+ listError = error;
54699
+ }
54700
+ try {
54701
+ const legacy = resultData(
54702
+ await client2.session.list(
54703
+ { directory: worktree.folder, limit: IMPORT_PAGE_SIZE },
54704
+ { throwOnError: true }
53934
54705
  )
53935
54706
  );
53936
- const sessions = Array.isArray(response.data) ? response.data : [];
53937
- for (const value of sessions) {
54707
+ if (!Array.isArray(legacy))
54708
+ throw new Error("Invalid legacy OpenCode session list");
54709
+ for (const value of legacy) {
53938
54710
  const session = asRecord(value);
53939
- const time = asRecord(session.time);
53940
- const selectedModel = asRecord(session.model);
53941
- let finalOutput;
54711
+ if (typeof session.id === "string" && !sessions.has(session.id))
54712
+ sessions.set(session.id, { session, surface: "LEGACY" });
54713
+ }
54714
+ listed = true;
54715
+ } catch (error) {
54716
+ listError ??= error;
54717
+ }
54718
+ if (!listed) throw listError;
54719
+ try {
54720
+ const statuses = asRecord(
54721
+ resultData(
54722
+ await client2.session.status(
54723
+ { directory: worktree.folder },
54724
+ { throwOnError: true }
54725
+ )
54726
+ )
54727
+ );
54728
+ for (const [id2, value] of Object.entries(statuses))
54729
+ if (["busy", "retry"].includes(String(asRecord(value).type)))
54730
+ activeIds.add(id2);
54731
+ } catch {
54732
+ }
54733
+ for (const [nativeId, { session, surface }] of sessions) {
54734
+ const directory = session.directory ?? asRecord(session.location).directory;
54735
+ if (typeof directory === "string" && resolve10(directory) !== resolve10(worktree.folder))
54736
+ continue;
54737
+ const time = asRecord(session.time);
54738
+ const active = activeIds.has(nativeId);
54739
+ const key = JSON.stringify([
54740
+ surface,
54741
+ time.updated,
54742
+ session.cost,
54743
+ session.tokens,
54744
+ session.model
54745
+ ]);
54746
+ const cached = this.hydrationCache.get(nativeId);
54747
+ if (active) this.hydrationCache.delete(nativeId);
54748
+ let history = {};
54749
+ if (!active && cached?.key === key) history = cached.history;
54750
+ else {
53942
54751
  try {
53943
- const messages = asRecord(
53944
- resultData(
53945
- await client2.v2.session.messages({
53946
- sessionID: String(session.id),
53947
- limit: 1,
53948
- order: "desc"
53949
- })
53950
- )
53951
- );
53952
- finalOutput = firstString(
53953
- Array.isArray(messages.data) ? messages.data[0] : messages
54752
+ const messages = await this.importedHistory(
54753
+ client2,
54754
+ nativeId,
54755
+ worktree.folder,
54756
+ surface
53954
54757
  );
53955
- } catch {
54758
+ history = opencodeImportedHistory(session, messages);
54759
+ if (!active) this.hydrationCache.set(nativeId, { key, history });
54760
+ } catch (error) {
54761
+ const detail = error instanceof Error ? error.message : firstString(asRecord(error).message) ?? String(asRecord(error)._tag ?? error);
54762
+ hydrationFailures.push(`${nativeId}: ${detail}`);
53956
54763
  }
53957
- results.push({
53958
- nativeId: String(session.id),
53959
- worktreeId: worktree.id,
53960
- kind: session.agent === "plan" ? "PLAN" : "SESSION",
53961
- status: activeIds.has(String(session.id)) ? "IN_PROGRESS" : "COMPLETED",
53962
- archived: Boolean(time.archived),
53963
- model: typeof selectedModel.providerID === "string" && typeof selectedModel.id === "string" ? `${selectedModel.providerID}/${selectedModel.id}` : void 0,
53964
- prompt: firstString(session.title ?? session.summary) || "Imported OpenCode session",
53965
- finalOutput: finalOutput || firstString(session.summary),
53966
- branch: worktree.branch || void 0,
53967
- createdAt: time.created ? new Date(Number(time.created)).toISOString() : void 0,
53968
- updatedAt: time.updated ? new Date(Number(time.updated)).toISOString() : void 0,
53969
- rawMetadata: session
53970
- });
53971
54764
  }
53972
- cursor = typeof asRecord(response.cursor).next === "string" ? String(asRecord(response.cursor).next) : void 0;
53973
- } while (cursor);
54765
+ if (history.events) hydratedCount += 1;
54766
+ results.push({
54767
+ nativeId,
54768
+ worktreeId: worktree.id,
54769
+ ...typeof session.cost === "number" && Number.isFinite(session.cost) && session.cost >= 0 ? { estimatedCost: session.cost, pricingSource: "opencode-history" } : {},
54770
+ ...history,
54771
+ kind: history.kind ?? (session.agent === "plan" ? "PLAN" : "SESSION"),
54772
+ status: active ? "IN_PROGRESS" : history.status ?? "COMPLETED",
54773
+ archived: Boolean(time.archived),
54774
+ model: history.model,
54775
+ prompt: history.prompt,
54776
+ branch: worktree.branch || void 0,
54777
+ createdAt: opencodeTimestamp(time.created),
54778
+ updatedAt: opencodeTimestamp(time.updated),
54779
+ rawMetadata: session
54780
+ });
54781
+ }
54782
+ }
54783
+ if (hydrationFailures.length) {
54784
+ const detail = `OpenCode history unavailable for ${hydrationFailures.length} sessions: ${hydrationFailures.slice(0, 3).join("; ")}`;
54785
+ if (!hydratedCount) throw new Error(detail);
54786
+ console.warn(detail);
53974
54787
  }
53975
54788
  return results;
53976
54789
  }
@@ -54707,10 +55520,19 @@ var RunManager = class {
54707
55520
  { ...catalog, capabilities: adapter.capabilities }
54708
55521
  );
54709
55522
  const runs = await adapter.discover(worktrees);
54710
- await this.client.importProviderRuns(
54711
- adapter.key,
54712
- runs
54713
- );
55523
+ let batch = [];
55524
+ let batchBytes = 0;
55525
+ for (const run of runs) {
55526
+ const bytes = Buffer.byteLength(JSON.stringify(run), "utf8");
55527
+ if (batch.length && batchBytes + bytes > 4 * 1024 * 1024) {
55528
+ await this.client.importProviderRuns(adapter.key, batch);
55529
+ batch = [];
55530
+ batchBytes = 0;
55531
+ }
55532
+ batch.push(run);
55533
+ batchBytes += bytes;
55534
+ }
55535
+ await this.client.importProviderRuns(adapter.key, batch);
54714
55536
  } catch (error) {
54715
55537
  await this.client.reportRunProviderImportStatus(
54716
55538
  adapter.key,
@@ -54745,7 +55567,7 @@ var RunManager = class {
54745
55567
  return await operation();
54746
55568
  } catch (error) {
54747
55569
  if (this.stopped || attempt >= attempts) throw error;
54748
- await new Promise((resolve10) => setTimeout(resolve10, delay));
55570
+ await new Promise((resolve11) => setTimeout(resolve11, delay));
54749
55571
  delay = Math.min(delay * 2, 3e4);
54750
55572
  }
54751
55573
  }
@@ -55025,9 +55847,9 @@ async function runAgentSession(config, signal, onUnhealthy) {
55025
55847
  }
55026
55848
  }
55027
55849
  );
55028
- await new Promise((resolve10) => {
55029
- if (signal.aborted) resolve10();
55030
- else signal.addEventListener("abort", () => resolve10(), { once: true });
55850
+ await new Promise((resolve11) => {
55851
+ if (signal.aborted) resolve11();
55852
+ else signal.addEventListener("abort", () => resolve11(), { once: true });
55031
55853
  });
55032
55854
  if (heartbeatTimer) clearTimeout(heartbeatTimer);
55033
55855
  if (jobReconciliationTimer) clearTimeout(jobReconciliationTimer);
@@ -55094,7 +55916,7 @@ var defaultDependencies = {
55094
55916
  inventory: collectInventory,
55095
55917
  load: loadConfig,
55096
55918
  save: saveConfig,
55097
- wait: (milliseconds, signal) => new Promise((resolve10, reject) => {
55919
+ wait: (milliseconds, signal) => new Promise((resolve11, reject) => {
55098
55920
  if (signal.aborted) {
55099
55921
  reject(new Error("Development agent startup was cancelled"));
55100
55922
  return;
@@ -55105,7 +55927,7 @@ var defaultDependencies = {
55105
55927
  };
55106
55928
  const timer = setTimeout(() => {
55107
55929
  signal.removeEventListener("abort", onAbort);
55108
- resolve10();
55930
+ resolve11();
55109
55931
  }, milliseconds);
55110
55932
  signal.addEventListener("abort", onAbort, { once: true });
55111
55933
  })