@evident-ai/cli 3.4.1-dev.6e623a8 → 3.4.1-dev.71ade33
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 +105 -37
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1138,6 +1138,37 @@ var TelemetryEventTypes = {
|
|
|
1138
1138
|
RUNNER_ACTIVITY: "runner.activity"
|
|
1139
1139
|
};
|
|
1140
1140
|
|
|
1141
|
+
// ../../packages/types/src/tunnel/binary-frame.ts
|
|
1142
|
+
var BINARY_FRAME_REQ_DATA = 1;
|
|
1143
|
+
var BINARY_FRAME_RES_DATA = 2;
|
|
1144
|
+
var textEncoder = new TextEncoder();
|
|
1145
|
+
var textDecoder = new TextDecoder();
|
|
1146
|
+
function encodeBinaryBodyFrame(type, sid, payload) {
|
|
1147
|
+
const sidBytes = textEncoder.encode(sid);
|
|
1148
|
+
if (sidBytes.length === 0 || sidBytes.length > 255) {
|
|
1149
|
+
throw new RangeError("sid must contain between 1 and 255 UTF-8 bytes");
|
|
1150
|
+
}
|
|
1151
|
+
const frame = new Uint8Array(2 + sidBytes.length + payload.length);
|
|
1152
|
+
frame[0] = type;
|
|
1153
|
+
frame[1] = sidBytes.length;
|
|
1154
|
+
frame.set(sidBytes, 2);
|
|
1155
|
+
frame.set(payload, 2 + sidBytes.length);
|
|
1156
|
+
return frame;
|
|
1157
|
+
}
|
|
1158
|
+
function decodeBinaryBodyFrame(bytes) {
|
|
1159
|
+
if (bytes.length < 2) return null;
|
|
1160
|
+
const type = bytes[0];
|
|
1161
|
+
if (type !== BINARY_FRAME_REQ_DATA && type !== BINARY_FRAME_RES_DATA) return null;
|
|
1162
|
+
const sidLen = bytes[1];
|
|
1163
|
+
if (sidLen === 0 || bytes.length < 2 + sidLen) return null;
|
|
1164
|
+
const payloadOffset = 2 + sidLen;
|
|
1165
|
+
return {
|
|
1166
|
+
type,
|
|
1167
|
+
sid: textDecoder.decode(bytes.subarray(2, payloadOffset)),
|
|
1168
|
+
payload: bytes.subarray(payloadOffset)
|
|
1169
|
+
};
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1141
1172
|
// ../../packages/types/src/tunnel/index.ts
|
|
1142
1173
|
var MAX_FRAME_BYTES = 256 * 1024;
|
|
1143
1174
|
var TUNNEL_DRAIN_PING_PATH = "/__evident/drain";
|
|
@@ -1182,7 +1213,7 @@ import ora4 from "ora";
|
|
|
1182
1213
|
import { select as select4 } from "@inquirer/prompts";
|
|
1183
1214
|
|
|
1184
1215
|
// src/lib/telemetry.ts
|
|
1185
|
-
var CLI_VERSION = (true ? "3.4.1-dev.
|
|
1216
|
+
var CLI_VERSION = (true ? "3.4.1-dev.71ade33" : void 0) ?? process.env.npm_package_version ?? "unknown";
|
|
1186
1217
|
function getCliVersion() {
|
|
1187
1218
|
return CLI_VERSION;
|
|
1188
1219
|
}
|
|
@@ -4720,6 +4751,7 @@ var StreamForwarder = class {
|
|
|
4720
4751
|
this.options = options;
|
|
4721
4752
|
}
|
|
4722
4753
|
inflight = /* @__PURE__ */ new Map();
|
|
4754
|
+
binaryFramesSupported = false;
|
|
4723
4755
|
/**
|
|
4724
4756
|
* Handle an edge→agent frame. Unknown frame types are ignored.
|
|
4725
4757
|
*/
|
|
@@ -4739,6 +4771,12 @@ var StreamForwarder = class {
|
|
|
4739
4771
|
break;
|
|
4740
4772
|
}
|
|
4741
4773
|
}
|
|
4774
|
+
handleBinaryBodyFrame(sid, payload) {
|
|
4775
|
+
this.inflight.get(sid)?.pushBody?.(payload);
|
|
4776
|
+
}
|
|
4777
|
+
setBinaryFramesSupported(supported) {
|
|
4778
|
+
this.binaryFramesSupported = supported;
|
|
4779
|
+
}
|
|
4742
4780
|
/**
|
|
4743
4781
|
* Abort every in-flight stream (e.g. on WebSocket close).
|
|
4744
4782
|
*/
|
|
@@ -4856,7 +4894,13 @@ var StreamForwarder = class {
|
|
|
4856
4894
|
const chunk = Buffer.from(value);
|
|
4857
4895
|
for (let i = 0; i < chunk.length; i += MAX_FRAME_BYTES) {
|
|
4858
4896
|
const slice = chunk.subarray(i, i + MAX_FRAME_BYTES);
|
|
4859
|
-
this.
|
|
4897
|
+
if (this.binaryFramesSupported) {
|
|
4898
|
+
if (this.ws.readyState === WebSocket.OPEN) {
|
|
4899
|
+
this.ws.send(encodeBinaryBodyFrame(BINARY_FRAME_RES_DATA, sid, slice));
|
|
4900
|
+
}
|
|
4901
|
+
} else {
|
|
4902
|
+
this.send({ type: "res_data", sid, b64: slice.toString("base64") });
|
|
4903
|
+
}
|
|
4860
4904
|
}
|
|
4861
4905
|
}
|
|
4862
4906
|
}
|
|
@@ -4873,6 +4917,11 @@ var StreamForwarder = class {
|
|
|
4873
4917
|
|
|
4874
4918
|
// src/lib/tunnel/connection.ts
|
|
4875
4919
|
var FAILURE_REASON_HEADER_LC = FORWARD_FAILURE_REASON_HEADER.toLowerCase();
|
|
4920
|
+
function toUint8Array(data) {
|
|
4921
|
+
if (Array.isArray(data)) return Buffer.concat(data);
|
|
4922
|
+
if (data instanceof ArrayBuffer) return new Uint8Array(data);
|
|
4923
|
+
return data;
|
|
4924
|
+
}
|
|
4876
4925
|
var TunnelUpgradeRejectedError = class extends Error {
|
|
4877
4926
|
constructor(message, reason) {
|
|
4878
4927
|
super(message);
|
|
@@ -4937,7 +4986,8 @@ function connectTunnel(options) {
|
|
|
4937
4986
|
return new Promise((resolve4, reject) => {
|
|
4938
4987
|
const ws = new WebSocket2(url, {
|
|
4939
4988
|
headers: {
|
|
4940
|
-
Authorization: authHeader
|
|
4989
|
+
Authorization: authHeader,
|
|
4990
|
+
"X-Evident-Tunnel-Binary-Frames": "1"
|
|
4941
4991
|
}
|
|
4942
4992
|
});
|
|
4943
4993
|
const forwarder = new StreamForwarder(
|
|
@@ -4986,7 +5036,25 @@ function connectTunnel(options) {
|
|
|
4986
5036
|
ws.on("open", () => {
|
|
4987
5037
|
onInfo?.("WebSocket connection established");
|
|
4988
5038
|
});
|
|
4989
|
-
ws.on("message", (data) => {
|
|
5039
|
+
ws.on("message", (data, isBinary) => {
|
|
5040
|
+
if (isBinary) {
|
|
5041
|
+
try {
|
|
5042
|
+
const frame = decodeBinaryBodyFrame(toUint8Array(data));
|
|
5043
|
+
if (frame === null) {
|
|
5044
|
+
onError?.("Failed to handle binary message: invalid frame");
|
|
5045
|
+
return;
|
|
5046
|
+
}
|
|
5047
|
+
if (frame.type !== BINARY_FRAME_REQ_DATA) {
|
|
5048
|
+
onError?.("Failed to handle binary message: unexpected frame type");
|
|
5049
|
+
return;
|
|
5050
|
+
}
|
|
5051
|
+
forwarder.handleBinaryBodyFrame(frame.sid, frame.payload);
|
|
5052
|
+
} catch (error2) {
|
|
5053
|
+
const errorMessage3 = error2 instanceof Error ? error2.message : "Unknown error";
|
|
5054
|
+
onError?.(`Failed to handle binary message: ${errorMessage3}`);
|
|
5055
|
+
}
|
|
5056
|
+
return;
|
|
5057
|
+
}
|
|
4990
5058
|
let message;
|
|
4991
5059
|
try {
|
|
4992
5060
|
message = JSON.parse(data.toString());
|
|
@@ -5002,6 +5070,7 @@ function connectTunnel(options) {
|
|
|
5002
5070
|
switch (message.type) {
|
|
5003
5071
|
case "connected": {
|
|
5004
5072
|
clearTimeout(connectionTimeout);
|
|
5073
|
+
forwarder.setBinaryFramesSupported(message.binary_frames === true);
|
|
5005
5074
|
const connectedAgentId = message.agent_id ?? agentId;
|
|
5006
5075
|
onConnected?.(connectedAgentId);
|
|
5007
5076
|
resolve4({
|
|
@@ -6299,23 +6368,21 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
6299
6368
|
*/
|
|
6300
6369
|
readopted = /* @__PURE__ */ new Set();
|
|
6301
6370
|
/**
|
|
6302
|
-
*
|
|
6303
|
-
*
|
|
6304
|
-
*
|
|
6305
|
-
*
|
|
6306
|
-
*
|
|
6307
|
-
*
|
|
6308
|
-
* CRITICAL (Bugbot #202): this suppresses ONLY the dispatch/re-attach paths, it
|
|
6309
|
-
* does NOT suppress DONE delivery. A row parked here whose reply later COMPLETES
|
|
6310
|
-
* in opencode must still be delivered via `markDone` on the next drain — so
|
|
6311
|
-
* `readoptOne` computes `state` FIRST and this set is checked only on the
|
|
6312
|
-
* non-done path. It is cleared once the row leaves both processing and pending
|
|
6313
|
-
* lists, so it can never leak. A V2 prompt
|
|
6314
|
-
* acknowledgement with no usable id also uses this fence: OpenCode accepted the
|
|
6315
|
-
* turn, but there is no safe id to watch, so a failed `markFailed` report must not
|
|
6316
|
-
* allow the pending row to post the prompt again.
|
|
6371
|
+
* Readopt give-up fence. Set when recovery declines to start or continue a turn
|
|
6372
|
+
* for a row that is still `processing`, so the next drain does not re-dispatch or
|
|
6373
|
+
* re-attach it before the cron safety net acts. It suppresses only non-done
|
|
6374
|
+
* recovery paths; DONE delivery still runs. Clear it when
|
|
6375
|
+
* `!stillProcessing.has(id)`, because leaving `processing` hands the row back to
|
|
6376
|
+
* normal processing.
|
|
6317
6377
|
*/
|
|
6318
6378
|
dontRedispatch = /* @__PURE__ */ new Set();
|
|
6379
|
+
/**
|
|
6380
|
+
* Untrackable-ack fence. Set after OpenCode accepts a prompt without returning a
|
|
6381
|
+
* usable message id, because another POST could create a duplicate turn. Keep it
|
|
6382
|
+
* fenced while the row is `processing` or `pending`; clear it only when the row
|
|
6383
|
+
* is absent from both lists.
|
|
6384
|
+
*/
|
|
6385
|
+
untrackableAck = /* @__PURE__ */ new Set();
|
|
6319
6386
|
/** Pending rows seen in the current drain, used to retain terminal dispatch fences. */
|
|
6320
6387
|
pendingMessageIds = /* @__PURE__ */ new Set();
|
|
6321
6388
|
/**
|
|
@@ -6900,7 +6967,7 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
6900
6967
|
skippedAlreadyDispatched += 1;
|
|
6901
6968
|
continue;
|
|
6902
6969
|
}
|
|
6903
|
-
if (this.
|
|
6970
|
+
if (this.untrackableAck.has(message.id)) {
|
|
6904
6971
|
this.log({
|
|
6905
6972
|
level: "warn",
|
|
6906
6973
|
message: `Message ${message.id.slice(0, 8)} is fenced after an untrackable OpenCode turn \u2014 skipping re-dispatch`,
|
|
@@ -6966,7 +7033,7 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
6966
7033
|
if (err instanceof ChannelAuthError) throw err;
|
|
6967
7034
|
if (this.isV2 && err instanceof OpenCodeV2PromptAckError) {
|
|
6968
7035
|
const errorMessage4 = err instanceof Error ? err.message : String(err);
|
|
6969
|
-
this.
|
|
7036
|
+
this.untrackableAck.add(message.id);
|
|
6970
7037
|
this.log({
|
|
6971
7038
|
level: "error",
|
|
6972
7039
|
message: `V2 prompt dispatch for message ${message.id.slice(0, 8)} failed after a positive ack with no usable id: ${errorMessage4}`,
|
|
@@ -8887,14 +8954,14 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
8887
8954
|
*/
|
|
8888
8955
|
async readoptProcessing() {
|
|
8889
8956
|
const rows = await this.getProcessingMessages();
|
|
8890
|
-
if (this.dontRedispatch.size > 0 || this.doneUndeliverable.size > 0 || this.readoptPollUnresolvedSignalled.size > 0) {
|
|
8957
|
+
if (this.dontRedispatch.size > 0 || this.untrackableAck.size > 0 || this.doneUndeliverable.size > 0 || this.readoptPollUnresolvedSignalled.size > 0) {
|
|
8891
8958
|
const stillProcessing = new Set(rows.map((r) => r.id));
|
|
8892
8959
|
for (const id of [
|
|
8893
8960
|
...this.dontRedispatch,
|
|
8894
8961
|
...this.doneUndeliverable,
|
|
8895
8962
|
...this.readoptPollUnresolvedSignalled
|
|
8896
8963
|
]) {
|
|
8897
|
-
if (!stillProcessing.has(id)
|
|
8964
|
+
if (!stillProcessing.has(id)) {
|
|
8898
8965
|
const cleared = this.dontRedispatch.delete(id);
|
|
8899
8966
|
const clearedUndeliverable = this.doneUndeliverable.delete(id);
|
|
8900
8967
|
this.readoptPollUnresolvedSignalled.delete(id);
|
|
@@ -8907,6 +8974,16 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
8907
8974
|
}
|
|
8908
8975
|
}
|
|
8909
8976
|
}
|
|
8977
|
+
for (const id of [...this.untrackableAck]) {
|
|
8978
|
+
if (!stillProcessing.has(id) && !this.pendingMessageIds.has(id)) {
|
|
8979
|
+
this.untrackableAck.delete(id);
|
|
8980
|
+
this.log({
|
|
8981
|
+
level: "debug",
|
|
8982
|
+
message: `Re-adopt: message ${id.slice(0, 8)} left processing and pending \u2014 cleared untrackable-ack fence`,
|
|
8983
|
+
message_id: id
|
|
8984
|
+
});
|
|
8985
|
+
}
|
|
8986
|
+
}
|
|
8910
8987
|
}
|
|
8911
8988
|
if (rows.length === 0) return;
|
|
8912
8989
|
const bySession = /* @__PURE__ */ new Map();
|
|
@@ -9063,10 +9140,11 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
9063
9140
|
}
|
|
9064
9141
|
await this.reportSubagentAuthFailures(row.conversation_id, ocId ?? "", row.id, messages);
|
|
9065
9142
|
this.dontRedispatch.delete(row.id);
|
|
9143
|
+
this.untrackableAck.delete(row.id);
|
|
9066
9144
|
void this.postSignal(row.conversation_id, row.id, "readopt_failed");
|
|
9067
9145
|
return;
|
|
9068
9146
|
}
|
|
9069
|
-
if (this.dontRedispatch.has(row.id)) {
|
|
9147
|
+
if (this.dontRedispatch.has(row.id) || this.untrackableAck.has(row.id)) {
|
|
9070
9148
|
this.log({
|
|
9071
9149
|
level: "debug",
|
|
9072
9150
|
message: `Re-adopt: message ${row.id.slice(0, 8)} already gave up \u2014 left to the cron; skipping until it leaves processing`,
|
|
@@ -9175,7 +9253,7 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
9175
9253
|
* extracted (#1493 Task 2.4) so the ambiguous-finish guard above can call the
|
|
9176
9254
|
* SAME delivery instead of duplicating it.
|
|
9177
9255
|
*
|
|
9178
|
-
* EVEN IF the row was previously parked
|
|
9256
|
+
* EVEN IF the row was previously parked by either recovery fence (a give-up stops
|
|
9179
9257
|
* re-dispatch, not delivery — Bugbot #202). Guarded EXACTLY like the watcher's
|
|
9180
9258
|
* `settleMessageDone`: auth re-throws; terminal → park in `doneUndeliverable` +
|
|
9181
9259
|
* leave for cron; transient → log + leave for the next drain (the still-
|
|
@@ -9242,6 +9320,7 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
9242
9320
|
await this.reportSubagentAuthFailures(row.conversation_id, ocId, row.id, messages);
|
|
9243
9321
|
}
|
|
9244
9322
|
this.dontRedispatch.delete(row.id);
|
|
9323
|
+
this.untrackableAck.delete(row.id);
|
|
9245
9324
|
void this.postSignal(row.conversation_id, row.id, "readopt_done");
|
|
9246
9325
|
}
|
|
9247
9326
|
/**
|
|
@@ -9364,7 +9443,7 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
9364
9443
|
return;
|
|
9365
9444
|
}
|
|
9366
9445
|
} else {
|
|
9367
|
-
this.
|
|
9446
|
+
this.untrackableAck.add(row.id);
|
|
9368
9447
|
}
|
|
9369
9448
|
this.log({
|
|
9370
9449
|
level: "error",
|
|
@@ -9399,17 +9478,6 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
9399
9478
|
return;
|
|
9400
9479
|
}
|
|
9401
9480
|
if (ocId === null) {
|
|
9402
|
-
if (this.isV2) {
|
|
9403
|
-
this.dontRedispatch.add(row.id);
|
|
9404
|
-
this.log({
|
|
9405
|
-
level: "error",
|
|
9406
|
-
message: `V2 re-adopt dispatch for message ${row.id.slice(0, 8)} completed without an acknowledged message id`,
|
|
9407
|
-
conversation_id: row.conversation_id,
|
|
9408
|
-
message_id: row.id
|
|
9409
|
-
});
|
|
9410
|
-
void this.postSignal(row.conversation_id, row.id, "ack_untrackable");
|
|
9411
|
-
return;
|
|
9412
|
-
}
|
|
9413
9481
|
this.awaitingReadopt.delete(row.id);
|
|
9414
9482
|
const streak = this.recordUnconfirmedDispatch(row.id, sessionId);
|
|
9415
9483
|
if (streak >= MAX_IDENTICAL_REDRIVE_POLL_FAILURES) {
|