@evident-ai/cli 3.1.1-dev.019c4e9 → 3.1.1-dev.0d69ecc

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
@@ -1548,6 +1548,42 @@ function messageError(messages, userMessageId) {
1548
1548
  }
1549
1549
  return "The agent run failed.";
1550
1550
  }
1551
+ function messageFailure(messages, userMessageId) {
1552
+ const reply = findLastAssistantReplyFor(messages, userMessageId);
1553
+ const error2 = errorOf(reply);
1554
+ if (error2 == null || typeof error2 !== "object") return null;
1555
+ const e = error2;
1556
+ const replyProviderId = reply?.info?.providerID ?? null;
1557
+ const replyModelId = reply?.info?.modelID ?? null;
1558
+ if (e.name === "ProviderAuthError") {
1559
+ const data = e.data;
1560
+ const providerId = typeof data?.providerID === "string" && data.providerID || replyProviderId;
1561
+ return { kind: "model_auth", providerId, modelId: replyModelId, reason: "missing" };
1562
+ }
1563
+ if (e.name === "APIError") {
1564
+ const data = e.data;
1565
+ const statusCode = data?.statusCode;
1566
+ if (statusCode === 401 || statusCode === 403) {
1567
+ return {
1568
+ kind: "model_auth",
1569
+ providerId: replyProviderId,
1570
+ modelId: replyModelId,
1571
+ reason: "rejected"
1572
+ };
1573
+ }
1574
+ }
1575
+ return null;
1576
+ }
1577
+ function applyZeroProviderFallback(classified, hasConfiguredProvider, replyProviderId, replyModelId = null) {
1578
+ if (classified != null) return classified;
1579
+ if (hasConfiguredProvider !== false) return null;
1580
+ return {
1581
+ kind: "model_auth",
1582
+ providerId: replyProviderId,
1583
+ modelId: replyModelId,
1584
+ reason: "missing"
1585
+ };
1586
+ }
1551
1587
  function hasRunningAssistantExcept(messages, exceptUserMessageId) {
1552
1588
  if (!messages || messages.length === 0) return false;
1553
1589
  return messages.some(
@@ -3598,8 +3634,16 @@ var ChannelDriver = class _ChannelDriver {
3598
3634
  message_id: inFlight.evidentMessageId
3599
3635
  });
3600
3636
  const usage = messageUsage(messages, inFlight.opencodeMessageId);
3637
+ const failure = await this.classifyModelAuthFailure(messages, inFlight.opencodeMessageId);
3601
3638
  try {
3602
- await this.markFailed(conv.id, inFlight.evidentMessageId, sessionId, error2, usage);
3639
+ await this.markFailed(
3640
+ conv.id,
3641
+ inFlight.evidentMessageId,
3642
+ sessionId,
3643
+ error2,
3644
+ usage,
3645
+ failure
3646
+ );
3603
3647
  } catch (err) {
3604
3648
  if (err instanceof ChannelAuthError) throw err;
3605
3649
  if (err instanceof ChannelTerminalError) {
@@ -3980,6 +4024,7 @@ var ChannelDriver = class _ChannelDriver {
3980
4024
  if (state === "failed") {
3981
4025
  const error2 = messageError(messages, ocId ?? "") ?? void 0;
3982
4026
  const usage = messageUsage(messages, ocId ?? "");
4027
+ const failure = await this.classifyModelAuthFailure(messages, ocId ?? "");
3983
4028
  this.log({
3984
4029
  level: "error",
3985
4030
  message: `Re-adopt: message ${row.id.slice(0, 8)} errored while unwatched \u2014 marking failed: ${error2 ?? "(no error text)"}`,
@@ -3987,7 +4032,7 @@ var ChannelDriver = class _ChannelDriver {
3987
4032
  message_id: row.id
3988
4033
  });
3989
4034
  try {
3990
- await this.markFailed(row.conversation_id, row.id, sessionId, error2, usage);
4035
+ await this.markFailed(row.conversation_id, row.id, sessionId, error2, usage, failure);
3991
4036
  } catch (err) {
3992
4037
  if (err instanceof ChannelAuthError) throw err;
3993
4038
  if (err instanceof ChannelTerminalError) {
@@ -4965,7 +5010,7 @@ var ChannelDriver = class _ChannelDriver {
4965
5010
  * exists but is wedged, so the next attempt must get a fresh one
4966
5011
  * instead of reusing it — see WI-1's server-side null-clearing PATCH).
4967
5012
  */
4968
- async markFailed(conversationId, messageId, sessionId, error2, usage) {
5013
+ async markFailed(conversationId, messageId, sessionId, error2, usage, failure) {
4969
5014
  const body = { status: "failed" };
4970
5015
  if (sessionId === null) {
4971
5016
  body.opencode_session_id = null;
@@ -4974,6 +5019,12 @@ var ChannelDriver = class _ChannelDriver {
4974
5019
  }
4975
5020
  if (error2 !== void 0) body.error = error2;
4976
5021
  if (usage) Object.assign(body, usage);
5022
+ if (failure) {
5023
+ body.failure_kind = failure.kind;
5024
+ body.failure_provider_id = failure.providerId;
5025
+ body.failure_model_id = failure.modelId;
5026
+ body.failure_reason = failure.reason;
5027
+ }
4977
5028
  await this.callWithRetry(
4978
5029
  "marking message as failed",
4979
5030
  () => this.fetchImpl(
@@ -4986,6 +5037,29 @@ var ChannelDriver = class _ChannelDriver {
4986
5037
  )
4987
5038
  );
4988
5039
  }
5040
+ /**
5041
+ * Classify an errored turn as a model-auth failure (#736 P1-4), for `markFailed`.
5042
+ *
5043
+ * `messageFailure` alone (structured OpenCode error → `model_auth`) covers
5044
+ * most cases; when it returns `null` on this ALREADY-FAILED turn, fall back
5045
+ * to the P1-2b zero-provider check — one extra loopback call to
5046
+ * `hasAnyConfiguredProvider`, only reached when the structured classifier
5047
+ * couldn't place it. Fails open (never throws): a fallback probe failure
5048
+ * (`null`/indeterminate) leaves the classification `null`, which produces
5049
+ * today's byte-identical PATCH body via `markFailed`'s `if (failure)` guard.
5050
+ */
5051
+ async classifyModelAuthFailure(messages, userMessageId) {
5052
+ const classified = messageFailure(messages, userMessageId);
5053
+ if (classified != null) return classified;
5054
+ const reply = findLastAssistantReplyFor(messages, userMessageId);
5055
+ const hasProvider = await hasAnyConfiguredProvider(this.port);
5056
+ return applyZeroProviderFallback(
5057
+ classified,
5058
+ hasProvider,
5059
+ reply?.info?.providerID ?? null,
5060
+ reply?.info?.modelID ?? null
5061
+ );
5062
+ }
4989
5063
  /**
4990
5064
  * Best-effort, SINGLE-ATTEMPT runner→server telemetry ping
4991
5065
  * (queued-followup-redrive). `POST .../messages/:id/signal {signal, ...extra}`