@evident-ai/cli 3.5.2-dev.35de651 → 3.5.2-dev.3f3949d

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.
package/dist/index.js CHANGED
@@ -315,14 +315,14 @@ function blank() {
315
315
  console.log();
316
316
  }
317
317
  function waitForEnter(prompt = "Press Enter to continue...") {
318
- return new Promise((resolve4) => {
318
+ return new Promise((resolve5) => {
319
319
  process.stdout.write(chalk.dim(prompt));
320
320
  const handler = () => {
321
321
  process.stdin.removeListener("data", handler);
322
322
  process.stdin.setRawMode?.(false);
323
323
  process.stdin.pause();
324
324
  console.log();
325
- resolve4();
325
+ resolve5();
326
326
  };
327
327
  if (process.stdin.isTTY) {
328
328
  process.stdin.setRawMode?.(true);
@@ -332,7 +332,7 @@ function waitForEnter(prompt = "Press Enter to continue...") {
332
332
  });
333
333
  }
334
334
  function sleep(ms) {
335
- return new Promise((resolve4) => setTimeout(resolve4, ms));
335
+ return new Promise((resolve5) => setTimeout(resolve5, ms));
336
336
  }
337
337
 
338
338
  // src/commands/login.ts
@@ -410,19 +410,19 @@ async function tokenLogin() {
410
410
  );
411
411
  blank();
412
412
  process.stdout.write("Paste token: ");
413
- const token = await new Promise((resolve4) => {
413
+ const token = await new Promise((resolve5) => {
414
414
  let data = "";
415
415
  process.stdin.setEncoding("utf8");
416
416
  process.stdin.on("data", (chunk) => {
417
417
  data += chunk;
418
418
  });
419
419
  process.stdin.on("end", () => {
420
- resolve4(data.trim());
420
+ resolve5(data.trim());
421
421
  });
422
422
  if (process.stdin.isTTY) {
423
423
  process.stdin.once("data", (chunk) => {
424
424
  process.stdin.pause();
425
- resolve4(chunk.toString().trim());
425
+ resolve5(chunk.toString().trim());
426
426
  });
427
427
  process.stdin.resume();
428
428
  }
@@ -1158,7 +1158,7 @@ async function claudeUsage() {
1158
1158
  // src/commands/run.ts
1159
1159
  import { chmodSync as chmodSync3, existsSync as existsSync3, readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "node:fs";
1160
1160
  import { homedir as homedir7 } from "node:os";
1161
- import { isAbsolute as isAbsolute3, join as join11, parse, resolve as resolvePath2 } from "node:path";
1161
+ import { isAbsolute as isAbsolute4, join as join12, parse, resolve as resolvePath2 } from "node:path";
1162
1162
  import chalk7 from "chalk";
1163
1163
 
1164
1164
  // ../../packages/types/src/agents/index.ts
@@ -1322,7 +1322,7 @@ import ora4 from "ora";
1322
1322
  import { select as select4 } from "@inquirer/prompts";
1323
1323
 
1324
1324
  // src/lib/telemetry.ts
1325
- var CLI_VERSION = (true ? "3.5.2-dev.35de651" : void 0) ?? process.env.npm_package_version ?? "unknown";
1325
+ var CLI_VERSION = (true ? "3.5.2-dev.3f3949d" : void 0) ?? process.env.npm_package_version ?? "unknown";
1326
1326
  function getCliVersion() {
1327
1327
  return CLI_VERSION;
1328
1328
  }
@@ -1810,6 +1810,263 @@ async function reclaimSessionDbSpace(input) {
1810
1810
  }
1811
1811
  }
1812
1812
 
1813
+ // src/lib/opencode/dependency-install-diagnostic.ts
1814
+ import { lstatSync, realpathSync } from "node:fs";
1815
+ import { basename, dirname as dirname3, isAbsolute, join as join2, relative, resolve, sep } from "node:path";
1816
+ var DEPENDENCY_INSTALL_FAILURE_MESSAGE = "background dependency install failed";
1817
+ var MAX_DISTINCT_DIAGNOSTICS = 32;
1818
+ var EFFECT = "OpenCode could not install background dependencies for this scope; its plugins and tools may be unavailable";
1819
+ function readOpenCodeLogField(line, key) {
1820
+ const match = line.match(new RegExp(`(?:^|\\s)${key}=(?:"((?:\\\\.|[^"])*)"|(\\S+))`));
1821
+ if (!match) return void 0;
1822
+ if (match[1] === void 0) return match[2];
1823
+ return match[1].replace(/\\(["\\n])/g, (_match, escaped) => {
1824
+ return escaped === "n" ? "\n" : escaped;
1825
+ });
1826
+ }
1827
+ function parseTrigger(line) {
1828
+ if (readOpenCodeLogField(line, "message") !== DEPENDENCY_INSTALL_FAILURE_MESSAGE) {
1829
+ return null;
1830
+ }
1831
+ const error2 = readOpenCodeLogField(line, "error");
1832
+ if (!error2 || !/\bEACCES\b/.test(error2)) return null;
1833
+ const errnoMatch = error2.match(/\bEACCES:\s*permission denied,\s*([a-z_]+)\s+'([^']*)'/);
1834
+ const capturedPath = errnoMatch?.[2];
1835
+ return {
1836
+ dir: readOpenCodeLogField(line, "dir"),
1837
+ op: errnoMatch?.[1],
1838
+ path: capturedPath && isAbsolute(capturedPath) ? capturedPath : void 0
1839
+ };
1840
+ }
1841
+ function getErrorCode(error2) {
1842
+ if (typeof error2 === "object" && error2 !== null && "code" in error2) {
1843
+ const code = error2.code;
1844
+ if (typeof code === "string") return code;
1845
+ }
1846
+ return "UNKNOWN";
1847
+ }
1848
+ function isWithin(path, root) {
1849
+ const candidate = resolve(path);
1850
+ const resolvedRoot = resolve(root);
1851
+ return candidate === resolvedRoot || candidate.startsWith(`${resolvedRoot}${sep}`);
1852
+ }
1853
+ function rootSpecs(context) {
1854
+ return [
1855
+ { token: "~", configured: resolve(context.homeDir) },
1856
+ { token: "$WORKSPACE", configured: resolve(context.cwd) }
1857
+ ];
1858
+ }
1859
+ function resolveRoots(context) {
1860
+ const resolved = [];
1861
+ const unresolved = [];
1862
+ for (const spec of rootSpecs(context)) {
1863
+ try {
1864
+ resolved.push({ ...spec, real: realpathSync(spec.configured) });
1865
+ } catch (_error) {
1866
+ unresolved.push(spec);
1867
+ }
1868
+ }
1869
+ return { resolved, unresolved };
1870
+ }
1871
+ function resolveNearestAncestor(failingPath, remaining, error2) {
1872
+ let ancestor = dirname3(failingPath);
1873
+ const suffix = [basename(failingPath), ...remaining];
1874
+ while (ancestor) {
1875
+ try {
1876
+ return {
1877
+ path: resolve(realpathSync(ancestor), ...suffix),
1878
+ failure: { path: resolve(failingPath), error: error2 }
1879
+ };
1880
+ } catch (ancestorError) {
1881
+ if (ancestor === dirname3(ancestor) || !getErrorCode(ancestorError).startsWith("E")) {
1882
+ return null;
1883
+ }
1884
+ suffix.unshift(basename(ancestor));
1885
+ ancestor = dirname3(ancestor);
1886
+ }
1887
+ }
1888
+ return null;
1889
+ }
1890
+ function resolveExistingPath(absolutePath) {
1891
+ if (!isAbsolute(absolutePath)) return null;
1892
+ const candidate = resolve(absolutePath);
1893
+ const remaining = [];
1894
+ let current = candidate;
1895
+ while (current) {
1896
+ try {
1897
+ const stat2 = lstatSync(current);
1898
+ if (stat2.isSymbolicLink()) {
1899
+ if (remaining.length === 0) {
1900
+ return { path: join2(realpathSync(dirname3(current)), basename(current)) };
1901
+ }
1902
+ return { path: resolve(realpathSync(current), ...remaining) };
1903
+ }
1904
+ return { path: resolve(realpathSync(current), ...remaining) };
1905
+ } catch (error2) {
1906
+ const code = getErrorCode(error2);
1907
+ if (code !== "ENOENT" && code !== "ENOTDIR") {
1908
+ return resolveNearestAncestor(current, remaining, error2);
1909
+ }
1910
+ }
1911
+ if (current === dirname3(current)) return null;
1912
+ remaining.unshift(basename(current));
1913
+ current = dirname3(current);
1914
+ }
1915
+ return null;
1916
+ }
1917
+ function tokenise(absolutePath, roots) {
1918
+ const pathResolution = resolveExistingPath(absolutePath);
1919
+ if (!pathResolution) return null;
1920
+ const root = roots.reduce((selected, candidate) => {
1921
+ if (!isWithin(pathResolution.path, candidate.real)) return selected;
1922
+ if (!selected || candidate.real.length > selected.real.length || candidate.real.length === selected.real.length && candidate.token === "$WORKSPACE") {
1923
+ return candidate;
1924
+ }
1925
+ return selected;
1926
+ }, null);
1927
+ if (!root) return null;
1928
+ const suffix = relative(root.real, pathResolution.path);
1929
+ return {
1930
+ value: suffix ? `${root.token}/${suffix.split(sep).join("/")}` : root.token,
1931
+ root,
1932
+ resolutionFailure: pathResolution.failure
1933
+ };
1934
+ }
1935
+ function classifyScope(dir, roots) {
1936
+ if (!dir) return "other";
1937
+ const tokenisedDir = tokenise(dir, roots.resolved);
1938
+ if (tokenisedDir) return tokenisedDir.root.token === "$WORKSPACE" ? "workspace" : "global";
1939
+ for (const spec of roots.unresolved) {
1940
+ if (isWithin(dir, spec.configured)) {
1941
+ return spec.token === "$WORKSPACE" ? "workspace" : "global";
1942
+ }
1943
+ }
1944
+ return "other";
1945
+ }
1946
+ function unresolvedRootForPath(path, unresolved) {
1947
+ return unresolved.find((spec) => isWithin(path, spec.configured));
1948
+ }
1949
+ function statType(stat2) {
1950
+ if (stat2.isDirectory()) return "d";
1951
+ if (stat2.isFile()) return "f";
1952
+ if (stat2.isSymbolicLink()) return "l";
1953
+ return "o";
1954
+ }
1955
+ function renderStat(name, stat2) {
1956
+ const mode = (stat2.mode & 4095).toString(8).padStart(4, "0");
1957
+ return `${name}:${statType(stat2)}:${stat2.uid}:${stat2.gid}:${mode}`;
1958
+ }
1959
+ function renderStatError(name, error2) {
1960
+ const code = getErrorCode(error2);
1961
+ return `${name}:${renderErrorCode(code)}`;
1962
+ }
1963
+ function renderErrorCode(code) {
1964
+ if (code === "ENOENT") return "missing";
1965
+ return code.startsWith("E") ? code : `E${code}`;
1966
+ }
1967
+ function inspectChain(rootReal, rootToken, absolutePath, resolutionFailure) {
1968
+ const target = resolve(absolutePath);
1969
+ const relativePath = relative(rootReal, target);
1970
+ const components = relativePath ? relativePath.split(sep) : [];
1971
+ const entries = [];
1972
+ let current = rootReal;
1973
+ let status2 = "complete";
1974
+ let finalStat;
1975
+ for (const name of [rootToken, ...components]) {
1976
+ if (name !== rootToken) current = join2(current, name);
1977
+ if (resolutionFailure && resolve(current) === resolve(resolutionFailure.path)) {
1978
+ entries.push(renderStatError(name, resolutionFailure.error));
1979
+ status2 = "stat_failed";
1980
+ break;
1981
+ }
1982
+ try {
1983
+ const stat2 = lstatSync(current);
1984
+ entries.push(renderStat(name, stat2));
1985
+ finalStat = stat2;
1986
+ } catch (error2) {
1987
+ entries.push(renderStatError(name, error2));
1988
+ if (getErrorCode(error2) !== "ENOENT") status2 = "stat_failed";
1989
+ break;
1990
+ }
1991
+ }
1992
+ let symlinkTarget;
1993
+ let symlinkMeta;
1994
+ if (finalStat?.isSymbolicLink() && entries.length === components.length + 1) {
1995
+ try {
1996
+ symlinkTarget = realpathSync(current);
1997
+ try {
1998
+ symlinkMeta = renderStat("", lstatSync(symlinkTarget)).slice(1);
1999
+ } catch (error2) {
2000
+ symlinkMeta = renderErrorCode(getErrorCode(error2));
2001
+ status2 = "stat_failed";
2002
+ }
2003
+ } catch (error2) {
2004
+ symlinkTarget = "<unresolved>";
2005
+ symlinkMeta = renderErrorCode(getErrorCode(error2));
2006
+ status2 = "stat_failed";
2007
+ }
2008
+ }
2009
+ return { status: status2, chain: entries.join(","), symlinkTarget, symlinkMeta };
2010
+ }
2011
+ function buildDiagnostic(line, context) {
2012
+ const trigger = parseTrigger(line);
2013
+ if (!trigger) return null;
2014
+ const roots = resolveRoots(context);
2015
+ const scope = classifyScope(trigger.dir, roots);
2016
+ const pathSource = trigger.path ? "error" : trigger.dir ? "dir" : "none";
2017
+ const inspectedPath = trigger.path ?? trigger.dir;
2018
+ const microvmId = context.microvmId?.trim() || "unknown";
2019
+ const euid = context.euid === void 0 ? "unavailable" : String(context.euid);
2020
+ const egid = context.egid === void 0 ? "unavailable" : String(context.egid);
2021
+ const op = trigger.op ?? "unknown";
2022
+ if (!inspectedPath) {
2023
+ return {
2024
+ event: `OPENCODE-DEPENDENCY-INSTALL-EACCES: status=no_path path_source=${pathSource} scope=${scope} microvm_id=${microvmId} op=${op} path=unknown cli_euid=${euid} cli_egid=${egid} effect="${EFFECT}" chain=unavailable`,
2025
+ localDetail: null
2026
+ };
2027
+ }
2028
+ const tokenisedPath = tokenise(inspectedPath, roots.resolved);
2029
+ if (!tokenisedPath) {
2030
+ const unresolvedRoot = unresolvedRootForPath(inspectedPath, roots.unresolved);
2031
+ if (unresolvedRoot) {
2032
+ return {
2033
+ event: `OPENCODE-DEPENDENCY-INSTALL-EACCES: status=root_unresolved path_source=${pathSource} scope=${scope} microvm_id=${microvmId} op=${op} path=unknown cli_euid=${euid} cli_egid=${egid} effect="${EFFECT}" chain=unavailable`,
2034
+ localDetail: null
2035
+ };
2036
+ }
2037
+ return {
2038
+ event: `OPENCODE-DEPENDENCY-INSTALL-EACCES: status=outside_roots path_source=${pathSource} scope=${scope} microvm_id=${microvmId} op=${op} path=<outside-approved-roots> cli_euid=${euid} cli_egid=${egid} effect="${EFFECT}" chain=unavailable`,
2039
+ localDetail: `OPENCODE-DEPENDENCY-INSTALL-EACCES (local only): outside approved roots: ${inspectedPath}`
2040
+ };
2041
+ }
2042
+ const inspection = inspectChain(
2043
+ tokenisedPath.root.real,
2044
+ tokenisedPath.root.token,
2045
+ inspectedPath,
2046
+ tokenisedPath.resolutionFailure
2047
+ );
2048
+ const optionalSymlinkFields = inspection.symlinkTarget ? ` symlink_target=${tokenise(inspection.symlinkTarget, roots.resolved)?.value ?? "<outside-approved-roots>"}` + (inspection.symlinkMeta ? ` symlink_meta=${inspection.symlinkMeta}` : "") : "";
2049
+ return {
2050
+ event: `OPENCODE-DEPENDENCY-INSTALL-EACCES: status=${inspection.status} path_source=${pathSource} scope=${scope} microvm_id=${microvmId} op=${op} path=${tokenisedPath.value} cli_euid=${euid} cli_egid=${egid} effect="${EFFECT}" chain=${inspection.chain}${optionalSymlinkFields}`,
2051
+ localDetail: null
2052
+ };
2053
+ }
2054
+ function createDependencyInstallDiagnosticReporter({
2055
+ context,
2056
+ emitEvent: emitEvent2,
2057
+ emitLocal
2058
+ }) {
2059
+ const emittedEvents = /* @__PURE__ */ new Set();
2060
+ return (line) => {
2061
+ const diagnostic = buildDiagnostic(line, context);
2062
+ if (!diagnostic || emittedEvents.has(diagnostic.event)) return;
2063
+ if (emittedEvents.size >= MAX_DISTINCT_DIAGNOSTICS) return;
2064
+ emittedEvents.add(diagnostic.event);
2065
+ emitEvent2(diagnostic.event);
2066
+ if (diagnostic.localDetail) emitLocal(diagnostic.localDetail);
2067
+ };
2068
+ }
2069
+
1813
2070
  // src/lib/runner-activity-telemetry.ts
1814
2071
  var FORWARDED_LEVELS_BY_SOURCE = {
1815
2072
  "cli.run": /* @__PURE__ */ new Set(["warn", "error"]),
@@ -1841,7 +2098,17 @@ function parseOpenCodeLogLine(line) {
1841
2098
  }
1842
2099
  var MAX_LINE_BUFFER_BYTES = 16 * 1024;
1843
2100
  var REPEAT_SUPPRESSION_WINDOW_MS = 6e4;
1844
- function createOpenCodeActivityForwarder(getContext, onSessionStoreWriteFailure) {
2101
+ function sanitiseDependencyInstallWarning(line) {
2102
+ const withoutDirectory = line.replace(
2103
+ /((?:^|\s)dir=)(?:"(?:\\.|[^"])*"|\/[^\s)"']+|\S+)/g,
2104
+ "$1<path>"
2105
+ );
2106
+ return withoutDirectory.replace(
2107
+ /((?:^|\s)error=)"((?:\\.|[^"])*)"/,
2108
+ (_match, prefix, error2) => `${prefix}"${error2.replace(/'\/[^']*'/g, "'<path>'")}"`
2109
+ );
2110
+ }
2111
+ function createOpenCodeActivityForwarder(getContext, onSessionStoreWriteFailure, onDependencyInstallFailure) {
1845
2112
  let buffer = Buffer.alloc(0);
1846
2113
  const repeatStates = /* @__PURE__ */ new Map();
1847
2114
  const forwardLine = (line, parsed, repeatsSuppressed = 0) => {
@@ -1853,7 +2120,7 @@ function createOpenCodeActivityForwarder(getContext, onSessionStoreWriteFailure)
1853
2120
  forwardRunnerActivity(
1854
2121
  {
1855
2122
  level: parsed.level,
1856
- error: line,
2123
+ error: parsed.message === DEPENDENCY_INSTALL_FAILURE_MESSAGE ? sanitiseDependencyInstallWarning(line) : line,
1857
2124
  metadata: Object.keys(metadata).length > 0 ? metadata : void 0,
1858
2125
  source: "opencode"
1859
2126
  },
@@ -1871,24 +2138,7 @@ function createOpenCodeActivityForwarder(getContext, onSessionStoreWriteFailure)
1871
2138
  repeatStates.delete(key);
1872
2139
  }
1873
2140
  };
1874
- const flushLine = (line) => {
1875
- const parsed = parseOpenCodeLogLine(line);
1876
- const isDiskFailure = isSessionStoreWriteFailure(line);
1877
- const cause = sessionStoreCauseToken(line);
1878
- if (onSessionStoreWriteFailure && (isDiskFailure || cause !== null)) {
1879
- try {
1880
- onSessionStoreWriteFailure({
1881
- isDiskFailure,
1882
- sessionId: parsed?.sessionID ?? null,
1883
- cause
1884
- });
1885
- } catch (err) {
1886
- reportTelemetryDiagnostic(
1887
- `Runner activity telemetry could not record session-store write failure: ${err instanceof Error ? err.message : String(err)}`
1888
- );
1889
- }
1890
- }
1891
- if (!parsed) return;
2141
+ const forwardWithRepeatSuppression = (line, parsed) => {
1892
2142
  const now = Date.now();
1893
2143
  const key = parsed.message === void 0 ? void 0 : `${parsed.level}\0${parsed.message}\0${parsed.sessionID ?? ""}\0${parsed.messageID ?? ""}`;
1894
2144
  expireRepeatStates(now, key);
@@ -1911,6 +2161,35 @@ function createOpenCodeActivityForwarder(getContext, onSessionStoreWriteFailure)
1911
2161
  previous.parsed = parsed;
1912
2162
  previous.repeatsSuppressed++;
1913
2163
  };
2164
+ const flushLine = (line) => {
2165
+ const parsed = parseOpenCodeLogLine(line);
2166
+ const isDiskFailure = isSessionStoreWriteFailure(line);
2167
+ const cause = sessionStoreCauseToken(line);
2168
+ if (onSessionStoreWriteFailure && (isDiskFailure || cause !== null)) {
2169
+ try {
2170
+ onSessionStoreWriteFailure({
2171
+ isDiskFailure,
2172
+ sessionId: parsed?.sessionID ?? null,
2173
+ cause
2174
+ });
2175
+ } catch (err) {
2176
+ reportTelemetryDiagnostic(
2177
+ `Runner activity telemetry could not record session-store write failure: ${err instanceof Error ? err.message : String(err)}`
2178
+ );
2179
+ }
2180
+ }
2181
+ if (!parsed) return;
2182
+ forwardWithRepeatSuppression(line, parsed);
2183
+ if (onDependencyInstallFailure && parsed.message === DEPENDENCY_INSTALL_FAILURE_MESSAGE) {
2184
+ try {
2185
+ onDependencyInstallFailure(line);
2186
+ } catch (err) {
2187
+ reportTelemetryDiagnostic(
2188
+ `Runner activity telemetry could not record dependency-install failure diagnostic: ${err instanceof Error ? err.message : String(err)}`
2189
+ );
2190
+ }
2191
+ }
2192
+ };
1914
2193
  const forward = ((chunk) => {
1915
2194
  buffer = Buffer.concat([buffer, Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, "utf-8")]);
1916
2195
  let newlineIndex;
@@ -1950,6 +2229,23 @@ function truncate(message) {
1950
2229
  const remaining = MAX_MESSAGE_LENGTH - marker.length;
1951
2230
  const headLength = Math.ceil(remaining * 0.6);
1952
2231
  const tailLength = remaining - headLength;
2232
+ const effectMatch = message.startsWith("OPENCODE-DEPENDENCY-INSTALL-EACCES:") ? message.match(/ effect="[^"]*"/) : null;
2233
+ if (effectMatch?.index !== void 0) {
2234
+ const effectEnd = effectMatch.index + effectMatch[0].length;
2235
+ if (effectEnd > headLength && effectEnd < message.length) {
2236
+ const effectLength = effectMatch[0].length;
2237
+ const nonEffectLength = remaining - effectLength;
2238
+ const preservedTailLength = Math.min(
2239
+ message.length - effectEnd,
2240
+ nonEffectLength - Math.min(effectMatch.index, Math.ceil(nonEffectLength * 0.6))
2241
+ );
2242
+ const preservedHeadLength = Math.min(
2243
+ effectMatch.index,
2244
+ nonEffectLength - preservedTailLength
2245
+ );
2246
+ return message.slice(0, preservedHeadLength) + marker + message.slice(effectMatch.index, effectEnd) + message.slice(message.length - preservedTailLength);
2247
+ }
2248
+ }
1953
2249
  return message.slice(0, headLength) + marker + message.slice(message.length - tailLength);
1954
2250
  }
1955
2251
  function sanitiseMetadata(metadata) {
@@ -2027,10 +2323,10 @@ function forwardRunnerActivity(entry, context) {
2027
2323
 
2028
2324
  // src/lib/opencode/session-db-recovery-report.ts
2029
2325
  import { readFileSync as readFileSync2, unlinkSync } from "node:fs";
2030
- import { join as join2 } from "node:path";
2326
+ import { join as join3 } from "node:path";
2031
2327
  function sessionDbRecoveryReportPath(homeDir, env) {
2032
2328
  const override = env.EVIDENT_SESSION_DB_RECOVERY_REPORT?.trim();
2033
- return override || join2(homeDir, ".local", "state", "evident", "session-db-recovery.jsonl");
2329
+ return override || join3(homeDir, ".local", "state", "evident", "session-db-recovery.jsonl");
2034
2330
  }
2035
2331
  function drainSessionDbRecoveryReport({
2036
2332
  homeDir,
@@ -2303,7 +2599,7 @@ async function waitForOpenCodeHealth(port, timeoutMs = 3e4) {
2303
2599
  if (health.healthy || health.authFailed) {
2304
2600
  return health;
2305
2601
  }
2306
- await new Promise((resolve4) => setTimeout(resolve4, 1e3));
2602
+ await new Promise((resolve5) => setTimeout(resolve5, 1e3));
2307
2603
  }
2308
2604
  return { healthy: false, error: "Timeout waiting for OpenCode to be healthy" };
2309
2605
  }
@@ -2314,7 +2610,7 @@ async function waitForOpenCode2Health(port, password, timeoutMs = 3e4) {
2314
2610
  if (health.healthy || health.authFailed) {
2315
2611
  return health;
2316
2612
  }
2317
- await new Promise((resolve4) => setTimeout(resolve4, 1e3));
2613
+ await new Promise((resolve5) => setTimeout(resolve5, 1e3));
2318
2614
  }
2319
2615
  return { healthy: false, error: "Timeout waiting for OpenCode to be healthy" };
2320
2616
  }
@@ -2904,7 +3200,7 @@ async function sendPromptAsync(port, sessionId, content, options, attachments, c
2904
3200
  return dispatchedUserMessageId;
2905
3201
  }
2906
3202
  if (attempt < READ_BACK_ATTEMPTS - 1) {
2907
- await new Promise((resolve4) => setTimeout(resolve4, READ_BACK_DELAY_MS));
3203
+ await new Promise((resolve5) => setTimeout(resolve5, READ_BACK_DELAY_MS));
2908
3204
  }
2909
3205
  }
2910
3206
  return null;
@@ -3076,9 +3372,11 @@ function sumAssistantUsage(messages) {
3076
3372
  let cacheWriteSum = 0;
3077
3373
  let costSum = 0;
3078
3374
  let sawCost = false;
3375
+ let toolCallCount = 0;
3079
3376
  let modelId = null;
3080
3377
  let providerId = null;
3081
3378
  for (const m of selected) {
3379
+ toolCallCount += m.parts?.filter((part) => part.type === "tool").length ?? 0;
3082
3380
  const info = m.info;
3083
3381
  if (!info) continue;
3084
3382
  const tokens = info.tokens;
@@ -3115,7 +3413,8 @@ function sumAssistantUsage(messages) {
3115
3413
  usage_tokens_cache_write: cacheWriteSum,
3116
3414
  // NULL means OpenCode never reported a cost; it is distinct from a genuine
3117
3415
  // zero-cost message, which sets `sawCost` with `costSum === 0`.
3118
- usage_cost_usd: sawCost ? costSum : null
3416
+ usage_cost_usd: sawCost ? costSum : null,
3417
+ turn_tool_calls: toolCallCount
3119
3418
  };
3120
3419
  }
3121
3420
  function messageUsage(messages, userMessageId) {
@@ -3964,12 +4263,12 @@ async function readV2SessionErrorStream(client, options) {
3964
4263
 
3965
4264
  // src/lib/opencode/credential-import-v2.ts
3966
4265
  import { readFileSync as readFileSync4 } from "node:fs";
3967
- import { join as join4 } from "node:path";
4266
+ import { join as join5 } from "node:path";
3968
4267
 
3969
4268
  // src/lib/openai-usage.ts
3970
4269
  import { readFileSync as readFileSync3 } from "node:fs";
3971
4270
  import { homedir as homedir3 } from "node:os";
3972
- import { join as join3 } from "node:path";
4271
+ import { join as join4 } from "node:path";
3973
4272
  var CODEX_RESPONSES_URL = "https://chatgpt.com/backend-api/codex/responses";
3974
4273
  var OPENCODE_AUTH_SEGMENTS = [".local", "share", "opencode", "auth.json"];
3975
4274
  var OpenAiUsageError = class extends Error {
@@ -3983,7 +4282,7 @@ function isLocalCredentialProblem2(err) {
3983
4282
  }
3984
4283
  function readOpenCodeChatGptCredentials() {
3985
4284
  try {
3986
- const raw = readFileSync3(join3(homedir3(), ...OPENCODE_AUTH_SEGMENTS), "utf-8");
4285
+ const raw = readFileSync3(join4(homedir3(), ...OPENCODE_AUTH_SEGMENTS), "utf-8");
3987
4286
  let parsed;
3988
4287
  try {
3989
4288
  parsed = JSON.parse(raw);
@@ -4209,13 +4508,13 @@ var INTEGRATION_LIST_RETRY_DELAY_MS = 1e3;
4209
4508
  function isRecord3(value) {
4210
4509
  return typeof value === "object" && value !== null && !Array.isArray(value);
4211
4510
  }
4212
- var sleep2 = (milliseconds) => new Promise((resolve4) => setTimeout(resolve4, milliseconds));
4511
+ var sleep2 = (milliseconds) => new Promise((resolve5) => setTimeout(resolve5, milliseconds));
4213
4512
  function emptyResult(unsupported = [], outcome = "completed") {
4214
4513
  return { outcome, imported: [], alreadyConnected: [], unsupported, failed: [] };
4215
4514
  }
4216
4515
  function readOpenCodeCredentials(homeDir, log3) {
4217
4516
  try {
4218
- const raw = readFileSync4(join4(homeDir, ...OPENCODE_AUTH_SEGMENTS), "utf8");
4517
+ const raw = readFileSync4(join5(homeDir, ...OPENCODE_AUTH_SEGMENTS), "utf8");
4219
4518
  let parsed;
4220
4519
  try {
4221
4520
  parsed = JSON.parse(raw);
@@ -4417,7 +4716,7 @@ async function importOpenCodeV2Credentials({
4417
4716
  import { spawn as spawn2 } from "node:child_process";
4418
4717
  import { mkdirSync, statSync as statSync3, unlinkSync as unlinkSync2, writeFileSync } from "node:fs";
4419
4718
  import { homedir as homedir4 } from "node:os";
4420
- import { dirname as dirname3, resolve as resolvePath } from "node:path";
4719
+ import { dirname as dirname4, resolve as resolvePath } from "node:path";
4421
4720
 
4422
4721
  // src/lib/runner-synchroniser.ts
4423
4722
  import { spawn } from "node:child_process";
@@ -4427,7 +4726,7 @@ function appendError(stderr, error2) {
4427
4726
  ${message}`;
4428
4727
  }
4429
4728
  function runSynchroniser(args, opts) {
4430
- return new Promise((resolve4) => {
4729
+ return new Promise((resolve5) => {
4431
4730
  let child;
4432
4731
  let stdout = "";
4433
4732
  let stderr = "";
@@ -4441,7 +4740,7 @@ function runSynchroniser(args, opts) {
4441
4740
  if (timer.handle) clearTimeout(timer.handle);
4442
4741
  if (abortListener) opts.signal?.removeEventListener("abort", abortListener);
4443
4742
  if (spawnListener) child.removeListener("spawn", spawnListener);
4444
- resolve4(result);
4743
+ resolve5(result);
4445
4744
  };
4446
4745
  try {
4447
4746
  child = spawn("runner-synchroniser", args, {
@@ -4545,7 +4844,7 @@ function clearMarker(options) {
4545
4844
  function markNoReplicate(options, message) {
4546
4845
  if (options.noReplicateMarker) {
4547
4846
  try {
4548
- mkdirSync(dirname3(options.noReplicateMarker), { recursive: true });
4847
+ mkdirSync(dirname4(options.noReplicateMarker), { recursive: true });
4549
4848
  writeFileSync(options.noReplicateMarker, "");
4550
4849
  } catch (error2) {
4551
4850
  options.log(
@@ -4604,7 +4903,7 @@ function parseSynchroniserEnv(stdout) {
4604
4903
  return values;
4605
4904
  }
4606
4905
  function runCommand(command, args, options) {
4607
- return new Promise((resolve4) => {
4906
+ return new Promise((resolve5) => {
4608
4907
  let child;
4609
4908
  let stdout = "";
4610
4909
  let stderr = "";
@@ -4613,7 +4912,7 @@ function runCommand(command, args, options) {
4613
4912
  if (settled) return;
4614
4913
  settled = true;
4615
4914
  if (timer) clearTimeout(timer);
4616
- resolve4(result);
4915
+ resolve5(result);
4617
4916
  };
4618
4917
  try {
4619
4918
  child = spawn2(command, args, {
@@ -4621,7 +4920,7 @@ function runCommand(command, args, options) {
4621
4920
  stdio: ["ignore", "pipe", "pipe"]
4622
4921
  });
4623
4922
  } catch (error2) {
4624
- resolve4({
4923
+ resolve5({
4625
4924
  code: null,
4626
4925
  stdout,
4627
4926
  stderr: error2 instanceof Error ? error2.message : String(error2),
@@ -4700,7 +4999,7 @@ async function ensureLitestreamConfig(options, env) {
4700
4999
  return null;
4701
5000
  }
4702
5001
  try {
4703
- mkdirSync(dirname3(configPath), { recursive: true });
5002
+ mkdirSync(dirname4(configPath), { recursive: true });
4704
5003
  writeFileSync(configPath, rendered.stdout);
4705
5004
  } catch (error2) {
4706
5005
  options.log(
@@ -4967,7 +5266,7 @@ async function restoreAndVerifySessionDb(options) {
4967
5266
  // src/lib/opencode/session-db-provenance.ts
4968
5267
  import { createRequire } from "node:module";
4969
5268
  import { mkdirSync as mkdirSync2, readFileSync as readFileSync5, writeFileSync as writeFileSync2 } from "node:fs";
4970
- import { dirname as dirname4, join as join5 } from "node:path";
5269
+ import { dirname as dirname5, join as join6 } from "node:path";
4971
5270
  var require2 = createRequire(import.meta.url);
4972
5271
  function readSessionDbMigrationIds(dbPath) {
4973
5272
  let db;
@@ -5004,7 +5303,7 @@ function readSessionDbMigrationIds(dbPath) {
5004
5303
  }
5005
5304
  function sessionDbProvenanceStatePath(homeDir, env) {
5006
5305
  const override = env.EVIDENT_SESSION_DB_PROVENANCE_STATE?.trim();
5007
- return override || join5(homeDir, ".local", "state", "evident", "session-db-provenance.json");
5306
+ return override || join6(homeDir, ".local", "state", "evident", "session-db-provenance.json");
5008
5307
  }
5009
5308
  function loadSessionDbProvenanceState(path) {
5010
5309
  let value;
@@ -5033,7 +5332,7 @@ function loadSessionDbProvenanceState(path) {
5033
5332
  }
5034
5333
  function saveSessionDbProvenanceState(path, state) {
5035
5334
  try {
5036
- mkdirSync2(dirname4(path), { recursive: true });
5335
+ mkdirSync2(dirname5(path), { recursive: true });
5037
5336
  writeFileSync2(path, `${JSON.stringify(state, null, 2)}
5038
5337
  `, "utf8");
5039
5338
  } catch (error2) {
@@ -5124,7 +5423,7 @@ async function stopProcessAndWait(child, timeoutMs, sendTerm, sendKill) {
5124
5423
  if (child.exitCode !== null || child.signalCode !== null) {
5125
5424
  return { outcome: "exited", code: child.exitCode, signal: child.signalCode };
5126
5425
  }
5127
- return new Promise((resolve4, reject) => {
5426
+ return new Promise((resolve5, reject) => {
5128
5427
  let forced = false;
5129
5428
  let settled = false;
5130
5429
  const timer = setTimeout(() => {
@@ -5144,7 +5443,7 @@ async function stopProcessAndWait(child, timeoutMs, sendTerm, sendKill) {
5144
5443
  settled = true;
5145
5444
  clearTimeout(timer);
5146
5445
  child.removeListener("exit", onExit);
5147
- resolve4(result);
5446
+ resolve5(result);
5148
5447
  };
5149
5448
  const fail = (error2) => {
5150
5449
  if (settled) return;
@@ -5620,7 +5919,7 @@ function selectSessionsToDelete(sessions, opts) {
5620
5919
  );
5621
5920
  }
5622
5921
  var DEFAULT_INTERVAL = "1h";
5623
- function resolve(flag, envValue, fallback) {
5922
+ function resolve2(flag, envValue, fallback) {
5624
5923
  return flag ?? envValue ?? fallback;
5625
5924
  }
5626
5925
  function parseMaxCount(input) {
@@ -5636,9 +5935,9 @@ function parseMaxCount(input) {
5636
5935
  }
5637
5936
  function resolveSessionCleanupConfig(flags, env = process.env) {
5638
5937
  const warnings = [];
5639
- const maxAgeRaw = resolve(flags.maxAge, env.EVIDENT_SESSION_CLEANUP_MAX_AGE);
5640
- const maxCountRaw = resolve(flags.maxCount, env.EVIDENT_SESSION_CLEANUP_MAX_COUNT);
5641
- const intervalRaw = resolve(
5938
+ const maxAgeRaw = resolve2(flags.maxAge, env.EVIDENT_SESSION_CLEANUP_MAX_AGE);
5939
+ const maxCountRaw = resolve2(flags.maxCount, env.EVIDENT_SESSION_CLEANUP_MAX_COUNT);
5940
+ const intervalRaw = resolve2(
5642
5941
  flags.interval,
5643
5942
  env.EVIDENT_SESSION_CLEANUP_INTERVAL,
5644
5943
  DEFAULT_INTERVAL
@@ -5678,10 +5977,10 @@ function resolveSessionCleanupConfig(flags, env = process.env) {
5678
5977
 
5679
5978
  // src/lib/opencode/session-db-size.ts
5680
5979
  import { statSync as statSync4 } from "node:fs";
5681
- import { join as join6 } from "node:path";
5980
+ import { join as join7 } from "node:path";
5682
5981
  var LARGE_DB_THRESHOLD_BYTES = 268435456;
5683
5982
  function statSessionDbBytes(homeDir) {
5684
- const dbPath = join6(homeDir, ".local", "share", "opencode", "opencode.db");
5983
+ const dbPath = join7(homeDir, ".local", "share", "opencode", "opencode.db");
5685
5984
  try {
5686
5985
  return statSync4(dbPath).size;
5687
5986
  } catch (err) {
@@ -5711,12 +6010,12 @@ function buildSessionStoreSizeWarning(input) {
5711
6010
  // src/lib/opencode/log-tail.ts
5712
6011
  import { statSync as statSync5 } from "node:fs";
5713
6012
  import { homedir as homedir5 } from "node:os";
5714
- import { join as join7 } from "node:path";
6013
+ import { join as join8 } from "node:path";
5715
6014
  import { open as open2, stat } from "node:fs/promises";
5716
6015
  var DEFAULT_POLL_INTERVAL_MS = 1e3;
5717
6016
  function resolveOpenCodeLogPath(homeDir = homedir5(), env = process.env) {
5718
- const dataDir = env.XDG_DATA_HOME || join7(homeDir, ".local", "share");
5719
- return join7(dataDir, "opencode", "log", "opencode.log");
6017
+ const dataDir = env.XDG_DATA_HOME || join8(homeDir, ".local", "share");
6018
+ return join8(dataDir, "opencode", "log", "opencode.log");
5720
6019
  }
5721
6020
  function isEnoent(error2) {
5722
6021
  return error2?.code === "ENOENT";
@@ -5871,8 +6170,8 @@ var StreamForwarder = class {
5871
6170
  pauseStream(sid, stream) {
5872
6171
  if (!stream.paused) {
5873
6172
  stream.paused = true;
5874
- stream.pausePromise = new Promise((resolve4) => {
5875
- stream.resume = resolve4;
6173
+ stream.pausePromise = new Promise((resolve5) => {
6174
+ stream.resume = resolve5;
5876
6175
  });
5877
6176
  }
5878
6177
  if (stream.pauseTimer !== null) clearTimeout(stream.pauseTimer);
@@ -5939,12 +6238,12 @@ var StreamForwarder = class {
5939
6238
  let endBody;
5940
6239
  if (has_body) {
5941
6240
  const chunks = [];
5942
- bodyPromise = new Promise((resolve4) => {
6241
+ bodyPromise = new Promise((resolve5) => {
5943
6242
  pushBody = (buf) => {
5944
6243
  chunks.push(buf);
5945
6244
  };
5946
6245
  endBody = () => {
5947
- resolve4(Buffer.concat(chunks));
6246
+ resolve5(Buffer.concat(chunks));
5948
6247
  };
5949
6248
  });
5950
6249
  }
@@ -6115,7 +6414,7 @@ function connectTunnel(options) {
6115
6414
  } = options;
6116
6415
  const tunnelUrl = getTunnelUrlConfig();
6117
6416
  const url = `${tunnelUrl}/tunnel/${agentId}/connect`;
6118
- return new Promise((resolve4, reject) => {
6417
+ return new Promise((resolve5, reject) => {
6119
6418
  const ws = new WebSocket2(url, {
6120
6419
  headers: {
6121
6420
  Authorization: authHeader,
@@ -6224,7 +6523,7 @@ function connectTunnel(options) {
6224
6523
  forwarder.setBinaryFramesSupported(message.binary_frames === true);
6225
6524
  const connectedAgentId = message.agent_id ?? agentId;
6226
6525
  onConnected?.(connectedAgentId);
6227
- resolve4({
6526
+ resolve5({
6228
6527
  ws,
6229
6528
  close: () => ws.close(1e3, "CLI shutdown")
6230
6529
  });
@@ -6779,12 +7078,12 @@ function createSessionStoreHealth(options = {}) {
6779
7078
  import { homedir as homedir6 } from "node:os";
6780
7079
 
6781
7080
  // src/lib/runner-file-sync.ts
6782
- import { join as join9 } from "node:path";
7081
+ import { join as join10 } from "node:path";
6783
7082
 
6784
7083
  // src/lib/file-push.ts
6785
7084
  import { randomUUID } from "node:crypto";
6786
7085
  import { chmod, mkdir, open as open3, realpath, rename, unlink } from "node:fs/promises";
6787
- import { basename, dirname as dirname5, isAbsolute, join as join8, relative, resolve as resolve2, sep } from "node:path";
7086
+ import { basename as basename2, dirname as dirname6, isAbsolute as isAbsolute2, join as join9, relative as relative2, resolve as resolve3, sep as sep2 } from "node:path";
6788
7087
  var FILE_MODE = 384;
6789
7088
  var DIRECTORY_MODE = 448;
6790
7089
  async function writePushedFile(request2) {
@@ -6815,9 +7114,9 @@ async function writePushedFile(request2) {
6815
7114
  }
6816
7115
  try {
6817
7116
  const { existingAncestor, missingSegments } = await resolveNearestExistingAncestor(
6818
- dirname5(candidate)
7117
+ dirname6(candidate)
6819
7118
  );
6820
- const realTarget = join8(existingAncestor, ...missingSegments, basename(candidate));
7119
+ const realTarget = join9(existingAncestor, ...missingSegments, basename2(candidate));
6821
7120
  const allowedDirectory = await findContainingAllowedDirectory(allowedDirectories, realTarget);
6822
7121
  if (allowedDirectory === null) {
6823
7122
  return refuse("path_not_allowed", "The runner does not allow writing to that location.", {
@@ -6827,8 +7126,8 @@ async function writePushedFile(request2) {
6827
7126
  }
6828
7127
  if (missingSegments.length > 0) {
6829
7128
  await createMissingDirectories(existingAncestor, missingSegments);
6830
- const realParent = await realpath(dirname5(realTarget));
6831
- if (realParent !== dirname5(realTarget) || !contains(allowedDirectory, realTarget)) {
7129
+ const realParent = await realpath(dirname6(realTarget));
7130
+ if (realParent !== dirname6(realTarget) || !contains(allowedDirectory, realTarget)) {
6832
7131
  return refuse("path_not_allowed", "The runner does not allow writing to that location.", {
6833
7132
  path: realTarget,
6834
7133
  bytes,
@@ -6853,15 +7152,15 @@ function expandAndValidate(requestedPath, homeDir) {
6853
7152
  if (requestedPath.trim() === "" || requestedPath.includes("\0")) {
6854
7153
  return null;
6855
7154
  }
6856
- const expanded = requestedPath === "~" ? homeDir : requestedPath.startsWith("~/") ? join8(homeDir, requestedPath.slice(2)) : requestedPath;
7155
+ const expanded = requestedPath === "~" ? homeDir : requestedPath.startsWith("~/") ? join9(homeDir, requestedPath.slice(2)) : requestedPath;
6857
7156
  if (expanded.split(/[/\\]/).includes("..")) {
6858
7157
  return null;
6859
7158
  }
6860
- if (!isAbsolute(expanded)) {
7159
+ if (!isAbsolute2(expanded)) {
6861
7160
  return null;
6862
7161
  }
6863
- const candidate = resolve2(expanded);
6864
- const name = basename(candidate);
7162
+ const candidate = resolve3(expanded);
7163
+ const name = basename2(candidate);
6865
7164
  return name === "" || name === "." || name === ".." ? null : candidate;
6866
7165
  }
6867
7166
  async function resolveNearestExistingAncestor(directory) {
@@ -6871,18 +7170,18 @@ async function resolveNearestExistingAncestor(directory) {
6871
7170
  try {
6872
7171
  return { existingAncestor: await realpath(current), missingSegments };
6873
7172
  } catch (err) {
6874
- const parent = dirname5(current);
7173
+ const parent = dirname6(current);
6875
7174
  if (err.code !== "ENOENT" || parent === current) {
6876
7175
  throw err;
6877
7176
  }
6878
- missingSegments.unshift(basename(current));
7177
+ missingSegments.unshift(basename2(current));
6879
7178
  current = parent;
6880
7179
  }
6881
7180
  }
6882
7181
  }
6883
7182
  async function findContainingAllowedDirectory(allowedDirectories, realTarget) {
6884
7183
  for (const directory of allowedDirectories) {
6885
- if (!isAbsolute(directory)) {
7184
+ if (!isAbsolute2(directory)) {
6886
7185
  log("warn", "file_push_allowed_directory_skipped", { directory, reason: "not_absolute" });
6887
7186
  continue;
6888
7187
  }
@@ -6920,19 +7219,19 @@ async function realpathCreatingIfMissing(directory) {
6920
7219
  }
6921
7220
  }
6922
7221
  function contains(realDirectory, realTarget) {
6923
- const rel = relative(realDirectory, realTarget);
6924
- return rel !== "" && rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute(rel);
7222
+ const rel = relative2(realDirectory, realTarget);
7223
+ return rel !== "" && rel !== ".." && !rel.startsWith(`..${sep2}`) && !isAbsolute2(rel);
6925
7224
  }
6926
7225
  async function createMissingDirectories(existingAncestor, missingSegments) {
6927
7226
  let current = existingAncestor;
6928
7227
  for (const segment of missingSegments) {
6929
- current = join8(current, segment);
7228
+ current = join9(current, segment);
6930
7229
  await mkdir(current, { recursive: true, mode: DIRECTORY_MODE });
6931
7230
  await chmod(current, DIRECTORY_MODE);
6932
7231
  }
6933
7232
  }
6934
7233
  async function writeAtomically(realTarget, content) {
6935
- const temporaryPath = join8(dirname5(realTarget), `.evident-push-${randomUUID()}.tmp`);
7234
+ const temporaryPath = join9(dirname6(realTarget), `.evident-push-${randomUUID()}.tmp`);
6936
7235
  let handle;
6937
7236
  try {
6938
7237
  handle = await open3(temporaryPath, "wx", FILE_MODE);
@@ -7062,12 +7361,12 @@ var NOT_APPLIED = {
7062
7361
  opencodeAuthApplied: false
7063
7362
  };
7064
7363
  function isClaudeCredentialPath(requestedPath, homeDir) {
7065
- const expanded = requestedPath === "~" ? homeDir : requestedPath.startsWith("~/") ? join9(homeDir, requestedPath.slice(2)) : requestedPath;
7066
- return expanded === join9(homeDir, ...CLAUDE_CREDENTIALS_SEGMENTS);
7364
+ const expanded = requestedPath === "~" ? homeDir : requestedPath.startsWith("~/") ? join10(homeDir, requestedPath.slice(2)) : requestedPath;
7365
+ return expanded === join10(homeDir, ...CLAUDE_CREDENTIALS_SEGMENTS);
7067
7366
  }
7068
7367
  function isOpenCodeAuthPath(requestedPath, homeDir) {
7069
- const expanded = requestedPath === "~" ? homeDir : requestedPath.startsWith("~/") ? join9(homeDir, requestedPath.slice(2)) : requestedPath;
7070
- return expanded === join9(homeDir, ...OPENCODE_AUTH_SEGMENTS);
7368
+ const expanded = requestedPath === "~" ? homeDir : requestedPath.startsWith("~/") ? join10(homeDir, requestedPath.slice(2)) : requestedPath;
7369
+ return expanded === join10(homeDir, ...OPENCODE_AUTH_SEGMENTS);
7071
7370
  }
7072
7371
  async function applyOne(options, file) {
7073
7372
  const label = `${file.id.slice(0, 8)} (${file.path})`;
@@ -7238,7 +7537,7 @@ var SESSION_ERROR_BUFFER_TTL_MS = SESSION_ERROR_STREAM_HEALTHY_MS;
7238
7537
  var MAX_BUFFERED_SESSION_ERRORS = 256;
7239
7538
  var SESSION_ERROR_RETENTION_TTL_MS = 6e4;
7240
7539
  var SESSION_STORE_UNWRITABLE_ERROR = "The runner session database could not be written, so this turn did not finish. Nothing was lost from the queue. Clear disk space and retry.";
7241
- var SESSION_ERROR_RETAINED_DETAIL = "The runner's session database could not be written, so this turn stopped part-way. Runner-observed session error: ";
7540
+ var SESSION_ERROR_RETAINED_DETAIL = "Runner-observed OpenCode session error: ";
7242
7541
  var SESSION_ERROR_OBSERVED_CAUSE = "Runner-observed OpenCode session-store cause: ";
7243
7542
  var DEFAULT_PAUSED_POLL_INTERVAL_MS = 2e3;
7244
7543
  var DEFAULT_PAUSED_MAX_WAIT_MS = 10 * 60 * 1e3;
@@ -9853,7 +10152,7 @@ var ChannelDriver = class _ChannelDriver {
9853
10152
  if (!inFlight.done) {
9854
10153
  const messageErrorText = messageError(messages, inFlight.opencodeMessageId);
9855
10154
  const retainedReason = this.readRetainedSessionError(sessionId);
9856
- const retainedError = retainedReason === null ? null : `${SESSION_ERROR_RETAINED_DETAIL}${retainedReason}`;
10155
+ const retainedError = retainedReason === null || messageErrorText !== null && messageErrorText.includes(retainedReason) ? null : `${SESSION_ERROR_RETAINED_DETAIL}${retainedReason}`;
9857
10156
  const observedCause = this.sessionStoreHealth?.readCause(sessionId);
9858
10157
  const causeError = observedCause === null || observedCause === void 0 ? null : `${SESSION_ERROR_OBSERVED_CAUSE}${observedCause}`;
9859
10158
  const storeError = this.sessionStoreHealth?.isUnwritable() ? SESSION_STORE_UNWRITABLE_ERROR : null;
@@ -11562,14 +11861,14 @@ var ChannelDriver = class _ChannelDriver {
11562
11861
  const fetchChildWithoutBlocking = async (sessionId) => {
11563
11862
  const pending = fetchChildMessages(sessionId);
11564
11863
  let timer;
11565
- const timeout = new Promise((resolve4) => {
11864
+ const timeout = new Promise((resolve5) => {
11566
11865
  timer = setTimeout(() => {
11567
11866
  this.log({
11568
11867
  level: "warn",
11569
11868
  message: `Best-effort subagent session fetch for message ${messageId.slice(0, 8)} and child session ${sessionId.slice(0, 8)} was slow \u2014 omitting invocation telemetry without delaying completion`,
11570
11869
  message_id: messageId
11571
11870
  });
11572
- resolve4(null);
11871
+ resolve5(null);
11573
11872
  }, SUBAGENT_TELEMETRY_FETCH_TIMEOUT_MS);
11574
11873
  });
11575
11874
  try {
@@ -12811,7 +13110,7 @@ function commandError2(result) {
12811
13110
  return result.stderr.trim() || `process exited with code ${result.code ?? "null"}`;
12812
13111
  }
12813
13112
  var runCommand2 = (command, args, opts) => {
12814
- return new Promise((resolve4) => {
13113
+ return new Promise((resolve5) => {
12815
13114
  let child;
12816
13115
  let stdout = "";
12817
13116
  let stderr = "";
@@ -12821,7 +13120,7 @@ var runCommand2 = (command, args, opts) => {
12821
13120
  if (settled) return;
12822
13121
  settled = true;
12823
13122
  if (timer.handle) clearTimeout(timer.handle);
12824
- resolve4(result);
13123
+ resolve5(result);
12825
13124
  };
12826
13125
  try {
12827
13126
  child = spawn5(command, args, { env: opts.env, stdio: ["ignore", "pipe", "pipe"] });
@@ -13077,7 +13376,7 @@ async function configureGitHubAccess({ env, log: log3 }) {
13077
13376
  // src/lib/opencode/config-overlay.ts
13078
13377
  import { execFileSync as execFileSync2 } from "node:child_process";
13079
13378
  import { copyFileSync, existsSync as existsSync2, statSync as statSync6 } from "node:fs";
13080
- import { isAbsolute as isAbsolute2, join as join10, resolve as resolve3 } from "node:path";
13379
+ import { isAbsolute as isAbsolute3, join as join11, resolve as resolve4 } from "node:path";
13081
13380
  function isFile(filePath) {
13082
13381
  return existsSync2(filePath) && statSync6(filePath).isFile();
13083
13382
  }
@@ -13090,8 +13389,8 @@ function applyRunnerOpenCodeConfig({
13090
13389
  log3("runner OpenCode config is not configured; using the baked project config", "debug");
13091
13390
  return;
13092
13391
  }
13093
- const source = isAbsolute2(overlayPath) ? overlayPath : resolve3(cwd, overlayPath);
13094
- const target = isFile(join10(cwd, "opencode.jsonc")) ? "opencode.jsonc" : "opencode.json";
13392
+ const source = isAbsolute3(overlayPath) ? overlayPath : resolve4(cwd, overlayPath);
13393
+ const target = isFile(join11(cwd, "opencode.jsonc")) ? "opencode.jsonc" : "opencode.json";
13095
13394
  if (!isFile(source)) {
13096
13395
  log3(
13097
13396
  `RUNNER-OPENCODE-CONFIG-MISSING: ${source} is not a file; headless turns will wedge on the first external-directory permission prompt (#563)`,
@@ -13099,7 +13398,7 @@ function applyRunnerOpenCodeConfig({
13099
13398
  );
13100
13399
  return;
13101
13400
  }
13102
- copyFileSync(source, join10(cwd, target));
13401
+ copyFileSync(source, join11(cwd, target));
13103
13402
  try {
13104
13403
  execFileSync2("git", ["-C", cwd, "update-index", "--skip-worktree", target], {
13105
13404
  stdio: "ignore"
@@ -13108,7 +13407,7 @@ function applyRunnerOpenCodeConfig({
13108
13407
  const detail = error2 instanceof Error ? error2.message : String(error2);
13109
13408
  log3(`could not mark ${target} skip-worktree: ${detail}; it may show as a local change`, "warn");
13110
13409
  }
13111
- log3(`Applied runner OpenCode config ${source} to ${join10(cwd, target)}`);
13410
+ log3(`Applied runner OpenCode config ${source} to ${join11(cwd, target)}`);
13112
13411
  }
13113
13412
 
13114
13413
  // src/lib/credential-sync.ts
@@ -13126,14 +13425,14 @@ function errorMessage2(error2) {
13126
13425
  return error2 instanceof Error ? error2.message : String(error2);
13127
13426
  }
13128
13427
  function waitForSettlement(promise, timeoutMs) {
13129
- return new Promise((resolve4) => {
13428
+ return new Promise((resolve5) => {
13130
13429
  let settled = false;
13131
13430
  const timer = setTimeout(() => finish(false), Math.max(0, timeoutMs));
13132
13431
  const finish = (value) => {
13133
13432
  if (settled) return;
13134
13433
  settled = true;
13135
13434
  clearTimeout(timer);
13136
- resolve4(value);
13435
+ resolve5(value);
13137
13436
  };
13138
13437
  promise.then(
13139
13438
  () => finish(true),
@@ -13384,8 +13683,8 @@ function resolveFileSyncDirectories(raw, homeDir) {
13384
13683
  if (trimmed === "") {
13385
13684
  throw new Error("--enable-file-sync-to requires a directory path (got an empty value)");
13386
13685
  }
13387
- const expanded = trimmed === "~" ? homeDir : trimmed.startsWith("~/") ? join11(homeDir, trimmed.slice(2)) : trimmed;
13388
- if (!isAbsolute3(expanded)) {
13686
+ const expanded = trimmed === "~" ? homeDir : trimmed.startsWith("~/") ? join12(homeDir, trimmed.slice(2)) : trimmed;
13687
+ if (!isAbsolute4(expanded)) {
13389
13688
  throw new Error(`--enable-file-sync-to requires an absolute directory path; got "${entry}"`);
13390
13689
  }
13391
13690
  const normalized = resolvePath2(expanded);
@@ -13485,8 +13784,7 @@ function resolveMaxActiveSessions(options, env = process.env) {
13485
13784
  function meetsThreshold(state, level) {
13486
13785
  return LOG_LEVELS[level] >= LOG_LEVELS[state.logLevel];
13487
13786
  }
13488
- function log2(state, message, level = "info") {
13489
- if (!meetsThreshold(state, level)) return;
13787
+ function writeConsoleLog(state, message, level) {
13490
13788
  if (state.json) {
13491
13789
  console.log(
13492
13790
  JSON.stringify({
@@ -13495,10 +13793,18 @@ function log2(state, message, level = "info") {
13495
13793
  message
13496
13794
  })
13497
13795
  );
13498
- } else if (!state.interactive) {
13499
- const prefix = level === "error" ? chalk7.red("\u2717") : level === "warn" ? chalk7.yellow("!") : level === "debug" ? chalk7.dim("\xB7") : chalk7.green("\u2022");
13500
- console.log(`${prefix} ${message}`);
13796
+ return;
13501
13797
  }
13798
+ const prefix = level === "error" ? chalk7.red("\u2717") : level === "warn" ? chalk7.yellow("!") : level === "debug" ? chalk7.dim("\xB7") : chalk7.green("\u2022");
13799
+ console.log(`${prefix} ${message}`);
13800
+ }
13801
+ function log2(state, message, level = "info") {
13802
+ if (!meetsThreshold(state, level)) return;
13803
+ if (state.json || !state.interactive) writeConsoleLog(state, message, level);
13804
+ }
13805
+ function logLocalOnly(state, message, level = "info") {
13806
+ if (!meetsThreshold(state, level)) return;
13807
+ writeConsoleLog(state, message, level);
13502
13808
  }
13503
13809
  function logActivity(state, entry) {
13504
13810
  const level = entry.level ?? (entry.type === "error" ? "error" : "info");
@@ -13800,7 +14106,7 @@ async function driveChannels(state, driver) {
13800
14106
  }
13801
14107
  }
13802
14108
  }
13803
- await new Promise((resolve4) => setTimeout(resolve4, CHANNEL_POLL_INTERVAL_MS));
14109
+ await new Promise((resolve5) => setTimeout(resolve5, CHANNEL_POLL_INTERVAL_MS));
13804
14110
  const cycleMs = performance.now() - cycleStartedAtMs;
13805
14111
  if (idleThisCycle) idleMs += cycleMs;
13806
14112
  if (unreachableThisCycle) unreachableMs += cycleMs;
@@ -13835,7 +14141,7 @@ function shouldWarnForReclaimSkip(reason) {
13835
14141
  return major > 22 || major === 22 && (minor > 13 || minor === 13 && patch >= 0);
13836
14142
  }
13837
14143
  function sessionDbPath() {
13838
- return join11(homedir7(), ".local", "share", "opencode", "opencode.db");
14144
+ return join12(homedir7(), ".local", "share", "opencode", "opencode.db");
13839
14145
  }
13840
14146
  function logSessionCleanupActivity(state, phase, entry) {
13841
14147
  logActivity(state, {
@@ -14515,8 +14821,8 @@ async function beginGracefulShutdown(state, trigger) {
14515
14821
  return true;
14516
14822
  }
14517
14823
  );
14518
- const timedOut = new Promise((resolve4) => {
14519
- timer = setTimeout(() => resolve4(false), telemetryBudgetMs);
14824
+ const timedOut = new Promise((resolve5) => {
14825
+ timer = setTimeout(() => resolve5(false), telemetryBudgetMs);
14520
14826
  });
14521
14827
  if (!await Promise.race([flushed, timedOut])) {
14522
14828
  log2(state, `Telemetry flush exceeded ${telemetryBudgetMs}ms \u2014 exiting anyway`, "warn");
@@ -14804,6 +15110,17 @@ async function run(options) {
14804
15110
  }
14805
15111
  if (githubTokenPopulated) await configureGitHubAccess(credentialContext);
14806
15112
  state.credentialSync?.arm();
15113
+ const dependencyInstallDiagnosticReporter = createDependencyInstallDiagnosticReporter({
15114
+ context: {
15115
+ homeDir: homedir7(),
15116
+ cwd: process.cwd(),
15117
+ microvmId: process.env.MICROVM_ID?.trim(),
15118
+ euid: process.getuid?.(),
15119
+ egid: process.getgid?.()
15120
+ },
15121
+ emitEvent: (message) => logActivity(state, { type: "info", level: "warn", message }),
15122
+ emitLocal: (message) => logLocalOnly(state, message, "warn")
15123
+ });
14807
15124
  const openCodeActivityForwarder = createOpenCodeActivityForwarder(
14808
15125
  () => ({
14809
15126
  agentId: state.agentId,
@@ -14814,7 +15131,8 @@ async function run(options) {
14814
15131
  if (cause !== null && sessionId !== null) {
14815
15132
  state.sessionStoreHealth.observeCause(sessionId, cause);
14816
15133
  }
14817
- }
15134
+ },
15135
+ dependencyInstallDiagnosticReporter
14818
15136
  );
14819
15137
  const openCodeLogTail = tailOpenCodeLogFile(
14820
15138
  resolveOpenCodeLogPath(homedir7(), process.env),