@evident-ai/cli 3.5.2-dev.06654ec → 3.5.2-dev.1ca1af3
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 +153 -45
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -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.
|
|
1325
|
+
var CLI_VERSION = (true ? "3.5.2-dev.1ca1af3" : void 0) ?? process.env.npm_package_version ?? "unknown";
|
|
1326
1326
|
function getCliVersion() {
|
|
1327
1327
|
return CLI_VERSION;
|
|
1328
1328
|
}
|
|
@@ -2054,12 +2054,23 @@ function buildSessionDbRecoveryActivity(record) {
|
|
|
2054
2054
|
metadata: withoutContractFields(record),
|
|
2055
2055
|
message: `This runner started without restoring session history because it could not verify where its session history is backed up. Nothing in the backup was changed; check the runner backup configuration and permissions.${replication}`
|
|
2056
2056
|
};
|
|
2057
|
-
case "session_db_boot_refused":
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2057
|
+
case "session_db_boot_refused": {
|
|
2058
|
+
const message = (() => {
|
|
2059
|
+
switch (record.reason) {
|
|
2060
|
+
case "local_discard_failed":
|
|
2061
|
+
return `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.`;
|
|
2062
|
+
case "replica_separation_unproven":
|
|
2063
|
+
return `This runner did not come online because its damaged session database could not be safely separated from its active backup. The SESSION-DB-REPLICA-SEPARATION-UNVERIFIED boot log contains the partial-quarantine moved and remaining counts. 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.`;
|
|
2064
|
+
case "verification_timeout_before_separation":
|
|
2065
|
+
return "This runner did not come online because session database verification timed out after detecting corruption, before replica separation was attempted. No replica progress is claimed. See SESSION-DB-VERIFY-TIMEOUT in the runner boot logs before retrying.";
|
|
2066
|
+
case "verification_timeout_during_separation":
|
|
2067
|
+
return "This runner did not come online because session database verification timed out while replica separation was in progress. Separation completion and object removal were not proven. See SESSION-DB-VERIFY-TIMEOUT in the runner boot logs before retrying.";
|
|
2068
|
+
default:
|
|
2069
|
+
return "This runner did not come online because session database recovery could not be completed. Inspect the runner boot logs for the refusal reason before retrying.";
|
|
2070
|
+
}
|
|
2071
|
+
})();
|
|
2072
|
+
return { level, metadata: withoutContractFields(record), message };
|
|
2073
|
+
}
|
|
2063
2074
|
case "schema_provenance_mismatch":
|
|
2064
2075
|
return {
|
|
2065
2076
|
level,
|
|
@@ -2345,8 +2356,12 @@ function errorOf(m) {
|
|
|
2345
2356
|
if (!m || typeof m !== "object") return void 0;
|
|
2346
2357
|
return m.info?.error ?? m.error;
|
|
2347
2358
|
}
|
|
2359
|
+
function isTerminalStopAnswer(m) {
|
|
2360
|
+
return completedOf(m) != null && errorOf(m) == null && finishOf(m) === "stop";
|
|
2361
|
+
}
|
|
2348
2362
|
function isAssistantInFlight(m) {
|
|
2349
2363
|
if (completedOf(m) == null) return true;
|
|
2364
|
+
if (errorOf(m) != null) return false;
|
|
2350
2365
|
return finishOf(m) === "tool-calls";
|
|
2351
2366
|
}
|
|
2352
2367
|
async function getSessionMessages(port, sessionId, client) {
|
|
@@ -2793,30 +2808,41 @@ function findAssistantReplyAfter(messages, userMessageId) {
|
|
|
2793
2808
|
function findLastAssistantReplyFor(messages, userMessageId) {
|
|
2794
2809
|
if (!messages || messages.length === 0) return null;
|
|
2795
2810
|
let lastCorrelated = null;
|
|
2796
|
-
let lastNonErrored = null;
|
|
2797
2811
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
2798
2812
|
const m = messages[i];
|
|
2799
2813
|
if (roleOf2(m) !== "assistant" || parentIdOf2(m) !== userMessageId) continue;
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2814
|
+
lastCorrelated = m;
|
|
2815
|
+
break;
|
|
2816
|
+
}
|
|
2817
|
+
if (lastCorrelated) {
|
|
2818
|
+
if (errorOf(lastCorrelated) == null) return lastCorrelated;
|
|
2819
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
2820
|
+
const m = messages[i];
|
|
2821
|
+
if (roleOf2(m) === "assistant" && parentIdOf2(m) === userMessageId && isTerminalStopAnswer(m)) {
|
|
2822
|
+
return m;
|
|
2823
|
+
}
|
|
2804
2824
|
}
|
|
2825
|
+
return lastCorrelated;
|
|
2805
2826
|
}
|
|
2806
|
-
if (lastCorrelated) return lastNonErrored ?? lastCorrelated;
|
|
2807
2827
|
const userIndex = messages.findIndex((m) => idOf2(m) === userMessageId);
|
|
2808
2828
|
if (userIndex === -1) return null;
|
|
2809
2829
|
let last = null;
|
|
2810
|
-
let
|
|
2830
|
+
let lastAssistantIndex = -1;
|
|
2811
2831
|
for (let i = userIndex + 1; i < messages.length; i++) {
|
|
2812
2832
|
const role = roleOf2(messages[i]);
|
|
2813
2833
|
if (role === "user") break;
|
|
2814
2834
|
if (role === "assistant") {
|
|
2815
2835
|
last = messages[i];
|
|
2816
|
-
|
|
2836
|
+
lastAssistantIndex = i;
|
|
2817
2837
|
}
|
|
2818
2838
|
}
|
|
2819
|
-
|
|
2839
|
+
if (last === null || errorOf(last) == null) return last;
|
|
2840
|
+
for (let i = lastAssistantIndex; i > userIndex; i--) {
|
|
2841
|
+
const m = messages[i];
|
|
2842
|
+
if (roleOf2(m) === "user") break;
|
|
2843
|
+
if (roleOf2(m) === "assistant" && isTerminalStopAnswer(m)) return m;
|
|
2844
|
+
}
|
|
2845
|
+
return last;
|
|
2820
2846
|
}
|
|
2821
2847
|
function collectSubagentSessions(messages, userMessageId) {
|
|
2822
2848
|
if (!messages || messages.length === 0) return [];
|
|
@@ -2922,8 +2948,9 @@ function attributeTaskCallUsage(messages, windows) {
|
|
|
2922
2948
|
}
|
|
2923
2949
|
function sumAssistantUsage(messages) {
|
|
2924
2950
|
if (!messages || messages.length === 0) return null;
|
|
2925
|
-
const
|
|
2926
|
-
const
|
|
2951
|
+
const hasTrailingError = errorOf(messages[messages.length - 1]) != null;
|
|
2952
|
+
const hasTerminalStopAnswer = messages.some(isTerminalStopAnswer);
|
|
2953
|
+
const selected = hasTrailingError && hasTerminalStopAnswer ? messages.filter((message) => errorOf(message) == null) : messages;
|
|
2927
2954
|
let sawAnyUsage = false;
|
|
2928
2955
|
let inputSum = 0;
|
|
2929
2956
|
let outputSum = 0;
|
|
@@ -2979,11 +3006,9 @@ function messageUsage(messages, userMessageId) {
|
|
|
2979
3006
|
const byParentAll = messages.filter(
|
|
2980
3007
|
(m) => roleOf2(m) === "assistant" && parentIdOf2(m) === userMessageId
|
|
2981
3008
|
);
|
|
2982
|
-
const byParentNonErrored = byParentAll.filter((m) => errorOf(m) == null);
|
|
2983
|
-
const byParent = byParentNonErrored.length > 0 ? byParentNonErrored : byParentAll;
|
|
2984
3009
|
let correlated;
|
|
2985
|
-
if (
|
|
2986
|
-
correlated =
|
|
3010
|
+
if (byParentAll.length > 0) {
|
|
3011
|
+
correlated = byParentAll;
|
|
2987
3012
|
} else {
|
|
2988
3013
|
const reply = findAssistantReplyAfter(messages, userMessageId);
|
|
2989
3014
|
correlated = reply ? [reply] : [];
|
|
@@ -3029,14 +3054,38 @@ function messageError(messages, userMessageId) {
|
|
|
3029
3054
|
const reply = findLastAssistantReplyFor(messages, userMessageId);
|
|
3030
3055
|
const error2 = errorOf(reply);
|
|
3031
3056
|
if (error2 == null) return null;
|
|
3032
|
-
|
|
3033
|
-
if (typeof error2 === "
|
|
3057
|
+
let rendered;
|
|
3058
|
+
if (typeof error2 === "string") {
|
|
3059
|
+
rendered = error2;
|
|
3060
|
+
} else if (typeof error2 === "object") {
|
|
3034
3061
|
const e = error2;
|
|
3035
3062
|
const dataMessage = e.data?.message;
|
|
3036
|
-
if (typeof dataMessage === "string")
|
|
3037
|
-
|
|
3063
|
+
if (typeof dataMessage === "string") {
|
|
3064
|
+
rendered = dataMessage;
|
|
3065
|
+
} else if (typeof e.message === "string") {
|
|
3066
|
+
rendered = e.message;
|
|
3067
|
+
} else {
|
|
3068
|
+
rendered = "The agent run failed.";
|
|
3069
|
+
}
|
|
3070
|
+
} else {
|
|
3071
|
+
rendered = "The agent run failed.";
|
|
3038
3072
|
}
|
|
3039
|
-
|
|
3073
|
+
const toolPart = Array.isArray(reply?.parts) ? reply.parts.find(
|
|
3074
|
+
(part) => part && typeof part === "object" && part.type === "tool" && part.state?.status === "error"
|
|
3075
|
+
) : void 0;
|
|
3076
|
+
return typeof toolPart?.tool === "string" && toolPart.tool.length > 0 ? `${rendered} (tool "${toolPart.tool}")` : rendered;
|
|
3077
|
+
}
|
|
3078
|
+
function describeAbandonedReplyShape(messages, userMessageId) {
|
|
3079
|
+
const reply = findLastAssistantReplyFor(messages, userMessageId);
|
|
3080
|
+
if (!reply) return null;
|
|
3081
|
+
const parts = Array.isArray(reply.parts) ? reply.parts : [];
|
|
3082
|
+
const lastTool = [...parts].reverse().find((part) => part?.type === "tool");
|
|
3083
|
+
return {
|
|
3084
|
+
partTypes: parts.slice(-12).map((part) => typeof part?.type === "string" ? part.type : "(unknown)"),
|
|
3085
|
+
droppedPartCount: Math.max(0, parts.length - 12),
|
|
3086
|
+
lastToolName: typeof lastTool?.tool === "string" && lastTool.tool.length > 0 ? lastTool.tool : null,
|
|
3087
|
+
lastToolStatus: typeof lastTool?.state?.status === "string" && lastTool.state.status.length > 0 ? lastTool.state.status : null
|
|
3088
|
+
};
|
|
3040
3089
|
}
|
|
3041
3090
|
function isAbortedTerminalReply(messages, userMessageId) {
|
|
3042
3091
|
const reply = findLastAssistantReplyFor(messages, userMessageId);
|
|
@@ -4320,9 +4369,12 @@ function runSynchroniser(args, opts) {
|
|
|
4320
4369
|
|
|
4321
4370
|
// src/lib/opencode/session-db-boot.ts
|
|
4322
4371
|
var SESSION_DB_RESTORE_TIMEOUT_MS = 3e5;
|
|
4323
|
-
var SESSION_DB_VERIFY_TIMEOUT_MS =
|
|
4372
|
+
var SESSION_DB_VERIFY_TIMEOUT_MS = 36e4;
|
|
4324
4373
|
var SESSION_DB_SYNCHRONISER_TIMEOUT_MS = 12e4;
|
|
4325
|
-
var SESSION_DB_VERIFY_WALKBACK_BUDGET_SECONDS =
|
|
4374
|
+
var SESSION_DB_VERIFY_WALKBACK_BUDGET_SECONDS = 40;
|
|
4375
|
+
var SESSION_DB_VERIFY_RESTORE_POINTS_TIMEOUT_SECONDS = 30;
|
|
4376
|
+
var SESSION_DB_VERIFY_RESTORE_CANDIDATE_TIMEOUT_SECONDS = 120;
|
|
4377
|
+
var SESSION_DB_VERIFY_DISPOSAL_BUDGET_SECONDS = 90;
|
|
4326
4378
|
function commandError(result) {
|
|
4327
4379
|
return result.stderr.trim() || `process exited with code ${result.code ?? "null"}`;
|
|
4328
4380
|
}
|
|
@@ -4633,20 +4685,37 @@ async function verifySessionDb(options, configPath, env) {
|
|
|
4633
4685
|
timeoutMs: SESSION_DB_VERIFY_TIMEOUT_MS,
|
|
4634
4686
|
env: {
|
|
4635
4687
|
...env,
|
|
4636
|
-
// The synchroniser reads
|
|
4637
|
-
// 120_000, so the walkback gives up before the outer process bound.
|
|
4688
|
+
// The synchroniser reads all four values in SECONDS.
|
|
4638
4689
|
EVIDENT_SESSION_DB_WALKBACK_BUDGET_SECONDS: String(
|
|
4639
4690
|
SESSION_DB_VERIFY_WALKBACK_BUDGET_SECONDS
|
|
4691
|
+
),
|
|
4692
|
+
EVIDENT_SESSION_DB_RESTORE_POINTS_TIMEOUT_SECONDS: String(
|
|
4693
|
+
SESSION_DB_VERIFY_RESTORE_POINTS_TIMEOUT_SECONDS
|
|
4694
|
+
),
|
|
4695
|
+
EVIDENT_SESSION_DB_RESTORE_CANDIDATE_TIMEOUT_SECONDS: String(
|
|
4696
|
+
SESSION_DB_VERIFY_RESTORE_CANDIDATE_TIMEOUT_SECONDS
|
|
4697
|
+
),
|
|
4698
|
+
EVIDENT_SESSION_DB_DISPOSAL_BUDGET_SECONDS: String(
|
|
4699
|
+
SESSION_DB_VERIFY_DISPOSAL_BUDGET_SECONDS
|
|
4640
4700
|
)
|
|
4641
4701
|
}
|
|
4642
4702
|
});
|
|
4643
4703
|
logSynchroniserDiagnostics(result, options);
|
|
4644
4704
|
if (result.timedOut || result.code === 124 || result.code === 137) {
|
|
4705
|
+
if (result.stderr.includes("SESSION-DB-INTEGRITY-FAILED")) {
|
|
4706
|
+
const reason = result.stderr.includes("SESSION-DB-REPLICA-SIZE") ? "verification_timeout_during_separation" : "verification_timeout_before_separation";
|
|
4707
|
+
options.log(
|
|
4708
|
+
`SESSION-DB-VERIFY-TIMEOUT: verification did not finish within its ${SESSION_DB_VERIFY_TIMEOUT_MS}ms deadline; refusing to start with the restored opencode.db (${reason})`,
|
|
4709
|
+
"error"
|
|
4710
|
+
);
|
|
4711
|
+
reportRecord("verify", "session_db_boot_refused", reason, null, options);
|
|
4712
|
+
return { verifyFatal: true, verifyFatalReason: reason };
|
|
4713
|
+
}
|
|
4645
4714
|
options.log(
|
|
4646
4715
|
`SESSION-DB-VERIFY-TIMEOUT: verification did not finish within its ${SESSION_DB_VERIFY_TIMEOUT_MS}ms deadline; continuing with the restored opencode.db as-is, unverified`,
|
|
4647
4716
|
"warn"
|
|
4648
4717
|
);
|
|
4649
|
-
return false;
|
|
4718
|
+
return { verifyFatal: false };
|
|
4650
4719
|
}
|
|
4651
4720
|
if (result.code === 34) {
|
|
4652
4721
|
reportRecord(
|
|
@@ -4656,14 +4725,14 @@ async function verifySessionDb(options, configPath, env) {
|
|
|
4656
4725
|
null,
|
|
4657
4726
|
options
|
|
4658
4727
|
);
|
|
4659
|
-
return true;
|
|
4728
|
+
return { verifyFatal: true };
|
|
4660
4729
|
}
|
|
4661
4730
|
if (result.code === 33) {
|
|
4662
4731
|
options.log(
|
|
4663
4732
|
"SESSION-DB-INTEGRITY-EXHAUSTED: booting continues and litestream still replicates, starting an empty backup chain after the corrupt replica was separated.",
|
|
4664
4733
|
"warn"
|
|
4665
4734
|
);
|
|
4666
|
-
return false;
|
|
4735
|
+
return { verifyFatal: false };
|
|
4667
4736
|
}
|
|
4668
4737
|
if (result.code !== 0) {
|
|
4669
4738
|
options.log(
|
|
@@ -4671,13 +4740,13 @@ async function verifySessionDb(options, configPath, env) {
|
|
|
4671
4740
|
"warn"
|
|
4672
4741
|
);
|
|
4673
4742
|
}
|
|
4674
|
-
return false;
|
|
4743
|
+
return { verifyFatal: false };
|
|
4675
4744
|
}
|
|
4676
4745
|
options.log(
|
|
4677
4746
|
"skipping session-DB verification: this boot's session DB was not proven safe to replicate",
|
|
4678
4747
|
"debug"
|
|
4679
4748
|
);
|
|
4680
|
-
return false;
|
|
4749
|
+
return { verifyFatal: false };
|
|
4681
4750
|
}
|
|
4682
4751
|
function fileExists(path) {
|
|
4683
4752
|
try {
|
|
@@ -4764,7 +4833,7 @@ async function restoreAndVerifySessionDb(options) {
|
|
|
4764
4833
|
if (options.noReplicateMarker && fileExists(options.noReplicateMarker)) {
|
|
4765
4834
|
return { verifyFatal: false };
|
|
4766
4835
|
}
|
|
4767
|
-
return
|
|
4836
|
+
return verifySessionDb(options, configPath, env);
|
|
4768
4837
|
}
|
|
4769
4838
|
|
|
4770
4839
|
// src/lib/opencode/session-db-provenance.ts
|
|
@@ -7016,6 +7085,9 @@ var SUBAGENT_TELEMETRY_FETCH_TIMEOUT_MS = 2e3;
|
|
|
7016
7085
|
var SESSION_ERROR_STREAM_HEALTHY_MS = 5e3;
|
|
7017
7086
|
var SESSION_ERROR_BUFFER_TTL_MS = SESSION_ERROR_STREAM_HEALTHY_MS;
|
|
7018
7087
|
var MAX_BUFFERED_SESSION_ERRORS = 256;
|
|
7088
|
+
var SESSION_ERROR_RETENTION_TTL_MS = 6e4;
|
|
7089
|
+
var SESSION_STORE_UNWRITABLE_ERROR = "The runner session database could not be written, so this turn did not finish. Nothing was lost from the queue. Clear disk space and retry.";
|
|
7090
|
+
var SESSION_ERROR_RETAINED_DETAIL = "The runner's session database could not be written, so this turn stopped part-way. Runner-observed session error: ";
|
|
7019
7091
|
var DEFAULT_PAUSED_POLL_INTERVAL_MS = 2e3;
|
|
7020
7092
|
var DEFAULT_PAUSED_MAX_WAIT_MS = 10 * 60 * 1e3;
|
|
7021
7093
|
var DRIVER_SESSION_ENUMERATION_LIMIT = 20;
|
|
@@ -7173,6 +7245,7 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
7173
7245
|
* with a short TTL so an unmatched session cannot retain an event indefinitely.
|
|
7174
7246
|
*/
|
|
7175
7247
|
bufferedSessionErrors = /* @__PURE__ */ new Map();
|
|
7248
|
+
retainedSessionErrors = /* @__PURE__ */ new Map();
|
|
7176
7249
|
/**
|
|
7177
7250
|
* AUTHORITATIVE local dedup (WI-3): Evident message ids that have been
|
|
7178
7251
|
* dispatched and are still in-flight. A message in this set is never
|
|
@@ -9013,6 +9086,28 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
9013
9086
|
this.bufferedSessionErrors.delete(oldest);
|
|
9014
9087
|
}
|
|
9015
9088
|
}
|
|
9089
|
+
retainSessionError(event) {
|
|
9090
|
+
this.retainedSessionErrors.delete(event.sessionId);
|
|
9091
|
+
this.retainedSessionErrors.set(event.sessionId, {
|
|
9092
|
+
reason: event.reason,
|
|
9093
|
+
observedAt: this.now()
|
|
9094
|
+
});
|
|
9095
|
+
while (this.retainedSessionErrors.size > MAX_BUFFERED_SESSION_ERRORS) {
|
|
9096
|
+
const oldest = this.retainedSessionErrors.keys().next().value;
|
|
9097
|
+
if (typeof oldest !== "string") break;
|
|
9098
|
+
this.retainedSessionErrors.delete(oldest);
|
|
9099
|
+
}
|
|
9100
|
+
}
|
|
9101
|
+
readRetainedSessionError(sessionId) {
|
|
9102
|
+
const retained = this.retainedSessionErrors.get(sessionId);
|
|
9103
|
+
if (!retained) return null;
|
|
9104
|
+
if (this.now() - retained.observedAt >= SESSION_ERROR_RETENTION_TTL_MS) {
|
|
9105
|
+
this.retainedSessionErrors.delete(sessionId);
|
|
9106
|
+
return null;
|
|
9107
|
+
}
|
|
9108
|
+
this.retainedSessionErrors.delete(sessionId);
|
|
9109
|
+
return retained.reason;
|
|
9110
|
+
}
|
|
9016
9111
|
/**
|
|
9017
9112
|
* Build a fresh `SessionWatcher`. Seeds `lastTickAt`/`lastObservedTickAt`
|
|
9018
9113
|
* EQUAL (#1618) so a watcher whose loop has not started ticking yet is never
|
|
@@ -9339,9 +9434,10 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
9339
9434
|
return;
|
|
9340
9435
|
}
|
|
9341
9436
|
if ([...watcher.inFlight.values()].some((message) => message.started && !message.done)) {
|
|
9437
|
+
this.retainSessionError(event);
|
|
9342
9438
|
this.log({
|
|
9343
|
-
level: "
|
|
9344
|
-
message: `A turn is already running in session ${event.sessionId.slice(0, 8)} \u2014 deferring to transcript polling`,
|
|
9439
|
+
level: "warn",
|
|
9440
|
+
message: `A turn is already running in session ${event.sessionId.slice(0, 8)} \u2014 retaining the session error and deferring to transcript polling: ${event.reason}`,
|
|
9345
9441
|
conversation_id: watcher.conv.id
|
|
9346
9442
|
});
|
|
9347
9443
|
return;
|
|
@@ -9371,9 +9467,10 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
9371
9467
|
const messages = await this.getSessionMessages(event.sessionId);
|
|
9372
9468
|
const state = messageRunState(messages, inFlight.opencodeMessageId);
|
|
9373
9469
|
if (state !== "queued") {
|
|
9470
|
+
this.retainSessionError(event);
|
|
9374
9471
|
this.log({
|
|
9375
|
-
level: "
|
|
9376
|
-
message: `Session error for message ${inFlight.evidentMessageId.slice(0, 8)} observed state ${state};
|
|
9472
|
+
level: "warn",
|
|
9473
|
+
message: `Session error for message ${inFlight.evidentMessageId.slice(0, 8)} in session ${event.sessionId.slice(0, 8)} observed state ${state}; retaining the reason for transcript polling: ${event.reason}`,
|
|
9377
9474
|
conversation_id: watcher.conv.id,
|
|
9378
9475
|
message_id: inFlight.evidentMessageId
|
|
9379
9476
|
});
|
|
@@ -9602,7 +9699,14 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
9602
9699
|
if (state === "failed") {
|
|
9603
9700
|
this.anchorDeliveryDeadline(inFlight);
|
|
9604
9701
|
if (!inFlight.done) {
|
|
9605
|
-
const
|
|
9702
|
+
const messageErrorText = messageError(messages, inFlight.opencodeMessageId);
|
|
9703
|
+
const retainedReason = this.readRetainedSessionError(sessionId);
|
|
9704
|
+
const retainedError = retainedReason === null ? null : `${SESSION_ERROR_RETAINED_DETAIL}${retainedReason}`;
|
|
9705
|
+
const storeError = this.sessionStoreHealth?.isUnwritable() ? SESSION_STORE_UNWRITABLE_ERROR : null;
|
|
9706
|
+
const runnerDetails = [retainedError, storeError].filter(
|
|
9707
|
+
(detail) => detail !== null
|
|
9708
|
+
);
|
|
9709
|
+
const error2 = messageErrorText === null ? runnerDetails.length === 0 ? void 0 : runnerDetails.join(" ") : [messageErrorText, ...runnerDetails].join(" ");
|
|
9606
9710
|
this.log({
|
|
9607
9711
|
level: "error",
|
|
9608
9712
|
message: `Message ${inFlight.evidentMessageId.slice(0, 8)} errored \u2014 marking failed: ${error2 ?? "(no error text)"}`,
|
|
@@ -10045,7 +10149,9 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
10045
10149
|
/** Report a confirmed b2 abandonment that produced no answer text. */
|
|
10046
10150
|
async settleMessageAbandonedWithoutAnswer(sessionId, watcher, inFlight, messages, finish) {
|
|
10047
10151
|
const conv = watcher.conv;
|
|
10048
|
-
const
|
|
10152
|
+
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.`;
|
|
10153
|
+
const replyShape = describeAbandonedReplyShape(messages, inFlight.opencodeMessageId);
|
|
10154
|
+
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)` : ""}.`;
|
|
10049
10155
|
this.anchorDeliveryDeadline(inFlight);
|
|
10050
10156
|
this.log({
|
|
10051
10157
|
level: "warn",
|
|
@@ -10172,7 +10278,7 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
10172
10278
|
*/
|
|
10173
10279
|
async settleMessageSessionStoreUnwritable(sessionId, watcher, inFlight) {
|
|
10174
10280
|
const conv = watcher.conv;
|
|
10175
|
-
const error2 =
|
|
10281
|
+
const error2 = SESSION_STORE_UNWRITABLE_ERROR;
|
|
10176
10282
|
this.log({
|
|
10177
10283
|
level: "error",
|
|
10178
10284
|
message: `Message ${inFlight.evidentMessageId.slice(0, 8)} was not resolved as done: ${error2}`,
|
|
@@ -14483,6 +14589,7 @@ async function run(options) {
|
|
|
14483
14589
|
openCodeLogTail.stop();
|
|
14484
14590
|
};
|
|
14485
14591
|
let sessionDbVerifyFatal = false;
|
|
14592
|
+
let sessionDbVerifyFatalReason;
|
|
14486
14593
|
if (!options.restoreSessionDb) {
|
|
14487
14594
|
log2(state, "Skipping session-DB restore: --restore-session-db was not passed", "debug");
|
|
14488
14595
|
} else {
|
|
@@ -14515,12 +14622,13 @@ async function run(options) {
|
|
|
14515
14622
|
reportRecovery: (record) => reportSessionDbRecoveryRecord(state, record)
|
|
14516
14623
|
});
|
|
14517
14624
|
sessionDbVerifyFatal = result.verifyFatal;
|
|
14625
|
+
sessionDbVerifyFatalReason = result.verifyFatalReason;
|
|
14518
14626
|
}
|
|
14519
14627
|
}
|
|
14520
14628
|
reportSessionDbRecovery(state);
|
|
14521
14629
|
if (sessionDbVerifyFatal) {
|
|
14522
14630
|
throw new Error(
|
|
14523
|
-
"SESSION-DB-INTEGRITY-EXHAUSTED: the corrupt session DB could not be proven separated from the active backup prefix or removed from disk"
|
|
14631
|
+
sessionDbVerifyFatalReason === void 0 ? "SESSION-DB-INTEGRITY-EXHAUSTED: the corrupt session DB could not be proven separated from the active backup prefix or removed from disk" : `SESSION-DB-VERIFY-TIMEOUT: session DB verification timed out (${sessionDbVerifyFatalReason}); refusing to start`
|
|
14524
14632
|
);
|
|
14525
14633
|
}
|
|
14526
14634
|
applyRunnerOpenCodeConfig({
|