@sjawhar/pi-legion-envoy 5.30.3 → 5.30.5
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/envoy.js +220 -46
- package/dist/legion.js +142 -24
- package/package.json +1 -1
package/dist/envoy.js
CHANGED
|
@@ -30475,6 +30475,23 @@ var EnvelopeSchema = exports_external.object({
|
|
|
30475
30475
|
urgency: exports_external.enum(["low", "med", "high", "blocking"]).optional(),
|
|
30476
30476
|
expects_reply: exports_external.enum(["none", "optional", "required"]).optional()
|
|
30477
30477
|
});
|
|
30478
|
+
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})$";
|
|
30479
|
+
var mintedDedupeKey = new RegExp(MINTED_DEDUPE_KEY_PATTERN);
|
|
30480
|
+
function dedupeKeyNamesItsEvent(envelope) {
|
|
30481
|
+
const key = envelope.dedupe_key;
|
|
30482
|
+
if (key === undefined)
|
|
30483
|
+
return false;
|
|
30484
|
+
if (envelope.source === "dispatch" || mintedDedupeKey.test(key))
|
|
30485
|
+
return true;
|
|
30486
|
+
switch (envelope.source) {
|
|
30487
|
+
case "github":
|
|
30488
|
+
case "slack":
|
|
30489
|
+
case "ghostwispr":
|
|
30490
|
+
return envelope.source_event_id !== undefined && envelope.source_event_id !== "" && key === `${envelope.source}.${envelope.source_event_id}`;
|
|
30491
|
+
default:
|
|
30492
|
+
return false;
|
|
30493
|
+
}
|
|
30494
|
+
}
|
|
30478
30495
|
// ../contracts/src/handoff-schema.ts
|
|
30479
30496
|
var HANDOFF_SCHEMA_VERSION = 1;
|
|
30480
30497
|
var HANDOFF_PHASES = ["architect", "plan", "implement", "test", "review"];
|
|
@@ -31612,6 +31629,7 @@ var InboundSenderSchema = exports_external.object({
|
|
|
31612
31629
|
var InboundEnvelopeSchema = exports_external.object({
|
|
31613
31630
|
event_id: exports_external.string().optional(),
|
|
31614
31631
|
source: exports_external.string(),
|
|
31632
|
+
source_event_id: exports_external.string().optional(),
|
|
31615
31633
|
source_session: exports_external.string().optional(),
|
|
31616
31634
|
topic: exports_external.string().optional(),
|
|
31617
31635
|
dedupe_key: exports_external.string().optional(),
|
|
@@ -31655,13 +31673,72 @@ async function postDeliveryReply(config2, sessionId, delivery, result) {
|
|
|
31655
31673
|
function expectsLaneReceipt(frame) {
|
|
31656
31674
|
return frame.reply !== undefined && frame.reply !== "" && frame.subject === frame.directSubject && frame.envelopeTopic !== undefined && frame.envelopeTopic !== frame.directSubject;
|
|
31657
31675
|
}
|
|
31658
|
-
|
|
31659
|
-
|
|
31660
|
-
|
|
31661
|
-
|
|
31662
|
-
|
|
31663
|
-
|
|
31664
|
-
|
|
31676
|
+
var DedupeIdentitySchema = exports_external.object({
|
|
31677
|
+
event_id: exports_external.string().min(1).optional(),
|
|
31678
|
+
dedupe_key: exports_external.string().min(1).optional(),
|
|
31679
|
+
source: exports_external.string().optional(),
|
|
31680
|
+
source_event_id: exports_external.string().optional()
|
|
31681
|
+
});
|
|
31682
|
+
var DELIVERY_DEDUPE_KEY_LIMIT = 250000;
|
|
31683
|
+
var DELIVERY_EVENT_ID_LIMIT = 1e4;
|
|
31684
|
+
function createDeliveryDedupe(now = Date.now) {
|
|
31685
|
+
const keys = new Map;
|
|
31686
|
+
let events = new Map;
|
|
31687
|
+
let olderEvents = new Map;
|
|
31688
|
+
let claims = 0;
|
|
31689
|
+
return {
|
|
31690
|
+
claim(frame) {
|
|
31691
|
+
const eventId = frame?.event_id;
|
|
31692
|
+
if (eventId !== undefined && (events.has(eventId) || olderEvents.has(eventId))) {
|
|
31693
|
+
return;
|
|
31694
|
+
}
|
|
31695
|
+
const key = frame?.dedupe_key;
|
|
31696
|
+
const named = frame !== undefined && key !== undefined && dedupeKeyNamesItsEvent(frame);
|
|
31697
|
+
const at = now();
|
|
31698
|
+
if (named) {
|
|
31699
|
+
const claimedAt = keys.get(key);
|
|
31700
|
+
if (claimedAt !== undefined && at - claimedAt < DELIVERY_DUPLICATE_WINDOW_MS) {
|
|
31701
|
+
return;
|
|
31702
|
+
}
|
|
31703
|
+
for (const [oldest, deliveredAt] of keys) {
|
|
31704
|
+
if (at - deliveredAt < DELIVERY_DUPLICATE_WINDOW_MS)
|
|
31705
|
+
break;
|
|
31706
|
+
keys.delete(oldest);
|
|
31707
|
+
}
|
|
31708
|
+
keys.delete(key);
|
|
31709
|
+
if (keys.size >= DELIVERY_DEDUPE_KEY_LIMIT) {
|
|
31710
|
+
const oldest = keys.keys().next();
|
|
31711
|
+
if (oldest.done !== true)
|
|
31712
|
+
keys.delete(oldest.value);
|
|
31713
|
+
}
|
|
31714
|
+
keys.set(key, at);
|
|
31715
|
+
}
|
|
31716
|
+
const claim = ++claims;
|
|
31717
|
+
if (eventId !== undefined) {
|
|
31718
|
+
if (events.size >= DELIVERY_EVENT_ID_LIMIT / 2) {
|
|
31719
|
+
olderEvents = events;
|
|
31720
|
+
events = new Map;
|
|
31721
|
+
}
|
|
31722
|
+
events.set(eventId, claim);
|
|
31723
|
+
}
|
|
31724
|
+
let released = false;
|
|
31725
|
+
return {
|
|
31726
|
+
release() {
|
|
31727
|
+
if (released)
|
|
31728
|
+
return;
|
|
31729
|
+
released = true;
|
|
31730
|
+
if (named && keys.get(key) === at)
|
|
31731
|
+
keys.delete(key);
|
|
31732
|
+
if (eventId === undefined)
|
|
31733
|
+
return;
|
|
31734
|
+
if (events.get(eventId) === claim)
|
|
31735
|
+
events.delete(eventId);
|
|
31736
|
+
if (olderEvents.get(eventId) === claim)
|
|
31737
|
+
olderEvents.delete(eventId);
|
|
31738
|
+
}
|
|
31739
|
+
};
|
|
31740
|
+
}
|
|
31741
|
+
};
|
|
31665
31742
|
}
|
|
31666
31743
|
function isCommentTargetedDelivery(delivery) {
|
|
31667
31744
|
return "comment_id" in delivery;
|
|
@@ -32386,6 +32463,7 @@ class DispatchServiceError extends Error {
|
|
|
32386
32463
|
current;
|
|
32387
32464
|
mismatches;
|
|
32388
32465
|
name = "DispatchServiceError";
|
|
32466
|
+
fromDispatch = true;
|
|
32389
32467
|
constructor(code, status, message, candidates, current, mismatches) {
|
|
32390
32468
|
super(message);
|
|
32391
32469
|
this.code = code;
|
|
@@ -32395,6 +32473,21 @@ class DispatchServiceError extends Error {
|
|
|
32395
32473
|
this.mismatches = mismatches;
|
|
32396
32474
|
}
|
|
32397
32475
|
}
|
|
32476
|
+
|
|
32477
|
+
class DispatchGatewayError extends DispatchServiceError {
|
|
32478
|
+
answer;
|
|
32479
|
+
advice;
|
|
32480
|
+
fromDispatch = false;
|
|
32481
|
+
transient;
|
|
32482
|
+
mayHaveReachedDispatch;
|
|
32483
|
+
constructor(status, answer, advice, message = `${answer}, so ${advice}.`) {
|
|
32484
|
+
super(`HTTP_${status}`, status, message);
|
|
32485
|
+
this.answer = answer;
|
|
32486
|
+
this.advice = advice;
|
|
32487
|
+
this.transient = transientStatus(status);
|
|
32488
|
+
this.mayHaveReachedDispatch = reachesDispatch(status);
|
|
32489
|
+
}
|
|
32490
|
+
}
|
|
32398
32491
|
function asErrorShape(value) {
|
|
32399
32492
|
return typeof value === "object" && value !== null ? value : {};
|
|
32400
32493
|
}
|
|
@@ -32408,7 +32501,7 @@ function requestSignal(signal) {
|
|
|
32408
32501
|
}
|
|
32409
32502
|
function whyNotAPage(answer) {
|
|
32410
32503
|
if (typeof answer === "string") {
|
|
32411
|
-
return
|
|
32504
|
+
return `text that is not a JSON page, ${GATEWAY_PAGE}, so a retry may succeed.`;
|
|
32412
32505
|
}
|
|
32413
32506
|
let what;
|
|
32414
32507
|
if (Array.isArray(answer)) {
|
|
@@ -32427,6 +32520,53 @@ function whyNotAPage(answer) {
|
|
|
32427
32520
|
}
|
|
32428
32521
|
return `${what}. Retrying will not help: the same request gets the same answer until that ` + "Dispatch is upgraded or fixed.";
|
|
32429
32522
|
}
|
|
32523
|
+
var GATEWAY_PAGE = "which looks like a proxy or gateway page rather than Dispatch's own answer";
|
|
32524
|
+
function gatewayAdvice(method, status) {
|
|
32525
|
+
if (!transientStatus(status)) {
|
|
32526
|
+
return "a retry gets the same answer until the Dispatch URL, or whatever answers in its place, is fixed";
|
|
32527
|
+
}
|
|
32528
|
+
if (method === "GET")
|
|
32529
|
+
return "a retry may succeed";
|
|
32530
|
+
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";
|
|
32531
|
+
}
|
|
32532
|
+
function transientStatus(status) {
|
|
32533
|
+
return status >= 500 || status === 408 || status === 429;
|
|
32534
|
+
}
|
|
32535
|
+
function reachesDispatch(status) {
|
|
32536
|
+
return status >= 500;
|
|
32537
|
+
}
|
|
32538
|
+
var REDACTED = "[redacted]";
|
|
32539
|
+
var EXCERPT_SCAN_LIMIT = 64 * 1024;
|
|
32540
|
+
var BEARER_PIECE = 8;
|
|
32541
|
+
function redactBearer(text, bearer) {
|
|
32542
|
+
if (bearer === "")
|
|
32543
|
+
return text.slice(0, EXCERPT_SCAN_LIMIT);
|
|
32544
|
+
const length = Math.min(BEARER_PIECE, bearer.length);
|
|
32545
|
+
const scanned = text.slice(0, EXCERPT_SCAN_LIMIT + length - 1);
|
|
32546
|
+
const covered = new Uint8Array(scanned.length);
|
|
32547
|
+
const pieces = new Set;
|
|
32548
|
+
for (let at = 0;at + length <= bearer.length; at++)
|
|
32549
|
+
pieces.add(bearer.slice(at, at + length));
|
|
32550
|
+
for (const piece of pieces) {
|
|
32551
|
+
let found = scanned.indexOf(piece);
|
|
32552
|
+
while (found !== -1) {
|
|
32553
|
+
covered.fill(1, found, found + length);
|
|
32554
|
+
found = scanned.indexOf(piece, found + 1);
|
|
32555
|
+
}
|
|
32556
|
+
}
|
|
32557
|
+
let redacted = "";
|
|
32558
|
+
let kept = 0;
|
|
32559
|
+
for (let start = covered.indexOf(1);start !== -1; start = covered.indexOf(1, kept)) {
|
|
32560
|
+
redacted += `${scanned.slice(kept, start)}${REDACTED}`;
|
|
32561
|
+
const end = covered.indexOf(0, start);
|
|
32562
|
+
kept = end === -1 ? scanned.length : end;
|
|
32563
|
+
}
|
|
32564
|
+
return `${redacted}${scanned.slice(kept, EXCERPT_SCAN_LIMIT)}`.slice(0, EXCERPT_SCAN_LIMIT);
|
|
32565
|
+
}
|
|
32566
|
+
function excerpt(text, token) {
|
|
32567
|
+
const plain = redactBearer(text, token.trim()).replace(/<(script|style)\b[^<>]*>(?:[\s\S]*?<\/\1\s*>|[\s\S]*$)/gi, " ").replace(/<[^<>]*>/g, " ");
|
|
32568
|
+
return textHead(plain.replace(/(\bauthorization["']?\s*[:=]\s*["']?(?:(?:bearer|basic|digest|token)\s+)?)[^\s"'<>,;]+/gi, `$1${REDACTED}`).replace(/(\bbearer\s+)[^\s"'<>,;]+/gi, `$1${REDACTED}`));
|
|
32569
|
+
}
|
|
32430
32570
|
|
|
32431
32571
|
class DispatchClient {
|
|
32432
32572
|
token;
|
|
@@ -32684,22 +32824,24 @@ class DispatchClient {
|
|
|
32684
32824
|
};
|
|
32685
32825
|
if (body !== undefined)
|
|
32686
32826
|
headers["Content-Type"] = "application/json";
|
|
32687
|
-
const
|
|
32827
|
+
const url2 = this.#url(path2, query);
|
|
32828
|
+
const response = await this.fetchImpl(url2, {
|
|
32688
32829
|
method,
|
|
32689
32830
|
headers,
|
|
32690
32831
|
signal: this.#signal,
|
|
32691
32832
|
...body === undefined ? {} : { body: JSON.stringify(body) }
|
|
32692
32833
|
});
|
|
32693
|
-
return this.#response(response);
|
|
32834
|
+
return this.#response(method, url2, response);
|
|
32694
32835
|
}
|
|
32695
32836
|
async#form(method, path2, body) {
|
|
32696
|
-
const
|
|
32837
|
+
const url2 = this.#url(path2);
|
|
32838
|
+
const response = await this.fetchImpl(url2, {
|
|
32697
32839
|
method,
|
|
32698
32840
|
headers: { Accept: "application/json", Authorization: `Bearer ${this.token}` },
|
|
32699
32841
|
body,
|
|
32700
32842
|
signal: this.#signal
|
|
32701
32843
|
});
|
|
32702
|
-
return this.#response(response);
|
|
32844
|
+
return this.#response(method, url2, response);
|
|
32703
32845
|
}
|
|
32704
32846
|
#url(path2, query) {
|
|
32705
32847
|
const url2 = new URL(`${path2.map((segment) => encodeURIComponent(segment)).join("/")}`, `${this.#baseUrl}/`);
|
|
@@ -32715,7 +32857,7 @@ class DispatchClient {
|
|
|
32715
32857
|
}
|
|
32716
32858
|
return url2.toString();
|
|
32717
32859
|
}
|
|
32718
|
-
async#response(response) {
|
|
32860
|
+
async#response(method, url2, response) {
|
|
32719
32861
|
const text = await response.text();
|
|
32720
32862
|
let payload = text;
|
|
32721
32863
|
if (text && isJson(response)) {
|
|
@@ -32727,7 +32869,23 @@ class DispatchClient {
|
|
|
32727
32869
|
}
|
|
32728
32870
|
if (!response.ok) {
|
|
32729
32871
|
const error48 = asErrorShape(payload);
|
|
32730
|
-
|
|
32872
|
+
if (typeof error48.error === "string") {
|
|
32873
|
+
throw new DispatchServiceError(error48.code ?? `HTTP_${response.status}`, response.status, error48.error, error48.candidates, error48.current, error48.mismatches);
|
|
32874
|
+
}
|
|
32875
|
+
const quoted = excerpt(text, this.token);
|
|
32876
|
+
let body;
|
|
32877
|
+
if (quoted !== "") {
|
|
32878
|
+
body = `a body that is not Dispatch's error JSON (${JSON.stringify(quoted)})`;
|
|
32879
|
+
} else if (text === "") {
|
|
32880
|
+
body = "an empty body";
|
|
32881
|
+
} else {
|
|
32882
|
+
const within = text.length > EXCERPT_SCAN_LIMIT ? ` in its first ${EXCERPT_SCAN_LIMIT / 1024} KiB` : "";
|
|
32883
|
+
const size = Buffer.byteLength(text).toLocaleString("en-US");
|
|
32884
|
+
body = `a body of ${size} bytes and no readable text${within}`;
|
|
32885
|
+
}
|
|
32886
|
+
const reasonPhrase = excerpt(response.statusText, this.token);
|
|
32887
|
+
const reason = reasonPhrase === "" ? "" : ` ${reasonPhrase}`;
|
|
32888
|
+
throw new DispatchGatewayError(response.status, `${method} ${url2} answered ${response.status}${reason} with ${body}, ${GATEWAY_PAGE}`, gatewayAdvice(method, response.status));
|
|
32731
32889
|
}
|
|
32732
32890
|
return payload;
|
|
32733
32891
|
}
|
|
@@ -33447,7 +33605,7 @@ async function resolveArtifact(client, owner, artifactReference, { canonical = f
|
|
|
33447
33605
|
throw new Error("artifact is required for a project document");
|
|
33448
33606
|
}
|
|
33449
33607
|
const routed = await client.getProjectArtifact(owner.project, artifactReference).catch((error48) => {
|
|
33450
|
-
if (!(error48
|
|
33608
|
+
if (!dispatchAnswered(error48, 404))
|
|
33451
33609
|
throw error48;
|
|
33452
33610
|
return;
|
|
33453
33611
|
});
|
|
@@ -33618,12 +33776,12 @@ function referenceLines(edges) {
|
|
|
33618
33776
|
if (edges.length === 0)
|
|
33619
33777
|
return ["- none"];
|
|
33620
33778
|
return edges.map((edge) => {
|
|
33621
|
-
const
|
|
33622
|
-
return `- ${edge.kind} ${edge.node.kind} ${edge.node.ref ?? edge.node.id} (${
|
|
33779
|
+
const excerpt2 = edge.excerpt === undefined ? "" : `${textHead(edge.excerpt.text)} \xB7 `;
|
|
33780
|
+
return `- ${edge.kind} ${edge.node.kind} ${edge.node.ref ?? edge.node.id} (${excerpt2}${edge.created_at})`;
|
|
33623
33781
|
});
|
|
33624
33782
|
}
|
|
33625
33783
|
function unavailableReason(error48) {
|
|
33626
|
-
return error48
|
|
33784
|
+
return dispatchAnswered(error48, 404) ? "unavailable" : `unavailable: ${messageFor(error48)}`;
|
|
33627
33785
|
}
|
|
33628
33786
|
async function graphEdges(client, query) {
|
|
33629
33787
|
try {
|
|
@@ -33813,9 +33971,8 @@ async function openArtifactMarks(client, resolved) {
|
|
|
33813
33971
|
const marks = asks.filter((ask) => ask.state === "open" && ask.anchor?.artifact_id === resolved.artifact.id).map((ask) => `ask ${ask.id}`);
|
|
33814
33972
|
const commentsResult = await commentsResultPromise;
|
|
33815
33973
|
if (commentsResult.status === "rejected") {
|
|
33816
|
-
if (commentsResult.reason
|
|
33974
|
+
if (dispatchAnswered(commentsResult.reason, 404))
|
|
33817
33975
|
return marks;
|
|
33818
|
-
}
|
|
33819
33976
|
throw commentsResult.reason;
|
|
33820
33977
|
}
|
|
33821
33978
|
return [
|
|
@@ -33902,10 +34059,30 @@ async function refuseRemovingOpenDecisionBlocks(client, tool, resolved, ops) {
|
|
|
33902
34059
|
`));
|
|
33903
34060
|
}
|
|
33904
34061
|
function refusalWithCode(error48, suffix = "") {
|
|
34062
|
+
if (error48 instanceof DispatchGatewayError) {
|
|
34063
|
+
let told = error48.message;
|
|
34064
|
+
if (suffix !== "") {
|
|
34065
|
+
told = error48.mayHaveReachedDispatch ? `${error48.answer}${suffix}` : `${error48.answer}, so ${error48.advice}${suffix}`;
|
|
34066
|
+
}
|
|
34067
|
+
return new DispatchGatewayError(error48.status, error48.answer, error48.advice, `${error48.code}: ${told}`);
|
|
34068
|
+
}
|
|
33905
34069
|
if (!(error48 instanceof DispatchServiceError))
|
|
33906
34070
|
return error48;
|
|
33907
34071
|
return new DispatchServiceError(error48.code, error48.status, `${error48.code}: ${error48.message}${suffix}`, error48.candidates, error48.current, error48.mismatches);
|
|
33908
34072
|
}
|
|
34073
|
+
function dispatchAnswered(error48, status) {
|
|
34074
|
+
return error48 instanceof DispatchServiceError && error48.fromDispatch && error48.status === status;
|
|
34075
|
+
}
|
|
34076
|
+
function writeMayHaveLanded(error48) {
|
|
34077
|
+
if (error48 instanceof DispatchGatewayError)
|
|
34078
|
+
return error48.mayHaveReachedDispatch;
|
|
34079
|
+
return !(error48 instanceof DispatchServiceError) || error48.status >= 500;
|
|
34080
|
+
}
|
|
34081
|
+
function withAccount(error48, account) {
|
|
34082
|
+
const message = messageFor(error48);
|
|
34083
|
+
const told = /[.!?]$/.test(message) ? `${message} ${account.charAt(0).toUpperCase()}${account.slice(1)}` : `${message}; ${account}`;
|
|
34084
|
+
return new Error(told, { cause: error48 });
|
|
34085
|
+
}
|
|
33909
34086
|
async function executeDispatchTool(input) {
|
|
33910
34087
|
const configUrl = input.config.url;
|
|
33911
34088
|
const configToken = input.config.token;
|
|
@@ -34075,7 +34252,10 @@ async function executeDispatchTool(input) {
|
|
|
34075
34252
|
ref: dispatchChildRef(dispatchIssueRef(issueKey), "message", message.id)
|
|
34076
34253
|
};
|
|
34077
34254
|
} catch (error48) {
|
|
34078
|
-
|
|
34255
|
+
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";
|
|
34256
|
+
if (error48 instanceof DispatchServiceError)
|
|
34257
|
+
throw refusalWithCode(error48, `; ${told}`);
|
|
34258
|
+
throw withAccount(error48, told);
|
|
34079
34259
|
}
|
|
34080
34260
|
}
|
|
34081
34261
|
const linked = before.external_links.map((link) => link.url);
|
|
@@ -34094,17 +34274,15 @@ async function executeDispatchTool(input) {
|
|
|
34094
34274
|
actor
|
|
34095
34275
|
});
|
|
34096
34276
|
} catch (error48) {
|
|
34097
|
-
const taken = error48
|
|
34277
|
+
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)` : "";
|
|
34098
34278
|
if (closingNote === undefined)
|
|
34099
34279
|
throw refusalWithCode(error48, taken);
|
|
34100
|
-
const
|
|
34101
|
-
const
|
|
34102
|
-
const landed =
|
|
34280
|
+
const posted = `the reason already landed as message ${closingNote.id} (${closingNote.ref})`;
|
|
34281
|
+
const fix = error48 instanceof DispatchGatewayError && error48.transient ? "" : "fix what refused the close, then ";
|
|
34282
|
+
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}`;
|
|
34103
34283
|
if (error48 instanceof DispatchServiceError)
|
|
34104
|
-
throw refusalWithCode(error48, taken
|
|
34105
|
-
throw
|
|
34106
|
-
cause: error48
|
|
34107
|
-
});
|
|
34284
|
+
throw refusalWithCode(error48, `${taken}; ${landed}`);
|
|
34285
|
+
throw withAccount(error48, landed);
|
|
34108
34286
|
}
|
|
34109
34287
|
const linkCount = `(${after.external_links.length} ${after.external_links.length === 1 ? "link" : "links"})`;
|
|
34110
34288
|
const changes = [
|
|
@@ -34708,7 +34886,7 @@ async function resolveExistingIssue(client, issueReference) {
|
|
|
34708
34886
|
try {
|
|
34709
34887
|
return await client.resolveIssue(issueReference);
|
|
34710
34888
|
} catch (error48) {
|
|
34711
|
-
if (error48
|
|
34889
|
+
if (dispatchAnswered(error48, 404)) {
|
|
34712
34890
|
throw new Error(`no Dispatch issue is linked to ${issueReference}; create it first with ` + `dispatch_issue({ external: "${issueReference}", ... })`);
|
|
34713
34891
|
}
|
|
34714
34892
|
throw error48;
|
|
@@ -35893,7 +36071,7 @@ function envoyExtension(pi) {
|
|
|
35893
36071
|
};
|
|
35894
36072
|
const client = createEnvoyClient({ baseUrl: defaults.envoyUrl, fetch });
|
|
35895
36073
|
const subscriptions = new Map;
|
|
35896
|
-
const
|
|
36074
|
+
const delivered = createDeliveryDedupe();
|
|
35897
36075
|
let connection;
|
|
35898
36076
|
let sessionDirectory = "";
|
|
35899
36077
|
let sessionID = "";
|
|
@@ -36051,9 +36229,8 @@ function envoyExtension(pi) {
|
|
|
36051
36229
|
}
|
|
36052
36230
|
for (const topic of subscriptionRemovedTopics(raw, sessionID) ?? [])
|
|
36053
36231
|
closeIntentionally(topic);
|
|
36054
|
-
const
|
|
36055
|
-
|
|
36056
|
-
if (!duplicate && !rendered.skip) {
|
|
36232
|
+
const claim2 = rendered.skip ? undefined : delivered.claim(rendered.envelope);
|
|
36233
|
+
if (claim2 !== undefined) {
|
|
36057
36234
|
const envelope2 = rendered.envelope;
|
|
36058
36235
|
if (envelope2 !== undefined) {
|
|
36059
36236
|
inbox.unshift({
|
|
@@ -36065,30 +36242,28 @@ function envoyExtension(pi) {
|
|
|
36065
36242
|
if (inbox.length > 50)
|
|
36066
36243
|
inbox.pop();
|
|
36067
36244
|
}
|
|
36245
|
+
const refuse = async (delivery, error48) => {
|
|
36246
|
+
claim2.release();
|
|
36247
|
+
await postDispatchReply(delivery, { error: error48 });
|
|
36248
|
+
};
|
|
36068
36249
|
try {
|
|
36069
36250
|
if (rendered.rejectedDelivery !== undefined) {
|
|
36070
36251
|
console.warn(`[envoy] rejecting malformed Dispatch targeted delivery ${rendered.rejectedDelivery.id}`);
|
|
36071
|
-
await
|
|
36072
|
-
error: "Invalid Dispatch targeted delivery frame"
|
|
36073
|
-
});
|
|
36252
|
+
await refuse(rendered.rejectedDelivery, "Invalid Dispatch targeted delivery frame");
|
|
36074
36253
|
} else if (rendered.malformedDelivery === true) {
|
|
36075
36254
|
console.warn("[envoy] dropping malformed Dispatch targeted delivery without a reply address");
|
|
36076
36255
|
} else if (rendered.delivery?.mode === "btw") {
|
|
36077
36256
|
const answer = sideTurn(pi, activeSessionContext);
|
|
36078
36257
|
if (shuttingDown) {
|
|
36079
|
-
await
|
|
36080
|
-
error: "This OMP session is shutting down"
|
|
36081
|
-
});
|
|
36258
|
+
await refuse(rendered.delivery, "This OMP session is shutting down");
|
|
36082
36259
|
} else if (answer === undefined) {
|
|
36083
|
-
await
|
|
36084
|
-
error: "This OMP host does not support BTW delivery"
|
|
36085
|
-
});
|
|
36260
|
+
await refuse(rendered.delivery, "This OMP host does not support BTW delivery");
|
|
36086
36261
|
} else {
|
|
36087
36262
|
try {
|
|
36088
36263
|
const reply2 = await answer({ prompt: rendered.delivery.body });
|
|
36089
36264
|
await postDispatchReply(rendered.delivery, { body: reply2.replyText });
|
|
36090
36265
|
} catch (error48) {
|
|
36091
|
-
await
|
|
36266
|
+
await refuse(rendered.delivery, messageFor(error48));
|
|
36092
36267
|
}
|
|
36093
36268
|
}
|
|
36094
36269
|
} else {
|
|
@@ -36104,11 +36279,10 @@ function envoyExtension(pi) {
|
|
|
36104
36279
|
}
|
|
36105
36280
|
}
|
|
36106
36281
|
} catch (error48) {
|
|
36282
|
+
claim2.release();
|
|
36107
36283
|
console.warn(`[envoy] failed to deliver envelope ${envelope2?.event_id ?? "unknown"}`, error48);
|
|
36108
36284
|
throw error48;
|
|
36109
36285
|
}
|
|
36110
|
-
if (dedupeKey !== undefined)
|
|
36111
|
-
rememberBounded(dedupeKeys, dedupeKey, 1000);
|
|
36112
36286
|
}
|
|
36113
36287
|
};
|
|
36114
36288
|
const RESUBSCRIBE_DELAY_MS = Number(process.env.ENVOY_RESUBSCRIBE_DELAY_MS ?? "") || 5000;
|
package/dist/legion.js
CHANGED
|
@@ -30644,6 +30644,23 @@ var EnvelopeSchema = exports_external.object({
|
|
|
30644
30644
|
urgency: exports_external.enum(["low", "med", "high", "blocking"]).optional(),
|
|
30645
30645
|
expects_reply: exports_external.enum(["none", "optional", "required"]).optional()
|
|
30646
30646
|
});
|
|
30647
|
+
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})$";
|
|
30648
|
+
var mintedDedupeKey = new RegExp(MINTED_DEDUPE_KEY_PATTERN);
|
|
30649
|
+
function dedupeKeyNamesItsEvent(envelope) {
|
|
30650
|
+
const key = envelope.dedupe_key;
|
|
30651
|
+
if (key === undefined)
|
|
30652
|
+
return false;
|
|
30653
|
+
if (envelope.source === "dispatch" || mintedDedupeKey.test(key))
|
|
30654
|
+
return true;
|
|
30655
|
+
switch (envelope.source) {
|
|
30656
|
+
case "github":
|
|
30657
|
+
case "slack":
|
|
30658
|
+
case "ghostwispr":
|
|
30659
|
+
return envelope.source_event_id !== undefined && envelope.source_event_id !== "" && key === `${envelope.source}.${envelope.source_event_id}`;
|
|
30660
|
+
default:
|
|
30661
|
+
return false;
|
|
30662
|
+
}
|
|
30663
|
+
}
|
|
30647
30664
|
// ../contracts/src/handoff-schema.ts
|
|
30648
30665
|
var HANDOFF_SCHEMA_VERSION = 1;
|
|
30649
30666
|
var HANDOFF_PHASES = ["architect", "plan", "implement", "test", "review"];
|
|
@@ -31291,6 +31308,7 @@ class DispatchServiceError extends Error {
|
|
|
31291
31308
|
current;
|
|
31292
31309
|
mismatches;
|
|
31293
31310
|
name = "DispatchServiceError";
|
|
31311
|
+
fromDispatch = true;
|
|
31294
31312
|
constructor(code, status, message, candidates, current, mismatches) {
|
|
31295
31313
|
super(message);
|
|
31296
31314
|
this.code = code;
|
|
@@ -31300,6 +31318,21 @@ class DispatchServiceError extends Error {
|
|
|
31300
31318
|
this.mismatches = mismatches;
|
|
31301
31319
|
}
|
|
31302
31320
|
}
|
|
31321
|
+
|
|
31322
|
+
class DispatchGatewayError extends DispatchServiceError {
|
|
31323
|
+
answer;
|
|
31324
|
+
advice;
|
|
31325
|
+
fromDispatch = false;
|
|
31326
|
+
transient;
|
|
31327
|
+
mayHaveReachedDispatch;
|
|
31328
|
+
constructor(status, answer, advice, message = `${answer}, so ${advice}.`) {
|
|
31329
|
+
super(`HTTP_${status}`, status, message);
|
|
31330
|
+
this.answer = answer;
|
|
31331
|
+
this.advice = advice;
|
|
31332
|
+
this.transient = transientStatus(status);
|
|
31333
|
+
this.mayHaveReachedDispatch = reachesDispatch(status);
|
|
31334
|
+
}
|
|
31335
|
+
}
|
|
31303
31336
|
function asErrorShape(value) {
|
|
31304
31337
|
return typeof value === "object" && value !== null ? value : {};
|
|
31305
31338
|
}
|
|
@@ -31313,7 +31346,7 @@ function requestSignal(signal) {
|
|
|
31313
31346
|
}
|
|
31314
31347
|
function whyNotAPage(answer) {
|
|
31315
31348
|
if (typeof answer === "string") {
|
|
31316
|
-
return
|
|
31349
|
+
return `text that is not a JSON page, ${GATEWAY_PAGE}, so a retry may succeed.`;
|
|
31317
31350
|
}
|
|
31318
31351
|
let what;
|
|
31319
31352
|
if (Array.isArray(answer)) {
|
|
@@ -31332,6 +31365,53 @@ function whyNotAPage(answer) {
|
|
|
31332
31365
|
}
|
|
31333
31366
|
return `${what}. Retrying will not help: the same request gets the same answer until that ` + "Dispatch is upgraded or fixed.";
|
|
31334
31367
|
}
|
|
31368
|
+
var GATEWAY_PAGE = "which looks like a proxy or gateway page rather than Dispatch's own answer";
|
|
31369
|
+
function gatewayAdvice(method, status) {
|
|
31370
|
+
if (!transientStatus(status)) {
|
|
31371
|
+
return "a retry gets the same answer until the Dispatch URL, or whatever answers in its place, is fixed";
|
|
31372
|
+
}
|
|
31373
|
+
if (method === "GET")
|
|
31374
|
+
return "a retry may succeed";
|
|
31375
|
+
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";
|
|
31376
|
+
}
|
|
31377
|
+
function transientStatus(status) {
|
|
31378
|
+
return status >= 500 || status === 408 || status === 429;
|
|
31379
|
+
}
|
|
31380
|
+
function reachesDispatch(status) {
|
|
31381
|
+
return status >= 500;
|
|
31382
|
+
}
|
|
31383
|
+
var REDACTED = "[redacted]";
|
|
31384
|
+
var EXCERPT_SCAN_LIMIT = 64 * 1024;
|
|
31385
|
+
var BEARER_PIECE = 8;
|
|
31386
|
+
function redactBearer(text, bearer) {
|
|
31387
|
+
if (bearer === "")
|
|
31388
|
+
return text.slice(0, EXCERPT_SCAN_LIMIT);
|
|
31389
|
+
const length = Math.min(BEARER_PIECE, bearer.length);
|
|
31390
|
+
const scanned = text.slice(0, EXCERPT_SCAN_LIMIT + length - 1);
|
|
31391
|
+
const covered = new Uint8Array(scanned.length);
|
|
31392
|
+
const pieces = new Set;
|
|
31393
|
+
for (let at = 0;at + length <= bearer.length; at++)
|
|
31394
|
+
pieces.add(bearer.slice(at, at + length));
|
|
31395
|
+
for (const piece of pieces) {
|
|
31396
|
+
let found = scanned.indexOf(piece);
|
|
31397
|
+
while (found !== -1) {
|
|
31398
|
+
covered.fill(1, found, found + length);
|
|
31399
|
+
found = scanned.indexOf(piece, found + 1);
|
|
31400
|
+
}
|
|
31401
|
+
}
|
|
31402
|
+
let redacted = "";
|
|
31403
|
+
let kept = 0;
|
|
31404
|
+
for (let start = covered.indexOf(1);start !== -1; start = covered.indexOf(1, kept)) {
|
|
31405
|
+
redacted += `${scanned.slice(kept, start)}${REDACTED}`;
|
|
31406
|
+
const end = covered.indexOf(0, start);
|
|
31407
|
+
kept = end === -1 ? scanned.length : end;
|
|
31408
|
+
}
|
|
31409
|
+
return `${redacted}${scanned.slice(kept, EXCERPT_SCAN_LIMIT)}`.slice(0, EXCERPT_SCAN_LIMIT);
|
|
31410
|
+
}
|
|
31411
|
+
function excerpt(text, token) {
|
|
31412
|
+
const plain = redactBearer(text, token.trim()).replace(/<(script|style)\b[^<>]*>(?:[\s\S]*?<\/\1\s*>|[\s\S]*$)/gi, " ").replace(/<[^<>]*>/g, " ");
|
|
31413
|
+
return textHead(plain.replace(/(\bauthorization["']?\s*[:=]\s*["']?(?:(?:bearer|basic|digest|token)\s+)?)[^\s"'<>,;]+/gi, `$1${REDACTED}`).replace(/(\bbearer\s+)[^\s"'<>,;]+/gi, `$1${REDACTED}`));
|
|
31414
|
+
}
|
|
31335
31415
|
|
|
31336
31416
|
class DispatchClient {
|
|
31337
31417
|
token;
|
|
@@ -31589,22 +31669,24 @@ class DispatchClient {
|
|
|
31589
31669
|
};
|
|
31590
31670
|
if (body !== undefined)
|
|
31591
31671
|
headers["Content-Type"] = "application/json";
|
|
31592
|
-
const
|
|
31672
|
+
const url2 = this.#url(path2, query);
|
|
31673
|
+
const response = await this.fetchImpl(url2, {
|
|
31593
31674
|
method,
|
|
31594
31675
|
headers,
|
|
31595
31676
|
signal: this.#signal,
|
|
31596
31677
|
...body === undefined ? {} : { body: JSON.stringify(body) }
|
|
31597
31678
|
});
|
|
31598
|
-
return this.#response(response);
|
|
31679
|
+
return this.#response(method, url2, response);
|
|
31599
31680
|
}
|
|
31600
31681
|
async#form(method, path2, body) {
|
|
31601
|
-
const
|
|
31682
|
+
const url2 = this.#url(path2);
|
|
31683
|
+
const response = await this.fetchImpl(url2, {
|
|
31602
31684
|
method,
|
|
31603
31685
|
headers: { Accept: "application/json", Authorization: `Bearer ${this.token}` },
|
|
31604
31686
|
body,
|
|
31605
31687
|
signal: this.#signal
|
|
31606
31688
|
});
|
|
31607
|
-
return this.#response(response);
|
|
31689
|
+
return this.#response(method, url2, response);
|
|
31608
31690
|
}
|
|
31609
31691
|
#url(path2, query) {
|
|
31610
31692
|
const url2 = new URL(`${path2.map((segment) => encodeURIComponent(segment)).join("/")}`, `${this.#baseUrl}/`);
|
|
@@ -31620,7 +31702,7 @@ class DispatchClient {
|
|
|
31620
31702
|
}
|
|
31621
31703
|
return url2.toString();
|
|
31622
31704
|
}
|
|
31623
|
-
async#response(response) {
|
|
31705
|
+
async#response(method, url2, response) {
|
|
31624
31706
|
const text = await response.text();
|
|
31625
31707
|
let payload = text;
|
|
31626
31708
|
if (text && isJson(response)) {
|
|
@@ -31632,7 +31714,23 @@ class DispatchClient {
|
|
|
31632
31714
|
}
|
|
31633
31715
|
if (!response.ok) {
|
|
31634
31716
|
const error48 = asErrorShape(payload);
|
|
31635
|
-
|
|
31717
|
+
if (typeof error48.error === "string") {
|
|
31718
|
+
throw new DispatchServiceError(error48.code ?? `HTTP_${response.status}`, response.status, error48.error, error48.candidates, error48.current, error48.mismatches);
|
|
31719
|
+
}
|
|
31720
|
+
const quoted = excerpt(text, this.token);
|
|
31721
|
+
let body;
|
|
31722
|
+
if (quoted !== "") {
|
|
31723
|
+
body = `a body that is not Dispatch's error JSON (${JSON.stringify(quoted)})`;
|
|
31724
|
+
} else if (text === "") {
|
|
31725
|
+
body = "an empty body";
|
|
31726
|
+
} else {
|
|
31727
|
+
const within = text.length > EXCERPT_SCAN_LIMIT ? ` in its first ${EXCERPT_SCAN_LIMIT / 1024} KiB` : "";
|
|
31728
|
+
const size = Buffer.byteLength(text).toLocaleString("en-US");
|
|
31729
|
+
body = `a body of ${size} bytes and no readable text${within}`;
|
|
31730
|
+
}
|
|
31731
|
+
const reasonPhrase = excerpt(response.statusText, this.token);
|
|
31732
|
+
const reason = reasonPhrase === "" ? "" : ` ${reasonPhrase}`;
|
|
31733
|
+
throw new DispatchGatewayError(response.status, `${method} ${url2} answered ${response.status}${reason} with ${body}, ${GATEWAY_PAGE}`, gatewayAdvice(method, response.status));
|
|
31636
31734
|
}
|
|
31637
31735
|
return payload;
|
|
31638
31736
|
}
|
|
@@ -32375,7 +32473,7 @@ async function resolveArtifact(client, owner, artifactReference, { canonical = f
|
|
|
32375
32473
|
throw new Error("artifact is required for a project document");
|
|
32376
32474
|
}
|
|
32377
32475
|
const routed = await client.getProjectArtifact(owner.project, artifactReference).catch((error48) => {
|
|
32378
|
-
if (!(error48
|
|
32476
|
+
if (!dispatchAnswered(error48, 404))
|
|
32379
32477
|
throw error48;
|
|
32380
32478
|
return;
|
|
32381
32479
|
});
|
|
@@ -32546,12 +32644,12 @@ function referenceLines(edges) {
|
|
|
32546
32644
|
if (edges.length === 0)
|
|
32547
32645
|
return ["- none"];
|
|
32548
32646
|
return edges.map((edge) => {
|
|
32549
|
-
const
|
|
32550
|
-
return `- ${edge.kind} ${edge.node.kind} ${edge.node.ref ?? edge.node.id} (${
|
|
32647
|
+
const excerpt2 = edge.excerpt === undefined ? "" : `${textHead(edge.excerpt.text)} \xB7 `;
|
|
32648
|
+
return `- ${edge.kind} ${edge.node.kind} ${edge.node.ref ?? edge.node.id} (${excerpt2}${edge.created_at})`;
|
|
32551
32649
|
});
|
|
32552
32650
|
}
|
|
32553
32651
|
function unavailableReason(error48) {
|
|
32554
|
-
return error48
|
|
32652
|
+
return dispatchAnswered(error48, 404) ? "unavailable" : `unavailable: ${messageFor(error48)}`;
|
|
32555
32653
|
}
|
|
32556
32654
|
async function graphEdges(client, query) {
|
|
32557
32655
|
try {
|
|
@@ -32741,9 +32839,8 @@ async function openArtifactMarks(client, resolved) {
|
|
|
32741
32839
|
const marks = asks.filter((ask) => ask.state === "open" && ask.anchor?.artifact_id === resolved.artifact.id).map((ask) => `ask ${ask.id}`);
|
|
32742
32840
|
const commentsResult = await commentsResultPromise;
|
|
32743
32841
|
if (commentsResult.status === "rejected") {
|
|
32744
|
-
if (commentsResult.reason
|
|
32842
|
+
if (dispatchAnswered(commentsResult.reason, 404))
|
|
32745
32843
|
return marks;
|
|
32746
|
-
}
|
|
32747
32844
|
throw commentsResult.reason;
|
|
32748
32845
|
}
|
|
32749
32846
|
return [
|
|
@@ -32830,10 +32927,30 @@ async function refuseRemovingOpenDecisionBlocks(client, tool, resolved, ops) {
|
|
|
32830
32927
|
`));
|
|
32831
32928
|
}
|
|
32832
32929
|
function refusalWithCode(error48, suffix = "") {
|
|
32930
|
+
if (error48 instanceof DispatchGatewayError) {
|
|
32931
|
+
let told = error48.message;
|
|
32932
|
+
if (suffix !== "") {
|
|
32933
|
+
told = error48.mayHaveReachedDispatch ? `${error48.answer}${suffix}` : `${error48.answer}, so ${error48.advice}${suffix}`;
|
|
32934
|
+
}
|
|
32935
|
+
return new DispatchGatewayError(error48.status, error48.answer, error48.advice, `${error48.code}: ${told}`);
|
|
32936
|
+
}
|
|
32833
32937
|
if (!(error48 instanceof DispatchServiceError))
|
|
32834
32938
|
return error48;
|
|
32835
32939
|
return new DispatchServiceError(error48.code, error48.status, `${error48.code}: ${error48.message}${suffix}`, error48.candidates, error48.current, error48.mismatches);
|
|
32836
32940
|
}
|
|
32941
|
+
function dispatchAnswered(error48, status) {
|
|
32942
|
+
return error48 instanceof DispatchServiceError && error48.fromDispatch && error48.status === status;
|
|
32943
|
+
}
|
|
32944
|
+
function writeMayHaveLanded(error48) {
|
|
32945
|
+
if (error48 instanceof DispatchGatewayError)
|
|
32946
|
+
return error48.mayHaveReachedDispatch;
|
|
32947
|
+
return !(error48 instanceof DispatchServiceError) || error48.status >= 500;
|
|
32948
|
+
}
|
|
32949
|
+
function withAccount(error48, account) {
|
|
32950
|
+
const message = messageFor(error48);
|
|
32951
|
+
const told = /[.!?]$/.test(message) ? `${message} ${account.charAt(0).toUpperCase()}${account.slice(1)}` : `${message}; ${account}`;
|
|
32952
|
+
return new Error(told, { cause: error48 });
|
|
32953
|
+
}
|
|
32837
32954
|
async function executeDispatchTool(input) {
|
|
32838
32955
|
const configUrl = input.config.url;
|
|
32839
32956
|
const configToken = input.config.token;
|
|
@@ -33003,7 +33120,10 @@ async function executeDispatchTool(input) {
|
|
|
33003
33120
|
ref: dispatchChildRef(dispatchIssueRef(issueKey), "message", message.id)
|
|
33004
33121
|
};
|
|
33005
33122
|
} catch (error48) {
|
|
33006
|
-
|
|
33123
|
+
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";
|
|
33124
|
+
if (error48 instanceof DispatchServiceError)
|
|
33125
|
+
throw refusalWithCode(error48, `; ${told}`);
|
|
33126
|
+
throw withAccount(error48, told);
|
|
33007
33127
|
}
|
|
33008
33128
|
}
|
|
33009
33129
|
const linked = before.external_links.map((link) => link.url);
|
|
@@ -33022,17 +33142,15 @@ async function executeDispatchTool(input) {
|
|
|
33022
33142
|
actor
|
|
33023
33143
|
});
|
|
33024
33144
|
} catch (error48) {
|
|
33025
|
-
const taken = error48
|
|
33145
|
+
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)` : "";
|
|
33026
33146
|
if (closingNote === undefined)
|
|
33027
33147
|
throw refusalWithCode(error48, taken);
|
|
33028
|
-
const
|
|
33029
|
-
const
|
|
33030
|
-
const landed =
|
|
33148
|
+
const posted = `the reason already landed as message ${closingNote.id} (${closingNote.ref})`;
|
|
33149
|
+
const fix = error48 instanceof DispatchGatewayError && error48.transient ? "" : "fix what refused the close, then ";
|
|
33150
|
+
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}`;
|
|
33031
33151
|
if (error48 instanceof DispatchServiceError)
|
|
33032
|
-
throw refusalWithCode(error48, taken
|
|
33033
|
-
throw
|
|
33034
|
-
cause: error48
|
|
33035
|
-
});
|
|
33152
|
+
throw refusalWithCode(error48, `${taken}; ${landed}`);
|
|
33153
|
+
throw withAccount(error48, landed);
|
|
33036
33154
|
}
|
|
33037
33155
|
const linkCount = `(${after.external_links.length} ${after.external_links.length === 1 ? "link" : "links"})`;
|
|
33038
33156
|
const changes = [
|
|
@@ -33636,7 +33754,7 @@ async function resolveExistingIssue(client, issueReference) {
|
|
|
33636
33754
|
try {
|
|
33637
33755
|
return await client.resolveIssue(issueReference);
|
|
33638
33756
|
} catch (error48) {
|
|
33639
|
-
if (error48
|
|
33757
|
+
if (dispatchAnswered(error48, 404)) {
|
|
33640
33758
|
throw new Error(`no Dispatch issue is linked to ${issueReference}; create it first with ` + `dispatch_issue({ external: "${issueReference}", ... })`);
|
|
33641
33759
|
}
|
|
33642
33760
|
throw error48;
|
|
@@ -33685,7 +33803,7 @@ import { logger } from "@oh-my-pi/pi-utils";
|
|
|
33685
33803
|
// package.json
|
|
33686
33804
|
var package_default = {
|
|
33687
33805
|
name: "@sjawhar/pi-legion-envoy",
|
|
33688
|
-
version: "5.30.
|
|
33806
|
+
version: "5.30.5",
|
|
33689
33807
|
type: "module",
|
|
33690
33808
|
omp: {
|
|
33691
33809
|
extensions: [
|