@sjawhar/opencode-legion-envoy 3.19.2 → 3.19.4
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/src/server.js +126 -23
- package/package.json +1 -1
package/dist/src/server.js
CHANGED
|
@@ -14361,6 +14361,8 @@ var EnvelopeSchema = exports_external.object({
|
|
|
14361
14361
|
urgency: exports_external.enum(["low", "med", "high", "blocking"]).optional(),
|
|
14362
14362
|
expects_reply: exports_external.enum(["none", "optional", "required"]).optional()
|
|
14363
14363
|
});
|
|
14364
|
+
var MINTED_DEDUPE_KEY_PATTERN = "^(?:envoy\\.role\\.forward\\.)?(?:publish|agent\\.[^.]+)\\.(?:[0-9a-f]{32}|[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$";
|
|
14365
|
+
var mintedDedupeKey = new RegExp(MINTED_DEDUPE_KEY_PATTERN);
|
|
14364
14366
|
// ../contracts/src/handoff-schema.ts
|
|
14365
14367
|
var HANDOFF_SCHEMA_VERSION = 1;
|
|
14366
14368
|
var HANDOFF_PHASES = ["architect", "plan", "implement", "test", "review"];
|
|
@@ -15142,6 +15144,7 @@ class DispatchServiceError extends Error {
|
|
|
15142
15144
|
current;
|
|
15143
15145
|
mismatches;
|
|
15144
15146
|
name = "DispatchServiceError";
|
|
15147
|
+
fromDispatch = true;
|
|
15145
15148
|
constructor(code, status, message, candidates, current, mismatches) {
|
|
15146
15149
|
super(message);
|
|
15147
15150
|
this.code = code;
|
|
@@ -15151,6 +15154,21 @@ class DispatchServiceError extends Error {
|
|
|
15151
15154
|
this.mismatches = mismatches;
|
|
15152
15155
|
}
|
|
15153
15156
|
}
|
|
15157
|
+
|
|
15158
|
+
class DispatchGatewayError extends DispatchServiceError {
|
|
15159
|
+
answer;
|
|
15160
|
+
advice;
|
|
15161
|
+
fromDispatch = false;
|
|
15162
|
+
transient;
|
|
15163
|
+
mayHaveReachedDispatch;
|
|
15164
|
+
constructor(status, answer, advice, message = `${answer}, so ${advice}.`) {
|
|
15165
|
+
super(`HTTP_${status}`, status, message);
|
|
15166
|
+
this.answer = answer;
|
|
15167
|
+
this.advice = advice;
|
|
15168
|
+
this.transient = transientStatus(status);
|
|
15169
|
+
this.mayHaveReachedDispatch = reachesDispatch(status);
|
|
15170
|
+
}
|
|
15171
|
+
}
|
|
15154
15172
|
function asErrorShape(value) {
|
|
15155
15173
|
return typeof value === "object" && value !== null ? value : {};
|
|
15156
15174
|
}
|
|
@@ -15164,7 +15182,7 @@ function requestSignal(signal) {
|
|
|
15164
15182
|
}
|
|
15165
15183
|
function whyNotAPage(answer) {
|
|
15166
15184
|
if (typeof answer === "string") {
|
|
15167
|
-
return
|
|
15185
|
+
return `text that is not a JSON page, ${GATEWAY_PAGE}, so a retry may succeed.`;
|
|
15168
15186
|
}
|
|
15169
15187
|
let what;
|
|
15170
15188
|
if (Array.isArray(answer)) {
|
|
@@ -15183,6 +15201,53 @@ function whyNotAPage(answer) {
|
|
|
15183
15201
|
}
|
|
15184
15202
|
return `${what}. Retrying will not help: the same request gets the same answer until that ` + "Dispatch is upgraded or fixed.";
|
|
15185
15203
|
}
|
|
15204
|
+
var GATEWAY_PAGE = "which looks like a proxy or gateway page rather than Dispatch's own answer";
|
|
15205
|
+
function gatewayAdvice(method, status) {
|
|
15206
|
+
if (!transientStatus(status)) {
|
|
15207
|
+
return "a retry gets the same answer until the Dispatch URL, or whatever answers in its place, is fixed";
|
|
15208
|
+
}
|
|
15209
|
+
if (method === "GET")
|
|
15210
|
+
return "a retry may succeed";
|
|
15211
|
+
return reachesDispatch(status) ? "the write may or may not have reached Dispatch: check whether it took effect before retrying it" : "the write did not reach Dispatch, and a retry may succeed";
|
|
15212
|
+
}
|
|
15213
|
+
function transientStatus(status) {
|
|
15214
|
+
return status >= 500 || status === 408 || status === 429;
|
|
15215
|
+
}
|
|
15216
|
+
function reachesDispatch(status) {
|
|
15217
|
+
return status >= 500;
|
|
15218
|
+
}
|
|
15219
|
+
var REDACTED = "[redacted]";
|
|
15220
|
+
var EXCERPT_SCAN_LIMIT = 64 * 1024;
|
|
15221
|
+
var BEARER_PIECE = 8;
|
|
15222
|
+
function redactBearer(text, bearer) {
|
|
15223
|
+
if (bearer === "")
|
|
15224
|
+
return text.slice(0, EXCERPT_SCAN_LIMIT);
|
|
15225
|
+
const length = Math.min(BEARER_PIECE, bearer.length);
|
|
15226
|
+
const scanned = text.slice(0, EXCERPT_SCAN_LIMIT + length - 1);
|
|
15227
|
+
const covered = new Uint8Array(scanned.length);
|
|
15228
|
+
const pieces = new Set;
|
|
15229
|
+
for (let at = 0;at + length <= bearer.length; at++)
|
|
15230
|
+
pieces.add(bearer.slice(at, at + length));
|
|
15231
|
+
for (const piece of pieces) {
|
|
15232
|
+
let found = scanned.indexOf(piece);
|
|
15233
|
+
while (found !== -1) {
|
|
15234
|
+
covered.fill(1, found, found + length);
|
|
15235
|
+
found = scanned.indexOf(piece, found + 1);
|
|
15236
|
+
}
|
|
15237
|
+
}
|
|
15238
|
+
let redacted = "";
|
|
15239
|
+
let kept = 0;
|
|
15240
|
+
for (let start = covered.indexOf(1);start !== -1; start = covered.indexOf(1, kept)) {
|
|
15241
|
+
redacted += `${scanned.slice(kept, start)}${REDACTED}`;
|
|
15242
|
+
const end = covered.indexOf(0, start);
|
|
15243
|
+
kept = end === -1 ? scanned.length : end;
|
|
15244
|
+
}
|
|
15245
|
+
return `${redacted}${scanned.slice(kept, EXCERPT_SCAN_LIMIT)}`.slice(0, EXCERPT_SCAN_LIMIT);
|
|
15246
|
+
}
|
|
15247
|
+
function excerpt(text, token) {
|
|
15248
|
+
const plain = redactBearer(text, token.trim()).replace(/<(script|style)\b[^<>]*>(?:[\s\S]*?<\/\1\s*>|[\s\S]*$)/gi, " ").replace(/<[^<>]*>/g, " ");
|
|
15249
|
+
return textHead(plain.replace(/(\bauthorization["']?\s*[:=]\s*["']?(?:(?:bearer|basic|digest|token)\s+)?)[^\s"'<>,;]+/gi, `$1${REDACTED}`).replace(/(\bbearer\s+)[^\s"'<>,;]+/gi, `$1${REDACTED}`));
|
|
15250
|
+
}
|
|
15186
15251
|
|
|
15187
15252
|
class DispatchClient {
|
|
15188
15253
|
token;
|
|
@@ -15440,22 +15505,24 @@ class DispatchClient {
|
|
|
15440
15505
|
};
|
|
15441
15506
|
if (body !== undefined)
|
|
15442
15507
|
headers["Content-Type"] = "application/json";
|
|
15443
|
-
const
|
|
15508
|
+
const url2 = this.#url(path2, query);
|
|
15509
|
+
const response = await this.fetchImpl(url2, {
|
|
15444
15510
|
method,
|
|
15445
15511
|
headers,
|
|
15446
15512
|
signal: this.#signal,
|
|
15447
15513
|
...body === undefined ? {} : { body: JSON.stringify(body) }
|
|
15448
15514
|
});
|
|
15449
|
-
return this.#response(response);
|
|
15515
|
+
return this.#response(method, url2, response);
|
|
15450
15516
|
}
|
|
15451
15517
|
async#form(method, path2, body) {
|
|
15452
|
-
const
|
|
15518
|
+
const url2 = this.#url(path2);
|
|
15519
|
+
const response = await this.fetchImpl(url2, {
|
|
15453
15520
|
method,
|
|
15454
15521
|
headers: { Accept: "application/json", Authorization: `Bearer ${this.token}` },
|
|
15455
15522
|
body,
|
|
15456
15523
|
signal: this.#signal
|
|
15457
15524
|
});
|
|
15458
|
-
return this.#response(response);
|
|
15525
|
+
return this.#response(method, url2, response);
|
|
15459
15526
|
}
|
|
15460
15527
|
#url(path2, query) {
|
|
15461
15528
|
const url2 = new URL(`${path2.map((segment) => encodeURIComponent(segment)).join("/")}`, `${this.#baseUrl}/`);
|
|
@@ -15471,7 +15538,7 @@ class DispatchClient {
|
|
|
15471
15538
|
}
|
|
15472
15539
|
return url2.toString();
|
|
15473
15540
|
}
|
|
15474
|
-
async#response(response) {
|
|
15541
|
+
async#response(method, url2, response) {
|
|
15475
15542
|
const text = await response.text();
|
|
15476
15543
|
let payload = text;
|
|
15477
15544
|
if (text && isJson(response)) {
|
|
@@ -15483,7 +15550,23 @@ class DispatchClient {
|
|
|
15483
15550
|
}
|
|
15484
15551
|
if (!response.ok) {
|
|
15485
15552
|
const error48 = asErrorShape(payload);
|
|
15486
|
-
|
|
15553
|
+
if (typeof error48.error === "string") {
|
|
15554
|
+
throw new DispatchServiceError(error48.code ?? `HTTP_${response.status}`, response.status, error48.error, error48.candidates, error48.current, error48.mismatches);
|
|
15555
|
+
}
|
|
15556
|
+
const quoted = excerpt(text, this.token);
|
|
15557
|
+
let body;
|
|
15558
|
+
if (quoted !== "") {
|
|
15559
|
+
body = `a body that is not Dispatch's error JSON (${JSON.stringify(quoted)})`;
|
|
15560
|
+
} else if (text === "") {
|
|
15561
|
+
body = "an empty body";
|
|
15562
|
+
} else {
|
|
15563
|
+
const within = text.length > EXCERPT_SCAN_LIMIT ? ` in its first ${EXCERPT_SCAN_LIMIT / 1024} KiB` : "";
|
|
15564
|
+
const size = Buffer.byteLength(text).toLocaleString("en-US");
|
|
15565
|
+
body = `a body of ${size} bytes and no readable text${within}`;
|
|
15566
|
+
}
|
|
15567
|
+
const reasonPhrase = excerpt(response.statusText, this.token);
|
|
15568
|
+
const reason = reasonPhrase === "" ? "" : ` ${reasonPhrase}`;
|
|
15569
|
+
throw new DispatchGatewayError(response.status, `${method} ${url2} answered ${response.status}${reason} with ${body}, ${GATEWAY_PAGE}`, gatewayAdvice(method, response.status));
|
|
15487
15570
|
}
|
|
15488
15571
|
return payload;
|
|
15489
15572
|
}
|
|
@@ -16226,7 +16309,7 @@ async function resolveArtifact(client, owner, artifactReference, { canonical = f
|
|
|
16226
16309
|
throw new Error("artifact is required for a project document");
|
|
16227
16310
|
}
|
|
16228
16311
|
const routed = await client.getProjectArtifact(owner.project, artifactReference).catch((error48) => {
|
|
16229
|
-
if (!(error48
|
|
16312
|
+
if (!dispatchAnswered(error48, 404))
|
|
16230
16313
|
throw error48;
|
|
16231
16314
|
return;
|
|
16232
16315
|
});
|
|
@@ -16394,12 +16477,12 @@ function referenceLines(edges) {
|
|
|
16394
16477
|
if (edges.length === 0)
|
|
16395
16478
|
return ["- none"];
|
|
16396
16479
|
return edges.map((edge) => {
|
|
16397
|
-
const
|
|
16398
|
-
return `- ${edge.kind} ${edge.node.kind} ${edge.node.ref ?? edge.node.id} (${
|
|
16480
|
+
const excerpt2 = edge.excerpt === undefined ? "" : `${textHead(edge.excerpt.text)} \xB7 `;
|
|
16481
|
+
return `- ${edge.kind} ${edge.node.kind} ${edge.node.ref ?? edge.node.id} (${excerpt2}${edge.created_at})`;
|
|
16399
16482
|
});
|
|
16400
16483
|
}
|
|
16401
16484
|
function unavailableReason(error48) {
|
|
16402
|
-
return error48
|
|
16485
|
+
return dispatchAnswered(error48, 404) ? "unavailable" : `unavailable: ${messageFor(error48)}`;
|
|
16403
16486
|
}
|
|
16404
16487
|
async function graphEdges(client, query) {
|
|
16405
16488
|
try {
|
|
@@ -16589,9 +16672,8 @@ async function openArtifactMarks(client, resolved) {
|
|
|
16589
16672
|
const marks = asks.filter((ask) => ask.state === "open" && ask.anchor?.artifact_id === resolved.artifact.id).map((ask) => `ask ${ask.id}`);
|
|
16590
16673
|
const commentsResult = await commentsResultPromise;
|
|
16591
16674
|
if (commentsResult.status === "rejected") {
|
|
16592
|
-
if (commentsResult.reason
|
|
16675
|
+
if (dispatchAnswered(commentsResult.reason, 404))
|
|
16593
16676
|
return marks;
|
|
16594
|
-
}
|
|
16595
16677
|
throw commentsResult.reason;
|
|
16596
16678
|
}
|
|
16597
16679
|
return [
|
|
@@ -16678,10 +16760,30 @@ async function refuseRemovingOpenDecisionBlocks(client, tool, resolved, ops) {
|
|
|
16678
16760
|
`));
|
|
16679
16761
|
}
|
|
16680
16762
|
function refusalWithCode(error48, suffix = "") {
|
|
16763
|
+
if (error48 instanceof DispatchGatewayError) {
|
|
16764
|
+
let told = error48.message;
|
|
16765
|
+
if (suffix !== "") {
|
|
16766
|
+
told = error48.mayHaveReachedDispatch ? `${error48.answer}${suffix}` : `${error48.answer}, so ${error48.advice}${suffix}`;
|
|
16767
|
+
}
|
|
16768
|
+
return new DispatchGatewayError(error48.status, error48.answer, error48.advice, `${error48.code}: ${told}`);
|
|
16769
|
+
}
|
|
16681
16770
|
if (!(error48 instanceof DispatchServiceError))
|
|
16682
16771
|
return error48;
|
|
16683
16772
|
return new DispatchServiceError(error48.code, error48.status, `${error48.code}: ${error48.message}${suffix}`, error48.candidates, error48.current, error48.mismatches);
|
|
16684
16773
|
}
|
|
16774
|
+
function dispatchAnswered(error48, status) {
|
|
16775
|
+
return error48 instanceof DispatchServiceError && error48.fromDispatch && error48.status === status;
|
|
16776
|
+
}
|
|
16777
|
+
function writeMayHaveLanded(error48) {
|
|
16778
|
+
if (error48 instanceof DispatchGatewayError)
|
|
16779
|
+
return error48.mayHaveReachedDispatch;
|
|
16780
|
+
return !(error48 instanceof DispatchServiceError) || error48.status >= 500;
|
|
16781
|
+
}
|
|
16782
|
+
function withAccount(error48, account) {
|
|
16783
|
+
const message = messageFor(error48);
|
|
16784
|
+
const told = /[.!?]$/.test(message) ? `${message} ${account.charAt(0).toUpperCase()}${account.slice(1)}` : `${message}; ${account}`;
|
|
16785
|
+
return new Error(told, { cause: error48 });
|
|
16786
|
+
}
|
|
16685
16787
|
async function executeDispatchTool(input) {
|
|
16686
16788
|
const configUrl = input.config.url;
|
|
16687
16789
|
const configToken = input.config.token;
|
|
@@ -16851,7 +16953,10 @@ async function executeDispatchTool(input) {
|
|
|
16851
16953
|
ref: dispatchChildRef(dispatchIssueRef(issueKey), "message", message.id)
|
|
16852
16954
|
};
|
|
16853
16955
|
} catch (error48) {
|
|
16854
|
-
|
|
16956
|
+
const told = writeMayHaveLanded(error48) ? "the reason may or may not have been posted, and the close was not sent: read the issue's messages before retrying, since retrying this call posts its reason again" : "the reason was not posted, so the close was not sent";
|
|
16957
|
+
if (error48 instanceof DispatchServiceError)
|
|
16958
|
+
throw refusalWithCode(error48, `; ${told}`);
|
|
16959
|
+
throw withAccount(error48, told);
|
|
16855
16960
|
}
|
|
16856
16961
|
}
|
|
16857
16962
|
const linked = before.external_links.map((link) => link.url);
|
|
@@ -16870,17 +16975,15 @@ async function executeDispatchTool(input) {
|
|
|
16870
16975
|
actor
|
|
16871
16976
|
});
|
|
16872
16977
|
} catch (error48) {
|
|
16873
|
-
const taken = error48
|
|
16978
|
+
const taken = dispatchAnswered(error48, 500) && newLinks.length > 0 ? `; one of ${newLinks.join(", ")} may already be linked from another issue (a URL links exactly one issue)` : "";
|
|
16874
16979
|
if (closingNote === undefined)
|
|
16875
16980
|
throw refusalWithCode(error48, taken);
|
|
16876
|
-
const
|
|
16877
|
-
const
|
|
16878
|
-
const landed =
|
|
16981
|
+
const posted = `the reason already landed as message ${closingNote.id} (${closingNote.ref})`;
|
|
16982
|
+
const fix = error48 instanceof DispatchGatewayError && error48.transient ? "" : "fix what refused the close, then ";
|
|
16983
|
+
const landed = writeMayHaveLanded(error48) ? `${posted}, and the close may or may not have taken effect. Read the issue's status before retrying: done means it closed; otherwise retry with a reason that points at message ${closingNote.id}, since retrying this call posts its reason again` : `${posted} but the issue did not close. Retrying this call posts its reason again, so ${fix}retry with a reason that points at message ${closingNote.id}`;
|
|
16879
16984
|
if (error48 instanceof DispatchServiceError)
|
|
16880
|
-
throw refusalWithCode(error48, taken
|
|
16881
|
-
throw
|
|
16882
|
-
cause: error48
|
|
16883
|
-
});
|
|
16985
|
+
throw refusalWithCode(error48, `${taken}; ${landed}`);
|
|
16986
|
+
throw withAccount(error48, landed);
|
|
16884
16987
|
}
|
|
16885
16988
|
const linkCount = `(${after.external_links.length} ${after.external_links.length === 1 ? "link" : "links"})`;
|
|
16886
16989
|
const changes = [
|
|
@@ -17484,7 +17587,7 @@ async function resolveExistingIssue(client, issueReference) {
|
|
|
17484
17587
|
try {
|
|
17485
17588
|
return await client.resolveIssue(issueReference);
|
|
17486
17589
|
} catch (error48) {
|
|
17487
|
-
if (error48
|
|
17590
|
+
if (dispatchAnswered(error48, 404)) {
|
|
17488
17591
|
throw new Error(`no Dispatch issue is linked to ${issueReference}; create it first with ` + `dispatch_issue({ external: "${issueReference}", ... })`);
|
|
17489
17592
|
}
|
|
17490
17593
|
throw error48;
|