@evident-ai/cli 3.4.1-dev.dbc0a1f → 3.4.1-dev.fb713ba

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/README.md CHANGED
@@ -221,6 +221,9 @@ targets the **production** Evident platform by default.
221
221
  flag wins if both are set.
222
222
  - `EVIDENT_SESSION_CLEANUP_INTERVAL` — Equivalent to
223
223
  `--session-cleanup-interval`; the flag wins if both are set.
224
+ - `EVIDENT_SESSION_DB_PROVENANCE_STATE` — Override the path where the CLI stores
225
+ session-database provenance state (default:
226
+ `~/.local/state/evident/session-db-provenance.json`).
224
227
 
225
228
  Authentication precedence for `run`: `EVIDENT_RUNNER_KEY`/`EVIDENT_AGENT_KEY`
226
229
  (tied; `EVIDENT_RUNNER_KEY` wins if both are set) → `EVIDENT_TOKEN` →
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import { createRequire } from "module";
4
+ import { createRequire as createRequire2 } from "module";
5
5
  import { Command } from "commander";
6
6
 
7
7
  // src/commands/login.ts
@@ -1183,9 +1183,9 @@ async function claudeUsage() {
1183
1183
  }
1184
1184
 
1185
1185
  // src/commands/run.ts
1186
- import { chmodSync as chmodSync3, existsSync as existsSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
1186
+ import { chmodSync as chmodSync3, existsSync as existsSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
1187
1187
  import { homedir as homedir5 } from "os";
1188
- import { isAbsolute as isAbsolute3, join as join8, parse, resolve as resolvePath2 } from "path";
1188
+ import { isAbsolute as isAbsolute3, join as join9, parse, resolve as resolvePath2 } from "path";
1189
1189
  import chalk6 from "chalk";
1190
1190
 
1191
1191
  // ../../packages/types/src/agents/index.ts
@@ -1525,7 +1525,14 @@ function drainSessionDbRecoveryReport({
1525
1525
  skippedLines++;
1526
1526
  return [];
1527
1527
  }
1528
- return [{ ...value, replication_suspended: value.replication_suspended ?? false }];
1528
+ return [
1529
+ {
1530
+ ...value,
1531
+ provenance_reason: value.provenance_reason ?? null,
1532
+ provenance_migration_delta: value.provenance_migration_delta ?? null,
1533
+ replication_suspended: value.replication_suspended ?? false
1534
+ }
1535
+ ];
1529
1536
  } catch (error2) {
1530
1537
  skippedLines++;
1531
1538
  console.error(
@@ -1628,6 +1635,12 @@ function buildSessionDbRecoveryActivity(record) {
1628
1635
  metadata: withoutContractFields(record),
1629
1636
  message: `This runner did not come online because its damaged session database could not be safely separated from its active backup or proven removed. Backed-up session history remains readable at ${record.quarantine_destination ?? "its original location or the quarantine destination named in the boot logs"}; any local session-database files that remain were left in place and nothing opened or wrote them. See the runner boot logs for SESSION-DB-LOCAL-DISCARD-FAILED details.`
1630
1637
  };
1638
+ case "schema_provenance_mismatch":
1639
+ return {
1640
+ level,
1641
+ metadata: withoutContractFields(record),
1642
+ message: `Session database schema provenance mismatch for ${record.dbPath ?? record.db_path ?? "unknown"}: recorded version=${record.recorded_version ?? "unknown"}, current version=${record.current_version ?? "unknown"}, reason=${record.provenance_reason ?? "unknown"}, migration delta=${record.provenance_migration_delta ?? "unknown"}. Inspect the session database and runner backup before continuing.`
1643
+ };
1631
1644
  default:
1632
1645
  return null;
1633
1646
  }
@@ -1642,7 +1655,8 @@ var OUTCOMES = /* @__PURE__ */ new Set([
1642
1655
  "fresh_session_db",
1643
1656
  "history_rolled_back",
1644
1657
  "restore_misconfigured",
1645
- "session_db_boot_refused"
1658
+ "session_db_boot_refused",
1659
+ "schema_provenance_mismatch"
1646
1660
  ]);
1647
1661
  var STAGES = /* @__PURE__ */ new Set(["restore", "verify"]);
1648
1662
  var SEVERITIES = /* @__PURE__ */ new Set(["warning", "error"]);
@@ -1660,7 +1674,7 @@ var STRING_OR_NULL_FIELDS = ["quarantine_destination", "verified_restore_point"]
1660
1674
  function isSessionDbRecoveryRecord(value) {
1661
1675
  if (!value || typeof value !== "object" || Array.isArray(value)) return false;
1662
1676
  const record = value;
1663
- return record.v === 1 && record.event === "session_db_recovery" && typeof record.at === "string" && STAGES.has(record.stage) && OUTCOMES.has(record.outcome) && SEVERITIES.has(record.severity) && typeof record.reason === "string" && (record.replication_suspended === void 0 || typeof record.replication_suspended === "boolean") && NUMBER_FIELDS.every((field) => record[field] === null || Number.isInteger(record[field])) && STRING_OR_NULL_FIELDS.every(
1677
+ return record.v === 1 && record.event === "session_db_recovery" && typeof record.at === "string" && STAGES.has(record.stage) && OUTCOMES.has(record.outcome) && SEVERITIES.has(record.severity) && typeof record.reason === "string" && (record.replication_suspended === void 0 || typeof record.replication_suspended === "boolean") && (record.provenance_reason === void 0 || record.provenance_reason === null || typeof record.provenance_reason === "string") && (record.provenance_migration_delta === void 0 || record.provenance_migration_delta === null || Number.isInteger(record.provenance_migration_delta)) && NUMBER_FIELDS.every((field) => record[field] === null || Number.isInteger(record[field])) && STRING_OR_NULL_FIELDS.every(
1664
1678
  (field) => record[field] === null || typeof record[field] === "string"
1665
1679
  );
1666
1680
  }
@@ -1780,6 +1794,8 @@ function reportRecord(stage, outcome, reason, litestreamExitCode, options) {
1780
1794
  quarantined_bytes: null,
1781
1795
  verified_restore_point: null,
1782
1796
  restore_points_tried: null,
1797
+ provenance_reason: null,
1798
+ provenance_migration_delta: null,
1783
1799
  replication_suspended: stage === "restore"
1784
1800
  });
1785
1801
  }
@@ -2200,6 +2216,131 @@ async function restoreAndVerifySessionDb(options) {
2200
2216
  return { verifyFatal: await verifySessionDb(options, configPath, env) };
2201
2217
  }
2202
2218
 
2219
+ // src/lib/opencode/session-db-provenance.ts
2220
+ import { createRequire } from "module";
2221
+ import { mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
2222
+ import { dirname as dirname3, join as join3 } from "path";
2223
+ var require2 = createRequire(import.meta.url);
2224
+ function readSessionDbMigrationIds(dbPath) {
2225
+ let db;
2226
+ try {
2227
+ const { DatabaseSync } = require2("node:sqlite");
2228
+ db = new DatabaseSync(dbPath, { readOnly: true });
2229
+ const columns = db.prepare("PRAGMA table_info(migration)").all();
2230
+ const hasExpectedShape = columns.length === 2 && columns.some(
2231
+ (column) => column.name === "id" && typeof column.type === "string" && column.type.toUpperCase() === "TEXT" && column.pk === 1
2232
+ ) && columns.some(
2233
+ (column) => column.name === "time_completed" && typeof column.type === "string" && column.type.toUpperCase() === "INTEGER" && column.notnull === 1 && column.pk === 0
2234
+ );
2235
+ if (!hasExpectedShape) {
2236
+ console.warn(`[readSessionDbMigrationIds] unsupported migration table shape in ${dbPath}`);
2237
+ return null;
2238
+ }
2239
+ const rows = db.prepare("SELECT id FROM migration ORDER BY id").all();
2240
+ if (rows.some((row) => typeof row.id !== "string")) return null;
2241
+ return rows.map((row) => row.id);
2242
+ } catch (error2) {
2243
+ console.warn(
2244
+ `[readSessionDbMigrationIds] could not read migration history from ${dbPath}: ${error2 instanceof Error ? error2.message : String(error2)}`
2245
+ );
2246
+ return null;
2247
+ } finally {
2248
+ try {
2249
+ db?.close();
2250
+ } catch (error2) {
2251
+ console.warn(
2252
+ `[readSessionDbMigrationIds] could not close ${dbPath}: ${error2 instanceof Error ? error2.message : String(error2)}`
2253
+ );
2254
+ }
2255
+ }
2256
+ }
2257
+ function sessionDbProvenanceStatePath(homeDir, env) {
2258
+ const override = env.EVIDENT_SESSION_DB_PROVENANCE_STATE?.trim();
2259
+ return override || join3(homeDir, ".local", "state", "evident", "session-db-provenance.json");
2260
+ }
2261
+ function loadSessionDbProvenanceState(path) {
2262
+ let value;
2263
+ try {
2264
+ value = JSON.parse(readFileSync3(path, "utf8"));
2265
+ } catch (error2) {
2266
+ if (error2 instanceof Error && "code" in error2 && error2.code === "ENOENT") return {};
2267
+ console.error(
2268
+ `[session-db-provenance] could not read ${path}: ${error2 instanceof Error ? error2.message : String(error2)}`
2269
+ );
2270
+ return {};
2271
+ }
2272
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
2273
+ console.error(`[session-db-provenance] ignored malformed state in ${path}`);
2274
+ return {};
2275
+ }
2276
+ const state = {};
2277
+ for (const [dbPath, record] of Object.entries(value)) {
2278
+ if (!isSessionDbProvenanceRecord(record)) {
2279
+ console.error(`[session-db-provenance] ignored malformed state in ${path}`);
2280
+ return {};
2281
+ }
2282
+ state[dbPath] = record;
2283
+ }
2284
+ return state;
2285
+ }
2286
+ function saveSessionDbProvenanceState(path, state) {
2287
+ try {
2288
+ mkdirSync2(dirname3(path), { recursive: true });
2289
+ writeFileSync2(path, `${JSON.stringify(state, null, 2)}
2290
+ `, "utf8");
2291
+ } catch (error2) {
2292
+ console.error(
2293
+ `[session-db-provenance] could not write ${path}: ${error2 instanceof Error ? error2.message : String(error2)}`
2294
+ );
2295
+ }
2296
+ }
2297
+ function evaluateSessionDbProvenance(input) {
2298
+ const { currentVersion, currentIds, previous } = input;
2299
+ if (!previous) return { anomaly: false, reason: null };
2300
+ const current = new Set(currentIds);
2301
+ const prior = new Set(previous.migrationIds);
2302
+ for (const id of prior) {
2303
+ if (!current.has(id)) return { anomaly: true, reason: "migration-history-regressed" };
2304
+ }
2305
+ if (current.size > prior.size && previous.opencodeVersion === currentVersion) {
2306
+ return { anomaly: true, reason: "foreign-version-migrations" };
2307
+ }
2308
+ return { anomaly: false, reason: null };
2309
+ }
2310
+ function checkSessionDbProvenance(input) {
2311
+ const { dbPath, currentVersion, homeDir, env } = input;
2312
+ const path = sessionDbProvenanceStatePath(homeDir, env);
2313
+ const state = loadSessionDbProvenanceState(path);
2314
+ const previous = state[dbPath];
2315
+ const currentIds = readSessionDbMigrationIds(dbPath);
2316
+ if (currentIds === null) {
2317
+ return {
2318
+ anomaly: false,
2319
+ reason: null,
2320
+ recordedVersion: previous?.opencodeVersion ?? null,
2321
+ migrationDelta: null
2322
+ };
2323
+ }
2324
+ const decision = evaluateSessionDbProvenance({ currentVersion, currentIds, previous });
2325
+ const migrationDelta = previous ? new Set(currentIds).size - new Set(previous.migrationIds).size : null;
2326
+ state[dbPath] = {
2327
+ opencodeVersion: currentVersion,
2328
+ migrationIds: currentIds,
2329
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
2330
+ };
2331
+ saveSessionDbProvenanceState(path, state);
2332
+ return {
2333
+ ...decision,
2334
+ recordedVersion: previous?.opencodeVersion ?? null,
2335
+ migrationDelta
2336
+ };
2337
+ }
2338
+ function isSessionDbProvenanceRecord(value) {
2339
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
2340
+ const record = value;
2341
+ return typeof record.opencodeVersion === "string" && Array.isArray(record.migrationIds) && record.migrationIds.every((id) => typeof id === "string") && typeof record.updatedAt === "string";
2342
+ }
2343
+
2203
2344
  // src/lib/opencode/opencode-version-gate.ts
2204
2345
  var QUEUE_VALIDATED_OPENCODE_VERSIONS = ["1.17.11", "1.18.3"];
2205
2346
  function isQueueValidatedVersion(version2) {
@@ -3286,10 +3427,10 @@ function resolveSessionCleanupConfig(flags, env = process.env) {
3286
3427
 
3287
3428
  // src/lib/opencode/session-db-size.ts
3288
3429
  import { statSync as statSync3 } from "fs";
3289
- import { join as join3 } from "path";
3430
+ import { join as join4 } from "path";
3290
3431
  var LARGE_DB_THRESHOLD_BYTES = 268435456;
3291
3432
  function statSessionDbBytes(homeDir) {
3292
- const dbPath = join3(homeDir, ".local", "share", "opencode", "opencode.db");
3433
+ const dbPath = join4(homeDir, ".local", "share", "opencode", "opencode.db");
3293
3434
  try {
3294
3435
  return statSync3(dbPath).size;
3295
3436
  } catch (err) {
@@ -3318,10 +3459,10 @@ function buildSessionStoreSizeWarning(input) {
3318
3459
 
3319
3460
  // src/lib/opencode/session-db-reclaim.ts
3320
3461
  import { statSync as statSync4, statfsSync } from "fs";
3321
- import { dirname as dirname3 } from "path";
3462
+ import { dirname as dirname4 } from "path";
3322
3463
  function insufficientSpaceReason(dbPath, requiredBytes) {
3323
3464
  try {
3324
- const fsStats = statfsSync(dirname3(dbPath));
3465
+ const fsStats = statfsSync(dirname4(dbPath));
3325
3466
  const availableBytes = fsStats.bavail * fsStats.bsize;
3326
3467
  if (availableBytes < requiredBytes) {
3327
3468
  return `only ${availableBytes} bytes free, need ${requiredBytes} for a second copy`;
@@ -3847,10 +3988,10 @@ var RunnerConnection = class {
3847
3988
  };
3848
3989
 
3849
3990
  // src/lib/tunnel/ready-marker.ts
3850
- import { writeFileSync as writeFileSync2 } from "fs";
3991
+ import { writeFileSync as writeFileSync3 } from "fs";
3851
3992
  function writeTunnelReadyMarker(path, agentId) {
3852
3993
  try {
3853
- writeFileSync2(path, `${agentId}
3994
+ writeFileSync3(path, `${agentId}
3854
3995
  `);
3855
3996
  return { ok: true };
3856
3997
  } catch (error2) {
@@ -3875,7 +4016,7 @@ async function stopSessionDbReplication(child, timeoutMs) {
3875
4016
  }
3876
4017
 
3877
4018
  // src/lib/process-liveness.ts
3878
- import { readFileSync as readFileSync3 } from "fs";
4019
+ import { readFileSync as readFileSync4 } from "fs";
3879
4020
  function isProcessAlive(pid) {
3880
4021
  try {
3881
4022
  process.kill(pid, 0);
@@ -3890,7 +4031,7 @@ function isProcessAlive(pid) {
3890
4031
  }
3891
4032
  if (process.platform !== "linux") return true;
3892
4033
  try {
3893
- const status2 = readFileSync3(`/proc/${pid}/status`, "utf8");
4034
+ const status2 = readFileSync4(`/proc/${pid}/status`, "utf8");
3894
4035
  return !/^State:\s+Z(?:\s|$)/m.test(status2);
3895
4036
  } catch (error2) {
3896
4037
  console.error(
@@ -3901,9 +4042,9 @@ function isProcessAlive(pid) {
3901
4042
  }
3902
4043
 
3903
4044
  // src/lib/openai-usage.ts
3904
- import { readFileSync as readFileSync4 } from "fs";
4045
+ import { readFileSync as readFileSync5 } from "fs";
3905
4046
  import { homedir as homedir3 } from "os";
3906
- import { join as join4 } from "path";
4047
+ import { join as join5 } from "path";
3907
4048
  var CODEX_RESPONSES_URL = "https://chatgpt.com/backend-api/codex/responses";
3908
4049
  var OPENCODE_AUTH_SEGMENTS = [".local", "share", "opencode", "auth.json"];
3909
4050
  var OpenAiUsageError = class extends Error {
@@ -3917,7 +4058,7 @@ function isLocalCredentialProblem2(err) {
3917
4058
  }
3918
4059
  function readOpenCodeChatGptCredentials() {
3919
4060
  try {
3920
- const raw = readFileSync4(join4(homedir3(), ...OPENCODE_AUTH_SEGMENTS), "utf-8");
4061
+ const raw = readFileSync5(join5(homedir3(), ...OPENCODE_AUTH_SEGMENTS), "utf-8");
3921
4062
  let parsed;
3922
4063
  try {
3923
4064
  parsed = JSON.parse(raw);
@@ -4293,12 +4434,12 @@ function createResourceUsageCollector(homeDir) {
4293
4434
  import { homedir as homedir4 } from "os";
4294
4435
 
4295
4436
  // src/lib/runner-file-sync.ts
4296
- import { join as join6 } from "path";
4437
+ import { join as join7 } from "path";
4297
4438
 
4298
4439
  // src/lib/file-push.ts
4299
4440
  import { randomUUID } from "crypto";
4300
4441
  import { chmod, mkdir, open as open2, realpath, rename, unlink } from "fs/promises";
4301
- import { basename, dirname as dirname4, isAbsolute, join as join5, relative, resolve as resolve2, sep } from "path";
4442
+ import { basename, dirname as dirname5, isAbsolute, join as join6, relative, resolve as resolve2, sep } from "path";
4302
4443
  var FILE_MODE = 384;
4303
4444
  var DIRECTORY_MODE = 448;
4304
4445
  async function writePushedFile(request) {
@@ -4329,9 +4470,9 @@ async function writePushedFile(request) {
4329
4470
  }
4330
4471
  try {
4331
4472
  const { existingAncestor, missingSegments } = await resolveNearestExistingAncestor(
4332
- dirname4(candidate)
4473
+ dirname5(candidate)
4333
4474
  );
4334
- const realTarget = join5(existingAncestor, ...missingSegments, basename(candidate));
4475
+ const realTarget = join6(existingAncestor, ...missingSegments, basename(candidate));
4335
4476
  const allowedDirectory = await findContainingAllowedDirectory(allowedDirectories, realTarget);
4336
4477
  if (allowedDirectory === null) {
4337
4478
  return refuse("path_not_allowed", "The runner does not allow writing to that location.", {
@@ -4341,8 +4482,8 @@ async function writePushedFile(request) {
4341
4482
  }
4342
4483
  if (missingSegments.length > 0) {
4343
4484
  await createMissingDirectories(existingAncestor, missingSegments);
4344
- const realParent = await realpath(dirname4(realTarget));
4345
- if (realParent !== dirname4(realTarget) || !contains(allowedDirectory, realTarget)) {
4485
+ const realParent = await realpath(dirname5(realTarget));
4486
+ if (realParent !== dirname5(realTarget) || !contains(allowedDirectory, realTarget)) {
4346
4487
  return refuse("path_not_allowed", "The runner does not allow writing to that location.", {
4347
4488
  path: realTarget,
4348
4489
  bytes,
@@ -4367,7 +4508,7 @@ function expandAndValidate(requestedPath, homeDir) {
4367
4508
  if (requestedPath.trim() === "" || requestedPath.includes("\0")) {
4368
4509
  return null;
4369
4510
  }
4370
- const expanded = requestedPath === "~" ? homeDir : requestedPath.startsWith("~/") ? join5(homeDir, requestedPath.slice(2)) : requestedPath;
4511
+ const expanded = requestedPath === "~" ? homeDir : requestedPath.startsWith("~/") ? join6(homeDir, requestedPath.slice(2)) : requestedPath;
4371
4512
  if (expanded.split(/[/\\]/).includes("..")) {
4372
4513
  return null;
4373
4514
  }
@@ -4385,7 +4526,7 @@ async function resolveNearestExistingAncestor(directory) {
4385
4526
  try {
4386
4527
  return { existingAncestor: await realpath(current), missingSegments };
4387
4528
  } catch (err) {
4388
- const parent = dirname4(current);
4529
+ const parent = dirname5(current);
4389
4530
  if (err.code !== "ENOENT" || parent === current) {
4390
4531
  throw err;
4391
4532
  }
@@ -4440,13 +4581,13 @@ function contains(realDirectory, realTarget) {
4440
4581
  async function createMissingDirectories(existingAncestor, missingSegments) {
4441
4582
  let current = existingAncestor;
4442
4583
  for (const segment of missingSegments) {
4443
- current = join5(current, segment);
4584
+ current = join6(current, segment);
4444
4585
  await mkdir(current, { recursive: true, mode: DIRECTORY_MODE });
4445
4586
  await chmod(current, DIRECTORY_MODE);
4446
4587
  }
4447
4588
  }
4448
4589
  async function writeAtomically(realTarget, content) {
4449
- const temporaryPath = join5(dirname4(realTarget), `.evident-push-${randomUUID()}.tmp`);
4590
+ const temporaryPath = join6(dirname5(realTarget), `.evident-push-${randomUUID()}.tmp`);
4450
4591
  let handle;
4451
4592
  try {
4452
4593
  handle = await open2(temporaryPath, "wx", FILE_MODE);
@@ -4576,12 +4717,12 @@ var NOT_APPLIED = {
4576
4717
  opencodeAuthApplied: false
4577
4718
  };
4578
4719
  function isClaudeCredentialPath(requestedPath, homeDir) {
4579
- const expanded = requestedPath === "~" ? homeDir : requestedPath.startsWith("~/") ? join6(homeDir, requestedPath.slice(2)) : requestedPath;
4580
- return expanded === join6(homeDir, ...CLAUDE_CREDENTIALS_SEGMENTS);
4720
+ const expanded = requestedPath === "~" ? homeDir : requestedPath.startsWith("~/") ? join7(homeDir, requestedPath.slice(2)) : requestedPath;
4721
+ return expanded === join7(homeDir, ...CLAUDE_CREDENTIALS_SEGMENTS);
4581
4722
  }
4582
4723
  function isOpenCodeAuthPath(requestedPath, homeDir) {
4583
- const expanded = requestedPath === "~" ? homeDir : requestedPath.startsWith("~/") ? join6(homeDir, requestedPath.slice(2)) : requestedPath;
4584
- return expanded === join6(homeDir, ...OPENCODE_AUTH_SEGMENTS);
4724
+ const expanded = requestedPath === "~" ? homeDir : requestedPath.startsWith("~/") ? join7(homeDir, requestedPath.slice(2)) : requestedPath;
4725
+ return expanded === join7(homeDir, ...OPENCODE_AUTH_SEGMENTS);
4585
4726
  }
4586
4727
  async function applyOne(options, file) {
4587
4728
  const label = `${file.id.slice(0, 8)} (${file.path})`;
@@ -8629,7 +8770,7 @@ Port ${port} is already in use.`));
8629
8770
  }
8630
8771
 
8631
8772
  // src/lib/runner-credentials.ts
8632
- import { chmodSync as chmodSync2, writeFileSync as writeFileSync3 } from "fs";
8773
+ import { chmodSync as chmodSync2, writeFileSync as writeFileSync4 } from "fs";
8633
8774
  import { spawn as spawn5 } from "child_process";
8634
8775
  var RUNNER_SECRET_FETCH_TIMEOUT_MS = 6e4;
8635
8776
  var CREDENTIAL_RESTORE_TIMEOUT_MS = 6e4;
@@ -8870,8 +9011,8 @@ async function configureGitHubAccess({ env, log: log3 }) {
8870
9011
  }
8871
9012
  try {
8872
9013
  env.GIT_CONFIG_GLOBAL = GIT_CONFIG_GLOBAL;
8873
- writeFileSync3(GIT_CONFIG_GLOBAL, "");
8874
- writeFileSync3(GIT_CREDENTIAL_HELPER, GIT_CREDENTIAL_HELPER_CONTENT, { mode: 448 });
9014
+ writeFileSync4(GIT_CONFIG_GLOBAL, "");
9015
+ writeFileSync4(GIT_CREDENTIAL_HELPER, GIT_CREDENTIAL_HELPER_CONTENT, { mode: 448 });
8875
9016
  chmodSync2(GIT_CREDENTIAL_HELPER, 448);
8876
9017
  const config = [
8877
9018
  ["user.name", env.GIT_USER_NAME ?? "evident-bot"],
@@ -8904,7 +9045,7 @@ async function configureGitHubAccess({ env, log: log3 }) {
8904
9045
  // src/lib/opencode/config-overlay.ts
8905
9046
  import { execFileSync as execFileSync2 } from "child_process";
8906
9047
  import { copyFileSync, existsSync as existsSync2, statSync as statSync5 } from "fs";
8907
- import { isAbsolute as isAbsolute2, join as join7, resolve as resolve3 } from "path";
9048
+ import { isAbsolute as isAbsolute2, join as join8, resolve as resolve3 } from "path";
8908
9049
  function isFile(filePath) {
8909
9050
  return existsSync2(filePath) && statSync5(filePath).isFile();
8910
9051
  }
@@ -8918,7 +9059,7 @@ function applyRunnerOpenCodeConfig({
8918
9059
  return;
8919
9060
  }
8920
9061
  const source = isAbsolute2(overlayPath) ? overlayPath : resolve3(cwd, overlayPath);
8921
- const target = isFile(join7(cwd, "opencode.jsonc")) ? "opencode.jsonc" : "opencode.json";
9062
+ const target = isFile(join8(cwd, "opencode.jsonc")) ? "opencode.jsonc" : "opencode.json";
8922
9063
  if (!isFile(source)) {
8923
9064
  log3(
8924
9065
  `RUNNER-OPENCODE-CONFIG-MISSING: ${source} is not a file; headless turns will wedge on the first external-directory permission prompt (#563)`,
@@ -8926,7 +9067,7 @@ function applyRunnerOpenCodeConfig({
8926
9067
  );
8927
9068
  return;
8928
9069
  }
8929
- copyFileSync(source, join7(cwd, target));
9070
+ copyFileSync(source, join8(cwd, target));
8930
9071
  try {
8931
9072
  execFileSync2("git", ["-C", cwd, "update-index", "--skip-worktree", target], {
8932
9073
  stdio: "ignore"
@@ -8935,7 +9076,7 @@ function applyRunnerOpenCodeConfig({
8935
9076
  const detail = error2 instanceof Error ? error2.message : String(error2);
8936
9077
  log3(`could not mark ${target} skip-worktree: ${detail}; it may show as a local change`, "warn");
8937
9078
  }
8938
- log3(`Applied runner OpenCode config ${source} to ${join7(cwd, target)}`);
9079
+ log3(`Applied runner OpenCode config ${source} to ${join8(cwd, target)}`);
8939
9080
  }
8940
9081
 
8941
9082
  // src/commands/run.ts
@@ -8975,7 +9116,7 @@ function resolveFileSyncDirectories(raw, homeDir) {
8975
9116
  if (trimmed === "") {
8976
9117
  throw new Error("--enable-file-sync-to requires a directory path (got an empty value)");
8977
9118
  }
8978
- const expanded = trimmed === "~" ? homeDir : trimmed.startsWith("~/") ? join8(homeDir, trimmed.slice(2)) : trimmed;
9119
+ const expanded = trimmed === "~" ? homeDir : trimmed.startsWith("~/") ? join9(homeDir, trimmed.slice(2)) : trimmed;
8979
9120
  if (!isAbsolute3(expanded)) {
8980
9121
  throw new Error(`--enable-file-sync-to requires an absolute directory path; got "${entry}"`);
8981
9122
  }
@@ -9318,7 +9459,43 @@ async function driveChannels(state, driver) {
9318
9459
  var SESSION_CLEANUP_FIRST_SWEEP_MS = 1e4;
9319
9460
  var SESSION_DB_RECLAIM_MAX_PAGES = 2e3;
9320
9461
  function sessionDbPath() {
9321
- return join8(homedir5(), ".local", "share", "opencode", "opencode.db");
9462
+ return join9(homedir5(), ".local", "share", "opencode", "opencode.db");
9463
+ }
9464
+ function logSessionDbProvenanceMismatch(state, provenance, currentVersion, preBootMigrationCount) {
9465
+ const record = {
9466
+ v: 1,
9467
+ event: "session_db_recovery",
9468
+ at: (/* @__PURE__ */ new Date()).toISOString(),
9469
+ stage: "verify",
9470
+ outcome: "schema_provenance_mismatch",
9471
+ severity: "error",
9472
+ reason: provenance.reason ?? "schema-provenance-mismatch",
9473
+ litestream_exit_code: null,
9474
+ attempt: null,
9475
+ replica_objects: null,
9476
+ replica_bytes: null,
9477
+ quarantine_destination: null,
9478
+ quarantined_objects: null,
9479
+ quarantine_failed_objects: null,
9480
+ quarantined_bytes: null,
9481
+ verified_restore_point: null,
9482
+ restore_points_tried: null,
9483
+ provenance_reason: provenance.reason,
9484
+ provenance_migration_delta: provenance.migrationDelta,
9485
+ replication_suspended: false,
9486
+ dbPath: sessionDbPath(),
9487
+ recorded_version: provenance.recordedVersion,
9488
+ current_version: currentVersion,
9489
+ provenance_pre_boot_migration_count: preBootMigrationCount
9490
+ };
9491
+ const activity = buildSessionDbRecoveryActivity(record);
9492
+ if (!activity) throw new Error("could not map session-DB provenance activity");
9493
+ logActivity(state, {
9494
+ type: activity.level === "error" ? "error" : "info",
9495
+ level: activity.level,
9496
+ ...activity.level === "error" ? { error: activity.message } : { message: activity.message },
9497
+ metadata: activity.metadata
9498
+ });
9322
9499
  }
9323
9500
  async function runSweep(state, driver, config) {
9324
9501
  const mode = `age=${config.maxAgeMs ?? "\u2014"} count=${config.maxCount ?? "\u2014"}`;
@@ -9365,7 +9542,7 @@ async function runSweep(state, driver, config) {
9365
9542
  const reclaimResult = await reclaimSessionDbSpace({
9366
9543
  dbPath: sessionDbPath(),
9367
9544
  maxPages: SESSION_DB_RECLAIM_MAX_PAGES,
9368
- allowFullVacuum: protectedNow.size === 0
9545
+ allowFullVacuum: protectedNow.size === 0 && !state.sessionDbProvenanceAnomaly
9369
9546
  });
9370
9547
  if (reclaimResult.ok) {
9371
9548
  const beforeMib = (reclaimResult.beforeBytes / 1024 / 1024).toFixed(1);
@@ -9804,6 +9981,7 @@ async function run(options) {
9804
9981
  connected: false,
9805
9982
  opencodeConnected: false,
9806
9983
  opencodeVersion: null,
9984
+ sessionDbProvenanceAnomaly: false,
9807
9985
  opencodeProcess: null,
9808
9986
  litestreamProcess: null,
9809
9987
  connection: null,
@@ -10082,6 +10260,7 @@ async function run(options) {
10082
10260
  for (const warning2 of maxActiveSessionsWarnings) {
10083
10261
  logActivity(state, { type: "info", level: "warn", message: warning2 });
10084
10262
  }
10263
+ const preBootMigrationIds = readSessionDbMigrationIds(sessionDbPath());
10085
10264
  const ocSpinner = interactive && !state.json ? ora3("Checking OpenCode...").start() : null;
10086
10265
  try {
10087
10266
  const oc = await ensureOpenCodeRunning({
@@ -10097,7 +10276,7 @@ async function run(options) {
10097
10276
  state.opencodeVersion = oc.version;
10098
10277
  if (options.opencodePidFile && oc.process?.pid !== void 0) {
10099
10278
  try {
10100
- writeFileSync4(options.opencodePidFile, `${oc.process.pid}
10279
+ writeFileSync5(options.opencodePidFile, `${oc.process.pid}
10101
10280
  `, { mode: 384 });
10102
10281
  chmodSync3(options.opencodePidFile, 384);
10103
10282
  } catch (error2) {
@@ -10107,6 +10286,23 @@ async function run(options) {
10107
10286
  });
10108
10287
  }
10109
10288
  }
10289
+ if (state.opencodeVersion !== null) {
10290
+ const provenance = checkSessionDbProvenance({
10291
+ dbPath: sessionDbPath(),
10292
+ currentVersion: state.opencodeVersion,
10293
+ homeDir: homedir5(),
10294
+ env: process.env
10295
+ });
10296
+ if (provenance.anomaly) {
10297
+ state.sessionDbProvenanceAnomaly = true;
10298
+ logSessionDbProvenanceMismatch(
10299
+ state,
10300
+ provenance,
10301
+ state.opencodeVersion,
10302
+ preBootMigrationIds?.length ?? null
10303
+ );
10304
+ }
10305
+ }
10110
10306
  state.opencodeConnected = oc.notReadyReason === null;
10111
10307
  const version2 = state.opencodeVersion ? ` (v${state.opencodeVersion})` : "";
10112
10308
  ocSpinner?.succeed(`OpenCode running on port ${state.port}${version2}`);
@@ -10159,7 +10355,7 @@ async function run(options) {
10159
10355
  let existingPid;
10160
10356
  if (existsSync3(options.litestreamPidFile)) {
10161
10357
  try {
10162
- const rawPid = readFileSync5(options.litestreamPidFile, "utf8").trim();
10358
+ const rawPid = readFileSync6(options.litestreamPidFile, "utf8").trim();
10163
10359
  const parsedPid = Number(rawPid);
10164
10360
  if (/^\d+$/.test(rawPid) && Number.isSafeInteger(parsedPid) && parsedPid > 0) {
10165
10361
  existingPid = parsedPid;
@@ -10196,7 +10392,7 @@ async function run(options) {
10196
10392
  });
10197
10393
  try {
10198
10394
  if (litestreamProcess.pid !== void 0) {
10199
- writeFileSync4(options.litestreamPidFile, `${litestreamProcess.pid}
10395
+ writeFileSync5(options.litestreamPidFile, `${litestreamProcess.pid}
10200
10396
  `, {
10201
10397
  mode: 384
10202
10398
  });
@@ -10448,7 +10644,7 @@ async function run(options) {
10448
10644
  }
10449
10645
 
10450
10646
  // src/index.ts
10451
- var { version } = createRequire(import.meta.url)("../package.json");
10647
+ var { version } = createRequire2(import.meta.url)("../package.json");
10452
10648
  var program = new Command();
10453
10649
  program.name("evident").description("Run OpenCode locally and connect it to Evident").version(version).option(
10454
10650
  "--endpoint <url>",