@evident-ai/cli 3.1.1-dev.1e270a5 → 3.1.1-dev.201318e
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 +123 -17
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -797,6 +797,16 @@ async function checkStatus(jsonMode) {
|
|
|
797
797
|
exitCode: 1
|
|
798
798
|
};
|
|
799
799
|
}
|
|
800
|
+
if (response.status === 404) {
|
|
801
|
+
return {
|
|
802
|
+
ok: false,
|
|
803
|
+
endpoint: apiUrl,
|
|
804
|
+
authLabel: authLabelFor(credentials2),
|
|
805
|
+
reason: "endpoint_not_found",
|
|
806
|
+
error: `${apiUrl}/me returned HTTP 404 \u2014 that endpoint has no /me route, so it is probably missing the /v1 prefix. The credentials were NOT validated.`,
|
|
807
|
+
exitCode: 75
|
|
808
|
+
};
|
|
809
|
+
}
|
|
800
810
|
if (response.status >= 500) {
|
|
801
811
|
const serverMessage = await readErrorMessage(response);
|
|
802
812
|
return {
|
|
@@ -3318,6 +3328,23 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
3318
3328
|
* `clearRedriveUnresolved` the instant either PATCH succeeds.
|
|
3319
3329
|
*/
|
|
3320
3330
|
redriveOutcomeUnreportedSignalled = /* @__PURE__ */ new Map();
|
|
3331
|
+
/**
|
|
3332
|
+
* First `now()` a Class B outcome PATCH (reattach/settle/fail_permanent) was
|
|
3333
|
+
* observed to fail for this message (#1366's failure-window trip arm,
|
|
3334
|
+
* `boundRedriveOutcome`). Duration, not a tick count — bounded by the
|
|
3335
|
+
* existing `pausedMaxWaitMs` window (reusing the knob, not a new constant).
|
|
3336
|
+
* Cleared by `clearRedriveUnresolved` the instant the original PATCH
|
|
3337
|
+
* succeeds.
|
|
3338
|
+
*/
|
|
3339
|
+
redriveOutcomeFailingSince = /* @__PURE__ */ new Map();
|
|
3340
|
+
/**
|
|
3341
|
+
* "Already posted `redrive_outcome_abandoned` with `reported: false` for this
|
|
3342
|
+
* row" (#1366) — the bound tripped but the terminal `markFailed` fallback ALSO
|
|
3343
|
+
* failed (the route-level fault of G2), so every following tick re-attempts
|
|
3344
|
+
* the same terminal PATCH. Guards that quiet retry from re-signalling on
|
|
3345
|
+
* every tick. Cleared by `clearRedriveUnresolved`.
|
|
3346
|
+
*/
|
|
3347
|
+
redriveOutcomeAbandonedSignalled = /* @__PURE__ */ new Set();
|
|
3321
3348
|
/**
|
|
3322
3349
|
* Consecutive-UNCONFIRMED-dispatch streak for a `pending` row with NO stored
|
|
3323
3350
|
* `opencode_message_id` yet — i.e. one that has never even reached the
|
|
@@ -3674,6 +3701,9 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
3674
3701
|
}
|
|
3675
3702
|
if (message.opencode_message_id) {
|
|
3676
3703
|
const outcome = await this.resolveRedrive(conv, sessionId, message, sessionCreated);
|
|
3704
|
+
if (outcome === "abandoned") {
|
|
3705
|
+
continue;
|
|
3706
|
+
}
|
|
3677
3707
|
if (outcome !== "dispatch") {
|
|
3678
3708
|
break;
|
|
3679
3709
|
}
|
|
@@ -3943,8 +3973,8 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
3943
3973
|
conversation_id: conv.id,
|
|
3944
3974
|
message_id: message.id
|
|
3945
3975
|
});
|
|
3946
|
-
this.
|
|
3947
|
-
return "unresolved";
|
|
3976
|
+
const bound = await this.boundRedriveOutcome(conv, message, "reattach");
|
|
3977
|
+
return bound === "abandoned" ? "abandoned" : "unresolved";
|
|
3948
3978
|
}
|
|
3949
3979
|
this.clearRedriveUnresolved(message.id);
|
|
3950
3980
|
this.registerReadopted(conv, sessionId, message, ocId ?? "", anchorMs);
|
|
@@ -4004,8 +4034,8 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
4004
4034
|
conversation_id: conv.id,
|
|
4005
4035
|
message_id: message.id
|
|
4006
4036
|
});
|
|
4007
|
-
this.
|
|
4008
|
-
return "unresolved";
|
|
4037
|
+
const bound = await this.boundRedriveOutcome(conv, message, "settle");
|
|
4038
|
+
return bound === "abandoned" ? "abandoned" : "unresolved";
|
|
4009
4039
|
}
|
|
4010
4040
|
this.clearRedriveUnresolved(message.id);
|
|
4011
4041
|
void this.postSignal(conv.id, message.id, "redrive_settled");
|
|
@@ -4044,8 +4074,8 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
4044
4074
|
conversation_id: conv.id,
|
|
4045
4075
|
message_id: message.id
|
|
4046
4076
|
});
|
|
4047
|
-
this.
|
|
4048
|
-
return "unresolved";
|
|
4077
|
+
const bound = await this.boundRedriveOutcome(conv, message, "fail_permanent");
|
|
4078
|
+
return bound === "abandoned" ? "abandoned" : "unresolved";
|
|
4049
4079
|
}
|
|
4050
4080
|
this.clearRedriveUnresolved(message.id);
|
|
4051
4081
|
void this.postSignal(conv.id, message.id, "redrive_poll_failed");
|
|
@@ -4054,10 +4084,12 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
4054
4084
|
/**
|
|
4055
4085
|
* The bounded `unresolved` outcome (Task 3.4): opencode's state could not be
|
|
4056
4086
|
* observed (snapshot unreadable/empty, or `isSessionOngoing` returned `null`).
|
|
4057
|
-
* A `pending` row is
|
|
4058
|
-
*
|
|
4059
|
-
*
|
|
4060
|
-
*
|
|
4087
|
+
* A `pending` row is swept by the server's own `PENDING_MAX_AGE_MS` (24h,
|
|
4088
|
+
* #1368) cron arm, but that is a day-scale backstop — this local bound acts
|
|
4089
|
+
* in minutes so the row (and the conversation it starves, per the ordering
|
|
4090
|
+
* invariant below) isn't left stranded for that long. Bound to the existing
|
|
4091
|
+
* `pausedMaxWaitMs` window (reusing the knob, not a new constant); takes
|
|
4092
|
+
* `dispatch` once elapsed.
|
|
4061
4093
|
*/
|
|
4062
4094
|
resolveRedriveUnresolved(conv, message) {
|
|
4063
4095
|
const now = this.now();
|
|
@@ -4082,16 +4114,15 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
4082
4114
|
this.redriveUnresolvedSignalled.delete(messageId);
|
|
4083
4115
|
this.redrivePollFailures.delete(messageId);
|
|
4084
4116
|
this.redriveOutcomeUnreportedSignalled.delete(messageId);
|
|
4117
|
+
this.redriveOutcomeFailingSince.delete(messageId);
|
|
4118
|
+
this.redriveOutcomeAbandonedSignalled.delete(messageId);
|
|
4085
4119
|
}
|
|
4086
4120
|
/**
|
|
4087
4121
|
* Class B (#1340): the runner DECIDED an outcome (reattach/settle/fail_permanent)
|
|
4088
|
-
* but its own PATCH to record it failed.
|
|
4089
|
-
*
|
|
4090
|
-
*
|
|
4091
|
-
*
|
|
4092
|
-
* (the next successful PATCH resolves it via `clearRedriveUnresolved`),
|
|
4093
|
-
* unlike #1340's deterministic, permanent shape. Fires at most once per
|
|
4094
|
-
* (message, outcome) streak.
|
|
4122
|
+
* but its own PATCH to record it failed. Fires at most once per (message,
|
|
4123
|
+
* outcome) streak, and only while `boundRedriveOutcome` has not yet tripped —
|
|
4124
|
+
* once it trips, `redrive_outcome_abandoned` takes over reporting for the row
|
|
4125
|
+
* (#1366).
|
|
4095
4126
|
*/
|
|
4096
4127
|
signalRedriveOutcomeUnreported(conv, message, outcome) {
|
|
4097
4128
|
if (this.redriveOutcomeUnreportedSignalled.get(message.id) === outcome) return;
|
|
@@ -4100,6 +4131,81 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
4100
4131
|
attempted_outcome: outcome
|
|
4101
4132
|
});
|
|
4102
4133
|
}
|
|
4134
|
+
/**
|
|
4135
|
+
* The runner-authored, honest error text for the terminal fallback a tripped
|
|
4136
|
+
* `boundRedriveOutcome` sends. Distinguishable per outcome and truthful about
|
|
4137
|
+
* what actually happened — the `settle`/done case must say the turn finished
|
|
4138
|
+
* but its result could not be recorded, never that the runner stopped
|
|
4139
|
+
* responding (that would be a lie for this shape, see #1366's "why this ships").
|
|
4140
|
+
*/
|
|
4141
|
+
static REDRIVE_ABANDON_ERROR = {
|
|
4142
|
+
reattach: "your runner could not record that this message had started, so it was given up on",
|
|
4143
|
+
settle: "your runner finished this message but could not record the result, so the reply could not be delivered",
|
|
4144
|
+
fail_permanent: "the runner could not read this conversation's state from OpenCode, and could not record that failure either, so the message was given up on"
|
|
4145
|
+
};
|
|
4146
|
+
/**
|
|
4147
|
+
* Bound for Class B (#1340, #1366): the runner DECIDED an outcome but its own
|
|
4148
|
+
* PATCH to record it failed. Two independent trip arms (either sufficient):
|
|
4149
|
+
* (1) this failure streak has lasted `pausedMaxWaitMs` — DURATION, not a tick
|
|
4150
|
+
* count, reusing the knob `resolveRedriveUnresolved` already established; (2)
|
|
4151
|
+
* the turn's `processing_started_at` age has crossed
|
|
4152
|
+
* `ABSOLUTE_MAX_PROCESSING_MS` — durable and restart-surviving, since arm (1)'s
|
|
4153
|
+
* in-memory streak resets on a scale-to-zero restart.
|
|
4154
|
+
*
|
|
4155
|
+
* INVARIANT — a tripped bound never suppresses the original outcome attempt;
|
|
4156
|
+
* it only adds a fallback after that attempt has failed again. This is only
|
|
4157
|
+
* ever reached from inside the catch of the ORIGINAL outcome PATCH, which is
|
|
4158
|
+
* attempted first on every tick whether or not this bound tripped before —
|
|
4159
|
+
* there is no give-up latch that would short-circuit it. That is what lets a
|
|
4160
|
+
* route-level fault that heals later still deliver the turn's real
|
|
4161
|
+
* `done`/`failed` payload: once the original PATCH succeeds again, this
|
|
4162
|
+
* helper is never entered and the row settles with its real result.
|
|
4163
|
+
*/
|
|
4164
|
+
async boundRedriveOutcome(conv, message, outcome) {
|
|
4165
|
+
const now = this.now();
|
|
4166
|
+
const since = this.redriveOutcomeFailingSince.get(message.id);
|
|
4167
|
+
if (since === void 0) this.redriveOutcomeFailingSince.set(message.id, now);
|
|
4168
|
+
const durationTripped = now - (since ?? now) >= this.pausedMaxWaitMs;
|
|
4169
|
+
const parsed = message.processing_started_at ? Date.parse(message.processing_started_at) : NaN;
|
|
4170
|
+
const absoluteAgeTripped = !Number.isNaN(parsed) && now - parsed >= ABSOLUTE_MAX_PROCESSING_MS;
|
|
4171
|
+
if (!durationTripped && !absoluteAgeTripped) {
|
|
4172
|
+
this.signalRedriveOutcomeUnreported(conv, message, outcome);
|
|
4173
|
+
return "retry";
|
|
4174
|
+
}
|
|
4175
|
+
const arm = durationTripped ? "failure_window" : "absolute_age";
|
|
4176
|
+
try {
|
|
4177
|
+
await this.markFailed(
|
|
4178
|
+
conv.id,
|
|
4179
|
+
message.id,
|
|
4180
|
+
void 0,
|
|
4181
|
+
_ChannelDriver.REDRIVE_ABANDON_ERROR[outcome]
|
|
4182
|
+
);
|
|
4183
|
+
} catch (err) {
|
|
4184
|
+
if (err instanceof ChannelAuthError) throw err;
|
|
4185
|
+
this.log({
|
|
4186
|
+
level: "warn",
|
|
4187
|
+
message: `Re-drive bound: fallback markFailed for message ${message.id.slice(0, 8)} also failed (arm ${arm}, will retry next drain): ${err instanceof Error ? err.message : String(err)}`,
|
|
4188
|
+
conversation_id: conv.id,
|
|
4189
|
+
message_id: message.id
|
|
4190
|
+
});
|
|
4191
|
+
if (!this.redriveOutcomeAbandonedSignalled.has(message.id)) {
|
|
4192
|
+
this.redriveOutcomeAbandonedSignalled.add(message.id);
|
|
4193
|
+
void this.postSignal(conv.id, message.id, "redrive_outcome_abandoned", {
|
|
4194
|
+
attempted_outcome: outcome,
|
|
4195
|
+
reported: false,
|
|
4196
|
+
arm
|
|
4197
|
+
});
|
|
4198
|
+
}
|
|
4199
|
+
return "retry";
|
|
4200
|
+
}
|
|
4201
|
+
this.clearRedriveUnresolved(message.id);
|
|
4202
|
+
void this.postSignal(conv.id, message.id, "redrive_outcome_abandoned", {
|
|
4203
|
+
attempted_outcome: outcome,
|
|
4204
|
+
reported: true,
|
|
4205
|
+
arm
|
|
4206
|
+
});
|
|
4207
|
+
return "abandoned";
|
|
4208
|
+
}
|
|
4103
4209
|
/**
|
|
4104
4210
|
* Record one poll outcome toward the re-drive fence's consecutive-identical-
|
|
4105
4211
|
* failure streak (#1348) and return the resulting count. `signature === null`
|