@sjawhar/pi-legion-envoy 5.30.4 → 5.30.6
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
CHANGED
|
@@ -30366,7 +30366,7 @@ var dispatchToolSpecs = [
|
|
|
30366
30366
|
{
|
|
30367
30367
|
name: "dispatch_read",
|
|
30368
30368
|
example: { issue: "DSP-1" },
|
|
30369
|
-
description: "Read an issue or project-document summary, targeted ask, or targeted comment reply chain, or the conversation " + "a message belongs to. Do not use it for document contents; use dispatch_doc_read instead. Supply ref, issue, " + "or project plus artifact; or message alone, which reads a human's direct message to this session and every " + "reply to it (they belong to no issue). " + "Every read ends with `Referenced by:` (what cites or hangs off this node, each with its dispatch:// address, " + "an excerpt, and when) and `Links:` (what it cites), so tracing provenance is one call. " + OWNER_REFERENCE,
|
|
30369
|
+
description: "Read an issue or project-document summary, targeted ask, or targeted comment reply chain, or the conversation " + "a message belongs to. Do not use it for document contents; use dispatch_doc_read instead. Supply ref, issue, " + "or project plus artifact; or message alone, which reads a human's direct message to this session and every " + "reply to it (they belong to no issue). " + "An anchored comment or ask also says where its quote sits, as `Position:`: the block's path from the top, " + "and in a table the row (0 is the header), the cells before the anchored one, and the column's header; " + "`Position: unavailable (<code>)` when Dispatch could not read the document: `DOC_SERVICE_UNAVAILABLE` " + "(try again shortly), `DOC_SCHEMA` (the document needs repair) or `INTERNAL`. " + "Every read ends with `Referenced by:` (what cites or hangs off this node, each with its dispatch:// address, " + "an excerpt, and when) and `Links:` (what it cites), so tracing provenance is one call. " + OWNER_REFERENCE,
|
|
30370
30370
|
arguments: (z2) => ({
|
|
30371
30371
|
issue: z2.string().describe(ISSUE_REFERENCE).optional(),
|
|
30372
30372
|
project: z2.string().describe("Project key owning the document.").optional(),
|
|
@@ -32463,6 +32463,7 @@ class DispatchServiceError extends Error {
|
|
|
32463
32463
|
current;
|
|
32464
32464
|
mismatches;
|
|
32465
32465
|
name = "DispatchServiceError";
|
|
32466
|
+
fromDispatch = true;
|
|
32466
32467
|
constructor(code, status, message, candidates, current, mismatches) {
|
|
32467
32468
|
super(message);
|
|
32468
32469
|
this.code = code;
|
|
@@ -32472,6 +32473,21 @@ class DispatchServiceError extends Error {
|
|
|
32472
32473
|
this.mismatches = mismatches;
|
|
32473
32474
|
}
|
|
32474
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
|
+
}
|
|
32475
32491
|
function asErrorShape(value) {
|
|
32476
32492
|
return typeof value === "object" && value !== null ? value : {};
|
|
32477
32493
|
}
|
|
@@ -32485,7 +32501,7 @@ function requestSignal(signal) {
|
|
|
32485
32501
|
}
|
|
32486
32502
|
function whyNotAPage(answer) {
|
|
32487
32503
|
if (typeof answer === "string") {
|
|
32488
|
-
return
|
|
32504
|
+
return `text that is not a JSON page, ${GATEWAY_PAGE}, so a retry may succeed.`;
|
|
32489
32505
|
}
|
|
32490
32506
|
let what;
|
|
32491
32507
|
if (Array.isArray(answer)) {
|
|
@@ -32504,6 +32520,53 @@ function whyNotAPage(answer) {
|
|
|
32504
32520
|
}
|
|
32505
32521
|
return `${what}. Retrying will not help: the same request gets the same answer until that ` + "Dispatch is upgraded or fixed.";
|
|
32506
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
|
+
}
|
|
32507
32570
|
|
|
32508
32571
|
class DispatchClient {
|
|
32509
32572
|
token;
|
|
@@ -32761,22 +32824,24 @@ class DispatchClient {
|
|
|
32761
32824
|
};
|
|
32762
32825
|
if (body !== undefined)
|
|
32763
32826
|
headers["Content-Type"] = "application/json";
|
|
32764
|
-
const
|
|
32827
|
+
const url2 = this.#url(path2, query);
|
|
32828
|
+
const response = await this.fetchImpl(url2, {
|
|
32765
32829
|
method,
|
|
32766
32830
|
headers,
|
|
32767
32831
|
signal: this.#signal,
|
|
32768
32832
|
...body === undefined ? {} : { body: JSON.stringify(body) }
|
|
32769
32833
|
});
|
|
32770
|
-
return this.#response(response);
|
|
32834
|
+
return this.#response(method, url2, response);
|
|
32771
32835
|
}
|
|
32772
32836
|
async#form(method, path2, body) {
|
|
32773
|
-
const
|
|
32837
|
+
const url2 = this.#url(path2);
|
|
32838
|
+
const response = await this.fetchImpl(url2, {
|
|
32774
32839
|
method,
|
|
32775
32840
|
headers: { Accept: "application/json", Authorization: `Bearer ${this.token}` },
|
|
32776
32841
|
body,
|
|
32777
32842
|
signal: this.#signal
|
|
32778
32843
|
});
|
|
32779
|
-
return this.#response(response);
|
|
32844
|
+
return this.#response(method, url2, response);
|
|
32780
32845
|
}
|
|
32781
32846
|
#url(path2, query) {
|
|
32782
32847
|
const url2 = new URL(`${path2.map((segment) => encodeURIComponent(segment)).join("/")}`, `${this.#baseUrl}/`);
|
|
@@ -32792,7 +32857,7 @@ class DispatchClient {
|
|
|
32792
32857
|
}
|
|
32793
32858
|
return url2.toString();
|
|
32794
32859
|
}
|
|
32795
|
-
async#response(response) {
|
|
32860
|
+
async#response(method, url2, response) {
|
|
32796
32861
|
const text = await response.text();
|
|
32797
32862
|
let payload = text;
|
|
32798
32863
|
if (text && isJson(response)) {
|
|
@@ -32804,7 +32869,23 @@ class DispatchClient {
|
|
|
32804
32869
|
}
|
|
32805
32870
|
if (!response.ok) {
|
|
32806
32871
|
const error48 = asErrorShape(payload);
|
|
32807
|
-
|
|
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));
|
|
32808
32889
|
}
|
|
32809
32890
|
return payload;
|
|
32810
32891
|
}
|
|
@@ -33524,7 +33605,7 @@ async function resolveArtifact(client, owner, artifactReference, { canonical = f
|
|
|
33524
33605
|
throw new Error("artifact is required for a project document");
|
|
33525
33606
|
}
|
|
33526
33607
|
const routed = await client.getProjectArtifact(owner.project, artifactReference).catch((error48) => {
|
|
33527
|
-
if (!(error48
|
|
33608
|
+
if (!dispatchAnswered(error48, 404))
|
|
33528
33609
|
throw error48;
|
|
33529
33610
|
return;
|
|
33530
33611
|
});
|
|
@@ -33695,12 +33776,12 @@ function referenceLines(edges) {
|
|
|
33695
33776
|
if (edges.length === 0)
|
|
33696
33777
|
return ["- none"];
|
|
33697
33778
|
return edges.map((edge) => {
|
|
33698
|
-
const
|
|
33699
|
-
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})`;
|
|
33700
33781
|
});
|
|
33701
33782
|
}
|
|
33702
33783
|
function unavailableReason(error48) {
|
|
33703
|
-
return error48
|
|
33784
|
+
return dispatchAnswered(error48, 404) ? "unavailable" : `unavailable: ${messageFor(error48)}`;
|
|
33704
33785
|
}
|
|
33705
33786
|
async function graphEdges(client, query) {
|
|
33706
33787
|
try {
|
|
@@ -33787,6 +33868,7 @@ function askSummary({ ask, replies }, graph) {
|
|
|
33787
33868
|
]);
|
|
33788
33869
|
return [
|
|
33789
33870
|
`Question: ${ask.question}`,
|
|
33871
|
+
...anchorLines(ask),
|
|
33790
33872
|
"Options:",
|
|
33791
33873
|
...ask.options.length === 0 ? ["- none"] : ask.options.map((option) => `- ${option.label}${option.description ? ` \u2014 ${option.description}` : ""}`),
|
|
33792
33874
|
`State: ${ask.state}`,
|
|
@@ -33850,12 +33932,12 @@ function formatOpenAsksSummary(response, baseUrl) {
|
|
|
33850
33932
|
function commentSummary({ comment, replies }, graph) {
|
|
33851
33933
|
const root = [
|
|
33852
33934
|
`${comment.id} \xB7 ${actorText(comment.author)}`,
|
|
33853
|
-
...comment
|
|
33935
|
+
...anchorLines(comment),
|
|
33854
33936
|
`Body: ${comment.body}`
|
|
33855
33937
|
];
|
|
33856
33938
|
const chain = replies.flatMap((reply) => [
|
|
33857
33939
|
`${reply.id} \xB7 ${actorText(reply.author)}`,
|
|
33858
|
-
...reply
|
|
33940
|
+
...anchorLines(reply),
|
|
33859
33941
|
`Body: ${reply.body}`
|
|
33860
33942
|
]);
|
|
33861
33943
|
return [
|
|
@@ -33867,6 +33949,28 @@ function commentSummary({ comment, replies }, graph) {
|
|
|
33867
33949
|
].join(`
|
|
33868
33950
|
`);
|
|
33869
33951
|
}
|
|
33952
|
+
function anchorLines(record2) {
|
|
33953
|
+
return [
|
|
33954
|
+
...record2.anchor?.quote === undefined ? [] : [`> ${record2.anchor.quote}`],
|
|
33955
|
+
...record2.anchor_block === undefined ? [] : [`Position: ${positionText(record2.anchor_block)}`],
|
|
33956
|
+
...record2.anchor_block_error === undefined ? [] : [`Position: unavailable (${record2.anchor_block_error})`]
|
|
33957
|
+
];
|
|
33958
|
+
}
|
|
33959
|
+
function positionText(block) {
|
|
33960
|
+
const { table, path: path2 } = block;
|
|
33961
|
+
const segments = path2.map((entry) => `${entry.type}[${entry.index}]`);
|
|
33962
|
+
if (table === undefined || table.row === null)
|
|
33963
|
+
return segments.join(" \u203A ");
|
|
33964
|
+
const tableAt = path2.findIndex((entry) => entry.type === "table");
|
|
33965
|
+
const cells = table.cells ?? [];
|
|
33966
|
+
const label = (table.column === null ? cells : cells.slice(0, table.column)).map((cell) => cell.trim()).filter((cell) => cell !== "" && !/^\d+$/.test(cell)).join(" \xB7 ");
|
|
33967
|
+
const row = label === "" ? `row ${table.row}` : `row ${table.row} (${label})`;
|
|
33968
|
+
const header = table.header === null || table.header === "" ? String(table.column) : table.header;
|
|
33969
|
+
return [
|
|
33970
|
+
...segments.slice(0, tableAt + 1),
|
|
33971
|
+
table.column === null ? row : `${row}, column ${header}`
|
|
33972
|
+
].join(" \u203A ");
|
|
33973
|
+
}
|
|
33870
33974
|
function messageSummary({ message, replies }, graph) {
|
|
33871
33975
|
const root = [`${message.id} \xB7 ${actorText(message.author)}`, `Body: ${message.body}`];
|
|
33872
33976
|
const chain = replies.flatMap((reply) => [
|
|
@@ -33890,9 +33994,8 @@ async function openArtifactMarks(client, resolved) {
|
|
|
33890
33994
|
const marks = asks.filter((ask) => ask.state === "open" && ask.anchor?.artifact_id === resolved.artifact.id).map((ask) => `ask ${ask.id}`);
|
|
33891
33995
|
const commentsResult = await commentsResultPromise;
|
|
33892
33996
|
if (commentsResult.status === "rejected") {
|
|
33893
|
-
if (commentsResult.reason
|
|
33997
|
+
if (dispatchAnswered(commentsResult.reason, 404))
|
|
33894
33998
|
return marks;
|
|
33895
|
-
}
|
|
33896
33999
|
throw commentsResult.reason;
|
|
33897
34000
|
}
|
|
33898
34001
|
return [
|
|
@@ -33979,10 +34082,30 @@ async function refuseRemovingOpenDecisionBlocks(client, tool, resolved, ops) {
|
|
|
33979
34082
|
`));
|
|
33980
34083
|
}
|
|
33981
34084
|
function refusalWithCode(error48, suffix = "") {
|
|
34085
|
+
if (error48 instanceof DispatchGatewayError) {
|
|
34086
|
+
let told = error48.message;
|
|
34087
|
+
if (suffix !== "") {
|
|
34088
|
+
told = error48.mayHaveReachedDispatch ? `${error48.answer}${suffix}` : `${error48.answer}, so ${error48.advice}${suffix}`;
|
|
34089
|
+
}
|
|
34090
|
+
return new DispatchGatewayError(error48.status, error48.answer, error48.advice, `${error48.code}: ${told}`);
|
|
34091
|
+
}
|
|
33982
34092
|
if (!(error48 instanceof DispatchServiceError))
|
|
33983
34093
|
return error48;
|
|
33984
34094
|
return new DispatchServiceError(error48.code, error48.status, `${error48.code}: ${error48.message}${suffix}`, error48.candidates, error48.current, error48.mismatches);
|
|
33985
34095
|
}
|
|
34096
|
+
function dispatchAnswered(error48, status) {
|
|
34097
|
+
return error48 instanceof DispatchServiceError && error48.fromDispatch && error48.status === status;
|
|
34098
|
+
}
|
|
34099
|
+
function writeMayHaveLanded(error48) {
|
|
34100
|
+
if (error48 instanceof DispatchGatewayError)
|
|
34101
|
+
return error48.mayHaveReachedDispatch;
|
|
34102
|
+
return !(error48 instanceof DispatchServiceError) || error48.status >= 500;
|
|
34103
|
+
}
|
|
34104
|
+
function withAccount(error48, account) {
|
|
34105
|
+
const message = messageFor(error48);
|
|
34106
|
+
const told = /[.!?]$/.test(message) ? `${message} ${account.charAt(0).toUpperCase()}${account.slice(1)}` : `${message}; ${account}`;
|
|
34107
|
+
return new Error(told, { cause: error48 });
|
|
34108
|
+
}
|
|
33986
34109
|
async function executeDispatchTool(input) {
|
|
33987
34110
|
const configUrl = input.config.url;
|
|
33988
34111
|
const configToken = input.config.token;
|
|
@@ -34152,7 +34275,10 @@ async function executeDispatchTool(input) {
|
|
|
34152
34275
|
ref: dispatchChildRef(dispatchIssueRef(issueKey), "message", message.id)
|
|
34153
34276
|
};
|
|
34154
34277
|
} catch (error48) {
|
|
34155
|
-
|
|
34278
|
+
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";
|
|
34279
|
+
if (error48 instanceof DispatchServiceError)
|
|
34280
|
+
throw refusalWithCode(error48, `; ${told}`);
|
|
34281
|
+
throw withAccount(error48, told);
|
|
34156
34282
|
}
|
|
34157
34283
|
}
|
|
34158
34284
|
const linked = before.external_links.map((link) => link.url);
|
|
@@ -34171,17 +34297,15 @@ async function executeDispatchTool(input) {
|
|
|
34171
34297
|
actor
|
|
34172
34298
|
});
|
|
34173
34299
|
} catch (error48) {
|
|
34174
|
-
const taken = error48
|
|
34300
|
+
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)` : "";
|
|
34175
34301
|
if (closingNote === undefined)
|
|
34176
34302
|
throw refusalWithCode(error48, taken);
|
|
34177
|
-
const
|
|
34178
|
-
const
|
|
34179
|
-
const landed =
|
|
34303
|
+
const posted = `the reason already landed as message ${closingNote.id} (${closingNote.ref})`;
|
|
34304
|
+
const fix = error48 instanceof DispatchGatewayError && error48.transient ? "" : "fix what refused the close, then ";
|
|
34305
|
+
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}`;
|
|
34180
34306
|
if (error48 instanceof DispatchServiceError)
|
|
34181
|
-
throw refusalWithCode(error48, taken
|
|
34182
|
-
throw
|
|
34183
|
-
cause: error48
|
|
34184
|
-
});
|
|
34307
|
+
throw refusalWithCode(error48, `${taken}; ${landed}`);
|
|
34308
|
+
throw withAccount(error48, landed);
|
|
34185
34309
|
}
|
|
34186
34310
|
const linkCount = `(${after.external_links.length} ${after.external_links.length === 1 ? "link" : "links"})`;
|
|
34187
34311
|
const changes = [
|
|
@@ -34785,7 +34909,7 @@ async function resolveExistingIssue(client, issueReference) {
|
|
|
34785
34909
|
try {
|
|
34786
34910
|
return await client.resolveIssue(issueReference);
|
|
34787
34911
|
} catch (error48) {
|
|
34788
|
-
if (error48
|
|
34912
|
+
if (dispatchAnswered(error48, 404)) {
|
|
34789
34913
|
throw new Error(`no Dispatch issue is linked to ${issueReference}; create it first with ` + `dispatch_issue({ external: "${issueReference}", ... })`);
|
|
34790
34914
|
}
|
|
34791
34915
|
throw error48;
|
package/dist/legion.js
CHANGED
|
@@ -30535,7 +30535,7 @@ var dispatchToolSpecs = [
|
|
|
30535
30535
|
{
|
|
30536
30536
|
name: "dispatch_read",
|
|
30537
30537
|
example: { issue: "DSP-1" },
|
|
30538
|
-
description: "Read an issue or project-document summary, targeted ask, or targeted comment reply chain, or the conversation " + "a message belongs to. Do not use it for document contents; use dispatch_doc_read instead. Supply ref, issue, " + "or project plus artifact; or message alone, which reads a human's direct message to this session and every " + "reply to it (they belong to no issue). " + "Every read ends with `Referenced by:` (what cites or hangs off this node, each with its dispatch:// address, " + "an excerpt, and when) and `Links:` (what it cites), so tracing provenance is one call. " + OWNER_REFERENCE,
|
|
30538
|
+
description: "Read an issue or project-document summary, targeted ask, or targeted comment reply chain, or the conversation " + "a message belongs to. Do not use it for document contents; use dispatch_doc_read instead. Supply ref, issue, " + "or project plus artifact; or message alone, which reads a human's direct message to this session and every " + "reply to it (they belong to no issue). " + "An anchored comment or ask also says where its quote sits, as `Position:`: the block's path from the top, " + "and in a table the row (0 is the header), the cells before the anchored one, and the column's header; " + "`Position: unavailable (<code>)` when Dispatch could not read the document: `DOC_SERVICE_UNAVAILABLE` " + "(try again shortly), `DOC_SCHEMA` (the document needs repair) or `INTERNAL`. " + "Every read ends with `Referenced by:` (what cites or hangs off this node, each with its dispatch:// address, " + "an excerpt, and when) and `Links:` (what it cites), so tracing provenance is one call. " + OWNER_REFERENCE,
|
|
30539
30539
|
arguments: (z2) => ({
|
|
30540
30540
|
issue: z2.string().describe(ISSUE_REFERENCE).optional(),
|
|
30541
30541
|
project: z2.string().describe("Project key owning the document.").optional(),
|
|
@@ -31308,6 +31308,7 @@ class DispatchServiceError extends Error {
|
|
|
31308
31308
|
current;
|
|
31309
31309
|
mismatches;
|
|
31310
31310
|
name = "DispatchServiceError";
|
|
31311
|
+
fromDispatch = true;
|
|
31311
31312
|
constructor(code, status, message, candidates, current, mismatches) {
|
|
31312
31313
|
super(message);
|
|
31313
31314
|
this.code = code;
|
|
@@ -31317,6 +31318,21 @@ class DispatchServiceError extends Error {
|
|
|
31317
31318
|
this.mismatches = mismatches;
|
|
31318
31319
|
}
|
|
31319
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
|
+
}
|
|
31320
31336
|
function asErrorShape(value) {
|
|
31321
31337
|
return typeof value === "object" && value !== null ? value : {};
|
|
31322
31338
|
}
|
|
@@ -31330,7 +31346,7 @@ function requestSignal(signal) {
|
|
|
31330
31346
|
}
|
|
31331
31347
|
function whyNotAPage(answer) {
|
|
31332
31348
|
if (typeof answer === "string") {
|
|
31333
|
-
return
|
|
31349
|
+
return `text that is not a JSON page, ${GATEWAY_PAGE}, so a retry may succeed.`;
|
|
31334
31350
|
}
|
|
31335
31351
|
let what;
|
|
31336
31352
|
if (Array.isArray(answer)) {
|
|
@@ -31349,6 +31365,53 @@ function whyNotAPage(answer) {
|
|
|
31349
31365
|
}
|
|
31350
31366
|
return `${what}. Retrying will not help: the same request gets the same answer until that ` + "Dispatch is upgraded or fixed.";
|
|
31351
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
|
+
}
|
|
31352
31415
|
|
|
31353
31416
|
class DispatchClient {
|
|
31354
31417
|
token;
|
|
@@ -31606,22 +31669,24 @@ class DispatchClient {
|
|
|
31606
31669
|
};
|
|
31607
31670
|
if (body !== undefined)
|
|
31608
31671
|
headers["Content-Type"] = "application/json";
|
|
31609
|
-
const
|
|
31672
|
+
const url2 = this.#url(path2, query);
|
|
31673
|
+
const response = await this.fetchImpl(url2, {
|
|
31610
31674
|
method,
|
|
31611
31675
|
headers,
|
|
31612
31676
|
signal: this.#signal,
|
|
31613
31677
|
...body === undefined ? {} : { body: JSON.stringify(body) }
|
|
31614
31678
|
});
|
|
31615
|
-
return this.#response(response);
|
|
31679
|
+
return this.#response(method, url2, response);
|
|
31616
31680
|
}
|
|
31617
31681
|
async#form(method, path2, body) {
|
|
31618
|
-
const
|
|
31682
|
+
const url2 = this.#url(path2);
|
|
31683
|
+
const response = await this.fetchImpl(url2, {
|
|
31619
31684
|
method,
|
|
31620
31685
|
headers: { Accept: "application/json", Authorization: `Bearer ${this.token}` },
|
|
31621
31686
|
body,
|
|
31622
31687
|
signal: this.#signal
|
|
31623
31688
|
});
|
|
31624
|
-
return this.#response(response);
|
|
31689
|
+
return this.#response(method, url2, response);
|
|
31625
31690
|
}
|
|
31626
31691
|
#url(path2, query) {
|
|
31627
31692
|
const url2 = new URL(`${path2.map((segment) => encodeURIComponent(segment)).join("/")}`, `${this.#baseUrl}/`);
|
|
@@ -31637,7 +31702,7 @@ class DispatchClient {
|
|
|
31637
31702
|
}
|
|
31638
31703
|
return url2.toString();
|
|
31639
31704
|
}
|
|
31640
|
-
async#response(response) {
|
|
31705
|
+
async#response(method, url2, response) {
|
|
31641
31706
|
const text = await response.text();
|
|
31642
31707
|
let payload = text;
|
|
31643
31708
|
if (text && isJson(response)) {
|
|
@@ -31649,7 +31714,23 @@ class DispatchClient {
|
|
|
31649
31714
|
}
|
|
31650
31715
|
if (!response.ok) {
|
|
31651
31716
|
const error48 = asErrorShape(payload);
|
|
31652
|
-
|
|
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));
|
|
31653
31734
|
}
|
|
31654
31735
|
return payload;
|
|
31655
31736
|
}
|
|
@@ -32392,7 +32473,7 @@ async function resolveArtifact(client, owner, artifactReference, { canonical = f
|
|
|
32392
32473
|
throw new Error("artifact is required for a project document");
|
|
32393
32474
|
}
|
|
32394
32475
|
const routed = await client.getProjectArtifact(owner.project, artifactReference).catch((error48) => {
|
|
32395
|
-
if (!(error48
|
|
32476
|
+
if (!dispatchAnswered(error48, 404))
|
|
32396
32477
|
throw error48;
|
|
32397
32478
|
return;
|
|
32398
32479
|
});
|
|
@@ -32563,12 +32644,12 @@ function referenceLines(edges) {
|
|
|
32563
32644
|
if (edges.length === 0)
|
|
32564
32645
|
return ["- none"];
|
|
32565
32646
|
return edges.map((edge) => {
|
|
32566
|
-
const
|
|
32567
|
-
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})`;
|
|
32568
32649
|
});
|
|
32569
32650
|
}
|
|
32570
32651
|
function unavailableReason(error48) {
|
|
32571
|
-
return error48
|
|
32652
|
+
return dispatchAnswered(error48, 404) ? "unavailable" : `unavailable: ${messageFor(error48)}`;
|
|
32572
32653
|
}
|
|
32573
32654
|
async function graphEdges(client, query) {
|
|
32574
32655
|
try {
|
|
@@ -32655,6 +32736,7 @@ function askSummary({ ask, replies }, graph) {
|
|
|
32655
32736
|
]);
|
|
32656
32737
|
return [
|
|
32657
32738
|
`Question: ${ask.question}`,
|
|
32739
|
+
...anchorLines(ask),
|
|
32658
32740
|
"Options:",
|
|
32659
32741
|
...ask.options.length === 0 ? ["- none"] : ask.options.map((option) => `- ${option.label}${option.description ? ` \u2014 ${option.description}` : ""}`),
|
|
32660
32742
|
`State: ${ask.state}`,
|
|
@@ -32718,12 +32800,12 @@ function formatOpenAsksSummary(response, baseUrl) {
|
|
|
32718
32800
|
function commentSummary({ comment, replies }, graph) {
|
|
32719
32801
|
const root = [
|
|
32720
32802
|
`${comment.id} \xB7 ${actorText(comment.author)}`,
|
|
32721
|
-
...comment
|
|
32803
|
+
...anchorLines(comment),
|
|
32722
32804
|
`Body: ${comment.body}`
|
|
32723
32805
|
];
|
|
32724
32806
|
const chain = replies.flatMap((reply) => [
|
|
32725
32807
|
`${reply.id} \xB7 ${actorText(reply.author)}`,
|
|
32726
|
-
...reply
|
|
32808
|
+
...anchorLines(reply),
|
|
32727
32809
|
`Body: ${reply.body}`
|
|
32728
32810
|
]);
|
|
32729
32811
|
return [
|
|
@@ -32735,6 +32817,28 @@ function commentSummary({ comment, replies }, graph) {
|
|
|
32735
32817
|
].join(`
|
|
32736
32818
|
`);
|
|
32737
32819
|
}
|
|
32820
|
+
function anchorLines(record2) {
|
|
32821
|
+
return [
|
|
32822
|
+
...record2.anchor?.quote === undefined ? [] : [`> ${record2.anchor.quote}`],
|
|
32823
|
+
...record2.anchor_block === undefined ? [] : [`Position: ${positionText(record2.anchor_block)}`],
|
|
32824
|
+
...record2.anchor_block_error === undefined ? [] : [`Position: unavailable (${record2.anchor_block_error})`]
|
|
32825
|
+
];
|
|
32826
|
+
}
|
|
32827
|
+
function positionText(block) {
|
|
32828
|
+
const { table, path: path2 } = block;
|
|
32829
|
+
const segments = path2.map((entry) => `${entry.type}[${entry.index}]`);
|
|
32830
|
+
if (table === undefined || table.row === null)
|
|
32831
|
+
return segments.join(" \u203A ");
|
|
32832
|
+
const tableAt = path2.findIndex((entry) => entry.type === "table");
|
|
32833
|
+
const cells = table.cells ?? [];
|
|
32834
|
+
const label = (table.column === null ? cells : cells.slice(0, table.column)).map((cell) => cell.trim()).filter((cell) => cell !== "" && !/^\d+$/.test(cell)).join(" \xB7 ");
|
|
32835
|
+
const row = label === "" ? `row ${table.row}` : `row ${table.row} (${label})`;
|
|
32836
|
+
const header = table.header === null || table.header === "" ? String(table.column) : table.header;
|
|
32837
|
+
return [
|
|
32838
|
+
...segments.slice(0, tableAt + 1),
|
|
32839
|
+
table.column === null ? row : `${row}, column ${header}`
|
|
32840
|
+
].join(" \u203A ");
|
|
32841
|
+
}
|
|
32738
32842
|
function messageSummary({ message, replies }, graph) {
|
|
32739
32843
|
const root = [`${message.id} \xB7 ${actorText(message.author)}`, `Body: ${message.body}`];
|
|
32740
32844
|
const chain = replies.flatMap((reply) => [
|
|
@@ -32758,9 +32862,8 @@ async function openArtifactMarks(client, resolved) {
|
|
|
32758
32862
|
const marks = asks.filter((ask) => ask.state === "open" && ask.anchor?.artifact_id === resolved.artifact.id).map((ask) => `ask ${ask.id}`);
|
|
32759
32863
|
const commentsResult = await commentsResultPromise;
|
|
32760
32864
|
if (commentsResult.status === "rejected") {
|
|
32761
|
-
if (commentsResult.reason
|
|
32865
|
+
if (dispatchAnswered(commentsResult.reason, 404))
|
|
32762
32866
|
return marks;
|
|
32763
|
-
}
|
|
32764
32867
|
throw commentsResult.reason;
|
|
32765
32868
|
}
|
|
32766
32869
|
return [
|
|
@@ -32847,10 +32950,30 @@ async function refuseRemovingOpenDecisionBlocks(client, tool, resolved, ops) {
|
|
|
32847
32950
|
`));
|
|
32848
32951
|
}
|
|
32849
32952
|
function refusalWithCode(error48, suffix = "") {
|
|
32953
|
+
if (error48 instanceof DispatchGatewayError) {
|
|
32954
|
+
let told = error48.message;
|
|
32955
|
+
if (suffix !== "") {
|
|
32956
|
+
told = error48.mayHaveReachedDispatch ? `${error48.answer}${suffix}` : `${error48.answer}, so ${error48.advice}${suffix}`;
|
|
32957
|
+
}
|
|
32958
|
+
return new DispatchGatewayError(error48.status, error48.answer, error48.advice, `${error48.code}: ${told}`);
|
|
32959
|
+
}
|
|
32850
32960
|
if (!(error48 instanceof DispatchServiceError))
|
|
32851
32961
|
return error48;
|
|
32852
32962
|
return new DispatchServiceError(error48.code, error48.status, `${error48.code}: ${error48.message}${suffix}`, error48.candidates, error48.current, error48.mismatches);
|
|
32853
32963
|
}
|
|
32964
|
+
function dispatchAnswered(error48, status) {
|
|
32965
|
+
return error48 instanceof DispatchServiceError && error48.fromDispatch && error48.status === status;
|
|
32966
|
+
}
|
|
32967
|
+
function writeMayHaveLanded(error48) {
|
|
32968
|
+
if (error48 instanceof DispatchGatewayError)
|
|
32969
|
+
return error48.mayHaveReachedDispatch;
|
|
32970
|
+
return !(error48 instanceof DispatchServiceError) || error48.status >= 500;
|
|
32971
|
+
}
|
|
32972
|
+
function withAccount(error48, account) {
|
|
32973
|
+
const message = messageFor(error48);
|
|
32974
|
+
const told = /[.!?]$/.test(message) ? `${message} ${account.charAt(0).toUpperCase()}${account.slice(1)}` : `${message}; ${account}`;
|
|
32975
|
+
return new Error(told, { cause: error48 });
|
|
32976
|
+
}
|
|
32854
32977
|
async function executeDispatchTool(input) {
|
|
32855
32978
|
const configUrl = input.config.url;
|
|
32856
32979
|
const configToken = input.config.token;
|
|
@@ -33020,7 +33143,10 @@ async function executeDispatchTool(input) {
|
|
|
33020
33143
|
ref: dispatchChildRef(dispatchIssueRef(issueKey), "message", message.id)
|
|
33021
33144
|
};
|
|
33022
33145
|
} catch (error48) {
|
|
33023
|
-
|
|
33146
|
+
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";
|
|
33147
|
+
if (error48 instanceof DispatchServiceError)
|
|
33148
|
+
throw refusalWithCode(error48, `; ${told}`);
|
|
33149
|
+
throw withAccount(error48, told);
|
|
33024
33150
|
}
|
|
33025
33151
|
}
|
|
33026
33152
|
const linked = before.external_links.map((link) => link.url);
|
|
@@ -33039,17 +33165,15 @@ async function executeDispatchTool(input) {
|
|
|
33039
33165
|
actor
|
|
33040
33166
|
});
|
|
33041
33167
|
} catch (error48) {
|
|
33042
|
-
const taken = error48
|
|
33168
|
+
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)` : "";
|
|
33043
33169
|
if (closingNote === undefined)
|
|
33044
33170
|
throw refusalWithCode(error48, taken);
|
|
33045
|
-
const
|
|
33046
|
-
const
|
|
33047
|
-
const landed =
|
|
33171
|
+
const posted = `the reason already landed as message ${closingNote.id} (${closingNote.ref})`;
|
|
33172
|
+
const fix = error48 instanceof DispatchGatewayError && error48.transient ? "" : "fix what refused the close, then ";
|
|
33173
|
+
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}`;
|
|
33048
33174
|
if (error48 instanceof DispatchServiceError)
|
|
33049
|
-
throw refusalWithCode(error48, taken
|
|
33050
|
-
throw
|
|
33051
|
-
cause: error48
|
|
33052
|
-
});
|
|
33175
|
+
throw refusalWithCode(error48, `${taken}; ${landed}`);
|
|
33176
|
+
throw withAccount(error48, landed);
|
|
33053
33177
|
}
|
|
33054
33178
|
const linkCount = `(${after.external_links.length} ${after.external_links.length === 1 ? "link" : "links"})`;
|
|
33055
33179
|
const changes = [
|
|
@@ -33653,7 +33777,7 @@ async function resolveExistingIssue(client, issueReference) {
|
|
|
33653
33777
|
try {
|
|
33654
33778
|
return await client.resolveIssue(issueReference);
|
|
33655
33779
|
} catch (error48) {
|
|
33656
|
-
if (error48
|
|
33780
|
+
if (dispatchAnswered(error48, 404)) {
|
|
33657
33781
|
throw new Error(`no Dispatch issue is linked to ${issueReference}; create it first with ` + `dispatch_issue({ external: "${issueReference}", ... })`);
|
|
33658
33782
|
}
|
|
33659
33783
|
throw error48;
|
|
@@ -33702,7 +33826,7 @@ import { logger } from "@oh-my-pi/pi-utils";
|
|
|
33702
33826
|
// package.json
|
|
33703
33827
|
var package_default = {
|
|
33704
33828
|
name: "@sjawhar/pi-legion-envoy",
|
|
33705
|
-
version: "5.30.
|
|
33829
|
+
version: "5.30.6",
|
|
33706
33830
|
type: "module",
|
|
33707
33831
|
omp: {
|
|
33708
33832
|
extensions: [
|
|
@@ -57,7 +57,10 @@ table's block id. A row index includes the header: row `0` is the header and its
|
|
|
57
57
|
row and any row's last column cannot be deleted. An index is required. A missing, non-integer, negative, or out-of-range index is
|
|
58
58
|
`INVALID_OP` on `index`, naming the supplied value and the table's actual dimensions before making any change. Markdown parsing
|
|
59
59
|
canonicalizes short ragged rows by padding missing cells, so column deletion preserves every non-selected cell in the canonical table.
|
|
60
|
-
`GET /api/v1/artifacts/<artifact UUID>/blocks` reports a table's own references plus its descendant cell anchors.
|
|
60
|
+
`GET /api/v1/artifacts/<artifact UUID>/blocks` reports a table's own references plus its descendant cell anchors.
|
|
61
|
+
`GET /api/v1/artifacts/<artifact UUID>/blocks/<block id>` places one block: its path from the top-level block down, and for a cell
|
|
62
|
+
the row index (0 is the header, what `delete_row` takes), the column index (what `delete_column` takes), the text of the header cell
|
|
63
|
+
drawn above it (with colspans or rowspans, the column the cell is drawn in) and the row's cells. A row or column
|
|
61
64
|
deletion that would remove an open ask or unresolved comment anchor is `INVALID_OP` on `index`, naming the axis and anchor ids;
|
|
62
65
|
answered asks and resolved comments are history and do not block it. A `find` or quote anchor tolerates inline Markdown
|
|
63
66
|
(`**bold**`, `` `code` ``) and a leading `# ` selects a heading by its text; a miss names the quote and the three nearest blocks so
|
|
@@ -16,6 +16,15 @@ document owner or ref, it returns a document summary with `details` `{ project,
|
|
|
16
16
|
question, options, state, answer, and its reply thread. With a comment ref, it returns that comment and its quoted reply chain. With a
|
|
17
17
|
message ref, it returns that message and its reply chain. Reads do not subscribe; use `dispatch_doc_read` for document contents.
|
|
18
18
|
|
|
19
|
+
An anchored comment or ask also prints `Position:`, where its quote's block stands — `table[3] › row 5 (Red-teamer loop), column Due`
|
|
20
|
+
is the fourth top-level block, a table, its row 5 (row 0 is the header; the index `delete_row` takes), labelled by the row's cells
|
|
21
|
+
before the anchored one, in the column headed Due; outside a table it is the path of types and child indexes down to the block. The
|
|
22
|
+
same facts are `anchor_block` on `GET /api/v1/comments/{id}` and `GET /api/v1/asks/{id}`, and
|
|
23
|
+
`GET /api/v1/artifacts/{id}/blocks/{block_id}` answers them for any block id a document holds. When Dispatch could not read the
|
|
24
|
+
document, the read still answers and prints `Position: unavailable (DOC_SERVICE_UNAVAILABLE)` (try again shortly),
|
|
25
|
+
`Position: unavailable (DOC_SCHEMA)` (the document needs repair) or `Position: unavailable (INTERNAL)`; the API carries that
|
|
26
|
+
code as `anchor_block_error`.
|
|
27
|
+
|
|
19
28
|
Every read ends with two sections from the reference graph. `Referenced by:` lists what points at the node — every document, ask,
|
|
20
29
|
comment, or message that cites it, plus its structure: child issues, attached documents, anchored and owned asks and comments, replies,
|
|
21
30
|
followers — and `Links:` lists what it cites. Each row is `- <edge kind> <node kind> dispatch://… (<excerpt> · <when>)`; for a
|