@evident-ai/cli 3.5.2-dev.fbf25b4 → 3.5.2-dev.ffa823b

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.fbf25b4" : void 0) ?? process.env.npm_package_version ?? "unknown";
1325
+ var CLI_VERSION = (true ? "3.5.2-dev.ffa823b" : void 0) ?? process.env.npm_package_version ?? "unknown";
1326
1326
  function getCliVersion() {
1327
1327
  return CLI_VERSION;
1328
1328
  }
@@ -1515,9 +1515,10 @@ function redactDiagnosticText(message) {
1515
1515
  }
1516
1516
 
1517
1517
  // src/lib/opencode/session-db-reclaim.ts
1518
- var SESSION_DB_RECLAIM_CHUNK_PAGES = 2e3;
1518
+ var SESSION_DB_RECLAIM_CHUNK_PAGES = 100;
1519
1519
  var SESSION_DB_RECLAIM_TIME_BUDGET_MS = 500;
1520
1520
  var SESSION_DB_RECLAIM_PAGE_CEILING = 4e4;
1521
+ var SESSION_DB_RECLAIM_BUSY_TIMEOUT_MS = 5e3;
1521
1522
  function errorMessage(error2) {
1522
1523
  if (!(error2 instanceof Error)) return String(error2);
1523
1524
  return error2.cause instanceof Error ? error2.cause.message : error2.message;
@@ -1642,6 +1643,7 @@ function readFreelistCount(db) {
1642
1643
  return db.prepare("PRAGMA freelist_count").get().freelist_count;
1643
1644
  }
1644
1645
  function readCheckpointResult(db) {
1646
+ db.exec("PRAGMA busy_timeout=0");
1645
1647
  const row = db.prepare("PRAGMA wal_checkpoint(TRUNCATE)").get();
1646
1648
  return { busy: row.busy !== 0, log: row.log, checkpointed: row.checkpointed };
1647
1649
  }
@@ -1659,7 +1661,7 @@ async function probeReclaimAvailability(input) {
1659
1661
  try {
1660
1662
  const db = new sqlite.DatabaseSync(dbPath, { readOnly: true });
1661
1663
  try {
1662
- db.exec("PRAGMA busy_timeout=5000");
1664
+ db.exec(`PRAGMA busy_timeout=${SESSION_DB_RECLAIM_BUSY_TIMEOUT_MS}`);
1663
1665
  autoVacuum = db.prepare("PRAGMA auto_vacuum").get().auto_vacuum;
1664
1666
  } finally {
1665
1667
  db.close();
@@ -1679,7 +1681,8 @@ async function reclaimSessionDbSpace(input) {
1679
1681
  timeBudgetMs = SESSION_DB_RECLAIM_TIME_BUDGET_MS,
1680
1682
  chunkPages = SESSION_DB_RECLAIM_CHUNK_PAGES,
1681
1683
  now = () => performance.now(),
1682
- allowFullVacuum = true
1684
+ hasLiveTurn = false,
1685
+ hasProvenanceAnomaly = false
1683
1686
  } = input;
1684
1687
  let sqlite;
1685
1688
  try {
@@ -1695,17 +1698,19 @@ async function reclaimSessionDbSpace(input) {
1695
1698
  let db;
1696
1699
  try {
1697
1700
  db = new DatabaseSync(dbPath);
1698
- db.exec("PRAGMA busy_timeout=5000");
1701
+ db.exec(`PRAGMA busy_timeout=${SESSION_DB_RECLAIM_BUSY_TIMEOUT_MS}`);
1699
1702
  const autoVacuum = db.prepare("PRAGMA auto_vacuum").get().auto_vacuum;
1700
1703
  if (autoVacuum === 0) {
1701
- if (!allowFullVacuum) {
1702
- console.warn(
1703
- `[reclaimSessionDbSpace] skipping VACUUM conversion of ${dbPath}: a session turn is live`
1704
- );
1704
+ if (hasLiveTurn || hasProvenanceAnomaly) {
1705
+ const detail = [
1706
+ ...hasLiveTurn ? ["a session turn is live"] : [],
1707
+ ...hasProvenanceAnomaly ? ["session-DB provenance is anomalous"] : []
1708
+ ].join(" and ");
1709
+ console.warn(`[reclaimSessionDbSpace] skipping VACUUM conversion of ${dbPath}: ${detail}`);
1705
1710
  return {
1706
1711
  ok: false,
1707
1712
  skipped: "full-vacuum-blocked",
1708
- detail: "a session turn is live"
1713
+ detail
1709
1714
  };
1710
1715
  }
1711
1716
  const fileBytesForGuard = statSync2(dbPath).size;
@@ -1723,7 +1728,9 @@ async function reclaimSessionDbSpace(input) {
1723
1728
  db.exec("VACUUM");
1724
1729
  const afterBytes = readLogicalBytes(db);
1725
1730
  const freelistAfter = readFreelistCount(db);
1731
+ const checkpointStartedAt = now();
1726
1732
  const checkpoint = readCheckpointResult(db);
1733
+ const checkpointElapsedMs = Math.max(0, now() - checkpointStartedAt);
1727
1734
  return {
1728
1735
  ok: true,
1729
1736
  mode: "convert",
@@ -1734,10 +1741,18 @@ async function reclaimSessionDbSpace(input) {
1734
1741
  pagesReclaimed: Math.max(0, freelistBefore - freelistAfter),
1735
1742
  stoppedBy: "freelist-drained",
1736
1743
  elapsedMs: Math.max(0, now() - startedAt),
1744
+ checkpointElapsedMs,
1737
1745
  checkpoint
1738
1746
  };
1739
1747
  }
1740
1748
  if (autoVacuum === 2) {
1749
+ if (hasLiveTurn) {
1750
+ const detail = "a session turn is live";
1751
+ console.warn(
1752
+ `[reclaimSessionDbSpace] skipping incremental reclaim of ${dbPath}: ${detail}`
1753
+ );
1754
+ return { ok: false, skipped: "live-turn", detail };
1755
+ }
1741
1756
  const beforeBytes = readLogicalBytes(db);
1742
1757
  const freelistBefore = readFreelistCount(db);
1743
1758
  const startedAt = now();
@@ -1762,7 +1777,9 @@ async function reclaimSessionDbSpace(input) {
1762
1777
  freelistAfter = readFreelistCount(db);
1763
1778
  }
1764
1779
  const afterBytes = readLogicalBytes(db);
1780
+ const checkpointStartedAt = now();
1765
1781
  const checkpoint = readCheckpointResult(db);
1782
+ const checkpointElapsedMs = Math.max(0, now() - checkpointStartedAt);
1766
1783
  return {
1767
1784
  ok: true,
1768
1785
  mode: "incremental",
@@ -1773,6 +1790,7 @@ async function reclaimSessionDbSpace(input) {
1773
1790
  pagesReclaimed: Math.max(0, freelistBefore - freelistAfter),
1774
1791
  stoppedBy,
1775
1792
  elapsedMs: Math.max(0, now() - startedAt),
1793
+ checkpointElapsedMs,
1776
1794
  checkpoint
1777
1795
  };
1778
1796
  }
@@ -1792,6 +1810,263 @@ async function reclaimSessionDbSpace(input) {
1792
1810
  }
1793
1811
  }
1794
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
+
1795
2070
  // src/lib/runner-activity-telemetry.ts
1796
2071
  var FORWARDED_LEVELS_BY_SOURCE = {
1797
2072
  "cli.run": /* @__PURE__ */ new Set(["warn", "error"]),
@@ -1823,7 +2098,17 @@ function parseOpenCodeLogLine(line) {
1823
2098
  }
1824
2099
  var MAX_LINE_BUFFER_BYTES = 16 * 1024;
1825
2100
  var REPEAT_SUPPRESSION_WINDOW_MS = 6e4;
1826
- 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) {
1827
2112
  let buffer = Buffer.alloc(0);
1828
2113
  const repeatStates = /* @__PURE__ */ new Map();
1829
2114
  const forwardLine = (line, parsed, repeatsSuppressed = 0) => {
@@ -1835,7 +2120,7 @@ function createOpenCodeActivityForwarder(getContext, onSessionStoreWriteFailure)
1835
2120
  forwardRunnerActivity(
1836
2121
  {
1837
2122
  level: parsed.level,
1838
- error: line,
2123
+ error: parsed.message === DEPENDENCY_INSTALL_FAILURE_MESSAGE ? sanitiseDependencyInstallWarning(line) : line,
1839
2124
  metadata: Object.keys(metadata).length > 0 ? metadata : void 0,
1840
2125
  source: "opencode"
1841
2126
  },
@@ -1853,24 +2138,7 @@ function createOpenCodeActivityForwarder(getContext, onSessionStoreWriteFailure)
1853
2138
  repeatStates.delete(key);
1854
2139
  }
1855
2140
  };
1856
- const flushLine = (line) => {
1857
- const parsed = parseOpenCodeLogLine(line);
1858
- const isDiskFailure = isSessionStoreWriteFailure(line);
1859
- const cause = sessionStoreCauseToken(line);
1860
- if (onSessionStoreWriteFailure && (isDiskFailure || cause !== null)) {
1861
- try {
1862
- onSessionStoreWriteFailure({
1863
- isDiskFailure,
1864
- sessionId: parsed?.sessionID ?? null,
1865
- cause
1866
- });
1867
- } catch (err) {
1868
- reportTelemetryDiagnostic(
1869
- `Runner activity telemetry could not record session-store write failure: ${err instanceof Error ? err.message : String(err)}`
1870
- );
1871
- }
1872
- }
1873
- if (!parsed) return;
2141
+ const forwardWithRepeatSuppression = (line, parsed) => {
1874
2142
  const now = Date.now();
1875
2143
  const key = parsed.message === void 0 ? void 0 : `${parsed.level}\0${parsed.message}\0${parsed.sessionID ?? ""}\0${parsed.messageID ?? ""}`;
1876
2144
  expireRepeatStates(now, key);
@@ -1893,6 +2161,35 @@ function createOpenCodeActivityForwarder(getContext, onSessionStoreWriteFailure)
1893
2161
  previous.parsed = parsed;
1894
2162
  previous.repeatsSuppressed++;
1895
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
+ };
1896
2193
  const forward = ((chunk) => {
1897
2194
  buffer = Buffer.concat([buffer, Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, "utf-8")]);
1898
2195
  let newlineIndex;
@@ -1932,6 +2229,23 @@ function truncate(message) {
1932
2229
  const remaining = MAX_MESSAGE_LENGTH - marker.length;
1933
2230
  const headLength = Math.ceil(remaining * 0.6);
1934
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
+ }
1935
2249
  return message.slice(0, headLength) + marker + message.slice(message.length - tailLength);
1936
2250
  }
1937
2251
  function sanitiseMetadata(metadata) {
@@ -2009,10 +2323,10 @@ function forwardRunnerActivity(entry, context) {
2009
2323
 
2010
2324
  // src/lib/opencode/session-db-recovery-report.ts
2011
2325
  import { readFileSync as readFileSync2, unlinkSync } from "node:fs";
2012
- import { join as join2 } from "node:path";
2326
+ import { join as join3 } from "node:path";
2013
2327
  function sessionDbRecoveryReportPath(homeDir, env) {
2014
2328
  const override = env.EVIDENT_SESSION_DB_RECOVERY_REPORT?.trim();
2015
- return override || join2(homeDir, ".local", "state", "evident", "session-db-recovery.jsonl");
2329
+ return override || join3(homeDir, ".local", "state", "evident", "session-db-recovery.jsonl");
2016
2330
  }
2017
2331
  function drainSessionDbRecoveryReport({
2018
2332
  homeDir,
@@ -2285,7 +2599,7 @@ async function waitForOpenCodeHealth(port, timeoutMs = 3e4) {
2285
2599
  if (health.healthy || health.authFailed) {
2286
2600
  return health;
2287
2601
  }
2288
- await new Promise((resolve4) => setTimeout(resolve4, 1e3));
2602
+ await new Promise((resolve5) => setTimeout(resolve5, 1e3));
2289
2603
  }
2290
2604
  return { healthy: false, error: "Timeout waiting for OpenCode to be healthy" };
2291
2605
  }
@@ -2296,7 +2610,7 @@ async function waitForOpenCode2Health(port, password, timeoutMs = 3e4) {
2296
2610
  if (health.healthy || health.authFailed) {
2297
2611
  return health;
2298
2612
  }
2299
- await new Promise((resolve4) => setTimeout(resolve4, 1e3));
2613
+ await new Promise((resolve5) => setTimeout(resolve5, 1e3));
2300
2614
  }
2301
2615
  return { healthy: false, error: "Timeout waiting for OpenCode to be healthy" };
2302
2616
  }
@@ -2563,7 +2877,18 @@ async function deleteSession(port, id, client) {
2563
2877
  { method: "DELETE" },
2564
2878
  { allowStatuses: ALL_HTTP_STATUSES }
2565
2879
  );
2566
- return { ok: res.status >= 200 && res.status < 300, status: res.status };
2880
+ if (res.status >= 200 && res.status < 300) {
2881
+ return { ok: true, status: res.status };
2882
+ }
2883
+ try {
2884
+ const body = await res.text();
2885
+ return body ? { ok: false, status: res.status, body } : { ok: false, status: res.status };
2886
+ } catch (error2) {
2887
+ console.error(
2888
+ `[deleteSession] DELETE /session/${id} response body read failed for HTTP ${res.status}: ${error2 instanceof Error ? error2.message : String(error2)}`
2889
+ );
2890
+ return { ok: false, status: res.status };
2891
+ }
2567
2892
  } catch (error2) {
2568
2893
  console.error(
2569
2894
  `[deleteSession] DELETE /session/${id} failed: ${error2 instanceof Error ? error2.message : String(error2)}`
@@ -2875,7 +3200,7 @@ async function sendPromptAsync(port, sessionId, content, options, attachments, c
2875
3200
  return dispatchedUserMessageId;
2876
3201
  }
2877
3202
  if (attempt < READ_BACK_ATTEMPTS - 1) {
2878
- await new Promise((resolve4) => setTimeout(resolve4, READ_BACK_DELAY_MS));
3203
+ await new Promise((resolve5) => setTimeout(resolve5, READ_BACK_DELAY_MS));
2879
3204
  }
2880
3205
  }
2881
3206
  return null;
@@ -3047,9 +3372,11 @@ function sumAssistantUsage(messages) {
3047
3372
  let cacheWriteSum = 0;
3048
3373
  let costSum = 0;
3049
3374
  let sawCost = false;
3375
+ let toolCallCount = 0;
3050
3376
  let modelId = null;
3051
3377
  let providerId = null;
3052
3378
  for (const m of selected) {
3379
+ toolCallCount += m.parts?.filter((part) => part.type === "tool").length ?? 0;
3053
3380
  const info = m.info;
3054
3381
  if (!info) continue;
3055
3382
  const tokens = info.tokens;
@@ -3086,7 +3413,8 @@ function sumAssistantUsage(messages) {
3086
3413
  usage_tokens_cache_write: cacheWriteSum,
3087
3414
  // NULL means OpenCode never reported a cost; it is distinct from a genuine
3088
3415
  // zero-cost message, which sets `sawCost` with `costSum === 0`.
3089
- usage_cost_usd: sawCost ? costSum : null
3416
+ usage_cost_usd: sawCost ? costSum : null,
3417
+ turn_tool_calls: toolCallCount
3090
3418
  };
3091
3419
  }
3092
3420
  function messageUsage(messages, userMessageId) {
@@ -3748,7 +4076,18 @@ async function deleteV2Session(client, sessionId) {
3748
4076
  { method: "DELETE" },
3749
4077
  { allowStatuses: Array.from({ length: 500 }, (_, index) => index + 100) }
3750
4078
  );
3751
- return { ok: response.status >= 200 && response.status < 300, status: response.status };
4079
+ if (response.status >= 200 && response.status < 300) {
4080
+ return { ok: true, status: response.status };
4081
+ }
4082
+ try {
4083
+ const body = await response.text();
4084
+ return body ? { ok: false, status: response.status, body } : { ok: false, status: response.status };
4085
+ } catch (error2) {
4086
+ console.error(
4087
+ `[deleteV2Session] DELETE /api/session/${sessionId} response body read failed for HTTP ${response.status}: ${error2 instanceof Error ? error2.message : String(error2)}`
4088
+ );
4089
+ return { ok: false, status: response.status };
4090
+ }
3752
4091
  } catch (error2) {
3753
4092
  console.error(
3754
4093
  `[deleteV2Session] DELETE /api/session/${sessionId} failed: ${error2 instanceof Error ? error2.message : String(error2)}`
@@ -3924,12 +4263,12 @@ async function readV2SessionErrorStream(client, options) {
3924
4263
 
3925
4264
  // src/lib/opencode/credential-import-v2.ts
3926
4265
  import { readFileSync as readFileSync4 } from "node:fs";
3927
- import { join as join4 } from "node:path";
4266
+ import { join as join5 } from "node:path";
3928
4267
 
3929
4268
  // src/lib/openai-usage.ts
3930
4269
  import { readFileSync as readFileSync3 } from "node:fs";
3931
4270
  import { homedir as homedir3 } from "node:os";
3932
- import { join as join3 } from "node:path";
4271
+ import { join as join4 } from "node:path";
3933
4272
  var CODEX_RESPONSES_URL = "https://chatgpt.com/backend-api/codex/responses";
3934
4273
  var OPENCODE_AUTH_SEGMENTS = [".local", "share", "opencode", "auth.json"];
3935
4274
  var OpenAiUsageError = class extends Error {
@@ -3943,7 +4282,7 @@ function isLocalCredentialProblem2(err) {
3943
4282
  }
3944
4283
  function readOpenCodeChatGptCredentials() {
3945
4284
  try {
3946
- const raw = readFileSync3(join3(homedir3(), ...OPENCODE_AUTH_SEGMENTS), "utf-8");
4285
+ const raw = readFileSync3(join4(homedir3(), ...OPENCODE_AUTH_SEGMENTS), "utf-8");
3947
4286
  let parsed;
3948
4287
  try {
3949
4288
  parsed = JSON.parse(raw);
@@ -4169,13 +4508,13 @@ var INTEGRATION_LIST_RETRY_DELAY_MS = 1e3;
4169
4508
  function isRecord3(value) {
4170
4509
  return typeof value === "object" && value !== null && !Array.isArray(value);
4171
4510
  }
4172
- var sleep2 = (milliseconds) => new Promise((resolve4) => setTimeout(resolve4, milliseconds));
4511
+ var sleep2 = (milliseconds) => new Promise((resolve5) => setTimeout(resolve5, milliseconds));
4173
4512
  function emptyResult(unsupported = [], outcome = "completed") {
4174
4513
  return { outcome, imported: [], alreadyConnected: [], unsupported, failed: [] };
4175
4514
  }
4176
4515
  function readOpenCodeCredentials(homeDir, log3) {
4177
4516
  try {
4178
- const raw = readFileSync4(join4(homeDir, ...OPENCODE_AUTH_SEGMENTS), "utf8");
4517
+ const raw = readFileSync4(join5(homeDir, ...OPENCODE_AUTH_SEGMENTS), "utf8");
4179
4518
  let parsed;
4180
4519
  try {
4181
4520
  parsed = JSON.parse(raw);
@@ -4377,7 +4716,7 @@ async function importOpenCodeV2Credentials({
4377
4716
  import { spawn as spawn2 } from "node:child_process";
4378
4717
  import { mkdirSync, statSync as statSync3, unlinkSync as unlinkSync2, writeFileSync } from "node:fs";
4379
4718
  import { homedir as homedir4 } from "node:os";
4380
- import { dirname as dirname3, resolve as resolvePath } from "node:path";
4719
+ import { dirname as dirname4, resolve as resolvePath } from "node:path";
4381
4720
 
4382
4721
  // src/lib/runner-synchroniser.ts
4383
4722
  import { spawn } from "node:child_process";
@@ -4387,7 +4726,7 @@ function appendError(stderr, error2) {
4387
4726
  ${message}`;
4388
4727
  }
4389
4728
  function runSynchroniser(args, opts) {
4390
- return new Promise((resolve4) => {
4729
+ return new Promise((resolve5) => {
4391
4730
  let child;
4392
4731
  let stdout = "";
4393
4732
  let stderr = "";
@@ -4401,7 +4740,7 @@ function runSynchroniser(args, opts) {
4401
4740
  if (timer.handle) clearTimeout(timer.handle);
4402
4741
  if (abortListener) opts.signal?.removeEventListener("abort", abortListener);
4403
4742
  if (spawnListener) child.removeListener("spawn", spawnListener);
4404
- resolve4(result);
4743
+ resolve5(result);
4405
4744
  };
4406
4745
  try {
4407
4746
  child = spawn("runner-synchroniser", args, {
@@ -4505,7 +4844,7 @@ function clearMarker(options) {
4505
4844
  function markNoReplicate(options, message) {
4506
4845
  if (options.noReplicateMarker) {
4507
4846
  try {
4508
- mkdirSync(dirname3(options.noReplicateMarker), { recursive: true });
4847
+ mkdirSync(dirname4(options.noReplicateMarker), { recursive: true });
4509
4848
  writeFileSync(options.noReplicateMarker, "");
4510
4849
  } catch (error2) {
4511
4850
  options.log(
@@ -4564,7 +4903,7 @@ function parseSynchroniserEnv(stdout) {
4564
4903
  return values;
4565
4904
  }
4566
4905
  function runCommand(command, args, options) {
4567
- return new Promise((resolve4) => {
4906
+ return new Promise((resolve5) => {
4568
4907
  let child;
4569
4908
  let stdout = "";
4570
4909
  let stderr = "";
@@ -4573,7 +4912,7 @@ function runCommand(command, args, options) {
4573
4912
  if (settled) return;
4574
4913
  settled = true;
4575
4914
  if (timer) clearTimeout(timer);
4576
- resolve4(result);
4915
+ resolve5(result);
4577
4916
  };
4578
4917
  try {
4579
4918
  child = spawn2(command, args, {
@@ -4581,7 +4920,7 @@ function runCommand(command, args, options) {
4581
4920
  stdio: ["ignore", "pipe", "pipe"]
4582
4921
  });
4583
4922
  } catch (error2) {
4584
- resolve4({
4923
+ resolve5({
4585
4924
  code: null,
4586
4925
  stdout,
4587
4926
  stderr: error2 instanceof Error ? error2.message : String(error2),
@@ -4660,7 +4999,7 @@ async function ensureLitestreamConfig(options, env) {
4660
4999
  return null;
4661
5000
  }
4662
5001
  try {
4663
- mkdirSync(dirname3(configPath), { recursive: true });
5002
+ mkdirSync(dirname4(configPath), { recursive: true });
4664
5003
  writeFileSync(configPath, rendered.stdout);
4665
5004
  } catch (error2) {
4666
5005
  options.log(
@@ -4927,7 +5266,7 @@ async function restoreAndVerifySessionDb(options) {
4927
5266
  // src/lib/opencode/session-db-provenance.ts
4928
5267
  import { createRequire } from "node:module";
4929
5268
  import { mkdirSync as mkdirSync2, readFileSync as readFileSync5, writeFileSync as writeFileSync2 } from "node:fs";
4930
- import { dirname as dirname4, join as join5 } from "node:path";
5269
+ import { dirname as dirname5, join as join6 } from "node:path";
4931
5270
  var require2 = createRequire(import.meta.url);
4932
5271
  function readSessionDbMigrationIds(dbPath) {
4933
5272
  let db;
@@ -4964,7 +5303,7 @@ function readSessionDbMigrationIds(dbPath) {
4964
5303
  }
4965
5304
  function sessionDbProvenanceStatePath(homeDir, env) {
4966
5305
  const override = env.EVIDENT_SESSION_DB_PROVENANCE_STATE?.trim();
4967
- return override || join5(homeDir, ".local", "state", "evident", "session-db-provenance.json");
5306
+ return override || join6(homeDir, ".local", "state", "evident", "session-db-provenance.json");
4968
5307
  }
4969
5308
  function loadSessionDbProvenanceState(path) {
4970
5309
  let value;
@@ -4993,7 +5332,7 @@ function loadSessionDbProvenanceState(path) {
4993
5332
  }
4994
5333
  function saveSessionDbProvenanceState(path, state) {
4995
5334
  try {
4996
- mkdirSync2(dirname4(path), { recursive: true });
5335
+ mkdirSync2(dirname5(path), { recursive: true });
4997
5336
  writeFileSync2(path, `${JSON.stringify(state, null, 2)}
4998
5337
  `, "utf8");
4999
5338
  } catch (error2) {
@@ -5084,7 +5423,7 @@ async function stopProcessAndWait(child, timeoutMs, sendTerm, sendKill) {
5084
5423
  if (child.exitCode !== null || child.signalCode !== null) {
5085
5424
  return { outcome: "exited", code: child.exitCode, signal: child.signalCode };
5086
5425
  }
5087
- return new Promise((resolve4, reject) => {
5426
+ return new Promise((resolve5, reject) => {
5088
5427
  let forced = false;
5089
5428
  let settled = false;
5090
5429
  const timer = setTimeout(() => {
@@ -5104,7 +5443,7 @@ async function stopProcessAndWait(child, timeoutMs, sendTerm, sendKill) {
5104
5443
  settled = true;
5105
5444
  clearTimeout(timer);
5106
5445
  child.removeListener("exit", onExit);
5107
- resolve4(result);
5446
+ resolve5(result);
5108
5447
  };
5109
5448
  const fail = (error2) => {
5110
5449
  if (settled) return;
@@ -5580,7 +5919,7 @@ function selectSessionsToDelete(sessions, opts) {
5580
5919
  );
5581
5920
  }
5582
5921
  var DEFAULT_INTERVAL = "1h";
5583
- function resolve(flag, envValue, fallback) {
5922
+ function resolve2(flag, envValue, fallback) {
5584
5923
  return flag ?? envValue ?? fallback;
5585
5924
  }
5586
5925
  function parseMaxCount(input) {
@@ -5596,9 +5935,9 @@ function parseMaxCount(input) {
5596
5935
  }
5597
5936
  function resolveSessionCleanupConfig(flags, env = process.env) {
5598
5937
  const warnings = [];
5599
- const maxAgeRaw = resolve(flags.maxAge, env.EVIDENT_SESSION_CLEANUP_MAX_AGE);
5600
- const maxCountRaw = resolve(flags.maxCount, env.EVIDENT_SESSION_CLEANUP_MAX_COUNT);
5601
- 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(
5602
5941
  flags.interval,
5603
5942
  env.EVIDENT_SESSION_CLEANUP_INTERVAL,
5604
5943
  DEFAULT_INTERVAL
@@ -5638,10 +5977,10 @@ function resolveSessionCleanupConfig(flags, env = process.env) {
5638
5977
 
5639
5978
  // src/lib/opencode/session-db-size.ts
5640
5979
  import { statSync as statSync4 } from "node:fs";
5641
- import { join as join6 } from "node:path";
5980
+ import { join as join7 } from "node:path";
5642
5981
  var LARGE_DB_THRESHOLD_BYTES = 268435456;
5643
5982
  function statSessionDbBytes(homeDir) {
5644
- const dbPath = join6(homeDir, ".local", "share", "opencode", "opencode.db");
5983
+ const dbPath = join7(homeDir, ".local", "share", "opencode", "opencode.db");
5645
5984
  try {
5646
5985
  return statSync4(dbPath).size;
5647
5986
  } catch (err) {
@@ -5671,12 +6010,12 @@ function buildSessionStoreSizeWarning(input) {
5671
6010
  // src/lib/opencode/log-tail.ts
5672
6011
  import { statSync as statSync5 } from "node:fs";
5673
6012
  import { homedir as homedir5 } from "node:os";
5674
- import { join as join7 } from "node:path";
6013
+ import { join as join8 } from "node:path";
5675
6014
  import { open as open2, stat } from "node:fs/promises";
5676
6015
  var DEFAULT_POLL_INTERVAL_MS = 1e3;
5677
6016
  function resolveOpenCodeLogPath(homeDir = homedir5(), env = process.env) {
5678
- const dataDir = env.XDG_DATA_HOME || join7(homeDir, ".local", "share");
5679
- 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");
5680
6019
  }
5681
6020
  function isEnoent(error2) {
5682
6021
  return error2?.code === "ENOENT";
@@ -5831,8 +6170,8 @@ var StreamForwarder = class {
5831
6170
  pauseStream(sid, stream) {
5832
6171
  if (!stream.paused) {
5833
6172
  stream.paused = true;
5834
- stream.pausePromise = new Promise((resolve4) => {
5835
- stream.resume = resolve4;
6173
+ stream.pausePromise = new Promise((resolve5) => {
6174
+ stream.resume = resolve5;
5836
6175
  });
5837
6176
  }
5838
6177
  if (stream.pauseTimer !== null) clearTimeout(stream.pauseTimer);
@@ -5899,12 +6238,12 @@ var StreamForwarder = class {
5899
6238
  let endBody;
5900
6239
  if (has_body) {
5901
6240
  const chunks = [];
5902
- bodyPromise = new Promise((resolve4) => {
6241
+ bodyPromise = new Promise((resolve5) => {
5903
6242
  pushBody = (buf) => {
5904
6243
  chunks.push(buf);
5905
6244
  };
5906
6245
  endBody = () => {
5907
- resolve4(Buffer.concat(chunks));
6246
+ resolve5(Buffer.concat(chunks));
5908
6247
  };
5909
6248
  });
5910
6249
  }
@@ -6075,7 +6414,7 @@ function connectTunnel(options) {
6075
6414
  } = options;
6076
6415
  const tunnelUrl = getTunnelUrlConfig();
6077
6416
  const url = `${tunnelUrl}/tunnel/${agentId}/connect`;
6078
- return new Promise((resolve4, reject) => {
6417
+ return new Promise((resolve5, reject) => {
6079
6418
  const ws = new WebSocket2(url, {
6080
6419
  headers: {
6081
6420
  Authorization: authHeader,
@@ -6184,7 +6523,7 @@ function connectTunnel(options) {
6184
6523
  forwarder.setBinaryFramesSupported(message.binary_frames === true);
6185
6524
  const connectedAgentId = message.agent_id ?? agentId;
6186
6525
  onConnected?.(connectedAgentId);
6187
- resolve4({
6526
+ resolve5({
6188
6527
  ws,
6189
6528
  close: () => ws.close(1e3, "CLI shutdown")
6190
6529
  });
@@ -6739,12 +7078,12 @@ function createSessionStoreHealth(options = {}) {
6739
7078
  import { homedir as homedir6 } from "node:os";
6740
7079
 
6741
7080
  // src/lib/runner-file-sync.ts
6742
- import { join as join9 } from "node:path";
7081
+ import { join as join10 } from "node:path";
6743
7082
 
6744
7083
  // src/lib/file-push.ts
6745
7084
  import { randomUUID } from "node:crypto";
6746
7085
  import { chmod, mkdir, open as open3, realpath, rename, unlink } from "node:fs/promises";
6747
- 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";
6748
7087
  var FILE_MODE = 384;
6749
7088
  var DIRECTORY_MODE = 448;
6750
7089
  async function writePushedFile(request2) {
@@ -6775,9 +7114,9 @@ async function writePushedFile(request2) {
6775
7114
  }
6776
7115
  try {
6777
7116
  const { existingAncestor, missingSegments } = await resolveNearestExistingAncestor(
6778
- dirname5(candidate)
7117
+ dirname6(candidate)
6779
7118
  );
6780
- const realTarget = join8(existingAncestor, ...missingSegments, basename(candidate));
7119
+ const realTarget = join9(existingAncestor, ...missingSegments, basename2(candidate));
6781
7120
  const allowedDirectory = await findContainingAllowedDirectory(allowedDirectories, realTarget);
6782
7121
  if (allowedDirectory === null) {
6783
7122
  return refuse("path_not_allowed", "The runner does not allow writing to that location.", {
@@ -6787,8 +7126,8 @@ async function writePushedFile(request2) {
6787
7126
  }
6788
7127
  if (missingSegments.length > 0) {
6789
7128
  await createMissingDirectories(existingAncestor, missingSegments);
6790
- const realParent = await realpath(dirname5(realTarget));
6791
- if (realParent !== dirname5(realTarget) || !contains(allowedDirectory, realTarget)) {
7129
+ const realParent = await realpath(dirname6(realTarget));
7130
+ if (realParent !== dirname6(realTarget) || !contains(allowedDirectory, realTarget)) {
6792
7131
  return refuse("path_not_allowed", "The runner does not allow writing to that location.", {
6793
7132
  path: realTarget,
6794
7133
  bytes,
@@ -6813,15 +7152,15 @@ function expandAndValidate(requestedPath, homeDir) {
6813
7152
  if (requestedPath.trim() === "" || requestedPath.includes("\0")) {
6814
7153
  return null;
6815
7154
  }
6816
- 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;
6817
7156
  if (expanded.split(/[/\\]/).includes("..")) {
6818
7157
  return null;
6819
7158
  }
6820
- if (!isAbsolute(expanded)) {
7159
+ if (!isAbsolute2(expanded)) {
6821
7160
  return null;
6822
7161
  }
6823
- const candidate = resolve2(expanded);
6824
- const name = basename(candidate);
7162
+ const candidate = resolve3(expanded);
7163
+ const name = basename2(candidate);
6825
7164
  return name === "" || name === "." || name === ".." ? null : candidate;
6826
7165
  }
6827
7166
  async function resolveNearestExistingAncestor(directory) {
@@ -6831,18 +7170,18 @@ async function resolveNearestExistingAncestor(directory) {
6831
7170
  try {
6832
7171
  return { existingAncestor: await realpath(current), missingSegments };
6833
7172
  } catch (err) {
6834
- const parent = dirname5(current);
7173
+ const parent = dirname6(current);
6835
7174
  if (err.code !== "ENOENT" || parent === current) {
6836
7175
  throw err;
6837
7176
  }
6838
- missingSegments.unshift(basename(current));
7177
+ missingSegments.unshift(basename2(current));
6839
7178
  current = parent;
6840
7179
  }
6841
7180
  }
6842
7181
  }
6843
7182
  async function findContainingAllowedDirectory(allowedDirectories, realTarget) {
6844
7183
  for (const directory of allowedDirectories) {
6845
- if (!isAbsolute(directory)) {
7184
+ if (!isAbsolute2(directory)) {
6846
7185
  log("warn", "file_push_allowed_directory_skipped", { directory, reason: "not_absolute" });
6847
7186
  continue;
6848
7187
  }
@@ -6880,19 +7219,19 @@ async function realpathCreatingIfMissing(directory) {
6880
7219
  }
6881
7220
  }
6882
7221
  function contains(realDirectory, realTarget) {
6883
- const rel = relative(realDirectory, realTarget);
6884
- return rel !== "" && rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute(rel);
7222
+ const rel = relative2(realDirectory, realTarget);
7223
+ return rel !== "" && rel !== ".." && !rel.startsWith(`..${sep2}`) && !isAbsolute2(rel);
6885
7224
  }
6886
7225
  async function createMissingDirectories(existingAncestor, missingSegments) {
6887
7226
  let current = existingAncestor;
6888
7227
  for (const segment of missingSegments) {
6889
- current = join8(current, segment);
7228
+ current = join9(current, segment);
6890
7229
  await mkdir(current, { recursive: true, mode: DIRECTORY_MODE });
6891
7230
  await chmod(current, DIRECTORY_MODE);
6892
7231
  }
6893
7232
  }
6894
7233
  async function writeAtomically(realTarget, content) {
6895
- const temporaryPath = join8(dirname5(realTarget), `.evident-push-${randomUUID()}.tmp`);
7234
+ const temporaryPath = join9(dirname6(realTarget), `.evident-push-${randomUUID()}.tmp`);
6896
7235
  let handle;
6897
7236
  try {
6898
7237
  handle = await open3(temporaryPath, "wx", FILE_MODE);
@@ -7022,12 +7361,12 @@ var NOT_APPLIED = {
7022
7361
  opencodeAuthApplied: false
7023
7362
  };
7024
7363
  function isClaudeCredentialPath(requestedPath, homeDir) {
7025
- const expanded = requestedPath === "~" ? homeDir : requestedPath.startsWith("~/") ? join9(homeDir, requestedPath.slice(2)) : requestedPath;
7026
- 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);
7027
7366
  }
7028
7367
  function isOpenCodeAuthPath(requestedPath, homeDir) {
7029
- const expanded = requestedPath === "~" ? homeDir : requestedPath.startsWith("~/") ? join9(homeDir, requestedPath.slice(2)) : requestedPath;
7030
- 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);
7031
7370
  }
7032
7371
  async function applyOne(options, file) {
7033
7372
  const label = `${file.id.slice(0, 8)} (${file.path})`;
@@ -7171,6 +7510,11 @@ function messageIdOf(m) {
7171
7510
  const infoId = m.info?.id;
7172
7511
  return typeof infoId === "string" ? infoId : void 0;
7173
7512
  }
7513
+ function composeAbandonmentNote(finish, replyShape, outcome) {
7514
+ const base = `OpenCode ended this turn after a tool call (finish="${finish ?? "(unknown)"}") ` + (outcome === "without-answer" ? "without writing an answer, and confirmed it is no longer working on this conversation. Evident is reporting the run as failed so the failure and its reason are visible in failed runs." : "after writing only part of an answer, and confirmed it is no longer working on this conversation. Evident delivered what was written and is reporting the run as finished.");
7515
+ if (replyShape === null) return base;
7516
+ return `${base} ${replyShape.lastToolName === null ? "No tool call was recorded" : `The last tool call was "${replyShape.lastToolName}" with status "${replyShape.lastToolStatus ?? "(unknown)"}"`}; the reply's final part types were [${replyShape.partTypes.join(", ")}]${replyShape.droppedPartCount > 0 ? ` (${replyShape.droppedPartCount} earlier part types omitted)` : ""}.`;
7517
+ }
7174
7518
  function cleanImageMime(contentType) {
7175
7519
  if (!contentType) return null;
7176
7520
  const media = contentType.split(";")[0].trim().toLowerCase();
@@ -9905,17 +10249,28 @@ var ChannelDriver = class _ChannelDriver {
9905
10249
  }
9906
10250
  } else {
9907
10251
  if (inFlight.b2AbandonedSignalled) {
10252
+ const reply = findLastAssistantReplyFor(messages, inFlight.opencodeMessageId);
10253
+ const finish = reply?.info?.finish ?? reply?.finish;
9908
10254
  if (inFlight.b2AbandonedWithoutAnswer) {
9909
- const reply = findLastAssistantReplyFor(messages, inFlight.opencodeMessageId);
9910
10255
  await this.settleMessageAbandonedWithoutAnswer(
9911
10256
  sessionId,
9912
10257
  watcher,
9913
10258
  inFlight,
9914
10259
  messages,
9915
- reply?.info?.finish ?? reply?.finish
10260
+ finish
9916
10261
  );
9917
10262
  } else {
9918
- await this.settleMessageDone(sessionId, watcher, inFlight, messages);
10263
+ await this.settleMessageDone(
10264
+ sessionId,
10265
+ watcher,
10266
+ inFlight,
10267
+ messages,
10268
+ composeAbandonmentNote(
10269
+ finish,
10270
+ describeAbandonedReplyShape(messages, inFlight.opencodeMessageId),
10271
+ "with-answer"
10272
+ )
10273
+ );
9919
10274
  }
9920
10275
  return;
9921
10276
  }
@@ -9978,7 +10333,17 @@ var ChannelDriver = class _ChannelDriver {
9978
10333
  ...descendantOngoing != null ? { descendant_ongoing: descendantOngoing } : {},
9979
10334
  opencode_message_id: inFlight.opencodeMessageId
9980
10335
  });
9981
- await this.settleMessageDone(sessionId, watcher, inFlight, messages);
10336
+ await this.settleMessageDone(
10337
+ sessionId,
10338
+ watcher,
10339
+ inFlight,
10340
+ messages,
10341
+ composeAbandonmentNote(
10342
+ finish,
10343
+ describeAbandonedReplyShape(messages, inFlight.opencodeMessageId),
10344
+ "with-answer"
10345
+ )
10346
+ );
9982
10347
  return;
9983
10348
  }
9984
10349
  if (rootOngoing === true && !inFlight.b2RootOngoingHeldLogged) {
@@ -10178,15 +10543,11 @@ var ChannelDriver = class _ChannelDriver {
10178
10543
  }
10179
10544
  }
10180
10545
  /**
10181
- * Settle a message whose run-state has resolved `'done'` — extracted verbatim
10182
- * (pure refactor, no behavior change) from `serviceInFlightMessage`'s former
10183
- * inline `state === 'done'` branch body, so a SECOND caller (the #721
10184
- * b2-abandonment resolution) can reach the exact same completion behavior
10185
- * (delivery-deadline anchoring, title resolution, usage extraction, and
10186
- * `markDone`'s auth/terminal/transient-retry discipline) without duplicating it
10187
- * and risking the two copies silently drifting apart.
10546
+ * Settle a message whose run-state has resolved `'done'`. Normal completion and
10547
+ * confirmed b2 abandonment share the same delivery, usage, and terminal handling.
10548
+ * Normal completions omit the optional terminal diagnostic.
10188
10549
  */
10189
- async settleMessageDone(sessionId, watcher, inFlight, messages) {
10550
+ async settleMessageDone(sessionId, watcher, inFlight, messages, note) {
10190
10551
  const conv = watcher.conv;
10191
10552
  this.anchorDeliveryDeadline(inFlight);
10192
10553
  if (!inFlight.done) {
@@ -10213,7 +10574,8 @@ var ChannelDriver = class _ChannelDriver {
10213
10574
  title,
10214
10575
  usage,
10215
10576
  usageAgentName,
10216
- subagentInvocations
10577
+ subagentInvocations,
10578
+ note
10217
10579
  );
10218
10580
  } catch (err) {
10219
10581
  if (err instanceof ChannelAuthError) throw err;
@@ -10258,9 +10620,11 @@ var ChannelDriver = class _ChannelDriver {
10258
10620
  /** Report a confirmed b2 abandonment that produced no answer text. */
10259
10621
  async settleMessageAbandonedWithoutAnswer(sessionId, watcher, inFlight, messages, finish) {
10260
10622
  const conv = watcher.conv;
10261
- const legacyError = `OpenCode ended this turn after a tool call (finish="${finish ?? "(unknown)"}") without writing an answer, and confirmed it is no longer working on this conversation. Evident is reporting the run as failed so the failure and its reason are visible in failed runs.`;
10262
- const replyShape = describeAbandonedReplyShape(messages, inFlight.opencodeMessageId);
10263
- const error2 = replyShape === null ? legacyError : `${legacyError} ${replyShape.lastToolName === null ? "No tool call was recorded" : `The last tool call was "${replyShape.lastToolName}" with status "${replyShape.lastToolStatus ?? "(unknown)"}"`}; the reply's final part types were [${replyShape.partTypes.join(", ")}]${replyShape.droppedPartCount > 0 ? ` (${replyShape.droppedPartCount} earlier part types omitted)` : ""}.`;
10623
+ const error2 = composeAbandonmentNote(
10624
+ finish,
10625
+ describeAbandonedReplyShape(messages, inFlight.opencodeMessageId),
10626
+ "without-answer"
10627
+ );
10264
10628
  this.anchorDeliveryDeadline(inFlight);
10265
10629
  this.log({
10266
10630
  level: "warn",
@@ -11497,14 +11861,14 @@ var ChannelDriver = class _ChannelDriver {
11497
11861
  const fetchChildWithoutBlocking = async (sessionId) => {
11498
11862
  const pending = fetchChildMessages(sessionId);
11499
11863
  let timer;
11500
- const timeout = new Promise((resolve4) => {
11864
+ const timeout = new Promise((resolve5) => {
11501
11865
  timer = setTimeout(() => {
11502
11866
  this.log({
11503
11867
  level: "warn",
11504
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`,
11505
11869
  message_id: messageId
11506
11870
  });
11507
- resolve4(null);
11871
+ resolve5(null);
11508
11872
  }, SUBAGENT_TELEMETRY_FETCH_TIMEOUT_MS);
11509
11873
  });
11510
11874
  try {
@@ -12064,7 +12428,7 @@ var ChannelDriver = class _ChannelDriver {
12064
12428
  * watcher retries next tick within the
12065
12429
  * deadline, Finding 4).
12066
12430
  */
12067
- async markDone(conversationId, messageId, sessionId, opencodeMessageId, title, usage, usageAgentName, subagentInvocations) {
12431
+ async markDone(conversationId, messageId, sessionId, opencodeMessageId, title, usage, usageAgentName, subagentInvocations, error2) {
12068
12432
  const res = await this.fetchImpl(
12069
12433
  `${this.apiUrl}/runners/${this.agentId}/threads/${conversationId}/messages/${messageId}`,
12070
12434
  {
@@ -12082,7 +12446,8 @@ var ChannelDriver = class _ChannelDriver {
12082
12446
  ...title ? { title } : {},
12083
12447
  ...usage ? usage : {},
12084
12448
  ...usageAgentName ? { usage_agent_name: usageAgentName } : {},
12085
- ...subagentInvocations && subagentInvocations.length > 0 ? { subagent_invocations: subagentInvocations } : {}
12449
+ ...subagentInvocations && subagentInvocations.length > 0 ? { subagent_invocations: subagentInvocations } : {},
12450
+ ...error2 ? { error: error2 } : {}
12086
12451
  })
12087
12452
  }
12088
12453
  );
@@ -12745,7 +13110,7 @@ function commandError2(result) {
12745
13110
  return result.stderr.trim() || `process exited with code ${result.code ?? "null"}`;
12746
13111
  }
12747
13112
  var runCommand2 = (command, args, opts) => {
12748
- return new Promise((resolve4) => {
13113
+ return new Promise((resolve5) => {
12749
13114
  let child;
12750
13115
  let stdout = "";
12751
13116
  let stderr = "";
@@ -12755,7 +13120,7 @@ var runCommand2 = (command, args, opts) => {
12755
13120
  if (settled) return;
12756
13121
  settled = true;
12757
13122
  if (timer.handle) clearTimeout(timer.handle);
12758
- resolve4(result);
13123
+ resolve5(result);
12759
13124
  };
12760
13125
  try {
12761
13126
  child = spawn5(command, args, { env: opts.env, stdio: ["ignore", "pipe", "pipe"] });
@@ -12810,7 +13175,9 @@ async function installRunnerSecret({
12810
13175
  }) {
12811
13176
  const arn = env.RUNNER_SECRET_ARN?.trim();
12812
13177
  if (!arn) {
12813
- log3("runner secret is not configured; continuing without GitHub and MCP credentials");
13178
+ if (!env.GH_TOKEN) {
13179
+ log3("runner secret is not configured; continuing without GitHub and MCP credentials");
13180
+ }
12814
13181
  return false;
12815
13182
  }
12816
13183
  const result = await (commandRunner ?? runCommand2)(
@@ -13009,7 +13376,7 @@ async function configureGitHubAccess({ env, log: log3 }) {
13009
13376
  // src/lib/opencode/config-overlay.ts
13010
13377
  import { execFileSync as execFileSync2 } from "node:child_process";
13011
13378
  import { copyFileSync, existsSync as existsSync2, statSync as statSync6 } from "node:fs";
13012
- 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";
13013
13380
  function isFile(filePath) {
13014
13381
  return existsSync2(filePath) && statSync6(filePath).isFile();
13015
13382
  }
@@ -13022,8 +13389,8 @@ function applyRunnerOpenCodeConfig({
13022
13389
  log3("runner OpenCode config is not configured; using the baked project config", "debug");
13023
13390
  return;
13024
13391
  }
13025
- const source = isAbsolute2(overlayPath) ? overlayPath : resolve3(cwd, overlayPath);
13026
- 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";
13027
13394
  if (!isFile(source)) {
13028
13395
  log3(
13029
13396
  `RUNNER-OPENCODE-CONFIG-MISSING: ${source} is not a file; headless turns will wedge on the first external-directory permission prompt (#563)`,
@@ -13031,7 +13398,7 @@ function applyRunnerOpenCodeConfig({
13031
13398
  );
13032
13399
  return;
13033
13400
  }
13034
- copyFileSync(source, join10(cwd, target));
13401
+ copyFileSync(source, join11(cwd, target));
13035
13402
  try {
13036
13403
  execFileSync2("git", ["-C", cwd, "update-index", "--skip-worktree", target], {
13037
13404
  stdio: "ignore"
@@ -13040,7 +13407,7 @@ function applyRunnerOpenCodeConfig({
13040
13407
  const detail = error2 instanceof Error ? error2.message : String(error2);
13041
13408
  log3(`could not mark ${target} skip-worktree: ${detail}; it may show as a local change`, "warn");
13042
13409
  }
13043
- log3(`Applied runner OpenCode config ${source} to ${join10(cwd, target)}`);
13410
+ log3(`Applied runner OpenCode config ${source} to ${join11(cwd, target)}`);
13044
13411
  }
13045
13412
 
13046
13413
  // src/lib/credential-sync.ts
@@ -13058,14 +13425,14 @@ function errorMessage2(error2) {
13058
13425
  return error2 instanceof Error ? error2.message : String(error2);
13059
13426
  }
13060
13427
  function waitForSettlement(promise, timeoutMs) {
13061
- return new Promise((resolve4) => {
13428
+ return new Promise((resolve5) => {
13062
13429
  let settled = false;
13063
13430
  const timer = setTimeout(() => finish(false), Math.max(0, timeoutMs));
13064
13431
  const finish = (value) => {
13065
13432
  if (settled) return;
13066
13433
  settled = true;
13067
13434
  clearTimeout(timer);
13068
- resolve4(value);
13435
+ resolve5(value);
13069
13436
  };
13070
13437
  promise.then(
13071
13438
  () => finish(true),
@@ -13316,8 +13683,8 @@ function resolveFileSyncDirectories(raw, homeDir) {
13316
13683
  if (trimmed === "") {
13317
13684
  throw new Error("--enable-file-sync-to requires a directory path (got an empty value)");
13318
13685
  }
13319
- const expanded = trimmed === "~" ? homeDir : trimmed.startsWith("~/") ? join11(homeDir, trimmed.slice(2)) : trimmed;
13320
- if (!isAbsolute3(expanded)) {
13686
+ const expanded = trimmed === "~" ? homeDir : trimmed.startsWith("~/") ? join12(homeDir, trimmed.slice(2)) : trimmed;
13687
+ if (!isAbsolute4(expanded)) {
13321
13688
  throw new Error(`--enable-file-sync-to requires an absolute directory path; got "${entry}"`);
13322
13689
  }
13323
13690
  const normalized = resolvePath2(expanded);
@@ -13417,8 +13784,7 @@ function resolveMaxActiveSessions(options, env = process.env) {
13417
13784
  function meetsThreshold(state, level) {
13418
13785
  return LOG_LEVELS[level] >= LOG_LEVELS[state.logLevel];
13419
13786
  }
13420
- function log2(state, message, level = "info") {
13421
- if (!meetsThreshold(state, level)) return;
13787
+ function writeConsoleLog(state, message, level) {
13422
13788
  if (state.json) {
13423
13789
  console.log(
13424
13790
  JSON.stringify({
@@ -13427,10 +13793,18 @@ function log2(state, message, level = "info") {
13427
13793
  message
13428
13794
  })
13429
13795
  );
13430
- } else if (!state.interactive) {
13431
- const prefix = level === "error" ? chalk7.red("\u2717") : level === "warn" ? chalk7.yellow("!") : level === "debug" ? chalk7.dim("\xB7") : chalk7.green("\u2022");
13432
- console.log(`${prefix} ${message}`);
13796
+ return;
13433
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);
13434
13808
  }
13435
13809
  function logActivity(state, entry) {
13436
13810
  const level = entry.level ?? (entry.type === "error" ? "error" : "info");
@@ -13732,7 +14106,7 @@ async function driveChannels(state, driver) {
13732
14106
  }
13733
14107
  }
13734
14108
  }
13735
- await new Promise((resolve4) => setTimeout(resolve4, CHANNEL_POLL_INTERVAL_MS));
14109
+ await new Promise((resolve5) => setTimeout(resolve5, CHANNEL_POLL_INTERVAL_MS));
13736
14110
  const cycleMs = performance.now() - cycleStartedAtMs;
13737
14111
  if (idleThisCycle) idleMs += cycleMs;
13738
14112
  if (unreachableThisCycle) unreachableMs += cycleMs;
@@ -13754,6 +14128,9 @@ async function driveChannels(state, driver) {
13754
14128
  }
13755
14129
  var SESSION_CLEANUP_FIRST_SWEEP_MS = 1e4;
13756
14130
  var SESSION_CLEANUP_MAX_DELETES_PER_SWEEP = 100;
14131
+ var SESSION_CLEANUP_MAX_ISOLATED_DELETE_FAILURES = 2;
14132
+ var SESSION_CLEANUP_GIVE_UP_AFTER_FAILURES = 2;
14133
+ var MAX_SESSION_DELETE_BODY_LENGTH = 200;
13757
14134
  function shouldWarnForReclaimSkip(reason) {
13758
14135
  if (reason !== "sqlite-unavailable" && reason !== "disk-exhaustion") return false;
13759
14136
  const version2 = /^v(\d+)\.(\d+)\.(\d+)$/.exec(process.version);
@@ -13764,7 +14141,7 @@ function shouldWarnForReclaimSkip(reason) {
13764
14141
  return major > 22 || major === 22 && (minor > 13 || minor === 13 && patch >= 0);
13765
14142
  }
13766
14143
  function sessionDbPath() {
13767
- return join11(homedir7(), ".local", "share", "opencode", "opencode.db");
14144
+ return join12(homedir7(), ".local", "share", "opencode", "opencode.db");
13768
14145
  }
13769
14146
  function logSessionCleanupActivity(state, phase, entry) {
13770
14147
  logActivity(state, {
@@ -13841,8 +14218,12 @@ async function runSweep(state, driver, config) {
13841
14218
  protectedIds: protectedNow
13842
14219
  }
13843
14220
  );
13844
- const deleteCandidates = toDelete.slice(0, SESSION_CLEANUP_MAX_DELETES_PER_SWEEP);
13845
- remaining = toDelete.length - deleteCandidates.length;
14221
+ const eligibleToDelete = toDelete.filter(
14222
+ (id) => (state.sessionDeleteFailures.get(id)?.isolatedFailures ?? 0) < SESSION_CLEANUP_GIVE_UP_AFTER_FAILURES
14223
+ );
14224
+ const deleteCandidates = eligibleToDelete.slice(0, SESSION_CLEANUP_MAX_DELETES_PER_SWEEP);
14225
+ remaining = eligibleToDelete.length - deleteCandidates.length;
14226
+ const answeredFailures = [];
13846
14227
  protectedNow = driver.protectedSessionIds();
13847
14228
  for (const id of deleteCandidates) {
13848
14229
  if (protectedNow.has(id)) {
@@ -13865,8 +14246,44 @@ async function runSweep(state, driver, config) {
13865
14246
  if (statusClass === 4) failed4xx++;
13866
14247
  else if (statusClass === 5) failed5xx++;
13867
14248
  else failedOtherStatus++;
14249
+ answeredFailures.push({
14250
+ id,
14251
+ status: outcome.status,
14252
+ ...outcome.body !== void 0 ? { body: outcome.body } : {}
14253
+ });
14254
+ }
14255
+ }
14256
+ const givenUpFailures = [];
14257
+ if (failed > SESSION_CLEANUP_MAX_ISOLATED_DELETE_FAILURES) {
14258
+ for (const { id } of answeredFailures) state.sessionDeleteFailures.delete(id);
14259
+ } else {
14260
+ for (const failure of answeredFailures) {
14261
+ const previous = state.sessionDeleteFailures.get(failure.id);
14262
+ const record = previous?.status === failure.status ? { status: failure.status, isolatedFailures: previous.isolatedFailures + 1 } : { status: failure.status, isolatedFailures: 1 };
14263
+ state.sessionDeleteFailures.set(failure.id, record);
14264
+ if (record.isolatedFailures >= SESSION_CLEANUP_GIVE_UP_AFTER_FAILURES) {
14265
+ givenUpFailures.push(failure);
14266
+ }
13868
14267
  }
13869
14268
  }
14269
+ for (const failure of givenUpFailures) {
14270
+ const excerpt = redactDiagnosticText(failure.body ?? "(no response body)").slice(
14271
+ 0,
14272
+ MAX_SESSION_DELETE_BODY_LENGTH
14273
+ );
14274
+ logSessionCleanupActivity(state, "sweep", {
14275
+ type: "info",
14276
+ level: "warn",
14277
+ message: `Session cleanup: gave up on session ${failure.id} after ${SESSION_CLEANUP_GIVE_UP_AFTER_FAILURES} isolated delete failures (status ${failure.status}): ${excerpt}`,
14278
+ metadata: {
14279
+ outcome: "session-delete-given-up",
14280
+ session_id: failure.id,
14281
+ status: failure.status,
14282
+ body_excerpt: excerpt,
14283
+ given_up_count: givenUpFailures.length
14284
+ }
14285
+ });
14286
+ }
13870
14287
  const failedNote = failed > 0 ? `, failed ${failed}` : "";
13871
14288
  const skippedNote = skippedNewlyActive > 0 ? `, skipped ${skippedNewlyActive} newly-active` : "";
13872
14289
  const remainingNote = remaining > 0 ? `, remaining ${remaining}` : "";
@@ -13887,21 +14304,31 @@ async function runSweep(state, driver, config) {
13887
14304
  sessions_remaining: remaining,
13888
14305
  ...enumerationReason !== null ? { session_enumeration_reason: enumerationReason } : {}
13889
14306
  };
14307
+ const protectedForReclaim = driver.protectedSessionIds();
13890
14308
  const reclaimResult = await reclaimSessionDbSpace({
13891
14309
  dbPath: sessionDbPath(),
13892
14310
  chunkPages: SESSION_DB_RECLAIM_CHUNK_PAGES,
13893
14311
  pageCeiling: SESSION_DB_RECLAIM_PAGE_CEILING,
13894
14312
  timeBudgetMs: SESSION_DB_RECLAIM_TIME_BUDGET_MS,
13895
- allowFullVacuum: protectedNow.size === 0 && !state.sessionDbProvenanceAnomaly
14313
+ hasLiveTurn: protectedForReclaim.size > 0,
14314
+ hasProvenanceAnomaly: state.sessionDbProvenanceAnomaly
13896
14315
  });
13897
14316
  if (reclaimResult.ok) {
13898
14317
  state.sessionStoreHealth.observeWritable();
13899
14318
  const beforeMib = (reclaimResult.beforeBytes / 1024 / 1024).toFixed(1);
13900
14319
  const afterMib = (reclaimResult.afterBytes / 1024 / 1024).toFixed(1);
13901
14320
  const checkpointNote = reclaimResult.checkpoint.busy ? ` (on-disk file truncation deferred: checkpoint busy, ${reclaimResult.checkpoint.log} WAL frames pending)` : "";
14321
+ const loopElapsedMs = Math.max(
14322
+ 0,
14323
+ reclaimResult.elapsedMs - reclaimResult.checkpointElapsedMs
14324
+ );
14325
+ const reclaimIsSlow = reclaimResult.elapsedMs >= 4 * SESSION_DB_RECLAIM_TIME_BUDGET_MS;
14326
+ const perPageNote = reclaimResult.pagesReclaimed > 0 ? `, per-page ${(loopElapsedMs / reclaimResult.pagesReclaimed).toFixed(2)} ms/page` : "";
14327
+ const slowReclaimNote = reclaimIsSlow ? ` (slow reclaim: total elapsed ${reclaimResult.elapsedMs} ms, checkpoint ${reclaimResult.checkpointElapsedMs} ms, loop ${loopElapsedMs} ms, budget ${SESSION_DB_RECLAIM_TIME_BUDGET_MS} ms${perPageNote})` : "";
13902
14328
  logSessionCleanupActivity(state, "sweep", {
13903
14329
  type: "info",
13904
- message: `Session cleanup: reclaimed session-db space (${reclaimResult.mode}): ${beforeMib} MiB -> ${afterMib} MiB (stopped by ${reclaimResult.stoppedBy}; freelist ${reclaimResult.freelistBefore} -> ${reclaimResult.freelistAfter} pages)` + checkpointNote,
14330
+ level: reclaimIsSlow ? "warn" : "info",
14331
+ message: `Session cleanup: reclaimed session-db space (${reclaimResult.mode}): ${beforeMib} MiB -> ${afterMib} MiB (stopped by ${reclaimResult.stoppedBy}; freelist ${reclaimResult.freelistBefore} -> ${reclaimResult.freelistAfter} pages)` + checkpointNote + slowReclaimNote,
13905
14332
  metadata: {
13906
14333
  outcome: "reclaimed",
13907
14334
  mode: reclaimResult.mode,
@@ -13914,6 +14341,7 @@ async function runSweep(state, driver, config) {
13914
14341
  freelist_after: reclaimResult.freelistAfter,
13915
14342
  pages_reclaimed: reclaimResult.pagesReclaimed,
13916
14343
  reclaim_elapsed_ms: reclaimResult.elapsedMs,
14344
+ checkpoint_elapsed_ms: reclaimResult.checkpointElapsedMs,
13917
14345
  ...sessionMetadata
13918
14346
  }
13919
14347
  });
@@ -14393,8 +14821,8 @@ async function beginGracefulShutdown(state, trigger) {
14393
14821
  return true;
14394
14822
  }
14395
14823
  );
14396
- const timedOut = new Promise((resolve4) => {
14397
- timer = setTimeout(() => resolve4(false), telemetryBudgetMs);
14824
+ const timedOut = new Promise((resolve5) => {
14825
+ timer = setTimeout(() => resolve5(false), telemetryBudgetMs);
14398
14826
  });
14399
14827
  if (!await Promise.race([flushed, timedOut])) {
14400
14828
  log2(state, `Telemetry flush exceeded ${telemetryBudgetMs}ms \u2014 exiting anyway`, "warn");
@@ -14457,6 +14885,7 @@ async function run(options) {
14457
14885
  messageCount: 0,
14458
14886
  lastProxiedActivityAt: null,
14459
14887
  sessionCleanupTimers: [],
14888
+ sessionDeleteFailures: /* @__PURE__ */ new Map(),
14460
14889
  claudeUsageTimer: null,
14461
14890
  claudeUsageRearm: null,
14462
14891
  openaiUsageTimer: null,
@@ -14665,23 +15094,33 @@ async function run(options) {
14665
15094
  );
14666
15095
  }
14667
15096
  }
14668
- if (options.restoreRunnerCredentials) {
14669
- log2(state, "Restoring runner credentials before starting OpenCode");
14670
- const credentialContext = {
14671
- env: process.env,
14672
- log: (message, level = "info") => {
14673
- if (level === "error") {
14674
- logActivity(state, { type: "error", error: message });
14675
- } else {
14676
- logActivity(state, { type: "info", level, message });
14677
- }
15097
+ const credentialContext = {
15098
+ env: process.env,
15099
+ log: (message, level = "info") => {
15100
+ if (level === "error") {
15101
+ logActivity(state, { type: "error", error: message });
15102
+ } else {
15103
+ logActivity(state, { type: "info", level, message });
14678
15104
  }
14679
- };
14680
- const githubTokenPopulated = await installRunnerSecret(credentialContext);
15105
+ }
15106
+ };
15107
+ const githubTokenPopulated = await installRunnerSecret(credentialContext);
15108
+ if (options.restoreRunnerCredentials) {
14681
15109
  await restoreCredentialStores(credentialContext);
14682
- if (githubTokenPopulated) await configureGitHubAccess(credentialContext);
14683
15110
  }
15111
+ if (githubTokenPopulated) await configureGitHubAccess(credentialContext);
14684
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
+ });
14685
15124
  const openCodeActivityForwarder = createOpenCodeActivityForwarder(
14686
15125
  () => ({
14687
15126
  agentId: state.agentId,
@@ -14692,7 +15131,8 @@ async function run(options) {
14692
15131
  if (cause !== null && sessionId !== null) {
14693
15132
  state.sessionStoreHealth.observeCause(sessionId, cause);
14694
15133
  }
14695
- }
15134
+ },
15135
+ dependencyInstallDiagnosticReporter
14696
15136
  );
14697
15137
  const openCodeLogTail = tailOpenCodeLogFile(
14698
15138
  resolveOpenCodeLogPath(homedir7(), process.env),